@@ -4,7 +4,10 @@ import type {
44 getDiagnosticSessionState as GetDiagnosticSessionStateType ,
55 SessionState ,
66} from "../../logging/diagnostic-session-state.js" ;
7- import type { wrapToolWithBeforeToolCallHook as WrapToolWithBeforeToolCallHookType } from "../pi-tools.before-tool-call.js" ;
7+ import type {
8+ ToolOutcomeObserver ,
9+ wrapToolWithBeforeToolCallHook as WrapToolWithBeforeToolCallHookType ,
10+ } from "../pi-tools.before-tool-call.js" ;
811import type {
912 recordToolCall as RecordToolCallType ,
1013 recordToolCallOutcome as RecordToolCallOutcomeType ,
@@ -72,6 +75,7 @@ async function executeWrappedToolOutcome(
7275 toolName : string ,
7376 toolParams : unknown ,
7477 result : unknown ,
78+ onToolOutcome ?: ToolOutcomeObserver ,
7579 runId = baseParams . runId ,
7680) : Promise < unknown > {
7781 const tool = wrapToolWithBeforeToolCallHook (
@@ -84,6 +88,7 @@ async function executeWrappedToolOutcome(
8488 sessionKey : baseParams . sessionKey ,
8589 sessionId : baseParams . sessionId ,
8690 runId,
91+ onToolOutcome,
8792 } ,
8893 ) ;
8994 liveToolCallSeq += 1 ;
@@ -159,12 +164,15 @@ describe("post-compaction loop guard wired into runEmbeddedPiAgent", () => {
159164 // Attempt 2: post-compaction. The live wrapped-tool path records each
160165 // outcome while the prompt is still running; the third identical result
161166 // aborts before the attempt can return.
162- mockedRunEmbeddedAttempt . mockImplementationOnce ( async ( ) => {
167+ mockedRunEmbeddedAttempt . mockImplementationOnce ( async ( attemptParams : unknown ) => {
168+ const onToolOutcome = ( attemptParams as { onToolOutcome ?: ToolOutcomeObserver } )
169+ . onToolOutcome ;
163170 for ( let i = 0 ; i < 3 ; i += 1 ) {
164171 await executeWrappedToolOutcome (
165172 "gateway" ,
166173 { action : "lookup" , path : "x" } ,
167174 "identical-result" ,
175+ onToolOutcome ,
168176 ) ;
169177 }
170178 attemptReturned = true ;
@@ -200,9 +208,16 @@ describe("post-compaction loop guard wired into runEmbeddedPiAgent", () => {
200208 // Attempt 2 (post-compaction): identical args, but DIFFERENT result hash
201209 // each time. This fills the window without triggering the persisted-loop
202210 // abort because the tool is making progress.
203- mockedRunEmbeddedAttempt . mockImplementationOnce ( async ( ) => {
211+ mockedRunEmbeddedAttempt . mockImplementationOnce ( async ( attemptParams : unknown ) => {
212+ const onToolOutcome = ( attemptParams as { onToolOutcome ?: ToolOutcomeObserver } )
213+ . onToolOutcome ;
204214 for ( let i = 0 ; i < 3 ; i += 1 ) {
205- await executeWrappedToolOutcome ( "gateway" , { action : "lookup" , path : "x" } , `result-${ i } ` ) ;
215+ await executeWrappedToolOutcome (
216+ "gateway" ,
217+ { action : "lookup" , path : "x" } ,
218+ `result-${ i } ` ,
219+ onToolOutcome ,
220+ ) ;
206221 }
207222 return makeAttemptResult ( {
208223 promptError : null ,
@@ -235,9 +250,11 @@ describe("post-compaction loop guard wired into runEmbeddedPiAgent", () => {
235250 // Attempt 2 (post-compaction): two distinct records → window full,
236251 // guard disarms with no abort. We then append more identical records
237252 // afterwards in this test to confirm they are not observed by the guard.
238- mockedRunEmbeddedAttempt . mockImplementationOnce ( async ( ) => {
239- await executeWrappedToolOutcome ( "read" , { path : "/a" } , "ra" ) ;
240- await executeWrappedToolOutcome ( "write" , { path : "/b" } , "rb" ) ;
253+ mockedRunEmbeddedAttempt . mockImplementationOnce ( async ( attemptParams : unknown ) => {
254+ const onToolOutcome = ( attemptParams as { onToolOutcome ?: ToolOutcomeObserver } )
255+ . onToolOutcome ;
256+ await executeWrappedToolOutcome ( "read" , { path : "/a" } , "ra" , onToolOutcome ) ;
257+ await executeWrappedToolOutcome ( "write" , { path : "/b" } , "rb" , onToolOutcome ) ;
241258 return makeAttemptResult ( {
242259 promptError : null ,
243260 toolMetas : [ { toolName : "read" } , { toolName : "write" } ] ,
@@ -293,12 +310,15 @@ describe("post-compaction loop guard wired into runEmbeddedPiAgent", () => {
293310 // Attempt 2 (post-compaction): three identical live tool outcomes while
294311 // history is already at the cap. The guard aborts on the third result
295312 // before the mocked attempt can return.
296- mockedRunEmbeddedAttempt . mockImplementationOnce ( async ( ) => {
313+ mockedRunEmbeddedAttempt . mockImplementationOnce ( async ( attemptParams : unknown ) => {
314+ const onToolOutcome = ( attemptParams as { onToolOutcome ?: ToolOutcomeObserver } )
315+ . onToolOutcome ;
297316 for ( let i = 0 ; i < 3 ; i += 1 ) {
298317 await executeWrappedToolOutcome (
299318 "gateway" ,
300319 { action : "lookup" , path : "x" } ,
301320 "identical-result" ,
321+ onToolOutcome ,
302322 ) ;
303323 }
304324 // History is still capped at HISTORY_TRIM_CAP after the trim.
0 commit comments