1010 * - subagent_wait: block until the listed subagents settle, return results.
1111 * - subagent_cancel: stop one or more running subagents.
1212 * - subagent_check: peek at a subagent's status and recent activity.
13+ * - subagent_result: read bounded pages of a settled exact result by subagent id.
1314 * - subagent_list: list all subagents.
1415 *
1516 * Unawaited subagents queue their result as a follow-up message when they
@@ -117,6 +118,8 @@ import {
117118 SUBAGENT_CHECK_PARAMETER_DESCRIPTIONS ,
118119 SUBAGENT_CHECK_TOOL_DESCRIPTION ,
119120 SUBAGENT_LIST_TOOL_DESCRIPTION ,
121+ SUBAGENT_RESULT_PARAMETER_DESCRIPTIONS ,
122+ SUBAGENT_RESULT_TOOL_DESCRIPTION ,
120123 SUBAGENT_SEND_PARAMETER_DESCRIPTIONS ,
121124 SUBAGENT_SEND_TOOL_DESCRIPTION ,
122125 SUBAGENT_SPAWN_PROMPT_GUIDELINES ,
@@ -125,9 +128,14 @@ import {
125128 SUBAGENT_WAIT_TOOL_DESCRIPTION ,
126129} from "./src/prompt.ts" ;
127130import {
131+ isResultArtifactRef ,
132+ MAX_RESULT_PAGE_BYTES ,
133+ MAX_RESULT_PAGE_LINES ,
134+ pageResultText ,
128135 persistResultArtifact ,
129136 projectResult ,
130137 readResultArtifact ,
138+ resolveExactResultText ,
131139} from "./src/result-artifact.ts" ;
132140import { createSubagentResultDelivery } from "./src/result-delivery.ts" ;
133141import {
@@ -184,7 +192,7 @@ interface SubagentProjectionDetails {
184192 readonly truncated : boolean ;
185193 readonly omittedBytes : number ;
186194 readonly omitted : NonNullable < SubagentSnapshot [ "snapshot" ] > [ "omitted" ] ;
187- readonly resultArtifact ?: string ;
195+ readonly exactResultAvailable ?: boolean ;
188196}
189197
190198interface SubagentResultEntryData {
@@ -214,39 +222,45 @@ function describeSubagent(snap: SubagentSnapshot) {
214222}
215223
216224function exactResultText ( snap : SubagentSnapshot ) : string | undefined {
217- if ( ! snap . resultArtifact ) return undefined ;
218- return readResultArtifact ( snap . resultArtifact ) ;
225+ if ( ! isResultArtifactRef ( snap . resultArtifact ) ) return undefined ;
226+ return readResultArtifact ( getAgentDir ( ) , snap . resultArtifact ) ;
219227}
220228
221229function resultText ( snap : SubagentSnapshot ) : string {
222230 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- }
231+ // Retention and best-effort writes can make a prior recovery path disappear.
232+ // A cache miss must degrade to the retained result, never suppress delivery.
228233 return artifact !== undefined
229234 ? artifact || "(no output)"
230235 : snap . finalText || "(no output)" ;
231236}
232237
238+ function withCanonicalResult (
239+ snap : SubagentSnapshot ,
240+ result : Pick < SubagentSnapshot , "finalText" | "resultArtifact" > | undefined ,
241+ ) {
242+ if ( ! result ) return { snap, resultIsCanonical : false } ;
243+ return {
244+ snap : {
245+ ...snap ,
246+ finalText : result . finalText ,
247+ resultArtifact : result . resultArtifact ,
248+ } ,
249+ resultIsCanonical : true ,
250+ } ;
251+ }
252+
233253function projectionDetails (
234254 snap : SubagentSnapshot ,
235255) : SubagentProjectionDetails | undefined {
236256 const projection = snap . snapshot ;
237- if ( ! projection ?. truncated && ! snap . resultArtifact ) return undefined ;
257+ if ( ! projection ?. truncated ) return undefined ;
258+ const exactResultAvailable = exactResultText ( snap ) !== undefined ;
238259 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 } : { } ) ,
260+ truncated : true ,
261+ omittedBytes : projection . omittedBytes ,
262+ omitted : projection . omitted ,
263+ ...( exactResultAvailable ? { exactResultAvailable : true } : { } ) ,
250264 } ;
251265}
252266
@@ -266,39 +280,42 @@ function projectionNotice(snap: SubagentSnapshot): string | undefined {
266280 projection . omitted . finalTextBytes > 0 ? "final output" : undefined ,
267281 ] . filter ( ( value ) : value is string => value !== undefined ) ;
268282 const detail = omitted . length > 0 ? omitted . join ( ", " ) : "display data" ;
269- return `snapshot truncated: ${ detail } omitted${ snap . resultArtifact ? "; exact result artifact available" : "" } ` ;
283+ const hasExactArtifact = exactResultText ( snap ) !== undefined ;
284+ return `snapshot truncated: ${ detail } omitted${ hasExactArtifact ? "; exact result artifact available" : "" } ` ;
270285}
271286
272287export function truncatedOutput (
273288 snap : SubagentSnapshot ,
274289 maxBytes = SUBAGENT_OUTPUT_MAX_BYTES ,
275- writeArtifact : ( content : string ) => string = ( content ) =>
290+ writeArtifact : ( content : string ) => unknown = ( content ) =>
276291 persistResultArtifact ( getAgentDir ( ) , content ) ,
292+ resultOptions : { readonly resultIsCanonical ?: boolean } = { } ,
277293) : string {
278- const artifactPath = snap . resultArtifact ;
279294 const artifact = exactResultText ( snap ) ;
280- if ( artifactPath && artifact === undefined ) {
281- throw new Error (
282- `The exact subagent result artifact is unavailable: ${ artifactPath } ` ,
283- ) ;
284- }
285295 const output =
286- artifact !== undefined ? artifact || "(no output)" : snap . finalText || "(no output)" ;
296+ artifact !== undefined
297+ ? artifact || "(no output)"
298+ : snap . finalText || "(no output)" ;
287299 // A projected finalText is not authoritative. Do not create a second
288300 // 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
301+ // unavailable. A cache miss with a complete retained result can safely
302+ // repopulate the cache on demand.
303+ const finalTextWasOmitted =
304+ ! resultOptions . resultIsCanonical &&
305+ ( snap . snapshot ?. omitted . finalTextBytes ?? 0 ) > 0 ;
306+ const artifactAvailable = artifact !== undefined ;
307+ const persist = artifactAvailable
308+ ? ( ) => undefined
309+ : ! finalTextWasOmitted
310+ ? writeArtifact
296311 : ( ) => {
297312 throw new Error ( "The exact subagent result artifact is unavailable" ) ;
298313 } ;
299314 return projectResult ( output , {
300315 maxBytes : Math . min ( maxBytes , DEFAULT_MAX_BYTES ) ,
301316 maxLines : Math . min ( 600 , DEFAULT_MAX_LINES ) ,
317+ recoveryId : snap . id ,
318+ artifactAvailable,
302319 writeArtifact : persist ,
303320 } ) . text ;
304321}
@@ -1003,16 +1020,23 @@ export default function (pi: ExtensionAPI) {
10031020 readonly id : string ;
10041021 readonly snap : SubagentSnapshot ;
10051022 readonly header : string ;
1023+ readonly resultIsCanonical : boolean ;
10061024 }
10071025 > = ids . map ( ( id ) => {
10081026 const snap = manager . view . get ( id ) ;
10091027 if ( ! snap ) return { id, section : `## ${ id } \n\n(no longer tracked)` } ;
1028+ const result = withCanonicalResult ( snap , manager . view . getResult ?.( id ) ) ;
10101029 const verb = snap . status === "error" ? "failed" : "finished" ;
10111030 let header = `## ${ snap . id } "${ snap . title } " ${ verb } ` ;
10121031 if ( snap . errorText ) header += `\nError: ${ snap . errorText } ` ;
10131032 const projection = projectionNotice ( snap ) ;
10141033 if ( projection ) header += `\n[${ projection } ]` ;
1015- return { id, snap, header } ;
1034+ return {
1035+ id,
1036+ snap : result . snap ,
1037+ header,
1038+ resultIsCanonical : result . resultIsCanonical ,
1039+ } ;
10161040 } ) ;
10171041 const separatorsBytes = Math . max ( 0 , entries . length - 1 ) * 7 ;
10181042 const fixedBytes =
@@ -1033,14 +1057,17 @@ export default function (pi: ExtensionAPI) {
10331057 readonly id : string ;
10341058 readonly snap : SubagentSnapshot ;
10351059 readonly header : string ;
1060+ readonly resultIsCanonical : boolean ;
10361061 } => "snap" in entry ,
10371062 ) ;
10381063 const projectionBatchBytes = Math . max (
10391064 WAIT_MIN_RESULT_BYTES * resultEntries . length ,
10401065 WAIT_OUTPUT_MAX_BYTES - fixedBytes ,
10411066 ) ;
10421067 const allocation = allocateResultBudgets (
1043- resultEntries . map ( ( { snap } ) => Buffer . byteLength ( resultText ( snap ) , "utf8" ) ) ,
1068+ resultEntries . map ( ( { snap } ) =>
1069+ Buffer . byteLength ( resultText ( snap ) , "utf8" ) ,
1070+ ) ,
10441071 ctx . getContextUsage ( ) ,
10451072 {
10461073 maxBatchBytes : projectionBatchBytes ,
@@ -1055,7 +1082,12 @@ export default function (pi: ExtensionAPI) {
10551082 const sections = entries . map ( ( entry ) => {
10561083 if ( "section" in entry ) return entry . section ;
10571084 const outputBudget = allocation . budgets [ resultIndex ++ ] ! ;
1058- return `${ entry . header } \n\n${ truncatedOutput ( entry . snap , outputBudget ) } ` ;
1085+ return `${ entry . header } \n\n${ truncatedOutput (
1086+ entry . snap ,
1087+ outputBudget ,
1088+ undefined ,
1089+ { resultIsCanonical : entry . resultIsCanonical } ,
1090+ ) } `;
10591091 } ) ;
10601092
10611093 const combined = sections . join ( "\n\n---\n\n" ) ;
@@ -1244,14 +1276,21 @@ export default function (pi: ExtensionAPI) {
12441276 let text = `${ describeSubagent ( snap ) } \nTurns: ${ snap . turns } ` ;
12451277 if ( snap . errorText ) text += `\nError: ${ snap . errorText } ` ;
12461278
1279+ const result =
1280+ snap . status === "running"
1281+ ? undefined
1282+ : withCanonicalResult ( snap , manager . view . getResult ?.( snap . id ) ) ;
1283+ const resultSnap = result ?. snap ?? snap ;
12471284 const output =
1248- snap . status === "running" ? latestText ( snap ) : resultText ( snap ) ;
1285+ snap . status === "running" ? latestText ( snap ) : resultText ( resultSnap ) ;
12491286 if ( output && output !== "(no output)" ) {
12501287 const preview =
12511288 snap . status === "running"
12521289 ? truncateHead ( output , { maxBytes : 2048 , maxLines : 20 } )
12531290 : ( ( ) => {
1254- const content = truncatedOutput ( snap , 2048 ) ;
1291+ const content = truncatedOutput ( resultSnap , 2048 , undefined , {
1292+ resultIsCanonical : result ?. resultIsCanonical ,
1293+ } ) ;
12551294 return { content, truncated : content !== output } ;
12561295 } ) ( ) ;
12571296 text += `\n\nLatest output:\n${ preview . content } ` ;
@@ -1274,6 +1313,84 @@ export default function (pi: ExtensionAPI) {
12741313 } ,
12751314 } ) ;
12761315
1316+ pi . registerTool ( {
1317+ name : "subagent_result" ,
1318+ label : "Read Subagent Result" ,
1319+ description : SUBAGENT_RESULT_TOOL_DESCRIPTION ,
1320+ parameters : Type . Object ( {
1321+ id : Type . String ( {
1322+ description : SUBAGENT_RESULT_PARAMETER_DESCRIPTIONS . id ,
1323+ } ) ,
1324+ offset : Type . Optional (
1325+ Type . Integer ( {
1326+ minimum : 0 ,
1327+ description : SUBAGENT_RESULT_PARAMETER_DESCRIPTIONS . offset ,
1328+ } ) ,
1329+ ) ,
1330+ limit : Type . Optional (
1331+ Type . Integer ( {
1332+ minimum : 1 ,
1333+ maximum : MAX_RESULT_PAGE_LINES ,
1334+ description : SUBAGENT_RESULT_PARAMETER_DESCRIPTIONS . limit ,
1335+ } ) ,
1336+ ) ,
1337+ } ) ,
1338+ async execute ( _toolCallId , params ) {
1339+ const manager = await getManager ( ) ;
1340+ const snap = manager . view . get ( params . id ) ;
1341+ if ( ! snap || ! isModelVisible ( snap ) ) {
1342+ const known = manager . view
1343+ . list ( )
1344+ . filter ( isModelVisible )
1345+ . map ( ( s ) => s . id ) ;
1346+ throw new Error (
1347+ `Unknown subagent id "${ params . id } ". Known: ${ known . join ( ", " ) || "none" } .` ,
1348+ ) ;
1349+ }
1350+ if ( snap . status === "running" ) {
1351+ throw new Error (
1352+ `Subagent ${ snap . id } is still running; use subagent_wait or wait for automatic delivery.` ,
1353+ ) ;
1354+ }
1355+
1356+ const canonical = withCanonicalResult (
1357+ snap ,
1358+ manager . view . getResult ?.( snap . id ) ,
1359+ ) ;
1360+ const artifact = exactResultText ( canonical . snap ) ;
1361+ // Prefer the protected artifact. Fall back to retained canonical
1362+ // finalText, never to a truncated projection.
1363+ const exact = resolveExactResultText ( {
1364+ artifactText : artifact ,
1365+ retainedFinalText : canonical . snap . finalText ,
1366+ resultIsCanonical : canonical . resultIsCanonical ,
1367+ omittedFinalTextBytes :
1368+ canonical . snap . snapshot ?. omitted . finalTextBytes ?? 0 ,
1369+ } ) ;
1370+ if ( exact === undefined ) {
1371+ throw new Error (
1372+ `Exact result for ${ snap . id } is unavailable; the bounded projection is not a recovery source.` ,
1373+ ) ;
1374+ }
1375+ const page = pageResultText ( exact , {
1376+ offset : params . offset ,
1377+ limit : params . limit ,
1378+ maxBytes : MAX_RESULT_PAGE_BYTES ,
1379+ } ) ;
1380+ return {
1381+ content : [ { type : "text" , text : page . text } ] ,
1382+ details : {
1383+ id : snap . id ,
1384+ offset : page . offset ,
1385+ limit : page . limit ,
1386+ totalLines : page . totalLines ,
1387+ hasMore : page . hasMore ,
1388+ ...( artifact !== undefined ? { exactResultAvailable : true } : { } ) ,
1389+ } ,
1390+ } ;
1391+ } ,
1392+ } ) ;
1393+
12771394 pi . registerTool ( {
12781395 name : "subagent_list" ,
12791396 label : "List Subagents" ,
0 commit comments