@getpaseo/server 0.1.16 → 0.1.17
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/scripts/daemon-runner.js +53 -14
- package/dist/scripts/daemon-runner.js.map +1 -1
- package/dist/scripts/dev-runner.js +9 -16
- package/dist/scripts/dev-runner.js.map +1 -1
- package/dist/scripts/supervisor.js +40 -13
- package/dist/scripts/supervisor.js.map +1 -1
- package/dist/server/client/daemon-client.d.ts +23 -3
- package/dist/server/client/daemon-client.d.ts.map +1 -1
- package/dist/server/client/daemon-client.js +81 -8
- package/dist/server/client/daemon-client.js.map +1 -1
- package/dist/server/server/agent/agent-manager.d.ts +3 -1
- package/dist/server/server/agent/agent-manager.d.ts.map +1 -1
- package/dist/server/server/agent/agent-manager.js +146 -24
- package/dist/server/server/agent/agent-manager.js.map +1 -1
- package/dist/server/server/agent/agent-metadata-generator.d.ts.map +1 -1
- package/dist/server/server/agent/agent-metadata-generator.js +13 -4
- package/dist/server/server/agent/agent-metadata-generator.js.map +1 -1
- package/dist/server/server/agent/agent-response-loop.js +1 -1
- package/dist/server/server/agent/agent-response-loop.js.map +1 -1
- package/dist/server/server/agent/agent-sdk-types.d.ts +9 -0
- package/dist/server/server/agent/agent-sdk-types.d.ts.map +1 -1
- package/dist/server/server/agent/agent-sdk-types.js +11 -1
- package/dist/server/server/agent/agent-sdk-types.js.map +1 -1
- package/dist/server/server/agent/agent-storage.d.ts +5 -1
- package/dist/server/server/agent/agent-storage.d.ts.map +1 -1
- package/dist/server/server/agent/agent-storage.js +41 -72
- package/dist/server/server/agent/agent-storage.js.map +1 -1
- package/dist/server/server/agent/agent-title-limits.d.ts +3 -0
- package/dist/server/server/agent/agent-title-limits.d.ts.map +1 -0
- package/dist/server/server/agent/agent-title-limits.js +3 -0
- package/dist/server/server/agent/agent-title-limits.js.map +1 -0
- package/dist/server/server/agent/providers/claude/model-catalog.d.ts +29 -0
- package/dist/server/server/agent/providers/claude/model-catalog.d.ts.map +1 -0
- package/dist/server/server/agent/providers/claude/model-catalog.js +70 -0
- package/dist/server/server/agent/providers/claude/model-catalog.js.map +1 -0
- package/dist/server/server/agent/providers/claude/task-notification-tool-call.d.ts +44 -0
- package/dist/server/server/agent/providers/claude/task-notification-tool-call.d.ts.map +1 -0
- package/dist/server/server/agent/providers/claude/task-notification-tool-call.js +250 -0
- package/dist/server/server/agent/providers/claude/task-notification-tool-call.js.map +1 -0
- package/dist/server/server/agent/providers/claude/tool-call-detail-parser.d.ts.map +1 -1
- package/dist/server/server/agent/providers/claude/tool-call-detail-parser.js +15 -0
- package/dist/server/server/agent/providers/claude/tool-call-detail-parser.js.map +1 -1
- package/dist/server/server/agent/providers/claude-agent.d.ts +3 -2
- package/dist/server/server/agent/providers/claude-agent.d.ts.map +1 -1
- package/dist/server/server/agent/providers/claude-agent.js +240 -106
- package/dist/server/server/agent/providers/claude-agent.js.map +1 -1
- package/dist/server/server/agent/providers/codex/tool-call-mapper.d.ts.map +1 -1
- package/dist/server/server/agent/providers/codex/tool-call-mapper.js +81 -28
- package/dist/server/server/agent/providers/codex/tool-call-mapper.js.map +1 -1
- package/dist/server/server/agent/providers/codex-app-server-agent.d.ts.map +1 -1
- package/dist/server/server/agent/providers/codex-app-server-agent.js +31 -5
- package/dist/server/server/agent/providers/codex-app-server-agent.js.map +1 -1
- package/dist/server/server/agent/providers/opencode-agent.d.ts +10 -1
- package/dist/server/server/agent/providers/opencode-agent.d.ts.map +1 -1
- package/dist/server/server/agent/providers/opencode-agent.js +207 -176
- package/dist/server/server/agent/providers/opencode-agent.js.map +1 -1
- package/dist/server/server/agent/providers/tool-call-detail-primitives.d.ts +15 -0
- package/dist/server/server/agent/providers/tool-call-detail-primitives.d.ts.map +1 -1
- package/dist/server/server/agent/timeline-projection.d.ts +20 -0
- package/dist/server/server/agent/timeline-projection.d.ts.map +1 -1
- package/dist/server/server/agent/timeline-projection.js +73 -0
- package/dist/server/server/agent/timeline-projection.js.map +1 -1
- package/dist/server/server/bootstrap.d.ts +15 -0
- package/dist/server/server/bootstrap.d.ts.map +1 -1
- package/dist/server/server/bootstrap.js +27 -4
- package/dist/server/server/bootstrap.js.map +1 -1
- package/dist/server/server/file-download/token-store.d.ts +0 -1
- package/dist/server/server/file-download/token-store.d.ts.map +1 -1
- package/dist/server/server/file-download/token-store.js.map +1 -1
- package/dist/server/server/file-explorer/service.d.ts.map +1 -1
- package/dist/server/server/file-explorer/service.js +56 -36
- package/dist/server/server/file-explorer/service.js.map +1 -1
- package/dist/server/server/index.js +85 -29
- package/dist/server/server/index.js.map +1 -1
- package/dist/server/server/logger.d.ts +24 -3
- package/dist/server/server/logger.d.ts.map +1 -1
- package/dist/server/server/logger.js +157 -21
- package/dist/server/server/logger.js.map +1 -1
- package/dist/server/server/persisted-config.d.ts +86 -0
- package/dist/server/server/persisted-config.d.ts.map +1 -1
- package/dist/server/server/persisted-config.js +25 -3
- package/dist/server/server/persisted-config.js.map +1 -1
- package/dist/server/server/pid-lock.d.ts +6 -2
- package/dist/server/server/pid-lock.d.ts.map +1 -1
- package/dist/server/server/pid-lock.js +7 -10
- package/dist/server/server/pid-lock.js.map +1 -1
- package/dist/server/server/relay-transport.d.ts.map +1 -1
- package/dist/server/server/relay-transport.js +1 -0
- package/dist/server/server/relay-transport.js.map +1 -1
- package/dist/server/server/session.d.ts +57 -3
- package/dist/server/server/session.d.ts.map +1 -1
- package/dist/server/server/session.js +755 -182
- package/dist/server/server/session.js.map +1 -1
- package/dist/server/server/websocket-server.d.ts +16 -1
- package/dist/server/server/websocket-server.d.ts.map +1 -1
- package/dist/server/server/websocket-server.js +135 -9
- package/dist/server/server/websocket-server.js.map +1 -1
- package/dist/server/server/worktree-bootstrap.d.ts.map +1 -1
- package/dist/server/server/worktree-bootstrap.js +45 -2
- package/dist/server/server/worktree-bootstrap.js.map +1 -1
- package/dist/server/shared/messages.d.ts +2841 -541
- package/dist/server/shared/messages.d.ts.map +1 -1
- package/dist/server/shared/messages.js +99 -5
- package/dist/server/shared/messages.js.map +1 -1
- package/dist/server/shared/tool-call-display.d.ts.map +1 -1
- package/dist/server/shared/tool-call-display.js +3 -0
- package/dist/server/shared/tool-call-display.js.map +1 -1
- package/dist/server/terminal/terminal-manager.d.ts.map +1 -1
- package/dist/server/terminal/terminal-manager.js +1 -13
- package/dist/server/terminal/terminal-manager.js.map +1 -1
- package/dist/server/terminal/terminal.d.ts.map +1 -1
- package/dist/server/terminal/terminal.js +29 -5
- package/dist/server/terminal/terminal.js.map +1 -1
- package/dist/server/utils/worktree.d.ts +1 -0
- package/dist/server/utils/worktree.d.ts.map +1 -1
- package/dist/server/utils/worktree.js +17 -2
- package/dist/server/utils/worktree.js.map +1 -1
- package/dist/src/server/agent/activity-curator.js +228 -0
- package/dist/src/server/agent/activity-curator.js.map +1 -0
- package/dist/src/server/agent/agent-manager.js +1712 -0
- package/dist/src/server/agent/agent-manager.js.map +1 -0
- package/dist/src/server/agent/agent-metadata-generator.js +163 -0
- package/dist/src/server/agent/agent-metadata-generator.js.map +1 -0
- package/dist/src/server/agent/agent-projections.js +262 -0
- package/dist/src/server/agent/agent-projections.js.map +1 -0
- package/dist/src/server/agent/agent-response-loop.js +304 -0
- package/dist/src/server/agent/agent-response-loop.js.map +1 -0
- package/dist/src/server/agent/agent-sdk-types.js +12 -0
- package/dist/src/server/agent/agent-sdk-types.js.map +1 -0
- package/dist/src/server/agent/agent-storage.js +299 -0
- package/dist/src/server/agent/agent-storage.js.map +1 -0
- package/dist/src/server/agent/agent-title-limits.js +3 -0
- package/dist/src/server/agent/agent-title-limits.js.map +1 -0
- package/dist/src/server/agent/audio-utils.js +19 -0
- package/dist/src/server/agent/audio-utils.js.map +1 -0
- package/dist/src/server/agent/dictation-debug.js +50 -0
- package/dist/src/server/agent/dictation-debug.js.map +1 -0
- package/dist/src/server/agent/mcp-server.js +787 -0
- package/dist/src/server/agent/mcp-server.js.map +1 -0
- package/dist/src/server/agent/orchestrator-instructions.js +51 -0
- package/dist/src/server/agent/orchestrator-instructions.js.map +1 -0
- package/dist/src/server/agent/pcm16-resampler.js +63 -0
- package/dist/src/server/agent/pcm16-resampler.js.map +1 -0
- package/dist/src/server/agent/provider-launch-config.js +83 -0
- package/dist/src/server/agent/provider-launch-config.js.map +1 -0
- package/dist/src/server/agent/provider-manifest.js +97 -0
- package/dist/src/server/agent/provider-manifest.js.map +1 -0
- package/dist/src/server/agent/provider-registry.js +45 -0
- package/dist/src/server/agent/provider-registry.js.map +1 -0
- package/dist/src/server/agent/providers/claude/model-catalog.js +70 -0
- package/dist/src/server/agent/providers/claude/model-catalog.js.map +1 -0
- package/dist/src/server/agent/providers/claude/task-notification-tool-call.js +250 -0
- package/dist/src/server/agent/providers/claude/task-notification-tool-call.js.map +1 -0
- package/dist/src/server/agent/providers/claude/tool-call-detail-parser.js +109 -0
- package/dist/src/server/agent/providers/claude/tool-call-detail-parser.js.map +1 -0
- package/dist/src/server/agent/providers/claude/tool-call-mapper.js +238 -0
- package/dist/src/server/agent/providers/claude/tool-call-mapper.js.map +1 -0
- package/dist/src/server/agent/providers/claude-agent.js +3747 -0
- package/dist/src/server/agent/providers/claude-agent.js.map +1 -0
- package/dist/src/server/agent/providers/codex/tool-call-detail-parser.js +104 -0
- package/dist/src/server/agent/providers/codex/tool-call-detail-parser.js.map +1 -0
- package/dist/src/server/agent/providers/codex/tool-call-mapper.js +720 -0
- package/dist/src/server/agent/providers/codex/tool-call-mapper.js.map +1 -0
- package/dist/src/server/agent/providers/codex-app-server-agent.js +2601 -0
- package/dist/src/server/agent/providers/codex-app-server-agent.js.map +1 -0
- package/dist/src/server/agent/providers/codex-rollout-timeline.js +487 -0
- package/dist/src/server/agent/providers/codex-rollout-timeline.js.map +1 -0
- package/dist/src/server/agent/providers/opencode/tool-call-detail-parser.js +39 -0
- package/dist/src/server/agent/providers/opencode/tool-call-detail-parser.js.map +1 -0
- package/dist/src/server/agent/providers/opencode/tool-call-mapper.js +151 -0
- package/dist/src/server/agent/providers/opencode/tool-call-mapper.js.map +1 -0
- package/dist/src/server/agent/providers/opencode-agent.js +905 -0
- package/dist/src/server/agent/providers/opencode-agent.js.map +1 -0
- package/dist/src/server/agent/providers/tool-call-detail-primitives.js +552 -0
- package/dist/src/server/agent/providers/tool-call-detail-primitives.js.map +1 -0
- package/dist/src/server/agent/providers/tool-call-mapper-utils.js +109 -0
- package/dist/src/server/agent/providers/tool-call-mapper-utils.js.map +1 -0
- package/dist/src/server/agent/recordings-debug.js +19 -0
- package/dist/src/server/agent/recordings-debug.js.map +1 -0
- package/dist/src/server/agent/stt-debug.js +33 -0
- package/dist/src/server/agent/stt-debug.js.map +1 -0
- package/dist/src/server/agent/stt-manager.js +233 -0
- package/dist/src/server/agent/stt-manager.js.map +1 -0
- package/dist/src/server/agent/timeline-append.js +27 -0
- package/dist/src/server/agent/timeline-append.js.map +1 -0
- package/dist/src/server/agent/timeline-projection.js +215 -0
- package/dist/src/server/agent/timeline-projection.js.map +1 -0
- package/dist/src/server/agent/tool-name-normalization.js +45 -0
- package/dist/src/server/agent/tool-name-normalization.js.map +1 -0
- package/dist/src/server/agent/tts-debug.js +24 -0
- package/dist/src/server/agent/tts-debug.js.map +1 -0
- package/dist/src/server/agent/tts-manager.js +249 -0
- package/dist/src/server/agent/tts-manager.js.map +1 -0
- package/dist/src/server/agent/wait-for-agent-tracker.js +53 -0
- package/dist/src/server/agent/wait-for-agent-tracker.js.map +1 -0
- package/dist/src/server/agent-attention-policy.js +40 -0
- package/dist/src/server/agent-attention-policy.js.map +1 -0
- package/dist/src/server/allowed-hosts.js +94 -0
- package/dist/src/server/allowed-hosts.js.map +1 -0
- package/dist/src/server/bootstrap.js +498 -0
- package/dist/src/server/bootstrap.js.map +1 -0
- package/dist/src/server/client-message-id.js +12 -0
- package/dist/src/server/client-message-id.js.map +1 -0
- package/dist/src/server/config.js +84 -0
- package/dist/src/server/config.js.map +1 -0
- package/dist/src/server/connection-offer.js +60 -0
- package/dist/src/server/connection-offer.js.map +1 -0
- package/dist/src/server/daemon-keypair.js +40 -0
- package/dist/src/server/daemon-keypair.js.map +1 -0
- package/dist/src/server/daemon-version.js +22 -0
- package/dist/src/server/daemon-version.js.map +1 -0
- package/dist/src/server/dictation/dictation-stream-manager.js +568 -0
- package/dist/src/server/dictation/dictation-stream-manager.js.map +1 -0
- package/dist/src/server/file-download/token-store.js +40 -0
- package/dist/src/server/file-download/token-store.js.map +1 -0
- package/dist/src/server/file-explorer/service.js +183 -0
- package/dist/src/server/file-explorer/service.js.map +1 -0
- package/dist/src/server/json-utils.js +45 -0
- package/dist/src/server/json-utils.js.map +1 -0
- package/dist/src/server/messages.js +29 -0
- package/dist/src/server/messages.js.map +1 -0
- package/dist/src/server/package-version.js +47 -0
- package/dist/src/server/package-version.js.map +1 -0
- package/dist/src/server/paseo-home.js +19 -0
- package/dist/src/server/paseo-home.js.map +1 -0
- package/dist/src/server/path-utils.js +20 -0
- package/dist/src/server/path-utils.js.map +1 -0
- package/dist/src/server/persisted-config.js +259 -0
- package/dist/src/server/persisted-config.js.map +1 -0
- package/dist/src/server/persistence-hooks.js +60 -0
- package/dist/src/server/persistence-hooks.js.map +1 -0
- package/dist/src/server/pid-lock.js +126 -0
- package/dist/src/server/pid-lock.js.map +1 -0
- package/dist/src/server/push/push-service.js +68 -0
- package/dist/src/server/push/push-service.js.map +1 -0
- package/dist/src/server/push/token-store.js +70 -0
- package/dist/src/server/push/token-store.js.map +1 -0
- package/dist/src/server/relay-transport.js +457 -0
- package/dist/src/server/relay-transport.js.map +1 -0
- package/dist/src/server/server-id.js +63 -0
- package/dist/src/server/server-id.js.map +1 -0
- package/dist/src/server/session.js +5947 -0
- package/dist/src/server/session.js.map +1 -0
- package/dist/src/server/speech/audio.js +101 -0
- package/dist/src/server/speech/audio.js.map +1 -0
- package/dist/src/server/speech/provider-resolver.js +7 -0
- package/dist/src/server/speech/provider-resolver.js.map +1 -0
- package/dist/src/server/speech/providers/local/config.js +83 -0
- package/dist/src/server/speech/providers/local/config.js.map +1 -0
- package/dist/src/server/speech/providers/local/models.js +17 -0
- package/dist/src/server/speech/providers/local/models.js.map +1 -0
- package/dist/src/server/speech/providers/local/pocket/pocket-tts-onnx.js +422 -0
- package/dist/src/server/speech/providers/local/pocket/pocket-tts-onnx.js.map +1 -0
- package/dist/src/server/speech/providers/local/runtime.js +253 -0
- package/dist/src/server/speech/providers/local/runtime.js.map +1 -0
- package/dist/src/server/speech/providers/local/sherpa/model-catalog.js +166 -0
- package/dist/src/server/speech/providers/local/sherpa/model-catalog.js.map +1 -0
- package/dist/src/server/speech/providers/local/sherpa/model-downloader.js +165 -0
- package/dist/src/server/speech/providers/local/sherpa/model-downloader.js.map +1 -0
- package/dist/src/server/speech/providers/local/sherpa/sherpa-offline-recognizer.js +68 -0
- package/dist/src/server/speech/providers/local/sherpa/sherpa-offline-recognizer.js.map +1 -0
- package/dist/src/server/speech/providers/local/sherpa/sherpa-online-recognizer.js +79 -0
- package/dist/src/server/speech/providers/local/sherpa/sherpa-online-recognizer.js.map +1 -0
- package/dist/src/server/speech/providers/local/sherpa/sherpa-onnx-loader.js +11 -0
- package/dist/src/server/speech/providers/local/sherpa/sherpa-onnx-loader.js.map +1 -0
- package/dist/src/server/speech/providers/local/sherpa/sherpa-onnx-node-loader.js +102 -0
- package/dist/src/server/speech/providers/local/sherpa/sherpa-onnx-node-loader.js.map +1 -0
- package/dist/src/server/speech/providers/local/sherpa/sherpa-parakeet-realtime-session.js +131 -0
- package/dist/src/server/speech/providers/local/sherpa/sherpa-parakeet-realtime-session.js.map +1 -0
- package/dist/src/server/speech/providers/local/sherpa/sherpa-parakeet-stt.js +132 -0
- package/dist/src/server/speech/providers/local/sherpa/sherpa-parakeet-stt.js.map +1 -0
- package/dist/src/server/speech/providers/local/sherpa/sherpa-realtime-session.js +112 -0
- package/dist/src/server/speech/providers/local/sherpa/sherpa-realtime-session.js.map +1 -0
- package/dist/src/server/speech/providers/local/sherpa/sherpa-stt.js +140 -0
- package/dist/src/server/speech/providers/local/sherpa/sherpa-stt.js.map +1 -0
- package/dist/src/server/speech/providers/local/sherpa/sherpa-tts.js +95 -0
- package/dist/src/server/speech/providers/local/sherpa/sherpa-tts.js.map +1 -0
- package/dist/src/server/speech/providers/openai/config.js +99 -0
- package/dist/src/server/speech/providers/openai/config.js.map +1 -0
- package/dist/src/server/speech/providers/openai/realtime-transcription-session.js +165 -0
- package/dist/src/server/speech/providers/openai/realtime-transcription-session.js.map +1 -0
- package/dist/src/server/speech/providers/openai/runtime.js +114 -0
- package/dist/src/server/speech/providers/openai/runtime.js.map +1 -0
- package/dist/src/server/speech/providers/openai/stt.js +208 -0
- package/dist/src/server/speech/providers/openai/stt.js.map +1 -0
- package/dist/src/server/speech/providers/openai/tts.js +46 -0
- package/dist/src/server/speech/providers/openai/tts.js.map +1 -0
- package/dist/src/server/speech/speech-config-resolver.js +85 -0
- package/dist/src/server/speech/speech-config-resolver.js.map +1 -0
- package/dist/src/server/speech/speech-provider.js +2 -0
- package/dist/src/server/speech/speech-provider.js.map +1 -0
- package/dist/src/server/speech/speech-runtime.js +497 -0
- package/dist/src/server/speech/speech-runtime.js.map +1 -0
- package/dist/src/server/speech/speech-types.js +8 -0
- package/dist/src/server/speech/speech-types.js.map +1 -0
- package/dist/src/server/utils/diff-highlighter.js +244 -0
- package/dist/src/server/utils/diff-highlighter.js.map +1 -0
- package/dist/src/server/utils/syntax-highlighter.js +145 -0
- package/dist/src/server/utils/syntax-highlighter.js.map +1 -0
- package/dist/src/server/voice-config.js +51 -0
- package/dist/src/server/voice-config.js.map +1 -0
- package/dist/src/server/voice-mcp-bridge-command.js +31 -0
- package/dist/src/server/voice-mcp-bridge-command.js.map +1 -0
- package/dist/src/server/voice-mcp-bridge.js +109 -0
- package/dist/src/server/voice-mcp-bridge.js.map +1 -0
- package/dist/src/server/voice-permission-policy.js +13 -0
- package/dist/src/server/voice-permission-policy.js.map +1 -0
- package/dist/src/server/voice-types.js +2 -0
- package/dist/src/server/voice-types.js.map +1 -0
- package/dist/src/server/websocket-server.js +967 -0
- package/dist/src/server/websocket-server.js.map +1 -0
- package/dist/src/server/worktree-bootstrap.js +497 -0
- package/dist/src/server/worktree-bootstrap.js.map +1 -0
- package/dist/src/shared/agent-attention-notification.js +130 -0
- package/dist/src/shared/agent-attention-notification.js.map +1 -0
- package/dist/src/shared/agent-lifecycle.js +8 -0
- package/dist/src/shared/agent-lifecycle.js.map +1 -0
- package/dist/src/shared/binary-mux.js +114 -0
- package/dist/src/shared/binary-mux.js.map +1 -0
- package/dist/src/shared/connection-offer.js +17 -0
- package/dist/src/shared/connection-offer.js.map +1 -0
- package/dist/src/shared/daemon-endpoints.js +113 -0
- package/dist/src/shared/daemon-endpoints.js.map +1 -0
- package/dist/src/shared/messages.js +2001 -0
- package/dist/src/shared/messages.js.map +1 -0
- package/dist/src/shared/path-utils.js +16 -0
- package/dist/src/shared/path-utils.js.map +1 -0
- package/dist/src/shared/tool-call-display.js +93 -0
- package/dist/src/shared/tool-call-display.js.map +1 -0
- package/dist/src/terminal/terminal-manager.js +136 -0
- package/dist/src/terminal/terminal-manager.js.map +1 -0
- package/dist/src/terminal/terminal.js +410 -0
- package/dist/src/terminal/terminal.js.map +1 -0
- package/dist/src/utils/checkout-git.js +1397 -0
- package/dist/src/utils/checkout-git.js.map +1 -0
- package/dist/src/utils/directory-suggestions.js +655 -0
- package/dist/src/utils/directory-suggestions.js.map +1 -0
- package/dist/src/utils/path.js +15 -0
- package/dist/src/utils/path.js.map +1 -0
- package/dist/src/utils/project-icon.js +391 -0
- package/dist/src/utils/project-icon.js.map +1 -0
- package/dist/src/utils/worktree-metadata.js +116 -0
- package/dist/src/utils/worktree-metadata.js.map +1 -0
- package/dist/src/utils/worktree.js +741 -0
- package/dist/src/utils/worktree.js.map +1 -0
- package/package.json +14 -6
|
@@ -0,0 +1,905 @@
|
|
|
1
|
+
import { execSync, spawn } from "node:child_process";
|
|
2
|
+
import { createOpencodeClient } from "@opencode-ai/sdk/v2/client";
|
|
3
|
+
import net from "node:net";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { applyProviderEnv, isProviderCommandAvailable, resolveProviderCommandPrefix, } from "../provider-launch-config.js";
|
|
6
|
+
import { mapOpencodeToolCall } from "./opencode/tool-call-mapper.js";
|
|
7
|
+
const OPENCODE_CAPABILITIES = {
|
|
8
|
+
supportsStreaming: true,
|
|
9
|
+
supportsSessionPersistence: true,
|
|
10
|
+
supportsDynamicModes: false,
|
|
11
|
+
supportsMcpServers: true,
|
|
12
|
+
supportsReasoningStream: true,
|
|
13
|
+
supportsToolInvocations: true,
|
|
14
|
+
};
|
|
15
|
+
const DEFAULT_MODES = [
|
|
16
|
+
{
|
|
17
|
+
id: "default",
|
|
18
|
+
label: "Default",
|
|
19
|
+
description: "Standard permission rules",
|
|
20
|
+
},
|
|
21
|
+
];
|
|
22
|
+
const MCP_ALREADY_PRESENT_ERROR_TOKENS = ["already", "exists", "connected"];
|
|
23
|
+
const OpencodeToolStateSchema = z
|
|
24
|
+
.object({
|
|
25
|
+
status: z.string().optional(),
|
|
26
|
+
input: z.unknown().optional(),
|
|
27
|
+
output: z.unknown().optional(),
|
|
28
|
+
error: z.unknown().optional(),
|
|
29
|
+
})
|
|
30
|
+
.passthrough();
|
|
31
|
+
const OpencodeToolPartBaseSchema = z
|
|
32
|
+
.object({
|
|
33
|
+
tool: z.string().trim().min(1),
|
|
34
|
+
state: OpencodeToolStateSchema.optional(),
|
|
35
|
+
})
|
|
36
|
+
.passthrough();
|
|
37
|
+
const OpencodeToolPartWithCallIdSchema = OpencodeToolPartBaseSchema.extend({
|
|
38
|
+
callID: z.string().trim().min(1),
|
|
39
|
+
id: z.string().optional(),
|
|
40
|
+
}).transform((part) => ({
|
|
41
|
+
toolName: part.tool,
|
|
42
|
+
callId: part.callID,
|
|
43
|
+
status: part.state?.status,
|
|
44
|
+
input: part.state?.input,
|
|
45
|
+
output: part.state?.output,
|
|
46
|
+
error: part.state?.error,
|
|
47
|
+
}));
|
|
48
|
+
const OpencodeToolPartWithIdSchema = OpencodeToolPartBaseSchema.extend({
|
|
49
|
+
id: z.string().trim().min(1),
|
|
50
|
+
callID: z.string().optional(),
|
|
51
|
+
}).transform((part) => ({
|
|
52
|
+
toolName: part.tool,
|
|
53
|
+
callId: part.id,
|
|
54
|
+
status: part.state?.status,
|
|
55
|
+
input: part.state?.input,
|
|
56
|
+
output: part.state?.output,
|
|
57
|
+
error: part.state?.error,
|
|
58
|
+
}));
|
|
59
|
+
const OpencodeToolPartWithoutIdSchema = OpencodeToolPartBaseSchema.extend({
|
|
60
|
+
id: z.string().optional(),
|
|
61
|
+
callID: z.string().optional(),
|
|
62
|
+
}).transform((part) => ({
|
|
63
|
+
toolName: part.tool,
|
|
64
|
+
callId: undefined,
|
|
65
|
+
status: part.state?.status,
|
|
66
|
+
input: part.state?.input,
|
|
67
|
+
output: part.state?.output,
|
|
68
|
+
error: part.state?.error,
|
|
69
|
+
}));
|
|
70
|
+
const OpencodeToolPartSchema = z.union([
|
|
71
|
+
OpencodeToolPartWithCallIdSchema,
|
|
72
|
+
OpencodeToolPartWithIdSchema,
|
|
73
|
+
OpencodeToolPartWithoutIdSchema,
|
|
74
|
+
]);
|
|
75
|
+
const OpencodeToolPartTimelineEnvelopeSchema = OpencodeToolPartSchema.transform((part) => ({
|
|
76
|
+
toolName: part.toolName,
|
|
77
|
+
callId: part.callId,
|
|
78
|
+
status: part.status,
|
|
79
|
+
input: part.input,
|
|
80
|
+
output: part.output,
|
|
81
|
+
error: part.error,
|
|
82
|
+
}));
|
|
83
|
+
const OpencodeToolPartToTimelineItemSchema = OpencodeToolPartTimelineEnvelopeSchema.transform((part) => mapOpencodeToolCall({
|
|
84
|
+
toolName: part.toolName,
|
|
85
|
+
callId: part.callId,
|
|
86
|
+
status: part.status,
|
|
87
|
+
input: part.input,
|
|
88
|
+
output: part.output,
|
|
89
|
+
error: part.error,
|
|
90
|
+
}));
|
|
91
|
+
function resolveOpenCodeBinary() {
|
|
92
|
+
try {
|
|
93
|
+
const opencodePath = execSync("which opencode", { encoding: "utf8" }).trim();
|
|
94
|
+
if (opencodePath) {
|
|
95
|
+
return opencodePath;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
// fall through
|
|
100
|
+
}
|
|
101
|
+
throw new Error("OpenCode CLI not found. Please install opencode globally so Paseo can launch the provider.");
|
|
102
|
+
}
|
|
103
|
+
function toOpenCodeMcpConfig(config) {
|
|
104
|
+
if (config.type === "stdio") {
|
|
105
|
+
return {
|
|
106
|
+
type: "local",
|
|
107
|
+
command: [config.command, ...(config.args ?? [])],
|
|
108
|
+
...(config.env ? { environment: config.env } : {}),
|
|
109
|
+
enabled: true,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
return {
|
|
113
|
+
type: "remote",
|
|
114
|
+
url: config.url,
|
|
115
|
+
...(config.headers ? { headers: config.headers } : {}),
|
|
116
|
+
enabled: true,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
function stringifyUnknownError(error) {
|
|
120
|
+
if (typeof error === "string") {
|
|
121
|
+
return error;
|
|
122
|
+
}
|
|
123
|
+
try {
|
|
124
|
+
return JSON.stringify(error);
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
return String(error);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
function isAlreadyPresentMcpError(error) {
|
|
131
|
+
const normalized = stringifyUnknownError(error).toLowerCase();
|
|
132
|
+
return MCP_ALREADY_PRESENT_ERROR_TOKENS.some((token) => normalized.includes(token));
|
|
133
|
+
}
|
|
134
|
+
async function findAvailablePort() {
|
|
135
|
+
return new Promise((resolve, reject) => {
|
|
136
|
+
const server = net.createServer();
|
|
137
|
+
server.listen(0, () => {
|
|
138
|
+
const address = server.address();
|
|
139
|
+
if (address && typeof address === "object") {
|
|
140
|
+
const port = address.port;
|
|
141
|
+
server.close(() => resolve(port));
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
server.close(() => reject(new Error("Failed to get port")));
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
server.on("error", reject);
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
function resolvePartDedupeKey(part, partType) {
|
|
151
|
+
const partId = part.id;
|
|
152
|
+
if (typeof partId === "string" && partId.trim().length > 0) {
|
|
153
|
+
return `${partType}:${partId}`;
|
|
154
|
+
}
|
|
155
|
+
const messageId = part.messageID;
|
|
156
|
+
if (typeof messageId === "string" && messageId.trim().length > 0) {
|
|
157
|
+
return `${partType}:message:${messageId}`;
|
|
158
|
+
}
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
export class OpenCodeServerManager {
|
|
162
|
+
constructor(logger, runtimeSettings) {
|
|
163
|
+
this.server = null;
|
|
164
|
+
this.port = null;
|
|
165
|
+
this.startPromise = null;
|
|
166
|
+
this.logger = logger;
|
|
167
|
+
this.runtimeSettings = runtimeSettings;
|
|
168
|
+
this.runtimeSettingsKey = JSON.stringify(runtimeSettings ?? {});
|
|
169
|
+
}
|
|
170
|
+
static getInstance(logger, runtimeSettings) {
|
|
171
|
+
const nextSettingsKey = JSON.stringify(runtimeSettings ?? {});
|
|
172
|
+
if (!OpenCodeServerManager.instance) {
|
|
173
|
+
OpenCodeServerManager.instance = new OpenCodeServerManager(logger, runtimeSettings);
|
|
174
|
+
OpenCodeServerManager.registerExitHandler();
|
|
175
|
+
}
|
|
176
|
+
else if (OpenCodeServerManager.instance.runtimeSettingsKey !== nextSettingsKey) {
|
|
177
|
+
logger.warn({
|
|
178
|
+
existingRuntimeSettings: OpenCodeServerManager.instance.runtimeSettingsKey,
|
|
179
|
+
requestedRuntimeSettings: nextSettingsKey,
|
|
180
|
+
}, "OpenCode server manager already initialized with different runtime settings");
|
|
181
|
+
}
|
|
182
|
+
return OpenCodeServerManager.instance;
|
|
183
|
+
}
|
|
184
|
+
static registerExitHandler() {
|
|
185
|
+
if (OpenCodeServerManager.exitHandlerRegistered) {
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
OpenCodeServerManager.exitHandlerRegistered = true;
|
|
189
|
+
const cleanup = () => {
|
|
190
|
+
const instance = OpenCodeServerManager.instance;
|
|
191
|
+
if (instance?.server && !instance.server.killed) {
|
|
192
|
+
instance.server.kill("SIGTERM");
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
process.on("exit", cleanup);
|
|
196
|
+
process.on("SIGTERM", cleanup);
|
|
197
|
+
process.on("SIGINT", cleanup);
|
|
198
|
+
}
|
|
199
|
+
async ensureRunning() {
|
|
200
|
+
if (this.startPromise) {
|
|
201
|
+
return this.startPromise;
|
|
202
|
+
}
|
|
203
|
+
if (this.server && this.port && !this.server.killed) {
|
|
204
|
+
return { port: this.port, url: `http://127.0.0.1:${this.port}` };
|
|
205
|
+
}
|
|
206
|
+
this.startPromise = this.startServer();
|
|
207
|
+
try {
|
|
208
|
+
const result = await this.startPromise;
|
|
209
|
+
return result;
|
|
210
|
+
}
|
|
211
|
+
finally {
|
|
212
|
+
this.startPromise = null;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
async startServer() {
|
|
216
|
+
this.port = await findAvailablePort();
|
|
217
|
+
const url = `http://127.0.0.1:${this.port}`;
|
|
218
|
+
const launchPrefix = resolveProviderCommandPrefix(this.runtimeSettings?.command, resolveOpenCodeBinary);
|
|
219
|
+
return new Promise((resolve, reject) => {
|
|
220
|
+
this.server = spawn(launchPrefix.command, [...launchPrefix.args, "serve", "--port", String(this.port)], {
|
|
221
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
222
|
+
env: applyProviderEnv(process.env, this.runtimeSettings),
|
|
223
|
+
});
|
|
224
|
+
let started = false;
|
|
225
|
+
const timeout = setTimeout(() => {
|
|
226
|
+
if (!started) {
|
|
227
|
+
reject(new Error("OpenCode server startup timeout"));
|
|
228
|
+
}
|
|
229
|
+
}, 30000);
|
|
230
|
+
this.server.stdout?.on("data", (data) => {
|
|
231
|
+
const output = data.toString();
|
|
232
|
+
if (output.includes("listening on") && !started) {
|
|
233
|
+
started = true;
|
|
234
|
+
clearTimeout(timeout);
|
|
235
|
+
resolve({ port: this.port, url });
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
this.server.stderr?.on("data", (data) => {
|
|
239
|
+
this.logger.error({ stderr: data.toString().trim() }, "OpenCode server stderr");
|
|
240
|
+
});
|
|
241
|
+
this.server.on("error", (error) => {
|
|
242
|
+
clearTimeout(timeout);
|
|
243
|
+
reject(error);
|
|
244
|
+
});
|
|
245
|
+
this.server.on("exit", (code) => {
|
|
246
|
+
if (!started) {
|
|
247
|
+
clearTimeout(timeout);
|
|
248
|
+
reject(new Error(`OpenCode server exited with code ${code}`));
|
|
249
|
+
}
|
|
250
|
+
this.server = null;
|
|
251
|
+
this.port = null;
|
|
252
|
+
});
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
async shutdown() {
|
|
256
|
+
if (this.server && !this.server.killed) {
|
|
257
|
+
this.server.kill("SIGTERM");
|
|
258
|
+
await new Promise((resolve) => {
|
|
259
|
+
const timeout = setTimeout(() => {
|
|
260
|
+
this.server?.kill("SIGKILL");
|
|
261
|
+
resolve();
|
|
262
|
+
}, 5000);
|
|
263
|
+
this.server?.on("exit", () => {
|
|
264
|
+
clearTimeout(timeout);
|
|
265
|
+
resolve();
|
|
266
|
+
});
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
this.server = null;
|
|
270
|
+
this.port = null;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
OpenCodeServerManager.instance = null;
|
|
274
|
+
OpenCodeServerManager.exitHandlerRegistered = false;
|
|
275
|
+
export class OpenCodeAgentClient {
|
|
276
|
+
constructor(logger, runtimeSettings) {
|
|
277
|
+
this.provider = "opencode";
|
|
278
|
+
this.capabilities = OPENCODE_CAPABILITIES;
|
|
279
|
+
this.logger = logger.child({ module: "agent", provider: "opencode" });
|
|
280
|
+
this.runtimeSettings = runtimeSettings;
|
|
281
|
+
this.serverManager = OpenCodeServerManager.getInstance(this.logger, runtimeSettings);
|
|
282
|
+
}
|
|
283
|
+
async createSession(config) {
|
|
284
|
+
const openCodeConfig = this.assertConfig(config);
|
|
285
|
+
const { url } = await this.serverManager.ensureRunning();
|
|
286
|
+
const client = createOpencodeClient({
|
|
287
|
+
baseUrl: url,
|
|
288
|
+
directory: openCodeConfig.cwd,
|
|
289
|
+
});
|
|
290
|
+
// Set a timeout for session creation to fail fast
|
|
291
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
292
|
+
setTimeout(() => reject(new Error("OpenCode session.create timed out after 10s")), 10000);
|
|
293
|
+
});
|
|
294
|
+
const response = await Promise.race([
|
|
295
|
+
client.session.create({ directory: openCodeConfig.cwd }),
|
|
296
|
+
timeoutPromise,
|
|
297
|
+
]);
|
|
298
|
+
if (response.error) {
|
|
299
|
+
throw new Error(`Failed to create OpenCode session: ${JSON.stringify(response.error)}`);
|
|
300
|
+
}
|
|
301
|
+
const session = response.data;
|
|
302
|
+
if (!session) {
|
|
303
|
+
throw new Error("OpenCode session creation returned no data");
|
|
304
|
+
}
|
|
305
|
+
return new OpenCodeAgentSession(openCodeConfig, client, session.id);
|
|
306
|
+
}
|
|
307
|
+
async resumeSession(handle, overrides) {
|
|
308
|
+
const cwd = overrides?.cwd ?? handle.metadata?.cwd;
|
|
309
|
+
if (!cwd) {
|
|
310
|
+
throw new Error("OpenCode resume requires the original working directory");
|
|
311
|
+
}
|
|
312
|
+
const config = {
|
|
313
|
+
provider: "opencode",
|
|
314
|
+
cwd,
|
|
315
|
+
...overrides,
|
|
316
|
+
};
|
|
317
|
+
const openCodeConfig = this.assertConfig(config);
|
|
318
|
+
const { url } = await this.serverManager.ensureRunning();
|
|
319
|
+
const client = createOpencodeClient({
|
|
320
|
+
baseUrl: url,
|
|
321
|
+
directory: openCodeConfig.cwd,
|
|
322
|
+
});
|
|
323
|
+
return new OpenCodeAgentSession(openCodeConfig, client, handle.sessionId);
|
|
324
|
+
}
|
|
325
|
+
async listModels(options) {
|
|
326
|
+
const { url } = await this.serverManager.ensureRunning();
|
|
327
|
+
const client = createOpencodeClient({
|
|
328
|
+
baseUrl: url,
|
|
329
|
+
directory: options?.cwd ?? process.cwd(),
|
|
330
|
+
});
|
|
331
|
+
// Set a timeout for the API call to fail fast if OpenCode isn't responding
|
|
332
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
333
|
+
setTimeout(() => reject(new Error("OpenCode provider.list timed out after 10s - server may not be authenticated or connected to any providers")), 10000);
|
|
334
|
+
});
|
|
335
|
+
const response = await Promise.race([
|
|
336
|
+
client.provider.list({ directory: options?.cwd ?? process.cwd() }),
|
|
337
|
+
timeoutPromise,
|
|
338
|
+
]);
|
|
339
|
+
if (response.error) {
|
|
340
|
+
throw new Error(`Failed to fetch OpenCode providers: ${JSON.stringify(response.error)}`);
|
|
341
|
+
}
|
|
342
|
+
const providers = response.data;
|
|
343
|
+
if (!providers) {
|
|
344
|
+
return [];
|
|
345
|
+
}
|
|
346
|
+
// Only include models from connected providers (ones that are actually available)
|
|
347
|
+
const connectedProviderIds = new Set(providers.connected);
|
|
348
|
+
// Fail fast if no providers are connected
|
|
349
|
+
if (connectedProviderIds.size === 0) {
|
|
350
|
+
throw new Error("OpenCode has no connected providers. Please authenticate with at least one provider (e.g., openai, anthropic) or set appropriate environment variables (e.g., OPENAI_API_KEY).");
|
|
351
|
+
}
|
|
352
|
+
const models = [];
|
|
353
|
+
for (const provider of providers.all) {
|
|
354
|
+
// Skip providers that aren't connected/configured
|
|
355
|
+
if (!connectedProviderIds.has(provider.id)) {
|
|
356
|
+
continue;
|
|
357
|
+
}
|
|
358
|
+
for (const [modelId, model] of Object.entries(provider.models)) {
|
|
359
|
+
const rawVariants = model.variants ? Object.keys(model.variants) : [];
|
|
360
|
+
const thinkingOptions = [
|
|
361
|
+
{ id: "default", label: "Model default", isDefault: true },
|
|
362
|
+
...rawVariants.map((id) => ({ id, label: id })),
|
|
363
|
+
];
|
|
364
|
+
models.push({
|
|
365
|
+
provider: "opencode",
|
|
366
|
+
id: `${provider.id}/${modelId}`,
|
|
367
|
+
label: model.name,
|
|
368
|
+
description: `${provider.name} - ${model.family ?? ""}`.trim(),
|
|
369
|
+
thinkingOptions: thinkingOptions.length > 1 ? thinkingOptions : undefined,
|
|
370
|
+
defaultThinkingOptionId: "default",
|
|
371
|
+
metadata: {
|
|
372
|
+
providerId: provider.id,
|
|
373
|
+
providerName: provider.name,
|
|
374
|
+
modelId,
|
|
375
|
+
family: model.family,
|
|
376
|
+
releaseDate: model.release_date,
|
|
377
|
+
supportsAttachments: model.attachment,
|
|
378
|
+
supportsReasoning: model.reasoning,
|
|
379
|
+
supportsToolCall: model.tool_call,
|
|
380
|
+
cost: model.cost,
|
|
381
|
+
},
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
return models;
|
|
386
|
+
}
|
|
387
|
+
async listPersistedAgents(_options) {
|
|
388
|
+
// TODO: Implement by listing sessions from OpenCode
|
|
389
|
+
return [];
|
|
390
|
+
}
|
|
391
|
+
async isAvailable() {
|
|
392
|
+
return isProviderCommandAvailable(this.runtimeSettings?.command, resolveOpenCodeBinary);
|
|
393
|
+
}
|
|
394
|
+
assertConfig(config) {
|
|
395
|
+
if (config.provider !== "opencode") {
|
|
396
|
+
throw new Error(`OpenCodeAgentClient received config for provider '${config.provider}'`);
|
|
397
|
+
}
|
|
398
|
+
return { ...config, provider: "opencode" };
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
export function translateOpenCodeEvent(event, state) {
|
|
402
|
+
const events = [];
|
|
403
|
+
if (!event || typeof event !== "object") {
|
|
404
|
+
return events;
|
|
405
|
+
}
|
|
406
|
+
const e = event;
|
|
407
|
+
const type = e.type;
|
|
408
|
+
const props = e.properties ?? {};
|
|
409
|
+
switch (type) {
|
|
410
|
+
case "session.created":
|
|
411
|
+
case "session.updated": {
|
|
412
|
+
const sessionId = props.id;
|
|
413
|
+
if (sessionId === state.sessionId) {
|
|
414
|
+
events.push({
|
|
415
|
+
type: "thread_started",
|
|
416
|
+
sessionId: state.sessionId,
|
|
417
|
+
provider: "opencode",
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
break;
|
|
421
|
+
}
|
|
422
|
+
case "message.updated": {
|
|
423
|
+
const info = props.info;
|
|
424
|
+
if (!info) {
|
|
425
|
+
break;
|
|
426
|
+
}
|
|
427
|
+
const messageId = info.id;
|
|
428
|
+
const messageSessionId = info.sessionID;
|
|
429
|
+
const role = info.role;
|
|
430
|
+
if (messageId && messageSessionId === state.sessionId && role) {
|
|
431
|
+
state.messageRoles.set(messageId, role);
|
|
432
|
+
}
|
|
433
|
+
break;
|
|
434
|
+
}
|
|
435
|
+
case "message.part.updated": {
|
|
436
|
+
const part = props.part;
|
|
437
|
+
const delta = props.delta;
|
|
438
|
+
if (!part) {
|
|
439
|
+
break;
|
|
440
|
+
}
|
|
441
|
+
const partSessionId = part.sessionID;
|
|
442
|
+
if (partSessionId !== state.sessionId) {
|
|
443
|
+
break;
|
|
444
|
+
}
|
|
445
|
+
const messageId = part.messageID;
|
|
446
|
+
const messageRole = messageId ? state.messageRoles.get(messageId) : undefined;
|
|
447
|
+
const partType = part.type;
|
|
448
|
+
const partTime = part.time;
|
|
449
|
+
if (partType === "text") {
|
|
450
|
+
const partKey = resolvePartDedupeKey(part, "text");
|
|
451
|
+
if (messageRole === "user") {
|
|
452
|
+
break;
|
|
453
|
+
}
|
|
454
|
+
if (!messageRole && !delta) {
|
|
455
|
+
break;
|
|
456
|
+
}
|
|
457
|
+
if (delta) {
|
|
458
|
+
if (partKey) {
|
|
459
|
+
state.streamedPartKeys.add(partKey);
|
|
460
|
+
}
|
|
461
|
+
events.push({
|
|
462
|
+
type: "timeline",
|
|
463
|
+
provider: "opencode",
|
|
464
|
+
item: { type: "assistant_message", text: delta },
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
else if (partTime?.end) {
|
|
468
|
+
if (partKey && state.streamedPartKeys.delete(partKey)) {
|
|
469
|
+
break;
|
|
470
|
+
}
|
|
471
|
+
const text = part.text;
|
|
472
|
+
if (text) {
|
|
473
|
+
events.push({
|
|
474
|
+
type: "timeline",
|
|
475
|
+
provider: "opencode",
|
|
476
|
+
item: { type: "assistant_message", text },
|
|
477
|
+
});
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
else if (partType === "reasoning") {
|
|
482
|
+
const partKey = resolvePartDedupeKey(part, "reasoning");
|
|
483
|
+
if (delta) {
|
|
484
|
+
if (partKey) {
|
|
485
|
+
state.streamedPartKeys.add(partKey);
|
|
486
|
+
}
|
|
487
|
+
events.push({
|
|
488
|
+
type: "timeline",
|
|
489
|
+
provider: "opencode",
|
|
490
|
+
item: { type: "reasoning", text: delta },
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
else if (partTime?.end) {
|
|
494
|
+
if (partKey && state.streamedPartKeys.delete(partKey)) {
|
|
495
|
+
break;
|
|
496
|
+
}
|
|
497
|
+
const text = part.text;
|
|
498
|
+
if (text) {
|
|
499
|
+
events.push({
|
|
500
|
+
type: "timeline",
|
|
501
|
+
provider: "opencode",
|
|
502
|
+
item: { type: "reasoning", text },
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
else if (partType === "tool") {
|
|
508
|
+
const parsedToolPart = OpencodeToolPartToTimelineItemSchema.safeParse(part);
|
|
509
|
+
if (parsedToolPart.success && parsedToolPart.data) {
|
|
510
|
+
events.push({
|
|
511
|
+
type: "timeline",
|
|
512
|
+
provider: "opencode",
|
|
513
|
+
item: parsedToolPart.data,
|
|
514
|
+
});
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
else if (partType === "step-finish") {
|
|
518
|
+
const tokens = part.tokens;
|
|
519
|
+
const cost = part.cost;
|
|
520
|
+
if (tokens) {
|
|
521
|
+
state.accumulatedUsage.inputTokens = (state.accumulatedUsage.inputTokens ?? 0) + (tokens.input ?? 0);
|
|
522
|
+
state.accumulatedUsage.outputTokens = (state.accumulatedUsage.outputTokens ?? 0) + (tokens.output ?? 0);
|
|
523
|
+
}
|
|
524
|
+
if (cost !== undefined) {
|
|
525
|
+
state.accumulatedUsage.totalCostUsd = (state.accumulatedUsage.totalCostUsd ?? 0) + cost;
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
break;
|
|
529
|
+
}
|
|
530
|
+
case "permission.asked": {
|
|
531
|
+
const sessionId = props.sessionID;
|
|
532
|
+
if (sessionId !== state.sessionId) {
|
|
533
|
+
break;
|
|
534
|
+
}
|
|
535
|
+
const requestId = props.id;
|
|
536
|
+
const permission = props.permission;
|
|
537
|
+
const metadata = props.metadata;
|
|
538
|
+
const patterns = props.patterns;
|
|
539
|
+
const permRequest = {
|
|
540
|
+
id: requestId,
|
|
541
|
+
provider: "opencode",
|
|
542
|
+
name: permission,
|
|
543
|
+
kind: "tool",
|
|
544
|
+
title: permission,
|
|
545
|
+
description: patterns?.join(", "),
|
|
546
|
+
input: metadata,
|
|
547
|
+
};
|
|
548
|
+
events.push({
|
|
549
|
+
type: "permission_requested",
|
|
550
|
+
provider: "opencode",
|
|
551
|
+
request: permRequest,
|
|
552
|
+
});
|
|
553
|
+
break;
|
|
554
|
+
}
|
|
555
|
+
case "session.idle": {
|
|
556
|
+
const sessionId = props.sessionID;
|
|
557
|
+
if (sessionId === state.sessionId) {
|
|
558
|
+
state.streamedPartKeys.clear();
|
|
559
|
+
events.push({
|
|
560
|
+
type: "turn_completed",
|
|
561
|
+
provider: "opencode",
|
|
562
|
+
usage: undefined,
|
|
563
|
+
});
|
|
564
|
+
}
|
|
565
|
+
break;
|
|
566
|
+
}
|
|
567
|
+
case "session.error": {
|
|
568
|
+
const sessionId = props.sessionID;
|
|
569
|
+
if (sessionId === state.sessionId) {
|
|
570
|
+
state.streamedPartKeys.clear();
|
|
571
|
+
const error = props.error;
|
|
572
|
+
events.push({
|
|
573
|
+
type: "turn_failed",
|
|
574
|
+
provider: "opencode",
|
|
575
|
+
error: error ?? "Unknown error",
|
|
576
|
+
});
|
|
577
|
+
}
|
|
578
|
+
break;
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
return events;
|
|
582
|
+
}
|
|
583
|
+
class OpenCodeAgentSession {
|
|
584
|
+
constructor(config, client, sessionId) {
|
|
585
|
+
this.provider = "opencode";
|
|
586
|
+
this.capabilities = OPENCODE_CAPABILITIES;
|
|
587
|
+
this.currentMode = "default";
|
|
588
|
+
this.pendingPermissions = new Map();
|
|
589
|
+
this.abortController = null;
|
|
590
|
+
this.accumulatedUsage = {};
|
|
591
|
+
this.mcpConfigured = false;
|
|
592
|
+
this.mcpSetupPromise = null;
|
|
593
|
+
/** Tracks the role of each message by ID to distinguish user from assistant messages */
|
|
594
|
+
this.messageRoles = new Map();
|
|
595
|
+
/** Tracks streamed textual part IDs to suppress final full-text echoes from OpenCode. */
|
|
596
|
+
this.streamedPartKeys = new Set();
|
|
597
|
+
this.config = config;
|
|
598
|
+
this.client = client;
|
|
599
|
+
this.sessionId = sessionId;
|
|
600
|
+
}
|
|
601
|
+
get id() {
|
|
602
|
+
return this.sessionId;
|
|
603
|
+
}
|
|
604
|
+
async getRuntimeInfo() {
|
|
605
|
+
return {
|
|
606
|
+
provider: "opencode",
|
|
607
|
+
sessionId: this.sessionId,
|
|
608
|
+
model: this.config.model ?? null,
|
|
609
|
+
modeId: this.currentMode,
|
|
610
|
+
};
|
|
611
|
+
}
|
|
612
|
+
async setModel(modelId) {
|
|
613
|
+
const normalizedModelId = typeof modelId === "string" && modelId.trim().length > 0 ? modelId : null;
|
|
614
|
+
this.config.model = normalizedModelId ?? undefined;
|
|
615
|
+
}
|
|
616
|
+
async setThinkingOption(thinkingOptionId) {
|
|
617
|
+
const normalizedThinkingOptionId = typeof thinkingOptionId === "string" && thinkingOptionId.trim().length > 0
|
|
618
|
+
? thinkingOptionId
|
|
619
|
+
: null;
|
|
620
|
+
this.config.thinkingOptionId = normalizedThinkingOptionId ?? undefined;
|
|
621
|
+
}
|
|
622
|
+
async run(prompt, _options) {
|
|
623
|
+
const events = this.stream(prompt);
|
|
624
|
+
const timeline = [];
|
|
625
|
+
let finalText = "";
|
|
626
|
+
let usage;
|
|
627
|
+
for await (const event of events) {
|
|
628
|
+
if (event.type === "timeline") {
|
|
629
|
+
timeline.push(event.item);
|
|
630
|
+
if (event.item.type === "assistant_message") {
|
|
631
|
+
finalText = event.item.text;
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
else if (event.type === "turn_completed") {
|
|
635
|
+
usage = event.usage;
|
|
636
|
+
}
|
|
637
|
+
else if (event.type === "turn_failed") {
|
|
638
|
+
throw new Error(event.error);
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
return {
|
|
642
|
+
sessionId: this.sessionId,
|
|
643
|
+
finalText,
|
|
644
|
+
usage,
|
|
645
|
+
timeline,
|
|
646
|
+
};
|
|
647
|
+
}
|
|
648
|
+
async *stream(prompt, _options) {
|
|
649
|
+
this.abortController = new AbortController();
|
|
650
|
+
await this.ensureMcpServersConfigured();
|
|
651
|
+
const parts = this.buildPromptParts(prompt);
|
|
652
|
+
const model = this.parseModel(this.config.model);
|
|
653
|
+
const thinkingOptionId = this.config.thinkingOptionId;
|
|
654
|
+
const effectiveVariant = thinkingOptionId && thinkingOptionId !== "default" ? thinkingOptionId : undefined;
|
|
655
|
+
// Send prompt asynchronously
|
|
656
|
+
const promptResponse = await this.client.session.promptAsync({
|
|
657
|
+
sessionID: this.sessionId,
|
|
658
|
+
directory: this.config.cwd,
|
|
659
|
+
parts,
|
|
660
|
+
...(this.config.systemPrompt ? { system: this.config.systemPrompt } : {}),
|
|
661
|
+
...(model ? { model } : {}),
|
|
662
|
+
...(effectiveVariant ? { variant: effectiveVariant } : {}),
|
|
663
|
+
});
|
|
664
|
+
if (promptResponse.error) {
|
|
665
|
+
yield {
|
|
666
|
+
type: "turn_failed",
|
|
667
|
+
provider: "opencode",
|
|
668
|
+
error: JSON.stringify(promptResponse.error),
|
|
669
|
+
};
|
|
670
|
+
return;
|
|
671
|
+
}
|
|
672
|
+
// Subscribe to events
|
|
673
|
+
const eventsResult = await this.client.event.subscribe({
|
|
674
|
+
directory: this.config.cwd,
|
|
675
|
+
});
|
|
676
|
+
try {
|
|
677
|
+
for await (const event of eventsResult.stream) {
|
|
678
|
+
if (this.abortController.signal.aborted) {
|
|
679
|
+
break;
|
|
680
|
+
}
|
|
681
|
+
const translated = this.translateEvent(event);
|
|
682
|
+
for (const e of translated) {
|
|
683
|
+
yield e;
|
|
684
|
+
if (e.type === "turn_completed" || e.type === "turn_failed") {
|
|
685
|
+
return;
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
catch (error) {
|
|
691
|
+
if (!this.abortController.signal.aborted) {
|
|
692
|
+
yield {
|
|
693
|
+
type: "turn_failed",
|
|
694
|
+
provider: "opencode",
|
|
695
|
+
error: error instanceof Error ? error.message : "Stream error",
|
|
696
|
+
};
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
async interrupt() {
|
|
701
|
+
this.abortController?.abort();
|
|
702
|
+
await this.client.session.abort({
|
|
703
|
+
sessionID: this.sessionId,
|
|
704
|
+
directory: this.config.cwd,
|
|
705
|
+
});
|
|
706
|
+
}
|
|
707
|
+
async *streamHistory() {
|
|
708
|
+
const response = await this.client.session.messages({
|
|
709
|
+
sessionID: this.sessionId,
|
|
710
|
+
directory: this.config.cwd,
|
|
711
|
+
});
|
|
712
|
+
if (response.error || !response.data) {
|
|
713
|
+
return;
|
|
714
|
+
}
|
|
715
|
+
const messages = response.data;
|
|
716
|
+
for (const message of messages) {
|
|
717
|
+
const { info, parts } = message;
|
|
718
|
+
const role = info.role;
|
|
719
|
+
if (role === "user") {
|
|
720
|
+
// Extract user message text from parts
|
|
721
|
+
const textParts = parts.filter((p) => p.type === "text");
|
|
722
|
+
const text = textParts
|
|
723
|
+
.map((p) => p.text ?? "")
|
|
724
|
+
.join("");
|
|
725
|
+
if (text) {
|
|
726
|
+
yield {
|
|
727
|
+
type: "timeline",
|
|
728
|
+
provider: "opencode",
|
|
729
|
+
item: { type: "user_message", text },
|
|
730
|
+
};
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
else if (role === "assistant") {
|
|
734
|
+
// Process each part
|
|
735
|
+
for (const part of parts) {
|
|
736
|
+
const partType = part.type;
|
|
737
|
+
if (partType === "text") {
|
|
738
|
+
const text = part.text;
|
|
739
|
+
if (text) {
|
|
740
|
+
yield {
|
|
741
|
+
type: "timeline",
|
|
742
|
+
provider: "opencode",
|
|
743
|
+
item: { type: "assistant_message", text },
|
|
744
|
+
};
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
else if (partType === "reasoning") {
|
|
748
|
+
const text = part.text;
|
|
749
|
+
if (text) {
|
|
750
|
+
yield {
|
|
751
|
+
type: "timeline",
|
|
752
|
+
provider: "opencode",
|
|
753
|
+
item: { type: "reasoning", text },
|
|
754
|
+
};
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
else if (partType === "tool") {
|
|
758
|
+
const parsedToolPart = OpencodeToolPartToTimelineItemSchema.safeParse(part);
|
|
759
|
+
if (parsedToolPart.success) {
|
|
760
|
+
if (parsedToolPart.data) {
|
|
761
|
+
yield {
|
|
762
|
+
type: "timeline",
|
|
763
|
+
provider: "opencode",
|
|
764
|
+
item: parsedToolPart.data,
|
|
765
|
+
};
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
async getAvailableModes() {
|
|
774
|
+
return DEFAULT_MODES;
|
|
775
|
+
}
|
|
776
|
+
async getCurrentMode() {
|
|
777
|
+
return this.currentMode;
|
|
778
|
+
}
|
|
779
|
+
async setMode(modeId) {
|
|
780
|
+
this.currentMode = modeId;
|
|
781
|
+
}
|
|
782
|
+
getPendingPermissions() {
|
|
783
|
+
return Array.from(this.pendingPermissions.values());
|
|
784
|
+
}
|
|
785
|
+
async respondToPermission(requestId, response) {
|
|
786
|
+
const pending = this.pendingPermissions.get(requestId);
|
|
787
|
+
if (!pending) {
|
|
788
|
+
throw new Error(`No pending permission request with id '${requestId}'`);
|
|
789
|
+
}
|
|
790
|
+
const reply = response.behavior === "allow" ? "once" : "reject";
|
|
791
|
+
await this.client.permission.reply({
|
|
792
|
+
requestID: requestId,
|
|
793
|
+
directory: this.config.cwd,
|
|
794
|
+
reply,
|
|
795
|
+
message: response.behavior === "deny" ? response.message : undefined,
|
|
796
|
+
});
|
|
797
|
+
this.pendingPermissions.delete(requestId);
|
|
798
|
+
}
|
|
799
|
+
describePersistence() {
|
|
800
|
+
return {
|
|
801
|
+
provider: "opencode",
|
|
802
|
+
sessionId: this.sessionId,
|
|
803
|
+
nativeHandle: this.sessionId,
|
|
804
|
+
metadata: {
|
|
805
|
+
cwd: this.config.cwd,
|
|
806
|
+
},
|
|
807
|
+
};
|
|
808
|
+
}
|
|
809
|
+
async close() {
|
|
810
|
+
this.abortController?.abort();
|
|
811
|
+
}
|
|
812
|
+
buildPromptParts(prompt) {
|
|
813
|
+
if (typeof prompt === "string") {
|
|
814
|
+
return [{ type: "text", text: prompt }];
|
|
815
|
+
}
|
|
816
|
+
return prompt
|
|
817
|
+
.filter((p) => p.type === "text")
|
|
818
|
+
.map((p) => ({ type: "text", text: p.text }));
|
|
819
|
+
}
|
|
820
|
+
parseModel(model) {
|
|
821
|
+
if (!model) {
|
|
822
|
+
return undefined;
|
|
823
|
+
}
|
|
824
|
+
const parts = model.split("/");
|
|
825
|
+
if (parts.length >= 2) {
|
|
826
|
+
return { providerID: parts[0], modelID: parts.slice(1).join("/") };
|
|
827
|
+
}
|
|
828
|
+
return { providerID: "opencode", modelID: model };
|
|
829
|
+
}
|
|
830
|
+
async ensureMcpServersConfigured() {
|
|
831
|
+
if (this.mcpConfigured) {
|
|
832
|
+
return;
|
|
833
|
+
}
|
|
834
|
+
const mcpServers = this.config.mcpServers;
|
|
835
|
+
if (!mcpServers || Object.keys(mcpServers).length === 0) {
|
|
836
|
+
this.mcpConfigured = true;
|
|
837
|
+
return;
|
|
838
|
+
}
|
|
839
|
+
if (!this.mcpSetupPromise) {
|
|
840
|
+
this.mcpSetupPromise = this.configureMcpServers(mcpServers);
|
|
841
|
+
}
|
|
842
|
+
try {
|
|
843
|
+
await this.mcpSetupPromise;
|
|
844
|
+
this.mcpConfigured = true;
|
|
845
|
+
}
|
|
846
|
+
catch (error) {
|
|
847
|
+
this.mcpSetupPromise = null;
|
|
848
|
+
throw error;
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
async configureMcpServers(mcpServers) {
|
|
852
|
+
for (const [name, serverConfig] of Object.entries(mcpServers)) {
|
|
853
|
+
const mappedConfig = toOpenCodeMcpConfig(serverConfig);
|
|
854
|
+
await this.registerMcpServer(name, mappedConfig);
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
async registerMcpServer(name, config) {
|
|
858
|
+
await this.runMcpOperation("add", name, () => this.client.mcp.add({
|
|
859
|
+
directory: this.config.cwd,
|
|
860
|
+
name,
|
|
861
|
+
config,
|
|
862
|
+
}));
|
|
863
|
+
await this.runMcpOperation("connect", name, () => this.client.mcp.connect({
|
|
864
|
+
directory: this.config.cwd,
|
|
865
|
+
name,
|
|
866
|
+
}));
|
|
867
|
+
}
|
|
868
|
+
async runMcpOperation(operation, name, run) {
|
|
869
|
+
const response = await run();
|
|
870
|
+
const error = response.error;
|
|
871
|
+
if (!error) {
|
|
872
|
+
return;
|
|
873
|
+
}
|
|
874
|
+
if (isAlreadyPresentMcpError(error)) {
|
|
875
|
+
return;
|
|
876
|
+
}
|
|
877
|
+
throw new Error(`Failed to ${operation} OpenCode MCP server '${name}': ${stringifyUnknownError(error)}`);
|
|
878
|
+
}
|
|
879
|
+
translateEvent(event) {
|
|
880
|
+
const translated = translateOpenCodeEvent(event, {
|
|
881
|
+
sessionId: this.sessionId,
|
|
882
|
+
messageRoles: this.messageRoles,
|
|
883
|
+
accumulatedUsage: this.accumulatedUsage,
|
|
884
|
+
streamedPartKeys: this.streamedPartKeys,
|
|
885
|
+
});
|
|
886
|
+
for (const translatedEvent of translated) {
|
|
887
|
+
if (translatedEvent.type === "permission_requested") {
|
|
888
|
+
this.pendingPermissions.set(translatedEvent.request.id, translatedEvent.request);
|
|
889
|
+
}
|
|
890
|
+
if (translatedEvent.type === "turn_completed") {
|
|
891
|
+
translatedEvent.usage = this.extractAndResetUsage();
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
return translated;
|
|
895
|
+
}
|
|
896
|
+
extractAndResetUsage() {
|
|
897
|
+
const usage = this.accumulatedUsage;
|
|
898
|
+
this.accumulatedUsage = {};
|
|
899
|
+
if (!usage.inputTokens && !usage.outputTokens && !usage.totalCostUsd) {
|
|
900
|
+
return undefined;
|
|
901
|
+
}
|
|
902
|
+
return usage;
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
//# sourceMappingURL=opencode-agent.js.map
|