@camerontaylor/paseo-server 0.8.0-fork.1
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/.env.example +31 -0
- package/README.md +111 -0
- package/dist/scripts/dev-runner.js +37 -0
- package/dist/scripts/mcp-stdio-socket-bridge-cli.mjs +62 -0
- package/dist/scripts/supervisor-entrypoint.js +160 -0
- package/dist/scripts/supervisor-log-config.js +31 -0
- package/dist/scripts/supervisor.js +313 -0
- package/dist/server/executable-resolution/executable-resolution.d.ts +10 -0
- package/dist/server/executable-resolution/executable-resolution.js +121 -0
- package/dist/server/executable-resolution/windows.d.ts +18 -0
- package/dist/server/executable-resolution/windows.js +65 -0
- package/dist/server/server/agent/activity-curator.d.ts +40 -0
- package/dist/server/server/agent/activity-curator.js +243 -0
- package/dist/server/server/agent/agent-archive.d.ts +11 -0
- package/dist/server/server/agent/agent-archive.js +16 -0
- package/dist/server/server/agent/agent-configuration-validator.d.ts +18 -0
- package/dist/server/server/agent/agent-configuration-validator.js +42 -0
- package/dist/server/server/agent/agent-loading.d.ts +17 -0
- package/dist/server/server/agent/agent-loading.js +88 -0
- package/dist/server/server/agent/agent-manager.d.ts +555 -0
- package/dist/server/server/agent/agent-manager.js +3930 -0
- package/dist/server/server/agent/agent-owner.d.ts +12 -0
- package/dist/server/server/agent/agent-owner.js +12 -0
- package/dist/server/server/agent/agent-projections.d.ts +26 -0
- package/dist/server/server/agent/agent-projections.js +414 -0
- package/dist/server/server/agent/agent-prompt.d.ts +91 -0
- package/dist/server/server/agent/agent-prompt.js +359 -0
- package/dist/server/server/agent/agent-response-loop.d.ts +72 -0
- package/dist/server/server/agent/agent-response-loop.js +336 -0
- package/dist/server/server/agent/agent-run-state.d.ts +76 -0
- package/dist/server/server/agent/agent-run-state.js +212 -0
- package/dist/server/server/agent/agent-sdk-types.d.ts +742 -0
- package/dist/server/server/agent/agent-sdk-types.js +25 -0
- package/dist/server/server/agent/agent-storage.d.ts +148 -0
- package/dist/server/server/agent/agent-storage.js +401 -0
- package/dist/server/server/agent/agent-stream-coalescer.d.ts +43 -0
- package/dist/server/server/agent/agent-stream-coalescer.js +187 -0
- package/dist/server/server/agent/agent-timeline-content.d.ts +6 -0
- package/dist/server/server/agent/agent-timeline-content.js +62 -0
- package/dist/server/server/agent/agent-timeline-store-types.d.ts +54 -0
- package/dist/server/server/agent/agent-timeline-store-types.js +2 -0
- package/dist/server/server/agent/agent-timeline-store.d.ts +31 -0
- package/dist/server/server/agent/agent-timeline-store.js +249 -0
- package/dist/server/server/agent/audio-utils.d.ts +3 -0
- package/dist/server/server/agent/audio-utils.js +19 -0
- package/dist/server/server/agent/create-agent/create.d.ts +102 -0
- package/dist/server/server/agent/create-agent/create.js +362 -0
- package/dist/server/server/agent/create-agent/intent.d.ts +25 -0
- package/dist/server/server/agent/create-agent/intent.js +30 -0
- package/dist/server/server/agent/create-agent-lifecycle-dispatch.d.ts +65 -0
- package/dist/server/server/agent/create-agent-lifecycle-dispatch.js +166 -0
- package/dist/server/server/agent/create-agent-mode.d.ts +13 -0
- package/dist/server/server/agent/create-agent-mode.js +65 -0
- package/dist/server/server/agent/create-agent-title.d.ts +10 -0
- package/dist/server/server/agent/create-agent-title.js +34 -0
- package/dist/server/server/agent/dictation-debug.d.ts +13 -0
- package/dist/server/server/agent/dictation-debug.js +50 -0
- package/dist/server/server/agent/import-sessions.d.ts +57 -0
- package/dist/server/server/agent/import-sessions.js +240 -0
- package/dist/server/server/agent/lifecycle-command.d.ts +73 -0
- package/dist/server/server/agent/lifecycle-command.js +99 -0
- package/dist/server/server/agent/mcp-server.d.ts +5 -0
- package/dist/server/server/agent/mcp-server.js +31 -0
- package/dist/server/server/agent/mcp-shared.d.ts +181 -0
- package/dist/server/server/agent/mcp-shared.js +199 -0
- package/dist/server/server/agent/mutable-provider-config-owner.d.ts +8 -0
- package/dist/server/server/agent/mutable-provider-config-owner.js +35 -0
- package/dist/server/server/agent/paseo-tool-policy.d.ts +9 -0
- package/dist/server/server/agent/paseo-tool-policy.js +16 -0
- package/dist/server/server/agent/pcm16-resampler.d.ts +14 -0
- package/dist/server/server/agent/pcm16-resampler.js +63 -0
- package/dist/server/server/agent/permission-response.d.ts +15 -0
- package/dist/server/server/agent/permission-response.js +14 -0
- package/dist/server/server/agent/plugin-provider.d.ts +16 -0
- package/dist/server/server/agent/plugin-provider.js +1356 -0
- package/dist/server/server/agent/prompt-attachments.d.ts +12 -0
- package/dist/server/server/agent/prompt-attachments.js +182 -0
- package/dist/server/server/agent/provider-history-timestamps.d.ts +2 -0
- package/dist/server/server/agent/provider-history-timestamps.js +16 -0
- package/dist/server/server/agent/provider-launch-config.d.ts +43 -0
- package/dist/server/server/agent/provider-launch-config.js +167 -0
- package/dist/server/server/agent/provider-notices.d.ts +4 -0
- package/dist/server/server/agent/provider-notices.js +9 -0
- package/dist/server/server/agent/provider-options.d.ts +19 -0
- package/dist/server/server/agent/provider-options.js +53 -0
- package/dist/server/server/agent/provider-refresh-deadline.d.ts +11 -0
- package/dist/server/server/agent/provider-refresh-deadline.js +65 -0
- package/dist/server/server/agent/provider-registry.d.ts +72 -0
- package/dist/server/server/agent/provider-registry.js +604 -0
- package/dist/server/server/agent/provider-runtime.d.ts +16 -0
- package/dist/server/server/agent/provider-runtime.js +35 -0
- package/dist/server/server/agent/provider-session-import.d.ts +10 -0
- package/dist/server/server/agent/provider-session-import.js +55 -0
- package/dist/server/server/agent/provider-snapshot-manager.d.ts +166 -0
- package/dist/server/server/agent/provider-snapshot-manager.js +843 -0
- package/dist/server/server/agent/provider-subagents/store.d.ts +68 -0
- package/dist/server/server/agent/provider-subagents/store.js +111 -0
- package/dist/server/server/agent/providers/acp-agent.d.ts +703 -0
- package/dist/server/server/agent/providers/acp-agent.js +3412 -0
- package/dist/server/server/agent/providers/claude/agent.d.ts +74 -0
- package/dist/server/server/agent/providers/claude/agent.js +5236 -0
- package/dist/server/server/agent/providers/claude/feature-definitions.d.ts +8 -0
- package/dist/server/server/agent/providers/claude/feature-definitions.js +24 -0
- package/dist/server/server/agent/providers/claude/hooks.d.ts +12 -0
- package/dist/server/server/agent/providers/claude/hooks.js +20 -0
- package/dist/server/server/agent/providers/claude/model-manifest.d.ts +147 -0
- package/dist/server/server/agent/providers/claude/model-manifest.js +342 -0
- package/dist/server/server/agent/providers/claude/models.d.ts +28 -0
- package/dist/server/server/agent/providers/claude/models.js +141 -0
- package/dist/server/server/agent/providers/claude/options.d.ts +85 -0
- package/dist/server/server/agent/providers/claude/options.js +93 -0
- package/dist/server/server/agent/providers/claude/partial-json.d.ts +5 -0
- package/dist/server/server/agent/providers/claude/partial-json.js +306 -0
- package/dist/server/server/agent/providers/claude/project-dir.d.ts +6 -0
- package/dist/server/server/agent/providers/claude/project-dir.js +57 -0
- package/dist/server/server/agent/providers/claude/query.d.ts +17 -0
- package/dist/server/server/agent/providers/claude/query.js +86 -0
- package/dist/server/server/agent/providers/claude/rewind.d.ts +30 -0
- package/dist/server/server/agent/providers/claude/rewind.js +30 -0
- package/dist/server/server/agent/providers/claude/side-question.d.ts +16 -0
- package/dist/server/server/agent/providers/claude/side-question.js +57 -0
- package/dist/server/server/agent/providers/claude/sidechain-tracker.d.ts +35 -0
- package/dist/server/server/agent/providers/claude/sidechain-tracker.js +392 -0
- package/dist/server/server/agent/providers/claude/subagents/live-source.d.ts +161 -0
- package/dist/server/server/agent/providers/claude/subagents/live-source.js +415 -0
- package/dist/server/server/agent/providers/claude/subagents/observation.d.ts +53 -0
- package/dist/server/server/agent/providers/claude/subagents/observation.js +57 -0
- package/dist/server/server/agent/providers/claude/subagents/presentation.d.ts +12 -0
- package/dist/server/server/agent/providers/claude/subagents/presentation.js +41 -0
- package/dist/server/server/agent/providers/claude/subagents/replay-source.d.ts +70 -0
- package/dist/server/server/agent/providers/claude/subagents/replay-source.js +305 -0
- package/dist/server/server/agent/providers/claude/subagents/workflow-output.d.ts +4 -0
- package/dist/server/server/agent/providers/claude/subagents/workflow-output.js +86 -0
- package/dist/server/server/agent/providers/claude/subagents/workflow-replay-source.d.ts +38 -0
- package/dist/server/server/agent/providers/claude/subagents/workflow-replay-source.js +225 -0
- package/dist/server/server/agent/providers/claude/task-notification-tool-call.d.ts +37 -0
- package/dist/server/server/agent/providers/claude/task-notification-tool-call.js +296 -0
- package/dist/server/server/agent/providers/claude/task-state.d.ts +18 -0
- package/dist/server/server/agent/providers/claude/task-state.js +185 -0
- package/dist/server/server/agent/providers/claude/test-rewind-claude-sdk.d.ts +24 -0
- package/dist/server/server/agent/providers/claude/test-rewind-claude-sdk.js +23 -0
- package/dist/server/server/agent/providers/claude/tool-call-detail-parser.d.ts +3 -0
- package/dist/server/server/agent/providers/claude/tool-call-detail-parser.js +130 -0
- package/dist/server/server/agent/providers/claude/tool-call-mapper.d.ts +16 -0
- package/dist/server/server/agent/providers/claude/tool-call-mapper.js +129 -0
- package/dist/server/server/agent/providers/codex/app-server-transport.d.ts +101 -0
- package/dist/server/server/agent/providers/codex/app-server-transport.js +290 -0
- package/dist/server/server/agent/providers/codex/async-questions.d.ts +18 -0
- package/dist/server/server/agent/providers/codex/async-questions.js +135 -0
- package/dist/server/server/agent/providers/codex/options.d.ts +59 -0
- package/dist/server/server/agent/providers/codex/options.js +82 -0
- package/dist/server/server/agent/providers/codex/rewind.d.ts +24 -0
- package/dist/server/server/agent/providers/codex/rewind.js +77 -0
- package/dist/server/server/agent/providers/codex/tool-call-detail-parser.d.ts +12 -0
- package/dist/server/server/agent/providers/codex/tool-call-detail-parser.js +104 -0
- package/dist/server/server/agent/providers/codex/tool-call-mapper.d.ts +24 -0
- package/dist/server/server/agent/providers/codex/tool-call-mapper.js +909 -0
- package/dist/server/server/agent/providers/codex-app-server-agent.d.ts +384 -0
- package/dist/server/server/agent/providers/codex-app-server-agent.js +5877 -0
- package/dist/server/server/agent/providers/codex-feature-definitions.d.ts +11 -0
- package/dist/server/server/agent/providers/codex-feature-definitions.js +55 -0
- package/dist/server/server/agent/providers/copilot-acp-agent.d.ts +26 -0
- package/dist/server/server/agent/providers/copilot-acp-agent.js +187 -0
- package/dist/server/server/agent/providers/cursor-acp-agent.d.ts +17 -0
- package/dist/server/server/agent/providers/cursor-acp-agent.js +31 -0
- package/dist/server/server/agent/providers/diagnostic-utils.d.ts +36 -0
- package/dist/server/server/agent/providers/diagnostic-utils.js +356 -0
- package/dist/server/server/agent/providers/generic-acp-agent.d.ts +55 -0
- package/dist/server/server/agent/providers/generic-acp-agent.js +178 -0
- package/dist/server/server/agent/providers/gjc-acp-agent.d.ts +155 -0
- package/dist/server/server/agent/providers/gjc-acp-agent.js +588 -0
- package/dist/server/server/agent/providers/jsonl-frame-decoder.d.ts +17 -0
- package/dist/server/server/agent/providers/jsonl-frame-decoder.js +149 -0
- package/dist/server/server/agent/providers/jsonl-rpc-process.d.ts +63 -0
- package/dist/server/server/agent/providers/jsonl-rpc-process.js +207 -0
- package/dist/server/server/agent/providers/kimi-acp-agent.d.ts +35 -0
- package/dist/server/server/agent/providers/kimi-acp-agent.js +65 -0
- package/dist/server/server/agent/providers/kiro-acp-agent.d.ts +17 -0
- package/dist/server/server/agent/providers/kiro-acp-agent.js +78 -0
- package/dist/server/server/agent/providers/mock-load-test-agent.d.ts +111 -0
- package/dist/server/server/agent/providers/mock-load-test-agent.js +1340 -0
- package/dist/server/server/agent/providers/mock-slow-provider.d.ts +14 -0
- package/dist/server/server/agent/providers/mock-slow-provider.js +40 -0
- package/dist/server/server/agent/providers/omp/advisor-message.d.ts +11 -0
- package/dist/server/server/agent/providers/omp/advisor-message.js +80 -0
- package/dist/server/server/agent/providers/omp/agent.d.ts +178 -0
- package/dist/server/server/agent/providers/omp/agent.js +1952 -0
- package/dist/server/server/agent/providers/omp/cli-runtime.d.ts +22 -0
- package/dist/server/server/agent/providers/omp/cli-runtime.js +208 -0
- package/dist/server/server/agent/providers/omp/commands.d.ts +8 -0
- package/dist/server/server/agent/providers/omp/commands.js +62 -0
- package/dist/server/server/agent/providers/omp/custom-message.d.ts +7 -0
- package/dist/server/server/agent/providers/omp/custom-message.js +4 -0
- package/dist/server/server/agent/providers/omp/event-mapper.d.ts +13 -0
- package/dist/server/server/agent/providers/omp/event-mapper.js +267 -0
- package/dist/server/server/agent/providers/omp/history-hooks.d.ts +3 -0
- package/dist/server/server/agent/providers/omp/history-hooks.js +13 -0
- package/dist/server/server/agent/providers/omp/history.d.ts +19 -0
- package/dist/server/server/agent/providers/omp/history.js +306 -0
- package/dist/server/server/agent/providers/omp/host-tools.d.ts +14 -0
- package/dist/server/server/agent/providers/omp/host-tools.js +196 -0
- package/dist/server/server/agent/providers/omp/map-omp-model.d.ts +11 -0
- package/dist/server/server/agent/providers/omp/map-omp-model.js +67 -0
- package/dist/server/server/agent/providers/omp/message-history.d.ts +38 -0
- package/dist/server/server/agent/providers/omp/message-history.js +216 -0
- package/dist/server/server/agent/providers/omp/protocol-session.d.ts +13 -0
- package/dist/server/server/agent/providers/omp/protocol-session.js +39 -0
- package/dist/server/server/agent/providers/omp/provider-config.d.ts +43 -0
- package/dist/server/server/agent/providers/omp/provider-config.js +120 -0
- package/dist/server/server/agent/providers/omp/rpc-types.d.ts +2040 -0
- package/dist/server/server/agent/providers/omp/rpc-types.js +551 -0
- package/dist/server/server/agent/providers/omp/rpc-ui-permission-mapper.d.ts +24 -0
- package/dist/server/server/agent/providers/omp/rpc-ui-permission-mapper.js +156 -0
- package/dist/server/server/agent/providers/omp/runtime.d.ts +85 -0
- package/dist/server/server/agent/providers/omp/runtime.js +62 -0
- package/dist/server/server/agent/providers/omp/session-descriptor.d.ts +16 -0
- package/dist/server/server/agent/providers/omp/session-descriptor.js +389 -0
- package/dist/server/server/agent/providers/omp/subagent-card-tracker.d.ts +33 -0
- package/dist/server/server/agent/providers/omp/subagent-card-tracker.js +264 -0
- package/dist/server/server/agent/providers/omp/subagent-index.d.ts +13 -0
- package/dist/server/server/agent/providers/omp/subagent-index.js +111 -0
- package/dist/server/server/agent/providers/omp/subagent-title.d.ts +2 -0
- package/dist/server/server/agent/providers/omp/subagent-title.js +14 -0
- package/dist/server/server/agent/providers/omp/system-notice.d.ts +8 -0
- package/dist/server/server/agent/providers/omp/system-notice.js +68 -0
- package/dist/server/server/agent/providers/omp/todo-mapper.d.ts +9 -0
- package/dist/server/server/agent/providers/omp/todo-mapper.js +52 -0
- package/dist/server/server/agent/providers/omp/tool-call-detail.d.ts +110 -0
- package/dist/server/server/agent/providers/omp/tool-call-detail.js +288 -0
- package/dist/server/server/agent/providers/omp/tool-call-id.d.ts +4 -0
- package/dist/server/server/agent/providers/omp/tool-call-id.js +34 -0
- package/dist/server/server/agent/providers/omp/tool-call-mapper.d.ts +7 -0
- package/dist/server/server/agent/providers/omp/tool-call-mapper.js +98 -0
- package/dist/server/server/agent/providers/omp/usage-poller.d.ts +32 -0
- package/dist/server/server/agent/providers/omp/usage-poller.js +141 -0
- package/dist/server/server/agent/providers/opencode/bridge-plugin.bundle.mjs +14678 -0
- package/dist/server/server/agent/providers/opencode/bridge.d.ts +39 -0
- package/dist/server/server/agent/providers/opencode/bridge.js +249 -0
- package/dist/server/server/agent/providers/opencode/event-consumer.d.ts +60 -0
- package/dist/server/server/agent/providers/opencode/event-consumer.js +221 -0
- package/dist/server/server/agent/providers/opencode/options.d.ts +156 -0
- package/dist/server/server/agent/providers/opencode/options.js +62 -0
- package/dist/server/server/agent/providers/opencode/paths.d.ts +2 -0
- package/dist/server/server/agent/providers/opencode/paths.js +7 -0
- package/dist/server/server/agent/providers/opencode/rewind.d.ts +18 -0
- package/dist/server/server/agent/providers/opencode/rewind.js +14 -0
- package/dist/server/server/agent/providers/opencode/server-manager.d.ts +101 -0
- package/dist/server/server/agent/providers/opencode/server-manager.js +513 -0
- package/dist/server/server/agent/providers/opencode/side-question.d.ts +78 -0
- package/dist/server/server/agent/providers/opencode/side-question.js +172 -0
- package/dist/server/server/agent/providers/opencode/subagent-presentation.d.ts +36 -0
- package/dist/server/server/agent/providers/opencode/subagent-presentation.js +62 -0
- package/dist/server/server/agent/providers/opencode/test-server-manager.d.ts +22 -0
- package/dist/server/server/agent/providers/opencode/test-server-manager.js +43 -0
- package/dist/server/server/agent/providers/opencode/tool-call-detail-parser.d.ts +3 -0
- package/dist/server/server/agent/providers/opencode/tool-call-detail-parser.js +256 -0
- package/dist/server/server/agent/providers/opencode/tool-call-mapper.d.ts +13 -0
- package/dist/server/server/agent/providers/opencode/tool-call-mapper.js +53 -0
- package/dist/server/server/agent/providers/opencode-agent.d.ts +393 -0
- package/dist/server/server/agent/providers/opencode-agent.js +4106 -0
- package/dist/server/server/agent/providers/pi/agent.d.ts +164 -0
- package/dist/server/server/agent/providers/pi/agent.js +2236 -0
- package/dist/server/server/agent/providers/pi/cli-runtime.d.ts +21 -0
- package/dist/server/server/agent/providers/pi/cli-runtime.js +177 -0
- package/dist/server/server/agent/providers/pi/history-mapper.d.ts +36 -0
- package/dist/server/server/agent/providers/pi/history-mapper.js +212 -0
- package/dist/server/server/agent/providers/pi/rewind.d.ts +8 -0
- package/dist/server/server/agent/providers/pi/rewind.js +8 -0
- package/dist/server/server/agent/providers/pi/rpc-types.d.ts +252 -0
- package/dist/server/server/agent/providers/pi/rpc-types.js +2 -0
- package/dist/server/server/agent/providers/pi/runtime.d.ts +77 -0
- package/dist/server/server/agent/providers/pi/runtime.js +65 -0
- package/dist/server/server/agent/providers/pi/session-descriptor.d.ts +16 -0
- package/dist/server/server/agent/providers/pi/session-descriptor.js +355 -0
- package/dist/server/server/agent/providers/pi/tool-call-mapper.d.ts +113 -0
- package/dist/server/server/agent/providers/pi/tool-call-mapper.js +340 -0
- package/dist/server/server/agent/providers/pi/usage-poller.d.ts +32 -0
- package/dist/server/server/agent/providers/pi/usage-poller.js +141 -0
- package/dist/server/server/agent/providers/provider-image-output.d.ts +25 -0
- package/dist/server/server/agent/providers/provider-image-output.js +165 -0
- package/dist/server/server/agent/providers/provider-runner.d.ts +27 -0
- package/dist/server/server/agent/providers/provider-runner.js +81 -0
- package/dist/server/server/agent/providers/tool-call-detail-primitives.d.ts +884 -0
- package/dist/server/server/agent/providers/tool-call-detail-primitives.js +778 -0
- package/dist/server/server/agent/providers/tool-call-mapper-utils.d.ts +25 -0
- package/dist/server/server/agent/providers/tool-call-mapper-utils.js +208 -0
- package/dist/server/server/agent/providers/trae-acp-agent.d.ts +15 -0
- package/dist/server/server/agent/providers/trae-acp-agent.js +18 -0
- package/dist/server/server/agent/recordings-debug.d.ts +3 -0
- package/dist/server/server/agent/recordings-debug.js +19 -0
- package/dist/server/server/agent/rewind/rewind.d.ts +10 -0
- package/dist/server/server/agent/rewind/rewind.js +29 -0
- package/dist/server/server/agent/rewind/test-rewind-session.d.ts +48 -0
- package/dist/server/server/agent/rewind/test-rewind-session.js +88 -0
- package/dist/server/server/agent/runtime-mcp-config.d.ts +14 -0
- package/dist/server/server/agent/runtime-mcp-config.js +53 -0
- package/dist/server/server/agent/side-conversations/store.d.ts +33 -0
- package/dist/server/server/agent/side-conversations/store.js +62 -0
- package/dist/server/server/agent/stale-provider-session-error.d.ts +6 -0
- package/dist/server/server/agent/stale-provider-session-error.js +11 -0
- package/dist/server/server/agent/structured-generation-providers.d.ts +29 -0
- package/dist/server/server/agent/structured-generation-providers.js +193 -0
- package/dist/server/server/agent/stt-debug.d.ts +10 -0
- package/dist/server/server/agent/stt-debug.js +33 -0
- package/dist/server/server/agent/stt-manager.d.ts +38 -0
- package/dist/server/server/agent/stt-manager.js +246 -0
- package/dist/server/server/agent/system-prompt.d.ts +2 -0
- package/dist/server/server/agent/system-prompt.js +8 -0
- package/dist/server/server/agent/timeline-append.d.ts +10 -0
- package/dist/server/server/agent/timeline-append.js +27 -0
- package/dist/server/server/agent/timeline-page-bounds.d.ts +19 -0
- package/dist/server/server/agent/timeline-page-bounds.js +37 -0
- package/dist/server/server/agent/timeline-projection.d.ts +74 -0
- package/dist/server/server/agent/timeline-projection.js +467 -0
- package/dist/server/server/agent/timeline-prompt-index.d.ts +12 -0
- package/dist/server/server/agent/timeline-prompt-index.js +23 -0
- package/dist/server/server/agent/tools/paseo-tool-serialization.d.ts +4 -0
- package/dist/server/server/agent/tools/paseo-tool-serialization.js +58 -0
- package/dist/server/server/agent/tools/paseo-tools.d.ts +62 -0
- package/dist/server/server/agent/tools/paseo-tools.js +2445 -0
- package/dist/server/server/agent/tools/types.d.ts +39 -0
- package/dist/server/server/agent/tools/types.js +2 -0
- package/dist/server/server/agent/tts-debug.d.ts +8 -0
- package/dist/server/server/agent/tts-debug.js +24 -0
- package/dist/server/server/agent/tts-manager.d.ts +41 -0
- package/dist/server/server/agent/tts-manager.js +375 -0
- package/dist/server/server/agent-attention-policy.d.ts +29 -0
- package/dist/server/server/agent-attention-policy.js +36 -0
- package/dist/server/server/agent-history-search.d.ts +48 -0
- package/dist/server/server/agent-history-search.js +129 -0
- package/dist/server/server/atomic-file.d.ts +3 -0
- package/dist/server/server/atomic-file.js +19 -0
- package/dist/server/server/auth.d.ts +38 -0
- package/dist/server/server/auth.js +122 -0
- package/dist/server/server/authorization/index.d.ts +18 -0
- package/dist/server/server/authorization/index.js +47 -0
- package/dist/server/server/authorization/operation-permissions.d.ts +8 -0
- package/dist/server/server/authorization/operation-permissions.js +438 -0
- package/dist/server/server/auto-archive-on-merge/archive-if-safe.d.ts +40 -0
- package/dist/server/server/auto-archive-on-merge/archive-if-safe.js +64 -0
- package/dist/server/server/auto-archive-on-merge/index.d.ts +13 -0
- package/dist/server/server/auto-archive-on-merge/index.js +77 -0
- package/dist/server/server/bootstrap.d.ts +166 -0
- package/dist/server/server/bootstrap.js +1388 -0
- package/dist/server/server/browser-tools/broker.d.ts +45 -0
- package/dist/server/server/browser-tools/broker.js +390 -0
- package/dist/server/server/browser-tools/errors.d.ts +22 -0
- package/dist/server/server/browser-tools/errors.js +27 -0
- package/dist/server/server/browser-tools/policy.d.ts +10 -0
- package/dist/server/server/browser-tools/policy.js +16 -0
- package/dist/server/server/browser-tools/tools.d.ts +16 -0
- package/dist/server/server/browser-tools/tools.js +796 -0
- package/dist/server/server/checkout/status-projection.d.ts +19 -0
- package/dist/server/server/checkout/status-projection.js +127 -0
- package/dist/server/server/checkout-diff-manager.d.ts +48 -0
- package/dist/server/server/checkout-diff-manager.js +284 -0
- package/dist/server/server/checkout-git-utils.d.ts +11 -0
- package/dist/server/server/checkout-git-utils.js +34 -0
- package/dist/server/server/client-message-id.d.ts +3 -0
- package/dist/server/server/client-message-id.js +12 -0
- package/dist/server/server/config.d.ts +26 -0
- package/dist/server/server/config.js +594 -0
- package/dist/server/server/connection-offer.d.ts +20 -0
- package/dist/server/server/connection-offer.js +59 -0
- package/dist/server/server/creation/index.d.ts +41 -0
- package/dist/server/server/creation/index.js +250 -0
- package/dist/server/server/daemon-config-store.d.ts +63 -0
- package/dist/server/server/daemon-config-store.js +524 -0
- package/dist/server/server/daemon-keypair.d.ts +8 -0
- package/dist/server/server/daemon-keypair.js +42 -0
- package/dist/server/server/daemon-version.d.ts +5 -0
- package/dist/server/server/daemon-version.js +22 -0
- package/dist/server/server/daemon-worker.d.ts +2 -0
- package/dist/server/server/daemon-worker.js +287 -0
- package/dist/server/server/dictation/dictation-stream-manager.d.ts +88 -0
- package/dist/server/server/dictation/dictation-stream-manager.js +578 -0
- package/dist/server/server/directory-sync/index.d.ts +45 -0
- package/dist/server/server/directory-sync/index.js +98 -0
- package/dist/server/server/directory-sync/internal/versioned-collection.d.ts +56 -0
- package/dist/server/server/directory-sync/internal/versioned-collection.js +94 -0
- package/dist/server/server/exports.d.ts +24 -0
- package/dist/server/server/exports.js +24 -0
- package/dist/server/server/file-download/token-store.d.ts +24 -0
- package/dist/server/server/file-download/token-store.js +40 -0
- package/dist/server/server/file-explorer/observer.d.ts +32 -0
- package/dist/server/server/file-explorer/observer.js +130 -0
- package/dist/server/server/file-explorer/service.d.ts +125 -0
- package/dist/server/server/file-explorer/service.js +663 -0
- package/dist/server/server/file-observer/index.d.ts +43 -0
- package/dist/server/server/file-observer/index.js +114 -0
- package/dist/server/server/file-observer/internal/contracts.d.ts +26 -0
- package/dist/server/server/file-observer/internal/contracts.js +2 -0
- package/dist/server/server/file-observer/internal/linux.d.ts +4 -0
- package/dist/server/server/file-observer/internal/linux.js +260 -0
- package/dist/server/server/file-observer/internal/native-recursive.d.ts +9 -0
- package/dist/server/server/file-observer/internal/native-recursive.js +577 -0
- package/dist/server/server/file-observer/internal/observation.d.ts +21 -0
- package/dist/server/server/file-observer/internal/observation.js +129 -0
- package/dist/server/server/file-observer/internal/paths.d.ts +10 -0
- package/dist/server/server/file-observer/internal/paths.js +43 -0
- package/dist/server/server/file-upload/index.d.ts +27 -0
- package/dist/server/server/file-upload/index.js +158 -0
- package/dist/server/server/git-metadata-event-rules.d.ts +123 -0
- package/dist/server/server/git-metadata-event-rules.js +152 -0
- package/dist/server/server/hostnames.d.ts +13 -0
- package/dist/server/server/hostnames.js +94 -0
- package/dist/server/server/hub/daemon-executions.d.ts +89 -0
- package/dist/server/server/hub/daemon-executions.js +287 -0
- package/dist/server/server/hub/execution-controller.d.ts +30 -0
- package/dist/server/server/hub/execution-controller.js +220 -0
- package/dist/server/server/hub/relationship-controller.d.ts +116 -0
- package/dist/server/server/hub/relationship-controller.js +534 -0
- package/dist/server/server/hub/relationship-remote.d.ts +68 -0
- package/dist/server/server/hub/relationship-remote.js +159 -0
- package/dist/server/server/hub/relationship-retry.d.ts +7 -0
- package/dist/server/server/hub/relationship-retry.js +15 -0
- package/dist/server/server/json-utils.d.ts +11 -0
- package/dist/server/server/json-utils.js +45 -0
- package/dist/server/server/lifecycle-reasons.d.ts +4 -0
- package/dist/server/server/lifecycle-reasons.js +6 -0
- package/dist/server/server/logger.d.ts +29 -0
- package/dist/server/server/logger.js +127 -0
- package/dist/server/server/managed-processes/managed-processes.d.ts +76 -0
- package/dist/server/server/managed-processes/managed-processes.js +326 -0
- package/dist/server/server/messages.d.ts +9 -0
- package/dist/server/server/messages.js +29 -0
- package/dist/server/server/migrations/backfill-workspace-id.migration.d.ts +9 -0
- package/dist/server/server/migrations/backfill-workspace-id.migration.js +64 -0
- package/dist/server/server/orchestration-skills/index.d.ts +18 -0
- package/dist/server/server/orchestration-skills/index.js +18 -0
- package/dist/server/server/orchestration-skills/internal/controller.d.ts +39 -0
- package/dist/server/server/orchestration-skills/internal/controller.js +108 -0
- package/dist/server/server/orchestration-skills/internal/operations.d.ts +43 -0
- package/dist/server/server/orchestration-skills/internal/operations.js +178 -0
- package/dist/server/server/orchestration-skills/internal/paths.d.ts +4 -0
- package/dist/server/server/orchestration-skills/internal/paths.js +21 -0
- package/dist/server/server/orchestration-skills/internal/selection-store.d.ts +12 -0
- package/dist/server/server/orchestration-skills/internal/selection-store.js +39 -0
- package/dist/server/server/orchestration-skills/internal/sync.d.ts +21 -0
- package/dist/server/server/orchestration-skills/internal/sync.js +177 -0
- package/dist/server/server/orchestration-skills/internal/transaction.d.ts +22 -0
- package/dist/server/server/orchestration-skills/internal/transaction.js +483 -0
- package/dist/server/server/package-version.d.ts +19 -0
- package/dist/server/server/package-version.js +60 -0
- package/dist/server/server/pagination/cursor.d.ts +16 -0
- package/dist/server/server/pagination/cursor.js +62 -0
- package/dist/server/server/pagination/sortable-pager.d.ts +24 -0
- package/dist/server/server/pagination/sortable-pager.js +68 -0
- package/dist/server/server/pairing-offer.d.ts +18 -0
- package/dist/server/server/pairing-offer.js +47 -0
- package/dist/server/server/pairing-qr.d.ts +2 -0
- package/dist/server/server/pairing-qr.js +15 -0
- package/dist/server/server/paseo-env.d.ts +13 -0
- package/dist/server/server/paseo-env.js +54 -0
- package/dist/server/server/paseo-home.d.ts +2 -0
- package/dist/server/server/paseo-home.js +19 -0
- package/dist/server/server/paseo-worktree-service.d.ts +45 -0
- package/dist/server/server/paseo-worktree-service.js +191 -0
- package/dist/server/server/path-utils.d.ts +5 -0
- package/dist/server/server/path-utils.js +34 -0
- package/dist/server/server/persisted-config.d.ts +397 -0
- package/dist/server/server/persisted-config.js +411 -0
- package/dist/server/server/persistence-hooks.d.ts +32 -0
- package/dist/server/server/persistence-hooks.js +96 -0
- package/dist/server/server/pid-lock.d.ts +44 -0
- package/dist/server/server/pid-lock.js +285 -0
- package/dist/server/server/plugins/compiler-imports.d.ts +13 -0
- package/dist/server/server/plugins/compiler-imports.js +90 -0
- package/dist/server/server/plugins/compiler.d.ts +10 -0
- package/dist/server/server/plugins/compiler.js +354 -0
- package/dist/server/server/plugins/daemon-transport.d.ts +9 -0
- package/dist/server/server/plugins/daemon-transport.js +68 -0
- package/dist/server/server/plugins/index.d.ts +102 -0
- package/dist/server/server/plugins/index.js +575 -0
- package/dist/server/server/plugins/lifecycle/index.d.ts +40 -0
- package/dist/server/server/plugins/lifecycle/index.js +200 -0
- package/dist/server/server/plugins/managed-source.d.ts +50 -0
- package/dist/server/server/plugins/managed-source.js +327 -0
- package/dist/server/server/plugins/manifest.d.ts +12 -0
- package/dist/server/server/plugins/manifest.js +26 -0
- package/dist/server/server/plugins/plugin-process-protocol.d.ts +120 -0
- package/dist/server/server/plugins/plugin-process-protocol.js +154 -0
- package/dist/server/server/plugins/plugin-process.d.ts +2 -0
- package/dist/server/server/plugins/plugin-process.js +419 -0
- package/dist/server/server/plugins/plugin-sdk-specifiers.d.ts +5 -0
- package/dist/server/server/plugins/plugin-sdk-specifiers.js +23 -0
- package/dist/server/server/plugins/plugin-session-identity.d.ts +4 -0
- package/dist/server/server/plugins/plugin-session-identity.js +11 -0
- package/dist/server/server/plugins/preparation.d.ts +4 -0
- package/dist/server/server/plugins/preparation.js +49 -0
- package/dist/server/server/plugins/provider-icon.d.ts +2 -0
- package/dist/server/server/plugins/provider-icon.js +55 -0
- package/dist/server/server/plugins/runtime.d.ts +83 -0
- package/dist/server/server/plugins/runtime.js +836 -0
- package/dist/server/server/plugins/session-socket.d.ts +23 -0
- package/dist/server/server/plugins/session-socket.js +82 -0
- package/dist/server/server/plugins/settings/index.d.ts +96 -0
- package/dist/server/server/plugins/settings/index.js +115 -0
- package/dist/server/server/private-files.d.ts +6 -0
- package/dist/server/server/private-files.js +37 -0
- package/dist/server/server/process-diagnostics.d.ts +17 -0
- package/dist/server/server/process-diagnostics.js +22 -0
- package/dist/server/server/project-directory-service.d.ts +30 -0
- package/dist/server/server/project-directory-service.js +120 -0
- package/dist/server/server/project-key.d.ts +14 -0
- package/dist/server/server/project-key.js +44 -0
- package/dist/server/server/push/index.d.ts +16 -0
- package/dist/server/server/push/index.js +26 -0
- package/dist/server/server/push/push-service.d.ts +19 -0
- package/dist/server/server/push/push-service.js +66 -0
- package/dist/server/server/push/token-store.d.ts +22 -0
- package/dist/server/server/push/token-store.js +125 -0
- package/dist/server/server/relay-runtime.d.ts +27 -0
- package/dist/server/server/relay-runtime.js +46 -0
- package/dist/server/server/relay-transport.d.ts +33 -0
- package/dist/server/server/relay-transport.js +481 -0
- package/dist/server/server/request-receipts/index.d.ts +33 -0
- package/dist/server/server/request-receipts/index.js +131 -0
- package/dist/server/server/resolve-workspace-id-for-path.d.ts +3 -0
- package/dist/server/server/resolve-workspace-id-for-path.js +41 -0
- package/dist/server/server/resolve-worktree-creation-intent.d.ts +44 -0
- package/dist/server/server/resolve-worktree-creation-intent.js +186 -0
- package/dist/server/server/schedule/cron.d.ts +4 -0
- package/dist/server/server/schedule/cron.js +83 -0
- package/dist/server/server/schedule/service.d.ts +75 -0
- package/dist/server/server/schedule/service.js +810 -0
- package/dist/server/server/schedule/store.d.ts +27 -0
- package/dist/server/server/schedule/store.js +190 -0
- package/dist/server/server/script-health-monitor.d.ts +39 -0
- package/dist/server/server/script-health-monitor.js +159 -0
- package/dist/server/server/script-proxy.d.ts +3 -0
- package/dist/server/server/script-proxy.js +2 -0
- package/dist/server/server/script-route-branch-handler.d.ts +10 -0
- package/dist/server/server/script-route-branch-handler.js +11 -0
- package/dist/server/server/script-status-projection.d.ts +36 -0
- package/dist/server/server/script-status-projection.js +200 -0
- package/dist/server/server/server-id.d.ts +17 -0
- package/dist/server/server/server-id.js +68 -0
- package/dist/server/server/service-proxy.d.ts +237 -0
- package/dist/server/server/service-proxy.js +679 -0
- package/dist/server/server/session/agent-config/agent-config-session.d.ts +75 -0
- package/dist/server/server/session/agent-config/agent-config-session.js +151 -0
- package/dist/server/server/session/agent-updates/agent-updates-service.d.ts +65 -0
- package/dist/server/server/session/agent-updates/agent-updates-service.js +266 -0
- package/dist/server/server/session/checkout/checkout-session.d.ts +150 -0
- package/dist/server/server/session/checkout/checkout-session.js +1187 -0
- package/dist/server/server/session/checkout/git-metadata-generator.d.ts +53 -0
- package/dist/server/server/session/checkout/git-metadata-generator.js +159 -0
- package/dist/server/server/session/daemon/daemon-self-update-session-controller.d.ts +34 -0
- package/dist/server/server/session/daemon/daemon-self-update-session-controller.js +90 -0
- package/dist/server/server/session/daemon/daemon-self-updater.d.ts +33 -0
- package/dist/server/server/session/daemon/daemon-self-updater.js +60 -0
- package/dist/server/server/session/daemon/daemon-session.d.ts +90 -0
- package/dist/server/server/session/daemon/daemon-session.js +224 -0
- package/dist/server/server/session/daemon/diagnostics.d.ts +41 -0
- package/dist/server/server/session/daemon/diagnostics.js +431 -0
- package/dist/server/server/session/daemon/install-origin.d.ts +7 -0
- package/dist/server/server/session/daemon/install-origin.js +64 -0
- package/dist/server/server/session/daemon/npm-global-cli.d.ts +29 -0
- package/dist/server/server/session/daemon/npm-global-cli.js +98 -0
- package/dist/server/server/session/files/workspace-files-session.d.ts +55 -0
- package/dist/server/server/session/files/workspace-files-session.js +359 -0
- package/dist/server/server/session/git-mutation/git-mutation-service.d.ts +32 -0
- package/dist/server/server/session/git-mutation/git-mutation-service.js +74 -0
- package/dist/server/server/session/project-config/project-config-session.d.ts +35 -0
- package/dist/server/server/session/project-config/project-config-session.js +143 -0
- package/dist/server/server/session/project-config/worktree-setup-commit-status.d.ts +6 -0
- package/dist/server/server/session/project-config/worktree-setup-commit-status.js +38 -0
- package/dist/server/server/session/provider/provider-catalog-session.d.ts +78 -0
- package/dist/server/server/session/provider/provider-catalog-session.js +388 -0
- package/dist/server/server/session/schedule/schedule-session.d.ts +47 -0
- package/dist/server/server/session/schedule/schedule-session.js +188 -0
- package/dist/server/server/session/voice/voice-session.d.ts +166 -0
- package/dist/server/server/session/voice/voice-session.js +904 -0
- package/dist/server/server/session/voice/voice-turn-controller.d.ts +38 -0
- package/dist/server/server/session/voice/voice-turn-controller.js +393 -0
- package/dist/server/server/session/workspace-git-observer/workspace-git-observer-service.d.ts +42 -0
- package/dist/server/server/session/workspace-git-observer/workspace-git-observer-service.js +170 -0
- package/dist/server/server/session/workspace-provisioning/workspace-provisioning-service.d.ts +59 -0
- package/dist/server/server/session/workspace-provisioning/workspace-provisioning-service.js +326 -0
- package/dist/server/server/session/workspace-recovery/workspace-recovery-service.d.ts +30 -0
- package/dist/server/server/session/workspace-recovery/workspace-recovery-service.js +178 -0
- package/dist/server/server/session/workspace-scripts/workspace-scripts-service.d.ts +54 -0
- package/dist/server/server/session/workspace-scripts/workspace-scripts-service.js +156 -0
- package/dist/server/server/session.d.ts +555 -0
- package/dist/server/server/session.js +6540 -0
- package/dist/server/server/speech/audio.d.ts +10 -0
- package/dist/server/server/speech/audio.js +101 -0
- package/dist/server/server/speech/provider-resolver.d.ts +3 -0
- package/dist/server/server/speech/provider-resolver.js +7 -0
- package/dist/server/server/speech/providers/local/config.d.ts +30 -0
- package/dist/server/server/speech/providers/local/config.js +134 -0
- package/dist/server/server/speech/providers/local/models.d.ts +12 -0
- package/dist/server/server/speech/providers/local/models.js +18 -0
- package/dist/server/server/speech/providers/local/runtime.d.ts +31 -0
- package/dist/server/server/speech/providers/local/runtime.js +135 -0
- package/dist/server/server/speech/providers/local/sherpa/assets/silero_vad.onnx +0 -0
- package/dist/server/server/speech/providers/local/sherpa/model-catalog.d.ts +56 -0
- package/dist/server/server/speech/providers/local/sherpa/model-catalog.js +74 -0
- package/dist/server/server/speech/providers/local/sherpa/model-downloader.d.ts +17 -0
- package/dist/server/server/speech/providers/local/sherpa/model-downloader.js +145 -0
- package/dist/server/server/speech/providers/local/sherpa/sherpa-offline-recognizer.d.ts +49 -0
- package/dist/server/server/speech/providers/local/sherpa/sherpa-offline-recognizer.js +73 -0
- package/dist/server/server/speech/providers/local/sherpa/sherpa-onnx-node-loader.d.ts +9 -0
- package/dist/server/server/speech/providers/local/sherpa/sherpa-onnx-node-loader.js +107 -0
- package/dist/server/server/speech/providers/local/sherpa/sherpa-parakeet-realtime-session.d.ts +28 -0
- package/dist/server/server/speech/providers/local/sherpa/sherpa-parakeet-realtime-session.js +137 -0
- package/dist/server/server/speech/providers/local/sherpa/sherpa-parakeet-stt.d.ts +21 -0
- package/dist/server/server/speech/providers/local/sherpa/sherpa-parakeet-stt.js +133 -0
- package/dist/server/server/speech/providers/local/sherpa/sherpa-runtime-env.d.ts +18 -0
- package/dist/server/server/speech/providers/local/sherpa/sherpa-runtime-env.js +84 -0
- package/dist/server/server/speech/providers/local/sherpa/sherpa-tts.d.ts +21 -0
- package/dist/server/server/speech/providers/local/sherpa/sherpa-tts.js +101 -0
- package/dist/server/server/speech/providers/local/sherpa/silero-vad-provider.d.ts +19 -0
- package/dist/server/server/speech/providers/local/sherpa/silero-vad-provider.js +49 -0
- package/dist/server/server/speech/providers/local/sherpa/silero-vad-session.d.ts +38 -0
- package/dist/server/server/speech/providers/local/sherpa/silero-vad-session.js +176 -0
- package/dist/server/server/speech/providers/local/worker-bytes.d.ts +4 -0
- package/dist/server/server/speech/providers/local/worker-bytes.js +9 -0
- package/dist/server/server/speech/providers/local/worker-client.d.ts +93 -0
- package/dist/server/server/speech/providers/local/worker-client.js +590 -0
- package/dist/server/server/speech/providers/local/worker-process.d.ts +2 -0
- package/dist/server/server/speech/providers/local/worker-process.js +270 -0
- package/dist/server/server/speech/providers/local/worker-protocol.d.ts +95 -0
- package/dist/server/server/speech/providers/local/worker-protocol.js +2 -0
- package/dist/server/server/speech/providers/openai/config.d.ts +19 -0
- package/dist/server/server/speech/providers/openai/config.js +148 -0
- package/dist/server/server/speech/providers/openai/runtime.d.ts +29 -0
- package/dist/server/server/speech/providers/openai/runtime.js +102 -0
- package/dist/server/server/speech/providers/openai/stt.d.ts +23 -0
- package/dist/server/server/speech/providers/openai/stt.js +209 -0
- package/dist/server/server/speech/providers/openai/tts.d.ts +19 -0
- package/dist/server/server/speech/providers/openai/tts.js +48 -0
- package/dist/server/server/speech/speech-config-resolver.d.ts +11 -0
- package/dist/server/server/speech/speech-config-resolver.js +132 -0
- package/dist/server/server/speech/speech-provider.d.ts +59 -0
- package/dist/server/server/speech/speech-provider.js +2 -0
- package/dist/server/server/speech/speech-runtime.d.ts +45 -0
- package/dist/server/server/speech/speech-runtime.js +571 -0
- package/dist/server/server/speech/speech-types.d.ts +22 -0
- package/dist/server/server/speech/speech-types.js +8 -0
- package/dist/server/server/speech/turn-detection-provider.d.ts +23 -0
- package/dist/server/server/speech/turn-detection-provider.js +2 -0
- package/dist/server/server/utils/diff-highlighter.d.ts +57 -0
- package/dist/server/server/utils/diff-highlighter.js +304 -0
- package/dist/server/server/voice-config.d.ts +15 -0
- package/dist/server/server/voice-config.js +54 -0
- package/dist/server/server/voice-permission-policy.d.ts +4 -0
- package/dist/server/server/voice-permission-policy.js +13 -0
- package/dist/server/server/voice-types.d.ts +12 -0
- package/dist/server/server/voice-types.js +2 -0
- package/dist/server/server/watcher-liveness-canary.d.ts +11 -0
- package/dist/server/server/watcher-liveness-canary.js +53 -0
- package/dist/server/server/web-ui.d.ts +10 -0
- package/dist/server/server/web-ui.js +205 -0
- package/dist/server/server/websocket/encrypted-relay-socket.d.ts +23 -0
- package/dist/server/server/websocket/encrypted-relay-socket.js +64 -0
- package/dist/server/server/websocket/physical-socket.d.ts +36 -0
- package/dist/server/server/websocket/physical-socket.js +95 -0
- package/dist/server/server/websocket/runtime-metrics.d.ts +95 -0
- package/dist/server/server/websocket/runtime-metrics.js +148 -0
- package/dist/server/server/websocket-server.d.ts +214 -0
- package/dist/server/server/websocket-server.js +2258 -0
- package/dist/server/server/workspace-archive-service.d.ts +63 -0
- package/dist/server/server/workspace-archive-service.js +355 -0
- package/dist/server/server/workspace-auto-name.d.ts +55 -0
- package/dist/server/server/workspace-auto-name.js +127 -0
- package/dist/server/server/workspace-automation-gate.d.ts +16 -0
- package/dist/server/server/workspace-automation-gate.js +35 -0
- package/dist/server/server/workspace-bootstrap-dedupe.d.ts +34 -0
- package/dist/server/server/workspace-bootstrap-dedupe.js +23 -0
- package/dist/server/server/workspace-directory.d.ts +99 -0
- package/dist/server/server/workspace-directory.js +467 -0
- package/dist/server/server/workspace-git-fetch.d.ts +33 -0
- package/dist/server/server/workspace-git-fetch.js +108 -0
- package/dist/server/server/workspace-git-metadata.d.ts +8 -0
- package/dist/server/server/workspace-git-metadata.js +24 -0
- package/dist/server/server/workspace-git-service.d.ts +348 -0
- package/dist/server/server/workspace-git-service.js +2534 -0
- package/dist/server/server/workspace-labels/index.d.ts +14 -0
- package/dist/server/server/workspace-labels/index.js +10 -0
- package/dist/server/server/workspace-labels/internal/catalog-store.d.ts +50 -0
- package/dist/server/server/workspace-labels/internal/catalog-store.js +203 -0
- package/dist/server/server/workspace-labels/internal/sequence.d.ts +43 -0
- package/dist/server/server/workspace-labels/internal/sequence.js +80 -0
- package/dist/server/server/workspace-labels/internal/service.d.ts +57 -0
- package/dist/server/server/workspace-labels/internal/service.js +224 -0
- package/dist/server/server/workspace-reconciliation-service.d.ts +103 -0
- package/dist/server/server/workspace-reconciliation-service.js +379 -0
- package/dist/server/server/workspace-registry-bootstrap-legacy.d.ts +21 -0
- package/dist/server/server/workspace-registry-bootstrap-legacy.js +57 -0
- package/dist/server/server/workspace-registry-bootstrap.d.ts +14 -0
- package/dist/server/server/workspace-registry-bootstrap.js +155 -0
- package/dist/server/server/workspace-registry-model.d.ts +56 -0
- package/dist/server/server/workspace-registry-model.js +150 -0
- package/dist/server/server/workspace-registry.d.ts +228 -0
- package/dist/server/server/workspace-registry.js +468 -0
- package/dist/server/server/workspace-script-runtime-store.d.ts +28 -0
- package/dist/server/server/workspace-script-runtime-store.js +78 -0
- package/dist/server/server/workspace-service-env.d.ts +18 -0
- package/dist/server/server/workspace-service-env.js +85 -0
- package/dist/server/server/workspace-service-port-allocator.d.ts +11 -0
- package/dist/server/server/workspace-service-port-allocator.js +89 -0
- package/dist/server/server/workspace-service-port-registry.d.ts +24 -0
- package/dist/server/server/workspace-service-port-registry.js +172 -0
- package/dist/server/server/workspace-setup-runtime.d.ts +7 -0
- package/dist/server/server/workspace-setup-runtime.js +30 -0
- package/dist/server/server/workspace-source.d.ts +7 -0
- package/dist/server/server/workspace-source.js +15 -0
- package/dist/server/server/worktree/commands.d.ts +55 -0
- package/dist/server/server/worktree/commands.js +99 -0
- package/dist/server/server/worktree-bootstrap.d.ts +86 -0
- package/dist/server/server/worktree-bootstrap.js +753 -0
- package/dist/server/server/worktree-branch-name-generator.d.ts +35 -0
- package/dist/server/server/worktree-branch-name-generator.js +89 -0
- package/dist/server/server/worktree-core.d.ts +32 -0
- package/dist/server/server/worktree-core.js +114 -0
- package/dist/server/server/worktree-errors.d.ts +12 -0
- package/dist/server/server/worktree-errors.js +28 -0
- package/dist/server/server/worktree-session.d.ts +165 -0
- package/dist/server/server/worktree-session.js +594 -0
- package/dist/server/services/forge-cli-command.d.ts +128 -0
- package/dist/server/services/forge-cli-command.js +212 -0
- package/dist/server/services/forge-registry.d.ts +25 -0
- package/dist/server/services/forge-registry.js +155 -0
- package/dist/server/services/forge-resolver.d.ts +38 -0
- package/dist/server/services/forge-resolver.js +191 -0
- package/dist/server/services/forge-service.d.ts +430 -0
- package/dist/server/services/forge-service.js +50 -0
- package/dist/server/services/gitea-facts.d.ts +11 -0
- package/dist/server/services/gitea-facts.js +4 -0
- package/dist/server/services/gitea-service.d.ts +62 -0
- package/dist/server/services/gitea-service.js +1844 -0
- package/dist/server/services/github-facts.d.ts +27 -0
- package/dist/server/services/github-facts.js +4 -0
- package/dist/server/services/github-service.d.ts +76 -0
- package/dist/server/services/github-service.js +3297 -0
- package/dist/server/services/gitlab-facts.d.ts +26 -0
- package/dist/server/services/gitlab-facts.js +4 -0
- package/dist/server/services/gitlab-service.d.ts +43 -0
- package/dist/server/services/gitlab-service.js +1092 -0
- package/dist/server/services/quota-fetcher/manifest.d.ts +5 -0
- package/dist/server/services/quota-fetcher/manifest.js +118 -0
- package/dist/server/services/quota-fetcher/provider.d.ts +18 -0
- package/dist/server/services/quota-fetcher/provider.js +2 -0
- package/dist/server/services/quota-fetcher/providers/claude.d.ts +40 -0
- package/dist/server/services/quota-fetcher/providers/claude.js +389 -0
- package/dist/server/services/quota-fetcher/providers/codex.d.ts +23 -0
- package/dist/server/services/quota-fetcher/providers/codex.js +167 -0
- package/dist/server/services/quota-fetcher/providers/copilot.d.ts +21 -0
- package/dist/server/services/quota-fetcher/providers/copilot.js +77 -0
- package/dist/server/services/quota-fetcher/providers/cursor.d.ts +19 -0
- package/dist/server/services/quota-fetcher/providers/cursor.js +185 -0
- package/dist/server/services/quota-fetcher/providers/grok.d.ts +23 -0
- package/dist/server/services/quota-fetcher/providers/grok.js +137 -0
- package/dist/server/services/quota-fetcher/providers/kimi.d.ts +23 -0
- package/dist/server/services/quota-fetcher/providers/kimi.js +260 -0
- package/dist/server/services/quota-fetcher/providers/minimax.d.ts +29 -0
- package/dist/server/services/quota-fetcher/providers/minimax.js +227 -0
- package/dist/server/services/quota-fetcher/providers/zai.d.ts +17 -0
- package/dist/server/services/quota-fetcher/providers/zai.js +58 -0
- package/dist/server/services/quota-fetcher/service.d.ts +34 -0
- package/dist/server/services/quota-fetcher/service.js +74 -0
- package/dist/server/services/quota-fetcher/usage.d.ts +39 -0
- package/dist/server/services/quota-fetcher/usage.js +78 -0
- package/dist/server/skills/paseo/SKILL.md +129 -0
- package/dist/server/skills/paseo-advisor/SKILL.md +63 -0
- package/dist/server/skills/paseo-committee/SKILL.md +46 -0
- package/dist/server/skills/paseo-handoff/SKILL.md +65 -0
- package/dist/server/skills/paseo-help/SKILL.md +92 -0
- package/dist/server/skills/paseo-plugin/SKILL.md +639 -0
- package/dist/server/terminal/activity/terminal-activity-tracker.d.ts +21 -0
- package/dist/server/terminal/activity/terminal-activity-tracker.js +65 -0
- package/dist/server/terminal/agent-hooks/agent-hook-installer.d.ts +62 -0
- package/dist/server/terminal/agent-hooks/agent-hook-installer.js +117 -0
- package/dist/server/terminal/agent-hooks/claude/claude-settings.d.ts +7 -0
- package/dist/server/terminal/agent-hooks/claude/claude-settings.js +88 -0
- package/dist/server/terminal/agent-hooks/claude/claude.d.ts +4 -0
- package/dist/server/terminal/agent-hooks/claude/claude.js +47 -0
- package/dist/server/terminal/agent-hooks/codex/codex-settings.d.ts +7 -0
- package/dist/server/terminal/agent-hooks/codex/codex-settings.js +99 -0
- package/dist/server/terminal/agent-hooks/codex/codex.d.ts +4 -0
- package/dist/server/terminal/agent-hooks/codex/codex.js +30 -0
- package/dist/server/terminal/agent-hooks/opencode/opencode-plugin.d.ts +4 -0
- package/dist/server/terminal/agent-hooks/opencode/opencode-plugin.js +81 -0
- package/dist/server/terminal/agent-hooks/opencode/opencode.d.ts +3 -0
- package/dist/server/terminal/agent-hooks/opencode/opencode.js +23 -0
- package/dist/server/terminal/agent-hooks/provider-registry.d.ts +24 -0
- package/dist/server/terminal/agent-hooks/provider-registry.js +36 -0
- package/dist/server/terminal/agent-hooks/terminal-agent-hook-setting.d.ts +10 -0
- package/dist/server/terminal/agent-hooks/terminal-agent-hook-setting.js +26 -0
- package/dist/server/terminal/shell-integration/zsh/.zshenv +17 -0
- package/dist/server/terminal/shell-integration/zsh/paseo-integration.zsh +32 -0
- package/dist/server/terminal/terminal-capture.d.ts +12 -0
- package/dist/server/terminal/terminal-capture.js +43 -0
- package/dist/server/terminal/terminal-manager-factory.d.ts +6 -0
- package/dist/server/terminal/terminal-manager-factory.js +5 -0
- package/dist/server/terminal/terminal-manager.d.ts +80 -0
- package/dist/server/terminal/terminal-manager.js +321 -0
- package/dist/server/terminal/terminal-output-coalescer.d.ts +34 -0
- package/dist/server/terminal/terminal-output-coalescer.js +73 -0
- package/dist/server/terminal/terminal-restore.d.ts +18 -0
- package/dist/server/terminal/terminal-restore.js +52 -0
- package/dist/server/terminal/terminal-session-controller.d.ts +90 -0
- package/dist/server/terminal/terminal-session-controller.js +849 -0
- package/dist/server/terminal/terminal-size-ownership.d.ts +9 -0
- package/dist/server/terminal/terminal-size-ownership.js +23 -0
- package/dist/server/terminal/terminal-ts-loader.mjs +20 -0
- package/dist/server/terminal/terminal-worker-process.d.ts +2 -0
- package/dist/server/terminal/terminal-worker-process.js +308 -0
- package/dist/server/terminal/terminal-worker-protocol.d.ts +121 -0
- package/dist/server/terminal/terminal-worker-protocol.js +2 -0
- package/dist/server/terminal/terminal.d.ts +151 -0
- package/dist/server/terminal/terminal.js +1234 -0
- package/dist/server/terminal/worker-terminal-manager.d.ts +20 -0
- package/dist/server/terminal/worker-terminal-manager.js +688 -0
- package/dist/server/utils/build-metadata-prompt.d.ts +19 -0
- package/dist/server/utils/build-metadata-prompt.js +29 -0
- package/dist/server/utils/checkout-git.d.ts +317 -0
- package/dist/server/utils/checkout-git.js +2886 -0
- package/dist/server/utils/directory-suggestions.d.ts +29 -0
- package/dist/server/utils/directory-suggestions.js +596 -0
- package/dist/server/utils/git-command-runtime-metrics.d.ts +67 -0
- package/dist/server/utils/git-command-runtime-metrics.js +126 -0
- package/dist/server/utils/git-command-trace.d.ts +29 -0
- package/dist/server/utils/git-command-trace.js +74 -0
- package/dist/server/utils/git-process-scheduler.d.ts +34 -0
- package/dist/server/utils/git-process-scheduler.js +112 -0
- package/dist/server/utils/git-rev-parse-path.d.ts +3 -0
- package/dist/server/utils/git-rev-parse-path.js +21 -0
- package/dist/server/utils/paseo-config-file.d.ts +30 -0
- package/dist/server/utils/paseo-config-file.js +90 -0
- package/dist/server/utils/path.d.ts +27 -0
- package/dist/server/utils/path.js +156 -0
- package/dist/server/utils/project-custom-icon.d.ts +40 -0
- package/dist/server/utils/project-custom-icon.js +144 -0
- package/dist/server/utils/project-icon.d.ts +54 -0
- package/dist/server/utils/project-icon.js +445 -0
- package/dist/server/utils/promise-timeout.d.ts +9 -0
- package/dist/server/utils/promise-timeout.js +25 -0
- package/dist/server/utils/run-git-command.d.ts +59 -0
- package/dist/server/utils/run-git-command.js +393 -0
- package/dist/server/utils/spawn.d.ts +26 -0
- package/dist/server/utils/spawn.js +60 -0
- package/dist/server/utils/ssh-hostname.d.ts +8 -0
- package/dist/server/utils/ssh-hostname.js +60 -0
- package/dist/server/utils/string-command-shell.d.ts +13 -0
- package/dist/server/utils/string-command-shell.js +37 -0
- package/dist/server/utils/tree-kill.d.ts +19 -0
- package/dist/server/utils/tree-kill.js +77 -0
- package/dist/server/utils/windows-command.d.ts +15 -0
- package/dist/server/utils/windows-command.js +41 -0
- package/dist/server/utils/worktree-metadata.d.ts +77 -0
- package/dist/server/utils/worktree-metadata.js +280 -0
- package/dist/server/utils/worktree.d.ts +232 -0
- package/dist/server/utils/worktree.js +1193 -0
- package/dist/server/web-ui/_expo/static/css/xterm-3bb1704bf6cb0876640973dc0244b4cb.css +1 -0
- package/dist/server/web-ui/_expo/static/css/xterm-3bb1704bf6cb0876640973dc0244b4cb.css.br +0 -0
- package/dist/server/web-ui/_expo/static/css/xterm-3bb1704bf6cb0876640973dc0244b4cb.css.gz +0 -0
- package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-bridge-29f33408b1a3c5b435c5202d62a36719.js +1 -0
- package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-bridge-29f33408b1a3c5b435c5202d62a36719.js.br +0 -0
- package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-bridge-29f33408b1a3c5b435c5202d62a36719.js.gz +0 -0
- package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-store-41547ee520dfff946caa727824742a5c.js +1 -0
- package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-store-41547ee520dfff946caa727824742a5c.js.br +0 -0
- package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-store-41547ee520dfff946caa727824742a5c.js.gz +0 -0
- package/dist/server/web-ui/_expo/static/js/web/index-03b99be4265ac4a2bc5a6d602fd59cc4.js +16819 -0
- package/dist/server/web-ui/_expo/static/js/web/index-03b99be4265ac4a2bc5a6d602fd59cc4.js.br +0 -0
- package/dist/server/web-ui/_expo/static/js/web/index-03b99be4265ac4a2bc5a6d602fd59cc4.js.gz +0 -0
- package/dist/server/web-ui/_expo/static/js/web/indexeddb-attachment-store-5444610146b0794f1e7d9edad4522d41.js +1 -0
- package/dist/server/web-ui/_expo/static/js/web/indexeddb-attachment-store-5444610146b0794f1e7d9edad4522d41.js.br +0 -0
- package/dist/server/web-ui/_expo/static/js/web/indexeddb-attachment-store-5444610146b0794f1e7d9edad4522d41.js.gz +0 -0
- package/dist/server/web-ui/_expo/static/js/web/native-file-attachment-store-88b083190031a44788be59103814f511.js +3 -0
- package/dist/server/web-ui/_expo/static/js/web/native-file-attachment-store-88b083190031a44788be59103814f511.js.br +0 -0
- package/dist/server/web-ui/_expo/static/js/web/native-file-attachment-store-88b083190031a44788be59103814f511.js.gz +0 -0
- package/dist/server/web-ui/apple-touch-icon.png +0 -0
- package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/back-icon-mask.0a328cd9c1afd0afe8e3b1ec5165b1b4.png +0 -0
- package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/back-icon.35ba0eaec5a4f5ed12ca16fabeae451d.png +0 -0
- package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/clear-icon.c94f6478e7ae0cdd9f15de1fcb9e5e55.png +0 -0
- package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/clear-icon.c94f6478e7ae0cdd9f15de1fcb9e5e55@2x.png +0 -0
- package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/clear-icon.c94f6478e7ae0cdd9f15de1fcb9e5e55@3x.png +0 -0
- package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/clear-icon.c94f6478e7ae0cdd9f15de1fcb9e5e55@4x.png +0 -0
- package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/close-icon.808e1b1b9b53114ec2838071a7e6daa7.png +0 -0
- package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/close-icon.808e1b1b9b53114ec2838071a7e6daa7@2x.png +0 -0
- package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/close-icon.808e1b1b9b53114ec2838071a7e6daa7@3x.png +0 -0
- package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/close-icon.808e1b1b9b53114ec2838071a7e6daa7@4x.png +0 -0
- package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/search-icon.286d67d3f74808a60a78d3ebf1a5fb57.png +0 -0
- package/dist/server/web-ui/assets/__node_modules/expo-router/assets/arrow_down.017bc6ba3fc25503e5eb5e53826d48a8.png +0 -0
- package/dist/server/web-ui/assets/__node_modules/expo-router/assets/error.d1ea1496f9057eb392d5bbf3732a61b7.png +0 -0
- package/dist/server/web-ui/assets/__node_modules/expo-router/assets/file.19eeb73b9593a38f8e9f418337fc7d10.png +0 -0
- package/dist/server/web-ui/assets/__node_modules/expo-router/assets/forward.d8b800c443b8972542883e0b9de2bdc6.png +0 -0
- package/dist/server/web-ui/assets/__node_modules/expo-router/assets/pkg.ab19f4cbc543357183a20571f68380a3.png +0 -0
- package/dist/server/web-ui/assets/__node_modules/expo-router/assets/sitemap.412dd9275b6b48ad28f5e3d81bb1f626.png +0 -0
- package/dist/server/web-ui/assets/__node_modules/expo-router/assets/unmatched.20e71bdf79e3a97bf55fd9e164041578.png +0 -0
- package/dist/server/web-ui/assets/assets/images/favicon-dark-attention.882b3a27dcb2073e9e31b334f9ed9728.png +0 -0
- package/dist/server/web-ui/assets/assets/images/favicon-dark-running.8112342ff0d39e047a7f8d4fad9402f3.png +0 -0
- package/dist/server/web-ui/assets/assets/images/favicon-dark.8005ed36ac07a5a7c60de25780897bd4.png +0 -0
- package/dist/server/web-ui/assets/assets/images/favicon-light-attention.882b3a27dcb2073e9e31b334f9ed9728.png +0 -0
- package/dist/server/web-ui/assets/assets/images/favicon-light-running.8112342ff0d39e047a7f8d4fad9402f3.png +0 -0
- package/dist/server/web-ui/assets/assets/images/favicon-light.8005ed36ac07a5a7c60de25780897bd4.png +0 -0
- package/dist/server/web-ui/assets/assets/images/notification-icon.3bf81d33ddbf380606bdd248ba83e158.png +0 -0
- package/dist/server/web-ui/favicon.ico +0 -0
- package/dist/server/web-ui/index.html +90 -0
- package/dist/server/web-ui/index.html.br +0 -0
- package/dist/server/web-ui/index.html.gz +0 -0
- package/dist/server/web-ui/manifest.json +27 -0
- package/dist/server/web-ui/manifest.json.br +0 -0
- package/dist/server/web-ui/manifest.json.gz +0 -0
- package/dist/server/web-ui/metadata.json +1 -0
- package/dist/server/web-ui/metadata.json.br +1 -0
- package/dist/server/web-ui/metadata.json.gz +0 -0
- package/dist/server/web-ui/pwa-icon-192.png +0 -0
- package/dist/server/web-ui/pwa-icon-512.png +0 -0
- package/dist/server/web-ui/robots.txt +2 -0
- package/dist/src/executable-resolution/executable-resolution.js +121 -0
- package/dist/src/executable-resolution/windows.js +65 -0
- package/dist/src/server/agent/provider-launch-config.js +167 -0
- package/dist/src/server/paseo-env.js +54 -0
- package/dist/src/server/paseo-home.js +19 -0
- package/dist/src/server/persisted-config.js +411 -0
- package/dist/src/server/pid-lock.js +285 -0
- package/dist/src/server/private-files.js +37 -0
- package/dist/src/server/speech/providers/local/sherpa/sherpa-runtime-env.js +84 -0
- package/dist/src/terminal/shell-integration/zsh/.zshenv +17 -0
- package/dist/src/terminal/shell-integration/zsh/paseo-integration.zsh +32 -0
- package/dist/src/utils/spawn.js +60 -0
- package/dist/src/utils/tree-kill.js +77 -0
- package/dist/src/utils/windows-command.js +41 -0
- package/package.json +125 -0
|
@@ -0,0 +1,3930 @@
|
|
|
1
|
+
import { describeHookAgent, publishAgentStream } from "../plugins/lifecycle/index.js";
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
import { basename, resolve } from "node:path";
|
|
4
|
+
import { stat } from "node:fs/promises";
|
|
5
|
+
import { AGENT_LIFECYCLE_STATUSES, } from "@camerontaylor/paseo-protocol/agent-lifecycle";
|
|
6
|
+
import { getParentAgentIdFromLabels, hasOpenAgentTab, isDelegatedAgent, isOpenAgentTabLabel, PARENT_AGENT_ID_LABEL, } from "@camerontaylor/paseo-protocol/agent-labels";
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
import { getAgentStreamEventTurnId, } from "./agent-sdk-types.js";
|
|
9
|
+
import { buildArchivedAgentRecord } from "./agent-archive.js";
|
|
10
|
+
import { InMemoryAgentTimelineStore, } from "./agent-timeline-store.js";
|
|
11
|
+
import { AGENT_STREAM_COALESCE_DEFAULT_WINDOW_MS, AgentStreamCoalescer, } from "./agent-stream-coalescer.js";
|
|
12
|
+
import { limitAgentTimelineItemContent } from "./agent-timeline-content.js";
|
|
13
|
+
import { AgentRunState, } from "./agent-run-state.js";
|
|
14
|
+
import { invokeRewindCapability } from "./rewind/rewind.js";
|
|
15
|
+
import { SideConversationStore, } from "./side-conversations/store.js";
|
|
16
|
+
import { isSystemInjectedEnvelope } from "./agent-prompt.js";
|
|
17
|
+
import { isStaleProviderSessionError } from "./stale-provider-session-error.js";
|
|
18
|
+
import { stripInternalPaseoMcpServer, withRuntimePaseoMcpServer } from "./runtime-mcp-config.js";
|
|
19
|
+
import { resolveCreateAgentTitles } from "./create-agent-title.js";
|
|
20
|
+
import { isPaseoToolPolicyEnabled } from "./paseo-tool-policy.js";
|
|
21
|
+
import { ProviderSubagentStore, } from "./provider-subagents/store.js";
|
|
22
|
+
import { withTimeout } from "../../utils/promise-timeout.js";
|
|
23
|
+
const RELOAD_SESSION_CLOSE_TIMEOUT_MS = 3000;
|
|
24
|
+
const INTERRUPT_SESSION_TIMEOUT_MS = 2000;
|
|
25
|
+
const IMPORTABLE_SESSION_LIST_TIMEOUT_MS = 90000;
|
|
26
|
+
const STORED_AGENT_CAPABILITIES = {
|
|
27
|
+
supportsStreaming: false,
|
|
28
|
+
supportsSessionPersistence: true,
|
|
29
|
+
supportsDynamicModes: false,
|
|
30
|
+
supportsMcpServers: false,
|
|
31
|
+
supportsReasoningStream: false,
|
|
32
|
+
supportsToolInvocations: true,
|
|
33
|
+
supportsRewindConversation: false,
|
|
34
|
+
supportsRewindFiles: false,
|
|
35
|
+
supportsRewindBoth: false,
|
|
36
|
+
};
|
|
37
|
+
function submittedPromptText(prompt) {
|
|
38
|
+
if (typeof prompt === "string") {
|
|
39
|
+
return prompt;
|
|
40
|
+
}
|
|
41
|
+
return prompt
|
|
42
|
+
.flatMap((block) => (block.type === "text" && !("mimeType" in block) ? [block.text] : []))
|
|
43
|
+
.join("\n")
|
|
44
|
+
.trim();
|
|
45
|
+
}
|
|
46
|
+
export class AgentManagerShuttingDownError extends Error {
|
|
47
|
+
constructor() {
|
|
48
|
+
super("Agent manager is shutting down");
|
|
49
|
+
this.name = "AgentManagerShuttingDownError";
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
export class AgentRunCancellationError extends Error {
|
|
53
|
+
constructor(agentId, action) {
|
|
54
|
+
super(`Cannot ${action} agent ${agentId} because its active run cancellation was not acknowledged`);
|
|
55
|
+
this.name = "AgentRunCancellationError";
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function formatProviderList(providers) {
|
|
59
|
+
return providers.length > 0 ? providers.join(", ") : "none";
|
|
60
|
+
}
|
|
61
|
+
function buildStoredAgentConfig(record) {
|
|
62
|
+
const config = {
|
|
63
|
+
provider: record.provider,
|
|
64
|
+
cwd: record.cwd,
|
|
65
|
+
};
|
|
66
|
+
if (!record.config) {
|
|
67
|
+
return config;
|
|
68
|
+
}
|
|
69
|
+
if (record.config.modeId != null)
|
|
70
|
+
config.modeId = record.config.modeId;
|
|
71
|
+
if (record.config.model != null)
|
|
72
|
+
config.model = record.config.model;
|
|
73
|
+
if (record.config.thinkingOptionId != null) {
|
|
74
|
+
config.thinkingOptionId = record.config.thinkingOptionId;
|
|
75
|
+
}
|
|
76
|
+
if (record.config.featureValues != null) {
|
|
77
|
+
config.featureValues = record.config.featureValues;
|
|
78
|
+
}
|
|
79
|
+
if (record.config.providerOptions != null) {
|
|
80
|
+
config.providerOptions = record.config.providerOptions;
|
|
81
|
+
}
|
|
82
|
+
if (record.config.toolPolicy != null)
|
|
83
|
+
config.toolPolicy = record.config.toolPolicy;
|
|
84
|
+
if (record.config.systemPrompt != null) {
|
|
85
|
+
config.systemPrompt = record.config.systemPrompt;
|
|
86
|
+
}
|
|
87
|
+
if (record.config.mcpServers != null)
|
|
88
|
+
config.mcpServers = record.config.mcpServers;
|
|
89
|
+
return stripInternalPaseoMcpServer(config);
|
|
90
|
+
}
|
|
91
|
+
export { AGENT_LIFECYCLE_STATUSES };
|
|
92
|
+
function sideConversationEventParentId(event) {
|
|
93
|
+
return event.type === "update" ? event.record.parentAgentId : event.parentAgentId;
|
|
94
|
+
}
|
|
95
|
+
function stripSteerOptions(options) {
|
|
96
|
+
if (!options)
|
|
97
|
+
return undefined;
|
|
98
|
+
const { clearPendingPermissions: _, ...runOptions } = options;
|
|
99
|
+
return runOptions;
|
|
100
|
+
}
|
|
101
|
+
function resolveInitialAttention(input) {
|
|
102
|
+
if (input == null || !input.requiresAttention) {
|
|
103
|
+
return { requiresAttention: false };
|
|
104
|
+
}
|
|
105
|
+
return {
|
|
106
|
+
requiresAttention: true,
|
|
107
|
+
attentionReason: input.attentionReason,
|
|
108
|
+
attentionTimestamp: new Date(input.attentionTimestamp),
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
function attachManagedTurnIdentity(agent, event, fromHistory) {
|
|
112
|
+
const existingTurnId = getAgentStreamEventTurnId(event);
|
|
113
|
+
if (fromHistory || existingTurnId !== undefined) {
|
|
114
|
+
return { event, turnId: existingTurnId };
|
|
115
|
+
}
|
|
116
|
+
switch (event.type) {
|
|
117
|
+
case "turn_started": {
|
|
118
|
+
const turnId = agent.activeForegroundTurnId ?? agent.activeTurnId ?? `autonomous-${randomUUID()}`;
|
|
119
|
+
return { event: { ...event, turnId }, turnId };
|
|
120
|
+
}
|
|
121
|
+
case "turn_completed":
|
|
122
|
+
case "turn_failed":
|
|
123
|
+
case "turn_canceled": {
|
|
124
|
+
const turnId = agent.activeForegroundTurnId ?? agent.activeTurnId ?? undefined;
|
|
125
|
+
return turnId ? { event: { ...event, turnId }, turnId } : { event, turnId };
|
|
126
|
+
}
|
|
127
|
+
case "timeline": {
|
|
128
|
+
// Live provider items belong to the foreground turn that owns their dispatch.
|
|
129
|
+
// Provider history deliberately keeps absent IDs because it has no daemon turn identity.
|
|
130
|
+
const turnId = agent.activeForegroundTurnId ?? agent.activeTurnId ?? undefined;
|
|
131
|
+
return turnId ? { event: { ...event, turnId }, turnId } : { event, turnId };
|
|
132
|
+
}
|
|
133
|
+
default:
|
|
134
|
+
return { event, turnId: undefined };
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
function limitAgentStreamEventContent(event) {
|
|
138
|
+
return event.type === "timeline"
|
|
139
|
+
? { ...event, item: limitAgentTimelineItemContent(event.item) }
|
|
140
|
+
: event;
|
|
141
|
+
}
|
|
142
|
+
const SYSTEM_ERROR_PREFIX = "[System Error]";
|
|
143
|
+
function attachPersistenceCwd(handle, cwd) {
|
|
144
|
+
if (!handle) {
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
return {
|
|
148
|
+
...handle,
|
|
149
|
+
metadata: {
|
|
150
|
+
...handle.metadata,
|
|
151
|
+
cwd,
|
|
152
|
+
},
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
const BUSY_STATUSES = new Set(["initializing", "running"]);
|
|
156
|
+
const AgentIdSchema = z.guid();
|
|
157
|
+
function isAgentBusy(status) {
|
|
158
|
+
return BUSY_STATUSES.has(status);
|
|
159
|
+
}
|
|
160
|
+
function isTurnTerminalEvent(event) {
|
|
161
|
+
return (event.type === "turn_completed" ||
|
|
162
|
+
event.type === "turn_failed" ||
|
|
163
|
+
event.type === "turn_canceled");
|
|
164
|
+
}
|
|
165
|
+
function abortMessage(reason, fallbackMessage) {
|
|
166
|
+
if (typeof reason === "string")
|
|
167
|
+
return reason;
|
|
168
|
+
if (reason instanceof Error)
|
|
169
|
+
return reason.message;
|
|
170
|
+
return fallbackMessage;
|
|
171
|
+
}
|
|
172
|
+
function createAbortError(signal, fallbackMessage) {
|
|
173
|
+
const message = abortMessage(signal?.reason, fallbackMessage);
|
|
174
|
+
return Object.assign(new Error(message), { name: "AbortError" });
|
|
175
|
+
}
|
|
176
|
+
function validateAgentId(agentId, source) {
|
|
177
|
+
const result = AgentIdSchema.safeParse(agentId);
|
|
178
|
+
if (!result.success) {
|
|
179
|
+
throw new Error(`${source}: agentId must be a UUID`);
|
|
180
|
+
}
|
|
181
|
+
return result.data;
|
|
182
|
+
}
|
|
183
|
+
function applyLabelPatch(labels, patch) {
|
|
184
|
+
const nextLabels = { ...labels };
|
|
185
|
+
for (const [key, value] of Object.entries(patch)) {
|
|
186
|
+
if (value === null) {
|
|
187
|
+
delete nextLabels[key];
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
nextLabels[key] = value;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return nextLabels;
|
|
194
|
+
}
|
|
195
|
+
function buildExplicitTimelineSeedForRegister(now, options) {
|
|
196
|
+
const hasTimeline = Boolean(options?.timeline?.length);
|
|
197
|
+
const hasTimelineRows = Boolean(options?.timelineRows?.length);
|
|
198
|
+
const hasTimelineNextSeq = options?.timelineNextSeq !== undefined;
|
|
199
|
+
if (!hasTimeline && !hasTimelineRows && !hasTimelineNextSeq) {
|
|
200
|
+
return null;
|
|
201
|
+
}
|
|
202
|
+
return {
|
|
203
|
+
items: options?.timeline,
|
|
204
|
+
rows: options?.timelineRows,
|
|
205
|
+
nextSeq: options?.timelineNextSeq,
|
|
206
|
+
timestamp: (options?.updatedAt ?? options?.createdAt ?? now).toISOString(),
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
function buildImportedTimelineRows(entries) {
|
|
210
|
+
const rows = [];
|
|
211
|
+
for (const entry of entries) {
|
|
212
|
+
if (entry.item.type === "user_message" && isSystemInjectedEnvelope(entry.item.text)) {
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
rows.push({
|
|
216
|
+
seq: rows.length + 1,
|
|
217
|
+
timestamp: entry.timestamp ?? new Date().toISOString(),
|
|
218
|
+
item: limitAgentTimelineItemContent(entry.item),
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
return rows;
|
|
222
|
+
}
|
|
223
|
+
function resolveImportedAgentTitle(config, timelineRows) {
|
|
224
|
+
const initialPrompt = getFirstUserMessageTextFromRows(timelineRows);
|
|
225
|
+
if (!initialPrompt) {
|
|
226
|
+
return null;
|
|
227
|
+
}
|
|
228
|
+
const { explicitTitle, provisionalTitle } = resolveCreateAgentTitles({
|
|
229
|
+
configTitle: config.title,
|
|
230
|
+
initialPrompt,
|
|
231
|
+
});
|
|
232
|
+
return explicitTitle ?? provisionalTitle ?? null;
|
|
233
|
+
}
|
|
234
|
+
function getFirstUserMessageTextFromRows(rows) {
|
|
235
|
+
for (const row of rows) {
|
|
236
|
+
const item = row.item;
|
|
237
|
+
if (item.type !== "user_message") {
|
|
238
|
+
continue;
|
|
239
|
+
}
|
|
240
|
+
const text = item.text.trim();
|
|
241
|
+
if (text) {
|
|
242
|
+
return text;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
return null;
|
|
246
|
+
}
|
|
247
|
+
function shouldDetachFromArchivedParent(parent, child) {
|
|
248
|
+
const isCrossWorkspace = parent.workspaceId !== undefined &&
|
|
249
|
+
child.workspaceId !== undefined &&
|
|
250
|
+
parent.workspaceId !== child.workspaceId;
|
|
251
|
+
return isCrossWorkspace || hasOpenAgentTab(child.labels);
|
|
252
|
+
}
|
|
253
|
+
function detachedAgentLabelPatch(labels) {
|
|
254
|
+
const patch = { [PARENT_AGENT_ID_LABEL]: null };
|
|
255
|
+
for (const label of Object.keys(labels)) {
|
|
256
|
+
if (isOpenAgentTabLabel(label)) {
|
|
257
|
+
patch[label] = null;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
return patch;
|
|
261
|
+
}
|
|
262
|
+
export class AgentManager {
|
|
263
|
+
constructor(options) {
|
|
264
|
+
this.clients = new Map();
|
|
265
|
+
this.providerEnabled = new Map();
|
|
266
|
+
this.providerDefinitions = new Map();
|
|
267
|
+
this.agents = new Map();
|
|
268
|
+
this.timelineStore = new InMemoryAgentTimelineStore();
|
|
269
|
+
this.providerSubagents = new ProviderSubagentStore();
|
|
270
|
+
this.sideConversations = new SideConversationStore();
|
|
271
|
+
this.activeSideQuestions = new Set();
|
|
272
|
+
this.agentsAwaitingInitialSnapshotPersist = new Set();
|
|
273
|
+
this.sessionEventTails = new Map();
|
|
274
|
+
this.steerEventBarriers = new Map();
|
|
275
|
+
this.foregroundMutationTails = new Map();
|
|
276
|
+
this.runs = new AgentRunState();
|
|
277
|
+
this.subscribers = new Set();
|
|
278
|
+
this.previousStatuses = new Map();
|
|
279
|
+
this.backgroundTasks = new Set();
|
|
280
|
+
this.agentRegistrationTasks = new Set();
|
|
281
|
+
this.inFlightAgentCloses = new Map();
|
|
282
|
+
this.reloadedSessionCloses = new WeakMap();
|
|
283
|
+
this.lifecycleMutationTails = new Map();
|
|
284
|
+
this.paseoToolsEnabled = true;
|
|
285
|
+
this.paseoToolCatalogFactory = null;
|
|
286
|
+
this.paseoToolPolicies = new Map();
|
|
287
|
+
this.acceptingAgentRegistrations = true;
|
|
288
|
+
this.pluginLifecycle = options.pluginLifecycle;
|
|
289
|
+
this.idFactory = options?.idFactory ?? (() => randomUUID());
|
|
290
|
+
this.registry = options?.registry;
|
|
291
|
+
this.durableTimelineStore = options?.durableTimelineStore;
|
|
292
|
+
this.onAgentAttention = options?.onAgentAttention;
|
|
293
|
+
this.onWorkspaceStateMayHaveChanged = options?.onWorkspaceStateMayHaveChanged;
|
|
294
|
+
this.mcpBaseUrl = options?.mcpBaseUrl ?? null;
|
|
295
|
+
this.mcpAuthToken = options?.mcpAuthToken ?? null;
|
|
296
|
+
this.configurePaseoTools(options);
|
|
297
|
+
this.resolvePaseoToolPolicy = options.resolvePaseoToolPolicy ?? (() => undefined);
|
|
298
|
+
this.appendSystemPrompt = options.appendSystemPrompt ?? "";
|
|
299
|
+
this.logger = options.logger.child({ module: "agent", component: "agent-manager" });
|
|
300
|
+
this.rescueTimeouts = {
|
|
301
|
+
reloadSessionCloseMs: options.rescueTimeouts?.reloadSessionCloseMs ?? RELOAD_SESSION_CLOSE_TIMEOUT_MS,
|
|
302
|
+
interruptSessionMs: options.rescueTimeouts?.interruptSessionMs ?? INTERRUPT_SESSION_TIMEOUT_MS,
|
|
303
|
+
};
|
|
304
|
+
this.beforeSteerUnavailableFallback = options.beforeSteerUnavailableFallback;
|
|
305
|
+
this.agentStreamCoalescer = new AgentStreamCoalescer({
|
|
306
|
+
windowMs: options.agentStreamCoalesceWindowMs ?? AGENT_STREAM_COALESCE_DEFAULT_WINDOW_MS,
|
|
307
|
+
timers: { setTimeout, clearTimeout },
|
|
308
|
+
onFlush: ({ agentId, item, provider, turnId }) => {
|
|
309
|
+
// This runs off a deferred timer (see AgentStreamCoalescer.scheduleFlush),
|
|
310
|
+
// so the agent's in-memory state can already be torn down by the time it
|
|
311
|
+
// fires — archived, closed, or otherwise removed between the event being
|
|
312
|
+
// buffered and the coalescing window elapsing. recordAndDispatchTimelineItem
|
|
313
|
+
// throws in that case ("Unknown agent"), and an uncaught throw out of a
|
|
314
|
+
// setTimeout callback takes the whole daemon down with it. The flush is
|
|
315
|
+
// best-effort against a live agent; drop it and move on if the agent is
|
|
316
|
+
// already gone rather than crashing every other agent's session with it.
|
|
317
|
+
try {
|
|
318
|
+
const event = this.recordAndDispatchTimelineItem(agentId, item, provider, turnId);
|
|
319
|
+
this.notifyForegroundTurnWaiters(agentId, event);
|
|
320
|
+
}
|
|
321
|
+
catch (error) {
|
|
322
|
+
this.logger.warn({ err: error, agentId }, "Dropped a coalesced timeline flush for an agent that is no longer tracked");
|
|
323
|
+
}
|
|
324
|
+
},
|
|
325
|
+
});
|
|
326
|
+
this.updateProviderRegistry({
|
|
327
|
+
providerDefinitions: options.providerDefinitions ?? {},
|
|
328
|
+
clients: options.clients ?? {},
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
configurePaseoTools(options) {
|
|
332
|
+
this.paseoToolsEnabled = options.paseoToolsEnabled ?? true;
|
|
333
|
+
this.paseoToolCatalogFactory = options.paseoToolCatalogFactory ?? null;
|
|
334
|
+
}
|
|
335
|
+
registerClient(provider, client) {
|
|
336
|
+
this.clients.set(provider, client);
|
|
337
|
+
}
|
|
338
|
+
updateProviderRegistry(input) {
|
|
339
|
+
this.providerEnabled.clear();
|
|
340
|
+
this.providerDefinitions.clear();
|
|
341
|
+
for (const [provider, definition] of Object.entries(input.providerDefinitions)) {
|
|
342
|
+
if (definition) {
|
|
343
|
+
this.providerEnabled.set(provider, definition.enabled);
|
|
344
|
+
this.providerDefinitions.set(provider, definition);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
this.clients.clear();
|
|
348
|
+
for (const [provider, client] of Object.entries(input.clients)) {
|
|
349
|
+
if (client) {
|
|
350
|
+
this.clients.set(provider, client);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
for (const provider of input.retiredProviders ?? []) {
|
|
354
|
+
for (const agent of this.agents.values()) {
|
|
355
|
+
if (agent.provider !== provider)
|
|
356
|
+
continue;
|
|
357
|
+
void this.closeAgent(agent.id).catch((error) => {
|
|
358
|
+
this.logger.warn({ err: error, agentId: agent.id, provider }, "Failed to close agent after provider retirement");
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* Repoints live agents at a renamed provider id. Must run after the registry rebuild so the
|
|
365
|
+
* new id already has a client, and before storage migration so the next persistence flush
|
|
366
|
+
* doesn't write the old id back over the migrated record.
|
|
367
|
+
*/
|
|
368
|
+
renameProviderOnLiveAgents(fromProviderId, toProviderId) {
|
|
369
|
+
for (const agent of this.agents.values()) {
|
|
370
|
+
if (agent.provider === fromProviderId) {
|
|
371
|
+
agent.provider = toProviderId;
|
|
372
|
+
}
|
|
373
|
+
if (agent.runtimeInfo?.provider === fromProviderId) {
|
|
374
|
+
agent.runtimeInfo = { ...agent.runtimeInfo, provider: toProviderId };
|
|
375
|
+
}
|
|
376
|
+
if (agent.persistence?.provider === fromProviderId) {
|
|
377
|
+
agent.persistence = { ...agent.persistence, provider: toProviderId };
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
getRegisteredProviderIds() {
|
|
382
|
+
return Array.from(this.clients.keys());
|
|
383
|
+
}
|
|
384
|
+
setAgentAttentionCallback(callback) {
|
|
385
|
+
this.onAgentAttention = callback;
|
|
386
|
+
}
|
|
387
|
+
setAgentArchivedCallback(callback) {
|
|
388
|
+
this.onAgentArchived = callback;
|
|
389
|
+
}
|
|
390
|
+
setMcpBaseUrl(url) {
|
|
391
|
+
this.mcpBaseUrl = url;
|
|
392
|
+
}
|
|
393
|
+
prepareForShutdown() {
|
|
394
|
+
this.acceptingAgentRegistrations = false;
|
|
395
|
+
}
|
|
396
|
+
setPaseoToolsEnabled(enabled) {
|
|
397
|
+
this.paseoToolsEnabled = enabled;
|
|
398
|
+
}
|
|
399
|
+
setPaseoToolCatalogFactory(factory) {
|
|
400
|
+
this.paseoToolCatalogFactory = factory;
|
|
401
|
+
}
|
|
402
|
+
getPaseoToolPolicy(agentId) {
|
|
403
|
+
return this.paseoToolPolicies.get(agentId);
|
|
404
|
+
}
|
|
405
|
+
/**
|
|
406
|
+
* Capability token the daemon's own MCP clients must present to the Agent MCP
|
|
407
|
+
* endpoint when a daemon password is configured. Read by the per-client
|
|
408
|
+
* session to authenticate its own MCP connection. Stays in the daemon — never
|
|
409
|
+
* sent to remote clients.
|
|
410
|
+
*/
|
|
411
|
+
getMcpAuthToken() {
|
|
412
|
+
return this.mcpAuthToken;
|
|
413
|
+
}
|
|
414
|
+
setAppendSystemPrompt(prompt) {
|
|
415
|
+
this.appendSystemPrompt = prompt ?? "";
|
|
416
|
+
}
|
|
417
|
+
getMetricsSnapshot() {
|
|
418
|
+
const byLifecycle = {};
|
|
419
|
+
let withActiveForegroundTurn = 0;
|
|
420
|
+
let totalItems = 0;
|
|
421
|
+
let maxItemsPerAgent = 0;
|
|
422
|
+
for (const agent of this.agents.values()) {
|
|
423
|
+
byLifecycle[agent.lifecycle] = (byLifecycle[agent.lifecycle] ?? 0) + 1;
|
|
424
|
+
if (agent.activeForegroundTurnId !== null) {
|
|
425
|
+
withActiveForegroundTurn++;
|
|
426
|
+
}
|
|
427
|
+
if (!this.timelineStore.has(agent.id)) {
|
|
428
|
+
continue;
|
|
429
|
+
}
|
|
430
|
+
const len = this.timelineStore.getItems(agent.id).length;
|
|
431
|
+
totalItems += len;
|
|
432
|
+
if (len > maxItemsPerAgent) {
|
|
433
|
+
maxItemsPerAgent = len;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
return {
|
|
437
|
+
total: this.agents.size,
|
|
438
|
+
subscriptionCount: this.subscribers.size,
|
|
439
|
+
byLifecycle,
|
|
440
|
+
withActiveForegroundTurn,
|
|
441
|
+
timelineStats: {
|
|
442
|
+
totalItems,
|
|
443
|
+
maxItemsPerAgent,
|
|
444
|
+
},
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
touchUpdatedAt(agent) {
|
|
448
|
+
const nowMs = Date.now();
|
|
449
|
+
const previousMs = agent.updatedAt.getTime();
|
|
450
|
+
const nextMs = nowMs > previousMs ? nowMs : previousMs + 1;
|
|
451
|
+
const next = new Date(nextMs);
|
|
452
|
+
agent.updatedAt = next;
|
|
453
|
+
return next;
|
|
454
|
+
}
|
|
455
|
+
nextStoredUpdatedAt(record) {
|
|
456
|
+
const previousMs = Date.parse(record.updatedAt);
|
|
457
|
+
const nowMs = Date.now();
|
|
458
|
+
const nextMs = nowMs > previousMs ? nowMs : previousMs + 1;
|
|
459
|
+
return new Date(nextMs).toISOString();
|
|
460
|
+
}
|
|
461
|
+
hasInFlightRun(agentId) {
|
|
462
|
+
const agent = this.agents.get(agentId);
|
|
463
|
+
if (!agent) {
|
|
464
|
+
return false;
|
|
465
|
+
}
|
|
466
|
+
return (agent.lifecycle === "running" ||
|
|
467
|
+
Boolean(agent.activeForegroundTurnId) ||
|
|
468
|
+
this.runs.hasRun(agentId));
|
|
469
|
+
}
|
|
470
|
+
subscribe(callback, options) {
|
|
471
|
+
const targetAgentId = options?.agentId == null ? null : validateAgentId(options.agentId, "subscribe");
|
|
472
|
+
const record = {
|
|
473
|
+
callback,
|
|
474
|
+
agentId: targetAgentId,
|
|
475
|
+
};
|
|
476
|
+
this.subscribers.add(record);
|
|
477
|
+
if (options?.replayState !== false) {
|
|
478
|
+
if (record.agentId) {
|
|
479
|
+
const agent = this.agents.get(record.agentId);
|
|
480
|
+
if (agent) {
|
|
481
|
+
callback({
|
|
482
|
+
type: "agent_state",
|
|
483
|
+
agent: { ...agent },
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
else {
|
|
488
|
+
// For global subscribers, skip internal agents during replay
|
|
489
|
+
for (const agent of this.agents.values()) {
|
|
490
|
+
if (agent.internal) {
|
|
491
|
+
continue;
|
|
492
|
+
}
|
|
493
|
+
callback({
|
|
494
|
+
type: "agent_state",
|
|
495
|
+
agent: { ...agent },
|
|
496
|
+
});
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
return () => {
|
|
501
|
+
this.subscribers.delete(record);
|
|
502
|
+
};
|
|
503
|
+
}
|
|
504
|
+
subscriptionCount() {
|
|
505
|
+
return this.subscribers.size;
|
|
506
|
+
}
|
|
507
|
+
listAgents() {
|
|
508
|
+
return Array.from(this.agents.values())
|
|
509
|
+
.filter((agent) => !agent.internal)
|
|
510
|
+
.map((agent) => Object.assign({}, agent));
|
|
511
|
+
}
|
|
512
|
+
async listImportableSessions(options) {
|
|
513
|
+
const providerEntries = Array.from(this.clients.entries()).filter(([provider, client]) => client.capabilities.supportsSessionListing &&
|
|
514
|
+
!!client.listImportableSessions &&
|
|
515
|
+
this.isProviderImportable(provider, options?.providerFilter));
|
|
516
|
+
const providerResults = await Promise.all(providerEntries.map(async ([provider, client]) => {
|
|
517
|
+
try {
|
|
518
|
+
const sessions = await withTimeout(client.listImportableSessions({
|
|
519
|
+
limit: options?.limit,
|
|
520
|
+
query: options?.query,
|
|
521
|
+
scanLimit: options?.scanLimit,
|
|
522
|
+
cwd: options?.cwd,
|
|
523
|
+
}), IMPORTABLE_SESSION_LIST_TIMEOUT_MS, `Timed out listing importable sessions for provider '${provider}' after ${IMPORTABLE_SESSION_LIST_TIMEOUT_MS}ms`);
|
|
524
|
+
return {
|
|
525
|
+
sessions: sessions
|
|
526
|
+
.filter((session) => matchesImportableSessionQuery(session, options?.query))
|
|
527
|
+
.map((session) => Object.assign(session, { provider })),
|
|
528
|
+
error: null,
|
|
529
|
+
};
|
|
530
|
+
}
|
|
531
|
+
catch (error) {
|
|
532
|
+
this.logger.warn({ err: error, provider }, "Failed to list importable sessions for provider");
|
|
533
|
+
return {
|
|
534
|
+
sessions: [],
|
|
535
|
+
error: {
|
|
536
|
+
provider,
|
|
537
|
+
message: error instanceof Error ? error.message : String(error),
|
|
538
|
+
},
|
|
539
|
+
};
|
|
540
|
+
}
|
|
541
|
+
}));
|
|
542
|
+
const sessions = providerResults.flatMap((result) => result.sessions);
|
|
543
|
+
const limit = options?.limit ?? 20;
|
|
544
|
+
return {
|
|
545
|
+
sessions: sessions
|
|
546
|
+
.sort((a, b) => b.lastActivityAt.getTime() - a.lastActivityAt.getTime())
|
|
547
|
+
.slice(0, limit),
|
|
548
|
+
providerErrors: providerResults.flatMap((result) => (result.error ? [result.error] : [])),
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
isProviderImportable(provider, providerFilter) {
|
|
552
|
+
if (this.providerEnabled.get(provider) === false) {
|
|
553
|
+
return false;
|
|
554
|
+
}
|
|
555
|
+
if (providerFilter && !providerFilter.has(provider)) {
|
|
556
|
+
return false;
|
|
557
|
+
}
|
|
558
|
+
return true;
|
|
559
|
+
}
|
|
560
|
+
async listProviderAvailability() {
|
|
561
|
+
return Promise.all(Array.from(this.clients.keys()).map((provider) => this.getProviderAvailability(provider)));
|
|
562
|
+
}
|
|
563
|
+
async getProviderAvailability(provider) {
|
|
564
|
+
const client = this.clients.get(provider);
|
|
565
|
+
if (!client) {
|
|
566
|
+
return {
|
|
567
|
+
provider,
|
|
568
|
+
available: false,
|
|
569
|
+
error: `No client registered for provider '${provider}'`,
|
|
570
|
+
};
|
|
571
|
+
}
|
|
572
|
+
try {
|
|
573
|
+
const available = await client.isAvailable();
|
|
574
|
+
return {
|
|
575
|
+
provider,
|
|
576
|
+
available,
|
|
577
|
+
error: null,
|
|
578
|
+
};
|
|
579
|
+
}
|
|
580
|
+
catch (error) {
|
|
581
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
582
|
+
this.logger.warn({ err: error, provider }, "Failed to check provider availability");
|
|
583
|
+
return {
|
|
584
|
+
provider,
|
|
585
|
+
available: false,
|
|
586
|
+
error: message,
|
|
587
|
+
};
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
async listDraftCommands(config) {
|
|
591
|
+
const normalizedConfig = await this.normalizeConfig(config, { resolveDefaultModel: false });
|
|
592
|
+
const client = this.requireClient(normalizedConfig.provider);
|
|
593
|
+
if (!normalizedConfig.model) {
|
|
594
|
+
return [];
|
|
595
|
+
}
|
|
596
|
+
const available = await client.isAvailable();
|
|
597
|
+
if (!available) {
|
|
598
|
+
throw new Error(`Provider '${normalizedConfig.provider}' is not available. Please ensure the CLI is installed.`);
|
|
599
|
+
}
|
|
600
|
+
if (client.listCommands) {
|
|
601
|
+
return await client.listCommands(normalizedConfig);
|
|
602
|
+
}
|
|
603
|
+
const session = await client.createSession(normalizedConfig);
|
|
604
|
+
try {
|
|
605
|
+
if (!session.listCommands) {
|
|
606
|
+
throw new Error(`Provider '${normalizedConfig.provider}' does not support listing commands`);
|
|
607
|
+
}
|
|
608
|
+
return await session.listCommands();
|
|
609
|
+
}
|
|
610
|
+
finally {
|
|
611
|
+
try {
|
|
612
|
+
await session.close();
|
|
613
|
+
}
|
|
614
|
+
catch (error) {
|
|
615
|
+
this.logger.warn({ err: error, provider: normalizedConfig.provider }, "Failed to close draft command listing session");
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
async listDraftFeatures(config) {
|
|
620
|
+
const normalizedConfig = await this.normalizeConfig(config, { resolveDefaultModel: false });
|
|
621
|
+
const client = this.requireClient(normalizedConfig.provider);
|
|
622
|
+
if (!normalizedConfig.model && !client.listFeatures) {
|
|
623
|
+
return [];
|
|
624
|
+
}
|
|
625
|
+
const available = await client.isAvailable();
|
|
626
|
+
if (!available) {
|
|
627
|
+
throw new Error(`Provider '${normalizedConfig.provider}' is not available. Please ensure the CLI is installed.`);
|
|
628
|
+
}
|
|
629
|
+
if (client.listFeatures) {
|
|
630
|
+
return await client.listFeatures(normalizedConfig);
|
|
631
|
+
}
|
|
632
|
+
const session = await client.createSession(normalizedConfig);
|
|
633
|
+
try {
|
|
634
|
+
return session.features ?? [];
|
|
635
|
+
}
|
|
636
|
+
finally {
|
|
637
|
+
try {
|
|
638
|
+
await session.close();
|
|
639
|
+
}
|
|
640
|
+
catch (error) {
|
|
641
|
+
this.logger.warn({ err: error, provider: normalizedConfig.provider }, "Failed to close draft feature listing session");
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
getAgent(id) {
|
|
646
|
+
const agent = this.agents.get(id);
|
|
647
|
+
return agent ? { ...agent } : null;
|
|
648
|
+
}
|
|
649
|
+
async waitForAgentClose(agentId) {
|
|
650
|
+
// Loading during reload must wait for the replacement, not resume another writer.
|
|
651
|
+
await this.lifecycleMutationTails.get(agentId);
|
|
652
|
+
await this.inFlightAgentCloses?.get(agentId)?.catch(() => undefined);
|
|
653
|
+
}
|
|
654
|
+
getTimeline(id) {
|
|
655
|
+
this.requireAgent(id);
|
|
656
|
+
return this.timelineStore.getItems(id);
|
|
657
|
+
}
|
|
658
|
+
async getTimelineRows(id) {
|
|
659
|
+
this.requireAgent(id);
|
|
660
|
+
if (this.durableTimelineStore) {
|
|
661
|
+
return await this.durableTimelineStore.getCommittedRows(id);
|
|
662
|
+
}
|
|
663
|
+
return this.timelineStore.getRows(id);
|
|
664
|
+
}
|
|
665
|
+
fetchTimeline(id, options) {
|
|
666
|
+
this.requireAgent(id);
|
|
667
|
+
return this.timelineStore.fetch(id, options);
|
|
668
|
+
}
|
|
669
|
+
listProviderSubagents(parentAgentId) {
|
|
670
|
+
this.requirePublicAgent(parentAgentId);
|
|
671
|
+
return this.providerSubagents.list(parentAgentId);
|
|
672
|
+
}
|
|
673
|
+
listProviderSubagentActivity() {
|
|
674
|
+
const publicParentIds = new Set(Array.from(this.agents.values())
|
|
675
|
+
.filter((agent) => !agent.internal)
|
|
676
|
+
.map((agent) => agent.id));
|
|
677
|
+
return this.providerSubagents
|
|
678
|
+
.listAll()
|
|
679
|
+
.filter((subagent) => publicParentIds.has(subagent.parentAgentId));
|
|
680
|
+
}
|
|
681
|
+
getProviderSubagent(parentAgentId, subagentId) {
|
|
682
|
+
this.requirePublicAgent(parentAgentId);
|
|
683
|
+
return this.providerSubagents.get(parentAgentId, subagentId);
|
|
684
|
+
}
|
|
685
|
+
fetchProviderSubagentTimeline(parentAgentId, subagentId, options) {
|
|
686
|
+
this.requirePublicAgent(parentAgentId);
|
|
687
|
+
return this.providerSubagents.fetchTimeline(parentAgentId, subagentId, options);
|
|
688
|
+
}
|
|
689
|
+
getSideConversation(parentAgentId, threadId) {
|
|
690
|
+
this.requirePublicAgent(parentAgentId);
|
|
691
|
+
return this.sideConversations.get(parentAgentId, threadId);
|
|
692
|
+
}
|
|
693
|
+
listSideConversations(parentAgentId) {
|
|
694
|
+
this.requirePublicAgent(parentAgentId);
|
|
695
|
+
return this.sideConversations.list(parentAgentId);
|
|
696
|
+
}
|
|
697
|
+
async askSideQuestion(parentAgentId, threadId, question, timeoutMs = 30000) {
|
|
698
|
+
// Both gates, in this order: an internal agent must be refused before any work starts, and the
|
|
699
|
+
// provider seam needs a live session. Checking only one of them lets a question run to
|
|
700
|
+
// completion against an agent the caller may not read back.
|
|
701
|
+
this.requirePublicAgent(parentAgentId);
|
|
702
|
+
const agent = this.requireSessionAgent(parentAgentId);
|
|
703
|
+
const activeKey = `${parentAgentId}\0${threadId}`;
|
|
704
|
+
if (this.activeSideQuestions.has(activeKey)) {
|
|
705
|
+
return {
|
|
706
|
+
status: "failed",
|
|
707
|
+
error: "A side question is already pending",
|
|
708
|
+
threading: "single_shot",
|
|
709
|
+
};
|
|
710
|
+
}
|
|
711
|
+
const history = this.sideConversations.history(parentAgentId, threadId);
|
|
712
|
+
this.dispatchSideConversationUpdate(this.sideConversations.begin(parentAgentId, threadId, question));
|
|
713
|
+
if (!agent.session.askSideQuestion) {
|
|
714
|
+
const answer = { status: "unavailable", reason: "unsupported_provider" };
|
|
715
|
+
this.completeSideConversation(parentAgentId, threadId, answer);
|
|
716
|
+
return answer;
|
|
717
|
+
}
|
|
718
|
+
this.activeSideQuestions.add(activeKey);
|
|
719
|
+
const abort = new AbortController();
|
|
720
|
+
let timer;
|
|
721
|
+
const providerAnswer = agent.session.askSideQuestion(question, history, {
|
|
722
|
+
signal: abort.signal,
|
|
723
|
+
});
|
|
724
|
+
// Attach the rejection arm before racing so a late provider failure is always observed.
|
|
725
|
+
void providerAnswer.catch(() => undefined);
|
|
726
|
+
try {
|
|
727
|
+
const answer = await Promise.race([
|
|
728
|
+
providerAnswer,
|
|
729
|
+
new Promise((settle) => {
|
|
730
|
+
timer = setTimeout(() => settle({ status: "timed_out", threading: "single_shot" }), Math.max(0, timeoutMs));
|
|
731
|
+
}),
|
|
732
|
+
]);
|
|
733
|
+
this.completeSideConversation(parentAgentId, threadId, answer);
|
|
734
|
+
return answer;
|
|
735
|
+
}
|
|
736
|
+
catch (error) {
|
|
737
|
+
const answer = {
|
|
738
|
+
status: "failed",
|
|
739
|
+
error: error instanceof Error ? error.message : "Side question failed",
|
|
740
|
+
threading: "single_shot",
|
|
741
|
+
};
|
|
742
|
+
this.completeSideConversation(parentAgentId, threadId, answer);
|
|
743
|
+
return answer;
|
|
744
|
+
}
|
|
745
|
+
finally {
|
|
746
|
+
if (timer)
|
|
747
|
+
clearTimeout(timer);
|
|
748
|
+
// Cancels the provider whenever we stopped waiting first — a timeout, a rejection, or a
|
|
749
|
+
// caller that already has its answer. Without it the provider keeps polling a forked session.
|
|
750
|
+
abort.abort();
|
|
751
|
+
this.activeSideQuestions.delete(activeKey);
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
/**
|
|
755
|
+
* The record can be wiped mid-flight by a reload, an archive, or a history clear, in which case
|
|
756
|
+
* there is nothing to update and nothing to broadcast — the caller still gets its answer.
|
|
757
|
+
*/
|
|
758
|
+
completeSideConversation(parentAgentId, threadId, answer) {
|
|
759
|
+
const record = this.sideConversations.complete(parentAgentId, threadId, answer);
|
|
760
|
+
if (record)
|
|
761
|
+
this.dispatchSideConversationUpdate(record);
|
|
762
|
+
}
|
|
763
|
+
dispatchSideConversationUpdate(record) {
|
|
764
|
+
this.dispatch({ type: "side_conversation", event: { type: "update", record } });
|
|
765
|
+
}
|
|
766
|
+
createAgent(config, agentId, options) {
|
|
767
|
+
return this.trackAgentRegistrationOperation(this.createAgentInternal(config, agentId, options));
|
|
768
|
+
}
|
|
769
|
+
async createAgentInternal(config, agentId, options) {
|
|
770
|
+
this.assertAcceptingAgentRegistrations();
|
|
771
|
+
const resolvedAgentId = validateAgentId(agentId ?? this.idFactory(), "createAgent");
|
|
772
|
+
if (this.pluginLifecycle && !config.internal) {
|
|
773
|
+
const request = await this.pluginLifecycle.before("agent.create", {
|
|
774
|
+
config,
|
|
775
|
+
env: options.env,
|
|
776
|
+
});
|
|
777
|
+
config = { ...request.config, internal: config.internal };
|
|
778
|
+
options = { ...options, env: request.env };
|
|
779
|
+
}
|
|
780
|
+
await this.deleteAgentState(resolvedAgentId);
|
|
781
|
+
const { storedConfig, launchConfig, paseoToolPolicy } = await this.prepareSessionConfig(config, resolvedAgentId, options?.env);
|
|
782
|
+
this.requireEnabledProvider(storedConfig.provider);
|
|
783
|
+
const client = await this.requireAvailableClient({
|
|
784
|
+
provider: storedConfig.provider,
|
|
785
|
+
});
|
|
786
|
+
this.paseoToolPolicies.set(resolvedAgentId, paseoToolPolicy);
|
|
787
|
+
const launchContext = await this.buildLaunchContext(resolvedAgentId, client, storedConfig.cwd, paseoToolPolicy, options?.env, { reason: "create", purpose: "interactive", workspaceId: options.workspaceId ?? null });
|
|
788
|
+
const providerLaunchConfig = this.resolveProviderLaunchConfig(launchConfig, launchContext);
|
|
789
|
+
const createOptions = this.buildCreateSessionOptions(options);
|
|
790
|
+
const session = await client.createSession(providerLaunchConfig, launchContext, createOptions);
|
|
791
|
+
await this.requireExternalMcpSupport(session, storedConfig);
|
|
792
|
+
const agent = await this.registerSession(session, storedConfig, resolvedAgentId, {
|
|
793
|
+
labels: options.labels,
|
|
794
|
+
initialTitle: options.initialTitle,
|
|
795
|
+
workspaceId: options.workspaceId,
|
|
796
|
+
owner: options.owner,
|
|
797
|
+
historyPrimed: true,
|
|
798
|
+
});
|
|
799
|
+
if (!agent.internal) {
|
|
800
|
+
this.pluginLifecycle?.emit("agent.created", {
|
|
801
|
+
agent: describeHookAgent({ ...agent, title: agent.config.title }),
|
|
802
|
+
});
|
|
803
|
+
}
|
|
804
|
+
return agent;
|
|
805
|
+
}
|
|
806
|
+
buildCreateSessionOptions(options) {
|
|
807
|
+
return options?.persistSession === undefined
|
|
808
|
+
? undefined
|
|
809
|
+
: { persistSession: options.persistSession };
|
|
810
|
+
}
|
|
811
|
+
// Reconstruct an agent from provider persistence. Callers should explicitly
|
|
812
|
+
// hydrate timeline history after resume.
|
|
813
|
+
resumeAgentFromPersistence(handle, overrides, agentId, options, resumeOptions) {
|
|
814
|
+
return this.trackAgentRegistrationOperation(this.resumeAgentFromPersistenceInternal(handle, overrides, agentId, options, resumeOptions));
|
|
815
|
+
}
|
|
816
|
+
async resumeAgentFromPersistenceInternal(handle, overrides, agentId, options, resumeOptions) {
|
|
817
|
+
this.assertAcceptingAgentRegistrations();
|
|
818
|
+
const resolvedAgentId = validateAgentId(agentId ?? this.idFactory(), "resumeAgentFromPersistence");
|
|
819
|
+
const metadata = (handle.metadata ?? {});
|
|
820
|
+
const mergedConfig = {
|
|
821
|
+
...metadata,
|
|
822
|
+
...overrides,
|
|
823
|
+
provider: handle.provider,
|
|
824
|
+
};
|
|
825
|
+
const { storedConfig, launchConfig, paseoToolPolicy } = await this.prepareSessionConfig(mergedConfig, resolvedAgentId);
|
|
826
|
+
const client = this.requireClient(handle.provider);
|
|
827
|
+
const available = await client.isAvailable();
|
|
828
|
+
if (!available) {
|
|
829
|
+
throw new Error(`Provider '${handle.provider}' is not available. Please ensure the CLI is installed.`);
|
|
830
|
+
}
|
|
831
|
+
this.paseoToolPolicies.set(resolvedAgentId, paseoToolPolicy);
|
|
832
|
+
const launchContext = await this.buildLaunchContext(resolvedAgentId, client, storedConfig.cwd, paseoToolPolicy, undefined, {
|
|
833
|
+
reason: "resume",
|
|
834
|
+
purpose: resumeOptions?.purpose ?? "interactive",
|
|
835
|
+
workspaceId: options?.workspaceId ?? null,
|
|
836
|
+
});
|
|
837
|
+
const providerLaunchConfig = this.resolveProviderLaunchConfig(launchConfig, launchContext);
|
|
838
|
+
const session = await client.resumeSession(handle, providerLaunchConfig, launchContext, resumeOptions);
|
|
839
|
+
await this.requireExternalMcpSupport(session, storedConfig);
|
|
840
|
+
return this.registerSession(session, storedConfig, resolvedAgentId, {
|
|
841
|
+
...options,
|
|
842
|
+
persistence: handle,
|
|
843
|
+
});
|
|
844
|
+
}
|
|
845
|
+
importProviderSession(input) {
|
|
846
|
+
return this.trackAgentRegistrationOperation(this.importProviderSessionInternal(input));
|
|
847
|
+
}
|
|
848
|
+
async importProviderSessionInternal(input) {
|
|
849
|
+
this.assertAcceptingAgentRegistrations();
|
|
850
|
+
const resolvedAgentId = validateAgentId(this.idFactory(), "importProviderSession");
|
|
851
|
+
this.requireEnabledProvider(input.provider);
|
|
852
|
+
const client = await this.requireAvailableClient({ provider: input.provider });
|
|
853
|
+
if (!client.importSession) {
|
|
854
|
+
throw new Error(`Provider '${input.provider}' does not support importing sessions`);
|
|
855
|
+
}
|
|
856
|
+
const { storedConfig, launchConfig, paseoToolPolicy } = await this.prepareSessionConfig({
|
|
857
|
+
provider: input.provider,
|
|
858
|
+
cwd: input.cwd,
|
|
859
|
+
}, resolvedAgentId);
|
|
860
|
+
this.paseoToolPolicies.set(resolvedAgentId, paseoToolPolicy);
|
|
861
|
+
const launchContext = await this.buildLaunchContext(resolvedAgentId, client, storedConfig.cwd, paseoToolPolicy, undefined, { reason: "import", purpose: "interactive", workspaceId: input.workspaceId });
|
|
862
|
+
const providerLaunchConfig = this.resolveProviderLaunchConfig(launchConfig, launchContext);
|
|
863
|
+
const imported = await client.importSession({
|
|
864
|
+
providerHandleId: input.providerHandleId,
|
|
865
|
+
cwd: input.cwd,
|
|
866
|
+
}, { config: providerLaunchConfig, storedConfig, launchContext });
|
|
867
|
+
let handedToRegistration = false;
|
|
868
|
+
try {
|
|
869
|
+
const importedConfig = await this.normalizeConfig(stripInternalPaseoMcpServer(imported.config));
|
|
870
|
+
const timelineRows = buildImportedTimelineRows(imported.timeline);
|
|
871
|
+
const initialTitle = resolveImportedAgentTitle(importedConfig, timelineRows);
|
|
872
|
+
handedToRegistration = true;
|
|
873
|
+
const agent = await this.registerSession(imported.session, importedConfig, resolvedAgentId, {
|
|
874
|
+
labels: input.labels,
|
|
875
|
+
workspaceId: input.workspaceId,
|
|
876
|
+
timelineRows,
|
|
877
|
+
timelineNextSeq: timelineRows.length + 1,
|
|
878
|
+
persistence: imported.persistence,
|
|
879
|
+
historyPrimed: true,
|
|
880
|
+
initialTitle,
|
|
881
|
+
publishWhenReady: true,
|
|
882
|
+
});
|
|
883
|
+
for (const event of imported.providerSubagentEvents ?? []) {
|
|
884
|
+
const update = this.providerSubagents.apply(agent.id, event.provider, event.event);
|
|
885
|
+
this.dispatch({ type: "provider_subagent", event: update });
|
|
886
|
+
}
|
|
887
|
+
return agent;
|
|
888
|
+
}
|
|
889
|
+
finally {
|
|
890
|
+
if (!handedToRegistration) {
|
|
891
|
+
await this.closeUnregisteredSession(imported.session);
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
// Hot-reload an active agent session with config overrides. By default the
|
|
896
|
+
// in-memory timeline is preserved (used for voice-mode toggles and similar
|
|
897
|
+
// config swaps). When `rehydrateFromDisk` is set, the timeline is wiped so a
|
|
898
|
+
// new epoch is minted and provider history is re-streamed — this is what the
|
|
899
|
+
// user-facing "Reload agent" action wants when the on-disk session was
|
|
900
|
+
// mutated outside Paseo.
|
|
901
|
+
reloadAgentSession(agentId, overrides, options) {
|
|
902
|
+
return this.trackAgentRegistrationOperation(this.runLifecycleMutation(agentId, () => this.reloadAgentSessionInternal(agentId, overrides, options)));
|
|
903
|
+
}
|
|
904
|
+
async reloadAgentSessionInternal(agentId, overrides, options) {
|
|
905
|
+
this.assertAcceptingAgentRegistrations();
|
|
906
|
+
let existing = this.requireSessionAgent(agentId);
|
|
907
|
+
if (this.hasInFlightRun(agentId)) {
|
|
908
|
+
await this.cancelAgentRunBefore(agentId, "reload");
|
|
909
|
+
existing = this.requireSessionAgent(agentId);
|
|
910
|
+
}
|
|
911
|
+
const rehydrateFromDisk = options?.rehydrateFromDisk ?? false;
|
|
912
|
+
const preservedHistoryPrimed = existing.historyPrimed;
|
|
913
|
+
const preservedLastUsage = existing.lastUsage;
|
|
914
|
+
const preservedLastError = existing.lastError;
|
|
915
|
+
const preservedAttention = existing.attention;
|
|
916
|
+
const handle = existing.persistence;
|
|
917
|
+
const provider = handle?.provider ?? existing.provider;
|
|
918
|
+
const client = this.requireClient(provider);
|
|
919
|
+
const refreshConfig = {
|
|
920
|
+
...existing.config,
|
|
921
|
+
...overrides,
|
|
922
|
+
provider,
|
|
923
|
+
};
|
|
924
|
+
const { storedConfig, launchConfig, paseoToolPolicy } = await this.prepareSessionConfig(refreshConfig, agentId);
|
|
925
|
+
const hadPreviousPaseoToolPolicy = this.paseoToolPolicies.has(agentId);
|
|
926
|
+
const previousPaseoToolPolicy = this.paseoToolPolicies.get(agentId);
|
|
927
|
+
const launchContext = await this.buildLaunchContext(agentId, client, storedConfig.cwd, paseoToolPolicy, undefined, { reason: "refresh", purpose: "interactive", workspaceId: existing.workspaceId });
|
|
928
|
+
const providerLaunchConfig = this.resolveProviderLaunchConfig(launchConfig, launchContext);
|
|
929
|
+
if (Object.keys(storedConfig.mcpServers ?? {}).length > 0 &&
|
|
930
|
+
existing.session.capabilities.supportsMcpServers !== true) {
|
|
931
|
+
throw new Error(`Provider '${provider}' does not support MCP servers`);
|
|
932
|
+
}
|
|
933
|
+
let session;
|
|
934
|
+
let closedExisting;
|
|
935
|
+
let handedToRegistration = false;
|
|
936
|
+
try {
|
|
937
|
+
// A persisted thread can have only one writer, even when its turn is idle.
|
|
938
|
+
await this.closeReloadedSession(existing.session, agentId);
|
|
939
|
+
await this.drainSessionEvents(agentId);
|
|
940
|
+
this.cancelRunningProviderSubagents(agentId);
|
|
941
|
+
closedExisting = this.prepareAgentForClosure(existing, "agent reloaded");
|
|
942
|
+
await this.persistSnapshot(closedExisting);
|
|
943
|
+
this.assertAcceptingAgentRegistrations();
|
|
944
|
+
this.paseoToolPolicies.set(agentId, paseoToolPolicy);
|
|
945
|
+
session = handle
|
|
946
|
+
? await client.resumeSession(handle, providerLaunchConfig, launchContext)
|
|
947
|
+
: await client.createSession(providerLaunchConfig, launchContext);
|
|
948
|
+
await this.requireExternalMcpSupport(session, storedConfig);
|
|
949
|
+
this.assertAcceptingAgentRegistrations();
|
|
950
|
+
if (rehydrateFromDisk) {
|
|
951
|
+
// Wipe the in-memory timeline so registerSession mints a new epoch and
|
|
952
|
+
// hydrateTimelineFromProvider re-streams the freshly read provider history.
|
|
953
|
+
this.timelineStore.delete(agentId);
|
|
954
|
+
this.discardReloadedChildren(agentId);
|
|
955
|
+
}
|
|
956
|
+
// Preserve existing labels and timeline during reload.
|
|
957
|
+
handedToRegistration = true;
|
|
958
|
+
return this.registerSession(session, storedConfig, agentId, {
|
|
959
|
+
labels: existing.labels,
|
|
960
|
+
workspaceId: existing.workspaceId,
|
|
961
|
+
owner: existing.owner,
|
|
962
|
+
createdAt: existing.createdAt,
|
|
963
|
+
updatedAt: existing.updatedAt,
|
|
964
|
+
lastUserMessageAt: existing.lastUserMessageAt,
|
|
965
|
+
historyPrimed: rehydrateFromDisk ? false : preservedHistoryPrimed,
|
|
966
|
+
lastUsage: preservedLastUsage,
|
|
967
|
+
lastError: preservedLastError,
|
|
968
|
+
attention: preservedAttention,
|
|
969
|
+
});
|
|
970
|
+
}
|
|
971
|
+
catch (error) {
|
|
972
|
+
if (closedExisting) {
|
|
973
|
+
this.emitClosedAgent(closedExisting, { persist: false });
|
|
974
|
+
}
|
|
975
|
+
else if (this.agents.get(agentId) === existing) {
|
|
976
|
+
existing.lifecycle = "error";
|
|
977
|
+
existing.lastError = error instanceof Error ? error.message : String(error);
|
|
978
|
+
this.emitState(existing);
|
|
979
|
+
}
|
|
980
|
+
throw error;
|
|
981
|
+
}
|
|
982
|
+
finally {
|
|
983
|
+
if (!handedToRegistration) {
|
|
984
|
+
if (hadPreviousPaseoToolPolicy) {
|
|
985
|
+
this.paseoToolPolicies.set(agentId, previousPaseoToolPolicy);
|
|
986
|
+
}
|
|
987
|
+
else {
|
|
988
|
+
this.paseoToolPolicies.delete(agentId);
|
|
989
|
+
}
|
|
990
|
+
if (session) {
|
|
991
|
+
await this.closeUnregisteredSession(session);
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
// Both child registries are dropped on rehydrate for the same reason: the timeline is
|
|
997
|
+
// about to be re-streamed from the provider, so anything hanging off the old epoch is
|
|
998
|
+
// stale. Extracted from reloadAgentSessionInternal because the fork's side-conversation
|
|
999
|
+
// loop is the branch that takes that method to complexity 21 against oxlint's max of 20 —
|
|
1000
|
+
// upstream sits exactly at the limit, so the fork has to pay its own branch back.
|
|
1001
|
+
discardReloadedChildren(agentId) {
|
|
1002
|
+
for (const event of this.sideConversations.deleteParent(agentId)) {
|
|
1003
|
+
this.dispatch({ type: "side_conversation", event });
|
|
1004
|
+
}
|
|
1005
|
+
for (const event of this.providerSubagents.deleteParent(agentId)) {
|
|
1006
|
+
this.dispatch({ type: "provider_subagent", event });
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
async closeReloadedSession(session, agentId) {
|
|
1010
|
+
let operation = this.reloadedSessionCloses.get(session);
|
|
1011
|
+
if (!operation) {
|
|
1012
|
+
operation = session.close();
|
|
1013
|
+
this.reloadedSessionCloses.set(session, operation);
|
|
1014
|
+
// Keep pending closes across request timeouts; a retry must await the same release.
|
|
1015
|
+
void operation.catch(() => this.reloadedSessionCloses.delete(session));
|
|
1016
|
+
}
|
|
1017
|
+
const result = await this.waitWithTimeout({
|
|
1018
|
+
operation,
|
|
1019
|
+
timeoutMs: this.rescueTimeouts.reloadSessionCloseMs,
|
|
1020
|
+
onLateError: (error) => {
|
|
1021
|
+
this.logger.warn({ err: error, agentId }, "Previous session close failed after refresh timeout");
|
|
1022
|
+
},
|
|
1023
|
+
});
|
|
1024
|
+
if (result === "timed_out") {
|
|
1025
|
+
throw new Error("Timed out closing previous session during refresh");
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
async waitWithTimeout(options) {
|
|
1029
|
+
let didTimeOut = false;
|
|
1030
|
+
let timer = null;
|
|
1031
|
+
const operation = options.operation
|
|
1032
|
+
.then(() => "completed")
|
|
1033
|
+
.catch((error) => {
|
|
1034
|
+
if (didTimeOut) {
|
|
1035
|
+
options.onLateError?.(error);
|
|
1036
|
+
return "timed_out";
|
|
1037
|
+
}
|
|
1038
|
+
throw error;
|
|
1039
|
+
});
|
|
1040
|
+
try {
|
|
1041
|
+
return await Promise.race([
|
|
1042
|
+
operation,
|
|
1043
|
+
new Promise((resolvePromise) => {
|
|
1044
|
+
timer = setTimeout(() => {
|
|
1045
|
+
didTimeOut = true;
|
|
1046
|
+
resolvePromise("timed_out");
|
|
1047
|
+
}, options.timeoutMs);
|
|
1048
|
+
}),
|
|
1049
|
+
]);
|
|
1050
|
+
}
|
|
1051
|
+
finally {
|
|
1052
|
+
if (timer) {
|
|
1053
|
+
clearTimeout(timer);
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
closeAgent(agentId) {
|
|
1058
|
+
const existing = this.inFlightAgentCloses.get(agentId);
|
|
1059
|
+
if (existing) {
|
|
1060
|
+
return existing;
|
|
1061
|
+
}
|
|
1062
|
+
const close = this.runLifecycleMutation(agentId, async () => {
|
|
1063
|
+
// A preceding reload or archive may already have closed the durable agent.
|
|
1064
|
+
if (this.agents.has(agentId))
|
|
1065
|
+
await this.closeAgentRuntime(agentId);
|
|
1066
|
+
});
|
|
1067
|
+
this.inFlightAgentCloses.set(agentId, close);
|
|
1068
|
+
const clearClose = () => {
|
|
1069
|
+
if (this.inFlightAgentCloses.get(agentId) === close) {
|
|
1070
|
+
this.inFlightAgentCloses.delete(agentId);
|
|
1071
|
+
}
|
|
1072
|
+
};
|
|
1073
|
+
void close.then(clearClose, clearClose);
|
|
1074
|
+
return close;
|
|
1075
|
+
}
|
|
1076
|
+
async closeAgentRuntime(agentId) {
|
|
1077
|
+
const agent = this.requireAgent(agentId);
|
|
1078
|
+
this.logger.trace({
|
|
1079
|
+
agentId,
|
|
1080
|
+
provider: agent.provider,
|
|
1081
|
+
sessionId: agent.persistence?.sessionId ?? undefined,
|
|
1082
|
+
turnId: agent.activeForegroundTurnId ?? undefined,
|
|
1083
|
+
lifecycle: agent.lifecycle,
|
|
1084
|
+
activeForegroundTurnId: agent.activeForegroundTurnId,
|
|
1085
|
+
pendingPermissions: agent.pendingPermissions.size,
|
|
1086
|
+
}, "agent.manager.close.start");
|
|
1087
|
+
await this.drainSessionEvents(agentId);
|
|
1088
|
+
this.cancelRunningProviderSubagents(agentId);
|
|
1089
|
+
const closedAgent = this.prepareAgentForClosure(agent, "agent closed");
|
|
1090
|
+
let closeError;
|
|
1091
|
+
try {
|
|
1092
|
+
await agent.session.close();
|
|
1093
|
+
}
|
|
1094
|
+
catch (error) {
|
|
1095
|
+
closeError = error;
|
|
1096
|
+
}
|
|
1097
|
+
let persistError;
|
|
1098
|
+
try {
|
|
1099
|
+
await this.persistSnapshot(closedAgent);
|
|
1100
|
+
}
|
|
1101
|
+
catch (error) {
|
|
1102
|
+
persistError = error;
|
|
1103
|
+
}
|
|
1104
|
+
this.emitClosedAgent(closedAgent, { persist: false });
|
|
1105
|
+
this.logger.trace({
|
|
1106
|
+
agentId,
|
|
1107
|
+
provider: closedAgent.provider,
|
|
1108
|
+
sessionId: closedAgent.persistence?.sessionId ?? undefined,
|
|
1109
|
+
}, "agent.manager.close.complete");
|
|
1110
|
+
if (closeError !== undefined) {
|
|
1111
|
+
throw closeError;
|
|
1112
|
+
}
|
|
1113
|
+
if (persistError !== undefined) {
|
|
1114
|
+
throw persistError;
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
cancelRunningProviderSubagents(parentAgentId) {
|
|
1118
|
+
for (const subagent of this.providerSubagents.list(parentAgentId)) {
|
|
1119
|
+
if (subagent.status !== "running") {
|
|
1120
|
+
continue;
|
|
1121
|
+
}
|
|
1122
|
+
const event = this.providerSubagents.apply(parentAgentId, subagent.provider, {
|
|
1123
|
+
type: "upsert",
|
|
1124
|
+
id: subagent.id,
|
|
1125
|
+
status: "canceled",
|
|
1126
|
+
});
|
|
1127
|
+
this.dispatch({ type: "provider_subagent", event });
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
async archiveAgent(agentId) {
|
|
1131
|
+
return this.runLifecycleMutation(agentId, () => this.archiveAgentUnlocked(agentId));
|
|
1132
|
+
}
|
|
1133
|
+
async archiveAgentUnlocked(agentId) {
|
|
1134
|
+
const agent = this.requireAgent(agentId);
|
|
1135
|
+
if (!this.registry) {
|
|
1136
|
+
throw new Error("Agent storage is not configured");
|
|
1137
|
+
}
|
|
1138
|
+
await this.registry.applySnapshot(agent, {
|
|
1139
|
+
internal: agent.internal,
|
|
1140
|
+
});
|
|
1141
|
+
const stored = await this.registry.get(agentId);
|
|
1142
|
+
if (!stored) {
|
|
1143
|
+
throw new Error(`Agent ${agentId} not found in storage after snapshot`);
|
|
1144
|
+
}
|
|
1145
|
+
const { archivedAt } = await this.markRecordArchived(stored);
|
|
1146
|
+
agent.updatedAt = new Date(archivedAt);
|
|
1147
|
+
await this.closeAgentRuntime(agentId);
|
|
1148
|
+
await this.syncNativeArchiveState(stored.provider, stored.persistence, "archive");
|
|
1149
|
+
this.discardRetainedAgentState(agentId);
|
|
1150
|
+
await this.cascadeArchiveChildren(agentId);
|
|
1151
|
+
return { archivedAt };
|
|
1152
|
+
}
|
|
1153
|
+
// Children created via the MCP `create_agent` tool carry the parent-agent-id
|
|
1154
|
+
// label pointing back at the caller. Archiving the parent cascades to those
|
|
1155
|
+
// children so subagent fleets don't outlive their orchestrator. Detached
|
|
1156
|
+
// handoff agents omit this label, so they stand outside the cascade.
|
|
1157
|
+
async cascadeArchiveChildren(parentAgentId) {
|
|
1158
|
+
const registry = this.registry;
|
|
1159
|
+
if (!registry) {
|
|
1160
|
+
return;
|
|
1161
|
+
}
|
|
1162
|
+
const records = await registry.list();
|
|
1163
|
+
const parent = records.find((record) => record.id === parentAgentId);
|
|
1164
|
+
if (!parent) {
|
|
1165
|
+
throw new Error(`Archived parent ${parentAgentId} not found in storage`);
|
|
1166
|
+
}
|
|
1167
|
+
for (const record of records) {
|
|
1168
|
+
if (record.archivedAt) {
|
|
1169
|
+
continue;
|
|
1170
|
+
}
|
|
1171
|
+
if (record.labels?.[PARENT_AGENT_ID_LABEL] !== parentAgentId) {
|
|
1172
|
+
continue;
|
|
1173
|
+
}
|
|
1174
|
+
const child = await registry.get(record.id);
|
|
1175
|
+
if (!child || child.archivedAt || child.labels?.[PARENT_AGENT_ID_LABEL] !== parentAgentId) {
|
|
1176
|
+
continue;
|
|
1177
|
+
}
|
|
1178
|
+
await this.runLifecycleMutation(child.id, async () => {
|
|
1179
|
+
const currentChild = await registry.get(child.id);
|
|
1180
|
+
if (!currentChild ||
|
|
1181
|
+
currentChild.archivedAt ||
|
|
1182
|
+
currentChild.labels?.[PARENT_AGENT_ID_LABEL] !== parentAgentId) {
|
|
1183
|
+
return;
|
|
1184
|
+
}
|
|
1185
|
+
if (shouldDetachFromArchivedParent(parent, currentChild)) {
|
|
1186
|
+
await this.detachAgentUnlocked(currentChild.id);
|
|
1187
|
+
}
|
|
1188
|
+
else if (this.agents.has(currentChild.id)) {
|
|
1189
|
+
await this.archiveAgentUnlocked(currentChild.id);
|
|
1190
|
+
}
|
|
1191
|
+
else {
|
|
1192
|
+
await this.archiveSnapshot(currentChild.id, new Date().toISOString());
|
|
1193
|
+
}
|
|
1194
|
+
});
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1197
|
+
async markRecordArchived(record) {
|
|
1198
|
+
const archivedAt = new Date().toISOString();
|
|
1199
|
+
const archivedRecord = await this.persistArchivedRecord(record, {
|
|
1200
|
+
archivedAt,
|
|
1201
|
+
updatedAt: archivedAt,
|
|
1202
|
+
});
|
|
1203
|
+
if (this.agents.has(record.id)) {
|
|
1204
|
+
this.notifyAgentState(record.id);
|
|
1205
|
+
}
|
|
1206
|
+
else if (!archivedRecord.internal) {
|
|
1207
|
+
this.dispatchStoredAgentState(archivedRecord);
|
|
1208
|
+
}
|
|
1209
|
+
await this.fireAgentArchived(record.id);
|
|
1210
|
+
return archivedRecord;
|
|
1211
|
+
}
|
|
1212
|
+
async persistArchivedRecord(record, options) {
|
|
1213
|
+
const archivedRecord = buildArchivedAgentRecord(record, options);
|
|
1214
|
+
await this.requireRegistry().upsert(archivedRecord);
|
|
1215
|
+
if (!record.archivedAt && !record.internal) {
|
|
1216
|
+
this.pluginLifecycle?.emit("agent.archived", {
|
|
1217
|
+
agent: describeHookAgent(archivedRecord),
|
|
1218
|
+
archivedAt: archivedRecord.archivedAt,
|
|
1219
|
+
});
|
|
1220
|
+
}
|
|
1221
|
+
return archivedRecord;
|
|
1222
|
+
}
|
|
1223
|
+
async fireAgentArchived(agentId) {
|
|
1224
|
+
const callback = this.onAgentArchived;
|
|
1225
|
+
if (!callback) {
|
|
1226
|
+
return;
|
|
1227
|
+
}
|
|
1228
|
+
try {
|
|
1229
|
+
await callback(agentId);
|
|
1230
|
+
}
|
|
1231
|
+
catch (error) {
|
|
1232
|
+
this.logger.warn({ err: error, agentId }, "onAgentArchived callback failed");
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
dispatchStoredAgentState(record) {
|
|
1236
|
+
const updatedAt = new Date(record.updatedAt);
|
|
1237
|
+
const attention = record.requiresAttention && record.attentionReason && record.attentionTimestamp
|
|
1238
|
+
? {
|
|
1239
|
+
requiresAttention: true,
|
|
1240
|
+
attentionReason: record.attentionReason,
|
|
1241
|
+
attentionTimestamp: new Date(record.attentionTimestamp),
|
|
1242
|
+
}
|
|
1243
|
+
: { requiresAttention: false };
|
|
1244
|
+
this.dispatch({
|
|
1245
|
+
type: "agent_state",
|
|
1246
|
+
agent: {
|
|
1247
|
+
id: record.id,
|
|
1248
|
+
provider: record.provider,
|
|
1249
|
+
cwd: record.cwd,
|
|
1250
|
+
workspaceId: record.workspaceId,
|
|
1251
|
+
owner: record.owner,
|
|
1252
|
+
session: null,
|
|
1253
|
+
capabilities: STORED_AGENT_CAPABILITIES,
|
|
1254
|
+
config: buildStoredAgentConfig(record),
|
|
1255
|
+
runtimeInfo: undefined,
|
|
1256
|
+
lifecycle: "closed",
|
|
1257
|
+
createdAt: new Date(record.createdAt),
|
|
1258
|
+
updatedAt,
|
|
1259
|
+
availableModes: [],
|
|
1260
|
+
features: record.features,
|
|
1261
|
+
currentModeId: record.lastModeId ?? null,
|
|
1262
|
+
pendingPermissions: new Map(),
|
|
1263
|
+
bufferedPermissionResolutions: new Map(),
|
|
1264
|
+
inFlightPermissionResponses: new Set(),
|
|
1265
|
+
pendingReplacement: false,
|
|
1266
|
+
activeForegroundTurnId: null,
|
|
1267
|
+
activeTurnId: null,
|
|
1268
|
+
activeTurnStartedAt: null,
|
|
1269
|
+
foregroundTurnWaiters: new Set(),
|
|
1270
|
+
finalizedForegroundTurnIds: new Set(),
|
|
1271
|
+
unsubscribeSession: null,
|
|
1272
|
+
persistence: record.persistence ?? null,
|
|
1273
|
+
historyPrimed: true,
|
|
1274
|
+
lastUserMessageAt: record.lastUserMessageAt ? new Date(record.lastUserMessageAt) : null,
|
|
1275
|
+
lastUsage: undefined,
|
|
1276
|
+
lastError: record.lastError ?? undefined,
|
|
1277
|
+
attention,
|
|
1278
|
+
internal: record.internal,
|
|
1279
|
+
labels: record.labels,
|
|
1280
|
+
},
|
|
1281
|
+
});
|
|
1282
|
+
}
|
|
1283
|
+
async setAgentMode(agentId, modeId) {
|
|
1284
|
+
const agent = this.requireSessionAgent(agentId);
|
|
1285
|
+
const notice = (await agent.session.setMode(modeId)) ?? null;
|
|
1286
|
+
await this.drainSessionEvents(agentId);
|
|
1287
|
+
const currentMode = (await agent.session.getCurrentMode()) ?? modeId;
|
|
1288
|
+
agent.config.modeId = currentMode ?? undefined;
|
|
1289
|
+
agent.currentModeId = currentMode;
|
|
1290
|
+
// Update runtimeInfo to reflect the new mode
|
|
1291
|
+
if (agent.runtimeInfo) {
|
|
1292
|
+
agent.runtimeInfo = { ...agent.runtimeInfo, modeId: currentMode };
|
|
1293
|
+
}
|
|
1294
|
+
this.touchUpdatedAt(agent);
|
|
1295
|
+
this.emitState(agent);
|
|
1296
|
+
return notice;
|
|
1297
|
+
}
|
|
1298
|
+
async setAgentModel(agentId, modelId) {
|
|
1299
|
+
const agent = this.requireSessionAgent(agentId);
|
|
1300
|
+
const normalizedModelId = typeof modelId === "string" && modelId.trim().length > 0 ? modelId : null;
|
|
1301
|
+
if (agent.session.setModel) {
|
|
1302
|
+
await agent.session.setModel(normalizedModelId);
|
|
1303
|
+
}
|
|
1304
|
+
await this.drainSessionEvents(agentId);
|
|
1305
|
+
agent.config.model = normalizedModelId ?? undefined;
|
|
1306
|
+
if (agent.runtimeInfo) {
|
|
1307
|
+
agent.runtimeInfo = { ...agent.runtimeInfo, model: normalizedModelId };
|
|
1308
|
+
}
|
|
1309
|
+
this.touchUpdatedAt(agent);
|
|
1310
|
+
this.emitState(agent);
|
|
1311
|
+
}
|
|
1312
|
+
async setAgentThinkingOption(agentId, thinkingOptionId) {
|
|
1313
|
+
const agent = this.requireSessionAgent(agentId);
|
|
1314
|
+
const normalizedThinkingOptionId = typeof thinkingOptionId === "string" && thinkingOptionId.trim().length > 0
|
|
1315
|
+
? thinkingOptionId
|
|
1316
|
+
: null;
|
|
1317
|
+
let notice = null;
|
|
1318
|
+
if (agent.session.setThinkingOption) {
|
|
1319
|
+
notice = (await agent.session.setThinkingOption(normalizedThinkingOptionId)) ?? null;
|
|
1320
|
+
}
|
|
1321
|
+
await this.drainSessionEvents(agentId);
|
|
1322
|
+
agent.config.thinkingOptionId = normalizedThinkingOptionId ?? undefined;
|
|
1323
|
+
if (agent.runtimeInfo) {
|
|
1324
|
+
agent.runtimeInfo = {
|
|
1325
|
+
...agent.runtimeInfo,
|
|
1326
|
+
thinkingOptionId: normalizedThinkingOptionId,
|
|
1327
|
+
};
|
|
1328
|
+
}
|
|
1329
|
+
this.touchUpdatedAt(agent);
|
|
1330
|
+
this.emitState(agent);
|
|
1331
|
+
return notice;
|
|
1332
|
+
}
|
|
1333
|
+
async setAgentFeature(agentId, featureId, value) {
|
|
1334
|
+
const agent = this.requireAgent(agentId);
|
|
1335
|
+
if (!agent.session.setFeature) {
|
|
1336
|
+
throw new Error("Agent session does not support setting features");
|
|
1337
|
+
}
|
|
1338
|
+
await agent.session.setFeature(featureId, value);
|
|
1339
|
+
await this.drainSessionEvents(agentId);
|
|
1340
|
+
agent.config.featureValues = { ...agent.config.featureValues, [featureId]: value };
|
|
1341
|
+
this.touchUpdatedAt(agent);
|
|
1342
|
+
this.emitState(agent);
|
|
1343
|
+
}
|
|
1344
|
+
async setTitle(agentId, title) {
|
|
1345
|
+
const agent = this.requireAgent(agentId);
|
|
1346
|
+
const normalizedTitle = title.trim();
|
|
1347
|
+
if (!normalizedTitle) {
|
|
1348
|
+
return;
|
|
1349
|
+
}
|
|
1350
|
+
if (this.agentsAwaitingInitialSnapshotPersist.has(agent.id) &&
|
|
1351
|
+
this.registry &&
|
|
1352
|
+
(await this.registry.get(agent.id)) === null) {
|
|
1353
|
+
return;
|
|
1354
|
+
}
|
|
1355
|
+
this.touchUpdatedAt(agent);
|
|
1356
|
+
await this.persistSnapshot(agent, { title: normalizedTitle });
|
|
1357
|
+
this.emitState(agent, { persist: false });
|
|
1358
|
+
}
|
|
1359
|
+
async setLabels(agentId, labels) {
|
|
1360
|
+
await this.runLifecycleMutation(agentId, async () => {
|
|
1361
|
+
const agent = this.requireAgent(agentId);
|
|
1362
|
+
await this.writeLabels(agent.id, labels);
|
|
1363
|
+
});
|
|
1364
|
+
}
|
|
1365
|
+
async writeLabels(agentId, patch) {
|
|
1366
|
+
const liveAgent = this.agents.get(agentId);
|
|
1367
|
+
if (liveAgent) {
|
|
1368
|
+
liveAgent.labels = applyLabelPatch(liveAgent.labels, patch);
|
|
1369
|
+
this.touchUpdatedAt(liveAgent);
|
|
1370
|
+
await this.persistSnapshot(liveAgent);
|
|
1371
|
+
this.emitState(liveAgent, { persist: false });
|
|
1372
|
+
const record = this.registry ? await this.registry.get(agentId) : null;
|
|
1373
|
+
return { record, live: true };
|
|
1374
|
+
}
|
|
1375
|
+
const nextRecord = await this.writeStoredMetadata(agentId, { labels: patch });
|
|
1376
|
+
return { record: nextRecord, live: false };
|
|
1377
|
+
}
|
|
1378
|
+
async writeStoredMetadata(agentId, patch) {
|
|
1379
|
+
const registry = this.requireRegistry();
|
|
1380
|
+
const record = await registry.get(agentId);
|
|
1381
|
+
if (!record) {
|
|
1382
|
+
throw new Error(`Agent not found: ${agentId}`);
|
|
1383
|
+
}
|
|
1384
|
+
const nextRecord = {
|
|
1385
|
+
...record,
|
|
1386
|
+
...(patch.title ? { title: patch.title } : {}),
|
|
1387
|
+
...(patch.labels ? { labels: applyLabelPatch(record.labels, patch.labels) } : {}),
|
|
1388
|
+
updatedAt: this.nextStoredUpdatedAt(record),
|
|
1389
|
+
};
|
|
1390
|
+
await registry.upsert(nextRecord);
|
|
1391
|
+
return nextRecord;
|
|
1392
|
+
}
|
|
1393
|
+
async detachAgent(agentId) {
|
|
1394
|
+
return this.runLifecycleMutation(agentId, () => this.detachAgentUnlocked(agentId));
|
|
1395
|
+
}
|
|
1396
|
+
async detachAgentUnlocked(agentId) {
|
|
1397
|
+
const registry = this.requireRegistry();
|
|
1398
|
+
const liveAgent = this.agents.get(agentId);
|
|
1399
|
+
if (liveAgent) {
|
|
1400
|
+
const previousParentAgentId = getParentAgentIdFromLabels(liveAgent.labels);
|
|
1401
|
+
if (!previousParentAgentId) {
|
|
1402
|
+
await this.persistSnapshot(liveAgent);
|
|
1403
|
+
const record = await registry.get(agentId);
|
|
1404
|
+
if (!record) {
|
|
1405
|
+
throw new Error(`Agent not found in storage after detach: ${agentId}`);
|
|
1406
|
+
}
|
|
1407
|
+
return { record, live: true, previousParentAgentId: null };
|
|
1408
|
+
}
|
|
1409
|
+
const { record } = await this.writeLabels(agentId, detachedAgentLabelPatch(liveAgent.labels));
|
|
1410
|
+
if (!record) {
|
|
1411
|
+
throw new Error(`Agent not found in storage after detach: ${agentId}`);
|
|
1412
|
+
}
|
|
1413
|
+
return { record, live: true, previousParentAgentId };
|
|
1414
|
+
}
|
|
1415
|
+
const record = await registry.get(agentId);
|
|
1416
|
+
if (!record) {
|
|
1417
|
+
throw new Error(`Agent not found: ${agentId}`);
|
|
1418
|
+
}
|
|
1419
|
+
const previousParentAgentId = getParentAgentIdFromLabels(record.labels);
|
|
1420
|
+
if (!previousParentAgentId) {
|
|
1421
|
+
return { record, live: false, previousParentAgentId: null };
|
|
1422
|
+
}
|
|
1423
|
+
const result = await this.writeLabels(agentId, detachedAgentLabelPatch(record.labels));
|
|
1424
|
+
if (!result.record) {
|
|
1425
|
+
throw new Error(`Agent not found in storage after detach: ${agentId}`);
|
|
1426
|
+
}
|
|
1427
|
+
return { record: result.record, live: false, previousParentAgentId };
|
|
1428
|
+
}
|
|
1429
|
+
notifyAgentState(agentId) {
|
|
1430
|
+
const agent = this.agents.get(agentId);
|
|
1431
|
+
if (!agent || agent.internal) {
|
|
1432
|
+
return;
|
|
1433
|
+
}
|
|
1434
|
+
this.touchUpdatedAt(agent);
|
|
1435
|
+
this.emitState(agent);
|
|
1436
|
+
}
|
|
1437
|
+
async clearAgentAttention(agentId) {
|
|
1438
|
+
const agent = this.requireAgent(agentId);
|
|
1439
|
+
if (agent.attention.requiresAttention) {
|
|
1440
|
+
agent.attention = { requiresAttention: false };
|
|
1441
|
+
await this.persistSnapshot(agent);
|
|
1442
|
+
this.emitState(agent, { persist: false });
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
async markAgentUnread(agentId) {
|
|
1446
|
+
const liveAgent = this.agents.get(agentId);
|
|
1447
|
+
if (liveAgent) {
|
|
1448
|
+
const isFinished = liveAgent.lifecycle === "idle";
|
|
1449
|
+
const hasPendingPermissions = liveAgent.pendingPermissions.size > 0;
|
|
1450
|
+
const canMarkUnread = isFinished && !liveAgent.attention.requiresAttention && !hasPendingPermissions;
|
|
1451
|
+
if (!canMarkUnread) {
|
|
1452
|
+
throw new Error(`Agent is no longer finished and read: ${agentId}`);
|
|
1453
|
+
}
|
|
1454
|
+
liveAgent.attention = {
|
|
1455
|
+
requiresAttention: true,
|
|
1456
|
+
attentionReason: "finished",
|
|
1457
|
+
attentionTimestamp: new Date(),
|
|
1458
|
+
};
|
|
1459
|
+
await this.persistSnapshot(liveAgent);
|
|
1460
|
+
this.emitState(liveAgent, { persist: false });
|
|
1461
|
+
return;
|
|
1462
|
+
}
|
|
1463
|
+
const registry = this.requireRegistry();
|
|
1464
|
+
const record = await registry.get(agentId);
|
|
1465
|
+
const hasFinishedStatus = record?.lastStatus === "idle" || record?.lastStatus === "closed";
|
|
1466
|
+
const canMarkUnread = record && !record.internal && !record.archivedAt && !record.requiresAttention;
|
|
1467
|
+
if (!canMarkUnread || !hasFinishedStatus) {
|
|
1468
|
+
throw new Error(`Agent is no longer finished and read: ${agentId}`);
|
|
1469
|
+
}
|
|
1470
|
+
const updatedAt = this.nextStoredUpdatedAt(record);
|
|
1471
|
+
const nextRecord = {
|
|
1472
|
+
...record,
|
|
1473
|
+
updatedAt,
|
|
1474
|
+
requiresAttention: true,
|
|
1475
|
+
attentionReason: "finished",
|
|
1476
|
+
attentionTimestamp: updatedAt,
|
|
1477
|
+
};
|
|
1478
|
+
await registry.upsert(nextRecord);
|
|
1479
|
+
this.dispatchStoredAgentState(nextRecord);
|
|
1480
|
+
}
|
|
1481
|
+
async archiveSnapshot(agentId, archivedAt) {
|
|
1482
|
+
const registry = this.requireRegistry();
|
|
1483
|
+
const liveAgent = this.getAgent(agentId);
|
|
1484
|
+
if (liveAgent) {
|
|
1485
|
+
await this.persistSnapshot(liveAgent, {
|
|
1486
|
+
internal: liveAgent.internal,
|
|
1487
|
+
});
|
|
1488
|
+
}
|
|
1489
|
+
const record = await registry.get(agentId);
|
|
1490
|
+
if (!record) {
|
|
1491
|
+
throw new Error(`Agent not found: ${agentId}`);
|
|
1492
|
+
}
|
|
1493
|
+
const nextRecord = await this.persistArchivedRecord(record, { archivedAt });
|
|
1494
|
+
await this.syncNativeArchiveState(record.provider, record.persistence, "archive");
|
|
1495
|
+
if (this.agents.has(agentId)) {
|
|
1496
|
+
this.notifyAgentState(agentId);
|
|
1497
|
+
}
|
|
1498
|
+
else {
|
|
1499
|
+
this.discardRetainedAgentState(agentId);
|
|
1500
|
+
if (!nextRecord.internal) {
|
|
1501
|
+
this.dispatchStoredAgentState(nextRecord);
|
|
1502
|
+
}
|
|
1503
|
+
}
|
|
1504
|
+
await this.fireAgentArchived(agentId);
|
|
1505
|
+
await this.cascadeArchiveChildren(agentId);
|
|
1506
|
+
return nextRecord;
|
|
1507
|
+
}
|
|
1508
|
+
async unarchiveSnapshot(agentId, updates) {
|
|
1509
|
+
const registry = this.requireRegistry();
|
|
1510
|
+
const record = await registry.get(agentId);
|
|
1511
|
+
if (!record || !record.archivedAt) {
|
|
1512
|
+
return false;
|
|
1513
|
+
}
|
|
1514
|
+
// Archived history may have loaded a runtime that still owns the native writer.
|
|
1515
|
+
await this.closeAgent(agentId);
|
|
1516
|
+
await this.syncNativeArchiveState(record.provider, record.persistence, "restore");
|
|
1517
|
+
await registry.upsert({
|
|
1518
|
+
...record,
|
|
1519
|
+
...(updates?.workspaceId ? { workspaceId: updates.workspaceId } : {}),
|
|
1520
|
+
...(updates?.labels ? { labels: applyLabelPatch(record.labels, updates.labels) } : {}),
|
|
1521
|
+
archivedAt: null,
|
|
1522
|
+
updatedAt: new Date().toISOString(),
|
|
1523
|
+
});
|
|
1524
|
+
if (this.getAgent(agentId)) {
|
|
1525
|
+
this.notifyAgentState(agentId);
|
|
1526
|
+
}
|
|
1527
|
+
return true;
|
|
1528
|
+
}
|
|
1529
|
+
async unarchiveSnapshotByHandle(handle) {
|
|
1530
|
+
const registry = this.requireRegistry();
|
|
1531
|
+
const records = await registry.list();
|
|
1532
|
+
const matched = records.find((record) => record.persistence?.provider === handle.provider &&
|
|
1533
|
+
record.persistence?.sessionId === handle.sessionId);
|
|
1534
|
+
if (!matched) {
|
|
1535
|
+
return;
|
|
1536
|
+
}
|
|
1537
|
+
await this.unarchiveSnapshot(matched.id);
|
|
1538
|
+
}
|
|
1539
|
+
async updateAgentMetadata(agentId, updates) {
|
|
1540
|
+
await this.runLifecycleMutation(agentId, () => this.updateAgentMetadataUnlocked(agentId, updates));
|
|
1541
|
+
}
|
|
1542
|
+
async updateAgentMetadataUnlocked(agentId, updates) {
|
|
1543
|
+
const liveAgent = this.getAgent(agentId);
|
|
1544
|
+
if (liveAgent) {
|
|
1545
|
+
if (updates.title) {
|
|
1546
|
+
await this.setTitle(agentId, updates.title);
|
|
1547
|
+
}
|
|
1548
|
+
if (updates.labels) {
|
|
1549
|
+
await this.writeLabels(agentId, updates.labels);
|
|
1550
|
+
}
|
|
1551
|
+
return;
|
|
1552
|
+
}
|
|
1553
|
+
await this.writeStoredMetadata(agentId, updates);
|
|
1554
|
+
}
|
|
1555
|
+
async runLifecycleMutation(agentId, mutation) {
|
|
1556
|
+
// Parent cascade classifies a child inside the same lane used by open-tab
|
|
1557
|
+
// label writes, so a received ownership update cannot be overtaken.
|
|
1558
|
+
const previous = this.lifecycleMutationTails.get(agentId) ?? Promise.resolve();
|
|
1559
|
+
const result = previous.catch(() => undefined).then(mutation);
|
|
1560
|
+
const tail = result.then(() => undefined, () => undefined);
|
|
1561
|
+
this.lifecycleMutationTails.set(agentId, tail);
|
|
1562
|
+
void tail.finally(() => {
|
|
1563
|
+
if (this.lifecycleMutationTails.get(agentId) === tail) {
|
|
1564
|
+
this.lifecycleMutationTails.delete(agentId);
|
|
1565
|
+
}
|
|
1566
|
+
});
|
|
1567
|
+
return result;
|
|
1568
|
+
}
|
|
1569
|
+
async runAgent(agentId, prompt, options) {
|
|
1570
|
+
const events = this.streamAgent(agentId, prompt, options);
|
|
1571
|
+
const timeline = [];
|
|
1572
|
+
let finalText = "";
|
|
1573
|
+
let usage;
|
|
1574
|
+
let canceled = false;
|
|
1575
|
+
for await (const event of events) {
|
|
1576
|
+
if (event.type === "timeline") {
|
|
1577
|
+
timeline.push(event.item);
|
|
1578
|
+
}
|
|
1579
|
+
else if (event.type === "turn_completed") {
|
|
1580
|
+
usage = event.usage;
|
|
1581
|
+
}
|
|
1582
|
+
else if (event.type === "turn_failed") {
|
|
1583
|
+
throw new Error(this.formatTurnFailedMessage(event));
|
|
1584
|
+
}
|
|
1585
|
+
else if (event.type === "turn_canceled") {
|
|
1586
|
+
canceled = true;
|
|
1587
|
+
}
|
|
1588
|
+
}
|
|
1589
|
+
finalText = this.getLastAssistantMessageFromTimeline(timeline) ?? "";
|
|
1590
|
+
const agent = this.requireAgent(agentId);
|
|
1591
|
+
const sessionId = agent.persistence?.sessionId;
|
|
1592
|
+
if (!sessionId) {
|
|
1593
|
+
throw new Error(`Agent ${agentId} has no persistence.sessionId after run completed`);
|
|
1594
|
+
}
|
|
1595
|
+
return {
|
|
1596
|
+
sessionId,
|
|
1597
|
+
finalText,
|
|
1598
|
+
usage,
|
|
1599
|
+
timeline,
|
|
1600
|
+
canceled,
|
|
1601
|
+
};
|
|
1602
|
+
}
|
|
1603
|
+
/**
|
|
1604
|
+
* Try to run a prompt out-of-band — i.e. without allocating a foreground turn
|
|
1605
|
+
* and without canceling any active turn. Returns true when the session
|
|
1606
|
+
* accepted the prompt as a side-effect command (e.g. /goal pause). Events
|
|
1607
|
+
* emitted by the handler flow through dispatchStream so they persist and
|
|
1608
|
+
* broadcast like normal timeline events.
|
|
1609
|
+
*/
|
|
1610
|
+
tryRunOutOfBand(agentId, prompt, options) {
|
|
1611
|
+
const agent = this.requireSessionAgent(agentId);
|
|
1612
|
+
const handler = agent.session.tryHandleOutOfBand?.(prompt);
|
|
1613
|
+
if (!handler) {
|
|
1614
|
+
return false;
|
|
1615
|
+
}
|
|
1616
|
+
if (options?.clientMessageId) {
|
|
1617
|
+
this.recordSubmittedPrompt(agent, prompt, options.clientMessageId);
|
|
1618
|
+
this.emitState(agent);
|
|
1619
|
+
}
|
|
1620
|
+
const dispatch = (event) => {
|
|
1621
|
+
// Persist timeline items so they show up in fetchAgentTimeline; broadcast
|
|
1622
|
+
// for live subscribers. Other event types are broadcast only.
|
|
1623
|
+
if (event.type === "timeline") {
|
|
1624
|
+
this.touchUpdatedAt(agent);
|
|
1625
|
+
const row = this.recordTimeline(agent.id, event.item);
|
|
1626
|
+
this.dispatchStream(agent.id, event, {
|
|
1627
|
+
seq: row.seq,
|
|
1628
|
+
epoch: this.timelineStore.getEpoch(agent.id),
|
|
1629
|
+
timestamp: row.timestamp,
|
|
1630
|
+
});
|
|
1631
|
+
return;
|
|
1632
|
+
}
|
|
1633
|
+
this.dispatchStream(agent.id, event, { timestamp: new Date().toISOString() });
|
|
1634
|
+
};
|
|
1635
|
+
void (async () => {
|
|
1636
|
+
try {
|
|
1637
|
+
await handler.run({ emit: dispatch });
|
|
1638
|
+
}
|
|
1639
|
+
catch (error) {
|
|
1640
|
+
const text = error instanceof Error ? error.message : "Out-of-band command failed";
|
|
1641
|
+
dispatch({
|
|
1642
|
+
type: "timeline",
|
|
1643
|
+
provider: agent.provider,
|
|
1644
|
+
item: { type: "assistant_message", text: `[Error] ${text}` },
|
|
1645
|
+
});
|
|
1646
|
+
}
|
|
1647
|
+
})();
|
|
1648
|
+
return true;
|
|
1649
|
+
}
|
|
1650
|
+
async appendTimelineItem(agentId, item) {
|
|
1651
|
+
const agent = this.requireAgent(agentId);
|
|
1652
|
+
item = limitAgentTimelineItemContent(item);
|
|
1653
|
+
this.touchUpdatedAt(agent);
|
|
1654
|
+
const row = this.recordTimeline(agentId, item);
|
|
1655
|
+
this.dispatchStream(agentId, {
|
|
1656
|
+
type: "timeline",
|
|
1657
|
+
item,
|
|
1658
|
+
provider: agent.provider,
|
|
1659
|
+
}, {
|
|
1660
|
+
seq: row.seq,
|
|
1661
|
+
epoch: this.timelineStore.getEpoch(agentId),
|
|
1662
|
+
timestamp: row.timestamp,
|
|
1663
|
+
});
|
|
1664
|
+
await this.persistSnapshot(agent);
|
|
1665
|
+
return { seq: row.seq, epoch: this.timelineStore.getEpoch(agentId) };
|
|
1666
|
+
}
|
|
1667
|
+
async emitLiveTimelineItem(agentId, item) {
|
|
1668
|
+
const agent = this.requireAgent(agentId);
|
|
1669
|
+
this.touchUpdatedAt(agent);
|
|
1670
|
+
this.dispatchStream(agentId, {
|
|
1671
|
+
type: "timeline",
|
|
1672
|
+
item,
|
|
1673
|
+
provider: agent.provider,
|
|
1674
|
+
});
|
|
1675
|
+
}
|
|
1676
|
+
async startPendingForegroundTurn(params) {
|
|
1677
|
+
const { agent, agentId, pendingRun, prompt, options } = params;
|
|
1678
|
+
// Correlation for the replace race: these are the manager's own facts about
|
|
1679
|
+
// the call it made. Whether the provider admitted or staged the turn is the
|
|
1680
|
+
// adapter's to log (provider.acp.successor_staged / admission_released).
|
|
1681
|
+
const replacement = agent.pendingReplacement;
|
|
1682
|
+
const sessionId = agent.persistence?.sessionId ?? undefined;
|
|
1683
|
+
if (replacement) {
|
|
1684
|
+
this.logger.info({ agentId, provider: agent.provider, sessionId }, "agent.replace.start_turn_invoked");
|
|
1685
|
+
}
|
|
1686
|
+
try {
|
|
1687
|
+
const result = await agent.session.startTurn(prompt, options);
|
|
1688
|
+
if (pendingRun.settled) {
|
|
1689
|
+
throw new Error(`Agent ${agentId} run was canceled before its turn started`);
|
|
1690
|
+
}
|
|
1691
|
+
if (replacement) {
|
|
1692
|
+
this.logger.info({ agentId, provider: agent.provider, sessionId, turnId: result.turnId }, "agent.replace.start_turn_resolved");
|
|
1693
|
+
}
|
|
1694
|
+
return result.turnId;
|
|
1695
|
+
}
|
|
1696
|
+
catch (error) {
|
|
1697
|
+
if (replacement) {
|
|
1698
|
+
this.logger.warn({ agentId, provider: agent.provider, sessionId, err: error }, "agent.replace.start_turn_rejected");
|
|
1699
|
+
}
|
|
1700
|
+
if (pendingRun.settled) {
|
|
1701
|
+
throw error;
|
|
1702
|
+
}
|
|
1703
|
+
if (isStaleProviderSessionError(error)) {
|
|
1704
|
+
pendingRun.start = { status: "failed", error: error.message };
|
|
1705
|
+
agent.pendingReplacement = false;
|
|
1706
|
+
if (!agent.activeForegroundTurnId)
|
|
1707
|
+
agent.lifecycle = "idle";
|
|
1708
|
+
this.runs.settleForegroundRun(agentId, pendingRun.token);
|
|
1709
|
+
throw error;
|
|
1710
|
+
}
|
|
1711
|
+
agent.pendingReplacement = false;
|
|
1712
|
+
const errorMsg = error instanceof Error ? error.message : "Failed to start turn";
|
|
1713
|
+
pendingRun.start = { status: "failed", error: errorMsg };
|
|
1714
|
+
await this.handleStreamEvent(agent, {
|
|
1715
|
+
type: "turn_failed",
|
|
1716
|
+
provider: agent.provider,
|
|
1717
|
+
error: errorMsg,
|
|
1718
|
+
});
|
|
1719
|
+
this.finalizeForegroundTurn(agent);
|
|
1720
|
+
this.runs.settleForegroundRun(agentId, pendingRun.token);
|
|
1721
|
+
throw error;
|
|
1722
|
+
}
|
|
1723
|
+
}
|
|
1724
|
+
streamAgent(agentId, prompt, options) {
|
|
1725
|
+
const existingAgent = this.requireSessionAgent(agentId);
|
|
1726
|
+
this.logger.trace({
|
|
1727
|
+
agentId,
|
|
1728
|
+
provider: existingAgent.provider,
|
|
1729
|
+
sessionId: existingAgent.persistence?.sessionId ?? undefined,
|
|
1730
|
+
turnId: existingAgent.activeForegroundTurnId ?? undefined,
|
|
1731
|
+
lifecycle: existingAgent.lifecycle,
|
|
1732
|
+
activeForegroundTurnId: existingAgent.activeForegroundTurnId,
|
|
1733
|
+
hasTrackedRun: this.runs.hasRun(agentId),
|
|
1734
|
+
promptType: typeof prompt === "string" ? "string" : "structured",
|
|
1735
|
+
hasRunOptions: Boolean(options),
|
|
1736
|
+
}, "agent.manager.stream.request");
|
|
1737
|
+
if (existingAgent.activeForegroundTurnId || this.runs.hasRun(agentId)) {
|
|
1738
|
+
this.logger.trace({
|
|
1739
|
+
agentId,
|
|
1740
|
+
provider: existingAgent.provider,
|
|
1741
|
+
sessionId: existingAgent.persistence?.sessionId ?? undefined,
|
|
1742
|
+
turnId: existingAgent.activeForegroundTurnId ?? undefined,
|
|
1743
|
+
lifecycle: existingAgent.lifecycle,
|
|
1744
|
+
hasTrackedRun: this.runs.hasRun(agentId),
|
|
1745
|
+
}, "agent.manager.stream.reject");
|
|
1746
|
+
throw new Error(`Agent ${agentId} already has an active run`);
|
|
1747
|
+
}
|
|
1748
|
+
const agent = existingAgent;
|
|
1749
|
+
const isReplacement = agent.pendingReplacement;
|
|
1750
|
+
agent.lastError = undefined;
|
|
1751
|
+
const pendingRun = this.runs.createPendingRun(agentId);
|
|
1752
|
+
const streamForwarder = async function* streamForwarder() {
|
|
1753
|
+
let turnId;
|
|
1754
|
+
let turnStream = null;
|
|
1755
|
+
turnId = await this.startPendingForegroundTurn({
|
|
1756
|
+
agent,
|
|
1757
|
+
agentId,
|
|
1758
|
+
pendingRun,
|
|
1759
|
+
prompt,
|
|
1760
|
+
options,
|
|
1761
|
+
});
|
|
1762
|
+
if (isReplacement) {
|
|
1763
|
+
agent.pendingReplacement = false;
|
|
1764
|
+
}
|
|
1765
|
+
const turnStartedAt = new Date();
|
|
1766
|
+
pendingRun.start = { status: "started", turnId };
|
|
1767
|
+
agent.activeForegroundTurnId = turnId;
|
|
1768
|
+
this.openActiveTurn(agent, turnId, turnStartedAt);
|
|
1769
|
+
agent.lifecycle = "running";
|
|
1770
|
+
this.touchUpdatedAt(agent);
|
|
1771
|
+
// AgentManager owns the accepted-turn boundary. Publish liveness before the canonical
|
|
1772
|
+
// prompt so clients can retire optimistic activity without painting an idle frame.
|
|
1773
|
+
// The provider's duplicate start for this turn is suppressed at the ingestion boundary.
|
|
1774
|
+
this.dispatchStream(agent.id, { type: "turn_started", provider: agent.provider, turnId }, { timestamp: turnStartedAt.toISOString() });
|
|
1775
|
+
const stagedSubmittedPromptEcho = options?.clientMessageId
|
|
1776
|
+
? pendingRun.stagedEvents.find((event) => event.type === "timeline" &&
|
|
1777
|
+
event.item.type === "user_message" &&
|
|
1778
|
+
event.item.clientMessageId === options.clientMessageId)
|
|
1779
|
+
: undefined;
|
|
1780
|
+
if (options?.clientMessageId) {
|
|
1781
|
+
this.recordSubmittedPrompt(agent, prompt, options.clientMessageId, {
|
|
1782
|
+
messageId: options.clientMessageId,
|
|
1783
|
+
turnId,
|
|
1784
|
+
providerMessageId: stagedSubmittedPromptEcho?.item.type === "user_message"
|
|
1785
|
+
? stagedSubmittedPromptEcho.item.messageId
|
|
1786
|
+
: undefined,
|
|
1787
|
+
});
|
|
1788
|
+
}
|
|
1789
|
+
for (const stagedEvent of pendingRun.stagedEvents.splice(0)) {
|
|
1790
|
+
const isAcceptedTurnStart = stagedEvent.type === "turn_started" && getAgentStreamEventTurnId(stagedEvent) === turnId;
|
|
1791
|
+
if (isAcceptedTurnStart || stagedEvent === stagedSubmittedPromptEcho) {
|
|
1792
|
+
continue;
|
|
1793
|
+
}
|
|
1794
|
+
this.enqueueSessionEvent(agent.id, stagedEvent);
|
|
1795
|
+
}
|
|
1796
|
+
this.emitState(agent);
|
|
1797
|
+
this.logger.trace({
|
|
1798
|
+
agentId,
|
|
1799
|
+
provider: agent.provider,
|
|
1800
|
+
sessionId: agent.persistence?.sessionId ?? undefined,
|
|
1801
|
+
turnId,
|
|
1802
|
+
lifecycle: agent.lifecycle,
|
|
1803
|
+
activeForegroundTurnId: agent.activeForegroundTurnId,
|
|
1804
|
+
}, "agent.manager.stream.start");
|
|
1805
|
+
turnStream = this.runs.createTurnStream(turnId);
|
|
1806
|
+
this.runs.addWaiter(agent, turnStream.waiter);
|
|
1807
|
+
try {
|
|
1808
|
+
const acceptedTurnStartedEvent = {
|
|
1809
|
+
type: "turn_started",
|
|
1810
|
+
provider: agent.provider,
|
|
1811
|
+
turnId,
|
|
1812
|
+
};
|
|
1813
|
+
yield acceptedTurnStartedEvent;
|
|
1814
|
+
for await (const event of turnStream.events(isTurnTerminalEvent)) {
|
|
1815
|
+
yield event;
|
|
1816
|
+
}
|
|
1817
|
+
}
|
|
1818
|
+
finally {
|
|
1819
|
+
if (turnStream) {
|
|
1820
|
+
this.runs.deleteWaiter(agent, turnStream.waiter);
|
|
1821
|
+
}
|
|
1822
|
+
this.runs.settleForegroundRun(agentId, pendingRun.token);
|
|
1823
|
+
if (!agent.activeForegroundTurnId) {
|
|
1824
|
+
await this.refreshRuntimeInfo(agent);
|
|
1825
|
+
}
|
|
1826
|
+
}
|
|
1827
|
+
}.call(this);
|
|
1828
|
+
return streamForwarder;
|
|
1829
|
+
}
|
|
1830
|
+
finalizeForegroundTurn(agent, turnId) {
|
|
1831
|
+
const mutableAgent = agent;
|
|
1832
|
+
if (turnId) {
|
|
1833
|
+
this.runs.rememberFinalizedTurn(mutableAgent, turnId);
|
|
1834
|
+
}
|
|
1835
|
+
mutableAgent.activeForegroundTurnId = null;
|
|
1836
|
+
this.applyActiveTurnTerminal(mutableAgent, turnId);
|
|
1837
|
+
const terminalError = mutableAgent.lastError;
|
|
1838
|
+
const shouldHoldBusyForReplacement = mutableAgent.pendingReplacement && !terminalError;
|
|
1839
|
+
let nextLifecycle;
|
|
1840
|
+
if (shouldHoldBusyForReplacement) {
|
|
1841
|
+
nextLifecycle = "running";
|
|
1842
|
+
}
|
|
1843
|
+
else if (terminalError) {
|
|
1844
|
+
nextLifecycle = "error";
|
|
1845
|
+
}
|
|
1846
|
+
else {
|
|
1847
|
+
nextLifecycle = "idle";
|
|
1848
|
+
}
|
|
1849
|
+
mutableAgent.lifecycle = nextLifecycle;
|
|
1850
|
+
const persistenceHandle = mutableAgent.session.describePersistence() ??
|
|
1851
|
+
(mutableAgent.runtimeInfo?.sessionId
|
|
1852
|
+
? { provider: mutableAgent.provider, sessionId: mutableAgent.runtimeInfo.sessionId }
|
|
1853
|
+
: null);
|
|
1854
|
+
if (persistenceHandle) {
|
|
1855
|
+
mutableAgent.persistence = attachPersistenceCwd(persistenceHandle, mutableAgent.cwd);
|
|
1856
|
+
}
|
|
1857
|
+
this.logger.trace({
|
|
1858
|
+
agentId: agent.id,
|
|
1859
|
+
provider: agent.provider,
|
|
1860
|
+
sessionId: mutableAgent.persistence?.sessionId ?? undefined,
|
|
1861
|
+
turnId,
|
|
1862
|
+
lifecycle: mutableAgent.lifecycle,
|
|
1863
|
+
terminalError,
|
|
1864
|
+
pendingReplacement: mutableAgent.pendingReplacement,
|
|
1865
|
+
}, "agent.manager.finalize");
|
|
1866
|
+
if (!shouldHoldBusyForReplacement) {
|
|
1867
|
+
this.touchUpdatedAt(mutableAgent);
|
|
1868
|
+
this.emitState(mutableAgent);
|
|
1869
|
+
}
|
|
1870
|
+
}
|
|
1871
|
+
openActiveTurn(agent, turnId, startedAt) {
|
|
1872
|
+
agent.activeTurnId = turnId;
|
|
1873
|
+
agent.activeTurnStartedAt = startedAt;
|
|
1874
|
+
}
|
|
1875
|
+
applyActiveTurnTerminal(agent, turnId, fromHistory = false) {
|
|
1876
|
+
if (fromHistory)
|
|
1877
|
+
return "stale";
|
|
1878
|
+
if (!agent.activeTurnId)
|
|
1879
|
+
return "untracked";
|
|
1880
|
+
if (turnId && agent.activeTurnId !== turnId)
|
|
1881
|
+
return "stale";
|
|
1882
|
+
agent.activeTurnId = null;
|
|
1883
|
+
agent.activeTurnStartedAt = null;
|
|
1884
|
+
return "closed_current";
|
|
1885
|
+
}
|
|
1886
|
+
async replaceAgentRun(agentId, prompt, options) {
|
|
1887
|
+
const snapshot = this.requireAgent(agentId);
|
|
1888
|
+
if (snapshot.lifecycle !== "running" &&
|
|
1889
|
+
!snapshot.activeForegroundTurnId &&
|
|
1890
|
+
!this.runs.hasRun(agentId)) {
|
|
1891
|
+
return this.streamAgent(agentId, prompt, options);
|
|
1892
|
+
}
|
|
1893
|
+
const agent = this.requireSessionAgent(agentId);
|
|
1894
|
+
agent.pendingReplacement = true;
|
|
1895
|
+
agent.lifecycle = "running";
|
|
1896
|
+
this.touchUpdatedAt(agent);
|
|
1897
|
+
this.emitState(agent);
|
|
1898
|
+
try {
|
|
1899
|
+
await this.cancelAgentRunBefore(agentId, "replace");
|
|
1900
|
+
return this.streamAgent(agentId, prompt, options);
|
|
1901
|
+
}
|
|
1902
|
+
catch (error) {
|
|
1903
|
+
const latest = this.agents.get(agentId);
|
|
1904
|
+
if (latest) {
|
|
1905
|
+
latest.pendingReplacement = false;
|
|
1906
|
+
}
|
|
1907
|
+
throw error;
|
|
1908
|
+
}
|
|
1909
|
+
}
|
|
1910
|
+
async steerAgentRun(agentId, prompt, options) {
|
|
1911
|
+
const agent = this.requireSessionAgent(agentId);
|
|
1912
|
+
const expectedTurnId = agent.activeForegroundTurnId ?? agent.activeTurnId;
|
|
1913
|
+
if (!expectedTurnId || !agent.session.steerActiveTurn) {
|
|
1914
|
+
return { status: "unavailable" };
|
|
1915
|
+
}
|
|
1916
|
+
const result = await this.runSteerAdmission(agent, expectedTurnId, async () => {
|
|
1917
|
+
const admission = await agent.session.steerActiveTurn(prompt, {
|
|
1918
|
+
...options,
|
|
1919
|
+
expectedTurnId,
|
|
1920
|
+
});
|
|
1921
|
+
if (admission.status === "accepted") {
|
|
1922
|
+
await this.recordAcceptedSteer(agent, prompt, options?.clientMessageId, expectedTurnId);
|
|
1923
|
+
}
|
|
1924
|
+
return admission;
|
|
1925
|
+
});
|
|
1926
|
+
// An unavailable answer is only safe to fall back from while this admission
|
|
1927
|
+
// still owns the active turn. Never let an A admission replace a later B.
|
|
1928
|
+
if (result.status === "unavailable" && agent.activeTurnId !== expectedTurnId) {
|
|
1929
|
+
throw new Error("Active turn changed before steering could be delivered");
|
|
1930
|
+
}
|
|
1931
|
+
return result;
|
|
1932
|
+
}
|
|
1933
|
+
async steerOrReplaceActiveTurn(agentId, prompt, options) {
|
|
1934
|
+
const agent = this.requireSessionAgent(agentId);
|
|
1935
|
+
const expectedTurnId = agent.activeForegroundTurnId ?? agent.activeTurnId;
|
|
1936
|
+
if (!expectedTurnId) {
|
|
1937
|
+
return { status: "inactive" };
|
|
1938
|
+
}
|
|
1939
|
+
const result = agent.session.steerActiveTurn
|
|
1940
|
+
? await this.runSteerAdmission(agent, expectedTurnId, async () => {
|
|
1941
|
+
const admission = await agent.session.steerActiveTurn(prompt, {
|
|
1942
|
+
...options,
|
|
1943
|
+
expectedTurnId,
|
|
1944
|
+
});
|
|
1945
|
+
if (admission.status === "accepted") {
|
|
1946
|
+
await this.recordAcceptedSteer(agent, prompt, options?.clientMessageId, expectedTurnId);
|
|
1947
|
+
}
|
|
1948
|
+
return admission;
|
|
1949
|
+
})
|
|
1950
|
+
: { status: "unavailable" };
|
|
1951
|
+
if (result.status === "accepted") {
|
|
1952
|
+
return { status: "steered" };
|
|
1953
|
+
}
|
|
1954
|
+
// Providers without autonomous steering keep their existing dispatch behavior. The shared
|
|
1955
|
+
// admission may recognize the turn, but only an accepted steer can own it without replacement.
|
|
1956
|
+
if (agent.activeForegroundTurnId === null && agent.activeTurnId === expectedTurnId) {
|
|
1957
|
+
return { status: "inactive" };
|
|
1958
|
+
}
|
|
1959
|
+
await this.beforeSteerUnavailableFallback?.({ agentId, expectedTurnId });
|
|
1960
|
+
this.assertSteerAdmissionOwnsTurn(agent, expectedTurnId);
|
|
1961
|
+
return {
|
|
1962
|
+
status: "replaced",
|
|
1963
|
+
iterator: await this.replaceAdmittedForegroundTurn(agent, expectedTurnId, prompt, stripSteerOptions(options)),
|
|
1964
|
+
};
|
|
1965
|
+
}
|
|
1966
|
+
assertSteerAdmissionOwnsTurn(agent, expectedTurnId) {
|
|
1967
|
+
if (agent.activeTurnId !== expectedTurnId) {
|
|
1968
|
+
throw new Error("Active turn changed before steering could be delivered");
|
|
1969
|
+
}
|
|
1970
|
+
}
|
|
1971
|
+
async runSteerAdmission(agent, expectedTurnId, operation) {
|
|
1972
|
+
return this.runForegroundMutation(agent.id, async () => {
|
|
1973
|
+
await this.drainSessionEvents(agent.id);
|
|
1974
|
+
this.agentStreamCoalescer.flushFor(agent.id);
|
|
1975
|
+
this.assertSteerAdmissionOwnsTurn(agent, expectedTurnId);
|
|
1976
|
+
const barrier = { events: [] };
|
|
1977
|
+
this.steerEventBarriers.set(agent.id, barrier);
|
|
1978
|
+
try {
|
|
1979
|
+
return await operation();
|
|
1980
|
+
}
|
|
1981
|
+
finally {
|
|
1982
|
+
if (this.steerEventBarriers.get(agent.id) === barrier) {
|
|
1983
|
+
this.steerEventBarriers.delete(agent.id);
|
|
1984
|
+
}
|
|
1985
|
+
for (const event of barrier.events) {
|
|
1986
|
+
this.enqueueSessionEvent(agent.id, event);
|
|
1987
|
+
}
|
|
1988
|
+
await this.drainSessionEvents(agent.id);
|
|
1989
|
+
}
|
|
1990
|
+
});
|
|
1991
|
+
}
|
|
1992
|
+
async runForegroundMutation(agentId, operation) {
|
|
1993
|
+
const previous = this.foregroundMutationTails.get(agentId) ?? Promise.resolve();
|
|
1994
|
+
const run = previous.catch(() => undefined).then(operation);
|
|
1995
|
+
const tail = run.then(() => undefined, () => undefined);
|
|
1996
|
+
this.foregroundMutationTails.set(agentId, tail);
|
|
1997
|
+
try {
|
|
1998
|
+
return await run;
|
|
1999
|
+
}
|
|
2000
|
+
finally {
|
|
2001
|
+
if (this.foregroundMutationTails.get(agentId) === tail) {
|
|
2002
|
+
this.foregroundMutationTails.delete(agentId);
|
|
2003
|
+
}
|
|
2004
|
+
}
|
|
2005
|
+
}
|
|
2006
|
+
async replaceAdmittedForegroundTurn(agent, expectedTurnId, prompt, options) {
|
|
2007
|
+
this.assertSteerAdmissionOwnsTurn(agent, expectedTurnId);
|
|
2008
|
+
agent.pendingReplacement = true;
|
|
2009
|
+
agent.lifecycle = "running";
|
|
2010
|
+
this.touchUpdatedAt(agent);
|
|
2011
|
+
this.emitState(agent);
|
|
2012
|
+
try {
|
|
2013
|
+
await this.cancelAgentRunBefore(agent.id, "replace");
|
|
2014
|
+
return this.streamAgent(agent.id, prompt, options);
|
|
2015
|
+
}
|
|
2016
|
+
catch (error) {
|
|
2017
|
+
const latest = this.agents.get(agent.id);
|
|
2018
|
+
if (latest) {
|
|
2019
|
+
latest.pendingReplacement = false;
|
|
2020
|
+
}
|
|
2021
|
+
throw error;
|
|
2022
|
+
}
|
|
2023
|
+
}
|
|
2024
|
+
async recordAcceptedSteer(agent, prompt, clientMessageId, expectedTurnId) {
|
|
2025
|
+
if (!clientMessageId) {
|
|
2026
|
+
return;
|
|
2027
|
+
}
|
|
2028
|
+
this.recordSubmittedPrompt(agent, prompt, clientMessageId, {
|
|
2029
|
+
messageId: clientMessageId,
|
|
2030
|
+
turnId: expectedTurnId,
|
|
2031
|
+
});
|
|
2032
|
+
this.emitState(agent);
|
|
2033
|
+
}
|
|
2034
|
+
async waitForAgentRunStart(agentId, options) {
|
|
2035
|
+
const snapshot = this.getAgent(agentId);
|
|
2036
|
+
if (!snapshot) {
|
|
2037
|
+
throw new Error(`Agent ${agentId} not found`);
|
|
2038
|
+
}
|
|
2039
|
+
const pendingRun = this.runs.getPendingRun(agentId);
|
|
2040
|
+
if ((snapshot.lifecycle === "running" || pendingRun?.start.status === "started") &&
|
|
2041
|
+
!snapshot.pendingReplacement) {
|
|
2042
|
+
return;
|
|
2043
|
+
}
|
|
2044
|
+
if (!snapshot.activeForegroundTurnId && !pendingRun && !snapshot.pendingReplacement) {
|
|
2045
|
+
throw new Error(`Agent ${agentId} has no pending run`);
|
|
2046
|
+
}
|
|
2047
|
+
if (options?.signal?.aborted) {
|
|
2048
|
+
throw createAbortError(options.signal, "wait_for_agent_start aborted");
|
|
2049
|
+
}
|
|
2050
|
+
await new Promise((resolvePromise, reject) => {
|
|
2051
|
+
if (options?.signal?.aborted) {
|
|
2052
|
+
reject(createAbortError(options.signal, "wait_for_agent_start aborted"));
|
|
2053
|
+
return;
|
|
2054
|
+
}
|
|
2055
|
+
let unsubscribe = null;
|
|
2056
|
+
let abortHandler = null;
|
|
2057
|
+
const cleanup = () => {
|
|
2058
|
+
if (unsubscribe) {
|
|
2059
|
+
try {
|
|
2060
|
+
unsubscribe();
|
|
2061
|
+
}
|
|
2062
|
+
catch {
|
|
2063
|
+
// ignore cleanup errors
|
|
2064
|
+
}
|
|
2065
|
+
unsubscribe = null;
|
|
2066
|
+
}
|
|
2067
|
+
if (abortHandler && options?.signal) {
|
|
2068
|
+
try {
|
|
2069
|
+
options.signal.removeEventListener("abort", abortHandler);
|
|
2070
|
+
}
|
|
2071
|
+
catch {
|
|
2072
|
+
// ignore cleanup errors
|
|
2073
|
+
}
|
|
2074
|
+
abortHandler = null;
|
|
2075
|
+
}
|
|
2076
|
+
};
|
|
2077
|
+
const finishOk = () => {
|
|
2078
|
+
cleanup();
|
|
2079
|
+
resolvePromise();
|
|
2080
|
+
};
|
|
2081
|
+
const finishErr = (error) => {
|
|
2082
|
+
cleanup();
|
|
2083
|
+
reject(error);
|
|
2084
|
+
};
|
|
2085
|
+
if (options?.signal) {
|
|
2086
|
+
abortHandler = () => finishErr(createAbortError(options.signal, "wait_for_agent_start aborted"));
|
|
2087
|
+
options.signal.addEventListener("abort", abortHandler, { once: true });
|
|
2088
|
+
}
|
|
2089
|
+
const checkCurrentState = () => {
|
|
2090
|
+
const current = this.getAgent(agentId);
|
|
2091
|
+
if (!current) {
|
|
2092
|
+
finishErr(new Error(`Agent ${agentId} not found`));
|
|
2093
|
+
return true;
|
|
2094
|
+
}
|
|
2095
|
+
const currentPendingRun = this.runs.getPendingRun(agentId);
|
|
2096
|
+
if ((current.lifecycle === "running" || currentPendingRun?.start.status === "started") &&
|
|
2097
|
+
!current.pendingReplacement) {
|
|
2098
|
+
finishOk();
|
|
2099
|
+
return true;
|
|
2100
|
+
}
|
|
2101
|
+
if (currentPendingRun?.start.status === "failed") {
|
|
2102
|
+
finishErr(new Error(currentPendingRun.start.error));
|
|
2103
|
+
return true;
|
|
2104
|
+
}
|
|
2105
|
+
if (current.lifecycle === "error" && !currentPendingRun) {
|
|
2106
|
+
finishErr(new Error(current.lastError ?? `Agent ${agentId} failed to start`));
|
|
2107
|
+
return true;
|
|
2108
|
+
}
|
|
2109
|
+
if (!currentPendingRun && !current.activeForegroundTurnId && !current.pendingReplacement) {
|
|
2110
|
+
finishErr(new Error(`Agent ${agentId} run finished before starting`));
|
|
2111
|
+
return true;
|
|
2112
|
+
}
|
|
2113
|
+
return false;
|
|
2114
|
+
};
|
|
2115
|
+
unsubscribe = this.subscribe((event) => {
|
|
2116
|
+
if (event.type !== "agent_state" || event.agent.id !== agentId) {
|
|
2117
|
+
return;
|
|
2118
|
+
}
|
|
2119
|
+
checkCurrentState();
|
|
2120
|
+
}, { agentId, replayState: false });
|
|
2121
|
+
checkCurrentState();
|
|
2122
|
+
});
|
|
2123
|
+
}
|
|
2124
|
+
async respondToPermission(agentId, requestId, response) {
|
|
2125
|
+
const agent = this.requireAgent(agentId);
|
|
2126
|
+
if (agent.inFlightPermissionResponses.has(requestId)) {
|
|
2127
|
+
throw new Error("A response to this permission request is already being submitted");
|
|
2128
|
+
}
|
|
2129
|
+
agent.inFlightPermissionResponses.add(requestId);
|
|
2130
|
+
try {
|
|
2131
|
+
const result = await agent.session.respondToPermission(requestId, response);
|
|
2132
|
+
agent.pendingPermissions.delete(requestId);
|
|
2133
|
+
try {
|
|
2134
|
+
await this.refreshSessionState(agent);
|
|
2135
|
+
}
|
|
2136
|
+
catch {
|
|
2137
|
+
// Ignore refresh errors - state sync after permission approval is best effort.
|
|
2138
|
+
}
|
|
2139
|
+
this.touchUpdatedAt(agent);
|
|
2140
|
+
await this.persistSnapshot(agent);
|
|
2141
|
+
this.emitState(agent);
|
|
2142
|
+
const bufferedResolution = agent.bufferedPermissionResolutions.get(requestId);
|
|
2143
|
+
if (bufferedResolution) {
|
|
2144
|
+
agent.bufferedPermissionResolutions.delete(requestId);
|
|
2145
|
+
this.dispatchStream(agent.id, bufferedResolution, { timestamp: new Date().toISOString() });
|
|
2146
|
+
}
|
|
2147
|
+
return result;
|
|
2148
|
+
}
|
|
2149
|
+
finally {
|
|
2150
|
+
agent.inFlightPermissionResponses.delete(requestId);
|
|
2151
|
+
agent.bufferedPermissionResolutions.delete(requestId);
|
|
2152
|
+
}
|
|
2153
|
+
}
|
|
2154
|
+
async cancelAgentRun(agentId) {
|
|
2155
|
+
return this.runForegroundMutation(agentId, () => this.cancelAgentRunNow(agentId));
|
|
2156
|
+
}
|
|
2157
|
+
async cancelAgentRunNow(agentId) {
|
|
2158
|
+
const agent = this.requireSessionAgent(agentId);
|
|
2159
|
+
const run = this.runs.getRun(agentId) ??
|
|
2160
|
+
(agent.lifecycle === "running" ? this.runs.trackAutonomousRun(agentId, null) : null);
|
|
2161
|
+
if (!run) {
|
|
2162
|
+
return { status: "not_running" };
|
|
2163
|
+
}
|
|
2164
|
+
// Correlation ids the manager owns: the run it is settling and the ACP
|
|
2165
|
+
// session that run belongs to. Whether the provider is ready or still
|
|
2166
|
+
// gated is the adapter's to log, never the manager's.
|
|
2167
|
+
const sessionId = agent.persistence?.sessionId ?? undefined;
|
|
2168
|
+
const interruptReturned = await this.interruptSession(agent.session, agentId);
|
|
2169
|
+
const settlement = await this.waitWithTimeout({
|
|
2170
|
+
operation: run.settledPromise,
|
|
2171
|
+
timeoutMs: interruptReturned
|
|
2172
|
+
? INTERRUPT_SESSION_TIMEOUT_MS
|
|
2173
|
+
: this.rescueTimeouts.interruptSessionMs,
|
|
2174
|
+
});
|
|
2175
|
+
if (!interruptReturned) {
|
|
2176
|
+
return { status: settlement === "completed" ? "settled" : "refused" };
|
|
2177
|
+
}
|
|
2178
|
+
const runTurnId = this.runs.getTurnId(agentId);
|
|
2179
|
+
if (settlement === "timed_out" && runTurnId) {
|
|
2180
|
+
// Manager-owned facts only. The adapter may still hold its stop boundary,
|
|
2181
|
+
// so this line must never read as the provider being ready or idle.
|
|
2182
|
+
this.logger.warn({
|
|
2183
|
+
agentId,
|
|
2184
|
+
turnId: runTurnId,
|
|
2185
|
+
kind: run.kind,
|
|
2186
|
+
sessionId,
|
|
2187
|
+
}, "cancelAgentRun: manager settlement wait timed out; force-settling the manager-owned run");
|
|
2188
|
+
await this.dispatchSessionEvent(agent, {
|
|
2189
|
+
type: "turn_canceled",
|
|
2190
|
+
provider: agent.provider,
|
|
2191
|
+
reason: "interrupted",
|
|
2192
|
+
turnId: runTurnId,
|
|
2193
|
+
});
|
|
2194
|
+
await run.settledPromise;
|
|
2195
|
+
}
|
|
2196
|
+
else if (settlement === "timed_out" && run.kind === "foreground") {
|
|
2197
|
+
this.logger.warn({ agentId, kind: run.kind, sessionId }, "cancelAgentRun: manager settlement wait timed out; force-settling the manager-owned pending run");
|
|
2198
|
+
this.runs.settleForegroundRun(agentId, run.token);
|
|
2199
|
+
if (!agent.pendingReplacement) {
|
|
2200
|
+
agent.lifecycle = "idle";
|
|
2201
|
+
this.touchUpdatedAt(agent);
|
|
2202
|
+
this.emitState(agent);
|
|
2203
|
+
}
|
|
2204
|
+
}
|
|
2205
|
+
else if (settlement === "timed_out" && run.kind === "autonomous") {
|
|
2206
|
+
this.logger.warn({ agentId, kind: run.kind, sessionId }, "cancelAgentRun: manager settlement wait timed out; force-settling the manager-owned autonomous run");
|
|
2207
|
+
await this.dispatchSessionEvent(agent, {
|
|
2208
|
+
type: "turn_canceled",
|
|
2209
|
+
provider: agent.provider,
|
|
2210
|
+
reason: "interrupted",
|
|
2211
|
+
});
|
|
2212
|
+
}
|
|
2213
|
+
if (agent.pendingPermissions.size > 0) {
|
|
2214
|
+
this.resolvePendingPermissionsForAgent(agent, agent.provider, undefined, "Interrupted");
|
|
2215
|
+
this.touchUpdatedAt(agent);
|
|
2216
|
+
this.emitState(agent);
|
|
2217
|
+
}
|
|
2218
|
+
return { status: "settled" };
|
|
2219
|
+
}
|
|
2220
|
+
async cancelAgentRunBefore(agentId, action) {
|
|
2221
|
+
const result = await this.cancelAgentRun(agentId);
|
|
2222
|
+
if (result.status === "refused") {
|
|
2223
|
+
throw new AgentRunCancellationError(agentId, action);
|
|
2224
|
+
}
|
|
2225
|
+
}
|
|
2226
|
+
async interruptSession(session, agentId) {
|
|
2227
|
+
// The manager owns the interrupt call and the wait around it, and nothing
|
|
2228
|
+
// else. It cannot see a provider stop boundary or prompt state, so its logs
|
|
2229
|
+
// record that the call was issued, that it returned or rejected, and when
|
|
2230
|
+
// the manager's own wait ended — never that the provider settled or is idle.
|
|
2231
|
+
const sessionId = session.describePersistence()?.sessionId ?? undefined;
|
|
2232
|
+
try {
|
|
2233
|
+
const result = await this.waitWithTimeout({
|
|
2234
|
+
operation: session.interrupt(),
|
|
2235
|
+
timeoutMs: this.rescueTimeouts.interruptSessionMs,
|
|
2236
|
+
onLateError: (error) => {
|
|
2237
|
+
this.logger.warn({ err: error, agentId, sessionId }, "interruptSession: provider rejected the interrupt after the manager stopped waiting");
|
|
2238
|
+
},
|
|
2239
|
+
});
|
|
2240
|
+
if (result === "timed_out") {
|
|
2241
|
+
this.logger.warn({ agentId, sessionId, timeoutMs: this.rescueTimeouts.interruptSessionMs }, "interruptSession: manager interrupt wait timed out");
|
|
2242
|
+
return false;
|
|
2243
|
+
}
|
|
2244
|
+
this.logger.info({ agentId, sessionId }, "interruptSession: interrupt call returned");
|
|
2245
|
+
return true;
|
|
2246
|
+
}
|
|
2247
|
+
catch (error) {
|
|
2248
|
+
this.logger.error({ err: error, agentId, sessionId }, "interruptSession: provider rejected the interrupt");
|
|
2249
|
+
return false;
|
|
2250
|
+
}
|
|
2251
|
+
}
|
|
2252
|
+
getPendingPermissions(agentId) {
|
|
2253
|
+
const agent = this.requireSessionAgent(agentId);
|
|
2254
|
+
return Array.from(agent.pendingPermissions.values());
|
|
2255
|
+
}
|
|
2256
|
+
peekPendingPermission(agent) {
|
|
2257
|
+
const iterator = agent.pendingPermissions.values().next();
|
|
2258
|
+
return iterator.done ? null : iterator.value;
|
|
2259
|
+
}
|
|
2260
|
+
/**
|
|
2261
|
+
* Hydrates the runtime timeline from provider history. No-ops if already hydrated.
|
|
2262
|
+
*/
|
|
2263
|
+
async hydrateTimelineFromProvider(agentId, options) {
|
|
2264
|
+
const agent = this.requireSessionAgent(agentId);
|
|
2265
|
+
await this.hydrateTimelineFromLegacyProviderHistory(agent, options);
|
|
2266
|
+
}
|
|
2267
|
+
async rewind(agentId, messageId, mode) {
|
|
2268
|
+
const agent = this.requireSessionAgent(agentId);
|
|
2269
|
+
const submittedRow = this.timelineStore
|
|
2270
|
+
.getRows(agentId)
|
|
2271
|
+
.find((row) => row.item.type === "user_message" &&
|
|
2272
|
+
row.item.messageId === messageId &&
|
|
2273
|
+
row.item.clientMessageId === messageId);
|
|
2274
|
+
if (submittedRow && !submittedRow.providerMessageId) {
|
|
2275
|
+
throw new Error("Cannot rewind before the provider acknowledges the submitted prompt");
|
|
2276
|
+
}
|
|
2277
|
+
const providerMessageId = submittedRow?.providerMessageId ?? messageId;
|
|
2278
|
+
if (this.hasInFlightRun(agentId)) {
|
|
2279
|
+
await this.cancelAgentRunBefore(agentId, "rewind");
|
|
2280
|
+
}
|
|
2281
|
+
const lock = this.runs.createPendingRun(agentId);
|
|
2282
|
+
try {
|
|
2283
|
+
this.logger.info({ agentId, provider: agent.provider, messageId, mode }, "agent.rewind.start");
|
|
2284
|
+
await invokeRewindCapability(agent.session, { messageId: providerMessageId, mode });
|
|
2285
|
+
if (mode !== "files") {
|
|
2286
|
+
await this.hydrateTimelineFromProvider(agentId, {
|
|
2287
|
+
force: true,
|
|
2288
|
+
broadcast: true,
|
|
2289
|
+
broadcastTimeline: false,
|
|
2290
|
+
});
|
|
2291
|
+
this.dispatch({
|
|
2292
|
+
type: "timeline_replacement",
|
|
2293
|
+
agentId,
|
|
2294
|
+
epoch: this.timelineStore.getEpoch(agentId),
|
|
2295
|
+
});
|
|
2296
|
+
}
|
|
2297
|
+
// Rewind stages provider events under the run lock; publish its final state directly.
|
|
2298
|
+
this.refreshSessionPersistence(agent);
|
|
2299
|
+
await this.refreshSessionState(agent, { emit: false });
|
|
2300
|
+
await this.persistSnapshot(agent);
|
|
2301
|
+
this.emitState(agent, { persist: false });
|
|
2302
|
+
this.logger.info({ agentId, provider: agent.provider, messageId, mode }, "agent.rewind.complete");
|
|
2303
|
+
}
|
|
2304
|
+
catch (error) {
|
|
2305
|
+
this.logger.warn({ err: error, agentId, provider: agent.provider, messageId, mode }, "agent.rewind.failed");
|
|
2306
|
+
throw error;
|
|
2307
|
+
}
|
|
2308
|
+
finally {
|
|
2309
|
+
this.runs.settleForegroundRun(agentId, lock.token);
|
|
2310
|
+
}
|
|
2311
|
+
}
|
|
2312
|
+
async deleteAgentState(agentId) {
|
|
2313
|
+
this.discardRetainedAgentState(agentId);
|
|
2314
|
+
await this.deleteCommittedTimeline(agentId);
|
|
2315
|
+
}
|
|
2316
|
+
async deleteCommittedTimeline(agentId) {
|
|
2317
|
+
await this.durableTimelineStore?.deleteAgent(agentId);
|
|
2318
|
+
}
|
|
2319
|
+
async getLastAssistantMessage(agentId) {
|
|
2320
|
+
const agent = this.agents.get(agentId);
|
|
2321
|
+
if (!agent) {
|
|
2322
|
+
return null;
|
|
2323
|
+
}
|
|
2324
|
+
return await this.getLastAssistantMessageFromStores(agentId);
|
|
2325
|
+
}
|
|
2326
|
+
getLastAssistantMessageFromTimeline(timeline) {
|
|
2327
|
+
return this.getLastAssistantMessageSegmentFromTimeline(timeline)?.text ?? null;
|
|
2328
|
+
}
|
|
2329
|
+
getLastAssistantMessageSegmentFromTimeline(timeline) {
|
|
2330
|
+
// Collect the last contiguous assistant messages (Claude streams chunks)
|
|
2331
|
+
const chunks = [];
|
|
2332
|
+
let startsAtBeginning = false;
|
|
2333
|
+
for (let i = timeline.length - 1; i >= 0; i--) {
|
|
2334
|
+
const item = timeline[i];
|
|
2335
|
+
if (item.type !== "assistant_message") {
|
|
2336
|
+
if (chunks.length) {
|
|
2337
|
+
break;
|
|
2338
|
+
}
|
|
2339
|
+
continue;
|
|
2340
|
+
}
|
|
2341
|
+
chunks.push(item.text);
|
|
2342
|
+
startsAtBeginning = i === 0;
|
|
2343
|
+
}
|
|
2344
|
+
if (!chunks.length) {
|
|
2345
|
+
return null;
|
|
2346
|
+
}
|
|
2347
|
+
return {
|
|
2348
|
+
text: chunks.toReversed().join(""),
|
|
2349
|
+
startsAtBeginning,
|
|
2350
|
+
};
|
|
2351
|
+
}
|
|
2352
|
+
async getLastAssistantMessageFromStores(agentId) {
|
|
2353
|
+
const liveTimeline = this.timelineStore.getItems(agentId);
|
|
2354
|
+
const liveSegment = this.getLastAssistantMessageSegmentFromTimeline(liveTimeline);
|
|
2355
|
+
if (!this.durableTimelineStore) {
|
|
2356
|
+
return liveSegment?.text ?? null;
|
|
2357
|
+
}
|
|
2358
|
+
if (!liveSegment) {
|
|
2359
|
+
return await this.durableTimelineStore.getLastAssistantMessage(agentId);
|
|
2360
|
+
}
|
|
2361
|
+
if (!liveSegment.startsAtBeginning) {
|
|
2362
|
+
return liveSegment.text;
|
|
2363
|
+
}
|
|
2364
|
+
const lastDurableItem = await this.durableTimelineStore.getLastItem(agentId);
|
|
2365
|
+
if (lastDurableItem?.type !== "assistant_message") {
|
|
2366
|
+
return liveSegment.text;
|
|
2367
|
+
}
|
|
2368
|
+
const durableMessage = await this.durableTimelineStore.getLastAssistantMessage(agentId);
|
|
2369
|
+
return durableMessage ? `${durableMessage}${liveSegment.text}` : liveSegment.text;
|
|
2370
|
+
}
|
|
2371
|
+
async getLastItemFromStores(agentId) {
|
|
2372
|
+
const lastLiveItem = this.timelineStore.getLastItem(agentId);
|
|
2373
|
+
return lastLiveItem ?? (await this.durableTimelineStore?.getLastItem(agentId)) ?? null;
|
|
2374
|
+
}
|
|
2375
|
+
async waitForAgentEvent(agentId, options) {
|
|
2376
|
+
const snapshot = this.getAgent(agentId);
|
|
2377
|
+
if (!snapshot) {
|
|
2378
|
+
throw new Error(`Agent ${agentId} not found`);
|
|
2379
|
+
}
|
|
2380
|
+
const pendingForegroundRun = this.runs.getPendingRun(agentId);
|
|
2381
|
+
const hasForegroundTurn = Boolean(snapshot.activeForegroundTurnId) || Boolean(pendingForegroundRun);
|
|
2382
|
+
const immediatePermission = this.peekPendingPermission(snapshot);
|
|
2383
|
+
if (immediatePermission) {
|
|
2384
|
+
return {
|
|
2385
|
+
status: snapshot.lifecycle,
|
|
2386
|
+
permission: immediatePermission,
|
|
2387
|
+
lastMessage: await this.getLastAssistantMessage(agentId),
|
|
2388
|
+
};
|
|
2389
|
+
}
|
|
2390
|
+
const initialStatus = snapshot.lifecycle;
|
|
2391
|
+
const initialBusy = isAgentBusy(initialStatus) || hasForegroundTurn;
|
|
2392
|
+
const waitForActive = options?.waitForActive ?? false;
|
|
2393
|
+
if (!waitForActive && !initialBusy) {
|
|
2394
|
+
return {
|
|
2395
|
+
status: initialStatus,
|
|
2396
|
+
permission: null,
|
|
2397
|
+
lastMessage: await this.getLastAssistantMessage(agentId),
|
|
2398
|
+
};
|
|
2399
|
+
}
|
|
2400
|
+
if (waitForActive && !initialBusy && !hasForegroundTurn) {
|
|
2401
|
+
return {
|
|
2402
|
+
status: initialStatus,
|
|
2403
|
+
permission: null,
|
|
2404
|
+
lastMessage: await this.getLastAssistantMessage(agentId),
|
|
2405
|
+
};
|
|
2406
|
+
}
|
|
2407
|
+
if (options?.signal?.aborted) {
|
|
2408
|
+
throw createAbortError(options.signal, "wait_for_agent aborted");
|
|
2409
|
+
}
|
|
2410
|
+
return await new Promise((resolvePromise, reject) => {
|
|
2411
|
+
// Bug #1 Fix: Check abort signal AGAIN inside Promise constructor
|
|
2412
|
+
// to avoid race condition between pre-Promise check and abort listener registration
|
|
2413
|
+
if (options?.signal?.aborted) {
|
|
2414
|
+
reject(createAbortError(options.signal, "wait_for_agent aborted"));
|
|
2415
|
+
return;
|
|
2416
|
+
}
|
|
2417
|
+
let currentStatus = initialStatus;
|
|
2418
|
+
let hasStarted = isAgentBusy(initialStatus) ||
|
|
2419
|
+
Boolean(snapshot.activeForegroundTurnId) ||
|
|
2420
|
+
pendingForegroundRun?.start.status === "started";
|
|
2421
|
+
let terminalStatusOverride = null;
|
|
2422
|
+
let finished = false;
|
|
2423
|
+
// Bug #3 Fix: Declare unsubscribe and abortHandler upfront so cleanup can reference them
|
|
2424
|
+
let unsubscribe = null;
|
|
2425
|
+
let abortHandler = null;
|
|
2426
|
+
const cleanup = () => {
|
|
2427
|
+
// Clean up subscription
|
|
2428
|
+
if (unsubscribe) {
|
|
2429
|
+
try {
|
|
2430
|
+
unsubscribe();
|
|
2431
|
+
}
|
|
2432
|
+
catch {
|
|
2433
|
+
// ignore cleanup errors
|
|
2434
|
+
}
|
|
2435
|
+
unsubscribe = null;
|
|
2436
|
+
}
|
|
2437
|
+
// Clean up abort listener
|
|
2438
|
+
if (abortHandler && options?.signal) {
|
|
2439
|
+
try {
|
|
2440
|
+
options.signal.removeEventListener("abort", abortHandler);
|
|
2441
|
+
}
|
|
2442
|
+
catch {
|
|
2443
|
+
// ignore cleanup errors
|
|
2444
|
+
}
|
|
2445
|
+
abortHandler = null;
|
|
2446
|
+
}
|
|
2447
|
+
};
|
|
2448
|
+
const finish = (permission) => {
|
|
2449
|
+
if (finished) {
|
|
2450
|
+
return;
|
|
2451
|
+
}
|
|
2452
|
+
finished = true;
|
|
2453
|
+
cleanup();
|
|
2454
|
+
void this.getLastAssistantMessage(agentId)
|
|
2455
|
+
.then((lastMessage) => {
|
|
2456
|
+
resolvePromise({
|
|
2457
|
+
status: currentStatus,
|
|
2458
|
+
permission,
|
|
2459
|
+
lastMessage,
|
|
2460
|
+
});
|
|
2461
|
+
return;
|
|
2462
|
+
})
|
|
2463
|
+
.catch(reject);
|
|
2464
|
+
};
|
|
2465
|
+
// Bug #3 Fix: Set up abort handler BEFORE subscription
|
|
2466
|
+
// to ensure cleanup handlers exist before callback can fire
|
|
2467
|
+
if (options?.signal) {
|
|
2468
|
+
abortHandler = () => {
|
|
2469
|
+
cleanup();
|
|
2470
|
+
reject(createAbortError(options.signal, "wait_for_agent aborted"));
|
|
2471
|
+
};
|
|
2472
|
+
options.signal.addEventListener("abort", abortHandler, { once: true });
|
|
2473
|
+
}
|
|
2474
|
+
// Bug #3 Fix: Now subscribe with cleanup handlers already in place
|
|
2475
|
+
// This prevents race condition if callback fires synchronously with replayState: true
|
|
2476
|
+
unsubscribe = this.subscribe((event) => {
|
|
2477
|
+
if (event.type === "agent_state") {
|
|
2478
|
+
currentStatus = event.agent.lifecycle;
|
|
2479
|
+
const pending = this.peekPendingPermission(event.agent);
|
|
2480
|
+
if (pending) {
|
|
2481
|
+
finish(pending);
|
|
2482
|
+
return;
|
|
2483
|
+
}
|
|
2484
|
+
if (isAgentBusy(event.agent.lifecycle)) {
|
|
2485
|
+
hasStarted = true;
|
|
2486
|
+
return;
|
|
2487
|
+
}
|
|
2488
|
+
if (!waitForActive || hasStarted) {
|
|
2489
|
+
if (terminalStatusOverride) {
|
|
2490
|
+
currentStatus = terminalStatusOverride;
|
|
2491
|
+
}
|
|
2492
|
+
finish(null);
|
|
2493
|
+
}
|
|
2494
|
+
return;
|
|
2495
|
+
}
|
|
2496
|
+
if (event.type === "agent_stream") {
|
|
2497
|
+
if (event.event.type === "permission_requested") {
|
|
2498
|
+
finish(event.event.request);
|
|
2499
|
+
return;
|
|
2500
|
+
}
|
|
2501
|
+
if (event.event.type === "turn_failed") {
|
|
2502
|
+
hasStarted = true;
|
|
2503
|
+
terminalStatusOverride = "error";
|
|
2504
|
+
return;
|
|
2505
|
+
}
|
|
2506
|
+
if (event.event.type === "turn_completed") {
|
|
2507
|
+
hasStarted = true;
|
|
2508
|
+
}
|
|
2509
|
+
if (event.event.type === "turn_canceled") {
|
|
2510
|
+
hasStarted = true;
|
|
2511
|
+
}
|
|
2512
|
+
}
|
|
2513
|
+
}, { agentId, replayState: true });
|
|
2514
|
+
});
|
|
2515
|
+
}
|
|
2516
|
+
async registerSession(session, config, agentId, options) {
|
|
2517
|
+
let registered = false;
|
|
2518
|
+
try {
|
|
2519
|
+
this.assertAcceptingAgentRegistrations();
|
|
2520
|
+
const resolvedAgentId = validateAgentId(agentId, "registerSession");
|
|
2521
|
+
if (this.agents.has(resolvedAgentId)) {
|
|
2522
|
+
throw new Error(`Agent with id ${resolvedAgentId} already exists`);
|
|
2523
|
+
}
|
|
2524
|
+
const initialPersistedTitle = await this.resolveInitialPersistedTitle(resolvedAgentId, config, options?.initialTitle ?? null);
|
|
2525
|
+
const now = new Date();
|
|
2526
|
+
const { durableTimelineHasRows } = await this.initializeAgentTimelineForRegister({
|
|
2527
|
+
agentId: resolvedAgentId,
|
|
2528
|
+
now,
|
|
2529
|
+
options,
|
|
2530
|
+
});
|
|
2531
|
+
const managed = this.buildManagedAgentForRegister({
|
|
2532
|
+
resolvedAgentId,
|
|
2533
|
+
session,
|
|
2534
|
+
config,
|
|
2535
|
+
now,
|
|
2536
|
+
durableTimelineHasRows,
|
|
2537
|
+
options,
|
|
2538
|
+
});
|
|
2539
|
+
this.assertAcceptingAgentRegistrations();
|
|
2540
|
+
this.agents.set(resolvedAgentId, managed);
|
|
2541
|
+
registered = true;
|
|
2542
|
+
// Initialize previousStatus to track transitions
|
|
2543
|
+
this.previousStatuses.set(resolvedAgentId, managed.lifecycle);
|
|
2544
|
+
await this.refreshRuntimeInfo(managed, { emit: false });
|
|
2545
|
+
this.assertAgentRegistrationActive(managed);
|
|
2546
|
+
await this.persistSnapshot(managed, {
|
|
2547
|
+
title: initialPersistedTitle,
|
|
2548
|
+
});
|
|
2549
|
+
this.assertAgentRegistrationActive(managed);
|
|
2550
|
+
if (!options?.publishWhenReady) {
|
|
2551
|
+
this.emitState(managed, { persist: false });
|
|
2552
|
+
}
|
|
2553
|
+
await this.refreshSessionState(managed, { emit: false });
|
|
2554
|
+
this.assertAgentRegistrationActive(managed);
|
|
2555
|
+
managed.lifecycle = "idle";
|
|
2556
|
+
this.touchUpdatedAt(managed);
|
|
2557
|
+
await this.persistSnapshot(managed);
|
|
2558
|
+
this.assertAgentRegistrationActive(managed);
|
|
2559
|
+
this.emitState(managed, { persist: false });
|
|
2560
|
+
this.subscribeToSession(managed);
|
|
2561
|
+
return { ...managed };
|
|
2562
|
+
}
|
|
2563
|
+
catch (error) {
|
|
2564
|
+
if (!registered) {
|
|
2565
|
+
await this.closeUnregisteredSession(session);
|
|
2566
|
+
}
|
|
2567
|
+
throw error;
|
|
2568
|
+
}
|
|
2569
|
+
}
|
|
2570
|
+
assertAcceptingAgentRegistrations() {
|
|
2571
|
+
if (!this.acceptingAgentRegistrations) {
|
|
2572
|
+
throw new AgentManagerShuttingDownError();
|
|
2573
|
+
}
|
|
2574
|
+
}
|
|
2575
|
+
assertAgentRegistrationActive(agent) {
|
|
2576
|
+
if (!this.acceptingAgentRegistrations || this.agents.get(agent.id) !== agent) {
|
|
2577
|
+
throw new AgentManagerShuttingDownError();
|
|
2578
|
+
}
|
|
2579
|
+
}
|
|
2580
|
+
async closeUnregisteredSession(session) {
|
|
2581
|
+
try {
|
|
2582
|
+
await session.close();
|
|
2583
|
+
}
|
|
2584
|
+
catch (error) {
|
|
2585
|
+
this.logger.warn({ err: error }, "Failed to close unregistered agent session");
|
|
2586
|
+
}
|
|
2587
|
+
}
|
|
2588
|
+
async requireExternalMcpSupport(session, storedConfig) {
|
|
2589
|
+
if (Object.keys(storedConfig.mcpServers ?? {}).length === 0 ||
|
|
2590
|
+
session.capabilities.supportsMcpServers === true) {
|
|
2591
|
+
return;
|
|
2592
|
+
}
|
|
2593
|
+
await this.closeUnregisteredSession(session);
|
|
2594
|
+
throw new Error(`Provider '${storedConfig.provider}' does not support MCP servers`);
|
|
2595
|
+
}
|
|
2596
|
+
async initializeAgentTimelineForRegister(params) {
|
|
2597
|
+
const { agentId, now, options } = params;
|
|
2598
|
+
const timelineAlreadyPrimed = this.timelineStore.has(agentId);
|
|
2599
|
+
const explicitTimelineSeed = buildExplicitTimelineSeedForRegister(now, options);
|
|
2600
|
+
const shouldSeedFromDurable = !explicitTimelineSeed && !this.timelineStore.has(agentId) && this.durableTimelineStore;
|
|
2601
|
+
const durableTimelineSeed = shouldSeedFromDurable
|
|
2602
|
+
? await this.loadCommittedTimelineSeed(agentId, now)
|
|
2603
|
+
: null;
|
|
2604
|
+
const durableTimelineHasRows = timelineAlreadyPrimed ||
|
|
2605
|
+
(durableTimelineSeed != null && (durableTimelineSeed.nextSeq ?? 1) > 1);
|
|
2606
|
+
const timelineSeed = explicitTimelineSeed ?? durableTimelineSeed;
|
|
2607
|
+
if (timelineSeed || !this.timelineStore.has(agentId)) {
|
|
2608
|
+
this.timelineStore.initialize(agentId, timelineSeed ?? { timestamp: now.toISOString() });
|
|
2609
|
+
}
|
|
2610
|
+
if (options?.timelineRows?.length) {
|
|
2611
|
+
this.enqueueDurableTimelineBulkInsert(agentId, options.timelineRows);
|
|
2612
|
+
}
|
|
2613
|
+
return { durableTimelineHasRows };
|
|
2614
|
+
}
|
|
2615
|
+
buildManagedAgentForRegister(params) {
|
|
2616
|
+
const { resolvedAgentId, session, config, now, durableTimelineHasRows, options } = params;
|
|
2617
|
+
return {
|
|
2618
|
+
id: resolvedAgentId,
|
|
2619
|
+
provider: config.provider,
|
|
2620
|
+
cwd: config.cwd,
|
|
2621
|
+
workspaceId: options?.workspaceId,
|
|
2622
|
+
owner: options?.owner,
|
|
2623
|
+
session,
|
|
2624
|
+
capabilities: session.capabilities,
|
|
2625
|
+
config,
|
|
2626
|
+
runtimeInfo: undefined,
|
|
2627
|
+
lifecycle: "initializing",
|
|
2628
|
+
createdAt: options?.createdAt ?? now,
|
|
2629
|
+
updatedAt: options?.updatedAt ?? now,
|
|
2630
|
+
availableModes: [],
|
|
2631
|
+
currentModeId: null,
|
|
2632
|
+
pendingPermissions: new Map(),
|
|
2633
|
+
bufferedPermissionResolutions: new Map(),
|
|
2634
|
+
inFlightPermissionResponses: new Set(),
|
|
2635
|
+
pendingReplacement: false,
|
|
2636
|
+
activeForegroundTurnId: null,
|
|
2637
|
+
activeTurnId: null,
|
|
2638
|
+
activeTurnStartedAt: null,
|
|
2639
|
+
foregroundTurnWaiters: new Set(),
|
|
2640
|
+
finalizedForegroundTurnIds: new Set(),
|
|
2641
|
+
unsubscribeSession: null,
|
|
2642
|
+
persistence: attachPersistenceCwd(options?.persistence ?? session.describePersistence(), config.cwd),
|
|
2643
|
+
historyPrimed: options?.historyPrimed ?? durableTimelineHasRows,
|
|
2644
|
+
lastUserMessageAt: options?.lastUserMessageAt ?? null,
|
|
2645
|
+
lastUsage: options?.lastUsage,
|
|
2646
|
+
lastError: options?.lastError,
|
|
2647
|
+
attention: resolveInitialAttention(options?.attention),
|
|
2648
|
+
internal: config.internal ?? false,
|
|
2649
|
+
labels: options?.labels ?? {},
|
|
2650
|
+
};
|
|
2651
|
+
}
|
|
2652
|
+
async loadCommittedTimelineSeed(agentId, now) {
|
|
2653
|
+
if (!this.durableTimelineStore) {
|
|
2654
|
+
return { timestamp: now.toISOString() };
|
|
2655
|
+
}
|
|
2656
|
+
return {
|
|
2657
|
+
nextSeq: (await this.durableTimelineStore.getLatestCommittedSeq(agentId)) + 1,
|
|
2658
|
+
timestamp: now.toISOString(),
|
|
2659
|
+
};
|
|
2660
|
+
}
|
|
2661
|
+
prepareAgentForClosure(agent, cancelReason) {
|
|
2662
|
+
this.agentStreamCoalescer.flushAndDiscard(agent.id);
|
|
2663
|
+
this.agents.delete(agent.id);
|
|
2664
|
+
this.previousStatuses.delete(agent.id);
|
|
2665
|
+
if (agent.unsubscribeSession) {
|
|
2666
|
+
agent.unsubscribeSession();
|
|
2667
|
+
agent.unsubscribeSession = null;
|
|
2668
|
+
}
|
|
2669
|
+
this.runs.cancelWaiters(agent, (turnId) => ({
|
|
2670
|
+
type: "turn_canceled",
|
|
2671
|
+
provider: agent.provider,
|
|
2672
|
+
reason: cancelReason,
|
|
2673
|
+
turnId,
|
|
2674
|
+
}));
|
|
2675
|
+
this.runs.clearAgentRun(agent.id);
|
|
2676
|
+
return {
|
|
2677
|
+
...agent,
|
|
2678
|
+
lifecycle: "closed",
|
|
2679
|
+
session: null,
|
|
2680
|
+
activeForegroundTurnId: null,
|
|
2681
|
+
activeTurnId: null,
|
|
2682
|
+
activeTurnStartedAt: null,
|
|
2683
|
+
pendingPermissions: new Map(),
|
|
2684
|
+
bufferedPermissionResolutions: new Map(),
|
|
2685
|
+
inFlightPermissionResponses: new Set(),
|
|
2686
|
+
pendingReplacement: false,
|
|
2687
|
+
foregroundTurnWaiters: new Set(),
|
|
2688
|
+
finalizedForegroundTurnIds: new Set(),
|
|
2689
|
+
unsubscribeSession: null,
|
|
2690
|
+
};
|
|
2691
|
+
}
|
|
2692
|
+
discardRetainedAgentState(agentId) {
|
|
2693
|
+
this.timelineStore.delete(agentId);
|
|
2694
|
+
this.paseoToolPolicies.delete(agentId);
|
|
2695
|
+
for (const event of this.sideConversations.deleteParent(agentId)) {
|
|
2696
|
+
this.dispatch({ type: "side_conversation", event });
|
|
2697
|
+
}
|
|
2698
|
+
for (const event of this.providerSubagents.deleteParent(agentId)) {
|
|
2699
|
+
this.dispatch({ type: "provider_subagent", event });
|
|
2700
|
+
}
|
|
2701
|
+
}
|
|
2702
|
+
emitClosedAgent(agent, options) {
|
|
2703
|
+
this.emitState(agent, options);
|
|
2704
|
+
}
|
|
2705
|
+
subscribeToSession(agent) {
|
|
2706
|
+
if (agent.unsubscribeSession) {
|
|
2707
|
+
return;
|
|
2708
|
+
}
|
|
2709
|
+
const agentId = agent.id;
|
|
2710
|
+
const unsubscribe = agent.session.subscribe((event) => {
|
|
2711
|
+
this.enqueueSessionEvent(agentId, event);
|
|
2712
|
+
});
|
|
2713
|
+
agent.unsubscribeSession = unsubscribe;
|
|
2714
|
+
}
|
|
2715
|
+
enqueueSessionEvent(agentId, event) {
|
|
2716
|
+
this.logger.trace({
|
|
2717
|
+
agentId,
|
|
2718
|
+
provider: event.provider,
|
|
2719
|
+
sessionId: this.agents.get(agentId)?.persistence?.sessionId ?? undefined,
|
|
2720
|
+
turnId: getAgentStreamEventTurnId(event),
|
|
2721
|
+
event,
|
|
2722
|
+
}, "agent.manager.enqueue");
|
|
2723
|
+
const steerBarrier = this.steerEventBarriers.get(agentId);
|
|
2724
|
+
if (steerBarrier) {
|
|
2725
|
+
steerBarrier.events.push(event);
|
|
2726
|
+
return;
|
|
2727
|
+
}
|
|
2728
|
+
const pendingRun = this.runs.getPendingRun(agentId);
|
|
2729
|
+
if (pendingRun?.start.status === "pending") {
|
|
2730
|
+
pendingRun.stagedEvents.push(event);
|
|
2731
|
+
return;
|
|
2732
|
+
}
|
|
2733
|
+
const previous = this.sessionEventTails.get(agentId) ?? Promise.resolve();
|
|
2734
|
+
const next = previous
|
|
2735
|
+
.catch(() => undefined)
|
|
2736
|
+
.then(async () => {
|
|
2737
|
+
const current = this.agents.get(agentId);
|
|
2738
|
+
if (!current) {
|
|
2739
|
+
return;
|
|
2740
|
+
}
|
|
2741
|
+
if (current.session == null) {
|
|
2742
|
+
return;
|
|
2743
|
+
}
|
|
2744
|
+
this.logger.trace({
|
|
2745
|
+
agentId,
|
|
2746
|
+
provider: event.provider,
|
|
2747
|
+
sessionId: current.persistence?.sessionId ?? undefined,
|
|
2748
|
+
turnId: getAgentStreamEventTurnId(event),
|
|
2749
|
+
event,
|
|
2750
|
+
}, "agent.manager.dequeue");
|
|
2751
|
+
await this.dispatchSessionEvent(current, event);
|
|
2752
|
+
return;
|
|
2753
|
+
})
|
|
2754
|
+
.catch((err) => {
|
|
2755
|
+
this.logger.error({ err, agentId, eventType: event.type }, "Failed to process session event");
|
|
2756
|
+
});
|
|
2757
|
+
this.sessionEventTails.set(agentId, next);
|
|
2758
|
+
this.trackBackgroundTask(next);
|
|
2759
|
+
void next.finally(() => {
|
|
2760
|
+
if (this.sessionEventTails.get(agentId) === next) {
|
|
2761
|
+
this.sessionEventTails.delete(agentId);
|
|
2762
|
+
}
|
|
2763
|
+
});
|
|
2764
|
+
}
|
|
2765
|
+
/**
|
|
2766
|
+
* Provider mutations may synchronously emit config events that are processed through the
|
|
2767
|
+
* asynchronous session queue. Apply those events before committing the mutation's explicit
|
|
2768
|
+
* manager state so call order remains authoritative.
|
|
2769
|
+
*/
|
|
2770
|
+
async drainSessionEvents(agentId) {
|
|
2771
|
+
while (true) {
|
|
2772
|
+
const tail = this.sessionEventTails.get(agentId);
|
|
2773
|
+
if (!tail) {
|
|
2774
|
+
return;
|
|
2775
|
+
}
|
|
2776
|
+
await tail;
|
|
2777
|
+
if (this.sessionEventTails.get(agentId) === tail) {
|
|
2778
|
+
return;
|
|
2779
|
+
}
|
|
2780
|
+
}
|
|
2781
|
+
}
|
|
2782
|
+
async dispatchSessionEvent(agent, event) {
|
|
2783
|
+
if (event.type === "provider_subagent") {
|
|
2784
|
+
const update = this.providerSubagents.apply(agent.id, event.provider, event.event);
|
|
2785
|
+
this.dispatch({ type: "provider_subagent", event: update });
|
|
2786
|
+
return;
|
|
2787
|
+
}
|
|
2788
|
+
const turnId = getAgentStreamEventTurnId(event);
|
|
2789
|
+
const matchingWaiters = this.runs.getMatchingWaiters(agent, turnId);
|
|
2790
|
+
this.logger.trace({
|
|
2791
|
+
agentId: agent.id,
|
|
2792
|
+
provider: event.provider,
|
|
2793
|
+
sessionId: agent.persistence?.sessionId ?? undefined,
|
|
2794
|
+
turnId,
|
|
2795
|
+
matchingWaiterCount: matchingWaiters.length,
|
|
2796
|
+
event,
|
|
2797
|
+
}, "agent.manager.dispatch_session_event");
|
|
2798
|
+
const shouldNotifyWaiters = await this.handleStreamEvent(agent, event);
|
|
2799
|
+
if (!shouldNotifyWaiters) {
|
|
2800
|
+
return;
|
|
2801
|
+
}
|
|
2802
|
+
this.runs.notifyWaiters(matchingWaiters, event, {
|
|
2803
|
+
terminal: isTurnTerminalEvent(event),
|
|
2804
|
+
});
|
|
2805
|
+
this.logger.trace({
|
|
2806
|
+
agentId: agent.id,
|
|
2807
|
+
provider: event.provider,
|
|
2808
|
+
sessionId: agent.persistence?.sessionId ?? undefined,
|
|
2809
|
+
turnId,
|
|
2810
|
+
notifiedWaiterCount: matchingWaiters.length,
|
|
2811
|
+
terminal: isTurnTerminalEvent(event),
|
|
2812
|
+
event,
|
|
2813
|
+
}, "agent.manager.notify_waiters");
|
|
2814
|
+
}
|
|
2815
|
+
async resolveInitialPersistedTitle(agentId, config, fallbackTitle) {
|
|
2816
|
+
const existing = await this.registry?.get(agentId);
|
|
2817
|
+
if (existing) {
|
|
2818
|
+
return existing.title ?? null;
|
|
2819
|
+
}
|
|
2820
|
+
const explicitTitle = typeof config.title === "string" && config.title.trim().length > 0
|
|
2821
|
+
? config.title.trim()
|
|
2822
|
+
: null;
|
|
2823
|
+
return explicitTitle ?? fallbackTitle;
|
|
2824
|
+
}
|
|
2825
|
+
async persistSnapshot(agent, options) {
|
|
2826
|
+
if (!this.registry) {
|
|
2827
|
+
return;
|
|
2828
|
+
}
|
|
2829
|
+
// Don't persist internal agents - they're ephemeral system tasks
|
|
2830
|
+
if (agent.internal) {
|
|
2831
|
+
return;
|
|
2832
|
+
}
|
|
2833
|
+
await this.registry.applySnapshot(agent, options);
|
|
2834
|
+
}
|
|
2835
|
+
requireRegistry() {
|
|
2836
|
+
if (!this.registry) {
|
|
2837
|
+
throw new Error("Agent storage unavailable");
|
|
2838
|
+
}
|
|
2839
|
+
return this.registry;
|
|
2840
|
+
}
|
|
2841
|
+
async refreshSessionState(agent, options) {
|
|
2842
|
+
try {
|
|
2843
|
+
const modes = await agent.session.getAvailableModes();
|
|
2844
|
+
agent.availableModes = modes;
|
|
2845
|
+
}
|
|
2846
|
+
catch {
|
|
2847
|
+
agent.availableModes = [];
|
|
2848
|
+
}
|
|
2849
|
+
try {
|
|
2850
|
+
agent.currentModeId = await agent.session.getCurrentMode();
|
|
2851
|
+
}
|
|
2852
|
+
catch {
|
|
2853
|
+
agent.currentModeId = null;
|
|
2854
|
+
}
|
|
2855
|
+
try {
|
|
2856
|
+
const pending = agent.session.getPendingPermissions();
|
|
2857
|
+
agent.pendingPermissions = new Map(pending.map((request) => [request.id, request]));
|
|
2858
|
+
}
|
|
2859
|
+
catch {
|
|
2860
|
+
agent.pendingPermissions.clear();
|
|
2861
|
+
}
|
|
2862
|
+
this.syncFeaturesFromSession(agent);
|
|
2863
|
+
await this.refreshRuntimeInfo(agent, options);
|
|
2864
|
+
}
|
|
2865
|
+
async refreshRuntimeInfo(agent, options) {
|
|
2866
|
+
try {
|
|
2867
|
+
const newInfo = await agent.session.getRuntimeInfo();
|
|
2868
|
+
const changed = newInfo.model !== agent.runtimeInfo?.model ||
|
|
2869
|
+
newInfo.thinkingOptionId !== agent.runtimeInfo?.thinkingOptionId ||
|
|
2870
|
+
newInfo.sessionId !== agent.runtimeInfo?.sessionId ||
|
|
2871
|
+
newInfo.modeId !== agent.runtimeInfo?.modeId;
|
|
2872
|
+
agent.runtimeInfo = newInfo;
|
|
2873
|
+
if (!agent.persistence && newInfo.sessionId) {
|
|
2874
|
+
agent.persistence = attachPersistenceCwd({ provider: agent.provider, sessionId: newInfo.sessionId }, agent.cwd);
|
|
2875
|
+
}
|
|
2876
|
+
// Emit state if runtimeInfo changed so clients get the updated model
|
|
2877
|
+
if (changed && options?.emit !== false) {
|
|
2878
|
+
this.emitState(agent);
|
|
2879
|
+
}
|
|
2880
|
+
}
|
|
2881
|
+
catch {
|
|
2882
|
+
// Keep existing runtimeInfo if refresh fails.
|
|
2883
|
+
}
|
|
2884
|
+
}
|
|
2885
|
+
async hydrateTimelineFromLegacyProviderHistory(agent, options) {
|
|
2886
|
+
if (agent.historyPrimed && !options?.force) {
|
|
2887
|
+
return;
|
|
2888
|
+
}
|
|
2889
|
+
const broadcast = options?.broadcast ?? false;
|
|
2890
|
+
const broadcastTimeline = options?.broadcastTimeline ?? broadcast;
|
|
2891
|
+
if (options?.force) {
|
|
2892
|
+
await this.forceHydrateTimelineFromLegacyProviderHistory(agent, typeof broadcast === "function" ? broadcast() : broadcast, typeof broadcastTimeline === "function" ? broadcastTimeline() : broadcastTimeline);
|
|
2893
|
+
return;
|
|
2894
|
+
}
|
|
2895
|
+
await this.primeTimelineFromLegacyProviderHistory(agent, broadcast);
|
|
2896
|
+
}
|
|
2897
|
+
async forceHydrateTimelineFromLegacyProviderHistory(agent, broadcast, broadcastTimeline) {
|
|
2898
|
+
const historyEvents = [];
|
|
2899
|
+
const providerSubagentEvents = [];
|
|
2900
|
+
for await (const rawEvent of agent.session.streamHistory()) {
|
|
2901
|
+
const event = limitAgentStreamEventContent(rawEvent);
|
|
2902
|
+
if (event.type === "timeline") {
|
|
2903
|
+
if (event.item.type === "user_message" && isSystemInjectedEnvelope(event.item.text)) {
|
|
2904
|
+
continue;
|
|
2905
|
+
}
|
|
2906
|
+
historyEvents.push(event);
|
|
2907
|
+
}
|
|
2908
|
+
else if (event.type === "provider_subagent") {
|
|
2909
|
+
providerSubagentEvents.push(event);
|
|
2910
|
+
}
|
|
2911
|
+
}
|
|
2912
|
+
this.agentStreamCoalescer.flushAndDiscard(agent.id);
|
|
2913
|
+
await this.deleteCommittedTimeline(agent.id);
|
|
2914
|
+
this.timelineStore.delete(agent.id);
|
|
2915
|
+
this.timelineStore.initialize(agent.id, { timestamp: new Date().toISOString() });
|
|
2916
|
+
agent.historyPrimed = true;
|
|
2917
|
+
for (const event of this.sideConversations.deleteParent(agent.id)) {
|
|
2918
|
+
if (broadcast) {
|
|
2919
|
+
this.dispatch({ type: "side_conversation", event });
|
|
2920
|
+
}
|
|
2921
|
+
}
|
|
2922
|
+
for (const event of this.providerSubagents.deleteParent(agent.id)) {
|
|
2923
|
+
if (broadcast) {
|
|
2924
|
+
this.dispatch({ type: "provider_subagent", event });
|
|
2925
|
+
}
|
|
2926
|
+
}
|
|
2927
|
+
for (const event of providerSubagentEvents) {
|
|
2928
|
+
const update = this.providerSubagents.apply(agent.id, event.provider, event.event);
|
|
2929
|
+
if (broadcast) {
|
|
2930
|
+
this.dispatch({ type: "provider_subagent", event: update });
|
|
2931
|
+
}
|
|
2932
|
+
}
|
|
2933
|
+
for (const event of historyEvents) {
|
|
2934
|
+
const row = this.recordTimeline(agent.id, event.item, event.timestamp ? { timestamp: event.timestamp } : undefined);
|
|
2935
|
+
if (broadcastTimeline) {
|
|
2936
|
+
this.dispatchStream(agent.id, event, {
|
|
2937
|
+
seq: row.seq,
|
|
2938
|
+
epoch: this.timelineStore.getEpoch(agent.id),
|
|
2939
|
+
timestamp: row.timestamp,
|
|
2940
|
+
});
|
|
2941
|
+
}
|
|
2942
|
+
}
|
|
2943
|
+
this.touchUpdatedAt(agent);
|
|
2944
|
+
this.emitState(agent);
|
|
2945
|
+
}
|
|
2946
|
+
async primeTimelineFromLegacyProviderHistory(agent, broadcast) {
|
|
2947
|
+
const deferredBroadcast = typeof broadcast === "function";
|
|
2948
|
+
const timelineEvents = [];
|
|
2949
|
+
const providerSubagentEvents = [];
|
|
2950
|
+
agent.historyPrimed = false;
|
|
2951
|
+
try {
|
|
2952
|
+
for await (const rawEvent of agent.session.streamHistory()) {
|
|
2953
|
+
const event = limitAgentStreamEventContent(rawEvent);
|
|
2954
|
+
if (event.type === "provider_subagent") {
|
|
2955
|
+
const update = this.providerSubagents.apply(agent.id, event.provider, event.event);
|
|
2956
|
+
const managerEvent = { type: "provider_subagent", event: update };
|
|
2957
|
+
if (deferredBroadcast) {
|
|
2958
|
+
providerSubagentEvents.push(managerEvent);
|
|
2959
|
+
}
|
|
2960
|
+
else if (broadcast) {
|
|
2961
|
+
this.dispatch(managerEvent);
|
|
2962
|
+
}
|
|
2963
|
+
continue;
|
|
2964
|
+
}
|
|
2965
|
+
if (event.type !== "timeline") {
|
|
2966
|
+
continue;
|
|
2967
|
+
}
|
|
2968
|
+
if (event.item.type === "user_message" && isSystemInjectedEnvelope(event.item.text)) {
|
|
2969
|
+
continue;
|
|
2970
|
+
}
|
|
2971
|
+
const row = this.recordTimeline(agent.id, event.item, event.timestamp ? { timestamp: event.timestamp } : undefined);
|
|
2972
|
+
if (deferredBroadcast) {
|
|
2973
|
+
timelineEvents.push({ event, row });
|
|
2974
|
+
}
|
|
2975
|
+
else if (broadcast) {
|
|
2976
|
+
this.dispatchStream(agent.id, event, {
|
|
2977
|
+
seq: row.seq,
|
|
2978
|
+
epoch: this.timelineStore.getEpoch(agent.id),
|
|
2979
|
+
timestamp: row.timestamp,
|
|
2980
|
+
});
|
|
2981
|
+
}
|
|
2982
|
+
}
|
|
2983
|
+
}
|
|
2984
|
+
catch (error) {
|
|
2985
|
+
this.logger.warn({ err: error, agentId: agent.id }, "Failed to hydrate provider history");
|
|
2986
|
+
throw error;
|
|
2987
|
+
}
|
|
2988
|
+
agent.historyPrimed = true;
|
|
2989
|
+
if (typeof broadcast !== "function" || !broadcast()) {
|
|
2990
|
+
return;
|
|
2991
|
+
}
|
|
2992
|
+
for (const event of providerSubagentEvents) {
|
|
2993
|
+
this.dispatch(event);
|
|
2994
|
+
}
|
|
2995
|
+
for (const { event, row } of timelineEvents) {
|
|
2996
|
+
this.dispatchStream(agent.id, event, {
|
|
2997
|
+
seq: row.seq,
|
|
2998
|
+
epoch: this.timelineStore.getEpoch(agent.id),
|
|
2999
|
+
timestamp: row.timestamp,
|
|
3000
|
+
});
|
|
3001
|
+
}
|
|
3002
|
+
}
|
|
3003
|
+
notifyForegroundTurnWaiters(agentId, event) {
|
|
3004
|
+
const turnId = getAgentStreamEventTurnId(event);
|
|
3005
|
+
if (turnId == null) {
|
|
3006
|
+
return;
|
|
3007
|
+
}
|
|
3008
|
+
const agent = this.agents.get(agentId);
|
|
3009
|
+
if (!agent) {
|
|
3010
|
+
return;
|
|
3011
|
+
}
|
|
3012
|
+
this.runs.notifyAgentWaiters(agent, event);
|
|
3013
|
+
this.logger.trace({
|
|
3014
|
+
agentId,
|
|
3015
|
+
provider: event.provider,
|
|
3016
|
+
sessionId: agent.persistence?.sessionId ?? undefined,
|
|
3017
|
+
turnId,
|
|
3018
|
+
event,
|
|
3019
|
+
}, "agent.manager.notify_waiters.coalesced");
|
|
3020
|
+
}
|
|
3021
|
+
async handleStreamEvent(agent, event, options) {
|
|
3022
|
+
event = limitAgentStreamEventContent(event);
|
|
3023
|
+
const identified = attachManagedTurnIdentity(agent, event, options?.fromHistory === true);
|
|
3024
|
+
event = identified.event;
|
|
3025
|
+
const eventTurnId = identified.turnId;
|
|
3026
|
+
const isForegroundEvent = agent.activeForegroundTurnId === eventTurnId;
|
|
3027
|
+
this.traceHandleStreamEventStart(agent, event, eventTurnId, isForegroundEvent);
|
|
3028
|
+
if (eventTurnId &&
|
|
3029
|
+
isTurnTerminalEvent(event) &&
|
|
3030
|
+
this.runs.hasFinalizedTurn(agent, eventTurnId)) {
|
|
3031
|
+
return false;
|
|
3032
|
+
}
|
|
3033
|
+
// Only update timestamp for live events, not history replay
|
|
3034
|
+
if (!options?.fromHistory) {
|
|
3035
|
+
this.touchUpdatedAt(agent);
|
|
3036
|
+
if (this.agentStreamCoalescer.handle(agent.id, event)) {
|
|
3037
|
+
this.traceCoalescerBuffered(agent, event, eventTurnId);
|
|
3038
|
+
return false;
|
|
3039
|
+
}
|
|
3040
|
+
this.agentStreamCoalescer.flushFor(agent.id);
|
|
3041
|
+
}
|
|
3042
|
+
let terminalDisposition = "untracked";
|
|
3043
|
+
if (isTurnTerminalEvent(event)) {
|
|
3044
|
+
terminalDisposition = this.applyActiveTurnTerminal(agent, eventTurnId, options?.fromHistory === true);
|
|
3045
|
+
}
|
|
3046
|
+
const flags = { shouldDispatchEvent: true, shouldNotifyWaiters: true };
|
|
3047
|
+
const dispatchPromise = this.dispatchStreamEventByType({
|
|
3048
|
+
agent,
|
|
3049
|
+
event,
|
|
3050
|
+
options,
|
|
3051
|
+
isForegroundEvent,
|
|
3052
|
+
eventTurnId,
|
|
3053
|
+
terminalDisposition,
|
|
3054
|
+
flags,
|
|
3055
|
+
});
|
|
3056
|
+
if (dispatchPromise) {
|
|
3057
|
+
await dispatchPromise;
|
|
3058
|
+
}
|
|
3059
|
+
if (!options?.fromHistory) {
|
|
3060
|
+
if (isTurnTerminalEvent(event)) {
|
|
3061
|
+
this.runs.settleTerminalRun(agent.id, eventTurnId);
|
|
3062
|
+
if (isForegroundEvent) {
|
|
3063
|
+
this.finalizeForegroundTurn(agent, eventTurnId);
|
|
3064
|
+
}
|
|
3065
|
+
}
|
|
3066
|
+
if (flags.shouldDispatchEvent) {
|
|
3067
|
+
this.dispatchStream(agent.id, event, { timestamp: new Date().toISOString() });
|
|
3068
|
+
}
|
|
3069
|
+
}
|
|
3070
|
+
this.traceHandleStreamEventEnd(agent, event, eventTurnId, flags);
|
|
3071
|
+
return flags.shouldNotifyWaiters;
|
|
3072
|
+
}
|
|
3073
|
+
traceHandleStreamEventStart(agent, event, turnId, isForegroundEvent) {
|
|
3074
|
+
this.logger.trace({
|
|
3075
|
+
agentId: agent.id,
|
|
3076
|
+
provider: event.provider,
|
|
3077
|
+
sessionId: agent.persistence?.sessionId ?? undefined,
|
|
3078
|
+
turnId,
|
|
3079
|
+
lifecycle: agent.lifecycle,
|
|
3080
|
+
activeForegroundTurnId: agent.activeForegroundTurnId,
|
|
3081
|
+
isForegroundEvent,
|
|
3082
|
+
event,
|
|
3083
|
+
}, "agent.manager.handle_stream_event.start");
|
|
3084
|
+
}
|
|
3085
|
+
traceCoalescerBuffered(agent, event, turnId) {
|
|
3086
|
+
this.logger.trace({
|
|
3087
|
+
agentId: agent.id,
|
|
3088
|
+
provider: event.provider,
|
|
3089
|
+
sessionId: agent.persistence?.sessionId ?? undefined,
|
|
3090
|
+
turnId,
|
|
3091
|
+
event,
|
|
3092
|
+
}, "agent.manager.coalescer.buffer");
|
|
3093
|
+
}
|
|
3094
|
+
traceHandleStreamEventEnd(agent, event, turnId, flags) {
|
|
3095
|
+
this.logger.trace({
|
|
3096
|
+
agentId: agent.id,
|
|
3097
|
+
provider: event.provider,
|
|
3098
|
+
sessionId: agent.persistence?.sessionId ?? undefined,
|
|
3099
|
+
turnId,
|
|
3100
|
+
lifecycle: agent.lifecycle,
|
|
3101
|
+
activeForegroundTurnId: agent.activeForegroundTurnId,
|
|
3102
|
+
shouldDispatchEvent: flags.shouldDispatchEvent,
|
|
3103
|
+
shouldNotifyWaiters: flags.shouldNotifyWaiters,
|
|
3104
|
+
event,
|
|
3105
|
+
}, "agent.manager.handle_stream_event.end");
|
|
3106
|
+
}
|
|
3107
|
+
dispatchStreamEventByType(params) {
|
|
3108
|
+
const { agent, event, options, isForegroundEvent, eventTurnId, terminalDisposition, flags } = params;
|
|
3109
|
+
switch (event.type) {
|
|
3110
|
+
case "thread_started":
|
|
3111
|
+
this.onStreamThreadStarted(agent);
|
|
3112
|
+
return undefined;
|
|
3113
|
+
case "usage_updated":
|
|
3114
|
+
agent.lastUsage = event.usage;
|
|
3115
|
+
this.emitState(agent);
|
|
3116
|
+
return undefined;
|
|
3117
|
+
case "mode_changed":
|
|
3118
|
+
agent.currentModeId = event.currentModeId;
|
|
3119
|
+
agent.availableModes = event.availableModes;
|
|
3120
|
+
if (agent.runtimeInfo) {
|
|
3121
|
+
agent.runtimeInfo = { ...agent.runtimeInfo, modeId: event.currentModeId };
|
|
3122
|
+
}
|
|
3123
|
+
flags.shouldDispatchEvent = false;
|
|
3124
|
+
this.emitState(agent);
|
|
3125
|
+
return undefined;
|
|
3126
|
+
case "model_changed":
|
|
3127
|
+
agent.runtimeInfo = event.runtimeInfo;
|
|
3128
|
+
if (!agent.persistence && event.runtimeInfo.sessionId) {
|
|
3129
|
+
agent.persistence = attachPersistenceCwd({ provider: agent.provider, sessionId: event.runtimeInfo.sessionId }, agent.cwd);
|
|
3130
|
+
}
|
|
3131
|
+
agent.currentModeId = event.runtimeInfo.modeId ?? agent.currentModeId;
|
|
3132
|
+
flags.shouldDispatchEvent = false;
|
|
3133
|
+
this.emitState(agent);
|
|
3134
|
+
return undefined;
|
|
3135
|
+
case "thinking_option_changed":
|
|
3136
|
+
agent.config.thinkingOptionId = event.thinkingOptionId ?? undefined;
|
|
3137
|
+
if (agent.runtimeInfo) {
|
|
3138
|
+
agent.runtimeInfo = {
|
|
3139
|
+
...agent.runtimeInfo,
|
|
3140
|
+
thinkingOptionId: event.thinkingOptionId,
|
|
3141
|
+
};
|
|
3142
|
+
}
|
|
3143
|
+
flags.shouldDispatchEvent = false;
|
|
3144
|
+
this.emitState(agent);
|
|
3145
|
+
return undefined;
|
|
3146
|
+
case "timeline":
|
|
3147
|
+
return this.onStreamTimelineEvent({ agent, event, options, flags });
|
|
3148
|
+
case "turn_completed":
|
|
3149
|
+
this.onStreamTurnCompleted({
|
|
3150
|
+
agent,
|
|
3151
|
+
event,
|
|
3152
|
+
eventTurnId,
|
|
3153
|
+
isForegroundEvent,
|
|
3154
|
+
terminalDisposition,
|
|
3155
|
+
});
|
|
3156
|
+
return undefined;
|
|
3157
|
+
case "turn_failed":
|
|
3158
|
+
return this.onStreamTurnFailed({
|
|
3159
|
+
agent,
|
|
3160
|
+
event,
|
|
3161
|
+
eventTurnId,
|
|
3162
|
+
isForegroundEvent,
|
|
3163
|
+
terminalDisposition,
|
|
3164
|
+
options,
|
|
3165
|
+
});
|
|
3166
|
+
case "turn_canceled":
|
|
3167
|
+
this.onStreamTurnCanceled({
|
|
3168
|
+
agent,
|
|
3169
|
+
event,
|
|
3170
|
+
eventTurnId,
|
|
3171
|
+
isForegroundEvent,
|
|
3172
|
+
terminalDisposition,
|
|
3173
|
+
options,
|
|
3174
|
+
});
|
|
3175
|
+
return undefined;
|
|
3176
|
+
case "turn_started":
|
|
3177
|
+
this.onStreamTurnStarted({ agent, eventTurnId, isForegroundEvent, flags });
|
|
3178
|
+
return undefined;
|
|
3179
|
+
case "permission_requested":
|
|
3180
|
+
this.onStreamPermissionRequested(agent, event);
|
|
3181
|
+
return undefined;
|
|
3182
|
+
case "permission_resolved":
|
|
3183
|
+
this.onStreamPermissionResolved({ agent, event, options, flags });
|
|
3184
|
+
return undefined;
|
|
3185
|
+
default:
|
|
3186
|
+
return undefined;
|
|
3187
|
+
}
|
|
3188
|
+
}
|
|
3189
|
+
onStreamThreadStarted(agent) {
|
|
3190
|
+
const previousSessionId = agent.persistence?.sessionId ?? null;
|
|
3191
|
+
this.refreshSessionPersistence(agent);
|
|
3192
|
+
if (agent.persistence?.sessionId !== previousSessionId) {
|
|
3193
|
+
this.emitState(agent);
|
|
3194
|
+
}
|
|
3195
|
+
void this.refreshRuntimeInfo(agent);
|
|
3196
|
+
}
|
|
3197
|
+
refreshSessionPersistence(agent) {
|
|
3198
|
+
const handle = agent.session.describePersistence();
|
|
3199
|
+
if (handle) {
|
|
3200
|
+
agent.persistence = attachPersistenceCwd(handle, agent.cwd);
|
|
3201
|
+
}
|
|
3202
|
+
}
|
|
3203
|
+
async onStreamTimelineEvent(params) {
|
|
3204
|
+
const { agent, event, options, flags } = params;
|
|
3205
|
+
if (event.item.type === "user_message" && isSystemInjectedEnvelope(event.item.text)) {
|
|
3206
|
+
flags.shouldDispatchEvent = false;
|
|
3207
|
+
flags.shouldNotifyWaiters = false;
|
|
3208
|
+
return;
|
|
3209
|
+
}
|
|
3210
|
+
if (event.item.type === "user_message" &&
|
|
3211
|
+
event.item.clientMessageId &&
|
|
3212
|
+
this.reconcileSubmittedPromptEcho(agent, event.item, event.turnId)) {
|
|
3213
|
+
flags.shouldDispatchEvent = false;
|
|
3214
|
+
flags.shouldNotifyWaiters = false;
|
|
3215
|
+
return;
|
|
3216
|
+
}
|
|
3217
|
+
if (options?.fromHistory) {
|
|
3218
|
+
this.recordTimeline(agent.id, event.item, event.timestamp ? { timestamp: event.timestamp } : undefined);
|
|
3219
|
+
flags.shouldDispatchEvent = false;
|
|
3220
|
+
flags.shouldNotifyWaiters = false;
|
|
3221
|
+
return;
|
|
3222
|
+
}
|
|
3223
|
+
this.recordAndDispatchTimelineItem(agent.id, event.item, event.provider, event.turnId);
|
|
3224
|
+
if (event.item.type === "user_message") {
|
|
3225
|
+
agent.lastUserMessageAt = new Date();
|
|
3226
|
+
this.emitState(agent);
|
|
3227
|
+
}
|
|
3228
|
+
flags.shouldDispatchEvent = false;
|
|
3229
|
+
flags.shouldNotifyWaiters = true;
|
|
3230
|
+
}
|
|
3231
|
+
onStreamTurnCompleted(params) {
|
|
3232
|
+
const { agent, event, eventTurnId, isForegroundEvent, terminalDisposition } = params;
|
|
3233
|
+
this.logger.trace({
|
|
3234
|
+
agentId: agent.id,
|
|
3235
|
+
provider: agent.provider,
|
|
3236
|
+
sessionId: agent.persistence?.sessionId ?? undefined,
|
|
3237
|
+
turnId: eventTurnId,
|
|
3238
|
+
lifecycle: agent.lifecycle,
|
|
3239
|
+
activeForegroundTurnId: agent.activeForegroundTurnId,
|
|
3240
|
+
}, "agent.manager.turn.completed");
|
|
3241
|
+
if (terminalDisposition === "stale")
|
|
3242
|
+
return;
|
|
3243
|
+
if (event.usage) {
|
|
3244
|
+
agent.lastUsage = { ...agent.lastUsage, ...event.usage };
|
|
3245
|
+
}
|
|
3246
|
+
// If no usage on turn_completed, keep lastUsage as-is so context window
|
|
3247
|
+
// data accumulated during streaming isn't lost when the provider omits
|
|
3248
|
+
// it from the completion event.
|
|
3249
|
+
agent.lastError = undefined;
|
|
3250
|
+
if (!isForegroundEvent &&
|
|
3251
|
+
!agent.activeForegroundTurnId &&
|
|
3252
|
+
agent.lifecycle !== "idle" &&
|
|
3253
|
+
!agent.pendingReplacement) {
|
|
3254
|
+
agent.lifecycle = "idle";
|
|
3255
|
+
this.emitState(agent);
|
|
3256
|
+
}
|
|
3257
|
+
void this.refreshRuntimeInfo(agent);
|
|
3258
|
+
}
|
|
3259
|
+
async onStreamTurnFailed(params) {
|
|
3260
|
+
const { agent, event, eventTurnId, isForegroundEvent, terminalDisposition, options } = params;
|
|
3261
|
+
this.logger.warn({
|
|
3262
|
+
agentId: agent.id,
|
|
3263
|
+
provider: agent.provider,
|
|
3264
|
+
sessionId: agent.persistence?.sessionId ?? undefined,
|
|
3265
|
+
turnId: eventTurnId,
|
|
3266
|
+
lifecycle: agent.lifecycle,
|
|
3267
|
+
activeForegroundTurnId: agent.activeForegroundTurnId,
|
|
3268
|
+
eventTurnId,
|
|
3269
|
+
error: event.error,
|
|
3270
|
+
code: event.code,
|
|
3271
|
+
diagnostic: event.diagnostic,
|
|
3272
|
+
}, "handleStreamEvent: turn_failed");
|
|
3273
|
+
if (terminalDisposition === "stale")
|
|
3274
|
+
return;
|
|
3275
|
+
if (!isForegroundEvent && !agent.activeForegroundTurnId) {
|
|
3276
|
+
agent.lifecycle = "error";
|
|
3277
|
+
}
|
|
3278
|
+
agent.lastError = event.error;
|
|
3279
|
+
await this.appendSystemErrorTimelineMessage(agent, event.provider, this.formatTurnFailedMessage(event), options);
|
|
3280
|
+
this.resolvePendingPermissionsForAgent(agent, event.provider, options, "Turn failed");
|
|
3281
|
+
if (!isForegroundEvent && !agent.activeForegroundTurnId) {
|
|
3282
|
+
this.emitState(agent);
|
|
3283
|
+
}
|
|
3284
|
+
}
|
|
3285
|
+
onStreamTurnCanceled(params) {
|
|
3286
|
+
const { agent, event, eventTurnId, isForegroundEvent, terminalDisposition, options } = params;
|
|
3287
|
+
this.logger.trace({
|
|
3288
|
+
agentId: agent.id,
|
|
3289
|
+
provider: agent.provider,
|
|
3290
|
+
sessionId: agent.persistence?.sessionId ?? undefined,
|
|
3291
|
+
turnId: eventTurnId,
|
|
3292
|
+
lifecycle: agent.lifecycle,
|
|
3293
|
+
activeForegroundTurnId: agent.activeForegroundTurnId,
|
|
3294
|
+
eventTurnId,
|
|
3295
|
+
}, "agent.manager.turn.canceled");
|
|
3296
|
+
if (terminalDisposition === "stale")
|
|
3297
|
+
return;
|
|
3298
|
+
if (!isForegroundEvent && !agent.activeForegroundTurnId && !agent.pendingReplacement) {
|
|
3299
|
+
agent.lifecycle = "idle";
|
|
3300
|
+
}
|
|
3301
|
+
agent.lastError = undefined;
|
|
3302
|
+
this.resolvePendingPermissionsForAgent(agent, event.provider, options, "Interrupted");
|
|
3303
|
+
if (!isForegroundEvent && !agent.activeForegroundTurnId) {
|
|
3304
|
+
this.emitState(agent);
|
|
3305
|
+
}
|
|
3306
|
+
}
|
|
3307
|
+
onStreamTurnStarted(params) {
|
|
3308
|
+
const { agent, eventTurnId, isForegroundEvent, flags } = params;
|
|
3309
|
+
this.logger.trace({
|
|
3310
|
+
agentId: agent.id,
|
|
3311
|
+
provider: agent.provider,
|
|
3312
|
+
sessionId: agent.persistence?.sessionId ?? undefined,
|
|
3313
|
+
turnId: eventTurnId,
|
|
3314
|
+
lifecycle: agent.lifecycle,
|
|
3315
|
+
activeForegroundTurnId: agent.activeForegroundTurnId,
|
|
3316
|
+
}, "agent.manager.turn.started");
|
|
3317
|
+
if (isForegroundEvent) {
|
|
3318
|
+
flags.shouldDispatchEvent = false;
|
|
3319
|
+
flags.shouldNotifyWaiters = false;
|
|
3320
|
+
return;
|
|
3321
|
+
}
|
|
3322
|
+
if (agent.activeForegroundTurnId) {
|
|
3323
|
+
flags.shouldDispatchEvent = false;
|
|
3324
|
+
flags.shouldNotifyWaiters = false;
|
|
3325
|
+
return;
|
|
3326
|
+
}
|
|
3327
|
+
this.runs.trackAutonomousRun(agent.id, eventTurnId ?? null);
|
|
3328
|
+
if (eventTurnId) {
|
|
3329
|
+
this.openActiveTurn(agent, eventTurnId, new Date());
|
|
3330
|
+
}
|
|
3331
|
+
agent.lifecycle = "running";
|
|
3332
|
+
this.emitState(agent);
|
|
3333
|
+
}
|
|
3334
|
+
onStreamPermissionRequested(agent, event) {
|
|
3335
|
+
const hadPendingPermissions = agent.pendingPermissions.size > 0;
|
|
3336
|
+
agent.pendingPermissions.set(event.request.id, event.request);
|
|
3337
|
+
this.refreshSessionPersistence(agent);
|
|
3338
|
+
if (!hadPendingPermissions && !agent.internal) {
|
|
3339
|
+
this.broadcastAgentAttention(agent, "permission");
|
|
3340
|
+
}
|
|
3341
|
+
this.emitState(agent);
|
|
3342
|
+
}
|
|
3343
|
+
onStreamPermissionResolved(params) {
|
|
3344
|
+
const { agent, event, options, flags } = params;
|
|
3345
|
+
agent.pendingPermissions.delete(event.requestId);
|
|
3346
|
+
this.refreshSessionPersistence(agent);
|
|
3347
|
+
if (!options?.fromHistory && agent.inFlightPermissionResponses.has(event.requestId)) {
|
|
3348
|
+
agent.bufferedPermissionResolutions.set(event.requestId, event);
|
|
3349
|
+
flags.shouldDispatchEvent = false;
|
|
3350
|
+
return;
|
|
3351
|
+
}
|
|
3352
|
+
this.emitState(agent);
|
|
3353
|
+
}
|
|
3354
|
+
resolvePendingPermissionsForAgent(agent, provider, options, message) {
|
|
3355
|
+
for (const [requestId] of agent.pendingPermissions) {
|
|
3356
|
+
agent.pendingPermissions.delete(requestId);
|
|
3357
|
+
if (!options?.fromHistory) {
|
|
3358
|
+
this.dispatchStream(agent.id, {
|
|
3359
|
+
type: "permission_resolved",
|
|
3360
|
+
provider,
|
|
3361
|
+
requestId,
|
|
3362
|
+
resolution: { behavior: "deny", message },
|
|
3363
|
+
});
|
|
3364
|
+
}
|
|
3365
|
+
}
|
|
3366
|
+
}
|
|
3367
|
+
recordAndDispatchTimelineItem(agentId, item, provider, turnId, options) {
|
|
3368
|
+
const row = this.recordTimeline(agentId, item, { ...options, turnId });
|
|
3369
|
+
const event = {
|
|
3370
|
+
type: "timeline",
|
|
3371
|
+
item,
|
|
3372
|
+
provider,
|
|
3373
|
+
...(turnId !== undefined ? { turnId } : {}),
|
|
3374
|
+
};
|
|
3375
|
+
this.dispatchStream(agentId, event, {
|
|
3376
|
+
seq: row.seq,
|
|
3377
|
+
epoch: this.timelineStore.getEpoch(agentId),
|
|
3378
|
+
timestamp: row.timestamp,
|
|
3379
|
+
});
|
|
3380
|
+
if (item.type === "tool_call" &&
|
|
3381
|
+
item.status === "completed" &&
|
|
3382
|
+
item.detail?.type === "shell" &&
|
|
3383
|
+
commandMayHaveChangedExternalState(item.detail.command)) {
|
|
3384
|
+
const agent = this.agents.get(agentId);
|
|
3385
|
+
if (agent) {
|
|
3386
|
+
this.onWorkspaceStateMayHaveChanged?.({ cwd: agent.cwd });
|
|
3387
|
+
}
|
|
3388
|
+
}
|
|
3389
|
+
return event;
|
|
3390
|
+
}
|
|
3391
|
+
recordSubmittedPrompt(agent, prompt, clientMessageId, options) {
|
|
3392
|
+
if (this.timelineStore.getSubmittedUserMessage(agent.id, clientMessageId)) {
|
|
3393
|
+
return;
|
|
3394
|
+
}
|
|
3395
|
+
this.touchUpdatedAt(agent);
|
|
3396
|
+
agent.lastUserMessageAt = new Date();
|
|
3397
|
+
const item = {
|
|
3398
|
+
type: "user_message",
|
|
3399
|
+
text: submittedPromptText(prompt),
|
|
3400
|
+
clientMessageId,
|
|
3401
|
+
...(options?.messageId ? { messageId: options.messageId } : {}),
|
|
3402
|
+
};
|
|
3403
|
+
this.recordAndDispatchTimelineItem(agent.id, item, agent.provider, options?.turnId, options);
|
|
3404
|
+
}
|
|
3405
|
+
reconcileSubmittedPromptEcho(agent, item, turnId) {
|
|
3406
|
+
const { clientMessageId, messageId } = item;
|
|
3407
|
+
if (!clientMessageId)
|
|
3408
|
+
return null;
|
|
3409
|
+
let existing = this.timelineStore.getSubmittedUserMessage(agent.id, clientMessageId);
|
|
3410
|
+
if (!existing) {
|
|
3411
|
+
this.recordSubmittedPrompt(agent, item.text, clientMessageId, {
|
|
3412
|
+
messageId: clientMessageId,
|
|
3413
|
+
...(messageId ? { providerMessageId: messageId } : {}),
|
|
3414
|
+
...(turnId ? { turnId } : {}),
|
|
3415
|
+
});
|
|
3416
|
+
existing = this.timelineStore.getSubmittedUserMessage(agent.id, clientMessageId);
|
|
3417
|
+
}
|
|
3418
|
+
if (!existing || existing.item.type !== "user_message")
|
|
3419
|
+
return null;
|
|
3420
|
+
if (messageId) {
|
|
3421
|
+
const enriched = this.timelineStore.enrichSubmittedUserMessage(agent.id, clientMessageId, messageId);
|
|
3422
|
+
if (enriched)
|
|
3423
|
+
this.enqueueDurableTimelineUpdate(agent.id, enriched);
|
|
3424
|
+
}
|
|
3425
|
+
return existing;
|
|
3426
|
+
}
|
|
3427
|
+
async appendSystemErrorTimelineMessage(agent, provider, message, options) {
|
|
3428
|
+
if (options?.fromHistory) {
|
|
3429
|
+
return;
|
|
3430
|
+
}
|
|
3431
|
+
const normalized = message.trim();
|
|
3432
|
+
if (!normalized) {
|
|
3433
|
+
return;
|
|
3434
|
+
}
|
|
3435
|
+
const text = `${SYSTEM_ERROR_PREFIX} ${normalized}`;
|
|
3436
|
+
const lastItem = await this.getLastItemFromStores(agent.id);
|
|
3437
|
+
if (lastItem?.type === "assistant_message" && lastItem.text === text) {
|
|
3438
|
+
return;
|
|
3439
|
+
}
|
|
3440
|
+
const item = { type: "assistant_message", text };
|
|
3441
|
+
const row = this.recordTimeline(agent.id, item);
|
|
3442
|
+
this.dispatchStream(agent.id, {
|
|
3443
|
+
type: "timeline",
|
|
3444
|
+
item,
|
|
3445
|
+
provider,
|
|
3446
|
+
}, {
|
|
3447
|
+
seq: row.seq,
|
|
3448
|
+
epoch: this.timelineStore.getEpoch(agent.id),
|
|
3449
|
+
timestamp: row.timestamp,
|
|
3450
|
+
});
|
|
3451
|
+
}
|
|
3452
|
+
formatTurnFailedMessage(event) {
|
|
3453
|
+
const base = event.error.trim();
|
|
3454
|
+
const parts = [base.length > 0 ? base : "Provider run failed"];
|
|
3455
|
+
const code = event.code?.trim();
|
|
3456
|
+
if (code) {
|
|
3457
|
+
parts.push(`code: ${code}`);
|
|
3458
|
+
}
|
|
3459
|
+
const diagnostic = event.diagnostic?.trim();
|
|
3460
|
+
if (diagnostic && diagnostic !== base) {
|
|
3461
|
+
parts.push(diagnostic);
|
|
3462
|
+
}
|
|
3463
|
+
return parts.join("\n\n");
|
|
3464
|
+
}
|
|
3465
|
+
recordTimeline(agentId, item, options) {
|
|
3466
|
+
item = limitAgentTimelineItemContent(item);
|
|
3467
|
+
const row = this.timelineStore.append(agentId, item, options);
|
|
3468
|
+
this.enqueueDurableTimelineAppend(agentId, row);
|
|
3469
|
+
return row;
|
|
3470
|
+
}
|
|
3471
|
+
emitState(agent, options) {
|
|
3472
|
+
// Keep attention as an edge-triggered unread signal, not a level signal.
|
|
3473
|
+
this.checkAndSetAttention(agent);
|
|
3474
|
+
if (options?.persist !== false) {
|
|
3475
|
+
this.enqueueBackgroundPersist(agent);
|
|
3476
|
+
}
|
|
3477
|
+
this.syncFeaturesFromSession(agent);
|
|
3478
|
+
this.logger.trace({
|
|
3479
|
+
agentId: agent.id,
|
|
3480
|
+
provider: agent.provider,
|
|
3481
|
+
sessionId: agent.persistence?.sessionId ?? undefined,
|
|
3482
|
+
turnId: agent.activeForegroundTurnId ?? undefined,
|
|
3483
|
+
lifecycle: agent.lifecycle,
|
|
3484
|
+
activeForegroundTurnId: agent.activeForegroundTurnId,
|
|
3485
|
+
pendingPermissions: agent.pendingPermissions.size,
|
|
3486
|
+
persist: options?.persist !== false,
|
|
3487
|
+
}, "agent.manager.emit_state");
|
|
3488
|
+
this.dispatch({
|
|
3489
|
+
type: "agent_state",
|
|
3490
|
+
agent: { ...agent },
|
|
3491
|
+
});
|
|
3492
|
+
}
|
|
3493
|
+
syncFeaturesFromSession(agent) {
|
|
3494
|
+
if ("session" in agent && agent.session?.features) {
|
|
3495
|
+
agent.features = agent.session.features;
|
|
3496
|
+
}
|
|
3497
|
+
}
|
|
3498
|
+
checkAndSetAttention(agent) {
|
|
3499
|
+
const previousStatus = this.previousStatuses.get(agent.id);
|
|
3500
|
+
const currentStatus = agent.lifecycle;
|
|
3501
|
+
// Track the new status
|
|
3502
|
+
this.previousStatuses.set(agent.id, currentStatus);
|
|
3503
|
+
// Skip attention tracking for internal agents
|
|
3504
|
+
if (agent.internal) {
|
|
3505
|
+
return;
|
|
3506
|
+
}
|
|
3507
|
+
// Skip if already requires attention
|
|
3508
|
+
if (agent.attention.requiresAttention) {
|
|
3509
|
+
return;
|
|
3510
|
+
}
|
|
3511
|
+
// Check if agent transitioned from running to idle (finished)
|
|
3512
|
+
if (previousStatus === "running" && currentStatus === "idle") {
|
|
3513
|
+
agent.attention = {
|
|
3514
|
+
requiresAttention: true,
|
|
3515
|
+
attentionReason: "finished",
|
|
3516
|
+
attentionTimestamp: new Date(),
|
|
3517
|
+
};
|
|
3518
|
+
this.broadcastAgentAttention(agent, "finished");
|
|
3519
|
+
return;
|
|
3520
|
+
}
|
|
3521
|
+
// Check if agent entered error state
|
|
3522
|
+
if (previousStatus !== "error" && currentStatus === "error") {
|
|
3523
|
+
agent.attention = {
|
|
3524
|
+
requiresAttention: true,
|
|
3525
|
+
attentionReason: "error",
|
|
3526
|
+
attentionTimestamp: new Date(),
|
|
3527
|
+
};
|
|
3528
|
+
this.broadcastAgentAttention(agent, "error");
|
|
3529
|
+
return;
|
|
3530
|
+
}
|
|
3531
|
+
}
|
|
3532
|
+
enqueueBackgroundPersist(agent) {
|
|
3533
|
+
const task = this.persistSnapshot(agent).catch((err) => {
|
|
3534
|
+
this.logger.error({ err, agentId: agent.id }, "Failed to persist agent snapshot");
|
|
3535
|
+
});
|
|
3536
|
+
this.trackBackgroundTask(task);
|
|
3537
|
+
}
|
|
3538
|
+
enqueueDurableTimelineAppend(agentId, row) {
|
|
3539
|
+
if (!this.durableTimelineStore) {
|
|
3540
|
+
return;
|
|
3541
|
+
}
|
|
3542
|
+
const task = this.durableTimelineStore.bulkInsert(agentId, [row]).catch((err) => {
|
|
3543
|
+
this.logger.error({ err, agentId, seq: row.seq, itemType: row.item.type }, "Failed to append timeline row to durable store");
|
|
3544
|
+
});
|
|
3545
|
+
this.trackBackgroundTask(task);
|
|
3546
|
+
}
|
|
3547
|
+
enqueueDurableTimelineBulkInsert(agentId, rows) {
|
|
3548
|
+
if (!this.durableTimelineStore || rows.length === 0) {
|
|
3549
|
+
return;
|
|
3550
|
+
}
|
|
3551
|
+
const task = this.durableTimelineStore.bulkInsert(agentId, rows).catch((err) => {
|
|
3552
|
+
this.logger.error({ err, agentId, rowCount: rows.length }, "Failed to seed durable timeline store");
|
|
3553
|
+
});
|
|
3554
|
+
this.trackBackgroundTask(task);
|
|
3555
|
+
}
|
|
3556
|
+
enqueueDurableTimelineUpdate(agentId, row) {
|
|
3557
|
+
if (!this.durableTimelineStore)
|
|
3558
|
+
return;
|
|
3559
|
+
const task = this.durableTimelineStore.updateCommittedRow(agentId, row).catch((err) => {
|
|
3560
|
+
this.logger.error({ err, agentId, seq: row.seq, itemType: row.item.type }, "Failed to enrich durable timeline row");
|
|
3561
|
+
});
|
|
3562
|
+
this.trackBackgroundTask(task);
|
|
3563
|
+
}
|
|
3564
|
+
trackBackgroundTask(task) {
|
|
3565
|
+
this.backgroundTasks.add(task);
|
|
3566
|
+
void task.finally(() => {
|
|
3567
|
+
this.backgroundTasks.delete(task);
|
|
3568
|
+
});
|
|
3569
|
+
}
|
|
3570
|
+
trackAgentRegistrationOperation(result) {
|
|
3571
|
+
const settled = result.then(() => undefined, () => undefined);
|
|
3572
|
+
this.agentRegistrationTasks.add(settled);
|
|
3573
|
+
void settled.then(() => {
|
|
3574
|
+
this.agentRegistrationTasks.delete(settled);
|
|
3575
|
+
return undefined;
|
|
3576
|
+
});
|
|
3577
|
+
return result;
|
|
3578
|
+
}
|
|
3579
|
+
/**
|
|
3580
|
+
* Flush any background persistence work (best-effort).
|
|
3581
|
+
*/
|
|
3582
|
+
async flush() {
|
|
3583
|
+
await this.flushTasks({ includeAgentRegistrations: false });
|
|
3584
|
+
}
|
|
3585
|
+
/**
|
|
3586
|
+
* Flush persistence and agent registrations that crossed the synchronous
|
|
3587
|
+
* shutdown barrier. Those registrations own provider sessions until they
|
|
3588
|
+
* either install them or close them.
|
|
3589
|
+
*/
|
|
3590
|
+
async flushForShutdown() {
|
|
3591
|
+
await this.flushTasks({ includeAgentRegistrations: true });
|
|
3592
|
+
}
|
|
3593
|
+
async flushTasks(options) {
|
|
3594
|
+
this.agentStreamCoalescer.flushAll();
|
|
3595
|
+
// Drain tasks, including tasks spawned while awaiting.
|
|
3596
|
+
while (this.backgroundTasks.size > 0 ||
|
|
3597
|
+
(options.includeAgentRegistrations && this.agentRegistrationTasks.size > 0)) {
|
|
3598
|
+
const pending = options.includeAgentRegistrations
|
|
3599
|
+
? [...this.backgroundTasks, ...this.agentRegistrationTasks]
|
|
3600
|
+
: [...this.backgroundTasks];
|
|
3601
|
+
await Promise.allSettled(pending);
|
|
3602
|
+
}
|
|
3603
|
+
}
|
|
3604
|
+
broadcastAgentAttention(agent, reason) {
|
|
3605
|
+
if (isDelegatedAgent(agent)) {
|
|
3606
|
+
return;
|
|
3607
|
+
}
|
|
3608
|
+
this.onAgentAttention?.({
|
|
3609
|
+
agentId: agent.id,
|
|
3610
|
+
provider: agent.provider,
|
|
3611
|
+
reason,
|
|
3612
|
+
});
|
|
3613
|
+
}
|
|
3614
|
+
dispatchStream(agentId, event, metadata) {
|
|
3615
|
+
if (event.type === "timeline") {
|
|
3616
|
+
event = {
|
|
3617
|
+
...event,
|
|
3618
|
+
item: limitAgentTimelineItemContent(event.item),
|
|
3619
|
+
};
|
|
3620
|
+
}
|
|
3621
|
+
const agent = this.agents.get(agentId);
|
|
3622
|
+
this.logger.trace({
|
|
3623
|
+
agentId,
|
|
3624
|
+
provider: event.provider,
|
|
3625
|
+
sessionId: agent?.persistence?.sessionId ?? undefined,
|
|
3626
|
+
turnId: getAgentStreamEventTurnId(event),
|
|
3627
|
+
metadata,
|
|
3628
|
+
event,
|
|
3629
|
+
}, "agent.manager.dispatch_stream");
|
|
3630
|
+
this.dispatch({ type: "agent_stream", agentId, event, ...metadata });
|
|
3631
|
+
if (this.pluginLifecycle && agent && !agent.internal && event.type !== "timeline") {
|
|
3632
|
+
publishAgentStream(this.pluginLifecycle, describeHookAgent({ ...agent, title: agent.config.title }), event, this.timelineStore.getItems(agentId));
|
|
3633
|
+
}
|
|
3634
|
+
}
|
|
3635
|
+
// An event variant with a parent-scoped payload needs a filtering arm here and a membership
|
|
3636
|
+
// arm in eventBelongsToInternalAgent(). Miss the first and an agentId-scoped subscriber
|
|
3637
|
+
// receives every parent's events; miss the second and internal-agent events leak to global
|
|
3638
|
+
// subscribers. provider_subagent and side_conversation carry the whole pattern.
|
|
3639
|
+
dispatch(event) {
|
|
3640
|
+
for (const subscriber of this.subscribers) {
|
|
3641
|
+
if (subscriber.agentId &&
|
|
3642
|
+
event.type === "agent_stream" &&
|
|
3643
|
+
subscriber.agentId !== event.agentId) {
|
|
3644
|
+
continue;
|
|
3645
|
+
}
|
|
3646
|
+
if (subscriber.agentId &&
|
|
3647
|
+
event.type === "agent_state" &&
|
|
3648
|
+
subscriber.agentId !== event.agent.id) {
|
|
3649
|
+
continue;
|
|
3650
|
+
}
|
|
3651
|
+
if (subscriber.agentId &&
|
|
3652
|
+
event.type === "provider_subagent" &&
|
|
3653
|
+
subscriber.agentId !==
|
|
3654
|
+
(event.event.type === "upsert"
|
|
3655
|
+
? event.event.subagent.parentAgentId
|
|
3656
|
+
: event.event.parentAgentId)) {
|
|
3657
|
+
continue;
|
|
3658
|
+
}
|
|
3659
|
+
if (subscriber.agentId &&
|
|
3660
|
+
event.type === "side_conversation" &&
|
|
3661
|
+
subscriber.agentId !== sideConversationEventParentId(event.event)) {
|
|
3662
|
+
continue;
|
|
3663
|
+
}
|
|
3664
|
+
// Skip internal agents for global subscribers (those without a specific agentId)
|
|
3665
|
+
if (!subscriber.agentId && this.eventBelongsToInternalAgent(event)) {
|
|
3666
|
+
continue;
|
|
3667
|
+
}
|
|
3668
|
+
subscriber.callback(event);
|
|
3669
|
+
}
|
|
3670
|
+
}
|
|
3671
|
+
eventBelongsToInternalAgent(event) {
|
|
3672
|
+
if (event.type === "agent_state")
|
|
3673
|
+
return event.agent.internal === true;
|
|
3674
|
+
if (event.type === "agent_stream")
|
|
3675
|
+
return this.agents.get(event.agentId)?.internal === true;
|
|
3676
|
+
if (event.type === "side_conversation") {
|
|
3677
|
+
return this.agents.get(sideConversationEventParentId(event.event))?.internal === true;
|
|
3678
|
+
}
|
|
3679
|
+
if (event.type !== "provider_subagent")
|
|
3680
|
+
return false;
|
|
3681
|
+
const parentAgentId = event.event.type === "upsert"
|
|
3682
|
+
? event.event.subagent.parentAgentId
|
|
3683
|
+
: event.event.parentAgentId;
|
|
3684
|
+
return this.agents.get(parentAgentId)?.internal === true;
|
|
3685
|
+
}
|
|
3686
|
+
async normalizeConfig(config, options = {}) {
|
|
3687
|
+
const normalized = { ...config };
|
|
3688
|
+
// Always resolve cwd to absolute path for consistent history file lookup
|
|
3689
|
+
if (normalized.cwd) {
|
|
3690
|
+
normalized.cwd = resolve(normalized.cwd);
|
|
3691
|
+
try {
|
|
3692
|
+
const cwdStats = await stat(normalized.cwd);
|
|
3693
|
+
if (!cwdStats.isDirectory()) {
|
|
3694
|
+
throw new Error(`Working directory is not a directory: ${normalized.cwd}`);
|
|
3695
|
+
}
|
|
3696
|
+
}
|
|
3697
|
+
catch (error) {
|
|
3698
|
+
if (error instanceof Error &&
|
|
3699
|
+
"code" in error &&
|
|
3700
|
+
error.code === "ENOENT") {
|
|
3701
|
+
throw new Error(`Working directory does not exist: ${normalized.cwd}`, { cause: error });
|
|
3702
|
+
}
|
|
3703
|
+
if (error instanceof Error) {
|
|
3704
|
+
throw error;
|
|
3705
|
+
}
|
|
3706
|
+
throw new Error(`Failed to access working directory: ${normalized.cwd}`, { cause: error });
|
|
3707
|
+
}
|
|
3708
|
+
}
|
|
3709
|
+
if (typeof normalized.model === "string") {
|
|
3710
|
+
const trimmed = normalized.model.trim();
|
|
3711
|
+
normalized.model = trimmed.length > 0 && trimmed !== "default" ? trimmed : undefined;
|
|
3712
|
+
}
|
|
3713
|
+
const shouldResolveDefaultModel = options.resolveDefaultModel ?? true;
|
|
3714
|
+
if (shouldResolveDefaultModel && !normalized.model) {
|
|
3715
|
+
const defaultModelId = await this.resolveDefaultModelId(normalized);
|
|
3716
|
+
if (defaultModelId) {
|
|
3717
|
+
normalized.model = defaultModelId;
|
|
3718
|
+
}
|
|
3719
|
+
}
|
|
3720
|
+
return this.applyProviderConfiguration(normalized);
|
|
3721
|
+
}
|
|
3722
|
+
applyProviderConfiguration(config) {
|
|
3723
|
+
const definition = this.providerDefinitions.get(config.provider);
|
|
3724
|
+
if (config.providerOptions !== undefined && !definition?.validateOptions) {
|
|
3725
|
+
throw new Error(`Provider '${config.provider}' does not accept providerOptions`);
|
|
3726
|
+
}
|
|
3727
|
+
const validatedOptions = definition?.validateOptions?.(config.providerOptions);
|
|
3728
|
+
const withOptions = definition?.applyOptions
|
|
3729
|
+
? definition.applyOptions(config, validatedOptions)
|
|
3730
|
+
: config;
|
|
3731
|
+
this.validateToolPolicyServers(withOptions);
|
|
3732
|
+
if (withOptions.toolPolicy && !definition?.applyToolPolicy) {
|
|
3733
|
+
throw new Error(`Provider '${config.provider}' cannot preapprove exact MCP tools for unattended execution`);
|
|
3734
|
+
}
|
|
3735
|
+
return definition?.applyToolPolicy
|
|
3736
|
+
? definition.applyToolPolicy(withOptions, withOptions.toolPolicy)
|
|
3737
|
+
: withOptions;
|
|
3738
|
+
}
|
|
3739
|
+
validateToolPolicyServers(config) {
|
|
3740
|
+
if (!config.toolPolicy)
|
|
3741
|
+
return;
|
|
3742
|
+
const serverNames = new Set(Object.keys(config.mcpServers ?? {}));
|
|
3743
|
+
for (const grant of config.toolPolicy.preapproved) {
|
|
3744
|
+
if (!serverNames.has(grant.server)) {
|
|
3745
|
+
throw new Error(`toolPolicy preapproval '${grant.server}.${grant.tool}' requires MCP server '${grant.server}' in the same agent request`);
|
|
3746
|
+
}
|
|
3747
|
+
}
|
|
3748
|
+
}
|
|
3749
|
+
async resolveDefaultModelId(config) {
|
|
3750
|
+
const client = this.clients.get(config.provider);
|
|
3751
|
+
if (!client) {
|
|
3752
|
+
return undefined;
|
|
3753
|
+
}
|
|
3754
|
+
try {
|
|
3755
|
+
const catalog = await client.fetchCatalog({
|
|
3756
|
+
scope: "workspace",
|
|
3757
|
+
cwd: config.cwd,
|
|
3758
|
+
force: false,
|
|
3759
|
+
});
|
|
3760
|
+
return (catalog.models.find((model) => model.isDefault) ?? catalog.models[0])?.id;
|
|
3761
|
+
}
|
|
3762
|
+
catch {
|
|
3763
|
+
// Provider may not support model listing — leave model undefined.
|
|
3764
|
+
return undefined;
|
|
3765
|
+
}
|
|
3766
|
+
}
|
|
3767
|
+
async prepareSessionConfig(config, agentId, env) {
|
|
3768
|
+
const storedConfig = await this.normalizeConfig(stripInternalPaseoMcpServer(config), { env });
|
|
3769
|
+
const paseoToolPolicy = this.paseoToolsEnabled
|
|
3770
|
+
? this.resolvePaseoToolPolicy(storedConfig.provider)
|
|
3771
|
+
: { enabled: false };
|
|
3772
|
+
const launchConfig = this.applyDaemonAppendSystemPrompt(withRuntimePaseoMcpServer({
|
|
3773
|
+
config: storedConfig,
|
|
3774
|
+
agentId,
|
|
3775
|
+
mcpBaseUrl: this.paseoToolsEnabled && isPaseoToolPolicyEnabled(paseoToolPolicy)
|
|
3776
|
+
? this.mcpBaseUrl
|
|
3777
|
+
: null,
|
|
3778
|
+
mcpAuthToken: this.mcpAuthToken,
|
|
3779
|
+
}));
|
|
3780
|
+
return { storedConfig, launchConfig, paseoToolPolicy };
|
|
3781
|
+
}
|
|
3782
|
+
applyDaemonAppendSystemPrompt(config) {
|
|
3783
|
+
const daemonAppendSystemPrompt = this.appendSystemPrompt.trim();
|
|
3784
|
+
const next = { ...config };
|
|
3785
|
+
delete next.daemonAppendSystemPrompt;
|
|
3786
|
+
return daemonAppendSystemPrompt
|
|
3787
|
+
? {
|
|
3788
|
+
...next,
|
|
3789
|
+
daemonAppendSystemPrompt,
|
|
3790
|
+
}
|
|
3791
|
+
: next;
|
|
3792
|
+
}
|
|
3793
|
+
async buildLaunchContext(agentId, client, cwd, paseoToolPolicy, env, opening) {
|
|
3794
|
+
if (this.pluginLifecycle) {
|
|
3795
|
+
const request = {
|
|
3796
|
+
agentId,
|
|
3797
|
+
provider: client.provider,
|
|
3798
|
+
cwd,
|
|
3799
|
+
workspaceId: opening?.workspaceId ?? null,
|
|
3800
|
+
reason: opening?.reason ?? "resume",
|
|
3801
|
+
purpose: opening?.purpose ?? "interactive",
|
|
3802
|
+
env: { ...env },
|
|
3803
|
+
};
|
|
3804
|
+
const transformed = await this.pluginLifecycle.before("agent.session_open", request);
|
|
3805
|
+
env = transformed.env;
|
|
3806
|
+
}
|
|
3807
|
+
const context = {
|
|
3808
|
+
agentId,
|
|
3809
|
+
env: {
|
|
3810
|
+
...env,
|
|
3811
|
+
PASEO_AGENT_ID: agentId,
|
|
3812
|
+
PASEO_AGENT_CWD: cwd,
|
|
3813
|
+
},
|
|
3814
|
+
};
|
|
3815
|
+
if (this.paseoToolsEnabled &&
|
|
3816
|
+
isPaseoToolPolicyEnabled(paseoToolPolicy) &&
|
|
3817
|
+
client.capabilities.supportsNativePaseoTools &&
|
|
3818
|
+
this.paseoToolCatalogFactory) {
|
|
3819
|
+
context.paseoTools = await this.paseoToolCatalogFactory({
|
|
3820
|
+
callerAgentId: agentId,
|
|
3821
|
+
paseoToolPolicy,
|
|
3822
|
+
});
|
|
3823
|
+
}
|
|
3824
|
+
return context;
|
|
3825
|
+
}
|
|
3826
|
+
resolveProviderLaunchConfig(launchConfig, launchContext) {
|
|
3827
|
+
return launchContext.paseoTools ? stripInternalPaseoMcpServer(launchConfig) : launchConfig;
|
|
3828
|
+
}
|
|
3829
|
+
async requireAvailableClient(options) {
|
|
3830
|
+
const client = this.clients.get(options.provider);
|
|
3831
|
+
if (!client) {
|
|
3832
|
+
const configuredProviders = this.getConfiguredProviderIds();
|
|
3833
|
+
throw new Error(`Unknown provider '${options.provider}'. Configured providers: ${formatProviderList(configuredProviders)}.`);
|
|
3834
|
+
}
|
|
3835
|
+
let unavailableReason = null;
|
|
3836
|
+
try {
|
|
3837
|
+
const available = await client.isAvailable();
|
|
3838
|
+
if (available) {
|
|
3839
|
+
return client;
|
|
3840
|
+
}
|
|
3841
|
+
}
|
|
3842
|
+
catch (error) {
|
|
3843
|
+
unavailableReason = error instanceof Error ? error.message : String(error);
|
|
3844
|
+
}
|
|
3845
|
+
const availableProviders = (await this.listProviderAvailability())
|
|
3846
|
+
.filter((entry) => entry.available)
|
|
3847
|
+
.map((entry) => entry.provider);
|
|
3848
|
+
const providerList = formatProviderList(availableProviders);
|
|
3849
|
+
const reason = unavailableReason ? ` Reason: ${unavailableReason}.` : "";
|
|
3850
|
+
throw new Error(`Provider '${options.provider}' is not available.${reason} Available providers: ${providerList}. Use one of those providers, or install/configure '${options.provider}'.`);
|
|
3851
|
+
}
|
|
3852
|
+
requireEnabledProvider(provider) {
|
|
3853
|
+
if (this.providerEnabled.get(provider) === false) {
|
|
3854
|
+
throw new Error(`Provider '${provider}' is disabled`);
|
|
3855
|
+
}
|
|
3856
|
+
}
|
|
3857
|
+
getConfiguredProviderIds() {
|
|
3858
|
+
return Array.from(new Set([...this.providerEnabled.keys(), ...this.clients.keys()]));
|
|
3859
|
+
}
|
|
3860
|
+
requireClient(provider) {
|
|
3861
|
+
const client = this.clients.get(provider);
|
|
3862
|
+
if (!client) {
|
|
3863
|
+
throw new Error(`No client registered for provider '${provider}'`);
|
|
3864
|
+
}
|
|
3865
|
+
return client;
|
|
3866
|
+
}
|
|
3867
|
+
async syncNativeArchiveState(provider, persistence, state) {
|
|
3868
|
+
if (!persistence)
|
|
3869
|
+
return;
|
|
3870
|
+
const client = this.clients.get(provider);
|
|
3871
|
+
const sync = state === "archive" ? client?.archiveNativeSession : client?.unarchiveNativeSession;
|
|
3872
|
+
if (!sync)
|
|
3873
|
+
return;
|
|
3874
|
+
if (state === "restore") {
|
|
3875
|
+
await sync.call(client, persistence);
|
|
3876
|
+
return;
|
|
3877
|
+
}
|
|
3878
|
+
try {
|
|
3879
|
+
await sync.call(client, persistence);
|
|
3880
|
+
}
|
|
3881
|
+
catch (error) {
|
|
3882
|
+
this.logger.warn({ error, provider, sessionId: persistence.sessionId }, "Failed to archive native session (best-effort)");
|
|
3883
|
+
}
|
|
3884
|
+
}
|
|
3885
|
+
requireAgent(id) {
|
|
3886
|
+
const normalizedId = validateAgentId(id, "requireAgent");
|
|
3887
|
+
const agent = this.agents.get(normalizedId);
|
|
3888
|
+
if (!agent) {
|
|
3889
|
+
throw new Error(`Unknown agent '${normalizedId}'`);
|
|
3890
|
+
}
|
|
3891
|
+
return agent;
|
|
3892
|
+
}
|
|
3893
|
+
requireSessionAgent(id) {
|
|
3894
|
+
const agent = this.requireAgent(id);
|
|
3895
|
+
if (agent.session === null) {
|
|
3896
|
+
throw new Error(`Agent '${agent.id}' has no managed session`);
|
|
3897
|
+
}
|
|
3898
|
+
return agent;
|
|
3899
|
+
}
|
|
3900
|
+
requirePublicAgent(id) {
|
|
3901
|
+
const agent = this.requireAgent(id);
|
|
3902
|
+
if (agent.internal) {
|
|
3903
|
+
throw new Error(`Unknown agent '${agent.id}'`);
|
|
3904
|
+
}
|
|
3905
|
+
return agent;
|
|
3906
|
+
}
|
|
3907
|
+
}
|
|
3908
|
+
function matchesImportableSessionQuery(session, rawQuery) {
|
|
3909
|
+
const query = rawQuery?.trim().toLowerCase();
|
|
3910
|
+
if (!query)
|
|
3911
|
+
return true;
|
|
3912
|
+
const cwdBasename = basename(session.cwd.replaceAll("\\", "/"));
|
|
3913
|
+
return [session.title, session.firstPromptPreview, session.lastPromptPreview, cwdBasename].some((value) => value?.toLowerCase().includes(query));
|
|
3914
|
+
}
|
|
3915
|
+
export function commandMayHaveChangedExternalState(command) {
|
|
3916
|
+
const normalized = command.toLowerCase();
|
|
3917
|
+
// Commands that operate on remote state and do NOT trigger local file
|
|
3918
|
+
// watchers. Local git mutations (commit, checkout, merge, rebase, reset,
|
|
3919
|
+
// pull) are already caught by watchers on .git/HEAD and refs/heads/.
|
|
3920
|
+
return (
|
|
3921
|
+
// GitHub PR operations (merge, close, create, edit, comment, review)
|
|
3922
|
+
/\bgh\s+pr\s+(merge|close|create|edit|comment|review)\b/.test(normalized) ||
|
|
3923
|
+
// Pushes to remote — local refs unchanged, but remote state (PR checks,
|
|
3924
|
+
// mergeable status) may shift immediately after.
|
|
3925
|
+
/\bgit\s+push\b/.test(normalized) ||
|
|
3926
|
+
// Fetches update refs/remotes/ which our watchers do not watch, so
|
|
3927
|
+
// ahead/behind counts can drift stale until the next refresh.
|
|
3928
|
+
/\bgit\s+fetch\b/.test(normalized));
|
|
3929
|
+
}
|
|
3930
|
+
//# sourceMappingURL=agent-manager.js.map
|