@loompad/cli 0.1.0 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +96 -11
- package/CHANGELOG.md +1042 -0
- package/README.md +1473 -44
- package/THIRD_PARTY_NOTICES.md +275 -0
- package/dist/adapters/antigravity-cli.d.ts +62 -0
- package/dist/adapters/antigravity-cli.js +258 -0
- package/dist/adapters/antigravity-cli.js.map +1 -0
- package/dist/adapters/base.d.ts +61 -9
- package/dist/adapters/base.js +266 -15
- package/dist/adapters/base.js.map +1 -1
- package/dist/adapters/bridges/antigravity.d.ts +9 -12
- package/dist/adapters/bridges/antigravity.js +9 -12
- package/dist/adapters/bridges/antigravity.js.map +1 -1
- package/dist/adapters/echo.d.ts +1 -0
- package/dist/adapters/echo.js +59 -3
- package/dist/adapters/echo.js.map +1 -1
- package/dist/adapters/grok.d.ts +7 -0
- package/dist/adapters/grok.js +20 -10
- package/dist/adapters/grok.js.map +1 -1
- package/dist/adapters/index.d.ts +5 -1
- package/dist/adapters/index.js +53 -7
- package/dist/adapters/index.js.map +1 -1
- package/dist/adapters/model.d.ts +137 -0
- package/dist/adapters/model.js +527 -0
- package/dist/adapters/model.js.map +1 -0
- package/dist/adapters/opencode.d.ts +101 -1
- package/dist/adapters/opencode.js +874 -98
- package/dist/adapters/opencode.js.map +1 -1
- package/dist/cli/doctor.d.ts +32 -0
- package/dist/cli/doctor.js +89 -4
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/index.js +2651 -87
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/tui-model.js +3 -1
- package/dist/cli/tui-model.js.map +1 -1
- package/dist/cli/tui.js +44 -1
- package/dist/cli/tui.js.map +1 -1
- package/dist/cli/ui.d.ts +2 -0
- package/dist/cli/ui.js +13 -0
- package/dist/cli/ui.js.map +1 -1
- package/dist/core/ades.d.ts +32 -16
- package/dist/core/ades.js +57 -20
- package/dist/core/ades.js.map +1 -1
- package/dist/core/agent-state.d.ts +14 -0
- package/dist/core/agent-state.js +32 -0
- package/dist/core/agent-state.js.map +1 -0
- package/dist/core/approvals.d.ts +33 -0
- package/dist/core/approvals.js +29 -0
- package/dist/core/approvals.js.map +1 -0
- package/dist/core/backup.d.ts +19 -0
- package/dist/core/backup.js +86 -0
- package/dist/core/backup.js.map +1 -0
- package/dist/core/baton.d.ts +2 -2
- package/dist/core/baton.js.map +1 -1
- package/dist/core/brain-extract.d.ts +17 -0
- package/dist/core/brain-extract.js +28 -2
- package/dist/core/brain-extract.js.map +1 -1
- package/dist/core/brain-index.d.ts +71 -0
- package/dist/core/brain-index.js +204 -5
- package/dist/core/brain-index.js.map +1 -1
- package/dist/core/brain.d.ts +58 -2
- package/dist/core/brain.js +79 -1
- package/dist/core/brain.js.map +1 -1
- package/dist/core/chat-import.d.ts +71 -0
- package/dist/core/chat-import.js +419 -0
- package/dist/core/chat-import.js.map +1 -0
- package/dist/core/checkpoint.d.ts +107 -0
- package/dist/core/checkpoint.js +259 -0
- package/dist/core/checkpoint.js.map +1 -0
- package/dist/core/claude-cli.js +1 -1
- package/dist/core/claude-cli.js.map +1 -1
- package/dist/core/continuity/artifacts.d.ts +14 -0
- package/dist/core/continuity/artifacts.js +118 -0
- package/dist/core/continuity/artifacts.js.map +1 -0
- package/dist/core/continuity/capabilities.d.ts +65 -0
- package/dist/core/continuity/capabilities.js +129 -0
- package/dist/core/continuity/capabilities.js.map +1 -0
- package/dist/core/continuity/contracts.d.ts +352 -0
- package/dist/core/continuity/contracts.js +117 -0
- package/dist/core/continuity/contracts.js.map +1 -0
- package/dist/core/continuity/engine.d.ts +55 -0
- package/dist/core/continuity/engine.js +504 -0
- package/dist/core/continuity/engine.js.map +1 -0
- package/dist/core/continuity/store.d.ts +73 -0
- package/dist/core/continuity/store.js +438 -0
- package/dist/core/continuity/store.js.map +1 -0
- package/dist/core/conversations.d.ts +19 -0
- package/dist/core/conversations.js +87 -0
- package/dist/core/conversations.js.map +1 -0
- package/dist/core/crew-coordination.d.ts +6 -0
- package/dist/core/crew-coordination.js +24 -0
- package/dist/core/crew-coordination.js.map +1 -0
- package/dist/core/crew-policy.d.ts +18 -0
- package/dist/core/crew-policy.js +40 -0
- package/dist/core/crew-policy.js.map +1 -0
- package/dist/core/crew-protocol.d.ts +64 -0
- package/dist/core/crew-protocol.js +146 -0
- package/dist/core/crew-protocol.js.map +1 -0
- package/dist/core/crew-requests.d.ts +14 -0
- package/dist/core/crew-requests.js +22 -0
- package/dist/core/crew-requests.js.map +1 -0
- package/dist/core/crew.d.ts +395 -0
- package/dist/core/crew.js +1857 -0
- package/dist/core/crew.js.map +1 -0
- package/dist/core/digest.d.ts +40 -0
- package/dist/core/digest.js +94 -0
- package/dist/core/digest.js.map +1 -0
- package/dist/core/distill.js +3 -5
- package/dist/core/distill.js.map +1 -1
- package/dist/core/eventlog.d.ts +27 -25
- package/dist/core/eventlog.js +97 -150
- package/dist/core/eventlog.js.map +1 -1
- package/dist/core/events/contracts.d.ts +51 -0
- package/dist/core/events/contracts.js +2 -0
- package/dist/core/events/contracts.js.map +1 -0
- package/dist/core/events/jsonl-store.d.ts +23 -0
- package/dist/core/events/jsonl-store.js +90 -0
- package/dist/core/events/jsonl-store.js.map +1 -0
- package/dist/core/events/sqlite-store.d.ts +37 -0
- package/dist/core/events/sqlite-store.js +215 -0
- package/dist/core/events/sqlite-store.js.map +1 -0
- package/dist/core/forecast.d.ts +44 -0
- package/dist/core/forecast.js +46 -0
- package/dist/core/forecast.js.map +1 -0
- package/dist/core/free-pool.d.ts +65 -0
- package/dist/core/free-pool.js +171 -0
- package/dist/core/free-pool.js.map +1 -0
- package/dist/core/git.d.ts +30 -0
- package/dist/core/git.js +50 -2
- package/dist/core/git.js.map +1 -1
- package/dist/core/github-events.d.ts +53 -0
- package/dist/core/github-events.js +227 -0
- package/dist/core/github-events.js.map +1 -0
- package/dist/core/goal-lanes.d.ts +53 -0
- package/dist/core/goal-lanes.js +99 -0
- package/dist/core/goal-lanes.js.map +1 -0
- package/dist/core/hosted.d.ts +27 -0
- package/dist/core/hosted.js +50 -0
- package/dist/core/hosted.js.map +1 -0
- package/dist/core/http-server.d.ts +5 -0
- package/dist/core/http-server.js +8 -0
- package/dist/core/http-server.js.map +1 -0
- package/dist/core/invite-link.d.ts +31 -0
- package/dist/core/invite-link.js +45 -0
- package/dist/core/invite-link.js.map +1 -0
- package/dist/core/loops.d.ts +37 -0
- package/dist/core/loops.js +81 -0
- package/dist/core/loops.js.map +1 -0
- package/dist/core/mcp-catalog.d.ts +182 -0
- package/dist/core/mcp-catalog.js +431 -0
- package/dist/core/mcp-catalog.js.map +1 -0
- package/dist/core/mcp.d.ts +87 -0
- package/dist/core/mcp.js +181 -0
- package/dist/core/mcp.js.map +1 -0
- package/dist/core/memory.d.ts +1 -0
- package/dist/core/memory.js +5 -2
- package/dist/core/memory.js.map +1 -1
- package/dist/core/model-tools.d.ts +97 -0
- package/dist/core/model-tools.js +388 -0
- package/dist/core/model-tools.js.map +1 -0
- package/dist/core/notify.js +19 -1
- package/dist/core/notify.js.map +1 -1
- package/dist/core/orchestra.d.ts +635 -0
- package/dist/core/orchestra.js +2120 -0
- package/dist/core/orchestra.js.map +1 -0
- package/dist/core/permissions.d.ts +56 -0
- package/dist/core/permissions.js +122 -0
- package/dist/core/permissions.js.map +1 -0
- package/dist/core/preview-proxy.d.ts +70 -0
- package/dist/core/preview-proxy.js +563 -0
- package/dist/core/preview-proxy.js.map +1 -0
- package/dist/core/preview-shot.d.ts +46 -0
- package/dist/core/preview-shot.js +109 -0
- package/dist/core/preview-shot.js.map +1 -0
- package/dist/core/process.d.ts +36 -0
- package/dist/core/process.js +168 -0
- package/dist/core/process.js.map +1 -0
- package/dist/core/prompt-queue.d.ts +136 -0
- package/dist/core/prompt-queue.js +269 -0
- package/dist/core/prompt-queue.js.map +1 -0
- package/dist/core/prompts.d.ts +53 -0
- package/dist/core/prompts.js +127 -0
- package/dist/core/prompts.js.map +1 -0
- package/dist/core/proof.d.ts +82 -0
- package/dist/core/proof.js +216 -0
- package/dist/core/proof.js.map +1 -0
- package/dist/core/provider-text.d.ts +23 -0
- package/dist/core/provider-text.js +66 -0
- package/dist/core/provider-text.js.map +1 -0
- package/dist/core/providers.d.ts +152 -0
- package/dist/core/providers.js +343 -0
- package/dist/core/providers.js.map +1 -0
- package/dist/core/quota.d.ts +33 -0
- package/dist/core/quota.js +74 -0
- package/dist/core/quota.js.map +1 -0
- package/dist/core/recipes.d.ts +50 -0
- package/dist/core/recipes.js +99 -0
- package/dist/core/recipes.js.map +1 -0
- package/dist/core/registry.d.ts +54 -0
- package/dist/core/registry.js +62 -5
- package/dist/core/registry.js.map +1 -1
- package/dist/core/relay-client.d.ts +67 -0
- package/dist/core/relay-client.js +184 -0
- package/dist/core/relay-client.js.map +1 -0
- package/dist/core/relay-protocol.d.ts +140 -0
- package/dist/core/relay-protocol.js +189 -0
- package/dist/core/relay-protocol.js.map +1 -0
- package/dist/core/replay.d.ts +18 -0
- package/dist/core/replay.js +191 -0
- package/dist/core/replay.js.map +1 -0
- package/dist/core/review-triage.d.ts +41 -0
- package/dist/core/review-triage.js +124 -0
- package/dist/core/review-triage.js.map +1 -0
- package/dist/core/router.js +2 -2
- package/dist/core/router.js.map +1 -1
- package/dist/core/routes.d.ts +39 -3
- package/dist/core/routes.js +137 -4
- package/dist/core/routes.js.map +1 -1
- package/dist/core/run-command.d.ts +14 -0
- package/dist/core/run-command.js +45 -0
- package/dist/core/run-command.js.map +1 -0
- package/dist/core/runner-setup.d.ts +36 -0
- package/dist/core/runner-setup.js +114 -0
- package/dist/core/runner-setup.js.map +1 -0
- package/dist/core/search.js +0 -0
- package/dist/core/search.js.map +1 -1
- package/dist/core/semantic.d.ts +92 -0
- package/dist/core/semantic.js +227 -0
- package/dist/core/semantic.js.map +1 -0
- package/dist/core/servers.d.ts +115 -0
- package/dist/core/servers.js +305 -0
- package/dist/core/servers.js.map +1 -0
- package/dist/core/setup.js +64 -6
- package/dist/core/setup.js.map +1 -1
- package/dist/core/shield.d.ts +17 -0
- package/dist/core/shield.js +81 -0
- package/dist/core/shield.js.map +1 -0
- package/dist/core/shift.d.ts +69 -0
- package/dist/core/shift.js +120 -0
- package/dist/core/shift.js.map +1 -0
- package/dist/core/skill-install.d.ts +125 -0
- package/dist/core/skill-install.js +328 -0
- package/dist/core/skill-install.js.map +1 -0
- package/dist/core/skills.d.ts +116 -0
- package/dist/core/skills.js +223 -0
- package/dist/core/skills.js.map +1 -0
- package/dist/core/step-conditions.d.ts +65 -0
- package/dist/core/step-conditions.js +102 -0
- package/dist/core/step-conditions.js.map +1 -0
- package/dist/core/subagents.d.ts +45 -0
- package/dist/core/subagents.js +90 -0
- package/dist/core/subagents.js.map +1 -0
- package/dist/core/team-canon.d.ts +42 -0
- package/dist/core/team-canon.js +121 -0
- package/dist/core/team-canon.js.map +1 -0
- package/dist/core/team-crypto.d.ts +97 -0
- package/dist/core/team-crypto.js +175 -0
- package/dist/core/team-crypto.js.map +1 -0
- package/dist/core/team-hub.d.ts +514 -0
- package/dist/core/team-hub.js +964 -0
- package/dist/core/team-hub.js.map +1 -0
- package/dist/core/team-landing.d.ts +239 -0
- package/dist/core/team-landing.js +504 -0
- package/dist/core/team-landing.js.map +1 -0
- package/dist/core/team-leases.d.ts +44 -0
- package/dist/core/team-leases.js +145 -0
- package/dist/core/team-leases.js.map +1 -0
- package/dist/core/team-memory.d.ts +42 -0
- package/dist/core/team-memory.js +84 -0
- package/dist/core/team-memory.js.map +1 -0
- package/dist/core/team-policy.d.ts +85 -0
- package/dist/core/team-policy.js +191 -0
- package/dist/core/team-policy.js.map +1 -0
- package/dist/core/thread-md.d.ts +6 -0
- package/dist/core/thread-md.js +42 -0
- package/dist/core/thread-md.js.map +1 -0
- package/dist/core/turn-stats.d.ts +61 -0
- package/dist/core/turn-stats.js +124 -0
- package/dist/core/turn-stats.js.map +1 -0
- package/dist/core/updater.d.ts +73 -0
- package/dist/core/updater.js +153 -0
- package/dist/core/updater.js.map +1 -0
- package/dist/core/usage.d.ts +75 -0
- package/dist/core/usage.js +163 -0
- package/dist/core/usage.js.map +1 -0
- package/dist/core/windows-jobs.d.ts +15 -0
- package/dist/core/windows-jobs.js +170 -0
- package/dist/core/windows-jobs.js.map +1 -0
- package/dist/core/worktree-merge.d.ts +57 -0
- package/dist/core/worktree-merge.js +123 -0
- package/dist/core/worktree-merge.js.map +1 -0
- package/dist/core/worktree.d.ts +5 -0
- package/dist/core/worktree.js +45 -5
- package/dist/core/worktree.js.map +1 -1
- package/dist/core/worktrees.d.ts +6 -0
- package/dist/core/worktrees.js +21 -0
- package/dist/core/worktrees.js.map +1 -0
- package/dist/daemon/app-page.d.ts +0 -17
- package/dist/daemon/app-page.js +10 -6346
- package/dist/daemon/app-page.js.map +1 -1
- package/dist/daemon/auth.d.ts +24 -3
- package/dist/daemon/auth.js +66 -7
- package/dist/daemon/auth.js.map +1 -1
- package/dist/daemon/board.d.ts +7 -0
- package/dist/daemon/board.js +12 -1
- package/dist/daemon/board.js.map +1 -1
- package/dist/daemon/brand-icons.d.ts +6 -1
- package/dist/daemon/brand-icons.js +11 -3
- package/dist/daemon/brand-icons.js.map +1 -1
- package/dist/daemon/client.d.ts +665 -1
- package/dist/daemon/client.js +381 -3
- package/dist/daemon/client.js.map +1 -1
- package/dist/daemon/delivery.d.ts +34 -0
- package/dist/daemon/delivery.js +63 -0
- package/dist/daemon/delivery.js.map +1 -0
- package/dist/daemon/deploys.d.ts +62 -0
- package/dist/daemon/deploys.js +159 -0
- package/dist/daemon/deploys.js.map +1 -0
- package/dist/daemon/landing.d.ts +192 -0
- package/dist/daemon/landing.js +992 -0
- package/dist/daemon/landing.js.map +1 -0
- package/dist/daemon/onboard.d.ts +150 -0
- package/dist/daemon/onboard.js +390 -0
- package/dist/daemon/onboard.js.map +1 -0
- package/dist/daemon/proof.d.ts +70 -0
- package/dist/daemon/proof.js +172 -0
- package/dist/daemon/proof.js.map +1 -0
- package/dist/daemon/push.d.ts +19 -1
- package/dist/daemon/push.js +90 -3
- package/dist/daemon/push.js.map +1 -1
- package/dist/daemon/relay.d.ts +82 -0
- package/dist/daemon/relay.js +351 -0
- package/dist/daemon/relay.js.map +1 -0
- package/dist/daemon/routes/agent-discovery.d.ts +4 -0
- package/dist/daemon/routes/agent-discovery.js +29 -0
- package/dist/daemon/routes/agent-discovery.js.map +1 -0
- package/dist/daemon/routes/agent-settings.d.ts +4 -0
- package/dist/daemon/routes/agent-settings.js +94 -0
- package/dist/daemon/routes/agent-settings.js.map +1 -0
- package/dist/daemon/routes/agents.d.ts +4 -0
- package/dist/daemon/routes/agents.js +105 -0
- package/dist/daemon/routes/agents.js.map +1 -0
- package/dist/daemon/routes/alerts.d.ts +4 -0
- package/dist/daemon/routes/alerts.js +137 -0
- package/dist/daemon/routes/alerts.js.map +1 -0
- package/dist/daemon/routes/approvals.d.ts +4 -0
- package/dist/daemon/routes/approvals.js +40 -0
- package/dist/daemon/routes/approvals.js.map +1 -0
- package/dist/daemon/routes/assets.d.ts +3 -0
- package/dist/daemon/routes/assets.js +64 -0
- package/dist/daemon/routes/assets.js.map +1 -0
- package/dist/daemon/routes/board.d.ts +4 -0
- package/dist/daemon/routes/board.js +147 -0
- package/dist/daemon/routes/board.js.map +1 -0
- package/dist/daemon/routes/brain.d.ts +4 -0
- package/dist/daemon/routes/brain.js +270 -0
- package/dist/daemon/routes/brain.js.map +1 -0
- package/dist/daemon/routes/chats.d.ts +4 -0
- package/dist/daemon/routes/chats.js +249 -0
- package/dist/daemon/routes/chats.js.map +1 -0
- package/dist/daemon/routes/clients.d.ts +4 -0
- package/dist/daemon/routes/clients.js +49 -0
- package/dist/daemon/routes/clients.js.map +1 -0
- package/dist/daemon/routes/cloud.d.ts +4 -0
- package/dist/daemon/routes/cloud.js +46 -0
- package/dist/daemon/routes/cloud.js.map +1 -0
- package/dist/daemon/routes/config.d.ts +4 -0
- package/dist/daemon/routes/config.js +35 -0
- package/dist/daemon/routes/config.js.map +1 -0
- package/dist/daemon/routes/context.d.ts +51 -0
- package/dist/daemon/routes/context.js +2 -0
- package/dist/daemon/routes/context.js.map +1 -0
- package/dist/daemon/routes/crews.d.ts +4 -0
- package/dist/daemon/routes/crews.js +149 -0
- package/dist/daemon/routes/crews.js.map +1 -0
- package/dist/daemon/routes/files.d.ts +3 -0
- package/dist/daemon/routes/files.js +253 -0
- package/dist/daemon/routes/files.js.map +1 -0
- package/dist/daemon/routes/git.d.ts +4 -0
- package/dist/daemon/routes/git.js +96 -0
- package/dist/daemon/routes/git.js.map +1 -0
- package/dist/daemon/routes/history.d.ts +4 -0
- package/dist/daemon/routes/history.js +101 -0
- package/dist/daemon/routes/history.js.map +1 -0
- package/dist/daemon/routes/integrations-project.d.ts +3 -0
- package/dist/daemon/routes/integrations-project.js +103 -0
- package/dist/daemon/routes/integrations-project.js.map +1 -0
- package/dist/daemon/routes/integrations.d.ts +3 -0
- package/dist/daemon/routes/integrations.js +111 -0
- package/dist/daemon/routes/integrations.js.map +1 -0
- package/dist/daemon/routes/logs.d.ts +3 -0
- package/dist/daemon/routes/logs.js +40 -0
- package/dist/daemon/routes/logs.js.map +1 -0
- package/dist/daemon/routes/messages.d.ts +4 -0
- package/dist/daemon/routes/messages.js +60 -0
- package/dist/daemon/routes/messages.js.map +1 -0
- package/dist/daemon/routes/observability.d.ts +4 -0
- package/dist/daemon/routes/observability.js +247 -0
- package/dist/daemon/routes/observability.js.map +1 -0
- package/dist/daemon/routes/onboard.d.ts +12 -0
- package/dist/daemon/routes/onboard.js +67 -0
- package/dist/daemon/routes/onboard.js.map +1 -0
- package/dist/daemon/routes/orchestra.d.ts +4 -0
- package/dist/daemon/routes/orchestra.js +110 -0
- package/dist/daemon/routes/orchestra.js.map +1 -0
- package/dist/daemon/routes/pairing.d.ts +4 -0
- package/dist/daemon/routes/pairing.js +155 -0
- package/dist/daemon/routes/pairing.js.map +1 -0
- package/dist/daemon/routes/preview.d.ts +24 -0
- package/dist/daemon/routes/preview.js +122 -0
- package/dist/daemon/routes/preview.js.map +1 -0
- package/dist/daemon/routes/project-actions.d.ts +4 -0
- package/dist/daemon/routes/project-actions.js +53 -0
- package/dist/daemon/routes/project-actions.js.map +1 -0
- package/dist/daemon/routes/project-team.d.ts +4 -0
- package/dist/daemon/routes/project-team.js +201 -0
- package/dist/daemon/routes/project-team.js.map +1 -0
- package/dist/daemon/routes/projects.d.ts +14 -0
- package/dist/daemon/routes/projects.js +138 -0
- package/dist/daemon/routes/projects.js.map +1 -0
- package/dist/daemon/routes/prompts.d.ts +4 -0
- package/dist/daemon/routes/prompts.js +44 -0
- package/dist/daemon/routes/prompts.js.map +1 -0
- package/dist/daemon/routes/proof.d.ts +4 -0
- package/dist/daemon/routes/proof.js +22 -0
- package/dist/daemon/routes/proof.js.map +1 -0
- package/dist/daemon/routes/providers.d.ts +3 -0
- package/dist/daemon/routes/providers.js +60 -0
- package/dist/daemon/routes/providers.js.map +1 -0
- package/dist/daemon/routes/public.d.ts +4 -0
- package/dist/daemon/routes/public.js +64 -0
- package/dist/daemon/routes/public.js.map +1 -0
- package/dist/daemon/routes/queue.d.ts +4 -0
- package/dist/daemon/routes/queue.js +140 -0
- package/dist/daemon/routes/queue.js.map +1 -0
- package/dist/daemon/routes/routing.d.ts +4 -0
- package/dist/daemon/routes/routing.js +61 -0
- package/dist/daemon/routes/routing.js.map +1 -0
- package/dist/daemon/routes/runtime-handler.d.ts +2 -0
- package/dist/daemon/routes/runtime-handler.js +63 -0
- package/dist/daemon/routes/runtime-handler.js.map +1 -0
- package/dist/daemon/routes/search.d.ts +4 -0
- package/dist/daemon/routes/search.js +17 -0
- package/dist/daemon/routes/search.js.map +1 -0
- package/dist/daemon/routes/servers.d.ts +4 -0
- package/dist/daemon/routes/servers.js +133 -0
- package/dist/daemon/routes/servers.js.map +1 -0
- package/dist/daemon/routes/setup.d.ts +3 -0
- package/dist/daemon/routes/setup.js +50 -0
- package/dist/daemon/routes/setup.js.map +1 -0
- package/dist/daemon/routes/shift.d.ts +4 -0
- package/dist/daemon/routes/shift.js +30 -0
- package/dist/daemon/routes/shift.js.map +1 -0
- package/dist/daemon/routes/specs.d.ts +4 -0
- package/dist/daemon/routes/specs.js +57 -0
- package/dist/daemon/routes/specs.js.map +1 -0
- package/dist/daemon/routes/task-delivery.d.ts +4 -0
- package/dist/daemon/routes/task-delivery.js +38 -0
- package/dist/daemon/routes/task-delivery.js.map +1 -0
- package/dist/daemon/routes/team.d.ts +4 -0
- package/dist/daemon/routes/team.js +193 -0
- package/dist/daemon/routes/team.js.map +1 -0
- package/dist/daemon/routes/terminals.d.ts +4 -0
- package/dist/daemon/routes/terminals.js +87 -0
- package/dist/daemon/routes/terminals.js.map +1 -0
- package/dist/daemon/routes/tools.d.ts +4 -0
- package/dist/daemon/routes/tools.js +180 -0
- package/dist/daemon/routes/tools.js.map +1 -0
- package/dist/daemon/routes/updates.d.ts +4 -0
- package/dist/daemon/routes/updates.js +89 -0
- package/dist/daemon/routes/updates.js.map +1 -0
- package/dist/daemon/routes/voice.d.ts +4 -0
- package/dist/daemon/routes/voice.js +53 -0
- package/dist/daemon/routes/voice.js.map +1 -0
- package/dist/daemon/runner.d.ts +186 -0
- package/dist/daemon/runner.js +612 -0
- package/dist/daemon/runner.js.map +1 -0
- package/dist/daemon/runtime/accounting.d.ts +130 -0
- package/dist/daemon/runtime/accounting.js +303 -0
- package/dist/daemon/runtime/accounting.js.map +1 -0
- package/dist/daemon/runtime/agents.d.ts +16 -0
- package/dist/daemon/runtime/agents.js +71 -0
- package/dist/daemon/runtime/agents.js.map +1 -0
- package/dist/daemon/runtime/briefings.d.ts +134 -0
- package/dist/daemon/runtime/briefings.js +329 -0
- package/dist/daemon/runtime/briefings.js.map +1 -0
- package/dist/daemon/runtime/native-usage.d.ts +21 -0
- package/dist/daemon/runtime/native-usage.js +71 -0
- package/dist/daemon/runtime/native-usage.js.map +1 -0
- package/dist/daemon/runtime/queue.d.ts +94 -0
- package/dist/daemon/runtime/queue.js +222 -0
- package/dist/daemon/runtime/queue.js.map +1 -0
- package/dist/daemon/runtime/relay.d.ts +93 -0
- package/dist/daemon/runtime/relay.js +240 -0
- package/dist/daemon/runtime/relay.js.map +1 -0
- package/dist/daemon/runtime/turns.d.ts +202 -0
- package/dist/daemon/runtime/turns.js +663 -0
- package/dist/daemon/runtime/turns.js.map +1 -0
- package/dist/daemon/runtime-support.d.ts +76 -0
- package/dist/daemon/runtime-support.js +134 -0
- package/dist/daemon/runtime-support.js.map +1 -0
- package/dist/daemon/runtime.d.ts +739 -70
- package/dist/daemon/runtime.js +2751 -470
- package/dist/daemon/runtime.js.map +1 -1
- package/dist/daemon/server.d.ts +82 -55
- package/dist/daemon/server.js +674 -1435
- package/dist/daemon/server.js.map +1 -1
- package/dist/daemon/shift.d.ts +70 -0
- package/dist/daemon/shift.js +170 -0
- package/dist/daemon/shift.js.map +1 -0
- package/dist/daemon/specs.d.ts +92 -0
- package/dist/daemon/specs.js +285 -0
- package/dist/daemon/specs.js.map +1 -0
- package/dist/daemon/system.d.ts +208 -0
- package/dist/daemon/system.js +557 -0
- package/dist/daemon/system.js.map +1 -0
- package/dist/daemon/tasks.js +1 -1
- package/dist/daemon/team-brain.d.ts +136 -0
- package/dist/daemon/team-brain.js +659 -0
- package/dist/daemon/team-brain.js.map +1 -0
- package/dist/daemon/team-coordinator.d.ts +78 -0
- package/dist/daemon/team-coordinator.js +408 -0
- package/dist/daemon/team-coordinator.js.map +1 -0
- package/dist/daemon/team.d.ts +339 -0
- package/dist/daemon/team.js +1433 -0
- package/dist/daemon/team.js.map +1 -0
- package/dist/daemon/terminals.d.ts +22 -3
- package/dist/daemon/terminals.js +236 -16
- package/dist/daemon/terminals.js.map +1 -1
- package/dist/hub/client.d.ts +120 -0
- package/dist/hub/client.js +228 -0
- package/dist/hub/client.js.map +1 -0
- package/dist/hub/server.d.ts +32 -0
- package/dist/hub/server.js +180 -0
- package/dist/hub/server.js.map +1 -0
- package/dist/hub/supabase-client.d.ts +220 -0
- package/dist/hub/supabase-client.js +843 -0
- package/dist/hub/supabase-client.js.map +1 -0
- package/dist/observability/ask.d.ts +91 -0
- package/dist/observability/ask.js +179 -0
- package/dist/observability/ask.js.map +1 -0
- package/dist/observability/clickhouse.d.ts +33 -0
- package/dist/observability/clickhouse.js +48 -0
- package/dist/observability/clickhouse.js.map +1 -0
- package/dist/observability/decisions.d.ts +176 -0
- package/dist/observability/decisions.js +377 -0
- package/dist/observability/decisions.js.map +1 -0
- package/dist/observability/index.d.ts +89 -0
- package/dist/observability/index.js +348 -0
- package/dist/observability/index.js.map +1 -0
- package/dist/observability/insights.d.ts +151 -0
- package/dist/observability/insights.js +319 -0
- package/dist/observability/insights.js.map +1 -0
- package/dist/observability/logs-query.d.ts +82 -0
- package/dist/observability/logs-query.js +102 -0
- package/dist/observability/logs-query.js.map +1 -0
- package/dist/observability/logs.d.ts +72 -0
- package/dist/observability/logs.js +295 -0
- package/dist/observability/logs.js.map +1 -0
- package/dist/observability/metrics.d.ts +86 -0
- package/dist/observability/metrics.js +303 -0
- package/dist/observability/metrics.js.map +1 -0
- package/dist/observability/otlp.d.ts +127 -0
- package/dist/observability/otlp.js +242 -0
- package/dist/observability/otlp.js.map +1 -0
- package/dist/observability/snapshots.d.ts +43 -0
- package/dist/observability/snapshots.js +129 -0
- package/dist/observability/snapshots.js.map +1 -0
- package/dist/observability/triage.d.ts +49 -0
- package/dist/observability/triage.js +284 -0
- package/dist/observability/triage.js.map +1 -0
- package/dist/providers/adapter.d.ts +50 -0
- package/dist/providers/adapter.js +38 -0
- package/dist/providers/adapter.js.map +1 -0
- package/dist/providers/agent.d.ts +110 -0
- package/dist/providers/agent.js +487 -0
- package/dist/providers/agent.js.map +1 -0
- package/dist/providers/approvals.d.ts +24 -0
- package/dist/providers/approvals.js +78 -0
- package/dist/providers/approvals.js.map +1 -0
- package/dist/providers/attachments.d.ts +20 -0
- package/dist/providers/attachments.js +51 -0
- package/dist/providers/attachments.js.map +1 -0
- package/dist/providers/claude/adapter.d.ts +99 -0
- package/dist/providers/claude/adapter.js +790 -0
- package/dist/providers/claude/adapter.js.map +1 -0
- package/dist/providers/codex/adapter.d.ts +113 -0
- package/dist/providers/codex/adapter.js +881 -0
- package/dist/providers/codex/adapter.js.map +1 -0
- package/dist/providers/codex/instructions.d.ts +11 -0
- package/dist/providers/codex/instructions.js +154 -0
- package/dist/providers/codex/instructions.js.map +1 -0
- package/dist/providers/codex/rpc.d.ts +36 -0
- package/dist/providers/codex/rpc.js +104 -0
- package/dist/providers/codex/rpc.js.map +1 -0
- package/dist/providers/contracts.d.ts +339 -0
- package/dist/providers/contracts.js +19 -0
- package/dist/providers/contracts.js.map +1 -0
- package/dist/providers/directory.d.ts +70 -0
- package/dist/providers/directory.js +95 -0
- package/dist/providers/directory.js.map +1 -0
- package/dist/providers/errors.d.ts +47 -0
- package/dist/providers/errors.js +24 -0
- package/dist/providers/errors.js.map +1 -0
- package/dist/providers/index.d.ts +18 -0
- package/dist/providers/index.js +19 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/ingestion.d.ts +80 -0
- package/dist/providers/ingestion.js +330 -0
- package/dist/providers/ingestion.js.map +1 -0
- package/dist/providers/live.d.ts +22 -0
- package/dist/providers/live.js +56 -0
- package/dist/providers/live.js.map +1 -0
- package/dist/providers/process.d.ts +29 -0
- package/dist/providers/process.js +71 -0
- package/dist/providers/process.js.map +1 -0
- package/dist/providers/reaper.d.ts +33 -0
- package/dist/providers/reaper.js +65 -0
- package/dist/providers/reaper.js.map +1 -0
- package/dist/providers/service.d.ts +87 -0
- package/dist/providers/service.js +256 -0
- package/dist/providers/service.js.map +1 -0
- package/dist/sdk.d.ts +1 -1
- package/dist/sdk.js +1 -1
- package/dist/sdk.js.map +1 -1
- package/dist/types.d.ts +385 -6
- package/dist/types.js +1 -0
- package/dist/types.js.map +1 -1
- package/dist/version.d.ts +7 -0
- package/dist/version.js +16 -0
- package/dist/version.js.map +1 -0
- package/dist/web/app.css +4643 -0
- package/dist/web/app.js +22640 -0
- package/dist/web/app.js.map +7 -0
- package/dist/web/shell.html +39 -0
- package/docs/README.md +21 -0
- package/docs/SETUP.md +10 -0
- package/docs/adapters.md +21 -8
- package/docs/brain-continuity.md +186 -0
- package/docs/cloud.md +108 -0
- package/docs/crews.md +62 -0
- package/docs/design-system.md +31 -2
- package/docs/integration-notes.md +128 -20
- package/docs/loompad.md +105 -0
- package/docs/observability.md +220 -0
- package/docs/play-store.md +123 -0
- package/docs/proposals/agent-teams.md +249 -0
- package/docs/proposals/brain-continuity-implementation.md +468 -0
- package/docs/proposals/brain-session-continuity.md +623 -0
- package/docs/proposals/t3code-port.md +81 -0
- package/docs/proposals/t3code-reference.md +95 -0
- package/docs/refactoring/AGENT-TEAMS-PROGRESS.md +88 -0
- package/docs/refactoring/BOUNDARIES.md +120 -0
- package/docs/refactoring/BRAIN-AUDIT.md +176 -0
- package/docs/refactoring/BRAIN-NOTES.md +390 -0
- package/docs/refactoring/BRAIN-TODO.md +214 -0
- package/docs/refactoring/NOTES.md +129 -0
- package/docs/refactoring/T3-PORT-NOTES.md +165 -0
- package/docs/refactoring/T3-PORT-TODO.md +123 -0
- package/docs/refactoring/TODO.md +46 -0
- package/docs/refactoring/brain-benchmark-2026-09-28.json +48 -0
- package/docs/refactoring/brain-benchmark-audit-2026-09-28.json +48 -0
- package/docs/release-notes/v0.2.0.md +35 -0
- package/docs/release-notes/v0.2.1.md +59 -0
- package/docs/release-notes/v0.2.3.md +77 -0
- package/docs/release-notes/v0.2.4.md +128 -0
- package/docs/release-notes/v0.2.5.md +153 -0
- package/docs/release-notes/v0.2.6.md +62 -0
- package/docs/release-notes/v1.0.0.md +120 -0
- package/docs/release-notes/v1.0.1.md +77 -0
- package/docs/release-notes/v1.1.0.md +71 -0
- package/docs/release-notes/v1.1.1.md +38 -0
- package/docs/teams-architecture.md +697 -0
- package/docs/teams.md +171 -0
- package/docs/windows.md +85 -0
- package/package.json +22 -9
- package/dist/adapters/claude-code.d.ts +0 -26
- package/dist/adapters/claude-code.js +0 -209
- package/dist/adapters/claude-code.js.map +0 -1
- package/dist/adapters/codex.d.ts +0 -51
- package/dist/adapters/codex.js +0 -291
- package/dist/adapters/codex.js.map +0 -1
- package/docs/img/board-linear.png +0 -0
- package/docs/img/board-projects.png +0 -0
- package/docs/img/brain.png +0 -0
- package/docs/img/settings.png +0 -0
- package/docs/img/workspace.png +0 -0
|
@@ -0,0 +1,659 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Team Brain — one project's share of the team's memory (Loom Teams, Phase 3,
|
|
3
|
+
* "one brain"; docs/teams-architecture.md D40–D51).
|
|
4
|
+
*
|
|
5
|
+
* What it does, and the decision each part serves:
|
|
6
|
+
* - publishes this project's durable memories to the hub, sealed under the
|
|
7
|
+
* team key with an HMAC so exact twins merge as confirmations (D13, D41).
|
|
8
|
+
* Untrusted (D43) and private (D13) memories stay home; an author's edit is
|
|
9
|
+
* an update, a local forget is a withdrawal (D40)
|
|
10
|
+
* - holds the team's memories for this repo — snapshot, then the stream (D50)
|
|
11
|
+
* — and the canon from AGENTS.md on origin's default branch (D44)
|
|
12
|
+
* - builds the tiered pool every briefing ranks: canon > confirmed > own >
|
|
13
|
+
* a teammate's proposal (D42), for this repo only (D51)
|
|
14
|
+
* - keeps an inbox of what needs a human: contradictions, near-duplicates,
|
|
15
|
+
* corrections awaiting resolution, untrusted memories, promotion candidates
|
|
16
|
+
* - promotes memories to canon through one rolling `loom/canon` PR (D45, D46)
|
|
17
|
+
* - writes the live team context block for briefings (D48)
|
|
18
|
+
*/
|
|
19
|
+
import { execFile } from "../core/process.js";
|
|
20
|
+
import fs from "node:fs";
|
|
21
|
+
import os from "node:os";
|
|
22
|
+
import path from "node:path";
|
|
23
|
+
import { CONFIDENCE_FLOOR, lemmatize, memoryHash } from "../core/brain.js";
|
|
24
|
+
import { cosine, findConflicts, trigramVector } from "../core/brain-index.js";
|
|
25
|
+
import { logbook } from "../core/logbook.js";
|
|
26
|
+
import { parseCanon, upsertCanon, withClaudeImport } from "../core/team-canon.js";
|
|
27
|
+
import { memoryHmac, openFromTeam, sealForTeam } from "../core/team-crypto.js";
|
|
28
|
+
import { LEASE_TTL_MS } from "../core/team-hub.js";
|
|
29
|
+
import { overlap } from "../core/team-leases.js";
|
|
30
|
+
/** Memory kinds worth sharing: `task` notes die with their run (D13). */
|
|
31
|
+
const DURABLE = new Set(["constraint", "failure", "decision", "convention", "fact"]);
|
|
32
|
+
const CONTEXT_CHARS = 1500;
|
|
33
|
+
export const CANON_BRANCH = "loom/canon";
|
|
34
|
+
function run(cmd, args, cwd) {
|
|
35
|
+
return new Promise((resolve, reject) => {
|
|
36
|
+
execFile(cmd, args, { cwd, timeout: 60_000, maxBuffer: 8 * 1024 * 1024 }, (err, stdout, stderr) => err ? reject(new Error((stderr || err.message).trim())) : resolve(stdout));
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
export function shareable(m) {
|
|
40
|
+
return DURABLE.has(m.kind) && m.confidence >= CONFIDENCE_FLOOR && !m.untrusted && !m.private && !m.expiresAt && !m.scope.crew;
|
|
41
|
+
}
|
|
42
|
+
export class TeamBrain {
|
|
43
|
+
rt;
|
|
44
|
+
deps;
|
|
45
|
+
/** Every team memory for this repo, every state (history too — D47 keeps losers). */
|
|
46
|
+
cache = new Map();
|
|
47
|
+
opened = new Map(); // id+hmac → decrypted
|
|
48
|
+
canon = [];
|
|
49
|
+
share = null;
|
|
50
|
+
syncing = null;
|
|
51
|
+
timer = null;
|
|
52
|
+
unsub = null;
|
|
53
|
+
/** Last error from a publish/sync, for the UI. */
|
|
54
|
+
lastError = null;
|
|
55
|
+
constructor(rt, deps) {
|
|
56
|
+
this.rt = rt;
|
|
57
|
+
this.deps = deps;
|
|
58
|
+
// A memory learned, edited or forgotten in a shared project reaches the team shortly after.
|
|
59
|
+
this.unsub = rt.log.onEvent((e) => {
|
|
60
|
+
if (e.kind === "memory_add" || e.kind === "memory_update" || e.kind === "memory_forget")
|
|
61
|
+
this.soon();
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
stop() {
|
|
65
|
+
this.unsub?.();
|
|
66
|
+
this.unsub = null;
|
|
67
|
+
if (this.timer)
|
|
68
|
+
clearTimeout(this.timer);
|
|
69
|
+
this.timer = null;
|
|
70
|
+
}
|
|
71
|
+
exec(cmd, args, cwd = this.rt.info.dir) {
|
|
72
|
+
return this.deps.exec ? this.deps.exec(cmd, args, cwd) : run(cmd, args, cwd);
|
|
73
|
+
}
|
|
74
|
+
soon() {
|
|
75
|
+
if (this.timer)
|
|
76
|
+
clearTimeout(this.timer);
|
|
77
|
+
this.timer = setTimeout(() => {
|
|
78
|
+
this.timer = null;
|
|
79
|
+
void this.sync().catch(() => { });
|
|
80
|
+
}, 1500);
|
|
81
|
+
this.timer.unref?.();
|
|
82
|
+
}
|
|
83
|
+
get teamId() {
|
|
84
|
+
return this.share?.teamId ?? null;
|
|
85
|
+
}
|
|
86
|
+
// ── sync: backfill, publish, canon ──
|
|
87
|
+
/** One pass: learn the share, backfill the team's memories once, publish ours, refresh canon. */
|
|
88
|
+
sync() {
|
|
89
|
+
if (this.syncing)
|
|
90
|
+
return this.syncing;
|
|
91
|
+
this.syncing = this.doSync().finally(() => {
|
|
92
|
+
this.syncing = null;
|
|
93
|
+
});
|
|
94
|
+
return this.syncing;
|
|
95
|
+
}
|
|
96
|
+
async doSync() {
|
|
97
|
+
const hub = this.deps.hub();
|
|
98
|
+
const prev = this.share;
|
|
99
|
+
this.share = hub ? await this.deps.share(this.rt) : null;
|
|
100
|
+
if (!this.share || !hub) {
|
|
101
|
+
this.cache.clear();
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
if (!prev || prev.teamId !== this.share.teamId || prev.repo !== this.share.repo || !this.cache.size) {
|
|
105
|
+
// D50: a live snapshot (with history, so resolutions stay visible), then the stream.
|
|
106
|
+
const all = await hub.teamMemories(this.share.teamId, this.share.repo, { history: true });
|
|
107
|
+
this.cache = new Map(all.map((m) => [m.id, m]));
|
|
108
|
+
}
|
|
109
|
+
try {
|
|
110
|
+
await this.publish();
|
|
111
|
+
this.lastError = null;
|
|
112
|
+
}
|
|
113
|
+
catch (e) {
|
|
114
|
+
this.lastError = e instanceof Error ? e.message : String(e);
|
|
115
|
+
logbook.warn("team", "couldn't publish memories", this.lastError);
|
|
116
|
+
}
|
|
117
|
+
await this.loadCanon().catch(() => { });
|
|
118
|
+
}
|
|
119
|
+
/** A hub event about a memory in our team. */
|
|
120
|
+
onMemory(m) {
|
|
121
|
+
if (!this.share || m.teamId !== this.share.teamId || m.repo !== this.share.repo)
|
|
122
|
+
return;
|
|
123
|
+
this.cache.set(m.id, m);
|
|
124
|
+
}
|
|
125
|
+
key() {
|
|
126
|
+
const keys = this.share ? this.deps.keys(this.share.teamId) : [];
|
|
127
|
+
return keys[keys.length - 1] ?? null;
|
|
128
|
+
}
|
|
129
|
+
open(m) {
|
|
130
|
+
const k = `${m.id}:${m.hmac}`;
|
|
131
|
+
if (!this.opened.has(k)) {
|
|
132
|
+
const v = openFromTeam(this.share ? this.deps.keys(this.share.teamId) : [], m.sealed);
|
|
133
|
+
this.opened.set(k, v && typeof v.text === "string" ? v : null);
|
|
134
|
+
}
|
|
135
|
+
return this.opened.get(k) ?? null;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Make the hub agree with this project's brain, idempotently and without any
|
|
139
|
+
* local bookkeeping: the cache says what the team already has.
|
|
140
|
+
*/
|
|
141
|
+
async publish() {
|
|
142
|
+
const hub = this.deps.hub();
|
|
143
|
+
const share = this.share;
|
|
144
|
+
const key = this.key();
|
|
145
|
+
const device = this.deps.deviceId();
|
|
146
|
+
const me = this.deps.github();
|
|
147
|
+
if (!hub || !share || !key || !device || !me)
|
|
148
|
+
return;
|
|
149
|
+
const local = this.rt.brain.all();
|
|
150
|
+
const localIds = new Set(local.map((m) => m.id));
|
|
151
|
+
const byHmac = new Map();
|
|
152
|
+
for (const t of this.cache.values())
|
|
153
|
+
if (t.state === "live")
|
|
154
|
+
byHmac.set(t.hmac, t);
|
|
155
|
+
for (const m of local) {
|
|
156
|
+
const mine = this.cache.get(m.id);
|
|
157
|
+
if (!shareable(m)) {
|
|
158
|
+
// made private or re-flagged since it was shared: withdraw it
|
|
159
|
+
if (mine && mine.state === "live" && mine.author === me) {
|
|
160
|
+
await hub.forgetTeamMemory(share.teamId, m.id, m.private ? "made private" : "no longer shareable");
|
|
161
|
+
this.cache.set(m.id, { ...mine, state: "forgotten" });
|
|
162
|
+
}
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
const h = memoryHmac(key, m.text);
|
|
166
|
+
if (mine) {
|
|
167
|
+
if (mine.state !== "live" || mine.hmac === h)
|
|
168
|
+
continue; // resolved against, or already current
|
|
169
|
+
const sealed = sealForTeam(key, sealedOf(m));
|
|
170
|
+
this.cache.set(m.id, await hub.updateTeamMemory(share.teamId, m.id, { hmac: h, sealed })); // D40: the author edits
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
const twin = byHmac.get(h);
|
|
174
|
+
if (twin && twin.confirmedBy.includes(me))
|
|
175
|
+
continue; // already counted
|
|
176
|
+
const out = await hub.publishMemory(share.teamId, {
|
|
177
|
+
id: m.id,
|
|
178
|
+
repo: share.repo,
|
|
179
|
+
hmac: h,
|
|
180
|
+
sealed: sealForTeam(key, sealedOf(m)),
|
|
181
|
+
deviceId: device,
|
|
182
|
+
});
|
|
183
|
+
this.cache.set(out.memory.id, out.memory);
|
|
184
|
+
byHmac.set(out.memory.hmac, out.memory);
|
|
185
|
+
}
|
|
186
|
+
// A memory this project forgot, that it had shared: withdraw it too.
|
|
187
|
+
const forgotten = new Set(this.rt.log.list({ kinds: ["memory_forget"] }).map((e) => String(e.payload.id ?? "")));
|
|
188
|
+
for (const t of this.cache.values()) {
|
|
189
|
+
if (t.state !== "live" || t.author !== me || localIds.has(t.id) || !forgotten.has(t.id))
|
|
190
|
+
continue;
|
|
191
|
+
await hub.forgetTeamMemory(share.teamId, t.id, "forgotten by its author");
|
|
192
|
+
this.cache.set(t.id, { ...t, state: "forgotten" });
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
/** Publish one correction to a teammate's memory (D40): a new memory that supersedes theirs. */
|
|
196
|
+
async correct(targetId, text) {
|
|
197
|
+
const hub = this.deps.hub();
|
|
198
|
+
const share = this.share;
|
|
199
|
+
const key = this.key();
|
|
200
|
+
const device = this.deps.deviceId();
|
|
201
|
+
if (!hub || !share || !key || !device)
|
|
202
|
+
throw new Error("this project isn't shared with a team");
|
|
203
|
+
const target = this.cache.get(targetId);
|
|
204
|
+
const opened = target ? this.open(target) : null;
|
|
205
|
+
const kind = opened?.kind ?? this.rt.brain.get(targetId)?.kind ?? "decision";
|
|
206
|
+
const { memory } = this.rt.brain.add({
|
|
207
|
+
kind,
|
|
208
|
+
text,
|
|
209
|
+
provenance: { agentId: "user", eventId: 0, ts: Date.now() },
|
|
210
|
+
confidence: 1,
|
|
211
|
+
});
|
|
212
|
+
const out = await hub.publishMemory(share.teamId, {
|
|
213
|
+
id: memory.id,
|
|
214
|
+
repo: share.repo,
|
|
215
|
+
hmac: memoryHmac(key, memory.text),
|
|
216
|
+
sealed: sealForTeam(key, sealedOf(memory)),
|
|
217
|
+
deviceId: device,
|
|
218
|
+
supersedes: targetId,
|
|
219
|
+
});
|
|
220
|
+
this.cache.set(out.memory.id, out.memory);
|
|
221
|
+
return out.memory;
|
|
222
|
+
}
|
|
223
|
+
/** Settle a contradiction or a correction (D47): the loser is kept, superseded, linked. */
|
|
224
|
+
async resolve(winnerId, loserId, reason) {
|
|
225
|
+
const hub = this.deps.hub();
|
|
226
|
+
if (!hub || !this.share)
|
|
227
|
+
throw new Error("this project isn't shared with a team");
|
|
228
|
+
if (this.canon.some((c) => c.id === loserId)) {
|
|
229
|
+
throw new Error("canon changes through a PR — edit or delete that line in AGENTS.md");
|
|
230
|
+
}
|
|
231
|
+
await this.ensurePublished(winnerId);
|
|
232
|
+
await this.ensurePublished(loserId);
|
|
233
|
+
await hub.resolveMemories(this.share.teamId, winnerId, loserId, reason || "resolved");
|
|
234
|
+
const all = await hub.teamMemories(this.share.teamId, this.share.repo, { history: true });
|
|
235
|
+
this.cache = new Map(all.map((m) => [m.id, m]));
|
|
236
|
+
}
|
|
237
|
+
/** A local-only memory that's about to be resolved has to exist on the hub first. */
|
|
238
|
+
async ensurePublished(id) {
|
|
239
|
+
if (this.cache.has(id))
|
|
240
|
+
return;
|
|
241
|
+
// canon winning over a team memory: the hub needs the canon line as a row to link to
|
|
242
|
+
const c = this.canon.find((e) => e.id === id);
|
|
243
|
+
const m = this.rt.brain.get(id) ?? (c ? asMemory(c.id, c, Date.now(), Date.now(), "canon") : undefined);
|
|
244
|
+
if (!m)
|
|
245
|
+
throw new Error(`no memory "${id}"`);
|
|
246
|
+
const hub = this.deps.hub();
|
|
247
|
+
const key = this.key();
|
|
248
|
+
const device = this.deps.deviceId();
|
|
249
|
+
if (!key || !device)
|
|
250
|
+
throw new Error("no team key on this device");
|
|
251
|
+
const out = await hub.publishMemory(this.share.teamId, {
|
|
252
|
+
id: m.id,
|
|
253
|
+
repo: this.share.repo,
|
|
254
|
+
hmac: memoryHmac(key, m.text),
|
|
255
|
+
sealed: sealForTeam(key, sealedOf(m)),
|
|
256
|
+
deviceId: device,
|
|
257
|
+
});
|
|
258
|
+
this.cache.set(out.memory.id, out.memory);
|
|
259
|
+
}
|
|
260
|
+
// ── canon ──
|
|
261
|
+
async defaultBranch() {
|
|
262
|
+
const head = (await this.exec("git", ["symbolic-ref", "--short", "refs/remotes/origin/HEAD"]).catch(() => "")).trim();
|
|
263
|
+
return head ? head.replace(/^origin\//, "") : "main";
|
|
264
|
+
}
|
|
265
|
+
/** D44: canon is what AGENTS.md on origin's default branch says. */
|
|
266
|
+
async loadCanon() {
|
|
267
|
+
const branch = await this.defaultBranch();
|
|
268
|
+
const text = await this.exec("git", ["show", `origin/${branch}:AGENTS.md`]).catch(() => "");
|
|
269
|
+
this.canon = parseCanon(text);
|
|
270
|
+
return this.canon;
|
|
271
|
+
}
|
|
272
|
+
canonEntries() {
|
|
273
|
+
return [...this.canon];
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* D45: put memories up for canon on the one rolling `loom/canon` PR. Built
|
|
277
|
+
* fresh from the default branch each time — its canon, plus what's already
|
|
278
|
+
* pending on the branch, plus these — so a merged PR never leaves the branch
|
|
279
|
+
* behind, and the PR always shows exactly what isn't canon yet.
|
|
280
|
+
*/
|
|
281
|
+
async promote(ids) {
|
|
282
|
+
if (!ids.length)
|
|
283
|
+
throw new Error("which memories?");
|
|
284
|
+
const entries = [];
|
|
285
|
+
for (const id of ids) {
|
|
286
|
+
const v = this.view(id);
|
|
287
|
+
if (!v)
|
|
288
|
+
throw new Error(`no memory "${id}"`);
|
|
289
|
+
if (v.untrusted)
|
|
290
|
+
throw new Error(`"${v.text.slice(0, 60)}" was learned from outside content — trust it first`);
|
|
291
|
+
if (v.kind === "task")
|
|
292
|
+
throw new Error("task notes don't belong in canon");
|
|
293
|
+
entries.push({ id: v.id, kind: v.kind, text: v.text });
|
|
294
|
+
}
|
|
295
|
+
const dir = this.rt.info.dir;
|
|
296
|
+
// The PR is the point: check for gh before force-pushing a branch nobody can open.
|
|
297
|
+
await this.exec("gh", ["auth", "status"], dir).catch(() => {
|
|
298
|
+
throw new Error("Loom opens the canon PR with GitHub's `gh` CLI — install it and run `gh auth login`, then promote again");
|
|
299
|
+
});
|
|
300
|
+
await this.exec("git", ["fetch", "--quiet", "origin"]).catch(() => { });
|
|
301
|
+
const base = await this.defaultBranch();
|
|
302
|
+
const pending = await this.exec("git", ["show", `origin/${CANON_BRANCH}:AGENTS.md`]).catch(() => "");
|
|
303
|
+
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "loom-canon-"));
|
|
304
|
+
const wt = path.join(tmp, "wt");
|
|
305
|
+
try {
|
|
306
|
+
await this.exec("git", ["worktree", "add", "--quiet", "--detach", wt, `origin/${base}`], dir);
|
|
307
|
+
const agentsFile = path.join(wt, "AGENTS.md");
|
|
308
|
+
const doc = fs.existsSync(agentsFile) ? fs.readFileSync(agentsFile, "utf8") : "# AGENTS.md\n\nGuidance for coding agents working in this repository.\n";
|
|
309
|
+
const merged = [];
|
|
310
|
+
const seen = new Set();
|
|
311
|
+
const add = (e) => {
|
|
312
|
+
const k = "t:" + normText(e.text);
|
|
313
|
+
if (seen.has(e.id) || seen.has(k))
|
|
314
|
+
return;
|
|
315
|
+
seen.add(e.id);
|
|
316
|
+
seen.add(k);
|
|
317
|
+
merged.push(e);
|
|
318
|
+
};
|
|
319
|
+
const current = parseCanon(doc);
|
|
320
|
+
for (const e of current)
|
|
321
|
+
add(e);
|
|
322
|
+
for (const e of parseCanon(pending))
|
|
323
|
+
add(e);
|
|
324
|
+
const before = merged.length;
|
|
325
|
+
for (const e of entries)
|
|
326
|
+
add(e);
|
|
327
|
+
const added = merged.length - before;
|
|
328
|
+
if (!added && merged.length === current.length) {
|
|
329
|
+
return { branch: CANON_BRANCH, prUrl: null, added: 0, note: "already canon" };
|
|
330
|
+
}
|
|
331
|
+
fs.writeFileSync(agentsFile, upsertCanon(doc, merged));
|
|
332
|
+
const claudeFile = path.join(wt, "CLAUDE.md");
|
|
333
|
+
if (fs.existsSync(claudeFile)) {
|
|
334
|
+
const next = withClaudeImport(fs.readFileSync(claudeFile, "utf8")); // D46
|
|
335
|
+
if (next)
|
|
336
|
+
fs.writeFileSync(claudeFile, next);
|
|
337
|
+
}
|
|
338
|
+
await this.exec("git", ["add", "AGENTS.md", ...(fs.existsSync(claudeFile) ? ["CLAUDE.md"] : [])], wt);
|
|
339
|
+
const subject = entries.length === 1 ? `canon: ${entries[0].text.slice(0, 60)}` : `canon: ${entries.length} memories`;
|
|
340
|
+
await this.exec("git", ["-c", "user.name=Loom", "-c", "user.email=loom@users.noreply.github.com", "commit", "--quiet", "-m", subject,
|
|
341
|
+
"-m", "Proposed as team canon by Loom (docs/teams-architecture.md D44–D46)."], wt);
|
|
342
|
+
// Rebuilt from base every time, so this is a force push of a branch Loom owns.
|
|
343
|
+
await this.exec("git", ["push", "--quiet", "--force", "origin", `HEAD:refs/heads/${CANON_BRANCH}`], wt);
|
|
344
|
+
let prUrl = null;
|
|
345
|
+
let note;
|
|
346
|
+
const repo = this.share?.repo;
|
|
347
|
+
try {
|
|
348
|
+
const open = JSON.parse(await this.exec("gh", ["pr", "list", ...(repo ? ["--repo", repo] : []), "--head", CANON_BRANCH, "--state", "open", "--json", "url"], wt));
|
|
349
|
+
prUrl = open[0]?.url ?? null;
|
|
350
|
+
if (!prUrl) {
|
|
351
|
+
prUrl = (await this.exec("gh", ["pr", "create", ...(repo ? ["--repo", repo] : []), "--base", base, "--head", CANON_BRANCH, "--title", "Team canon",
|
|
352
|
+
"--body", canonPrBody()], wt)).trim().split("\n").pop() || null;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
catch (e) {
|
|
356
|
+
note = `pushed ${CANON_BRANCH}; couldn't open the PR with gh: ${e instanceof Error ? e.message : String(e)}`;
|
|
357
|
+
}
|
|
358
|
+
await this.announce("canon_proposed", { ids: entries.map((e) => e.id), ...(prUrl ? { prUrl } : {}) });
|
|
359
|
+
return { branch: CANON_BRANCH, prUrl, added, ...(note ? { note } : {}) };
|
|
360
|
+
}
|
|
361
|
+
finally {
|
|
362
|
+
await this.exec("git", ["worktree", "remove", "--force", wt], dir).catch(() => { });
|
|
363
|
+
fs.rmSync(tmp, { recursive: true, force: true });
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
async announce(type, meta) {
|
|
367
|
+
const hub = this.deps.hub();
|
|
368
|
+
const device = this.deps.deviceId();
|
|
369
|
+
if (!hub || !this.share || !device)
|
|
370
|
+
return;
|
|
371
|
+
await hub.appendFeed(this.share.teamId, { repo: this.share.repo, type, meta, deviceId: device }).catch(() => null);
|
|
372
|
+
}
|
|
373
|
+
// ── the pool every briefing ranks (D42) ──
|
|
374
|
+
/**
|
|
375
|
+
* Canon, the team's live memories and this project's own, as one tiered
|
|
376
|
+
* pool — or null when the project isn't shared (the solo brain applies).
|
|
377
|
+
*/
|
|
378
|
+
pool(own = this.rt.brain.all()) {
|
|
379
|
+
if (!this.share)
|
|
380
|
+
return null;
|
|
381
|
+
const me = this.deps.github();
|
|
382
|
+
const out = [];
|
|
383
|
+
const seen = new Set(); // ids and normalized texts already in the pool
|
|
384
|
+
const take = (m) => {
|
|
385
|
+
const k = "t:" + normText(m.text); // the normalization the HMAC merges on (D41)
|
|
386
|
+
if (seen.has(m.id) || seen.has(k))
|
|
387
|
+
return;
|
|
388
|
+
seen.add(m.id);
|
|
389
|
+
seen.add(k);
|
|
390
|
+
out.push(m);
|
|
391
|
+
};
|
|
392
|
+
const now = Date.now();
|
|
393
|
+
for (const c of this.canon) {
|
|
394
|
+
take({ ...asMemory(c.id, { text: c.text, kind: c.kind }, now, now, "canon"), tier: "canon" });
|
|
395
|
+
}
|
|
396
|
+
const team = [...this.cache.values()].filter((t) => t.state === "live");
|
|
397
|
+
// Confirmed first, so a twin of our own memory shows its confirmations.
|
|
398
|
+
team.sort((a, b) => b.confirmedBy.length - a.confirmedBy.length);
|
|
399
|
+
for (const t of team) {
|
|
400
|
+
const s = this.open(t);
|
|
401
|
+
if (!s)
|
|
402
|
+
continue;
|
|
403
|
+
const confirmed = t.confirmedBy.length >= 2;
|
|
404
|
+
if (!confirmed && t.author === me)
|
|
405
|
+
continue; // ours: the local copy below carries it
|
|
406
|
+
take({
|
|
407
|
+
...asMemory(t.id, s, t.createdAt, t.updatedAt, t.author),
|
|
408
|
+
tier: confirmed ? "confirmed" : "proposed",
|
|
409
|
+
author: t.author,
|
|
410
|
+
confirmedBy: [...t.confirmedBy],
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
for (const m of own) {
|
|
414
|
+
const t = this.cache.get(m.id);
|
|
415
|
+
if (t && t.state !== "live")
|
|
416
|
+
continue; // resolved against (D47) or withdrawn
|
|
417
|
+
take({ ...m, tier: "own" });
|
|
418
|
+
}
|
|
419
|
+
return out;
|
|
420
|
+
}
|
|
421
|
+
/** One memory as the UI shows it, from any tier. */
|
|
422
|
+
view(id) {
|
|
423
|
+
const me = this.deps.github();
|
|
424
|
+
const c = this.canon.find((e) => e.id === id);
|
|
425
|
+
if (c)
|
|
426
|
+
return { id, text: c.text, kind: c.kind, tier: "canon", author: null, confirmedBy: [], mine: false };
|
|
427
|
+
const t = this.cache.get(id);
|
|
428
|
+
const s = t ? this.open(t) : null;
|
|
429
|
+
const local = this.rt.brain.get(id);
|
|
430
|
+
if (t && s) {
|
|
431
|
+
return {
|
|
432
|
+
id,
|
|
433
|
+
text: s.text,
|
|
434
|
+
kind: s.kind,
|
|
435
|
+
tier: t.confirmedBy.length >= 2 ? "confirmed" : t.author === me ? "own" : "proposed",
|
|
436
|
+
author: t.author,
|
|
437
|
+
confirmedBy: [...t.confirmedBy],
|
|
438
|
+
mine: t.author === me,
|
|
439
|
+
...(local?.untrusted ? { untrusted: true } : {}),
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
if (local) {
|
|
443
|
+
return {
|
|
444
|
+
id,
|
|
445
|
+
text: local.text,
|
|
446
|
+
kind: local.kind,
|
|
447
|
+
tier: "own",
|
|
448
|
+
author: me,
|
|
449
|
+
confirmedBy: me ? [me] : [],
|
|
450
|
+
mine: true,
|
|
451
|
+
...(local.untrusted ? { untrusted: true } : {}),
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
return null;
|
|
455
|
+
}
|
|
456
|
+
/** Every live team memory plus the canon, for the Team view (D49). */
|
|
457
|
+
memories(opts = {}) {
|
|
458
|
+
const out = [];
|
|
459
|
+
for (const c of this.canon)
|
|
460
|
+
out.push({ ...this.view(c.id), state: "live" });
|
|
461
|
+
for (const t of this.cache.values()) {
|
|
462
|
+
if (!opts.history && t.state !== "live")
|
|
463
|
+
continue;
|
|
464
|
+
if (this.canon.some((c) => c.id === t.id))
|
|
465
|
+
continue;
|
|
466
|
+
const v = this.view(t.id);
|
|
467
|
+
if (!v)
|
|
468
|
+
continue;
|
|
469
|
+
out.push({
|
|
470
|
+
...v,
|
|
471
|
+
state: t.state,
|
|
472
|
+
...(t.supersedes ? { supersedes: t.supersedes } : {}),
|
|
473
|
+
...(t.supersededBy ? { supersededBy: t.supersededBy } : {}),
|
|
474
|
+
...(t.resolvedBy ? { resolvedBy: t.resolvedBy } : {}),
|
|
475
|
+
...(t.resolvedReason ? { resolvedReason: t.resolvedReason } : {}),
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
return out;
|
|
479
|
+
}
|
|
480
|
+
// ── the inbox (D49) ──
|
|
481
|
+
inbox() {
|
|
482
|
+
const pool = this.pool();
|
|
483
|
+
if (!pool)
|
|
484
|
+
return [];
|
|
485
|
+
const byId = new Map(pool.map((m) => [m.id, m]));
|
|
486
|
+
const view = (m) => this.view(m.id) ?? {
|
|
487
|
+
id: m.id, text: m.text, kind: m.kind, tier: m.tier, author: m.author ?? null, confirmedBy: m.confirmedBy ?? [], mine: m.tier === "own",
|
|
488
|
+
};
|
|
489
|
+
const items = [];
|
|
490
|
+
const pairs = new Set();
|
|
491
|
+
const pairKey = (a, b) => [a, b].sort().join("|");
|
|
492
|
+
// Corrections waiting on a decision: a live memory that supersedes another live one.
|
|
493
|
+
for (const t of this.cache.values()) {
|
|
494
|
+
if (t.state !== "live" || !t.supersedes)
|
|
495
|
+
continue;
|
|
496
|
+
const target = this.cache.get(t.supersedes);
|
|
497
|
+
if (!target || target.state !== "live")
|
|
498
|
+
continue;
|
|
499
|
+
const a = byId.get(t.id);
|
|
500
|
+
const b = byId.get(target.id);
|
|
501
|
+
if (!a || !b)
|
|
502
|
+
continue;
|
|
503
|
+
pairs.add(pairKey(a.id, b.id));
|
|
504
|
+
items.push({ id: `correction:${pairKey(a.id, b.id)}`, type: "correction", detail: `${t.author} corrected ${target.author}`, a: view(a), b: view(b) });
|
|
505
|
+
}
|
|
506
|
+
// Contradictions across people (a teammate or canon on one side).
|
|
507
|
+
for (const c of findConflicts(pool)) {
|
|
508
|
+
const a = byId.get(c.a.id);
|
|
509
|
+
const b = byId.get(c.b.id);
|
|
510
|
+
if (a.tier === "own" && b.tier === "own")
|
|
511
|
+
continue; // the solo brain's own conflict view covers these
|
|
512
|
+
if (a.tier === "canon" && b.tier === "canon")
|
|
513
|
+
continue;
|
|
514
|
+
const k = pairKey(a.id, b.id);
|
|
515
|
+
if (pairs.has(k))
|
|
516
|
+
continue;
|
|
517
|
+
pairs.add(k);
|
|
518
|
+
items.push({ id: `contradiction:${k}`, type: "contradiction", detail: c.signal === "negation" ? "one says the opposite of the other" : "same topic, different answer", a: view(a), b: view(b) });
|
|
519
|
+
}
|
|
520
|
+
// Near-duplicates the HMAC couldn't merge (D41: exact merges, near flags).
|
|
521
|
+
const vecs = new Map(pool.map((m) => [m.id, trigramVector(m.text)]));
|
|
522
|
+
for (let i = 0; i < pool.length; i++) {
|
|
523
|
+
for (let j = i + 1; j < pool.length; j++) {
|
|
524
|
+
const a = pool[i];
|
|
525
|
+
const b = pool[j];
|
|
526
|
+
if (a.kind !== b.kind || (a.tier === "own" && b.tier === "own") || (a.tier === "canon" && b.tier === "canon"))
|
|
527
|
+
continue;
|
|
528
|
+
const k = pairKey(a.id, b.id);
|
|
529
|
+
if (pairs.has(k))
|
|
530
|
+
continue;
|
|
531
|
+
if (cosine(vecs.get(a.id), vecs.get(b.id)) < 0.92)
|
|
532
|
+
continue;
|
|
533
|
+
pairs.add(k);
|
|
534
|
+
items.push({ id: `duplicate:${k}`, type: "duplicate", detail: "nearly the same memory, worded differently", a: view(a), b: view(b) });
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
// Learned from outside content: needs a human before it can reach anyone (D43).
|
|
538
|
+
for (const m of this.rt.brain.all()) {
|
|
539
|
+
if (!m.untrusted || m.private || !DURABLE.has(m.kind))
|
|
540
|
+
continue;
|
|
541
|
+
items.push({ id: `untrusted:${m.id}`, type: "untrusted", detail: "learned while reading outside content — trust it to share it", a: view({ ...m, tier: "own" }) });
|
|
542
|
+
}
|
|
543
|
+
// Confirmed by 2+ members, not canon yet: worth a PR.
|
|
544
|
+
for (const m of pool) {
|
|
545
|
+
if (m.tier !== "confirmed" || m.kind === "task")
|
|
546
|
+
continue;
|
|
547
|
+
items.push({ id: `promote:${m.id}`, type: "promote", detail: `confirmed by ${m.confirmedBy?.length ?? 2} teammates — make it canon?`, a: view(m) });
|
|
548
|
+
}
|
|
549
|
+
return items;
|
|
550
|
+
}
|
|
551
|
+
// ── live team context (D48) ──
|
|
552
|
+
/**
|
|
553
|
+
* What teammates are doing near this work, right now: their leases on these
|
|
554
|
+
* paths, open PRs that change them, failing checks, predicted conflicts.
|
|
555
|
+
* ≤1.5k chars; "" when there's nothing near.
|
|
556
|
+
*/
|
|
557
|
+
context(files = []) {
|
|
558
|
+
if (!this.share)
|
|
559
|
+
return "";
|
|
560
|
+
const me = this.deps.github();
|
|
561
|
+
const lines = [];
|
|
562
|
+
const scope = { globs: files, files, prefixes: files };
|
|
563
|
+
const now = Date.now();
|
|
564
|
+
for (const l of this.deps.leases(this.share.teamId)) {
|
|
565
|
+
if (l.repo !== this.share.repo || l.github === me || now - l.ts > LEASE_TTL_MS)
|
|
566
|
+
continue;
|
|
567
|
+
const hit = files.length ? overlap(scope, { globs: l.globs, files: l.files, prefixes: l.prefixes }) : [];
|
|
568
|
+
if (files.length && !hit.length)
|
|
569
|
+
continue;
|
|
570
|
+
const who = l.github || "a teammate";
|
|
571
|
+
const intent = openFromTeam(this.deps.keys(this.share.teamId), l.sealed);
|
|
572
|
+
lines.push(`- ${who}'s agent holds ${(hit.length ? hit : l.globs).slice(0, 4).join(", ")}${intent?.task ? ` — "${intent.task.slice(0, 80)}"` : ""}${l.state === "landing" ? " (landing, PR open)" : ""}`);
|
|
573
|
+
}
|
|
574
|
+
const feed = this.deps.feed(this.share.teamId).filter((e) => e.repo === this.share.repo);
|
|
575
|
+
const closed = new Set(feed.filter((e) => e.type === "pr_merged" || e.type === "pr_closed").map((e) => Number(e.meta.number)));
|
|
576
|
+
const fileSet = new Set(files);
|
|
577
|
+
for (const e of feed) {
|
|
578
|
+
if (e.type !== "pr_opened" || closed.has(Number(e.meta.number)) || e.meta.number === undefined)
|
|
579
|
+
continue;
|
|
580
|
+
if (e.meta.author && e.meta.author === me)
|
|
581
|
+
continue;
|
|
582
|
+
const prFiles = Array.isArray(e.meta.files) ? e.meta.files : [];
|
|
583
|
+
const touching = files.length ? prFiles.filter((f) => fileSet.has(f) || files.some((x) => f.startsWith(x.replace(/\/?$/, "/")))) : [];
|
|
584
|
+
if (!touching.length)
|
|
585
|
+
continue;
|
|
586
|
+
lines.push(`- open PR #${String(e.meta.number)} by ${String(e.meta.author ?? "a teammate")} also changes ${touching.slice(0, 4).join(", ")}`);
|
|
587
|
+
}
|
|
588
|
+
const day = now - 24 * 60 * 60_000;
|
|
589
|
+
for (const e of feed) {
|
|
590
|
+
if (e.type !== "check_failed" || e.ts < day || closed.has(Number(e.meta.number)))
|
|
591
|
+
continue;
|
|
592
|
+
const checks = Array.isArray(e.meta.checks) ? e.meta.checks : [];
|
|
593
|
+
lines.push(`- checks failing on PR #${String(e.meta.number)}: ${checks.slice(0, 3).join(", ")}`);
|
|
594
|
+
}
|
|
595
|
+
for (const e of feed) {
|
|
596
|
+
if (e.type !== "conflict_predicted" || e.ts < day)
|
|
597
|
+
continue;
|
|
598
|
+
const paths = Array.isArray(e.meta.paths) ? e.meta.paths : [];
|
|
599
|
+
if (files.length && !paths.some((p) => fileSet.has(p)))
|
|
600
|
+
continue;
|
|
601
|
+
lines.push(`- a merge conflict is predicted on ${paths.slice(0, 4).join(", ") || "shared files"}`);
|
|
602
|
+
}
|
|
603
|
+
if (!lines.length)
|
|
604
|
+
return "";
|
|
605
|
+
const head = "## Your team, right now (live — near this work)";
|
|
606
|
+
let out = head;
|
|
607
|
+
for (const l of [...new Set(lines)]) {
|
|
608
|
+
if (out.length + l.length + 1 > CONTEXT_CHARS)
|
|
609
|
+
break;
|
|
610
|
+
out += "\n" + l;
|
|
611
|
+
}
|
|
612
|
+
return out;
|
|
613
|
+
}
|
|
614
|
+
status() {
|
|
615
|
+
const live = [...this.cache.values()].filter((t) => t.state === "live");
|
|
616
|
+
return {
|
|
617
|
+
shared: Boolean(this.share),
|
|
618
|
+
teamId: this.share?.teamId ?? null,
|
|
619
|
+
repo: this.share?.repo ?? null,
|
|
620
|
+
canon: this.canon.length,
|
|
621
|
+
team: live.length,
|
|
622
|
+
confirmed: live.filter((t) => t.confirmedBy.length >= 2).length,
|
|
623
|
+
mine: live.filter((t) => t.author === this.deps.github()).length,
|
|
624
|
+
lastError: this.lastError,
|
|
625
|
+
};
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
function normText(t) {
|
|
629
|
+
return t.trim().toLowerCase().replace(/\s+/g, " ");
|
|
630
|
+
}
|
|
631
|
+
function sealedOf(m) {
|
|
632
|
+
return { text: m.text, kind: m.kind, entities: m.entities.slice(0, 30), confidence: m.confidence };
|
|
633
|
+
}
|
|
634
|
+
function asMemory(id, s, createdAt, updatedAt, by) {
|
|
635
|
+
return {
|
|
636
|
+
id,
|
|
637
|
+
kind: s.kind,
|
|
638
|
+
text: s.text,
|
|
639
|
+
entities: s.entities ?? [],
|
|
640
|
+
scope: {},
|
|
641
|
+
provenance: { agentId: `@${by}`, eventId: 0, ts: createdAt },
|
|
642
|
+
confidence: s.confidence ?? 1,
|
|
643
|
+
lemmas: lemmatize(s.text),
|
|
644
|
+
hash: memoryHash(s.text),
|
|
645
|
+
createdAt,
|
|
646
|
+
updatedAt,
|
|
647
|
+
};
|
|
648
|
+
}
|
|
649
|
+
function canonPrBody() {
|
|
650
|
+
return [
|
|
651
|
+
"Loom proposes these memories as **team canon** — things every agent on the team (Loom or not) treats as settled.",
|
|
652
|
+
"",
|
|
653
|
+
"They live in a managed section of `AGENTS.md`. Review them like code: edit or delete a line to change canon, or add a line by hand.",
|
|
654
|
+
"This PR rolls: Loom adds to it until it merges.",
|
|
655
|
+
"",
|
|
656
|
+
"🤖 Generated with [Loom](https://github.com/nickthelegend/loom)",
|
|
657
|
+
].join("\n");
|
|
658
|
+
}
|
|
659
|
+
//# sourceMappingURL=team-brain.js.map
|