toolroll 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +1113 -0
- package/THIRD_PARTY_NOTICES.md +449 -0
- package/dist/accent-colors.d.ts +48 -0
- package/dist/accent-colors.js +122 -0
- package/dist/action-ledger.d.ts +26 -0
- package/dist/action-ledger.js +75 -0
- package/dist/agent-fence.d.ts +42 -0
- package/dist/agent-fence.js +183 -0
- package/dist/agentconfig.d.ts +216 -0
- package/dist/agentconfig.js +454 -0
- package/dist/api-tokens.d.ts +16 -0
- package/dist/api-tokens.js +27 -0
- package/dist/approval-policy.d.ts +69 -0
- package/dist/approval-policy.js +137 -0
- package/dist/approval-rules-ui.d.ts +25 -0
- package/dist/approval-rules-ui.js +39 -0
- package/dist/assignment-adapters.d.ts +180 -0
- package/dist/assignment-adapters.js +239 -0
- package/dist/assignment-brief.d.ts +71 -0
- package/dist/assignment-brief.js +129 -0
- package/dist/assignment-delivery.d.ts +56 -0
- package/dist/assignment-delivery.js +160 -0
- package/dist/assignment-presentation.d.ts +26 -0
- package/dist/assignment-presentation.js +80 -0
- package/dist/assignment-status.d.ts +62 -0
- package/dist/assignment-status.js +152 -0
- package/dist/assignment-ui.d.ts +71 -0
- package/dist/assignment-ui.js +103 -0
- package/dist/assignment.d.ts +222 -0
- package/dist/assignment.js +399 -0
- package/dist/attest.d.ts +56 -0
- package/dist/attest.js +153 -0
- package/dist/backend.d.ts +97 -0
- package/dist/backend.js +166 -0
- package/dist/backup-ui.d.ts +22 -0
- package/dist/backup-ui.js +59 -0
- package/dist/backup.d.ts +84 -0
- package/dist/backup.js +421 -0
- package/dist/beads.d.ts +34 -0
- package/dist/beads.js +135 -0
- package/dist/bin.d.ts +2 -0
- package/dist/bin.js +22 -0
- package/dist/board.d.ts +150 -0
- package/dist/board.js +210 -0
- package/dist/boot-identity.d.ts +63 -0
- package/dist/boot-identity.js +99 -0
- package/dist/browser/THIRD_PARTY_NOTICES.txt +2295 -0
- package/dist/browser/workspace.css +4 -0
- package/dist/browser/workspace.js +225 -0
- package/dist/browser-crew.d.ts +11 -0
- package/dist/browser-crew.js +50 -0
- package/dist/browser-shell.d.ts +8 -0
- package/dist/browser-shell.js +60 -0
- package/dist/browser-workspace.d.ts +780 -0
- package/dist/browser-workspace.js +47 -0
- package/dist/budget-alerts.d.ts +19 -0
- package/dist/budget-alerts.js +46 -0
- package/dist/builder.d.ts +370 -0
- package/dist/builder.js +3262 -0
- package/dist/capscan.d.ts +35 -0
- package/dist/capscan.js +113 -0
- package/dist/chat-acceptance.d.ts +71 -0
- package/dist/chat-acceptance.js +120 -0
- package/dist/chat-actions.d.ts +265 -0
- package/dist/chat-actions.js +1273 -0
- package/dist/chat-channel.d.ts +109 -0
- package/dist/chat-channel.js +652 -0
- package/dist/chat-continuity.d.ts +32 -0
- package/dist/chat-continuity.js +317 -0
- package/dist/chat-controls.d.ts +113 -0
- package/dist/chat-controls.js +53 -0
- package/dist/chat-delivery-state.d.ts +159 -0
- package/dist/chat-delivery-state.js +309 -0
- package/dist/chat-delivery.d.ts +38 -0
- package/dist/chat-delivery.js +693 -0
- package/dist/chat-display.d.ts +1 -0
- package/dist/chat-display.js +18 -0
- package/dist/chat-evidence.d.ts +123 -0
- package/dist/chat-evidence.js +207 -0
- package/dist/chat-flow.d.ts +44 -0
- package/dist/chat-flow.js +215 -0
- package/dist/chat-inbox.d.ts +57 -0
- package/dist/chat-inbox.js +123 -0
- package/dist/chat-polish.d.ts +16 -0
- package/dist/chat-polish.js +135 -0
- package/dist/chat-review.d.ts +46 -0
- package/dist/chat-review.js +98 -0
- package/dist/chat-rooms.d.ts +123 -0
- package/dist/chat-rooms.js +216 -0
- package/dist/chat-task-actions.d.ts +44 -0
- package/dist/chat-task-actions.js +79 -0
- package/dist/check-progress.d.ts +51 -0
- package/dist/check-progress.js +290 -0
- package/dist/child-database.d.ts +12 -0
- package/dist/child-database.js +30 -0
- package/dist/claim.d.ts +688 -0
- package/dist/claim.js +1740 -0
- package/dist/cli.d.ts +137 -0
- package/dist/cli.js +1461 -0
- package/dist/codex-limits.d.ts +15 -0
- package/dist/codex-limits.js +145 -0
- package/dist/coding-context.d.ts +55 -0
- package/dist/coding-context.js +92 -0
- package/dist/coding-handoff.d.ts +57 -0
- package/dist/coding-handoff.js +277 -0
- package/dist/coding-provider.d.ts +72 -0
- package/dist/coding-provider.js +424 -0
- package/dist/coding-shipping-ui.d.ts +4 -0
- package/dist/coding-shipping-ui.js +12 -0
- package/dist/coding-types.d.ts +62 -0
- package/dist/coding-types.js +1 -0
- package/dist/coding-ui.d.ts +17 -0
- package/dist/coding-ui.js +364 -0
- package/dist/coding-update.d.ts +15 -0
- package/dist/coding-update.js +149 -0
- package/dist/coding-workspace.d.ts +124 -0
- package/dist/coding-workspace.js +768 -0
- package/dist/container-state.d.ts +5 -0
- package/dist/container-state.js +62 -0
- package/dist/containment.d.ts +197 -0
- package/dist/containment.js +559 -0
- package/dist/contest.d.ts +318 -0
- package/dist/contest.js +753 -0
- package/dist/control-setup.d.ts +35 -0
- package/dist/control-setup.js +40 -0
- package/dist/control-ui.d.ts +29 -0
- package/dist/control-ui.js +53 -0
- package/dist/controller-service.d.ts +1 -0
- package/dist/controller-service.js +19 -0
- package/dist/controller-supervisor.d.ts +30 -0
- package/dist/controller-supervisor.js +104 -0
- package/dist/converse.d.ts +304 -0
- package/dist/converse.js +849 -0
- package/dist/coordinator-proposals.d.ts +29 -0
- package/dist/coordinator-proposals.js +137 -0
- package/dist/coordinator.d.ts +186 -0
- package/dist/coordinator.js +447 -0
- package/dist/credentials-ui.d.ts +25 -0
- package/dist/credentials-ui.js +48 -0
- package/dist/daemon.d.ts +209 -0
- package/dist/daemon.js +604 -0
- package/dist/decision.d.ts +120 -0
- package/dist/decision.js +388 -0
- package/dist/demo.d.ts +55 -0
- package/dist/demo.js +992 -0
- package/dist/desktop-access.d.ts +28 -0
- package/dist/desktop-access.js +88 -0
- package/dist/desktop-bundle.d.ts +16 -0
- package/dist/desktop-bundle.js +74 -0
- package/dist/desktop-host.d.ts +54 -0
- package/dist/desktop-host.js +508 -0
- package/dist/desktop-update-gate.d.ts +12 -0
- package/dist/desktop-update-gate.js +91 -0
- package/dist/desktop-update-recovery.d.ts +14 -0
- package/dist/desktop-update-recovery.js +243 -0
- package/dist/desktop-update.d.ts +110 -0
- package/dist/desktop-update.js +740 -0
- package/dist/discord-api.d.ts +20 -0
- package/dist/discord-api.js +138 -0
- package/dist/discord-chat.d.ts +16 -0
- package/dist/discord-chat.js +381 -0
- package/dist/discord-settings.d.ts +7 -0
- package/dist/discord-settings.js +75 -0
- package/dist/discord.d.ts +10 -0
- package/dist/discord.js +169 -0
- package/dist/discover.d.ts +75 -0
- package/dist/discover.js +150 -0
- package/dist/dispatch.d.ts +100 -0
- package/dist/dispatch.js +311 -0
- package/dist/dispose.d.ts +161 -0
- package/dist/dispose.js +803 -0
- package/dist/email-settings.d.ts +34 -0
- package/dist/email-settings.js +50 -0
- package/dist/envelope.d.ts +47 -0
- package/dist/envelope.js +105 -0
- package/dist/evidence-pack.d.ts +185 -0
- package/dist/evidence-pack.js +258 -0
- package/dist/evidence.d.ts +345 -0
- package/dist/evidence.js +782 -0
- package/dist/exec.d.ts +285 -0
- package/dist/exec.js +1841 -0
- package/dist/exhaustion.d.ts +85 -0
- package/dist/exhaustion.js +141 -0
- package/dist/export-ui.d.ts +4 -0
- package/dist/export-ui.js +17 -0
- package/dist/export.d.ts +48 -0
- package/dist/export.js +305 -0
- package/dist/flow-actions.d.ts +77 -0
- package/dist/flow-actions.js +203 -0
- package/dist/flow-code.d.ts +50 -0
- package/dist/flow-code.js +159 -0
- package/dist/flow-draft.d.ts +38 -0
- package/dist/flow-draft.js +80 -0
- package/dist/flow-engine.d.ts +66 -0
- package/dist/flow-engine.js +416 -0
- package/dist/flow-insights.d.ts +95 -0
- package/dist/flow-insights.js +149 -0
- package/dist/flow-live.d.ts +24 -0
- package/dist/flow-live.js +150 -0
- package/dist/flow-people.d.ts +24 -0
- package/dist/flow-people.js +91 -0
- package/dist/flow-replies.d.ts +48 -0
- package/dist/flow-replies.js +113 -0
- package/dist/flow-scripts.d.ts +38 -0
- package/dist/flow-scripts.js +73 -0
- package/dist/flow-secrets.d.ts +13 -0
- package/dist/flow-secrets.js +45 -0
- package/dist/flow-sort.d.ts +82 -0
- package/dist/flow-sort.js +153 -0
- package/dist/flow-steps.d.ts +40 -0
- package/dist/flow-steps.js +408 -0
- package/dist/flow-triggers.d.ts +244 -0
- package/dist/flow-triggers.js +959 -0
- package/dist/flows-ui.d.ts +20 -0
- package/dist/flows-ui.js +175 -0
- package/dist/flows.d.ts +229 -0
- package/dist/flows.js +968 -0
- package/dist/fonts.d.ts +19 -0
- package/dist/fonts.js +19 -0
- package/dist/gaps.d.ts +35 -0
- package/dist/gaps.js +101 -0
- package/dist/gate-failure.d.ts +40 -0
- package/dist/gate-failure.js +66 -0
- package/dist/git.d.ts +54 -0
- package/dist/git.js +94 -0
- package/dist/google-mail.d.ts +58 -0
- package/dist/google-mail.js +151 -0
- package/dist/grant.d.ts +136 -0
- package/dist/grant.js +238 -0
- package/dist/graph.d.ts +164 -0
- package/dist/graph.js +383 -0
- package/dist/guides.d.ts +22 -0
- package/dist/guides.js +363 -0
- package/dist/held.d.ts +150 -0
- package/dist/held.js +799 -0
- package/dist/invoke.d.ts +112 -0
- package/dist/invoke.js +780 -0
- package/dist/issues.d.ts +41 -0
- package/dist/issues.js +131 -0
- package/dist/job-object-helper.ps1 +252 -0
- package/dist/jsonl-discriminants.d.ts +11 -0
- package/dist/jsonl-discriminants.js +93 -0
- package/dist/keys.d.ts +104 -0
- package/dist/keys.js +229 -0
- package/dist/kits-ui.d.ts +17 -0
- package/dist/kits-ui.js +46 -0
- package/dist/kits.d.ts +86 -0
- package/dist/kits.js +215 -0
- package/dist/knowledge-cli.d.ts +101 -0
- package/dist/knowledge-cli.js +92 -0
- package/dist/knowledge-ui.d.ts +24 -0
- package/dist/knowledge-ui.js +40 -0
- package/dist/lead-context.d.ts +8 -0
- package/dist/lead-context.js +56 -0
- package/dist/lead-follow.d.ts +22 -0
- package/dist/lead-follow.js +167 -0
- package/dist/lead-status.d.ts +75 -0
- package/dist/lead-status.js +285 -0
- package/dist/ledger-chain.d.ts +46 -0
- package/dist/ledger-chain.js +187 -0
- package/dist/ledger-csv.d.ts +4 -0
- package/dist/ledger-csv.js +11 -0
- package/dist/ledger-view.d.ts +24 -0
- package/dist/ledger-view.js +58 -0
- package/dist/limits-ui.d.ts +26 -0
- package/dist/limits-ui.js +67 -0
- package/dist/link.d.ts +72 -0
- package/dist/link.js +217 -0
- package/dist/live.d.ts +96 -0
- package/dist/live.js +366 -0
- package/dist/liveness.d.ts +30 -0
- package/dist/liveness.js +42 -0
- package/dist/log.d.ts +7 -0
- package/dist/log.js +25 -0
- package/dist/mailbox.d.ts +57 -0
- package/dist/mailbox.js +150 -0
- package/dist/maintenance.d.ts +11 -0
- package/dist/maintenance.js +35 -0
- package/dist/mate-cli.d.ts +52 -0
- package/dist/mate-cli.js +345 -0
- package/dist/mate-contract.d.ts +10 -0
- package/dist/mate-contract.js +30 -0
- package/dist/mate-doors.d.ts +69 -0
- package/dist/mate-doors.js +548 -0
- package/dist/mate-progress.d.ts +29 -0
- package/dist/mate-progress.js +121 -0
- package/dist/mate-tools.d.ts +94 -0
- package/dist/mate-tools.js +1878 -0
- package/dist/mate.d.ts +92 -0
- package/dist/mate.js +435 -0
- package/dist/mcp-connect.d.ts +103 -0
- package/dist/mcp-connect.js +252 -0
- package/dist/mcp.d.ts +27 -0
- package/dist/mcp.js +651 -0
- package/dist/memory-cli.d.ts +466 -0
- package/dist/memory-cli.js +111 -0
- package/dist/memory-pass.d.ts +160 -0
- package/dist/memory-pass.js +409 -0
- package/dist/metrics.d.ts +3 -0
- package/dist/metrics.js +56 -0
- package/dist/mobile-viewport.d.ts +3 -0
- package/dist/mobile-viewport.js +37 -0
- package/dist/model-catalog.d.ts +118 -0
- package/dist/model-catalog.js +376 -0
- package/dist/models-cli.d.ts +102 -0
- package/dist/models-cli.js +66 -0
- package/dist/models-ui.d.ts +34 -0
- package/dist/models-ui.js +68 -0
- package/dist/modes.d.ts +80 -0
- package/dist/modes.js +182 -0
- package/dist/monitoring-settings.d.ts +44 -0
- package/dist/monitoring-settings.js +173 -0
- package/dist/monitoring-ui.d.ts +20 -0
- package/dist/monitoring-ui.js +51 -0
- package/dist/monitoring.d.ts +34 -0
- package/dist/monitoring.js +221 -0
- package/dist/names.d.ts +67 -0
- package/dist/names.js +102 -0
- package/dist/observations.d.ts +40 -0
- package/dist/observations.js +211 -0
- package/dist/oidc.d.ts +71 -0
- package/dist/oidc.js +182 -0
- package/dist/onboard.d.ts +108 -0
- package/dist/onboard.js +325 -0
- package/dist/openrouter-models.d.ts +30 -0
- package/dist/openrouter-models.js +120 -0
- package/dist/operate.d.ts +118 -0
- package/dist/operate.js +11321 -0
- package/dist/peek-cli.d.ts +97 -0
- package/dist/peek-cli.js +215 -0
- package/dist/peek.d.ts +134 -0
- package/dist/peek.js +578 -0
- package/dist/phase-routing.d.ts +307 -0
- package/dist/phase-routing.js +658 -0
- package/dist/plan-auto.d.ts +14 -0
- package/dist/plan-auto.js +92 -0
- package/dist/plan.d.ts +186 -0
- package/dist/plan.js +401 -0
- package/dist/planner-source.d.ts +251 -0
- package/dist/planner-source.js +460 -0
- package/dist/planner.d.ts +75 -0
- package/dist/planner.js +992 -0
- package/dist/policy-ui.d.ts +20 -0
- package/dist/policy-ui.js +41 -0
- package/dist/policy.d.ts +111 -0
- package/dist/policy.js +231 -0
- package/dist/prepared-evidence.d.ts +16 -0
- package/dist/prepared-evidence.js +96 -0
- package/dist/pricing.d.ts +45 -0
- package/dist/pricing.js +77 -0
- package/dist/principal.d.ts +42 -0
- package/dist/principal.js +82 -0
- package/dist/probe.d.ts +46 -0
- package/dist/probe.js +79 -0
- package/dist/process-custody.d.ts +17 -0
- package/dist/process-custody.js +91 -0
- package/dist/process-liveness.d.ts +10 -0
- package/dist/process-liveness.js +81 -0
- package/dist/process-recovery-anchor.d.ts +75 -0
- package/dist/process-recovery-anchor.js +188 -0
- package/dist/process-recovery-coalition.d.ts +25 -0
- package/dist/process-recovery-coalition.js +105 -0
- package/dist/process-recovery-eligibility.d.ts +178 -0
- package/dist/process-recovery-eligibility.js +170 -0
- package/dist/process-recovery-native.d.ts +172 -0
- package/dist/process-recovery-native.js +592 -0
- package/dist/process-recovery-provenance.d.ts +163 -0
- package/dist/process-recovery-provenance.js +292 -0
- package/dist/process-recovery-services.d.ts +56 -0
- package/dist/process-recovery-services.js +191 -0
- package/dist/process-recovery-settlement.d.ts +32 -0
- package/dist/process-recovery-settlement.js +132 -0
- package/dist/process-recovery.d.ts +41 -0
- package/dist/process-recovery.js +91 -0
- package/dist/process-tree.d.ts +41 -0
- package/dist/process-tree.js +264 -0
- package/dist/project-access.d.ts +14 -0
- package/dist/project-access.js +34 -0
- package/dist/project-cli.d.ts +18 -0
- package/dist/project-cli.js +104 -0
- package/dist/project-delete-ui.d.ts +22 -0
- package/dist/project-delete-ui.js +48 -0
- package/dist/project-delete.d.ts +55 -0
- package/dist/project-delete.js +413 -0
- package/dist/project-knowledge.d.ts +111 -0
- package/dist/project-knowledge.js +241 -0
- package/dist/project-learning.d.ts +100 -0
- package/dist/project-learning.js +438 -0
- package/dist/project-memory.d.ts +81 -0
- package/dist/project-memory.js +264 -0
- package/dist/project-skills.d.ts +152 -0
- package/dist/project-skills.js +660 -0
- package/dist/project-tools.d.ts +219 -0
- package/dist/project-tools.js +796 -0
- package/dist/project.d.ts +61 -0
- package/dist/project.js +125 -0
- package/dist/prompt.d.ts +22 -0
- package/dist/prompt.js +72 -0
- package/dist/proof.d.ts +513 -0
- package/dist/proof.js +1140 -0
- package/dist/proposal.d.ts +82 -0
- package/dist/proposal.js +210 -0
- package/dist/provider-connection.d.ts +22 -0
- package/dist/provider-connection.js +98 -0
- package/dist/provider-limits.d.ts +35 -0
- package/dist/provider-limits.js +95 -0
- package/dist/provider.d.ts +279 -0
- package/dist/provider.js +935 -0
- package/dist/publish.d.ts +107 -0
- package/dist/publish.js +650 -0
- package/dist/pulls.d.ts +118 -0
- package/dist/pulls.js +240 -0
- package/dist/push.d.ts +95 -0
- package/dist/push.js +353 -0
- package/dist/quality.d.ts +8 -0
- package/dist/quality.js +6 -0
- package/dist/recipe-ui.d.ts +18 -0
- package/dist/recipe-ui.js +152 -0
- package/dist/recipes.d.ts +81 -0
- package/dist/recipes.js +332 -0
- package/dist/remote.d.ts +67 -0
- package/dist/remote.js +122 -0
- package/dist/render.d.ts +64 -0
- package/dist/render.js +587 -0
- package/dist/report-summary.d.ts +13 -0
- package/dist/report-summary.js +15 -0
- package/dist/repos.d.ts +65 -0
- package/dist/repos.js +277 -0
- package/dist/repository-context-ui.d.ts +2 -0
- package/dist/repository-context-ui.js +10 -0
- package/dist/repository-context.d.ts +67 -0
- package/dist/repository-context.js +376 -0
- package/dist/restart-certification.d.ts +134 -0
- package/dist/restart-certification.js +226 -0
- package/dist/result-actions.d.ts +28 -0
- package/dist/result-actions.js +270 -0
- package/dist/result-completion.d.ts +18 -0
- package/dist/result-completion.js +66 -0
- package/dist/result-review.d.ts +213 -0
- package/dist/result-review.js +430 -0
- package/dist/retention-ui.d.ts +12 -0
- package/dist/retention-ui.js +39 -0
- package/dist/retention.d.ts +72 -0
- package/dist/retention.js +288 -0
- package/dist/review-context.d.ts +243 -0
- package/dist/review-context.js +1043 -0
- package/dist/review-evidence.d.ts +33 -0
- package/dist/review-evidence.js +56 -0
- package/dist/reviewer.d.ts +97 -0
- package/dist/reviewer.js +202 -0
- package/dist/routine.d.ts +228 -0
- package/dist/routine.js +764 -0
- package/dist/runner.d.ts +266 -0
- package/dist/runner.js +399 -0
- package/dist/scan.d.ts +32 -0
- package/dist/scan.js +92 -0
- package/dist/scope.d.ts +695 -0
- package/dist/scope.js +1176 -0
- package/dist/scout-report.d.ts +42 -0
- package/dist/scout-report.js +108 -0
- package/dist/scout.d.ts +69 -0
- package/dist/scout.js +351 -0
- package/dist/serve.d.ts +313 -0
- package/dist/serve.js +21776 -0
- package/dist/session-brief.d.ts +8 -0
- package/dist/session-brief.js +24 -0
- package/dist/session-cli.d.ts +13 -0
- package/dist/session-cli.js +284 -0
- package/dist/session-contract.d.ts +183 -0
- package/dist/session-contract.js +122 -0
- package/dist/session-http.d.ts +19 -0
- package/dist/session-http.js +154 -0
- package/dist/session-server.d.ts +12 -0
- package/dist/session-server.js +37 -0
- package/dist/session-service.d.ts +22 -0
- package/dist/session-service.js +157 -0
- package/dist/setup-guide.d.ts +39 -0
- package/dist/setup-guide.js +93 -0
- package/dist/sign-in-guard.d.ts +45 -0
- package/dist/sign-in-guard.js +74 -0
- package/dist/skills-ui.d.ts +11 -0
- package/dist/skills-ui.js +58 -0
- package/dist/skills.d.ts +97 -0
- package/dist/skills.js +276 -0
- package/dist/slack-api.d.ts +55 -0
- package/dist/slack-api.js +229 -0
- package/dist/slack-chat.d.ts +28 -0
- package/dist/slack-chat.js +413 -0
- package/dist/slack-settings.d.ts +7 -0
- package/dist/slack-settings.js +75 -0
- package/dist/slack-state.d.ts +13 -0
- package/dist/slack-state.js +9 -0
- package/dist/slack.d.ts +13 -0
- package/dist/slack.js +167 -0
- package/dist/spend-ui.d.ts +28 -0
- package/dist/spend-ui.js +95 -0
- package/dist/spend.d.ts +142 -0
- package/dist/spend.js +313 -0
- package/dist/sqlite-runtime.d.ts +3 -0
- package/dist/sqlite-runtime.js +6 -0
- package/dist/sso-settings.d.ts +33 -0
- package/dist/sso-settings.js +103 -0
- package/dist/sso-ui.d.ts +22 -0
- package/dist/sso-ui.js +39 -0
- package/dist/storage.d.ts +15 -0
- package/dist/storage.js +84 -0
- package/dist/store.d.ts +7754 -0
- package/dist/store.js +23279 -0
- package/dist/structured-output.d.ts +58 -0
- package/dist/structured-output.js +103 -0
- package/dist/style-asset.d.ts +7 -0
- package/dist/style-asset.js +41 -0
- package/dist/subscription-chat.d.ts +46 -0
- package/dist/subscription-chat.js +275 -0
- package/dist/summary.d.ts +33 -0
- package/dist/summary.js +71 -0
- package/dist/supervisor.mjs +297 -0
- package/dist/surface.d.ts +63 -0
- package/dist/surface.js +352 -0
- package/dist/sync.d.ts +94 -0
- package/dist/sync.js +279 -0
- package/dist/task-composer.d.ts +13 -0
- package/dist/task-composer.js +52 -0
- package/dist/task-control.d.ts +165 -0
- package/dist/task-control.js +214 -0
- package/dist/task-outcome-cli.d.ts +10 -0
- package/dist/task-outcome-cli.js +92 -0
- package/dist/task-text.d.ts +30 -0
- package/dist/task-text.js +41 -0
- package/dist/team-cli.d.ts +15 -0
- package/dist/team-cli.js +618 -0
- package/dist/team-contract.d.ts +92 -0
- package/dist/team-contract.js +1 -0
- package/dist/team-http.d.ts +17 -0
- package/dist/team-http.js +144 -0
- package/dist/team-leads.d.ts +81 -0
- package/dist/team-leads.js +565 -0
- package/dist/team-runtime.d.ts +27 -0
- package/dist/team-runtime.js +264 -0
- package/dist/team-ui.d.ts +3 -0
- package/dist/team-ui.js +9 -0
- package/dist/team-updates.d.ts +8 -0
- package/dist/team-updates.js +57 -0
- package/dist/teammate-admin.d.ts +53 -0
- package/dist/teammate-admin.js +111 -0
- package/dist/teammate-desk.d.ts +60 -0
- package/dist/teammate-desk.js +154 -0
- package/dist/teammate-memory.d.ts +64 -0
- package/dist/teammate-memory.js +196 -0
- package/dist/teammate-question.d.ts +59 -0
- package/dist/teammate-question.js +175 -0
- package/dist/teammate-tools.d.ts +108 -0
- package/dist/teammate-tools.js +271 -0
- package/dist/teammate-week.d.ts +68 -0
- package/dist/teammate-week.js +132 -0
- package/dist/teammate-work.d.ts +76 -0
- package/dist/teammate-work.js +280 -0
- package/dist/teammates-ui.d.ts +20 -0
- package/dist/teammates-ui.js +166 -0
- package/dist/teammates.d.ts +180 -0
- package/dist/teammates.js +253 -0
- package/dist/teams-api.d.ts +33 -0
- package/dist/teams-api.js +204 -0
- package/dist/teams-chat.d.ts +26 -0
- package/dist/teams-chat.js +215 -0
- package/dist/teams-settings.d.ts +12 -0
- package/dist/teams-settings.js +57 -0
- package/dist/teams.d.ts +21 -0
- package/dist/teams.js +124 -0
- package/dist/telegram-flow.d.ts +49 -0
- package/dist/telegram-flow.js +110 -0
- package/dist/telegram-mate.d.ts +137 -0
- package/dist/telegram-mate.js +707 -0
- package/dist/telegram-progress.d.ts +14 -0
- package/dist/telegram-progress.js +129 -0
- package/dist/telegram-settings.d.ts +9 -0
- package/dist/telegram-settings.js +36 -0
- package/dist/telegram-status.d.ts +75 -0
- package/dist/telegram-status.js +265 -0
- package/dist/telegram-team.d.ts +85 -0
- package/dist/telegram-team.js +359 -0
- package/dist/telegram.d.ts +230 -0
- package/dist/telegram.js +1931 -0
- package/dist/templates.d.ts +65 -0
- package/dist/templates.js +118 -0
- package/dist/tool-launcher.d.ts +1 -0
- package/dist/tool-launcher.js +101 -0
- package/dist/tools-ui.d.ts +30 -0
- package/dist/tools-ui.js +40 -0
- package/dist/transitions-recipes.d.ts +1 -0
- package/dist/transitions-recipes.js +203 -0
- package/dist/tree-proof.d.ts +34 -0
- package/dist/tree-proof.js +58 -0
- package/dist/verification-evidence.d.ts +44 -0
- package/dist/verification-evidence.js +238 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.js +10 -0
- package/dist/webhooks.d.ts +91 -0
- package/dist/webhooks.js +283 -0
- package/dist/work-index.d.ts +68 -0
- package/dist/work-index.js +384 -0
- package/dist/work-summary.d.ts +57 -0
- package/dist/work-summary.js +116 -0
- package/dist/workspace-motion.d.ts +4 -0
- package/dist/workspace-motion.js +133 -0
- package/dist/workspace-revision.d.ts +19 -0
- package/dist/workspace-revision.js +92 -0
- package/dist/workspace-ui.d.ts +204 -0
- package/dist/workspace-ui.js +411 -0
- package/dist/worktree-notices.d.ts +13 -0
- package/dist/worktree-notices.js +17 -0
- package/dist/worktree.d.ts +249 -0
- package/dist/worktree.js +720 -0
- package/package.json +122 -0
- package/scripts/canary-assertions.mjs +42 -0
- package/scripts/crash-canary.mjs +253 -0
- package/scripts/fixtures/crash-process.mjs +74 -0
- package/scripts/fixtures/pilot-scenarios.mjs +85 -0
- package/scripts/fixtures/restart-service.mjs +27 -0
- package/scripts/launchd-certification.mjs +143 -0
- package/scripts/pilot.mjs +121 -0
- package/scripts/proof-preflight.mjs +140 -0
- package/scripts/provider-canary.mjs +379 -0
- package/scripts/recovery-canary.mjs +135 -0
- package/scripts/restart-certification.mjs +98 -0
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import type { Store } from './store.js';
|
|
2
|
+
import { performSubscriptionMateRequest } from './subscription-chat.js';
|
|
3
|
+
export declare const MEMORY_TRACE_BYTES = 40000;
|
|
4
|
+
export declare const MEMORY_MIN_SESSIONS = 2;
|
|
5
|
+
export declare const MEMORY_MAX_PROPOSALS = 6;
|
|
6
|
+
export declare const INSTRUCTION_BUDGET_BYTES = 4000;
|
|
7
|
+
export declare const MEMORY_GAP_MAX_AGE_MS: number;
|
|
8
|
+
export type MemorySource = {
|
|
9
|
+
id: string;
|
|
10
|
+
repo: string;
|
|
11
|
+
kind: 'claude' | 'codex' | 'lead' | 'crew';
|
|
12
|
+
source: string;
|
|
13
|
+
at: string;
|
|
14
|
+
trace: () => string;
|
|
15
|
+
};
|
|
16
|
+
/** The Claude Code project directory for a checkout: the path with every slash turned into a dash. */
|
|
17
|
+
export declare const claudeProjectDir: (cwd: string, home?: string) => string;
|
|
18
|
+
/** Claude Code: one JSONL per session; user/assistant text verbatim, tool calls one line, results clipped, thinking dropped. */
|
|
19
|
+
export declare function distillClaude(path: string): string;
|
|
20
|
+
/** Codex: rollout JSONL; messages and custom tool calls, outputs clipped. */
|
|
21
|
+
export declare function distillCodex(path: string): string;
|
|
22
|
+
export declare function collectSessions(store: Store, repo: string, options?: {
|
|
23
|
+
evidenceRoot?: string;
|
|
24
|
+
home?: string;
|
|
25
|
+
local?: boolean;
|
|
26
|
+
}): MemorySource[];
|
|
27
|
+
export type MemorySurface = {
|
|
28
|
+
version: string;
|
|
29
|
+
instructions: {
|
|
30
|
+
id: string;
|
|
31
|
+
text: string;
|
|
32
|
+
}[];
|
|
33
|
+
decisions: {
|
|
34
|
+
id: string;
|
|
35
|
+
claim: string;
|
|
36
|
+
}[];
|
|
37
|
+
};
|
|
38
|
+
/** The always-loaded surface: instruction lines with stable ids, active decisions with theirs. Its hash keys the analysis cache. */
|
|
39
|
+
export declare function memorySurface(store: Store, repo: string, actor: string): MemorySurface;
|
|
40
|
+
export type Verdict = {
|
|
41
|
+
positive: {
|
|
42
|
+
instruction: string;
|
|
43
|
+
effect: string;
|
|
44
|
+
quote: string;
|
|
45
|
+
}[];
|
|
46
|
+
negative: {
|
|
47
|
+
instruction: string;
|
|
48
|
+
effect: string;
|
|
49
|
+
class: 'harm' | 'non-compliance' | 'irrelevant';
|
|
50
|
+
quote: string;
|
|
51
|
+
}[];
|
|
52
|
+
gaps: {
|
|
53
|
+
mistake: string;
|
|
54
|
+
proposedInstruction: string;
|
|
55
|
+
domain: 'project' | 'orchestration';
|
|
56
|
+
quote: string;
|
|
57
|
+
matchesGap?: string;
|
|
58
|
+
}[];
|
|
59
|
+
};
|
|
60
|
+
export type MemoryAnalyzer = (input: {
|
|
61
|
+
trace: string;
|
|
62
|
+
surface: MemorySurface;
|
|
63
|
+
openGaps: {
|
|
64
|
+
key: string;
|
|
65
|
+
mistake: string;
|
|
66
|
+
}[];
|
|
67
|
+
kind: MemorySource['kind'];
|
|
68
|
+
}) => Promise<{
|
|
69
|
+
ok: true;
|
|
70
|
+
text: string;
|
|
71
|
+
} | {
|
|
72
|
+
ok: false;
|
|
73
|
+
problem: string;
|
|
74
|
+
}>;
|
|
75
|
+
export declare function analysisPrompt(surface: MemorySurface, openGaps: {
|
|
76
|
+
key: string;
|
|
77
|
+
mistake: string;
|
|
78
|
+
}[]): string;
|
|
79
|
+
/** The production analyser: one call through the configured chat provider, no tools, no history. */
|
|
80
|
+
export declare function defaultAnalyzer(store: Store, options: {
|
|
81
|
+
configDir?: string;
|
|
82
|
+
env?: NodeJS.ProcessEnv;
|
|
83
|
+
fetcher?: typeof fetch;
|
|
84
|
+
timeoutMs?: number;
|
|
85
|
+
runner?: Parameters<typeof performSubscriptionMateRequest>[1];
|
|
86
|
+
}): MemoryAnalyzer;
|
|
87
|
+
/** Parse strictly and keep only claims whose quote is really in the trace. */
|
|
88
|
+
export declare function parseVerdict(text: string, trace: string, surface: MemorySurface): Verdict | null;
|
|
89
|
+
export type PassReport = {
|
|
90
|
+
repo: string;
|
|
91
|
+
surface: string;
|
|
92
|
+
sessions: number;
|
|
93
|
+
analyzed: number;
|
|
94
|
+
cached: number;
|
|
95
|
+
failed: number;
|
|
96
|
+
problems: string[];
|
|
97
|
+
gaps: number;
|
|
98
|
+
proposals: number;
|
|
99
|
+
};
|
|
100
|
+
/**
|
|
101
|
+
* Collect, distil, analyse and aggregate for one project. Never writes memory:
|
|
102
|
+
* it fills the ledger and the proposal queue. Each session is analysed once
|
|
103
|
+
* per surface version; a failed analysis is named and retried next run.
|
|
104
|
+
*/
|
|
105
|
+
export declare function runMemoryPass(store: Store, args: {
|
|
106
|
+
repo: string;
|
|
107
|
+
actor: string;
|
|
108
|
+
analyzer: MemoryAnalyzer;
|
|
109
|
+
evidenceRoot?: string;
|
|
110
|
+
home?: string;
|
|
111
|
+
local?: boolean;
|
|
112
|
+
maxSessions?: number;
|
|
113
|
+
}, now?: Date): Promise<PassReport>;
|
|
114
|
+
export type MemoryProposal = {
|
|
115
|
+
id: number;
|
|
116
|
+
repo: string;
|
|
117
|
+
kind: 'instruction-add' | 'instruction-remove' | 'decision-add';
|
|
118
|
+
fingerprint: string;
|
|
119
|
+
title: string;
|
|
120
|
+
rationale: string;
|
|
121
|
+
beforeText: string | null;
|
|
122
|
+
afterText: string;
|
|
123
|
+
evidence: {
|
|
124
|
+
session: string;
|
|
125
|
+
quote: string;
|
|
126
|
+
}[];
|
|
127
|
+
sessions: number;
|
|
128
|
+
status: string;
|
|
129
|
+
createdAt: string;
|
|
130
|
+
surface: string;
|
|
131
|
+
};
|
|
132
|
+
/**
|
|
133
|
+
* Turn corroborated gaps and harm into a few proposals: an addition needs
|
|
134
|
+
* the gap seen in at least two distinct sessions; a removal needs harm from
|
|
135
|
+
* following the instruction in two sessions. Under the instruction budget an
|
|
136
|
+
* addition is proposed only when it fits; over it, the run says so instead
|
|
137
|
+
* of growing the file. A rejected fingerprint is not proposed again until
|
|
138
|
+
* more sessions corroborate it.
|
|
139
|
+
*/
|
|
140
|
+
export declare function synthesizeProposals(store: Store, repo: string, actor: string, surface: MemorySurface, now?: Date): number;
|
|
141
|
+
export declare function listProposals(store: Store, repo: string, status?: 'pending' | 'all'): MemoryProposal[];
|
|
142
|
+
/** The one writer: accept applies the edit through the knowledge or decision store as the person; reject remembers the fingerprint and its corroboration. */
|
|
143
|
+
export declare function decideProposal(store: Store, args: {
|
|
144
|
+
repo: string;
|
|
145
|
+
actor: string;
|
|
146
|
+
id: number;
|
|
147
|
+
decision: 'accept' | 'reject';
|
|
148
|
+
}, now?: Date): MemoryProposal;
|
|
149
|
+
export declare function memoryStatus(store: Store, repo: string, actor: string): {
|
|
150
|
+
surface: string;
|
|
151
|
+
instructionBytes: number;
|
|
152
|
+
budgetBytes: number;
|
|
153
|
+
sessions: {
|
|
154
|
+
total: number;
|
|
155
|
+
analyzed: number;
|
|
156
|
+
failed: number;
|
|
157
|
+
};
|
|
158
|
+
openGaps: number;
|
|
159
|
+
pending: number;
|
|
160
|
+
};
|
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The backward pass over project memory (modelled on backpass, 2026-09-21):
|
|
3
|
+
* sessions are the loss signal, the always-loaded instructions are the
|
|
4
|
+
* weights, and nothing lands without verbatim evidence from at least two
|
|
5
|
+
* distinct sessions and a person's accept. Four stages, all read-only until
|
|
6
|
+
* apply: collect sessions (plane threads and crew runs from the database,
|
|
7
|
+
* Claude Code and Codex session files on this machine), distil each to a
|
|
8
|
+
* bounded trace, analyse each trace once per surface version, aggregate
|
|
9
|
+
* gaps in a ledger that survives runs, then propose a few edits. `apply`
|
|
10
|
+
* is the one writer, and it writes through the ordinary knowledge and
|
|
11
|
+
* decision stores so every edit keeps its revision.
|
|
12
|
+
*/
|
|
13
|
+
import { createHash } from 'node:crypto';
|
|
14
|
+
import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
|
|
15
|
+
import { homedir } from 'node:os';
|
|
16
|
+
import { basename, join } from 'node:path';
|
|
17
|
+
import { scanForSecrets, readVerifiedArtifact } from './evidence.js';
|
|
18
|
+
import { changeKnowledge, knowledgeView } from './project-knowledge.js';
|
|
19
|
+
import { listDecisions, recordDecision } from './project-memory.js';
|
|
20
|
+
import { composeMateRequest, performMateRequest, isDirectChatProvider, CHAT_KEY_ENV } from './converse.js';
|
|
21
|
+
import { performSubscriptionMateRequest } from './subscription-chat.js';
|
|
22
|
+
export const MEMORY_TRACE_BYTES = 40_000;
|
|
23
|
+
export const MEMORY_MIN_SESSIONS = 2;
|
|
24
|
+
export const MEMORY_MAX_PROPOSALS = 6;
|
|
25
|
+
export const INSTRUCTION_BUDGET_BYTES = 4000;
|
|
26
|
+
export const MEMORY_GAP_MAX_AGE_MS = 90 * 24 * 3_600_000;
|
|
27
|
+
const sha = (text) => createHash('sha256').update(text).digest('hex');
|
|
28
|
+
/** The Claude Code project directory for a checkout: the path with every slash turned into a dash. */
|
|
29
|
+
export const claudeProjectDir = (cwd, home = homedir()) => join(home, '.claude', 'projects', cwd.replaceAll('/', '-'));
|
|
30
|
+
/** Coarse by design: a line that trips the secret scanner is dropped whole, named by pattern. */
|
|
31
|
+
function redact(text) {
|
|
32
|
+
const hits = new Map(scanForSecrets(text).map(hit => [hit.line, hit.name]));
|
|
33
|
+
if (hits.size === 0)
|
|
34
|
+
return text;
|
|
35
|
+
return text.split('\n').map((line, index) => hits.has(index + 1) ? `[redacted ${hits.get(index + 1)}]` : line).join('\n');
|
|
36
|
+
}
|
|
37
|
+
const clip = (text, max) => (text.length > max ? `${text.slice(0, max)}…` : text);
|
|
38
|
+
const jsonLines = (path) => readFileSync(path, 'utf8').split('\n').flatMap(line => { try {
|
|
39
|
+
return line.trim() ? [JSON.parse(line)] : [];
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return [];
|
|
43
|
+
} });
|
|
44
|
+
const obj = (v) => (v !== null && typeof v === 'object' && !Array.isArray(v) ? v : {});
|
|
45
|
+
/** Claude Code: one JSONL per session; user/assistant text verbatim, tool calls one line, results clipped, thinking dropped. */
|
|
46
|
+
export function distillClaude(path) {
|
|
47
|
+
const lines = [];
|
|
48
|
+
for (const raw of jsonLines(path)) {
|
|
49
|
+
const row = obj(raw), message = obj(row['message']);
|
|
50
|
+
if (row['type'] !== 'user' && row['type'] !== 'assistant')
|
|
51
|
+
continue;
|
|
52
|
+
const content = message['content'];
|
|
53
|
+
if (typeof content === 'string') {
|
|
54
|
+
lines.push(`${row['type']}: ${clip(content, 4000)}`);
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
for (const block of Array.isArray(content) ? content.map(obj) : []) {
|
|
58
|
+
if (block['type'] === 'text' && typeof block['text'] === 'string')
|
|
59
|
+
lines.push(`${row['type']}: ${clip(block['text'], 4000)}`);
|
|
60
|
+
else if (block['type'] === 'tool_use')
|
|
61
|
+
lines.push(`tool ${String(block['name'])}: ${clip(JSON.stringify(block['input'] ?? {}), 160)}`);
|
|
62
|
+
else if (block['type'] === 'tool_result') {
|
|
63
|
+
const c = block['content'];
|
|
64
|
+
lines.push(`result: ${clip(typeof c === 'string' ? c : JSON.stringify(c ?? ''), 200)}`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return redact(lines.join('\n'));
|
|
69
|
+
}
|
|
70
|
+
/** Codex: rollout JSONL; messages and custom tool calls, outputs clipped. */
|
|
71
|
+
export function distillCodex(path) {
|
|
72
|
+
const lines = [];
|
|
73
|
+
for (const raw of jsonLines(path)) {
|
|
74
|
+
const row = obj(raw), payload = obj(row['payload']);
|
|
75
|
+
if (row['type'] !== 'response_item')
|
|
76
|
+
continue;
|
|
77
|
+
if (payload['type'] === 'message' && (payload['role'] === 'user' || payload['role'] === 'assistant')) {
|
|
78
|
+
const text = (Array.isArray(payload['content']) ? payload['content'].map(obj) : []).map(c => typeof c['text'] === 'string' ? c['text'] : '').join(' ').trim();
|
|
79
|
+
if (text && !text.startsWith('<environment_context>'))
|
|
80
|
+
lines.push(`${payload['role']}: ${clip(text, 4000)}`);
|
|
81
|
+
}
|
|
82
|
+
else if (payload['type'] === 'custom_tool_call')
|
|
83
|
+
lines.push(`tool ${String(payload['name'])}: ${clip(String(payload['input'] ?? ''), 160)}`);
|
|
84
|
+
else if (payload['type'] === 'custom_tool_call_output')
|
|
85
|
+
lines.push(`result: ${clip(String(payload['output'] ?? ''), 200)}`);
|
|
86
|
+
}
|
|
87
|
+
return redact(lines.join('\n'));
|
|
88
|
+
}
|
|
89
|
+
function fileSources(repo, kind, home) {
|
|
90
|
+
const out = [];
|
|
91
|
+
if (kind === 'claude') {
|
|
92
|
+
const dir = claudeProjectDir(repo, home);
|
|
93
|
+
if (!existsSync(dir))
|
|
94
|
+
return out;
|
|
95
|
+
for (const name of readdirSync(dir)) {
|
|
96
|
+
if (!name.endsWith('.jsonl'))
|
|
97
|
+
continue;
|
|
98
|
+
const path = join(dir, name), st = statSync(path);
|
|
99
|
+
out.push({ id: `claude:${sha(path)}:${st.mtimeMs}:${st.size}`, repo, kind, source: path, at: st.mtime.toISOString(), trace: () => distillClaude(path) });
|
|
100
|
+
}
|
|
101
|
+
return out;
|
|
102
|
+
}
|
|
103
|
+
const root = join(home, '.codex', 'sessions');
|
|
104
|
+
if (!existsSync(root))
|
|
105
|
+
return out;
|
|
106
|
+
const walk = (dir) => { for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
107
|
+
const path = join(dir, entry.name);
|
|
108
|
+
if (entry.isDirectory())
|
|
109
|
+
walk(path);
|
|
110
|
+
else if (entry.name.startsWith('rollout-') && entry.name.endsWith('.jsonl'))
|
|
111
|
+
files.push(path);
|
|
112
|
+
} };
|
|
113
|
+
const files = [];
|
|
114
|
+
walk(root);
|
|
115
|
+
for (const path of files) {
|
|
116
|
+
const first = readFileSync(path, 'utf8').split('\n', 1)[0] ?? '';
|
|
117
|
+
let meta;
|
|
118
|
+
try {
|
|
119
|
+
meta = obj(obj(JSON.parse(first))['payload']);
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
if (meta['cwd'] !== repo)
|
|
125
|
+
continue;
|
|
126
|
+
const st = statSync(path);
|
|
127
|
+
out.push({ id: `codex:${sha(path)}:${st.mtimeMs}:${st.size}`, repo, kind, source: path, at: st.mtime.toISOString(), trace: () => distillCodex(path) });
|
|
128
|
+
}
|
|
129
|
+
return out;
|
|
130
|
+
}
|
|
131
|
+
/** The plane's own sessions: lead threads that touched the project, and finished crew runs with their handoff and check output. */
|
|
132
|
+
function planeSources(store, repo, evidenceRoot) {
|
|
133
|
+
const out = [];
|
|
134
|
+
const threads = store.handle.prepare(`SELECT c.thread AS thread, c.id AS conversation, MAX(m.id) AS last, MAX(m.created_at) AS at FROM team_conversation c JOIN mate_message m ON m.thread=c.thread
|
|
135
|
+
WHERE EXISTS (SELECT 1 FROM json_each(c.projects_json) WHERE value=?) GROUP BY c.thread`).all(repo);
|
|
136
|
+
for (const row of threads) {
|
|
137
|
+
const thread = Number(row['thread']);
|
|
138
|
+
out.push({ id: `lead:${thread}:${Number(row['last'])}`, repo, kind: 'lead', source: `conversation:${String(row['conversation'])}`, at: String(row['at']), trace: () => redact(store.handle.prepare('SELECT role,text FROM mate_message WHERE thread=? ORDER BY id LIMIT 400').all(thread).map(m => `${m['role']}: ${clip(String(m['text']), 4000)}`).join('\n')) });
|
|
139
|
+
}
|
|
140
|
+
const runs = store.handle.prepare(`SELECT r.id, r.role, r.finished_at, r.handoff, t.external_id FROM run r JOIN task_ref t ON t.id=r.task_ref WHERE t.repo=? AND r.role IN ('builder','repair') AND r.outcome IS NOT NULL ORDER BY r.id DESC LIMIT 100`).all(repo);
|
|
141
|
+
for (const row of runs) {
|
|
142
|
+
const id = Number(row['id']);
|
|
143
|
+
out.push({ id: `crew:${id}`, repo, kind: 'crew', source: `run:${id}`, at: String(row['finished_at'] ?? ''), trace: () => {
|
|
144
|
+
const parts = [`task ${String(row['external_id'])} · ${String(row['role'])} run ${id}`, `handoff: ${clip(String(row['handoff'] ?? ''), 6000)}`];
|
|
145
|
+
if (evidenceRoot !== undefined)
|
|
146
|
+
for (const artifact of store.artifactsFor(id).filter(a => a.kind === 'check-log' || a.kind === 'report')) {
|
|
147
|
+
const read = readVerifiedArtifact(evidenceRoot, artifact);
|
|
148
|
+
if (read.ok)
|
|
149
|
+
parts.push(`${artifact.kind}: ${clip(read.content.toString('utf8').slice(-6000), 6000)}`);
|
|
150
|
+
}
|
|
151
|
+
const proof = store.proofVerdictFor(id);
|
|
152
|
+
if (proof)
|
|
153
|
+
parts.push(`verdict: ${proof.verdict}${proof.reasons.length ? ` — ${proof.reasons.join('; ')}` : ''}`);
|
|
154
|
+
return redact(parts.join('\n'));
|
|
155
|
+
} });
|
|
156
|
+
}
|
|
157
|
+
return out;
|
|
158
|
+
}
|
|
159
|
+
export function collectSessions(store, repo, options = {}) {
|
|
160
|
+
const home = options.home ?? homedir();
|
|
161
|
+
const sources = [...planeSources(store, repo, options.evidenceRoot), ...(options.local === false ? [] : [...fileSources(repo, 'claude', home), ...fileSources(repo, 'codex', home)])];
|
|
162
|
+
return sources.sort((a, b) => b.at.localeCompare(a.at));
|
|
163
|
+
}
|
|
164
|
+
/** The always-loaded surface: instruction lines with stable ids, active decisions with theirs. Its hash keys the analysis cache. */
|
|
165
|
+
export function memorySurface(store, repo, actor) {
|
|
166
|
+
const view = knowledgeView(store, repo, actor);
|
|
167
|
+
const instructions = view.knowledge.instructions.split('\n').map(line => line.trim()).filter(Boolean).map((text, index) => ({ id: `IN-${String(index + 1).padStart(3, '0')}`, text }));
|
|
168
|
+
const decisions = listDecisions(store, repo, actor, { limit: 50 }).map(d => ({ id: `DE-${d.id}`, claim: d.claim }));
|
|
169
|
+
return { version: sha(JSON.stringify({ instructions, decisions })).slice(0, 16), instructions, decisions };
|
|
170
|
+
}
|
|
171
|
+
export function analysisPrompt(surface, openGaps) {
|
|
172
|
+
return [
|
|
173
|
+
'You are auditing one past session of an agent working on this project against the project memory it was given. Do not review the code. Measure how the memory steered the session and spot mistakes an instruction or a recorded decision could have prevented.',
|
|
174
|
+
'INSTRUCTIONS (refer to them ONLY by id):', ...surface.instructions.map(i => `[${i.id}] ${i.text}`),
|
|
175
|
+
'DECISIONS on record (refer by id):', ...surface.decisions.map(d => `[${d.id}] ${d.claim}`),
|
|
176
|
+
'GAPS ALREADY ON THE BOOKS (cite the key in matchesGap when the same underlying gap):', ...openGaps.map(g => `[${g.key}] ${g.mistake}`),
|
|
177
|
+
'Return ONE JSON object and nothing else: {"positive":[{"instruction":"IN-001","effect":"...","quote":"verbatim"}],"negative":[{"instruction":"IN-001","effect":"...","class":"harm|non-compliance|irrelevant","quote":"verbatim"}],"gaps":[{"mistake":"...","proposedInstruction":"one imperative sentence","domain":"project|orchestration","quote":"verbatim","matchesGap":"key when known"}]}',
|
|
178
|
+
'Rules: every item needs a verbatim quote copied from the trace or it is discarded; negative evidence outranks positive; class harm means following the instruction caused damage, non-compliance means it was ignored; a mistake caused by the harness or task framing is domain orchestration; report nothing rather than something weak; an empty array is a good answer.',
|
|
179
|
+
].join('\n');
|
|
180
|
+
}
|
|
181
|
+
/** The production analyser: one call through the configured chat provider, no tools, no history. */
|
|
182
|
+
export function defaultAnalyzer(store, options) {
|
|
183
|
+
return async (input) => {
|
|
184
|
+
const config = store.getChatConfig();
|
|
185
|
+
if (config === null)
|
|
186
|
+
return { ok: false, problem: 'Chat is not configured; the backward pass uses the chat model.' };
|
|
187
|
+
const system = analysisPrompt(input.surface, input.openGaps);
|
|
188
|
+
const dataDocument = `TRACE (${input.kind} session):\n${input.trace}`;
|
|
189
|
+
const history = [{ role: 'operator', text: 'Audit the trace above and return the JSON object.' }];
|
|
190
|
+
if (isDirectChatProvider(config.provider)) {
|
|
191
|
+
const env = options.env ?? process.env;
|
|
192
|
+
let key = env[CHAT_KEY_ENV[config.provider]] ?? '';
|
|
193
|
+
if (!key && options.configDir !== undefined) {
|
|
194
|
+
try {
|
|
195
|
+
key = readFileSync(join(options.configDir, `chat-key-${config.provider}`), 'utf8').trim();
|
|
196
|
+
}
|
|
197
|
+
catch {
|
|
198
|
+
key = '';
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
if (!key)
|
|
202
|
+
return { ok: false, problem: `No ${config.provider} key is available for the backward pass.` };
|
|
203
|
+
const request = composeMateRequest({ provider: config.provider, model: config.model, key, system, dataDocument, history, tools: [] });
|
|
204
|
+
const answer = await performMateRequest(request, config.provider, AbortSignal.timeout(options.timeoutMs ?? 120_000), options.fetcher);
|
|
205
|
+
return answer.ok ? { ok: true, text: answer.answer.text } : { ok: false, problem: answer.problem };
|
|
206
|
+
}
|
|
207
|
+
const answer = await performSubscriptionMateRequest({ provider: config.provider, model: config.model, system, dataDocument, history, tools: [], timeoutMs: options.timeoutMs ?? 180_000 }, options.runner);
|
|
208
|
+
return answer.ok ? { ok: true, text: answer.answer.text } : { ok: false, problem: answer.problem };
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
/** Parse strictly and keep only claims whose quote is really in the trace. */
|
|
212
|
+
export function parseVerdict(text, trace, surface) {
|
|
213
|
+
const start = text.indexOf('{'), end = text.lastIndexOf('}');
|
|
214
|
+
if (start < 0 || end <= start)
|
|
215
|
+
return null;
|
|
216
|
+
let raw;
|
|
217
|
+
try {
|
|
218
|
+
raw = obj(JSON.parse(text.slice(start, end + 1)));
|
|
219
|
+
}
|
|
220
|
+
catch {
|
|
221
|
+
return null;
|
|
222
|
+
}
|
|
223
|
+
const ids = new Set(surface.instructions.map(i => i.id));
|
|
224
|
+
const quoted = (item) => typeof item['quote'] === 'string' && item['quote'].trim().length >= 12 && trace.includes(item['quote'].trim());
|
|
225
|
+
const list = (key) => (Array.isArray(raw[key]) ? raw[key].map(obj) : []).filter(quoted).slice(0, 20);
|
|
226
|
+
return {
|
|
227
|
+
positive: list('positive').filter(i => ids.has(String(i['instruction']))).map(i => ({ instruction: String(i['instruction']), effect: clip(String(i['effect'] ?? ''), 300), quote: String(i['quote']).trim() })),
|
|
228
|
+
negative: list('negative').filter(i => ids.has(String(i['instruction'])) && ['harm', 'non-compliance', 'irrelevant'].includes(String(i['class']))).map(i => ({ instruction: String(i['instruction']), effect: clip(String(i['effect'] ?? ''), 300), class: i['class'], quote: String(i['quote']).trim() })),
|
|
229
|
+
gaps: list('gaps').filter(i => typeof i['mistake'] === 'string' && typeof i['proposedInstruction'] === 'string').map(i => ({ mistake: clip(String(i['mistake']), 300), proposedInstruction: clip(String(i['proposedInstruction']), 240), domain: i['domain'] === 'orchestration' ? 'orchestration' : 'project', quote: String(i['quote']).trim(), ...(typeof i['matchesGap'] === 'string' ? { matchesGap: i['matchesGap'] } : {}) })),
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
const gapKey = (mistake) => sha(mistake.toLowerCase().replace(/[^\p{L}\p{N}]+/gu, ' ').trim()).slice(0, 16);
|
|
233
|
+
/**
|
|
234
|
+
* Collect, distil, analyse and aggregate for one project. Never writes memory:
|
|
235
|
+
* it fills the ledger and the proposal queue. Each session is analysed once
|
|
236
|
+
* per surface version; a failed analysis is named and retried next run.
|
|
237
|
+
*/
|
|
238
|
+
export async function runMemoryPass(store, args, now = new Date()) {
|
|
239
|
+
const surface = memorySurface(store, args.repo, args.actor);
|
|
240
|
+
const sessions = collectSessions(store, args.repo, { ...(args.evidenceRoot === undefined ? {} : { evidenceRoot: args.evidenceRoot }), ...(args.home === undefined ? {} : { home: args.home }), ...(args.local === undefined ? {} : { local: args.local }) }).slice(0, args.maxSessions ?? 100);
|
|
241
|
+
const report = { repo: args.repo, surface: surface.version, sessions: sessions.length, analyzed: 0, cached: 0, failed: 0, problems: [], gaps: 0, proposals: 0 };
|
|
242
|
+
const openGaps = () => store.handle.prepare("SELECT key, mistake FROM memory_gap WHERE repo=? AND retired_at IS NULL AND last_seen>=?").all(args.repo, new Date(now.getTime() - MEMORY_GAP_MAX_AGE_MS).toISOString()).map(row => ({ key: String(row['key']), mistake: String(row['mistake']) }));
|
|
243
|
+
for (const session of sessions) {
|
|
244
|
+
const existing = store.handle.prepare('SELECT surface, analyzed_at, verdict FROM memory_session WHERE id=?').get(session.id);
|
|
245
|
+
if (existing && existing['surface'] === surface.version && existing['analyzed_at'] !== null && existing['verdict'] !== null) {
|
|
246
|
+
report.cached++;
|
|
247
|
+
continue;
|
|
248
|
+
}
|
|
249
|
+
let trace;
|
|
250
|
+
try {
|
|
251
|
+
trace = session.trace();
|
|
252
|
+
}
|
|
253
|
+
catch (error) {
|
|
254
|
+
report.failed++;
|
|
255
|
+
report.problems.push(`${session.id}: ${error instanceof Error ? error.message : 'unreadable'}`);
|
|
256
|
+
continue;
|
|
257
|
+
}
|
|
258
|
+
if (trace.length > MEMORY_TRACE_BYTES)
|
|
259
|
+
trace = trace.slice(0, MEMORY_TRACE_BYTES);
|
|
260
|
+
if (trace.trim().length < 40)
|
|
261
|
+
continue;
|
|
262
|
+
store.handle.prepare(`INSERT INTO memory_session(id,repo,kind,source,seen_at,surface,trace_sha) VALUES (?,?,?,?,?,?,?)
|
|
263
|
+
ON CONFLICT(id) DO UPDATE SET surface=excluded.surface, trace_sha=excluded.trace_sha, analyzed_at=NULL, verdict=NULL, problem=NULL`).run(session.id, args.repo, session.kind, session.source, session.at || now.toISOString(), surface.version, sha(trace));
|
|
264
|
+
const gaps = openGaps();
|
|
265
|
+
const answer = await args.analyzer({ trace, surface, openGaps: gaps, kind: session.kind });
|
|
266
|
+
const verdict = answer.ok ? parseVerdict(answer.text, trace, surface) : null;
|
|
267
|
+
if (verdict === null) {
|
|
268
|
+
report.failed++;
|
|
269
|
+
const problem = answer.ok ? 'The analysis was not a readable verdict.' : answer.problem;
|
|
270
|
+
report.problems.push(`${session.id}: ${problem}`);
|
|
271
|
+
store.handle.prepare('UPDATE memory_session SET problem=? WHERE id=?').run(problem, session.id);
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
store.transact(() => {
|
|
275
|
+
store.handle.prepare('UPDATE memory_session SET analyzed_at=?, verdict=?, problem=NULL WHERE id=?').run(now.toISOString(), JSON.stringify(verdict), session.id);
|
|
276
|
+
for (const gap of verdict.gaps) {
|
|
277
|
+
if (gap.domain === 'orchestration')
|
|
278
|
+
continue;
|
|
279
|
+
const key = gap.matchesGap !== undefined && gaps.some(g => g.key === gap.matchesGap) ? gap.matchesGap : gapKey(gap.mistake);
|
|
280
|
+
store.handle.prepare(`INSERT INTO memory_gap(repo,key,mistake,proposed,domain,first_seen,last_seen) VALUES (?,?,?,?,?,?,?)
|
|
281
|
+
ON CONFLICT(repo,key) DO UPDATE SET last_seen=excluded.last_seen, retired_at=NULL, retired_reason=NULL`).run(args.repo, key, gap.mistake, gap.proposedInstruction, gap.domain, now.toISOString(), now.toISOString());
|
|
282
|
+
const id = Number(store.handle.prepare('SELECT id FROM memory_gap WHERE repo=? AND key=?').get(args.repo, key)['id']);
|
|
283
|
+
store.handle.prepare('INSERT OR IGNORE INTO memory_sighting(gap,session,at,quote) VALUES (?,?,?,?)').run(id, session.id, now.toISOString(), gap.quote);
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
report.analyzed++;
|
|
287
|
+
}
|
|
288
|
+
report.gaps = Number(store.handle.prepare('SELECT COUNT(*) AS n FROM memory_gap WHERE repo=? AND retired_at IS NULL').get(args.repo)?.['n'] ?? 0);
|
|
289
|
+
report.proposals = synthesizeProposals(store, args.repo, args.actor, surface, now);
|
|
290
|
+
return report;
|
|
291
|
+
}
|
|
292
|
+
function proposalOf(row) {
|
|
293
|
+
return { id: Number(row['id']), repo: String(row['repo']), kind: row['kind'], fingerprint: String(row['fingerprint']), title: String(row['title']), rationale: String(row['rationale']),
|
|
294
|
+
beforeText: row['before_text'] === null ? null : String(row['before_text']), afterText: String(row['after_text']), evidence: JSON.parse(String(row['evidence'])),
|
|
295
|
+
sessions: Number(row['sessions']), status: String(row['status']), createdAt: String(row['created_at']), surface: String(row['surface']) };
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Turn corroborated gaps and harm into a few proposals: an addition needs
|
|
299
|
+
* the gap seen in at least two distinct sessions; a removal needs harm from
|
|
300
|
+
* following the instruction in two sessions. Under the instruction budget an
|
|
301
|
+
* addition is proposed only when it fits; over it, the run says so instead
|
|
302
|
+
* of growing the file. A rejected fingerprint is not proposed again until
|
|
303
|
+
* more sessions corroborate it.
|
|
304
|
+
*/
|
|
305
|
+
export function synthesizeProposals(store, repo, actor, surface, now = new Date()) {
|
|
306
|
+
const view = knowledgeView(store, repo, actor);
|
|
307
|
+
const budgetLeft = INSTRUCTION_BUDGET_BYTES - Buffer.byteLength(view.knowledge.instructions);
|
|
308
|
+
let made = 0;
|
|
309
|
+
store.transact(() => {
|
|
310
|
+
store.handle.prepare("UPDATE memory_proposal SET status='stale' WHERE repo=? AND status='pending' AND surface<>?").run(repo, surface.version);
|
|
311
|
+
const pendingFingerprints = new Set(store.handle.prepare("SELECT fingerprint FROM memory_proposal WHERE repo=? AND status='pending'").all(repo).map(row => String(row['fingerprint'])));
|
|
312
|
+
const rejected = new Map(store.handle.prepare('SELECT fingerprint, sessions FROM memory_rejection WHERE repo=?').all(repo).map(row => [String(row['fingerprint']), Number(row['sessions'])]));
|
|
313
|
+
const consider = (kind, fingerprint, title, rationale, beforeText, afterText, evidence) => {
|
|
314
|
+
if (made >= MEMORY_MAX_PROPOSALS || pendingFingerprints.has(fingerprint))
|
|
315
|
+
return;
|
|
316
|
+
const sessions = new Set(evidence.map(e => e.session)).size;
|
|
317
|
+
if (sessions < MEMORY_MIN_SESSIONS || (rejected.get(fingerprint) ?? -1) >= sessions)
|
|
318
|
+
return;
|
|
319
|
+
store.handle.prepare('INSERT INTO memory_proposal(repo,kind,fingerprint,title,rationale,before_text,after_text,evidence,sessions,status,created_at,surface) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)')
|
|
320
|
+
.run(repo, kind, fingerprint, title, rationale, beforeText, afterText, JSON.stringify(evidence.slice(0, 6)), sessions, 'pending', now.toISOString(), surface.version);
|
|
321
|
+
pendingFingerprints.add(fingerprint);
|
|
322
|
+
made++;
|
|
323
|
+
};
|
|
324
|
+
// Additions from corroborated gaps.
|
|
325
|
+
const gaps = store.handle.prepare("SELECT g.id, g.key, g.mistake, g.proposed FROM memory_gap g WHERE g.repo=? AND g.retired_at IS NULL AND g.domain='project' ORDER BY g.last_seen DESC").all(repo);
|
|
326
|
+
for (const gap of gaps) {
|
|
327
|
+
const sightings = store.handle.prepare('SELECT session, quote FROM memory_sighting WHERE gap=? ORDER BY at').all(Number(gap['id'])).map(row => ({ session: String(row['session']), quote: String(row['quote']) }));
|
|
328
|
+
if (new Set(sightings.map(s => s.session)).size < MEMORY_MIN_SESSIONS)
|
|
329
|
+
continue;
|
|
330
|
+
const proposed = String(gap['proposed']).trim();
|
|
331
|
+
const already = surface.instructions.some(i => i.text.toLowerCase() === proposed.toLowerCase());
|
|
332
|
+
if (already) {
|
|
333
|
+
store.handle.prepare("UPDATE memory_gap SET retired_at=?, retired_reason='covered by an instruction' WHERE id=?").run(now.toISOString(), Number(gap['id']));
|
|
334
|
+
continue;
|
|
335
|
+
}
|
|
336
|
+
if (Buffer.byteLength(proposed) + 1 > budgetLeft) {
|
|
337
|
+
consider('decision-add', `budget:${String(gap['key'])}`, `Over budget: ${clip(proposed, 80)}`, `The instructions are at their 4 KB budget. Record this as a decision instead, or remove an instruction first. Mistake seen: ${String(gap['mistake'])}`, null, proposed, sightings);
|
|
338
|
+
continue;
|
|
339
|
+
}
|
|
340
|
+
consider('instruction-add', `add:${String(gap['key'])}`, `Add: ${clip(proposed, 80)}`, `Mistake seen in ${new Set(sightings.map(s => s.session)).size} sessions: ${String(gap['mistake'])}`, null, proposed, sightings);
|
|
341
|
+
}
|
|
342
|
+
// Removals from harm.
|
|
343
|
+
const harm = new Map();
|
|
344
|
+
for (const row of store.handle.prepare("SELECT id, verdict FROM memory_session WHERE repo=? AND surface=? AND verdict IS NOT NULL").all(repo, surface.version)) {
|
|
345
|
+
let verdict;
|
|
346
|
+
try {
|
|
347
|
+
verdict = JSON.parse(String(row['verdict']));
|
|
348
|
+
}
|
|
349
|
+
catch {
|
|
350
|
+
continue;
|
|
351
|
+
}
|
|
352
|
+
for (const item of verdict.negative)
|
|
353
|
+
if (item.class === 'harm')
|
|
354
|
+
harm.set(item.instruction, [...(harm.get(item.instruction) ?? []), { session: String(row['id']), quote: item.quote }]);
|
|
355
|
+
}
|
|
356
|
+
for (const [instruction, evidence] of harm) {
|
|
357
|
+
const line = surface.instructions.find(i => i.id === instruction);
|
|
358
|
+
if (line === undefined)
|
|
359
|
+
continue;
|
|
360
|
+
consider('instruction-remove', `remove:${sha(line.text).slice(0, 16)}`, `Remove: ${clip(line.text, 80)}`, `Following this instruction caused harm in ${new Set(evidence.map(e => e.session)).size} sessions.`, line.text, '', evidence);
|
|
361
|
+
}
|
|
362
|
+
});
|
|
363
|
+
return made;
|
|
364
|
+
}
|
|
365
|
+
export function listProposals(store, repo, status = 'pending') {
|
|
366
|
+
return store.handle.prepare(`SELECT * FROM memory_proposal WHERE repo=? AND (?='all' OR status=?) ORDER BY id DESC LIMIT 100`).all(repo, status, status).map(row => proposalOf(row));
|
|
367
|
+
}
|
|
368
|
+
/** The one writer: accept applies the edit through the knowledge or decision store as the person; reject remembers the fingerprint and its corroboration. */
|
|
369
|
+
export function decideProposal(store, args, now = new Date()) {
|
|
370
|
+
return store.transact(() => {
|
|
371
|
+
const row = store.handle.prepare('SELECT * FROM memory_proposal WHERE id=? AND repo=?').get(args.id, args.repo);
|
|
372
|
+
if (!row)
|
|
373
|
+
throw Error('That proposal is unavailable.');
|
|
374
|
+
const proposal = proposalOf(row);
|
|
375
|
+
if (proposal.status !== 'pending')
|
|
376
|
+
throw Error('That proposal was already decided.');
|
|
377
|
+
const surface = memorySurface(store, args.repo, args.actor);
|
|
378
|
+
if (surface.version !== proposal.surface) {
|
|
379
|
+
store.handle.prepare("UPDATE memory_proposal SET status='stale' WHERE id=?").run(proposal.id);
|
|
380
|
+
throw Error('Project memory changed since this was proposed. Run the pass again.');
|
|
381
|
+
}
|
|
382
|
+
if (args.decision === 'reject') {
|
|
383
|
+
store.handle.prepare('INSERT INTO memory_rejection(repo,fingerprint,rejected_at,rejected_by,sessions) VALUES (?,?,?,?,?) ON CONFLICT(repo,fingerprint) DO UPDATE SET rejected_at=excluded.rejected_at, rejected_by=excluded.rejected_by, sessions=excluded.sessions')
|
|
384
|
+
.run(args.repo, proposal.fingerprint, now.toISOString(), args.actor, proposal.sessions);
|
|
385
|
+
}
|
|
386
|
+
else {
|
|
387
|
+
const view = knowledgeView(store, args.repo, args.actor);
|
|
388
|
+
if (proposal.kind === 'instruction-add')
|
|
389
|
+
changeKnowledge(store, { repo: args.repo, actor: args.actor, identity: view.identity, revision: view.revision, action: 'instructions', draft: { instructions: `${view.knowledge.instructions.trimEnd()}\n${proposal.afterText}`.trim() } }, now);
|
|
390
|
+
else if (proposal.kind === 'instruction-remove')
|
|
391
|
+
changeKnowledge(store, { repo: args.repo, actor: args.actor, identity: view.identity, revision: view.revision, action: 'instructions', draft: { instructions: view.knowledge.instructions.split('\n').filter(line => line.trim() !== (proposal.beforeText ?? '').trim()).join('\n').trim() } }, now);
|
|
392
|
+
else
|
|
393
|
+
recordDecision(store, { repo: args.repo, actor: args.actor, draft: { claim: proposal.afterText, why: proposal.rationale, sourceKind: 'backward-pass', sourceRef: `proposal:${proposal.id}` } }, now);
|
|
394
|
+
const key = proposal.fingerprint.split(':').slice(1).join(':');
|
|
395
|
+
store.handle.prepare("UPDATE memory_gap SET retired_at=?, retired_reason='accepted into memory' WHERE repo=? AND key=?").run(now.toISOString(), args.repo, key);
|
|
396
|
+
}
|
|
397
|
+
store.handle.prepare('UPDATE memory_proposal SET status=?, decided_by=?, decided_at=? WHERE id=?').run(args.decision === 'accept' ? 'accepted' : 'rejected', args.actor, now.toISOString(), proposal.id);
|
|
398
|
+
return { ...proposal, status: args.decision === 'accept' ? 'accepted' : 'rejected' };
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
export function memoryStatus(store, repo, actor) {
|
|
402
|
+
const surface = memorySurface(store, repo, actor);
|
|
403
|
+
const view = knowledgeView(store, repo, actor);
|
|
404
|
+
const counts = store.handle.prepare('SELECT COUNT(*) AS total, SUM(CASE WHEN analyzed_at IS NOT NULL THEN 1 ELSE 0 END) AS analyzed, SUM(CASE WHEN problem IS NOT NULL THEN 1 ELSE 0 END) AS failed FROM memory_session WHERE repo=?').get(repo);
|
|
405
|
+
return { surface: surface.version, instructionBytes: Buffer.byteLength(view.knowledge.instructions), budgetBytes: INSTRUCTION_BUDGET_BYTES,
|
|
406
|
+
sessions: { total: Number(counts['total'] ?? 0), analyzed: Number(counts['analyzed'] ?? 0), failed: Number(counts['failed'] ?? 0) },
|
|
407
|
+
openGaps: Number(store.handle.prepare('SELECT COUNT(*) AS n FROM memory_gap WHERE repo=? AND retired_at IS NULL').get(repo)?.['n'] ?? 0),
|
|
408
|
+
pending: Number(store.handle.prepare("SELECT COUNT(*) AS n FROM memory_proposal WHERE repo=? AND status='pending'").get(repo)?.['n'] ?? 0) };
|
|
409
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { Store } from "./store.js";
|
|
2
|
+
/** `repos`: the projects this console serves (null: all); task, run and worker figures count only theirs. */
|
|
3
|
+
export declare function prometheusMetrics(store: Store, now: Date, repos?: readonly string[] | null, destinations?: ReadonlyMap<string, string>): string;
|
package/dist/metrics.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `/metrics` (v104): Toolroll in the Prometheus text format, for the
|
|
3
|
+
* dashboards and alerts a company already runs. Labels are small, fixed
|
|
4
|
+
* sets (state, role, outcome, provider, project, destination), never a task
|
|
5
|
+
* id, a person or a path, so a scrape stays cheap and says nothing private.
|
|
6
|
+
*/
|
|
7
|
+
import { createHash } from "node:crypto";
|
|
8
|
+
import { basename } from "node:path";
|
|
9
|
+
const label = (value) => value.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n");
|
|
10
|
+
const line = (name, sample) => {
|
|
11
|
+
const labels = Object.entries(sample.labels ?? {});
|
|
12
|
+
return `${name}${labels.length === 0 ? "" : `{${labels.map(([key, value]) => `${key}="${label(value)}"`).join(",")}}`} ${Number.isFinite(sample.value) ? sample.value : 0}`;
|
|
13
|
+
};
|
|
14
|
+
/** `repos`: the projects this console serves (null: all); task, run and worker figures count only theirs. */
|
|
15
|
+
export function prometheusMetrics(store, now, repos = null, destinations) {
|
|
16
|
+
const db = store.handle;
|
|
17
|
+
const inProjects = (column) => repos === null ? "1 = 1" : repos.length === 0 ? "1 = 0" : `${column} IN (${repos.map(() => "?").join(",")})`;
|
|
18
|
+
const scoped = repos ?? [];
|
|
19
|
+
const rows = (sql, ...args) => db.prepare(sql).all(...args);
|
|
20
|
+
const metrics = [];
|
|
21
|
+
const add = (name, help, type, samples) => metrics.push({ name, help, type, samples });
|
|
22
|
+
// A project's label is its folder name, told apart by a short digest when two share one.
|
|
23
|
+
const names = new Map();
|
|
24
|
+
for (const repo of rows(`SELECT DISTINCT repo FROM watch_lease WHERE ${inProjects("repo")}`, ...scoped).map(row => String(row["repo"])))
|
|
25
|
+
names.set(basename(repo), (names.get(basename(repo)) ?? 0) + 1);
|
|
26
|
+
const project = (repo) => (names.get(basename(repo)) ?? 0) > 1 ? `${basename(repo)}-${createHash("sha256").update(repo).digest("hex").slice(0, 6)}` : basename(repo);
|
|
27
|
+
add("toolroll_tasks", "Tasks by state.", "gauge", rows(`SELECT t.state, COUNT(*) AS n FROM task t JOIN task_ref r ON r.external_id = t.id AND r.backend = 'built-in' WHERE ${inProjects("r.repo")} GROUP BY t.state`, ...scoped)
|
|
28
|
+
.map(row => ({ labels: { state: String(row["state"]) }, value: Number(row["n"]) })));
|
|
29
|
+
const runs = rows(`SELECT run.role, COALESCE(run.outcome, 'running') AS outcome, run.provider, COUNT(*) AS n,
|
|
30
|
+
SUM(CASE WHEN run.finished_at IS NULL THEN 0 ELSE (julianday(run.finished_at) - julianday(run.started_at)) * 86400 END) AS seconds,
|
|
31
|
+
SUM(COALESCE(run.cost_usd, 0)) AS cost
|
|
32
|
+
FROM run JOIN task_ref r ON r.id = run.task_ref WHERE ${inProjects("r.repo")} GROUP BY run.role, COALESCE(run.outcome, 'running'), run.provider`, ...scoped);
|
|
33
|
+
const byRun = (pick) => runs.map(row => ({ labels: { role: String(row["role"]), outcome: String(row["outcome"]), provider: String(row["provider"]) }, value: pick(row) }));
|
|
34
|
+
add("toolroll_runs_total", "Agent runs, by role, outcome and provider.", "counter", byRun(row => Number(row["n"])));
|
|
35
|
+
add("toolroll_run_seconds_total", "Time agent runs took, in seconds.", "counter", byRun(row => Math.round(Number(row["seconds"] ?? 0))));
|
|
36
|
+
add("toolroll_spend_usd_total", "What agent runs cost, in US dollars (as providers report it).", "counter", byRun(row => Number(row["cost"] ?? 0)));
|
|
37
|
+
add("toolroll_tokens_total", "Tokens agent runs used.", "counter", rows(`SELECT run.role, run.provider, SUM(COALESCE(run.tokens_in, 0)) AS tokens_in, SUM(COALESCE(run.tokens_out, 0)) AS tokens_out
|
|
38
|
+
FROM run JOIN task_ref r ON r.id = run.task_ref WHERE ${inProjects("r.repo")} GROUP BY run.role, run.provider`, ...scoped).flatMap(row => [
|
|
39
|
+
{ labels: { role: String(row["role"]), provider: String(row["provider"]), direction: "in" }, value: Number(row["tokens_in"] ?? 0) },
|
|
40
|
+
{ labels: { role: String(row["role"]), provider: String(row["provider"]), direction: "out" }, value: Number(row["tokens_out"] ?? 0) },
|
|
41
|
+
]));
|
|
42
|
+
add("toolroll_decisions_waiting", "Questions from agents waiting on a person.", "gauge", [{ value: repos === null ? store.countUnansweredScoped(null) : repos.reduce((sum, repo) => sum + store.countUnansweredScoped(repo), 0) }]);
|
|
43
|
+
const chain = store.ledgerChain();
|
|
44
|
+
add("toolroll_ledger_entries", "Entries in the action ledger's chain.", "gauge", [{ value: chain.entries }]);
|
|
45
|
+
add("toolroll_ledger_chain_ok", "1 when the action ledger's chain verifies, 0 when it's broken.", "gauge", [{ value: chain.ok ? 1 : 0 }]);
|
|
46
|
+
add("toolroll_ledger_checked_timestamp_seconds", "When the whole chain was last walked.", "gauge", chain.checkedAt === null ? [] : [{ value: Math.floor(Date.parse(chain.checkedAt) / 1000) }]);
|
|
47
|
+
const head = store.ledgerHeadId();
|
|
48
|
+
// Only destinations set up now, each for its current address (`destinations`: sink → target; all when not given).
|
|
49
|
+
const status = store.monitoringStatus().filter(one => destinations === undefined || destinations.get(one.sink) === one.target);
|
|
50
|
+
add("toolroll_monitoring_lag_entries", "Ledger entries a monitoring destination hasn't been sent yet.", "gauge", status.map(one => ({ labels: { destination: one.sink }, value: Math.max(0, head - one.through) })));
|
|
51
|
+
add("toolroll_monitoring_failures", "Failed deliveries in a row, by destination.", "gauge", status.map(one => ({ labels: { destination: one.sink }, value: one.failures })));
|
|
52
|
+
add("toolroll_monitoring_last_ok_timestamp_seconds", "When a destination last took a delivery.", "gauge", status.filter(one => one.lastOkAt !== null).map(one => ({ labels: { destination: one.sink }, value: Math.floor(Date.parse(one.lastOkAt) / 1000) })));
|
|
53
|
+
add("toolroll_worker_heartbeat_age_seconds", "Seconds since each project's worker last checked in.", "gauge", rows(`SELECT repo, MAX(heartbeat_at) AS at FROM watch_lease WHERE ${inProjects("repo")} GROUP BY repo`, ...scoped).map(row => ({ labels: { project: project(String(row["repo"])) }, value: Math.max(0, Math.round((now.getTime() - Date.parse(String(row["at"]))) / 1000)) })));
|
|
54
|
+
add("toolroll_checkouts", "Build checkouts on disk, by state.", "gauge", rows(`SELECT CASE WHEN runner IS NOT NULL AND released_at IS NULL THEN 'leased' ELSE 'released' END AS state, COUNT(*) AS n FROM worktree WHERE ${inProjects("repo")} GROUP BY 1`, ...scoped).map(row => ({ labels: { state: String(row["state"]) }, value: Number(row["n"]) })));
|
|
55
|
+
return `${metrics.map(metric => [`# HELP ${metric.name} ${metric.help}`, `# TYPE ${metric.name} ${metric.type}`, ...metric.samples.map(sample => line(metric.name, sample))].join("\n")).join("\n")}\n`;
|
|
56
|
+
}
|