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/provider.js
ADDED
|
@@ -0,0 +1,935 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The provider registry: every way this plane can spend money on an agent,
|
|
3
|
+
* in one file (§5's "one door", widened to three harness shapes).
|
|
4
|
+
*
|
|
5
|
+
* A provider here is how to SPAWN a harness and how to READ its envelope —
|
|
6
|
+
* argv dialect, transport, session identity, usage. The briefs, the mailbox
|
|
7
|
+
* protocol, and every custody proof are provider-neutral and live where
|
|
8
|
+
* they always did; this module owns only the dialect differences.
|
|
9
|
+
*
|
|
10
|
+
* Boundary (the architecture test enforces it): only `invoke.ts` may import
|
|
11
|
+
* the spawning surface (`adapterFor`); everything else that needs provider
|
|
12
|
+
* facts uses the non-spending inspection surface (`PROVIDERS`,
|
|
13
|
+
* `inspectionOf`) — identification must never be a way to start an LLM.
|
|
14
|
+
*
|
|
15
|
+
* `openrouter` is deliberately not a fourth transport: it is the codex
|
|
16
|
+
* harness pointed at OpenRouter through `-c` overrides whose keys and
|
|
17
|
+
* values are CONSTANTS below — no caller-supplied dotted keys, values
|
|
18
|
+
* TOML-quoted deterministically, and the API key excluded from every
|
|
19
|
+
* shell the model itself launches (Codex provider review, Q5).
|
|
20
|
+
*/
|
|
21
|
+
import { claudeFenceSettings, codexFenceArgv } from "./agent-fence.js";
|
|
22
|
+
import {} from "./exec.js";
|
|
23
|
+
import { runStreamJsonl, runClaudeStreamJsonl, runGeminiStreamJsonl } from "./exec.js";
|
|
24
|
+
import { scanForSecrets } from "./evidence.js";
|
|
25
|
+
import { REVIEW_OUTPUT_LIMITS } from "./structured-output.js";
|
|
26
|
+
export const PROVIDER_IDS = ["claude", "codex", "openrouter", "gemini"];
|
|
27
|
+
export function isProviderId(value) {
|
|
28
|
+
return PROVIDER_IDS.includes(value);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Model ids cross providers ("anthropic/claude-sonnet-4.5", "gpt-5-codex",
|
|
32
|
+
* "opus"). Bounded and printable, never leading-dash (argv safety), and no
|
|
33
|
+
* TOML-hostile characters — but NOT alphanumeric-only, which would refuse
|
|
34
|
+
* real ids carrying `/ . : -` (Codex provider review, Q5).
|
|
35
|
+
*/
|
|
36
|
+
export const MODEL_ID = /^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$/;
|
|
37
|
+
export function validModelId(model) {
|
|
38
|
+
return model === null || MODEL_ID.test(model);
|
|
39
|
+
}
|
|
40
|
+
const USAGE_JSON_CAP = 8 * 1024;
|
|
41
|
+
/** Empty and whitespace-only identities are absence, never resumable ids. */
|
|
42
|
+
function sessionIdOf(value) {
|
|
43
|
+
if (typeof value !== "string")
|
|
44
|
+
return null;
|
|
45
|
+
const id = value.trim();
|
|
46
|
+
return id === "" ? null : id;
|
|
47
|
+
}
|
|
48
|
+
/** Deterministic TOML basic-string quoting for -c values — never the CLI's raw fallback. */
|
|
49
|
+
function toml(value) {
|
|
50
|
+
return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
51
|
+
}
|
|
52
|
+
/** OpenRouter rides codex under a PRIVATE provider key, isolated from user config. */
|
|
53
|
+
const OPENROUTER_PROVIDER_KEY = "standing-orders_openrouter";
|
|
54
|
+
export const OPENROUTER_ENV_KEY = "OPENROUTER_API_KEY";
|
|
55
|
+
const OPENROUTER_BASE_URL = "https://openrouter.ai/api/v1";
|
|
56
|
+
/**
|
|
57
|
+
* The held-session argv (Parity II Phase 2): the same family as
|
|
58
|
+
* claudeArgv MINUS the positional prompt — every turn, the brief
|
|
59
|
+
* included, rides stdin as stream-json — plus the input format flag.
|
|
60
|
+
* maxBudgetUsd is the Probe-6 backstop: the CLI's cap is cumulative
|
|
61
|
+
* across the whole process, so the remaining authorization budget rides
|
|
62
|
+
* the argv and later turns cannot spend past it.
|
|
63
|
+
*/
|
|
64
|
+
export const claudeHeldArgv = (invocation) => [
|
|
65
|
+
"-p",
|
|
66
|
+
...(invocation.resumeSession === null ? [] : ["--resume", invocation.resumeSession]),
|
|
67
|
+
"--input-format",
|
|
68
|
+
"stream-json",
|
|
69
|
+
"--output-format",
|
|
70
|
+
"stream-json",
|
|
71
|
+
"--verbose",
|
|
72
|
+
"--max-turns",
|
|
73
|
+
String(invocation.maxTurns),
|
|
74
|
+
...(invocation.skipPermissions
|
|
75
|
+
? ["--dangerously-skip-permissions"]
|
|
76
|
+
: ["--permission-mode", invocation.permissionMode]),
|
|
77
|
+
...(invocation.model === null ? [] : ["--model", invocation.model]),
|
|
78
|
+
...(invocation.maxBudgetUsd === undefined ? [] : ["--max-budget-usd", String(invocation.maxBudgetUsd)]),
|
|
79
|
+
];
|
|
80
|
+
/**
|
|
81
|
+
* Reviewer isolation (fail-closed for the phase that must never mutate):
|
|
82
|
+
* the SAME flag family `subscription-chat.ts` already uses for its own
|
|
83
|
+
* sealed-scratch turn — no tool but reading the sealed files, no prompt
|
|
84
|
+
* that can stall a headless run waiting on an
|
|
85
|
+
* approval nobody can answer, and no MCP server (global OR project) loaded
|
|
86
|
+
* at all. A reviewer that goes looking for tools beyond its scratch
|
|
87
|
+
* directory is exactly the leak this closes: `--permission-mode plan`
|
|
88
|
+
* alone governs EDITS, not an MCP server's own tools or an interactive
|
|
89
|
+
* prompt, and a headless `-p` turn that stalls on either just sits idle
|
|
90
|
+
* until the timeout kills it, having spent the money on nothing.
|
|
91
|
+
*/
|
|
92
|
+
const CLAUDE_REVIEW_ISOLATION_ARGV = [
|
|
93
|
+
"--restricted",
|
|
94
|
+
"--safe-mode",
|
|
95
|
+
"--tools",
|
|
96
|
+
"Read",
|
|
97
|
+
"--permission-prompts",
|
|
98
|
+
"none",
|
|
99
|
+
"--strict-mcp-config",
|
|
100
|
+
"--mcp-config",
|
|
101
|
+
'{"mcpServers":{}}',
|
|
102
|
+
];
|
|
103
|
+
/**
|
|
104
|
+
* Run 1467's fix: Opus's reply to a review turn came back as JSON that
|
|
105
|
+
* `parseReview` refused outright — prose around the object, a stray code
|
|
106
|
+
* fence, a shape close but not exact. `--json-schema` is claude's own
|
|
107
|
+
* structured-output enforcement, so the CLI (not this codebase) is what
|
|
108
|
+
* makes the model's final message parse as JSON shaped like a review — it
|
|
109
|
+
* is a formatting floor, never a validator: an id absent from the signed
|
|
110
|
+
* rubric, a duplicate id, or a judgement word outside the three legal ones
|
|
111
|
+
* still only `parseReview` can catch, because only `parseReview` is handed
|
|
112
|
+
* the run's actual signed criteria. Claude-only and review-phase-only: no
|
|
113
|
+
* other provider has this flag, and no other phase asks for structured
|
|
114
|
+
* output.
|
|
115
|
+
*
|
|
116
|
+
* Run 1638's fix: ONE flat object, never a top-level union. The evidence
|
|
117
|
+
* read channel (`readEvidence`, review-evidence.ts) shares this reply
|
|
118
|
+
* schema and session, and the first draft expressed "a review OR a read
|
|
119
|
+
* request" as a root `anyOf`. The API refuses that before any model turn
|
|
120
|
+
* (`tools.N.custom.input_schema.type: Field required`), and a root
|
|
121
|
+
* `type: "object"` beside the `anyOf` is refused the same way — top-level
|
|
122
|
+
* unions are unsupported for structured output. So the two reply shapes
|
|
123
|
+
* are merged into one property set with only `version` required, and
|
|
124
|
+
* the machine's own parsers do the discrimination the schema cannot:
|
|
125
|
+
* `isEvidenceOnlyReply`/`evidenceRequest` accept exactly
|
|
126
|
+
* `{version, readEvidence}` and nothing else; `parseReview` refuses any
|
|
127
|
+
* reply carrying `readEvidence` (a mixed reply is neither), and still
|
|
128
|
+
* refuses a review missing its comments or any signed criterion. Nothing
|
|
129
|
+
* a looser floor lets through lands anywhere but a typed refusal.
|
|
130
|
+
*/
|
|
131
|
+
const CLAUDE_REVIEW_JSON_SCHEMA = {
|
|
132
|
+
type: "object",
|
|
133
|
+
properties: {
|
|
134
|
+
// Optional advice is not a review gate. project-learning validates its
|
|
135
|
+
// shape, UTF-8 byte limits, secrets and provenance after core ingestion,
|
|
136
|
+
// recording invalid advice without changing the review. Duplicating that
|
|
137
|
+
// contract here made a 126-character observation kill a valid review.
|
|
138
|
+
learningAssessment: {},
|
|
139
|
+
learning: {},
|
|
140
|
+
version: { type: "integer", enum: [1] },
|
|
141
|
+
comments: {
|
|
142
|
+
type: "array",
|
|
143
|
+
maxItems: REVIEW_OUTPUT_LIMITS.comments,
|
|
144
|
+
items: {
|
|
145
|
+
type: "object",
|
|
146
|
+
properties: {
|
|
147
|
+
path: { type: "string" },
|
|
148
|
+
line: { type: ["integer", "null"], minimum: 1 },
|
|
149
|
+
// Text limits belong to parseReview and its bounded same-session
|
|
150
|
+
// correction, not a second provider retry loop counting code points.
|
|
151
|
+
note: { type: "string" },
|
|
152
|
+
severity: { type: "string", enum: ["note", "question", "problem"] },
|
|
153
|
+
},
|
|
154
|
+
required: ["path", "note"],
|
|
155
|
+
additionalProperties: false,
|
|
156
|
+
},
|
|
157
|
+
},
|
|
158
|
+
criteria: {
|
|
159
|
+
type: "array",
|
|
160
|
+
maxItems: REVIEW_OUTPUT_LIMITS.criteria,
|
|
161
|
+
items: {
|
|
162
|
+
type: "object",
|
|
163
|
+
properties: {
|
|
164
|
+
id: { type: "string" },
|
|
165
|
+
judgement: { type: "string", enum: ["upholds", "contradicts", "cannot-tell"] },
|
|
166
|
+
note: { type: "string" },
|
|
167
|
+
},
|
|
168
|
+
required: ["id", "judgement", "note"],
|
|
169
|
+
additionalProperties: false,
|
|
170
|
+
},
|
|
171
|
+
},
|
|
172
|
+
// The read request rides the same structured reply channel and
|
|
173
|
+
// session. The machine validates the exact allowlist/hash/range
|
|
174
|
+
// before supplying bytes; this only shapes the request.
|
|
175
|
+
readEvidence: {
|
|
176
|
+
type: "object", properties: {
|
|
177
|
+
file: { type: "string", minLength: 1, maxLength: 100 },
|
|
178
|
+
sha256: { type: "string", pattern: "^[0-9a-f]{64}$" },
|
|
179
|
+
offset: { type: "integer", minimum: 0 },
|
|
180
|
+
length: { type: "integer", minimum: 1, maximum: 65536 },
|
|
181
|
+
}, required: ["file", "sha256", "offset", "length"], additionalProperties: false,
|
|
182
|
+
},
|
|
183
|
+
},
|
|
184
|
+
required: ["version"],
|
|
185
|
+
additionalProperties: false,
|
|
186
|
+
};
|
|
187
|
+
const claudeArgv = (invocation) => [
|
|
188
|
+
"-p",
|
|
189
|
+
invocation.brief,
|
|
190
|
+
...(invocation.resumeSession === null ? [] : ["--resume", invocation.resumeSession]),
|
|
191
|
+
// stream-json (arc 1): the terminal result event IS the old buffered
|
|
192
|
+
// envelope, now arriving as one line of many — the streaming transport
|
|
193
|
+
// retains it structurally instead of buffering the whole session into
|
|
194
|
+
// an 8 MiB kill. --verbose is required by the harness for stream-json
|
|
195
|
+
// with -p and changes nothing else.
|
|
196
|
+
"--output-format",
|
|
197
|
+
"stream-json",
|
|
198
|
+
"--verbose",
|
|
199
|
+
"--max-turns",
|
|
200
|
+
String(invocation.maxTurns),
|
|
201
|
+
...(invocation.phase === "review"
|
|
202
|
+
? ["--permission-mode", invocation.permissionMode === "bypassPermissions" ? "plan" : invocation.permissionMode]
|
|
203
|
+
: invocation.skipPermissions
|
|
204
|
+
? ["--dangerously-skip-permissions"]
|
|
205
|
+
: ["--permission-mode", invocation.permissionMode]),
|
|
206
|
+
...(invocation.model === null ? [] : ["--model", invocation.model]),
|
|
207
|
+
...(invocation.maxBudgetUsd === undefined ? [] : ["--max-budget-usd", String(invocation.maxBudgetUsd)]),
|
|
208
|
+
...(invocation.phase === "review"
|
|
209
|
+
? [...CLAUDE_REVIEW_ISOLATION_ARGV, "--json-schema", JSON.stringify(CLAUDE_REVIEW_JSON_SCHEMA)]
|
|
210
|
+
: [...(invocation.toolArgv ?? []), ...(invocation.fence !== undefined && invocation.fence.length > 0 ? ["--settings", claudeFenceSettings(invocation.fence)] : [])]),
|
|
211
|
+
];
|
|
212
|
+
/**
|
|
213
|
+
* The turn's own words, preferring the schema-validated structured field
|
|
214
|
+
* (present only under `--json-schema`) over the plain result string — the
|
|
215
|
+
* same preference `subscription-chat.ts`'s `claudeOutput` already applies
|
|
216
|
+
* for its own `--json-schema` turn. Re-serialized (never the CLI's raw
|
|
217
|
+
* text) so downstream parsing (`parseReview`) sees canonical JSON either
|
|
218
|
+
* way.
|
|
219
|
+
*/
|
|
220
|
+
function claudeFinalMessage(result) {
|
|
221
|
+
if (result === null)
|
|
222
|
+
return null;
|
|
223
|
+
if (result.structured_output !== undefined && result.structured_output !== null) {
|
|
224
|
+
return JSON.stringify(result.structured_output);
|
|
225
|
+
}
|
|
226
|
+
return typeof result.result === "string" ? result.result : null;
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* The primary-result allowlist (arc 1 finding 10): only an absent origin or
|
|
230
|
+
* an explicit human origin can be the main query's accounting envelope.
|
|
231
|
+
* Every other kind — task-notification, channel, peer, coordinator, and
|
|
232
|
+
* anything the SDK grows later — fails closed as a non-primary result.
|
|
233
|
+
*/
|
|
234
|
+
function claudePrimaryOrigin(event) {
|
|
235
|
+
if (event.origin === undefined || event.origin === null)
|
|
236
|
+
return true;
|
|
237
|
+
return typeof event.origin === "object" && String(event.origin["kind"] ?? "") === "human";
|
|
238
|
+
}
|
|
239
|
+
function claudeEnvelopeOf(result, sessionFromInit, initObserved, initSessionConflict = false) {
|
|
240
|
+
const input = result?.usage?.input_tokens;
|
|
241
|
+
const output = result?.usage?.output_tokens;
|
|
242
|
+
const cost = result?.total_cost_usd;
|
|
243
|
+
const sessionFromResult = sessionIdOf(result?.session_id);
|
|
244
|
+
const resultSessionConflict = sessionFromInit !== null && sessionFromResult !== null && sessionFromInit !== sessionFromResult;
|
|
245
|
+
const sessionConflict = initSessionConflict || resultSessionConflict;
|
|
246
|
+
return {
|
|
247
|
+
// Never choose one side of a contradictory identity. A repair must not
|
|
248
|
+
// resume either session until the gateway has refused this turn.
|
|
249
|
+
sessionId: sessionConflict ? null : sessionFromResult ?? sessionFromInit,
|
|
250
|
+
protocolError: initSessionConflict
|
|
251
|
+
? "Claude emitted conflicting system/init session ids"
|
|
252
|
+
: resultSessionConflict
|
|
253
|
+
? "the Claude init event and terminal result announced different session ids"
|
|
254
|
+
: null,
|
|
255
|
+
finalMessage: claudeFinalMessage(result),
|
|
256
|
+
ending: result === null ? null : {
|
|
257
|
+
subtype: typeof result.subtype === "string" ? result.subtype : null,
|
|
258
|
+
turns: typeof result.num_turns === "number" && result.num_turns >= 0 ? result.num_turns : null,
|
|
259
|
+
},
|
|
260
|
+
tokensIn: typeof input === "number" && input >= 0 ? input : null,
|
|
261
|
+
tokensOut: typeof output === "number" && output >= 0 ? output : null,
|
|
262
|
+
costUsd: typeof cost === "number" && cost >= 0 ? cost : null,
|
|
263
|
+
usageRaw: result?.usage === undefined ? null : JSON.stringify(result.usage).slice(0, USAGE_JSON_CAP),
|
|
264
|
+
initObserved,
|
|
265
|
+
// Structural, never prose (finding 15): consumed means the primary
|
|
266
|
+
// result says SUCCESS. An error result keeps its text as diagnostics
|
|
267
|
+
// while proving nothing about delivery; when the transport carries no
|
|
268
|
+
// signal (legacy buffered), null defers to the gateway's old rule.
|
|
269
|
+
promptConsumed: initObserved === null
|
|
270
|
+
? null
|
|
271
|
+
: result !== null && result.is_error !== true && String(result.subtype ?? "") === "success",
|
|
272
|
+
diagnostic: null,
|
|
273
|
+
// Claude's usage-limit signal, when a fixture exists, is recognized
|
|
274
|
+
// from the error result's text at classification; the parser records a
|
|
275
|
+
// structural failure terminal when the result is a non-success error.
|
|
276
|
+
structuralTerminal: result !== null &&
|
|
277
|
+
(result.is_error === true ||
|
|
278
|
+
(typeof result.subtype === "string" && result.subtype !== "success") ||
|
|
279
|
+
(initObserved !== null && typeof result.subtype !== "string"))
|
|
280
|
+
? { failed: true, text: typeof result.result === "string" ? result.result.slice(0, 2048) : null, code: typeof result.subtype === "string" ? result.subtype.slice(0, 128) : null }
|
|
281
|
+
: null,
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Reads the streaming runner's retained lines: a bounded set of
|
|
286
|
+
* `system`/`init` identity witnesses and one primary `result`, re-proved here
|
|
287
|
+
* (the parser trusts no transport to have selected correctly). A single
|
|
288
|
+
* object without a `type` field is the
|
|
289
|
+
* legacy buffered envelope — kept for recorded fixtures, carrying no init
|
|
290
|
+
* signal, exactly as before the transport switch.
|
|
291
|
+
*/
|
|
292
|
+
function claudeParse(stdout) {
|
|
293
|
+
let initSeen = false;
|
|
294
|
+
let sessionFromInit = null;
|
|
295
|
+
let initSessionConflict = false;
|
|
296
|
+
let primary = null;
|
|
297
|
+
let transportFailure = null;
|
|
298
|
+
for (const line of stdout.split("\n")) {
|
|
299
|
+
if (line.trim() === "")
|
|
300
|
+
continue;
|
|
301
|
+
let event;
|
|
302
|
+
try {
|
|
303
|
+
event = JSON.parse(line);
|
|
304
|
+
}
|
|
305
|
+
catch {
|
|
306
|
+
continue;
|
|
307
|
+
}
|
|
308
|
+
if (event === null || typeof event !== "object")
|
|
309
|
+
continue;
|
|
310
|
+
const type = event["type"];
|
|
311
|
+
if (type === undefined) {
|
|
312
|
+
// Legacy buffered envelope: one JSON object, no event framing.
|
|
313
|
+
return claudeEnvelopeOf(event, null, null);
|
|
314
|
+
}
|
|
315
|
+
if (String(type) === "system" && String(event["subtype"] ?? "") === "init") {
|
|
316
|
+
initSeen = true;
|
|
317
|
+
const id = sessionIdOf(event["session_id"]);
|
|
318
|
+
if (id !== null) {
|
|
319
|
+
if (sessionFromInit === null)
|
|
320
|
+
sessionFromInit = id;
|
|
321
|
+
else if (id !== sessionFromInit)
|
|
322
|
+
initSessionConflict = true;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
else if (String(type) === "result") {
|
|
326
|
+
const result = event;
|
|
327
|
+
if (result.is_error === true &&
|
|
328
|
+
result.subtype === "standing-orders-stream-event-overflow" &&
|
|
329
|
+
transportFailure === null) {
|
|
330
|
+
// The transport may append this bounded witness after an otherwise
|
|
331
|
+
// valid result when a later oversized init could conceal a second
|
|
332
|
+
// session identity. It is an independent protocol failure, not a
|
|
333
|
+
// replacement for the primary result's usage/accounting envelope.
|
|
334
|
+
transportFailure = result;
|
|
335
|
+
}
|
|
336
|
+
if (primary === null && claudePrimaryOrigin(result))
|
|
337
|
+
primary = result;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
const envelope = claudeEnvelopeOf(primary, sessionFromInit, initSeen, initSessionConflict);
|
|
341
|
+
if (transportFailure === null)
|
|
342
|
+
return envelope;
|
|
343
|
+
return {
|
|
344
|
+
...envelope,
|
|
345
|
+
structuralTerminal: {
|
|
346
|
+
failed: true,
|
|
347
|
+
text: typeof transportFailure.result === "string" ? transportFailure.result.slice(0, 2048) : null,
|
|
348
|
+
code: "standing-orders-stream-event-overflow",
|
|
349
|
+
},
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
/**
|
|
353
|
+
* The sandbox on a RESUME rides as a config override (`-c sandbox_mode=…`),
|
|
354
|
+
* never as `--sandbox`: `codex exec resume` (0.145.0, probed 2026-09-11)
|
|
355
|
+
* has no `--sandbox` flag and exits 2 before initializing when handed one
|
|
356
|
+
* — every structured correction and repair-by-resume was an immediately
|
|
357
|
+
* doomed turn that never echoed its thread. The override names the same
|
|
358
|
+
* exact mode the fresh turn was sealed with; the bypass flag and every
|
|
359
|
+
* `-c` override are accepted by both subcommands.
|
|
360
|
+
*/
|
|
361
|
+
const codexSandboxArgv = (mode, resuming) => resuming ? ["-c", `sandbox_mode="${mode}"`] : ["--sandbox", mode];
|
|
362
|
+
/**
|
|
363
|
+
* Codex's own fail-closed equivalent of `CLAUDE_REVIEW_ISOLATION_ARGV`:
|
|
364
|
+
* read-only sandbox rather than the ordinary `workspace-write` (a reviewer
|
|
365
|
+
* dispatched to codex was, until this, handed a write-capable sandbox —
|
|
366
|
+
* the same class of leak the claude side had), approvals that REFUSE
|
|
367
|
+
* rather than wait on a prompt a headless turn can never answer, and the
|
|
368
|
+
* user's own `~/.codex/config.toml` — which can name arbitrary MCP
|
|
369
|
+
* servers, exactly the surface `--strict-mcp-config` closes for claude —
|
|
370
|
+
* ignored outright.
|
|
371
|
+
*/
|
|
372
|
+
const CODEX_REVIEW_ISOLATION_ARGV = (resuming) => [
|
|
373
|
+
"--ignore-user-config",
|
|
374
|
+
...codexSandboxArgv("read-only", resuming),
|
|
375
|
+
"-c",
|
|
376
|
+
'approval_policy="never"',
|
|
377
|
+
"-c",
|
|
378
|
+
'web_search="disabled"',
|
|
379
|
+
"-c",
|
|
380
|
+
"features.shell_tool=false",
|
|
381
|
+
"-c",
|
|
382
|
+
"features.unified_exec=false",
|
|
383
|
+
"-c",
|
|
384
|
+
"features.multi_agent=false",
|
|
385
|
+
"-c",
|
|
386
|
+
"apps._default.enabled=false",
|
|
387
|
+
];
|
|
388
|
+
/**
|
|
389
|
+
* Codex argv. The brief is the positional prompt; resume is a subcommand.
|
|
390
|
+
* Auto uses `workspace-write` because the protocol REQUIRES workspace
|
|
391
|
+
* writes (the mailbox, the handoff). Full access uses Codex's one explicit
|
|
392
|
+
* combined bypass flag; that exact choice came from the sealed profile.
|
|
393
|
+
* Never `--ephemeral`: repair resumes.
|
|
394
|
+
*/
|
|
395
|
+
const codexArgv = (extra) => (invocation) => {
|
|
396
|
+
const resuming = invocation.resumeSession !== null;
|
|
397
|
+
return [
|
|
398
|
+
"exec",
|
|
399
|
+
...(invocation.resumeSession === null ? [] : ["resume", invocation.resumeSession]),
|
|
400
|
+
"--json",
|
|
401
|
+
"--skip-git-repo-check",
|
|
402
|
+
...(invocation.phase === "review"
|
|
403
|
+
? CODEX_REVIEW_ISOLATION_ARGV(resuming)
|
|
404
|
+
// The agent fence: Codex's own sandbox with Toolroll's secrets
|
|
405
|
+
// denied. Full access becomes that sandbox widened to write anywhere
|
|
406
|
+
// with network, instead of no sandbox (which fenced nothing).
|
|
407
|
+
: invocation.fence !== undefined && invocation.fence.length > 0
|
|
408
|
+
? codexFenceArgv(invocation.fence, invocation.skipPermissions)
|
|
409
|
+
: invocation.skipPermissions
|
|
410
|
+
? ["--dangerously-bypass-approvals-and-sandbox"]
|
|
411
|
+
: codexSandboxArgv("workspace-write", resuming)),
|
|
412
|
+
...(invocation.model === null ? [] : ["-m", invocation.model]),
|
|
413
|
+
...extra,
|
|
414
|
+
// After `extra`: the tools' shell_environment_policy must be the last word (it keeps OpenRouter's key out too).
|
|
415
|
+
...(invocation.phase === "review" ? [] : invocation.toolArgv ?? []),
|
|
416
|
+
...(invocation.phase === "review" ? (invocation.reviewImages ?? []).flatMap(path => ["--image", path]) : []),
|
|
417
|
+
// Review includes the sealed text itself: no shell/read tool exists in
|
|
418
|
+
// this posture. stdin avoids both per-argument and Windows argv limits.
|
|
419
|
+
invocation.phase === "review" ? "-" : invocation.brief,
|
|
420
|
+
];
|
|
421
|
+
};
|
|
422
|
+
/**
|
|
423
|
+
* The retained JSONL lines (the streaming transport keeps only these):
|
|
424
|
+
* thread.started (session), turn.completed (usage), the last agent_message.
|
|
425
|
+
* Unknown events were dropped at the transport; unknown here are ignored
|
|
426
|
+
* too. Missing terminal usage stays NULL — unmeasured, and said so.
|
|
427
|
+
*/
|
|
428
|
+
function codexParse(stdout) {
|
|
429
|
+
let sessionId = null;
|
|
430
|
+
let sessionConflict = false;
|
|
431
|
+
let finalMessage = null;
|
|
432
|
+
let tokensIn = null;
|
|
433
|
+
let tokensOut = null;
|
|
434
|
+
let usageRaw = null;
|
|
435
|
+
let initObserved = false;
|
|
436
|
+
let structuralTerminal = null;
|
|
437
|
+
for (const line of stdout.split("\n")) {
|
|
438
|
+
if (line.trim() === "")
|
|
439
|
+
continue;
|
|
440
|
+
let event;
|
|
441
|
+
try {
|
|
442
|
+
event = JSON.parse(line);
|
|
443
|
+
}
|
|
444
|
+
catch {
|
|
445
|
+
continue;
|
|
446
|
+
}
|
|
447
|
+
const type = String(event["type"] ?? "");
|
|
448
|
+
if (type === "thread.started") {
|
|
449
|
+
// The init signal, id or no id: the harness came up. A malformed
|
|
450
|
+
// thread_id loses the session, not the fact of initialization.
|
|
451
|
+
initObserved = true;
|
|
452
|
+
const announced = sessionIdOf(event["thread_id"]);
|
|
453
|
+
if (announced !== null) {
|
|
454
|
+
if (sessionId === null)
|
|
455
|
+
sessionId = announced;
|
|
456
|
+
else if (announced !== sessionId)
|
|
457
|
+
sessionConflict = true;
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
else if (type === "turn.completed") {
|
|
461
|
+
const usage = event["usage"];
|
|
462
|
+
const input = usage?.["input_tokens"];
|
|
463
|
+
const output = usage?.["output_tokens"];
|
|
464
|
+
// cached_input_tokens deliberately NOT summed into input — it rides
|
|
465
|
+
// only in the raw record (Codex provider review, Q2).
|
|
466
|
+
if (typeof input === "number" && input >= 0)
|
|
467
|
+
tokensIn = input;
|
|
468
|
+
if (typeof output === "number" && output >= 0)
|
|
469
|
+
tokensOut = output;
|
|
470
|
+
if (usage !== undefined)
|
|
471
|
+
usageRaw = JSON.stringify(usage).slice(0, USAGE_JSON_CAP);
|
|
472
|
+
}
|
|
473
|
+
else if (type === "item.completed") {
|
|
474
|
+
const item = event["item"];
|
|
475
|
+
if (item !== undefined && String(item["type"] ?? "") === "agent_message") {
|
|
476
|
+
const text = item["text"];
|
|
477
|
+
if (typeof text === "string")
|
|
478
|
+
finalMessage = text;
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
else if (type === "turn.failed") {
|
|
482
|
+
// RETAIN the structural failure terminal (Codex fallback verify,
|
|
483
|
+
// finding 1): a turn.failed carries the usage-limit signal, and
|
|
484
|
+
// dropping it here is where the evidence used to die. The error
|
|
485
|
+
// object's message + typed code ride into the taxonomy; its mere
|
|
486
|
+
// presence blocks success ingestion downstream.
|
|
487
|
+
const error = event["error"];
|
|
488
|
+
const message = error?.["message"];
|
|
489
|
+
const code = error?.["type"] ?? error?.["code"];
|
|
490
|
+
structuralTerminal = {
|
|
491
|
+
failed: true,
|
|
492
|
+
text: typeof message === "string" ? message.slice(0, 2048) : null,
|
|
493
|
+
code: typeof code === "string" ? code.slice(0, 128) : null,
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
// Codex reports no dollars. NULL is the honest cost — unmeasured — and
|
|
498
|
+
// every surface downstream already says so instead of summing a lie.
|
|
499
|
+
// promptConsumed stays null: codex carries no structural consumption
|
|
500
|
+
// signal, and the gateway keeps its historical rule for it.
|
|
501
|
+
return {
|
|
502
|
+
// As with Claude, never let either side of a contradictory identity
|
|
503
|
+
// become repair state. The gateway records the spend, then refuses it.
|
|
504
|
+
sessionId: sessionConflict ? null : sessionId,
|
|
505
|
+
protocolError: sessionConflict
|
|
506
|
+
? "Codex emitted conflicting thread.started session ids"
|
|
507
|
+
: null,
|
|
508
|
+
finalMessage,
|
|
509
|
+
tokensIn,
|
|
510
|
+
tokensOut,
|
|
511
|
+
costUsd: null,
|
|
512
|
+
usageRaw,
|
|
513
|
+
initObserved,
|
|
514
|
+
promptConsumed: null,
|
|
515
|
+
diagnostic: null,
|
|
516
|
+
structuralTerminal,
|
|
517
|
+
};
|
|
518
|
+
}
|
|
519
|
+
/** Codex interval caps: inactivity for current profiles, wall clock for
|
|
520
|
+
* repair and legacy profiles. */
|
|
521
|
+
const CODEX_TIMEOUT_CAP_MS = {
|
|
522
|
+
build: 20 * 60_000,
|
|
523
|
+
plan: 20 * 60_000,
|
|
524
|
+
repair: 5 * 60_000,
|
|
525
|
+
review: 20 * 60_000,
|
|
526
|
+
};
|
|
527
|
+
/**
|
|
528
|
+
* Gemini argv (Phase 3, v0.57.0 audit). The brief is `-p` (headless);
|
|
529
|
+
* `--approval-mode` is the ONE sealed autonomy dial — skipPermissions
|
|
530
|
+
* maps to yolo exactly where claude maps it to bypass, anything else is
|
|
531
|
+
* auto_edit (fail-closed: never `default`, whose headless behavior is
|
|
532
|
+
* tool failure, and never `plan`, which cannot write the mailbox).
|
|
533
|
+
* Session identity is minted by the plane (`--session-id`) or resumed
|
|
534
|
+
* (`--resume`) — never both. No turn bound and no dollar cap exist to
|
|
535
|
+
* render (the audit and money capabilities say so instead).
|
|
536
|
+
*/
|
|
537
|
+
const geminiArgv = (invocation) => [
|
|
538
|
+
"-p",
|
|
539
|
+
invocation.brief,
|
|
540
|
+
// S2 (live spike, v0.57.0): headless REFUSES untrusted directories, and
|
|
541
|
+
// a freshly leased worktree is always untrusted. --skip-trust grants
|
|
542
|
+
// this run's trust — and honestly (Codex gemini verify, finding 1): the
|
|
543
|
+
// CLI implements the flag by setting GEMINI_CLI_TRUST_WORKSPACE in its
|
|
544
|
+
// OWN process, which descendants inherit, and trusted mode is what lets
|
|
545
|
+
// gemini load a worktree's .gemini/ config (hooks, MCP). We accept that
|
|
546
|
+
// because gemini is dispatched ONLY on EXPLICIT operator selection (a
|
|
547
|
+
// phase-config row, a task pin, or a flag — never a default or fallback;
|
|
548
|
+
// the resolver's default is always claude), so trusting the workspace is
|
|
549
|
+
// the operator's own deliberate choice, not an ambient grant. A per-run
|
|
550
|
+
// config-isolation boundary (isolated GEMINI_DIR) is the tracked
|
|
551
|
+
// follow-up if gemini ever becomes a default. Until that lands, gemini
|
|
552
|
+
// has no fail-closed equivalent of the claude/codex review isolation
|
|
553
|
+
// above — `resolvePhaseAgent` (agentconfig.ts) refuses gemini for the
|
|
554
|
+
// review phase outright rather than let a documented, untracked leak
|
|
555
|
+
// pose as a confined reviewer.
|
|
556
|
+
"--skip-trust",
|
|
557
|
+
"--output-format",
|
|
558
|
+
"stream-json",
|
|
559
|
+
"--approval-mode",
|
|
560
|
+
invocation.skipPermissions ? "yolo" : "auto_edit",
|
|
561
|
+
...(invocation.resumeSession !== null
|
|
562
|
+
? ["--resume", invocation.resumeSession]
|
|
563
|
+
: invocation.startSessionId !== undefined
|
|
564
|
+
? ["--session-id", invocation.startSessionId]
|
|
565
|
+
: []),
|
|
566
|
+
...(invocation.model === null ? [] : ["-m", invocation.model]),
|
|
567
|
+
...(invocation.toolArgv ?? []),
|
|
568
|
+
];
|
|
569
|
+
/** Gemini interval caps: inactivity for current profiles, wall clock for
|
|
570
|
+
* repair and legacy profiles. */
|
|
571
|
+
const GEMINI_TIMEOUT_CAP_MS = {
|
|
572
|
+
build: 20 * 60_000,
|
|
573
|
+
plan: 20 * 60_000,
|
|
574
|
+
repair: 5 * 60_000,
|
|
575
|
+
review: 20 * 60_000,
|
|
576
|
+
};
|
|
577
|
+
const DIAGNOSTIC_CAP = 2 * 1024;
|
|
578
|
+
/**
|
|
579
|
+
* Reads the gemini retention runner's synthetic stdout (Phase 3 D2/A7):
|
|
580
|
+
* a bounded set of `init` identity witnesses (result events carry no id, so
|
|
581
|
+
* identity is init-or-nothing), one `synthetic_message` (the runner-assembled
|
|
582
|
+
* assistant text; a type the real CLI cannot emit, so fixtures and transport
|
|
583
|
+
* share an unambiguous contract), the LAST `result` (tokens + structural
|
|
584
|
+
* status), and the first error line (diagnostics only). No legacy branch:
|
|
585
|
+
* the attestation floor is the only dialect this parser has ever had to honor.
|
|
586
|
+
*/
|
|
587
|
+
function geminiParse(stdout) {
|
|
588
|
+
let initSeen = false;
|
|
589
|
+
let sessionId = null;
|
|
590
|
+
let sessionConflict = false;
|
|
591
|
+
let finalMessage = null;
|
|
592
|
+
let tokensIn = null;
|
|
593
|
+
let tokensOut = null;
|
|
594
|
+
let usageRaw = null;
|
|
595
|
+
let resultStatus = null;
|
|
596
|
+
let diagnostic = null;
|
|
597
|
+
for (const line of stdout.split("\n")) {
|
|
598
|
+
if (line.trim() === "")
|
|
599
|
+
continue;
|
|
600
|
+
let event;
|
|
601
|
+
try {
|
|
602
|
+
event = JSON.parse(line);
|
|
603
|
+
}
|
|
604
|
+
catch {
|
|
605
|
+
continue;
|
|
606
|
+
}
|
|
607
|
+
if (event === null || typeof event !== "object")
|
|
608
|
+
continue;
|
|
609
|
+
const type = String(event["type"] ?? "");
|
|
610
|
+
if (type === "init") {
|
|
611
|
+
initSeen = true;
|
|
612
|
+
const announced = sessionIdOf(event["session_id"]);
|
|
613
|
+
if (announced !== null) {
|
|
614
|
+
if (sessionId === null)
|
|
615
|
+
sessionId = announced;
|
|
616
|
+
else if (announced !== sessionId)
|
|
617
|
+
sessionConflict = true;
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
else if (type === "synthetic_message") {
|
|
621
|
+
const content = event["content"];
|
|
622
|
+
if (typeof content === "string")
|
|
623
|
+
finalMessage = content;
|
|
624
|
+
}
|
|
625
|
+
else if (type === "result") {
|
|
626
|
+
resultStatus = String(event["status"] ?? "");
|
|
627
|
+
const stats = event["stats"];
|
|
628
|
+
const input = stats?.["input_tokens"];
|
|
629
|
+
const output = stats?.["output_tokens"];
|
|
630
|
+
if (typeof input === "number" && input >= 0)
|
|
631
|
+
tokensIn = input;
|
|
632
|
+
if (typeof output === "number" && output >= 0)
|
|
633
|
+
tokensOut = output;
|
|
634
|
+
if (stats !== undefined)
|
|
635
|
+
usageRaw = JSON.stringify(stats).slice(0, USAGE_JSON_CAP);
|
|
636
|
+
const resultError = event["error"];
|
|
637
|
+
const message = resultError?.["message"];
|
|
638
|
+
if (diagnostic === null && typeof message === "string" && message !== "") {
|
|
639
|
+
diagnostic = safeDiagnostic(message);
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
else if (type === "error") {
|
|
643
|
+
const message = event["message"];
|
|
644
|
+
if (diagnostic === null && String(event["severity"] ?? "") === "error" && typeof message === "string" && message !== "") {
|
|
645
|
+
diagnostic = safeDiagnostic(message);
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
return {
|
|
650
|
+
// A plane-minted id is useful only while the provider tells one
|
|
651
|
+
// consistent story about it. Never select either side of contradictory
|
|
652
|
+
// init events; the gateway records usage and refuses the turn.
|
|
653
|
+
sessionId: sessionConflict ? null : sessionId,
|
|
654
|
+
protocolError: sessionConflict ? "Gemini emitted conflicting init session ids" : null,
|
|
655
|
+
finalMessage,
|
|
656
|
+
tokensIn,
|
|
657
|
+
tokensOut,
|
|
658
|
+
// Gemini reports tokens, never dollars. NULL is the honest cost.
|
|
659
|
+
costUsd: null,
|
|
660
|
+
usageRaw,
|
|
661
|
+
initObserved: initSeen,
|
|
662
|
+
// Structural, never prose: consumed means the terminal result said
|
|
663
|
+
// SUCCESS. Missing or error results prove nothing about delivery —
|
|
664
|
+
// and for this provider the gateway's terminal contract REQUIRES the
|
|
665
|
+
// proof before exit 0 is believed (Phase 3 A4).
|
|
666
|
+
promptConsumed: resultStatus === "success",
|
|
667
|
+
diagnostic,
|
|
668
|
+
// A structural failure terminal ONLY when an ACTUAL non-success RESULT
|
|
669
|
+
// was seen (Codex foundation review, finding 2): a MISSING result
|
|
670
|
+
// (resultStatus null) is not evidence of anything — never fabricate a
|
|
671
|
+
// failure terminal from its absence (terminalContract already handles
|
|
672
|
+
// the exit-0-without-success case as protocol, not exhaustion).
|
|
673
|
+
structuralTerminal: resultStatus === null || resultStatus === "success"
|
|
674
|
+
? null
|
|
675
|
+
: { failed: true, text: diagnostic, code: String(resultStatus).slice(0, 128) },
|
|
676
|
+
};
|
|
677
|
+
}
|
|
678
|
+
/** Truncate to a UTF-8 byte budget without splitting a code point. */
|
|
679
|
+
function capUtf8(text, bytes) {
|
|
680
|
+
if (Buffer.byteLength(text, "utf8") <= bytes)
|
|
681
|
+
return text;
|
|
682
|
+
// The ellipsis lives INSIDE the byte budget, not on top of it.
|
|
683
|
+
const buffer = Buffer.from(text, "utf8").subarray(0, Math.max(0, bytes - 3));
|
|
684
|
+
return buffer.toString("utf8").replace(/�+$/, "") + "…";
|
|
685
|
+
}
|
|
686
|
+
/**
|
|
687
|
+
* The diagnostic discipline (Phase 3 B6/C6): the harness's own words are
|
|
688
|
+
* untrusted bytes headed for refusal screens and notifications — controls
|
|
689
|
+
* and line separators collapse to spaces (the fence's character class),
|
|
690
|
+
* and a line that trips the secret scanner is REPLACED, never quoted.
|
|
691
|
+
*/
|
|
692
|
+
export function safeDiagnostic(text) {
|
|
693
|
+
const normalized = text.replace(/[\u0000-\u001F\u007F-\u009F\u2028\u2029]+/g, " ").trim();
|
|
694
|
+
if (normalized === "")
|
|
695
|
+
return null;
|
|
696
|
+
if (scanForSecrets(normalized).length > 0)
|
|
697
|
+
return "the harness's error text was withheld — it matched a secret pattern";
|
|
698
|
+
return capUtf8(normalized, DIAGNOSTIC_CAP);
|
|
699
|
+
}
|
|
700
|
+
/** Every provider credential the plane knows about, by owner. A spawned
|
|
701
|
+
* provider gets its OWN keys and nobody else's: S4 (live spike) confirmed
|
|
702
|
+
* what inheritance makes true by construction — an agent's shell reads its
|
|
703
|
+
* process env, so a foreign key in that env is a foreign key disclosed. */
|
|
704
|
+
const CREDENTIAL_ENV = {
|
|
705
|
+
claude: ["ANTHROPIC_API_KEY", "ANTHROPIC_AUTH_TOKEN"],
|
|
706
|
+
codex: ["OPENAI_API_KEY", "CODEX_API_KEY"],
|
|
707
|
+
openrouter: [OPENROUTER_ENV_KEY],
|
|
708
|
+
gemini: ["GEMINI_API_KEY", "GOOGLE_API_KEY"],
|
|
709
|
+
};
|
|
710
|
+
const foreignCredentialEnv = (own) => Object.entries(CREDENTIAL_ENV)
|
|
711
|
+
.filter(([provider]) => provider !== own)
|
|
712
|
+
.flatMap(([, keys]) => [...keys]);
|
|
713
|
+
/** EVERY provider credential — what a version/help/which PROBE strips,
|
|
714
|
+
* since a feature check needs no key at all (Codex gemini verify,
|
|
715
|
+
* finding 2). Agent spawns keep their own; probes keep none. */
|
|
716
|
+
export const ALL_CREDENTIAL_ENV = Object.values(CREDENTIAL_ENV).flat();
|
|
717
|
+
const ADAPTERS = {
|
|
718
|
+
claude: {
|
|
719
|
+
binary: "claude",
|
|
720
|
+
argv: claudeArgv,
|
|
721
|
+
parse: claudeParse,
|
|
722
|
+
defaultRunner: runClaudeStreamJsonl,
|
|
723
|
+
extraOmitEnv: foreignCredentialEnv("claude"),
|
|
724
|
+
clampTimeout: (_phase, requested) => requested,
|
|
725
|
+
},
|
|
726
|
+
codex: {
|
|
727
|
+
binary: "codex",
|
|
728
|
+
argv: codexArgv([]),
|
|
729
|
+
stdin: invocation => invocation.phase === "review" ? invocation.brief : undefined,
|
|
730
|
+
parse: codexParse,
|
|
731
|
+
defaultRunner: runStreamJsonl,
|
|
732
|
+
extraOmitEnv: foreignCredentialEnv("codex"),
|
|
733
|
+
clampTimeout: (phase, requested) => Math.min(requested, CODEX_TIMEOUT_CAP_MS[phase]),
|
|
734
|
+
},
|
|
735
|
+
openrouter: {
|
|
736
|
+
binary: "codex",
|
|
737
|
+
stdin: invocation => invocation.phase === "review" ? invocation.brief : undefined,
|
|
738
|
+
argv: codexArgv([
|
|
739
|
+
"-c",
|
|
740
|
+
`model_provider=${toml(OPENROUTER_PROVIDER_KEY)}`,
|
|
741
|
+
"-c",
|
|
742
|
+
`model_providers.${OPENROUTER_PROVIDER_KEY}.name=${toml("OpenRouter")}`,
|
|
743
|
+
"-c",
|
|
744
|
+
`model_providers.${OPENROUTER_PROVIDER_KEY}.base_url=${toml(OPENROUTER_BASE_URL)}`,
|
|
745
|
+
"-c",
|
|
746
|
+
`model_providers.${OPENROUTER_PROVIDER_KEY}.env_key=${toml(OPENROUTER_ENV_KEY)}`,
|
|
747
|
+
// Asks Codex to keep the key out of the agent's shells. codex-cli
|
|
748
|
+
// 0.156 ignores shell_environment_policy, so the key IS readable from
|
|
749
|
+
// the agent's shell in practice; kept for Codex builds that honour it.
|
|
750
|
+
// What does hold is the agent fence: nothing else of Toolroll's
|
|
751
|
+
// (other keys, logins, tokens, the database) is reachable.
|
|
752
|
+
"-c",
|
|
753
|
+
`shell_environment_policy.exclude=[${toml(OPENROUTER_ENV_KEY)}]`,
|
|
754
|
+
]),
|
|
755
|
+
parse: codexParse,
|
|
756
|
+
defaultRunner: runStreamJsonl,
|
|
757
|
+
extraOmitEnv: foreignCredentialEnv("openrouter"),
|
|
758
|
+
clampTimeout: (phase, requested) => Math.min(requested, CODEX_TIMEOUT_CAP_MS[phase]),
|
|
759
|
+
},
|
|
760
|
+
gemini: {
|
|
761
|
+
binary: "gemini",
|
|
762
|
+
argv: geminiArgv,
|
|
763
|
+
parse: geminiParse,
|
|
764
|
+
defaultRunner: runGeminiStreamJsonl,
|
|
765
|
+
extraOmitEnv: foreignCredentialEnv("gemini"),
|
|
766
|
+
clampTimeout: (phase, requested) => Math.min(requested, GEMINI_TIMEOUT_CAP_MS[phase]),
|
|
767
|
+
},
|
|
768
|
+
};
|
|
769
|
+
/**
|
|
770
|
+
* The spawning surface. Imported by invoke.ts and NOWHERE else — the
|
|
771
|
+
* architecture test reads imports, not string literals, because provider
|
|
772
|
+
* ids and binary names are legitimately the same words elsewhere.
|
|
773
|
+
*/
|
|
774
|
+
export function adapterFor(provider) {
|
|
775
|
+
return ADAPTERS[provider];
|
|
776
|
+
}
|
|
777
|
+
const AUDITS = {
|
|
778
|
+
claude: {
|
|
779
|
+
transport: "streaming-jsonl",
|
|
780
|
+
resume: "native",
|
|
781
|
+
initSignal: "system-init",
|
|
782
|
+
sessionIdentity: "announced",
|
|
783
|
+
terminalContract: "none",
|
|
784
|
+
// Restricted mode is resume-safe and is enforced for sealed reviewer
|
|
785
|
+
// turns. `enforced` remains false here because this provider-wide audit
|
|
786
|
+
// also covers ordinary build turns, where the flag is not applied.
|
|
787
|
+
isolation: { flag: "--restricted", resumeSafe: true, enforced: false },
|
|
788
|
+
configSurface: [
|
|
789
|
+
"~/.claude/CLAUDE.md and settings (hooks, MCP servers, plugins)",
|
|
790
|
+
"repository CLAUDE.md / .claude directory",
|
|
791
|
+
],
|
|
792
|
+
},
|
|
793
|
+
codex: {
|
|
794
|
+
transport: "streaming-jsonl",
|
|
795
|
+
resume: "native",
|
|
796
|
+
initSignal: "thread.started",
|
|
797
|
+
sessionIdentity: "announced",
|
|
798
|
+
terminalContract: "none",
|
|
799
|
+
// --ephemeral exists and is deliberately not passed: repair resumes
|
|
800
|
+
// sessions, and ephemeral runs have none to resume.
|
|
801
|
+
isolation: { flag: "--ephemeral", resumeSafe: false, enforced: false },
|
|
802
|
+
configSurface: ["~/.codex/config.toml", "repository AGENTS.md"],
|
|
803
|
+
},
|
|
804
|
+
openrouter: {
|
|
805
|
+
transport: "streaming-jsonl",
|
|
806
|
+
resume: "native",
|
|
807
|
+
initSignal: "thread.started",
|
|
808
|
+
sessionIdentity: "announced",
|
|
809
|
+
terminalContract: "none",
|
|
810
|
+
isolation: { flag: "--ephemeral", resumeSafe: false, enforced: false },
|
|
811
|
+
// The constant -c overrides pin the model provider per invocation, so
|
|
812
|
+
// user config cannot reroute the spend — but the file still loads.
|
|
813
|
+
configSurface: ["~/.codex/config.toml (model_provider pinned per invocation)", "repository AGENTS.md"],
|
|
814
|
+
},
|
|
815
|
+
gemini: {
|
|
816
|
+
transport: "streaming-jsonl",
|
|
817
|
+
// S1 (live spike 2026-08-29, v0.57.0) PROVED headless persistence AND
|
|
818
|
+
// resume-by-uuid in the same cwd — the flip from "none" is the
|
|
819
|
+
// re-attestation the comment always demanded, now earned. Repair
|
|
820
|
+
// resumes the session instead of paying for a fresh one; geminiArgv
|
|
821
|
+
// must mint OR resume, never both (see the repair caller).
|
|
822
|
+
resume: "native",
|
|
823
|
+
initSignal: "init-event",
|
|
824
|
+
sessionIdentity: "minted",
|
|
825
|
+
terminalContract: "required",
|
|
826
|
+
isolation: { flag: "--sandbox", resumeSafe: null, enforced: false },
|
|
827
|
+
configSurface: [
|
|
828
|
+
"~/.gemini/settings.json (HOOKS — BeforeAgent/AfterTool commands run inside every invocation — plus MCP servers and model settings)",
|
|
829
|
+
"project .gemini/settings.json",
|
|
830
|
+
"GEMINI.md (global and repository)",
|
|
831
|
+
"extensions, skills, and policy files",
|
|
832
|
+
"GEMINI_API_KEY / GOOGLE_GENAI_USE_* environment (an API key in env is visible to the gemini agent's own shells — S4; every OTHER provider's spawn now sheds it, and cached login remains the tighter posture)",
|
|
833
|
+
"trusted-folder store (S2, live at 0.57.0: headless REFUSES untrusted directories; the plane grants --skip-trust per-invocation to its own leased worktrees)",
|
|
834
|
+
],
|
|
835
|
+
},
|
|
836
|
+
};
|
|
837
|
+
export const MONEY_CAPABILITIES = {
|
|
838
|
+
claude: {
|
|
839
|
+
incrementalUsage: true,
|
|
840
|
+
nativeDollarCapFlag: "--max-budget-usd",
|
|
841
|
+
usageSemantics: "per-invocation",
|
|
842
|
+
tournamentEligible: true,
|
|
843
|
+
whyIneligible: null,
|
|
844
|
+
},
|
|
845
|
+
codex: {
|
|
846
|
+
incrementalUsage: false,
|
|
847
|
+
nativeDollarCapFlag: null,
|
|
848
|
+
usageSemantics: "cumulative-session",
|
|
849
|
+
tournamentEligible: false,
|
|
850
|
+
whyIneligible: "codex reports billable usage only when a turn completes — no mid-run dollar cap exists to enforce",
|
|
851
|
+
},
|
|
852
|
+
openrouter: {
|
|
853
|
+
incrementalUsage: false,
|
|
854
|
+
nativeDollarCapFlag: null,
|
|
855
|
+
usageSemantics: "cumulative-session",
|
|
856
|
+
tournamentEligible: false,
|
|
857
|
+
whyIneligible: "openrouter rides the codex harness here and shares its turn-end-only usage reporting",
|
|
858
|
+
},
|
|
859
|
+
gemini: {
|
|
860
|
+
incrementalUsage: false,
|
|
861
|
+
nativeDollarCapFlag: null,
|
|
862
|
+
// Stats come from a per-process telemetry service: an invocation's
|
|
863
|
+
// numbers cover that invocation only (conformance fixture j).
|
|
864
|
+
usageSemantics: "per-invocation",
|
|
865
|
+
tournamentEligible: false,
|
|
866
|
+
whyIneligible: "gemini reports tokens, never dollars — no native cap exists to hold",
|
|
867
|
+
},
|
|
868
|
+
};
|
|
869
|
+
/**
|
|
870
|
+
* The fail-closed budget-flag probe (finding 24's amendment): resolve
|
|
871
|
+
* the EXACT executable that will spawn, read its version, and prove the
|
|
872
|
+
* flag exists in that binary's own help — presence is a feature check
|
|
873
|
+
* and nothing more; pricing and semantics stay pinned in pricing.ts.
|
|
874
|
+
*/
|
|
875
|
+
export async function probeBudgetCap(provider, runner) {
|
|
876
|
+
const flag = MONEY_CAPABILITIES[provider].nativeDollarCapFlag;
|
|
877
|
+
if (flag === null)
|
|
878
|
+
return { ok: false, problem: `${provider} has no native dollar cap` };
|
|
879
|
+
const binary = provider === "claude" ? "claude" : provider;
|
|
880
|
+
// A feature probe needs no credential (round 2, finding 2): strip them all.
|
|
881
|
+
const strip = { omitEnv: ALL_CREDENTIAL_ENV };
|
|
882
|
+
const where = await runner("which", [binary], { timeoutMs: 5_000, ...strip });
|
|
883
|
+
if (where.code !== 0)
|
|
884
|
+
return { ok: false, problem: `${binary} is not on PATH` };
|
|
885
|
+
const executable = where.stdout.trim().split("\n")[0] ?? "";
|
|
886
|
+
const version = await runner(executable, ["--version"], { timeoutMs: 10_000, ...strip });
|
|
887
|
+
if (version.code !== 0)
|
|
888
|
+
return { ok: false, problem: `${executable} did not answer --version` };
|
|
889
|
+
const help = await runner(executable, ["--help"], { timeoutMs: 10_000, ...strip });
|
|
890
|
+
if (help.code !== 0 || !help.stdout.includes(flag)) {
|
|
891
|
+
return { ok: false, problem: `${executable} does not advertise ${flag} — the dollar cap cannot be enforced` };
|
|
892
|
+
}
|
|
893
|
+
return { ok: true, executable, version: version.stdout.trim() };
|
|
894
|
+
}
|
|
895
|
+
export function auditOf(provider) {
|
|
896
|
+
return AUDITS[provider];
|
|
897
|
+
}
|
|
898
|
+
/**
|
|
899
|
+
* A spec a person or a config row proposed, validated to a complete pair.
|
|
900
|
+
* openrouter REQUIRES a model: there is no meaningful harness default
|
|
901
|
+
* across a 300-model catalog.
|
|
902
|
+
*/
|
|
903
|
+
export function validateSpec(spec) {
|
|
904
|
+
if (!isProviderId(spec.provider)) {
|
|
905
|
+
return { ok: false, problem: `unknown provider \`${String(spec.provider)}\` — one of ${PROVIDER_IDS.join(", ")}` };
|
|
906
|
+
}
|
|
907
|
+
if (!validModelId(spec.model)) {
|
|
908
|
+
return { ok: false, problem: "a model id is 1–128 characters of letters, digits, and . _ : / - (never leading with a dash)" };
|
|
909
|
+
}
|
|
910
|
+
if (spec.provider === "openrouter" && spec.model === null) {
|
|
911
|
+
return { ok: false, problem: "openrouter needs an explicit model — there is no default across its catalog" };
|
|
912
|
+
}
|
|
913
|
+
if (spec.provider === "gemini" && spec.model === null) {
|
|
914
|
+
return { ok: false, problem: "gemini needs an explicit model — the harness default drifts with its releases" };
|
|
915
|
+
}
|
|
916
|
+
return { ok: true };
|
|
917
|
+
}
|
|
918
|
+
/**
|
|
919
|
+
* Whether this provider reports dollar cost. The routine budget interacts:
|
|
920
|
+
* a ceiling against an unmeasured provider fails closed by design, so the
|
|
921
|
+
* approval surfaces refuse the combination outright.
|
|
922
|
+
*/
|
|
923
|
+
export function reportsCost(provider) {
|
|
924
|
+
return provider === "claude";
|
|
925
|
+
}
|
|
926
|
+
export function inspectionOf(provider) {
|
|
927
|
+
const adapter = ADAPTERS[provider];
|
|
928
|
+
return {
|
|
929
|
+
id: provider,
|
|
930
|
+
binary: adapter.binary,
|
|
931
|
+
identityProbe: provider === "codex" || provider === "openrouter" ? ["login", "status"] : null,
|
|
932
|
+
requiresEnv: provider === "openrouter" ? OPENROUTER_ENV_KEY : null,
|
|
933
|
+
measuresCost: reportsCost(provider),
|
|
934
|
+
};
|
|
935
|
+
}
|