@loompad/cli 0.1.0 → 1.1.2
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 +1054 -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 +4646 -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/release-notes/v1.1.2.md +21 -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
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,1060 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.1.2] — 2026-10-17
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- **A working agent's row in the Fleet spun over the row below it.** The
|
|
15
|
+
spinner's style applied to anything marked busy, so a busy Fleet row
|
|
16
|
+
shrank to a spinning dot with its name and status spilling over the next
|
|
17
|
+
row. The same thing happened to the terminal's input while a command ran,
|
|
18
|
+
the ✨ commit-message button while it worked, and busy nodes in the
|
|
19
|
+
Observatory graph. Only the spinner spins now.
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
## [1.1.1] — 2026-10-16
|
|
23
|
+
|
|
24
|
+
Found installing 1.1.0 on a Mac whose projects live on an external drive.
|
|
25
|
+
|
|
26
|
+
### Fixed
|
|
27
|
+
|
|
28
|
+
- **Loom Desktop explains a slow start instead of sitting blank.** The first
|
|
29
|
+
time it reads projects on an external drive (or in Documents or Desktop),
|
|
30
|
+
macOS asks whether Loom Desktop may access those files, and the background
|
|
31
|
+
service waits for the answer. The window now says *Starting Loom…* at once,
|
|
32
|
+
and after a few seconds that macOS may be asking, and to choose Allow.
|
|
33
|
+
- **It no longer starts a second background service** onto a port that's
|
|
34
|
+
taken when the first is running but waiting on that prompt. It waits for
|
|
35
|
+
the first one, up to five minutes.
|
|
36
|
+
- **The error page gives an installed app's advice**: quit and reopen, where
|
|
37
|
+
the permission lives in System Settings, and where the log is. It no longer
|
|
38
|
+
says "run npm run build". The pairing request has a timeout.
|
|
39
|
+
- **Builds prune compiled files whose source is gone.** Leftovers in a
|
|
40
|
+
long-lived checkout changed the build fingerprint the desktop app and the
|
|
41
|
+
CLI compare, so the same release could look like two builds and each would
|
|
42
|
+
restart the other's background service.
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
## [1.1.0] — 2026-10-16
|
|
46
|
+
|
|
47
|
+
### Can you trust what your agents did?
|
|
48
|
+
|
|
49
|
+
- **Proof**: when a reply claims the tests, build, typecheck or lint pass,
|
|
50
|
+
Loom checks the claim against what that turn actually ran, and re-runs the
|
|
51
|
+
tests in a clean worktree holding exactly the agent's changes. The verdict
|
|
52
|
+
is a card in the thread: Verified, Didn't hold (with why), or Unverified.
|
|
53
|
+
Every turn card has **Check it**; `loom proof` from a terminal.
|
|
54
|
+
- **Loop watch**: the same command failing three times in a turn (or the same
|
|
55
|
+
call five times) gets a card with Stop / Nudge it / Hand it off, and a push.
|
|
56
|
+
`watchdog.auto: "nudge"` also stops it and tells it to step back, once.
|
|
57
|
+
- **Blast-Radius Shield**: a force-push, `reset --hard`, a recursive delete
|
|
58
|
+
outside the project, `DROP TABLE`, `terraform destroy`, `npm publish`,
|
|
59
|
+
`sudo`, `curl | sh`, and the like get a red card and a push the moment an
|
|
60
|
+
agent runs them. `shield.auto: "stop"` (the default on a night shift) stops
|
|
61
|
+
the turn.
|
|
62
|
+
- **Review Triage**: every turn card says which files to read first, and why:
|
|
63
|
+
an added secret, a deleted or skipped test, auth code, a migration, CI,
|
|
64
|
+
dependencies, risky calls, a big change with no test.
|
|
65
|
+
- **Scorecards**: each agent's record of how often its "done" held up, its
|
|
66
|
+
circles and shield trips, and cost per verified result, in the
|
|
67
|
+
Observatory leaderboard, the phone, and `loom scorecard`.
|
|
68
|
+
|
|
69
|
+
### Work that carries on without you
|
|
70
|
+
|
|
71
|
+
- **Night Shift**: line up tasks, one per line. They run one after another, a
|
|
72
|
+
chat each, while you're away, guarded by Proof, the loop watch and the
|
|
73
|
+
shield, and stop at a stop-by time or a budget. One morning report says how
|
|
74
|
+
each went. Desktop ⋯ → Night shift, phone 🌙, or `loom shift`.
|
|
75
|
+
- **Quota Relay**: an agent that hits its usage limit mid-task hands its work
|
|
76
|
+
(and a briefing) to the next agent, routes new prompts there while it's out,
|
|
77
|
+
and takes the baton back when the window resets.
|
|
78
|
+
- **Pre-flight forecast**: the composer shows what a turn on the chosen agent
|
|
79
|
+
usually costs and takes, from its own recent turns, and turns amber when
|
|
80
|
+
one more could cross its daily budget.
|
|
81
|
+
- **Replay**: export any chat as one self-contained HTML page to share, with
|
|
82
|
+
no scripts and nothing to load. ⋯ → Export as replay, or `loom replay`.
|
|
83
|
+
- **Design Mode**: a picked element in the Browser tab brings its computed
|
|
84
|
+
styles and its own screenshot into the composer.
|
|
85
|
+
|
|
86
|
+
### Agent Teams
|
|
87
|
+
|
|
88
|
+
- **Persistent teammates** (P2) with their own native sessions and **crew
|
|
89
|
+
memory**. Teammates can now `remember` decisions, constraints and
|
|
90
|
+
conventions themselves.
|
|
91
|
+
- **Parallel builders** in isolated worktrees, and **races** where you pick
|
|
92
|
+
the result (P3).
|
|
93
|
+
- **Team coordination**: crews across teammates' daemons, owner requests, and
|
|
94
|
+
runners that execute crew goals. Full crew controls on desktop and phone.
|
|
95
|
+
|
|
96
|
+
### Windows and a lighter daemon
|
|
97
|
+
|
|
98
|
+
- Windows **Job Object** containment, native continuity on Windows, and
|
|
99
|
+
OpenCode 1.19 continuity. Fixes for ConPTY exit races and CLI launching.
|
|
100
|
+
- **Memory**: the daemon starts ~25 MB lighter (the Claude SDK loads on first
|
|
101
|
+
use), idle OpenCode servers stop after 30 minutes, CLI probes are cached,
|
|
102
|
+
event reads are indexed, and long sessions no longer leak turn text,
|
|
103
|
+
attachments or socket backlogs.
|
|
104
|
+
- OpenCode bash calls report their exit code, so a failing command no longer
|
|
105
|
+
reads as a success.
|
|
106
|
+
|
|
107
|
+
### Fixed
|
|
108
|
+
|
|
109
|
+
- A prompt sent to an agent just after its usage window reset, before the
|
|
110
|
+
baton came back, failed with "does not hold the baton". A stand-in that was
|
|
111
|
+
mid-turn at the reset kept the baton for good.
|
|
112
|
+
- A runner's dropped progress heartbeat left a goal's PR invisible to the
|
|
113
|
+
team for up to a minute; it's re-sent after 5 seconds.
|
|
114
|
+
- Phone sign-in aligned with GitHub; OAuth callbacks are validated.
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
### One link to bring a teammate in, and crews of agents
|
|
118
|
+
|
|
119
|
+
- **Invite** (a project's header, or `loom invite`) gives you one https link.
|
|
120
|
+
Opening it signs the teammate in with GitHub, joins the team, and clones the
|
|
121
|
+
repo; for a private repo, your Loom grants push access on join and theirs
|
|
122
|
+
accepts GitHub's invitation. It then opens the repo with *their* agents,
|
|
123
|
+
shares it with the team, and sets up your crews. Without Loom, the link's
|
|
124
|
+
page shows one command that installs it and joins. `loom join <link>` does
|
|
125
|
+
the same from a terminal.
|
|
126
|
+
- **Crews** (Agent Teams, Phase 1): a lead, builders, a reviewer and a tester
|
|
127
|
+
work one goal in its own worktree. The Lead plans, you approve, and each card
|
|
128
|
+
is built, reviewed (with rounds of changes) and tested; then you apply the
|
|
129
|
+
branch. Use the Crew tab, or `loom crew create|goal|say|approve|stop|resume|apply`.
|
|
130
|
+
- The Crew tab: the crew's faces in a hero, each teammate as a card that
|
|
131
|
+
lights up while it works, the goal's progress, a board with a column per
|
|
132
|
+
stage, an approval banner, the channel as a conversation, and a composer
|
|
133
|
+
that knows whether you're starting, answering or changing the plan. Pick
|
|
134
|
+
which agent sits in each seat when you make a crew, and swap one later.
|
|
135
|
+
- Crews keep going when an agent misbehaves: a teammate that goes silent is
|
|
136
|
+
interrupted and retried once (`stallMinutes`, default 8); a turn that errors
|
|
137
|
+
is retried once; a reviewer or tester whose turn failed fails the card
|
|
138
|
+
instead of approving it.
|
|
139
|
+
- **Fixed:** OpenCode chat turns could end after a step that only ran tools,
|
|
140
|
+
before the answer, when the model was slow to start its next step. And
|
|
141
|
+
interrupting an OpenCode turn that never answered no longer waits out the hour.
|
|
142
|
+
- **Fixed:** a project forgot which tab you were on whenever it redrew while
|
|
143
|
+
loading, so you landed back on Chat.
|
|
144
|
+
- Team invite links are https links now. `loom://team/join#…` links still work
|
|
145
|
+
anywhere a link is accepted.
|
|
146
|
+
|
|
147
|
+
### A hundred small things (the enhancement sweep)
|
|
148
|
+
|
|
149
|
+
Ranked, filed as #109–#208, and built top down. Each one was tried in the
|
|
150
|
+
running app with real agents before the next started.
|
|
151
|
+
|
|
152
|
+
**In the thread**
|
|
153
|
+
- **Find** (⌘F) with highlighted matches; **drafts** that survive reloads and
|
|
154
|
+
chat switches; **↑** recalls your last prompts; **[ / ]** jump between them.
|
|
155
|
+
- A reply's ⋯ menu: **Retry**, **Retry with…** another agent, **Continue** a
|
|
156
|
+
stopped reply, **Edit & resend**, **Quote**, **Star**, **Copy link**
|
|
157
|
+
(permalinks), **Branch from here**, **Make a card**, **Read aloud**.
|
|
158
|
+
- 👍 / 👎 on replies, counted on the Insights leaderboard.
|
|
159
|
+
- Day separators, relative times that stay fresh, **Show more** on very long
|
|
160
|
+
messages, a token estimate in the composer, **Reply length** (Brief /
|
|
161
|
+
Normal / Detailed), `{{variables}}` in saved prompts.
|
|
162
|
+
- **Mermaid** blocks get *Draw diagram* — the renderer is fetched the first
|
|
163
|
+
time you press it, never before.
|
|
164
|
+
- Export a thread to Markdown (also `loom export`).
|
|
165
|
+
- Error cards you can act on: a countdown on rate limits, *Send to…* another
|
|
166
|
+
agent, *Copy details*.
|
|
167
|
+
- A prompt sent while Loom is down waits and sends itself.
|
|
168
|
+
- Very long live threads trim themselves; the outline rail lists your prompts.
|
|
169
|
+
|
|
170
|
+
**The sidebar**
|
|
171
|
+
- Pin, archive and unread dots on chats; **folders** (right-click → Move to
|
|
172
|
+
folder…, fold them, rename or ungroup); **Group chats by agent**.
|
|
173
|
+
- *Since you were here*: back on a new day, Main opens with what happened
|
|
174
|
+
while you were away.
|
|
175
|
+
|
|
176
|
+
**Orchestra**
|
|
177
|
+
- The plan as a **graph** and a **timeline**; a live cost and time ticker;
|
|
178
|
+
**Run again**; a finish flourish.
|
|
179
|
+
- **Resume**: a run Loom stopped by restarting picks up where it was.
|
|
180
|
+
- **Race**: every picked agent takes the same goal in its own worktree; the
|
|
181
|
+
entrants sit side by side with a diff each, and *Pick this one* merges only
|
|
182
|
+
that one.
|
|
183
|
+
|
|
184
|
+
**Agents, memory, board, insights**
|
|
185
|
+
- Per-agent **standing instructions**, **pictures**, model **temperature and
|
|
186
|
+
max tokens**, and **Check** — installed, signed in, model listed, no prompt
|
|
187
|
+
sent.
|
|
188
|
+
- Memory you can **edit**, **export** and **import**; which memories agents
|
|
189
|
+
actually get (*used N×*, Most used); a **graph** of memories joined by the
|
|
190
|
+
files they share.
|
|
191
|
+
- Board cards with **priority** and **due** dates; **WIP limits** on columns.
|
|
192
|
+
- Insights: an agent **leaderboard**, an activity **heatmap**, costs as
|
|
193
|
+
**CSV**, and month-end at this pace.
|
|
194
|
+
- Diffs **side by side**, and **revert one file** of a turn.
|
|
195
|
+
|
|
196
|
+
**The app around it**
|
|
197
|
+
- Tab title and favicon show what's working and who needs you; a finished turn
|
|
198
|
+
in a hidden tab notifies (with an optional chime); **?** lists every
|
|
199
|
+
shortcut; **focus mode** (⌘.); a first-run tour.
|
|
200
|
+
- Text size, density and accent colour; view transitions between tabs;
|
|
201
|
+
reduced-motion respected; skeleton loaders; the branch in the status bar;
|
|
202
|
+
empty states you can recognise; a project whose folder is gone says so.
|
|
203
|
+
- Terminal find and clear. Diagnostics says what's running. Project settings →
|
|
204
|
+
Storage shows the log's size and compacts it.
|
|
205
|
+
- Voice input without a transcriber uses the browser's own speech recognition.
|
|
206
|
+
- CLI: `loom backup`, `loom export`, `loom stats`, `loom completion zsh|bash`,
|
|
207
|
+
`loom orchestra:resume`; `loom pair` carries the Loom Cloud route.
|
|
208
|
+
- Hardened edges: security headers, a throttled pairing claim, JSON errors
|
|
209
|
+
instead of Express's HTML pages (which leaked stack traces).
|
|
210
|
+
|
|
211
|
+
**On the phone**
|
|
212
|
+
- Follows the system theme; long-press a message to copy or share; a copy
|
|
213
|
+
button on code; pull to refresh; unread and working dots on the chat chips;
|
|
214
|
+
haptics.
|
|
215
|
+
- The daemon card graphs **link quality** over the last two minutes.
|
|
216
|
+
- An **offline copy** of the project list and your last eight threads, per
|
|
217
|
+
paired daemon, cleared on unpair.
|
|
218
|
+
- Android's status bar no longer covers every screen's header.
|
|
219
|
+
|
|
220
|
+
**Fixed on the way**
|
|
221
|
+
- Every *Rename…* used `window.prompt`, which the desktop app doesn't have —
|
|
222
|
+
they silently did nothing. An in-app dialog replaces all seven.
|
|
223
|
+
- A turn's diff was logged to Main whatever chat the turn ran in.
|
|
224
|
+
- An orchestra task's second attempt sent an empty prompt.
|
|
225
|
+
- A queued prompt lost its reply length.
|
|
226
|
+
- A skipped tab transition filled the console with uncaught rejections.
|
|
227
|
+
- `npm test` skipped four DOM suites; it runs them now.
|
|
228
|
+
|
|
229
|
+
### Replies you can watch being written
|
|
230
|
+
|
|
231
|
+
- **The thread showed nothing between your send and the finished reply** —
|
|
232
|
+
fifteen seconds of a blank pane, then a wall of text. Nothing between an
|
|
233
|
+
adapter and the socket carried text that wasn't a finished event.
|
|
234
|
+
- Replies now stream token by token: Claude Code (`--include-partial-messages`),
|
|
235
|
+
OpenCode 1.18+ (`session.next.text.delta`, which the adapter had never
|
|
236
|
+
listened for), OpenAI-compatible model agents, and every orchestra worker in
|
|
237
|
+
its own task thread. The typing is a `stream` socket frame and is **never
|
|
238
|
+
logged** — the finished message is still the record, exactly once.
|
|
239
|
+
- Model agents logged a reply as a message per sentence (and a message per
|
|
240
|
+
reasoning token), which read as one answer chopped into a stack of bubbles.
|
|
241
|
+
They log it once now.
|
|
242
|
+
- **Stop keeps what was written.** A turn stopped mid-reply used to throw away
|
|
243
|
+
everything you'd watched arrive; it's kept, marked *stopped*.
|
|
244
|
+
- From the moment you send, the thread shows who's on it, for how long, and
|
|
245
|
+
what they're doing ("Codex · shell: npm test · 54s") instead of nothing.
|
|
246
|
+
|
|
247
|
+
### A thread that reads like a document
|
|
248
|
+
|
|
249
|
+
- One reading column; replies have a byline (the agent's mark, name, model and
|
|
250
|
+
time) instead of a bordered bubble; prose is Geist, mono only for code.
|
|
251
|
+
- A run of tool calls folds into one line — *Ran 3 commands, read 2 files* —
|
|
252
|
+
that opens to the list.
|
|
253
|
+
- Every turn ends on a footer: who, how long, which model, what it cost.
|
|
254
|
+
- An orchestrator's plan is a card of tasks, agents and files, not a page of
|
|
255
|
+
JSON. An empty round says "still looking" rather than "didn't parse". A
|
|
256
|
+
task's status updates in its own row instead of adding a line per state.
|
|
257
|
+
- Code blocks are syntax-highlighted with a language label; markdown tables
|
|
258
|
+
render; errors are a card with the provider's raw payload folded away.
|
|
259
|
+
- The thread stays where you are when you've scrolled up to read, with a
|
|
260
|
+
**Latest** pill, instead of yanking you to the bottom on every event.
|
|
261
|
+
- **Load earlier messages** pages the thread backwards (`?before=` on the
|
|
262
|
+
events API); it stopped at the last 60 events before.
|
|
263
|
+
- An empty thread offers something to start with instead of a blank pane.
|
|
264
|
+
|
|
265
|
+
### Picking who and on what
|
|
266
|
+
|
|
267
|
+
- The agent and its model are one control in the composer. The agent picker
|
|
268
|
+
shows each agent's model and permission mode, hides switched-off agents, and
|
|
269
|
+
ticks with a check (it was the info icon).
|
|
270
|
+
- The model picker has descriptions, keyboard navigation, **free models
|
|
271
|
+
first**, and fits the window (it ran off the top with Orchestrate open).
|
|
272
|
+
- **Picking a model agent's model from the list broke the agent.** The list
|
|
273
|
+
leads each id with its provider (`openrouter/google/gemma…`), and that whole
|
|
274
|
+
string went to the provider as the model name. It's split now, on save and
|
|
275
|
+
for configs already saved that way.
|
|
276
|
+
- A model agent with the generic id is named by its model ("gemma-4-31b-it").
|
|
277
|
+
- **Ask several models** is in the composer's More menu: tick models, and
|
|
278
|
+
what's in the box goes to each, a thread apiece. It was CLI-only.
|
|
279
|
+
|
|
280
|
+
### Orchestrate
|
|
281
|
+
|
|
282
|
+
- The cast panel reads as Lead / Team / At once, and each worker chip has two
|
|
283
|
+
zones — include/leave out, and its model and mode — so a click on the chip
|
|
284
|
+
no longer lands on a badge and opens a menu instead.
|
|
285
|
+
- The cast is remembered per project; a reload reset it to "every agent" and
|
|
286
|
+
the next run quietly used agents you'd left out.
|
|
287
|
+
- Each task thread is pinned to its worker: the sidebar says who, and the
|
|
288
|
+
composer in that thread aims at that agent.
|
|
289
|
+
- A finished run ends on a result card with **Apply to your branch**; a run
|
|
290
|
+
that ended before any task finished no longer offers an Apply with nothing
|
|
291
|
+
on its branch.
|
|
292
|
+
- Loom's own `.loom/` state no longer counts as "uncommitted changes" in the
|
|
293
|
+
warning every run showed.
|
|
294
|
+
- Fleet said "no agents running" above a run with workers in flight.
|
|
295
|
+
|
|
296
|
+
### One workspace at every window size
|
|
297
|
+
|
|
298
|
+
- **Narrowing the window swapped in the phone layout** and took the sidebar,
|
|
299
|
+
the tabs and the panels away. On anything with a mouse the workspace now
|
|
300
|
+
stays at every width: the sidebar slims, then slides in behind a toggle; tabs
|
|
301
|
+
go icon-only (each with a tooltip); the right panel steps aside. The single
|
|
302
|
+
column is for touch phones only.
|
|
303
|
+
- Tabs say what they are: **Chat, Orchestra, Agents, Board, Memory, Insights**
|
|
304
|
+
(they were Thread, Fleet, Brain and Observatory), each with a one-line hint.
|
|
305
|
+
- A turn reads as a timeline: the agent's mark, a hairline through its tool
|
|
306
|
+
steps, a node where it ends. Your prompt is the card that opens a turn;
|
|
307
|
+
code blocks have an editor header; file edits are a review card
|
|
308
|
+
("Edited 3 files +12 −4"); a reply can be copied from its byline; the
|
|
309
|
+
composer's border lights up when you're in it and send lights up when
|
|
310
|
+
there's something to send.
|
|
311
|
+
|
|
312
|
+
### And the rest
|
|
313
|
+
|
|
314
|
+
- **Loom on Node older than 22.5 showed every thread empty** — no node:sqlite,
|
|
315
|
+
so it opened a fresh JSONL log beside the real one (a shell that put nvm's
|
|
316
|
+
Node 20 first after a reboot). The CLI now refuses to start on <22.5 and says
|
|
317
|
+
why, and the log refuses to open an empty fallback beside a log.db.
|
|
318
|
+
|
|
319
|
+
- `loom up --restart` left the old daemon alive — its port freed, the process
|
|
320
|
+
and everything it held still running — because shutdown waited on the
|
|
321
|
+
browser's keep-alive and WebSocket connections forever. They're closed now,
|
|
322
|
+
and a stuck shutdown exits after five seconds.
|
|
323
|
+
- The sidebar rebuilt itself every five seconds, dropping hover and focus under
|
|
324
|
+
the pointer; it only redraws when something changed. Older threads fold
|
|
325
|
+
behind "Show N older".
|
|
326
|
+
- The everyday confirmations (abort a run, remove a project, clear the queue,
|
|
327
|
+
update Loom) are an in-app dialog; the browser's own was dismissed unseen by
|
|
328
|
+
some embedders, so Abort simply did nothing.
|
|
329
|
+
- Escape closes any composer menu; the right panel opens by default only on
|
|
330
|
+
wide windows; a reload reopens the project you were in.
|
|
331
|
+
- **Reload mid-reply and the reply is still there.** A thread's first page
|
|
332
|
+
carries what agents are typing in it right now, and stream frames carry their
|
|
333
|
+
offset, so a window opened mid-answer shows the whole reply so far and keeps
|
|
334
|
+
going without repeating or dropping words.
|
|
335
|
+
- When the daemon stops answering, one banner says so and how to start it,
|
|
336
|
+
instead of a "Failed to fetch" toast every few seconds; it clears itself and
|
|
337
|
+
the views refresh when the daemon is back.
|
|
338
|
+
- Terminals stopped stacking a "restored scrollback" divider and prompt for
|
|
339
|
+
every restart nobody typed through.
|
|
340
|
+
|
|
341
|
+
### On the phone
|
|
342
|
+
|
|
343
|
+
- Replies stream in live, over the LAN, the tailnet or Loom Cloud, and render
|
|
344
|
+
as markdown — code blocks, lists, bold, inline code. The orchestrator's plan
|
|
345
|
+
reads as a plan card, not a JSON dump, and the Fleet previews drop the marks.
|
|
346
|
+
- The Pair screen's second button said "Paste link instead" and actually
|
|
347
|
+
submitted the fields. It now says Connect when something's typed (Enter works
|
|
348
|
+
too) and pastes from the clipboard when nothing is.
|
|
349
|
+
- A thread that can't load (the route still settling, the daemon down) says so
|
|
350
|
+
and retries until it does, instead of sitting blank; "Network request failed"
|
|
351
|
+
became a sentence that says what to check.
|
|
352
|
+
|
|
353
|
+
## [1.0.1] — 2026-09-21
|
|
354
|
+
|
|
355
|
+
### Orchestra spawns its workers again
|
|
356
|
+
|
|
357
|
+
- **An orchestrator's plan was being thrown away before it ran.** Every run
|
|
358
|
+
planned tasks and started none: the Fleet sat idle, no task threads appeared,
|
|
359
|
+
and the thread said *"your reply had no ```loom actions block"* directly
|
|
360
|
+
underneath a reply that plainly contained one.
|
|
361
|
+
- The engine parses accumulated turn text, and that text was capped to its
|
|
362
|
+
**last** 20,000 characters. A plan's block sits at the END of a long reply,
|
|
363
|
+
so the cap kept the closing fence and threw away the opening one; the parser
|
|
364
|
+
saw an unterminated block and reported nothing to run. **The better the
|
|
365
|
+
orchestrator planned, the more certainly its plan was binned.** Measured on a
|
|
366
|
+
real run: seven consecutive plans of 6, 5, 5, 5, 5, 8 and 8 tasks, every one
|
|
367
|
+
parseable from the log and every one destroyed.
|
|
368
|
+
- The budget is now far clear of any plausible reply, and when it is reached
|
|
369
|
+
the cut is made at the start of the actions block rather than at a blind
|
|
370
|
+
character offset — so what protects memory can never decapitate the payload.
|
|
371
|
+
|
|
372
|
+
### Answer an agent where it asked
|
|
373
|
+
|
|
374
|
+
- **An agent's question is now a card you can answer in the thread.** It was a
|
|
375
|
+
line of text with nothing to click, which made the one moment Loom exists to
|
|
376
|
+
surface — an agent blocked on a human — the worst affordance in the app.
|
|
377
|
+
- The answer goes to **the agent that asked**, taken from the card. Through the
|
|
378
|
+
composer it went to `state.selected`, which in an orchestra thread is the
|
|
379
|
+
orchestrator — so answering a worker sent the answer to the wrong agent.
|
|
380
|
+
- A question that plainly offers a choice becomes buttons. Anything ambiguous
|
|
381
|
+
falls back to the text box, because a wrong guess puts words in your mouth
|
|
382
|
+
and sends them to an agent.
|
|
383
|
+
|
|
384
|
+
### A giant icon no longer eats the Board
|
|
385
|
+
|
|
386
|
+
- Board → GitHub rendered a single enormous ⓘ filling the pane whenever `gh`
|
|
387
|
+
couldn't list pull requests, pushing the columns out of view.
|
|
388
|
+
- `svg()` emitted no width or height, so an icon in a container that forgot its
|
|
389
|
+
CSS rule stretched to fill the line — and `.bnote` was such a container.
|
|
390
|
+
Icons now carry an intrinsic size, so a **future** icon dropped somewhere
|
|
391
|
+
unstyled looks slightly wrong instead of covering the screen.
|
|
392
|
+
|
|
393
|
+
## [1.0.0] — 2026-09-21
|
|
394
|
+
|
|
395
|
+
### Rewind — put the work back the way it was
|
|
396
|
+
|
|
397
|
+
- **A checkpoint before every turn**, and **Rewind** on the Update card of the
|
|
398
|
+
turn it undoes. There is a **Rewind…** list under More for all of them, and
|
|
399
|
+
`loom rewind` in the terminal. The honest answer to *undo that* used to be
|
|
400
|
+
"read the diff and retype it": `turn_diff` looks like the answer and isn't,
|
|
401
|
+
since its patch is truncated at 12KB and an untracked file appears in it as
|
|
402
|
+
a line with no content.
|
|
403
|
+
- **It restores files, not history.** HEAD does not move, your commits are not
|
|
404
|
+
touched, and the thread is not edited. What happened still happened.
|
|
405
|
+
- **`.gitignore` applies at capture**, so `node_modules`, build output, `.env`
|
|
406
|
+
and `.loom/` are never read, never stored and never restored — which is why
|
|
407
|
+
a rewind cannot eat the event log.
|
|
408
|
+
- **A rewind is itself rewindable.** It saves what it is about to replace
|
|
409
|
+
before replacing it, and hands that back: *Undo the rewind* in the thread,
|
|
410
|
+
an id on the command line.
|
|
411
|
+
- **Refused while an agent is mid-turn**, naming who. Replacing the tree under
|
|
412
|
+
a running agent puts the damage in whatever it writes next.
|
|
413
|
+
- Checkpoints live on hidden refs under `refs/loom/checkpoints/`, so no branch
|
|
414
|
+
listing, no stash and no `git log` shows one. The newest 60 are kept.
|
|
415
|
+
|
|
416
|
+
### A transcript you can turn up
|
|
417
|
+
|
|
418
|
+
- **Normal / Thinking / Verbose**, under More. Normal drops reasoning entirely
|
|
419
|
+
rather than folding it — the working out is not the transcript. Verbose
|
|
420
|
+
opens it and attaches the raw payload to tool calls and to the
|
|
421
|
+
orchestrator's brief, the two places a summary stands in for something
|
|
422
|
+
bigger. Remembered per project.
|
|
423
|
+
- **A long block is readable to the end.** Code wraps instead of scrolling out
|
|
424
|
+
of the bubble, and every block — markdown or raw — carries a copy button.
|
|
425
|
+
The orchestrator's `{"actions": …}` plan used to be clipped in a scroll box
|
|
426
|
+
with no way to reach the closing brace.
|
|
427
|
+
|
|
428
|
+
### Orchestrating from Main stays in Main
|
|
429
|
+
|
|
430
|
+
- **The orchestrator answers in the thread you asked in.** It used to open one
|
|
431
|
+
of its own and walk you into it, leaving the goal you typed behind in a
|
|
432
|
+
thread that then said nothing about it.
|
|
433
|
+
- **Each task row opens that task's thread.** They were findable only by
|
|
434
|
+
hunting the sidebar for a title you half remembered.
|
|
435
|
+
- **A borrowed thread is given back.** Replying in a run's own thread steers
|
|
436
|
+
the run for good; a thread the run borrowed stops answering for it once the
|
|
437
|
+
run is terminal, so Main goes back to being Main rather than forwarding
|
|
438
|
+
every later message to a finished goal.
|
|
439
|
+
|
|
440
|
+
### A thread says who's working in it, and whether it's still going
|
|
441
|
+
|
|
442
|
+
- **A task thread is labelled with the agent doing that task**, not with
|
|
443
|
+
whoever holds the project baton. During a run with four workers every task
|
|
444
|
+
thread used to claim to be the baton holder, so a Codex task and an
|
|
445
|
+
Antigravity task looked identical. A thread pinned to an agent answers the
|
|
446
|
+
same way.
|
|
447
|
+
- **Thread rows show status** — a live pulse while a task runs, green when it
|
|
448
|
+
finishes, red when it fails, amber when it's waiting. Every answer comes
|
|
449
|
+
from a status the daemon already reports; a thread it can't know about gets
|
|
450
|
+
no mark rather than a guess.
|
|
451
|
+
- The status payload now carries one row per task (thread, agent, kind,
|
|
452
|
+
state) instead of only a count, which is what made both of those possible.
|
|
453
|
+
|
|
454
|
+
### Models in Orchestrate
|
|
455
|
+
|
|
456
|
+
- **Every agent chip carries its model**, orchestrator and workers alike,
|
|
457
|
+
beside its permission chip. Orchestrate could pick who ran a goal and never
|
|
458
|
+
what they ran it on: the model picker was bound to the one agent the chat
|
|
459
|
+
composer is aimed at, which in Orchestrate is nobody.
|
|
460
|
+
- **A dashed “+ model” chip** adds an API model to the cast and opens its
|
|
461
|
+
picker straight away. Until a model is picked the chip warns, and Orchestrate
|
|
462
|
+
refuses to start — naming the agent — rather than failing three tasks in.
|
|
463
|
+
- The model list's provider-asked source had no case in its footer, so it
|
|
464
|
+
printed *“no model list for this agent”* directly above 446 of them. And it
|
|
465
|
+
no longer offers a model agent a **Default**, which for that kind is the one
|
|
466
|
+
option that cannot work.
|
|
467
|
+
|
|
468
|
+
## [0.2.6] — 2026-09-20
|
|
469
|
+
|
|
470
|
+
### Right-click, and a composer that fits
|
|
471
|
+
|
|
472
|
+
- **Right-click a project** for everything you can do to it: open, new chat,
|
|
473
|
+
settings, **rename**, copy path, and **Remove from Loom**. Those were
|
|
474
|
+
scattered — settings behind a gear, rename nowhere at all, and removing a
|
|
475
|
+
project only in `loom projects --forget`.
|
|
476
|
+
- It says **Remove from Loom**, not Delete, and the confirm says the folder,
|
|
477
|
+
the history and `.loom/` stay on disk. A menu item that says delete and
|
|
478
|
+
doesn't is as bad as one that says delete and does.
|
|
479
|
+
- **Right-click a thread** to open it, rename it, pin which agent answers in
|
|
480
|
+
it, or forget it. The main thread offers only what applies to it, rather
|
|
481
|
+
than offering everything and refusing half.
|
|
482
|
+
- Escape and any click outside close the menu, arrow keys walk it, and it
|
|
483
|
+
flips rather than hanging off the edge of the window.
|
|
484
|
+
- **MCPs and Skills moved into a More menu** in the composer, which also
|
|
485
|
+
reaches Prompts and Attach. That row was holding the model, the agent, a
|
|
486
|
+
permission chip, five buttons and send, and wrapped on a narrow window. The
|
|
487
|
+
count badge stayed outside — *two skills are on* is the part you need
|
|
488
|
+
without opening anything.
|
|
489
|
+
|
|
490
|
+
### Setup tells the truth about this machine
|
|
491
|
+
|
|
492
|
+
- **Claude Code showed "not installed" on machines where it is.** Anthropic's
|
|
493
|
+
installer puts the binary in `~/.local/bin`, and Loom trusted `PATH` alone —
|
|
494
|
+
which an interactive shell has and a detached daemon may not. Codex, Grok
|
|
495
|
+
and Antigravity already looked in their known locations; claude-code was the
|
|
496
|
+
one still guessing. Telling someone to reinstall software they already have
|
|
497
|
+
is the worst kind of wrong answer.
|
|
498
|
+
- **The Model (API) row said "couldn't confirm it's signed in" and printed a
|
|
499
|
+
blank command.** There is nothing to sign in to: a model agent is usable
|
|
500
|
+
when a provider has a key, so it says which providers are configured, and
|
|
501
|
+
when none is, it gives you `loom providers:set …` instead of an empty box.
|
|
502
|
+
- **The desktop app says Loom in the menu bar**, in a dev run as well as a
|
|
503
|
+
packaged one. macOS takes that title from the running bundle, not from
|
|
504
|
+
`app.setName()` — which is why setting the name in code never fixed it. The
|
|
505
|
+
`.app` filename is unchanged, so the Homebrew cask keeps working.
|
|
506
|
+
|
|
507
|
+
## [0.2.5] — 2026-09-20
|
|
508
|
+
|
|
509
|
+
### Install it with Homebrew
|
|
510
|
+
|
|
511
|
+
- **`brew tap nickthelegend/loom https://github.com/nickthelegend/loom` then
|
|
512
|
+
`brew install --cask loom-desktop`** — and `brew upgrade --cask` after that,
|
|
513
|
+
which is a real update path for the macOS build that can't replace itself.
|
|
514
|
+
The cask carries both architectures and is regenerated from the published
|
|
515
|
+
checksums by the release job, so it can't go stale.
|
|
516
|
+
- **Check for Updates… recognises a Homebrew install** and hands you the
|
|
517
|
+
`brew upgrade` command instead of downloading over a copy brew is tracking —
|
|
518
|
+
the same rule the Linux `.deb` already had. **Run it in Loom** runs it in
|
|
519
|
+
Loom's own terminal, the one in the dock, so you watch the upgrade rather
|
|
520
|
+
than having it happen invisibly inside the app.
|
|
521
|
+
- It does **not** get you past Gatekeeper, and doesn't pretend to: brew
|
|
522
|
+
quarantines cask downloads on purpose. First launch asks once, as it does
|
|
523
|
+
for a dmg you downloaded yourself. The cask deliberately does not strip the
|
|
524
|
+
quarantine attribute — that protection exists *because* the app is unsigned.
|
|
525
|
+
|
|
526
|
+
### A model agent can write, when you say so
|
|
527
|
+
|
|
528
|
+
- **`--write`** gives a model agent `write_file`, and **`--run "npm test"`**
|
|
529
|
+
gives it `run` with an allow-list. Every write and every command is a card
|
|
530
|
+
in the thread that you allow or deny before anything happens — the same
|
|
531
|
+
approval surface Claude Code's "always ask" already uses, now asked from
|
|
532
|
+
inside the daemon instead of over MCP.
|
|
533
|
+
- **It asks in `auto` too**, which is stricter than the CLI mapping and
|
|
534
|
+
deliberately so: a CLI in your roster is one you installed and signed into,
|
|
535
|
+
and a model agent is a name you picked off a provider's list an hour ago.
|
|
536
|
+
`bypass` is the only mode that doesn't ask, because that is what bypass
|
|
537
|
+
means.
|
|
538
|
+
- **The card says what would happen** — *write src/app.ts: 40 lines
|
|
539
|
+
(replacing 12) — fix the port* — rather than dumping the whole new file at
|
|
540
|
+
you.
|
|
541
|
+
- The allow-list is a prefix match on whole commands and there is no shell, so
|
|
542
|
+
`npm test` permits `npm test --watch` and refuses `npm testify`,
|
|
543
|
+
`npm test; rm -rf ~`, backticks, pipes and redirection. A tool is only
|
|
544
|
+
offered when it can be used: no allow-list, no `run` tool at all.
|
|
545
|
+
- With nobody to ask — no daemon, a script, a test — the answer is **no**. A
|
|
546
|
+
tool that proceeded because the UI wasn't wired up would be the worst
|
|
547
|
+
failure this could have.
|
|
548
|
+
|
|
549
|
+
### A model agent can read the project
|
|
550
|
+
|
|
551
|
+
- **`"tools": true`** on a `model` agent gives it `read_file`, `list_files`
|
|
552
|
+
and `search` — read-only, inside the project, `.git` and `.loom` excluded,
|
|
553
|
+
every path resolved and proven contained before anything opens it. A
|
|
554
|
+
reviewer that can't read the file it's reviewing is being asked to guess.
|
|
555
|
+
- Each call appears in the thread as a `tool_call`, like any other agent's
|
|
556
|
+
work, and the loop is bounded: after eight hops the model is told to answer
|
|
557
|
+
with what it has rather than being cut off mid-thought.
|
|
558
|
+
- **No writing and no shell.** A model that can write files is exactly as
|
|
559
|
+
dangerous as a CLI that can, and the right answer is the permission layer
|
|
560
|
+
Loom already has. That's the rest of #87, and it stays open for it — a free
|
|
561
|
+
model editing a repository because it felt like it is not a thing to ship
|
|
562
|
+
quietly.
|
|
563
|
+
|
|
564
|
+
### Ask several models at once
|
|
565
|
+
|
|
566
|
+
- **`loom ask --models a,b,c "…"`** sends one prompt to several models at the
|
|
567
|
+
same time, each in its own thread named after the model, with the project's
|
|
568
|
+
memory brief attached. `--free` picks the free ones the providers have right
|
|
569
|
+
now, up to `--limit`. With free quota, asking five models costs what asking
|
|
570
|
+
one costs — and *which of these is right* is a judgement a person makes in
|
|
571
|
+
ten seconds and a model makes badly.
|
|
572
|
+
- The agents that run are **transient**: five asks don't leave five agents in
|
|
573
|
+
your roster. The threads stay, because those are the part worth keeping.
|
|
574
|
+
- One model failing doesn't take the others down — its thread carries the
|
|
575
|
+
error, the rest carry their answers.
|
|
576
|
+
|
|
577
|
+
### A thread remembers who answers in it
|
|
578
|
+
|
|
579
|
+
- **Pin an agent to a thread** and it answers there, whoever holds the baton —
|
|
580
|
+
so two threads can be talking to two agents at once, which is the point of
|
|
581
|
+
having agents that cost nothing. Picking an agent for a new chat now pins it
|
|
582
|
+
instead of handing over the baton, and the sidebar shows which thread is
|
|
583
|
+
with whom.
|
|
584
|
+
- **It doesn't take the baton.** The baton is the write lock for work on the
|
|
585
|
+
repository; a conversation doesn't need it, and taking it would stop
|
|
586
|
+
whatever is actually working.
|
|
587
|
+
- A thread can pin a **model** too, for agents that can change model per turn
|
|
588
|
+
(the `model` kind). Binding one to an agent that bakes its model into a
|
|
589
|
+
spawned process is refused where you can fix it, rather than ignored where
|
|
590
|
+
you'd never notice. The main thread still follows the baton — that's what
|
|
591
|
+
makes it the main thread — and a thread whose agent has left the roster
|
|
592
|
+
falls back to the baton rather than becoming one nobody can type in.
|
|
593
|
+
|
|
594
|
+
### Agents that are models, not CLIs
|
|
595
|
+
|
|
596
|
+
- **A new agent kind, `model`.** It's an HTTP endpoint rather than a
|
|
597
|
+
subprocess: OpenAI-compatible chat completions, streamed into the thread as
|
|
598
|
+
they arrive, with reasoning shown as reasoning and token counts on
|
|
599
|
+
`run_complete`. Nothing to install. It has no tools yet, so what it's for is
|
|
600
|
+
the thinking work — planning, reviewing, summarising, answering — which is
|
|
601
|
+
most of what a fleet does between edits.
|
|
602
|
+
- **Providers, and keys where keys belong.** OpenRouter, AgentRouter, OpenAI,
|
|
603
|
+
Groq and a local Ollama are known by name; anything else speaking
|
|
604
|
+
`/v1/chat/completions` works with a base URL. The project config names a
|
|
605
|
+
*provider*, never a secret: keys live in the environment or in
|
|
606
|
+
`~/.loom/providers.json` (0600), and no route, log or listing ever returns
|
|
607
|
+
one — you get the last four characters. `loom providers`,
|
|
608
|
+
`loom providers:set <id> --key …`.
|
|
609
|
+
- **`loom models`** asks each configured provider what it can run right now,
|
|
610
|
+
cached for ten minutes and refreshable, with the free ones marked. A model
|
|
611
|
+
list that's typed out by hand goes stale; one that's asked for doesn't.
|
|
612
|
+
- **Fallbacks that read the answer.** A dry free pool (402) or a rate limit
|
|
613
|
+
(429) moves to the next model in `fallbacks` and says so once in the thread;
|
|
614
|
+
a wrong or retired model name (503) stops, because trying something else
|
|
615
|
+
would hide the typo rather than fix it. Each refusal is explained in its own
|
|
616
|
+
terms — a rejected *client* is not a rejected key.
|
|
617
|
+
|
|
618
|
+
### Retrieval that can follow a synonym
|
|
619
|
+
|
|
620
|
+
- **A semantic channel for the brain** (`brain.semantic`, or Settings →
|
|
621
|
+
Brain): all-MiniLM-L6-v2 embeddings unioned with the three lexical channels,
|
|
622
|
+
so *how does login work* reaches a note about Supabase JWKS. Measured on the
|
|
623
|
+
recall set that shipped with 0.2.4: recall@5 **0.50 → 1.00** on synonyms,
|
|
624
|
+
**0.80 → 1.00** across word forms, unchanged at **1.00** on literal queries.
|
|
625
|
+
Warm cost is about a millisecond a query.
|
|
626
|
+
- It is **not** a dependency and never will be: the model is 23MB but its ONNX
|
|
627
|
+
runtime is ~470MB, so Loom asks you to install that yourself
|
|
628
|
+
(`npm i -g @huggingface/transformers`) and behaves exactly as before when
|
|
629
|
+
it's missing — a channel that can be absent must fail into the old answer,
|
|
630
|
+
not into an error. Vectors are cached per memory hash beside the brain, and
|
|
631
|
+
the model stays offline after its first download.
|
|
632
|
+
|
|
633
|
+
### macOS gets the update too
|
|
634
|
+
|
|
635
|
+
- **Help → Check for Updates… on macOS** now finds the build for your
|
|
636
|
+
architecture, downloads it, and **verifies its SHA-256** against the
|
|
637
|
+
checksums the release publishes — a file that doesn't match is deleted
|
|
638
|
+
rather than opened — then opens the disk image for you to drag across. The
|
|
639
|
+
drag stays yours: macOS only lets an app signed by a certificate it trusts
|
|
640
|
+
replace itself, and this build is ad-hoc signed. The checking is the part
|
|
641
|
+
worth having; the checksums have shipped since 0.2.0 and nobody compares
|
|
642
|
+
them by hand.
|
|
643
|
+
|
|
644
|
+
## [0.2.4] — 2026-09-20
|
|
645
|
+
|
|
646
|
+
### The desktop app can update itself, where that's honest
|
|
647
|
+
|
|
648
|
+
- **Help → Check for Updates…** on **Windows** and the **Linux AppImage**:
|
|
649
|
+
it asks before downloading, installs on quit, and restarts only when you
|
|
650
|
+
press Restart. On **macOS** it refuses and says why — the build is ad-hoc
|
|
651
|
+
signed, and macOS only replaces an app signed by a certificate it already
|
|
652
|
+
trusts — and opens the releases page instead. A `.deb` install is left to
|
|
653
|
+
the package manager that owns it. The release now publishes the update feed
|
|
654
|
+
for those two platforms and deliberately not for macOS: advertising an
|
|
655
|
+
update the OS will refuse to install is worse than advertising none.
|
|
656
|
+
|
|
657
|
+
### Dark mode in the preview
|
|
658
|
+
|
|
659
|
+
- **Auto / light / dark for the previewed page**, independent of Loom's own
|
|
660
|
+
theme and remembered per project, alongside the widths. It re-points the
|
|
661
|
+
page's own `prefers-color-scheme` rules through the proxy bridge and makes
|
|
662
|
+
`matchMedia` answer to match, so both CSS-themed and JS-themed apps follow;
|
|
663
|
+
Auto restores every rule exactly as it shipped. Where Loom can't reach the
|
|
664
|
+
page it says so rather than offering a switch that does nothing, and the
|
|
665
|
+
screenshot — a real browser — honours the scheme either way.
|
|
666
|
+
- The width you chose is now actually **read back**: it was being saved and
|
|
667
|
+
never restored, which is the same as not remembering it.
|
|
668
|
+
- A screenshot and an element pick both carry the conditions they were taken
|
|
669
|
+
under (*375×812, dark mode*) into the prompt.
|
|
670
|
+
|
|
671
|
+
### The baton can carry the work
|
|
672
|
+
|
|
673
|
+
- **`git.mergeOnHandoff`** (with `worktreePerAgent`): handing the baton from A
|
|
674
|
+
to B merges `agent/A` into B's checkout, so the next agent starts from what
|
|
675
|
+
the last one finished. It refuses rather than guesses — uncommitted work on
|
|
676
|
+
either side, or a merge already in progress, is reported instead of merged —
|
|
677
|
+
and a conflict is left in the tree with the files named, in the briefing and
|
|
678
|
+
in the handoff event. A conflict stops a route instead of prompting an agent
|
|
679
|
+
on top of conflict markers.
|
|
680
|
+
|
|
681
|
+
### Routes: steps that decide whether to run
|
|
682
|
+
|
|
683
|
+
- **A route step can carry a condition on the previous turn** and is skipped
|
|
684
|
+
when it doesn't hold: `loom route 'planner,executor,reviewer?lines>200' "…"`,
|
|
685
|
+
or `{ "step": "reviewer", "when": "lines>200" }` in the project config.
|
|
686
|
+
Conditions read the turn's own diff — `changed>N`, `changed<N`, `lines>N`,
|
|
687
|
+
`lines<N`, `touched:<glob>`, `!touched:<glob>` — and nothing else: a
|
|
688
|
+
condition must be something Loom measured, never a judgement about the work.
|
|
689
|
+
Unreadable ones are refused when the route is defined rather than quietly
|
|
690
|
+
never matching, the first step can't be conditional (no turn to measure),
|
|
691
|
+
and a skipped step appears in the thread with the numbers that decided it.
|
|
692
|
+
Skipping isn't failing — the route completes.
|
|
693
|
+
|
|
694
|
+
### A pull request you asked for
|
|
695
|
+
|
|
696
|
+
- **Open PR on a review card.** With branch-per-card on, a card in review shows
|
|
697
|
+
what a PR would carry — the branch, its commits, its files and the exact
|
|
698
|
+
command — and opens it only when you press the button. Asking pushes
|
|
699
|
+
nothing; publishing has never been implicit and still isn't.
|
|
700
|
+
|
|
701
|
+
### Coming back to a project
|
|
702
|
+
|
|
703
|
+
- **A digest of what happened while you were away** — goals, landings,
|
|
704
|
+
failures, a dead server, and what's waiting on you, newest first and each
|
|
705
|
+
line clicking through to the moment it came from. Shown when you've actually
|
|
706
|
+
been away; `loom digest --since 8` in the terminal.
|
|
707
|
+
- **The desktop app's notifications carry the question itself**, not "an agent
|
|
708
|
+
needs you", and on macOS you can answer from the notification. Nothing fires
|
|
709
|
+
while you're looking at that conversation.
|
|
710
|
+
|
|
711
|
+
### Goals: a spend cap, and more than one at a time
|
|
712
|
+
|
|
713
|
+
- **`loom orchestrate --max-usd 2`** stops a goal at its cap the way the team
|
|
714
|
+
budget always did — running work finishes, nothing new starts, and the goal
|
|
715
|
+
waits for you. A project default lives in `budgets.perGoalUsd`, and a word
|
|
716
|
+
lands in the thread at 80% rather than the stop being the first you hear.
|
|
717
|
+
- **`maxConcurrentGoals`** lets disjoint goals run side by side. A goal's
|
|
718
|
+
scope is the `touches` its plan declares; one that hasn't said what it
|
|
719
|
+
touches counts as everything and waits. Overlaps queue with the collision
|
|
720
|
+
named. Still one at a time unless you ask for more.
|
|
721
|
+
|
|
722
|
+
### The queue can wait, and can be kept
|
|
723
|
+
|
|
724
|
+
- **Queue for later**: a prompt can be held for a time, until a goal actually
|
|
725
|
+
**lands**, until that goal's **checks go green**, or until the project has
|
|
726
|
+
been quiet for a while. The row says what it's waiting for and a click
|
|
727
|
+
releases it. (`loom queue at 03:00 …`, `loom queue after green:<runId> …`)
|
|
728
|
+
- **Recipes**: save what's queued and run it on any project. Steps remember
|
|
729
|
+
their target by **role**, so a recipe travels; anything the project can't
|
|
730
|
+
resolve becomes an Auto prompt rather than a refusal. (`loom queue save`,
|
|
731
|
+
`loom queue run`, `loom queue recipes`)
|
|
732
|
+
- **Drag to reorder** in the web app; the arrows stay for anyone who can't
|
|
733
|
+
drag, and the phone gained up/down controls it never had.
|
|
734
|
+
|
|
735
|
+
## [0.2.3] — 2026-09-20
|
|
736
|
+
|
|
737
|
+
### The Browser tab knows your dev servers
|
|
738
|
+
|
|
739
|
+
- **Loom can run them.** Declared under `servers` in `.loom/config.json` and
|
|
740
|
+
suggested from `package.json` (a suggestion you accept, never something that
|
|
741
|
+
starts itself). Start, stop and restart from the Browser tab or the CLI.
|
|
742
|
+
- **The state is a fact, not a guess**: *running* means a port answered,
|
|
743
|
+
*starting* means the process is up and nothing is listening yet, and
|
|
744
|
+
*crashed* carries the exit code.
|
|
745
|
+
- **Its output is there too**, under the page it serves — which is where the
|
|
746
|
+
reason for a blank frame usually is — and a server that dies while an agent
|
|
747
|
+
is working against it says so in the thread.
|
|
748
|
+
- Clicking a server points the preview at it; no more typing ports from memory.
|
|
749
|
+
- Every server Loom started is stopped when the daemon goes, so nothing is left
|
|
750
|
+
holding a port.
|
|
751
|
+
- `loom servers`, `loom servers start|stop|restart <name>`, `loom servers logs
|
|
752
|
+
<name>`.
|
|
753
|
+
- **The preview emulates a width** — Fit / 375 / 768 / 1280, remembered per
|
|
754
|
+
project and scaled down when the dock is narrower than the page.
|
|
755
|
+
- **It reloads when an agent changes files** (a toggle, on by default), so
|
|
756
|
+
watching a change land doesn't involve remembering to refresh.
|
|
757
|
+
- **The previewed page reports back.** Loom previews a server through its own
|
|
758
|
+
proxy — forwarding everything, including the hot-reload socket — and injects
|
|
759
|
+
one script. The **Page** pane shows the page's console and its requests,
|
|
760
|
+
live, and a click puts a line into the composer as context.
|
|
761
|
+
- **An element picker.** Click the thing that's wrong in the preview and its
|
|
762
|
+
selector, text, box and markup land in the composer.
|
|
763
|
+
- **A camera button puts the view into the composer.** The page is another
|
|
764
|
+
origin and can't photograph itself, so the daemon takes the shot with the
|
|
765
|
+
project's own Playwright; a project without it is told exactly that.
|
|
766
|
+
|
|
767
|
+
### An Update button that actually updates
|
|
768
|
+
|
|
769
|
+
- **Loom says when a newer version is out** — checked against the published
|
|
770
|
+
release, cached for hours, shown as a pill in the status bar.
|
|
771
|
+
- **One button in Settings → Updates**, which says exactly what it will run
|
|
772
|
+
before it runs it: a git checkout pulls and rebuilds, a global npm install
|
|
773
|
+
reinstalls itself, and anything else is pointed at the release rather than
|
|
774
|
+
having a command guessed at and run over it.
|
|
775
|
+
- **A checkout with uncommitted changes is refused** (modified tracked files —
|
|
776
|
+
an untracked scratch file doesn't stop a fast-forward).
|
|
777
|
+
- The commands' output streams while they run, then the daemon restarts on the
|
|
778
|
+
new build and the page reconnects to it.
|
|
779
|
+
- `loom update [--check] [--yes]` does the same from the terminal.
|
|
780
|
+
|
|
781
|
+
## [0.2.1] — 2026-09-20
|
|
782
|
+
|
|
783
|
+
### The prompt queue
|
|
784
|
+
|
|
785
|
+
- **What you type while something is running now waits where you can see it.** A
|
|
786
|
+
prompt sent to a busy agent used to be queued invisibly — logged into the
|
|
787
|
+
thread as if it had been sent, with nothing to look at and nothing to change —
|
|
788
|
+
and a goal typed while another goal ran was refused outright.
|
|
789
|
+
- **The queue is a surface**, above the composer on the web, the desktop app and
|
|
790
|
+
the phone, and `loom queue` in the terminal: every waiting prompt with who
|
|
791
|
+
takes it, editable in place, movable, removable, pausable.
|
|
792
|
+
- **Each prompt names its own target:** one agent, the **orchestrator** (a whole
|
|
793
|
+
new goal, started when the running one finishes), or the auto router — and you
|
|
794
|
+
can change it while the prompt waits. The baton moves with it.
|
|
795
|
+
- **A question holds the queue:** an agent that stops to ask you something isn't
|
|
796
|
+
answered by whatever you queued behind it. The queue waits and names who
|
|
797
|
+
asked; your typed reply goes straight out and lifts the hold, so what was
|
|
798
|
+
waiting runs next. A pause you set yourself stays until you resume it.
|
|
799
|
+
- **Stop pauses the queue** instead of emptying it, and a prompt that can't be
|
|
800
|
+
sent (budget, quarantine, a policy, a missing agent) stays put with the reason
|
|
801
|
+
on the queue rather than disappearing.
|
|
802
|
+
- **A prompt enters the conversation when it is sent**, not when it is queued, so
|
|
803
|
+
the thread stays an honest record of what each agent was actually asked.
|
|
804
|
+
- Survives a daemon restart, reloaded **paused** (`.loom/queue.json`). API:
|
|
805
|
+
`GET/POST /api/projects/:id/queue`, `PATCH|DELETE .../queue/:itemId`,
|
|
806
|
+
`POST .../queue/pause`, `DELETE .../queue`, plus a live `queue` socket frame.
|
|
807
|
+
|
|
808
|
+
### Fixed
|
|
809
|
+
|
|
810
|
+
- **An override of `loom/review` made while that review is still running** is no
|
|
811
|
+
longer overwritten when the review finishes (D61) — found on a real PR, where
|
|
812
|
+
a late review wrote `failure` over the owner's decision.
|
|
813
|
+
- **A turn is no longer blamed for Loom's own `.loom/` files.** In a project that
|
|
814
|
+
doesn't gitignore `.loom/`, the event log and the queue file were landing in
|
|
815
|
+
the agent's turn diff.
|
|
816
|
+
- **grok-code and agy** are detected from their own auth files instead of being
|
|
817
|
+
reported signed-out, and the status bar says **live**, not offline, before a
|
|
818
|
+
project is open.
|
|
819
|
+
- **A reply that lands after its window closed** no longer throws from a redraw.
|
|
820
|
+
- **An overridden review leaves its status where the owner put it.** A review
|
|
821
|
+
coming back to a commit that was already overridden kept the decision but
|
|
822
|
+
posted nothing, leaving its own "reviewing…" status pending — on a repo that
|
|
823
|
+
requires `loom/review`, for ever.
|
|
824
|
+
- **Retargeting a queued prompt** to an agent the project doesn't have is
|
|
825
|
+
refused when you make the change, not when the queue reaches it.
|
|
826
|
+
|
|
827
|
+
### Loom Teams, Phase 6: land in turn, hear it now
|
|
828
|
+
|
|
829
|
+
- **6 more design decisions** (D79–D84), settled with the owner.
|
|
830
|
+
- **The landing train:** on a repo with no merge queue, Land queues a goal in its
|
|
831
|
+
lanes and Loom merges it on its turn: fresh main in, the fast tests, a push,
|
|
832
|
+
green checks on that commit, then `gh pr merge --squash`. The slot is a team-hub
|
|
833
|
+
lease on `.loom/landing/<lane>` held as a hard zone, so every hub (in-memory,
|
|
834
|
+
`loom hub`, hosted SQL) arbitrates it with no new API. Waiting goals show
|
|
835
|
+
**queued** and go as soon as the lane frees. A red check on its turn hands the
|
|
836
|
+
lane on, and the goal rejoins when it's green. Repos with a merge queue land as
|
|
837
|
+
before.
|
|
838
|
+
- **Lanes:** `landing.lanes` in `loom.team.json` are path scopes; goals in
|
|
839
|
+
different lanes land at the same time.
|
|
840
|
+
- **GitHub webhooks into the hub, no App needed:** `loom hub` takes
|
|
841
|
+
`POST /github/webhook/:teamId`, checks `X-Hub-Signature-256` against the team's
|
|
842
|
+
secret, and posts PR, check, review and deploy events to the feed.
|
|
843
|
+
`loom team webhook [--install] [--rotate]` sets it up. The hosted hub gets a
|
|
844
|
+
`github-webhook` Edge Function and `supabase/migrations/0009_phase6.sql`
|
|
845
|
+
(deployment pending).
|
|
846
|
+
- **Polling and webhooks agree:** one mapping (`src/core/github-events.ts`) gives
|
|
847
|
+
both the same dedupe keys, so a fact is posted once. Failing checks are now one
|
|
848
|
+
feed event per check and commit, and `gh pr list` polling reads `headRefOid`.
|
|
849
|
+
- **Faster fixes:** a check result or merge for one of your goal PRs makes your
|
|
850
|
+
daemon look at it immediately, not at the next 30-second poll.
|
|
851
|
+
- **UI:** a `queued` landing chip (desktop and phone), and feed lines for the
|
|
852
|
+
train and for reviews that arrive by webhook.
|
|
853
|
+
|
|
854
|
+
## [0.2.0] — 2026-09-19
|
|
855
|
+
|
|
856
|
+
Loom Teams: five phases, 78 design decisions, several people and their agents on
|
|
857
|
+
one repo. The sections below go phase by phase, newest first.
|
|
858
|
+
|
|
859
|
+
### Loom Teams: loose ends closed
|
|
860
|
+
|
|
861
|
+
- **Paste-the-URL sign-in** for the hosted hub on machines without a browser
|
|
862
|
+
(`loom team signin --paste`; `loom runner join` uses it by default).
|
|
863
|
+
- **Stacks fold when a lower PR keeps failing:** its lower PRs close and the top
|
|
864
|
+
one retargets the base branch, so the normal fix loop can fix it.
|
|
865
|
+
- **CI minutes per goal:** Actions time on a goal's branch is recorded when it
|
|
866
|
+
lands and shown in team spend (desktop and phone).
|
|
867
|
+
- **Docs:** a Teams user guide ([docs/teams.md](docs/teams.md)), a docs index,
|
|
868
|
+
a source map, and READMEs for `supabase/`, `src/hub/`, `test/`, `scripts/`
|
|
869
|
+
and `hardware/`.
|
|
870
|
+
|
|
871
|
+
### Loom Teams, Phase 5: runners
|
|
872
|
+
|
|
873
|
+
- **12 more design decisions** (D67–D78), settled in a design interview.
|
|
874
|
+
- **Runners:** a member's always-on Loom daemon that takes goals from the team
|
|
875
|
+
hub. Pair it with `loom runner pair` / `join` (a device of the same member,
|
|
876
|
+
revocable with key rotation); keep it up with `loom runner install` or
|
|
877
|
+
`Dockerfile.runner`.
|
|
878
|
+
- **Jobs on the hub:** start / continue / fix / return / land, with atomic claims,
|
|
879
|
+
heartbeats carrying a progress snapshot, and stale reclaim (MemoryHub, `loom
|
|
880
|
+
hub`, SQL `0007_runners.sql`).
|
|
881
|
+
- **Move a running goal** to a runner at a safe point (branches pushed to
|
|
882
|
+
`refs/loom/run/<id>/*`, the orchestrator resumes there) and **bring it back**;
|
|
883
|
+
Land reaches goals that live on a runner.
|
|
884
|
+
- **Trust tier:** a fresh clone (or container) per goal, a scrubbed environment,
|
|
885
|
+
`runners.permissions` as the ceiling, shared runners only when
|
|
886
|
+
`runners.shared` allows, a fine-grained token checked by `loom runner doctor`.
|
|
887
|
+
- **Away-from-keyboard CI fixes:** the owner's runner takes a goal that needs
|
|
888
|
+
someone once the owner has been offline 15 minutes.
|
|
889
|
+
- **Deploys and release notes:** deployment results in the feed, a phone alert
|
|
890
|
+
when a failed deploy contains your goal, `loom team release-notes --since`.
|
|
891
|
+
- **Phone:** tapping an alert opens its project and goal.
|
|
892
|
+
|
|
893
|
+
### Loom Teams, Phase 4: land safely
|
|
894
|
+
|
|
895
|
+
- **15 more design decisions** (D52–D66), settled in a design interview.
|
|
896
|
+
- **Checks come back to the owner:** the owner's daemon polls its goal PRs every
|
|
897
|
+
30s (`gh pr checks --required`). A failing check is rerun once; a pass on rerun
|
|
898
|
+
is labelled `loom:flaky`. A real failure's log tail (fenced as untrusted) reopens
|
|
899
|
+
the goal, and the fix is pushed to the same PR. At most 2 attempts, then the
|
|
900
|
+
goal needs a human and the phone is alerted.
|
|
901
|
+
- **Cross-vendor review:** a different-vendor agent reviews each goal PR on open
|
|
902
|
+
and after each fix (max 3), posts a COMMENT review and a `loom/review` status;
|
|
903
|
+
only high findings fail it, and they share the fix budget. Owners can override
|
|
904
|
+
with a reason.
|
|
905
|
+
- **Land** (`loom land`, a button on desktop and phone): fresh main merged in,
|
|
906
|
+
`landing.fastTest` from `loom.team.json`, push, `gh pr merge --auto --squash`.
|
|
907
|
+
Conflicts get one agent attempt; hard-zone conflicts go to a human.
|
|
908
|
+
- **Stacks** (opt-in, `delivery.stack: "auto"`): big goals become 2–4 PRs cut
|
|
909
|
+
along the integration branch's merge commits, landed bottom-up.
|
|
910
|
+
- **Adopt:** a teammate takes a stuck goal whose owner is away, fixes it on the
|
|
911
|
+
owner's branch, and hands it back; the owner's daemon backs off meanwhile.
|
|
912
|
+
- **Budgets:** per-goal cap (pauses the goal) and per-member daily cap (no new
|
|
913
|
+
goals); cost rollups per member/day, per goal and per landed PR.
|
|
914
|
+
- **Doctor:** `loom team doctor` reports merge-queue traps and opens a PR adding
|
|
915
|
+
`merge_group:` to workflows. It never changes settings.
|
|
916
|
+
- **Hosted hub:** `SupabaseHubClient` over RPC and Realtime, GitHub sign-in via
|
|
917
|
+
Supabase (loopback), `loom team signin` defaults to hosted; SQL `0005`
|
|
918
|
+
(`extend_lease`, `team_member_list`) and `0006` (landing feed events).
|
|
919
|
+
|
|
920
|
+
### Loom Teams, Phase 3: one brain
|
|
921
|
+
|
|
922
|
+
- **12 more design decisions** (D40–D51), settled in a design interview.
|
|
923
|
+
- **Team memories:** durable memories from a shared project are published to the
|
|
924
|
+
hub sealed under the team key, with an HMAC of the normalized text. The hub
|
|
925
|
+
merges exact twins as confirmations without reading them (MemoryHub, `loom hub`
|
|
926
|
+
and SQL `0004_team_memories.sql`, tested on Postgres).
|
|
927
|
+
- **Who can change what:** only a memory's author edits or forgets it; a teammate
|
|
928
|
+
records a correction that supersedes it. Resolving keeps the loser, linked to
|
|
929
|
+
the winner, which inherits its confirmations.
|
|
930
|
+
- **Tiered briefings:** canon > confirmed by 2+ > your own > a teammate's
|
|
931
|
+
proposal, each line labelled. Failures and facts age out of briefings after ~90
|
|
932
|
+
days unless re-confirmed.
|
|
933
|
+
- **Untrusted memories:** a turn that read the web, GitHub issue/PR text or a
|
|
934
|
+
fetched URL marks what it taught as untrusted. Untrusted memories stay personal
|
|
935
|
+
until you trust them.
|
|
936
|
+
- **Canon in `AGENTS.md`:** a Loom-managed section with per-line markers; lines
|
|
937
|
+
added by hand count too. Promotion goes through one rolling `loom/canon` PR,
|
|
938
|
+
which also adds `@AGENTS.md` to `CLAUDE.md`. The native-memory import skips the
|
|
939
|
+
section, so canon never loops back in.
|
|
940
|
+
- **Live team context** in worker and handoff briefings, up to 1.5k characters:
|
|
941
|
+
- teammates' leases on the same paths;
|
|
942
|
+
- open PRs that change them (PR feed events now carry changed paths);
|
|
943
|
+
- failing checks;
|
|
944
|
+
- predicted conflicts.
|
|
945
|
+
- **Inbox:** contradictions, near-duplicates, corrections, untrusted memories and
|
|
946
|
+
promotion candidates. REST `/api/projects/:id/team/brain[/:action]` and
|
|
947
|
+
`loom team brain`.
|
|
948
|
+
|
|
949
|
+
### Loom Teams, Phase 2: stop colliding
|
|
950
|
+
|
|
951
|
+
- **12 more design decisions** (D28–D39), settled in a design interview.
|
|
952
|
+
- **Leases:**
|
|
953
|
+
- scopes are expanded to real files plus directory prefixes, and hard zones come
|
|
954
|
+
from `loom.team.json`;
|
|
955
|
+
- hub claim, extend, renew, landing and release, with atomic hard-zone
|
|
956
|
+
arbitration, in both MemoryHub and SQL (tested on Postgres).
|
|
957
|
+
- **Orchestra holds** stop a task from starting:
|
|
958
|
+
- **decide:** the task overlaps a teammate's lease and needs an `overlap`
|
|
959
|
+
decision;
|
|
960
|
+
- **wait:** it waits for another goal's PR to merge, then rebases onto main;
|
|
961
|
+
- **zone:** it queues behind a held hard zone;
|
|
962
|
+
- **capacity:** the team is at its agent limit;
|
|
963
|
+
- "Stop waiting" releases a wait by hand.
|
|
964
|
+
- **Drift:** when a worker edits outside its declared touches, the lease widens and
|
|
965
|
+
the team is told. Drift into someone else's hard zone pauses the worker.
|
|
966
|
+
- **WIP refs** (`refs/loom/wip/<member>/<run>`) plus `git merge-tree` predict
|
|
967
|
+
conflicts. Both owners and both orchestrators hear about them.
|
|
968
|
+
- **Leases are released when the goal lands.** "Landing" means a merged PR, a
|
|
969
|
+
delivered push, or an applied run.
|
|
970
|
+
- **Team policy enforced:** permission ceiling (`bypassRequiresPlan`), agent
|
|
971
|
+
allowlist, protected-branch delivery (forces a PR), and per-member and
|
|
972
|
+
team-wide concurrency caps.
|
|
973
|
+
- **Fixes:**
|
|
974
|
+
- an orchestrator replying with no actions while nothing was running looped
|
|
975
|
+
until the round limit, and now waits for a human;
|
|
976
|
+
- an abort during a run's final wrap-up was overwritten by "completed";
|
|
977
|
+
- a member's own `gh` poll reporting their PR merged was ignored.
|
|
978
|
+
|
|
979
|
+
### Loom Teams, Phase 1: see each other
|
|
980
|
+
|
|
981
|
+
- **The design is settled:** 27 decisions from a design interview, recorded in
|
|
982
|
+
docs/teams-architecture.md §1a.
|
|
983
|
+
- **Team crypto:**
|
|
984
|
+
- an XChaCha20-Poly1305 team key;
|
|
985
|
+
- an X25519 sealed box for key envelopes;
|
|
986
|
+
- Ed25519 signatures on everything a daemon publishes;
|
|
987
|
+
- the invite link carries the key in its `#fragment`.
|
|
988
|
+
- **The hub:**
|
|
989
|
+
- the `HubClient` protocol, with `MemoryHub` as the reference rules;
|
|
990
|
+
- `loom hub`, a self-hosted hub over HTTP + WebSocket;
|
|
991
|
+
- the hosted hub's SQL (RLS plus rule functions), tested against a real Postgres.
|
|
992
|
+
- **Team Link in the daemon:**
|
|
993
|
+
- sign-in, create, invite, join and leave;
|
|
994
|
+
- removal rotates the key forward;
|
|
995
|
+
- opt-in project sharing, with auto-match on the git remote;
|
|
996
|
+
- heartbeats carry intent only;
|
|
997
|
+
- a goal/plan/PR feed, with `gh` polling when there is no GitHub App.
|
|
998
|
+
- **Worker commits carry `Loom-Goal`, `Loom-Task`, `Loom-Agent` and `Loom-Member`
|
|
999
|
+
trailers.**
|
|
1000
|
+
- **The orchestra protocol gains `touches`** (declared file globs per task).
|
|
1001
|
+
- **Fix:** a removed member's daemon now hears its own removal. It was cut off
|
|
1002
|
+
before the announcement.
|
|
1003
|
+
|
|
1004
|
+
### Permissions, plan mode, git delivery, fleet, prompts
|
|
1005
|
+
|
|
1006
|
+
- **Permission modes per agent: bypass, auto, always ask.**
|
|
1007
|
+
- Each is mapped onto each CLI's own flags, and every cell was verified against
|
|
1008
|
+
the real CLI (`scripts/verify-permissions.mjs`).
|
|
1009
|
+
- Always ask on Claude Code routes each tool use to a real approval in Loom,
|
|
1010
|
+
through a built-in MCP permission-prompt server. Verified with the real CLI
|
|
1011
|
+
(`scripts/verify-approvals.mjs`).
|
|
1012
|
+
- Two cells measured broken are shown as unavailable instead of lying: OpenCode
|
|
1013
|
+
"ask", and Antigravity "auto" (which writes to agy's scratch folder).
|
|
1014
|
+
- **Plan mode.**
|
|
1015
|
+
- Chat turns write a markdown plan instead of code.
|
|
1016
|
+
- Orchestras commit `plans/<run>/PLAN.md` plus a spec per task before workers
|
|
1017
|
+
start, and write results back at the end.
|
|
1018
|
+
- **Git delivery policy: none, commit, push, or PR.** Covers chat turns and finished
|
|
1019
|
+
orchestras. PR mode pushes the run's branch and opens a PR with `gh`.
|
|
1020
|
+
- **Fleet:** `GET /api/activity` and a Fleet view of every agent's thread, task and
|
|
1021
|
+
last step.
|
|
1022
|
+
- **Prompt manager:** saved and pinned prompts, plus automatic sent-prompt history
|
|
1023
|
+
(`/api/prompts`).
|
|
1024
|
+
- **Teams:** researched architecture in docs/teams-architecture.md.
|
|
1025
|
+
|
|
1026
|
+
### Orchestra: one orchestrator, many parallel workers
|
|
1027
|
+
|
|
1028
|
+
- **`loom orchestrate "<goal>"`**, plus Orchestrate mode in the app.
|
|
1029
|
+
- Any agent can orchestrate: Claude Code, Codex, Antigravity, Grok or OpenCode.
|
|
1030
|
+
It plans the goal into a task graph.
|
|
1031
|
+
- Worker agents run every ready task at once, mixed kinds or several of the same
|
|
1032
|
+
kind. Each task gets its own thread and git worktree.
|
|
1033
|
+
- Finished work merges into `loom/orchestra/<run>/main`, and the orchestrator
|
|
1034
|
+
reviews, follows up, or finishes.
|
|
1035
|
+
- `orchestra:apply` merges the run into your branch. Nothing touches it before
|
|
1036
|
+
that.
|
|
1037
|
+
- Verified against the real CLIs:
|
|
1038
|
+
- Claude orchestrating Codex and Antigravity (278 s);
|
|
1039
|
+
- Codex orchestrating Grok, OpenCode and Claude Code (82 s, $0.31).
|
|
1040
|
+
- Both delivered working code with passing tests
|
|
1041
|
+
(`scripts/verify-orchestra.mjs`).
|
|
1042
|
+
- Design credit: Agent Orchestrator (Apache-2.0). See THIRD_PARTY_NOTICES.md.
|
|
1043
|
+
|
|
1044
|
+
### Loom Cloud: reach your agents from any network
|
|
1045
|
+
|
|
1046
|
+
- A Supabase Realtime relay, end-to-end encrypted (XChaCha20-Poly1305). The key
|
|
1047
|
+
exists only in the pairing QR's URL fragment, so Supabase relays ciphertext.
|
|
1048
|
+
- Relayed requests run against the daemon with the phone's own token. The admin
|
|
1049
|
+
bootstrap is refused over the relay.
|
|
1050
|
+
- New commands: `loom cloud enable|disable|rotate`. Setup guide: docs/cloud.md.
|
|
1051
|
+
- Connection patterns credited to T3 Code (MIT).
|
|
1052
|
+
|
|
1053
|
+
### Fixes found by running the real agents
|
|
1054
|
+
|
|
1055
|
+
- A worker's `.loom/` state made `git add` fail whenever the project gitignored
|
|
1056
|
+
`.loom/`. That failed every real Codex task.
|
|
1057
|
+
- A project reached through a symlink (every macOS temp dir) registered twice, so
|
|
1058
|
+
two runtimes ran over one `.loom/`.
|
|
1059
|
+
- OpenCode's default model is refused headless ("Model is unavailable"). With no
|
|
1060
|
+
model pinned, the adapter now picks one the server can actually run.
|
|
1061
|
+
- `test/git.test.ts` assumed the default branch is `main`; CI runners use `master`.
|
|
1062
|
+
|
|
1063
|
+
|
|
10
1064
|
### Hardening (production-readiness pass)
|
|
11
1065
|
|
|
12
1066
|
- **Closed a DNS-rebinding path to the admin token.** `GET /api/bootstrap` handed
|