aws-runtime-bridge 1.9.33 → 1.9.40
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapter/AcodeSdkAdapter.d.ts +35 -1
- package/dist/adapter/AcodeSdkAdapter.d.ts.map +1 -1
- package/dist/adapter/AcodeSdkAdapter.js +417 -40
- package/dist/adapter/ClaudeSdkAdapter.d.ts +6 -0
- package/dist/adapter/ClaudeSdkAdapter.d.ts.map +1 -1
- package/dist/adapter/ClaudeSdkAdapter.js +23 -1
- package/dist/adapter/idle-poll-loop.d.ts +2 -1
- package/dist/adapter/idle-poll-loop.d.ts.map +1 -1
- package/dist/adapter/types.d.ts +65 -3
- package/dist/adapter/types.d.ts.map +1 -1
- package/dist/adapter/types.js +5 -4
- package/dist/index.js +4 -0
- package/dist/routes/acode-todos.d.ts +11 -0
- package/dist/routes/acode-todos.d.ts.map +1 -0
- package/dist/routes/acode-todos.js +75 -0
- package/dist/routes/memory.d.ts +1 -1
- package/dist/routes/memory.d.ts.map +1 -1
- package/dist/routes/memory.js +3 -3
- package/dist/routes/pty.d.ts.map +1 -1
- package/dist/routes/pty.js +9 -0
- package/dist/routes/sub-agents.d.ts +14 -0
- package/dist/routes/sub-agents.d.ts.map +1 -0
- package/dist/routes/sub-agents.js +253 -0
- package/dist/routes/terminal.d.ts +9 -1
- package/dist/routes/terminal.d.ts.map +1 -1
- package/dist/routes/terminal.js +207 -22
- package/dist/services/agent-event-store.d.ts +1 -0
- package/dist/services/agent-event-store.d.ts.map +1 -1
- package/dist/services/event-delta-pusher.js +4 -4
- package/dist/services/session-output.d.ts +3 -0
- package/dist/services/session-output.d.ts.map +1 -1
- package/dist/services/session-output.js +38 -2
- package/dist/services/sub-agent-manager-instance.d.ts +4 -0
- package/dist/services/sub-agent-manager-instance.d.ts.map +1 -0
- package/dist/services/sub-agent-manager-instance.js +12 -0
- package/dist/services/workspace-files.d.ts +1 -0
- package/dist/services/workspace-files.d.ts.map +1 -1
- package/dist/services/workspace-files.js +57 -1
- package/package/acode/dist/ask-user-question-tool.d.ts +47 -0
- package/package/acode/dist/ask-user-question-tool.d.ts.map +1 -0
- package/package/acode/dist/ask-user-question-tool.js +233 -0
- package/package/acode/dist/built-in-file-tools.d.ts +2 -0
- package/package/acode/dist/built-in-file-tools.d.ts.map +1 -1
- package/package/acode/dist/compaction.d.ts +206 -0
- package/package/acode/dist/compaction.d.ts.map +1 -0
- package/package/acode/dist/compaction.js +664 -0
- package/package/acode/dist/config.d.ts +6 -1
- package/package/acode/dist/config.d.ts.map +1 -1
- package/package/acode/dist/config.js +27 -2
- package/package/acode/dist/index.d.ts +4 -0
- package/package/acode/dist/index.d.ts.map +1 -1
- package/package/acode/dist/index.js +4 -0
- package/package/acode/dist/runtime.d.ts +137 -0
- package/package/acode/dist/runtime.d.ts.map +1 -1
- package/package/acode/dist/runtime.js +974 -172
- package/package/acode/dist/skill-tool.d.ts +30 -0
- package/package/acode/dist/skill-tool.d.ts.map +1 -0
- package/package/acode/dist/skill-tool.js +174 -0
- package/package/acode/dist/sub-agent-manager.d.ts +169 -0
- package/package/acode/dist/sub-agent-manager.d.ts.map +1 -0
- package/package/acode/dist/sub-agent-manager.js +535 -0
- package/package/acode/dist/sub-agent-store.d.ts +33 -0
- package/package/acode/dist/sub-agent-store.d.ts.map +1 -0
- package/package/acode/dist/sub-agent-store.js +109 -0
- package/package/acode/dist/sub-agent-tools.d.ts +72 -0
- package/package/acode/dist/sub-agent-tools.d.ts.map +1 -0
- package/package/acode/dist/sub-agent-tools.js +429 -0
- package/package/acode/dist/sub-agent-types.d.ts +81 -0
- package/package/acode/dist/sub-agent-types.d.ts.map +1 -0
- package/package/acode/dist/sub-agent-types.js +8 -0
- package/package/acode/dist/todo-store.d.ts +42 -0
- package/package/acode/dist/todo-store.d.ts.map +1 -0
- package/package/acode/dist/todo-store.js +68 -0
- package/package/acode/dist/todo-tool.d.ts +6 -0
- package/package/acode/dist/todo-tool.d.ts.map +1 -0
- package/package/acode/dist/todo-tool.js +138 -0
- package/package/acode/dist/types.d.ts +22 -1
- package/package/acode/dist/types.d.ts.map +1 -1
- package/package/acode/package.json +13 -2
- package/package/aws-client-agent-mcp/dist/context-manager.test.js +0 -3
- package/package/aws-client-agent-mcp/dist/context-manager.test.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/mcp-server.d.ts +83 -50
- package/package/aws-client-agent-mcp/dist/mcp-server.d.ts.map +1 -1
- package/package/aws-client-agent-mcp/dist/mcp-server.js +451 -346
- package/package/aws-client-agent-mcp/dist/mcp-server.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/mcp-server.test.js +259 -241
- package/package/aws-client-agent-mcp/dist/mcp-server.test.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/mcp-tools.d.ts +1 -19
- package/package/aws-client-agent-mcp/dist/mcp-tools.d.ts.map +1 -1
- package/package/aws-client-agent-mcp/dist/mcp-tools.js +4 -77
- package/package/aws-client-agent-mcp/dist/mcp-tools.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/memory-store.d.ts +17 -40
- package/package/aws-client-agent-mcp/dist/memory-store.d.ts.map +1 -1
- package/package/aws-client-agent-mcp/dist/memory-store.js +62 -149
- package/package/aws-client-agent-mcp/dist/memory-store.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/memory-store.test.js +175 -164
- package/package/aws-client-agent-mcp/dist/memory-store.test.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/memory-tools.d.ts +3 -9
- package/package/aws-client-agent-mcp/dist/memory-tools.d.ts.map +1 -1
- package/package/aws-client-agent-mcp/dist/memory-tools.js +28 -55
- package/package/aws-client-agent-mcp/dist/memory-tools.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/message-buffer.d.ts +27 -9
- package/package/aws-client-agent-mcp/dist/message-buffer.d.ts.map +1 -1
- package/package/aws-client-agent-mcp/dist/message-buffer.js +100 -23
- package/package/aws-client-agent-mcp/dist/message-buffer.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/server-memory-store.d.ts +1 -14
- package/package/aws-client-agent-mcp/dist/server-memory-store.d.ts.map +1 -1
- package/package/aws-client-agent-mcp/dist/server-memory-store.js +7 -18
- package/package/aws-client-agent-mcp/dist/server-memory-store.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/status-reporter.d.ts.map +1 -1
- package/package/aws-client-agent-mcp/dist/status-reporter.js +8 -5
- package/package/aws-client-agent-mcp/dist/status-reporter.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/status-reporter.test.js +93 -0
- package/package/aws-client-agent-mcp/dist/status-reporter.test.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/types.d.ts +3 -25
- package/package/aws-client-agent-mcp/dist/types.d.ts.map +1 -1
- package/package.json +17 -7
- package/dist/adapter/AcodeSdkAdapter.test.d.ts +0 -2
- package/dist/adapter/AcodeSdkAdapter.test.d.ts.map +0 -1
- package/dist/adapter/AcodeSdkAdapter.test.js +0 -2000
- package/dist/adapter/AdapterRegistry.test.d.ts +0 -5
- package/dist/adapter/AdapterRegistry.test.d.ts.map +0 -1
- package/dist/adapter/AdapterRegistry.test.js +0 -118
- package/dist/adapter/ClaudeSdkAdapter.test.d.ts +0 -2
- package/dist/adapter/ClaudeSdkAdapter.test.d.ts.map +0 -1
- package/dist/adapter/ClaudeSdkAdapter.test.js +0 -311
- package/dist/adapter/CodexSdkAdapter.test.d.ts +0 -2
- package/dist/adapter/CodexSdkAdapter.test.d.ts.map +0 -1
- package/dist/adapter/CodexSdkAdapter.test.js +0 -270
- package/dist/adapter/OpencodeSdkAdapter.test.d.ts +0 -2
- package/dist/adapter/OpencodeSdkAdapter.test.d.ts.map +0 -1
- package/dist/adapter/OpencodeSdkAdapter.test.js +0 -158
- package/dist/adapter/acode-tool-names.test.d.ts +0 -2
- package/dist/adapter/acode-tool-names.test.d.ts.map +0 -1
- package/dist/adapter/acode-tool-names.test.js +0 -28
- package/dist/adapter/adapter.test.d.ts +0 -5
- package/dist/adapter/adapter.test.d.ts.map +0 -1
- package/dist/adapter/adapter.test.js +0 -180
- package/dist/adapter/idle-poll-loop.test.d.ts +0 -2
- package/dist/adapter/idle-poll-loop.test.d.ts.map +0 -1
- package/dist/adapter/idle-poll-loop.test.js +0 -85
- package/dist/adapter/types.test.d.ts +0 -2
- package/dist/adapter/types.test.d.ts.map +0 -1
- package/dist/adapter/types.test.js +0 -50
- package/dist/adapters/cc-switch/mcp-acode.test.d.ts +0 -2
- package/dist/adapters/cc-switch/mcp-acode.test.d.ts.map +0 -1
- package/dist/adapters/cc-switch/mcp-acode.test.js +0 -80
- package/dist/adapters/cc-switch/mcp-claudecode.test.d.ts +0 -2
- package/dist/adapters/cc-switch/mcp-claudecode.test.d.ts.map +0 -1
- package/dist/adapters/cc-switch/mcp-claudecode.test.js +0 -75
- package/dist/adapters/cc-switch/mcp-codex.test.d.ts +0 -2
- package/dist/adapters/cc-switch/mcp-codex.test.d.ts.map +0 -1
- package/dist/adapters/cc-switch/mcp-codex.test.js +0 -126
- package/dist/adapters/cc-switch/mcp-opencode.test.d.ts +0 -2
- package/dist/adapters/cc-switch/mcp-opencode.test.d.ts.map +0 -1
- package/dist/adapters/cc-switch/mcp-opencode.test.js +0 -62
- package/dist/adapters/cc-switch/skill-directory.test.d.ts +0 -2
- package/dist/adapters/cc-switch/skill-directory.test.d.ts.map +0 -1
- package/dist/adapters/cc-switch/skill-directory.test.js +0 -74
- package/dist/config.test.d.ts +0 -2
- package/dist/config.test.d.ts.map +0 -1
- package/dist/config.test.js +0 -107
- package/dist/routes/ai-sources.test.d.ts +0 -2
- package/dist/routes/ai-sources.test.d.ts.map +0 -1
- package/dist/routes/ai-sources.test.js +0 -169
- package/dist/routes/file-browser.test.d.ts +0 -5
- package/dist/routes/file-browser.test.d.ts.map +0 -1
- package/dist/routes/file-browser.test.js +0 -155
- package/dist/routes/git.test.d.ts +0 -2
- package/dist/routes/git.test.d.ts.map +0 -1
- package/dist/routes/git.test.js +0 -330
- package/dist/routes/instance.test.d.ts +0 -5
- package/dist/routes/instance.test.d.ts.map +0 -1
- package/dist/routes/instance.test.js +0 -389
- package/dist/routes/mcp.test.d.ts +0 -5
- package/dist/routes/mcp.test.d.ts.map +0 -1
- package/dist/routes/mcp.test.js +0 -92
- package/dist/routes/memory-route-wiring.test.d.ts +0 -2
- package/dist/routes/memory-route-wiring.test.d.ts.map +0 -1
- package/dist/routes/memory-route-wiring.test.js +0 -15
- package/dist/routes/memory.test.d.ts +0 -2
- package/dist/routes/memory.test.d.ts.map +0 -1
- package/dist/routes/memory.test.js +0 -177
- package/dist/routes/properties.test.d.ts +0 -5
- package/dist/routes/properties.test.d.ts.map +0 -1
- package/dist/routes/properties.test.js +0 -72
- package/dist/routes/pty.test.d.ts +0 -2
- package/dist/routes/pty.test.d.ts.map +0 -1
- package/dist/routes/pty.test.js +0 -82
- package/dist/routes/runtime-binding.test.d.ts +0 -2
- package/dist/routes/runtime-binding.test.d.ts.map +0 -1
- package/dist/routes/runtime-binding.test.js +0 -32
- package/dist/routes/sessions.test.d.ts +0 -5
- package/dist/routes/sessions.test.d.ts.map +0 -1
- package/dist/routes/sessions.test.js +0 -86
- package/dist/routes/skills.test.d.ts +0 -5
- package/dist/routes/skills.test.d.ts.map +0 -1
- package/dist/routes/skills.test.js +0 -50
- package/dist/routes/system-metrics.test.d.ts +0 -2
- package/dist/routes/system-metrics.test.d.ts.map +0 -1
- package/dist/routes/system-metrics.test.js +0 -16
- package/dist/routes/terminal.stop-options.test.d.ts +0 -2
- package/dist/routes/terminal.stop-options.test.d.ts.map +0 -1
- package/dist/routes/terminal.stop-options.test.js +0 -39
- package/dist/routes/terminal.test.d.ts +0 -2
- package/dist/routes/terminal.test.d.ts.map +0 -1
- package/dist/routes/terminal.test.js +0 -746
- package/dist/routes/yml.test.d.ts +0 -5
- package/dist/routes/yml.test.d.ts.map +0 -1
- package/dist/routes/yml.test.js +0 -58
- package/dist/services/ai-source-apply.test.d.ts +0 -2
- package/dist/services/ai-source-apply.test.d.ts.map +0 -1
- package/dist/services/ai-source-apply.test.js +0 -138
- package/dist/services/auto-register.test.d.ts +0 -2
- package/dist/services/auto-register.test.d.ts.map +0 -1
- package/dist/services/auto-register.test.js +0 -184
- package/dist/services/aws-client-agent-mcp.test.d.ts +0 -2
- package/dist/services/aws-client-agent-mcp.test.d.ts.map +0 -1
- package/dist/services/aws-client-agent-mcp.test.js +0 -242
- package/dist/services/bridge-package-publish-hooks.test.d.ts +0 -2
- package/dist/services/bridge-package-publish-hooks.test.d.ts.map +0 -1
- package/dist/services/bridge-package-publish-hooks.test.js +0 -136
- package/dist/services/cc-switch-discovery.test.d.ts +0 -2
- package/dist/services/cc-switch-discovery.test.d.ts.map +0 -1
- package/dist/services/cc-switch-discovery.test.js +0 -56
- package/dist/services/cli-commands.test.d.ts +0 -2
- package/dist/services/cli-commands.test.d.ts.map +0 -1
- package/dist/services/cli-commands.test.js +0 -230
- package/dist/services/instance-init-service.test.d.ts +0 -2
- package/dist/services/instance-init-service.test.d.ts.map +0 -1
- package/dist/services/instance-init-service.test.js +0 -140
- package/dist/services/instance-state.test.d.ts +0 -2
- package/dist/services/instance-state.test.d.ts.map +0 -1
- package/dist/services/instance-state.test.js +0 -245
- package/dist/services/mcp-launch-binding-queue.test.d.ts +0 -2
- package/dist/services/mcp-launch-binding-queue.test.d.ts.map +0 -1
- package/dist/services/mcp-launch-binding-queue.test.js +0 -181
- package/dist/services/runtime-binding.test.d.ts +0 -2
- package/dist/services/runtime-binding.test.d.ts.map +0 -1
- package/dist/services/runtime-binding.test.js +0 -78
- package/dist/services/runtime-lifecycle-policy.test.d.ts +0 -2
- package/dist/services/runtime-lifecycle-policy.test.d.ts.map +0 -1
- package/dist/services/runtime-lifecycle-policy.test.js +0 -41
- package/dist/services/session-output.test.d.ts +0 -5
- package/dist/services/session-output.test.d.ts.map +0 -1
- package/dist/services/session-output.test.js +0 -177
- package/dist/services/startup-config-wizard.test.d.ts +0 -2
- package/dist/services/startup-config-wizard.test.d.ts.map +0 -1
- package/dist/services/startup-config-wizard.test.js +0 -184
- package/dist/services/terminal-persistence.test.d.ts +0 -5
- package/dist/services/terminal-persistence.test.d.ts.map +0 -1
- package/dist/services/terminal-persistence.test.js +0 -159
- package/dist/services/tool-installer.test.d.ts +0 -2
- package/dist/services/tool-installer.test.d.ts.map +0 -1
- package/dist/services/tool-installer.test.js +0 -225
- package/dist/services/workspace-files.test.d.ts +0 -2
- package/dist/services/workspace-files.test.d.ts.map +0 -1
- package/dist/services/workspace-files.test.js +0 -648
- package/dist/services/workspace-watch.test.d.ts +0 -2
- package/dist/services/workspace-watch.test.d.ts.map +0 -1
- package/dist/services/workspace-watch.test.js +0 -38
- package/dist/utils/file-utils.test.d.ts +0 -2
- package/dist/utils/file-utils.test.d.ts.map +0 -1
- package/dist/utils/file-utils.test.js +0 -244
- package/dist/utils/logger.test.d.ts +0 -2
- package/dist/utils/logger.test.d.ts.map +0 -1
- package/dist/utils/logger.test.js +0 -93
- package/dist/utils/sdk-package-loader.test.d.ts +0 -2
- package/dist/utils/sdk-package-loader.test.d.ts.map +0 -1
- package/dist/utils/sdk-package-loader.test.js +0 -84
- package/dist/utils/validation.test.d.ts +0 -2
- package/dist/utils/validation.test.d.ts.map +0 -1
- package/dist/utils/validation.test.js +0 -88
- package/dist/utils/yaml-utils.test.d.ts +0 -2
- package/dist/utils/yaml-utils.test.d.ts.map +0 -1
- package/dist/utils/yaml-utils.test.js +0 -363
|
@@ -19,10 +19,22 @@ import { getAcodeConfigFile, getAcodeSkillsDir, getRuntimeHomeDir, } from "../co
|
|
|
19
19
|
import { getReleasedAcodeEntryPath } from "../services/acode-package.js";
|
|
20
20
|
import { getInstanceAiConfigDebugLogger } from "../utils/instance-ai-config-debug-logger.js";
|
|
21
21
|
import { redactForLog } from "../services/instance-ai-config-service.js";
|
|
22
|
+
import { subAgentManager } from "../services/sub-agent-manager-instance.js";
|
|
22
23
|
import { resolveAcodeProviderConfig, } from "./instance-ai-config-resolver.js";
|
|
23
24
|
import { isMessagePollingIdleCommand, isPollMessageToolDescriptor, } from "./acode-tool-names.js";
|
|
24
25
|
import { IdlePollLoop } from "./idle-poll-loop.js";
|
|
25
26
|
import { getCanonicalMcpDetail, getToolActionInfo } from "./types.js";
|
|
27
|
+
/**
|
|
28
|
+
* Adapter 调试日志:受 ACODE_COMPACTION_DEBUG=1 开关控制(与 runtime.ts 共用同一开关)。
|
|
29
|
+
* 默认关闭以避免生产环境噪声与潜在的 sessionId/agentId/event 信息泄漏。
|
|
30
|
+
* 文件中所有原 `console.log` 调用均改为走此 helper。
|
|
31
|
+
*/
|
|
32
|
+
const ACODE_DEBUG_ENABLED = process.env.ACODE_COMPACTION_DEBUG === "1";
|
|
33
|
+
function adapterDebugLog(...args) {
|
|
34
|
+
if (!ACODE_DEBUG_ENABLED)
|
|
35
|
+
return;
|
|
36
|
+
console.log(...args);
|
|
37
|
+
}
|
|
26
38
|
const ACODE_AUTO_POLL_PROMPT = [
|
|
27
39
|
"Continue the AgentsWork message loop now.",
|
|
28
40
|
"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.",
|
|
@@ -37,6 +49,9 @@ const ACODE_AUTO_POLL_FAILURE_DELAY_MS = 2000;
|
|
|
37
49
|
// 新增:submit 超时检测配置(治本修复:防止状态永久卡住)
|
|
38
50
|
// 当 submit 超过此时间未返回,强制触发恢复机制
|
|
39
51
|
const ACODE_SUBMIT_TIMEOUT_MS = 120000; // 120秒(2分钟)
|
|
52
|
+
// 兜底超时:mcp_status 缓存能力后,等待 skill_status 的最长时间。
|
|
53
|
+
// 超时后单独发出 MCP 能力清单,避免 skill_status 事件缺失导致"暂无能力清单"。
|
|
54
|
+
const CAPABILITIES_FALLBACK_MS = 3000;
|
|
40
55
|
const ACODE_MESSAGE_POLLING_IDLE_PROMPT = "系统提醒:当前工作已完成或运行时已进入空闲状态。请立即调用 poll_message 工具阻塞等待新消息和流程线任务提醒;收到流程任务提醒后调用 my_task 查看已由后台静默抢占的任务详情并直接处理;完成任务后调用 submit_task_result 提交结果,然后继续 poll_message。";
|
|
41
56
|
function getAcodePackageEntryPath() {
|
|
42
57
|
return path.resolve(getReleasedAcodeEntryPath());
|
|
@@ -249,20 +264,24 @@ export class AcodeSdkAdapter extends EventEmitter {
|
|
|
249
264
|
session.status = "error";
|
|
250
265
|
this.emitRuntimeErrorEvent(sessionId, error);
|
|
251
266
|
this.emit("status-change", sessionId, "error");
|
|
267
|
+
const failedEntry = this.sessions.get(sessionId);
|
|
268
|
+
if (failedEntry?.capabilitiesFallbackTimer) {
|
|
269
|
+
clearTimeout(failedEntry.capabilitiesFallbackTimer);
|
|
270
|
+
}
|
|
252
271
|
this.sessions.delete(sessionId);
|
|
253
272
|
throw error;
|
|
254
273
|
}
|
|
255
274
|
}
|
|
256
275
|
async sendMessage(sessionId, message) {
|
|
257
276
|
const sendStartTime = Date.now();
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
277
|
+
adapterDebugLog(`[ACode] ═══════════════════════════════════════════════════`);
|
|
278
|
+
adapterDebugLog(`[ACode] ▶️ sendMessage STARTED`);
|
|
279
|
+
adapterDebugLog(`[ACode] Session: ${sessionId}`);
|
|
280
|
+
adapterDebugLog(`[ACode] Message: ${message.slice(0, 100)}${message.length > 100 ? '...' : ''}`);
|
|
281
|
+
adapterDebugLog(`[ACode] ════════════════════════════════════════════════════`);
|
|
263
282
|
const entry = this.requireSession(sessionId);
|
|
264
283
|
const previousStatus = entry.adapterSession.status;
|
|
265
|
-
|
|
284
|
+
adapterDebugLog(`[ACode] Previous status: ${previousStatus}`);
|
|
266
285
|
this.cancelScheduledAutoPoll(entry);
|
|
267
286
|
entry.failedTurnRecoveryScheduled = false;
|
|
268
287
|
entry.idlePollLoop?.stop();
|
|
@@ -275,39 +294,63 @@ export class AcodeSdkAdapter extends EventEmitter {
|
|
|
275
294
|
};
|
|
276
295
|
entry.adapterSession.messages.push(userMessage);
|
|
277
296
|
this.emit("conversation-message", sessionId, userMessage);
|
|
278
|
-
|
|
297
|
+
adapterDebugLog(`[ACode] Changing status: ${previousStatus} → thinking`);
|
|
279
298
|
entry.adapterSession.status = "thinking";
|
|
280
299
|
this.emit("status-change", sessionId, "thinking");
|
|
281
300
|
// 启动超时检测
|
|
282
301
|
this.startSubmitTimeoutDetection(sessionId, entry, "sendMessage");
|
|
283
302
|
try {
|
|
284
|
-
|
|
303
|
+
adapterDebugLog(`[ACode] ▶️ Calling runtime.submit()...`);
|
|
285
304
|
const submitStartTime = Date.now();
|
|
286
305
|
await entry.runtime.submit(message);
|
|
287
306
|
const submitDuration = Date.now() - submitStartTime;
|
|
288
307
|
// submit 成功返回,取消超时检测
|
|
289
308
|
this.cancelSubmitTimeoutDetection(entry);
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
309
|
+
adapterDebugLog(`[ACode] ✅ runtime.submit() completed in ${submitDuration}ms`);
|
|
310
|
+
adapterDebugLog(`[ACode] Current status after submit: ${entry.adapterSession.status}`);
|
|
311
|
+
adapterDebugLog(`[ACode] ▶️ Calling recoverResolvedTurnWithoutTerminalEvent()...`);
|
|
293
312
|
this.recoverResolvedTurnWithoutTerminalEvent(sessionId);
|
|
294
|
-
|
|
313
|
+
adapterDebugLog(`[ACode] Status after recovery: ${entry.adapterSession.status}`);
|
|
295
314
|
const totalDuration = Date.now() - sendStartTime;
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
315
|
+
adapterDebugLog(`[ACode] ════════════════════════════════════════════════════`);
|
|
316
|
+
adapterDebugLog(`[ACode] ✅ sendMessage COMPLETED in ${totalDuration}ms`);
|
|
317
|
+
adapterDebugLog(`[ACode] ═══════════════════════════════════════════════════`);
|
|
299
318
|
}
|
|
300
319
|
catch (error) {
|
|
301
320
|
const errorDuration = Date.now() - sendStartTime;
|
|
302
321
|
console.error(`[ACode] ❌ sendMessage FAILED after ${errorDuration}ms:`, error instanceof Error ? error.message : String(error));
|
|
303
322
|
// submit 抛出异常,取消超时检测
|
|
304
323
|
this.cancelSubmitTimeoutDetection(entry);
|
|
305
|
-
|
|
324
|
+
adapterDebugLog(`[ACode] ▶️ Calling emitRuntimeError with recover=true...`);
|
|
306
325
|
this.emitRuntimeError(sessionId, error, { recover: true });
|
|
307
|
-
|
|
326
|
+
adapterDebugLog(`[ACode] Status after error recovery: ${entry.adapterSession.status}`);
|
|
308
327
|
}
|
|
309
328
|
}
|
|
310
329
|
async sendConfirmation(_sessionId, _accept) { }
|
|
330
|
+
/**
|
|
331
|
+
* 接收前端对 ask_user_question 的回答(来自 terminal.ts /sdk/answer 路由)。
|
|
332
|
+
* 通过 pendingQuestionToolCallId 找到 runtime.submitQuestionAnswer 的入参 toolCallId,
|
|
333
|
+
* resolve 阻塞的工具 execute(),让 ACode 继续下一轮 LLM 请求。
|
|
334
|
+
* 重新启动 submit 超时检测(继续计费后续工具执行时间)。
|
|
335
|
+
*/
|
|
336
|
+
async sendQuestionAnswer(sessionId, answers) {
|
|
337
|
+
const entry = this.sessions.get(sessionId);
|
|
338
|
+
if (!entry) {
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
const toolCallId = entry.pendingQuestionToolCallId;
|
|
342
|
+
if (!toolCallId) {
|
|
343
|
+
adapterDebugLog(`[AcodeSdkAdapter] sendQuestionAnswer: no pending question for session ${sessionId}`);
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
entry.pendingQuestionToolCallId = undefined;
|
|
347
|
+
entry.pendingQuestions = undefined;
|
|
348
|
+
if (entry.runtime.submitQuestionAnswer) {
|
|
349
|
+
entry.runtime.submitQuestionAnswer(toolCallId, answers);
|
|
350
|
+
}
|
|
351
|
+
// 用户回答后,重新启动 submit 超时检测(剩余工具执行时间计入)
|
|
352
|
+
this.startSubmitTimeoutDetection(sessionId, entry, "sendQuestionAnswer");
|
|
353
|
+
}
|
|
311
354
|
async abortCurrentTurn(sessionId) {
|
|
312
355
|
await this.requireSession(sessionId).runtime.abort();
|
|
313
356
|
}
|
|
@@ -344,6 +387,10 @@ export class AcodeSdkAdapter extends EventEmitter {
|
|
|
344
387
|
this.cancelScheduledAutoPoll(entry);
|
|
345
388
|
entry.failedTurnRecoveryScheduled = false;
|
|
346
389
|
entry.idlePollLoop?.stop();
|
|
390
|
+
if (entry.capabilitiesFallbackTimer) {
|
|
391
|
+
clearTimeout(entry.capabilitiesFallbackTimer);
|
|
392
|
+
entry.capabilitiesFallbackTimer = undefined;
|
|
393
|
+
}
|
|
347
394
|
await entry.runtime.stop();
|
|
348
395
|
entry.adapterSession.status = "terminated";
|
|
349
396
|
this.sessions.delete(sessionId);
|
|
@@ -361,6 +408,29 @@ export class AcodeSdkAdapter extends EventEmitter {
|
|
|
361
408
|
getSessionStatus(sessionId) {
|
|
362
409
|
return this.sessions.get(sessionId)?.adapterSession.status;
|
|
363
410
|
}
|
|
411
|
+
/**
|
|
412
|
+
* 主动查询会话的运行时能力清单(MCP 工具 + skills)。
|
|
413
|
+
* 只读 adapter 内存缓存(lastCombinedCapabilities),不触发 runtime 重新探测。
|
|
414
|
+
* 由 mcp_status / skill_status 事件维护;若尚未收到任何 runtime_capabilities 事件则返回 undefined。
|
|
415
|
+
*/
|
|
416
|
+
getCapabilities(sessionId) {
|
|
417
|
+
return this.sessions.get(sessionId)?.lastCombinedCapabilities;
|
|
418
|
+
}
|
|
419
|
+
/**
|
|
420
|
+
* 主动查询会话当前挂起的 ask_user_question。
|
|
421
|
+
* 只读 adapter 内存缓存(pendingQuestionToolCallId + pendingQuestions)。
|
|
422
|
+
* 由 bridge GET /runtime/sdk/pending-questions 端点调用,用于前端刷新后重建提问 UI。
|
|
423
|
+
* 返回 undefined 表示当前无挂起提问(会话不存在或 agent 未提问)。
|
|
424
|
+
*/
|
|
425
|
+
getPendingQuestion(sessionId) {
|
|
426
|
+
const entry = this.sessions.get(sessionId);
|
|
427
|
+
if (!entry?.pendingQuestionToolCallId)
|
|
428
|
+
return undefined;
|
|
429
|
+
return {
|
|
430
|
+
toolCallId: entry.pendingQuestionToolCallId,
|
|
431
|
+
questions: entry.pendingQuestions ?? [],
|
|
432
|
+
};
|
|
433
|
+
}
|
|
364
434
|
cleanup() {
|
|
365
435
|
for (const sessionId of this.sessions.keys()) {
|
|
366
436
|
void this.terminateSession(sessionId);
|
|
@@ -548,6 +618,7 @@ export class AcodeSdkAdapter extends EventEmitter {
|
|
|
548
618
|
? config.memory
|
|
549
619
|
: undefined,
|
|
550
620
|
env: config.envOverrides,
|
|
621
|
+
subAgentManager,
|
|
551
622
|
});
|
|
552
623
|
const debugLogger = getInstanceAiConfigDebugLogger();
|
|
553
624
|
void debugLogger.log({
|
|
@@ -672,10 +743,23 @@ export class AcodeSdkAdapter extends EventEmitter {
|
|
|
672
743
|
if (event.type === "ai_request") {
|
|
673
744
|
const estimatedTokens = toEstimatedContextTokens(event.data);
|
|
674
745
|
if (estimatedTokens > 0) {
|
|
675
|
-
|
|
676
|
-
|
|
746
|
+
// ★ 不再覆盖 totalUsage.inputTokens 和重置 outputTokens=0。
|
|
747
|
+
// runModelTurn 的多轮迭代中每个 iter 都会发 ai_request,
|
|
748
|
+
// 重置 outputTokens=0 导致面板在整个 executeSubmit 期间看到 0。
|
|
749
|
+
// 改用 Math.max 确保 inputTokens 单调递增即可。
|
|
750
|
+
// 注意:如果上下文缩减(如释放历史),Math.max 不会反映真实缩减,
|
|
751
|
+
// 但对面板展示而言单调递增是可接受的设计取舍。
|
|
752
|
+
if (estimatedTokens > entry.adapterSession.totalUsage.inputTokens) {
|
|
753
|
+
entry.adapterSession.totalUsage.inputTokens = estimatedTokens;
|
|
754
|
+
}
|
|
755
|
+
// ★ 使用消息历史估算实时的 output tokens,确保面板在会话期间看到非零值
|
|
756
|
+
const liveOutputEstimate = this.estimateOutputTokensFromMessages(entry.adapterSession.messages);
|
|
757
|
+
const displayUsage = {
|
|
758
|
+
...entry.adapterSession.totalUsage,
|
|
759
|
+
outputTokens: Math.max(entry.adapterSession.totalUsage.outputTokens, liveOutputEstimate),
|
|
760
|
+
};
|
|
677
761
|
this.emit("status-change", event.sessionId, entry.adapterSession.status, {
|
|
678
|
-
usage:
|
|
762
|
+
usage: displayUsage,
|
|
679
763
|
});
|
|
680
764
|
}
|
|
681
765
|
this.emitProviderEvent(event, "ai_request", {
|
|
@@ -711,12 +795,12 @@ export class AcodeSdkAdapter extends EventEmitter {
|
|
|
711
795
|
if (event.type === "turn_complete") {
|
|
712
796
|
const usage = toUsage(event.data.usage);
|
|
713
797
|
if (usage) {
|
|
714
|
-
|
|
798
|
+
// ★ 只累加 outputTokens;inputTokens 由 ai_request 的事件估算维护(Math.max 单调递增)
|
|
715
799
|
entry.adapterSession.totalUsage.outputTokens += usage.outputTokens;
|
|
716
800
|
}
|
|
717
801
|
// 关键日志:记录 turn_complete 事件
|
|
718
802
|
const previousStatus = entry.adapterSession.status;
|
|
719
|
-
|
|
803
|
+
adapterDebugLog(`[ACode] turn_complete event received (session: ${event.sessionId}), ` +
|
|
720
804
|
`previous status: ${previousStatus}, failed: ${event.data.failed === true}, ` +
|
|
721
805
|
`aborted: ${event.data.aborted === true}`);
|
|
722
806
|
if (isFailedAcodeTurnComplete(event.data)) {
|
|
@@ -732,7 +816,7 @@ export class AcodeSdkAdapter extends EventEmitter {
|
|
|
732
816
|
// initial prompt turn failed, autoPollActive was false and the
|
|
733
817
|
// session got permanently stuck in 'error'. Now we always recover
|
|
734
818
|
// for non-terminal sessions so the idle loop can retry.
|
|
735
|
-
|
|
819
|
+
adapterDebugLog(`[ACode] Recovering from failed turn_complete (session: ${event.sessionId})`);
|
|
736
820
|
this.recoverTurnFailure(event.sessionId, entry);
|
|
737
821
|
return;
|
|
738
822
|
}
|
|
@@ -749,7 +833,7 @@ export class AcodeSdkAdapter extends EventEmitter {
|
|
|
749
833
|
this.emit("status-change", event.sessionId, "waiting_input", {
|
|
750
834
|
usage: entry.adapterSession.totalUsage,
|
|
751
835
|
});
|
|
752
|
-
|
|
836
|
+
adapterDebugLog(`[ACode] Status changed to waiting_input after turn_complete (session: ${event.sessionId})`);
|
|
753
837
|
if (entry.autoPollActive &&
|
|
754
838
|
entry.autoPollObservedAssistantText &&
|
|
755
839
|
!entry.autoPollObservedPollingTool) {
|
|
@@ -771,7 +855,37 @@ export class AcodeSdkAdapter extends EventEmitter {
|
|
|
771
855
|
this.emit("status-change", event.sessionId, "completed");
|
|
772
856
|
return;
|
|
773
857
|
}
|
|
858
|
+
if (event.type === "question_request") {
|
|
859
|
+
// ask_user_question 工具被调用:runtime 在 execute() 中阻塞等待答案。
|
|
860
|
+
// 取消 submit 超时检测(用户回答时间不计入 2 分钟超时),
|
|
861
|
+
// 记录 toolCallId + questions 供 sendQuestionAnswer 和 getPendingQuestion 使用,
|
|
862
|
+
// 转发事件让 terminal.ts 推到前端。
|
|
863
|
+
this.cancelSubmitTimeoutDetection(entry);
|
|
864
|
+
const toolCallId = typeof event.data.toolCallId === "string"
|
|
865
|
+
? event.data.toolCallId
|
|
866
|
+
: "";
|
|
867
|
+
const questions = Array.isArray(event.data.questions)
|
|
868
|
+
? event.data.questions
|
|
869
|
+
: [];
|
|
870
|
+
if (toolCallId) {
|
|
871
|
+
entry.pendingQuestionToolCallId = toolCallId;
|
|
872
|
+
entry.pendingQuestions = questions;
|
|
873
|
+
}
|
|
874
|
+
this.emit("ask-user-question", {
|
|
875
|
+
sessionId: event.sessionId,
|
|
876
|
+
questions,
|
|
877
|
+
});
|
|
878
|
+
return;
|
|
879
|
+
}
|
|
774
880
|
if (event.type === "error") {
|
|
881
|
+
// nonFatal=true 表示工具执行失败等非致命错误(Agent 会继续处理),
|
|
882
|
+
// 仅推送事件到时间线,不变更运行态为 "error",避免面板启动/停止按钮异常。
|
|
883
|
+
if (event.data.nonFatal) {
|
|
884
|
+
this.emitProviderEvent(event, "error", {
|
|
885
|
+
text: String(event.data.message || "ACode runtime error"),
|
|
886
|
+
});
|
|
887
|
+
return;
|
|
888
|
+
}
|
|
775
889
|
entry.adapterSession.status = "error";
|
|
776
890
|
this.emitProviderEvent(event, "error", {
|
|
777
891
|
text: String(event.data.message || "ACode runtime error"),
|
|
@@ -812,6 +926,69 @@ export class AcodeSdkAdapter extends EventEmitter {
|
|
|
812
926
|
"Please configure an MCP server that provides poll_message (e.g. aws-mcp) in the dashboard panel for this runtime.";
|
|
813
927
|
console.warn(`[${this.displayName}] ${message}`);
|
|
814
928
|
}
|
|
929
|
+
// 将启动时加载的 MCP 工具清单缓存到 entry,等待 skill_status 事件到达后合并发出,
|
|
930
|
+
// 避免两个 runtime_capabilities 事件并发 HTTP POST 导致其中一个丢失
|
|
931
|
+
const mcpServers = Array.isArray(event.data.servers)
|
|
932
|
+
? event.data.servers
|
|
933
|
+
: [];
|
|
934
|
+
const mcpTools = tools
|
|
935
|
+
.map((tool) => ({
|
|
936
|
+
name: String(tool.name ?? ""),
|
|
937
|
+
source: String(tool.source ?? ""),
|
|
938
|
+
description: String(tool.description ?? ""),
|
|
939
|
+
}))
|
|
940
|
+
.filter((tool) => tool.name);
|
|
941
|
+
const mcpCapabilities = {
|
|
942
|
+
kind: "mcp",
|
|
943
|
+
mcpServers,
|
|
944
|
+
mcpTools,
|
|
945
|
+
};
|
|
946
|
+
entry.cachedMcpCapabilities = mcpCapabilities;
|
|
947
|
+
// 同步保存到 lastMcpCapabilities,运行中再次发 skill_status(如 skill 目录变更)
|
|
948
|
+
// 时,会用它合并 MCP 信息,避免覆盖为空数组
|
|
949
|
+
entry.lastMcpCapabilities = mcpCapabilities;
|
|
950
|
+
// 更新 lastCombinedCapabilities 的 MCP 部分,保留已有的 skills(若 skill_status 已先到达)
|
|
951
|
+
entry.lastCombinedCapabilities = {
|
|
952
|
+
kind: "combined",
|
|
953
|
+
mcpServers,
|
|
954
|
+
mcpTools,
|
|
955
|
+
skills: entry.lastCombinedCapabilities?.skills ?? [],
|
|
956
|
+
skillsDir: entry.lastCombinedCapabilities?.skillsDir,
|
|
957
|
+
skillGroups: entry.lastCombinedCapabilities?.skillGroups ?? [],
|
|
958
|
+
};
|
|
959
|
+
adapterDebugLog(`[cap-diag] MCP capabilities cached: ${mcpServers.length} servers, ${mcpTools.length} tools`, {
|
|
960
|
+
sessionId: event.sessionId,
|
|
961
|
+
servers: mcpServers,
|
|
962
|
+
toolNames: mcpTools.map(t => t.name),
|
|
963
|
+
});
|
|
964
|
+
// 兜底:如果 skill_status 事件在 CAPABILITIES_FALLBACK_MS 内未到达,
|
|
965
|
+
// 单独发出 MCP 能力清单,避免 skill_status 缺失导致"暂无能力清单"。
|
|
966
|
+
if (entry.capabilitiesFallbackTimer) {
|
|
967
|
+
clearTimeout(entry.capabilitiesFallbackTimer);
|
|
968
|
+
}
|
|
969
|
+
entry.capabilitiesFallbackTimer = setTimeout(() => {
|
|
970
|
+
const cached = entry.cachedMcpCapabilities;
|
|
971
|
+
if (!cached)
|
|
972
|
+
return;
|
|
973
|
+
const fallbackCapabilities = {
|
|
974
|
+
...cached,
|
|
975
|
+
skills: [],
|
|
976
|
+
skillGroups: [],
|
|
977
|
+
};
|
|
978
|
+
this.emitProviderEvent(event, "runtime_capabilities", {
|
|
979
|
+
capabilities: fallbackCapabilities,
|
|
980
|
+
actionType: "runtime_capabilities",
|
|
981
|
+
actionLabel: "运行时能力",
|
|
982
|
+
actionDetail: JSON.stringify(fallbackCapabilities),
|
|
983
|
+
});
|
|
984
|
+
adapterDebugLog(`[cap-diag] MCP capabilities emitted via fallback (skill_status not received within ${CAPABILITIES_FALLBACK_MS}ms)`, {
|
|
985
|
+
sessionId: event.sessionId,
|
|
986
|
+
mcpServers: cached.mcpServers,
|
|
987
|
+
mcpToolsCount: cached.mcpTools?.length ?? 0,
|
|
988
|
+
});
|
|
989
|
+
entry.cachedMcpCapabilities = undefined;
|
|
990
|
+
entry.capabilitiesFallbackTimer = undefined;
|
|
991
|
+
}, CAPABILITIES_FALLBACK_MS);
|
|
815
992
|
return;
|
|
816
993
|
}
|
|
817
994
|
const server = typeof event.data.server === "string" ? event.data.server : "mcp";
|
|
@@ -843,6 +1020,8 @@ export class AcodeSdkAdapter extends EventEmitter {
|
|
|
843
1020
|
...(actionInput ? { actionInput } : {}),
|
|
844
1021
|
...(toolInput ? { toolInput } : {}),
|
|
845
1022
|
...(toolUseId ? { toolUseId, actionId: toolUseId } : {}),
|
|
1023
|
+
// 传递 MCP 服务器名,供前端时间线展示工具来源
|
|
1024
|
+
metadata: { mcpServer: server },
|
|
846
1025
|
};
|
|
847
1026
|
if (event.data.action === "tool_result") {
|
|
848
1027
|
this.emitProviderEvent(event, "tool_use_end", {
|
|
@@ -856,11 +1035,12 @@ export class AcodeSdkAdapter extends EventEmitter {
|
|
|
856
1035
|
? "waiting_input"
|
|
857
1036
|
: "thinking";
|
|
858
1037
|
entry.adapterSession.status = status;
|
|
859
|
-
//
|
|
860
|
-
//
|
|
861
|
-
//
|
|
862
|
-
//
|
|
863
|
-
|
|
1038
|
+
// ★ 修复重复时间线条目:idle 类型(poll_message)的 tool_use_end status-change
|
|
1039
|
+
// 始终传 undefined,避免 terminal.ts 的 restoredActionInfo 恢复出 actionType+actionLabel
|
|
1040
|
+
// 导致 dashboard 创建第二个"调用MCP"条目。tool_use_start 的 status-change 已创建条目。
|
|
1041
|
+
// 非 idle 类型仍按原逻辑传 eventData(waiting_input 时)以保留输入参数展示。
|
|
1042
|
+
const isIdleTool = actionInfo.actionType === "idle";
|
|
1043
|
+
this.emit("status-change", event.sessionId, status, isIdleTool ? undefined : (status === "thinking" ? undefined : eventData));
|
|
864
1044
|
return;
|
|
865
1045
|
}
|
|
866
1046
|
const status = actionInfo.actionType === "idle" ? "waiting_input" : "tool_using";
|
|
@@ -869,6 +1049,89 @@ export class AcodeSdkAdapter extends EventEmitter {
|
|
|
869
1049
|
this.emit("status-change", event.sessionId, status, eventData);
|
|
870
1050
|
return;
|
|
871
1051
|
}
|
|
1052
|
+
if (event.type === "skill_status") {
|
|
1053
|
+
// skill_status 事件可能在两个时机到达:
|
|
1054
|
+
// 1. ACode runtime 启动时(包含已加载的 Skill 列表)
|
|
1055
|
+
// 2. 运行中 skills 目录变更触发重扫后(动态感知新 skill)
|
|
1056
|
+
// 合并 MCP 能力清单后,作为单个 runtime_capabilities 事件发出,
|
|
1057
|
+
// 避免与 mcp_status 的 runtime_capabilities 事件并发 HTTP POST 导致其中一个丢失。
|
|
1058
|
+
// 启动时用 cachedMcpCapabilities(等待合并);运行中 cachedMcp 已清理,
|
|
1059
|
+
// 回退到 lastMcpCapabilities(保留最近一次 MCP 信息,避免覆盖为空数组)。
|
|
1060
|
+
const skillsRaw = Array.isArray(event.data.skills)
|
|
1061
|
+
? event.data.skills
|
|
1062
|
+
: [];
|
|
1063
|
+
const skills = skillsRaw
|
|
1064
|
+
.map((skill) => ({
|
|
1065
|
+
name: String(skill.name ?? ""),
|
|
1066
|
+
description: typeof skill.description === "string"
|
|
1067
|
+
? skill.description
|
|
1068
|
+
: undefined,
|
|
1069
|
+
directory: typeof skill.directory === "string"
|
|
1070
|
+
? skill.directory
|
|
1071
|
+
: undefined,
|
|
1072
|
+
}))
|
|
1073
|
+
.filter((skill) => skill.name);
|
|
1074
|
+
const skillsDir = typeof event.data.skillsDir === "string"
|
|
1075
|
+
? event.data.skillsDir
|
|
1076
|
+
: undefined;
|
|
1077
|
+
// 按目录分组的 skills(含空目录节点),供前端按目录展示
|
|
1078
|
+
const skillGroupsRaw = Array.isArray(event.data.skillGroups)
|
|
1079
|
+
? event.data.skillGroups
|
|
1080
|
+
: [];
|
|
1081
|
+
const skillGroups = skillGroupsRaw
|
|
1082
|
+
.map((group) => {
|
|
1083
|
+
const groupSkills = Array.isArray(group.skills)
|
|
1084
|
+
? group.skills
|
|
1085
|
+
: [];
|
|
1086
|
+
return {
|
|
1087
|
+
dir: typeof group.dir === "string" ? group.dir : "",
|
|
1088
|
+
exists: group.exists === true,
|
|
1089
|
+
skills: groupSkills
|
|
1090
|
+
.map((skill) => ({
|
|
1091
|
+
name: String(skill.name ?? ""),
|
|
1092
|
+
description: typeof skill.description === "string"
|
|
1093
|
+
? skill.description
|
|
1094
|
+
: undefined,
|
|
1095
|
+
directory: typeof skill.directory === "string"
|
|
1096
|
+
? skill.directory
|
|
1097
|
+
: undefined,
|
|
1098
|
+
}))
|
|
1099
|
+
.filter((skill) => skill.name),
|
|
1100
|
+
};
|
|
1101
|
+
})
|
|
1102
|
+
.filter((group) => group.dir);
|
|
1103
|
+
// 优先用启动缓存(cachedMcp),运行中回退到 lastMcpCapabilities
|
|
1104
|
+
const mcpSource = entry.cachedMcpCapabilities ?? entry.lastMcpCapabilities;
|
|
1105
|
+
const isStartupMerge = entry.cachedMcpCapabilities !== undefined;
|
|
1106
|
+
const mergedCapabilities = {
|
|
1107
|
+
kind: "combined",
|
|
1108
|
+
mcpServers: mcpSource?.mcpServers ?? [],
|
|
1109
|
+
mcpTools: mcpSource?.mcpTools ?? [],
|
|
1110
|
+
skills,
|
|
1111
|
+
skillsDir,
|
|
1112
|
+
skillGroups,
|
|
1113
|
+
};
|
|
1114
|
+
this.emitProviderEvent(event, "runtime_capabilities", {
|
|
1115
|
+
capabilities: mergedCapabilities,
|
|
1116
|
+
actionType: "runtime_capabilities",
|
|
1117
|
+
actionLabel: "运行时能力",
|
|
1118
|
+
actionDetail: JSON.stringify(mergedCapabilities),
|
|
1119
|
+
});
|
|
1120
|
+
// 更新 lastCombinedCapabilities,作为主动查询的单一数据源
|
|
1121
|
+
entry.lastCombinedCapabilities = mergedCapabilities;
|
|
1122
|
+
adapterDebugLog(`[cap-diag] Combined capabilities emitted ${isStartupMerge ? "(startup merge)" : "(runtime refresh)"}: ${(mcpSource?.mcpTools ?? []).length} MCP tools, ${skills.length} skills`, {
|
|
1123
|
+
sessionId: event.sessionId,
|
|
1124
|
+
mcpServers: mcpSource?.mcpServers ?? [],
|
|
1125
|
+
skillsDir,
|
|
1126
|
+
});
|
|
1127
|
+
// 清理启动缓存与兜底定时器(仅启动合并路径需要;运行中刷新时 cachedMcp 已是 undefined)
|
|
1128
|
+
if (entry.capabilitiesFallbackTimer) {
|
|
1129
|
+
clearTimeout(entry.capabilitiesFallbackTimer);
|
|
1130
|
+
entry.capabilitiesFallbackTimer = undefined;
|
|
1131
|
+
}
|
|
1132
|
+
entry.cachedMcpCapabilities = undefined;
|
|
1133
|
+
return;
|
|
1134
|
+
}
|
|
872
1135
|
if (event.type === "session_started") {
|
|
873
1136
|
const estimatedTokens = toEstimatedContextTokens(event.data);
|
|
874
1137
|
if (estimatedTokens > 0) {
|
|
@@ -898,6 +1161,90 @@ export class AcodeSdkAdapter extends EventEmitter {
|
|
|
898
1161
|
});
|
|
899
1162
|
return;
|
|
900
1163
|
}
|
|
1164
|
+
if (event.type === "context_compaction") {
|
|
1165
|
+
const data = event.data;
|
|
1166
|
+
const action = data.action;
|
|
1167
|
+
const isStart = action === "start";
|
|
1168
|
+
const isEnd = action === "end";
|
|
1169
|
+
const isPhase = action === "phase";
|
|
1170
|
+
// parentId 用于将子事件(phase)关联到父压缩事件
|
|
1171
|
+
const parentId = data.parentId;
|
|
1172
|
+
// compactionId 作为父事件的 actionId,子事件通过 parentId 引用它
|
|
1173
|
+
const compactionId = data.compactionId;
|
|
1174
|
+
let actionLabel;
|
|
1175
|
+
let actionDetail;
|
|
1176
|
+
if (isStart) {
|
|
1177
|
+
const sizeKB = String(data.sizeKB ?? data.sizeBeforeKB ?? "?");
|
|
1178
|
+
const msgCount = String(data.messageCount ?? data.messageCountBefore ?? "?");
|
|
1179
|
+
const actualInput = data.actualInputTokens ? `input=${data.actualInputTokens}` : "";
|
|
1180
|
+
const actualOutput = data.actualOutputTokens ? `output=${data.actualOutputTokens}` : "";
|
|
1181
|
+
const trigger = data.trigger ? `, 触发: ${data.trigger}` : "";
|
|
1182
|
+
actionLabel = "上下文压缩开始";
|
|
1183
|
+
actionDetail = `压缩前: ${sizeKB}KB, 消息: ${msgCount}条` +
|
|
1184
|
+
(actualInput ? `, 实际 tokens: ${actualInput} ${actualOutput}` : "") + trigger;
|
|
1185
|
+
}
|
|
1186
|
+
else if (isPhase) {
|
|
1187
|
+
// 子事件(阶段事件):使用 runtime 传入的 actionLabel 和 actionDetail
|
|
1188
|
+
actionLabel = String(data.phaseLabel ?? "压缩阶段");
|
|
1189
|
+
actionDetail = String(data.phaseDetail ?? "");
|
|
1190
|
+
}
|
|
1191
|
+
else {
|
|
1192
|
+
// action === "end" —— 作为压缩完成子事件,嵌套在父事件下
|
|
1193
|
+
const sizeBefore = String(data.sizeBeforeKB ?? "?");
|
|
1194
|
+
const sizeAfter = String(data.sizeAfterKB ?? "?");
|
|
1195
|
+
const freedKB = String(data.freedKB ?? "?");
|
|
1196
|
+
const strategy = String(data.strategy ?? "?");
|
|
1197
|
+
const pruned = Number(data.prunedToolMessages ?? 0);
|
|
1198
|
+
const summarized = Number(data.summarizedMessages ?? 0);
|
|
1199
|
+
actionLabel = "压缩完成";
|
|
1200
|
+
actionDetail = `释放: ${freedKB}KB, ${sizeBefore}KB → ${sizeAfter}KB, 策略: ${strategy}` +
|
|
1201
|
+
(pruned ? `, 裁剪工具: ${pruned}条` : "") +
|
|
1202
|
+
(summarized ? `, 摘要化: ${summarized}条` : "");
|
|
1203
|
+
}
|
|
1204
|
+
// ★ 更新 adapter 内部状态为 compacting,使前端 agent 卡片显示压缩中
|
|
1205
|
+
// 子事件(phase/end)不重复更新状态,仅推送到时间线
|
|
1206
|
+
if (isStart) {
|
|
1207
|
+
entry.adapterSession.status = "compacting";
|
|
1208
|
+
}
|
|
1209
|
+
const actionInfo = {
|
|
1210
|
+
actionType: "context_compaction",
|
|
1211
|
+
actionLabel,
|
|
1212
|
+
actionDetail,
|
|
1213
|
+
};
|
|
1214
|
+
// ★ 事件层级:start 为父事件(root),phase 和 end 为子事件(nested)
|
|
1215
|
+
// - 父事件:actionId = compactionId
|
|
1216
|
+
// - 子事件:parentId = compactionId, actionId 唯一(避免前端 seenActionIds 去重冲突)
|
|
1217
|
+
if (isStart && compactionId) {
|
|
1218
|
+
actionInfo.actionId = compactionId;
|
|
1219
|
+
}
|
|
1220
|
+
else if ((isPhase || isEnd) && compactionId) {
|
|
1221
|
+
actionInfo.parentId = compactionId;
|
|
1222
|
+
if (isPhase) {
|
|
1223
|
+
const phase = String(data.phase ?? "unknown");
|
|
1224
|
+
actionInfo.actionId = `${compactionId}-phase-${phase}`;
|
|
1225
|
+
}
|
|
1226
|
+
else {
|
|
1227
|
+
// end 事件使用独立 actionId,避免与 start 的 compactionId 重复
|
|
1228
|
+
actionInfo.actionId = `${compactionId}-end`;
|
|
1229
|
+
}
|
|
1230
|
+
}
|
|
1231
|
+
// 兼容旧路径:runtime 可能通过 parentId 字段传入
|
|
1232
|
+
if (isPhase && parentId && !actionInfo.parentId) {
|
|
1233
|
+
actionInfo.parentId = parentId;
|
|
1234
|
+
}
|
|
1235
|
+
// ★ 压缩结束时重置 totalUsage.inputTokens,使面板显示压缩后的真实上下文大小。
|
|
1236
|
+
// 将 usage 合并到 end 事件的 actionInfo 中,避免额外的无 actionInfo status-change
|
|
1237
|
+
// 触发前端 handleDeltaStatusEvent 创建无 actionId 的占位事件(会导致扁平化渲染)。
|
|
1238
|
+
if (isEnd) {
|
|
1239
|
+
entry.adapterSession.totalUsage.inputTokens = 0;
|
|
1240
|
+
actionInfo.usage = entry.adapterSession.totalUsage;
|
|
1241
|
+
}
|
|
1242
|
+
this.emitProviderEvent(event, "context_compaction", actionInfo);
|
|
1243
|
+
// ★ 与其他事件(tool_use_start/turn_complete 等)一致,
|
|
1244
|
+
// 通过 status-change 事件将压缩状态推送到面板时间线。
|
|
1245
|
+
this.emit("status-change", event.sessionId, "compacting", actionInfo);
|
|
1246
|
+
return;
|
|
1247
|
+
}
|
|
901
1248
|
this.emitProviderEvent(event, "thinking", {
|
|
902
1249
|
text: JSON.stringify(event.data),
|
|
903
1250
|
});
|
|
@@ -1057,10 +1404,34 @@ export class AcodeSdkAdapter extends EventEmitter {
|
|
|
1057
1404
|
data: { text: error instanceof Error ? error.message : String(error) },
|
|
1058
1405
|
});
|
|
1059
1406
|
}
|
|
1407
|
+
/**
|
|
1408
|
+
* 从会话消息中估算 AI 输出 token 数(字符数 / 4 粗略估算)。
|
|
1409
|
+
* 注意:该估算对 CJK 文本偏高(实际每字约 1-2 tokens),
|
|
1410
|
+
* 对纯 ASCII 文本偏低(约 3-5 字符/token),仅供面板展示实时趋势使用,
|
|
1411
|
+
* 不应用于精确计费或配额判定。精确值以 turn_complete 事件中的 usage 为准。
|
|
1412
|
+
*/
|
|
1413
|
+
estimateOutputTokensFromMessages(messages) {
|
|
1414
|
+
const assistantText = messages
|
|
1415
|
+
.filter((m) => m.role === "assistant")
|
|
1416
|
+
.map((m) => m.content)
|
|
1417
|
+
.join("");
|
|
1418
|
+
return Math.max(0, Math.round(assistantText.length / 4));
|
|
1419
|
+
}
|
|
1060
1420
|
recoverTurnFailure(sessionId, entry) {
|
|
1061
1421
|
if (this.isTerminalStatus(entry.adapterSession.status)) {
|
|
1062
1422
|
return;
|
|
1063
1423
|
}
|
|
1424
|
+
// ★ 修复:超时恢复前强制 emit turn_complete,计入已有的 output tokens,
|
|
1425
|
+
// 否则面板上 output_tokens 永远是 0(turn_complete 从未发射)。
|
|
1426
|
+
// 使用消息历史中的 assistant 文本粗略估算 output tokens。
|
|
1427
|
+
const estimatedOutput = this.estimateOutputTokensFromMessages(entry.adapterSession.messages);
|
|
1428
|
+
if (estimatedOutput > 0 && entry.adapterSession.totalUsage.outputTokens < estimatedOutput) {
|
|
1429
|
+
const delta = estimatedOutput - entry.adapterSession.totalUsage.outputTokens;
|
|
1430
|
+
entry.adapterSession.totalUsage.outputTokens += delta;
|
|
1431
|
+
adapterDebugLog(`[ACode] recoverTurnFailure: 强制计入 ${delta} output tokens ` +
|
|
1432
|
+
`(totalUsage now: input=${entry.adapterSession.totalUsage.inputTokens}, ` +
|
|
1433
|
+
`output=${entry.adapterSession.totalUsage.outputTokens})`);
|
|
1434
|
+
}
|
|
1064
1435
|
// ★ 修复:检测 idle 命令是否包含 poll_message,在 waiting_input 状态变更中携带 idle 动作信息
|
|
1065
1436
|
const idleCommands = entry.idleCommands?.idleInputCommand;
|
|
1066
1437
|
const willResumePolling = Boolean(idleCommands && isMessagePollingIdleCommand(idleCommands));
|
|
@@ -1072,13 +1443,19 @@ export class AcodeSdkAdapter extends EventEmitter {
|
|
|
1072
1443
|
if (entry.autoPollTimer || entry.failedTurnRecoveryScheduled) {
|
|
1073
1444
|
if (entry.adapterSession.status !== "waiting_input") {
|
|
1074
1445
|
entry.adapterSession.status = "waiting_input";
|
|
1075
|
-
this.emit("status-change", sessionId, "waiting_input",
|
|
1446
|
+
this.emit("status-change", sessionId, "waiting_input", {
|
|
1447
|
+
usage: entry.adapterSession.totalUsage,
|
|
1448
|
+
...idleActionInfo,
|
|
1449
|
+
});
|
|
1076
1450
|
}
|
|
1077
1451
|
return;
|
|
1078
1452
|
}
|
|
1079
1453
|
this.cancelScheduledAutoPoll(entry);
|
|
1080
1454
|
entry.adapterSession.status = "waiting_input";
|
|
1081
|
-
this.emit("status-change", sessionId, "waiting_input",
|
|
1455
|
+
this.emit("status-change", sessionId, "waiting_input", {
|
|
1456
|
+
usage: entry.adapterSession.totalUsage,
|
|
1457
|
+
...idleActionInfo,
|
|
1458
|
+
});
|
|
1082
1459
|
entry.failedTurnRecoveryScheduled = true;
|
|
1083
1460
|
entry.autoPollTimer = setTimeout(() => {
|
|
1084
1461
|
const currentEntry = this.sessions.get(sessionId);
|
|
@@ -1112,20 +1489,20 @@ export class AcodeSdkAdapter extends EventEmitter {
|
|
|
1112
1489
|
* look permanently stuck. Recover to waiting_input and arm the listening loop.
|
|
1113
1490
|
*/
|
|
1114
1491
|
recoverResolvedTurnWithoutTerminalEvent(sessionId) {
|
|
1115
|
-
|
|
1492
|
+
adapterDebugLog(`[ACode] ▶️ recoverResolvedTurnWithoutTerminalEvent called for session ${sessionId}`);
|
|
1116
1493
|
const entry = this.sessions.get(sessionId);
|
|
1117
1494
|
if (!entry || this.isTerminalStatus(entry.adapterSession.status)) {
|
|
1118
|
-
|
|
1495
|
+
adapterDebugLog(`[ACode] Skipping recovery: entry=${!!entry}, isTerminal=${entry ? this.isTerminalStatus(entry.adapterSession.status) : 'N/A'}`);
|
|
1119
1496
|
return;
|
|
1120
1497
|
}
|
|
1121
|
-
|
|
1498
|
+
adapterDebugLog(`[ACode] Current status: ${entry.adapterSession.status}`);
|
|
1122
1499
|
if (entry.adapterSession.status !== "thinking" &&
|
|
1123
1500
|
entry.adapterSession.status !== "tool_using") {
|
|
1124
|
-
|
|
1501
|
+
adapterDebugLog(`[ACode] Skipping recovery: status is ${entry.adapterSession.status} (not thinking/tool_using)`);
|
|
1125
1502
|
return;
|
|
1126
1503
|
}
|
|
1127
|
-
|
|
1128
|
-
|
|
1504
|
+
adapterDebugLog(`[ACode] ⚠️ Detected stuck state: ${entry.adapterSession.status} after submit resolved!`);
|
|
1505
|
+
adapterDebugLog(`[ACode] Recovering to waiting_input...`);
|
|
1129
1506
|
entry.adapterSession.status = "waiting_input";
|
|
1130
1507
|
// ★ 修复:检测 idle 命令是否包含 poll_message,在 waiting_input 状态变更中携带 idle 动作信息
|
|
1131
1508
|
const idleCommands = entry.idleCommands?.idleInputCommand;
|
|
@@ -1136,10 +1513,10 @@ export class AcodeSdkAdapter extends EventEmitter {
|
|
|
1136
1513
|
actionDetail: 'aws-mcp/poll_message',
|
|
1137
1514
|
} : undefined;
|
|
1138
1515
|
this.emit("status-change", sessionId, "waiting_input", idleActionInfo);
|
|
1139
|
-
|
|
1140
|
-
|
|
1516
|
+
adapterDebugLog(`[ACode] ✅ Recovery complete, status now: ${entry.adapterSession.status}`);
|
|
1517
|
+
adapterDebugLog(`[ACode] ▶️ Arming idle loop or auto-poll...`);
|
|
1141
1518
|
this.armIdleLoopOrAutoPoll(sessionId);
|
|
1142
|
-
|
|
1519
|
+
adapterDebugLog(`[ACode] ✅ Idle loop armed, recovery process complete`);
|
|
1143
1520
|
}
|
|
1144
1521
|
/**
|
|
1145
1522
|
* 启动 submit 超时检测(治本修复:防止状态永久卡住)
|