aws-runtime-bridge 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +56 -0
- package/dist/adapter/AdapterRegistry.d.ts +53 -0
- package/dist/adapter/AdapterRegistry.d.ts.map +1 -0
- package/dist/adapter/AdapterRegistry.js +100 -0
- package/dist/adapter/AdapterRegistry.test.d.ts +5 -0
- package/dist/adapter/AdapterRegistry.test.d.ts.map +1 -0
- package/dist/adapter/AdapterRegistry.test.js +109 -0
- package/dist/adapter/ClaudeSdkAdapter.d.ts +120 -0
- package/dist/adapter/ClaudeSdkAdapter.d.ts.map +1 -0
- package/dist/adapter/ClaudeSdkAdapter.js +1140 -0
- package/dist/adapter/ClaudeSdkAdapter.test.d.ts +2 -0
- package/dist/adapter/ClaudeSdkAdapter.test.d.ts.map +1 -0
- package/dist/adapter/ClaudeSdkAdapter.test.js +95 -0
- package/dist/adapter/CodexSdkAdapter.d.ts +48 -0
- package/dist/adapter/CodexSdkAdapter.d.ts.map +1 -0
- package/dist/adapter/CodexSdkAdapter.js +750 -0
- package/dist/adapter/CodexSdkAdapter.test.d.ts +2 -0
- package/dist/adapter/CodexSdkAdapter.test.d.ts.map +1 -0
- package/dist/adapter/CodexSdkAdapter.test.js +245 -0
- package/dist/adapter/OpencodeSdkAdapter.d.ts +45 -0
- package/dist/adapter/OpencodeSdkAdapter.d.ts.map +1 -0
- package/dist/adapter/OpencodeSdkAdapter.js +622 -0
- package/dist/adapter/OpencodeSdkAdapter.test.d.ts +2 -0
- package/dist/adapter/OpencodeSdkAdapter.test.d.ts.map +1 -0
- package/dist/adapter/OpencodeSdkAdapter.test.js +14 -0
- package/dist/adapter/SdkProviderSpi.d.ts +15 -0
- package/dist/adapter/SdkProviderSpi.d.ts.map +1 -0
- package/dist/adapter/SdkProviderSpi.js +46 -0
- package/dist/adapter/adapter.test.d.ts +5 -0
- package/dist/adapter/adapter.test.d.ts.map +1 -0
- package/dist/adapter/adapter.test.js +180 -0
- package/dist/adapter/index.d.ts +11 -0
- package/dist/adapter/index.d.ts.map +1 -0
- package/dist/adapter/index.js +10 -0
- package/dist/adapter/types.d.ts +278 -0
- package/dist/adapter/types.d.ts.map +1 -0
- package/dist/adapter/types.js +278 -0
- package/dist/adapter/types.test.d.ts +2 -0
- package/dist/adapter/types.test.d.ts.map +1 -0
- package/dist/adapter/types.test.js +59 -0
- package/dist/adapters/cc-switch/common.d.ts +52 -0
- package/dist/adapters/cc-switch/common.d.ts.map +1 -0
- package/dist/adapters/cc-switch/common.js +61 -0
- package/dist/adapters/cc-switch/index.d.ts +25 -0
- package/dist/adapters/cc-switch/index.d.ts.map +1 -0
- package/dist/adapters/cc-switch/index.js +51 -0
- package/dist/adapters/cc-switch/mcp-claude.d.ts +15 -0
- package/dist/adapters/cc-switch/mcp-claude.d.ts.map +1 -0
- package/dist/adapters/cc-switch/mcp-claude.js +88 -0
- package/dist/adapters/cc-switch/mcp-claudecode.d.ts +15 -0
- package/dist/adapters/cc-switch/mcp-claudecode.d.ts.map +1 -0
- package/dist/adapters/cc-switch/mcp-claudecode.js +77 -0
- package/dist/adapters/cc-switch/mcp-codex.d.ts +17 -0
- package/dist/adapters/cc-switch/mcp-codex.d.ts.map +1 -0
- package/dist/adapters/cc-switch/mcp-codex.js +248 -0
- package/dist/adapters/cc-switch/mcp-codex.test.d.ts +2 -0
- package/dist/adapters/cc-switch/mcp-codex.test.d.ts.map +1 -0
- package/dist/adapters/cc-switch/mcp-codex.test.js +125 -0
- package/dist/adapters/cc-switch/mcp-opencode.d.ts +23 -0
- package/dist/adapters/cc-switch/mcp-opencode.d.ts.map +1 -0
- package/dist/adapters/cc-switch/mcp-opencode.js +100 -0
- package/dist/adapters/cc-switch/mcp-placeholder.d.ts +14 -0
- package/dist/adapters/cc-switch/mcp-placeholder.d.ts.map +1 -0
- package/dist/adapters/cc-switch/mcp-placeholder.js +19 -0
- package/dist/adapters/cc-switch/skill-claude.d.ts +15 -0
- package/dist/adapters/cc-switch/skill-claude.d.ts.map +1 -0
- package/dist/adapters/cc-switch/skill-claude.js +91 -0
- package/dist/adapters/cc-switch/skill-claudecode.d.ts +15 -0
- package/dist/adapters/cc-switch/skill-claudecode.d.ts.map +1 -0
- package/dist/adapters/cc-switch/skill-claudecode.js +91 -0
- package/dist/adapters/cc-switch/skill-opencode.d.ts +15 -0
- package/dist/adapters/cc-switch/skill-opencode.d.ts.map +1 -0
- package/dist/adapters/cc-switch/skill-opencode.js +95 -0
- package/dist/adapters/cc-switch/skill-placeholder.d.ts +14 -0
- package/dist/adapters/cc-switch/skill-placeholder.d.ts.map +1 -0
- package/dist/adapters/cc-switch/skill-placeholder.js +19 -0
- package/dist/config.d.ts +76 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +109 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +503 -0
- package/dist/middleware/auth.d.ts +12 -0
- package/dist/middleware/auth.d.ts.map +1 -0
- package/dist/middleware/auth.js +18 -0
- package/dist/routes/ai-sources.d.ts +2 -0
- package/dist/routes/ai-sources.d.ts.map +1 -0
- package/dist/routes/ai-sources.js +136 -0
- package/dist/routes/ai-sources.test.d.ts +2 -0
- package/dist/routes/ai-sources.test.d.ts.map +1 -0
- package/dist/routes/ai-sources.test.js +148 -0
- package/dist/routes/aws-mcp.d.ts +10 -0
- package/dist/routes/aws-mcp.d.ts.map +1 -0
- package/dist/routes/aws-mcp.js +74 -0
- package/dist/routes/aws-mcp.test.d.ts +2 -0
- package/dist/routes/aws-mcp.test.d.ts.map +1 -0
- package/dist/routes/aws-mcp.test.js +42 -0
- package/dist/routes/file-browser.d.ts +7 -0
- package/dist/routes/file-browser.d.ts.map +1 -0
- package/dist/routes/file-browser.js +227 -0
- package/dist/routes/file-browser.test.d.ts +5 -0
- package/dist/routes/file-browser.test.d.ts.map +1 -0
- package/dist/routes/file-browser.test.js +88 -0
- package/dist/routes/git.d.ts +7 -0
- package/dist/routes/git.d.ts.map +1 -0
- package/dist/routes/git.js +470 -0
- package/dist/routes/git.test.d.ts +5 -0
- package/dist/routes/git.test.d.ts.map +1 -0
- package/dist/routes/git.test.js +87 -0
- package/dist/routes/instance.d.ts +3 -0
- package/dist/routes/instance.d.ts.map +1 -0
- package/dist/routes/instance.js +170 -0
- package/dist/routes/instance.test.d.ts +5 -0
- package/dist/routes/instance.test.d.ts.map +1 -0
- package/dist/routes/instance.test.js +64 -0
- package/dist/routes/mcp.d.ts +2 -0
- package/dist/routes/mcp.d.ts.map +1 -0
- package/dist/routes/mcp.js +171 -0
- package/dist/routes/mcp.test.d.ts +5 -0
- package/dist/routes/mcp.test.d.ts.map +1 -0
- package/dist/routes/mcp.test.js +84 -0
- package/dist/routes/memory.d.ts +13 -0
- package/dist/routes/memory.d.ts.map +1 -0
- package/dist/routes/memory.js +429 -0
- package/dist/routes/processes.d.ts +7 -0
- package/dist/routes/processes.d.ts.map +1 -0
- package/dist/routes/processes.js +245 -0
- package/dist/routes/properties.d.ts +7 -0
- package/dist/routes/properties.d.ts.map +1 -0
- package/dist/routes/properties.js +72 -0
- package/dist/routes/properties.test.d.ts +5 -0
- package/dist/routes/properties.test.d.ts.map +1 -0
- package/dist/routes/properties.test.js +72 -0
- package/dist/routes/sessions.d.ts +7 -0
- package/dist/routes/sessions.d.ts.map +1 -0
- package/dist/routes/sessions.js +808 -0
- package/dist/routes/sessions.test.d.ts +5 -0
- package/dist/routes/sessions.test.d.ts.map +1 -0
- package/dist/routes/sessions.test.js +70 -0
- package/dist/routes/skills.d.ts +2 -0
- package/dist/routes/skills.d.ts.map +1 -0
- package/dist/routes/skills.js +162 -0
- package/dist/routes/skills.test.d.ts +5 -0
- package/dist/routes/skills.test.d.ts.map +1 -0
- package/dist/routes/skills.test.js +42 -0
- package/dist/routes/terminal.d.ts +18 -0
- package/dist/routes/terminal.d.ts.map +1 -0
- package/dist/routes/terminal.js +663 -0
- package/dist/routes/terminal.test.d.ts +2 -0
- package/dist/routes/terminal.test.d.ts.map +1 -0
- package/dist/routes/terminal.test.js +91 -0
- package/dist/routes/yml.d.ts +7 -0
- package/dist/routes/yml.d.ts.map +1 -0
- package/dist/routes/yml.js +223 -0
- package/dist/routes/yml.test.d.ts +5 -0
- package/dist/routes/yml.test.d.ts.map +1 -0
- package/dist/routes/yml.test.js +58 -0
- package/dist/services/agent-process-manager.d.ts +241 -0
- package/dist/services/agent-process-manager.d.ts.map +1 -0
- package/dist/services/agent-process-manager.js +762 -0
- package/dist/services/auto-register.d.ts +94 -0
- package/dist/services/auto-register.d.ts.map +1 -0
- package/dist/services/auto-register.js +510 -0
- package/dist/services/aws-client-agent-mcp.d.ts +26 -0
- package/dist/services/aws-client-agent-mcp.d.ts.map +1 -0
- package/dist/services/aws-client-agent-mcp.js +142 -0
- package/dist/services/aws-client-agent-mcp.test.d.ts +2 -0
- package/dist/services/aws-client-agent-mcp.test.d.ts.map +1 -0
- package/dist/services/aws-client-agent-mcp.test.js +89 -0
- package/dist/services/aws-mcp-http.d.ts +11 -0
- package/dist/services/aws-mcp-http.d.ts.map +1 -0
- package/dist/services/aws-mcp-http.js +225 -0
- package/dist/services/aws-mcp-http.test.d.ts +2 -0
- package/dist/services/aws-mcp-http.test.d.ts.map +1 -0
- package/dist/services/aws-mcp-http.test.js +27 -0
- package/dist/services/cc-switch-sdk.d.ts +18 -0
- package/dist/services/cc-switch-sdk.d.ts.map +1 -0
- package/dist/services/cc-switch-sdk.js +117 -0
- package/dist/services/easytier-manager.d.ts +106 -0
- package/dist/services/easytier-manager.d.ts.map +1 -0
- package/dist/services/easytier-manager.js +331 -0
- package/dist/services/easytier-manager.test.d.ts +5 -0
- package/dist/services/easytier-manager.test.d.ts.map +1 -0
- package/dist/services/easytier-manager.test.js +98 -0
- package/dist/services/instance-init-service.d.ts +35 -0
- package/dist/services/instance-init-service.d.ts.map +1 -0
- package/dist/services/instance-init-service.js +190 -0
- package/dist/services/instance-service.d.ts +88 -0
- package/dist/services/instance-service.d.ts.map +1 -0
- package/dist/services/instance-service.js +236 -0
- package/dist/services/instance-state.d.ts +36 -0
- package/dist/services/instance-state.d.ts.map +1 -0
- package/dist/services/instance-state.js +79 -0
- package/dist/services/instance-state.test.d.ts +2 -0
- package/dist/services/instance-state.test.d.ts.map +1 -0
- package/dist/services/instance-state.test.js +213 -0
- package/dist/services/memory-service.d.ts +195 -0
- package/dist/services/memory-service.d.ts.map +1 -0
- package/dist/services/memory-service.js +650 -0
- package/dist/services/orphan-monitor.d.ts +94 -0
- package/dist/services/orphan-monitor.d.ts.map +1 -0
- package/dist/services/orphan-monitor.js +321 -0
- package/dist/services/process-detector.d.ts +175 -0
- package/dist/services/process-detector.d.ts.map +1 -0
- package/dist/services/process-detector.js +992 -0
- package/dist/services/process-registry.d.ts +208 -0
- package/dist/services/process-registry.d.ts.map +1 -0
- package/dist/services/process-registry.js +354 -0
- package/dist/services/session-lookup.d.ts +20 -0
- package/dist/services/session-lookup.d.ts.map +1 -0
- package/dist/services/session-lookup.js +43 -0
- package/dist/services/session-output.d.ts +56 -0
- package/dist/services/session-output.d.ts.map +1 -0
- package/dist/services/session-output.js +122 -0
- package/dist/services/session-output.test.d.ts +5 -0
- package/dist/services/session-output.test.d.ts.map +1 -0
- package/dist/services/session-output.test.js +68 -0
- package/dist/services/terminal-persistence.d.ts +51 -0
- package/dist/services/terminal-persistence.d.ts.map +1 -0
- package/dist/services/terminal-persistence.js +125 -0
- package/dist/services/terminal-persistence.test.d.ts +5 -0
- package/dist/services/terminal-persistence.test.d.ts.map +1 -0
- package/dist/services/terminal-persistence.test.js +88 -0
- package/dist/services/tool-installer.d.ts +15 -0
- package/dist/services/tool-installer.d.ts.map +1 -0
- package/dist/services/tool-installer.js +297 -0
- package/dist/services/tool-installer.test.d.ts +2 -0
- package/dist/services/tool-installer.test.d.ts.map +1 -0
- package/dist/services/tool-installer.test.js +102 -0
- package/dist/services/user-api-key-service.d.ts +28 -0
- package/dist/services/user-api-key-service.d.ts.map +1 -0
- package/dist/services/user-api-key-service.js +75 -0
- package/dist/services/workspace-files.d.ts +85 -0
- package/dist/services/workspace-files.d.ts.map +1 -0
- package/dist/services/workspace-files.js +224 -0
- package/dist/services/workspace-files.test.d.ts +2 -0
- package/dist/services/workspace-files.test.d.ts.map +1 -0
- package/dist/services/workspace-files.test.js +117 -0
- package/dist/types.d.ts +233 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/dist/utils/file-utils.d.ts +13 -0
- package/dist/utils/file-utils.d.ts.map +1 -0
- package/dist/utils/file-utils.js +140 -0
- package/dist/utils/file-utils.test.d.ts +2 -0
- package/dist/utils/file-utils.test.d.ts.map +1 -0
- package/dist/utils/file-utils.test.js +201 -0
- package/dist/utils/logger.d.ts +39 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +110 -0
- package/dist/utils/logger.test.d.ts +2 -0
- package/dist/utils/logger.test.d.ts.map +1 -0
- package/dist/utils/logger.test.js +93 -0
- package/dist/utils/mcp-utils.d.ts +73 -0
- package/dist/utils/mcp-utils.d.ts.map +1 -0
- package/dist/utils/mcp-utils.js +165 -0
- package/dist/utils/path-utils.d.ts +24 -0
- package/dist/utils/path-utils.d.ts.map +1 -0
- package/dist/utils/path-utils.js +44 -0
- package/dist/utils/validation.d.ts +23 -0
- package/dist/utils/validation.d.ts.map +1 -0
- package/dist/utils/validation.js +53 -0
- package/dist/utils/validation.test.d.ts +2 -0
- package/dist/utils/validation.test.d.ts.map +1 -0
- package/dist/utils/validation.test.js +88 -0
- package/dist/utils/yaml-utils.d.ts +73 -0
- package/dist/utils/yaml-utils.d.ts.map +1 -0
- package/dist/utils/yaml-utils.js +309 -0
- package/dist/utils/yaml-utils.test.d.ts +2 -0
- package/dist/utils/yaml-utils.test.d.ts.map +1 -0
- package/dist/utils/yaml-utils.test.js +363 -0
- package/package/aws-client-agent-mcp/README.md +288 -0
- package/package/aws-client-agent-mcp/dist/activity-reporter.d.ts +150 -0
- package/package/aws-client-agent-mcp/dist/activity-reporter.d.ts.map +1 -0
- package/package/aws-client-agent-mcp/dist/activity-reporter.js +353 -0
- package/package/aws-client-agent-mcp/dist/activity-reporter.js.map +1 -0
- package/package/aws-client-agent-mcp/dist/activity-reporter.test.d.ts +2 -0
- package/package/aws-client-agent-mcp/dist/activity-reporter.test.d.ts.map +1 -0
- package/package/aws-client-agent-mcp/dist/activity-reporter.test.js +75 -0
- package/package/aws-client-agent-mcp/dist/activity-reporter.test.js.map +1 -0
- package/package/aws-client-agent-mcp/dist/activity-types.d.ts +96 -0
- package/package/aws-client-agent-mcp/dist/activity-types.d.ts.map +1 -0
- package/package/aws-client-agent-mcp/dist/activity-types.js +141 -0
- package/package/aws-client-agent-mcp/dist/activity-types.js.map +1 -0
- package/package/aws-client-agent-mcp/dist/agent-client.d.ts +165 -0
- package/package/aws-client-agent-mcp/dist/agent-client.d.ts.map +1 -0
- package/package/aws-client-agent-mcp/dist/agent-client.js +588 -0
- package/package/aws-client-agent-mcp/dist/agent-client.js.map +1 -0
- package/package/aws-client-agent-mcp/dist/agent-client.test.d.ts +2 -0
- package/package/aws-client-agent-mcp/dist/agent-client.test.d.ts.map +1 -0
- package/package/aws-client-agent-mcp/dist/agent-client.test.js +534 -0
- package/package/aws-client-agent-mcp/dist/agent-client.test.js.map +1 -0
- package/package/aws-client-agent-mcp/dist/config.d.ts +21 -0
- package/package/aws-client-agent-mcp/dist/config.d.ts.map +1 -0
- package/package/aws-client-agent-mcp/dist/config.js +67 -0
- package/package/aws-client-agent-mcp/dist/config.js.map +1 -0
- package/package/aws-client-agent-mcp/dist/config.test.d.ts +2 -0
- package/package/aws-client-agent-mcp/dist/config.test.d.ts.map +1 -0
- package/package/aws-client-agent-mcp/dist/config.test.js +139 -0
- package/package/aws-client-agent-mcp/dist/config.test.js.map +1 -0
- package/package/aws-client-agent-mcp/dist/constants.d.ts +15 -0
- package/package/aws-client-agent-mcp/dist/constants.d.ts.map +1 -0
- package/package/aws-client-agent-mcp/dist/constants.js +19 -0
- package/package/aws-client-agent-mcp/dist/constants.js.map +1 -0
- package/package/aws-client-agent-mcp/dist/http-client.d.ts +27 -0
- package/package/aws-client-agent-mcp/dist/http-client.d.ts.map +1 -0
- package/package/aws-client-agent-mcp/dist/http-client.js +65 -0
- package/package/aws-client-agent-mcp/dist/http-client.js.map +1 -0
- package/package/aws-client-agent-mcp/dist/http-client.test.d.ts +2 -0
- package/package/aws-client-agent-mcp/dist/http-client.test.d.ts.map +1 -0
- package/package/aws-client-agent-mcp/dist/http-client.test.js +228 -0
- package/package/aws-client-agent-mcp/dist/http-client.test.js.map +1 -0
- package/package/aws-client-agent-mcp/dist/index.d.ts +14 -0
- package/package/aws-client-agent-mcp/dist/index.d.ts.map +1 -0
- package/package/aws-client-agent-mcp/dist/index.js +30 -0
- package/package/aws-client-agent-mcp/dist/index.js.map +1 -0
- package/package/aws-client-agent-mcp/dist/logger.d.ts +7 -0
- package/package/aws-client-agent-mcp/dist/logger.d.ts.map +1 -0
- package/package/aws-client-agent-mcp/dist/logger.js +19 -0
- package/package/aws-client-agent-mcp/dist/logger.js.map +1 -0
- package/package/aws-client-agent-mcp/dist/mcp-server.d.ts +77 -0
- package/package/aws-client-agent-mcp/dist/mcp-server.d.ts.map +1 -0
- package/package/aws-client-agent-mcp/dist/mcp-server.js +427 -0
- package/package/aws-client-agent-mcp/dist/mcp-server.js.map +1 -0
- package/package/aws-client-agent-mcp/dist/mcp-server.test.d.ts +2 -0
- package/package/aws-client-agent-mcp/dist/mcp-server.test.d.ts.map +1 -0
- package/package/aws-client-agent-mcp/dist/mcp-server.test.js +624 -0
- package/package/aws-client-agent-mcp/dist/mcp-server.test.js.map +1 -0
- package/package/aws-client-agent-mcp/dist/mcp-tools.d.ts +78 -0
- package/package/aws-client-agent-mcp/dist/mcp-tools.d.ts.map +1 -0
- package/package/aws-client-agent-mcp/dist/mcp-tools.js +420 -0
- package/package/aws-client-agent-mcp/dist/mcp-tools.js.map +1 -0
- package/package/aws-client-agent-mcp/dist/memory-store.d.ts +61 -0
- package/package/aws-client-agent-mcp/dist/memory-store.d.ts.map +1 -0
- package/package/aws-client-agent-mcp/dist/memory-store.js +268 -0
- package/package/aws-client-agent-mcp/dist/memory-store.js.map +1 -0
- package/package/aws-client-agent-mcp/dist/memory-store.test.d.ts +2 -0
- package/package/aws-client-agent-mcp/dist/memory-store.test.d.ts.map +1 -0
- package/package/aws-client-agent-mcp/dist/memory-store.test.js +164 -0
- package/package/aws-client-agent-mcp/dist/memory-store.test.js.map +1 -0
- package/package/aws-client-agent-mcp/dist/message-buffer.d.ts +74 -0
- package/package/aws-client-agent-mcp/dist/message-buffer.d.ts.map +1 -0
- package/package/aws-client-agent-mcp/dist/message-buffer.js +159 -0
- package/package/aws-client-agent-mcp/dist/message-buffer.js.map +1 -0
- package/package/aws-client-agent-mcp/dist/message-buffer.test.d.ts +2 -0
- package/package/aws-client-agent-mcp/dist/message-buffer.test.d.ts.map +1 -0
- package/package/aws-client-agent-mcp/dist/message-buffer.test.js +44 -0
- package/package/aws-client-agent-mcp/dist/message-buffer.test.js.map +1 -0
- package/package/aws-client-agent-mcp/dist/messageContent.d.ts +53 -0
- package/package/aws-client-agent-mcp/dist/messageContent.d.ts.map +1 -0
- package/package/aws-client-agent-mcp/dist/messageContent.js +125 -0
- package/package/aws-client-agent-mcp/dist/messageContent.js.map +1 -0
- package/package/aws-client-agent-mcp/dist/orchestration-tools.d.ts +19 -0
- package/package/aws-client-agent-mcp/dist/orchestration-tools.d.ts.map +1 -0
- package/package/aws-client-agent-mcp/dist/orchestration-tools.js +317 -0
- package/package/aws-client-agent-mcp/dist/orchestration-tools.js.map +1 -0
- package/package/aws-client-agent-mcp/dist/status-reporter.d.ts +66 -0
- package/package/aws-client-agent-mcp/dist/status-reporter.d.ts.map +1 -0
- package/package/aws-client-agent-mcp/dist/status-reporter.js +220 -0
- package/package/aws-client-agent-mcp/dist/status-reporter.js.map +1 -0
- package/package/aws-client-agent-mcp/dist/status-reporter.test.d.ts +2 -0
- package/package/aws-client-agent-mcp/dist/status-reporter.test.d.ts.map +1 -0
- package/package/aws-client-agent-mcp/dist/status-reporter.test.js +45 -0
- package/package/aws-client-agent-mcp/dist/status-reporter.test.js.map +1 -0
- package/package/aws-client-agent-mcp/dist/types.d.ts +286 -0
- package/package/aws-client-agent-mcp/dist/types.d.ts.map +1 -0
- package/package/aws-client-agent-mcp/dist/types.js +9 -0
- package/package/aws-client-agent-mcp/dist/types.js.map +1 -0
- package/package/aws-client-agent-mcp/dist/user-config-reader.d.ts +63 -0
- package/package/aws-client-agent-mcp/dist/user-config-reader.d.ts.map +1 -0
- package/package/aws-client-agent-mcp/dist/user-config-reader.js +161 -0
- package/package/aws-client-agent-mcp/dist/user-config-reader.js.map +1 -0
- package/package/aws-client-agent-mcp/dist/websocket-client.d.ts +94 -0
- package/package/aws-client-agent-mcp/dist/websocket-client.d.ts.map +1 -0
- package/package/aws-client-agent-mcp/dist/websocket-client.js +316 -0
- package/package/aws-client-agent-mcp/dist/websocket-client.js.map +1 -0
- package/package/aws-client-agent-mcp/dist/websocket-client.test.d.ts +2 -0
- package/package/aws-client-agent-mcp/dist/websocket-client.test.d.ts.map +1 -0
- package/package/aws-client-agent-mcp/dist/websocket-client.test.js +191 -0
- package/package/aws-client-agent-mcp/dist/websocket-client.test.js.map +1 -0
- package/package/aws-client-agent-mcp/package.json +51 -0
- package/package/cc-switch-sdk/README.md +541 -0
- package/package/cc-switch-sdk/dist/adapters/common.d.ts +38 -0
- package/package/cc-switch-sdk/dist/adapters/common.d.ts.map +1 -0
- package/package/cc-switch-sdk/dist/adapters/common.js +47 -0
- package/package/cc-switch-sdk/dist/adapters/index.d.ts +5 -0
- package/package/cc-switch-sdk/dist/adapters/index.d.ts.map +1 -0
- package/package/cc-switch-sdk/dist/adapters/index.js +28 -0
- package/package/cc-switch-sdk/dist/adapters/mcp-claude.d.ts +10 -0
- package/package/cc-switch-sdk/dist/adapters/mcp-claude.d.ts.map +1 -0
- package/package/cc-switch-sdk/dist/adapters/mcp-claude.js +39 -0
- package/package/cc-switch-sdk/dist/adapters/mcp-claudecode.d.ts +10 -0
- package/package/cc-switch-sdk/dist/adapters/mcp-claudecode.d.ts.map +1 -0
- package/package/cc-switch-sdk/dist/adapters/mcp-claudecode.js +40 -0
- package/package/cc-switch-sdk/dist/adapters/mcp-opencode.d.ts +18 -0
- package/package/cc-switch-sdk/dist/adapters/mcp-opencode.d.ts.map +1 -0
- package/package/cc-switch-sdk/dist/adapters/mcp-opencode.js +63 -0
- package/package/cc-switch-sdk/dist/adapters/mcp-opencode.test.d.ts +2 -0
- package/package/cc-switch-sdk/dist/adapters/mcp-opencode.test.d.ts.map +1 -0
- package/package/cc-switch-sdk/dist/adapters/mcp-opencode.test.js +86 -0
- package/package/cc-switch-sdk/dist/adapters/mcp-placeholder.d.ts +9 -0
- package/package/cc-switch-sdk/dist/adapters/mcp-placeholder.d.ts.map +1 -0
- package/package/cc-switch-sdk/dist/adapters/mcp-placeholder.js +14 -0
- package/package/cc-switch-sdk/dist/adapters/skill-claude.d.ts +10 -0
- package/package/cc-switch-sdk/dist/adapters/skill-claude.d.ts.map +1 -0
- package/package/cc-switch-sdk/dist/adapters/skill-claude.js +51 -0
- package/package/cc-switch-sdk/dist/adapters/skill-claudecode.d.ts +10 -0
- package/package/cc-switch-sdk/dist/adapters/skill-claudecode.d.ts.map +1 -0
- package/package/cc-switch-sdk/dist/adapters/skill-claudecode.js +51 -0
- package/package/cc-switch-sdk/dist/adapters/skill-opencode.d.ts +10 -0
- package/package/cc-switch-sdk/dist/adapters/skill-opencode.d.ts.map +1 -0
- package/package/cc-switch-sdk/dist/adapters/skill-opencode.js +51 -0
- package/package/cc-switch-sdk/dist/adapters/skill-placeholder.d.ts +9 -0
- package/package/cc-switch-sdk/dist/adapters/skill-placeholder.d.ts.map +1 -0
- package/package/cc-switch-sdk/dist/adapters/skill-placeholder.js +14 -0
- package/package/cc-switch-sdk/dist/constants.d.ts +9 -0
- package/package/cc-switch-sdk/dist/constants.d.ts.map +1 -0
- package/package/cc-switch-sdk/dist/constants.js +54 -0
- package/package/cc-switch-sdk/dist/errors.d.ts +6 -0
- package/package/cc-switch-sdk/dist/errors.d.ts.map +1 -0
- package/package/cc-switch-sdk/dist/errors.js +8 -0
- package/package/cc-switch-sdk/dist/index.d.ts +11 -0
- package/package/cc-switch-sdk/dist/index.d.ts.map +1 -0
- package/package/cc-switch-sdk/dist/index.js +9 -0
- package/package/cc-switch-sdk/dist/schemas.d.ts +8 -0
- package/package/cc-switch-sdk/dist/schemas.d.ts.map +1 -0
- package/package/cc-switch-sdk/dist/schemas.js +37 -0
- package/package/cc-switch-sdk/dist/sdk.d.ts +91 -0
- package/package/cc-switch-sdk/dist/sdk.d.ts.map +1 -0
- package/package/cc-switch-sdk/dist/sdk.js +427 -0
- package/package/cc-switch-sdk/dist/services/ai-config-service.d.ts +75 -0
- package/package/cc-switch-sdk/dist/services/ai-config-service.d.ts.map +1 -0
- package/package/cc-switch-sdk/dist/services/ai-config-service.js +280 -0
- package/package/cc-switch-sdk/dist/services/instance-service.d.ts +78 -0
- package/package/cc-switch-sdk/dist/services/instance-service.d.ts.map +1 -0
- package/package/cc-switch-sdk/dist/services/instance-service.js +180 -0
- package/package/cc-switch-sdk/dist/services/mcp-model.d.ts +17 -0
- package/package/cc-switch-sdk/dist/services/mcp-model.d.ts.map +1 -0
- package/package/cc-switch-sdk/dist/services/mcp-model.js +34 -0
- package/package/cc-switch-sdk/dist/services/mcp-service.d.ts +18 -0
- package/package/cc-switch-sdk/dist/services/mcp-service.d.ts.map +1 -0
- package/package/cc-switch-sdk/dist/services/mcp-service.js +9 -0
- package/package/cc-switch-sdk/dist/services/skill-model.d.ts +17 -0
- package/package/cc-switch-sdk/dist/services/skill-model.d.ts.map +1 -0
- package/package/cc-switch-sdk/dist/services/skill-model.js +38 -0
- package/package/cc-switch-sdk/dist/services/skill-service.d.ts +17 -0
- package/package/cc-switch-sdk/dist/services/skill-service.d.ts.map +1 -0
- package/package/cc-switch-sdk/dist/services/skill-service.js +9 -0
- package/package/cc-switch-sdk/dist/state.d.ts +4 -0
- package/package/cc-switch-sdk/dist/state.d.ts.map +1 -0
- package/package/cc-switch-sdk/dist/state.js +19 -0
- package/package/cc-switch-sdk/dist/types.d.ts +75 -0
- package/package/cc-switch-sdk/dist/types.d.ts.map +1 -0
- package/package/cc-switch-sdk/dist/types.js +1 -0
- package/package/cc-switch-sdk/dist/utils/fs.d.ts +10 -0
- package/package/cc-switch-sdk/dist/utils/fs.d.ts.map +1 -0
- package/package/cc-switch-sdk/dist/utils/fs.js +91 -0
- package/package/cc-switch-sdk/dist/utils/id.d.ts +4 -0
- package/package/cc-switch-sdk/dist/utils/id.d.ts.map +1 -0
- package/package/cc-switch-sdk/dist/utils/id.js +12 -0
- package/package/cc-switch-sdk/package.json +31 -0
- package/package.json +73 -0
|
@@ -0,0 +1,1140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Code Agent SDK Adapter
|
|
3
|
+
*
|
|
4
|
+
* 通过 @anthropic-ai/claude-agent-sdk 的 query() API
|
|
5
|
+
* 实现与 Claude Code 的结构化交互。
|
|
6
|
+
*
|
|
7
|
+
* 核心特性:
|
|
8
|
+
* - 支持 autoAccept 模式自动确认所有权限
|
|
9
|
+
* - 支持 AskUserQuestion 拦截并转发到聊天面板
|
|
10
|
+
* - 支持 turn_complete 事件精确判断 AI 就绪状态
|
|
11
|
+
*/
|
|
12
|
+
import { EventEmitter } from 'events';
|
|
13
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
14
|
+
import * as fs from 'fs';
|
|
15
|
+
import * as path from 'path';
|
|
16
|
+
import { execSync } from 'child_process';
|
|
17
|
+
import { getToolActionInfo } from './types.js';
|
|
18
|
+
// ============ AsyncIterableQueue ============
|
|
19
|
+
/**
|
|
20
|
+
* 可排队的 AsyncIterable,用于向 SDK query() 输入流推送用户消息
|
|
21
|
+
*/
|
|
22
|
+
class AsyncIterableQueue {
|
|
23
|
+
constructor() {
|
|
24
|
+
this.queue = [];
|
|
25
|
+
this.resolve = null;
|
|
26
|
+
this.done = false;
|
|
27
|
+
}
|
|
28
|
+
enqueue(item) {
|
|
29
|
+
if (this.done)
|
|
30
|
+
return;
|
|
31
|
+
if (this.resolve) {
|
|
32
|
+
const r = this.resolve;
|
|
33
|
+
this.resolve = null;
|
|
34
|
+
r({ value: item, done: false });
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
this.queue.push(item);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
close() {
|
|
41
|
+
this.done = true;
|
|
42
|
+
if (this.resolve) {
|
|
43
|
+
const r = this.resolve;
|
|
44
|
+
this.resolve = null;
|
|
45
|
+
r({ value: undefined, done: true });
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
[Symbol.asyncIterator]() {
|
|
49
|
+
return {
|
|
50
|
+
next: () => {
|
|
51
|
+
if (this.queue.length > 0) {
|
|
52
|
+
return Promise.resolve({ value: this.queue.shift(), done: false });
|
|
53
|
+
}
|
|
54
|
+
if (this.done) {
|
|
55
|
+
return Promise.resolve({ value: undefined, done: true });
|
|
56
|
+
}
|
|
57
|
+
return new Promise((resolve) => {
|
|
58
|
+
this.resolve = resolve;
|
|
59
|
+
});
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
// ============ ClaudeSdkAdapter ============
|
|
65
|
+
/**
|
|
66
|
+
* Claude Code Agent SDK Adapter
|
|
67
|
+
*/
|
|
68
|
+
export class ClaudeSdkAdapter extends EventEmitter {
|
|
69
|
+
constructor() {
|
|
70
|
+
super(...arguments);
|
|
71
|
+
this.providerId = 'claude-code';
|
|
72
|
+
this.displayName = 'Claude Code';
|
|
73
|
+
this.sessions = new Map();
|
|
74
|
+
this.sdkQueries = new Map();
|
|
75
|
+
this.inputStreams = new Map();
|
|
76
|
+
this.abortControllers = new Map();
|
|
77
|
+
this.sdkModule = null;
|
|
78
|
+
// 挂起的权限请求
|
|
79
|
+
this.pendingPermissions = new Map();
|
|
80
|
+
// 挂起的 AskUserQuestion
|
|
81
|
+
this.pendingQuestions = new Map();
|
|
82
|
+
// 正在处理的工具调用 ID(用于去重)
|
|
83
|
+
this.activeToolUseIds = new Map(); // sessionId -> toolUseId
|
|
84
|
+
// 最近处理的工具名(用于 stream_event 和 tool_use 的去重)
|
|
85
|
+
this.recentToolNames = new Map(); // sessionId -> { name, time }
|
|
86
|
+
// 收集工具参数的缓冲区(用于 stream_event 模式)
|
|
87
|
+
this.toolInputBuffers = new Map(); // "${sessionId}:${toolUseId}" -> JSON string
|
|
88
|
+
// ============ 空闲命令管理 ============
|
|
89
|
+
this.idleCommands = new Map();
|
|
90
|
+
this.idleTimers = new Map();
|
|
91
|
+
this.lastActivityAt = new Map();
|
|
92
|
+
}
|
|
93
|
+
// ============ 生命周期方法 ============
|
|
94
|
+
async startSession(sessionId, config) {
|
|
95
|
+
// ★ 特征日志:确认使用的是 Claude Code Adapter
|
|
96
|
+
console.log(`[ClaudeSdkAdapter] ★★★ 启动 Claude Code SDK 会话 ★★★ sessionId="${sessionId}", command="${config.command}", workingDirectory="${config.workingDirectory}"`);
|
|
97
|
+
const session = {
|
|
98
|
+
sessionId,
|
|
99
|
+
status: 'starting',
|
|
100
|
+
messages: [],
|
|
101
|
+
createdAt: new Date().toISOString(),
|
|
102
|
+
totalUsage: { inputTokens: 0, outputTokens: 0 },
|
|
103
|
+
};
|
|
104
|
+
this.sessions.set(sessionId, session);
|
|
105
|
+
try {
|
|
106
|
+
const sdk = await this.loadSdk();
|
|
107
|
+
const abortController = new AbortController();
|
|
108
|
+
this.abortControllers.set(sessionId, abortController);
|
|
109
|
+
// 创建输入流
|
|
110
|
+
const inputStream = new AsyncIterableQueue();
|
|
111
|
+
this.inputStreams.set(sessionId, inputStream);
|
|
112
|
+
// 构建选项
|
|
113
|
+
const options = this.buildQueryOptions(sessionId, config, abortController);
|
|
114
|
+
// 启动 SDK query
|
|
115
|
+
const sdkQuery = sdk.query({
|
|
116
|
+
prompt: inputStream,
|
|
117
|
+
options,
|
|
118
|
+
});
|
|
119
|
+
this.sdkQueries.set(sessionId, sdkQuery);
|
|
120
|
+
// 启动流消费(异步,不阻塞)
|
|
121
|
+
this.consumeStream(sessionId, sdkQuery).catch(err => {
|
|
122
|
+
console.error(`[ClaudeSdkAdapter] Stream error for ${sessionId}:`, err);
|
|
123
|
+
});
|
|
124
|
+
// ★ 等待一小段时间让 SDK 进程启动
|
|
125
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
126
|
+
// 发送初始 prompt
|
|
127
|
+
if (config.initialPrompt) {
|
|
128
|
+
// 有初始 prompt,启动后会进入思考状态
|
|
129
|
+
session.status = 'starting';
|
|
130
|
+
this.emit('status-change', sessionId, 'starting');
|
|
131
|
+
const userMsg = {
|
|
132
|
+
id: uuidv4(),
|
|
133
|
+
sessionId,
|
|
134
|
+
role: 'user',
|
|
135
|
+
content: config.initialPrompt,
|
|
136
|
+
timestamp: new Date().toISOString(),
|
|
137
|
+
};
|
|
138
|
+
session.messages.push(userMsg);
|
|
139
|
+
this.emit('conversation-message', sessionId, userMsg);
|
|
140
|
+
// ★ 解析初始 prompt 中的图片标记
|
|
141
|
+
const initialContent = this.parseMessageContent(config.initialPrompt);
|
|
142
|
+
inputStream.enqueue({
|
|
143
|
+
type: 'user',
|
|
144
|
+
message: {
|
|
145
|
+
role: 'user',
|
|
146
|
+
content: initialContent,
|
|
147
|
+
},
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
// 没有初始 prompt,等待用户输入
|
|
152
|
+
session.status = 'waiting_input';
|
|
153
|
+
this.emit('status-change', sessionId, 'waiting_input');
|
|
154
|
+
// 如果有空闲命令配置,立即启动空闲检测
|
|
155
|
+
if (this.idleCommands.has(sessionId)) {
|
|
156
|
+
this.startIdleDetection(sessionId);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
catch (error) {
|
|
161
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
162
|
+
session.status = 'error';
|
|
163
|
+
this.emit('status-change', sessionId, 'error');
|
|
164
|
+
this.emitEvent(sessionId, {
|
|
165
|
+
type: 'error',
|
|
166
|
+
sessionId,
|
|
167
|
+
timestamp: new Date().toISOString(),
|
|
168
|
+
data: { text: `Failed to start session: ${errorMessage}` },
|
|
169
|
+
});
|
|
170
|
+
throw error;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
async sendMessage(sessionId, message) {
|
|
174
|
+
const session = this.sessions.get(sessionId);
|
|
175
|
+
const inputStream = this.inputStreams.get(sessionId);
|
|
176
|
+
if (!session || !inputStream) {
|
|
177
|
+
throw new Error(`Session ${sessionId} not found`);
|
|
178
|
+
}
|
|
179
|
+
if (session.status === 'error') {
|
|
180
|
+
throw new Error(`Session ${sessionId} is in error state`);
|
|
181
|
+
}
|
|
182
|
+
// 记录用户消息
|
|
183
|
+
const userMsg = {
|
|
184
|
+
id: uuidv4(),
|
|
185
|
+
sessionId,
|
|
186
|
+
role: 'user',
|
|
187
|
+
content: message,
|
|
188
|
+
timestamp: new Date().toISOString(),
|
|
189
|
+
};
|
|
190
|
+
session.messages.push(userMsg);
|
|
191
|
+
this.emit('conversation-message', sessionId, userMsg);
|
|
192
|
+
// 更新状态 - 用户发送消息后,AI 将开始处理
|
|
193
|
+
session.status = 'thinking';
|
|
194
|
+
this.emit('status-change', sessionId, 'thinking');
|
|
195
|
+
// ★ 解析消息中的图片标记,构造多模态内容
|
|
196
|
+
const content = this.parseMessageContent(message);
|
|
197
|
+
// 推送到输入流
|
|
198
|
+
inputStream.enqueue({
|
|
199
|
+
type: 'user',
|
|
200
|
+
message: {
|
|
201
|
+
role: 'user',
|
|
202
|
+
content,
|
|
203
|
+
},
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* 解析消息内容,提取图片和文本
|
|
208
|
+
* 支持 [图片: url] 标记格式
|
|
209
|
+
*/
|
|
210
|
+
parseMessageContent(message) {
|
|
211
|
+
const content = [];
|
|
212
|
+
// 匹配 [图片: url] 格式
|
|
213
|
+
const imageTagRegex = /\[图片\s*:\s*([^\]\n]+?)\s*\]/gi;
|
|
214
|
+
let lastIndex = 0;
|
|
215
|
+
let match = imageTagRegex.exec(message);
|
|
216
|
+
while (match !== null) {
|
|
217
|
+
// 添加图片之前的文本
|
|
218
|
+
if (match.index > lastIndex) {
|
|
219
|
+
const text = message.slice(lastIndex, match.index).trim();
|
|
220
|
+
if (text) {
|
|
221
|
+
content.push({ type: 'text', text });
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
// 添加图片
|
|
225
|
+
const imageUrl = match[1].trim();
|
|
226
|
+
if (imageUrl) {
|
|
227
|
+
console.log(`[ClaudeSdkAdapter] 添加图片 URL: ${imageUrl}`);
|
|
228
|
+
content.push({
|
|
229
|
+
type: 'image',
|
|
230
|
+
source: {
|
|
231
|
+
type: 'url',
|
|
232
|
+
url: imageUrl,
|
|
233
|
+
},
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
lastIndex = match.index + match[0].length;
|
|
237
|
+
match = imageTagRegex.exec(message);
|
|
238
|
+
}
|
|
239
|
+
// 添加最后的文本
|
|
240
|
+
if (lastIndex < message.length) {
|
|
241
|
+
const text = message.slice(lastIndex).trim();
|
|
242
|
+
if (text) {
|
|
243
|
+
content.push({ type: 'text', text });
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
// 如果没有找到任何内容,返回原始消息作为文本
|
|
247
|
+
if (content.length === 0) {
|
|
248
|
+
content.push({ type: 'text', text: message });
|
|
249
|
+
}
|
|
250
|
+
return content;
|
|
251
|
+
}
|
|
252
|
+
async sendConfirmation(sessionId, accept) {
|
|
253
|
+
const pending = this.pendingPermissions.get(sessionId);
|
|
254
|
+
if (pending) {
|
|
255
|
+
if (accept) {
|
|
256
|
+
pending.resolve({ behavior: 'allow', updatedInput: pending.toolInput });
|
|
257
|
+
}
|
|
258
|
+
else {
|
|
259
|
+
pending.resolve({ behavior: 'deny', message: 'User denied' });
|
|
260
|
+
}
|
|
261
|
+
this.pendingPermissions.delete(sessionId);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
async sendQuestionAnswer(sessionId, answers) {
|
|
265
|
+
const pending = this.pendingQuestions.get(sessionId);
|
|
266
|
+
if (!pending)
|
|
267
|
+
return;
|
|
268
|
+
const questions = pending.toolInput.questions;
|
|
269
|
+
let answersText = '用户已回答了您的问题:\n';
|
|
270
|
+
if (Array.isArray(questions)) {
|
|
271
|
+
questions.forEach((q, i) => {
|
|
272
|
+
const key = String(i);
|
|
273
|
+
const answer = answers[key] || answers[q.header || ''] || answers[q.question] || '(未填写)';
|
|
274
|
+
answersText += `• ${q.header || q.question}:${answer}\n`;
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
else {
|
|
278
|
+
answersText += JSON.stringify(answers);
|
|
279
|
+
}
|
|
280
|
+
pending.resolve({ behavior: 'deny', message: answersText });
|
|
281
|
+
this.pendingQuestions.delete(sessionId);
|
|
282
|
+
}
|
|
283
|
+
async abortCurrentTurn(sessionId) {
|
|
284
|
+
const abortController = this.abortControllers.get(sessionId);
|
|
285
|
+
if (abortController) {
|
|
286
|
+
abortController.abort();
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
async terminateSession(sessionId) {
|
|
290
|
+
const session = this.sessions.get(sessionId);
|
|
291
|
+
const sdkQuery = this.sdkQueries.get(sessionId);
|
|
292
|
+
const inputStream = this.inputStreams.get(sessionId);
|
|
293
|
+
if (inputStream) {
|
|
294
|
+
inputStream.close();
|
|
295
|
+
}
|
|
296
|
+
if (sdkQuery) {
|
|
297
|
+
try {
|
|
298
|
+
sdkQuery.close();
|
|
299
|
+
}
|
|
300
|
+
catch {
|
|
301
|
+
// ignore
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
if (session) {
|
|
305
|
+
session.status = 'terminated';
|
|
306
|
+
this.emit('status-change', sessionId, 'terminated');
|
|
307
|
+
this.emitEvent(sessionId, {
|
|
308
|
+
type: 'session_complete',
|
|
309
|
+
sessionId,
|
|
310
|
+
timestamp: new Date().toISOString(),
|
|
311
|
+
data: { exitCode: 0 },
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
this.cleanupSession(sessionId);
|
|
315
|
+
}
|
|
316
|
+
getConversation(sessionId) {
|
|
317
|
+
return this.sessions.get(sessionId)?.messages ?? [];
|
|
318
|
+
}
|
|
319
|
+
hasSession(sessionId) {
|
|
320
|
+
return this.sessions.has(sessionId);
|
|
321
|
+
}
|
|
322
|
+
getProviderSessionId(sessionId) {
|
|
323
|
+
return this.sessions.get(sessionId)?.providerSessionId;
|
|
324
|
+
}
|
|
325
|
+
getSessionStatus(sessionId) {
|
|
326
|
+
return this.sessions.get(sessionId)?.status;
|
|
327
|
+
}
|
|
328
|
+
getSessionPid(sessionId) {
|
|
329
|
+
return this.sessions.get(sessionId)?.pid;
|
|
330
|
+
}
|
|
331
|
+
cleanup() {
|
|
332
|
+
for (const sessionId of this.sessions.keys()) {
|
|
333
|
+
try {
|
|
334
|
+
this.terminateSession(sessionId);
|
|
335
|
+
}
|
|
336
|
+
catch {
|
|
337
|
+
// ignore
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
this.sessions.clear();
|
|
341
|
+
this.sdkQueries.clear();
|
|
342
|
+
this.inputStreams.clear();
|
|
343
|
+
this.abortControllers.clear();
|
|
344
|
+
this.pendingPermissions.clear();
|
|
345
|
+
this.pendingQuestions.clear();
|
|
346
|
+
this.idleCommands.clear();
|
|
347
|
+
this.idleTimers.clear();
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* 设置空闲命令配置
|
|
351
|
+
*/
|
|
352
|
+
setIdleCommands(sessionId, commands) {
|
|
353
|
+
this.idleCommands.set(sessionId, commands);
|
|
354
|
+
// 如果当前状态是 waiting_input,立即启动空闲检测
|
|
355
|
+
const session = this.sessions.get(sessionId);
|
|
356
|
+
if (session && session.status === 'waiting_input' && (commands.idleInputCommand || commands.nonInputCommand)) {
|
|
357
|
+
this.startIdleDetection(sessionId);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* 启动空闲检测定时器
|
|
362
|
+
*/
|
|
363
|
+
startIdleDetection(sessionId) {
|
|
364
|
+
// 清除现有定时器
|
|
365
|
+
this.stopIdleDetection(sessionId);
|
|
366
|
+
const commands = this.idleCommands.get(sessionId);
|
|
367
|
+
if (!commands?.idleInputCommand) {
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
if (this.isTaskPollingIdleCommand(commands.idleInputCommand)) {
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
this.lastActivityAt.set(sessionId, Date.now());
|
|
374
|
+
// 每500ms检测一次空闲状态
|
|
375
|
+
const timer = setInterval(() => {
|
|
376
|
+
this.checkIdleState(sessionId);
|
|
377
|
+
}, 500);
|
|
378
|
+
this.idleTimers.set(sessionId, timer);
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* 停止空闲检测定时器
|
|
382
|
+
*/
|
|
383
|
+
stopIdleDetection(sessionId) {
|
|
384
|
+
const timer = this.idleTimers.get(sessionId);
|
|
385
|
+
if (timer) {
|
|
386
|
+
clearInterval(timer);
|
|
387
|
+
this.idleTimers.delete(sessionId);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
/**
|
|
391
|
+
* 检测空闲状态并执行命令
|
|
392
|
+
*/
|
|
393
|
+
checkIdleState(sessionId) {
|
|
394
|
+
const session = this.sessions.get(sessionId);
|
|
395
|
+
if (!session || session.status !== 'waiting_input') {
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
const commands = this.idleCommands.get(sessionId);
|
|
399
|
+
if (!commands || (!commands.idleInputCommand && !commands.nonInputCommand)) {
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
const lastActivity = this.lastActivityAt.get(sessionId) || 0;
|
|
403
|
+
const idleTime = Date.now() - lastActivity;
|
|
404
|
+
// 空闲1.5秒后执行空闲命令
|
|
405
|
+
const IDLE_THRESHOLD = 1500;
|
|
406
|
+
if (idleTime < IDLE_THRESHOLD) {
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
409
|
+
// 标记活动时间,避免重复执行
|
|
410
|
+
this.lastActivityAt.set(sessionId, Date.now());
|
|
411
|
+
// SDK 模式:空闲时发送一个简单的提示消息让 AI 调用 poll_message
|
|
412
|
+
// 实际的消息获取由 AI 通过 MCP 工具 poll_message 完成
|
|
413
|
+
if (commands.idleInputCommand) {
|
|
414
|
+
// 解析空闲命令,提取实际要执行的指令
|
|
415
|
+
const command = commands.idleInputCommand;
|
|
416
|
+
// 如果空闲命令包含 poll_message,发送一个提示让 AI 执行
|
|
417
|
+
if (this.isMessagePollingIdleCommand(command)) {
|
|
418
|
+
this.sendIdlePrompt(sessionId, '请使用 poll_message 工具检查是否有新消息,如果有的话处理它们。');
|
|
419
|
+
}
|
|
420
|
+
else {
|
|
421
|
+
// 其他命令,直接作为消息发送给 AI
|
|
422
|
+
this.sendIdlePrompt(sessionId, command);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
/**
|
|
427
|
+
* 发送空闲提示到 SDK
|
|
428
|
+
*/
|
|
429
|
+
sendIdlePrompt(sessionId, prompt) {
|
|
430
|
+
const inputStream = this.inputStreams.get(sessionId);
|
|
431
|
+
if (!inputStream)
|
|
432
|
+
return;
|
|
433
|
+
// 更新状态为思考中
|
|
434
|
+
const session = this.sessions.get(sessionId);
|
|
435
|
+
if (session) {
|
|
436
|
+
session.status = 'thinking';
|
|
437
|
+
this.emit('status-change', sessionId, 'thinking');
|
|
438
|
+
}
|
|
439
|
+
// 发送空闲提示
|
|
440
|
+
inputStream.enqueue({
|
|
441
|
+
type: 'user',
|
|
442
|
+
message: {
|
|
443
|
+
role: 'user',
|
|
444
|
+
content: [{ type: 'text', text: prompt }],
|
|
445
|
+
},
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
isTaskPollingIdleCommand(command) {
|
|
449
|
+
const normalized = command.trim();
|
|
450
|
+
if (!normalized) {
|
|
451
|
+
return false;
|
|
452
|
+
}
|
|
453
|
+
return normalized.toLowerCase().includes('my_task') || normalized.includes('流程任务');
|
|
454
|
+
}
|
|
455
|
+
isMessagePollingIdleCommand(command) {
|
|
456
|
+
const normalized = command.trim();
|
|
457
|
+
if (!normalized) {
|
|
458
|
+
return false;
|
|
459
|
+
}
|
|
460
|
+
return normalized.toLowerCase().includes('poll_message') || normalized.includes('取消息');
|
|
461
|
+
}
|
|
462
|
+
/**
|
|
463
|
+
* 执行空闲命令(支持 [enter], [wait:n] 等 token)
|
|
464
|
+
*/
|
|
465
|
+
async executeIdleCommand(sessionId, command) {
|
|
466
|
+
const inputStream = this.inputStreams.get(sessionId);
|
|
467
|
+
if (!inputStream)
|
|
468
|
+
return;
|
|
469
|
+
// 解析命令 token
|
|
470
|
+
const tokenPattern = /\[(.+?)\]/g;
|
|
471
|
+
let cursor = 0;
|
|
472
|
+
const tokens = [];
|
|
473
|
+
let match = tokenPattern.exec(command);
|
|
474
|
+
while (match !== null) {
|
|
475
|
+
if (match.index > cursor) {
|
|
476
|
+
tokens.push({ type: 'text', value: command.slice(cursor, match.index) });
|
|
477
|
+
}
|
|
478
|
+
const token = match[1];
|
|
479
|
+
if (token.startsWith('wait:')) {
|
|
480
|
+
const seconds = parseFloat(token.slice(5));
|
|
481
|
+
if (!isNaN(seconds)) {
|
|
482
|
+
tokens.push({ type: 'wait', value: seconds * 1000 });
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
else {
|
|
486
|
+
// 映射按键 token
|
|
487
|
+
const keyMap = {
|
|
488
|
+
'enter': '\r',
|
|
489
|
+
'up': '\x1b[A',
|
|
490
|
+
'down': '\x1b[B',
|
|
491
|
+
'left': '\x1b[D',
|
|
492
|
+
'right': '\x1b[C',
|
|
493
|
+
'tab': '\t',
|
|
494
|
+
'esc': '\x1b',
|
|
495
|
+
'escape': '\x1b',
|
|
496
|
+
'space': ' ',
|
|
497
|
+
'ctrl+c': '\x03',
|
|
498
|
+
};
|
|
499
|
+
tokens.push({ type: 'key', value: keyMap[token.toLowerCase()] || token });
|
|
500
|
+
}
|
|
501
|
+
cursor = match.index + match[0].length;
|
|
502
|
+
match = tokenPattern.exec(command);
|
|
503
|
+
}
|
|
504
|
+
if (cursor < command.length) {
|
|
505
|
+
tokens.push({ type: 'text', value: command.slice(cursor) });
|
|
506
|
+
}
|
|
507
|
+
// 执行 tokens
|
|
508
|
+
for (const token of tokens) {
|
|
509
|
+
if (token.type === 'wait') {
|
|
510
|
+
await new Promise(resolve => setTimeout(resolve, token.value));
|
|
511
|
+
}
|
|
512
|
+
else {
|
|
513
|
+
const text = String(token.value);
|
|
514
|
+
inputStream.enqueue({
|
|
515
|
+
type: 'user',
|
|
516
|
+
message: {
|
|
517
|
+
role: 'user',
|
|
518
|
+
content: [{ type: 'text', text }],
|
|
519
|
+
},
|
|
520
|
+
});
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
/**
|
|
525
|
+
* 更新活动时间(在收到输出或状态变化时调用)
|
|
526
|
+
*/
|
|
527
|
+
updateActivity(sessionId) {
|
|
528
|
+
this.lastActivityAt.set(sessionId, Date.now());
|
|
529
|
+
}
|
|
530
|
+
/**
|
|
531
|
+
* 恢复之前的会话
|
|
532
|
+
* 注意:SDK 的 resume 功能需要 providerSessionId
|
|
533
|
+
*/
|
|
534
|
+
async resumeSession(sessionId, providerSessionId, config) {
|
|
535
|
+
// 目前简化实现:重新启动一个新会话
|
|
536
|
+
// TODO: 使用 SDK 的 resume API 恢复会话上下文
|
|
537
|
+
const session = {
|
|
538
|
+
sessionId,
|
|
539
|
+
providerSessionId,
|
|
540
|
+
status: 'starting',
|
|
541
|
+
messages: [],
|
|
542
|
+
createdAt: new Date().toISOString(),
|
|
543
|
+
totalUsage: { inputTokens: 0, outputTokens: 0 },
|
|
544
|
+
};
|
|
545
|
+
this.sessions.set(sessionId, session);
|
|
546
|
+
try {
|
|
547
|
+
await this.startSession(sessionId, config);
|
|
548
|
+
session.providerSessionId = providerSessionId;
|
|
549
|
+
}
|
|
550
|
+
catch (error) {
|
|
551
|
+
session.status = 'error';
|
|
552
|
+
this.emit('status-change', sessionId, 'error');
|
|
553
|
+
throw error;
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
// ============ 私有方法 ============
|
|
557
|
+
async loadSdk() {
|
|
558
|
+
if (!this.sdkModule) {
|
|
559
|
+
try {
|
|
560
|
+
this.sdkModule = await import('@anthropic-ai/claude-agent-sdk');
|
|
561
|
+
}
|
|
562
|
+
catch (err) {
|
|
563
|
+
throw new Error(`Failed to load @anthropic-ai/claude-agent-sdk. Please install: npm install @anthropic-ai/claude-agent-sdk. Original error: ${err}`);
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
return this.sdkModule;
|
|
567
|
+
}
|
|
568
|
+
/**
|
|
569
|
+
* 查找 Claude Code 可执行文件路径
|
|
570
|
+
* 支持 Windows / macOS / Linux
|
|
571
|
+
*/
|
|
572
|
+
findClaudeExecutable(command) {
|
|
573
|
+
const normalizedCommand = (command || 'claude').trim() || 'claude';
|
|
574
|
+
// 1. 如果是绝对路径,直接返回
|
|
575
|
+
if (path.isAbsolute(normalizedCommand) && fs.existsSync(normalizedCommand)) {
|
|
576
|
+
return normalizedCommand;
|
|
577
|
+
}
|
|
578
|
+
// 2. Windows 特定路径(优先级高)
|
|
579
|
+
if (process.platform === 'win32') {
|
|
580
|
+
const userProfile = process.env.USERPROFILE || '';
|
|
581
|
+
const localAppData = process.env.LOCALAPPDATA || '';
|
|
582
|
+
const appData = process.env.APPDATA || '';
|
|
583
|
+
// Volta 安装路径(优先检查)
|
|
584
|
+
const voltaCandidates = [
|
|
585
|
+
path.join(localAppData, 'Volta', 'tools', 'shared', '@anthropic-ai', 'claude-code', 'cli.js'),
|
|
586
|
+
path.join(localAppData, 'Volta', 'tools', 'image', 'packages', '@anthropic-ai', 'claude-code', 'cli.js'),
|
|
587
|
+
];
|
|
588
|
+
for (const p of voltaCandidates) {
|
|
589
|
+
if (fs.existsSync(p)) {
|
|
590
|
+
console.log(`[ClaudeSdkAdapter] Found Claude Code CLI via Volta: ${p}`);
|
|
591
|
+
return p;
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
const candidates = [
|
|
595
|
+
path.join(appData, 'npm', 'node_modules', '@anthropic-ai', 'claude-code', 'cli.js'),
|
|
596
|
+
path.join(localAppData, 'Programs', 'claude-code', 'claude.exe'),
|
|
597
|
+
path.join(userProfile, '.local', 'bin', 'claude.exe'),
|
|
598
|
+
];
|
|
599
|
+
for (const p of candidates) {
|
|
600
|
+
if (p && fs.existsSync(p)) {
|
|
601
|
+
console.log(`[ClaudeSdkAdapter] Found Claude Code CLI: ${p}`);
|
|
602
|
+
return p;
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
// 3. 使用 which/where 查找
|
|
607
|
+
try {
|
|
608
|
+
const findCmd = process.platform === 'win32' ? `where ${normalizedCommand}` : `which ${normalizedCommand}`;
|
|
609
|
+
const result = execSync(findCmd, { encoding: 'utf8', timeout: 5000 }).trim();
|
|
610
|
+
if (result) {
|
|
611
|
+
const foundPath = result.split(/\r?\n/)[0].trim();
|
|
612
|
+
if (fs.existsSync(foundPath)) {
|
|
613
|
+
// 如果是 wrapper 脚本,尝试找到实际的 cli.js
|
|
614
|
+
if (foundPath.endsWith('.cmd') || foundPath.endsWith('.ps1')) {
|
|
615
|
+
const cliJs = this.resolveCliJsFromWrapper(foundPath);
|
|
616
|
+
if (cliJs)
|
|
617
|
+
return cliJs;
|
|
618
|
+
}
|
|
619
|
+
return foundPath;
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
catch {
|
|
624
|
+
// 忽略查找失败
|
|
625
|
+
}
|
|
626
|
+
// 4. macOS/Linux 特定路径
|
|
627
|
+
const home = process.env.HOME || '';
|
|
628
|
+
const candidates = [
|
|
629
|
+
`/usr/local/lib/node_modules/@anthropic-ai/claude-code/cli.js`,
|
|
630
|
+
`/usr/lib/node_modules/@anthropic-ai/claude-code/cli.js`,
|
|
631
|
+
path.join(home, '.local', 'share', 'volta', 'tools', 'shared', '@anthropic-ai', 'claude-code', 'cli.js'),
|
|
632
|
+
path.join(home, '.volta', 'tools', 'shared', '@anthropic-ai', 'claude-code', 'cli.js'),
|
|
633
|
+
];
|
|
634
|
+
for (const p of candidates) {
|
|
635
|
+
if (p && fs.existsSync(p)) {
|
|
636
|
+
console.log(`[ClaudeSdkAdapter] Found Claude Code CLI: ${p}`);
|
|
637
|
+
return p;
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
// 5. 尝试 npm root -g
|
|
641
|
+
try {
|
|
642
|
+
const npmRoot = execSync('npm root -g', { encoding: 'utf8', timeout: 5000 }).trim();
|
|
643
|
+
const cliJs = path.join(npmRoot, '@anthropic-ai', 'claude-code', 'cli.js');
|
|
644
|
+
if (fs.existsSync(cliJs)) {
|
|
645
|
+
console.log(`[ClaudeSdkAdapter] Found Claude Code CLI via npm root: ${cliJs}`);
|
|
646
|
+
return cliJs;
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
catch {
|
|
650
|
+
// 忽略
|
|
651
|
+
}
|
|
652
|
+
console.warn(`[ClaudeSdkAdapter] Claude Code executable not found for command: ${normalizedCommand}`);
|
|
653
|
+
return undefined;
|
|
654
|
+
}
|
|
655
|
+
/**
|
|
656
|
+
* 从 wrapper 脚本解析 cli.js 路径
|
|
657
|
+
*/
|
|
658
|
+
resolveCliJsFromWrapper(wrapperPath) {
|
|
659
|
+
try {
|
|
660
|
+
const content = fs.readFileSync(wrapperPath, 'utf8');
|
|
661
|
+
// Volta wrapper: volta run %~n0 %*
|
|
662
|
+
if (content.includes('volta run')) {
|
|
663
|
+
const voltaShared = path.join(process.env.LOCALAPPDATA || '', 'Volta', 'tools', 'shared', '@anthropic-ai', 'claude-code', 'cli.js');
|
|
664
|
+
if (fs.existsSync(voltaShared)) {
|
|
665
|
+
return voltaShared;
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
catch {
|
|
670
|
+
// 忽略
|
|
671
|
+
}
|
|
672
|
+
return undefined;
|
|
673
|
+
}
|
|
674
|
+
buildQueryOptions(sessionId, config, abortController) {
|
|
675
|
+
const options = {
|
|
676
|
+
cwd: config.workingDirectory,
|
|
677
|
+
abortController,
|
|
678
|
+
settingSources: ['user', 'project', 'local'],
|
|
679
|
+
// 启用部分消息以获取增量输出
|
|
680
|
+
includePartialMessages: true,
|
|
681
|
+
};
|
|
682
|
+
// ★ 关键:指定 Claude Code 可执行文件路径
|
|
683
|
+
// SDK 需要这个来启动 Claude Code CLI
|
|
684
|
+
const execPath = this.findClaudeExecutable(config.command);
|
|
685
|
+
if (execPath) {
|
|
686
|
+
options.pathToClaudeCodeExecutable = execPath;
|
|
687
|
+
// ★ 特征日志:明确显示使用的是 Claude Code 可执行文件
|
|
688
|
+
console.log(`[ClaudeSdkAdapter] ★★★ 使用 Claude Code 可执行文件 ★★★ path="${execPath}"`);
|
|
689
|
+
}
|
|
690
|
+
else {
|
|
691
|
+
console.warn(`[ClaudeSdkAdapter] ★★★ 警告:未找到 Claude Code 可执行文件 ★★★ command="${config.command}"`);
|
|
692
|
+
}
|
|
693
|
+
// 模型配置
|
|
694
|
+
if (config.model) {
|
|
695
|
+
options.model = config.model;
|
|
696
|
+
}
|
|
697
|
+
// 最大轮次
|
|
698
|
+
if (config.maxTurns && config.maxTurns > 0) {
|
|
699
|
+
options.maxTurns = config.maxTurns;
|
|
700
|
+
}
|
|
701
|
+
// MCP 服务器
|
|
702
|
+
if (config.mcpConfigPath) {
|
|
703
|
+
try {
|
|
704
|
+
const raw = fs.readFileSync(config.mcpConfigPath, 'utf-8');
|
|
705
|
+
const parsed = JSON.parse(raw);
|
|
706
|
+
options.mcpServers = { ...config.extraMcpServers, ...parsed.mcpServers };
|
|
707
|
+
}
|
|
708
|
+
catch {
|
|
709
|
+
options.mcpServers = config.extraMcpServers || {};
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
else {
|
|
713
|
+
options.mcpServers = config.extraMcpServers || {};
|
|
714
|
+
}
|
|
715
|
+
// 允许的工具
|
|
716
|
+
if (config.allowedTools && config.allowedTools.length > 0) {
|
|
717
|
+
options.allowedTools = config.allowedTools;
|
|
718
|
+
}
|
|
719
|
+
else {
|
|
720
|
+
options.allowedTools = [
|
|
721
|
+
'Read',
|
|
722
|
+
'Write',
|
|
723
|
+
'Edit',
|
|
724
|
+
'Bash',
|
|
725
|
+
'Glob',
|
|
726
|
+
'Grep',
|
|
727
|
+
'WebSearch',
|
|
728
|
+
'WebFetch',
|
|
729
|
+
'Task',
|
|
730
|
+
'NotebookEdit',
|
|
731
|
+
'LSP',
|
|
732
|
+
'Skill',
|
|
733
|
+
'mcp__*',
|
|
734
|
+
];
|
|
735
|
+
}
|
|
736
|
+
// 环境变量
|
|
737
|
+
options.env = { ...process.env, ...config.envOverrides };
|
|
738
|
+
// 权限模式 - 使用 canUseTool 回调统一处理权限,不使用 bypassPermissions
|
|
739
|
+
// 原因:bypassPermissions 模式下 SDK 可能跳过 canUseTool 回调,
|
|
740
|
+
// 导致 AskUserQuestion 等需要用户交互的工具无法被拦截,前端收不到提问事件。
|
|
741
|
+
// 改为通过 canUseTool 回调实现 autoAccept 逻辑:
|
|
742
|
+
// - AskUserQuestion → 始终拦截,转发到前端等待用户回答
|
|
743
|
+
// - autoAccept 普通工具 → 直接允许
|
|
744
|
+
// - 非 autoAccept 普通工具 → 询问用户
|
|
745
|
+
options.canUseTool = this.createPermissionHandler(sessionId, config.autoAccept);
|
|
746
|
+
return options;
|
|
747
|
+
}
|
|
748
|
+
createPermissionHandler(sessionId, autoAccept) {
|
|
749
|
+
return async (toolName, toolInput, _options) => {
|
|
750
|
+
// AskUserQuestion 特殊处理:始终拦截,转发到前端等待用户回答
|
|
751
|
+
if (toolName === 'AskUserQuestion' || toolName === 'ask_user_question') {
|
|
752
|
+
return new Promise((resolve) => {
|
|
753
|
+
this.pendingQuestions.set(sessionId, { resolve, toolInput });
|
|
754
|
+
this.emit('ask-user-question', {
|
|
755
|
+
sessionId,
|
|
756
|
+
questions: toolInput.questions,
|
|
757
|
+
});
|
|
758
|
+
});
|
|
759
|
+
}
|
|
760
|
+
// autoAccept 模式直接允许普通工具
|
|
761
|
+
// 返回 { behavior: 'allow', updatedInput } 格式
|
|
762
|
+
if (autoAccept) {
|
|
763
|
+
return { behavior: 'allow', updatedInput: toolInput };
|
|
764
|
+
}
|
|
765
|
+
// 非 autoAccept 模式,询问用户
|
|
766
|
+
return new Promise((resolve) => {
|
|
767
|
+
this.pendingPermissions.set(sessionId, { resolve, toolInput });
|
|
768
|
+
this.emit('permission-request', {
|
|
769
|
+
sessionId,
|
|
770
|
+
toolName,
|
|
771
|
+
toolInput,
|
|
772
|
+
permissionPrompt: `Allow ${toolName}?`,
|
|
773
|
+
});
|
|
774
|
+
});
|
|
775
|
+
};
|
|
776
|
+
}
|
|
777
|
+
async consumeStream(sessionId, sdkQuery) {
|
|
778
|
+
const session = this.sessions.get(sessionId);
|
|
779
|
+
if (!session)
|
|
780
|
+
return;
|
|
781
|
+
try {
|
|
782
|
+
for await (const msg of sdkQuery) {
|
|
783
|
+
const sdkMsg = msg;
|
|
784
|
+
this.handleSdkMessage(sessionId, session, sdkMsg);
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
catch (error) {
|
|
788
|
+
if (this.abortControllers.get(sessionId)?.signal.aborted) {
|
|
789
|
+
// 主动中止,正常结束
|
|
790
|
+
return;
|
|
791
|
+
}
|
|
792
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
793
|
+
this.emitEvent(sessionId, {
|
|
794
|
+
type: 'error',
|
|
795
|
+
sessionId,
|
|
796
|
+
timestamp: new Date().toISOString(),
|
|
797
|
+
data: { text: `Stream error: ${errorMessage}` },
|
|
798
|
+
});
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
handleSdkMessage(sessionId, session, msg) {
|
|
802
|
+
const timestamp = new Date().toISOString();
|
|
803
|
+
// ★ 调试日志:记录所有收到的消息类型
|
|
804
|
+
console.log(`[ClaudeSdkAdapter] Received message type: "${msg.type}", subtype: "${msg.subtype || ''}"`);
|
|
805
|
+
// ★ 如果有工具名,也记录下来
|
|
806
|
+
if (msg.tool_name) {
|
|
807
|
+
console.log(`[ClaudeSdkAdapter] Tool name: "${msg.tool_name}"`);
|
|
808
|
+
}
|
|
809
|
+
switch (msg.type) {
|
|
810
|
+
case 'text':
|
|
811
|
+
// AI 正在输出文本
|
|
812
|
+
if (msg.delta) {
|
|
813
|
+
// 更新状态为思考中(如果有文本输出)
|
|
814
|
+
if (session.status !== 'thinking') {
|
|
815
|
+
session.status = 'thinking';
|
|
816
|
+
this.emit('status-change', sessionId, 'thinking');
|
|
817
|
+
this.stopIdleDetection(sessionId);
|
|
818
|
+
}
|
|
819
|
+
this.emitEvent(sessionId, {
|
|
820
|
+
type: 'text_delta',
|
|
821
|
+
sessionId,
|
|
822
|
+
timestamp,
|
|
823
|
+
data: { text: msg.delta },
|
|
824
|
+
});
|
|
825
|
+
}
|
|
826
|
+
break;
|
|
827
|
+
case 'thinking':
|
|
828
|
+
// AI 正在思考/推理
|
|
829
|
+
if (session.status !== 'thinking') {
|
|
830
|
+
session.status = 'thinking';
|
|
831
|
+
this.emit('status-change', sessionId, 'thinking');
|
|
832
|
+
this.stopIdleDetection(sessionId);
|
|
833
|
+
}
|
|
834
|
+
if (msg.content || msg.delta) {
|
|
835
|
+
this.emitEvent(sessionId, {
|
|
836
|
+
type: 'thinking',
|
|
837
|
+
sessionId,
|
|
838
|
+
timestamp,
|
|
839
|
+
data: { text: msg.delta || msg.content },
|
|
840
|
+
});
|
|
841
|
+
}
|
|
842
|
+
break;
|
|
843
|
+
case 'tool_use':
|
|
844
|
+
case 'tool_use_start':
|
|
845
|
+
// AI 正在调用工具
|
|
846
|
+
{
|
|
847
|
+
const actionInfo = getToolActionInfo(msg.tool_name, msg.tool_input);
|
|
848
|
+
session.status = 'tool_using';
|
|
849
|
+
this.emit('status-change', sessionId, 'tool_using', {
|
|
850
|
+
actionType: actionInfo.actionType,
|
|
851
|
+
actionLabel: actionInfo.actionLabel,
|
|
852
|
+
actionDetail: actionInfo.actionDetail,
|
|
853
|
+
});
|
|
854
|
+
this.stopIdleDetection(sessionId);
|
|
855
|
+
this.handleToolUse(sessionId, session, msg, timestamp);
|
|
856
|
+
}
|
|
857
|
+
break;
|
|
858
|
+
case 'tool_result':
|
|
859
|
+
case 'tool_use_end':
|
|
860
|
+
this.emitEvent(sessionId, {
|
|
861
|
+
type: 'tool_use_end',
|
|
862
|
+
sessionId,
|
|
863
|
+
timestamp,
|
|
864
|
+
data: {
|
|
865
|
+
toolName: msg.tool_name,
|
|
866
|
+
toolResult: msg.tool_result,
|
|
867
|
+
isError: msg.is_error,
|
|
868
|
+
toolUseId: msg.tool_use_id,
|
|
869
|
+
},
|
|
870
|
+
});
|
|
871
|
+
break;
|
|
872
|
+
case 'result':
|
|
873
|
+
// ★ SDK 的 turn_complete 事件是 'result' 类型
|
|
874
|
+
{
|
|
875
|
+
const isSuccess = msg.subtype === 'success';
|
|
876
|
+
if (msg.usage) {
|
|
877
|
+
session.totalUsage.inputTokens = msg.usage.input_tokens || session.totalUsage.inputTokens;
|
|
878
|
+
session.totalUsage.outputTokens = msg.usage.output_tokens || session.totalUsage.outputTokens;
|
|
879
|
+
}
|
|
880
|
+
// 发送 turn_complete 事件
|
|
881
|
+
this.emitEvent(sessionId, {
|
|
882
|
+
type: 'turn_complete',
|
|
883
|
+
sessionId,
|
|
884
|
+
timestamp,
|
|
885
|
+
data: {
|
|
886
|
+
usage: session.totalUsage,
|
|
887
|
+
text: isSuccess ? msg.result : undefined,
|
|
888
|
+
},
|
|
889
|
+
});
|
|
890
|
+
// ★ 更新状态为等待输入
|
|
891
|
+
session.status = 'waiting_input';
|
|
892
|
+
this.emit('status-change', sessionId, 'waiting_input');
|
|
893
|
+
// 如果有空闲命令配置,启动空闲检测
|
|
894
|
+
if (this.idleCommands.has(sessionId)) {
|
|
895
|
+
this.startIdleDetection(sessionId);
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
break;
|
|
899
|
+
case 'system':
|
|
900
|
+
// 系统消息,可能包含初始化数据
|
|
901
|
+
if (msg.subtype === 'init') {
|
|
902
|
+
console.log(`[ClaudeSdkAdapter] Session ${sessionId} initialized: model=${msg.model}, tools=${msg.tools?.length || 0}`);
|
|
903
|
+
}
|
|
904
|
+
break;
|
|
905
|
+
case 'assistant':
|
|
906
|
+
// AI 响应完成,可能包含完整消息
|
|
907
|
+
if (msg.message?.content) {
|
|
908
|
+
const textContent = msg.message.content
|
|
909
|
+
.filter((c) => c.type === 'text' && c.text)
|
|
910
|
+
.map((c) => c.text || '')
|
|
911
|
+
.join('');
|
|
912
|
+
if (textContent) {
|
|
913
|
+
session.messages.push({
|
|
914
|
+
id: uuidv4(),
|
|
915
|
+
sessionId,
|
|
916
|
+
role: 'assistant',
|
|
917
|
+
content: textContent,
|
|
918
|
+
timestamp,
|
|
919
|
+
});
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
break;
|
|
923
|
+
case 'user':
|
|
924
|
+
// 用户消息(工具结果)
|
|
925
|
+
if (msg.tool_use_result !== undefined) {
|
|
926
|
+
// 工具执行结果
|
|
927
|
+
}
|
|
928
|
+
break;
|
|
929
|
+
case 'error':
|
|
930
|
+
session.status = 'error';
|
|
931
|
+
this.emit('status-change', sessionId, 'error');
|
|
932
|
+
this.emitEvent(sessionId, {
|
|
933
|
+
type: 'error',
|
|
934
|
+
sessionId,
|
|
935
|
+
timestamp,
|
|
936
|
+
data: { text: msg.content || 'Unknown error' },
|
|
937
|
+
});
|
|
938
|
+
break;
|
|
939
|
+
case 'stream_event':
|
|
940
|
+
// SDK 流式事件 - 处理工具调用的增量更新
|
|
941
|
+
this.handleStreamEvent(sessionId, session, msg, timestamp);
|
|
942
|
+
break;
|
|
943
|
+
default:
|
|
944
|
+
// 未知消息类型,记录日志
|
|
945
|
+
console.log(`[ClaudeSdkAdapter] Unknown message type: ${msg.type}`, msg);
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
handleToolUse(sessionId, session, msg, timestamp) {
|
|
949
|
+
// 检查是否与 stream_event 处理的工具调用匹配(去重)
|
|
950
|
+
const recentInfo = this.recentToolNames.get(sessionId);
|
|
951
|
+
const toolNameSuffix = this.extractToolNameSuffix(msg.tool_name || '');
|
|
952
|
+
if (recentInfo) {
|
|
953
|
+
const timeDiff = Date.now() - recentInfo.time;
|
|
954
|
+
// 如果在 30s 内,且工具名后缀相同,认为是同一个调用(content_block_stop 已发出事件)
|
|
955
|
+
if (timeDiff < 30000 && recentInfo.name === toolNameSuffix) {
|
|
956
|
+
console.log(`[ClaudeSdkAdapter] Tool use ${msg.tool_name} matches recent stream_event (${recentInfo.name}), skipping duplicate event`);
|
|
957
|
+
// 清理记录
|
|
958
|
+
this.recentToolNames.delete(sessionId);
|
|
959
|
+
this.activeToolUseIds.delete(sessionId);
|
|
960
|
+
// 只记录到消息历史,不重复发送事件(content_block_stop 已经发过)
|
|
961
|
+
session.messages.push({
|
|
962
|
+
id: uuidv4(),
|
|
963
|
+
sessionId,
|
|
964
|
+
role: 'tool_use',
|
|
965
|
+
content: `${msg.tool_name}: ${JSON.stringify(msg.tool_input).slice(0, 100)}`,
|
|
966
|
+
timestamp,
|
|
967
|
+
toolName: msg.tool_name,
|
|
968
|
+
toolInput: msg.tool_input,
|
|
969
|
+
toolUseId: msg.tool_use_id,
|
|
970
|
+
});
|
|
971
|
+
return;
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
// 清理过期记录
|
|
975
|
+
this.recentToolNames.delete(sessionId);
|
|
976
|
+
this.activeToolUseIds.delete(sessionId);
|
|
977
|
+
// 获取工具动作分类信息
|
|
978
|
+
const actionInfo = getToolActionInfo(msg.tool_name, msg.tool_input);
|
|
979
|
+
console.log(`[ClaudeSdkAdapter] Tool use: ${msg.tool_name}, actionType: ${actionInfo.actionType}, actionDetail: ${actionInfo.actionDetail}`);
|
|
980
|
+
this.emitEvent(sessionId, {
|
|
981
|
+
type: 'tool_use_start',
|
|
982
|
+
sessionId,
|
|
983
|
+
timestamp,
|
|
984
|
+
data: {
|
|
985
|
+
toolName: msg.tool_name,
|
|
986
|
+
toolInput: msg.tool_input,
|
|
987
|
+
toolUseId: msg.tool_use_id,
|
|
988
|
+
actionType: actionInfo.actionType,
|
|
989
|
+
actionLabel: actionInfo.actionLabel,
|
|
990
|
+
actionDetail: actionInfo.actionDetail,
|
|
991
|
+
},
|
|
992
|
+
});
|
|
993
|
+
// 记录到消息历史
|
|
994
|
+
session.messages.push({
|
|
995
|
+
id: uuidv4(),
|
|
996
|
+
sessionId,
|
|
997
|
+
role: 'tool_use',
|
|
998
|
+
content: `${msg.tool_name}: ${JSON.stringify(msg.tool_input).slice(0, 100)}`,
|
|
999
|
+
timestamp,
|
|
1000
|
+
toolName: msg.tool_name,
|
|
1001
|
+
toolInput: msg.tool_input,
|
|
1002
|
+
toolUseId: msg.tool_use_id,
|
|
1003
|
+
});
|
|
1004
|
+
}
|
|
1005
|
+
/**
|
|
1006
|
+
* 处理 SDK 流式事件
|
|
1007
|
+
* 用于检测工具调用开始、增量更新和结束
|
|
1008
|
+
*
|
|
1009
|
+
* 注意:此方法只更新状态,不发送时间线事件
|
|
1010
|
+
* 时间线事件由 handleToolUse 发送,这样能获取完整的工具参数
|
|
1011
|
+
*/
|
|
1012
|
+
handleStreamEvent(sessionId, session, msg, timestamp) {
|
|
1013
|
+
const event = msg.event;
|
|
1014
|
+
if (!event)
|
|
1015
|
+
return;
|
|
1016
|
+
// 处理 content_block_start 事件 - 工具调用开始
|
|
1017
|
+
if (event.type === 'content_block_start') {
|
|
1018
|
+
const contentBlock = event.content_block;
|
|
1019
|
+
if (contentBlock?.type === 'tool_use' && contentBlock.name) {
|
|
1020
|
+
const toolName = contentBlock.name;
|
|
1021
|
+
const toolId = contentBlock.id || `stream_${Date.now()}`;
|
|
1022
|
+
console.log(`[ClaudeSdkAdapter] Tool use started via stream_event: ${toolName} (id: ${toolId})`);
|
|
1023
|
+
// 记录当前活跃的工具调用 ID(用于去重)
|
|
1024
|
+
this.activeToolUseIds.set(sessionId, toolId);
|
|
1025
|
+
// 记录工具名(用于与后续 tool_use 消息匹配去重)
|
|
1026
|
+
// 提取工具名后缀:aws-mcp__get_profile -> get_profile
|
|
1027
|
+
const toolNameSuffix = this.extractToolNameSuffix(toolName);
|
|
1028
|
+
this.recentToolNames.set(sessionId, { name: toolNameSuffix, time: Date.now() });
|
|
1029
|
+
// 初始化参数缓冲区
|
|
1030
|
+
const bufferKey = `${sessionId}:${toolId}`;
|
|
1031
|
+
this.toolInputBuffers.set(bufferKey, '');
|
|
1032
|
+
// 先不更新状态,等待收集参数后更新
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
// 处理 content_block_delta 事件 - 收集工具参数增量
|
|
1036
|
+
if (event.type === 'content_block_delta') {
|
|
1037
|
+
if (event.delta?.type === 'input_json_delta' && event.delta.partial_json) {
|
|
1038
|
+
const toolId = this.activeToolUseIds.get(sessionId);
|
|
1039
|
+
if (toolId) {
|
|
1040
|
+
const bufferKey = `${sessionId}:${toolId}`;
|
|
1041
|
+
const currentBuffer = this.toolInputBuffers.get(bufferKey) || '';
|
|
1042
|
+
this.toolInputBuffers.set(bufferKey, currentBuffer + event.delta.partial_json);
|
|
1043
|
+
console.log(`[ClaudeSdkAdapter] Tool input delta at index ${event.index}, buffer length: ${currentBuffer.length + event.delta.partial_json.length}`);
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
// 处理 content_block_stop 事件 - 工具调用参数收集完成
|
|
1048
|
+
if (event.type === 'content_block_stop') {
|
|
1049
|
+
const toolId = this.activeToolUseIds.get(sessionId);
|
|
1050
|
+
const recentInfo = this.recentToolNames.get(sessionId);
|
|
1051
|
+
if (toolId && recentInfo) {
|
|
1052
|
+
const bufferKey = `${sessionId}:${toolId}`;
|
|
1053
|
+
const jsonStr = this.toolInputBuffers.get(bufferKey) || '';
|
|
1054
|
+
console.log(`[ClaudeSdkAdapter] Content block stopped at index ${event.index}, collected JSON length: ${jsonStr.length}`);
|
|
1055
|
+
// 尝试解析完整的工具参数
|
|
1056
|
+
let toolInput;
|
|
1057
|
+
if (jsonStr) {
|
|
1058
|
+
try {
|
|
1059
|
+
toolInput = JSON.parse(jsonStr);
|
|
1060
|
+
console.log(`[ClaudeSdkAdapter] Parsed tool input:`, JSON.stringify(toolInput).slice(0, 200));
|
|
1061
|
+
}
|
|
1062
|
+
catch (e) {
|
|
1063
|
+
console.warn(`[ClaudeSdkAdapter] Failed to parse tool input JSON:`, jsonStr.slice(0, 100));
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1066
|
+
// 获取工具动作分类信息(使用完整参数)
|
|
1067
|
+
const actionInfo = getToolActionInfo(recentInfo.name, toolInput);
|
|
1068
|
+
// 更新状态
|
|
1069
|
+
session.status = 'tool_using';
|
|
1070
|
+
this.emit('status-change', sessionId, 'tool_using', {
|
|
1071
|
+
actionType: actionInfo.actionType,
|
|
1072
|
+
actionLabel: actionInfo.actionLabel,
|
|
1073
|
+
actionDetail: actionInfo.actionDetail,
|
|
1074
|
+
});
|
|
1075
|
+
this.stopIdleDetection(sessionId);
|
|
1076
|
+
// 发送工具调用开始事件(此时有完整参数)
|
|
1077
|
+
this.emitEvent(sessionId, {
|
|
1078
|
+
type: 'tool_use_start',
|
|
1079
|
+
sessionId,
|
|
1080
|
+
timestamp,
|
|
1081
|
+
data: {
|
|
1082
|
+
toolName: recentInfo.name,
|
|
1083
|
+
toolInput,
|
|
1084
|
+
toolUseId: toolId,
|
|
1085
|
+
actionType: actionInfo.actionType,
|
|
1086
|
+
actionLabel: actionInfo.actionLabel,
|
|
1087
|
+
actionDetail: actionInfo.actionDetail,
|
|
1088
|
+
},
|
|
1089
|
+
});
|
|
1090
|
+
// 记录到消息历史
|
|
1091
|
+
session.messages.push({
|
|
1092
|
+
id: uuidv4(),
|
|
1093
|
+
sessionId,
|
|
1094
|
+
role: 'tool_use',
|
|
1095
|
+
content: `${recentInfo.name}: ${jsonStr.slice(0, 100)}`,
|
|
1096
|
+
timestamp,
|
|
1097
|
+
toolName: recentInfo.name,
|
|
1098
|
+
toolInput,
|
|
1099
|
+
toolUseId: toolId,
|
|
1100
|
+
});
|
|
1101
|
+
// 清理缓冲区(保留 recentToolNames 用于后续 tool_use 消息去重)
|
|
1102
|
+
this.toolInputBuffers.delete(bufferKey);
|
|
1103
|
+
this.activeToolUseIds.delete(sessionId);
|
|
1104
|
+
// 注意:不在此处删除 recentToolNames,让后续 tool_use 消息能匹配去重
|
|
1105
|
+
// recentToolNames 会在 handleToolUse 中匹配成功后删除,或过期后自动清理
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
/**
|
|
1110
|
+
* 提取工具名后缀
|
|
1111
|
+
* 用于匹配不同格式的工具名
|
|
1112
|
+
* 例如:aws-mcp__get_profile -> get_profile
|
|
1113
|
+
*/
|
|
1114
|
+
extractToolNameSuffix(toolName) {
|
|
1115
|
+
// 如果包含 __,取最后一部分
|
|
1116
|
+
if (toolName.includes('__')) {
|
|
1117
|
+
return toolName.split('__').pop() || toolName;
|
|
1118
|
+
}
|
|
1119
|
+
// 如果包含 -mcp__,取后面的部分
|
|
1120
|
+
if (toolName.includes('-mcp__')) {
|
|
1121
|
+
const idx = toolName.indexOf('-mcp__');
|
|
1122
|
+
return toolName.substring(idx + 6);
|
|
1123
|
+
}
|
|
1124
|
+
return toolName;
|
|
1125
|
+
}
|
|
1126
|
+
emitEvent(sessionId, event) {
|
|
1127
|
+
this.emit('event', event);
|
|
1128
|
+
}
|
|
1129
|
+
cleanupSession(sessionId) {
|
|
1130
|
+
this.stopIdleDetection(sessionId);
|
|
1131
|
+
this.sessions.delete(sessionId);
|
|
1132
|
+
this.sdkQueries.delete(sessionId);
|
|
1133
|
+
this.inputStreams.delete(sessionId);
|
|
1134
|
+
this.abortControllers.delete(sessionId);
|
|
1135
|
+
this.pendingPermissions.delete(sessionId);
|
|
1136
|
+
this.pendingQuestions.delete(sessionId);
|
|
1137
|
+
this.idleCommands.delete(sessionId);
|
|
1138
|
+
this.lastActivityAt.delete(sessionId);
|
|
1139
|
+
}
|
|
1140
|
+
}
|