dominds 1.17.7 → 1.18.2

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 (140) hide show
  1. package/dist/dialog-fork.js +11 -5
  2. package/dist/dialog-instance-registry.js +1 -18
  3. package/dist/dialog.d.ts +21 -31
  4. package/dist/dialog.js +207 -56
  5. package/dist/docs/dialog-system.md +3 -2
  6. package/dist/docs/dialog-system.zh.md +3 -2
  7. package/dist/docs/tellask-collab.md +2 -1
  8. package/dist/docs/tellask-collab.zh.md +2 -1
  9. package/dist/llm/defaults.yaml +43 -0
  10. package/dist/llm/gen/anthropic.js +153 -12
  11. package/dist/llm/gen/codex.js +160 -10
  12. package/dist/llm/gen/openai-compatible.js +141 -81
  13. package/dist/llm/gen/openai.js +178 -12
  14. package/dist/llm/gen/tool-result-image-ingest.d.ts +17 -8
  15. package/dist/llm/gen/tool-result-image-ingest.js +127 -27
  16. package/dist/llm/gen.d.ts +13 -0
  17. package/dist/llm/kernel-driver/drive.js +79 -15
  18. package/dist/llm/kernel-driver/flow.js +158 -41
  19. package/dist/llm/kernel-driver/reply-guidance.d.ts +6 -6
  20. package/dist/llm/kernel-driver/reply-guidance.js +169 -2
  21. package/dist/llm/kernel-driver/runtime.d.ts +2 -2
  22. package/dist/llm/kernel-driver/subdialog.js +4 -0
  23. package/dist/llm/kernel-driver/tellask-special.d.ts +2 -0
  24. package/dist/llm/kernel-driver/tellask-special.js +11 -6
  25. package/dist/llm/kernel-driver/types.d.ts +14 -24
  26. package/dist/minds/system-prompt.js +8 -8
  27. package/dist/persistence.d.ts +6 -5
  28. package/dist/persistence.js +198 -39
  29. package/dist/priming.js +98 -3
  30. package/dist/runtime/driver-messages.d.ts +1 -0
  31. package/dist/runtime/driver-messages.js +32 -10
  32. package/dist/runtime/reply-prompt-copy.js +4 -4
  33. package/dist/server/api-routes.js +11 -43
  34. package/dist/server/websocket-handler.js +155 -10
  35. package/dist/tools/builtins.js +10 -4
  36. package/dist/tools/cmd-runner.js +110 -49
  37. package/dist/tools/picture.d.ts +3 -0
  38. package/dist/tools/picture.js +344 -0
  39. package/dist/tools/prompts/control/en/principles.md +4 -2
  40. package/dist/tools/prompts/control/en/scenarios.md +2 -1
  41. package/dist/tools/prompts/control/en/tools.md +6 -6
  42. package/dist/tools/prompts/control/zh/principles.md +4 -2
  43. package/dist/tools/prompts/control/zh/scenarios.md +2 -1
  44. package/dist/tools/prompts/control/zh/tools.md +1 -1
  45. package/dist/tools/prompts/ws_mod.en.md +1 -0
  46. package/dist/tools/prompts/ws_mod.zh.md +1 -0
  47. package/dist/tools/prompts/ws_read/en/tools.md +25 -5
  48. package/dist/tools/prompts/ws_read/zh/tools.md +25 -5
  49. package/package.json +4 -4
  50. package/webapp/dist/assets/{_basePickBy-u7tNFRWr.js → _basePickBy-BPJaiZdW.js} +3 -3
  51. package/webapp/dist/assets/{_basePickBy-u7tNFRWr.js.map → _basePickBy-BPJaiZdW.js.map} +1 -1
  52. package/webapp/dist/assets/{_baseUniq-CH9LRkiH.js → _baseUniq-BEetT15i.js} +2 -2
  53. package/webapp/dist/assets/{_baseUniq-CH9LRkiH.js.map → _baseUniq-BEetT15i.js.map} +1 -1
  54. package/webapp/dist/assets/{arc-Bo0Lw3ZP.js → arc-Dm7Zf36f.js} +2 -2
  55. package/webapp/dist/assets/{arc-Bo0Lw3ZP.js.map → arc-Dm7Zf36f.js.map} +1 -1
  56. package/webapp/dist/assets/{architectureDiagram-VXUJARFQ-Ckyr89Iw.js → architectureDiagram-VXUJARFQ-BpTPtkuo.js} +7 -7
  57. package/webapp/dist/assets/{architectureDiagram-VXUJARFQ-Ckyr89Iw.js.map → architectureDiagram-VXUJARFQ-BpTPtkuo.js.map} +1 -1
  58. package/webapp/dist/assets/{blockDiagram-VD42YOAC-BSXoLLq_.js → blockDiagram-VD42YOAC-C8fLN0iu.js} +7 -7
  59. package/webapp/dist/assets/{blockDiagram-VD42YOAC-BSXoLLq_.js.map → blockDiagram-VD42YOAC-C8fLN0iu.js.map} +1 -1
  60. package/webapp/dist/assets/{c4Diagram-YG6GDRKO-CgCG1cP0.js → c4Diagram-YG6GDRKO-BpPr62CH.js} +3 -3
  61. package/webapp/dist/assets/{c4Diagram-YG6GDRKO-CgCG1cP0.js.map → c4Diagram-YG6GDRKO-BpPr62CH.js.map} +1 -1
  62. package/webapp/dist/assets/{channel-Crbz0zgt.js → channel-EMYoPjW3.js} +2 -2
  63. package/webapp/dist/assets/{channel-Crbz0zgt.js.map → channel-EMYoPjW3.js.map} +1 -1
  64. package/webapp/dist/assets/{chunk-4BX2VUAB-BIIEb_5S.js → chunk-4BX2VUAB-CefNtjWG.js} +2 -2
  65. package/webapp/dist/assets/{chunk-4BX2VUAB-BIIEb_5S.js.map → chunk-4BX2VUAB-CefNtjWG.js.map} +1 -1
  66. package/webapp/dist/assets/{chunk-55IACEB6-CaJzGgc9.js → chunk-55IACEB6-C_X7T43V.js} +2 -2
  67. package/webapp/dist/assets/{chunk-55IACEB6-CaJzGgc9.js.map → chunk-55IACEB6-C_X7T43V.js.map} +1 -1
  68. package/webapp/dist/assets/{chunk-B4BG7PRW-DHQwhO9F.js → chunk-B4BG7PRW-BRe3_2oA.js} +5 -5
  69. package/webapp/dist/assets/{chunk-B4BG7PRW-DHQwhO9F.js.map → chunk-B4BG7PRW-BRe3_2oA.js.map} +1 -1
  70. package/webapp/dist/assets/{chunk-DI55MBZ5-CG1lO0R8.js → chunk-DI55MBZ5-CbvrsI_w.js} +4 -4
  71. package/webapp/dist/assets/{chunk-DI55MBZ5-CG1lO0R8.js.map → chunk-DI55MBZ5-CbvrsI_w.js.map} +1 -1
  72. package/webapp/dist/assets/{chunk-FMBD7UC4-DAUsTLPS.js → chunk-FMBD7UC4-ORmtkrtS.js} +2 -2
  73. package/webapp/dist/assets/{chunk-FMBD7UC4-DAUsTLPS.js.map → chunk-FMBD7UC4-ORmtkrtS.js.map} +1 -1
  74. package/webapp/dist/assets/{chunk-QN33PNHL-BfQs-QHE.js → chunk-QN33PNHL-LTAOVhWu.js} +2 -2
  75. package/webapp/dist/assets/{chunk-QN33PNHL-BfQs-QHE.js.map → chunk-QN33PNHL-LTAOVhWu.js.map} +1 -1
  76. package/webapp/dist/assets/{chunk-QZHKN3VN-C5iKQ6mQ.js → chunk-QZHKN3VN-ZoUM_4u5.js} +2 -2
  77. package/webapp/dist/assets/{chunk-QZHKN3VN-C5iKQ6mQ.js.map → chunk-QZHKN3VN-ZoUM_4u5.js.map} +1 -1
  78. package/webapp/dist/assets/{chunk-TZMSLE5B-CBShDwy2.js → chunk-TZMSLE5B-Gao4qrq7.js} +2 -2
  79. package/webapp/dist/assets/{chunk-TZMSLE5B-CBShDwy2.js.map → chunk-TZMSLE5B-Gao4qrq7.js.map} +1 -1
  80. package/webapp/dist/assets/{classDiagram-2ON5EDUG-DrfJDzYO.js → classDiagram-2ON5EDUG-uha1vIGN.js} +6 -6
  81. package/webapp/dist/assets/{classDiagram-2ON5EDUG-DrfJDzYO.js.map → classDiagram-2ON5EDUG-uha1vIGN.js.map} +1 -1
  82. package/webapp/dist/assets/{classDiagram-v2-WZHVMYZB-DrfJDzYO.js → classDiagram-v2-WZHVMYZB-uha1vIGN.js} +6 -6
  83. package/webapp/dist/assets/{classDiagram-v2-WZHVMYZB-DrfJDzYO.js.map → classDiagram-v2-WZHVMYZB-uha1vIGN.js.map} +1 -1
  84. package/webapp/dist/assets/{clone-Cd-48URG.js → clone-_9Ayb1Gp.js} +2 -2
  85. package/webapp/dist/assets/{clone-Cd-48URG.js.map → clone-_9Ayb1Gp.js.map} +1 -1
  86. package/webapp/dist/assets/{cose-bilkent-S5V4N54A-CCji0YN3.js → cose-bilkent-S5V4N54A-C8wDw3NY.js} +2 -2
  87. package/webapp/dist/assets/{cose-bilkent-S5V4N54A-CCji0YN3.js.map → cose-bilkent-S5V4N54A-C8wDw3NY.js.map} +1 -1
  88. package/webapp/dist/assets/{dagre-6UL2VRFP-B94p-Dpl.js → dagre-6UL2VRFP-BUSeNot0.js} +7 -7
  89. package/webapp/dist/assets/{dagre-6UL2VRFP-B94p-Dpl.js.map → dagre-6UL2VRFP-BUSeNot0.js.map} +1 -1
  90. package/webapp/dist/assets/{diagram-PSM6KHXK-DP-zGmAS.js → diagram-PSM6KHXK-CMZAksVC.js} +8 -8
  91. package/webapp/dist/assets/{diagram-PSM6KHXK-DP-zGmAS.js.map → diagram-PSM6KHXK-CMZAksVC.js.map} +1 -1
  92. package/webapp/dist/assets/{diagram-QEK2KX5R-DquJirs4.js → diagram-QEK2KX5R-BQKoRtwy.js} +7 -7
  93. package/webapp/dist/assets/{diagram-QEK2KX5R-DquJirs4.js.map → diagram-QEK2KX5R-BQKoRtwy.js.map} +1 -1
  94. package/webapp/dist/assets/{diagram-S2PKOQOG-Dt5W2t6V.js → diagram-S2PKOQOG-DjMG97kd.js} +7 -7
  95. package/webapp/dist/assets/{diagram-S2PKOQOG-Dt5W2t6V.js.map → diagram-S2PKOQOG-DjMG97kd.js.map} +1 -1
  96. package/webapp/dist/assets/{erDiagram-Q2GNP2WA-Bs0-2Rfj.js → erDiagram-Q2GNP2WA-BujwA137.js} +5 -5
  97. package/webapp/dist/assets/{erDiagram-Q2GNP2WA-Bs0-2Rfj.js.map → erDiagram-Q2GNP2WA-BujwA137.js.map} +1 -1
  98. package/webapp/dist/assets/{flowDiagram-NV44I4VS-cJjXWAlK.js → flowDiagram-NV44I4VS-DgwPjg4y.js} +6 -6
  99. package/webapp/dist/assets/{flowDiagram-NV44I4VS-cJjXWAlK.js.map → flowDiagram-NV44I4VS-DgwPjg4y.js.map} +1 -1
  100. package/webapp/dist/assets/{ganttDiagram-JELNMOA3-Du1AUaKm.js → ganttDiagram-JELNMOA3-Db2ykf3E.js} +3 -3
  101. package/webapp/dist/assets/{ganttDiagram-JELNMOA3-Du1AUaKm.js.map → ganttDiagram-JELNMOA3-Db2ykf3E.js.map} +1 -1
  102. package/webapp/dist/assets/{gitGraphDiagram-V2S2FVAM-D_jVOYOK.js → gitGraphDiagram-V2S2FVAM-D_gSifkv.js} +8 -8
  103. package/webapp/dist/assets/{gitGraphDiagram-V2S2FVAM-D_jVOYOK.js.map → gitGraphDiagram-V2S2FVAM-D_gSifkv.js.map} +1 -1
  104. package/webapp/dist/assets/{graph-CuF_sq4r.js → graph-BHjCU5xP.js} +3 -3
  105. package/webapp/dist/assets/{graph-CuF_sq4r.js.map → graph-BHjCU5xP.js.map} +1 -1
  106. package/webapp/dist/assets/{index-DAShQcjb.js → index-DLajsIDJ.js} +1363 -248
  107. package/webapp/dist/assets/{index-DAShQcjb.js.map → index-DLajsIDJ.js.map} +1 -1
  108. package/webapp/dist/assets/{infoDiagram-HS3SLOUP-CEFlo_Hl.js → infoDiagram-HS3SLOUP-BDba5pKs.js} +6 -6
  109. package/webapp/dist/assets/{infoDiagram-HS3SLOUP-CEFlo_Hl.js.map → infoDiagram-HS3SLOUP-BDba5pKs.js.map} +1 -1
  110. package/webapp/dist/assets/{journeyDiagram-XKPGCS4Q-zc2Q4Se9.js → journeyDiagram-XKPGCS4Q-CmJAbmlm.js} +5 -5
  111. package/webapp/dist/assets/{journeyDiagram-XKPGCS4Q-zc2Q4Se9.js.map → journeyDiagram-XKPGCS4Q-CmJAbmlm.js.map} +1 -1
  112. package/webapp/dist/assets/{kanban-definition-3W4ZIXB7-oT42RM2a.js → kanban-definition-3W4ZIXB7-DxQeBTDk.js} +3 -3
  113. package/webapp/dist/assets/{kanban-definition-3W4ZIXB7-oT42RM2a.js.map → kanban-definition-3W4ZIXB7-DxQeBTDk.js.map} +1 -1
  114. package/webapp/dist/assets/{layout-BvaOu3k2.js → layout-DteV_yE8.js} +5 -5
  115. package/webapp/dist/assets/{layout-BvaOu3k2.js.map → layout-DteV_yE8.js.map} +1 -1
  116. package/webapp/dist/assets/{linear-Cg-CjocS.js → linear-zItbPrND.js} +2 -2
  117. package/webapp/dist/assets/{linear-Cg-CjocS.js.map → linear-zItbPrND.js.map} +1 -1
  118. package/webapp/dist/assets/{mindmap-definition-VGOIOE7T-CVFVrU22.js → mindmap-definition-VGOIOE7T-BJXI7UqO.js} +4 -4
  119. package/webapp/dist/assets/{mindmap-definition-VGOIOE7T-CVFVrU22.js.map → mindmap-definition-VGOIOE7T-BJXI7UqO.js.map} +1 -1
  120. package/webapp/dist/assets/{pieDiagram-ADFJNKIX-Bai5CMos.js → pieDiagram-ADFJNKIX-BpM-aH2p.js} +8 -8
  121. package/webapp/dist/assets/{pieDiagram-ADFJNKIX-Bai5CMos.js.map → pieDiagram-ADFJNKIX-BpM-aH2p.js.map} +1 -1
  122. package/webapp/dist/assets/{quadrantDiagram-AYHSOK5B-BPXDO_2E.js → quadrantDiagram-AYHSOK5B-NXdIpA15.js} +3 -3
  123. package/webapp/dist/assets/{quadrantDiagram-AYHSOK5B-BPXDO_2E.js.map → quadrantDiagram-AYHSOK5B-NXdIpA15.js.map} +1 -1
  124. package/webapp/dist/assets/{requirementDiagram-UZGBJVZJ-Dgj9X9cE.js → requirementDiagram-UZGBJVZJ-D1AICAA0.js} +4 -4
  125. package/webapp/dist/assets/{requirementDiagram-UZGBJVZJ-Dgj9X9cE.js.map → requirementDiagram-UZGBJVZJ-D1AICAA0.js.map} +1 -1
  126. package/webapp/dist/assets/{sankeyDiagram-TZEHDZUN-Dc0mO4OD.js → sankeyDiagram-TZEHDZUN-WiReDPfo.js} +2 -2
  127. package/webapp/dist/assets/{sankeyDiagram-TZEHDZUN-Dc0mO4OD.js.map → sankeyDiagram-TZEHDZUN-WiReDPfo.js.map} +1 -1
  128. package/webapp/dist/assets/{sequenceDiagram-WL72ISMW-DZJTga0d.js → sequenceDiagram-WL72ISMW-Cw76oP8t.js} +4 -4
  129. package/webapp/dist/assets/{sequenceDiagram-WL72ISMW-DZJTga0d.js.map → sequenceDiagram-WL72ISMW-Cw76oP8t.js.map} +1 -1
  130. package/webapp/dist/assets/{stateDiagram-FKZM4ZOC-RNxYatKM.js → stateDiagram-FKZM4ZOC-QjCeRczs.js} +9 -9
  131. package/webapp/dist/assets/{stateDiagram-FKZM4ZOC-RNxYatKM.js.map → stateDiagram-FKZM4ZOC-QjCeRczs.js.map} +1 -1
  132. package/webapp/dist/assets/{stateDiagram-v2-4FDKWEC3-ADxYqWzo.js → stateDiagram-v2-4FDKWEC3-IClqxQ4s.js} +5 -5
  133. package/webapp/dist/assets/{stateDiagram-v2-4FDKWEC3-ADxYqWzo.js.map → stateDiagram-v2-4FDKWEC3-IClqxQ4s.js.map} +1 -1
  134. package/webapp/dist/assets/{timeline-definition-IT6M3QCI-Qx_h1e-i.js → timeline-definition-IT6M3QCI-BfyfTY7m.js} +3 -3
  135. package/webapp/dist/assets/{timeline-definition-IT6M3QCI-Qx_h1e-i.js.map → timeline-definition-IT6M3QCI-BfyfTY7m.js.map} +1 -1
  136. package/webapp/dist/assets/{treemap-GDKQZRPO-BHzYvXGn.js → treemap-GDKQZRPO-C5MiL6--.js} +5 -5
  137. package/webapp/dist/assets/{treemap-GDKQZRPO-BHzYvXGn.js.map → treemap-GDKQZRPO-C5MiL6--.js.map} +1 -1
  138. package/webapp/dist/assets/{xychartDiagram-PRI3JC2R-DGdjkYQQ.js → xychartDiagram-PRI3JC2R-ybaJrSry.js} +3 -3
  139. package/webapp/dist/assets/{xychartDiagram-PRI3JC2R-DGdjkYQQ.js.map → xychartDiagram-PRI3JC2R-ybaJrSry.js.map} +1 -1
  140. package/webapp/dist/index.html +1 -1
@@ -211,6 +211,127 @@ function chatMessageToOpenAiInputItem(msg) {
211
211
  }
212
212
  }
213
213
  }
214
+ async function userLikeMessageToOpenAiInputItemWithImages(msg, requestContext, providerConfig, allowedImageKeys, onUserImageIngest) {
215
+ const items = msg.contentItems;
216
+ if (!Array.isArray(items) || items.length === 0) {
217
+ return chatMessageToOpenAiInputItem(msg);
218
+ }
219
+ const content = [{ type: 'input_text', text: msg.content }];
220
+ const supportsImageInput = (0, tool_result_image_ingest_1.resolveModelImageInputSupport)(requestContext.modelKey === undefined
221
+ ? undefined
222
+ : providerConfig?.models[requestContext.modelKey], true);
223
+ for (const [itemIndex, item] of items.entries()) {
224
+ if (item.type === 'input_text') {
225
+ content.push({ type: 'input_text', text: item.text });
226
+ continue;
227
+ }
228
+ if (item.type === 'input_image') {
229
+ if (!supportsImageInput) {
230
+ if (onUserImageIngest) {
231
+ await onUserImageIngest((0, tool_result_image_ingest_1.buildUserImageIngest)({
232
+ requestContext,
233
+ ...(msg.type === 'prompting_msg' ? { msgId: msg.msgId } : {}),
234
+ artifact: item.artifact,
235
+ disposition: 'filtered_model_unsupported',
236
+ providerPathLabel: 'OpenAI Responses path',
237
+ }));
238
+ }
239
+ content.push({
240
+ type: 'input_text',
241
+ text: `[image not sent: current model does not support image input]`,
242
+ });
243
+ continue;
244
+ }
245
+ if (!(0, artifacts_1.isVisionImageMimeType)(item.mimeType)) {
246
+ if (onUserImageIngest) {
247
+ await onUserImageIngest((0, tool_result_image_ingest_1.buildUserImageIngest)({
248
+ requestContext,
249
+ ...(msg.type === 'prompting_msg' ? { msgId: msg.msgId } : {}),
250
+ artifact: item.artifact,
251
+ disposition: 'filtered_mime_unsupported',
252
+ mimeType: item.mimeType,
253
+ providerPathLabel: 'OpenAI Responses path',
254
+ }));
255
+ }
256
+ content.push({
257
+ type: 'input_text',
258
+ text: `[image not sent: unsupported mimeType=${item.mimeType}]`,
259
+ });
260
+ continue;
261
+ }
262
+ if (!allowedImageKeys.has((0, tool_result_image_ingest_1.buildImageBudgetKeyForContentItem)({ msg, itemIndex, artifact: item.artifact }))) {
263
+ if (onUserImageIngest) {
264
+ await onUserImageIngest((0, tool_result_image_ingest_1.buildUserImageIngest)({
265
+ requestContext,
266
+ ...(msg.type === 'prompting_msg' ? { msgId: msg.msgId } : {}),
267
+ artifact: item.artifact,
268
+ disposition: 'filtered_size_limit',
269
+ detail: (0, tool_result_image_ingest_1.buildImageBudgetLimitDetail)({
270
+ byteLength: item.byteLength,
271
+ budgetBytes: tool_result_image_ingest_1.OPENAI_TOOL_RESULT_IMAGE_BUDGET_BYTES,
272
+ }),
273
+ providerPathLabel: 'OpenAI Responses path',
274
+ }));
275
+ }
276
+ content.push({
277
+ type: 'input_text',
278
+ text: `[image not sent: request image budget exceeded bytes=${String(item.byteLength)} budget=${String(tool_result_image_ingest_1.OPENAI_TOOL_RESULT_IMAGE_BUDGET_BYTES)}]`,
279
+ });
280
+ continue;
281
+ }
282
+ const bytesResult = await (0, tool_result_image_ingest_1.readToolResultImageBytesSafe)(item.artifact);
283
+ if (bytesResult.kind === 'missing') {
284
+ if (onUserImageIngest) {
285
+ await onUserImageIngest((0, tool_result_image_ingest_1.buildUserImageIngest)({
286
+ requestContext,
287
+ ...(msg.type === 'prompting_msg' ? { msgId: msg.msgId } : {}),
288
+ artifact: item.artifact,
289
+ disposition: 'filtered_missing',
290
+ providerPathLabel: 'OpenAI Responses path',
291
+ }));
292
+ }
293
+ content.push({ type: 'input_text', text: `[image missing: ${item.artifact.relPath}]` });
294
+ continue;
295
+ }
296
+ if (bytesResult.kind === 'read_failed') {
297
+ if (onUserImageIngest) {
298
+ await onUserImageIngest((0, tool_result_image_ingest_1.buildUserImageIngest)({
299
+ requestContext,
300
+ ...(msg.type === 'prompting_msg' ? { msgId: msg.msgId } : {}),
301
+ artifact: item.artifact,
302
+ disposition: 'filtered_read_failed',
303
+ detail: bytesResult.detail,
304
+ providerPathLabel: 'OpenAI Responses path',
305
+ }));
306
+ }
307
+ content.push({ type: 'input_text', text: `[image unreadable: ${item.artifact.relPath}]` });
308
+ continue;
309
+ }
310
+ if (onUserImageIngest) {
311
+ await onUserImageIngest((0, tool_result_image_ingest_1.buildUserImageIngest)({
312
+ requestContext,
313
+ ...(msg.type === 'prompting_msg' ? { msgId: msg.msgId } : {}),
314
+ artifact: item.artifact,
315
+ disposition: 'fed_native',
316
+ providerPathLabel: 'OpenAI Responses path',
317
+ }));
318
+ }
319
+ content.push({
320
+ type: 'input_image',
321
+ detail: 'auto',
322
+ image_url: (0, artifacts_1.bytesToDataUrl)({ mimeType: item.mimeType, bytes: bytesResult.bytes }),
323
+ });
324
+ continue;
325
+ }
326
+ const _exhaustive = item;
327
+ throw new Error(`Unsupported user content item: ${String(_exhaustive)}`);
328
+ }
329
+ return {
330
+ type: 'message',
331
+ role: 'user',
332
+ content,
333
+ };
334
+ }
214
335
  function buildReasoningPayloadFromText(text) {
215
336
  if (text.trim().length === 0)
216
337
  return undefined;
@@ -238,7 +359,7 @@ function thinkingMessageToOpenAiReasoningItem(msg) {
238
359
  }
239
360
  return out;
240
361
  }
241
- async function funcResultToOpenAiInputItemWithLimit(msg, limitChars, requestContext, allowedImageKeys, onToolResultImageIngest) {
362
+ async function funcResultToOpenAiInputItemWithLimit(msg, limitChars, requestContext, allowedImageKeys, supportsImageInput, onToolResultImageIngest) {
242
363
  const items = msg.contentItems;
243
364
  if (!Array.isArray(items) || items.length === 0) {
244
365
  return {
@@ -248,12 +369,29 @@ async function funcResultToOpenAiInputItemWithLimit(msg, limitChars, requestCont
248
369
  };
249
370
  }
250
371
  const output = [];
251
- for (const item of items) {
372
+ for (const [itemIndex, item] of items.entries()) {
252
373
  if (item.type === 'input_text') {
253
374
  output.push({ type: 'input_text', text: item.text });
254
375
  continue;
255
376
  }
256
377
  if (item.type === 'input_image') {
378
+ if (!supportsImageInput) {
379
+ if (onToolResultImageIngest) {
380
+ await onToolResultImageIngest((0, tool_result_image_ingest_1.buildToolResultImageIngest)({
381
+ requestContext,
382
+ toolCallId: msg.id,
383
+ toolName: msg.name,
384
+ artifact: item.artifact,
385
+ disposition: 'filtered_model_unsupported',
386
+ providerPathLabel: 'OpenAI Responses path',
387
+ }));
388
+ }
389
+ output.push({
390
+ type: 'input_text',
391
+ text: `[image not sent: current model does not support image input]`,
392
+ });
393
+ continue;
394
+ }
257
395
  if (!(0, artifacts_1.isVisionImageMimeType)(item.mimeType)) {
258
396
  if (onToolResultImageIngest) {
259
397
  await onToolResultImageIngest((0, tool_result_image_ingest_1.buildToolResultImageIngest)({
@@ -272,7 +410,7 @@ async function funcResultToOpenAiInputItemWithLimit(msg, limitChars, requestCont
272
410
  });
273
411
  continue;
274
412
  }
275
- if (!allowedImageKeys.has((0, tool_result_image_ingest_1.buildToolResultImageBudgetKeyForMsg)(msg, item.artifact))) {
413
+ if (!allowedImageKeys.has((0, tool_result_image_ingest_1.buildImageBudgetKeyForContentItem)({ msg, itemIndex, artifact: item.artifact }))) {
276
414
  if (onToolResultImageIngest) {
277
415
  await onToolResultImageIngest((0, tool_result_image_ingest_1.buildToolResultImageIngest)({
278
416
  requestContext,
@@ -280,7 +418,7 @@ async function funcResultToOpenAiInputItemWithLimit(msg, limitChars, requestCont
280
418
  toolName: msg.name,
281
419
  artifact: item.artifact,
282
420
  disposition: 'filtered_size_limit',
283
- detail: (0, tool_result_image_ingest_1.buildToolResultImageBudgetLimitDetail)({
421
+ detail: (0, tool_result_image_ingest_1.buildImageBudgetLimitDetail)({
284
422
  byteLength: item.byteLength,
285
423
  budgetBytes: tool_result_image_ingest_1.OPENAI_TOOL_RESULT_IMAGE_BUDGET_BYTES,
286
424
  }),
@@ -394,7 +532,7 @@ function shouldIncludeOpenAiEncryptedReasoning(input, reasoning) {
394
532
  return true;
395
533
  return input.some((item) => isRecord(item) && item.type === 'reasoning');
396
534
  }
397
- async function buildOpenAiRequestInput(context, requestContext, providerConfig, onToolResultImageIngest) {
535
+ async function buildOpenAiRequestInput(context, requestContext, providerConfig, onToolResultImageIngest, onUserImageIngest) {
398
536
  const normalized = (0, tool_call_context_1.normalizeToolCallPairs)(context);
399
537
  const violation = (0, tool_call_context_1.findFirstToolCallAdjacencyViolation)(normalized);
400
538
  if (violation) {
@@ -409,10 +547,21 @@ async function buildOpenAiRequestInput(context, requestContext, providerConfig,
409
547
  }
410
548
  const input = [];
411
549
  const toolResultMaxChars = (0, tool_output_limit_1.resolveProviderToolResultMaxChars)(providerConfig);
412
- const allowedImageKeys = (0, tool_result_image_ingest_1.selectLatestToolResultImagesWithinBudget)(normalized, tool_result_image_ingest_1.OPENAI_TOOL_RESULT_IMAGE_BUDGET_BYTES);
550
+ const allowedImageKeys = (0, tool_result_image_ingest_1.selectLatestImagesWithinBudget)(normalized, tool_result_image_ingest_1.OPENAI_TOOL_RESULT_IMAGE_BUDGET_BYTES);
551
+ const supportsImageInput = (0, tool_result_image_ingest_1.resolveModelImageInputSupport)(requestContext.modelKey === undefined
552
+ ? undefined
553
+ : providerConfig?.models[requestContext.modelKey], true);
413
554
  for (const msg of normalized) {
555
+ if ((msg.type === 'prompting_msg' ||
556
+ msg.type === 'tellask_result_msg' ||
557
+ msg.type === 'tellask_carryover_msg') &&
558
+ Array.isArray(msg.contentItems) &&
559
+ msg.contentItems.length > 0) {
560
+ input.push(await userLikeMessageToOpenAiInputItemWithImages(msg, requestContext, providerConfig, allowedImageKeys, onUserImageIngest));
561
+ continue;
562
+ }
414
563
  if (msg.type === 'func_result_msg') {
415
- input.push(await funcResultToOpenAiInputItemWithLimit(msg, toolResultMaxChars, requestContext, allowedImageKeys, onToolResultImageIngest));
564
+ input.push(await funcResultToOpenAiInputItemWithLimit(msg, toolResultMaxChars, requestContext, allowedImageKeys, supportsImageInput, onToolResultImageIngest));
416
565
  continue;
417
566
  }
418
567
  input.push(chatMessageToOpenAiInputItem(msg));
@@ -1063,7 +1212,7 @@ class OpenAiGen {
1063
1212
  throw new Error(`Internal error: Model is undefined for agent '${agent.id}'`);
1064
1213
  }
1065
1214
  const client = new openai_1.default({ apiKey, baseURL: providerConfig.baseUrl });
1066
- const requestInput = await buildOpenAiRequestInput(context, requestContext, providerConfig, receiver.toolResultImageIngest);
1215
+ const requestInput = await buildOpenAiRequestInput(context, requestContext, providerConfig, receiver.toolResultImageIngest, receiver.userImageIngest);
1067
1216
  const openAiParams = agent.model_params?.openai || {};
1068
1217
  const maxTokens = agent.model_params?.max_tokens;
1069
1218
  const modelInfo = providerConfig.models[agent.model];
@@ -1100,12 +1249,13 @@ class OpenAiGen {
1100
1249
  let sayingStarted = false;
1101
1250
  let thinkingStarted = false;
1102
1251
  let currentThinkingContent = '';
1103
- let finishedThinkingFromDelta = false;
1104
1252
  let sawOutputText = false;
1105
1253
  let currentAudioTranscript = '';
1106
1254
  let activeStream = 'idle';
1107
1255
  let usage = { kind: 'unavailable' };
1108
1256
  let returnedModel;
1257
+ const streamedReasoningItemIds = new Set();
1258
+ let sawReasoningDeltaWithoutItemId = false;
1109
1259
  const nativeToolTracker = new OpenAiNativeToolTracker();
1110
1260
  function claimNativeToolStateByEventItemId(itemId, itemType, title) {
1111
1261
  try {
@@ -1334,6 +1484,12 @@ class OpenAiGen {
1334
1484
  await receiver.thinkingStart();
1335
1485
  activeStream = 'thinking';
1336
1486
  }
1487
+ if (typeof event.item_id === 'string' && event.item_id.length > 0) {
1488
+ streamedReasoningItemIds.add(event.item_id);
1489
+ }
1490
+ else {
1491
+ sawReasoningDeltaWithoutItemId = true;
1492
+ }
1337
1493
  currentThinkingContent += delta;
1338
1494
  await receiver.thinkingChunk(delta);
1339
1495
  }
@@ -1367,7 +1523,6 @@ class OpenAiGen {
1367
1523
  await receiver.thinkingFinish(buildReasoningPayloadFromText(currentThinkingContent));
1368
1524
  thinkingStarted = false;
1369
1525
  currentThinkingContent = '';
1370
- finishedThinkingFromDelta = true;
1371
1526
  if (activeStream === 'thinking')
1372
1527
  activeStream = 'idle';
1373
1528
  }
@@ -1476,8 +1631,17 @@ class OpenAiGen {
1476
1631
  break;
1477
1632
  }
1478
1633
  if (isRecord(item) && item.type === 'reasoning') {
1479
- if (finishedThinkingFromDelta) {
1480
- finishedThinkingFromDelta = false;
1634
+ const itemId = typeof item.id === 'string' && item.id.length > 0 ? item.id : null;
1635
+ const sawReasoningDelta = itemId !== null
1636
+ ? streamedReasoningItemIds.has(itemId)
1637
+ : sawReasoningDeltaWithoutItemId;
1638
+ if (sawReasoningDelta) {
1639
+ if (itemId !== null) {
1640
+ streamedReasoningItemIds.delete(itemId);
1641
+ }
1642
+ else {
1643
+ sawReasoningDeltaWithoutItemId = false;
1644
+ }
1481
1645
  break;
1482
1646
  }
1483
1647
  const payload = extractReasoningPayload(item);
@@ -1821,6 +1985,8 @@ class OpenAiGen {
1821
1985
  const outputs = [];
1822
1986
  const requestInput = await buildOpenAiRequestInput(context, requestContext, providerConfig, async (ingest) => {
1823
1987
  outputs.push({ kind: 'tool_result_image_ingest', ingest });
1988
+ }, async (ingest) => {
1989
+ outputs.push({ kind: 'user_image_ingest', ingest });
1824
1990
  });
1825
1991
  const openAiParams = agent.model_params?.openai || {};
1826
1992
  const maxTokens = agent.model_params?.max_tokens;
@@ -1,6 +1,6 @@
1
1
  import type { ToolResultImageArtifact, ToolResultImageDisposition } from '@longrun-ai/kernel/types/storage';
2
- import type { ChatMessage, FuncResultMsg } from '../client';
3
- import type { LlmRequestContext, ToolResultImageIngest } from '../gen';
2
+ import type { ChatMessage, ModelInfo } from '../client';
3
+ import type { LlmRequestContext, ToolResultImageIngest, UserImageIngest } from '../gen';
4
4
  type ToolResultImageReadResult = {
5
5
  kind: 'ready';
6
6
  bytes: Buffer;
@@ -14,17 +14,17 @@ export declare const OPENAI_TOOL_RESULT_IMAGE_BUDGET_BYTES: number;
14
14
  export declare const OPENAI_COMPATIBLE_TOOL_RESULT_IMAGE_BUDGET_BYTES: number;
15
15
  export declare const CODEX_TOOL_RESULT_IMAGE_BUDGET_BYTES: number;
16
16
  export declare const ANTHROPIC_TOOL_RESULT_IMAGE_BUDGET_BYTES: number;
17
- export declare function buildToolResultImageBudgetLimitDetail(args: {
17
+ export declare function resolveModelImageInputSupport(modelInfo: ModelInfo | undefined, defaultValue: boolean): boolean;
18
+ export declare function buildImageBudgetLimitDetail(args: {
18
19
  byteLength: number;
19
20
  budgetBytes: number;
20
21
  }): string;
21
- export declare function buildToolResultImageBudgetKey(args: {
22
- genseq: number;
23
- toolCallId: string;
22
+ export declare function buildImageBudgetKeyForContentItem(args: {
23
+ msg: ChatMessage;
24
+ itemIndex: number;
24
25
  artifact: ToolResultImageArtifact;
25
26
  }): string;
26
- export declare function buildToolResultImageBudgetKeyForMsg(msg: FuncResultMsg, artifact: ToolResultImageArtifact): string;
27
- export declare function selectLatestToolResultImagesWithinBudget(context: ChatMessage[], budgetBytes: number): Set<string>;
27
+ export declare function selectLatestImagesWithinBudget(context: ChatMessage[], budgetBytes: number): Set<string>;
28
28
  export declare function readToolResultImageBytesSafe(artifact: ToolResultImageArtifact): Promise<ToolResultImageReadResult>;
29
29
  export declare function buildToolResultImageIngest(args: {
30
30
  requestContext: LlmRequestContext;
@@ -36,4 +36,13 @@ export declare function buildToolResultImageIngest(args: {
36
36
  detail?: string;
37
37
  providerPathLabel?: string;
38
38
  }): ToolResultImageIngest;
39
+ export declare function buildUserImageIngest(args: {
40
+ requestContext: LlmRequestContext;
41
+ msgId?: string;
42
+ artifact: ToolResultImageArtifact;
43
+ disposition: ToolResultImageDisposition;
44
+ mimeType?: string;
45
+ detail?: string;
46
+ providerPathLabel?: string;
47
+ }): UserImageIngest;
39
48
  export {};
@@ -1,12 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ANTHROPIC_TOOL_RESULT_IMAGE_BUDGET_BYTES = exports.CODEX_TOOL_RESULT_IMAGE_BUDGET_BYTES = exports.OPENAI_COMPATIBLE_TOOL_RESULT_IMAGE_BUDGET_BYTES = exports.OPENAI_TOOL_RESULT_IMAGE_BUDGET_BYTES = void 0;
4
- exports.buildToolResultImageBudgetLimitDetail = buildToolResultImageBudgetLimitDetail;
5
- exports.buildToolResultImageBudgetKey = buildToolResultImageBudgetKey;
6
- exports.buildToolResultImageBudgetKeyForMsg = buildToolResultImageBudgetKeyForMsg;
7
- exports.selectLatestToolResultImagesWithinBudget = selectLatestToolResultImagesWithinBudget;
4
+ exports.resolveModelImageInputSupport = resolveModelImageInputSupport;
5
+ exports.buildImageBudgetLimitDetail = buildImageBudgetLimitDetail;
6
+ exports.buildImageBudgetKeyForContentItem = buildImageBudgetKeyForContentItem;
7
+ exports.selectLatestImagesWithinBudget = selectLatestImagesWithinBudget;
8
8
  exports.readToolResultImageBytesSafe = readToolResultImageBytesSafe;
9
9
  exports.buildToolResultImageIngest = buildToolResultImageIngest;
10
+ exports.buildUserImageIngest = buildUserImageIngest;
10
11
  const work_language_1 = require("../../runtime/work-language");
11
12
  const artifacts_1 = require("./artifacts");
12
13
  // These are provider-path guardrails derived from publicly documented request payload limits.
@@ -23,32 +24,71 @@ exports.OPENAI_TOOL_RESULT_IMAGE_BUDGET_BYTES = 50 * 1024 * 1024;
23
24
  exports.OPENAI_COMPATIBLE_TOOL_RESULT_IMAGE_BUDGET_BYTES = 50 * 1024 * 1024;
24
25
  exports.CODEX_TOOL_RESULT_IMAGE_BUDGET_BYTES = 50 * 1024 * 1024;
25
26
  exports.ANTHROPIC_TOOL_RESULT_IMAGE_BUDGET_BYTES = 32 * 1024 * 1024;
26
- function buildToolResultImageBudgetLimitDetail(args) {
27
+ function resolveModelImageInputSupport(modelInfo, defaultValue) {
28
+ const value = modelInfo?.['supports_image_input'];
29
+ if (value === undefined)
30
+ return defaultValue;
31
+ return value === true;
32
+ }
33
+ function resolveProviderModelLabel(requestContext) {
34
+ const providerKey = typeof requestContext.providerKey === 'string' && requestContext.providerKey.trim().length > 0
35
+ ? requestContext.providerKey.trim()
36
+ : 'unknown-provider';
37
+ const modelKey = typeof requestContext.modelKey === 'string' && requestContext.modelKey.trim().length > 0
38
+ ? requestContext.modelKey.trim()
39
+ : 'unknown-model';
40
+ return {
41
+ providerKey,
42
+ modelKey,
43
+ providerModel: `${providerKey}/${modelKey}`,
44
+ };
45
+ }
46
+ function buildImageBudgetLimitDetail(args) {
27
47
  return `image_bytes=${String(args.byteLength)}, request_image_budget_bytes=${String(args.budgetBytes)}`;
28
48
  }
29
- function buildToolResultImageBudgetKey(args) {
30
- return `${String(args.genseq)}::${args.toolCallId}::${args.artifact.rootId}::${args.artifact.selfId}::${args.artifact.relPath}`;
49
+ function imageBudgetMessageKey(msg) {
50
+ switch (msg.type) {
51
+ case 'prompting_msg':
52
+ return `prompting:${String(msg.genseq)}:${msg.msgId}`;
53
+ case 'func_result_msg':
54
+ return `func_result:${String(msg.genseq)}:${msg.id}`;
55
+ case 'tellask_result_msg':
56
+ return `tellask_result:${msg.callName}:${msg.callId}`;
57
+ case 'tellask_carryover_msg':
58
+ return `tellask_carryover:${String(msg.genseq)}:${msg.callName}:${msg.callId}`;
59
+ case 'environment_msg':
60
+ case 'transient_guide_msg':
61
+ case 'saying_msg':
62
+ case 'thinking_msg':
63
+ case 'func_call_msg':
64
+ return null;
65
+ default: {
66
+ const _exhaustive = msg;
67
+ return _exhaustive;
68
+ }
69
+ }
31
70
  }
32
- function buildToolResultImageBudgetKeyForMsg(msg, artifact) {
33
- return buildToolResultImageBudgetKey({
34
- genseq: msg.genseq,
35
- toolCallId: msg.id,
36
- artifact,
37
- });
71
+ function buildImageBudgetKeyForContentItem(args) {
72
+ const msgKey = imageBudgetMessageKey(args.msg);
73
+ if (msgKey === null) {
74
+ throw new Error(`Message type ${args.msg.type} cannot carry image budget items`);
75
+ }
76
+ return `${msgKey}:${String(args.itemIndex)}:${args.artifact.rootId}:${args.artifact.selfId}:${args.artifact.relPath}`;
38
77
  }
39
- function selectLatestToolResultImagesWithinBudget(context, budgetBytes) {
78
+ function selectLatestImagesWithinBudget(context, budgetBytes) {
40
79
  const candidates = [];
41
80
  for (const msg of context) {
42
- if (msg.type !== 'func_result_msg')
81
+ const msgKey = imageBudgetMessageKey(msg);
82
+ if (msgKey === null)
43
83
  continue;
44
- const items = msg.contentItems;
84
+ const items = 'contentItems' in msg ? msg.contentItems : undefined;
45
85
  if (!Array.isArray(items) || items.length === 0)
46
86
  continue;
47
- for (const item of items) {
87
+ for (const [itemIndex, item] of items.entries()) {
48
88
  if (item.type !== 'input_image')
49
89
  continue;
50
90
  candidates.push({
51
- key: buildToolResultImageBudgetKeyForMsg(msg, item.artifact),
91
+ key: buildImageBudgetKeyForContentItem({ msg, itemIndex, artifact: item.artifact }),
52
92
  byteLength: item.byteLength,
53
93
  });
54
94
  }
@@ -81,15 +121,7 @@ async function readToolResultImageBytesSafe(artifact) {
81
121
  }
82
122
  function buildToolResultImageIngest(args) {
83
123
  const language = (0, work_language_1.getWorkLanguage)();
84
- const providerKey = typeof args.requestContext.providerKey === 'string' &&
85
- args.requestContext.providerKey.trim().length > 0
86
- ? args.requestContext.providerKey.trim()
87
- : 'unknown-provider';
88
- const modelKey = typeof args.requestContext.modelKey === 'string' &&
89
- args.requestContext.modelKey.trim().length > 0
90
- ? args.requestContext.modelKey.trim()
91
- : 'unknown-model';
92
- const providerModel = `${providerKey}/${modelKey}`;
124
+ const { providerKey, modelKey, providerModel } = resolveProviderModelLabel(args.requestContext);
93
125
  const pathLabel = typeof args.providerPathLabel === 'string' && args.providerPathLabel.trim().length > 0
94
126
  ? args.providerPathLabel.trim()
95
127
  : 'current provider path';
@@ -156,3 +188,71 @@ function buildToolResultImageIngest(args) {
156
188
  ...(args.detail !== undefined ? { detail: args.detail } : {}),
157
189
  };
158
190
  }
191
+ function buildUserImageIngest(args) {
192
+ const language = (0, work_language_1.getWorkLanguage)();
193
+ const { providerKey, modelKey, providerModel } = resolveProviderModelLabel(args.requestContext);
194
+ const pathLabel = typeof args.providerPathLabel === 'string' && args.providerPathLabel.trim().length > 0
195
+ ? args.providerPathLabel.trim()
196
+ : 'current provider path';
197
+ const relPath = args.artifact.relPath;
198
+ const mimeText = typeof args.mimeType === 'string' && args.mimeType.trim().length > 0
199
+ ? args.mimeType
200
+ : undefined;
201
+ const message = (() => {
202
+ if (language === 'zh') {
203
+ switch (args.disposition) {
204
+ case 'fed_native':
205
+ return `本轮已将这张用户附件图片发送给 ${providerModel}。`;
206
+ case 'fed_provider_transformed':
207
+ return `本轮已将这张用户附件图片发送给 ${providerModel}(按当前 provider 的原生图片消息方式投喂)。`;
208
+ case 'filtered_provider_unsupported':
209
+ return `本轮未将这张用户附件图片发送给 ${providerModel}:当前 ${pathLabel} 不支持图片输入。对话仍会继续,但模型不会看到这张图片。`;
210
+ case 'filtered_model_unsupported':
211
+ return `本轮未将这张用户附件图片发送给 ${providerModel}:当前模型不支持图片输入。对话仍会继续,但模型不会看到这张图片。`;
212
+ case 'filtered_mime_unsupported':
213
+ return `本轮未将这张用户附件图片发送给 ${providerModel}:当前 ${pathLabel} 不接受该图片格式${mimeText ? `(${mimeText})` : ''}。对话仍会继续,但模型不会看到这张图片。`;
214
+ case 'filtered_size_limit':
215
+ return `本轮未将这张用户附件图片发送给 ${providerModel}:图片超出当前 ${pathLabel} 的限制。对话仍会继续,但模型不会看到这张图片。`;
216
+ case 'filtered_read_failed':
217
+ return `本轮未将这张用户附件图片发送给 ${providerModel}:读取图片 artifact 失败(${relPath})。对话仍会继续,但模型不会看到这张图片。`;
218
+ case 'filtered_missing':
219
+ return `本轮未将这张用户附件图片发送给 ${providerModel}:图片 artifact 缺失(${relPath})。对话仍会继续,但模型不会看到这张图片。`;
220
+ default: {
221
+ const _exhaustive = args.disposition;
222
+ return _exhaustive;
223
+ }
224
+ }
225
+ }
226
+ switch (args.disposition) {
227
+ case 'fed_native':
228
+ return `This round sent this user attachment image to ${providerModel}.`;
229
+ case 'fed_provider_transformed':
230
+ return `This round sent this user attachment image to ${providerModel} using the provider's native image-message projection.`;
231
+ case 'filtered_provider_unsupported':
232
+ return `This round did not send this user attachment image to ${providerModel}: the current ${pathLabel} does not support image input. The dialog will continue, but the model will not see this image.`;
233
+ case 'filtered_model_unsupported':
234
+ return `This round did not send this user attachment image to ${providerModel}: the current model does not support image input. The dialog will continue, but the model will not see this image.`;
235
+ case 'filtered_mime_unsupported':
236
+ return `This round did not send this user attachment image to ${providerModel}: the current ${pathLabel} does not accept this image format${mimeText ? ` (${mimeText})` : ''}. The dialog will continue, but the model will not see this image.`;
237
+ case 'filtered_size_limit':
238
+ return `This round did not send this user attachment image to ${providerModel}: the image exceeds the current ${pathLabel} limit. The dialog will continue, but the model will not see this image.`;
239
+ case 'filtered_read_failed':
240
+ return `This round did not send this user attachment image to ${providerModel}: failed to read the image artifact (${relPath}). The dialog will continue, but the model will not see this image.`;
241
+ case 'filtered_missing':
242
+ return `This round did not send this user attachment image to ${providerModel}: the image artifact is missing (${relPath}). The dialog will continue, but the model will not see this image.`;
243
+ default: {
244
+ const _exhaustive = args.disposition;
245
+ return _exhaustive;
246
+ }
247
+ }
248
+ })();
249
+ return {
250
+ ...(args.msgId !== undefined ? { msgId: args.msgId } : {}),
251
+ artifact: args.artifact,
252
+ provider: providerKey,
253
+ model: modelKey,
254
+ disposition: args.disposition,
255
+ message,
256
+ ...(args.detail !== undefined ? { detail: args.detail } : {}),
257
+ };
258
+ }
package/dist/llm/gen.d.ts CHANGED
@@ -29,6 +29,9 @@ export type LlmBatchOutput = {
29
29
  } | {
30
30
  kind: 'tool_result_image_ingest';
31
31
  ingest: ToolResultImageIngest;
32
+ } | {
33
+ kind: 'user_image_ingest';
34
+ ingest: UserImageIngest;
32
35
  };
33
36
  export interface LlmBatchResult {
34
37
  messages: ChatMessage[];
@@ -63,6 +66,15 @@ export type ToolResultImageIngest = {
63
66
  message: string;
64
67
  detail?: string;
65
68
  };
69
+ export type UserImageIngest = {
70
+ msgId?: string;
71
+ artifact: ToolResultImageArtifact;
72
+ provider: string;
73
+ model: string;
74
+ disposition: ToolResultImageDisposition;
75
+ message: string;
76
+ detail?: string;
77
+ };
66
78
  export type CodexLlmWebSearchAction = {
67
79
  type: 'search';
68
80
  query?: string;
@@ -136,6 +148,7 @@ export interface LlmStreamReceiver {
136
148
  webSearchCall?: (call: LlmWebSearchCall) => Promise<void>;
137
149
  nativeToolCall?: (call: OpenAiResponsesNativeToolCall) => Promise<void>;
138
150
  toolResultImageIngest?: (ingest: ToolResultImageIngest) => Promise<void>;
151
+ userImageIngest?: (ingest: UserImageIngest) => Promise<void>;
139
152
  streamError?: (detail: string) => Promise<void>;
140
153
  }
141
154
  export interface LlmGenerator {