dominds 1.16.3 → 1.16.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (175) hide show
  1. package/dist/dialog-display-state.d.ts +1 -0
  2. package/dist/dialog-display-state.js +71 -2
  3. package/dist/dialog-fork.js +9 -0
  4. package/dist/dialog.d.ts +3 -0
  5. package/dist/dialog.js +4 -0
  6. package/dist/docs/mcp-support.md +6 -2
  7. package/dist/llm/api-quirks.js +34 -6
  8. package/dist/llm/client.d.ts +1 -1
  9. package/dist/llm/defaults.yaml +6 -3
  10. package/dist/llm/gen/anthropic.d.ts +3 -3
  11. package/dist/llm/gen/anthropic.js +115 -21
  12. package/dist/llm/gen/codex.js +78 -13
  13. package/dist/llm/gen/failure-classifier.d.ts +0 -2
  14. package/dist/llm/gen/failure-classifier.js +102 -25
  15. package/dist/llm/gen/openai-compatible.d.ts +6 -3
  16. package/dist/llm/gen/openai-compatible.js +132 -16
  17. package/dist/llm/gen/openai.d.ts +2 -2
  18. package/dist/llm/gen/openai.js +95 -15
  19. package/dist/llm/gen/tool-result-image-ingest.d.ts +39 -0
  20. package/dist/llm/gen/tool-result-image-ingest.js +158 -0
  21. package/dist/llm/gen.d.ts +17 -1
  22. package/dist/llm/kernel-driver/drive.js +23 -8
  23. package/dist/llm/kernel-driver/runtime.d.ts +1 -1
  24. package/dist/llm/kernel-driver/runtime.js +63 -34
  25. package/dist/persistence.d.ts +2 -0
  26. package/dist/persistence.js +107 -0
  27. package/dist/priming.js +76 -0
  28. package/dist/server/websocket-handler.js +90 -3
  29. package/dist/tools/team_mgmt.js +2 -2
  30. package/package.json +4 -4
  31. package/webapp/dist/assets/{_basePickBy-BJWCmtUm.js → _basePickBy-B7FV6Gnn.js} +3 -3
  32. package/webapp/dist/assets/_basePickBy-B7FV6Gnn.js.map +1 -0
  33. package/webapp/dist/assets/{_baseUniq-D6k_lGfs.js → _baseUniq-CmVnLJpw.js} +2 -2
  34. package/webapp/dist/assets/_baseUniq-CmVnLJpw.js.map +1 -0
  35. package/webapp/dist/assets/{arc-DYDaESgj.js → arc-DSJlh9AU.js} +2 -2
  36. package/webapp/dist/assets/arc-DSJlh9AU.js.map +1 -0
  37. package/webapp/dist/assets/{architectureDiagram-VXUJARFQ-DZMT7dqc.js → architectureDiagram-2XIMDMQ5-Cd2cTHzo.js} +26 -8
  38. package/webapp/dist/assets/architectureDiagram-2XIMDMQ5-Cd2cTHzo.js.map +1 -0
  39. package/webapp/dist/assets/{blockDiagram-VD42YOAC-CABpgVAa.js → blockDiagram-WCTKOSBZ-C0uEp_Tz.js} +187 -170
  40. package/webapp/dist/assets/blockDiagram-WCTKOSBZ-C0uEp_Tz.js.map +1 -0
  41. package/webapp/dist/assets/{c4Diagram-YG6GDRKO-COLZS8Ul.js → c4Diagram-IC4MRINW-B37U92JK.js} +4 -4
  42. package/webapp/dist/assets/c4Diagram-IC4MRINW-B37U92JK.js.map +1 -0
  43. package/webapp/dist/assets/{channel-CYFm9Cri.js → channel-DLHnjnQf.js} +2 -2
  44. package/webapp/dist/assets/channel-DLHnjnQf.js.map +1 -0
  45. package/webapp/dist/assets/{chunk-4BX2VUAB-CX_-XbaN.js → chunk-4BX2VUAB-DnZvfQyp.js} +2 -2
  46. package/webapp/dist/assets/chunk-4BX2VUAB-DnZvfQyp.js.map +1 -0
  47. package/webapp/dist/assets/{chunk-55IACEB6-ByD-NdBC.js → chunk-55IACEB6-BG-cjz3M.js} +2 -2
  48. package/webapp/dist/assets/chunk-55IACEB6-BG-cjz3M.js.map +1 -0
  49. package/webapp/dist/assets/{chunk-FMBD7UC4-DYGviJnf.js → chunk-FMBD7UC4-B1L8cPfl.js} +2 -2
  50. package/webapp/dist/assets/chunk-FMBD7UC4-B1L8cPfl.js.map +1 -0
  51. package/webapp/dist/assets/{chunk-TZMSLE5B-_HISzxl3.js → chunk-JSJVCQXG-C65w23ZF.js} +14 -6
  52. package/webapp/dist/assets/chunk-JSJVCQXG-C65w23ZF.js.map +1 -0
  53. package/webapp/dist/assets/{chunk-QN33PNHL-B8DWRL9f.js → chunk-KX2RTZJC-_4YSMrEL.js} +2 -2
  54. package/webapp/dist/assets/chunk-KX2RTZJC-_4YSMrEL.js.map +1 -0
  55. package/webapp/dist/assets/{chunk-DI55MBZ5-DV7sdJmQ.js → chunk-NQ4KR5QH-ComURSQb.js} +9 -7
  56. package/webapp/dist/assets/chunk-NQ4KR5QH-ComURSQb.js.map +1 -0
  57. package/webapp/dist/assets/{chunk-QZHKN3VN-B1aYIzMR.js → chunk-QZHKN3VN-BrxvuRI6.js} +2 -2
  58. package/webapp/dist/assets/chunk-QZHKN3VN-BrxvuRI6.js.map +1 -0
  59. package/webapp/dist/assets/{chunk-B4BG7PRW-BANemsDD.js → chunk-WL4C6EOR-CW7tAF_z.js} +171 -121
  60. package/webapp/dist/assets/chunk-WL4C6EOR-CW7tAF_z.js.map +1 -0
  61. package/webapp/dist/assets/{classDiagram-2ON5EDUG-CpyYj1Rc.js → classDiagram-VBA2DB6C-BWrPbwfd.js} +7 -6
  62. package/webapp/dist/assets/classDiagram-VBA2DB6C-BWrPbwfd.js.map +1 -0
  63. package/webapp/dist/assets/{classDiagram-v2-WZHVMYZB-CpyYj1Rc.js → classDiagram-v2-RAHNMMFH-BWrPbwfd.js} +7 -6
  64. package/webapp/dist/assets/classDiagram-v2-RAHNMMFH-BWrPbwfd.js.map +1 -0
  65. package/webapp/dist/assets/{clone-B_9AxWIU.js → clone-B-TYPsxN.js} +2 -2
  66. package/webapp/dist/assets/clone-B-TYPsxN.js.map +1 -0
  67. package/webapp/dist/assets/{cose-bilkent-S5V4N54A-17ECLfPR.js → cose-bilkent-S5V4N54A-C1bfZcVY.js} +2 -2
  68. package/webapp/dist/assets/cose-bilkent-S5V4N54A-C1bfZcVY.js.map +1 -0
  69. package/webapp/dist/assets/cytoscape.esm-Bm8DJGmZ.js.map +1 -1
  70. package/webapp/dist/assets/{dagre-6UL2VRFP-DhRqcF1o.js → dagre-KLK3FWXG-BfD08d2e.js} +7 -7
  71. package/webapp/dist/assets/dagre-KLK3FWXG-BfD08d2e.js.map +1 -0
  72. package/webapp/dist/assets/defaultLocale-B2RvLBDe.js.map +1 -1
  73. package/webapp/dist/assets/{diagram-PSM6KHXK-lpDT6Wwb.js → diagram-E7M64L7V-Dyd9OucT.js} +10 -10
  74. package/webapp/dist/assets/diagram-E7M64L7V-Dyd9OucT.js.map +1 -0
  75. package/webapp/dist/assets/{diagram-QEK2KX5R-ohxbPpDH.js → diagram-IFDJBPK2-BN-JCceb.js} +9 -8
  76. package/webapp/dist/assets/diagram-IFDJBPK2-BN-JCceb.js.map +1 -0
  77. package/webapp/dist/assets/{diagram-S2PKOQOG-DAFFGfch.js → diagram-P4PSJMXO-C2jh_Kry.js} +8 -8
  78. package/webapp/dist/assets/diagram-P4PSJMXO-C2jh_Kry.js.map +1 -0
  79. package/webapp/dist/assets/{erDiagram-Q2GNP2WA-BH-7rI43.js → erDiagram-INFDFZHY-C3Pj10OJ.js} +96 -75
  80. package/webapp/dist/assets/erDiagram-INFDFZHY-C3Pj10OJ.js.map +1 -0
  81. package/webapp/dist/assets/{flowDiagram-NV44I4VS-CdEjFbz4.js → flowDiagram-PKNHOUZH-a5vzSSCo.js} +98 -81
  82. package/webapp/dist/assets/flowDiagram-PKNHOUZH-a5vzSSCo.js.map +1 -0
  83. package/webapp/dist/assets/{ganttDiagram-JELNMOA3-Cju2t-lK.js → ganttDiagram-A5KZAMGK-DIua0Qjr.js} +28 -3
  84. package/webapp/dist/assets/ganttDiagram-A5KZAMGK-DIua0Qjr.js.map +1 -0
  85. package/webapp/dist/assets/{gitGraphDiagram-V2S2FVAM-CUJ5oxCg.js → gitGraphDiagram-K3NZZRJ6-CJt16FXK.js} +38 -46
  86. package/webapp/dist/assets/gitGraphDiagram-K3NZZRJ6-CJt16FXK.js.map +1 -0
  87. package/webapp/dist/assets/graph-lQSuHjYm.js +782 -0
  88. package/webapp/dist/assets/graph-lQSuHjYm.js.map +1 -0
  89. package/webapp/dist/assets/{index-BLnM-uON.js → index-B7llu28V.js} +1279 -1059
  90. package/webapp/dist/assets/{index-BLnM-uON.js.map → index-B7llu28V.js.map} +1 -1
  91. package/webapp/dist/assets/{index-xvYYeHuy.css → index-YaxF76or.css} +1 -1
  92. package/webapp/dist/assets/{infoDiagram-HS3SLOUP-Df8p0okQ.js → infoDiagram-LFFYTUFH-DzJuXz5H.js} +7 -7
  93. package/webapp/dist/assets/infoDiagram-LFFYTUFH-DzJuXz5H.js.map +1 -0
  94. package/webapp/dist/assets/init-ZxktEp_H.js.map +1 -1
  95. package/webapp/dist/assets/ishikawaDiagram-PHBUUO56-BP_s17vw.js +966 -0
  96. package/webapp/dist/assets/ishikawaDiagram-PHBUUO56-BP_s17vw.js.map +1 -0
  97. package/webapp/dist/assets/{journeyDiagram-XKPGCS4Q-BXMl8H-d.js → journeyDiagram-4ABVD52K-Lic1mhBM.js} +5 -5
  98. package/webapp/dist/assets/journeyDiagram-4ABVD52K-Lic1mhBM.js.map +1 -0
  99. package/webapp/dist/assets/{kanban-definition-3W4ZIXB7-Cc5RwCEh.js → kanban-definition-K7BYSVSG-Baf2kCwQ.js} +5 -3
  100. package/webapp/dist/assets/kanban-definition-K7BYSVSG-Baf2kCwQ.js.map +1 -0
  101. package/webapp/dist/assets/{layout-BB2SvQcE.js → layout-DUMDc8rv.js} +5 -5
  102. package/webapp/dist/assets/layout-DUMDc8rv.js.map +1 -0
  103. package/webapp/dist/assets/{linear-Cj50lA0a.js → linear-CZMoHeVH.js} +2 -2
  104. package/webapp/dist/assets/linear-CZMoHeVH.js.map +1 -0
  105. package/webapp/dist/assets/{mindmap-definition-VGOIOE7T-BLBklJVX.js → mindmap-definition-YRQLILUH-DB2sQ--_.js} +7 -5
  106. package/webapp/dist/assets/mindmap-definition-YRQLILUH-DB2sQ--_.js.map +1 -0
  107. package/webapp/dist/assets/ordinal-CxptdPJm.js.map +1 -1
  108. package/webapp/dist/assets/{pieDiagram-ADFJNKIX-BQrOgSc-.js → pieDiagram-SKSYHLDU-Dgdrlric.js} +8 -8
  109. package/webapp/dist/assets/pieDiagram-SKSYHLDU-Dgdrlric.js.map +1 -0
  110. package/webapp/dist/assets/{quadrantDiagram-AYHSOK5B-C62TxtsO.js → quadrantDiagram-337W2JSQ-CJcFYfqf.js} +3 -3
  111. package/webapp/dist/assets/quadrantDiagram-337W2JSQ-CJcFYfqf.js.map +1 -0
  112. package/webapp/dist/assets/{requirementDiagram-UZGBJVZJ-Is6Q3osM.js → requirementDiagram-Z7DCOOCP-CDsT-ac7.js} +16 -6
  113. package/webapp/dist/assets/requirementDiagram-Z7DCOOCP-CDsT-ac7.js.map +1 -0
  114. package/webapp/dist/assets/{sankeyDiagram-TZEHDZUN-CCAW8Dr2.js → sankeyDiagram-WA2Y5GQK-DiO55skm.js} +2 -2
  115. package/webapp/dist/assets/sankeyDiagram-WA2Y5GQK-DiO55skm.js.map +1 -0
  116. package/webapp/dist/assets/{sequenceDiagram-WL72ISMW-DpODpbl6.js → sequenceDiagram-2WXFIKYE-CNHjdBNC.js} +601 -201
  117. package/webapp/dist/assets/sequenceDiagram-2WXFIKYE-CNHjdBNC.js.map +1 -0
  118. package/webapp/dist/assets/{stateDiagram-FKZM4ZOC-B8LHaf5T.js → stateDiagram-RAJIS63D-CtS3TXEd.js} +9 -9
  119. package/webapp/dist/assets/stateDiagram-RAJIS63D-CtS3TXEd.js.map +1 -0
  120. package/webapp/dist/assets/{stateDiagram-v2-4FDKWEC3-4FzTkpgz.js → stateDiagram-v2-FVOUBMTO-BdjJA1de.js} +5 -5
  121. package/webapp/dist/assets/stateDiagram-v2-FVOUBMTO-BdjJA1de.js.map +1 -0
  122. package/webapp/dist/assets/{timeline-definition-IT6M3QCI-Ckxc4qZe.js → timeline-definition-YZTLITO2-D3AiTIhK.js} +3 -3
  123. package/webapp/dist/assets/timeline-definition-YZTLITO2-D3AiTIhK.js.map +1 -0
  124. package/webapp/dist/assets/{treemap-GDKQZRPO-QQa4vKMv.js → treemap-KZPCXAKY-F6nRvLGK.js} +37 -24
  125. package/webapp/dist/assets/treemap-KZPCXAKY-F6nRvLGK.js.map +1 -0
  126. package/webapp/dist/assets/vennDiagram-LZ73GAT5-BoukZEuo.js +2487 -0
  127. package/webapp/dist/assets/vennDiagram-LZ73GAT5-BoukZEuo.js.map +1 -0
  128. package/webapp/dist/assets/{xychartDiagram-PRI3JC2R-Ba2uJcs6.js → xychartDiagram-JWTSCODW-ByfGkhZz.js} +4 -4
  129. package/webapp/dist/assets/xychartDiagram-JWTSCODW-ByfGkhZz.js.map +1 -0
  130. package/webapp/dist/index.html +2 -2
  131. package/webapp/dist/assets/_basePickBy-BJWCmtUm.js.map +0 -1
  132. package/webapp/dist/assets/_baseUniq-D6k_lGfs.js.map +0 -1
  133. package/webapp/dist/assets/arc-DYDaESgj.js.map +0 -1
  134. package/webapp/dist/assets/architectureDiagram-VXUJARFQ-DZMT7dqc.js.map +0 -1
  135. package/webapp/dist/assets/blockDiagram-VD42YOAC-CABpgVAa.js.map +0 -1
  136. package/webapp/dist/assets/c4Diagram-YG6GDRKO-COLZS8Ul.js.map +0 -1
  137. package/webapp/dist/assets/channel-CYFm9Cri.js.map +0 -1
  138. package/webapp/dist/assets/chunk-4BX2VUAB-CX_-XbaN.js.map +0 -1
  139. package/webapp/dist/assets/chunk-55IACEB6-ByD-NdBC.js.map +0 -1
  140. package/webapp/dist/assets/chunk-B4BG7PRW-BANemsDD.js.map +0 -1
  141. package/webapp/dist/assets/chunk-DI55MBZ5-DV7sdJmQ.js.map +0 -1
  142. package/webapp/dist/assets/chunk-FMBD7UC4-DYGviJnf.js.map +0 -1
  143. package/webapp/dist/assets/chunk-QN33PNHL-B8DWRL9f.js.map +0 -1
  144. package/webapp/dist/assets/chunk-QZHKN3VN-B1aYIzMR.js.map +0 -1
  145. package/webapp/dist/assets/chunk-TZMSLE5B-_HISzxl3.js.map +0 -1
  146. package/webapp/dist/assets/classDiagram-2ON5EDUG-CpyYj1Rc.js.map +0 -1
  147. package/webapp/dist/assets/classDiagram-v2-WZHVMYZB-CpyYj1Rc.js.map +0 -1
  148. package/webapp/dist/assets/clone-B_9AxWIU.js.map +0 -1
  149. package/webapp/dist/assets/cose-bilkent-S5V4N54A-17ECLfPR.js.map +0 -1
  150. package/webapp/dist/assets/dagre-6UL2VRFP-DhRqcF1o.js.map +0 -1
  151. package/webapp/dist/assets/diagram-PSM6KHXK-lpDT6Wwb.js.map +0 -1
  152. package/webapp/dist/assets/diagram-QEK2KX5R-ohxbPpDH.js.map +0 -1
  153. package/webapp/dist/assets/diagram-S2PKOQOG-DAFFGfch.js.map +0 -1
  154. package/webapp/dist/assets/erDiagram-Q2GNP2WA-BH-7rI43.js.map +0 -1
  155. package/webapp/dist/assets/flowDiagram-NV44I4VS-CdEjFbz4.js.map +0 -1
  156. package/webapp/dist/assets/ganttDiagram-JELNMOA3-Cju2t-lK.js.map +0 -1
  157. package/webapp/dist/assets/gitGraphDiagram-V2S2FVAM-CUJ5oxCg.js.map +0 -1
  158. package/webapp/dist/assets/graph-mhcc7ldf.js +0 -425
  159. package/webapp/dist/assets/graph-mhcc7ldf.js.map +0 -1
  160. package/webapp/dist/assets/infoDiagram-HS3SLOUP-Df8p0okQ.js.map +0 -1
  161. package/webapp/dist/assets/journeyDiagram-XKPGCS4Q-BXMl8H-d.js.map +0 -1
  162. package/webapp/dist/assets/kanban-definition-3W4ZIXB7-Cc5RwCEh.js.map +0 -1
  163. package/webapp/dist/assets/layout-BB2SvQcE.js.map +0 -1
  164. package/webapp/dist/assets/linear-Cj50lA0a.js.map +0 -1
  165. package/webapp/dist/assets/mindmap-definition-VGOIOE7T-BLBklJVX.js.map +0 -1
  166. package/webapp/dist/assets/pieDiagram-ADFJNKIX-BQrOgSc-.js.map +0 -1
  167. package/webapp/dist/assets/quadrantDiagram-AYHSOK5B-C62TxtsO.js.map +0 -1
  168. package/webapp/dist/assets/requirementDiagram-UZGBJVZJ-Is6Q3osM.js.map +0 -1
  169. package/webapp/dist/assets/sankeyDiagram-TZEHDZUN-CCAW8Dr2.js.map +0 -1
  170. package/webapp/dist/assets/sequenceDiagram-WL72ISMW-DpODpbl6.js.map +0 -1
  171. package/webapp/dist/assets/stateDiagram-FKZM4ZOC-B8LHaf5T.js.map +0 -1
  172. package/webapp/dist/assets/stateDiagram-v2-4FDKWEC3-4FzTkpgz.js.map +0 -1
  173. package/webapp/dist/assets/timeline-definition-IT6M3QCI-Ckxc4qZe.js.map +0 -1
  174. package/webapp/dist/assets/treemap-GDKQZRPO-QQa4vKMv.js.map +0 -1
  175. package/webapp/dist/assets/xychartDiagram-PRI3JC2R-Ba2uJcs6.js.map +0 -1
@@ -51,6 +51,7 @@ export declare function broadcastDisplayStateMarker(dialogId: DialogID, marker:
51
51
  reason?: DialogInterruptionReason;
52
52
  }): void;
53
53
  export declare function computeIdleDisplayState(dlg: Dialog): Promise<DialogDisplayState>;
54
+ export declare function refreshRunControlProjectionFromPersistenceFacts(dialogId: DialogID, trigger: 'resume_dialog' | 'resume_all' | 'run_control_snapshot' | 'pending_subdialogs_changed' | 'q4h_changed'): Promise<DialogLatestFile | null>;
54
55
  export declare function reconcileDisplayStatesAfterRestart(): Promise<void>;
55
56
  export declare function requestInterruptDialog(dialogId: DialogID, reason: StopRequestedReason): Promise<{
56
57
  applied: boolean;
@@ -39,6 +39,7 @@ exports.clearDialogInterruptedExecutionMarker = clearDialogInterruptedExecutionM
39
39
  exports.setDialogDisplayState = setDialogDisplayState;
40
40
  exports.broadcastDisplayStateMarker = broadcastDisplayStateMarker;
41
41
  exports.computeIdleDisplayState = computeIdleDisplayState;
42
+ exports.refreshRunControlProjectionFromPersistenceFacts = refreshRunControlProjectionFromPersistenceFacts;
42
43
  exports.reconcileDisplayStatesAfterRestart = reconcileDisplayStatesAfterRestart;
43
44
  exports.requestInterruptDialog = requestInterruptDialog;
44
45
  exports.requestEmergencyStopAll = requestEmergencyStopAll;
@@ -84,6 +85,9 @@ function isDialogLatestResumable(latest) {
84
85
  latest.displayState.continueEnabled &&
85
86
  latest.executionMarker?.kind === 'interrupted');
86
87
  }
88
+ function isSameDisplayState(left, right) {
89
+ return JSON.stringify(left) === JSON.stringify(right);
90
+ }
87
91
  function classifyRunControlBucket(state) {
88
92
  if (!state)
89
93
  return 'none';
@@ -122,8 +126,13 @@ async function getRunControlCountsSnapshot() {
122
126
  if (latest?.generating === true) {
123
127
  proceeding++;
124
128
  }
125
- else if (isDialogLatestResumable(latest)) {
126
- resumable++;
129
+ else if (latest?.executionMarker?.kind === 'interrupted' &&
130
+ isStoppedReasonResumable(latest.executionMarker.reason)) {
131
+ const q4h = await persistence_1.DialogPersistence.loadQuestions4HumanState(dialogId, 'running');
132
+ const pendingSubdialogs = await persistence_1.DialogPersistence.loadPendingSubdialogs(dialogId, 'running');
133
+ if (q4h.length === 0 && pendingSubdialogs.length === 0) {
134
+ resumable++;
135
+ }
127
136
  }
128
137
  }
129
138
  catch (error) {
@@ -444,6 +453,66 @@ async function computeIdleDisplayStateFromPersistence(dialogId) {
444
453
  }
445
454
  return { kind: 'idle_waiting_user' };
446
455
  }
456
+ async function refreshRunControlProjectionFromPersistenceFacts(dialogId, trigger) {
457
+ const latest = await persistence_1.DialogPersistence.loadDialogLatest(dialogId, 'running');
458
+ if (!latest) {
459
+ return null;
460
+ }
461
+ if (latest.generating === true) {
462
+ return latest;
463
+ }
464
+ if (hasActiveRun(dialogId)) {
465
+ return latest;
466
+ }
467
+ const desired = await (async () => {
468
+ if (dialogId.selfId !== dialogId.rootId &&
469
+ latest.executionMarker &&
470
+ latest.executionMarker.kind === 'dead') {
471
+ return { kind: 'dead', reason: latest.executionMarker.reason };
472
+ }
473
+ const q4h = await persistence_1.DialogPersistence.loadQuestions4HumanState(dialogId, 'running');
474
+ const pendingSubdialogs = await persistence_1.DialogPersistence.loadPendingSubdialogs(dialogId, 'running');
475
+ const hasQ4H = q4h.length > 0;
476
+ const hasSubdialogs = pendingSubdialogs.length > 0;
477
+ if (hasQ4H && hasSubdialogs) {
478
+ return { kind: 'blocked', reason: { kind: 'needs_human_input_and_subdialogs' } };
479
+ }
480
+ if (hasQ4H) {
481
+ return { kind: 'blocked', reason: { kind: 'needs_human_input' } };
482
+ }
483
+ if (hasSubdialogs) {
484
+ return { kind: 'blocked', reason: { kind: 'waiting_for_subdialogs' } };
485
+ }
486
+ if (latest.executionMarker?.kind === 'interrupted') {
487
+ return {
488
+ kind: 'stopped',
489
+ reason: latest.executionMarker.reason,
490
+ continueEnabled: isStoppedReasonResumable(latest.executionMarker.reason),
491
+ };
492
+ }
493
+ return { kind: 'idle_waiting_user' };
494
+ })();
495
+ const executionMarkerNeedsHealing = desired.kind === 'stopped'
496
+ ? latest.executionMarker?.kind !== 'interrupted' ||
497
+ JSON.stringify(latest.executionMarker.reason) !== JSON.stringify(desired.reason)
498
+ : desired.kind === 'dead'
499
+ ? latest.executionMarker?.kind !== 'dead' ||
500
+ JSON.stringify(latest.executionMarker.reason) !== JSON.stringify(desired.reason)
501
+ : latest.executionMarker?.kind === 'interrupted';
502
+ const displayStateNeedsHealing = !isSameDisplayState(latest.displayState, desired);
503
+ if (!displayStateNeedsHealing && !executionMarkerNeedsHealing) {
504
+ return latest;
505
+ }
506
+ log.warn('Healing stale run-control projection from persistence facts', undefined, {
507
+ dialogId: dialogId.valueOf(),
508
+ trigger,
509
+ previousDisplayState: latest.displayState ?? null,
510
+ previousExecutionMarker: latest.executionMarker ?? null,
511
+ healedDisplayState: desired,
512
+ });
513
+ await setDialogDisplayState(dialogId, desired);
514
+ return await persistence_1.DialogPersistence.loadDialogLatest(dialogId, 'running');
515
+ }
447
516
  async function computeIdleDisplayStateForReconciliation(dialogId) {
448
517
  try {
449
518
  return await computeIdleDisplayStateFromPersistence(dialogId);
@@ -113,6 +113,7 @@ function isPersistedMessageRecord(record) {
113
113
  // not contribute to message-count / context reconstruction semantics.
114
114
  case 'web_search_call_record':
115
115
  case 'native_tool_call_record':
116
+ case 'tool_result_image_ingest_record':
116
117
  case 'quest_for_sup_record':
117
118
  case 'tellask_reply_resolution_record':
118
119
  case 'tellask_call_anchor_record':
@@ -199,6 +200,14 @@ function rewriteRecordForFork(record, newRootId) {
199
200
  ...record,
200
201
  contentItems: rewriteFuncResultContentItems(record.contentItems, newRootId),
201
202
  };
203
+ case 'tool_result_image_ingest_record':
204
+ return {
205
+ ...record,
206
+ artifact: {
207
+ ...record.artifact,
208
+ rootId: newRootId,
209
+ },
210
+ };
202
211
  case 'subdialog_created_record':
203
212
  case 'reminders_reconciled_record':
204
213
  case 'questions4human_reconciled_record':
package/dist/dialog.d.ts CHANGED
@@ -18,6 +18,7 @@ import type { DialogPrompt, DialogRunControlSpec, DialogSubdialogReplyTarget, Dr
18
18
  import type { LanguageCode } from '@longrun-ai/kernel/types/language';
19
19
  import type { CalleeCourseNumber, CalleeGenerationSeqNumber, CallingCourseNumber, DialogMetadataFile, HumanQuestion, ProviderData, ReasoningPayload, TellaskCallRecordName, TellaskReplyDirective } from '@longrun-ai/kernel/types/storage';
20
20
  import { ChatMessage, FuncResultMsg, TellaskCarryoverMsg, TellaskResultMsg } from './llm/client';
21
+ import type { ToolResultImageIngest } from './llm/gen';
21
22
  import type { JsonValue } from './tool';
22
23
  import { Reminder, ReminderOptions, ReminderOwner } from './tool';
23
24
  type NewCourseHookResult = {
@@ -406,6 +407,7 @@ export declare abstract class Dialog {
406
407
  action?: WebSearchCallAction;
407
408
  }): Promise<void>;
408
409
  nativeToolCall(payload: NativeToolCallPayload): Promise<void>;
410
+ toolResultImageIngest(payload: ToolResultImageIngest): Promise<void>;
409
411
  callingStart(payload: {
410
412
  callName: 'tellaskBack' | 'tellask' | 'tellaskSessionless' | 'askHuman' | 'freshBootsReasoning';
411
413
  callId: string;
@@ -624,6 +626,7 @@ export declare abstract class DialogStore {
624
626
  action?: WebSearchCallAction;
625
627
  }): Promise<void>;
626
628
  nativeToolCall(_dialog: Dialog, _payload: NativeToolCallPayload): Promise<void>;
629
+ toolResultImageIngest(_dialog: Dialog, _payload: ToolResultImageIngest): Promise<void>;
627
630
  /**
628
631
  * Load current course number from persisted metadata
629
632
  * This method should be implemented by subclasses to read from storage
package/dist/dialog.js CHANGED
@@ -1144,6 +1144,9 @@ class Dialog {
1144
1144
  async nativeToolCall(payload) {
1145
1145
  await this.dlgStore.nativeToolCall(this, payload);
1146
1146
  }
1147
+ async toolResultImageIngest(payload) {
1148
+ await this.dlgStore.toolResultImageIngest(this, payload);
1149
+ }
1147
1150
  // Tellask-special call lifecycle events
1148
1151
  async callingStart(payload) {
1149
1152
  // Store callId for inline call-result correlation
@@ -1578,6 +1581,7 @@ class DialogStore {
1578
1581
  async funcCallRequested(_dialog, _funcId, _funcName, _argumentsStr) { }
1579
1582
  async webSearchCall(_dialog, _payload) { }
1580
1583
  async nativeToolCall(_dialog, _payload) { }
1584
+ async toolResultImageIngest(_dialog, _payload) { }
1581
1585
  /**
1582
1586
  * Load current course number from persisted metadata
1583
1587
  * This method should be implemented by subclasses to read from storage
@@ -335,9 +335,13 @@ Policy:
335
335
  reduce tool set, etc.).
336
336
  - **Network/retriable errors**: Dominds may auto-retry only for clearly retriable classes such as
337
337
  transient network failures/timeouts and provider transient errors (e.g. rate limits or 5xx), using
338
- bounded backoff and a max retry count.
338
+ strategy-specific backoff. Explicit short transport failures may use a brief aggressive burst, explicit
339
+ rate-limit signals may use smart-rate backoff, and everything else should default to conservative
340
+ keepalive retry instead of stopping just because a fixed retry-count ceiling was reached. Only
341
+ high-confidence non-progress cases should stop automatic retry.
339
342
 
340
- This keeps the system responsive and avoids infinite “retry loops caused by invalid tool schemas.
343
+ This keeps the system responsive while still avoiding pointless automatic loops caused by invalid tool
344
+ schemas or other high-confidence non-retriable failures.
341
345
 
342
346
  ## Environment Variables (`env`)
343
347
 
@@ -7,6 +7,7 @@ const DOMINDS_LLM_EMPTY_RESPONSE_ERROR_CODE = 'DOMINDS_LLM_EMPTY_RESPONSE';
7
7
  const XCODE_BEST_EMPTY_RESPONSE_SINGLE_RETRY_DELAY_MS = 3000;
8
8
  const XCODE_BEST_EMPTY_RESPONSE_GIVE_UP_THRESHOLD = 5;
9
9
  const XCODE_BEST_GATEWAY_HTML_502_RETRY_MESSAGE = 'xcode.best gateway returned an HTML 502 Bad Gateway page; retrying conservatively.';
10
+ const XCODE_BEST_AUTH_UNAVAILABLE_RETRY_MESSAGE = 'xcode.best upstream returned 500 auth_unavailable: no auth available; treating it as an infrastructure failure and retrying conservatively.';
10
11
  const XCODE_BEST_UNEXPECTED_EOF_RETRY_MESSAGE = 'xcode.best upstream stream ended unexpectedly (unexpected EOF); retrying conservatively.';
11
12
  const LOCAL_FILE_IO_ERROR_CODES = new Set(['ENOENT', 'ENOTDIR', 'EISDIR', 'EACCES', 'EPERM']);
12
13
  const LOCAL_FILE_IO_SYSCALLS = new Set([
@@ -42,6 +43,21 @@ function isXcodeBestUnexpectedEofFailure(failure, error) {
42
43
  failure.message.toLowerCase().includes('unexpected eof')) &&
43
44
  !hasLikelyLocalFileErrorContext(error));
44
45
  }
46
+ function isXcodeBestAuthUnavailableFailure(failure, error) {
47
+ const status = failure.status ?? readErrorStatus(error);
48
+ if (status !== 500) {
49
+ return false;
50
+ }
51
+ const code = (failure.code ?? readErrorCode(error))?.trim().toLowerCase();
52
+ if (code === 'auth_unavailable') {
53
+ return true;
54
+ }
55
+ const message = (readErrorMessage(error) ?? failure.message).toLowerCase();
56
+ if (message.includes('auth_unavailable')) {
57
+ return true;
58
+ }
59
+ return code === 'internal_server_error' && message.includes('no auth available');
60
+ }
45
61
  function getErrorChain(error) {
46
62
  const queue = [error];
47
63
  const visited = new Set();
@@ -248,12 +264,12 @@ function buildXcodeBestEmptyResponseGiveUpText(providerConfig, provider) {
248
264
  const providerName = providerConfig.name.trim().length > 0 ? providerConfig.name : provider;
249
265
  const summaryTextI18n = {
250
266
  zh: `${providerName} 在同一对话上下文中连续返回 empty response。` +
251
- `Dominds 已在 ${String(XCODE_BEST_EMPTY_RESPONSE_GIVE_UP_THRESHOLD)} 次 empty response 后停止继续重试,因为这通常表示 provider 侧该对话上下文已经卡住;` +
252
- '如果直接点继续,大概率仍然无真实进展;更建议结合真实情况灵活尝试多种新的指令,例如改写问题、补充上下文、换一个切入方式。',
267
+ `Dominds 已在 ${String(XCODE_BEST_EMPTY_RESPONSE_GIVE_UP_THRESHOLD)} 次 empty response 后停止沿用同一上下文继续自动重试,因为这通常表示 provider 侧该对话上下文已经卡住;` +
268
+ '如果不引入新的信息或新的指令,直接点继续大概率仍然无真实进展;更建议补充上下文、改写问题、换一个切入方式,或在确实需要人类判断时调用 askHuman。',
253
269
  en: `${providerName} returned empty responses repeatedly for the same dialog context. ` +
254
- `Dominds stopped retrying after ${String(XCODE_BEST_EMPTY_RESPONSE_GIVE_UP_THRESHOLD)} empty responses because this usually means the provider-side conversation ` +
255
- 'context is stuck; simply pressing Continue is still unlikely to make real progress, ' +
256
- 'so it is better to try different fresh instructions based on the real situation, such as reframing the ask, adding context, or changing the angle.',
270
+ `Dominds stopped repeating the same-context automatic retry path after ${String(XCODE_BEST_EMPTY_RESPONSE_GIVE_UP_THRESHOLD)} empty responses because this usually means the provider-side conversation ` +
271
+ 'context is stuck; simply pressing Continue without new information or fresh instructions is still unlikely to make real progress, ' +
272
+ 'so it is better to add context, reframe the ask, change the angle, or call askHuman when human judgment is genuinely needed.',
257
273
  };
258
274
  return {
259
275
  providerName,
@@ -269,6 +285,11 @@ function createXcodeBestFailureQuirkHandlerSession(providerConfig) {
269
285
  onFailure(args) {
270
286
  const { providerName, summaryTextI18n, recoveryAction } = buildXcodeBestEmptyResponseGiveUpText(providerConfig, args.provider);
271
287
  if (args.failure.code === DOMINDS_LLM_EMPTY_RESPONSE_ERROR_CODE) {
288
+ // xcode.best can enter a same-context deadlock where the upstream keeps returning empty
289
+ // responses forever until the dialog context changes materially. A short burst of
290
+ // temporary retries is still worthwhile for transient glitches, but once the streak reaches
291
+ // the threshold we must stop repeating the exact same automatic path and require fresh
292
+ // information / fresh instructions instead of hiding the deadlock behind slow retries.
272
293
  consecutiveEmptyResponseCount += 1;
273
294
  if (consecutiveEmptyResponseCount < XCODE_BEST_EMPTY_RESPONSE_GIVE_UP_THRESHOLD) {
274
295
  return {
@@ -279,7 +300,7 @@ function createXcodeBestFailureQuirkHandlerSession(providerConfig) {
279
300
  return {
280
301
  kind: 'give_up',
281
302
  message: `${providerName} returned empty responses repeatedly for the same dialog context; ` +
282
- 'automatic retries were stopped; simply continuing is still unlikely to make real progress, so it is better to flexibly try different fresh instructions based on the real situation.',
303
+ 'Dominds stopped repeating the same-context automatic retry path; continuing without new information is still unlikely to make real progress, so it is better to introduce fresh instructions or new context based on the real situation.',
283
304
  summaryTextI18n,
284
305
  recoveryAction: consumedDiligencePushRecoverySinceLastSuccess
285
306
  ? { kind: 'none' }
@@ -301,6 +322,13 @@ function createXcodeBestFailureQuirkHandlerSession(providerConfig) {
301
322
  message: XCODE_BEST_GATEWAY_HTML_502_RETRY_MESSAGE,
302
323
  };
303
324
  }
325
+ if (isXcodeBestAuthUnavailableFailure(args.failure, args.error)) {
326
+ return {
327
+ kind: 'retry_strategy',
328
+ retryStrategy: 'conservative',
329
+ message: XCODE_BEST_AUTH_UNAVAILABLE_RETRY_MESSAGE,
330
+ };
331
+ }
304
332
  return { kind: 'default' };
305
333
  },
306
334
  onRequestSucceeded() {
@@ -56,7 +56,7 @@ export type ProviderConfig = {
56
56
  baseUrl: string;
57
57
  apiKeyEnvVar: string;
58
58
  tool_result_max_chars?: number;
59
- llm_retry_max_retries?: number;
59
+ llm_retry_aggressive_max_retries?: number;
60
60
  llm_retry_initial_delay_ms?: number;
61
61
  llm_retry_conservative_delay_ms?: number;
62
62
  llm_retry_backoff_multiplier?: number;
@@ -1,5 +1,7 @@
1
1
  # Optional provider-level retry controls for kernel dialog driving:
2
- # - llm_retry_max_retries: extra retries after the initial attempt (default 99; ~41.2h total retry window with current backoff defaults).
2
+ # - llm_retry_aggressive_max_retries: front-loaded fast retries before Dominds automatically downgrades
3
+ # to conservative slow retry (default 3). Automatic retries no longer stop just because a retry
4
+ # count budget was exhausted.
3
5
  # - llm_retry_initial_delay_ms: delay before the 1st retry (default 1000).
4
6
  # - llm_retry_conservative_delay_ms: base delay for provider-classified conservative retries (default 30000; fixed for first 10 retries, then ramps by x1.5 with the normal max-delay cap).
5
7
  # - llm_retry_backoff_multiplier: exponential factor between retries (default 1.5).
@@ -7,8 +9,9 @@
7
9
  # - apiQuirks: optional provider/gateway quirk profile(s) for non-standard transport behavior
8
10
  # and provider-specific failure handling that can override the driver's default retry disposition.
9
11
  # Example: `apiQuirks: xcode.best` currently covers both keepalive/heartbeat stream quirks and
10
- # repeated empty-response handling for the same dialog context, plus conservative retry
11
- # classification for gateway-returned HTML 502 Bad Gateway pages.
12
+ # repeated empty-response deadlock detection for the same unchanged dialog context, plus conservative retry
13
+ # classification for gateway-returned HTML 502 Bad Gateway pages and
14
+ # `500 auth_unavailable: no auth available` infrastructure failures.
12
15
  # - tool_result_max_chars: optional transport-level cap for a single tool-result text payload
13
16
  # before Dominds projects it into the provider request. Use this when a provider/gateway enforces
14
17
  # a stricter per-item string limit than Dominds' built-in defaults.
@@ -8,7 +8,7 @@ export type AnthropicStreamConsumeResult = {
8
8
  usage: LlmUsageStats;
9
9
  llmGenModel?: string;
10
10
  };
11
- export declare function buildAnthropicRequestMessagesWrapper(context: ChatMessage[], providerConfig?: ProviderConfig): Promise<MessageParam[]>;
11
+ export declare function buildAnthropicRequestMessagesWrapper(context: ChatMessage[], requestContextOrProviderConfig?: LlmRequestContext | ProviderConfig, providerConfigMaybe?: ProviderConfig): Promise<MessageParam[]>;
12
12
  export declare function consumeAnthropicStream(stream: AsyncIterable<MessageStreamEvent>, receiver: LlmStreamReceiver, abortSignal?: AbortSignal, forcedJsonToolName?: string): Promise<AnthropicStreamConsumeResult>;
13
13
  /**
14
14
  * Reconstruct Anthropic context from persisted messages with genseq tracking.
@@ -29,6 +29,6 @@ export declare function reconstructAnthropicContextWrapperAsync(persistedMessage
29
29
  export declare class AnthropicGen implements LlmGenerator {
30
30
  get apiType(): string;
31
31
  classifyFailure(error: unknown): LlmFailureDisposition | undefined;
32
- genToReceiver(providerConfig: ProviderConfig, agent: Team.Member, systemPrompt: string, funcTools: FuncTool[], _requestContext: LlmRequestContext, context: ChatMessage[], receiver: LlmStreamReceiver, _genseq: number, abortSignal?: AbortSignal): Promise<LlmStreamResult>;
33
- genMoreMessages(providerConfig: ProviderConfig, agent: Team.Member, systemPrompt: string, funcTools: FuncTool[], _requestContext: LlmRequestContext, context: ChatMessage[], genseq: number, abortSignal?: AbortSignal): Promise<LlmBatchResult>;
32
+ genToReceiver(providerConfig: ProviderConfig, agent: Team.Member, systemPrompt: string, funcTools: FuncTool[], requestContext: LlmRequestContext, context: ChatMessage[], receiver: LlmStreamReceiver, _genseq: number, abortSignal?: AbortSignal): Promise<LlmStreamResult>;
33
+ genMoreMessages(providerConfig: ProviderConfig, agent: Team.Member, systemPrompt: string, funcTools: FuncTool[], requestContext: LlmRequestContext, context: ChatMessage[], genseq: number, abortSignal?: AbortSignal): Promise<LlmBatchResult>;
34
34
  }
@@ -18,6 +18,7 @@ const artifacts_1 = require("./artifacts");
18
18
  const failure_classifier_1 = require("./failure-classifier");
19
19
  const tool_call_context_1 = require("./tool-call-context");
20
20
  const tool_output_limit_1 = require("./tool-output-limit");
21
+ const tool_result_image_ingest_1 = require("./tool-result-image-ingest");
21
22
  const log = (0, log_1.createLogger)('llm/anthropic');
22
23
  const ANTHROPIC_JSON_RESPONSE_TOOL_NAME = 'dominds_json_response';
23
24
  const ANTHROPIC_JSON_RESPONSE_TOOL_DESCRIPTION = 'Return the final answer as a JSON object. Do not include any non-JSON text.';
@@ -75,6 +76,13 @@ function isRecord(value) {
75
76
  function isNonArrayRecord(value) {
76
77
  return isRecord(value) && !Array.isArray(value);
77
78
  }
79
+ function isLlmRequestContext(value) {
80
+ return (isNonArrayRecord(value) &&
81
+ typeof value.dialogSelfId === 'string' &&
82
+ typeof value.dialogRootId === 'string' &&
83
+ typeof value.providerKey === 'string' &&
84
+ typeof value.modelKey === 'string');
85
+ }
78
86
  function tryExtractApiReturnedModel(value) {
79
87
  // NOTE: External API payload; a runtime check is unavoidable.
80
88
  if (!isRecord(value))
@@ -177,7 +185,7 @@ function parseForcedJsonToolInput(rawJson, fallbackInput, at) {
177
185
  }
178
186
  throw new Error(`Invalid ${at}: ${errorText}; raw=${JSON.stringify(preview)}`);
179
187
  }
180
- async function funcResultToAnthropicToolResultBlock(chatMsg, limitChars) {
188
+ async function funcResultToAnthropicToolResultBlock(chatMsg, limitChars, requestContext, allowedImageKeys, onToolResultImageIngest) {
181
189
  const items = chatMsg.contentItems;
182
190
  if (!Array.isArray(items) || items.length === 0) {
183
191
  return {
@@ -194,22 +202,85 @@ async function funcResultToAnthropicToolResultBlock(chatMsg, limitChars) {
194
202
  }
195
203
  if (item.type === 'input_image') {
196
204
  if (!(0, artifacts_1.isVisionImageMimeType)(item.mimeType)) {
205
+ if (onToolResultImageIngest) {
206
+ await onToolResultImageIngest((0, tool_result_image_ingest_1.buildToolResultImageIngest)({
207
+ requestContext,
208
+ toolCallId: chatMsg.id,
209
+ toolName: chatMsg.name,
210
+ artifact: item.artifact,
211
+ disposition: 'filtered_mime_unsupported',
212
+ mimeType: item.mimeType,
213
+ providerPathLabel: 'Anthropic Messages path',
214
+ }));
215
+ }
197
216
  content.push({
198
217
  type: 'text',
199
218
  text: `[image omitted: unsupported mimeType=${item.mimeType}]`,
200
219
  });
201
220
  continue;
202
221
  }
203
- const bytes = await (0, artifacts_1.readDialogArtifactBytes)({
204
- rootId: item.artifact.rootId,
205
- selfId: item.artifact.selfId,
206
- status: item.artifact.status,
207
- relPath: item.artifact.relPath,
208
- });
209
- if (!bytes) {
222
+ if (!allowedImageKeys.has((0, tool_result_image_ingest_1.buildToolResultImageBudgetKeyForMsg)(chatMsg, item.artifact))) {
223
+ if (onToolResultImageIngest) {
224
+ await onToolResultImageIngest((0, tool_result_image_ingest_1.buildToolResultImageIngest)({
225
+ requestContext,
226
+ toolCallId: chatMsg.id,
227
+ toolName: chatMsg.name,
228
+ artifact: item.artifact,
229
+ disposition: 'filtered_size_limit',
230
+ detail: (0, tool_result_image_ingest_1.buildToolResultImageBudgetLimitDetail)({
231
+ byteLength: item.byteLength,
232
+ budgetBytes: tool_result_image_ingest_1.ANTHROPIC_TOOL_RESULT_IMAGE_BUDGET_BYTES,
233
+ }),
234
+ providerPathLabel: 'Anthropic Messages path',
235
+ }));
236
+ }
237
+ content.push({
238
+ type: 'text',
239
+ text: `[image omitted: request image budget exceeded bytes=${String(item.byteLength)} budget=${String(tool_result_image_ingest_1.ANTHROPIC_TOOL_RESULT_IMAGE_BUDGET_BYTES)}]`,
240
+ });
241
+ continue;
242
+ }
243
+ const bytesResult = await (0, tool_result_image_ingest_1.readToolResultImageBytesSafe)(item.artifact);
244
+ if (bytesResult.kind === 'missing') {
245
+ if (onToolResultImageIngest) {
246
+ await onToolResultImageIngest((0, tool_result_image_ingest_1.buildToolResultImageIngest)({
247
+ requestContext,
248
+ toolCallId: chatMsg.id,
249
+ toolName: chatMsg.name,
250
+ artifact: item.artifact,
251
+ disposition: 'filtered_missing',
252
+ providerPathLabel: 'Anthropic Messages path',
253
+ }));
254
+ }
210
255
  content.push({ type: 'text', text: `[image missing: ${item.artifact.relPath}]` });
211
256
  continue;
212
257
  }
258
+ if (bytesResult.kind === 'read_failed') {
259
+ if (onToolResultImageIngest) {
260
+ await onToolResultImageIngest((0, tool_result_image_ingest_1.buildToolResultImageIngest)({
261
+ requestContext,
262
+ toolCallId: chatMsg.id,
263
+ toolName: chatMsg.name,
264
+ artifact: item.artifact,
265
+ disposition: 'filtered_read_failed',
266
+ detail: bytesResult.detail,
267
+ providerPathLabel: 'Anthropic Messages path',
268
+ }));
269
+ }
270
+ content.push({ type: 'text', text: `[image unreadable: ${item.artifact.relPath}]` });
271
+ continue;
272
+ }
273
+ if (onToolResultImageIngest) {
274
+ await onToolResultImageIngest((0, tool_result_image_ingest_1.buildToolResultImageIngest)({
275
+ requestContext,
276
+ toolCallId: chatMsg.id,
277
+ toolName: chatMsg.name,
278
+ artifact: item.artifact,
279
+ disposition: 'fed_native',
280
+ providerPathLabel: 'Anthropic Messages path',
281
+ }));
282
+ }
283
+ const bytes = bytesResult.bytes;
213
284
  const base64 = bytes.toString('base64');
214
285
  content.push({
215
286
  type: 'image',
@@ -237,14 +308,16 @@ async function funcResultToAnthropicToolResultBlock(chatMsg, limitChars) {
237
308
  content: limitAnthropicToolOutputBlocks(content, chatMsg, limitChars),
238
309
  };
239
310
  }
240
- async function chatMessageToContentBlocksAsync(chatMsg, limitChars) {
311
+ async function chatMessageToContentBlocksAsync(chatMsg, limitChars, requestContext, allowedImageKeys, onToolResultImageIngest) {
241
312
  if (chatMsg.type !== 'func_result_msg') {
242
313
  return chatMessageToContentBlocks(chatMsg);
243
314
  }
244
- return [await funcResultToAnthropicToolResultBlock(chatMsg, limitChars)];
315
+ return [
316
+ await funcResultToAnthropicToolResultBlock(chatMsg, limitChars, requestContext, allowedImageKeys, onToolResultImageIngest),
317
+ ];
245
318
  }
246
- async function chatMessageToAnthropicAsync(chatMsg, limitChars) {
247
- const contentBlocks = await chatMessageToContentBlocksAsync(chatMsg, limitChars);
319
+ async function chatMessageToAnthropicAsync(chatMsg, limitChars, requestContext, allowedImageKeys, onToolResultImageIngest) {
320
+ const contentBlocks = await chatMessageToContentBlocksAsync(chatMsg, limitChars, requestContext, allowedImageKeys, onToolResultImageIngest);
248
321
  if (contentBlocks.length === 0) {
249
322
  throw new Error(`No content blocks generated for message: ${JSON.stringify(chatMsg)}`);
250
323
  }
@@ -257,7 +330,7 @@ async function chatMessageToAnthropicAsync(chatMsg, limitChars) {
257
330
  content: contentBlocks.length === 1 ? contentBlocks : contentBlocks,
258
331
  };
259
332
  }
260
- async function buildAnthropicRequestMessages(context, providerConfig) {
333
+ async function buildAnthropicRequestMessages(context, requestContext, providerConfig, onToolResultImageIngest) {
261
334
  // We keep the async path for func_result_msg because it may contain image artifacts.
262
335
  const normalized = (0, tool_call_context_1.normalizeToolCallPairs)(context);
263
336
  const violation = (0, tool_call_context_1.findFirstToolCallAdjacencyViolation)(normalized);
@@ -273,13 +346,25 @@ async function buildAnthropicRequestMessages(context, providerConfig) {
273
346
  }
274
347
  const messages = [];
275
348
  const toolResultMaxChars = (0, tool_output_limit_1.resolveProviderToolResultMaxChars)(providerConfig);
349
+ const allowedImageKeys = (0, tool_result_image_ingest_1.selectLatestToolResultImagesWithinBudget)(normalized, tool_result_image_ingest_1.ANTHROPIC_TOOL_RESULT_IMAGE_BUDGET_BYTES);
276
350
  for (const msg of normalized) {
277
- messages.push(await chatMessageToAnthropicAsync(msg, toolResultMaxChars));
351
+ messages.push(await chatMessageToAnthropicAsync(msg, toolResultMaxChars, requestContext, allowedImageKeys, onToolResultImageIngest));
278
352
  }
279
353
  return assembleAnthropicTurns(messages);
280
354
  }
281
- async function buildAnthropicRequestMessagesWrapper(context, providerConfig) {
282
- return await buildAnthropicRequestMessages(context, providerConfig);
355
+ async function buildAnthropicRequestMessagesWrapper(context, requestContextOrProviderConfig, providerConfigMaybe) {
356
+ const requestContext = isLlmRequestContext(requestContextOrProviderConfig)
357
+ ? requestContextOrProviderConfig
358
+ : {
359
+ dialogSelfId: '',
360
+ dialogRootId: '',
361
+ providerKey: 'anthropic',
362
+ modelKey: 'unknown',
363
+ };
364
+ const providerConfig = isLlmRequestContext(requestContextOrProviderConfig)
365
+ ? providerConfigMaybe
366
+ : requestContextOrProviderConfig;
367
+ return await buildAnthropicRequestMessages(context, requestContext, providerConfig);
283
368
  }
284
369
  /**
285
370
  * Reconstruct Anthropic context from persisted messages.
@@ -800,7 +885,12 @@ function reconstructAnthropicContextWrapper(persistedMessages) {
800
885
  return reconstructed;
801
886
  }
802
887
  async function reconstructAnthropicContextWrapperAsync(persistedMessages) {
803
- const reconstructed = await buildAnthropicRequestMessages(persistedMessages);
888
+ const reconstructed = await buildAnthropicRequestMessages(persistedMessages, {
889
+ dialogSelfId: '',
890
+ dialogRootId: '',
891
+ providerKey: 'anthropic',
892
+ modelKey: 'unknown',
893
+ });
804
894
  // Validate the reconstructed context
805
895
  try {
806
896
  validateReconstructedContext(reconstructed);
@@ -824,12 +914,12 @@ class AnthropicGen {
824
914
  classifyFailure(error) {
825
915
  return (0, failure_classifier_1.classifyAnthropicFailure)(error);
826
916
  }
827
- async genToReceiver(providerConfig, agent, systemPrompt, funcTools, _requestContext, context, receiver, _genseq, abortSignal) {
917
+ async genToReceiver(providerConfig, agent, systemPrompt, funcTools, requestContext, context, receiver, _genseq, abortSignal) {
828
918
  const apiKey = process.env[providerConfig.apiKeyEnvVar];
829
919
  if (!apiKey)
830
920
  throw new Error(`Missing API key env var ${providerConfig.apiKeyEnvVar}`);
831
921
  const client = new sdk_1.Anthropic({ apiKey, baseURL: providerConfig.baseUrl });
832
- const requestMessages = await buildAnthropicRequestMessages(context, providerConfig);
922
+ const requestMessages = await buildAnthropicRequestMessages(context, requestContext, providerConfig, receiver.toolResultImageIngest);
833
923
  const anthropicParams = agent.model_params?.anthropic || {};
834
924
  const forceJsonResponse = resolveAnthropicJsonResponseEnabled(agent);
835
925
  const maxTokens = agent.model_params?.max_tokens;
@@ -874,12 +964,15 @@ class AnthropicGen {
874
964
  const stream = client.messages.stream(streamParams);
875
965
  return consumeAnthropicStream(stream, receiver, abortSignal, forceJsonResponse ? ANTHROPIC_JSON_RESPONSE_TOOL_NAME : undefined);
876
966
  }
877
- async genMoreMessages(providerConfig, agent, systemPrompt, funcTools, _requestContext, context, genseq, abortSignal) {
967
+ async genMoreMessages(providerConfig, agent, systemPrompt, funcTools, requestContext, context, genseq, abortSignal) {
878
968
  const apiKey = process.env[providerConfig.apiKeyEnvVar];
879
969
  if (!apiKey)
880
970
  throw new Error(`Missing API key env var ${providerConfig.apiKeyEnvVar}`);
881
971
  const client = new sdk_1.Anthropic({ apiKey, baseURL: providerConfig.baseUrl });
882
- const requestMessages = await buildAnthropicRequestMessages(context, providerConfig);
972
+ const outputs = [];
973
+ const requestMessages = await buildAnthropicRequestMessages(context, requestContext, providerConfig, async (ingest) => {
974
+ outputs.push({ kind: 'tool_result_image_ingest', ingest });
975
+ });
883
976
  const anthropicParams = agent.model_params?.anthropic || {};
884
977
  const forceJsonResponse = resolveAnthropicJsonResponseEnabled(agent);
885
978
  const maxTokens = agent.model_params?.max_tokens;
@@ -940,6 +1033,7 @@ class AnthropicGen {
940
1033
  };
941
1034
  return {
942
1035
  messages: anthropicToChatMessages(response, genseq, forceJsonResponse ? ANTHROPIC_JSON_RESPONSE_TOOL_NAME : undefined),
1036
+ ...(outputs.length > 0 ? { outputs } : {}),
943
1037
  usage,
944
1038
  llmGenModel: returnedModel,
945
1039
  };