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
@@ -4,44 +4,91 @@
4
4
  * 提供 SDK Agent 会话的启动、输入、调整大小和停止功能
5
5
  * 终端 UI 仅作为 SDK Agent 的消息输入/状态输出载体
6
6
  */
7
- import { spawn } from 'node:child_process';
8
- import { promises as fs } from 'node:fs';
9
- import os from 'node:os';
10
- import path from 'node:path';
11
- import { TextDecoder } from 'node:util';
12
- import { Router } from 'express';
13
- import { v4 as uuidv4 } from 'uuid';
14
- import { adapterRegistry, getToolActionInfo, isTransientClipboardPasteFilePath, redactTransientClipboardPastePaths, registerSdkProviders, resolveSdkProviderIdByCommand, } from '../adapter/index.js';
15
- import { validateToken } from '../middleware/auth.js';
16
- import { getAgentProcessManager } from '../services/agent-process-manager.js';
17
- import { getAiConfigService } from '../services/cc-switch-sdk.js';
18
- import { enqueueMcpLaunchBinding } from '../services/mcp-launch-binding-queue.js';
19
- import { findClaudeCodeProcesses, isProcessRunning } from '../services/process-detector.js';
20
- import { sendOutput, sendQuestionRequest, sendStatus } from '../services/session-output.js';
21
- import { findPersistedSessionByAgentId, removePersistedSession, savePersistedSessions, updatePersistedSessionAutoCommands, updatePersistedSessionRuntimeState, upsertPersistedSession, } from '../services/terminal-persistence.js';
7
+ import { spawn } from "node:child_process";
8
+ import { promises as fs } from "node:fs";
9
+ import os from "node:os";
10
+ import path from "node:path";
11
+ import { TextDecoder } from "node:util";
12
+ import { Router } from "express";
13
+ import { v4 as uuidv4 } from "uuid";
14
+ import { adapterRegistry, getToolActionInfo, isTransientClipboardPasteFilePath, redactTransientClipboardPastePaths, registerSdkProviders, resolveSdkProviderIdByCommand, } from "../adapter/index.js";
15
+ import { validateToken } from "../middleware/auth.js";
16
+ import { getAgentProcessManager } from "../services/agent-process-manager.js";
17
+ import { getAiConfigService } from "../services/cc-switch-sdk.js";
18
+ import { enqueueMcpLaunchBinding } from "../services/mcp-launch-binding-queue.js";
19
+ import { findClaudeCodeProcesses, isProcessRunning, } from "../services/process-detector.js";
20
+ import { sendOutput, sendQuestionRequest, sendStatus, } from "../services/session-output.js";
21
+ import { findPersistedSessionByAgentId, removePersistedSession, savePersistedSessions, updatePersistedSessionAutoCommands, updatePersistedSessionRuntimeState, upsertPersistedSession, } from "../services/terminal-persistence.js";
22
22
  export const terminalRouter = Router();
23
23
  // 导出 SDK 会话存储,供其他模块使用(如 sessions.ts)
24
24
  export const sdkSessions = new Map();
25
25
  const TOOL_RESULT_PREVIEW_MAX_LENGTH = 4000;
26
+ const AI_REQUEST_BODY_MARKER = "\n\n请求体:\n";
26
27
  const startingAgents = new Set();
27
28
  export function resolveStatusChangeUsage(actionInfo) {
28
29
  const usage = actionInfo?.usage;
29
30
  if (!usage)
30
31
  return undefined;
31
- const inputTokens = Number.isFinite(usage.inputTokens) ? usage.inputTokens : 0;
32
- const outputTokens = Number.isFinite(usage.outputTokens) ? usage.outputTokens : 0;
32
+ const inputTokens = Number.isFinite(usage.inputTokens)
33
+ ? usage.inputTokens
34
+ : 0;
35
+ const outputTokens = Number.isFinite(usage.outputTokens)
36
+ ? usage.outputTokens
37
+ : 0;
33
38
  if (inputTokens <= 0 && outputTokens <= 0)
34
39
  return undefined;
35
40
  return { inputTokens, outputTokens };
36
41
  }
37
42
  const terminalCommandStates = new Map();
38
- const DEFAULT_WINDOWS_TERMINAL_OUTPUT_ENCODING = 'gb18030';
39
- const DEFAULT_TERMINAL_OUTPUT_ENCODING = 'utf-8';
43
+ const DEFAULT_WINDOWS_TERMINAL_OUTPUT_ENCODING = "gb18030";
44
+ const DEFAULT_TERMINAL_OUTPUT_ENCODING = "utf-8";
45
+ const TERMINAL_COMMAND_OUTPUT_SPILLOVER_LINE_LIMIT = 100;
46
+ const TERMINAL_COMMAND_INACTIVITY_TIMEOUT_MS = 60_000;
47
+ function padDatePart(value) {
48
+ return String(value).padStart(2, "0");
49
+ }
50
+ export function buildAcodeCommandOutputPath(workspacePath, now = new Date()) {
51
+ const year = now.getFullYear();
52
+ const month = padDatePart(now.getMonth() + 1);
53
+ const day = padDatePart(now.getDate());
54
+ const hour = padDatePart(now.getHours());
55
+ const minute = padDatePart(now.getMinutes());
56
+ const timestamp = `${year}${month}${day}-${hour}${minute}${padDatePart(now.getSeconds())}-${String(now.getMilliseconds()).padStart(3, "0")}`;
57
+ return path.join(workspacePath, ".agentswork", "acode", "cmd-dist", `${year}-${month}-${day}`, hour, minute, `${timestamp}-cmd.out`);
58
+ }
59
+ export function countTerminalOutputLines(output) {
60
+ if (!output) {
61
+ return 0;
62
+ }
63
+ const normalized = output.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
64
+ const withoutTrailingLineBreak = normalized.endsWith("\n")
65
+ ? normalized.slice(0, -1)
66
+ : normalized;
67
+ return withoutTrailingLineBreak
68
+ ? withoutTrailingLineBreak.split("\n").length
69
+ : 1;
70
+ }
71
+ export function formatCommandOutputSpilloverNotice(outputPath, lineLimit = TERMINAL_COMMAND_OUTPUT_SPILLOVER_LINE_LIMIT) {
72
+ return `\r\n[ACode] 命令输出超过 ${lineLimit} 行,完整输出已写入: ${outputPath}\r\n[ACode] Agent 可自行读取该文件,或在该文件中搜索需要的内容。\r\n`;
73
+ }
74
+ export function formatCommandInactivityTimeoutNotice(timeoutMs = TERMINAL_COMMAND_INACTIVITY_TIMEOUT_MS) {
75
+ return `\r\n[ACode] 命令超过 ${Math.round(timeoutMs / 1000)} 秒没有新的输出,已停止执行并保留此前输出。\r\n`;
76
+ }
77
+ async function persistTerminalCommandOutput(state) {
78
+ const outputPath = state.outputPath ||
79
+ buildAcodeCommandOutputPath(state.workspacePath, state.startedAt);
80
+ state.outputPath = outputPath;
81
+ await fs.mkdir(path.dirname(outputPath), { recursive: true });
82
+ await fs.writeFile(outputPath, state.chunks.join(""), "utf-8");
83
+ return outputPath;
84
+ }
40
85
  /**
41
86
  * 标准化终端输入:将前端回车符还原为 shell 可执行命令文本。
42
87
  */
43
88
  export function normalizeTerminalCommandInput(value) {
44
- return String(value ?? '').replace(/\r/g, '\n').trim();
89
+ return String(value ?? "")
90
+ .replace(/\r/g, "\n")
91
+ .trim();
45
92
  }
46
93
  /**
47
94
  * 解析终端命令输出解码编码。
@@ -52,7 +99,9 @@ export function resolveTerminalOutputEncoding(platform = process.platform, env =
52
99
  if (configuredEncoding) {
53
100
  return configuredEncoding;
54
101
  }
55
- return platform === 'win32' ? DEFAULT_WINDOWS_TERMINAL_OUTPUT_ENCODING : DEFAULT_TERMINAL_OUTPUT_ENCODING;
102
+ return platform === "win32"
103
+ ? DEFAULT_WINDOWS_TERMINAL_OUTPUT_ENCODING
104
+ : DEFAULT_TERMINAL_OUTPUT_ENCODING;
56
105
  }
57
106
  /**
58
107
  * 创建终端输出流式解码器,避免多字节中文被 chunk 边界截断。
@@ -73,7 +122,7 @@ export function decodeTerminalOutputChunk(decoder, chunk, end = false) {
73
122
  return decoder.decode();
74
123
  }
75
124
  if (!chunk || chunk.length === 0) {
76
- return '';
125
+ return "";
77
126
  }
78
127
  return decoder.decode(chunk, { stream: true });
79
128
  }
@@ -89,9 +138,9 @@ export function parseTerminalDirectoryChangeTarget(command) {
89
138
  if (!rawTarget) {
90
139
  return null;
91
140
  }
92
- const withoutDriveSwitch = rawTarget.replace(/^\/d\s+/i, '').trim();
93
- if ((withoutDriveSwitch.startsWith('"') && withoutDriveSwitch.endsWith('"'))
94
- || (withoutDriveSwitch.startsWith("'") && withoutDriveSwitch.endsWith("'"))) {
141
+ const withoutDriveSwitch = rawTarget.replace(/^\/d\s+/i, "").trim();
142
+ if ((withoutDriveSwitch.startsWith('"') && withoutDriveSwitch.endsWith('"')) ||
143
+ (withoutDriveSwitch.startsWith("'") && withoutDriveSwitch.endsWith("'"))) {
95
144
  return withoutDriveSwitch.slice(1, -1);
96
145
  }
97
146
  return withoutDriveSwitch;
@@ -117,34 +166,50 @@ function findActiveSdkSessionByAgentId(agentId) {
117
166
  return undefined;
118
167
  }
119
168
  async function buildExistingSessionResponse(entry) {
120
- const adapter = adapterRegistry.get(entry.providerId || 'claude-code');
169
+ const adapter = adapterRegistry.get(entry.providerId || "claude-code");
170
+ const status = resolveSdkSessionDisplayStatus(adapter.getSessionStatus(entry.sessionId), entry.currentRuntimeStatus);
121
171
  return {
122
172
  sessionId: entry.sessionId,
123
- status: adapter.getSessionStatus(entry.sessionId) || 'running',
124
- runtimeStatus: entry.currentRuntimeStatus,
173
+ status,
174
+ runtimeStatus: status,
125
175
  runtimeActionType: entry.currentRuntimeActionType,
126
176
  runtimeActionLabel: entry.currentRuntimeActionLabel,
127
177
  runtimeActionDetail: entry.currentRuntimeActionDetail,
128
178
  agentId: entry.agentId,
129
179
  workspacePath: entry.workspacePath,
130
180
  command: entry.config.command,
131
- mode: 'sdk',
181
+ mode: "sdk",
132
182
  providerSessionId: adapter.getProviderSessionId(entry.sessionId),
133
183
  reused: true,
134
184
  };
135
185
  }
136
186
  /**
137
- * 判断持久化 SDK 会话能否在显式启动请求中复用。
138
- * 主流程:必须存在 PID OS 仍能确认该进程存活;否则视为重启后遗留的陈旧会话。
187
+ * 解析 SDK 会话对外展示状态。
188
+ * 主流程:优先使用 adapter 当前细粒度状态,其次使用路由内存记录的最近状态,最后才兼容旧会话回退 running。
189
+ */
190
+ export function resolveSdkSessionDisplayStatus(adapterStatus, currentRuntimeStatus) {
191
+ const normalizedAdapterStatus = String(adapterStatus || "").trim();
192
+ if (normalizedAdapterStatus) {
193
+ return normalizedAdapterStatus;
194
+ }
195
+ const normalizedRuntimeStatus = String(currentRuntimeStatus || "").trim();
196
+ return normalizedRuntimeStatus || "running";
197
+ }
198
+ /**
199
+ * Decide whether a persisted SDK session can be reused for a start request.
200
+ * Main flow: require a live PID, then reject in-process providers whose adapter control channel cannot be restored.
139
201
  */
140
202
  export function evaluatePersistedSessionReuse(session, isPidRunning = isProcessRunning) {
141
203
  if (!session.pid) {
142
- return { reusable: false, reason: 'missing-pid' };
204
+ return { reusable: false, reason: "missing-pid" };
143
205
  }
144
206
  if (!isPidRunning(session.pid)) {
145
- return { reusable: false, reason: 'dead-pid' };
207
+ return { reusable: false, reason: "dead-pid" };
208
+ }
209
+ if (resolveSdkProviderId(session.command) === "acode") {
210
+ return { reusable: false, reason: "in-process-provider" };
146
211
  }
147
- return { reusable: true, reason: 'active-pid' };
212
+ return { reusable: true, reason: "active-pid" };
148
213
  }
149
214
  function stopTerminalCommandProcess(sessionId) {
150
215
  const state = terminalCommandStates.get(sessionId);
@@ -177,7 +242,7 @@ export function formatTerminalPrompt(currentDirectory) {
177
242
  return `\x1b[36m${currentDirectory}>\x1b[0m `;
178
243
  }
179
244
  function resolveDirectoryTarget(currentDirectory, target) {
180
- if (target === '~') {
245
+ if (target === "~") {
181
246
  return os.homedir();
182
247
  }
183
248
  return path.resolve(currentDirectory, target);
@@ -217,7 +282,7 @@ async function handleDirectoryChangeCommand(entry, state, command) {
217
282
  async function executeTerminalCommand(sessionId, commandValue) {
218
283
  const entry = sdkSessions.get(sessionId);
219
284
  if (!entry) {
220
- throw new Error('SDK session not found');
285
+ throw new Error("SDK session not found");
221
286
  }
222
287
  const command = normalizeTerminalCommandInput(commandValue);
223
288
  const state = getTerminalCommandState(entry);
@@ -225,12 +290,12 @@ async function executeTerminalCommand(sessionId, commandValue) {
225
290
  return { started: false, cwd: state.currentDirectory };
226
291
  }
227
292
  if (state.runningProcess) {
228
- throw new Error('A terminal command is already running for this session');
293
+ throw new Error("A terminal command is already running for this session");
229
294
  }
230
295
  const displayCommand = redactTransientClipboardPastePaths(command);
231
296
  appendCommandOutput(entry, `${formatTerminalPrompt(state.currentDirectory)}${displayCommand}\r\n`);
232
297
  if (isTransientClipboardPasteFilePath(command)) {
233
- appendCommandOutput(entry, '[Terminal Warning] 检测到剪贴板临时 paste 文件路径,已跳过执行。请粘贴文件内容或使用显式读取命令。\r\n');
298
+ appendCommandOutput(entry, "[Terminal Warning] 检测到剪贴板临时 paste 文件路径,已跳过执行。请粘贴文件内容或使用显式读取命令。\r\n");
234
299
  appendCommandOutput(entry, formatTerminalPrompt(state.currentDirectory));
235
300
  return { started: false, cwd: state.currentDirectory };
236
301
  }
@@ -238,9 +303,9 @@ async function executeTerminalCommand(sessionId, commandValue) {
238
303
  if (handledDirectoryChange) {
239
304
  return { started: false, cwd: state.currentDirectory };
240
305
  }
241
- await sendStatus(entry.agentId, sessionId, 'tool_using', {
242
- actionType: 'bash',
243
- actionLabel: '终端命令',
306
+ await sendStatus(entry.agentId, sessionId, "tool_using", {
307
+ actionType: "bash",
308
+ actionLabel: "终端命令",
244
309
  actionDetail: displayCommand,
245
310
  });
246
311
  const child = spawn(command, {
@@ -251,19 +316,81 @@ async function executeTerminalCommand(sessionId, commandValue) {
251
316
  state.runningProcess = child;
252
317
  const stdoutDecoder = createTerminalOutputDecoder();
253
318
  const stderrDecoder = createTerminalOutputDecoder();
254
- child.stdout.on('data', (chunk) => {
255
- appendCommandOutput(entry, decodeTerminalOutputChunk(stdoutDecoder, chunk));
319
+ const usesAcodeCommandOutputPolicy = entry.providerId === "acode";
320
+ const outputSpilloverState = {
321
+ workspacePath: entry.workspacePath,
322
+ startedAt: new Date(),
323
+ chunks: [],
324
+ spilled: false,
325
+ };
326
+ let inactivityTimer;
327
+ const clearInactivityTimer = () => {
328
+ if (inactivityTimer) {
329
+ clearTimeout(inactivityTimer);
330
+ inactivityTimer = undefined;
331
+ }
332
+ };
333
+ const resetInactivityTimer = () => {
334
+ clearInactivityTimer();
335
+ inactivityTimer = setTimeout(() => {
336
+ if (state.runningProcess === child) {
337
+ appendCommandOutput(entry, formatCommandInactivityTimeoutNotice());
338
+ child.kill();
339
+ }
340
+ }, TERMINAL_COMMAND_INACTIVITY_TIMEOUT_MS);
341
+ };
342
+ const appendProcessOutput = (output, trackActivity = true) => {
343
+ if (!output) {
344
+ return;
345
+ }
346
+ if (usesAcodeCommandOutputPolicy) {
347
+ outputSpilloverState.chunks.push(output);
348
+ }
349
+ if (usesAcodeCommandOutputPolicy && trackActivity) {
350
+ resetInactivityTimer();
351
+ }
352
+ if (usesAcodeCommandOutputPolicy && outputSpilloverState.spilled) {
353
+ return;
354
+ }
355
+ if (usesAcodeCommandOutputPolicy &&
356
+ countTerminalOutputLines(outputSpilloverState.chunks.join("")) >
357
+ TERMINAL_COMMAND_OUTPUT_SPILLOVER_LINE_LIMIT) {
358
+ outputSpilloverState.spilled = true;
359
+ void persistTerminalCommandOutput(outputSpilloverState)
360
+ .then((outputPath) => {
361
+ appendCommandOutput(entry, formatCommandOutputSpilloverNotice(outputPath));
362
+ })
363
+ .catch((error) => {
364
+ const message = error instanceof Error ? error.message : String(error);
365
+ appendCommandOutput(entry, `\r\n[ACode] 命令输出落盘失败: ${message}\r\n`);
366
+ });
367
+ return;
368
+ }
369
+ appendCommandOutput(entry, output);
370
+ };
371
+ if (usesAcodeCommandOutputPolicy) {
372
+ resetInactivityTimer();
373
+ }
374
+ child.stdout.on("data", (chunk) => {
375
+ appendProcessOutput(decodeTerminalOutputChunk(stdoutDecoder, chunk));
256
376
  });
257
- child.stderr.on('data', (chunk) => {
258
- appendCommandOutput(entry, decodeTerminalOutputChunk(stderrDecoder, chunk));
377
+ child.stderr.on("data", (chunk) => {
378
+ appendProcessOutput(decodeTerminalOutputChunk(stderrDecoder, chunk));
259
379
  });
260
- child.on('error', (error) => {
380
+ child.on("error", (error) => {
261
381
  appendCommandOutput(entry, `\r\n[Terminal Error] ${error.message}\r\n`);
262
382
  });
263
- child.on('close', (code, signal) => {
383
+ child.on("close", (code, signal) => {
384
+ clearInactivityTimer();
264
385
  state.runningProcess = null;
265
- appendCommandOutput(entry, decodeTerminalOutputChunk(stdoutDecoder, undefined, true));
266
- appendCommandOutput(entry, decodeTerminalOutputChunk(stderrDecoder, undefined, true));
386
+ appendProcessOutput(decodeTerminalOutputChunk(stdoutDecoder, undefined, true), false);
387
+ appendProcessOutput(decodeTerminalOutputChunk(stderrDecoder, undefined, true), false);
388
+ if (outputSpilloverState.spilled) {
389
+ void persistTerminalCommandOutput(outputSpilloverState).catch((error) => {
390
+ const message = error instanceof Error ? error.message : String(error);
391
+ appendCommandOutput(entry, `\r\n[ACode] 命令输出最终落盘失败: ${message}\r\n`);
392
+ });
393
+ }
267
394
  if (code && code !== 0) {
268
395
  appendCommandOutput(entry, `\r\n[exit ${code}]\r\n`);
269
396
  }
@@ -271,7 +398,7 @@ async function executeTerminalCommand(sessionId, commandValue) {
271
398
  appendCommandOutput(entry, `\r\n[signal ${signal}]\r\n`);
272
399
  }
273
400
  appendCommandOutput(entry, formatTerminalPrompt(state.currentDirectory));
274
- void sendStatus(entry.agentId, sessionId, 'waiting_input');
401
+ void sendStatus(entry.agentId, sessionId, "waiting_input");
275
402
  });
276
403
  return { started: true, cwd: state.currentDirectory };
277
404
  }
@@ -299,7 +426,7 @@ export function buildRuntimeEnv(agentId, workspacePath, baseEnv, envOverrides) {
299
426
  }
300
427
  }
301
428
  env.AWS_AGENT_ID = String(agentId);
302
- env.AWS_MCP_CLAIM_LAUNCH_BINDING = 'true';
429
+ env.AWS_MCP_CLAIM_LAUNCH_BINDING = "true";
303
430
  return env;
304
431
  }
305
432
  function stripWrappingQuotes(value) {
@@ -324,7 +451,7 @@ export async function resolveSdkInputMessage(input) {
324
451
  if (!path.isAbsolute(filePath)) {
325
452
  return message;
326
453
  }
327
- return fs.readFile(filePath, 'utf-8');
454
+ return fs.readFile(filePath, "utf-8");
328
455
  }
329
456
  /**
330
457
  * 主流程:将已持久化的 ClaudeCode AI 配置合并到启动配置,再让请求级覆盖保持最高优先级。
@@ -347,9 +474,9 @@ export function buildClaudeCodeLaunchConfig(savedClaudeConfig, requestEnvOverrid
347
474
  envOverrides[normalizedKey] = normalizedValue;
348
475
  }
349
476
  }
350
- const model = envOverrides.ANTHROPIC_MODEL?.trim()
351
- || envOverrides.CODEX_MODEL?.trim()
352
- || undefined;
477
+ const model = envOverrides.ANTHROPIC_MODEL?.trim() ||
478
+ envOverrides.CODEX_MODEL?.trim() ||
479
+ undefined;
353
480
  return { envOverrides, model };
354
481
  }
355
482
  /**
@@ -369,7 +496,7 @@ function formatToolResultForTimeline(result) {
369
496
  if (result === undefined || result === null) {
370
497
  return undefined;
371
498
  }
372
- const text = typeof result === 'string' ? result : JSON.stringify(result, null, 2);
499
+ const text = typeof result === "string" ? result : JSON.stringify(result, null, 2);
373
500
  const normalized = text.trim();
374
501
  if (!normalized) {
375
502
  return undefined;
@@ -381,18 +508,20 @@ function formatToolResultForTimeline(result) {
381
508
  }
382
509
  function isMcpProviderEvent(event) {
383
510
  const actionType = event.data.actionType;
384
- if (actionType === 'mcp') {
511
+ if (actionType === "mcp") {
385
512
  return true;
386
513
  }
387
- const toolName = String(event.data.toolName || '').toLowerCase();
388
- return toolName.startsWith('mcp__') || toolName.includes('-mcp__') || toolName.includes('mcp_');
514
+ const toolName = String(event.data.toolName || "").toLowerCase();
515
+ return (toolName.startsWith("mcp__") ||
516
+ toolName.includes("-mcp__") ||
517
+ toolName.includes("mcp_"));
389
518
  }
390
519
  /**
391
520
  * 将 SDK 工具结束事件转换为时间线返回数据状态。
392
521
  * 主流程:格式化工具结果 -> 保留 MCP 专用展示 -> 其它工具按动作类型追加到对应时间线事件。
393
522
  */
394
523
  export function buildToolResultTimelineActionInfo(event) {
395
- if (event.type !== 'tool_use_end') {
524
+ if (event.type !== "tool_use_end") {
396
525
  return undefined;
397
526
  }
398
527
  const actionResult = formatToolResultForTimeline(event.data.toolResult ?? event.data.actionResult);
@@ -401,13 +530,13 @@ export function buildToolResultTimelineActionInfo(event) {
401
530
  }
402
531
  const inferredAction = getToolActionInfo(event.data.toolName, event.data.toolInput);
403
532
  const actionType = event.data.actionType || inferredAction.actionType;
404
- if (actionType === 'idle') {
533
+ if (actionType === "idle") {
405
534
  return undefined;
406
535
  }
407
- if (actionType === 'mcp' && isMcpProviderEvent(event)) {
536
+ if (actionType === "mcp" && isMcpProviderEvent(event)) {
408
537
  return {
409
- actionType: 'mcp',
410
- actionLabel: 'MCP返回',
538
+ actionType: "mcp",
539
+ actionLabel: "MCP返回",
411
540
  actionDetail: event.data.actionDetail || event.data.toolName,
412
541
  actionId: event.data.actionId || event.data.toolUseId,
413
542
  actionResult,
@@ -416,17 +545,122 @@ export function buildToolResultTimelineActionInfo(event) {
416
545
  return {
417
546
  actionType,
418
547
  actionLabel: event.data.actionLabel || inferredAction.actionLabel,
419
- actionDetail: event.data.actionDetail || inferredAction.actionDetail || event.data.toolName,
548
+ actionDetail: event.data.actionDetail ||
549
+ inferredAction.actionDetail ||
550
+ event.data.toolName,
420
551
  actionId: event.data.actionId || event.data.toolUseId,
421
552
  actionResult,
422
553
  };
423
554
  }
424
555
  export function formatSdkOutputEvent(event) {
425
- if (event.type === 'text_delta' || event.type === 'thinking') {
556
+ if (event.type === "ai_request") {
557
+ const model = event.data.model ? ` ${event.data.model}` : "";
558
+ const preview = event.data.lastUserMessagePreview
559
+ ? `:${event.data.lastUserMessagePreview}`
560
+ : "";
561
+ return `\r\n[ACode] 正在请求模型${model}${preview}\r\n`;
562
+ }
563
+ if (event.type === "text_delta") {
426
564
  return event.data.text || undefined;
427
565
  }
428
- if (event.type === 'error') {
429
- return event.data.text ? `\r\n[SDK Error] ${event.data.text}\r\n` : undefined;
566
+ if (event.type === "thinking") {
567
+ return event.data.text || undefined;
568
+ }
569
+ if (event.type === "error") {
570
+ return event.data.text
571
+ ? `\r\n[SDK Error] ${event.data.text}\r\n`
572
+ : undefined;
573
+ }
574
+ return undefined;
575
+ }
576
+ function formatAiRequestDetail(data) {
577
+ const lines = [
578
+ data.model ? `模型: ${data.model}` : undefined,
579
+ data.endpoint ? `接口: ${data.endpoint}` : undefined,
580
+ typeof data.stream === "boolean"
581
+ ? `流式: ${data.stream ? "是" : "否"}`
582
+ : undefined,
583
+ typeof data.messageCount === "number"
584
+ ? `上下文消息: ${data.messageCount}`
585
+ : undefined,
586
+ typeof data.toolCount === "number"
587
+ ? `可用工具: ${data.toolCount}`
588
+ : undefined,
589
+ data.lastUserMessagePreview
590
+ ? `最后请求: ${data.lastUserMessagePreview}`
591
+ : undefined,
592
+ ].filter((line) => Boolean(line));
593
+ const base = lines.join("\n") || "ACode 正在发送 AI 请求";
594
+ return data.requestBody
595
+ ? `${base}${AI_REQUEST_BODY_MARKER}${data.requestBody}`
596
+ : base;
597
+ }
598
+ /**
599
+ * 主干流程:把 provider 事件转换为面板运行状态。
600
+ *
601
+ * text_delta 表示助手正在生成;thinking 表示推理/内部过程。
602
+ * tool_use_start 表示正在执行工具;turn_complete 才表示真正空闲。
603
+ */
604
+ export function resolveRuntimeStatusFromProviderEvent(event) {
605
+ if (event.type === "ai_request") {
606
+ return {
607
+ status: "thinking",
608
+ actionInfo: {
609
+ actionType: "ai_request",
610
+ actionLabel: "AI请求",
611
+ actionDetail: formatAiRequestDetail(event.data),
612
+ },
613
+ };
614
+ }
615
+ if (event.type === "thinking" || event.type === "text_delta") {
616
+ return { status: "thinking" };
617
+ }
618
+ if (event.type === "tool_use_start") {
619
+ const inferredAction = getToolActionInfo(event.data.toolName, event.data.toolInput);
620
+ if (event.data.actionType === "idle" ||
621
+ inferredAction.actionType === "idle") {
622
+ return {
623
+ status: "waiting_input",
624
+ actionInfo: {
625
+ actionType: event.data.actionType || inferredAction.actionType,
626
+ actionLabel: event.data.actionLabel || inferredAction.actionLabel,
627
+ actionDetail: event.data.actionDetail ||
628
+ inferredAction.actionDetail ||
629
+ event.data.toolName,
630
+ actionId: event.data.actionId || event.data.toolUseId,
631
+ },
632
+ };
633
+ }
634
+ return {
635
+ status: "tool_using",
636
+ actionInfo: {
637
+ actionType: event.data.actionType || inferredAction.actionType,
638
+ actionLabel: event.data.actionLabel || inferredAction.actionLabel,
639
+ actionDetail: event.data.actionDetail ||
640
+ inferredAction.actionDetail ||
641
+ event.data.toolName,
642
+ actionId: event.data.actionId || event.data.toolUseId,
643
+ },
644
+ };
645
+ }
646
+ if (event.type === "turn_complete") {
647
+ const responseActionInfo = event.data
648
+ .responseBody
649
+ ? {
650
+ actionType: "ai_request",
651
+ actionLabel: "AI响应",
652
+ actionResult: event.data.responseBody,
653
+ usage: event.data.usage,
654
+ }
655
+ : undefined;
656
+ return {
657
+ status: "waiting_input",
658
+ actionInfo: responseActionInfo ||
659
+ (event.data.usage ? { usage: event.data.usage } : undefined),
660
+ };
661
+ }
662
+ if (event.type === "error") {
663
+ return { status: "error" };
430
664
  }
431
665
  return undefined;
432
666
  }
@@ -436,7 +670,7 @@ function forwardSdkOutputEvent(event, entry) {
436
670
  return;
437
671
  }
438
672
  entry.seq += 1;
439
- void sendOutput(entry.agentId, output, event.sessionId, entry.seq);
673
+ void sendOutput(entry.agentId, output, event.sessionId, entry.seq, { sourceEventType: event.type }, { runtimeAccessToken: entry.runtimeAccessToken });
440
674
  }
441
675
  export function resolveSdkProviderId(command) {
442
676
  return resolveSdkProviderIdByCommand(command);
@@ -450,45 +684,52 @@ function ensureAdapterInitialized() {
450
684
  adapterInitialized = true;
451
685
  }
452
686
  function wireSdkAdapterEvents(adapter, definition) {
453
- adapter.on('event', (event) => {
687
+ adapter.on("event", (event) => {
454
688
  const entry = sdkSessions.get(event.sessionId);
455
689
  if (entry) {
456
690
  console.log(`[${definition.displayName} Adapter] Event: ${event.type} for session ${event.sessionId}`);
691
+ const runtimeStatus = resolveRuntimeStatusFromProviderEvent(event);
692
+ if (runtimeStatus) {
693
+ void sendStatus(entry.agentId, event.sessionId, runtimeStatus.status, runtimeStatus.actionInfo, resolveStatusChangeUsage(runtimeStatus.actionInfo), { runtimeAccessToken: entry.runtimeAccessToken });
694
+ }
457
695
  forwardSdkOutputEvent(event, entry);
458
696
  const toolResultActionInfo = buildToolResultTimelineActionInfo(event);
459
697
  if (toolResultActionInfo) {
460
- void sendStatus(entry.agentId, event.sessionId, 'tool_result', toolResultActionInfo);
461
- }
462
- if (event.type === 'turn_complete' && event.data.usage) {
463
- sendStatus(entry.agentId, event.sessionId, 'waiting_input', undefined, resolveStatusChangeUsage({ usage: event.data.usage }));
698
+ void sendStatus(entry.agentId, event.sessionId, "tool_result", toolResultActionInfo, undefined, {
699
+ runtimeAccessToken: entry.runtimeAccessToken,
700
+ });
464
701
  }
465
702
  }
466
703
  });
467
- adapter.on('status-change', (sessionId, status, actionInfo) => {
704
+ adapter.on("status-change", (sessionId, status, actionInfo) => {
468
705
  const entry = sdkSessions.get(sessionId);
469
706
  if (entry) {
470
707
  entry.currentRuntimeStatus = status;
471
708
  entry.currentRuntimeActionType = actionInfo?.actionType;
472
709
  entry.currentRuntimeActionLabel = actionInfo?.actionLabel;
473
710
  entry.currentRuntimeActionDetail = actionInfo?.actionDetail;
474
- console.log(`[${definition.displayName} Adapter] Status changed: ${status} for agent ${entry.agentId}`, actionInfo || '');
711
+ console.log(`[${definition.displayName} Adapter] Status changed: ${status} for agent ${entry.agentId}`, actionInfo || "");
475
712
  void updatePersistedSessionRuntimeState(sessionId, {
476
713
  runtimeStatus: status,
477
714
  runtimeActionType: actionInfo?.actionType,
478
715
  runtimeActionLabel: actionInfo?.actionLabel,
479
716
  runtimeActionDetail: actionInfo?.actionDetail,
480
717
  });
481
- void sendStatus(entry.agentId, sessionId, status, actionInfo, resolveStatusChangeUsage(actionInfo));
718
+ void sendStatus(entry.agentId, sessionId, status, actionInfo, resolveStatusChangeUsage(actionInfo), {
719
+ runtimeAccessToken: entry.runtimeAccessToken,
720
+ });
482
721
  }
483
722
  });
484
- adapter.on('ask-user-question', (data) => {
723
+ adapter.on("ask-user-question", (data) => {
485
724
  const entry = sdkSessions.get(data.sessionId);
486
725
  if (entry) {
487
726
  console.log(`[${definition.displayName} Adapter] AskUserQuestion for agent ${entry.agentId}`);
488
- void sendQuestionRequest(entry.agentId, data.sessionId, Array.isArray(data.questions) ? data.questions : []);
727
+ void sendQuestionRequest(entry.agentId, data.sessionId, Array.isArray(data.questions) ? data.questions : [], {
728
+ runtimeAccessToken: entry.runtimeAccessToken,
729
+ });
489
730
  }
490
731
  });
491
- adapter.on('permission-request', (data) => {
732
+ adapter.on("permission-request", (data) => {
492
733
  const entry = sdkSessions.get(data.sessionId);
493
734
  if (entry) {
494
735
  console.log(`[${definition.displayName} Adapter] Permission request: ${data.toolName} for agent ${entry.agentId}`);
@@ -499,10 +740,10 @@ function wireSdkAdapterEvents(adapter, definition) {
499
740
  * 启动 SDK Agent 会话
500
741
  * POST /runtime/start
501
742
  */
502
- terminalRouter.post('/start', validateToken, async (req, res) => {
503
- const { agentId, workspacePath, mode = 'sdk', } = req.body || {};
743
+ terminalRouter.post("/start", validateToken, async (req, res) => {
744
+ const { agentId, workspacePath, mode = "sdk" } = req.body || {};
504
745
  if (!agentId || !workspacePath) {
505
- res.status(400).json({ error: 'agentId and workspacePath are required' });
746
+ res.status(400).json({ error: "agentId and workspacePath are required" });
506
747
  return;
507
748
  }
508
749
  enqueueMcpLaunchBinding({
@@ -513,7 +754,7 @@ terminalRouter.post('/start', validateToken, async (req, res) => {
513
754
  userId: req.body?.userId,
514
755
  schedulerBaseUrl: req.body?.schedulerBaseUrl,
515
756
  });
516
- if (mode && mode !== 'sdk') {
757
+ if (mode && mode !== "sdk") {
517
758
  console.warn(`[Runtime] Unsupported runtime mode "${mode}" requested for agent ${agentId}; starting SDK session instead.`);
518
759
  }
519
760
  await startSdkSession(req, res);
@@ -529,11 +770,12 @@ async function startSdkSession(req, res) {
529
770
  // 空闲命令配置
530
771
  idleInputAutoCommand, nonInputAutoCommand, } = req.body || {};
531
772
  let sessionId = null;
532
- const normalizedAgentId = String(agentId || '').trim();
773
+ const normalizedAgentId = String(agentId || "").trim();
774
+ const runtimeAccessToken = String(req.body?.runtimeAccessToken || "").trim() || undefined;
533
775
  try {
534
776
  ensureAdapterInitialized();
535
777
  if (!normalizedAgentId) {
536
- res.status(400).json({ error: 'agentId is required' });
778
+ res.status(400).json({ error: "agentId is required" });
537
779
  return;
538
780
  }
539
781
  const activeSession = findActiveSdkSessionByAgentId(normalizedAgentId);
@@ -545,8 +787,8 @@ async function startSdkSession(req, res) {
545
787
  if (persistedSession) {
546
788
  const reuseDecision = evaluatePersistedSessionReuse(persistedSession);
547
789
  if (!reuseDecision.reusable) {
548
- console.warn(`[Runtime] Ignoring stale persisted SDK session for agentId=${normalizedAgentId}, `
549
- + `sessionId=${persistedSession.sessionId}, reason=${reuseDecision.reason}`);
790
+ console.warn(`[Runtime] Ignoring stale persisted SDK session for agentId=${normalizedAgentId}, ` +
791
+ `sessionId=${persistedSession.sessionId}, reason=${reuseDecision.reason}`);
550
792
  await removePersistedSession(persistedSession.sessionId);
551
793
  }
552
794
  else {
@@ -556,7 +798,7 @@ async function startSdkSession(req, res) {
556
798
  agentId: persistedSession.agentId,
557
799
  workspacePath: persistedSession.workspacePath,
558
800
  command: persistedSession.command,
559
- mode: persistedSession.mode || 'sdk',
801
+ mode: persistedSession.mode || "sdk",
560
802
  pid: persistedSession.pid,
561
803
  reused: true,
562
804
  persistedOnly: true,
@@ -565,26 +807,28 @@ async function startSdkSession(req, res) {
565
807
  }
566
808
  }
567
809
  if (startingAgents.has(normalizedAgentId)) {
568
- res.status(409).json({ error: 'SDK session is already starting for this agent' });
810
+ res
811
+ .status(409)
812
+ .json({ error: "SDK session is already starting for this agent" });
569
813
  return;
570
814
  }
571
815
  startingAgents.add(normalizedAgentId);
572
816
  // 根据命令选择对应的 adapter
573
- const normalizedCommand = (command || 'claude').toLowerCase();
817
+ const normalizedCommand = (command || "acode").toLowerCase();
574
818
  const providerId = resolveSdkProviderId(command);
575
819
  // ★ 特征日志:明确输出选择的 provider
576
820
  console.log(`[Runtime] ★★★ SDK启动 ★★★ command="${command}", normalizedCommand="${normalizedCommand}", providerId="${providerId}"`);
577
821
  const adapter = adapterRegistry.get(providerId);
578
822
  sessionId = uuidv4();
579
- const savedClaudeConfig = providerId === 'claude-code'
823
+ const savedClaudeConfig = providerId === "claude-code"
580
824
  ? await readSavedClaudeCodeLaunchConfig()
581
825
  : null;
582
- const claudeLaunchConfig = providerId === 'claude-code'
826
+ const claudeLaunchConfig = providerId === "claude-code"
583
827
  ? buildClaudeCodeLaunchConfig(savedClaudeConfig, envOverrides)
584
828
  : buildClaudeCodeLaunchConfig(null, envOverrides);
585
829
  // 构建SDK配置
586
830
  const config = {
587
- command: command || 'claude',
831
+ command: command || "acode",
588
832
  workingDirectory: workspacePath,
589
833
  autoAccept: Boolean(autoAccept),
590
834
  initialPrompt,
@@ -602,22 +846,30 @@ async function startSdkSession(req, res) {
602
846
  config,
603
847
  providerId, // 记录使用的 provider
604
848
  seq: 0,
849
+ runtimeAccessToken,
605
850
  });
606
851
  // 启动 SDK 会话
607
852
  await adapter.startSession(sessionId, config);
853
+ // 尽早注入空闲命令,避免后续 PID 探测/进程注册延迟 poll_message 空闲轮询。
854
+ if (idleInputAutoCommand || nonInputAutoCommand) {
855
+ adapter.setIdleCommands?.(sessionId, {
856
+ idleInputCommand: idleInputAutoCommand || "",
857
+ nonInputCommand: nonInputAutoCommand || "",
858
+ });
859
+ }
608
860
  // ★ 尝试获取 SDK 进程的 PID(用于孤儿进程检测)
609
861
  // SDK 不直接暴露 PID,通过进程检测服务查找匹配的进程
610
862
  let sdkPid;
611
863
  try {
612
864
  // 等待一小段时间让进程启动
613
- await new Promise(resolve => setTimeout(resolve, 500));
865
+ await new Promise((resolve) => setTimeout(resolve, 500));
614
866
  // 尝试通过 adapter 获取 PID(如果支持)
615
867
  sdkPid = adapter.getSessionPid?.(sessionId);
616
868
  // 如果 adapter 不支持,尝试通过进程检测服务查找
617
869
  if (!sdkPid) {
618
870
  const processes = findClaudeCodeProcesses();
619
871
  // 查找匹配 agentId 或工作目录的进程
620
- const matched = processes.find(p => p.agentId === agentId ||
872
+ const matched = processes.find((p) => p.agentId === agentId ||
621
873
  p.cwd === workspacePath ||
622
874
  p.command.includes(workspacePath));
623
875
  if (matched) {
@@ -630,15 +882,16 @@ async function startSdkSession(req, res) {
630
882
  console.warn(`[Runtime] Failed to get SDK PID for agentId=${agentId}:`, pidError);
631
883
  }
632
884
  // ★ 持久化会话信息(包含 PID)
885
+ const startedStatus = resolveSdkSessionDisplayStatus(adapter.getSessionStatus(sessionId), sdkSessions.get(sessionId)?.currentRuntimeStatus);
633
886
  await upsertPersistedSession({
634
887
  sessionId,
635
888
  agentId,
636
889
  workspacePath,
637
890
  command: config.command,
638
891
  startedAt: new Date().toISOString(),
639
- status: 'running',
640
- runtimeStatus: 'running',
641
- mode: 'sdk',
892
+ status: "running",
893
+ runtimeStatus: startedStatus,
894
+ mode: "sdk",
642
895
  pid: sdkPid,
643
896
  providerSessionId: adapter.getProviderSessionId(sessionId),
644
897
  });
@@ -649,25 +902,19 @@ async function startSdkSession(req, res) {
649
902
  agentId: normalizedAgentId,
650
903
  sessionId,
651
904
  pid: sdkPid,
652
- mode: 'sdk',
905
+ mode: "sdk",
653
906
  workspacePath,
654
907
  command: config.command,
655
908
  });
656
909
  }
657
- // 存储空闲命令配置(用于SDK模式的空闲检测)
658
- if (idleInputAutoCommand || nonInputAutoCommand) {
659
- adapter.setIdleCommands?.(sessionId, {
660
- idleInputCommand: idleInputAutoCommand || '',
661
- nonInputCommand: nonInputAutoCommand || '',
662
- });
663
- }
664
910
  res.json({
665
911
  sessionId,
666
- status: 'running',
912
+ status: startedStatus,
913
+ runtimeStatus: startedStatus,
667
914
  agentId,
668
915
  workspacePath,
669
916
  command: config.command,
670
- mode: 'sdk',
917
+ mode: "sdk",
671
918
  pid: sdkPid, // ★ 返回 PID 信息
672
919
  });
673
920
  }
@@ -677,7 +924,9 @@ async function startSdkSession(req, res) {
677
924
  await removePersistedSession(sessionId);
678
925
  }
679
926
  const errorMessage = error instanceof Error ? error.message : String(error);
680
- res.status(500).json({ error: `Failed to start SDK session: ${errorMessage}` });
927
+ res
928
+ .status(500)
929
+ .json({ error: `Failed to start SDK session: ${errorMessage}` });
681
930
  }
682
931
  finally {
683
932
  if (normalizedAgentId) {
@@ -690,20 +939,20 @@ async function startSdkSession(req, res) {
690
939
  * 发送消息到 SDK 会话
691
940
  * POST /runtime/sdk/message
692
941
  */
693
- terminalRouter.post('/sdk/message', validateToken, async (req, res) => {
942
+ terminalRouter.post("/sdk/message", validateToken, async (req, res) => {
694
943
  const { sessionId, message } = req.body || {};
695
944
  if (!sessionId || !message) {
696
- res.status(400).json({ error: 'sessionId and message are required' });
945
+ res.status(400).json({ error: "sessionId and message are required" });
697
946
  return;
698
947
  }
699
948
  const entry = sdkSessions.get(sessionId);
700
949
  if (!entry) {
701
- res.status(404).json({ error: 'SDK session not found' });
950
+ res.status(404).json({ error: "SDK session not found" });
702
951
  return;
703
952
  }
704
953
  try {
705
954
  // 根据 providerId 获取对应的 adapter
706
- const providerId = entry.providerId || 'claude-code';
955
+ const providerId = entry.providerId || "claude-code";
707
956
  const adapter = adapterRegistry.get(providerId);
708
957
  await adapter.sendMessage(sessionId, message);
709
958
  res.json({ ok: true });
@@ -717,20 +966,20 @@ terminalRouter.post('/sdk/message', validateToken, async (req, res) => {
717
966
  * 回答 AskUserQuestion
718
967
  * POST /runtime/sdk/answer
719
968
  */
720
- terminalRouter.post('/sdk/answer', validateToken, async (req, res) => {
969
+ terminalRouter.post("/sdk/answer", validateToken, async (req, res) => {
721
970
  const { sessionId, answers } = req.body || {};
722
971
  if (!sessionId || !answers) {
723
- res.status(400).json({ error: 'sessionId and answers are required' });
972
+ res.status(400).json({ error: "sessionId and answers are required" });
724
973
  return;
725
974
  }
726
975
  const entry = sdkSessions.get(sessionId);
727
976
  if (!entry) {
728
- res.status(404).json({ error: 'SDK session not found' });
977
+ res.status(404).json({ error: "SDK session not found" });
729
978
  return;
730
979
  }
731
980
  try {
732
981
  // 根据 providerId 获取对应的 adapter
733
- const providerId = entry.providerId || 'claude-code';
982
+ const providerId = entry.providerId || "claude-code";
734
983
  const adapter = adapterRegistry.get(providerId);
735
984
  if (adapter.sendQuestionAnswer) {
736
985
  await adapter.sendQuestionAnswer(sessionId, answers);
@@ -746,20 +995,20 @@ terminalRouter.post('/sdk/answer', validateToken, async (req, res) => {
746
995
  * 确认权限请求
747
996
  * POST /runtime/sdk/confirm
748
997
  */
749
- terminalRouter.post('/sdk/confirm', validateToken, async (req, res) => {
998
+ terminalRouter.post("/sdk/confirm", validateToken, async (req, res) => {
750
999
  const { sessionId, accept } = req.body || {};
751
1000
  if (!sessionId) {
752
- res.status(400).json({ error: 'sessionId is required' });
1001
+ res.status(400).json({ error: "sessionId is required" });
753
1002
  return;
754
1003
  }
755
1004
  const entry = sdkSessions.get(sessionId);
756
1005
  if (!entry) {
757
- res.status(404).json({ error: 'SDK session not found' });
1006
+ res.status(404).json({ error: "SDK session not found" });
758
1007
  return;
759
1008
  }
760
1009
  try {
761
1010
  // 根据 providerId 获取对应的 adapter
762
- const providerId = entry.providerId || 'claude-code';
1011
+ const providerId = entry.providerId || "claude-code";
763
1012
  const adapter = adapterRegistry.get(providerId);
764
1013
  await adapter.sendConfirmation(sessionId, Boolean(accept));
765
1014
  res.json({ ok: true });
@@ -773,20 +1022,20 @@ terminalRouter.post('/sdk/confirm', validateToken, async (req, res) => {
773
1022
  * 中止当前轮次
774
1023
  * POST /runtime/sdk/abort
775
1024
  */
776
- terminalRouter.post('/sdk/abort', validateToken, async (req, res) => {
1025
+ terminalRouter.post("/sdk/abort", validateToken, async (req, res) => {
777
1026
  const { sessionId } = req.body || {};
778
1027
  if (!sessionId) {
779
- res.status(400).json({ error: 'sessionId is required' });
1028
+ res.status(400).json({ error: "sessionId is required" });
780
1029
  return;
781
1030
  }
782
1031
  const entry = sdkSessions.get(sessionId);
783
1032
  if (!entry) {
784
- res.status(404).json({ error: 'SDK session not found' });
1033
+ res.status(404).json({ error: "SDK session not found" });
785
1034
  return;
786
1035
  }
787
1036
  try {
788
1037
  // 根据 providerId 获取对应的 adapter
789
- const providerId = entry.providerId || 'claude-code';
1038
+ const providerId = entry.providerId || "claude-code";
790
1039
  const adapter = adapterRegistry.get(providerId);
791
1040
  await adapter.abortCurrentTurn(sessionId);
792
1041
  res.json({ ok: true });
@@ -800,20 +1049,20 @@ terminalRouter.post('/sdk/abort', validateToken, async (req, res) => {
800
1049
  * 停止 SDK 会话
801
1050
  * POST /runtime/sdk/stop
802
1051
  */
803
- terminalRouter.post('/sdk/stop', validateToken, async (req, res) => {
1052
+ terminalRouter.post("/sdk/stop", validateToken, async (req, res) => {
804
1053
  const { sessionId } = req.body || {};
805
1054
  if (!sessionId) {
806
- res.status(400).json({ error: 'sessionId is required' });
1055
+ res.status(400).json({ error: "sessionId is required" });
807
1056
  return;
808
1057
  }
809
1058
  const entry = sdkSessions.get(sessionId);
810
1059
  if (!entry) {
811
- res.json({ ok: true, status: 'stopped' });
1060
+ res.json({ ok: true, status: "stopped" });
812
1061
  return;
813
1062
  }
814
1063
  try {
815
1064
  // 根据 providerId 获取对应的 adapter
816
- const providerId = entry.providerId || 'claude-code';
1065
+ const providerId = entry.providerId || "claude-code";
817
1066
  const adapter = adapterRegistry.get(providerId);
818
1067
  await adapter.terminateSession(sessionId);
819
1068
  sdkSessions.delete(sessionId);
@@ -822,8 +1071,10 @@ terminalRouter.post('/sdk/stop', validateToken, async (req, res) => {
822
1071
  // ★ 从进程管理器中移除
823
1072
  const processManager = getAgentProcessManager();
824
1073
  await processManager.removeProcess(entry.agentId);
825
- await sendStatus(entry.agentId, null, 'stopped');
826
- res.json({ ok: true, status: 'stopped' });
1074
+ await sendStatus(entry.agentId, null, "stopped", undefined, undefined, {
1075
+ runtimeAccessToken: entry.runtimeAccessToken,
1076
+ });
1077
+ res.json({ ok: true, status: "stopped" });
827
1078
  }
828
1079
  catch (error) {
829
1080
  const errorMessage = error instanceof Error ? error.message : String(error);
@@ -834,20 +1085,20 @@ terminalRouter.post('/sdk/stop', validateToken, async (req, res) => {
834
1085
  * 获取 SDK 会话状态
835
1086
  * GET /runtime/sdk/status/:sessionId
836
1087
  */
837
- terminalRouter.get('/sdk/status/:sessionId', validateToken, (req, res) => {
1088
+ terminalRouter.get("/sdk/status/:sessionId", validateToken, (req, res) => {
838
1089
  const sessionId = req.params.sessionId;
839
1090
  if (!sessionId) {
840
- res.status(400).json({ error: 'sessionId is required' });
1091
+ res.status(400).json({ error: "sessionId is required" });
841
1092
  return;
842
1093
  }
843
1094
  const entry = sdkSessions.get(sessionId);
844
1095
  if (!entry) {
845
- res.status(404).json({ error: 'SDK session not found' });
1096
+ res.status(404).json({ error: "SDK session not found" });
846
1097
  return;
847
1098
  }
848
1099
  try {
849
1100
  // 根据 providerId 获取对应的 adapter
850
- const providerId = entry.providerId || 'claude-code';
1101
+ const providerId = entry.providerId || "claude-code";
851
1102
  const adapter = adapterRegistry.get(providerId);
852
1103
  const status = adapter.getSessionStatus(sessionId);
853
1104
  const conversation = adapter.getConversation(sessionId);
@@ -870,15 +1121,15 @@ terminalRouter.get('/sdk/status/:sessionId', validateToken, (req, res) => {
870
1121
  * 主流程:校验 session/command -> 读取会话 workspacePath -> 在该 cwd 下启动 shell 命令 -> 通过终端输出回调流式返回。
871
1122
  * POST /runtime/command
872
1123
  */
873
- terminalRouter.post('/command', validateToken, async (req, res) => {
1124
+ terminalRouter.post("/command", validateToken, async (req, res) => {
874
1125
  const { sessionId, command } = req.body || {};
875
1126
  if (!sessionId || command === undefined || command === null) {
876
- res.status(400).json({ error: 'sessionId and command are required' });
1127
+ res.status(400).json({ error: "sessionId and command are required" });
877
1128
  return;
878
1129
  }
879
1130
  try {
880
1131
  const result = await executeTerminalCommand(String(sessionId), command);
881
- res.json({ ok: true, mode: 'terminal', ...result });
1132
+ res.json({ ok: true, mode: "terminal", ...result });
882
1133
  }
883
1134
  catch (error) {
884
1135
  const errorMessage = error instanceof Error ? error.message : String(error);
@@ -892,31 +1143,33 @@ terminalRouter.post('/command', validateToken, async (req, res) => {
892
1143
  * 主流程:校验 session/input -> 定位 SDK 会话 -> 通过 provider adapter 发送用户消息。
893
1144
  * POST /runtime/input
894
1145
  */
895
- terminalRouter.post('/input', validateToken, async (req, res) => {
1146
+ terminalRouter.post("/input", validateToken, async (req, res) => {
896
1147
  const { sessionId, input, mode } = req.body || {};
897
1148
  if (!sessionId || input === undefined || input === null) {
898
- res.status(400).json({ error: 'sessionId and input are required' });
1149
+ res.status(400).json({ error: "sessionId and input are required" });
899
1150
  return;
900
1151
  }
901
1152
  const entry = sdkSessions.get(sessionId);
902
1153
  if (!entry) {
903
- res.status(404).json({ error: 'SDK session not found' });
1154
+ res.status(404).json({ error: "SDK session not found" });
904
1155
  return;
905
1156
  }
906
1157
  try {
907
- const providerId = entry.providerId || 'claude-code';
1158
+ const providerId = entry.providerId || "claude-code";
908
1159
  const adapter = adapterRegistry.get(providerId);
909
- if (mode === 'shortcut') {
1160
+ if (mode === "shortcut") {
910
1161
  if (!adapter.sendShortcutInput) {
911
- res.status(400).json({ error: 'shortcut input is not supported by this SDK provider' });
1162
+ res.status(400).json({
1163
+ error: "shortcut input is not supported by this SDK provider",
1164
+ });
912
1165
  return;
913
1166
  }
914
1167
  await adapter.sendShortcutInput(sessionId, String(input));
915
- res.json({ ok: true, mode: 'shortcut' });
1168
+ res.json({ ok: true, mode: "shortcut" });
916
1169
  return;
917
1170
  }
918
1171
  await adapter.sendMessage(sessionId, await resolveSdkInputMessage(input));
919
- res.json({ ok: true, mode: 'sdk' });
1172
+ res.json({ ok: true, mode: "sdk" });
920
1173
  }
921
1174
  catch (error) {
922
1175
  const errorMessage = error instanceof Error ? error.message : String(error);
@@ -929,22 +1182,22 @@ terminalRouter.post('/input', validateToken, async (req, res) => {
929
1182
  * 主流程:按 agentId 定位 SDK 会话 -> 更新 adapter 内存配置 -> 同步持久化会话字段。
930
1183
  * POST /runtime/update-auto-commands
931
1184
  */
932
- terminalRouter.post('/update-auto-commands', validateToken, async (req, res) => {
933
- const { agentId, idleInputAutoCommand = '', nonInputAutoCommand = '' } = req.body || {};
934
- const normalizedAgentId = String(agentId || '').trim();
1185
+ terminalRouter.post("/update-auto-commands", validateToken, async (req, res) => {
1186
+ const { agentId, idleInputAutoCommand = "", nonInputAutoCommand = "", } = req.body || {};
1187
+ const normalizedAgentId = String(agentId || "").trim();
935
1188
  if (!normalizedAgentId) {
936
- res.status(400).json({ error: 'agentId is required' });
1189
+ res.status(400).json({ error: "agentId is required" });
937
1190
  return;
938
1191
  }
939
1192
  const entry = [...sdkSessions.values()].find((session) => session.agentId === normalizedAgentId);
940
1193
  const commands = {
941
- idleInputCommand: String(idleInputAutoCommand || ''),
942
- nonInputCommand: String(nonInputAutoCommand || ''),
1194
+ idleInputCommand: String(idleInputAutoCommand || ""),
1195
+ nonInputCommand: String(nonInputAutoCommand || ""),
943
1196
  };
944
1197
  try {
945
1198
  let sessionUpdated = false;
946
1199
  if (entry) {
947
- const adapter = adapterRegistry.get(entry.providerId || 'claude-code');
1200
+ const adapter = adapterRegistry.get(entry.providerId || "claude-code");
948
1201
  adapter.setIdleCommands?.(entry.sessionId, commands);
949
1202
  sessionUpdated = true;
950
1203
  }
@@ -952,11 +1205,11 @@ terminalRouter.post('/update-auto-commands', validateToken, async (req, res) =>
952
1205
  idleInputAutoCommand: commands.idleInputCommand,
953
1206
  nonInputAutoCommand: commands.nonInputCommand,
954
1207
  });
955
- res.json({ ok: true, mode: 'sdk', sessionUpdated, persistedUpdated });
1208
+ res.json({ ok: true, mode: "sdk", sessionUpdated, persistedUpdated });
956
1209
  }
957
1210
  catch (error) {
958
1211
  const errorMessage = error instanceof Error ? error.message : String(error);
959
- res.status(500).json({ ok: false, mode: 'sdk', error: errorMessage });
1212
+ res.status(500).json({ ok: false, mode: "sdk", error: errorMessage });
960
1213
  }
961
1214
  });
962
1215
  /**
@@ -965,37 +1218,43 @@ terminalRouter.post('/update-auto-commands', validateToken, async (req, res) =>
965
1218
  * SDK 会话没有终端尺寸概念;保留该端点用于前端 xterm fit 回调,返回 no-op。
966
1219
  * POST /runtime/resize
967
1220
  */
968
- terminalRouter.post('/resize', validateToken, (req, res) => {
1221
+ terminalRouter.post("/resize", validateToken, (req, res) => {
969
1222
  const { sessionId, cols, rows } = req.body || {};
970
1223
  if (!sessionId || !cols || !rows) {
971
- res.status(400).json({ error: 'sessionId, cols, rows are required' });
1224
+ res.status(400).json({ error: "sessionId, cols, rows are required" });
972
1225
  return;
973
1226
  }
974
1227
  if (!sdkSessions.has(sessionId)) {
975
- res.status(404).json({ error: 'SDK session not found' });
1228
+ res.status(404).json({ error: "SDK session not found" });
976
1229
  return;
977
1230
  }
978
- res.json({ ok: true, mode: 'sdk', noop: true, cols: Number(cols), rows: Number(rows) });
1231
+ res.json({
1232
+ ok: true,
1233
+ mode: "sdk",
1234
+ noop: true,
1235
+ cols: Number(cols),
1236
+ rows: Number(rows),
1237
+ });
979
1238
  });
980
1239
  /**
981
1240
  * 停止 SDK 会话。
982
1241
  * POST /runtime/stop
983
1242
  */
984
- terminalRouter.post('/stop', validateToken, async (req, res) => {
1243
+ terminalRouter.post("/stop", validateToken, async (req, res) => {
985
1244
  const { sessionId } = req.body || {};
986
1245
  if (!sessionId) {
987
- res.status(400).json({ error: 'sessionId is required' });
1246
+ res.status(400).json({ error: "sessionId is required" });
988
1247
  return;
989
1248
  }
990
1249
  const sdkEntry = sdkSessions.get(sessionId);
991
1250
  if (!sdkEntry) {
992
1251
  await removePersistedSession(sessionId);
993
- res.json({ ok: true, status: 'stopped', mode: 'none' });
1252
+ res.json({ ok: true, status: "stopped", mode: "none" });
994
1253
  return;
995
1254
  }
996
1255
  const agentId = sdkEntry.agentId;
997
1256
  try {
998
- const providerId = sdkEntry.providerId || 'claude-code';
1257
+ const providerId = sdkEntry.providerId || "claude-code";
999
1258
  const adapter = adapterRegistry.get(providerId);
1000
1259
  await adapter.terminateSession(sessionId);
1001
1260
  sdkSessions.delete(sessionId);
@@ -1003,13 +1262,20 @@ terminalRouter.post('/stop', validateToken, async (req, res) => {
1003
1262
  await removePersistedSession(sessionId);
1004
1263
  const processManager = getAgentProcessManager();
1005
1264
  await processManager.removeProcess(agentId);
1006
- await sendStatus(agentId, null, 'stopped');
1007
- res.json({ ok: true, status: 'stopped', mode: 'sdk' });
1265
+ await sendStatus(agentId, null, "stopped", undefined, undefined, {
1266
+ runtimeAccessToken: sdkEntry.runtimeAccessToken,
1267
+ });
1268
+ res.json({ ok: true, status: "stopped", mode: "sdk" });
1008
1269
  }
1009
1270
  catch (error) {
1010
1271
  const errorMessage = error instanceof Error ? error.message : String(error);
1011
1272
  console.error(`[Runtime] Failed to terminate SDK session ${sessionId}:`, errorMessage);
1012
- res.status(500).json({ ok: false, status: 'stop_failed', mode: 'sdk', error: errorMessage });
1273
+ res.status(500).json({
1274
+ ok: false,
1275
+ status: "stop_failed",
1276
+ mode: "sdk",
1277
+ error: errorMessage,
1278
+ });
1013
1279
  }
1014
1280
  });
1015
1281
  /**
@@ -1017,20 +1283,22 @@ terminalRouter.post('/stop', validateToken, async (req, res) => {
1017
1283
  * POST /runtime/stop-all
1018
1284
  * 用于 aws-mcp-server 重启时清理所有 SDK Agent 会话
1019
1285
  */
1020
- terminalRouter.post('/stop-all', validateToken, async (_req, res) => {
1286
+ terminalRouter.post("/stop-all", validateToken, async (_req, res) => {
1021
1287
  const stoppedSdk = [];
1022
1288
  const errors = [];
1023
1289
  // 1. 停止所有 SDK 会话
1024
1290
  for (const [sessionId, entry] of sdkSessions.entries()) {
1025
1291
  try {
1026
1292
  // 根据 providerId 获取对应的 adapter
1027
- const providerId = entry.providerId || 'claude-code';
1293
+ const providerId = entry.providerId || "claude-code";
1028
1294
  const adapter = adapterRegistry.get(providerId);
1029
1295
  await adapter.terminateSession(sessionId);
1030
1296
  sdkSessions.delete(sessionId);
1031
1297
  stopTerminalCommandProcess(sessionId);
1032
1298
  stoppedSdk.push(sessionId);
1033
- await sendStatus(entry.agentId, null, 'stopped');
1299
+ await sendStatus(entry.agentId, null, "stopped", undefined, undefined, {
1300
+ runtimeAccessToken: entry.runtimeAccessToken,
1301
+ });
1034
1302
  }
1035
1303
  catch (error) {
1036
1304
  const errorMessage = error instanceof Error ? error.message : String(error);
@@ -1041,7 +1309,7 @@ terminalRouter.post('/stop-all', validateToken, async (_req, res) => {
1041
1309
  // 直接清空整个持久化文件,确保不会有残留
1042
1310
  try {
1043
1311
  await savePersistedSessions([]);
1044
- console.log('[Runtime] 已清空所有持久化会话');
1312
+ console.log("[Runtime] 已清空所有持久化会话");
1045
1313
  }
1046
1314
  catch (error) {
1047
1315
  const errorMessage = error instanceof Error ? error.message : String(error);