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
package/dist/invoke.js
ADDED
|
@@ -0,0 +1,780 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The invocation gateway: the only door to any provider binary (§5).
|
|
3
|
+
*
|
|
4
|
+
* "Never let an LLM poll" is only enforceable if there is exactly one place
|
|
5
|
+
* that can start an LLM, and this is it. Every call verifies an open run
|
|
6
|
+
* record WHOSE RECORDED PROVIDER MATCHES the one about to spawn (a repair
|
|
7
|
+
* resumed on the wrong harness is a meaningless session id and a silent
|
|
8
|
+
* fresh spend — refused here, structurally), stamps `provider_started_at`
|
|
9
|
+
* the instant before the process spawns, and parses usage off the envelope
|
|
10
|
+
* of EVERY completed process — nonzero exits included, because a failed
|
|
11
|
+
* turn still spent money and a cost report that skips failures is a
|
|
12
|
+
* smaller number, not a truer one.
|
|
13
|
+
*
|
|
14
|
+
* The architecture test asserts the boundary by imports, not by string
|
|
15
|
+
* scan: only this module imports the registry's spawning surface
|
|
16
|
+
* (`adapterFor`), and only builder, planner, reviewer, and scout import
|
|
17
|
+
* `invokeAgent`.
|
|
18
|
+
*/
|
|
19
|
+
import { adapterFor, auditOf } from "./provider.js";
|
|
20
|
+
import { readProviderKey, readAuthModeStrict, PROVIDER_KEY_ENV, OWN_KEY_ENV } from "./keys.js";
|
|
21
|
+
import { classifyTerminal } from "./exhaustion.js";
|
|
22
|
+
import { attestProvider } from "./attest.js";
|
|
23
|
+
import { run as runCommand, runOwnerTag, startClaudeHeldSession } from "./exec.js";
|
|
24
|
+
import { currentContainment } from "./containment.js";
|
|
25
|
+
import { witnessedRunner, recordProcessObservationFailure, preserveObservedProcesses } from "./process-custody.js";
|
|
26
|
+
import { underStopWatch } from "./task-control.js";
|
|
27
|
+
import { childDatabaseEnv, isolatedChildDatabase, removeChildDatabase as removeAgentDatabase } from "./child-database.js";
|
|
28
|
+
import { noToolsArgs, prepareRunTools } from "./project-tools.js";
|
|
29
|
+
import { realpathSync } from "node:fs";
|
|
30
|
+
import { agentFence, claudeFenceSettings, linuxFenceAvailable, macosFenceAvailable } from "./agent-fence.js";
|
|
31
|
+
import { billingOf, claudeBillingFrom, seenBilling } from "./spend.js";
|
|
32
|
+
/** The claude binary name — kept for the legacy quota rows and tests that
|
|
33
|
+
* describe history; new code carries a resolved AgentSpec instead. */
|
|
34
|
+
export const PROVIDER_BINARY = "claude";
|
|
35
|
+
/**
|
|
36
|
+
* A provider can run the repository's own CLI. In a self-hosting build that
|
|
37
|
+
* must never mean "open the database that launched me": a migration from the
|
|
38
|
+
* candidate checkout would change the live schema underneath the older
|
|
39
|
+
* supervisor, and even a read can contend with its watch transaction. Every
|
|
40
|
+
* provider process therefore sees a unique disposable Toolroll
|
|
41
|
+
* database while retaining its normal HOME/XDG environment for subscriptions
|
|
42
|
+
* and unrelated developer tools.
|
|
43
|
+
*/
|
|
44
|
+
function isolatedAgentDatabase(runId) {
|
|
45
|
+
return isolatedChildDatabase(`agent-${runId}`);
|
|
46
|
+
}
|
|
47
|
+
/** Defend the durable registry even when a test or alternate transport calls
|
|
48
|
+
* the callback directly instead of passing through an in-tree JSONL runner. */
|
|
49
|
+
function transportSessionId(id) {
|
|
50
|
+
if (typeof id !== "string")
|
|
51
|
+
return null;
|
|
52
|
+
const normalized = id.trim();
|
|
53
|
+
return normalized === "" ? null : normalized;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Spend money, on the record. Throws — never refuses quietly — when the run
|
|
57
|
+
* is missing, already finished, or recorded against a different provider:
|
|
58
|
+
* a caller that reaches for a harness without a matching open run is a
|
|
59
|
+
* bug, not a case. Attestation and protocol refusals are VALUES (above),
|
|
60
|
+
* not throws — those are cases, and they must dispose cleanly.
|
|
61
|
+
*/
|
|
62
|
+
export async function invokeAgent(store, runId, spec, invocation, options) {
|
|
63
|
+
const clock = options.clock ?? (() => new Date());
|
|
64
|
+
if (options.accumulateUsage && (invocation.phase !== "review" || invocation.resumeSession === null)) {
|
|
65
|
+
throw new Error("accumulated usage is only for evidence reads in an existing reviewer session");
|
|
66
|
+
}
|
|
67
|
+
const run = store.getRun(runId);
|
|
68
|
+
if (run === null || run.outcome !== null) {
|
|
69
|
+
throw new Error(`run ${runId} is not an open attempt — nothing spends without a run record that will outlive it`);
|
|
70
|
+
}
|
|
71
|
+
if (run.provider !== spec.provider) {
|
|
72
|
+
throw new Error(`run ${runId} was opened for ${run.provider} but ${spec.provider} is about to spawn — a session resumed across harnesses is not a session`);
|
|
73
|
+
}
|
|
74
|
+
// Session selection is durable authority, not an argv-only hint. A resumed
|
|
75
|
+
// turn must name exactly the canonical identity already recorded on THIS
|
|
76
|
+
// run before anything awaits or spawns. That makes a wrong child-row id a
|
|
77
|
+
// caller bug in the same class as a wrong provider, and prevents COALESCE's
|
|
78
|
+
// first-write rule from leaving row A while the process actually resumes B.
|
|
79
|
+
const recordedSessionId = transportSessionId(run.sessionId);
|
|
80
|
+
if (run.sessionId !== null && recordedSessionId === null) {
|
|
81
|
+
throw new Error(`run ${runId} carries an empty durable session identity — nothing resumes an identity the registry cannot name`);
|
|
82
|
+
}
|
|
83
|
+
const requestedResumeSessionId = invocation.resumeSession === null ? null : transportSessionId(invocation.resumeSession);
|
|
84
|
+
if (invocation.resumeSession !== null && requestedResumeSessionId === null) {
|
|
85
|
+
throw new Error(`run ${runId} was asked to resume an empty session identity`);
|
|
86
|
+
}
|
|
87
|
+
if (requestedResumeSessionId !== null && recordedSessionId !== requestedResumeSessionId) {
|
|
88
|
+
throw new Error(recordedSessionId === null
|
|
89
|
+
? `run ${runId} has no durable session identity matching the requested resume ${requestedResumeSessionId}`
|
|
90
|
+
: `run ${runId} records session ${recordedSessionId} but was asked to resume ${requestedResumeSessionId}`);
|
|
91
|
+
}
|
|
92
|
+
const requestedStartSessionId = requestedResumeSessionId !== null || invocation.startSessionId === undefined
|
|
93
|
+
? undefined
|
|
94
|
+
: transportSessionId(invocation.startSessionId);
|
|
95
|
+
if (requestedResumeSessionId === null && invocation.startSessionId !== undefined && requestedStartSessionId === null) {
|
|
96
|
+
throw new Error(`run ${runId} was asked to start under an empty session identity`);
|
|
97
|
+
}
|
|
98
|
+
const startSessionId = requestedResumeSessionId !== null ? undefined : (requestedStartSessionId ?? undefined);
|
|
99
|
+
if (recordedSessionId !== null && requestedResumeSessionId === null && startSessionId !== recordedSessionId) {
|
|
100
|
+
throw new Error(`run ${runId} already records session ${recordedSessionId} but this invocation neither resumes nor starts under it`);
|
|
101
|
+
}
|
|
102
|
+
const adapter = adapterFor(spec.provider);
|
|
103
|
+
const { startSessionId: _untrustedStartSessionId, ...invocationWithoutStart } = invocation;
|
|
104
|
+
const resolvedInvocation = {
|
|
105
|
+
...invocationWithoutStart,
|
|
106
|
+
model: spec.model,
|
|
107
|
+
resumeSession: requestedResumeSessionId,
|
|
108
|
+
...(startSessionId === undefined ? {} : { startSessionId }),
|
|
109
|
+
};
|
|
110
|
+
const argv = adapter.argv(resolvedInvocation);
|
|
111
|
+
const stdin = adapter.stdin?.(resolvedInvocation);
|
|
112
|
+
// Long-running phases pass idleTimeoutMs: the process has no absolute
|
|
113
|
+
// deadline and is stopped only after a full no-output interval. Bounded
|
|
114
|
+
// callers (notably repair) keep timeoutMs and its hard wall clock. A
|
|
115
|
+
// legacy caller that names neither retains the historic bounded default.
|
|
116
|
+
const hardTimeoutMs = options.timeoutMs === undefined ? undefined : adapter.clampTimeout(invocation.phase, options.timeoutMs);
|
|
117
|
+
const idleTimeoutMs = options.idleTimeoutMs === undefined ? undefined : adapter.clampTimeout(invocation.phase, options.idleTimeoutMs);
|
|
118
|
+
const defaultTimeoutMs = hardTimeoutMs === undefined && idleTimeoutMs === undefined
|
|
119
|
+
? adapter.clampTimeout(invocation.phase, 30 * 60_000)
|
|
120
|
+
: undefined;
|
|
121
|
+
// Tier-2 attestation (A2/B3): the authoritative check, immediately
|
|
122
|
+
// before the spawn it authorizes. Tier-1 providers return null here and
|
|
123
|
+
// keep their road byte-identical.
|
|
124
|
+
const attested = await attestProvider(spec.provider, adapter.binary, options.versionProbe);
|
|
125
|
+
if (attested !== null && !attested.ok) {
|
|
126
|
+
if (attested.version !== null)
|
|
127
|
+
store.stampProviderVersion(runId, attested.version);
|
|
128
|
+
return {
|
|
129
|
+
kind: "refused",
|
|
130
|
+
reason: "provider-unattested",
|
|
131
|
+
providerVersion: attested.version,
|
|
132
|
+
diagnostic: attested.problem,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
// Auth mode decides how the credential reaches the process (the operator
|
|
136
|
+
// keeps BOTH and prefers the subscription): in "api-key" mode the
|
|
137
|
+
// managed file wins, the ambient env is a real fallback (explicit
|
|
138
|
+
// re-supply survives resolveChildEnv's chat-key strip); in
|
|
139
|
+
// "subscription" mode the key is STRIPPED from the child so the CLI's
|
|
140
|
+
// own login is used — its key is retained, just not handed over.
|
|
141
|
+
// A CHAIN-BOUND run carries a PINNED mode sealed into its approved entry
|
|
142
|
+
// (E3d, review finding 5): the pin IS the authority — a per-provider
|
|
143
|
+
// mode-file flip between admission and spawn must never move the spend
|
|
144
|
+
// onto a credential the operator didn't approve for this entry. Every
|
|
145
|
+
// other run reads the operator's live setting, as always.
|
|
146
|
+
const { runner, clock: _clock, versionProbe: _probe, keyHome, timeoutMs: _hardTimeout, idleTimeoutMs: _idleTimeout, stdin: _callerStdin, accumulateUsage: _accumulateUsage, ...runOptions } = options;
|
|
147
|
+
// The live setting is read STRICTLY (atomic authority closure): a
|
|
148
|
+
// present mode file that says neither word is a value-shaped refusal
|
|
149
|
+
// before any stamp or process — never the subscription default a
|
|
150
|
+
// lenient read would coerce it to, which would spend on a credential
|
|
151
|
+
// the seal never proved. The same reader gates filing and the seal.
|
|
152
|
+
let authMode;
|
|
153
|
+
if (run.chainCycle != null && run.authMode != null) {
|
|
154
|
+
authMode = run.authMode;
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
const strict = readAuthModeStrict(spec.provider, keyHome);
|
|
158
|
+
if (!strict.ok) {
|
|
159
|
+
return {
|
|
160
|
+
kind: "refused",
|
|
161
|
+
reason: "auth-mode",
|
|
162
|
+
providerVersion: attested === null ? null : attested.version,
|
|
163
|
+
diagnostic: strict.problem,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
authMode = strict.mode;
|
|
167
|
+
}
|
|
168
|
+
const ownKeyEnv = OWN_KEY_ENV[spec.provider];
|
|
169
|
+
const managedKey = authMode === "api-key"
|
|
170
|
+
? readProviderKey(spec.provider, keyHome) ?? (process.env[PROVIDER_KEY_ENV[spec.provider]] || null)
|
|
171
|
+
: null;
|
|
172
|
+
// A pinned api-key entry with NO key is REFUSED, not spawned (Codex E3d
|
|
173
|
+
// review, finding 1): with nothing to inject, the provider CLI would
|
|
174
|
+
// quietly fall back to its cached subscription login — spend on a
|
|
175
|
+
// credential the entry never approved. A value-shaped refusal before any
|
|
176
|
+
// stamp: no process, no spend, no strike.
|
|
177
|
+
if (run.chainCycle != null && authMode === "api-key" && managedKey === null) {
|
|
178
|
+
return {
|
|
179
|
+
kind: "refused",
|
|
180
|
+
reason: "chain-credential",
|
|
181
|
+
providerVersion: attested === null ? null : attested.version,
|
|
182
|
+
diagnostic: `the approved entry is pinned to a ${spec.provider} API key, and no managed or ambient key exists — add one on the keys screen`,
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
// Resume XOR mint, enforced at the GATEWAY (Codex gemini verify round 2,
|
|
186
|
+
// finding 3): a resume and a minted start id are mutually exclusive —
|
|
187
|
+
// geminiArgv silently prefers --resume, so a minted id that rides
|
|
188
|
+
// alongside a resume would be stamped and later enforced against an
|
|
189
|
+
// envelope that never carried it, a paid protocol refusal. When resuming,
|
|
190
|
+
// the minted id is dropped here, before it is ever stamped.
|
|
191
|
+
// The minted session identity (A5): stamped durably BEFORE the start
|
|
192
|
+
// stamp — intent precedes the process, and the envelope must later
|
|
193
|
+
// MATCH this id or the run fails typed.
|
|
194
|
+
if (startSessionId !== undefined) {
|
|
195
|
+
store.stampRun(runId, { sessionId: startSessionId });
|
|
196
|
+
}
|
|
197
|
+
// The stamp precedes the spawn, so a crash between the two leaves a run
|
|
198
|
+
// that claims spend which never happened — the honest direction. A spawn
|
|
199
|
+
// before the stamp would leave spend no record claims, which is the lie
|
|
200
|
+
// the invariant exists to rule out. For attested providers the probed
|
|
201
|
+
// version rides the SAME durable write (B2). A CHAIN-BOUND run's stamp is
|
|
202
|
+
// the PRE-SPAWN CUSTODY PROOF (Codex E3d review, finding 3): one
|
|
203
|
+
// transaction re-derives the approval, cycle, entry, pin, and (past the
|
|
204
|
+
// base) the live paid-fallback grant, and stamps ONLY if all still stand
|
|
205
|
+
// — a grant revoked between admission and this instant refuses here.
|
|
206
|
+
// The spawn leg of the runner gate (MCP spec v6, review finding 4):
|
|
207
|
+
// the tuple re-proven against LIVE rows AFTER every awaited step, in
|
|
208
|
+
// the same breath as the stamp — a takeover during the attestation
|
|
209
|
+
// await can no longer slip a stale process through.
|
|
210
|
+
// THE PLANNER'S ROUTE, RE-PROVED AT THE SPAWN (final authority closure):
|
|
211
|
+
// the provenance the planner was admitted under is held to the authority
|
|
212
|
+
// its task holds RIGHT NOW — the strict working projection of a filed
|
|
213
|
+
// scope (exact terms, resolved profile, whole chain, route parity, digest,
|
|
214
|
+
// live auth mode), or the bare word `legacy` on a task with no scope. A
|
|
215
|
+
// scope rewritten, corrupted, or unresolved between the claim and this
|
|
216
|
+
// instant invokes no provider: a value-shaped refusal, no spend.
|
|
217
|
+
if (run.role === "planner") {
|
|
218
|
+
const proved = store.proveRouteForSpawn(runId, clock());
|
|
219
|
+
if (!proved.ok) {
|
|
220
|
+
return {
|
|
221
|
+
kind: "refused",
|
|
222
|
+
reason: "route-authority",
|
|
223
|
+
providerVersion: attested === null ? null : attested.version,
|
|
224
|
+
diagnostic: `the planner's route authority lapsed before spawn — ${proved.problem}`,
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
if (!store.proveRunnerCustodyForSpawn(runId, clock())) {
|
|
229
|
+
return {
|
|
230
|
+
kind: "refused",
|
|
231
|
+
reason: "runner-custody",
|
|
232
|
+
providerVersion: attested === null ? null : attested.version,
|
|
233
|
+
diagnostic: "the run's runner custody lapsed before spawn — the lease, the runner, or its repo binding no longer stands",
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
// THE STOP FENCE at the spawn (v52): a stop recorded against this run
|
|
237
|
+
// — or the attempt it runs under — before this instant spawns nothing.
|
|
238
|
+
// A value-shaped refusal, no stamp, no process, no spend; the road's
|
|
239
|
+
// settlement seals the attempt as interrupted.
|
|
240
|
+
const stopBeforeSpawn = store.applicableStopFor(runId);
|
|
241
|
+
if (stopBeforeSpawn !== null) {
|
|
242
|
+
return {
|
|
243
|
+
kind: "refused",
|
|
244
|
+
reason: "stopped",
|
|
245
|
+
providerVersion: attested === null ? null : attested.version,
|
|
246
|
+
diagnostic: `stopped by ${stopBeforeSpawn.requestedBy} (run #${stopBeforeSpawn.run}) before the provider spawned — nothing was spent`,
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
// THE CONTAINMENT FENCE at the spawn (OS containment plan): a required
|
|
250
|
+
// native policy this runner cannot meet — macOS, an undelegated cgroup,
|
|
251
|
+
// a missing helper — spawns nothing. A value-shaped refusal in the
|
|
252
|
+
// policy's own words, no stamp, no process, no spend.
|
|
253
|
+
const containment = currentContainment();
|
|
254
|
+
if (containment.refusal !== null) {
|
|
255
|
+
return {
|
|
256
|
+
kind: "refused",
|
|
257
|
+
reason: "containment",
|
|
258
|
+
providerVersion: attested === null ? null : attested.version,
|
|
259
|
+
diagnostic: containment.refusal,
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
if (run.chainCycle != null) {
|
|
263
|
+
const custody = attested !== null
|
|
264
|
+
? store.proveChainCustodyForSpawn(runId, clock(), attested.version)
|
|
265
|
+
: store.proveChainCustodyForSpawn(runId, clock());
|
|
266
|
+
if (!custody) {
|
|
267
|
+
return {
|
|
268
|
+
kind: "refused",
|
|
269
|
+
reason: "chain-custody",
|
|
270
|
+
providerVersion: attested === null ? null : attested.version,
|
|
271
|
+
diagnostic: "the run's chain custody lapsed before spawn — the approval, cycle, entry pin, or paid-fallback grant no longer stands",
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
else if (attested !== null)
|
|
276
|
+
store.stampProviderStart(runId, clock(), attested.version);
|
|
277
|
+
else
|
|
278
|
+
store.stampProviderStart(runId, clock());
|
|
279
|
+
const spawn = witnessedRunner(store, runId, clock, runner ?? adapter.defaultRunner);
|
|
280
|
+
let transportAnnouncedSessionId = null;
|
|
281
|
+
let transportSessionConflict = false;
|
|
282
|
+
// B3: the attested executable IS the spawned executable — one resolution.
|
|
283
|
+
// A MANAGED key reaches exactly its own provider's child environment
|
|
284
|
+
// (keys.ts): the foreign-credential strip already shed everybody
|
|
285
|
+
// else's, and the plane's own env never needed to carry it. A key
|
|
286
|
+
// already ambient in the environment keeps working; the managed file,
|
|
287
|
+
// being deliberate, wins.
|
|
288
|
+
const isolatedDb = isolatedAgentDatabase(runId);
|
|
289
|
+
// The project's tools (v80): exactly its MCP servers, and none of the
|
|
290
|
+
// operator's global or a repository's own. A review keeps its isolation.
|
|
291
|
+
const tools = invocation.phase === "review" ? null : runTools(store, runId, spec, options.keyHome, clock);
|
|
292
|
+
// The agent fence: Toolroll's own secrets, database and other runs'
|
|
293
|
+
// evidence stay out of reach, whatever the permission mode. A review is
|
|
294
|
+
// already confined to its sealed files.
|
|
295
|
+
const baseFence = invocation.phase === "review" ? [] : runFence(store, runId, options.keyHome);
|
|
296
|
+
const toolsDir = (spec.provider === "codex" || spec.provider === "openrouter") && baseFence.length > 0 ? tools?.privateDir : undefined;
|
|
297
|
+
const fence = toolsDir === undefined ? baseFence : [...baseFence, realpathSync(toolsDir)];
|
|
298
|
+
const launchArgv = tools === null ? argv : adapter.argv({ ...resolvedInvocation, toolArgv: tools.argv, fence });
|
|
299
|
+
const fenced = fenceLaunch(spec.provider, fence);
|
|
300
|
+
if (invocation.phase !== "review")
|
|
301
|
+
store.recordRunFence(runId, { method: fenced.method, paths: fence.length }, clock());
|
|
302
|
+
const billingSeen = { keySource: null, model: null, planWindows: false, answered: false };
|
|
303
|
+
// v105: a Codex run on its sign-in bills as Codex says it's signed in (a ChatGPT plan, or a saved key, or Bedrock),
|
|
304
|
+
// read right before it runs. Never with an injected runner: tests don't start a real Codex.
|
|
305
|
+
const codexLogin = spec.provider === "codex" && authMode === "subscription" && runner === undefined
|
|
306
|
+
? await codexLoginBilling(attested !== null ? attested.executable : adapter.binary, [...(runOptions.omitEnv ?? []), ...adapter.extraOmitEnv, ...ownKeyEnv])
|
|
307
|
+
: null;
|
|
308
|
+
let result;
|
|
309
|
+
try {
|
|
310
|
+
// Under the stop watch (v52): the run's stop row is re-read while the
|
|
311
|
+
// provider runs, and a stop kills THIS run's process group through the
|
|
312
|
+
// handle registered under its owner tag — the exact-attempt road,
|
|
313
|
+
// never the global sweep. Settlement then reads the same row.
|
|
314
|
+
result = await underStopWatch(store, runId, () => spawn(attested !== null ? attested.executable : adapter.binary, launchArgv, {
|
|
315
|
+
...(fenced.wrap.length === 0 ? {} : { fence: fenced.wrap }),
|
|
316
|
+
...runOptions,
|
|
317
|
+
// v105: how the CLI really billed this turn (its key source, its plan's windows), read off its own stream.
|
|
318
|
+
onStreamEvent: event => {
|
|
319
|
+
if (event["type"] === "system" && event["subtype"] === "init") {
|
|
320
|
+
if (typeof event["apiKeySource"] === "string")
|
|
321
|
+
billingSeen.keySource = event["apiKeySource"].slice(0, 80);
|
|
322
|
+
if (typeof event["model"] === "string")
|
|
323
|
+
billingSeen.model = event["model"].slice(0, 200);
|
|
324
|
+
}
|
|
325
|
+
else if (event["type"] === "rate_limit_event") {
|
|
326
|
+
billingSeen.planWindows = true;
|
|
327
|
+
}
|
|
328
|
+
else if (event["type"] === "result" && event["is_error"] !== true && event["subtype"] === "success") {
|
|
329
|
+
billingSeen.answered = true;
|
|
330
|
+
}
|
|
331
|
+
runOptions.onStreamEvent?.(event);
|
|
332
|
+
},
|
|
333
|
+
owner: runOwnerTag(store, runId),
|
|
334
|
+
beforeSpawn: () => store.applicableStopFor(runId) === null && store.getRun(runId)?.outcome === null,
|
|
335
|
+
onSpawn: pid => {
|
|
336
|
+
runOptions.onSpawn?.(pid);
|
|
337
|
+
if (store.applicableStopFor(runId) !== null)
|
|
338
|
+
throw new Error("the attempt was stopped before spawn custody completed");
|
|
339
|
+
},
|
|
340
|
+
...(stdin === undefined ? {} : { stdin }),
|
|
341
|
+
...(hardTimeoutMs === undefined ? {} : { timeoutMs: hardTimeoutMs }),
|
|
342
|
+
...(idleTimeoutMs === undefined ? {} : { idleTimeoutMs }),
|
|
343
|
+
...(defaultTimeoutMs === undefined ? {} : { timeoutMs: defaultTimeoutMs }),
|
|
344
|
+
env: {
|
|
345
|
+
...(runOptions.env ?? {}),
|
|
346
|
+
...(tools?.env ?? {}),
|
|
347
|
+
...(managedKey === null ? {} : { [PROVIDER_KEY_ENV[spec.provider]]: managedKey }),
|
|
348
|
+
// This key is deliberately last: no caller may point an agent back at
|
|
349
|
+
// the live control database through a generic RunOptions override.
|
|
350
|
+
...childDatabaseEnv(isolatedDb.file),
|
|
351
|
+
},
|
|
352
|
+
omitEnv: [
|
|
353
|
+
...(runOptions.omitEnv ?? []),
|
|
354
|
+
...adapter.extraOmitEnv,
|
|
355
|
+
// Subscription mode: shed this provider's OWN key too, so an ambient
|
|
356
|
+
// one cannot force API billing over the login the operator prefers.
|
|
357
|
+
// Api-key mode: shed every OTHER own-key alias (finding 1) — gemini
|
|
358
|
+
// reads GOOGLE_API_KEY as well as GEMINI_API_KEY, and a stray alias
|
|
359
|
+
// must not override the ONE canonical key this mode injected.
|
|
360
|
+
...(authMode === "subscription"
|
|
361
|
+
? ownKeyEnv
|
|
362
|
+
: ownKeyEnv.filter(name => name !== PROVIDER_KEY_ENV[spec.provider])),
|
|
363
|
+
],
|
|
364
|
+
// Providers run in their own process group (M6.12): the harness spawns
|
|
365
|
+
// shells and tools of its own, and both the timeout and the watch's
|
|
366
|
+
// hard stop must end the whole tree, not orphan the grandchildren.
|
|
367
|
+
processGroup: true,
|
|
368
|
+
// The session registry's crash guarantee (M6.9): the id is stamped the
|
|
369
|
+
// moment the stream announces it, first write wins — a daemon that
|
|
370
|
+
// dies mid-turn still knows which session to offer the successor.
|
|
371
|
+
onSessionId: id => {
|
|
372
|
+
const normalized = transportSessionId(id);
|
|
373
|
+
if (normalized === null)
|
|
374
|
+
return;
|
|
375
|
+
if (transportAnnouncedSessionId === null)
|
|
376
|
+
transportAnnouncedSessionId = normalized;
|
|
377
|
+
else if (transportAnnouncedSessionId !== normalized)
|
|
378
|
+
transportSessionConflict = true;
|
|
379
|
+
store.stampRun(runId, { sessionId: normalized });
|
|
380
|
+
},
|
|
381
|
+
}));
|
|
382
|
+
}
|
|
383
|
+
finally {
|
|
384
|
+
removeAgentDatabase(isolatedDb.dir);
|
|
385
|
+
tools?.cleanup();
|
|
386
|
+
}
|
|
387
|
+
// v105: the run's billing, fixed from evidence before its usage is priced: a key it was given is a key; otherwise
|
|
388
|
+
// what the CLI said (Claude's stream) or was last seen doing (Codex's account), and the setting only when neither.
|
|
389
|
+
const evidence = spec.provider === "claude" ? claudeBillingFrom(billingSeen)
|
|
390
|
+
// Codex signed in with ChatGPT is its plan, unless its plan is itself billed by use (seen by the console's probe).
|
|
391
|
+
: codexLogin === "subscription" ? seenBilling(store.handle, "codex") ?? "subscription" : codexLogin;
|
|
392
|
+
// What this computer's Claude does with no key from us (teammates and drafts bill the same way): learnt only from a
|
|
393
|
+
// run we gave no key that got as far as an answer.
|
|
394
|
+
if (spec.provider === "claude" && managedKey === null && authMode === "subscription" && billingSeen.answered && evidence !== null) {
|
|
395
|
+
store.recordProviderBilling("claude", evidence, new Date());
|
|
396
|
+
}
|
|
397
|
+
store.fixRunBilling(runId, authMode === "api-key" ? "api-key" : evidence ?? billingOf(spec.provider, store.handle), new Date());
|
|
398
|
+
const envelope = adapter.parse(result.stdout);
|
|
399
|
+
// The callback is the crash-safe early stamp; the parsed envelope is the
|
|
400
|
+
// authoritative completed-stream account. Stamp it as a fallback for an
|
|
401
|
+
// alternate transport. On fresh runs COALESCE makes callback/parser
|
|
402
|
+
// disagreement visible in the durable row; resumed runs need the separate
|
|
403
|
+
// callback latch because their row deliberately carries the input session.
|
|
404
|
+
if (envelope.sessionId !== null) {
|
|
405
|
+
store.stampRun(runId, { sessionId: envelope.sessionId });
|
|
406
|
+
}
|
|
407
|
+
const durableSessionId = store.getRun(runId)?.sessionId ?? null;
|
|
408
|
+
// The durable id, callback id, and retained envelope must all resolve to
|
|
409
|
+
// ONE identity. This deliberately includes resumed turns: a provider that
|
|
410
|
+
// returns a fork has not proved the same-session repair/warm-resume
|
|
411
|
+
// contract, and COALESCE must not disguise row A / process B as success.
|
|
412
|
+
const sessionIdentityProblem = transportSessionConflict
|
|
413
|
+
? "the provider transport announced conflicting session ids"
|
|
414
|
+
: transportAnnouncedSessionId !== null && transportAnnouncedSessionId !== envelope.sessionId
|
|
415
|
+
? "the retained provider envelope did not match the session identity announced through the transport callback"
|
|
416
|
+
: durableSessionId !== envelope.sessionId
|
|
417
|
+
? envelope.sessionId === null
|
|
418
|
+
? "the retained provider envelope did not confirm the durably recorded session id"
|
|
419
|
+
: requestedResumeSessionId !== null
|
|
420
|
+
? "the provider returned a session id different from the exact identity recorded for resume"
|
|
421
|
+
: "the provider session id was different from the identity durably recorded when the stream initialized"
|
|
422
|
+
: null;
|
|
423
|
+
store.recordUsage(runId, {
|
|
424
|
+
...(envelope.tokensIn === null ? {} : { tokensIn: envelope.tokensIn + (options.accumulateUsage ? run.tokensIn ?? 0 : 0) }),
|
|
425
|
+
...(envelope.tokensOut === null ? {} : { tokensOut: envelope.tokensOut + (options.accumulateUsage ? run.tokensOut ?? 0 : 0) }),
|
|
426
|
+
...(envelope.costUsd === null ? {} : { costUsd: envelope.costUsd + (options.accumulateUsage ? run.costUsd ?? 0 : 0) }),
|
|
427
|
+
...(envelope.usageRaw === null ? {} : { usageJson: envelope.usageRaw }),
|
|
428
|
+
});
|
|
429
|
+
// The fallback taxonomy stamp (E2): classify HERE, where the evidence
|
|
430
|
+
// still exists — the structural terminal off this exact envelope, the
|
|
431
|
+
// AUTHORITATIVE version the gateway proved at spawn, and the auth mode
|
|
432
|
+
// that spawned it. `classifyTerminal` is fail-closed: with no
|
|
433
|
+
// fixture-backed recognizer for this (provider, version) — the state
|
|
434
|
+
// every build ships in — it can only ever return a non-eligible class,
|
|
435
|
+
// so this stamp authorizes nothing. It is the honest disposal record the
|
|
436
|
+
// dispatch's C8 gate later re-checks against `hasRecognizer` before it
|
|
437
|
+
// reads the class as anything more than history. Tier-1 providers prove
|
|
438
|
+
// no version here yet (attested === null), so they classify fail-closed
|
|
439
|
+
// until their exhaustion fixture — and the version proving it needs — is
|
|
440
|
+
// captured and reviewed.
|
|
441
|
+
const terminalClass = classifyTerminal({
|
|
442
|
+
provider: spec.provider,
|
|
443
|
+
version: attested === null ? null : attested.version,
|
|
444
|
+
authMode,
|
|
445
|
+
terminal: envelope.structuralTerminal,
|
|
446
|
+
});
|
|
447
|
+
store.stampTerminalClass(runId, authMode, terminalClass);
|
|
448
|
+
// Contradictory structural identity is a provider protocol failure, not
|
|
449
|
+
// a session selection problem. Usage above remains recorded because the
|
|
450
|
+
// process ran and spent, but no downstream handoff or repair may trust
|
|
451
|
+
// either id from this envelope.
|
|
452
|
+
if (envelope.protocolError !== null) {
|
|
453
|
+
return {
|
|
454
|
+
kind: "refused",
|
|
455
|
+
reason: "provider-protocol",
|
|
456
|
+
providerVersion: attested === null ? null : attested.version,
|
|
457
|
+
diagnostic: envelope.protocolError,
|
|
458
|
+
finalMessage: envelope.finalMessage,
|
|
459
|
+
};
|
|
460
|
+
}
|
|
461
|
+
// The minted-identity proof (A5): once the harness initialized, the id
|
|
462
|
+
// it announced must be the id the plane minted — anything else means
|
|
463
|
+
// the session on disk is not the session on record, and repair must
|
|
464
|
+
// never resume it. Usage above is already recorded: the spend happened.
|
|
465
|
+
if (startSessionId !== undefined &&
|
|
466
|
+
envelope.initObserved === true &&
|
|
467
|
+
envelope.sessionId !== startSessionId) {
|
|
468
|
+
return {
|
|
469
|
+
kind: "refused",
|
|
470
|
+
reason: "provider-protocol",
|
|
471
|
+
providerVersion: attested === null ? null : attested.version,
|
|
472
|
+
diagnostic: envelope.sessionId === null
|
|
473
|
+
? "the harness initialized without announcing its session id"
|
|
474
|
+
: "the harness announced a session id different from the one it was started under",
|
|
475
|
+
finalMessage: envelope.finalMessage,
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
// An early transport announcement, a pre-existing resume identity, and
|
|
479
|
+
// the completed retained envelope must resolve to one canonical id. If
|
|
480
|
+
// either side is missing or different, no handoff or warm resume may use
|
|
481
|
+
// this run even though its spend remains on the record.
|
|
482
|
+
if (sessionIdentityProblem !== null) {
|
|
483
|
+
return {
|
|
484
|
+
kind: "refused",
|
|
485
|
+
reason: "provider-protocol",
|
|
486
|
+
providerVersion: attested === null ? null : attested.version,
|
|
487
|
+
diagnostic: sessionIdentityProblem,
|
|
488
|
+
finalMessage: envelope.finalMessage,
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
// A provider's explicit failure terminal always wins over its process exit
|
|
492
|
+
// status. Exit 0 only proves the wrapper closed cleanly; it cannot turn a
|
|
493
|
+
// failed model turn into a successful build.
|
|
494
|
+
if (result.code === 0 && envelope.structuralTerminal?.failed === true) {
|
|
495
|
+
return {
|
|
496
|
+
kind: "refused",
|
|
497
|
+
reason: "provider-protocol",
|
|
498
|
+
providerVersion: attested === null ? null : attested.version,
|
|
499
|
+
diagnostic: "the provider reported a failed terminal despite exiting with code 0",
|
|
500
|
+
finalMessage: envelope.finalMessage,
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
// The terminal contract (A4): when the audit requires it, a zero exit is
|
|
504
|
+
// believed ONLY with initialization observed and the structural success
|
|
505
|
+
// terminal present. A missing, truncated, or error-status terminal on
|
|
506
|
+
// exit 0 is the harness breaking its own protocol — never a completed
|
|
507
|
+
// build, and the handoff is never ingested.
|
|
508
|
+
if (auditOf(spec.provider).terminalContract === "required" &&
|
|
509
|
+
result.code === 0 &&
|
|
510
|
+
!(envelope.initObserved === true && envelope.promptConsumed === true)) {
|
|
511
|
+
return {
|
|
512
|
+
kind: "refused",
|
|
513
|
+
reason: "provider-protocol",
|
|
514
|
+
providerVersion: attested === null ? null : attested.version,
|
|
515
|
+
diagnostic: envelope.diagnostic ??
|
|
516
|
+
(envelope.initObserved === true
|
|
517
|
+
? "the harness exited 0 without its success terminal — the stream ended mid-protocol"
|
|
518
|
+
: "the harness exited 0 without ever initializing"),
|
|
519
|
+
finalMessage: envelope.finalMessage,
|
|
520
|
+
};
|
|
521
|
+
}
|
|
522
|
+
return {
|
|
523
|
+
kind: "ran",
|
|
524
|
+
outcome: {
|
|
525
|
+
code: result.code,
|
|
526
|
+
stderr: result.stderr,
|
|
527
|
+
timedOut: result.timedOut,
|
|
528
|
+
notFound: result.notFound,
|
|
529
|
+
sessionId: envelope.sessionId,
|
|
530
|
+
finalMessage: envelope.finalMessage,
|
|
531
|
+
ending: envelope.ending ?? null,
|
|
532
|
+
usage: {
|
|
533
|
+
tokensIn: envelope.tokensIn,
|
|
534
|
+
tokensOut: envelope.tokensOut,
|
|
535
|
+
costUsd: envelope.costUsd,
|
|
536
|
+
},
|
|
537
|
+
// Gated on the run having NOTHING to show (Codex M5-M8 audit, C-8),
|
|
538
|
+
// where "nothing to show" is STRUCTURAL when the transport can say so
|
|
539
|
+
// (arc 1 finding 15): a retained error result carries diagnostic text
|
|
540
|
+
// in finalMessage, and that prose must not read as an agent's attempt.
|
|
541
|
+
// Transports without the consumption signal keep the historical
|
|
542
|
+
// no-final-message rule. A nonzero exit WITH a consumed prompt is a
|
|
543
|
+
// failed agent turn — the agent ran, spoke, and failed — and must be
|
|
544
|
+
// classified as that, never as the harness failing to come up.
|
|
545
|
+
initFailed: envelope.initObserved === false &&
|
|
546
|
+
(envelope.promptConsumed === null
|
|
547
|
+
? envelope.finalMessage === null
|
|
548
|
+
: envelope.promptConsumed === false) &&
|
|
549
|
+
!result.timedOut &&
|
|
550
|
+
!result.notFound,
|
|
551
|
+
},
|
|
552
|
+
};
|
|
553
|
+
}
|
|
554
|
+
/**
|
|
555
|
+
* The HELD invocation gateway (Parity II Phase 2, spec v2 S0b): the only
|
|
556
|
+
* door to the held-session transport, symmetric with invokeAgent — same
|
|
557
|
+
* open-run verification, same provider-match rule (claude only in Phase
|
|
558
|
+
* 2: nothing else can hold), same stamp-before-spawn honesty. It returns
|
|
559
|
+
* the live handle rather than awaiting session end: ownership of the
|
|
560
|
+
* hold belongs to the coordinator, never to a promise chain that would
|
|
561
|
+
* stall the watch.
|
|
562
|
+
*/
|
|
563
|
+
export async function invokeHeldAgent(store, runId, spec, argv, options) {
|
|
564
|
+
const clock = options.clock ?? (() => new Date());
|
|
565
|
+
const run = store.getRun(runId);
|
|
566
|
+
if (run === null || run.outcome !== null) {
|
|
567
|
+
throw new Error(`run ${runId} is not an open attempt — nothing spends without a run record that will outlive it`);
|
|
568
|
+
}
|
|
569
|
+
if (spec.provider !== "claude" || run.provider !== "claude") {
|
|
570
|
+
throw new Error(`run ${runId}: only claude can hold a session in Phase 2 — ${run.provider}/${spec.provider} cannot`);
|
|
571
|
+
}
|
|
572
|
+
const adapter = adapterFor("claude");
|
|
573
|
+
const { clock: _clock, starter, socketPath, cookie, graceMs, events, readyTimeoutMs, keyHome, ...runOptions } = options;
|
|
574
|
+
// The spawn leg of the runner gate (MCP spec v6) — held sessions are a
|
|
575
|
+
// provider spawn like any other; a lapsed custody throws, because the
|
|
576
|
+
// held road's contract is exceptions, not refusal values.
|
|
577
|
+
if (!store.proveRunnerCustodyForSpawn(runId, clock())) {
|
|
578
|
+
throw new Error(`run ${runId}: runner custody lapsed before the held spawn — the lease, the runner, or its repo binding no longer stands`);
|
|
579
|
+
}
|
|
580
|
+
// The held road reads the mode STRICTLY too (atomic authority closure),
|
|
581
|
+
// before the start stamp: a mode file that says neither word throws in
|
|
582
|
+
// its words — the held road's contract — and no spend is claimed.
|
|
583
|
+
const heldStrict = readAuthModeStrict("claude", keyHome);
|
|
584
|
+
if (!heldStrict.ok)
|
|
585
|
+
throw new Error(`run ${runId}: ${heldStrict.problem}`);
|
|
586
|
+
const heldMode = heldStrict.mode;
|
|
587
|
+
// The containment fence, same direction as the one-shot gateway: a
|
|
588
|
+
// required policy this runner cannot meet throws in its words before
|
|
589
|
+
// the start stamp, and no supervisor spawns.
|
|
590
|
+
const heldContainment = currentContainment();
|
|
591
|
+
if (heldContainment.refusal !== null)
|
|
592
|
+
throw new Error(`run ${runId}: ${heldContainment.refusal}`);
|
|
593
|
+
// The stamp precedes the spawn — same direction as the one-shot gateway.
|
|
594
|
+
store.stampProviderStart(runId, clock());
|
|
595
|
+
const heldKey = heldMode === "api-key" ? readProviderKey("claude", keyHome) ?? (process.env[PROVIDER_KEY_ENV.claude] || null) : null;
|
|
596
|
+
const heldSeen = { keySource: null, model: null, planWindows: false, decided: false };
|
|
597
|
+
// A session given a key bills the key from its first byte.
|
|
598
|
+
if (heldMode === "api-key")
|
|
599
|
+
store.fixRunBilling(runId, "api-key", clock());
|
|
600
|
+
const start = starter ?? startClaudeHeldSession;
|
|
601
|
+
const isolatedDb = isolatedAgentDatabase(runId);
|
|
602
|
+
// The project's tools (v80), as for every build: exactly its MCP servers;
|
|
603
|
+
// and the agent fence around Toolroll's own secrets.
|
|
604
|
+
const heldTools = runTools(store, runId, spec, keyHome, clock);
|
|
605
|
+
const heldFence = runFence(store, runId, keyHome);
|
|
606
|
+
argv = [...argv, ...heldTools.argv, ...(heldFence.length > 0 ? ["--settings", claudeFenceSettings(heldFence)] : [])];
|
|
607
|
+
const heldLaunch = fenceLaunch("claude", heldFence);
|
|
608
|
+
store.recordRunFence(runId, { method: heldLaunch.method, paths: heldFence.length }, clock());
|
|
609
|
+
let started;
|
|
610
|
+
let heldWitness;
|
|
611
|
+
let unknownTree = false;
|
|
612
|
+
let nativeHeld = false;
|
|
613
|
+
try {
|
|
614
|
+
started = await start(adapter.binary, argv, {
|
|
615
|
+
...(heldLaunch.wrap.length === 0 ? {} : { fence: heldLaunch.wrap }),
|
|
616
|
+
...runOptions,
|
|
617
|
+
beforeSpawn: () => {
|
|
618
|
+
if (store.applicableStopFor(runId) !== null)
|
|
619
|
+
return false;
|
|
620
|
+
heldWitness = store.reserveRunProcess(runId, clock(), false);
|
|
621
|
+
return true;
|
|
622
|
+
},
|
|
623
|
+
onSpawn: pid => {
|
|
624
|
+
store.recordRunProcess(runId, pid, clock(), false, heldWitness);
|
|
625
|
+
runOptions.onSpawn?.(pid);
|
|
626
|
+
},
|
|
627
|
+
onContainer: info => {
|
|
628
|
+
nativeHeld = true;
|
|
629
|
+
if (heldWitness !== undefined)
|
|
630
|
+
store.recordRunContainer(heldWitness, info.backend, info.id, info.identity);
|
|
631
|
+
runOptions.onContainer?.(info);
|
|
632
|
+
},
|
|
633
|
+
onContainerEmpty: () => {
|
|
634
|
+
if (heldWitness !== undefined)
|
|
635
|
+
store.markRunContainerEmpty(heldWitness, clock());
|
|
636
|
+
runOptions.onContainerEmpty?.();
|
|
637
|
+
},
|
|
638
|
+
onDescendant: (pid, group) => {
|
|
639
|
+
store.recordRunProcess(runId, pid, clock(), group);
|
|
640
|
+
runOptions.onDescendant?.(pid, group);
|
|
641
|
+
},
|
|
642
|
+
onDescendantWriteFailure: rows => runOptions.onDescendant === undefined
|
|
643
|
+
&& preserveObservedProcesses(store, runId, clock(), rows),
|
|
644
|
+
onDescendantExit: (pid, group) => {
|
|
645
|
+
store.recordRunProcessExits(runId, clock(), { pid, group });
|
|
646
|
+
runOptions.onDescendantExit?.(pid, group);
|
|
647
|
+
},
|
|
648
|
+
onObservationFailure: failure => {
|
|
649
|
+
recordProcessObservationFailure(store, runId, clock(), failure);
|
|
650
|
+
runOptions.onObservationFailure?.(failure);
|
|
651
|
+
},
|
|
652
|
+
onUnknown: () => {
|
|
653
|
+
if (!nativeHeld && !unknownTree) {
|
|
654
|
+
store.reserveRunProcess(runId, clock());
|
|
655
|
+
unknownTree = true;
|
|
656
|
+
}
|
|
657
|
+
runOptions.onUnknown?.();
|
|
658
|
+
},
|
|
659
|
+
env: {
|
|
660
|
+
...(runOptions.env ?? {}),
|
|
661
|
+
...heldTools.env,
|
|
662
|
+
...(heldKey === null ? {} : { [PROVIDER_KEY_ENV.claude]: heldKey }),
|
|
663
|
+
...childDatabaseEnv(isolatedDb.file),
|
|
664
|
+
},
|
|
665
|
+
omitEnv: [
|
|
666
|
+
...(runOptions.omitEnv ?? []),
|
|
667
|
+
...adapter.extraOmitEnv,
|
|
668
|
+
...(heldMode === "subscription" ? OWN_KEY_ENV.claude : []),
|
|
669
|
+
],
|
|
670
|
+
socketPath,
|
|
671
|
+
cookie,
|
|
672
|
+
...(graceMs === undefined ? {} : { graceMs }),
|
|
673
|
+
...(readyTimeoutMs === undefined ? {} : { readyTimeoutMs }),
|
|
674
|
+
events: {
|
|
675
|
+
...events,
|
|
676
|
+
// v105: the session's billing from its own stream. A sign-in can't change inside one process, and Claude reports
|
|
677
|
+
// its plan's windows on a process's first answer but later only when they move: so the first answer decides, and
|
|
678
|
+
// after it only a named key source or a cloud model can make the session a key.
|
|
679
|
+
onStreamEvent: event => {
|
|
680
|
+
if (event["type"] === "system" && event["subtype"] === "init") {
|
|
681
|
+
if (typeof event["apiKeySource"] === "string")
|
|
682
|
+
heldSeen.keySource = event["apiKeySource"].slice(0, 80);
|
|
683
|
+
if (typeof event["model"] === "string")
|
|
684
|
+
heldSeen.model = event["model"].slice(0, 200);
|
|
685
|
+
}
|
|
686
|
+
else if (event["type"] === "rate_limit_event") {
|
|
687
|
+
heldSeen.planWindows = true;
|
|
688
|
+
}
|
|
689
|
+
else if (event["type"] === "result" && event["is_error"] !== true && event["subtype"] === "success") {
|
|
690
|
+
// Only an answer decides: a failure before the first answer (a startup or sign-in error) says nothing yet.
|
|
691
|
+
const billing = heldMode === "api-key" ? "api-key"
|
|
692
|
+
: !heldSeen.decided ? claudeBillingFrom(heldSeen)
|
|
693
|
+
: claudeBillingFrom({ ...heldSeen, keySource: heldSeen.keySource === "none" ? null : heldSeen.keySource, planWindows: false }) === "api-key" ? "api-key" : null;
|
|
694
|
+
if (billing !== null) {
|
|
695
|
+
store.fixRunBilling(runId, billing, new Date());
|
|
696
|
+
if (!heldSeen.decided && heldMode === "subscription" && heldKey === null)
|
|
697
|
+
store.recordProviderBilling("claude", billing, new Date());
|
|
698
|
+
}
|
|
699
|
+
heldSeen.decided = true;
|
|
700
|
+
}
|
|
701
|
+
events?.onStreamEvent?.(event);
|
|
702
|
+
},
|
|
703
|
+
onSessionId: id => {
|
|
704
|
+
const normalized = transportSessionId(id);
|
|
705
|
+
if (normalized === null)
|
|
706
|
+
return;
|
|
707
|
+
store.stampRun(runId, { sessionId: normalized });
|
|
708
|
+
try {
|
|
709
|
+
events?.onSessionId?.(normalized);
|
|
710
|
+
}
|
|
711
|
+
catch {
|
|
712
|
+
// Observational.
|
|
713
|
+
}
|
|
714
|
+
},
|
|
715
|
+
},
|
|
716
|
+
});
|
|
717
|
+
}
|
|
718
|
+
catch (error) {
|
|
719
|
+
removeAgentDatabase(isolatedDb.dir);
|
|
720
|
+
heldTools.cleanup();
|
|
721
|
+
throw error;
|
|
722
|
+
}
|
|
723
|
+
if (!started.ok) {
|
|
724
|
+
removeAgentDatabase(isolatedDb.dir);
|
|
725
|
+
heldTools.cleanup();
|
|
726
|
+
}
|
|
727
|
+
else {
|
|
728
|
+
void started.handle.exited.then(() => { removeAgentDatabase(isolatedDb.dir); heldTools.cleanup(); }, () => { removeAgentDatabase(isolatedDb.dir); heldTools.cleanup(); });
|
|
729
|
+
}
|
|
730
|
+
return started;
|
|
731
|
+
}
|
|
732
|
+
/** The paths this run's agent may not reach: the state folder around the live database (but not its own worktree) and ~/.toolroll (or its older name). */
|
|
733
|
+
function runFence(store, runId, keyHome) {
|
|
734
|
+
try {
|
|
735
|
+
// A plane always has a database file; an in-memory store (a test) has nothing to fence.
|
|
736
|
+
const databaseFile = store.databaseFile();
|
|
737
|
+
if (databaseFile === null)
|
|
738
|
+
return [];
|
|
739
|
+
return agentFence({ databaseFile, worktree: store.getRun(runId)?.worktree ?? null, ...(keyHome === undefined ? {} : { home: keyHome }) });
|
|
740
|
+
}
|
|
741
|
+
catch {
|
|
742
|
+
return [];
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
/** How the fence reaches a provider: Codex through its own sandbox profile
|
|
746
|
+
* (already in its argv; wrapping it would nest sandboxes), Claude and
|
|
747
|
+
* Gemini inside the macOS sandbox the spawn road applies (`wrap`), Claude
|
|
748
|
+
* elsewhere through its own file-tool rules only. */
|
|
749
|
+
function fenceLaunch(provider, fence) {
|
|
750
|
+
if (fence.length === 0)
|
|
751
|
+
return { wrap: [], method: "none" };
|
|
752
|
+
if (provider === "codex" || provider === "openrouter")
|
|
753
|
+
return { wrap: [], method: "codex-profile" };
|
|
754
|
+
if (macosFenceAvailable())
|
|
755
|
+
return { wrap: fence, method: "macos-sandbox" };
|
|
756
|
+
if (linuxFenceAvailable())
|
|
757
|
+
return { wrap: fence, method: "linux-bubblewrap" };
|
|
758
|
+
return { wrap: [], method: provider === "claude" ? "claude-rules" : "none" };
|
|
759
|
+
}
|
|
760
|
+
/** One launch's tools, or — when they cannot be prepared — the same isolation with none, never the operator's global servers. */
|
|
761
|
+
function runTools(store, runId, spec, keyHome, clock) {
|
|
762
|
+
try {
|
|
763
|
+
return prepareRunTools(store, runId, spec.provider, { ...(keyHome === undefined ? {} : { home: keyHome }), now: clock(), includeModel: spec.model === null });
|
|
764
|
+
}
|
|
765
|
+
catch {
|
|
766
|
+
return noToolsArgs(spec.provider, spec.model === null);
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
/** How Codex says it's signed in (`codex login status`): a ChatGPT sign-in is its plan; a key or Bedrock is a key;
|
|
770
|
+
* null when it says neither (not signed in, or it didn't answer). */
|
|
771
|
+
async function codexLoginBilling(binary, omitEnv) {
|
|
772
|
+
try {
|
|
773
|
+
const status = await runCommand(binary, ["login", "status"], { timeoutMs: 15_000, omitEnv, processGroup: true });
|
|
774
|
+
const said = `${status.stdout}\n${status.stderr}`;
|
|
775
|
+
return /using chatgpt/i.test(said) ? "subscription" : /api key|bedrock/i.test(said) ? "api-key" : null;
|
|
776
|
+
}
|
|
777
|
+
catch {
|
|
778
|
+
return null;
|
|
779
|
+
}
|
|
780
|
+
}
|