approval-md 0.0.1 → 0.2.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 +176 -0
- package/NOTICE +5 -0
- package/README.md +940 -4
- package/SPEC.md +476 -34
- package/cli.js +29 -3
- package/dist/src/adapters/agentmail.d.ts +426 -0
- package/dist/src/adapters/agentmail.js +1200 -0
- package/dist/src/adapters/agentmail.js.map +1 -0
- package/dist/src/adapters/conformance.d.ts +149 -0
- package/dist/src/adapters/conformance.js +461 -0
- package/dist/src/adapters/conformance.js.map +1 -0
- package/dist/src/adapters/contract.d.ts +628 -0
- package/dist/src/adapters/contract.js +1035 -0
- package/dist/src/adapters/contract.js.map +1 -0
- package/dist/src/adapters/email.d.ts +324 -0
- package/dist/src/adapters/email.js +749 -0
- package/dist/src/adapters/email.js.map +1 -0
- package/dist/src/adapters/env-passphrase.d.ts +93 -0
- package/dist/src/adapters/env-passphrase.js +132 -0
- package/dist/src/adapters/env-passphrase.js.map +1 -0
- package/dist/src/adapters/public.d.ts +11 -0
- package/dist/src/adapters/public.js +11 -0
- package/dist/src/adapters/public.js.map +1 -0
- package/dist/src/adapters/registry.d.ts +59 -0
- package/dist/src/adapters/registry.js +77 -0
- package/dist/src/adapters/registry.js.map +1 -0
- package/dist/src/adapters/smtp.d.ts +213 -0
- package/dist/src/adapters/smtp.js +499 -0
- package/dist/src/adapters/smtp.js.map +1 -0
- package/dist/src/adapters/vault-provider.d.ts +114 -0
- package/dist/src/adapters/vault-provider.js +161 -0
- package/dist/src/adapters/vault-provider.js.map +1 -0
- package/dist/src/adapters/zzz.d.ts +66 -0
- package/dist/src/adapters/zzz.js +299 -0
- package/dist/src/adapters/zzz.js.map +1 -0
- package/dist/src/channels/batch.d.ts +109 -0
- package/dist/src/channels/batch.js +121 -0
- package/dist/src/channels/batch.js.map +1 -0
- package/dist/src/channels/cli.d.ts +193 -0
- package/dist/src/channels/cli.js +468 -0
- package/dist/src/channels/cli.js.map +1 -0
- package/dist/src/channels/conformance.d.ts +92 -0
- package/dist/src/channels/conformance.js +445 -0
- package/dist/src/channels/conformance.js.map +1 -0
- package/dist/src/channels/contract.d.ts +623 -0
- package/dist/src/channels/contract.js +494 -0
- package/dist/src/channels/contract.js.map +1 -0
- package/dist/src/channels/payload-view.d.ts +35 -0
- package/dist/src/channels/payload-view.js +43 -0
- package/dist/src/channels/payload-view.js.map +1 -0
- package/dist/src/channels/render-queue.d.ts +149 -0
- package/dist/src/channels/render-queue.js +564 -0
- package/dist/src/channels/render-queue.js.map +1 -0
- package/dist/src/channels/tagging.d.ts +196 -0
- package/dist/src/channels/tagging.js +723 -0
- package/dist/src/channels/tagging.js.map +1 -0
- package/dist/src/channels/telegram.d.ts +1832 -0
- package/dist/src/channels/telegram.js +3190 -0
- package/dist/src/channels/telegram.js.map +1 -0
- package/dist/src/channels/web.d.ts +341 -0
- package/dist/src/channels/web.js +903 -0
- package/dist/src/channels/web.js.map +1 -0
- package/dist/src/cli/adapter.d.ts +90 -0
- package/dist/src/cli/adapter.js +288 -0
- package/dist/src/cli/adapter.js.map +1 -0
- package/dist/src/cli/amend.d.ts +59 -0
- package/dist/src/cli/amend.js +2171 -0
- package/dist/src/cli/amend.js.map +1 -0
- package/dist/src/cli/args.d.ts +43 -0
- package/dist/src/cli/args.js +86 -0
- package/dist/src/cli/args.js.map +1 -0
- package/dist/src/cli/attest.d.ts +41 -0
- package/dist/src/cli/attest.js +307 -0
- package/dist/src/cli/attest.js.map +1 -0
- package/dist/src/cli/audit-card.d.ts +62 -0
- package/dist/src/cli/audit-card.js +201 -0
- package/dist/src/cli/audit-card.js.map +1 -0
- package/dist/src/cli/audit.d.ts +59 -0
- package/dist/src/cli/audit.js +460 -0
- package/dist/src/cli/audit.js.map +1 -0
- package/dist/src/cli/channel-telegram.d.ts +806 -0
- package/dist/src/cli/channel-telegram.js +2063 -0
- package/dist/src/cli/channel-telegram.js.map +1 -0
- package/dist/src/cli/channel-web.d.ts +131 -0
- package/dist/src/cli/channel-web.js +357 -0
- package/dist/src/cli/channel-web.js.map +1 -0
- package/dist/src/cli/channel.d.ts +71 -0
- package/dist/src/cli/channel.js +438 -0
- package/dist/src/cli/channel.js.map +1 -0
- package/dist/src/cli/checkpoint-tap.d.ts +169 -0
- package/dist/src/cli/checkpoint-tap.js +238 -0
- package/dist/src/cli/checkpoint-tap.js.map +1 -0
- package/dist/src/cli/codex.d.ts +2 -0
- package/dist/src/cli/codex.js +172 -0
- package/dist/src/cli/codex.js.map +1 -0
- package/dist/src/cli/coverage.d.ts +61 -0
- package/dist/src/cli/coverage.js +343 -0
- package/dist/src/cli/coverage.js.map +1 -0
- package/dist/src/cli/daemon.d.ts +120 -0
- package/dist/src/cli/daemon.js +631 -0
- package/dist/src/cli/daemon.js.map +1 -0
- package/dist/src/cli/doctor.d.ts +129 -0
- package/dist/src/cli/doctor.js +2762 -0
- package/dist/src/cli/doctor.js.map +1 -0
- package/dist/src/cli/env.d.ts +65 -0
- package/dist/src/cli/env.js +302 -0
- package/dist/src/cli/env.js.map +1 -0
- package/dist/src/cli/execute.d.ts +202 -0
- package/dist/src/cli/execute.js +1682 -0
- package/dist/src/cli/execute.js.map +1 -0
- package/dist/src/cli/exit-codes.d.ts +73 -0
- package/dist/src/cli/exit-codes.js +82 -0
- package/dist/src/cli/exit-codes.js.map +1 -0
- package/dist/src/cli/feedback.d.ts +60 -0
- package/dist/src/cli/feedback.js +205 -0
- package/dist/src/cli/feedback.js.map +1 -0
- package/dist/src/cli/gate-window.d.ts +40 -0
- package/dist/src/cli/gate-window.js +294 -0
- package/dist/src/cli/gate-window.js.map +1 -0
- package/dist/src/cli/gate.d.ts +68 -0
- package/dist/src/cli/gate.js +557 -0
- package/dist/src/cli/gate.js.map +1 -0
- package/dist/src/cli/git-scope.d.ts +190 -0
- package/dist/src/cli/git-scope.js +295 -0
- package/dist/src/cli/git-scope.js.map +1 -0
- package/dist/src/cli/gloss-attach.d.ts +85 -0
- package/dist/src/cli/gloss-attach.js +107 -0
- package/dist/src/cli/gloss-attach.js.map +1 -0
- package/dist/src/cli/gloss-codex-child.d.ts +9 -0
- package/dist/src/cli/gloss-codex-child.js +149 -0
- package/dist/src/cli/gloss-codex-child.js.map +1 -0
- package/dist/src/cli/gloss-codex.d.ts +24 -0
- package/dist/src/cli/gloss-codex.js +255 -0
- package/dist/src/cli/gloss-codex.js.map +1 -0
- package/dist/src/cli/gloss-options.d.ts +42 -0
- package/dist/src/cli/gloss-options.js +79 -0
- package/dist/src/cli/gloss-options.js.map +1 -0
- package/dist/src/cli/gloss.d.ts +265 -0
- package/dist/src/cli/gloss.js +362 -0
- package/dist/src/cli/gloss.js.map +1 -0
- package/dist/src/cli/help.d.ts +103 -0
- package/dist/src/cli/help.js +2339 -0
- package/dist/src/cli/help.js.map +1 -0
- package/dist/src/cli/hook-codex.d.ts +78 -0
- package/dist/src/cli/hook-codex.js +167 -0
- package/dist/src/cli/hook-codex.js.map +1 -0
- package/dist/src/cli/hook.d.ts +331 -0
- package/dist/src/cli/hook.js +2849 -0
- package/dist/src/cli/hook.js.map +1 -0
- package/dist/src/cli/import.d.ts +35 -0
- package/dist/src/cli/import.js +175 -0
- package/dist/src/cli/import.js.map +1 -0
- package/dist/src/cli/init.d.ts +84 -0
- package/dist/src/cli/init.js +336 -0
- package/dist/src/cli/init.js.map +1 -0
- package/dist/src/cli/instructions.d.ts +23 -0
- package/dist/src/cli/instructions.js +262 -0
- package/dist/src/cli/instructions.js.map +1 -0
- package/dist/src/cli/journal.d.ts +41 -0
- package/dist/src/cli/journal.js +238 -0
- package/dist/src/cli/journal.js.map +1 -0
- package/dist/src/cli/log-advance.d.ts +287 -0
- package/dist/src/cli/log-advance.js +840 -0
- package/dist/src/cli/log-advance.js.map +1 -0
- package/dist/src/cli/log-anchor.d.ts +176 -0
- package/dist/src/cli/log-anchor.js +387 -0
- package/dist/src/cli/log-anchor.js.map +1 -0
- package/dist/src/cli/log-checkpoint.d.ts +22 -0
- package/dist/src/cli/log-checkpoint.js +128 -0
- package/dist/src/cli/log-checkpoint.js.map +1 -0
- package/dist/src/cli/log-sync.d.ts +243 -0
- package/dist/src/cli/log-sync.js +849 -0
- package/dist/src/cli/log-sync.js.map +1 -0
- package/dist/src/cli/log-verbs.d.ts +16 -0
- package/dist/src/cli/log-verbs.js +360 -0
- package/dist/src/cli/log-verbs.js.map +1 -0
- package/dist/src/cli/long-help.d.ts +70 -0
- package/dist/src/cli/long-help.js +148 -0
- package/dist/src/cli/long-help.js.map +1 -0
- package/dist/src/cli/main.d.ts +77 -0
- package/dist/src/cli/main.js +1206 -0
- package/dist/src/cli/main.js.map +1 -0
- package/dist/src/cli/mcp.d.ts +52 -0
- package/dist/src/cli/mcp.js +306 -0
- package/dist/src/cli/mcp.js.map +1 -0
- package/dist/src/cli/paths.d.ts +56 -0
- package/dist/src/cli/paths.js +79 -0
- package/dist/src/cli/paths.js.map +1 -0
- package/dist/src/cli/payload.d.ts +58 -0
- package/dist/src/cli/payload.js +253 -0
- package/dist/src/cli/payload.js.map +1 -0
- package/dist/src/cli/policy.d.ts +43 -0
- package/dist/src/cli/policy.js +229 -0
- package/dist/src/cli/policy.js.map +1 -0
- package/dist/src/cli/preflight.d.ts +363 -0
- package/dist/src/cli/preflight.js +1175 -0
- package/dist/src/cli/preflight.js.map +1 -0
- package/dist/src/cli/progress.d.ts +78 -0
- package/dist/src/cli/progress.js +112 -0
- package/dist/src/cli/progress.js.map +1 -0
- package/dist/src/cli/prompt.d.ts +209 -0
- package/dist/src/cli/prompt.js +312 -0
- package/dist/src/cli/prompt.js.map +1 -0
- package/dist/src/cli/quickstart.d.ts +46 -0
- package/dist/src/cli/quickstart.js +297 -0
- package/dist/src/cli/quickstart.js.map +1 -0
- package/dist/src/cli/records.d.ts +34 -0
- package/dist/src/cli/records.js +66 -0
- package/dist/src/cli/records.js.map +1 -0
- package/dist/src/cli/render.d.ts +22 -0
- package/dist/src/cli/render.js +132 -0
- package/dist/src/cli/render.js.map +1 -0
- package/dist/src/cli/sandbox.d.ts +51 -0
- package/dist/src/cli/sandbox.js +150 -0
- package/dist/src/cli/sandbox.js.map +1 -0
- package/dist/src/cli/scaffold.d.ts +79 -0
- package/dist/src/cli/scaffold.js +137 -0
- package/dist/src/cli/scaffold.js.map +1 -0
- package/dist/src/cli/setup-adapter.d.ts +137 -0
- package/dist/src/cli/setup-adapter.js +509 -0
- package/dist/src/cli/setup-adapter.js.map +1 -0
- package/dist/src/cli/setup-channel.d.ts +117 -0
- package/dist/src/cli/setup-channel.js +635 -0
- package/dist/src/cli/setup-channel.js.map +1 -0
- package/dist/src/cli/setup-checkpoint.d.ts +57 -0
- package/dist/src/cli/setup-checkpoint.js +196 -0
- package/dist/src/cli/setup-checkpoint.js.map +1 -0
- package/dist/src/cli/setup-common.d.ts +275 -0
- package/dist/src/cli/setup-common.js +376 -0
- package/dist/src/cli/setup-common.js.map +1 -0
- package/dist/src/cli/setup-flow.d.ts +287 -0
- package/dist/src/cli/setup-flow.js +476 -0
- package/dist/src/cli/setup-flow.js.map +1 -0
- package/dist/src/cli/setup-service.d.ts +96 -0
- package/dist/src/cli/setup-service.js +308 -0
- package/dist/src/cli/setup-service.js.map +1 -0
- package/dist/src/cli/setup.d.ts +202 -0
- package/dist/src/cli/setup.js +473 -0
- package/dist/src/cli/setup.js.map +1 -0
- package/dist/src/cli/style.d.ts +320 -0
- package/dist/src/cli/style.js +469 -0
- package/dist/src/cli/style.js.map +1 -0
- package/dist/src/cli/token.d.ts +39 -0
- package/dist/src/cli/token.js +274 -0
- package/dist/src/cli/token.js.map +1 -0
- package/dist/src/cli/up.d.ts +155 -0
- package/dist/src/cli/up.js +849 -0
- package/dist/src/cli/up.js.map +1 -0
- package/dist/src/cli/usage.d.ts +37 -0
- package/dist/src/cli/usage.js +91 -0
- package/dist/src/cli/usage.js.map +1 -0
- package/dist/src/cli/values.d.ts +40 -0
- package/dist/src/cli/values.js +189 -0
- package/dist/src/cli/values.js.map +1 -0
- package/dist/src/cli/vault.d.ts +59 -0
- package/dist/src/cli/vault.js +362 -0
- package/dist/src/cli/vault.js.map +1 -0
- package/dist/src/cli/verb-registry.d.ts +76 -0
- package/dist/src/cli/verb-registry.js +2341 -0
- package/dist/src/cli/verb-registry.js.map +1 -0
- package/dist/src/cli/wordmark.d.ts +31 -0
- package/dist/src/cli/wordmark.js +52 -0
- package/dist/src/cli/wordmark.js.map +1 -0
- package/dist/src/codex/doctor.d.ts +13 -0
- package/dist/src/codex/doctor.js +41 -0
- package/dist/src/codex/doctor.js.map +1 -0
- package/dist/src/codex/manifest.d.ts +49 -0
- package/dist/src/codex/manifest.js +103 -0
- package/dist/src/codex/manifest.js.map +1 -0
- package/dist/src/codex/templates.d.ts +41 -0
- package/dist/src/codex/templates.js +319 -0
- package/dist/src/codex/templates.js.map +1 -0
- package/dist/src/codex/trust.d.ts +19 -0
- package/dist/src/codex/trust.js +183 -0
- package/dist/src/codex/trust.js.map +1 -0
- package/dist/src/codex/workspace-plan.d.ts +131 -0
- package/dist/src/codex/workspace-plan.js +561 -0
- package/dist/src/codex/workspace-plan.js.map +1 -0
- package/dist/src/core/actor.d.ts +2 -0
- package/dist/src/core/actor.js +5 -0
- package/dist/src/core/actor.js.map +1 -0
- package/dist/src/core/advance-cycle.d.ts +170 -0
- package/dist/src/core/advance-cycle.js +200 -0
- package/dist/src/core/advance-cycle.js.map +1 -0
- package/dist/src/core/agents-md.d.ts +276 -0
- package/dist/src/core/agents-md.js +747 -0
- package/dist/src/core/agents-md.js.map +1 -0
- package/dist/src/core/apply-patch.d.ts +49 -0
- package/dist/src/core/apply-patch.js +266 -0
- package/dist/src/core/apply-patch.js.map +1 -0
- package/dist/src/core/attest.d.ts +420 -0
- package/dist/src/core/attest.js +589 -0
- package/dist/src/core/attest.js.map +1 -0
- package/dist/src/core/audit.d.ts +492 -0
- package/dist/src/core/audit.js +882 -0
- package/dist/src/core/audit.js.map +1 -0
- package/dist/src/core/budgets.d.ts +238 -0
- package/dist/src/core/budgets.js +449 -0
- package/dist/src/core/budgets.js.map +1 -0
- package/dist/src/core/checkpoint.d.ts +500 -0
- package/dist/src/core/checkpoint.js +738 -0
- package/dist/src/core/checkpoint.js.map +1 -0
- package/dist/src/core/child-env.d.ts +88 -0
- package/dist/src/core/child-env.js +86 -0
- package/dist/src/core/child-env.js.map +1 -0
- package/dist/src/core/clock.d.ts +52 -0
- package/dist/src/core/clock.js +43 -0
- package/dist/src/core/clock.js.map +1 -0
- package/dist/src/core/command-class.d.ts +543 -0
- package/dist/src/core/command-class.js +2356 -0
- package/dist/src/core/command-class.js.map +1 -0
- package/dist/src/core/coverage-sources/adapter.d.ts +40 -0
- package/dist/src/core/coverage-sources/adapter.js +71 -0
- package/dist/src/core/coverage-sources/adapter.js.map +1 -0
- package/dist/src/core/coverage-sources/gh.d.ts +48 -0
- package/dist/src/core/coverage-sources/gh.js +136 -0
- package/dist/src/core/coverage-sources/gh.js.map +1 -0
- package/dist/src/core/coverage-sources/git.d.ts +101 -0
- package/dist/src/core/coverage-sources/git.js +269 -0
- package/dist/src/core/coverage-sources/git.js.map +1 -0
- package/dist/src/core/coverage.d.ts +217 -0
- package/dist/src/core/coverage.js +337 -0
- package/dist/src/core/coverage.js.map +1 -0
- package/dist/src/core/credential-spec.d.ts +72 -0
- package/dist/src/core/credential-spec.js +23 -0
- package/dist/src/core/credential-spec.js.map +1 -0
- package/dist/src/core/dark-session.d.ts +331 -0
- package/dist/src/core/dark-session.js +714 -0
- package/dist/src/core/dark-session.js.map +1 -0
- package/dist/src/core/decision-refusal.d.ts +185 -0
- package/dist/src/core/decision-refusal.js +265 -0
- package/dist/src/core/decision-refusal.js.map +1 -0
- package/dist/src/core/env-file.d.ts +450 -0
- package/dist/src/core/env-file.js +837 -0
- package/dist/src/core/env-file.js.map +1 -0
- package/dist/src/core/execute.d.ts +858 -0
- package/dist/src/core/execute.js +1271 -0
- package/dist/src/core/execute.js.map +1 -0
- package/dist/src/core/frontmatter.d.ts +78 -0
- package/dist/src/core/frontmatter.js +100 -0
- package/dist/src/core/frontmatter.js.map +1 -0
- package/dist/src/core/gate-window.d.ts +312 -0
- package/dist/src/core/gate-window.js +506 -0
- package/dist/src/core/gate-window.js.map +1 -0
- package/dist/src/core/gate.d.ts +1364 -0
- package/dist/src/core/gate.js +3002 -0
- package/dist/src/core/gate.js.map +1 -0
- package/dist/src/core/git-run.d.ts +73 -0
- package/dist/src/core/git-run.js +93 -0
- package/dist/src/core/git-run.js.map +1 -0
- package/dist/src/core/harness-version.d.ts +157 -0
- package/dist/src/core/harness-version.js +211 -0
- package/dist/src/core/harness-version.js.map +1 -0
- package/dist/src/core/harness-wait.d.ts +55 -0
- package/dist/src/core/harness-wait.js +58 -0
- package/dist/src/core/harness-wait.js.map +1 -0
- package/dist/src/core/head-retry.d.ts +107 -0
- package/dist/src/core/head-retry.js +121 -0
- package/dist/src/core/head-retry.js.map +1 -0
- package/dist/src/core/instance.d.ts +253 -0
- package/dist/src/core/instance.js +319 -0
- package/dist/src/core/instance.js.map +1 -0
- package/dist/src/core/intake-limits.d.ts +247 -0
- package/dist/src/core/intake-limits.js +350 -0
- package/dist/src/core/intake-limits.js.map +1 -0
- package/dist/src/core/jcs.d.ts +52 -0
- package/dist/src/core/jcs.js +132 -0
- package/dist/src/core/jcs.js.map +1 -0
- package/dist/src/core/journal.d.ts +144 -0
- package/dist/src/core/journal.js +200 -0
- package/dist/src/core/journal.js.map +1 -0
- package/dist/src/core/live-draw.d.ts +436 -0
- package/dist/src/core/live-draw.js +703 -0
- package/dist/src/core/live-draw.js.map +1 -0
- package/dist/src/core/log-reconcile.d.ts +89 -0
- package/dist/src/core/log-reconcile.js +136 -0
- package/dist/src/core/log-reconcile.js.map +1 -0
- package/dist/src/core/log-subscribe.d.ts +36 -0
- package/dist/src/core/log-subscribe.js +162 -0
- package/dist/src/core/log-subscribe.js.map +1 -0
- package/dist/src/core/log.d.ts +278 -0
- package/dist/src/core/log.js +546 -0
- package/dist/src/core/log.js.map +1 -0
- package/dist/src/core/loop.d.ts +274 -0
- package/dist/src/core/loop.js +487 -0
- package/dist/src/core/loop.js.map +1 -0
- package/dist/src/core/md-fence.d.ts +41 -0
- package/dist/src/core/md-fence.js +74 -0
- package/dist/src/core/md-fence.js.map +1 -0
- package/dist/src/core/money.d.ts +147 -0
- package/dist/src/core/money.js +195 -0
- package/dist/src/core/money.js.map +1 -0
- package/dist/src/core/payload-census.d.ts +74 -0
- package/dist/src/core/payload-census.js +146 -0
- package/dist/src/core/payload-census.js.map +1 -0
- package/dist/src/core/payload-store.d.ts +175 -0
- package/dist/src/core/payload-store.js +340 -0
- package/dist/src/core/payload-store.js.map +1 -0
- package/dist/src/core/payload.d.ts +71 -0
- package/dist/src/core/payload.js +80 -0
- package/dist/src/core/payload.js.map +1 -0
- package/dist/src/core/policy-diff.d.ts +292 -0
- package/dist/src/core/policy-diff.js +588 -0
- package/dist/src/core/policy-diff.js.map +1 -0
- package/dist/src/core/policy-expectations.d.ts +199 -0
- package/dist/src/core/policy-expectations.js +394 -0
- package/dist/src/core/policy-expectations.js.map +1 -0
- package/dist/src/core/policy-explain.d.ts +150 -0
- package/dist/src/core/policy-explain.js +258 -0
- package/dist/src/core/policy-explain.js.map +1 -0
- package/dist/src/core/policy-load.d.ts +527 -0
- package/dist/src/core/policy-load.js +536 -0
- package/dist/src/core/policy-load.js.map +1 -0
- package/dist/src/core/policy-match.d.ts +281 -0
- package/dist/src/core/policy-match.js +478 -0
- package/dist/src/core/policy-match.js.map +1 -0
- package/dist/src/core/policy-proposal.d.ts +265 -0
- package/dist/src/core/policy-proposal.js +458 -0
- package/dist/src/core/policy-proposal.js.map +1 -0
- package/dist/src/core/prompt-layout.d.ts +221 -0
- package/dist/src/core/prompt-layout.js +422 -0
- package/dist/src/core/prompt-layout.js.map +1 -0
- package/dist/src/core/protected-path-guard.d.ts +453 -0
- package/dist/src/core/protected-path-guard.js +1566 -0
- package/dist/src/core/protected-path-guard.js.map +1 -0
- package/dist/src/core/registration.d.ts +25 -0
- package/dist/src/core/registration.js +39 -0
- package/dist/src/core/registration.js.map +1 -0
- package/dist/src/core/reindex.d.ts +99 -0
- package/dist/src/core/reindex.js +336 -0
- package/dist/src/core/reindex.js.map +1 -0
- package/dist/src/core/sampler.d.ts +313 -0
- package/dist/src/core/sampler.js +388 -0
- package/dist/src/core/sampler.js.map +1 -0
- package/dist/src/core/sandbox.d.ts +290 -0
- package/dist/src/core/sandbox.js +424 -0
- package/dist/src/core/sandbox.js.map +1 -0
- package/dist/src/core/seal.d.ts +165 -0
- package/dist/src/core/seal.js +290 -0
- package/dist/src/core/seal.js.map +1 -0
- package/dist/src/core/state.d.ts +505 -0
- package/dist/src/core/state.js +1009 -0
- package/dist/src/core/state.js.map +1 -0
- package/dist/src/core/task-file.d.ts +185 -0
- package/dist/src/core/task-file.js +464 -0
- package/dist/src/core/task-file.js.map +1 -0
- package/dist/src/core/telegram-config.d.ts +93 -0
- package/dist/src/core/telegram-config.js +114 -0
- package/dist/src/core/telegram-config.js.map +1 -0
- package/dist/src/core/token.d.ts +409 -0
- package/dist/src/core/token.js +561 -0
- package/dist/src/core/token.js.map +1 -0
- package/dist/src/core/validate.d.ts +138 -0
- package/dist/src/core/validate.js +0 -0
- package/dist/src/core/validate.js.map +1 -0
- package/dist/src/core/values.d.ts +137 -0
- package/dist/src/core/values.js +153 -0
- package/dist/src/core/values.js.map +1 -0
- package/dist/src/core/vault.d.ts +291 -0
- package/dist/src/core/vault.js +612 -0
- package/dist/src/core/vault.js.map +1 -0
- package/dist/src/core/verified-snapshot.d.ts +204 -0
- package/dist/src/core/verified-snapshot.js +506 -0
- package/dist/src/core/verified-snapshot.js.map +1 -0
- package/dist/src/core/verify.d.ts +336 -0
- package/dist/src/core/verify.js +549 -0
- package/dist/src/core/verify.js.map +1 -0
- package/dist/src/core/version.d.ts +8 -0
- package/dist/src/core/version.js +9 -0
- package/dist/src/core/version.js.map +1 -0
- package/dist/src/core/wysiwys.d.ts +370 -0
- package/dist/src/core/wysiwys.js +728 -0
- package/dist/src/core/wysiwys.js.map +1 -0
- package/dist/src/daemon/advance-child.d.ts +39 -0
- package/dist/src/daemon/advance-child.js +78 -0
- package/dist/src/daemon/advance-child.js.map +1 -0
- package/dist/src/daemon/advance.d.ts +466 -0
- package/dist/src/daemon/advance.js +849 -0
- package/dist/src/daemon/advance.js.map +1 -0
- package/dist/src/daemon/audit.d.ts +87 -0
- package/dist/src/daemon/audit.js +90 -0
- package/dist/src/daemon/audit.js.map +1 -0
- package/dist/src/daemon/daemon.d.ts +1180 -0
- package/dist/src/daemon/daemon.js +1988 -0
- package/dist/src/daemon/daemon.js.map +1 -0
- package/dist/src/daemon/dark-session.d.ts +64 -0
- package/dist/src/daemon/dark-session.js +119 -0
- package/dist/src/daemon/dark-session.js.map +1 -0
- package/dist/src/daemon/draw-child.d.ts +36 -0
- package/dist/src/daemon/draw-child.js +132 -0
- package/dist/src/daemon/draw-child.js.map +1 -0
- package/dist/src/daemon/draw.d.ts +154 -0
- package/dist/src/daemon/draw.js +458 -0
- package/dist/src/daemon/draw.js.map +1 -0
- package/dist/src/daemon/git-evidence.d.ts +173 -0
- package/dist/src/daemon/git-evidence.js +345 -0
- package/dist/src/daemon/git-evidence.js.map +1 -0
- package/dist/src/daemon/projection.d.ts +180 -0
- package/dist/src/daemon/projection.js +233 -0
- package/dist/src/daemon/projection.js.map +1 -0
- package/dist/src/daemon/prune.d.ts +207 -0
- package/dist/src/daemon/prune.js +376 -0
- package/dist/src/daemon/prune.js.map +1 -0
- package/dist/src/mcp/http.d.ts +113 -0
- package/dist/src/mcp/http.js +343 -0
- package/dist/src/mcp/http.js.map +1 -0
- package/dist/src/mcp/server.d.ts +265 -0
- package/dist/src/mcp/server.js +602 -0
- package/dist/src/mcp/server.js.map +1 -0
- package/docs/adapter-api.md +106 -0
- package/docs/cli-reference.md +5716 -0
- package/docs/codex-enforced-session.md +30 -0
- package/package.json +53 -4
- package/schema/.gitkeep +0 -0
- package/schema/LICENSE +117 -0
- package/schema/codex-instance.schema.json +82 -0
- package/schema/envelope.schema.json +137 -0
- package/schema/event.schema.json +1811 -0
- package/schema/fixtures/codex-instance/invalid/unpinned-codex-version.json +40 -0
- package/schema/fixtures/codex-instance/valid/canonical.json +40 -0
- package/schema/fixtures/envelope/invalid/action-missing-idempotency-key.json +15 -0
- package/schema/fixtures/envelope/invalid/action-unknown-class-format.json +14 -0
- package/schema/fixtures/envelope/invalid/confidence-out-of-range.json +11 -0
- package/schema/fixtures/envelope/invalid/est-cost-bare-number.json +14 -0
- package/schema/fixtures/envelope/invalid/est-cost-noncanonical-string.json +14 -0
- package/schema/fixtures/envelope/invalid/malformed-assignee.json +10 -0
- package/schema/fixtures/envelope/invalid/malformed-created-by.json +7 -0
- package/schema/fixtures/envelope/invalid/malformed-max-latency.json +11 -0
- package/schema/fixtures/envelope/invalid/malformed-payload-hash.json +14 -0
- package/schema/fixtures/envelope/invalid/max-cost-bare-number.json +11 -0
- package/schema/fixtures/envelope/invalid/missing-origin.json +3 -0
- package/schema/fixtures/envelope/invalid/negative-est-cost.json +14 -0
- package/schema/fixtures/envelope/invalid/unknown-state.json +7 -0
- package/schema/fixtures/envelope/invalid/unknown-top-level-field.json +8 -0
- package/schema/fixtures/envelope/valid/action-payload-hash.json +17 -0
- package/schema/fixtures/envelope/valid/actions-without-budget.json +18 -0
- package/schema/fixtures/envelope/valid/canonical.json +25 -0
- package/schema/fixtures/envelope/valid/minimal.json +7 -0
- package/schema/fixtures/envelope/valid/multi-action-executed.json +30 -0
- package/schema/fixtures/envelope/valid/record-write-stage.json +22 -0
- package/schema/fixtures/event/invalid/approval-granted-agent-actor.json +15 -0
- package/schema/fixtures/event/invalid/approval-granted-empty-batch-delivery-id.json +17 -0
- package/schema/fixtures/event/invalid/approval-granted-fifth-reaction.json +16 -0
- package/schema/fixtures/event/invalid/approval-granted-missing-actor.json +14 -0
- package/schema/fixtures/event/invalid/approval-requested-missing-action-key.json +14 -0
- package/schema/fixtures/event/invalid/approval-withdrawn-agent-policy-drift.json +15 -0
- package/schema/fixtures/event/invalid/approval-withdrawn-missing-reason.json +15 -0
- package/schema/fixtures/event/invalid/approval-withdrawn-system-actor.json +15 -0
- package/schema/fixtures/event/invalid/audit-decision-refused-human-actor.json +17 -0
- package/schema/fixtures/event/invalid/audit-decision-refused-missing-code.json +16 -0
- package/schema/fixtures/event/invalid/audit-reviewed-agent-actor.json +15 -0
- package/schema/fixtures/event/invalid/audit-reviewed-loved-no-note.json +16 -0
- package/schema/fixtures/event/invalid/audit-reviewed-system-actor.json +15 -0
- package/schema/fixtures/event/invalid/bad-actor-prefix.json +15 -0
- package/schema/fixtures/event/invalid/est-cost-bare-number.json +17 -0
- package/schema/fixtures/event/invalid/execution-completed-fabricated-exit-code.json +16 -0
- package/schema/fixtures/event/invalid/execution-completed-provider-ref-empty-id.json +18 -0
- package/schema/fixtures/event/invalid/execution-completed-provider-ref-extra-field.json +19 -0
- package/schema/fixtures/event/invalid/execution-completed-provider-ref-id-not-string.json +18 -0
- package/schema/fixtures/event/invalid/execution-completed-provider-ref-missing-adapter.json +17 -0
- package/schema/fixtures/event/invalid/execution-failed-open-reported-by.json +16 -0
- package/schema/fixtures/event/invalid/execution-indeterminate-open-reason.json +14 -0
- package/schema/fixtures/event/invalid/execution-reconciled-agent-actor.json +17 -0
- package/schema/fixtures/event/invalid/execution-started-negative-env-stripped.json +16 -0
- package/schema/fixtures/event/invalid/gate-bypassed-missing-opened-seq.json +15 -0
- package/schema/fixtures/event/invalid/gate-closed-non-integer-opened-seq.json +13 -0
- package/schema/fixtures/event/invalid/gate-opened-agent-actor.json +16 -0
- package/schema/fixtures/event/invalid/gate-organ-attested-absolute-path.json +14 -0
- package/schema/fixtures/event/invalid/gate-organ-attested-agent-actor.json +14 -0
- package/schema/fixtures/event/invalid/gate-organ-attested-missing-organ-path.json +13 -0
- package/schema/fixtures/event/invalid/harness-unknown-kind.json +18 -0
- package/schema/fixtures/event/invalid/harness-version-multiline.json +16 -0
- package/schema/fixtures/event/invalid/log-checkpoint-agent-actor.json +17 -0
- package/schema/fixtures/event/invalid/log-checkpoint-missing-signature.json +16 -0
- package/schema/fixtures/event/invalid/log-checkpoint-short-signed-hash.json +17 -0
- package/schema/fixtures/event/invalid/log-checkpoint-unknown-signature-alg.json +17 -0
- package/schema/fixtures/event/invalid/malformed-ts.json +15 -0
- package/schema/fixtures/event/invalid/missing-alg.json +14 -0
- package/schema/fixtures/event/invalid/missing-hash.json +14 -0
- package/schema/fixtures/event/invalid/non-integer-seq.json +15 -0
- package/schema/fixtures/event/invalid/payload-pruned-human-actor.json +14 -0
- package/schema/fixtures/event/invalid/payload-pruned-missing-hash.json +14 -0
- package/schema/fixtures/event/invalid/policy-declined-agent-actor.json +15 -0
- package/schema/fixtures/event/invalid/policy-proposed-missing-diff.json +19 -0
- package/schema/fixtures/event/invalid/policy-proposed-system-actor.json +26 -0
- package/schema/fixtures/event/invalid/short-hash.json +15 -0
- package/schema/fixtures/event/invalid/unknown-alg.json +15 -0
- package/schema/fixtures/event/invalid/unknown-event-type.json +15 -0
- package/schema/fixtures/event/invalid/unknown-top-level-field.json +16 -0
- package/schema/fixtures/event/valid/approval-expired.json +15 -0
- package/schema/fixtures/event/valid/approval-granted-batch.json +19 -0
- package/schema/fixtures/event/valid/approval-granted-reaction.json +16 -0
- package/schema/fixtures/event/valid/approval-granted.json +15 -0
- package/schema/fixtures/event/valid/approval-rejected.json +15 -0
- package/schema/fixtures/event/valid/approval-requested.json +19 -0
- package/schema/fixtures/event/valid/approval-revoked.json +15 -0
- package/schema/fixtures/event/valid/approval-withdrawn-policy-drift.json +17 -0
- package/schema/fixtures/event/valid/approval-withdrawn.json +16 -0
- package/schema/fixtures/event/valid/audit-decision-refused.json +20 -0
- package/schema/fixtures/event/valid/audit-reviewed-reaction.json +17 -0
- package/schema/fixtures/event/valid/audit-reviewed.json +15 -0
- package/schema/fixtures/event/valid/audit-sampled.json +14 -0
- package/schema/fixtures/event/valid/budget-exceeded.json +21 -0
- package/schema/fixtures/event/valid/envelope-drift.json +16 -0
- package/schema/fixtures/event/valid/execution-completed-harness-report.json +16 -0
- package/schema/fixtures/event/valid/execution-completed-provider-ref.json +18 -0
- package/schema/fixtures/event/valid/execution-completed.json +15 -0
- package/schema/fixtures/event/valid/execution-failed-harness-report.json +16 -0
- package/schema/fixtures/event/valid/execution-failed.json +15 -0
- package/schema/fixtures/event/valid/execution-indeterminate.json +15 -0
- package/schema/fixtures/event/valid/execution-reconciled.json +17 -0
- package/schema/fixtures/event/valid/execution-started-env-stripped.json +17 -0
- package/schema/fixtures/event/valid/execution-started.json +14 -0
- package/schema/fixtures/event/valid/gate-bypassed-harness-version.json +18 -0
- package/schema/fixtures/event/valid/gate-bypassed.json +19 -0
- package/schema/fixtures/event/valid/gate-closed.json +14 -0
- package/schema/fixtures/event/valid/gate-opened.json +16 -0
- package/schema/fixtures/event/valid/gate-organ-attested.json +14 -0
- package/schema/fixtures/event/valid/genesis-null-prev.json +14 -0
- package/schema/fixtures/event/valid/log-checkpoint.json +17 -0
- package/schema/fixtures/event/valid/payload-pruned-orphan.json +13 -0
- package/schema/fixtures/event/valid/payload-pruned.json +17 -0
- package/schema/fixtures/event/valid/policy-declined.json +16 -0
- package/schema/fixtures/event/valid/policy-proposed.json +35 -0
- package/schema/fixtures/event/valid/policy-updated.json +14 -0
- package/schema/fixtures/event/valid/reconciliation-required.json +18 -0
- package/schema/fixtures/event/valid/reconciliation-satisfied.json +17 -0
- package/schema/fixtures/event/valid/route-accepted.json +15 -0
- package/schema/fixtures/event/valid/route-proposed.json +16 -0
- package/schema/fixtures/event/valid/spec-example.json +15 -0
- package/schema/fixtures/event/valid/task-registered-harness-version.json +23 -0
- package/schema/fixtures/event/valid/task-registered.json +14 -0
- package/schema/fixtures/hash/known-answer-pre-121.json +74 -0
- package/schema/fixtures/hash/known-answer.json +74 -0
- package/schema/fixtures/policy/invalid/bad-approval-ttl.json +7 -0
- package/schema/fixtures/policy/invalid/bad-web-port.json +4 -0
- package/schema/fixtures/policy/invalid/checkpoint-key-not-base64.json +6 -0
- package/schema/fixtures/policy/invalid/class-rule-missing-autonomy.json +9 -0
- package/schema/fixtures/policy/invalid/empty-class-key.json +6 -0
- package/schema/fixtures/policy/invalid/live-rate-on-human-only.json +7 -0
- package/schema/fixtures/policy/invalid/malformed-class-key.json +6 -0
- package/schema/fixtures/policy/invalid/missing-version.json +8 -0
- package/schema/fixtures/policy/invalid/negative-limit.json +9 -0
- package/schema/fixtures/policy/invalid/non-numeric-limit.json +9 -0
- package/schema/fixtures/policy/invalid/non-positive-max-pending.json +9 -0
- package/schema/fixtures/policy/invalid/on-expiry-grant.json +8 -0
- package/schema/fixtures/policy/invalid/payload-retention-bare-number.json +4 -0
- package/schema/fixtures/policy/invalid/payload-retention-compound.json +4 -0
- package/schema/fixtures/policy/invalid/payload-retention-fractional.json +4 -0
- package/schema/fixtures/policy/invalid/payload-retention-zero.json +4 -0
- package/schema/fixtures/policy/invalid/protected-paths-escape.json +4 -0
- package/schema/fixtures/policy/invalid/protected-paths-glob.json +4 -0
- package/schema/fixtures/policy/invalid/retro-rate-on-human-only.json +7 -0
- package/schema/fixtures/policy/invalid/retro-rate-on-manual.json +7 -0
- package/schema/fixtures/policy/invalid/retro-rate-zero.json +7 -0
- package/schema/fixtures/policy/invalid/sample-rate-too-high.json +5 -0
- package/schema/fixtures/policy/invalid/sampling-secret-env-empty.json +7 -0
- package/schema/fixtures/policy/invalid/sampling-secret-env-not-string.json +6 -0
- package/schema/fixtures/policy/invalid/skew-tolerance-compound.json +6 -0
- package/schema/fixtures/policy/invalid/unknown-autonomy.json +7 -0
- package/schema/fixtures/policy/invalid/unknown-class-rule-key.json +6 -0
- package/schema/fixtures/policy/invalid/unknown-top-level-key.json +7 -0
- package/schema/fixtures/policy/invalid/vault-passphrase-env-empty.json +6 -0
- package/schema/fixtures/policy/invalid/vault-passphrase-literal.json +6 -0
- package/schema/fixtures/policy/invalid/version-not-string.json +4 -0
- package/schema/fixtures/policy/valid/canonical.json +47 -0
- package/schema/fixtures/policy/valid/checkpoint-keys.json +18 -0
- package/schema/fixtures/policy/valid/class-approvers-limits.json +25 -0
- package/schema/fixtures/policy/valid/class-retro-rate.json +17 -0
- package/schema/fixtures/policy/valid/global-budgets.json +19 -0
- package/schema/fixtures/policy/valid/human-only.json +9 -0
- package/schema/fixtures/policy/valid/minimal.json +6 -0
- package/schema/fixtures/policy/valid/protected-paths.json +10 -0
- package/schema/fixtures/policy/valid/record-namespace.json +13 -0
- package/schema/fixtures/policy/valid/request-volume-limits.json +26 -0
- package/schema/fixtures/policy/valid/retention-and-sampling-secret.json +16 -0
- package/schema/fixtures/policy/valid/skew-tolerance.json +15 -0
- package/schema/fixtures/policy/valid/vault-passphrase-env.json +14 -0
- package/schema/fixtures/policy/valid/wildcards.json +15 -0
- package/schema/fixtures/policy-md/invalid/alias-bomb.md +15 -0
- package/schema/fixtures/policy-md/invalid/no-fence.md +7 -0
- package/schema/fixtures/policy-md/invalid/protected-route-not-a-subclass.md +16 -0
- package/schema/fixtures/policy-md/invalid/schema-invalid-autonomy.md +16 -0
- package/schema/fixtures/policy-md/invalid/schema-invalid-read-proof.md +17 -0
- package/schema/fixtures/policy-md/invalid/two-fences.md +19 -0
- package/schema/fixtures/policy-md/invalid/unclosed-fence.md +11 -0
- package/schema/fixtures/policy-md/invalid/wrong-info-string.md +11 -0
- package/schema/fixtures/policy-md/invalid/yaml-syntax-error.md +13 -0
- package/schema/fixtures/policy-md/precedence/both/APPROVAL.md +7 -0
- package/schema/fixtures/policy-md/precedence/both/APPROVALS.md +7 -0
- package/schema/fixtures/policy-md/precedence/fallback-only/APPROVALS.md +7 -0
- package/schema/fixtures/policy-md/valid/canonical.md +50 -0
- package/schema/fixtures/policy-md/valid/daemon-read-proof.md +18 -0
- package/schema/fixtures/policy-md/valid/minimal.md +3 -0
- package/schema/fixtures/policy-md/valid/prose-lookalikes.md +54 -0
- package/schema/fixtures/policy-md/valid/routed-protected-paths.md +49 -0
- package/schema/fixtures/policy-md/valid/with-values.md +79 -0
- package/schema/fixtures/sample-record/invalid/bad-date-time.json +4 -0
- package/schema/fixtures/sample-record/invalid/missing-required-field.json +3 -0
- package/schema/fixtures/sample-record/invalid/unknown-top-level-field.json +5 -0
- package/schema/fixtures/sample-record/invalid/wrong-type.json +4 -0
- package/schema/fixtures/sample-record/valid/minimal.json +4 -0
- package/schema/fixtures/sample-record/valid/with-note.json +5 -0
- package/schema/fixtures/values/invalid/class-shaped.json +9 -0
- package/schema/fixtures/values/invalid/duplicate-entry.json +4 -0
- package/schema/fixtures/values/invalid/non-string-item.json +4 -0
- package/schema/fixtures/values/invalid/over-cap.json +26 -0
- package/schema/fixtures/values/invalid/unknown-key.json +5 -0
- package/schema/fixtures/values/invalid/version-string.json +1 -0
- package/schema/fixtures/values/valid/empty-lists.json +7 -0
- package/schema/fixtures/values/valid/full.json +20 -0
- package/schema/fixtures/values/valid/minimal.json +1 -0
- package/schema/fixtures/values-md/invalid/schema-invalid.md +62 -0
- package/schema/fixtures/values-md/invalid/two-blocks.md +69 -0
- package/schema/fixtures/values-md/invalid/unterminated.md +61 -0
- package/schema/fixtures/values-md/invalid/yaml-error.md +63 -0
- package/schema/fixtures/values-md/valid/absent.md +50 -0
- package/schema/fixtures/values-md/valid/with-values.md +79 -0
- package/schema/policy.schema.json +501 -0
- package/schema/sample-record.schema.json +26 -0
- package/schema/values.schema.json +55 -0
- package/templates/codex/README.md +9 -0
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which harness binary issued a hook-written record (APRV-227).
|
|
3
|
+
*
|
|
4
|
+
* ## The hole
|
|
5
|
+
*
|
|
6
|
+
* A harness upgrade — `claude update`, a global npm install, an unattended
|
|
7
|
+
* updater on a launchd timer — swaps the binary that hosts the PreToolUse hook
|
|
8
|
+
* without a line anywhere. A new harness release can change the hook envelope
|
|
9
|
+
* semantics and quietly stop the gate firing, and the first evidence would be
|
|
10
|
+
* an action nobody was asked about. The gate cannot stop a person upgrading
|
|
11
|
+
* their own machine, and should not. What it can do is notice the effect: the
|
|
12
|
+
* records the hook writes name the harness that issued them, and `approval
|
|
13
|
+
* doctor` says so when the installed binary no longer matches the last one the
|
|
14
|
+
* log saw.
|
|
15
|
+
*
|
|
16
|
+
* ## What this field is, and what it is emphatically not
|
|
17
|
+
*
|
|
18
|
+
* It is INFORMATIONAL. Nothing in the runtime reads it back as an input:
|
|
19
|
+
* no class resolution, no irreversibility floor, no loop-escalation streak, no
|
|
20
|
+
* budget arithmetic, no sampling draw. SPEC.md §11.1 invariant 4 says a
|
|
21
|
+
* self-reported field never reduces scrutiny, and the discipline that keeps
|
|
22
|
+
* that true here is that the value has exactly one reader — a doctor row that
|
|
23
|
+
* can only ADD a red line — and exactly one direction it can move a human's
|
|
24
|
+
* attention, which is toward the log rather than away from it. A harness that
|
|
25
|
+
* lies about its own version buys itself nothing: the row it defeats is a row
|
|
26
|
+
* that would have asked for a look.
|
|
27
|
+
*
|
|
28
|
+
* It is also OPTIONAL and additive. A record written before the field existed
|
|
29
|
+
* carries neither half and still validates and still verifies; a hook that
|
|
30
|
+
* cannot establish a version writes neither half rather than a guess.
|
|
31
|
+
*
|
|
32
|
+
* ## Where the value comes from, in order
|
|
33
|
+
*
|
|
34
|
+
* 1. The hook event's own version field, where the harness supplies one.
|
|
35
|
+
* Claude Code's PreToolUse event may carry it; Cursor's does not.
|
|
36
|
+
* 2. `<binary> --version`, read at most ONCE per process ({@link
|
|
37
|
+
* installedHarnessVersion} memoizes, including the failures) with a short
|
|
38
|
+
* hard timeout.
|
|
39
|
+
* 3. Absent.
|
|
40
|
+
*
|
|
41
|
+
* Step 2 is why this module is careful about cost. A hook process exists per
|
|
42
|
+
* gated tool call, and APRV-186/188/212 each removed a term from what that
|
|
43
|
+
* process pays. So the probe is reached only on a path that is about to WRITE a
|
|
44
|
+
* record — a registration or a bypass — and never on the pass-through path that
|
|
45
|
+
* answers `cat README.md`. The memo makes a multi-class command pay once.
|
|
46
|
+
*
|
|
47
|
+
* ## Why the binary name is not configurable
|
|
48
|
+
*
|
|
49
|
+
* `cli/gloss.ts` already settled this: a runtime that let a policy or an
|
|
50
|
+
* environment variable name the executable it runs would have invented a new
|
|
51
|
+
* way to be told what to execute. The map below is the whole list, PATH is the
|
|
52
|
+
* only seam, and a test puts a stub binary in front of it.
|
|
53
|
+
*/
|
|
54
|
+
import { spawnSync } from "node:child_process";
|
|
55
|
+
import { childEnvironment } from "./child-env.js";
|
|
56
|
+
/** The harnesses this runtime speaks a hook protocol for. */
|
|
57
|
+
export const HARNESS_KINDS = ["claude-code", "cursor", "codex"];
|
|
58
|
+
/** Is `value` one of the harnesses this runtime knows? */
|
|
59
|
+
export function isHarnessKind(value) {
|
|
60
|
+
return typeof value === "string" && HARNESS_KINDS.includes(value);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* The executable each harness installs on PATH.
|
|
64
|
+
*
|
|
65
|
+
* Not configurable, deliberately (see the header). A harness whose binary is
|
|
66
|
+
* not one of these has no version to read here, and the field is simply absent.
|
|
67
|
+
*/
|
|
68
|
+
export const HARNESS_BINARY = {
|
|
69
|
+
"claude-code": "claude",
|
|
70
|
+
cursor: "cursor-agent",
|
|
71
|
+
codex: "codex",
|
|
72
|
+
};
|
|
73
|
+
/**
|
|
74
|
+
* How long a version string may be, and the reason there is a cap at all.
|
|
75
|
+
*
|
|
76
|
+
* `<binary> --version` is the output of a third-party process, and this value
|
|
77
|
+
* is appended to an append-only log. SPEC.md §11.1 invariant 3 has no exception
|
|
78
|
+
* for provenance: a field that accepted arbitrary bytes is a field where a
|
|
79
|
+
* banner, a stack trace, or the credential quoted inside one arrives and stays
|
|
80
|
+
* forever. So the write boundary takes one line, printable ASCII only, capped.
|
|
81
|
+
*/
|
|
82
|
+
export const HARNESS_VERSION_LIMIT = 64;
|
|
83
|
+
/** Printable ASCII, no control characters, no newline. The schema pins the same. */
|
|
84
|
+
const PRINTABLE = /^[\x20-\x7e]+$/u;
|
|
85
|
+
/**
|
|
86
|
+
* The one spelling of a harness version this runtime ever writes or compares.
|
|
87
|
+
*
|
|
88
|
+
* First line, trimmed, and nothing else: `claude --version` prints
|
|
89
|
+
* `2.0.14 (Claude Code)` and a future release may add a second line of banner.
|
|
90
|
+
* Both the hook (which records) and doctor (which compares) call this, so the
|
|
91
|
+
* comparison is between two values normalized identically — a row that failed
|
|
92
|
+
* because one side kept a trailing newline would be a false alarm, and a false
|
|
93
|
+
* alarm in a health check trains an operator to ignore it.
|
|
94
|
+
*
|
|
95
|
+
* Returns `null` for anything that is not a usable version: empty output, a
|
|
96
|
+
* line that is not printable ASCII, or one longer than the cap. Absence is
|
|
97
|
+
* always available and always honest.
|
|
98
|
+
*/
|
|
99
|
+
export function normalizeHarnessVersion(raw) {
|
|
100
|
+
if (typeof raw !== "string")
|
|
101
|
+
return null;
|
|
102
|
+
const first = raw.split("\n", 1)[0] ?? "";
|
|
103
|
+
const text = first.trim();
|
|
104
|
+
if (text.length === 0 || text.length > HARNESS_VERSION_LIMIT)
|
|
105
|
+
return null;
|
|
106
|
+
return PRINTABLE.test(text) ? text : null;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* How long the probe may take before it is killed and reported as absent.
|
|
110
|
+
*
|
|
111
|
+
* A bound against a HUNG binary, not a latency target: `<binary> --version`
|
|
112
|
+
* answers in milliseconds on any machine that is not already in trouble, and
|
|
113
|
+
* this number is only ever reached by one that is. It is generous for the same
|
|
114
|
+
* reason `cli/gloss.ts`'s is (20s there): a timeout tuned to a healthy machine
|
|
115
|
+
* turns a loaded one into a silent, intermittent absence, and an absence that
|
|
116
|
+
* appears under load is the least useful failure a provenance field could have.
|
|
117
|
+
* The cost is bounded by the fact that this runs only where a record is being
|
|
118
|
+
* written, and at most once per process.
|
|
119
|
+
*/
|
|
120
|
+
export const HARNESS_PROBE_TIMEOUT_MS = 10_000;
|
|
121
|
+
/**
|
|
122
|
+
* Run `<binary> --version` once, uncached, and normalize what came back.
|
|
123
|
+
*
|
|
124
|
+
* Every failure is a value rather than an exception, in the manner of
|
|
125
|
+
* {@link spawnGloss}: a missing binary, a non-zero exit and a timeout kill are
|
|
126
|
+
* all reported on the result object, and all of them are simply "no version".
|
|
127
|
+
*
|
|
128
|
+
* The child is STARVED, for the reason the gloss runner is: this is a
|
|
129
|
+
* third-party CLI spawned by a process that may be holding a bot token and a
|
|
130
|
+
* vault passphrase, and it has no use for either. Nothing is declared, because
|
|
131
|
+
* reading a version is not a granted action.
|
|
132
|
+
*/
|
|
133
|
+
export function probeHarnessVersion(kind) {
|
|
134
|
+
let result;
|
|
135
|
+
try {
|
|
136
|
+
result = spawnSync(HARNESS_BINARY[kind], ["--version"], {
|
|
137
|
+
encoding: "utf8",
|
|
138
|
+
env: childEnvironment().env,
|
|
139
|
+
timeout: HARNESS_PROBE_TIMEOUT_MS,
|
|
140
|
+
killSignal: "SIGKILL",
|
|
141
|
+
// A version is a few dozen bytes. A binary that answers with a megabyte
|
|
142
|
+
// is a binary whose answer is dropped rather than buffered.
|
|
143
|
+
maxBuffer: 64 * 1024,
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
if (result.error !== undefined || result.status !== 0)
|
|
150
|
+
return null;
|
|
151
|
+
return normalizeHarnessVersion(result.stdout);
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* The memo. Holds the FAILURES too: a harness that is not on PATH is not on
|
|
155
|
+
* PATH, and asking a second time in the same process would pay a second spawn
|
|
156
|
+
* for the same `null`.
|
|
157
|
+
*/
|
|
158
|
+
const probed = new Map();
|
|
159
|
+
/**
|
|
160
|
+
* The installed version of `kind`, read at most once per process.
|
|
161
|
+
*
|
|
162
|
+
* This is the only entry point callers should use. `probeHarnessVersion` is
|
|
163
|
+
* exported for the test that proves the memo is a memo.
|
|
164
|
+
*/
|
|
165
|
+
export function installedHarnessVersion(kind) {
|
|
166
|
+
const memo = probed.get(kind);
|
|
167
|
+
if (memo !== undefined)
|
|
168
|
+
return memo;
|
|
169
|
+
const value = probeHarnessVersion(kind);
|
|
170
|
+
probed.set(kind, value);
|
|
171
|
+
return value;
|
|
172
|
+
}
|
|
173
|
+
/** Drop the memo. TEST ONLY: a process reads a version once, by design. */
|
|
174
|
+
export function resetHarnessVersionCache() {
|
|
175
|
+
probed.clear();
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Build the pair for `kind`, preferring the version the hook event supplied.
|
|
179
|
+
*
|
|
180
|
+
* `eventVersion` is whatever the harness put in its own event, unvalidated:
|
|
181
|
+
* it goes through {@link normalizeHarnessVersion} exactly as the probe's
|
|
182
|
+
* output does, because it arrives from the same untrusted side of the boundary
|
|
183
|
+
* and the write boundary does not have two standards.
|
|
184
|
+
*/
|
|
185
|
+
export function harnessProvenance(kind, eventVersion = null) {
|
|
186
|
+
const stated = normalizeHarnessVersion(eventVersion);
|
|
187
|
+
const version = stated ?? installedHarnessVersion(kind);
|
|
188
|
+
return version === null ? null : { harness: kind, harness_version: version };
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* The provenance pair carried by an already-written record's payload, or
|
|
192
|
+
* `null` when it carries none (or carries half of one).
|
|
193
|
+
*
|
|
194
|
+
* The read side of the same contract, used by `approval doctor`. Strict on
|
|
195
|
+
* purpose: a record whose `harness` is a string this runtime does not know is a
|
|
196
|
+
* record from a harness this build cannot probe, so it is not evidence about
|
|
197
|
+
* any binary here and is skipped rather than guessed at.
|
|
198
|
+
*/
|
|
199
|
+
export function readHarnessProvenance(payload) {
|
|
200
|
+
if (typeof payload !== "object" || payload === null || Array.isArray(payload))
|
|
201
|
+
return null;
|
|
202
|
+
const fields = payload;
|
|
203
|
+
const kind = fields["harness"];
|
|
204
|
+
if (!isHarnessKind(kind))
|
|
205
|
+
return null;
|
|
206
|
+
const version = fields["harness_version"];
|
|
207
|
+
if (typeof version !== "string" || normalizeHarnessVersion(version) !== version)
|
|
208
|
+
return null;
|
|
209
|
+
return { harness: kind, harness_version: version };
|
|
210
|
+
}
|
|
211
|
+
//# sourceMappingURL=harness-version.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"harness-version.js","sourceRoot":"","sources":["../../../src/core/harness-version.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoDG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAE/C,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAElD,6DAA6D;AAC7D,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,aAAa,EAAE,QAAQ,EAAE,OAAO,CAAU,CAAC;AAIzE,0DAA0D;AAC1D,MAAM,UAAU,aAAa,CAAC,KAAc;IAC1C,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAK,aAAmC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AAC3F,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,MAAM,cAAc,GAA0C;IACnE,aAAa,EAAE,QAAQ;IACvB,MAAM,EAAE,cAAc;IACtB,KAAK,EAAE,OAAO;CACf,CAAC;AAEF;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,EAAE,CAAC;AAExC,oFAAoF;AACpF,MAAM,SAAS,GAAG,iBAAiB,CAAC;AAEpC;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,uBAAuB,CAAC,GAAY;IAClD,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IACzC,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAC1C,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAC1B,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,CAAC,MAAM,GAAG,qBAAqB;QAAE,OAAO,IAAI,CAAC;IAC1E,OAAO,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;AAC5C,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,MAAM,CAAC;AAE/C;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAiB;IACnD,IAAI,MAAM,CAAC;IACX,IAAI,CAAC;QACH,MAAM,GAAG,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,EAAE,CAAC,WAAW,CAAC,EAAE;YACtD,QAAQ,EAAE,MAAM;YAChB,GAAG,EAAE,gBAAgB,EAAE,CAAC,GAAG;YAC3B,OAAO,EAAE,wBAAwB;YACjC,UAAU,EAAE,SAAS;YACrB,wEAAwE;YACxE,4DAA4D;YAC5D,SAAS,EAAE,EAAE,GAAG,IAAI;SACrB,CAAC,CAAC;IACL,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,MAAM,CAAC,KAAK,KAAK,SAAS,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACnE,OAAO,uBAAuB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;AAChD,CAAC;AAED;;;;GAIG;AACH,MAAM,MAAM,GAAG,IAAI,GAAG,EAA8B,CAAC;AAErD;;;;;GAKG;AACH,MAAM,UAAU,uBAAuB,CAAC,IAAiB;IACvD,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC9B,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IACpC,MAAM,KAAK,GAAG,mBAAmB,CAAC,IAAI,CAAC,CAAC;IACxC,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACxB,OAAO,KAAK,CAAC;AACf,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,wBAAwB;IACtC,MAAM,CAAC,KAAK,EAAE,CAAC;AACjB,CAAC;AAgBD;;;;;;;GAOG;AACH,MAAM,UAAU,iBAAiB,CAC/B,IAAiB,EACjB,YAAY,GAAY,IAAI;IAE5B,MAAM,MAAM,GAAG,uBAAuB,CAAC,YAAY,CAAC,CAAC;IACrD,MAAM,OAAO,GAAG,MAAM,IAAI,uBAAuB,CAAC,IAAI,CAAC,CAAC;IACxD,OAAO,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,eAAe,EAAE,OAAO,EAAE,CAAC;AAC/E,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,qBAAqB,CAAC,OAAgB;IACpD,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC;QAAE,OAAO,IAAI,CAAC;IAC3F,MAAM,MAAM,GAAG,OAAkC,CAAC;IAClD,MAAM,IAAI,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC;IAC/B,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACtC,MAAM,OAAO,GAAG,MAAM,CAAC,iBAAiB,CAAC,CAAC;IAC1C,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,uBAAuB,CAAC,OAAO,CAAC,KAAK,OAAO;QAAE,OAAO,IAAI,CAAC;IAC7F,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,eAAe,EAAE,OAAO,EAAE,CAAC;AACrD,CAAC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How long a harness hook waits, and how long the question it opened outlives
|
|
3
|
+
* that wait (APRV-287).
|
|
4
|
+
*
|
|
5
|
+
* Two numbers with two readers each, which is the whole reason they live here
|
|
6
|
+
* rather than beside either one. `cli/hook.ts` uses them to decide when a
|
|
7
|
+
* request it opened has been abandoned; `cli/channel-telegram.ts` uses the wait
|
|
8
|
+
* to decide which pending requests on a listener's first cycle are stale enough
|
|
9
|
+
* to collapse into one message. Two copies of the same duration would be two
|
|
10
|
+
* answers to "is anybody still waiting on this", and the channel would collapse
|
|
11
|
+
* questions the hook was still holding open, or leave a dead queue on a phone.
|
|
12
|
+
*
|
|
13
|
+
* Nothing here reads a clock, a policy or the log: these are durations, and
|
|
14
|
+
* every judgment made with them is made by a caller that already holds the
|
|
15
|
+
* instant it is judging against.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* The hook's default wait, chosen to sit inside Claude Code's own 60s hook
|
|
19
|
+
* default. The string form is what the flag parser takes; the millisecond form
|
|
20
|
+
* is what a comparison takes.
|
|
21
|
+
*/
|
|
22
|
+
export declare const HOOK_DEFAULT_WAIT = "55s";
|
|
23
|
+
/** {@link HOOK_DEFAULT_WAIT} in milliseconds. */
|
|
24
|
+
export declare const HOOK_DEFAULT_WAIT_MS = 55000;
|
|
25
|
+
/**
|
|
26
|
+
* How long a request outlives the wait that opened it, before the hook takes it
|
|
27
|
+
* back (APRV-287).
|
|
28
|
+
*
|
|
29
|
+
* The window exists for exactly one thing: the retry. A hook whose wait expires
|
|
30
|
+
* leaves its question open because a decision inside the policy's TTL still
|
|
31
|
+
* authorizes an identical retry of the identical command (APRV-117), and the
|
|
32
|
+
* agent that was denied usually retries within a minute or two. So the question
|
|
33
|
+
* stays live for that long and no longer.
|
|
34
|
+
*
|
|
35
|
+
* Past it, nobody is coming back for the answer. The request was accruing on an
|
|
36
|
+
* approver's phone as one more message a restarted listener would re-deliver,
|
|
37
|
+
* and on 2026-09-06 a dozen of those arrived at once behind a dead daemon. A
|
|
38
|
+
* request nothing will adopt is withdrawn (reason `timeout`), which authorizes
|
|
39
|
+
* nothing, refuses a late tap in the approver's own words, and leaves the log
|
|
40
|
+
* saying exactly what happened: the asker gave up.
|
|
41
|
+
*
|
|
42
|
+
* Five minutes because it is longer than any retry loop this runtime has been
|
|
43
|
+
* observed to take and shorter than the shortest TTL an operator is likely to
|
|
44
|
+
* write. It is a default: `--retry-grace` moves it per invocation, and `0`
|
|
45
|
+
* withdraws at the moment the wait expires.
|
|
46
|
+
*/
|
|
47
|
+
export declare const HOOK_RETRY_GRACE_MS: number;
|
|
48
|
+
/**
|
|
49
|
+
* How old a request opened under `waitMs` must be before the grace has run out.
|
|
50
|
+
*
|
|
51
|
+
* Measured from the `approval.requested` record's own timestamp, which is the
|
|
52
|
+
* instant the runtime wrote at the write boundary, and never from anything a
|
|
53
|
+
* caller states about when it started waiting.
|
|
54
|
+
*/
|
|
55
|
+
export declare function abandonedAfterMs(waitMs: number, graceMs: number): number;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How long a harness hook waits, and how long the question it opened outlives
|
|
3
|
+
* that wait (APRV-287).
|
|
4
|
+
*
|
|
5
|
+
* Two numbers with two readers each, which is the whole reason they live here
|
|
6
|
+
* rather than beside either one. `cli/hook.ts` uses them to decide when a
|
|
7
|
+
* request it opened has been abandoned; `cli/channel-telegram.ts` uses the wait
|
|
8
|
+
* to decide which pending requests on a listener's first cycle are stale enough
|
|
9
|
+
* to collapse into one message. Two copies of the same duration would be two
|
|
10
|
+
* answers to "is anybody still waiting on this", and the channel would collapse
|
|
11
|
+
* questions the hook was still holding open, or leave a dead queue on a phone.
|
|
12
|
+
*
|
|
13
|
+
* Nothing here reads a clock, a policy or the log: these are durations, and
|
|
14
|
+
* every judgment made with them is made by a caller that already holds the
|
|
15
|
+
* instant it is judging against.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* The hook's default wait, chosen to sit inside Claude Code's own 60s hook
|
|
19
|
+
* default. The string form is what the flag parser takes; the millisecond form
|
|
20
|
+
* is what a comparison takes.
|
|
21
|
+
*/
|
|
22
|
+
export const HOOK_DEFAULT_WAIT = "55s";
|
|
23
|
+
/** {@link HOOK_DEFAULT_WAIT} in milliseconds. */
|
|
24
|
+
export const HOOK_DEFAULT_WAIT_MS = 55_000;
|
|
25
|
+
/**
|
|
26
|
+
* How long a request outlives the wait that opened it, before the hook takes it
|
|
27
|
+
* back (APRV-287).
|
|
28
|
+
*
|
|
29
|
+
* The window exists for exactly one thing: the retry. A hook whose wait expires
|
|
30
|
+
* leaves its question open because a decision inside the policy's TTL still
|
|
31
|
+
* authorizes an identical retry of the identical command (APRV-117), and the
|
|
32
|
+
* agent that was denied usually retries within a minute or two. So the question
|
|
33
|
+
* stays live for that long and no longer.
|
|
34
|
+
*
|
|
35
|
+
* Past it, nobody is coming back for the answer. The request was accruing on an
|
|
36
|
+
* approver's phone as one more message a restarted listener would re-deliver,
|
|
37
|
+
* and on 2026-09-06 a dozen of those arrived at once behind a dead daemon. A
|
|
38
|
+
* request nothing will adopt is withdrawn (reason `timeout`), which authorizes
|
|
39
|
+
* nothing, refuses a late tap in the approver's own words, and leaves the log
|
|
40
|
+
* saying exactly what happened: the asker gave up.
|
|
41
|
+
*
|
|
42
|
+
* Five minutes because it is longer than any retry loop this runtime has been
|
|
43
|
+
* observed to take and shorter than the shortest TTL an operator is likely to
|
|
44
|
+
* write. It is a default: `--retry-grace` moves it per invocation, and `0`
|
|
45
|
+
* withdraws at the moment the wait expires.
|
|
46
|
+
*/
|
|
47
|
+
export const HOOK_RETRY_GRACE_MS = 5 * 60_000;
|
|
48
|
+
/**
|
|
49
|
+
* How old a request opened under `waitMs` must be before the grace has run out.
|
|
50
|
+
*
|
|
51
|
+
* Measured from the `approval.requested` record's own timestamp, which is the
|
|
52
|
+
* instant the runtime wrote at the write boundary, and never from anything a
|
|
53
|
+
* caller states about when it started waiting.
|
|
54
|
+
*/
|
|
55
|
+
export function abandonedAfterMs(waitMs, graceMs) {
|
|
56
|
+
return Math.max(0, waitMs) + Math.max(0, graceMs);
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=harness-wait.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"harness-wait.js","sourceRoot":"","sources":["../../../src/core/harness-wait.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH;;;;GAIG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,KAAK,CAAC;AAEvC,iDAAiD;AACjD,MAAM,CAAC,MAAM,oBAAoB,GAAG,MAAM,CAAC;AAE3C;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,GAAG,MAAM,CAAC;AAE9C;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAAc,EAAE,OAAe;IAC9D,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;AACpD,CAAC"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The bounded head-moved retry, in one place (APRV-150, APRV-236).
|
|
3
|
+
*
|
|
4
|
+
* ## The fact this module is about
|
|
5
|
+
*
|
|
6
|
+
* `core/log.ts` takes a compare-and-append precondition: a writer states the
|
|
7
|
+
* `(seq, hash)` its checks were made against, and the append is refused under
|
|
8
|
+
* the lock when the tail is something else. That refusal is `append-failed`
|
|
9
|
+
* with `append.code === "head-moved"`, and it is correct: every read-dependent
|
|
10
|
+
* check that authorized the write is stale.
|
|
11
|
+
*
|
|
12
|
+
* Stale is not the same as wrong. A moved head says the verdict must be
|
|
13
|
+
* computed again; it does not say what the verdict is. Treating the two as the
|
|
14
|
+
* same is what turned a busy log into a lottery, twice:
|
|
15
|
+
*
|
|
16
|
+
* - 2026-08-29 (APRV-150): a session's first `git status` — class `read.shell`,
|
|
17
|
+
* autonomous, no human anywhere near it — was denied by the Claude Code hook
|
|
18
|
+
* because an unrelated record landed a few milliseconds earlier.
|
|
19
|
+
* - 2026-09-02 (APRV-236): `approval grant` refused a human's decision with
|
|
20
|
+
* `head moved: expected seq 14218, found 14219` while two lanes and the
|
|
21
|
+
* daemon were appending. The human ran it again. Twice more.
|
|
22
|
+
*
|
|
23
|
+
* The second one is the worse of the two, because the caller it handed the
|
|
24
|
+
* retry to was a person. A compare-and-append refusal is a fact about timing
|
|
25
|
+
* rather than about authority, and asking a human to retype is the wrong party
|
|
26
|
+
* to hand it to.
|
|
27
|
+
*
|
|
28
|
+
* ## What a retry is here, and what it is not
|
|
29
|
+
*
|
|
30
|
+
* The unit of retry is the WHOLE read-check-append cycle: a new read of the
|
|
31
|
+
* verified log, a new read of the policy, a fresh attestation, a fresh
|
|
32
|
+
* derivation of the request's state, fresh escalation, single-use, intake and
|
|
33
|
+
* budget checks, and a new append against the head that the new read observed.
|
|
34
|
+
* Nothing crosses an attempt except the caller's own inputs.
|
|
35
|
+
*
|
|
36
|
+
* The append is never retried on its own. That distinction is the whole safety
|
|
37
|
+
* argument, and it is worth stating as three properties:
|
|
38
|
+
*
|
|
39
|
+
* - **compare-and-append is untouched.** Every attempt supplies the head it
|
|
40
|
+
* read, and a stale write is still refused under the lock. SPEC.md §11.1
|
|
41
|
+
* invariant 5 holds per attempt, which is where it has to hold.
|
|
42
|
+
* - **A changed verdict is the new verdict.** If the record that moved the head
|
|
43
|
+
* decided the request, spent the key, exhausted the budget, lapsed the TTL or
|
|
44
|
+
* re-attested a different policy, the next attempt derives that and refuses
|
|
45
|
+
* it with the code those fresh facts produce — `already-decided`,
|
|
46
|
+
* `request-withdrawn`, `already-executed`, `budget-exceeded`, `expired`,
|
|
47
|
+
* `policy-drift` — never `append-failed`. A retry cannot launder a denial
|
|
48
|
+
* into an allow, because it never replays the earlier conclusion.
|
|
49
|
+
* - **Only `head-moved` retries.** Every other refusal, a real verdict included,
|
|
50
|
+
* is returned on the first attempt. So is a lock timeout, a corrupt log and a
|
|
51
|
+
* schema refusal at the write boundary: retrying those would be either
|
|
52
|
+
* pointless or a second write.
|
|
53
|
+
*
|
|
54
|
+
* The bound is what keeps a busy log from turning one call into an unbounded
|
|
55
|
+
* write loop. When it is spent the last `head-moved` refusal is returned with
|
|
56
|
+
* its code and its `append` error unchanged, and with the attempt count added to
|
|
57
|
+
* its message so a reader can tell one lost race from a log under sustained
|
|
58
|
+
* contention. The caller fails closed on it exactly as it always did.
|
|
59
|
+
*/
|
|
60
|
+
import type { AppendError } from "./log.js";
|
|
61
|
+
/**
|
|
62
|
+
* How many times a gate writer re-derives its verdict against a moved head
|
|
63
|
+
* before it gives up. Small, fixed, and not configurable upward.
|
|
64
|
+
*
|
|
65
|
+
* APRV-150 chose three for the hook's writers; APRV-236 gives the same three to
|
|
66
|
+
* every other writer a human or a session drives, rather than inventing a
|
|
67
|
+
* second number for the same fact.
|
|
68
|
+
*/
|
|
69
|
+
export declare const HEAD_MOVED_ATTEMPTS = 3;
|
|
70
|
+
/**
|
|
71
|
+
* The shape this module needs of a writer's result: an `ok` discriminator, and
|
|
72
|
+
* on the refusal side a code and the append error beneath it.
|
|
73
|
+
*
|
|
74
|
+
* Deliberately structural. `GateRefusal`, `ExecuteRefusal`, `TokenRefusal` and
|
|
75
|
+
* `GateWindowRefusal` are four frozen unions in four modules that must not
|
|
76
|
+
* learn about each other; what they share is this, and this is all the helper
|
|
77
|
+
* reads.
|
|
78
|
+
*/
|
|
79
|
+
export interface HeadRetryable {
|
|
80
|
+
ok: boolean;
|
|
81
|
+
code?: string;
|
|
82
|
+
message?: string;
|
|
83
|
+
append?: AppendError;
|
|
84
|
+
}
|
|
85
|
+
/** Did this refusal come from the compare-and-append precondition alone? */
|
|
86
|
+
export declare function isHeadMoved(result: HeadRetryable): boolean;
|
|
87
|
+
/**
|
|
88
|
+
* The attempt budget for one operation: `ceiling` unless the caller asked for
|
|
89
|
+
* fewer.
|
|
90
|
+
*
|
|
91
|
+
* Clamped rather than trusted, and clamped in the one direction that matters: a
|
|
92
|
+
* caller may ask for LESS tolerance of a moved head (a test pinning the
|
|
93
|
+
* unretried behaviour, a caller that would rather fail fast), never for more.
|
|
94
|
+
* Ambiguity — a non-integer, a zero, a negative — resolves to the value the
|
|
95
|
+
* runtime chose, not the caller's.
|
|
96
|
+
*/
|
|
97
|
+
export declare function attemptsOf(asked: number | undefined, ceiling?: number): number;
|
|
98
|
+
/**
|
|
99
|
+
* Run one whole read-check-append cycle, and run it again from the top while it
|
|
100
|
+
* loses the race, up to `attempts` times.
|
|
101
|
+
*
|
|
102
|
+
* `cycle` MUST be the entire operation: it re-reads, re-runs every check on the
|
|
103
|
+
* head it just read, and returns either the append it attempted or the refusal
|
|
104
|
+
* those fresh facts produced. A `cycle` that closes over a stale read would
|
|
105
|
+
* defeat the only property that makes this safe.
|
|
106
|
+
*/
|
|
107
|
+
export declare function withHeadRetry<T extends HeadRetryable>(attempts: number, cycle: () => T): T;
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The bounded head-moved retry, in one place (APRV-150, APRV-236).
|
|
3
|
+
*
|
|
4
|
+
* ## The fact this module is about
|
|
5
|
+
*
|
|
6
|
+
* `core/log.ts` takes a compare-and-append precondition: a writer states the
|
|
7
|
+
* `(seq, hash)` its checks were made against, and the append is refused under
|
|
8
|
+
* the lock when the tail is something else. That refusal is `append-failed`
|
|
9
|
+
* with `append.code === "head-moved"`, and it is correct: every read-dependent
|
|
10
|
+
* check that authorized the write is stale.
|
|
11
|
+
*
|
|
12
|
+
* Stale is not the same as wrong. A moved head says the verdict must be
|
|
13
|
+
* computed again; it does not say what the verdict is. Treating the two as the
|
|
14
|
+
* same is what turned a busy log into a lottery, twice:
|
|
15
|
+
*
|
|
16
|
+
* - 2026-08-29 (APRV-150): a session's first `git status` — class `read.shell`,
|
|
17
|
+
* autonomous, no human anywhere near it — was denied by the Claude Code hook
|
|
18
|
+
* because an unrelated record landed a few milliseconds earlier.
|
|
19
|
+
* - 2026-09-02 (APRV-236): `approval grant` refused a human's decision with
|
|
20
|
+
* `head moved: expected seq 14218, found 14219` while two lanes and the
|
|
21
|
+
* daemon were appending. The human ran it again. Twice more.
|
|
22
|
+
*
|
|
23
|
+
* The second one is the worse of the two, because the caller it handed the
|
|
24
|
+
* retry to was a person. A compare-and-append refusal is a fact about timing
|
|
25
|
+
* rather than about authority, and asking a human to retype is the wrong party
|
|
26
|
+
* to hand it to.
|
|
27
|
+
*
|
|
28
|
+
* ## What a retry is here, and what it is not
|
|
29
|
+
*
|
|
30
|
+
* The unit of retry is the WHOLE read-check-append cycle: a new read of the
|
|
31
|
+
* verified log, a new read of the policy, a fresh attestation, a fresh
|
|
32
|
+
* derivation of the request's state, fresh escalation, single-use, intake and
|
|
33
|
+
* budget checks, and a new append against the head that the new read observed.
|
|
34
|
+
* Nothing crosses an attempt except the caller's own inputs.
|
|
35
|
+
*
|
|
36
|
+
* The append is never retried on its own. That distinction is the whole safety
|
|
37
|
+
* argument, and it is worth stating as three properties:
|
|
38
|
+
*
|
|
39
|
+
* - **compare-and-append is untouched.** Every attempt supplies the head it
|
|
40
|
+
* read, and a stale write is still refused under the lock. SPEC.md §11.1
|
|
41
|
+
* invariant 5 holds per attempt, which is where it has to hold.
|
|
42
|
+
* - **A changed verdict is the new verdict.** If the record that moved the head
|
|
43
|
+
* decided the request, spent the key, exhausted the budget, lapsed the TTL or
|
|
44
|
+
* re-attested a different policy, the next attempt derives that and refuses
|
|
45
|
+
* it with the code those fresh facts produce — `already-decided`,
|
|
46
|
+
* `request-withdrawn`, `already-executed`, `budget-exceeded`, `expired`,
|
|
47
|
+
* `policy-drift` — never `append-failed`. A retry cannot launder a denial
|
|
48
|
+
* into an allow, because it never replays the earlier conclusion.
|
|
49
|
+
* - **Only `head-moved` retries.** Every other refusal, a real verdict included,
|
|
50
|
+
* is returned on the first attempt. So is a lock timeout, a corrupt log and a
|
|
51
|
+
* schema refusal at the write boundary: retrying those would be either
|
|
52
|
+
* pointless or a second write.
|
|
53
|
+
*
|
|
54
|
+
* The bound is what keeps a busy log from turning one call into an unbounded
|
|
55
|
+
* write loop. When it is spent the last `head-moved` refusal is returned with
|
|
56
|
+
* its code and its `append` error unchanged, and with the attempt count added to
|
|
57
|
+
* its message so a reader can tell one lost race from a log under sustained
|
|
58
|
+
* contention. The caller fails closed on it exactly as it always did.
|
|
59
|
+
*/
|
|
60
|
+
/**
|
|
61
|
+
* How many times a gate writer re-derives its verdict against a moved head
|
|
62
|
+
* before it gives up. Small, fixed, and not configurable upward.
|
|
63
|
+
*
|
|
64
|
+
* APRV-150 chose three for the hook's writers; APRV-236 gives the same three to
|
|
65
|
+
* every other writer a human or a session drives, rather than inventing a
|
|
66
|
+
* second number for the same fact.
|
|
67
|
+
*/
|
|
68
|
+
export const HEAD_MOVED_ATTEMPTS = 3;
|
|
69
|
+
/** Did this refusal come from the compare-and-append precondition alone? */
|
|
70
|
+
export function isHeadMoved(result) {
|
|
71
|
+
return !result.ok && result.code === "append-failed" && result.append?.code === "head-moved";
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* The attempt budget for one operation: `ceiling` unless the caller asked for
|
|
75
|
+
* fewer.
|
|
76
|
+
*
|
|
77
|
+
* Clamped rather than trusted, and clamped in the one direction that matters: a
|
|
78
|
+
* caller may ask for LESS tolerance of a moved head (a test pinning the
|
|
79
|
+
* unretried behaviour, a caller that would rather fail fast), never for more.
|
|
80
|
+
* Ambiguity — a non-integer, a zero, a negative — resolves to the value the
|
|
81
|
+
* runtime chose, not the caller's.
|
|
82
|
+
*/
|
|
83
|
+
export function attemptsOf(asked, ceiling = HEAD_MOVED_ATTEMPTS) {
|
|
84
|
+
if (asked === undefined || !Number.isInteger(asked) || asked < 1)
|
|
85
|
+
return ceiling;
|
|
86
|
+
return Math.min(asked, ceiling);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Run one whole read-check-append cycle, and run it again from the top while it
|
|
90
|
+
* loses the race, up to `attempts` times.
|
|
91
|
+
*
|
|
92
|
+
* `cycle` MUST be the entire operation: it re-reads, re-runs every check on the
|
|
93
|
+
* head it just read, and returns either the append it attempted or the refusal
|
|
94
|
+
* those fresh facts produced. A `cycle` that closes over a stale read would
|
|
95
|
+
* defeat the only property that makes this safe.
|
|
96
|
+
*/
|
|
97
|
+
export function withHeadRetry(attempts, cycle) {
|
|
98
|
+
let result = cycle();
|
|
99
|
+
let made = 1;
|
|
100
|
+
for (; made < attempts && isHeadMoved(result); made += 1) {
|
|
101
|
+
result = cycle();
|
|
102
|
+
}
|
|
103
|
+
return isHeadMoved(result) ? exhausted(result, made) : result;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* The refusal handed back once the bound is spent.
|
|
107
|
+
*
|
|
108
|
+
* The code stays `append-failed` and the `append` error stays the writer's own,
|
|
109
|
+
* because that is what a caller branches on and it is still the true reason.
|
|
110
|
+
* What is added is the count, so the message says how hard the writer tried:
|
|
111
|
+
* one lost race and a log under sustained contention are different operational
|
|
112
|
+
* facts, and a reader who cannot tell them apart cannot act on either.
|
|
113
|
+
*/
|
|
114
|
+
function exhausted(result, attempts) {
|
|
115
|
+
const note = `; ${String(attempts)} attempt${attempts === 1 ? " was" : "s were"} made, each a fresh read, fresh checks and a fresh compare-and-append, and the head had moved again every time. Nothing was appended.`;
|
|
116
|
+
// A copy, never a mutation: the caller's refusal objects are values, and the
|
|
117
|
+
// only field that changes is the human-readable one. The cast restores `T`
|
|
118
|
+
// after the spread, which widens `message` to `string | undefined`.
|
|
119
|
+
return { ...result, message: `${result.message ?? "the head moved"}${note}` };
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=head-retry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"head-retry.js","sourceRoot":"","sources":["../../../src/core/head-retry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0DG;AAIH;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC;AAkBrC,4EAA4E;AAC5E,MAAM,UAAU,WAAW,CAAC,MAAqB;IAC/C,OAAO,CAAC,MAAM,CAAC,EAAE,IAAI,MAAM,CAAC,IAAI,KAAK,eAAe,IAAI,MAAM,CAAC,MAAM,EAAE,IAAI,KAAK,YAAY,CAAC;AAC/F,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,UAAU,CAAC,KAAyB,EAAE,OAAO,GAAG,mBAAmB;IACjF,IAAI,KAAK,KAAK,SAAS,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC;QAAE,OAAO,OAAO,CAAC;IACjF,OAAO,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;AAClC,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,aAAa,CAA0B,QAAgB,EAAE,KAAc;IACrF,IAAI,MAAM,GAAG,KAAK,EAAE,CAAC;IACrB,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,OAAO,IAAI,GAAG,QAAQ,IAAI,WAAW,CAAC,MAAM,CAAC,EAAE,IAAI,IAAI,CAAC,EAAE,CAAC;QACzD,MAAM,GAAG,KAAK,EAAE,CAAC;IACnB,CAAC;IACD,OAAO,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;AAChE,CAAC;AAED;;;;;;;;GAQG;AACH,SAAS,SAAS,CAA0B,MAAS,EAAE,QAAgB;IACrE,MAAM,IAAI,GAAG,KAAK,MAAM,CAAC,QAAQ,CAAC,WAChC,QAAQ,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAC5B,uIAAuI,CAAC;IACxI,6EAA6E;IAC7E,2EAA2E;IAC3E,oEAAoE;IACpE,OAAO,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,CAAC,OAAO,IAAI,gBAAgB,GAAG,IAAI,EAAE,EAAO,CAAC;AACrF,CAAC"}
|