aws-runtime-bridge 1.9.79 → 1.9.81

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 (596) hide show
  1. package/dist/adapter/AcodeSdkAdapter.d.ts +0 -1
  2. package/dist/adapter/AcodeSdkAdapter.js +11 -1986
  3. package/dist/adapter/AdapterRegistry.d.ts +0 -1
  4. package/dist/adapter/AdapterRegistry.js +2 -100
  5. package/dist/adapter/ClaudeSdkAdapter.d.ts +0 -1
  6. package/dist/adapter/ClaudeSdkAdapter.js +4 -1300
  7. package/dist/adapter/CodexSdkAdapter.d.ts +0 -1
  8. package/dist/adapter/CodexSdkAdapter.js +4 -836
  9. package/dist/adapter/OpencodeSdkAdapter.d.ts +0 -1
  10. package/dist/adapter/OpencodeSdkAdapter.js +4 -879
  11. package/dist/adapter/SdkProviderSpi.d.ts +0 -1
  12. package/dist/adapter/SdkProviderSpi.js +2 -55
  13. package/dist/adapter/acode-tool-names.d.ts +0 -1
  14. package/dist/adapter/acode-tool-names.js +2 -37
  15. package/dist/adapter/idle-poll-loop.d.ts +0 -1
  16. package/dist/adapter/idle-poll-loop.js +2 -59
  17. package/dist/adapter/index.d.ts +0 -1
  18. package/dist/adapter/index.js +2 -11
  19. package/dist/adapter/instance-ai-config-resolver.d.ts +0 -1
  20. package/dist/adapter/instance-ai-config-resolver.js +2 -46
  21. package/dist/adapter/types.d.ts +0 -1
  22. package/dist/adapter/types.js +2 -443
  23. package/dist/adapters/cc-switch/common.d.ts +0 -1
  24. package/dist/adapters/cc-switch/common.js +2 -61
  25. package/dist/adapters/cc-switch/index.d.ts +0 -1
  26. package/dist/adapters/cc-switch/index.js +2 -57
  27. package/dist/adapters/cc-switch/mcp-acode.d.ts +0 -1
  28. package/dist/adapters/cc-switch/mcp-acode.js +2 -71
  29. package/dist/adapters/cc-switch/mcp-claude.d.ts +0 -1
  30. package/dist/adapters/cc-switch/mcp-claude.js +2 -89
  31. package/dist/adapters/cc-switch/mcp-claudecode.d.ts +0 -1
  32. package/dist/adapters/cc-switch/mcp-claudecode.js +2 -77
  33. package/dist/adapters/cc-switch/mcp-codex.d.ts +0 -1
  34. package/dist/adapters/cc-switch/mcp-codex.js +11 -249
  35. package/dist/adapters/cc-switch/mcp-opencode.d.ts +0 -1
  36. package/dist/adapters/cc-switch/mcp-opencode.js +2 -97
  37. package/dist/adapters/cc-switch/mcp-placeholder.d.ts +0 -1
  38. package/dist/adapters/cc-switch/mcp-placeholder.js +2 -19
  39. package/dist/adapters/cc-switch/skill-acode.d.ts +0 -1
  40. package/dist/adapters/cc-switch/skill-acode.js +2 -12
  41. package/dist/adapters/cc-switch/skill-claude.d.ts +0 -1
  42. package/dist/adapters/cc-switch/skill-claude.js +2 -11
  43. package/dist/adapters/cc-switch/skill-claudecode.d.ts +0 -1
  44. package/dist/adapters/cc-switch/skill-claudecode.js +2 -11
  45. package/dist/adapters/cc-switch/skill-codex.d.ts +0 -1
  46. package/dist/adapters/cc-switch/skill-codex.js +2 -6
  47. package/dist/adapters/cc-switch/skill-directory.d.ts +0 -1
  48. package/dist/adapters/cc-switch/skill-directory.js +3 -137
  49. package/dist/adapters/cc-switch/skill-opencode.d.ts +0 -1
  50. package/dist/adapters/cc-switch/skill-opencode.js +2 -11
  51. package/dist/adapters/cc-switch/skill-placeholder.d.ts +0 -1
  52. package/dist/adapters/cc-switch/skill-placeholder.js +2 -19
  53. package/dist/config.d.ts +0 -1
  54. package/dist/config.js +2 -290
  55. package/dist/index.d.ts +0 -1
  56. package/dist/index.js +2 -573
  57. package/dist/middleware/auth.d.ts +0 -1
  58. package/dist/middleware/auth.js +2 -98
  59. package/dist/routes/acode-todos.d.ts +0 -1
  60. package/dist/routes/acode-todos.js +2 -75
  61. package/dist/routes/ai-sources.d.ts +0 -1
  62. package/dist/routes/ai-sources.js +2 -132
  63. package/dist/routes/dashboard.d.ts +0 -1
  64. package/dist/routes/dashboard.js +182 -649
  65. package/dist/routes/events.d.ts +0 -1
  66. package/dist/routes/events.js +2 -22
  67. package/dist/routes/file-browser.d.ts +0 -1
  68. package/dist/routes/file-browser.js +2 -781
  69. package/dist/routes/file-diff-rollback.d.ts +0 -1
  70. package/dist/routes/file-diff-rollback.js +2 -301
  71. package/dist/routes/file-snapshot.d.ts +0 -1
  72. package/dist/routes/file-snapshot.js +2 -156
  73. package/dist/routes/git.d.ts +0 -1
  74. package/dist/routes/git.js +28 -1900
  75. package/dist/routes/instance.d.ts +0 -1
  76. package/dist/routes/instance.js +3 -748
  77. package/dist/routes/mcp.d.ts +0 -1
  78. package/dist/routes/mcp.js +2 -196
  79. package/dist/routes/processes.d.ts +0 -1
  80. package/dist/routes/processes.js +2 -252
  81. package/dist/routes/properties.d.ts +0 -1
  82. package/dist/routes/properties.js +2 -72
  83. package/dist/routes/pty.d.ts +0 -1
  84. package/dist/routes/pty.js +6 -560
  85. package/dist/routes/runtime-binding.d.ts +0 -1
  86. package/dist/routes/runtime-binding.js +2 -263
  87. package/dist/routes/sessions.d.ts +0 -1
  88. package/dist/routes/sessions.js +2 -758
  89. package/dist/routes/skills.d.ts +0 -1
  90. package/dist/routes/skills.js +2 -187
  91. package/dist/routes/sub-agents.d.ts +0 -1
  92. package/dist/routes/sub-agents.js +20 -279
  93. package/dist/routes/system-metrics.d.ts +0 -1
  94. package/dist/routes/system-metrics.js +2 -20
  95. package/dist/routes/task-context.d.ts +0 -1
  96. package/dist/routes/task-context.js +2 -78
  97. package/dist/routes/terminal.d.ts +0 -1
  98. package/dist/routes/terminal.js +54 -2882
  99. package/dist/routes/yml.d.ts +0 -1
  100. package/dist/routes/yml.js +2 -223
  101. package/dist/services/acode-package.d.ts +0 -1
  102. package/dist/services/acode-package.js +2 -111
  103. package/dist/services/agent-event-store.d.ts +0 -1
  104. package/dist/services/agent-event-store.js +2 -81
  105. package/dist/services/agent-process-manager.d.ts +0 -1
  106. package/dist/services/agent-process-manager.js +2 -796
  107. package/dist/services/ai-source-apply.d.ts +0 -1
  108. package/dist/services/ai-source-apply.js +2 -83
  109. package/dist/services/auto-register.d.ts +5 -1
  110. package/dist/services/auto-register.js +2 -1053
  111. package/dist/services/aws-client-agent-mcp.d.ts +0 -1
  112. package/dist/services/aws-client-agent-mcp.js +3 -337
  113. package/dist/services/bridge-package-publish-hooks.d.ts +0 -1
  114. package/dist/services/bridge-package-publish-hooks.js +2 -115
  115. package/dist/services/cc-switch-sdk.d.ts +0 -1
  116. package/dist/services/cc-switch-sdk.js +2 -135
  117. package/dist/services/cli-commands.d.ts +6 -1
  118. package/dist/services/cli-commands.js +6 -645
  119. package/dist/services/event-delta-pusher.d.ts +0 -1
  120. package/dist/services/event-delta-pusher.js +2 -95
  121. package/dist/services/fileSnapshotStore.d.ts +0 -1
  122. package/dist/services/fileSnapshotStore.js +2 -500
  123. package/dist/services/instance-ai-config-service.d.ts +0 -1
  124. package/dist/services/instance-ai-config-service.js +2 -174
  125. package/dist/services/instance-init-service.d.ts +0 -1
  126. package/dist/services/instance-init-service.js +2 -193
  127. package/dist/services/instance-service.d.ts +0 -1
  128. package/dist/services/instance-service.js +2 -237
  129. package/dist/services/instance-state.d.ts +0 -1
  130. package/dist/services/instance-state.js +2 -94
  131. package/dist/services/legacy-path-migration.d.ts +0 -1
  132. package/dist/services/legacy-path-migration.js +2 -66
  133. package/dist/services/lifecycle-state.d.ts +0 -1
  134. package/dist/services/lifecycle-state.js +3 -110
  135. package/dist/services/mcp-launch-binding-queue.d.ts +0 -1
  136. package/dist/services/mcp-launch-binding-queue.js +2 -175
  137. package/dist/services/orphan-monitor.d.ts +0 -1
  138. package/dist/services/orphan-monitor.js +2 -382
  139. package/dist/services/panel-auth.d.ts +0 -1
  140. package/dist/services/panel-auth.js +2 -168
  141. package/dist/services/process-detector.d.ts +0 -1
  142. package/dist/services/process-detector.js +18 -1019
  143. package/dist/services/process-registry.d.ts +0 -1
  144. package/dist/services/process-registry.js +2 -375
  145. package/dist/services/ring-buffer.d.ts +0 -1
  146. package/dist/services/ring-buffer.js +2 -103
  147. package/dist/services/runtime-binding.d.ts +0 -1
  148. package/dist/services/runtime-binding.js +6 -363
  149. package/dist/services/runtime-lifecycle-policy.d.ts +0 -1
  150. package/dist/services/runtime-lifecycle-policy.js +2 -35
  151. package/dist/services/session-output.d.ts +0 -1
  152. package/dist/services/session-output.js +5 -539
  153. package/dist/services/startup-config-wizard.d.ts +0 -1
  154. package/dist/services/startup-config-wizard.js +14 -258
  155. package/dist/services/sub-agent-manager-instance.d.ts +0 -1
  156. package/dist/services/sub-agent-manager-instance.js +2 -12
  157. package/dist/services/system-metrics.d.ts +0 -1
  158. package/dist/services/system-metrics.js +2 -252
  159. package/dist/services/terminal-persistence.d.ts +0 -1
  160. package/dist/services/terminal-persistence.js +2 -176
  161. package/dist/services/tool-installer.d.ts +0 -1
  162. package/dist/services/tool-installer.js +5 -751
  163. package/dist/services/tunnel-client.d.ts +0 -1
  164. package/dist/services/tunnel-client.js +2 -402
  165. package/dist/services/windows-service-manager.d.ts +3 -3
  166. package/dist/services/windows-service-manager.js +2 -59
  167. package/dist/services/workspace-files.d.ts +0 -1
  168. package/dist/services/workspace-files.js +2 -1275
  169. package/dist/services/workspace-watch.d.ts +0 -1
  170. package/dist/services/workspace-watch.js +2 -123
  171. package/dist/services/workspaceTaskChangeWatcher.d.ts +0 -1
  172. package/dist/services/workspaceTaskChangeWatcher.js +2 -483
  173. package/dist/types.d.ts +0 -1
  174. package/dist/types.js +1 -1
  175. package/dist/utils/file-utils.d.ts +0 -1
  176. package/dist/utils/file-utils.js +3 -208
  177. package/dist/utils/instance-ai-config-debug-logger.d.ts +0 -1
  178. package/dist/utils/instance-ai-config-debug-logger.js +4 -63
  179. package/dist/utils/logger.d.ts +0 -1
  180. package/dist/utils/logger.js +2 -154
  181. package/dist/utils/mcp-utils.d.ts +0 -1
  182. package/dist/utils/mcp-utils.js +2 -187
  183. package/dist/utils/path-utils.d.ts +0 -1
  184. package/dist/utils/path-utils.js +2 -44
  185. package/dist/utils/sdk-package-loader.d.ts +0 -1
  186. package/dist/utils/sdk-package-loader.js +2 -78
  187. package/dist/utils/validation.d.ts +0 -1
  188. package/dist/utils/validation.js +2 -53
  189. package/dist/utils/yaml-utils.d.ts +0 -1
  190. package/dist/utils/yaml-utils.js +2 -312
  191. package/node_modules/@cc-switch/sdk/dist/constants.d.ts +0 -1
  192. package/node_modules/@cc-switch/sdk/dist/constants.js +2 -63
  193. package/node_modules/@cc-switch/sdk/dist/errors.d.ts +0 -1
  194. package/node_modules/@cc-switch/sdk/dist/errors.js +2 -8
  195. package/node_modules/@cc-switch/sdk/dist/index.d.ts +0 -1
  196. package/node_modules/@cc-switch/sdk/dist/index.js +2 -9
  197. package/node_modules/@cc-switch/sdk/dist/schemas.d.ts +0 -1
  198. package/node_modules/@cc-switch/sdk/dist/schemas.js +2 -38
  199. package/node_modules/@cc-switch/sdk/dist/sdk.d.ts +0 -1
  200. package/node_modules/@cc-switch/sdk/dist/sdk.js +2 -470
  201. package/node_modules/@cc-switch/sdk/dist/services/ai-config-service.d.ts +0 -1
  202. package/node_modules/@cc-switch/sdk/dist/services/ai-config-service.js +4 -307
  203. package/node_modules/@cc-switch/sdk/dist/services/mcp-model.d.ts +0 -1
  204. package/node_modules/@cc-switch/sdk/dist/services/mcp-model.js +2 -37
  205. package/node_modules/@cc-switch/sdk/dist/services/mcp-service.d.ts +0 -1
  206. package/node_modules/@cc-switch/sdk/dist/services/mcp-service.js +2 -9
  207. package/node_modules/@cc-switch/sdk/dist/services/skill-model.d.ts +0 -1
  208. package/node_modules/@cc-switch/sdk/dist/services/skill-model.js +2 -41
  209. package/node_modules/@cc-switch/sdk/dist/services/skill-service.d.ts +0 -1
  210. package/node_modules/@cc-switch/sdk/dist/services/skill-service.js +2 -9
  211. package/node_modules/@cc-switch/sdk/dist/state.d.ts +0 -1
  212. package/node_modules/@cc-switch/sdk/dist/state.js +2 -19
  213. package/node_modules/@cc-switch/sdk/dist/types.d.ts +0 -1
  214. package/node_modules/@cc-switch/sdk/dist/types.js +1 -1
  215. package/node_modules/@cc-switch/sdk/dist/utils/fs.d.ts +0 -1
  216. package/node_modules/@cc-switch/sdk/dist/utils/fs.js +3 -114
  217. package/node_modules/@cc-switch/sdk/dist/utils/id.d.ts +0 -1
  218. package/node_modules/@cc-switch/sdk/dist/utils/id.js +2 -12
  219. package/node_modules/@cc-switch/sdk/package.json +1 -1
  220. package/package/acode/dist/ask-user-question-tool.d.ts +0 -1
  221. package/package/acode/dist/ask-user-question-tool.js +4 -233
  222. package/package/acode/dist/built-in-file-tools.d.ts +0 -1
  223. package/package/acode/dist/built-in-file-tools.js +4 -966
  224. package/package/acode/dist/built-in-symbols-tool.d.ts +0 -1
  225. package/package/acode/dist/built-in-symbols-tool.js +2 -117
  226. package/package/acode/dist/builtins/commandArtifacts.d.ts +0 -1
  227. package/package/acode/dist/builtins/commandArtifacts.js +9 -97
  228. package/package/acode/dist/builtins/commandOutputEvents.d.ts +0 -1
  229. package/package/acode/dist/builtins/commandOutputEvents.js +2 -56
  230. package/package/acode/dist/builtins/shellCommandPreparation.d.ts +0 -1
  231. package/package/acode/dist/builtins/shellCommandPreparation.js +5 -44
  232. package/package/acode/dist/builtins/shellCommandResult.d.ts +0 -1
  233. package/package/acode/dist/builtins/shellCommandResult.js +2 -71
  234. package/package/acode/dist/builtins/shellCommandRunner.d.ts +0 -1
  235. package/package/acode/dist/builtins/shellCommandRunner.js +2 -115
  236. package/package/acode/dist/builtins/shellOutputDecoder.d.ts +0 -1
  237. package/package/acode/dist/builtins/shellOutputDecoder.js +2 -94
  238. package/package/acode/dist/cli.d.ts +0 -1
  239. package/package/acode/dist/cli.js +4 -38
  240. package/package/acode/dist/commands/commandLoader.d.ts +0 -1
  241. package/package/acode/dist/commands/commandLoader.js +2 -33
  242. package/package/acode/dist/commands/commandParser.d.ts +0 -1
  243. package/package/acode/dist/commands/commandParser.js +3 -77
  244. package/package/acode/dist/commands/commandRegistry.d.ts +0 -1
  245. package/package/acode/dist/commands/commandRegistry.js +2 -30
  246. package/package/acode/dist/compaction.d.ts +0 -1
  247. package/package/acode/dist/compaction.js +7 -919
  248. package/package/acode/dist/config.d.ts +0 -1
  249. package/package/acode/dist/config.js +5 -105
  250. package/package/acode/dist/fs-utils.d.ts +0 -1
  251. package/package/acode/dist/fs-utils.js +2 -79
  252. package/package/acode/dist/hooks/hookBus.d.ts +0 -1
  253. package/package/acode/dist/hooks/hookBus.js +2 -40
  254. package/package/acode/dist/index.d.ts +0 -1
  255. package/package/acode/dist/index.js +2 -19
  256. package/package/acode/dist/mcpToolNames.d.ts +0 -1
  257. package/package/acode/dist/mcpToolNames.js +2 -17
  258. package/package/acode/dist/parsers/ast-cache.d.ts +0 -1
  259. package/package/acode/dist/parsers/ast-cache.js +2 -76
  260. package/package/acode/dist/parsers/language-detector.d.ts +0 -1
  261. package/package/acode/dist/parsers/language-detector.js +2 -44
  262. package/package/acode/dist/parsers/language-queries.d.ts +0 -1
  263. package/package/acode/dist/parsers/language-queries.js +12 -602
  264. package/package/acode/dist/parsers/symbol-extractor.d.ts +0 -1
  265. package/package/acode/dist/parsers/symbol-extractor.js +3 -164
  266. package/package/acode/dist/parsers/tree-sitter-loader.d.ts +0 -1
  267. package/package/acode/dist/parsers/tree-sitter-loader.js +2 -64
  268. package/package/acode/dist/parsers/types.d.ts +0 -1
  269. package/package/acode/dist/parsers/types.js +1 -7
  270. package/package/acode/dist/runtime.d.ts +0 -1
  271. package/package/acode/dist/runtime.js +86 -3771
  272. package/package/acode/dist/skill-tool.d.ts +0 -1
  273. package/package/acode/dist/skill-tool.js +5 -174
  274. package/package/acode/dist/sub-agent-manager.d.ts +0 -1
  275. package/package/acode/dist/sub-agent-manager.js +2 -1159
  276. package/package/acode/dist/sub-agent-store.d.ts +0 -1
  277. package/package/acode/dist/sub-agent-store.js +4 -236
  278. package/package/acode/dist/sub-agent-tools.d.ts +0 -1
  279. package/package/acode/dist/sub-agent-tools.js +15 -369
  280. package/package/acode/dist/sub-agent-types.d.ts +0 -1
  281. package/package/acode/dist/sub-agent-types.js +1 -8
  282. package/package/acode/dist/todo-store.d.ts +0 -1
  283. package/package/acode/dist/todo-store.js +2 -73
  284. package/package/acode/dist/todo-tool.d.ts +0 -1
  285. package/package/acode/dist/todo-tool.js +5 -121
  286. package/package/acode/dist/types.d.ts +0 -1
  287. package/package/acode/dist/types.js +1 -1
  288. package/package/acode/package.json +2 -2
  289. package/package/aws-client-agent-mcp/dist/agent-client.d.ts +0 -1
  290. package/package/aws-client-agent-mcp/dist/agent-client.js +2 -890
  291. package/package/aws-client-agent-mcp/dist/agent-client.test.d.ts +0 -1
  292. package/package/aws-client-agent-mcp/dist/agent-client.test.js +0 -1
  293. package/package/aws-client-agent-mcp/dist/config.d.ts +0 -1
  294. package/package/aws-client-agent-mcp/dist/config.js +2 -243
  295. package/package/aws-client-agent-mcp/dist/config.test.d.ts +0 -1
  296. package/package/aws-client-agent-mcp/dist/config.test.js +0 -1
  297. package/package/aws-client-agent-mcp/dist/constants.d.ts +0 -1
  298. package/package/aws-client-agent-mcp/dist/constants.js +2 -24
  299. package/package/aws-client-agent-mcp/dist/context-manager.d.ts +0 -1
  300. package/package/aws-client-agent-mcp/dist/context-manager.js +2 -477
  301. package/package/aws-client-agent-mcp/dist/context-manager.test.d.ts +0 -1
  302. package/package/aws-client-agent-mcp/dist/context-manager.test.js +0 -1
  303. package/package/aws-client-agent-mcp/dist/http-client.d.ts +0 -1
  304. package/package/aws-client-agent-mcp/dist/http-client.js +2 -446
  305. package/package/aws-client-agent-mcp/dist/http-client.test.d.ts +0 -1
  306. package/package/aws-client-agent-mcp/dist/http-client.test.js +0 -1
  307. package/package/aws-client-agent-mcp/dist/index.d.ts +0 -1
  308. package/package/aws-client-agent-mcp/dist/index.js +2 -34
  309. package/package/aws-client-agent-mcp/dist/logger.d.ts +0 -1
  310. package/package/aws-client-agent-mcp/dist/logger.js +3 -178
  311. package/package/aws-client-agent-mcp/dist/logger.test.d.ts +0 -1
  312. package/package/aws-client-agent-mcp/dist/logger.test.js +0 -1
  313. package/package/aws-client-agent-mcp/dist/mcp-server.d.ts +0 -1
  314. package/package/aws-client-agent-mcp/dist/mcp-server.js +38 -2709
  315. package/package/aws-client-agent-mcp/dist/mcp-server.test.d.ts +0 -1
  316. package/package/aws-client-agent-mcp/dist/mcp-server.test.js +0 -1
  317. package/package/aws-client-agent-mcp/dist/mcp-tools.d.ts +0 -1
  318. package/package/aws-client-agent-mcp/dist/mcp-tools.js +22 -562
  319. package/package/aws-client-agent-mcp/dist/mcp-tools.test.d.ts +0 -1
  320. package/package/aws-client-agent-mcp/dist/mcp-tools.test.js +0 -1
  321. package/package/aws-client-agent-mcp/dist/memory-store.d.ts +0 -1
  322. package/package/aws-client-agent-mcp/dist/memory-store.js +4 -826
  323. package/package/aws-client-agent-mcp/dist/memory-store.test.d.ts +0 -1
  324. package/package/aws-client-agent-mcp/dist/memory-store.test.js +0 -1
  325. package/package/aws-client-agent-mcp/dist/memory-tools.d.ts +0 -1
  326. package/package/aws-client-agent-mcp/dist/memory-tools.js +31 -294
  327. package/package/aws-client-agent-mcp/dist/message-buffer.d.ts +0 -1
  328. package/package/aws-client-agent-mcp/dist/message-buffer.js +2 -288
  329. package/package/aws-client-agent-mcp/dist/message-buffer.test.d.ts +0 -1
  330. package/package/aws-client-agent-mcp/dist/message-buffer.test.js +0 -1
  331. package/package/aws-client-agent-mcp/dist/messageContent.d.ts +0 -1
  332. package/package/aws-client-agent-mcp/dist/messageContent.js +2 -125
  333. package/package/aws-client-agent-mcp/dist/runtime-launch-binding.d.ts +0 -1
  334. package/package/aws-client-agent-mcp/dist/runtime-launch-binding.js +2 -103
  335. package/package/aws-client-agent-mcp/dist/runtime-launch-binding.test.d.ts +0 -1
  336. package/package/aws-client-agent-mcp/dist/runtime-launch-binding.test.js +0 -1
  337. package/package/aws-client-agent-mcp/dist/server-memory-store.d.ts +0 -1
  338. package/package/aws-client-agent-mcp/dist/server-memory-store.js +2 -96
  339. package/package/aws-client-agent-mcp/dist/status-reporter.d.ts +0 -1
  340. package/package/aws-client-agent-mcp/dist/status-reporter.js +3 -327
  341. package/package/aws-client-agent-mcp/dist/status-reporter.test.d.ts +0 -1
  342. package/package/aws-client-agent-mcp/dist/status-reporter.test.js +0 -1
  343. package/package/aws-client-agent-mcp/dist/types.d.ts +0 -1
  344. package/package/aws-client-agent-mcp/dist/types.js +2 -9
  345. package/package/aws-client-agent-mcp/dist/websocket-client.d.ts +0 -1
  346. package/package/aws-client-agent-mcp/dist/websocket-client.js +2 -677
  347. package/package/aws-client-agent-mcp/dist/websocket-client.test.d.ts +0 -1
  348. package/package/aws-client-agent-mcp/dist/websocket-client.test.js +0 -1
  349. package/package/aws-client-agent-mcp/package.json +1 -1
  350. package/package/cc-switch-sdk/dist/constants.d.ts +0 -1
  351. package/package/cc-switch-sdk/dist/constants.js +2 -63
  352. package/package/cc-switch-sdk/dist/errors.d.ts +0 -1
  353. package/package/cc-switch-sdk/dist/errors.js +2 -8
  354. package/package/cc-switch-sdk/dist/index.d.ts +0 -1
  355. package/package/cc-switch-sdk/dist/index.js +2 -9
  356. package/package/cc-switch-sdk/dist/schemas.d.ts +0 -1
  357. package/package/cc-switch-sdk/dist/schemas.js +2 -38
  358. package/package/cc-switch-sdk/dist/sdk.d.ts +0 -1
  359. package/package/cc-switch-sdk/dist/sdk.js +2 -470
  360. package/package/cc-switch-sdk/dist/services/ai-config-service.d.ts +0 -1
  361. package/package/cc-switch-sdk/dist/services/ai-config-service.js +4 -307
  362. package/package/cc-switch-sdk/dist/services/mcp-model.d.ts +0 -1
  363. package/package/cc-switch-sdk/dist/services/mcp-model.js +2 -37
  364. package/package/cc-switch-sdk/dist/services/mcp-service.d.ts +0 -1
  365. package/package/cc-switch-sdk/dist/services/mcp-service.js +2 -9
  366. package/package/cc-switch-sdk/dist/services/skill-model.d.ts +0 -1
  367. package/package/cc-switch-sdk/dist/services/skill-model.js +2 -41
  368. package/package/cc-switch-sdk/dist/services/skill-service.d.ts +0 -1
  369. package/package/cc-switch-sdk/dist/services/skill-service.js +2 -9
  370. package/package/cc-switch-sdk/dist/state.d.ts +0 -1
  371. package/package/cc-switch-sdk/dist/state.js +2 -19
  372. package/package/cc-switch-sdk/dist/types.d.ts +0 -1
  373. package/package/cc-switch-sdk/dist/types.js +1 -1
  374. package/package/cc-switch-sdk/dist/utils/fs.d.ts +0 -1
  375. package/package/cc-switch-sdk/dist/utils/fs.js +3 -114
  376. package/package/cc-switch-sdk/dist/utils/id.d.ts +0 -1
  377. package/package/cc-switch-sdk/dist/utils/id.js +2 -12
  378. package/package/cc-switch-sdk/package.json +1 -1
  379. package/package.json +4 -2
  380. package/dist/adapter/AcodeSdkAdapter.d.ts.map +0 -1
  381. package/dist/adapter/AdapterRegistry.d.ts.map +0 -1
  382. package/dist/adapter/ClaudeSdkAdapter.d.ts.map +0 -1
  383. package/dist/adapter/CodexSdkAdapter.d.ts.map +0 -1
  384. package/dist/adapter/OpencodeSdkAdapter.d.ts.map +0 -1
  385. package/dist/adapter/SdkProviderSpi.d.ts.map +0 -1
  386. package/dist/adapter/acode-tool-names.d.ts.map +0 -1
  387. package/dist/adapter/idle-poll-loop.d.ts.map +0 -1
  388. package/dist/adapter/index.d.ts.map +0 -1
  389. package/dist/adapter/instance-ai-config-resolver.d.ts.map +0 -1
  390. package/dist/adapter/types.d.ts.map +0 -1
  391. package/dist/adapters/cc-switch/common.d.ts.map +0 -1
  392. package/dist/adapters/cc-switch/index.d.ts.map +0 -1
  393. package/dist/adapters/cc-switch/mcp-acode.d.ts.map +0 -1
  394. package/dist/adapters/cc-switch/mcp-claude.d.ts.map +0 -1
  395. package/dist/adapters/cc-switch/mcp-claudecode.d.ts.map +0 -1
  396. package/dist/adapters/cc-switch/mcp-codex.d.ts.map +0 -1
  397. package/dist/adapters/cc-switch/mcp-opencode.d.ts.map +0 -1
  398. package/dist/adapters/cc-switch/mcp-placeholder.d.ts.map +0 -1
  399. package/dist/adapters/cc-switch/skill-acode.d.ts.map +0 -1
  400. package/dist/adapters/cc-switch/skill-claude.d.ts.map +0 -1
  401. package/dist/adapters/cc-switch/skill-claudecode.d.ts.map +0 -1
  402. package/dist/adapters/cc-switch/skill-codex.d.ts.map +0 -1
  403. package/dist/adapters/cc-switch/skill-directory.d.ts.map +0 -1
  404. package/dist/adapters/cc-switch/skill-opencode.d.ts.map +0 -1
  405. package/dist/adapters/cc-switch/skill-placeholder.d.ts.map +0 -1
  406. package/dist/config.d.ts.map +0 -1
  407. package/dist/index.d.ts.map +0 -1
  408. package/dist/middleware/auth.d.ts.map +0 -1
  409. package/dist/routes/acode-todos.d.ts.map +0 -1
  410. package/dist/routes/ai-sources.d.ts.map +0 -1
  411. package/dist/routes/dashboard.d.ts.map +0 -1
  412. package/dist/routes/events.d.ts.map +0 -1
  413. package/dist/routes/file-browser.d.ts.map +0 -1
  414. package/dist/routes/file-diff-rollback.d.ts.map +0 -1
  415. package/dist/routes/file-snapshot.d.ts.map +0 -1
  416. package/dist/routes/git.d.ts.map +0 -1
  417. package/dist/routes/instance.d.ts.map +0 -1
  418. package/dist/routes/mcp.d.ts.map +0 -1
  419. package/dist/routes/processes.d.ts.map +0 -1
  420. package/dist/routes/properties.d.ts.map +0 -1
  421. package/dist/routes/pty.d.ts.map +0 -1
  422. package/dist/routes/runtime-binding.d.ts.map +0 -1
  423. package/dist/routes/sessions.d.ts.map +0 -1
  424. package/dist/routes/skills.d.ts.map +0 -1
  425. package/dist/routes/sub-agents.d.ts.map +0 -1
  426. package/dist/routes/system-metrics.d.ts.map +0 -1
  427. package/dist/routes/task-context.d.ts.map +0 -1
  428. package/dist/routes/terminal.d.ts.map +0 -1
  429. package/dist/routes/yml.d.ts.map +0 -1
  430. package/dist/services/acode-package.d.ts.map +0 -1
  431. package/dist/services/agent-event-store.d.ts.map +0 -1
  432. package/dist/services/agent-process-manager.d.ts.map +0 -1
  433. package/dist/services/ai-source-apply.d.ts.map +0 -1
  434. package/dist/services/auto-register.d.ts.map +0 -1
  435. package/dist/services/aws-client-agent-mcp.d.ts.map +0 -1
  436. package/dist/services/bridge-package-publish-hooks.d.ts.map +0 -1
  437. package/dist/services/cc-switch-sdk.d.ts.map +0 -1
  438. package/dist/services/cli-commands.d.ts.map +0 -1
  439. package/dist/services/event-delta-pusher.d.ts.map +0 -1
  440. package/dist/services/fileSnapshotStore.d.ts.map +0 -1
  441. package/dist/services/instance-ai-config-service.d.ts.map +0 -1
  442. package/dist/services/instance-init-service.d.ts.map +0 -1
  443. package/dist/services/instance-service.d.ts.map +0 -1
  444. package/dist/services/instance-state.d.ts.map +0 -1
  445. package/dist/services/legacy-path-migration.d.ts.map +0 -1
  446. package/dist/services/lifecycle-state.d.ts.map +0 -1
  447. package/dist/services/mcp-launch-binding-queue.d.ts.map +0 -1
  448. package/dist/services/orphan-monitor.d.ts.map +0 -1
  449. package/dist/services/panel-auth.d.ts.map +0 -1
  450. package/dist/services/process-detector.d.ts.map +0 -1
  451. package/dist/services/process-registry.d.ts.map +0 -1
  452. package/dist/services/ring-buffer.d.ts.map +0 -1
  453. package/dist/services/runtime-binding.d.ts.map +0 -1
  454. package/dist/services/runtime-lifecycle-policy.d.ts.map +0 -1
  455. package/dist/services/session-output.d.ts.map +0 -1
  456. package/dist/services/startup-config-wizard.d.ts.map +0 -1
  457. package/dist/services/sub-agent-manager-instance.d.ts.map +0 -1
  458. package/dist/services/system-metrics.d.ts.map +0 -1
  459. package/dist/services/terminal-persistence.d.ts.map +0 -1
  460. package/dist/services/tool-installer.d.ts.map +0 -1
  461. package/dist/services/tunnel-client.d.ts.map +0 -1
  462. package/dist/services/windows-service-manager.d.ts.map +0 -1
  463. package/dist/services/workspace-files.d.ts.map +0 -1
  464. package/dist/services/workspace-watch.d.ts.map +0 -1
  465. package/dist/services/workspaceTaskChangeWatcher.d.ts.map +0 -1
  466. package/dist/types.d.ts.map +0 -1
  467. package/dist/utils/file-utils.d.ts.map +0 -1
  468. package/dist/utils/instance-ai-config-debug-logger.d.ts.map +0 -1
  469. package/dist/utils/logger.d.ts.map +0 -1
  470. package/dist/utils/mcp-utils.d.ts.map +0 -1
  471. package/dist/utils/path-utils.d.ts.map +0 -1
  472. package/dist/utils/sdk-package-loader.d.ts.map +0 -1
  473. package/dist/utils/validation.d.ts.map +0 -1
  474. package/dist/utils/yaml-utils.d.ts.map +0 -1
  475. package/node_modules/@cc-switch/sdk/dist/constants.d.ts.map +0 -1
  476. package/node_modules/@cc-switch/sdk/dist/errors.d.ts.map +0 -1
  477. package/node_modules/@cc-switch/sdk/dist/index.d.ts.map +0 -1
  478. package/node_modules/@cc-switch/sdk/dist/schemas.d.ts.map +0 -1
  479. package/node_modules/@cc-switch/sdk/dist/sdk.d.ts.map +0 -1
  480. package/node_modules/@cc-switch/sdk/dist/services/ai-config-service.d.ts.map +0 -1
  481. package/node_modules/@cc-switch/sdk/dist/services/mcp-model.d.ts.map +0 -1
  482. package/node_modules/@cc-switch/sdk/dist/services/mcp-service.d.ts.map +0 -1
  483. package/node_modules/@cc-switch/sdk/dist/services/skill-model.d.ts.map +0 -1
  484. package/node_modules/@cc-switch/sdk/dist/services/skill-service.d.ts.map +0 -1
  485. package/node_modules/@cc-switch/sdk/dist/state.d.ts.map +0 -1
  486. package/node_modules/@cc-switch/sdk/dist/types.d.ts.map +0 -1
  487. package/node_modules/@cc-switch/sdk/dist/utils/fs.d.ts.map +0 -1
  488. package/node_modules/@cc-switch/sdk/dist/utils/id.d.ts.map +0 -1
  489. package/package/acode/dist/ask-user-question-tool.d.ts.map +0 -1
  490. package/package/acode/dist/built-in-file-tools.d.ts.map +0 -1
  491. package/package/acode/dist/built-in-symbols-tool.d.ts.map +0 -1
  492. package/package/acode/dist/builtins/commandArtifacts.d.ts.map +0 -1
  493. package/package/acode/dist/builtins/commandOutputEvents.d.ts.map +0 -1
  494. package/package/acode/dist/builtins/shellCommandPreparation.d.ts.map +0 -1
  495. package/package/acode/dist/builtins/shellCommandResult.d.ts.map +0 -1
  496. package/package/acode/dist/builtins/shellCommandRunner.d.ts.map +0 -1
  497. package/package/acode/dist/builtins/shellOutputDecoder.d.ts.map +0 -1
  498. package/package/acode/dist/cli.d.ts.map +0 -1
  499. package/package/acode/dist/commands/commandLoader.d.ts.map +0 -1
  500. package/package/acode/dist/commands/commandParser.d.ts.map +0 -1
  501. package/package/acode/dist/commands/commandRegistry.d.ts.map +0 -1
  502. package/package/acode/dist/compaction.d.ts.map +0 -1
  503. package/package/acode/dist/config.d.ts.map +0 -1
  504. package/package/acode/dist/fs-utils.d.ts.map +0 -1
  505. package/package/acode/dist/hooks/hookBus.d.ts.map +0 -1
  506. package/package/acode/dist/index.d.ts.map +0 -1
  507. package/package/acode/dist/mcpToolNames.d.ts.map +0 -1
  508. package/package/acode/dist/parsers/ast-cache.d.ts.map +0 -1
  509. package/package/acode/dist/parsers/language-detector.d.ts.map +0 -1
  510. package/package/acode/dist/parsers/language-queries.d.ts.map +0 -1
  511. package/package/acode/dist/parsers/symbol-extractor.d.ts.map +0 -1
  512. package/package/acode/dist/parsers/tree-sitter-loader.d.ts.map +0 -1
  513. package/package/acode/dist/parsers/types.d.ts.map +0 -1
  514. package/package/acode/dist/runtime.d.ts.map +0 -1
  515. package/package/acode/dist/skill-tool.d.ts.map +0 -1
  516. package/package/acode/dist/sub-agent-manager.d.ts.map +0 -1
  517. package/package/acode/dist/sub-agent-store.d.ts.map +0 -1
  518. package/package/acode/dist/sub-agent-tools.d.ts.map +0 -1
  519. package/package/acode/dist/sub-agent-types.d.ts.map +0 -1
  520. package/package/acode/dist/todo-store.d.ts.map +0 -1
  521. package/package/acode/dist/todo-tool.d.ts.map +0 -1
  522. package/package/acode/dist/types.d.ts.map +0 -1
  523. package/package/aws-client-agent-mcp/dist/agent-client.d.ts.map +0 -1
  524. package/package/aws-client-agent-mcp/dist/agent-client.js.map +0 -1
  525. package/package/aws-client-agent-mcp/dist/agent-client.test.d.ts.map +0 -1
  526. package/package/aws-client-agent-mcp/dist/agent-client.test.js.map +0 -1
  527. package/package/aws-client-agent-mcp/dist/config.d.ts.map +0 -1
  528. package/package/aws-client-agent-mcp/dist/config.js.map +0 -1
  529. package/package/aws-client-agent-mcp/dist/config.test.d.ts.map +0 -1
  530. package/package/aws-client-agent-mcp/dist/config.test.js.map +0 -1
  531. package/package/aws-client-agent-mcp/dist/constants.d.ts.map +0 -1
  532. package/package/aws-client-agent-mcp/dist/constants.js.map +0 -1
  533. package/package/aws-client-agent-mcp/dist/context-manager.d.ts.map +0 -1
  534. package/package/aws-client-agent-mcp/dist/context-manager.js.map +0 -1
  535. package/package/aws-client-agent-mcp/dist/context-manager.test.d.ts.map +0 -1
  536. package/package/aws-client-agent-mcp/dist/context-manager.test.js.map +0 -1
  537. package/package/aws-client-agent-mcp/dist/http-client.d.ts.map +0 -1
  538. package/package/aws-client-agent-mcp/dist/http-client.js.map +0 -1
  539. package/package/aws-client-agent-mcp/dist/http-client.test.d.ts.map +0 -1
  540. package/package/aws-client-agent-mcp/dist/http-client.test.js.map +0 -1
  541. package/package/aws-client-agent-mcp/dist/index.d.ts.map +0 -1
  542. package/package/aws-client-agent-mcp/dist/index.js.map +0 -1
  543. package/package/aws-client-agent-mcp/dist/logger.d.ts.map +0 -1
  544. package/package/aws-client-agent-mcp/dist/logger.js.map +0 -1
  545. package/package/aws-client-agent-mcp/dist/logger.test.d.ts.map +0 -1
  546. package/package/aws-client-agent-mcp/dist/logger.test.js.map +0 -1
  547. package/package/aws-client-agent-mcp/dist/mcp-server.d.ts.map +0 -1
  548. package/package/aws-client-agent-mcp/dist/mcp-server.js.map +0 -1
  549. package/package/aws-client-agent-mcp/dist/mcp-server.test.d.ts.map +0 -1
  550. package/package/aws-client-agent-mcp/dist/mcp-server.test.js.map +0 -1
  551. package/package/aws-client-agent-mcp/dist/mcp-tools.d.ts.map +0 -1
  552. package/package/aws-client-agent-mcp/dist/mcp-tools.js.map +0 -1
  553. package/package/aws-client-agent-mcp/dist/mcp-tools.test.d.ts.map +0 -1
  554. package/package/aws-client-agent-mcp/dist/mcp-tools.test.js.map +0 -1
  555. package/package/aws-client-agent-mcp/dist/memory-store.d.ts.map +0 -1
  556. package/package/aws-client-agent-mcp/dist/memory-store.js.map +0 -1
  557. package/package/aws-client-agent-mcp/dist/memory-store.test.d.ts.map +0 -1
  558. package/package/aws-client-agent-mcp/dist/memory-store.test.js.map +0 -1
  559. package/package/aws-client-agent-mcp/dist/memory-tools.d.ts.map +0 -1
  560. package/package/aws-client-agent-mcp/dist/memory-tools.js.map +0 -1
  561. package/package/aws-client-agent-mcp/dist/message-buffer.d.ts.map +0 -1
  562. package/package/aws-client-agent-mcp/dist/message-buffer.js.map +0 -1
  563. package/package/aws-client-agent-mcp/dist/message-buffer.test.d.ts.map +0 -1
  564. package/package/aws-client-agent-mcp/dist/message-buffer.test.js.map +0 -1
  565. package/package/aws-client-agent-mcp/dist/messageContent.d.ts.map +0 -1
  566. package/package/aws-client-agent-mcp/dist/messageContent.js.map +0 -1
  567. package/package/aws-client-agent-mcp/dist/runtime-launch-binding.d.ts.map +0 -1
  568. package/package/aws-client-agent-mcp/dist/runtime-launch-binding.js.map +0 -1
  569. package/package/aws-client-agent-mcp/dist/runtime-launch-binding.test.d.ts.map +0 -1
  570. package/package/aws-client-agent-mcp/dist/runtime-launch-binding.test.js.map +0 -1
  571. package/package/aws-client-agent-mcp/dist/server-memory-store.d.ts.map +0 -1
  572. package/package/aws-client-agent-mcp/dist/server-memory-store.js.map +0 -1
  573. package/package/aws-client-agent-mcp/dist/status-reporter.d.ts.map +0 -1
  574. package/package/aws-client-agent-mcp/dist/status-reporter.js.map +0 -1
  575. package/package/aws-client-agent-mcp/dist/status-reporter.test.d.ts.map +0 -1
  576. package/package/aws-client-agent-mcp/dist/status-reporter.test.js.map +0 -1
  577. package/package/aws-client-agent-mcp/dist/types.d.ts.map +0 -1
  578. package/package/aws-client-agent-mcp/dist/types.js.map +0 -1
  579. package/package/aws-client-agent-mcp/dist/websocket-client.d.ts.map +0 -1
  580. package/package/aws-client-agent-mcp/dist/websocket-client.js.map +0 -1
  581. package/package/aws-client-agent-mcp/dist/websocket-client.test.d.ts.map +0 -1
  582. package/package/aws-client-agent-mcp/dist/websocket-client.test.js.map +0 -1
  583. package/package/cc-switch-sdk/dist/constants.d.ts.map +0 -1
  584. package/package/cc-switch-sdk/dist/errors.d.ts.map +0 -1
  585. package/package/cc-switch-sdk/dist/index.d.ts.map +0 -1
  586. package/package/cc-switch-sdk/dist/schemas.d.ts.map +0 -1
  587. package/package/cc-switch-sdk/dist/sdk.d.ts.map +0 -1
  588. package/package/cc-switch-sdk/dist/services/ai-config-service.d.ts.map +0 -1
  589. package/package/cc-switch-sdk/dist/services/mcp-model.d.ts.map +0 -1
  590. package/package/cc-switch-sdk/dist/services/mcp-service.d.ts.map +0 -1
  591. package/package/cc-switch-sdk/dist/services/skill-model.d.ts.map +0 -1
  592. package/package/cc-switch-sdk/dist/services/skill-service.d.ts.map +0 -1
  593. package/package/cc-switch-sdk/dist/state.d.ts.map +0 -1
  594. package/package/cc-switch-sdk/dist/types.d.ts.map +0 -1
  595. package/package/cc-switch-sdk/dist/utils/fs.d.ts.map +0 -1
  596. package/package/cc-switch-sdk/dist/utils/id.d.ts.map +0 -1
@@ -1,1986 +1,11 @@
1
- /**
2
- * ACode SDK Adapter
3
- *
4
- * 主流程:直接加载随 aws-runtime-bridge 内置发布的 ACode headless runtime,
5
- * 不依赖外部 CLI 安装;将 ACode 的结构化事件映射为 runtime-bridge 统一事件流。
6
- *
7
- * 模型/baseURL/apiKey 解析(核心):
8
- * - 调用 instance-ai-config-resolver 按 "instance > global > default" 三层仲裁
9
- * - "instance" 包含 per-session config.model 与 per-instance config.instanceAiConfig
10
- * - "global" 来自 configPath 指向的 acode.json(per-instance 文件已 deep-merge 全局)
11
- * - 解析结果会写入 d:/Instance-ai-configuration-debug.log,bridge + 后端共享同一份
12
- */
13
- import { EventEmitter } from "node:events";
14
- import path from "node:path";
15
- import fs from "node:fs/promises";
16
- import { pathToFileURL } from "node:url";
17
- import { v4 as uuidv4 } from "uuid";
18
- import { getAcodeConfigFile, getAcodeSkillsDir, getRuntimeHomeDir, } from "../config.js";
19
- import { getReleasedAcodeEntryPath } from "../services/acode-package.js";
20
- import { getInstanceAiConfigDebugLogger } from "../utils/instance-ai-config-debug-logger.js";
21
- import { redactForLog } from "../services/instance-ai-config-service.js";
22
- import { subAgentManager } from "../services/sub-agent-manager-instance.js";
23
- import { resolveAcodeProviderConfig, } from "./instance-ai-config-resolver.js";
24
- import { recordAgentFileChange } from "../services/workspaceTaskChangeWatcher.js";
25
- import { isMessagePollingIdleCommand, isPollMessageToolDescriptor, } from "./acode-tool-names.js";
26
- import { IdlePollLoop } from "./idle-poll-loop.js";
27
- import { getCanonicalMcpDetail, getToolActionInfo } from "./types.js";
28
- /**
29
- * Adapter 调试日志:受 ACODE_COMPACTION_DEBUG=1 开关控制(与 runtime.ts 共用同一开关)。
30
- * 默认关闭以避免生产环境噪声与潜在的 sessionId/agentId/event 信息泄漏。
31
- * 文件中所有原 `console.log` 调用均改为走此 helper。
32
- */
33
- const ACODE_DEBUG_ENABLED = process.env.ACODE_COMPACTION_DEBUG === "1";
34
- function adapterDebugLog(...args) {
35
- if (!ACODE_DEBUG_ENABLED)
36
- return;
37
- console.log(...args);
38
- }
39
- const ACODE_AUTO_POLL_PROMPT = [
40
- "Continue the AgentsWork message loop now.",
41
- "You MUST call the exact MCP tool named aws-mcp__poll_message as your next action and wait for the next assigned message or task.",
42
- "Do not answer in plain text before calling aws-mcp__poll_message.",
43
- "After handling any received work, reply through the appropriate aws-mcp messaging tool, then continue listening again.",
44
- ].join(" ");
45
- // 异步看门狗轮询间隔:每 5s 检查一次运行时状态
46
- const ACODE_WATCHDOG_POLL_INTERVAL_MS = 5_000;
47
- // 数据流看门狗超时:ai_request 发出后,若 40s 内没有收到任何流式数据(assistant_delta/thinking_delta),
48
- // 触发 recoverTurnFailure 强制恢复。覆盖 provider requestTimeoutMs 配置不当或流挂死等场景。
49
- // 仅在 thinking/tool_using 状态下累计计时,waiting_input 状态下不计时(agent 正在有意等待)。
50
- const ACODE_DATA_FLOW_STREAM_WATCHDOG_MS = 40_000;
51
- /**
52
- * tool_using 慢超时兜底(毫秒)。
53
- * ★ 修复 BUG:原实现 tool_using 状态完全无超时保护——
54
- * - ask_user_question 依赖 question_request 事件取消看门狗,但事件丢失时永久卡死
55
- * - await_sub_agents 依赖子 agent 自己的 runtime 看门狗,子 agent 崩溃不通知时永久卡死
56
- * - 工具内部 hang(如 bash 等待 stdin、MCP 工具死锁)无兜底
57
- * 设 5min 兜底:远大于正常工具耗时(MCP 60s / Bash 默认 30s / 文件操作通常 < 5s),
58
- * 仅在工具确实彻底 hang 时触发恢复。每次 tool_use_start 会重置 watchdogLastDataFlowTime,
59
- * 连续多个工具串接不会累积超时。
60
- */
61
- const ACODE_TOOL_USING_SLOW_TIMEOUT_MS = 5 * 60_000;
62
- const ACODE_AUTO_POLL_DELAY_MS = 250;
63
- // Backoff used when a polling-driven turn fails (e.g., 60s MCP tool timeout,
64
- // model 5xx, provider unreachable). Larger than ACODE_AUTO_POLL_DELAY_MS to
65
- // avoid busy-looping when the same failure recurs on every retry.
66
- const ACODE_AUTO_POLL_FAILURE_DELAY_MS = 2000;
67
- // 新增:submit 超时检测配置(治本修复:防止状态永久卡住)
68
- // 当 submit 超过此时间未返回,强制触发恢复机制
69
- const ACODE_SUBMIT_TIMEOUT_MS = 120000; // 120秒(2分钟)
70
- // 兜底超时:mcp_status 缓存能力后,等待 skill_status 的最长时间。
71
- // 超时后单独发出 MCP 能力清单,避免 skill_status 事件缺失导致"暂无能力清单"。
72
- const CAPABILITIES_FALLBACK_MS = 3000;
73
- const ACODE_MESSAGE_POLLING_IDLE_PROMPT = "系统提醒:当前工作已完成或运行时已进入空闲状态。请立即调用 poll_message 工具阻塞等待新消息和流程线任务提醒;收到流程任务提醒后调用 my_task 查看已由后台静默抢占的任务详情并直接处理;完成任务后调用 submit_task_result 提交结果,然后继续 poll_message。 调用 submit_task_result 时请一并传入 changeLog(本次处理的文件/内容变更日志,包含 operation 与 path),用于 AI 回退。";
74
- function getAcodePackageEntryPath() {
75
- return path.resolve(getReleasedAcodeEntryPath());
76
- }
77
- export function toAcodeEsmImportSpecifier(modulePath) {
78
- return pathToFileURL(path.resolve(modulePath)).href;
79
- }
80
- /**
81
- * Known model context window limits lookup.
82
- * Maps model name prefixes to { contextLimit, maxOutput }.
83
- * Falls back to DEFAULT_CONTEXT_LIMIT (128K) when no match is found.
84
- * This is aligned with aws-client-agent-mcp's context-manager.ts MODEL_CONTEXT_LIMITS.
85
- */
86
- const DEFAULT_CONTEXT_LIMIT = 128_000;
87
- const MODEL_CONTEXT_LIMITS = {
88
- "claude-4": { contextLimit: 200_000, maxOutput: 8_192 },
89
- "claude-sonnet-4": { contextLimit: 200_000, maxOutput: 8_192 },
90
- "claude-3-5-sonnet": { contextLimit: 200_000, maxOutput: 8_192 },
91
- "claude-3-opus": { contextLimit: 200_000, maxOutput: 8_192 },
92
- "claude-3-haiku": { contextLimit: 200_000, maxOutput: 8_192 },
93
- "claude-2": { contextLimit: 100_000, maxOutput: 4_096 },
94
- "gpt-4o": { contextLimit: 128_000, maxOutput: 16_384 },
95
- "gpt-4-turbo": { contextLimit: 128_000, maxOutput: 4_096 },
96
- "gpt-4": { contextLimit: 8_192, maxOutput: 4_096 },
97
- "gpt-3.5-turbo": { contextLimit: 16_384, maxOutput: 4_096 },
98
- "o1": { contextLimit: 200_000, maxOutput: 100_000 },
99
- "o3-mini": { contextLimit: 200_000, maxOutput: 100_000 },
100
- "gemini-2.0-flash": { contextLimit: 1_048_576, maxOutput: 8_192 },
101
- "gemini-2.5-pro": { contextLimit: 1_048_576, maxOutput: 8_192 },
102
- "gemini-1.5-pro": { contextLimit: 2_097_152, maxOutput: 8_192 },
103
- "gemini-1.5-flash": { contextLimit: 1_048_576, maxOutput: 8_192 },
104
- "deepseek": { contextLimit: 64_000, maxOutput: 8_192 },
105
- "deepseek-chat": { contextLimit: 64_000, maxOutput: 8_192 },
106
- "deepseek-reasoner": { contextLimit: 64_000, maxOutput: 8_192 },
107
- "qwen-max": { contextLimit: 32_000, maxOutput: 8_192 },
108
- "qwen-plus": { contextLimit: 128_000, maxOutput: 8_192 },
109
- "qwen-turbo": { contextLimit: 1_000_000, maxOutput: 8_192 },
110
- "glm-4": { contextLimit: 128_000, maxOutput: 4_096 },
111
- "yi-": { contextLimit: 200_000, maxOutput: 4_096 },
112
- "llama-3": { contextLimit: 8_192, maxOutput: 4_096 },
113
- "llama-2": { contextLimit: 4_096, maxOutput: 4_096 },
114
- "mistral": { contextLimit: 32_000, maxOutput: 4_096 },
115
- "mixtral": { contextLimit: 32_000, maxOutput: 4_096 },
116
- "codestral": { contextLimit: 256_000, maxOutput: 8_192 },
117
- "grok": { contextLimit: 131_072, maxOutput: 4_096 },
118
- "nova-": { contextLimit: 200_000, maxOutput: 8_192 },
119
- };
120
- /**
121
- * Look up model context window limit by model name.
122
- * Exact match first, then prefix match (longest prefix wins).
123
- */
124
- function lookupModelContextLimit(modelName) {
125
- if (!modelName)
126
- return DEFAULT_CONTEXT_LIMIT;
127
- const key = modelName.toLowerCase().trim();
128
- if (MODEL_CONTEXT_LIMITS[key])
129
- return MODEL_CONTEXT_LIMITS[key].contextLimit;
130
- const matches = Object.keys(MODEL_CONTEXT_LIMITS)
131
- .filter((prefix) => key.startsWith(prefix))
132
- .sort((a, b) => b.length - a.length);
133
- if (matches.length > 0)
134
- return MODEL_CONTEXT_LIMITS[matches[0]].contextLimit;
135
- return DEFAULT_CONTEXT_LIMIT;
136
- }
137
- const DEFAULT_MAX_OUTPUT = 4_096;
138
- /**
139
- * Look up model max output token limit by model name.
140
- * Mirrors lookupModelContextLimit: exact match first, then longest-prefix match.
141
- * Used to compute the compaction point (contextLimit - maxOutput) on the frontend.
142
- */
143
- function lookupModelMaxOutput(modelName) {
144
- if (!modelName)
145
- return DEFAULT_MAX_OUTPUT;
146
- const key = modelName.toLowerCase().trim();
147
- if (MODEL_CONTEXT_LIMITS[key])
148
- return MODEL_CONTEXT_LIMITS[key].maxOutput;
149
- const matches = Object.keys(MODEL_CONTEXT_LIMITS)
150
- .filter((prefix) => key.startsWith(prefix))
151
- .sort((a, b) => b.length - a.length);
152
- if (matches.length > 0)
153
- return MODEL_CONTEXT_LIMITS[matches[0]].maxOutput;
154
- return DEFAULT_MAX_OUTPUT;
155
- }
156
- function toUsage(value) {
157
- if (!value || typeof value !== "object") {
158
- return undefined;
159
- }
160
- const usage = value;
161
- const inputTokens = toFiniteTokenCount(usage.inputTokens, usage.input_tokens, usage.promptTokens, usage.prompt_tokens);
162
- const outputTokens = toFiniteTokenCount(usage.outputTokens, usage.output_tokens, usage.completionTokens, usage.completion_tokens);
163
- if (inputTokens === 0 && outputTokens === 0) {
164
- return undefined;
165
- }
166
- return {
167
- inputTokens,
168
- outputTokens,
169
- };
170
- }
171
- function toFiniteTokenCount(...values) {
172
- for (const value of values) {
173
- const count = Number(value);
174
- if (Number.isFinite(count) && count > 0) {
175
- return count;
176
- }
177
- }
178
- return 0;
179
- }
180
- function toEstimatedContextTokens(data) {
181
- return toFiniteTokenCount(data.estimatedContextTokens, data.contextTokens, data.context_tokens, data.contextUsedTokens, data.context_used_tokens);
182
- }
183
- function toCommandOutputStream(value) {
184
- if (value === "stderr" || value === "mixed") {
185
- return value;
186
- }
187
- return "stdout";
188
- }
189
- function toStringRecord(value) {
190
- if (!value || typeof value !== "object" || Array.isArray(value)) {
191
- return undefined;
192
- }
193
- const headers = {};
194
- for (const [key, headerValue] of Object.entries(value)) {
195
- if (typeof headerValue === "string") {
196
- headers[key] = headerValue;
197
- }
198
- }
199
- return Object.keys(headers).length > 0 ? headers : undefined;
200
- }
201
- /**
202
- * Check whether an ACode turn_complete event represents a failed turn.
203
- * Main flow: only explicit failed=true prevents error status from being reset to waiting_input.
204
- */
205
- export function isFailedAcodeTurnComplete(data) {
206
- return data.failed === true;
207
- }
208
- function toAcodeMcpActionDetail(actionDetail, server, tool) {
209
- if (!actionDetail) {
210
- return tool ? `${server}/${tool}` : server;
211
- }
212
- const prefix = `${server}__`;
213
- if (actionDetail.startsWith(prefix)) {
214
- return getCanonicalMcpDetail(`${server}__${actionDetail.slice(prefix.length)}`);
215
- }
216
- return getCanonicalMcpDetail(actionDetail.replace("/", "__"));
217
- }
218
- function isAcodeSemanticToolAction(actionType) {
219
- return (actionType === "read_file" ||
220
- actionType === "edit_file" ||
221
- actionType === "write_file" ||
222
- actionType === "bash");
223
- }
224
- function hasPollingMessages(result) {
225
- const parsed = parseToolResultObject(result);
226
- if (!parsed) {
227
- return typeof result === "string" && result.trim().length > 0;
228
- }
229
- return [parsed.directMessages, parsed.groupMessages, parsed.messages].some((value) => Array.isArray(value) && value.length > 0);
230
- }
231
- function parseToolResultObject(result) {
232
- if (result && typeof result === "object" && !Array.isArray(result)) {
233
- return result;
234
- }
235
- if (typeof result !== "string" || !result.trim()) {
236
- return undefined;
237
- }
238
- try {
239
- const parsed = JSON.parse(result);
240
- return parsed && typeof parsed === "object" && !Array.isArray(parsed)
241
- ? parsed
242
- : undefined;
243
- }
244
- catch {
245
- return undefined;
246
- }
247
- }
248
- export class AcodeSdkAdapter extends EventEmitter {
249
- constructor() {
250
- super(...arguments);
251
- this.providerId = "acode";
252
- this.displayName = "ACode";
253
- this.sessions = new Map();
254
- }
255
- async startSession(sessionId, config) {
256
- const runtimeHome = getRuntimeHomeDir();
257
- // Resolve provider config first to get the resolved model and context limit,
258
- // so totalUsage can carry contextLimit from the start.
259
- const configPath = config.mcpConfigPath || getAcodeConfigFile(runtimeHome);
260
- const { resolved, configContextLimit } = await this.resolveAcodeProvider(config, configPath);
261
- const resolvedModel = resolved.model;
262
- // priority: acode.json top-level contextLimit > model lookup table > 128K default
263
- const contextLimit = configContextLimit ?? lookupModelContextLimit(resolvedModel);
264
- const maxOutput = lookupModelMaxOutput(resolvedModel);
265
- const session = {
266
- sessionId,
267
- status: "starting",
268
- messages: [],
269
- createdAt: new Date().toISOString(),
270
- totalUsage: { inputTokens: 0, outputTokens: 0, contextLimit, maxOutput },
271
- };
272
- this.sessions.set(sessionId, {
273
- adapterSession: session,
274
- config,
275
- runtime: await this.createRuntime(sessionId, resolvedModel, config, runtimeHome),
276
- autoPollActive: false,
277
- });
278
- this.emit("status-change", sessionId, "starting");
279
- const entry = this.sessions.get(sessionId);
280
- if (!entry) {
281
- throw new Error("ACode session was not initialized");
282
- }
283
- try {
284
- await entry.runtime.start();
285
- session.status = "waiting_input";
286
- this.emit("status-change", sessionId, "waiting_input", {
287
- usage: entry.adapterSession.totalUsage,
288
- });
289
- if (config.initialPrompt?.trim()) {
290
- this.startInitialPromptTurn(sessionId, config.initialPrompt);
291
- }
292
- else {
293
- // R1 fix: when no initial prompt is provided, explicitly arm the
294
- // auto-poll loop. Without this, the session sits at 'waiting_input'
295
- // forever if setIdleCommands hasn't been called yet (or is configured
296
- // with a non-polling command). setIdleCommands will cancel this timer
297
- // and replace it with the configured idle loop when invoked.
298
- this.armIdleLoopOrAutoPoll(sessionId);
299
- }
300
- }
301
- catch (error) {
302
- session.status = "error";
303
- this.emitRuntimeErrorEvent(sessionId, error);
304
- this.emit("status-change", sessionId, "error");
305
- const failedEntry = this.sessions.get(sessionId);
306
- if (failedEntry?.capabilitiesFallbackTimer) {
307
- clearTimeout(failedEntry.capabilitiesFallbackTimer);
308
- }
309
- this.sessions.delete(sessionId);
310
- throw error;
311
- }
312
- }
313
- async sendMessage(sessionId, message) {
314
- const sendStartTime = Date.now();
315
- adapterDebugLog(`[ACode] ═══════════════════════════════════════════════════`);
316
- adapterDebugLog(`[ACode] ▶️ sendMessage STARTED`);
317
- adapterDebugLog(`[ACode] Session: ${sessionId}`);
318
- adapterDebugLog(`[ACode] Message: ${message.slice(0, 100)}${message.length > 100 ? '...' : ''}`);
319
- adapterDebugLog(`[ACode] ════════════════════════════════════════════════════`);
320
- const entry = this.requireSession(sessionId);
321
- const previousStatus = entry.adapterSession.status;
322
- adapterDebugLog(`[ACode] Previous status: ${previousStatus}`);
323
- this.cancelScheduledAutoPoll(entry);
324
- entry.failedTurnRecoveryScheduled = false;
325
- entry.idlePollLoop?.stop();
326
- const userMessage = {
327
- id: uuidv4(),
328
- sessionId,
329
- role: "user",
330
- content: message,
331
- timestamp: new Date().toISOString(),
332
- };
333
- entry.adapterSession.messages.push(userMessage);
334
- this.emit("conversation-message", sessionId, userMessage);
335
- adapterDebugLog(`[ACode] Changing status: ${previousStatus} → thinking`);
336
- entry.adapterSession.status = "thinking";
337
- this.emit("status-change", sessionId, "thinking");
338
- // 启动超时检测
339
- this.startSubmitTimeoutDetection(sessionId, entry, "sendMessage");
340
- try {
341
- adapterDebugLog(`[ACode] ▶️ Calling runtime.submit()...`);
342
- const submitStartTime = Date.now();
343
- await entry.runtime.submit(message);
344
- const submitDuration = Date.now() - submitStartTime;
345
- // submit 成功返回,取消超时检测
346
- this.cancelSubmitTimeoutDetection(entry);
347
- adapterDebugLog(`[ACode] ✅ runtime.submit() completed in ${submitDuration}ms`);
348
- adapterDebugLog(`[ACode] Current status after submit: ${entry.adapterSession.status}`);
349
- adapterDebugLog(`[ACode] ▶️ Calling recoverResolvedTurnWithoutTerminalEvent()...`);
350
- this.recoverResolvedTurnWithoutTerminalEvent(sessionId);
351
- adapterDebugLog(`[ACode] Status after recovery: ${entry.adapterSession.status}`);
352
- const totalDuration = Date.now() - sendStartTime;
353
- adapterDebugLog(`[ACode] ════════════════════════════════════════════════════`);
354
- adapterDebugLog(`[ACode] ✅ sendMessage COMPLETED in ${totalDuration}ms`);
355
- adapterDebugLog(`[ACode] ═══════════════════════════════════════════════════`);
356
- }
357
- catch (error) {
358
- const errorDuration = Date.now() - sendStartTime;
359
- console.error(`[ACode] ❌ sendMessage FAILED after ${errorDuration}ms:`, error instanceof Error ? error.message : String(error));
360
- // submit 抛出异常,取消超时检测
361
- this.cancelSubmitTimeoutDetection(entry);
362
- adapterDebugLog(`[ACode] ▶️ Calling emitRuntimeError with recover=true...`);
363
- this.emitRuntimeError(sessionId, error, { recover: true });
364
- adapterDebugLog(`[ACode] Status after error recovery: ${entry.adapterSession.status}`);
365
- }
366
- }
367
- async sendConfirmation(_sessionId, _accept) { }
368
- /**
369
- * 接收前端对 ask_user_question 的回答(来自 terminal.ts /sdk/answer 路由)。
370
- * 通过 pendingQuestionToolCallId 找到 runtime.submitQuestionAnswer 的入参 toolCallId,
371
- * resolve 阻塞的工具 execute(),让 ACode 继续下一轮 LLM 请求。
372
- * 重新启动 submit 超时检测(继续计费后续工具执行时间)。
373
- */
374
- async sendQuestionAnswer(sessionId, answers) {
375
- const entry = this.sessions.get(sessionId);
376
- if (!entry) {
377
- return;
378
- }
379
- const toolCallId = entry.pendingQuestionToolCallId;
380
- if (!toolCallId) {
381
- adapterDebugLog(`[AcodeSdkAdapter] sendQuestionAnswer: no pending question for session ${sessionId}`);
382
- return;
383
- }
384
- entry.pendingQuestionToolCallId = undefined;
385
- entry.pendingQuestions = undefined;
386
- if (entry.runtime.submitQuestionAnswer) {
387
- entry.runtime.submitQuestionAnswer(toolCallId, answers);
388
- }
389
- // 用户回答后,重新启动 submit 超时检测(剩余工具执行时间计入)
390
- this.startSubmitTimeoutDetection(sessionId, entry, "sendQuestionAnswer");
391
- }
392
- async abortCurrentTurn(sessionId) {
393
- await this.requireSession(sessionId).runtime.abort();
394
- }
395
- async resetContext(sessionId) {
396
- const entry = this.requireSession(sessionId);
397
- this.cancelScheduledAutoPoll(entry);
398
- entry.failedTurnRecoveryScheduled = false;
399
- entry.autoPollActive = false;
400
- entry.autoPollObservedPollingTool = false;
401
- entry.autoPollObservedAssistantText = false;
402
- entry.autoPollObservedAnyTool = false;
403
- entry.stopAutoPollAfterCurrentTurn = false;
404
- entry.idlePollLoop?.stop();
405
- if (!entry.runtime.resetContext) {
406
- throw new Error("ACode runtime does not support context reset");
407
- }
408
- await entry.runtime.resetContext();
409
- entry.adapterSession.messages = [];
410
- // ★ 重置 token 计数但保留压缩配置(contextLimit/maxOutput/inputLimit/compactionThreshold),
411
- // 这些值在 session 期间不变,避免重置后前端压缩点竖线消失。
412
- const preservedUsage = entry.adapterSession.totalUsage;
413
- entry.adapterSession.totalUsage = {
414
- inputTokens: 0,
415
- outputTokens: 0,
416
- contextLimit: preservedUsage.contextLimit,
417
- maxOutput: preservedUsage.maxOutput,
418
- inputLimit: preservedUsage.inputLimit,
419
- compactionThreshold: preservedUsage.compactionThreshold,
420
- };
421
- entry.adapterSession.status = "waiting_input";
422
- this.emit("status-change", sessionId, "waiting_input", {
423
- usage: entry.adapterSession.totalUsage,
424
- });
425
- if (entry.config.initialPrompt?.trim()) {
426
- this.startInitialPromptTurn(sessionId, entry.config.initialPrompt);
427
- return;
428
- }
429
- this.armIdleLoopOrAutoPoll(sessionId);
430
- }
431
- async terminateSession(sessionId) {
432
- const entry = this.sessions.get(sessionId);
433
- if (!entry) {
434
- return;
435
- }
436
- this.cancelScheduledAutoPoll(entry);
437
- entry.failedTurnRecoveryScheduled = false;
438
- entry.idlePollLoop?.stop();
439
- if (entry.capabilitiesFallbackTimer) {
440
- clearTimeout(entry.capabilitiesFallbackTimer);
441
- entry.capabilitiesFallbackTimer = undefined;
442
- }
443
- await entry.runtime.stop();
444
- entry.adapterSession.status = "terminated";
445
- this.sessions.delete(sessionId);
446
- this.emit("status-change", sessionId, "terminated");
447
- }
448
- getConversation(sessionId) {
449
- return this.sessions.get(sessionId)?.adapterSession.messages ?? [];
450
- }
451
- hasSession(sessionId) {
452
- return this.sessions.has(sessionId);
453
- }
454
- getProviderSessionId(sessionId) {
455
- return this.sessions.has(sessionId) ? sessionId : undefined;
456
- }
457
- getSessionStatus(sessionId) {
458
- return this.sessions.get(sessionId)?.adapterSession.status;
459
- }
460
- /**
461
- * 主动查询会话的运行时能力清单(MCP 工具 + skills)。
462
- * 只读 adapter 内存缓存(lastCombinedCapabilities),不触发 runtime 重新探测。
463
- * 由 mcp_status / skill_status 事件维护;若尚未收到任何 runtime_capabilities 事件则返回 undefined。
464
- */
465
- getCapabilities(sessionId) {
466
- return this.sessions.get(sessionId)?.lastCombinedCapabilities;
467
- }
468
- /**
469
- * 主动查询会话当前挂起的 ask_user_question。
470
- * 只读 adapter 内存缓存(pendingQuestionToolCallId + pendingQuestions)。
471
- * 由 bridge GET /runtime/sdk/pending-questions 端点调用,用于前端刷新后重建提问 UI。
472
- * 返回 undefined 表示当前无挂起提问(会话不存在或 agent 未提问)。
473
- */
474
- getPendingQuestion(sessionId) {
475
- const entry = this.sessions.get(sessionId);
476
- if (!entry?.pendingQuestionToolCallId)
477
- return undefined;
478
- return {
479
- toolCallId: entry.pendingQuestionToolCallId,
480
- questions: entry.pendingQuestions ?? [],
481
- };
482
- }
483
- cleanup() {
484
- for (const sessionId of this.sessions.keys()) {
485
- void this.terminateSession(sessionId);
486
- }
487
- }
488
- setIdleCommands(sessionId, commands) {
489
- const entry = this.sessions.get(sessionId);
490
- if (!entry)
491
- return;
492
- entry.idleCommands = commands;
493
- this.cancelScheduledAutoPoll(entry);
494
- if (commands.idleInputCommand &&
495
- isMessagePollingIdleCommand(commands.idleInputCommand)) {
496
- const loop = this.getIdlePollLoop(entry);
497
- loop.start();
498
- return;
499
- }
500
- if (entry.adapterSession.status === "waiting_input" &&
501
- (commands.idleInputCommand || commands.nonInputCommand)) {
502
- this.getIdlePollLoop(entry).start();
503
- }
504
- }
505
- /** Injects a runtime constructor for adapter tests without loading the packaged ACode module. */
506
- setRuntimeConstructorForTest(runtimeConstructor) {
507
- this.runtimeConstructor = runtimeConstructor;
508
- }
509
- /**
510
- * 加载 ACode acode.json 顶层 provider 字段,提取 model/baseURL/apiKey,
511
- * 以及顶层 contextLimit(用户自定义上下文窗口大小)。
512
- * 主流程:先尝试 per-instance 文件(由 instance-ai-config-service 写入,含 deep-merge 结果),
513
- * 失败回退到 global acode.json;解析失败返回空对象。
514
- */
515
- /**
516
- * 从 acode.json 中读取 provider.default 信息及模型配置字典。
517
- * 返回包含 model/baseURL/apiKey 的仲裁配置,以及:
518
- * - contextLimit: 顶层 contextLimit(优先)
519
- * - modelContextLimits: 各模型的 contextLimit 字典,供 resolveAcodeProvider 回退查找
520
- */
521
- async loadAcodeProviderSection(configPath) {
522
- try {
523
- const raw = await fs.readFile(configPath, "utf-8");
524
- const parsed = JSON.parse(raw);
525
- const provider = parsed.provider;
526
- const defaultProvider = provider?.default;
527
- const options = defaultProvider?.options;
528
- // 读取顶层 contextLimit(用户可在 acode.json 中配置自定义上下文窗口)
529
- const contextLimit = toFiniteTokenCount(parsed.contextLimit);
530
- // 从 provider.default.models 中提取各模型的 contextLimit 字典
531
- const modelContextLimits = {};
532
- const models = defaultProvider?.models;
533
- if (models && typeof models === "object") {
534
- for (const [modelName, modelConfig] of Object.entries(models)) {
535
- if (modelConfig && typeof modelConfig === "object") {
536
- const limit = toFiniteTokenCount(modelConfig.contextLimit);
537
- if (limit > 0) {
538
- modelContextLimits[modelName.trim()] = limit;
539
- }
540
- }
541
- }
542
- }
543
- return {
544
- ...(defaultProvider?.model ? { model: defaultProvider.model } : {}),
545
- ...(options?.baseURL || options?.baseUrl
546
- ? { baseURL: options?.baseURL || options?.baseUrl }
547
- : {}),
548
- ...(options?.apiKey ? { apiKey: options.apiKey } : {}),
549
- ...(contextLimit > 0 ? { contextLimit } : {}),
550
- ...(Object.keys(modelContextLimits).length > 0 ? { modelContextLimits } : {}),
551
- };
552
- }
553
- catch (error) {
554
- const message = error instanceof Error ? error.message : String(error);
555
- const dbg = getInstanceAiConfigDebugLogger();
556
- void dbg.log({
557
- level: "warn",
558
- category: "load",
559
- message: `loadAcodeProviderSection failed: ${configPath}: ${message}`,
560
- source: "bridge",
561
- });
562
- return {};
563
- }
564
- }
565
- /**
566
- * 解析 ACode provider 配置:按 "instance > global > default" 优先级逐字段仲裁。
567
- * 入口在 createRuntime;该方法独立出来便于测试。
568
- *
569
- * @param config AdapterSessionConfig(per-session model 与 per-instance instanceAiConfig)
570
- * @param configPath 实际作为 ACodeRuntime.configPath 的 acode.json 路径
571
- * (可能为 per-instance 文件或全局文件)
572
- */
573
- async resolveAcodeProvider(config, configPath) {
574
- // 实例层 = per-session model (config.model) + per-instance instanceAiConfig
575
- // 注意:priority 上 per-session model 高于 per-instance instanceAiConfig.model
576
- // per-session model 是请求级最具体的覆盖,应拥有最高优先级
577
- const perSessionModel = typeof config.model === "string" && config.model.trim()
578
- ? config.model.trim()
579
- : undefined;
580
- const _dbg = getInstanceAiConfigDebugLogger();
581
- void _dbg.log({
582
- level: "info",
583
- category: "resolve",
584
- message: "resolveAcodeProvider 输入",
585
- source: "bridge",
586
- details: {
587
- configModel: config.model ?? null,
588
- configInstanceAiConfig: config.instanceAiConfig ? redactForLog(config.instanceAiConfig) : null,
589
- configPath,
590
- },
591
- });
592
- // 优先级仲裁:perSessionModel(request) > instanceAiConfig(instance) > global > default
593
- // perSessionModel 是请求级最具体的覆盖,拥有最高优先级
594
- const instanceConfig = {
595
- ...(config.instanceAiConfig?.model
596
- ? { model: config.instanceAiConfig.model }
597
- : {}),
598
- ...(config.instanceAiConfig?.baseURL
599
- ? { baseURL: config.instanceAiConfig.baseURL }
600
- : {}),
601
- ...(config.instanceAiConfig?.apiKey
602
- ? { apiKey: config.instanceAiConfig.apiKey }
603
- : {}),
604
- ...(config.instanceAiConfig?.effort
605
- ? { effort: config.instanceAiConfig.effort }
606
- : {}),
607
- // perSessionModel 最后设置,确保优先级最高
608
- ...(perSessionModel ? { model: perSessionModel } : {}),
609
- };
610
- // 全局层 = acode.json 顶层 provider.default(含 deep-merge 后的 per-instance 文件)
611
- const globalConfig = await this.loadAcodeProviderSection(configPath);
612
- // 顶层 contextLimit(用户自定义上下文窗口大小)
613
- // 回退:从 provider.default.models[resolvedModel].contextLimit 读取
614
- let configContextLimit = globalConfig.contextLimit;
615
- if (!configContextLimit && globalConfig.modelContextLimits) {
616
- // 优先使用 perSessionModel,其次 instanceAiConfig 中的 model
617
- const fallbackModel = perSessionModel ||
618
- config.instanceAiConfig?.model ||
619
- globalConfig.model;
620
- if (fallbackModel && globalConfig.modelContextLimits[fallbackModel]) {
621
- configContextLimit = globalConfig.modelContextLimits[fallbackModel];
622
- }
623
- }
624
- const resolved = resolveAcodeProviderConfig(instanceConfig, globalConfig, "acode-default");
625
- void _dbg.log({
626
- level: "info",
627
- category: "resolve",
628
- message: "resolveAcodeProvider 解析结果",
629
- source: "bridge",
630
- details: {
631
- instanceConfig: redactForLog(instanceConfig),
632
- globalConfigModel: globalConfig?.model ?? null,
633
- resolvedModel: resolved.model,
634
- resolvedBaseURL: resolved.baseURL ?? null,
635
- resolvedApiKey: resolved.apiKey ? "[set]" : null,
636
- configContextLimit,
637
- resolvedSource: resolved.source,
638
- },
639
- });
640
- return {
641
- resolved,
642
- configPath,
643
- perSessionModel,
644
- configContextLimit,
645
- };
646
- }
647
- async createRuntime(sessionId, resolvedModel, config, runtimeHome) {
648
- const ACodeRuntime = await this.loadRuntimeConstructor();
649
- const configPath = config.mcpConfigPath || getAcodeConfigFile(runtimeHome);
650
- const runtime = new ACodeRuntime({
651
- sessionId,
652
- agentId: config.agentId,
653
- workingDirectory: config.workingDirectory,
654
- configPath,
655
- skillsDir: getAcodeSkillsDir(runtimeHome),
656
- model: resolvedModel,
657
- systemPrompt: typeof config.systemPrompt === "string"
658
- ? config.systemPrompt
659
- : undefined,
660
- globalPrompt: typeof config.globalPrompt === "string"
661
- ? config.globalPrompt
662
- : undefined,
663
- rolePrompt: typeof config.rolePrompt === "string"
664
- ? config.rolePrompt
665
- : undefined,
666
- memory: typeof config.memory === "string"
667
- ? config.memory
668
- : undefined,
669
- env: config.envOverrides,
670
- subAgentManager,
671
- ...(config.subAgentModel ? { subAgentModel: config.subAgentModel } : {}),
672
- ...(config.subAgentEffort ? { subAgentEffort: config.subAgentEffort } : {}),
673
- ...(config.instanceAiConfig?.effort ? { effort: config.instanceAiConfig.effort } : {}),
674
- onFileChange: (record) => recordAgentFileChange(record),
675
- });
676
- const debugLogger = getInstanceAiConfigDebugLogger();
677
- void debugLogger.log({
678
- level: "info",
679
- category: "resolve",
680
- message: "ACode provider resolved",
681
- source: "bridge",
682
- sessionId,
683
- details: {
684
- resolvedModel,
685
- perInstanceModel: config.instanceAiConfig?.model || null,
686
- perInstanceBaseURL: config.instanceAiConfig?.baseURL
687
- ? "[set]"
688
- : null,
689
- perInstanceApiKey: config.instanceAiConfig?.apiKey
690
- ? "[set]"
691
- : null,
692
- configPath,
693
- },
694
- });
695
- runtime.on("event", (event) => this.forwardAcodeEvent(event));
696
- return runtime;
697
- }
698
- async loadRuntimeConstructor() {
699
- if (this.runtimeConstructor) {
700
- return this.runtimeConstructor;
701
- }
702
- const modulePath = getAcodePackageEntryPath();
703
- const module = (await import(toAcodeEsmImportSpecifier(modulePath)));
704
- if (!module.ACodeRuntime) {
705
- throw new Error(`ACode runtime export not found: ${modulePath}`);
706
- }
707
- this.runtimeConstructor = module.ACodeRuntime;
708
- return this.runtimeConstructor;
709
- }
710
- requireSession(sessionId) {
711
- const entry = this.sessions.get(sessionId);
712
- if (!entry) {
713
- throw new Error("ACode session not found");
714
- }
715
- return entry;
716
- }
717
- /**
718
- * Run the startup prompt after the HTTP launch has already completed.
719
- * Main flow: mark the session busy -> submit the prompt in the background ->
720
- * rely on normal ACode events to publish turn completion or errors.
721
- */
722
- startInitialPromptTurn(sessionId, initialPrompt) {
723
- const entry = this.sessions.get(sessionId);
724
- if (!entry) {
725
- return;
726
- }
727
- entry.adapterSession.status = "thinking";
728
- this.emit("status-change", sessionId, "thinking");
729
- // 启动超时检测
730
- this.startSubmitTimeoutDetection(sessionId, entry, "startInitialPromptTurn");
731
- void entry.runtime
732
- .submit(initialPrompt)
733
- .then(() => {
734
- // submit 成功返回,取消超时检测
735
- this.cancelSubmitTimeoutDetection(entry);
736
- this.recoverResolvedTurnWithoutTerminalEvent(sessionId);
737
- })
738
- .catch((error) => {
739
- const currentEntry = this.sessions.get(sessionId);
740
- if (!currentEntry) {
741
- return;
742
- }
743
- // submit 抛出异常,取消超时检测
744
- this.cancelSubmitTimeoutDetection(currentEntry);
745
- // R4 fix: a failed initial prompt turn used to leave the session
746
- // permanently in 'error' because autoPollActive was false at this
747
- // point (only set by submitAutoPollPrompt), so the R2 recovery in
748
- // forwardAcodeEvent skipped it, and no idle loop was armed.
749
- // Now we always recover to 'waiting_input' and arm the idle loop
750
- // so the agent can retry via poll_message.
751
- this.emitRuntimeError(sessionId, error, { recover: true });
752
- });
753
- }
754
- forwardAcodeEvent(event) {
755
- const entry = this.sessions.get(event.sessionId);
756
- if (!entry) {
757
- return;
758
- }
759
- if (event.type === "assistant_delta") {
760
- const text = typeof event.data.text === "string" ? event.data.text : "";
761
- if (entry.autoPollActive) {
762
- entry.autoPollObservedAssistantText = true;
763
- }
764
- entry.adapterSession.messages.push({
765
- id: uuidv4(),
766
- sessionId: event.sessionId,
767
- role: "assistant",
768
- content: text,
769
- timestamp: event.timestamp,
770
- });
771
- // 收到流式数据,重置看门狗
772
- this.resetDataFlowWatchdog(event.sessionId, entry);
773
- this.emitProviderEvent(event, "text_delta", { text });
774
- return;
775
- }
776
- if (event.type === "thinking_delta") {
777
- const text = typeof event.data.text === "string" ? event.data.text : "";
778
- // 收到流式思考过程数据,重置看门狗
779
- this.resetDataFlowWatchdog(event.sessionId, entry);
780
- this.emitProviderEvent(event, "thinking", { text });
781
- return;
782
- }
783
- if (event.type === "command_output") {
784
- this.emitProviderEvent(event, "command_output", {
785
- text: typeof event.data.text === "string" ? event.data.text : "",
786
- stream: toCommandOutputStream(event.data.stream),
787
- sequence: typeof event.data.sequence === "number"
788
- ? event.data.sequence
789
- : undefined,
790
- cwd: typeof event.data.cwd === "string" ? event.data.cwd : undefined,
791
- commandLineCount: typeof event.data.commandLineCount === "number"
792
- ? event.data.commandLineCount
793
- : undefined,
794
- commandFilePath: typeof event.data.commandFilePath === "string"
795
- ? event.data.commandFilePath
796
- : undefined,
797
- });
798
- return;
799
- }
800
- if (event.type === "ai_request") {
801
- const estimatedTokens = toEstimatedContextTokens(event.data);
802
- let displayUsage;
803
- if (estimatedTokens > 0) {
804
- // ★ 限制估算值的增长幅度,防止 JSON 结构开销导致的虚高跳变。
805
- // estimateTokensFromText(requestBody) 基于 pretty-printed JSON 字符串长度 / 3 估算,
806
- // JSON 结构开销(键名、引号、缩进、转义)导致估算值系统性虚高 3-5x+,
807
- // 在 turn_complete 到达前会造成面板显示从 xx k 突跳到几 M 再跳回。
808
- // 正常单轮上下文增长 < 50%,cap 1.5x 足够覆盖增长同时防止跳变。
809
- // turn_complete 携带 provider 实际 prompt_tokens 时始终覆盖此估算值。
810
- // 压缩后 context_compaction end 将 inputTokens 重置为 0,
811
- // 此处 0 值直接使用估算值(压缩后对话更小,估算值也相应更小)。
812
- const currentInput = entry.adapterSession.totalUsage.inputTokens;
813
- if (currentInput > 0) {
814
- entry.adapterSession.totalUsage.inputTokens = Math.min(estimatedTokens, Math.floor(currentInput * 1.5));
815
- }
816
- else {
817
- entry.adapterSession.totalUsage.inputTokens = estimatedTokens;
818
- }
819
- // ★ outputTokens 保持上一轮 turn_complete 写入的单轮值(覆盖式语义),
820
- // 不再用 estimateOutputTokensFromMessages(累计估算)通过 Math.max 撑高。
821
- // 该累计估算会把历史所有 assistant 输出叠加,与 prompt_tokens(已含历史) 重复,
822
- // 导致显示 total 系统性虚高。结构与 runtime 的 actualUsed 一致:
823
- // input(请求体估算) + output(当前轮 completion)。
824
- displayUsage = { ...entry.adapterSession.totalUsage };
825
- }
826
- // ★ 修复 BUG:状态切换与 status-change 发射必须移到 if (estimatedTokens > 0) 块外。
827
- // 原实现仅在 estimatedTokens > 0 时才切换状态并发射 status-change,
828
- // 导致 estimatedTokens === 0(如 ai_request 无 requestBody 或无法解析)时:
829
- // 1. adapter 内部 status 保持 waiting_input(未切换为 thinking)
830
- // 2. 不发射 status-change 事件,但 emitProviderEvent 照常发射
831
- // 3. terminal.ts 通过 provider event 路径发送 thinking + ai_request 到前端
832
- // 4. recoverResolvedTurnWithoutTerminalEvent 因 status 是 waiting_input 而跳过恢复
833
- // 5. 残留 actionType:"ai_request" 永远无法清理,前端永久显示 "AI请求: 模型: xxx"
834
- // ★ 压缩完成后 adapterSession.status 仍为 "compacting",此处恢复为 "thinking",
835
- // 使 status-change 走普通路径(而非 compacting 串行队列),
836
- // 并带上 actionType 避免 restoredActionInfo 覆盖 usage。
837
- if (entry.adapterSession.status === "compacting") {
838
- entry.adapterSession.status = "thinking";
839
- }
840
- // ★ 修复:ai_request 表示 AI 正在请求模型,无论 keepWaitingStatus 如何,
841
- // 状态都应切换为 "thinking"。否则 keepWaitingStatus=true 时 status 保持
842
- // "waiting_input",导致 submit 完成后跳过 recoverResolvedTurnWithoutTerminalEvent,
843
- // 残留 actionType:"ai_request" 永远无法清理,前端永久显示 "AI请求: 模型: xxx"。
844
- if (entry.adapterSession.status === "waiting_input") {
845
- entry.adapterSession.status = "thinking";
846
- }
847
- const formattedDetail = this.formatAiRequestActionDetail(event.data);
848
- const aiRequestActionInfo = {
849
- actionType: "ai_request",
850
- actionLabel: "AI请求",
851
- actionDetail: formattedDetail,
852
- };
853
- if (displayUsage) {
854
- aiRequestActionInfo.usage = displayUsage;
855
- }
856
- this.emit("status-change", event.sessionId, entry.adapterSession.status, aiRequestActionInfo);
857
- // 启动数据流看门狗:ai_request 发出后开始监控流式数据返回
858
- this.startDataFlowWatchdog(event.sessionId, entry);
859
- this.emitProviderEvent(event, "ai_request", {
860
- model: typeof event.data.model === "string" ? event.data.model : undefined,
861
- endpoint: typeof event.data.endpoint === "string"
862
- ? event.data.endpoint
863
- : undefined,
864
- requestMethod: typeof event.data.requestMethod === "string"
865
- ? event.data.requestMethod
866
- : undefined,
867
- requestUrl: typeof event.data.requestUrl === "string"
868
- ? event.data.requestUrl
869
- : undefined,
870
- requestHeaders: toStringRecord(event.data.requestHeaders),
871
- stream: typeof event.data.stream === "boolean"
872
- ? event.data.stream
873
- : undefined,
874
- messageCount: typeof event.data.messageCount === "number"
875
- ? event.data.messageCount
876
- : undefined,
877
- toolCount: typeof event.data.toolCount === "number"
878
- ? event.data.toolCount
879
- : undefined,
880
- lastUserMessagePreview: typeof event.data.lastUserMessagePreview === "string"
881
- ? event.data.lastUserMessagePreview
882
- : undefined,
883
- requestBody: typeof event.data.requestBody === "string"
884
- ? event.data.requestBody
885
- : undefined,
886
- });
887
- return;
888
- }
889
- if (event.type === "turn_complete") {
890
- const usage = toUsage(event.data.usage);
891
- if (usage) {
892
- // ★ 同时更新 inputTokens 和 outputTokens(均覆盖式赋值)。
893
- // provider 返回的 prompt_tokens 是当前上下文大小的最准确度量,
894
- // 优于 ai_request 的 requestBody/3 估算。直接赋值(非累加),
895
- // 因为 prompt_tokens 代表本次请求时 provider 看到的完整上下文大小。
896
- // outputTokens 也采用覆盖式(= 当前轮 completion_tokens),与 runtime 的
897
- // state.accumulatedOutputTokens(runtime.ts,覆盖式)同构。
898
- // 之前 outputTokens 用 += 累加,导致显示的 total = prompt_tokens(已含历史output)
899
- // + Σcompletion 把历史 output 重复计算,面板系统性虚高,先于 runtime 达到压缩阈值。
900
- // ★ 过滤 fallback usage:当 provider 不返回 usage 时,runtime 会用
901
- // { inputTokens: trimmed.length, outputTokens: text.length } 作为 fallback。
902
- // trimmed.length 只是用户消息长度(通常 < 1000),远小于实际上下文,
903
- // 会把面板从几十 k 砸到几百,造成显示突降。检测方式:fallback inputTokens
904
- // 远小于当前值(< 20%),说明不是真实 provider 数据,跳过更新。
905
- const currentInput = entry.adapterSession.totalUsage.inputTokens;
906
- const isFallbackUsage = currentInput > 0 && usage.inputTokens < Math.floor(currentInput * 0.2);
907
- if (!isFallbackUsage) {
908
- entry.adapterSession.totalUsage.inputTokens = usage.inputTokens;
909
- entry.adapterSession.totalUsage.outputTokens = usage.outputTokens;
910
- }
911
- }
912
- // 关键日志:记录 turn_complete 事件
913
- const previousStatus = entry.adapterSession.status;
914
- adapterDebugLog(`[ACode] turn_complete event received (session: ${event.sessionId}), ` +
915
- `previous status: ${previousStatus}, failed: ${event.data.failed === true}, ` +
916
- `aborted: ${event.data.aborted === true}`);
917
- if (isFailedAcodeTurnComplete(event.data)) {
918
- // R2 fix: a failed polling-driven turn (e.g., 60s MCP tool timeout,
919
- // model 5xx, provider unreachable) used to leave the session parked
920
- // in 'error' or 'thinking' forever, so the agent stopped calling
921
- // poll_message permanently. Recover to 'waiting_input' and re-arm
922
- // the auto-poll with a longer backoff so a transient failure
923
- // doesn't break the message loop.
924
- //
925
- // R4 fix: previously this only recovered when autoPollActive was
926
- // true (i.e. during a submitAutoPollPrompt-driven turn). When the
927
- // initial prompt turn failed, autoPollActive was false and the
928
- // session got permanently stuck in 'error'. Now we always recover
929
- // for non-terminal sessions so the idle loop can retry.
930
- adapterDebugLog(`[ACode] Recovering from failed turn_complete (session: ${event.sessionId})`);
931
- this.cancelDataFlowWatchdog(entry);
932
- this.recoverTurnFailure(event.sessionId, entry);
933
- return;
934
- }
935
- // ★ 修复竞态:看门狗触发 abort() 后,runtime 在 catch 块发射
936
- // turn_complete { aborted: true }(而非 failed: true)。
937
- // 原逻辑走正常完成路径,立即 armIdleLoopOrAutoPoll(250ms 重臂),
938
- // 导致新 submit 在前一轮 executeSubmit 仍占用 submitQueue 时就排队,
939
- // 加剧竞态。改为走 R2 恢复路径(2s 退避),给前一轮更多时间完成。
940
- if (event.data.aborted === true && entry.autoPollActive) {
941
- adapterDebugLog(`[ACode] Recovering from aborted turn_complete (session: ${event.sessionId}), ` +
942
- `using R2 2s backoff instead of immediate rearm`);
943
- this.cancelDataFlowWatchdog(entry);
944
- this.recoverTurnFailure(event.sessionId, entry);
945
- return;
946
- }
947
- // turn 正常完成,取消数据流看门狗
948
- this.cancelDataFlowWatchdog(entry);
949
- this.emitProviderEvent(event, "turn_complete", {
950
- usage: entry.adapterSession.totalUsage,
951
- responseBody: typeof event.data.responseBody === "string"
952
- ? event.data.responseBody
953
- : undefined,
954
- finishReason: typeof event.data.finishReason === "string"
955
- ? event.data.finishReason
956
- : undefined,
957
- });
958
- entry.adapterSession.status = "waiting_input";
959
- this.emit("status-change", event.sessionId, "waiting_input", {
960
- usage: entry.adapterSession.totalUsage,
961
- });
962
- adapterDebugLog(`[ACode] Status changed to waiting_input after turn_complete (session: ${event.sessionId})`);
963
- if (entry.autoPollActive &&
964
- entry.autoPollObservedAssistantText &&
965
- !entry.autoPollObservedPollingTool &&
966
- !entry.autoPollObservedAnyTool) {
967
- entry.stopAutoPollAfterCurrentTurn = true;
968
- console.warn(`[${this.displayName}] Auto-poll turn produced assistant text without calling any tool; stopping auto-poll rearm for session ${event.sessionId}`);
969
- return;
970
- }
971
- this.armIdleLoopOrAutoPoll(event.sessionId);
972
- return;
973
- }
974
- if (event.type === "session_complete") {
975
- this.cancelDataFlowWatchdog(entry);
976
- this.cancelScheduledAutoPoll(entry);
977
- entry.failedTurnRecoveryScheduled = false;
978
- entry.idlePollLoop?.stop();
979
- entry.adapterSession.status = "completed";
980
- this.emitProviderEvent(event, "session_complete", {
981
- exitCode: Number(event.data.exitCode) || 0,
982
- });
983
- this.emit("status-change", event.sessionId, "completed");
984
- return;
985
- }
986
- if (event.type === "question_request") {
987
- // ask_user_question 工具被调用:runtime 在 execute() 中阻塞等待答案。
988
- // 取消数据流看门狗(用户思考/回答时间不计入 40s 超时),
989
- // 等用户回答后 LLM 重新输出时 assistant_delta/thinking_delta 会重新启动看门狗。
990
- this.cancelDataFlowWatchdog(entry);
991
- // 取消 submit 超时检测(用户回答时间不计入 2 分钟超时),
992
- // 记录 toolCallId + questions 供 sendQuestionAnswer 和 getPendingQuestion 使用,
993
- // 转发事件让 terminal.ts 推到前端。
994
- this.cancelSubmitTimeoutDetection(entry);
995
- const toolCallId = typeof event.data.toolCallId === "string"
996
- ? event.data.toolCallId
997
- : "";
998
- const questions = Array.isArray(event.data.questions)
999
- ? event.data.questions
1000
- : [];
1001
- if (toolCallId) {
1002
- entry.pendingQuestionToolCallId = toolCallId;
1003
- entry.pendingQuestions = questions;
1004
- }
1005
- this.emit("ask-user-question", {
1006
- sessionId: event.sessionId,
1007
- toolCallId: toolCallId || undefined,
1008
- questions,
1009
- });
1010
- return;
1011
- }
1012
- if (event.type === "error") {
1013
- // nonFatal=true 表示工具执行失败等非致命错误(Agent 会继续处理),
1014
- // 仅推送事件到时间线,不变更运行态为 "error",避免面板启动/停止按钮异常。
1015
- if (event.data.nonFatal) {
1016
- // ★ 修复:重试期间(每次 fetch failed 后退避)无流式数据到达,
1017
- // 看门狗仍以"上次 delta + 40s"倒计时,可能在重试周期内误触发 40s 超时。
1018
- // 重置数据流计时基准,让看门狗从重试时刻重新计时。
1019
- this.resetDataFlowWatchdog(event.sessionId, entry);
1020
- this.emitProviderEvent(event, "error", {
1021
- text: String(event.data.message || "ACode runtime error"),
1022
- });
1023
- return;
1024
- }
1025
- this.cancelDataFlowWatchdog(entry);
1026
- entry.adapterSession.status = "error";
1027
- this.emitProviderEvent(event, "error", {
1028
- text: String(event.data.message || "ACode runtime error"),
1029
- });
1030
- this.emit("status-change", event.sessionId, "error");
1031
- if (!entry.autoPollActive) {
1032
- this.recoverTurnFailure(event.sessionId, entry);
1033
- }
1034
- return;
1035
- }
1036
- if (event.type === "mcp_status") {
1037
- // R3 fix: the ACode runtime emits a startup mcp_status event with no
1038
- // `action` field but containing a `tools` array describing what the
1039
- // LLM can actually call. Validate that a poll_message tool is exposed,
1040
- // otherwise the message loop cannot continue.
1041
- //
1042
- // The previous version of this check matched the literal string
1043
- // 'aws-mcp' against the user-configured MCP server keys in the
1044
- // `servers` field, which produced false positives when the operator
1045
- // named the entry differently (e.g. 'aws-mcp-server' or 'agent-mcp')
1046
- // while still exposing aws-mcp's tools to the LLM. Worse, on a
1047
- // mismatch it emitted a generic `type: 'error'` ProviderEvent, which
1048
- // the rest of the bridge promotes to a fatal session-level 'error'
1049
- // status that the dashboard cannot recover from — the LLM keeps
1050
- // working but the timeline header gets stuck on "发生错误" forever.
1051
- //
1052
- // The fix below validates against the actual `tools` list (the same
1053
- // tools the LLM sees) and on a missing poll_message it only logs a
1054
- // warning. The runtime will naturally emit a real 'error' event the
1055
- // first time the LLM tries to call a missing tool, which the existing
1056
- // error handling already routes correctly without permanently locking
1057
- // the session status.
1058
- if (!event.data.action && Array.isArray(event.data.tools)) {
1059
- const tools = event.data.tools;
1060
- const hasPollMessage = tools.some((tool) => isPollMessageToolDescriptor(tool));
1061
- if (!hasPollMessage) {
1062
- const message = "ACode runtime is not exposing a poll_message tool. The LLM cannot call poll_message. " +
1063
- "Please configure an MCP server that provides poll_message (e.g. aws-mcp) in the dashboard panel for this runtime.";
1064
- console.warn(`[${this.displayName}] ${message}`);
1065
- }
1066
- // 将启动时加载的 MCP 工具清单缓存到 entry,等待 skill_status 事件到达后合并发出,
1067
- // 避免两个 runtime_capabilities 事件并发 HTTP POST 导致其中一个丢失
1068
- const mcpServers = Array.isArray(event.data.servers)
1069
- ? event.data.servers
1070
- : [];
1071
- const mcpTools = tools
1072
- .map((tool) => ({
1073
- name: String(tool.name ?? ""),
1074
- source: String(tool.source ?? ""),
1075
- description: String(tool.description ?? ""),
1076
- }))
1077
- .filter((tool) => tool.name);
1078
- const mcpCapabilities = {
1079
- kind: "mcp",
1080
- mcpServers,
1081
- mcpTools,
1082
- };
1083
- entry.cachedMcpCapabilities = mcpCapabilities;
1084
- // 同步保存到 lastMcpCapabilities,运行中再次发 skill_status(如 skill 目录变更)
1085
- // 时,会用它合并 MCP 信息,避免覆盖为空数组
1086
- entry.lastMcpCapabilities = mcpCapabilities;
1087
- // 更新 lastCombinedCapabilities 的 MCP 部分,保留已有的 skills(若 skill_status 已先到达)
1088
- entry.lastCombinedCapabilities = {
1089
- kind: "combined",
1090
- mcpServers,
1091
- mcpTools,
1092
- skills: entry.lastCombinedCapabilities?.skills ?? [],
1093
- skillsDir: entry.lastCombinedCapabilities?.skillsDir,
1094
- skillGroups: entry.lastCombinedCapabilities?.skillGroups ?? [],
1095
- };
1096
- adapterDebugLog(`[cap-diag] MCP capabilities cached: ${mcpServers.length} servers, ${mcpTools.length} tools`, {
1097
- sessionId: event.sessionId,
1098
- servers: mcpServers,
1099
- toolNames: mcpTools.map(t => t.name),
1100
- });
1101
- // 兜底:如果 skill_status 事件在 CAPABILITIES_FALLBACK_MS 内未到达,
1102
- // 单独发出 MCP 能力清单,避免 skill_status 缺失导致"暂无能力清单"。
1103
- if (entry.capabilitiesFallbackTimer) {
1104
- clearTimeout(entry.capabilitiesFallbackTimer);
1105
- }
1106
- entry.capabilitiesFallbackTimer = setTimeout(() => {
1107
- const cached = entry.cachedMcpCapabilities;
1108
- if (!cached)
1109
- return;
1110
- const fallbackCapabilities = {
1111
- ...cached,
1112
- skills: [],
1113
- skillGroups: [],
1114
- };
1115
- this.emitProviderEvent(event, "runtime_capabilities", {
1116
- capabilities: fallbackCapabilities,
1117
- actionType: "runtime_capabilities",
1118
- actionLabel: "运行时能力",
1119
- actionDetail: JSON.stringify(fallbackCapabilities),
1120
- });
1121
- adapterDebugLog(`[cap-diag] MCP capabilities emitted via fallback (skill_status not received within ${CAPABILITIES_FALLBACK_MS}ms)`, {
1122
- sessionId: event.sessionId,
1123
- mcpServers: cached.mcpServers,
1124
- mcpToolsCount: cached.mcpTools?.length ?? 0,
1125
- });
1126
- entry.cachedMcpCapabilities = undefined;
1127
- entry.capabilitiesFallbackTimer = undefined;
1128
- }, CAPABILITIES_FALLBACK_MS);
1129
- return;
1130
- }
1131
- const eventAction = String(event.data.action ?? "");
1132
- adapterDebugLog(`[ACode] 📡 mcp_status event: action=${eventAction}, ` +
1133
- `server=${typeof event.data.server === "string" ? event.data.server : "?"}, ` +
1134
- `tool=${typeof event.data.tool === "string" ? event.data.tool : "?"}, ` +
1135
- `toolUseId=${typeof event.data.toolUseId === "string" ? event.data.toolUseId.slice(0, 16) + "…" : "none"}, ` +
1136
- `failed=${event.data.failed === true}, ` +
1137
- `session=${event.sessionId}, status=${entry.adapterSession.status}`);
1138
- const server = typeof event.data.server === "string" ? event.data.server : "mcp";
1139
- const tool = typeof event.data.tool === "string" ? event.data.tool : "";
1140
- const toolName = server && tool ? `${server}__${tool}` : tool || server;
1141
- const toolInput = event.data.arguments &&
1142
- typeof event.data.arguments === "object" &&
1143
- !Array.isArray(event.data.arguments)
1144
- ? event.data.arguments
1145
- : undefined;
1146
- const actionInfo = getToolActionInfo(toolName, toolInput);
1147
- if (entry.autoPollActive) {
1148
- // ★ 修复永久卡死:任何工具调用(包括工作工具)都标记为已观察,
1149
- // 避免 turn_complete 时误判为"只输出文本不调用工具"而停止 auto-poll。
1150
- entry.autoPollObservedAnyTool = true;
1151
- if (actionInfo.actionType === "idle") {
1152
- entry.autoPollObservedPollingTool = true;
1153
- }
1154
- }
1155
- const actionDetail = isAcodeSemanticToolAction(actionInfo.actionType)
1156
- ? actionInfo.actionDetail ||
1157
- toAcodeMcpActionDetail(undefined, server, tool)
1158
- : toAcodeMcpActionDetail(actionInfo.actionDetail, server, tool);
1159
- const toolUseId = typeof event.data.toolUseId === "string"
1160
- ? event.data.toolUseId
1161
- : undefined;
1162
- // 序列化输入参数为 JSON 字符串,用于时间线展示
1163
- const actionInput = toolInput ? JSON.stringify(toolInput, null, 2) : undefined;
1164
- const eventData = {
1165
- toolName: actionDetail,
1166
- // ★ 保存原始工具名(如 "acode__await_sub_agents"),供看门狗豁免判断使用
1167
- rawToolName: toolName,
1168
- actionType: actionInfo.actionType,
1169
- actionLabel: actionInfo.actionLabel,
1170
- actionDetail,
1171
- ...(actionInput ? { actionInput } : {}),
1172
- ...(toolInput ? { toolInput } : {}),
1173
- ...(toolUseId ? { toolUseId, actionId: toolUseId } : {}),
1174
- // 传递 MCP 服务器名,供前端时间线展示工具来源
1175
- metadata: { mcpServer: server },
1176
- };
1177
- if (event.data.action === "tool_result") {
1178
- this.emitProviderEvent(event, "tool_use_end", {
1179
- ...eventData,
1180
- toolResult: typeof event.data.result === "string"
1181
- ? event.data.result
1182
- : JSON.stringify(event.data.result ?? ""),
1183
- });
1184
- const status = actionInfo.actionType === "idle" &&
1185
- !hasPollingMessages(event.data.result)
1186
- ? "waiting_input"
1187
- : "thinking";
1188
- entry.adapterSession.status = status;
1189
- // ★ 修复 BUG(时间线永久卡在"编辑文件"):工具执行完成时取消数据流看门狗。
1190
- // 原实现中 tool_use_start/tool_use_end 既不取消也不重置看门狗,
1191
- // 导致工具执行期间看门狗仍以"上次 delta + 40s"倒计时,
1192
- // 若工具执行时间接近或超过 40s(如大文件编辑、Windows 杀毒扫描),
1193
- // 看门狗会在 tool_using 状态误触发,abort 无效(内置工具不可中断),
1194
- // recoverTurnFailure 不补发 tool_use_end → 时间线永久卡在"编辑文件"。
1195
- this.cancelDataFlowWatchdog(entry);
1196
- entry.pendingToolEventData = undefined;
1197
- // ★ 修复重复时间线条目:idle 类型(poll_message)的 tool_use_end status-change
1198
- // 始终传 undefined,避免 terminal.ts 的 restoredActionInfo 恢复出 actionType+actionLabel
1199
- // 导致 dashboard 创建第二个"调用MCP"条目。tool_use_start 的 status-change 已创建条目。
1200
- // 非 idle 类型仍按原逻辑传 eventData(waiting_input 时)以保留输入参数展示。
1201
- const isIdleTool = actionInfo.actionType === "idle";
1202
- this.emit("status-change", event.sessionId, status, isIdleTool ? undefined : (status === "thinking" ? undefined : eventData));
1203
- return;
1204
- }
1205
- const status = actionInfo.actionType === "idle" ? "waiting_input" : "tool_using";
1206
- entry.adapterSession.status = status;
1207
- // ★ 修复 BUG(时间线永久卡在"编辑文件"):工具开始执行时重置数据流看门狗。
1208
- // 工具执行(如 edit_file)可能耗时较长(大文件、Windows I/O、杀毒扫描),
1209
- // 期间无流式 delta 数据返回。若不重置看门狗,40s 超时后会在 tool_using 状态误触发,
1210
- // 导致 recoverTurnFailure 不补发 tool_use_end,时间线永久卡住。
1211
- // 重置看门狗让计时从工具开始时重新计算,给工具执行留出充足时间。
1212
- if (status === "tool_using") {
1213
- this.resetDataFlowWatchdog(event.sessionId, entry);
1214
- // 缓存当前工具事件数据,供 recoverTurnFailure 补发 tool_use_end 使用
1215
- entry.pendingToolEventData = { ...eventData };
1216
- }
1217
- this.emitProviderEvent(event, "tool_use_start", eventData);
1218
- this.emit("status-change", event.sessionId, status, eventData);
1219
- return;
1220
- }
1221
- if (event.type === "skill_status") {
1222
- // skill_status 事件可能在两个时机到达:
1223
- // 1. ACode runtime 启动时(包含已加载的 Skill 列表)
1224
- // 2. 运行中 skills 目录变更触发重扫后(动态感知新 skill)
1225
- // 合并 MCP 能力清单后,作为单个 runtime_capabilities 事件发出,
1226
- // 避免与 mcp_status 的 runtime_capabilities 事件并发 HTTP POST 导致其中一个丢失。
1227
- // 启动时用 cachedMcpCapabilities(等待合并);运行中 cachedMcp 已清理,
1228
- // 回退到 lastMcpCapabilities(保留最近一次 MCP 信息,避免覆盖为空数组)。
1229
- const skillsRaw = Array.isArray(event.data.skills)
1230
- ? event.data.skills
1231
- : [];
1232
- const skills = skillsRaw
1233
- .map((skill) => ({
1234
- name: String(skill.name ?? ""),
1235
- description: typeof skill.description === "string"
1236
- ? skill.description
1237
- : undefined,
1238
- directory: typeof skill.directory === "string"
1239
- ? skill.directory
1240
- : undefined,
1241
- }))
1242
- .filter((skill) => skill.name);
1243
- const skillsDir = typeof event.data.skillsDir === "string"
1244
- ? event.data.skillsDir
1245
- : undefined;
1246
- // 按目录分组的 skills(含空目录节点),供前端按目录展示
1247
- const skillGroupsRaw = Array.isArray(event.data.skillGroups)
1248
- ? event.data.skillGroups
1249
- : [];
1250
- const skillGroups = skillGroupsRaw
1251
- .map((group) => {
1252
- const groupSkills = Array.isArray(group.skills)
1253
- ? group.skills
1254
- : [];
1255
- return {
1256
- dir: typeof group.dir === "string" ? group.dir : "",
1257
- exists: group.exists === true,
1258
- skills: groupSkills
1259
- .map((skill) => ({
1260
- name: String(skill.name ?? ""),
1261
- description: typeof skill.description === "string"
1262
- ? skill.description
1263
- : undefined,
1264
- directory: typeof skill.directory === "string"
1265
- ? skill.directory
1266
- : undefined,
1267
- }))
1268
- .filter((skill) => skill.name),
1269
- };
1270
- })
1271
- .filter((group) => group.dir);
1272
- // 优先用启动缓存(cachedMcp),运行中回退到 lastMcpCapabilities
1273
- const mcpSource = entry.cachedMcpCapabilities ?? entry.lastMcpCapabilities;
1274
- const isStartupMerge = entry.cachedMcpCapabilities !== undefined;
1275
- const mergedCapabilities = {
1276
- kind: "combined",
1277
- mcpServers: mcpSource?.mcpServers ?? [],
1278
- mcpTools: mcpSource?.mcpTools ?? [],
1279
- skills,
1280
- skillsDir,
1281
- skillGroups,
1282
- };
1283
- this.emitProviderEvent(event, "runtime_capabilities", {
1284
- capabilities: mergedCapabilities,
1285
- actionType: "runtime_capabilities",
1286
- actionLabel: "运行时能力",
1287
- actionDetail: JSON.stringify(mergedCapabilities),
1288
- });
1289
- // 更新 lastCombinedCapabilities,作为主动查询的单一数据源
1290
- entry.lastCombinedCapabilities = mergedCapabilities;
1291
- adapterDebugLog(`[cap-diag] Combined capabilities emitted ${isStartupMerge ? "(startup merge)" : "(runtime refresh)"}: ${(mcpSource?.mcpTools ?? []).length} MCP tools, ${skills.length} skills`, {
1292
- sessionId: event.sessionId,
1293
- mcpServers: mcpSource?.mcpServers ?? [],
1294
- skillsDir,
1295
- });
1296
- // 清理启动缓存与兜底定时器(仅启动合并路径需要;运行中刷新时 cachedMcp 已是 undefined)
1297
- if (entry.capabilitiesFallbackTimer) {
1298
- clearTimeout(entry.capabilitiesFallbackTimer);
1299
- entry.capabilitiesFallbackTimer = undefined;
1300
- }
1301
- entry.cachedMcpCapabilities = undefined;
1302
- return;
1303
- }
1304
- if (event.type === "session_started") {
1305
- const estimatedTokens = toEstimatedContextTokens(event.data);
1306
- // ★ 用 runtime emit 的实际压缩配置覆盖 totalUsage。
1307
- // runtime 的 contextLimit/maxOutput/inputLimit 来自 resolveProviderConfig(读 provider.default.* 三级),
1308
- // 是后端实际触发压缩用的值;adapter 启动时从顶层+查表解析的值仅作初始占位,必须被覆盖。
1309
- // compactionThreshold 是后端算好的实际阈值,前端直接显示此值,不再自算。
1310
- const runtimeContextLimit = toFiniteTokenCount(event.data.contextLimit) || undefined;
1311
- const runtimeMaxOutput = toFiniteTokenCount(event.data.maxOutput) || undefined;
1312
- const runtimeInputLimit = toFiniteTokenCount(event.data.inputLimit) || undefined;
1313
- const runtimeCompactionThreshold = toFiniteTokenCount(event.data.compactionThreshold) || undefined;
1314
- let usageChanged = false;
1315
- if (estimatedTokens > 0) {
1316
- entry.adapterSession.totalUsage.inputTokens = estimatedTokens;
1317
- entry.adapterSession.totalUsage.outputTokens = 0;
1318
- usageChanged = true;
1319
- }
1320
- if (runtimeContextLimit !== undefined) {
1321
- entry.adapterSession.totalUsage.contextLimit = runtimeContextLimit;
1322
- usageChanged = true;
1323
- }
1324
- if (runtimeMaxOutput !== undefined) {
1325
- entry.adapterSession.totalUsage.maxOutput = runtimeMaxOutput;
1326
- usageChanged = true;
1327
- }
1328
- if (runtimeInputLimit !== undefined) {
1329
- entry.adapterSession.totalUsage.inputLimit = runtimeInputLimit;
1330
- usageChanged = true;
1331
- }
1332
- if (runtimeCompactionThreshold !== undefined) {
1333
- entry.adapterSession.totalUsage.compactionThreshold = runtimeCompactionThreshold;
1334
- usageChanged = true;
1335
- }
1336
- if (usageChanged) {
1337
- this.emit("status-change", event.sessionId, entry.adapterSession.status, {
1338
- usage: entry.adapterSession.totalUsage,
1339
- });
1340
- }
1341
- return;
1342
- }
1343
- if (event.type === "exit_plan_mode") {
1344
- // 需求 4.1.1:exit_plan_mode 映射为 status 事件,等待用户审批计划
1345
- entry.adapterSession.status = "waiting_input";
1346
- const planText = typeof event.data.plan === "string"
1347
- ? event.data.plan
1348
- : undefined;
1349
- this.emitProviderEvent(event, "exit_plan_mode", {
1350
- toolInput: event.data.toolInput && typeof event.data.toolInput === "object"
1351
- ? event.data.toolInput
1352
- : undefined,
1353
- text: planText || "退出计划模式",
1354
- });
1355
- this.emit("status-change", event.sessionId, "waiting_input", {
1356
- actionLabel: "审批计划",
1357
- actionDetail: planText || "等待用户审批退出计划模式",
1358
- });
1359
- return;
1360
- }
1361
- if (event.type === "context_compaction") {
1362
- const data = event.data;
1363
- const action = data.action;
1364
- const isStart = action === "start";
1365
- const isEnd = action === "end";
1366
- const isPhase = action === "phase";
1367
- // parentId 用于将子事件(phase)关联到父压缩事件
1368
- const parentId = data.parentId;
1369
- // compactionId 作为父事件的 actionId,子事件通过 parentId 引用它
1370
- const compactionId = data.compactionId;
1371
- let actionLabel;
1372
- let actionDetail;
1373
- if (isStart) {
1374
- const sizeKB = String(data.sizeKB ?? data.sizeBeforeKB ?? "?");
1375
- const msgCount = String(data.messageCount ?? data.messageCountBefore ?? "?");
1376
- const estimatedTokens = data.estimatedTokens;
1377
- const actualInput = data.actualInputTokens ? `input=${data.actualInputTokens}` : "";
1378
- const actualOutput = data.actualOutputTokens ? `output=${data.actualOutputTokens}` : "";
1379
- const trigger = data.trigger ? `, 触发: ${data.trigger}` : "";
1380
- actionLabel = "上下文压缩开始";
1381
- if (estimatedTokens !== undefined) {
1382
- actionDetail = `压缩前: ~${estimatedTokens} tokens, 消息: ${msgCount}条` +
1383
- (actualInput ? `, 实际 tokens: ${actualInput} ${actualOutput}` : "") + trigger;
1384
- }
1385
- else {
1386
- actionDetail = `压缩前: ${sizeKB}KB, 消息: ${msgCount}条` +
1387
- (actualInput ? `, 实际 tokens: ${actualInput} ${actualOutput}` : "") + trigger;
1388
- }
1389
- }
1390
- else if (isPhase) {
1391
- // 子事件(阶段事件):使用 runtime 传入的 actionLabel 和 actionDetail
1392
- actionLabel = String(data.phaseLabel ?? "压缩阶段");
1393
- actionDetail = String(data.phaseDetail ?? "");
1394
- }
1395
- else {
1396
- // action === "end" —— 作为压缩完成子事件,嵌套在父事件下
1397
- const tokensBefore = data.tokensBefore;
1398
- const tokensAfter = data.tokensAfter;
1399
- const freedTokensNum = data.freedTokens;
1400
- const sizeBefore = String(data.sizeBeforeKB ?? "?");
1401
- const sizeAfter = String(data.sizeAfterKB ?? "?");
1402
- const freedKB = String(data.freedKB ?? "?");
1403
- const strategy = String(data.strategy ?? "?");
1404
- const pruned = Number(data.prunedToolMessages ?? 0);
1405
- const summarized = Number(data.summarizedMessages ?? 0);
1406
- actionLabel = "压缩完成";
1407
- if (tokensBefore !== undefined && tokensAfter !== undefined) {
1408
- const diff = freedTokensNum ?? (tokensBefore - tokensAfter);
1409
- actionDetail = `释放: ${diff} tokens, ${tokensBefore} tokens → ${tokensAfter} tokens, 策略: ${strategy}` +
1410
- (pruned ? `, 裁剪工具: ${pruned}条` : "") +
1411
- (summarized ? `, 摘要化: ${summarized}条` : "");
1412
- }
1413
- else {
1414
- actionDetail = `释放: ${freedKB}KB, ${sizeBefore}KB → ${sizeAfter}KB, 策略: ${strategy}` +
1415
- (pruned ? `, 裁剪工具: ${pruned}条` : "") +
1416
- (summarized ? `, 摘要化: ${summarized}条` : "");
1417
- }
1418
- }
1419
- // ★ 更新 adapter 内部状态为 compacting,使前端 agent 卡片显示压缩中
1420
- // 子事件(phase/end)不重复更新状态,仅推送到时间线
1421
- if (isStart) {
1422
- entry.adapterSession.status = "compacting";
1423
- }
1424
- const actionInfo = {
1425
- actionType: "context_compaction",
1426
- actionLabel,
1427
- actionDetail,
1428
- };
1429
- // ★ 事件层级:start 为父事件(root),phase 和 end 为子事件(nested)
1430
- // - 父事件:actionId = compactionId
1431
- // - 子事件:parentId = compactionId, actionId 唯一(避免前端 seenActionIds 去重冲突)
1432
- if (isStart && compactionId) {
1433
- actionInfo.actionId = compactionId;
1434
- }
1435
- else if ((isPhase || isEnd) && compactionId) {
1436
- actionInfo.parentId = compactionId;
1437
- if (isPhase) {
1438
- const phase = String(data.phase ?? "unknown");
1439
- actionInfo.actionId = `${compactionId}-phase-${phase}`;
1440
- }
1441
- else {
1442
- // end 事件使用独立 actionId,避免与 start 的 compactionId 重复
1443
- actionInfo.actionId = `${compactionId}-end`;
1444
- }
1445
- }
1446
- // 兼容旧路径:runtime 可能通过 parentId 字段传入
1447
- if (isPhase && parentId && !actionInfo.parentId) {
1448
- actionInfo.parentId = parentId;
1449
- }
1450
- // ★ 压缩结束时重置 totalUsage(input 和 output 均归零),使面板显示压缩后的真实上下文大小。
1451
- // 与 runtime 压缩后重置 accumulatedInputTokens/accumulatedOutputTokens = 0(runtime.ts)对齐。
1452
- // 若只重置 input 不重置 output,压缩后面板 total 仍含压缩前 output,持续虚高。
1453
- // 将 usage 合并到 end 事件的 actionInfo 中,避免额外的无 actionInfo status-change
1454
- // 触发前端 handleDeltaStatusEvent 创建无 actionId 的占位事件(会导致扁平化渲染)。
1455
- if (isEnd) {
1456
- entry.adapterSession.totalUsage.inputTokens = 0;
1457
- entry.adapterSession.totalUsage.outputTokens = 0;
1458
- // ★ 使用 spread 副本,避免后续事件(如 ai_request)修改 totalUsage 时
1459
- // 通过引用别名反向改变已发出的 compaction 事件 usage 快照。
1460
- actionInfo.usage = { ...entry.adapterSession.totalUsage };
1461
- }
1462
- this.emitProviderEvent(event, "context_compaction", actionInfo);
1463
- // ★ 与其他事件(tool_use_start/turn_complete 等)一致,
1464
- // 通过 status-change 事件将压缩状态推送到面板时间线。
1465
- this.emit("status-change", event.sessionId, "compacting", actionInfo);
1466
- return;
1467
- }
1468
- this.emitProviderEvent(event, "thinking", {
1469
- text: JSON.stringify(event.data),
1470
- });
1471
- }
1472
- emitProviderEvent(event, type, data) {
1473
- this.emit("event", {
1474
- type,
1475
- sessionId: event.sessionId,
1476
- timestamp: event.timestamp,
1477
- data,
1478
- });
1479
- }
1480
- /**
1481
- * After a completed turn, prefer the configured IdlePollLoop (set via setIdleCommands).
1482
- * Fall back to the legacy auto-poll mechanism when no idle commands are configured.
1483
- */
1484
- armIdleLoopOrAutoPoll(sessionId) {
1485
- const entry = this.sessions.get(sessionId);
1486
- if (!entry || this.isTerminalStatus(entry.adapterSession.status)) {
1487
- return;
1488
- }
1489
- if (entry.idleCommands?.idleInputCommand) {
1490
- this.getIdlePollLoop(entry).start();
1491
- return;
1492
- }
1493
- this.scheduleAutoPoll(sessionId);
1494
- }
1495
- getIdlePollLoop(entry) {
1496
- if (entry.idlePollLoop)
1497
- return entry.idlePollLoop;
1498
- const sessionId = entry.adapterSession.sessionId;
1499
- entry.idlePollLoop = new IdlePollLoop({
1500
- getStatus: () => this.sessions.get(sessionId)?.adapterSession.status,
1501
- getCommand: () => this.sessions.get(sessionId)?.idleCommands?.idleInputCommand,
1502
- isPollingCommand: (command) => isMessagePollingIdleCommand(command),
1503
- dispatchPollingPrompt: () => this.submitAutoPollPrompt(sessionId, ACODE_MESSAGE_POLLING_IDLE_PROMPT, {
1504
- keepWaitingStatus: true,
1505
- }),
1506
- dispatchRegularIdleCommand: (command) => this.submitAutoPollPrompt(sessionId, command),
1507
- });
1508
- return entry.idlePollLoop;
1509
- }
1510
- submitAutoPollPrompt(sessionId, prompt, options) {
1511
- const entry = this.sessions.get(sessionId);
1512
- if (!entry || this.isTerminalStatus(entry.adapterSession.status))
1513
- return;
1514
- // ★ 修复竞态:递增 token,.finally() 只在 token 匹配时执行清理。
1515
- // 防止前一轮 submit() 的 .finally() 清除新一轮的看门狗和 autoPollActive。
1516
- // 场景:看门狗 120s 触发 abort → R2 恢复 → 新一轮 submit 排队 →
1517
- // 前一轮 executeSubmit 数百秒后才完成 → .finally() 错误清除新一轮状态。
1518
- const myToken = (entry.autoPollToken ?? 0) + 1;
1519
- entry.autoPollToken = myToken;
1520
- entry.autoPollActive = true;
1521
- entry.autoPollObservedPollingTool = false;
1522
- entry.autoPollObservedAssistantText = false;
1523
- entry.autoPollObservedAnyTool = false;
1524
- entry.stopAutoPollAfterCurrentTurn = false;
1525
- if (!options?.keepWaitingStatus) {
1526
- entry.adapterSession.status = "thinking";
1527
- this.emit("status-change", sessionId, "thinking");
1528
- }
1529
- // 启动 submit 超时检测,防止 auto-poll 请求永久卡住
1530
- this.startSubmitTimeoutDetection(sessionId, entry, "submitAutoPollPrompt");
1531
- void entry.runtime
1532
- .submit(prompt)
1533
- .catch((error) => {
1534
- this.emitRuntimeError(sessionId, error);
1535
- })
1536
- .finally(() => {
1537
- const latestEntry = this.sessions.get(sessionId);
1538
- if (!latestEntry)
1539
- return;
1540
- // ★ token 守卫:如果 token 不匹配,说明新一轮 submitAutoPollPrompt 已启动,
1541
- // 当前轮的清理操作(取消看门狗、清除 autoPollActive)会错误覆盖新一轮状态。
1542
- // 新一轮已设置了新的看门狗和 autoPollActive=true,直接跳过清理。
1543
- // 关键:这防止了"前一轮 .finally() 清除新一轮看门狗 → 新一轮 fetch 挂死时无保护 → 永久卡死"的竞态。
1544
- if (latestEntry.autoPollToken !== myToken) {
1545
- adapterDebugLog(`[ACode] submitAutoPollPrompt.finally: token mismatch ` +
1546
- `(my=${myToken}, current=${latestEntry.autoPollToken}), ` +
1547
- `skipping cleanup — newer turn has taken over`);
1548
- return;
1549
- }
1550
- // 无论 submit 是正常返回还是超时恢复,都需要取消超时检测(超时回调里已设置 submitTimeoutTimer=undefined,再调 cancel 是安全的)
1551
- this.cancelSubmitTimeoutDetection(latestEntry);
1552
- latestEntry.autoPollActive = false;
1553
- // If the R2 recovery path already scheduled a delayed re-arm for a
1554
- // failed polling-driven turn, skip armIdleLoopOrAutoPoll so we do
1555
- // not double-arm (immediate fire via IdlePollLoop AND a 2s timer).
1556
- // The recovery timer is the source of truth here: the flag is just
1557
- // a defensive marker; the timer itself is what matters.
1558
- if (latestEntry.failedTurnRecoveryScheduled ||
1559
- latestEntry.autoPollTimer) {
1560
- latestEntry.failedTurnRecoveryScheduled = false;
1561
- return;
1562
- }
1563
- if (latestEntry.stopAutoPollAfterCurrentTurn) {
1564
- latestEntry.stopAutoPollAfterCurrentTurn = false;
1565
- latestEntry.autoPollObservedPollingTool = false;
1566
- latestEntry.autoPollObservedAssistantText = false;
1567
- latestEntry.autoPollObservedAnyTool = false;
1568
- return;
1569
- }
1570
- if (latestEntry.adapterSession.status === "error") {
1571
- this.recoverTurnFailure(sessionId, latestEntry);
1572
- return;
1573
- }
1574
- if (latestEntry.adapterSession.status === "thinking" ||
1575
- latestEntry.adapterSession.status === "tool_using") {
1576
- this.recoverResolvedTurnWithoutTerminalEvent(sessionId);
1577
- return;
1578
- }
1579
- if (latestEntry.adapterSession.status === "waiting_input") {
1580
- this.armIdleLoopOrAutoPoll(sessionId);
1581
- }
1582
- });
1583
- }
1584
- /**
1585
- * Continue ACode's headless agent loop after a completed turn.
1586
- * Main flow: debounce duplicate scheduling -> mark busy -> submit an internal
1587
- * poll instruction -> let normal ACode events publish MCP/tool/turn states.
1588
- *
1589
- * @param delayMs Optional override for the default 250ms backoff. The R2
1590
- * recovery path uses ACODE_AUTO_POLL_FAILURE_DELAY_MS (2s) to avoid
1591
- * busy-looping when the same failure recurs on every retry.
1592
- */
1593
- scheduleAutoPoll(sessionId, delayMs = ACODE_AUTO_POLL_DELAY_MS) {
1594
- const entry = this.sessions.get(sessionId);
1595
- if (!entry ||
1596
- entry.autoPollActive ||
1597
- entry.autoPollTimer ||
1598
- this.isTerminalStatus(entry.adapterSession.status)) {
1599
- return;
1600
- }
1601
- entry.autoPollTimer = setTimeout(() => {
1602
- const currentEntry = this.sessions.get(sessionId);
1603
- if (!currentEntry ||
1604
- currentEntry.autoPollActive ||
1605
- this.isTerminalStatus(currentEntry.adapterSession.status)) {
1606
- return;
1607
- }
1608
- currentEntry.autoPollTimer = undefined;
1609
- this.submitAutoPollPrompt(sessionId, ACODE_AUTO_POLL_PROMPT, {
1610
- keepWaitingStatus: true,
1611
- });
1612
- }, delayMs);
1613
- }
1614
- cancelScheduledAutoPoll(entry) {
1615
- if (!entry.autoPollTimer) {
1616
- return;
1617
- }
1618
- clearTimeout(entry.autoPollTimer);
1619
- entry.autoPollTimer = undefined;
1620
- }
1621
- isTerminalStatus(status) {
1622
- return status === "completed" || status === "terminated";
1623
- }
1624
- emitRuntimeError(sessionId, error, options) {
1625
- const currentEntry = this.sessions.get(sessionId);
1626
- if (!currentEntry) {
1627
- return;
1628
- }
1629
- currentEntry.adapterSession.status = "error";
1630
- this.emitRuntimeErrorEvent(sessionId, error);
1631
- this.emit("status-change", sessionId, "error");
1632
- if (options?.recover) {
1633
- this.recoverTurnFailure(sessionId, currentEntry);
1634
- }
1635
- }
1636
- emitRuntimeErrorEvent(sessionId, error) {
1637
- const timestamp = new Date().toISOString();
1638
- this.emit("event", {
1639
- type: "error",
1640
- sessionId,
1641
- timestamp,
1642
- data: { text: error instanceof Error ? error.message : String(error) },
1643
- });
1644
- }
1645
- /**
1646
- * 从会话消息中估算 AI 输出 token 数(字符数 / 4 粗略估算)。
1647
- * 注意:该估算对 CJK 文本偏高(实际每字约 1-2 tokens),
1648
- * 对纯 ASCII 文本偏低(约 3-5 字符/token),仅供面板展示实时趋势使用,
1649
- * 不应用于精确计费或配额判定。精确值以 turn_complete 事件中的 usage 为准。
1650
- */
1651
- estimateOutputTokensFromMessages(messages) {
1652
- const assistantText = messages
1653
- .filter((m) => m.role === "assistant")
1654
- .map((m) => m.content)
1655
- .join("");
1656
- return Math.max(0, Math.round(assistantText.length / 4));
1657
- }
1658
- recoverTurnFailure(sessionId, entry) {
1659
- if (this.isTerminalStatus(entry.adapterSession.status)) {
1660
- return;
1661
- }
1662
- // ★ 修复 BUG(时间线永久卡在"编辑文件"):当恢复前状态为 tool_using 时,
1663
- // 补发 tool_use_end 事件,关闭前端时间线中由 tool_use_start 创建的工具条目。
1664
- // 根因:数据流看门狗在 tool_using 状态触发后调用本方法,但原实现只切换状态为
1665
- // waiting_input,从不补发 tool_use_end。而 runtime 端因 abortRequested===true,
1666
- // 在 catch 块中 rethrow 前跳过了 tool_result 发射(3241 行),
1667
- // 导致 adapter 永远收不到 tool_result,tool_use_end 永不发射,时间线永久卡住。
1668
- if (entry.adapterSession.status === "tool_using" && entry.pendingToolEventData) {
1669
- const toolData = entry.pendingToolEventData;
1670
- adapterDebugLog(`[ACode] recoverTurnFailure: 补发 tool_use_end (tool: ${toolData.toolName ?? "unknown"}) ` +
1671
- `关闭时间线条目,避免永久卡在工具执行状态`);
1672
- this.emit("event", {
1673
- type: "tool_use_end",
1674
- sessionId,
1675
- timestamp: new Date().toISOString(),
1676
- data: {
1677
- ...toolData,
1678
- toolResult: "[aborted] 工具执行被数据流看门狗中断",
1679
- },
1680
- });
1681
- entry.pendingToolEventData = undefined;
1682
- }
1683
- // ★ 修复:超时恢复前强制 emit turn_complete,计入已有的 output tokens,
1684
- // 否则面板上 output_tokens 永远是 0(turn_complete 从未发射)。
1685
- // 使用消息历史中的 assistant 文本粗略估算 output tokens(累计估算)。
1686
- // 此为异常恢复路径(turn_complete 未到达,无真实 completion_tokens),
1687
- // 直接覆盖式赋值(不再用 delta += 累加,避免与覆盖式语义冲突导致重复计算)。
1688
- const estimatedOutput = this.estimateOutputTokensFromMessages(entry.adapterSession.messages);
1689
- if (estimatedOutput > 0) {
1690
- const prev = entry.adapterSession.totalUsage.outputTokens;
1691
- entry.adapterSession.totalUsage.outputTokens = estimatedOutput;
1692
- adapterDebugLog(`[ACode] recoverTurnFailure: 估算覆盖 output tokens ${prev} -> ${estimatedOutput} ` +
1693
- `(totalUsage now: input=${entry.adapterSession.totalUsage.inputTokens}, ` +
1694
- `output=${entry.adapterSession.totalUsage.outputTokens})`);
1695
- }
1696
- // ★ 修复:检测 idle 命令是否包含 poll_message,在 waiting_input 状态变更中携带 idle 动作信息
1697
- const idleCommands = entry.idleCommands?.idleInputCommand;
1698
- const willResumePolling = Boolean(idleCommands && isMessagePollingIdleCommand(idleCommands));
1699
- const idleActionInfo = willResumePolling ? {
1700
- actionType: 'idle',
1701
- actionLabel: '监听消息',
1702
- actionDetail: 'aws-mcp/poll_message',
1703
- } : undefined;
1704
- if (entry.autoPollTimer || entry.failedTurnRecoveryScheduled) {
1705
- if (entry.adapterSession.status !== "waiting_input") {
1706
- entry.adapterSession.status = "waiting_input";
1707
- this.emit("status-change", sessionId, "waiting_input", {
1708
- usage: entry.adapterSession.totalUsage,
1709
- ...idleActionInfo,
1710
- });
1711
- }
1712
- return;
1713
- }
1714
- this.cancelScheduledAutoPoll(entry);
1715
- entry.adapterSession.status = "waiting_input";
1716
- this.emit("status-change", sessionId, "waiting_input", {
1717
- usage: entry.adapterSession.totalUsage,
1718
- ...idleActionInfo,
1719
- });
1720
- entry.failedTurnRecoveryScheduled = true;
1721
- entry.autoPollTimer = setTimeout(() => {
1722
- const currentEntry = this.sessions.get(sessionId);
1723
- if (!currentEntry ||
1724
- currentEntry.autoPollActive ||
1725
- this.isTerminalStatus(currentEntry.adapterSession.status)) {
1726
- if (currentEntry) {
1727
- currentEntry.failedTurnRecoveryScheduled = false;
1728
- }
1729
- return;
1730
- }
1731
- currentEntry.autoPollTimer = undefined;
1732
- currentEntry.failedTurnRecoveryScheduled = false;
1733
- if (currentEntry.idleCommands?.idleInputCommand) {
1734
- this.getIdlePollLoop(currentEntry).start();
1735
- }
1736
- else {
1737
- this.submitAutoPollPrompt(sessionId, ACODE_AUTO_POLL_PROMPT, {
1738
- keepWaitingStatus: true,
1739
- });
1740
- }
1741
- }, ACODE_AUTO_POLL_FAILURE_DELAY_MS);
1742
- }
1743
- /**
1744
- * Recover turns where runtime.submit() resolved but no terminal turn event arrived.
1745
- *
1746
- * Main flow: ACode should normally emit turn_complete/session_complete/error before
1747
- * submit resolves. Some edge paths can emit only intermediate events (for example
1748
- * command_status or tool_result), then resolve. At that point there is no active
1749
- * turn anymore, so keeping the adapter at thinking/tool_using makes the dashboard
1750
- * look permanently stuck. Recover to waiting_input and arm the listening loop.
1751
- */
1752
- recoverResolvedTurnWithoutTerminalEvent(sessionId) {
1753
- adapterDebugLog(`[ACode] ▶️ recoverResolvedTurnWithoutTerminalEvent called for session ${sessionId}`);
1754
- const entry = this.sessions.get(sessionId);
1755
- if (!entry || this.isTerminalStatus(entry.adapterSession.status)) {
1756
- adapterDebugLog(`[ACode] Skipping recovery: entry=${!!entry}, isTerminal=${entry ? this.isTerminalStatus(entry.adapterSession.status) : 'N/A'}`);
1757
- return;
1758
- }
1759
- adapterDebugLog(`[ACode] Current status: ${entry.adapterSession.status}`);
1760
- if (entry.adapterSession.status !== "thinking" &&
1761
- entry.adapterSession.status !== "tool_using") {
1762
- adapterDebugLog(`[ACode] Skipping recovery: status is ${entry.adapterSession.status} (not thinking/tool_using)`);
1763
- return;
1764
- }
1765
- console.warn(`[ACode] ⚠️ Detected stuck state: ${entry.adapterSession.status} after submit resolved! ` +
1766
- `Recovering to waiting_input. ` +
1767
- `If runtime.submit() is still pending (e.g., built-in tool execution not interruptible by abort()), ` +
1768
- `the submitQueue is deadlocked and subsequent auto-poll/user submits will never execute.`);
1769
- adapterDebugLog(`[ACode] Recovering to waiting_input...`);
1770
- entry.adapterSession.status = "waiting_input";
1771
- // ★ 修复:检测 idle 命令是否包含 poll_message,在 waiting_input 状态变更中携带 idle 动作信息
1772
- const idleCommands = entry.idleCommands?.idleInputCommand;
1773
- const willResumePolling = Boolean(idleCommands && isMessagePollingIdleCommand(idleCommands));
1774
- const idleActionInfo = willResumePolling ? {
1775
- actionType: 'idle',
1776
- actionLabel: '监听消息',
1777
- actionDetail: 'aws-mcp/poll_message',
1778
- } : undefined;
1779
- this.emit("status-change", sessionId, "waiting_input", idleActionInfo);
1780
- adapterDebugLog(`[ACode] ✅ Recovery complete, status now: ${entry.adapterSession.status}`);
1781
- adapterDebugLog(`[ACode] ▶️ Arming idle loop or auto-poll...`);
1782
- this.armIdleLoopOrAutoPoll(sessionId);
1783
- adapterDebugLog(`[ACode] ✅ Idle loop armed, recovery process complete`);
1784
- }
1785
- /**
1786
- * 启动异步看门狗:周期性轮询运行时状态,仅在真正卡死时恢复。
1787
- *
1788
- * 替代原来两个独立的 setTimeout(submitTimeoutDetection + dataFlowWatchdog),
1789
- * 改为单一 setInterval 轮询。核心设计:
1790
- * - 每 ACODE_WATCHDOG_POLL_INTERVAL_MS(5s)检查一次状态
1791
- * - waiting_input 状态下不累计超时(agent 正在有意等待,如 poll_message/ask_user_question)
1792
- * - 仅在 thinking/tool_using 状态下累计"活跃持续时间"和"无数据流时间"
1793
- * - submit 超时 120s 或数据流超时 40s 时触发 recoverTurnFailure
1794
- *
1795
- * 相比 setTimeout 方案的优势:
1796
- * 1. 状态感知:每次轮询都读取当前真实状态,不会因状态在定时周期内多次变化而误判
1797
- * 2. waiting_input 自动暂停计时:无需递归重新计时,天然跳过
1798
- * 3. 单一 timer:避免两个独立 timer 的竞态和重复恢复
1799
- */
1800
- startSubmitTimeoutDetection(sessionId, entry, source) {
1801
- this.cancelSubmitTimeoutDetection(entry);
1802
- entry.watchdogSubmitStartTime = Date.now();
1803
- entry.watchdogLastDataFlowTime = Date.now();
1804
- entry.watchdogTimer = setInterval(() => {
1805
- const currentEntry = this.sessions.get(sessionId);
1806
- if (!currentEntry) {
1807
- this.cancelSubmitTimeoutDetection(entry);
1808
- return;
1809
- }
1810
- const status = currentEntry.adapterSession.status;
1811
- const now = Date.now();
1812
- // waiting_input:agent 正在有意等待(poll_message/ask_user_question),不计时
1813
- if (status === "waiting_input") {
1814
- // 重置计时基准,下次进入 thinking/tool_using 时从 0 开始计
1815
- currentEntry.watchdogSubmitStartTime = now;
1816
- currentEntry.watchdogLastDataFlowTime = now;
1817
- return;
1818
- }
1819
- // ★ tool_using 慢超时兜底:原实现完全无超时,工具 hang 时永久卡死。
1820
- // 现加 5min 慢超时,仅当工具确实彻底 hang(无 tool_use_end、无状态切换)时触发恢复。
1821
- // - 正常工具调用:tool_use_end 会切换状态为 thinking/waiting_input,重置计时
1822
- // - 多工具串接:每个 tool_use_start 重置 watchdogLastDataFlowTime,不累积
1823
- // - ask_user_question:question_request 事件切到 waiting_input,自动重置
1824
- // 恢复方式与 thinking 一致:abort + recoverTurnFailure(补发 tool_use_end)。
1825
- if (status === "tool_using") {
1826
- const toolUsingElapsed = now - (currentEntry.watchdogLastDataFlowTime || now);
1827
- if (toolUsingElapsed >= ACODE_TOOL_USING_SLOW_TIMEOUT_MS) {
1828
- // ★ 豁免 await_sub_agents:父 agent 阻塞等待子 agent 完成,
1829
- // 多个子 agent 串行执行总时长可能远超 5min(每个子 agent 自带看门狗)。
1830
- // 对其触发 abort 会中断所有正在跑的子 agent,造成不可恢复的混乱。
1831
- // 仅记录告警 + 重置计时,让等待继续。子 agent 卡死时由各自的看门狗处理。
1832
- const rawToolName = String(currentEntry.pendingToolEventData?.rawToolName ?? "");
1833
- if (rawToolName.endsWith("await_sub_agents")) {
1834
- console.warn(`[ACode] tool_using elapsed ${Math.round(toolUsingElapsed / 1000)}s but await_sub_agents ` +
1835
- `is running (session: ${sessionId}, tool: ${rawToolName}), skipping recovery ` +
1836
- `(sub-agents have their own watchdogs)`);
1837
- currentEntry.watchdogSubmitStartTime = now;
1838
- currentEntry.watchdogLastDataFlowTime = now;
1839
- return;
1840
- }
1841
- console.warn(`[ACode] CRITICAL: tool_using slow timeout triggered after ${Math.round(toolUsingElapsed / 1000)}s ` +
1842
- `(source: ${source}, session: ${sessionId}). ` +
1843
- `Tool execution likely hung. Forcing recovery via recoverTurnFailure.`);
1844
- this.executeWatchdogRecovery(sessionId, currentEntry, source, "tool_using_slow", toolUsingElapsed);
1845
- return;
1846
- }
1847
- // 未超时则重置 submit 计时(避免 tool_using 期间累积到下一次 thinking 误触发 submit_timeout)
1848
- currentEntry.watchdogSubmitStartTime = now;
1849
- return;
1850
- }
1851
- // 其他非 thinking 状态(compacting/error 等)不计时
1852
- if (status !== "thinking") {
1853
- return;
1854
- }
1855
- const submitElapsed = now - (currentEntry.watchdogSubmitStartTime || now);
1856
- const dataFlowElapsed = now - (currentEntry.watchdogLastDataFlowTime || now);
1857
- // 数据流超时(40s 无流式数据)— 仅 thinking 状态触发,比 submit 超时(120s)更短,优先触发
1858
- if (dataFlowElapsed >= ACODE_DATA_FLOW_STREAM_WATCHDOG_MS) {
1859
- console.warn(`[ACode] CRITICAL: Data flow watchdog triggered after ${Math.round(dataFlowElapsed / 1000)}s ` +
1860
- `(source: ${source}, session: ${sessionId}, status: ${status}). ` +
1861
- `No stream data received. Forcing recovery via recoverTurnFailure.`);
1862
- this.executeWatchdogRecovery(sessionId, currentEntry, source, "data_flow", dataFlowElapsed);
1863
- return;
1864
- }
1865
- // submit 超时(120s 未返回)
1866
- if (submitElapsed >= ACODE_SUBMIT_TIMEOUT_MS) {
1867
- console.warn(`[ACode] CRITICAL: submit timeout detected after ${Math.round(submitElapsed / 1000)}s ` +
1868
- `(source: ${source}, session: ${sessionId}, status: ${status}). ` +
1869
- `Forcing recovery to prevent permanent state stuck.`);
1870
- this.executeWatchdogRecovery(sessionId, currentEntry, source, "submit_timeout", submitElapsed);
1871
- return;
1872
- }
1873
- }, ACODE_WATCHDOG_POLL_INTERVAL_MS);
1874
- }
1875
- /**
1876
- * 执行看门狗恢复:abort runtime + recoverTurnFailure
1877
- */
1878
- executeWatchdogRecovery(sessionId, entry, source, reason, elapsedMs) {
1879
- // ★ 关键修复:先 abort runtime 打断挂起的 fetch / stream reader,
1880
- // 否则 submitQueue 永久阻塞,后续 submit 全部排队等待,agent 彻底死锁。
1881
- adapterDebugLog(`[ACode] Watchdog (${reason}): calling runtime.abort() after ${Math.round(elapsedMs / 1000)}s...`);
1882
- try {
1883
- void entry.runtime.abort();
1884
- adapterDebugLog(`[ACode] Watchdog (${reason}): runtime.abort() called`);
1885
- }
1886
- catch (abortError) {
1887
- console.warn(`[ACode] ⚠️ Watchdog (${reason}): runtime.abort() threw: ${abortError instanceof Error ? abortError.message : String(abortError)}`);
1888
- }
1889
- // 取消看门狗(recoverTurnFailure 会重新 arm idle loop)
1890
- this.cancelSubmitTimeoutDetection(entry);
1891
- this.recoverTurnFailure(sessionId, entry);
1892
- console.warn(`[ACode] ⚠️ Watchdog (${reason}) recovery complete. ` +
1893
- `If abort() could not cancel the underlying operation, submitQueue may still be deadlocked.`);
1894
- }
1895
- /**
1896
- * 取消看门狗
1897
- */
1898
- cancelSubmitTimeoutDetection(entry) {
1899
- if (entry.watchdogTimer) {
1900
- clearInterval(entry.watchdogTimer);
1901
- entry.watchdogTimer = undefined;
1902
- }
1903
- entry.watchdogSubmitStartTime = undefined;
1904
- entry.watchdogLastDataFlowTime = undefined;
1905
- }
1906
- /**
1907
- * 重置数据流计时基准:收到流式数据(assistant_delta/thinking_delta)时调用。
1908
- * 不再重启定时器,仅更新计时基准,下次轮询时从新基准计算。
1909
- */
1910
- resetDataFlowWatchdog(sessionId, entry) {
1911
- void sessionId;
1912
- entry.watchdogLastDataFlowTime = Date.now();
1913
- }
1914
- /**
1915
- * 启动数据流看门狗:由 ai_request 调用,初始化数据流计时基准。
1916
- * 实际的轮询由 startSubmitTimeoutDetection 的 interval 统一处理。
1917
- */
1918
- startDataFlowWatchdog(sessionId, entry) {
1919
- void sessionId;
1920
- entry.watchdogLastDataFlowTime = Date.now();
1921
- }
1922
- /**
1923
- * 取消数据流看门狗(已合并到 cancelSubmitTimeoutDetection,此方法保留兼容性)
1924
- */
1925
- cancelDataFlowWatchdog(entry) {
1926
- entry.watchdogLastDataFlowTime = Date.now();
1927
- }
1928
- /**
1929
- * 构建 ai_request 事件的 actionDetail,携带请求头和请求体,
1930
- * 供前端 DevTools 网络面板 Payload 页签展示。
1931
- * 格式与 terminal.ts 的 formatAiRequestDetail 保持一致:
1932
- * 模型/接口/方法等元信息 + "\n\n请求头:\n" + headers + "\n\n请求体:\n" + body
1933
- */
1934
- formatAiRequestActionDetail(data) {
1935
- const AI_REQUEST_HEADERS_MARKER = "\n\n请求头:\n";
1936
- const AI_REQUEST_BODY_MARKER = "\n\n请求体:\n";
1937
- // 请求体最大 60KB,确保 actionDetail 总长不超过 MySQL TEXT 列 64KB 上限
1938
- const MAX_REQUEST_BODY_CHARS = 60_000;
1939
- const model = typeof data.model === "string" ? data.model : "";
1940
- const endpoint = typeof data.endpoint === "string"
1941
- ? data.endpoint
1942
- : typeof data.requestUrl === "string"
1943
- ? data.requestUrl
1944
- : "";
1945
- const requestMethod = typeof data.requestMethod === "string" ? data.requestMethod : "";
1946
- const stream = typeof data.stream === "boolean" ? data.stream : undefined;
1947
- const messageCount = typeof data.messageCount === "number" ? data.messageCount : undefined;
1948
- const toolCount = typeof data.toolCount === "number" ? data.toolCount : undefined;
1949
- const lastUserMessagePreview = typeof data.lastUserMessagePreview === "string"
1950
- ? data.lastUserMessagePreview
1951
- : "";
1952
- const lines = [
1953
- model ? `模型: ${model}` : undefined,
1954
- endpoint ? `接口: ${endpoint}` : undefined,
1955
- requestMethod ? `方法: ${requestMethod}` : undefined,
1956
- stream !== undefined ? `流式: ${stream ? "是" : "否"}` : undefined,
1957
- messageCount !== undefined
1958
- ? `上下文消息: ${messageCount}`
1959
- : undefined,
1960
- toolCount !== undefined ? `可用工具: ${toolCount}` : undefined,
1961
- lastUserMessagePreview
1962
- ? `最后请求: ${lastUserMessagePreview}`
1963
- : undefined,
1964
- ].filter((line) => Boolean(line));
1965
- // 请求头(过滤敏感字段,避免 Authorization / API Key 泄露到前端)
1966
- const SENSITIVE_HEADER_PATTERN = /^(authorization|x-api-key|api-key|cookie|set-cookie|x-auth-token|token)$/i;
1967
- const requestHeaders = data.requestHeaders;
1968
- const headerLines = requestHeaders
1969
- ? Object.entries(requestHeaders)
1970
- .filter(([name]) => !SENSITIVE_HEADER_PATTERN.test(name))
1971
- .map(([name, value]) => `${name}: ${value}`)
1972
- .filter((line) => line.trim().length > 0)
1973
- : [];
1974
- const base = lines.join("\n") || "ACode 正在发送 AI 请求";
1975
- const headerBlock = headerLines.length > 0
1976
- ? `${AI_REQUEST_HEADERS_MARKER}${headerLines.join("\n")}`
1977
- : "";
1978
- // 请求体(截断保护)
1979
- const rawBody = typeof data.requestBody === "string" ? data.requestBody : "";
1980
- const body = rawBody.length > MAX_REQUEST_BODY_CHARS
1981
- ? `${rawBody.slice(0, MAX_REQUEST_BODY_CHARS)}\n...(请求体过长,已截断)`
1982
- : rawBody;
1983
- const bodyBlock = body ? `${AI_REQUEST_BODY_MARKER}${body}` : "";
1984
- return `${base}${headerBlock}${bodyBlock}`;
1985
- }
1986
- }
1
+ import{EventEmitter as q}from"node:events";import L from"node:path";import B from"node:fs/promises";import{pathToFileURL as W}from"node:url";import{v4 as O}from"uuid";import{getAcodeConfigFile as D,getAcodeSkillsDir as N,getRuntimeHomeDir as K}from"../config.js";import{getReleasedAcodeEntryPath as Q}from"../services/acode-package.js";import{getInstanceAiConfigDebugLogger as k}from"../utils/instance-ai-config-debug-logger.js";import{redactForLog as $}from"../services/instance-ai-config-service.js";import{subAgentManager as j}from"../services/sub-agent-manager-instance.js";import{resolveAcodeProviderConfig as G}from"./instance-ai-config-resolver.js";import{recordAgentFileChange as H}from"../services/workspaceTaskChangeWatcher.js";import{isMessagePollingIdleCommand as w,isPollMessageToolDescriptor as z}from"./acode-tool-names.js";import{IdlePollLoop as J}from"./idle-poll-loop.js";import{getCanonicalMcpDetail as E,getToolActionInfo as Y}from"./types.js";const X=process.env.ACODE_COMPACTION_DEBUG==="1";function l(...n){X&&console.log(...n)}const I=["Continue the AgentsWork message loop now.","You MUST call the exact MCP tool named aws-mcp__poll_message as your next action and wait for the next assigned message or task.","Do not answer in plain text before calling aws-mcp__poll_message.","After handling any received work, reply through the appropriate aws-mcp messaging tool, then continue listening again."].join(" "),V=5e3,Z=4e4,tt=5*6e4,et=250,ot=2e3,st=12e4,M=3e3,it="\u7CFB\u7EDF\u63D0\u9192\uFF1A\u5F53\u524D\u5DE5\u4F5C\u5DF2\u5B8C\u6210\u6216\u8FD0\u884C\u65F6\u5DF2\u8FDB\u5165\u7A7A\u95F2\u72B6\u6001\u3002\u8BF7\u7ACB\u5373\u8C03\u7528 poll_message \u5DE5\u5177\u963B\u585E\u7B49\u5F85\u65B0\u6D88\u606F\u548C\u6D41\u7A0B\u7EBF\u4EFB\u52A1\u63D0\u9192\uFF1B\u6536\u5230\u6D41\u7A0B\u4EFB\u52A1\u63D0\u9192\u540E\u8C03\u7528 my_task \u67E5\u770B\u5DF2\u7531\u540E\u53F0\u9759\u9ED8\u62A2\u5360\u7684\u4EFB\u52A1\u8BE6\u60C5\u5E76\u76F4\u63A5\u5904\u7406\uFF1B\u5B8C\u6210\u4EFB\u52A1\u540E\u8C03\u7528 submit_task_result \u63D0\u4EA4\u7ED3\u679C\uFF0C\u7136\u540E\u7EE7\u7EED poll_message\u3002 \u8C03\u7528 submit_task_result \u65F6\u8BF7\u4E00\u5E76\u4F20\u5165 changeLog\uFF08\u672C\u6B21\u5904\u7406\u7684\u6587\u4EF6/\u5185\u5BB9\u53D8\u66F4\u65E5\u5FD7\uFF0C\u5305\u542B operation \u4E0E path\uFF09\uFF0C\u7528\u4E8E AI \u56DE\u9000\u3002";function at(){return L.resolve(Q())}function nt(n){return W(L.resolve(n)).href}const R=128e3,P={"claude-4":{contextLimit:2e5,maxOutput:8192},"claude-sonnet-4":{contextLimit:2e5,maxOutput:8192},"claude-3-5-sonnet":{contextLimit:2e5,maxOutput:8192},"claude-3-opus":{contextLimit:2e5,maxOutput:8192},"claude-3-haiku":{contextLimit:2e5,maxOutput:8192},"claude-2":{contextLimit:1e5,maxOutput:4096},"gpt-4o":{contextLimit:128e3,maxOutput:16384},"gpt-4-turbo":{contextLimit:128e3,maxOutput:4096},"gpt-4":{contextLimit:8192,maxOutput:4096},"gpt-3.5-turbo":{contextLimit:16384,maxOutput:4096},o1:{contextLimit:2e5,maxOutput:1e5},"o3-mini":{contextLimit:2e5,maxOutput:1e5},"gemini-2.0-flash":{contextLimit:1048576,maxOutput:8192},"gemini-2.5-pro":{contextLimit:1048576,maxOutput:8192},"gemini-1.5-pro":{contextLimit:2097152,maxOutput:8192},"gemini-1.5-flash":{contextLimit:1048576,maxOutput:8192},deepseek:{contextLimit:64e3,maxOutput:8192},"deepseek-chat":{contextLimit:64e3,maxOutput:8192},"deepseek-reasoner":{contextLimit:64e3,maxOutput:8192},"qwen-max":{contextLimit:32e3,maxOutput:8192},"qwen-plus":{contextLimit:128e3,maxOutput:8192},"qwen-turbo":{contextLimit:1e6,maxOutput:8192},"glm-4":{contextLimit:128e3,maxOutput:4096},"yi-":{contextLimit:2e5,maxOutput:4096},"llama-3":{contextLimit:8192,maxOutput:4096},"llama-2":{contextLimit:4096,maxOutput:4096},mistral:{contextLimit:32e3,maxOutput:4096},mixtral:{contextLimit:32e3,maxOutput:4096},codestral:{contextLimit:256e3,maxOutput:8192},grok:{contextLimit:131072,maxOutput:4096},"nova-":{contextLimit:2e5,maxOutput:8192}};function rt(n){if(!n)return R;const t=n.toLowerCase().trim();if(P[t])return P[t].contextLimit;const e=Object.keys(P).filter(o=>t.startsWith(o)).sort((o,s)=>s.length-o.length);return e.length>0?P[e[0]].contextLimit:R}const U=4096;function ut(n){if(!n)return U;const t=n.toLowerCase().trim();if(P[t])return P[t].maxOutput;const e=Object.keys(P).filter(o=>t.startsWith(o)).sort((o,s)=>s.length-o.length);return e.length>0?P[e[0]].maxOutput:U}function lt(n){if(!n||typeof n!="object")return;const t=n,e=y(t.inputTokens,t.input_tokens,t.promptTokens,t.prompt_tokens),o=y(t.outputTokens,t.output_tokens,t.completionTokens,t.completion_tokens);if(!(e===0&&o===0))return{inputTokens:e,outputTokens:o}}function y(...n){for(const t of n){const e=Number(t);if(Number.isFinite(e)&&e>0)return e}return 0}function F(n){return y(n.estimatedContextTokens,n.contextTokens,n.context_tokens,n.contextUsedTokens,n.context_used_tokens)}function dt(n){return n==="stderr"||n==="mixed"?n:"stdout"}function ct(n){if(!n||typeof n!="object"||Array.isArray(n))return;const t={};for(const[e,o]of Object.entries(n))typeof o=="string"&&(t[e]=o);return Object.keys(t).length>0?t:void 0}function mt(n){return n.failed===!0}function v(n,t,e){if(!n)return e?`${t}/${e}`:t;const o=`${t}__`;return n.startsWith(o)?E(`${t}__${n.slice(o.length)}`):E(n.replace("/","__"))}function pt(n){return n==="read_file"||n==="edit_file"||n==="write_file"||n==="bash"}function gt(n){const t=ft(n);return t?[t.directMessages,t.groupMessages,t.messages].some(e=>Array.isArray(e)&&e.length>0):typeof n=="string"&&n.trim().length>0}function ft(n){if(n&&typeof n=="object"&&!Array.isArray(n))return n;if(!(typeof n!="string"||!n.trim()))try{const t=JSON.parse(n);return t&&typeof t=="object"&&!Array.isArray(t)?t:void 0}catch{return}}class $t extends q{constructor(){super(...arguments),this.providerId="acode",this.displayName="ACode",this.sessions=new Map}async startSession(t,e){const o=K(),s=e.mcpConfigPath||D(o),{resolved:r,configContextLimit:i}=await this.resolveAcodeProvider(e,s),a=r.model,u=i??rt(a),c=ut(a),p={sessionId:t,status:"starting",messages:[],createdAt:new Date().toISOString(),totalUsage:{inputTokens:0,outputTokens:0,contextLimit:u,maxOutput:c}};this.sessions.set(t,{adapterSession:p,config:e,runtime:await this.createRuntime(t,a,e,o),autoPollActive:!1}),this.emit("status-change",t,"starting");const d=this.sessions.get(t);if(!d)throw new Error("ACode session was not initialized");try{await d.runtime.start(),p.status="waiting_input",this.emit("status-change",t,"waiting_input",{usage:d.adapterSession.totalUsage}),e.initialPrompt?.trim()?this.startInitialPromptTurn(t,e.initialPrompt):this.armIdleLoopOrAutoPoll(t)}catch(m){p.status="error",this.emitRuntimeErrorEvent(t,m),this.emit("status-change",t,"error");const g=this.sessions.get(t);throw g?.capabilitiesFallbackTimer&&clearTimeout(g.capabilitiesFallbackTimer),this.sessions.delete(t),m}}async sendMessage(t,e){const o=Date.now();l("[ACode] \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"),l("[ACode] \u25B6\uFE0F sendMessage STARTED"),l(`[ACode] Session: ${t}`),l(`[ACode] Message: ${e.slice(0,100)}${e.length>100?"...":""}`),l("[ACode] \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550");const s=this.requireSession(t),r=s.adapterSession.status;l(`[ACode] Previous status: ${r}`),this.cancelScheduledAutoPoll(s),s.failedTurnRecoveryScheduled=!1,s.idlePollLoop?.stop();const i={id:O(),sessionId:t,role:"user",content:e,timestamp:new Date().toISOString()};s.adapterSession.messages.push(i),this.emit("conversation-message",t,i),l(`[ACode] Changing status: ${r} \u2192 thinking`),s.adapterSession.status="thinking",this.emit("status-change",t,"thinking"),this.startSubmitTimeoutDetection(t,s,"sendMessage");try{l("[ACode] \u25B6\uFE0F Calling runtime.submit()...");const a=Date.now();await s.runtime.submit(e);const u=Date.now()-a;this.cancelSubmitTimeoutDetection(s),l(`[ACode] \u2705 runtime.submit() completed in ${u}ms`),l(`[ACode] Current status after submit: ${s.adapterSession.status}`),l("[ACode] \u25B6\uFE0F Calling recoverResolvedTurnWithoutTerminalEvent()..."),this.recoverResolvedTurnWithoutTerminalEvent(t),l(`[ACode] Status after recovery: ${s.adapterSession.status}`);const c=Date.now()-o;l("[ACode] \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"),l(`[ACode] \u2705 sendMessage COMPLETED in ${c}ms`),l("[ACode] \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550")}catch(a){const u=Date.now()-o;console.error(`[ACode] \u274C sendMessage FAILED after ${u}ms:`,a instanceof Error?a.message:String(a)),this.cancelSubmitTimeoutDetection(s),l("[ACode] \u25B6\uFE0F Calling emitRuntimeError with recover=true..."),this.emitRuntimeError(t,a,{recover:!0}),l(`[ACode] Status after error recovery: ${s.adapterSession.status}`)}}async sendConfirmation(t,e){}async sendQuestionAnswer(t,e){const o=this.sessions.get(t);if(!o)return;const s=o.pendingQuestionToolCallId;if(!s){l(`[AcodeSdkAdapter] sendQuestionAnswer: no pending question for session ${t}`);return}o.pendingQuestionToolCallId=void 0,o.pendingQuestions=void 0,o.runtime.submitQuestionAnswer&&o.runtime.submitQuestionAnswer(s,e),this.startSubmitTimeoutDetection(t,o,"sendQuestionAnswer")}async abortCurrentTurn(t){await this.requireSession(t).runtime.abort()}async resetContext(t){const e=this.requireSession(t);if(this.cancelScheduledAutoPoll(e),e.failedTurnRecoveryScheduled=!1,e.autoPollActive=!1,e.autoPollObservedPollingTool=!1,e.autoPollObservedAssistantText=!1,e.autoPollObservedAnyTool=!1,e.stopAutoPollAfterCurrentTurn=!1,e.idlePollLoop?.stop(),!e.runtime.resetContext)throw new Error("ACode runtime does not support context reset");await e.runtime.resetContext(),e.adapterSession.messages=[];const o=e.adapterSession.totalUsage;if(e.adapterSession.totalUsage={inputTokens:0,outputTokens:0,contextLimit:o.contextLimit,maxOutput:o.maxOutput,inputLimit:o.inputLimit,compactionThreshold:o.compactionThreshold},e.adapterSession.status="waiting_input",this.emit("status-change",t,"waiting_input",{usage:e.adapterSession.totalUsage}),e.config.initialPrompt?.trim()){this.startInitialPromptTurn(t,e.config.initialPrompt);return}this.armIdleLoopOrAutoPoll(t)}async terminateSession(t){const e=this.sessions.get(t);e&&(this.cancelScheduledAutoPoll(e),e.failedTurnRecoveryScheduled=!1,e.idlePollLoop?.stop(),e.capabilitiesFallbackTimer&&(clearTimeout(e.capabilitiesFallbackTimer),e.capabilitiesFallbackTimer=void 0),await e.runtime.stop(),e.adapterSession.status="terminated",this.sessions.delete(t),this.emit("status-change",t,"terminated"))}getConversation(t){return this.sessions.get(t)?.adapterSession.messages??[]}hasSession(t){return this.sessions.has(t)}getProviderSessionId(t){return this.sessions.has(t)?t:void 0}getSessionStatus(t){return this.sessions.get(t)?.adapterSession.status}getCapabilities(t){return this.sessions.get(t)?.lastCombinedCapabilities}getPendingQuestion(t){const e=this.sessions.get(t);if(e?.pendingQuestionToolCallId)return{toolCallId:e.pendingQuestionToolCallId,questions:e.pendingQuestions??[]}}cleanup(){for(const t of this.sessions.keys())this.terminateSession(t)}setIdleCommands(t,e){const o=this.sessions.get(t);if(o){if(o.idleCommands=e,this.cancelScheduledAutoPoll(o),e.idleInputCommand&&w(e.idleInputCommand)){this.getIdlePollLoop(o).start();return}o.adapterSession.status==="waiting_input"&&(e.idleInputCommand||e.nonInputCommand)&&this.getIdlePollLoop(o).start()}}setRuntimeConstructorForTest(t){this.runtimeConstructor=t}async loadAcodeProviderSection(t){try{const e=await B.readFile(t,"utf-8"),o=JSON.parse(e),r=o.provider?.default,i=r?.options,a=y(o.contextLimit),u={},c=r?.models;if(c&&typeof c=="object"){for(const[p,d]of Object.entries(c))if(d&&typeof d=="object"){const m=y(d.contextLimit);m>0&&(u[p.trim()]=m)}}return{...r?.model?{model:r.model}:{},...i?.baseURL||i?.baseUrl?{baseURL:i?.baseURL||i?.baseUrl}:{},...i?.apiKey?{apiKey:i.apiKey}:{},...a>0?{contextLimit:a}:{},...Object.keys(u).length>0?{modelContextLimits:u}:{}}}catch(e){const o=e instanceof Error?e.message:String(e);return k().log({level:"warn",category:"load",message:`loadAcodeProviderSection failed: ${t}: ${o}`,source:"bridge"}),{}}}async resolveAcodeProvider(t,e){const o=typeof t.model=="string"&&t.model.trim()?t.model.trim():void 0,s=k();s.log({level:"info",category:"resolve",message:"resolveAcodeProvider \u8F93\u5165",source:"bridge",details:{configModel:t.model??null,configInstanceAiConfig:t.instanceAiConfig?$(t.instanceAiConfig):null,configPath:e}});const r={...t.instanceAiConfig?.model?{model:t.instanceAiConfig.model}:{},...t.instanceAiConfig?.baseURL?{baseURL:t.instanceAiConfig.baseURL}:{},...t.instanceAiConfig?.apiKey?{apiKey:t.instanceAiConfig.apiKey}:{},...t.instanceAiConfig?.effort?{effort:t.instanceAiConfig.effort}:{},...o?{model:o}:{}},i=await this.loadAcodeProviderSection(e);let a=i.contextLimit;if(!a&&i.modelContextLimits){const c=o||t.instanceAiConfig?.model||i.model;c&&i.modelContextLimits[c]&&(a=i.modelContextLimits[c])}const u=G(r,i,"acode-default");return s.log({level:"info",category:"resolve",message:"resolveAcodeProvider \u89E3\u6790\u7ED3\u679C",source:"bridge",details:{instanceConfig:$(r),globalConfigModel:i?.model??null,resolvedModel:u.model,resolvedBaseURL:u.baseURL??null,resolvedApiKey:u.apiKey?"[set]":null,configContextLimit:a,resolvedSource:u.source}}),{resolved:u,configPath:e,perSessionModel:o,configContextLimit:a}}async createRuntime(t,e,o,s){const r=await this.loadRuntimeConstructor(),i=o.mcpConfigPath||D(s),a=new r({sessionId:t,agentId:o.agentId,workingDirectory:o.workingDirectory,configPath:i,skillsDir:N(s),model:e,systemPrompt:typeof o.systemPrompt=="string"?o.systemPrompt:void 0,globalPrompt:typeof o.globalPrompt=="string"?o.globalPrompt:void 0,rolePrompt:typeof o.rolePrompt=="string"?o.rolePrompt:void 0,memory:typeof o.memory=="string"?o.memory:void 0,env:o.envOverrides,subAgentManager:j,...o.subAgentModel?{subAgentModel:o.subAgentModel}:{},...o.subAgentEffort?{subAgentEffort:o.subAgentEffort}:{},...o.instanceAiConfig?.effort?{effort:o.instanceAiConfig.effort}:{},onFileChange:c=>H(c)});return k().log({level:"info",category:"resolve",message:"ACode provider resolved",source:"bridge",sessionId:t,details:{resolvedModel:e,perInstanceModel:o.instanceAiConfig?.model||null,perInstanceBaseURL:o.instanceAiConfig?.baseURL?"[set]":null,perInstanceApiKey:o.instanceAiConfig?.apiKey?"[set]":null,configPath:i}}),a.on("event",c=>this.forwardAcodeEvent(c)),a}async loadRuntimeConstructor(){if(this.runtimeConstructor)return this.runtimeConstructor;const t=at(),e=await import(nt(t));if(!e.ACodeRuntime)throw new Error(`ACode runtime export not found: ${t}`);return this.runtimeConstructor=e.ACodeRuntime,this.runtimeConstructor}requireSession(t){const e=this.sessions.get(t);if(!e)throw new Error("ACode session not found");return e}startInitialPromptTurn(t,e){const o=this.sessions.get(t);o&&(o.adapterSession.status="thinking",this.emit("status-change",t,"thinking"),this.startSubmitTimeoutDetection(t,o,"startInitialPromptTurn"),o.runtime.submit(e).then(()=>{this.cancelSubmitTimeoutDetection(o),this.recoverResolvedTurnWithoutTerminalEvent(t)}).catch(s=>{const r=this.sessions.get(t);r&&(this.cancelSubmitTimeoutDetection(r),this.emitRuntimeError(t,s,{recover:!0}))}))}forwardAcodeEvent(t){const e=this.sessions.get(t.sessionId);if(e){if(t.type==="assistant_delta"){const o=typeof t.data.text=="string"?t.data.text:"";e.autoPollActive&&(e.autoPollObservedAssistantText=!0),e.adapterSession.messages.push({id:O(),sessionId:t.sessionId,role:"assistant",content:o,timestamp:t.timestamp}),this.resetDataFlowWatchdog(t.sessionId,e),this.emitProviderEvent(t,"text_delta",{text:o});return}if(t.type==="thinking_delta"){const o=typeof t.data.text=="string"?t.data.text:"";this.resetDataFlowWatchdog(t.sessionId,e),this.emitProviderEvent(t,"thinking",{text:o});return}if(t.type==="command_output"){this.emitProviderEvent(t,"command_output",{text:typeof t.data.text=="string"?t.data.text:"",stream:dt(t.data.stream),sequence:typeof t.data.sequence=="number"?t.data.sequence:void 0,cwd:typeof t.data.cwd=="string"?t.data.cwd:void 0,commandLineCount:typeof t.data.commandLineCount=="number"?t.data.commandLineCount:void 0,commandFilePath:typeof t.data.commandFilePath=="string"?t.data.commandFilePath:void 0});return}if(t.type==="ai_request"){const o=F(t.data);let s;if(o>0){const a=e.adapterSession.totalUsage.inputTokens;a>0?e.adapterSession.totalUsage.inputTokens=Math.min(o,Math.floor(a*1.5)):e.adapterSession.totalUsage.inputTokens=o,s={...e.adapterSession.totalUsage}}e.adapterSession.status==="compacting"&&(e.adapterSession.status="thinking"),e.adapterSession.status==="waiting_input"&&(e.adapterSession.status="thinking");const i={actionType:"ai_request",actionLabel:"AI\u8BF7\u6C42",actionDetail:this.formatAiRequestActionDetail(t.data)};s&&(i.usage=s),this.emit("status-change",t.sessionId,e.adapterSession.status,i),this.startDataFlowWatchdog(t.sessionId,e),this.emitProviderEvent(t,"ai_request",{model:typeof t.data.model=="string"?t.data.model:void 0,endpoint:typeof t.data.endpoint=="string"?t.data.endpoint:void 0,requestMethod:typeof t.data.requestMethod=="string"?t.data.requestMethod:void 0,requestUrl:typeof t.data.requestUrl=="string"?t.data.requestUrl:void 0,requestHeaders:ct(t.data.requestHeaders),stream:typeof t.data.stream=="boolean"?t.data.stream:void 0,messageCount:typeof t.data.messageCount=="number"?t.data.messageCount:void 0,toolCount:typeof t.data.toolCount=="number"?t.data.toolCount:void 0,lastUserMessagePreview:typeof t.data.lastUserMessagePreview=="string"?t.data.lastUserMessagePreview:void 0,requestBody:typeof t.data.requestBody=="string"?t.data.requestBody:void 0});return}if(t.type==="turn_complete"){const o=lt(t.data.usage);if(o){const r=e.adapterSession.totalUsage.inputTokens;r>0&&o.inputTokens<Math.floor(r*.2)||(e.adapterSession.totalUsage.inputTokens=o.inputTokens,e.adapterSession.totalUsage.outputTokens=o.outputTokens)}const s=e.adapterSession.status;if(l(`[ACode] turn_complete event received (session: ${t.sessionId}), previous status: ${s}, failed: ${t.data.failed===!0}, aborted: ${t.data.aborted===!0}`),mt(t.data)){l(`[ACode] Recovering from failed turn_complete (session: ${t.sessionId})`),this.cancelDataFlowWatchdog(e),this.recoverTurnFailure(t.sessionId,e);return}if(t.data.aborted===!0&&e.autoPollActive){l(`[ACode] Recovering from aborted turn_complete (session: ${t.sessionId}), using R2 2s backoff instead of immediate rearm`),this.cancelDataFlowWatchdog(e),this.recoverTurnFailure(t.sessionId,e);return}if(this.cancelDataFlowWatchdog(e),this.emitProviderEvent(t,"turn_complete",{usage:e.adapterSession.totalUsage,responseBody:typeof t.data.responseBody=="string"?t.data.responseBody:void 0,finishReason:typeof t.data.finishReason=="string"?t.data.finishReason:void 0}),e.adapterSession.status="waiting_input",this.emit("status-change",t.sessionId,"waiting_input",{usage:e.adapterSession.totalUsage}),l(`[ACode] Status changed to waiting_input after turn_complete (session: ${t.sessionId})`),e.autoPollActive&&e.autoPollObservedAssistantText&&!e.autoPollObservedPollingTool&&!e.autoPollObservedAnyTool){e.stopAutoPollAfterCurrentTurn=!0,console.warn(`[${this.displayName}] Auto-poll turn produced assistant text without calling any tool; stopping auto-poll rearm for session ${t.sessionId}`);return}this.armIdleLoopOrAutoPoll(t.sessionId);return}if(t.type==="session_complete"){this.cancelDataFlowWatchdog(e),this.cancelScheduledAutoPoll(e),e.failedTurnRecoveryScheduled=!1,e.idlePollLoop?.stop(),e.adapterSession.status="completed",this.emitProviderEvent(t,"session_complete",{exitCode:Number(t.data.exitCode)||0}),this.emit("status-change",t.sessionId,"completed");return}if(t.type==="question_request"){this.cancelDataFlowWatchdog(e),this.cancelSubmitTimeoutDetection(e);const o=typeof t.data.toolCallId=="string"?t.data.toolCallId:"",s=Array.isArray(t.data.questions)?t.data.questions:[];o&&(e.pendingQuestionToolCallId=o,e.pendingQuestions=s),this.emit("ask-user-question",{sessionId:t.sessionId,toolCallId:o||void 0,questions:s});return}if(t.type==="error"){if(t.data.nonFatal){this.resetDataFlowWatchdog(t.sessionId,e),this.emitProviderEvent(t,"error",{text:String(t.data.message||"ACode runtime error")});return}this.cancelDataFlowWatchdog(e),e.adapterSession.status="error",this.emitProviderEvent(t,"error",{text:String(t.data.message||"ACode runtime error")}),this.emit("status-change",t.sessionId,"error"),e.autoPollActive||this.recoverTurnFailure(t.sessionId,e);return}if(t.type==="mcp_status"){if(!t.data.action&&Array.isArray(t.data.tools)){const h=t.data.tools;h.some(f=>z(f))||console.warn(`[${this.displayName}] ACode runtime is not exposing a poll_message tool. The LLM cannot call poll_message. Please configure an MCP server that provides poll_message (e.g. aws-mcp) in the dashboard panel for this runtime.`);const T=Array.isArray(t.data.servers)?t.data.servers:[],_=h.map(f=>({name:String(f.name??""),source:String(f.source??""),description:String(f.description??"")})).filter(f=>f.name),S={kind:"mcp",mcpServers:T,mcpTools:_};e.cachedMcpCapabilities=S,e.lastMcpCapabilities=S,e.lastCombinedCapabilities={kind:"combined",mcpServers:T,mcpTools:_,skills:e.lastCombinedCapabilities?.skills??[],skillsDir:e.lastCombinedCapabilities?.skillsDir,skillGroups:e.lastCombinedCapabilities?.skillGroups??[]},l(`[cap-diag] MCP capabilities cached: ${T.length} servers, ${_.length} tools`,{sessionId:t.sessionId,servers:T,toolNames:_.map(f=>f.name)}),e.capabilitiesFallbackTimer&&clearTimeout(e.capabilitiesFallbackTimer),e.capabilitiesFallbackTimer=setTimeout(()=>{const f=e.cachedMcpCapabilities;if(!f)return;const b={...f,skills:[],skillGroups:[]};this.emitProviderEvent(t,"runtime_capabilities",{capabilities:b,actionType:"runtime_capabilities",actionLabel:"\u8FD0\u884C\u65F6\u80FD\u529B",actionDetail:JSON.stringify(b)}),l(`[cap-diag] MCP capabilities emitted via fallback (skill_status not received within ${M}ms)`,{sessionId:t.sessionId,mcpServers:f.mcpServers,mcpToolsCount:f.mcpTools?.length??0}),e.cachedMcpCapabilities=void 0,e.capabilitiesFallbackTimer=void 0},M);return}const o=String(t.data.action??"");l(`[ACode] \u{1F4E1} mcp_status event: action=${o}, server=${typeof t.data.server=="string"?t.data.server:"?"}, tool=${typeof t.data.tool=="string"?t.data.tool:"?"}, toolUseId=${typeof t.data.toolUseId=="string"?t.data.toolUseId.slice(0,16)+"\u2026":"none"}, failed=${t.data.failed===!0}, session=${t.sessionId}, status=${e.adapterSession.status}`);const s=typeof t.data.server=="string"?t.data.server:"mcp",r=typeof t.data.tool=="string"?t.data.tool:"",i=s&&r?`${s}__${r}`:r||s,a=t.data.arguments&&typeof t.data.arguments=="object"&&!Array.isArray(t.data.arguments)?t.data.arguments:void 0,u=Y(i,a);e.autoPollActive&&(e.autoPollObservedAnyTool=!0,u.actionType==="idle"&&(e.autoPollObservedPollingTool=!0));const c=pt(u.actionType)?u.actionDetail||v(void 0,s,r):v(u.actionDetail,s,r),p=typeof t.data.toolUseId=="string"?t.data.toolUseId:void 0,d=a?JSON.stringify(a,null,2):void 0,m={toolName:c,rawToolName:i,actionType:u.actionType,actionLabel:u.actionLabel,actionDetail:c,...d?{actionInput:d}:{},...a?{toolInput:a}:{},...p?{toolUseId:p,actionId:p}:{},metadata:{mcpServer:s}};if(t.data.action==="tool_result"){this.emitProviderEvent(t,"tool_use_end",{...m,toolResult:typeof t.data.result=="string"?t.data.result:JSON.stringify(t.data.result??"")});const h=u.actionType==="idle"&&!gt(t.data.result)?"waiting_input":"thinking";e.adapterSession.status=h,this.cancelDataFlowWatchdog(e),e.pendingToolEventData=void 0;const C=u.actionType==="idle";this.emit("status-change",t.sessionId,h,C||h==="thinking"?void 0:m);return}const g=u.actionType==="idle"?"waiting_input":"tool_using";e.adapterSession.status=g,g==="tool_using"&&(this.resetDataFlowWatchdog(t.sessionId,e),e.pendingToolEventData={...m}),this.emitProviderEvent(t,"tool_use_start",m),this.emit("status-change",t.sessionId,g,m);return}if(t.type==="skill_status"){const s=(Array.isArray(t.data.skills)?t.data.skills:[]).map(d=>({name:String(d.name??""),description:typeof d.description=="string"?d.description:void 0,directory:typeof d.directory=="string"?d.directory:void 0})).filter(d=>d.name),r=typeof t.data.skillsDir=="string"?t.data.skillsDir:void 0,a=(Array.isArray(t.data.skillGroups)?t.data.skillGroups:[]).map(d=>{const m=Array.isArray(d.skills)?d.skills:[];return{dir:typeof d.dir=="string"?d.dir:"",exists:d.exists===!0,skills:m.map(g=>({name:String(g.name??""),description:typeof g.description=="string"?g.description:void 0,directory:typeof g.directory=="string"?g.directory:void 0})).filter(g=>g.name)}}).filter(d=>d.dir),u=e.cachedMcpCapabilities??e.lastMcpCapabilities,c=e.cachedMcpCapabilities!==void 0,p={kind:"combined",mcpServers:u?.mcpServers??[],mcpTools:u?.mcpTools??[],skills:s,skillsDir:r,skillGroups:a};this.emitProviderEvent(t,"runtime_capabilities",{capabilities:p,actionType:"runtime_capabilities",actionLabel:"\u8FD0\u884C\u65F6\u80FD\u529B",actionDetail:JSON.stringify(p)}),e.lastCombinedCapabilities=p,l(`[cap-diag] Combined capabilities emitted ${c?"(startup merge)":"(runtime refresh)"}: ${(u?.mcpTools??[]).length} MCP tools, ${s.length} skills`,{sessionId:t.sessionId,mcpServers:u?.mcpServers??[],skillsDir:r}),e.capabilitiesFallbackTimer&&(clearTimeout(e.capabilitiesFallbackTimer),e.capabilitiesFallbackTimer=void 0),e.cachedMcpCapabilities=void 0;return}if(t.type==="session_started"){const o=F(t.data),s=y(t.data.contextLimit)||void 0,r=y(t.data.maxOutput)||void 0,i=y(t.data.inputLimit)||void 0,a=y(t.data.compactionThreshold)||void 0;let u=!1;o>0&&(e.adapterSession.totalUsage.inputTokens=o,e.adapterSession.totalUsage.outputTokens=0,u=!0),s!==void 0&&(e.adapterSession.totalUsage.contextLimit=s,u=!0),r!==void 0&&(e.adapterSession.totalUsage.maxOutput=r,u=!0),i!==void 0&&(e.adapterSession.totalUsage.inputLimit=i,u=!0),a!==void 0&&(e.adapterSession.totalUsage.compactionThreshold=a,u=!0),u&&this.emit("status-change",t.sessionId,e.adapterSession.status,{usage:e.adapterSession.totalUsage});return}if(t.type==="exit_plan_mode"){e.adapterSession.status="waiting_input";const o=typeof t.data.plan=="string"?t.data.plan:void 0;this.emitProviderEvent(t,"exit_plan_mode",{toolInput:t.data.toolInput&&typeof t.data.toolInput=="object"?t.data.toolInput:void 0,text:o||"\u9000\u51FA\u8BA1\u5212\u6A21\u5F0F"}),this.emit("status-change",t.sessionId,"waiting_input",{actionLabel:"\u5BA1\u6279\u8BA1\u5212",actionDetail:o||"\u7B49\u5F85\u7528\u6237\u5BA1\u6279\u9000\u51FA\u8BA1\u5212\u6A21\u5F0F"});return}if(t.type==="context_compaction"){const o=t.data,s=o.action,r=s==="start",i=s==="end",a=s==="phase",u=o.parentId,c=o.compactionId;let p,d;if(r){const g=String(o.sizeKB??o.sizeBeforeKB??"?"),h=String(o.messageCount??o.messageCountBefore??"?"),C=o.estimatedTokens,T=o.actualInputTokens?`input=${o.actualInputTokens}`:"",_=o.actualOutputTokens?`output=${o.actualOutputTokens}`:"",S=o.trigger?`, \u89E6\u53D1: ${o.trigger}`:"";p="\u4E0A\u4E0B\u6587\u538B\u7F29\u5F00\u59CB",C!==void 0?d=`\u538B\u7F29\u524D: ~${C} tokens, \u6D88\u606F: ${h}\u6761`+(T?`, \u5B9E\u9645 tokens: ${T} ${_}`:"")+S:d=`\u538B\u7F29\u524D: ${g}KB, \u6D88\u606F: ${h}\u6761`+(T?`, \u5B9E\u9645 tokens: ${T} ${_}`:"")+S}else if(a)p=String(o.phaseLabel??"\u538B\u7F29\u9636\u6BB5"),d=String(o.phaseDetail??"");else{const g=o.tokensBefore,h=o.tokensAfter,C=o.freedTokens,T=String(o.sizeBeforeKB??"?"),_=String(o.sizeAfterKB??"?"),S=String(o.freedKB??"?"),f=String(o.strategy??"?"),b=Number(o.prunedToolMessages??0),A=Number(o.summarizedMessages??0);p="\u538B\u7F29\u5B8C\u6210",g!==void 0&&h!==void 0?d=`\u91CA\u653E: ${C??g-h} tokens, ${g} tokens \u2192 ${h} tokens, \u7B56\u7565: ${f}`+(b?`, \u88C1\u526A\u5DE5\u5177: ${b}\u6761`:"")+(A?`, \u6458\u8981\u5316: ${A}\u6761`:""):d=`\u91CA\u653E: ${S}KB, ${T}KB \u2192 ${_}KB, \u7B56\u7565: ${f}`+(b?`, \u88C1\u526A\u5DE5\u5177: ${b}\u6761`:"")+(A?`, \u6458\u8981\u5316: ${A}\u6761`:"")}r&&(e.adapterSession.status="compacting");const m={actionType:"context_compaction",actionLabel:p,actionDetail:d};if(r&&c)m.actionId=c;else if((a||i)&&c)if(m.parentId=c,a){const g=String(o.phase??"unknown");m.actionId=`${c}-phase-${g}`}else m.actionId=`${c}-end`;a&&u&&!m.parentId&&(m.parentId=u),i&&(e.adapterSession.totalUsage.inputTokens=0,e.adapterSession.totalUsage.outputTokens=0,m.usage={...e.adapterSession.totalUsage}),this.emitProviderEvent(t,"context_compaction",m),this.emit("status-change",t.sessionId,"compacting",m);return}this.emitProviderEvent(t,"thinking",{text:JSON.stringify(t.data)})}}emitProviderEvent(t,e,o){this.emit("event",{type:e,sessionId:t.sessionId,timestamp:t.timestamp,data:o})}armIdleLoopOrAutoPoll(t){const e=this.sessions.get(t);if(!(!e||this.isTerminalStatus(e.adapterSession.status))){if(e.idleCommands?.idleInputCommand){this.getIdlePollLoop(e).start();return}this.scheduleAutoPoll(t)}}getIdlePollLoop(t){if(t.idlePollLoop)return t.idlePollLoop;const e=t.adapterSession.sessionId;return t.idlePollLoop=new J({getStatus:()=>this.sessions.get(e)?.adapterSession.status,getCommand:()=>this.sessions.get(e)?.idleCommands?.idleInputCommand,isPollingCommand:o=>w(o),dispatchPollingPrompt:()=>this.submitAutoPollPrompt(e,it,{keepWaitingStatus:!0}),dispatchRegularIdleCommand:o=>this.submitAutoPollPrompt(e,o)}),t.idlePollLoop}submitAutoPollPrompt(t,e,o){const s=this.sessions.get(t);if(!s||this.isTerminalStatus(s.adapterSession.status))return;const r=(s.autoPollToken??0)+1;s.autoPollToken=r,s.autoPollActive=!0,s.autoPollObservedPollingTool=!1,s.autoPollObservedAssistantText=!1,s.autoPollObservedAnyTool=!1,s.stopAutoPollAfterCurrentTurn=!1,o?.keepWaitingStatus||(s.adapterSession.status="thinking",this.emit("status-change",t,"thinking")),this.startSubmitTimeoutDetection(t,s,"submitAutoPollPrompt"),s.runtime.submit(e).catch(i=>{this.emitRuntimeError(t,i)}).finally(()=>{const i=this.sessions.get(t);if(i){if(i.autoPollToken!==r){l(`[ACode] submitAutoPollPrompt.finally: token mismatch (my=${r}, current=${i.autoPollToken}), skipping cleanup \u2014 newer turn has taken over`);return}if(this.cancelSubmitTimeoutDetection(i),i.autoPollActive=!1,i.failedTurnRecoveryScheduled||i.autoPollTimer){i.failedTurnRecoveryScheduled=!1;return}if(i.stopAutoPollAfterCurrentTurn){i.stopAutoPollAfterCurrentTurn=!1,i.autoPollObservedPollingTool=!1,i.autoPollObservedAssistantText=!1,i.autoPollObservedAnyTool=!1;return}if(i.adapterSession.status==="error"){this.recoverTurnFailure(t,i);return}if(i.adapterSession.status==="thinking"||i.adapterSession.status==="tool_using"){this.recoverResolvedTurnWithoutTerminalEvent(t);return}i.adapterSession.status==="waiting_input"&&this.armIdleLoopOrAutoPoll(t)}})}scheduleAutoPoll(t,e=et){const o=this.sessions.get(t);!o||o.autoPollActive||o.autoPollTimer||this.isTerminalStatus(o.adapterSession.status)||(o.autoPollTimer=setTimeout(()=>{const s=this.sessions.get(t);!s||s.autoPollActive||this.isTerminalStatus(s.adapterSession.status)||(s.autoPollTimer=void 0,this.submitAutoPollPrompt(t,I,{keepWaitingStatus:!0}))},e))}cancelScheduledAutoPoll(t){t.autoPollTimer&&(clearTimeout(t.autoPollTimer),t.autoPollTimer=void 0)}isTerminalStatus(t){return t==="completed"||t==="terminated"}emitRuntimeError(t,e,o){const s=this.sessions.get(t);s&&(s.adapterSession.status="error",this.emitRuntimeErrorEvent(t,e),this.emit("status-change",t,"error"),o?.recover&&this.recoverTurnFailure(t,s))}emitRuntimeErrorEvent(t,e){const o=new Date().toISOString();this.emit("event",{type:"error",sessionId:t,timestamp:o,data:{text:e instanceof Error?e.message:String(e)}})}estimateOutputTokensFromMessages(t){const e=t.filter(o=>o.role==="assistant").map(o=>o.content).join("");return Math.max(0,Math.round(e.length/4))}recoverTurnFailure(t,e){if(this.isTerminalStatus(e.adapterSession.status))return;if(e.adapterSession.status==="tool_using"&&e.pendingToolEventData){const a=e.pendingToolEventData;l(`[ACode] recoverTurnFailure: \u8865\u53D1 tool_use_end (tool: ${a.toolName??"unknown"}) \u5173\u95ED\u65F6\u95F4\u7EBF\u6761\u76EE\uFF0C\u907F\u514D\u6C38\u4E45\u5361\u5728\u5DE5\u5177\u6267\u884C\u72B6\u6001`),this.emit("event",{type:"tool_use_end",sessionId:t,timestamp:new Date().toISOString(),data:{...a,toolResult:"[aborted] \u5DE5\u5177\u6267\u884C\u88AB\u6570\u636E\u6D41\u770B\u95E8\u72D7\u4E2D\u65AD"}}),e.pendingToolEventData=void 0}const o=this.estimateOutputTokensFromMessages(e.adapterSession.messages);if(o>0){const a=e.adapterSession.totalUsage.outputTokens;e.adapterSession.totalUsage.outputTokens=o,l(`[ACode] recoverTurnFailure: \u4F30\u7B97\u8986\u76D6 output tokens ${a} -> ${o} (totalUsage now: input=${e.adapterSession.totalUsage.inputTokens}, output=${e.adapterSession.totalUsage.outputTokens})`)}const s=e.idleCommands?.idleInputCommand,i=!!(s&&w(s))?{actionType:"idle",actionLabel:"\u76D1\u542C\u6D88\u606F",actionDetail:"aws-mcp/poll_message"}:void 0;if(e.autoPollTimer||e.failedTurnRecoveryScheduled){e.adapterSession.status!=="waiting_input"&&(e.adapterSession.status="waiting_input",this.emit("status-change",t,"waiting_input",{usage:e.adapterSession.totalUsage,...i}));return}this.cancelScheduledAutoPoll(e),e.adapterSession.status="waiting_input",this.emit("status-change",t,"waiting_input",{usage:e.adapterSession.totalUsage,...i}),e.failedTurnRecoveryScheduled=!0,e.autoPollTimer=setTimeout(()=>{const a=this.sessions.get(t);if(!a||a.autoPollActive||this.isTerminalStatus(a.adapterSession.status)){a&&(a.failedTurnRecoveryScheduled=!1);return}a.autoPollTimer=void 0,a.failedTurnRecoveryScheduled=!1,a.idleCommands?.idleInputCommand?this.getIdlePollLoop(a).start():this.submitAutoPollPrompt(t,I,{keepWaitingStatus:!0})},ot)}recoverResolvedTurnWithoutTerminalEvent(t){l(`[ACode] \u25B6\uFE0F recoverResolvedTurnWithoutTerminalEvent called for session ${t}`);const e=this.sessions.get(t);if(!e||this.isTerminalStatus(e.adapterSession.status)){l(`[ACode] Skipping recovery: entry=${!!e}, isTerminal=${e?this.isTerminalStatus(e.adapterSession.status):"N/A"}`);return}if(l(`[ACode] Current status: ${e.adapterSession.status}`),e.adapterSession.status!=="thinking"&&e.adapterSession.status!=="tool_using"){l(`[ACode] Skipping recovery: status is ${e.adapterSession.status} (not thinking/tool_using)`);return}console.warn(`[ACode] \u26A0\uFE0F Detected stuck state: ${e.adapterSession.status} after submit resolved! Recovering to waiting_input. If runtime.submit() is still pending (e.g., built-in tool execution not interruptible by abort()), the submitQueue is deadlocked and subsequent auto-poll/user submits will never execute.`),l("[ACode] Recovering to waiting_input..."),e.adapterSession.status="waiting_input";const o=e.idleCommands?.idleInputCommand,r=!!(o&&w(o))?{actionType:"idle",actionLabel:"\u76D1\u542C\u6D88\u606F",actionDetail:"aws-mcp/poll_message"}:void 0;this.emit("status-change",t,"waiting_input",r),l(`[ACode] \u2705 Recovery complete, status now: ${e.adapterSession.status}`),l("[ACode] \u25B6\uFE0F Arming idle loop or auto-poll..."),this.armIdleLoopOrAutoPoll(t),l("[ACode] \u2705 Idle loop armed, recovery process complete")}startSubmitTimeoutDetection(t,e,o){this.cancelSubmitTimeoutDetection(e),e.watchdogSubmitStartTime=Date.now(),e.watchdogLastDataFlowTime=Date.now(),e.watchdogTimer=setInterval(()=>{const s=this.sessions.get(t);if(!s){this.cancelSubmitTimeoutDetection(e);return}const r=s.adapterSession.status,i=Date.now();if(r==="waiting_input"){s.watchdogSubmitStartTime=i,s.watchdogLastDataFlowTime=i;return}if(r==="tool_using"){const c=i-(s.watchdogLastDataFlowTime||i);if(c>=tt){const p=String(s.pendingToolEventData?.rawToolName??"");if(p.endsWith("await_sub_agents")){console.warn(`[ACode] tool_using elapsed ${Math.round(c/1e3)}s but await_sub_agents is running (session: ${t}, tool: ${p}), skipping recovery (sub-agents have their own watchdogs)`),s.watchdogSubmitStartTime=i,s.watchdogLastDataFlowTime=i;return}console.warn(`[ACode] CRITICAL: tool_using slow timeout triggered after ${Math.round(c/1e3)}s (source: ${o}, session: ${t}). Tool execution likely hung. Forcing recovery via recoverTurnFailure.`),this.executeWatchdogRecovery(t,s,o,"tool_using_slow",c);return}s.watchdogSubmitStartTime=i;return}if(r!=="thinking")return;const a=i-(s.watchdogSubmitStartTime||i),u=i-(s.watchdogLastDataFlowTime||i);if(u>=Z){console.warn(`[ACode] CRITICAL: Data flow watchdog triggered after ${Math.round(u/1e3)}s (source: ${o}, session: ${t}, status: ${r}). No stream data received. Forcing recovery via recoverTurnFailure.`),this.executeWatchdogRecovery(t,s,o,"data_flow",u);return}if(a>=st){console.warn(`[ACode] CRITICAL: submit timeout detected after ${Math.round(a/1e3)}s (source: ${o}, session: ${t}, status: ${r}). Forcing recovery to prevent permanent state stuck.`),this.executeWatchdogRecovery(t,s,o,"submit_timeout",a);return}},V)}executeWatchdogRecovery(t,e,o,s,r){l(`[ACode] Watchdog (${s}): calling runtime.abort() after ${Math.round(r/1e3)}s...`);try{e.runtime.abort(),l(`[ACode] Watchdog (${s}): runtime.abort() called`)}catch(i){console.warn(`[ACode] \u26A0\uFE0F Watchdog (${s}): runtime.abort() threw: ${i instanceof Error?i.message:String(i)}`)}this.cancelSubmitTimeoutDetection(e),this.recoverTurnFailure(t,e),console.warn(`[ACode] \u26A0\uFE0F Watchdog (${s}) recovery complete. If abort() could not cancel the underlying operation, submitQueue may still be deadlocked.`)}cancelSubmitTimeoutDetection(t){t.watchdogTimer&&(clearInterval(t.watchdogTimer),t.watchdogTimer=void 0),t.watchdogSubmitStartTime=void 0,t.watchdogLastDataFlowTime=void 0}resetDataFlowWatchdog(t,e){e.watchdogLastDataFlowTime=Date.now()}startDataFlowWatchdog(t,e){e.watchdogLastDataFlowTime=Date.now()}cancelDataFlowWatchdog(t){t.watchdogLastDataFlowTime=Date.now()}formatAiRequestActionDetail(t){const e=`
2
+
3
+ \u8BF7\u6C42\u5934:
4
+ `,o=`
5
+
6
+ \u8BF7\u6C42\u4F53:
7
+ `,r=typeof t.model=="string"?t.model:"",i=typeof t.endpoint=="string"?t.endpoint:typeof t.requestUrl=="string"?t.requestUrl:"",a=typeof t.requestMethod=="string"?t.requestMethod:"",u=typeof t.stream=="boolean"?t.stream:void 0,c=typeof t.messageCount=="number"?t.messageCount:void 0,p=typeof t.toolCount=="number"?t.toolCount:void 0,d=typeof t.lastUserMessagePreview=="string"?t.lastUserMessagePreview:"",m=[r?`\u6A21\u578B: ${r}`:void 0,i?`\u63A5\u53E3: ${i}`:void 0,a?`\u65B9\u6CD5: ${a}`:void 0,u!==void 0?`\u6D41\u5F0F: ${u?"\u662F":"\u5426"}`:void 0,c!==void 0?`\u4E0A\u4E0B\u6587\u6D88\u606F: ${c}`:void 0,p!==void 0?`\u53EF\u7528\u5DE5\u5177: ${p}`:void 0,d?`\u6700\u540E\u8BF7\u6C42: ${d}`:void 0].filter(A=>!!A),g=/^(authorization|x-api-key|api-key|cookie|set-cookie|x-auth-token|token)$/i,h=t.requestHeaders,C=h?Object.entries(h).filter(([A])=>!g.test(A)).map(([A,x])=>`${A}: ${x}`).filter(A=>A.trim().length>0):[],T=m.join(`
8
+ `)||"ACode \u6B63\u5728\u53D1\u9001 AI \u8BF7\u6C42",_=C.length>0?`${e}${C.join(`
9
+ `)}`:"",S=typeof t.requestBody=="string"?t.requestBody:"",f=S.length>6e4?`${S.slice(0,6e4)}
10
+ ...(\u8BF7\u6C42\u4F53\u8FC7\u957F\uFF0C\u5DF2\u622A\u65AD)`:S,b=f?`${o}${f}`:"";return`${T}${_}${b}`}}export{$t as AcodeSdkAdapter,mt as isFailedAcodeTurnComplete,nt as toAcodeEsmImportSpecifier};
11
+