@@ -14,77 +14,59 @@ export function resetToolTrackerCount(tracker: ToolTracker): void {
1414 tracker . toolResultCount = 0
1515}
1616
17- /** Adapter interface for format-specific message operations */
18- interface MessageFormatAdapter {
19- countToolResults ( messages : any [ ] , tracker : ToolTracker ) : number
20- appendNudge ( messages : any [ ] , nudgeText : string ) : void
17+ /**
18+ * Counts total tool results in OpenAI/Anthropic messages (without tracker).
19+ * Used for determining if nudge threshold is met.
20+ */
21+ export function countToolResults ( messages : any [ ] ) : number {
22+ let count = 0
23+ for ( const m of messages ) {
24+ if ( m . role === 'tool' ) {
25+ count ++
26+ } else if ( m . role === 'user' && Array . isArray ( m . content ) ) {
27+ for ( const part of m . content ) {
28+ if ( part . type === 'tool_result' ) {
29+ count ++
30+ }
31+ }
32+ }
33+ }
34+ return count
35+ }
36+
37+ /**
38+ * Counts total tool results in Gemini contents (without tracker).
39+ */
40+ export function countToolResultsGemini ( contents : any [ ] ) : number {
41+ let count = 0
42+ for ( const content of contents ) {
43+ if ( ! Array . isArray ( content . parts ) ) continue
44+ for ( const part of content . parts ) {
45+ if ( part . functionResponse ) {
46+ count ++
47+ }
48+ }
49+ }
50+ return count
2151}
2252
23- /** Generic nudge injection - nudges every fetch once tools since last prune exceeds freq */
24- function injectNudgeCore (
25- messages : any [ ] ,
26- tracker : ToolTracker ,
27- nudgeText : string ,
28- freq : number ,
29- adapter : MessageFormatAdapter
30- ) : boolean {
31- // Count any new tool results
32- adapter . countToolResults ( messages , tracker )
33-
34- // Once we've exceeded the threshold, nudge on every fetch
35- if ( tracker . toolResultCount > freq ) {
36- adapter . appendNudge ( messages , nudgeText )
37- return true
53+ /**
54+ * Counts total tool results in OpenAI Responses API input (without tracker).
55+ */
56+ export function countToolResultsResponses ( input : any [ ] ) : number {
57+ let count = 0
58+ for ( const item of input ) {
59+ if ( item . type === 'function_call_output' ) {
60+ count ++
61+ }
3862 }
39- return false
63+ return count
4064}
4165
4266// ============================================================================
4367// OpenAI Chat / Anthropic Format
4468// ============================================================================
4569
46- const openaiAdapter : MessageFormatAdapter = {
47- countToolResults ( messages , tracker ) {
48- let newCount = 0
49- for ( const m of messages ) {
50- if ( m . role === 'tool' && m . tool_call_id ) {
51- const id = String ( m . tool_call_id ) . toLowerCase ( )
52- if ( ! tracker . seenToolResultIds . has ( id ) ) {
53- tracker . seenToolResultIds . add ( id )
54- newCount ++
55- const toolName = m . name || tracker . getToolName ?.( m . tool_call_id )
56- if ( toolName !== 'prune' ) {
57- tracker . skipNextIdle = false
58- }
59- }
60- } else if ( m . role === 'user' && Array . isArray ( m . content ) ) {
61- for ( const part of m . content ) {
62- if ( part . type === 'tool_result' && part . tool_use_id ) {
63- const id = String ( part . tool_use_id ) . toLowerCase ( )
64- if ( ! tracker . seenToolResultIds . has ( id ) ) {
65- tracker . seenToolResultIds . add ( id )
66- newCount ++
67- const toolName = tracker . getToolName ?.( part . tool_use_id )
68- if ( toolName !== 'prune' ) {
69- tracker . skipNextIdle = false
70- }
71- }
72- }
73- }
74- }
75- }
76- tracker . toolResultCount += newCount
77- return newCount
78- } ,
79- appendNudge ( messages , nudgeText ) {
80- messages . push ( { role : 'user' , content : nudgeText } )
81- }
82- }
83-
84- export function injectNudge ( messages : any [ ] , tracker : ToolTracker , nudgeText : string , freq : number ) : boolean {
85- return injectNudgeCore ( messages , tracker , nudgeText , freq , openaiAdapter )
86- }
87-
8870/** Check if a message content matches nudge text (OpenAI/Anthropic format) */
8971function isNudgeMessage ( msg : any , nudgeText : string ) : boolean {
9072 if ( typeof msg . content === 'string' ) {
@@ -120,37 +102,6 @@ export function injectSynth(messages: any[], instruction: string, nudgeText: str
120102// Google/Gemini Format (body.contents with parts)
121103// ============================================================================
122104
123- const geminiAdapter : MessageFormatAdapter = {
124- countToolResults ( contents , tracker ) {
125- let newCount = 0
126- for ( const content of contents ) {
127- if ( ! Array . isArray ( content . parts ) ) continue
128- for ( const part of content . parts ) {
129- if ( part . functionResponse ) {
130- const funcName = part . functionResponse . name ?. toLowerCase ( ) || 'unknown'
131- const pseudoId = `gemini:${ funcName } :${ tracker . seenToolResultIds . size } `
132- if ( ! tracker . seenToolResultIds . has ( pseudoId ) ) {
133- tracker . seenToolResultIds . add ( pseudoId )
134- newCount ++
135- if ( funcName !== 'prune' ) {
136- tracker . skipNextIdle = false
137- }
138- }
139- }
140- }
141- }
142- tracker . toolResultCount += newCount
143- return newCount
144- } ,
145- appendNudge ( contents , nudgeText ) {
146- contents . push ( { role : 'user' , parts : [ { text : nudgeText } ] } )
147- }
148- }
149-
150- export function injectNudgeGemini ( contents : any [ ] , tracker : ToolTracker , nudgeText : string , freq : number ) : boolean {
151- return injectNudgeCore ( contents , tracker , nudgeText , freq , geminiAdapter )
152- }
153-
154105/** Check if a Gemini content matches nudge text */
155106function isNudgeContentGemini ( content : any , nudgeText : string ) : boolean {
156107 if ( Array . isArray ( content . parts ) && content . parts . length === 1 ) {
@@ -182,34 +133,6 @@ export function injectSynthGemini(contents: any[], instruction: string, nudgeTex
182133// OpenAI Responses API Format (body.input with type-based items)
183134// ============================================================================
184135
185- const responsesAdapter : MessageFormatAdapter = {
186- countToolResults ( input , tracker ) {
187- let newCount = 0
188- for ( const item of input ) {
189- if ( item . type === 'function_call_output' && item . call_id ) {
190- const id = String ( item . call_id ) . toLowerCase ( )
191- if ( ! tracker . seenToolResultIds . has ( id ) ) {
192- tracker . seenToolResultIds . add ( id )
193- newCount ++
194- const toolName = item . name || tracker . getToolName ?.( item . call_id )
195- if ( toolName !== 'prune' ) {
196- tracker . skipNextIdle = false
197- }
198- }
199- }
200- }
201- tracker . toolResultCount += newCount
202- return newCount
203- } ,
204- appendNudge ( input , nudgeText ) {
205- input . push ( { type : 'message' , role : 'user' , content : nudgeText } )
206- }
207- }
208-
209- export function injectNudgeResponses ( input : any [ ] , tracker : ToolTracker , nudgeText : string , freq : number ) : boolean {
210- return injectNudgeCore ( input , tracker , nudgeText , freq , responsesAdapter )
211- }
212-
213136/** Check if a Responses API item matches nudge text */
214137function isNudgeItemResponses ( item : any , nudgeText : string ) : boolean {
215138 if ( typeof item . content === 'string' ) {
0 commit comments