@getpaseo/server 0.1.16 → 0.1.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/scripts/daemon-runner.js +53 -14
- package/dist/scripts/daemon-runner.js.map +1 -1
- package/dist/scripts/dev-runner.js +9 -16
- package/dist/scripts/dev-runner.js.map +1 -1
- package/dist/scripts/supervisor.js +40 -13
- package/dist/scripts/supervisor.js.map +1 -1
- package/dist/server/client/daemon-client.d.ts +23 -3
- package/dist/server/client/daemon-client.d.ts.map +1 -1
- package/dist/server/client/daemon-client.js +81 -8
- package/dist/server/client/daemon-client.js.map +1 -1
- package/dist/server/server/agent/agent-manager.d.ts +3 -1
- package/dist/server/server/agent/agent-manager.d.ts.map +1 -1
- package/dist/server/server/agent/agent-manager.js +146 -24
- package/dist/server/server/agent/agent-manager.js.map +1 -1
- package/dist/server/server/agent/agent-metadata-generator.d.ts.map +1 -1
- package/dist/server/server/agent/agent-metadata-generator.js +13 -4
- package/dist/server/server/agent/agent-metadata-generator.js.map +1 -1
- package/dist/server/server/agent/agent-response-loop.js +1 -1
- package/dist/server/server/agent/agent-response-loop.js.map +1 -1
- package/dist/server/server/agent/agent-sdk-types.d.ts +9 -0
- package/dist/server/server/agent/agent-sdk-types.d.ts.map +1 -1
- package/dist/server/server/agent/agent-sdk-types.js +11 -1
- package/dist/server/server/agent/agent-sdk-types.js.map +1 -1
- package/dist/server/server/agent/agent-storage.d.ts +5 -1
- package/dist/server/server/agent/agent-storage.d.ts.map +1 -1
- package/dist/server/server/agent/agent-storage.js +41 -72
- package/dist/server/server/agent/agent-storage.js.map +1 -1
- package/dist/server/server/agent/agent-title-limits.d.ts +3 -0
- package/dist/server/server/agent/agent-title-limits.d.ts.map +1 -0
- package/dist/server/server/agent/agent-title-limits.js +3 -0
- package/dist/server/server/agent/agent-title-limits.js.map +1 -0
- package/dist/server/server/agent/providers/claude/model-catalog.d.ts +29 -0
- package/dist/server/server/agent/providers/claude/model-catalog.d.ts.map +1 -0
- package/dist/server/server/agent/providers/claude/model-catalog.js +70 -0
- package/dist/server/server/agent/providers/claude/model-catalog.js.map +1 -0
- package/dist/server/server/agent/providers/claude/task-notification-tool-call.d.ts +44 -0
- package/dist/server/server/agent/providers/claude/task-notification-tool-call.d.ts.map +1 -0
- package/dist/server/server/agent/providers/claude/task-notification-tool-call.js +250 -0
- package/dist/server/server/agent/providers/claude/task-notification-tool-call.js.map +1 -0
- package/dist/server/server/agent/providers/claude/tool-call-detail-parser.d.ts.map +1 -1
- package/dist/server/server/agent/providers/claude/tool-call-detail-parser.js +15 -0
- package/dist/server/server/agent/providers/claude/tool-call-detail-parser.js.map +1 -1
- package/dist/server/server/agent/providers/claude-agent.d.ts +3 -2
- package/dist/server/server/agent/providers/claude-agent.d.ts.map +1 -1
- package/dist/server/server/agent/providers/claude-agent.js +244 -107
- package/dist/server/server/agent/providers/claude-agent.js.map +1 -1
- package/dist/server/server/agent/providers/codex/tool-call-mapper.d.ts.map +1 -1
- package/dist/server/server/agent/providers/codex/tool-call-mapper.js +81 -28
- package/dist/server/server/agent/providers/codex/tool-call-mapper.js.map +1 -1
- package/dist/server/server/agent/providers/codex-app-server-agent.d.ts.map +1 -1
- package/dist/server/server/agent/providers/codex-app-server-agent.js +31 -5
- package/dist/server/server/agent/providers/codex-app-server-agent.js.map +1 -1
- package/dist/server/server/agent/providers/opencode-agent.d.ts +10 -1
- package/dist/server/server/agent/providers/opencode-agent.d.ts.map +1 -1
- package/dist/server/server/agent/providers/opencode-agent.js +207 -176
- package/dist/server/server/agent/providers/opencode-agent.js.map +1 -1
- package/dist/server/server/agent/providers/tool-call-detail-primitives.d.ts +15 -0
- package/dist/server/server/agent/providers/tool-call-detail-primitives.d.ts.map +1 -1
- package/dist/server/server/agent/timeline-projection.d.ts +20 -0
- package/dist/server/server/agent/timeline-projection.d.ts.map +1 -1
- package/dist/server/server/agent/timeline-projection.js +73 -0
- package/dist/server/server/agent/timeline-projection.js.map +1 -1
- package/dist/server/server/bootstrap.d.ts +15 -0
- package/dist/server/server/bootstrap.d.ts.map +1 -1
- package/dist/server/server/bootstrap.js +27 -4
- package/dist/server/server/bootstrap.js.map +1 -1
- package/dist/server/server/file-download/token-store.d.ts +0 -1
- package/dist/server/server/file-download/token-store.d.ts.map +1 -1
- package/dist/server/server/file-download/token-store.js.map +1 -1
- package/dist/server/server/file-explorer/service.d.ts.map +1 -1
- package/dist/server/server/file-explorer/service.js +56 -36
- package/dist/server/server/file-explorer/service.js.map +1 -1
- package/dist/server/server/index.js +85 -29
- package/dist/server/server/index.js.map +1 -1
- package/dist/server/server/logger.d.ts +24 -3
- package/dist/server/server/logger.d.ts.map +1 -1
- package/dist/server/server/logger.js +157 -21
- package/dist/server/server/logger.js.map +1 -1
- package/dist/server/server/persisted-config.d.ts +86 -0
- package/dist/server/server/persisted-config.d.ts.map +1 -1
- package/dist/server/server/persisted-config.js +25 -3
- package/dist/server/server/persisted-config.js.map +1 -1
- package/dist/server/server/pid-lock.d.ts +6 -2
- package/dist/server/server/pid-lock.d.ts.map +1 -1
- package/dist/server/server/pid-lock.js +7 -10
- package/dist/server/server/pid-lock.js.map +1 -1
- package/dist/server/server/relay-transport.d.ts.map +1 -1
- package/dist/server/server/relay-transport.js +1 -0
- package/dist/server/server/relay-transport.js.map +1 -1
- package/dist/server/server/session.d.ts +57 -3
- package/dist/server/server/session.d.ts.map +1 -1
- package/dist/server/server/session.js +755 -182
- package/dist/server/server/session.js.map +1 -1
- package/dist/server/server/websocket-server.d.ts +16 -1
- package/dist/server/server/websocket-server.d.ts.map +1 -1
- package/dist/server/server/websocket-server.js +135 -9
- package/dist/server/server/websocket-server.js.map +1 -1
- package/dist/server/server/worktree-bootstrap.d.ts.map +1 -1
- package/dist/server/server/worktree-bootstrap.js +45 -2
- package/dist/server/server/worktree-bootstrap.js.map +1 -1
- package/dist/server/shared/messages.d.ts +2841 -541
- package/dist/server/shared/messages.d.ts.map +1 -1
- package/dist/server/shared/messages.js +99 -5
- package/dist/server/shared/messages.js.map +1 -1
- package/dist/server/shared/tool-call-display.d.ts.map +1 -1
- package/dist/server/shared/tool-call-display.js +3 -0
- package/dist/server/shared/tool-call-display.js.map +1 -1
- package/dist/server/terminal/terminal-manager.d.ts.map +1 -1
- package/dist/server/terminal/terminal-manager.js +1 -13
- package/dist/server/terminal/terminal-manager.js.map +1 -1
- package/dist/server/terminal/terminal.d.ts.map +1 -1
- package/dist/server/terminal/terminal.js +29 -5
- package/dist/server/terminal/terminal.js.map +1 -1
- package/dist/server/utils/project-icon.d.ts +1 -1
- package/dist/server/utils/project-icon.d.ts.map +1 -1
- package/dist/server/utils/project-icon.js +9 -2
- package/dist/server/utils/project-icon.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 +3750 -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 +398 -0
- package/dist/src/utils/project-icon.js.map +1 -0
- package/dist/src/utils/worktree-metadata.js +116 -0
- package/dist/src/utils/worktree-metadata.js.map +1 -0
- package/dist/src/utils/worktree.js +741 -0
- package/dist/src/utils/worktree.js.map +1 -0
- package/package.json +14 -6
|
@@ -18,8 +18,9 @@ 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
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';
|
|
@@ -34,19 +35,44 @@ import { searchHomeDirectories, searchWorkspaceEntries } from '../utils/director
|
|
|
34
35
|
import { ensureLocalSpeechModels, getLocalSpeechModelDir, listLocalSpeechModels, } from './speech/providers/local/models.js';
|
|
35
36
|
import { resolveClientMessageId } from './client-message-id.js';
|
|
36
37
|
const execAsync = promisify(exec);
|
|
38
|
+
const MAX_INITIAL_AGENT_TITLE_CHARS = Math.min(60, MAX_EXPLICIT_AGENT_TITLE_CHARS);
|
|
37
39
|
const READ_ONLY_GIT_ENV = {
|
|
38
40
|
...process.env,
|
|
39
41
|
GIT_OPTIONAL_LOCKS: '0',
|
|
40
42
|
};
|
|
41
43
|
const pendingAgentInitializations = new Map();
|
|
42
|
-
let restartRequested = false;
|
|
43
44
|
const DEFAULT_AGENT_PROVIDER = AGENT_PROVIDER_IDS[0];
|
|
44
|
-
const RESTART_EXIT_DELAY_MS = 250;
|
|
45
45
|
const CHECKOUT_DIFF_WATCH_DEBOUNCE_MS = 150;
|
|
46
46
|
const CHECKOUT_DIFF_FALLBACK_REFRESH_MS = 5000;
|
|
47
47
|
const TERMINAL_STREAM_WINDOW_BYTES = 256 * 1024;
|
|
48
48
|
const TERMINAL_STREAM_MAX_PENDING_BYTES = 2 * 1024 * 1024;
|
|
49
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
|
+
}
|
|
50
76
|
function deriveRemoteProjectKey(remoteUrl) {
|
|
51
77
|
if (!remoteUrl) {
|
|
52
78
|
return null;
|
|
@@ -93,10 +119,9 @@ function deriveProjectGroupingKey(options) {
|
|
|
93
119
|
if (remoteKey) {
|
|
94
120
|
return remoteKey;
|
|
95
121
|
}
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
return options.cwd.slice(0, idx).replace(/\/$/, '');
|
|
122
|
+
const mainRepoRoot = options.mainRepoRoot?.trim();
|
|
123
|
+
if (options.isPaseoOwnedWorktree && mainRepoRoot) {
|
|
124
|
+
return mainRepoRoot;
|
|
100
125
|
}
|
|
101
126
|
return options.cwd;
|
|
102
127
|
}
|
|
@@ -214,6 +239,7 @@ export class Session {
|
|
|
214
239
|
this.agentTools = null;
|
|
215
240
|
this.unsubscribeAgentEvents = null;
|
|
216
241
|
this.agentUpdatesSubscription = null;
|
|
242
|
+
this.workspaceUpdatesSubscription = null;
|
|
217
243
|
this.clientActivity = null;
|
|
218
244
|
this.MOBILE_BACKGROUND_STREAM_GRACE_MS = 60000;
|
|
219
245
|
this.subscribedTerminalDirectories = new Set();
|
|
@@ -227,11 +253,19 @@ export class Session {
|
|
|
227
253
|
this.checkoutDiffTargets = new Map();
|
|
228
254
|
this.voiceModeAgentId = null;
|
|
229
255
|
this.voiceModeBaseConfig = null;
|
|
230
|
-
|
|
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;
|
|
231
264
|
this.clientId = clientId;
|
|
232
265
|
this.sessionId = uuidv4();
|
|
233
266
|
this.onMessage = onMessage;
|
|
234
267
|
this.onBinaryMessage = onBinaryMessage ?? null;
|
|
268
|
+
this.onLifecycleIntent = onLifecycleIntent ?? null;
|
|
235
269
|
this.downloadTokenStore = downloadTokenStore;
|
|
236
270
|
this.pushTokenStore = pushTokenStore;
|
|
237
271
|
this.paseoHome = paseoHome;
|
|
@@ -297,6 +331,25 @@ export class Session {
|
|
|
297
331
|
getClientActivity() {
|
|
298
332
|
return this.clientActivity;
|
|
299
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
|
+
}
|
|
300
353
|
/**
|
|
301
354
|
* Send initial state to client after connection
|
|
302
355
|
*/
|
|
@@ -327,10 +380,11 @@ export class Session {
|
|
|
327
380
|
async interruptAgentIfRunning(agentId) {
|
|
328
381
|
const snapshot = this.agentManager.getAgent(agentId);
|
|
329
382
|
if (!snapshot) {
|
|
383
|
+
this.sessionLogger.trace({ agentId }, 'interruptAgentIfRunning: agent not found');
|
|
330
384
|
throw new Error(`Agent ${agentId} not found`);
|
|
331
385
|
}
|
|
332
386
|
if (snapshot.lifecycle !== 'running' && !snapshot.pendingRun) {
|
|
333
|
-
this.sessionLogger.
|
|
387
|
+
this.sessionLogger.trace({ agentId, lifecycle: snapshot.lifecycle, pendingRun: Boolean(snapshot.pendingRun) }, 'interruptAgentIfRunning: skipping because agent is not running');
|
|
334
388
|
return;
|
|
335
389
|
}
|
|
336
390
|
this.sessionLogger.debug({ agentId, lifecycle: snapshot.lifecycle, pendingRun: Boolean(snapshot.pendingRun) }, 'interruptAgentIfRunning: interrupting');
|
|
@@ -360,10 +414,15 @@ export class Session {
|
|
|
360
414
|
* Start streaming an agent run and forward results via the websocket broadcast
|
|
361
415
|
*/
|
|
362
416
|
startAgentStream(agentId, prompt, runOptions) {
|
|
363
|
-
this.sessionLogger.
|
|
417
|
+
this.sessionLogger.trace({
|
|
418
|
+
agentId,
|
|
419
|
+
promptType: typeof prompt === 'string' ? 'string' : 'structured',
|
|
420
|
+
hasRunOptions: Boolean(runOptions),
|
|
421
|
+
}, 'startAgentStream: requested');
|
|
364
422
|
let iterator;
|
|
365
423
|
try {
|
|
366
424
|
iterator = this.agentManager.streamAgent(agentId, prompt, runOptions);
|
|
425
|
+
this.sessionLogger.trace({ agentId }, 'startAgentStream: streamAgent returned iterator');
|
|
367
426
|
}
|
|
368
427
|
catch (error) {
|
|
369
428
|
this.handleAgentRunError(agentId, error, 'Failed to start agent run');
|
|
@@ -375,8 +434,10 @@ export class Session {
|
|
|
375
434
|
for await (const _ of iterator) {
|
|
376
435
|
// Events are forwarded via the session's AgentManager subscription.
|
|
377
436
|
}
|
|
437
|
+
this.sessionLogger.trace({ agentId }, 'startAgentStream: iterator drained');
|
|
378
438
|
}
|
|
379
439
|
catch (error) {
|
|
440
|
+
this.sessionLogger.trace({ agentId, err: error }, 'startAgentStream: iterator threw');
|
|
380
441
|
this.handleAgentRunError(agentId, error, 'Agent stream failed');
|
|
381
442
|
}
|
|
382
443
|
})();
|
|
@@ -499,7 +560,7 @@ export class Session {
|
|
|
499
560
|
}
|
|
500
561
|
async buildAgentPayload(agent) {
|
|
501
562
|
const storedRecord = await this.agentStorage.get(agent.id);
|
|
502
|
-
const title = storedRecord?.title ?? null;
|
|
563
|
+
const title = storedRecord?.title ?? storedRecord?.config?.title ?? null;
|
|
503
564
|
const payload = toAgentPayload(agent, { title });
|
|
504
565
|
payload.archivedAt = storedRecord?.archivedAt ?? null;
|
|
505
566
|
return payload;
|
|
@@ -555,7 +616,7 @@ export class Session {
|
|
|
555
616
|
persistence: toAgentPersistenceHandle(this.sessionLogger, record.persistence),
|
|
556
617
|
lastUsage: undefined,
|
|
557
618
|
lastError: undefined,
|
|
558
|
-
title: record.title ?? null,
|
|
619
|
+
title: record.title ?? record.config?.title ?? null,
|
|
559
620
|
requiresAttention: record.requiresAttention ?? false,
|
|
560
621
|
attentionReason: record.attentionReason ?? null,
|
|
561
622
|
attentionTimestamp: record.attentionTimestamp ?? null,
|
|
@@ -724,6 +785,8 @@ export class Session {
|
|
|
724
785
|
const projectKey = deriveProjectGroupingKey({
|
|
725
786
|
cwd,
|
|
726
787
|
remoteUrl: checkout.remoteUrl,
|
|
788
|
+
isPaseoOwnedWorktree: checkout.isPaseoOwnedWorktree,
|
|
789
|
+
mainRepoRoot: checkout.mainRepoRoot,
|
|
727
790
|
});
|
|
728
791
|
return {
|
|
729
792
|
projectKey,
|
|
@@ -734,28 +797,29 @@ export class Session {
|
|
|
734
797
|
async forwardAgentUpdate(agent) {
|
|
735
798
|
try {
|
|
736
799
|
const subscription = this.agentUpdatesSubscription;
|
|
737
|
-
if (!subscription) {
|
|
738
|
-
return;
|
|
739
|
-
}
|
|
740
800
|
const payload = await this.buildAgentPayload(agent);
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
project,
|
|
745
|
-
filter: subscription.filter,
|
|
746
|
-
});
|
|
747
|
-
if (matches) {
|
|
748
|
-
this.bufferOrEmitAgentUpdate(subscription, {
|
|
749
|
-
kind: 'upsert',
|
|
801
|
+
if (subscription) {
|
|
802
|
+
const project = await this.buildProjectPlacement(payload.cwd);
|
|
803
|
+
const matches = this.matchesAgentFilter({
|
|
750
804
|
agent: payload,
|
|
751
805
|
project,
|
|
806
|
+
filter: subscription.filter,
|
|
752
807
|
});
|
|
753
|
-
|
|
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
|
+
}
|
|
754
821
|
}
|
|
755
|
-
this.
|
|
756
|
-
kind: 'remove',
|
|
757
|
-
agentId: payload.id,
|
|
758
|
-
});
|
|
822
|
+
await this.emitWorkspaceUpdateForCwd(payload.cwd);
|
|
759
823
|
}
|
|
760
824
|
catch (error) {
|
|
761
825
|
this.sessionLogger.error({ err: error }, 'Failed to emit agent update');
|
|
@@ -779,6 +843,9 @@ export class Session {
|
|
|
779
843
|
case 'fetch_agents_request':
|
|
780
844
|
await this.handleFetchAgents(msg);
|
|
781
845
|
break;
|
|
846
|
+
case 'fetch_workspaces_request':
|
|
847
|
+
await this.handleFetchWorkspacesRequest(msg);
|
|
848
|
+
break;
|
|
782
849
|
case 'fetch_agent_request':
|
|
783
850
|
await this.handleFetchAgent(msg.agentId, msg.requestId);
|
|
784
851
|
break;
|
|
@@ -848,6 +915,9 @@ export class Session {
|
|
|
848
915
|
case 'restart_server_request':
|
|
849
916
|
await this.handleRestartServerRequest(msg.requestId, msg.reason);
|
|
850
917
|
break;
|
|
918
|
+
case 'shutdown_server_request':
|
|
919
|
+
await this.handleShutdownServerRequest(msg.requestId);
|
|
920
|
+
break;
|
|
851
921
|
case 'fetch_agent_timeline_request':
|
|
852
922
|
await this.handleFetchAgentTimelineRequest(msg);
|
|
853
923
|
break;
|
|
@@ -1067,11 +1137,6 @@ export class Session {
|
|
|
1067
1137
|
this.sessionLogger.warn({ streamId: frame.streamId, messageType: frame.messageType }, 'Unhandled terminal binary frame');
|
|
1068
1138
|
}
|
|
1069
1139
|
async handleRestartServerRequest(requestId, reason) {
|
|
1070
|
-
if (restartRequested) {
|
|
1071
|
-
this.sessionLogger.debug('Restart already requested, ignoring duplicate');
|
|
1072
|
-
return;
|
|
1073
|
-
}
|
|
1074
|
-
restartRequested = true;
|
|
1075
1140
|
const payload = {
|
|
1076
1141
|
status: 'restart_requested',
|
|
1077
1142
|
clientId: this.clientId,
|
|
@@ -1085,19 +1150,45 @@ export class Session {
|
|
|
1085
1150
|
type: 'status',
|
|
1086
1151
|
payload,
|
|
1087
1152
|
});
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
})
|
|
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) {
|
|
1093
1178
|
return;
|
|
1094
1179
|
}
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
}
|
|
1180
|
+
try {
|
|
1181
|
+
this.onLifecycleIntent(intent);
|
|
1182
|
+
}
|
|
1183
|
+
catch (error) {
|
|
1184
|
+
this.sessionLogger.error({ err: error, intent }, 'Lifecycle intent handler failed');
|
|
1185
|
+
}
|
|
1098
1186
|
}
|
|
1099
1187
|
async handleDeleteAgentRequest(agentId, requestId) {
|
|
1100
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;
|
|
1101
1192
|
// Prevent the persistence hook from re-creating the record while we close/delete.
|
|
1102
1193
|
this.agentStorage.beginDelete(agentId);
|
|
1103
1194
|
try {
|
|
@@ -1125,11 +1216,31 @@ export class Session {
|
|
|
1125
1216
|
agentId,
|
|
1126
1217
|
});
|
|
1127
1218
|
}
|
|
1219
|
+
if (knownCwd) {
|
|
1220
|
+
await this.emitWorkspaceUpdateForCwd(knownCwd);
|
|
1221
|
+
}
|
|
1128
1222
|
}
|
|
1129
1223
|
async handleArchiveAgentRequest(agentId, requestId) {
|
|
1130
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) {
|
|
1131
1241
|
if (this.agentManager.getAgent(agentId)) {
|
|
1132
1242
|
await this.interruptAgentIfRunning(agentId);
|
|
1243
|
+
await this.agentManager.clearAgentAttention(agentId).catch(() => undefined);
|
|
1133
1244
|
}
|
|
1134
1245
|
const archivedAt = new Date().toISOString();
|
|
1135
1246
|
const existing = await this.agentStorage.get(agentId);
|
|
@@ -1147,29 +1258,41 @@ export class Session {
|
|
|
1147
1258
|
throw new Error(`Agent not found in storage after snapshot: ${agentId}`);
|
|
1148
1259
|
}
|
|
1149
1260
|
}
|
|
1150
|
-
|
|
1261
|
+
const normalizedStatus = archivedRecord.lastStatus === 'running' || archivedRecord.lastStatus === 'initializing'
|
|
1262
|
+
? 'idle'
|
|
1263
|
+
: archivedRecord.lastStatus;
|
|
1264
|
+
const nextRecord = {
|
|
1151
1265
|
...archivedRecord,
|
|
1152
1266
|
archivedAt,
|
|
1267
|
+
lastStatus: normalizedStatus,
|
|
1268
|
+
requiresAttention: false,
|
|
1269
|
+
attentionReason: null,
|
|
1270
|
+
attentionTimestamp: null,
|
|
1153
1271
|
};
|
|
1154
|
-
await this.agentStorage.upsert(
|
|
1272
|
+
await this.agentStorage.upsert(nextRecord);
|
|
1155
1273
|
this.agentManager.notifyAgentState(agentId);
|
|
1156
|
-
|
|
1157
|
-
type: 'agent_archived',
|
|
1158
|
-
payload: {
|
|
1159
|
-
agentId,
|
|
1160
|
-
archivedAt,
|
|
1161
|
-
requestId,
|
|
1162
|
-
},
|
|
1163
|
-
});
|
|
1164
|
-
await this.maybeArchiveWorktreeAfterLastAgentArchived({
|
|
1165
|
-
archivedAgentId: agentId,
|
|
1166
|
-
archivedAgentCwd: archivedRecord.cwd,
|
|
1167
|
-
requestId,
|
|
1168
|
-
});
|
|
1274
|
+
return { archivedAt, archivedRecord: nextRecord };
|
|
1169
1275
|
}
|
|
1170
|
-
async
|
|
1276
|
+
async unarchiveAgentState(agentId) {
|
|
1171
1277
|
const record = await this.agentStorage.get(agentId);
|
|
1172
|
-
|
|
1278
|
+
if (!record || !record.archivedAt) {
|
|
1279
|
+
return false;
|
|
1280
|
+
}
|
|
1281
|
+
await this.agentStorage.upsert({
|
|
1282
|
+
...record,
|
|
1283
|
+
archivedAt: null,
|
|
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);
|
|
1173
1296
|
}
|
|
1174
1297
|
async handleUpdateAgentRequest(agentId, name, labels, requestId) {
|
|
1175
1298
|
this.sessionLogger.info({
|
|
@@ -1642,6 +1765,7 @@ export class Session {
|
|
|
1642
1765
|
*/
|
|
1643
1766
|
async handleSendAgentMessage(agentId, text, messageId, images, runOptions) {
|
|
1644
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);
|
|
1645
1769
|
try {
|
|
1646
1770
|
await this.ensureAgentLoaded(agentId);
|
|
1647
1771
|
}
|
|
@@ -1649,11 +1773,6 @@ export class Session {
|
|
|
1649
1773
|
this.handleAgentRunError(agentId, error, 'Failed to initialize agent before sending prompt');
|
|
1650
1774
|
return;
|
|
1651
1775
|
}
|
|
1652
|
-
const archivedAt = await this.getArchivedAt(agentId);
|
|
1653
|
-
if (archivedAt) {
|
|
1654
|
-
this.handleAgentRunError(agentId, new Error(`Agent ${agentId} is archived`), 'Refusing to send prompt to archived agent');
|
|
1655
|
-
return;
|
|
1656
|
-
}
|
|
1657
1776
|
try {
|
|
1658
1777
|
await this.interruptAgentIfRunning(agentId);
|
|
1659
1778
|
}
|
|
@@ -1680,24 +1799,44 @@ export class Session {
|
|
|
1680
1799
|
const { config, worktreeName, requestId, initialPrompt, clientMessageId, outputSchema, git, images, labels, } = msg;
|
|
1681
1800
|
this.sessionLogger.info({ cwd: config.cwd, provider: config.provider, worktreeName }, `Creating agent in ${config.cwd} (${config.provider})${worktreeName ? ` with worktree ${worktreeName}` : ''}`);
|
|
1682
1801
|
try {
|
|
1683
|
-
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);
|
|
1684
1812
|
const snapshot = await this.agentManager.createAgent(sessionConfig, undefined, { labels });
|
|
1685
1813
|
await this.forwardAgentUpdate(snapshot);
|
|
1686
|
-
|
|
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
|
+
}
|
|
1687
1829
|
if (trimmedPrompt) {
|
|
1688
1830
|
scheduleAgentMetadataGeneration({
|
|
1689
1831
|
agentManager: this.agentManager,
|
|
1690
1832
|
agentId: snapshot.id,
|
|
1691
1833
|
cwd: snapshot.cwd,
|
|
1692
1834
|
initialPrompt: trimmedPrompt,
|
|
1693
|
-
explicitTitle
|
|
1835
|
+
explicitTitle,
|
|
1694
1836
|
paseoHome: this.paseoHome,
|
|
1695
1837
|
logger: this.sessionLogger,
|
|
1696
1838
|
});
|
|
1697
|
-
|
|
1698
|
-
await this.handleSendAgentMessage(snapshot.id, trimmedPrompt, resolveClientMessageId(clientMessageId), images, outputSchema ? { outputSchema } : undefined);
|
|
1699
|
-
}
|
|
1700
|
-
catch (promptError) {
|
|
1839
|
+
void this.handleSendAgentMessage(snapshot.id, trimmedPrompt, resolveClientMessageId(clientMessageId), images, outputSchema ? { outputSchema } : undefined).catch((promptError) => {
|
|
1701
1840
|
this.sessionLogger.error({ err: promptError, agentId: snapshot.id }, `Failed to run initial prompt for agent ${snapshot.id}`);
|
|
1702
1841
|
this.emit({
|
|
1703
1842
|
type: 'activity_log',
|
|
@@ -1708,21 +1847,6 @@ export class Session {
|
|
|
1708
1847
|
content: `Initial prompt failed: ${promptError?.message ?? promptError}`,
|
|
1709
1848
|
},
|
|
1710
1849
|
});
|
|
1711
|
-
}
|
|
1712
|
-
}
|
|
1713
|
-
if (requestId) {
|
|
1714
|
-
const agentPayload = await this.getAgentPayloadById(snapshot.id);
|
|
1715
|
-
if (!agentPayload) {
|
|
1716
|
-
throw new Error(`Agent ${snapshot.id} not found after creation`);
|
|
1717
|
-
}
|
|
1718
|
-
this.emit({
|
|
1719
|
-
type: 'status',
|
|
1720
|
-
payload: {
|
|
1721
|
-
status: 'agent_created',
|
|
1722
|
-
agentId: snapshot.id,
|
|
1723
|
-
requestId,
|
|
1724
|
-
agent: agentPayload,
|
|
1725
|
-
},
|
|
1726
1850
|
});
|
|
1727
1851
|
}
|
|
1728
1852
|
if (worktreeConfig) {
|
|
@@ -1785,7 +1909,9 @@ export class Session {
|
|
|
1785
1909
|
}
|
|
1786
1910
|
this.sessionLogger.info({ sessionId: handle.sessionId, provider: handle.provider }, `Resuming agent ${handle.sessionId} (${handle.provider})`);
|
|
1787
1911
|
try {
|
|
1912
|
+
await this.unarchiveAgentByHandle(handle);
|
|
1788
1913
|
const snapshot = await this.agentManager.resumeAgentFromPersistence(handle, overrides);
|
|
1914
|
+
await this.unarchiveAgentState(snapshot.id);
|
|
1789
1915
|
await this.agentManager.hydrateTimelineFromProvider(snapshot.id);
|
|
1790
1916
|
await this.forwardAgentUpdate(snapshot);
|
|
1791
1917
|
const timelineSize = this.agentManager.getTimeline(snapshot.id).length;
|
|
@@ -1823,6 +1949,7 @@ export class Session {
|
|
|
1823
1949
|
const { agentId, requestId } = msg;
|
|
1824
1950
|
this.sessionLogger.info({ agentId }, `Refreshing agent ${agentId} from persistence`);
|
|
1825
1951
|
try {
|
|
1952
|
+
await this.unarchiveAgentState(agentId);
|
|
1826
1953
|
let snapshot;
|
|
1827
1954
|
const existing = this.agentManager.getAgent(agentId);
|
|
1828
1955
|
if (existing) {
|
|
@@ -2446,7 +2573,6 @@ export class Session {
|
|
|
2446
2573
|
*/
|
|
2447
2574
|
async handleClearAgentAttention(agentId) {
|
|
2448
2575
|
const agentIds = Array.isArray(agentId) ? agentId : [agentId];
|
|
2449
|
-
this.sessionLogger.debug({ agentIds }, `Clearing attention for ${agentIds.length} agent(s): ${agentIds.join(', ')}`);
|
|
2450
2576
|
try {
|
|
2451
2577
|
await Promise.all(agentIds.map((id) => this.agentManager.clearAgentAttention(id)));
|
|
2452
2578
|
}
|
|
@@ -3323,6 +3449,7 @@ export class Session {
|
|
|
3323
3449
|
payload: {
|
|
3324
3450
|
worktrees: worktrees.map((entry) => ({
|
|
3325
3451
|
worktreePath: entry.path,
|
|
3452
|
+
createdAt: entry.createdAt,
|
|
3326
3453
|
branchName: entry.branchName ?? null,
|
|
3327
3454
|
head: entry.head ?? null,
|
|
3328
3455
|
})),
|
|
@@ -3404,10 +3531,12 @@ export class Session {
|
|
|
3404
3531
|
targetPath = resolvedWorktree.worktreePath;
|
|
3405
3532
|
}
|
|
3406
3533
|
const removedAgents = new Set();
|
|
3534
|
+
const affectedWorkspaceCwds = new Set([targetPath]);
|
|
3407
3535
|
const agents = this.agentManager.listAgents();
|
|
3408
3536
|
for (const agent of agents) {
|
|
3409
3537
|
if (this.isPathWithinRoot(targetPath, agent.cwd)) {
|
|
3410
3538
|
removedAgents.add(agent.id);
|
|
3539
|
+
affectedWorkspaceCwds.add(agent.cwd);
|
|
3411
3540
|
try {
|
|
3412
3541
|
await this.agentManager.closeAgent(agent.id);
|
|
3413
3542
|
}
|
|
@@ -3426,6 +3555,7 @@ export class Session {
|
|
|
3426
3555
|
for (const record of registryRecords) {
|
|
3427
3556
|
if (this.isPathWithinRoot(targetPath, record.cwd)) {
|
|
3428
3557
|
removedAgents.add(record.id);
|
|
3558
|
+
affectedWorkspaceCwds.add(record.cwd);
|
|
3429
3559
|
try {
|
|
3430
3560
|
await this.agentStorage.remove(record.id);
|
|
3431
3561
|
}
|
|
@@ -3449,6 +3579,7 @@ export class Session {
|
|
|
3449
3579
|
},
|
|
3450
3580
|
});
|
|
3451
3581
|
}
|
|
3582
|
+
await this.emitWorkspaceUpdatesForCwds(affectedWorkspaceCwds);
|
|
3452
3583
|
return Array.from(removedAgents);
|
|
3453
3584
|
}
|
|
3454
3585
|
async handlePaseoWorktreeArchiveRequest(msg) {
|
|
@@ -3517,38 +3648,36 @@ export class Session {
|
|
|
3517
3648
|
}
|
|
3518
3649
|
}
|
|
3519
3650
|
/**
|
|
3520
|
-
* Handle read-only file explorer requests scoped to
|
|
3651
|
+
* Handle read-only file explorer requests scoped to a workspace cwd
|
|
3521
3652
|
*/
|
|
3522
3653
|
async handleFileExplorerRequest(request) {
|
|
3523
|
-
const {
|
|
3524
|
-
|
|
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
|
+
}
|
|
3525
3671
|
try {
|
|
3526
|
-
const agents = this.agentManager.listAgents();
|
|
3527
|
-
const agent = agents.find((a) => a.id === agentId);
|
|
3528
|
-
if (!agent) {
|
|
3529
|
-
this.emit({
|
|
3530
|
-
type: 'file_explorer_response',
|
|
3531
|
-
payload: {
|
|
3532
|
-
agentId,
|
|
3533
|
-
path: requestedPath,
|
|
3534
|
-
mode,
|
|
3535
|
-
directory: null,
|
|
3536
|
-
file: null,
|
|
3537
|
-
error: `Agent not found: ${agentId}`,
|
|
3538
|
-
requestId,
|
|
3539
|
-
},
|
|
3540
|
-
});
|
|
3541
|
-
return;
|
|
3542
|
-
}
|
|
3543
3672
|
if (mode === 'list') {
|
|
3544
3673
|
const directory = await listDirectoryEntries({
|
|
3545
|
-
root:
|
|
3674
|
+
root: cwd,
|
|
3546
3675
|
relativePath: requestedPath,
|
|
3547
3676
|
});
|
|
3548
3677
|
this.emit({
|
|
3549
3678
|
type: 'file_explorer_response',
|
|
3550
3679
|
payload: {
|
|
3551
|
-
|
|
3680
|
+
cwd,
|
|
3552
3681
|
path: directory.path,
|
|
3553
3682
|
mode,
|
|
3554
3683
|
directory,
|
|
@@ -3560,13 +3689,13 @@ export class Session {
|
|
|
3560
3689
|
}
|
|
3561
3690
|
else {
|
|
3562
3691
|
const file = await readExplorerFile({
|
|
3563
|
-
root:
|
|
3692
|
+
root: cwd,
|
|
3564
3693
|
relativePath: requestedPath,
|
|
3565
3694
|
});
|
|
3566
3695
|
this.emit({
|
|
3567
3696
|
type: 'file_explorer_response',
|
|
3568
3697
|
payload: {
|
|
3569
|
-
|
|
3698
|
+
cwd,
|
|
3570
3699
|
path: file.path,
|
|
3571
3700
|
mode,
|
|
3572
3701
|
directory: null,
|
|
@@ -3578,11 +3707,11 @@ export class Session {
|
|
|
3578
3707
|
}
|
|
3579
3708
|
}
|
|
3580
3709
|
catch (error) {
|
|
3581
|
-
this.sessionLogger.error({ err: error,
|
|
3710
|
+
this.sessionLogger.error({ err: error, cwd, path: requestedPath }, `Failed to fulfill file explorer request for workspace ${cwd}`);
|
|
3582
3711
|
this.emit({
|
|
3583
3712
|
type: 'file_explorer_response',
|
|
3584
3713
|
payload: {
|
|
3585
|
-
|
|
3714
|
+
cwd,
|
|
3586
3715
|
path: requestedPath,
|
|
3587
3716
|
mode,
|
|
3588
3717
|
directory: null,
|
|
@@ -3623,36 +3752,34 @@ export class Session {
|
|
|
3623
3752
|
}
|
|
3624
3753
|
}
|
|
3625
3754
|
/**
|
|
3626
|
-
* Handle file download token request scoped to
|
|
3755
|
+
* Handle file download token request scoped to a workspace cwd
|
|
3627
3756
|
*/
|
|
3628
3757
|
async handleFileDownloadTokenRequest(request) {
|
|
3629
|
-
const {
|
|
3630
|
-
|
|
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})`);
|
|
3631
3777
|
try {
|
|
3632
|
-
const agents = this.agentManager.listAgents();
|
|
3633
|
-
const agent = agents.find((a) => a.id === agentId);
|
|
3634
|
-
if (!agent) {
|
|
3635
|
-
this.emit({
|
|
3636
|
-
type: 'file_download_token_response',
|
|
3637
|
-
payload: {
|
|
3638
|
-
agentId,
|
|
3639
|
-
path: requestedPath,
|
|
3640
|
-
token: null,
|
|
3641
|
-
fileName: null,
|
|
3642
|
-
mimeType: null,
|
|
3643
|
-
size: null,
|
|
3644
|
-
error: `Agent not found: ${agentId}`,
|
|
3645
|
-
requestId,
|
|
3646
|
-
},
|
|
3647
|
-
});
|
|
3648
|
-
return;
|
|
3649
|
-
}
|
|
3650
3778
|
const info = await getDownloadableFileInfo({
|
|
3651
|
-
root:
|
|
3779
|
+
root: cwd,
|
|
3652
3780
|
relativePath: requestedPath,
|
|
3653
3781
|
});
|
|
3654
3782
|
const entry = this.downloadTokenStore.issueToken({
|
|
3655
|
-
agentId,
|
|
3656
3783
|
path: info.path,
|
|
3657
3784
|
absolutePath: info.absolutePath,
|
|
3658
3785
|
fileName: info.fileName,
|
|
@@ -3662,7 +3789,7 @@ export class Session {
|
|
|
3662
3789
|
this.emit({
|
|
3663
3790
|
type: 'file_download_token_response',
|
|
3664
3791
|
payload: {
|
|
3665
|
-
|
|
3792
|
+
cwd,
|
|
3666
3793
|
path: info.path,
|
|
3667
3794
|
token: entry.token,
|
|
3668
3795
|
fileName: entry.fileName,
|
|
@@ -3674,11 +3801,11 @@ export class Session {
|
|
|
3674
3801
|
});
|
|
3675
3802
|
}
|
|
3676
3803
|
catch (error) {
|
|
3677
|
-
this.sessionLogger.error({ err: error,
|
|
3804
|
+
this.sessionLogger.error({ err: error, cwd, path: requestedPath }, `Failed to issue download token for workspace ${cwd}`);
|
|
3678
3805
|
this.emit({
|
|
3679
3806
|
type: 'file_download_token_response',
|
|
3680
3807
|
payload: {
|
|
3681
|
-
|
|
3808
|
+
cwd,
|
|
3682
3809
|
path: requestedPath,
|
|
3683
3810
|
token: null,
|
|
3684
3811
|
fileName: null,
|
|
@@ -3785,9 +3912,9 @@ export class Session {
|
|
|
3785
3912
|
return deduped.length > 0 ? deduped : fallback;
|
|
3786
3913
|
}
|
|
3787
3914
|
getStatusPriority(agent) {
|
|
3788
|
-
const requiresAttention = agent.requiresAttention ?? false;
|
|
3789
3915
|
const attentionReason = agent.attentionReason ?? null;
|
|
3790
|
-
|
|
3916
|
+
const hasPendingPermission = (agent.pendingPermissions?.length ?? 0) > 0;
|
|
3917
|
+
if (hasPendingPermission || attentionReason === 'permission') {
|
|
3791
3918
|
return 0;
|
|
3792
3919
|
}
|
|
3793
3920
|
if (agent.status === 'error' || attentionReason === 'error') {
|
|
@@ -3982,6 +4109,339 @@ export class Session {
|
|
|
3982
4109
|
},
|
|
3983
4110
|
};
|
|
3984
4111
|
}
|
|
4112
|
+
normalizeWorkspaceId(cwd) {
|
|
4113
|
+
const trimmed = cwd.trim();
|
|
4114
|
+
if (!trimmed) {
|
|
4115
|
+
return cwd;
|
|
4116
|
+
}
|
|
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;
|
|
4351
|
+
const pagedEntries = entries.slice(0, limit);
|
|
4352
|
+
const hasMore = entries.length > limit;
|
|
4353
|
+
const nextCursor = hasMore && pagedEntries.length > 0
|
|
4354
|
+
? this.encodeFetchWorkspacesCursor(pagedEntries[pagedEntries.length - 1], sort)
|
|
4355
|
+
: null;
|
|
4356
|
+
return {
|
|
4357
|
+
entries: pagedEntries,
|
|
4358
|
+
pageInfo: {
|
|
4359
|
+
nextCursor,
|
|
4360
|
+
prevCursor: request.page?.cursor ?? null,
|
|
4361
|
+
hasMore,
|
|
4362
|
+
},
|
|
4363
|
+
};
|
|
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
|
+
}
|
|
3985
4445
|
async handleFetchAgents(request) {
|
|
3986
4446
|
const requestedSubscriptionId = request.subscribe?.subscriptionId?.trim();
|
|
3987
4447
|
const subscriptionId = request.subscribe
|
|
@@ -4036,6 +4496,62 @@ export class Session {
|
|
|
4036
4496
|
});
|
|
4037
4497
|
}
|
|
4038
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
|
+
}
|
|
4039
4555
|
async handleFetchAgent(agentIdOrIdentifier, requestId) {
|
|
4040
4556
|
const resolved = await this.resolveAgentIdentifier(agentIdOrIdentifier);
|
|
4041
4557
|
if (!resolved.ok) {
|
|
@@ -4067,7 +4583,12 @@ export class Session {
|
|
|
4067
4583
|
async handleFetchAgentTimelineRequest(msg) {
|
|
4068
4584
|
const direction = msg.direction ?? (msg.cursor ? 'after' : 'tail');
|
|
4069
4585
|
const projection = msg.projection ?? 'projected';
|
|
4070
|
-
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;
|
|
4071
4592
|
const cursor = msg.cursor
|
|
4072
4593
|
? {
|
|
4073
4594
|
epoch: msg.cursor.epoch,
|
|
@@ -4076,21 +4597,81 @@ export class Session {
|
|
|
4076
4597
|
: undefined;
|
|
4077
4598
|
try {
|
|
4078
4599
|
const snapshot = await this.ensureAgentLoaded(msg.agentId);
|
|
4079
|
-
const
|
|
4600
|
+
const agentPayload = await this.buildAgentPayload(snapshot);
|
|
4601
|
+
let timeline = this.agentManager.fetchTimeline(msg.agentId, {
|
|
4080
4602
|
direction,
|
|
4081
4603
|
cursor,
|
|
4082
|
-
limit
|
|
4083
|
-
|
|
4084
|
-
|
|
4085
|
-
|
|
4086
|
-
|
|
4087
|
-
|
|
4088
|
-
|
|
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
|
+
}
|
|
4089
4669
|
this.emit({
|
|
4090
4670
|
type: 'fetch_agent_timeline_response',
|
|
4091
4671
|
payload: {
|
|
4092
4672
|
requestId: msg.requestId,
|
|
4093
4673
|
agentId: msg.agentId,
|
|
4674
|
+
agent: agentPayload,
|
|
4094
4675
|
direction,
|
|
4095
4676
|
projection,
|
|
4096
4677
|
epoch: timeline.epoch,
|
|
@@ -4100,9 +4681,9 @@ export class Session {
|
|
|
4100
4681
|
window: timeline.window,
|
|
4101
4682
|
startCursor,
|
|
4102
4683
|
endCursor,
|
|
4103
|
-
hasOlder
|
|
4104
|
-
hasNewer
|
|
4105
|
-
entries
|
|
4684
|
+
hasOlder,
|
|
4685
|
+
hasNewer,
|
|
4686
|
+
entries,
|
|
4106
4687
|
error: null,
|
|
4107
4688
|
},
|
|
4108
4689
|
});
|
|
@@ -4114,6 +4695,7 @@ export class Session {
|
|
|
4114
4695
|
payload: {
|
|
4115
4696
|
requestId: msg.requestId,
|
|
4116
4697
|
agentId: msg.agentId,
|
|
4698
|
+
agent: null,
|
|
4117
4699
|
direction,
|
|
4118
4700
|
projection,
|
|
4119
4701
|
epoch: '',
|
|
@@ -4147,19 +4729,7 @@ export class Session {
|
|
|
4147
4729
|
}
|
|
4148
4730
|
try {
|
|
4149
4731
|
const agentId = resolved.agentId;
|
|
4150
|
-
|
|
4151
|
-
if (archivedAt) {
|
|
4152
|
-
this.emit({
|
|
4153
|
-
type: 'send_agent_message_response',
|
|
4154
|
-
payload: {
|
|
4155
|
-
requestId: msg.requestId,
|
|
4156
|
-
agentId,
|
|
4157
|
-
accepted: false,
|
|
4158
|
-
error: `Agent ${agentId} is archived`,
|
|
4159
|
-
},
|
|
4160
|
-
});
|
|
4161
|
-
return;
|
|
4162
|
-
}
|
|
4732
|
+
await this.unarchiveAgentState(agentId);
|
|
4163
4733
|
await this.ensureAgentLoaded(agentId);
|
|
4164
4734
|
await this.interruptAgentIfRunning(agentId);
|
|
4165
4735
|
try {
|
|
@@ -4279,10 +4849,12 @@ export class Session {
|
|
|
4279
4849
|
return;
|
|
4280
4850
|
}
|
|
4281
4851
|
const abortController = new AbortController();
|
|
4282
|
-
const
|
|
4283
|
-
const timeoutHandle =
|
|
4284
|
-
|
|
4285
|
-
|
|
4852
|
+
const hasTimeout = typeof timeoutMs === 'number' && timeoutMs > 0;
|
|
4853
|
+
const timeoutHandle = hasTimeout
|
|
4854
|
+
? setTimeout(() => {
|
|
4855
|
+
abortController.abort('timeout');
|
|
4856
|
+
}, timeoutMs)
|
|
4857
|
+
: null;
|
|
4286
4858
|
try {
|
|
4287
4859
|
let result = await this.agentManager.waitForAgentEvent(agentId, {
|
|
4288
4860
|
signal: abortController.signal,
|
|
@@ -4330,7 +4902,9 @@ export class Session {
|
|
|
4330
4902
|
});
|
|
4331
4903
|
}
|
|
4332
4904
|
finally {
|
|
4333
|
-
|
|
4905
|
+
if (timeoutHandle) {
|
|
4906
|
+
clearTimeout(timeoutHandle);
|
|
4907
|
+
}
|
|
4334
4908
|
}
|
|
4335
4909
|
}
|
|
4336
4910
|
/**
|
|
@@ -4603,7 +5177,7 @@ export class Session {
|
|
|
4603
5177
|
});
|
|
4604
5178
|
});
|
|
4605
5179
|
this.registerVoiceCallerContext?.(agentId, {
|
|
4606
|
-
childAgentDefaultLabels: {
|
|
5180
|
+
childAgentDefaultLabels: {},
|
|
4607
5181
|
allowCustomCwd: false,
|
|
4608
5182
|
enableVoiceTools: true,
|
|
4609
5183
|
});
|
|
@@ -4827,7 +5401,6 @@ export class Session {
|
|
|
4827
5401
|
if (this.agentMcpClient) {
|
|
4828
5402
|
try {
|
|
4829
5403
|
await this.agentMcpClient.close();
|
|
4830
|
-
this.sessionLogger.debug('Agent MCP client closed');
|
|
4831
5404
|
}
|
|
4832
5405
|
catch (error) {
|
|
4833
5406
|
this.sessionLogger.error({ err: error }, 'Failed to close Agent MCP client');
|
|
@@ -4923,17 +5496,11 @@ export class Session {
|
|
|
4923
5496
|
if (!this.terminalManager || !this.subscribedTerminalDirectories.has(cwd)) {
|
|
4924
5497
|
return;
|
|
4925
5498
|
}
|
|
4926
|
-
const hadDirectoryBeforeSubscribe = this.terminalManager.listDirectories().includes(cwd);
|
|
4927
5499
|
try {
|
|
4928
5500
|
const terminals = await this.terminalManager.getTerminals(cwd);
|
|
4929
5501
|
for (const terminal of terminals) {
|
|
4930
5502
|
this.ensureTerminalExitSubscription(terminal);
|
|
4931
5503
|
}
|
|
4932
|
-
// New directories auto-create Terminal 1, which already emits through
|
|
4933
|
-
// terminal-manager change listeners.
|
|
4934
|
-
if (!hadDirectoryBeforeSubscribe) {
|
|
4935
|
-
return;
|
|
4936
|
-
}
|
|
4937
5504
|
if (!this.subscribedTerminalDirectories.has(cwd)) {
|
|
4938
5505
|
return;
|
|
4939
5506
|
}
|
|
@@ -5206,10 +5773,16 @@ export class Session {
|
|
|
5206
5773
|
}
|
|
5207
5774
|
const existingStreamId = this.terminalStreamByTerminalId.get(msg.terminalId);
|
|
5208
5775
|
if (typeof existingStreamId === 'number') {
|
|
5209
|
-
|
|
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 });
|
|
5210
5780
|
}
|
|
5211
5781
|
const streamId = this.allocateTerminalStreamId();
|
|
5212
|
-
const
|
|
5782
|
+
const requestedResumeOffset = typeof msg.resumeOffset === 'number'
|
|
5783
|
+
? msg.resumeOffset
|
|
5784
|
+
: session.getOutputOffset();
|
|
5785
|
+
const initialOffset = Math.max(0, Math.floor(requestedResumeOffset));
|
|
5213
5786
|
const binding = {
|
|
5214
5787
|
terminalId: msg.terminalId,
|
|
5215
5788
|
unsubscribe: () => { },
|
|
@@ -5233,7 +5806,7 @@ export class Session {
|
|
|
5233
5806
|
endOffset: chunk.endOffset,
|
|
5234
5807
|
replay: chunk.replay,
|
|
5235
5808
|
});
|
|
5236
|
-
}, { fromOffset:
|
|
5809
|
+
}, { fromOffset: requestedResumeOffset });
|
|
5237
5810
|
}
|
|
5238
5811
|
catch (error) {
|
|
5239
5812
|
this.terminalStreams.delete(streamId);
|