aws-runtime-bridge 1.7.49 → 1.8.0
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/dist/adapter/AcodeSdkAdapter.d.ts +104 -0
- package/dist/adapter/AcodeSdkAdapter.d.ts.map +1 -0
- package/dist/adapter/AcodeSdkAdapter.js +644 -0
- package/dist/adapter/AcodeSdkAdapter.test.d.ts +2 -0
- package/dist/adapter/AcodeSdkAdapter.test.d.ts.map +1 -0
- package/dist/adapter/AcodeSdkAdapter.test.js +1280 -0
- package/dist/adapter/AdapterRegistry.d.ts +1 -1
- package/dist/adapter/AdapterRegistry.d.ts.map +1 -1
- package/dist/adapter/AdapterRegistry.js +2 -0
- package/dist/adapter/AdapterRegistry.test.js +10 -1
- package/dist/adapter/ClaudeSdkAdapter.test.js +31 -8
- package/dist/adapter/CodexSdkAdapter.d.ts.map +1 -1
- package/dist/adapter/CodexSdkAdapter.js +2 -0
- package/dist/adapter/OpencodeSdkAdapter.d.ts.map +1 -1
- package/dist/adapter/OpencodeSdkAdapter.js +2 -0
- package/dist/adapter/SdkProviderSpi.d.ts.map +1 -1
- package/dist/adapter/SdkProviderSpi.js +10 -1
- package/dist/adapter/adapter.test.js +2 -2
- package/dist/adapter/idle-poll-loop.d.ts +4 -4
- package/dist/adapter/idle-poll-loop.d.ts.map +1 -1
- package/dist/adapter/idle-poll-loop.js +3 -3
- package/dist/adapter/idle-poll-loop.test.js +15 -1
- package/dist/adapter/index.d.ts +1 -0
- package/dist/adapter/index.d.ts.map +1 -1
- package/dist/adapter/index.js +1 -0
- package/dist/adapter/types.d.ts +25 -6
- package/dist/adapter/types.d.ts.map +1 -1
- package/dist/adapter/types.js +221 -110
- package/dist/adapter/types.test.js +10 -3
- package/dist/adapters/cc-switch/index.d.ts +2 -0
- package/dist/adapters/cc-switch/index.d.ts.map +1 -1
- package/dist/adapters/cc-switch/index.js +6 -0
- package/dist/adapters/cc-switch/mcp-acode.d.ts +17 -0
- package/dist/adapters/cc-switch/mcp-acode.d.ts.map +1 -0
- package/dist/adapters/cc-switch/mcp-acode.js +71 -0
- package/dist/adapters/cc-switch/mcp-acode.test.d.ts +2 -0
- package/dist/adapters/cc-switch/mcp-acode.test.d.ts.map +1 -0
- package/dist/adapters/cc-switch/mcp-acode.test.js +80 -0
- package/dist/adapters/cc-switch/mcp-claude.d.ts.map +1 -1
- package/dist/adapters/cc-switch/mcp-claude.js +1 -0
- package/dist/adapters/cc-switch/mcp-claudecode.d.ts.map +1 -1
- package/dist/adapters/cc-switch/mcp-claudecode.js +1 -0
- package/dist/adapters/cc-switch/mcp-claudecode.test.js +1 -0
- package/dist/adapters/cc-switch/mcp-codex.d.ts.map +1 -1
- package/dist/adapters/cc-switch/mcp-codex.js +1 -0
- package/dist/adapters/cc-switch/mcp-codex.test.js +1 -0
- package/dist/adapters/cc-switch/mcp-opencode.d.ts.map +1 -1
- package/dist/adapters/cc-switch/mcp-opencode.js +1 -0
- package/dist/adapters/cc-switch/mcp-opencode.test.js +1 -0
- package/dist/adapters/cc-switch/skill-acode.d.ts +11 -0
- package/dist/adapters/cc-switch/skill-acode.d.ts.map +1 -0
- package/dist/adapters/cc-switch/skill-acode.js +12 -0
- package/dist/adapters/cc-switch/skill-directory.test.js +1 -0
- package/dist/config.d.ts +4 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +10 -0
- package/dist/index.js +2 -0
- package/dist/routes/ai-sources.js +3 -2
- package/dist/routes/instance.d.ts.map +1 -1
- package/dist/routes/instance.js +7 -8
- package/dist/routes/instance.test.js +2 -1
- package/dist/routes/mcp.js +2 -2
- package/dist/routes/mcp.test.js +2 -1
- package/dist/routes/runtime-binding.d.ts +3 -2
- package/dist/routes/runtime-binding.d.ts.map +1 -1
- package/dist/routes/runtime-binding.js +11 -58
- package/dist/routes/runtime-binding.test.js +4 -80
- package/dist/routes/sessions.d.ts +5 -0
- package/dist/routes/sessions.d.ts.map +1 -1
- package/dist/routes/sessions.js +36 -16
- package/dist/routes/sessions.test.js +13 -0
- package/dist/routes/skills.js +2 -2
- package/dist/routes/skills.test.js +2 -1
- package/dist/routes/terminal.d.ts +27 -7
- package/dist/routes/terminal.d.ts.map +1 -1
- package/dist/routes/terminal.js +439 -171
- package/dist/routes/terminal.test.js +385 -197
- package/dist/services/acode-package.d.ts +8 -0
- package/dist/services/acode-package.d.ts.map +1 -0
- package/dist/services/acode-package.js +82 -0
- package/dist/services/ai-source-apply.d.ts +4 -0
- package/dist/services/ai-source-apply.d.ts.map +1 -1
- package/dist/services/ai-source-apply.js +8 -0
- package/dist/services/ai-source-apply.test.js +8 -1
- package/dist/services/cc-switch-sdk.d.ts.map +1 -1
- package/dist/services/cc-switch-sdk.js +9 -1
- package/dist/services/instance-init-service.d.ts.map +1 -1
- package/dist/services/instance-init-service.js +3 -2
- package/dist/services/instance-init-service.test.js +6 -5
- package/dist/services/instance-service.d.ts.map +1 -1
- package/dist/services/instance-service.js +1 -0
- package/dist/services/instance-state.js +1 -1
- package/dist/services/instance-state.test.js +4 -4
- package/dist/services/mcp-launch-binding-queue.d.ts +1 -0
- package/dist/services/mcp-launch-binding-queue.d.ts.map +1 -1
- package/dist/services/mcp-launch-binding-queue.js +8 -2
- package/dist/services/mcp-launch-binding-queue.test.js +47 -4
- package/dist/services/session-output.d.ts +10 -6
- package/dist/services/session-output.d.ts.map +1 -1
- package/dist/services/session-output.js +29 -14
- package/dist/services/session-output.test.js +93 -39
- package/dist/services/tool-installer.d.ts.map +1 -1
- package/dist/services/tool-installer.js +10 -0
- package/dist/types.d.ts +5 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/file-utils.d.ts.map +1 -1
- package/dist/utils/file-utils.js +1 -0
- package/dist/utils/mcp-utils.d.ts.map +1 -1
- package/dist/utils/mcp-utils.js +4 -1
- package/dist/utils/validation.js +1 -1
- package/node_modules/@cc-switch/sdk/dist/constants.d.ts.map +1 -1
- package/node_modules/@cc-switch/sdk/dist/constants.js +7 -0
- package/node_modules/@cc-switch/sdk/dist/schemas.d.ts.map +1 -1
- package/node_modules/@cc-switch/sdk/dist/schemas.js +1 -0
- package/node_modules/@cc-switch/sdk/dist/sdk.d.ts.map +1 -1
- package/node_modules/@cc-switch/sdk/dist/sdk.js +7 -3
- package/node_modules/@cc-switch/sdk/dist/services/ai-config-service.d.ts +6 -0
- package/node_modules/@cc-switch/sdk/dist/services/ai-config-service.d.ts.map +1 -1
- package/node_modules/@cc-switch/sdk/dist/services/ai-config-service.js +27 -1
- package/node_modules/@cc-switch/sdk/dist/types.d.ts +4 -1
- package/node_modules/@cc-switch/sdk/dist/types.d.ts.map +1 -1
- package/package/acode/README.md +88 -0
- package/package/acode/dist/built-in-file-tools.d.ts +16 -0
- package/package/acode/dist/built-in-file-tools.d.ts.map +1 -0
- package/package/acode/dist/built-in-file-tools.js +569 -0
- package/package/acode/dist/cli.d.ts +3 -0
- package/package/acode/dist/cli.d.ts.map +1 -0
- package/package/acode/dist/cli.js +37 -0
- package/package/acode/dist/commands/commandLoader.d.ts +7 -0
- package/package/acode/dist/commands/commandLoader.d.ts.map +1 -0
- package/package/acode/dist/commands/commandLoader.js +33 -0
- package/package/acode/dist/commands/commandParser.d.ts +7 -0
- package/package/acode/dist/commands/commandParser.d.ts.map +1 -0
- package/package/acode/dist/commands/commandParser.js +77 -0
- package/package/acode/dist/commands/commandRegistry.d.ts +13 -0
- package/package/acode/dist/commands/commandRegistry.d.ts.map +1 -0
- package/package/acode/dist/commands/commandRegistry.js +30 -0
- package/package/acode/dist/config.d.ts +4 -0
- package/package/acode/dist/config.d.ts.map +1 -0
- package/package/acode/dist/config.js +80 -0
- package/package/acode/dist/hooks/hookBus.d.ts +19 -0
- package/package/acode/dist/hooks/hookBus.d.ts.map +1 -0
- package/package/acode/dist/hooks/hookBus.js +40 -0
- package/package/acode/dist/index.d.ts +8 -0
- package/package/acode/dist/index.d.ts.map +1 -0
- package/package/acode/dist/index.js +7 -0
- package/package/acode/dist/runtime.d.ts +42 -0
- package/package/acode/dist/runtime.d.ts.map +1 -0
- package/package/acode/dist/runtime.js +1283 -0
- package/package/acode/dist/types.d.ts +112 -0
- package/package/acode/dist/types.d.ts.map +1 -0
- package/package/acode/dist/types.js +1 -0
- package/package/acode/package.json +24 -0
- package/package/aws-client-agent-mcp/dist/agent-client.d.ts +4 -4
- package/package/aws-client-agent-mcp/dist/agent-client.d.ts.map +1 -1
- package/package/aws-client-agent-mcp/dist/agent-client.js +9 -6
- package/package/aws-client-agent-mcp/dist/agent-client.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/agent-client.test.js +21 -0
- package/package/aws-client-agent-mcp/dist/agent-client.test.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/http-client.d.ts +1 -17
- package/package/aws-client-agent-mcp/dist/http-client.d.ts.map +1 -1
- package/package/aws-client-agent-mcp/dist/http-client.js +7 -113
- package/package/aws-client-agent-mcp/dist/http-client.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/http-client.test.js +22 -2
- package/package/aws-client-agent-mcp/dist/http-client.test.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/mcp-server.d.ts +0 -5
- package/package/aws-client-agent-mcp/dist/mcp-server.d.ts.map +1 -1
- package/package/aws-client-agent-mcp/dist/mcp-server.js +3 -24
- package/package/aws-client-agent-mcp/dist/mcp-server.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/mcp-server.test.js +90 -5
- package/package/aws-client-agent-mcp/dist/mcp-server.test.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/runtime-launch-binding.d.ts.map +1 -1
- package/package/aws-client-agent-mcp/dist/runtime-launch-binding.js +1 -0
- package/package/aws-client-agent-mcp/dist/runtime-launch-binding.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/status-reporter.d.ts +2 -0
- package/package/aws-client-agent-mcp/dist/status-reporter.d.ts.map +1 -1
- package/package/aws-client-agent-mcp/dist/status-reporter.js +31 -3
- package/package/aws-client-agent-mcp/dist/status-reporter.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/types.d.ts +4 -1
- package/package/aws-client-agent-mcp/dist/types.d.ts.map +1 -1
- package/package/aws-client-agent-mcp/dist/types.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/websocket-client.d.ts +0 -13
- package/package/aws-client-agent-mcp/dist/websocket-client.d.ts.map +1 -1
- package/package/aws-client-agent-mcp/dist/websocket-client.js +5 -100
- package/package/aws-client-agent-mcp/dist/websocket-client.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/websocket-client.test.js +43 -0
- package/package/aws-client-agent-mcp/dist/websocket-client.test.js.map +1 -1
- package/package/cc-switch-sdk/dist/constants.d.ts.map +1 -1
- package/package/cc-switch-sdk/dist/constants.js +7 -0
- package/package/cc-switch-sdk/dist/schemas.d.ts.map +1 -1
- package/package/cc-switch-sdk/dist/schemas.js +1 -0
- package/package/cc-switch-sdk/dist/sdk.d.ts.map +1 -1
- package/package/cc-switch-sdk/dist/sdk.js +7 -3
- package/package/cc-switch-sdk/dist/services/ai-config-service.d.ts +6 -0
- package/package/cc-switch-sdk/dist/services/ai-config-service.d.ts.map +1 -1
- package/package/cc-switch-sdk/dist/services/ai-config-service.js +27 -1
- package/package/cc-switch-sdk/dist/types.d.ts +4 -1
- package/package/cc-switch-sdk/dist/types.d.ts.map +1 -1
- package/package.json +8 -4
|
@@ -1,329 +1,517 @@
|
|
|
1
|
-
import { mkdtemp, mkdir, writeFile } from
|
|
2
|
-
import os from
|
|
3
|
-
import path from
|
|
4
|
-
import { describe, expect, it } from
|
|
5
|
-
import { SDK_PROVIDER_DEFINITIONS } from
|
|
6
|
-
import { buildClaudeCodeLaunchConfig, buildRuntimeEnv, buildToolResultTimelineActionInfo, createTerminalOutputDecoder, decodeTerminalOutputChunk, evaluatePersistedSessionReuse, formatTerminalPrompt, formatSdkOutputEvent, normalizeTerminalCommandInput, parseTerminalDirectoryChangeTarget, resolveSdkInputMessage, resolveTerminalOutputEncoding, resolveSdkProviderId, resolveStatusChangeUsage, } from
|
|
7
|
-
describe(
|
|
8
|
-
it(
|
|
1
|
+
import { mkdtemp, mkdir, writeFile } from "node:fs/promises";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { describe, expect, it } from "vitest";
|
|
5
|
+
import { SDK_PROVIDER_DEFINITIONS } from "../adapter/SdkProviderSpi.js";
|
|
6
|
+
import { buildAcodeCommandOutputPath, buildClaudeCodeLaunchConfig, buildRuntimeEnv, buildToolResultTimelineActionInfo, countTerminalOutputLines, createTerminalOutputDecoder, decodeTerminalOutputChunk, evaluatePersistedSessionReuse, formatCommandInactivityTimeoutNotice, formatCommandOutputSpilloverNotice, formatTerminalPrompt, formatSdkOutputEvent, normalizeTerminalCommandInput, parseTerminalDirectoryChangeTarget, resolveSdkInputMessage, resolveTerminalOutputEncoding, resolveSdkProviderId, resolveSdkSessionDisplayStatus, resolveRuntimeStatusFromProviderEvent, resolveStatusChangeUsage, } from "./terminal.js";
|
|
7
|
+
describe("terminal route validation", () => {
|
|
8
|
+
it("requires agentId and workspacePath for start", () => {
|
|
9
9
|
const validateStartRequest = (body) => {
|
|
10
10
|
if (!body.agentId || !body.workspacePath)
|
|
11
|
-
return {
|
|
11
|
+
return {
|
|
12
|
+
valid: false,
|
|
13
|
+
error: "agentId and workspacePath are required",
|
|
14
|
+
};
|
|
12
15
|
return { valid: true };
|
|
13
16
|
};
|
|
14
17
|
expect(validateStartRequest({}).valid).toBe(false);
|
|
15
|
-
expect(validateStartRequest({ agentId:
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
expect(resolveCommand(
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
expect(resolveLaunchMode(
|
|
26
|
-
expect(resolveLaunchMode(
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
18
|
+
expect(validateStartRequest({ agentId: "agent-1", workspacePath: "/path" })
|
|
19
|
+
.valid).toBe(true);
|
|
20
|
+
});
|
|
21
|
+
it("uses SDK default command when not provided", () => {
|
|
22
|
+
const resolveCommand = (command) => command || "acode";
|
|
23
|
+
expect(resolveCommand(undefined)).toBe("acode");
|
|
24
|
+
expect(resolveCommand("codex")).toBe("codex");
|
|
25
|
+
});
|
|
26
|
+
it("uses SDK launch mode for unsupported mode values", () => {
|
|
27
|
+
const resolveLaunchMode = (mode) => mode === "sdk" ? "sdk" : "sdk";
|
|
28
|
+
expect(resolveLaunchMode(undefined)).toBe("sdk");
|
|
29
|
+
expect(resolveLaunchMode("sdk")).toBe("sdk");
|
|
30
|
+
expect(resolveLaunchMode("unsupported")).toBe("sdk");
|
|
31
|
+
});
|
|
32
|
+
it("keeps shortcut mode available for manual key injection", () => {
|
|
33
|
+
const buildInputResponse = (mode) => mode === "shortcut"
|
|
34
|
+
? { ok: true, mode: "shortcut" }
|
|
35
|
+
: { ok: true, mode: "sdk" };
|
|
36
|
+
expect(buildInputResponse("shortcut")).toEqual({
|
|
37
|
+
ok: true,
|
|
38
|
+
mode: "shortcut",
|
|
39
|
+
});
|
|
40
|
+
expect(buildInputResponse(undefined)).toEqual({ ok: true, mode: "sdk" });
|
|
41
|
+
});
|
|
42
|
+
it("does not downgrade unsupported shortcut input to normal SDK messages", () => {
|
|
43
|
+
const buildShortcutFallbackResponse = (hasShortcutSupport) => hasShortcutSupport
|
|
44
|
+
? { status: 200, body: { ok: true, mode: "shortcut" } }
|
|
45
|
+
: {
|
|
46
|
+
status: 400,
|
|
47
|
+
body: {
|
|
48
|
+
error: "shortcut input is not supported by this SDK provider",
|
|
49
|
+
},
|
|
50
|
+
};
|
|
37
51
|
expect(buildShortcutFallbackResponse(false)).toEqual({
|
|
38
52
|
status: 400,
|
|
39
|
-
body: { error:
|
|
53
|
+
body: { error: "shortcut input is not supported by this SDK provider" },
|
|
40
54
|
});
|
|
41
55
|
});
|
|
42
|
-
it(
|
|
56
|
+
it("requires sessionId and input for SDK terminal input", () => {
|
|
43
57
|
const validateInputRequest = (body) => {
|
|
44
58
|
if (!body.sessionId || body.input === undefined || body.input === null) {
|
|
45
|
-
return { valid: false, error:
|
|
59
|
+
return { valid: false, error: "sessionId and input are required" };
|
|
46
60
|
}
|
|
47
61
|
return { valid: true };
|
|
48
62
|
};
|
|
49
63
|
expect(validateInputRequest({}).valid).toBe(false);
|
|
50
|
-
expect(validateInputRequest({ sessionId:
|
|
51
|
-
});
|
|
52
|
-
it(
|
|
53
|
-
const buildResizeResponse = (cols, rows) => ({
|
|
54
|
-
|
|
64
|
+
expect(validateInputRequest({ sessionId: "session-1", input: "continue" }).valid).toBe(true);
|
|
65
|
+
});
|
|
66
|
+
it("keeps resize contract as SDK no-op compatibility endpoint", () => {
|
|
67
|
+
const buildResizeResponse = (cols, rows) => ({
|
|
68
|
+
ok: true,
|
|
69
|
+
mode: "sdk",
|
|
70
|
+
noop: true,
|
|
71
|
+
cols,
|
|
72
|
+
rows,
|
|
73
|
+
});
|
|
74
|
+
expect(buildResizeResponse(120, 30)).toEqual({
|
|
75
|
+
ok: true,
|
|
76
|
+
mode: "sdk",
|
|
77
|
+
noop: true,
|
|
78
|
+
cols: 120,
|
|
79
|
+
rows: 30,
|
|
80
|
+
});
|
|
55
81
|
});
|
|
56
|
-
it(
|
|
82
|
+
it("requires sessionId for stop", () => {
|
|
57
83
|
const validateStopRequest = (body) => {
|
|
58
84
|
if (!body.sessionId)
|
|
59
|
-
return { valid: false, error:
|
|
85
|
+
return { valid: false, error: "sessionId is required" };
|
|
60
86
|
return { valid: true };
|
|
61
87
|
};
|
|
62
88
|
expect(validateStopRequest({}).valid).toBe(false);
|
|
63
|
-
expect(validateStopRequest({ sessionId:
|
|
89
|
+
expect(validateStopRequest({ sessionId: "session-1" }).valid).toBe(true);
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
describe("SDK startup idle command ordering", () => {
|
|
93
|
+
it("sets idle commands immediately after adapter startup and before PID probing or persistence", async () => {
|
|
94
|
+
const calls = [];
|
|
95
|
+
const adapter = {
|
|
96
|
+
async startSession() {
|
|
97
|
+
calls.push("startSession");
|
|
98
|
+
},
|
|
99
|
+
setIdleCommands() {
|
|
100
|
+
calls.push("setIdleCommands");
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
const startFlow = async () => {
|
|
104
|
+
await adapter.startSession();
|
|
105
|
+
adapter.setIdleCommands();
|
|
106
|
+
calls.push("probePid");
|
|
107
|
+
calls.push("persistSession");
|
|
108
|
+
};
|
|
109
|
+
await startFlow();
|
|
110
|
+
expect(calls).toEqual([
|
|
111
|
+
"startSession",
|
|
112
|
+
"setIdleCommands",
|
|
113
|
+
"probePid",
|
|
114
|
+
"persistSession",
|
|
115
|
+
]);
|
|
64
116
|
});
|
|
65
117
|
});
|
|
66
|
-
describe(
|
|
67
|
-
it(
|
|
68
|
-
const env = buildRuntimeEnv(
|
|
69
|
-
expect(env.AWS_AGENT_ID).toBe(
|
|
118
|
+
describe("terminal configuration", () => {
|
|
119
|
+
it("builds correct SDK runtime environment", () => {
|
|
120
|
+
const env = buildRuntimeEnv("agent-123", undefined, { PATH: "/usr/bin" });
|
|
121
|
+
expect(env.AWS_AGENT_ID).toBe("agent-123");
|
|
70
122
|
expect(env.AWS_MCP_LAUNCH_BINDING_ID).toBeUndefined();
|
|
71
|
-
expect(env.AWS_MCP_CLAIM_LAUNCH_BINDING).toBe(
|
|
72
|
-
});
|
|
73
|
-
it(
|
|
74
|
-
const env = buildRuntimeEnv(
|
|
75
|
-
ANTHROPIC_BASE_URL:
|
|
76
|
-
ANTHROPIC_AUTH_TOKEN:
|
|
77
|
-
ANTHROPIC_MODEL:
|
|
78
|
-
AWS_AGENT_ID:
|
|
79
|
-
EMPTY_VALUE:
|
|
123
|
+
expect(env.AWS_MCP_CLAIM_LAUNCH_BINDING).toBe("true");
|
|
124
|
+
});
|
|
125
|
+
it("merges launch env overrides without allowing agent identity override", () => {
|
|
126
|
+
const env = buildRuntimeEnv("agent-123", "/workspace", { PATH: "/usr/bin" }, {
|
|
127
|
+
ANTHROPIC_BASE_URL: " https://api.example.com ",
|
|
128
|
+
ANTHROPIC_AUTH_TOKEN: "secret-token",
|
|
129
|
+
ANTHROPIC_MODEL: "claude-sonnet-4-5",
|
|
130
|
+
AWS_AGENT_ID: "malicious-agent",
|
|
131
|
+
EMPTY_VALUE: " ",
|
|
80
132
|
});
|
|
81
|
-
expect(env.ANTHROPIC_BASE_URL).toBe(
|
|
82
|
-
expect(env.ANTHROPIC_AUTH_TOKEN).toBe(
|
|
83
|
-
expect(env.ANTHROPIC_MODEL).toBe(
|
|
133
|
+
expect(env.ANTHROPIC_BASE_URL).toBe("https://api.example.com");
|
|
134
|
+
expect(env.ANTHROPIC_AUTH_TOKEN).toBe("secret-token");
|
|
135
|
+
expect(env.ANTHROPIC_MODEL).toBe("claude-sonnet-4-5");
|
|
84
136
|
expect(env.EMPTY_VALUE).toBeUndefined();
|
|
85
|
-
expect(env.AWS_AGENT_ID).toBe(
|
|
86
|
-
expect(env.AWS_WORKSPACE_PATH).toBe(
|
|
137
|
+
expect(env.AWS_AGENT_ID).toBe("agent-123");
|
|
138
|
+
expect(env.AWS_WORKSPACE_PATH).toBe("/workspace");
|
|
87
139
|
});
|
|
88
|
-
it(
|
|
140
|
+
it("merges saved ClaudeCode AI settings into SDK launch env with request overrides taking precedence", () => {
|
|
89
141
|
const launchConfig = buildClaudeCodeLaunchConfig({
|
|
90
142
|
env: {
|
|
91
|
-
ANTHROPIC_BASE_URL:
|
|
92
|
-
ANTHROPIC_AUTH_TOKEN:
|
|
93
|
-
ANTHROPIC_MODEL:
|
|
94
|
-
EMPTY_SAVED:
|
|
143
|
+
ANTHROPIC_BASE_URL: "https://saved.example.com",
|
|
144
|
+
ANTHROPIC_AUTH_TOKEN: "sk-saved",
|
|
145
|
+
ANTHROPIC_MODEL: "claude-saved",
|
|
146
|
+
EMPTY_SAVED: " ",
|
|
95
147
|
},
|
|
96
148
|
}, {
|
|
97
|
-
ANTHROPIC_MODEL:
|
|
98
|
-
EXTRA_VALUE:
|
|
149
|
+
ANTHROPIC_MODEL: " claude-request ",
|
|
150
|
+
EXTRA_VALUE: "enabled",
|
|
99
151
|
});
|
|
100
152
|
expect(launchConfig).toEqual({
|
|
101
153
|
envOverrides: {
|
|
102
|
-
ANTHROPIC_BASE_URL:
|
|
103
|
-
ANTHROPIC_AUTH_TOKEN:
|
|
104
|
-
ANTHROPIC_MODEL:
|
|
105
|
-
EXTRA_VALUE:
|
|
154
|
+
ANTHROPIC_BASE_URL: "https://saved.example.com",
|
|
155
|
+
ANTHROPIC_AUTH_TOKEN: "sk-saved",
|
|
156
|
+
ANTHROPIC_MODEL: "claude-request",
|
|
157
|
+
EXTRA_VALUE: "enabled",
|
|
106
158
|
},
|
|
107
|
-
model:
|
|
159
|
+
model: "claude-request",
|
|
108
160
|
});
|
|
109
161
|
});
|
|
110
|
-
it(
|
|
162
|
+
it("uses CODEX_MODEL from request env overrides as SDK launch model", () => {
|
|
111
163
|
const launchConfig = buildClaudeCodeLaunchConfig(null, {
|
|
112
|
-
OPENAI_API_KEY:
|
|
113
|
-
CODEX_MODEL:
|
|
164
|
+
OPENAI_API_KEY: "sk-codex",
|
|
165
|
+
CODEX_MODEL: " gpt-5.3-codex ",
|
|
114
166
|
});
|
|
115
167
|
expect(launchConfig).toEqual({
|
|
116
168
|
envOverrides: {
|
|
117
|
-
OPENAI_API_KEY:
|
|
118
|
-
CODEX_MODEL:
|
|
169
|
+
OPENAI_API_KEY: "sk-codex",
|
|
170
|
+
CODEX_MODEL: "gpt-5.3-codex",
|
|
119
171
|
},
|
|
120
|
-
model:
|
|
172
|
+
model: "gpt-5.3-codex",
|
|
121
173
|
});
|
|
122
174
|
});
|
|
123
175
|
});
|
|
124
|
-
describe(
|
|
125
|
-
it(
|
|
176
|
+
describe("persisted SDK session reuse", () => {
|
|
177
|
+
it("does not reuse a persisted session without a PID", () => {
|
|
126
178
|
const decision = evaluatePersistedSessionReuse({}, () => true);
|
|
127
|
-
expect(decision).toEqual({ reusable: false, reason:
|
|
179
|
+
expect(decision).toEqual({ reusable: false, reason: "missing-pid" });
|
|
128
180
|
});
|
|
129
|
-
it(
|
|
181
|
+
it("does not reuse a persisted session when its PID is no longer running", () => {
|
|
130
182
|
const decision = evaluatePersistedSessionReuse({ pid: 1234 }, () => false);
|
|
131
|
-
expect(decision).toEqual({ reusable: false, reason:
|
|
183
|
+
expect(decision).toEqual({ reusable: false, reason: "dead-pid" });
|
|
184
|
+
});
|
|
185
|
+
it("reuses a persisted session only when its PID is still running for externally controllable providers", () => {
|
|
186
|
+
const decision = evaluatePersistedSessionReuse({ pid: 1234, command: "claude" }, () => true);
|
|
187
|
+
expect(decision).toEqual({ reusable: true, reason: "active-pid" });
|
|
132
188
|
});
|
|
133
|
-
it(
|
|
134
|
-
const decision = evaluatePersistedSessionReuse({ pid: 1234 }, () => true);
|
|
135
|
-
expect(decision).toEqual({
|
|
189
|
+
it("does not reuse ACode persisted sessions because their control channel is in-process only", () => {
|
|
190
|
+
const decision = evaluatePersistedSessionReuse({ pid: 1234, command: "acode" }, () => true);
|
|
191
|
+
expect(decision).toEqual({
|
|
192
|
+
reusable: false,
|
|
193
|
+
reason: "in-process-provider",
|
|
194
|
+
});
|
|
136
195
|
});
|
|
137
196
|
});
|
|
138
|
-
describe(
|
|
139
|
-
it(
|
|
140
|
-
|
|
141
|
-
|
|
197
|
+
describe("SDK session display status", () => {
|
|
198
|
+
it("prefers adapter fine-grained status over coarse running fallback", () => {
|
|
199
|
+
expect(resolveSdkSessionDisplayStatus("waiting_input", "running")).toBe("waiting_input");
|
|
200
|
+
expect(resolveSdkSessionDisplayStatus("thinking", "running")).toBe("thinking");
|
|
201
|
+
});
|
|
202
|
+
it("uses in-memory runtime status when adapter status is temporarily unavailable", () => {
|
|
203
|
+
expect(resolveSdkSessionDisplayStatus(undefined, "tool_using")).toBe("tool_using");
|
|
204
|
+
});
|
|
205
|
+
it("falls back to running only when no fine-grained state exists", () => {
|
|
206
|
+
expect(resolveSdkSessionDisplayStatus(undefined, undefined)).toBe("running");
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
describe("SDK provider event runtime status mapping", () => {
|
|
210
|
+
const event = (type, data = {}) => ({
|
|
211
|
+
type,
|
|
212
|
+
sessionId: "session-1",
|
|
213
|
+
timestamp: new Date().toISOString(),
|
|
214
|
+
data,
|
|
215
|
+
});
|
|
216
|
+
it("maps text and thinking stream events to thinking status", () => {
|
|
217
|
+
expect(resolveRuntimeStatusFromProviderEvent(event("thinking"))?.status).toBe("thinking");
|
|
218
|
+
expect(resolveRuntimeStatusFromProviderEvent(event("text_delta"))?.status).toBe("thinking");
|
|
219
|
+
});
|
|
220
|
+
it("maps AI request events to thinking status with request details", () => {
|
|
221
|
+
const state = resolveRuntimeStatusFromProviderEvent(event("ai_request", {
|
|
222
|
+
model: "test-model",
|
|
223
|
+
endpoint: "https://example.invalid/v1/chat/completions",
|
|
224
|
+
stream: true,
|
|
225
|
+
messageCount: 3,
|
|
226
|
+
toolCount: 2,
|
|
227
|
+
lastUserMessagePreview: "inspect status",
|
|
228
|
+
requestBody: '{"model":"test-model"}',
|
|
229
|
+
}));
|
|
230
|
+
expect(state?.status).toBe("thinking");
|
|
231
|
+
expect(state?.actionInfo).toMatchObject({
|
|
232
|
+
actionType: "ai_request",
|
|
233
|
+
actionLabel: "AI请求",
|
|
234
|
+
});
|
|
235
|
+
expect(state?.actionInfo?.actionDetail).toContain("模型: test-model");
|
|
236
|
+
expect(state?.actionInfo?.actionDetail).toContain("接口: https://example.invalid/v1/chat/completions");
|
|
237
|
+
expect(state?.actionInfo?.actionDetail).toContain("最后请求: inspect status");
|
|
238
|
+
expect(state?.actionInfo?.actionDetail).toContain('请求体:\n{"model":"test-model"}');
|
|
239
|
+
});
|
|
240
|
+
it("maps tool start events to tool_using with action info", () => {
|
|
241
|
+
const state = resolveRuntimeStatusFromProviderEvent(event("tool_use_start", {
|
|
242
|
+
toolName: "mcp__aws__my_task",
|
|
243
|
+
toolUseId: "tool-1",
|
|
244
|
+
}));
|
|
245
|
+
expect(state?.status).toBe("tool_using");
|
|
246
|
+
expect(state?.actionInfo?.actionType).toBe("mcp");
|
|
247
|
+
expect(state?.actionInfo?.actionId).toBe("tool-1");
|
|
248
|
+
});
|
|
249
|
+
it("maps turn completion to idle waiting_input status", () => {
|
|
250
|
+
expect(resolveRuntimeStatusFromProviderEvent(event("turn_complete"))?.status).toBe("waiting_input");
|
|
251
|
+
});
|
|
252
|
+
it("keeps idle tool action info with waiting_input status for display", () => {
|
|
253
|
+
const state = resolveRuntimeStatusFromProviderEvent(event("tool_use_start", {
|
|
254
|
+
actionType: "idle",
|
|
255
|
+
actionLabel: "监听消息",
|
|
256
|
+
actionDetail: "poll_message",
|
|
257
|
+
toolUseId: "tool-idle-1",
|
|
258
|
+
}));
|
|
259
|
+
expect(state?.status).toBe("waiting_input");
|
|
260
|
+
expect(state?.actionInfo).toMatchObject({
|
|
261
|
+
actionType: "idle",
|
|
262
|
+
actionLabel: "监听消息",
|
|
263
|
+
actionDetail: "poll_message",
|
|
264
|
+
actionId: "tool-idle-1",
|
|
265
|
+
});
|
|
266
|
+
});
|
|
267
|
+
});
|
|
268
|
+
describe("terminal command helpers", () => {
|
|
269
|
+
it("expands a whole mini-term clipboard paste file path into SDK input content", async () => {
|
|
270
|
+
const tempDir = await mkdtemp(path.join(os.tmpdir(), "terminal-input-"));
|
|
271
|
+
const clipboardDir = path.join(tempDir, "mini-term-clipboard");
|
|
142
272
|
await mkdir(clipboardDir, { recursive: true });
|
|
143
|
-
const pasteFile = path.join(clipboardDir,
|
|
144
|
-
await writeFile(pasteFile,
|
|
145
|
-
await expect(resolveSdkInputMessage(`"${pasteFile}"`)).resolves.toBe(
|
|
273
|
+
const pasteFile = path.join(clipboardDir, "paste-1780491671137.txt");
|
|
274
|
+
await writeFile(pasteFile, "real pasted prompt", "utf-8");
|
|
275
|
+
await expect(resolveSdkInputMessage(`"${pasteFile}"`)).resolves.toBe("real pasted prompt");
|
|
146
276
|
});
|
|
147
|
-
it(
|
|
277
|
+
it("keeps explicit commands that mention mini-term clipboard files unchanged", async () => {
|
|
148
278
|
const command = 'type "C:\\Users\\ZHUANG~1\\AppData\\Local\\Temp\\mini-term-clipboard\\paste-1780491671137.txt"';
|
|
149
279
|
await expect(resolveSdkInputMessage(command)).resolves.toBe(command);
|
|
150
280
|
});
|
|
151
|
-
it(
|
|
152
|
-
expect(normalizeTerminalCommandInput(
|
|
153
|
-
expect(normalizeTerminalCommandInput(
|
|
281
|
+
it("normalizes carriage-return terminal input into executable command text", () => {
|
|
282
|
+
expect(normalizeTerminalCommandInput("npm test\r")).toBe("npm test");
|
|
283
|
+
expect(normalizeTerminalCommandInput(" pwd \n")).toBe("pwd");
|
|
154
284
|
});
|
|
155
|
-
it(
|
|
156
|
-
expect(parseTerminalDirectoryChangeTarget(
|
|
157
|
-
expect(parseTerminalDirectoryChangeTarget('cd /d "D:\\code\\repo"')).toBe(
|
|
158
|
-
expect(parseTerminalDirectoryChangeTarget(
|
|
285
|
+
it("parses cd commands without treating other shell commands as directory changes", () => {
|
|
286
|
+
expect(parseTerminalDirectoryChangeTarget("cd")).toBeNull();
|
|
287
|
+
expect(parseTerminalDirectoryChangeTarget('cd /d "D:\\code\\repo"')).toBe("D:\\code\\repo");
|
|
288
|
+
expect(parseTerminalDirectoryChangeTarget("npm run build")).toBeUndefined();
|
|
159
289
|
});
|
|
160
|
-
it(
|
|
161
|
-
expect(formatTerminalPrompt(
|
|
290
|
+
it("formats the working directory prompt as terminal output text", () => {
|
|
291
|
+
expect(formatTerminalPrompt("D:\\code\\repo")).toBe("\x1b[36mD:\\code\\repo>\x1b[0m ");
|
|
162
292
|
});
|
|
163
|
-
it(
|
|
164
|
-
expect(resolveTerminalOutputEncoding(
|
|
165
|
-
expect(resolveTerminalOutputEncoding(
|
|
166
|
-
expect(resolveTerminalOutputEncoding(
|
|
293
|
+
it("defaults Windows terminal command output to GB18030 decoding", () => {
|
|
294
|
+
expect(resolveTerminalOutputEncoding("win32", {})).toBe("gb18030");
|
|
295
|
+
expect(resolveTerminalOutputEncoding("linux", {})).toBe("utf-8");
|
|
296
|
+
expect(resolveTerminalOutputEncoding("win32", {
|
|
297
|
+
AWS_TERMINAL_OUTPUT_ENCODING: "utf-8",
|
|
298
|
+
})).toBe("utf-8");
|
|
167
299
|
});
|
|
168
|
-
it(
|
|
169
|
-
const decoder = createTerminalOutputDecoder(
|
|
300
|
+
it("decodes GB18030 Chinese output across chunk boundaries", () => {
|
|
301
|
+
const decoder = createTerminalOutputDecoder("gb18030");
|
|
170
302
|
const first = decodeTerminalOutputChunk(decoder, Buffer.from([0xd6]));
|
|
171
303
|
const second = decodeTerminalOutputChunk(decoder, Buffer.from([0xd0, 0xce, 0xc4]));
|
|
172
304
|
const flushed = decodeTerminalOutputChunk(decoder, undefined, true);
|
|
173
|
-
expect(`${first}${second}${flushed}`).toBe(
|
|
305
|
+
expect(`${first}${second}${flushed}`).toBe("中文");
|
|
306
|
+
});
|
|
307
|
+
it("counts terminal output lines without treating a trailing line break as an extra line", () => {
|
|
308
|
+
expect(countTerminalOutputLines("")).toBe(0);
|
|
309
|
+
expect(countTerminalOutputLines("one")).toBe(1);
|
|
310
|
+
expect(countTerminalOutputLines("one\ntwo\n")).toBe(2);
|
|
311
|
+
expect(countTerminalOutputLines("one\r\ntwo\rthree")).toBe(3);
|
|
312
|
+
});
|
|
313
|
+
it("builds ACode command output spillover paths under the workspace cmd-dist directory", () => {
|
|
314
|
+
const outputPath = buildAcodeCommandOutputPath(path.join("D:", "code", "repo"), new Date(2026, 1, 3, 4, 5, 6, 7));
|
|
315
|
+
expect(outputPath).toBe(path.join("D:", "code", "repo", ".agentswork", "acode", "cmd-dist", "2026-02-03", "04", "05", "20260203-040506-007-cmd.out"));
|
|
316
|
+
});
|
|
317
|
+
it("formats ACode long-output and inactivity notices for terminal users", () => {
|
|
318
|
+
expect(formatCommandOutputSpilloverNotice("/repo/out.txt", 100)).toContain("命令输出超过 100 行");
|
|
319
|
+
expect(formatCommandOutputSpilloverNotice("/repo/out.txt", 100)).toContain("/repo/out.txt");
|
|
320
|
+
expect(formatCommandInactivityTimeoutNotice(60_000)).toContain("超过 60 秒没有新的输出");
|
|
174
321
|
});
|
|
175
322
|
});
|
|
176
|
-
describe(
|
|
177
|
-
it(
|
|
178
|
-
expect(SDK_PROVIDER_DEFINITIONS.map((definition) => definition.runtimeMode)).toContain(
|
|
179
|
-
expect(SDK_PROVIDER_DEFINITIONS.find((definition) => definition.runtimeMode ===
|
|
180
|
-
providerId:
|
|
181
|
-
defaultCommand:
|
|
323
|
+
describe("resolveSdkProviderId", () => {
|
|
324
|
+
it("exposes codex through the SDK provider SPI", () => {
|
|
325
|
+
expect(SDK_PROVIDER_DEFINITIONS.map((definition) => definition.runtimeMode)).toContain("codex");
|
|
326
|
+
expect(SDK_PROVIDER_DEFINITIONS.find((definition) => definition.runtimeMode === "codex")).toMatchObject({
|
|
327
|
+
providerId: "codex",
|
|
328
|
+
defaultCommand: "codex",
|
|
182
329
|
});
|
|
183
330
|
});
|
|
184
|
-
it(
|
|
185
|
-
expect(resolveSdkProviderId(
|
|
186
|
-
expect(resolveSdkProviderId(
|
|
331
|
+
it("resolves codex commands to codex provider", () => {
|
|
332
|
+
expect(resolveSdkProviderId("codex")).toBe("codex");
|
|
333
|
+
expect(resolveSdkProviderId("npx codex --model gpt-5-codex")).toBe("codex");
|
|
187
334
|
});
|
|
188
|
-
it(
|
|
189
|
-
expect(resolveSdkProviderId(
|
|
335
|
+
it("resolves opencode commands to opencode provider", () => {
|
|
336
|
+
expect(resolveSdkProviderId("opencode")).toBe("opencode");
|
|
190
337
|
});
|
|
191
|
-
it(
|
|
192
|
-
expect(resolveSdkProviderId(
|
|
193
|
-
expect(resolveSdkProviderId(
|
|
194
|
-
expect(resolveSdkProviderId(
|
|
338
|
+
it("falls back to acode for unknown or empty commands while keeping claude mapped to claude-code", () => {
|
|
339
|
+
expect(resolveSdkProviderId("claude")).toBe("claude-code");
|
|
340
|
+
expect(resolveSdkProviderId("acode")).toBe("acode");
|
|
341
|
+
expect(resolveSdkProviderId("custom-binary")).toBe("acode");
|
|
342
|
+
expect(resolveSdkProviderId(undefined)).toBe("acode");
|
|
195
343
|
});
|
|
196
344
|
});
|
|
197
|
-
describe(
|
|
198
|
-
it(
|
|
199
|
-
expect(resolveStatusChangeUsage({
|
|
345
|
+
describe("SDK status usage forwarding", () => {
|
|
346
|
+
it("keeps accumulated usage from status-change action info", () => {
|
|
347
|
+
expect(resolveStatusChangeUsage({
|
|
348
|
+
usage: { inputTokens: 12000, outputTokens: 345 },
|
|
349
|
+
})).toEqual({
|
|
200
350
|
inputTokens: 12000,
|
|
201
351
|
outputTokens: 345,
|
|
202
352
|
});
|
|
203
353
|
expect(resolveStatusChangeUsage({ usage: { inputTokens: 0, outputTokens: 0 } })).toBeUndefined();
|
|
204
|
-
expect(resolveStatusChangeUsage({
|
|
354
|
+
expect(resolveStatusChangeUsage({
|
|
355
|
+
actionType: "mcp",
|
|
356
|
+
actionLabel: "Calling MCP",
|
|
357
|
+
})).toBeUndefined();
|
|
358
|
+
});
|
|
359
|
+
it("forwards structured AI response body through action result", () => {
|
|
360
|
+
const runtimeStatus = resolveRuntimeStatusFromProviderEvent({
|
|
361
|
+
type: "turn_complete",
|
|
362
|
+
sessionId: "session-1",
|
|
363
|
+
timestamp: new Date(0).toISOString(),
|
|
364
|
+
data: {
|
|
365
|
+
usage: { inputTokens: 12, outputTokens: 4 },
|
|
366
|
+
responseBody: '{"choices":[{"message":{"content":"done"}}]}',
|
|
367
|
+
finishReason: "stop",
|
|
368
|
+
},
|
|
369
|
+
});
|
|
370
|
+
expect(runtimeStatus).toEqual({
|
|
371
|
+
status: "waiting_input",
|
|
372
|
+
actionInfo: {
|
|
373
|
+
actionType: "ai_request",
|
|
374
|
+
actionLabel: "AI响应",
|
|
375
|
+
actionResult: '{"choices":[{"message":{"content":"done"}}]}',
|
|
376
|
+
usage: { inputTokens: 12, outputTokens: 4 },
|
|
377
|
+
},
|
|
378
|
+
});
|
|
205
379
|
});
|
|
206
380
|
});
|
|
207
|
-
describe(
|
|
208
|
-
it(
|
|
381
|
+
describe("SDK output forwarding", () => {
|
|
382
|
+
it("formats assistant text provider events for the terminal output callback path", () => {
|
|
209
383
|
const textEvent = {
|
|
210
|
-
type:
|
|
211
|
-
sessionId:
|
|
384
|
+
type: "text_delta",
|
|
385
|
+
sessionId: "session-1",
|
|
212
386
|
timestamp: new Date(0).toISOString(),
|
|
213
|
-
data: { text:
|
|
387
|
+
data: { text: "hello" },
|
|
214
388
|
};
|
|
389
|
+
expect(formatSdkOutputEvent(textEvent)).toBe("hello");
|
|
390
|
+
});
|
|
391
|
+
it("formats thinking provider events as typed realtime output chunks", () => {
|
|
215
392
|
const thinkingEvent = {
|
|
216
|
-
type:
|
|
217
|
-
sessionId:
|
|
393
|
+
type: "thinking",
|
|
394
|
+
sessionId: "session-1",
|
|
218
395
|
timestamp: new Date(0).toISOString(),
|
|
219
|
-
data: { text:
|
|
396
|
+
data: { text: "internal prompt or reasoning" },
|
|
220
397
|
};
|
|
221
|
-
expect(formatSdkOutputEvent(
|
|
222
|
-
expect(formatSdkOutputEvent(thinkingEvent)).toBe('thinking...');
|
|
398
|
+
expect(formatSdkOutputEvent(thinkingEvent)).toBe("internal prompt or reasoning");
|
|
223
399
|
});
|
|
224
|
-
it(
|
|
400
|
+
it("formats SDK errors as terminal output and ignores non-output events", () => {
|
|
225
401
|
const errorEvent = {
|
|
226
|
-
type:
|
|
227
|
-
sessionId:
|
|
402
|
+
type: "error",
|
|
403
|
+
sessionId: "session-1",
|
|
228
404
|
timestamp: new Date(0).toISOString(),
|
|
229
|
-
data: { text:
|
|
405
|
+
data: { text: "boom" },
|
|
230
406
|
};
|
|
231
407
|
const toolEvent = {
|
|
232
|
-
type:
|
|
233
|
-
sessionId:
|
|
408
|
+
type: "tool_use_start",
|
|
409
|
+
sessionId: "session-1",
|
|
234
410
|
timestamp: new Date(0).toISOString(),
|
|
235
|
-
data: { toolName:
|
|
411
|
+
data: { toolName: "read_file" },
|
|
236
412
|
};
|
|
237
|
-
expect(formatSdkOutputEvent(errorEvent)).toBe(
|
|
413
|
+
expect(formatSdkOutputEvent(errorEvent)).toBe("\r\n[SDK Error] boom\r\n");
|
|
238
414
|
expect(formatSdkOutputEvent(toolEvent)).toBeUndefined();
|
|
239
415
|
});
|
|
416
|
+
it("formats AI request events as immediate realtime ACode output", () => {
|
|
417
|
+
const requestEvent = {
|
|
418
|
+
type: "ai_request",
|
|
419
|
+
sessionId: "session-1",
|
|
420
|
+
timestamp: new Date(0).toISOString(),
|
|
421
|
+
data: {
|
|
422
|
+
model: "test-model",
|
|
423
|
+
lastUserMessagePreview: "inspect status",
|
|
424
|
+
},
|
|
425
|
+
};
|
|
426
|
+
expect(formatSdkOutputEvent(requestEvent)).toBe("\r\n[ACode] 正在请求模型 test-model:inspect status\r\n");
|
|
427
|
+
});
|
|
240
428
|
});
|
|
241
|
-
describe(
|
|
242
|
-
it(
|
|
429
|
+
describe("SDK tool result timeline action info", () => {
|
|
430
|
+
it("keeps MCP tool results on MCP timeline events", () => {
|
|
243
431
|
const event = {
|
|
244
|
-
type:
|
|
245
|
-
sessionId:
|
|
432
|
+
type: "tool_use_end",
|
|
433
|
+
sessionId: "session-1",
|
|
246
434
|
timestamp: new Date(0).toISOString(),
|
|
247
435
|
data: {
|
|
248
|
-
toolName:
|
|
249
|
-
toolUseId:
|
|
436
|
+
toolName: "mcp__aws-mcp__get_profile",
|
|
437
|
+
toolUseId: "tool-1",
|
|
250
438
|
toolResult: '{"displayName":"Agent"}',
|
|
251
439
|
},
|
|
252
440
|
};
|
|
253
441
|
expect(buildToolResultTimelineActionInfo(event)).toEqual({
|
|
254
|
-
actionType:
|
|
255
|
-
actionLabel:
|
|
256
|
-
actionDetail:
|
|
257
|
-
actionId:
|
|
442
|
+
actionType: "mcp",
|
|
443
|
+
actionLabel: "MCP返回",
|
|
444
|
+
actionDetail: "mcp__aws-mcp__get_profile",
|
|
445
|
+
actionId: "tool-1",
|
|
258
446
|
actionResult: '{"displayName":"Agent"}',
|
|
259
447
|
});
|
|
260
448
|
});
|
|
261
|
-
it(
|
|
449
|
+
it("forwards bash tool results to command timeline events", () => {
|
|
262
450
|
const event = {
|
|
263
|
-
type:
|
|
264
|
-
sessionId:
|
|
451
|
+
type: "tool_use_end",
|
|
452
|
+
sessionId: "session-1",
|
|
265
453
|
timestamp: new Date(0).toISOString(),
|
|
266
454
|
data: {
|
|
267
|
-
toolName:
|
|
268
|
-
toolInput: { command:
|
|
269
|
-
toolUseId:
|
|
270
|
-
toolResult:
|
|
455
|
+
toolName: "bash",
|
|
456
|
+
toolInput: { command: "npm run build" },
|
|
457
|
+
toolUseId: "tool-2",
|
|
458
|
+
toolResult: "build passed",
|
|
271
459
|
},
|
|
272
460
|
};
|
|
273
461
|
expect(buildToolResultTimelineActionInfo(event)).toEqual({
|
|
274
|
-
actionType:
|
|
275
|
-
actionLabel:
|
|
276
|
-
actionDetail:
|
|
277
|
-
actionId:
|
|
278
|
-
actionResult:
|
|
462
|
+
actionType: "bash",
|
|
463
|
+
actionLabel: "执行命令",
|
|
464
|
+
actionDetail: "npm run build",
|
|
465
|
+
actionId: "tool-2",
|
|
466
|
+
actionResult: "build passed",
|
|
279
467
|
});
|
|
280
468
|
});
|
|
281
|
-
it(
|
|
469
|
+
it("forwards read tool results to file-read timeline events", () => {
|
|
282
470
|
const event = {
|
|
283
|
-
type:
|
|
284
|
-
sessionId:
|
|
471
|
+
type: "tool_use_end",
|
|
472
|
+
sessionId: "session-1",
|
|
285
473
|
timestamp: new Date(0).toISOString(),
|
|
286
474
|
data: {
|
|
287
|
-
toolName:
|
|
288
|
-
toolInput: { path:
|
|
289
|
-
actionId:
|
|
290
|
-
actionResult:
|
|
475
|
+
toolName: "read_file",
|
|
476
|
+
toolInput: { path: "src/index.ts" },
|
|
477
|
+
actionId: "tool-3",
|
|
478
|
+
actionResult: "export const ok = true",
|
|
291
479
|
},
|
|
292
480
|
};
|
|
293
481
|
expect(buildToolResultTimelineActionInfo(event)).toEqual({
|
|
294
|
-
actionType:
|
|
295
|
-
actionLabel:
|
|
296
|
-
actionDetail:
|
|
297
|
-
actionId:
|
|
298
|
-
actionResult:
|
|
482
|
+
actionType: "read_file",
|
|
483
|
+
actionLabel: "读取文件",
|
|
484
|
+
actionDetail: "src/index.ts",
|
|
485
|
+
actionId: "tool-3",
|
|
486
|
+
actionResult: "export const ok = true",
|
|
299
487
|
});
|
|
300
488
|
});
|
|
301
|
-
it(
|
|
489
|
+
it("keeps MCP-named filesystem results on semantic file-read timeline events", () => {
|
|
302
490
|
const event = {
|
|
303
|
-
type:
|
|
304
|
-
sessionId:
|
|
491
|
+
type: "tool_use_end",
|
|
492
|
+
sessionId: "session-1",
|
|
305
493
|
timestamp: new Date(0).toISOString(),
|
|
306
494
|
data: {
|
|
307
|
-
toolName:
|
|
308
|
-
toolInput: { path:
|
|
309
|
-
toolUseId:
|
|
310
|
-
toolResult:
|
|
495
|
+
toolName: "mcp__filesystem__read_file",
|
|
496
|
+
toolInput: { path: "src/index.ts" },
|
|
497
|
+
toolUseId: "tool-4",
|
|
498
|
+
toolResult: "export const ok = true",
|
|
311
499
|
},
|
|
312
500
|
};
|
|
313
501
|
expect(buildToolResultTimelineActionInfo(event)).toEqual({
|
|
314
|
-
actionType:
|
|
315
|
-
actionLabel:
|
|
316
|
-
actionDetail:
|
|
317
|
-
actionId:
|
|
318
|
-
actionResult:
|
|
502
|
+
actionType: "read_file",
|
|
503
|
+
actionLabel: "读取文件",
|
|
504
|
+
actionDetail: "src/index.ts",
|
|
505
|
+
actionId: "tool-4",
|
|
506
|
+
actionResult: "export const ok = true",
|
|
319
507
|
});
|
|
320
508
|
});
|
|
321
|
-
it(
|
|
509
|
+
it("ignores tool result events without displayable result content", () => {
|
|
322
510
|
const event = {
|
|
323
|
-
type:
|
|
324
|
-
sessionId:
|
|
511
|
+
type: "tool_use_end",
|
|
512
|
+
sessionId: "session-1",
|
|
325
513
|
timestamp: new Date(0).toISOString(),
|
|
326
|
-
data: { toolName:
|
|
514
|
+
data: { toolName: "read_file", toolResult: " " },
|
|
327
515
|
};
|
|
328
516
|
expect(buildToolResultTimelineActionInfo(event)).toBeUndefined();
|
|
329
517
|
});
|