1+ export interface ToolResultTracker {
2+ seenToolResultIds : Set < string >
3+ toolResultCount : number
4+ }
5+
6+ export function createToolResultTracker ( ) : ToolResultTracker {
7+ return {
8+ seenToolResultIds : new Set ( ) ,
9+ toolResultCount : 0
10+ }
11+ }
12+
13+ function countNewToolResults ( messages : any [ ] , tracker : ToolResultTracker ) : number {
14+ let newCount = 0
15+
16+ for ( const m of messages ) {
17+ if ( m . role === 'tool' && m . tool_call_id ) {
18+ const id = String ( m . tool_call_id ) . toLowerCase ( )
19+ if ( ! tracker . seenToolResultIds . has ( id ) ) {
20+ tracker . seenToolResultIds . add ( id )
21+ newCount ++
22+ }
23+ } else if ( m . role === 'user' && Array . isArray ( m . content ) ) {
24+ for ( const part of m . content ) {
25+ if ( part . type === 'tool_result' && part . tool_use_id ) {
26+ const id = String ( part . tool_use_id ) . toLowerCase ( )
27+ if ( ! tracker . seenToolResultIds . has ( id ) ) {
28+ tracker . seenToolResultIds . add ( id )
29+ newCount ++
30+ }
31+ }
32+ }
33+ }
34+ }
35+
36+ tracker . toolResultCount += newCount
37+ return newCount
38+ }
39+
40+ /**
41+ * Counts new tool results and injects nudge instruction every 5th tool result.
42+ * Returns true if injection happened.
43+ */
44+ export function maybeInjectToolResultNudge (
45+ messages : any [ ] ,
46+ tracker : ToolResultTracker ,
47+ nudgeText : string
48+ ) : boolean {
49+ const prevCount = tracker . toolResultCount
50+ const newCount = countNewToolResults ( messages , tracker )
51+
52+ if ( newCount > 0 ) {
53+ // Check if we crossed a multiple of 5
54+ const prevBucket = Math . floor ( prevCount / 5 )
55+ const newBucket = Math . floor ( tracker . toolResultCount / 5 )
56+ if ( newBucket > prevBucket ) {
57+ // Inject at the END of messages so it's in immediate context
58+ return injectNudgeAtEnd ( messages , nudgeText )
59+ }
60+ }
61+ return false
62+ }
63+
164export function isIgnoredUserMessage ( msg : any ) : boolean {
265 if ( ! msg || msg . role !== 'user' ) {
366 return false
467 }
568
6- if ( msg . ignored || msg . info ?. ignored ) {
69+ // Skip ignored or synthetic messages
70+ if ( msg . ignored || msg . info ?. ignored || msg . synthetic ) {
771 return true
872 }
973
@@ -17,6 +81,19 @@ export function isIgnoredUserMessage(msg: any): boolean {
1781 return false
1882}
1983
84+ /**
85+ * Injects a nudge message at the END of the messages array as a new user message.
86+ * This ensures it's in the model's immediate context, not buried in old messages.
87+ */
88+ export function injectNudgeAtEnd ( messages : any [ ] , nudgeText : string ) : boolean {
89+ messages . push ( {
90+ role : 'user' ,
91+ content : nudgeText ,
92+ synthetic : true
93+ } )
94+ return true
95+ }
96+
2097export function injectSynthInstruction ( messages : any [ ] , instruction : string ) : boolean {
2198 // Find the last user message that is not ignored
2299 for ( let i = messages . length - 1 ; i >= 0 ; i -- ) {
0 commit comments