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
@@ -9,6 +9,7 @@ export declare function formatNewCourseStartPrompt(language: LanguageCode, args:
9
9
  source: 'clear_mind' | 'critical_auto_clear';
10
10
  }): string;
11
11
  export declare function formatDiligenceAutoContinuePrompt(language: LanguageCode, diligenceText: string): string;
12
+ export declare function formatReminderContextGuide(language: LanguageCode): string;
12
13
  export declare function formatReminderItemGuide(language: LanguageCode, reminderId: string, content: string, options?: {
13
14
  meta?: unknown;
14
15
  scope?: 'dialog' | 'personal' | 'agent_shared';
@@ -7,6 +7,7 @@ exports.formatRegisteredTellaskCallerUpdateNotice = formatRegisteredTellaskCalle
7
7
  exports.formatRegisteredTellaskCalleeUpdateNotice = formatRegisteredTellaskCalleeUpdateNotice;
8
8
  exports.formatNewCourseStartPrompt = formatNewCourseStartPrompt;
9
9
  exports.formatDiligenceAutoContinuePrompt = formatDiligenceAutoContinuePrompt;
10
+ exports.formatReminderContextGuide = formatReminderContextGuide;
10
11
  exports.formatReminderItemGuide = formatReminderItemGuide;
11
12
  exports.formatQ4HDiligencePushBudgetExhausted = formatQ4HDiligencePushBudgetExhausted;
12
13
  exports.formatDomindsNoteTellaskForTeammatesOnly = formatDomindsNoteTellaskForTeammatesOnly;
@@ -149,6 +150,25 @@ function formatDiligenceAutoContinuePrompt(language, diligenceText) {
149
150
  trimmed,
150
151
  ].join('\n');
151
152
  }
153
+ function formatReminderContextGuide(language) {
154
+ if (language === 'zh') {
155
+ return [
156
+ formatSystemNoticePrefix(language),
157
+ '以下是当前可见提醒项的运行时上下文投影。它们会按各自语义进入模型上下文(例如 self-reminder 保持 assistant-side 工作集语义),但不是我刚刚生成给用户的聊天正文。',
158
+ '在 WebUI 中,用户通过独立的 Reminder 小组件/面板项看到这些提醒,并能把它们和聊天正文区分开。',
159
+ '提醒项只作为工作集/状态参考;只有实际改变当前判断、计划或风险的信息,才需要提炼进后续有实质内容的对外回复。',
160
+ ].join('\n');
161
+ }
162
+ return [
163
+ formatSystemNoticePrefix(language),
164
+ 'The following visible reminders are runtime-added context projections. They enter model context according to their own semantics (for example, self-reminders keep assistant-side workset semantics), but they are not chat text I just generated for the user.',
165
+ 'In the WebUI, the user sees these reminders through a separate Reminder widget/panel item and can distinguish them from the chat transcript.',
166
+ 'Use reminders as workset/state references; only carry information into a later substantive outward reply when it materially changes current judgment, plan, or risk.',
167
+ ].join('\n');
168
+ }
169
+ function formatReminderItemProjectionNote(language) {
170
+ return language === 'zh' ? 'Reminder 上下文投影条目:' : 'Reminder context projection item:';
171
+ }
152
172
  function formatReminderItemGuide(language, reminderId, content, options) {
153
173
  function isRecord(value) {
154
174
  return typeof value === 'object' && value !== null && !Array.isArray(value);
@@ -190,13 +210,15 @@ function formatReminderItemGuide(language, reminderId, content, options) {
190
210
  : isPendingTellaskReminder && pendingTellaskCount === 0
191
211
  ? `If I have confirmed this is only noise cleanup and not an action step, I may run: delete_reminder({ "reminder_id": "${reminderId}" })`
192
212
  : `If I need to delete this reminder, run: delete_reminder({ "reminder_id": "${reminderId}" })`;
213
+ const projectionNote = formatReminderItemProjectionNote(language);
214
+ const enProjectionPrefix = `${projectionNote} `;
193
215
  if (language === 'zh') {
194
216
  if (managementTool) {
195
217
  const updateInstructionSafe = updateInstruction ?? `${managementTool}({ ... })`;
196
218
  return [
197
219
  `提醒项 [${reminderId}](工具状态)`,
198
220
  '',
199
- '我把这条当作工具维护的状态参考。默认不在对外回复里专门确认、复述或总结它;只有它实际改变当前判断、计划或风险时,我才提炼真正相关的部分。',
221
+ `${projectionNote}我把这条当作工具维护的状态参考。默认不在对外回复里专门确认、复述或总结它;只有它实际改变当前判断、计划或风险时,我才提炼真正相关的部分。`,
200
222
  '',
201
223
  `这条提醒项由工具 ${managementTool} 管理;如果我要调整它,就用 ${managementTool}(不要用 update_reminder)。`,
202
224
  '',
@@ -211,7 +233,7 @@ function formatReminderItemGuide(language, reminderId, content, options) {
211
233
  return [
212
234
  `提醒项 [${reminderId}]`,
213
235
  '',
214
- '这是带有 meta 控制更新规则的提醒项。我仍把它当作状态参考,但不要用 update_reminder 直接改写内容。',
236
+ `${projectionNote}这是带有 meta 控制更新规则的提醒项。我仍把它当作状态参考,但不要用 update_reminder 直接改写内容。`,
215
237
  '',
216
238
  `如果我要更新这条提醒项,不能用 update_reminder;请按此处理:${updateInstruction}`,
217
239
  deleteInstruction,
@@ -224,7 +246,7 @@ function formatReminderItemGuide(language, reminderId, content, options) {
224
246
  return [
225
247
  `提醒项 [${reminderId}](换程接续信息)`,
226
248
  '',
227
- '我把这条当作换程后快速恢复工作的接续包,不把它自动当成当前必须立刻执行的指令。',
249
+ `${projectionNote}我把这条当作换程后快速恢复工作的接续包,不把它自动当成当前必须立刻执行的指令。`,
228
250
  '',
229
251
  '我应优先保留下一步行动、关键定位、运行/验证信息、容易丢的临时细节;不要重复差遣牒已覆盖的内容。进入新一程后,我的第一步就是以清醒头脑重新审视并整理更新:删除冗余、纠正偏激/失真思路、压缩成高质量提醒项。若目前只是粗略过桥笔记,进入新一程后我必须尽快收敛。',
230
252
  '',
@@ -239,8 +261,8 @@ function formatReminderItemGuide(language, reminderId, content, options) {
239
261
  `提醒项 [${reminderId}]${scope === 'personal' ? '(个人范围)' : ''}`,
240
262
  '',
241
263
  scope === 'personal'
242
- ? '这是我给自己的个人范围显眼提示;在所有由我主理的后续对话里都会看到它。我不把它自动当成系统下发的下一步动作。'
243
- : '这是我给自己的显眼提示,用于保留当前对话里容易丢的工作信息;我不把它自动当成系统下发的下一步动作。',
264
+ ? `${projectionNote}这是我给自己的个人范围显眼提示;在所有由我主理的后续对话里都会看到它。我不把它自动当成系统下发的下一步动作。`
265
+ : `${projectionNote}这是我给自己的显眼提示,用于保留当前对话里容易丢的工作信息;我不把它自动当成系统下发的下一步动作。`,
244
266
  '',
245
267
  scope === 'personal'
246
268
  ? '我应保持简洁、及时更新;不再需要时就删除。若它只对当前对话有效,应改写成 dialog 范围提醒而不是长期堆在个人范围里。'
@@ -257,7 +279,7 @@ function formatReminderItemGuide(language, reminderId, content, options) {
257
279
  const updateInstructionSafe = updateInstruction ?? `${managementTool}({ ... })`;
258
280
  return `REMINDER [${reminderId}] (TOOL STATE)
259
281
 
260
- I treat this as a tool-maintained state reference. By default I should not explicitly acknowledge, restate, or summarize it in my outward reply; I should only extract the parts that materially change my current judgment, plan, or risk.
282
+ ${enProjectionPrefix}I treat this as a tool-maintained state reference. By default I should not explicitly acknowledge, restate, or summarize it in my outward reply; I should only extract the parts that materially change my current judgment, plan, or risk.
261
283
 
262
284
  This reminder is managed by tool ${managementTool}; if I need to change it, I should use ${managementTool} instead of update_reminder.
263
285
 
@@ -269,7 +291,7 @@ ${content}`;
269
291
  if (updateInstruction) {
270
292
  return `REMINDER [${reminderId}]
271
293
 
272
- This reminder has a meta-controlled update path. I should still treat it as state/reference, and I must not rewrite it directly with update_reminder.
294
+ ${enProjectionPrefix}This reminder has a meta-controlled update path. I should still treat it as state/reference, and I must not rewrite it directly with update_reminder.
273
295
 
274
296
  If I need to update this reminder, I must not use update_reminder; follow instead: ${updateInstruction}
275
297
  ${deleteInstruction}
@@ -279,7 +301,7 @@ ${content}`;
279
301
  if (isContinuationPackageReminder) {
280
302
  return `REMINDER [${reminderId}] (CONTINUATION PACKAGE)
281
303
 
282
- I treat this as resume information for the next course, not as an automatic must-do command.
304
+ ${enProjectionPrefix}I treat this as resume information for the next course, not as an automatic must-do command.
283
305
 
284
306
  I should keep the next step, key pointers, run/verify info, and easy-to-lose volatile details here. I should not duplicate Taskdoc content. In the new course, my first step is to review and rewrite this with a clear head: remove redundancy, correct biased or distorted bridge notes, and compress it into a high-quality reminder. If this is only a rough bridge note, I should reconcile it early in the new course.
285
307
 
@@ -291,8 +313,8 @@ ${content}`;
291
313
  return `REMINDER [${reminderId}]${scope === 'personal' ? ' (PERSONAL SCOPE)' : ''}
292
314
 
293
315
  ${scope === 'personal'
294
- ? 'This is my conspicuous personal-scope reminder. I will keep seeing it in all later dialogs I lead, and I do not treat it as an automatically assigned next action.'
295
- : 'This is my conspicuous self-reminder for easy-to-lose work details in the current dialog. I do not treat it as an automatically assigned next action.'}
316
+ ? `${enProjectionPrefix}This is my conspicuous personal-scope reminder. I will keep seeing it in all later dialogs I lead, and I do not treat it as an automatically assigned next action.`
317
+ : `${enProjectionPrefix}This is my conspicuous self-reminder for easy-to-lose work details in the current dialog. I do not treat it as an automatically assigned next action.`}
296
318
 
297
319
  ${scope === 'personal'
298
320
  ? 'I should keep it concise, refresh it when needed, and delete it when obsolete. If it is only useful for the current dialog, I should rewrite it into dialog scope instead of letting personal scope accumulate noise.'
@@ -68,8 +68,8 @@ function buildSubdialogRoleHeaderCopy(args) {
68
68
  const requester = `@${args.requesterId}`;
69
69
  if (args.expectedReplyTool === undefined) {
70
70
  return args.language === 'zh'
71
- ? `${requester} 已通过诉请安排你处理下述诉请内容。只有需要回问时,才调用 \`tellaskBack\`。`
72
- : `${requester} has assigned you to handle the request content below. Call \`tellaskBack\` only when you truly need to ask back.`;
71
+ ? `${requester} 已通过诉请安排你处理下述诉请内容。只有确实需要向上游回问、且现有规程无法直接判责时,才调用 \`tellaskBack\`。`
72
+ : `${requester} has assigned you to handle the request content below. Call \`tellaskBack\` only when you truly need to ask upstream back and existing SOP cannot directly identify another owner.`;
73
73
  }
74
74
  const kindLabel = (0, tellask_labels_1.getTellaskKindLabel)({
75
75
  language: args.language,
@@ -77,8 +77,8 @@ function buildSubdialogRoleHeaderCopy(args) {
77
77
  bracketed: true,
78
78
  });
79
79
  return args.language === 'zh'
80
- ? `${requester} 已通过${kindLabel}安排你处理下述诉请内容。等你准备好回复内容后,调用 \`${args.expectedReplyTool}\` 完成回复。只有需要回问时,才调用 \`tellaskBack\`。`
81
- : `${requester} has assigned you, via this ${kindLabel}, to handle the request content below. Once your reply content is ready, call \`${args.expectedReplyTool}\` to deliver it. Call \`tellaskBack\` only when you truly need to ask back.`;
80
+ ? `${requester} 已通过${kindLabel}安排你处理下述诉请内容。等你准备好回复内容后,调用 \`${args.expectedReplyTool}\` 完成回复。只有确实需要向上游回问、且现有规程无法直接判责时,才调用 \`tellaskBack\`。`
81
+ : `${requester} has assigned you, via this ${kindLabel}, to handle the request content below. Once your reply content is ready, call \`${args.expectedReplyTool}\` to deliver it. Call \`tellaskBack\` only when you truly need to ask upstream back and existing SOP cannot directly identify another owner.`;
82
82
  }
83
83
  function buildReplyObligationSuppressionGuideText(language) {
84
84
  if (language === 'zh') {
@@ -2109,6 +2109,13 @@ async function handleGetTeamConfig(res) {
2109
2109
  async function handleGetDialogs(res, status) {
2110
2110
  try {
2111
2111
  const rootDialogs = [];
2112
+ const allDialogIds = await persistence_1.DialogPersistence.listAllDialogIds(status);
2113
+ const subdialogCountByRootId = new Map();
2114
+ for (const dialogId of allDialogIds) {
2115
+ if (dialogId.selfId === dialogId.rootId)
2116
+ continue;
2117
+ subdialogCountByRootId.set(dialogId.rootId, (subdialogCountByRootId.get(dialogId.rootId) ?? 0) + 1);
2118
+ }
2112
2119
  const ids = await persistence_1.DialogPersistence.listDialogs(status);
2113
2120
  for (const id of ids) {
2114
2121
  const dialogId = new dialog_1.DialogID(id);
@@ -2124,9 +2131,7 @@ async function handleGetDialogs(res, status) {
2124
2131
  continue;
2125
2132
  }
2126
2133
  const waitingForFreshBootsReasoning = await detectWaitingForFreshBootsReasoning(dialogId, status);
2127
- // Count subdialogs for this root dialog
2128
- const subPath = path.join(rootPath, 'subdialogs');
2129
- const subdialogCount = await countSubdialogs(subPath);
2134
+ const subdialogCount = subdialogCountByRootId.get(meta.id) ?? 0;
2130
2135
  if (!(await pathStillExistsForLookup(rootPath))) {
2131
2136
  continue;
2132
2137
  }
@@ -2170,45 +2175,6 @@ async function handleGetRunControlCounts(res) {
2170
2175
  return true;
2171
2176
  }
2172
2177
  }
2173
- /**
2174
- * Count subdialog directories recursively
2175
- */
2176
- async function countSubdialogs(dirPath) {
2177
- try {
2178
- const entries = await promises_1.default.readdir(dirPath, { withFileTypes: true });
2179
- let count = 0;
2180
- for (const entry of entries) {
2181
- if (entry.isDirectory()) {
2182
- const fullPath = path.join(dirPath, entry.name);
2183
- const dialogYamlPath = path.join(fullPath, 'dialog.yaml');
2184
- try {
2185
- await promises_1.default.access(dialogYamlPath);
2186
- // This directory contains dialog.yaml - it's a subdialog.
2187
- count++;
2188
- }
2189
- catch (error) {
2190
- if (getErrorCode(error) === 'ENOENT') {
2191
- // Only a missing dialog.yaml means "keep recursing". Permission/I/O failures must stay
2192
- // loud so operators can distinguish inaccessible paths from ordinary non-dialog dirs.
2193
- count += await countSubdialogs(fullPath);
2194
- continue;
2195
- }
2196
- throw error;
2197
- }
2198
- }
2199
- }
2200
- return count;
2201
- }
2202
- catch (error) {
2203
- if (typeof error === 'object' &&
2204
- error !== null &&
2205
- 'code' in error &&
2206
- error.code === 'ENOENT') {
2207
- return 0;
2208
- }
2209
- throw error;
2210
- }
2211
- }
2212
2178
  /**
2213
2179
  * Get full hierarchy (root + subdialogs) for a single root dialog
2214
2180
  */
@@ -2292,7 +2258,7 @@ async function handleGetDialogHierarchy(res, rootId, status) {
2292
2258
  respondJson(res, 200, {
2293
2259
  success: true,
2294
2260
  hierarchy: {
2295
- root: rootInfo,
2261
+ root: { ...rootInfo, subdialogCount: subdialogs.length },
2296
2262
  subdialogs,
2297
2263
  },
2298
2264
  });
@@ -2331,6 +2297,7 @@ async function handleGetDialogListSubdialogNode(res, dialog) {
2331
2297
  }
2332
2298
  const latest = await loadDialogLatestForLookup(dialogId, status, 'handleGetDialogListSubdialogNode');
2333
2299
  const waitingForFreshBootsReasoning = await detectWaitingForFreshBootsReasoning(dialogId, status);
2300
+ const rootSubdialogCount = await persistence_1.DialogPersistence.countAllSubdialogsUnderRoot(new dialog_1.DialogID(dialog.rootId), status);
2334
2301
  const subdialogPath = persistence_1.DialogPersistence.getSubdialogPath(dialogId, status);
2335
2302
  if (!(await pathStillExistsForLookup(subdialogPath))) {
2336
2303
  respondJson(res, 404, { success: false, error: `Dialog not found in ${status}` });
@@ -2347,6 +2314,7 @@ async function handleGetDialogListSubdialogNode(res, dialog) {
2347
2314
  subdialogNode: {
2348
2315
  selfId: metadata.id,
2349
2316
  rootId: dialog.rootId,
2317
+ rootSubdialogCount,
2350
2318
  supdialogId: derivedSupdialogId,
2351
2319
  agentId: metadata.agentId,
2352
2320
  taskDocPath: metadata.taskDocPath,
@@ -1,4 +1,7 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
6
  exports.shouldQueueUserSupplementAtGenerationBoundary = shouldQueueUserSupplementAtGenerationBoundary;
4
7
  exports.handleWebSocketMessage = handleWebSocketMessage;
@@ -14,6 +17,9 @@ const evt_1 = require("@longrun-ai/kernel/evt");
14
17
  const language_1 = require("@longrun-ai/kernel/types/language");
15
18
  const storage_1 = require("@longrun-ai/kernel/types/storage");
16
19
  const time_1 = require("@longrun-ai/kernel/utils/time");
20
+ const crypto_1 = require("crypto");
21
+ const promises_1 = __importDefault(require("fs/promises"));
22
+ const path_1 = __importDefault(require("path"));
17
23
  const ws_1 = require("ws");
18
24
  const runtime_1 = require("../apps/runtime");
19
25
  const global_dialog_event_broadcaster_1 = require("../bootstrap/global-dialog-event-broadcaster");
@@ -54,6 +60,113 @@ function normalizeDiligencePushMax(value) {
54
60
  return 0;
55
61
  return Math.floor(value);
56
62
  }
63
+ const USER_IMAGE_ATTACHMENT_MAX_COUNT = 10;
64
+ const USER_IMAGE_ATTACHMENT_MAX_BYTES = 10 * 1024 * 1024;
65
+ function userImageMimeTypeToExt(mimeType) {
66
+ switch (mimeType) {
67
+ case 'image/png':
68
+ return 'png';
69
+ case 'image/jpeg':
70
+ return 'jpg';
71
+ case 'image/webp':
72
+ return 'webp';
73
+ case 'image/gif':
74
+ return 'gif';
75
+ default:
76
+ return null;
77
+ }
78
+ }
79
+ function sanitizeArtifactPathSegment(value) {
80
+ const cleaned = value.replace(/[^a-zA-Z0-9._-]/g, '_').replace(/_+/g, '_');
81
+ const trimmed = cleaned.replace(/^_+|_+$/g, '');
82
+ return trimmed.length > 0 ? trimmed.slice(0, 96) : 'item';
83
+ }
84
+ function parseUserImageAttachments(raw) {
85
+ if (raw === undefined)
86
+ return [];
87
+ if (!Array.isArray(raw)) {
88
+ throw new Error('attachments must be an array when provided');
89
+ }
90
+ if (raw.length > USER_IMAGE_ATTACHMENT_MAX_COUNT) {
91
+ throw new Error(`at most ${String(USER_IMAGE_ATTACHMENT_MAX_COUNT)} image attachments are allowed`);
92
+ }
93
+ return raw.map((item, index) => {
94
+ if (typeof item !== 'object' || item === null || Array.isArray(item)) {
95
+ throw new Error(`attachments[${String(index)}] must be an object`);
96
+ }
97
+ const record = item;
98
+ const kind = record['kind'];
99
+ const mimeType = record['mimeType'];
100
+ const byteLength = record['byteLength'];
101
+ const dataBase64 = record['dataBase64'];
102
+ if (kind !== 'image') {
103
+ throw new Error(`attachments[${String(index)}].kind must be image`);
104
+ }
105
+ if (typeof mimeType !== 'string' || userImageMimeTypeToExt(mimeType) === null) {
106
+ throw new Error(`attachments[${String(index)}].mimeType is unsupported`);
107
+ }
108
+ if (typeof byteLength !== 'number' ||
109
+ !Number.isFinite(byteLength) ||
110
+ byteLength <= 0 ||
111
+ byteLength > USER_IMAGE_ATTACHMENT_MAX_BYTES) {
112
+ throw new Error(`attachments[${String(index)}].byteLength must be between 1 and ${String(USER_IMAGE_ATTACHMENT_MAX_BYTES)}`);
113
+ }
114
+ if (typeof dataBase64 !== 'string' || dataBase64.trim() === '') {
115
+ throw new Error(`attachments[${String(index)}].dataBase64 is required`);
116
+ }
117
+ return { kind, mimeType, byteLength, dataBase64 };
118
+ });
119
+ }
120
+ function isStrictBase64Payload(value) {
121
+ if (value.length === 0 || value.length % 4 !== 0)
122
+ return false;
123
+ return /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(value);
124
+ }
125
+ function prepareUserImageAttachments(attachments) {
126
+ return attachments.map((attachment, index) => {
127
+ if (!isStrictBase64Payload(attachment.dataBase64)) {
128
+ throw new Error(`attachments[${String(index)}].dataBase64 must be strict base64`);
129
+ }
130
+ const bytes = Buffer.from(attachment.dataBase64, 'base64');
131
+ if (bytes.length !== attachment.byteLength) {
132
+ throw new Error(`attachments[${String(index)}].byteLength mismatch: declared=${String(attachment.byteLength)} decoded=${String(bytes.length)}`);
133
+ }
134
+ return {
135
+ mimeType: attachment.mimeType,
136
+ bytes,
137
+ };
138
+ });
139
+ }
140
+ async function persistPreparedUserImageAttachments(args) {
141
+ if (args.attachments.length === 0)
142
+ return undefined;
143
+ const eventsBase = persistence_1.DialogPersistence.getDialogEventsPath(args.dialog.id, args.dialog.status);
144
+ const safeMsgId = sanitizeArtifactPathSegment(args.msgId);
145
+ const contentItems = [];
146
+ for (let index = 0; index < args.attachments.length; index += 1) {
147
+ const attachment = args.attachments[index];
148
+ const ext = userImageMimeTypeToExt(attachment.mimeType);
149
+ if (ext === null) {
150
+ throw new Error(`attachments[${String(index)}].mimeType is unsupported`);
151
+ }
152
+ const relPath = path_1.default.posix.join('artifacts', 'user-input', safeMsgId, `${String(index + 1).padStart(2, '0')}-${(0, crypto_1.randomUUID)()}.${ext}`);
153
+ const absPath = path_1.default.join(eventsBase, ...relPath.split('/'));
154
+ await promises_1.default.mkdir(path_1.default.dirname(absPath), { recursive: true });
155
+ await promises_1.default.writeFile(absPath, attachment.bytes);
156
+ contentItems.push({
157
+ type: 'input_image',
158
+ mimeType: attachment.mimeType,
159
+ byteLength: attachment.bytes.length,
160
+ artifact: {
161
+ rootId: args.dialog.id.rootId,
162
+ selfId: args.dialog.id.selfId,
163
+ status: args.dialog.status,
164
+ relPath,
165
+ },
166
+ });
167
+ }
168
+ return contentItems;
169
+ }
57
170
  function parsePersistableDialogStatus(raw) {
58
171
  if (raw !== 'running' && raw !== 'completed' && raw !== 'archived') {
59
172
  return null;
@@ -230,6 +343,7 @@ async function queueUserSupplementAtGenerationBoundary(dialog, prompt) {
230
343
  }
231
344
  const queued = dialog.queueUserPromptAtGenerationBoundary({
232
345
  prompt: prompt.content,
346
+ contentItems: prompt.contentItems,
233
347
  msgId: prompt.msgId,
234
348
  grammar: prompt.grammar,
235
349
  userLanguageCode: prompt.userLanguageCode,
@@ -239,6 +353,7 @@ async function queueUserSupplementAtGenerationBoundary(dialog, prompt) {
239
353
  course: dialog.currentCourse,
240
354
  msgId: queued.msgId,
241
355
  content: queued.prompt,
356
+ contentItems: queued.contentItems,
242
357
  grammar: queued.grammar ?? 'markdown',
243
358
  origin: 'user',
244
359
  userLanguageCode: queued.userLanguageCode,
@@ -1135,6 +1250,7 @@ async function handleDisplayCourse(ws, packet) {
1135
1250
  async function handleUserMsg2Dlg(ws, packet) {
1136
1251
  try {
1137
1252
  const { dialog: dialogIdent, content, msgId } = packet;
1253
+ const attachments = parseUserImageAttachments(packet.attachments);
1138
1254
  const userLanguageCode = resolveUserLanguageCode(ws, packet.userLanguageCode);
1139
1255
  // Basic validation
1140
1256
  if (!dialogIdent || !content || !msgId) {
@@ -1170,6 +1286,7 @@ async function handleUserMsg2Dlg(ws, packet) {
1170
1286
  grammar: 'markdown',
1171
1287
  userLanguageCode,
1172
1288
  };
1289
+ const preparedAttachments = prepareUserImageAttachments(attachments);
1173
1290
  // If the dialog is already active for this WebSocket, runnable (status === 'running'),
1174
1291
  // and has an event forwarder (subChan),
1175
1292
  // drive it directly to preserve in-memory state (pending subdialogs, teammate tellask tracking, etc).
@@ -1187,12 +1304,21 @@ async function handleUserMsg2Dlg(ws, packet) {
1187
1304
  existingDialog.status === 'running' &&
1188
1305
  existingSub &&
1189
1306
  existingSub.dialogKey === existingDialog.id.valueOf()) {
1190
- const queuedAtBoundary = await queueUserSupplementAtGenerationBoundary(existingDialog, effectivePrompt);
1307
+ const contentItems = await persistPreparedUserImageAttachments({
1308
+ dialog: existingDialog,
1309
+ msgId: effectivePrompt.msgId,
1310
+ attachments: preparedAttachments,
1311
+ });
1312
+ const queuedAtBoundary = await queueUserSupplementAtGenerationBoundary(existingDialog, {
1313
+ ...effectivePrompt,
1314
+ contentItems,
1315
+ });
1191
1316
  if (queuedAtBoundary) {
1192
1317
  return;
1193
1318
  }
1194
1319
  await (0, kernel_driver_1.driveDialogStream)(existingDialog, {
1195
1320
  content: effectivePrompt.content,
1321
+ ...(contentItems === undefined ? {} : { contentItems }),
1196
1322
  msgId: effectivePrompt.msgId,
1197
1323
  grammar: effectivePrompt.grammar,
1198
1324
  userLanguageCode: effectivePrompt.userLanguageCode,
@@ -1220,12 +1346,21 @@ async function handleUserMsg2Dlg(ws, packet) {
1220
1346
  return;
1221
1347
  }
1222
1348
  await setupWebSocketSubscription(ws, dialog);
1223
- const queuedAtBoundary = await queueUserSupplementAtGenerationBoundary(dialog, effectivePrompt);
1349
+ const contentItems = await persistPreparedUserImageAttachments({
1350
+ dialog,
1351
+ msgId: effectivePrompt.msgId,
1352
+ attachments: preparedAttachments,
1353
+ });
1354
+ const queuedAtBoundary = await queueUserSupplementAtGenerationBoundary(dialog, {
1355
+ ...effectivePrompt,
1356
+ contentItems,
1357
+ });
1224
1358
  if (queuedAtBoundary) {
1225
1359
  return;
1226
1360
  }
1227
1361
  await (0, kernel_driver_1.driveDialogStream)(dialog, {
1228
1362
  content: effectivePrompt.content,
1363
+ ...(contentItems === undefined ? {} : { contentItems }),
1229
1364
  msgId: effectivePrompt.msgId,
1230
1365
  grammar: effectivePrompt.grammar,
1231
1366
  userLanguageCode: effectivePrompt.userLanguageCode,
@@ -1401,6 +1536,7 @@ async function handleResumeAll(ws, packet) {
1401
1536
  async function handleReceiveHumanReply(ws, packet) {
1402
1537
  try {
1403
1538
  const { dialog: dialogIdent, content, msgId, questionId, continuationType } = packet;
1539
+ const attachments = parseUserImageAttachments(packet.attachments);
1404
1540
  const userLanguageCode = resolveUserLanguageCode(ws, packet.userLanguageCode);
1405
1541
  // Basic validation
1406
1542
  if (!dialogIdent || !content || !msgId || !questionId) {
@@ -1436,14 +1572,7 @@ async function handleReceiveHumanReply(ws, packet) {
1436
1572
  grammar: 'markdown',
1437
1573
  userLanguageCode,
1438
1574
  };
1439
- const removed = await persistence_1.DialogPersistence.removeQuestion4HumanState(dialogIdObj, questionId);
1440
- if (!removed.found) {
1441
- ws.send(JSON.stringify({
1442
- type: 'error',
1443
- message: `Question ${questionId} not found in dialog ${dialogId}`,
1444
- }));
1445
- return;
1446
- }
1575
+ const preparedAttachments = prepareUserImageAttachments(attachments);
1447
1576
  // Restore the canonical dialog instances (root + subdialogs) to avoid duplicates.
1448
1577
  const rootDialog = await (0, dialog_instance_registry_1.getOrRestoreRootDialog)(dialogIdObj.rootId, 'running');
1449
1578
  if (!rootDialog) {
@@ -1460,6 +1589,14 @@ async function handleReceiveHumanReply(ws, packet) {
1460
1589
  }
1461
1590
  // Ensure the requesting WebSocket receives q4h_answered and subsequent resume stream events.
1462
1591
  await setupWebSocketSubscription(ws, dialog);
1592
+ const removed = await persistence_1.DialogPersistence.removeQuestion4HumanState(dialogIdObj, questionId);
1593
+ if (!removed.found) {
1594
+ ws.send(JSON.stringify({
1595
+ type: 'error',
1596
+ message: `Question ${questionId} not found in dialog ${dialogId}`,
1597
+ }));
1598
+ return;
1599
+ }
1463
1600
  const removedQuestion = removed.removedQuestion;
1464
1601
  if (!removedQuestion) {
1465
1602
  throw new Error(`Q4H remove invariant violation: found=true but removedQuestion missing (rootId=${dialog.id.rootId} selfId=${dialog.id.selfId} questionId=${questionId})`);
@@ -1469,6 +1606,11 @@ async function handleReceiveHumanReply(ws, packet) {
1469
1606
  throw new Error(`Q4H remove invariant violation: missing callId on answered question ` +
1470
1607
  `(rootId=${dialog.id.rootId} selfId=${dialog.id.selfId} questionId=${questionId})`);
1471
1608
  }
1609
+ const contentItems = await persistPreparedUserImageAttachments({
1610
+ dialog,
1611
+ msgId: effectivePrompt.msgId,
1612
+ attachments: preparedAttachments,
1613
+ });
1472
1614
  const askHumanOriginCourse = removedQuestion.callSiteRef.course;
1473
1615
  const askHumanCarryoverContent = (0, inter_dialog_format_1.formatTellaskCarryoverResultContent)({
1474
1616
  originCourse: askHumanOriginCourse,
@@ -1487,6 +1629,7 @@ async function handleReceiveHumanReply(ws, packet) {
1487
1629
  originCourse: (0, storage_1.toCallingCourseNumber)(askHumanOriginCourse),
1488
1630
  calling_genseq: removedQuestion.callSiteRef.callingGenseq,
1489
1631
  carryoverContent: askHumanCarryoverContent,
1632
+ contentItems,
1490
1633
  });
1491
1634
  await dialog.addChatMessages(askHumanResultMirror);
1492
1635
  // Emit q4h_answered event for answered question
@@ -1505,6 +1648,7 @@ async function handleReceiveHumanReply(ws, packet) {
1505
1648
  prompt: effectivePrompt.content,
1506
1649
  msgId: effectivePrompt.msgId,
1507
1650
  grammar: effectivePrompt.grammar,
1651
+ contentItems,
1508
1652
  userLanguageCode: effectivePrompt.userLanguageCode,
1509
1653
  q4hAnswerCallId: askHumanCallId,
1510
1654
  });
@@ -1520,6 +1664,7 @@ async function handleReceiveHumanReply(ws, packet) {
1520
1664
  // The continuation input carries correlation only; it does not persist another user prompt fact.
1521
1665
  await (0, kernel_driver_1.driveDialogStream)(dialog, {
1522
1666
  content: effectivePrompt.content,
1667
+ ...(contentItems === undefined ? {} : { contentItems }),
1523
1668
  msgId: effectivePrompt.msgId,
1524
1669
  grammar: effectivePrompt.grammar,
1525
1670
  userLanguageCode: effectivePrompt.userLanguageCode,
@@ -16,6 +16,7 @@ const mcp_1 = require("./mcp");
16
16
  const mem_1 = require("./mem");
17
17
  const os_1 = require("./os");
18
18
  const pending_tellask_reminder_1 = require("./pending-tellask-reminder");
19
+ const picture_1 = require("./picture");
19
20
  const registry_1 = require("./registry");
20
21
  const ripgrep_1 = require("./ripgrep");
21
22
  const team_mgmt_1 = require("./team_mgmt");
@@ -50,6 +51,8 @@ function manualSpecFor(toolsetId) {
50
51
  (0, registry_1.registerTool)(fs_1.moveFileTool);
51
52
  (0, registry_1.registerTool)(fs_1.moveDirTool);
52
53
  (0, registry_1.registerTool)(txt_1.readFileTool);
54
+ (0, registry_1.registerTool)(picture_1.readPictureTool);
55
+ (0, registry_1.registerTool)(picture_1.writePictureTool);
53
56
  (0, registry_1.registerTool)(txt_1.createNewFileTool);
54
57
  (0, registry_1.registerTool)(txt_1.overwriteEntireFileTool);
55
58
  (0, registry_1.registerTool)(txt_1.prepareFileRangeEditTool);
@@ -182,6 +185,7 @@ for (const tool of team_mgmt_1.teamMgmtTools) {
182
185
  (0, registry_1.registerToolset)('ws_read', [
183
186
  fs_1.listDirTool,
184
187
  txt_1.readFileTool,
188
+ picture_1.readPictureTool,
185
189
  ripgrep_1.ripgrepFilesTool,
186
190
  ripgrep_1.ripgrepSnippetsTool,
187
191
  ripgrep_1.ripgrepCountTool,
@@ -191,8 +195,8 @@ for (const tool of team_mgmt_1.teamMgmtTools) {
191
195
  (0, registry_1.setToolsetMeta)('ws_read', {
192
196
  source: 'dominds',
193
197
  descriptionI18n: {
194
- en: 'rtws read-only access: list directories, read files, and search code/content to gather facts safely.',
195
- zh: '运行时工作区只读访问:列目录、读文件、检索代码与文本,用于安全获取事实。',
198
+ en: 'rtws read-only access: list directories, read text/images, and search code/content to gather facts safely.',
199
+ zh: '运行时工作区只读访问:列目录、读文本/图片、检索代码与文本,用于安全获取事实。',
196
200
  },
197
201
  promptFilesI18n: promptFilesFor('ws_read'),
198
202
  manualSpec: manualSpecFor('ws_read'),
@@ -205,6 +209,8 @@ for (const tool of team_mgmt_1.teamMgmtTools) {
205
209
  fs_1.moveFileTool,
206
210
  fs_1.moveDirTool,
207
211
  txt_1.readFileTool,
212
+ picture_1.readPictureTool,
213
+ picture_1.writePictureTool,
208
214
  txt_1.createNewFileTool,
209
215
  txt_1.overwriteEntireFileTool,
210
216
  txt_1.prepareFileAppendTool,
@@ -222,8 +228,8 @@ for (const tool of team_mgmt_1.teamMgmtTools) {
222
228
  (0, registry_1.setToolsetMeta)('ws_mod', {
223
229
  source: 'dominds',
224
230
  descriptionI18n: {
225
- en: 'rtws read/write access: inspect, create, move, delete, and precisely edit workspace files.',
226
- zh: '运行时工作区读写访问:检查、创建、移动、删除,并精确编辑工作区文件。',
231
+ en: 'rtws read/write access: inspect, create, move, delete, precisely edit text files, and read/write workspace images.',
232
+ zh: '运行时工作区读写访问:检查、创建、移动、删除、精确编辑文本文件,并读写工作区图片。',
227
233
  },
228
234
  promptFilesI18n: promptFilesFor('ws_mod'),
229
235
  manualSpec: manualSpecFor('ws_mod'),