neoctl 0.2.31 → 0.2.32

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 (99) 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 +97 -18
  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 +88 -6
  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 +14 -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.js +24 -6
  52. package/dist/tools/builtins/edit-tool.js.map +1 -1
  53. package/dist/tools/builtins/exec-process-manager.d.ts +4 -0
  54. package/dist/tools/builtins/exec-process-manager.js +15 -6
  55. package/dist/tools/builtins/exec-process-manager.js.map +1 -1
  56. package/dist/tools/builtins/exec-tool.js +24 -8
  57. package/dist/tools/builtins/exec-tool.js.map +1 -1
  58. package/dist/tools/builtins/filesystem-tools.js +2 -4
  59. package/dist/tools/builtins/filesystem-tools.js.map +1 -1
  60. package/dist/tools/builtins/grep-tool.js +2 -3
  61. package/dist/tools/builtins/grep-tool.js.map +1 -1
  62. package/dist/tools/builtins/image-generation-tool.js +4 -4
  63. package/dist/tools/builtins/image-generation-tool.js.map +1 -1
  64. package/dist/tools/builtins/image-loader-tool.js +2 -3
  65. package/dist/tools/builtins/image-loader-tool.js.map +1 -1
  66. package/dist/tools/builtins/image-note-tool.js +0 -1
  67. package/dist/tools/builtins/image-note-tool.js.map +1 -1
  68. package/dist/tools/builtins/plan-tool.js +1 -1
  69. package/dist/tools/builtins/plan-tool.js.map +1 -1
  70. package/dist/tools/builtins/search-tool.js +3 -3
  71. package/dist/tools/builtins/search-tool.js.map +1 -1
  72. package/dist/tools/run-tool-use.js +3 -2
  73. package/dist/tools/run-tool-use.js.map +1 -1
  74. package/dist/tools/smoke-tool-system.js +44 -25
  75. package/dist/tools/smoke-tool-system.js.map +1 -1
  76. package/dist/tools/tool-catalog.d.ts +36 -0
  77. package/dist/tools/tool-catalog.js +64 -0
  78. package/dist/tools/tool-catalog.js.map +1 -0
  79. package/dist/tools/tool-orchestration.d.ts +20 -3
  80. package/dist/tools/tool-orchestration.js +29 -4
  81. package/dist/tools/tool-orchestration.js.map +1 -1
  82. package/dist/tools/tool.d.ts +13 -0
  83. package/dist/tools/tool.js.map +1 -1
  84. package/dist/types/events.d.ts +23 -0
  85. package/dist/web/index.d.ts +39 -0
  86. package/dist/web/index.js +293 -92
  87. package/dist/web/index.js.map +1 -1
  88. package/dist/web/runtime-context-protocol.d.ts +7 -0
  89. package/dist/web/runtime-context-protocol.js +3 -2
  90. package/dist/web/runtime-context-protocol.js.map +1 -1
  91. package/dist/web/smoke-runtime-context-protocol.js +10 -2
  92. package/dist/web/smoke-runtime-context-protocol.js.map +1 -1
  93. package/dist/web/smoke-web-history.js +59 -1
  94. package/dist/web/smoke-web-history.js.map +1 -1
  95. package/dist/web/smoke-web-terminal-tasks.js +1 -0
  96. package/dist/web/smoke-web-terminal-tasks.js.map +1 -1
  97. package/package.json +4 -4
  98. package/dist/tasks/task-tools.d.ts +0 -31
  99. 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,6 +1304,18 @@ 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") {
1173
1320
  let replacedStreamingContent = false;
1174
1321
  if (event.message.role === "assistant" && this.assistantLineId !== undefined) {
@@ -1199,10 +1346,16 @@ export class WebRepl {
1199
1346
  if (event.message.role === "tool_result") {
1200
1347
  renderToolResultMessage(event.message, (line, toolUseId) => {
1201
1348
  const liveLineId = this.liveToolLineIds.get(toolUseId);
1202
- if (liveLineId === undefined)
1349
+ if (liveLineId === undefined) {
1350
+ const existing = this.lines.find((item) => item.toolUseId === toolUseId);
1351
+ if (existing) {
1352
+ this.replaceLine(existing.id, { ...line, toolStream: existing.toolStream, presentationLevel: mergePresentationLevel(line.presentationLevel, existing.presentationLevel), toolDisplay: mergeToolDisplays(line.toolDisplay, existing.toolDisplay) });
1353
+ return existing.id;
1354
+ }
1203
1355
  return this.append(line);
1356
+ }
1204
1357
  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) });
1358
+ this.replaceLine(liveLineId, { ...line, toolStream: liveLine?.toolStream, presentationLevel: mergePresentationLevel(line.presentationLevel, liveLine?.presentationLevel), toolDisplay: mergeToolDisplays(line.toolDisplay, liveLine?.toolDisplay) });
1206
1359
  this.liveToolLineIds.delete(toolUseId);
1207
1360
  return liveLineId;
1208
1361
  }, runtimeToolResultPresenter(this.runtime));
@@ -1233,10 +1386,8 @@ export class WebRepl {
1233
1386
  }
1234
1387
  if (event.type === "tool.finished") {
1235
1388
  const id = this.liveToolLineIds.get(event.toolUse.id);
1236
- if (id !== undefined) {
1389
+ if (id !== undefined)
1237
1390
  this.finalizeLiveLine(id);
1238
- this.liveToolLineIds.delete(event.toolUse.id);
1239
- }
1240
1391
  return;
1241
1392
  }
1242
1393
  if (event.type === "terminal") {
@@ -1500,6 +1651,7 @@ export class WebRepl {
1500
1651
  return createWebRuntimeContextPayload(snapshot, {
1501
1652
  revision,
1502
1653
  sessionId: this.runtime.engine.snapshot().session?.sessionId,
1654
+ toolPresentations: Object.fromEntries((this.runtime.toolSupport?.catalog ?? []).map((tool) => [tool.name, tool.presentation])),
1503
1655
  });
1504
1656
  }
1505
1657
  publishRuntimeContext(targets) {
@@ -1971,6 +2123,71 @@ function pushTextBlock(blocks, text) {
1971
2123
  else
1972
2124
  blocks.push({ type: "text", text });
1973
2125
  }
2126
+ function inferToolStreamChannel(data) {
2127
+ if (data && typeof data === "object" && !Array.isArray(data) && data.type === "terminal.output.delta") {
2128
+ return data.stream === "stderr" ? "stderr" : "stdout";
2129
+ }
2130
+ return "state";
2131
+ }
2132
+ function terminalOutputDelta(data) {
2133
+ if (!data || typeof data !== "object" || Array.isArray(data))
2134
+ return undefined;
2135
+ const record = data;
2136
+ if (record.type !== "terminal.output.delta" || typeof record.text !== "string")
2137
+ return undefined;
2138
+ return { stream: record.stream === "stderr" ? "stderr" : "stdout", text: record.text };
2139
+ }
2140
+ function toolStreamTextDelta(data) {
2141
+ if (typeof data === "string")
2142
+ return data;
2143
+ if (!data || typeof data !== "object" || Array.isArray(data))
2144
+ return undefined;
2145
+ const record = data;
2146
+ for (const value of [record.text, record.delta, record.patch, record.content]) {
2147
+ if (typeof value === "string")
2148
+ return value;
2149
+ }
2150
+ return undefined;
2151
+ }
2152
+ function childToolName(data) {
2153
+ if (!data || typeof data !== "object" || Array.isArray(data))
2154
+ return undefined;
2155
+ const childEvent = data.child_event;
2156
+ if (!childEvent || typeof childEvent !== "object" || Array.isArray(childEvent))
2157
+ return undefined;
2158
+ const toolUse = childEvent.toolUse;
2159
+ if (!toolUse || typeof toolUse !== "object" || Array.isArray(toolUse))
2160
+ return undefined;
2161
+ return stringValue(toolUse.name);
2162
+ }
2163
+ function appendToolStreamChunk(chunks, sequence, stream, text, operation) {
2164
+ const next = operation === "replace" ? [] : [...(chunks ?? [])];
2165
+ next.push({ sequence: sequence ?? (next.at(-1)?.sequence ?? 0) + 1, stream, text });
2166
+ let total = 0;
2167
+ const retained = [];
2168
+ for (let index = next.length - 1; index >= 0; index -= 1) {
2169
+ const chunk = next[index];
2170
+ if (total >= 40_000)
2171
+ break;
2172
+ retained.push(chunk);
2173
+ total += chunk.text.length;
2174
+ }
2175
+ return retained.reverse();
2176
+ }
2177
+ function trimToolStreamText(value, maxChars) {
2178
+ return value.length <= maxChars ? value : value.slice(value.length - maxChars);
2179
+ }
2180
+ function appendToolStreamText(current, delta, maxChars) {
2181
+ return trimToolStreamText(`${current ?? ""}${delta}`, maxChars);
2182
+ }
2183
+ function toolStreamStepStatus(phase) {
2184
+ const normalized = phase?.toLowerCase() ?? "";
2185
+ if (normalized.includes("fail") || normalized.includes("error") || normalized.includes("cancel") || normalized.includes("abort"))
2186
+ return "failed";
2187
+ if (normalized.includes("complete") || normalized.includes("finish") || normalized.includes("done") || normalized.includes("success"))
2188
+ return "completed";
2189
+ return "running";
2190
+ }
1974
2191
  function reduceStatus(status, event) {
1975
2192
  if (event.type === "state")
1976
2193
  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 +2207,10 @@ function reduceStatus(status, event) {
1990
2207
  return { ...status, phase: "calling_model", detail: `retrying in ${(event.delayMs / 1000).toFixed(1)}s`, retryCooldownUntil: Date.now() + event.delayMs, activityTick: status.activityTick + 1 };
1991
2208
  if (event.type === "tool.started")
1992
2209
  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 };
2210
+ if (event.type === "tool.progress")
2211
+ 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 };
2212
+ if (event.type === "tool.result.available" || event.type === "tool.finished" || event.type === "tool.batch.completed")
2213
+ return { ...status, currentTool: "toolUse" in event && status.currentTool?.id === event.toolUse.id ? undefined : status.currentTool, activityTick: status.activityTick + 1 };
1995
2214
  if (event.type === "terminal")
1996
2215
  return { ...status, phase: "stopped", detail: event.reason, currentTool: undefined, inputTokenUpdatedAt: undefined, outputTokenUpdatedAt: undefined, retryCooldownUntil: undefined, activityTick: status.activityTick + 1 };
1997
2216
  if (event.type === "message" || event.type === "error")
@@ -2490,13 +2709,14 @@ function thinkingLine(text, live = false) {
2490
2709
  return { kind: "thinking", title: titleForKind("thinking"), text, previewStyle: "summary", summaryMaxLines: THINKING_SUMMARY_MAX_LINES, live };
2491
2710
  }
2492
2711
  function formatToolUse(toolUse) {
2712
+ const toolPresentation = toolPresentationForSource(toolUse.name);
2493
2713
  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 };
2714
+ if (toolUse.name === "plan_update" && isWebPlanPayload(toolUse.input))
2715
+ return { kind: "tool", toolName: toolUse.name, toolPresentation, title: toolPresentation.label, bodyTitle: webPlanBodyTitle(toolUse.input), text: serializeWebPlanPayload(toolUse.input), presentationLevel, collapsible: true };
2716
+ if (toolUse.name === "image_create")
2717
+ 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
2718
  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 };
2719
+ 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
2720
  }
2501
2721
  function formatToolResultLine(toolName, output, ok, presentToolResult) {
2502
2722
  const presentation = normalizeToolResultPresentation(presentToolResult?.(toolName, output, ok));
@@ -2507,6 +2727,7 @@ function formatToolResultLine(toolName, output, ok, presentToolResult) {
2507
2727
  return {
2508
2728
  kind: ok ? "tool" : "error",
2509
2729
  toolName,
2730
+ toolPresentation: toolPresentationForSource(toolName),
2510
2731
  title: presentation?.title ?? toolTitle(toolName, "finished"),
2511
2732
  bodyTitle: presentation?.bodyTitle ?? formatted.bodyTitle,
2512
2733
  titleStatus: ok ? "success" : "failure",
@@ -2522,7 +2743,7 @@ function formatToolResultLine(toolName, output, ok, presentToolResult) {
2522
2743
  };
2523
2744
  }
2524
2745
  function toolPresentationLevel(toolName, payload) {
2525
- if (toolName === "image2")
2746
+ if (toolName === "image_create")
2526
2747
  return "primary";
2527
2748
  if (!isRecord(payload))
2528
2749
  return "process";
@@ -2616,40 +2837,7 @@ function safeResourceUrl(value) {
2616
2837
  return /^(?:https?:\/\/|\/)/i.test(value) && !/^\/\//.test(value);
2617
2838
  }
2618
2839
  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;
2840
+ return builtinToolPresentation(toolName)?.label ?? toolName;
2653
2841
  }
2654
2842
  function summarizeToolUse(toolName, input) {
2655
2843
  const purpose = toolUsePurpose(toolName, input);
@@ -2664,17 +2852,30 @@ function toolUsePurpose(toolName, input) {
2664
2852
  const description = typeof input.description === "string" ? input.description.trim() : "";
2665
2853
  if (description)
2666
2854
  return description;
2855
+ const taskId = stringValue(input.task_id);
2856
+ if (toolName === "subagent_output")
2857
+ return taskId ? `读取子任务 ${taskId} 的输出` : "读取子任务输出";
2858
+ if (toolName === "subagent_list")
2859
+ return "查看子任务状态";
2860
+ if (toolName === "subagent_get")
2861
+ return taskId ? `查看子任务 ${taskId}` : "查看子任务";
2862
+ if (toolName === "subagent_stop")
2863
+ return taskId ? `停止子任务 ${taskId}` : "停止子任务";
2864
+ if (toolName === "subagent_resume")
2865
+ return taskId ? `恢复子任务 ${taskId}` : "恢复子任务";
2866
+ if (toolName === "subagent_message")
2867
+ return stringValue(input.target) ? `向 ${stringValue(input.target)} 发送消息` : "发送子任务消息";
2667
2868
  if (toolName === "expose_downloads")
2668
2869
  return "准备网页下载链接";
2669
- if (toolName === "read" && typeof input.path === "string")
2870
+ if (toolName === "file_read" && typeof input.path === "string")
2670
2871
  return `读取 ${path.basename(input.path)}`;
2671
- if (toolName === "list" && typeof input.path === "string")
2872
+ if (toolName === "file_list" && typeof input.path === "string")
2672
2873
  return `查看 ${input.path}`;
2673
- if (toolName === "grep" && typeof input.query === "string")
2874
+ if (toolName === "file_search" && typeof input.query === "string")
2674
2875
  return `搜索 ${input.query}`;
2675
- if (toolName === "write" && typeof input.path === "string")
2876
+ if (toolName === "file_write" && typeof input.path === "string")
2676
2877
  return `写入 ${path.basename(input.path)}`;
2677
- if (toolName === "edit" && typeof input.path === "string")
2878
+ if (toolName === "file_edit" && typeof input.path === "string")
2678
2879
  return `修改 ${path.basename(input.path)}`;
2679
2880
  return undefined;
2680
2881
  }
@@ -2730,35 +2931,35 @@ function normalizeDisplayText(value) {
2730
2931
  }
2731
2932
  function buildToolUseDisplay(toolName, input) {
2732
2933
  const data = isRecord(input) ? input : {};
2733
- const purpose = stringValue(data.description);
2734
- const subject = toolName === "exec_command" ? undefined : toolDisplaySubject(toolName, data);
2934
+ const purpose = toolUsePurpose(toolName, input);
2935
+ const subject = toolName === "terminal_run" ? undefined : toolDisplaySubject(toolName, data);
2735
2936
  const facts = [];
2736
2937
  const previews = [];
2737
- if (toolName === "exec_command")
2938
+ if (toolName === "terminal_run")
2738
2939
  pushToolPreview(previews, "命令", "code", data.cmd);
2739
- else if (toolName === "write_stdin") {
2940
+ else if (toolName === "terminal_control") {
2740
2941
  pushToolFact(facts, "会话", data.session_id, true);
2741
2942
  pushToolFact(facts, "输入", data.chars, true);
2742
2943
  }
2743
- else if (toolName === "grep")
2944
+ else if (toolName === "file_search")
2744
2945
  pushToolFact(facts, "范围", data.path, true);
2745
- else if (toolName === "load_image")
2946
+ else if (toolName === "image_inspect")
2746
2947
  pushToolFact(facts, "图片", arrayLabel(data.imageRefs), true);
2747
- else if (toolName === "image2") {
2948
+ else if (toolName === "image_create") {
2748
2949
  pushToolFact(facts, "模式", data.mode === "edit" ? "编辑" : "生成");
2749
2950
  pushToolFact(facts, "名称", data.semanticName);
2750
2951
  }
2751
2952
  else if (toolName === "expose_downloads")
2752
2953
  pushToolFact(facts, "文件", arrayLabel(data.paths), true);
2753
- else if (toolName === "SendMessage" || toolName.toLowerCase() === "sendmessage")
2954
+ else if (toolName === "subagent_message")
2754
2955
  pushToolFact(facts, "接收方", data.target);
2755
- else if (toolName.toLowerCase().startsWith("task"))
2956
+ else if (toolName.startsWith("subagent_"))
2756
2957
  pushToolFact(facts, "任务", data.task_id, true);
2757
2958
  else if (toolName.startsWith("secret_")) {
2758
2959
  pushToolFact(facts, "密钥", data.key, true);
2759
2960
  pushToolFact(facts, "用途", data.reason);
2760
2961
  }
2761
- else if (toolName === "agent") {
2962
+ else if (toolName === "subagent_run") {
2762
2963
  pushToolFact(facts, "任务", data.description ?? data.prompt);
2763
2964
  pushToolFact(facts, "类型", data.subagent_type ?? data.mode);
2764
2965
  }
@@ -2770,7 +2971,7 @@ function buildToolResultDisplay(toolName, output, ok) {
2770
2971
  const previews = [];
2771
2972
  let subject = toolDisplaySubject(toolName, data);
2772
2973
  let purpose = stringValue(data.description);
2773
- if ((toolName === "edit" || toolName === "write") && isEditToolOutput(data)) {
2974
+ if ((toolName === "file_edit" || toolName === "file_write") && isEditToolOutput(data)) {
2774
2975
  subject = data.path;
2775
2976
  pushToolPreview(previews, undefined, "diff", compactEditDiff(data));
2776
2977
  }
@@ -2782,43 +2983,43 @@ function buildToolResultDisplay(toolName, output, ok) {
2782
2983
  if (outputText)
2783
2984
  pushToolPreview(previews, data.stderr ? "输出 / 错误" : "输出", "code", outputText);
2784
2985
  if (data.timed_out && !outputText)
2785
- pushToolPreview(previews, undefined, "text", "命令执行超时");
2986
+ pushToolFact(facts, "状态", "命令执行超时", false, "warning");
2786
2987
  else if ((!ok || (typeof data.exit_code === "number" && data.exit_code !== 0)) && !outputText)
2787
- pushToolPreview(previews, undefined, "text", `命令退出码:${data.exit_code ?? "未知"}`);
2988
+ pushToolFact(facts, "状态", `退出码 ${data.exit_code ?? "未知"}`, false, "danger");
2788
2989
  }
2789
- else if (toolName === "grep") {
2990
+ else if (toolName === "file_search") {
2790
2991
  subject = stringValue(data.query) || stringValue(data.grepPath) || stringValue(data.path);
2791
2992
  pushToolPreview(previews, undefined, "list", grepPreview(data));
2792
2993
  }
2793
- else if (toolName === "search") {
2994
+ else if (toolName === "web_search") {
2794
2995
  subject = stringValue(data.query);
2795
2996
  pushToolPreview(previews, undefined, "list", searchPreview(data));
2796
2997
  }
2797
- else if (toolName === "read") {
2998
+ else if (toolName === "file_read") {
2798
2999
  subject = stringValue(data.path);
2799
3000
  if (numberValue(data.startLine) !== undefined && numberValue(data.endLine) !== undefined)
2800
3001
  pushToolFact(facts, "范围", `${data.startLine}–${data.endLine} 行`);
2801
3002
  pushToolPreview(previews, undefined, "code", data.content);
2802
3003
  }
2803
- else if (toolName === "list") {
3004
+ else if (toolName === "file_list") {
2804
3005
  subject = stringValue(data.path);
2805
3006
  pushToolPreview(previews, undefined, "list", listPreview(data));
2806
3007
  }
2807
- else if (toolName === "image2") {
3008
+ else if (toolName === "image_create") {
2808
3009
  subject = stringValue(data.semanticName);
2809
3010
  pushToolFact(facts, "图片", `${numberValue(data.returnedImages) ?? arrayLength(data.images)} 张`);
2810
3011
  pushToolFact(facts, "尺寸", data.size);
2811
3012
  }
2812
- else if (toolName === "load_image") {
3013
+ else if (toolName === "image_inspect") {
2813
3014
  subject = arrayLabel(data.imageRefs);
2814
3015
  }
2815
3016
  else if (toolResources(output)?.length) {
2816
3017
  subject = toolResources(output)?.map((item) => item.label || item.downloadName).filter(Boolean).join("、");
2817
3018
  }
2818
- else if (toolName.toLowerCase().startsWith("task") || toolName === "agent") {
3019
+ else if (toolName.startsWith("subagent_") || toolName === "subagent_run") {
2819
3020
  subject = stringValue(data.description) || stringValue(data.task_id) || stringValue(data.agent_id);
2820
3021
  }
2821
- else if (toolName === "SendMessage" || toolName.toLowerCase() === "sendmessage") {
3022
+ else if (toolName === "subagent_message") {
2822
3023
  subject = stringValue(data.target);
2823
3024
  }
2824
3025
  else if (toolName.startsWith("secret_")) {
@@ -2827,7 +3028,7 @@ function buildToolResultDisplay(toolName, output, ok) {
2827
3028
  if (toolName === "secret_list")
2828
3029
  pushToolFact(facts, "数量", `${arrayLength(data.secrets)} 项`);
2829
3030
  }
2830
- else if (toolName === "agent_report") {
3031
+ else if (toolName === "subagent_report") {
2831
3032
  }
2832
3033
  else {
2833
3034
  subject = subject || stringValue(data.path) || stringValue(data.id) || stringValue(data.name);
@@ -2835,17 +3036,17 @@ function buildToolResultDisplay(toolName, output, ok) {
2835
3036
  return normalizeToolDisplay({ subject, purpose, facts, previews });
2836
3037
  }
2837
3038
  function toolDisplaySubject(toolName, data) {
2838
- if (["read", "write", "edit", "list"].includes(toolName))
3039
+ if (["file_read", "file_write", "file_edit", "file_list"].includes(toolName))
2839
3040
  return stringValue(data.path);
2840
- if (toolName === "exec_command")
3041
+ if (toolName === "terminal_run")
2841
3042
  return stringValue(data.cmd) || stringValue(data.command);
2842
- if (toolName === "grep" || toolName === "search")
3043
+ if (toolName === "file_search" || toolName === "web_search")
2843
3044
  return stringValue(data.query);
2844
- if (toolName === "image2")
3045
+ if (toolName === "image_create")
2845
3046
  return stringValue(data.semanticName);
2846
- if (toolName === "SendMessage" || toolName.toLowerCase() === "sendmessage")
3047
+ if (toolName === "subagent_message")
2847
3048
  return stringValue(data.target);
2848
- if (toolName.toLowerCase().startsWith("task"))
3049
+ if (toolName.startsWith("subagent_"))
2849
3050
  return stringValue(data.task_id);
2850
3051
  return undefined;
2851
3052
  }
@@ -2926,24 +3127,24 @@ function taskStatusDisplay(value, ok) {
2926
3127
  return status ? labels[status] ?? status : ok ? "已完成" : "失败";
2927
3128
  }
2928
3129
  function formatToolResult(toolName, output, ok) {
2929
- if ((toolName === "edit" || toolName === "write") && isRecord(output) && isEditToolOutput(output))
3130
+ if ((toolName === "file_edit" || toolName === "file_write") && isRecord(output) && isEditToolOutput(output))
2930
3131
  return { text: formatEditToolDiff(output, ok), format: "diff", summaryMaxLines: EDIT_TOOL_SUMMARY_MAX_LINES };
2931
3132
  if (isExecOutput(output))
2932
3133
  return { text: formatExecToolResult(output, ok), format: "plain", summaryMaxLines: EXPANDED_SUMMARY_MAX_LINES };
2933
- if (toolName === "list" && isRecord(output))
3134
+ if (toolName === "file_list" && isRecord(output))
2934
3135
  return { text: formatListToolResult(output, ok) };
2935
- if (toolName === "read" && isRecord(output))
3136
+ if (toolName === "file_read" && isRecord(output))
2936
3137
  return { text: formatReadToolResult(output, ok) };
2937
- if (toolName === "grep" && isRecord(output))
3138
+ if (toolName === "file_search" && isRecord(output))
2938
3139
  return { text: formatGrepToolResult(output, ok) };
2939
- if (toolName === "search" && isRecord(output))
3140
+ if (toolName === "web_search" && isRecord(output))
2940
3141
  return { text: formatWebSearchToolResult(output, ok), summaryMaxLines: EXPANDED_SUMMARY_MAX_LINES };
2941
- if (toolName === "image2" && isRecord(output))
3142
+ if (toolName === "image_create" && isRecord(output))
2942
3143
  return { text: formatImageGenerationToolResult(output, ok), format: "plain", summaryMaxLines: 4 };
2943
3144
  const resources = toolResources(output);
2944
3145
  if (resources?.length)
2945
3146
  return { text: ok ? "资源已准备好。" : "资源准备失败。", full: true, bodyTitle: ok ? "资源已就绪" : "资源准备失败" };
2946
- if (toolName === "plan" && isWebPlanPayload(output))
3147
+ if (toolName === "plan_update" && isWebPlanPayload(output))
2947
3148
  return { text: serializeWebPlanPayload(output), full: true, bodyTitle: webPlanBodyTitle(output) };
2948
3149
  if (typeof output === "string")
2949
3150
  return { text: output, format: hasAnsi(output) ? "ansi" : undefined, summaryMaxLines: EXPANDED_SUMMARY_MAX_LINES };