@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
|
@@ -17,9 +17,10 @@ import { buildConfigOverrides, buildSessionConfig, extractTimestamps } from './p
|
|
|
17
17
|
import { experimental_createMCPClient } from 'ai';
|
|
18
18
|
import { buildProviderRegistry } from './agent/provider-registry.js';
|
|
19
19
|
import { scheduleAgentMetadataGeneration } from './agent/agent-metadata-generator.js';
|
|
20
|
-
import { toAgentPayload } from './agent/agent-projections.js';
|
|
20
|
+
import { resolveEffectiveThinkingOptionId, toAgentPayload } from './agent/agent-projections.js';
|
|
21
|
+
import { MAX_EXPLICIT_AGENT_TITLE_CHARS } from './agent/agent-title-limits.js';
|
|
21
22
|
import { appendTimelineItemIfAgentKnown, emitLiveTimelineItemIfAgentKnown, } from './agent/timeline-append.js';
|
|
22
|
-
import { projectTimelineRows } from './agent/timeline-projection.js';
|
|
23
|
+
import { projectTimelineRows, selectTimelineWindowByProjectedLimit, } from './agent/timeline-projection.js';
|
|
23
24
|
import { DEFAULT_STRUCTURED_GENERATION_PROVIDERS, StructuredAgentFallbackError, StructuredAgentResponseError, generateStructuredAgentResponseWithFallback, } from './agent/agent-response-loop.js';
|
|
24
25
|
import { isValidAgentProvider, AGENT_PROVIDER_IDS } from './agent/provider-manifest.js';
|
|
25
26
|
import { buildVoiceAgentMcpServerConfig, buildVoiceModeSystemPrompt, stripVoiceModeSystemPrompt, } from './voice-config.js';
|
|
@@ -32,20 +33,46 @@ import { getProjectIcon } from '../utils/project-icon.js';
|
|
|
32
33
|
import { expandTilde } from '../utils/path.js';
|
|
33
34
|
import { searchHomeDirectories, searchWorkspaceEntries } from '../utils/directory-suggestions.js';
|
|
34
35
|
import { ensureLocalSpeechModels, getLocalSpeechModelDir, listLocalSpeechModels, } from './speech/providers/local/models.js';
|
|
36
|
+
import { resolveClientMessageId } from './client-message-id.js';
|
|
35
37
|
const execAsync = promisify(exec);
|
|
38
|
+
const MAX_INITIAL_AGENT_TITLE_CHARS = Math.min(60, MAX_EXPLICIT_AGENT_TITLE_CHARS);
|
|
36
39
|
const READ_ONLY_GIT_ENV = {
|
|
37
40
|
...process.env,
|
|
38
41
|
GIT_OPTIONAL_LOCKS: '0',
|
|
39
42
|
};
|
|
40
43
|
const pendingAgentInitializations = new Map();
|
|
41
|
-
let restartRequested = false;
|
|
42
44
|
const DEFAULT_AGENT_PROVIDER = AGENT_PROVIDER_IDS[0];
|
|
43
|
-
const RESTART_EXIT_DELAY_MS = 250;
|
|
44
45
|
const CHECKOUT_DIFF_WATCH_DEBOUNCE_MS = 150;
|
|
45
46
|
const CHECKOUT_DIFF_FALLBACK_REFRESH_MS = 5000;
|
|
46
47
|
const TERMINAL_STREAM_WINDOW_BYTES = 256 * 1024;
|
|
47
48
|
const TERMINAL_STREAM_MAX_PENDING_BYTES = 2 * 1024 * 1024;
|
|
48
49
|
const TERMINAL_STREAM_MAX_PENDING_CHUNKS = 2048;
|
|
50
|
+
function deriveInitialAgentTitle(prompt) {
|
|
51
|
+
const firstContentLine = prompt
|
|
52
|
+
.split(/\r?\n/)
|
|
53
|
+
.map((line) => line.trim())
|
|
54
|
+
.find((line) => line.length > 0);
|
|
55
|
+
if (!firstContentLine) {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
const normalized = firstContentLine.replace(/\s+/g, ' ').trim();
|
|
59
|
+
if (!normalized) {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
const clamped = normalized.slice(0, MAX_INITIAL_AGENT_TITLE_CHARS).trim();
|
|
63
|
+
return clamped.length > 0 ? clamped : null;
|
|
64
|
+
}
|
|
65
|
+
export function resolveCreateAgentTitles(options) {
|
|
66
|
+
const explicitTitle = typeof options.configTitle === 'string' && options.configTitle.trim().length > 0
|
|
67
|
+
? options.configTitle.trim()
|
|
68
|
+
: null;
|
|
69
|
+
const trimmedPrompt = options.initialPrompt?.trim();
|
|
70
|
+
const provisionalTitle = explicitTitle ?? (trimmedPrompt ? deriveInitialAgentTitle(trimmedPrompt) : null);
|
|
71
|
+
return {
|
|
72
|
+
explicitTitle,
|
|
73
|
+
provisionalTitle,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
49
76
|
function deriveRemoteProjectKey(remoteUrl) {
|
|
50
77
|
if (!remoteUrl) {
|
|
51
78
|
return null;
|
|
@@ -92,10 +119,9 @@ function deriveProjectGroupingKey(options) {
|
|
|
92
119
|
if (remoteKey) {
|
|
93
120
|
return remoteKey;
|
|
94
121
|
}
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
return options.cwd.slice(0, idx).replace(/\/$/, '');
|
|
122
|
+
const mainRepoRoot = options.mainRepoRoot?.trim();
|
|
123
|
+
if (options.isPaseoOwnedWorktree && mainRepoRoot) {
|
|
124
|
+
return mainRepoRoot;
|
|
99
125
|
}
|
|
100
126
|
return options.cwd;
|
|
101
127
|
}
|
|
@@ -213,6 +239,7 @@ export class Session {
|
|
|
213
239
|
this.agentTools = null;
|
|
214
240
|
this.unsubscribeAgentEvents = null;
|
|
215
241
|
this.agentUpdatesSubscription = null;
|
|
242
|
+
this.workspaceUpdatesSubscription = null;
|
|
216
243
|
this.clientActivity = null;
|
|
217
244
|
this.MOBILE_BACKGROUND_STREAM_GRACE_MS = 60000;
|
|
218
245
|
this.subscribedTerminalDirectories = new Set();
|
|
@@ -226,11 +253,19 @@ export class Session {
|
|
|
226
253
|
this.checkoutDiffTargets = new Map();
|
|
227
254
|
this.voiceModeAgentId = null;
|
|
228
255
|
this.voiceModeBaseConfig = null;
|
|
229
|
-
|
|
256
|
+
this.workspaceStatePriority = {
|
|
257
|
+
needs_input: 0,
|
|
258
|
+
failed: 1,
|
|
259
|
+
running: 2,
|
|
260
|
+
attention: 3,
|
|
261
|
+
done: 4,
|
|
262
|
+
};
|
|
263
|
+
const { clientId, onMessage, onBinaryMessage, onLifecycleIntent, logger, downloadTokenStore, pushTokenStore, paseoHome, agentManager, agentStorage, createAgentMcpTransport, stt, tts, terminalManager, voice, voiceBridge, dictation, agentProviderRuntimeSettings, } = options;
|
|
230
264
|
this.clientId = clientId;
|
|
231
265
|
this.sessionId = uuidv4();
|
|
232
266
|
this.onMessage = onMessage;
|
|
233
267
|
this.onBinaryMessage = onBinaryMessage ?? null;
|
|
268
|
+
this.onLifecycleIntent = onLifecycleIntent ?? null;
|
|
234
269
|
this.downloadTokenStore = downloadTokenStore;
|
|
235
270
|
this.pushTokenStore = pushTokenStore;
|
|
236
271
|
this.paseoHome = paseoHome;
|
|
@@ -296,6 +331,25 @@ export class Session {
|
|
|
296
331
|
getClientActivity() {
|
|
297
332
|
return this.clientActivity;
|
|
298
333
|
}
|
|
334
|
+
getRuntimeMetrics() {
|
|
335
|
+
let checkoutDiffWatcherCount = 0;
|
|
336
|
+
let checkoutDiffFallbackRefreshTargetCount = 0;
|
|
337
|
+
for (const target of this.checkoutDiffTargets.values()) {
|
|
338
|
+
checkoutDiffWatcherCount += target.watchers.length;
|
|
339
|
+
if (target.fallbackRefreshInterval) {
|
|
340
|
+
checkoutDiffFallbackRefreshTargetCount += 1;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
return {
|
|
344
|
+
checkoutDiffTargetCount: this.checkoutDiffTargets.size,
|
|
345
|
+
checkoutDiffSubscriptionCount: this.checkoutDiffSubscriptions.size,
|
|
346
|
+
checkoutDiffWatcherCount,
|
|
347
|
+
checkoutDiffFallbackRefreshTargetCount,
|
|
348
|
+
terminalDirectorySubscriptionCount: this.subscribedTerminalDirectories.size,
|
|
349
|
+
terminalSubscriptionCount: this.terminalSubscriptions.size,
|
|
350
|
+
terminalStreamCount: this.terminalStreams.size,
|
|
351
|
+
};
|
|
352
|
+
}
|
|
299
353
|
/**
|
|
300
354
|
* Send initial state to client after connection
|
|
301
355
|
*/
|
|
@@ -326,10 +380,11 @@ export class Session {
|
|
|
326
380
|
async interruptAgentIfRunning(agentId) {
|
|
327
381
|
const snapshot = this.agentManager.getAgent(agentId);
|
|
328
382
|
if (!snapshot) {
|
|
383
|
+
this.sessionLogger.trace({ agentId }, 'interruptAgentIfRunning: agent not found');
|
|
329
384
|
throw new Error(`Agent ${agentId} not found`);
|
|
330
385
|
}
|
|
331
386
|
if (snapshot.lifecycle !== 'running' && !snapshot.pendingRun) {
|
|
332
|
-
this.sessionLogger.
|
|
387
|
+
this.sessionLogger.trace({ agentId, lifecycle: snapshot.lifecycle, pendingRun: Boolean(snapshot.pendingRun) }, 'interruptAgentIfRunning: skipping because agent is not running');
|
|
333
388
|
return;
|
|
334
389
|
}
|
|
335
390
|
this.sessionLogger.debug({ agentId, lifecycle: snapshot.lifecycle, pendingRun: Boolean(snapshot.pendingRun) }, 'interruptAgentIfRunning: interrupting');
|
|
@@ -359,10 +414,15 @@ export class Session {
|
|
|
359
414
|
* Start streaming an agent run and forward results via the websocket broadcast
|
|
360
415
|
*/
|
|
361
416
|
startAgentStream(agentId, prompt, runOptions) {
|
|
362
|
-
this.sessionLogger.
|
|
417
|
+
this.sessionLogger.trace({
|
|
418
|
+
agentId,
|
|
419
|
+
promptType: typeof prompt === 'string' ? 'string' : 'structured',
|
|
420
|
+
hasRunOptions: Boolean(runOptions),
|
|
421
|
+
}, 'startAgentStream: requested');
|
|
363
422
|
let iterator;
|
|
364
423
|
try {
|
|
365
424
|
iterator = this.agentManager.streamAgent(agentId, prompt, runOptions);
|
|
425
|
+
this.sessionLogger.trace({ agentId }, 'startAgentStream: streamAgent returned iterator');
|
|
366
426
|
}
|
|
367
427
|
catch (error) {
|
|
368
428
|
this.handleAgentRunError(agentId, error, 'Failed to start agent run');
|
|
@@ -374,8 +434,10 @@ export class Session {
|
|
|
374
434
|
for await (const _ of iterator) {
|
|
375
435
|
// Events are forwarded via the session's AgentManager subscription.
|
|
376
436
|
}
|
|
437
|
+
this.sessionLogger.trace({ agentId }, 'startAgentStream: iterator drained');
|
|
377
438
|
}
|
|
378
439
|
catch (error) {
|
|
440
|
+
this.sessionLogger.trace({ agentId, err: error }, 'startAgentStream: iterator threw');
|
|
379
441
|
this.handleAgentRunError(agentId, error, 'Agent stream failed');
|
|
380
442
|
}
|
|
381
443
|
})();
|
|
@@ -498,7 +560,7 @@ export class Session {
|
|
|
498
560
|
}
|
|
499
561
|
async buildAgentPayload(agent) {
|
|
500
562
|
const storedRecord = await this.agentStorage.get(agent.id);
|
|
501
|
-
const title = storedRecord?.title ?? null;
|
|
563
|
+
const title = storedRecord?.title ?? storedRecord?.config?.title ?? null;
|
|
502
564
|
const payload = toAgentPayload(agent, { title });
|
|
503
565
|
payload.archivedAt = storedRecord?.archivedAt ?? null;
|
|
504
566
|
return payload;
|
|
@@ -516,12 +578,33 @@ export class Session {
|
|
|
516
578
|
const updatedAt = new Date(record.lastActivityAt ?? record.updatedAt);
|
|
517
579
|
const lastUserMessageAt = record.lastUserMessageAt ? new Date(record.lastUserMessageAt) : null;
|
|
518
580
|
const provider = coerceAgentProvider(this.sessionLogger, record.provider, record.id);
|
|
581
|
+
const runtimeInfo = record.runtimeInfo
|
|
582
|
+
? {
|
|
583
|
+
provider: coerceAgentProvider(this.sessionLogger, record.runtimeInfo.provider, record.id),
|
|
584
|
+
sessionId: record.runtimeInfo.sessionId,
|
|
585
|
+
...(Object.prototype.hasOwnProperty.call(record.runtimeInfo, 'model')
|
|
586
|
+
? { model: record.runtimeInfo.model ?? null }
|
|
587
|
+
: {}),
|
|
588
|
+
...(Object.prototype.hasOwnProperty.call(record.runtimeInfo, 'thinkingOptionId')
|
|
589
|
+
? { thinkingOptionId: record.runtimeInfo.thinkingOptionId ?? null }
|
|
590
|
+
: {}),
|
|
591
|
+
...(Object.prototype.hasOwnProperty.call(record.runtimeInfo, 'modeId')
|
|
592
|
+
? { modeId: record.runtimeInfo.modeId ?? null }
|
|
593
|
+
: {}),
|
|
594
|
+
...(record.runtimeInfo.extra ? { extra: record.runtimeInfo.extra } : {}),
|
|
595
|
+
}
|
|
596
|
+
: undefined;
|
|
519
597
|
return {
|
|
520
598
|
id: record.id,
|
|
521
599
|
provider,
|
|
522
600
|
cwd: record.cwd,
|
|
523
601
|
model: record.config?.model ?? null,
|
|
524
602
|
thinkingOptionId: record.config?.thinkingOptionId ?? null,
|
|
603
|
+
effectiveThinkingOptionId: resolveEffectiveThinkingOptionId({
|
|
604
|
+
runtimeInfo,
|
|
605
|
+
configuredThinkingOptionId: record.config?.thinkingOptionId ?? null,
|
|
606
|
+
}),
|
|
607
|
+
...(runtimeInfo ? { runtimeInfo } : {}),
|
|
525
608
|
createdAt: createdAt.toISOString(),
|
|
526
609
|
updatedAt: updatedAt.toISOString(),
|
|
527
610
|
lastUserMessageAt: lastUserMessageAt ? lastUserMessageAt.toISOString() : null,
|
|
@@ -533,7 +616,7 @@ export class Session {
|
|
|
533
616
|
persistence: toAgentPersistenceHandle(this.sessionLogger, record.persistence),
|
|
534
617
|
lastUsage: undefined,
|
|
535
618
|
lastError: undefined,
|
|
536
|
-
title: record.title ?? null,
|
|
619
|
+
title: record.title ?? record.config?.title ?? null,
|
|
537
620
|
requiresAttention: record.requiresAttention ?? false,
|
|
538
621
|
attentionReason: record.attentionReason ?? null,
|
|
539
622
|
attentionTimestamp: record.attentionTimestamp ?? null,
|
|
@@ -592,6 +675,19 @@ export class Session {
|
|
|
592
675
|
if (!includeArchived && agent.archivedAt) {
|
|
593
676
|
return false;
|
|
594
677
|
}
|
|
678
|
+
if (filter?.thinkingOptionId !== undefined) {
|
|
679
|
+
const expectedThinkingOptionId = resolveEffectiveThinkingOptionId({
|
|
680
|
+
configuredThinkingOptionId: filter.thinkingOptionId ?? null,
|
|
681
|
+
});
|
|
682
|
+
const resolvedThinkingOptionId = agent.effectiveThinkingOptionId ??
|
|
683
|
+
resolveEffectiveThinkingOptionId({
|
|
684
|
+
runtimeInfo: agent.runtimeInfo,
|
|
685
|
+
configuredThinkingOptionId: agent.thinkingOptionId ?? null,
|
|
686
|
+
});
|
|
687
|
+
if (resolvedThinkingOptionId !== expectedThinkingOptionId) {
|
|
688
|
+
return false;
|
|
689
|
+
}
|
|
690
|
+
}
|
|
595
691
|
if (filter?.statuses && filter.statuses.length > 0) {
|
|
596
692
|
const statuses = new Set(filter.statuses);
|
|
597
693
|
if (!statuses.has(agent.status)) {
|
|
@@ -689,6 +785,8 @@ export class Session {
|
|
|
689
785
|
const projectKey = deriveProjectGroupingKey({
|
|
690
786
|
cwd,
|
|
691
787
|
remoteUrl: checkout.remoteUrl,
|
|
788
|
+
isPaseoOwnedWorktree: checkout.isPaseoOwnedWorktree,
|
|
789
|
+
mainRepoRoot: checkout.mainRepoRoot,
|
|
692
790
|
});
|
|
693
791
|
return {
|
|
694
792
|
projectKey,
|
|
@@ -699,28 +797,29 @@ export class Session {
|
|
|
699
797
|
async forwardAgentUpdate(agent) {
|
|
700
798
|
try {
|
|
701
799
|
const subscription = this.agentUpdatesSubscription;
|
|
702
|
-
if (!subscription) {
|
|
703
|
-
return;
|
|
704
|
-
}
|
|
705
800
|
const payload = await this.buildAgentPayload(agent);
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
project,
|
|
710
|
-
filter: subscription.filter,
|
|
711
|
-
});
|
|
712
|
-
if (matches) {
|
|
713
|
-
this.bufferOrEmitAgentUpdate(subscription, {
|
|
714
|
-
kind: 'upsert',
|
|
801
|
+
if (subscription) {
|
|
802
|
+
const project = await this.buildProjectPlacement(payload.cwd);
|
|
803
|
+
const matches = this.matchesAgentFilter({
|
|
715
804
|
agent: payload,
|
|
716
805
|
project,
|
|
806
|
+
filter: subscription.filter,
|
|
717
807
|
});
|
|
718
|
-
|
|
808
|
+
if (matches) {
|
|
809
|
+
this.bufferOrEmitAgentUpdate(subscription, {
|
|
810
|
+
kind: 'upsert',
|
|
811
|
+
agent: payload,
|
|
812
|
+
project,
|
|
813
|
+
});
|
|
814
|
+
}
|
|
815
|
+
else {
|
|
816
|
+
this.bufferOrEmitAgentUpdate(subscription, {
|
|
817
|
+
kind: 'remove',
|
|
818
|
+
agentId: payload.id,
|
|
819
|
+
});
|
|
820
|
+
}
|
|
719
821
|
}
|
|
720
|
-
this.
|
|
721
|
-
kind: 'remove',
|
|
722
|
-
agentId: payload.id,
|
|
723
|
-
});
|
|
822
|
+
await this.emitWorkspaceUpdateForCwd(payload.cwd);
|
|
724
823
|
}
|
|
725
824
|
catch (error) {
|
|
726
825
|
this.sessionLogger.error({ err: error }, 'Failed to emit agent update');
|
|
@@ -744,6 +843,9 @@ export class Session {
|
|
|
744
843
|
case 'fetch_agents_request':
|
|
745
844
|
await this.handleFetchAgents(msg);
|
|
746
845
|
break;
|
|
846
|
+
case 'fetch_workspaces_request':
|
|
847
|
+
await this.handleFetchWorkspacesRequest(msg);
|
|
848
|
+
break;
|
|
747
849
|
case 'fetch_agent_request':
|
|
748
850
|
await this.handleFetchAgent(msg.agentId, msg.requestId);
|
|
749
851
|
break;
|
|
@@ -813,6 +915,9 @@ export class Session {
|
|
|
813
915
|
case 'restart_server_request':
|
|
814
916
|
await this.handleRestartServerRequest(msg.requestId, msg.reason);
|
|
815
917
|
break;
|
|
918
|
+
case 'shutdown_server_request':
|
|
919
|
+
await this.handleShutdownServerRequest(msg.requestId);
|
|
920
|
+
break;
|
|
816
921
|
case 'fetch_agent_timeline_request':
|
|
817
922
|
await this.handleFetchAgentTimelineRequest(msg);
|
|
818
923
|
break;
|
|
@@ -1032,11 +1137,6 @@ export class Session {
|
|
|
1032
1137
|
this.sessionLogger.warn({ streamId: frame.streamId, messageType: frame.messageType }, 'Unhandled terminal binary frame');
|
|
1033
1138
|
}
|
|
1034
1139
|
async handleRestartServerRequest(requestId, reason) {
|
|
1035
|
-
if (restartRequested) {
|
|
1036
|
-
this.sessionLogger.debug('Restart already requested, ignoring duplicate');
|
|
1037
|
-
return;
|
|
1038
|
-
}
|
|
1039
|
-
restartRequested = true;
|
|
1040
1140
|
const payload = {
|
|
1041
1141
|
status: 'restart_requested',
|
|
1042
1142
|
clientId: this.clientId,
|
|
@@ -1050,19 +1150,45 @@ export class Session {
|
|
|
1050
1150
|
type: 'status',
|
|
1051
1151
|
payload,
|
|
1052
1152
|
});
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
})
|
|
1153
|
+
this.emitLifecycleIntent({
|
|
1154
|
+
type: 'restart',
|
|
1155
|
+
clientId: this.clientId,
|
|
1156
|
+
requestId,
|
|
1157
|
+
...(reason ? { reason } : {}),
|
|
1158
|
+
});
|
|
1159
|
+
}
|
|
1160
|
+
async handleShutdownServerRequest(requestId) {
|
|
1161
|
+
this.sessionLogger.warn('Shutdown requested via websocket');
|
|
1162
|
+
this.emit({
|
|
1163
|
+
type: 'status',
|
|
1164
|
+
payload: {
|
|
1165
|
+
status: 'shutdown_requested',
|
|
1166
|
+
clientId: this.clientId,
|
|
1167
|
+
requestId,
|
|
1168
|
+
},
|
|
1169
|
+
});
|
|
1170
|
+
this.emitLifecycleIntent({
|
|
1171
|
+
type: 'shutdown',
|
|
1172
|
+
clientId: this.clientId,
|
|
1173
|
+
requestId,
|
|
1174
|
+
});
|
|
1175
|
+
}
|
|
1176
|
+
emitLifecycleIntent(intent) {
|
|
1177
|
+
if (!this.onLifecycleIntent) {
|
|
1058
1178
|
return;
|
|
1059
1179
|
}
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
}
|
|
1180
|
+
try {
|
|
1181
|
+
this.onLifecycleIntent(intent);
|
|
1182
|
+
}
|
|
1183
|
+
catch (error) {
|
|
1184
|
+
this.sessionLogger.error({ err: error, intent }, 'Lifecycle intent handler failed');
|
|
1185
|
+
}
|
|
1063
1186
|
}
|
|
1064
1187
|
async handleDeleteAgentRequest(agentId, requestId) {
|
|
1065
1188
|
this.sessionLogger.info({ agentId }, `Deleting agent ${agentId} from registry`);
|
|
1189
|
+
const knownCwd = this.agentManager.getAgent(agentId)?.cwd ??
|
|
1190
|
+
(await this.agentStorage.get(agentId))?.cwd ??
|
|
1191
|
+
null;
|
|
1066
1192
|
// Prevent the persistence hook from re-creating the record while we close/delete.
|
|
1067
1193
|
this.agentStorage.beginDelete(agentId);
|
|
1068
1194
|
try {
|
|
@@ -1090,9 +1216,32 @@ export class Session {
|
|
|
1090
1216
|
agentId,
|
|
1091
1217
|
});
|
|
1092
1218
|
}
|
|
1219
|
+
if (knownCwd) {
|
|
1220
|
+
await this.emitWorkspaceUpdateForCwd(knownCwd);
|
|
1221
|
+
}
|
|
1093
1222
|
}
|
|
1094
1223
|
async handleArchiveAgentRequest(agentId, requestId) {
|
|
1095
1224
|
this.sessionLogger.info({ agentId }, `Archiving agent ${agentId}`);
|
|
1225
|
+
const { archivedAt, archivedRecord } = await this.archiveAgentState(agentId);
|
|
1226
|
+
this.emit({
|
|
1227
|
+
type: 'agent_archived',
|
|
1228
|
+
payload: {
|
|
1229
|
+
agentId,
|
|
1230
|
+
archivedAt,
|
|
1231
|
+
requestId,
|
|
1232
|
+
},
|
|
1233
|
+
});
|
|
1234
|
+
await this.maybeArchiveWorktreeAfterLastAgentArchived({
|
|
1235
|
+
archivedAgentId: agentId,
|
|
1236
|
+
archivedAgentCwd: archivedRecord.cwd,
|
|
1237
|
+
requestId,
|
|
1238
|
+
});
|
|
1239
|
+
}
|
|
1240
|
+
async archiveAgentState(agentId) {
|
|
1241
|
+
if (this.agentManager.getAgent(agentId)) {
|
|
1242
|
+
await this.interruptAgentIfRunning(agentId);
|
|
1243
|
+
await this.agentManager.clearAgentAttention(agentId).catch(() => undefined);
|
|
1244
|
+
}
|
|
1096
1245
|
const archivedAt = new Date().toISOString();
|
|
1097
1246
|
const existing = await this.agentStorage.get(agentId);
|
|
1098
1247
|
let archivedRecord = existing;
|
|
@@ -1102,7 +1251,6 @@ export class Session {
|
|
|
1102
1251
|
throw new Error(`Agent not found: ${agentId}`);
|
|
1103
1252
|
}
|
|
1104
1253
|
await this.agentStorage.applySnapshot(liveAgent, {
|
|
1105
|
-
title: liveAgent.config.title ?? null,
|
|
1106
1254
|
internal: liveAgent.internal,
|
|
1107
1255
|
});
|
|
1108
1256
|
archivedRecord = await this.agentStorage.get(agentId);
|
|
@@ -1110,25 +1258,41 @@ export class Session {
|
|
|
1110
1258
|
throw new Error(`Agent not found in storage after snapshot: ${agentId}`);
|
|
1111
1259
|
}
|
|
1112
1260
|
}
|
|
1113
|
-
|
|
1261
|
+
const normalizedStatus = archivedRecord.lastStatus === 'running' || archivedRecord.lastStatus === 'initializing'
|
|
1262
|
+
? 'idle'
|
|
1263
|
+
: archivedRecord.lastStatus;
|
|
1264
|
+
const nextRecord = {
|
|
1114
1265
|
...archivedRecord,
|
|
1115
1266
|
archivedAt,
|
|
1267
|
+
lastStatus: normalizedStatus,
|
|
1268
|
+
requiresAttention: false,
|
|
1269
|
+
attentionReason: null,
|
|
1270
|
+
attentionTimestamp: null,
|
|
1116
1271
|
};
|
|
1117
|
-
await this.agentStorage.upsert(
|
|
1272
|
+
await this.agentStorage.upsert(nextRecord);
|
|
1118
1273
|
this.agentManager.notifyAgentState(agentId);
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
archivedAgentCwd: archivedRecord.cwd,
|
|
1130
|
-
requestId,
|
|
1274
|
+
return { archivedAt, archivedRecord: nextRecord };
|
|
1275
|
+
}
|
|
1276
|
+
async unarchiveAgentState(agentId) {
|
|
1277
|
+
const record = await this.agentStorage.get(agentId);
|
|
1278
|
+
if (!record || !record.archivedAt) {
|
|
1279
|
+
return false;
|
|
1280
|
+
}
|
|
1281
|
+
await this.agentStorage.upsert({
|
|
1282
|
+
...record,
|
|
1283
|
+
archivedAt: null,
|
|
1131
1284
|
});
|
|
1285
|
+
this.agentManager.notifyAgentState(agentId);
|
|
1286
|
+
return true;
|
|
1287
|
+
}
|
|
1288
|
+
async unarchiveAgentByHandle(handle) {
|
|
1289
|
+
const records = await this.agentStorage.list();
|
|
1290
|
+
const matched = records.find((record) => record.persistence?.provider === handle.provider &&
|
|
1291
|
+
record.persistence?.sessionId === handle.sessionId);
|
|
1292
|
+
if (!matched) {
|
|
1293
|
+
return;
|
|
1294
|
+
}
|
|
1295
|
+
await this.unarchiveAgentState(matched.id);
|
|
1132
1296
|
}
|
|
1133
1297
|
async handleUpdateAgentRequest(agentId, name, labels, requestId) {
|
|
1134
1298
|
this.sessionLogger.info({
|
|
@@ -1601,6 +1765,7 @@ export class Session {
|
|
|
1601
1765
|
*/
|
|
1602
1766
|
async handleSendAgentMessage(agentId, text, messageId, images, runOptions) {
|
|
1603
1767
|
this.sessionLogger.info({ agentId, textPreview: text.substring(0, 50), imageCount: images?.length ?? 0 }, `Sending text to agent ${agentId}${images && images.length > 0 ? ` with ${images.length} image attachment(s)` : ''}`);
|
|
1768
|
+
await this.unarchiveAgentState(agentId);
|
|
1604
1769
|
try {
|
|
1605
1770
|
await this.ensureAgentLoaded(agentId);
|
|
1606
1771
|
}
|
|
@@ -1631,27 +1796,47 @@ export class Session {
|
|
|
1631
1796
|
* Handle create agent request
|
|
1632
1797
|
*/
|
|
1633
1798
|
async handleCreateAgentRequest(msg) {
|
|
1634
|
-
const { config, worktreeName, requestId, initialPrompt, outputSchema, git, images, labels } = msg;
|
|
1799
|
+
const { config, worktreeName, requestId, initialPrompt, clientMessageId, outputSchema, git, images, labels, } = msg;
|
|
1635
1800
|
this.sessionLogger.info({ cwd: config.cwd, provider: config.provider, worktreeName }, `Creating agent in ${config.cwd} (${config.provider})${worktreeName ? ` with worktree ${worktreeName}` : ''}`);
|
|
1636
1801
|
try {
|
|
1637
|
-
const
|
|
1802
|
+
const trimmedPrompt = initialPrompt?.trim();
|
|
1803
|
+
const { explicitTitle, provisionalTitle } = resolveCreateAgentTitles({
|
|
1804
|
+
configTitle: config.title,
|
|
1805
|
+
initialPrompt: trimmedPrompt,
|
|
1806
|
+
});
|
|
1807
|
+
const resolvedConfig = {
|
|
1808
|
+
...config,
|
|
1809
|
+
...(provisionalTitle ? { title: provisionalTitle } : {}),
|
|
1810
|
+
};
|
|
1811
|
+
const { sessionConfig, worktreeConfig } = await this.buildAgentSessionConfig(resolvedConfig, git, worktreeName, labels);
|
|
1638
1812
|
const snapshot = await this.agentManager.createAgent(sessionConfig, undefined, { labels });
|
|
1639
1813
|
await this.forwardAgentUpdate(snapshot);
|
|
1640
|
-
|
|
1814
|
+
if (requestId) {
|
|
1815
|
+
const agentPayload = await this.getAgentPayloadById(snapshot.id);
|
|
1816
|
+
if (!agentPayload) {
|
|
1817
|
+
throw new Error(`Agent ${snapshot.id} not found after creation`);
|
|
1818
|
+
}
|
|
1819
|
+
this.emit({
|
|
1820
|
+
type: 'status',
|
|
1821
|
+
payload: {
|
|
1822
|
+
status: 'agent_created',
|
|
1823
|
+
agentId: snapshot.id,
|
|
1824
|
+
requestId,
|
|
1825
|
+
agent: agentPayload,
|
|
1826
|
+
},
|
|
1827
|
+
});
|
|
1828
|
+
}
|
|
1641
1829
|
if (trimmedPrompt) {
|
|
1642
1830
|
scheduleAgentMetadataGeneration({
|
|
1643
1831
|
agentManager: this.agentManager,
|
|
1644
1832
|
agentId: snapshot.id,
|
|
1645
1833
|
cwd: snapshot.cwd,
|
|
1646
1834
|
initialPrompt: trimmedPrompt,
|
|
1647
|
-
explicitTitle
|
|
1835
|
+
explicitTitle,
|
|
1648
1836
|
paseoHome: this.paseoHome,
|
|
1649
1837
|
logger: this.sessionLogger,
|
|
1650
1838
|
});
|
|
1651
|
-
|
|
1652
|
-
await this.handleSendAgentMessage(snapshot.id, trimmedPrompt, uuidv4(), images, outputSchema ? { outputSchema } : undefined);
|
|
1653
|
-
}
|
|
1654
|
-
catch (promptError) {
|
|
1839
|
+
void this.handleSendAgentMessage(snapshot.id, trimmedPrompt, resolveClientMessageId(clientMessageId), images, outputSchema ? { outputSchema } : undefined).catch((promptError) => {
|
|
1655
1840
|
this.sessionLogger.error({ err: promptError, agentId: snapshot.id }, `Failed to run initial prompt for agent ${snapshot.id}`);
|
|
1656
1841
|
this.emit({
|
|
1657
1842
|
type: 'activity_log',
|
|
@@ -1662,21 +1847,6 @@ export class Session {
|
|
|
1662
1847
|
content: `Initial prompt failed: ${promptError?.message ?? promptError}`,
|
|
1663
1848
|
},
|
|
1664
1849
|
});
|
|
1665
|
-
}
|
|
1666
|
-
}
|
|
1667
|
-
if (requestId) {
|
|
1668
|
-
const agentPayload = await this.getAgentPayloadById(snapshot.id);
|
|
1669
|
-
if (!agentPayload) {
|
|
1670
|
-
throw new Error(`Agent ${snapshot.id} not found after creation`);
|
|
1671
|
-
}
|
|
1672
|
-
this.emit({
|
|
1673
|
-
type: 'status',
|
|
1674
|
-
payload: {
|
|
1675
|
-
status: 'agent_created',
|
|
1676
|
-
agentId: snapshot.id,
|
|
1677
|
-
requestId,
|
|
1678
|
-
agent: agentPayload,
|
|
1679
|
-
},
|
|
1680
1850
|
});
|
|
1681
1851
|
}
|
|
1682
1852
|
if (worktreeConfig) {
|
|
@@ -1739,7 +1909,9 @@ export class Session {
|
|
|
1739
1909
|
}
|
|
1740
1910
|
this.sessionLogger.info({ sessionId: handle.sessionId, provider: handle.provider }, `Resuming agent ${handle.sessionId} (${handle.provider})`);
|
|
1741
1911
|
try {
|
|
1912
|
+
await this.unarchiveAgentByHandle(handle);
|
|
1742
1913
|
const snapshot = await this.agentManager.resumeAgentFromPersistence(handle, overrides);
|
|
1914
|
+
await this.unarchiveAgentState(snapshot.id);
|
|
1743
1915
|
await this.agentManager.hydrateTimelineFromProvider(snapshot.id);
|
|
1744
1916
|
await this.forwardAgentUpdate(snapshot);
|
|
1745
1917
|
const timelineSize = this.agentManager.getTimeline(snapshot.id).length;
|
|
@@ -1777,6 +1949,7 @@ export class Session {
|
|
|
1777
1949
|
const { agentId, requestId } = msg;
|
|
1778
1950
|
this.sessionLogger.info({ agentId }, `Refreshing agent ${agentId} from persistence`);
|
|
1779
1951
|
try {
|
|
1952
|
+
await this.unarchiveAgentState(agentId);
|
|
1780
1953
|
let snapshot;
|
|
1781
1954
|
const existing = this.agentManager.getAgent(agentId);
|
|
1782
1955
|
if (existing) {
|
|
@@ -2400,7 +2573,6 @@ export class Session {
|
|
|
2400
2573
|
*/
|
|
2401
2574
|
async handleClearAgentAttention(agentId) {
|
|
2402
2575
|
const agentIds = Array.isArray(agentId) ? agentId : [agentId];
|
|
2403
|
-
this.sessionLogger.debug({ agentIds }, `Clearing attention for ${agentIds.length} agent(s): ${agentIds.join(', ')}`);
|
|
2404
2576
|
try {
|
|
2405
2577
|
await Promise.all(agentIds.map((id) => this.agentManager.clearAgentAttention(id)));
|
|
2406
2578
|
}
|
|
@@ -3277,6 +3449,7 @@ export class Session {
|
|
|
3277
3449
|
payload: {
|
|
3278
3450
|
worktrees: worktrees.map((entry) => ({
|
|
3279
3451
|
worktreePath: entry.path,
|
|
3452
|
+
createdAt: entry.createdAt,
|
|
3280
3453
|
branchName: entry.branchName ?? null,
|
|
3281
3454
|
head: entry.head ?? null,
|
|
3282
3455
|
})),
|
|
@@ -3358,10 +3531,12 @@ export class Session {
|
|
|
3358
3531
|
targetPath = resolvedWorktree.worktreePath;
|
|
3359
3532
|
}
|
|
3360
3533
|
const removedAgents = new Set();
|
|
3534
|
+
const affectedWorkspaceCwds = new Set([targetPath]);
|
|
3361
3535
|
const agents = this.agentManager.listAgents();
|
|
3362
3536
|
for (const agent of agents) {
|
|
3363
3537
|
if (this.isPathWithinRoot(targetPath, agent.cwd)) {
|
|
3364
3538
|
removedAgents.add(agent.id);
|
|
3539
|
+
affectedWorkspaceCwds.add(agent.cwd);
|
|
3365
3540
|
try {
|
|
3366
3541
|
await this.agentManager.closeAgent(agent.id);
|
|
3367
3542
|
}
|
|
@@ -3380,6 +3555,7 @@ export class Session {
|
|
|
3380
3555
|
for (const record of registryRecords) {
|
|
3381
3556
|
if (this.isPathWithinRoot(targetPath, record.cwd)) {
|
|
3382
3557
|
removedAgents.add(record.id);
|
|
3558
|
+
affectedWorkspaceCwds.add(record.cwd);
|
|
3383
3559
|
try {
|
|
3384
3560
|
await this.agentStorage.remove(record.id);
|
|
3385
3561
|
}
|
|
@@ -3403,6 +3579,7 @@ export class Session {
|
|
|
3403
3579
|
},
|
|
3404
3580
|
});
|
|
3405
3581
|
}
|
|
3582
|
+
await this.emitWorkspaceUpdatesForCwds(affectedWorkspaceCwds);
|
|
3406
3583
|
return Array.from(removedAgents);
|
|
3407
3584
|
}
|
|
3408
3585
|
async handlePaseoWorktreeArchiveRequest(msg) {
|
|
@@ -3471,38 +3648,36 @@ export class Session {
|
|
|
3471
3648
|
}
|
|
3472
3649
|
}
|
|
3473
3650
|
/**
|
|
3474
|
-
* Handle read-only file explorer requests scoped to
|
|
3651
|
+
* Handle read-only file explorer requests scoped to a workspace cwd
|
|
3475
3652
|
*/
|
|
3476
3653
|
async handleFileExplorerRequest(request) {
|
|
3477
|
-
const {
|
|
3478
|
-
|
|
3654
|
+
const { cwd: workspaceCwd, path: requestedPath = '.', mode, requestId } = request;
|
|
3655
|
+
const cwd = workspaceCwd.trim();
|
|
3656
|
+
if (!cwd) {
|
|
3657
|
+
this.emit({
|
|
3658
|
+
type: 'file_explorer_response',
|
|
3659
|
+
payload: {
|
|
3660
|
+
cwd: workspaceCwd,
|
|
3661
|
+
path: requestedPath,
|
|
3662
|
+
mode,
|
|
3663
|
+
directory: null,
|
|
3664
|
+
file: null,
|
|
3665
|
+
error: 'cwd is required',
|
|
3666
|
+
requestId,
|
|
3667
|
+
},
|
|
3668
|
+
});
|
|
3669
|
+
return;
|
|
3670
|
+
}
|
|
3479
3671
|
try {
|
|
3480
|
-
const agents = this.agentManager.listAgents();
|
|
3481
|
-
const agent = agents.find((a) => a.id === agentId);
|
|
3482
|
-
if (!agent) {
|
|
3483
|
-
this.emit({
|
|
3484
|
-
type: 'file_explorer_response',
|
|
3485
|
-
payload: {
|
|
3486
|
-
agentId,
|
|
3487
|
-
path: requestedPath,
|
|
3488
|
-
mode,
|
|
3489
|
-
directory: null,
|
|
3490
|
-
file: null,
|
|
3491
|
-
error: `Agent not found: ${agentId}`,
|
|
3492
|
-
requestId,
|
|
3493
|
-
},
|
|
3494
|
-
});
|
|
3495
|
-
return;
|
|
3496
|
-
}
|
|
3497
3672
|
if (mode === 'list') {
|
|
3498
3673
|
const directory = await listDirectoryEntries({
|
|
3499
|
-
root:
|
|
3674
|
+
root: cwd,
|
|
3500
3675
|
relativePath: requestedPath,
|
|
3501
3676
|
});
|
|
3502
3677
|
this.emit({
|
|
3503
3678
|
type: 'file_explorer_response',
|
|
3504
3679
|
payload: {
|
|
3505
|
-
|
|
3680
|
+
cwd,
|
|
3506
3681
|
path: directory.path,
|
|
3507
3682
|
mode,
|
|
3508
3683
|
directory,
|
|
@@ -3514,13 +3689,13 @@ export class Session {
|
|
|
3514
3689
|
}
|
|
3515
3690
|
else {
|
|
3516
3691
|
const file = await readExplorerFile({
|
|
3517
|
-
root:
|
|
3692
|
+
root: cwd,
|
|
3518
3693
|
relativePath: requestedPath,
|
|
3519
3694
|
});
|
|
3520
3695
|
this.emit({
|
|
3521
3696
|
type: 'file_explorer_response',
|
|
3522
3697
|
payload: {
|
|
3523
|
-
|
|
3698
|
+
cwd,
|
|
3524
3699
|
path: file.path,
|
|
3525
3700
|
mode,
|
|
3526
3701
|
directory: null,
|
|
@@ -3532,11 +3707,11 @@ export class Session {
|
|
|
3532
3707
|
}
|
|
3533
3708
|
}
|
|
3534
3709
|
catch (error) {
|
|
3535
|
-
this.sessionLogger.error({ err: error,
|
|
3710
|
+
this.sessionLogger.error({ err: error, cwd, path: requestedPath }, `Failed to fulfill file explorer request for workspace ${cwd}`);
|
|
3536
3711
|
this.emit({
|
|
3537
3712
|
type: 'file_explorer_response',
|
|
3538
3713
|
payload: {
|
|
3539
|
-
|
|
3714
|
+
cwd,
|
|
3540
3715
|
path: requestedPath,
|
|
3541
3716
|
mode,
|
|
3542
3717
|
directory: null,
|
|
@@ -3577,36 +3752,34 @@ export class Session {
|
|
|
3577
3752
|
}
|
|
3578
3753
|
}
|
|
3579
3754
|
/**
|
|
3580
|
-
* Handle file download token request scoped to
|
|
3755
|
+
* Handle file download token request scoped to a workspace cwd
|
|
3581
3756
|
*/
|
|
3582
3757
|
async handleFileDownloadTokenRequest(request) {
|
|
3583
|
-
const {
|
|
3584
|
-
|
|
3758
|
+
const { cwd: workspaceCwd, path: requestedPath, requestId } = request;
|
|
3759
|
+
const cwd = workspaceCwd.trim();
|
|
3760
|
+
if (!cwd) {
|
|
3761
|
+
this.emit({
|
|
3762
|
+
type: 'file_download_token_response',
|
|
3763
|
+
payload: {
|
|
3764
|
+
cwd: workspaceCwd,
|
|
3765
|
+
path: requestedPath,
|
|
3766
|
+
token: null,
|
|
3767
|
+
fileName: null,
|
|
3768
|
+
mimeType: null,
|
|
3769
|
+
size: null,
|
|
3770
|
+
error: 'cwd is required',
|
|
3771
|
+
requestId,
|
|
3772
|
+
},
|
|
3773
|
+
});
|
|
3774
|
+
return;
|
|
3775
|
+
}
|
|
3776
|
+
this.sessionLogger.debug({ cwd, path: requestedPath }, `Handling file download token request for workspace ${cwd} (${requestedPath})`);
|
|
3585
3777
|
try {
|
|
3586
|
-
const agents = this.agentManager.listAgents();
|
|
3587
|
-
const agent = agents.find((a) => a.id === agentId);
|
|
3588
|
-
if (!agent) {
|
|
3589
|
-
this.emit({
|
|
3590
|
-
type: 'file_download_token_response',
|
|
3591
|
-
payload: {
|
|
3592
|
-
agentId,
|
|
3593
|
-
path: requestedPath,
|
|
3594
|
-
token: null,
|
|
3595
|
-
fileName: null,
|
|
3596
|
-
mimeType: null,
|
|
3597
|
-
size: null,
|
|
3598
|
-
error: `Agent not found: ${agentId}`,
|
|
3599
|
-
requestId,
|
|
3600
|
-
},
|
|
3601
|
-
});
|
|
3602
|
-
return;
|
|
3603
|
-
}
|
|
3604
3778
|
const info = await getDownloadableFileInfo({
|
|
3605
|
-
root:
|
|
3779
|
+
root: cwd,
|
|
3606
3780
|
relativePath: requestedPath,
|
|
3607
3781
|
});
|
|
3608
3782
|
const entry = this.downloadTokenStore.issueToken({
|
|
3609
|
-
agentId,
|
|
3610
3783
|
path: info.path,
|
|
3611
3784
|
absolutePath: info.absolutePath,
|
|
3612
3785
|
fileName: info.fileName,
|
|
@@ -3616,7 +3789,7 @@ export class Session {
|
|
|
3616
3789
|
this.emit({
|
|
3617
3790
|
type: 'file_download_token_response',
|
|
3618
3791
|
payload: {
|
|
3619
|
-
|
|
3792
|
+
cwd,
|
|
3620
3793
|
path: info.path,
|
|
3621
3794
|
token: entry.token,
|
|
3622
3795
|
fileName: entry.fileName,
|
|
@@ -3628,11 +3801,11 @@ export class Session {
|
|
|
3628
3801
|
});
|
|
3629
3802
|
}
|
|
3630
3803
|
catch (error) {
|
|
3631
|
-
this.sessionLogger.error({ err: error,
|
|
3804
|
+
this.sessionLogger.error({ err: error, cwd, path: requestedPath }, `Failed to issue download token for workspace ${cwd}`);
|
|
3632
3805
|
this.emit({
|
|
3633
3806
|
type: 'file_download_token_response',
|
|
3634
3807
|
payload: {
|
|
3635
|
-
|
|
3808
|
+
cwd,
|
|
3636
3809
|
path: requestedPath,
|
|
3637
3810
|
token: null,
|
|
3638
3811
|
fileName: null,
|
|
@@ -3739,9 +3912,9 @@ export class Session {
|
|
|
3739
3912
|
return deduped.length > 0 ? deduped : fallback;
|
|
3740
3913
|
}
|
|
3741
3914
|
getStatusPriority(agent) {
|
|
3742
|
-
const requiresAttention = agent.requiresAttention ?? false;
|
|
3743
3915
|
const attentionReason = agent.attentionReason ?? null;
|
|
3744
|
-
|
|
3916
|
+
const hasPendingPermission = (agent.pendingPermissions?.length ?? 0) > 0;
|
|
3917
|
+
if (hasPendingPermission || attentionReason === 'permission') {
|
|
3745
3918
|
return 0;
|
|
3746
3919
|
}
|
|
3747
3920
|
if (agent.status === 'error' || attentionReason === 'error') {
|
|
@@ -3767,6 +3940,18 @@ export class Session {
|
|
|
3767
3940
|
return entry.agent.title?.toLocaleLowerCase() ?? '';
|
|
3768
3941
|
}
|
|
3769
3942
|
}
|
|
3943
|
+
getFetchAgentsSortValueFromAgent(agent, key) {
|
|
3944
|
+
switch (key) {
|
|
3945
|
+
case 'status_priority':
|
|
3946
|
+
return this.getStatusPriority(agent);
|
|
3947
|
+
case 'created_at':
|
|
3948
|
+
return Date.parse(agent.createdAt);
|
|
3949
|
+
case 'updated_at':
|
|
3950
|
+
return Date.parse(agent.updatedAt);
|
|
3951
|
+
case 'title':
|
|
3952
|
+
return agent.title?.toLocaleLowerCase() ?? '';
|
|
3953
|
+
}
|
|
3954
|
+
}
|
|
3770
3955
|
compareSortValues(left, right) {
|
|
3771
3956
|
if (left === right) {
|
|
3772
3957
|
return 0;
|
|
@@ -3782,17 +3967,17 @@ export class Session {
|
|
|
3782
3967
|
}
|
|
3783
3968
|
return String(left).localeCompare(String(right));
|
|
3784
3969
|
}
|
|
3785
|
-
|
|
3970
|
+
compareFetchAgentsAgents(left, right, sort) {
|
|
3786
3971
|
for (const spec of sort) {
|
|
3787
|
-
const leftValue = this.
|
|
3788
|
-
const rightValue = this.
|
|
3972
|
+
const leftValue = this.getFetchAgentsSortValueFromAgent(left, spec.key);
|
|
3973
|
+
const rightValue = this.getFetchAgentsSortValueFromAgent(right, spec.key);
|
|
3789
3974
|
const base = this.compareSortValues(leftValue, rightValue);
|
|
3790
3975
|
if (base === 0) {
|
|
3791
3976
|
continue;
|
|
3792
3977
|
}
|
|
3793
3978
|
return spec.direction === 'asc' ? base : -base;
|
|
3794
3979
|
}
|
|
3795
|
-
return left.
|
|
3980
|
+
return left.id.localeCompare(right.id);
|
|
3796
3981
|
}
|
|
3797
3982
|
encodeFetchAgentsCursor(entry, sort) {
|
|
3798
3983
|
const values = {};
|
|
@@ -3852,9 +4037,9 @@ export class Session {
|
|
|
3852
4037
|
id: payload.id,
|
|
3853
4038
|
};
|
|
3854
4039
|
}
|
|
3855
|
-
|
|
4040
|
+
compareAgentWithCursor(agent, cursor, sort) {
|
|
3856
4041
|
for (const spec of sort) {
|
|
3857
|
-
const leftValue = this.
|
|
4042
|
+
const leftValue = this.getFetchAgentsSortValueFromAgent(agent, spec.key);
|
|
3858
4043
|
const rightValue = cursor.values[spec.key] !== undefined ? (cursor.values[spec.key] ?? null) : null;
|
|
3859
4044
|
const base = this.compareSortValues(leftValue, rightValue);
|
|
3860
4045
|
if (base === 0) {
|
|
@@ -3862,7 +4047,7 @@ export class Session {
|
|
|
3862
4047
|
}
|
|
3863
4048
|
return spec.direction === 'asc' ? base : -base;
|
|
3864
4049
|
}
|
|
3865
|
-
return
|
|
4050
|
+
return agent.id.localeCompare(cursor.id);
|
|
3866
4051
|
}
|
|
3867
4052
|
async listFetchAgentsEntries(request) {
|
|
3868
4053
|
const filter = request.filter;
|
|
@@ -3880,26 +4065,293 @@ export class Session {
|
|
|
3880
4065
|
placementByCwd.set(cwd, placementPromise);
|
|
3881
4066
|
return placementPromise;
|
|
3882
4067
|
};
|
|
3883
|
-
let
|
|
3884
|
-
|
|
3885
|
-
project: await getPlacement(agent.cwd),
|
|
3886
|
-
})));
|
|
3887
|
-
entries = entries.filter((entry) => this.matchesAgentFilter({
|
|
3888
|
-
agent: entry.agent,
|
|
3889
|
-
project: entry.project,
|
|
3890
|
-
filter,
|
|
3891
|
-
}));
|
|
3892
|
-
entries.sort((left, right) => this.compareFetchAgentsEntries(left, right, sort));
|
|
4068
|
+
let candidates = [...agents];
|
|
4069
|
+
candidates.sort((left, right) => this.compareFetchAgentsAgents(left, right, sort));
|
|
3893
4070
|
const cursorToken = request.page?.cursor;
|
|
3894
4071
|
if (cursorToken) {
|
|
3895
4072
|
const cursor = this.decodeFetchAgentsCursor(cursorToken, sort);
|
|
3896
|
-
|
|
4073
|
+
candidates = candidates.filter((agent) => this.compareAgentWithCursor(agent, cursor, sort) > 0);
|
|
4074
|
+
}
|
|
4075
|
+
const limit = request.page?.limit ?? 200;
|
|
4076
|
+
const matchedEntries = [];
|
|
4077
|
+
const batchSize = 25;
|
|
4078
|
+
for (let start = 0; start < candidates.length && matchedEntries.length <= limit; start += batchSize) {
|
|
4079
|
+
const batch = candidates.slice(start, start + batchSize);
|
|
4080
|
+
const batchEntries = await Promise.all(batch.map(async (agent) => ({
|
|
4081
|
+
agent,
|
|
4082
|
+
project: await getPlacement(agent.cwd),
|
|
4083
|
+
})));
|
|
4084
|
+
for (const entry of batchEntries) {
|
|
4085
|
+
if (!this.matchesAgentFilter({
|
|
4086
|
+
agent: entry.agent,
|
|
4087
|
+
project: entry.project,
|
|
4088
|
+
filter,
|
|
4089
|
+
})) {
|
|
4090
|
+
continue;
|
|
4091
|
+
}
|
|
4092
|
+
matchedEntries.push(entry);
|
|
4093
|
+
if (matchedEntries.length > limit) {
|
|
4094
|
+
break;
|
|
4095
|
+
}
|
|
4096
|
+
}
|
|
4097
|
+
}
|
|
4098
|
+
const pagedEntries = matchedEntries.slice(0, limit);
|
|
4099
|
+
const hasMore = matchedEntries.length > limit;
|
|
4100
|
+
const nextCursor = hasMore && pagedEntries.length > 0
|
|
4101
|
+
? this.encodeFetchAgentsCursor(pagedEntries[pagedEntries.length - 1], sort)
|
|
4102
|
+
: null;
|
|
4103
|
+
return {
|
|
4104
|
+
entries: pagedEntries,
|
|
4105
|
+
pageInfo: {
|
|
4106
|
+
nextCursor,
|
|
4107
|
+
prevCursor: request.page?.cursor ?? null,
|
|
4108
|
+
hasMore,
|
|
4109
|
+
},
|
|
4110
|
+
};
|
|
4111
|
+
}
|
|
4112
|
+
normalizeWorkspaceId(cwd) {
|
|
4113
|
+
const trimmed = cwd.trim();
|
|
4114
|
+
if (!trimmed) {
|
|
4115
|
+
return cwd;
|
|
3897
4116
|
}
|
|
3898
|
-
|
|
4117
|
+
return resolve(trimmed);
|
|
4118
|
+
}
|
|
4119
|
+
deriveWorkspaceStateBucket(agent) {
|
|
4120
|
+
const pendingPermissionCount = agent.pendingPermissions?.length ?? 0;
|
|
4121
|
+
if (pendingPermissionCount > 0 || agent.attentionReason === 'permission') {
|
|
4122
|
+
return 'needs_input';
|
|
4123
|
+
}
|
|
4124
|
+
if (agent.status === 'error' || agent.attentionReason === 'error') {
|
|
4125
|
+
return 'failed';
|
|
4126
|
+
}
|
|
4127
|
+
if (agent.status === 'running' || agent.status === 'initializing') {
|
|
4128
|
+
return 'running';
|
|
4129
|
+
}
|
|
4130
|
+
if (agent.requiresAttention) {
|
|
4131
|
+
return 'attention';
|
|
4132
|
+
}
|
|
4133
|
+
return 'done';
|
|
4134
|
+
}
|
|
4135
|
+
deriveWorkspaceDirectoryName(cwd) {
|
|
4136
|
+
const normalized = cwd.replace(/\\/g, '/');
|
|
4137
|
+
const segments = normalized.split('/').filter(Boolean);
|
|
4138
|
+
return segments[segments.length - 1] ?? cwd;
|
|
4139
|
+
}
|
|
4140
|
+
deriveWorkspaceName(input) {
|
|
4141
|
+
const branch = input.checkout.currentBranch?.trim() ?? null;
|
|
4142
|
+
if (branch && branch.toUpperCase() !== 'HEAD') {
|
|
4143
|
+
return branch;
|
|
4144
|
+
}
|
|
4145
|
+
return this.deriveWorkspaceDirectoryName(input.cwd);
|
|
4146
|
+
}
|
|
4147
|
+
accumulateLatestActivityAt(current, agent) {
|
|
4148
|
+
const candidateRaw = agent.lastUserMessageAt ?? agent.updatedAt;
|
|
4149
|
+
const candidateMs = Date.parse(candidateRaw);
|
|
4150
|
+
if (Number.isNaN(candidateMs)) {
|
|
4151
|
+
return current;
|
|
4152
|
+
}
|
|
4153
|
+
if (!current) {
|
|
4154
|
+
return new Date(candidateMs).toISOString();
|
|
4155
|
+
}
|
|
4156
|
+
const currentMs = Date.parse(current);
|
|
4157
|
+
if (Number.isNaN(currentMs) || candidateMs > currentMs) {
|
|
4158
|
+
return new Date(candidateMs).toISOString();
|
|
4159
|
+
}
|
|
4160
|
+
return current;
|
|
4161
|
+
}
|
|
4162
|
+
async listWorkspaceDescriptors() {
|
|
4163
|
+
const agents = await this.listAgentPayloads();
|
|
4164
|
+
const descriptorsByWorkspaceId = new Map();
|
|
4165
|
+
const placementByWorkspaceId = new Map();
|
|
4166
|
+
const getPlacement = (workspaceCwd) => {
|
|
4167
|
+
const key = this.normalizeWorkspaceId(workspaceCwd);
|
|
4168
|
+
const existing = placementByWorkspaceId.get(key);
|
|
4169
|
+
if (existing) {
|
|
4170
|
+
return existing;
|
|
4171
|
+
}
|
|
4172
|
+
const next = this.buildProjectPlacement(workspaceCwd);
|
|
4173
|
+
placementByWorkspaceId.set(key, next);
|
|
4174
|
+
return next;
|
|
4175
|
+
};
|
|
4176
|
+
for (const agent of agents) {
|
|
4177
|
+
if (agent.archivedAt) {
|
|
4178
|
+
continue;
|
|
4179
|
+
}
|
|
4180
|
+
const workspaceId = this.normalizeWorkspaceId(agent.cwd);
|
|
4181
|
+
const placement = await getPlacement(workspaceId);
|
|
4182
|
+
const existing = descriptorsByWorkspaceId.get(workspaceId);
|
|
4183
|
+
if (!existing) {
|
|
4184
|
+
const bucket = this.deriveWorkspaceStateBucket(agent);
|
|
4185
|
+
descriptorsByWorkspaceId.set(workspaceId, {
|
|
4186
|
+
id: workspaceId,
|
|
4187
|
+
projectId: placement.projectKey,
|
|
4188
|
+
name: this.deriveWorkspaceName({
|
|
4189
|
+
cwd: workspaceId,
|
|
4190
|
+
checkout: placement.checkout,
|
|
4191
|
+
}),
|
|
4192
|
+
status: bucket,
|
|
4193
|
+
activityAt: this.accumulateLatestActivityAt(null, agent),
|
|
4194
|
+
});
|
|
4195
|
+
continue;
|
|
4196
|
+
}
|
|
4197
|
+
const bucket = this.deriveWorkspaceStateBucket(agent);
|
|
4198
|
+
if (this.workspaceStatePriority[bucket] < this.workspaceStatePriority[existing.status]) {
|
|
4199
|
+
existing.status = bucket;
|
|
4200
|
+
}
|
|
4201
|
+
existing.activityAt = this.accumulateLatestActivityAt(existing.activityAt, agent);
|
|
4202
|
+
}
|
|
4203
|
+
return Array.from(descriptorsByWorkspaceId.values());
|
|
4204
|
+
}
|
|
4205
|
+
normalizeFetchWorkspacesSort(sort) {
|
|
4206
|
+
const fallback = [{ key: 'activity_at', direction: 'desc' }];
|
|
4207
|
+
if (!sort || sort.length === 0) {
|
|
4208
|
+
return fallback;
|
|
4209
|
+
}
|
|
4210
|
+
const deduped = [];
|
|
4211
|
+
const seen = new Set();
|
|
4212
|
+
for (const entry of sort) {
|
|
4213
|
+
if (seen.has(entry.key)) {
|
|
4214
|
+
continue;
|
|
4215
|
+
}
|
|
4216
|
+
seen.add(entry.key);
|
|
4217
|
+
deduped.push(entry);
|
|
4218
|
+
}
|
|
4219
|
+
return deduped.length > 0 ? deduped : fallback;
|
|
4220
|
+
}
|
|
4221
|
+
getFetchWorkspacesSortValue(workspace, key) {
|
|
4222
|
+
switch (key) {
|
|
4223
|
+
case 'status_priority':
|
|
4224
|
+
return this.workspaceStatePriority[workspace.status];
|
|
4225
|
+
case 'activity_at':
|
|
4226
|
+
return workspace.activityAt ? Date.parse(workspace.activityAt) : null;
|
|
4227
|
+
case 'name':
|
|
4228
|
+
return workspace.name.toLocaleLowerCase();
|
|
4229
|
+
case 'project_id':
|
|
4230
|
+
return workspace.projectId.toLocaleLowerCase();
|
|
4231
|
+
}
|
|
4232
|
+
}
|
|
4233
|
+
compareFetchWorkspacesEntries(left, right, sort) {
|
|
4234
|
+
for (const spec of sort) {
|
|
4235
|
+
const leftValue = this.getFetchWorkspacesSortValue(left, spec.key);
|
|
4236
|
+
const rightValue = this.getFetchWorkspacesSortValue(right, spec.key);
|
|
4237
|
+
const base = this.compareSortValues(leftValue, rightValue);
|
|
4238
|
+
if (base === 0) {
|
|
4239
|
+
continue;
|
|
4240
|
+
}
|
|
4241
|
+
return spec.direction === 'asc' ? base : -base;
|
|
4242
|
+
}
|
|
4243
|
+
return left.id.localeCompare(right.id);
|
|
4244
|
+
}
|
|
4245
|
+
encodeFetchWorkspacesCursor(entry, sort) {
|
|
4246
|
+
const values = {};
|
|
4247
|
+
for (const spec of sort) {
|
|
4248
|
+
values[spec.key] = this.getFetchWorkspacesSortValue(entry, spec.key);
|
|
4249
|
+
}
|
|
4250
|
+
return Buffer.from(JSON.stringify({
|
|
4251
|
+
sort,
|
|
4252
|
+
values,
|
|
4253
|
+
id: entry.id,
|
|
4254
|
+
}), 'utf8').toString('base64url');
|
|
4255
|
+
}
|
|
4256
|
+
decodeFetchWorkspacesCursor(cursor, sort) {
|
|
4257
|
+
let parsed;
|
|
4258
|
+
try {
|
|
4259
|
+
parsed = JSON.parse(Buffer.from(cursor, 'base64url').toString('utf8'));
|
|
4260
|
+
}
|
|
4261
|
+
catch {
|
|
4262
|
+
throw new SessionRequestError('invalid_cursor', 'Invalid fetch_workspaces cursor');
|
|
4263
|
+
}
|
|
4264
|
+
if (!parsed || typeof parsed !== 'object') {
|
|
4265
|
+
throw new SessionRequestError('invalid_cursor', 'Invalid fetch_workspaces cursor');
|
|
4266
|
+
}
|
|
4267
|
+
const payload = parsed;
|
|
4268
|
+
if (!Array.isArray(payload.sort) || typeof payload.id !== 'string') {
|
|
4269
|
+
throw new SessionRequestError('invalid_cursor', 'Invalid fetch_workspaces cursor');
|
|
4270
|
+
}
|
|
4271
|
+
if (!payload.values || typeof payload.values !== 'object') {
|
|
4272
|
+
throw new SessionRequestError('invalid_cursor', 'Invalid fetch_workspaces cursor');
|
|
4273
|
+
}
|
|
4274
|
+
const cursorSort = [];
|
|
4275
|
+
for (const item of payload.sort) {
|
|
4276
|
+
if (!item ||
|
|
4277
|
+
typeof item !== 'object' ||
|
|
4278
|
+
typeof item.key !== 'string' ||
|
|
4279
|
+
typeof item.direction !== 'string') {
|
|
4280
|
+
throw new SessionRequestError('invalid_cursor', 'Invalid fetch_workspaces cursor');
|
|
4281
|
+
}
|
|
4282
|
+
const key = item.key;
|
|
4283
|
+
const direction = item.direction;
|
|
4284
|
+
if ((key !== 'status_priority' &&
|
|
4285
|
+
key !== 'activity_at' &&
|
|
4286
|
+
key !== 'name' &&
|
|
4287
|
+
key !== 'project_id') ||
|
|
4288
|
+
(direction !== 'asc' && direction !== 'desc')) {
|
|
4289
|
+
throw new SessionRequestError('invalid_cursor', 'Invalid fetch_workspaces cursor');
|
|
4290
|
+
}
|
|
4291
|
+
cursorSort.push({ key, direction });
|
|
4292
|
+
}
|
|
4293
|
+
if (cursorSort.length !== sort.length ||
|
|
4294
|
+
cursorSort.some((entry, index) => entry.key !== sort[index]?.key || entry.direction !== sort[index]?.direction)) {
|
|
4295
|
+
throw new SessionRequestError('invalid_cursor', 'fetch_workspaces cursor does not match current sort');
|
|
4296
|
+
}
|
|
4297
|
+
return {
|
|
4298
|
+
sort: cursorSort,
|
|
4299
|
+
values: payload.values,
|
|
4300
|
+
id: payload.id,
|
|
4301
|
+
};
|
|
4302
|
+
}
|
|
4303
|
+
compareWorkspaceWithCursor(workspace, cursor, sort) {
|
|
4304
|
+
for (const spec of sort) {
|
|
4305
|
+
const leftValue = this.getFetchWorkspacesSortValue(workspace, spec.key);
|
|
4306
|
+
const rightValue = cursor.values[spec.key] !== undefined ? (cursor.values[spec.key] ?? null) : null;
|
|
4307
|
+
const base = this.compareSortValues(leftValue, rightValue);
|
|
4308
|
+
if (base === 0) {
|
|
4309
|
+
continue;
|
|
4310
|
+
}
|
|
4311
|
+
return spec.direction === 'asc' ? base : -base;
|
|
4312
|
+
}
|
|
4313
|
+
return workspace.id.localeCompare(cursor.id);
|
|
4314
|
+
}
|
|
4315
|
+
matchesWorkspaceFilter(input) {
|
|
4316
|
+
const { workspace, filter } = input;
|
|
4317
|
+
if (!filter) {
|
|
4318
|
+
return true;
|
|
4319
|
+
}
|
|
4320
|
+
if (filter.projectId && filter.projectId.trim().length > 0) {
|
|
4321
|
+
if (workspace.projectId !== filter.projectId.trim()) {
|
|
4322
|
+
return false;
|
|
4323
|
+
}
|
|
4324
|
+
}
|
|
4325
|
+
if (filter.idPrefix && filter.idPrefix.trim().length > 0) {
|
|
4326
|
+
if (!workspace.id.startsWith(filter.idPrefix.trim())) {
|
|
4327
|
+
return false;
|
|
4328
|
+
}
|
|
4329
|
+
}
|
|
4330
|
+
if (filter.query && filter.query.trim().length > 0) {
|
|
4331
|
+
const query = filter.query.trim().toLocaleLowerCase();
|
|
4332
|
+
const haystacks = [workspace.name, workspace.projectId, workspace.id];
|
|
4333
|
+
if (!haystacks.some((value) => value.toLocaleLowerCase().includes(query))) {
|
|
4334
|
+
return false;
|
|
4335
|
+
}
|
|
4336
|
+
}
|
|
4337
|
+
return true;
|
|
4338
|
+
}
|
|
4339
|
+
async listFetchWorkspacesEntries(request) {
|
|
4340
|
+
const filter = request.filter;
|
|
4341
|
+
const sort = this.normalizeFetchWorkspacesSort(request.sort);
|
|
4342
|
+
let entries = await this.listWorkspaceDescriptors();
|
|
4343
|
+
entries = entries.filter((workspace) => this.matchesWorkspaceFilter({ workspace, filter }));
|
|
4344
|
+
entries.sort((left, right) => this.compareFetchWorkspacesEntries(left, right, sort));
|
|
4345
|
+
const cursorToken = request.page?.cursor;
|
|
4346
|
+
if (cursorToken) {
|
|
4347
|
+
const cursor = this.decodeFetchWorkspacesCursor(cursorToken, sort);
|
|
4348
|
+
entries = entries.filter((workspace) => this.compareWorkspaceWithCursor(workspace, cursor, sort) > 0);
|
|
4349
|
+
}
|
|
4350
|
+
const limit = request.page?.limit ?? 200;
|
|
3899
4351
|
const pagedEntries = entries.slice(0, limit);
|
|
3900
4352
|
const hasMore = entries.length > limit;
|
|
3901
4353
|
const nextCursor = hasMore && pagedEntries.length > 0
|
|
3902
|
-
? this.
|
|
4354
|
+
? this.encodeFetchWorkspacesCursor(pagedEntries[pagedEntries.length - 1], sort)
|
|
3903
4355
|
: null;
|
|
3904
4356
|
return {
|
|
3905
4357
|
entries: pagedEntries,
|
|
@@ -3910,6 +4362,86 @@ export class Session {
|
|
|
3910
4362
|
},
|
|
3911
4363
|
};
|
|
3912
4364
|
}
|
|
4365
|
+
bufferOrEmitWorkspaceUpdate(subscription, payload) {
|
|
4366
|
+
if (subscription.isBootstrapping) {
|
|
4367
|
+
const workspaceId = payload.kind === 'upsert' ? payload.workspace.id : payload.id;
|
|
4368
|
+
subscription.pendingUpdatesByWorkspaceId.set(workspaceId, payload);
|
|
4369
|
+
return;
|
|
4370
|
+
}
|
|
4371
|
+
this.emit({
|
|
4372
|
+
type: 'workspace_update',
|
|
4373
|
+
payload,
|
|
4374
|
+
});
|
|
4375
|
+
}
|
|
4376
|
+
flushBootstrappedWorkspaceUpdates(options) {
|
|
4377
|
+
const subscription = this.workspaceUpdatesSubscription;
|
|
4378
|
+
if (!subscription || !subscription.isBootstrapping) {
|
|
4379
|
+
return;
|
|
4380
|
+
}
|
|
4381
|
+
subscription.isBootstrapping = false;
|
|
4382
|
+
const pending = Array.from(subscription.pendingUpdatesByWorkspaceId.values());
|
|
4383
|
+
subscription.pendingUpdatesByWorkspaceId.clear();
|
|
4384
|
+
for (const payload of pending) {
|
|
4385
|
+
if (payload.kind === 'upsert') {
|
|
4386
|
+
const snapshotLatestActivity = options?.snapshotLatestActivityByWorkspaceId?.get(payload.workspace.id);
|
|
4387
|
+
if (typeof snapshotLatestActivity === 'number') {
|
|
4388
|
+
const updateLatestActivity = payload.workspace.activityAt
|
|
4389
|
+
? Date.parse(payload.workspace.activityAt)
|
|
4390
|
+
: Number.NEGATIVE_INFINITY;
|
|
4391
|
+
if (!Number.isNaN(updateLatestActivity) && updateLatestActivity <= snapshotLatestActivity) {
|
|
4392
|
+
continue;
|
|
4393
|
+
}
|
|
4394
|
+
}
|
|
4395
|
+
}
|
|
4396
|
+
this.emit({
|
|
4397
|
+
type: 'workspace_update',
|
|
4398
|
+
payload,
|
|
4399
|
+
});
|
|
4400
|
+
}
|
|
4401
|
+
}
|
|
4402
|
+
async emitWorkspaceUpdateForCwd(cwd) {
|
|
4403
|
+
const subscription = this.workspaceUpdatesSubscription;
|
|
4404
|
+
if (!subscription) {
|
|
4405
|
+
return;
|
|
4406
|
+
}
|
|
4407
|
+
const workspaceId = this.normalizeWorkspaceId(cwd);
|
|
4408
|
+
const all = await this.listWorkspaceDescriptors();
|
|
4409
|
+
const workspace = all.find((entry) => entry.id === workspaceId);
|
|
4410
|
+
if (!workspace) {
|
|
4411
|
+
this.bufferOrEmitWorkspaceUpdate(subscription, {
|
|
4412
|
+
kind: 'remove',
|
|
4413
|
+
id: workspaceId,
|
|
4414
|
+
});
|
|
4415
|
+
return;
|
|
4416
|
+
}
|
|
4417
|
+
if (!this.matchesWorkspaceFilter({ workspace, filter: subscription.filter })) {
|
|
4418
|
+
this.bufferOrEmitWorkspaceUpdate(subscription, {
|
|
4419
|
+
kind: 'remove',
|
|
4420
|
+
id: workspaceId,
|
|
4421
|
+
});
|
|
4422
|
+
return;
|
|
4423
|
+
}
|
|
4424
|
+
this.bufferOrEmitWorkspaceUpdate(subscription, {
|
|
4425
|
+
kind: 'upsert',
|
|
4426
|
+
workspace,
|
|
4427
|
+
});
|
|
4428
|
+
}
|
|
4429
|
+
async emitWorkspaceUpdatesForCwds(cwds) {
|
|
4430
|
+
if (!this.workspaceUpdatesSubscription) {
|
|
4431
|
+
return;
|
|
4432
|
+
}
|
|
4433
|
+
const uniqueWorkspaceCwds = new Set();
|
|
4434
|
+
for (const cwd of cwds) {
|
|
4435
|
+
const normalized = this.normalizeWorkspaceId(cwd);
|
|
4436
|
+
if (!normalized) {
|
|
4437
|
+
continue;
|
|
4438
|
+
}
|
|
4439
|
+
uniqueWorkspaceCwds.add(normalized);
|
|
4440
|
+
}
|
|
4441
|
+
for (const workspaceCwd of uniqueWorkspaceCwds) {
|
|
4442
|
+
await this.emitWorkspaceUpdateForCwd(workspaceCwd);
|
|
4443
|
+
}
|
|
4444
|
+
}
|
|
3913
4445
|
async handleFetchAgents(request) {
|
|
3914
4446
|
const requestedSubscriptionId = request.subscribe?.subscriptionId?.trim();
|
|
3915
4447
|
const subscriptionId = request.subscribe
|
|
@@ -3964,12 +4496,68 @@ export class Session {
|
|
|
3964
4496
|
});
|
|
3965
4497
|
}
|
|
3966
4498
|
}
|
|
4499
|
+
async handleFetchWorkspacesRequest(request) {
|
|
4500
|
+
const requestedSubscriptionId = request.subscribe?.subscriptionId?.trim();
|
|
4501
|
+
const subscriptionId = request.subscribe
|
|
4502
|
+
? requestedSubscriptionId && requestedSubscriptionId.length > 0
|
|
4503
|
+
? requestedSubscriptionId
|
|
4504
|
+
: uuidv4()
|
|
4505
|
+
: null;
|
|
4506
|
+
try {
|
|
4507
|
+
if (subscriptionId) {
|
|
4508
|
+
this.workspaceUpdatesSubscription = {
|
|
4509
|
+
subscriptionId,
|
|
4510
|
+
filter: request.filter,
|
|
4511
|
+
isBootstrapping: true,
|
|
4512
|
+
pendingUpdatesByWorkspaceId: new Map(),
|
|
4513
|
+
};
|
|
4514
|
+
}
|
|
4515
|
+
const payload = await this.listFetchWorkspacesEntries(request);
|
|
4516
|
+
const snapshotLatestActivityByWorkspaceId = new Map();
|
|
4517
|
+
for (const entry of payload.entries) {
|
|
4518
|
+
const parsedLatestActivity = entry.activityAt
|
|
4519
|
+
? Date.parse(entry.activityAt)
|
|
4520
|
+
: Number.NEGATIVE_INFINITY;
|
|
4521
|
+
if (!Number.isNaN(parsedLatestActivity)) {
|
|
4522
|
+
snapshotLatestActivityByWorkspaceId.set(entry.id, parsedLatestActivity);
|
|
4523
|
+
}
|
|
4524
|
+
}
|
|
4525
|
+
this.emit({
|
|
4526
|
+
type: 'fetch_workspaces_response',
|
|
4527
|
+
payload: {
|
|
4528
|
+
requestId: request.requestId,
|
|
4529
|
+
...(subscriptionId ? { subscriptionId } : {}),
|
|
4530
|
+
...payload,
|
|
4531
|
+
},
|
|
4532
|
+
});
|
|
4533
|
+
if (subscriptionId && this.workspaceUpdatesSubscription?.subscriptionId === subscriptionId) {
|
|
4534
|
+
this.flushBootstrappedWorkspaceUpdates({ snapshotLatestActivityByWorkspaceId });
|
|
4535
|
+
}
|
|
4536
|
+
}
|
|
4537
|
+
catch (error) {
|
|
4538
|
+
if (subscriptionId && this.workspaceUpdatesSubscription?.subscriptionId === subscriptionId) {
|
|
4539
|
+
this.workspaceUpdatesSubscription = null;
|
|
4540
|
+
}
|
|
4541
|
+
const code = error instanceof SessionRequestError ? error.code : 'fetch_workspaces_failed';
|
|
4542
|
+
const message = error instanceof Error ? error.message : 'Failed to fetch workspaces';
|
|
4543
|
+
this.sessionLogger.error({ err: error }, 'Failed to handle fetch_workspaces_request');
|
|
4544
|
+
this.emit({
|
|
4545
|
+
type: 'rpc_error',
|
|
4546
|
+
payload: {
|
|
4547
|
+
requestId: request.requestId,
|
|
4548
|
+
requestType: request.type,
|
|
4549
|
+
error: message,
|
|
4550
|
+
code,
|
|
4551
|
+
},
|
|
4552
|
+
});
|
|
4553
|
+
}
|
|
4554
|
+
}
|
|
3967
4555
|
async handleFetchAgent(agentIdOrIdentifier, requestId) {
|
|
3968
4556
|
const resolved = await this.resolveAgentIdentifier(agentIdOrIdentifier);
|
|
3969
4557
|
if (!resolved.ok) {
|
|
3970
4558
|
this.emit({
|
|
3971
4559
|
type: 'fetch_agent_response',
|
|
3972
|
-
payload: { requestId, agent: null, error: resolved.error },
|
|
4560
|
+
payload: { requestId, agent: null, project: null, error: resolved.error },
|
|
3973
4561
|
});
|
|
3974
4562
|
return;
|
|
3975
4563
|
}
|
|
@@ -3977,19 +4565,30 @@ export class Session {
|
|
|
3977
4565
|
if (!agent) {
|
|
3978
4566
|
this.emit({
|
|
3979
4567
|
type: 'fetch_agent_response',
|
|
3980
|
-
payload: {
|
|
4568
|
+
payload: {
|
|
4569
|
+
requestId,
|
|
4570
|
+
agent: null,
|
|
4571
|
+
project: null,
|
|
4572
|
+
error: `Agent not found: ${resolved.agentId}`,
|
|
4573
|
+
},
|
|
3981
4574
|
});
|
|
3982
4575
|
return;
|
|
3983
4576
|
}
|
|
4577
|
+
const project = await this.buildProjectPlacement(agent.cwd);
|
|
3984
4578
|
this.emit({
|
|
3985
4579
|
type: 'fetch_agent_response',
|
|
3986
|
-
payload: { requestId, agent, error: null },
|
|
4580
|
+
payload: { requestId, agent, project, error: null },
|
|
3987
4581
|
});
|
|
3988
4582
|
}
|
|
3989
4583
|
async handleFetchAgentTimelineRequest(msg) {
|
|
3990
4584
|
const direction = msg.direction ?? (msg.cursor ? 'after' : 'tail');
|
|
3991
4585
|
const projection = msg.projection ?? 'projected';
|
|
3992
|
-
const
|
|
4586
|
+
const requestedLimit = msg.limit;
|
|
4587
|
+
const limit = requestedLimit ?? (direction === 'after' ? 0 : undefined);
|
|
4588
|
+
const shouldLimitByProjectedWindow = projection === 'canonical' &&
|
|
4589
|
+
direction === 'tail' &&
|
|
4590
|
+
typeof requestedLimit === 'number' &&
|
|
4591
|
+
requestedLimit > 0;
|
|
3993
4592
|
const cursor = msg.cursor
|
|
3994
4593
|
? {
|
|
3995
4594
|
epoch: msg.cursor.epoch,
|
|
@@ -3998,21 +4597,81 @@ export class Session {
|
|
|
3998
4597
|
: undefined;
|
|
3999
4598
|
try {
|
|
4000
4599
|
const snapshot = await this.ensureAgentLoaded(msg.agentId);
|
|
4001
|
-
const
|
|
4600
|
+
const agentPayload = await this.buildAgentPayload(snapshot);
|
|
4601
|
+
let timeline = this.agentManager.fetchTimeline(msg.agentId, {
|
|
4002
4602
|
direction,
|
|
4003
4603
|
cursor,
|
|
4004
|
-
limit
|
|
4005
|
-
|
|
4006
|
-
|
|
4007
|
-
|
|
4008
|
-
|
|
4009
|
-
|
|
4010
|
-
|
|
4604
|
+
limit: shouldLimitByProjectedWindow && typeof requestedLimit === 'number'
|
|
4605
|
+
? Math.max(1, Math.floor(requestedLimit))
|
|
4606
|
+
: limit,
|
|
4607
|
+
});
|
|
4608
|
+
let hasOlder = timeline.hasOlder;
|
|
4609
|
+
let hasNewer = timeline.hasNewer;
|
|
4610
|
+
let startCursor = null;
|
|
4611
|
+
let endCursor = null;
|
|
4612
|
+
let entries;
|
|
4613
|
+
if (shouldLimitByProjectedWindow) {
|
|
4614
|
+
const projectedLimit = Math.max(1, Math.floor(requestedLimit));
|
|
4615
|
+
let fetchLimit = projectedLimit;
|
|
4616
|
+
let projectedWindow = selectTimelineWindowByProjectedLimit({
|
|
4617
|
+
rows: timeline.rows,
|
|
4618
|
+
provider: snapshot.provider,
|
|
4619
|
+
direction,
|
|
4620
|
+
limit: projectedLimit,
|
|
4621
|
+
collapseToolLifecycle: false,
|
|
4622
|
+
});
|
|
4623
|
+
while (timeline.hasOlder) {
|
|
4624
|
+
const needsMoreProjectedEntries = projectedWindow.projectedEntries.length < projectedLimit;
|
|
4625
|
+
const firstLoadedRow = timeline.rows[0];
|
|
4626
|
+
const firstSelectedRow = projectedWindow.selectedRows[0];
|
|
4627
|
+
const startsAtLoadedBoundary = firstLoadedRow != null &&
|
|
4628
|
+
firstSelectedRow != null &&
|
|
4629
|
+
firstSelectedRow.seq === firstLoadedRow.seq;
|
|
4630
|
+
const boundaryIsAssistantChunk = startsAtLoadedBoundary && firstLoadedRow.item.type === 'assistant_message';
|
|
4631
|
+
if (!needsMoreProjectedEntries && !boundaryIsAssistantChunk) {
|
|
4632
|
+
break;
|
|
4633
|
+
}
|
|
4634
|
+
const maxRows = Math.max(0, timeline.window.maxSeq - timeline.window.minSeq + 1);
|
|
4635
|
+
const nextFetchLimit = Math.min(maxRows, fetchLimit * 2);
|
|
4636
|
+
if (nextFetchLimit <= fetchLimit) {
|
|
4637
|
+
break;
|
|
4638
|
+
}
|
|
4639
|
+
fetchLimit = nextFetchLimit;
|
|
4640
|
+
timeline = this.agentManager.fetchTimeline(msg.agentId, {
|
|
4641
|
+
direction,
|
|
4642
|
+
cursor,
|
|
4643
|
+
limit: fetchLimit,
|
|
4644
|
+
});
|
|
4645
|
+
projectedWindow = selectTimelineWindowByProjectedLimit({
|
|
4646
|
+
rows: timeline.rows,
|
|
4647
|
+
provider: snapshot.provider,
|
|
4648
|
+
direction,
|
|
4649
|
+
limit: projectedLimit,
|
|
4650
|
+
collapseToolLifecycle: false,
|
|
4651
|
+
});
|
|
4652
|
+
}
|
|
4653
|
+
const selectedRows = projectedWindow.selectedRows;
|
|
4654
|
+
entries = projectTimelineRows(selectedRows, snapshot.provider, projection);
|
|
4655
|
+
if (projectedWindow.minSeq !== null && projectedWindow.maxSeq !== null) {
|
|
4656
|
+
startCursor = { epoch: timeline.epoch, seq: projectedWindow.minSeq };
|
|
4657
|
+
endCursor = { epoch: timeline.epoch, seq: projectedWindow.maxSeq };
|
|
4658
|
+
hasOlder = projectedWindow.minSeq > timeline.window.minSeq;
|
|
4659
|
+
hasNewer = false;
|
|
4660
|
+
}
|
|
4661
|
+
}
|
|
4662
|
+
else {
|
|
4663
|
+
const firstRow = timeline.rows[0];
|
|
4664
|
+
const lastRow = timeline.rows[timeline.rows.length - 1];
|
|
4665
|
+
startCursor = firstRow ? { epoch: timeline.epoch, seq: firstRow.seq } : null;
|
|
4666
|
+
endCursor = lastRow ? { epoch: timeline.epoch, seq: lastRow.seq } : null;
|
|
4667
|
+
entries = projectTimelineRows(timeline.rows, snapshot.provider, projection);
|
|
4668
|
+
}
|
|
4011
4669
|
this.emit({
|
|
4012
4670
|
type: 'fetch_agent_timeline_response',
|
|
4013
4671
|
payload: {
|
|
4014
4672
|
requestId: msg.requestId,
|
|
4015
4673
|
agentId: msg.agentId,
|
|
4674
|
+
agent: agentPayload,
|
|
4016
4675
|
direction,
|
|
4017
4676
|
projection,
|
|
4018
4677
|
epoch: timeline.epoch,
|
|
@@ -4022,9 +4681,9 @@ export class Session {
|
|
|
4022
4681
|
window: timeline.window,
|
|
4023
4682
|
startCursor,
|
|
4024
4683
|
endCursor,
|
|
4025
|
-
hasOlder
|
|
4026
|
-
hasNewer
|
|
4027
|
-
entries
|
|
4684
|
+
hasOlder,
|
|
4685
|
+
hasNewer,
|
|
4686
|
+
entries,
|
|
4028
4687
|
error: null,
|
|
4029
4688
|
},
|
|
4030
4689
|
});
|
|
@@ -4036,6 +4695,7 @@ export class Session {
|
|
|
4036
4695
|
payload: {
|
|
4037
4696
|
requestId: msg.requestId,
|
|
4038
4697
|
agentId: msg.agentId,
|
|
4698
|
+
agent: null,
|
|
4039
4699
|
direction,
|
|
4040
4700
|
projection,
|
|
4041
4701
|
epoch: '',
|
|
@@ -4069,6 +4729,7 @@ export class Session {
|
|
|
4069
4729
|
}
|
|
4070
4730
|
try {
|
|
4071
4731
|
const agentId = resolved.agentId;
|
|
4732
|
+
await this.unarchiveAgentState(agentId);
|
|
4072
4733
|
await this.ensureAgentLoaded(agentId);
|
|
4073
4734
|
await this.interruptAgentIfRunning(agentId);
|
|
4074
4735
|
try {
|
|
@@ -4188,19 +4849,21 @@ export class Session {
|
|
|
4188
4849
|
return;
|
|
4189
4850
|
}
|
|
4190
4851
|
const abortController = new AbortController();
|
|
4191
|
-
const
|
|
4192
|
-
const timeoutHandle =
|
|
4193
|
-
|
|
4194
|
-
|
|
4852
|
+
const hasTimeout = typeof timeoutMs === 'number' && timeoutMs > 0;
|
|
4853
|
+
const timeoutHandle = hasTimeout
|
|
4854
|
+
? setTimeout(() => {
|
|
4855
|
+
abortController.abort('timeout');
|
|
4856
|
+
}, timeoutMs)
|
|
4857
|
+
: null;
|
|
4195
4858
|
try {
|
|
4196
|
-
|
|
4859
|
+
let result = await this.agentManager.waitForAgentEvent(agentId, {
|
|
4197
4860
|
signal: abortController.signal,
|
|
4198
4861
|
});
|
|
4199
|
-
|
|
4862
|
+
let final = await this.getAgentPayloadById(agentId);
|
|
4200
4863
|
if (!final) {
|
|
4201
4864
|
throw new Error(`Agent ${agentId} disappeared while waiting`);
|
|
4202
4865
|
}
|
|
4203
|
-
|
|
4866
|
+
let status = result.permission ? 'permission' : result.status === 'error' ? 'error' : 'idle';
|
|
4204
4867
|
this.emit({
|
|
4205
4868
|
type: 'wait_for_finish_response',
|
|
4206
4869
|
payload: { requestId, status, final, error: null, lastMessage: result.lastMessage },
|
|
@@ -4239,7 +4902,9 @@ export class Session {
|
|
|
4239
4902
|
});
|
|
4240
4903
|
}
|
|
4241
4904
|
finally {
|
|
4242
|
-
|
|
4905
|
+
if (timeoutHandle) {
|
|
4906
|
+
clearTimeout(timeoutHandle);
|
|
4907
|
+
}
|
|
4243
4908
|
}
|
|
4244
4909
|
}
|
|
4245
4910
|
/**
|
|
@@ -4512,7 +5177,7 @@ export class Session {
|
|
|
4512
5177
|
});
|
|
4513
5178
|
});
|
|
4514
5179
|
this.registerVoiceCallerContext?.(agentId, {
|
|
4515
|
-
childAgentDefaultLabels: {
|
|
5180
|
+
childAgentDefaultLabels: {},
|
|
4516
5181
|
allowCustomCwd: false,
|
|
4517
5182
|
enableVoiceTools: true,
|
|
4518
5183
|
});
|
|
@@ -4736,7 +5401,6 @@ export class Session {
|
|
|
4736
5401
|
if (this.agentMcpClient) {
|
|
4737
5402
|
try {
|
|
4738
5403
|
await this.agentMcpClient.close();
|
|
4739
|
-
this.sessionLogger.debug('Agent MCP client closed');
|
|
4740
5404
|
}
|
|
4741
5405
|
catch (error) {
|
|
4742
5406
|
this.sessionLogger.error({ err: error }, 'Failed to close Agent MCP client');
|
|
@@ -4832,17 +5496,11 @@ export class Session {
|
|
|
4832
5496
|
if (!this.terminalManager || !this.subscribedTerminalDirectories.has(cwd)) {
|
|
4833
5497
|
return;
|
|
4834
5498
|
}
|
|
4835
|
-
const hadDirectoryBeforeSubscribe = this.terminalManager.listDirectories().includes(cwd);
|
|
4836
5499
|
try {
|
|
4837
5500
|
const terminals = await this.terminalManager.getTerminals(cwd);
|
|
4838
5501
|
for (const terminal of terminals) {
|
|
4839
5502
|
this.ensureTerminalExitSubscription(terminal);
|
|
4840
5503
|
}
|
|
4841
|
-
// New directories auto-create Terminal 1, which already emits through
|
|
4842
|
-
// terminal-manager change listeners.
|
|
4843
|
-
if (!hadDirectoryBeforeSubscribe) {
|
|
4844
|
-
return;
|
|
4845
|
-
}
|
|
4846
5504
|
if (!this.subscribedTerminalDirectories.has(cwd)) {
|
|
4847
5505
|
return;
|
|
4848
5506
|
}
|
|
@@ -5115,10 +5773,16 @@ export class Session {
|
|
|
5115
5773
|
}
|
|
5116
5774
|
const existingStreamId = this.terminalStreamByTerminalId.get(msg.terminalId);
|
|
5117
5775
|
if (typeof existingStreamId === 'number') {
|
|
5118
|
-
|
|
5776
|
+
// Replacing an active stream can happen when multiple UI surfaces attach to the
|
|
5777
|
+
// same terminal. Emit exit for the replaced stream so stale listeners reconnect
|
|
5778
|
+
// instead of continuing to send input to an invalid stream id.
|
|
5779
|
+
this.detachTerminalStream(existingStreamId, { emitExit: true });
|
|
5119
5780
|
}
|
|
5120
5781
|
const streamId = this.allocateTerminalStreamId();
|
|
5121
|
-
const
|
|
5782
|
+
const requestedResumeOffset = typeof msg.resumeOffset === 'number'
|
|
5783
|
+
? msg.resumeOffset
|
|
5784
|
+
: session.getOutputOffset();
|
|
5785
|
+
const initialOffset = Math.max(0, Math.floor(requestedResumeOffset));
|
|
5122
5786
|
const binding = {
|
|
5123
5787
|
terminalId: msg.terminalId,
|
|
5124
5788
|
unsubscribe: () => { },
|
|
@@ -5142,7 +5806,7 @@ export class Session {
|
|
|
5142
5806
|
endOffset: chunk.endOffset,
|
|
5143
5807
|
replay: chunk.replay,
|
|
5144
5808
|
});
|
|
5145
|
-
}, { fromOffset:
|
|
5809
|
+
}, { fromOffset: requestedResumeOffset });
|
|
5146
5810
|
}
|
|
5147
5811
|
catch (error) {
|
|
5148
5812
|
this.terminalStreams.delete(streamId);
|