@@ -67,6 +67,10 @@ import {
6767 planModeAllowsDeclaredTools ,
6868 planModeChildTools ,
6969} from "../shared/plan-mode-state.ts" ;
70+ import {
71+ allocateResultBudgets ,
72+ type ParentContextUsage ,
73+ } from "../shared/result-budget.ts" ;
7074import { loadSetupConfig } from "../shared/setup-config.ts" ;
7175import {
7276 OPENPI_TOOL_SURFACE ,
@@ -120,11 +124,11 @@ import {
120124 SUBAGENT_WAIT_PARAMETER_DESCRIPTIONS ,
121125 SUBAGENT_WAIT_TOOL_DESCRIPTION ,
122126} from "./src/prompt.ts" ;
123- import { persistResultArtifact , projectResult } from "./src/result-artifact.ts" ;
124127import {
125- allocateResultBudgets ,
126- type ParentContextUsage ,
127- } from "../shared/result-budget.ts" ;
128+ persistResultArtifact ,
129+ projectResult ,
130+ readResultArtifact ,
131+ } from "./src/result-artifact.ts" ;
128132import { createSubagentResultDelivery } from "./src/result-delivery.ts" ;
129133import {
130134 createSubagentRuntime ,
@@ -167,13 +171,22 @@ interface SubagentResultDetails {
167171 readonly title ?: string ;
168172 readonly status ?: SubagentSnapshot [ "status" ] ;
169173 readonly count ?: number ;
174+ readonly projection ?: SubagentProjectionDetails ;
170175 readonly results ?: ReadonlyArray < {
171176 readonly id : string ;
172177 readonly title : string ;
173178 readonly status : SubagentSnapshot [ "status" ] ;
179+ readonly projection ?: SubagentProjectionDetails ;
174180 } > ;
175181}
176182
183+ interface SubagentProjectionDetails {
184+ readonly truncated : boolean ;
185+ readonly omittedBytes : number ;
186+ readonly omitted : NonNullable < SubagentSnapshot [ "snapshot" ] > [ "omitted" ] ;
187+ readonly resultArtifact ?: string ;
188+ }
189+
177190interface SubagentResultEntryData {
178191 readonly content : string ;
179192 readonly details : SubagentResultDetails ;
@@ -196,7 +209,64 @@ function describeSubagent(snap: SubagentSnapshot) {
196209 formatElapsed ( snap ) ,
197210 snap . cwd ,
198211 ] . filter ( Boolean ) ;
199- return `${ snap . id } [${ snap . status } ] "${ snap . title } " (${ details . join ( ", " ) } )` ;
212+ const projection = projectionNotice ( snap ) ;
213+ return `${ snap . id } [${ snap . status } ] "${ snap . title } " (${ details . join ( ", " ) } )${ projection ? ` · ${ projection } ` : "" } ` ;
214+ }
215+
216+ function exactResultText ( snap : SubagentSnapshot ) : string | undefined {
217+ if ( ! snap . resultArtifact ) return undefined ;
218+ return readResultArtifact ( snap . resultArtifact ) ;
219+ }
220+
221+ function resultText ( snap : SubagentSnapshot ) : string {
222+ const artifact = exactResultText ( snap ) ;
223+ if ( snap . resultArtifact && artifact === undefined ) {
224+ throw new Error (
225+ `The exact subagent result artifact is unavailable: ${ snap . resultArtifact } ` ,
226+ ) ;
227+ }
228+ return artifact !== undefined
229+ ? artifact || "(no output)"
230+ : snap . finalText || "(no output)" ;
231+ }
232+
233+ function projectionDetails (
234+ snap : SubagentSnapshot ,
235+ ) : SubagentProjectionDetails | undefined {
236+ const projection = snap . snapshot ;
237+ if ( ! projection ?. truncated && ! snap . resultArtifact ) return undefined ;
238+ return {
239+ truncated : projection ?. truncated ?? false ,
240+ omittedBytes : projection ?. omittedBytes ?? 0 ,
241+ omitted : projection ?. omitted ?? {
242+ transcriptItems : 0 ,
243+ liveTools : 0 ,
244+ queued : 0 ,
245+ liveAssistantBytes : 0 ,
246+ finalTextBytes : 0 ,
247+ promptBytes : 0 ,
248+ } ,
249+ ...( snap . resultArtifact ? { resultArtifact : snap . resultArtifact } : { } ) ,
250+ } ;
251+ }
252+
253+ function projectionNotice ( snap : SubagentSnapshot ) : string | undefined {
254+ const projection = snap . snapshot ;
255+ if ( ! projection ?. truncated ) return undefined ;
256+ const omitted = [
257+ projection . omitted . transcriptItems > 0
258+ ? `${ projection . omitted . transcriptItems } transcript item(s)`
259+ : undefined ,
260+ projection . omitted . liveTools > 0
261+ ? `${ projection . omitted . liveTools } live tool(s)`
262+ : undefined ,
263+ projection . omitted . queued > 0
264+ ? `${ projection . omitted . queued } queued message(s)`
265+ : undefined ,
266+ projection . omitted . finalTextBytes > 0 ? "final output" : undefined ,
267+ ] . filter ( ( value ) : value is string => value !== undefined ) ;
268+ const detail = omitted . length > 0 ? omitted . join ( ", " ) : "display data" ;
269+ return `snapshot truncated: ${ detail } omitted${ snap . resultArtifact ? "; exact result artifact available" : "" } ` ;
200270}
201271
202272export function truncatedOutput (
@@ -205,11 +275,31 @@ export function truncatedOutput(
205275 writeArtifact : ( content : string ) => string = ( content ) =>
206276 persistResultArtifact ( getAgentDir ( ) , content ) ,
207277) : string {
208- const output = snap . finalText || "(no output)" ;
278+ const artifactPath = snap . resultArtifact ;
279+ const artifact = exactResultText ( snap ) ;
280+ if ( artifactPath && artifact === undefined ) {
281+ throw new Error (
282+ `The exact subagent result artifact is unavailable: ${ artifactPath } ` ,
283+ ) ;
284+ }
285+ const output =
286+ artifact !== undefined ? artifact || "(no output)" : snap . finalText || "(no output)" ;
287+ // A projected finalText is not authoritative. Do not create a second
288+ // artifact containing only that projection when the original artifact is
289+ // unavailable.
290+ const finalTextWasOmitted = ( snap . snapshot ?. omitted . finalTextBytes ?? 0 ) > 0 ;
291+ const persist =
292+ artifact !== undefined || ! finalTextWasOmitted
293+ ? artifact !== undefined && artifactPath
294+ ? ( ) => artifactPath
295+ : writeArtifact
296+ : ( ) => {
297+ throw new Error ( "The exact subagent result artifact is unavailable" ) ;
298+ } ;
209299 return projectResult ( output , {
210300 maxBytes : Math . min ( maxBytes , DEFAULT_MAX_BYTES ) ,
211301 maxLines : Math . min ( 600 , DEFAULT_MAX_LINES ) ,
212- writeArtifact,
302+ writeArtifact : persist ,
213303 } ) . text ;
214304}
215305
@@ -243,9 +333,7 @@ export function createSubagentResultDispatcher(
243333 AUTOMATIC_OUTPUT_MAX_BYTES - wrapperBytes ,
244334 ) ;
245335 const allocation = allocateResultBudgets (
246- snaps . map ( ( snap ) =>
247- Buffer . byteLength ( snap . finalText || "(no output)" , "utf8" ) ,
248- ) ,
336+ snaps . map ( ( snap ) => Buffer . byteLength ( resultText ( snap ) , "utf8" ) ) ,
249337 getContextUsage ( ) ,
250338 {
251339 maxBatchBytes : projectionBatchBytes ,
@@ -269,18 +357,26 @@ export function createSubagentResultDispatcher(
269357 . join ( "\n\n" ) ;
270358 const details : SubagentResultDetails =
271359 snaps . length === 1
272- ? {
273- id : snaps [ 0 ] ! . id ,
274- title : snaps [ 0 ] ! . title ,
275- status : snaps [ 0 ] ! . status ,
276- }
360+ ? ( ( ) => {
361+ const projection = projectionDetails ( snaps [ 0 ] ! ) ;
362+ return {
363+ id : snaps [ 0 ] ! . id ,
364+ title : snaps [ 0 ] ! . title ,
365+ status : snaps [ 0 ] ! . status ,
366+ ...( projection ? { projection } : { } ) ,
367+ } ;
368+ } ) ( )
277369 : {
278370 count : snaps . length ,
279- results : snaps . map ( ( snap ) => ( {
280- id : snap . id ,
281- title : snap . title ,
282- status : snap . status ,
283- } ) ) ,
371+ results : snaps . map ( ( snap ) => {
372+ const projection = projectionDetails ( snap ) ;
373+ return {
374+ id : snap . id ,
375+ title : snap . title ,
376+ status : snap . status ,
377+ ...( projection ? { projection } : { } ) ,
378+ } ;
379+ } ) ,
284380 } ;
285381 pi . appendEntry < SubagentResultEntryData > ( "subagent-result" , {
286382 content,
@@ -391,6 +487,8 @@ export default function (pi: ExtensionAPI) {
391487 ( runtime ??= createSubagentRuntime ( {
392488 initialModelCounter : restoredIdCounters . modelCounter ,
393489 initialBtwCounter : restoredIdCounters . btwCounter ,
490+ persistResultArtifact : ( content ) =>
491+ persistResultArtifact ( getAgentDir ( ) , content ) ,
394492 } ) ) ;
395493
396494 const persistId = ( id : string ) =>
@@ -912,6 +1010,8 @@ export default function (pi: ExtensionAPI) {
9121010 const verb = snap . status === "error" ? "failed" : "finished" ;
9131011 let header = `## ${ snap . id } "${ snap . title } " ${ verb } ` ;
9141012 if ( snap . errorText ) header += `\nError: ${ snap . errorText } ` ;
1013+ const projection = projectionNotice ( snap ) ;
1014+ if ( projection ) header += `\n[${ projection } ]` ;
9151015 return { id, snap, header } ;
9161016 } ) ;
9171017 const separatorsBytes = Math . max ( 0 , entries . length - 1 ) * 7 ;
@@ -940,9 +1040,7 @@ export default function (pi: ExtensionAPI) {
9401040 WAIT_OUTPUT_MAX_BYTES - fixedBytes ,
9411041 ) ;
9421042 const allocation = allocateResultBudgets (
943- resultEntries . map ( ( { snap } ) =>
944- Buffer . byteLength ( snap . finalText || "(no output)" , "utf8" ) ,
945- ) ,
1043+ resultEntries . map ( ( { snap } ) => Buffer . byteLength ( resultText ( snap ) , "utf8" ) ) ,
9461044 ctx . getContextUsage ( ) ,
9471045 {
9481046 maxBatchBytes : projectionBatchBytes ,
@@ -973,7 +1071,13 @@ export default function (pi: ExtensionAPI) {
9731071 details : {
9741072 results : ids . map ( ( id ) => {
9751073 const snap = manager . view . get ( id ) ;
976- return { id, title : snap ?. title , status : snap ?. status } ;
1074+ const projection = snap ? projectionDetails ( snap ) : undefined ;
1075+ return {
1076+ id,
1077+ title : snap ?. title ,
1078+ status : snap ?. status ,
1079+ ...( projection ? { projection } : { } ) ,
1080+ } ;
9771081 } ) ,
9781082 } ,
9791083 } ;
@@ -1140,9 +1244,16 @@ export default function (pi: ExtensionAPI) {
11401244 let text = `${ describeSubagent ( snap ) } \nTurns: ${ snap . turns } ` ;
11411245 if ( snap . errorText ) text += `\nError: ${ snap . errorText } ` ;
11421246
1143- const output = latestText ( snap ) ;
1144- if ( output ) {
1145- const preview = truncateHead ( output , { maxBytes : 2048 , maxLines : 20 } ) ;
1247+ const output =
1248+ snap . status === "running" ? latestText ( snap ) : resultText ( snap ) ;
1249+ if ( output && output !== "(no output)" ) {
1250+ const preview =
1251+ snap . status === "running"
1252+ ? truncateHead ( output , { maxBytes : 2048 , maxLines : 20 } )
1253+ : ( ( ) => {
1254+ const content = truncatedOutput ( snap , 2048 ) ;
1255+ return { content, truncated : content !== output } ;
1256+ } ) ( ) ;
11461257 text += `\n\nLatest output:\n${ preview . content } ` ;
11471258 if ( preview . truncated ) text += "\n[...]" ;
11481259 } else if ( snap . status === "running" ) {
@@ -1151,7 +1262,14 @@ export default function (pi: ExtensionAPI) {
11511262
11521263 return {
11531264 content : [ { type : "text" , text } ] ,
1154- details : { id : snap . id , status : snap . status , turns : snap . turns } ,
1265+ details : {
1266+ id : snap . id ,
1267+ status : snap . status ,
1268+ turns : snap . turns ,
1269+ ...( projectionDetails ( snap )
1270+ ? { projection : projectionDetails ( snap ) }
1271+ : { } ) ,
1272+ } ,
11551273 } ;
11561274 } ,
11571275 } ) ;
@@ -1171,12 +1289,16 @@ export default function (pi: ExtensionAPI) {
11711289 return {
11721290 content : [ { type : "text" , text } ] ,
11731291 details : {
1174- subagents : subs . map ( ( snap ) => ( {
1175- id : snap . id ,
1176- title : snap . title ,
1177- harness : snap . backend ,
1178- status : snap . status ,
1179- } ) ) ,
1292+ subagents : subs . map ( ( snap ) => {
1293+ const projection = projectionDetails ( snap ) ;
1294+ return {
1295+ id : snap . id ,
1296+ title : snap . title ,
1297+ harness : snap . backend ,
1298+ status : snap . status ,
1299+ ...( projection ? { projection } : { } ) ,
1300+ } ;
1301+ } ) ,
11801302 } ,
11811303 } ;
11821304 } ,
0 commit comments