neoctl 0.2.46 → 0.2.48
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.
- package/README.md +59 -59
- package/dist/agents/agent-activity.js +1 -1
- package/dist/agents/agent-activity.js.map +1 -1
- package/dist/agents/agent-tool.js +2 -1
- package/dist/agents/agent-tool.js.map +1 -1
- package/dist/context/compaction.d.ts +1 -0
- package/dist/context/compaction.js +6 -2
- package/dist/context/compaction.js.map +1 -1
- package/dist/context/prompts.js +3 -4
- package/dist/context/prompts.js.map +1 -1
- package/dist/context/system.md +16 -16
- package/dist/core/abortable-stream.d.ts +2 -0
- package/dist/core/abortable-stream.js +17 -0
- package/dist/core/abortable-stream.js.map +1 -1
- package/dist/core/compaction-progress.d.ts +1 -1
- package/dist/core/compaction-progress.js +7 -5
- package/dist/core/compaction-progress.js.map +1 -1
- package/dist/core/execution-state.d.ts +2 -0
- package/dist/core/execution-state.js +2 -1
- package/dist/core/execution-state.js.map +1 -1
- package/dist/core/message-pipeline.js +2 -2
- package/dist/core/message-pipeline.js.map +1 -1
- package/dist/core/query-engine.d.ts +12 -1
- package/dist/core/query-engine.js +125 -21
- package/dist/core/query-engine.js.map +1 -1
- package/dist/core/query.js +46 -6
- package/dist/core/query.js.map +1 -1
- package/dist/execution/filesystem.js +22 -22
- package/dist/execution/process.js +44 -44
- package/dist/model/model-gateway.d.ts +4 -0
- package/dist/model/model-gateway.js.map +1 -1
- package/dist/model/model-metadata.json +137 -137
- package/dist/model/openai-chat-mapper.js +2 -0
- package/dist/model/openai-chat-mapper.js.map +1 -1
- package/dist/model/openai-responses-mapper.js +3 -0
- package/dist/model/openai-responses-mapper.js.map +1 -1
- package/dist/model/tool-input-stream.d.ts +31 -0
- package/dist/model/tool-input-stream.js +226 -0
- package/dist/model/tool-input-stream.js.map +1 -0
- package/dist/repl/index.js +30 -30
- package/dist/repl/index.js.map +1 -1
- package/dist/session/session-store.js +2 -0
- package/dist/session/session-store.js.map +1 -1
- package/dist/session/tool-result-memory.js +2 -2
- package/dist/session/tool-result-memory.js.map +1 -1
- package/dist/tasks/subagent-tools.js +1 -1
- package/dist/tasks/subagent-tools.js.map +1 -1
- package/dist/tools/builtins/exec-process-manager.d.ts +2 -1
- package/dist/tools/builtins/exec-process-manager.js +2 -1
- package/dist/tools/builtins/exec-process-manager.js.map +1 -1
- package/dist/tools/builtins/exec-tool.js +1 -1
- package/dist/tools/builtins/exec-tool.js.map +1 -1
- package/dist/tools/builtins/image-capabilities.d.ts +2 -2
- package/dist/tools/builtins/image-capabilities.js +3 -3
- package/dist/tools/builtins/image-capabilities.js.map +1 -1
- package/dist/tools/builtins/image-generation-tool.js +1 -1
- package/dist/tools/builtins/image-generation-tool.js.map +1 -1
- package/dist/tools/builtins/image-loader-tool.js +2 -1
- package/dist/tools/builtins/image-loader-tool.js.map +1 -1
- package/dist/tools/run-tool-use.js +2 -1
- package/dist/tools/run-tool-use.js.map +1 -1
- package/dist/tools/terminal-output-store.d.ts +1 -0
- package/dist/tools/terminal-output-store.js +13 -1
- package/dist/tools/terminal-output-store.js.map +1 -1
- package/dist/types/events.d.ts +11 -0
- package/dist/web/demo-tools.js +1 -1
- package/dist/web/demo-tools.js.map +1 -1
- package/dist/web/html.js +5 -5
- package/dist/web/image-result-metadata.d.ts +6 -0
- package/dist/web/image-result-metadata.js +3 -0
- package/dist/web/image-result-metadata.js.map +1 -1
- package/dist/web/index.d.ts +15 -1
- package/dist/web/index.js +134 -20
- package/dist/web/index.js.map +1 -1
- package/dist/web/terminal-presentation.js +1 -1
- package/dist/web/terminal-presentation.js.map +1 -1
- package/dist/web/tool-activity.d.ts +9 -0
- package/dist/web/tool-activity.js +19 -0
- package/dist/web/tool-activity.js.map +1 -0
- package/dist/web/tool-observation-result.d.ts +1 -0
- package/dist/web/tool-observation-result.js +35 -0
- package/dist/web/tool-observation-result.js.map +1 -0
- package/docs/prompt-config.md +86 -86
- package/package.json +1 -1
- package/scripts/copy-prompt-resources.mjs +5 -5
package/dist/web/index.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { applyToolInputChanges } from "../model/tool-input-stream.js";
|
|
3
|
+
import { toolActivity } from "./tool-activity.js";
|
|
4
|
+
import { toolObservationResult } from "./tool-observation-result.js";
|
|
2
5
|
import { DemoMode, DEMO_MARKER, DEMO_PROMPT } from "./demo-mode.js";
|
|
3
6
|
import { DemoTools } from "./demo-tools.js";
|
|
4
7
|
import { createTextMessage } from "../types/messages.js";
|
|
@@ -224,6 +227,7 @@ export async function createWebRuntime(options = {}) {
|
|
|
224
227
|
taskNotificationSource: createTaskNotificationSource(taskStore),
|
|
225
228
|
commands: replCommandDefinitions.map((command) => command.usage),
|
|
226
229
|
plugins: [...(options.plugins ?? []), ...activePlugins.map((plugin) => plugin.id)],
|
|
230
|
+
workspacePaths: options.workspacePaths,
|
|
227
231
|
cwdTransitionPaths: options.cwdTransitionPaths,
|
|
228
232
|
onCwdTransitionConsumed: options.onCwdTransitionConsumed,
|
|
229
233
|
session: {
|
|
@@ -562,6 +566,7 @@ export class WebRepl {
|
|
|
562
566
|
lineId = 0;
|
|
563
567
|
assistantLineId;
|
|
564
568
|
thinkingLineId;
|
|
569
|
+
previousTurnToolId;
|
|
565
570
|
finalizedThinkingLineId;
|
|
566
571
|
liveToolLineIds = new Map();
|
|
567
572
|
activeAbortController;
|
|
@@ -723,7 +728,7 @@ export class WebRepl {
|
|
|
723
728
|
redact: value => this.runtime.engine.redactDisplayValue(value),
|
|
724
729
|
}, request);
|
|
725
730
|
}
|
|
726
|
-
terminalOutput(sessionId, runId, stream, offset, limitBytes) {
|
|
731
|
+
terminalOutput(sessionId, runId, stream, offset, limitBytes, tail = false) {
|
|
727
732
|
const session = this.runtime.engine.snapshot().session;
|
|
728
733
|
if (!session?.sessionDir || !sessionId || sessionId !== session.sessionId || !runId || !["stdout", "stderr"].includes(stream))
|
|
729
734
|
return undefined;
|
|
@@ -733,7 +738,7 @@ export class WebRepl {
|
|
|
733
738
|
const registered = manager.registerOwnerSession(sessionId, session.sessionDir);
|
|
734
739
|
if (!registered.ok)
|
|
735
740
|
return undefined;
|
|
736
|
-
const page = manager.readOutput(sessionId, runId, { stream: stream, offset, limitBytes });
|
|
741
|
+
const page = manager.readOutput(sessionId, runId, { stream: stream, offset, limitBytes, tail });
|
|
737
742
|
// Cursor addresses stored bytes, not the possibly additionally redacted response length.
|
|
738
743
|
return page.ok ? this.runtime.engine.redactDisplayValue({ sessionId, runId, ...page.value }) : undefined;
|
|
739
744
|
}
|
|
@@ -1138,6 +1143,7 @@ export class WebRepl {
|
|
|
1138
1143
|
this.runtime.usage.reset();
|
|
1139
1144
|
this.status = initialStatus(this.runtime, metrics);
|
|
1140
1145
|
const lineId = { current: 0 };
|
|
1146
|
+
this.previousTurnToolId = undefined;
|
|
1141
1147
|
this.lines = initialLines(this.runtime, lineId);
|
|
1142
1148
|
this.lineId = lineId.current;
|
|
1143
1149
|
this.assistantLineId = undefined;
|
|
@@ -1317,6 +1323,9 @@ export class WebRepl {
|
|
|
1317
1323
|
return actionFailure("COMPACTION_IN_PROGRESS", "Context compaction is already running");
|
|
1318
1324
|
const showProgress = !this.busy;
|
|
1319
1325
|
const runToken = this.foregroundRunToken;
|
|
1326
|
+
const controller = showProgress ? new AbortController() : undefined;
|
|
1327
|
+
if (controller)
|
|
1328
|
+
this.activeAbortController = controller;
|
|
1320
1329
|
let finishForeground;
|
|
1321
1330
|
const foreground = showProgress ? new Promise(resolve => { finishForeground = resolve; }) : undefined;
|
|
1322
1331
|
if (foreground)
|
|
@@ -1328,7 +1337,7 @@ export class WebRepl {
|
|
|
1328
1337
|
}
|
|
1329
1338
|
try {
|
|
1330
1339
|
// An idle click executes now through the settings barrier, not the chat/input queue.
|
|
1331
|
-
const result = await engine.updateSessionSettings({ compact: true });
|
|
1340
|
+
const result = await engine.updateSessionSettings({ compact: true }, { abortSignal: controller?.signal });
|
|
1332
1341
|
if (this.runtime.engine === engine && this.foregroundRunToken === runToken) {
|
|
1333
1342
|
if (result.compaction?.report)
|
|
1334
1343
|
this.appendCompaction(result.compaction.report);
|
|
@@ -1342,6 +1351,8 @@ export class WebRepl {
|
|
|
1342
1351
|
return actionFailure("COMPACTION_FAILED", error instanceof Error ? error.message : String(error));
|
|
1343
1352
|
}
|
|
1344
1353
|
finally {
|
|
1354
|
+
if (controller && this.activeAbortController === controller)
|
|
1355
|
+
this.activeAbortController = undefined;
|
|
1345
1356
|
if (this.compactingSession === engine)
|
|
1346
1357
|
this.compactingSession = undefined;
|
|
1347
1358
|
if (this.foregroundRun === foreground)
|
|
@@ -1483,6 +1494,10 @@ export class WebRepl {
|
|
|
1483
1494
|
append(line, streaming = false) {
|
|
1484
1495
|
const id = ++this.lineId;
|
|
1485
1496
|
const nextLine = { id, ...line };
|
|
1497
|
+
if (line.activity && line.toolName && line.toolUseId && !line.image) {
|
|
1498
|
+
nextLine.connectPreviousTool = this.previousTurnToolId !== undefined;
|
|
1499
|
+
this.previousTurnToolId = id;
|
|
1500
|
+
}
|
|
1486
1501
|
this.lines.push(nextLine);
|
|
1487
1502
|
if (streaming)
|
|
1488
1503
|
this.broadcastDelta({ type: "line.append", line: nextLine });
|
|
@@ -1505,6 +1520,9 @@ export class WebRepl {
|
|
|
1505
1520
|
this.broadcastSync();
|
|
1506
1521
|
}
|
|
1507
1522
|
replaceLine(id, patch) {
|
|
1523
|
+
const previous = this.lines.find(line => line.id === id);
|
|
1524
|
+
if (previous?.activity && patch.titleStatus)
|
|
1525
|
+
patch.activity = { ...previous.activity, phase: patch.titleStatus === "success" ? "completed" : "failed" };
|
|
1508
1526
|
this.lines = this.lines.map((line) => line.id === id ? { ...line, ...patch } : line);
|
|
1509
1527
|
this.broadcastSync();
|
|
1510
1528
|
}
|
|
@@ -1653,6 +1671,7 @@ export class WebRepl {
|
|
|
1653
1671
|
signal: terminal.signal,
|
|
1654
1672
|
terminationReason: terminal.termination_reason,
|
|
1655
1673
|
outputAvailability: terminal.output_ref.availability,
|
|
1674
|
+
outputRevision: terminal.output_ref.streams,
|
|
1656
1675
|
}));
|
|
1657
1676
|
const sessionTasks = [...this.backgroundSessionRuns.values()].map((run) => ({
|
|
1658
1677
|
kind: "session",
|
|
@@ -1742,6 +1761,12 @@ export class WebRepl {
|
|
|
1742
1761
|
finalizeLiveLine(id) {
|
|
1743
1762
|
if (id === undefined)
|
|
1744
1763
|
return;
|
|
1764
|
+
const previous = this.lines.find(line => line.id === id);
|
|
1765
|
+
if (previous?.activity && ["generating", "queued", "running"].includes(previous.activity.phase)) {
|
|
1766
|
+
const activity = { ...previous.activity, phase: previous.titleStatus === "success" ? "completed" : previous.titleStatus === "failure" ? "failed" : "interrupted" };
|
|
1767
|
+
this.lines = this.lines.map(line => line.id === id ? { ...line, activity } : line);
|
|
1768
|
+
this.queueDeltaOperation({ type: "line.patch", id, patch: { activity } });
|
|
1769
|
+
}
|
|
1745
1770
|
this.lines = this.lines.map((line) => line.id === id ? { ...line, live: false } : line);
|
|
1746
1771
|
this.queueDeltaOperation({ type: "line.patch", id, patch: { live: false } });
|
|
1747
1772
|
}
|
|
@@ -1761,6 +1786,8 @@ export class WebRepl {
|
|
|
1761
1786
|
cancelLiveToolLines(reason) {
|
|
1762
1787
|
for (const id of this.liveToolLineIds.values()) {
|
|
1763
1788
|
const line = this.lines.find((item) => item.id === id);
|
|
1789
|
+
if (line?.activity)
|
|
1790
|
+
this.patchLiveToolLine(line.toolUseId, { activity: { ...line.activity, phase: "interrupted" } });
|
|
1764
1791
|
const steps = (line?.toolStream?.steps ?? []).map((step) => step.status === "running" ? { ...step, status: "failed", phase: "cancelled" } : step);
|
|
1765
1792
|
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);
|
|
1766
1793
|
this.queueDeltaOperation({ type: "line.patch", id, patch: { live: false, kind: "error", titleStatus: "failure", bodyTitle: reason, toolStream: { ...line?.toolStream, phase: "cancelled", message: reason, steps } } });
|
|
@@ -1861,7 +1888,12 @@ export class WebRepl {
|
|
|
1861
1888
|
else {
|
|
1862
1889
|
stream.message = progress.message;
|
|
1863
1890
|
}
|
|
1864
|
-
|
|
1891
|
+
const data = progress.data;
|
|
1892
|
+
const observation = data && typeof data.task_id === "string" && Number.isSafeInteger(data.run_generation)
|
|
1893
|
+
? { ...line.observation, task_id: data.task_id, run_generation: data.run_generation }
|
|
1894
|
+
: data && typeof data.session_id === "string" && ["terminal.started", "terminal.output.delta"].includes(String(data.type)) && ["terminal_run", "terminal_control"].includes(event.toolUse.name)
|
|
1895
|
+
? { ...line.observation, session_id: data.session_id, status: "running", command: typeof data.command === "string" ? data.command : line.observation?.command } : line.observation;
|
|
1896
|
+
this.patchLiveToolLine(event.toolUse.id, { toolStream: stream, observation });
|
|
1865
1897
|
}
|
|
1866
1898
|
applyAvailableToolResult(event) {
|
|
1867
1899
|
const liveLineId = this.liveToolLineIds.get(event.toolUse.id);
|
|
@@ -1893,19 +1925,28 @@ export class WebRepl {
|
|
|
1893
1925
|
}
|
|
1894
1926
|
}
|
|
1895
1927
|
handleEvent(event) {
|
|
1928
|
+
// Model lifecycle defines membership only. The frontend owns the fixed
|
|
1929
|
+
// two-second playback and must not be interrupted by the next request.
|
|
1930
|
+
if (event.type === "state" && event.phase === "calling_model")
|
|
1931
|
+
this.previousTurnToolId = undefined;
|
|
1896
1932
|
this.reduce(event);
|
|
1897
1933
|
if (event.type === "timing.updated") {
|
|
1898
1934
|
if (event.timing.kind === "tool") {
|
|
1899
1935
|
const line = this.lines.find(item => item.timing?.id === event.timing.id)
|
|
1900
1936
|
?? this.lines.find(item => item.id === this.liveToolLineIds.get(event.timing.toolUseId));
|
|
1901
1937
|
if (line) {
|
|
1902
|
-
|
|
1903
|
-
this.
|
|
1938
|
+
const activity = line.activity && event.timing.status === "running" ? { ...line.activity, phase: "running" } : line.activity;
|
|
1939
|
+
this.lines = this.lines.map(item => item.id === line.id ? { ...item, timing: event.timing, activity } : item);
|
|
1940
|
+
this.queueDeltaOperation({ type: "line.patch", id: line.id, patch: { timing: event.timing, activity } });
|
|
1904
1941
|
}
|
|
1905
1942
|
}
|
|
1906
1943
|
this.queueDeltaStatus();
|
|
1907
1944
|
return;
|
|
1908
1945
|
}
|
|
1946
|
+
if (event.type === "message" && event.message.metadata?.loadedImages === true && typeof event.message.metadata.sourceToolUseId === "string") {
|
|
1947
|
+
renderObservedImages(event.message, line => this.append(line));
|
|
1948
|
+
return;
|
|
1949
|
+
}
|
|
1909
1950
|
if (event.type === "message" && this.matchesPendingUserImageEcho(event.message)) {
|
|
1910
1951
|
const messageId = this.pendingUserImageEchoMessageId;
|
|
1911
1952
|
this.pendingUserImageEchoIds = undefined;
|
|
@@ -1914,7 +1955,35 @@ export class WebRepl {
|
|
|
1914
1955
|
this.broadcastSync();
|
|
1915
1956
|
return;
|
|
1916
1957
|
}
|
|
1917
|
-
if (event.type === "tool_call.started" || event.type === "tool_call.
|
|
1958
|
+
if (event.type === "tool_call.started" || event.type === "tool_call.input") {
|
|
1959
|
+
let id = this.liveToolLineIds.get(event.callId);
|
|
1960
|
+
let previous = this.lines.find(line => line.id === id);
|
|
1961
|
+
const name = event.name || previous?.toolName || "tool";
|
|
1962
|
+
if (id === undefined) {
|
|
1963
|
+
const activity = { phase: "generating", input: {} };
|
|
1964
|
+
id = this.append({ ...formatToolUse({ id: event.callId, name, input: {} }), activity, live: true });
|
|
1965
|
+
this.liveToolLineIds.set(event.callId, id);
|
|
1966
|
+
previous = this.lines.find(line => line.id === id);
|
|
1967
|
+
}
|
|
1968
|
+
if (previous?.toolName !== name && name !== "tool")
|
|
1969
|
+
this.patchLiveToolLine(event.callId, { toolName: name, title: toolTitle(name, "running"), toolPresentation: toolPresentationForSource(name) });
|
|
1970
|
+
if (event.type === "tool_call.input") {
|
|
1971
|
+
const activity = { phase: "generating", input: applyToolInputChanges(previous?.activity?.input || {}, event.changes), truncated: event.truncated };
|
|
1972
|
+
this.lines = this.lines.map(line => line.id === id ? { ...line, activity } : line);
|
|
1973
|
+
this.queueDeltaOperation({ type: "line.activity.input", id, changes: event.changes, truncated: event.truncated });
|
|
1974
|
+
}
|
|
1975
|
+
this.queueDeltaStatus();
|
|
1976
|
+
return;
|
|
1977
|
+
}
|
|
1978
|
+
if (event.type === "tool_call.ready") {
|
|
1979
|
+
const patch = { ...formatToolUse(event.toolUse), activity: toolActivity(event.toolUse.input, "queued"), live: true };
|
|
1980
|
+
if (this.liveToolLineIds.has(event.toolUse.id))
|
|
1981
|
+
this.patchLiveToolLine(event.toolUse.id, patch);
|
|
1982
|
+
else
|
|
1983
|
+
this.liveToolLineIds.set(event.toolUse.id, this.append(patch));
|
|
1984
|
+
return;
|
|
1985
|
+
}
|
|
1986
|
+
if (event.type === "tool_call.delta") {
|
|
1918
1987
|
this.queueDeltaStatus();
|
|
1919
1988
|
return;
|
|
1920
1989
|
}
|
|
@@ -1926,6 +1995,10 @@ export class WebRepl {
|
|
|
1926
1995
|
this.broadcastSync();
|
|
1927
1996
|
return;
|
|
1928
1997
|
}
|
|
1998
|
+
if (event.type === "assistant.output.done") {
|
|
1999
|
+
this.queueDeltaStatus();
|
|
2000
|
+
return;
|
|
2001
|
+
}
|
|
1929
2002
|
if (event.type === "assistant.delta") {
|
|
1930
2003
|
this.finalizeThinkingLine();
|
|
1931
2004
|
const id = this.assistantLineId ?? this.append({ kind: "assistant", text: "", live: true }, true);
|
|
@@ -2018,8 +2091,12 @@ export class WebRepl {
|
|
|
2018
2091
|
this.finalizeLiveLine(this.assistantLineId);
|
|
2019
2092
|
this.finalizeThinkingLine();
|
|
2020
2093
|
this.assistantLineId = undefined;
|
|
2021
|
-
const
|
|
2022
|
-
this.liveToolLineIds.
|
|
2094
|
+
const patch = { ...formatToolUse(event.toolUse), activity: toolActivity(event.toolUse.input, "queued"), live: true };
|
|
2095
|
+
const existing = this.liveToolLineIds.get(event.toolUse.id);
|
|
2096
|
+
if (existing === undefined)
|
|
2097
|
+
this.liveToolLineIds.set(event.toolUse.id, this.append(patch));
|
|
2098
|
+
else
|
|
2099
|
+
this.patchLiveToolLine(event.toolUse.id, patch);
|
|
2023
2100
|
return;
|
|
2024
2101
|
}
|
|
2025
2102
|
if (event.type === "tool.finished") {
|
|
@@ -2409,11 +2486,16 @@ export function compactLineOperations(operations) {
|
|
|
2409
2486
|
previous.text += operation.text;
|
|
2410
2487
|
continue;
|
|
2411
2488
|
}
|
|
2489
|
+
if (operation.type === "line.activity.input" && previous?.type === "line.activity.input" && previous.id === operation.id) {
|
|
2490
|
+
previous.changes.push(...operation.changes);
|
|
2491
|
+
previous.truncated = operation.truncated;
|
|
2492
|
+
continue;
|
|
2493
|
+
}
|
|
2412
2494
|
if (operation.type === "line.patch" && previous?.type === "line.patch" && previous.id === operation.id) {
|
|
2413
2495
|
previous.patch = { ...previous.patch, ...operation.patch };
|
|
2414
2496
|
continue;
|
|
2415
2497
|
}
|
|
2416
|
-
compacted.push(operation.type === "line.patch" ? { ...operation, patch: { ...operation.patch } } : { ...operation });
|
|
2498
|
+
compacted.push(operation.type === "line.patch" ? { ...operation, patch: { ...operation.patch } } : operation.type === "line.activity.input" ? { ...operation, changes: [...operation.changes] } : { ...operation });
|
|
2417
2499
|
}
|
|
2418
2500
|
return compacted;
|
|
2419
2501
|
}
|
|
@@ -2471,7 +2553,7 @@ export async function handleWebRequest(req, res, router) {
|
|
|
2471
2553
|
}
|
|
2472
2554
|
if (req.method === "GET" && url.pathname === "/api/terminal-output") {
|
|
2473
2555
|
res.setHeader("Cache-Control", "no-store");
|
|
2474
|
-
const page = repl.terminalOutput(url.searchParams.get("sessionId") ?? "", url.searchParams.get("runId") ?? "", url.searchParams.get("stream") ?? "stdout", Number(url.searchParams.get("offset") ?? 0), Number(url.searchParams.get("limitBytes") ?? 65536));
|
|
2556
|
+
const page = repl.terminalOutput(url.searchParams.get("sessionId") ?? "", url.searchParams.get("runId") ?? "", url.searchParams.get("stream") ?? "stdout", Number(url.searchParams.get("offset") ?? 0), Number(url.searchParams.get("limitBytes") ?? 65536), url.searchParams.get("tail") === "true");
|
|
2475
2557
|
return page ? sendJson(res, page) : sendJson(res, { error: "终端输出不可获取或无权访问" }, 404);
|
|
2476
2558
|
}
|
|
2477
2559
|
if (req.method === "GET" && url.pathname === "/api/state")
|
|
@@ -2479,6 +2561,15 @@ export async function handleWebRequest(req, res, router) {
|
|
|
2479
2561
|
if (req.method === "GET" && url.pathname === "/api/runtime-context")
|
|
2480
2562
|
return sendJson(res, await repl.runtimeContext());
|
|
2481
2563
|
const workspaceRepl = repl;
|
|
2564
|
+
if (req.method === "GET" && url.pathname === "/api/cwd/scope") {
|
|
2565
|
+
return sendJson(res, workspaceRepl.workspaceScope?.() ?? actionFailure("CWD_NOT_CONFIGURED", "Workspace scope is not configured"));
|
|
2566
|
+
}
|
|
2567
|
+
if (req.method === "POST" && ["/api/cwd/add", "/api/cwd/remove"].includes(url.pathname)) {
|
|
2568
|
+
const body = await readJsonBody(req);
|
|
2569
|
+
return sendJson(res, workspaceRepl.updateWorkspaceScope
|
|
2570
|
+
? await workspaceRepl.updateWorkspaceScope(body.path, url.pathname.endsWith("/remove"))
|
|
2571
|
+
: actionFailure("CWD_NOT_CONFIGURED", "Workspace scope is not configured"));
|
|
2572
|
+
}
|
|
2482
2573
|
if (req.method === "GET" && url.pathname === "/api/cwd") {
|
|
2483
2574
|
return sendJson(res, workspaceRepl.browseWorkspace
|
|
2484
2575
|
? await workspaceRepl.browseWorkspace(url.searchParams.get("path") ?? undefined)
|
|
@@ -2737,7 +2828,7 @@ function restoredHistoryLines(runtime) {
|
|
|
2737
2828
|
if (line.toolUseId && line.titleStatus && historyToolLineIndexes.has(line.toolUseId)) {
|
|
2738
2829
|
const index = historyToolLineIndexes.get(line.toolUseId);
|
|
2739
2830
|
const started = lines[index];
|
|
2740
|
-
lines[index] = { ...line, presentationLevel: mergePresentationLevel(line.presentationLevel, started.presentationLevel), toolDisplay: mergeToolDisplays(line.toolDisplay, started.toolDisplay) };
|
|
2831
|
+
lines[index] = { ...line, activity: started.activity ? { ...started.activity, phase: line.titleStatus === "success" ? "completed" : "failed" } : undefined, presentationLevel: mergePresentationLevel(line.presentationLevel, started.presentationLevel), toolDisplay: mergeToolDisplays(line.toolDisplay, started.toolDisplay) };
|
|
2741
2832
|
return index + 1;
|
|
2742
2833
|
}
|
|
2743
2834
|
lines.push(line);
|
|
@@ -2763,6 +2854,10 @@ function restoredHistoryLines(runtime) {
|
|
|
2763
2854
|
}
|
|
2764
2855
|
for (const [entryIndex, entry] of entries.entries()) {
|
|
2765
2856
|
if (entry.type === "message") {
|
|
2857
|
+
if (entry.message.metadata?.loadedImages === true && typeof entry.message.metadata.sourceToolUseId === "string") {
|
|
2858
|
+
renderObservedImages(entry.message, append);
|
|
2859
|
+
continue;
|
|
2860
|
+
}
|
|
2766
2861
|
renderMessage(entry.message, append, undefined, {
|
|
2767
2862
|
includeToolUseBlocks: true,
|
|
2768
2863
|
includeThinkingBlocks: true,
|
|
@@ -2778,6 +2873,8 @@ function restoredHistoryLines(runtime) {
|
|
|
2778
2873
|
const matches = line.toolUseId ? toolTimings.get(line.toolUseId) : undefined;
|
|
2779
2874
|
if (matches?.length === 1)
|
|
2780
2875
|
line.timing = matches[0];
|
|
2876
|
+
if (line.activity && !line.titleStatus)
|
|
2877
|
+
line.activity = { ...line.activity, phase: "interrupted" };
|
|
2781
2878
|
}
|
|
2782
2879
|
return lines;
|
|
2783
2880
|
}
|
|
@@ -2933,6 +3030,12 @@ function reduceStatus(status, event) {
|
|
|
2933
3030
|
return { ...status, metrics: event.metrics, inputTokenUpdatedAt: event.metrics.estimatedInputTokens !== status.metrics?.estimatedInputTokens ? Date.now() : status.inputTokenUpdatedAt, activityTick: status.activityTick + 1 };
|
|
2934
3031
|
if (event.type === "usage")
|
|
2935
3032
|
return { ...status, usage: event.usage, inputTokenUpdatedAt: event.usage.inputTokens !== undefined ? Date.now() : status.inputTokenUpdatedAt, outputTokenUpdatedAt: event.usage.outputTokens !== undefined ? Date.now() : status.outputTokenUpdatedAt, activityTick: status.activityTick + 1 };
|
|
3033
|
+
if (event.type === "assistant.output.done")
|
|
3034
|
+
return status.modelOutput?.kind === "text" ? { ...status, modelOutput: undefined, activityTick: status.activityTick + 1 } : status;
|
|
3035
|
+
if (event.type === "message" && event.message.role === "assistant" && event.message.blocks.some(block => block.type === "text") && status.modelOutput?.kind === "text")
|
|
3036
|
+
return { ...status, modelOutput: undefined, activityTick: status.activityTick + 1 };
|
|
3037
|
+
if (event.type === "assistant.delta" && !event.text)
|
|
3038
|
+
return status;
|
|
2936
3039
|
if (event.type === "assistant.delta")
|
|
2937
3040
|
return { ...status, phase: "calling_model", modelOutput: { kind: "text" }, streamedOutputTokens: status.streamedOutputTokens + estimateTokens(event.text), outputTokenUpdatedAt: Date.now(), activityTick: status.activityTick + 1 };
|
|
2938
3041
|
if (event.type === "thinking.delta")
|
|
@@ -3323,6 +3426,15 @@ function renderMessageImages(message, append, messageId = message.id) {
|
|
|
3323
3426
|
}
|
|
3324
3427
|
return rendered;
|
|
3325
3428
|
}
|
|
3429
|
+
function renderObservedImages(message, append) {
|
|
3430
|
+
for (const [index, block] of message.blocks.entries()) {
|
|
3431
|
+
if (block.type !== "image")
|
|
3432
|
+
continue;
|
|
3433
|
+
const line = imageLineForBlock("tool_result", block, message.id, index);
|
|
3434
|
+
if (line)
|
|
3435
|
+
append({ ...line, observationOnly: true, messageId: message.id, parentToolName: "image_inspect", parentToolUseId: message.metadata?.sourceToolUseId });
|
|
3436
|
+
}
|
|
3437
|
+
}
|
|
3326
3438
|
function imageLineForBlock(role, block, messageId, blockIndex) {
|
|
3327
3439
|
const kind = kindForRole(role);
|
|
3328
3440
|
if (kind === "meta")
|
|
@@ -3450,14 +3562,15 @@ function thinkingLine(text, live = false) {
|
|
|
3450
3562
|
return { kind: "thinking", title: titleForKind("thinking"), text, previewStyle: "summary", summaryMaxLines: THINKING_SUMMARY_MAX_LINES, live };
|
|
3451
3563
|
}
|
|
3452
3564
|
function formatToolUse(toolUse) {
|
|
3565
|
+
const activity = toolActivity(toolUse.input, "running");
|
|
3453
3566
|
const toolPresentation = toolPresentationForSource(toolUse.name);
|
|
3454
3567
|
const presentationLevel = toolPresentationLevel(toolUse.name, toolUse.input);
|
|
3455
3568
|
if (toolUse.name === "plan_update" && isWebPlanPayload(toolUse.input))
|
|
3456
|
-
return { kind: "tool", toolName: toolUse.name, toolPresentation, title: toolPresentation.label, bodyTitle: webPlanBodyTitle(toolUse.input), text: serializeWebPlanPayload(toolUse.input), presentationLevel, collapsible: true };
|
|
3569
|
+
return { kind: "tool", activity, toolName: toolUse.name, toolPresentation, toolUseId: toolUse.id, title: toolPresentation.label, bodyTitle: webPlanBodyTitle(toolUse.input), text: serializeWebPlanPayload(toolUse.input), presentationLevel, collapsible: true };
|
|
3457
3570
|
if (toolUse.name === "image_create")
|
|
3458
|
-
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 };
|
|
3571
|
+
return { kind: "tool", activity, 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 };
|
|
3459
3572
|
const summary = summarizeToolUse(toolUse.name, toolUse.input);
|
|
3460
|
-
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 };
|
|
3573
|
+
return { kind: "tool", activity, 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 };
|
|
3461
3574
|
}
|
|
3462
3575
|
function formatToolResultLine(toolName, output, ok, presentToolResult) {
|
|
3463
3576
|
// Hidden fallback reports in historical agent payloads must not enter line.text
|
|
@@ -3472,6 +3585,7 @@ function formatToolResultLine(toolName, output, ok, presentToolResult) {
|
|
|
3472
3585
|
: formatToolResult(toolName, toolName.startsWith("subagent_") ? redactToolDetail(output) : output, ok);
|
|
3473
3586
|
return {
|
|
3474
3587
|
kind: ok ? "tool" : "error",
|
|
3588
|
+
observation: toolObservationResult(toolName, output),
|
|
3475
3589
|
toolError: ok ? undefined : toolErrorText(output, ok).slice(0, 2000),
|
|
3476
3590
|
toolName,
|
|
3477
3591
|
toolPresentation: toolPresentationForSource(toolName),
|
|
@@ -3982,13 +4096,13 @@ function formatImageGenerationToolResult(output, ok) {
|
|
|
3982
4096
|
const lines = [`${mode === "edit" ? "edited" : "generated"} ${returnedImages ?? 0} image${returnedImages === 1 ? "" : "s"}`];
|
|
3983
4097
|
const details = [provider, model, size, quality && quality !== "auto" ? quality : undefined, format].filter((value) => Boolean(value));
|
|
3984
4098
|
if (details.length > 0)
|
|
3985
|
-
lines.push(`${output.requested ? "requested: " : ""}${details.join("
|
|
4099
|
+
lines.push(`${output.requested ? "requested: " : ""}${details.join(" ")}`);
|
|
3986
4100
|
if (isRecord(output.actual)) {
|
|
3987
4101
|
lines.push(`upstream-reported quality: ${String(output.actual.quality ?? "unreported")}`);
|
|
3988
4102
|
if (Array.isArray(output.images))
|
|
3989
4103
|
for (const image of output.images.filter(isRecord)) {
|
|
3990
4104
|
if (typeof image.width === "number" && typeof image.height === "number")
|
|
3991
|
-
lines.push(`actual image: ${image.width}x${image.height}
|
|
4105
|
+
lines.push(`actual image: ${image.width}x${image.height} ${String(image.mimeType ?? "")}`);
|
|
3992
4106
|
}
|
|
3993
4107
|
}
|
|
3994
4108
|
if (Array.isArray(output.warnings))
|
|
@@ -4033,7 +4147,7 @@ function formatListToolResult(output, ok) {
|
|
|
4033
4147
|
lines.push(`type: ${typeValue}`);
|
|
4034
4148
|
const counts = [returnedEntries !== undefined ? `${returnedEntries} shown` : undefined, totalFiles !== undefined ? `${totalFiles} files` : undefined, totalDirectories !== undefined ? `${totalDirectories} dirs` : undefined].filter((value) => Boolean(value));
|
|
4035
4149
|
if (counts.length > 0)
|
|
4036
|
-
lines.push(`entries: ${counts.join("
|
|
4150
|
+
lines.push(`entries: ${counts.join(" ")}`);
|
|
4037
4151
|
if (names.length > 0)
|
|
4038
4152
|
lines.push("sample:", ...names.map((name) => ` ${name}`));
|
|
4039
4153
|
return lines.join("\n");
|
|
@@ -4079,7 +4193,7 @@ function formatWebSearchToolResult(output, ok) {
|
|
|
4079
4193
|
return;
|
|
4080
4194
|
const title = typeof item.title === "string" && item.title.trim() ? item.title.trim() : "Untitled";
|
|
4081
4195
|
const url = typeof item.url === "string" ? item.url : "";
|
|
4082
|
-
const published = typeof item.published === "string" ? `
|
|
4196
|
+
const published = typeof item.published === "string" ? ` ${item.published}` : "";
|
|
4083
4197
|
lines.push(`[${index + 1}] ${title}${published}`);
|
|
4084
4198
|
if (url)
|
|
4085
4199
|
lines.push(url);
|
|
@@ -4106,7 +4220,7 @@ function formatGrepToolResult(output, ok) {
|
|
|
4106
4220
|
if (grepPath !== undefined)
|
|
4107
4221
|
lines.push(`path: ${grepPath}`);
|
|
4108
4222
|
const countParts = [`${returnedMatches ?? matches.length} shown`, totalMatchesKnown !== undefined ? `${totalMatchesKnown} known` : undefined, truncated ? "truncated" : undefined].filter((value) => Boolean(value));
|
|
4109
|
-
lines.push(`matches: ${countParts.join("
|
|
4223
|
+
lines.push(`matches: ${countParts.join(" ")}`);
|
|
4110
4224
|
if (matches.length === 0)
|
|
4111
4225
|
return [...lines, "no matches"].join("\n");
|
|
4112
4226
|
lines.push("results:");
|