aws-runtime-bridge 1.9.78 → 1.9.80
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 +0 -1
- package/dist/adapter/AcodeSdkAdapter.js +11 -1986
- package/dist/adapter/AdapterRegistry.d.ts +0 -1
- package/dist/adapter/AdapterRegistry.js +2 -100
- package/dist/adapter/ClaudeSdkAdapter.d.ts +0 -1
- package/dist/adapter/ClaudeSdkAdapter.js +4 -1300
- package/dist/adapter/CodexSdkAdapter.d.ts +0 -1
- package/dist/adapter/CodexSdkAdapter.js +4 -836
- package/dist/adapter/OpencodeSdkAdapter.d.ts +0 -1
- package/dist/adapter/OpencodeSdkAdapter.js +4 -879
- package/dist/adapter/SdkProviderSpi.d.ts +0 -1
- package/dist/adapter/SdkProviderSpi.js +2 -55
- package/dist/adapter/acode-tool-names.d.ts +0 -1
- package/dist/adapter/acode-tool-names.js +2 -37
- package/dist/adapter/idle-poll-loop.d.ts +0 -1
- package/dist/adapter/idle-poll-loop.js +2 -59
- package/dist/adapter/index.d.ts +0 -1
- package/dist/adapter/index.js +2 -11
- package/dist/adapter/instance-ai-config-resolver.d.ts +0 -1
- package/dist/adapter/instance-ai-config-resolver.js +2 -46
- package/dist/adapter/types.d.ts +0 -1
- package/dist/adapter/types.js +2 -443
- package/dist/adapters/cc-switch/common.d.ts +0 -1
- package/dist/adapters/cc-switch/common.js +2 -61
- package/dist/adapters/cc-switch/index.d.ts +0 -1
- package/dist/adapters/cc-switch/index.js +2 -57
- package/dist/adapters/cc-switch/mcp-acode.d.ts +0 -1
- package/dist/adapters/cc-switch/mcp-acode.js +2 -71
- package/dist/adapters/cc-switch/mcp-claude.d.ts +0 -1
- package/dist/adapters/cc-switch/mcp-claude.js +2 -89
- package/dist/adapters/cc-switch/mcp-claudecode.d.ts +0 -1
- package/dist/adapters/cc-switch/mcp-claudecode.js +2 -77
- package/dist/adapters/cc-switch/mcp-codex.d.ts +0 -1
- package/dist/adapters/cc-switch/mcp-codex.js +11 -249
- package/dist/adapters/cc-switch/mcp-opencode.d.ts +0 -1
- package/dist/adapters/cc-switch/mcp-opencode.js +2 -97
- package/dist/adapters/cc-switch/mcp-placeholder.d.ts +0 -1
- package/dist/adapters/cc-switch/mcp-placeholder.js +2 -19
- package/dist/adapters/cc-switch/skill-acode.d.ts +0 -1
- package/dist/adapters/cc-switch/skill-acode.js +2 -12
- package/dist/adapters/cc-switch/skill-claude.d.ts +0 -1
- package/dist/adapters/cc-switch/skill-claude.js +2 -11
- package/dist/adapters/cc-switch/skill-claudecode.d.ts +0 -1
- package/dist/adapters/cc-switch/skill-claudecode.js +2 -11
- package/dist/adapters/cc-switch/skill-codex.d.ts +0 -1
- package/dist/adapters/cc-switch/skill-codex.js +2 -6
- package/dist/adapters/cc-switch/skill-directory.d.ts +0 -1
- package/dist/adapters/cc-switch/skill-directory.js +3 -137
- package/dist/adapters/cc-switch/skill-opencode.d.ts +0 -1
- package/dist/adapters/cc-switch/skill-opencode.js +2 -11
- package/dist/adapters/cc-switch/skill-placeholder.d.ts +0 -1
- package/dist/adapters/cc-switch/skill-placeholder.js +2 -19
- package/dist/config.d.ts +0 -1
- package/dist/config.js +2 -290
- package/dist/index.d.ts +0 -1
- package/dist/index.js +2 -573
- package/dist/middleware/auth.d.ts +0 -1
- package/dist/middleware/auth.js +2 -98
- package/dist/routes/acode-todos.d.ts +0 -1
- package/dist/routes/acode-todos.js +2 -75
- package/dist/routes/ai-sources.d.ts +0 -1
- package/dist/routes/ai-sources.js +2 -132
- package/dist/routes/dashboard.d.ts +0 -1
- package/dist/routes/dashboard.js +182 -649
- package/dist/routes/events.d.ts +0 -1
- package/dist/routes/events.js +2 -22
- package/dist/routes/file-browser.d.ts +0 -1
- package/dist/routes/file-browser.js +2 -781
- package/dist/routes/file-diff-rollback.d.ts +0 -1
- package/dist/routes/file-diff-rollback.js +2 -301
- package/dist/routes/file-snapshot.d.ts +0 -1
- package/dist/routes/file-snapshot.js +2 -156
- package/dist/routes/git.d.ts +0 -1
- package/dist/routes/git.js +28 -1900
- package/dist/routes/instance.d.ts +0 -1
- package/dist/routes/instance.js +3 -748
- package/dist/routes/mcp.d.ts +0 -1
- package/dist/routes/mcp.js +2 -196
- package/dist/routes/processes.d.ts +0 -1
- package/dist/routes/processes.js +2 -252
- package/dist/routes/properties.d.ts +0 -1
- package/dist/routes/properties.js +2 -72
- package/dist/routes/pty.d.ts +0 -1
- package/dist/routes/pty.js +6 -560
- package/dist/routes/runtime-binding.d.ts +0 -1
- package/dist/routes/runtime-binding.js +2 -263
- package/dist/routes/sessions.d.ts +0 -1
- package/dist/routes/sessions.js +2 -758
- package/dist/routes/skills.d.ts +0 -1
- package/dist/routes/skills.js +2 -187
- package/dist/routes/sub-agents.d.ts +0 -1
- package/dist/routes/sub-agents.js +20 -279
- package/dist/routes/system-metrics.d.ts +0 -1
- package/dist/routes/system-metrics.js +2 -20
- package/dist/routes/task-context.d.ts +0 -1
- package/dist/routes/task-context.js +2 -78
- package/dist/routes/terminal.d.ts +0 -1
- package/dist/routes/terminal.js +54 -2882
- package/dist/routes/yml.d.ts +0 -1
- package/dist/routes/yml.js +2 -223
- package/dist/services/acode-package.d.ts +0 -1
- package/dist/services/acode-package.js +2 -111
- package/dist/services/agent-event-store.d.ts +0 -1
- package/dist/services/agent-event-store.js +2 -81
- package/dist/services/agent-process-manager.d.ts +0 -1
- package/dist/services/agent-process-manager.js +2 -796
- package/dist/services/ai-source-apply.d.ts +0 -1
- package/dist/services/ai-source-apply.js +2 -83
- package/dist/services/auto-register.d.ts +0 -1
- package/dist/services/auto-register.js +2 -1053
- package/dist/services/aws-client-agent-mcp.d.ts +0 -1
- package/dist/services/aws-client-agent-mcp.js +3 -337
- package/dist/services/bridge-package-publish-hooks.d.ts +0 -1
- package/dist/services/bridge-package-publish-hooks.js +2 -115
- package/dist/services/cc-switch-sdk.d.ts +0 -1
- package/dist/services/cc-switch-sdk.js +2 -135
- package/dist/services/cli-commands.d.ts +6 -1
- package/dist/services/cli-commands.js +6 -645
- package/dist/services/event-delta-pusher.d.ts +0 -1
- package/dist/services/event-delta-pusher.js +2 -95
- package/dist/services/fileSnapshotStore.d.ts +0 -1
- package/dist/services/fileSnapshotStore.js +2 -500
- package/dist/services/instance-ai-config-service.d.ts +0 -1
- package/dist/services/instance-ai-config-service.js +2 -174
- package/dist/services/instance-init-service.d.ts +0 -1
- package/dist/services/instance-init-service.js +2 -193
- package/dist/services/instance-service.d.ts +0 -1
- package/dist/services/instance-service.js +2 -237
- package/dist/services/instance-state.d.ts +0 -1
- package/dist/services/instance-state.js +2 -94
- package/dist/services/legacy-path-migration.d.ts +0 -1
- package/dist/services/legacy-path-migration.js +2 -66
- package/dist/services/lifecycle-state.d.ts +0 -1
- package/dist/services/lifecycle-state.js +3 -110
- package/dist/services/mcp-launch-binding-queue.d.ts +0 -1
- package/dist/services/mcp-launch-binding-queue.js +2 -175
- package/dist/services/orphan-monitor.d.ts +0 -1
- package/dist/services/orphan-monitor.js +2 -382
- package/dist/services/panel-auth.d.ts +0 -1
- package/dist/services/panel-auth.js +2 -168
- package/dist/services/process-detector.d.ts +0 -1
- package/dist/services/process-detector.js +18 -1019
- package/dist/services/process-registry.d.ts +0 -1
- package/dist/services/process-registry.js +2 -375
- package/dist/services/ring-buffer.d.ts +0 -1
- package/dist/services/ring-buffer.js +2 -103
- package/dist/services/runtime-binding.d.ts +0 -1
- package/dist/services/runtime-binding.js +6 -363
- package/dist/services/runtime-lifecycle-policy.d.ts +0 -1
- package/dist/services/runtime-lifecycle-policy.js +2 -35
- package/dist/services/session-output.d.ts +0 -1
- package/dist/services/session-output.js +5 -539
- package/dist/services/startup-config-wizard.d.ts +0 -1
- package/dist/services/startup-config-wizard.js +14 -258
- package/dist/services/sub-agent-manager-instance.d.ts +0 -1
- package/dist/services/sub-agent-manager-instance.js +2 -12
- package/dist/services/system-metrics.d.ts +0 -1
- package/dist/services/system-metrics.js +2 -252
- package/dist/services/terminal-persistence.d.ts +0 -1
- package/dist/services/terminal-persistence.js +2 -176
- package/dist/services/tool-installer.d.ts +0 -1
- package/dist/services/tool-installer.js +5 -751
- package/dist/services/tunnel-client.d.ts +0 -1
- package/dist/services/tunnel-client.js +2 -402
- package/dist/services/windows-service-manager.d.ts +3 -2
- package/dist/services/windows-service-manager.js +2 -54
- package/dist/services/workspace-files.d.ts +0 -1
- package/dist/services/workspace-files.js +2 -1275
- package/dist/services/workspace-watch.d.ts +0 -1
- package/dist/services/workspace-watch.js +2 -123
- package/dist/services/workspaceTaskChangeWatcher.d.ts +0 -1
- package/dist/services/workspaceTaskChangeWatcher.js +2 -483
- package/dist/types.d.ts +0 -1
- package/dist/types.js +1 -1
- package/dist/utils/file-utils.d.ts +0 -1
- package/dist/utils/file-utils.js +3 -208
- package/dist/utils/instance-ai-config-debug-logger.d.ts +0 -1
- package/dist/utils/instance-ai-config-debug-logger.js +4 -63
- package/dist/utils/logger.d.ts +0 -1
- package/dist/utils/logger.js +2 -154
- package/dist/utils/mcp-utils.d.ts +0 -1
- package/dist/utils/mcp-utils.js +2 -187
- package/dist/utils/path-utils.d.ts +0 -1
- package/dist/utils/path-utils.js +2 -44
- package/dist/utils/sdk-package-loader.d.ts +0 -1
- package/dist/utils/sdk-package-loader.js +2 -78
- package/dist/utils/validation.d.ts +0 -1
- package/dist/utils/validation.js +2 -53
- package/dist/utils/yaml-utils.d.ts +0 -1
- package/dist/utils/yaml-utils.js +2 -312
- package/node_modules/@cc-switch/sdk/dist/constants.d.ts +0 -1
- package/node_modules/@cc-switch/sdk/dist/constants.js +2 -63
- package/node_modules/@cc-switch/sdk/dist/errors.d.ts +0 -1
- package/node_modules/@cc-switch/sdk/dist/errors.js +2 -8
- package/node_modules/@cc-switch/sdk/dist/index.d.ts +0 -1
- package/node_modules/@cc-switch/sdk/dist/index.js +2 -9
- package/node_modules/@cc-switch/sdk/dist/schemas.d.ts +0 -1
- package/node_modules/@cc-switch/sdk/dist/schemas.js +2 -38
- package/node_modules/@cc-switch/sdk/dist/sdk.d.ts +0 -1
- package/node_modules/@cc-switch/sdk/dist/sdk.js +2 -470
- package/node_modules/@cc-switch/sdk/dist/services/ai-config-service.d.ts +0 -1
- package/node_modules/@cc-switch/sdk/dist/services/ai-config-service.js +4 -307
- package/node_modules/@cc-switch/sdk/dist/services/mcp-model.d.ts +0 -1
- package/node_modules/@cc-switch/sdk/dist/services/mcp-model.js +2 -37
- package/node_modules/@cc-switch/sdk/dist/services/mcp-service.d.ts +0 -1
- package/node_modules/@cc-switch/sdk/dist/services/mcp-service.js +2 -9
- package/node_modules/@cc-switch/sdk/dist/services/skill-model.d.ts +0 -1
- package/node_modules/@cc-switch/sdk/dist/services/skill-model.js +2 -41
- package/node_modules/@cc-switch/sdk/dist/services/skill-service.d.ts +0 -1
- package/node_modules/@cc-switch/sdk/dist/services/skill-service.js +2 -9
- package/node_modules/@cc-switch/sdk/dist/state.d.ts +0 -1
- package/node_modules/@cc-switch/sdk/dist/state.js +2 -19
- package/node_modules/@cc-switch/sdk/dist/types.d.ts +0 -1
- package/node_modules/@cc-switch/sdk/dist/types.js +1 -1
- package/node_modules/@cc-switch/sdk/dist/utils/fs.d.ts +0 -1
- package/node_modules/@cc-switch/sdk/dist/utils/fs.js +3 -114
- package/node_modules/@cc-switch/sdk/dist/utils/id.d.ts +0 -1
- package/node_modules/@cc-switch/sdk/dist/utils/id.js +2 -12
- package/node_modules/@cc-switch/sdk/package.json +1 -1
- package/package/acode/dist/ask-user-question-tool.d.ts +0 -1
- package/package/acode/dist/ask-user-question-tool.js +4 -233
- package/package/acode/dist/built-in-file-tools.d.ts +0 -1
- package/package/acode/dist/built-in-file-tools.js +4 -966
- package/package/acode/dist/built-in-symbols-tool.d.ts +0 -1
- package/package/acode/dist/built-in-symbols-tool.js +2 -117
- package/package/acode/dist/builtins/commandArtifacts.d.ts +0 -1
- package/package/acode/dist/builtins/commandArtifacts.js +9 -97
- package/package/acode/dist/builtins/commandOutputEvents.d.ts +0 -1
- package/package/acode/dist/builtins/commandOutputEvents.js +2 -56
- package/package/acode/dist/builtins/shellCommandPreparation.d.ts +0 -1
- package/package/acode/dist/builtins/shellCommandPreparation.js +5 -44
- package/package/acode/dist/builtins/shellCommandResult.d.ts +0 -1
- package/package/acode/dist/builtins/shellCommandResult.js +2 -71
- package/package/acode/dist/builtins/shellCommandRunner.d.ts +0 -1
- package/package/acode/dist/builtins/shellCommandRunner.js +2 -115
- package/package/acode/dist/builtins/shellOutputDecoder.d.ts +0 -1
- package/package/acode/dist/builtins/shellOutputDecoder.js +2 -94
- package/package/acode/dist/cli.d.ts +0 -1
- package/package/acode/dist/cli.js +4 -38
- package/package/acode/dist/commands/commandLoader.d.ts +0 -1
- package/package/acode/dist/commands/commandLoader.js +2 -33
- package/package/acode/dist/commands/commandParser.d.ts +0 -1
- package/package/acode/dist/commands/commandParser.js +3 -77
- package/package/acode/dist/commands/commandRegistry.d.ts +0 -1
- package/package/acode/dist/commands/commandRegistry.js +2 -30
- package/package/acode/dist/compaction.d.ts +0 -1
- package/package/acode/dist/compaction.js +7 -919
- package/package/acode/dist/config.d.ts +0 -1
- package/package/acode/dist/config.js +5 -105
- package/package/acode/dist/fs-utils.d.ts +0 -1
- package/package/acode/dist/fs-utils.js +2 -79
- package/package/acode/dist/hooks/hookBus.d.ts +0 -1
- package/package/acode/dist/hooks/hookBus.js +2 -40
- package/package/acode/dist/index.d.ts +0 -1
- package/package/acode/dist/index.js +2 -19
- package/package/acode/dist/mcpToolNames.d.ts +0 -1
- package/package/acode/dist/mcpToolNames.js +2 -17
- package/package/acode/dist/parsers/ast-cache.d.ts +0 -1
- package/package/acode/dist/parsers/ast-cache.js +2 -76
- package/package/acode/dist/parsers/language-detector.d.ts +0 -1
- package/package/acode/dist/parsers/language-detector.js +2 -44
- package/package/acode/dist/parsers/language-queries.d.ts +0 -1
- package/package/acode/dist/parsers/language-queries.js +12 -602
- package/package/acode/dist/parsers/symbol-extractor.d.ts +0 -1
- package/package/acode/dist/parsers/symbol-extractor.js +3 -164
- package/package/acode/dist/parsers/tree-sitter-loader.d.ts +0 -1
- package/package/acode/dist/parsers/tree-sitter-loader.js +2 -64
- package/package/acode/dist/parsers/types.d.ts +0 -1
- package/package/acode/dist/parsers/types.js +1 -7
- package/package/acode/dist/runtime.d.ts +0 -1
- package/package/acode/dist/runtime.js +86 -3771
- package/package/acode/dist/skill-tool.d.ts +0 -1
- package/package/acode/dist/skill-tool.js +5 -174
- package/package/acode/dist/sub-agent-manager.d.ts +0 -1
- package/package/acode/dist/sub-agent-manager.js +2 -1159
- package/package/acode/dist/sub-agent-store.d.ts +0 -1
- package/package/acode/dist/sub-agent-store.js +4 -236
- package/package/acode/dist/sub-agent-tools.d.ts +0 -1
- package/package/acode/dist/sub-agent-tools.js +15 -369
- package/package/acode/dist/sub-agent-types.d.ts +0 -1
- package/package/acode/dist/sub-agent-types.js +1 -8
- package/package/acode/dist/todo-store.d.ts +0 -1
- package/package/acode/dist/todo-store.js +2 -73
- package/package/acode/dist/todo-tool.d.ts +0 -1
- package/package/acode/dist/todo-tool.js +5 -121
- package/package/acode/dist/types.d.ts +0 -1
- package/package/acode/dist/types.js +1 -1
- package/package/acode/package.json +2 -2
- package/package/aws-client-agent-mcp/dist/agent-client.d.ts +0 -1
- package/package/aws-client-agent-mcp/dist/agent-client.js +2 -890
- package/package/aws-client-agent-mcp/dist/agent-client.test.d.ts +0 -1
- package/package/aws-client-agent-mcp/dist/agent-client.test.js +0 -1
- package/package/aws-client-agent-mcp/dist/config.d.ts +0 -1
- package/package/aws-client-agent-mcp/dist/config.js +2 -243
- package/package/aws-client-agent-mcp/dist/config.test.d.ts +0 -1
- package/package/aws-client-agent-mcp/dist/config.test.js +0 -1
- package/package/aws-client-agent-mcp/dist/constants.d.ts +0 -1
- package/package/aws-client-agent-mcp/dist/constants.js +2 -24
- package/package/aws-client-agent-mcp/dist/context-manager.d.ts +0 -1
- package/package/aws-client-agent-mcp/dist/context-manager.js +2 -477
- package/package/aws-client-agent-mcp/dist/context-manager.test.d.ts +0 -1
- package/package/aws-client-agent-mcp/dist/context-manager.test.js +0 -1
- package/package/aws-client-agent-mcp/dist/http-client.d.ts +0 -1
- package/package/aws-client-agent-mcp/dist/http-client.js +2 -446
- package/package/aws-client-agent-mcp/dist/http-client.test.d.ts +0 -1
- package/package/aws-client-agent-mcp/dist/http-client.test.js +0 -1
- package/package/aws-client-agent-mcp/dist/index.d.ts +0 -1
- package/package/aws-client-agent-mcp/dist/index.js +2 -34
- package/package/aws-client-agent-mcp/dist/logger.d.ts +0 -1
- package/package/aws-client-agent-mcp/dist/logger.js +3 -178
- package/package/aws-client-agent-mcp/dist/logger.test.d.ts +0 -1
- package/package/aws-client-agent-mcp/dist/logger.test.js +0 -1
- package/package/aws-client-agent-mcp/dist/mcp-server.d.ts +0 -1
- package/package/aws-client-agent-mcp/dist/mcp-server.js +38 -2709
- package/package/aws-client-agent-mcp/dist/mcp-server.test.d.ts +0 -1
- package/package/aws-client-agent-mcp/dist/mcp-server.test.js +0 -1
- package/package/aws-client-agent-mcp/dist/mcp-tools.d.ts +0 -1
- package/package/aws-client-agent-mcp/dist/mcp-tools.js +22 -562
- package/package/aws-client-agent-mcp/dist/mcp-tools.test.d.ts +0 -1
- package/package/aws-client-agent-mcp/dist/mcp-tools.test.js +0 -1
- package/package/aws-client-agent-mcp/dist/memory-store.d.ts +0 -1
- package/package/aws-client-agent-mcp/dist/memory-store.js +4 -826
- package/package/aws-client-agent-mcp/dist/memory-store.test.d.ts +0 -1
- package/package/aws-client-agent-mcp/dist/memory-store.test.js +0 -1
- package/package/aws-client-agent-mcp/dist/memory-tools.d.ts +0 -1
- package/package/aws-client-agent-mcp/dist/memory-tools.js +31 -294
- package/package/aws-client-agent-mcp/dist/message-buffer.d.ts +0 -1
- package/package/aws-client-agent-mcp/dist/message-buffer.js +2 -288
- package/package/aws-client-agent-mcp/dist/message-buffer.test.d.ts +0 -1
- package/package/aws-client-agent-mcp/dist/message-buffer.test.js +0 -1
- package/package/aws-client-agent-mcp/dist/messageContent.d.ts +0 -1
- package/package/aws-client-agent-mcp/dist/messageContent.js +2 -125
- package/package/aws-client-agent-mcp/dist/runtime-launch-binding.d.ts +0 -1
- package/package/aws-client-agent-mcp/dist/runtime-launch-binding.js +2 -103
- package/package/aws-client-agent-mcp/dist/runtime-launch-binding.test.d.ts +0 -1
- package/package/aws-client-agent-mcp/dist/runtime-launch-binding.test.js +0 -1
- package/package/aws-client-agent-mcp/dist/server-memory-store.d.ts +0 -1
- package/package/aws-client-agent-mcp/dist/server-memory-store.js +2 -96
- package/package/aws-client-agent-mcp/dist/status-reporter.d.ts +0 -1
- package/package/aws-client-agent-mcp/dist/status-reporter.js +3 -327
- package/package/aws-client-agent-mcp/dist/status-reporter.test.d.ts +0 -1
- package/package/aws-client-agent-mcp/dist/status-reporter.test.js +0 -1
- package/package/aws-client-agent-mcp/dist/types.d.ts +0 -1
- package/package/aws-client-agent-mcp/dist/types.js +2 -9
- package/package/aws-client-agent-mcp/dist/websocket-client.d.ts +0 -1
- package/package/aws-client-agent-mcp/dist/websocket-client.js +2 -677
- package/package/aws-client-agent-mcp/dist/websocket-client.test.d.ts +0 -1
- package/package/aws-client-agent-mcp/dist/websocket-client.test.js +0 -1
- package/package/aws-client-agent-mcp/package.json +1 -1
- package/package/cc-switch-sdk/dist/constants.d.ts +0 -1
- package/package/cc-switch-sdk/dist/constants.js +2 -63
- package/package/cc-switch-sdk/dist/errors.d.ts +0 -1
- package/package/cc-switch-sdk/dist/errors.js +2 -8
- package/package/cc-switch-sdk/dist/index.d.ts +0 -1
- package/package/cc-switch-sdk/dist/index.js +2 -9
- package/package/cc-switch-sdk/dist/schemas.d.ts +0 -1
- package/package/cc-switch-sdk/dist/schemas.js +2 -38
- package/package/cc-switch-sdk/dist/sdk.d.ts +0 -1
- package/package/cc-switch-sdk/dist/sdk.js +2 -470
- package/package/cc-switch-sdk/dist/services/ai-config-service.d.ts +0 -1
- package/package/cc-switch-sdk/dist/services/ai-config-service.js +4 -307
- package/package/cc-switch-sdk/dist/services/mcp-model.d.ts +0 -1
- package/package/cc-switch-sdk/dist/services/mcp-model.js +2 -37
- package/package/cc-switch-sdk/dist/services/mcp-service.d.ts +0 -1
- package/package/cc-switch-sdk/dist/services/mcp-service.js +2 -9
- package/package/cc-switch-sdk/dist/services/skill-model.d.ts +0 -1
- package/package/cc-switch-sdk/dist/services/skill-model.js +2 -41
- package/package/cc-switch-sdk/dist/services/skill-service.d.ts +0 -1
- package/package/cc-switch-sdk/dist/services/skill-service.js +2 -9
- package/package/cc-switch-sdk/dist/state.d.ts +0 -1
- package/package/cc-switch-sdk/dist/state.js +2 -19
- package/package/cc-switch-sdk/dist/types.d.ts +0 -1
- package/package/cc-switch-sdk/dist/types.js +1 -1
- package/package/cc-switch-sdk/dist/utils/fs.d.ts +0 -1
- package/package/cc-switch-sdk/dist/utils/fs.js +3 -114
- package/package/cc-switch-sdk/dist/utils/id.d.ts +0 -1
- package/package/cc-switch-sdk/dist/utils/id.js +2 -12
- package/package/cc-switch-sdk/package.json +1 -1
- package/package.json +4 -2
- package/dist/adapter/AcodeSdkAdapter.d.ts.map +0 -1
- package/dist/adapter/AdapterRegistry.d.ts.map +0 -1
- package/dist/adapter/ClaudeSdkAdapter.d.ts.map +0 -1
- package/dist/adapter/CodexSdkAdapter.d.ts.map +0 -1
- package/dist/adapter/OpencodeSdkAdapter.d.ts.map +0 -1
- package/dist/adapter/SdkProviderSpi.d.ts.map +0 -1
- package/dist/adapter/acode-tool-names.d.ts.map +0 -1
- package/dist/adapter/idle-poll-loop.d.ts.map +0 -1
- package/dist/adapter/index.d.ts.map +0 -1
- package/dist/adapter/instance-ai-config-resolver.d.ts.map +0 -1
- package/dist/adapter/types.d.ts.map +0 -1
- package/dist/adapters/cc-switch/common.d.ts.map +0 -1
- package/dist/adapters/cc-switch/index.d.ts.map +0 -1
- package/dist/adapters/cc-switch/mcp-acode.d.ts.map +0 -1
- package/dist/adapters/cc-switch/mcp-claude.d.ts.map +0 -1
- package/dist/adapters/cc-switch/mcp-claudecode.d.ts.map +0 -1
- package/dist/adapters/cc-switch/mcp-codex.d.ts.map +0 -1
- package/dist/adapters/cc-switch/mcp-opencode.d.ts.map +0 -1
- package/dist/adapters/cc-switch/mcp-placeholder.d.ts.map +0 -1
- package/dist/adapters/cc-switch/skill-acode.d.ts.map +0 -1
- package/dist/adapters/cc-switch/skill-claude.d.ts.map +0 -1
- package/dist/adapters/cc-switch/skill-claudecode.d.ts.map +0 -1
- package/dist/adapters/cc-switch/skill-codex.d.ts.map +0 -1
- package/dist/adapters/cc-switch/skill-directory.d.ts.map +0 -1
- package/dist/adapters/cc-switch/skill-opencode.d.ts.map +0 -1
- package/dist/adapters/cc-switch/skill-placeholder.d.ts.map +0 -1
- package/dist/config.d.ts.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/middleware/auth.d.ts.map +0 -1
- package/dist/routes/acode-todos.d.ts.map +0 -1
- package/dist/routes/ai-sources.d.ts.map +0 -1
- package/dist/routes/dashboard.d.ts.map +0 -1
- package/dist/routes/events.d.ts.map +0 -1
- package/dist/routes/file-browser.d.ts.map +0 -1
- package/dist/routes/file-diff-rollback.d.ts.map +0 -1
- package/dist/routes/file-snapshot.d.ts.map +0 -1
- package/dist/routes/git.d.ts.map +0 -1
- package/dist/routes/instance.d.ts.map +0 -1
- package/dist/routes/mcp.d.ts.map +0 -1
- package/dist/routes/processes.d.ts.map +0 -1
- package/dist/routes/properties.d.ts.map +0 -1
- package/dist/routes/pty.d.ts.map +0 -1
- package/dist/routes/runtime-binding.d.ts.map +0 -1
- package/dist/routes/sessions.d.ts.map +0 -1
- package/dist/routes/skills.d.ts.map +0 -1
- package/dist/routes/sub-agents.d.ts.map +0 -1
- package/dist/routes/system-metrics.d.ts.map +0 -1
- package/dist/routes/task-context.d.ts.map +0 -1
- package/dist/routes/terminal.d.ts.map +0 -1
- package/dist/routes/yml.d.ts.map +0 -1
- package/dist/services/acode-package.d.ts.map +0 -1
- package/dist/services/agent-event-store.d.ts.map +0 -1
- package/dist/services/agent-process-manager.d.ts.map +0 -1
- package/dist/services/ai-source-apply.d.ts.map +0 -1
- package/dist/services/auto-register.d.ts.map +0 -1
- package/dist/services/aws-client-agent-mcp.d.ts.map +0 -1
- package/dist/services/bridge-package-publish-hooks.d.ts.map +0 -1
- package/dist/services/cc-switch-sdk.d.ts.map +0 -1
- package/dist/services/cli-commands.d.ts.map +0 -1
- package/dist/services/event-delta-pusher.d.ts.map +0 -1
- package/dist/services/fileSnapshotStore.d.ts.map +0 -1
- package/dist/services/instance-ai-config-service.d.ts.map +0 -1
- package/dist/services/instance-init-service.d.ts.map +0 -1
- package/dist/services/instance-service.d.ts.map +0 -1
- package/dist/services/instance-state.d.ts.map +0 -1
- package/dist/services/legacy-path-migration.d.ts.map +0 -1
- package/dist/services/lifecycle-state.d.ts.map +0 -1
- package/dist/services/mcp-launch-binding-queue.d.ts.map +0 -1
- package/dist/services/orphan-monitor.d.ts.map +0 -1
- package/dist/services/panel-auth.d.ts.map +0 -1
- package/dist/services/process-detector.d.ts.map +0 -1
- package/dist/services/process-registry.d.ts.map +0 -1
- package/dist/services/ring-buffer.d.ts.map +0 -1
- package/dist/services/runtime-binding.d.ts.map +0 -1
- package/dist/services/runtime-lifecycle-policy.d.ts.map +0 -1
- package/dist/services/session-output.d.ts.map +0 -1
- package/dist/services/startup-config-wizard.d.ts.map +0 -1
- package/dist/services/sub-agent-manager-instance.d.ts.map +0 -1
- package/dist/services/system-metrics.d.ts.map +0 -1
- package/dist/services/terminal-persistence.d.ts.map +0 -1
- package/dist/services/tool-installer.d.ts.map +0 -1
- package/dist/services/tunnel-client.d.ts.map +0 -1
- package/dist/services/windows-service-manager.d.ts.map +0 -1
- package/dist/services/workspace-files.d.ts.map +0 -1
- package/dist/services/workspace-watch.d.ts.map +0 -1
- package/dist/services/workspaceTaskChangeWatcher.d.ts.map +0 -1
- package/dist/types.d.ts.map +0 -1
- package/dist/utils/file-utils.d.ts.map +0 -1
- package/dist/utils/instance-ai-config-debug-logger.d.ts.map +0 -1
- package/dist/utils/logger.d.ts.map +0 -1
- package/dist/utils/mcp-utils.d.ts.map +0 -1
- package/dist/utils/path-utils.d.ts.map +0 -1
- package/dist/utils/sdk-package-loader.d.ts.map +0 -1
- package/dist/utils/validation.d.ts.map +0 -1
- package/dist/utils/yaml-utils.d.ts.map +0 -1
- package/node_modules/@cc-switch/sdk/dist/constants.d.ts.map +0 -1
- package/node_modules/@cc-switch/sdk/dist/errors.d.ts.map +0 -1
- package/node_modules/@cc-switch/sdk/dist/index.d.ts.map +0 -1
- package/node_modules/@cc-switch/sdk/dist/schemas.d.ts.map +0 -1
- package/node_modules/@cc-switch/sdk/dist/sdk.d.ts.map +0 -1
- package/node_modules/@cc-switch/sdk/dist/services/ai-config-service.d.ts.map +0 -1
- package/node_modules/@cc-switch/sdk/dist/services/mcp-model.d.ts.map +0 -1
- package/node_modules/@cc-switch/sdk/dist/services/mcp-service.d.ts.map +0 -1
- package/node_modules/@cc-switch/sdk/dist/services/skill-model.d.ts.map +0 -1
- package/node_modules/@cc-switch/sdk/dist/services/skill-service.d.ts.map +0 -1
- package/node_modules/@cc-switch/sdk/dist/state.d.ts.map +0 -1
- package/node_modules/@cc-switch/sdk/dist/types.d.ts.map +0 -1
- package/node_modules/@cc-switch/sdk/dist/utils/fs.d.ts.map +0 -1
- package/node_modules/@cc-switch/sdk/dist/utils/id.d.ts.map +0 -1
- package/package/acode/dist/ask-user-question-tool.d.ts.map +0 -1
- package/package/acode/dist/built-in-file-tools.d.ts.map +0 -1
- package/package/acode/dist/built-in-symbols-tool.d.ts.map +0 -1
- package/package/acode/dist/builtins/commandArtifacts.d.ts.map +0 -1
- package/package/acode/dist/builtins/commandOutputEvents.d.ts.map +0 -1
- package/package/acode/dist/builtins/shellCommandPreparation.d.ts.map +0 -1
- package/package/acode/dist/builtins/shellCommandResult.d.ts.map +0 -1
- package/package/acode/dist/builtins/shellCommandRunner.d.ts.map +0 -1
- package/package/acode/dist/builtins/shellOutputDecoder.d.ts.map +0 -1
- package/package/acode/dist/cli.d.ts.map +0 -1
- package/package/acode/dist/commands/commandLoader.d.ts.map +0 -1
- package/package/acode/dist/commands/commandParser.d.ts.map +0 -1
- package/package/acode/dist/commands/commandRegistry.d.ts.map +0 -1
- package/package/acode/dist/compaction.d.ts.map +0 -1
- package/package/acode/dist/config.d.ts.map +0 -1
- package/package/acode/dist/fs-utils.d.ts.map +0 -1
- package/package/acode/dist/hooks/hookBus.d.ts.map +0 -1
- package/package/acode/dist/index.d.ts.map +0 -1
- package/package/acode/dist/mcpToolNames.d.ts.map +0 -1
- package/package/acode/dist/parsers/ast-cache.d.ts.map +0 -1
- package/package/acode/dist/parsers/language-detector.d.ts.map +0 -1
- package/package/acode/dist/parsers/language-queries.d.ts.map +0 -1
- package/package/acode/dist/parsers/symbol-extractor.d.ts.map +0 -1
- package/package/acode/dist/parsers/tree-sitter-loader.d.ts.map +0 -1
- package/package/acode/dist/parsers/types.d.ts.map +0 -1
- package/package/acode/dist/runtime.d.ts.map +0 -1
- package/package/acode/dist/skill-tool.d.ts.map +0 -1
- package/package/acode/dist/sub-agent-manager.d.ts.map +0 -1
- package/package/acode/dist/sub-agent-store.d.ts.map +0 -1
- package/package/acode/dist/sub-agent-tools.d.ts.map +0 -1
- package/package/acode/dist/sub-agent-types.d.ts.map +0 -1
- package/package/acode/dist/todo-store.d.ts.map +0 -1
- package/package/acode/dist/todo-tool.d.ts.map +0 -1
- package/package/acode/dist/types.d.ts.map +0 -1
- package/package/aws-client-agent-mcp/dist/agent-client.d.ts.map +0 -1
- package/package/aws-client-agent-mcp/dist/agent-client.js.map +0 -1
- package/package/aws-client-agent-mcp/dist/agent-client.test.d.ts.map +0 -1
- package/package/aws-client-agent-mcp/dist/agent-client.test.js.map +0 -1
- package/package/aws-client-agent-mcp/dist/config.d.ts.map +0 -1
- package/package/aws-client-agent-mcp/dist/config.js.map +0 -1
- package/package/aws-client-agent-mcp/dist/config.test.d.ts.map +0 -1
- package/package/aws-client-agent-mcp/dist/config.test.js.map +0 -1
- package/package/aws-client-agent-mcp/dist/constants.d.ts.map +0 -1
- package/package/aws-client-agent-mcp/dist/constants.js.map +0 -1
- package/package/aws-client-agent-mcp/dist/context-manager.d.ts.map +0 -1
- package/package/aws-client-agent-mcp/dist/context-manager.js.map +0 -1
- package/package/aws-client-agent-mcp/dist/context-manager.test.d.ts.map +0 -1
- package/package/aws-client-agent-mcp/dist/context-manager.test.js.map +0 -1
- package/package/aws-client-agent-mcp/dist/http-client.d.ts.map +0 -1
- package/package/aws-client-agent-mcp/dist/http-client.js.map +0 -1
- package/package/aws-client-agent-mcp/dist/http-client.test.d.ts.map +0 -1
- package/package/aws-client-agent-mcp/dist/http-client.test.js.map +0 -1
- package/package/aws-client-agent-mcp/dist/index.d.ts.map +0 -1
- package/package/aws-client-agent-mcp/dist/index.js.map +0 -1
- package/package/aws-client-agent-mcp/dist/logger.d.ts.map +0 -1
- package/package/aws-client-agent-mcp/dist/logger.js.map +0 -1
- package/package/aws-client-agent-mcp/dist/logger.test.d.ts.map +0 -1
- package/package/aws-client-agent-mcp/dist/logger.test.js.map +0 -1
- package/package/aws-client-agent-mcp/dist/mcp-server.d.ts.map +0 -1
- package/package/aws-client-agent-mcp/dist/mcp-server.js.map +0 -1
- package/package/aws-client-agent-mcp/dist/mcp-server.test.d.ts.map +0 -1
- package/package/aws-client-agent-mcp/dist/mcp-server.test.js.map +0 -1
- package/package/aws-client-agent-mcp/dist/mcp-tools.d.ts.map +0 -1
- package/package/aws-client-agent-mcp/dist/mcp-tools.js.map +0 -1
- package/package/aws-client-agent-mcp/dist/mcp-tools.test.d.ts.map +0 -1
- package/package/aws-client-agent-mcp/dist/mcp-tools.test.js.map +0 -1
- package/package/aws-client-agent-mcp/dist/memory-store.d.ts.map +0 -1
- package/package/aws-client-agent-mcp/dist/memory-store.js.map +0 -1
- package/package/aws-client-agent-mcp/dist/memory-store.test.d.ts.map +0 -1
- package/package/aws-client-agent-mcp/dist/memory-store.test.js.map +0 -1
- package/package/aws-client-agent-mcp/dist/memory-tools.d.ts.map +0 -1
- package/package/aws-client-agent-mcp/dist/memory-tools.js.map +0 -1
- package/package/aws-client-agent-mcp/dist/message-buffer.d.ts.map +0 -1
- package/package/aws-client-agent-mcp/dist/message-buffer.js.map +0 -1
- package/package/aws-client-agent-mcp/dist/message-buffer.test.d.ts.map +0 -1
- package/package/aws-client-agent-mcp/dist/message-buffer.test.js.map +0 -1
- package/package/aws-client-agent-mcp/dist/messageContent.d.ts.map +0 -1
- package/package/aws-client-agent-mcp/dist/messageContent.js.map +0 -1
- package/package/aws-client-agent-mcp/dist/runtime-launch-binding.d.ts.map +0 -1
- package/package/aws-client-agent-mcp/dist/runtime-launch-binding.js.map +0 -1
- package/package/aws-client-agent-mcp/dist/runtime-launch-binding.test.d.ts.map +0 -1
- package/package/aws-client-agent-mcp/dist/runtime-launch-binding.test.js.map +0 -1
- package/package/aws-client-agent-mcp/dist/server-memory-store.d.ts.map +0 -1
- package/package/aws-client-agent-mcp/dist/server-memory-store.js.map +0 -1
- package/package/aws-client-agent-mcp/dist/status-reporter.d.ts.map +0 -1
- package/package/aws-client-agent-mcp/dist/status-reporter.js.map +0 -1
- package/package/aws-client-agent-mcp/dist/status-reporter.test.d.ts.map +0 -1
- package/package/aws-client-agent-mcp/dist/status-reporter.test.js.map +0 -1
- package/package/aws-client-agent-mcp/dist/types.d.ts.map +0 -1
- package/package/aws-client-agent-mcp/dist/types.js.map +0 -1
- package/package/aws-client-agent-mcp/dist/websocket-client.d.ts.map +0 -1
- package/package/aws-client-agent-mcp/dist/websocket-client.js.map +0 -1
- package/package/aws-client-agent-mcp/dist/websocket-client.test.d.ts.map +0 -1
- package/package/aws-client-agent-mcp/dist/websocket-client.test.js.map +0 -1
- package/package/cc-switch-sdk/dist/constants.d.ts.map +0 -1
- package/package/cc-switch-sdk/dist/errors.d.ts.map +0 -1
- package/package/cc-switch-sdk/dist/index.d.ts.map +0 -1
- package/package/cc-switch-sdk/dist/schemas.d.ts.map +0 -1
- package/package/cc-switch-sdk/dist/sdk.d.ts.map +0 -1
- package/package/cc-switch-sdk/dist/services/ai-config-service.d.ts.map +0 -1
- package/package/cc-switch-sdk/dist/services/mcp-model.d.ts.map +0 -1
- package/package/cc-switch-sdk/dist/services/mcp-service.d.ts.map +0 -1
- package/package/cc-switch-sdk/dist/services/skill-model.d.ts.map +0 -1
- package/package/cc-switch-sdk/dist/services/skill-service.d.ts.map +0 -1
- package/package/cc-switch-sdk/dist/state.d.ts.map +0 -1
- package/package/cc-switch-sdk/dist/types.d.ts.map +0 -1
- package/package/cc-switch-sdk/dist/utils/fs.d.ts.map +0 -1
- package/package/cc-switch-sdk/dist/utils/id.d.ts.map +0 -1
|
@@ -1,1300 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
*
|
|
4
|
-
|
|
5
|
-
* 实现与 Claude Code 的结构化交互。
|
|
6
|
-
*
|
|
7
|
-
* 核心特性:
|
|
8
|
-
* - 支持 autoAccept 模式自动确认所有权限
|
|
9
|
-
* - 支持 AskUserQuestion 拦截并转发到聊天面板
|
|
10
|
-
* - 支持 turn_complete 事件精确判断 AI 就绪状态
|
|
11
|
-
*/
|
|
12
|
-
import { execSync } from 'child_process';
|
|
13
|
-
import { EventEmitter } from 'events';
|
|
14
|
-
import * as fs from 'fs';
|
|
15
|
-
import * as path from 'path';
|
|
16
|
-
import { v4 as uuidv4 } from 'uuid';
|
|
17
|
-
import { importBridgeSdkPackage } from '../utils/sdk-package-loader.js';
|
|
18
|
-
import { IdlePollLoop } from './idle-poll-loop.js';
|
|
19
|
-
import { getToolActionInfo } from './types.js';
|
|
20
|
-
// ============ AsyncIterableQueue ============
|
|
21
|
-
/**
|
|
22
|
-
* 可排队的 AsyncIterable,用于向 SDK query() 输入流推送用户消息
|
|
23
|
-
*/
|
|
24
|
-
class AsyncIterableQueue {
|
|
25
|
-
constructor() {
|
|
26
|
-
this.queue = [];
|
|
27
|
-
this.resolve = null;
|
|
28
|
-
this.done = false;
|
|
29
|
-
}
|
|
30
|
-
enqueue(item) {
|
|
31
|
-
if (this.done)
|
|
32
|
-
return;
|
|
33
|
-
if (this.resolve) {
|
|
34
|
-
const r = this.resolve;
|
|
35
|
-
this.resolve = null;
|
|
36
|
-
r({ value: item, done: false });
|
|
37
|
-
}
|
|
38
|
-
else {
|
|
39
|
-
this.queue.push(item);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
close() {
|
|
43
|
-
this.done = true;
|
|
44
|
-
if (this.resolve) {
|
|
45
|
-
const r = this.resolve;
|
|
46
|
-
this.resolve = null;
|
|
47
|
-
r({ value: undefined, done: true });
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
[Symbol.asyncIterator]() {
|
|
51
|
-
return {
|
|
52
|
-
next: () => {
|
|
53
|
-
if (this.queue.length > 0) {
|
|
54
|
-
return Promise.resolve({ value: this.queue.shift(), done: false });
|
|
55
|
-
}
|
|
56
|
-
if (this.done) {
|
|
57
|
-
return Promise.resolve({ value: undefined, done: true });
|
|
58
|
-
}
|
|
59
|
-
return new Promise((resolve) => {
|
|
60
|
-
this.resolve = resolve;
|
|
61
|
-
});
|
|
62
|
-
},
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
// ============ ClaudeSdkAdapter ============
|
|
67
|
-
/**
|
|
68
|
-
* Claude Code Agent SDK Adapter
|
|
69
|
-
*/
|
|
70
|
-
export class ClaudeSdkAdapter extends EventEmitter {
|
|
71
|
-
constructor() {
|
|
72
|
-
super(...arguments);
|
|
73
|
-
this.providerId = 'claude-code';
|
|
74
|
-
this.displayName = 'Claude Code';
|
|
75
|
-
this.sessions = new Map();
|
|
76
|
-
this.sdkQueries = new Map();
|
|
77
|
-
this.inputStreams = new Map();
|
|
78
|
-
this.abortControllers = new Map();
|
|
79
|
-
this.sdkModule = null;
|
|
80
|
-
// 挂起的权限请求
|
|
81
|
-
this.pendingPermissions = new Map();
|
|
82
|
-
// 挂起的 AskUserQuestion
|
|
83
|
-
this.pendingQuestions = new Map();
|
|
84
|
-
// 正在处理的工具调用 ID(用于去重)
|
|
85
|
-
this.activeToolUseIds = new Map(); // sessionId -> toolUseId
|
|
86
|
-
// 最近处理的工具名(用于 stream_event 和 tool_use 的去重)
|
|
87
|
-
this.recentToolNames = new Map(); // sessionId -> { name, time }
|
|
88
|
-
// 收集工具参数的缓冲区(用于 stream_event 模式)
|
|
89
|
-
this.toolInputBuffers = new Map(); // "${sessionId}:${toolUseId}" -> JSON string
|
|
90
|
-
// ============ 空闲命令管理 ============
|
|
91
|
-
this.idleCommands = new Map();
|
|
92
|
-
this.idleTimers = new Map();
|
|
93
|
-
this.idlePollLoops = new Map();
|
|
94
|
-
this.lastActivityAt = new Map();
|
|
95
|
-
}
|
|
96
|
-
// ============ 生命周期方法 ============
|
|
97
|
-
async startSession(sessionId, config) {
|
|
98
|
-
// ★ 特征日志:确认使用的是 Claude Code Adapter
|
|
99
|
-
console.log(`[ClaudeSdkAdapter] ★★★ 启动 Claude Code SDK 会话 ★★★ sessionId="${sessionId}", command="${config.command}", workingDirectory="${config.workingDirectory}"`);
|
|
100
|
-
const session = {
|
|
101
|
-
sessionId,
|
|
102
|
-
status: 'starting',
|
|
103
|
-
messages: [],
|
|
104
|
-
createdAt: new Date().toISOString(),
|
|
105
|
-
totalUsage: { inputTokens: 0, outputTokens: 0 },
|
|
106
|
-
};
|
|
107
|
-
this.sessions.set(sessionId, session);
|
|
108
|
-
try {
|
|
109
|
-
const sdk = await this.loadSdk();
|
|
110
|
-
const abortController = new AbortController();
|
|
111
|
-
this.abortControllers.set(sessionId, abortController);
|
|
112
|
-
// 创建输入流
|
|
113
|
-
const inputStream = new AsyncIterableQueue();
|
|
114
|
-
this.inputStreams.set(sessionId, inputStream);
|
|
115
|
-
// 构建选项
|
|
116
|
-
const options = this.buildQueryOptions(sessionId, config, abortController);
|
|
117
|
-
// 启动 SDK query
|
|
118
|
-
const sdkQuery = sdk.query({
|
|
119
|
-
prompt: inputStream,
|
|
120
|
-
options,
|
|
121
|
-
});
|
|
122
|
-
this.sdkQueries.set(sessionId, sdkQuery);
|
|
123
|
-
// 启动流消费(异步,不阻塞)
|
|
124
|
-
this.consumeStream(sessionId, sdkQuery).catch(err => {
|
|
125
|
-
console.error(`[ClaudeSdkAdapter] Stream error for ${sessionId}:`, err);
|
|
126
|
-
});
|
|
127
|
-
// ★ 等待一小段时间让 SDK 进程启动
|
|
128
|
-
await new Promise(resolve => setTimeout(resolve, 100));
|
|
129
|
-
// 发送初始 prompt
|
|
130
|
-
if (config.initialPrompt) {
|
|
131
|
-
// 有初始 prompt,启动后会进入思考状态
|
|
132
|
-
session.status = 'starting';
|
|
133
|
-
this.emit('status-change', sessionId, 'starting');
|
|
134
|
-
const userMsg = {
|
|
135
|
-
id: uuidv4(),
|
|
136
|
-
sessionId,
|
|
137
|
-
role: 'user',
|
|
138
|
-
content: config.initialPrompt,
|
|
139
|
-
timestamp: new Date().toISOString(),
|
|
140
|
-
};
|
|
141
|
-
session.messages.push(userMsg);
|
|
142
|
-
this.emit('conversation-message', sessionId, userMsg);
|
|
143
|
-
// ★ 解析初始 prompt 中的图片标记
|
|
144
|
-
const initialContent = this.parseMessageContent(config.initialPrompt);
|
|
145
|
-
inputStream.enqueue({
|
|
146
|
-
type: 'user',
|
|
147
|
-
message: {
|
|
148
|
-
role: 'user',
|
|
149
|
-
content: initialContent,
|
|
150
|
-
},
|
|
151
|
-
});
|
|
152
|
-
}
|
|
153
|
-
else {
|
|
154
|
-
// 没有初始 prompt,等待用户输入
|
|
155
|
-
session.status = 'waiting_input';
|
|
156
|
-
this.emit('status-change', sessionId, 'waiting_input', { usage: session.totalUsage });
|
|
157
|
-
// 如果有空闲命令配置,立即启动空闲检测
|
|
158
|
-
if (this.idleCommands.has(sessionId)) {
|
|
159
|
-
this.startIdleDetection(sessionId);
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
catch (error) {
|
|
164
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
165
|
-
session.status = 'error';
|
|
166
|
-
this.emit('status-change', sessionId, 'error');
|
|
167
|
-
this.emitEvent(sessionId, {
|
|
168
|
-
type: 'error',
|
|
169
|
-
sessionId,
|
|
170
|
-
timestamp: new Date().toISOString(),
|
|
171
|
-
data: { text: `Failed to start session: ${errorMessage}` },
|
|
172
|
-
});
|
|
173
|
-
throw error;
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
async sendMessage(sessionId, message) {
|
|
177
|
-
const session = this.sessions.get(sessionId);
|
|
178
|
-
const inputStream = this.inputStreams.get(sessionId);
|
|
179
|
-
if (!session || !inputStream) {
|
|
180
|
-
throw new Error(`Session ${sessionId} not found`);
|
|
181
|
-
}
|
|
182
|
-
if (session.status === 'error') {
|
|
183
|
-
throw new Error(`Session ${sessionId} is in error state`);
|
|
184
|
-
}
|
|
185
|
-
// 记录用户消息
|
|
186
|
-
const userMsg = {
|
|
187
|
-
id: uuidv4(),
|
|
188
|
-
sessionId,
|
|
189
|
-
role: 'user',
|
|
190
|
-
content: message,
|
|
191
|
-
timestamp: new Date().toISOString(),
|
|
192
|
-
};
|
|
193
|
-
session.messages.push(userMsg);
|
|
194
|
-
this.emit('conversation-message', sessionId, userMsg);
|
|
195
|
-
// 更新状态 - 用户发送消息后,AI 将开始处理
|
|
196
|
-
session.status = 'thinking';
|
|
197
|
-
this.emit('status-change', sessionId, 'thinking');
|
|
198
|
-
// ★ 解析消息中的图片标记,构造多模态内容
|
|
199
|
-
const content = this.parseMessageContent(message);
|
|
200
|
-
// 推送到输入流
|
|
201
|
-
inputStream.enqueue({
|
|
202
|
-
type: 'user',
|
|
203
|
-
message: {
|
|
204
|
-
role: 'user',
|
|
205
|
-
content,
|
|
206
|
-
},
|
|
207
|
-
});
|
|
208
|
-
}
|
|
209
|
-
/**
|
|
210
|
-
* 解析消息内容,提取图片和文本
|
|
211
|
-
* 支持 [图片: url] 标记格式
|
|
212
|
-
*/
|
|
213
|
-
parseMessageContent(message) {
|
|
214
|
-
const content = [];
|
|
215
|
-
// 匹配 [图片: url] 格式
|
|
216
|
-
const imageTagRegex = /\[图片\s*:\s*([^\]\n]+?)\s*\]/gi;
|
|
217
|
-
let lastIndex = 0;
|
|
218
|
-
let match = imageTagRegex.exec(message);
|
|
219
|
-
while (match !== null) {
|
|
220
|
-
// 添加图片之前的文本
|
|
221
|
-
if (match.index > lastIndex) {
|
|
222
|
-
const text = message.slice(lastIndex, match.index).trim();
|
|
223
|
-
if (text) {
|
|
224
|
-
content.push({ type: 'text', text });
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
// 添加图片
|
|
228
|
-
const imageUrl = match[1].trim();
|
|
229
|
-
if (imageUrl) {
|
|
230
|
-
console.log(`[ClaudeSdkAdapter] 添加图片 URL: ${imageUrl}`);
|
|
231
|
-
content.push({
|
|
232
|
-
type: 'image',
|
|
233
|
-
source: {
|
|
234
|
-
type: 'url',
|
|
235
|
-
url: imageUrl,
|
|
236
|
-
},
|
|
237
|
-
});
|
|
238
|
-
}
|
|
239
|
-
lastIndex = match.index + match[0].length;
|
|
240
|
-
match = imageTagRegex.exec(message);
|
|
241
|
-
}
|
|
242
|
-
// 添加最后的文本
|
|
243
|
-
if (lastIndex < message.length) {
|
|
244
|
-
const text = message.slice(lastIndex).trim();
|
|
245
|
-
if (text) {
|
|
246
|
-
content.push({ type: 'text', text });
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
// 如果没有找到任何内容,返回原始消息作为文本
|
|
250
|
-
if (content.length === 0) {
|
|
251
|
-
content.push({ type: 'text', text: message });
|
|
252
|
-
}
|
|
253
|
-
return content;
|
|
254
|
-
}
|
|
255
|
-
async sendConfirmation(sessionId, accept) {
|
|
256
|
-
const pending = this.pendingPermissions.get(sessionId);
|
|
257
|
-
if (pending) {
|
|
258
|
-
if (accept) {
|
|
259
|
-
pending.resolve({ behavior: 'allow', updatedInput: pending.toolInput });
|
|
260
|
-
}
|
|
261
|
-
else {
|
|
262
|
-
pending.resolve({ behavior: 'deny', message: 'User denied' });
|
|
263
|
-
}
|
|
264
|
-
this.pendingPermissions.delete(sessionId);
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
async sendQuestionAnswer(sessionId, answers) {
|
|
268
|
-
const pending = this.pendingQuestions.get(sessionId);
|
|
269
|
-
if (!pending)
|
|
270
|
-
return;
|
|
271
|
-
const questions = pending.toolInput.questions;
|
|
272
|
-
let answersText = '用户已回答了您的问题:\n';
|
|
273
|
-
if (Array.isArray(questions)) {
|
|
274
|
-
questions.forEach((q, i) => {
|
|
275
|
-
const key = String(i);
|
|
276
|
-
const answer = answers[key] || answers[q.header || ''] || answers[q.question] || '(未填写)';
|
|
277
|
-
answersText += `• ${q.header || q.question}:${answer}\n`;
|
|
278
|
-
});
|
|
279
|
-
}
|
|
280
|
-
else {
|
|
281
|
-
answersText += JSON.stringify(answers);
|
|
282
|
-
}
|
|
283
|
-
pending.resolve({ behavior: 'deny', message: answersText });
|
|
284
|
-
this.pendingQuestions.delete(sessionId);
|
|
285
|
-
}
|
|
286
|
-
async abortCurrentTurn(sessionId) {
|
|
287
|
-
const abortController = this.abortControllers.get(sessionId);
|
|
288
|
-
if (abortController) {
|
|
289
|
-
abortController.abort();
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
async terminateSession(sessionId) {
|
|
293
|
-
const session = this.sessions.get(sessionId);
|
|
294
|
-
const sdkQuery = this.sdkQueries.get(sessionId);
|
|
295
|
-
const inputStream = this.inputStreams.get(sessionId);
|
|
296
|
-
if (inputStream) {
|
|
297
|
-
inputStream.close();
|
|
298
|
-
}
|
|
299
|
-
if (sdkQuery) {
|
|
300
|
-
try {
|
|
301
|
-
sdkQuery.close();
|
|
302
|
-
}
|
|
303
|
-
catch {
|
|
304
|
-
// ignore
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
if (session) {
|
|
308
|
-
session.status = 'terminated';
|
|
309
|
-
this.emit('status-change', sessionId, 'terminated');
|
|
310
|
-
this.emitEvent(sessionId, {
|
|
311
|
-
type: 'session_complete',
|
|
312
|
-
sessionId,
|
|
313
|
-
timestamp: new Date().toISOString(),
|
|
314
|
-
data: { exitCode: 0 },
|
|
315
|
-
});
|
|
316
|
-
}
|
|
317
|
-
this.cleanupSession(sessionId);
|
|
318
|
-
}
|
|
319
|
-
getConversation(sessionId) {
|
|
320
|
-
return this.sessions.get(sessionId)?.messages ?? [];
|
|
321
|
-
}
|
|
322
|
-
hasSession(sessionId) {
|
|
323
|
-
return this.sessions.has(sessionId);
|
|
324
|
-
}
|
|
325
|
-
getProviderSessionId(sessionId) {
|
|
326
|
-
return this.sessions.get(sessionId)?.providerSessionId;
|
|
327
|
-
}
|
|
328
|
-
getSessionStatus(sessionId) {
|
|
329
|
-
return this.sessions.get(sessionId)?.status;
|
|
330
|
-
}
|
|
331
|
-
getSessionPid(sessionId) {
|
|
332
|
-
return this.sessions.get(sessionId)?.pid;
|
|
333
|
-
}
|
|
334
|
-
cleanup() {
|
|
335
|
-
for (const sessionId of this.sessions.keys()) {
|
|
336
|
-
try {
|
|
337
|
-
this.terminateSession(sessionId);
|
|
338
|
-
}
|
|
339
|
-
catch {
|
|
340
|
-
// ignore
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
this.sessions.clear();
|
|
344
|
-
this.sdkQueries.clear();
|
|
345
|
-
this.inputStreams.clear();
|
|
346
|
-
this.abortControllers.clear();
|
|
347
|
-
this.pendingPermissions.clear();
|
|
348
|
-
this.pendingQuestions.clear();
|
|
349
|
-
this.idleCommands.clear();
|
|
350
|
-
this.idleTimers.clear();
|
|
351
|
-
}
|
|
352
|
-
/**
|
|
353
|
-
* 设置空闲命令配置
|
|
354
|
-
*/
|
|
355
|
-
setIdleCommands(sessionId, commands) {
|
|
356
|
-
this.idleCommands.set(sessionId, commands);
|
|
357
|
-
// 如果当前状态是 waiting_input,立即启动空闲检测
|
|
358
|
-
const session = this.sessions.get(sessionId);
|
|
359
|
-
if (session && session.status === 'waiting_input' && (commands.idleInputCommand || commands.nonInputCommand)) {
|
|
360
|
-
this.startIdleDetection(sessionId);
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
/**
|
|
364
|
-
* 启动空闲检测定时器
|
|
365
|
-
*/
|
|
366
|
-
startIdleDetection(sessionId) {
|
|
367
|
-
// 清除现有定时器
|
|
368
|
-
this.stopIdleDetection(sessionId);
|
|
369
|
-
const commands = this.idleCommands.get(sessionId);
|
|
370
|
-
if (!commands?.idleInputCommand) {
|
|
371
|
-
return;
|
|
372
|
-
}
|
|
373
|
-
if (this.isMessagePollingIdleCommand(commands.idleInputCommand)) {
|
|
374
|
-
const idleLoop = this.getIdlePollLoop(sessionId);
|
|
375
|
-
idleLoop.start();
|
|
376
|
-
return;
|
|
377
|
-
}
|
|
378
|
-
this.lastActivityAt.set(sessionId, Date.now());
|
|
379
|
-
// 每500ms检测一次空闲状态
|
|
380
|
-
const timer = setInterval(() => {
|
|
381
|
-
this.checkIdleState(sessionId);
|
|
382
|
-
}, 500);
|
|
383
|
-
this.idleTimers.set(sessionId, timer);
|
|
384
|
-
}
|
|
385
|
-
/**
|
|
386
|
-
* 停止空闲检测定时器
|
|
387
|
-
*/
|
|
388
|
-
stopIdleDetection(sessionId) {
|
|
389
|
-
this.idlePollLoops.get(sessionId)?.stop();
|
|
390
|
-
const timer = this.idleTimers.get(sessionId);
|
|
391
|
-
if (timer) {
|
|
392
|
-
clearInterval(timer);
|
|
393
|
-
this.idleTimers.delete(sessionId);
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
/**
|
|
397
|
-
* 检测空闲状态并执行命令
|
|
398
|
-
*/
|
|
399
|
-
checkIdleState(sessionId) {
|
|
400
|
-
const session = this.sessions.get(sessionId);
|
|
401
|
-
if (!session || session.status !== 'waiting_input') {
|
|
402
|
-
return;
|
|
403
|
-
}
|
|
404
|
-
const commands = this.idleCommands.get(sessionId);
|
|
405
|
-
if (!commands || (!commands.idleInputCommand && !commands.nonInputCommand)) {
|
|
406
|
-
return;
|
|
407
|
-
}
|
|
408
|
-
const lastActivity = this.lastActivityAt.get(sessionId) || 0;
|
|
409
|
-
const idleTime = Date.now() - lastActivity;
|
|
410
|
-
// 空闲1.5秒后执行空闲命令
|
|
411
|
-
const IDLE_THRESHOLD = 1500;
|
|
412
|
-
if (idleTime < IDLE_THRESHOLD) {
|
|
413
|
-
return;
|
|
414
|
-
}
|
|
415
|
-
// 标记活动时间,避免重复执行
|
|
416
|
-
this.lastActivityAt.set(sessionId, Date.now());
|
|
417
|
-
// SDK 模式:空闲时发送一个简单的提示消息让 AI 调用 poll_message
|
|
418
|
-
// 实际的消息获取由 AI 通过 MCP 工具 poll_message 完成
|
|
419
|
-
if (commands.idleInputCommand) {
|
|
420
|
-
// 解析空闲命令,提取实际要执行的指令
|
|
421
|
-
const command = commands.idleInputCommand;
|
|
422
|
-
// 如果空闲命令包含 poll_message/get_message,引导 AI 进入阻塞式消息轮询。
|
|
423
|
-
if (this.isMessagePollingIdleCommand(command)) {
|
|
424
|
-
this.sendIdlePrompt(sessionId, this.buildMessagePollingIdlePrompt());
|
|
425
|
-
}
|
|
426
|
-
else {
|
|
427
|
-
// 其他命令,直接作为消息发送给 AI
|
|
428
|
-
this.sendIdlePrompt(sessionId, command);
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
/**
|
|
433
|
-
* 发送空闲提示到 SDK
|
|
434
|
-
*/
|
|
435
|
-
sendIdlePrompt(sessionId, prompt, options) {
|
|
436
|
-
const inputStream = this.inputStreams.get(sessionId);
|
|
437
|
-
if (!inputStream)
|
|
438
|
-
return;
|
|
439
|
-
// 更新状态。poll_message 是阻塞监听动作,保持 waiting_input,避免 UI 误判为“思考中卡死”。
|
|
440
|
-
const session = this.sessions.get(sessionId);
|
|
441
|
-
if (session && !options?.keepWaitingStatus) {
|
|
442
|
-
session.status = 'thinking';
|
|
443
|
-
this.emit('status-change', sessionId, 'thinking');
|
|
444
|
-
}
|
|
445
|
-
// 发送空闲提示
|
|
446
|
-
inputStream.enqueue({
|
|
447
|
-
type: 'user',
|
|
448
|
-
message: {
|
|
449
|
-
role: 'user',
|
|
450
|
-
content: [{ type: 'text', text: prompt }],
|
|
451
|
-
},
|
|
452
|
-
});
|
|
453
|
-
}
|
|
454
|
-
isMessagePollingIdleCommand(command) {
|
|
455
|
-
const normalized = command.trim();
|
|
456
|
-
if (!normalized) {
|
|
457
|
-
return false;
|
|
458
|
-
}
|
|
459
|
-
const lower = normalized.toLowerCase();
|
|
460
|
-
return lower.includes('poll_message') || lower.includes('get_message') || normalized.includes('取消息');
|
|
461
|
-
}
|
|
462
|
-
buildMessagePollingIdlePrompt() {
|
|
463
|
-
return '系统提醒:当前工作已完成或运行时已进入空闲状态。请立即调用 poll_message 工具阻塞等待新消息和流程线任务提醒;收到流程任务提醒后调用 my_task 查看已由后台静默抢占的任务详情并直接处理;完成任务后调用 submit_task_result 提交结果,然后继续 poll_message。';
|
|
464
|
-
}
|
|
465
|
-
/**
|
|
466
|
-
* 获取当前会话的空闲轮询驱动器。
|
|
467
|
-
* 主流程:惰性创建 -> 绑定会话状态与命令读取 -> 统一派发 poll/普通 idle prompt。
|
|
468
|
-
*/
|
|
469
|
-
getIdlePollLoop(sessionId) {
|
|
470
|
-
const existing = this.idlePollLoops.get(sessionId);
|
|
471
|
-
if (existing)
|
|
472
|
-
return existing;
|
|
473
|
-
const loop = new IdlePollLoop({
|
|
474
|
-
getStatus: () => this.sessions.get(sessionId)?.status,
|
|
475
|
-
getCommand: () => this.idleCommands.get(sessionId)?.idleInputCommand,
|
|
476
|
-
isPollingCommand: (command) => this.isMessagePollingIdleCommand(command),
|
|
477
|
-
dispatchPollingPrompt: () => this.sendIdlePrompt(sessionId, this.buildMessagePollingIdlePrompt(), { keepWaitingStatus: true }),
|
|
478
|
-
dispatchRegularIdleCommand: (command) => this.sendIdlePrompt(sessionId, command),
|
|
479
|
-
});
|
|
480
|
-
this.idlePollLoops.set(sessionId, loop);
|
|
481
|
-
return loop;
|
|
482
|
-
}
|
|
483
|
-
/**
|
|
484
|
-
* 发送手动快捷键 token 序列,和空闲自动命令共用解析逻辑。
|
|
485
|
-
*/
|
|
486
|
-
async sendShortcutInput(sessionId, command) {
|
|
487
|
-
await this.executeIdleCommand(sessionId, command);
|
|
488
|
-
}
|
|
489
|
-
/**
|
|
490
|
-
* 执行空闲命令(支持 [enter], [wait:n] 等 token)
|
|
491
|
-
*/
|
|
492
|
-
async executeIdleCommand(sessionId, command) {
|
|
493
|
-
const inputStream = this.inputStreams.get(sessionId);
|
|
494
|
-
if (!inputStream)
|
|
495
|
-
return;
|
|
496
|
-
// 解析命令 token
|
|
497
|
-
const tokenPattern = /\[(.+?)\]/g;
|
|
498
|
-
let cursor = 0;
|
|
499
|
-
const tokens = [];
|
|
500
|
-
let match = tokenPattern.exec(command);
|
|
501
|
-
while (match !== null) {
|
|
502
|
-
if (match.index > cursor) {
|
|
503
|
-
tokens.push({ type: 'text', value: command.slice(cursor, match.index) });
|
|
504
|
-
}
|
|
505
|
-
const token = match[1];
|
|
506
|
-
if (token.startsWith('wait:')) {
|
|
507
|
-
const seconds = parseFloat(token.slice(5));
|
|
508
|
-
if (!isNaN(seconds)) {
|
|
509
|
-
tokens.push({ type: 'wait', value: seconds * 1000 });
|
|
510
|
-
}
|
|
511
|
-
}
|
|
512
|
-
else {
|
|
513
|
-
// 映射按键 token
|
|
514
|
-
const keyMap = {
|
|
515
|
-
'enter': '\r',
|
|
516
|
-
'up': '\x1b[A',
|
|
517
|
-
'down': '\x1b[B',
|
|
518
|
-
'left': '\x1b[D',
|
|
519
|
-
'right': '\x1b[C',
|
|
520
|
-
'tab': '\t',
|
|
521
|
-
'esc': '\x1b',
|
|
522
|
-
'escape': '\x1b',
|
|
523
|
-
'space': ' ',
|
|
524
|
-
'ctrl+c': '\x03',
|
|
525
|
-
};
|
|
526
|
-
tokens.push({ type: 'key', value: keyMap[token.toLowerCase()] || token });
|
|
527
|
-
}
|
|
528
|
-
cursor = match.index + match[0].length;
|
|
529
|
-
match = tokenPattern.exec(command);
|
|
530
|
-
}
|
|
531
|
-
if (cursor < command.length) {
|
|
532
|
-
tokens.push({ type: 'text', value: command.slice(cursor) });
|
|
533
|
-
}
|
|
534
|
-
// 执行 tokens
|
|
535
|
-
for (const token of tokens) {
|
|
536
|
-
if (token.type === 'wait') {
|
|
537
|
-
await new Promise(resolve => setTimeout(resolve, token.value));
|
|
538
|
-
}
|
|
539
|
-
else {
|
|
540
|
-
const text = String(token.value);
|
|
541
|
-
inputStream.enqueue({
|
|
542
|
-
type: 'user',
|
|
543
|
-
message: {
|
|
544
|
-
role: 'user',
|
|
545
|
-
content: [{ type: 'text', text }],
|
|
546
|
-
},
|
|
547
|
-
});
|
|
548
|
-
}
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
/**
|
|
552
|
-
* 更新活动时间(在收到输出或状态变化时调用)
|
|
553
|
-
*/
|
|
554
|
-
updateActivity(sessionId) {
|
|
555
|
-
this.lastActivityAt.set(sessionId, Date.now());
|
|
556
|
-
}
|
|
557
|
-
/**
|
|
558
|
-
* 恢复之前的会话
|
|
559
|
-
* 注意:SDK 的 resume 功能需要 providerSessionId
|
|
560
|
-
*/
|
|
561
|
-
async resumeSession(sessionId, providerSessionId, config) {
|
|
562
|
-
// 目前简化实现:重新启动一个新会话
|
|
563
|
-
// TODO: 使用 SDK 的 resume API 恢复会话上下文
|
|
564
|
-
const session = {
|
|
565
|
-
sessionId,
|
|
566
|
-
providerSessionId,
|
|
567
|
-
status: 'starting',
|
|
568
|
-
messages: [],
|
|
569
|
-
createdAt: new Date().toISOString(),
|
|
570
|
-
totalUsage: { inputTokens: 0, outputTokens: 0 },
|
|
571
|
-
};
|
|
572
|
-
this.sessions.set(sessionId, session);
|
|
573
|
-
try {
|
|
574
|
-
await this.startSession(sessionId, config);
|
|
575
|
-
session.providerSessionId = providerSessionId;
|
|
576
|
-
}
|
|
577
|
-
catch (error) {
|
|
578
|
-
session.status = 'error';
|
|
579
|
-
this.emit('status-change', sessionId, 'error');
|
|
580
|
-
throw error;
|
|
581
|
-
}
|
|
582
|
-
}
|
|
583
|
-
// ============ 私有方法 ============
|
|
584
|
-
async loadSdk() {
|
|
585
|
-
if (!this.sdkModule) {
|
|
586
|
-
try {
|
|
587
|
-
this.sdkModule = await importBridgeSdkPackage('@anthropic-ai/claude-agent-sdk');
|
|
588
|
-
}
|
|
589
|
-
catch (err) {
|
|
590
|
-
throw new Error(`Failed to load @anthropic-ai/claude-agent-sdk. Please install: npm install @anthropic-ai/claude-agent-sdk. Original error: ${err}`);
|
|
591
|
-
}
|
|
592
|
-
}
|
|
593
|
-
return this.sdkModule;
|
|
594
|
-
}
|
|
595
|
-
/**
|
|
596
|
-
* 查找 Claude Code 可执行文件路径
|
|
597
|
-
* 支持 Windows / macOS / Linux
|
|
598
|
-
*/
|
|
599
|
-
findClaudeExecutable(command) {
|
|
600
|
-
const normalizedCommand = this.extractExecutableCommand(command);
|
|
601
|
-
// 1. 如果是绝对路径,直接返回
|
|
602
|
-
if (path.isAbsolute(normalizedCommand) && fs.existsSync(normalizedCommand)) {
|
|
603
|
-
const resolvedWrapper = this.resolveWindowsWrapper(normalizedCommand);
|
|
604
|
-
if (resolvedWrapper) {
|
|
605
|
-
return resolvedWrapper;
|
|
606
|
-
}
|
|
607
|
-
if (this.isWindowsWrapperPath(normalizedCommand)) {
|
|
608
|
-
console.warn(`[ClaudeSdkAdapter] Skipping wrapper script for Claude Code executable: ${normalizedCommand}`);
|
|
609
|
-
return undefined;
|
|
610
|
-
}
|
|
611
|
-
return normalizedCommand;
|
|
612
|
-
}
|
|
613
|
-
const pathExecutable = this.resolveExecutableFromPath(normalizedCommand);
|
|
614
|
-
if (pathExecutable) {
|
|
615
|
-
return pathExecutable;
|
|
616
|
-
}
|
|
617
|
-
// 2. Windows 特定路径(作为 claude 默认命令的兜底)
|
|
618
|
-
if (process.platform === 'win32' && this.shouldUseDefaultClaudeFallbacks(normalizedCommand)) {
|
|
619
|
-
const userProfile = process.env.USERPROFILE || '';
|
|
620
|
-
const localAppData = process.env.LOCALAPPDATA || '';
|
|
621
|
-
const appData = process.env.APPDATA || '';
|
|
622
|
-
// Volta 安装路径(优先检查)
|
|
623
|
-
const voltaCandidates = [
|
|
624
|
-
path.join(localAppData, 'Volta', 'tools', 'shared', '@anthropic-ai', 'claude-code', 'cli.js'),
|
|
625
|
-
path.join(localAppData, 'Volta', 'tools', 'image', 'packages', '@anthropic-ai', 'claude-code', 'cli.js'),
|
|
626
|
-
];
|
|
627
|
-
for (const p of voltaCandidates) {
|
|
628
|
-
if (fs.existsSync(p)) {
|
|
629
|
-
console.log(`[ClaudeSdkAdapter] Found Claude Code CLI via Volta: ${p}`);
|
|
630
|
-
return p;
|
|
631
|
-
}
|
|
632
|
-
}
|
|
633
|
-
const candidates = [
|
|
634
|
-
path.join(appData, 'npm', 'node_modules', '@anthropic-ai', 'claude-code', 'cli.js'),
|
|
635
|
-
path.join(localAppData, 'Programs', 'claude-code', 'claude.exe'),
|
|
636
|
-
path.join(userProfile, '.local', 'bin', 'claude.exe'),
|
|
637
|
-
];
|
|
638
|
-
for (const p of candidates) {
|
|
639
|
-
if (p && fs.existsSync(p)) {
|
|
640
|
-
console.log(`[ClaudeSdkAdapter] Found Claude Code CLI: ${p}`);
|
|
641
|
-
return p;
|
|
642
|
-
}
|
|
643
|
-
}
|
|
644
|
-
}
|
|
645
|
-
// 3. macOS/Linux 特定路径(作为 claude 默认命令的兜底)
|
|
646
|
-
const home = process.env.HOME || '';
|
|
647
|
-
const candidates = [
|
|
648
|
-
path.join(home, '.local', 'bin', 'claude'),
|
|
649
|
-
path.join(home, '.npm-global', 'bin', 'claude'),
|
|
650
|
-
path.join(home, '.local', 'share', 'mise', 'shims', 'claude'),
|
|
651
|
-
path.join(home, '.asdf', 'shims', 'claude'),
|
|
652
|
-
`/opt/homebrew/bin/claude`,
|
|
653
|
-
`/usr/local/bin/claude`,
|
|
654
|
-
`/usr/bin/claude`,
|
|
655
|
-
`/usr/local/lib/node_modules/@anthropic-ai/claude-code/cli.js`,
|
|
656
|
-
`/usr/lib/node_modules/@anthropic-ai/claude-code/cli.js`,
|
|
657
|
-
path.join(home, '.local', 'share', 'volta', 'tools', 'shared', '@anthropic-ai', 'claude-code', 'cli.js'),
|
|
658
|
-
path.join(home, '.volta', 'tools', 'shared', '@anthropic-ai', 'claude-code', 'cli.js'),
|
|
659
|
-
];
|
|
660
|
-
if (this.shouldUseDefaultClaudeFallbacks(normalizedCommand)) {
|
|
661
|
-
for (const p of candidates) {
|
|
662
|
-
if (p && fs.existsSync(p)) {
|
|
663
|
-
console.log(`[ClaudeSdkAdapter] Found Claude Code CLI: ${p}`);
|
|
664
|
-
return p;
|
|
665
|
-
}
|
|
666
|
-
}
|
|
667
|
-
}
|
|
668
|
-
// 4. 尝试 npm root -g
|
|
669
|
-
if (this.shouldUseDefaultClaudeFallbacks(normalizedCommand)) {
|
|
670
|
-
try {
|
|
671
|
-
const npmRoot = execSync('npm root -g', { encoding: 'utf8', timeout: 5000 }).trim();
|
|
672
|
-
const cliJs = path.join(npmRoot, '@anthropic-ai', 'claude-code', 'cli.js');
|
|
673
|
-
if (fs.existsSync(cliJs)) {
|
|
674
|
-
console.log(`[ClaudeSdkAdapter] Found Claude Code CLI via npm root: ${cliJs}`);
|
|
675
|
-
return cliJs;
|
|
676
|
-
}
|
|
677
|
-
}
|
|
678
|
-
catch {
|
|
679
|
-
// 忽略
|
|
680
|
-
}
|
|
681
|
-
}
|
|
682
|
-
console.warn(`[ClaudeSdkAdapter] Claude Code executable not found for command: ${normalizedCommand}`);
|
|
683
|
-
return undefined;
|
|
684
|
-
}
|
|
685
|
-
/**
|
|
686
|
-
* 从面板配置的命令行中提取实际可执行文件,避免把参数一起传给 PATH 查找。
|
|
687
|
-
*/
|
|
688
|
-
extractExecutableCommand(command) {
|
|
689
|
-
const input = (command || 'claude').trim() || 'claude';
|
|
690
|
-
const firstToken = input.match(/^\s*(?:"([^"]+)"|'([^']+)'|(\S+))/);
|
|
691
|
-
return firstToken?.[1] || firstToken?.[2] || firstToken?.[3] || 'claude';
|
|
692
|
-
}
|
|
693
|
-
/**
|
|
694
|
-
* 按当前 Bridge 进程的 PATH/PATHEXT 直接查找可执行文件,避免依赖 which/where 和 shell 解析。
|
|
695
|
-
*/
|
|
696
|
-
resolveExecutableFromPath(command) {
|
|
697
|
-
const pathValue = process.env.PATH || '';
|
|
698
|
-
if (!pathValue) {
|
|
699
|
-
return undefined;
|
|
700
|
-
}
|
|
701
|
-
const commandHasExtension = path.extname(command).length > 0;
|
|
702
|
-
const extensions = process.platform === 'win32'
|
|
703
|
-
? this.getWindowsPathExtensions(commandHasExtension)
|
|
704
|
-
: [''];
|
|
705
|
-
for (const directory of pathValue.split(path.delimiter)) {
|
|
706
|
-
if (!directory) {
|
|
707
|
-
continue;
|
|
708
|
-
}
|
|
709
|
-
for (const extension of extensions) {
|
|
710
|
-
const candidate = path.join(directory, `${command}${extension}`);
|
|
711
|
-
if (!fs.existsSync(candidate)) {
|
|
712
|
-
continue;
|
|
713
|
-
}
|
|
714
|
-
if (this.isWindowsWrapperPath(candidate)) {
|
|
715
|
-
const resolvedWrapper = this.resolveWindowsWrapper(candidate);
|
|
716
|
-
if (resolvedWrapper) {
|
|
717
|
-
return resolvedWrapper;
|
|
718
|
-
}
|
|
719
|
-
continue;
|
|
720
|
-
}
|
|
721
|
-
console.log(`[ClaudeSdkAdapter] Found Claude Code CLI via PATH: ${candidate}`);
|
|
722
|
-
return candidate;
|
|
723
|
-
}
|
|
724
|
-
}
|
|
725
|
-
return undefined;
|
|
726
|
-
}
|
|
727
|
-
getWindowsPathExtensions(commandHasExtension) {
|
|
728
|
-
if (commandHasExtension) {
|
|
729
|
-
return [''];
|
|
730
|
-
}
|
|
731
|
-
const pathExt = process.env.PATHEXT || '.EXE;.CMD;.BAT;.COM;.PS1';
|
|
732
|
-
return pathExt
|
|
733
|
-
.split(';')
|
|
734
|
-
.map((extension) => extension.trim())
|
|
735
|
-
.filter((extension) => extension.length > 0);
|
|
736
|
-
}
|
|
737
|
-
shouldUseDefaultClaudeFallbacks(command) {
|
|
738
|
-
return ['claude', 'claude-code', 'claudecode'].includes(command.toLowerCase());
|
|
739
|
-
}
|
|
740
|
-
isWindowsWrapperPath(filePath) {
|
|
741
|
-
const extension = path.extname(filePath).toLowerCase();
|
|
742
|
-
return ['.cmd', '.bat', '.ps1'].includes(extension);
|
|
743
|
-
}
|
|
744
|
-
resolveWindowsWrapper(wrapperPath) {
|
|
745
|
-
if (!this.isWindowsWrapperPath(wrapperPath)) {
|
|
746
|
-
return undefined;
|
|
747
|
-
}
|
|
748
|
-
return this.resolveCliJsFromWrapper(wrapperPath);
|
|
749
|
-
}
|
|
750
|
-
/**
|
|
751
|
-
* 从 wrapper 脚本解析 cli.js 路径
|
|
752
|
-
*/
|
|
753
|
-
resolveCliJsFromWrapper(wrapperPath) {
|
|
754
|
-
try {
|
|
755
|
-
const content = fs.readFileSync(wrapperPath, 'utf8');
|
|
756
|
-
// Volta wrapper: volta run %~n0 %*
|
|
757
|
-
if (content.includes('volta run')) {
|
|
758
|
-
const voltaShared = path.join(process.env.LOCALAPPDATA || '', 'Volta', 'tools', 'shared', '@anthropic-ai', 'claude-code', 'cli.js');
|
|
759
|
-
if (fs.existsSync(voltaShared)) {
|
|
760
|
-
return voltaShared;
|
|
761
|
-
}
|
|
762
|
-
}
|
|
763
|
-
const wrapperDirectory = path.dirname(wrapperPath);
|
|
764
|
-
const npmCliJs = path.join(wrapperDirectory, 'node_modules', '@anthropic-ai', 'claude-code', 'cli.js');
|
|
765
|
-
if (fs.existsSync(npmCliJs)) {
|
|
766
|
-
return npmCliJs;
|
|
767
|
-
}
|
|
768
|
-
}
|
|
769
|
-
catch {
|
|
770
|
-
// 忽略
|
|
771
|
-
}
|
|
772
|
-
return undefined;
|
|
773
|
-
}
|
|
774
|
-
buildQueryOptions(sessionId, config, abortController) {
|
|
775
|
-
const options = {
|
|
776
|
-
cwd: config.workingDirectory,
|
|
777
|
-
abortController,
|
|
778
|
-
settingSources: ['user', 'project', 'local'],
|
|
779
|
-
// 启用部分消息以获取增量输出
|
|
780
|
-
includePartialMessages: true,
|
|
781
|
-
};
|
|
782
|
-
// ★ 关键:指定 Claude Code 可执行文件路径
|
|
783
|
-
// SDK 需要这个来启动 Claude Code CLI
|
|
784
|
-
const execPath = this.findClaudeExecutable(config.command);
|
|
785
|
-
if (execPath) {
|
|
786
|
-
options.pathToClaudeCodeExecutable = execPath;
|
|
787
|
-
// ★ 特征日志:明确显示使用的是 Claude Code 可执行文件
|
|
788
|
-
console.log(`[ClaudeSdkAdapter] ★★★ 使用 Claude Code 可执行文件 ★★★ path="${execPath}"`);
|
|
789
|
-
}
|
|
790
|
-
else {
|
|
791
|
-
console.warn(`[ClaudeSdkAdapter] ★★★ 警告:未找到 Claude Code 可执行文件 ★★★ command="${config.command}"`);
|
|
792
|
-
}
|
|
793
|
-
// 模型配置
|
|
794
|
-
if (config.model) {
|
|
795
|
-
options.model = config.model;
|
|
796
|
-
}
|
|
797
|
-
// 最大轮次
|
|
798
|
-
if (config.maxTurns && config.maxTurns > 0) {
|
|
799
|
-
options.maxTurns = config.maxTurns;
|
|
800
|
-
}
|
|
801
|
-
// MCP 服务器
|
|
802
|
-
if (config.mcpConfigPath) {
|
|
803
|
-
try {
|
|
804
|
-
const raw = fs.readFileSync(config.mcpConfigPath, 'utf-8');
|
|
805
|
-
const parsed = JSON.parse(raw);
|
|
806
|
-
options.mcpServers = { ...config.extraMcpServers, ...parsed.mcpServers };
|
|
807
|
-
}
|
|
808
|
-
catch {
|
|
809
|
-
options.mcpServers = config.extraMcpServers || {};
|
|
810
|
-
}
|
|
811
|
-
}
|
|
812
|
-
else {
|
|
813
|
-
options.mcpServers = config.extraMcpServers || {};
|
|
814
|
-
}
|
|
815
|
-
// 允许的工具
|
|
816
|
-
if (config.allowedTools && config.allowedTools.length > 0) {
|
|
817
|
-
options.allowedTools = config.allowedTools;
|
|
818
|
-
}
|
|
819
|
-
else {
|
|
820
|
-
options.allowedTools = [
|
|
821
|
-
'Read',
|
|
822
|
-
'Write',
|
|
823
|
-
'Edit',
|
|
824
|
-
'Bash',
|
|
825
|
-
'Glob',
|
|
826
|
-
'Grep',
|
|
827
|
-
'WebSearch',
|
|
828
|
-
'WebFetch',
|
|
829
|
-
'Task',
|
|
830
|
-
'NotebookEdit',
|
|
831
|
-
'LSP',
|
|
832
|
-
'Skill',
|
|
833
|
-
'mcp__*',
|
|
834
|
-
];
|
|
835
|
-
}
|
|
836
|
-
// 环境变量
|
|
837
|
-
options.env = { ...process.env, ...config.envOverrides };
|
|
838
|
-
// 权限模式 - 使用 canUseTool 回调统一处理权限,不使用 bypassPermissions
|
|
839
|
-
// 原因:bypassPermissions 模式下 SDK 可能跳过 canUseTool 回调,
|
|
840
|
-
// 导致 AskUserQuestion 等需要用户交互的工具无法被拦截,前端收不到提问事件。
|
|
841
|
-
// 改为通过 canUseTool 回调实现 autoAccept 逻辑:
|
|
842
|
-
// - AskUserQuestion → 始终拦截,转发到前端等待用户回答
|
|
843
|
-
// - autoAccept 普通工具 → 直接允许
|
|
844
|
-
// - 非 autoAccept 普通工具 → 询问用户
|
|
845
|
-
options.canUseTool = this.createPermissionHandler(sessionId, config.autoAccept);
|
|
846
|
-
return options;
|
|
847
|
-
}
|
|
848
|
-
createPermissionHandler(sessionId, autoAccept) {
|
|
849
|
-
return async (toolName, toolInput, _options) => {
|
|
850
|
-
// AskUserQuestion 特殊处理:始终拦截,转发到前端等待用户回答
|
|
851
|
-
if (toolName === 'AskUserQuestion' || toolName === 'ask_user_question') {
|
|
852
|
-
return new Promise((resolve) => {
|
|
853
|
-
this.pendingQuestions.set(sessionId, { resolve, toolInput });
|
|
854
|
-
this.emit('ask-user-question', {
|
|
855
|
-
sessionId,
|
|
856
|
-
toolCallId: this.activeToolUseIds.get(sessionId) || undefined,
|
|
857
|
-
questions: toolInput.questions,
|
|
858
|
-
});
|
|
859
|
-
});
|
|
860
|
-
}
|
|
861
|
-
// autoAccept 模式直接允许普通工具
|
|
862
|
-
// 返回 { behavior: 'allow', updatedInput } 格式
|
|
863
|
-
if (autoAccept) {
|
|
864
|
-
return { behavior: 'allow', updatedInput: toolInput };
|
|
865
|
-
}
|
|
866
|
-
// 非 autoAccept 模式,询问用户
|
|
867
|
-
return new Promise((resolve) => {
|
|
868
|
-
this.pendingPermissions.set(sessionId, { resolve, toolInput });
|
|
869
|
-
this.emit('permission-request', {
|
|
870
|
-
sessionId,
|
|
871
|
-
toolName,
|
|
872
|
-
toolInput,
|
|
873
|
-
permissionPrompt: `Allow ${toolName}?`,
|
|
874
|
-
});
|
|
875
|
-
});
|
|
876
|
-
};
|
|
877
|
-
}
|
|
878
|
-
async consumeStream(sessionId, sdkQuery) {
|
|
879
|
-
const session = this.sessions.get(sessionId);
|
|
880
|
-
if (!session)
|
|
881
|
-
return;
|
|
882
|
-
try {
|
|
883
|
-
for await (const msg of sdkQuery) {
|
|
884
|
-
const sdkMsg = msg;
|
|
885
|
-
this.handleSdkMessage(sessionId, session, sdkMsg);
|
|
886
|
-
}
|
|
887
|
-
}
|
|
888
|
-
catch (error) {
|
|
889
|
-
if (this.abortControllers.get(sessionId)?.signal.aborted) {
|
|
890
|
-
// 主动中止,正常结束
|
|
891
|
-
return;
|
|
892
|
-
}
|
|
893
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
894
|
-
this.emitEvent(sessionId, {
|
|
895
|
-
type: 'error',
|
|
896
|
-
sessionId,
|
|
897
|
-
timestamp: new Date().toISOString(),
|
|
898
|
-
data: { text: `Stream error: ${errorMessage}` },
|
|
899
|
-
});
|
|
900
|
-
}
|
|
901
|
-
}
|
|
902
|
-
/**
|
|
903
|
-
* Apply Claude SDK usage snapshots to the adapter session.
|
|
904
|
-
* Main flow: accept assistant-step or final-result usage, then keep the largest observed totals.
|
|
905
|
-
*/
|
|
906
|
-
applyUsage(session, usage) {
|
|
907
|
-
if (!usage)
|
|
908
|
-
return;
|
|
909
|
-
if (Number.isFinite(usage.input_tokens)) {
|
|
910
|
-
session.totalUsage.inputTokens = Math.max(session.totalUsage.inputTokens, usage.input_tokens ?? 0);
|
|
911
|
-
}
|
|
912
|
-
if (Number.isFinite(usage.output_tokens)) {
|
|
913
|
-
session.totalUsage.outputTokens = Math.max(session.totalUsage.outputTokens, usage.output_tokens ?? 0);
|
|
914
|
-
}
|
|
915
|
-
}
|
|
916
|
-
handleSdkMessage(sessionId, session, msg) {
|
|
917
|
-
const timestamp = new Date().toISOString();
|
|
918
|
-
// ★ 调试日志:记录所有收到的消息类型
|
|
919
|
-
console.log(`[ClaudeSdkAdapter] Received message type: "${msg.type}", subtype: "${msg.subtype || ''}"`);
|
|
920
|
-
// ★ 如果有工具名,也记录下来
|
|
921
|
-
if (msg.tool_name) {
|
|
922
|
-
console.log(`[ClaudeSdkAdapter] Tool name: "${msg.tool_name}"`);
|
|
923
|
-
}
|
|
924
|
-
switch (msg.type) {
|
|
925
|
-
case 'text':
|
|
926
|
-
// AI 正在输出文本
|
|
927
|
-
if (msg.delta) {
|
|
928
|
-
// 更新状态为思考中(如果有文本输出)
|
|
929
|
-
if (session.status !== 'thinking') {
|
|
930
|
-
session.status = 'thinking';
|
|
931
|
-
this.emit('status-change', sessionId, 'thinking');
|
|
932
|
-
this.stopIdleDetection(sessionId);
|
|
933
|
-
}
|
|
934
|
-
this.emitEvent(sessionId, {
|
|
935
|
-
type: 'text_delta',
|
|
936
|
-
sessionId,
|
|
937
|
-
timestamp,
|
|
938
|
-
data: { text: msg.delta },
|
|
939
|
-
});
|
|
940
|
-
}
|
|
941
|
-
break;
|
|
942
|
-
case 'thinking':
|
|
943
|
-
// AI 正在思考/推理
|
|
944
|
-
if (session.status !== 'thinking') {
|
|
945
|
-
session.status = 'thinking';
|
|
946
|
-
this.emit('status-change', sessionId, 'thinking');
|
|
947
|
-
this.stopIdleDetection(sessionId);
|
|
948
|
-
}
|
|
949
|
-
if (msg.content || msg.delta) {
|
|
950
|
-
this.emitEvent(sessionId, {
|
|
951
|
-
type: 'thinking',
|
|
952
|
-
sessionId,
|
|
953
|
-
timestamp,
|
|
954
|
-
data: { text: msg.delta || msg.content },
|
|
955
|
-
});
|
|
956
|
-
}
|
|
957
|
-
break;
|
|
958
|
-
case 'tool_use':
|
|
959
|
-
case 'tool_use_start':
|
|
960
|
-
// AI 正在调用工具
|
|
961
|
-
{
|
|
962
|
-
const actionInfo = { ...getToolActionInfo(msg.tool_name, msg.tool_input), actionId: msg.tool_use_id };
|
|
963
|
-
const nextStatus = actionInfo.actionType === 'idle' ? 'waiting_input' : 'tool_using';
|
|
964
|
-
session.status = nextStatus;
|
|
965
|
-
const mcpServer = this.extractMcpServerName(msg.tool_name || '');
|
|
966
|
-
this.emit('status-change', sessionId, nextStatus, {
|
|
967
|
-
actionType: actionInfo.actionType,
|
|
968
|
-
actionLabel: actionInfo.actionLabel,
|
|
969
|
-
actionDetail: actionInfo.actionDetail,
|
|
970
|
-
actionId: actionInfo.actionId,
|
|
971
|
-
usage: session.totalUsage,
|
|
972
|
-
...(mcpServer ? { metadata: { mcpServer } } : {}),
|
|
973
|
-
});
|
|
974
|
-
if (actionInfo.actionType !== 'idle') {
|
|
975
|
-
this.stopIdleDetection(sessionId);
|
|
976
|
-
}
|
|
977
|
-
this.handleToolUse(sessionId, session, msg, timestamp);
|
|
978
|
-
}
|
|
979
|
-
break;
|
|
980
|
-
case 'tool_result':
|
|
981
|
-
case 'tool_use_end':
|
|
982
|
-
this.emitEvent(sessionId, {
|
|
983
|
-
type: 'tool_use_end',
|
|
984
|
-
sessionId,
|
|
985
|
-
timestamp,
|
|
986
|
-
data: {
|
|
987
|
-
toolName: msg.tool_name,
|
|
988
|
-
toolResult: msg.tool_result,
|
|
989
|
-
isError: msg.is_error,
|
|
990
|
-
toolUseId: msg.tool_use_id,
|
|
991
|
-
},
|
|
992
|
-
});
|
|
993
|
-
break;
|
|
994
|
-
case 'result':
|
|
995
|
-
// ★ SDK 的 turn_complete 事件是 'result' 类型
|
|
996
|
-
{
|
|
997
|
-
const isSuccess = msg.subtype === 'success';
|
|
998
|
-
this.applyUsage(session, msg.usage);
|
|
999
|
-
// 发送 turn_complete 事件
|
|
1000
|
-
this.emitEvent(sessionId, {
|
|
1001
|
-
type: 'turn_complete',
|
|
1002
|
-
sessionId,
|
|
1003
|
-
timestamp,
|
|
1004
|
-
data: {
|
|
1005
|
-
usage: session.totalUsage,
|
|
1006
|
-
text: isSuccess ? msg.result : undefined,
|
|
1007
|
-
},
|
|
1008
|
-
});
|
|
1009
|
-
// ★ 更新状态为等待输入
|
|
1010
|
-
session.status = 'waiting_input';
|
|
1011
|
-
// ★ 修复:当空闲检测即将重新启动 poll_message 轮询时,在 status-change 中
|
|
1012
|
-
// 保留 idle 动作信息,避免 bridge 清空动作字段导致前端显示 "空闲"。
|
|
1013
|
-
const willResumePolling = (() => {
|
|
1014
|
-
const idleCmds = this.idleCommands.get(sessionId);
|
|
1015
|
-
return Boolean(idleCmds?.idleInputCommand && this.isMessagePollingIdleCommand(idleCmds.idleInputCommand));
|
|
1016
|
-
})();
|
|
1017
|
-
if (willResumePolling) {
|
|
1018
|
-
this.emit('status-change', sessionId, 'waiting_input', {
|
|
1019
|
-
actionType: 'idle',
|
|
1020
|
-
actionLabel: '监听消息',
|
|
1021
|
-
actionDetail: 'aws-mcp/poll_message',
|
|
1022
|
-
usage: session.totalUsage,
|
|
1023
|
-
});
|
|
1024
|
-
}
|
|
1025
|
-
else {
|
|
1026
|
-
this.emit('status-change', sessionId, 'waiting_input', { usage: session.totalUsage });
|
|
1027
|
-
}
|
|
1028
|
-
// 如果有空闲命令配置,启动空闲检测
|
|
1029
|
-
if (this.idleCommands.has(sessionId)) {
|
|
1030
|
-
this.startIdleDetection(sessionId);
|
|
1031
|
-
}
|
|
1032
|
-
}
|
|
1033
|
-
break;
|
|
1034
|
-
case 'system':
|
|
1035
|
-
// 系统消息,可能包含初始化数据
|
|
1036
|
-
if (msg.subtype === 'init') {
|
|
1037
|
-
console.log(`[ClaudeSdkAdapter] Session ${sessionId} initialized: model=${msg.model}, tools=${msg.tools?.length || 0}`);
|
|
1038
|
-
}
|
|
1039
|
-
break;
|
|
1040
|
-
case 'assistant':
|
|
1041
|
-
// AI 响应完成,可能包含完整消息
|
|
1042
|
-
this.applyUsage(session, msg.message?.usage);
|
|
1043
|
-
if (msg.message?.content) {
|
|
1044
|
-
const textContent = msg.message.content
|
|
1045
|
-
.filter((c) => c.type === 'text' && c.text)
|
|
1046
|
-
.map((c) => c.text || '')
|
|
1047
|
-
.join('');
|
|
1048
|
-
if (textContent) {
|
|
1049
|
-
session.messages.push({
|
|
1050
|
-
id: uuidv4(),
|
|
1051
|
-
sessionId,
|
|
1052
|
-
role: 'assistant',
|
|
1053
|
-
content: textContent,
|
|
1054
|
-
timestamp,
|
|
1055
|
-
});
|
|
1056
|
-
}
|
|
1057
|
-
}
|
|
1058
|
-
break;
|
|
1059
|
-
case 'user':
|
|
1060
|
-
// 用户消息(工具结果)
|
|
1061
|
-
if (msg.tool_use_result !== undefined) {
|
|
1062
|
-
// 工具执行结果
|
|
1063
|
-
}
|
|
1064
|
-
break;
|
|
1065
|
-
case 'error':
|
|
1066
|
-
session.status = 'error';
|
|
1067
|
-
this.emit('status-change', sessionId, 'error');
|
|
1068
|
-
this.emitEvent(sessionId, {
|
|
1069
|
-
type: 'error',
|
|
1070
|
-
sessionId,
|
|
1071
|
-
timestamp,
|
|
1072
|
-
data: { text: msg.content || 'Unknown error' },
|
|
1073
|
-
});
|
|
1074
|
-
break;
|
|
1075
|
-
case 'stream_event':
|
|
1076
|
-
// SDK 流式事件 - 处理工具调用的增量更新
|
|
1077
|
-
this.handleStreamEvent(sessionId, session, msg, timestamp);
|
|
1078
|
-
break;
|
|
1079
|
-
default:
|
|
1080
|
-
// 未知消息类型,记录日志
|
|
1081
|
-
console.log(`[ClaudeSdkAdapter] Unknown message type: ${msg.type}`, msg);
|
|
1082
|
-
}
|
|
1083
|
-
}
|
|
1084
|
-
handleToolUse(sessionId, session, msg, timestamp) {
|
|
1085
|
-
// 检查是否与 stream_event 处理的工具调用匹配(去重)
|
|
1086
|
-
const recentInfo = this.recentToolNames.get(sessionId);
|
|
1087
|
-
const toolNameSuffix = this.extractToolNameSuffix(msg.tool_name || '');
|
|
1088
|
-
if (recentInfo) {
|
|
1089
|
-
const timeDiff = Date.now() - recentInfo.time;
|
|
1090
|
-
// 如果在 30s 内,且工具名后缀相同,认为是同一个调用(content_block_stop 已发出事件)
|
|
1091
|
-
if (timeDiff < 30000 && recentInfo.name === toolNameSuffix) {
|
|
1092
|
-
console.log(`[ClaudeSdkAdapter] Tool use ${msg.tool_name} matches recent stream_event (${recentInfo.name}), skipping duplicate event`);
|
|
1093
|
-
// 清理记录
|
|
1094
|
-
this.recentToolNames.delete(sessionId);
|
|
1095
|
-
this.activeToolUseIds.delete(sessionId);
|
|
1096
|
-
// 只记录到消息历史,不重复发送事件(content_block_stop 已经发过)
|
|
1097
|
-
session.messages.push({
|
|
1098
|
-
id: uuidv4(),
|
|
1099
|
-
sessionId,
|
|
1100
|
-
role: 'tool_use',
|
|
1101
|
-
content: `${msg.tool_name}: ${JSON.stringify(msg.tool_input).slice(0, 100)}`,
|
|
1102
|
-
timestamp,
|
|
1103
|
-
toolName: msg.tool_name,
|
|
1104
|
-
toolInput: msg.tool_input,
|
|
1105
|
-
toolUseId: msg.tool_use_id,
|
|
1106
|
-
});
|
|
1107
|
-
return;
|
|
1108
|
-
}
|
|
1109
|
-
}
|
|
1110
|
-
// 清理过期记录
|
|
1111
|
-
this.recentToolNames.delete(sessionId);
|
|
1112
|
-
this.activeToolUseIds.delete(sessionId);
|
|
1113
|
-
// 获取工具动作分类信息
|
|
1114
|
-
const actionInfo = getToolActionInfo(msg.tool_name, msg.tool_input);
|
|
1115
|
-
console.log(`[ClaudeSdkAdapter] Tool use: ${msg.tool_name}, actionType: ${actionInfo.actionType}, actionDetail: ${actionInfo.actionDetail}`);
|
|
1116
|
-
const mcpServer = this.extractMcpServerName(msg.tool_name || '');
|
|
1117
|
-
this.emitEvent(sessionId, {
|
|
1118
|
-
type: 'tool_use_start',
|
|
1119
|
-
sessionId,
|
|
1120
|
-
timestamp,
|
|
1121
|
-
data: {
|
|
1122
|
-
toolName: msg.tool_name,
|
|
1123
|
-
toolInput: msg.tool_input,
|
|
1124
|
-
toolUseId: msg.tool_use_id,
|
|
1125
|
-
actionType: actionInfo.actionType,
|
|
1126
|
-
actionLabel: actionInfo.actionLabel,
|
|
1127
|
-
actionDetail: actionInfo.actionDetail,
|
|
1128
|
-
...(mcpServer ? { metadata: { mcpServer } } : {}),
|
|
1129
|
-
},
|
|
1130
|
-
});
|
|
1131
|
-
// 记录到消息历史
|
|
1132
|
-
session.messages.push({
|
|
1133
|
-
id: uuidv4(),
|
|
1134
|
-
sessionId,
|
|
1135
|
-
role: 'tool_use',
|
|
1136
|
-
content: `${msg.tool_name}: ${JSON.stringify(msg.tool_input).slice(0, 100)}`,
|
|
1137
|
-
timestamp,
|
|
1138
|
-
toolName: msg.tool_name,
|
|
1139
|
-
toolInput: msg.tool_input,
|
|
1140
|
-
toolUseId: msg.tool_use_id,
|
|
1141
|
-
});
|
|
1142
|
-
}
|
|
1143
|
-
/**
|
|
1144
|
-
* 处理 SDK 流式事件
|
|
1145
|
-
* 用于检测工具调用开始、增量更新和结束
|
|
1146
|
-
*
|
|
1147
|
-
* 注意:此方法只更新状态,不发送时间线事件
|
|
1148
|
-
* 时间线事件由 handleToolUse 发送,这样能获取完整的工具参数
|
|
1149
|
-
*/
|
|
1150
|
-
handleStreamEvent(sessionId, session, msg, timestamp) {
|
|
1151
|
-
const event = msg.event;
|
|
1152
|
-
if (!event)
|
|
1153
|
-
return;
|
|
1154
|
-
// 处理 content_block_start 事件 - 工具调用开始
|
|
1155
|
-
if (event.type === 'content_block_start') {
|
|
1156
|
-
const contentBlock = event.content_block;
|
|
1157
|
-
if (contentBlock?.type === 'tool_use' && contentBlock.name) {
|
|
1158
|
-
const toolName = contentBlock.name;
|
|
1159
|
-
const toolId = contentBlock.id || `stream_${Date.now()}`;
|
|
1160
|
-
console.log(`[ClaudeSdkAdapter] Tool use started via stream_event: ${toolName} (id: ${toolId})`);
|
|
1161
|
-
// 记录当前活跃的工具调用 ID(用于去重)
|
|
1162
|
-
this.activeToolUseIds.set(sessionId, toolId);
|
|
1163
|
-
// 记录工具名(用于与后续 tool_use 消息匹配去重)
|
|
1164
|
-
// 提取工具名后缀:aws-mcp__get_profile -> get_profile
|
|
1165
|
-
const toolNameSuffix = this.extractToolNameSuffix(toolName);
|
|
1166
|
-
this.recentToolNames.set(sessionId, { name: toolNameSuffix, time: Date.now() });
|
|
1167
|
-
// 初始化参数缓冲区
|
|
1168
|
-
const bufferKey = `${sessionId}:${toolId}`;
|
|
1169
|
-
this.toolInputBuffers.set(bufferKey, '');
|
|
1170
|
-
// 先不更新状态,等待收集参数后更新
|
|
1171
|
-
}
|
|
1172
|
-
}
|
|
1173
|
-
// 处理 content_block_delta 事件 - 收集工具参数增量
|
|
1174
|
-
if (event.type === 'content_block_delta') {
|
|
1175
|
-
if (event.delta?.type === 'input_json_delta' && event.delta.partial_json) {
|
|
1176
|
-
const toolId = this.activeToolUseIds.get(sessionId);
|
|
1177
|
-
if (toolId) {
|
|
1178
|
-
const bufferKey = `${sessionId}:${toolId}`;
|
|
1179
|
-
const currentBuffer = this.toolInputBuffers.get(bufferKey) || '';
|
|
1180
|
-
this.toolInputBuffers.set(bufferKey, currentBuffer + event.delta.partial_json);
|
|
1181
|
-
console.log(`[ClaudeSdkAdapter] Tool input delta at index ${event.index}, buffer length: ${currentBuffer.length + event.delta.partial_json.length}`);
|
|
1182
|
-
}
|
|
1183
|
-
}
|
|
1184
|
-
}
|
|
1185
|
-
// 处理 content_block_stop 事件 - 工具调用参数收集完成
|
|
1186
|
-
if (event.type === 'content_block_stop') {
|
|
1187
|
-
const toolId = this.activeToolUseIds.get(sessionId);
|
|
1188
|
-
const recentInfo = this.recentToolNames.get(sessionId);
|
|
1189
|
-
if (toolId && recentInfo) {
|
|
1190
|
-
const bufferKey = `${sessionId}:${toolId}`;
|
|
1191
|
-
const jsonStr = this.toolInputBuffers.get(bufferKey) || '';
|
|
1192
|
-
console.log(`[ClaudeSdkAdapter] Content block stopped at index ${event.index}, collected JSON length: ${jsonStr.length}`);
|
|
1193
|
-
// 尝试解析完整的工具参数
|
|
1194
|
-
let toolInput;
|
|
1195
|
-
if (jsonStr) {
|
|
1196
|
-
try {
|
|
1197
|
-
toolInput = JSON.parse(jsonStr);
|
|
1198
|
-
console.log(`[ClaudeSdkAdapter] Parsed tool input:`, JSON.stringify(toolInput).slice(0, 200));
|
|
1199
|
-
}
|
|
1200
|
-
catch {
|
|
1201
|
-
console.warn(`[ClaudeSdkAdapter] Failed to parse tool input JSON:`, jsonStr.slice(0, 100));
|
|
1202
|
-
}
|
|
1203
|
-
}
|
|
1204
|
-
// 获取工具动作分类信息(使用��整参数)
|
|
1205
|
-
const actionInfo = { ...getToolActionInfo(recentInfo.name, toolInput), actionId: toolId };
|
|
1206
|
-
const mcpServer = this.extractMcpServerName(recentInfo.name);
|
|
1207
|
-
// 更新状态
|
|
1208
|
-
session.status = 'tool_using';
|
|
1209
|
-
this.emit('status-change', sessionId, 'tool_using', {
|
|
1210
|
-
actionType: actionInfo.actionType,
|
|
1211
|
-
actionLabel: actionInfo.actionLabel,
|
|
1212
|
-
actionDetail: actionInfo.actionDetail,
|
|
1213
|
-
actionId: actionInfo.actionId,
|
|
1214
|
-
usage: session.totalUsage,
|
|
1215
|
-
...(mcpServer ? { metadata: { mcpServer } } : {}),
|
|
1216
|
-
});
|
|
1217
|
-
this.stopIdleDetection(sessionId);
|
|
1218
|
-
// 发送工具调用开始事件(此时有完整参数)
|
|
1219
|
-
this.emitEvent(sessionId, {
|
|
1220
|
-
type: 'tool_use_start',
|
|
1221
|
-
sessionId,
|
|
1222
|
-
timestamp,
|
|
1223
|
-
data: {
|
|
1224
|
-
toolName: recentInfo.name,
|
|
1225
|
-
toolInput,
|
|
1226
|
-
toolUseId: toolId,
|
|
1227
|
-
actionType: actionInfo.actionType,
|
|
1228
|
-
actionLabel: actionInfo.actionLabel,
|
|
1229
|
-
actionDetail: actionInfo.actionDetail,
|
|
1230
|
-
actionId: actionInfo.actionId,
|
|
1231
|
-
...(mcpServer ? { metadata: { mcpServer } } : {}),
|
|
1232
|
-
},
|
|
1233
|
-
});
|
|
1234
|
-
// 记录到消息历史
|
|
1235
|
-
session.messages.push({
|
|
1236
|
-
id: uuidv4(),
|
|
1237
|
-
sessionId,
|
|
1238
|
-
role: 'tool_use',
|
|
1239
|
-
content: `${recentInfo.name}: ${jsonStr.slice(0, 100)}`,
|
|
1240
|
-
timestamp,
|
|
1241
|
-
toolName: recentInfo.name,
|
|
1242
|
-
toolInput,
|
|
1243
|
-
toolUseId: toolId,
|
|
1244
|
-
});
|
|
1245
|
-
// 清理缓冲区(保留 recentToolNames 用于后续 tool_use 消息去重)
|
|
1246
|
-
this.toolInputBuffers.delete(bufferKey);
|
|
1247
|
-
this.activeToolUseIds.delete(sessionId);
|
|
1248
|
-
// 注意:不在此处删除 recentToolNames,让后续 tool_use 消息能匹配去重
|
|
1249
|
-
// recentToolNames 会在 handleToolUse 中匹配成功后删除,或过期后自动清理
|
|
1250
|
-
}
|
|
1251
|
-
}
|
|
1252
|
-
}
|
|
1253
|
-
/**
|
|
1254
|
-
* 从 Claude SDK 工具名中提取 MCP 服务器名
|
|
1255
|
-
* Claude SDK 的 MCP 工具命名格式:mcp__<server>__<tool>
|
|
1256
|
-
* 例如:mcp__memory-server__list_memories -> memory-server
|
|
1257
|
-
*/
|
|
1258
|
-
extractMcpServerName(toolName) {
|
|
1259
|
-
if (toolName.startsWith('mcp__')) {
|
|
1260
|
-
const parts = toolName.split('__');
|
|
1261
|
-
// mcp__<server>__<tool> — 至少 3 段
|
|
1262
|
-
if (parts.length >= 3) {
|
|
1263
|
-
return parts[1];
|
|
1264
|
-
}
|
|
1265
|
-
}
|
|
1266
|
-
return undefined;
|
|
1267
|
-
}
|
|
1268
|
-
/**
|
|
1269
|
-
* 提取工具名后缀
|
|
1270
|
-
* 用于匹配不同格式的工具名
|
|
1271
|
-
* 例如:aws-mcp__get_profile -> get_profile
|
|
1272
|
-
*/
|
|
1273
|
-
extractToolNameSuffix(toolName) {
|
|
1274
|
-
// 如果包含 __,取最后一部分
|
|
1275
|
-
if (toolName.includes('__')) {
|
|
1276
|
-
return toolName.split('__').pop() || toolName;
|
|
1277
|
-
}
|
|
1278
|
-
// 如果包含 -mcp__,取后面的部分
|
|
1279
|
-
if (toolName.includes('-mcp__')) {
|
|
1280
|
-
const idx = toolName.indexOf('-mcp__');
|
|
1281
|
-
return toolName.substring(idx + 6);
|
|
1282
|
-
}
|
|
1283
|
-
return toolName;
|
|
1284
|
-
}
|
|
1285
|
-
emitEvent(sessionId, event) {
|
|
1286
|
-
this.emit('event', event);
|
|
1287
|
-
}
|
|
1288
|
-
cleanupSession(sessionId) {
|
|
1289
|
-
this.stopIdleDetection(sessionId);
|
|
1290
|
-
this.sessions.delete(sessionId);
|
|
1291
|
-
this.sdkQueries.delete(sessionId);
|
|
1292
|
-
this.inputStreams.delete(sessionId);
|
|
1293
|
-
this.abortControllers.delete(sessionId);
|
|
1294
|
-
this.pendingPermissions.delete(sessionId);
|
|
1295
|
-
this.pendingQuestions.delete(sessionId);
|
|
1296
|
-
this.idleCommands.delete(sessionId);
|
|
1297
|
-
this.idlePollLoops.delete(sessionId);
|
|
1298
|
-
this.lastActivityAt.delete(sessionId);
|
|
1299
|
-
}
|
|
1300
|
-
}
|
|
1
|
+
import{execSync as S}from"child_process";import{EventEmitter as C}from"events";import*as p from"fs";import*as c from"path";import{v4 as h}from"uuid";import{importBridgeSdkPackage as y}from"../utils/sdk-package-loader.js";import{IdlePollLoop as k}from"./idle-poll-loop.js";import{getToolActionInfo as f}from"./types.js";class v{constructor(){this.queue=[],this.resolve=null,this.done=!1}enqueue(t){if(!this.done)if(this.resolve){const e=this.resolve;this.resolve=null,e({value:t,done:!1})}else this.queue.push(t)}close(){if(this.done=!0,this.resolve){const t=this.resolve;this.resolve=null,t({value:void 0,done:!0})}}[Symbol.asyncIterator](){return{next:()=>this.queue.length>0?Promise.resolve({value:this.queue.shift(),done:!1}):this.done?Promise.resolve({value:void 0,done:!0}):new Promise(t=>{this.resolve=t})}}}class A extends C{constructor(){super(...arguments),this.providerId="claude-code",this.displayName="Claude Code",this.sessions=new Map,this.sdkQueries=new Map,this.inputStreams=new Map,this.abortControllers=new Map,this.sdkModule=null,this.pendingPermissions=new Map,this.pendingQuestions=new Map,this.activeToolUseIds=new Map,this.recentToolNames=new Map,this.toolInputBuffers=new Map,this.idleCommands=new Map,this.idleTimers=new Map,this.idlePollLoops=new Map,this.lastActivityAt=new Map}async startSession(t,e){console.log(`[ClaudeSdkAdapter] \u2605\u2605\u2605 \u542F\u52A8 Claude Code SDK \u4F1A\u8BDD \u2605\u2605\u2605 sessionId="${t}", command="${e.command}", workingDirectory="${e.workingDirectory}"`);const o={sessionId:t,status:"starting",messages:[],createdAt:new Date().toISOString(),totalUsage:{inputTokens:0,outputTokens:0}};this.sessions.set(t,o);try{const a=await this.loadSdk(),n=new AbortController;this.abortControllers.set(t,n);const i=new v;this.inputStreams.set(t,i);const s=this.buildQueryOptions(t,e,n),r=a.query({prompt:i,options:s});if(this.sdkQueries.set(t,r),this.consumeStream(t,r).catch(l=>{console.error(`[ClaudeSdkAdapter] Stream error for ${t}:`,l)}),await new Promise(l=>setTimeout(l,100)),e.initialPrompt){o.status="starting",this.emit("status-change",t,"starting");const l={id:h(),sessionId:t,role:"user",content:e.initialPrompt,timestamp:new Date().toISOString()};o.messages.push(l),this.emit("conversation-message",t,l);const d=this.parseMessageContent(e.initialPrompt);i.enqueue({type:"user",message:{role:"user",content:d}})}else o.status="waiting_input",this.emit("status-change",t,"waiting_input",{usage:o.totalUsage}),this.idleCommands.has(t)&&this.startIdleDetection(t)}catch(a){const n=a instanceof Error?a.message:String(a);throw o.status="error",this.emit("status-change",t,"error"),this.emitEvent(t,{type:"error",sessionId:t,timestamp:new Date().toISOString(),data:{text:`Failed to start session: ${n}`}}),a}}async sendMessage(t,e){const o=this.sessions.get(t),a=this.inputStreams.get(t);if(!o||!a)throw new Error(`Session ${t} not found`);if(o.status==="error")throw new Error(`Session ${t} is in error state`);const n={id:h(),sessionId:t,role:"user",content:e,timestamp:new Date().toISOString()};o.messages.push(n),this.emit("conversation-message",t,n),o.status="thinking",this.emit("status-change",t,"thinking");const i=this.parseMessageContent(e);a.enqueue({type:"user",message:{role:"user",content:i}})}parseMessageContent(t){const e=[],o=/\[图片\s*:\s*([^\]\n]+?)\s*\]/gi;let a=0,n=o.exec(t);for(;n!==null;){if(n.index>a){const s=t.slice(a,n.index).trim();s&&e.push({type:"text",text:s})}const i=n[1].trim();i&&(console.log(`[ClaudeSdkAdapter] \u6DFB\u52A0\u56FE\u7247 URL: ${i}`),e.push({type:"image",source:{type:"url",url:i}})),a=n.index+n[0].length,n=o.exec(t)}if(a<t.length){const i=t.slice(a).trim();i&&e.push({type:"text",text:i})}return e.length===0&&e.push({type:"text",text:t}),e}async sendConfirmation(t,e){const o=this.pendingPermissions.get(t);o&&(e?o.resolve({behavior:"allow",updatedInput:o.toolInput}):o.resolve({behavior:"deny",message:"User denied"}),this.pendingPermissions.delete(t))}async sendQuestionAnswer(t,e){const o=this.pendingQuestions.get(t);if(!o)return;const a=o.toolInput.questions;let n=`\u7528\u6237\u5DF2\u56DE\u7B54\u4E86\u60A8\u7684\u95EE\u9898\uFF1A
|
|
2
|
+
`;Array.isArray(a)?a.forEach((i,s)=>{const r=String(s),l=e[r]||e[i.header||""]||e[i.question]||"\uFF08\u672A\u586B\u5199\uFF09";n+=`\u2022 ${i.header||i.question}\uFF1A${l}
|
|
3
|
+
`}):n+=JSON.stringify(e),o.resolve({behavior:"deny",message:n}),this.pendingQuestions.delete(t)}async abortCurrentTurn(t){const e=this.abortControllers.get(t);e&&e.abort()}async terminateSession(t){const e=this.sessions.get(t),o=this.sdkQueries.get(t),a=this.inputStreams.get(t);if(a&&a.close(),o)try{o.close()}catch{}e&&(e.status="terminated",this.emit("status-change",t,"terminated"),this.emitEvent(t,{type:"session_complete",sessionId:t,timestamp:new Date().toISOString(),data:{exitCode:0}})),this.cleanupSession(t)}getConversation(t){return this.sessions.get(t)?.messages??[]}hasSession(t){return this.sessions.has(t)}getProviderSessionId(t){return this.sessions.get(t)?.providerSessionId}getSessionStatus(t){return this.sessions.get(t)?.status}getSessionPid(t){return this.sessions.get(t)?.pid}cleanup(){for(const t of this.sessions.keys())try{this.terminateSession(t)}catch{}this.sessions.clear(),this.sdkQueries.clear(),this.inputStreams.clear(),this.abortControllers.clear(),this.pendingPermissions.clear(),this.pendingQuestions.clear(),this.idleCommands.clear(),this.idleTimers.clear()}setIdleCommands(t,e){this.idleCommands.set(t,e);const o=this.sessions.get(t);o&&o.status==="waiting_input"&&(e.idleInputCommand||e.nonInputCommand)&&this.startIdleDetection(t)}startIdleDetection(t){this.stopIdleDetection(t);const e=this.idleCommands.get(t);if(!e?.idleInputCommand)return;if(this.isMessagePollingIdleCommand(e.idleInputCommand)){this.getIdlePollLoop(t).start();return}this.lastActivityAt.set(t,Date.now());const o=setInterval(()=>{this.checkIdleState(t)},500);this.idleTimers.set(t,o)}stopIdleDetection(t){this.idlePollLoops.get(t)?.stop();const e=this.idleTimers.get(t);e&&(clearInterval(e),this.idleTimers.delete(t))}checkIdleState(t){const e=this.sessions.get(t);if(!e||e.status!=="waiting_input")return;const o=this.idleCommands.get(t);if(!o||!o.idleInputCommand&&!o.nonInputCommand)return;const a=this.lastActivityAt.get(t)||0;if(!(Date.now()-a<1500)&&(this.lastActivityAt.set(t,Date.now()),o.idleInputCommand)){const s=o.idleInputCommand;this.isMessagePollingIdleCommand(s)?this.sendIdlePrompt(t,this.buildMessagePollingIdlePrompt()):this.sendIdlePrompt(t,s)}}sendIdlePrompt(t,e,o){const a=this.inputStreams.get(t);if(!a)return;const n=this.sessions.get(t);n&&!o?.keepWaitingStatus&&(n.status="thinking",this.emit("status-change",t,"thinking")),a.enqueue({type:"user",message:{role:"user",content:[{type:"text",text:e}]}})}isMessagePollingIdleCommand(t){const e=t.trim();if(!e)return!1;const o=e.toLowerCase();return o.includes("poll_message")||o.includes("get_message")||e.includes("\u53D6\u6D88\u606F")}buildMessagePollingIdlePrompt(){return"\u7CFB\u7EDF\u63D0\u9192\uFF1A\u5F53\u524D\u5DE5\u4F5C\u5DF2\u5B8C\u6210\u6216\u8FD0\u884C\u65F6\u5DF2\u8FDB\u5165\u7A7A\u95F2\u72B6\u6001\u3002\u8BF7\u7ACB\u5373\u8C03\u7528 poll_message \u5DE5\u5177\u963B\u585E\u7B49\u5F85\u65B0\u6D88\u606F\u548C\u6D41\u7A0B\u7EBF\u4EFB\u52A1\u63D0\u9192\uFF1B\u6536\u5230\u6D41\u7A0B\u4EFB\u52A1\u63D0\u9192\u540E\u8C03\u7528 my_task \u67E5\u770B\u5DF2\u7531\u540E\u53F0\u9759\u9ED8\u62A2\u5360\u7684\u4EFB\u52A1\u8BE6\u60C5\u5E76\u76F4\u63A5\u5904\u7406\uFF1B\u5B8C\u6210\u4EFB\u52A1\u540E\u8C03\u7528 submit_task_result \u63D0\u4EA4\u7ED3\u679C\uFF0C\u7136\u540E\u7EE7\u7EED poll_message\u3002"}getIdlePollLoop(t){const e=this.idlePollLoops.get(t);if(e)return e;const o=new k({getStatus:()=>this.sessions.get(t)?.status,getCommand:()=>this.idleCommands.get(t)?.idleInputCommand,isPollingCommand:a=>this.isMessagePollingIdleCommand(a),dispatchPollingPrompt:()=>this.sendIdlePrompt(t,this.buildMessagePollingIdlePrompt(),{keepWaitingStatus:!0}),dispatchRegularIdleCommand:a=>this.sendIdlePrompt(t,a)});return this.idlePollLoops.set(t,o),o}async sendShortcutInput(t,e){await this.executeIdleCommand(t,e)}async executeIdleCommand(t,e){const o=this.inputStreams.get(t);if(!o)return;const a=/\[(.+?)\]/g;let n=0;const i=[];let s=a.exec(e);for(;s!==null;){s.index>n&&i.push({type:"text",value:e.slice(n,s.index)});const r=s[1];if(r.startsWith("wait:")){const l=parseFloat(r.slice(5));isNaN(l)||i.push({type:"wait",value:l*1e3})}else{const l={enter:"\r",up:"\x1B[A",down:"\x1B[B",left:"\x1B[D",right:"\x1B[C",tab:" ",esc:"\x1B",escape:"\x1B",space:" ","ctrl+c":""};i.push({type:"key",value:l[r.toLowerCase()]||r})}n=s.index+s[0].length,s=a.exec(e)}n<e.length&&i.push({type:"text",value:e.slice(n)});for(const r of i)if(r.type==="wait")await new Promise(l=>setTimeout(l,r.value));else{const l=String(r.value);o.enqueue({type:"user",message:{role:"user",content:[{type:"text",text:l}]}})}}updateActivity(t){this.lastActivityAt.set(t,Date.now())}async resumeSession(t,e,o){const a={sessionId:t,providerSessionId:e,status:"starting",messages:[],createdAt:new Date().toISOString(),totalUsage:{inputTokens:0,outputTokens:0}};this.sessions.set(t,a);try{await this.startSession(t,o),a.providerSessionId=e}catch(n){throw a.status="error",this.emit("status-change",t,"error"),n}}async loadSdk(){if(!this.sdkModule)try{this.sdkModule=await y("@anthropic-ai/claude-agent-sdk")}catch(t){throw new Error(`Failed to load @anthropic-ai/claude-agent-sdk. Please install: npm install @anthropic-ai/claude-agent-sdk. Original error: ${t}`)}return this.sdkModule}findClaudeExecutable(t){const e=this.extractExecutableCommand(t);if(c.isAbsolute(e)&&p.existsSync(e)){const i=this.resolveWindowsWrapper(e);if(i)return i;if(this.isWindowsWrapperPath(e)){console.warn(`[ClaudeSdkAdapter] Skipping wrapper script for Claude Code executable: ${e}`);return}return e}const o=this.resolveExecutableFromPath(e);if(o)return o;if(process.platform==="win32"&&this.shouldUseDefaultClaudeFallbacks(e)){const i=process.env.USERPROFILE||"",s=process.env.LOCALAPPDATA||"",r=process.env.APPDATA||"",l=[c.join(s,"Volta","tools","shared","@anthropic-ai","claude-code","cli.js"),c.join(s,"Volta","tools","image","packages","@anthropic-ai","claude-code","cli.js")];for(const u of l)if(p.existsSync(u))return console.log(`[ClaudeSdkAdapter] Found Claude Code CLI via Volta: ${u}`),u;const d=[c.join(r,"npm","node_modules","@anthropic-ai","claude-code","cli.js"),c.join(s,"Programs","claude-code","claude.exe"),c.join(i,".local","bin","claude.exe")];for(const u of d)if(u&&p.existsSync(u))return console.log(`[ClaudeSdkAdapter] Found Claude Code CLI: ${u}`),u}const a=process.env.HOME||"",n=[c.join(a,".local","bin","claude"),c.join(a,".npm-global","bin","claude"),c.join(a,".local","share","mise","shims","claude"),c.join(a,".asdf","shims","claude"),"/opt/homebrew/bin/claude","/usr/local/bin/claude","/usr/bin/claude","/usr/local/lib/node_modules/@anthropic-ai/claude-code/cli.js","/usr/lib/node_modules/@anthropic-ai/claude-code/cli.js",c.join(a,".local","share","volta","tools","shared","@anthropic-ai","claude-code","cli.js"),c.join(a,".volta","tools","shared","@anthropic-ai","claude-code","cli.js")];if(this.shouldUseDefaultClaudeFallbacks(e)){for(const i of n)if(i&&p.existsSync(i))return console.log(`[ClaudeSdkAdapter] Found Claude Code CLI: ${i}`),i}if(this.shouldUseDefaultClaudeFallbacks(e))try{const i=S("npm root -g",{encoding:"utf8",timeout:5e3}).trim(),s=c.join(i,"@anthropic-ai","claude-code","cli.js");if(p.existsSync(s))return console.log(`[ClaudeSdkAdapter] Found Claude Code CLI via npm root: ${s}`),s}catch{}console.warn(`[ClaudeSdkAdapter] Claude Code executable not found for command: ${e}`)}extractExecutableCommand(t){const o=((t||"claude").trim()||"claude").match(/^\s*(?:"([^"]+)"|'([^']+)'|(\S+))/);return o?.[1]||o?.[2]||o?.[3]||"claude"}resolveExecutableFromPath(t){const e=process.env.PATH||"";if(!e)return;const o=c.extname(t).length>0,a=process.platform==="win32"?this.getWindowsPathExtensions(o):[""];for(const n of e.split(c.delimiter))if(n)for(const i of a){const s=c.join(n,`${t}${i}`);if(p.existsSync(s)){if(this.isWindowsWrapperPath(s)){const r=this.resolveWindowsWrapper(s);if(r)return r;continue}return console.log(`[ClaudeSdkAdapter] Found Claude Code CLI via PATH: ${s}`),s}}}getWindowsPathExtensions(t){return t?[""]:(process.env.PATHEXT||".EXE;.CMD;.BAT;.COM;.PS1").split(";").map(o=>o.trim()).filter(o=>o.length>0)}shouldUseDefaultClaudeFallbacks(t){return["claude","claude-code","claudecode"].includes(t.toLowerCase())}isWindowsWrapperPath(t){const e=c.extname(t).toLowerCase();return[".cmd",".bat",".ps1"].includes(e)}resolveWindowsWrapper(t){if(this.isWindowsWrapperPath(t))return this.resolveCliJsFromWrapper(t)}resolveCliJsFromWrapper(t){try{if(p.readFileSync(t,"utf8").includes("volta run")){const n=c.join(process.env.LOCALAPPDATA||"","Volta","tools","shared","@anthropic-ai","claude-code","cli.js");if(p.existsSync(n))return n}const o=c.dirname(t),a=c.join(o,"node_modules","@anthropic-ai","claude-code","cli.js");if(p.existsSync(a))return a}catch{}}buildQueryOptions(t,e,o){const a={cwd:e.workingDirectory,abortController:o,settingSources:["user","project","local"],includePartialMessages:!0},n=this.findClaudeExecutable(e.command);if(n?(a.pathToClaudeCodeExecutable=n,console.log(`[ClaudeSdkAdapter] \u2605\u2605\u2605 \u4F7F\u7528 Claude Code \u53EF\u6267\u884C\u6587\u4EF6 \u2605\u2605\u2605 path="${n}"`)):console.warn(`[ClaudeSdkAdapter] \u2605\u2605\u2605 \u8B66\u544A\uFF1A\u672A\u627E\u5230 Claude Code \u53EF\u6267\u884C\u6587\u4EF6 \u2605\u2605\u2605 command="${e.command}"`),e.model&&(a.model=e.model),e.maxTurns&&e.maxTurns>0&&(a.maxTurns=e.maxTurns),e.mcpConfigPath)try{const i=p.readFileSync(e.mcpConfigPath,"utf-8"),s=JSON.parse(i);a.mcpServers={...e.extraMcpServers,...s.mcpServers}}catch{a.mcpServers=e.extraMcpServers||{}}else a.mcpServers=e.extraMcpServers||{};return e.allowedTools&&e.allowedTools.length>0?a.allowedTools=e.allowedTools:a.allowedTools=["Read","Write","Edit","Bash","Glob","Grep","WebSearch","WebFetch","Task","NotebookEdit","LSP","Skill","mcp__*"],a.env={...process.env,...e.envOverrides},a.canUseTool=this.createPermissionHandler(t,e.autoAccept),a}createPermissionHandler(t,e){return async(o,a,n)=>o==="AskUserQuestion"||o==="ask_user_question"?new Promise(i=>{this.pendingQuestions.set(t,{resolve:i,toolInput:a}),this.emit("ask-user-question",{sessionId:t,toolCallId:this.activeToolUseIds.get(t)||void 0,questions:a.questions})}):e?{behavior:"allow",updatedInput:a}:new Promise(i=>{this.pendingPermissions.set(t,{resolve:i,toolInput:a}),this.emit("permission-request",{sessionId:t,toolName:o,toolInput:a,permissionPrompt:`Allow ${o}?`})})}async consumeStream(t,e){const o=this.sessions.get(t);if(o)try{for await(const a of e){const n=a;this.handleSdkMessage(t,o,n)}}catch(a){if(this.abortControllers.get(t)?.signal.aborted)return;const n=a instanceof Error?a.message:String(a);this.emitEvent(t,{type:"error",sessionId:t,timestamp:new Date().toISOString(),data:{text:`Stream error: ${n}`}})}}applyUsage(t,e){e&&(Number.isFinite(e.input_tokens)&&(t.totalUsage.inputTokens=Math.max(t.totalUsage.inputTokens,e.input_tokens??0)),Number.isFinite(e.output_tokens)&&(t.totalUsage.outputTokens=Math.max(t.totalUsage.outputTokens,e.output_tokens??0)))}handleSdkMessage(t,e,o){const a=new Date().toISOString();switch(console.log(`[ClaudeSdkAdapter] Received message type: "${o.type}", subtype: "${o.subtype||""}"`),o.tool_name&&console.log(`[ClaudeSdkAdapter] Tool name: "${o.tool_name}"`),o.type){case"text":o.delta&&(e.status!=="thinking"&&(e.status="thinking",this.emit("status-change",t,"thinking"),this.stopIdleDetection(t)),this.emitEvent(t,{type:"text_delta",sessionId:t,timestamp:a,data:{text:o.delta}}));break;case"thinking":e.status!=="thinking"&&(e.status="thinking",this.emit("status-change",t,"thinking"),this.stopIdleDetection(t)),(o.content||o.delta)&&this.emitEvent(t,{type:"thinking",sessionId:t,timestamp:a,data:{text:o.delta||o.content}});break;case"tool_use":case"tool_use_start":{const n={...f(o.tool_name,o.tool_input),actionId:o.tool_use_id},i=n.actionType==="idle"?"waiting_input":"tool_using";e.status=i;const s=this.extractMcpServerName(o.tool_name||"");this.emit("status-change",t,i,{actionType:n.actionType,actionLabel:n.actionLabel,actionDetail:n.actionDetail,actionId:n.actionId,usage:e.totalUsage,...s?{metadata:{mcpServer:s}}:{}}),n.actionType!=="idle"&&this.stopIdleDetection(t),this.handleToolUse(t,e,o,a)}break;case"tool_result":case"tool_use_end":this.emitEvent(t,{type:"tool_use_end",sessionId:t,timestamp:a,data:{toolName:o.tool_name,toolResult:o.tool_result,isError:o.is_error,toolUseId:o.tool_use_id}});break;case"result":{const n=o.subtype==="success";this.applyUsage(e,o.usage),this.emitEvent(t,{type:"turn_complete",sessionId:t,timestamp:a,data:{usage:e.totalUsage,text:n?o.result:void 0}}),e.status="waiting_input",(()=>{const s=this.idleCommands.get(t);return!!(s?.idleInputCommand&&this.isMessagePollingIdleCommand(s.idleInputCommand))})()?this.emit("status-change",t,"waiting_input",{actionType:"idle",actionLabel:"\u76D1\u542C\u6D88\u606F",actionDetail:"aws-mcp/poll_message",usage:e.totalUsage}):this.emit("status-change",t,"waiting_input",{usage:e.totalUsage}),this.idleCommands.has(t)&&this.startIdleDetection(t)}break;case"system":o.subtype==="init"&&console.log(`[ClaudeSdkAdapter] Session ${t} initialized: model=${o.model}, tools=${o.tools?.length||0}`);break;case"assistant":if(this.applyUsage(e,o.message?.usage),o.message?.content){const n=o.message.content.filter(i=>i.type==="text"&&i.text).map(i=>i.text||"").join("");n&&e.messages.push({id:h(),sessionId:t,role:"assistant",content:n,timestamp:a})}break;case"user":o.tool_use_result;break;case"error":e.status="error",this.emit("status-change",t,"error"),this.emitEvent(t,{type:"error",sessionId:t,timestamp:a,data:{text:o.content||"Unknown error"}});break;case"stream_event":this.handleStreamEvent(t,e,o,a);break;default:console.log(`[ClaudeSdkAdapter] Unknown message type: ${o.type}`,o)}}handleToolUse(t,e,o,a){const n=this.recentToolNames.get(t),i=this.extractToolNameSuffix(o.tool_name||"");if(n&&Date.now()-n.time<3e4&&n.name===i){console.log(`[ClaudeSdkAdapter] Tool use ${o.tool_name} matches recent stream_event (${n.name}), skipping duplicate event`),this.recentToolNames.delete(t),this.activeToolUseIds.delete(t),e.messages.push({id:h(),sessionId:t,role:"tool_use",content:`${o.tool_name}: ${JSON.stringify(o.tool_input).slice(0,100)}`,timestamp:a,toolName:o.tool_name,toolInput:o.tool_input,toolUseId:o.tool_use_id});return}this.recentToolNames.delete(t),this.activeToolUseIds.delete(t);const s=f(o.tool_name,o.tool_input);console.log(`[ClaudeSdkAdapter] Tool use: ${o.tool_name}, actionType: ${s.actionType}, actionDetail: ${s.actionDetail}`);const r=this.extractMcpServerName(o.tool_name||"");this.emitEvent(t,{type:"tool_use_start",sessionId:t,timestamp:a,data:{toolName:o.tool_name,toolInput:o.tool_input,toolUseId:o.tool_use_id,actionType:s.actionType,actionLabel:s.actionLabel,actionDetail:s.actionDetail,...r?{metadata:{mcpServer:r}}:{}}}),e.messages.push({id:h(),sessionId:t,role:"tool_use",content:`${o.tool_name}: ${JSON.stringify(o.tool_input).slice(0,100)}`,timestamp:a,toolName:o.tool_name,toolInput:o.tool_input,toolUseId:o.tool_use_id})}handleStreamEvent(t,e,o,a){const n=o.event;if(n){if(n.type==="content_block_start"){const i=n.content_block;if(i?.type==="tool_use"&&i.name){const s=i.name,r=i.id||`stream_${Date.now()}`;console.log(`[ClaudeSdkAdapter] Tool use started via stream_event: ${s} (id: ${r})`),this.activeToolUseIds.set(t,r);const l=this.extractToolNameSuffix(s);this.recentToolNames.set(t,{name:l,time:Date.now()});const d=`${t}:${r}`;this.toolInputBuffers.set(d,"")}}if(n.type==="content_block_delta"&&n.delta?.type==="input_json_delta"&&n.delta.partial_json){const i=this.activeToolUseIds.get(t);if(i){const s=`${t}:${i}`,r=this.toolInputBuffers.get(s)||"";this.toolInputBuffers.set(s,r+n.delta.partial_json),console.log(`[ClaudeSdkAdapter] Tool input delta at index ${n.index}, buffer length: ${r.length+n.delta.partial_json.length}`)}}if(n.type==="content_block_stop"){const i=this.activeToolUseIds.get(t),s=this.recentToolNames.get(t);if(i&&s){const r=`${t}:${i}`,l=this.toolInputBuffers.get(r)||"";console.log(`[ClaudeSdkAdapter] Content block stopped at index ${n.index}, collected JSON length: ${l.length}`);let d;if(l)try{d=JSON.parse(l),console.log("[ClaudeSdkAdapter] Parsed tool input:",JSON.stringify(d).slice(0,200))}catch{console.warn("[ClaudeSdkAdapter] Failed to parse tool input JSON:",l.slice(0,100))}const u={...f(s.name,d),actionId:i},m=this.extractMcpServerName(s.name);e.status="tool_using",this.emit("status-change",t,"tool_using",{actionType:u.actionType,actionLabel:u.actionLabel,actionDetail:u.actionDetail,actionId:u.actionId,usage:e.totalUsage,...m?{metadata:{mcpServer:m}}:{}}),this.stopIdleDetection(t),this.emitEvent(t,{type:"tool_use_start",sessionId:t,timestamp:a,data:{toolName:s.name,toolInput:d,toolUseId:i,actionType:u.actionType,actionLabel:u.actionLabel,actionDetail:u.actionDetail,actionId:u.actionId,...m?{metadata:{mcpServer:m}}:{}}}),e.messages.push({id:h(),sessionId:t,role:"tool_use",content:`${s.name}: ${l.slice(0,100)}`,timestamp:a,toolName:s.name,toolInput:d,toolUseId:i}),this.toolInputBuffers.delete(r),this.activeToolUseIds.delete(t)}}}}extractMcpServerName(t){if(t.startsWith("mcp__")){const e=t.split("__");if(e.length>=3)return e[1]}}extractToolNameSuffix(t){if(t.includes("__"))return t.split("__").pop()||t;if(t.includes("-mcp__")){const e=t.indexOf("-mcp__");return t.substring(e+6)}return t}emitEvent(t,e){this.emit("event",e)}cleanupSession(t){this.stopIdleDetection(t),this.sessions.delete(t),this.sdkQueries.delete(t),this.inputStreams.delete(t),this.abortControllers.delete(t),this.pendingPermissions.delete(t),this.pendingQuestions.delete(t),this.idleCommands.delete(t),this.idlePollLoops.delete(t),this.lastActivityAt.delete(t)}}export{A as ClaudeSdkAdapter};
|
|
4
|
+
|