neoctl 0.2.31 → 0.2.33

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 (103) hide show
  1. package/dist/agents/agent-activity.d.ts +3 -0
  2. package/dist/agents/agent-activity.js +33 -6
  3. package/dist/agents/agent-activity.js.map +1 -1
  4. package/dist/agents/agent-definition.d.ts +2 -2
  5. package/dist/agents/agent-definition.js +6 -6
  6. package/dist/agents/agent-definition.js.map +1 -1
  7. package/dist/agents/agent-report-tool.d.ts +1 -1
  8. package/dist/agents/agent-report-tool.js +4 -4
  9. package/dist/agents/agent-report-tool.js.map +1 -1
  10. package/dist/agents/agent-tool.d.ts +1 -1
  11. package/dist/agents/agent-tool.js +99 -20
  12. package/dist/agents/agent-tool.js.map +1 -1
  13. package/dist/agents/local-agent-task.d.ts +12 -0
  14. package/dist/agents/local-agent-task.js +38 -1
  15. package/dist/agents/local-agent-task.js.map +1 -1
  16. package/dist/agents/smoke-agents.js +37 -26
  17. package/dist/agents/smoke-agents.js.map +1 -1
  18. package/dist/context/compaction.js +1 -1
  19. package/dist/context/compaction.js.map +1 -1
  20. package/dist/context/prompts.js +6 -6
  21. package/dist/context/prompts.js.map +1 -1
  22. package/dist/context/smoke-context.js +15 -15
  23. package/dist/context/smoke-context.js.map +1 -1
  24. package/dist/core/query-engine.js +2 -0
  25. package/dist/core/query-engine.js.map +1 -1
  26. package/dist/core/query.js +96 -8
  27. package/dist/core/query.js.map +1 -1
  28. package/dist/core/run-agent.d.ts +13 -2
  29. package/dist/core/run-agent.js +16 -13
  30. package/dist/core/run-agent.js.map +1 -1
  31. package/dist/core/smoke-core-loop.js +55 -2
  32. package/dist/core/smoke-core-loop.js.map +1 -1
  33. package/dist/index.d.ts +1 -1
  34. package/dist/index.js +1 -1
  35. package/dist/index.js.map +1 -1
  36. package/dist/plugins/smoke-plugin-system.js +14 -14
  37. package/dist/repl/index.js +16 -14
  38. package/dist/repl/index.js.map +1 -1
  39. package/dist/secrets/secret-redaction.d.ts +5 -0
  40. package/dist/secrets/secret-redaction.js +43 -0
  41. package/dist/secrets/secret-redaction.js.map +1 -1
  42. package/dist/secrets/secret-types.d.ts +4 -0
  43. package/dist/secrets/secret-types.js.map +1 -1
  44. package/dist/secrets/smoke-secrets.js +7 -3
  45. package/dist/secrets/smoke-secrets.js.map +1 -1
  46. package/dist/skills/smoke-skills.js +5 -5
  47. package/dist/skills/smoke-skills.js.map +1 -1
  48. package/dist/tasks/subagent-tools.d.ts +31 -0
  49. package/dist/tasks/{task-tools.js → subagent-tools.js} +26 -32
  50. package/dist/tasks/subagent-tools.js.map +1 -0
  51. package/dist/tools/builtins/edit-tool.d.ts +2 -0
  52. package/dist/tools/builtins/edit-tool.js +28 -6
  53. package/dist/tools/builtins/edit-tool.js.map +1 -1
  54. package/dist/tools/builtins/exec-process-manager.d.ts +4 -0
  55. package/dist/tools/builtins/exec-process-manager.js +15 -6
  56. package/dist/tools/builtins/exec-process-manager.js.map +1 -1
  57. package/dist/tools/builtins/exec-tool.js +24 -8
  58. package/dist/tools/builtins/exec-tool.js.map +1 -1
  59. package/dist/tools/builtins/filesystem-tools.js +2 -4
  60. package/dist/tools/builtins/filesystem-tools.js.map +1 -1
  61. package/dist/tools/builtins/grep-tool.js +2 -3
  62. package/dist/tools/builtins/grep-tool.js.map +1 -1
  63. package/dist/tools/builtins/image-generation-tool.js +4 -4
  64. package/dist/tools/builtins/image-generation-tool.js.map +1 -1
  65. package/dist/tools/builtins/image-loader-tool.js +2 -3
  66. package/dist/tools/builtins/image-loader-tool.js.map +1 -1
  67. package/dist/tools/builtins/image-note-tool.js +0 -1
  68. package/dist/tools/builtins/image-note-tool.js.map +1 -1
  69. package/dist/tools/builtins/plan-tool.js +1 -1
  70. package/dist/tools/builtins/plan-tool.js.map +1 -1
  71. package/dist/tools/builtins/search-tool.js +3 -3
  72. package/dist/tools/builtins/search-tool.js.map +1 -1
  73. package/dist/tools/run-tool-use.js +3 -2
  74. package/dist/tools/run-tool-use.js.map +1 -1
  75. package/dist/tools/smoke-tool-system.js +49 -26
  76. package/dist/tools/smoke-tool-system.js.map +1 -1
  77. package/dist/tools/tool-catalog.d.ts +36 -0
  78. package/dist/tools/tool-catalog.js +64 -0
  79. package/dist/tools/tool-catalog.js.map +1 -0
  80. package/dist/tools/tool-orchestration.d.ts +20 -3
  81. package/dist/tools/tool-orchestration.js +29 -4
  82. package/dist/tools/tool-orchestration.js.map +1 -1
  83. package/dist/tools/tool.d.ts +13 -0
  84. package/dist/tools/tool.js.map +1 -1
  85. package/dist/types/events.d.ts +23 -0
  86. package/dist/types/messages.d.ts +8 -0
  87. package/dist/types/messages.js +16 -0
  88. package/dist/types/messages.js.map +1 -1
  89. package/dist/web/index.d.ts +39 -0
  90. package/dist/web/index.js +295 -92
  91. package/dist/web/index.js.map +1 -1
  92. package/dist/web/runtime-context-protocol.d.ts +7 -0
  93. package/dist/web/runtime-context-protocol.js +3 -2
  94. package/dist/web/runtime-context-protocol.js.map +1 -1
  95. package/dist/web/smoke-runtime-context-protocol.js +10 -2
  96. package/dist/web/smoke-runtime-context-protocol.js.map +1 -1
  97. package/dist/web/smoke-web-history.js +64 -2
  98. package/dist/web/smoke-web-history.js.map +1 -1
  99. package/dist/web/smoke-web-terminal-tasks.js +1 -0
  100. package/dist/web/smoke-web-terminal-tasks.js.map +1 -1
  101. package/package.json +4 -4
  102. package/dist/tasks/task-tools.d.ts +0 -31
  103. package/dist/tasks/task-tools.js.map +0 -1
package/dist/web/index.js CHANGED
@@ -14,6 +14,7 @@ import { findModelMetadata, loadModelCatalog, reasoningEffortsForModel, resolveC
14
14
  import { CommunicationLogger, LoggingModelGateway } from "../model/communication-logger.js";
15
15
  import { createModelGatewayFromConfig, createModelGatewayFromProcessEnv } from "../model/provider-factory.js";
16
16
  import { ToolRegistry } from "../tools/registry.js";
17
+ import { builtinToolPresentation, toolPresentationForSource } from "../tools/tool-catalog.js";
17
18
  import { editTool, writeTool } from "../tools/builtins/edit-tool.js";
18
19
  import { createExecTool, createWriteStdinTool } from "../tools/builtins/exec-tool.js";
19
20
  import { ExecProcessManager } from "../tools/builtins/exec-process-manager.js";
@@ -24,7 +25,7 @@ import { planTool } from "../tools/builtins/plan-tool.js";
24
25
  import { createOpenAIImageGenerationTool } from "../tools/builtins/image-generation-tool.js";
25
26
  import { createLoadImageTool } from "../tools/builtins/image-loader-tool.js";
26
27
  import { createAgentTool, resumeAgentTask } from "../agents/agent-tool.js";
27
- import { createTaskTools } from "../tasks/task-tools.js";
28
+ import { createSubagentTools } from "../tasks/subagent-tools.js";
28
29
  import { TaskStore } from "../tasks/task-store.js";
29
30
  import { parseReplCommand, helpText, replCommandDefinitions } from "../repl/commands.js";
30
31
  import { writeSessionMarkdownExport } from "../session/session-export.js";
@@ -136,7 +137,7 @@ export async function createWebRuntime(options = {}) {
136
137
  if (catalogedToolNames.has(tool.name))
137
138
  return;
138
139
  catalogedToolNames.add(tool.name);
139
- toolCatalog.push({ name: tool.name, ...item });
140
+ toolCatalog.push({ name: tool.name, presentation: toolPresentationForSource(tool.name, item.source), ...item });
140
141
  };
141
142
  const registerTool = (tool, item) => {
142
143
  tools.register(tool);
@@ -175,7 +176,7 @@ export async function createWebRuntime(options = {}) {
175
176
  };
176
177
  return resumeAgentTask(taskId, directive, agentRuntime, taskStore, dummyContext);
177
178
  };
178
- for (const tool of createTaskTools(taskStore, resumeHandler))
179
+ for (const tool of createSubagentTools(taskStore, resumeHandler))
179
180
  registerTool(tool, { source: "builtin" });
180
181
  const globalToolOverrides = normalizeToolOverrides(options.globalToolOverrides, toolCatalog);
181
182
  const sessionToolOverrides = normalizeToolOverrides(options.sessionToolOverrides, toolCatalog);
@@ -290,7 +291,7 @@ function createTaskNotificationSource(taskStore) {
290
291
  };
291
292
  }
292
293
  function syncImageGenerationTool(runtime, provider) {
293
- runtime.tools.unregister("image2");
294
+ runtime.tools.unregister("image_create");
294
295
  if (provider === "openai")
295
296
  runtime.tools.register(createOpenAIImageGenerationTool());
296
297
  }
@@ -1004,7 +1005,7 @@ export class WebRepl {
1004
1005
  this.pendingUserImageEchoIds = undefined;
1005
1006
  this.pendingUserImageEchoMessageId = undefined;
1006
1007
  this.finalizeForegroundView();
1007
- this.finalizeLiveToolLines();
1008
+ this.cancelLiveToolLines(reason);
1008
1009
  this.busy = false;
1009
1010
  this.status = { ...this.status, phase: "ready", detail: undefined, currentTool: undefined, inputTokenUpdatedAt: undefined, outputTokenUpdatedAt: undefined, retryCooldownUntil: undefined };
1010
1011
  this.broadcastSync();
@@ -1133,6 +1134,140 @@ export class WebRepl {
1133
1134
  this.finalizeLiveLine(id);
1134
1135
  this.liveToolLineIds.clear();
1135
1136
  }
1137
+ cancelLiveToolLines(reason) {
1138
+ for (const id of this.liveToolLineIds.values()) {
1139
+ const line = this.lines.find((item) => item.id === id);
1140
+ const steps = (line?.toolStream?.steps ?? []).map((step) => step.status === "running" ? { ...step, status: "failed", phase: "cancelled" } : step);
1141
+ this.lines = this.lines.map((item) => item.id === id ? { ...item, live: false, kind: "error", titleStatus: "failure", bodyTitle: reason, toolStream: { ...item.toolStream, phase: "cancelled", message: reason, steps } } : item);
1142
+ this.queueDeltaOperation({ type: "line.patch", id, patch: { live: false, kind: "error", titleStatus: "failure", bodyTitle: reason, toolStream: { ...line?.toolStream, phase: "cancelled", message: reason, steps } } });
1143
+ }
1144
+ this.liveToolLineIds.clear();
1145
+ }
1146
+ patchLiveToolLine(toolUseId, patch) {
1147
+ const id = this.liveToolLineIds.get(toolUseId);
1148
+ if (id === undefined)
1149
+ return;
1150
+ const line = this.lines.find((item) => item.id === id);
1151
+ if (!line)
1152
+ return;
1153
+ this.lines = this.lines.map((item) => item.id === id ? { ...item, ...patch } : item);
1154
+ this.queueDeltaOperation({ type: "line.patch", id, patch });
1155
+ }
1156
+ applyToolProgress(event) {
1157
+ const id = this.liveToolLineIds.get(event.toolUse.id);
1158
+ if (id === undefined)
1159
+ return;
1160
+ const line = this.lines.find((item) => item.id === id);
1161
+ if (!line)
1162
+ return;
1163
+ const progress = event.progress;
1164
+ const previousStream = line.toolStream ?? {};
1165
+ if (progress.sequence !== undefined && previousStream.sequence !== undefined && progress.sequence <= previousStream.sequence)
1166
+ return;
1167
+ const channel = progress.channel ?? inferToolStreamChannel(progress.data);
1168
+ const operation = progress.operation ?? (channel === "stdout" || channel === "stderr" ? "append" : "replace");
1169
+ const stream = { ...previousStream };
1170
+ stream.channel = channel;
1171
+ stream.phase = progress.phase;
1172
+ stream.sequence = progress.sequence ?? stream.sequence;
1173
+ if (progress.progress) {
1174
+ stream.current = progress.progress.current;
1175
+ stream.total = progress.progress.total;
1176
+ stream.unit = progress.progress.unit;
1177
+ }
1178
+ const terminalDelta = terminalOutputDelta(progress.data);
1179
+ const textDelta = toolStreamTextDelta(progress.data);
1180
+ if (terminalDelta) {
1181
+ const key = terminalDelta.stream === "stderr" ? "stderr" : "stdout";
1182
+ stream[key] = operation === "replace"
1183
+ ? trimToolStreamText(terminalDelta.text, 40_000)
1184
+ : appendToolStreamText(stream[key], terminalDelta.text, 40_000);
1185
+ stream.chunks = appendToolStreamChunk(stream.chunks, progress.sequence, key, terminalDelta.text, operation);
1186
+ }
1187
+ else if ((channel === "stdout" || channel === "stderr") && textDelta !== undefined) {
1188
+ stream[channel] = operation === "replace"
1189
+ ? trimToolStreamText(textDelta, 40_000)
1190
+ : appendToolStreamText(stream[channel], textDelta, 40_000);
1191
+ stream.chunks = appendToolStreamChunk(stream.chunks, progress.sequence, channel, textDelta, operation);
1192
+ }
1193
+ else if ((channel === "patch" || channel === "artifact") && textDelta !== undefined) {
1194
+ stream.output = operation === "replace"
1195
+ ? trimToolStreamText(textDelta, 40_000)
1196
+ : appendToolStreamText(stream.output, textDelta, 40_000);
1197
+ stream.chunks = appendToolStreamChunk(stream.chunks, progress.sequence, "output", textDelta, operation);
1198
+ stream.message = progress.message;
1199
+ }
1200
+ else if (channel === "item") {
1201
+ const key = progress.key || progress.phase || `step-${progress.sequence ?? Date.now()}`;
1202
+ const steps = [...(stream.steps ?? [])];
1203
+ const index = steps.findIndex((step) => step.key === key);
1204
+ if (operation === "remove") {
1205
+ if (index >= 0)
1206
+ steps.splice(index, 1);
1207
+ }
1208
+ else {
1209
+ const status = toolStreamStepStatus(progress.phase);
1210
+ const message = operation === "append" && index >= 0
1211
+ ? `${steps[index].message}${progress.message}`
1212
+ : progress.message;
1213
+ const stepToolName = childToolName(progress.data) ?? steps[index]?.toolName;
1214
+ const step = {
1215
+ key,
1216
+ message,
1217
+ toolName: stepToolName,
1218
+ toolLabel: stepToolName ? toolTitle(stepToolName, "running") : steps[index]?.toolLabel,
1219
+ status,
1220
+ phase: progress.phase,
1221
+ sequence: progress.sequence,
1222
+ };
1223
+ if (index >= 0)
1224
+ steps[index] = step;
1225
+ else
1226
+ steps.push(step);
1227
+ }
1228
+ stream.steps = steps.slice(-24);
1229
+ stream.message = progress.message;
1230
+ }
1231
+ else if (operation === "append") {
1232
+ stream.message = `${stream.message ?? ""}${progress.message}`;
1233
+ }
1234
+ else if (operation === "remove") {
1235
+ stream.message = undefined;
1236
+ }
1237
+ else {
1238
+ stream.message = progress.message;
1239
+ }
1240
+ this.patchLiveToolLine(event.toolUse.id, { toolStream: stream });
1241
+ }
1242
+ applyAvailableToolResult(event) {
1243
+ const liveLineId = this.liveToolLineIds.get(event.toolUse.id);
1244
+ if (liveLineId === undefined)
1245
+ return;
1246
+ const liveLine = this.lines.find((item) => item.id === liveLineId);
1247
+ if (!liveLine)
1248
+ return;
1249
+ let replacement;
1250
+ for (const message of event.messages) {
1251
+ renderToolResultMessage(message, (line, toolUseId) => {
1252
+ if (toolUseId === event.toolUse.id)
1253
+ replacement = line;
1254
+ return liveLineId;
1255
+ }, runtimeToolResultPresenter(this.runtime));
1256
+ }
1257
+ const steps = (liveLine.toolStream?.steps ?? []).map((step) => step.status === "running" ? { ...step, status: event.ok ? "completed" : "failed" } : step);
1258
+ if (replacement) {
1259
+ this.replaceLine(liveLineId, {
1260
+ ...replacement,
1261
+ live: false,
1262
+ presentationLevel: mergePresentationLevel(replacement.presentationLevel, liveLine.presentationLevel),
1263
+ toolDisplay: mergeToolDisplays(replacement.toolDisplay, liveLine.toolDisplay),
1264
+ toolStream: { ...liveLine.toolStream, steps, message: liveLine.toolStream?.message },
1265
+ });
1266
+ }
1267
+ else {
1268
+ this.replaceLine(liveLineId, { live: false, titleStatus: event.ok ? "success" : "failure", toolStream: { ...liveLine.toolStream, steps } });
1269
+ }
1270
+ }
1136
1271
  handleEvent(event) {
1137
1272
  this.reduce(event);
1138
1273
  if (event.type === "message" && this.matchesPendingUserImageEcho(event.message)) {
@@ -1169,7 +1304,21 @@ export class WebRepl {
1169
1304
  this.appendLineText(id, event.text);
1170
1305
  return;
1171
1306
  }
1307
+ if (event.type === "tool.progress") {
1308
+ this.applyToolProgress(event);
1309
+ return;
1310
+ }
1311
+ if (event.type === "tool.result.available") {
1312
+ this.applyAvailableToolResult(event);
1313
+ return;
1314
+ }
1315
+ if (event.type === "tool.batch.completed") {
1316
+ this.broadcastSync();
1317
+ return;
1318
+ }
1172
1319
  if (event.type === "message") {
1320
+ if (event.message.isMeta === true)
1321
+ return;
1173
1322
  let replacedStreamingContent = false;
1174
1323
  if (event.message.role === "assistant" && this.assistantLineId !== undefined) {
1175
1324
  const text = assistantText(event.message);
@@ -1199,10 +1348,16 @@ export class WebRepl {
1199
1348
  if (event.message.role === "tool_result") {
1200
1349
  renderToolResultMessage(event.message, (line, toolUseId) => {
1201
1350
  const liveLineId = this.liveToolLineIds.get(toolUseId);
1202
- if (liveLineId === undefined)
1351
+ if (liveLineId === undefined) {
1352
+ const existing = this.lines.find((item) => item.toolUseId === toolUseId);
1353
+ if (existing) {
1354
+ this.replaceLine(existing.id, { ...line, toolStream: existing.toolStream, presentationLevel: mergePresentationLevel(line.presentationLevel, existing.presentationLevel), toolDisplay: mergeToolDisplays(line.toolDisplay, existing.toolDisplay) });
1355
+ return existing.id;
1356
+ }
1203
1357
  return this.append(line);
1358
+ }
1204
1359
  const liveLine = this.lines.find((item) => item.id === liveLineId);
1205
- this.replaceLine(liveLineId, { ...line, presentationLevel: mergePresentationLevel(line.presentationLevel, liveLine?.presentationLevel), toolDisplay: mergeToolDisplays(line.toolDisplay, liveLine?.toolDisplay) });
1360
+ this.replaceLine(liveLineId, { ...line, toolStream: liveLine?.toolStream, presentationLevel: mergePresentationLevel(line.presentationLevel, liveLine?.presentationLevel), toolDisplay: mergeToolDisplays(line.toolDisplay, liveLine?.toolDisplay) });
1206
1361
  this.liveToolLineIds.delete(toolUseId);
1207
1362
  return liveLineId;
1208
1363
  }, runtimeToolResultPresenter(this.runtime));
@@ -1233,10 +1388,8 @@ export class WebRepl {
1233
1388
  }
1234
1389
  if (event.type === "tool.finished") {
1235
1390
  const id = this.liveToolLineIds.get(event.toolUse.id);
1236
- if (id !== undefined) {
1391
+ if (id !== undefined)
1237
1392
  this.finalizeLiveLine(id);
1238
- this.liveToolLineIds.delete(event.toolUse.id);
1239
- }
1240
1393
  return;
1241
1394
  }
1242
1395
  if (event.type === "terminal") {
@@ -1500,6 +1653,7 @@ export class WebRepl {
1500
1653
  return createWebRuntimeContextPayload(snapshot, {
1501
1654
  revision,
1502
1655
  sessionId: this.runtime.engine.snapshot().session?.sessionId,
1656
+ toolPresentations: Object.fromEntries((this.runtime.toolSupport?.catalog ?? []).map((tool) => [tool.name, tool.presentation])),
1503
1657
  });
1504
1658
  }
1505
1659
  publishRuntimeContext(targets) {
@@ -1971,6 +2125,71 @@ function pushTextBlock(blocks, text) {
1971
2125
  else
1972
2126
  blocks.push({ type: "text", text });
1973
2127
  }
2128
+ function inferToolStreamChannel(data) {
2129
+ if (data && typeof data === "object" && !Array.isArray(data) && data.type === "terminal.output.delta") {
2130
+ return data.stream === "stderr" ? "stderr" : "stdout";
2131
+ }
2132
+ return "state";
2133
+ }
2134
+ function terminalOutputDelta(data) {
2135
+ if (!data || typeof data !== "object" || Array.isArray(data))
2136
+ return undefined;
2137
+ const record = data;
2138
+ if (record.type !== "terminal.output.delta" || typeof record.text !== "string")
2139
+ return undefined;
2140
+ return { stream: record.stream === "stderr" ? "stderr" : "stdout", text: record.text };
2141
+ }
2142
+ function toolStreamTextDelta(data) {
2143
+ if (typeof data === "string")
2144
+ return data;
2145
+ if (!data || typeof data !== "object" || Array.isArray(data))
2146
+ return undefined;
2147
+ const record = data;
2148
+ for (const value of [record.text, record.delta, record.patch, record.content]) {
2149
+ if (typeof value === "string")
2150
+ return value;
2151
+ }
2152
+ return undefined;
2153
+ }
2154
+ function childToolName(data) {
2155
+ if (!data || typeof data !== "object" || Array.isArray(data))
2156
+ return undefined;
2157
+ const childEvent = data.child_event;
2158
+ if (!childEvent || typeof childEvent !== "object" || Array.isArray(childEvent))
2159
+ return undefined;
2160
+ const toolUse = childEvent.toolUse;
2161
+ if (!toolUse || typeof toolUse !== "object" || Array.isArray(toolUse))
2162
+ return undefined;
2163
+ return stringValue(toolUse.name);
2164
+ }
2165
+ function appendToolStreamChunk(chunks, sequence, stream, text, operation) {
2166
+ const next = operation === "replace" ? [] : [...(chunks ?? [])];
2167
+ next.push({ sequence: sequence ?? (next.at(-1)?.sequence ?? 0) + 1, stream, text });
2168
+ let total = 0;
2169
+ const retained = [];
2170
+ for (let index = next.length - 1; index >= 0; index -= 1) {
2171
+ const chunk = next[index];
2172
+ if (total >= 40_000)
2173
+ break;
2174
+ retained.push(chunk);
2175
+ total += chunk.text.length;
2176
+ }
2177
+ return retained.reverse();
2178
+ }
2179
+ function trimToolStreamText(value, maxChars) {
2180
+ return value.length <= maxChars ? value : value.slice(value.length - maxChars);
2181
+ }
2182
+ function appendToolStreamText(current, delta, maxChars) {
2183
+ return trimToolStreamText(`${current ?? ""}${delta}`, maxChars);
2184
+ }
2185
+ function toolStreamStepStatus(phase) {
2186
+ const normalized = phase?.toLowerCase() ?? "";
2187
+ if (normalized.includes("fail") || normalized.includes("error") || normalized.includes("cancel") || normalized.includes("abort"))
2188
+ return "failed";
2189
+ if (normalized.includes("complete") || normalized.includes("finish") || normalized.includes("done") || normalized.includes("success"))
2190
+ return "completed";
2191
+ return "running";
2192
+ }
1974
2193
  function reduceStatus(status, event) {
1975
2194
  if (event.type === "state")
1976
2195
  return { ...status, phase: event.phase, detail: event.detail, currentTool: event.phase === "running_tools" ? status.currentTool : undefined, usage: event.phase === "preparing" ? undefined : status.usage, streamedOutputTokens: event.phase === "preparing" ? 0 : status.streamedOutputTokens, inputTokenUpdatedAt: event.phase === "preparing" ? undefined : status.inputTokenUpdatedAt, outputTokenUpdatedAt: event.phase === "preparing" ? undefined : status.outputTokenUpdatedAt, retryCooldownUntil: event.phase === "preparing" ? undefined : status.retryCooldownUntil, activityTick: status.activityTick + 1 };
@@ -1990,8 +2209,10 @@ function reduceStatus(status, event) {
1990
2209
  return { ...status, phase: "calling_model", detail: `retrying in ${(event.delayMs / 1000).toFixed(1)}s`, retryCooldownUntil: Date.now() + event.delayMs, activityTick: status.activityTick + 1 };
1991
2210
  if (event.type === "tool.started")
1992
2211
  return { ...status, phase: "running_tools", currentTool: { id: event.toolUse.id, name: event.toolUse.name, input: event.toolUse.input, startedAt: Date.now() }, activityTick: status.activityTick + 1 };
1993
- if (event.type === "tool.finished")
1994
- return { ...status, currentTool: status.currentTool?.id === event.toolUse.id ? undefined : status.currentTool, activityTick: status.activityTick + 1 };
2212
+ if (event.type === "tool.progress")
2213
+ return { ...status, phase: "running_tools", detail: event.progress.message || status.detail, currentTool: { id: event.toolUse.id, name: event.toolUse.name, input: event.toolUse.input, startedAt: status.currentTool?.id === event.toolUse.id ? status.currentTool.startedAt : Date.now() }, activityTick: status.activityTick + 1 };
2214
+ if (event.type === "tool.result.available" || event.type === "tool.finished" || event.type === "tool.batch.completed")
2215
+ return { ...status, currentTool: "toolUse" in event && status.currentTool?.id === event.toolUse.id ? undefined : status.currentTool, activityTick: status.activityTick + 1 };
1995
2216
  if (event.type === "terminal")
1996
2217
  return { ...status, phase: "stopped", detail: event.reason, currentTool: undefined, inputTokenUpdatedAt: undefined, outputTokenUpdatedAt: undefined, retryCooldownUntil: undefined, activityTick: status.activityTick + 1 };
1997
2218
  if (event.type === "message" || event.type === "error")
@@ -2490,13 +2711,14 @@ function thinkingLine(text, live = false) {
2490
2711
  return { kind: "thinking", title: titleForKind("thinking"), text, previewStyle: "summary", summaryMaxLines: THINKING_SUMMARY_MAX_LINES, live };
2491
2712
  }
2492
2713
  function formatToolUse(toolUse) {
2714
+ const toolPresentation = toolPresentationForSource(toolUse.name);
2493
2715
  const presentationLevel = toolPresentationLevel(toolUse.name, toolUse.input);
2494
- if (toolUse.name === "plan" && isWebPlanPayload(toolUse.input))
2495
- return { kind: "tool", toolName: toolUse.name, title: toolTitle(toolUse.name, "running"), bodyTitle: webPlanBodyTitle(toolUse.input), text: serializeWebPlanPayload(toolUse.input), presentationLevel, collapsible: true };
2496
- if (toolUse.name === "image2")
2497
- return { kind: "tool", toolName: toolUse.name, toolUseId: toolUse.id, title: toolTitle(toolUse.name, "running"), bodyTitle: "图片模型处理中", text: JSON.stringify(toolUse.input ?? {}, null, 2), format: "plain", previewStyle: "summary", presentationLevel: "primary", collapsible: true };
2716
+ if (toolUse.name === "plan_update" && isWebPlanPayload(toolUse.input))
2717
+ return { kind: "tool", toolName: toolUse.name, toolPresentation, title: toolPresentation.label, bodyTitle: webPlanBodyTitle(toolUse.input), text: serializeWebPlanPayload(toolUse.input), presentationLevel, collapsible: true };
2718
+ if (toolUse.name === "image_create")
2719
+ return { kind: "tool", toolName: toolUse.name, toolPresentation, toolUseId: toolUse.id, title: toolPresentation.label, bodyTitle: "图片模型处理中", text: JSON.stringify(toolUse.input ?? {}, null, 2), format: "plain", previewStyle: "summary", presentationLevel: "primary", collapsible: true };
2498
2720
  const summary = summarizeToolUse(toolUse.name, toolUse.input);
2499
- return { kind: "tool", toolName: toolUse.name, toolUseId: toolUse.id, title: toolTitle(toolUse.name, "running"), bodyTitle: summary.bodyTitle, text: summary.text, toolDisplay: buildToolUseDisplay(toolUse.name, toolUse.input), previewStyle: "summary", presentationLevel, collapsible: true };
2721
+ return { kind: "tool", toolName: toolUse.name, toolPresentation, toolUseId: toolUse.id, title: toolPresentation.label, bodyTitle: summary.bodyTitle, text: summary.text, toolDisplay: buildToolUseDisplay(toolUse.name, toolUse.input), previewStyle: "summary", presentationLevel, collapsible: true };
2500
2722
  }
2501
2723
  function formatToolResultLine(toolName, output, ok, presentToolResult) {
2502
2724
  const presentation = normalizeToolResultPresentation(presentToolResult?.(toolName, output, ok));
@@ -2507,6 +2729,7 @@ function formatToolResultLine(toolName, output, ok, presentToolResult) {
2507
2729
  return {
2508
2730
  kind: ok ? "tool" : "error",
2509
2731
  toolName,
2732
+ toolPresentation: toolPresentationForSource(toolName),
2510
2733
  title: presentation?.title ?? toolTitle(toolName, "finished"),
2511
2734
  bodyTitle: presentation?.bodyTitle ?? formatted.bodyTitle,
2512
2735
  titleStatus: ok ? "success" : "failure",
@@ -2522,7 +2745,7 @@ function formatToolResultLine(toolName, output, ok, presentToolResult) {
2522
2745
  };
2523
2746
  }
2524
2747
  function toolPresentationLevel(toolName, payload) {
2525
- if (toolName === "image2")
2748
+ if (toolName === "image_create")
2526
2749
  return "primary";
2527
2750
  if (!isRecord(payload))
2528
2751
  return "process";
@@ -2616,40 +2839,7 @@ function safeResourceUrl(value) {
2616
2839
  return /^(?:https?:\/\/|\/)/i.test(value) && !/^\/\//.test(value);
2617
2840
  }
2618
2841
  function toolTitle(toolName, _phase) {
2619
- const labels = {
2620
- agent: "子任务",
2621
- edit: "编辑文件",
2622
- exec_command: "执行命令",
2623
- write_stdin: "终端交互",
2624
- expose_downloads: "文件下载",
2625
- grep: "搜索文本",
2626
- image2: "图片生成",
2627
- image_note: "记录图片",
2628
- list: "列出文件",
2629
- load_image: "读取图片",
2630
- plan: "任务计划",
2631
- read: "读取文件",
2632
- search: "网络搜索",
2633
- write: "写入文件",
2634
- SendMessage: "发送协作消息",
2635
- sendmessage: "发送协作消息",
2636
- TaskGet: "读取后台任务",
2637
- taskget: "读取后台任务",
2638
- TaskList: "后台任务列表",
2639
- tasklist: "后台任务列表",
2640
- TaskOutput: "读取任务输出",
2641
- taskoutput: "读取任务输出",
2642
- TaskResume: "继续后台任务",
2643
- taskresume: "继续后台任务",
2644
- TaskStop: "停止后台任务",
2645
- taskstop: "停止后台任务",
2646
- multi_tool_use: "并行执行工具",
2647
- secret_list: "密钥列表",
2648
- secret_info: "查看密钥",
2649
- secret_request: "申请密钥",
2650
- agent_report: "子任务报告",
2651
- };
2652
- return labels[toolName] ?? toolName;
2842
+ return builtinToolPresentation(toolName)?.label ?? toolName;
2653
2843
  }
2654
2844
  function summarizeToolUse(toolName, input) {
2655
2845
  const purpose = toolUsePurpose(toolName, input);
@@ -2664,17 +2854,30 @@ function toolUsePurpose(toolName, input) {
2664
2854
  const description = typeof input.description === "string" ? input.description.trim() : "";
2665
2855
  if (description)
2666
2856
  return description;
2857
+ const taskId = stringValue(input.task_id);
2858
+ if (toolName === "subagent_output")
2859
+ return taskId ? `读取子任务 ${taskId} 的输出` : "读取子任务输出";
2860
+ if (toolName === "subagent_list")
2861
+ return "查看子任务状态";
2862
+ if (toolName === "subagent_get")
2863
+ return taskId ? `查看子任务 ${taskId}` : "查看子任务";
2864
+ if (toolName === "subagent_stop")
2865
+ return taskId ? `停止子任务 ${taskId}` : "停止子任务";
2866
+ if (toolName === "subagent_resume")
2867
+ return taskId ? `恢复子任务 ${taskId}` : "恢复子任务";
2868
+ if (toolName === "subagent_message")
2869
+ return stringValue(input.target) ? `向 ${stringValue(input.target)} 发送消息` : "发送子任务消息";
2667
2870
  if (toolName === "expose_downloads")
2668
2871
  return "准备网页下载链接";
2669
- if (toolName === "read" && typeof input.path === "string")
2872
+ if (toolName === "file_read" && typeof input.path === "string")
2670
2873
  return `读取 ${path.basename(input.path)}`;
2671
- if (toolName === "list" && typeof input.path === "string")
2874
+ if (toolName === "file_list" && typeof input.path === "string")
2672
2875
  return `查看 ${input.path}`;
2673
- if (toolName === "grep" && typeof input.query === "string")
2876
+ if (toolName === "file_search" && typeof input.query === "string")
2674
2877
  return `搜索 ${input.query}`;
2675
- if (toolName === "write" && typeof input.path === "string")
2878
+ if (toolName === "file_write" && typeof input.path === "string")
2676
2879
  return `写入 ${path.basename(input.path)}`;
2677
- if (toolName === "edit" && typeof input.path === "string")
2880
+ if (toolName === "file_edit" && typeof input.path === "string")
2678
2881
  return `修改 ${path.basename(input.path)}`;
2679
2882
  return undefined;
2680
2883
  }
@@ -2730,35 +2933,35 @@ function normalizeDisplayText(value) {
2730
2933
  }
2731
2934
  function buildToolUseDisplay(toolName, input) {
2732
2935
  const data = isRecord(input) ? input : {};
2733
- const purpose = stringValue(data.description);
2734
- const subject = toolName === "exec_command" ? undefined : toolDisplaySubject(toolName, data);
2936
+ const purpose = toolUsePurpose(toolName, input);
2937
+ const subject = toolName === "terminal_run" ? undefined : toolDisplaySubject(toolName, data);
2735
2938
  const facts = [];
2736
2939
  const previews = [];
2737
- if (toolName === "exec_command")
2940
+ if (toolName === "terminal_run")
2738
2941
  pushToolPreview(previews, "命令", "code", data.cmd);
2739
- else if (toolName === "write_stdin") {
2942
+ else if (toolName === "terminal_control") {
2740
2943
  pushToolFact(facts, "会话", data.session_id, true);
2741
2944
  pushToolFact(facts, "输入", data.chars, true);
2742
2945
  }
2743
- else if (toolName === "grep")
2946
+ else if (toolName === "file_search")
2744
2947
  pushToolFact(facts, "范围", data.path, true);
2745
- else if (toolName === "load_image")
2948
+ else if (toolName === "image_inspect")
2746
2949
  pushToolFact(facts, "图片", arrayLabel(data.imageRefs), true);
2747
- else if (toolName === "image2") {
2950
+ else if (toolName === "image_create") {
2748
2951
  pushToolFact(facts, "模式", data.mode === "edit" ? "编辑" : "生成");
2749
2952
  pushToolFact(facts, "名称", data.semanticName);
2750
2953
  }
2751
2954
  else if (toolName === "expose_downloads")
2752
2955
  pushToolFact(facts, "文件", arrayLabel(data.paths), true);
2753
- else if (toolName === "SendMessage" || toolName.toLowerCase() === "sendmessage")
2956
+ else if (toolName === "subagent_message")
2754
2957
  pushToolFact(facts, "接收方", data.target);
2755
- else if (toolName.toLowerCase().startsWith("task"))
2958
+ else if (toolName.startsWith("subagent_"))
2756
2959
  pushToolFact(facts, "任务", data.task_id, true);
2757
2960
  else if (toolName.startsWith("secret_")) {
2758
2961
  pushToolFact(facts, "密钥", data.key, true);
2759
2962
  pushToolFact(facts, "用途", data.reason);
2760
2963
  }
2761
- else if (toolName === "agent") {
2964
+ else if (toolName === "subagent_run") {
2762
2965
  pushToolFact(facts, "任务", data.description ?? data.prompt);
2763
2966
  pushToolFact(facts, "类型", data.subagent_type ?? data.mode);
2764
2967
  }
@@ -2770,7 +2973,7 @@ function buildToolResultDisplay(toolName, output, ok) {
2770
2973
  const previews = [];
2771
2974
  let subject = toolDisplaySubject(toolName, data);
2772
2975
  let purpose = stringValue(data.description);
2773
- if ((toolName === "edit" || toolName === "write") && isEditToolOutput(data)) {
2976
+ if ((toolName === "file_edit" || toolName === "file_write") && isEditToolOutput(data)) {
2774
2977
  subject = data.path;
2775
2978
  pushToolPreview(previews, undefined, "diff", compactEditDiff(data));
2776
2979
  }
@@ -2782,43 +2985,43 @@ function buildToolResultDisplay(toolName, output, ok) {
2782
2985
  if (outputText)
2783
2986
  pushToolPreview(previews, data.stderr ? "输出 / 错误" : "输出", "code", outputText);
2784
2987
  if (data.timed_out && !outputText)
2785
- pushToolPreview(previews, undefined, "text", "命令执行超时");
2988
+ pushToolFact(facts, "状态", "命令执行超时", false, "warning");
2786
2989
  else if ((!ok || (typeof data.exit_code === "number" && data.exit_code !== 0)) && !outputText)
2787
- pushToolPreview(previews, undefined, "text", `命令退出码:${data.exit_code ?? "未知"}`);
2990
+ pushToolFact(facts, "状态", `退出码 ${data.exit_code ?? "未知"}`, false, "danger");
2788
2991
  }
2789
- else if (toolName === "grep") {
2992
+ else if (toolName === "file_search") {
2790
2993
  subject = stringValue(data.query) || stringValue(data.grepPath) || stringValue(data.path);
2791
2994
  pushToolPreview(previews, undefined, "list", grepPreview(data));
2792
2995
  }
2793
- else if (toolName === "search") {
2996
+ else if (toolName === "web_search") {
2794
2997
  subject = stringValue(data.query);
2795
2998
  pushToolPreview(previews, undefined, "list", searchPreview(data));
2796
2999
  }
2797
- else if (toolName === "read") {
3000
+ else if (toolName === "file_read") {
2798
3001
  subject = stringValue(data.path);
2799
3002
  if (numberValue(data.startLine) !== undefined && numberValue(data.endLine) !== undefined)
2800
3003
  pushToolFact(facts, "范围", `${data.startLine}–${data.endLine} 行`);
2801
3004
  pushToolPreview(previews, undefined, "code", data.content);
2802
3005
  }
2803
- else if (toolName === "list") {
3006
+ else if (toolName === "file_list") {
2804
3007
  subject = stringValue(data.path);
2805
3008
  pushToolPreview(previews, undefined, "list", listPreview(data));
2806
3009
  }
2807
- else if (toolName === "image2") {
3010
+ else if (toolName === "image_create") {
2808
3011
  subject = stringValue(data.semanticName);
2809
3012
  pushToolFact(facts, "图片", `${numberValue(data.returnedImages) ?? arrayLength(data.images)} 张`);
2810
3013
  pushToolFact(facts, "尺寸", data.size);
2811
3014
  }
2812
- else if (toolName === "load_image") {
3015
+ else if (toolName === "image_inspect") {
2813
3016
  subject = arrayLabel(data.imageRefs);
2814
3017
  }
2815
3018
  else if (toolResources(output)?.length) {
2816
3019
  subject = toolResources(output)?.map((item) => item.label || item.downloadName).filter(Boolean).join("、");
2817
3020
  }
2818
- else if (toolName.toLowerCase().startsWith("task") || toolName === "agent") {
3021
+ else if (toolName.startsWith("subagent_") || toolName === "subagent_run") {
2819
3022
  subject = stringValue(data.description) || stringValue(data.task_id) || stringValue(data.agent_id);
2820
3023
  }
2821
- else if (toolName === "SendMessage" || toolName.toLowerCase() === "sendmessage") {
3024
+ else if (toolName === "subagent_message") {
2822
3025
  subject = stringValue(data.target);
2823
3026
  }
2824
3027
  else if (toolName.startsWith("secret_")) {
@@ -2827,7 +3030,7 @@ function buildToolResultDisplay(toolName, output, ok) {
2827
3030
  if (toolName === "secret_list")
2828
3031
  pushToolFact(facts, "数量", `${arrayLength(data.secrets)} 项`);
2829
3032
  }
2830
- else if (toolName === "agent_report") {
3033
+ else if (toolName === "subagent_report") {
2831
3034
  }
2832
3035
  else {
2833
3036
  subject = subject || stringValue(data.path) || stringValue(data.id) || stringValue(data.name);
@@ -2835,17 +3038,17 @@ function buildToolResultDisplay(toolName, output, ok) {
2835
3038
  return normalizeToolDisplay({ subject, purpose, facts, previews });
2836
3039
  }
2837
3040
  function toolDisplaySubject(toolName, data) {
2838
- if (["read", "write", "edit", "list"].includes(toolName))
3041
+ if (["file_read", "file_write", "file_edit", "file_list"].includes(toolName))
2839
3042
  return stringValue(data.path);
2840
- if (toolName === "exec_command")
3043
+ if (toolName === "terminal_run")
2841
3044
  return stringValue(data.cmd) || stringValue(data.command);
2842
- if (toolName === "grep" || toolName === "search")
3045
+ if (toolName === "file_search" || toolName === "web_search")
2843
3046
  return stringValue(data.query);
2844
- if (toolName === "image2")
3047
+ if (toolName === "image_create")
2845
3048
  return stringValue(data.semanticName);
2846
- if (toolName === "SendMessage" || toolName.toLowerCase() === "sendmessage")
3049
+ if (toolName === "subagent_message")
2847
3050
  return stringValue(data.target);
2848
- if (toolName.toLowerCase().startsWith("task"))
3051
+ if (toolName.startsWith("subagent_"))
2849
3052
  return stringValue(data.task_id);
2850
3053
  return undefined;
2851
3054
  }
@@ -2926,24 +3129,24 @@ function taskStatusDisplay(value, ok) {
2926
3129
  return status ? labels[status] ?? status : ok ? "已完成" : "失败";
2927
3130
  }
2928
3131
  function formatToolResult(toolName, output, ok) {
2929
- if ((toolName === "edit" || toolName === "write") && isRecord(output) && isEditToolOutput(output))
3132
+ if ((toolName === "file_edit" || toolName === "file_write") && isRecord(output) && isEditToolOutput(output))
2930
3133
  return { text: formatEditToolDiff(output, ok), format: "diff", summaryMaxLines: EDIT_TOOL_SUMMARY_MAX_LINES };
2931
3134
  if (isExecOutput(output))
2932
3135
  return { text: formatExecToolResult(output, ok), format: "plain", summaryMaxLines: EXPANDED_SUMMARY_MAX_LINES };
2933
- if (toolName === "list" && isRecord(output))
3136
+ if (toolName === "file_list" && isRecord(output))
2934
3137
  return { text: formatListToolResult(output, ok) };
2935
- if (toolName === "read" && isRecord(output))
3138
+ if (toolName === "file_read" && isRecord(output))
2936
3139
  return { text: formatReadToolResult(output, ok) };
2937
- if (toolName === "grep" && isRecord(output))
3140
+ if (toolName === "file_search" && isRecord(output))
2938
3141
  return { text: formatGrepToolResult(output, ok) };
2939
- if (toolName === "search" && isRecord(output))
3142
+ if (toolName === "web_search" && isRecord(output))
2940
3143
  return { text: formatWebSearchToolResult(output, ok), summaryMaxLines: EXPANDED_SUMMARY_MAX_LINES };
2941
- if (toolName === "image2" && isRecord(output))
3144
+ if (toolName === "image_create" && isRecord(output))
2942
3145
  return { text: formatImageGenerationToolResult(output, ok), format: "plain", summaryMaxLines: 4 };
2943
3146
  const resources = toolResources(output);
2944
3147
  if (resources?.length)
2945
3148
  return { text: ok ? "资源已准备好。" : "资源准备失败。", full: true, bodyTitle: ok ? "资源已就绪" : "资源准备失败" };
2946
- if (toolName === "plan" && isWebPlanPayload(output))
3149
+ if (toolName === "plan_update" && isWebPlanPayload(output))
2947
3150
  return { text: serializeWebPlanPayload(output), full: true, bodyTitle: webPlanBodyTitle(output) };
2948
3151
  if (typeof output === "string")
2949
3152
  return { text: output, format: hasAnsi(output) ? "ansi" : undefined, summaryMaxLines: EXPANDED_SUMMARY_MAX_LINES };