approval-md 0.1.0 → 0.3.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/README.md +629 -559
- package/SPEC.md +99 -24
- package/dist/src/adapters/agentmail.d.ts +426 -0
- package/dist/src/adapters/agentmail.js +2 -2
- package/dist/src/adapters/conformance.d.ts +149 -0
- package/dist/src/adapters/contract.d.ts +628 -0
- package/dist/src/adapters/contract.js +110 -16
- package/dist/src/adapters/contract.js.map +1 -1
- package/dist/src/adapters/email.d.ts +324 -0
- package/dist/src/adapters/env-passphrase.d.ts +93 -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 +2 -1
- package/dist/src/adapters/registry.js.map +1 -1
- package/dist/src/adapters/smtp.d.ts +213 -0
- package/dist/src/adapters/vault-provider.d.ts +114 -0
- package/dist/src/adapters/vault-provider.js +3 -3
- 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/cli.d.ts +193 -0
- package/dist/src/channels/conformance.d.ts +92 -0
- package/dist/src/channels/contract.d.ts +656 -0
- package/dist/src/channels/contract.js +200 -7
- package/dist/src/channels/contract.js.map +1 -1
- package/dist/src/channels/payload-view.d.ts +35 -0
- package/dist/src/channels/render-queue.d.ts +149 -0
- package/dist/src/channels/tagging.d.ts +196 -0
- package/dist/src/channels/telegram.d.ts +1944 -0
- package/dist/src/channels/telegram.js +218 -23
- package/dist/src/channels/telegram.js.map +1 -1
- package/dist/src/channels/web.d.ts +350 -0
- package/dist/src/channels/web.js +17 -0
- package/dist/src/channels/web.js.map +1 -1
- package/dist/src/cli/adapter.d.ts +90 -0
- package/dist/src/cli/adapter.js +25 -15
- package/dist/src/cli/adapter.js.map +1 -1
- package/dist/src/cli/amend.d.ts +59 -0
- package/dist/src/cli/amend.js +214 -30
- package/dist/src/cli/amend.js.map +1 -1
- package/dist/src/cli/args.d.ts +43 -0
- package/dist/src/cli/attest.d.ts +50 -0
- package/dist/src/cli/attest.js +134 -7
- package/dist/src/cli/attest.js.map +1 -1
- package/dist/src/cli/audit-card.d.ts +62 -0
- package/dist/src/cli/audit.d.ts +59 -0
- package/dist/src/cli/channel-telegram.d.ts +879 -0
- package/dist/src/cli/channel-telegram.js +311 -13
- package/dist/src/cli/channel-telegram.js.map +1 -1
- package/dist/src/cli/channel-web.d.ts +131 -0
- package/dist/src/cli/channel.d.ts +80 -0
- package/dist/src/cli/channel.js +9 -0
- package/dist/src/cli/channel.js.map +1 -1
- package/dist/src/cli/checkpoint-tap.d.ts +169 -0
- package/dist/src/cli/codex-bridge.d.ts +819 -0
- package/dist/src/cli/codex-bridge.js +1607 -0
- package/dist/src/cli/codex-bridge.js.map +1 -0
- package/dist/src/cli/codex.d.ts +2 -0
- package/dist/src/cli/codex.js +469 -0
- package/dist/src/cli/codex.js.map +1 -0
- package/dist/src/cli/coverage.d.ts +61 -0
- package/dist/src/cli/daemon.d.ts +120 -0
- package/dist/src/cli/daemon.js +4 -1
- package/dist/src/cli/daemon.js.map +1 -1
- package/dist/src/cli/doctor.d.ts +129 -0
- package/dist/src/cli/doctor.js +586 -17
- package/dist/src/cli/doctor.js.map +1 -1
- package/dist/src/cli/env.d.ts +65 -0
- package/dist/src/cli/execute.d.ts +202 -0
- package/dist/src/cli/execute.js +25 -2
- package/dist/src/cli/execute.js.map +1 -1
- package/dist/src/cli/exit-codes.d.ts +73 -0
- package/dist/src/cli/feedback.d.ts +60 -0
- package/dist/src/cli/gate-window.d.ts +40 -0
- package/dist/src/cli/gate.d.ts +68 -0
- package/dist/src/cli/git-scope.d.ts +190 -0
- package/dist/src/cli/gloss-attach.d.ts +85 -0
- package/dist/src/cli/gloss-codex-child.d.ts +9 -0
- package/dist/src/cli/gloss-codex.d.ts +24 -0
- package/dist/src/cli/gloss-options.d.ts +42 -0
- package/dist/src/cli/gloss.d.ts +265 -0
- package/dist/src/cli/help.d.ts +107 -0
- package/dist/src/cli/help.js +320 -93
- package/dist/src/cli/help.js.map +1 -1
- package/dist/src/cli/hook-codex.d.ts +126 -0
- package/dist/src/cli/hook-codex.js +226 -0
- package/dist/src/cli/hook-codex.js.map +1 -0
- package/dist/src/cli/hook.d.ts +787 -0
- package/dist/src/cli/hook.js +1235 -181
- package/dist/src/cli/hook.js.map +1 -1
- package/dist/src/cli/import.d.ts +35 -0
- package/dist/src/cli/import.js +1 -1
- package/dist/src/cli/import.js.map +1 -1
- package/dist/src/cli/init.d.ts +84 -0
- package/dist/src/cli/init.js +2 -2
- package/dist/src/cli/init.js.map +1 -1
- package/dist/src/cli/instructions.d.ts +23 -0
- package/dist/src/cli/journal.d.ts +41 -0
- package/dist/src/cli/log-advance.d.ts +287 -0
- package/dist/src/cli/log-advance.js +102 -11
- package/dist/src/cli/log-advance.js.map +1 -1
- package/dist/src/cli/log-anchor.d.ts +176 -0
- package/dist/src/cli/log-checkpoint.d.ts +22 -0
- package/dist/src/cli/log-sync.d.ts +243 -0
- package/dist/src/cli/log-verbs.d.ts +16 -0
- package/dist/src/cli/log-verbs.js +7 -1
- package/dist/src/cli/log-verbs.js.map +1 -1
- package/dist/src/cli/long-help.d.ts +70 -0
- package/dist/src/cli/main.d.ts +77 -0
- package/dist/src/cli/main.js +159 -7
- package/dist/src/cli/main.js.map +1 -1
- package/dist/src/cli/mcp.d.ts +52 -0
- package/dist/src/cli/paths.d.ts +56 -0
- package/dist/src/cli/payload.d.ts +58 -0
- package/dist/src/cli/policy-apply.d.ts +195 -0
- package/dist/src/cli/policy-apply.js +573 -0
- package/dist/src/cli/policy-apply.js.map +1 -0
- package/dist/src/cli/policy.d.ts +43 -0
- package/dist/src/cli/policy.js +14 -1
- package/dist/src/cli/policy.js.map +1 -1
- package/dist/src/cli/preflight.d.ts +501 -0
- package/dist/src/cli/preflight.js +689 -45
- package/dist/src/cli/preflight.js.map +1 -1
- package/dist/src/cli/progress.d.ts +78 -0
- package/dist/src/cli/prompt.d.ts +209 -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/render.d.ts +22 -0
- package/dist/src/cli/sandbox.d.ts +51 -0
- package/dist/src/cli/sandbox.js +17 -1
- package/dist/src/cli/sandbox.js.map +1 -1
- package/dist/src/cli/scaffold.d.ts +79 -0
- package/dist/src/cli/scaffold.js +1 -1
- package/dist/src/cli/setup-adapter.d.ts +137 -0
- package/dist/src/cli/setup-adapter.js +38 -4
- package/dist/src/cli/setup-adapter.js.map +1 -1
- package/dist/src/cli/setup-channel.d.ts +126 -0
- package/dist/src/cli/setup-channel.js +28 -1
- package/dist/src/cli/setup-channel.js.map +1 -1
- package/dist/src/cli/setup-checkpoint.d.ts +57 -0
- package/dist/src/cli/setup-common.d.ts +277 -0
- package/dist/src/cli/setup-common.js +3 -2
- package/dist/src/cli/setup-common.js.map +1 -1
- package/dist/src/cli/setup-flow.d.ts +287 -0
- package/dist/src/cli/setup-service.d.ts +96 -0
- package/dist/src/cli/setup.d.ts +204 -0
- package/dist/src/cli/setup.js +94 -2
- package/dist/src/cli/setup.js.map +1 -1
- package/dist/src/cli/style.d.ts +320 -0
- package/dist/src/cli/token.d.ts +39 -0
- package/dist/src/cli/up.d.ts +155 -0
- package/dist/src/cli/up.js +119 -53
- package/dist/src/cli/up.js.map +1 -1
- package/dist/src/cli/usage.d.ts +37 -0
- package/dist/src/cli/values.d.ts +40 -0
- package/dist/src/cli/values.js +3 -4
- package/dist/src/cli/values.js.map +1 -1
- package/dist/src/cli/vault.d.ts +59 -0
- package/dist/src/cli/vault.js +2 -2
- package/dist/src/cli/vault.js.map +1 -1
- package/dist/src/cli/verb-registry.d.ts +76 -0
- package/dist/src/cli/verb-registry.js +344 -11
- package/dist/src/cli/verb-registry.js.map +1 -1
- package/dist/src/cli/wordmark.d.ts +31 -0
- package/dist/src/cli/wordmark.js +2 -2
- package/dist/src/codex/broker.d.ts +229 -0
- package/dist/src/codex/broker.js +548 -0
- package/dist/src/codex/broker.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/runner.d.ts +178 -0
- package/dist/src/codex/runner.js +231 -0
- package/dist/src/codex/runner.js.map +1 -0
- package/dist/src/codex/serve.d.ts +56 -0
- package/dist/src/codex/serve.js +98 -0
- package/dist/src/codex/serve.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-commit.d.ts +219 -0
- package/dist/src/codex/workspace-commit.js +549 -0
- package/dist/src/codex/workspace-commit.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 +221 -0
- package/dist/src/core/advance-cycle.js +66 -2
- package/dist/src/core/advance-cycle.js.map +1 -1
- package/dist/src/core/agents-md.d.ts +278 -0
- package/dist/src/core/agents-md.js +33 -31
- package/dist/src/core/agents-md.js.map +1 -1
- 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 +635 -0
- package/dist/src/core/attest.js +326 -4
- package/dist/src/core/attest.js.map +1 -1
- package/dist/src/core/audit.d.ts +510 -0
- package/dist/src/core/audit.js +13 -0
- package/dist/src/core/audit.js.map +1 -1
- package/dist/src/core/budgets.d.ts +238 -0
- package/dist/src/core/channel-owner.d.ts +213 -0
- package/dist/src/core/channel-owner.js +358 -0
- package/dist/src/core/channel-owner.js.map +1 -0
- package/dist/src/core/checkpoint.d.ts +500 -0
- package/dist/src/core/child-env.d.ts +88 -0
- package/dist/src/core/clock.d.ts +52 -0
- package/dist/src/core/command-class.d.ts +697 -0
- package/dist/src/core/command-class.js +713 -25
- package/dist/src/core/command-class.js.map +1 -1
- package/dist/src/core/commit-guard.d.ts +272 -0
- package/dist/src/core/commit-guard.js +424 -0
- package/dist/src/core/commit-guard.js.map +1 -0
- package/dist/src/core/coverage-sources/adapter.d.ts +40 -0
- package/dist/src/core/coverage-sources/gh.d.ts +48 -0
- package/dist/src/core/coverage-sources/git.d.ts +101 -0
- package/dist/src/core/coverage.d.ts +217 -0
- package/dist/src/core/credential-spec.d.ts +72 -0
- package/dist/src/core/daemon-actor.d.ts +45 -0
- package/dist/src/core/daemon-actor.js +54 -0
- package/dist/src/core/daemon-actor.js.map +1 -0
- package/dist/src/core/dark-session.d.ts +432 -0
- package/dist/src/core/dark-session.js +266 -82
- package/dist/src/core/dark-session.js.map +1 -1
- package/dist/src/core/decision-refusal.d.ts +206 -0
- package/dist/src/core/decision-refusal.js +24 -2
- package/dist/src/core/decision-refusal.js.map +1 -1
- package/dist/src/core/env-file.d.ts +455 -0
- package/dist/src/core/env-file.js +60 -1
- package/dist/src/core/env-file.js.map +1 -1
- package/dist/src/core/execute.d.ts +871 -0
- package/dist/src/core/execute.js +59 -8
- package/dist/src/core/execute.js.map +1 -1
- package/dist/src/core/frontmatter.d.ts +78 -0
- package/dist/src/core/gate-window.d.ts +312 -0
- package/dist/src/core/gate.d.ts +1449 -0
- package/dist/src/core/gate.js +149 -14
- package/dist/src/core/gate.js.map +1 -1
- package/dist/src/core/gesture-refusal.d.ts +166 -0
- package/dist/src/core/gesture-refusal.js +188 -0
- package/dist/src/core/gesture-refusal.js.map +1 -0
- package/dist/src/core/git-run.d.ts +73 -0
- package/dist/src/core/harness-version.d.ts +157 -0
- package/dist/src/core/harness-version.js +4 -1
- package/dist/src/core/harness-version.js.map +1 -1
- package/dist/src/core/harness-wait.d.ts +55 -0
- package/dist/src/core/head-retry.d.ts +107 -0
- package/dist/src/core/instance.d.ts +310 -0
- package/dist/src/core/instance.js +113 -0
- package/dist/src/core/instance.js.map +1 -1
- package/dist/src/core/intake-limits.d.ts +247 -0
- package/dist/src/core/jcs.d.ts +52 -0
- package/dist/src/core/journal.d.ts +144 -0
- package/dist/src/core/live-draw.d.ts +436 -0
- package/dist/src/core/log-reconcile.d.ts +89 -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 +316 -0
- package/dist/src/core/log.js.map +1 -1
- package/dist/src/core/loop.d.ts +274 -0
- package/dist/src/core/loop.js +11 -0
- package/dist/src/core/loop.js.map +1 -1
- package/dist/src/core/md-fence.d.ts +41 -0
- package/dist/src/core/money.d.ts +147 -0
- package/dist/src/core/payload-census.d.ts +74 -0
- package/dist/src/core/payload-store.d.ts +175 -0
- package/dist/src/core/payload.d.ts +71 -0
- package/dist/src/core/policy-diff.d.ts +292 -0
- package/dist/src/core/policy-diff.js +27 -4
- package/dist/src/core/policy-diff.js.map +1 -1
- package/dist/src/core/policy-expectations.d.ts +199 -0
- package/dist/src/core/policy-explain.d.ts +160 -0
- package/dist/src/core/policy-explain.js +63 -3
- package/dist/src/core/policy-explain.js.map +1 -1
- package/dist/src/core/policy-load.d.ts +567 -0
- package/dist/src/core/policy-load.js +36 -6
- package/dist/src/core/policy-load.js.map +1 -1
- package/dist/src/core/policy-match.d.ts +324 -0
- package/dist/src/core/policy-match.js +72 -9
- package/dist/src/core/policy-match.js.map +1 -1
- package/dist/src/core/policy-proposal.d.ts +317 -0
- package/dist/src/core/policy-proposal.js +102 -2
- package/dist/src/core/policy-proposal.js.map +1 -1
- package/dist/src/core/prompt-layout.d.ts +221 -0
- package/dist/src/core/protected-path-guard.d.ts +566 -0
- package/dist/src/core/protected-path-guard.js +848 -55
- package/dist/src/core/protected-path-guard.js.map +1 -1
- package/dist/src/core/question-preempted.d.ts +141 -0
- package/dist/src/core/question-preempted.js +152 -0
- package/dist/src/core/question-preempted.js.map +1 -0
- package/dist/src/core/read-scope.d.ts +172 -0
- package/dist/src/core/read-scope.js +252 -0
- package/dist/src/core/read-scope.js.map +1 -0
- package/dist/src/core/registration.d.ts +25 -0
- package/dist/src/core/reindex.d.ts +99 -0
- package/dist/src/core/sampler.d.ts +313 -0
- package/dist/src/core/sandbox.d.ts +371 -0
- package/dist/src/core/sandbox.js +190 -1
- package/dist/src/core/sandbox.js.map +1 -1
- package/dist/src/core/seal.d.ts +165 -0
- package/dist/src/core/sender-identity.d.ts +476 -0
- package/dist/src/core/sender-identity.js +572 -0
- package/dist/src/core/sender-identity.js.map +1 -0
- package/dist/src/core/shlex.d.ts +102 -0
- package/dist/src/core/shlex.js +159 -0
- package/dist/src/core/shlex.js.map +1 -0
- package/dist/src/core/state.d.ts +505 -0
- package/dist/src/core/task-file.d.ts +185 -0
- package/dist/src/core/telegram-config.d.ts +93 -0
- package/dist/src/core/token.d.ts +409 -0
- package/dist/src/core/token.js +21 -38
- package/dist/src/core/token.js.map +1 -1
- package/dist/src/core/validate.d.ts +138 -0
- package/dist/src/core/values.d.ts +147 -0
- package/dist/src/core/values.js +36 -1
- package/dist/src/core/values.js.map +1 -1
- package/dist/src/core/vault.d.ts +291 -0
- package/dist/src/core/verified-snapshot.d.ts +204 -0
- package/dist/src/core/verify.d.ts +336 -0
- package/dist/src/core/version.d.ts +8 -0
- package/dist/src/core/wysiwys.d.ts +370 -0
- package/dist/src/daemon/advance-child.d.ts +39 -0
- package/dist/src/daemon/advance.d.ts +476 -0
- package/dist/src/daemon/advance.js +25 -4
- package/dist/src/daemon/advance.js.map +1 -1
- package/dist/src/daemon/audit.d.ts +87 -0
- package/dist/src/daemon/daemon.d.ts +1180 -0
- package/dist/src/daemon/daemon.js +9 -0
- package/dist/src/daemon/daemon.js.map +1 -1
- package/dist/src/daemon/dark-session.d.ts +64 -0
- package/dist/src/daemon/draw-child.d.ts +36 -0
- package/dist/src/daemon/draw.d.ts +154 -0
- package/dist/src/daemon/git-evidence.d.ts +173 -0
- package/dist/src/daemon/git-evidence.js +1 -1
- package/dist/src/daemon/projection.d.ts +180 -0
- package/dist/src/daemon/prune.d.ts +207 -0
- package/dist/src/mcp/http.d.ts +113 -0
- package/dist/src/mcp/server.d.ts +265 -0
- package/dist/src/mcp/server.js +17 -1
- package/dist/src/mcp/server.js.map +1 -1
- package/docs/adapter-api.md +106 -0
- package/docs/cli-reference.md +1316 -63
- package/docs/codex-enforced-session.md +103 -0
- package/docs/codex-workspace-broker.md +118 -0
- package/package.json +14 -2
- package/schema/codex-instance.schema.json +82 -0
- package/schema/event.schema.json +539 -9
- 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/event/invalid/approval-granted-sender-hashed-false.json +20 -0
- package/schema/fixtures/event/invalid/approval-granted-sender-hashed-raw-id.json +20 -0
- package/schema/fixtures/event/invalid/audit-gesture-refused-human-actor.json +16 -0
- package/schema/fixtures/event/invalid/audit-gesture-refused-no-actor-no-sender.json +15 -0
- package/schema/fixtures/event/invalid/audit-gesture-refused-unknown-gesture.json +16 -0
- package/schema/fixtures/event/invalid/audit-question-preempted-agent-actor.json +16 -0
- package/schema/fixtures/event/invalid/audit-question-preempted-no-question-id.json +16 -0
- package/schema/fixtures/event/invalid/audit-question-preempted-unknown-source.json +15 -0
- package/schema/fixtures/event/invalid/gate-path-signed-off-absolute-path.json +14 -0
- package/schema/fixtures/event/invalid/gate-path-signed-off-agent-actor.json +14 -0
- package/schema/fixtures/event/invalid/gate-path-signed-off-missing-path.json +13 -0
- package/schema/fixtures/event/valid/approval-granted-sender-hashed.json +20 -0
- package/schema/fixtures/event/valid/audit-gesture-refused-review-note.json +21 -0
- package/schema/fixtures/event/valid/audit-gesture-refused-sender-key-unavailable.json +19 -0
- package/schema/fixtures/event/valid/audit-gesture-refused.json +19 -0
- package/schema/fixtures/event/valid/audit-question-preempted-no-verdict.json +16 -0
- package/schema/fixtures/event/valid/audit-question-preempted.json +20 -0
- package/schema/fixtures/event/valid/gate-path-signed-off.json +14 -0
- package/schema/fixtures/event/valid/harness-kind-claude-code.json +23 -0
- package/schema/fixtures/event/valid/harness-kind-codex.json +23 -0
- package/schema/fixtures/event/valid/harness-kind-cursor.json +23 -0
- package/schema/fixtures/event/valid/harness-kind-grok.json +23 -0
- package/schema/fixtures/event/valid/harness-kind-muse.json +23 -0
- package/schema/fixtures/policy/invalid/senders-half-keyed.json +20 -0
- package/schema/fixtures/policy/valid/canonical.json +1 -1
- package/schema/fixtures/policy/valid/senders-keyed.json +24 -0
- package/schema/fixtures/policy-md/valid/canonical.md +1 -1
- package/schema/fixtures/policy-md/valid/with-values.md +5 -7
- package/schema/fixtures/values/invalid/class-shaped.json +1 -1
- package/schema/fixtures/values/invalid/duplicate-entry.json +1 -1
- package/schema/fixtures/values/invalid/non-string-item.json +1 -1
- package/schema/fixtures/values/invalid/over-cap.json +1 -1
- package/schema/fixtures/values/invalid/unknown-key.json +1 -1
- package/schema/fixtures/values/invalid/version-float.json +1 -0
- package/schema/fixtures/values/invalid/version-integer.json +1 -0
- package/schema/fixtures/values/invalid/version-wrong-string.json +1 -0
- package/schema/fixtures/values/valid/empty-lists.json +2 -3
- package/schema/fixtures/values/valid/full.json +5 -7
- package/schema/fixtures/values/valid/minimal.json +1 -1
- package/schema/fixtures/values-md/invalid/schema-invalid.md +5 -3
- package/schema/fixtures/values-md/invalid/two-blocks.md +3 -3
- package/schema/fixtures/values-md/invalid/unterminated.md +2 -2
- package/schema/fixtures/values-md/invalid/version-1.md +69 -0
- package/schema/fixtures/values-md/invalid/version-unquoted.md +64 -0
- package/schema/fixtures/values-md/invalid/yaml-error.md +2 -2
- package/schema/fixtures/values-md/valid/absent.md +1 -1
- package/schema/fixtures/values-md/valid/with-values.md +5 -7
- package/schema/policy.schema.json +75 -3
- package/schema/values.schema.json +7 -11
- package/templates/codex/README.md +9 -0
- package/schema/fixtures/values/invalid/version-string.json +0 -1
|
@@ -0,0 +1,1449 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The gate: request lifecycle and write-boundary transition enforcement
|
|
3
|
+
* (SPEC.md §6.3, §7, §10.1).
|
|
4
|
+
*
|
|
5
|
+
* This is the module that decides whether a side effect may be authorized, and
|
|
6
|
+
* it is the only module that appends approval lifecycle events. Everything it
|
|
7
|
+
* knows it derives from the append-only log; everything it decides it decides
|
|
8
|
+
* before a byte is written.
|
|
9
|
+
*
|
|
10
|
+
* ## Four rules this module exists to enforce
|
|
11
|
+
*
|
|
12
|
+
* 1. **State is derived, never stored.** {@link requestState} rebuilds one
|
|
13
|
+
* action's approval state from the log alone. There is no status field, no
|
|
14
|
+
* cache, no in-memory session. The envelope's `state:` key is a projection
|
|
15
|
+
* written by the daemon *after* the event lands (SPEC.md §6.3), never a
|
|
16
|
+
* source this module reads.
|
|
17
|
+
* 2. **Illegal transitions are refused before append.** A second grant, a grant
|
|
18
|
+
* on a rejected request, a revoke of an executed action, a decision after the
|
|
19
|
+
* TTL — each is refused with its own machine-readable code and **nothing is
|
|
20
|
+
* appended**. The one deliberate exception is a failed budget check, which
|
|
21
|
+
* appends `budget.exceeded` *and then* refuses: a budget refusal is a fact
|
|
22
|
+
* about the world that an operator must be able to see afterwards, and a
|
|
23
|
+
* refusal nobody can audit is how quiet budget creep starts.
|
|
24
|
+
* 3. **No approval events off the manual path** (amended SPEC.md §6.3). An
|
|
25
|
+
* action whose class resolves to `supervised` or `autonomous` produces *no*
|
|
26
|
+
* `approval.*` record at all — {@link request} returns `proceed: true` and
|
|
27
|
+
* appends nothing. Its authorization is recorded by `execution.started`,
|
|
28
|
+
* which APRV-18 appends, and which is also where its budget is charged (see
|
|
29
|
+
* the consumption contract in `core/budgets.ts`).
|
|
30
|
+
* 4. **Time is assigned by the runtime, not by the caller** (amended SPEC.md
|
|
31
|
+
* §8, A2). No public function here takes a `ts`. TTL lapse, budget windows,
|
|
32
|
+
* and the timestamp stamped on every append all come from one read of
|
|
33
|
+
* {@link GateOptions.clock} — the real clock unless a caller injects one —
|
|
34
|
+
* made once per operation, so a gate decision is still replayable from its
|
|
35
|
+
* inputs while the party being judged no longer authors the clock it is
|
|
36
|
+
* judged by. Tests inject a fixed clock; production passes none.
|
|
37
|
+
*
|
|
38
|
+
* ## Lazy expiry — the named requirement
|
|
39
|
+
*
|
|
40
|
+
* A request expires when `ts > requestTs + defaults.approval_ttl`, **whether or
|
|
41
|
+
* not** an `approval.expired` event exists. Nothing may depend on a daemon
|
|
42
|
+
* having run: if the expiry sweep is asleep, a late grant must still be refused.
|
|
43
|
+
* {@link requestState} therefore computes expiry two ways — from the event, and
|
|
44
|
+
* lazily from the arithmetic — and treats them as equivalent.
|
|
45
|
+
*
|
|
46
|
+
* When {@link decide} refuses a decision because the TTL has lapsed and no
|
|
47
|
+
* `approval.expired` event exists yet, it **first appends that event** (actor
|
|
48
|
+
* {@link EXPIRY_ACTOR}) and then refuses. The alternative — refuse silently and
|
|
49
|
+
* leave the log claiming the request is still live — was rejected: the log is
|
|
50
|
+
* the truth, and a state every reader can derive but no reader can see recorded
|
|
51
|
+
* makes the log disagree with itself. The append is the same one
|
|
52
|
+
* {@link expire} would have made, so a later sweep is a no-op rather than a
|
|
53
|
+
* duplicate.
|
|
54
|
+
*
|
|
55
|
+
* ## `defaults.on_expiry`
|
|
56
|
+
*
|
|
57
|
+
* SPEC.md §5 defines exactly one value, `reject`. An expired request is
|
|
58
|
+
* terminal here under either setting: no grant, no reject, no revoke ever
|
|
59
|
+
* follows it. `on_expiry` is recorded in the `approval.expired` payload so the
|
|
60
|
+
* projection layer (M5) can render the envelope's `state:` as `rejected` rather
|
|
61
|
+
* than `expired` when the policy asks for it. Re-requesting the same action key
|
|
62
|
+
* after expiry is a *new* request and is allowed — the key has not executed, and
|
|
63
|
+
* refusing forever would make a lapsed TTL more punishing than a human's "no".
|
|
64
|
+
*
|
|
65
|
+
* ## The budgets contract (`core/budgets.ts`)
|
|
66
|
+
*
|
|
67
|
+
* That module obligates this one: every `approval.granted` this module appends
|
|
68
|
+
* carries `payload.est_cost_usd` (number, USD) and `payload.class` (the dotted
|
|
69
|
+
* class). `approval.requested` carries them too, so the grant can copy them from
|
|
70
|
+
* the request rather than re-derive them from a file that may have changed. An
|
|
71
|
+
* action that declared no cost is recorded as `0` — an authorization with no
|
|
72
|
+
* declared cost is still an authorization, and still counts as one action.
|
|
73
|
+
*
|
|
74
|
+
* ## Reads are verified, writes are compare-and-append (APRV-20)
|
|
75
|
+
*
|
|
76
|
+
* The gate no longer trusts the bytes it reads. {@link readGateRecords}
|
|
77
|
+
* delegates to `core/state.ts`, which runs the *same* chain verification
|
|
78
|
+
* `approval log verify` runs — one walk, one vocabulary — and refuses
|
|
79
|
+
* `log-corrupt` on anything that does not verify. The gate still does not
|
|
80
|
+
* *diagnose* corruption: it reports that the log is untrustworthy and points at
|
|
81
|
+
* `approval log verify` for the detail, because two modules with two opinions
|
|
82
|
+
* about what "corrupt" means is worse than one.
|
|
83
|
+
*
|
|
84
|
+
* Every append this module makes is authorized by something it read, so every
|
|
85
|
+
* append passes `expectedHead` — the `(seq, hash)` observed at that read. If any
|
|
86
|
+
* record landed in between, `appendEvent` refuses `head-moved` under its lock
|
|
87
|
+
* and nothing is written.
|
|
88
|
+
*
|
|
89
|
+
* Every writer of this module then re-derives and tries again, bounded
|
|
90
|
+
* (APRV-150 for the two harness writers, APRV-236 for {@link register},
|
|
91
|
+
* {@link request}, {@link decide}, {@link withdraw} and
|
|
92
|
+
* {@link finishHarnessExecution}): see {@link withHeadMovedRetry} and
|
|
93
|
+
* `core/head-retry.ts` for why a lost race is not a verdict, and why the retry
|
|
94
|
+
* is a new read plus new checks plus a new compare-and-append rather than a
|
|
95
|
+
* second attempt at the same write. {@link expire} is the one exception, and it
|
|
96
|
+
* needs none: it is materialisation the daemon's next tick performs again.
|
|
97
|
+
*
|
|
98
|
+
* It does not define execution tokens — `core/token.ts` does. {@link decide}'s
|
|
99
|
+
* grant path calls that module's `mintToken` at the seam APRV-17 documented,
|
|
100
|
+
* records only the digest in the `approval.granted` payload, and returns the raw
|
|
101
|
+
* token to its caller. {@link decide} still appends no `execution.*` event:
|
|
102
|
+
* spending a token is `core/token.ts`'s `consumeToken`.
|
|
103
|
+
*
|
|
104
|
+
* The one place this module writes an execution event is
|
|
105
|
+
* {@link consumeHarnessGrant} (APRV-117), and it is the exception that proves
|
|
106
|
+
* the rule: a harness grant mints no token, so nothing else in the system could
|
|
107
|
+
* record that it had been spent, and an authorization with no record of its
|
|
108
|
+
* spending is an authorization that never runs out. See that function for why
|
|
109
|
+
* the marker is `execution.started` and why no completion ever follows it.
|
|
110
|
+
*/
|
|
111
|
+
import { type AttestationRefusalDetail } from "./attest.js";
|
|
112
|
+
import type { Reaction } from "./audit.js";
|
|
113
|
+
import { type BudgetVerdict } from "./budgets.js";
|
|
114
|
+
import { type IntakeVerdict } from "./intake-limits.js";
|
|
115
|
+
import { type ClockOptions } from "./clock.js";
|
|
116
|
+
import { type HarnessProvenance } from "./harness-version.js";
|
|
117
|
+
import { type AppendError, type AppendOptions, type EventRecord, type LogHead } from "./log.js";
|
|
118
|
+
import { type UsdInput } from "./money.js";
|
|
119
|
+
import { type Autonomy, type PolicyLoadResult } from "./policy-load.js";
|
|
120
|
+
import { type Resolution } from "./policy-match.js";
|
|
121
|
+
import { type DrawAsker, type DrawRefusalReason, type LiveDrawRecord } from "./live-draw.js";
|
|
122
|
+
import { LIVE_SELECTION, type LiveSelectorUnavailableReason } from "./sampler.js";
|
|
123
|
+
import { type Decision, type RequestState, type WithdrawReason } from "./state.js";
|
|
124
|
+
import { type ValidationError } from "./validate.js";
|
|
125
|
+
/**
|
|
126
|
+
* The approval-state derivation moved to `core/state.ts` in APRV-20 (finding
|
|
127
|
+
* S4: `gate.ts` and `token.ts` imported each other). It is re-exported here, its
|
|
128
|
+
* documented home, so every existing importer — the CLI, the tests — is
|
|
129
|
+
* unaffected by the move.
|
|
130
|
+
*/
|
|
131
|
+
export { requestState, type Decision, type DeclaredAction, type ExecutionFacts, type RequestDerivation, type RequestState, WITHDRAW_REASONS, isWithdrawReason, type WithdrawReason, } from "./state.js";
|
|
132
|
+
/** Actor stamped on runtime-originated expiry events (SPEC.md §8 `system:`). */
|
|
133
|
+
export declare const EXPIRY_ACTOR = "system:gate";
|
|
134
|
+
/**
|
|
135
|
+
* The closed set of gate refusal codes. Agents branch on these, so the union is
|
|
136
|
+
* frozen public API in the same sense the exit codes are: adding a code is a
|
|
137
|
+
* spec change, redefining one is a breaking change.
|
|
138
|
+
*/
|
|
139
|
+
export declare const GATE_REFUSAL_CODES: readonly [
|
|
140
|
+
/** Policy is unattested or its bytes changed (`core/attest.ts`). */
|
|
141
|
+
"policy-not-attested",
|
|
142
|
+
/**
|
|
143
|
+
* The policy attested now is not the policy the request was routed under
|
|
144
|
+
* (APRV-118, amended SPEC.md §5.2): the hash pinned on `approval.requested`
|
|
145
|
+
* differs from the hash in force at the moment of the grant.
|
|
146
|
+
*
|
|
147
|
+
* Distinct from `policy-not-attested`, and the distinction is the whole point.
|
|
148
|
+
* That code says the live file is unverified; this one says the file is
|
|
149
|
+
* perfectly verified and is a DIFFERENT file from the one that decided this
|
|
150
|
+
* action's autonomy, its limits, and its TTL. A human re-attested in between,
|
|
151
|
+
* so the routing that put the question in front of an approver was computed
|
|
152
|
+
* from rules nobody is enforcing any more, and a grant recorded here would
|
|
153
|
+
* claim a decision under rules the approver never saw. The pending request is
|
|
154
|
+
* void: nothing is appended, and the action is requested again so that it is
|
|
155
|
+
* routed, budgeted, and displayed under the policy actually in force.
|
|
156
|
+
*/
|
|
157
|
+
"policy-drift",
|
|
158
|
+
/** The envelope failed `envelope.schema.json`, or the task file has none. */
|
|
159
|
+
"envelope-invalid",
|
|
160
|
+
/** The task file could not be read. */
|
|
161
|
+
"task-file-unreadable",
|
|
162
|
+
/** This task id already has a `task.registered` record. */
|
|
163
|
+
"task-already-registered",
|
|
164
|
+
/**
|
|
165
|
+
* The task has log history and the file no longer carries an envelope
|
|
166
|
+
* (APRV-63).
|
|
167
|
+
*
|
|
168
|
+
* Observed live in APRV-60: a third-party rewrite of a task file dropped the
|
|
169
|
+
* `approval:` key it did not recognize. Without this code the file reads as an
|
|
170
|
+
* ordinary envelope-less task, and a re-registration from a stripped file
|
|
171
|
+
* would narrow the record silently — declaring fewer actions, or none, for a
|
|
172
|
+
* task the log already says declared them. The loss is named instead, and the
|
|
173
|
+
* envelope is restored by a human from the log; nothing here repairs a file.
|
|
174
|
+
*/
|
|
175
|
+
"envelope-missing",
|
|
176
|
+
/** No `task.registered` record for this task id. */
|
|
177
|
+
"not-registered",
|
|
178
|
+
/** The task is registered but declares no action with this key (SPEC.md §7). */
|
|
179
|
+
"action-not-registered",
|
|
180
|
+
/** A live `approval.requested` for this action key already exists. */
|
|
181
|
+
"duplicate-request",
|
|
182
|
+
/** The action key already has an `execution.*` record (idempotency). */
|
|
183
|
+
"already-executed",
|
|
184
|
+
/**
|
|
185
|
+
* APRV-14 verdicts failed; a `budget.exceeded` event was appended. Covers
|
|
186
|
+
* class limits, `policy.budgets`, and — since S2 — the registered envelope's
|
|
187
|
+
* own `budget.max_cost_usd`, which appears as a `task`-scoped verdict in
|
|
188
|
+
* `verdicts` and in the appended event's payload.
|
|
189
|
+
*/
|
|
190
|
+
"budget-exceeded",
|
|
191
|
+
/**
|
|
192
|
+
* The approver's queue is at the ceiling the policy declared (SPEC.md §5.2's
|
|
193
|
+
* `limits.max_pending`, per class or on a `budgets` scope; APRV-173).
|
|
194
|
+
*
|
|
195
|
+
* A limit on ATTENTION rather than on money, which is why it is its own code
|
|
196
|
+
* and why it fires where it does: after the legality checks that say whether
|
|
197
|
+
* this request may exist at all, and before budgets, which are about the
|
|
198
|
+
* world's exposure rather than the human's. An agent that floods the queue
|
|
199
|
+
* with cheap in-budget requests spends nothing and still defeats the gate,
|
|
200
|
+
* because an approver facing two hundred prompts stops reading them and
|
|
201
|
+
* starts clearing them.
|
|
202
|
+
*
|
|
203
|
+
* Nothing is appended, deliberately, and this is the one refusal shaped
|
|
204
|
+
* differently from `budget-exceeded` on purpose (Carter's approved reading,
|
|
205
|
+
* 2026-08-31). A `budget.exceeded` record exists because a budget refusal is
|
|
206
|
+
* a fact about a commitment audit must be able to reconstruct; a record per
|
|
207
|
+
* refused flood request would hand the flooder the log growth it was refused
|
|
208
|
+
* the queue for. `error.limits` carries the failing verdicts, and the
|
|
209
|
+
* requests that WERE admitted are all in the log to count from.
|
|
210
|
+
*
|
|
211
|
+
* Transient in the sense that matters to a caller: the queue drains when a
|
|
212
|
+
* human decides, a requester withdraws, or a TTL lapses. Retrying at once
|
|
213
|
+
* gets the same answer.
|
|
214
|
+
*/
|
|
215
|
+
"queue-full",
|
|
216
|
+
/**
|
|
217
|
+
* This origin created more requests in the last hour than the policy's
|
|
218
|
+
* `limits.requests_per_hour` allows (SPEC.md §5.2, APRV-173).
|
|
219
|
+
*
|
|
220
|
+
* Distinct from `queue-full`, and the distinction is the repair. That code
|
|
221
|
+
* says the queue is full whoever is asking, so the caller waits for an
|
|
222
|
+
* approver; this one says the caller's own recent volume is the problem, so
|
|
223
|
+
* it slows down. Origin is the requesting actor at v0.1, which the runtime
|
|
224
|
+
* assigns rather than the caller (see `core/intake-limits.ts`), so a
|
|
225
|
+
* requester cannot re-label itself into a fresh hour.
|
|
226
|
+
*
|
|
227
|
+
* Counted over request CREATION, not over live requests: a request that was
|
|
228
|
+
* answered a minute after it was made still spent the origin's share of the
|
|
229
|
+
* hour. A ceiling that forgot each request as it was answered could be
|
|
230
|
+
* cleared by withdrawing every request as fast as it was made.
|
|
231
|
+
*
|
|
232
|
+
* Nothing is appended, for the same reason `queue-full` appends nothing.
|
|
233
|
+
*/
|
|
234
|
+
"rate-limited",
|
|
235
|
+
/**
|
|
236
|
+
* The action resolves to `manual` and its registered declaration carries no
|
|
237
|
+
* `payload_hash` (amended SPEC.md §6.2: MUST for `manual` actions).
|
|
238
|
+
*
|
|
239
|
+
* Enforced here rather than in `envelope.schema.json` because the schema
|
|
240
|
+
* cannot know an action's resolved autonomy — that answer depends on the
|
|
241
|
+
* policy, the irreversibility floor, and the class, none of which the
|
|
242
|
+
* envelope alone determines. A manual action with nothing to bind to would
|
|
243
|
+
* give a human a decision about bytes nobody committed to, so intake refuses
|
|
244
|
+
* and nothing is appended.
|
|
245
|
+
*
|
|
246
|
+
* Since APRV-146 the same code answers the same fact at the harness write
|
|
247
|
+
* boundary: {@link startHarnessExecution} refuses a start that names no
|
|
248
|
+
* payload hash, and {@link consumeHarnessGrant} refuses a spend that presents
|
|
249
|
+
* none (or a grant whose request recorded none). The fact is identical at both
|
|
250
|
+
* ends — a binding is required here and there is none — and the repair is the
|
|
251
|
+
* same shape: state the bytes, or request the action again so the record does.
|
|
252
|
+
* `payload-mismatch` stays the code for bytes that are stated and wrong.
|
|
253
|
+
*/
|
|
254
|
+
"payload-hash-required",
|
|
255
|
+
/**
|
|
256
|
+
* Payload material was supplied at intake and does not hash to the
|
|
257
|
+
* `payload_hash` the registration declared (APRV-28).
|
|
258
|
+
*
|
|
259
|
+
* The same code, and the same reason, as `core/token.ts`'s refusal at spend
|
|
260
|
+
* time: a grant approves specific bytes, so material that hashes to something
|
|
261
|
+
* else is not the payload this request is about. Refused before anything is
|
|
262
|
+
* stored and before anything is appended.
|
|
263
|
+
*/
|
|
264
|
+
"payload-mismatch",
|
|
265
|
+
/**
|
|
266
|
+
* The declared payload material could not be stored (APRV-28): it cannot be
|
|
267
|
+
* canonicalized, or the store directory could not be written.
|
|
268
|
+
*
|
|
269
|
+
* Fails closed rather than requesting anyway. A manual request whose bytes no
|
|
270
|
+
* channel can display is a request no human can answer — SPEC.md §10.4 —
|
|
271
|
+
* so intake refuses and the log is left untouched.
|
|
272
|
+
*/
|
|
273
|
+
"payload-store-failed",
|
|
274
|
+
/**
|
|
275
|
+
* A grant was attempted on a request whose payload carries no usable `class`.
|
|
276
|
+
*
|
|
277
|
+
* Its own code since APRV-20 pass two: the previous behavior substituted the
|
|
278
|
+
* empty string and granted anyway, which recorded an authorization that no
|
|
279
|
+
* class-scoped budget could ever charge and no policy rule could ever match.
|
|
280
|
+
* Fail closed and say which fact was missing.
|
|
281
|
+
*/
|
|
282
|
+
"grant-classless-request",
|
|
283
|
+
/**
|
|
284
|
+
* The action's class resolves to `human-only` (APRV-185, amended SPEC.md
|
|
285
|
+
* §5.2): the policy reserves it to human hands, and a person performs it
|
|
286
|
+
* outside agent execution entirely.
|
|
287
|
+
*
|
|
288
|
+
* Its own code, and distinct from every rejection, because nobody decided
|
|
289
|
+
* anything. A `reject` is a human's answer to a question that was legitimately
|
|
290
|
+
* asked; this is the policy answering that the question does not arise — there
|
|
291
|
+
* is no approval to seek, no approver to ask, and no grant that could be
|
|
292
|
+
* recorded. An agent that read a rejection would sensibly try again with a
|
|
293
|
+
* better summary; an agent that reads this must stop asking and hand the
|
|
294
|
+
* action to a person.
|
|
295
|
+
*
|
|
296
|
+
* Every verb of this module that could mint or withdraw authority returns it:
|
|
297
|
+
* {@link request}, {@link decide} in all three of its decisions, and
|
|
298
|
+
* {@link consumeHarnessGrant}. Grant is the obvious one. Reject and revoke are
|
|
299
|
+
* refused too, and the reason is stated plainly rather than assumed: those
|
|
300
|
+
* verbs WITHDRAW authority, and withdrawing authority that cannot exist would
|
|
301
|
+
* write a decision record about a human-only class into the log, which reads
|
|
302
|
+
* afterwards as a class the gate transacts in. A pending request that a policy
|
|
303
|
+
* amendment has since raised to `human-only` is not stranded by that: it
|
|
304
|
+
* authorizes nothing, no token can be minted for it and no run can spend it,
|
|
305
|
+
* and its requester withdraws it (`withdraw`) or its TTL lapses (`expire`).
|
|
306
|
+
* Neither of those verbs is refused here, deliberately — they are the exits
|
|
307
|
+
* from a question nobody may answer.
|
|
308
|
+
*
|
|
309
|
+
* Evaluated immediately after the check that establishes a request exists at
|
|
310
|
+
* all, and before every other check on the path, on all three verbs. A class
|
|
311
|
+
* that cannot be transacted in is answered before any question about who may
|
|
312
|
+
* decide it, under which policy hash, or against which budget.
|
|
313
|
+
*/
|
|
314
|
+
"class-human-only",
|
|
315
|
+
/**
|
|
316
|
+
* A `harness.launch.*` class that no rule of this policy names (APRV-354).
|
|
317
|
+
*
|
|
318
|
+
* The gate's half of the hook's `hook-harness-launch-unruled`, and it exists
|
|
319
|
+
* so there is no second door. The hook refuses a harness launch it classified
|
|
320
|
+
* from a command line; this refuses one a caller DECLARES, through
|
|
321
|
+
* `approval register` and `approval request`, which is the other way an
|
|
322
|
+
* action class reaches the gate.
|
|
323
|
+
*
|
|
324
|
+
* SPEC.md §7: the family resolves only under an explicit rule, never under
|
|
325
|
+
* `defaults.autonomy`. What a grant of it covers is the launch and nothing
|
|
326
|
+
* the launched session then does, so a class arriving by upgrade rather than
|
|
327
|
+
* by an operator's decision would be a capability nobody chose.
|
|
328
|
+
*
|
|
329
|
+
* Evaluated in the same position as `class-human-only` and immediately above
|
|
330
|
+
* it: both are the policy answering before any question about registration,
|
|
331
|
+
* budget or approver, and this one is the narrower statement of the two — not
|
|
332
|
+
* "reserved to human hands" but "not spoken about at all". Nothing is
|
|
333
|
+
* appended, so no `approval.requested` exists for a class no rule governs.
|
|
334
|
+
*/
|
|
335
|
+
"harness-launch-unruled",
|
|
336
|
+
/**
|
|
337
|
+
* Loop safety escalated the task to manual (SPEC.md §10.2, APRV-18): three
|
|
338
|
+
* consecutive `execution.failed` events. Only the non-manual paths are
|
|
339
|
+
* refused — see {@link request}.
|
|
340
|
+
*/
|
|
341
|
+
"loop-escalated",
|
|
342
|
+
/**
|
|
343
|
+
* A harness outcome was reported for an action key whose `execution.started`
|
|
344
|
+
* carries no `execution: "harness"` marker (APRV-145).
|
|
345
|
+
*
|
|
346
|
+
* The mirror image of `core/execute.ts`'s `execution-delegated`, and the pair
|
|
347
|
+
* is what keeps the two write surfaces from overlapping by one record. That
|
|
348
|
+
* code refuses a HUMAN recovery verb over a harness start; this one refuses a
|
|
349
|
+
* HARNESS report over a start this runtime is watching itself. An untrusted
|
|
350
|
+
* report that could close an `approval run` execution would be reporting an
|
|
351
|
+
* exit code the runtime was about to observe for itself, and the outcome the
|
|
352
|
+
* log kept would be whichever one landed first.
|
|
353
|
+
*/
|
|
354
|
+
"not-delegated",
|
|
355
|
+
/**
|
|
356
|
+
* Every harness-marked start the reported tool call opened already carries an
|
|
357
|
+
* outcome (APRV-145). An execution has exactly one, and a second report would
|
|
358
|
+
* be a second answer about one command — including a `completed` written over
|
|
359
|
+
* a `failed`, which is a streak cleared by repetition rather than by recovery.
|
|
360
|
+
*
|
|
361
|
+
* Named for the fact rather than for the reporter, and spelled exactly as
|
|
362
|
+
* `core/execute.ts` spells the same fact, so a reader who has met one has met
|
|
363
|
+
* both.
|
|
364
|
+
*/
|
|
365
|
+
"already-finished",
|
|
366
|
+
/** No request to decide. */
|
|
367
|
+
"not-requested",
|
|
368
|
+
/** The request already has a terminal decision. */
|
|
369
|
+
"already-decided",
|
|
370
|
+
/** Revoke was attempted on a request that is not granted. */
|
|
371
|
+
"not-granted",
|
|
372
|
+
/**
|
|
373
|
+
* A decision was attempted on a request the requester had already withdrawn
|
|
374
|
+
* (APRV-106, amended SPEC.md §6.3).
|
|
375
|
+
*
|
|
376
|
+
* Distinct from `already-decided` because the facts and the repairs are
|
|
377
|
+
* distinct. `already-decided` says a human answered and the answer stands;
|
|
378
|
+
* this one says nobody answered and nobody can — the party that asked has
|
|
379
|
+
* stopped listening, so a grant here would authorize an action no process is
|
|
380
|
+
* waiting to perform. The repair is to request the action again, which is a
|
|
381
|
+
* new request with a new decision, not to try the decision a second time.
|
|
382
|
+
*/
|
|
383
|
+
"request-withdrawn",
|
|
384
|
+
/**
|
|
385
|
+
* A withdrawal was attempted by an actor other than the one that appended the
|
|
386
|
+
* matching `approval.requested` (APRV-106).
|
|
387
|
+
*
|
|
388
|
+
* Withdrawal is the requester's own retraction, and nothing more. If any
|
|
389
|
+
* actor could withdraw, then any actor could clear an approver's queue — the
|
|
390
|
+
* queue would become deniable by whoever reached the log first, which is the
|
|
391
|
+
* one property the gate exists to deny. A human who wants a pending request
|
|
392
|
+
* gone rejects it, on the record, as themselves.
|
|
393
|
+
*/
|
|
394
|
+
"not-requester",
|
|
395
|
+
/** The TTL lapsed — judged from the request's own ts, event or no event. */
|
|
396
|
+
"expired",
|
|
397
|
+
/** `expire` was called on a request whose TTL has not lapsed. */
|
|
398
|
+
"not-expired",
|
|
399
|
+
/** The actor is not a well-formed `human:`/`agent:` identity. */
|
|
400
|
+
"actor-invalid",
|
|
401
|
+
/** A human-only verb was attempted by a non-human actor. */
|
|
402
|
+
"actor-not-human",
|
|
403
|
+
/**
|
|
404
|
+
* A grant was recorded by a person the resolved rule's `approvers` list does
|
|
405
|
+
* not name (APRV-137, amended SPEC.md §5.2).
|
|
406
|
+
*
|
|
407
|
+
* Distinct from `actor-not-human`, and the distinction is the repair. That
|
|
408
|
+
* code says the actor is not a person at all, and the fix is to run the verb
|
|
409
|
+
* as one. This one says the actor IS a person and is not one the policy
|
|
410
|
+
* named for this class, so the fix is to ask a named approver. Before this
|
|
411
|
+
* code the list was parsed, surfaced by `policy explain`, and enforced
|
|
412
|
+
* nowhere: a policy writing `approvers: [alice]` on `financial.spend` bound
|
|
413
|
+
* nothing while its author believed it bound the class.
|
|
414
|
+
*
|
|
415
|
+
* Scope, and its limits. The check is defense in depth inside the trust
|
|
416
|
+
* boundary §11 states plainly: human identity in v0.1 is config-declared, so
|
|
417
|
+
* anyone who can set that configuration can present any name on this list.
|
|
418
|
+
* What it defends is the honest mistake and the wrong-approver routing, not
|
|
419
|
+
* an actor choosing whose name to wear. The check binds `grant` alone;
|
|
420
|
+
* reject and revoke withdraw authority rather than confer it, and
|
|
421
|
+
* restricting them would leave a request standing, or an authorization live,
|
|
422
|
+
* because the wrong person tried to end it.
|
|
423
|
+
*/
|
|
424
|
+
"actor-not-approver",
|
|
425
|
+
/** The log could not be read, or holds a line that is not a record. */
|
|
426
|
+
"log-unreadable",
|
|
427
|
+
/** The log's final line is unterminated (a crashed write). */
|
|
428
|
+
"log-torn-tail",
|
|
429
|
+
/**
|
|
430
|
+
* The chain does not verify (APRV-20 finding S1). Distinct from
|
|
431
|
+
* `log-unreadable`, which is a filesystem fact: this one says the log's own
|
|
432
|
+
* contents contradict each other, so nothing may be authorized from it.
|
|
433
|
+
*/
|
|
434
|
+
"log-corrupt",
|
|
435
|
+
/**
|
|
436
|
+
* The rendered semantic diff of a proposed policy is larger than a channel
|
|
437
|
+
* prompt can show whole (APRV-109, amended SPEC.md §10.3).
|
|
438
|
+
*
|
|
439
|
+
* A refusal rather than a truncation, and its own code so a caller can tell
|
|
440
|
+
* "this amendment is too big for a phone" from every other reason a proposal
|
|
441
|
+
* fails. A prompt that showed two thirds of a policy change would collect a
|
|
442
|
+
* signature for the third it did not show; the repair is to read the diff at
|
|
443
|
+
* a terminal and attest there, which the message names.
|
|
444
|
+
*/
|
|
445
|
+
"diff-too-large",
|
|
446
|
+
/** No `policy.proposed` record at the named seq (APRV-109). */
|
|
447
|
+
"proposal-not-found",
|
|
448
|
+
/**
|
|
449
|
+
* The policy bytes changed after the attestation prompt was rendered
|
|
450
|
+
* (APRV-109).
|
|
451
|
+
*
|
|
452
|
+
* Distinct from `policy-drift`, which is about a pending approval routed
|
|
453
|
+
* under superseded rules. This one says the human is looking at a hash the
|
|
454
|
+
* file no longer has, so attesting would name bytes the approver was never
|
|
455
|
+
* shown. Nothing is appended and the amendment is proposed again.
|
|
456
|
+
*/
|
|
457
|
+
"proposal-stale",
|
|
458
|
+
/**
|
|
459
|
+
* An attestation was proposed for a policy file that already matches its
|
|
460
|
+
* attestation (APRV-109). There is no amendment to sign, and a prompt for one
|
|
461
|
+
* would ask a human to re-attest bytes already in force.
|
|
462
|
+
*/
|
|
463
|
+
"policy-already-attested",
|
|
464
|
+
/**
|
|
465
|
+
* A grant carrying `reaction: loved` or `reaction: disliked` and no non-blank
|
|
466
|
+
* note (APRV-239, amended SPEC.md §5.2).
|
|
467
|
+
*
|
|
468
|
+
* Grant only. Evaluated with the other checks that read nothing, and nothing
|
|
469
|
+
* is appended. `reject` and `revoke` accept no reaction at all, which is a
|
|
470
|
+
* usage error at the verb rather than a member of this union: their reason IS
|
|
471
|
+
* their note, and there is no second field for a grade to sit in.
|
|
472
|
+
*
|
|
473
|
+
* Its own code rather than the audit path's `note-required` because a caller
|
|
474
|
+
* branching on a gate refusal is branching on this union, and the two verbs
|
|
475
|
+
* are answered by two different modules. The message names `--note`, which is
|
|
476
|
+
* the whole of the fix.
|
|
477
|
+
*/
|
|
478
|
+
"reaction-note-required",
|
|
479
|
+
/**
|
|
480
|
+
* The append itself failed; `append` carries the underlying error. Its
|
|
481
|
+
* `code` is `head-moved` when the log grew between this module's read and its
|
|
482
|
+
* append: every check that authorized the write was made against an older log,
|
|
483
|
+
* so nothing was written. Since APRV-236 this code reaches a caller only after
|
|
484
|
+
* the bounded read-check-append retry is spent (`core/head-retry.ts`), and its
|
|
485
|
+
* message says how many attempts were made. A single lost race is no longer
|
|
486
|
+
* reported at all: it is re-derived, and the answer the fresh log supports is
|
|
487
|
+
* what the caller receives.
|
|
488
|
+
*/
|
|
489
|
+
"append-failed",
|
|
490
|
+
/**
|
|
491
|
+
* A `delivery: "self"` request could not publish a delivery address (APRV-211):
|
|
492
|
+
* the ephemeral private key could not be written beside the log.
|
|
493
|
+
*
|
|
494
|
+
* Fail closed, and unlike APRV-105's ordinary sealed path, which drops the
|
|
495
|
+
* convenience and leaves the paste path standing. There is no paste path
|
|
496
|
+
* here — the requester is a process, not a terminal — so a request admitted
|
|
497
|
+
* without an address would spend a human's decision on an authorization
|
|
498
|
+
* nothing can ever open. Nothing is appended; the next attempt asks again.
|
|
499
|
+
*/
|
|
500
|
+
"token-delivery-unavailable"];
|
|
501
|
+
export type GateRefusalCode = (typeof GATE_REFUSAL_CODES)[number];
|
|
502
|
+
/** Every gate failure is one of these. Nothing here throws. */
|
|
503
|
+
export interface GateRefusal {
|
|
504
|
+
ok: false;
|
|
505
|
+
code: GateRefusalCode;
|
|
506
|
+
message: string;
|
|
507
|
+
/** Attestation discriminator, when `code` is `policy-not-attested`. */
|
|
508
|
+
detail?: AttestationRefusalDetail;
|
|
509
|
+
/** The derived state at refusal time, for transition refusals. */
|
|
510
|
+
state?: RequestState;
|
|
511
|
+
/** The failing verdicts, when `code` is `budget-exceeded`. */
|
|
512
|
+
verdicts?: BudgetVerdict[];
|
|
513
|
+
/**
|
|
514
|
+
* The failing request-volume verdicts, when `code` is `queue-full` or
|
|
515
|
+
* `rate-limited` (APRV-173). Separate from `verdicts` because they are a
|
|
516
|
+
* different measurement with a different shape, and because a caller that
|
|
517
|
+
* branched on `verdicts` to read money out of a budget refusal must not find
|
|
518
|
+
* queue counts there.
|
|
519
|
+
*/
|
|
520
|
+
limits?: IntakeVerdict[];
|
|
521
|
+
/** Schema errors, when `code` is `envelope-invalid`. */
|
|
522
|
+
errors?: ValidationError[];
|
|
523
|
+
/** The underlying append error, when `code` is `append-failed`. */
|
|
524
|
+
append?: AppendError;
|
|
525
|
+
/**
|
|
526
|
+
* The two policy hashes actually compared, when `code` is `policy-drift`
|
|
527
|
+
* (APRV-235): the hash the request (or the grant) pinned, and the hash
|
|
528
|
+
* attested at the moment of the refusal.
|
|
529
|
+
*
|
|
530
|
+
* Carried on the refusal so that whatever RECORDS the refusal records the
|
|
531
|
+
* comparison that was made. Re-deriving the pair afterwards would be a second
|
|
532
|
+
* comparison, at a second instant, against a file that may have moved again,
|
|
533
|
+
* and a record describing a comparison nobody performed is worse than no
|
|
534
|
+
* record at all. `core/decision-refusal.ts` copies these verbatim.
|
|
535
|
+
*/
|
|
536
|
+
drift?: {
|
|
537
|
+
requested: string;
|
|
538
|
+
attested: string;
|
|
539
|
+
};
|
|
540
|
+
/**
|
|
541
|
+
* An event appended *alongside* the refusal: the `budget.exceeded` record, or
|
|
542
|
+
* the lazily-materialised `approval.expired` record. Never an authorization.
|
|
543
|
+
*/
|
|
544
|
+
record?: EventRecord;
|
|
545
|
+
}
|
|
546
|
+
/**
|
|
547
|
+
* Options shared by every gate operation.
|
|
548
|
+
*
|
|
549
|
+
* Note what is **not** here and no longer a parameter anywhere in this module:
|
|
550
|
+
* `ts`. Under amended SPEC.md §8 a gate-typed event's timestamp is assigned by
|
|
551
|
+
* the runtime at the write boundary, so it is read from {@link ClockOptions
|
|
552
|
+
* clock} (defaulting to the real clock) rather than accepted from the caller.
|
|
553
|
+
* The refusal the spec asks for is structural: there is no parameter to pass.
|
|
554
|
+
*/
|
|
555
|
+
export interface GateOptions extends ClockOptions {
|
|
556
|
+
/** Schema directory, passed to both envelope validation and the append. */
|
|
557
|
+
schemaDir?: string;
|
|
558
|
+
/**
|
|
559
|
+
* Where to find `APPROVAL.md`. `dir`/`file` have the same semantics as
|
|
560
|
+
* `loadPolicy`.
|
|
561
|
+
*
|
|
562
|
+
* `read` is the one read seam a gate operation uses to fetch the policy bytes
|
|
563
|
+
* (APRV-142), defaulting to `readFileSync`. It exists so a test can simulate
|
|
564
|
+
* a file swapped mid-operation and prove the swap cannot land: the seam is
|
|
565
|
+
* called exactly once per gate operation, so a reader that returns different
|
|
566
|
+
* bytes on its second call has no second call to return them to. It is not a
|
|
567
|
+
* widening of anything — a caller holding `GateOptions` can already name any
|
|
568
|
+
* file through `file`.
|
|
569
|
+
*/
|
|
570
|
+
policy?: {
|
|
571
|
+
dir?: string;
|
|
572
|
+
file?: string;
|
|
573
|
+
read?: (path: string) => Uint8Array;
|
|
574
|
+
};
|
|
575
|
+
/** Lock tuning for the append path. */
|
|
576
|
+
append?: AppendOptions;
|
|
577
|
+
/**
|
|
578
|
+
* How many times a writer of this module re-derives its verdict after a
|
|
579
|
+
* `head-moved` refusal, at most `head-retry.ts`'s `HEAD_MOVED_ATTEMPTS`
|
|
580
|
+
* (APRV-150, APRV-236, {@link withHeadMovedRetry}).
|
|
581
|
+
*
|
|
582
|
+
* Only ever lowers the bound. `1` is the pre-APRV-150 behaviour — one read,
|
|
583
|
+
* one set of checks, one append, and a lost race is reported as a refusal —
|
|
584
|
+
* which is what a test pins the old shape with. A larger number, a zero or a
|
|
585
|
+
* fraction is ignored in favour of the runtime's own value: the ceiling is not
|
|
586
|
+
* a caller's to raise.
|
|
587
|
+
*/
|
|
588
|
+
retryOnHeadMoved?: number;
|
|
589
|
+
/**
|
|
590
|
+
* Where payload material is stored (APRV-28). Defaults to the convention
|
|
591
|
+
* `core/payload-store.ts` defines: `.approval/payloads/`, beside the log.
|
|
592
|
+
*/
|
|
593
|
+
payloadStoreDir?: string;
|
|
594
|
+
/**
|
|
595
|
+
* Where per-request private keys live (APRV-105). Defaults to the convention
|
|
596
|
+
* `core/seal.ts` defines: `.approval/keys/`, beside the log. Under the default
|
|
597
|
+
* `token_delivery: manual` nothing here is ever written or read.
|
|
598
|
+
*/
|
|
599
|
+
keyStoreDir?: string;
|
|
600
|
+
/**
|
|
601
|
+
* The environment the operator's sampling secret is read from (APRV-127).
|
|
602
|
+
* Injected by tests; defaults to `process.env`. The secret is never read from
|
|
603
|
+
* the policy file or from anywhere else inside the repository.
|
|
604
|
+
*/
|
|
605
|
+
env?: NodeJS.ProcessEnv;
|
|
606
|
+
/**
|
|
607
|
+
* How a process with no sampling secret asks the daemon for a live draw
|
|
608
|
+
* (APRV-208). Defaults to `core/live-draw.ts`'s `askDaemonDraw`, which
|
|
609
|
+
* `spawnSync`s a relay against the owner-only socket under the approval home.
|
|
610
|
+
*
|
|
611
|
+
* A seam for tests, and stated plainly rather than defended: an in-process
|
|
612
|
+
* caller that supplies a lying asker can wave a live action through, and so
|
|
613
|
+
* can one that points `policy.file` at a policy of its own. Both are the same
|
|
614
|
+
* trust boundary, which is the process holding `GateOptions` — SPEC.md §11's
|
|
615
|
+
* "the trust boundary is the local machine". The property this seam does NOT
|
|
616
|
+
* weaken is the one that matters across processes: a HOOK never sets it, the
|
|
617
|
+
* default asker talks only to the socket under the approval home, and the
|
|
618
|
+
* verdict it brings back is recorded with the MAC an operator recomputes.
|
|
619
|
+
*/
|
|
620
|
+
drawAsk?: DrawAsker;
|
|
621
|
+
}
|
|
622
|
+
type ReadOutcome = {
|
|
623
|
+
ok: true;
|
|
624
|
+
records: EventRecord[];
|
|
625
|
+
head: LogHead | null;
|
|
626
|
+
} | GateRefusal;
|
|
627
|
+
/**
|
|
628
|
+
* Read the log's records, refusing unless the whole chain verifies.
|
|
629
|
+
*
|
|
630
|
+
* Delegates to `core/state.ts`'s {@link readVerifiedRecords}: since APRV-20
|
|
631
|
+
* (finding S1) the gate does not merely parse the log, it verifies it. A
|
|
632
|
+
* corrupt log refuses `log-corrupt` and authorizes nothing; a torn tail refuses
|
|
633
|
+
* `log-torn-tail`, unchanged, because the repair is a human decision and never a
|
|
634
|
+
* gate's; an unopenable file refuses `log-unreadable`, an I/O fact rather than an
|
|
635
|
+
* accusation.
|
|
636
|
+
*
|
|
637
|
+
* The returned `head` is what every append site here passes as `expectedHead`,
|
|
638
|
+
* so a decision derived from these records cannot land on a log that moved
|
|
639
|
+
* underneath it.
|
|
640
|
+
*/
|
|
641
|
+
export declare function readGateRecords(logPath: string, schemaDir?: string): ReadOutcome;
|
|
642
|
+
/**
|
|
643
|
+
* The policy as a gate operation would load it: one read through the seam of
|
|
644
|
+
* {@link GateOptions.policy}, parsed, failing closed (APRV-324).
|
|
645
|
+
*
|
|
646
|
+
* Exported for one caller, `channels/contract.ts`, which resolves an
|
|
647
|
+
* authenticated sender to an approver identity BEFORE it calls {@link decide}
|
|
648
|
+
* and must do so against the same file, discovered the same way, through the
|
|
649
|
+
* same injectable reader. Writing a second `loadPolicy` call there would have
|
|
650
|
+
* been a second discovery path that could find a different file.
|
|
651
|
+
*
|
|
652
|
+
* It is still a SECOND read of the bytes, one operation earlier, and that is
|
|
653
|
+
* deliberate rather than overlooked: `decide` reads once per attempt on purpose
|
|
654
|
+
* (see {@link PolicyRead}), and pinning bytes across its bounded retry would
|
|
655
|
+
* make a re-attestation inside the retry window invisible to the drift check.
|
|
656
|
+
* What closes the gap is the check itself — a policy that changes between the
|
|
657
|
+
* two reads is either unattested or a different hash from the one the request
|
|
658
|
+
* pinned, and `decide` refuses `policy-not-attested` or `policy-drift` rather
|
|
659
|
+
* than authorizing under bytes the resolution never saw.
|
|
660
|
+
*/
|
|
661
|
+
export declare function readGatePolicy(options: GateOptions): PolicyLoadResult;
|
|
662
|
+
/** One declared action of an envelope (SPEC.md §6.2 `actions[]`). */
|
|
663
|
+
export interface RegisteredAction {
|
|
664
|
+
class: string;
|
|
665
|
+
idempotency_key: string;
|
|
666
|
+
summary?: string;
|
|
667
|
+
reversible?: boolean;
|
|
668
|
+
/** Canonical decimal USD string (APRV-121); see `core/money.ts`. */
|
|
669
|
+
est_cost_usd?: string;
|
|
670
|
+
/**
|
|
671
|
+
* The content binding of amended SPEC.md §6.2. MUST be present for an action
|
|
672
|
+
* that resolves to `manual`; the enforcement point is {@link request}, not
|
|
673
|
+
* registration, because autonomy is not known until policy is consulted and
|
|
674
|
+
* refusing at registration would make an envelope unregisterable for a
|
|
675
|
+
* property of a policy file it never mentions.
|
|
676
|
+
*/
|
|
677
|
+
payload_hash?: string;
|
|
678
|
+
}
|
|
679
|
+
/**
|
|
680
|
+
* What to register: a task file to read, or an already-in-hand envelope.
|
|
681
|
+
*
|
|
682
|
+
* The task **id** is not part of the envelope — `envelope.schema.json` governs
|
|
683
|
+
* the value of the `approval:` key only, and `id:` is a sibling board key owned
|
|
684
|
+
* by Backlog.md (SPEC.md §6). So the file form reads it from the frontmatter's
|
|
685
|
+
* `id`, and the in-memory form takes it explicitly.
|
|
686
|
+
*/
|
|
687
|
+
export type RegisterSource = {
|
|
688
|
+
file: string;
|
|
689
|
+
} | {
|
|
690
|
+
task: string;
|
|
691
|
+
envelope: unknown;
|
|
692
|
+
};
|
|
693
|
+
export type RegisterResult = {
|
|
694
|
+
ok: true;
|
|
695
|
+
record: EventRecord;
|
|
696
|
+
task: string;
|
|
697
|
+
actions: RegisteredAction[];
|
|
698
|
+
} | GateRefusal;
|
|
699
|
+
/**
|
|
700
|
+
* Validate an envelope and append `task.registered`.
|
|
701
|
+
*
|
|
702
|
+
* Fail closed: the envelope is validated against `envelope.schema.json` **before
|
|
703
|
+
* anything is read from it and before any byte is written**. A schema-invalid
|
|
704
|
+
* envelope leaves the log untouched.
|
|
705
|
+
*
|
|
706
|
+
* Double registration is refused. Re-registering a task id would give the same
|
|
707
|
+
* id two different declared action sets in one log, and every later lookup
|
|
708
|
+
* ("what class is this key?") would have to pick one — silently. Envelope
|
|
709
|
+
* *changes* are `envelope.drift` (SPEC.md §6.3, M5), not a second registration.
|
|
710
|
+
*
|
|
711
|
+
* `actor` is a `human:` or `agent:` identity; registration is an ordinary
|
|
712
|
+
* proposal, not a privileged act, so an agent may perform it. `system:` is
|
|
713
|
+
* refused: the runtime does not author tasks.
|
|
714
|
+
*
|
|
715
|
+
* The registration payload carries the envelope's `actions` and — since S2 —
|
|
716
|
+
* its `budget` block, so the task's own `max_cost_usd` cap is enforced from the
|
|
717
|
+
* log rather than from a task file that may be edited afterwards.
|
|
718
|
+
*/
|
|
719
|
+
export declare function register(logPath: string, source: RegisterSource, actor: string, options?: RegisterOptions): RegisterResult;
|
|
720
|
+
/**
|
|
721
|
+
* {@link register}'s options, plus the one field only a harness hook passes.
|
|
722
|
+
*
|
|
723
|
+
* `harness` is APRV-227's provenance pair, and where it comes from is the whole
|
|
724
|
+
* of its safety. It is a CALL option: the hook process derives it from its own
|
|
725
|
+
* event and its own PATH and hands it in. It is NOT read from the envelope, the
|
|
726
|
+
* task file, or anything else on disk — an envelope field would be a value an
|
|
727
|
+
* agent authors about the binary that is supposed to be watching it, and a task
|
|
728
|
+
* file is a file an agent edits. Copied into the payload verbatim when present
|
|
729
|
+
* and omitted entirely when absent; nothing downstream reads it back (SPEC.md
|
|
730
|
+
* §11.1 invariant 4 — see `core/harness-version.ts`).
|
|
731
|
+
*/
|
|
732
|
+
export interface RegisterOptions extends GateOptions {
|
|
733
|
+
harness?: HarnessProvenance;
|
|
734
|
+
}
|
|
735
|
+
/**
|
|
736
|
+
* The declared action for `(task, actionKey)`, as registered in the log.
|
|
737
|
+
*
|
|
738
|
+
* SPEC.md §7: "an action's class MUST be declared before an execution token can
|
|
739
|
+
* be requested for it". The declaration lives in `task.registered`, so the log —
|
|
740
|
+
* not the file, which may have been edited since — is what the gate reads back.
|
|
741
|
+
*/
|
|
742
|
+
export declare function registeredAction(records: EventRecord[], task: string, actionKey: string): {
|
|
743
|
+
ok: true;
|
|
744
|
+
action: RegisteredAction;
|
|
745
|
+
} | GateRefusal;
|
|
746
|
+
/** The action being submitted to the gate. */
|
|
747
|
+
export interface RequestInput {
|
|
748
|
+
task: string;
|
|
749
|
+
actionKey: string;
|
|
750
|
+
/** The dotted side-effect class (SPEC.md §7). */
|
|
751
|
+
cls: string;
|
|
752
|
+
/** Canonical decimal USD string (APRV-121); a JSON number is read as the historical form. */
|
|
753
|
+
est_cost_usd?: UsdInput;
|
|
754
|
+
reversible?: boolean;
|
|
755
|
+
summary?: string;
|
|
756
|
+
/**
|
|
757
|
+
* The content binding (amended SPEC.md §6.2). A fallback only: {@link request}
|
|
758
|
+
* prefers the value on the `task.registered` record, because the log is what
|
|
759
|
+
* the human's policy was attested against and a caller-supplied hash could
|
|
760
|
+
* name bytes the registration never declared.
|
|
761
|
+
*/
|
|
762
|
+
payload_hash?: string;
|
|
763
|
+
/**
|
|
764
|
+
* The concrete payload material, to be filed in the payload store (APRV-28).
|
|
765
|
+
*
|
|
766
|
+
* Wrapped in an object so that "supplied, and the material happens to be
|
|
767
|
+
* `undefined`" is distinguishable from "not supplied at all" — the first is a
|
|
768
|
+
* payload that cannot be bound to and is refused, the second is the ordinary
|
|
769
|
+
* case of a caller that stored the bytes some other way (or holds none).
|
|
770
|
+
*
|
|
771
|
+
* Its hash MUST equal the declared `payload_hash`; a difference refuses
|
|
772
|
+
* `payload-mismatch` and stores nothing. Material supplied for an action that
|
|
773
|
+
* resolves to `supervised` or `autonomous` is ignored: that path records no
|
|
774
|
+
* request, so there is no binding a stored payload could belong to.
|
|
775
|
+
*/
|
|
776
|
+
payload?: {
|
|
777
|
+
value: unknown;
|
|
778
|
+
};
|
|
779
|
+
/**
|
|
780
|
+
* `"harness"` when this request will never be executed through
|
|
781
|
+
* `approval run` (APRV-106).
|
|
782
|
+
*
|
|
783
|
+
* The Claude Code hook is the case it exists for: the hook asks the gate a
|
|
784
|
+
* permission question and the *harness* runs the command, so a grant here has
|
|
785
|
+
* nothing to hand a token to. Recorded on `approval.requested` and copied by
|
|
786
|
+
* {@link decide} onto the grant, where it suppresses the mint. See
|
|
787
|
+
* `DeclaredAction.execution` in `core/state.ts` for why a false claim can
|
|
788
|
+
* only remove the claimant's own capability.
|
|
789
|
+
*/
|
|
790
|
+
execution?: "harness";
|
|
791
|
+
/**
|
|
792
|
+
* ISO-8601 instant after which the requester stops waiting (APRV-106).
|
|
793
|
+
*
|
|
794
|
+
* Recorded for CHANNELS TO DISPLAY and for nothing else: an approver seeing
|
|
795
|
+
* "requester waits until 09:23 UTC" knows that an answer at 09:40 reaches
|
|
796
|
+
* nobody. It bounds no TTL, charges no budget, and gates nothing — the
|
|
797
|
+
* policy's `defaults.approval_ttl` remains the only deadline with authority.
|
|
798
|
+
*/
|
|
799
|
+
wait_until?: string;
|
|
800
|
+
/**
|
|
801
|
+
* The caller has established that loop safety floors this action to `manual`
|
|
802
|
+
* for this invocation (APRV-145, amended SPEC.md §10.2).
|
|
803
|
+
*
|
|
804
|
+
* The THIRD way into the manual path, beside a class that resolves manual and
|
|
805
|
+
* an action the APRV-127 live draw selected, and it works exactly as that
|
|
806
|
+
* second one does: the non-manual branch is skipped and everything below it
|
|
807
|
+
* runs unchanged, so nothing in the manual path knows or asks how the action
|
|
808
|
+
* got here. The flag is a fact the caller computed from the log
|
|
809
|
+
* (`core/loop.ts`'s `harnessLoopFloor`) and it can only ever ADD scrutiny: a
|
|
810
|
+
* caller that sets it wrongly asks a human about a command that did not need
|
|
811
|
+
* one, and a caller that omits it is refused at the write boundary by
|
|
812
|
+
* {@link startHarnessExecution}, which re-checks the same streaks.
|
|
813
|
+
*/
|
|
814
|
+
loopFloor?: boolean;
|
|
815
|
+
/**
|
|
816
|
+
* `"self"` when the requesting process will consume its own grant, in its own
|
|
817
|
+
* process, and no other principal needs the raw token (APRV-211).
|
|
818
|
+
*
|
|
819
|
+
* The daemon's cadence advance is the case it exists for. The daemon is the
|
|
820
|
+
* requester AND the executor: it asks the gate, a human answers on the phone,
|
|
821
|
+
* and the same process spends the answer through {@link startExecution}'s
|
|
822
|
+
* APRV-105 sealed path. Before this field, the grant handed its raw token back
|
|
823
|
+
* to the GRANTING surface — the Telegram listener's terminal — which printed
|
|
824
|
+
* it under "single-use · stored nowhere · copy it now", the APRV-166 relay
|
|
825
|
+
* path meant for a requester in another process. Nobody was ever going to
|
|
826
|
+
* carry that value anywhere; it was a live credential rendered for a principal
|
|
827
|
+
* that was not the requester, which SPEC.md §11.1's raw-secrets invariant
|
|
828
|
+
* exists to prevent.
|
|
829
|
+
*
|
|
830
|
+
* So it does two things and nothing else. {@link request} mints the sealed
|
|
831
|
+
* delivery address for this action REGARDLESS of `defaults.token_delivery`,
|
|
832
|
+
* because there is no terminal on the other end of the paste path and a
|
|
833
|
+
* request with no address would be an authorization nobody could open; and
|
|
834
|
+
* {@link decide} withholds the raw token from its own return value, so no
|
|
835
|
+
* granting surface has one to print. The token still exists, still binds to
|
|
836
|
+
* the payload bytes, is still single-use, and is still minted only by a
|
|
837
|
+
* human's grant. Nothing here reduces scrutiny: it removes a reader, not a
|
|
838
|
+
* check.
|
|
839
|
+
*
|
|
840
|
+
* Refused when the key cannot be written (`token-delivery-unavailable`), which
|
|
841
|
+
* is the fail-closed direction: a self-delivered grant nobody can open would
|
|
842
|
+
* spend a human's decision on an authorization that can never execute.
|
|
843
|
+
*/
|
|
844
|
+
delivery?: "self";
|
|
845
|
+
}
|
|
846
|
+
/**
|
|
847
|
+
* Why a `supervised-live` action was, or was not, sent to the human gate
|
|
848
|
+
* (amended SPEC.md §5.2/§6.3, APRV-127).
|
|
849
|
+
*
|
|
850
|
+
* Returned to the caller and **never written to the log**. See
|
|
851
|
+
* {@link liveVerdict} for why the log carries no trace of the selection.
|
|
852
|
+
*/
|
|
853
|
+
export interface LiveVerdict {
|
|
854
|
+
/** The class's declared `live_rate`, as `policy-match.ts` resolved it. */
|
|
855
|
+
rate: number;
|
|
856
|
+
/** True when this action must stop for a human before it may execute. */
|
|
857
|
+
gated: boolean;
|
|
858
|
+
/**
|
|
859
|
+
* Machine-readable and closed, because a supervisor branches on it:
|
|
860
|
+
*
|
|
861
|
+
* - `selected` — the HMAC fell under the rate. This is the fraction working.
|
|
862
|
+
* - `not-selected` — it did not. The action proceeds, and still enters the
|
|
863
|
+
* retrospective pool.
|
|
864
|
+
* - `payload-hash-absent` — the registration declared no `payload_hash`, so
|
|
865
|
+
* there is nothing to select over. Gated: an action whose bytes nobody
|
|
866
|
+
* named cannot be shown to have been fairly sampled, and the manual path
|
|
867
|
+
* refuses it by name a moment later.
|
|
868
|
+
* - the three {@link LiveSelectorUnavailableReason}s — no usable secret.
|
|
869
|
+
* Gated. See `core/sampler.ts` on why live selection fails closed where
|
|
870
|
+
* retrospective sampling fails open.
|
|
871
|
+
*/
|
|
872
|
+
reason: "selected" | "not-selected" | "payload-hash-absent" | LiveSelectorUnavailableReason | DrawRefusalReason;
|
|
873
|
+
/** The algorithm an operator holding the secret recomputes to check this. */
|
|
874
|
+
selection: typeof LIVE_SELECTION;
|
|
875
|
+
/** The NAME of the secret's environment variable. Never the secret. */
|
|
876
|
+
secretEnv: string | null;
|
|
877
|
+
/**
|
|
878
|
+
* The delegation, when this verdict was not computed in this process
|
|
879
|
+
* (APRV-208). Absent for an in-process draw, which is why a sampled
|
|
880
|
+
* supervised-live request in an operator's own terminal is still byte-for-byte
|
|
881
|
+
* a manual one. See {@link LiveDrawRecord} for why a DELEGATED verdict is
|
|
882
|
+
* recorded and an in-process one is not.
|
|
883
|
+
*/
|
|
884
|
+
draw?: LiveDrawRecord;
|
|
885
|
+
}
|
|
886
|
+
export type RequestResult = {
|
|
887
|
+
ok: true;
|
|
888
|
+
autonomy: Autonomy;
|
|
889
|
+
/** True when execution may start now: the supervised/autonomous path. */
|
|
890
|
+
proceed: boolean;
|
|
891
|
+
resolution: Resolution;
|
|
892
|
+
/** The `approval.requested` record, or `null` off the manual path. */
|
|
893
|
+
record: EventRecord | null;
|
|
894
|
+
/** Digest of the attested policy bytes that produced this intake verdict. */
|
|
895
|
+
policySha256: string;
|
|
896
|
+
/**
|
|
897
|
+
* The live-selection verdict, for a `supervised-live` class only
|
|
898
|
+
* (APRV-127). Absent for every other class: there was no fraction to fall
|
|
899
|
+
* inside or outside of.
|
|
900
|
+
*/
|
|
901
|
+
live?: LiveVerdict;
|
|
902
|
+
} | GateRefusal;
|
|
903
|
+
/**
|
|
904
|
+
* Gate intake.
|
|
905
|
+
*
|
|
906
|
+
* Check order, and why it is this order:
|
|
907
|
+
*
|
|
908
|
+
* 1. **Actor.** A malformed identity is a bad call, not a policy question.
|
|
909
|
+
* 2. **Attestation.** An unverified policy cannot answer anything, so it is
|
|
910
|
+
* checked before the policy is consulted rather than after.
|
|
911
|
+
* 3. **Policy resolution** (`loadPolicy` + `resolve`, including the §7
|
|
912
|
+
* irreversibility floor). A failed load resolves everything to `manual` —
|
|
913
|
+
* that is `policy-match.ts`'s contract, and this module does not soften it.
|
|
914
|
+
* 3b. **Declaration** (SPEC.md §7, APRV-147), for a `manual` resolution and for
|
|
915
|
+
* a `supervised-live` one. The log must carry a `task.registered` for the
|
|
916
|
+
* task and an action with this idempotency key, or the request is refused
|
|
917
|
+
* `not-registered` / `action-not-registered` and nothing is appended. Before
|
|
918
|
+
* the live draw and before the binding below, so an undeclared action never
|
|
919
|
+
* reaches a human's queue, never has the live fraction drawn over a hash it
|
|
920
|
+
* chose for itself, and hears the real reason rather than
|
|
921
|
+
* `payload-hash-required`.
|
|
922
|
+
* 4. **Off the manual path, retain supplied bound material, then stop — unless
|
|
923
|
+
* the live fraction says otherwise.** `supervised`/`autonomous` append **no
|
|
924
|
+
* event** (amended SPEC.md §6.3) and return `proceed: true`. When the caller
|
|
925
|
+
* supplies payload material, it is checked against the registered declaration
|
|
926
|
+
* and retained for the later execution evidence. Their budget is charged at `execution.started`,
|
|
927
|
+
* which APRV-18 appends — checking budgets here as well would charge them
|
|
928
|
+
* twice or, worse, pass here and fail there. A `supervised-live` class
|
|
929
|
+
* (APRV-127) draws its declared fraction here: an action the draw selects
|
|
930
|
+
* falls through into everything below and is treated as `manual` from this
|
|
931
|
+
* line on, and an action it does not proceeds exactly as before.
|
|
932
|
+
* 5. **Content binding** (amended SPEC.md §6.2, A1). A manual action whose
|
|
933
|
+
* registered declaration carries no `payload_hash` is refused
|
|
934
|
+
* `payload-hash-required` and nothing is appended. This is the first check
|
|
935
|
+
* after the manual path is known, because a request with nothing to bind to
|
|
936
|
+
* should never reach a human's queue at all.
|
|
937
|
+
* 5b. **Payload material**, when the caller supplied any (APRV-28). Its hash is
|
|
938
|
+
* checked against the declaration here — before legality, before budgets,
|
|
939
|
+
* before any file — and the bytes are written to the payload store in the
|
|
940
|
+
* step immediately before the append, so a refused request stores nothing.
|
|
941
|
+
* See the two comments in the body for the ordering and the one orphan it
|
|
942
|
+
* permits.
|
|
943
|
+
* 6. **Request legality**, then **budgets**, then the append. Legality first
|
|
944
|
+
* because a duplicate request is a caller bug that no budget outcome should
|
|
945
|
+
* obscure, and because refusing it must leave the log untouched.
|
|
946
|
+
*
|
|
947
|
+
* The `approval.requested` payload carries `class`, `est_cost_usd`, and (on the
|
|
948
|
+
* manual path, always) `payload_hash` — the budgets contract requires the first
|
|
949
|
+
* two on the grant and the token binding requires the third, and the grant
|
|
950
|
+
* copies all of them from here rather than re-deriving them from a file that
|
|
951
|
+
* may have changed.
|
|
952
|
+
*/
|
|
953
|
+
export declare function request(logPath: string, input: RequestInput, actor: string, options?: GateOptions): RequestResult;
|
|
954
|
+
export interface DecideOptions extends GateOptions {
|
|
955
|
+
/** Free-text note recorded in the event payload (SPEC.md §8's example). */
|
|
956
|
+
note?: string;
|
|
957
|
+
/**
|
|
958
|
+
* The channel delivery id of the batch this decision answered (amended
|
|
959
|
+
* SPEC.md §10.3, APRV-38), recorded as `payload.batch_delivery_id` on
|
|
960
|
+
* `approval.granted` / `approval.rejected`.
|
|
961
|
+
*
|
|
962
|
+
* The log never batches: one gesture over five requests is five events, and
|
|
963
|
+
* this is the only thing tying them back together for audit. It is recorded
|
|
964
|
+
* on grant and reject alone, the two decisions a channel can collect;
|
|
965
|
+
* `revoke` is a considered act performed against the log through the CLI and
|
|
966
|
+
* never arrives as part of a batch gesture, so a value supplied with it is
|
|
967
|
+
* ignored rather than written.
|
|
968
|
+
*
|
|
969
|
+
* Empty strings are ignored for the same reason the schema requires
|
|
970
|
+
* `minLength: 1`: a batch id that identifies no batch is worse than none,
|
|
971
|
+
* since audit would read it as a grouping that never existed.
|
|
972
|
+
*/
|
|
973
|
+
batchDeliveryId?: string;
|
|
974
|
+
/**
|
|
975
|
+
* The graded reaction the approver gave, on `grant` only (APRV-239, amended
|
|
976
|
+
* SPEC.md §5.2), recorded as `payload.reaction` on `approval.granted`.
|
|
977
|
+
*
|
|
978
|
+
* A human answering the gate is already saying what they think of the action;
|
|
979
|
+
* this is where they can say it in one word rather than in prose nothing can
|
|
980
|
+
* read back. It is GUIDANCE and never enforcement: the grant record itself is
|
|
981
|
+
* the authorization, and no routing, matching, sampling, budget, token or
|
|
982
|
+
* execution decision reads this field (SPEC.md §11.1 invariant 10). It cannot
|
|
983
|
+
* widen or narrow what the grant authorizes, and a `disliked` grant is exactly
|
|
984
|
+
* as much of a grant as a `loved` one.
|
|
985
|
+
*
|
|
986
|
+
* Ignored on `reject` and `revoke` — the CLI refuses the flag outright there,
|
|
987
|
+
* as a usage error naming `--note` — because their reason is their note and a
|
|
988
|
+
* grade beside a refusal is a second answer to a question with one.
|
|
989
|
+
*/
|
|
990
|
+
reaction?: Reaction;
|
|
991
|
+
/**
|
|
992
|
+
* The surface that collected the decision (amended SPEC.md §8, APRV-324),
|
|
993
|
+
* recorded as the record's top-level `channel`.
|
|
994
|
+
*
|
|
995
|
+
* The base event schema has defined the field since v0.1 and the decision
|
|
996
|
+
* events never set it, so a reader of a grant could not tell a tap on a phone
|
|
997
|
+
* from a line typed into a terminal. Set by the decision surface, from its own
|
|
998
|
+
* name, exactly as `audit.decision_refused` has always set it.
|
|
999
|
+
*/
|
|
1000
|
+
channel?: string;
|
|
1001
|
+
/**
|
|
1002
|
+
* The authenticated sender the `actor` was resolved from (amended SPEC.md
|
|
1003
|
+
* §6.3, APRV-324), recorded as `payload.sender`.
|
|
1004
|
+
*
|
|
1005
|
+
* ABSENT is meaningful and is the common case: it says the attribution came
|
|
1006
|
+
* from the surface's configuration rather than from anything the transport
|
|
1007
|
+
* authenticated, which is how every decision before this key existed was
|
|
1008
|
+
* made. Present, it is the transport's own attribution — never a name, handle
|
|
1009
|
+
* or id a message claimed about itself (§11.1 invariant 4).
|
|
1010
|
+
*
|
|
1011
|
+
* This is a RECORD of how the actor was chosen, and it is not the choosing:
|
|
1012
|
+
* `channels/contract.ts` resolves the sender against the attested policy
|
|
1013
|
+
* before calling this verb, and no check here reads this field. There is no
|
|
1014
|
+
* CLI flag that supplies it, so no agent-reachable surface mints one.
|
|
1015
|
+
*
|
|
1016
|
+
* `hashed` (APRV-370) says the `id` is the operator's keyed digest of the
|
|
1017
|
+
* account rather than the account, which is what a policy mapping senders in
|
|
1018
|
+
* the keyed form records. Present-and-`true` or absent, never `false`: a raw
|
|
1019
|
+
* record is the record this runtime already wrote, and it is written
|
|
1020
|
+
* unchanged.
|
|
1021
|
+
*/
|
|
1022
|
+
sender?: {
|
|
1023
|
+
channel: string;
|
|
1024
|
+
id: string;
|
|
1025
|
+
hashed?: true;
|
|
1026
|
+
};
|
|
1027
|
+
/**
|
|
1028
|
+
* How {@link DecideOptions.sender} became the actor (APRV-324), recorded as
|
|
1029
|
+
* `payload.sender_source`. `policy` is the attested `approvers[id].senders`
|
|
1030
|
+
* mapping, and it is the only member today — a closed vocabulary so a future
|
|
1031
|
+
* source (a signed receipt, APRV-249) is distinguishable in a log rather than
|
|
1032
|
+
* silently mixed in with policy-attested ones.
|
|
1033
|
+
*/
|
|
1034
|
+
senderSource?: "policy";
|
|
1035
|
+
}
|
|
1036
|
+
export type DecideResult = {
|
|
1037
|
+
ok: true;
|
|
1038
|
+
decision: Decision;
|
|
1039
|
+
state: RequestState;
|
|
1040
|
+
record: EventRecord;
|
|
1041
|
+
/**
|
|
1042
|
+
* The raw single-use execution token, on `grant` only (APRV-17). Returned
|
|
1043
|
+
* here and nowhere else: the log carries only its SHA-256, so this value
|
|
1044
|
+
* is unrecoverable once the caller drops it.
|
|
1045
|
+
*
|
|
1046
|
+
* Absent — on a grant that DID mint one — when the request declared
|
|
1047
|
+
* self-delivery and the token was sealed to its address (APRV-211). The
|
|
1048
|
+
* authorization is complete; the granting surface simply has no copy to
|
|
1049
|
+
* print, because the requester is a process that opens the seal itself.
|
|
1050
|
+
* See {@link RequestInput.delivery}.
|
|
1051
|
+
*/
|
|
1052
|
+
token?: string;
|
|
1053
|
+
} | GateRefusal;
|
|
1054
|
+
/**
|
|
1055
|
+
* Record a human decision on a request.
|
|
1056
|
+
*
|
|
1057
|
+
* **Human-only**, enforced here in code and again by the event schema for
|
|
1058
|
+
* grant/reject. `revoke` is human-only too: withdrawing an authorization is a
|
|
1059
|
+
* decision about an authorization, and an agent that could revoke could also
|
|
1060
|
+
* churn the queue.
|
|
1061
|
+
*
|
|
1062
|
+
* Attestation is required **for `grant` only**. Grant is the authorizing
|
|
1063
|
+
* decision, so an unverified policy must not be able to produce one. Reject and
|
|
1064
|
+
* revoke *withdraw* authority, and refusing them on an unattested policy would
|
|
1065
|
+
* leave a live grant standing because a file changed — the strict direction and
|
|
1066
|
+
* the safe direction point the same way, and it is not "refuse everything".
|
|
1067
|
+
*
|
|
1068
|
+
* Attestation also answers a question it could not answer before APRV-118:
|
|
1069
|
+
* *which* policy. The hash the live file matched is compared against the hash
|
|
1070
|
+
* `approval.requested` pinned, and a difference refuses `policy-drift` with
|
|
1071
|
+
* nothing appended. Attestation alone catches an unattested edit; this catches
|
|
1072
|
+
* an attested one, which is the case where every check still passes and the
|
|
1073
|
+
* rules have nonetheless changed underneath a pending question. The hash in
|
|
1074
|
+
* force is then recorded on the grant, so the log states the rules the approver
|
|
1075
|
+
* decided under rather than leaving a reader to assume they were the
|
|
1076
|
+
* requester's.
|
|
1077
|
+
*
|
|
1078
|
+
* Budgets are re-evaluated at grant time. A request may have sat in the queue
|
|
1079
|
+
* while other actions consumed the window, and the moment that matters for a
|
|
1080
|
+
* commitment is the moment the human commits.
|
|
1081
|
+
*
|
|
1082
|
+
* On `grant` a single-use execution token is minted (`core/token.ts`) and its
|
|
1083
|
+
* SHA-256 recorded in the payload as `token_sha256`, **alongside the request's
|
|
1084
|
+
* `payload_hash`** (amended SPEC.md §10, A1). The token is therefore bound to
|
|
1085
|
+
* three things — the request, its `idempotency_key`, and the bytes — and
|
|
1086
|
+
* `core/token.ts` refuses `payload-mismatch` for anything else. The raw token
|
|
1087
|
+
* is returned in `token` and is written nowhere: whoever calls this is the only
|
|
1088
|
+
* party that will ever hold it, and a lost token is unrecoverable by design —
|
|
1089
|
+
* revoke and request again.
|
|
1090
|
+
*/
|
|
1091
|
+
export declare function decide(logPath: string, actionKey: string, decision: Decision, actor: string, options?: DecideOptions): DecideResult;
|
|
1092
|
+
export interface WithdrawOptions extends GateOptions {
|
|
1093
|
+
/** Why the requester is retracting. Defaults to `cancelled`. */
|
|
1094
|
+
reason?: WithdrawReason;
|
|
1095
|
+
/** The requester's free-text elaboration, recorded in the payload. */
|
|
1096
|
+
note?: string;
|
|
1097
|
+
}
|
|
1098
|
+
export type WithdrawResult = {
|
|
1099
|
+
ok: true;
|
|
1100
|
+
state: RequestState;
|
|
1101
|
+
record: EventRecord;
|
|
1102
|
+
} | GateRefusal;
|
|
1103
|
+
/**
|
|
1104
|
+
* Retract a pending request, as the party that opened it (amended SPEC.md §6.3,
|
|
1105
|
+
* APRV-106).
|
|
1106
|
+
*
|
|
1107
|
+
* ## Why the verb exists
|
|
1108
|
+
*
|
|
1109
|
+
* Observed live on 2026-08-19. A builder's `git commit --amend` went through the
|
|
1110
|
+
* Claude Code hook, which classified it manual and appended
|
|
1111
|
+
* `approval.requested`. The hook waited nine minutes, got nothing, denied the
|
|
1112
|
+
* tool call and moved on — but the request stayed pending for the policy's 24h
|
|
1113
|
+
* TTL. Half an hour later the human was pinged on their phone and approved it,
|
|
1114
|
+
* and the grant authorized nothing at all: the hook had long since answered,
|
|
1115
|
+
* and a retried tool call is a new request with a new key. A person spent
|
|
1116
|
+
* attention on a question whose asker had left. SPEC.md §11 makes human
|
|
1117
|
+
* attention the audit budget, and a decision nobody can consume must not be
|
|
1118
|
+
* solicited; so the asker takes the question back.
|
|
1119
|
+
*
|
|
1120
|
+
* ## The four rules
|
|
1121
|
+
*
|
|
1122
|
+
* 1. **Requester-only.** The actor MUST equal the actor of the
|
|
1123
|
+
* `approval.requested` that opened the current cycle, else `not-requester`.
|
|
1124
|
+
* Anything looser would make the approver's queue clearable by whoever
|
|
1125
|
+
* reached the log first. A human who wants a pending request gone rejects
|
|
1126
|
+
* it, on the record, as themselves.
|
|
1127
|
+
* 2. **Pending-only.** `not-requested` when there is nothing to withdraw,
|
|
1128
|
+
* `already-decided` when a human has answered, `request-withdrawn` for a
|
|
1129
|
+
* second withdrawal, `expired` when the TTL has lapsed — and expiry is
|
|
1130
|
+
* judged here exactly as {@link decide} judges it, from the request's own
|
|
1131
|
+
* timestamp, with the same lazy materialisation of the `approval.expired`
|
|
1132
|
+
* record. A lapse is a lapse whether or not an event says so, and a
|
|
1133
|
+
* withdrawal that pretended otherwise would rewrite the reason a request
|
|
1134
|
+
* ended.
|
|
1135
|
+
* 3. **No attestation, no budget.** Withdrawal removes a question; it authorizes
|
|
1136
|
+
* nothing and commits nothing. Refusing it on an unattested policy would
|
|
1137
|
+
* leave requests standing in a human's queue because a file changed, which
|
|
1138
|
+
* is the strict direction pointing the wrong way.
|
|
1139
|
+
* 4. **Compare-and-append, like everything else here.** The legality check and
|
|
1140
|
+
* the write are made against the same head (SPEC.md §11.1(5)), so a grant
|
|
1141
|
+
* that lands in between wins and this withdrawal never overwrites it. Since
|
|
1142
|
+
* APRV-236 the loser of that race re-reads and re-checks rather than
|
|
1143
|
+
* reporting the lost race: the human's answer is on the fresh head, so the
|
|
1144
|
+
* refusal the requester receives is `already-decided`, which is the fact they
|
|
1145
|
+
* need. `tests/concurrency.test.ts` races the two.
|
|
1146
|
+
*
|
|
1147
|
+
* `ts` is assigned at the write boundary from the injected clock, like every
|
|
1148
|
+
* other gate-typed event (SPEC.md §8, A2): there is no parameter to pass one.
|
|
1149
|
+
*/
|
|
1150
|
+
export declare function withdraw(logPath: string, actionKey: string, actor: string, options?: WithdrawOptions): WithdrawResult;
|
|
1151
|
+
/**
|
|
1152
|
+
* What a harness invocation may do with a request some earlier invocation
|
|
1153
|
+
* opened for the very same bytes.
|
|
1154
|
+
*
|
|
1155
|
+
* `pending` — the question is still in front of a human. A retry ADOPTS it and
|
|
1156
|
+
* waits out the remainder, rather than opening a second one: two prompts for
|
|
1157
|
+
* one command spend a human's attention twice on a single question, and
|
|
1158
|
+
* attention is the audit budget (SPEC.md §11).
|
|
1159
|
+
*
|
|
1160
|
+
* `granted` — a human answered, the TTL has not lapsed, and nothing has spent
|
|
1161
|
+
* the grant yet. A retry proceeds on it, once, through
|
|
1162
|
+
* {@link consumeHarnessGrant}.
|
|
1163
|
+
*/
|
|
1164
|
+
export type HarnessCarryKind = "pending" | "granted";
|
|
1165
|
+
/** A request an identical harness invocation may adopt or spend (APRV-117). */
|
|
1166
|
+
export interface HarnessCarry {
|
|
1167
|
+
actionKey: string;
|
|
1168
|
+
task: string | null;
|
|
1169
|
+
kind: HarnessCarryKind;
|
|
1170
|
+
/** `seq` of the `approval.requested` that opened the current cycle. */
|
|
1171
|
+
requestSeq: number | null;
|
|
1172
|
+
/** `seq` of the `approval.granted`, on `granted` only. */
|
|
1173
|
+
decisionSeq: number | null;
|
|
1174
|
+
}
|
|
1175
|
+
export declare function findHarnessCarry(records: EventRecord[], payloadHash: string, cls: string, ts: string, ttlMs: number | null): HarnessCarry | null;
|
|
1176
|
+
/**
|
|
1177
|
+
* `GateOptions` plus the one thing a harness spend states about itself
|
|
1178
|
+
* (APRV-146).
|
|
1179
|
+
*/
|
|
1180
|
+
export interface ConsumeHarnessOptions extends GateOptions {
|
|
1181
|
+
/**
|
|
1182
|
+
* The hash of the payload the harness is about to run (amended SPEC.md §10.4).
|
|
1183
|
+
*
|
|
1184
|
+
* REQUIRED for every spend. It is never read from the log: a value read from
|
|
1185
|
+
* the log would prove nothing, and the whole point is that the process about
|
|
1186
|
+
* to run the command states, independently, what it holds so the runtime can
|
|
1187
|
+
* compare it against what the human approved. Optional in the type and
|
|
1188
|
+
* enforced at the write boundary, exactly as `core/execute.ts` enforces
|
|
1189
|
+
* `presentedPayloadHash`, so omitting it is a machine-readable refusal rather
|
|
1190
|
+
* than a compile error a caller could silence with a placeholder.
|
|
1191
|
+
*/
|
|
1192
|
+
presentedPayloadHash?: string;
|
|
1193
|
+
/**
|
|
1194
|
+
* The task id of the invocation doing the spending (APRV-200).
|
|
1195
|
+
*
|
|
1196
|
+
* Used for one thing and nothing else: to decide whether the recorded
|
|
1197
|
+
* {@link HARNESS_GRANT_ORIGIN} is `direct` or `carried`. It never gates the
|
|
1198
|
+
* spend, never changes a refusal, and never appears on the record itself.
|
|
1199
|
+
*
|
|
1200
|
+
* It is a CLAIM, in §9's computed-versus-claimed vocabulary, and it is bounded
|
|
1201
|
+
* the way §11.1 invariant 4 bounds every claim: the only value it can produce
|
|
1202
|
+
* unaided is the one that ADDS scrutiny. `direct` is reachable solely by
|
|
1203
|
+
* presenting the task the request record already carries, which is a fact the
|
|
1204
|
+
* gate reads out of the verified log rather than out of the caller; anything
|
|
1205
|
+
* else, absence included, records `carried`.
|
|
1206
|
+
*/
|
|
1207
|
+
spendingTask?: string;
|
|
1208
|
+
}
|
|
1209
|
+
/**
|
|
1210
|
+
* How the authorization reached the process that spent it (APRV-200).
|
|
1211
|
+
*
|
|
1212
|
+
* `direct` — the tool call that spent this grant is the tool call that asked for
|
|
1213
|
+
* it. One process opened the request, waited, saw the decision and proceeded, so
|
|
1214
|
+
* the gate observed the whole ordering: nothing this runtime authorized could
|
|
1215
|
+
* have run before the human answered.
|
|
1216
|
+
*
|
|
1217
|
+
* `carried` — a LATER tool call spent it, under APRV-117's carryover or its
|
|
1218
|
+
* adoption sibling. The asking invocation had already returned a verdict (a
|
|
1219
|
+
* `hook-timeout` deny, which leaves the request open), and whether the harness
|
|
1220
|
+
* honoured that verdict is a fact this runtime never observes: it decides, and
|
|
1221
|
+
* the harness executes. So the ordering the record implies — grant, then
|
|
1222
|
+
* execution — is only guaranteed for `direct`, and this marker is what lets an
|
|
1223
|
+
* auditor tell the two apart from the committed log alone. See
|
|
1224
|
+
* `docs/claude-code-hook.md`, "When the grant can follow the write".
|
|
1225
|
+
*
|
|
1226
|
+
* Absent on an `execution.started` that names no `grant_seq`: an unattended
|
|
1227
|
+
* execution has no grant, so it has no origin to report (SPEC.md §6.3).
|
|
1228
|
+
*/
|
|
1229
|
+
export type HarnessGrantOrigin = "direct" | "carried";
|
|
1230
|
+
/** The payload field {@link HarnessGrantOrigin} is recorded under. */
|
|
1231
|
+
export declare const HARNESS_GRANT_ORIGIN = "grant_origin";
|
|
1232
|
+
/**
|
|
1233
|
+
* The payload field naming the TOOL CALL that spent a carried grant (APRV-287).
|
|
1234
|
+
*
|
|
1235
|
+
* A carried grant's `execution.started` names the task of the request, because
|
|
1236
|
+
* that is the task the log holds the approval lifecycle under. The tool call
|
|
1237
|
+
* that actually ran the command is a different one, and until this field the
|
|
1238
|
+
* runtime had no way back to it: the completion counterpart rebuilds a task id
|
|
1239
|
+
* from the reporting event's session and tool-use id, found no start under it,
|
|
1240
|
+
* and refused `not-delegated`. The consequence was the one an operator saw on
|
|
1241
|
+
* 2026-09-06 — a granted commit-and-push completed, no `execution.completed`
|
|
1242
|
+
* was ever written, and the loop floor the refusal text promises would clear on
|
|
1243
|
+
* a completion stayed shut over the rest of the session.
|
|
1244
|
+
*
|
|
1245
|
+
* DERIVED, never declared: the value is the task id the runtime minted for the
|
|
1246
|
+
* spending invocation from the harness's session and tool-use ids, the same one
|
|
1247
|
+
* {@link HARNESS_GRANT_ORIGIN} is computed against. A reporter cannot name a
|
|
1248
|
+
* bucket with it, because the only thing it can reach is a start this runtime
|
|
1249
|
+
* wrote for that same tool call.
|
|
1250
|
+
*
|
|
1251
|
+
* Absent where the spend is `direct` (the record's own `task` already names the
|
|
1252
|
+
* tool call) and on every record written before this field existed, which is why
|
|
1253
|
+
* every reader treats absence as "no second name" rather than as a fault.
|
|
1254
|
+
*/
|
|
1255
|
+
export declare const HARNESS_SPENDING_TASK = "spent_by_task";
|
|
1256
|
+
export type ConsumeHarnessResult = {
|
|
1257
|
+
ok: true;
|
|
1258
|
+
record: EventRecord;
|
|
1259
|
+
} | GateRefusal;
|
|
1260
|
+
/**
|
|
1261
|
+
* Spend a harness grant, exactly once (APRV-117).
|
|
1262
|
+
*
|
|
1263
|
+
* ## Why this is `execution.started`, and why it is alone
|
|
1264
|
+
*
|
|
1265
|
+
* A harness grant mints no token (APRV-106), so nothing in `core/token.ts`
|
|
1266
|
+
* records that it was used, and without such a record a grant could authorize
|
|
1267
|
+
* an unbounded number of identical retries for the whole TTL. The consumption
|
|
1268
|
+
* marker has to be a real event through compare-and-append (SPEC.md §11.1(5)),
|
|
1269
|
+
* and it has to be one the gate already reads as terminal for an idempotency
|
|
1270
|
+
* key. `execution.started` is exactly that: {@link request} refuses a key that
|
|
1271
|
+
* has one as `already-executed`, and {@link decide} refuses to revoke past it.
|
|
1272
|
+
* Reusing it means the single-use rule is the gate's existing rule rather than
|
|
1273
|
+
* a second one written next to it.
|
|
1274
|
+
*
|
|
1275
|
+
* **No `execution.completed` or `execution.failed` follows, ever.** The harness
|
|
1276
|
+
* runs the command; this runtime hands over permission and never observes an
|
|
1277
|
+
* exit status. Appending a completion would fabricate an outcome, and in this
|
|
1278
|
+
* vocabulary it would also assert something with consequences — an
|
|
1279
|
+
* `execution.completed` clears a task's loop-escalation streak (SPEC.md §10.2).
|
|
1280
|
+
* A harness execution is therefore recorded as begun and never as finished,
|
|
1281
|
+
* which is precisely what the runtime knows. The `execution: "harness"` marker
|
|
1282
|
+
* on the payload says so on the record itself, so a reader of the start event
|
|
1283
|
+
* alone can see why no outcome ever lands.
|
|
1284
|
+
*
|
|
1285
|
+
* ## What it refuses
|
|
1286
|
+
*
|
|
1287
|
+
* Attestation is checked here, and not as a formality: this is the one
|
|
1288
|
+
* enforcement path that reaches a harness `allow` without passing through
|
|
1289
|
+
* {@link request} (that happened in an earlier process, possibly against
|
|
1290
|
+
* earlier policy bytes). A policy that changed since the human attested it
|
|
1291
|
+
* cannot answer anything, so it answers nothing. `policy-drift` is the second
|
|
1292
|
+
* half of the same idea and is APRV-134: attested is not enough when what is
|
|
1293
|
+
* attested is a DIFFERENT policy from the one the approver decided under, and
|
|
1294
|
+
* the gap between a tap and a retry's spend is exactly where a re-attestation
|
|
1295
|
+
* fits.
|
|
1296
|
+
*
|
|
1297
|
+
* Everything else follows the derivation: `not-requested` when the key has no
|
|
1298
|
+
* request, `expired` when the TTL lapsed (judged from the request's own `ts`,
|
|
1299
|
+
* event or no event, exactly as {@link decide} judges it), `already-executed`
|
|
1300
|
+
* when something already spent it, `not-granted` for every other state and for
|
|
1301
|
+
* a grant that is not harness-executed. The content binding is checked last and
|
|
1302
|
+
* refuses twice over (APRV-146): `payload-hash-required` when the grant records
|
|
1303
|
+
* no bytes or the consumer states none, `payload-mismatch` when the bytes stated
|
|
1304
|
+
* are not the bytes approved. Budgets are not re-evaluated: the
|
|
1305
|
+
* authorization was charged at `approval.granted`, and `core/budgets.ts`'s
|
|
1306
|
+
* consumption contract already dedupes a start event against a grant carrying
|
|
1307
|
+
* the same `action_key`.
|
|
1308
|
+
*
|
|
1309
|
+
* ## What the record says about ORDER (APRV-200)
|
|
1310
|
+
*
|
|
1311
|
+
* The start carries `grant_origin`, which answers a question the log could not
|
|
1312
|
+
* previously be asked: was the tool call that spent this grant the tool call
|
|
1313
|
+
* that asked for it? `direct` says yes, and the gate observed the whole ordering
|
|
1314
|
+
* in one process. `carried` says a LATER invocation spent it — APRV-117's
|
|
1315
|
+
* carryover, or its adoption sibling — which means the asking invocation had
|
|
1316
|
+
* already returned a verdict, and this runtime never sees whether the harness
|
|
1317
|
+
* honoured it. A grant that arrives after the effect it names is a ratification
|
|
1318
|
+
* and not an approval, and `carried` is the window in which that is possible.
|
|
1319
|
+
* See {@link HarnessGrantOrigin} and `docs/claude-code-hook.md`.
|
|
1320
|
+
*/
|
|
1321
|
+
export declare function consumeHarnessGrant(logPath: string, actionKey: string, actor: string, options?: ConsumeHarnessOptions): ConsumeHarnessResult;
|
|
1322
|
+
/** What the harness is about to run, as one class of one command. */
|
|
1323
|
+
export interface HarnessStartInput {
|
|
1324
|
+
task: string;
|
|
1325
|
+
actionKey: string;
|
|
1326
|
+
cls: string;
|
|
1327
|
+
/**
|
|
1328
|
+
* The hash of the bytes the verdict was computed over, and which are about to
|
|
1329
|
+
* run (amended SPEC.md §6.2/§10.4, APRV-140).
|
|
1330
|
+
*
|
|
1331
|
+
* REQUIRED. A start event that cannot say WHAT ran records only that something
|
|
1332
|
+
* did, which is the whole of what APRV-140 set out to fix and is exactly the
|
|
1333
|
+
* state the harness path was left in. Optional in the type and enforced at the
|
|
1334
|
+
* write boundary, on the reading `core/execute.ts` gives
|
|
1335
|
+
* `presentedPayloadHash`: a caller that omits it is refused
|
|
1336
|
+
* `payload-hash-required` and told what to compute, rather than turned away by
|
|
1337
|
+
* the compiler and left to satisfy it with a placeholder.
|
|
1338
|
+
*/
|
|
1339
|
+
payload_hash?: string;
|
|
1340
|
+
/** Canonical decimal USD string (APRV-121); a JSON number is read as the historical form. */
|
|
1341
|
+
est_cost_usd?: UsdInput;
|
|
1342
|
+
}
|
|
1343
|
+
export type HarnessStartResult = {
|
|
1344
|
+
ok: true;
|
|
1345
|
+
record: EventRecord;
|
|
1346
|
+
} | GateRefusal;
|
|
1347
|
+
/**
|
|
1348
|
+
* Charge and record a harness execution that no human was asked about
|
|
1349
|
+
* (APRV-141).
|
|
1350
|
+
*
|
|
1351
|
+
* ## The blind spot this closes
|
|
1352
|
+
*
|
|
1353
|
+
* `core/budgets.ts` computes consumption from `approval.granted` and
|
|
1354
|
+
* `execution.started`, and `core/audit.ts` draws its retrospective sample from
|
|
1355
|
+
* `execution.started` alone. The harness hook wrote neither for a supervised or
|
|
1356
|
+
* autonomous verdict — the comment said, correctly, that writing one per agent
|
|
1357
|
+
* action fills the log — so under Claude Code the majority of real activity
|
|
1358
|
+
* consumed no budget, `daily_actions` included, and was invisible to the
|
|
1359
|
+
* overseer that exists to read a sample of it. A budget that the busiest
|
|
1360
|
+
* execution path does not charge is not a budget, and the decision recorded on
|
|
1361
|
+
* APRV-141 is that the log volume is the lesser cost.
|
|
1362
|
+
*
|
|
1363
|
+
* ## Why this record and not a new event type
|
|
1364
|
+
*
|
|
1365
|
+
* It is the same `execution.started` {@link consumeHarnessGrant} appends, with
|
|
1366
|
+
* the same `execution: "harness"` marker saying why no `execution.completed` or
|
|
1367
|
+
* `execution.failed` will ever follow: the harness runs the command and this
|
|
1368
|
+
* runtime never observes an exit status. Reusing the shape means budgets and
|
|
1369
|
+
* audit count these without learning a second vocabulary, and the gate's
|
|
1370
|
+
* existing single-use rule (a key with an `execution.started` is
|
|
1371
|
+
* `already-executed`) applies unchanged. What differs is only the authorization
|
|
1372
|
+
* being recorded: there, a human's grant; here, the policy itself.
|
|
1373
|
+
*
|
|
1374
|
+
* ## What it refuses
|
|
1375
|
+
*
|
|
1376
|
+
* The same two facts the hook's own guard checks and `core/execute.ts` checks
|
|
1377
|
+
* before an unattended start — attestation and loop-escalation — re-checked at
|
|
1378
|
+
* the write boundary against the records this append is authorized by, plus the
|
|
1379
|
+
* budget verdict this record is the charge for. A class that resolves `manual`
|
|
1380
|
+
* is refused outright: a manual action is authorized by a grant and spent
|
|
1381
|
+
* through {@link consumeHarnessGrant} or a token, and admitting one here would
|
|
1382
|
+
* be a second, unapproved spender.
|
|
1383
|
+
*
|
|
1384
|
+
* Since APRV-146 the content binding is refused here too. `payload-hash-required`
|
|
1385
|
+
* says the caller named no bytes, and it is a refusal rather than an omitted
|
|
1386
|
+
* field because a start event with no `payload_hash` is a record that says
|
|
1387
|
+
* something ran without saying what — the state APRV-140 closed everywhere else.
|
|
1388
|
+
*/
|
|
1389
|
+
export declare function startHarnessExecution(logPath: string, input: HarnessStartInput, actor: string, options?: GateOptions): HarnessStartResult;
|
|
1390
|
+
/**
|
|
1391
|
+
* Which untrusted reporter asserted a harness outcome. CLOSED, and extended only
|
|
1392
|
+
* by a task that adds the case.
|
|
1393
|
+
*
|
|
1394
|
+
* It names the reporter and reduces nothing: it is a CLAIMED field in the
|
|
1395
|
+
* computed-versus-claimed vocabulary of SPEC.md §9, recorded so a reader can
|
|
1396
|
+
* tell a report from an observation without reading the record's provenance out
|
|
1397
|
+
* of its shape.
|
|
1398
|
+
*/
|
|
1399
|
+
export declare const HARNESS_REPORTERS: readonly ["post-tool-use"];
|
|
1400
|
+
export type HarnessReporter = (typeof HARNESS_REPORTERS)[number];
|
|
1401
|
+
export declare function isHarnessReporter(value: unknown): value is HarnessReporter;
|
|
1402
|
+
/** What a harness says happened to one tool call. */
|
|
1403
|
+
export interface HarnessFinishInput {
|
|
1404
|
+
/** The harness's identifier for the run of tool calls. */
|
|
1405
|
+
sessionId: string;
|
|
1406
|
+
/** The harness's identifier for this tool call. */
|
|
1407
|
+
toolUseId: string;
|
|
1408
|
+
/** Read from the reporting event by a CLOSED set of readings; never guessed. */
|
|
1409
|
+
outcome: "completed" | "failed";
|
|
1410
|
+
reportedBy: HarnessReporter;
|
|
1411
|
+
/**
|
|
1412
|
+
* The exit code, when the harness stated one, and `null` otherwise.
|
|
1413
|
+
*
|
|
1414
|
+
* Claude Code's post-execution event carries none (`docs/claude-code-hook.md`),
|
|
1415
|
+
* so in practice this is `null` on that adapter. It is `null` rather than
|
|
1416
|
+
* omitted-and-inferred for the reason `execution.indeterminate` carries a null
|
|
1417
|
+
* one: a fabricated number reads exactly like a measured one.
|
|
1418
|
+
*/
|
|
1419
|
+
exitCode?: number | null;
|
|
1420
|
+
}
|
|
1421
|
+
export type HarnessFinishResult = {
|
|
1422
|
+
ok: true;
|
|
1423
|
+
task: string;
|
|
1424
|
+
records: EventRecord[];
|
|
1425
|
+
} | GateRefusal;
|
|
1426
|
+
export declare function finishHarnessExecution(logPath: string, input: HarnessFinishInput, actor: string, options?: GateOptions): HarnessFinishResult;
|
|
1427
|
+
export type ExpireResult = {
|
|
1428
|
+
ok: true;
|
|
1429
|
+
record: EventRecord;
|
|
1430
|
+
} | GateRefusal;
|
|
1431
|
+
/**
|
|
1432
|
+
* Append `approval.expired` for a live request whose TTL has lapsed.
|
|
1433
|
+
*
|
|
1434
|
+
* The system verb: no human decides an expiry, so the actor is
|
|
1435
|
+
* {@link EXPIRY_ACTOR} and there is no identity to resolve. Used by the daemon's
|
|
1436
|
+
* sweep (M5) and by tests; `decide` performs the same append itself when it
|
|
1437
|
+
* discovers a lapse first.
|
|
1438
|
+
*
|
|
1439
|
+
* Refuses when the request is not live (`not-requested`, `already-decided`) or
|
|
1440
|
+
* when the TTL has not lapsed (`not-expired`, which also covers a policy that
|
|
1441
|
+
* declares no `defaults.approval_ttl` — no TTL means no lapse, and expiring a
|
|
1442
|
+
* request the policy never bounded would be the runtime inventing a deadline).
|
|
1443
|
+
*
|
|
1444
|
+
* `defaults.on_expiry` is recorded in the payload. Its only v0.1 value,
|
|
1445
|
+
* `reject`, does not change the mechanics here — an expired request is terminal
|
|
1446
|
+
* either way — it tells the projection layer to render the envelope's `state:`
|
|
1447
|
+
* as `rejected`.
|
|
1448
|
+
*/
|
|
1449
|
+
export declare function expire(logPath: string, actionKey: string, options?: GateOptions): ExpireResult;
|