@loompad/cli 0.1.0 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +96 -11
- package/CHANGELOG.md +1042 -0
- package/README.md +1473 -44
- package/THIRD_PARTY_NOTICES.md +275 -0
- package/dist/adapters/antigravity-cli.d.ts +62 -0
- package/dist/adapters/antigravity-cli.js +258 -0
- package/dist/adapters/antigravity-cli.js.map +1 -0
- package/dist/adapters/base.d.ts +61 -9
- package/dist/adapters/base.js +266 -15
- package/dist/adapters/base.js.map +1 -1
- package/dist/adapters/bridges/antigravity.d.ts +9 -12
- package/dist/adapters/bridges/antigravity.js +9 -12
- package/dist/adapters/bridges/antigravity.js.map +1 -1
- package/dist/adapters/echo.d.ts +1 -0
- package/dist/adapters/echo.js +59 -3
- package/dist/adapters/echo.js.map +1 -1
- package/dist/adapters/grok.d.ts +7 -0
- package/dist/adapters/grok.js +20 -10
- package/dist/adapters/grok.js.map +1 -1
- package/dist/adapters/index.d.ts +5 -1
- package/dist/adapters/index.js +53 -7
- package/dist/adapters/index.js.map +1 -1
- package/dist/adapters/model.d.ts +137 -0
- package/dist/adapters/model.js +527 -0
- package/dist/adapters/model.js.map +1 -0
- package/dist/adapters/opencode.d.ts +101 -1
- package/dist/adapters/opencode.js +874 -98
- package/dist/adapters/opencode.js.map +1 -1
- package/dist/cli/doctor.d.ts +32 -0
- package/dist/cli/doctor.js +89 -4
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/index.js +2651 -87
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/tui-model.js +3 -1
- package/dist/cli/tui-model.js.map +1 -1
- package/dist/cli/tui.js +44 -1
- package/dist/cli/tui.js.map +1 -1
- package/dist/cli/ui.d.ts +2 -0
- package/dist/cli/ui.js +13 -0
- package/dist/cli/ui.js.map +1 -1
- package/dist/core/ades.d.ts +32 -16
- package/dist/core/ades.js +57 -20
- package/dist/core/ades.js.map +1 -1
- package/dist/core/agent-state.d.ts +14 -0
- package/dist/core/agent-state.js +32 -0
- package/dist/core/agent-state.js.map +1 -0
- package/dist/core/approvals.d.ts +33 -0
- package/dist/core/approvals.js +29 -0
- package/dist/core/approvals.js.map +1 -0
- package/dist/core/backup.d.ts +19 -0
- package/dist/core/backup.js +86 -0
- package/dist/core/backup.js.map +1 -0
- package/dist/core/baton.d.ts +2 -2
- package/dist/core/baton.js.map +1 -1
- package/dist/core/brain-extract.d.ts +17 -0
- package/dist/core/brain-extract.js +28 -2
- package/dist/core/brain-extract.js.map +1 -1
- package/dist/core/brain-index.d.ts +71 -0
- package/dist/core/brain-index.js +204 -5
- package/dist/core/brain-index.js.map +1 -1
- package/dist/core/brain.d.ts +58 -2
- package/dist/core/brain.js +79 -1
- package/dist/core/brain.js.map +1 -1
- package/dist/core/chat-import.d.ts +71 -0
- package/dist/core/chat-import.js +419 -0
- package/dist/core/chat-import.js.map +1 -0
- package/dist/core/checkpoint.d.ts +107 -0
- package/dist/core/checkpoint.js +259 -0
- package/dist/core/checkpoint.js.map +1 -0
- package/dist/core/claude-cli.js +1 -1
- package/dist/core/claude-cli.js.map +1 -1
- package/dist/core/continuity/artifacts.d.ts +14 -0
- package/dist/core/continuity/artifacts.js +118 -0
- package/dist/core/continuity/artifacts.js.map +1 -0
- package/dist/core/continuity/capabilities.d.ts +65 -0
- package/dist/core/continuity/capabilities.js +129 -0
- package/dist/core/continuity/capabilities.js.map +1 -0
- package/dist/core/continuity/contracts.d.ts +352 -0
- package/dist/core/continuity/contracts.js +117 -0
- package/dist/core/continuity/contracts.js.map +1 -0
- package/dist/core/continuity/engine.d.ts +55 -0
- package/dist/core/continuity/engine.js +504 -0
- package/dist/core/continuity/engine.js.map +1 -0
- package/dist/core/continuity/store.d.ts +73 -0
- package/dist/core/continuity/store.js +438 -0
- package/dist/core/continuity/store.js.map +1 -0
- package/dist/core/conversations.d.ts +19 -0
- package/dist/core/conversations.js +87 -0
- package/dist/core/conversations.js.map +1 -0
- package/dist/core/crew-coordination.d.ts +6 -0
- package/dist/core/crew-coordination.js +24 -0
- package/dist/core/crew-coordination.js.map +1 -0
- package/dist/core/crew-policy.d.ts +18 -0
- package/dist/core/crew-policy.js +40 -0
- package/dist/core/crew-policy.js.map +1 -0
- package/dist/core/crew-protocol.d.ts +64 -0
- package/dist/core/crew-protocol.js +146 -0
- package/dist/core/crew-protocol.js.map +1 -0
- package/dist/core/crew-requests.d.ts +14 -0
- package/dist/core/crew-requests.js +22 -0
- package/dist/core/crew-requests.js.map +1 -0
- package/dist/core/crew.d.ts +395 -0
- package/dist/core/crew.js +1857 -0
- package/dist/core/crew.js.map +1 -0
- package/dist/core/digest.d.ts +40 -0
- package/dist/core/digest.js +94 -0
- package/dist/core/digest.js.map +1 -0
- package/dist/core/distill.js +3 -5
- package/dist/core/distill.js.map +1 -1
- package/dist/core/eventlog.d.ts +27 -25
- package/dist/core/eventlog.js +97 -150
- package/dist/core/eventlog.js.map +1 -1
- package/dist/core/events/contracts.d.ts +51 -0
- package/dist/core/events/contracts.js +2 -0
- package/dist/core/events/contracts.js.map +1 -0
- package/dist/core/events/jsonl-store.d.ts +23 -0
- package/dist/core/events/jsonl-store.js +90 -0
- package/dist/core/events/jsonl-store.js.map +1 -0
- package/dist/core/events/sqlite-store.d.ts +37 -0
- package/dist/core/events/sqlite-store.js +215 -0
- package/dist/core/events/sqlite-store.js.map +1 -0
- package/dist/core/forecast.d.ts +44 -0
- package/dist/core/forecast.js +46 -0
- package/dist/core/forecast.js.map +1 -0
- package/dist/core/free-pool.d.ts +65 -0
- package/dist/core/free-pool.js +171 -0
- package/dist/core/free-pool.js.map +1 -0
- package/dist/core/git.d.ts +30 -0
- package/dist/core/git.js +50 -2
- package/dist/core/git.js.map +1 -1
- package/dist/core/github-events.d.ts +53 -0
- package/dist/core/github-events.js +227 -0
- package/dist/core/github-events.js.map +1 -0
- package/dist/core/goal-lanes.d.ts +53 -0
- package/dist/core/goal-lanes.js +99 -0
- package/dist/core/goal-lanes.js.map +1 -0
- package/dist/core/hosted.d.ts +27 -0
- package/dist/core/hosted.js +50 -0
- package/dist/core/hosted.js.map +1 -0
- package/dist/core/http-server.d.ts +5 -0
- package/dist/core/http-server.js +8 -0
- package/dist/core/http-server.js.map +1 -0
- package/dist/core/invite-link.d.ts +31 -0
- package/dist/core/invite-link.js +45 -0
- package/dist/core/invite-link.js.map +1 -0
- package/dist/core/loops.d.ts +37 -0
- package/dist/core/loops.js +81 -0
- package/dist/core/loops.js.map +1 -0
- package/dist/core/mcp-catalog.d.ts +182 -0
- package/dist/core/mcp-catalog.js +431 -0
- package/dist/core/mcp-catalog.js.map +1 -0
- package/dist/core/mcp.d.ts +87 -0
- package/dist/core/mcp.js +181 -0
- package/dist/core/mcp.js.map +1 -0
- package/dist/core/memory.d.ts +1 -0
- package/dist/core/memory.js +5 -2
- package/dist/core/memory.js.map +1 -1
- package/dist/core/model-tools.d.ts +97 -0
- package/dist/core/model-tools.js +388 -0
- package/dist/core/model-tools.js.map +1 -0
- package/dist/core/notify.js +19 -1
- package/dist/core/notify.js.map +1 -1
- package/dist/core/orchestra.d.ts +635 -0
- package/dist/core/orchestra.js +2120 -0
- package/dist/core/orchestra.js.map +1 -0
- package/dist/core/permissions.d.ts +56 -0
- package/dist/core/permissions.js +122 -0
- package/dist/core/permissions.js.map +1 -0
- package/dist/core/preview-proxy.d.ts +70 -0
- package/dist/core/preview-proxy.js +563 -0
- package/dist/core/preview-proxy.js.map +1 -0
- package/dist/core/preview-shot.d.ts +46 -0
- package/dist/core/preview-shot.js +109 -0
- package/dist/core/preview-shot.js.map +1 -0
- package/dist/core/process.d.ts +36 -0
- package/dist/core/process.js +168 -0
- package/dist/core/process.js.map +1 -0
- package/dist/core/prompt-queue.d.ts +136 -0
- package/dist/core/prompt-queue.js +269 -0
- package/dist/core/prompt-queue.js.map +1 -0
- package/dist/core/prompts.d.ts +53 -0
- package/dist/core/prompts.js +127 -0
- package/dist/core/prompts.js.map +1 -0
- package/dist/core/proof.d.ts +82 -0
- package/dist/core/proof.js +216 -0
- package/dist/core/proof.js.map +1 -0
- package/dist/core/provider-text.d.ts +23 -0
- package/dist/core/provider-text.js +66 -0
- package/dist/core/provider-text.js.map +1 -0
- package/dist/core/providers.d.ts +152 -0
- package/dist/core/providers.js +343 -0
- package/dist/core/providers.js.map +1 -0
- package/dist/core/quota.d.ts +33 -0
- package/dist/core/quota.js +74 -0
- package/dist/core/quota.js.map +1 -0
- package/dist/core/recipes.d.ts +50 -0
- package/dist/core/recipes.js +99 -0
- package/dist/core/recipes.js.map +1 -0
- package/dist/core/registry.d.ts +54 -0
- package/dist/core/registry.js +62 -5
- package/dist/core/registry.js.map +1 -1
- package/dist/core/relay-client.d.ts +67 -0
- package/dist/core/relay-client.js +184 -0
- package/dist/core/relay-client.js.map +1 -0
- package/dist/core/relay-protocol.d.ts +140 -0
- package/dist/core/relay-protocol.js +189 -0
- package/dist/core/relay-protocol.js.map +1 -0
- package/dist/core/replay.d.ts +18 -0
- package/dist/core/replay.js +191 -0
- package/dist/core/replay.js.map +1 -0
- package/dist/core/review-triage.d.ts +41 -0
- package/dist/core/review-triage.js +124 -0
- package/dist/core/review-triage.js.map +1 -0
- package/dist/core/router.js +2 -2
- package/dist/core/router.js.map +1 -1
- package/dist/core/routes.d.ts +39 -3
- package/dist/core/routes.js +137 -4
- package/dist/core/routes.js.map +1 -1
- package/dist/core/run-command.d.ts +14 -0
- package/dist/core/run-command.js +45 -0
- package/dist/core/run-command.js.map +1 -0
- package/dist/core/runner-setup.d.ts +36 -0
- package/dist/core/runner-setup.js +114 -0
- package/dist/core/runner-setup.js.map +1 -0
- package/dist/core/search.js +0 -0
- package/dist/core/search.js.map +1 -1
- package/dist/core/semantic.d.ts +92 -0
- package/dist/core/semantic.js +227 -0
- package/dist/core/semantic.js.map +1 -0
- package/dist/core/servers.d.ts +115 -0
- package/dist/core/servers.js +305 -0
- package/dist/core/servers.js.map +1 -0
- package/dist/core/setup.js +64 -6
- package/dist/core/setup.js.map +1 -1
- package/dist/core/shield.d.ts +17 -0
- package/dist/core/shield.js +81 -0
- package/dist/core/shield.js.map +1 -0
- package/dist/core/shift.d.ts +69 -0
- package/dist/core/shift.js +120 -0
- package/dist/core/shift.js.map +1 -0
- package/dist/core/skill-install.d.ts +125 -0
- package/dist/core/skill-install.js +328 -0
- package/dist/core/skill-install.js.map +1 -0
- package/dist/core/skills.d.ts +116 -0
- package/dist/core/skills.js +223 -0
- package/dist/core/skills.js.map +1 -0
- package/dist/core/step-conditions.d.ts +65 -0
- package/dist/core/step-conditions.js +102 -0
- package/dist/core/step-conditions.js.map +1 -0
- package/dist/core/subagents.d.ts +45 -0
- package/dist/core/subagents.js +90 -0
- package/dist/core/subagents.js.map +1 -0
- package/dist/core/team-canon.d.ts +42 -0
- package/dist/core/team-canon.js +121 -0
- package/dist/core/team-canon.js.map +1 -0
- package/dist/core/team-crypto.d.ts +97 -0
- package/dist/core/team-crypto.js +175 -0
- package/dist/core/team-crypto.js.map +1 -0
- package/dist/core/team-hub.d.ts +514 -0
- package/dist/core/team-hub.js +964 -0
- package/dist/core/team-hub.js.map +1 -0
- package/dist/core/team-landing.d.ts +239 -0
- package/dist/core/team-landing.js +504 -0
- package/dist/core/team-landing.js.map +1 -0
- package/dist/core/team-leases.d.ts +44 -0
- package/dist/core/team-leases.js +145 -0
- package/dist/core/team-leases.js.map +1 -0
- package/dist/core/team-memory.d.ts +42 -0
- package/dist/core/team-memory.js +84 -0
- package/dist/core/team-memory.js.map +1 -0
- package/dist/core/team-policy.d.ts +85 -0
- package/dist/core/team-policy.js +191 -0
- package/dist/core/team-policy.js.map +1 -0
- package/dist/core/thread-md.d.ts +6 -0
- package/dist/core/thread-md.js +42 -0
- package/dist/core/thread-md.js.map +1 -0
- package/dist/core/turn-stats.d.ts +61 -0
- package/dist/core/turn-stats.js +124 -0
- package/dist/core/turn-stats.js.map +1 -0
- package/dist/core/updater.d.ts +73 -0
- package/dist/core/updater.js +153 -0
- package/dist/core/updater.js.map +1 -0
- package/dist/core/usage.d.ts +75 -0
- package/dist/core/usage.js +163 -0
- package/dist/core/usage.js.map +1 -0
- package/dist/core/windows-jobs.d.ts +15 -0
- package/dist/core/windows-jobs.js +170 -0
- package/dist/core/windows-jobs.js.map +1 -0
- package/dist/core/worktree-merge.d.ts +57 -0
- package/dist/core/worktree-merge.js +123 -0
- package/dist/core/worktree-merge.js.map +1 -0
- package/dist/core/worktree.d.ts +5 -0
- package/dist/core/worktree.js +45 -5
- package/dist/core/worktree.js.map +1 -1
- package/dist/core/worktrees.d.ts +6 -0
- package/dist/core/worktrees.js +21 -0
- package/dist/core/worktrees.js.map +1 -0
- package/dist/daemon/app-page.d.ts +0 -17
- package/dist/daemon/app-page.js +10 -6346
- package/dist/daemon/app-page.js.map +1 -1
- package/dist/daemon/auth.d.ts +24 -3
- package/dist/daemon/auth.js +66 -7
- package/dist/daemon/auth.js.map +1 -1
- package/dist/daemon/board.d.ts +7 -0
- package/dist/daemon/board.js +12 -1
- package/dist/daemon/board.js.map +1 -1
- package/dist/daemon/brand-icons.d.ts +6 -1
- package/dist/daemon/brand-icons.js +11 -3
- package/dist/daemon/brand-icons.js.map +1 -1
- package/dist/daemon/client.d.ts +665 -1
- package/dist/daemon/client.js +381 -3
- package/dist/daemon/client.js.map +1 -1
- package/dist/daemon/delivery.d.ts +34 -0
- package/dist/daemon/delivery.js +63 -0
- package/dist/daemon/delivery.js.map +1 -0
- package/dist/daemon/deploys.d.ts +62 -0
- package/dist/daemon/deploys.js +159 -0
- package/dist/daemon/deploys.js.map +1 -0
- package/dist/daemon/landing.d.ts +192 -0
- package/dist/daemon/landing.js +992 -0
- package/dist/daemon/landing.js.map +1 -0
- package/dist/daemon/onboard.d.ts +150 -0
- package/dist/daemon/onboard.js +390 -0
- package/dist/daemon/onboard.js.map +1 -0
- package/dist/daemon/proof.d.ts +70 -0
- package/dist/daemon/proof.js +172 -0
- package/dist/daemon/proof.js.map +1 -0
- package/dist/daemon/push.d.ts +19 -1
- package/dist/daemon/push.js +90 -3
- package/dist/daemon/push.js.map +1 -1
- package/dist/daemon/relay.d.ts +82 -0
- package/dist/daemon/relay.js +351 -0
- package/dist/daemon/relay.js.map +1 -0
- package/dist/daemon/routes/agent-discovery.d.ts +4 -0
- package/dist/daemon/routes/agent-discovery.js +29 -0
- package/dist/daemon/routes/agent-discovery.js.map +1 -0
- package/dist/daemon/routes/agent-settings.d.ts +4 -0
- package/dist/daemon/routes/agent-settings.js +94 -0
- package/dist/daemon/routes/agent-settings.js.map +1 -0
- package/dist/daemon/routes/agents.d.ts +4 -0
- package/dist/daemon/routes/agents.js +105 -0
- package/dist/daemon/routes/agents.js.map +1 -0
- package/dist/daemon/routes/alerts.d.ts +4 -0
- package/dist/daemon/routes/alerts.js +137 -0
- package/dist/daemon/routes/alerts.js.map +1 -0
- package/dist/daemon/routes/approvals.d.ts +4 -0
- package/dist/daemon/routes/approvals.js +40 -0
- package/dist/daemon/routes/approvals.js.map +1 -0
- package/dist/daemon/routes/assets.d.ts +3 -0
- package/dist/daemon/routes/assets.js +64 -0
- package/dist/daemon/routes/assets.js.map +1 -0
- package/dist/daemon/routes/board.d.ts +4 -0
- package/dist/daemon/routes/board.js +147 -0
- package/dist/daemon/routes/board.js.map +1 -0
- package/dist/daemon/routes/brain.d.ts +4 -0
- package/dist/daemon/routes/brain.js +270 -0
- package/dist/daemon/routes/brain.js.map +1 -0
- package/dist/daemon/routes/chats.d.ts +4 -0
- package/dist/daemon/routes/chats.js +249 -0
- package/dist/daemon/routes/chats.js.map +1 -0
- package/dist/daemon/routes/clients.d.ts +4 -0
- package/dist/daemon/routes/clients.js +49 -0
- package/dist/daemon/routes/clients.js.map +1 -0
- package/dist/daemon/routes/cloud.d.ts +4 -0
- package/dist/daemon/routes/cloud.js +46 -0
- package/dist/daemon/routes/cloud.js.map +1 -0
- package/dist/daemon/routes/config.d.ts +4 -0
- package/dist/daemon/routes/config.js +35 -0
- package/dist/daemon/routes/config.js.map +1 -0
- package/dist/daemon/routes/context.d.ts +51 -0
- package/dist/daemon/routes/context.js +2 -0
- package/dist/daemon/routes/context.js.map +1 -0
- package/dist/daemon/routes/crews.d.ts +4 -0
- package/dist/daemon/routes/crews.js +149 -0
- package/dist/daemon/routes/crews.js.map +1 -0
- package/dist/daemon/routes/files.d.ts +3 -0
- package/dist/daemon/routes/files.js +253 -0
- package/dist/daemon/routes/files.js.map +1 -0
- package/dist/daemon/routes/git.d.ts +4 -0
- package/dist/daemon/routes/git.js +96 -0
- package/dist/daemon/routes/git.js.map +1 -0
- package/dist/daemon/routes/history.d.ts +4 -0
- package/dist/daemon/routes/history.js +101 -0
- package/dist/daemon/routes/history.js.map +1 -0
- package/dist/daemon/routes/integrations-project.d.ts +3 -0
- package/dist/daemon/routes/integrations-project.js +103 -0
- package/dist/daemon/routes/integrations-project.js.map +1 -0
- package/dist/daemon/routes/integrations.d.ts +3 -0
- package/dist/daemon/routes/integrations.js +111 -0
- package/dist/daemon/routes/integrations.js.map +1 -0
- package/dist/daemon/routes/logs.d.ts +3 -0
- package/dist/daemon/routes/logs.js +40 -0
- package/dist/daemon/routes/logs.js.map +1 -0
- package/dist/daemon/routes/messages.d.ts +4 -0
- package/dist/daemon/routes/messages.js +60 -0
- package/dist/daemon/routes/messages.js.map +1 -0
- package/dist/daemon/routes/observability.d.ts +4 -0
- package/dist/daemon/routes/observability.js +247 -0
- package/dist/daemon/routes/observability.js.map +1 -0
- package/dist/daemon/routes/onboard.d.ts +12 -0
- package/dist/daemon/routes/onboard.js +67 -0
- package/dist/daemon/routes/onboard.js.map +1 -0
- package/dist/daemon/routes/orchestra.d.ts +4 -0
- package/dist/daemon/routes/orchestra.js +110 -0
- package/dist/daemon/routes/orchestra.js.map +1 -0
- package/dist/daemon/routes/pairing.d.ts +4 -0
- package/dist/daemon/routes/pairing.js +155 -0
- package/dist/daemon/routes/pairing.js.map +1 -0
- package/dist/daemon/routes/preview.d.ts +24 -0
- package/dist/daemon/routes/preview.js +122 -0
- package/dist/daemon/routes/preview.js.map +1 -0
- package/dist/daemon/routes/project-actions.d.ts +4 -0
- package/dist/daemon/routes/project-actions.js +53 -0
- package/dist/daemon/routes/project-actions.js.map +1 -0
- package/dist/daemon/routes/project-team.d.ts +4 -0
- package/dist/daemon/routes/project-team.js +201 -0
- package/dist/daemon/routes/project-team.js.map +1 -0
- package/dist/daemon/routes/projects.d.ts +14 -0
- package/dist/daemon/routes/projects.js +138 -0
- package/dist/daemon/routes/projects.js.map +1 -0
- package/dist/daemon/routes/prompts.d.ts +4 -0
- package/dist/daemon/routes/prompts.js +44 -0
- package/dist/daemon/routes/prompts.js.map +1 -0
- package/dist/daemon/routes/proof.d.ts +4 -0
- package/dist/daemon/routes/proof.js +22 -0
- package/dist/daemon/routes/proof.js.map +1 -0
- package/dist/daemon/routes/providers.d.ts +3 -0
- package/dist/daemon/routes/providers.js +60 -0
- package/dist/daemon/routes/providers.js.map +1 -0
- package/dist/daemon/routes/public.d.ts +4 -0
- package/dist/daemon/routes/public.js +64 -0
- package/dist/daemon/routes/public.js.map +1 -0
- package/dist/daemon/routes/queue.d.ts +4 -0
- package/dist/daemon/routes/queue.js +140 -0
- package/dist/daemon/routes/queue.js.map +1 -0
- package/dist/daemon/routes/routing.d.ts +4 -0
- package/dist/daemon/routes/routing.js +61 -0
- package/dist/daemon/routes/routing.js.map +1 -0
- package/dist/daemon/routes/runtime-handler.d.ts +2 -0
- package/dist/daemon/routes/runtime-handler.js +63 -0
- package/dist/daemon/routes/runtime-handler.js.map +1 -0
- package/dist/daemon/routes/search.d.ts +4 -0
- package/dist/daemon/routes/search.js +17 -0
- package/dist/daemon/routes/search.js.map +1 -0
- package/dist/daemon/routes/servers.d.ts +4 -0
- package/dist/daemon/routes/servers.js +133 -0
- package/dist/daemon/routes/servers.js.map +1 -0
- package/dist/daemon/routes/setup.d.ts +3 -0
- package/dist/daemon/routes/setup.js +50 -0
- package/dist/daemon/routes/setup.js.map +1 -0
- package/dist/daemon/routes/shift.d.ts +4 -0
- package/dist/daemon/routes/shift.js +30 -0
- package/dist/daemon/routes/shift.js.map +1 -0
- package/dist/daemon/routes/specs.d.ts +4 -0
- package/dist/daemon/routes/specs.js +57 -0
- package/dist/daemon/routes/specs.js.map +1 -0
- package/dist/daemon/routes/task-delivery.d.ts +4 -0
- package/dist/daemon/routes/task-delivery.js +38 -0
- package/dist/daemon/routes/task-delivery.js.map +1 -0
- package/dist/daemon/routes/team.d.ts +4 -0
- package/dist/daemon/routes/team.js +193 -0
- package/dist/daemon/routes/team.js.map +1 -0
- package/dist/daemon/routes/terminals.d.ts +4 -0
- package/dist/daemon/routes/terminals.js +87 -0
- package/dist/daemon/routes/terminals.js.map +1 -0
- package/dist/daemon/routes/tools.d.ts +4 -0
- package/dist/daemon/routes/tools.js +180 -0
- package/dist/daemon/routes/tools.js.map +1 -0
- package/dist/daemon/routes/updates.d.ts +4 -0
- package/dist/daemon/routes/updates.js +89 -0
- package/dist/daemon/routes/updates.js.map +1 -0
- package/dist/daemon/routes/voice.d.ts +4 -0
- package/dist/daemon/routes/voice.js +53 -0
- package/dist/daemon/routes/voice.js.map +1 -0
- package/dist/daemon/runner.d.ts +186 -0
- package/dist/daemon/runner.js +612 -0
- package/dist/daemon/runner.js.map +1 -0
- package/dist/daemon/runtime/accounting.d.ts +130 -0
- package/dist/daemon/runtime/accounting.js +303 -0
- package/dist/daemon/runtime/accounting.js.map +1 -0
- package/dist/daemon/runtime/agents.d.ts +16 -0
- package/dist/daemon/runtime/agents.js +71 -0
- package/dist/daemon/runtime/agents.js.map +1 -0
- package/dist/daemon/runtime/briefings.d.ts +134 -0
- package/dist/daemon/runtime/briefings.js +329 -0
- package/dist/daemon/runtime/briefings.js.map +1 -0
- package/dist/daemon/runtime/native-usage.d.ts +21 -0
- package/dist/daemon/runtime/native-usage.js +71 -0
- package/dist/daemon/runtime/native-usage.js.map +1 -0
- package/dist/daemon/runtime/queue.d.ts +94 -0
- package/dist/daemon/runtime/queue.js +222 -0
- package/dist/daemon/runtime/queue.js.map +1 -0
- package/dist/daemon/runtime/relay.d.ts +93 -0
- package/dist/daemon/runtime/relay.js +240 -0
- package/dist/daemon/runtime/relay.js.map +1 -0
- package/dist/daemon/runtime/turns.d.ts +202 -0
- package/dist/daemon/runtime/turns.js +663 -0
- package/dist/daemon/runtime/turns.js.map +1 -0
- package/dist/daemon/runtime-support.d.ts +76 -0
- package/dist/daemon/runtime-support.js +134 -0
- package/dist/daemon/runtime-support.js.map +1 -0
- package/dist/daemon/runtime.d.ts +739 -70
- package/dist/daemon/runtime.js +2751 -470
- package/dist/daemon/runtime.js.map +1 -1
- package/dist/daemon/server.d.ts +82 -55
- package/dist/daemon/server.js +674 -1435
- package/dist/daemon/server.js.map +1 -1
- package/dist/daemon/shift.d.ts +70 -0
- package/dist/daemon/shift.js +170 -0
- package/dist/daemon/shift.js.map +1 -0
- package/dist/daemon/specs.d.ts +92 -0
- package/dist/daemon/specs.js +285 -0
- package/dist/daemon/specs.js.map +1 -0
- package/dist/daemon/system.d.ts +208 -0
- package/dist/daemon/system.js +557 -0
- package/dist/daemon/system.js.map +1 -0
- package/dist/daemon/tasks.js +1 -1
- package/dist/daemon/team-brain.d.ts +136 -0
- package/dist/daemon/team-brain.js +659 -0
- package/dist/daemon/team-brain.js.map +1 -0
- package/dist/daemon/team-coordinator.d.ts +78 -0
- package/dist/daemon/team-coordinator.js +408 -0
- package/dist/daemon/team-coordinator.js.map +1 -0
- package/dist/daemon/team.d.ts +339 -0
- package/dist/daemon/team.js +1433 -0
- package/dist/daemon/team.js.map +1 -0
- package/dist/daemon/terminals.d.ts +22 -3
- package/dist/daemon/terminals.js +236 -16
- package/dist/daemon/terminals.js.map +1 -1
- package/dist/hub/client.d.ts +120 -0
- package/dist/hub/client.js +228 -0
- package/dist/hub/client.js.map +1 -0
- package/dist/hub/server.d.ts +32 -0
- package/dist/hub/server.js +180 -0
- package/dist/hub/server.js.map +1 -0
- package/dist/hub/supabase-client.d.ts +220 -0
- package/dist/hub/supabase-client.js +843 -0
- package/dist/hub/supabase-client.js.map +1 -0
- package/dist/observability/ask.d.ts +91 -0
- package/dist/observability/ask.js +179 -0
- package/dist/observability/ask.js.map +1 -0
- package/dist/observability/clickhouse.d.ts +33 -0
- package/dist/observability/clickhouse.js +48 -0
- package/dist/observability/clickhouse.js.map +1 -0
- package/dist/observability/decisions.d.ts +176 -0
- package/dist/observability/decisions.js +377 -0
- package/dist/observability/decisions.js.map +1 -0
- package/dist/observability/index.d.ts +89 -0
- package/dist/observability/index.js +348 -0
- package/dist/observability/index.js.map +1 -0
- package/dist/observability/insights.d.ts +151 -0
- package/dist/observability/insights.js +319 -0
- package/dist/observability/insights.js.map +1 -0
- package/dist/observability/logs-query.d.ts +82 -0
- package/dist/observability/logs-query.js +102 -0
- package/dist/observability/logs-query.js.map +1 -0
- package/dist/observability/logs.d.ts +72 -0
- package/dist/observability/logs.js +295 -0
- package/dist/observability/logs.js.map +1 -0
- package/dist/observability/metrics.d.ts +86 -0
- package/dist/observability/metrics.js +303 -0
- package/dist/observability/metrics.js.map +1 -0
- package/dist/observability/otlp.d.ts +127 -0
- package/dist/observability/otlp.js +242 -0
- package/dist/observability/otlp.js.map +1 -0
- package/dist/observability/snapshots.d.ts +43 -0
- package/dist/observability/snapshots.js +129 -0
- package/dist/observability/snapshots.js.map +1 -0
- package/dist/observability/triage.d.ts +49 -0
- package/dist/observability/triage.js +284 -0
- package/dist/observability/triage.js.map +1 -0
- package/dist/providers/adapter.d.ts +50 -0
- package/dist/providers/adapter.js +38 -0
- package/dist/providers/adapter.js.map +1 -0
- package/dist/providers/agent.d.ts +110 -0
- package/dist/providers/agent.js +487 -0
- package/dist/providers/agent.js.map +1 -0
- package/dist/providers/approvals.d.ts +24 -0
- package/dist/providers/approvals.js +78 -0
- package/dist/providers/approvals.js.map +1 -0
- package/dist/providers/attachments.d.ts +20 -0
- package/dist/providers/attachments.js +51 -0
- package/dist/providers/attachments.js.map +1 -0
- package/dist/providers/claude/adapter.d.ts +99 -0
- package/dist/providers/claude/adapter.js +790 -0
- package/dist/providers/claude/adapter.js.map +1 -0
- package/dist/providers/codex/adapter.d.ts +113 -0
- package/dist/providers/codex/adapter.js +881 -0
- package/dist/providers/codex/adapter.js.map +1 -0
- package/dist/providers/codex/instructions.d.ts +11 -0
- package/dist/providers/codex/instructions.js +154 -0
- package/dist/providers/codex/instructions.js.map +1 -0
- package/dist/providers/codex/rpc.d.ts +36 -0
- package/dist/providers/codex/rpc.js +104 -0
- package/dist/providers/codex/rpc.js.map +1 -0
- package/dist/providers/contracts.d.ts +339 -0
- package/dist/providers/contracts.js +19 -0
- package/dist/providers/contracts.js.map +1 -0
- package/dist/providers/directory.d.ts +70 -0
- package/dist/providers/directory.js +95 -0
- package/dist/providers/directory.js.map +1 -0
- package/dist/providers/errors.d.ts +47 -0
- package/dist/providers/errors.js +24 -0
- package/dist/providers/errors.js.map +1 -0
- package/dist/providers/index.d.ts +18 -0
- package/dist/providers/index.js +19 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/ingestion.d.ts +80 -0
- package/dist/providers/ingestion.js +330 -0
- package/dist/providers/ingestion.js.map +1 -0
- package/dist/providers/live.d.ts +22 -0
- package/dist/providers/live.js +56 -0
- package/dist/providers/live.js.map +1 -0
- package/dist/providers/process.d.ts +29 -0
- package/dist/providers/process.js +71 -0
- package/dist/providers/process.js.map +1 -0
- package/dist/providers/reaper.d.ts +33 -0
- package/dist/providers/reaper.js +65 -0
- package/dist/providers/reaper.js.map +1 -0
- package/dist/providers/service.d.ts +87 -0
- package/dist/providers/service.js +256 -0
- package/dist/providers/service.js.map +1 -0
- package/dist/sdk.d.ts +1 -1
- package/dist/sdk.js +1 -1
- package/dist/sdk.js.map +1 -1
- package/dist/types.d.ts +385 -6
- package/dist/types.js +1 -0
- package/dist/types.js.map +1 -1
- package/dist/version.d.ts +7 -0
- package/dist/version.js +16 -0
- package/dist/version.js.map +1 -0
- package/dist/web/app.css +4643 -0
- package/dist/web/app.js +22640 -0
- package/dist/web/app.js.map +7 -0
- package/dist/web/shell.html +39 -0
- package/docs/README.md +21 -0
- package/docs/SETUP.md +10 -0
- package/docs/adapters.md +21 -8
- package/docs/brain-continuity.md +186 -0
- package/docs/cloud.md +108 -0
- package/docs/crews.md +62 -0
- package/docs/design-system.md +31 -2
- package/docs/integration-notes.md +128 -20
- package/docs/loompad.md +105 -0
- package/docs/observability.md +220 -0
- package/docs/play-store.md +123 -0
- package/docs/proposals/agent-teams.md +249 -0
- package/docs/proposals/brain-continuity-implementation.md +468 -0
- package/docs/proposals/brain-session-continuity.md +623 -0
- package/docs/proposals/t3code-port.md +81 -0
- package/docs/proposals/t3code-reference.md +95 -0
- package/docs/refactoring/AGENT-TEAMS-PROGRESS.md +88 -0
- package/docs/refactoring/BOUNDARIES.md +120 -0
- package/docs/refactoring/BRAIN-AUDIT.md +176 -0
- package/docs/refactoring/BRAIN-NOTES.md +390 -0
- package/docs/refactoring/BRAIN-TODO.md +214 -0
- package/docs/refactoring/NOTES.md +129 -0
- package/docs/refactoring/T3-PORT-NOTES.md +165 -0
- package/docs/refactoring/T3-PORT-TODO.md +123 -0
- package/docs/refactoring/TODO.md +46 -0
- package/docs/refactoring/brain-benchmark-2026-09-28.json +48 -0
- package/docs/refactoring/brain-benchmark-audit-2026-09-28.json +48 -0
- package/docs/release-notes/v0.2.0.md +35 -0
- package/docs/release-notes/v0.2.1.md +59 -0
- package/docs/release-notes/v0.2.3.md +77 -0
- package/docs/release-notes/v0.2.4.md +128 -0
- package/docs/release-notes/v0.2.5.md +153 -0
- package/docs/release-notes/v0.2.6.md +62 -0
- package/docs/release-notes/v1.0.0.md +120 -0
- package/docs/release-notes/v1.0.1.md +77 -0
- package/docs/release-notes/v1.1.0.md +71 -0
- package/docs/release-notes/v1.1.1.md +38 -0
- package/docs/teams-architecture.md +697 -0
- package/docs/teams.md +171 -0
- package/docs/windows.md +85 -0
- package/package.json +22 -9
- package/dist/adapters/claude-code.d.ts +0 -26
- package/dist/adapters/claude-code.js +0 -209
- package/dist/adapters/claude-code.js.map +0 -1
- package/dist/adapters/codex.d.ts +0 -51
- package/dist/adapters/codex.js +0 -291
- package/dist/adapters/codex.js.map +0 -1
- package/docs/img/board-linear.png +0 -0
- package/docs/img/board-projects.png +0 -0
- package/docs/img/brain.png +0 -0
- package/docs/img/settings.png +0 -0
- package/docs/img/workspace.png +0 -0
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where a model agent's turns actually go, and where its key lives.
|
|
3
|
+
*
|
|
4
|
+
* Loom's agents have always been CLIs, which bring their own auth. A model
|
|
5
|
+
* agent (adapters/model.ts) doesn't: it needs a base URL, a key, and whatever
|
|
6
|
+
* headers that particular service insists on. This is the one place that
|
|
7
|
+
* knows those things.
|
|
8
|
+
*
|
|
9
|
+
* ## Keys are not project config
|
|
10
|
+
*
|
|
11
|
+
* `.loom/config.json` is committed in most projects. A key in it is a key in
|
|
12
|
+
* the repository, and then in the fork, and then in the search index. So the
|
|
13
|
+
* project config names a PROVIDER — `"provider": "openrouter"` — and the key
|
|
14
|
+
* lives either in the environment or in `~/.loom/providers.json`, mode 0600,
|
|
15
|
+
* beside the daemon token that already lives there under the same rule.
|
|
16
|
+
*
|
|
17
|
+
* Nothing here returns a key to an HTTP client, logs one, or prints one. What
|
|
18
|
+
* a person sees is whether a provider is configured and the last four
|
|
19
|
+
* characters, which is enough to tell two keys apart and not enough to use
|
|
20
|
+
* one.
|
|
21
|
+
*
|
|
22
|
+
* ## Providers differ in ways that matter
|
|
23
|
+
*
|
|
24
|
+
* They all speak `/v1/chat/completions`, and then each has a quirk: OpenRouter
|
|
25
|
+
* wants attribution headers, AgentRouter refuses clients it doesn't recognise,
|
|
26
|
+
* a local Ollama wants no key at all. Those live in the table rather than in
|
|
27
|
+
* the adapter, so adding a provider is data.
|
|
28
|
+
*/
|
|
29
|
+
import fs from "node:fs";
|
|
30
|
+
import path from "node:path";
|
|
31
|
+
import { loomHome } from "./registry.js";
|
|
32
|
+
/**
|
|
33
|
+
* The ones Loom knows by name. Anything else is a custom provider: give it a
|
|
34
|
+
* base URL and it works, because the protocol is the protocol.
|
|
35
|
+
*/
|
|
36
|
+
export const BUILTIN_PROVIDERS = [
|
|
37
|
+
{
|
|
38
|
+
id: "openrouter",
|
|
39
|
+
label: "OpenRouter",
|
|
40
|
+
baseUrl: "https://openrouter.ai/api",
|
|
41
|
+
envKeys: ["OPENROUTER_API_KEY"],
|
|
42
|
+
// OpenRouter asks callers to identify the app; it's used for their
|
|
43
|
+
// leaderboards and rate limits, and it's ours to give honestly.
|
|
44
|
+
headers: {
|
|
45
|
+
"HTTP-Referer": "https://github.com/nickthelegend/loom",
|
|
46
|
+
"X-Title": "Loom",
|
|
47
|
+
},
|
|
48
|
+
// Their own convention: a free model's id ends in `:free`.
|
|
49
|
+
free: (id) => id.endsWith(":free"),
|
|
50
|
+
docs: "https://openrouter.ai/docs",
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
id: "agentrouter",
|
|
54
|
+
label: "AgentRouter",
|
|
55
|
+
baseUrl: "https://agentrouter.org",
|
|
56
|
+
envKeys: ["AGENTROUTER_API_KEY"],
|
|
57
|
+
// Everything AgentRouter serves is free quota, which is the whole point
|
|
58
|
+
// of the service.
|
|
59
|
+
free: () => true,
|
|
60
|
+
note: "refuses clients it doesn't recognise (401 unauthorized_client_error) — ask them to allow-list Loom, or set a user-agent they accept with `loom providers:set agentrouter --header`",
|
|
61
|
+
docs: "https://agentrouter.org/docs/",
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
id: "openai",
|
|
65
|
+
label: "OpenAI",
|
|
66
|
+
baseUrl: "https://api.openai.com",
|
|
67
|
+
envKeys: ["OPENAI_API_KEY"],
|
|
68
|
+
docs: "https://platform.openai.com/docs/api-reference",
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
id: "groq",
|
|
72
|
+
label: "Groq",
|
|
73
|
+
baseUrl: "https://api.groq.com/openai",
|
|
74
|
+
envKeys: ["GROQ_API_KEY"],
|
|
75
|
+
docs: "https://console.groq.com/docs",
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
id: "ollama",
|
|
79
|
+
label: "Ollama (local)",
|
|
80
|
+
baseUrl: "http://127.0.0.1:11434",
|
|
81
|
+
envKeys: ["OLLAMA_API_KEY"],
|
|
82
|
+
// A local server wants no key, and demanding one would be theatre.
|
|
83
|
+
free: () => true,
|
|
84
|
+
note: "local — no key needed",
|
|
85
|
+
docs: "https://github.com/ollama/ollama/blob/main/docs/api.md",
|
|
86
|
+
},
|
|
87
|
+
];
|
|
88
|
+
export function providersFile() {
|
|
89
|
+
return path.join(loomHome(), "providers.json");
|
|
90
|
+
}
|
|
91
|
+
function readStore() {
|
|
92
|
+
try {
|
|
93
|
+
return JSON.parse(fs.readFileSync(providersFile(), "utf8"));
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
return {};
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
function writeStore(store) {
|
|
100
|
+
const file = providersFile();
|
|
101
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
102
|
+
// 0600 before anything is in it: the window between create and chmod is
|
|
103
|
+
// exactly when a key would be world-readable.
|
|
104
|
+
fs.writeFileSync(file, JSON.stringify(store, null, 2), { mode: 0o600 });
|
|
105
|
+
fs.chmodSync(file, 0o600);
|
|
106
|
+
}
|
|
107
|
+
/** The spec for an id, built-in or stored, or null if nobody has heard of it. */
|
|
108
|
+
export function specFor(id) {
|
|
109
|
+
const builtin = BUILTIN_PROVIDERS.find((p) => p.id === id);
|
|
110
|
+
if (builtin)
|
|
111
|
+
return builtin;
|
|
112
|
+
const stored = readStore()[id];
|
|
113
|
+
if (!stored?.baseUrl)
|
|
114
|
+
return null;
|
|
115
|
+
return {
|
|
116
|
+
id,
|
|
117
|
+
label: stored.label ?? id,
|
|
118
|
+
baseUrl: stored.baseUrl.replace(/\/+$/, ""),
|
|
119
|
+
envKeys: [`${id.toUpperCase().replace(/[^A-Z0-9]/g, "_")}_API_KEY`],
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Everything needed to make a call: URL, key, headers.
|
|
124
|
+
*
|
|
125
|
+
* The environment wins over the stored file, so a shell that sets a key for
|
|
126
|
+
* one run doesn't have to undo anything afterwards.
|
|
127
|
+
*/
|
|
128
|
+
export function resolveProvider(id, env = process.env) {
|
|
129
|
+
const spec = specFor(id);
|
|
130
|
+
if (!spec)
|
|
131
|
+
return null;
|
|
132
|
+
const stored = readStore()[id] ?? {};
|
|
133
|
+
const key = spec.envKeys.map((k) => env[k]).find((v) => v?.trim()) ?? stored.key ?? null;
|
|
134
|
+
return {
|
|
135
|
+
id: spec.id,
|
|
136
|
+
label: stored.label ?? spec.label,
|
|
137
|
+
baseUrl: (stored.baseUrl ?? spec.baseUrl).replace(/\/+$/, ""),
|
|
138
|
+
key: key?.trim() || null,
|
|
139
|
+
headers: { ...(spec.headers ?? {}), ...(stored.headers ?? {}) },
|
|
140
|
+
free: spec.free ?? (() => false),
|
|
141
|
+
...(spec.note ? { note: spec.note } : {}),
|
|
142
|
+
...(spec.docs ? { docs: spec.docs } : {}),
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
/** Save a key, a base URL, headers — any subset. Writes 0600. */
|
|
146
|
+
export function setProvider(id, patch) {
|
|
147
|
+
const clean = id.trim().toLowerCase();
|
|
148
|
+
if (!/^[a-z0-9][a-z0-9-]*$/.test(clean))
|
|
149
|
+
throw new Error(`"${id}" isn't a usable provider id`);
|
|
150
|
+
if (!specFor(clean) && !patch.baseUrl) {
|
|
151
|
+
throw new Error(`"${clean}" isn't a provider Loom knows — give it a base URL and it will be`);
|
|
152
|
+
}
|
|
153
|
+
const store = readStore();
|
|
154
|
+
const next = { ...(store[clean] ?? {}), ...patch };
|
|
155
|
+
if (next.baseUrl)
|
|
156
|
+
next.baseUrl = next.baseUrl.replace(/\/+$/, "");
|
|
157
|
+
store[clean] = next;
|
|
158
|
+
writeStore(store);
|
|
159
|
+
}
|
|
160
|
+
export function forgetProvider(id) {
|
|
161
|
+
const store = readStore();
|
|
162
|
+
if (!(id in store))
|
|
163
|
+
return false;
|
|
164
|
+
delete store[id];
|
|
165
|
+
writeStore(store);
|
|
166
|
+
return true;
|
|
167
|
+
}
|
|
168
|
+
/** The last four characters, which tells two keys apart and uses neither. */
|
|
169
|
+
export function maskKey(key) {
|
|
170
|
+
if (!key)
|
|
171
|
+
return "";
|
|
172
|
+
return key.length <= 4 ? "…" : `…${key.slice(-4)}`;
|
|
173
|
+
}
|
|
174
|
+
/** Every provider Loom knows or has been told about, and whether it can be used. */
|
|
175
|
+
export function listProviders(env = process.env) {
|
|
176
|
+
const ids = new Set([...BUILTIN_PROVIDERS.map((p) => p.id), ...Object.keys(readStore())]);
|
|
177
|
+
const out = [];
|
|
178
|
+
for (const id of ids) {
|
|
179
|
+
const p = resolveProvider(id, env);
|
|
180
|
+
if (!p)
|
|
181
|
+
continue;
|
|
182
|
+
const spec = specFor(id);
|
|
183
|
+
const fromEnv = Boolean(spec?.envKeys.some((k) => env[k]?.trim()));
|
|
184
|
+
out.push({
|
|
185
|
+
id: p.id,
|
|
186
|
+
label: p.label,
|
|
187
|
+
baseUrl: p.baseUrl,
|
|
188
|
+
configured: Boolean(p.key) || id === "ollama",
|
|
189
|
+
hint: maskKey(p.key),
|
|
190
|
+
source: fromEnv ? "env" : p.key ? "file" : "none",
|
|
191
|
+
...(p.note ? { note: p.note } : {}),
|
|
192
|
+
...(p.docs ? { docs: p.docs } : {}),
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
return out.sort((a, b) => a.id.localeCompare(b.id));
|
|
196
|
+
}
|
|
197
|
+
// ---------------------------------------------------------------------------
|
|
198
|
+
// The wire
|
|
199
|
+
// ---------------------------------------------------------------------------
|
|
200
|
+
export function chatUrl(p) {
|
|
201
|
+
return `${p.baseUrl}/v1/chat/completions`;
|
|
202
|
+
}
|
|
203
|
+
export function modelsUrl(p) {
|
|
204
|
+
return `${p.baseUrl}/v1/models`;
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Headers for a request. A provider with no key gets none — a local server
|
|
208
|
+
* doesn't want `Authorization: Bearer null`, which is a 401 that reads like a
|
|
209
|
+
* bad key rather than like no key.
|
|
210
|
+
*/
|
|
211
|
+
export function requestHeaders(p) {
|
|
212
|
+
return {
|
|
213
|
+
"content-type": "application/json",
|
|
214
|
+
accept: "application/json",
|
|
215
|
+
...p.headers,
|
|
216
|
+
...(p.key ? { authorization: `Bearer ${p.key}` } : {}),
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Turn a provider's refusal into something a person can act on.
|
|
221
|
+
*
|
|
222
|
+
* These four are the ones worth naming: they're the difference between "your
|
|
223
|
+
* key is wrong", "your free quota is spent", "that model is gone" and "slow
|
|
224
|
+
* down", and each has a different next step.
|
|
225
|
+
*/
|
|
226
|
+
export function explainStatus(status, body, p) {
|
|
227
|
+
const snippet = body.replace(/\s+/g, " ").slice(0, 200);
|
|
228
|
+
if (status === 401 && /unauthorized_client|client detected/i.test(body)) {
|
|
229
|
+
return `${p.label} refused Loom as a client, not your key — it only accepts callers it recognises. ${p.note ?? ""}`.trim();
|
|
230
|
+
}
|
|
231
|
+
// "only available on agentic harnesses": the provider keeps this model for its
|
|
232
|
+
// own app list. Nothing is wrong with the key, and a key check would mislead.
|
|
233
|
+
if ((status === 403 || status === 404) && /only available (?:on|to|in)|not available (?:for|to|via)|restricted to/i.test(body)) {
|
|
234
|
+
return `${p.label} doesn't serve this model to Loom (${status}) — it keeps it for its own app list. Your key is fine: pick another model, or \`pool:free\` to spread across the free ones. ${snippet}`;
|
|
235
|
+
}
|
|
236
|
+
if (status === 401 || status === 403) {
|
|
237
|
+
return `${p.label} rejected the key (${status}). Check it with \`loom providers\`. ${snippet}`;
|
|
238
|
+
}
|
|
239
|
+
if (status === 402) {
|
|
240
|
+
return `${p.label} has no quota left for this model right now (402). Another model on the same key may still work. ${snippet}`;
|
|
241
|
+
}
|
|
242
|
+
if (status === 404 || status === 503) {
|
|
243
|
+
return `${p.label} has no channel for that model (${status}) — the name may be wrong or the model retired. \`loom models --refresh\`. ${snippet}`;
|
|
244
|
+
}
|
|
245
|
+
if (status === 429)
|
|
246
|
+
return `${p.label} is rate-limiting this key (429). ${snippet}`;
|
|
247
|
+
return `${p.label} answered ${status}: ${snippet}`;
|
|
248
|
+
}
|
|
249
|
+
/** Is this the kind of failure another model on the same key might survive? */
|
|
250
|
+
export function isExhausted(status) {
|
|
251
|
+
return status === 402 || status === 429;
|
|
252
|
+
}
|
|
253
|
+
/** How long a model list is believed before asking again. */
|
|
254
|
+
export const MODELS_TTL_MS = 10 * 60_000;
|
|
255
|
+
function cacheFile() {
|
|
256
|
+
return path.join(loomHome(), "models-cache.json");
|
|
257
|
+
}
|
|
258
|
+
function readCache() {
|
|
259
|
+
try {
|
|
260
|
+
return JSON.parse(fs.readFileSync(cacheFile(), "utf8"));
|
|
261
|
+
}
|
|
262
|
+
catch {
|
|
263
|
+
return {};
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* The models a provider has right now.
|
|
268
|
+
*
|
|
269
|
+
* Cached on disk rather than in memory: the CLI is a new process every time,
|
|
270
|
+
* and asking a provider for its catalogue once per command is rude to them and
|
|
271
|
+
* slow for you. `refresh` skips the cache — which is what a model that has
|
|
272
|
+
* just appeared (or vanished) needs.
|
|
273
|
+
*/
|
|
274
|
+
export async function fetchModels(p, opts = {}) {
|
|
275
|
+
const cache = readCache();
|
|
276
|
+
const hit = cache[p.id];
|
|
277
|
+
if (!opts.refresh && hit && Date.now() - hit.at < MODELS_TTL_MS) {
|
|
278
|
+
return { models: hit.models, cached: true };
|
|
279
|
+
}
|
|
280
|
+
const doFetch = opts.fetchImpl ?? fetch;
|
|
281
|
+
try {
|
|
282
|
+
const res = await doFetch(modelsUrl(p), { headers: requestHeaders(p) });
|
|
283
|
+
if (!res.ok) {
|
|
284
|
+
const body = await res.text().catch(() => "");
|
|
285
|
+
// A stale list beats no list: the models probably still exist, and the
|
|
286
|
+
// person asked what they could use, not whether the network is up.
|
|
287
|
+
return { models: hit?.models ?? [], cached: Boolean(hit), error: explainStatus(res.status, body, p) };
|
|
288
|
+
}
|
|
289
|
+
const body = (await res.json());
|
|
290
|
+
const models = (body.data ?? [])
|
|
291
|
+
.map((m) => String(m.id ?? ""))
|
|
292
|
+
.filter(Boolean)
|
|
293
|
+
.map((id) => {
|
|
294
|
+
const raw = (body.data ?? []).find((m) => m.id === id);
|
|
295
|
+
return {
|
|
296
|
+
id,
|
|
297
|
+
provider: p.id,
|
|
298
|
+
free: p.free(id),
|
|
299
|
+
...(raw?.supported_endpoint_types ? { endpoints: raw.supported_endpoint_types } : {}),
|
|
300
|
+
};
|
|
301
|
+
})
|
|
302
|
+
.sort((a, b) => a.id.localeCompare(b.id));
|
|
303
|
+
try {
|
|
304
|
+
const next = { ...cache, [p.id]: { at: Date.now(), models } };
|
|
305
|
+
fs.mkdirSync(path.dirname(cacheFile()), { recursive: true });
|
|
306
|
+
fs.writeFileSync(cacheFile(), JSON.stringify(next));
|
|
307
|
+
}
|
|
308
|
+
catch {
|
|
309
|
+
/* a cache that can't be written is still a list that can be returned */
|
|
310
|
+
}
|
|
311
|
+
return { models, cached: false };
|
|
312
|
+
}
|
|
313
|
+
catch (err) {
|
|
314
|
+
return {
|
|
315
|
+
models: hit?.models ?? [],
|
|
316
|
+
cached: Boolean(hit),
|
|
317
|
+
error: `couldn't reach ${p.label}: ${String(err.message)}`,
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
/** Every configured provider's models, for a picker that spans providers. */
|
|
322
|
+
export async function allModels(opts = {}) {
|
|
323
|
+
const env = opts.env ?? process.env;
|
|
324
|
+
const models = [];
|
|
325
|
+
const errors = [];
|
|
326
|
+
for (const row of listProviders(env)) {
|
|
327
|
+
if (!row.configured)
|
|
328
|
+
continue;
|
|
329
|
+
const p = resolveProvider(row.id, env);
|
|
330
|
+
if (!p)
|
|
331
|
+
continue;
|
|
332
|
+
const got = await fetchModels(p, opts.refresh ? { refresh: true } : {});
|
|
333
|
+
// A provider with free models offers the free pool first: every turn on the
|
|
334
|
+
// next free model in a rotation (core/free-pool.ts), so the work spreads.
|
|
335
|
+
if (got.models.some((m) => m.free))
|
|
336
|
+
models.push({ id: "pool:free", provider: row.id, free: true });
|
|
337
|
+
models.push(...got.models);
|
|
338
|
+
if (got.error)
|
|
339
|
+
errors.push({ provider: row.id, error: got.error });
|
|
340
|
+
}
|
|
341
|
+
return { models, errors };
|
|
342
|
+
}
|
|
343
|
+
//# sourceMappingURL=providers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"providers.js","sourceRoot":"","sources":["../../src/core/providers.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Quota Relay — recognising "this agent is out of quota", and until when.
|
|
3
|
+
*
|
|
4
|
+
* A subscription CLI that hits its 5-hour or weekly cap mid-task either parks
|
|
5
|
+
* the turn until the reset (Claude Code: no result arrives for hours) or ends
|
|
6
|
+
* it with an error (Codex: "You've hit your usage limit… try again at 3:04
|
|
7
|
+
* PM"). Either way the work stops. Loom sees both: Claude and Codex report
|
|
8
|
+
* their windows as a `usage_limits` status with `reached` set, and every CLI
|
|
9
|
+
* says it in words. This file turns either into { resetAt }; the runtime
|
|
10
|
+
* (daemon/runtime/relay.ts) hands the work to the next agent.
|
|
11
|
+
*/
|
|
12
|
+
import type { LoomEvent } from "../types.js";
|
|
13
|
+
export interface QuotaHit {
|
|
14
|
+
/** When the limit lifts (epoch ms), when the agent said; otherwise a guess an hour out. */
|
|
15
|
+
resetAt: number;
|
|
16
|
+
/** Whether resetAt came from the agent rather than the default. */
|
|
17
|
+
known: boolean;
|
|
18
|
+
/** Which window: "five_hour", "seven_day", "primary"… or "limit". */
|
|
19
|
+
window: string;
|
|
20
|
+
/** The words, for the thread. */
|
|
21
|
+
reason: string;
|
|
22
|
+
}
|
|
23
|
+
/** The default when the agent doesn't say when: an hour, the shortest common window. */
|
|
24
|
+
export declare const UNKNOWN_RESET_MS: number;
|
|
25
|
+
/**
|
|
26
|
+
* "try again at 3:04 PM", "resets at 19:00", "resets in 2 hours",
|
|
27
|
+
* "in 45 minutes", "resets Oct 16, 9:00 AM" → epoch ms (the next such moment).
|
|
28
|
+
*/
|
|
29
|
+
export declare function parseReset(text: string, now?: number): number | null;
|
|
30
|
+
/** Did this event say the agent is out of quota? */
|
|
31
|
+
export declare function quotaHit(event: LoomEvent, now?: number): QuotaHit | null;
|
|
32
|
+
/** "19:00" / "tomorrow 09:00" — when it's back, for a person reading the thread. */
|
|
33
|
+
export declare function backAt(resetAt: number, now?: number): string;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Quota Relay — recognising "this agent is out of quota", and until when.
|
|
3
|
+
*
|
|
4
|
+
* A subscription CLI that hits its 5-hour or weekly cap mid-task either parks
|
|
5
|
+
* the turn until the reset (Claude Code: no result arrives for hours) or ends
|
|
6
|
+
* it with an error (Codex: "You've hit your usage limit… try again at 3:04
|
|
7
|
+
* PM"). Either way the work stops. Loom sees both: Claude and Codex report
|
|
8
|
+
* their windows as a `usage_limits` status with `reached` set, and every CLI
|
|
9
|
+
* says it in words. This file turns either into { resetAt }; the runtime
|
|
10
|
+
* (daemon/runtime/relay.ts) hands the work to the next agent.
|
|
11
|
+
*/
|
|
12
|
+
const LIMIT_TEXT = /\b(?:usage limit|hit (?:your|the) (?:usage |rate )?limit|rate[- ]limit(?:ed)?|quota (?:exceeded|exhausted|reached)|exceeded (?:your |the )?(?:current )?quota|limit reached|too many requests|out of (?:credits|quota)|credits? (?:exhausted|exceeded)|insufficient (?:quota|credits|balance)|weekly limit|5-hour limit)\b/i;
|
|
13
|
+
/** Words that mean it's *not* a quota: a context window, a file-size or a request-size limit. */
|
|
14
|
+
const NOT_QUOTA = /\b(?:context (?:window|length)|token limit for (?:this|the) (?:request|prompt)|max(?:imum)? (?:tokens|context)|too large|file size|payload)\b/i;
|
|
15
|
+
/** The default when the agent doesn't say when: an hour, the shortest common window. */
|
|
16
|
+
export const UNKNOWN_RESET_MS = 60 * 60_000;
|
|
17
|
+
/**
|
|
18
|
+
* "try again at 3:04 PM", "resets at 19:00", "resets in 2 hours",
|
|
19
|
+
* "in 45 minutes", "resets Oct 16, 9:00 AM" → epoch ms (the next such moment).
|
|
20
|
+
*/
|
|
21
|
+
export function parseReset(text, now = Date.now()) {
|
|
22
|
+
const t = text.replace(/\s+/g, " ");
|
|
23
|
+
let m = /\b(?:in|after)\s+(\d+(?:\.\d+)?)\s*(minutes?|mins?|m|hours?|hrs?|h|days?|d)\b/i.exec(t);
|
|
24
|
+
if (m) {
|
|
25
|
+
const n = Number(m[1]), unit = m[2].toLowerCase();
|
|
26
|
+
const ms = unit.startsWith("d") ? 86_400_000 : unit.startsWith("h") ? 3_600_000 : 60_000;
|
|
27
|
+
return now + Math.round(n * ms);
|
|
28
|
+
}
|
|
29
|
+
m = /\b(?:at|after|until|resets?(?: at)?|try again at)\s+(\d{1,2})(?::(\d{2}))?\s*(am|pm)?\b/i.exec(t);
|
|
30
|
+
if (m && (m[2] !== undefined || m[3] !== undefined)) {
|
|
31
|
+
let h = Number(m[1]);
|
|
32
|
+
const min = Number(m[2] ?? 0), ap = m[3]?.toLowerCase();
|
|
33
|
+
if (ap === "pm" && h < 12)
|
|
34
|
+
h += 12;
|
|
35
|
+
if (ap === "am" && h === 12)
|
|
36
|
+
h = 0;
|
|
37
|
+
if (h > 23 || min > 59)
|
|
38
|
+
return null;
|
|
39
|
+
const d = new Date(now);
|
|
40
|
+
d.setHours(h, min, 0, 0);
|
|
41
|
+
if (d.getTime() <= now)
|
|
42
|
+
d.setDate(d.getDate() + 1);
|
|
43
|
+
return d.getTime();
|
|
44
|
+
}
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
/** Did this event say the agent is out of quota? */
|
|
48
|
+
export function quotaHit(event, now = Date.now()) {
|
|
49
|
+
const p = event.payload;
|
|
50
|
+
if (event.kind === "status" && p.state === "usage_limits" && typeof p.reached === "string" && p.reached) {
|
|
51
|
+
const windows = Array.isArray(p.windows) ? p.windows : [];
|
|
52
|
+
const hit = windows.find((w) => w.id === p.reached) ?? windows.find((w) => typeof w.resetsAt === "number");
|
|
53
|
+
const resetAt = typeof hit?.resetsAt === "number" && hit.resetsAt > now ? hit.resetsAt : null;
|
|
54
|
+
return { resetAt: resetAt ?? now + UNKNOWN_RESET_MS, known: resetAt !== null, window: p.reached,
|
|
55
|
+
reason: `usage limit reached (${p.reached})` };
|
|
56
|
+
}
|
|
57
|
+
const text = event.kind === "error" ? String(p.message ?? "")
|
|
58
|
+
: event.kind === "run_complete" && typeof p.error === "string" ? p.error
|
|
59
|
+
: event.kind === "status" && (p.state === "notice" || p.state === "warning") ? String(p.message ?? "") : "";
|
|
60
|
+
// a CLI retrying a brief rate limit by itself hasn't stopped: no relay for that
|
|
61
|
+
if (p.retrying === true)
|
|
62
|
+
return null;
|
|
63
|
+
if (!text || !LIMIT_TEXT.test(text) || NOT_QUOTA.test(text))
|
|
64
|
+
return null;
|
|
65
|
+
const resetAt = parseReset(text, now);
|
|
66
|
+
return { resetAt: resetAt ?? now + UNKNOWN_RESET_MS, known: resetAt !== null, window: "limit", reason: text.trim().slice(0, 240) };
|
|
67
|
+
}
|
|
68
|
+
/** "19:00" / "tomorrow 09:00" — when it's back, for a person reading the thread. */
|
|
69
|
+
export function backAt(resetAt, now = Date.now()) {
|
|
70
|
+
const d = new Date(resetAt), today = new Date(now);
|
|
71
|
+
const time = d.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
|
|
72
|
+
return d.toDateString() === today.toDateString() ? time : `${d.toLocaleDateString([], { weekday: "short" })} ${time}`;
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=quota.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"quota.js","sourceRoot":"","sources":["../../src/core/quota.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A queue worth keeping: the same five steps you run on every project.
|
|
3
|
+
*
|
|
4
|
+
* "Update deps, run the tests, fix what broke, update the changelog, open the
|
|
5
|
+
* PR" is a queue you build by hand every time. A recipe is that queue saved —
|
|
6
|
+
* an ordered list of prompts, each remembering who it goes to.
|
|
7
|
+
*
|
|
8
|
+
* Targets travel by ROLE rather than by agent id, because `claude-code` on one
|
|
9
|
+
* project is `reviewer` on another and an id from someone else's machine means
|
|
10
|
+
* nothing here. Running a recipe resolves those roles against the project it
|
|
11
|
+
* lands in, and anything it can't resolve becomes an Auto prompt rather than a
|
|
12
|
+
* refusal — the prompts are still editable before they go.
|
|
13
|
+
*
|
|
14
|
+
* Stored in ~/.loom/recipes.json. Nothing leaves the machine.
|
|
15
|
+
*/
|
|
16
|
+
import type { QueueTarget } from "./prompt-queue.js";
|
|
17
|
+
export interface RecipeStep {
|
|
18
|
+
text: string;
|
|
19
|
+
/** "auto", "orchestra", or a role/kind like "reviewer" or "codex". */
|
|
20
|
+
to: string;
|
|
21
|
+
plan?: boolean;
|
|
22
|
+
}
|
|
23
|
+
export interface Recipe {
|
|
24
|
+
name: string;
|
|
25
|
+
steps: RecipeStep[];
|
|
26
|
+
/** Where it came from, for the person reading a list of them later. */
|
|
27
|
+
fromProject?: string;
|
|
28
|
+
savedAt: number;
|
|
29
|
+
}
|
|
30
|
+
export declare const MAX_STEPS = 50;
|
|
31
|
+
export declare function listRecipes(): Recipe[];
|
|
32
|
+
export declare function getRecipe(name: string): Recipe | null;
|
|
33
|
+
/** Save (or replace) a recipe. Names are how you call it back, so they're checked. */
|
|
34
|
+
export declare function saveRecipe(recipe: Omit<Recipe, "savedAt">): Recipe;
|
|
35
|
+
export declare function deleteRecipe(name: string): boolean;
|
|
36
|
+
/** What a queued item's target becomes when a recipe is saved. */
|
|
37
|
+
export declare function targetToRole(target: QueueTarget, roleOf: (agentId: string) => string | undefined): string;
|
|
38
|
+
/**
|
|
39
|
+
* Resolve a saved step's target against a project's agents.
|
|
40
|
+
*
|
|
41
|
+
* An exact id wins, then a role, then a kind. Nothing matching means Auto: the
|
|
42
|
+
* prompt still runs, the router still picks, and the person can still change
|
|
43
|
+
* it before it goes — better than refusing a recipe because a machine happens
|
|
44
|
+
* to call its reviewer something else.
|
|
45
|
+
*/
|
|
46
|
+
export declare function roleToTarget(to: string, agents: Array<{
|
|
47
|
+
id: string;
|
|
48
|
+
role?: string;
|
|
49
|
+
kind?: string;
|
|
50
|
+
}>): QueueTarget;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A queue worth keeping: the same five steps you run on every project.
|
|
3
|
+
*
|
|
4
|
+
* "Update deps, run the tests, fix what broke, update the changelog, open the
|
|
5
|
+
* PR" is a queue you build by hand every time. A recipe is that queue saved —
|
|
6
|
+
* an ordered list of prompts, each remembering who it goes to.
|
|
7
|
+
*
|
|
8
|
+
* Targets travel by ROLE rather than by agent id, because `claude-code` on one
|
|
9
|
+
* project is `reviewer` on another and an id from someone else's machine means
|
|
10
|
+
* nothing here. Running a recipe resolves those roles against the project it
|
|
11
|
+
* lands in, and anything it can't resolve becomes an Auto prompt rather than a
|
|
12
|
+
* refusal — the prompts are still editable before they go.
|
|
13
|
+
*
|
|
14
|
+
* Stored in ~/.loom/recipes.json. Nothing leaves the machine.
|
|
15
|
+
*/
|
|
16
|
+
import fs from "node:fs";
|
|
17
|
+
import path from "node:path";
|
|
18
|
+
import { loomHome } from "./registry.js";
|
|
19
|
+
export const MAX_STEPS = 50;
|
|
20
|
+
function file() {
|
|
21
|
+
return path.join(loomHome(), "recipes.json");
|
|
22
|
+
}
|
|
23
|
+
export function listRecipes() {
|
|
24
|
+
try {
|
|
25
|
+
const raw = JSON.parse(fs.readFileSync(file(), "utf8"));
|
|
26
|
+
return Array.isArray(raw) ? raw.filter((r) => r?.name && Array.isArray(r.steps)) : [];
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return [];
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
export function getRecipe(name) {
|
|
33
|
+
return listRecipes().find((r) => r.name === name) ?? null;
|
|
34
|
+
}
|
|
35
|
+
function write(all) {
|
|
36
|
+
fs.mkdirSync(loomHome(), { recursive: true });
|
|
37
|
+
fs.writeFileSync(file(), JSON.stringify(all, null, 2));
|
|
38
|
+
}
|
|
39
|
+
/** Save (or replace) a recipe. Names are how you call it back, so they're checked. */
|
|
40
|
+
export function saveRecipe(recipe) {
|
|
41
|
+
const name = recipe.name.trim();
|
|
42
|
+
if (!/^[\w][\w .-]{0,48}$/.test(name))
|
|
43
|
+
throw new Error(`"${name}" isn't a usable recipe name`);
|
|
44
|
+
if (!recipe.steps.length)
|
|
45
|
+
throw new Error("a recipe with no steps isn't a recipe");
|
|
46
|
+
if (recipe.steps.length > MAX_STEPS)
|
|
47
|
+
throw new Error(`a recipe holds at most ${MAX_STEPS} steps`);
|
|
48
|
+
const steps = recipe.steps.map((s) => {
|
|
49
|
+
const text = String(s.text ?? "").trim();
|
|
50
|
+
if (!text)
|
|
51
|
+
throw new Error("a recipe step needs something to say");
|
|
52
|
+
return { text, to: String(s.to ?? "auto").trim() || "auto", ...(s.plan ? { plan: true } : {}) };
|
|
53
|
+
});
|
|
54
|
+
const saved = { name, steps, ...(recipe.fromProject ? { fromProject: recipe.fromProject } : {}), savedAt: Date.now() };
|
|
55
|
+
write([saved, ...listRecipes().filter((r) => r.name !== name)]);
|
|
56
|
+
return saved;
|
|
57
|
+
}
|
|
58
|
+
export function deleteRecipe(name) {
|
|
59
|
+
const all = listRecipes();
|
|
60
|
+
const left = all.filter((r) => r.name !== name);
|
|
61
|
+
if (left.length === all.length)
|
|
62
|
+
return false;
|
|
63
|
+
write(left);
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
/** What a queued item's target becomes when a recipe is saved. */
|
|
67
|
+
export function targetToRole(target, roleOf) {
|
|
68
|
+
if (target.kind === "orchestra")
|
|
69
|
+
return "orchestra";
|
|
70
|
+
if (target.kind === "auto")
|
|
71
|
+
return "auto";
|
|
72
|
+
return roleOf(target.agentId) ?? target.agentId;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Resolve a saved step's target against a project's agents.
|
|
76
|
+
*
|
|
77
|
+
* An exact id wins, then a role, then a kind. Nothing matching means Auto: the
|
|
78
|
+
* prompt still runs, the router still picks, and the person can still change
|
|
79
|
+
* it before it goes — better than refusing a recipe because a machine happens
|
|
80
|
+
* to call its reviewer something else.
|
|
81
|
+
*/
|
|
82
|
+
export function roleToTarget(to, agents) {
|
|
83
|
+
const want = to.trim().toLowerCase();
|
|
84
|
+
if (!want || want === "auto")
|
|
85
|
+
return { kind: "auto" };
|
|
86
|
+
if (want === "orchestra" || want === "orchestrate")
|
|
87
|
+
return { kind: "orchestra" };
|
|
88
|
+
const byId = agents.find((a) => a.id.toLowerCase() === want);
|
|
89
|
+
if (byId)
|
|
90
|
+
return { kind: "agent", agentId: byId.id };
|
|
91
|
+
const byRole = agents.find((a) => (a.role ?? "").toLowerCase() === want);
|
|
92
|
+
if (byRole)
|
|
93
|
+
return { kind: "agent", agentId: byRole.id };
|
|
94
|
+
const byKind = agents.find((a) => (a.kind ?? "").toLowerCase() === want);
|
|
95
|
+
if (byKind)
|
|
96
|
+
return { kind: "agent", agentId: byKind.id };
|
|
97
|
+
return { kind: "auto" };
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=recipes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recipes.js","sourceRoot":"","sources":["../../src/core/recipes.ts"],"names":[],"mappings":"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"}
|