@otto-code/server 0.5.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 +31 -0
- package/README.md +111 -0
- package/dist/scripts/dev-runner.js +37 -0
- package/dist/scripts/mcp-stdio-socket-bridge-cli.mjs +62 -0
- package/dist/scripts/supervisor-entrypoint.js +141 -0
- package/dist/scripts/supervisor-log-config.js +31 -0
- package/dist/scripts/supervisor.js +252 -0
- package/dist/server/executable-resolution/executable-resolution.d.ts +10 -0
- package/dist/server/executable-resolution/executable-resolution.js +118 -0
- package/dist/server/executable-resolution/windows.d.ts +18 -0
- package/dist/server/executable-resolution/windows.js +65 -0
- package/dist/server/server/agent/activity-curator.d.ts +28 -0
- package/dist/server/server/agent/activity-curator.js +227 -0
- package/dist/server/server/agent/agent-archive.d.ts +11 -0
- package/dist/server/server/agent/agent-archive.js +16 -0
- package/dist/server/server/agent/agent-loading.d.ts +14 -0
- package/dist/server/server/agent/agent-loading.js +54 -0
- package/dist/server/server/agent/agent-manager.d.ts +470 -0
- package/dist/server/server/agent/agent-manager.js +3049 -0
- package/dist/server/server/agent/agent-personalities.d.ts +59 -0
- package/dist/server/server/agent/agent-personalities.js +103 -0
- package/dist/server/server/agent/agent-projections.d.ts +35 -0
- package/dist/server/server/agent/agent-projections.js +488 -0
- package/dist/server/server/agent/agent-prompt.d.ts +76 -0
- package/dist/server/server/agent/agent-prompt.js +230 -0
- package/dist/server/server/agent/agent-response-loop.d.ts +72 -0
- package/dist/server/server/agent/agent-response-loop.js +333 -0
- package/dist/server/server/agent/agent-sdk-types.d.ts +736 -0
- package/dist/server/server/agent/agent-sdk-types.js +22 -0
- package/dist/server/server/agent/agent-storage.d.ts +147 -0
- package/dist/server/server/agent/agent-storage.js +326 -0
- package/dist/server/server/agent/agent-stream-coalescer.d.ts +41 -0
- package/dist/server/server/agent/agent-stream-coalescer.js +166 -0
- package/dist/server/server/agent/agent-timeline-store-types.d.ts +50 -0
- package/dist/server/server/agent/agent-timeline-store-types.js +2 -0
- package/dist/server/server/agent/agent-timeline-store.d.ts +27 -0
- package/dist/server/server/agent/agent-timeline-store.js +230 -0
- package/dist/server/server/agent/audio-utils.d.ts +3 -0
- package/dist/server/server/agent/audio-utils.js +19 -0
- package/dist/server/server/agent/create-agent/create.d.ts +91 -0
- package/dist/server/server/agent/create-agent/create.js +370 -0
- package/dist/server/server/agent/create-agent-lifecycle-dispatch.d.ts +54 -0
- package/dist/server/server/agent/create-agent-lifecycle-dispatch.js +152 -0
- package/dist/server/server/agent/create-agent-mode.d.ts +13 -0
- package/dist/server/server/agent/create-agent-mode.js +62 -0
- package/dist/server/server/agent/create-agent-title.d.ts +10 -0
- package/dist/server/server/agent/create-agent-title.js +34 -0
- package/dist/server/server/agent/dictation-debug.d.ts +13 -0
- package/dist/server/server/agent/dictation-debug.js +50 -0
- package/dist/server/server/agent/effort-levels.d.ts +2 -0
- package/dist/server/server/agent/effort-levels.js +7 -0
- package/dist/server/server/agent/foreground-run-state.d.ts +50 -0
- package/dist/server/server/agent/foreground-run-state.js +160 -0
- package/dist/server/server/agent/import-sessions.d.ts +47 -0
- package/dist/server/server/agent/import-sessions.js +145 -0
- package/dist/server/server/agent/lifecycle-command.d.ts +73 -0
- package/dist/server/server/agent/lifecycle-command.js +92 -0
- package/dist/server/server/agent/mcp-server.d.ts +5 -0
- package/dist/server/server/agent/mcp-server.js +66 -0
- package/dist/server/server/agent/mcp-shared.d.ts +192 -0
- package/dist/server/server/agent/mcp-shared.js +201 -0
- package/dist/server/server/agent/pcm16-resampler.d.ts +14 -0
- package/dist/server/server/agent/pcm16-resampler.js +63 -0
- package/dist/server/server/agent/permission-response.d.ts +15 -0
- package/dist/server/server/agent/permission-response.js +14 -0
- package/dist/server/server/agent/personality-stats-store.d.ts +23 -0
- package/dist/server/server/agent/personality-stats-store.js +65 -0
- package/dist/server/server/agent/prompt-attachments.d.ts +7 -0
- package/dist/server/server/agent/prompt-attachments.js +117 -0
- package/dist/server/server/agent/provider-history-timestamps.d.ts +2 -0
- package/dist/server/server/agent/provider-history-timestamps.js +16 -0
- package/dist/server/server/agent/provider-launch-config.d.ts +43 -0
- package/dist/server/server/agent/provider-launch-config.js +167 -0
- package/dist/server/server/agent/provider-notices.d.ts +3 -0
- package/dist/server/server/agent/provider-notices.js +5 -0
- package/dist/server/server/agent/provider-registry.d.ts +41 -0
- package/dist/server/server/agent/provider-registry.js +532 -0
- package/dist/server/server/agent/provider-session-import.d.ts +10 -0
- package/dist/server/server/agent/provider-session-import.js +49 -0
- package/dist/server/server/agent/provider-snapshot-manager.d.ts +142 -0
- package/dist/server/server/agent/provider-snapshot-manager.js +690 -0
- package/dist/server/server/agent/providers/acp-agent.d.ts +353 -0
- package/dist/server/server/agent/providers/acp-agent.js +2439 -0
- package/dist/server/server/agent/providers/claude/agent.d.ts +66 -0
- package/dist/server/server/agent/providers/claude/agent.js +4535 -0
- package/dist/server/server/agent/providers/claude/feature-definitions.d.ts +8 -0
- package/dist/server/server/agent/providers/claude/feature-definitions.js +24 -0
- package/dist/server/server/agent/providers/claude/model-manifest.d.ts +90 -0
- package/dist/server/server/agent/providers/claude/model-manifest.js +205 -0
- package/dist/server/server/agent/providers/claude/models.d.ts +11 -0
- package/dist/server/server/agent/providers/claude/models.js +97 -0
- package/dist/server/server/agent/providers/claude/partial-json.d.ts +5 -0
- package/dist/server/server/agent/providers/claude/partial-json.js +306 -0
- package/dist/server/server/agent/providers/claude/project-dir.d.ts +6 -0
- package/dist/server/server/agent/providers/claude/project-dir.js +57 -0
- package/dist/server/server/agent/providers/claude/query.d.ts +17 -0
- package/dist/server/server/agent/providers/claude/query.js +86 -0
- package/dist/server/server/agent/providers/claude/rewind.d.ts +30 -0
- package/dist/server/server/agent/providers/claude/rewind.js +30 -0
- package/dist/server/server/agent/providers/claude/sidechain-tracker.d.ts +22 -0
- package/dist/server/server/agent/providers/claude/sidechain-tracker.js +232 -0
- package/dist/server/server/agent/providers/claude/task-notification-tool-call.d.ts +35 -0
- package/dist/server/server/agent/providers/claude/task-notification-tool-call.js +272 -0
- package/dist/server/server/agent/providers/claude/test-rewind-claude-sdk.d.ts +24 -0
- package/dist/server/server/agent/providers/claude/test-rewind-claude-sdk.js +23 -0
- package/dist/server/server/agent/providers/claude/tool-call-detail-parser.d.ts +3 -0
- package/dist/server/server/agent/providers/claude/tool-call-detail-parser.js +130 -0
- package/dist/server/server/agent/providers/claude/tool-call-mapper.d.ts +16 -0
- package/dist/server/server/agent/providers/claude/tool-call-mapper.js +129 -0
- package/dist/server/server/agent/providers/codex/app-server-transport.d.ts +90 -0
- package/dist/server/server/agent/providers/codex/app-server-transport.js +262 -0
- package/dist/server/server/agent/providers/codex/rewind.d.ts +21 -0
- package/dist/server/server/agent/providers/codex/rewind.js +46 -0
- package/dist/server/server/agent/providers/codex/test-utils/fake-app-server.d.ts +36 -0
- package/dist/server/server/agent/providers/codex/test-utils/fake-app-server.js +217 -0
- package/dist/server/server/agent/providers/codex/tool-call-detail-parser.d.ts +12 -0
- package/dist/server/server/agent/providers/codex/tool-call-detail-parser.js +104 -0
- package/dist/server/server/agent/providers/codex/tool-call-mapper.d.ts +24 -0
- package/dist/server/server/agent/providers/codex/tool-call-mapper.js +830 -0
- package/dist/server/server/agent/providers/codex-app-server-agent.d.ts +308 -0
- package/dist/server/server/agent/providers/codex-app-server-agent.js +4515 -0
- package/dist/server/server/agent/providers/codex-feature-definitions.d.ts +11 -0
- package/dist/server/server/agent/providers/codex-feature-definitions.js +45 -0
- package/dist/server/server/agent/providers/copilot-acp-agent.d.ts +26 -0
- package/dist/server/server/agent/providers/copilot-acp-agent.js +188 -0
- package/dist/server/server/agent/providers/cursor-acp-agent.d.ts +15 -0
- package/dist/server/server/agent/providers/cursor-acp-agent.js +18 -0
- package/dist/server/server/agent/providers/diagnostic-utils.d.ts +36 -0
- package/dist/server/server/agent/providers/diagnostic-utils.js +355 -0
- package/dist/server/server/agent/providers/generic-acp-agent.d.ts +42 -0
- package/dist/server/server/agent/providers/generic-acp-agent.js +160 -0
- package/dist/server/server/agent/providers/kiro-acp-agent.d.ts +17 -0
- package/dist/server/server/agent/providers/kiro-acp-agent.js +78 -0
- package/dist/server/server/agent/providers/mock-load-test-agent.d.ts +85 -0
- package/dist/server/server/agent/providers/mock-load-test-agent.js +1010 -0
- package/dist/server/server/agent/providers/mock-slow-provider.d.ts +14 -0
- package/dist/server/server/agent/providers/mock-slow-provider.js +39 -0
- package/dist/server/server/agent/providers/openai-compat-agent.d.ts +422 -0
- package/dist/server/server/agent/providers/openai-compat-agent.js +2536 -0
- package/dist/server/server/agent/providers/openai-compat-feature-definitions.d.ts +40 -0
- package/dist/server/server/agent/providers/openai-compat-feature-definitions.js +82 -0
- package/dist/server/server/agent/providers/openai-compat-mcp.d.ts +87 -0
- package/dist/server/server/agent/providers/openai-compat-mcp.js +353 -0
- package/dist/server/server/agent/providers/openai-compat-otto-tool-permissions.d.ts +29 -0
- package/dist/server/server/agent/providers/openai-compat-otto-tool-permissions.js +96 -0
- package/dist/server/server/agent/providers/openai-compat-tools.d.ts +69 -0
- package/dist/server/server/agent/providers/openai-compat-tools.js +1224 -0
- package/dist/server/server/agent/providers/opencode/paths.d.ts +2 -0
- package/dist/server/server/agent/providers/opencode/paths.js +7 -0
- package/dist/server/server/agent/providers/opencode/rewind.d.ts +18 -0
- package/dist/server/server/agent/providers/opencode/rewind.js +14 -0
- package/dist/server/server/agent/providers/opencode/server-manager.d.ts +91 -0
- package/dist/server/server/agent/providers/opencode/server-manager.js +425 -0
- package/dist/server/server/agent/providers/opencode/test-server-manager.d.ts +25 -0
- package/dist/server/server/agent/providers/opencode/test-server-manager.js +39 -0
- package/dist/server/server/agent/providers/opencode/test-utils/test-opencode-harness.d.ts +92 -0
- package/dist/server/server/agent/providers/opencode/test-utils/test-opencode-harness.js +260 -0
- package/dist/server/server/agent/providers/opencode/tool-call-detail-parser.d.ts +3 -0
- package/dist/server/server/agent/providers/opencode/tool-call-detail-parser.js +256 -0
- package/dist/server/server/agent/providers/opencode/tool-call-mapper.d.ts +13 -0
- package/dist/server/server/agent/providers/opencode/tool-call-mapper.js +53 -0
- package/dist/server/server/agent/providers/opencode-agent.d.ts +286 -0
- package/dist/server/server/agent/providers/opencode-agent.js +2830 -0
- package/dist/server/server/agent/providers/pi/agent.d.ts +132 -0
- package/dist/server/server/agent/providers/pi/agent.js +1649 -0
- package/dist/server/server/agent/providers/pi/cli-runtime.d.ts +21 -0
- package/dist/server/server/agent/providers/pi/cli-runtime.js +261 -0
- package/dist/server/server/agent/providers/pi/history-mapper.d.ts +9 -0
- package/dist/server/server/agent/providers/pi/history-mapper.js +140 -0
- package/dist/server/server/agent/providers/pi/rewind.d.ts +8 -0
- package/dist/server/server/agent/providers/pi/rewind.js +8 -0
- package/dist/server/server/agent/providers/pi/rpc-types.d.ts +208 -0
- package/dist/server/server/agent/providers/pi/rpc-types.js +2 -0
- package/dist/server/server/agent/providers/pi/runtime.d.ts +57 -0
- package/dist/server/server/agent/providers/pi/runtime.js +56 -0
- package/dist/server/server/agent/providers/pi/session-descriptor.d.ts +16 -0
- package/dist/server/server/agent/providers/pi/session-descriptor.js +346 -0
- package/dist/server/server/agent/providers/pi/test-utils/fake-pi.d.ts +83 -0
- package/dist/server/server/agent/providers/pi/test-utils/fake-pi.js +184 -0
- package/dist/server/server/agent/providers/pi/tool-call-mapper.d.ts +112 -0
- package/dist/server/server/agent/providers/pi/tool-call-mapper.js +284 -0
- package/dist/server/server/agent/providers/provider-image-output.d.ts +25 -0
- package/dist/server/server/agent/providers/provider-image-output.js +111 -0
- package/dist/server/server/agent/providers/provider-runner.d.ts +27 -0
- package/dist/server/server/agent/providers/provider-runner.js +81 -0
- package/dist/server/server/agent/providers/test-utils/session-stream-adapter.d.ts +3 -0
- package/dist/server/server/agent/providers/test-utils/session-stream-adapter.js +60 -0
- package/dist/server/server/agent/providers/tool-call-detail-primitives.d.ts +884 -0
- package/dist/server/server/agent/providers/tool-call-detail-primitives.js +778 -0
- package/dist/server/server/agent/providers/tool-call-mapper-utils.d.ts +25 -0
- package/dist/server/server/agent/providers/tool-call-mapper-utils.js +208 -0
- package/dist/server/server/agent/providers/trae-acp-agent.d.ts +15 -0
- package/dist/server/server/agent/providers/trae-acp-agent.js +18 -0
- package/dist/server/server/agent/recordings-debug.d.ts +3 -0
- package/dist/server/server/agent/recordings-debug.js +19 -0
- package/dist/server/server/agent/rewind/rewind.d.ts +10 -0
- package/dist/server/server/agent/rewind/rewind.js +29 -0
- package/dist/server/server/agent/rewind/test-rewind-session.d.ts +48 -0
- package/dist/server/server/agent/rewind/test-rewind-session.js +88 -0
- package/dist/server/server/agent/runtime-mcp-config.d.ts +14 -0
- package/dist/server/server/agent/runtime-mcp-config.js +53 -0
- package/dist/server/server/agent/structured-generation-providers.d.ts +29 -0
- package/dist/server/server/agent/structured-generation-providers.js +236 -0
- package/dist/server/server/agent/stt-debug.d.ts +10 -0
- package/dist/server/server/agent/stt-debug.js +33 -0
- package/dist/server/server/agent/stt-manager.d.ts +38 -0
- package/dist/server/server/agent/stt-manager.js +246 -0
- package/dist/server/server/agent/system-prompt.d.ts +2 -0
- package/dist/server/server/agent/system-prompt.js +8 -0
- package/dist/server/server/agent/timeline-append.d.ts +10 -0
- package/dist/server/server/agent/timeline-append.js +27 -0
- package/dist/server/server/agent/timeline-projection.d.ts +71 -0
- package/dist/server/server/agent/timeline-projection.js +373 -0
- package/dist/server/server/agent/tools/otto-tools.d.ts +80 -0
- package/dist/server/server/agent/tools/otto-tools.js +2912 -0
- package/dist/server/server/agent/tools/types.d.ts +36 -0
- package/dist/server/server/agent/tools/types.js +2 -0
- package/dist/server/server/agent/tts-debug.d.ts +8 -0
- package/dist/server/server/agent/tts-debug.js +24 -0
- package/dist/server/server/agent/tts-manager.d.ts +41 -0
- package/dist/server/server/agent/tts-manager.js +375 -0
- package/dist/server/server/agent-attention-policy.d.ts +29 -0
- package/dist/server/server/agent-attention-policy.js +36 -0
- package/dist/server/server/artifact/artifact-prompt.d.ts +2 -0
- package/dist/server/server/artifact/artifact-prompt.js +15 -0
- package/dist/server/server/artifact/artifact-service.d.ts +91 -0
- package/dist/server/server/artifact/artifact-service.js +419 -0
- package/dist/server/server/artifact/artifact-store.d.ts +24 -0
- package/dist/server/server/artifact/artifact-store.js +168 -0
- package/dist/server/server/artifact/artifact-watcher.d.ts +34 -0
- package/dist/server/server/artifact/artifact-watcher.js +146 -0
- package/dist/server/server/artifact/html-validator.d.ts +26 -0
- package/dist/server/server/artifact/html-validator.js +92 -0
- package/dist/server/server/atomic-file.d.ts +5 -0
- package/dist/server/server/atomic-file.js +19 -0
- package/dist/server/server/auth.d.ts +38 -0
- package/dist/server/server/auth.js +143 -0
- package/dist/server/server/auto-archive-on-merge/archive-if-safe.d.ts +40 -0
- package/dist/server/server/auto-archive-on-merge/archive-if-safe.js +92 -0
- package/dist/server/server/auto-archive-on-merge/index.d.ts +8 -0
- package/dist/server/server/auto-archive-on-merge/index.js +15 -0
- package/dist/server/server/bootstrap.d.ts +127 -0
- package/dist/server/server/bootstrap.js +1331 -0
- package/dist/server/server/browser-tools/broker.d.ts +45 -0
- package/dist/server/server/browser-tools/broker.js +390 -0
- package/dist/server/server/browser-tools/errors.d.ts +22 -0
- package/dist/server/server/browser-tools/errors.js +27 -0
- package/dist/server/server/browser-tools/policy.d.ts +10 -0
- package/dist/server/server/browser-tools/policy.js +16 -0
- package/dist/server/server/browser-tools/tools.d.ts +24 -0
- package/dist/server/server/browser-tools/tools.js +967 -0
- package/dist/server/server/chat/chat-mentions.d.ts +47 -0
- package/dist/server/server/chat/chat-mentions.js +113 -0
- package/dist/server/server/chat/chat-service.d.ts +78 -0
- package/dist/server/server/chat/chat-service.js +337 -0
- package/dist/server/server/checkout/status-projection.d.ts +19 -0
- package/dist/server/server/checkout/status-projection.js +117 -0
- package/dist/server/server/checkout-diff-manager.d.ts +41 -0
- package/dist/server/server/checkout-diff-manager.js +189 -0
- package/dist/server/server/checkout-git-utils.d.ts +11 -0
- package/dist/server/server/checkout-git-utils.js +34 -0
- package/dist/server/server/client-message-id.d.ts +3 -0
- package/dist/server/server/client-message-id.js +12 -0
- package/dist/server/server/config.d.ts +22 -0
- package/dist/server/server/config.js +330 -0
- package/dist/server/server/connection-offer.d.ts +20 -0
- package/dist/server/server/connection-offer.js +59 -0
- package/dist/server/server/daemon-config-store.d.ts +44 -0
- package/dist/server/server/daemon-config-store.js +530 -0
- package/dist/server/server/daemon-keypair.d.ts +8 -0
- package/dist/server/server/daemon-keypair.js +42 -0
- package/dist/server/server/daemon-version.d.ts +5 -0
- package/dist/server/server/daemon-version.js +22 -0
- package/dist/server/server/daemon-worker.d.ts +2 -0
- package/dist/server/server/daemon-worker.js +251 -0
- package/dist/server/server/dictation/dictation-stream-manager.d.ts +87 -0
- package/dist/server/server/dictation/dictation-stream-manager.js +572 -0
- package/dist/server/server/exports.d.ts +24 -0
- package/dist/server/server/exports.js +24 -0
- package/dist/server/server/file-download/token-store.d.ts +24 -0
- package/dist/server/server/file-download/token-store.js +40 -0
- package/dist/server/server/file-explorer/code-index.d.ts +28 -0
- package/dist/server/server/file-explorer/code-index.js +127 -0
- package/dist/server/server/file-explorer/file-search.d.ts +72 -0
- package/dist/server/server/file-explorer/file-search.js +297 -0
- package/dist/server/server/file-explorer/file-watcher.d.ts +40 -0
- package/dist/server/server/file-explorer/file-watcher.js +0 -0
- package/dist/server/server/file-explorer/gitignore.d.ts +39 -0
- package/dist/server/server/file-explorer/gitignore.js +191 -0
- package/dist/server/server/file-explorer/service.d.ts +103 -0
- package/dist/server/server/file-explorer/service.js +420 -0
- package/dist/server/server/file-upload/index.d.ts +27 -0
- package/dist/server/server/file-upload/index.js +158 -0
- package/dist/server/server/hostnames.d.ts +13 -0
- package/dist/server/server/hostnames.js +94 -0
- package/dist/server/server/json-utils.d.ts +11 -0
- package/dist/server/server/json-utils.js +45 -0
- package/dist/server/server/lifecycle-reasons.d.ts +4 -0
- package/dist/server/server/lifecycle-reasons.js +6 -0
- package/dist/server/server/logger.d.ts +29 -0
- package/dist/server/server/logger.js +125 -0
- package/dist/server/server/loop-service.d.ts +234 -0
- package/dist/server/server/loop-service.js +804 -0
- package/dist/server/server/managed-processes/managed-processes.d.ts +76 -0
- package/dist/server/server/managed-processes/managed-processes.js +326 -0
- package/dist/server/server/messages.d.ts +9 -0
- package/dist/server/server/messages.js +29 -0
- package/dist/server/server/migrations/backfill-workspace-id.migration.d.ts +9 -0
- package/dist/server/server/migrations/backfill-workspace-id.migration.js +64 -0
- package/dist/server/server/otto-env.d.ts +13 -0
- package/dist/server/server/otto-env.js +53 -0
- package/dist/server/server/otto-home.d.ts +2 -0
- package/dist/server/server/otto-home.js +19 -0
- package/dist/server/server/otto-worktree-service.d.ts +52 -0
- package/dist/server/server/otto-worktree-service.js +294 -0
- package/dist/server/server/package-version.d.ts +19 -0
- package/dist/server/server/package-version.js +60 -0
- package/dist/server/server/pagination/cursor.d.ts +16 -0
- package/dist/server/server/pagination/cursor.js +62 -0
- package/dist/server/server/pagination/sortable-pager.d.ts +24 -0
- package/dist/server/server/pagination/sortable-pager.js +68 -0
- package/dist/server/server/pairing-offer.d.ts +18 -0
- package/dist/server/server/pairing-offer.js +47 -0
- package/dist/server/server/pairing-qr.d.ts +7 -0
- package/dist/server/server/pairing-qr.js +45 -0
- package/dist/server/server/path-utils.d.ts +5 -0
- package/dist/server/server/path-utils.js +34 -0
- package/dist/server/server/persisted-config.d.ts +417 -0
- package/dist/server/server/persisted-config.js +471 -0
- package/dist/server/server/persistence-hooks.d.ts +31 -0
- package/dist/server/server/persistence-hooks.js +93 -0
- package/dist/server/server/pid-lock.d.ts +32 -0
- package/dist/server/server/pid-lock.js +170 -0
- package/dist/server/server/preview/dev-server-manager.d.ts +108 -0
- package/dist/server/server/preview/dev-server-manager.js +426 -0
- package/dist/server/server/preview/launch-config.d.ts +37 -0
- package/dist/server/server/preview/launch-config.js +63 -0
- package/dist/server/server/preview/preview-tools.d.ts +30 -0
- package/dist/server/server/preview/preview-tools.js +249 -0
- package/dist/server/server/private-files.d.ts +6 -0
- package/dist/server/server/private-files.js +37 -0
- package/dist/server/server/process-diagnostics.d.ts +17 -0
- package/dist/server/server/process-diagnostics.js +22 -0
- package/dist/server/server/push/notifications.d.ts +9 -0
- package/dist/server/server/push/notifications.js +15 -0
- package/dist/server/server/push/push-service.d.ts +20 -0
- package/dist/server/server/push/push-service.js +66 -0
- package/dist/server/server/push/token-store.d.ts +18 -0
- package/dist/server/server/push/token-store.js +68 -0
- package/dist/server/server/relay-transport.d.ts +31 -0
- package/dist/server/server/relay-transport.js +514 -0
- package/dist/server/server/resolve-workspace-id-for-path.d.ts +3 -0
- package/dist/server/server/resolve-workspace-id-for-path.js +41 -0
- package/dist/server/server/resolve-worktree-creation-intent.d.ts +32 -0
- package/dist/server/server/resolve-worktree-creation-intent.js +121 -0
- package/dist/server/server/schedule/cron.d.ts +4 -0
- package/dist/server/server/schedule/cron.js +81 -0
- package/dist/server/server/schedule/service.d.ts +89 -0
- package/dist/server/server/schedule/service.js +882 -0
- package/dist/server/server/schedule/store.d.ts +27 -0
- package/dist/server/server/schedule/store.js +190 -0
- package/dist/server/server/script-health-monitor.d.ts +39 -0
- package/dist/server/server/script-health-monitor.js +159 -0
- package/dist/server/server/script-proxy.d.ts +3 -0
- package/dist/server/server/script-proxy.js +2 -0
- package/dist/server/server/script-route-branch-handler.d.ts +10 -0
- package/dist/server/server/script-route-branch-handler.js +11 -0
- package/dist/server/server/script-status-projection.d.ts +36 -0
- package/dist/server/server/script-status-projection.js +200 -0
- package/dist/server/server/server-id.d.ts +17 -0
- package/dist/server/server/server-id.js +68 -0
- package/dist/server/server/service-proxy.d.ts +237 -0
- package/dist/server/server/service-proxy.js +714 -0
- package/dist/server/server/session/agent-config/agent-config-session.d.ts +59 -0
- package/dist/server/server/session/agent-config/agent-config-session.js +110 -0
- package/dist/server/server/session/agent-updates/agent-updates-service.d.ts +65 -0
- package/dist/server/server/session/agent-updates/agent-updates-service.js +232 -0
- package/dist/server/server/session/artifact/artifact-session.d.ts +45 -0
- package/dist/server/server/session/artifact/artifact-session.js +293 -0
- package/dist/server/server/session/chat/chat-schedule-loop-session.d.ts +120 -0
- package/dist/server/server/session/chat/chat-schedule-loop-session.js +489 -0
- package/dist/server/server/session/checkout/checkout-session.d.ts +146 -0
- package/dist/server/server/session/checkout/checkout-session.js +1000 -0
- package/dist/server/server/session/checkout/git-metadata-generator.d.ts +53 -0
- package/dist/server/server/session/checkout/git-metadata-generator.js +159 -0
- package/dist/server/server/session/daemon/daemon-self-update-session-controller.d.ts +32 -0
- package/dist/server/server/session/daemon/daemon-self-update-session-controller.js +88 -0
- package/dist/server/server/session/daemon/daemon-self-updater.d.ts +32 -0
- package/dist/server/server/session/daemon/daemon-self-updater.js +56 -0
- package/dist/server/server/session/daemon/daemon-session.d.ts +76 -0
- package/dist/server/server/session/daemon/daemon-session.js +148 -0
- package/dist/server/server/session/daemon/diagnostics.d.ts +41 -0
- package/dist/server/server/session/daemon/diagnostics.js +431 -0
- package/dist/server/server/session/daemon/install-origin.d.ts +7 -0
- package/dist/server/server/session/daemon/install-origin.js +64 -0
- package/dist/server/server/session/daemon/npm-global-cli.d.ts +29 -0
- package/dist/server/server/session/daemon/npm-global-cli.js +98 -0
- package/dist/server/server/session/files/workspace-files-session.d.ts +60 -0
- package/dist/server/server/session/files/workspace-files-session.js +518 -0
- package/dist/server/server/session/git-mutation/git-mutation-service.d.ts +34 -0
- package/dist/server/server/session/git-mutation/git-mutation-service.js +71 -0
- package/dist/server/server/session/project-config/project-config-session.d.ts +34 -0
- package/dist/server/server/session/project-config/project-config-session.js +125 -0
- package/dist/server/server/session/provider/provider-catalog-session.d.ts +74 -0
- package/dist/server/server/session/provider/provider-catalog-session.js +343 -0
- package/dist/server/server/session/voice/voice-session.d.ts +167 -0
- package/dist/server/server/session/voice/voice-session.js +912 -0
- package/dist/server/server/session/voice/voice-turn-controller.d.ts +38 -0
- package/dist/server/server/session/voice/voice-turn-controller.js +393 -0
- package/dist/server/server/session/workspace-git-observer/workspace-git-observer-service.d.ts +36 -0
- package/dist/server/server/session/workspace-git-observer/workspace-git-observer-service.js +134 -0
- package/dist/server/server/session/workspace-provisioning/workspace-provisioning-service.d.ts +34 -0
- package/dist/server/server/session/workspace-provisioning/workspace-provisioning-service.js +190 -0
- package/dist/server/server/session/workspace-scripts/workspace-scripts-service.d.ts +41 -0
- package/dist/server/server/session/workspace-scripts/workspace-scripts-service.js +100 -0
- package/dist/server/server/session.d.ts +450 -0
- package/dist/server/server/session.js +4763 -0
- package/dist/server/server/speech/audio.d.ts +10 -0
- package/dist/server/server/speech/audio.js +101 -0
- package/dist/server/server/speech/provider-resolver.d.ts +3 -0
- package/dist/server/server/speech/provider-resolver.js +7 -0
- package/dist/server/server/speech/providers/local/config.d.ts +30 -0
- package/dist/server/server/speech/providers/local/config.js +133 -0
- package/dist/server/server/speech/providers/local/models.d.ts +13 -0
- package/dist/server/server/speech/providers/local/models.js +21 -0
- package/dist/server/server/speech/providers/local/runtime.d.ts +31 -0
- package/dist/server/server/speech/providers/local/runtime.js +135 -0
- package/dist/server/server/speech/providers/local/sherpa/assets/silero_vad.onnx +0 -0
- package/dist/server/server/speech/providers/local/sherpa/model-catalog.d.ts +109 -0
- package/dist/server/server/speech/providers/local/sherpa/model-catalog.js +129 -0
- package/dist/server/server/speech/providers/local/sherpa/model-downloader.d.ts +16 -0
- package/dist/server/server/speech/providers/local/sherpa/model-downloader.js +195 -0
- package/dist/server/server/speech/providers/local/sherpa/sherpa-offline-recognizer.d.ts +49 -0
- package/dist/server/server/speech/providers/local/sherpa/sherpa-offline-recognizer.js +73 -0
- package/dist/server/server/speech/providers/local/sherpa/sherpa-onnx-node-loader.d.ts +9 -0
- package/dist/server/server/speech/providers/local/sherpa/sherpa-onnx-node-loader.js +107 -0
- package/dist/server/server/speech/providers/local/sherpa/sherpa-parakeet-realtime-session.d.ts +28 -0
- package/dist/server/server/speech/providers/local/sherpa/sherpa-parakeet-realtime-session.js +137 -0
- package/dist/server/server/speech/providers/local/sherpa/sherpa-parakeet-stt.d.ts +21 -0
- package/dist/server/server/speech/providers/local/sherpa/sherpa-parakeet-stt.js +133 -0
- package/dist/server/server/speech/providers/local/sherpa/sherpa-runtime-env.d.ts +18 -0
- package/dist/server/server/speech/providers/local/sherpa/sherpa-runtime-env.js +84 -0
- package/dist/server/server/speech/providers/local/sherpa/sherpa-tts.d.ts +24 -0
- package/dist/server/server/speech/providers/local/sherpa/sherpa-tts.js +145 -0
- package/dist/server/server/speech/providers/local/sherpa/silero-vad-provider.d.ts +19 -0
- package/dist/server/server/speech/providers/local/sherpa/silero-vad-provider.js +49 -0
- package/dist/server/server/speech/providers/local/sherpa/silero-vad-session.d.ts +38 -0
- package/dist/server/server/speech/providers/local/sherpa/silero-vad-session.js +176 -0
- package/dist/server/server/speech/providers/local/sherpa/tts-voices.d.ts +10 -0
- package/dist/server/server/speech/providers/local/sherpa/tts-voices.js +86 -0
- package/dist/server/server/speech/providers/local/worker-bytes.d.ts +4 -0
- package/dist/server/server/speech/providers/local/worker-bytes.js +9 -0
- package/dist/server/server/speech/providers/local/worker-client.d.ts +93 -0
- package/dist/server/server/speech/providers/local/worker-client.js +591 -0
- package/dist/server/server/speech/providers/local/worker-process.d.ts +2 -0
- package/dist/server/server/speech/providers/local/worker-process.js +270 -0
- package/dist/server/server/speech/providers/local/worker-protocol.d.ts +96 -0
- package/dist/server/server/speech/providers/local/worker-protocol.js +2 -0
- package/dist/server/server/speech/providers/openai/config.d.ts +19 -0
- package/dist/server/server/speech/providers/openai/config.js +160 -0
- package/dist/server/server/speech/providers/openai/runtime.d.ts +29 -0
- package/dist/server/server/speech/providers/openai/runtime.js +102 -0
- package/dist/server/server/speech/providers/openai/stt.d.ts +23 -0
- package/dist/server/server/speech/providers/openai/stt.js +209 -0
- package/dist/server/server/speech/providers/openai/tts.d.ts +20 -0
- package/dist/server/server/speech/providers/openai/tts.js +70 -0
- package/dist/server/server/speech/speech-config-resolver.d.ts +11 -0
- package/dist/server/server/speech/speech-config-resolver.js +132 -0
- package/dist/server/server/speech/speech-provider.d.ts +71 -0
- package/dist/server/server/speech/speech-provider.js +2 -0
- package/dist/server/server/speech/speech-runtime.d.ts +67 -0
- package/dist/server/server/speech/speech-runtime.js +615 -0
- package/dist/server/server/speech/speech-settings-options.d.ts +14 -0
- package/dist/server/server/speech/speech-settings-options.js +73 -0
- package/dist/server/server/speech/speech-types.d.ts +22 -0
- package/dist/server/server/speech/speech-types.js +8 -0
- package/dist/server/server/speech/turn-detection-provider.d.ts +23 -0
- package/dist/server/server/speech/turn-detection-provider.js +2 -0
- package/dist/server/server/utils/diff-highlighter.d.ts +57 -0
- package/dist/server/server/utils/diff-highlighter.js +279 -0
- package/dist/server/server/voice-config.d.ts +15 -0
- package/dist/server/server/voice-config.js +54 -0
- package/dist/server/server/voice-permission-policy.d.ts +4 -0
- package/dist/server/server/voice-permission-policy.js +13 -0
- package/dist/server/server/voice-types.d.ts +12 -0
- package/dist/server/server/voice-types.js +2 -0
- package/dist/server/server/web-ui.d.ts +10 -0
- package/dist/server/server/web-ui.js +205 -0
- package/dist/server/server/websocket/runtime-metrics.d.ts +94 -0
- package/dist/server/server/websocket/runtime-metrics.js +148 -0
- package/dist/server/server/websocket-server.d.ts +187 -0
- package/dist/server/server/websocket-server.js +1766 -0
- package/dist/server/server/workspace-archive-service.d.ts +65 -0
- package/dist/server/server/workspace-archive-service.js +229 -0
- package/dist/server/server/workspace-auto-name.d.ts +55 -0
- package/dist/server/server/workspace-auto-name.js +128 -0
- package/dist/server/server/workspace-bootstrap-dedupe.d.ts +34 -0
- package/dist/server/server/workspace-bootstrap-dedupe.js +23 -0
- package/dist/server/server/workspace-directory.d.ts +93 -0
- package/dist/server/server/workspace-directory.js +422 -0
- package/dist/server/server/workspace-git-metadata.d.ts +24 -0
- package/dist/server/server/workspace-git-metadata.js +55 -0
- package/dist/server/server/workspace-git-service.d.ts +270 -0
- package/dist/server/server/workspace-git-service.js +1423 -0
- package/dist/server/server/workspace-reconciliation-service.d.ts +57 -0
- package/dist/server/server/workspace-reconciliation-service.js +278 -0
- package/dist/server/server/workspace-registry-bootstrap.d.ts +13 -0
- package/dist/server/server/workspace-registry-bootstrap.js +121 -0
- package/dist/server/server/workspace-registry-model.d.ts +55 -0
- package/dist/server/server/workspace-registry-model.js +190 -0
- package/dist/server/server/workspace-registry.d.ts +117 -0
- package/dist/server/server/workspace-registry.js +194 -0
- package/dist/server/server/workspace-script-runtime-store.d.ts +28 -0
- package/dist/server/server/workspace-script-runtime-store.js +78 -0
- package/dist/server/server/workspace-service-env.d.ts +18 -0
- package/dist/server/server/workspace-service-env.js +85 -0
- package/dist/server/server/workspace-service-port-registry.d.ts +19 -0
- package/dist/server/server/workspace-service-port-registry.js +59 -0
- package/dist/server/server/worktree/commands.d.ts +55 -0
- package/dist/server/server/worktree/commands.js +107 -0
- package/dist/server/server/worktree-bootstrap.d.ts +76 -0
- package/dist/server/server/worktree-bootstrap.js +720 -0
- package/dist/server/server/worktree-branch-name-generator.d.ts +35 -0
- package/dist/server/server/worktree-branch-name-generator.js +91 -0
- package/dist/server/server/worktree-core.d.ts +31 -0
- package/dist/server/server/worktree-core.js +105 -0
- package/dist/server/server/worktree-errors.d.ts +12 -0
- package/dist/server/server/worktree-errors.js +28 -0
- package/dist/server/server/worktree-session.d.ts +152 -0
- package/dist/server/server/worktree-session.js +466 -0
- package/dist/server/services/git-hosting/bitbucket-cloud-service.d.ts +24 -0
- package/dist/server/services/git-hosting/bitbucket-cloud-service.js +754 -0
- package/dist/server/services/git-hosting/github-hosting-service.d.ts +4 -0
- package/dist/server/services/git-hosting/github-hosting-service.js +11 -0
- package/dist/server/services/git-hosting/hosting-http-client.d.ts +20 -0
- package/dist/server/services/git-hosting/hosting-http-client.js +131 -0
- package/dist/server/services/git-hosting/request-cache.d.ts +20 -0
- package/dist/server/services/git-hosting/request-cache.js +68 -0
- package/dist/server/services/git-hosting/resolver.d.ts +32 -0
- package/dist/server/services/git-hosting/resolver.js +152 -0
- package/dist/server/services/git-hosting/router.d.ts +4 -0
- package/dist/server/services/git-hosting/router.js +105 -0
- package/dist/server/services/git-hosting/status-poller.d.ts +39 -0
- package/dist/server/services/git-hosting/status-poller.js +149 -0
- package/dist/server/services/git-hosting/types.d.ts +53 -0
- package/dist/server/services/git-hosting/types.js +75 -0
- package/dist/server/services/github-service.d.ts +339 -0
- package/dist/server/services/github-service.js +2109 -0
- package/dist/server/services/quota-fetcher/manifest.d.ts +4 -0
- package/dist/server/services/quota-fetcher/manifest.js +52 -0
- package/dist/server/services/quota-fetcher/provider.d.ts +17 -0
- package/dist/server/services/quota-fetcher/provider.js +2 -0
- package/dist/server/services/quota-fetcher/providers/claude.d.ts +26 -0
- package/dist/server/services/quota-fetcher/providers/claude.js +217 -0
- package/dist/server/services/quota-fetcher/providers/codex.d.ts +23 -0
- package/dist/server/services/quota-fetcher/providers/codex.js +211 -0
- package/dist/server/services/quota-fetcher/providers/copilot.d.ts +17 -0
- package/dist/server/services/quota-fetcher/providers/copilot.js +75 -0
- package/dist/server/services/quota-fetcher/providers/cursor.d.ts +17 -0
- package/dist/server/services/quota-fetcher/providers/cursor.js +123 -0
- package/dist/server/services/quota-fetcher/providers/grok.d.ts +18 -0
- package/dist/server/services/quota-fetcher/providers/grok.js +89 -0
- package/dist/server/services/quota-fetcher/providers/kimi.d.ts +20 -0
- package/dist/server/services/quota-fetcher/providers/kimi.js +89 -0
- package/dist/server/services/quota-fetcher/providers/minimax.d.ts +29 -0
- package/dist/server/services/quota-fetcher/providers/minimax.js +227 -0
- package/dist/server/services/quota-fetcher/providers/zai.d.ts +17 -0
- package/dist/server/services/quota-fetcher/providers/zai.js +58 -0
- package/dist/server/services/quota-fetcher/service.d.ts +28 -0
- package/dist/server/services/quota-fetcher/service.js +58 -0
- package/dist/server/services/quota-fetcher/usage.d.ts +22 -0
- package/dist/server/services/quota-fetcher/usage.js +49 -0
- package/dist/server/terminal/activity/terminal-activity-tracker.d.ts +20 -0
- package/dist/server/terminal/activity/terminal-activity-tracker.js +59 -0
- package/dist/server/terminal/agent-hooks/agent-hook-installer.d.ts +62 -0
- package/dist/server/terminal/agent-hooks/agent-hook-installer.js +117 -0
- package/dist/server/terminal/agent-hooks/claude/claude-settings.d.ts +7 -0
- package/dist/server/terminal/agent-hooks/claude/claude-settings.js +88 -0
- package/dist/server/terminal/agent-hooks/claude/claude.d.ts +4 -0
- package/dist/server/terminal/agent-hooks/claude/claude.js +47 -0
- package/dist/server/terminal/agent-hooks/codex/codex-settings.d.ts +7 -0
- package/dist/server/terminal/agent-hooks/codex/codex-settings.js +99 -0
- package/dist/server/terminal/agent-hooks/codex/codex.d.ts +4 -0
- package/dist/server/terminal/agent-hooks/codex/codex.js +30 -0
- package/dist/server/terminal/agent-hooks/opencode/opencode-plugin.d.ts +4 -0
- package/dist/server/terminal/agent-hooks/opencode/opencode-plugin.js +46 -0
- package/dist/server/terminal/agent-hooks/opencode/opencode.d.ts +3 -0
- package/dist/server/terminal/agent-hooks/opencode/opencode.js +23 -0
- package/dist/server/terminal/agent-hooks/provider-registry.d.ts +24 -0
- package/dist/server/terminal/agent-hooks/provider-registry.js +36 -0
- package/dist/server/terminal/agent-hooks/terminal-agent-hook-setting.d.ts +10 -0
- package/dist/server/terminal/agent-hooks/terminal-agent-hook-setting.js +26 -0
- package/dist/server/terminal/shell-integration/zsh/.zshenv +17 -0
- package/dist/server/terminal/shell-integration/zsh/otto-integration.zsh +32 -0
- package/dist/server/terminal/terminal-capture.d.ts +12 -0
- package/dist/server/terminal/terminal-capture.js +43 -0
- package/dist/server/terminal/terminal-manager-factory.d.ts +6 -0
- package/dist/server/terminal/terminal-manager-factory.js +5 -0
- package/dist/server/terminal/terminal-manager.d.ts +78 -0
- package/dist/server/terminal/terminal-manager.js +319 -0
- package/dist/server/terminal/terminal-output-coalescer.d.ts +34 -0
- package/dist/server/terminal/terminal-output-coalescer.js +73 -0
- package/dist/server/terminal/terminal-restore.d.ts +18 -0
- package/dist/server/terminal/terminal-restore.js +52 -0
- package/dist/server/terminal/terminal-session-controller.d.ts +85 -0
- package/dist/server/terminal/terminal-session-controller.js +819 -0
- package/dist/server/terminal/terminal-ts-loader.mjs +20 -0
- package/dist/server/terminal/terminal-worker-process.d.ts +2 -0
- package/dist/server/terminal/terminal-worker-process.js +304 -0
- package/dist/server/terminal/terminal-worker-protocol.d.ts +119 -0
- package/dist/server/terminal/terminal-worker-protocol.js +2 -0
- package/dist/server/terminal/terminal.d.ts +151 -0
- package/dist/server/terminal/terminal.js +1155 -0
- package/dist/server/terminal/worker-terminal-manager.d.ts +20 -0
- package/dist/server/terminal/worker-terminal-manager.js +688 -0
- package/dist/server/utils/build-metadata-prompt.d.ts +19 -0
- package/dist/server/utils/build-metadata-prompt.js +29 -0
- package/dist/server/utils/checkout-git.d.ts +253 -0
- package/dist/server/utils/checkout-git.js +2241 -0
- package/dist/server/utils/directory-suggestions.d.ts +26 -0
- package/dist/server/utils/directory-suggestions.js +830 -0
- package/dist/server/utils/git-rev-parse-path.d.ts +3 -0
- package/dist/server/utils/git-rev-parse-path.js +21 -0
- package/dist/server/utils/github-remote.d.ts +9 -0
- package/dist/server/utils/github-remote.js +62 -0
- package/dist/server/utils/otto-config-file.d.ts +30 -0
- package/dist/server/utils/otto-config-file.js +90 -0
- package/dist/server/utils/path.d.ts +18 -0
- package/dist/server/utils/path.js +123 -0
- package/dist/server/utils/project-icon.d.ts +39 -0
- package/dist/server/utils/project-icon.js +364 -0
- package/dist/server/utils/promise-timeout.d.ts +9 -0
- package/dist/server/utils/promise-timeout.js +25 -0
- package/dist/server/utils/run-git-command.d.ts +37 -0
- package/dist/server/utils/run-git-command.js +232 -0
- package/dist/server/utils/spawn.d.ts +25 -0
- package/dist/server/utils/spawn.js +59 -0
- package/dist/server/utils/string-command-shell.d.ts +13 -0
- package/dist/server/utils/string-command-shell.js +37 -0
- package/dist/server/utils/tree-kill.d.ts +18 -0
- package/dist/server/utils/tree-kill.js +77 -0
- package/dist/server/utils/windows-command.d.ts +15 -0
- package/dist/server/utils/windows-command.js +41 -0
- package/dist/server/utils/worktree-metadata.d.ts +39 -0
- package/dist/server/utils/worktree-metadata.js +174 -0
- package/dist/server/utils/worktree.d.ts +205 -0
- package/dist/server/utils/worktree.js +1120 -0
- package/dist/server/web-ui/_expo/static/css/xterm-3bb1704bf6cb0876640973dc0244b4cb.css +1 -0
- package/dist/server/web-ui/_expo/static/css/xterm-3bb1704bf6cb0876640973dc0244b4cb.css.br +0 -0
- package/dist/server/web-ui/_expo/static/css/xterm-3bb1704bf6cb0876640973dc0244b4cb.css.gz +0 -0
- package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-bridge-a25c6055b348629689f75618db2c83e1.js +1 -0
- package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-bridge-a25c6055b348629689f75618db2c83e1.js.br +0 -0
- package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-bridge-a25c6055b348629689f75618db2c83e1.js.gz +0 -0
- package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-store-7dc26ea1611eb9d7c2971a6745e5bf5c.js +1 -0
- package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-store-7dc26ea1611eb9d7c2971a6745e5bf5c.js.br +0 -0
- package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-store-7dc26ea1611eb9d7c2971a6745e5bf5c.js.gz +0 -0
- package/dist/server/web-ui/_expo/static/js/web/index-ea91f496cee180fa5bf308c8cc177646.js +3199 -0
- package/dist/server/web-ui/_expo/static/js/web/index-ea91f496cee180fa5bf308c8cc177646.js.br +0 -0
- package/dist/server/web-ui/_expo/static/js/web/index-ea91f496cee180fa5bf308c8cc177646.js.gz +0 -0
- package/dist/server/web-ui/_expo/static/js/web/indexeddb-attachment-store-b6b853a967343babf305512fe8c5958c.js +1 -0
- package/dist/server/web-ui/_expo/static/js/web/indexeddb-attachment-store-b6b853a967343babf305512fe8c5958c.js.br +0 -0
- package/dist/server/web-ui/_expo/static/js/web/indexeddb-attachment-store-b6b853a967343babf305512fe8c5958c.js.gz +0 -0
- package/dist/server/web-ui/_expo/static/js/web/native-file-attachment-store-17223e158406802adc686d02d946573d.js +3 -0
- package/dist/server/web-ui/_expo/static/js/web/native-file-attachment-store-17223e158406802adc686d02d946573d.js.br +0 -0
- package/dist/server/web-ui/_expo/static/js/web/native-file-attachment-store-17223e158406802adc686d02d946573d.js.gz +0 -0
- package/dist/server/web-ui/apple-touch-icon.png +0 -0
- package/dist/server/web-ui/assets/__node_modules/@expo-google-fonts/inter/400Regular/Inter_400Regular.51b6ad87261f18b6433ec52871ddfabc.ttf +0 -0
- package/dist/server/web-ui/assets/__node_modules/@expo-google-fonts/jetbrains-mono/400Regular/JetBrainsMono_400Regular.a0147b5ab9e4946e81879aef45313def.ttf +0 -0
- package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/back-icon-mask.0a328cd9c1afd0afe8e3b1ec5165b1b4.png +0 -0
- package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/back-icon.35ba0eaec5a4f5ed12ca16fabeae451d.png +0 -0
- package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/clear-icon.c94f6478e7ae0cdd9f15de1fcb9e5e55.png +0 -0
- package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/clear-icon.c94f6478e7ae0cdd9f15de1fcb9e5e55@2x.png +0 -0
- package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/clear-icon.c94f6478e7ae0cdd9f15de1fcb9e5e55@3x.png +0 -0
- package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/clear-icon.c94f6478e7ae0cdd9f15de1fcb9e5e55@4x.png +0 -0
- package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/close-icon.808e1b1b9b53114ec2838071a7e6daa7.png +0 -0
- package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/close-icon.808e1b1b9b53114ec2838071a7e6daa7@2x.png +0 -0
- package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/close-icon.808e1b1b9b53114ec2838071a7e6daa7@3x.png +0 -0
- package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/close-icon.808e1b1b9b53114ec2838071a7e6daa7@4x.png +0 -0
- package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/search-icon.286d67d3f74808a60a78d3ebf1a5fb57.png +0 -0
- package/dist/server/web-ui/assets/__node_modules/expo-router/assets/arrow_down.017bc6ba3fc25503e5eb5e53826d48a8.png +0 -0
- package/dist/server/web-ui/assets/__node_modules/expo-router/assets/error.d1ea1496f9057eb392d5bbf3732a61b7.png +0 -0
- package/dist/server/web-ui/assets/__node_modules/expo-router/assets/file.19eeb73b9593a38f8e9f418337fc7d10.png +0 -0
- package/dist/server/web-ui/assets/__node_modules/expo-router/assets/forward.d8b800c443b8972542883e0b9de2bdc6.png +0 -0
- package/dist/server/web-ui/assets/__node_modules/expo-router/assets/pkg.ab19f4cbc543357183a20571f68380a3.png +0 -0
- package/dist/server/web-ui/assets/__node_modules/expo-router/assets/sitemap.412dd9275b6b48ad28f5e3d81bb1f626.png +0 -0
- package/dist/server/web-ui/assets/__node_modules/expo-router/assets/unmatched.20e71bdf79e3a97bf55fd9e164041578.png +0 -0
- package/dist/server/web-ui/assets/assets/images/editor-apps/antigravity.6e91a685c33435e0b466a56db86cf141.png +0 -0
- package/dist/server/web-ui/assets/assets/images/editor-apps/clion.baa1f4eb572e962a6117bdbee95749e4.png +0 -0
- package/dist/server/web-ui/assets/assets/images/editor-apps/cursor.c31d6bce4fe9aadc3fe59962f4c4fcf3.png +0 -0
- package/dist/server/web-ui/assets/assets/images/editor-apps/finder.7f68fc2c475621a672e1be09309d5567.png +0 -0
- package/dist/server/web-ui/assets/assets/images/editor-apps/goland.0796ab2b9be2637fd1cdcde891fad3ca.png +0 -0
- package/dist/server/web-ui/assets/assets/images/editor-apps/intellij-idea.33bc9ff9c0815d8ad43f618c8c244401.png +0 -0
- package/dist/server/web-ui/assets/assets/images/editor-apps/phpstorm.c60b33bc7a8309143d232e14b76554c5.png +0 -0
- package/dist/server/web-ui/assets/assets/images/editor-apps/pycharm.88f25ac484989c45c25323e0adccfa9f.png +0 -0
- package/dist/server/web-ui/assets/assets/images/editor-apps/rider.1a1091dc3a0ede0461f0aa38a54f080a.png +0 -0
- package/dist/server/web-ui/assets/assets/images/editor-apps/rubymine.0e5b66fd7d43f8cbac0f05bba97c9a21.png +0 -0
- package/dist/server/web-ui/assets/assets/images/editor-apps/rustrover.3590ef05482369f41d3484c90272570d.png +0 -0
- package/dist/server/web-ui/assets/assets/images/editor-apps/vscode.832bdb4c685d930f1c864c793703600b.png +0 -0
- package/dist/server/web-ui/assets/assets/images/editor-apps/webstorm.aa5dc2cd8c20cc0a155c4c5c5ab3c5f5.png +0 -0
- package/dist/server/web-ui/assets/assets/images/editor-apps/zed.f3a670b7f9aa226da4fe53fb86f1abbd.png +0 -0
- package/dist/server/web-ui/assets/assets/images/favicon-dark-attention.3b2c331d11122d8f743c73a5878593be.png +0 -0
- package/dist/server/web-ui/assets/assets/images/favicon-dark-running.6077c2ab71021b31c82a42e0b42ad837.png +0 -0
- package/dist/server/web-ui/assets/assets/images/favicon-dark.12984efe6d435a13794a74cd2f858dcd.png +0 -0
- package/dist/server/web-ui/assets/assets/images/favicon-light-attention.3b2c331d11122d8f743c73a5878593be.png +0 -0
- package/dist/server/web-ui/assets/assets/images/favicon-light-running.6077c2ab71021b31c82a42e0b42ad837.png +0 -0
- package/dist/server/web-ui/assets/assets/images/favicon-light.12984efe6d435a13794a74cd2f858dcd.png +0 -0
- package/dist/server/web-ui/assets/assets/images/notification-icon.2394c3b427c0f5ee2bb84864d8fb0f45.png +0 -0
- package/dist/server/web-ui/favicon.ico +0 -0
- package/dist/server/web-ui/index.html +147 -0
- package/dist/server/web-ui/index.html.br +0 -0
- package/dist/server/web-ui/index.html.gz +0 -0
- package/dist/server/web-ui/manifest.json +27 -0
- package/dist/server/web-ui/manifest.json.br +0 -0
- package/dist/server/web-ui/manifest.json.gz +0 -0
- package/dist/server/web-ui/metadata.json +1 -0
- package/dist/server/web-ui/metadata.json.br +1 -0
- package/dist/server/web-ui/metadata.json.gz +0 -0
- package/dist/server/web-ui/pwa-icon-192.png +0 -0
- package/dist/server/web-ui/pwa-icon-512.png +0 -0
- package/dist/server/web-ui/robots.txt +2 -0
- package/dist/src/executable-resolution/executable-resolution.js +118 -0
- package/dist/src/executable-resolution/windows.js +65 -0
- package/dist/src/server/agent/provider-launch-config.js +167 -0
- package/dist/src/server/otto-env.js +53 -0
- package/dist/src/server/otto-home.js +19 -0
- package/dist/src/server/persisted-config.js +471 -0
- package/dist/src/server/pid-lock.js +170 -0
- package/dist/src/server/private-files.js +37 -0
- package/dist/src/server/speech/providers/local/sherpa/sherpa-runtime-env.js +84 -0
- package/dist/src/terminal/shell-integration/zsh/.zshenv +17 -0
- package/dist/src/terminal/shell-integration/zsh/otto-integration.zsh +32 -0
- package/dist/src/utils/spawn.js +59 -0
- package/dist/src/utils/windows-command.js +41 -0
- package/package.json +118 -0
|
@@ -0,0 +1,2912 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { ensureValidJson } from "../../json-utils.js";
|
|
3
|
+
import { resolveEffortOption } from "../effort-levels.js";
|
|
4
|
+
import { resolvePersonality } from "../agent-personalities.js";
|
|
5
|
+
import { isPersonalityRole, normalizePersonalityRoles, personalityHasRole, } from "@otto-code/protocol/agent-personalities";
|
|
6
|
+
import { AgentFeatureSchema, AgentPermissionRequestPayloadSchema, AgentListItemPayloadSchema, AgentPermissionResponseSchema, AgentSnapshotPayloadSchema, } from "../../messages.js";
|
|
7
|
+
import { buildStoredAgentPayload, toAgentListItemPayload, toAgentPayload, } from "../agent-projections.js";
|
|
8
|
+
import { curateAgentActivity } from "../activity-curator.js";
|
|
9
|
+
import { selectItemsByProjectedLimit } from "../timeline-projection.js";
|
|
10
|
+
import { ensureAgentLoaded } from "../agent-loading.js";
|
|
11
|
+
import { isStoredAgentProviderAvailable } from "../../persistence-hooks.js";
|
|
12
|
+
import { killTerminalsForWorkspace, } from "../../workspace-archive-service.js";
|
|
13
|
+
import { createAgentCommand } from "../create-agent/create.js";
|
|
14
|
+
import { expandUserPath, isSameOrDescendantPath, resolvePathFromBase } from "../../path-utils.js";
|
|
15
|
+
import { ScheduleRunSchema, ScheduleSummarySchema, StoredScheduleSchema, } from "@otto-code/protocol/schedule/types";
|
|
16
|
+
import { AgentModelSchema, AgentProviderEnum, AgentStatusEnum, ProviderModeSchema, ProviderSummarySchema, parseDurationString, resolveRequiredProviderModel, sanitizePermissionRequest, serializeSnapshotWithMetadata, toScheduleSummary, waitForAgentWithTimeout, } from "../mcp-shared.js";
|
|
17
|
+
import { sendPromptToAgent, setupFinishNotification } from "../agent-prompt.js";
|
|
18
|
+
import { respondToAgentPermission } from "../permission-response.js";
|
|
19
|
+
import { archiveAgentCommand, cancelAgentRunCommand, closeAgentCommand, setAgentModeCommand, updateAgentCommand, } from "../lifecycle-command.js";
|
|
20
|
+
import { WorktreeRequestError } from "../../worktree-errors.js";
|
|
21
|
+
import { archiveCommand, createOttoWorktreeCommand, listOttoWorktreesCommand, } from "../../worktree/commands.js";
|
|
22
|
+
import { registerBrowserTools } from "../../browser-tools/tools.js";
|
|
23
|
+
import { registerPreviewTools } from "../../preview/preview-tools.js";
|
|
24
|
+
import { StoredArtifactSchema } from "@otto-code/protocol/artifacts/types";
|
|
25
|
+
function parseTimestamp(value) {
|
|
26
|
+
if (!value) {
|
|
27
|
+
return 0;
|
|
28
|
+
}
|
|
29
|
+
const parsed = Date.parse(value);
|
|
30
|
+
return Number.isNaN(parsed) ? 0 : parsed;
|
|
31
|
+
}
|
|
32
|
+
function resolveAgentListActivityTime(agent) {
|
|
33
|
+
return Math.max(parseTimestamp(agent.updatedAt), parseTimestamp(agent.lastUserMessageAt), parseTimestamp(agent.attentionTimestamp), parseTimestamp(agent.archivedAt), parseTimestamp(agent.createdAt));
|
|
34
|
+
}
|
|
35
|
+
function toProviderSummary(entry) {
|
|
36
|
+
return {
|
|
37
|
+
id: entry.provider,
|
|
38
|
+
label: entry.label ?? entry.provider,
|
|
39
|
+
description: entry.description ?? "",
|
|
40
|
+
enabled: entry.enabled,
|
|
41
|
+
modes: entry.modes ?? [],
|
|
42
|
+
status: entry.status === "ready" ? "available" : entry.status,
|
|
43
|
+
...(entry.error ? { error: entry.error } : {}),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
function compareAgentListItems(a, b) {
|
|
47
|
+
const attentionDelta = Number(b.requiresAttention ?? false) - Number(a.requiresAttention ?? false);
|
|
48
|
+
if (attentionDelta !== 0) {
|
|
49
|
+
return attentionDelta;
|
|
50
|
+
}
|
|
51
|
+
const statusOrder = {
|
|
52
|
+
running: 0,
|
|
53
|
+
initializing: 1,
|
|
54
|
+
idle: 2,
|
|
55
|
+
error: 3,
|
|
56
|
+
closed: 4,
|
|
57
|
+
};
|
|
58
|
+
const statusDelta = (statusOrder[a.status] ?? 999) - (statusOrder[b.status] ?? 999);
|
|
59
|
+
if (statusDelta !== 0) {
|
|
60
|
+
return statusDelta;
|
|
61
|
+
}
|
|
62
|
+
return resolveAgentListActivityTime(b) - resolveAgentListActivityTime(a);
|
|
63
|
+
}
|
|
64
|
+
function resolveScheduleProviderAndModel(params) {
|
|
65
|
+
const providerInput = params.provider?.trim() || params.defaultProvider;
|
|
66
|
+
const slashIndex = providerInput.indexOf("/");
|
|
67
|
+
if (slashIndex === -1) {
|
|
68
|
+
return { provider: providerInput };
|
|
69
|
+
}
|
|
70
|
+
const provider = providerInput.slice(0, slashIndex).trim();
|
|
71
|
+
const model = providerInput.slice(slashIndex + 1).trim();
|
|
72
|
+
if (!provider || !model) {
|
|
73
|
+
throw new Error("provider must be <provider> or <provider>/<model>");
|
|
74
|
+
}
|
|
75
|
+
return {
|
|
76
|
+
provider: provider,
|
|
77
|
+
model,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
function resolveScheduleUpdateProviderAndModel(params) {
|
|
81
|
+
const providerInput = params.provider?.trim();
|
|
82
|
+
const modelInput = typeof params.model === "string" ? params.model.trim() : params.model;
|
|
83
|
+
if (params.model !== undefined && modelInput === "") {
|
|
84
|
+
throw new Error("model cannot be empty");
|
|
85
|
+
}
|
|
86
|
+
if (!providerInput) {
|
|
87
|
+
return params.model !== undefined ? { model: modelInput } : {};
|
|
88
|
+
}
|
|
89
|
+
const slashIndex = providerInput.indexOf("/");
|
|
90
|
+
if (slashIndex === -1) {
|
|
91
|
+
return {
|
|
92
|
+
provider: providerInput,
|
|
93
|
+
...(params.model !== undefined ? { model: modelInput } : {}),
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
const provider = providerInput.slice(0, slashIndex).trim();
|
|
97
|
+
const modelFromProvider = providerInput.slice(slashIndex + 1).trim();
|
|
98
|
+
if (!provider || !modelFromProvider) {
|
|
99
|
+
throw new Error("provider must be <provider> or <provider>/<model>");
|
|
100
|
+
}
|
|
101
|
+
if (params.model === null) {
|
|
102
|
+
throw new Error("provider specifies a model but model is null");
|
|
103
|
+
}
|
|
104
|
+
if (typeof modelInput === "string" && modelInput !== modelFromProvider) {
|
|
105
|
+
throw new Error("Conflicting model values provided");
|
|
106
|
+
}
|
|
107
|
+
return {
|
|
108
|
+
provider,
|
|
109
|
+
model: modelInput ?? modelFromProvider,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
function normalizeScheduleCadenceArg(value) {
|
|
113
|
+
if (value === undefined) {
|
|
114
|
+
return undefined;
|
|
115
|
+
}
|
|
116
|
+
const trimmed = value.trim();
|
|
117
|
+
if (!trimmed) {
|
|
118
|
+
return undefined;
|
|
119
|
+
}
|
|
120
|
+
return trimmed;
|
|
121
|
+
}
|
|
122
|
+
function normalizeScheduleTimeZoneArg(value) {
|
|
123
|
+
return normalizeScheduleCadenceArg(value);
|
|
124
|
+
}
|
|
125
|
+
function resolveScheduleUpdateCadence(input) {
|
|
126
|
+
const every = normalizeScheduleCadenceArg(input.every);
|
|
127
|
+
const cron = normalizeScheduleCadenceArg(input.cron);
|
|
128
|
+
const timeZone = normalizeScheduleTimeZoneArg(input.timezone);
|
|
129
|
+
if (every !== undefined && cron !== undefined) {
|
|
130
|
+
throw new Error("Specify at most one of every or cron");
|
|
131
|
+
}
|
|
132
|
+
if (timeZone !== undefined && cron === undefined) {
|
|
133
|
+
throw new Error("timezone can only be used with cron");
|
|
134
|
+
}
|
|
135
|
+
if (every !== undefined) {
|
|
136
|
+
return { type: "every", everyMs: parseDurationString(every) };
|
|
137
|
+
}
|
|
138
|
+
if (cron !== undefined) {
|
|
139
|
+
return {
|
|
140
|
+
type: "cron",
|
|
141
|
+
expression: cron,
|
|
142
|
+
...(timeZone !== undefined ? { timezone: timeZone } : {}),
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
return undefined;
|
|
146
|
+
}
|
|
147
|
+
function resolveScheduleUpdateExpiresAt(input) {
|
|
148
|
+
if (input.expiresIn !== undefined && input.clearExpires) {
|
|
149
|
+
throw new Error("Specify at most one of expiresIn or clearExpires");
|
|
150
|
+
}
|
|
151
|
+
if (input.expiresIn !== undefined) {
|
|
152
|
+
return new Date(Date.now() + parseDurationString(input.expiresIn)).toISOString();
|
|
153
|
+
}
|
|
154
|
+
if (input.clearExpires) {
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
return undefined;
|
|
158
|
+
}
|
|
159
|
+
function buildScheduleUpdateInput(input) {
|
|
160
|
+
const cadence = resolveScheduleUpdateCadence(input);
|
|
161
|
+
const expiresAt = resolveScheduleUpdateExpiresAt(input);
|
|
162
|
+
const providerModelPatch = resolveScheduleUpdateProviderAndModel({
|
|
163
|
+
provider: input.provider,
|
|
164
|
+
model: input.model,
|
|
165
|
+
});
|
|
166
|
+
const newAgentConfig = {
|
|
167
|
+
...(providerModelPatch.provider !== undefined ? { provider: providerModelPatch.provider } : {}),
|
|
168
|
+
...(input.personality !== undefined ? { personality: input.personality } : {}),
|
|
169
|
+
...(providerModelPatch.model !== undefined ? { model: providerModelPatch.model } : {}),
|
|
170
|
+
...(input.mode !== undefined ? { modeId: input.mode } : {}),
|
|
171
|
+
...(input.thinkingOptionId !== undefined ? { thinkingOptionId: input.thinkingOptionId } : {}),
|
|
172
|
+
...(input.cwd !== undefined ? { cwd: input.cwd } : {}),
|
|
173
|
+
};
|
|
174
|
+
return {
|
|
175
|
+
id: input.id,
|
|
176
|
+
...(input.name !== undefined ? { name: input.name } : {}),
|
|
177
|
+
...(input.prompt !== undefined ? { prompt: input.prompt } : {}),
|
|
178
|
+
...(cadence !== undefined ? { cadence } : {}),
|
|
179
|
+
...(input.maxRuns !== undefined ? { maxRuns: input.maxRuns } : {}),
|
|
180
|
+
...(expiresAt !== undefined ? { expiresAt } : {}),
|
|
181
|
+
...(Object.keys(newAgentConfig).length > 0 ? { newAgentConfig } : {}),
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
function resolveChildAgentCwd(params) {
|
|
185
|
+
const lockedCwd = params.lockedCwd?.trim();
|
|
186
|
+
if (lockedCwd) {
|
|
187
|
+
return expandUserPath(lockedCwd);
|
|
188
|
+
}
|
|
189
|
+
const requestedCwd = params.requestedCwd?.trim();
|
|
190
|
+
if (!requestedCwd || !params.allowCustomCwd) {
|
|
191
|
+
return params.parentCwd;
|
|
192
|
+
}
|
|
193
|
+
return resolvePathFromBase(params.parentCwd, requestedCwd);
|
|
194
|
+
}
|
|
195
|
+
const TerminalSummarySchema = z.object({
|
|
196
|
+
id: z.string(),
|
|
197
|
+
name: z.string(),
|
|
198
|
+
cwd: z.string(),
|
|
199
|
+
});
|
|
200
|
+
const WorktreeSummarySchema = z.object({
|
|
201
|
+
path: z.string(),
|
|
202
|
+
createdAt: z.string(),
|
|
203
|
+
branchName: z.string().optional(),
|
|
204
|
+
head: z.string().optional(),
|
|
205
|
+
});
|
|
206
|
+
function resolveTerminalKeyToken(key, literal) {
|
|
207
|
+
if (literal) {
|
|
208
|
+
return key;
|
|
209
|
+
}
|
|
210
|
+
switch (key) {
|
|
211
|
+
case "Enter":
|
|
212
|
+
return "\r";
|
|
213
|
+
case "Tab":
|
|
214
|
+
return "\t";
|
|
215
|
+
case "Escape":
|
|
216
|
+
return "\u001b";
|
|
217
|
+
case "Space":
|
|
218
|
+
return " ";
|
|
219
|
+
case "BSpace":
|
|
220
|
+
return "\u007f";
|
|
221
|
+
case "C-c":
|
|
222
|
+
return "\u0003";
|
|
223
|
+
case "C-d":
|
|
224
|
+
return "\u0004";
|
|
225
|
+
case "C-z":
|
|
226
|
+
return "\u001a";
|
|
227
|
+
case "C-l":
|
|
228
|
+
return "\u000c";
|
|
229
|
+
case "C-a":
|
|
230
|
+
return "\u0001";
|
|
231
|
+
case "C-e":
|
|
232
|
+
return "\u0005";
|
|
233
|
+
default:
|
|
234
|
+
return key;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
function resolveArtifactProviderModel(params) {
|
|
238
|
+
const hasProviderOverride = Boolean(params.providerArg?.trim());
|
|
239
|
+
if (!hasProviderOverride && !params.callerProvider) {
|
|
240
|
+
throw new Error("provider is required outside an agent-scoped session");
|
|
241
|
+
}
|
|
242
|
+
const resolved = resolveScheduleProviderAndModel({
|
|
243
|
+
provider: params.providerArg,
|
|
244
|
+
defaultProvider: params.callerProvider ?? "",
|
|
245
|
+
});
|
|
246
|
+
// Model precedence: explicit model arg > provider/<model> > the caller's
|
|
247
|
+
// own model, but only when the caller's provider is the one generating.
|
|
248
|
+
const model = params.modelArg?.trim() ||
|
|
249
|
+
resolved.model ||
|
|
250
|
+
(!hasProviderOverride ? params.callerModel : undefined) ||
|
|
251
|
+
undefined;
|
|
252
|
+
return { provider: resolved.provider, model };
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Thinking options and modes are provider-scoped, so the caller's own effort
|
|
256
|
+
* level and permission mode only carry over when the caller's provider is the
|
|
257
|
+
* one generating. The mode is a request, not a demand: the artifact service
|
|
258
|
+
* only honors unattended modes and otherwise resolves the provider's
|
|
259
|
+
* unattended default, so an attended caller mode can never stall generation
|
|
260
|
+
* on an approval prompt.
|
|
261
|
+
*/
|
|
262
|
+
function resolveArtifactGenerationSettings(params) {
|
|
263
|
+
const sameProviderAsCaller = params.callerProvider === params.provider;
|
|
264
|
+
return {
|
|
265
|
+
thinkingOptionId: params.thinkingOptionIdArg ??
|
|
266
|
+
(sameProviderAsCaller ? params.callerThinkingOptionId : undefined),
|
|
267
|
+
modeId: params.modeIdArg ?? (sameProviderAsCaller ? params.callerModeId : undefined),
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
const EFFORT_INPUT_DESCRIPTION = "Effort: a canonical level (off, minimal, low, medium, high, xhigh, max), resolved to the nearest option the target model supports, or an exact option id from the model's thinkingOptions in list_models.";
|
|
271
|
+
/**
|
|
272
|
+
* Resolve a requested effort — canonical level or exact option id — against a
|
|
273
|
+
* provider's advertised models. Levels clamp to the nearest supported option.
|
|
274
|
+
* When the target model (or its thinkingOptions) isn't in the snapshot the
|
|
275
|
+
* request passes through unchanged and the provider normalizes it like any
|
|
276
|
+
* hand-typed id.
|
|
277
|
+
*/
|
|
278
|
+
function resolveEffortAgainstModels(params) {
|
|
279
|
+
const definition = params.model
|
|
280
|
+
? params.models.find((candidate) => candidate.id === params.model)
|
|
281
|
+
: (params.models.find((candidate) => candidate.isDefault) ?? params.models[0]);
|
|
282
|
+
const thinkingOptions = definition?.thinkingOptions;
|
|
283
|
+
if (!thinkingOptions || thinkingOptions.length === 0) {
|
|
284
|
+
return params.requested;
|
|
285
|
+
}
|
|
286
|
+
return resolveEffortOption({ requested: params.requested, thinkingOptions }).optionId;
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Fold a resolved personality's prompt + frozen snapshot into a partial agent
|
|
290
|
+
* config, or undefined when there's nothing to carry. Kept top-level so the
|
|
291
|
+
* create_agent handler stays under the complexity budget.
|
|
292
|
+
*/
|
|
293
|
+
function buildPersonalityAgentConfig(brain) {
|
|
294
|
+
if (brain.systemPrompt === undefined && brain.personalitySnapshot === undefined) {
|
|
295
|
+
return undefined;
|
|
296
|
+
}
|
|
297
|
+
const config = {};
|
|
298
|
+
if (brain.systemPrompt !== undefined) {
|
|
299
|
+
config.systemPrompt = brain.systemPrompt;
|
|
300
|
+
}
|
|
301
|
+
if (brain.personalitySnapshot !== undefined) {
|
|
302
|
+
config.personalitySnapshot = brain.personalitySnapshot;
|
|
303
|
+
}
|
|
304
|
+
return config;
|
|
305
|
+
}
|
|
306
|
+
const ArtifactToolSummarySchema = z.object({
|
|
307
|
+
artifactId: z.string(),
|
|
308
|
+
name: z.string(),
|
|
309
|
+
description: z.string(),
|
|
310
|
+
status: z.string(),
|
|
311
|
+
provider: z.string().nullable(),
|
|
312
|
+
model: z.string().nullable(),
|
|
313
|
+
thinkingOptionId: z.string().nullable(),
|
|
314
|
+
modeId: z.string().nullable(),
|
|
315
|
+
projectId: z.string(),
|
|
316
|
+
updatedAt: z.string(),
|
|
317
|
+
errorMessage: z.string().nullable(),
|
|
318
|
+
});
|
|
319
|
+
function toArtifactToolSummary(artifact) {
|
|
320
|
+
return {
|
|
321
|
+
artifactId: artifact.id,
|
|
322
|
+
name: artifact.name,
|
|
323
|
+
description: artifact.description,
|
|
324
|
+
status: artifact.status,
|
|
325
|
+
provider: artifact.generationProvider,
|
|
326
|
+
model: artifact.generationModel,
|
|
327
|
+
thinkingOptionId: artifact.generationThinkingOptionId ?? null,
|
|
328
|
+
modeId: artifact.generationModeId ?? null,
|
|
329
|
+
projectId: artifact.projectId,
|
|
330
|
+
updatedAt: artifact.updatedAt,
|
|
331
|
+
errorMessage: artifact.errorMessage,
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
async function requireArtifact(artifactService, artifactId) {
|
|
335
|
+
const artifact = (await artifactService.list()).find((candidate) => candidate.id === artifactId);
|
|
336
|
+
if (!artifact) {
|
|
337
|
+
throw new Error(`Artifact ${artifactId} not found. Call list_artifacts for ids.`);
|
|
338
|
+
}
|
|
339
|
+
return artifact;
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* Work out the provider/model the update leaves the artifact on: the patch
|
|
343
|
+
* values to store (undefined = unchanged, null model = clear) and the
|
|
344
|
+
* effective pair to resolve a requested effort against.
|
|
345
|
+
*/
|
|
346
|
+
function resolveArtifactUpdateTargets(input, existing) {
|
|
347
|
+
const providerPatch = input.provider
|
|
348
|
+
? resolveScheduleProviderAndModel({
|
|
349
|
+
provider: input.provider,
|
|
350
|
+
defaultProvider: input.provider,
|
|
351
|
+
})
|
|
352
|
+
: undefined;
|
|
353
|
+
// An explicit model arg beats one embedded in provider/<model>.
|
|
354
|
+
const model = input.model !== undefined ? input.model : providerPatch?.model;
|
|
355
|
+
const effortProvider = (providerPatch?.provider ??
|
|
356
|
+
existing.generationProvider);
|
|
357
|
+
const effortModel = model === null ? undefined : (model ?? existing.generationModel ?? undefined);
|
|
358
|
+
return { provider: providerPatch?.provider, model, effortProvider, effortModel };
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* Effort patch for update_artifact: undefined = unchanged, null = clear
|
|
362
|
+
* (the service stores empty string as null), string = resolve strictly.
|
|
363
|
+
*/
|
|
364
|
+
function resolveArtifactUpdateEffort(params) {
|
|
365
|
+
if (params.requested === undefined) {
|
|
366
|
+
return undefined;
|
|
367
|
+
}
|
|
368
|
+
if (params.requested === null) {
|
|
369
|
+
return "";
|
|
370
|
+
}
|
|
371
|
+
return resolveEffortAgainstModels({
|
|
372
|
+
requested: params.requested,
|
|
373
|
+
models: params.models,
|
|
374
|
+
model: params.model,
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
function buildArtifactUpdateServiceInput(input, targets, thinkingPatch) {
|
|
378
|
+
return {
|
|
379
|
+
artifactId: input.artifactId,
|
|
380
|
+
...(input.name !== undefined ? { name: input.name } : {}),
|
|
381
|
+
...(input.description !== undefined ? { description: input.description } : {}),
|
|
382
|
+
...(input.projectId !== undefined ? { projectId: input.projectId } : {}),
|
|
383
|
+
...(targets.provider ? { provider: targets.provider } : {}),
|
|
384
|
+
// The service stores empty string as null (clear back to provider default).
|
|
385
|
+
...(targets.model !== undefined ? { model: targets.model ?? "" } : {}),
|
|
386
|
+
...(thinkingPatch !== undefined ? { thinkingOptionId: thinkingPatch } : {}),
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* Effort resolution for values that may be inherited rather than asked for:
|
|
391
|
+
* an explicit request resolves strictly (unknown values throw), while an
|
|
392
|
+
* effort inherited from a caller on another provider gets clamped, or
|
|
393
|
+
* dropped (undefined) when it can't be mapped.
|
|
394
|
+
*/
|
|
395
|
+
function resolveEffortOrDropInherited(params) {
|
|
396
|
+
if (!params.requested) {
|
|
397
|
+
return undefined;
|
|
398
|
+
}
|
|
399
|
+
try {
|
|
400
|
+
return resolveEffortAgainstModels({
|
|
401
|
+
requested: params.requested,
|
|
402
|
+
models: params.models ?? [],
|
|
403
|
+
model: params.model,
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
catch (error) {
|
|
407
|
+
if (params.explicit) {
|
|
408
|
+
throw error;
|
|
409
|
+
}
|
|
410
|
+
return undefined;
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
const MAX_DERIVED_ARTIFACT_NAME_LENGTH = 60;
|
|
414
|
+
// Fallback title when the agent passes only a description: first non-empty
|
|
415
|
+
// line, stripped of leading markdown markers, truncated at a word boundary.
|
|
416
|
+
function deriveArtifactName(description) {
|
|
417
|
+
const firstLine = description.split("\n").find((line) => line.trim().length > 0) ?? "";
|
|
418
|
+
const cleaned = firstLine
|
|
419
|
+
.replace(/^[#>\-*\s]+/, "")
|
|
420
|
+
.replace(/\s+/g, " ")
|
|
421
|
+
.trim();
|
|
422
|
+
if (!cleaned) {
|
|
423
|
+
return "Untitled artifact";
|
|
424
|
+
}
|
|
425
|
+
if (cleaned.length <= MAX_DERIVED_ARTIFACT_NAME_LENGTH) {
|
|
426
|
+
return cleaned;
|
|
427
|
+
}
|
|
428
|
+
const truncated = cleaned.slice(0, MAX_DERIVED_ARTIFACT_NAME_LENGTH);
|
|
429
|
+
const lastSpace = truncated.lastIndexOf(" ");
|
|
430
|
+
const clipped = lastSpace > MAX_DERIVED_ARTIFACT_NAME_LENGTH / 2 ? truncated.slice(0, lastSpace) : truncated;
|
|
431
|
+
return `${clipped.trimEnd()}…`;
|
|
432
|
+
}
|
|
433
|
+
/**
|
|
434
|
+
* Resolve the projectId to stamp on a created artifact. Artifacts store the
|
|
435
|
+
* project's canonical *root path* (matching what the client's create sheet
|
|
436
|
+
* stores and what the app's project pickers/filters key on) — NOT the
|
|
437
|
+
* registry's opaque grouping key (`remote:host/owner/repo` for repos with a
|
|
438
|
+
* git remote), which nothing client-side can display or match against a
|
|
439
|
+
* workspace. The workspace record only carries the grouping key, so map it
|
|
440
|
+
* through the project registry to the project's rootPath; fall back to the
|
|
441
|
+
* workspace's cwd when the project record is missing.
|
|
442
|
+
*/
|
|
443
|
+
async function resolveArtifactProjectId(params) {
|
|
444
|
+
const explicitProjectId = params.projectIdArg?.trim();
|
|
445
|
+
if (explicitProjectId) {
|
|
446
|
+
return explicitProjectId;
|
|
447
|
+
}
|
|
448
|
+
if (params.callerWorkspaceId && params.workspaceRegistry) {
|
|
449
|
+
const record = await params.workspaceRegistry.get(params.callerWorkspaceId);
|
|
450
|
+
if (record) {
|
|
451
|
+
const project = record.projectId ? await params.projectRegistry?.get(record.projectId) : null;
|
|
452
|
+
if (project?.rootPath) {
|
|
453
|
+
return project.rootPath;
|
|
454
|
+
}
|
|
455
|
+
if (record.cwd) {
|
|
456
|
+
return record.cwd;
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
throw new Error("projectId is required because it could not be derived from your workspace");
|
|
461
|
+
}
|
|
462
|
+
export function createOttoToolCatalog(options) {
|
|
463
|
+
const { agentManager, agentStorage, terminalManager, scheduleService, providerSnapshotManager, readAgentPersonalities, callerAgentId, resolveSpeakHandler, resolveCallerContext, logger, } = options;
|
|
464
|
+
const childLogger = logger.child({ module: "agent", component: "otto-tool-catalog" });
|
|
465
|
+
const callerContext = callerAgentId ? (resolveCallerContext?.(callerAgentId) ?? null) : null;
|
|
466
|
+
const parseToolInput = async (tool, input) => {
|
|
467
|
+
const inputSchema = tool.inputSchema;
|
|
468
|
+
if (!inputSchema) {
|
|
469
|
+
return input;
|
|
470
|
+
}
|
|
471
|
+
const schema = typeof inputSchema === "object" &&
|
|
472
|
+
inputSchema !== null &&
|
|
473
|
+
typeof inputSchema.safeParseAsync === "function"
|
|
474
|
+
? inputSchema
|
|
475
|
+
: z.object(inputSchema).passthrough();
|
|
476
|
+
return schema.parseAsync(input);
|
|
477
|
+
};
|
|
478
|
+
const tools = new Map();
|
|
479
|
+
const registerTool = (name, config,
|
|
480
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Tool handlers are schema-validated at registration boundaries.
|
|
481
|
+
handler) => {
|
|
482
|
+
tools.set(name, {
|
|
483
|
+
name,
|
|
484
|
+
title: config.title,
|
|
485
|
+
description: config.description ?? name,
|
|
486
|
+
inputSchema: config.inputSchema,
|
|
487
|
+
outputSchema: config.outputSchema,
|
|
488
|
+
handler: handler,
|
|
489
|
+
});
|
|
490
|
+
};
|
|
491
|
+
const toCatalog = () => ({
|
|
492
|
+
tools,
|
|
493
|
+
getTool(name) {
|
|
494
|
+
return tools.get(name);
|
|
495
|
+
},
|
|
496
|
+
async executeTool(name, input, context = {}) {
|
|
497
|
+
const tool = tools.get(name);
|
|
498
|
+
if (!tool) {
|
|
499
|
+
throw new Error(`Otto tool not found: ${name}`);
|
|
500
|
+
}
|
|
501
|
+
return tool.handler(await parseToolInput(tool, input), context);
|
|
502
|
+
},
|
|
503
|
+
});
|
|
504
|
+
const buildCronScheduleCadence = (input) => {
|
|
505
|
+
const expression = input.cron?.trim() ?? "";
|
|
506
|
+
if (!expression) {
|
|
507
|
+
throw new Error("cron is required");
|
|
508
|
+
}
|
|
509
|
+
const timezone = normalizeScheduleTimeZoneArg(input.timezone);
|
|
510
|
+
return {
|
|
511
|
+
type: "cron",
|
|
512
|
+
expression,
|
|
513
|
+
...(timezone !== undefined ? { timezone } : {}),
|
|
514
|
+
};
|
|
515
|
+
};
|
|
516
|
+
const buildScheduleExpiry = (expiresIn) => {
|
|
517
|
+
return expiresIn === undefined
|
|
518
|
+
? undefined
|
|
519
|
+
: new Date(Date.now() + parseDurationString(expiresIn)).toISOString();
|
|
520
|
+
};
|
|
521
|
+
const resolveCallerAgent = () => {
|
|
522
|
+
if (!callerAgentId) {
|
|
523
|
+
return null;
|
|
524
|
+
}
|
|
525
|
+
const parentAgent = agentManager.getAgent(callerAgentId);
|
|
526
|
+
if (!parentAgent) {
|
|
527
|
+
throw new Error(`Parent agent ${callerAgentId} not found`);
|
|
528
|
+
}
|
|
529
|
+
return parentAgent;
|
|
530
|
+
};
|
|
531
|
+
const resolveScopedCwd = (requestedCwd, opts) => {
|
|
532
|
+
const callerAgent = resolveCallerAgent();
|
|
533
|
+
if (callerAgent) {
|
|
534
|
+
return resolveChildAgentCwd({
|
|
535
|
+
parentCwd: callerAgent.cwd,
|
|
536
|
+
requestedCwd,
|
|
537
|
+
lockedCwd: callerContext?.lockedCwd,
|
|
538
|
+
allowCustomCwd: callerContext?.allowCustomCwd ?? true,
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
const trimmedCwd = requestedCwd?.trim();
|
|
542
|
+
if (!trimmedCwd) {
|
|
543
|
+
if (opts?.required) {
|
|
544
|
+
throw new Error("cwd is required");
|
|
545
|
+
}
|
|
546
|
+
throw new Error("cwd is required outside an agent-scoped session");
|
|
547
|
+
}
|
|
548
|
+
return expandUserPath(trimmedCwd);
|
|
549
|
+
};
|
|
550
|
+
async function resolveTerminalWorkspaceId(resolvedCwd) {
|
|
551
|
+
// An agent-spawned terminal belongs to the caller agent's workspace. Only if
|
|
552
|
+
// the caller has no workspace do we mint one for the cwd.
|
|
553
|
+
const callerAgent = callerAgentId ? agentManager.getAgent(callerAgentId) : null;
|
|
554
|
+
if (callerAgent?.workspaceId) {
|
|
555
|
+
return callerAgent.workspaceId;
|
|
556
|
+
}
|
|
557
|
+
if (!options.ensureWorkspaceForCreate) {
|
|
558
|
+
throw new Error(callerAgentId
|
|
559
|
+
? `Caller agent ${callerAgentId} has no workspace and workspace minting is not configured`
|
|
560
|
+
: "workspaceId is required outside an agent-scoped session");
|
|
561
|
+
}
|
|
562
|
+
return options.ensureWorkspaceForCreate(resolvedCwd);
|
|
563
|
+
}
|
|
564
|
+
function resolveWorkspaceIdForRename(requestedWorkspaceId) {
|
|
565
|
+
const explicitWorkspaceId = requestedWorkspaceId?.trim();
|
|
566
|
+
if (explicitWorkspaceId) {
|
|
567
|
+
return explicitWorkspaceId;
|
|
568
|
+
}
|
|
569
|
+
if (callerAgentId) {
|
|
570
|
+
const callerAgent = resolveCallerAgent();
|
|
571
|
+
if (!callerAgent?.workspaceId) {
|
|
572
|
+
throw new Error(`Caller agent ${callerAgentId} has no current workspace`);
|
|
573
|
+
}
|
|
574
|
+
return callerAgent.workspaceId;
|
|
575
|
+
}
|
|
576
|
+
throw new Error("workspaceId is required outside an agent-scoped session");
|
|
577
|
+
}
|
|
578
|
+
const buildCallerAgentScheduleConfigExtras = (callerAgent) => {
|
|
579
|
+
return {
|
|
580
|
+
...(callerAgent.config.thinkingOptionId
|
|
581
|
+
? { thinkingOptionId: callerAgent.config.thinkingOptionId }
|
|
582
|
+
: {}),
|
|
583
|
+
...(callerAgent.config.approvalPolicy
|
|
584
|
+
? { approvalPolicy: callerAgent.config.approvalPolicy }
|
|
585
|
+
: {}),
|
|
586
|
+
...(callerAgent.config.sandboxMode ? { sandboxMode: callerAgent.config.sandboxMode } : {}),
|
|
587
|
+
...(typeof callerAgent.config.networkAccess === "boolean"
|
|
588
|
+
? { networkAccess: callerAgent.config.networkAccess }
|
|
589
|
+
: {}),
|
|
590
|
+
...(typeof callerAgent.config.webSearch === "boolean"
|
|
591
|
+
? { webSearch: callerAgent.config.webSearch }
|
|
592
|
+
: {}),
|
|
593
|
+
...(callerAgent.config.title ? { title: callerAgent.config.title } : {}),
|
|
594
|
+
...(callerAgent.config.extra ? { extra: callerAgent.config.extra } : {}),
|
|
595
|
+
...(callerAgent.config.featureValues
|
|
596
|
+
? { featureValues: callerAgent.config.featureValues }
|
|
597
|
+
: {}),
|
|
598
|
+
...(callerAgent.config.systemPrompt ? { systemPrompt: callerAgent.config.systemPrompt } : {}),
|
|
599
|
+
...(callerAgent.config.mcpServers ? { mcpServers: callerAgent.config.mcpServers } : {}),
|
|
600
|
+
};
|
|
601
|
+
};
|
|
602
|
+
const buildCallerAgentScheduleConfig = (callerAgent, params) => {
|
|
603
|
+
const hasProviderOverride = params?.provider !== undefined;
|
|
604
|
+
const resolvedProviderModel = hasProviderOverride
|
|
605
|
+
? resolveScheduleProviderAndModel({
|
|
606
|
+
provider: params?.provider,
|
|
607
|
+
defaultProvider: callerAgent.provider,
|
|
608
|
+
})
|
|
609
|
+
: null;
|
|
610
|
+
const resolvedProvider = resolvedProviderModel?.provider ?? callerAgent.provider;
|
|
611
|
+
let resolvedModel;
|
|
612
|
+
if (resolvedProviderModel?.model) {
|
|
613
|
+
resolvedModel = resolvedProviderModel.model;
|
|
614
|
+
}
|
|
615
|
+
else if (!hasProviderOverride && callerAgent.config.model) {
|
|
616
|
+
resolvedModel = callerAgent.config.model;
|
|
617
|
+
}
|
|
618
|
+
return {
|
|
619
|
+
provider: resolvedProvider,
|
|
620
|
+
cwd: params?.cwd?.trim() ? expandUserPath(params.cwd) : callerAgent.cwd,
|
|
621
|
+
...(callerAgent.currentModeId && callerAgent.provider === resolvedProvider
|
|
622
|
+
? {
|
|
623
|
+
modeId: callerAgent.currentModeId,
|
|
624
|
+
}
|
|
625
|
+
: {}),
|
|
626
|
+
...(resolvedModel ? { model: resolvedModel } : {}),
|
|
627
|
+
...buildCallerAgentScheduleConfigExtras(callerAgent),
|
|
628
|
+
};
|
|
629
|
+
};
|
|
630
|
+
const listProviderModels = async (provider) => {
|
|
631
|
+
const entry = (await providerSnapshotManager.listProviders({ wait: true })).find((candidate) => candidate.provider === provider);
|
|
632
|
+
return entry?.models ?? [];
|
|
633
|
+
};
|
|
634
|
+
const getPersonalityRoster = () => readAgentPersonalities?.() ?? [];
|
|
635
|
+
const findPersonalityByName = (name) => {
|
|
636
|
+
const trimmed = name.trim();
|
|
637
|
+
const roster = getPersonalityRoster();
|
|
638
|
+
return (roster.find((p) => p.name === trimmed) ??
|
|
639
|
+
roster.find((p) => p.name.toLowerCase() === trimmed.toLowerCase()));
|
|
640
|
+
};
|
|
641
|
+
// Only Orchestrator personalities may enumerate or spawn other personalities.
|
|
642
|
+
// A top-level (non-agent) session is the user acting directly and is allowed;
|
|
643
|
+
// an agent-scoped session must have been born from an Orchestrator personality.
|
|
644
|
+
const assertPersonalityOrchestrationAllowed = (action) => {
|
|
645
|
+
if (!callerAgentId) {
|
|
646
|
+
return;
|
|
647
|
+
}
|
|
648
|
+
const caller = agentManager.getAgent(callerAgentId);
|
|
649
|
+
const roles = caller?.config.personalitySnapshot?.roles ?? [];
|
|
650
|
+
if (!roles.includes("orchestrator")) {
|
|
651
|
+
throw new Error(`Only agents spawned from an Orchestrator personality may ${action}. This agent is not an Orchestrator.`);
|
|
652
|
+
}
|
|
653
|
+
};
|
|
654
|
+
// Turn the create_agent brain inputs — a personality name and/or explicit
|
|
655
|
+
// provider/settings — into the concrete provider/model/effort/mode/prompt to
|
|
656
|
+
// spawn with. A personality expands to its resolved snapshot; explicit sibling
|
|
657
|
+
// fields override it per-field (no heuristic substitution). Without a
|
|
658
|
+
// personality this is the plain provider/model path.
|
|
659
|
+
const resolveCreateAgentBrain = async (input) => {
|
|
660
|
+
const resolveThinkingAgainstProvider = async (requested, providerModel) => {
|
|
661
|
+
const { provider, model } = resolveScheduleProviderAndModel({
|
|
662
|
+
provider: providerModel,
|
|
663
|
+
defaultProvider: providerModel,
|
|
664
|
+
});
|
|
665
|
+
return resolveEffortAgainstModels({
|
|
666
|
+
requested,
|
|
667
|
+
models: await listProviderModels(provider),
|
|
668
|
+
model,
|
|
669
|
+
});
|
|
670
|
+
};
|
|
671
|
+
if (input.personalityName) {
|
|
672
|
+
assertPersonalityOrchestrationAllowed("spawn agents from a personality");
|
|
673
|
+
const personality = findPersonalityByName(input.personalityName);
|
|
674
|
+
if (!personality) {
|
|
675
|
+
const names = getPersonalityRoster()
|
|
676
|
+
.map((p) => p.name)
|
|
677
|
+
.join(", ");
|
|
678
|
+
throw new Error(`Personality "${input.personalityName}" not found.${names ? ` Available: ${names}.` : " No personalities are configured on this host."}`);
|
|
679
|
+
}
|
|
680
|
+
const entries = await providerSnapshotManager.listProviders({ cwd: input.cwd, wait: true });
|
|
681
|
+
const resolution = resolvePersonality(personality, entries);
|
|
682
|
+
if (resolution.status === "unavailable") {
|
|
683
|
+
throw new Error(`Personality "${personality.name}" is unavailable here: ${resolution.reason}`);
|
|
684
|
+
}
|
|
685
|
+
const snapshot = resolution.snapshot;
|
|
686
|
+
// Explicit args override the personality per-field.
|
|
687
|
+
const snapshotProviderModel = snapshot.model
|
|
688
|
+
? `${snapshot.provider}/${snapshot.model}`
|
|
689
|
+
: snapshot.provider;
|
|
690
|
+
const providerModel = input.providerOverride?.trim() || snapshotProviderModel;
|
|
691
|
+
const modeId = input.modeOverride ?? snapshot.modeId;
|
|
692
|
+
const thinkingOptionId = input.thinkingOverride
|
|
693
|
+
? await resolveThinkingAgainstProvider(input.thinkingOverride, providerModel)
|
|
694
|
+
: snapshot.thinkingOptionId;
|
|
695
|
+
return {
|
|
696
|
+
providerModel,
|
|
697
|
+
...(modeId !== undefined ? { modeId } : {}),
|
|
698
|
+
...(thinkingOptionId !== undefined ? { thinkingOptionId } : {}),
|
|
699
|
+
...(snapshot.systemPrompt !== undefined ? { systemPrompt: snapshot.systemPrompt } : {}),
|
|
700
|
+
personalitySnapshot: snapshot,
|
|
701
|
+
};
|
|
702
|
+
}
|
|
703
|
+
const providerModel = input.providerOverride?.trim();
|
|
704
|
+
if (!providerModel) {
|
|
705
|
+
throw new Error("Either provider or personality is required.");
|
|
706
|
+
}
|
|
707
|
+
const thinkingOptionId = input.thinkingOverride
|
|
708
|
+
? await resolveThinkingAgainstProvider(input.thinkingOverride, providerModel)
|
|
709
|
+
: undefined;
|
|
710
|
+
return {
|
|
711
|
+
providerModel,
|
|
712
|
+
...(input.modeOverride !== undefined ? { modeId: input.modeOverride } : {}),
|
|
713
|
+
...(thinkingOptionId !== undefined ? { thinkingOptionId } : {}),
|
|
714
|
+
};
|
|
715
|
+
};
|
|
716
|
+
const resolveNewAgentScheduleTarget = (params) => {
|
|
717
|
+
if (!params?.provider?.trim()) {
|
|
718
|
+
throw new Error("provider is required when target is new-agent");
|
|
719
|
+
}
|
|
720
|
+
const callerAgent = resolveCallerAgent();
|
|
721
|
+
if (callerAgent) {
|
|
722
|
+
return {
|
|
723
|
+
type: "new-agent",
|
|
724
|
+
config: buildCallerAgentScheduleConfig(callerAgent, params),
|
|
725
|
+
};
|
|
726
|
+
}
|
|
727
|
+
const resolvedProviderModel = resolveScheduleProviderAndModel({
|
|
728
|
+
provider: params?.provider,
|
|
729
|
+
defaultProvider: params.provider,
|
|
730
|
+
});
|
|
731
|
+
return {
|
|
732
|
+
type: "new-agent",
|
|
733
|
+
config: {
|
|
734
|
+
provider: resolvedProviderModel.provider,
|
|
735
|
+
cwd: params?.cwd?.trim() ? expandUserPath(params.cwd) : process.cwd(),
|
|
736
|
+
...(resolvedProviderModel.model ? { model: resolvedProviderModel.model } : {}),
|
|
737
|
+
},
|
|
738
|
+
};
|
|
739
|
+
};
|
|
740
|
+
const ProviderModelInputSchema = AgentProviderEnum.trim()
|
|
741
|
+
.refine((value) => value.includes("/"), {
|
|
742
|
+
message: "provider must be provider/model, for example codex/gpt-5.4",
|
|
743
|
+
})
|
|
744
|
+
.refine((value) => {
|
|
745
|
+
try {
|
|
746
|
+
resolveRequiredProviderModel(value);
|
|
747
|
+
return true;
|
|
748
|
+
}
|
|
749
|
+
catch {
|
|
750
|
+
return false;
|
|
751
|
+
}
|
|
752
|
+
}, { message: "provider must be provider/model, for example codex/gpt-5.4" });
|
|
753
|
+
const ProviderOrProviderModelInputSchema = AgentProviderEnum.trim()
|
|
754
|
+
.min(1, "provider is required")
|
|
755
|
+
.refine((value) => {
|
|
756
|
+
if (!value.includes("/")) {
|
|
757
|
+
return true;
|
|
758
|
+
}
|
|
759
|
+
try {
|
|
760
|
+
resolveRequiredProviderModel(value);
|
|
761
|
+
return true;
|
|
762
|
+
}
|
|
763
|
+
catch {
|
|
764
|
+
return false;
|
|
765
|
+
}
|
|
766
|
+
}, { message: "provider must be provider or provider/model, for example codex/gpt-5.4" });
|
|
767
|
+
const CreateAgentSettingsInputSchema = z
|
|
768
|
+
.object({
|
|
769
|
+
modeId: z.string().optional().describe("Session mode to configure before the first run."),
|
|
770
|
+
thinkingOptionId: z.string().optional().describe(EFFORT_INPUT_DESCRIPTION),
|
|
771
|
+
features: z
|
|
772
|
+
.record(z.string(), z.unknown())
|
|
773
|
+
.optional()
|
|
774
|
+
.describe("Provider-specific feature values, for example { fast_mode: true } for Codex."),
|
|
775
|
+
})
|
|
776
|
+
.strict();
|
|
777
|
+
const UpdateAgentSettingsInputSchema = z
|
|
778
|
+
.object({
|
|
779
|
+
modeId: z.string().optional().describe("Session mode ID."),
|
|
780
|
+
model: z.string().nullable().optional().describe("Model ID. Pass null to clear."),
|
|
781
|
+
thinkingOptionId: z
|
|
782
|
+
.string()
|
|
783
|
+
.nullable()
|
|
784
|
+
.optional()
|
|
785
|
+
.describe(`${EFFORT_INPUT_DESCRIPTION} Pass null to clear.`),
|
|
786
|
+
features: z
|
|
787
|
+
.record(z.string(), z.unknown())
|
|
788
|
+
.optional()
|
|
789
|
+
.describe("Provider-specific feature values, for example { fast_mode: true } for Codex."),
|
|
790
|
+
})
|
|
791
|
+
.strict();
|
|
792
|
+
const InspectProviderSettingsInputSchema = z
|
|
793
|
+
.object({
|
|
794
|
+
modeId: z.string().optional().describe("Draft session mode ID."),
|
|
795
|
+
model: z.string().optional().describe("Draft model ID."),
|
|
796
|
+
thinkingOptionId: z.string().optional().describe("Draft effort option id."),
|
|
797
|
+
features: z
|
|
798
|
+
.record(z.string(), z.unknown())
|
|
799
|
+
.optional()
|
|
800
|
+
.describe("Draft provider feature values."),
|
|
801
|
+
})
|
|
802
|
+
.strict();
|
|
803
|
+
const AgentRelationshipInputSchema = z.discriminatedUnion("kind", [
|
|
804
|
+
z
|
|
805
|
+
.object({ kind: z.literal("subagent") })
|
|
806
|
+
.strict()
|
|
807
|
+
.describe("Create a child agent under this agent's subagent track."),
|
|
808
|
+
z
|
|
809
|
+
.object({ kind: z.literal("detached") })
|
|
810
|
+
.strict()
|
|
811
|
+
.describe("Create a root agent that does not appear in this agent's subagent track."),
|
|
812
|
+
]);
|
|
813
|
+
const AgentCreateWorktreeTargetInputSchema = z.discriminatedUnion("kind", [
|
|
814
|
+
z
|
|
815
|
+
.object({
|
|
816
|
+
kind: z.literal("branch-off"),
|
|
817
|
+
worktreeSlug: z
|
|
818
|
+
.string()
|
|
819
|
+
.min(1)
|
|
820
|
+
.optional()
|
|
821
|
+
.describe("Optional worktree slug/path label. Omit to let Otto generate one."),
|
|
822
|
+
branchName: z
|
|
823
|
+
.string()
|
|
824
|
+
.min(1)
|
|
825
|
+
.optional()
|
|
826
|
+
.describe("Optional git branch name. Defaults to the worktree slug."),
|
|
827
|
+
baseBranch: z
|
|
828
|
+
.string()
|
|
829
|
+
.min(1)
|
|
830
|
+
.optional()
|
|
831
|
+
.describe("Optional base branch. Defaults to the repository default branch."),
|
|
832
|
+
})
|
|
833
|
+
.strict()
|
|
834
|
+
.describe("Create a new branch in a new Otto worktree."),
|
|
835
|
+
z
|
|
836
|
+
.object({
|
|
837
|
+
kind: z.literal("checkout-branch"),
|
|
838
|
+
branch: z.string().min(1).describe("Existing branch to check out."),
|
|
839
|
+
})
|
|
840
|
+
.strict()
|
|
841
|
+
.describe("Check out an existing branch in a new Otto worktree."),
|
|
842
|
+
z
|
|
843
|
+
.object({
|
|
844
|
+
kind: z.literal("checkout-pr"),
|
|
845
|
+
githubPrNumber: z.number().int().positive().describe("GitHub pull request number."),
|
|
846
|
+
})
|
|
847
|
+
.strict()
|
|
848
|
+
.describe("Check out a GitHub pull request in a new Otto worktree."),
|
|
849
|
+
]);
|
|
850
|
+
const AgentWorkspaceInputSchema = z.discriminatedUnion("kind", [
|
|
851
|
+
z
|
|
852
|
+
.object({
|
|
853
|
+
kind: z.literal("current"),
|
|
854
|
+
cwd: z.string().optional().describe("Optional runtime cwd. Defaults to the caller's cwd."),
|
|
855
|
+
})
|
|
856
|
+
.strict()
|
|
857
|
+
.describe("Use the caller's current workspace."),
|
|
858
|
+
z
|
|
859
|
+
.object({
|
|
860
|
+
kind: z.literal("existing"),
|
|
861
|
+
workspaceId: z.string().min(1).describe("Existing workspace id to attach the agent to."),
|
|
862
|
+
cwd: z
|
|
863
|
+
.string()
|
|
864
|
+
.optional()
|
|
865
|
+
.describe("Optional runtime cwd. Defaults to the existing workspace cwd."),
|
|
866
|
+
})
|
|
867
|
+
.strict()
|
|
868
|
+
.describe("Attach the agent to an existing workspace."),
|
|
869
|
+
z
|
|
870
|
+
.object({
|
|
871
|
+
kind: z.literal("create"),
|
|
872
|
+
source: z.discriminatedUnion("kind", [
|
|
873
|
+
z
|
|
874
|
+
.object({
|
|
875
|
+
kind: z.literal("directory"),
|
|
876
|
+
path: z
|
|
877
|
+
.string()
|
|
878
|
+
.optional()
|
|
879
|
+
.describe("Optional directory path. Defaults to the caller's cwd."),
|
|
880
|
+
})
|
|
881
|
+
.strict(),
|
|
882
|
+
z
|
|
883
|
+
.object({
|
|
884
|
+
kind: z.literal("worktree"),
|
|
885
|
+
cwd: z
|
|
886
|
+
.string()
|
|
887
|
+
.optional()
|
|
888
|
+
.describe("Optional source repository. Defaults to the caller's cwd."),
|
|
889
|
+
target: AgentCreateWorktreeTargetInputSchema,
|
|
890
|
+
})
|
|
891
|
+
.strict(),
|
|
892
|
+
]),
|
|
893
|
+
})
|
|
894
|
+
.strict()
|
|
895
|
+
.describe("Create a new workspace for the agent."),
|
|
896
|
+
]);
|
|
897
|
+
const commonCreateAgentInputSchema = {
|
|
898
|
+
relationship: AgentRelationshipInputSchema.describe("Whether the created agent is a subagent under you or a detached root agent."),
|
|
899
|
+
workspace: AgentWorkspaceInputSchema.describe("Workspace ownership/location for the created agent."),
|
|
900
|
+
title: z
|
|
901
|
+
.string()
|
|
902
|
+
.trim()
|
|
903
|
+
.min(1, "Title is required")
|
|
904
|
+
.max(60, "Title must be 60 characters or fewer")
|
|
905
|
+
.describe("Short descriptive title (<= 60 chars) summarizing the agent's focus."),
|
|
906
|
+
provider: ProviderModelInputSchema.optional().describe("Provider/model pair, for example codex/gpt-5.4. Required unless `personality` is given; when both are given, this overrides the personality's provider/model."),
|
|
907
|
+
personality: z
|
|
908
|
+
.string()
|
|
909
|
+
.trim()
|
|
910
|
+
.min(1)
|
|
911
|
+
.optional()
|
|
912
|
+
.describe("Spawn from a named Agent Personality configured on this host. Expands to its provider/model/effort/mode/prompt; explicit provider/settings override per-field. Personality-bound spawns require the caller to be an Orchestrator personality (top-level user sessions are always allowed). Fails loudly if the personality is unavailable here — no fallback."),
|
|
913
|
+
labels: z.record(z.string(), z.string()).optional().describe("Labels to set on the agent"),
|
|
914
|
+
settings: CreateAgentSettingsInputSchema.optional().describe("Initial runtime settings for the new agent."),
|
|
915
|
+
initialPrompt: z
|
|
916
|
+
.string()
|
|
917
|
+
.trim()
|
|
918
|
+
.min(1, "initialPrompt is required")
|
|
919
|
+
.describe("Required first task to run immediately after creation."),
|
|
920
|
+
};
|
|
921
|
+
const agentToAgentInputSchema = {
|
|
922
|
+
...commonCreateAgentInputSchema,
|
|
923
|
+
notifyOnFinish: z
|
|
924
|
+
.boolean()
|
|
925
|
+
.optional()
|
|
926
|
+
.default(true)
|
|
927
|
+
.describe("Get notified when the created agent finishes, errors, or needs permission. Set false only for truly fire-and-forget agents."),
|
|
928
|
+
};
|
|
929
|
+
const canonicalTopLevelInputSchema = {
|
|
930
|
+
...commonCreateAgentInputSchema,
|
|
931
|
+
background: z
|
|
932
|
+
.boolean()
|
|
933
|
+
.optional()
|
|
934
|
+
.default(false)
|
|
935
|
+
.describe("Run agent in background. If false (default), waits for completion or permission request. If true, returns immediately."),
|
|
936
|
+
notifyOnFinish: z
|
|
937
|
+
.boolean()
|
|
938
|
+
.optional()
|
|
939
|
+
.default(false)
|
|
940
|
+
.describe("Agent-scoped only: get notified when the created agent finishes, errors, or needs permission."),
|
|
941
|
+
};
|
|
942
|
+
const legacyTopLevelCreateAgentInputSchema = {
|
|
943
|
+
relationship: commonCreateAgentInputSchema.relationship.optional(),
|
|
944
|
+
workspace: commonCreateAgentInputSchema.workspace.optional(),
|
|
945
|
+
cwd: z
|
|
946
|
+
.string()
|
|
947
|
+
.optional()
|
|
948
|
+
.describe("Legacy top-level working directory. Prefer workspace.source.path."),
|
|
949
|
+
mode: z.string().optional().describe("Legacy session mode ID. Prefer settings.modeId."),
|
|
950
|
+
thinking: z
|
|
951
|
+
.string()
|
|
952
|
+
.optional()
|
|
953
|
+
.describe("Legacy thinking option ID. Prefer settings.thinkingOptionId."),
|
|
954
|
+
features: z
|
|
955
|
+
.record(z.string(), z.unknown())
|
|
956
|
+
.optional()
|
|
957
|
+
.describe("Legacy feature values. Prefer settings.features."),
|
|
958
|
+
worktreeName: z
|
|
959
|
+
.string()
|
|
960
|
+
.min(1)
|
|
961
|
+
.optional()
|
|
962
|
+
.describe("Legacy worktree slug. Prefer workspace.source.target.worktreeSlug."),
|
|
963
|
+
branchName: z
|
|
964
|
+
.string()
|
|
965
|
+
.min(1)
|
|
966
|
+
.optional()
|
|
967
|
+
.describe("Legacy branch name. Prefer workspace.source.target.branchName."),
|
|
968
|
+
baseBranch: z
|
|
969
|
+
.string()
|
|
970
|
+
.min(1)
|
|
971
|
+
.optional()
|
|
972
|
+
.describe("Legacy base branch. Prefer workspace.source.target.baseBranch."),
|
|
973
|
+
refName: z
|
|
974
|
+
.string()
|
|
975
|
+
.min(1)
|
|
976
|
+
.optional()
|
|
977
|
+
.describe("Legacy branch/ref to check out. Prefer workspace.source.target.branch."),
|
|
978
|
+
githubPrNumber: z
|
|
979
|
+
.number()
|
|
980
|
+
.int()
|
|
981
|
+
.positive()
|
|
982
|
+
.optional()
|
|
983
|
+
.describe("Legacy GitHub PR number. Prefer workspace.source.target.githubPrNumber."),
|
|
984
|
+
};
|
|
985
|
+
const topLevelInputSchema = {
|
|
986
|
+
...canonicalTopLevelInputSchema,
|
|
987
|
+
...legacyTopLevelCreateAgentInputSchema,
|
|
988
|
+
};
|
|
989
|
+
const createAgentInputSchema = callerAgentId ? agentToAgentInputSchema : topLevelInputSchema;
|
|
990
|
+
const agentToAgentCreateAgentArgsSchema = z.object(agentToAgentInputSchema).strict();
|
|
991
|
+
const canonicalTopLevelCreateAgentArgsSchema = z.object(canonicalTopLevelInputSchema).strict();
|
|
992
|
+
const topLevelCreateAgentArgsSchema = z.object(topLevelInputSchema).strict();
|
|
993
|
+
const commonSendAgentPromptInputSchema = {
|
|
994
|
+
agentId: z.string(),
|
|
995
|
+
prompt: z.string(),
|
|
996
|
+
sessionMode: z.string().optional().describe("Optional mode to set before running the prompt."),
|
|
997
|
+
};
|
|
998
|
+
const agentToAgentSendAgentPromptInputSchema = {
|
|
999
|
+
...commonSendAgentPromptInputSchema,
|
|
1000
|
+
background: z
|
|
1001
|
+
.boolean()
|
|
1002
|
+
.optional()
|
|
1003
|
+
.default(true)
|
|
1004
|
+
.describe("Run agent in background. Agent-scoped default is true so you can continue until the finish notification arrives. Set false only when you need a blocking response."),
|
|
1005
|
+
notifyOnFinish: z
|
|
1006
|
+
.boolean()
|
|
1007
|
+
.optional()
|
|
1008
|
+
.default(true)
|
|
1009
|
+
.describe("Get notified when the prompted agent finishes, errors, or needs permission. Set false only for truly fire-and-forget prompts."),
|
|
1010
|
+
};
|
|
1011
|
+
const topLevelSendAgentPromptInputSchema = {
|
|
1012
|
+
...commonSendAgentPromptInputSchema,
|
|
1013
|
+
background: z
|
|
1014
|
+
.boolean()
|
|
1015
|
+
.optional()
|
|
1016
|
+
.default(false)
|
|
1017
|
+
.describe("Run agent in background. If false (default), waits for completion or permission request. If true, returns immediately."),
|
|
1018
|
+
notifyOnFinish: z
|
|
1019
|
+
.boolean()
|
|
1020
|
+
.optional()
|
|
1021
|
+
.default(false)
|
|
1022
|
+
.describe("Agent-scoped only: get notified when the prompted agent finishes, errors, or needs permission."),
|
|
1023
|
+
};
|
|
1024
|
+
const sendAgentPromptInputSchema = callerAgentId
|
|
1025
|
+
? agentToAgentSendAgentPromptInputSchema
|
|
1026
|
+
: topLevelSendAgentPromptInputSchema;
|
|
1027
|
+
const inspectProviderInputSchema = {
|
|
1028
|
+
provider: ProviderOrProviderModelInputSchema.describe("Provider ID, optionally with a model ID (for example codex or codex/gpt-5.4)."),
|
|
1029
|
+
cwd: z
|
|
1030
|
+
.string()
|
|
1031
|
+
.optional()
|
|
1032
|
+
.describe("Working directory used to resolve provider feature availability."),
|
|
1033
|
+
settings: InspectProviderSettingsInputSchema.optional().describe("Draft provider settings used to compute available features."),
|
|
1034
|
+
};
|
|
1035
|
+
if (options.voiceOnly || options.enableVoiceTools || callerContext?.enableVoiceTools) {
|
|
1036
|
+
registerTool("speak", {
|
|
1037
|
+
title: "Speak",
|
|
1038
|
+
description: "Speak text to the user via daemon-managed voice output. Blocks until playback completes.",
|
|
1039
|
+
inputSchema: {
|
|
1040
|
+
text: z
|
|
1041
|
+
.string()
|
|
1042
|
+
.trim()
|
|
1043
|
+
.min(1, "text is required")
|
|
1044
|
+
.max(4000, "text must be 4000 characters or fewer"),
|
|
1045
|
+
},
|
|
1046
|
+
outputSchema: {
|
|
1047
|
+
ok: z.boolean(),
|
|
1048
|
+
},
|
|
1049
|
+
}, async (args, context) => {
|
|
1050
|
+
if (!callerAgentId) {
|
|
1051
|
+
throw new Error("speak is only available to agent-scoped tool sessions");
|
|
1052
|
+
}
|
|
1053
|
+
const handler = resolveSpeakHandler?.(callerAgentId) ?? null;
|
|
1054
|
+
if (!handler) {
|
|
1055
|
+
throw new Error(`No speak handler registered for your session '${callerAgentId}'`);
|
|
1056
|
+
}
|
|
1057
|
+
await handler({
|
|
1058
|
+
text: args.text,
|
|
1059
|
+
callerAgentId,
|
|
1060
|
+
signal: context?.signal,
|
|
1061
|
+
});
|
|
1062
|
+
return {
|
|
1063
|
+
content: [],
|
|
1064
|
+
structuredContent: ensureValidJson({ ok: true }),
|
|
1065
|
+
};
|
|
1066
|
+
});
|
|
1067
|
+
}
|
|
1068
|
+
if (options.voiceOnly) {
|
|
1069
|
+
return toCatalog();
|
|
1070
|
+
}
|
|
1071
|
+
if (options.browserToolsEnabled && options.browserToolsBroker) {
|
|
1072
|
+
registerBrowserTools({
|
|
1073
|
+
registerTool,
|
|
1074
|
+
broker: options.browserToolsBroker,
|
|
1075
|
+
callerAgentId,
|
|
1076
|
+
resolveCallerAgent,
|
|
1077
|
+
previewServers: options.previewDevServers ?? null,
|
|
1078
|
+
});
|
|
1079
|
+
}
|
|
1080
|
+
if (options.previewDevServers) {
|
|
1081
|
+
registerPreviewTools({
|
|
1082
|
+
registerTool,
|
|
1083
|
+
manager: options.previewDevServers,
|
|
1084
|
+
broker: options.browserToolsBroker ?? null,
|
|
1085
|
+
resolveCallerAgent,
|
|
1086
|
+
});
|
|
1087
|
+
}
|
|
1088
|
+
registerTool("create_agent", {
|
|
1089
|
+
title: "Create agent",
|
|
1090
|
+
description: "Create an agent. Requires relationship, workspace, an initial prompt, and either a provider/model (for example codex/gpt-5.4) or a personality name. Prefer a personality when the host has them (call list_personalities). Do not guess; call list_providers and list_models first if uncertain.",
|
|
1091
|
+
inputSchema: createAgentInputSchema,
|
|
1092
|
+
outputSchema: {
|
|
1093
|
+
agentId: z.string(),
|
|
1094
|
+
type: AgentProviderEnum,
|
|
1095
|
+
status: AgentStatusEnum,
|
|
1096
|
+
cwd: z.string(),
|
|
1097
|
+
workspaceId: z.string().optional(),
|
|
1098
|
+
currentModeId: z.string().nullable(),
|
|
1099
|
+
availableModes: z.array(ProviderModeSchema),
|
|
1100
|
+
lastMessage: z.string().nullable().optional(),
|
|
1101
|
+
permission: AgentPermissionRequestPayloadSchema.nullable().optional(),
|
|
1102
|
+
guidance: z.string().optional(),
|
|
1103
|
+
},
|
|
1104
|
+
}, async (args) => {
|
|
1105
|
+
const resolvedArgs = await resolveCreateAgentToolArgs(args);
|
|
1106
|
+
const { parsedArgs, worktree } = resolvedArgs;
|
|
1107
|
+
let requestedBackground;
|
|
1108
|
+
let notifyOnFinish;
|
|
1109
|
+
let detached;
|
|
1110
|
+
if (resolvedArgs.kind === "agent-scoped") {
|
|
1111
|
+
requestedBackground = true;
|
|
1112
|
+
notifyOnFinish = parsedArgs.notifyOnFinish;
|
|
1113
|
+
detached = resolvedArgs.relationship.kind === "detached";
|
|
1114
|
+
}
|
|
1115
|
+
else {
|
|
1116
|
+
requestedBackground = resolvedArgs.parsedArgs.background;
|
|
1117
|
+
notifyOnFinish = resolvedArgs.parsedArgs.notifyOnFinish ?? false;
|
|
1118
|
+
detached = resolvedArgs.parsedArgs.relationship.kind === "detached";
|
|
1119
|
+
}
|
|
1120
|
+
const brain = await resolveCreateAgentBrain({
|
|
1121
|
+
personalityName: parsedArgs.personality,
|
|
1122
|
+
providerOverride: parsedArgs.provider,
|
|
1123
|
+
modeOverride: parsedArgs.settings?.modeId,
|
|
1124
|
+
thinkingOverride: parsedArgs.settings?.thinkingOptionId,
|
|
1125
|
+
cwd: resolvedArgs.cwd,
|
|
1126
|
+
});
|
|
1127
|
+
// A personality carries a systemPrompt and its frozen snapshot onto the
|
|
1128
|
+
// agent config (spread first in buildMcpSessionConfig, so nothing below
|
|
1129
|
+
// clobbers them).
|
|
1130
|
+
const personalityConfig = buildPersonalityAgentConfig(brain);
|
|
1131
|
+
const { snapshot, background: createdInBackground, initialPromptStarted, } = await createAgentCommand({
|
|
1132
|
+
agentManager,
|
|
1133
|
+
agentStorage,
|
|
1134
|
+
logger: childLogger,
|
|
1135
|
+
ottoHome: options.ottoHome,
|
|
1136
|
+
worktreesRoot: options.worktreesRoot,
|
|
1137
|
+
terminalManager,
|
|
1138
|
+
providerSnapshotManager,
|
|
1139
|
+
createOttoWorktree: options.createOttoWorktree,
|
|
1140
|
+
...(options.ensureWorkspaceForCreate
|
|
1141
|
+
? { ensureWorkspaceForCreate: options.ensureWorkspaceForCreate }
|
|
1142
|
+
: {}),
|
|
1143
|
+
}, {
|
|
1144
|
+
kind: "mcp",
|
|
1145
|
+
provider: brain.providerModel,
|
|
1146
|
+
...(personalityConfig ? { config: personalityConfig } : {}),
|
|
1147
|
+
title: parsedArgs.title,
|
|
1148
|
+
initialPrompt: parsedArgs.initialPrompt,
|
|
1149
|
+
cwd: resolvedArgs.cwd,
|
|
1150
|
+
workspaceId: resolvedArgs.workspaceId,
|
|
1151
|
+
thinking: brain.thinkingOptionId,
|
|
1152
|
+
features: parsedArgs.settings?.features,
|
|
1153
|
+
labels: parsedArgs.labels,
|
|
1154
|
+
mode: brain.modeId,
|
|
1155
|
+
background: requestedBackground,
|
|
1156
|
+
notifyOnFinish,
|
|
1157
|
+
detached,
|
|
1158
|
+
callerAgentId,
|
|
1159
|
+
callerContext,
|
|
1160
|
+
worktree,
|
|
1161
|
+
});
|
|
1162
|
+
try {
|
|
1163
|
+
if (!createdInBackground && initialPromptStarted) {
|
|
1164
|
+
const result = await waitForAgentWithTimeout(agentManager, snapshot.id, {
|
|
1165
|
+
waitForActive: true,
|
|
1166
|
+
});
|
|
1167
|
+
const liveSnapshot = agentManager.getAgent(snapshot.id) ?? snapshot;
|
|
1168
|
+
const responseData = {
|
|
1169
|
+
agentId: snapshot.id,
|
|
1170
|
+
type: snapshot.provider,
|
|
1171
|
+
status: result.status,
|
|
1172
|
+
cwd: liveSnapshot.cwd,
|
|
1173
|
+
...(liveSnapshot.workspaceId ? { workspaceId: liveSnapshot.workspaceId } : {}),
|
|
1174
|
+
currentModeId: liveSnapshot.currentModeId,
|
|
1175
|
+
availableModes: liveSnapshot.availableModes,
|
|
1176
|
+
lastMessage: result.lastMessage,
|
|
1177
|
+
permission: sanitizePermissionRequest(result.permission),
|
|
1178
|
+
};
|
|
1179
|
+
const validJson = ensureValidJson(responseData);
|
|
1180
|
+
const response = {
|
|
1181
|
+
content: [],
|
|
1182
|
+
structuredContent: validJson,
|
|
1183
|
+
};
|
|
1184
|
+
return response;
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
catch (error) {
|
|
1188
|
+
childLogger.error({ err: error, agentId: snapshot.id }, "Failed to run initial prompt");
|
|
1189
|
+
throw error;
|
|
1190
|
+
}
|
|
1191
|
+
// Return immediately for async creation.
|
|
1192
|
+
const currentSnapshot = agentManager.getAgent(snapshot.id) ?? snapshot;
|
|
1193
|
+
const guidance = callerAgentId && notifyOnFinish && initialPromptStarted
|
|
1194
|
+
? "You will get notified when the created agent finishes, errors, or needs permission. Do not poll for status; continue with other work until the notification arrives."
|
|
1195
|
+
: undefined;
|
|
1196
|
+
const response = {
|
|
1197
|
+
content: [],
|
|
1198
|
+
structuredContent: ensureValidJson({
|
|
1199
|
+
agentId: currentSnapshot.id,
|
|
1200
|
+
type: snapshot.provider,
|
|
1201
|
+
status: currentSnapshot.lifecycle,
|
|
1202
|
+
cwd: currentSnapshot.cwd,
|
|
1203
|
+
...(currentSnapshot.workspaceId ? { workspaceId: currentSnapshot.workspaceId } : {}),
|
|
1204
|
+
currentModeId: currentSnapshot.currentModeId,
|
|
1205
|
+
availableModes: currentSnapshot.availableModes,
|
|
1206
|
+
lastMessage: null,
|
|
1207
|
+
permission: null,
|
|
1208
|
+
...(guidance ? { guidance } : {}),
|
|
1209
|
+
}),
|
|
1210
|
+
};
|
|
1211
|
+
return response;
|
|
1212
|
+
});
|
|
1213
|
+
if (readAgentPersonalities) {
|
|
1214
|
+
registerTool("list_personalities", {
|
|
1215
|
+
title: "List personalities",
|
|
1216
|
+
description: "List the Agent Personalities configured on this host — named templates binding a provider/model, effort, mode, prompt, and roles. Use a personality's name with create_agent's `personality` argument to spawn it. Availability is resolved against a workspace; unavailable personalities cannot be spawned there. Restricted to Orchestrator personalities (top-level user sessions are always allowed).",
|
|
1217
|
+
inputSchema: {
|
|
1218
|
+
cwd: z
|
|
1219
|
+
.string()
|
|
1220
|
+
.optional()
|
|
1221
|
+
.describe("Workspace directory to resolve availability against. Defaults to your current cwd."),
|
|
1222
|
+
role: z
|
|
1223
|
+
.string()
|
|
1224
|
+
.optional()
|
|
1225
|
+
.describe("Only return personalities carrying this role (for example worker, judger, advisor)."),
|
|
1226
|
+
},
|
|
1227
|
+
outputSchema: {
|
|
1228
|
+
personalities: z.array(z.object({
|
|
1229
|
+
id: z.string(),
|
|
1230
|
+
name: z.string(),
|
|
1231
|
+
roles: z.array(z.string()),
|
|
1232
|
+
provider: z.string(),
|
|
1233
|
+
model: z.string(),
|
|
1234
|
+
available: z.boolean(),
|
|
1235
|
+
unavailableReason: z.string().optional(),
|
|
1236
|
+
modeId: z.string().optional(),
|
|
1237
|
+
thinkingOptionId: z.string().optional(),
|
|
1238
|
+
effortLevel: z.string().optional(),
|
|
1239
|
+
})),
|
|
1240
|
+
},
|
|
1241
|
+
}, async (args) => {
|
|
1242
|
+
assertPersonalityOrchestrationAllowed("list personalities");
|
|
1243
|
+
const roleFilter = args.role?.trim();
|
|
1244
|
+
const caller = callerAgentId ? agentManager.getAgent(callerAgentId) : null;
|
|
1245
|
+
const cwd = args.cwd?.trim() || caller?.cwd || undefined;
|
|
1246
|
+
const entries = await providerSnapshotManager.listProviders({ cwd, wait: true });
|
|
1247
|
+
const personalities = getPersonalityRoster()
|
|
1248
|
+
.filter((personality) => !roleFilter ||
|
|
1249
|
+
(isPersonalityRole(roleFilter) && personalityHasRole(personality, roleFilter)))
|
|
1250
|
+
.map((personality) => {
|
|
1251
|
+
const resolution = resolvePersonality(personality, entries);
|
|
1252
|
+
const entryOut = {
|
|
1253
|
+
id: personality.id,
|
|
1254
|
+
name: personality.name,
|
|
1255
|
+
roles: normalizePersonalityRoles(personality.roles),
|
|
1256
|
+
provider: personality.provider,
|
|
1257
|
+
model: personality.model,
|
|
1258
|
+
available: resolution.status === "available",
|
|
1259
|
+
};
|
|
1260
|
+
if (resolution.status === "unavailable") {
|
|
1261
|
+
entryOut.unavailableReason = resolution.reason;
|
|
1262
|
+
return entryOut;
|
|
1263
|
+
}
|
|
1264
|
+
const snapshot = resolution.snapshot;
|
|
1265
|
+
if (snapshot.modeId !== undefined) {
|
|
1266
|
+
entryOut.modeId = snapshot.modeId;
|
|
1267
|
+
}
|
|
1268
|
+
if (snapshot.thinkingOptionId !== undefined) {
|
|
1269
|
+
entryOut.thinkingOptionId = snapshot.thinkingOptionId;
|
|
1270
|
+
}
|
|
1271
|
+
if (snapshot.effortLevel !== undefined) {
|
|
1272
|
+
entryOut.effortLevel = snapshot.effortLevel;
|
|
1273
|
+
}
|
|
1274
|
+
return entryOut;
|
|
1275
|
+
});
|
|
1276
|
+
return {
|
|
1277
|
+
content: [],
|
|
1278
|
+
structuredContent: ensureValidJson({ personalities }),
|
|
1279
|
+
};
|
|
1280
|
+
});
|
|
1281
|
+
}
|
|
1282
|
+
async function resolveCreateAgentToolArgs(args) {
|
|
1283
|
+
if (callerAgentId) {
|
|
1284
|
+
const parsed = agentToAgentCreateAgentArgsSchema.parse(args);
|
|
1285
|
+
const { cwd, workspaceId, worktree } = await resolveCreateAgentWorkspace(parsed.workspace, {
|
|
1286
|
+
prompt: parsed.initialPrompt,
|
|
1287
|
+
});
|
|
1288
|
+
return {
|
|
1289
|
+
kind: "agent-scoped",
|
|
1290
|
+
parsedArgs: parsed,
|
|
1291
|
+
relationship: parsed.relationship,
|
|
1292
|
+
cwd,
|
|
1293
|
+
workspaceId,
|
|
1294
|
+
worktree,
|
|
1295
|
+
};
|
|
1296
|
+
}
|
|
1297
|
+
const parsedArgs = normalizeTopLevelCreateAgentArgs(topLevelCreateAgentArgsSchema.parse(args));
|
|
1298
|
+
if (parsedArgs.relationship.kind === "subagent") {
|
|
1299
|
+
throw new Error("relationship subagent requires an agent-scoped tool session");
|
|
1300
|
+
}
|
|
1301
|
+
const { cwd, workspaceId, worktree } = await resolveCreateAgentWorkspace(parsedArgs.workspace, {
|
|
1302
|
+
prompt: parsedArgs.initialPrompt,
|
|
1303
|
+
});
|
|
1304
|
+
return {
|
|
1305
|
+
kind: "top-level",
|
|
1306
|
+
parsedArgs,
|
|
1307
|
+
cwd,
|
|
1308
|
+
workspaceId,
|
|
1309
|
+
worktree,
|
|
1310
|
+
};
|
|
1311
|
+
}
|
|
1312
|
+
function normalizeTopLevelCreateAgentArgs(args) {
|
|
1313
|
+
const { cwd, mode, thinking, features, worktreeName, branchName, baseBranch, refName, githubPrNumber, ...canonicalCandidate } = args;
|
|
1314
|
+
const settings = {
|
|
1315
|
+
...canonicalCandidate.settings,
|
|
1316
|
+
...(mode ? { modeId: mode } : {}),
|
|
1317
|
+
...(thinking ? { thinkingOptionId: thinking } : {}),
|
|
1318
|
+
...(features ? { features } : {}),
|
|
1319
|
+
};
|
|
1320
|
+
if (canonicalCandidate.relationship && canonicalCandidate.workspace) {
|
|
1321
|
+
return canonicalTopLevelCreateAgentArgsSchema.parse({
|
|
1322
|
+
...canonicalCandidate,
|
|
1323
|
+
...(Object.keys(settings).length > 0 ? { settings } : {}),
|
|
1324
|
+
});
|
|
1325
|
+
}
|
|
1326
|
+
if (canonicalCandidate.relationship || canonicalCandidate.workspace) {
|
|
1327
|
+
throw new Error("relationship and workspace must be provided together");
|
|
1328
|
+
}
|
|
1329
|
+
if (!cwd?.trim()) {
|
|
1330
|
+
throw new Error("cwd is required for legacy top-level create_agent calls");
|
|
1331
|
+
}
|
|
1332
|
+
const legacyWorktreeTarget = resolveLegacyCreateAgentWorktreeTarget({
|
|
1333
|
+
worktreeName,
|
|
1334
|
+
branchName,
|
|
1335
|
+
baseBranch,
|
|
1336
|
+
refName,
|
|
1337
|
+
githubPrNumber,
|
|
1338
|
+
});
|
|
1339
|
+
const workspace = legacyWorktreeTarget
|
|
1340
|
+
? {
|
|
1341
|
+
kind: "create",
|
|
1342
|
+
source: {
|
|
1343
|
+
kind: "worktree",
|
|
1344
|
+
cwd,
|
|
1345
|
+
target: legacyWorktreeTarget,
|
|
1346
|
+
},
|
|
1347
|
+
}
|
|
1348
|
+
: {
|
|
1349
|
+
kind: "create",
|
|
1350
|
+
source: {
|
|
1351
|
+
kind: "directory",
|
|
1352
|
+
path: cwd,
|
|
1353
|
+
},
|
|
1354
|
+
};
|
|
1355
|
+
return canonicalTopLevelCreateAgentArgsSchema.parse({
|
|
1356
|
+
...canonicalCandidate,
|
|
1357
|
+
relationship: { kind: "detached" },
|
|
1358
|
+
workspace,
|
|
1359
|
+
...(Object.keys(settings).length > 0 ? { settings } : {}),
|
|
1360
|
+
});
|
|
1361
|
+
}
|
|
1362
|
+
function resolveLegacyCreateAgentWorktreeTarget(input) {
|
|
1363
|
+
if (input.githubPrNumber !== undefined) {
|
|
1364
|
+
return {
|
|
1365
|
+
kind: "checkout-pr",
|
|
1366
|
+
githubPrNumber: input.githubPrNumber,
|
|
1367
|
+
};
|
|
1368
|
+
}
|
|
1369
|
+
if (input.refName) {
|
|
1370
|
+
return {
|
|
1371
|
+
kind: "checkout-branch",
|
|
1372
|
+
branch: input.refName,
|
|
1373
|
+
};
|
|
1374
|
+
}
|
|
1375
|
+
if (input.worktreeName || input.branchName || input.baseBranch) {
|
|
1376
|
+
return {
|
|
1377
|
+
kind: "branch-off",
|
|
1378
|
+
worktreeSlug: input.worktreeName,
|
|
1379
|
+
branchName: input.branchName,
|
|
1380
|
+
baseBranch: input.baseBranch,
|
|
1381
|
+
};
|
|
1382
|
+
}
|
|
1383
|
+
return null;
|
|
1384
|
+
}
|
|
1385
|
+
async function resolveCreateAgentWorkspace(workspace, firstAgentContext) {
|
|
1386
|
+
if (workspace.kind === "current") {
|
|
1387
|
+
if (!callerAgentId) {
|
|
1388
|
+
throw new Error("workspace current requires an agent-scoped tool session");
|
|
1389
|
+
}
|
|
1390
|
+
const callerAgent = resolveCallerAgent();
|
|
1391
|
+
if (!callerAgent?.workspaceId) {
|
|
1392
|
+
throw new Error(`Caller agent ${callerAgentId} has no current workspace`);
|
|
1393
|
+
}
|
|
1394
|
+
return {
|
|
1395
|
+
cwd: workspace.cwd,
|
|
1396
|
+
workspaceId: callerAgent.workspaceId,
|
|
1397
|
+
worktree: undefined,
|
|
1398
|
+
};
|
|
1399
|
+
}
|
|
1400
|
+
if (workspace.kind === "existing") {
|
|
1401
|
+
if (!options.listActiveWorkspaces) {
|
|
1402
|
+
throw new Error("Workspace lookup is not configured");
|
|
1403
|
+
}
|
|
1404
|
+
const existingWorkspace = (await options.listActiveWorkspaces()).find((candidate) => candidate.workspaceId === workspace.workspaceId);
|
|
1405
|
+
if (!existingWorkspace) {
|
|
1406
|
+
throw new Error(`Workspace ${workspace.workspaceId} not found`);
|
|
1407
|
+
}
|
|
1408
|
+
const cwd = workspace.cwd
|
|
1409
|
+
? resolveScopedCwd(workspace.cwd, { required: true })
|
|
1410
|
+
: existingWorkspace.cwd;
|
|
1411
|
+
const lockedCwd = callerContext?.lockedCwd?.trim();
|
|
1412
|
+
if (lockedCwd && !isSameOrDescendantPath(expandUserPath(lockedCwd), cwd)) {
|
|
1413
|
+
throw new Error(`Workspace ${workspace.workspaceId} is outside the allowed cwd`);
|
|
1414
|
+
}
|
|
1415
|
+
return {
|
|
1416
|
+
cwd,
|
|
1417
|
+
workspaceId: workspace.workspaceId,
|
|
1418
|
+
worktree: undefined,
|
|
1419
|
+
};
|
|
1420
|
+
}
|
|
1421
|
+
if (workspace.source.kind === "directory") {
|
|
1422
|
+
const cwd = resolveScopedCwd(workspace.source.path, { required: true });
|
|
1423
|
+
if (!options.ensureWorkspaceForCreate) {
|
|
1424
|
+
throw new Error("Workspace creation is not configured");
|
|
1425
|
+
}
|
|
1426
|
+
return {
|
|
1427
|
+
cwd,
|
|
1428
|
+
workspaceId: await options.ensureWorkspaceForCreate(cwd, firstAgentContext),
|
|
1429
|
+
worktree: undefined,
|
|
1430
|
+
};
|
|
1431
|
+
}
|
|
1432
|
+
const cwd = resolveScopedCwd(workspace.source.cwd, { required: true });
|
|
1433
|
+
return {
|
|
1434
|
+
cwd,
|
|
1435
|
+
workspaceId: undefined,
|
|
1436
|
+
worktree: resolveCreateAgentWorktree(workspace.source.target),
|
|
1437
|
+
};
|
|
1438
|
+
}
|
|
1439
|
+
function resolveCreateAgentWorktree(target) {
|
|
1440
|
+
switch (target.kind) {
|
|
1441
|
+
case "branch-off":
|
|
1442
|
+
return {
|
|
1443
|
+
action: "branch-off",
|
|
1444
|
+
worktreeName: target.worktreeSlug,
|
|
1445
|
+
branchName: target.branchName,
|
|
1446
|
+
baseBranch: target.baseBranch,
|
|
1447
|
+
};
|
|
1448
|
+
case "checkout-branch":
|
|
1449
|
+
return {
|
|
1450
|
+
action: "checkout",
|
|
1451
|
+
refName: target.branch,
|
|
1452
|
+
};
|
|
1453
|
+
case "checkout-pr":
|
|
1454
|
+
return {
|
|
1455
|
+
action: "checkout",
|
|
1456
|
+
githubPrNumber: target.githubPrNumber,
|
|
1457
|
+
};
|
|
1458
|
+
default:
|
|
1459
|
+
throw new Error("unreachable");
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
1462
|
+
registerTool("send_agent_prompt", {
|
|
1463
|
+
title: "Send agent prompt",
|
|
1464
|
+
description: "Send a task to a running agent. Agent-scoped callers run in background by default; top-level callers wait by default.",
|
|
1465
|
+
inputSchema: sendAgentPromptInputSchema,
|
|
1466
|
+
outputSchema: {
|
|
1467
|
+
success: z.boolean(),
|
|
1468
|
+
status: AgentStatusEnum,
|
|
1469
|
+
lastMessage: z.string().nullable().optional(),
|
|
1470
|
+
permission: AgentPermissionRequestPayloadSchema.nullable().optional(),
|
|
1471
|
+
guidance: z.string().optional(),
|
|
1472
|
+
},
|
|
1473
|
+
}, async ({ agentId, prompt, sessionMode, background = Boolean(callerAgentId), notifyOnFinish = Boolean(callerAgentId), }) => {
|
|
1474
|
+
const shouldNotifyOnFinish = Boolean(callerAgentId && notifyOnFinish && background);
|
|
1475
|
+
await sendPromptToAgent({
|
|
1476
|
+
agentManager,
|
|
1477
|
+
agentStorage,
|
|
1478
|
+
agentId,
|
|
1479
|
+
prompt,
|
|
1480
|
+
sessionMode,
|
|
1481
|
+
logger: childLogger,
|
|
1482
|
+
});
|
|
1483
|
+
if (shouldNotifyOnFinish && callerAgentId) {
|
|
1484
|
+
setupFinishNotification({
|
|
1485
|
+
agentManager,
|
|
1486
|
+
agentStorage,
|
|
1487
|
+
childAgentId: agentId,
|
|
1488
|
+
callerAgentId,
|
|
1489
|
+
logger: childLogger,
|
|
1490
|
+
});
|
|
1491
|
+
}
|
|
1492
|
+
// If not running in background, wait for completion
|
|
1493
|
+
if (!background) {
|
|
1494
|
+
const result = await waitForAgentWithTimeout(agentManager, agentId, {
|
|
1495
|
+
waitForActive: true,
|
|
1496
|
+
});
|
|
1497
|
+
const responseData = {
|
|
1498
|
+
success: true,
|
|
1499
|
+
status: result.status,
|
|
1500
|
+
lastMessage: result.lastMessage,
|
|
1501
|
+
permission: sanitizePermissionRequest(result.permission),
|
|
1502
|
+
};
|
|
1503
|
+
const validJson = ensureValidJson(responseData);
|
|
1504
|
+
const response = {
|
|
1505
|
+
content: [],
|
|
1506
|
+
structuredContent: validJson,
|
|
1507
|
+
};
|
|
1508
|
+
return response;
|
|
1509
|
+
}
|
|
1510
|
+
// Return immediately if background=true
|
|
1511
|
+
// Re-fetch snapshot since the state may have changed
|
|
1512
|
+
const currentSnapshot = agentManager.getAgent(agentId);
|
|
1513
|
+
const responseData = {
|
|
1514
|
+
success: true,
|
|
1515
|
+
status: currentSnapshot?.lifecycle ?? "idle",
|
|
1516
|
+
lastMessage: null,
|
|
1517
|
+
permission: null,
|
|
1518
|
+
...(shouldNotifyOnFinish
|
|
1519
|
+
? {
|
|
1520
|
+
guidance: "You will get notified when the prompted agent finishes, errors, or needs permission. Do not poll for status; continue with other work until the notification arrives.",
|
|
1521
|
+
}
|
|
1522
|
+
: {}),
|
|
1523
|
+
};
|
|
1524
|
+
const validJson = ensureValidJson(responseData);
|
|
1525
|
+
const response = {
|
|
1526
|
+
content: [],
|
|
1527
|
+
structuredContent: validJson,
|
|
1528
|
+
};
|
|
1529
|
+
return response;
|
|
1530
|
+
});
|
|
1531
|
+
registerTool("get_agent_status", {
|
|
1532
|
+
title: "Get agent status",
|
|
1533
|
+
description: "Return the latest snapshot for an agent, including lifecycle state, capabilities, and pending permissions.",
|
|
1534
|
+
inputSchema: {
|
|
1535
|
+
agentId: z.string(),
|
|
1536
|
+
},
|
|
1537
|
+
outputSchema: {
|
|
1538
|
+
status: AgentStatusEnum,
|
|
1539
|
+
snapshot: AgentSnapshotPayloadSchema,
|
|
1540
|
+
},
|
|
1541
|
+
}, async ({ agentId }) => {
|
|
1542
|
+
const snapshot = agentManager.getAgent(agentId);
|
|
1543
|
+
if (snapshot) {
|
|
1544
|
+
const structuredSnapshot = await serializeSnapshotWithMetadata(agentStorage, snapshot, childLogger);
|
|
1545
|
+
return {
|
|
1546
|
+
content: [],
|
|
1547
|
+
structuredContent: ensureValidJson({
|
|
1548
|
+
status: snapshot.lifecycle,
|
|
1549
|
+
snapshot: structuredSnapshot,
|
|
1550
|
+
}),
|
|
1551
|
+
};
|
|
1552
|
+
}
|
|
1553
|
+
const record = await agentStorage.get(agentId);
|
|
1554
|
+
if (!record || record.internal) {
|
|
1555
|
+
throw new Error(`Agent ${agentId} not found`);
|
|
1556
|
+
}
|
|
1557
|
+
const structuredSnapshot = buildStoredAgentPayload(record, new Set(providerSnapshotManager.listRegisteredProviderIds()));
|
|
1558
|
+
return {
|
|
1559
|
+
content: [],
|
|
1560
|
+
structuredContent: ensureValidJson({
|
|
1561
|
+
status: structuredSnapshot.status,
|
|
1562
|
+
snapshot: structuredSnapshot,
|
|
1563
|
+
}),
|
|
1564
|
+
};
|
|
1565
|
+
});
|
|
1566
|
+
registerTool("list_agents", {
|
|
1567
|
+
title: "List agents",
|
|
1568
|
+
description: "List recent agents as compact metadata.",
|
|
1569
|
+
inputSchema: {
|
|
1570
|
+
includeArchived: z.boolean().optional().default(false),
|
|
1571
|
+
cwd: z.string().optional(),
|
|
1572
|
+
sinceHours: z
|
|
1573
|
+
.number()
|
|
1574
|
+
.int()
|
|
1575
|
+
.positive()
|
|
1576
|
+
.max(24 * 30)
|
|
1577
|
+
.optional()
|
|
1578
|
+
.default(48),
|
|
1579
|
+
statuses: z.array(AgentStatusEnum).optional(),
|
|
1580
|
+
limit: z.number().int().positive().max(200).optional().default(50),
|
|
1581
|
+
},
|
|
1582
|
+
outputSchema: {
|
|
1583
|
+
agents: z.array(AgentListItemPayloadSchema),
|
|
1584
|
+
},
|
|
1585
|
+
}, async ({ includeArchived = false, cwd, sinceHours = 48, statuses, limit = 50 }) => {
|
|
1586
|
+
const callerCwd = callerAgentId ? resolveCallerAgent()?.cwd : undefined;
|
|
1587
|
+
const requestedCwd = cwd?.trim() ? expandUserPath(cwd) : callerCwd;
|
|
1588
|
+
const statusFilter = statuses && statuses.length > 0 ? new Set(statuses) : null;
|
|
1589
|
+
const sinceMs = Date.now() - sinceHours * 60 * 60 * 1000;
|
|
1590
|
+
const liveSnapshots = agentManager.listAgents();
|
|
1591
|
+
const liveAgents = await Promise.all(liveSnapshots.map((snapshot) => serializeSnapshotWithMetadata(agentStorage, snapshot, childLogger)));
|
|
1592
|
+
const liveIds = new Set(liveSnapshots.map((snapshot) => snapshot.id));
|
|
1593
|
+
const storedRecords = await agentStorage.list();
|
|
1594
|
+
const registeredProviderIds = new Set(providerSnapshotManager.listRegisteredProviderIds());
|
|
1595
|
+
const storedAgents = storedRecords
|
|
1596
|
+
.filter((record) => !record.internal && !liveIds.has(record.id))
|
|
1597
|
+
.filter((record) => includeArchived || !record.archivedAt)
|
|
1598
|
+
.filter((record) => includeArchived || isStoredAgentProviderAvailable(record, registeredProviderIds))
|
|
1599
|
+
.map((record) => buildStoredAgentPayload(record, registeredProviderIds));
|
|
1600
|
+
const agents = [...liveAgents, ...storedAgents]
|
|
1601
|
+
.map(toAgentListItemPayload)
|
|
1602
|
+
.filter((agent) => !requestedCwd || isSameOrDescendantPath(requestedCwd, agent.cwd))
|
|
1603
|
+
.filter((agent) => !statusFilter || statusFilter.has(agent.status))
|
|
1604
|
+
.filter((agent) => !agent.archivedAt || resolveAgentListActivityTime(agent) >= sinceMs)
|
|
1605
|
+
.sort(compareAgentListItems)
|
|
1606
|
+
.slice(0, limit);
|
|
1607
|
+
return {
|
|
1608
|
+
content: [],
|
|
1609
|
+
structuredContent: ensureValidJson({ agents }),
|
|
1610
|
+
};
|
|
1611
|
+
});
|
|
1612
|
+
registerTool("cancel_agent", {
|
|
1613
|
+
title: "Cancel agent run",
|
|
1614
|
+
description: "Abort the agent's current run but keep the agent alive for future tasks.",
|
|
1615
|
+
inputSchema: {
|
|
1616
|
+
agentId: z.string(),
|
|
1617
|
+
},
|
|
1618
|
+
outputSchema: {
|
|
1619
|
+
success: z.boolean(),
|
|
1620
|
+
},
|
|
1621
|
+
}, async ({ agentId }) => {
|
|
1622
|
+
const { cancelled } = await cancelAgentRunCommand({ agentManager, logger: childLogger }, agentId);
|
|
1623
|
+
return {
|
|
1624
|
+
content: [],
|
|
1625
|
+
structuredContent: ensureValidJson({ success: cancelled }),
|
|
1626
|
+
};
|
|
1627
|
+
});
|
|
1628
|
+
registerTool("archive_agent", {
|
|
1629
|
+
title: "Archive agent",
|
|
1630
|
+
description: "Archive an agent (soft-delete). The agent is interrupted if running and removed from the active list.",
|
|
1631
|
+
inputSchema: {
|
|
1632
|
+
agentId: z.string(),
|
|
1633
|
+
},
|
|
1634
|
+
outputSchema: {
|
|
1635
|
+
success: z.boolean(),
|
|
1636
|
+
},
|
|
1637
|
+
}, async ({ agentId }) => {
|
|
1638
|
+
await archiveAgentCommand({
|
|
1639
|
+
agentManager,
|
|
1640
|
+
agentStorage,
|
|
1641
|
+
logger: childLogger,
|
|
1642
|
+
}, agentId);
|
|
1643
|
+
return {
|
|
1644
|
+
content: [],
|
|
1645
|
+
structuredContent: ensureValidJson({ success: true }),
|
|
1646
|
+
};
|
|
1647
|
+
});
|
|
1648
|
+
registerTool("kill_agent", {
|
|
1649
|
+
title: "Kill agent",
|
|
1650
|
+
description: "Terminate an agent session permanently.",
|
|
1651
|
+
inputSchema: {
|
|
1652
|
+
agentId: z.string(),
|
|
1653
|
+
},
|
|
1654
|
+
outputSchema: {
|
|
1655
|
+
success: z.boolean(),
|
|
1656
|
+
},
|
|
1657
|
+
}, async ({ agentId }) => {
|
|
1658
|
+
await closeAgentCommand({ agentManager }, agentId);
|
|
1659
|
+
return {
|
|
1660
|
+
content: [],
|
|
1661
|
+
structuredContent: ensureValidJson({ success: true }),
|
|
1662
|
+
};
|
|
1663
|
+
});
|
|
1664
|
+
registerTool("update_agent", {
|
|
1665
|
+
title: "Update agent",
|
|
1666
|
+
description: "Update an agent name, labels, and/or runtime settings.",
|
|
1667
|
+
inputSchema: {
|
|
1668
|
+
agentId: z.string(),
|
|
1669
|
+
name: z.string().optional(),
|
|
1670
|
+
labels: z.record(z.string(), z.string()).optional().describe("Labels to set on the agent"),
|
|
1671
|
+
settings: UpdateAgentSettingsInputSchema.optional().describe("Runtime settings to apply to the agent."),
|
|
1672
|
+
},
|
|
1673
|
+
outputSchema: {
|
|
1674
|
+
success: z.boolean(),
|
|
1675
|
+
},
|
|
1676
|
+
}, async ({ agentId, name, labels, settings }) => {
|
|
1677
|
+
if (settings?.modeId !== undefined) {
|
|
1678
|
+
await agentManager.setAgentMode(agentId, settings.modeId);
|
|
1679
|
+
}
|
|
1680
|
+
if (settings?.model !== undefined) {
|
|
1681
|
+
await agentManager.setAgentModel(agentId, settings.model);
|
|
1682
|
+
}
|
|
1683
|
+
if (settings?.thinkingOptionId !== undefined) {
|
|
1684
|
+
let thinkingOptionId = settings.thinkingOptionId;
|
|
1685
|
+
const agent = agentManager.getAgent(agentId);
|
|
1686
|
+
if (thinkingOptionId !== null && agent) {
|
|
1687
|
+
// Resolve against the model this call leaves the agent on.
|
|
1688
|
+
const targetModel = settings.model !== undefined ? (settings.model ?? undefined) : agent.config.model;
|
|
1689
|
+
thinkingOptionId = resolveEffortAgainstModels({
|
|
1690
|
+
requested: thinkingOptionId,
|
|
1691
|
+
models: await listProviderModels(agent.provider),
|
|
1692
|
+
model: targetModel,
|
|
1693
|
+
});
|
|
1694
|
+
}
|
|
1695
|
+
await agentManager.setAgentThinkingOption(agentId, thinkingOptionId);
|
|
1696
|
+
}
|
|
1697
|
+
if (settings?.features) {
|
|
1698
|
+
for (const [featureId, value] of Object.entries(settings.features)) {
|
|
1699
|
+
await agentManager.setAgentFeature(agentId, featureId, value);
|
|
1700
|
+
}
|
|
1701
|
+
}
|
|
1702
|
+
await updateAgentCommand({ agentManager }, { agentId, name, labels });
|
|
1703
|
+
return {
|
|
1704
|
+
content: [],
|
|
1705
|
+
structuredContent: ensureValidJson({ success: true }),
|
|
1706
|
+
};
|
|
1707
|
+
});
|
|
1708
|
+
registerTool("rename_workspace", {
|
|
1709
|
+
title: "Rename workspace",
|
|
1710
|
+
description: "Rename a workspace by setting its user-visible title. Omit workspaceId to rename your current workspace.",
|
|
1711
|
+
inputSchema: {
|
|
1712
|
+
workspaceId: z
|
|
1713
|
+
.string()
|
|
1714
|
+
.trim()
|
|
1715
|
+
.min(1)
|
|
1716
|
+
.optional()
|
|
1717
|
+
.describe("Workspace id to rename. Omit to rename your current workspace."),
|
|
1718
|
+
title: z
|
|
1719
|
+
.string()
|
|
1720
|
+
.trim()
|
|
1721
|
+
.min(1, "title is required")
|
|
1722
|
+
.describe("New user-visible workspace title."),
|
|
1723
|
+
},
|
|
1724
|
+
outputSchema: {
|
|
1725
|
+
success: z.boolean(),
|
|
1726
|
+
workspaceId: z.string(),
|
|
1727
|
+
title: z.string(),
|
|
1728
|
+
},
|
|
1729
|
+
}, async ({ workspaceId: requestedWorkspaceId, title }) => {
|
|
1730
|
+
if (!options.workspaceRegistry) {
|
|
1731
|
+
throw new Error("Workspace registry is required to rename workspaces");
|
|
1732
|
+
}
|
|
1733
|
+
if (!options.emitWorkspaceUpdatesForWorkspaceIds) {
|
|
1734
|
+
throw new Error("Workspace update emitter is required to rename workspaces");
|
|
1735
|
+
}
|
|
1736
|
+
const workspaceId = resolveWorkspaceIdForRename(requestedWorkspaceId);
|
|
1737
|
+
const existing = await options.workspaceRegistry.get(workspaceId);
|
|
1738
|
+
if (!existing) {
|
|
1739
|
+
throw new Error(`Workspace ${workspaceId} not found`);
|
|
1740
|
+
}
|
|
1741
|
+
if (existing.archivedAt) {
|
|
1742
|
+
throw new Error(`Workspace ${workspaceId} is archived`);
|
|
1743
|
+
}
|
|
1744
|
+
await options.workspaceRegistry.upsert({
|
|
1745
|
+
...existing,
|
|
1746
|
+
title,
|
|
1747
|
+
updatedAt: new Date().toISOString(),
|
|
1748
|
+
});
|
|
1749
|
+
await options.emitWorkspaceUpdatesForWorkspaceIds([workspaceId]);
|
|
1750
|
+
return {
|
|
1751
|
+
content: [],
|
|
1752
|
+
structuredContent: ensureValidJson({
|
|
1753
|
+
success: true,
|
|
1754
|
+
workspaceId,
|
|
1755
|
+
title,
|
|
1756
|
+
}),
|
|
1757
|
+
};
|
|
1758
|
+
});
|
|
1759
|
+
registerTool("create_artifact", {
|
|
1760
|
+
title: "Create artifact",
|
|
1761
|
+
description: 'Create an artifact: a self-contained HTML page (report, dashboard, visualization, mockup) generated by a dedicated background agent and shown in the client\'s Artifacts screen. Returns immediately with status "generating"; the artifact flips to "ready" (or "error") on its own, typically within a few minutes — no need to wait or poll. The generator always runs unattended (bypass/no approval prompts) and inherits your provider, model, effort, and mode unless overridden. The generator cannot see this conversation, so the description must carry all content, data, and requirements it needs.',
|
|
1762
|
+
inputSchema: {
|
|
1763
|
+
name: z
|
|
1764
|
+
.string()
|
|
1765
|
+
.trim()
|
|
1766
|
+
.min(1)
|
|
1767
|
+
.optional()
|
|
1768
|
+
.describe("User-visible artifact title. Omit to derive one from the description's first line."),
|
|
1769
|
+
description: z
|
|
1770
|
+
.string()
|
|
1771
|
+
.trim()
|
|
1772
|
+
.min(1, "description is required")
|
|
1773
|
+
.describe("Generation prompt. Self-contained: include all content, data, and requirements — the generator has no access to this conversation."),
|
|
1774
|
+
provider: z
|
|
1775
|
+
.string()
|
|
1776
|
+
.trim()
|
|
1777
|
+
.min(1)
|
|
1778
|
+
.optional()
|
|
1779
|
+
.describe("Provider to generate with, as <provider> or <provider>/<model> (for example codex/gpt-5.4). Defaults to your own provider and model; call list_providers or list_models if uncertain."),
|
|
1780
|
+
model: z
|
|
1781
|
+
.string()
|
|
1782
|
+
.trim()
|
|
1783
|
+
.min(1)
|
|
1784
|
+
.optional()
|
|
1785
|
+
.describe("Model id for the generation agent. Takes precedence over a model embedded in provider."),
|
|
1786
|
+
thinkingOptionId: z
|
|
1787
|
+
.string()
|
|
1788
|
+
.trim()
|
|
1789
|
+
.min(1)
|
|
1790
|
+
.optional()
|
|
1791
|
+
.describe(`${EFFORT_INPUT_DESCRIPTION} Defaults to your own effort option when generating with your provider.`),
|
|
1792
|
+
modeId: z
|
|
1793
|
+
.string()
|
|
1794
|
+
.trim()
|
|
1795
|
+
.min(1)
|
|
1796
|
+
.optional()
|
|
1797
|
+
.describe("Permission mode id for the generation agent (see modes with isUnattended: true in list_providers or inspect_provider). Only unattended (bypass) modes are honored — anything else falls back to the provider's unattended default, so generation never stalls on approval prompts. Defaults to your own mode when generating with your provider."),
|
|
1798
|
+
projectId: z
|
|
1799
|
+
.string()
|
|
1800
|
+
.trim()
|
|
1801
|
+
.min(1)
|
|
1802
|
+
.optional()
|
|
1803
|
+
.describe("Project to file the artifact under, as the project's root directory path. Defaults to your workspace's project."),
|
|
1804
|
+
},
|
|
1805
|
+
outputSchema: {
|
|
1806
|
+
artifactId: z.string(),
|
|
1807
|
+
name: z.string(),
|
|
1808
|
+
status: z.string(),
|
|
1809
|
+
provider: z.string(),
|
|
1810
|
+
model: z.string().nullable(),
|
|
1811
|
+
thinkingOptionId: z.string().nullable(),
|
|
1812
|
+
modeId: z.string().nullable(),
|
|
1813
|
+
projectId: z.string(),
|
|
1814
|
+
guidance: z.string(),
|
|
1815
|
+
},
|
|
1816
|
+
}, async (input) => {
|
|
1817
|
+
const artifactService = options.artifactService;
|
|
1818
|
+
if (!artifactService) {
|
|
1819
|
+
throw new Error("Artifact service is not available on this daemon");
|
|
1820
|
+
}
|
|
1821
|
+
const callerAgent = callerAgentId ? agentManager.getAgent(callerAgentId) : null;
|
|
1822
|
+
const { provider, model } = resolveArtifactProviderModel({
|
|
1823
|
+
providerArg: input.provider,
|
|
1824
|
+
modelArg: input.model,
|
|
1825
|
+
callerProvider: callerAgent?.provider,
|
|
1826
|
+
callerModel: callerAgent?.config.model,
|
|
1827
|
+
});
|
|
1828
|
+
const { thinkingOptionId, modeId } = resolveArtifactGenerationSettings({
|
|
1829
|
+
provider,
|
|
1830
|
+
thinkingOptionIdArg: input.thinkingOptionId,
|
|
1831
|
+
modeIdArg: input.modeId,
|
|
1832
|
+
callerProvider: callerAgent?.provider,
|
|
1833
|
+
callerThinkingOptionId: callerAgent?.config.thinkingOptionId,
|
|
1834
|
+
callerModeId: callerAgent?.config.modeId,
|
|
1835
|
+
});
|
|
1836
|
+
const name = input.name?.trim() || deriveArtifactName(input.description);
|
|
1837
|
+
const providerEntry = (await providerSnapshotManager.listProviders({ wait: true })).find((entry) => entry.provider === provider);
|
|
1838
|
+
if (!providerEntry?.enabled) {
|
|
1839
|
+
throw new Error(`Provider "${provider}" is not available. Call list_providers for options.`);
|
|
1840
|
+
}
|
|
1841
|
+
const resolvedThinkingOptionId = resolveEffortOrDropInherited({
|
|
1842
|
+
requested: thinkingOptionId,
|
|
1843
|
+
explicit: Boolean(input.thinkingOptionId),
|
|
1844
|
+
models: providerEntry.models,
|
|
1845
|
+
model,
|
|
1846
|
+
});
|
|
1847
|
+
const projectId = await resolveArtifactProjectId({
|
|
1848
|
+
projectIdArg: input.projectId,
|
|
1849
|
+
callerWorkspaceId: callerAgent?.workspaceId,
|
|
1850
|
+
workspaceRegistry: options.workspaceRegistry,
|
|
1851
|
+
projectRegistry: options.projectRegistry,
|
|
1852
|
+
});
|
|
1853
|
+
const artifact = await artifactService.create({
|
|
1854
|
+
name,
|
|
1855
|
+
description: input.description,
|
|
1856
|
+
projectId,
|
|
1857
|
+
provider,
|
|
1858
|
+
...(model ? { model } : {}),
|
|
1859
|
+
...(resolvedThinkingOptionId ? { thinkingOptionId: resolvedThinkingOptionId } : {}),
|
|
1860
|
+
...(modeId ? { modeId } : {}),
|
|
1861
|
+
});
|
|
1862
|
+
options.emitArtifactCreated?.(artifact);
|
|
1863
|
+
return {
|
|
1864
|
+
content: [],
|
|
1865
|
+
structuredContent: ensureValidJson({
|
|
1866
|
+
artifactId: artifact.id,
|
|
1867
|
+
name: artifact.name,
|
|
1868
|
+
status: artifact.status,
|
|
1869
|
+
provider,
|
|
1870
|
+
model: artifact.generationModel,
|
|
1871
|
+
thinkingOptionId: artifact.generationThinkingOptionId ?? null,
|
|
1872
|
+
modeId: artifact.generationModeId ?? null,
|
|
1873
|
+
projectId: artifact.projectId,
|
|
1874
|
+
guidance: 'Generation runs unattended in the background; the artifact appears in the Artifacts screen and flips to "ready" when done. You do not need to wait or poll.',
|
|
1875
|
+
}),
|
|
1876
|
+
};
|
|
1877
|
+
});
|
|
1878
|
+
registerTool("list_artifacts", {
|
|
1879
|
+
title: "List artifacts",
|
|
1880
|
+
description: "List generated artifacts with their ids, status, and generation settings, optionally filtered by project.",
|
|
1881
|
+
inputSchema: {
|
|
1882
|
+
projectId: z
|
|
1883
|
+
.string()
|
|
1884
|
+
.trim()
|
|
1885
|
+
.min(1)
|
|
1886
|
+
.optional()
|
|
1887
|
+
.describe("Filter by project root directory path. Omit to list every project."),
|
|
1888
|
+
},
|
|
1889
|
+
outputSchema: {
|
|
1890
|
+
artifacts: z.array(ArtifactToolSummarySchema),
|
|
1891
|
+
},
|
|
1892
|
+
}, async ({ projectId }) => {
|
|
1893
|
+
const artifactService = options.artifactService;
|
|
1894
|
+
if (!artifactService) {
|
|
1895
|
+
throw new Error("Artifact service is not available on this daemon");
|
|
1896
|
+
}
|
|
1897
|
+
const artifacts = (await artifactService.list(projectId)).map(toArtifactToolSummary);
|
|
1898
|
+
return {
|
|
1899
|
+
content: [],
|
|
1900
|
+
structuredContent: ensureValidJson({ artifacts }),
|
|
1901
|
+
};
|
|
1902
|
+
});
|
|
1903
|
+
registerTool("inspect_artifact", {
|
|
1904
|
+
title: "Inspect artifact",
|
|
1905
|
+
description: "Inspect an artifact and its generation run history.",
|
|
1906
|
+
inputSchema: {
|
|
1907
|
+
artifactId: z
|
|
1908
|
+
.string()
|
|
1909
|
+
.trim()
|
|
1910
|
+
.min(1)
|
|
1911
|
+
.describe("Artifact to inspect; call list_artifacts for ids."),
|
|
1912
|
+
},
|
|
1913
|
+
outputSchema: StoredArtifactSchema.shape,
|
|
1914
|
+
}, async ({ artifactId }) => {
|
|
1915
|
+
const artifactService = options.artifactService;
|
|
1916
|
+
if (!artifactService) {
|
|
1917
|
+
throw new Error("Artifact service is not available on this daemon");
|
|
1918
|
+
}
|
|
1919
|
+
const record = await artifactService.inspect(artifactId);
|
|
1920
|
+
return {
|
|
1921
|
+
content: [],
|
|
1922
|
+
structuredContent: ensureValidJson(record),
|
|
1923
|
+
};
|
|
1924
|
+
});
|
|
1925
|
+
registerTool("update_artifact", {
|
|
1926
|
+
title: "Update artifact",
|
|
1927
|
+
description: "Edit an artifact's metadata — name, prompt, project, provider, model, effort — WITHOUT re-running generation. Call generate_artifact afterwards to re-generate with the new settings.",
|
|
1928
|
+
inputSchema: {
|
|
1929
|
+
artifactId: z
|
|
1930
|
+
.string()
|
|
1931
|
+
.trim()
|
|
1932
|
+
.min(1)
|
|
1933
|
+
.describe("Artifact to edit; call list_artifacts for ids."),
|
|
1934
|
+
name: z.string().trim().min(1).optional().describe("New name."),
|
|
1935
|
+
description: z.string().trim().min(1).optional().describe("New generation prompt."),
|
|
1936
|
+
provider: z
|
|
1937
|
+
.string()
|
|
1938
|
+
.trim()
|
|
1939
|
+
.min(1)
|
|
1940
|
+
.optional()
|
|
1941
|
+
.describe("New provider, as <provider> or <provider>/<model>."),
|
|
1942
|
+
model: z
|
|
1943
|
+
.string()
|
|
1944
|
+
.trim()
|
|
1945
|
+
.min(1)
|
|
1946
|
+
.nullable()
|
|
1947
|
+
.optional()
|
|
1948
|
+
.describe("New model id (null to clear back to the provider default)."),
|
|
1949
|
+
thinkingOptionId: z
|
|
1950
|
+
.string()
|
|
1951
|
+
.trim()
|
|
1952
|
+
.min(1)
|
|
1953
|
+
.nullable()
|
|
1954
|
+
.optional()
|
|
1955
|
+
.describe(`New effort (null to clear). ${EFFORT_INPUT_DESCRIPTION}`),
|
|
1956
|
+
projectId: z.string().trim().min(1).optional().describe("New project root directory path."),
|
|
1957
|
+
},
|
|
1958
|
+
outputSchema: ArtifactToolSummarySchema.shape,
|
|
1959
|
+
}, async (input) => {
|
|
1960
|
+
const artifactService = options.artifactService;
|
|
1961
|
+
if (!artifactService) {
|
|
1962
|
+
throw new Error("Artifact service is not available on this daemon");
|
|
1963
|
+
}
|
|
1964
|
+
const existing = await requireArtifact(artifactService, input.artifactId);
|
|
1965
|
+
const targets = resolveArtifactUpdateTargets(input, existing);
|
|
1966
|
+
if (targets.provider) {
|
|
1967
|
+
const entry = (await providerSnapshotManager.listProviders({ wait: true })).find((candidate) => candidate.provider === targets.provider);
|
|
1968
|
+
if (!entry?.enabled) {
|
|
1969
|
+
throw new Error(`Provider "${targets.provider}" is not available. Call list_providers for options.`);
|
|
1970
|
+
}
|
|
1971
|
+
}
|
|
1972
|
+
const effortModels = input.thinkingOptionId && targets.effortProvider
|
|
1973
|
+
? await listProviderModels(targets.effortProvider)
|
|
1974
|
+
: [];
|
|
1975
|
+
const thinkingPatch = resolveArtifactUpdateEffort({
|
|
1976
|
+
requested: input.thinkingOptionId,
|
|
1977
|
+
models: effortModels,
|
|
1978
|
+
model: targets.effortModel,
|
|
1979
|
+
});
|
|
1980
|
+
const updated = await artifactService.update(buildArtifactUpdateServiceInput(input, targets, thinkingPatch));
|
|
1981
|
+
options.emitArtifactUpdated?.(updated);
|
|
1982
|
+
return {
|
|
1983
|
+
content: [],
|
|
1984
|
+
structuredContent: ensureValidJson(toArtifactToolSummary(updated)),
|
|
1985
|
+
};
|
|
1986
|
+
});
|
|
1987
|
+
registerTool("generate_artifact", {
|
|
1988
|
+
title: "Generate artifact",
|
|
1989
|
+
description: "Re-run generation for an existing artifact using its stored settings (prompt, provider, model, effort). Edit those first via update_artifact. Generation runs unattended in the background.",
|
|
1990
|
+
inputSchema: {
|
|
1991
|
+
artifactId: z
|
|
1992
|
+
.string()
|
|
1993
|
+
.trim()
|
|
1994
|
+
.min(1)
|
|
1995
|
+
.describe("Artifact to regenerate; call list_artifacts for ids."),
|
|
1996
|
+
},
|
|
1997
|
+
outputSchema: {
|
|
1998
|
+
...ArtifactToolSummarySchema.shape,
|
|
1999
|
+
guidance: z.string(),
|
|
2000
|
+
},
|
|
2001
|
+
}, async ({ artifactId }) => {
|
|
2002
|
+
const artifactService = options.artifactService;
|
|
2003
|
+
if (!artifactService) {
|
|
2004
|
+
throw new Error("Artifact service is not available on this daemon");
|
|
2005
|
+
}
|
|
2006
|
+
const existing = await requireArtifact(artifactService, artifactId);
|
|
2007
|
+
if (existing.status === "generating") {
|
|
2008
|
+
throw new Error(`Artifact ${artifactId} is already generating. Wait for it to finish or cancel it from the Artifacts screen first.`);
|
|
2009
|
+
}
|
|
2010
|
+
const artifact = await artifactService.regenerate(artifactId);
|
|
2011
|
+
options.emitArtifactUpdated?.(artifact);
|
|
2012
|
+
return {
|
|
2013
|
+
content: [],
|
|
2014
|
+
structuredContent: ensureValidJson({
|
|
2015
|
+
...toArtifactToolSummary(artifact),
|
|
2016
|
+
guidance: 'Generation runs unattended in the background; the artifact appears in the Artifacts screen and flips to "ready" when done. You do not need to wait or poll.',
|
|
2017
|
+
}),
|
|
2018
|
+
};
|
|
2019
|
+
});
|
|
2020
|
+
registerTool("list_terminals", {
|
|
2021
|
+
title: "List terminals",
|
|
2022
|
+
description: "List terminals for a working directory or across all working directories.",
|
|
2023
|
+
inputSchema: {
|
|
2024
|
+
cwd: z
|
|
2025
|
+
.string()
|
|
2026
|
+
.optional()
|
|
2027
|
+
.describe("Optional working directory. Defaults to your current working directory."),
|
|
2028
|
+
all: z.boolean().optional().describe("List terminals across all working directories."),
|
|
2029
|
+
},
|
|
2030
|
+
outputSchema: {
|
|
2031
|
+
terminals: z.array(TerminalSummarySchema),
|
|
2032
|
+
},
|
|
2033
|
+
}, async ({ cwd, all }) => {
|
|
2034
|
+
if (!terminalManager) {
|
|
2035
|
+
throw new Error("Terminal manager is not configured");
|
|
2036
|
+
}
|
|
2037
|
+
const terminals = all
|
|
2038
|
+
? (await Promise.all(terminalManager.listDirectories().map(async (directory) => (await terminalManager.getTerminals(directory)).map((terminal) => ({
|
|
2039
|
+
id: terminal.id,
|
|
2040
|
+
name: terminal.name,
|
|
2041
|
+
cwd: terminal.cwd,
|
|
2042
|
+
}))))).flat()
|
|
2043
|
+
: (await terminalManager.getTerminals(resolveScopedCwd(cwd, { required: true }))).map((terminal) => ({
|
|
2044
|
+
id: terminal.id,
|
|
2045
|
+
name: terminal.name,
|
|
2046
|
+
cwd: terminal.cwd,
|
|
2047
|
+
}));
|
|
2048
|
+
return {
|
|
2049
|
+
content: [],
|
|
2050
|
+
structuredContent: ensureValidJson({ terminals }),
|
|
2051
|
+
};
|
|
2052
|
+
});
|
|
2053
|
+
registerTool("create_terminal", {
|
|
2054
|
+
title: "Create terminal",
|
|
2055
|
+
description: "Create a terminal session for a working directory.",
|
|
2056
|
+
inputSchema: {
|
|
2057
|
+
cwd: z
|
|
2058
|
+
.string()
|
|
2059
|
+
.optional()
|
|
2060
|
+
.describe("Optional working directory. Defaults to your current working directory."),
|
|
2061
|
+
name: z.string().optional().describe("Optional terminal name."),
|
|
2062
|
+
},
|
|
2063
|
+
outputSchema: TerminalSummarySchema.shape,
|
|
2064
|
+
}, async ({ cwd, name }) => {
|
|
2065
|
+
if (!terminalManager) {
|
|
2066
|
+
throw new Error("Terminal manager is not configured");
|
|
2067
|
+
}
|
|
2068
|
+
const resolvedCwd = resolveScopedCwd(cwd, { required: true });
|
|
2069
|
+
const workspaceId = await resolveTerminalWorkspaceId(resolvedCwd);
|
|
2070
|
+
const terminal = await terminalManager.createTerminal({
|
|
2071
|
+
cwd: resolvedCwd,
|
|
2072
|
+
workspaceId,
|
|
2073
|
+
...(name?.trim() ? { name: name.trim() } : {}),
|
|
2074
|
+
});
|
|
2075
|
+
return {
|
|
2076
|
+
content: [],
|
|
2077
|
+
structuredContent: ensureValidJson({
|
|
2078
|
+
id: terminal.id,
|
|
2079
|
+
name: terminal.name,
|
|
2080
|
+
cwd: terminal.cwd,
|
|
2081
|
+
}),
|
|
2082
|
+
};
|
|
2083
|
+
});
|
|
2084
|
+
registerTool("kill_terminal", {
|
|
2085
|
+
title: "Kill terminal",
|
|
2086
|
+
description: "Kill an existing terminal session.",
|
|
2087
|
+
inputSchema: {
|
|
2088
|
+
terminalId: z.string(),
|
|
2089
|
+
},
|
|
2090
|
+
outputSchema: {
|
|
2091
|
+
success: z.boolean(),
|
|
2092
|
+
},
|
|
2093
|
+
}, async ({ terminalId }) => {
|
|
2094
|
+
if (!terminalManager) {
|
|
2095
|
+
throw new Error("Terminal manager is not configured");
|
|
2096
|
+
}
|
|
2097
|
+
const terminal = terminalManager.getTerminal(terminalId);
|
|
2098
|
+
if (!terminal) {
|
|
2099
|
+
throw new Error(`Terminal ${terminalId} not found`);
|
|
2100
|
+
}
|
|
2101
|
+
terminal.kill();
|
|
2102
|
+
return {
|
|
2103
|
+
content: [],
|
|
2104
|
+
structuredContent: ensureValidJson({ success: true }),
|
|
2105
|
+
};
|
|
2106
|
+
});
|
|
2107
|
+
registerTool("capture_terminal", {
|
|
2108
|
+
title: "Capture terminal",
|
|
2109
|
+
description: "Capture plain-text terminal output lines from a terminal session.",
|
|
2110
|
+
inputSchema: {
|
|
2111
|
+
terminalId: z.string(),
|
|
2112
|
+
start: z.number().optional(),
|
|
2113
|
+
end: z.number().optional(),
|
|
2114
|
+
scrollback: z.boolean().optional(),
|
|
2115
|
+
stripAnsi: z.boolean().optional().default(true),
|
|
2116
|
+
},
|
|
2117
|
+
outputSchema: {
|
|
2118
|
+
terminalId: z.string(),
|
|
2119
|
+
lines: z.array(z.string()),
|
|
2120
|
+
totalLines: z.number().int().nonnegative(),
|
|
2121
|
+
},
|
|
2122
|
+
}, async ({ terminalId, start, end, scrollback, stripAnsi = true }) => {
|
|
2123
|
+
if (!terminalManager) {
|
|
2124
|
+
throw new Error("Terminal manager is not configured");
|
|
2125
|
+
}
|
|
2126
|
+
if (!terminalManager.getTerminal(terminalId)) {
|
|
2127
|
+
throw new Error(`Terminal ${terminalId} not found`);
|
|
2128
|
+
}
|
|
2129
|
+
const capture = await terminalManager.captureTerminal(terminalId, {
|
|
2130
|
+
start: scrollback ? 0 : start,
|
|
2131
|
+
end,
|
|
2132
|
+
stripAnsi,
|
|
2133
|
+
});
|
|
2134
|
+
return {
|
|
2135
|
+
content: [],
|
|
2136
|
+
structuredContent: ensureValidJson({
|
|
2137
|
+
terminalId,
|
|
2138
|
+
lines: capture.lines,
|
|
2139
|
+
totalLines: capture.totalLines,
|
|
2140
|
+
}),
|
|
2141
|
+
};
|
|
2142
|
+
});
|
|
2143
|
+
registerTool("send_terminal_keys", {
|
|
2144
|
+
title: "Send terminal keys",
|
|
2145
|
+
description: "Send literal text or special key tokens to a terminal session.",
|
|
2146
|
+
inputSchema: {
|
|
2147
|
+
terminalId: z.string(),
|
|
2148
|
+
keys: z.string(),
|
|
2149
|
+
literal: z.boolean().optional(),
|
|
2150
|
+
},
|
|
2151
|
+
outputSchema: {
|
|
2152
|
+
success: z.boolean(),
|
|
2153
|
+
},
|
|
2154
|
+
}, async ({ terminalId, keys, literal = false }) => {
|
|
2155
|
+
if (!terminalManager) {
|
|
2156
|
+
throw new Error("Terminal manager is not configured");
|
|
2157
|
+
}
|
|
2158
|
+
const terminal = terminalManager.getTerminal(terminalId);
|
|
2159
|
+
if (!terminal) {
|
|
2160
|
+
throw new Error(`Terminal ${terminalId} not found`);
|
|
2161
|
+
}
|
|
2162
|
+
terminal.send({
|
|
2163
|
+
type: "input",
|
|
2164
|
+
data: resolveTerminalKeyToken(keys, literal),
|
|
2165
|
+
});
|
|
2166
|
+
return {
|
|
2167
|
+
content: [],
|
|
2168
|
+
structuredContent: ensureValidJson({ success: true }),
|
|
2169
|
+
};
|
|
2170
|
+
});
|
|
2171
|
+
// Build a new-agent schedule config from either a personality binding or a
|
|
2172
|
+
// raw provider. A personality is validated + resolved now (to fill the
|
|
2173
|
+
// required provider field and fail fast), and its name is stored so each run
|
|
2174
|
+
// re-resolves it authoritatively.
|
|
2175
|
+
const buildScheduleNewAgentConfig = async (input) => {
|
|
2176
|
+
const personalityName = input.personality?.trim();
|
|
2177
|
+
if (personalityName) {
|
|
2178
|
+
const brain = await resolveCreateAgentBrain({
|
|
2179
|
+
personalityName,
|
|
2180
|
+
providerOverride: input.provider,
|
|
2181
|
+
modeOverride: undefined,
|
|
2182
|
+
thinkingOverride: input.thinkingOptionId,
|
|
2183
|
+
cwd: input.cwd,
|
|
2184
|
+
});
|
|
2185
|
+
const baseTarget = resolveNewAgentScheduleTarget({
|
|
2186
|
+
provider: brain.providerModel,
|
|
2187
|
+
cwd: input.cwd,
|
|
2188
|
+
});
|
|
2189
|
+
return {
|
|
2190
|
+
...baseTarget.config,
|
|
2191
|
+
personality: personalityName,
|
|
2192
|
+
...(brain.modeId !== undefined ? { modeId: brain.modeId } : {}),
|
|
2193
|
+
...(brain.thinkingOptionId !== undefined
|
|
2194
|
+
? { thinkingOptionId: brain.thinkingOptionId }
|
|
2195
|
+
: {}),
|
|
2196
|
+
};
|
|
2197
|
+
}
|
|
2198
|
+
const baseTarget = resolveNewAgentScheduleTarget({ provider: input.provider, cwd: input.cwd });
|
|
2199
|
+
const config = {
|
|
2200
|
+
...baseTarget.config,
|
|
2201
|
+
};
|
|
2202
|
+
const inheritedEffort = typeof config.thinkingOptionId === "string" ? config.thinkingOptionId : undefined;
|
|
2203
|
+
const requestedEffort = input.thinkingOptionId ?? inheritedEffort;
|
|
2204
|
+
if (requestedEffort) {
|
|
2205
|
+
const resolved = resolveEffortOrDropInherited({
|
|
2206
|
+
requested: requestedEffort,
|
|
2207
|
+
explicit: Boolean(input.thinkingOptionId),
|
|
2208
|
+
models: await listProviderModels(config.provider),
|
|
2209
|
+
model: config.model,
|
|
2210
|
+
});
|
|
2211
|
+
if (resolved === undefined) {
|
|
2212
|
+
delete config.thinkingOptionId;
|
|
2213
|
+
}
|
|
2214
|
+
else {
|
|
2215
|
+
config.thinkingOptionId = resolved;
|
|
2216
|
+
}
|
|
2217
|
+
}
|
|
2218
|
+
return config;
|
|
2219
|
+
};
|
|
2220
|
+
registerTool("create_schedule", {
|
|
2221
|
+
title: "Create schedule",
|
|
2222
|
+
description: "Create a recurring schedule that starts a new agent on a cron cadence.",
|
|
2223
|
+
inputSchema: {
|
|
2224
|
+
prompt: z.string().trim().min(1, "prompt is required"),
|
|
2225
|
+
cron: z.string().trim().min(1, "cron is required"),
|
|
2226
|
+
timezone: z
|
|
2227
|
+
.string()
|
|
2228
|
+
.trim()
|
|
2229
|
+
.min(1)
|
|
2230
|
+
.optional()
|
|
2231
|
+
.describe("IANA time zone for the cron cadence. For example: America/New_York."),
|
|
2232
|
+
name: z.string().optional(),
|
|
2233
|
+
provider: AgentProviderEnum.optional().describe("Provider, or provider/model (for example: codex or codex/gpt-5.4). Required unless `personality` is given."),
|
|
2234
|
+
personality: z
|
|
2235
|
+
.string()
|
|
2236
|
+
.trim()
|
|
2237
|
+
.min(1)
|
|
2238
|
+
.optional()
|
|
2239
|
+
.describe("Bind this schedule to an Agent Personality by name. Each run re-resolves it against the run workspace and hard-fails if it's unavailable. Requires the Orchestrator role when called by an agent."),
|
|
2240
|
+
cwd: z.string().optional(),
|
|
2241
|
+
thinkingOptionId: z
|
|
2242
|
+
.string()
|
|
2243
|
+
.trim()
|
|
2244
|
+
.min(1)
|
|
2245
|
+
.optional()
|
|
2246
|
+
.describe(`${EFFORT_INPUT_DESCRIPTION} Defaults to your own effort option when scheduling your provider.`),
|
|
2247
|
+
maxRuns: z.number().int().positive().optional(),
|
|
2248
|
+
expiresIn: z.string().optional(),
|
|
2249
|
+
},
|
|
2250
|
+
outputSchema: ScheduleSummarySchema.shape,
|
|
2251
|
+
}, async ({ prompt, cron, timezone, name, provider, personality, cwd, thinkingOptionId, maxRuns, expiresIn, }) => {
|
|
2252
|
+
if (!scheduleService) {
|
|
2253
|
+
throw new Error("Schedule service is not configured");
|
|
2254
|
+
}
|
|
2255
|
+
const config = await buildScheduleNewAgentConfig({
|
|
2256
|
+
provider,
|
|
2257
|
+
personality,
|
|
2258
|
+
cwd,
|
|
2259
|
+
thinkingOptionId,
|
|
2260
|
+
});
|
|
2261
|
+
const expiresAt = buildScheduleExpiry(expiresIn);
|
|
2262
|
+
const schedule = await scheduleService.createOrReplace({
|
|
2263
|
+
prompt: prompt.trim(),
|
|
2264
|
+
cadence: buildCronScheduleCadence({
|
|
2265
|
+
cron,
|
|
2266
|
+
...(timezone !== undefined ? { timezone } : {}),
|
|
2267
|
+
}),
|
|
2268
|
+
target: { type: "new-agent", config },
|
|
2269
|
+
...(name?.trim() ? { name: name.trim() } : {}),
|
|
2270
|
+
...(maxRuns === undefined ? {} : { maxRuns }),
|
|
2271
|
+
...(expiresAt === undefined ? {} : { expiresAt }),
|
|
2272
|
+
});
|
|
2273
|
+
return {
|
|
2274
|
+
content: [],
|
|
2275
|
+
structuredContent: ensureValidJson(toScheduleSummary(schedule)),
|
|
2276
|
+
};
|
|
2277
|
+
});
|
|
2278
|
+
registerTool("create_heartbeat", {
|
|
2279
|
+
title: "Create heartbeat",
|
|
2280
|
+
description: "Create a recurring heartbeat that sends you a prompt on a cron cadence.",
|
|
2281
|
+
inputSchema: {
|
|
2282
|
+
prompt: z.string().trim().min(1, "prompt is required"),
|
|
2283
|
+
cron: z.string().trim().min(1, "cron is required"),
|
|
2284
|
+
timezone: z
|
|
2285
|
+
.string()
|
|
2286
|
+
.trim()
|
|
2287
|
+
.min(1)
|
|
2288
|
+
.optional()
|
|
2289
|
+
.describe("IANA time zone for the cron cadence. For example: America/New_York."),
|
|
2290
|
+
name: z.string().optional(),
|
|
2291
|
+
maxRuns: z.number().int().positive().optional(),
|
|
2292
|
+
expiresIn: z.string().optional(),
|
|
2293
|
+
},
|
|
2294
|
+
outputSchema: ScheduleSummarySchema.shape,
|
|
2295
|
+
}, async ({ prompt, cron, timezone, name, maxRuns, expiresIn }) => {
|
|
2296
|
+
if (!scheduleService) {
|
|
2297
|
+
throw new Error("Schedule service is not configured");
|
|
2298
|
+
}
|
|
2299
|
+
if (!callerAgentId) {
|
|
2300
|
+
throw new Error("create_heartbeat requires an agent-scoped session");
|
|
2301
|
+
}
|
|
2302
|
+
resolveCallerAgent();
|
|
2303
|
+
const expiresAt = buildScheduleExpiry(expiresIn);
|
|
2304
|
+
const schedule = await scheduleService.createOrReplace({
|
|
2305
|
+
prompt: prompt.trim(),
|
|
2306
|
+
cadence: buildCronScheduleCadence({
|
|
2307
|
+
cron,
|
|
2308
|
+
...(timezone !== undefined ? { timezone } : {}),
|
|
2309
|
+
}),
|
|
2310
|
+
target: { type: "agent", agentId: callerAgentId },
|
|
2311
|
+
...(name?.trim() ? { name: name.trim() } : {}),
|
|
2312
|
+
...(maxRuns === undefined ? {} : { maxRuns }),
|
|
2313
|
+
...(expiresAt === undefined ? {} : { expiresAt }),
|
|
2314
|
+
});
|
|
2315
|
+
return {
|
|
2316
|
+
content: [],
|
|
2317
|
+
structuredContent: ensureValidJson(toScheduleSummary(schedule)),
|
|
2318
|
+
};
|
|
2319
|
+
});
|
|
2320
|
+
registerTool("list_schedules", {
|
|
2321
|
+
title: "List schedules",
|
|
2322
|
+
description: "List all schedules managed by the daemon.",
|
|
2323
|
+
inputSchema: {},
|
|
2324
|
+
outputSchema: {
|
|
2325
|
+
schedules: z.array(ScheduleSummarySchema),
|
|
2326
|
+
},
|
|
2327
|
+
}, async () => {
|
|
2328
|
+
if (!scheduleService) {
|
|
2329
|
+
throw new Error("Schedule service is not configured");
|
|
2330
|
+
}
|
|
2331
|
+
const schedules = (await scheduleService.list()).map((schedule) => toScheduleSummary(schedule));
|
|
2332
|
+
return {
|
|
2333
|
+
content: [],
|
|
2334
|
+
structuredContent: ensureValidJson({ schedules }),
|
|
2335
|
+
};
|
|
2336
|
+
});
|
|
2337
|
+
registerTool("inspect_schedule", {
|
|
2338
|
+
title: "Inspect schedule",
|
|
2339
|
+
description: "Inspect a schedule and its run history.",
|
|
2340
|
+
inputSchema: {
|
|
2341
|
+
id: z.string(),
|
|
2342
|
+
},
|
|
2343
|
+
outputSchema: StoredScheduleSchema.shape,
|
|
2344
|
+
}, async ({ id }) => {
|
|
2345
|
+
if (!scheduleService) {
|
|
2346
|
+
throw new Error("Schedule service is not configured");
|
|
2347
|
+
}
|
|
2348
|
+
const schedule = await scheduleService.inspect(id);
|
|
2349
|
+
return {
|
|
2350
|
+
content: [],
|
|
2351
|
+
structuredContent: ensureValidJson(schedule),
|
|
2352
|
+
};
|
|
2353
|
+
});
|
|
2354
|
+
registerTool("pause_schedule", {
|
|
2355
|
+
title: "Pause schedule",
|
|
2356
|
+
description: "Pause an active schedule.",
|
|
2357
|
+
inputSchema: {
|
|
2358
|
+
id: z.string(),
|
|
2359
|
+
},
|
|
2360
|
+
outputSchema: {
|
|
2361
|
+
success: z.boolean(),
|
|
2362
|
+
},
|
|
2363
|
+
}, async ({ id }) => {
|
|
2364
|
+
if (!scheduleService) {
|
|
2365
|
+
throw new Error("Schedule service is not configured");
|
|
2366
|
+
}
|
|
2367
|
+
await scheduleService.pause(id);
|
|
2368
|
+
return {
|
|
2369
|
+
content: [],
|
|
2370
|
+
structuredContent: ensureValidJson({ success: true }),
|
|
2371
|
+
};
|
|
2372
|
+
});
|
|
2373
|
+
registerTool("resume_schedule", {
|
|
2374
|
+
title: "Resume schedule",
|
|
2375
|
+
description: "Resume a paused schedule.",
|
|
2376
|
+
inputSchema: {
|
|
2377
|
+
id: z.string(),
|
|
2378
|
+
},
|
|
2379
|
+
outputSchema: {
|
|
2380
|
+
success: z.boolean(),
|
|
2381
|
+
},
|
|
2382
|
+
}, async ({ id }) => {
|
|
2383
|
+
if (!scheduleService) {
|
|
2384
|
+
throw new Error("Schedule service is not configured");
|
|
2385
|
+
}
|
|
2386
|
+
await scheduleService.resume(id);
|
|
2387
|
+
return {
|
|
2388
|
+
content: [],
|
|
2389
|
+
structuredContent: ensureValidJson({ success: true }),
|
|
2390
|
+
};
|
|
2391
|
+
});
|
|
2392
|
+
registerTool("delete_schedule", {
|
|
2393
|
+
title: "Delete schedule",
|
|
2394
|
+
description: "Delete a schedule permanently.",
|
|
2395
|
+
inputSchema: {
|
|
2396
|
+
id: z.string(),
|
|
2397
|
+
},
|
|
2398
|
+
outputSchema: {
|
|
2399
|
+
success: z.boolean(),
|
|
2400
|
+
},
|
|
2401
|
+
}, async ({ id }) => {
|
|
2402
|
+
if (!scheduleService) {
|
|
2403
|
+
throw new Error("Schedule service is not configured");
|
|
2404
|
+
}
|
|
2405
|
+
await scheduleService.delete(id);
|
|
2406
|
+
return {
|
|
2407
|
+
content: [],
|
|
2408
|
+
structuredContent: ensureValidJson({ success: true }),
|
|
2409
|
+
};
|
|
2410
|
+
});
|
|
2411
|
+
registerTool("update_schedule", {
|
|
2412
|
+
title: "Update schedule",
|
|
2413
|
+
description: "Update an existing schedule. Only provided fields are changed; omitted fields remain unchanged.",
|
|
2414
|
+
inputSchema: {
|
|
2415
|
+
id: z.string(),
|
|
2416
|
+
every: z.string().optional().describe("New interval duration string (e.g. 5m, 1h)."),
|
|
2417
|
+
cron: z.string().optional().describe("New cron expression."),
|
|
2418
|
+
timezone: z
|
|
2419
|
+
.string()
|
|
2420
|
+
.trim()
|
|
2421
|
+
.min(1)
|
|
2422
|
+
.optional()
|
|
2423
|
+
.describe("IANA time zone for cron cadence; requires cron. For example: America/New_York."),
|
|
2424
|
+
name: z.string().nullable().optional().describe("New name (null to clear)."),
|
|
2425
|
+
prompt: z.string().trim().min(1).optional().describe("New prompt text."),
|
|
2426
|
+
maxRuns: z
|
|
2427
|
+
.number()
|
|
2428
|
+
.int()
|
|
2429
|
+
.positive()
|
|
2430
|
+
.nullable()
|
|
2431
|
+
.optional()
|
|
2432
|
+
.describe("New max runs limit (null to clear)."),
|
|
2433
|
+
provider: z
|
|
2434
|
+
.string()
|
|
2435
|
+
.trim()
|
|
2436
|
+
.min(1)
|
|
2437
|
+
.optional()
|
|
2438
|
+
.describe("New provider for new-agent target."),
|
|
2439
|
+
personality: z
|
|
2440
|
+
.string()
|
|
2441
|
+
.trim()
|
|
2442
|
+
.min(1)
|
|
2443
|
+
.nullable()
|
|
2444
|
+
.optional()
|
|
2445
|
+
.describe("Bind (or, with null, unbind) an Agent Personality by name for the new-agent target. Re-resolved at each run."),
|
|
2446
|
+
model: z
|
|
2447
|
+
.string()
|
|
2448
|
+
.trim()
|
|
2449
|
+
.min(1)
|
|
2450
|
+
.nullable()
|
|
2451
|
+
.optional()
|
|
2452
|
+
.describe("New model for new-agent target (null to clear)."),
|
|
2453
|
+
mode: z
|
|
2454
|
+
.string()
|
|
2455
|
+
.trim()
|
|
2456
|
+
.min(1)
|
|
2457
|
+
.nullable()
|
|
2458
|
+
.optional()
|
|
2459
|
+
.describe("New mode for new-agent target (null to clear)."),
|
|
2460
|
+
thinkingOptionId: z
|
|
2461
|
+
.string()
|
|
2462
|
+
.trim()
|
|
2463
|
+
.min(1)
|
|
2464
|
+
.nullable()
|
|
2465
|
+
.optional()
|
|
2466
|
+
.describe(`New effort for new-agent target (null to clear). ${EFFORT_INPUT_DESCRIPTION}`),
|
|
2467
|
+
cwd: z.string().trim().min(1).optional().describe("New cwd for new-agent target."),
|
|
2468
|
+
expiresIn: z
|
|
2469
|
+
.string()
|
|
2470
|
+
.optional()
|
|
2471
|
+
.describe("New relative expiry duration (for example: 1h, 2d)."),
|
|
2472
|
+
clearExpires: z.boolean().optional().describe("Clear any schedule expiry."),
|
|
2473
|
+
},
|
|
2474
|
+
outputSchema: StoredScheduleSchema.shape,
|
|
2475
|
+
}, async (input) => {
|
|
2476
|
+
if (!scheduleService) {
|
|
2477
|
+
throw new Error("Schedule service is not configured");
|
|
2478
|
+
}
|
|
2479
|
+
let resolvedInput = input;
|
|
2480
|
+
if (typeof input.thinkingOptionId === "string") {
|
|
2481
|
+
// Resolve against the provider/model the schedule ends up with —
|
|
2482
|
+
// either from this same update or from the stored target.
|
|
2483
|
+
const existing = await scheduleService.inspect(input.id);
|
|
2484
|
+
const existingConfig = existing?.target.type === "new-agent" ? existing.target.config : undefined;
|
|
2485
|
+
const providerModelPatch = resolveScheduleUpdateProviderAndModel({
|
|
2486
|
+
provider: input.provider,
|
|
2487
|
+
model: input.model,
|
|
2488
|
+
});
|
|
2489
|
+
const provider = providerModelPatch.provider ?? existingConfig?.provider;
|
|
2490
|
+
const model = providerModelPatch.model !== undefined
|
|
2491
|
+
? (providerModelPatch.model ?? undefined)
|
|
2492
|
+
: existingConfig?.model;
|
|
2493
|
+
if (provider) {
|
|
2494
|
+
resolvedInput = {
|
|
2495
|
+
...input,
|
|
2496
|
+
thinkingOptionId: resolveEffortAgainstModels({
|
|
2497
|
+
requested: input.thinkingOptionId,
|
|
2498
|
+
models: await listProviderModels(provider),
|
|
2499
|
+
model,
|
|
2500
|
+
}),
|
|
2501
|
+
};
|
|
2502
|
+
}
|
|
2503
|
+
}
|
|
2504
|
+
const schedule = await scheduleService.update(buildScheduleUpdateInput(resolvedInput));
|
|
2505
|
+
return {
|
|
2506
|
+
content: [],
|
|
2507
|
+
structuredContent: ensureValidJson(schedule),
|
|
2508
|
+
};
|
|
2509
|
+
});
|
|
2510
|
+
registerTool("schedule_logs", {
|
|
2511
|
+
title: "Schedule logs",
|
|
2512
|
+
description: "Get the run history (logs) for a schedule.",
|
|
2513
|
+
inputSchema: {
|
|
2514
|
+
id: z.string(),
|
|
2515
|
+
},
|
|
2516
|
+
outputSchema: {
|
|
2517
|
+
runs: z.array(ScheduleRunSchema),
|
|
2518
|
+
},
|
|
2519
|
+
}, async ({ id }) => {
|
|
2520
|
+
if (!scheduleService) {
|
|
2521
|
+
throw new Error("Schedule service is not configured");
|
|
2522
|
+
}
|
|
2523
|
+
const runs = await scheduleService.logs(id);
|
|
2524
|
+
return {
|
|
2525
|
+
content: [],
|
|
2526
|
+
structuredContent: ensureValidJson({ runs }),
|
|
2527
|
+
};
|
|
2528
|
+
});
|
|
2529
|
+
registerTool("list_providers", {
|
|
2530
|
+
title: "List providers",
|
|
2531
|
+
description: "List configured agent providers, availability, and their modes.",
|
|
2532
|
+
inputSchema: {},
|
|
2533
|
+
outputSchema: {
|
|
2534
|
+
providers: z.array(ProviderSummarySchema),
|
|
2535
|
+
},
|
|
2536
|
+
}, async () => {
|
|
2537
|
+
const providers = (await providerSnapshotManager.listProviders({ wait: true })).map(toProviderSummary);
|
|
2538
|
+
return {
|
|
2539
|
+
content: [],
|
|
2540
|
+
structuredContent: ensureValidJson({ providers }),
|
|
2541
|
+
};
|
|
2542
|
+
});
|
|
2543
|
+
registerTool("list_models", {
|
|
2544
|
+
title: "List models",
|
|
2545
|
+
description: "List models for an agent provider.",
|
|
2546
|
+
inputSchema: {
|
|
2547
|
+
provider: AgentProviderEnum,
|
|
2548
|
+
},
|
|
2549
|
+
outputSchema: {
|
|
2550
|
+
provider: z.string(),
|
|
2551
|
+
models: z.array(AgentModelSchema),
|
|
2552
|
+
},
|
|
2553
|
+
}, async ({ provider }) => {
|
|
2554
|
+
const models = await providerSnapshotManager.listModels({
|
|
2555
|
+
provider,
|
|
2556
|
+
wait: true,
|
|
2557
|
+
});
|
|
2558
|
+
return {
|
|
2559
|
+
content: [],
|
|
2560
|
+
structuredContent: ensureValidJson({
|
|
2561
|
+
provider,
|
|
2562
|
+
models,
|
|
2563
|
+
}),
|
|
2564
|
+
};
|
|
2565
|
+
});
|
|
2566
|
+
registerTool("inspect_provider", {
|
|
2567
|
+
title: "Inspect provider",
|
|
2568
|
+
description: "Inspect compact provider capabilities for orchestration, including modes and draft feature settings. Use list_models for the full model list.",
|
|
2569
|
+
inputSchema: inspectProviderInputSchema,
|
|
2570
|
+
outputSchema: {
|
|
2571
|
+
provider: AgentProviderEnum,
|
|
2572
|
+
label: z.string().nullable().optional(),
|
|
2573
|
+
description: z.string().nullable().optional(),
|
|
2574
|
+
enabled: z.boolean(),
|
|
2575
|
+
status: z.string(),
|
|
2576
|
+
modes: z.array(ProviderModeSchema).nullish(),
|
|
2577
|
+
selectedModel: z.string().nullable(),
|
|
2578
|
+
features: z.array(AgentFeatureSchema),
|
|
2579
|
+
},
|
|
2580
|
+
}, async ({ provider, cwd, settings }) => {
|
|
2581
|
+
const resolvedProviderModel = resolveScheduleProviderAndModel({
|
|
2582
|
+
provider,
|
|
2583
|
+
defaultProvider: provider,
|
|
2584
|
+
});
|
|
2585
|
+
const providerId = resolvedProviderModel.provider;
|
|
2586
|
+
const resolvedCwd = resolveScopedCwd(cwd, { required: true });
|
|
2587
|
+
const entry = await providerSnapshotManager.getProvider({
|
|
2588
|
+
cwd: resolvedCwd,
|
|
2589
|
+
provider: providerId,
|
|
2590
|
+
wait: true,
|
|
2591
|
+
});
|
|
2592
|
+
const summary = toProviderSummary(entry);
|
|
2593
|
+
if (!entry.enabled) {
|
|
2594
|
+
throw new Error(`Provider '${providerId}' is disabled`);
|
|
2595
|
+
}
|
|
2596
|
+
if (entry.status !== "ready") {
|
|
2597
|
+
throw new Error(entry.error ?? `Provider '${providerId}' is unavailable`);
|
|
2598
|
+
}
|
|
2599
|
+
const selectedModel = settings?.model ?? resolvedProviderModel.model;
|
|
2600
|
+
const features = await agentManager.listDraftFeatures({
|
|
2601
|
+
provider: providerId,
|
|
2602
|
+
cwd: resolvedCwd,
|
|
2603
|
+
...(settings?.modeId ? { modeId: settings.modeId } : {}),
|
|
2604
|
+
...(selectedModel ? { model: selectedModel } : {}),
|
|
2605
|
+
...(settings?.thinkingOptionId ? { thinkingOptionId: settings.thinkingOptionId } : {}),
|
|
2606
|
+
...(settings?.features ? { featureValues: settings.features } : {}),
|
|
2607
|
+
});
|
|
2608
|
+
return {
|
|
2609
|
+
content: [],
|
|
2610
|
+
structuredContent: ensureValidJson({
|
|
2611
|
+
provider: providerId,
|
|
2612
|
+
label: summary.label,
|
|
2613
|
+
description: summary.description,
|
|
2614
|
+
enabled: summary.enabled,
|
|
2615
|
+
status: summary.status,
|
|
2616
|
+
modes: summary.modes,
|
|
2617
|
+
selectedModel: selectedModel ?? null,
|
|
2618
|
+
features,
|
|
2619
|
+
}),
|
|
2620
|
+
};
|
|
2621
|
+
});
|
|
2622
|
+
registerTool("list_worktrees", {
|
|
2623
|
+
title: "List worktrees",
|
|
2624
|
+
description: "List Otto-managed git worktrees for a repository.",
|
|
2625
|
+
inputSchema: {
|
|
2626
|
+
cwd: z
|
|
2627
|
+
.string()
|
|
2628
|
+
.optional()
|
|
2629
|
+
.describe("Optional repository cwd. Defaults to your current working directory."),
|
|
2630
|
+
},
|
|
2631
|
+
outputSchema: {
|
|
2632
|
+
worktrees: z.array(WorktreeSummarySchema),
|
|
2633
|
+
},
|
|
2634
|
+
}, async ({ cwd }) => {
|
|
2635
|
+
const resolvedCwd = resolveScopedCwd(cwd, { required: true });
|
|
2636
|
+
if (!options.workspaceGitService) {
|
|
2637
|
+
throw new Error("WorkspaceGitService is required to list worktrees");
|
|
2638
|
+
}
|
|
2639
|
+
const worktrees = await listOttoWorktreesCommand({ workspaceGitService: options.workspaceGitService }, {
|
|
2640
|
+
cwd: resolvedCwd,
|
|
2641
|
+
reason: "mcp:list-worktrees",
|
|
2642
|
+
});
|
|
2643
|
+
return {
|
|
2644
|
+
content: [],
|
|
2645
|
+
structuredContent: ensureValidJson({ worktrees }),
|
|
2646
|
+
};
|
|
2647
|
+
});
|
|
2648
|
+
registerTool("create_worktree", {
|
|
2649
|
+
title: "Create worktree",
|
|
2650
|
+
description: "Create a Otto-managed git worktree. Branch off a new branch, check out an existing branch, or check out a GitHub PR.",
|
|
2651
|
+
inputSchema: {
|
|
2652
|
+
cwd: z.string().optional().describe("Repository directory. Defaults to the agent's cwd."),
|
|
2653
|
+
target: AgentCreateWorktreeTargetInputSchema.describe("What the worktree should contain."),
|
|
2654
|
+
},
|
|
2655
|
+
outputSchema: {
|
|
2656
|
+
branchName: z.string(),
|
|
2657
|
+
worktreePath: z.string(),
|
|
2658
|
+
workspaceId: z.string(),
|
|
2659
|
+
},
|
|
2660
|
+
}, async ({ cwd, target }) => {
|
|
2661
|
+
const repoRoot = resolveScopedCwd(cwd, { required: true });
|
|
2662
|
+
const commandResult = await createOttoWorktreeCommand({
|
|
2663
|
+
ottoHome: options.ottoHome,
|
|
2664
|
+
worktreesRoot: options.worktreesRoot,
|
|
2665
|
+
createOttoWorktreeWorkflow: options.createOttoWorktree,
|
|
2666
|
+
}, createMcpWorktreeCommandInput(repoRoot, target));
|
|
2667
|
+
if (!commandResult.ok) {
|
|
2668
|
+
throw new WorktreeRequestError(commandResult.error);
|
|
2669
|
+
}
|
|
2670
|
+
const { worktree, workspace } = commandResult.createdWorktree;
|
|
2671
|
+
await options.workspaceGitService?.listWorktrees?.(repoRoot, {
|
|
2672
|
+
force: true,
|
|
2673
|
+
reason: "mcp:create-worktree",
|
|
2674
|
+
});
|
|
2675
|
+
return {
|
|
2676
|
+
content: [],
|
|
2677
|
+
structuredContent: ensureValidJson({
|
|
2678
|
+
branchName: worktree.branchName,
|
|
2679
|
+
worktreePath: worktree.worktreePath,
|
|
2680
|
+
workspaceId: workspace.workspaceId,
|
|
2681
|
+
}),
|
|
2682
|
+
};
|
|
2683
|
+
});
|
|
2684
|
+
registerTool("archive_worktree", {
|
|
2685
|
+
title: "Archive worktree",
|
|
2686
|
+
description: "Delete a Otto-managed git worktree.",
|
|
2687
|
+
inputSchema: {
|
|
2688
|
+
cwd: z
|
|
2689
|
+
.string()
|
|
2690
|
+
.optional()
|
|
2691
|
+
.describe("Optional repository cwd. Defaults to your current working directory."),
|
|
2692
|
+
worktreePath: z.string().optional(),
|
|
2693
|
+
worktreeSlug: z.string().optional(),
|
|
2694
|
+
},
|
|
2695
|
+
outputSchema: {
|
|
2696
|
+
success: z.boolean(),
|
|
2697
|
+
},
|
|
2698
|
+
}, async ({ cwd, worktreePath, worktreeSlug }) => {
|
|
2699
|
+
const resolvedCwd = resolveScopedCwd(cwd, { required: true });
|
|
2700
|
+
if (!worktreePath && !worktreeSlug) {
|
|
2701
|
+
throw new Error("worktreePath or worktreeSlug is required");
|
|
2702
|
+
}
|
|
2703
|
+
if (!options.workspaceGitService) {
|
|
2704
|
+
throw new Error("WorkspaceGitService is required to archive worktrees");
|
|
2705
|
+
}
|
|
2706
|
+
const repoRoot = await options.workspaceGitService.resolveRepoRoot(resolvedCwd);
|
|
2707
|
+
const result = await archiveCommand(archiveWorktreeDependencies(options, {
|
|
2708
|
+
agentManager,
|
|
2709
|
+
agentStorage,
|
|
2710
|
+
terminalManager: terminalManager ?? null,
|
|
2711
|
+
logger: childLogger,
|
|
2712
|
+
}), {
|
|
2713
|
+
requestId: "mcp:archive_worktree",
|
|
2714
|
+
repoRoot,
|
|
2715
|
+
worktreePath,
|
|
2716
|
+
worktreeSlug,
|
|
2717
|
+
// This tool archives every workspace on the directory, then removes the
|
|
2718
|
+
// directory. Disk removal is derived from scope + last-reference.
|
|
2719
|
+
scope: "worktree",
|
|
2720
|
+
});
|
|
2721
|
+
if (!result.ok) {
|
|
2722
|
+
throw new Error(result.message);
|
|
2723
|
+
}
|
|
2724
|
+
await options.workspaceGitService.listWorktrees(repoRoot, {
|
|
2725
|
+
force: true,
|
|
2726
|
+
reason: "mcp:archive-worktree",
|
|
2727
|
+
});
|
|
2728
|
+
return {
|
|
2729
|
+
content: [],
|
|
2730
|
+
structuredContent: ensureValidJson({ success: true }),
|
|
2731
|
+
};
|
|
2732
|
+
});
|
|
2733
|
+
registerTool("get_agent_activity", {
|
|
2734
|
+
title: "Get agent activity",
|
|
2735
|
+
description: "Return recent agent timeline entries as a curated summary.",
|
|
2736
|
+
inputSchema: {
|
|
2737
|
+
agentId: z.string(),
|
|
2738
|
+
limit: z
|
|
2739
|
+
.number()
|
|
2740
|
+
.optional()
|
|
2741
|
+
.describe("Optional limit for number of activities to include (most recent first)."),
|
|
2742
|
+
},
|
|
2743
|
+
outputSchema: {
|
|
2744
|
+
agentId: z.string(),
|
|
2745
|
+
updateCount: z.number(),
|
|
2746
|
+
currentModeId: z.string().nullable(),
|
|
2747
|
+
content: z.string(),
|
|
2748
|
+
},
|
|
2749
|
+
}, async ({ agentId, limit }) => {
|
|
2750
|
+
await ensureAgentLoaded(agentId, {
|
|
2751
|
+
agentManager,
|
|
2752
|
+
agentStorage,
|
|
2753
|
+
logger: childLogger,
|
|
2754
|
+
});
|
|
2755
|
+
const timeline = agentManager.getTimeline(agentId);
|
|
2756
|
+
const snapshot = agentManager.getAgent(agentId);
|
|
2757
|
+
const selection = selectItemsByProjectedLimit({
|
|
2758
|
+
items: timeline,
|
|
2759
|
+
direction: "tail",
|
|
2760
|
+
limit: limit ?? 0,
|
|
2761
|
+
});
|
|
2762
|
+
const curatedContent = curateAgentActivity(selection.items);
|
|
2763
|
+
const { totalProjected, shownProjected } = selection;
|
|
2764
|
+
const noun = totalProjected === 1 ? "activity" : "activities";
|
|
2765
|
+
const countHeader = limit && shownProjected < totalProjected
|
|
2766
|
+
? `Showing ${shownProjected} of ${totalProjected} ${noun} (limited to ${limit})`
|
|
2767
|
+
: `Showing all ${totalProjected} ${noun}`;
|
|
2768
|
+
const contentWithCount = `${countHeader}\n\n${curatedContent}`;
|
|
2769
|
+
return {
|
|
2770
|
+
content: [],
|
|
2771
|
+
structuredContent: ensureValidJson({
|
|
2772
|
+
agentId,
|
|
2773
|
+
updateCount: timeline.length,
|
|
2774
|
+
currentModeId: snapshot?.currentModeId ?? null,
|
|
2775
|
+
content: contentWithCount,
|
|
2776
|
+
}),
|
|
2777
|
+
};
|
|
2778
|
+
});
|
|
2779
|
+
registerTool("set_agent_mode", {
|
|
2780
|
+
title: "Set agent session mode",
|
|
2781
|
+
description: "Switch the agent's session mode (plan, bypassPermissions, read-only, auto, etc.).",
|
|
2782
|
+
inputSchema: {
|
|
2783
|
+
agentId: z.string(),
|
|
2784
|
+
modeId: z.string(),
|
|
2785
|
+
},
|
|
2786
|
+
outputSchema: {
|
|
2787
|
+
success: z.boolean(),
|
|
2788
|
+
newMode: z.string(),
|
|
2789
|
+
},
|
|
2790
|
+
}, async ({ agentId, modeId }) => {
|
|
2791
|
+
const result = await setAgentModeCommand({ agentManager }, { agentId, modeId });
|
|
2792
|
+
return {
|
|
2793
|
+
content: [],
|
|
2794
|
+
structuredContent: ensureValidJson({ success: true, newMode: result.modeId }),
|
|
2795
|
+
};
|
|
2796
|
+
});
|
|
2797
|
+
registerTool("list_pending_permissions", {
|
|
2798
|
+
title: "List pending permissions",
|
|
2799
|
+
description: "Return all pending permission requests across all agents with the normalized payloads.",
|
|
2800
|
+
inputSchema: {},
|
|
2801
|
+
outputSchema: {
|
|
2802
|
+
permissions: z.array(z.object({
|
|
2803
|
+
agentId: z.string(),
|
|
2804
|
+
status: AgentStatusEnum,
|
|
2805
|
+
request: AgentPermissionRequestPayloadSchema,
|
|
2806
|
+
})),
|
|
2807
|
+
},
|
|
2808
|
+
}, async () => {
|
|
2809
|
+
const permissions = agentManager.listAgents().flatMap((agent) => {
|
|
2810
|
+
const payload = toAgentPayload(agent);
|
|
2811
|
+
return payload.pendingPermissions.map((request) => ({
|
|
2812
|
+
agentId: agent.id,
|
|
2813
|
+
status: payload.status,
|
|
2814
|
+
request: sanitizePermissionRequest(request),
|
|
2815
|
+
}));
|
|
2816
|
+
});
|
|
2817
|
+
return {
|
|
2818
|
+
content: [],
|
|
2819
|
+
structuredContent: ensureValidJson({ permissions }),
|
|
2820
|
+
};
|
|
2821
|
+
});
|
|
2822
|
+
registerTool("respond_to_permission", {
|
|
2823
|
+
title: "Respond to permission",
|
|
2824
|
+
description: "Approve or deny a pending permission request with an AgentManager-compatible response payload.",
|
|
2825
|
+
inputSchema: {
|
|
2826
|
+
agentId: z.string(),
|
|
2827
|
+
requestId: z.string(),
|
|
2828
|
+
response: AgentPermissionResponseSchema,
|
|
2829
|
+
},
|
|
2830
|
+
outputSchema: {
|
|
2831
|
+
success: z.boolean(),
|
|
2832
|
+
},
|
|
2833
|
+
}, async ({ agentId, requestId, response }) => {
|
|
2834
|
+
await respondToAgentPermission({
|
|
2835
|
+
agentManager,
|
|
2836
|
+
agentId,
|
|
2837
|
+
requestId,
|
|
2838
|
+
response,
|
|
2839
|
+
logger: childLogger,
|
|
2840
|
+
});
|
|
2841
|
+
return {
|
|
2842
|
+
content: [],
|
|
2843
|
+
structuredContent: ensureValidJson({ success: true }),
|
|
2844
|
+
};
|
|
2845
|
+
});
|
|
2846
|
+
return toCatalog();
|
|
2847
|
+
}
|
|
2848
|
+
function archiveWorktreeDependencies(options, context) {
|
|
2849
|
+
if (!options.github) {
|
|
2850
|
+
throw new Error("GitHub service is required to archive worktrees");
|
|
2851
|
+
}
|
|
2852
|
+
if (!options.workspaceGitService) {
|
|
2853
|
+
throw new Error("WorkspaceGitService is required to archive worktrees");
|
|
2854
|
+
}
|
|
2855
|
+
if (!options.archiveWorkspaceRecord) {
|
|
2856
|
+
throw new Error("Workspace registry archiver is required to archive worktrees");
|
|
2857
|
+
}
|
|
2858
|
+
if (!options.findWorkspaceIdForCwd) {
|
|
2859
|
+
throw new Error("Workspace resolver is required to archive worktrees");
|
|
2860
|
+
}
|
|
2861
|
+
if (!options.listActiveWorkspaces) {
|
|
2862
|
+
throw new Error("Active workspace lister is required to archive worktrees");
|
|
2863
|
+
}
|
|
2864
|
+
if (!options.emitWorkspaceUpdatesForWorkspaceIds) {
|
|
2865
|
+
throw new Error("Workspace update emitter is required to archive worktrees");
|
|
2866
|
+
}
|
|
2867
|
+
if (!options.markWorkspaceArchiving) {
|
|
2868
|
+
throw new Error("Workspace archiving marker is required to archive worktrees");
|
|
2869
|
+
}
|
|
2870
|
+
if (!options.clearWorkspaceArchiving) {
|
|
2871
|
+
throw new Error("Workspace archiving clearer is required to archive worktrees");
|
|
2872
|
+
}
|
|
2873
|
+
return {
|
|
2874
|
+
ottoHome: options.ottoHome,
|
|
2875
|
+
ottoWorktreesBaseRoot: options.worktreesRoot,
|
|
2876
|
+
github: options.github,
|
|
2877
|
+
workspaceGitService: options.workspaceGitService,
|
|
2878
|
+
agentManager: context.agentManager,
|
|
2879
|
+
agentStorage: context.agentStorage,
|
|
2880
|
+
findWorkspaceIdForCwd: options.findWorkspaceIdForCwd,
|
|
2881
|
+
listActiveWorkspaces: options.listActiveWorkspaces,
|
|
2882
|
+
archiveWorkspaceRecord: options.archiveWorkspaceRecord,
|
|
2883
|
+
emitWorkspaceUpdatesForWorkspaceIds: options.emitWorkspaceUpdatesForWorkspaceIds,
|
|
2884
|
+
markWorkspaceArchiving: options.markWorkspaceArchiving,
|
|
2885
|
+
clearWorkspaceArchiving: options.clearWorkspaceArchiving,
|
|
2886
|
+
killTerminalsForWorkspace: (workspaceId) => killTerminalsForWorkspace({
|
|
2887
|
+
terminalManager: context.terminalManager,
|
|
2888
|
+
sessionLogger: context.logger,
|
|
2889
|
+
}, workspaceId),
|
|
2890
|
+
sessionLogger: context.logger,
|
|
2891
|
+
};
|
|
2892
|
+
}
|
|
2893
|
+
function createMcpWorktreeCommandInput(repoRoot, target) {
|
|
2894
|
+
const base = { cwd: repoRoot };
|
|
2895
|
+
switch (target.kind) {
|
|
2896
|
+
case "branch-off":
|
|
2897
|
+
return {
|
|
2898
|
+
...base,
|
|
2899
|
+
worktreeSlug: target.worktreeSlug,
|
|
2900
|
+
branchName: target.branchName,
|
|
2901
|
+
action: "branch-off",
|
|
2902
|
+
...(target.baseBranch ? { refName: target.baseBranch } : {}),
|
|
2903
|
+
};
|
|
2904
|
+
case "checkout-branch":
|
|
2905
|
+
return { ...base, action: "checkout", refName: target.branch };
|
|
2906
|
+
case "checkout-pr":
|
|
2907
|
+
return { ...base, action: "checkout", githubPrNumber: target.githubPrNumber };
|
|
2908
|
+
default:
|
|
2909
|
+
throw new Error("unreachable");
|
|
2910
|
+
}
|
|
2911
|
+
}
|
|
2912
|
+
//# sourceMappingURL=otto-tools.js.map
|