@seawork/server 1.0.0
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 +23 -0
- package/README.md +107 -0
- package/dist/scripts/dev-runner.js +27 -0
- package/dist/scripts/dev-runner.js.map +1 -0
- package/dist/scripts/mcp-stdio-socket-bridge-cli.mjs +62 -0
- package/dist/scripts/supervisor-entrypoint.js +123 -0
- package/dist/scripts/supervisor-entrypoint.js.map +1 -0
- package/dist/scripts/supervisor.js +148 -0
- package/dist/scripts/supervisor.js.map +1 -0
- package/dist/server/client/daemon-client-relay-e2ee-transport.d.ts +8 -0
- package/dist/server/client/daemon-client-relay-e2ee-transport.d.ts.map +1 -0
- package/dist/server/client/daemon-client-relay-e2ee-transport.js +161 -0
- package/dist/server/client/daemon-client-relay-e2ee-transport.js.map +1 -0
- package/dist/server/client/daemon-client-transport-types.d.ts +34 -0
- package/dist/server/client/daemon-client-transport-types.d.ts.map +1 -0
- package/dist/server/client/daemon-client-transport-types.js +2 -0
- package/dist/server/client/daemon-client-transport-types.js.map +1 -0
- package/dist/server/client/daemon-client-transport-utils.d.ts +9 -0
- package/dist/server/client/daemon-client-transport-utils.d.ts.map +1 -0
- package/dist/server/client/daemon-client-transport-utils.js +121 -0
- package/dist/server/client/daemon-client-transport-utils.js.map +1 -0
- package/dist/server/client/daemon-client-transport.d.ts +5 -0
- package/dist/server/client/daemon-client-transport.d.ts.map +1 -0
- package/dist/server/client/daemon-client-transport.js +4 -0
- package/dist/server/client/daemon-client-transport.js.map +1 -0
- package/dist/server/client/daemon-client-websocket-transport.d.ts +7 -0
- package/dist/server/client/daemon-client-websocket-transport.d.ts.map +1 -0
- package/dist/server/client/daemon-client-websocket-transport.js +119 -0
- package/dist/server/client/daemon-client-websocket-transport.js.map +1 -0
- package/dist/server/client/daemon-client.d.ts +697 -0
- package/dist/server/client/daemon-client.d.ts.map +1 -0
- package/dist/server/client/daemon-client.js +2885 -0
- package/dist/server/client/daemon-client.js.map +1 -0
- package/dist/server/server/agent/activity-curator.d.ts +8 -0
- package/dist/server/server/agent/activity-curator.d.ts.map +1 -0
- package/dist/server/server/agent/activity-curator.js +243 -0
- package/dist/server/server/agent/activity-curator.js.map +1 -0
- package/dist/server/server/agent/agent-management-mcp.d.ts +41 -0
- package/dist/server/server/agent/agent-management-mcp.d.ts.map +1 -0
- package/dist/server/server/agent/agent-management-mcp.js +767 -0
- package/dist/server/server/agent/agent-management-mcp.js.map +1 -0
- package/dist/server/server/agent/agent-manager.d.ts +287 -0
- package/dist/server/server/agent/agent-manager.d.ts.map +1 -0
- package/dist/server/server/agent/agent-manager.js +1956 -0
- package/dist/server/server/agent/agent-manager.js.map +1 -0
- package/dist/server/server/agent/agent-metadata-generator.d.ts +29 -0
- package/dist/server/server/agent/agent-metadata-generator.d.ts.map +1 -0
- package/dist/server/server/agent/agent-metadata-generator.js +161 -0
- package/dist/server/server/agent/agent-metadata-generator.js.map +1 -0
- package/dist/server/server/agent/agent-projections.d.ts +17 -0
- package/dist/server/server/agent/agent-projections.d.ts.map +1 -0
- package/dist/server/server/agent/agent-projections.js +280 -0
- package/dist/server/server/agent/agent-projections.js.map +1 -0
- package/dist/server/server/agent/agent-response-loop.d.ts +60 -0
- package/dist/server/server/agent/agent-response-loop.d.ts.map +1 -0
- package/dist/server/server/agent/agent-response-loop.js +304 -0
- package/dist/server/server/agent/agent-response-loop.js.map +1 -0
- package/dist/server/server/agent/agent-sdk-types.d.ts +470 -0
- package/dist/server/server/agent/agent-sdk-types.d.ts.map +1 -0
- package/dist/server/server/agent/agent-sdk-types.js +12 -0
- package/dist/server/server/agent/agent-sdk-types.js.map +1 -0
- package/dist/server/server/agent/agent-storage.d.ts +336 -0
- package/dist/server/server/agent/agent-storage.d.ts.map +1 -0
- package/dist/server/server/agent/agent-storage.js +304 -0
- package/dist/server/server/agent/agent-storage.js.map +1 -0
- package/dist/server/server/agent/agent-title-limits.d.ts +3 -0
- package/dist/server/server/agent/agent-title-limits.d.ts.map +1 -0
- package/dist/server/server/agent/agent-title-limits.js +3 -0
- package/dist/server/server/agent/agent-title-limits.js.map +1 -0
- package/dist/server/server/agent/audio-utils.d.ts +3 -0
- package/dist/server/server/agent/audio-utils.d.ts.map +1 -0
- package/dist/server/server/agent/audio-utils.js +19 -0
- package/dist/server/server/agent/audio-utils.js.map +1 -0
- package/dist/server/server/agent/dictation-debug.d.ts +13 -0
- package/dist/server/server/agent/dictation-debug.d.ts.map +1 -0
- package/dist/server/server/agent/dictation-debug.js +50 -0
- package/dist/server/server/agent/dictation-debug.js.map +1 -0
- package/dist/server/server/agent/llm-openai.d.ts +7 -0
- package/dist/server/server/agent/llm-openai.d.ts.map +1 -0
- package/dist/server/server/agent/llm-openai.js +8 -0
- package/dist/server/server/agent/llm-openai.js.map +1 -0
- package/dist/server/server/agent/mcp-server.d.ts +33 -0
- package/dist/server/server/agent/mcp-server.d.ts.map +1 -0
- package/dist/server/server/agent/mcp-server.js +1279 -0
- package/dist/server/server/agent/mcp-server.js.map +1 -0
- package/dist/server/server/agent/mcp-shared.d.ts +251 -0
- package/dist/server/server/agent/mcp-shared.d.ts.map +1 -0
- package/dist/server/server/agent/mcp-shared.js +242 -0
- package/dist/server/server/agent/mcp-shared.js.map +1 -0
- package/dist/server/server/agent/model-resolver.d.ts +11 -0
- package/dist/server/server/agent/model-resolver.d.ts.map +1 -0
- package/dist/server/server/agent/model-resolver.js +21 -0
- package/dist/server/server/agent/model-resolver.js.map +1 -0
- package/dist/server/server/agent/orchestrator-instructions.d.ts +7 -0
- package/dist/server/server/agent/orchestrator-instructions.d.ts.map +1 -0
- package/dist/server/server/agent/orchestrator-instructions.js +51 -0
- package/dist/server/server/agent/orchestrator-instructions.js.map +1 -0
- package/dist/server/server/agent/orchestrator.d.ts +12 -0
- package/dist/server/server/agent/orchestrator.d.ts.map +1 -0
- package/dist/server/server/agent/orchestrator.js +12 -0
- package/dist/server/server/agent/orchestrator.js.map +1 -0
- package/dist/server/server/agent/pcm16-resampler.d.ts +14 -0
- package/dist/server/server/agent/pcm16-resampler.d.ts.map +1 -0
- package/dist/server/server/agent/pcm16-resampler.js +63 -0
- package/dist/server/server/agent/pcm16-resampler.js.map +1 -0
- package/dist/server/server/agent/provider-launch-config.d.ts +138 -0
- package/dist/server/server/agent/provider-launch-config.d.ts.map +1 -0
- package/dist/server/server/agent/provider-launch-config.js +80 -0
- package/dist/server/server/agent/provider-launch-config.js.map +1 -0
- package/dist/server/server/agent/provider-manifest.d.ts +29 -0
- package/dist/server/server/agent/provider-manifest.d.ts.map +1 -0
- package/dist/server/server/agent/provider-manifest.js +157 -0
- package/dist/server/server/agent/provider-manifest.js.map +1 -0
- package/dist/server/server/agent/provider-registry.d.ts +19 -0
- package/dist/server/server/agent/provider-registry.d.ts.map +1 -0
- package/dist/server/server/agent/provider-registry.js +58 -0
- package/dist/server/server/agent/provider-registry.js.map +1 -0
- package/dist/server/server/agent/provider-snapshot-manager.d.ts +27 -0
- package/dist/server/server/agent/provider-snapshot-manager.d.ts.map +1 -0
- package/dist/server/server/agent/provider-snapshot-manager.js +181 -0
- package/dist/server/server/agent/provider-snapshot-manager.js.map +1 -0
- package/dist/server/server/agent/providers/acp-agent.d.ts +202 -0
- package/dist/server/server/agent/providers/acp-agent.d.ts.map +1 -0
- package/dist/server/server/agent/providers/acp-agent.js +1650 -0
- package/dist/server/server/agent/providers/acp-agent.js.map +1 -0
- package/dist/server/server/agent/providers/claude/claude-models.d.ts +8 -0
- package/dist/server/server/agent/providers/claude/claude-models.d.ts.map +1 -0
- package/dist/server/server/agent/providers/claude/claude-models.js +63 -0
- package/dist/server/server/agent/providers/claude/claude-models.js.map +1 -0
- package/dist/server/server/agent/providers/claude/partial-json.d.ts +5 -0
- package/dist/server/server/agent/providers/claude/partial-json.d.ts.map +1 -0
- package/dist/server/server/agent/providers/claude/partial-json.js +306 -0
- package/dist/server/server/agent/providers/claude/partial-json.js.map +1 -0
- package/dist/server/server/agent/providers/claude/sidechain-tracker.d.ts +20 -0
- package/dist/server/server/agent/providers/claude/sidechain-tracker.d.ts.map +1 -0
- package/dist/server/server/agent/providers/claude/sidechain-tracker.js +230 -0
- package/dist/server/server/agent/providers/claude/sidechain-tracker.js.map +1 -0
- package/dist/server/server/agent/providers/claude/task-notification-tool-call.d.ts +55 -0
- package/dist/server/server/agent/providers/claude/task-notification-tool-call.d.ts.map +1 -0
- package/dist/server/server/agent/providers/claude/task-notification-tool-call.js +267 -0
- package/dist/server/server/agent/providers/claude/task-notification-tool-call.js.map +1 -0
- package/dist/server/server/agent/providers/claude/tool-call-detail-parser.d.ts +3 -0
- package/dist/server/server/agent/providers/claude/tool-call-detail-parser.d.ts.map +1 -0
- package/dist/server/server/agent/providers/claude/tool-call-detail-parser.js +121 -0
- package/dist/server/server/agent/providers/claude/tool-call-detail-parser.js.map +1 -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.d.ts.map +1 -0
- package/dist/server/server/agent/providers/claude/tool-call-mapper.js +252 -0
- package/dist/server/server/agent/providers/claude/tool-call-mapper.js.map +1 -0
- package/dist/server/server/agent/providers/claude-agent.d.ts +44 -0
- package/dist/server/server/agent/providers/claude-agent.d.ts.map +1 -0
- package/dist/server/server/agent/providers/claude-agent.js +3452 -0
- package/dist/server/server/agent/providers/claude-agent.js.map +1 -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.d.ts.map +1 -0
- package/dist/server/server/agent/providers/codex/tool-call-detail-parser.js +104 -0
- package/dist/server/server/agent/providers/codex/tool-call-detail-parser.js.map +1 -0
- package/dist/server/server/agent/providers/codex/tool-call-mapper.d.ts +15 -0
- package/dist/server/server/agent/providers/codex/tool-call-mapper.d.ts.map +1 -0
- package/dist/server/server/agent/providers/codex/tool-call-mapper.js +762 -0
- package/dist/server/server/agent/providers/codex/tool-call-mapper.js.map +1 -0
- package/dist/server/server/agent/providers/codex-app-server-agent.d.ts +200 -0
- package/dist/server/server/agent/providers/codex-app-server-agent.d.ts.map +1 -0
- package/dist/server/server/agent/providers/codex-app-server-agent.js +3474 -0
- package/dist/server/server/agent/providers/codex-app-server-agent.js.map +1 -0
- package/dist/server/server/agent/providers/codex-feature-definitions.d.ts +11 -0
- package/dist/server/server/agent/providers/codex-feature-definitions.d.ts.map +1 -0
- package/dist/server/server/agent/providers/codex-feature-definitions.js +45 -0
- package/dist/server/server/agent/providers/codex-feature-definitions.js.map +1 -0
- package/dist/server/server/agent/providers/codex-rollout-timeline.d.ts +9 -0
- package/dist/server/server/agent/providers/codex-rollout-timeline.d.ts.map +1 -0
- package/dist/server/server/agent/providers/codex-rollout-timeline.js +544 -0
- package/dist/server/server/agent/providers/codex-rollout-timeline.js.map +1 -0
- package/dist/server/server/agent/providers/copilot-acp-agent.d.ts +16 -0
- package/dist/server/server/agent/providers/copilot-acp-agent.d.ts.map +1 -0
- package/dist/server/server/agent/providers/copilot-acp-agent.js +95 -0
- package/dist/server/server/agent/providers/copilot-acp-agent.js.map +1 -0
- package/dist/server/server/agent/providers/diagnostic-utils.d.ts +17 -0
- package/dist/server/server/agent/providers/diagnostic-utils.d.ts.map +1 -0
- package/dist/server/server/agent/providers/diagnostic-utils.js +56 -0
- package/dist/server/server/agent/providers/diagnostic-utils.js.map +1 -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.d.ts.map +1 -0
- package/dist/server/server/agent/providers/opencode/tool-call-detail-parser.js +39 -0
- package/dist/server/server/agent/providers/opencode/tool-call-detail-parser.js.map +1 -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.d.ts.map +1 -0
- package/dist/server/server/agent/providers/opencode/tool-call-mapper.js +144 -0
- package/dist/server/server/agent/providers/opencode/tool-call-mapper.js.map +1 -0
- package/dist/server/server/agent/providers/opencode-agent.d.ts +121 -0
- package/dist/server/server/agent/providers/opencode-agent.d.ts.map +1 -0
- package/dist/server/server/agent/providers/opencode-agent.js +1649 -0
- package/dist/server/server/agent/providers/opencode-agent.js.map +1 -0
- package/dist/server/server/agent/providers/pi-acp-agent.d.ts +28 -0
- package/dist/server/server/agent/providers/pi-acp-agent.d.ts.map +1 -0
- package/dist/server/server/agent/providers/pi-acp-agent.js +302 -0
- package/dist/server/server/agent/providers/pi-acp-agent.js.map +1 -0
- package/dist/server/server/agent/providers/test-utils/session-stream-adapter.d.ts +3 -0
- package/dist/server/server/agent/providers/test-utils/session-stream-adapter.d.ts.map +1 -0
- package/dist/server/server/agent/providers/test-utils/session-stream-adapter.js +57 -0
- package/dist/server/server/agent/providers/test-utils/session-stream-adapter.js.map +1 -0
- package/dist/server/server/agent/providers/tool-call-detail-primitives.d.ts +1745 -0
- package/dist/server/server/agent/providers/tool-call-detail-primitives.d.ts.map +1 -0
- package/dist/server/server/agent/providers/tool-call-detail-primitives.js +686 -0
- package/dist/server/server/agent/providers/tool-call-detail-primitives.js.map +1 -0
- package/dist/server/server/agent/providers/tool-call-mapper-utils.d.ts +18 -0
- package/dist/server/server/agent/providers/tool-call-mapper-utils.d.ts.map +1 -0
- package/dist/server/server/agent/providers/tool-call-mapper-utils.js +115 -0
- package/dist/server/server/agent/providers/tool-call-mapper-utils.js.map +1 -0
- package/dist/server/server/agent/recordings-debug.d.ts +3 -0
- package/dist/server/server/agent/recordings-debug.d.ts.map +1 -0
- package/dist/server/server/agent/recordings-debug.js +19 -0
- package/dist/server/server/agent/recordings-debug.js.map +1 -0
- package/dist/server/server/agent/stt-debug.d.ts +10 -0
- package/dist/server/server/agent/stt-debug.d.ts.map +1 -0
- package/dist/server/server/agent/stt-debug.js +33 -0
- package/dist/server/server/agent/stt-debug.js.map +1 -0
- package/dist/server/server/agent/stt-manager.d.ts +33 -0
- package/dist/server/server/agent/stt-manager.d.ts.map +1 -0
- package/dist/server/server/agent/stt-manager.js +232 -0
- package/dist/server/server/agent/stt-manager.js.map +1 -0
- package/dist/server/server/agent/timeline-append.d.ts +10 -0
- package/dist/server/server/agent/timeline-append.d.ts.map +1 -0
- package/dist/server/server/agent/timeline-append.js +27 -0
- package/dist/server/server/agent/timeline-append.js.map +1 -0
- package/dist/server/server/agent/timeline-projection.d.ts +39 -0
- package/dist/server/server/agent/timeline-projection.d.ts.map +1 -0
- package/dist/server/server/agent/timeline-projection.js +215 -0
- package/dist/server/server/agent/timeline-projection.js.map +1 -0
- package/dist/server/server/agent/tool-name-normalization.d.ts +9 -0
- package/dist/server/server/agent/tool-name-normalization.d.ts.map +1 -0
- package/dist/server/server/agent/tool-name-normalization.js +82 -0
- package/dist/server/server/agent/tool-name-normalization.js.map +1 -0
- package/dist/server/server/agent/tts-debug.d.ts +8 -0
- package/dist/server/server/agent/tts-debug.d.ts.map +1 -0
- package/dist/server/server/agent/tts-debug.js +24 -0
- package/dist/server/server/agent/tts-debug.js.map +1 -0
- package/dist/server/server/agent/tts-manager.d.ts +41 -0
- package/dist/server/server/agent/tts-manager.d.ts.map +1 -0
- package/dist/server/server/agent/tts-manager.js +374 -0
- package/dist/server/server/agent/tts-manager.js.map +1 -0
- package/dist/server/server/agent/wait-for-agent-tracker.d.ts +15 -0
- package/dist/server/server/agent/wait-for-agent-tracker.d.ts.map +1 -0
- package/dist/server/server/agent/wait-for-agent-tracker.js +53 -0
- package/dist/server/server/agent/wait-for-agent-tracker.js.map +1 -0
- package/dist/server/server/agent-attention-policy.d.ts +20 -0
- package/dist/server/server/agent-attention-policy.d.ts.map +1 -0
- package/dist/server/server/agent-attention-policy.js +40 -0
- package/dist/server/server/agent-attention-policy.js.map +1 -0
- package/dist/server/server/allowed-hosts.d.ts +13 -0
- package/dist/server/server/allowed-hosts.d.ts.map +1 -0
- package/dist/server/server/allowed-hosts.js +94 -0
- package/dist/server/server/allowed-hosts.js.map +1 -0
- package/dist/server/server/bootstrap.d.ts +74 -0
- package/dist/server/server/bootstrap.d.ts.map +1 -0
- package/dist/server/server/bootstrap.js +537 -0
- package/dist/server/server/bootstrap.js.map +1 -0
- package/dist/server/server/chat/chat-mentions.d.ts +31 -0
- package/dist/server/server/chat/chat-mentions.d.ts.map +1 -0
- package/dist/server/server/chat/chat-mentions.js +71 -0
- package/dist/server/server/chat/chat-mentions.js.map +1 -0
- package/dist/server/server/chat/chat-rpc-schemas.d.ts +728 -0
- package/dist/server/server/chat/chat-rpc-schemas.d.ts.map +1 -0
- package/dist/server/server/chat/chat-rpc-schemas.js +103 -0
- package/dist/server/server/chat/chat-rpc-schemas.js.map +1 -0
- package/dist/server/server/chat/chat-service.d.ts +74 -0
- package/dist/server/server/chat/chat-service.d.ts.map +1 -0
- package/dist/server/server/chat/chat-service.js +330 -0
- package/dist/server/server/chat/chat-service.js.map +1 -0
- package/dist/server/server/chat/chat-types.d.ts +75 -0
- package/dist/server/server/chat/chat-types.d.ts.map +1 -0
- package/dist/server/server/chat/chat-types.js +22 -0
- package/dist/server/server/chat/chat-types.js.map +1 -0
- package/dist/server/server/checkout-diff-manager.d.ts +45 -0
- package/dist/server/server/checkout-diff-manager.d.ts.map +1 -0
- package/dist/server/server/checkout-diff-manager.js +374 -0
- package/dist/server/server/checkout-diff-manager.js.map +1 -0
- package/dist/server/server/checkout-git-utils.d.ts +9 -0
- package/dist/server/server/checkout-git-utils.d.ts.map +1 -0
- package/dist/server/server/checkout-git-utils.js +37 -0
- package/dist/server/server/checkout-git-utils.js.map +1 -0
- package/dist/server/server/client-message-id.d.ts +3 -0
- package/dist/server/server/client-message-id.d.ts.map +1 -0
- package/dist/server/server/client-message-id.js +12 -0
- package/dist/server/server/client-message-id.js.map +1 -0
- package/dist/server/server/config.d.ts +14 -0
- package/dist/server/server/config.d.ts.map +1 -0
- package/dist/server/server/config.js +96 -0
- package/dist/server/server/config.js.map +1 -0
- package/dist/server/server/connection-offer.d.ts +19 -0
- package/dist/server/server/connection-offer.d.ts.map +1 -0
- package/dist/server/server/connection-offer.js +59 -0
- package/dist/server/server/connection-offer.js.map +1 -0
- package/dist/server/server/daemon-config-store.d.ts +23 -0
- package/dist/server/server/daemon-config-store.d.ts.map +1 -0
- package/dist/server/server/daemon-config-store.js +114 -0
- package/dist/server/server/daemon-config-store.js.map +1 -0
- package/dist/server/server/daemon-keypair.d.ts +8 -0
- package/dist/server/server/daemon-keypair.d.ts.map +1 -0
- package/dist/server/server/daemon-keypair.js +40 -0
- package/dist/server/server/daemon-keypair.js.map +1 -0
- package/dist/server/server/daemon-version.d.ts +5 -0
- package/dist/server/server/daemon-version.d.ts.map +1 -0
- package/dist/server/server/daemon-version.js +22 -0
- package/dist/server/server/daemon-version.js.map +1 -0
- package/dist/server/server/dictation/dictation-stream-manager.d.ts +85 -0
- package/dist/server/server/dictation/dictation-stream-manager.d.ts.map +1 -0
- package/dist/server/server/dictation/dictation-stream-manager.js +571 -0
- package/dist/server/server/dictation/dictation-stream-manager.js.map +1 -0
- package/dist/server/server/editor-targets.d.ts +18 -0
- package/dist/server/server/editor-targets.d.ts.map +1 -0
- package/dist/server/server/editor-targets.js +113 -0
- package/dist/server/server/editor-targets.js.map +1 -0
- package/dist/server/server/exports.d.ts +19 -0
- package/dist/server/server/exports.d.ts.map +1 -0
- package/dist/server/server/exports.js +21 -0
- package/dist/server/server/exports.js.map +1 -0
- package/dist/server/server/file-download/token-store.d.ts +24 -0
- package/dist/server/server/file-download/token-store.d.ts.map +1 -0
- package/dist/server/server/file-download/token-store.js +40 -0
- package/dist/server/server/file-download/token-store.js.map +1 -0
- package/dist/server/server/file-explorer/service.d.ts +41 -0
- package/dist/server/server/file-explorer/service.d.ts.map +1 -0
- package/dist/server/server/file-explorer/service.js +194 -0
- package/dist/server/server/file-explorer/service.js.map +1 -0
- package/dist/server/server/index.d.ts +2 -0
- package/dist/server/server/index.d.ts.map +1 -0
- package/dist/server/server/index.js +176 -0
- package/dist/server/server/index.js.map +1 -0
- package/dist/server/server/json-utils.d.ts +11 -0
- package/dist/server/server/json-utils.d.ts.map +1 -0
- package/dist/server/server/json-utils.js +45 -0
- package/dist/server/server/json-utils.js.map +1 -0
- package/dist/server/server/logger.d.ts +33 -0
- package/dist/server/server/logger.d.ts.map +1 -0
- package/dist/server/server/logger.js +195 -0
- package/dist/server/server/logger.js.map +1 -0
- package/dist/server/server/loop/rpc-schemas.d.ts +2937 -0
- package/dist/server/server/loop/rpc-schemas.d.ts.map +1 -0
- package/dist/server/server/loop/rpc-schemas.js +159 -0
- package/dist/server/server/loop/rpc-schemas.js.map +1 -0
- package/dist/server/server/loop-service.d.ts +520 -0
- package/dist/server/server/loop-service.d.ts.map +1 -0
- package/dist/server/server/loop-service.js +739 -0
- package/dist/server/server/loop-service.js.map +1 -0
- package/dist/server/server/messages.d.ts +9 -0
- package/dist/server/server/messages.d.ts.map +1 -0
- package/dist/server/server/messages.js +29 -0
- package/dist/server/server/messages.js.map +1 -0
- package/dist/server/server/package-version.d.ts +13 -0
- package/dist/server/server/package-version.d.ts.map +1 -0
- package/dist/server/server/package-version.js +46 -0
- package/dist/server/server/package-version.js.map +1 -0
- package/dist/server/server/pairing-offer.d.ts +16 -0
- package/dist/server/server/pairing-offer.d.ts.map +1 -0
- package/dist/server/server/pairing-offer.js +45 -0
- package/dist/server/server/pairing-offer.js.map +1 -0
- package/dist/server/server/pairing-qr.d.ts +7 -0
- package/dist/server/server/pairing-qr.d.ts.map +1 -0
- package/dist/server/server/pairing-qr.js +45 -0
- package/dist/server/server/pairing-qr.js.map +1 -0
- package/dist/server/server/path-utils.d.ts +3 -0
- package/dist/server/server/path-utils.d.ts.map +1 -0
- package/dist/server/server/path-utils.js +20 -0
- package/dist/server/server/path-utils.js.map +1 -0
- package/dist/server/server/persisted-config.d.ts +625 -0
- package/dist/server/server/persisted-config.d.ts.map +1 -0
- package/dist/server/server/persisted-config.js +265 -0
- package/dist/server/server/persisted-config.js.map +1 -0
- package/dist/server/server/persistence-hooks.d.ts +24 -0
- package/dist/server/server/persistence-hooks.d.ts.map +1 -0
- package/dist/server/server/persistence-hooks.js +60 -0
- package/dist/server/server/persistence-hooks.js.map +1 -0
- package/dist/server/server/pid-lock.d.ts +29 -0
- package/dist/server/server/pid-lock.d.ts.map +1 -0
- package/dist/server/server/pid-lock.js +148 -0
- package/dist/server/server/pid-lock.js.map +1 -0
- package/dist/server/server/push/push-service.d.ts +22 -0
- package/dist/server/server/push/push-service.d.ts.map +1 -0
- package/dist/server/server/push/push-service.js +69 -0
- package/dist/server/server/push/push-service.js.map +1 -0
- package/dist/server/server/push/token-store.d.ts +18 -0
- package/dist/server/server/push/token-store.d.ts.map +1 -0
- package/dist/server/server/push/token-store.js +70 -0
- package/dist/server/server/push/token-store.js.map +1 -0
- package/dist/server/server/relay-transport.d.ts +23 -0
- package/dist/server/server/relay-transport.d.ts.map +1 -0
- package/dist/server/server/relay-transport.js +461 -0
- package/dist/server/server/relay-transport.js.map +1 -0
- package/dist/server/server/schedule/cron.d.ts +4 -0
- package/dist/server/server/schedule/cron.d.ts.map +1 -0
- package/dist/server/server/schedule/cron.js +103 -0
- package/dist/server/server/schedule/cron.js.map +1 -0
- package/dist/server/server/schedule/rpc-schemas.d.ts +2773 -0
- package/dist/server/server/schedule/rpc-schemas.d.ts.map +1 -0
- package/dist/server/server/schedule/rpc-schemas.js +112 -0
- package/dist/server/server/schedule/rpc-schemas.js.map +1 -0
- package/dist/server/server/schedule/service.d.ts +39 -0
- package/dist/server/server/schedule/service.d.ts.map +1 -0
- package/dist/server/server/schedule/service.js +410 -0
- package/dist/server/server/schedule/service.js.map +1 -0
- package/dist/server/server/schedule/store.d.ts +13 -0
- package/dist/server/server/schedule/store.d.ts.map +1 -0
- package/dist/server/server/schedule/store.js +56 -0
- package/dist/server/server/schedule/store.js.map +1 -0
- package/dist/server/server/schedule/types.d.ts +710 -0
- package/dist/server/server/schedule/types.d.ts.map +1 -0
- package/dist/server/server/schedule/types.js +73 -0
- package/dist/server/server/schedule/types.js.map +1 -0
- package/dist/server/server/seawork-home.d.ts +2 -0
- package/dist/server/server/seawork-home.d.ts.map +1 -0
- package/dist/server/server/seawork-home.js +19 -0
- package/dist/server/server/seawork-home.js.map +1 -0
- package/dist/server/server/server-id.d.ts +17 -0
- package/dist/server/server/server-id.d.ts.map +1 -0
- package/dist/server/server/server-id.js +63 -0
- package/dist/server/server/server-id.js.map +1 -0
- package/dist/server/server/session.d.ts +510 -0
- package/dist/server/server/session.d.ts.map +1 -0
- package/dist/server/server/session.js +6414 -0
- package/dist/server/server/session.js.map +1 -0
- package/dist/server/server/speech/audio.d.ts +10 -0
- package/dist/server/server/speech/audio.d.ts.map +1 -0
- package/dist/server/server/speech/audio.js +101 -0
- package/dist/server/server/speech/audio.js.map +1 -0
- package/dist/server/server/speech/provider-resolver.d.ts +3 -0
- package/dist/server/server/speech/provider-resolver.d.ts.map +1 -0
- package/dist/server/server/speech/provider-resolver.js +7 -0
- package/dist/server/server/speech/provider-resolver.js.map +1 -0
- package/dist/server/server/speech/providers/local/config.d.ts +25 -0
- package/dist/server/server/speech/providers/local/config.d.ts.map +1 -0
- package/dist/server/server/speech/providers/local/config.js +74 -0
- package/dist/server/server/speech/providers/local/config.js.map +1 -0
- package/dist/server/server/speech/providers/local/models.d.ts +11 -0
- package/dist/server/server/speech/providers/local/models.d.ts.map +1 -0
- package/dist/server/server/speech/providers/local/models.js +17 -0
- package/dist/server/server/speech/providers/local/models.js.map +1 -0
- package/dist/server/server/speech/providers/local/pocket/pocket-tts-onnx.d.ts +24 -0
- package/dist/server/server/speech/providers/local/pocket/pocket-tts-onnx.d.ts.map +1 -0
- package/dist/server/server/speech/providers/local/pocket/pocket-tts-onnx.js +436 -0
- package/dist/server/server/speech/providers/local/pocket/pocket-tts-onnx.js.map +1 -0
- package/dist/server/server/speech/providers/local/runtime.d.ts +31 -0
- package/dist/server/server/speech/providers/local/runtime.d.ts.map +1 -0
- package/dist/server/server/speech/providers/local/runtime.js +247 -0
- package/dist/server/server/speech/providers/local/runtime.js.map +1 -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 +117 -0
- package/dist/server/server/speech/providers/local/sherpa/model-catalog.d.ts.map +1 -0
- package/dist/server/server/speech/providers/local/sherpa/model-catalog.js +166 -0
- package/dist/server/server/speech/providers/local/sherpa/model-catalog.js.map +1 -0
- package/dist/server/server/speech/providers/local/sherpa/model-downloader.d.ts +15 -0
- package/dist/server/server/speech/providers/local/sherpa/model-downloader.d.ts.map +1 -0
- package/dist/server/server/speech/providers/local/sherpa/model-downloader.js +165 -0
- package/dist/server/server/speech/providers/local/sherpa/model-downloader.js.map +1 -0
- package/dist/server/server/speech/providers/local/sherpa/sherpa-offline-recognizer.d.ts +28 -0
- package/dist/server/server/speech/providers/local/sherpa/sherpa-offline-recognizer.d.ts.map +1 -0
- package/dist/server/server/speech/providers/local/sherpa/sherpa-offline-recognizer.js +73 -0
- package/dist/server/server/speech/providers/local/sherpa/sherpa-offline-recognizer.js.map +1 -0
- package/dist/server/server/speech/providers/local/sherpa/sherpa-online-recognizer.d.ts +37 -0
- package/dist/server/server/speech/providers/local/sherpa/sherpa-online-recognizer.d.ts.map +1 -0
- package/dist/server/server/speech/providers/local/sherpa/sherpa-online-recognizer.js +84 -0
- package/dist/server/server/speech/providers/local/sherpa/sherpa-online-recognizer.js.map +1 -0
- package/dist/server/server/speech/providers/local/sherpa/sherpa-onnx-loader.d.ts +7 -0
- package/dist/server/server/speech/providers/local/sherpa/sherpa-onnx-loader.d.ts.map +1 -0
- package/dist/server/server/speech/providers/local/sherpa/sherpa-onnx-loader.js +11 -0
- package/dist/server/server/speech/providers/local/sherpa/sherpa-onnx-loader.js.map +1 -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.d.ts.map +1 -0
- package/dist/server/server/speech/providers/local/sherpa/sherpa-onnx-node-loader.js +102 -0
- package/dist/server/server/speech/providers/local/sherpa/sherpa-onnx-node-loader.js.map +1 -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.d.ts.map +1 -0
- package/dist/server/server/speech/providers/local/sherpa/sherpa-parakeet-realtime-session.js +135 -0
- package/dist/server/server/speech/providers/local/sherpa/sherpa-parakeet-realtime-session.js.map +1 -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.d.ts.map +1 -0
- package/dist/server/server/speech/providers/local/sherpa/sherpa-parakeet-stt.js +131 -0
- package/dist/server/server/speech/providers/local/sherpa/sherpa-parakeet-stt.js.map +1 -0
- package/dist/server/server/speech/providers/local/sherpa/sherpa-realtime-session.d.ts +23 -0
- package/dist/server/server/speech/providers/local/sherpa/sherpa-realtime-session.d.ts.map +1 -0
- package/dist/server/server/speech/providers/local/sherpa/sherpa-realtime-session.js +110 -0
- package/dist/server/server/speech/providers/local/sherpa/sherpa-realtime-session.js.map +1 -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.d.ts.map +1 -0
- package/dist/server/server/speech/providers/local/sherpa/sherpa-runtime-env.js +84 -0
- package/dist/server/server/speech/providers/local/sherpa/sherpa-runtime-env.js.map +1 -0
- package/dist/server/server/speech/providers/local/sherpa/sherpa-stt.d.ts +23 -0
- package/dist/server/server/speech/providers/local/sherpa/sherpa-stt.d.ts.map +1 -0
- package/dist/server/server/speech/providers/local/sherpa/sherpa-stt.js +138 -0
- package/dist/server/server/speech/providers/local/sherpa/sherpa-stt.js.map +1 -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.d.ts.map +1 -0
- package/dist/server/server/speech/providers/local/sherpa/sherpa-tts.js +108 -0
- package/dist/server/server/speech/providers/local/sherpa/sherpa-tts.js.map +1 -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.d.ts.map +1 -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-provider.js.map +1 -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.d.ts.map +1 -0
- package/dist/server/server/speech/providers/local/sherpa/silero-vad-session.js +176 -0
- package/dist/server/server/speech/providers/local/sherpa/silero-vad-session.js.map +1 -0
- package/dist/server/server/speech/providers/openai/config.d.ts +22 -0
- package/dist/server/server/speech/providers/openai/config.d.ts.map +1 -0
- package/dist/server/server/speech/providers/openai/config.js +80 -0
- package/dist/server/server/speech/providers/openai/config.js.map +1 -0
- package/dist/server/server/speech/providers/openai/realtime-transcription-session.d.ts +42 -0
- package/dist/server/server/speech/providers/openai/realtime-transcription-session.d.ts.map +1 -0
- package/dist/server/server/speech/providers/openai/realtime-transcription-session.js +168 -0
- package/dist/server/server/speech/providers/openai/realtime-transcription-session.js.map +1 -0
- package/dist/server/server/speech/providers/openai/runtime.d.ts +29 -0
- package/dist/server/server/speech/providers/openai/runtime.d.ts.map +1 -0
- package/dist/server/server/speech/providers/openai/runtime.js +112 -0
- package/dist/server/server/speech/providers/openai/runtime.js.map +1 -0
- package/dist/server/server/speech/providers/openai/stt.d.ts +22 -0
- package/dist/server/server/speech/providers/openai/stt.d.ts.map +1 -0
- package/dist/server/server/speech/providers/openai/stt.js +206 -0
- package/dist/server/server/speech/providers/openai/stt.js.map +1 -0
- package/dist/server/server/speech/providers/openai/tts.d.ts +18 -0
- package/dist/server/server/speech/providers/openai/tts.d.ts.map +1 -0
- package/dist/server/server/speech/providers/openai/tts.js +46 -0
- package/dist/server/server/speech/providers/openai/tts.js.map +1 -0
- package/dist/server/server/speech/speech-config-resolver.d.ts +11 -0
- package/dist/server/server/speech/speech-config-resolver.d.ts.map +1 -0
- package/dist/server/server/speech/speech-config-resolver.js +104 -0
- package/dist/server/server/speech/speech-config-resolver.js.map +1 -0
- package/dist/server/server/speech/speech-provider.d.ts +59 -0
- package/dist/server/server/speech/speech-provider.d.ts.map +1 -0
- package/dist/server/server/speech/speech-provider.js +2 -0
- package/dist/server/server/speech/speech-provider.js.map +1 -0
- package/dist/server/server/speech/speech-runtime.d.ts +43 -0
- package/dist/server/server/speech/speech-runtime.d.ts.map +1 -0
- package/dist/server/server/speech/speech-runtime.js +560 -0
- package/dist/server/server/speech/speech-runtime.js.map +1 -0
- package/dist/server/server/speech/speech-types.d.ts +24 -0
- package/dist/server/server/speech/speech-types.d.ts.map +1 -0
- package/dist/server/server/speech/speech-types.js +8 -0
- package/dist/server/server/speech/speech-types.js.map +1 -0
- package/dist/server/server/speech/turn-detection-provider.d.ts +23 -0
- package/dist/server/server/speech/turn-detection-provider.d.ts.map +1 -0
- package/dist/server/server/speech/turn-detection-provider.js +2 -0
- package/dist/server/server/speech/turn-detection-provider.js.map +1 -0
- package/dist/server/server/types.d.ts +5 -0
- package/dist/server/server/types.d.ts.map +1 -0
- package/dist/server/server/types.js +3 -0
- package/dist/server/server/types.js.map +1 -0
- package/dist/server/server/utils/diff-highlighter.d.ts +60 -0
- package/dist/server/server/utils/diff-highlighter.d.ts.map +1 -0
- package/dist/server/server/utils/diff-highlighter.js +257 -0
- package/dist/server/server/utils/diff-highlighter.js.map +1 -0
- package/dist/server/server/voice/fixed-duration-pcm-ring-buffer.d.ts +16 -0
- package/dist/server/server/voice/fixed-duration-pcm-ring-buffer.d.ts.map +1 -0
- package/dist/server/server/voice/fixed-duration-pcm-ring-buffer.js +35 -0
- package/dist/server/server/voice/fixed-duration-pcm-ring-buffer.js.map +1 -0
- package/dist/server/server/voice/voice-turn-controller.d.ts +34 -0
- package/dist/server/server/voice/voice-turn-controller.d.ts.map +1 -0
- package/dist/server/server/voice/voice-turn-controller.js +160 -0
- package/dist/server/server/voice/voice-turn-controller.js.map +1 -0
- package/dist/server/server/voice-config.d.ts +15 -0
- package/dist/server/server/voice-config.d.ts.map +1 -0
- package/dist/server/server/voice-config.js +54 -0
- package/dist/server/server/voice-config.js.map +1 -0
- package/dist/server/server/voice-permission-policy.d.ts +4 -0
- package/dist/server/server/voice-permission-policy.d.ts.map +1 -0
- package/dist/server/server/voice-permission-policy.js +13 -0
- package/dist/server/server/voice-permission-policy.js.map +1 -0
- package/dist/server/server/voice-types.d.ts +12 -0
- package/dist/server/server/voice-types.d.ts.map +1 -0
- package/dist/server/server/voice-types.js +2 -0
- package/dist/server/server/voice-types.js.map +1 -0
- package/dist/server/server/websocket-server.d.ts +122 -0
- package/dist/server/server/websocket-server.d.ts.map +1 -0
- package/dist/server/server/websocket-server.js +1092 -0
- package/dist/server/server/websocket-server.js.map +1 -0
- package/dist/server/server/workspace-git-service.d.ts +108 -0
- package/dist/server/server/workspace-git-service.d.ts.map +1 -0
- package/dist/server/server/workspace-git-service.js +404 -0
- package/dist/server/server/workspace-git-service.js.map +1 -0
- package/dist/server/server/workspace-registry-bootstrap.d.ts +11 -0
- package/dist/server/server/workspace-registry-bootstrap.d.ts.map +1 -0
- package/dist/server/server/workspace-registry-bootstrap.js +100 -0
- package/dist/server/server/workspace-registry-bootstrap.js.map +1 -0
- package/dist/server/server/workspace-registry-model.d.ts +33 -0
- package/dist/server/server/workspace-registry-model.d.ts.map +1 -0
- package/dist/server/server/workspace-registry-model.js +167 -0
- package/dist/server/server/workspace-registry-model.js.map +1 -0
- package/dist/server/server/workspace-registry.d.ts +130 -0
- package/dist/server/server/workspace-registry.d.ts.map +1 -0
- package/dist/server/server/workspace-registry.js +151 -0
- package/dist/server/server/workspace-registry.js.map +1 -0
- package/dist/server/server/worktree-bootstrap.d.ts +29 -0
- package/dist/server/server/worktree-bootstrap.d.ts.map +1 -0
- package/dist/server/server/worktree-bootstrap.js +508 -0
- package/dist/server/server/worktree-bootstrap.js.map +1 -0
- package/dist/server/server/worktree-session.d.ts +131 -0
- package/dist/server/server/worktree-session.d.ts.map +1 -0
- package/dist/server/server/worktree-session.js +487 -0
- package/dist/server/server/worktree-session.js.map +1 -0
- package/dist/server/shared/agent-attention-notification.d.ts +40 -0
- package/dist/server/shared/agent-attention-notification.d.ts.map +1 -0
- package/dist/server/shared/agent-attention-notification.js +130 -0
- package/dist/server/shared/agent-attention-notification.js.map +1 -0
- package/dist/server/shared/agent-lifecycle.d.ts +3 -0
- package/dist/server/shared/agent-lifecycle.d.ts.map +1 -0
- package/dist/server/shared/agent-lifecycle.js +8 -0
- package/dist/server/shared/agent-lifecycle.js.map +1 -0
- package/dist/server/shared/connection-offer.d.ts +62 -0
- package/dist/server/shared/connection-offer.d.ts.map +1 -0
- package/dist/server/shared/connection-offer.js +17 -0
- package/dist/server/shared/connection-offer.js.map +1 -0
- package/dist/server/shared/daemon-endpoints.d.ts +28 -0
- package/dist/server/shared/daemon-endpoints.d.ts.map +1 -0
- package/dist/server/shared/daemon-endpoints.js +122 -0
- package/dist/server/shared/daemon-endpoints.js.map +1 -0
- package/dist/server/shared/literal-union.d.ts +2 -0
- package/dist/server/shared/literal-union.d.ts.map +1 -0
- package/dist/server/shared/literal-union.js +2 -0
- package/dist/server/shared/literal-union.js.map +1 -0
- package/dist/server/shared/messages.d.ts +81816 -0
- package/dist/server/shared/messages.d.ts.map +1 -0
- package/dist/server/shared/messages.js +2553 -0
- package/dist/server/shared/messages.js.map +1 -0
- package/dist/server/shared/path-utils.d.ts +2 -0
- package/dist/server/shared/path-utils.d.ts.map +1 -0
- package/dist/server/shared/path-utils.js +16 -0
- package/dist/server/shared/path-utils.js.map +1 -0
- package/dist/server/shared/terminal-stream-protocol.d.ts +36 -0
- package/dist/server/shared/terminal-stream-protocol.d.ts.map +1 -0
- package/dist/server/shared/terminal-stream-protocol.js +99 -0
- package/dist/server/shared/terminal-stream-protocol.js.map +1 -0
- package/dist/server/shared/tool-call-display.d.ts +11 -0
- package/dist/server/shared/tool-call-display.d.ts.map +1 -0
- package/dist/server/shared/tool-call-display.js +133 -0
- package/dist/server/shared/tool-call-display.js.map +1 -0
- package/dist/server/terminal/terminal-manager.d.ts +30 -0
- package/dist/server/terminal/terminal-manager.d.ts.map +1 -0
- package/dist/server/terminal/terminal-manager.js +136 -0
- package/dist/server/terminal/terminal-manager.js.map +1 -0
- package/dist/server/terminal/terminal.d.ts +68 -0
- package/dist/server/terminal/terminal.d.ts.map +1 -0
- package/dist/server/terminal/terminal.js +384 -0
- package/dist/server/terminal/terminal.js.map +1 -0
- package/dist/server/utils/checkout-git.d.ts +180 -0
- package/dist/server/utils/checkout-git.d.ts.map +1 -0
- package/dist/server/utils/checkout-git.js +1621 -0
- package/dist/server/utils/checkout-git.js.map +1 -0
- package/dist/server/utils/directory-suggestions.d.ts +24 -0
- package/dist/server/utils/directory-suggestions.d.ts.map +1 -0
- package/dist/server/utils/directory-suggestions.js +671 -0
- package/dist/server/utils/directory-suggestions.js.map +1 -0
- package/dist/server/utils/executable.d.ts +33 -0
- package/dist/server/utils/executable.d.ts.map +1 -0
- package/dist/server/utils/executable.js +134 -0
- package/dist/server/utils/executable.js.map +1 -0
- package/dist/server/utils/path.d.ts +5 -0
- package/dist/server/utils/path.d.ts.map +1 -0
- package/dist/server/utils/path.js +15 -0
- package/dist/server/utils/path.js.map +1 -0
- package/dist/server/utils/project-icon.d.ts +39 -0
- package/dist/server/utils/project-icon.d.ts.map +1 -0
- package/dist/server/utils/project-icon.js +389 -0
- package/dist/server/utils/project-icon.js.map +1 -0
- package/dist/server/utils/spawn.d.ts +13 -0
- package/dist/server/utils/spawn.d.ts.map +1 -0
- package/dist/server/utils/spawn.js +33 -0
- package/dist/server/utils/spawn.js.map +1 -0
- package/dist/server/utils/worktree-metadata.d.ts +47 -0
- package/dist/server/utils/worktree-metadata.d.ts.map +1 -0
- package/dist/server/utils/worktree-metadata.js +116 -0
- package/dist/server/utils/worktree-metadata.js.map +1 -0
- package/dist/server/utils/worktree.d.ts +144 -0
- package/dist/server/utils/worktree.d.ts.map +1 -0
- package/dist/server/utils/worktree.js +745 -0
- package/dist/server/utils/worktree.js.map +1 -0
- package/dist/src/server/pid-lock.js +148 -0
- package/dist/src/server/pid-lock.js.map +1 -0
- package/dist/src/server/seawork-home.js +19 -0
- package/dist/src/server/seawork-home.js.map +1 -0
- package/dist/src/server/speech/providers/local/sherpa/sherpa-runtime-env.js +84 -0
- package/dist/src/server/speech/providers/local/sherpa/sherpa-runtime-env.js.map +1 -0
- package/package.json +117 -0
- package/src/server/speech/providers/local/sherpa/assets/silero_vad.onnx +0 -0
|
@@ -0,0 +1,1956 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import { stat } from "node:fs/promises";
|
|
4
|
+
import { AGENT_LIFECYCLE_STATUSES, } from "../../shared/agent-lifecycle.js";
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
import { AGENT_PROVIDER_IDS, getAgentProviderDefinition } from "./provider-manifest.js";
|
|
7
|
+
export { AGENT_LIFECYCLE_STATUSES };
|
|
8
|
+
const SYSTEM_ERROR_PREFIX = "[System Error]";
|
|
9
|
+
function attachPersistenceCwd(handle, cwd) {
|
|
10
|
+
if (!handle) {
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
return {
|
|
14
|
+
...handle,
|
|
15
|
+
metadata: {
|
|
16
|
+
...(handle.metadata ?? {}),
|
|
17
|
+
cwd,
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
const DEFAULT_TIMELINE_FETCH_LIMIT = 200;
|
|
22
|
+
const BUSY_STATUSES = ["initializing", "running"];
|
|
23
|
+
const AgentIdSchema = z.string().uuid();
|
|
24
|
+
function isAgentBusy(status) {
|
|
25
|
+
return BUSY_STATUSES.includes(status);
|
|
26
|
+
}
|
|
27
|
+
function isTurnTerminalEvent(event) {
|
|
28
|
+
return (event.type === "turn_completed" ||
|
|
29
|
+
event.type === "turn_failed" ||
|
|
30
|
+
event.type === "turn_canceled");
|
|
31
|
+
}
|
|
32
|
+
function createAbortError(signal, fallbackMessage) {
|
|
33
|
+
const reason = signal?.reason;
|
|
34
|
+
const message = typeof reason === "string"
|
|
35
|
+
? reason
|
|
36
|
+
: reason instanceof Error
|
|
37
|
+
? reason.message
|
|
38
|
+
: fallbackMessage;
|
|
39
|
+
return Object.assign(new Error(message), { name: "AbortError" });
|
|
40
|
+
}
|
|
41
|
+
function validateAgentId(agentId, source) {
|
|
42
|
+
const result = AgentIdSchema.safeParse(agentId);
|
|
43
|
+
if (!result.success) {
|
|
44
|
+
throw new Error(`${source}: agentId must be a UUID`);
|
|
45
|
+
}
|
|
46
|
+
return result.data;
|
|
47
|
+
}
|
|
48
|
+
function normalizeMessageId(messageId) {
|
|
49
|
+
if (typeof messageId !== "string") {
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
const trimmed = messageId.trim();
|
|
53
|
+
return trimmed.length > 0 ? trimmed : undefined;
|
|
54
|
+
}
|
|
55
|
+
export class AgentManager {
|
|
56
|
+
constructor(options) {
|
|
57
|
+
this.clients = new Map();
|
|
58
|
+
this.agents = new Map();
|
|
59
|
+
this.pendingForegroundRuns = new Map();
|
|
60
|
+
this.subscribers = new Set();
|
|
61
|
+
this.previousStatuses = new Map();
|
|
62
|
+
this.backgroundTasks = new Set();
|
|
63
|
+
const maxTimelineItems = options?.maxTimelineItems;
|
|
64
|
+
this.maxTimelineItems =
|
|
65
|
+
typeof maxTimelineItems === "number" &&
|
|
66
|
+
Number.isFinite(maxTimelineItems) &&
|
|
67
|
+
maxTimelineItems >= 0
|
|
68
|
+
? Math.floor(maxTimelineItems)
|
|
69
|
+
: null;
|
|
70
|
+
this.idFactory = options?.idFactory ?? (() => randomUUID());
|
|
71
|
+
this.registry = options?.registry;
|
|
72
|
+
this.onAgentAttention = options?.onAgentAttention;
|
|
73
|
+
this.mcpBaseUrl = options?.mcpBaseUrl ?? null;
|
|
74
|
+
this.logger = options.logger.child({ module: "agent", component: "agent-manager" });
|
|
75
|
+
if (options?.clients) {
|
|
76
|
+
for (const [provider, client] of Object.entries(options.clients)) {
|
|
77
|
+
if (client) {
|
|
78
|
+
this.registerClient(provider, client);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
registerClient(provider, client) {
|
|
84
|
+
this.clients.set(provider, client);
|
|
85
|
+
}
|
|
86
|
+
setAgentAttentionCallback(callback) {
|
|
87
|
+
this.onAgentAttention = callback;
|
|
88
|
+
}
|
|
89
|
+
setMcpBaseUrl(url) {
|
|
90
|
+
this.mcpBaseUrl = url;
|
|
91
|
+
}
|
|
92
|
+
getMetricsSnapshot() {
|
|
93
|
+
const byLifecycle = {};
|
|
94
|
+
let withActiveForegroundTurn = 0;
|
|
95
|
+
let totalItems = 0;
|
|
96
|
+
let maxItemsPerAgent = 0;
|
|
97
|
+
for (const agent of this.agents.values()) {
|
|
98
|
+
byLifecycle[agent.lifecycle] = (byLifecycle[agent.lifecycle] ?? 0) + 1;
|
|
99
|
+
if (agent.activeForegroundTurnId !== null) {
|
|
100
|
+
withActiveForegroundTurn++;
|
|
101
|
+
}
|
|
102
|
+
const len = agent.timeline.length;
|
|
103
|
+
totalItems += len;
|
|
104
|
+
if (len > maxItemsPerAgent) {
|
|
105
|
+
maxItemsPerAgent = len;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return {
|
|
109
|
+
total: this.agents.size,
|
|
110
|
+
byLifecycle,
|
|
111
|
+
withActiveForegroundTurn,
|
|
112
|
+
timelineStats: {
|
|
113
|
+
totalItems,
|
|
114
|
+
maxItemsPerAgent,
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
touchUpdatedAt(agent) {
|
|
119
|
+
const nowMs = Date.now();
|
|
120
|
+
const previousMs = agent.updatedAt.getTime();
|
|
121
|
+
const nextMs = nowMs > previousMs ? nowMs : previousMs + 1;
|
|
122
|
+
const next = new Date(nextMs);
|
|
123
|
+
agent.updatedAt = next;
|
|
124
|
+
return next;
|
|
125
|
+
}
|
|
126
|
+
hasInFlightRun(agentId) {
|
|
127
|
+
const agent = this.agents.get(agentId);
|
|
128
|
+
if (!agent) {
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
return (agent.lifecycle === "running" ||
|
|
132
|
+
Boolean(agent.activeForegroundTurnId) ||
|
|
133
|
+
this.hasPendingForegroundRun(agentId));
|
|
134
|
+
}
|
|
135
|
+
subscribe(callback, options) {
|
|
136
|
+
const targetAgentId = options?.agentId == null ? null : validateAgentId(options.agentId, "subscribe");
|
|
137
|
+
const record = {
|
|
138
|
+
callback,
|
|
139
|
+
agentId: targetAgentId,
|
|
140
|
+
};
|
|
141
|
+
this.subscribers.add(record);
|
|
142
|
+
if (options?.replayState !== false) {
|
|
143
|
+
if (record.agentId) {
|
|
144
|
+
const agent = this.agents.get(record.agentId);
|
|
145
|
+
if (agent) {
|
|
146
|
+
callback({
|
|
147
|
+
type: "agent_state",
|
|
148
|
+
agent: { ...agent },
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
// For global subscribers, skip internal agents during replay
|
|
154
|
+
for (const agent of this.agents.values()) {
|
|
155
|
+
if (agent.internal) {
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
callback({
|
|
159
|
+
type: "agent_state",
|
|
160
|
+
agent: { ...agent },
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return () => {
|
|
166
|
+
this.subscribers.delete(record);
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
listAgents() {
|
|
170
|
+
return Array.from(this.agents.values())
|
|
171
|
+
.filter((agent) => !agent.internal)
|
|
172
|
+
.map((agent) => ({
|
|
173
|
+
...agent,
|
|
174
|
+
}));
|
|
175
|
+
}
|
|
176
|
+
async listPersistedAgents(options) {
|
|
177
|
+
if (options?.provider) {
|
|
178
|
+
const client = this.requireClient(options.provider);
|
|
179
|
+
if (!client.listPersistedAgents) {
|
|
180
|
+
return [];
|
|
181
|
+
}
|
|
182
|
+
return client.listPersistedAgents({ limit: options.limit });
|
|
183
|
+
}
|
|
184
|
+
const descriptors = [];
|
|
185
|
+
for (const [provider, client] of this.clients.entries()) {
|
|
186
|
+
if (!client.listPersistedAgents) {
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
try {
|
|
190
|
+
const entries = await client.listPersistedAgents({
|
|
191
|
+
limit: options?.limit,
|
|
192
|
+
});
|
|
193
|
+
descriptors.push(...entries);
|
|
194
|
+
}
|
|
195
|
+
catch (error) {
|
|
196
|
+
this.logger.warn({ err: error, provider }, "Failed to list persisted agents for provider");
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
const limit = options?.limit ?? 20;
|
|
200
|
+
return descriptors
|
|
201
|
+
.sort((a, b) => b.lastActivityAt.getTime() - a.lastActivityAt.getTime())
|
|
202
|
+
.slice(0, limit);
|
|
203
|
+
}
|
|
204
|
+
async listProviderAvailability() {
|
|
205
|
+
const checks = AGENT_PROVIDER_IDS.map(async (providerId) => {
|
|
206
|
+
const provider = providerId;
|
|
207
|
+
const client = this.clients.get(provider);
|
|
208
|
+
if (!client) {
|
|
209
|
+
return {
|
|
210
|
+
provider,
|
|
211
|
+
available: false,
|
|
212
|
+
error: `No client registered for provider '${provider}'`,
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
try {
|
|
216
|
+
const available = await client.isAvailable();
|
|
217
|
+
return {
|
|
218
|
+
provider,
|
|
219
|
+
available,
|
|
220
|
+
error: null,
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
catch (error) {
|
|
224
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
225
|
+
this.logger.warn({ err: error, provider }, "Failed to check provider availability");
|
|
226
|
+
return {
|
|
227
|
+
provider,
|
|
228
|
+
available: false,
|
|
229
|
+
error: message,
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
return Promise.all(checks);
|
|
234
|
+
}
|
|
235
|
+
async listDraftCommands(config) {
|
|
236
|
+
const normalizedConfig = await this.normalizeConfig(config);
|
|
237
|
+
const client = this.requireClient(normalizedConfig.provider);
|
|
238
|
+
const available = await client.isAvailable();
|
|
239
|
+
if (!available) {
|
|
240
|
+
throw new Error(`Provider '${normalizedConfig.provider}' is not available. Please ensure the CLI is installed.`);
|
|
241
|
+
}
|
|
242
|
+
const session = await client.createSession(normalizedConfig);
|
|
243
|
+
try {
|
|
244
|
+
if (!session.listCommands) {
|
|
245
|
+
throw new Error(`Provider '${normalizedConfig.provider}' does not support listing commands`);
|
|
246
|
+
}
|
|
247
|
+
return await session.listCommands();
|
|
248
|
+
}
|
|
249
|
+
finally {
|
|
250
|
+
try {
|
|
251
|
+
await session.close();
|
|
252
|
+
}
|
|
253
|
+
catch (error) {
|
|
254
|
+
this.logger.warn({ err: error, provider: normalizedConfig.provider }, "Failed to close draft command listing session");
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
async listDraftFeatures(config) {
|
|
259
|
+
const normalizedConfig = await this.normalizeConfig(config);
|
|
260
|
+
const client = this.requireClient(normalizedConfig.provider);
|
|
261
|
+
const available = await client.isAvailable();
|
|
262
|
+
if (!available) {
|
|
263
|
+
throw new Error(`Provider '${normalizedConfig.provider}' is not available. Please ensure the CLI is installed.`);
|
|
264
|
+
}
|
|
265
|
+
const session = await client.createSession(normalizedConfig);
|
|
266
|
+
try {
|
|
267
|
+
return session.features ?? [];
|
|
268
|
+
}
|
|
269
|
+
finally {
|
|
270
|
+
try {
|
|
271
|
+
await session.close();
|
|
272
|
+
}
|
|
273
|
+
catch (error) {
|
|
274
|
+
this.logger.warn({ err: error, provider: normalizedConfig.provider }, "Failed to close draft feature listing session");
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
getAgent(id) {
|
|
279
|
+
const agent = this.agents.get(id);
|
|
280
|
+
return agent ? { ...agent } : null;
|
|
281
|
+
}
|
|
282
|
+
getTimeline(id) {
|
|
283
|
+
const agent = this.requireAgent(id);
|
|
284
|
+
return [...agent.timeline];
|
|
285
|
+
}
|
|
286
|
+
getTimelineRows(id) {
|
|
287
|
+
const agent = this.requireAgent(id);
|
|
288
|
+
const { rows } = this.ensureTimelineState(agent);
|
|
289
|
+
return rows.map((row) => ({ ...row }));
|
|
290
|
+
}
|
|
291
|
+
fetchTimeline(id, options) {
|
|
292
|
+
const agent = this.requireAgent(id);
|
|
293
|
+
const { rows, epoch, nextSeq, minSeq, maxSeq } = this.ensureTimelineState(agent);
|
|
294
|
+
const direction = options?.direction ?? "tail";
|
|
295
|
+
const requestedLimit = options?.limit;
|
|
296
|
+
const limit = requestedLimit === undefined
|
|
297
|
+
? DEFAULT_TIMELINE_FETCH_LIMIT
|
|
298
|
+
: Math.max(0, Math.floor(requestedLimit));
|
|
299
|
+
const cursor = options?.cursor;
|
|
300
|
+
const window = { minSeq, maxSeq, nextSeq };
|
|
301
|
+
if (cursor && cursor.epoch !== epoch) {
|
|
302
|
+
return {
|
|
303
|
+
epoch,
|
|
304
|
+
direction,
|
|
305
|
+
reset: true,
|
|
306
|
+
staleCursor: true,
|
|
307
|
+
gap: false,
|
|
308
|
+
window,
|
|
309
|
+
hasOlder: false,
|
|
310
|
+
hasNewer: false,
|
|
311
|
+
rows: rows.map((row) => ({ ...row })),
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
const selectAll = limit === 0;
|
|
315
|
+
const cloneRows = (items) => items.map((row) => ({ ...row }));
|
|
316
|
+
if (direction === "after" && cursor && rows.length > 0 && cursor.seq < minSeq - 1) {
|
|
317
|
+
return {
|
|
318
|
+
epoch,
|
|
319
|
+
direction,
|
|
320
|
+
reset: true,
|
|
321
|
+
staleCursor: false,
|
|
322
|
+
gap: true,
|
|
323
|
+
window,
|
|
324
|
+
hasOlder: false,
|
|
325
|
+
hasNewer: false,
|
|
326
|
+
rows: cloneRows(rows),
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
if (rows.length === 0) {
|
|
330
|
+
return {
|
|
331
|
+
epoch,
|
|
332
|
+
direction,
|
|
333
|
+
reset: false,
|
|
334
|
+
staleCursor: false,
|
|
335
|
+
gap: false,
|
|
336
|
+
window,
|
|
337
|
+
hasOlder: false,
|
|
338
|
+
hasNewer: false,
|
|
339
|
+
rows: [],
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
if (direction === "tail") {
|
|
343
|
+
const selected = selectAll || limit >= rows.length ? rows : rows.slice(rows.length - limit);
|
|
344
|
+
const hasOlder = selected.length > 0 && selected[0].seq > minSeq;
|
|
345
|
+
return {
|
|
346
|
+
epoch,
|
|
347
|
+
direction,
|
|
348
|
+
reset: false,
|
|
349
|
+
staleCursor: false,
|
|
350
|
+
gap: false,
|
|
351
|
+
window,
|
|
352
|
+
hasOlder,
|
|
353
|
+
hasNewer: false,
|
|
354
|
+
rows: cloneRows(selected),
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
if (direction === "after") {
|
|
358
|
+
const baseSeq = cursor?.seq ?? 0;
|
|
359
|
+
const startIdx = rows.findIndex((row) => row.seq > baseSeq);
|
|
360
|
+
if (startIdx < 0) {
|
|
361
|
+
return {
|
|
362
|
+
epoch,
|
|
363
|
+
direction,
|
|
364
|
+
reset: false,
|
|
365
|
+
staleCursor: false,
|
|
366
|
+
gap: false,
|
|
367
|
+
window,
|
|
368
|
+
hasOlder: baseSeq >= minSeq,
|
|
369
|
+
hasNewer: false,
|
|
370
|
+
rows: [],
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
const selected = selectAll ? rows.slice(startIdx) : rows.slice(startIdx, startIdx + limit);
|
|
374
|
+
const lastSelected = selected[selected.length - 1];
|
|
375
|
+
return {
|
|
376
|
+
epoch,
|
|
377
|
+
direction,
|
|
378
|
+
reset: false,
|
|
379
|
+
staleCursor: false,
|
|
380
|
+
gap: false,
|
|
381
|
+
window,
|
|
382
|
+
hasOlder: selected[0].seq > minSeq,
|
|
383
|
+
hasNewer: Boolean(lastSelected && lastSelected.seq < maxSeq),
|
|
384
|
+
rows: cloneRows(selected),
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
// direction === "before"
|
|
388
|
+
const beforeSeq = cursor?.seq ?? nextSeq;
|
|
389
|
+
const endExclusive = rows.findIndex((row) => row.seq >= beforeSeq);
|
|
390
|
+
const boundedRows = endExclusive < 0 ? rows : rows.slice(0, endExclusive);
|
|
391
|
+
const selected = selectAll || limit >= boundedRows.length
|
|
392
|
+
? boundedRows
|
|
393
|
+
: boundedRows.slice(boundedRows.length - limit);
|
|
394
|
+
const hasOlder = selected.length > 0 && selected[0].seq > minSeq;
|
|
395
|
+
const hasNewer = endExclusive >= 0;
|
|
396
|
+
return {
|
|
397
|
+
epoch,
|
|
398
|
+
direction,
|
|
399
|
+
reset: false,
|
|
400
|
+
staleCursor: false,
|
|
401
|
+
gap: false,
|
|
402
|
+
window,
|
|
403
|
+
hasOlder,
|
|
404
|
+
hasNewer,
|
|
405
|
+
rows: cloneRows(selected),
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
async createAgent(config, agentId, options) {
|
|
409
|
+
const resolvedAgentId = validateAgentId(agentId ?? this.idFactory(), "createAgent");
|
|
410
|
+
const injectedConfig = this.mcpBaseUrl == null
|
|
411
|
+
? config
|
|
412
|
+
: {
|
|
413
|
+
...config,
|
|
414
|
+
mcpServers: {
|
|
415
|
+
seawork: {
|
|
416
|
+
type: "http",
|
|
417
|
+
url: `${this.mcpBaseUrl}?callerAgentId=${resolvedAgentId}`,
|
|
418
|
+
},
|
|
419
|
+
...(config.mcpServers ?? {}),
|
|
420
|
+
},
|
|
421
|
+
};
|
|
422
|
+
const normalizedConfig = await this.normalizeConfig(injectedConfig);
|
|
423
|
+
const launchContext = this.buildLaunchContext(resolvedAgentId);
|
|
424
|
+
const client = this.requireClient(normalizedConfig.provider);
|
|
425
|
+
const available = await client.isAvailable();
|
|
426
|
+
if (!available) {
|
|
427
|
+
throw new Error(`Provider '${normalizedConfig.provider}' is not available. Please ensure the CLI is installed.`);
|
|
428
|
+
}
|
|
429
|
+
const session = await client.createSession(normalizedConfig, launchContext);
|
|
430
|
+
return this.registerSession(session, normalizedConfig, resolvedAgentId, {
|
|
431
|
+
labels: options?.labels,
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
// Reconstruct an agent from provider persistence. Callers should explicitly
|
|
435
|
+
// hydrate timeline history after resume.
|
|
436
|
+
async resumeAgentFromPersistence(handle, overrides, agentId, options) {
|
|
437
|
+
const resolvedAgentId = validateAgentId(agentId ?? this.idFactory(), "resumeAgentFromPersistence");
|
|
438
|
+
const metadata = (handle.metadata ?? {});
|
|
439
|
+
const mergedConfig = {
|
|
440
|
+
...metadata,
|
|
441
|
+
...overrides,
|
|
442
|
+
provider: handle.provider,
|
|
443
|
+
};
|
|
444
|
+
const normalizedConfig = await this.normalizeConfig(mergedConfig);
|
|
445
|
+
const resumeOverrides = normalizedConfig.model !== mergedConfig.model
|
|
446
|
+
? { ...overrides, model: normalizedConfig.model }
|
|
447
|
+
: overrides;
|
|
448
|
+
const launchContext = this.buildLaunchContext(resolvedAgentId);
|
|
449
|
+
const client = this.requireClient(handle.provider);
|
|
450
|
+
const session = await client.resumeSession(handle, resumeOverrides, launchContext);
|
|
451
|
+
return this.registerSession(session, normalizedConfig, resolvedAgentId, options);
|
|
452
|
+
}
|
|
453
|
+
// Hot-reload an active agent session with config overrides while preserving
|
|
454
|
+
// in-memory timeline state.
|
|
455
|
+
async reloadAgentSession(agentId, overrides) {
|
|
456
|
+
let existing = this.requireAgent(agentId);
|
|
457
|
+
if (this.hasInFlightRun(agentId)) {
|
|
458
|
+
await this.cancelAgentRun(agentId);
|
|
459
|
+
existing = this.requireAgent(agentId);
|
|
460
|
+
}
|
|
461
|
+
const timelineState = this.ensureTimelineState(existing);
|
|
462
|
+
const preservedTimeline = [...existing.timeline];
|
|
463
|
+
const preservedTimelineRows = timelineState.rows.map((row) => ({ ...row }));
|
|
464
|
+
const preservedTimelineEpoch = timelineState.epoch;
|
|
465
|
+
const preservedTimelineNextSeq = timelineState.nextSeq;
|
|
466
|
+
const preservedHistoryPrimed = existing.historyPrimed;
|
|
467
|
+
const preservedLastUsage = existing.lastUsage;
|
|
468
|
+
const preservedLastError = existing.lastError;
|
|
469
|
+
const preservedAttention = existing.attention;
|
|
470
|
+
const handle = existing.persistence;
|
|
471
|
+
const provider = handle?.provider ?? existing.provider;
|
|
472
|
+
const client = this.requireClient(provider);
|
|
473
|
+
const refreshConfig = {
|
|
474
|
+
...existing.config,
|
|
475
|
+
...overrides,
|
|
476
|
+
provider,
|
|
477
|
+
};
|
|
478
|
+
const normalizedConfig = await this.normalizeConfig(refreshConfig);
|
|
479
|
+
const launchContext = this.buildLaunchContext(agentId);
|
|
480
|
+
const session = handle
|
|
481
|
+
? await client.resumeSession(handle, normalizedConfig, launchContext)
|
|
482
|
+
: await client.createSession(normalizedConfig, launchContext);
|
|
483
|
+
// Remove the existing agent entry before swapping sessions
|
|
484
|
+
this.agents.delete(agentId);
|
|
485
|
+
if (existing.unsubscribeSession) {
|
|
486
|
+
existing.unsubscribeSession();
|
|
487
|
+
existing.unsubscribeSession = null;
|
|
488
|
+
}
|
|
489
|
+
for (const waiter of existing.foregroundTurnWaiters) {
|
|
490
|
+
this.settleForegroundTurnWaiter(waiter);
|
|
491
|
+
}
|
|
492
|
+
existing.foregroundTurnWaiters.clear();
|
|
493
|
+
this.settlePendingForegroundRun(agentId);
|
|
494
|
+
try {
|
|
495
|
+
await existing.session.close();
|
|
496
|
+
}
|
|
497
|
+
catch (error) {
|
|
498
|
+
this.logger.warn({ err: error, agentId }, "Failed to close previous session during refresh");
|
|
499
|
+
}
|
|
500
|
+
// Preserve existing labels and timeline during reload.
|
|
501
|
+
return this.registerSession(session, normalizedConfig, agentId, {
|
|
502
|
+
labels: existing.labels,
|
|
503
|
+
createdAt: existing.createdAt,
|
|
504
|
+
updatedAt: existing.updatedAt,
|
|
505
|
+
lastUserMessageAt: existing.lastUserMessageAt,
|
|
506
|
+
timeline: preservedTimeline,
|
|
507
|
+
timelineRows: preservedTimelineRows,
|
|
508
|
+
timelineEpoch: preservedTimelineEpoch,
|
|
509
|
+
timelineNextSeq: preservedTimelineNextSeq,
|
|
510
|
+
historyPrimed: preservedHistoryPrimed,
|
|
511
|
+
lastUsage: preservedLastUsage,
|
|
512
|
+
lastError: preservedLastError,
|
|
513
|
+
attention: preservedAttention,
|
|
514
|
+
});
|
|
515
|
+
}
|
|
516
|
+
async closeAgent(agentId) {
|
|
517
|
+
const agent = this.requireAgent(agentId);
|
|
518
|
+
this.logger.trace({
|
|
519
|
+
agentId,
|
|
520
|
+
lifecycle: agent.lifecycle,
|
|
521
|
+
activeForegroundTurnId: agent.activeForegroundTurnId,
|
|
522
|
+
pendingPermissions: agent.pendingPermissions.size,
|
|
523
|
+
}, "closeAgent: start");
|
|
524
|
+
this.agents.delete(agentId);
|
|
525
|
+
// Clean up previousStatus to prevent memory leak
|
|
526
|
+
this.previousStatuses.delete(agentId);
|
|
527
|
+
if (agent.unsubscribeSession) {
|
|
528
|
+
agent.unsubscribeSession();
|
|
529
|
+
agent.unsubscribeSession = null;
|
|
530
|
+
}
|
|
531
|
+
for (const waiter of agent.foregroundTurnWaiters) {
|
|
532
|
+
// Wake up the generator so it can exit the await loop
|
|
533
|
+
waiter.callback({
|
|
534
|
+
type: "turn_canceled",
|
|
535
|
+
provider: agent.provider,
|
|
536
|
+
reason: "agent closed",
|
|
537
|
+
turnId: waiter.turnId,
|
|
538
|
+
});
|
|
539
|
+
this.settleForegroundTurnWaiter(waiter);
|
|
540
|
+
}
|
|
541
|
+
agent.foregroundTurnWaiters.clear();
|
|
542
|
+
this.settlePendingForegroundRun(agentId);
|
|
543
|
+
const session = agent.session;
|
|
544
|
+
const closedAgent = {
|
|
545
|
+
...agent,
|
|
546
|
+
lifecycle: "closed",
|
|
547
|
+
session: null,
|
|
548
|
+
activeForegroundTurnId: null,
|
|
549
|
+
};
|
|
550
|
+
await session.close();
|
|
551
|
+
this.emitState(closedAgent);
|
|
552
|
+
this.logger.trace({ agentId }, "closeAgent: completed");
|
|
553
|
+
}
|
|
554
|
+
async archiveAgent(agentId) {
|
|
555
|
+
const agent = this.requireAgent(agentId);
|
|
556
|
+
if (!this.registry) {
|
|
557
|
+
throw new Error("Agent storage is not configured");
|
|
558
|
+
}
|
|
559
|
+
await this.registry.applySnapshot(agent, {
|
|
560
|
+
internal: agent.internal,
|
|
561
|
+
});
|
|
562
|
+
const stored = await this.registry.get(agentId);
|
|
563
|
+
if (!stored) {
|
|
564
|
+
throw new Error(`Agent ${agentId} not found in storage after snapshot`);
|
|
565
|
+
}
|
|
566
|
+
const archivedAt = new Date().toISOString();
|
|
567
|
+
const normalizedStatus = stored.lastStatus === "running" || stored.lastStatus === "initializing"
|
|
568
|
+
? "idle"
|
|
569
|
+
: stored.lastStatus;
|
|
570
|
+
await this.registry.upsert({
|
|
571
|
+
...stored,
|
|
572
|
+
archivedAt,
|
|
573
|
+
updatedAt: archivedAt,
|
|
574
|
+
lastStatus: normalizedStatus,
|
|
575
|
+
requiresAttention: false,
|
|
576
|
+
attentionReason: null,
|
|
577
|
+
attentionTimestamp: null,
|
|
578
|
+
});
|
|
579
|
+
this.notifyAgentState(agentId);
|
|
580
|
+
await this.closeAgent(agentId);
|
|
581
|
+
return { archivedAt };
|
|
582
|
+
}
|
|
583
|
+
async setAgentMode(agentId, modeId) {
|
|
584
|
+
const agent = this.requireAgent(agentId);
|
|
585
|
+
await agent.session.setMode(modeId);
|
|
586
|
+
agent.currentModeId = modeId;
|
|
587
|
+
// Update runtimeInfo to reflect the new mode
|
|
588
|
+
if (agent.runtimeInfo) {
|
|
589
|
+
agent.runtimeInfo = { ...agent.runtimeInfo, modeId };
|
|
590
|
+
}
|
|
591
|
+
this.touchUpdatedAt(agent);
|
|
592
|
+
this.emitState(agent);
|
|
593
|
+
}
|
|
594
|
+
async setAgentModel(agentId, modelId) {
|
|
595
|
+
const agent = this.requireAgent(agentId);
|
|
596
|
+
const normalizedModelId = typeof modelId === "string" && modelId.trim().length > 0 ? modelId : null;
|
|
597
|
+
if (agent.session.setModel) {
|
|
598
|
+
await agent.session.setModel(normalizedModelId);
|
|
599
|
+
}
|
|
600
|
+
agent.config.model = normalizedModelId ?? undefined;
|
|
601
|
+
if (agent.runtimeInfo) {
|
|
602
|
+
agent.runtimeInfo = { ...agent.runtimeInfo, model: normalizedModelId };
|
|
603
|
+
}
|
|
604
|
+
this.touchUpdatedAt(agent);
|
|
605
|
+
this.emitState(agent);
|
|
606
|
+
}
|
|
607
|
+
async setAgentThinkingOption(agentId, thinkingOptionId) {
|
|
608
|
+
const agent = this.requireAgent(agentId);
|
|
609
|
+
const normalizedThinkingOptionId = typeof thinkingOptionId === "string" && thinkingOptionId.trim().length > 0
|
|
610
|
+
? thinkingOptionId
|
|
611
|
+
: null;
|
|
612
|
+
if (agent.session.setThinkingOption) {
|
|
613
|
+
await agent.session.setThinkingOption(normalizedThinkingOptionId);
|
|
614
|
+
}
|
|
615
|
+
agent.config.thinkingOptionId = normalizedThinkingOptionId ?? undefined;
|
|
616
|
+
this.touchUpdatedAt(agent);
|
|
617
|
+
this.emitState(agent);
|
|
618
|
+
}
|
|
619
|
+
async setAgentFeature(agentId, featureId, value) {
|
|
620
|
+
const agent = this.requireAgent(agentId);
|
|
621
|
+
if (!agent.session.setFeature) {
|
|
622
|
+
throw new Error("Agent session does not support setting features");
|
|
623
|
+
}
|
|
624
|
+
await agent.session.setFeature(featureId, value);
|
|
625
|
+
agent.config.featureValues = { ...agent.config.featureValues, [featureId]: value };
|
|
626
|
+
this.touchUpdatedAt(agent);
|
|
627
|
+
this.emitState(agent);
|
|
628
|
+
}
|
|
629
|
+
async setTitle(agentId, title) {
|
|
630
|
+
const agent = this.requireAgent(agentId);
|
|
631
|
+
const normalizedTitle = title.trim();
|
|
632
|
+
if (!normalizedTitle) {
|
|
633
|
+
return;
|
|
634
|
+
}
|
|
635
|
+
this.touchUpdatedAt(agent);
|
|
636
|
+
await this.persistSnapshot(agent, { title: normalizedTitle });
|
|
637
|
+
this.emitState(agent);
|
|
638
|
+
}
|
|
639
|
+
async setLabels(agentId, labels) {
|
|
640
|
+
const agent = this.requireAgent(agentId);
|
|
641
|
+
agent.labels = { ...agent.labels, ...labels };
|
|
642
|
+
await this.persistSnapshot(agent);
|
|
643
|
+
this.touchUpdatedAt(agent);
|
|
644
|
+
this.emitState(agent);
|
|
645
|
+
}
|
|
646
|
+
notifyAgentState(agentId) {
|
|
647
|
+
const agent = this.agents.get(agentId);
|
|
648
|
+
if (!agent || agent.internal) {
|
|
649
|
+
return;
|
|
650
|
+
}
|
|
651
|
+
this.touchUpdatedAt(agent);
|
|
652
|
+
this.emitState(agent);
|
|
653
|
+
}
|
|
654
|
+
async clearAgentAttention(agentId) {
|
|
655
|
+
const agent = this.requireAgent(agentId);
|
|
656
|
+
if (agent.attention.requiresAttention) {
|
|
657
|
+
agent.attention = { requiresAttention: false };
|
|
658
|
+
await this.persistSnapshot(agent);
|
|
659
|
+
this.emitState(agent);
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
async runAgent(agentId, prompt, options) {
|
|
663
|
+
const events = this.streamAgent(agentId, prompt, options);
|
|
664
|
+
const timeline = [];
|
|
665
|
+
let finalText = "";
|
|
666
|
+
let usage;
|
|
667
|
+
let canceled = false;
|
|
668
|
+
for await (const event of events) {
|
|
669
|
+
if (event.type === "timeline") {
|
|
670
|
+
timeline.push(event.item);
|
|
671
|
+
}
|
|
672
|
+
else if (event.type === "turn_completed") {
|
|
673
|
+
usage = event.usage;
|
|
674
|
+
}
|
|
675
|
+
else if (event.type === "turn_failed") {
|
|
676
|
+
throw new Error(this.formatTurnFailedMessage(event));
|
|
677
|
+
}
|
|
678
|
+
else if (event.type === "turn_canceled") {
|
|
679
|
+
canceled = true;
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
finalText = this.getLastAssistantMessageFromTimeline(timeline) ?? "";
|
|
683
|
+
const agent = this.requireAgent(agentId);
|
|
684
|
+
const sessionId = agent.persistence?.sessionId;
|
|
685
|
+
if (!sessionId) {
|
|
686
|
+
throw new Error(`Agent ${agentId} has no persistence.sessionId after run completed`);
|
|
687
|
+
}
|
|
688
|
+
return {
|
|
689
|
+
sessionId,
|
|
690
|
+
finalText,
|
|
691
|
+
usage,
|
|
692
|
+
timeline,
|
|
693
|
+
canceled,
|
|
694
|
+
};
|
|
695
|
+
}
|
|
696
|
+
recordUserMessage(agentId, text, options) {
|
|
697
|
+
const agent = this.requireAgent(agentId);
|
|
698
|
+
const normalizedMessageId = normalizeMessageId(options?.messageId);
|
|
699
|
+
const item = {
|
|
700
|
+
type: "user_message",
|
|
701
|
+
text,
|
|
702
|
+
messageId: normalizedMessageId,
|
|
703
|
+
};
|
|
704
|
+
const updatedAt = this.touchUpdatedAt(agent);
|
|
705
|
+
agent.lastUserMessageAt = updatedAt;
|
|
706
|
+
const row = this.recordTimeline(agent, item);
|
|
707
|
+
this.dispatchStream(agentId, {
|
|
708
|
+
type: "timeline",
|
|
709
|
+
item,
|
|
710
|
+
provider: agent.provider,
|
|
711
|
+
}, {
|
|
712
|
+
seq: row.seq,
|
|
713
|
+
epoch: this.ensureTimelineState(agent).epoch,
|
|
714
|
+
});
|
|
715
|
+
if (options?.emitState !== false) {
|
|
716
|
+
this.emitState(agent);
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
async appendTimelineItem(agentId, item) {
|
|
720
|
+
const agent = this.requireAgent(agentId);
|
|
721
|
+
this.touchUpdatedAt(agent);
|
|
722
|
+
const row = this.recordTimeline(agent, item);
|
|
723
|
+
this.dispatchStream(agentId, {
|
|
724
|
+
type: "timeline",
|
|
725
|
+
item,
|
|
726
|
+
provider: agent.provider,
|
|
727
|
+
}, {
|
|
728
|
+
seq: row.seq,
|
|
729
|
+
epoch: this.ensureTimelineState(agent).epoch,
|
|
730
|
+
});
|
|
731
|
+
await this.persistSnapshot(agent);
|
|
732
|
+
}
|
|
733
|
+
async emitLiveTimelineItem(agentId, item) {
|
|
734
|
+
const agent = this.requireAgent(agentId);
|
|
735
|
+
this.touchUpdatedAt(agent);
|
|
736
|
+
this.dispatchStream(agentId, {
|
|
737
|
+
type: "timeline",
|
|
738
|
+
item,
|
|
739
|
+
provider: agent.provider,
|
|
740
|
+
});
|
|
741
|
+
}
|
|
742
|
+
streamAgent(agentId, prompt, options) {
|
|
743
|
+
const existingAgent = this.requireAgent(agentId);
|
|
744
|
+
this.logger.trace({
|
|
745
|
+
agentId,
|
|
746
|
+
lifecycle: existingAgent.lifecycle,
|
|
747
|
+
activeForegroundTurnId: existingAgent.activeForegroundTurnId,
|
|
748
|
+
hasPendingForegroundRun: this.hasPendingForegroundRun(agentId),
|
|
749
|
+
promptType: typeof prompt === "string" ? "string" : "structured",
|
|
750
|
+
hasRunOptions: Boolean(options),
|
|
751
|
+
}, "streamAgent: requested");
|
|
752
|
+
if (existingAgent.activeForegroundTurnId || this.hasPendingForegroundRun(agentId)) {
|
|
753
|
+
this.logger.trace({
|
|
754
|
+
agentId,
|
|
755
|
+
lifecycle: existingAgent.lifecycle,
|
|
756
|
+
hasPendingForegroundRun: this.hasPendingForegroundRun(agentId),
|
|
757
|
+
}, "streamAgent: rejected because a foreground run is already in flight");
|
|
758
|
+
throw new Error(`Agent ${agentId} already has an active run`);
|
|
759
|
+
}
|
|
760
|
+
const agent = existingAgent;
|
|
761
|
+
agent.pendingReplacement = false;
|
|
762
|
+
agent.lastError = undefined;
|
|
763
|
+
const self = this;
|
|
764
|
+
const pendingRun = self.createPendingForegroundRun();
|
|
765
|
+
self.pendingForegroundRuns.set(agentId, pendingRun);
|
|
766
|
+
const streamForwarder = (async function* streamForwarder() {
|
|
767
|
+
let turnId;
|
|
768
|
+
let waiter = null;
|
|
769
|
+
try {
|
|
770
|
+
const result = await agent.session.startTurn(prompt, options);
|
|
771
|
+
turnId = result.turnId;
|
|
772
|
+
}
|
|
773
|
+
catch (error) {
|
|
774
|
+
const errorMsg = error instanceof Error ? error.message : "Failed to start turn";
|
|
775
|
+
self.handleStreamEvent(agent, {
|
|
776
|
+
type: "turn_failed",
|
|
777
|
+
provider: agent.provider,
|
|
778
|
+
error: errorMsg,
|
|
779
|
+
});
|
|
780
|
+
self.finalizeForegroundTurn(agent);
|
|
781
|
+
throw error;
|
|
782
|
+
}
|
|
783
|
+
pendingRun.started = true;
|
|
784
|
+
agent.activeForegroundTurnId = turnId;
|
|
785
|
+
agent.lifecycle = "running";
|
|
786
|
+
self.touchUpdatedAt(agent);
|
|
787
|
+
self.emitState(agent);
|
|
788
|
+
self.logger.trace({
|
|
789
|
+
agentId,
|
|
790
|
+
lifecycle: agent.lifecycle,
|
|
791
|
+
activeForegroundTurnId: agent.activeForegroundTurnId,
|
|
792
|
+
}, "streamAgent: started");
|
|
793
|
+
// Create a pushable queue for this foreground turn
|
|
794
|
+
const queue = [];
|
|
795
|
+
let queueResolve = null;
|
|
796
|
+
let done = false;
|
|
797
|
+
let resolveSettled;
|
|
798
|
+
const settledPromise = new Promise((resolve) => {
|
|
799
|
+
resolveSettled = resolve;
|
|
800
|
+
});
|
|
801
|
+
waiter = {
|
|
802
|
+
turnId,
|
|
803
|
+
settled: false,
|
|
804
|
+
settledPromise,
|
|
805
|
+
resolveSettled,
|
|
806
|
+
callback: (event) => {
|
|
807
|
+
queue.push(event);
|
|
808
|
+
if (queueResolve) {
|
|
809
|
+
queueResolve();
|
|
810
|
+
queueResolve = null;
|
|
811
|
+
}
|
|
812
|
+
},
|
|
813
|
+
};
|
|
814
|
+
agent.foregroundTurnWaiters.add(waiter);
|
|
815
|
+
try {
|
|
816
|
+
while (!done) {
|
|
817
|
+
while (queue.length > 0) {
|
|
818
|
+
const event = queue.shift();
|
|
819
|
+
yield event;
|
|
820
|
+
if (isTurnTerminalEvent(event)) {
|
|
821
|
+
done = true;
|
|
822
|
+
break;
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
if (!done && queue.length === 0) {
|
|
826
|
+
if (waiter.settled) {
|
|
827
|
+
break;
|
|
828
|
+
}
|
|
829
|
+
await new Promise((resolve) => {
|
|
830
|
+
queueResolve = resolve;
|
|
831
|
+
});
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
finally {
|
|
836
|
+
if (waiter) {
|
|
837
|
+
agent.foregroundTurnWaiters.delete(waiter);
|
|
838
|
+
self.settleForegroundTurnWaiter(waiter);
|
|
839
|
+
}
|
|
840
|
+
self.settlePendingForegroundRun(agentId, pendingRun.token);
|
|
841
|
+
if (!agent.activeForegroundTurnId) {
|
|
842
|
+
await self.refreshRuntimeInfo(agent);
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
})();
|
|
846
|
+
return streamForwarder;
|
|
847
|
+
}
|
|
848
|
+
finalizeForegroundTurn(agent) {
|
|
849
|
+
const mutableAgent = agent;
|
|
850
|
+
mutableAgent.activeForegroundTurnId = null;
|
|
851
|
+
const terminalError = mutableAgent.lastError;
|
|
852
|
+
const shouldHoldBusyForReplacement = mutableAgent.pendingReplacement && !terminalError;
|
|
853
|
+
mutableAgent.lifecycle = shouldHoldBusyForReplacement
|
|
854
|
+
? "running"
|
|
855
|
+
: terminalError
|
|
856
|
+
? "error"
|
|
857
|
+
: "idle";
|
|
858
|
+
const persistenceHandle = mutableAgent.session.describePersistence() ??
|
|
859
|
+
(mutableAgent.runtimeInfo?.sessionId
|
|
860
|
+
? { provider: mutableAgent.provider, sessionId: mutableAgent.runtimeInfo.sessionId }
|
|
861
|
+
: null);
|
|
862
|
+
if (persistenceHandle) {
|
|
863
|
+
mutableAgent.persistence = attachPersistenceCwd(persistenceHandle, mutableAgent.cwd);
|
|
864
|
+
}
|
|
865
|
+
this.logger.trace({
|
|
866
|
+
agentId: agent.id,
|
|
867
|
+
lifecycle: mutableAgent.lifecycle,
|
|
868
|
+
terminalError,
|
|
869
|
+
pendingReplacement: mutableAgent.pendingReplacement,
|
|
870
|
+
}, "finalizeForegroundTurn: applying terminal state");
|
|
871
|
+
if (!shouldHoldBusyForReplacement) {
|
|
872
|
+
this.touchUpdatedAt(mutableAgent);
|
|
873
|
+
this.emitState(mutableAgent);
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
replaceAgentRun(agentId, prompt, options) {
|
|
877
|
+
const snapshot = this.requireAgent(agentId);
|
|
878
|
+
if (snapshot.lifecycle !== "running" &&
|
|
879
|
+
!snapshot.activeForegroundTurnId &&
|
|
880
|
+
!this.hasPendingForegroundRun(agentId)) {
|
|
881
|
+
return this.streamAgent(agentId, prompt, options);
|
|
882
|
+
}
|
|
883
|
+
const agent = snapshot;
|
|
884
|
+
agent.pendingReplacement = true;
|
|
885
|
+
const self = this;
|
|
886
|
+
return (async function* replaceRunForwarder() {
|
|
887
|
+
try {
|
|
888
|
+
await self.cancelAgentRun(agentId);
|
|
889
|
+
const nextRun = self.streamAgent(agentId, prompt, options);
|
|
890
|
+
for await (const event of nextRun) {
|
|
891
|
+
yield event;
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
catch (error) {
|
|
895
|
+
const latest = self.agents.get(agentId);
|
|
896
|
+
if (latest) {
|
|
897
|
+
const latestActive = latest;
|
|
898
|
+
latestActive.pendingReplacement = false;
|
|
899
|
+
if (!latestActive.activeForegroundTurnId && latestActive.lifecycle === "running") {
|
|
900
|
+
latestActive.lifecycle = "idle";
|
|
901
|
+
self.touchUpdatedAt(latestActive);
|
|
902
|
+
self.emitState(latestActive);
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
throw error;
|
|
906
|
+
}
|
|
907
|
+
})();
|
|
908
|
+
}
|
|
909
|
+
async waitForAgentRunStart(agentId, options) {
|
|
910
|
+
const snapshot = this.getAgent(agentId);
|
|
911
|
+
if (!snapshot) {
|
|
912
|
+
throw new Error(`Agent ${agentId} not found`);
|
|
913
|
+
}
|
|
914
|
+
const pendingRun = this.getPendingForegroundRun(agentId);
|
|
915
|
+
if ((snapshot.lifecycle === "running" || pendingRun?.started) && !snapshot.pendingReplacement) {
|
|
916
|
+
return;
|
|
917
|
+
}
|
|
918
|
+
if (!snapshot.activeForegroundTurnId && !pendingRun && !snapshot.pendingReplacement) {
|
|
919
|
+
throw new Error(`Agent ${agentId} has no pending run`);
|
|
920
|
+
}
|
|
921
|
+
if (options?.signal?.aborted) {
|
|
922
|
+
throw createAbortError(options.signal, "wait_for_agent_start aborted");
|
|
923
|
+
}
|
|
924
|
+
await new Promise((resolve, reject) => {
|
|
925
|
+
if (options?.signal?.aborted) {
|
|
926
|
+
reject(createAbortError(options.signal, "wait_for_agent_start aborted"));
|
|
927
|
+
return;
|
|
928
|
+
}
|
|
929
|
+
let unsubscribe = null;
|
|
930
|
+
let abortHandler = null;
|
|
931
|
+
const cleanup = () => {
|
|
932
|
+
if (unsubscribe) {
|
|
933
|
+
try {
|
|
934
|
+
unsubscribe();
|
|
935
|
+
}
|
|
936
|
+
catch {
|
|
937
|
+
// ignore cleanup errors
|
|
938
|
+
}
|
|
939
|
+
unsubscribe = null;
|
|
940
|
+
}
|
|
941
|
+
if (abortHandler && options?.signal) {
|
|
942
|
+
try {
|
|
943
|
+
options.signal.removeEventListener("abort", abortHandler);
|
|
944
|
+
}
|
|
945
|
+
catch {
|
|
946
|
+
// ignore cleanup errors
|
|
947
|
+
}
|
|
948
|
+
abortHandler = null;
|
|
949
|
+
}
|
|
950
|
+
};
|
|
951
|
+
const finishOk = () => {
|
|
952
|
+
cleanup();
|
|
953
|
+
resolve();
|
|
954
|
+
};
|
|
955
|
+
const finishErr = (error) => {
|
|
956
|
+
cleanup();
|
|
957
|
+
reject(error);
|
|
958
|
+
};
|
|
959
|
+
if (options?.signal) {
|
|
960
|
+
abortHandler = () => finishErr(createAbortError(options.signal, "wait_for_agent_start aborted"));
|
|
961
|
+
options.signal.addEventListener("abort", abortHandler, { once: true });
|
|
962
|
+
}
|
|
963
|
+
const checkCurrentState = () => {
|
|
964
|
+
const current = this.getAgent(agentId);
|
|
965
|
+
if (!current) {
|
|
966
|
+
finishErr(new Error(`Agent ${agentId} not found`));
|
|
967
|
+
return true;
|
|
968
|
+
}
|
|
969
|
+
const currentPendingRun = this.getPendingForegroundRun(agentId);
|
|
970
|
+
if ((current.lifecycle === "running" || currentPendingRun?.started) &&
|
|
971
|
+
!current.pendingReplacement) {
|
|
972
|
+
finishOk();
|
|
973
|
+
return true;
|
|
974
|
+
}
|
|
975
|
+
if (current.lifecycle === "error" && !currentPendingRun?.started) {
|
|
976
|
+
finishErr(new Error(current.lastError ?? `Agent ${agentId} failed to start`));
|
|
977
|
+
return true;
|
|
978
|
+
}
|
|
979
|
+
if (!currentPendingRun && !current.activeForegroundTurnId && !current.pendingReplacement) {
|
|
980
|
+
finishErr(new Error(`Agent ${agentId} run finished before starting`));
|
|
981
|
+
return true;
|
|
982
|
+
}
|
|
983
|
+
return false;
|
|
984
|
+
};
|
|
985
|
+
unsubscribe = this.subscribe((event) => {
|
|
986
|
+
if (event.type !== "agent_state" || event.agent.id !== agentId) {
|
|
987
|
+
return;
|
|
988
|
+
}
|
|
989
|
+
checkCurrentState();
|
|
990
|
+
}, { agentId, replayState: false });
|
|
991
|
+
checkCurrentState();
|
|
992
|
+
});
|
|
993
|
+
}
|
|
994
|
+
async respondToPermission(agentId, requestId, response) {
|
|
995
|
+
const agent = this.requireAgent(agentId);
|
|
996
|
+
agent.inFlightPermissionResponses.add(requestId);
|
|
997
|
+
try {
|
|
998
|
+
const result = await agent.session.respondToPermission(requestId, response);
|
|
999
|
+
agent.pendingPermissions.delete(requestId);
|
|
1000
|
+
try {
|
|
1001
|
+
await this.refreshSessionState(agent);
|
|
1002
|
+
}
|
|
1003
|
+
catch {
|
|
1004
|
+
// Ignore refresh errors - state sync after permission approval is best effort.
|
|
1005
|
+
}
|
|
1006
|
+
this.touchUpdatedAt(agent);
|
|
1007
|
+
await this.persistSnapshot(agent);
|
|
1008
|
+
this.emitState(agent);
|
|
1009
|
+
const bufferedResolution = agent.bufferedPermissionResolutions.get(requestId);
|
|
1010
|
+
if (bufferedResolution) {
|
|
1011
|
+
agent.bufferedPermissionResolutions.delete(requestId);
|
|
1012
|
+
this.dispatchStream(agent.id, bufferedResolution);
|
|
1013
|
+
}
|
|
1014
|
+
return result;
|
|
1015
|
+
}
|
|
1016
|
+
finally {
|
|
1017
|
+
agent.inFlightPermissionResponses.delete(requestId);
|
|
1018
|
+
agent.bufferedPermissionResolutions.delete(requestId);
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
async cancelAgentRun(agentId) {
|
|
1022
|
+
const agent = this.requireAgent(agentId);
|
|
1023
|
+
const pendingRun = this.getPendingForegroundRun(agentId);
|
|
1024
|
+
const foregroundTurnId = agent.activeForegroundTurnId;
|
|
1025
|
+
const hasForegroundTurn = Boolean(foregroundTurnId);
|
|
1026
|
+
const isAutonomousRunning = agent.lifecycle === "running" && !hasForegroundTurn && !pendingRun;
|
|
1027
|
+
if (!hasForegroundTurn && !isAutonomousRunning && !pendingRun) {
|
|
1028
|
+
return false;
|
|
1029
|
+
}
|
|
1030
|
+
try {
|
|
1031
|
+
await agent.session.interrupt();
|
|
1032
|
+
}
|
|
1033
|
+
catch (error) {
|
|
1034
|
+
this.logger.error({ err: error, agentId }, "Failed to interrupt session");
|
|
1035
|
+
}
|
|
1036
|
+
// The interrupt will produce a turn_canceled/turn_failed event via subscribe(),
|
|
1037
|
+
// which flows through the session event dispatcher and settles the foreground turn waiter.
|
|
1038
|
+
// Wait briefly for the event to propagate if there's an active foreground turn.
|
|
1039
|
+
if (foregroundTurnId) {
|
|
1040
|
+
const waiter = Array.from(agent.foregroundTurnWaiters).find((candidate) => candidate.turnId === foregroundTurnId);
|
|
1041
|
+
const timeout = new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1042
|
+
if (waiter) {
|
|
1043
|
+
await Promise.race([waiter.settledPromise, timeout]);
|
|
1044
|
+
}
|
|
1045
|
+
else if (agent.activeForegroundTurnId === foregroundTurnId) {
|
|
1046
|
+
await Promise.race([
|
|
1047
|
+
new Promise((resolve) => {
|
|
1048
|
+
const unsubscribe = this.subscribe((event) => {
|
|
1049
|
+
if (event.type === "agent_state" &&
|
|
1050
|
+
event.agent.id === agentId &&
|
|
1051
|
+
!event.agent.activeForegroundTurnId) {
|
|
1052
|
+
unsubscribe();
|
|
1053
|
+
resolve();
|
|
1054
|
+
}
|
|
1055
|
+
}, { agentId, replayState: false });
|
|
1056
|
+
}),
|
|
1057
|
+
timeout,
|
|
1058
|
+
]);
|
|
1059
|
+
}
|
|
1060
|
+
// The waiter settling wakes up the streamForwarder generator, but its
|
|
1061
|
+
// finally block (which deletes the pendingForegroundRun) runs asynchronously.
|
|
1062
|
+
// Wait for the pending run to be fully cleaned up so the next streamAgent
|
|
1063
|
+
// call doesn't see a stale entry and reject with "already has an active run".
|
|
1064
|
+
if (pendingRun && !pendingRun.settled) {
|
|
1065
|
+
await Promise.race([pendingRun.settledPromise, timeout]);
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
else if (pendingRun) {
|
|
1069
|
+
const timeout = new Promise((resolve) => setTimeout(resolve, 2000));
|
|
1070
|
+
await Promise.race([pendingRun.settledPromise, timeout]);
|
|
1071
|
+
}
|
|
1072
|
+
// If the foreground turn is still stuck after the timeout, force-dispatch a
|
|
1073
|
+
// synthetic turn_canceled so the normal event pipeline cleans up
|
|
1074
|
+
// activeForegroundTurnId, settles waiters, and unblocks the streamForwarder.
|
|
1075
|
+
if (foregroundTurnId && agent.activeForegroundTurnId === foregroundTurnId) {
|
|
1076
|
+
this.logger.warn({ agentId, foregroundTurnId }, "cancelAgentRun: foreground turn still active after timeout, force-canceling");
|
|
1077
|
+
this.dispatchSessionEvent(agent, {
|
|
1078
|
+
type: "turn_canceled",
|
|
1079
|
+
provider: agent.provider,
|
|
1080
|
+
reason: "interrupted",
|
|
1081
|
+
turnId: foregroundTurnId,
|
|
1082
|
+
});
|
|
1083
|
+
// The synthetic event unblocks the streamForwarder generator, whose finally
|
|
1084
|
+
// block settles the pending foreground run asynchronously. Wait for it.
|
|
1085
|
+
const staleRun = this.getPendingForegroundRun(agentId);
|
|
1086
|
+
if (staleRun && !staleRun.settled) {
|
|
1087
|
+
await staleRun.settledPromise;
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
// Clear any pending permissions that weren't cleaned up by handleStreamEvent.
|
|
1091
|
+
if (agent.pendingPermissions.size > 0) {
|
|
1092
|
+
for (const [requestId] of agent.pendingPermissions) {
|
|
1093
|
+
this.dispatchStream(agent.id, {
|
|
1094
|
+
type: "permission_resolved",
|
|
1095
|
+
provider: agent.provider,
|
|
1096
|
+
requestId,
|
|
1097
|
+
resolution: { behavior: "deny", message: "Interrupted" },
|
|
1098
|
+
});
|
|
1099
|
+
}
|
|
1100
|
+
agent.pendingPermissions.clear();
|
|
1101
|
+
this.touchUpdatedAt(agent);
|
|
1102
|
+
this.emitState(agent);
|
|
1103
|
+
}
|
|
1104
|
+
return true;
|
|
1105
|
+
}
|
|
1106
|
+
getPendingPermissions(agentId) {
|
|
1107
|
+
const agent = this.requireAgent(agentId);
|
|
1108
|
+
return Array.from(agent.pendingPermissions.values());
|
|
1109
|
+
}
|
|
1110
|
+
peekPendingPermission(agent) {
|
|
1111
|
+
const iterator = agent.pendingPermissions.values().next();
|
|
1112
|
+
return iterator.done ? null : iterator.value;
|
|
1113
|
+
}
|
|
1114
|
+
async hydrateTimelineFromProvider(agentId) {
|
|
1115
|
+
const agent = this.requireAgent(agentId);
|
|
1116
|
+
await this.hydrateTimeline(agent);
|
|
1117
|
+
}
|
|
1118
|
+
getLastAssistantMessage(agentId) {
|
|
1119
|
+
const agent = this.agents.get(agentId);
|
|
1120
|
+
if (!agent) {
|
|
1121
|
+
return null;
|
|
1122
|
+
}
|
|
1123
|
+
return this.getLastAssistantMessageFromTimeline(agent.timeline);
|
|
1124
|
+
}
|
|
1125
|
+
getLastAssistantMessageFromTimeline(timeline) {
|
|
1126
|
+
// Collect the last contiguous assistant messages (Claude streams chunks)
|
|
1127
|
+
const chunks = [];
|
|
1128
|
+
for (let i = timeline.length - 1; i >= 0; i--) {
|
|
1129
|
+
const item = timeline[i];
|
|
1130
|
+
if (item.type !== "assistant_message") {
|
|
1131
|
+
if (chunks.length) {
|
|
1132
|
+
break;
|
|
1133
|
+
}
|
|
1134
|
+
continue;
|
|
1135
|
+
}
|
|
1136
|
+
chunks.push(item.text);
|
|
1137
|
+
}
|
|
1138
|
+
if (!chunks.length) {
|
|
1139
|
+
return null;
|
|
1140
|
+
}
|
|
1141
|
+
return chunks.reverse().join("");
|
|
1142
|
+
}
|
|
1143
|
+
async waitForAgentEvent(agentId, options) {
|
|
1144
|
+
const snapshot = this.getAgent(agentId);
|
|
1145
|
+
if (!snapshot) {
|
|
1146
|
+
throw new Error(`Agent ${agentId} not found`);
|
|
1147
|
+
}
|
|
1148
|
+
const pendingForegroundRun = this.getPendingForegroundRun(agentId);
|
|
1149
|
+
const hasForegroundTurn = Boolean(snapshot.activeForegroundTurnId) || Boolean(pendingForegroundRun);
|
|
1150
|
+
const immediatePermission = this.peekPendingPermission(snapshot);
|
|
1151
|
+
if (immediatePermission) {
|
|
1152
|
+
return {
|
|
1153
|
+
status: snapshot.lifecycle,
|
|
1154
|
+
permission: immediatePermission,
|
|
1155
|
+
lastMessage: this.getLastAssistantMessage(agentId),
|
|
1156
|
+
};
|
|
1157
|
+
}
|
|
1158
|
+
const initialStatus = snapshot.lifecycle;
|
|
1159
|
+
const initialBusy = isAgentBusy(initialStatus) || hasForegroundTurn;
|
|
1160
|
+
const waitForActive = options?.waitForActive ?? false;
|
|
1161
|
+
if (!waitForActive && !initialBusy) {
|
|
1162
|
+
return {
|
|
1163
|
+
status: initialStatus,
|
|
1164
|
+
permission: null,
|
|
1165
|
+
lastMessage: this.getLastAssistantMessage(agentId),
|
|
1166
|
+
};
|
|
1167
|
+
}
|
|
1168
|
+
if (waitForActive && !initialBusy && !hasForegroundTurn) {
|
|
1169
|
+
return {
|
|
1170
|
+
status: initialStatus,
|
|
1171
|
+
permission: null,
|
|
1172
|
+
lastMessage: this.getLastAssistantMessage(agentId),
|
|
1173
|
+
};
|
|
1174
|
+
}
|
|
1175
|
+
if (options?.signal?.aborted) {
|
|
1176
|
+
throw createAbortError(options.signal, "wait_for_agent aborted");
|
|
1177
|
+
}
|
|
1178
|
+
return await new Promise((resolve, reject) => {
|
|
1179
|
+
// Bug #1 Fix: Check abort signal AGAIN inside Promise constructor
|
|
1180
|
+
// to avoid race condition between pre-Promise check and abort listener registration
|
|
1181
|
+
if (options?.signal?.aborted) {
|
|
1182
|
+
reject(createAbortError(options.signal, "wait_for_agent aborted"));
|
|
1183
|
+
return;
|
|
1184
|
+
}
|
|
1185
|
+
let currentStatus = initialStatus;
|
|
1186
|
+
let hasStarted = isAgentBusy(initialStatus) ||
|
|
1187
|
+
Boolean(snapshot.activeForegroundTurnId) ||
|
|
1188
|
+
Boolean(pendingForegroundRun?.started);
|
|
1189
|
+
let terminalStatusOverride = null;
|
|
1190
|
+
// Bug #3 Fix: Declare unsubscribe and abortHandler upfront so cleanup can reference them
|
|
1191
|
+
let unsubscribe = null;
|
|
1192
|
+
let abortHandler = null;
|
|
1193
|
+
const cleanup = () => {
|
|
1194
|
+
// Clean up subscription
|
|
1195
|
+
if (unsubscribe) {
|
|
1196
|
+
try {
|
|
1197
|
+
unsubscribe();
|
|
1198
|
+
}
|
|
1199
|
+
catch {
|
|
1200
|
+
// ignore cleanup errors
|
|
1201
|
+
}
|
|
1202
|
+
unsubscribe = null;
|
|
1203
|
+
}
|
|
1204
|
+
// Clean up abort listener
|
|
1205
|
+
if (abortHandler && options?.signal) {
|
|
1206
|
+
try {
|
|
1207
|
+
options.signal.removeEventListener("abort", abortHandler);
|
|
1208
|
+
}
|
|
1209
|
+
catch {
|
|
1210
|
+
// ignore cleanup errors
|
|
1211
|
+
}
|
|
1212
|
+
abortHandler = null;
|
|
1213
|
+
}
|
|
1214
|
+
};
|
|
1215
|
+
const finish = (permission) => {
|
|
1216
|
+
cleanup();
|
|
1217
|
+
resolve({
|
|
1218
|
+
status: currentStatus,
|
|
1219
|
+
permission,
|
|
1220
|
+
lastMessage: this.getLastAssistantMessage(agentId),
|
|
1221
|
+
});
|
|
1222
|
+
};
|
|
1223
|
+
// Bug #3 Fix: Set up abort handler BEFORE subscription
|
|
1224
|
+
// to ensure cleanup handlers exist before callback can fire
|
|
1225
|
+
if (options?.signal) {
|
|
1226
|
+
abortHandler = () => {
|
|
1227
|
+
cleanup();
|
|
1228
|
+
reject(createAbortError(options.signal, "wait_for_agent aborted"));
|
|
1229
|
+
};
|
|
1230
|
+
options.signal.addEventListener("abort", abortHandler, { once: true });
|
|
1231
|
+
}
|
|
1232
|
+
// Bug #3 Fix: Now subscribe with cleanup handlers already in place
|
|
1233
|
+
// This prevents race condition if callback fires synchronously with replayState: true
|
|
1234
|
+
unsubscribe = this.subscribe((event) => {
|
|
1235
|
+
if (event.type === "agent_state") {
|
|
1236
|
+
currentStatus = event.agent.lifecycle;
|
|
1237
|
+
const pending = this.peekPendingPermission(event.agent);
|
|
1238
|
+
if (pending) {
|
|
1239
|
+
finish(pending);
|
|
1240
|
+
return;
|
|
1241
|
+
}
|
|
1242
|
+
if (isAgentBusy(event.agent.lifecycle)) {
|
|
1243
|
+
hasStarted = true;
|
|
1244
|
+
return;
|
|
1245
|
+
}
|
|
1246
|
+
if (!waitForActive || hasStarted) {
|
|
1247
|
+
if (terminalStatusOverride) {
|
|
1248
|
+
currentStatus = terminalStatusOverride;
|
|
1249
|
+
}
|
|
1250
|
+
finish(null);
|
|
1251
|
+
}
|
|
1252
|
+
return;
|
|
1253
|
+
}
|
|
1254
|
+
if (event.type === "agent_stream") {
|
|
1255
|
+
if (event.event.type === "permission_requested") {
|
|
1256
|
+
finish(event.event.request);
|
|
1257
|
+
return;
|
|
1258
|
+
}
|
|
1259
|
+
if (event.event.type === "turn_failed") {
|
|
1260
|
+
hasStarted = true;
|
|
1261
|
+
terminalStatusOverride = "error";
|
|
1262
|
+
return;
|
|
1263
|
+
}
|
|
1264
|
+
if (event.event.type === "turn_completed") {
|
|
1265
|
+
hasStarted = true;
|
|
1266
|
+
}
|
|
1267
|
+
if (event.event.type === "turn_canceled") {
|
|
1268
|
+
hasStarted = true;
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
}, { agentId, replayState: true });
|
|
1272
|
+
});
|
|
1273
|
+
}
|
|
1274
|
+
async registerSession(session, config, agentId, options) {
|
|
1275
|
+
const resolvedAgentId = validateAgentId(agentId, "registerSession");
|
|
1276
|
+
if (this.agents.has(resolvedAgentId)) {
|
|
1277
|
+
throw new Error(`Agent with id ${resolvedAgentId} already exists`);
|
|
1278
|
+
}
|
|
1279
|
+
const initialPersistedTitle = await this.resolveInitialPersistedTitle(resolvedAgentId, config);
|
|
1280
|
+
const now = new Date();
|
|
1281
|
+
const initialTimeline = options?.timeline ? [...options.timeline] : [];
|
|
1282
|
+
const initialTimelineRows = options?.timelineRows?.length
|
|
1283
|
+
? options.timelineRows.map((row) => ({ ...row }))
|
|
1284
|
+
: this.buildTimelineRowsFromItems(initialTimeline, options?.timelineNextSeq ?? 1, (options?.updatedAt ?? options?.createdAt ?? now).toISOString());
|
|
1285
|
+
const derivedNextSeq = options?.timelineNextSeq ??
|
|
1286
|
+
(initialTimelineRows.length
|
|
1287
|
+
? initialTimelineRows[initialTimelineRows.length - 1].seq + 1
|
|
1288
|
+
: 1);
|
|
1289
|
+
const managed = {
|
|
1290
|
+
id: resolvedAgentId,
|
|
1291
|
+
provider: config.provider,
|
|
1292
|
+
cwd: config.cwd,
|
|
1293
|
+
session,
|
|
1294
|
+
capabilities: session.capabilities,
|
|
1295
|
+
config,
|
|
1296
|
+
runtimeInfo: undefined,
|
|
1297
|
+
lifecycle: "initializing",
|
|
1298
|
+
createdAt: options?.createdAt ?? now,
|
|
1299
|
+
updatedAt: options?.updatedAt ?? now,
|
|
1300
|
+
availableModes: [],
|
|
1301
|
+
currentModeId: null,
|
|
1302
|
+
pendingPermissions: new Map(),
|
|
1303
|
+
bufferedPermissionResolutions: new Map(),
|
|
1304
|
+
inFlightPermissionResponses: new Set(),
|
|
1305
|
+
pendingReplacement: false,
|
|
1306
|
+
activeForegroundTurnId: null,
|
|
1307
|
+
foregroundTurnWaiters: new Set(),
|
|
1308
|
+
unsubscribeSession: null,
|
|
1309
|
+
timeline: initialTimeline,
|
|
1310
|
+
timelineRows: initialTimelineRows,
|
|
1311
|
+
timelineEpoch: options?.timelineEpoch ?? randomUUID(),
|
|
1312
|
+
timelineNextSeq: derivedNextSeq,
|
|
1313
|
+
persistence: attachPersistenceCwd(session.describePersistence(), config.cwd),
|
|
1314
|
+
historyPrimed: options?.historyPrimed ?? false,
|
|
1315
|
+
lastUserMessageAt: options?.lastUserMessageAt ?? null,
|
|
1316
|
+
lastUsage: options?.lastUsage,
|
|
1317
|
+
lastError: options?.lastError,
|
|
1318
|
+
attention: options?.attention != null
|
|
1319
|
+
? options.attention.requiresAttention
|
|
1320
|
+
? {
|
|
1321
|
+
requiresAttention: true,
|
|
1322
|
+
attentionReason: options.attention.attentionReason,
|
|
1323
|
+
attentionTimestamp: new Date(options.attention.attentionTimestamp),
|
|
1324
|
+
}
|
|
1325
|
+
: { requiresAttention: false }
|
|
1326
|
+
: { requiresAttention: false },
|
|
1327
|
+
internal: config.internal ?? false,
|
|
1328
|
+
labels: options?.labels ?? {},
|
|
1329
|
+
};
|
|
1330
|
+
this.agents.set(resolvedAgentId, managed);
|
|
1331
|
+
// Initialize previousStatus to track transitions
|
|
1332
|
+
this.previousStatuses.set(resolvedAgentId, managed.lifecycle);
|
|
1333
|
+
await this.refreshRuntimeInfo(managed);
|
|
1334
|
+
await this.persistSnapshot(managed, {
|
|
1335
|
+
title: initialPersistedTitle,
|
|
1336
|
+
});
|
|
1337
|
+
this.emitState(managed);
|
|
1338
|
+
await this.refreshSessionState(managed);
|
|
1339
|
+
managed.lifecycle = "idle";
|
|
1340
|
+
await this.persistSnapshot(managed);
|
|
1341
|
+
this.emitState(managed);
|
|
1342
|
+
this.subscribeToSession(managed);
|
|
1343
|
+
return { ...managed };
|
|
1344
|
+
}
|
|
1345
|
+
subscribeToSession(agent) {
|
|
1346
|
+
if (agent.unsubscribeSession) {
|
|
1347
|
+
return;
|
|
1348
|
+
}
|
|
1349
|
+
const agentId = agent.id;
|
|
1350
|
+
const unsubscribe = agent.session.subscribe((event) => {
|
|
1351
|
+
const current = this.agents.get(agentId);
|
|
1352
|
+
if (!current) {
|
|
1353
|
+
return;
|
|
1354
|
+
}
|
|
1355
|
+
this.dispatchSessionEvent(current, event);
|
|
1356
|
+
});
|
|
1357
|
+
agent.unsubscribeSession = unsubscribe;
|
|
1358
|
+
}
|
|
1359
|
+
dispatchSessionEvent(agent, event) {
|
|
1360
|
+
const turnId = event.turnId;
|
|
1361
|
+
const matchingWaiters = turnId == null
|
|
1362
|
+
? []
|
|
1363
|
+
: Array.from(agent.foregroundTurnWaiters).filter((waiter) => waiter.turnId === turnId && !waiter.settled);
|
|
1364
|
+
this.handleStreamEvent(agent, event);
|
|
1365
|
+
for (const waiter of matchingWaiters) {
|
|
1366
|
+
waiter.callback(event);
|
|
1367
|
+
if (isTurnTerminalEvent(event)) {
|
|
1368
|
+
this.settleForegroundTurnWaiter(waiter);
|
|
1369
|
+
}
|
|
1370
|
+
}
|
|
1371
|
+
}
|
|
1372
|
+
settleForegroundTurnWaiter(waiter) {
|
|
1373
|
+
if (waiter.settled) {
|
|
1374
|
+
return;
|
|
1375
|
+
}
|
|
1376
|
+
waiter.settled = true;
|
|
1377
|
+
waiter.resolveSettled();
|
|
1378
|
+
}
|
|
1379
|
+
createPendingForegroundRun() {
|
|
1380
|
+
let resolveSettled;
|
|
1381
|
+
const settledPromise = new Promise((resolve) => {
|
|
1382
|
+
resolveSettled = resolve;
|
|
1383
|
+
});
|
|
1384
|
+
return {
|
|
1385
|
+
token: randomUUID(),
|
|
1386
|
+
started: false,
|
|
1387
|
+
settled: false,
|
|
1388
|
+
settledPromise,
|
|
1389
|
+
resolveSettled,
|
|
1390
|
+
};
|
|
1391
|
+
}
|
|
1392
|
+
getPendingForegroundRun(agentId) {
|
|
1393
|
+
return this.pendingForegroundRuns.get(agentId) ?? null;
|
|
1394
|
+
}
|
|
1395
|
+
hasPendingForegroundRun(agentId) {
|
|
1396
|
+
return this.pendingForegroundRuns.has(agentId);
|
|
1397
|
+
}
|
|
1398
|
+
settlePendingForegroundRun(agentId, token) {
|
|
1399
|
+
const pendingRun = this.pendingForegroundRuns.get(agentId);
|
|
1400
|
+
if (!pendingRun) {
|
|
1401
|
+
return;
|
|
1402
|
+
}
|
|
1403
|
+
if (token && pendingRun.token !== token) {
|
|
1404
|
+
return;
|
|
1405
|
+
}
|
|
1406
|
+
this.pendingForegroundRuns.delete(agentId);
|
|
1407
|
+
if (pendingRun.settled) {
|
|
1408
|
+
return;
|
|
1409
|
+
}
|
|
1410
|
+
pendingRun.settled = true;
|
|
1411
|
+
pendingRun.resolveSettled();
|
|
1412
|
+
}
|
|
1413
|
+
async resolveInitialPersistedTitle(agentId, config) {
|
|
1414
|
+
const existing = await this.registry?.get(agentId);
|
|
1415
|
+
if (existing) {
|
|
1416
|
+
return existing.title ?? null;
|
|
1417
|
+
}
|
|
1418
|
+
if (Object.prototype.hasOwnProperty.call(config, "title")) {
|
|
1419
|
+
return config.title ?? null;
|
|
1420
|
+
}
|
|
1421
|
+
return null;
|
|
1422
|
+
}
|
|
1423
|
+
buildTimelineRowsFromItems(items, startSeq, timestamp) {
|
|
1424
|
+
let nextSeq = startSeq;
|
|
1425
|
+
return items.map((item) => {
|
|
1426
|
+
const row = {
|
|
1427
|
+
seq: nextSeq,
|
|
1428
|
+
timestamp,
|
|
1429
|
+
item,
|
|
1430
|
+
};
|
|
1431
|
+
nextSeq += 1;
|
|
1432
|
+
return row;
|
|
1433
|
+
});
|
|
1434
|
+
}
|
|
1435
|
+
ensureTimelineState(agent) {
|
|
1436
|
+
const minSeq = agent.timelineRows.length ? agent.timelineRows[0].seq : 0;
|
|
1437
|
+
const maxSeq = agent.timelineRows.length
|
|
1438
|
+
? agent.timelineRows[agent.timelineRows.length - 1].seq
|
|
1439
|
+
: 0;
|
|
1440
|
+
return {
|
|
1441
|
+
rows: agent.timelineRows,
|
|
1442
|
+
epoch: agent.timelineEpoch,
|
|
1443
|
+
nextSeq: agent.timelineNextSeq,
|
|
1444
|
+
minSeq,
|
|
1445
|
+
maxSeq,
|
|
1446
|
+
};
|
|
1447
|
+
}
|
|
1448
|
+
async persistSnapshot(agent, options) {
|
|
1449
|
+
if (!this.registry) {
|
|
1450
|
+
return;
|
|
1451
|
+
}
|
|
1452
|
+
// Don't persist internal agents - they're ephemeral system tasks
|
|
1453
|
+
if (agent.internal) {
|
|
1454
|
+
return;
|
|
1455
|
+
}
|
|
1456
|
+
await this.registry.applySnapshot(agent, options);
|
|
1457
|
+
}
|
|
1458
|
+
async refreshSessionState(agent) {
|
|
1459
|
+
try {
|
|
1460
|
+
const modes = await agent.session.getAvailableModes();
|
|
1461
|
+
agent.availableModes = modes;
|
|
1462
|
+
}
|
|
1463
|
+
catch {
|
|
1464
|
+
agent.availableModes = [];
|
|
1465
|
+
}
|
|
1466
|
+
try {
|
|
1467
|
+
agent.currentModeId = await agent.session.getCurrentMode();
|
|
1468
|
+
}
|
|
1469
|
+
catch {
|
|
1470
|
+
agent.currentModeId = null;
|
|
1471
|
+
}
|
|
1472
|
+
try {
|
|
1473
|
+
const pending = agent.session.getPendingPermissions();
|
|
1474
|
+
agent.pendingPermissions = new Map(pending.map((request) => [request.id, request]));
|
|
1475
|
+
}
|
|
1476
|
+
catch {
|
|
1477
|
+
agent.pendingPermissions.clear();
|
|
1478
|
+
}
|
|
1479
|
+
this.syncFeaturesFromSession(agent);
|
|
1480
|
+
await this.refreshRuntimeInfo(agent);
|
|
1481
|
+
}
|
|
1482
|
+
async refreshRuntimeInfo(agent) {
|
|
1483
|
+
try {
|
|
1484
|
+
const newInfo = await agent.session.getRuntimeInfo();
|
|
1485
|
+
const changed = newInfo.model !== agent.runtimeInfo?.model ||
|
|
1486
|
+
newInfo.thinkingOptionId !== agent.runtimeInfo?.thinkingOptionId ||
|
|
1487
|
+
newInfo.sessionId !== agent.runtimeInfo?.sessionId ||
|
|
1488
|
+
newInfo.modeId !== agent.runtimeInfo?.modeId;
|
|
1489
|
+
agent.runtimeInfo = newInfo;
|
|
1490
|
+
if (!agent.persistence && newInfo.sessionId) {
|
|
1491
|
+
agent.persistence = attachPersistenceCwd({ provider: agent.provider, sessionId: newInfo.sessionId }, agent.cwd);
|
|
1492
|
+
}
|
|
1493
|
+
// Emit state if runtimeInfo changed so clients get the updated model
|
|
1494
|
+
if (changed) {
|
|
1495
|
+
this.emitState(agent);
|
|
1496
|
+
}
|
|
1497
|
+
}
|
|
1498
|
+
catch {
|
|
1499
|
+
// Keep existing runtimeInfo if refresh fails.
|
|
1500
|
+
}
|
|
1501
|
+
}
|
|
1502
|
+
async hydrateTimeline(agent) {
|
|
1503
|
+
if (agent.historyPrimed) {
|
|
1504
|
+
return;
|
|
1505
|
+
}
|
|
1506
|
+
agent.historyPrimed = true;
|
|
1507
|
+
const canonicalUserMessagesById = new Map(agent.timelineRows.flatMap((row) => {
|
|
1508
|
+
if (row.item.type !== "user_message") {
|
|
1509
|
+
return [];
|
|
1510
|
+
}
|
|
1511
|
+
const messageId = normalizeMessageId(row.item.messageId);
|
|
1512
|
+
if (!messageId) {
|
|
1513
|
+
return [];
|
|
1514
|
+
}
|
|
1515
|
+
return [[messageId, row.item.text]];
|
|
1516
|
+
}));
|
|
1517
|
+
try {
|
|
1518
|
+
for await (const event of agent.session.streamHistory()) {
|
|
1519
|
+
this.handleStreamEvent(agent, event, {
|
|
1520
|
+
fromHistory: true,
|
|
1521
|
+
canonicalUserMessagesById: canonicalUserMessagesById.size > 0 ? canonicalUserMessagesById : undefined,
|
|
1522
|
+
});
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
1525
|
+
catch {
|
|
1526
|
+
// ignore history failures
|
|
1527
|
+
}
|
|
1528
|
+
}
|
|
1529
|
+
handleStreamEvent(agent, event, options) {
|
|
1530
|
+
const eventTurnId = event.turnId;
|
|
1531
|
+
const isForegroundEvent = Boolean(eventTurnId && agent.activeForegroundTurnId === eventTurnId);
|
|
1532
|
+
// Only update timestamp for live events, not history replay
|
|
1533
|
+
if (!options?.fromHistory) {
|
|
1534
|
+
this.touchUpdatedAt(agent);
|
|
1535
|
+
}
|
|
1536
|
+
let timelineRow = null;
|
|
1537
|
+
let shouldDispatchEvent = true;
|
|
1538
|
+
switch (event.type) {
|
|
1539
|
+
case "thread_started":
|
|
1540
|
+
{
|
|
1541
|
+
const previousSessionId = agent.persistence?.sessionId ?? null;
|
|
1542
|
+
const handle = agent.session.describePersistence();
|
|
1543
|
+
if (handle) {
|
|
1544
|
+
agent.persistence = attachPersistenceCwd(handle, agent.cwd);
|
|
1545
|
+
if (agent.persistence?.sessionId !== previousSessionId) {
|
|
1546
|
+
this.emitState(agent);
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1549
|
+
void this.refreshRuntimeInfo(agent);
|
|
1550
|
+
}
|
|
1551
|
+
break;
|
|
1552
|
+
case "usage_updated":
|
|
1553
|
+
agent.lastUsage = event.usage;
|
|
1554
|
+
this.emitState(agent);
|
|
1555
|
+
break;
|
|
1556
|
+
case "timeline":
|
|
1557
|
+
// Skip provider-replayed user_message items during history hydration.
|
|
1558
|
+
if (options?.fromHistory && event.item.type === "user_message") {
|
|
1559
|
+
const eventMessageId = normalizeMessageId(event.item.messageId);
|
|
1560
|
+
if (eventMessageId) {
|
|
1561
|
+
const canonicalText = options?.canonicalUserMessagesById?.get(eventMessageId);
|
|
1562
|
+
if (canonicalText === event.item.text) {
|
|
1563
|
+
break;
|
|
1564
|
+
}
|
|
1565
|
+
}
|
|
1566
|
+
}
|
|
1567
|
+
// Suppress user_message echoes for the active foreground turn —
|
|
1568
|
+
// these are already recorded by recordUserMessage().
|
|
1569
|
+
if (!options?.fromHistory && event.item.type === "user_message" && isForegroundEvent) {
|
|
1570
|
+
const eventMessageId = normalizeMessageId(event.item.messageId);
|
|
1571
|
+
const eventText = event.item.text;
|
|
1572
|
+
if (eventMessageId) {
|
|
1573
|
+
const alreadyRecorded = agent.timelineRows.some((row) => {
|
|
1574
|
+
if (row.item.type !== "user_message") {
|
|
1575
|
+
return false;
|
|
1576
|
+
}
|
|
1577
|
+
const rowMessageId = normalizeMessageId(row.item.messageId);
|
|
1578
|
+
return rowMessageId === eventMessageId && row.item.text === eventText;
|
|
1579
|
+
});
|
|
1580
|
+
if (alreadyRecorded) {
|
|
1581
|
+
break;
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
timelineRow = this.recordTimeline(agent, event.item);
|
|
1586
|
+
if (!options?.fromHistory && event.item.type === "user_message") {
|
|
1587
|
+
agent.lastUserMessageAt = new Date();
|
|
1588
|
+
this.emitState(agent);
|
|
1589
|
+
}
|
|
1590
|
+
break;
|
|
1591
|
+
case "turn_completed":
|
|
1592
|
+
this.logger.trace({
|
|
1593
|
+
agentId: agent.id,
|
|
1594
|
+
lifecycle: agent.lifecycle,
|
|
1595
|
+
activeForegroundTurnId: agent.activeForegroundTurnId,
|
|
1596
|
+
eventTurnId,
|
|
1597
|
+
}, "handleStreamEvent: turn_completed");
|
|
1598
|
+
agent.lastUsage = event.usage;
|
|
1599
|
+
agent.lastError = undefined;
|
|
1600
|
+
// For autonomous turns (not foreground), transition to idle
|
|
1601
|
+
// unless a replacement is pending (avoid idle flash during replace)
|
|
1602
|
+
if (!isForegroundEvent && agent.lifecycle !== "idle" && !agent.pendingReplacement) {
|
|
1603
|
+
agent.lifecycle = "idle";
|
|
1604
|
+
this.emitState(agent);
|
|
1605
|
+
}
|
|
1606
|
+
void this.refreshRuntimeInfo(agent);
|
|
1607
|
+
break;
|
|
1608
|
+
case "turn_failed":
|
|
1609
|
+
this.logger.warn({
|
|
1610
|
+
agentId: agent.id,
|
|
1611
|
+
lifecycle: agent.lifecycle,
|
|
1612
|
+
activeForegroundTurnId: agent.activeForegroundTurnId,
|
|
1613
|
+
eventTurnId,
|
|
1614
|
+
error: event.error,
|
|
1615
|
+
code: event.code,
|
|
1616
|
+
diagnostic: event.diagnostic,
|
|
1617
|
+
}, "handleStreamEvent: turn_failed");
|
|
1618
|
+
// For autonomous turns, set error state directly
|
|
1619
|
+
if (!isForegroundEvent) {
|
|
1620
|
+
agent.lifecycle = "error";
|
|
1621
|
+
}
|
|
1622
|
+
agent.lastError = event.error;
|
|
1623
|
+
this.appendSystemErrorTimelineMessage(agent, event.provider, this.formatTurnFailedMessage(event), options);
|
|
1624
|
+
for (const [requestId] of agent.pendingPermissions) {
|
|
1625
|
+
agent.pendingPermissions.delete(requestId);
|
|
1626
|
+
if (!options?.fromHistory) {
|
|
1627
|
+
this.dispatchStream(agent.id, {
|
|
1628
|
+
type: "permission_resolved",
|
|
1629
|
+
provider: event.provider,
|
|
1630
|
+
requestId,
|
|
1631
|
+
resolution: { behavior: "deny", message: "Turn failed" },
|
|
1632
|
+
});
|
|
1633
|
+
}
|
|
1634
|
+
}
|
|
1635
|
+
if (!isForegroundEvent) {
|
|
1636
|
+
this.emitState(agent);
|
|
1637
|
+
}
|
|
1638
|
+
break;
|
|
1639
|
+
case "turn_canceled":
|
|
1640
|
+
this.logger.trace({
|
|
1641
|
+
agentId: agent.id,
|
|
1642
|
+
lifecycle: agent.lifecycle,
|
|
1643
|
+
activeForegroundTurnId: agent.activeForegroundTurnId,
|
|
1644
|
+
eventTurnId,
|
|
1645
|
+
}, "handleStreamEvent: turn_canceled");
|
|
1646
|
+
// For autonomous turns, transition to idle
|
|
1647
|
+
// unless a replacement is pending (avoid idle flash during replace)
|
|
1648
|
+
if (!isForegroundEvent && !agent.pendingReplacement) {
|
|
1649
|
+
agent.lifecycle = "idle";
|
|
1650
|
+
}
|
|
1651
|
+
agent.lastError = undefined;
|
|
1652
|
+
for (const [requestId] of agent.pendingPermissions) {
|
|
1653
|
+
agent.pendingPermissions.delete(requestId);
|
|
1654
|
+
if (!options?.fromHistory) {
|
|
1655
|
+
this.dispatchStream(agent.id, {
|
|
1656
|
+
type: "permission_resolved",
|
|
1657
|
+
provider: event.provider,
|
|
1658
|
+
requestId,
|
|
1659
|
+
resolution: { behavior: "deny", message: "Interrupted" },
|
|
1660
|
+
});
|
|
1661
|
+
}
|
|
1662
|
+
}
|
|
1663
|
+
if (!isForegroundEvent) {
|
|
1664
|
+
this.emitState(agent);
|
|
1665
|
+
}
|
|
1666
|
+
break;
|
|
1667
|
+
case "turn_started":
|
|
1668
|
+
this.logger.trace({
|
|
1669
|
+
agentId: agent.id,
|
|
1670
|
+
lifecycle: agent.lifecycle,
|
|
1671
|
+
activeForegroundTurnId: agent.activeForegroundTurnId,
|
|
1672
|
+
eventTurnId,
|
|
1673
|
+
}, "handleStreamEvent: turn_started");
|
|
1674
|
+
// For autonomous turn_started (no foreground match), set running
|
|
1675
|
+
if (!isForegroundEvent) {
|
|
1676
|
+
agent.lifecycle = "running";
|
|
1677
|
+
this.emitState(agent);
|
|
1678
|
+
}
|
|
1679
|
+
break;
|
|
1680
|
+
case "permission_requested":
|
|
1681
|
+
{
|
|
1682
|
+
const hadPendingPermissions = agent.pendingPermissions.size > 0;
|
|
1683
|
+
agent.pendingPermissions.set(event.request.id, event.request);
|
|
1684
|
+
if (!hadPendingPermissions && !agent.internal) {
|
|
1685
|
+
this.broadcastAgentAttention(agent, "permission");
|
|
1686
|
+
}
|
|
1687
|
+
}
|
|
1688
|
+
this.emitState(agent);
|
|
1689
|
+
break;
|
|
1690
|
+
case "permission_resolved":
|
|
1691
|
+
agent.pendingPermissions.delete(event.requestId);
|
|
1692
|
+
if (!options?.fromHistory && agent.inFlightPermissionResponses.has(event.requestId)) {
|
|
1693
|
+
agent.bufferedPermissionResolutions.set(event.requestId, event);
|
|
1694
|
+
shouldDispatchEvent = false;
|
|
1695
|
+
break;
|
|
1696
|
+
}
|
|
1697
|
+
this.emitState(agent);
|
|
1698
|
+
break;
|
|
1699
|
+
default:
|
|
1700
|
+
break;
|
|
1701
|
+
}
|
|
1702
|
+
if (!options?.fromHistory && isForegroundEvent && isTurnTerminalEvent(event)) {
|
|
1703
|
+
this.finalizeForegroundTurn(agent);
|
|
1704
|
+
}
|
|
1705
|
+
// Skip dispatching individual stream events during history replay.
|
|
1706
|
+
if (!options?.fromHistory && shouldDispatchEvent) {
|
|
1707
|
+
this.dispatchStream(agent.id, event, timelineRow
|
|
1708
|
+
? {
|
|
1709
|
+
seq: timelineRow.seq,
|
|
1710
|
+
epoch: this.ensureTimelineState(agent).epoch,
|
|
1711
|
+
}
|
|
1712
|
+
: undefined);
|
|
1713
|
+
}
|
|
1714
|
+
}
|
|
1715
|
+
appendSystemErrorTimelineMessage(agent, provider, message, options) {
|
|
1716
|
+
if (options?.fromHistory) {
|
|
1717
|
+
return;
|
|
1718
|
+
}
|
|
1719
|
+
const normalized = message.trim();
|
|
1720
|
+
if (!normalized) {
|
|
1721
|
+
return;
|
|
1722
|
+
}
|
|
1723
|
+
const text = `${SYSTEM_ERROR_PREFIX} ${normalized}`;
|
|
1724
|
+
const lastItem = agent.timelineRows[agent.timelineRows.length - 1]?.item;
|
|
1725
|
+
if (lastItem?.type === "assistant_message" && lastItem.text === text) {
|
|
1726
|
+
return;
|
|
1727
|
+
}
|
|
1728
|
+
const item = { type: "assistant_message", text };
|
|
1729
|
+
const row = this.recordTimeline(agent, item);
|
|
1730
|
+
this.dispatchStream(agent.id, {
|
|
1731
|
+
type: "timeline",
|
|
1732
|
+
item,
|
|
1733
|
+
provider,
|
|
1734
|
+
}, {
|
|
1735
|
+
seq: row.seq,
|
|
1736
|
+
epoch: this.ensureTimelineState(agent).epoch,
|
|
1737
|
+
});
|
|
1738
|
+
}
|
|
1739
|
+
formatTurnFailedMessage(event) {
|
|
1740
|
+
const base = event.error.trim();
|
|
1741
|
+
const parts = [base.length > 0 ? base : "Provider run failed"];
|
|
1742
|
+
const code = event.code?.trim();
|
|
1743
|
+
if (code) {
|
|
1744
|
+
parts.push(`code: ${code}`);
|
|
1745
|
+
}
|
|
1746
|
+
const diagnostic = event.diagnostic?.trim();
|
|
1747
|
+
if (diagnostic && diagnostic !== base) {
|
|
1748
|
+
parts.push(diagnostic);
|
|
1749
|
+
}
|
|
1750
|
+
return parts.join("\n\n");
|
|
1751
|
+
}
|
|
1752
|
+
recordTimeline(agent, item) {
|
|
1753
|
+
const timelineState = this.ensureTimelineState(agent);
|
|
1754
|
+
const row = {
|
|
1755
|
+
seq: timelineState.nextSeq,
|
|
1756
|
+
timestamp: new Date().toISOString(),
|
|
1757
|
+
item,
|
|
1758
|
+
};
|
|
1759
|
+
agent.timelineNextSeq = timelineState.nextSeq + 1;
|
|
1760
|
+
agent.timeline.push(item);
|
|
1761
|
+
timelineState.rows.push(row);
|
|
1762
|
+
if (typeof this.maxTimelineItems === "number" &&
|
|
1763
|
+
agent.timeline.length > this.maxTimelineItems) {
|
|
1764
|
+
const removeCount = agent.timeline.length - this.maxTimelineItems;
|
|
1765
|
+
agent.timeline.splice(0, removeCount);
|
|
1766
|
+
timelineState.rows.splice(0, removeCount);
|
|
1767
|
+
}
|
|
1768
|
+
return row;
|
|
1769
|
+
}
|
|
1770
|
+
emitState(agent) {
|
|
1771
|
+
// Keep attention as an edge-triggered unread signal, not a level signal.
|
|
1772
|
+
this.checkAndSetAttention(agent);
|
|
1773
|
+
this.syncFeaturesFromSession(agent);
|
|
1774
|
+
this.dispatch({
|
|
1775
|
+
type: "agent_state",
|
|
1776
|
+
agent: { ...agent },
|
|
1777
|
+
});
|
|
1778
|
+
}
|
|
1779
|
+
syncFeaturesFromSession(agent) {
|
|
1780
|
+
if ("session" in agent && agent.session?.features) {
|
|
1781
|
+
agent.features = agent.session.features;
|
|
1782
|
+
}
|
|
1783
|
+
}
|
|
1784
|
+
checkAndSetAttention(agent) {
|
|
1785
|
+
const previousStatus = this.previousStatuses.get(agent.id);
|
|
1786
|
+
const currentStatus = agent.lifecycle;
|
|
1787
|
+
// Track the new status
|
|
1788
|
+
this.previousStatuses.set(agent.id, currentStatus);
|
|
1789
|
+
// Skip attention tracking for internal agents
|
|
1790
|
+
if (agent.internal) {
|
|
1791
|
+
return;
|
|
1792
|
+
}
|
|
1793
|
+
// Skip if already requires attention
|
|
1794
|
+
if (agent.attention.requiresAttention) {
|
|
1795
|
+
return;
|
|
1796
|
+
}
|
|
1797
|
+
// Check if agent transitioned from running to idle (finished)
|
|
1798
|
+
if (previousStatus === "running" && currentStatus === "idle") {
|
|
1799
|
+
agent.attention = {
|
|
1800
|
+
requiresAttention: true,
|
|
1801
|
+
attentionReason: "finished",
|
|
1802
|
+
attentionTimestamp: new Date(),
|
|
1803
|
+
};
|
|
1804
|
+
this.broadcastAgentAttention(agent, "finished");
|
|
1805
|
+
this.enqueueBackgroundPersist(agent);
|
|
1806
|
+
return;
|
|
1807
|
+
}
|
|
1808
|
+
// Check if agent entered error state
|
|
1809
|
+
if (previousStatus !== "error" && currentStatus === "error") {
|
|
1810
|
+
agent.attention = {
|
|
1811
|
+
requiresAttention: true,
|
|
1812
|
+
attentionReason: "error",
|
|
1813
|
+
attentionTimestamp: new Date(),
|
|
1814
|
+
};
|
|
1815
|
+
this.broadcastAgentAttention(agent, "error");
|
|
1816
|
+
this.enqueueBackgroundPersist(agent);
|
|
1817
|
+
return;
|
|
1818
|
+
}
|
|
1819
|
+
}
|
|
1820
|
+
enqueueBackgroundPersist(agent) {
|
|
1821
|
+
const task = this.persistSnapshot(agent).catch((err) => {
|
|
1822
|
+
this.logger.error({ err, agentId: agent.id }, "Failed to persist agent snapshot");
|
|
1823
|
+
});
|
|
1824
|
+
this.trackBackgroundTask(task);
|
|
1825
|
+
}
|
|
1826
|
+
trackBackgroundTask(task) {
|
|
1827
|
+
this.backgroundTasks.add(task);
|
|
1828
|
+
void task.finally(() => {
|
|
1829
|
+
this.backgroundTasks.delete(task);
|
|
1830
|
+
});
|
|
1831
|
+
}
|
|
1832
|
+
/**
|
|
1833
|
+
* Flush any background persistence work (best-effort).
|
|
1834
|
+
* Used by daemon shutdown paths to avoid unhandled rejections after cleanup.
|
|
1835
|
+
*/
|
|
1836
|
+
async flush() {
|
|
1837
|
+
// Drain tasks, including tasks spawned while awaiting.
|
|
1838
|
+
while (this.backgroundTasks.size > 0) {
|
|
1839
|
+
const pending = Array.from(this.backgroundTasks);
|
|
1840
|
+
await Promise.allSettled(pending);
|
|
1841
|
+
}
|
|
1842
|
+
}
|
|
1843
|
+
broadcastAgentAttention(agent, reason) {
|
|
1844
|
+
this.onAgentAttention?.({
|
|
1845
|
+
agentId: agent.id,
|
|
1846
|
+
provider: agent.provider,
|
|
1847
|
+
reason,
|
|
1848
|
+
});
|
|
1849
|
+
}
|
|
1850
|
+
dispatchStream(agentId, event, metadata) {
|
|
1851
|
+
this.dispatch({ type: "agent_stream", agentId, event, ...metadata });
|
|
1852
|
+
}
|
|
1853
|
+
dispatch(event) {
|
|
1854
|
+
for (const subscriber of this.subscribers) {
|
|
1855
|
+
if (subscriber.agentId &&
|
|
1856
|
+
event.type === "agent_stream" &&
|
|
1857
|
+
subscriber.agentId !== event.agentId) {
|
|
1858
|
+
continue;
|
|
1859
|
+
}
|
|
1860
|
+
if (subscriber.agentId &&
|
|
1861
|
+
event.type === "agent_state" &&
|
|
1862
|
+
subscriber.agentId !== event.agent.id) {
|
|
1863
|
+
continue;
|
|
1864
|
+
}
|
|
1865
|
+
// Skip internal agents for global subscribers (those without a specific agentId)
|
|
1866
|
+
if (!subscriber.agentId) {
|
|
1867
|
+
if (event.type === "agent_state" && event.agent.internal) {
|
|
1868
|
+
continue;
|
|
1869
|
+
}
|
|
1870
|
+
if (event.type === "agent_stream") {
|
|
1871
|
+
const agent = this.agents.get(event.agentId);
|
|
1872
|
+
if (agent?.internal) {
|
|
1873
|
+
continue;
|
|
1874
|
+
}
|
|
1875
|
+
}
|
|
1876
|
+
}
|
|
1877
|
+
subscriber.callback(event);
|
|
1878
|
+
}
|
|
1879
|
+
}
|
|
1880
|
+
async normalizeConfig(config) {
|
|
1881
|
+
const normalized = { ...config };
|
|
1882
|
+
// Always resolve cwd to absolute path for consistent history file lookup
|
|
1883
|
+
if (normalized.cwd) {
|
|
1884
|
+
normalized.cwd = resolve(normalized.cwd);
|
|
1885
|
+
try {
|
|
1886
|
+
const cwdStats = await stat(normalized.cwd);
|
|
1887
|
+
if (!cwdStats.isDirectory()) {
|
|
1888
|
+
throw new Error(`Working directory is not a directory: ${normalized.cwd}`);
|
|
1889
|
+
}
|
|
1890
|
+
}
|
|
1891
|
+
catch (error) {
|
|
1892
|
+
if (error instanceof Error &&
|
|
1893
|
+
"code" in error &&
|
|
1894
|
+
error.code === "ENOENT") {
|
|
1895
|
+
throw new Error(`Working directory does not exist: ${normalized.cwd}`);
|
|
1896
|
+
}
|
|
1897
|
+
if (error instanceof Error) {
|
|
1898
|
+
throw error;
|
|
1899
|
+
}
|
|
1900
|
+
throw new Error(`Failed to access working directory: ${normalized.cwd}`);
|
|
1901
|
+
}
|
|
1902
|
+
}
|
|
1903
|
+
if (typeof normalized.model === "string") {
|
|
1904
|
+
const trimmed = normalized.model.trim();
|
|
1905
|
+
normalized.model = trimmed.length > 0 && trimmed !== "default" ? trimmed : undefined;
|
|
1906
|
+
}
|
|
1907
|
+
if (!normalized.model) {
|
|
1908
|
+
const client = this.clients.get(normalized.provider);
|
|
1909
|
+
if (client) {
|
|
1910
|
+
try {
|
|
1911
|
+
const models = await client.listModels();
|
|
1912
|
+
const defaultModel = models.find((model) => model.isDefault) ?? models[0];
|
|
1913
|
+
if (defaultModel) {
|
|
1914
|
+
normalized.model = defaultModel.id;
|
|
1915
|
+
}
|
|
1916
|
+
}
|
|
1917
|
+
catch {
|
|
1918
|
+
// Provider may not support model listing — leave model undefined
|
|
1919
|
+
}
|
|
1920
|
+
}
|
|
1921
|
+
}
|
|
1922
|
+
if (!normalized.modeId) {
|
|
1923
|
+
try {
|
|
1924
|
+
normalized.modeId =
|
|
1925
|
+
getAgentProviderDefinition(normalized.provider).defaultModeId ?? undefined;
|
|
1926
|
+
}
|
|
1927
|
+
catch {
|
|
1928
|
+
// Unknown provider
|
|
1929
|
+
}
|
|
1930
|
+
}
|
|
1931
|
+
return normalized;
|
|
1932
|
+
}
|
|
1933
|
+
buildLaunchContext(agentId) {
|
|
1934
|
+
return {
|
|
1935
|
+
env: {
|
|
1936
|
+
SEAWORK_AGENT_ID: agentId,
|
|
1937
|
+
},
|
|
1938
|
+
};
|
|
1939
|
+
}
|
|
1940
|
+
requireClient(provider) {
|
|
1941
|
+
const client = this.clients.get(provider);
|
|
1942
|
+
if (!client) {
|
|
1943
|
+
throw new Error(`No client registered for provider '${provider}'`);
|
|
1944
|
+
}
|
|
1945
|
+
return client;
|
|
1946
|
+
}
|
|
1947
|
+
requireAgent(id) {
|
|
1948
|
+
const normalizedId = validateAgentId(id, "requireAgent");
|
|
1949
|
+
const agent = this.agents.get(normalizedId);
|
|
1950
|
+
if (!agent) {
|
|
1951
|
+
throw new Error(`Unknown agent '${normalizedId}'`);
|
|
1952
|
+
}
|
|
1953
|
+
return agent;
|
|
1954
|
+
}
|
|
1955
|
+
}
|
|
1956
|
+
//# sourceMappingURL=agent-manager.js.map
|