@getpaseo/server 0.1.15 → 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 +63 -6
- package/dist/server/client/daemon-client.d.ts.map +1 -1
- package/dist/server/client/daemon-client.js +436 -92
- package/dist/server/client/daemon-client.js.map +1 -1
- package/dist/server/server/agent/agent-manager.d.ts +13 -1
- package/dist/server/server/agent/agent-manager.d.ts.map +1 -1
- package/dist/server/server/agent/agent-manager.js +404 -39
- 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-projections.d.ts +5 -0
- package/dist/server/server/agent/agent-projections.d.ts.map +1 -1
- package/dist/server/server/agent/agent-projections.js +24 -0
- package/dist/server/server/agent/agent-projections.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 +20 -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 +20 -6
- package/dist/server/server/agent/agent-storage.d.ts.map +1 -1
- package/dist/server/server/agent/agent-storage.js +43 -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 +17 -0
- package/dist/server/server/agent/providers/claude/tool-call-detail-parser.js.map +1 -1
- package/dist/server/server/agent/providers/claude/tool-call-mapper.d.ts.map +1 -1
- package/dist/server/server/agent/providers/claude/tool-call-mapper.js +2 -0
- package/dist/server/server/agent/providers/claude/tool-call-mapper.js.map +1 -1
- package/dist/server/server/agent/providers/claude-agent.d.ts +10 -3
- package/dist/server/server/agent/providers/claude-agent.d.ts.map +1 -1
- package/dist/server/server/agent/providers/claude-agent.js +1702 -330
- 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 +50 -9
- 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 +55 -0
- package/dist/server/server/agent/providers/tool-call-detail-primitives.d.ts.map +1 -1
- package/dist/server/server/agent/providers/tool-call-detail-primitives.js +1 -0
- package/dist/server/server/agent/providers/tool-call-detail-primitives.js.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/client-message-id.d.ts +3 -0
- package/dist/server/server/client-message-id.d.ts.map +1 -0
- package/dist/server/server/client-message-id.js +12 -0
- package/dist/server/server/client-message-id.js.map +1 -0
- 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 +94 -8
- 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/persistence-hooks.js +1 -1
- package/dist/server/server/persistence-hooks.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.js +28 -28
- package/dist/server/server/relay-transport.js.map +1 -1
- package/dist/server/server/session.d.ts +60 -4
- package/dist/server/server/session.d.ts.map +1 -1
- package/dist/server/server/session.js +854 -190
- package/dist/server/server/session.js.map +1 -1
- package/dist/server/server/websocket-server.d.ts +24 -5
- package/dist/server/server/websocket-server.d.ts.map +1 -1
- package/dist/server/server/websocket-server.js +400 -77
- 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/daemon-endpoints.d.ts +9 -1
- package/dist/server/shared/daemon-endpoints.d.ts.map +1 -1
- package/dist/server/shared/daemon-endpoints.js +18 -3
- package/dist/server/shared/daemon-endpoints.js.map +1 -1
- package/dist/server/shared/messages.d.ts +4432 -380
- package/dist/server/shared/messages.d.ts.map +1 -1
- package/dist/server/shared/messages.js +139 -6
- 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 +7 -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 +15 -7
|
@@ -4,7 +4,7 @@ import { hostname as getHostname } from "node:os";
|
|
|
4
4
|
import { WSInboundMessageSchema, wrapSessionMessage, } from "./messages.js";
|
|
5
5
|
import { asUint8Array, decodeBinaryMuxFrame, encodeBinaryMuxFrame, } from "../shared/binary-mux.js";
|
|
6
6
|
import { isHostAllowed } from "./allowed-hosts.js";
|
|
7
|
-
import { Session } from "./session.js";
|
|
7
|
+
import { Session, } from "./session.js";
|
|
8
8
|
import { PushTokenStore } from "./push/token-store.js";
|
|
9
9
|
import { PushService } from "./push/push-service.js";
|
|
10
10
|
import { computeShouldNotifyClient, computeShouldSendPush, } from "./agent-attention-policy.js";
|
|
@@ -68,6 +68,12 @@ function bufferFromWsData(data) {
|
|
|
68
68
|
return Buffer.from(data);
|
|
69
69
|
}
|
|
70
70
|
const EXTERNAL_SESSION_DISCONNECT_GRACE_MS = 90000;
|
|
71
|
+
const HELLO_TIMEOUT_MS = 15000;
|
|
72
|
+
const WS_CLOSE_HELLO_TIMEOUT = 4001;
|
|
73
|
+
const WS_CLOSE_INVALID_HELLO = 4002;
|
|
74
|
+
const WS_CLOSE_INCOMPATIBLE_PROTOCOL = 4003;
|
|
75
|
+
const WS_PROTOCOL_VERSION = 1;
|
|
76
|
+
const WS_RUNTIME_METRICS_FLUSH_MS = 30000;
|
|
71
77
|
export class MissingDaemonVersionError extends Error {
|
|
72
78
|
constructor() {
|
|
73
79
|
super("VoiceAssistantWebSocketServer requires a non-empty daemonVersion.");
|
|
@@ -78,12 +84,33 @@ export class MissingDaemonVersionError extends Error {
|
|
|
78
84
|
* WebSocket server that only accepts sockets + parses/forwards messages to the session layer.
|
|
79
85
|
*/
|
|
80
86
|
export class VoiceAssistantWebSocketServer {
|
|
81
|
-
constructor(server, logger, serverId, agentManager, agentStorage, downloadTokenStore, paseoHome, createAgentMcpTransport, wsConfig, speech, terminalManager, voice, dictation, agentProviderRuntimeSettings, daemonVersion) {
|
|
87
|
+
constructor(server, logger, serverId, agentManager, agentStorage, downloadTokenStore, paseoHome, createAgentMcpTransport, wsConfig, speech, terminalManager, voice, dictation, agentProviderRuntimeSettings, daemonVersion, onLifecycleIntent) {
|
|
88
|
+
this.pendingConnections = new Map();
|
|
82
89
|
this.sessions = new Map();
|
|
83
90
|
this.externalSessionsByKey = new Map();
|
|
84
|
-
this.clientIdCounter = 0;
|
|
85
91
|
this.voiceSpeakHandlers = new Map();
|
|
86
92
|
this.voiceCallerContexts = new Map();
|
|
93
|
+
this.runtimeWindowStartedAt = Date.now();
|
|
94
|
+
this.runtimeCounters = {
|
|
95
|
+
connectedAwaitingHello: 0,
|
|
96
|
+
helloResumed: 0,
|
|
97
|
+
helloNew: 0,
|
|
98
|
+
pendingDisconnected: 0,
|
|
99
|
+
sessionDisconnectedWaitingReconnect: 0,
|
|
100
|
+
sessionSocketDisconnectedAttached: 0,
|
|
101
|
+
sessionCleanup: 0,
|
|
102
|
+
validationFailed: 0,
|
|
103
|
+
binaryBeforeHelloRejected: 0,
|
|
104
|
+
pendingMessageRejectedBeforeHello: 0,
|
|
105
|
+
missingConnectionForMessage: 0,
|
|
106
|
+
unexpectedHelloOnActiveConnection: 0,
|
|
107
|
+
relayExternalSocketAttached: 0,
|
|
108
|
+
originRejected: 0,
|
|
109
|
+
hostRejected: 0,
|
|
110
|
+
};
|
|
111
|
+
this.inboundMessageCounts = new Map();
|
|
112
|
+
this.inboundSessionRequestCounts = new Map();
|
|
113
|
+
this.runtimeMetricsInterval = null;
|
|
87
114
|
this.ACTIVITY_THRESHOLD_MS = 120000;
|
|
88
115
|
this.logger = logger.child({ module: "websocket-server" });
|
|
89
116
|
this.serverId = serverId;
|
|
@@ -102,6 +129,7 @@ export class VoiceAssistantWebSocketServer {
|
|
|
102
129
|
this.voice = voice ?? null;
|
|
103
130
|
this.dictation = dictation ?? null;
|
|
104
131
|
this.agentProviderRuntimeSettings = agentProviderRuntimeSettings;
|
|
132
|
+
this.onLifecycleIntent = onLifecycleIntent ?? null;
|
|
105
133
|
this.serverCapabilities = buildServerCapabilities({
|
|
106
134
|
readiness: this.dictation?.getSpeechReadiness?.() ?? null,
|
|
107
135
|
});
|
|
@@ -120,6 +148,7 @@ export class VoiceAssistantWebSocketServer {
|
|
|
120
148
|
const origin = requestMetadata.origin;
|
|
121
149
|
const requestHost = requestMetadata.host ?? null;
|
|
122
150
|
if (requestHost && !isHostAllowed(requestHost, allowedHosts)) {
|
|
151
|
+
this.incrementRuntimeCounter("hostRejected");
|
|
123
152
|
this.logger.warn({ ...requestMetadata, host: requestHost }, "Rejected connection from disallowed host");
|
|
124
153
|
callback(false, 403, "Host not allowed");
|
|
125
154
|
return;
|
|
@@ -131,6 +160,7 @@ export class VoiceAssistantWebSocketServer {
|
|
|
131
160
|
callback(true);
|
|
132
161
|
}
|
|
133
162
|
else {
|
|
163
|
+
this.incrementRuntimeCounter("originRejected");
|
|
134
164
|
this.logger.warn({ ...requestMetadata, origin }, "Rejected connection from origin");
|
|
135
165
|
callback(false, 403, "Origin not allowed");
|
|
136
166
|
}
|
|
@@ -139,6 +169,11 @@ export class VoiceAssistantWebSocketServer {
|
|
|
139
169
|
this.wss.on("connection", (ws, request) => {
|
|
140
170
|
void this.attachSocket(ws, request);
|
|
141
171
|
});
|
|
172
|
+
const runtimeMetricsInterval = setInterval(() => {
|
|
173
|
+
this.flushRuntimeMetrics();
|
|
174
|
+
}, WS_RUNTIME_METRICS_FLUSH_MS);
|
|
175
|
+
this.runtimeMetricsInterval = runtimeMetricsInterval;
|
|
176
|
+
runtimeMetricsInterval.unref?.();
|
|
142
177
|
this.logger.info("WebSocket server initialized on /ws");
|
|
143
178
|
}
|
|
144
179
|
broadcast(message) {
|
|
@@ -159,26 +194,52 @@ export class VoiceAssistantWebSocketServer {
|
|
|
159
194
|
return;
|
|
160
195
|
}
|
|
161
196
|
this.serverCapabilities = next;
|
|
162
|
-
this.
|
|
197
|
+
this.broadcastCapabilitiesUpdate();
|
|
163
198
|
}
|
|
164
199
|
async attachExternalSocket(ws, metadata) {
|
|
200
|
+
if (metadata?.transport === "relay") {
|
|
201
|
+
this.incrementRuntimeCounter("relayExternalSocketAttached");
|
|
202
|
+
}
|
|
165
203
|
await this.attachSocket(ws, undefined, metadata);
|
|
166
204
|
}
|
|
167
205
|
async close() {
|
|
206
|
+
if (this.runtimeMetricsInterval) {
|
|
207
|
+
clearInterval(this.runtimeMetricsInterval);
|
|
208
|
+
this.runtimeMetricsInterval = null;
|
|
209
|
+
}
|
|
210
|
+
this.flushRuntimeMetrics({ final: true });
|
|
168
211
|
const uniqueConnections = new Set([
|
|
169
212
|
...this.sessions.values(),
|
|
170
213
|
...this.externalSessionsByKey.values(),
|
|
171
214
|
]);
|
|
215
|
+
const pendingSockets = new Set(this.pendingConnections.keys());
|
|
216
|
+
for (const pending of this.pendingConnections.values()) {
|
|
217
|
+
if (pending.helloTimeout) {
|
|
218
|
+
clearTimeout(pending.helloTimeout);
|
|
219
|
+
pending.helloTimeout = null;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
172
222
|
const cleanupPromises = [];
|
|
173
223
|
for (const connection of uniqueConnections) {
|
|
174
224
|
if (connection.externalDisconnectCleanupTimeout) {
|
|
175
225
|
clearTimeout(connection.externalDisconnectCleanupTimeout);
|
|
176
226
|
connection.externalDisconnectCleanupTimeout = null;
|
|
177
227
|
}
|
|
178
|
-
const ws = connection.socketRef.current;
|
|
179
228
|
cleanupPromises.push(connection.session.cleanup());
|
|
229
|
+
for (const ws of connection.sockets) {
|
|
230
|
+
cleanupPromises.push(new Promise((resolve) => {
|
|
231
|
+
// WebSocket.CLOSED = 3
|
|
232
|
+
if (ws.readyState === 3) {
|
|
233
|
+
resolve();
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
ws.once("close", () => resolve());
|
|
237
|
+
ws.close();
|
|
238
|
+
}));
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
for (const ws of pendingSockets) {
|
|
180
242
|
cleanupPromises.push(new Promise((resolve) => {
|
|
181
|
-
// WebSocket.CLOSED = 3
|
|
182
243
|
if (ws.readyState === 3) {
|
|
183
244
|
resolve();
|
|
184
245
|
return;
|
|
@@ -188,6 +249,7 @@ export class VoiceAssistantWebSocketServer {
|
|
|
188
249
|
}));
|
|
189
250
|
}
|
|
190
251
|
await Promise.all(cleanupPromises);
|
|
252
|
+
this.pendingConnections.clear();
|
|
191
253
|
this.sessions.clear();
|
|
192
254
|
this.externalSessionsByKey.clear();
|
|
193
255
|
this.wss.close();
|
|
@@ -204,38 +266,20 @@ export class VoiceAssistantWebSocketServer {
|
|
|
204
266
|
}
|
|
205
267
|
ws.send(encodeBinaryMuxFrame(frame));
|
|
206
268
|
}
|
|
207
|
-
|
|
208
|
-
const
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
clearTimeout(existing.externalDisconnectCleanupTimeout);
|
|
216
|
-
existing.externalDisconnectCleanupTimeout = null;
|
|
217
|
-
}
|
|
218
|
-
const previousSocket = existing.socketRef.current;
|
|
219
|
-
if (previousSocket !== ws) {
|
|
220
|
-
this.sessions.delete(previousSocket);
|
|
221
|
-
existing.socketRef.current = ws;
|
|
222
|
-
}
|
|
223
|
-
this.sessions.set(ws, existing);
|
|
224
|
-
this.sendServerInfo(ws);
|
|
225
|
-
existing.connectionLogger.trace({
|
|
226
|
-
clientId: existing.clientId,
|
|
227
|
-
externalSessionKey,
|
|
228
|
-
totalSessions: this.sessions.size,
|
|
229
|
-
}, "Client reconnected");
|
|
230
|
-
this.bindSocketHandlers(ws, existing);
|
|
231
|
-
return;
|
|
232
|
-
}
|
|
269
|
+
sendToConnection(connection, message) {
|
|
270
|
+
for (const ws of connection.sockets) {
|
|
271
|
+
this.sendToClient(ws, message);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
sendBinaryToConnection(connection, frame) {
|
|
275
|
+
for (const ws of connection.sockets) {
|
|
276
|
+
this.sendBinaryToClient(ws, frame);
|
|
233
277
|
}
|
|
234
|
-
|
|
278
|
+
}
|
|
279
|
+
async attachSocket(ws, request, metadata) {
|
|
235
280
|
const requestMetadata = extractSocketRequestMetadata(request);
|
|
236
281
|
const connectionLoggerFields = {
|
|
237
|
-
|
|
238
|
-
transport: externalSessionKey ? "relay" : "direct",
|
|
282
|
+
transport: metadata?.transport === "relay" ? "relay" : "direct",
|
|
239
283
|
};
|
|
240
284
|
if (requestMetadata.host) {
|
|
241
285
|
connectionLoggerFields.host = requestMetadata.host;
|
|
@@ -250,14 +294,52 @@ export class VoiceAssistantWebSocketServer {
|
|
|
250
294
|
connectionLoggerFields.remoteAddress = requestMetadata.remoteAddress;
|
|
251
295
|
}
|
|
252
296
|
const connectionLogger = this.logger.child(connectionLoggerFields);
|
|
253
|
-
const
|
|
297
|
+
const pending = {
|
|
298
|
+
connectionLogger,
|
|
299
|
+
helloTimeout: null,
|
|
300
|
+
};
|
|
301
|
+
const timeout = setTimeout(() => {
|
|
302
|
+
if (this.pendingConnections.get(ws) !== pending) {
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
pending.helloTimeout = null;
|
|
306
|
+
this.pendingConnections.delete(ws);
|
|
307
|
+
pending.connectionLogger.warn({ timeoutMs: HELLO_TIMEOUT_MS }, "Closing connection due to missing hello");
|
|
308
|
+
try {
|
|
309
|
+
ws.close(WS_CLOSE_HELLO_TIMEOUT, "Hello timeout");
|
|
310
|
+
}
|
|
311
|
+
catch {
|
|
312
|
+
// ignore close errors
|
|
313
|
+
}
|
|
314
|
+
}, HELLO_TIMEOUT_MS);
|
|
315
|
+
pending.helloTimeout = timeout;
|
|
316
|
+
timeout.unref?.();
|
|
317
|
+
this.pendingConnections.set(ws, pending);
|
|
318
|
+
this.incrementRuntimeCounter("connectedAwaitingHello");
|
|
319
|
+
this.bindSocketHandlers(ws);
|
|
320
|
+
pending.connectionLogger.trace({
|
|
321
|
+
totalPendingConnections: this.pendingConnections.size,
|
|
322
|
+
}, "Client connected; awaiting hello");
|
|
323
|
+
}
|
|
324
|
+
createSessionConnection(params) {
|
|
325
|
+
const { ws, clientId, connectionLogger } = params;
|
|
326
|
+
let connection = null;
|
|
254
327
|
const session = new Session({
|
|
255
328
|
clientId,
|
|
256
329
|
onMessage: (msg) => {
|
|
257
|
-
|
|
330
|
+
if (!connection) {
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
this.sendToConnection(connection, wrapSessionMessage(msg));
|
|
258
334
|
},
|
|
259
335
|
onBinaryMessage: (frame) => {
|
|
260
|
-
|
|
336
|
+
if (!connection) {
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
this.sendBinaryToConnection(connection, frame);
|
|
340
|
+
},
|
|
341
|
+
onLifecycleIntent: (intent) => {
|
|
342
|
+
this.onLifecycleIntent?.(intent);
|
|
261
343
|
},
|
|
262
344
|
logger: connectionLogger.child({ module: "session" }),
|
|
263
345
|
downloadTokenStore: this.downloadTokenStore,
|
|
@@ -289,21 +371,98 @@ export class VoiceAssistantWebSocketServer {
|
|
|
289
371
|
dictation: this.dictation ?? undefined,
|
|
290
372
|
agentProviderRuntimeSettings: this.agentProviderRuntimeSettings,
|
|
291
373
|
});
|
|
292
|
-
|
|
374
|
+
connection = {
|
|
293
375
|
session,
|
|
294
376
|
clientId,
|
|
295
377
|
connectionLogger,
|
|
296
|
-
|
|
297
|
-
externalSessionKey,
|
|
378
|
+
sockets: new Set([ws]),
|
|
298
379
|
externalDisconnectCleanupTimeout: null,
|
|
299
380
|
};
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
381
|
+
return connection;
|
|
382
|
+
}
|
|
383
|
+
clearPendingConnection(ws) {
|
|
384
|
+
const pending = this.pendingConnections.get(ws);
|
|
385
|
+
if (!pending) {
|
|
386
|
+
return null;
|
|
387
|
+
}
|
|
388
|
+
if (pending.helloTimeout) {
|
|
389
|
+
clearTimeout(pending.helloTimeout);
|
|
390
|
+
pending.helloTimeout = null;
|
|
391
|
+
}
|
|
392
|
+
this.pendingConnections.delete(ws);
|
|
393
|
+
return pending;
|
|
394
|
+
}
|
|
395
|
+
buildWelcomeMessage(params) {
|
|
396
|
+
return {
|
|
397
|
+
type: "welcome",
|
|
398
|
+
serverId: this.serverId,
|
|
399
|
+
hostname: getHostname(),
|
|
400
|
+
version: this.daemonVersion,
|
|
401
|
+
resumed: params.resumed,
|
|
402
|
+
...(this.serverCapabilities ? { capabilities: this.serverCapabilities } : {}),
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
handleHello(params) {
|
|
406
|
+
const { ws, message, pending } = params;
|
|
407
|
+
if (message.protocolVersion !== WS_PROTOCOL_VERSION) {
|
|
408
|
+
this.clearPendingConnection(ws);
|
|
409
|
+
pending.connectionLogger.warn({
|
|
410
|
+
receivedProtocolVersion: message.protocolVersion,
|
|
411
|
+
expectedProtocolVersion: WS_PROTOCOL_VERSION,
|
|
412
|
+
}, "Rejected hello due to protocol version mismatch");
|
|
413
|
+
try {
|
|
414
|
+
ws.close(WS_CLOSE_INCOMPATIBLE_PROTOCOL, "Incompatible protocol version");
|
|
415
|
+
}
|
|
416
|
+
catch {
|
|
417
|
+
// ignore close errors
|
|
418
|
+
}
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
const clientId = message.clientId.trim();
|
|
422
|
+
if (clientId.length === 0) {
|
|
423
|
+
this.clearPendingConnection(ws);
|
|
424
|
+
pending.connectionLogger.warn("Rejected hello with empty clientId");
|
|
425
|
+
try {
|
|
426
|
+
ws.close(WS_CLOSE_INVALID_HELLO, "Invalid hello");
|
|
427
|
+
}
|
|
428
|
+
catch {
|
|
429
|
+
// ignore close errors
|
|
430
|
+
}
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
433
|
+
this.clearPendingConnection(ws);
|
|
434
|
+
const existing = this.externalSessionsByKey.get(clientId);
|
|
435
|
+
if (existing) {
|
|
436
|
+
this.incrementRuntimeCounter("helloResumed");
|
|
437
|
+
if (existing.externalDisconnectCleanupTimeout) {
|
|
438
|
+
clearTimeout(existing.externalDisconnectCleanupTimeout);
|
|
439
|
+
existing.externalDisconnectCleanupTimeout = null;
|
|
440
|
+
}
|
|
441
|
+
existing.sockets.add(ws);
|
|
442
|
+
this.sessions.set(ws, existing);
|
|
443
|
+
this.sendToClient(ws, this.buildWelcomeMessage({ resumed: true }));
|
|
444
|
+
existing.connectionLogger.trace({
|
|
445
|
+
clientId,
|
|
446
|
+
resumed: true,
|
|
447
|
+
totalSessions: this.sessions.size,
|
|
448
|
+
}, "Client connected via hello");
|
|
449
|
+
return;
|
|
303
450
|
}
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
this.
|
|
451
|
+
const connectionLogger = pending.connectionLogger.child({ clientId });
|
|
452
|
+
this.incrementRuntimeCounter("helloNew");
|
|
453
|
+
const connection = this.createSessionConnection({
|
|
454
|
+
ws,
|
|
455
|
+
clientId,
|
|
456
|
+
connectionLogger,
|
|
457
|
+
});
|
|
458
|
+
this.sessions.set(ws, connection);
|
|
459
|
+
this.externalSessionsByKey.set(clientId, connection);
|
|
460
|
+
this.sendToClient(ws, this.buildWelcomeMessage({ resumed: false }));
|
|
461
|
+
connection.connectionLogger.trace({
|
|
462
|
+
clientId,
|
|
463
|
+
resumed: false,
|
|
464
|
+
totalSessions: this.sessions.size,
|
|
465
|
+
}, "Client connected via hello");
|
|
307
466
|
}
|
|
308
467
|
buildServerInfoStatusPayload() {
|
|
309
468
|
return {
|
|
@@ -314,33 +473,29 @@ export class VoiceAssistantWebSocketServer {
|
|
|
314
473
|
...(this.serverCapabilities ? { capabilities: this.serverCapabilities } : {}),
|
|
315
474
|
};
|
|
316
475
|
}
|
|
317
|
-
|
|
476
|
+
broadcastCapabilitiesUpdate() {
|
|
318
477
|
this.broadcast(wrapSessionMessage({
|
|
319
478
|
type: "status",
|
|
320
479
|
payload: this.buildServerInfoStatusPayload(),
|
|
321
480
|
}));
|
|
322
481
|
}
|
|
323
|
-
|
|
324
|
-
// Advertise stable server identity immediately on connect (used for URL/shareable IDs).
|
|
325
|
-
this.sendToClient(ws, wrapSessionMessage({
|
|
326
|
-
type: "status",
|
|
327
|
-
payload: this.buildServerInfoStatusPayload(),
|
|
328
|
-
}));
|
|
329
|
-
}
|
|
330
|
-
bindSocketHandlers(ws, connection) {
|
|
482
|
+
bindSocketHandlers(ws) {
|
|
331
483
|
ws.on("message", (data) => {
|
|
332
484
|
void this.handleRawMessage(ws, data);
|
|
333
485
|
});
|
|
334
486
|
ws.on("close", async (code, reason) => {
|
|
335
|
-
await this.detachSocket(ws,
|
|
487
|
+
await this.detachSocket(ws, {
|
|
336
488
|
code: typeof code === "number" ? code : undefined,
|
|
337
489
|
reason,
|
|
338
490
|
});
|
|
339
491
|
});
|
|
340
492
|
ws.on("error", async (error) => {
|
|
341
493
|
const err = error instanceof Error ? error : new Error(String(error));
|
|
342
|
-
|
|
343
|
-
|
|
494
|
+
const active = this.sessions.get(ws);
|
|
495
|
+
const pending = this.pendingConnections.get(ws);
|
|
496
|
+
const log = active?.connectionLogger ?? pending?.connectionLogger ?? this.logger;
|
|
497
|
+
log.error({ err }, "Client error");
|
|
498
|
+
await this.detachSocket(ws, { error: err });
|
|
344
499
|
});
|
|
345
500
|
}
|
|
346
501
|
resolveVoiceSpeakHandler(callerAgentId) {
|
|
@@ -349,13 +504,24 @@ export class VoiceAssistantWebSocketServer {
|
|
|
349
504
|
resolveVoiceCallerContext(callerAgentId) {
|
|
350
505
|
return this.voiceCallerContexts.get(callerAgentId) ?? null;
|
|
351
506
|
}
|
|
352
|
-
async detachSocket(ws,
|
|
353
|
-
const
|
|
354
|
-
if (
|
|
507
|
+
async detachSocket(ws, details) {
|
|
508
|
+
const pending = this.clearPendingConnection(ws);
|
|
509
|
+
if (pending) {
|
|
510
|
+
this.incrementRuntimeCounter("pendingDisconnected");
|
|
511
|
+
pending.connectionLogger.trace({
|
|
512
|
+
code: details.code,
|
|
513
|
+
reason: stringifyCloseReason(details.reason),
|
|
514
|
+
}, "Pending client disconnected");
|
|
355
515
|
return;
|
|
516
|
+
}
|
|
517
|
+
const connection = this.sessions.get(ws);
|
|
518
|
+
if (!connection) {
|
|
519
|
+
return;
|
|
520
|
+
}
|
|
356
521
|
this.sessions.delete(ws);
|
|
357
|
-
|
|
358
|
-
|
|
522
|
+
connection.sockets.delete(ws);
|
|
523
|
+
if (connection.sockets.size === 0) {
|
|
524
|
+
this.incrementRuntimeCounter("sessionDisconnectedWaitingReconnect");
|
|
359
525
|
if (connection.externalDisconnectCleanupTimeout) {
|
|
360
526
|
clearTimeout(connection.externalDisconnectCleanupTimeout);
|
|
361
527
|
}
|
|
@@ -369,41 +535,61 @@ export class VoiceAssistantWebSocketServer {
|
|
|
369
535
|
connection.externalDisconnectCleanupTimeout = timeout;
|
|
370
536
|
connection.connectionLogger.trace({
|
|
371
537
|
clientId: connection.clientId,
|
|
372
|
-
externalSessionKey: connection.externalSessionKey,
|
|
373
538
|
code: details.code,
|
|
374
539
|
reason: stringifyCloseReason(details.reason),
|
|
375
540
|
reconnectGraceMs: EXTERNAL_SESSION_DISCONNECT_GRACE_MS,
|
|
376
541
|
}, "Client disconnected; waiting for reconnect");
|
|
377
542
|
return;
|
|
378
543
|
}
|
|
544
|
+
if (connection.sockets.size > 0) {
|
|
545
|
+
this.incrementRuntimeCounter("sessionSocketDisconnectedAttached");
|
|
546
|
+
connection.connectionLogger.trace({
|
|
547
|
+
clientId: connection.clientId,
|
|
548
|
+
remainingSockets: connection.sockets.size,
|
|
549
|
+
code: details.code,
|
|
550
|
+
reason: stringifyCloseReason(details.reason),
|
|
551
|
+
}, "Client socket disconnected; session remains attached");
|
|
552
|
+
return;
|
|
553
|
+
}
|
|
379
554
|
await this.cleanupConnection(connection, "Client disconnected");
|
|
380
555
|
}
|
|
381
556
|
async cleanupConnection(connection, logMessage) {
|
|
557
|
+
this.incrementRuntimeCounter("sessionCleanup");
|
|
382
558
|
if (connection.externalDisconnectCleanupTimeout) {
|
|
383
559
|
clearTimeout(connection.externalDisconnectCleanupTimeout);
|
|
384
560
|
connection.externalDisconnectCleanupTimeout = null;
|
|
385
561
|
}
|
|
386
|
-
const
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
562
|
+
for (const socket of connection.sockets) {
|
|
563
|
+
this.sessions.delete(socket);
|
|
564
|
+
}
|
|
565
|
+
connection.sockets.clear();
|
|
566
|
+
const existing = this.externalSessionsByKey.get(connection.clientId);
|
|
567
|
+
if (existing === connection) {
|
|
568
|
+
this.externalSessionsByKey.delete(connection.clientId);
|
|
393
569
|
}
|
|
394
570
|
connection.connectionLogger.trace({ clientId: connection.clientId, totalSessions: this.sessions.size }, logMessage);
|
|
395
571
|
await connection.session.cleanup();
|
|
396
572
|
}
|
|
397
573
|
async handleRawMessage(ws, data) {
|
|
574
|
+
const activeConnection = this.sessions.get(ws);
|
|
575
|
+
const pendingConnection = this.pendingConnections.get(ws);
|
|
576
|
+
const log = activeConnection?.connectionLogger ?? pendingConnection?.connectionLogger ?? this.logger;
|
|
398
577
|
try {
|
|
399
|
-
const activeConnection = this.sessions.get(ws);
|
|
400
578
|
const buffer = bufferFromWsData(data);
|
|
401
579
|
const asBytes = asUint8Array(buffer);
|
|
402
580
|
if (asBytes) {
|
|
403
581
|
const frame = decodeBinaryMuxFrame(asBytes);
|
|
404
582
|
if (frame) {
|
|
405
583
|
if (!activeConnection) {
|
|
406
|
-
this.
|
|
584
|
+
this.incrementRuntimeCounter("binaryBeforeHelloRejected");
|
|
585
|
+
log.warn("Rejected binary frame before hello");
|
|
586
|
+
this.clearPendingConnection(ws);
|
|
587
|
+
try {
|
|
588
|
+
ws.close(WS_CLOSE_INVALID_HELLO, "Session message before hello");
|
|
589
|
+
}
|
|
590
|
+
catch {
|
|
591
|
+
// ignore close errors
|
|
592
|
+
}
|
|
407
593
|
return;
|
|
408
594
|
}
|
|
409
595
|
activeConnection.session.handleBinaryFrame(frame);
|
|
@@ -413,13 +599,26 @@ export class VoiceAssistantWebSocketServer {
|
|
|
413
599
|
const parsed = JSON.parse(buffer.toString());
|
|
414
600
|
const parsedMessage = WSInboundMessageSchema.safeParse(parsed);
|
|
415
601
|
if (!parsedMessage.success) {
|
|
602
|
+
this.incrementRuntimeCounter("validationFailed");
|
|
603
|
+
if (pendingConnection) {
|
|
604
|
+
pendingConnection.connectionLogger.warn({
|
|
605
|
+
error: parsedMessage.error.message,
|
|
606
|
+
}, "Rejected pending message before hello");
|
|
607
|
+
this.clearPendingConnection(ws);
|
|
608
|
+
try {
|
|
609
|
+
ws.close(WS_CLOSE_INVALID_HELLO, "Invalid hello");
|
|
610
|
+
}
|
|
611
|
+
catch {
|
|
612
|
+
// ignore close errors
|
|
613
|
+
}
|
|
614
|
+
return;
|
|
615
|
+
}
|
|
416
616
|
const requestInfo = extractRequestInfoFromUnknownWsInbound(parsed);
|
|
417
617
|
const isUnknownSchema = requestInfo?.requestId != null &&
|
|
418
618
|
typeof parsed === "object" &&
|
|
419
619
|
parsed != null &&
|
|
420
620
|
"type" in parsed &&
|
|
421
621
|
parsed.type === "session";
|
|
422
|
-
const log = activeConnection?.connectionLogger ?? this.logger;
|
|
423
622
|
log.warn({
|
|
424
623
|
clientId: activeConnection?.clientId,
|
|
425
624
|
requestId: requestInfo?.requestId,
|
|
@@ -449,6 +648,7 @@ export class VoiceAssistantWebSocketServer {
|
|
|
449
648
|
return;
|
|
450
649
|
}
|
|
451
650
|
const message = parsedMessage.data;
|
|
651
|
+
this.recordInboundMessageType(message.type);
|
|
452
652
|
if (message.type === "ping") {
|
|
453
653
|
this.sendToClient(ws, { type: "pong" });
|
|
454
654
|
return;
|
|
@@ -456,11 +656,46 @@ export class VoiceAssistantWebSocketServer {
|
|
|
456
656
|
if (message.type === "recording_state") {
|
|
457
657
|
return;
|
|
458
658
|
}
|
|
659
|
+
if (pendingConnection) {
|
|
660
|
+
if (message.type === "hello") {
|
|
661
|
+
this.handleHello({
|
|
662
|
+
ws,
|
|
663
|
+
message,
|
|
664
|
+
pending: pendingConnection,
|
|
665
|
+
});
|
|
666
|
+
return;
|
|
667
|
+
}
|
|
668
|
+
pendingConnection.connectionLogger.warn({
|
|
669
|
+
messageType: message.type,
|
|
670
|
+
}, "Rejected pending message before hello");
|
|
671
|
+
this.incrementRuntimeCounter("pendingMessageRejectedBeforeHello");
|
|
672
|
+
this.clearPendingConnection(ws);
|
|
673
|
+
try {
|
|
674
|
+
ws.close(WS_CLOSE_INVALID_HELLO, "Session message before hello");
|
|
675
|
+
}
|
|
676
|
+
catch {
|
|
677
|
+
// ignore close errors
|
|
678
|
+
}
|
|
679
|
+
return;
|
|
680
|
+
}
|
|
459
681
|
if (!activeConnection) {
|
|
460
|
-
this.
|
|
682
|
+
this.incrementRuntimeCounter("missingConnectionForMessage");
|
|
683
|
+
this.logger.error("No connection found for websocket");
|
|
684
|
+
return;
|
|
685
|
+
}
|
|
686
|
+
if (message.type === "hello") {
|
|
687
|
+
this.incrementRuntimeCounter("unexpectedHelloOnActiveConnection");
|
|
688
|
+
activeConnection.connectionLogger.warn("Received hello on active connection");
|
|
689
|
+
try {
|
|
690
|
+
ws.close(WS_CLOSE_INVALID_HELLO, "Unexpected hello");
|
|
691
|
+
}
|
|
692
|
+
catch {
|
|
693
|
+
// ignore close errors
|
|
694
|
+
}
|
|
461
695
|
return;
|
|
462
696
|
}
|
|
463
697
|
if (message.type === "session") {
|
|
698
|
+
this.recordInboundSessionRequestType(message.message.type);
|
|
464
699
|
await activeConnection.session.handleMessage(message.message);
|
|
465
700
|
}
|
|
466
701
|
}
|
|
@@ -482,11 +717,21 @@ export class VoiceAssistantWebSocketServer {
|
|
|
482
717
|
const trimmedRawPayload = typeof rawPayload === "string" && rawPayload.length > 2000
|
|
483
718
|
? `${rawPayload.slice(0, 2000)}... (truncated)`
|
|
484
719
|
: rawPayload;
|
|
485
|
-
|
|
720
|
+
log.error({
|
|
486
721
|
err,
|
|
487
722
|
rawPayload: trimmedRawPayload,
|
|
488
723
|
parsedPayload,
|
|
489
724
|
}, "Failed to parse/handle message");
|
|
725
|
+
if (this.pendingConnections.has(ws)) {
|
|
726
|
+
this.clearPendingConnection(ws);
|
|
727
|
+
try {
|
|
728
|
+
ws.close(WS_CLOSE_INVALID_HELLO, "Invalid hello");
|
|
729
|
+
}
|
|
730
|
+
catch {
|
|
731
|
+
// ignore close errors
|
|
732
|
+
}
|
|
733
|
+
return;
|
|
734
|
+
}
|
|
490
735
|
const requestInfo = extractRequestInfoFromUnknownWsInbound(parsedPayload);
|
|
491
736
|
if (requestInfo) {
|
|
492
737
|
this.sendToClient(ws, wrapSessionMessage({
|
|
@@ -509,6 +754,84 @@ export class VoiceAssistantWebSocketServer {
|
|
|
509
754
|
}));
|
|
510
755
|
}
|
|
511
756
|
}
|
|
757
|
+
incrementRuntimeCounter(counter) {
|
|
758
|
+
this.runtimeCounters[counter] += 1;
|
|
759
|
+
}
|
|
760
|
+
incrementCount(map, key) {
|
|
761
|
+
map.set(key, (map.get(key) ?? 0) + 1);
|
|
762
|
+
}
|
|
763
|
+
recordInboundMessageType(type) {
|
|
764
|
+
this.incrementCount(this.inboundMessageCounts, type);
|
|
765
|
+
}
|
|
766
|
+
recordInboundSessionRequestType(type) {
|
|
767
|
+
this.incrementCount(this.inboundSessionRequestCounts, type);
|
|
768
|
+
}
|
|
769
|
+
getTopCounts(map, limit) {
|
|
770
|
+
return [...map.entries()].sort((a, b) => b[1] - a[1]).slice(0, limit);
|
|
771
|
+
}
|
|
772
|
+
collectSessionRuntimeMetrics() {
|
|
773
|
+
const uniqueConnections = new Set(this.externalSessionsByKey.values());
|
|
774
|
+
let checkoutDiffTargetCount = 0;
|
|
775
|
+
let checkoutDiffSubscriptionCount = 0;
|
|
776
|
+
let checkoutDiffWatcherCount = 0;
|
|
777
|
+
let checkoutDiffFallbackRefreshTargetCount = 0;
|
|
778
|
+
let terminalDirectorySubscriptionCount = 0;
|
|
779
|
+
let terminalSubscriptionCount = 0;
|
|
780
|
+
let terminalStreamCount = 0;
|
|
781
|
+
for (const connection of uniqueConnections) {
|
|
782
|
+
const sessionMetrics = connection.session.getRuntimeMetrics();
|
|
783
|
+
checkoutDiffTargetCount += sessionMetrics.checkoutDiffTargetCount;
|
|
784
|
+
checkoutDiffSubscriptionCount += sessionMetrics.checkoutDiffSubscriptionCount;
|
|
785
|
+
checkoutDiffWatcherCount += sessionMetrics.checkoutDiffWatcherCount;
|
|
786
|
+
checkoutDiffFallbackRefreshTargetCount +=
|
|
787
|
+
sessionMetrics.checkoutDiffFallbackRefreshTargetCount;
|
|
788
|
+
terminalDirectorySubscriptionCount += sessionMetrics.terminalDirectorySubscriptionCount;
|
|
789
|
+
terminalSubscriptionCount += sessionMetrics.terminalSubscriptionCount;
|
|
790
|
+
terminalStreamCount += sessionMetrics.terminalStreamCount;
|
|
791
|
+
}
|
|
792
|
+
return {
|
|
793
|
+
checkoutDiffTargetCount,
|
|
794
|
+
checkoutDiffSubscriptionCount,
|
|
795
|
+
checkoutDiffWatcherCount,
|
|
796
|
+
checkoutDiffFallbackRefreshTargetCount,
|
|
797
|
+
terminalDirectorySubscriptionCount,
|
|
798
|
+
terminalSubscriptionCount,
|
|
799
|
+
terminalStreamCount,
|
|
800
|
+
};
|
|
801
|
+
}
|
|
802
|
+
flushRuntimeMetrics(options) {
|
|
803
|
+
const now = Date.now();
|
|
804
|
+
const windowMs = Math.max(0, now - this.runtimeWindowStartedAt);
|
|
805
|
+
const activeConnections = new Set(this.sessions.values()).size;
|
|
806
|
+
const activeSockets = this.sessions.size;
|
|
807
|
+
const pendingConnections = this.pendingConnections.size;
|
|
808
|
+
const reconnectGraceSessions = [...this.externalSessionsByKey.values()].filter((connection) => connection.sockets.size === 0 &&
|
|
809
|
+
connection.externalDisconnectCleanupTimeout !== null).length;
|
|
810
|
+
const sessionMetrics = this.collectSessionRuntimeMetrics();
|
|
811
|
+
this.logger.info({
|
|
812
|
+
windowMs,
|
|
813
|
+
final: Boolean(options?.final),
|
|
814
|
+
sessions: {
|
|
815
|
+
activeConnections,
|
|
816
|
+
externalSessionKeys: this.externalSessionsByKey.size,
|
|
817
|
+
reconnectGraceSessions,
|
|
818
|
+
},
|
|
819
|
+
sockets: {
|
|
820
|
+
activeSockets,
|
|
821
|
+
pendingConnections,
|
|
822
|
+
},
|
|
823
|
+
counters: { ...this.runtimeCounters },
|
|
824
|
+
inboundMessageTypesTop: this.getTopCounts(this.inboundMessageCounts, 12),
|
|
825
|
+
inboundSessionRequestTypesTop: this.getTopCounts(this.inboundSessionRequestCounts, 20),
|
|
826
|
+
runtime: sessionMetrics,
|
|
827
|
+
}, "ws_runtime_metrics");
|
|
828
|
+
for (const counter of Object.keys(this.runtimeCounters)) {
|
|
829
|
+
this.runtimeCounters[counter] = 0;
|
|
830
|
+
}
|
|
831
|
+
this.inboundMessageCounts.clear();
|
|
832
|
+
this.inboundSessionRequestCounts.clear();
|
|
833
|
+
this.runtimeWindowStartedAt = now;
|
|
834
|
+
}
|
|
512
835
|
getClientActivityState(session) {
|
|
513
836
|
const activity = session.getClientActivity();
|
|
514
837
|
if (!activity) {
|