@@ -38,11 +38,16 @@ import {
3838
3939const EXECUTION_ID = '0f4d5a4c-6a1e-4c2f-9b7d-2c8f1a3e5d90'
4040const SECRET = 'fake-secret-for-test-only'
41- const context = {
42- userId : 'user-1' ,
43- workspaceId : 'workspace-1' ,
44- toolCallId : 'tool-call-1' ,
45- } as ExecutionContext
41+
42+ /** The handler and the projection share the call's registry, as the tool executor wires them. */
43+ function callContext ( registry : ResolvedSecretTraceRegistry ) {
44+ return {
45+ userId : 'user-1' ,
46+ workspaceId : 'workspace-1' ,
47+ toolCallId : 'tool-call-1' ,
48+ resolvedSecretTraceRegistry : registry ,
49+ } as ExecutionContext
50+ }
4651
4752/** Rows and approximate encoded bytes per block of a synthetic trace-shaped run. */
4853const TABLE_QUERIES : ReadonlyArray < readonly [ rows : number , bytes : number ] > = [
@@ -85,17 +90,30 @@ function traceShapedLogs() {
8590 } )
8691}
8792
88- function secretRegistry ( ) {
93+ /** A configured secret; `active` records that the run resolved it into its result. */
94+ function secretRegistry ( { active = true } = { } ) {
8995 const registry = new ResolvedSecretTraceRegistry ( [
9096 { name : 'API_KEY' , plaintext : SECRET , encryptedValue : 'ciphertext' } ,
9197 ] )
92- registry . recordResolved ( 'API_KEY' , SECRET , { propagated : true } )
98+ if ( active ) registry . recordResolved ( 'API_KEY' , SECRET , { propagated : true } )
9399 return registry
94100}
95101
102+ /** Row-shaped output of about 27.5k values: past the log budget, under the projection's cap. */
103+ function wideRows ( ) {
104+ return Array . from ( { length : 2_500 } , ( _ , index ) =>
105+ Object . fromEntries ( Array . from ( { length : 10 } , ( _ , column ) => [ `c${ column } ` , `r${ index } ` ] ) )
106+ )
107+ }
108+
96109describe ( 'run_workflow model-facing result budget' , ( ) => {
110+ let registry : ResolvedSecretTraceRegistry
111+ let context : ExecutionContext
112+
97113 beforeEach ( ( ) => {
98114 mocks . executeWorkflowUseCase . mockReset ( )
115+ registry = secretRegistry ( )
116+ context = callContext ( registry )
99117 } )
100118
101119 it ( 'projects a trace-shaped result with an active secret instead of withholding it' , async ( ) => {
@@ -110,7 +128,7 @@ describe('run_workflow model-facing result budget', () => {
110128 } )
111129
112130 const settled = await executeRunWorkflow ( { workflowId : 'wf-1' } , context )
113- const projection = inspectToolResultForCopilot ( settled , secretRegistry ( ) , 'run_workflow' )
131+ const projection = inspectToolResultForCopilot ( settled , registry , 'run_workflow' )
114132
115133 expect ( projection . safe ) . toBe ( true )
116134 const output = projection . result . output as Record < string , unknown >
@@ -140,7 +158,7 @@ describe('run_workflow model-facing result budget', () => {
140158 } )
141159
142160 const settled = await executeRunWorkflow ( { workflowId : 'wf-1' } , context )
143- const projection = inspectToolResultForCopilot ( settled , secretRegistry ( ) , 'run_workflow' )
161+ const projection = inspectToolResultForCopilot ( settled , registry , 'run_workflow' )
144162
145163 expect ( projection . safe ) . toBe ( true )
146164 expect ( ( projection . result . output as Record < string , unknown > ) . output ) . toEqual ( finalOutput )
@@ -164,7 +182,7 @@ describe('run_workflow model-facing result budget', () => {
164182
165183 const settled = await executeRunWorkflow ( { workflowId : 'wf-1' } , context )
166184 expect ( Buffer . byteLength ( JSON . stringify ( settled . output ) ) ) . toBeLessThan ( 4 * 1024 * 1024 )
167- const projection = inspectToolResultForCopilot ( settled , secretRegistry ( ) , 'run_workflow' )
185+ const projection = inspectToolResultForCopilot ( settled , registry , 'run_workflow' )
168186
169187 expect ( projection . safe ) . toBe ( true )
170188 expect ( ( projection . result . output as Record < string , unknown > ) . output ) . toEqual ( finalOutput )
@@ -183,7 +201,7 @@ describe('run_workflow model-facing result budget', () => {
183201 { workflowId : 'wf-1' , select : [ 'Query 4.rows' ] } ,
184202 context
185203 )
186- const projection = inspectToolResultForCopilot ( settled , secretRegistry ( ) , 'run_workflow' )
204+ const projection = inspectToolResultForCopilot ( settled , registry , 'run_workflow' )
187205
188206 expect ( projection . safe ) . toBe ( true )
189207 const output = projection . result . output as Record < string , unknown >
@@ -235,7 +253,7 @@ describe('run_workflow model-facing result budget', () => {
235253 { workflowId : 'wf-1' , stopAfterBlockId : 'query' } ,
236254 context
237255 )
238- const projection = inspectToolResultForCopilot ( settled , secretRegistry ( ) , 'run_workflow' )
256+ const projection = inspectToolResultForCopilot ( settled , registry , 'run_workflow' )
239257
240258 expect ( projection . safe ) . toBe ( true )
241259 const output = projection . result . output as Record < string , unknown >
@@ -286,7 +304,7 @@ describe('run_workflow model-facing result budget', () => {
286304 } )
287305
288306 const settled = await executeRunWorkflow ( { workflowId : 'wf-1' } , context )
289- const projection = inspectToolResultForCopilot ( settled , secretRegistry ( ) , 'run_workflow' )
307+ const projection = inspectToolResultForCopilot ( settled , registry , 'run_workflow' )
290308
291309 expect ( projection . safe ) . toBe ( true )
292310 const logs = ( projection . result . output as { logs : Array < Record < string , unknown > > } ) . logs
@@ -313,7 +331,7 @@ describe('run_workflow model-facing result budget', () => {
313331 } )
314332
315333 const settled = await executeRunWorkflow ( { workflowId : 'wf-1' } , context )
316- const projection = inspectToolResultForCopilot ( settled , secretRegistry ( ) , 'run_workflow' )
334+ const projection = inspectToolResultForCopilot ( settled , registry , 'run_workflow' )
317335
318336 expect ( projection . safe ) . toBe ( true )
319337 const serialized = JSON . stringify ( projection . result )
@@ -322,4 +340,59 @@ describe('run_workflow model-facing result budget', () => {
322340 expect ( serialized ) . not . toContain ( SECRET . slice ( 0 , length ) )
323341 }
324342 } )
343+
344+ /**
345+ * Without an active secret the projection passes JSON through under its byte cap alone, so
346+ * nothing is bounded: a lifted output is the whole point of run_block and reaches the worker in
347+ * full, which spills an oversized one to storage for the model to read.
348+ */
349+ it ( 'returns a large lifted output and its logs in full when no secret is active' , async ( ) => {
350+ const rows = wideRows ( )
351+ mocks . executeWorkflowUseCase . mockResolvedValue ( {
352+ success : true ,
353+ output : { } ,
354+ logs : [
355+ { blockId : 'start' , blockName : 'Start' , success : true , output : { ok : true } } ,
356+ { blockId : 'query' , blockName : 'Query' , success : true , output : { rows } } ,
357+ ] ,
358+ metadata : { executionId : EXECUTION_ID } ,
359+ } )
360+ const inactive = secretRegistry ( { active : false } )
361+
362+ const settled = await executeRunWorkflowUntilBlock (
363+ { workflowId : 'wf-1' , stopAfterBlockId : 'query' } ,
364+ callContext ( inactive )
365+ )
366+ const projection = inspectToolResultForCopilot ( settled , inactive , 'run_workflow' )
367+
368+ expect ( projection . safe ) . toBe ( true )
369+ const output = projection . result . output as Record < string , unknown >
370+ expect ( output . output ) . toEqual ( { rows } )
371+ expect ( ( output . logs as Array < Record < string , unknown > > ) [ 1 ] ?. output ) . toEqual ( { rows } )
372+ } )
373+
374+ /** A lifted output is the run's final output, so it keeps the final output's larger share. */
375+ it ( 'returns a lifted output within the final share in full while a secret is active' , async ( ) => {
376+ const rows = wideRows ( )
377+ mocks . executeWorkflowUseCase . mockResolvedValue ( {
378+ success : true ,
379+ output : { } ,
380+ logs : [ { blockId : 'query' , blockName : 'Query' , success : true , output : { rows } } ] ,
381+ metadata : { executionId : EXECUTION_ID } ,
382+ } )
383+
384+ const settled = await executeRunWorkflowUntilBlock (
385+ { workflowId : 'wf-1' , stopAfterBlockId : 'query' } ,
386+ context
387+ )
388+ const projection = inspectToolResultForCopilot ( settled , registry , 'run_workflow' )
389+
390+ expect ( projection . safe ) . toBe ( true )
391+ const output = projection . result . output as Record < string , unknown >
392+ expect ( output . output ) . toEqual ( { rows } )
393+ // Its log copy is still bounded, so the two together stay under the projection's caps.
394+ expect ( ( output . logs as Array < Record < string , unknown > > ) [ 0 ] ?. output ) . toEqual (
395+ expect . stringContaining ( `logs get ${ EXECUTION_ID } --trace` )
396+ )
397+ } )
325398} )
0 commit comments