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.
Files changed (199) hide show
  1. package/dist/adapter/AcodeSdkAdapter.d.ts +104 -0
  2. package/dist/adapter/AcodeSdkAdapter.d.ts.map +1 -0
  3. package/dist/adapter/AcodeSdkAdapter.js +644 -0
  4. package/dist/adapter/AcodeSdkAdapter.test.d.ts +2 -0
  5. package/dist/adapter/AcodeSdkAdapter.test.d.ts.map +1 -0
  6. package/dist/adapter/AcodeSdkAdapter.test.js +1280 -0
  7. package/dist/adapter/AdapterRegistry.d.ts +1 -1
  8. package/dist/adapter/AdapterRegistry.d.ts.map +1 -1
  9. package/dist/adapter/AdapterRegistry.js +2 -0
  10. package/dist/adapter/AdapterRegistry.test.js +10 -1
  11. package/dist/adapter/ClaudeSdkAdapter.test.js +31 -8
  12. package/dist/adapter/CodexSdkAdapter.d.ts.map +1 -1
  13. package/dist/adapter/CodexSdkAdapter.js +2 -0
  14. package/dist/adapter/OpencodeSdkAdapter.d.ts.map +1 -1
  15. package/dist/adapter/OpencodeSdkAdapter.js +2 -0
  16. package/dist/adapter/SdkProviderSpi.d.ts.map +1 -1
  17. package/dist/adapter/SdkProviderSpi.js +10 -1
  18. package/dist/adapter/adapter.test.js +2 -2
  19. package/dist/adapter/idle-poll-loop.d.ts +4 -4
  20. package/dist/adapter/idle-poll-loop.d.ts.map +1 -1
  21. package/dist/adapter/idle-poll-loop.js +3 -3
  22. package/dist/adapter/idle-poll-loop.test.js +15 -1
  23. package/dist/adapter/index.d.ts +1 -0
  24. package/dist/adapter/index.d.ts.map +1 -1
  25. package/dist/adapter/index.js +1 -0
  26. package/dist/adapter/types.d.ts +25 -6
  27. package/dist/adapter/types.d.ts.map +1 -1
  28. package/dist/adapter/types.js +221 -110
  29. package/dist/adapter/types.test.js +10 -3
  30. package/dist/adapters/cc-switch/index.d.ts +2 -0
  31. package/dist/adapters/cc-switch/index.d.ts.map +1 -1
  32. package/dist/adapters/cc-switch/index.js +6 -0
  33. package/dist/adapters/cc-switch/mcp-acode.d.ts +17 -0
  34. package/dist/adapters/cc-switch/mcp-acode.d.ts.map +1 -0
  35. package/dist/adapters/cc-switch/mcp-acode.js +71 -0
  36. package/dist/adapters/cc-switch/mcp-acode.test.d.ts +2 -0
  37. package/dist/adapters/cc-switch/mcp-acode.test.d.ts.map +1 -0
  38. package/dist/adapters/cc-switch/mcp-acode.test.js +80 -0
  39. package/dist/adapters/cc-switch/mcp-claude.d.ts.map +1 -1
  40. package/dist/adapters/cc-switch/mcp-claude.js +1 -0
  41. package/dist/adapters/cc-switch/mcp-claudecode.d.ts.map +1 -1
  42. package/dist/adapters/cc-switch/mcp-claudecode.js +1 -0
  43. package/dist/adapters/cc-switch/mcp-claudecode.test.js +1 -0
  44. package/dist/adapters/cc-switch/mcp-codex.d.ts.map +1 -1
  45. package/dist/adapters/cc-switch/mcp-codex.js +1 -0
  46. package/dist/adapters/cc-switch/mcp-codex.test.js +1 -0
  47. package/dist/adapters/cc-switch/mcp-opencode.d.ts.map +1 -1
  48. package/dist/adapters/cc-switch/mcp-opencode.js +1 -0
  49. package/dist/adapters/cc-switch/mcp-opencode.test.js +1 -0
  50. package/dist/adapters/cc-switch/skill-acode.d.ts +11 -0
  51. package/dist/adapters/cc-switch/skill-acode.d.ts.map +1 -0
  52. package/dist/adapters/cc-switch/skill-acode.js +12 -0
  53. package/dist/adapters/cc-switch/skill-directory.test.js +1 -0
  54. package/dist/config.d.ts +4 -0
  55. package/dist/config.d.ts.map +1 -1
  56. package/dist/config.js +10 -0
  57. package/dist/index.js +2 -0
  58. package/dist/routes/ai-sources.js +3 -2
  59. package/dist/routes/instance.d.ts.map +1 -1
  60. package/dist/routes/instance.js +7 -8
  61. package/dist/routes/instance.test.js +2 -1
  62. package/dist/routes/mcp.js +2 -2
  63. package/dist/routes/mcp.test.js +2 -1
  64. package/dist/routes/runtime-binding.d.ts +3 -2
  65. package/dist/routes/runtime-binding.d.ts.map +1 -1
  66. package/dist/routes/runtime-binding.js +11 -58
  67. package/dist/routes/runtime-binding.test.js +4 -80
  68. package/dist/routes/sessions.d.ts +5 -0
  69. package/dist/routes/sessions.d.ts.map +1 -1
  70. package/dist/routes/sessions.js +36 -16
  71. package/dist/routes/sessions.test.js +13 -0
  72. package/dist/routes/skills.js +2 -2
  73. package/dist/routes/skills.test.js +2 -1
  74. package/dist/routes/terminal.d.ts +27 -7
  75. package/dist/routes/terminal.d.ts.map +1 -1
  76. package/dist/routes/terminal.js +439 -171
  77. package/dist/routes/terminal.test.js +385 -197
  78. package/dist/services/acode-package.d.ts +8 -0
  79. package/dist/services/acode-package.d.ts.map +1 -0
  80. package/dist/services/acode-package.js +82 -0
  81. package/dist/services/ai-source-apply.d.ts +4 -0
  82. package/dist/services/ai-source-apply.d.ts.map +1 -1
  83. package/dist/services/ai-source-apply.js +8 -0
  84. package/dist/services/ai-source-apply.test.js +8 -1
  85. package/dist/services/cc-switch-sdk.d.ts.map +1 -1
  86. package/dist/services/cc-switch-sdk.js +9 -1
  87. package/dist/services/instance-init-service.d.ts.map +1 -1
  88. package/dist/services/instance-init-service.js +3 -2
  89. package/dist/services/instance-init-service.test.js +6 -5
  90. package/dist/services/instance-service.d.ts.map +1 -1
  91. package/dist/services/instance-service.js +1 -0
  92. package/dist/services/instance-state.js +1 -1
  93. package/dist/services/instance-state.test.js +4 -4
  94. package/dist/services/mcp-launch-binding-queue.d.ts +1 -0
  95. package/dist/services/mcp-launch-binding-queue.d.ts.map +1 -1
  96. package/dist/services/mcp-launch-binding-queue.js +8 -2
  97. package/dist/services/mcp-launch-binding-queue.test.js +47 -4
  98. package/dist/services/session-output.d.ts +10 -6
  99. package/dist/services/session-output.d.ts.map +1 -1
  100. package/dist/services/session-output.js +29 -14
  101. package/dist/services/session-output.test.js +93 -39
  102. package/dist/services/tool-installer.d.ts.map +1 -1
  103. package/dist/services/tool-installer.js +10 -0
  104. package/dist/types.d.ts +5 -0
  105. package/dist/types.d.ts.map +1 -1
  106. package/dist/utils/file-utils.d.ts.map +1 -1
  107. package/dist/utils/file-utils.js +1 -0
  108. package/dist/utils/mcp-utils.d.ts.map +1 -1
  109. package/dist/utils/mcp-utils.js +4 -1
  110. package/dist/utils/validation.js +1 -1
  111. package/node_modules/@cc-switch/sdk/dist/constants.d.ts.map +1 -1
  112. package/node_modules/@cc-switch/sdk/dist/constants.js +7 -0
  113. package/node_modules/@cc-switch/sdk/dist/schemas.d.ts.map +1 -1
  114. package/node_modules/@cc-switch/sdk/dist/schemas.js +1 -0
  115. package/node_modules/@cc-switch/sdk/dist/sdk.d.ts.map +1 -1
  116. package/node_modules/@cc-switch/sdk/dist/sdk.js +7 -3
  117. package/node_modules/@cc-switch/sdk/dist/services/ai-config-service.d.ts +6 -0
  118. package/node_modules/@cc-switch/sdk/dist/services/ai-config-service.d.ts.map +1 -1
  119. package/node_modules/@cc-switch/sdk/dist/services/ai-config-service.js +27 -1
  120. package/node_modules/@cc-switch/sdk/dist/types.d.ts +4 -1
  121. package/node_modules/@cc-switch/sdk/dist/types.d.ts.map +1 -1
  122. package/package/acode/README.md +88 -0
  123. package/package/acode/dist/built-in-file-tools.d.ts +16 -0
  124. package/package/acode/dist/built-in-file-tools.d.ts.map +1 -0
  125. package/package/acode/dist/built-in-file-tools.js +569 -0
  126. package/package/acode/dist/cli.d.ts +3 -0
  127. package/package/acode/dist/cli.d.ts.map +1 -0
  128. package/package/acode/dist/cli.js +37 -0
  129. package/package/acode/dist/commands/commandLoader.d.ts +7 -0
  130. package/package/acode/dist/commands/commandLoader.d.ts.map +1 -0
  131. package/package/acode/dist/commands/commandLoader.js +33 -0
  132. package/package/acode/dist/commands/commandParser.d.ts +7 -0
  133. package/package/acode/dist/commands/commandParser.d.ts.map +1 -0
  134. package/package/acode/dist/commands/commandParser.js +77 -0
  135. package/package/acode/dist/commands/commandRegistry.d.ts +13 -0
  136. package/package/acode/dist/commands/commandRegistry.d.ts.map +1 -0
  137. package/package/acode/dist/commands/commandRegistry.js +30 -0
  138. package/package/acode/dist/config.d.ts +4 -0
  139. package/package/acode/dist/config.d.ts.map +1 -0
  140. package/package/acode/dist/config.js +80 -0
  141. package/package/acode/dist/hooks/hookBus.d.ts +19 -0
  142. package/package/acode/dist/hooks/hookBus.d.ts.map +1 -0
  143. package/package/acode/dist/hooks/hookBus.js +40 -0
  144. package/package/acode/dist/index.d.ts +8 -0
  145. package/package/acode/dist/index.d.ts.map +1 -0
  146. package/package/acode/dist/index.js +7 -0
  147. package/package/acode/dist/runtime.d.ts +42 -0
  148. package/package/acode/dist/runtime.d.ts.map +1 -0
  149. package/package/acode/dist/runtime.js +1283 -0
  150. package/package/acode/dist/types.d.ts +112 -0
  151. package/package/acode/dist/types.d.ts.map +1 -0
  152. package/package/acode/dist/types.js +1 -0
  153. package/package/acode/package.json +24 -0
  154. package/package/aws-client-agent-mcp/dist/agent-client.d.ts +4 -4
  155. package/package/aws-client-agent-mcp/dist/agent-client.d.ts.map +1 -1
  156. package/package/aws-client-agent-mcp/dist/agent-client.js +9 -6
  157. package/package/aws-client-agent-mcp/dist/agent-client.js.map +1 -1
  158. package/package/aws-client-agent-mcp/dist/agent-client.test.js +21 -0
  159. package/package/aws-client-agent-mcp/dist/agent-client.test.js.map +1 -1
  160. package/package/aws-client-agent-mcp/dist/http-client.d.ts +1 -17
  161. package/package/aws-client-agent-mcp/dist/http-client.d.ts.map +1 -1
  162. package/package/aws-client-agent-mcp/dist/http-client.js +7 -113
  163. package/package/aws-client-agent-mcp/dist/http-client.js.map +1 -1
  164. package/package/aws-client-agent-mcp/dist/http-client.test.js +22 -2
  165. package/package/aws-client-agent-mcp/dist/http-client.test.js.map +1 -1
  166. package/package/aws-client-agent-mcp/dist/mcp-server.d.ts +0 -5
  167. package/package/aws-client-agent-mcp/dist/mcp-server.d.ts.map +1 -1
  168. package/package/aws-client-agent-mcp/dist/mcp-server.js +3 -24
  169. package/package/aws-client-agent-mcp/dist/mcp-server.js.map +1 -1
  170. package/package/aws-client-agent-mcp/dist/mcp-server.test.js +90 -5
  171. package/package/aws-client-agent-mcp/dist/mcp-server.test.js.map +1 -1
  172. package/package/aws-client-agent-mcp/dist/runtime-launch-binding.d.ts.map +1 -1
  173. package/package/aws-client-agent-mcp/dist/runtime-launch-binding.js +1 -0
  174. package/package/aws-client-agent-mcp/dist/runtime-launch-binding.js.map +1 -1
  175. package/package/aws-client-agent-mcp/dist/status-reporter.d.ts +2 -0
  176. package/package/aws-client-agent-mcp/dist/status-reporter.d.ts.map +1 -1
  177. package/package/aws-client-agent-mcp/dist/status-reporter.js +31 -3
  178. package/package/aws-client-agent-mcp/dist/status-reporter.js.map +1 -1
  179. package/package/aws-client-agent-mcp/dist/types.d.ts +4 -1
  180. package/package/aws-client-agent-mcp/dist/types.d.ts.map +1 -1
  181. package/package/aws-client-agent-mcp/dist/types.js.map +1 -1
  182. package/package/aws-client-agent-mcp/dist/websocket-client.d.ts +0 -13
  183. package/package/aws-client-agent-mcp/dist/websocket-client.d.ts.map +1 -1
  184. package/package/aws-client-agent-mcp/dist/websocket-client.js +5 -100
  185. package/package/aws-client-agent-mcp/dist/websocket-client.js.map +1 -1
  186. package/package/aws-client-agent-mcp/dist/websocket-client.test.js +43 -0
  187. package/package/aws-client-agent-mcp/dist/websocket-client.test.js.map +1 -1
  188. package/package/cc-switch-sdk/dist/constants.d.ts.map +1 -1
  189. package/package/cc-switch-sdk/dist/constants.js +7 -0
  190. package/package/cc-switch-sdk/dist/schemas.d.ts.map +1 -1
  191. package/package/cc-switch-sdk/dist/schemas.js +1 -0
  192. package/package/cc-switch-sdk/dist/sdk.d.ts.map +1 -1
  193. package/package/cc-switch-sdk/dist/sdk.js +7 -3
  194. package/package/cc-switch-sdk/dist/services/ai-config-service.d.ts +6 -0
  195. package/package/cc-switch-sdk/dist/services/ai-config-service.d.ts.map +1 -1
  196. package/package/cc-switch-sdk/dist/services/ai-config-service.js +27 -1
  197. package/package/cc-switch-sdk/dist/types.d.ts +4 -1
  198. package/package/cc-switch-sdk/dist/types.d.ts.map +1 -1
  199. package/package.json +8 -4
@@ -1,329 +1,517 @@
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 { buildClaudeCodeLaunchConfig, buildRuntimeEnv, buildToolResultTimelineActionInfo, createTerminalOutputDecoder, decodeTerminalOutputChunk, evaluatePersistedSessionReuse, formatTerminalPrompt, formatSdkOutputEvent, normalizeTerminalCommandInput, parseTerminalDirectoryChangeTarget, resolveSdkInputMessage, resolveTerminalOutputEncoding, resolveSdkProviderId, resolveStatusChangeUsage, } from './terminal.js';
7
- describe('terminal route validation', () => {
8
- it('requires agentId and workspacePath for start', () => {
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 { valid: false, error: 'agentId and workspacePath are required' };
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: 'agent-1', workspacePath: '/path' }).valid).toBe(true);
16
- });
17
- it('uses SDK default command when not provided', () => {
18
- const resolveCommand = (command) => command || 'claude';
19
- expect(resolveCommand(undefined)).toBe('claude');
20
- expect(resolveCommand('codex')).toBe('codex');
21
- });
22
- it('uses SDK launch mode for unsupported mode values', () => {
23
- const resolveLaunchMode = (mode) => (mode === 'sdk' ? 'sdk' : 'sdk');
24
- expect(resolveLaunchMode(undefined)).toBe('sdk');
25
- expect(resolveLaunchMode('sdk')).toBe('sdk');
26
- expect(resolveLaunchMode('unsupported')).toBe('sdk');
27
- });
28
- it('keeps shortcut mode available for manual key injection', () => {
29
- const buildInputResponse = (mode) => mode === 'shortcut' ? { ok: true, mode: 'shortcut' } : { ok: true, mode: 'sdk' };
30
- expect(buildInputResponse('shortcut')).toEqual({ ok: true, mode: 'shortcut' });
31
- expect(buildInputResponse(undefined)).toEqual({ ok: true, mode: 'sdk' });
32
- });
33
- it('does not downgrade unsupported shortcut input to normal SDK messages', () => {
34
- const buildShortcutFallbackResponse = (hasShortcutSupport) => (hasShortcutSupport
35
- ? { status: 200, body: { ok: true, mode: 'shortcut' } }
36
- : { status: 400, body: { error: 'shortcut input is not supported by this SDK provider' } });
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: 'shortcut input is not supported by this SDK provider' },
53
+ body: { error: "shortcut input is not supported by this SDK provider" },
40
54
  });
41
55
  });
42
- it('requires sessionId and input for SDK terminal input', () => {
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: 'sessionId and input are required' };
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: 'session-1', input: 'continue' }).valid).toBe(true);
51
- });
52
- it('keeps resize contract as SDK no-op compatibility endpoint', () => {
53
- const buildResizeResponse = (cols, rows) => ({ ok: true, mode: 'sdk', noop: true, cols, rows });
54
- expect(buildResizeResponse(120, 30)).toEqual({ ok: true, mode: 'sdk', noop: true, cols: 120, rows: 30 });
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('requires sessionId for stop', () => {
82
+ it("requires sessionId for stop", () => {
57
83
  const validateStopRequest = (body) => {
58
84
  if (!body.sessionId)
59
- return { valid: false, error: 'sessionId is required' };
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: 'session-1' }).valid).toBe(true);
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('terminal configuration', () => {
67
- it('builds correct SDK runtime environment', () => {
68
- const env = buildRuntimeEnv('agent-123', undefined, { PATH: '/usr/bin' });
69
- expect(env.AWS_AGENT_ID).toBe('agent-123');
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('true');
72
- });
73
- it('merges launch env overrides without allowing agent identity override', () => {
74
- const env = buildRuntimeEnv('agent-123', '/workspace', { PATH: '/usr/bin' }, {
75
- ANTHROPIC_BASE_URL: ' https://api.example.com ',
76
- ANTHROPIC_AUTH_TOKEN: 'secret-token',
77
- ANTHROPIC_MODEL: 'claude-sonnet-4-5',
78
- AWS_AGENT_ID: 'malicious-agent',
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('https://api.example.com');
82
- expect(env.ANTHROPIC_AUTH_TOKEN).toBe('secret-token');
83
- expect(env.ANTHROPIC_MODEL).toBe('claude-sonnet-4-5');
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('agent-123');
86
- expect(env.AWS_WORKSPACE_PATH).toBe('/workspace');
137
+ expect(env.AWS_AGENT_ID).toBe("agent-123");
138
+ expect(env.AWS_WORKSPACE_PATH).toBe("/workspace");
87
139
  });
88
- it('merges saved ClaudeCode AI settings into SDK launch env with request overrides taking precedence', () => {
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: 'https://saved.example.com',
92
- ANTHROPIC_AUTH_TOKEN: 'sk-saved',
93
- ANTHROPIC_MODEL: 'claude-saved',
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: ' claude-request ',
98
- EXTRA_VALUE: 'enabled',
149
+ ANTHROPIC_MODEL: " claude-request ",
150
+ EXTRA_VALUE: "enabled",
99
151
  });
100
152
  expect(launchConfig).toEqual({
101
153
  envOverrides: {
102
- ANTHROPIC_BASE_URL: 'https://saved.example.com',
103
- ANTHROPIC_AUTH_TOKEN: 'sk-saved',
104
- ANTHROPIC_MODEL: 'claude-request',
105
- EXTRA_VALUE: 'enabled',
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: 'claude-request',
159
+ model: "claude-request",
108
160
  });
109
161
  });
110
- it('uses CODEX_MODEL from request env overrides as SDK launch model', () => {
162
+ it("uses CODEX_MODEL from request env overrides as SDK launch model", () => {
111
163
  const launchConfig = buildClaudeCodeLaunchConfig(null, {
112
- OPENAI_API_KEY: 'sk-codex',
113
- CODEX_MODEL: ' gpt-5.3-codex ',
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: 'sk-codex',
118
- CODEX_MODEL: 'gpt-5.3-codex',
169
+ OPENAI_API_KEY: "sk-codex",
170
+ CODEX_MODEL: "gpt-5.3-codex",
119
171
  },
120
- model: 'gpt-5.3-codex',
172
+ model: "gpt-5.3-codex",
121
173
  });
122
174
  });
123
175
  });
124
- describe('persisted SDK session reuse', () => {
125
- it('does not reuse a persisted session without a PID', () => {
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: 'missing-pid' });
179
+ expect(decision).toEqual({ reusable: false, reason: "missing-pid" });
128
180
  });
129
- it('does not reuse a persisted session when its PID is no longer running', () => {
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: 'dead-pid' });
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('reuses a persisted session only when its PID is still running', () => {
134
- const decision = evaluatePersistedSessionReuse({ pid: 1234 }, () => true);
135
- expect(decision).toEqual({ reusable: true, reason: 'active-pid' });
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('terminal command helpers', () => {
139
- it('expands a whole mini-term clipboard paste file path into SDK input content', async () => {
140
- const tempDir = await mkdtemp(path.join(os.tmpdir(), 'terminal-input-'));
141
- const clipboardDir = path.join(tempDir, 'mini-term-clipboard');
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, 'paste-1780491671137.txt');
144
- await writeFile(pasteFile, 'real pasted prompt', 'utf-8');
145
- await expect(resolveSdkInputMessage(`"${pasteFile}"`)).resolves.toBe('real pasted prompt');
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('keeps explicit commands that mention mini-term clipboard files unchanged', async () => {
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('normalizes carriage-return terminal input into executable command text', () => {
152
- expect(normalizeTerminalCommandInput('npm test\r')).toBe('npm test');
153
- expect(normalizeTerminalCommandInput(' pwd \n')).toBe('pwd');
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('parses cd commands without treating other shell commands as directory changes', () => {
156
- expect(parseTerminalDirectoryChangeTarget('cd')).toBeNull();
157
- expect(parseTerminalDirectoryChangeTarget('cd /d "D:\\code\\repo"')).toBe('D:\\code\\repo');
158
- expect(parseTerminalDirectoryChangeTarget('npm run build')).toBeUndefined();
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('formats the working directory prompt as terminal output text', () => {
161
- expect(formatTerminalPrompt('D:\\code\\repo')).toBe('\x1b[36mD:\\code\\repo>\x1b[0m ');
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('defaults Windows terminal command output to GB18030 decoding', () => {
164
- expect(resolveTerminalOutputEncoding('win32', {})).toBe('gb18030');
165
- expect(resolveTerminalOutputEncoding('linux', {})).toBe('utf-8');
166
- expect(resolveTerminalOutputEncoding('win32', { AWS_TERMINAL_OUTPUT_ENCODING: 'utf-8' })).toBe('utf-8');
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('decodes GB18030 Chinese output across chunk boundaries', () => {
169
- const decoder = createTerminalOutputDecoder('gb18030');
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('resolveSdkProviderId', () => {
177
- it('exposes codex through the SDK provider SPI', () => {
178
- expect(SDK_PROVIDER_DEFINITIONS.map((definition) => definition.runtimeMode)).toContain('codex');
179
- expect(SDK_PROVIDER_DEFINITIONS.find((definition) => definition.runtimeMode === 'codex')).toMatchObject({
180
- providerId: 'codex',
181
- defaultCommand: 'codex',
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('resolves codex commands to codex provider', () => {
185
- expect(resolveSdkProviderId('codex')).toBe('codex');
186
- expect(resolveSdkProviderId('npx codex --model gpt-5-codex')).toBe('codex');
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('resolves opencode commands to opencode provider', () => {
189
- expect(resolveSdkProviderId('opencode')).toBe('opencode');
335
+ it("resolves opencode commands to opencode provider", () => {
336
+ expect(resolveSdkProviderId("opencode")).toBe("opencode");
190
337
  });
191
- it('falls back to claude-code for claude or unknown commands', () => {
192
- expect(resolveSdkProviderId('claude')).toBe('claude-code');
193
- expect(resolveSdkProviderId('custom-binary')).toBe('claude-code');
194
- expect(resolveSdkProviderId(undefined)).toBe('claude-code');
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('SDK status usage forwarding', () => {
198
- it('keeps accumulated usage from status-change action info', () => {
199
- expect(resolveStatusChangeUsage({ usage: { inputTokens: 12000, outputTokens: 345 } })).toEqual({
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({ actionType: 'mcp', actionLabel: 'Calling MCP' })).toBeUndefined();
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('SDK output forwarding', () => {
208
- it('formats text and thinking provider events for the terminal output callback path', () => {
381
+ describe("SDK output forwarding", () => {
382
+ it("formats assistant text provider events for the terminal output callback path", () => {
209
383
  const textEvent = {
210
- type: 'text_delta',
211
- sessionId: 'session-1',
384
+ type: "text_delta",
385
+ sessionId: "session-1",
212
386
  timestamp: new Date(0).toISOString(),
213
- data: { text: 'hello' },
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: 'thinking',
217
- sessionId: 'session-1',
393
+ type: "thinking",
394
+ sessionId: "session-1",
218
395
  timestamp: new Date(0).toISOString(),
219
- data: { text: 'thinking...' },
396
+ data: { text: "internal prompt or reasoning" },
220
397
  };
221
- expect(formatSdkOutputEvent(textEvent)).toBe('hello');
222
- expect(formatSdkOutputEvent(thinkingEvent)).toBe('thinking...');
398
+ expect(formatSdkOutputEvent(thinkingEvent)).toBe("internal prompt or reasoning");
223
399
  });
224
- it('formats SDK errors as terminal output and ignores non-output events', () => {
400
+ it("formats SDK errors as terminal output and ignores non-output events", () => {
225
401
  const errorEvent = {
226
- type: 'error',
227
- sessionId: 'session-1',
402
+ type: "error",
403
+ sessionId: "session-1",
228
404
  timestamp: new Date(0).toISOString(),
229
- data: { text: 'boom' },
405
+ data: { text: "boom" },
230
406
  };
231
407
  const toolEvent = {
232
- type: 'tool_use_start',
233
- sessionId: 'session-1',
408
+ type: "tool_use_start",
409
+ sessionId: "session-1",
234
410
  timestamp: new Date(0).toISOString(),
235
- data: { toolName: 'read_file' },
411
+ data: { toolName: "read_file" },
236
412
  };
237
- expect(formatSdkOutputEvent(errorEvent)).toBe('\r\n[SDK Error] boom\r\n');
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('SDK tool result timeline action info', () => {
242
- it('keeps MCP tool results on MCP timeline events', () => {
429
+ describe("SDK tool result timeline action info", () => {
430
+ it("keeps MCP tool results on MCP timeline events", () => {
243
431
  const event = {
244
- type: 'tool_use_end',
245
- sessionId: 'session-1',
432
+ type: "tool_use_end",
433
+ sessionId: "session-1",
246
434
  timestamp: new Date(0).toISOString(),
247
435
  data: {
248
- toolName: 'mcp__aws-mcp__get_profile',
249
- toolUseId: 'tool-1',
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: 'mcp',
255
- actionLabel: 'MCP返回',
256
- actionDetail: 'mcp__aws-mcp__get_profile',
257
- actionId: 'tool-1',
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('forwards bash tool results to command timeline events', () => {
449
+ it("forwards bash tool results to command timeline events", () => {
262
450
  const event = {
263
- type: 'tool_use_end',
264
- sessionId: 'session-1',
451
+ type: "tool_use_end",
452
+ sessionId: "session-1",
265
453
  timestamp: new Date(0).toISOString(),
266
454
  data: {
267
- toolName: 'bash',
268
- toolInput: { command: 'npm run build' },
269
- toolUseId: 'tool-2',
270
- toolResult: 'build passed',
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: 'bash',
275
- actionLabel: '执行命令',
276
- actionDetail: 'npm run build',
277
- actionId: 'tool-2',
278
- actionResult: 'build passed',
462
+ actionType: "bash",
463
+ actionLabel: "执行命令",
464
+ actionDetail: "npm run build",
465
+ actionId: "tool-2",
466
+ actionResult: "build passed",
279
467
  });
280
468
  });
281
- it('forwards read tool results to file-read timeline events', () => {
469
+ it("forwards read tool results to file-read timeline events", () => {
282
470
  const event = {
283
- type: 'tool_use_end',
284
- sessionId: 'session-1',
471
+ type: "tool_use_end",
472
+ sessionId: "session-1",
285
473
  timestamp: new Date(0).toISOString(),
286
474
  data: {
287
- toolName: 'read_file',
288
- toolInput: { path: 'src/index.ts' },
289
- actionId: 'tool-3',
290
- actionResult: 'export const ok = true',
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: 'read_file',
295
- actionLabel: '读取文件',
296
- actionDetail: 'src/index.ts',
297
- actionId: 'tool-3',
298
- actionResult: 'export const ok = true',
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('keeps MCP-named filesystem results on semantic file-read timeline events', () => {
489
+ it("keeps MCP-named filesystem results on semantic file-read timeline events", () => {
302
490
  const event = {
303
- type: 'tool_use_end',
304
- sessionId: 'session-1',
491
+ type: "tool_use_end",
492
+ sessionId: "session-1",
305
493
  timestamp: new Date(0).toISOString(),
306
494
  data: {
307
- toolName: 'mcp__filesystem__read_file',
308
- toolInput: { path: 'src/index.ts' },
309
- toolUseId: 'tool-4',
310
- toolResult: 'export const ok = true',
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: 'read_file',
315
- actionLabel: '读取文件',
316
- actionDetail: 'src/index.ts',
317
- actionId: 'tool-4',
318
- actionResult: 'export const ok = true',
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('ignores tool result events without displayable result content', () => {
509
+ it("ignores tool result events without displayable result content", () => {
322
510
  const event = {
323
- type: 'tool_use_end',
324
- sessionId: 'session-1',
511
+ type: "tool_use_end",
512
+ sessionId: "session-1",
325
513
  timestamp: new Date(0).toISOString(),
326
- data: { toolName: 'read_file', toolResult: ' ' },
514
+ data: { toolName: "read_file", toolResult: " " },
327
515
  };
328
516
  expect(buildToolResultTimelineActionInfo(event)).toBeUndefined();
329
517
  });