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
@@ -43,6 +43,20 @@ function sendIpc(msg) {
43
43
  }
44
44
  process.send(msg);
45
45
  }
46
+ async function flushIpc(msg) {
47
+ if (typeof process.send !== 'function') {
48
+ throw new Error('cmd_runner must be launched with an IPC channel');
49
+ }
50
+ await new Promise((resolve, reject) => {
51
+ process.send?.(msg, (error) => {
52
+ if (error) {
53
+ reject(error);
54
+ return;
55
+ }
56
+ resolve();
57
+ });
58
+ });
59
+ }
46
60
  async function readProcessCommandLine(pid) {
47
61
  try {
48
62
  if (process.platform === 'win32') {
@@ -140,13 +154,90 @@ async function main() {
140
154
  stdout: new ScrollingBuffer(init.scrollbackLines),
141
155
  stderr: new ScrollingBuffer(init.scrollbackLines),
142
156
  };
157
+ let server;
158
+ let closeRequested = false;
159
+ let timeoutHandle;
160
+ const closeServerAndExit = (code) => {
161
+ if (closeRequested) {
162
+ return;
163
+ }
164
+ closeRequested = true;
165
+ const exit = () => {
166
+ setImmediate(() => {
167
+ process.exit(code);
168
+ });
169
+ };
170
+ const cleanupEndpoint = () => {
171
+ if (process.platform !== 'win32') {
172
+ void promises_1.default.unlink(endpoint).catch(() => {
173
+ // Best effort only.
174
+ });
175
+ }
176
+ };
177
+ if (!server) {
178
+ cleanupEndpoint();
179
+ exit();
180
+ return;
181
+ }
182
+ try {
183
+ server.close(() => {
184
+ cleanupEndpoint();
185
+ exit();
186
+ });
187
+ }
188
+ catch (error) {
189
+ const codeValue = typeof error === 'object' && error !== null
190
+ ? error.code
191
+ : undefined;
192
+ if (codeValue !== 'ERR_SERVER_NOT_RUNNING') {
193
+ throw error;
194
+ }
195
+ cleanupEndpoint();
196
+ exit();
197
+ }
198
+ };
199
+ childProcess.once('close', (code, signal) => {
200
+ if (timeoutHandle) {
201
+ clearTimeout(timeoutHandle);
202
+ }
203
+ state.isRunning = false;
204
+ state.exitCode = code;
205
+ state.exitSignal = signal;
206
+ void (async () => {
207
+ if (state.daemonCommandLine === null) {
208
+ await flushIpc({
209
+ type: 'completed',
210
+ exitCode: code,
211
+ exitSignal: signal,
212
+ stdout: state.stdout.snapshot(),
213
+ stderr: state.stderr.snapshot(),
214
+ });
215
+ }
216
+ closeServerAndExit(0);
217
+ })().catch(() => {
218
+ closeServerAndExit(0);
219
+ });
220
+ });
221
+ childProcess.once('error', (error) => {
222
+ if (timeoutHandle) {
223
+ clearTimeout(timeoutHandle);
224
+ }
225
+ void flushIpc({
226
+ type: 'failed',
227
+ errorText: error.message,
228
+ })
229
+ .catch(() => undefined)
230
+ .finally(() => {
231
+ closeServerAndExit(1);
232
+ });
233
+ });
143
234
  childProcess.stdout?.on('data', (data) => {
144
235
  state.stdout.addText(data.toString());
145
236
  });
146
237
  childProcess.stderr?.on('data', (data) => {
147
238
  state.stderr.addText(data.toString());
148
239
  });
149
- const server = node_net_1.default.createServer((socket) => {
240
+ server = node_net_1.default.createServer((socket) => {
150
241
  socket.setEncoding('utf8');
151
242
  let buffer = '';
152
243
  socket.on('data', (chunk) => {
@@ -211,9 +302,12 @@ async function main() {
211
302
  resolve();
212
303
  });
213
304
  });
214
- const timeoutHandle = setTimeout(() => {
305
+ timeoutHandle = setTimeout(() => {
215
306
  void (async () => {
216
307
  const daemonCommandLine = await readProcessCommandLine(daemonPid);
308
+ if (!state.isRunning) {
309
+ return;
310
+ }
217
311
  if (daemonCommandLine === undefined || daemonCommandLine.trim() === '') {
218
312
  try {
219
313
  process.kill(daemonPid, 'SIGTERM');
@@ -221,12 +315,18 @@ async function main() {
221
315
  catch {
222
316
  // Best effort only.
223
317
  }
318
+ if (!state.isRunning) {
319
+ return;
320
+ }
224
321
  sendIpc({
225
322
  type: 'failed',
226
323
  errorText: `failed to capture daemon command line from OS for pid ${String(daemonPid)}`,
227
324
  });
228
325
  return;
229
326
  }
327
+ if (!state.isRunning) {
328
+ return;
329
+ }
230
330
  state.daemonCommandLine = daemonCommandLine;
231
331
  sendIpc({
232
332
  type: 'daemonized',
@@ -245,43 +345,6 @@ async function main() {
245
345
  });
246
346
  });
247
347
  }, init.timeoutSeconds * 1000);
248
- childProcess.once('close', (code, signal) => {
249
- clearTimeout(timeoutHandle);
250
- state.isRunning = false;
251
- state.exitCode = code;
252
- state.exitSignal = signal;
253
- if (state.daemonCommandLine === null) {
254
- sendIpc({
255
- type: 'completed',
256
- exitCode: code,
257
- exitSignal: signal,
258
- stdout: state.stdout.snapshot(),
259
- stderr: state.stderr.snapshot(),
260
- });
261
- }
262
- server.close(() => {
263
- if (process.platform !== 'win32') {
264
- void promises_1.default.unlink(endpoint).catch(() => {
265
- // Best effort only.
266
- });
267
- }
268
- setImmediate(() => {
269
- process.exit(0);
270
- });
271
- });
272
- });
273
- childProcess.once('error', (error) => {
274
- clearTimeout(timeoutHandle);
275
- sendIpc({
276
- type: 'failed',
277
- errorText: error.message,
278
- });
279
- server.close(() => {
280
- setImmediate(() => {
281
- process.exit(1);
282
- });
283
- });
284
- });
285
348
  }
286
349
  async function handleStopRequest(request, state, childProcess) {
287
350
  if (!state.isRunning) {
@@ -307,14 +370,12 @@ async function handleStopRequest(request, state, childProcess) {
307
370
  }
308
371
  }
309
372
  void main().catch((error) => {
310
- try {
311
- sendIpc({
312
- type: 'failed',
313
- errorText: error instanceof Error ? error.message : String(error),
314
- });
315
- }
316
- catch {
317
- // No IPC channel available.
318
- }
319
- process.exit(1);
373
+ void flushIpc({
374
+ type: 'failed',
375
+ errorText: error instanceof Error ? error.message : String(error),
376
+ })
377
+ .catch(() => undefined)
378
+ .finally(() => {
379
+ process.exit(1);
380
+ });
320
381
  });
@@ -0,0 +1,3 @@
1
+ import type { FuncTool } from '../tool';
2
+ export declare const readPictureTool: FuncTool;
3
+ export declare const writePictureTool: FuncTool;
@@ -0,0 +1,344 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.writePictureTool = exports.readPictureTool = void 0;
7
+ const crypto_1 = require("crypto");
8
+ const promises_1 = __importDefault(require("fs/promises"));
9
+ const path_1 = __importDefault(require("path"));
10
+ const access_control_1 = require("../access-control");
11
+ const persistence_1 = require("../persistence");
12
+ const work_language_1 = require("../runtime/work-language");
13
+ const tool_1 = require("../tool");
14
+ const PICTURE_MAX_BYTES = 50 * 1024 * 1024;
15
+ function ok(content, contentItems) {
16
+ return (0, tool_1.toolSuccess)(content, contentItems);
17
+ }
18
+ function fail(content) {
19
+ return (0, tool_1.toolFailure)(content);
20
+ }
21
+ function formatYamlCodeBlock(yaml) {
22
+ return `\`\`\`yaml\n${yaml}\n\`\`\``;
23
+ }
24
+ function yamlQuote(value) {
25
+ return `'${value.replace(/'/g, "''")}'`;
26
+ }
27
+ function ensureInsideWorkspace(rel) {
28
+ const absPath = path_1.default.resolve(process.cwd(), rel);
29
+ const cwd = path_1.default.resolve(process.cwd());
30
+ const relative = path_1.default.relative(cwd, absPath);
31
+ if (relative === '' || (!relative.startsWith('..') && !path_1.default.isAbsolute(relative))) {
32
+ return absPath;
33
+ }
34
+ throw new Error('Path must be within rtws (runtime workspace)');
35
+ }
36
+ function requirePathArg(args) {
37
+ const value = args['path'];
38
+ if (typeof value !== 'string' || value.trim() === '') {
39
+ throw new Error('Invalid arguments: `path` must be a non-empty string');
40
+ }
41
+ return value.trim();
42
+ }
43
+ function optionalBooleanArg(args, key) {
44
+ const value = args[key];
45
+ if (value === undefined)
46
+ return undefined;
47
+ if (typeof value !== 'boolean') {
48
+ throw new Error(`Invalid arguments: \`${key}\` must be a boolean`);
49
+ }
50
+ return value;
51
+ }
52
+ function extToMimeType(relPath) {
53
+ switch (path_1.default.extname(relPath).toLowerCase()) {
54
+ case '.png':
55
+ return 'image/png';
56
+ case '.jpg':
57
+ case '.jpeg':
58
+ return 'image/jpeg';
59
+ case '.webp':
60
+ return 'image/webp';
61
+ case '.gif':
62
+ return 'image/gif';
63
+ default:
64
+ return null;
65
+ }
66
+ }
67
+ function mimeTypeToExt(mimeType) {
68
+ switch (mimeType) {
69
+ case 'image/png':
70
+ return 'png';
71
+ case 'image/jpeg':
72
+ return 'jpg';
73
+ case 'image/webp':
74
+ return 'webp';
75
+ case 'image/gif':
76
+ return 'gif';
77
+ default: {
78
+ const _exhaustive = mimeType;
79
+ return _exhaustive;
80
+ }
81
+ }
82
+ }
83
+ function parseSupportedMimeType(value) {
84
+ switch (value) {
85
+ case 'image/png':
86
+ case 'image/jpeg':
87
+ case 'image/webp':
88
+ case 'image/gif':
89
+ return value;
90
+ default:
91
+ return null;
92
+ }
93
+ }
94
+ function detectImageMimeType(bytes) {
95
+ if (bytes.length >= 8 &&
96
+ bytes[0] === 0x89 &&
97
+ bytes[1] === 0x50 &&
98
+ bytes[2] === 0x4e &&
99
+ bytes[3] === 0x47 &&
100
+ bytes[4] === 0x0d &&
101
+ bytes[5] === 0x0a &&
102
+ bytes[6] === 0x1a &&
103
+ bytes[7] === 0x0a) {
104
+ return 'image/png';
105
+ }
106
+ if (bytes.length >= 3 && bytes[0] === 0xff && bytes[1] === 0xd8 && bytes[2] === 0xff) {
107
+ return 'image/jpeg';
108
+ }
109
+ if (bytes.length >= 12 &&
110
+ bytes.subarray(0, 4).toString('ascii') === 'RIFF' &&
111
+ bytes.subarray(8, 12).toString('ascii') === 'WEBP') {
112
+ return 'image/webp';
113
+ }
114
+ if (bytes.length >= 6 &&
115
+ (bytes.subarray(0, 6).toString('ascii') === 'GIF87a' ||
116
+ bytes.subarray(0, 6).toString('ascii') === 'GIF89a')) {
117
+ return 'image/gif';
118
+ }
119
+ return null;
120
+ }
121
+ function validateImageBytesMatchMimeType(bytes, mimeType) {
122
+ const detected = detectImageMimeType(bytes);
123
+ if (detected === null) {
124
+ throw new Error('Image bytes do not match a supported PNG/JPEG/WebP/GIF signature');
125
+ }
126
+ if (detected !== mimeType) {
127
+ throw new Error(`Image bytes are ${detected}, but path/mime_type declares ${mimeType}`);
128
+ }
129
+ }
130
+ function stripDataUrlPrefix(value) {
131
+ const trimmed = value.trim();
132
+ if (!trimmed.startsWith('data:'))
133
+ return { base64: trimmed };
134
+ const match = /^data:([^;,]+);base64,(.*)$/s.exec(trimmed);
135
+ if (!match)
136
+ return { base64: trimmed };
137
+ const mimeType = parseSupportedMimeType(match[1]);
138
+ return {
139
+ base64: match[2] ?? '',
140
+ ...(mimeType === null ? {} : { mimeType }),
141
+ };
142
+ }
143
+ function isStrictBase64Payload(value) {
144
+ if (value.length === 0 || value.length % 4 !== 0)
145
+ return false;
146
+ return /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(value);
147
+ }
148
+ function decodeStrictBase64(value) {
149
+ const normalized = value.replace(/\s+/g, '');
150
+ if (!isStrictBase64Payload(normalized)) {
151
+ throw new Error('Image data must be strict base64 or a base64 data URL');
152
+ }
153
+ return Buffer.from(normalized, 'base64');
154
+ }
155
+ function sanitizePathSegment(value) {
156
+ const cleaned = value.replace(/[^0-9A-Za-z._-]/g, '_').replace(/_+/g, '_');
157
+ const trimmed = cleaned.replace(/^_+|_+$/g, '');
158
+ return trimmed.length > 0 ? trimmed.slice(0, 96) : 'picture';
159
+ }
160
+ async function persistPictureArtifact(args) {
161
+ const eventsBase = persistence_1.DialogPersistence.getDialogEventsPath(args.dlg.id, args.dlg.status);
162
+ const relPath = path_1.default.posix.join('artifacts', 'workspace', sanitizePathSegment(args.toolName), `${Date.now().toString(36)}-${(0, crypto_1.randomUUID)()}.${mimeTypeToExt(args.mimeType)}`);
163
+ const absPath = path_1.default.join(eventsBase, ...relPath.split('/'));
164
+ await promises_1.default.mkdir(path_1.default.dirname(absPath), { recursive: true });
165
+ await promises_1.default.writeFile(absPath, args.bytes);
166
+ return {
167
+ type: 'input_image',
168
+ mimeType: args.mimeType,
169
+ byteLength: args.bytes.length,
170
+ artifact: {
171
+ rootId: args.dlg.id.rootId,
172
+ selfId: args.dlg.id.selfId,
173
+ status: args.dlg.status,
174
+ relPath,
175
+ },
176
+ };
177
+ }
178
+ function formatPictureResultYaml(args) {
179
+ return formatYamlCodeBlock([
180
+ `status: ${args.status}`,
181
+ `action: ${args.action}`,
182
+ `path: ${yamlQuote(args.path)}`,
183
+ `mime_type: ${yamlQuote(args.mimeType)}`,
184
+ `byte_length: ${String(args.byteLength)}`,
185
+ `artifact_rel_path: ${yamlQuote(args.artifactRelPath)}`,
186
+ 'llm_context: image_attached',
187
+ ].join('\n'));
188
+ }
189
+ exports.readPictureTool = {
190
+ type: 'func',
191
+ name: 'read_picture',
192
+ description: 'Read a PNG/JPEG/WebP/GIF image from rtws and attach it as an image content item for the next LLM context.',
193
+ descriptionI18n: {
194
+ en: 'Read a PNG/JPEG/WebP/GIF image from rtws and attach it as an image content item for the next LLM context.',
195
+ zh: '读取 rtws 中的 PNG/JPEG/WebP/GIF 图片,并作为图片 content item 放入后续 LLM 上下文。',
196
+ },
197
+ parameters: {
198
+ type: 'object',
199
+ additionalProperties: false,
200
+ properties: {
201
+ path: {
202
+ type: 'string',
203
+ description: 'rtws-relative image path to read. Supported extensions: .png, .jpg, .jpeg, .webp, .gif.',
204
+ },
205
+ },
206
+ required: ['path'],
207
+ },
208
+ argsValidation: 'dominds',
209
+ call: async (dlg, caller, args) => {
210
+ const language = (0, work_language_1.getWorkLanguage)();
211
+ try {
212
+ const relPath = requirePathArg(args);
213
+ if (!(0, access_control_1.hasReadAccess)(caller, relPath)) {
214
+ return fail((0, access_control_1.getAccessDeniedMessage)('read', relPath, language));
215
+ }
216
+ const mimeType = extToMimeType(relPath);
217
+ if (mimeType === null) {
218
+ return fail('Unsupported image extension. Supported extensions: .png, .jpg, .jpeg, .webp, .gif');
219
+ }
220
+ const absPath = ensureInsideWorkspace(relPath);
221
+ const stat = await promises_1.default.stat(absPath);
222
+ if (!stat.isFile())
223
+ return fail(`Path is not a file: ${relPath}`);
224
+ if (stat.size <= 0 || stat.size > PICTURE_MAX_BYTES) {
225
+ return fail(`Image must be between 1 byte and ${String(PICTURE_MAX_BYTES)} bytes`);
226
+ }
227
+ const bytes = await promises_1.default.readFile(absPath);
228
+ validateImageBytesMatchMimeType(bytes, mimeType);
229
+ const item = await persistPictureArtifact({
230
+ dlg,
231
+ toolName: 'read_picture',
232
+ mimeType,
233
+ bytes,
234
+ });
235
+ return ok(formatPictureResultYaml({
236
+ status: 'ok',
237
+ action: 'read_picture',
238
+ path: relPath,
239
+ mimeType,
240
+ byteLength: bytes.length,
241
+ artifactRelPath: item.artifact.relPath,
242
+ }), [item]);
243
+ }
244
+ catch (error) {
245
+ return fail(error instanceof Error ? error.message : String(error));
246
+ }
247
+ },
248
+ };
249
+ exports.writePictureTool = {
250
+ type: 'func',
251
+ name: 'write_picture',
252
+ description: 'Write a PNG/JPEG/WebP/GIF image to rtws from strict base64 or a base64 data URL, then attach the written image as a content item.',
253
+ descriptionI18n: {
254
+ en: 'Write a PNG/JPEG/WebP/GIF image to rtws from strict base64 or a base64 data URL, then attach the written image as a content item.',
255
+ zh: '把 strict base64 或 base64 data URL 写成 rtws 中的 PNG/JPEG/WebP/GIF 图片,并把写出的图片作为 content item 返回。',
256
+ },
257
+ parameters: {
258
+ type: 'object',
259
+ additionalProperties: false,
260
+ properties: {
261
+ path: {
262
+ type: 'string',
263
+ description: 'rtws-relative destination image path. Supported extensions: .png, .jpg, .jpeg, .webp, .gif.',
264
+ },
265
+ data_base64: {
266
+ type: 'string',
267
+ description: 'Strict base64 image payload, or a base64 data URL.',
268
+ },
269
+ mime_type: {
270
+ type: 'string',
271
+ description: 'Optional MIME type. Supported values: image/png, image/jpeg, image/webp, image/gif.',
272
+ },
273
+ overwrite: {
274
+ type: 'boolean',
275
+ description: 'Whether to overwrite an existing file. Defaults to false.',
276
+ },
277
+ },
278
+ required: ['path', 'data_base64'],
279
+ },
280
+ argsValidation: 'dominds',
281
+ call: async (dlg, caller, args) => {
282
+ const language = (0, work_language_1.getWorkLanguage)();
283
+ try {
284
+ const relPath = requirePathArg(args);
285
+ if (!(0, access_control_1.hasWriteAccess)(caller, relPath)) {
286
+ return fail((0, access_control_1.getAccessDeniedMessage)('write', relPath, language));
287
+ }
288
+ const rawData = args['data_base64'];
289
+ if (typeof rawData !== 'string' || rawData.trim() === '') {
290
+ return fail('Invalid arguments: `data_base64` must be a non-empty string');
291
+ }
292
+ const parsedData = stripDataUrlPrefix(rawData);
293
+ const explicitMimeType = parseSupportedMimeType(args['mime_type']);
294
+ if (args['mime_type'] !== undefined && explicitMimeType === null) {
295
+ return fail('Unsupported mime_type. Supported values: image/png, image/jpeg, image/webp, image/gif');
296
+ }
297
+ const pathMimeType = extToMimeType(relPath);
298
+ if (pathMimeType === null) {
299
+ return fail('Unsupported image extension. Supported extensions: .png, .jpg, .jpeg, .webp, .gif');
300
+ }
301
+ const mimeType = explicitMimeType ?? parsedData.mimeType ?? pathMimeType;
302
+ if (mimeType !== pathMimeType) {
303
+ return fail(`mime_type ${mimeType} does not match destination extension for ${relPath}`);
304
+ }
305
+ const bytes = decodeStrictBase64(parsedData.base64);
306
+ if (bytes.length <= 0 || bytes.length > PICTURE_MAX_BYTES) {
307
+ return fail(`Image must be between 1 byte and ${String(PICTURE_MAX_BYTES)} bytes`);
308
+ }
309
+ validateImageBytesMatchMimeType(bytes, mimeType);
310
+ const overwrite = optionalBooleanArg(args, 'overwrite') ?? false;
311
+ const absPath = ensureInsideWorkspace(relPath);
312
+ if (!overwrite) {
313
+ try {
314
+ await promises_1.default.stat(absPath);
315
+ return fail(`File already exists: ${relPath}. Pass overwrite=true to replace it.`);
316
+ }
317
+ catch (error) {
318
+ if (!(error instanceof Error) || !('code' in error) || error.code !== 'ENOENT') {
319
+ throw error;
320
+ }
321
+ }
322
+ }
323
+ await promises_1.default.mkdir(path_1.default.dirname(absPath), { recursive: true });
324
+ await promises_1.default.writeFile(absPath, bytes);
325
+ const item = await persistPictureArtifact({
326
+ dlg,
327
+ toolName: 'write_picture',
328
+ mimeType,
329
+ bytes,
330
+ });
331
+ return ok(formatPictureResultYaml({
332
+ status: 'ok',
333
+ action: 'write_picture',
334
+ path: relPath,
335
+ mimeType,
336
+ byteLength: bytes.length,
337
+ artifactRelPath: item.artifact.relPath,
338
+ }), [item]);
339
+ }
340
+ catch (error) {
341
+ return fail(error instanceof Error ? error.message : String(error));
342
+ }
343
+ },
344
+ };
@@ -92,7 +92,9 @@ Taskdoc is a **task contract** and the task's **team-shared source of current tr
92
92
 
93
93
  ### Decision Rules
94
94
 
95
- - If the current sideline is unfinished, blocked, uncertain, or needs an upstream clarification: call `tellaskBack({ tellaskContent })`
95
+ - If the current sideline is unfinished, first judge whether team SOP / role ownership already identifies the responsible owner; if yes and the issue is execution work, directly use `tellask` / `tellaskSessionless` for that owner
96
+ - Call `tellaskBack({ tellaskContent })` only when upstream must clarify the request, decide a tradeoff, confirm acceptance criteria, provide missing input, or current SOP cannot determine ownership
97
+ - If a human must personally perform login / GUI / captcha / high-risk authorization: call `askHuman({ tellaskContent })`
96
98
  - If the current sideline is complete and the assignment header says `replyTellask`: call `replyTellask({ replyContent })`
97
99
  - If the current sideline is complete and the assignment header says `replyTellaskSessionless`: call `replyTellaskSessionless({ replyContent })`
98
100
  - If you are answering an upstream `tellaskBack` follow-up and runtime exposes `replyTellaskBack`: call `replyTellaskBack({ replyContent })`
@@ -104,7 +106,7 @@ Taskdoc is a **task contract** and the task's **team-shared source of current tr
104
106
  - Do not memorize reply variants by yourself; follow the current assignment header and the function currently exposed by runtime
105
107
  - `reply*` tool descriptions are intentionally minimal and spec-like; use this manual's principles / scenarios for situational guidance
106
108
  - If runtime exposes only one `reply*`, that is the only correct completion path for the current state
107
- - `tellaskBack` is for ask-back only, not final delivery
109
+ - `tellaskBack` is the fallback when ownership cannot be determined from existing SOP, or when upstream must answer; it is not the default first move for every blocked state
108
110
 
109
111
  ## Best Practices
110
112
 
@@ -89,7 +89,8 @@ tellaskBack({
89
89
 
90
90
  ### Key Points
91
91
 
92
- - Use `tellaskBack` while the work is still unfinished
92
+ - This example uses `tellaskBack` because upstream input is specifically required
93
+ - If team SOP / role ownership already identifies the responsible executor, directly use `tellask` / `tellaskSessionless` for that owner instead of mapping every unfinished state to `tellaskBack`
93
94
  - Do not use `replyTellask*` for intermediate clarifications
94
95
 
95
96
  ## Scenario 4: Upstream answered the ask-back, so use replyTellaskBack to close
@@ -23,12 +23,12 @@
23
23
 
24
24
  The **tool descriptions themselves** for these functions intentionally stay minimal and spec-like. This section carries the smallest practical lookup for when they appear and how to choose among them.
25
25
 
26
- | Function | Minimal parameter contract | When runtime exposes it | Effect |
27
- | ------------------------- | ---------------------------- | --------------------------------------------------------------------------------------------- | ---------------------------------------------------------- |
28
- | `replyTellask` | `{ replyContent: string }` | Current sideline comes from a sessioned `tellask` and is ready for final delivery | Delivers the final result for the current tellask session |
29
- | `replyTellaskSessionless` | `{ replyContent: string }` | Current sideline comes from a one-shot `tellaskSessionless` and is ready for final delivery | Delivers the final result for the current one-shot tellask |
30
- | `replyTellaskBack` | `{ replyContent: string }` | Current dialog holds an unresolved `tellaskBack` reply directive | Delivers the final answer to the upstream ask-back |
31
- | `tellaskBack` | `{ tellaskContent: string }` | Current sideline is not done yet and needs clarification / ask-back / blocked-state reporting | Sends a follow-up request upstream; not final delivery |
26
+ | Function | Minimal parameter contract | When runtime exposes it | Effect |
27
+ | ------------------------- | ---------------------------- | --------------------------------------------------------------------------------------------------- | ---------------------------------------------------------- |
28
+ | `replyTellask` | `{ replyContent: string }` | Current sideline comes from a sessioned `tellask` and is ready for final delivery | Delivers the final result for the current tellask session |
29
+ | `replyTellaskSessionless` | `{ replyContent: string }` | Current sideline comes from a one-shot `tellaskSessionless` and is ready for final delivery | Delivers the final result for the current one-shot tellask |
30
+ | `replyTellaskBack` | `{ replyContent: string }` | Current dialog holds an unresolved `tellaskBack` reply directive | Delivers the final answer to the upstream ask-back |
31
+ | `tellaskBack` | `{ tellaskContent: string }` | Current sideline must ask upstream back, and existing team SOP cannot directly assign another owner | Sends a follow-up request upstream; not final delivery |
32
32
 
33
33
  ### Minimal Usage Rules
34
34
 
@@ -92,7 +92,9 @@
92
92
 
93
93
  ### 决策规则
94
94
 
95
- - 当前支线未完成、仍有疑问、被阻塞,或必须向上游补问时:调用 `tellaskBack({ tellaskContent })`
95
+ - 当前支线未完成时,先判断团队规程 / SOP / 职责卡能否明确负责人;若能明确且属于执行性处理,直接 `tellask` / `tellaskSessionless` 对应负责人
96
+ - 只有当必须向上游补需求、澄清目标、裁决取舍、确认验收口径、提供缺失输入,或现有规程无法明确判责时:调用 `tellaskBack({ tellaskContent })`
97
+ - 需要人类亲自登录 / GUI / 验证码 / 高风险授权时:调用 `askHuman({ tellaskContent })`
96
98
  - 当前支线已经完成,且当前 assignment 明确要求 `replyTellask`:调用 `replyTellask({ replyContent })`
97
99
  - 当前支线已经完成,且当前 assignment 明确要求 `replyTellaskSessionless`:调用 `replyTellaskSessionless({ replyContent })`
98
100
  - 当前是在回复一条上游发来的 `tellaskBack` 续诉请,且 runtime 暴露了 `replyTellaskBack`:调用 `replyTellaskBack({ replyContent })`
@@ -104,7 +106,7 @@
104
106
  - 不要靠记忆硬选 reply 变体;以当前 assignment 头部和 runtime 当前暴露的函数名为准
105
107
  - `reply*` 函数自身说明文案故意保持极简,只承载最小规格;情景判断看本手册的 principles / scenarios
106
108
  - 若 runtime 只暴露一个 `reply*`,那就是当前应调用的唯一完成路径
107
- - `tellaskBack` 只用于回问,不用于最终交付
109
+ - `tellaskBack` 是“无法按现有规程明确判责,或必须回问上游”的兜底动作,不是所有阻塞的默认第一动作
108
110
 
109
111
  ## 最佳实践
110
112
 
@@ -89,7 +89,8 @@ tellaskBack({
89
89
 
90
90
  ### 关键点
91
91
 
92
- - 未完成态用 `tellaskBack`,不要用 `replyTellask*`
92
+ - 这里只是“必须向上游补输入”的例子,所以用 `tellaskBack`
93
+ - 若团队规程 / SOP / 职责卡已经能明确执行负责人,应直接 `tellask` / `tellaskSessionless` 对应负责人,而不是机械因为“未完成态”就用 `tellaskBack`
93
94
  - `tellaskBack` 只负责把问题问回去,不负责最终交付
94
95
 
95
96
  ## 场景 4:收到 ask-back 续诉请后,用 replyTellaskBack 收口
@@ -28,7 +28,7 @@
28
28
  | `replyTellask` | `{ replyContent: string }` | 当前支线承接的是 sessioned `tellask`,且已进入可交付完成态 | 把最终结果回复给当前 `tellask` 会话 |
29
29
  | `replyTellaskSessionless` | `{ replyContent: string }` | 当前支线承接的是 one-shot `tellaskSessionless`,且已进入可交付完成态 | 把最终结果回复给当前一次性诉请 |
30
30
  | `replyTellaskBack` | `{ replyContent: string }` | 当前对话持有一条未完成的 `tellaskBack` 回复指令 | 把对上一条回问的最终答复送回上游 |
31
- | `tellaskBack` | `{ tellaskContent: string }` | 当前支线尚未完成,但需要补问/澄清/报阻塞 | 向上游发起续诉请,不算最终交付 |
31
+ | `tellaskBack` | `{ tellaskContent: string }` | 当前支线必须回问上游,且现有团队规程无法直接判责到其他负责人 | 向上游发起续诉请,不算最终交付 |
32
32
 
33
33
  ### 最小使用规则
34
34
 
@@ -13,6 +13,7 @@ You have read/write access to the rtws (runtime workspace), but **all incrementa
13
13
  - Normalization: all writes follow “each line ends with `\n` (including the last line)”; missing EOF newline will be added and shown in `normalized.*`.
14
14
  - Exception: `overwrite_entire_file` overwrites an existing file (writes immediately; does not use prepare/apply). It requires `known_old_total_lines/known_old_total_bytes` guardrails (read `total_lines/size_bytes` from the YAML header of `read_file`). `content_format` accepts any non-empty text label (for example `yaml`), but diff/patch-like content is still rejected by default unless `content_format=diff|patch`. Use it only for “small content (<100 lines)” or “intentional reset/generated output”; otherwise prefer prepare/apply.
15
15
  - Exception: `create_new_file` only creates a new file (empty content allowed). It does not do incremental edits and does not use prepare/apply; it refuses to overwrite existing files.
16
+ - Binary image tools: use `read_picture({ path })` to inspect PNG/JPEG/WebP/GIF images as real image context; use `write_picture({ path, data_base64, mime_type, overwrite })` to write a base64 image. These are binary image operations and do not use prepare/apply.
16
17
 
17
18
  ## Which `prepare_*` to use
18
19