approval-md 0.0.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +176 -0
- package/NOTICE +5 -0
- package/README.md +940 -4
- package/SPEC.md +476 -34
- package/cli.js +29 -3
- package/dist/src/adapters/agentmail.d.ts +426 -0
- package/dist/src/adapters/agentmail.js +1200 -0
- package/dist/src/adapters/agentmail.js.map +1 -0
- package/dist/src/adapters/conformance.d.ts +149 -0
- package/dist/src/adapters/conformance.js +461 -0
- package/dist/src/adapters/conformance.js.map +1 -0
- package/dist/src/adapters/contract.d.ts +628 -0
- package/dist/src/adapters/contract.js +1035 -0
- package/dist/src/adapters/contract.js.map +1 -0
- package/dist/src/adapters/email.d.ts +324 -0
- package/dist/src/adapters/email.js +749 -0
- package/dist/src/adapters/email.js.map +1 -0
- package/dist/src/adapters/env-passphrase.d.ts +93 -0
- package/dist/src/adapters/env-passphrase.js +132 -0
- package/dist/src/adapters/env-passphrase.js.map +1 -0
- package/dist/src/adapters/public.d.ts +11 -0
- package/dist/src/adapters/public.js +11 -0
- package/dist/src/adapters/public.js.map +1 -0
- package/dist/src/adapters/registry.d.ts +59 -0
- package/dist/src/adapters/registry.js +77 -0
- package/dist/src/adapters/registry.js.map +1 -0
- package/dist/src/adapters/smtp.d.ts +213 -0
- package/dist/src/adapters/smtp.js +499 -0
- package/dist/src/adapters/smtp.js.map +1 -0
- package/dist/src/adapters/vault-provider.d.ts +114 -0
- package/dist/src/adapters/vault-provider.js +161 -0
- package/dist/src/adapters/vault-provider.js.map +1 -0
- package/dist/src/adapters/zzz.d.ts +66 -0
- package/dist/src/adapters/zzz.js +299 -0
- package/dist/src/adapters/zzz.js.map +1 -0
- package/dist/src/channels/batch.d.ts +109 -0
- package/dist/src/channels/batch.js +121 -0
- package/dist/src/channels/batch.js.map +1 -0
- package/dist/src/channels/cli.d.ts +193 -0
- package/dist/src/channels/cli.js +468 -0
- package/dist/src/channels/cli.js.map +1 -0
- package/dist/src/channels/conformance.d.ts +92 -0
- package/dist/src/channels/conformance.js +445 -0
- package/dist/src/channels/conformance.js.map +1 -0
- package/dist/src/channels/contract.d.ts +623 -0
- package/dist/src/channels/contract.js +494 -0
- package/dist/src/channels/contract.js.map +1 -0
- package/dist/src/channels/payload-view.d.ts +35 -0
- package/dist/src/channels/payload-view.js +43 -0
- package/dist/src/channels/payload-view.js.map +1 -0
- package/dist/src/channels/render-queue.d.ts +149 -0
- package/dist/src/channels/render-queue.js +564 -0
- package/dist/src/channels/render-queue.js.map +1 -0
- package/dist/src/channels/tagging.d.ts +196 -0
- package/dist/src/channels/tagging.js +723 -0
- package/dist/src/channels/tagging.js.map +1 -0
- package/dist/src/channels/telegram.d.ts +1832 -0
- package/dist/src/channels/telegram.js +3190 -0
- package/dist/src/channels/telegram.js.map +1 -0
- package/dist/src/channels/web.d.ts +341 -0
- package/dist/src/channels/web.js +903 -0
- package/dist/src/channels/web.js.map +1 -0
- package/dist/src/cli/adapter.d.ts +90 -0
- package/dist/src/cli/adapter.js +288 -0
- package/dist/src/cli/adapter.js.map +1 -0
- package/dist/src/cli/amend.d.ts +59 -0
- package/dist/src/cli/amend.js +2171 -0
- package/dist/src/cli/amend.js.map +1 -0
- package/dist/src/cli/args.d.ts +43 -0
- package/dist/src/cli/args.js +86 -0
- package/dist/src/cli/args.js.map +1 -0
- package/dist/src/cli/attest.d.ts +41 -0
- package/dist/src/cli/attest.js +307 -0
- package/dist/src/cli/attest.js.map +1 -0
- package/dist/src/cli/audit-card.d.ts +62 -0
- package/dist/src/cli/audit-card.js +201 -0
- package/dist/src/cli/audit-card.js.map +1 -0
- package/dist/src/cli/audit.d.ts +59 -0
- package/dist/src/cli/audit.js +460 -0
- package/dist/src/cli/audit.js.map +1 -0
- package/dist/src/cli/channel-telegram.d.ts +806 -0
- package/dist/src/cli/channel-telegram.js +2063 -0
- package/dist/src/cli/channel-telegram.js.map +1 -0
- package/dist/src/cli/channel-web.d.ts +131 -0
- package/dist/src/cli/channel-web.js +357 -0
- package/dist/src/cli/channel-web.js.map +1 -0
- package/dist/src/cli/channel.d.ts +71 -0
- package/dist/src/cli/channel.js +438 -0
- package/dist/src/cli/channel.js.map +1 -0
- package/dist/src/cli/checkpoint-tap.d.ts +169 -0
- package/dist/src/cli/checkpoint-tap.js +238 -0
- package/dist/src/cli/checkpoint-tap.js.map +1 -0
- package/dist/src/cli/codex.d.ts +2 -0
- package/dist/src/cli/codex.js +172 -0
- package/dist/src/cli/codex.js.map +1 -0
- package/dist/src/cli/coverage.d.ts +61 -0
- package/dist/src/cli/coverage.js +343 -0
- package/dist/src/cli/coverage.js.map +1 -0
- package/dist/src/cli/daemon.d.ts +120 -0
- package/dist/src/cli/daemon.js +631 -0
- package/dist/src/cli/daemon.js.map +1 -0
- package/dist/src/cli/doctor.d.ts +129 -0
- package/dist/src/cli/doctor.js +2762 -0
- package/dist/src/cli/doctor.js.map +1 -0
- package/dist/src/cli/env.d.ts +65 -0
- package/dist/src/cli/env.js +302 -0
- package/dist/src/cli/env.js.map +1 -0
- package/dist/src/cli/execute.d.ts +202 -0
- package/dist/src/cli/execute.js +1682 -0
- package/dist/src/cli/execute.js.map +1 -0
- package/dist/src/cli/exit-codes.d.ts +73 -0
- package/dist/src/cli/exit-codes.js +82 -0
- package/dist/src/cli/exit-codes.js.map +1 -0
- package/dist/src/cli/feedback.d.ts +60 -0
- package/dist/src/cli/feedback.js +205 -0
- package/dist/src/cli/feedback.js.map +1 -0
- package/dist/src/cli/gate-window.d.ts +40 -0
- package/dist/src/cli/gate-window.js +294 -0
- package/dist/src/cli/gate-window.js.map +1 -0
- package/dist/src/cli/gate.d.ts +68 -0
- package/dist/src/cli/gate.js +557 -0
- package/dist/src/cli/gate.js.map +1 -0
- package/dist/src/cli/git-scope.d.ts +190 -0
- package/dist/src/cli/git-scope.js +295 -0
- package/dist/src/cli/git-scope.js.map +1 -0
- package/dist/src/cli/gloss-attach.d.ts +85 -0
- package/dist/src/cli/gloss-attach.js +107 -0
- package/dist/src/cli/gloss-attach.js.map +1 -0
- package/dist/src/cli/gloss-codex-child.d.ts +9 -0
- package/dist/src/cli/gloss-codex-child.js +149 -0
- package/dist/src/cli/gloss-codex-child.js.map +1 -0
- package/dist/src/cli/gloss-codex.d.ts +24 -0
- package/dist/src/cli/gloss-codex.js +255 -0
- package/dist/src/cli/gloss-codex.js.map +1 -0
- package/dist/src/cli/gloss-options.d.ts +42 -0
- package/dist/src/cli/gloss-options.js +79 -0
- package/dist/src/cli/gloss-options.js.map +1 -0
- package/dist/src/cli/gloss.d.ts +265 -0
- package/dist/src/cli/gloss.js +362 -0
- package/dist/src/cli/gloss.js.map +1 -0
- package/dist/src/cli/help.d.ts +103 -0
- package/dist/src/cli/help.js +2339 -0
- package/dist/src/cli/help.js.map +1 -0
- package/dist/src/cli/hook-codex.d.ts +78 -0
- package/dist/src/cli/hook-codex.js +167 -0
- package/dist/src/cli/hook-codex.js.map +1 -0
- package/dist/src/cli/hook.d.ts +331 -0
- package/dist/src/cli/hook.js +2849 -0
- package/dist/src/cli/hook.js.map +1 -0
- package/dist/src/cli/import.d.ts +35 -0
- package/dist/src/cli/import.js +175 -0
- package/dist/src/cli/import.js.map +1 -0
- package/dist/src/cli/init.d.ts +84 -0
- package/dist/src/cli/init.js +336 -0
- package/dist/src/cli/init.js.map +1 -0
- package/dist/src/cli/instructions.d.ts +23 -0
- package/dist/src/cli/instructions.js +262 -0
- package/dist/src/cli/instructions.js.map +1 -0
- package/dist/src/cli/journal.d.ts +41 -0
- package/dist/src/cli/journal.js +238 -0
- package/dist/src/cli/journal.js.map +1 -0
- package/dist/src/cli/log-advance.d.ts +287 -0
- package/dist/src/cli/log-advance.js +840 -0
- package/dist/src/cli/log-advance.js.map +1 -0
- package/dist/src/cli/log-anchor.d.ts +176 -0
- package/dist/src/cli/log-anchor.js +387 -0
- package/dist/src/cli/log-anchor.js.map +1 -0
- package/dist/src/cli/log-checkpoint.d.ts +22 -0
- package/dist/src/cli/log-checkpoint.js +128 -0
- package/dist/src/cli/log-checkpoint.js.map +1 -0
- package/dist/src/cli/log-sync.d.ts +243 -0
- package/dist/src/cli/log-sync.js +849 -0
- package/dist/src/cli/log-sync.js.map +1 -0
- package/dist/src/cli/log-verbs.d.ts +16 -0
- package/dist/src/cli/log-verbs.js +360 -0
- package/dist/src/cli/log-verbs.js.map +1 -0
- package/dist/src/cli/long-help.d.ts +70 -0
- package/dist/src/cli/long-help.js +148 -0
- package/dist/src/cli/long-help.js.map +1 -0
- package/dist/src/cli/main.d.ts +77 -0
- package/dist/src/cli/main.js +1206 -0
- package/dist/src/cli/main.js.map +1 -0
- package/dist/src/cli/mcp.d.ts +52 -0
- package/dist/src/cli/mcp.js +306 -0
- package/dist/src/cli/mcp.js.map +1 -0
- package/dist/src/cli/paths.d.ts +56 -0
- package/dist/src/cli/paths.js +79 -0
- package/dist/src/cli/paths.js.map +1 -0
- package/dist/src/cli/payload.d.ts +58 -0
- package/dist/src/cli/payload.js +253 -0
- package/dist/src/cli/payload.js.map +1 -0
- package/dist/src/cli/policy.d.ts +43 -0
- package/dist/src/cli/policy.js +229 -0
- package/dist/src/cli/policy.js.map +1 -0
- package/dist/src/cli/preflight.d.ts +363 -0
- package/dist/src/cli/preflight.js +1175 -0
- package/dist/src/cli/preflight.js.map +1 -0
- package/dist/src/cli/progress.d.ts +78 -0
- package/dist/src/cli/progress.js +112 -0
- package/dist/src/cli/progress.js.map +1 -0
- package/dist/src/cli/prompt.d.ts +209 -0
- package/dist/src/cli/prompt.js +312 -0
- package/dist/src/cli/prompt.js.map +1 -0
- package/dist/src/cli/quickstart.d.ts +46 -0
- package/dist/src/cli/quickstart.js +297 -0
- package/dist/src/cli/quickstart.js.map +1 -0
- package/dist/src/cli/records.d.ts +34 -0
- package/dist/src/cli/records.js +66 -0
- package/dist/src/cli/records.js.map +1 -0
- package/dist/src/cli/render.d.ts +22 -0
- package/dist/src/cli/render.js +132 -0
- package/dist/src/cli/render.js.map +1 -0
- package/dist/src/cli/sandbox.d.ts +51 -0
- package/dist/src/cli/sandbox.js +150 -0
- package/dist/src/cli/sandbox.js.map +1 -0
- package/dist/src/cli/scaffold.d.ts +79 -0
- package/dist/src/cli/scaffold.js +137 -0
- package/dist/src/cli/scaffold.js.map +1 -0
- package/dist/src/cli/setup-adapter.d.ts +137 -0
- package/dist/src/cli/setup-adapter.js +509 -0
- package/dist/src/cli/setup-adapter.js.map +1 -0
- package/dist/src/cli/setup-channel.d.ts +117 -0
- package/dist/src/cli/setup-channel.js +635 -0
- package/dist/src/cli/setup-channel.js.map +1 -0
- package/dist/src/cli/setup-checkpoint.d.ts +57 -0
- package/dist/src/cli/setup-checkpoint.js +196 -0
- package/dist/src/cli/setup-checkpoint.js.map +1 -0
- package/dist/src/cli/setup-common.d.ts +275 -0
- package/dist/src/cli/setup-common.js +376 -0
- package/dist/src/cli/setup-common.js.map +1 -0
- package/dist/src/cli/setup-flow.d.ts +287 -0
- package/dist/src/cli/setup-flow.js +476 -0
- package/dist/src/cli/setup-flow.js.map +1 -0
- package/dist/src/cli/setup-service.d.ts +96 -0
- package/dist/src/cli/setup-service.js +308 -0
- package/dist/src/cli/setup-service.js.map +1 -0
- package/dist/src/cli/setup.d.ts +202 -0
- package/dist/src/cli/setup.js +473 -0
- package/dist/src/cli/setup.js.map +1 -0
- package/dist/src/cli/style.d.ts +320 -0
- package/dist/src/cli/style.js +469 -0
- package/dist/src/cli/style.js.map +1 -0
- package/dist/src/cli/token.d.ts +39 -0
- package/dist/src/cli/token.js +274 -0
- package/dist/src/cli/token.js.map +1 -0
- package/dist/src/cli/up.d.ts +155 -0
- package/dist/src/cli/up.js +849 -0
- package/dist/src/cli/up.js.map +1 -0
- package/dist/src/cli/usage.d.ts +37 -0
- package/dist/src/cli/usage.js +91 -0
- package/dist/src/cli/usage.js.map +1 -0
- package/dist/src/cli/values.d.ts +40 -0
- package/dist/src/cli/values.js +189 -0
- package/dist/src/cli/values.js.map +1 -0
- package/dist/src/cli/vault.d.ts +59 -0
- package/dist/src/cli/vault.js +362 -0
- package/dist/src/cli/vault.js.map +1 -0
- package/dist/src/cli/verb-registry.d.ts +76 -0
- package/dist/src/cli/verb-registry.js +2341 -0
- package/dist/src/cli/verb-registry.js.map +1 -0
- package/dist/src/cli/wordmark.d.ts +31 -0
- package/dist/src/cli/wordmark.js +52 -0
- package/dist/src/cli/wordmark.js.map +1 -0
- package/dist/src/codex/doctor.d.ts +13 -0
- package/dist/src/codex/doctor.js +41 -0
- package/dist/src/codex/doctor.js.map +1 -0
- package/dist/src/codex/manifest.d.ts +49 -0
- package/dist/src/codex/manifest.js +103 -0
- package/dist/src/codex/manifest.js.map +1 -0
- package/dist/src/codex/templates.d.ts +41 -0
- package/dist/src/codex/templates.js +319 -0
- package/dist/src/codex/templates.js.map +1 -0
- package/dist/src/codex/trust.d.ts +19 -0
- package/dist/src/codex/trust.js +183 -0
- package/dist/src/codex/trust.js.map +1 -0
- package/dist/src/codex/workspace-plan.d.ts +131 -0
- package/dist/src/codex/workspace-plan.js +561 -0
- package/dist/src/codex/workspace-plan.js.map +1 -0
- package/dist/src/core/actor.d.ts +2 -0
- package/dist/src/core/actor.js +5 -0
- package/dist/src/core/actor.js.map +1 -0
- package/dist/src/core/advance-cycle.d.ts +170 -0
- package/dist/src/core/advance-cycle.js +200 -0
- package/dist/src/core/advance-cycle.js.map +1 -0
- package/dist/src/core/agents-md.d.ts +276 -0
- package/dist/src/core/agents-md.js +747 -0
- package/dist/src/core/agents-md.js.map +1 -0
- package/dist/src/core/apply-patch.d.ts +49 -0
- package/dist/src/core/apply-patch.js +266 -0
- package/dist/src/core/apply-patch.js.map +1 -0
- package/dist/src/core/attest.d.ts +420 -0
- package/dist/src/core/attest.js +589 -0
- package/dist/src/core/attest.js.map +1 -0
- package/dist/src/core/audit.d.ts +492 -0
- package/dist/src/core/audit.js +882 -0
- package/dist/src/core/audit.js.map +1 -0
- package/dist/src/core/budgets.d.ts +238 -0
- package/dist/src/core/budgets.js +449 -0
- package/dist/src/core/budgets.js.map +1 -0
- package/dist/src/core/checkpoint.d.ts +500 -0
- package/dist/src/core/checkpoint.js +738 -0
- package/dist/src/core/checkpoint.js.map +1 -0
- package/dist/src/core/child-env.d.ts +88 -0
- package/dist/src/core/child-env.js +86 -0
- package/dist/src/core/child-env.js.map +1 -0
- package/dist/src/core/clock.d.ts +52 -0
- package/dist/src/core/clock.js +43 -0
- package/dist/src/core/clock.js.map +1 -0
- package/dist/src/core/command-class.d.ts +543 -0
- package/dist/src/core/command-class.js +2356 -0
- package/dist/src/core/command-class.js.map +1 -0
- package/dist/src/core/coverage-sources/adapter.d.ts +40 -0
- package/dist/src/core/coverage-sources/adapter.js +71 -0
- package/dist/src/core/coverage-sources/adapter.js.map +1 -0
- package/dist/src/core/coverage-sources/gh.d.ts +48 -0
- package/dist/src/core/coverage-sources/gh.js +136 -0
- package/dist/src/core/coverage-sources/gh.js.map +1 -0
- package/dist/src/core/coverage-sources/git.d.ts +101 -0
- package/dist/src/core/coverage-sources/git.js +269 -0
- package/dist/src/core/coverage-sources/git.js.map +1 -0
- package/dist/src/core/coverage.d.ts +217 -0
- package/dist/src/core/coverage.js +337 -0
- package/dist/src/core/coverage.js.map +1 -0
- package/dist/src/core/credential-spec.d.ts +72 -0
- package/dist/src/core/credential-spec.js +23 -0
- package/dist/src/core/credential-spec.js.map +1 -0
- package/dist/src/core/dark-session.d.ts +331 -0
- package/dist/src/core/dark-session.js +714 -0
- package/dist/src/core/dark-session.js.map +1 -0
- package/dist/src/core/decision-refusal.d.ts +185 -0
- package/dist/src/core/decision-refusal.js +265 -0
- package/dist/src/core/decision-refusal.js.map +1 -0
- package/dist/src/core/env-file.d.ts +450 -0
- package/dist/src/core/env-file.js +837 -0
- package/dist/src/core/env-file.js.map +1 -0
- package/dist/src/core/execute.d.ts +858 -0
- package/dist/src/core/execute.js +1271 -0
- package/dist/src/core/execute.js.map +1 -0
- package/dist/src/core/frontmatter.d.ts +78 -0
- package/dist/src/core/frontmatter.js +100 -0
- package/dist/src/core/frontmatter.js.map +1 -0
- package/dist/src/core/gate-window.d.ts +312 -0
- package/dist/src/core/gate-window.js +506 -0
- package/dist/src/core/gate-window.js.map +1 -0
- package/dist/src/core/gate.d.ts +1364 -0
- package/dist/src/core/gate.js +3002 -0
- package/dist/src/core/gate.js.map +1 -0
- package/dist/src/core/git-run.d.ts +73 -0
- package/dist/src/core/git-run.js +93 -0
- package/dist/src/core/git-run.js.map +1 -0
- package/dist/src/core/harness-version.d.ts +157 -0
- package/dist/src/core/harness-version.js +211 -0
- package/dist/src/core/harness-version.js.map +1 -0
- package/dist/src/core/harness-wait.d.ts +55 -0
- package/dist/src/core/harness-wait.js +58 -0
- package/dist/src/core/harness-wait.js.map +1 -0
- package/dist/src/core/head-retry.d.ts +107 -0
- package/dist/src/core/head-retry.js +121 -0
- package/dist/src/core/head-retry.js.map +1 -0
- package/dist/src/core/instance.d.ts +253 -0
- package/dist/src/core/instance.js +319 -0
- package/dist/src/core/instance.js.map +1 -0
- package/dist/src/core/intake-limits.d.ts +247 -0
- package/dist/src/core/intake-limits.js +350 -0
- package/dist/src/core/intake-limits.js.map +1 -0
- package/dist/src/core/jcs.d.ts +52 -0
- package/dist/src/core/jcs.js +132 -0
- package/dist/src/core/jcs.js.map +1 -0
- package/dist/src/core/journal.d.ts +144 -0
- package/dist/src/core/journal.js +200 -0
- package/dist/src/core/journal.js.map +1 -0
- package/dist/src/core/live-draw.d.ts +436 -0
- package/dist/src/core/live-draw.js +703 -0
- package/dist/src/core/live-draw.js.map +1 -0
- package/dist/src/core/log-reconcile.d.ts +89 -0
- package/dist/src/core/log-reconcile.js +136 -0
- package/dist/src/core/log-reconcile.js.map +1 -0
- package/dist/src/core/log-subscribe.d.ts +36 -0
- package/dist/src/core/log-subscribe.js +162 -0
- package/dist/src/core/log-subscribe.js.map +1 -0
- package/dist/src/core/log.d.ts +278 -0
- package/dist/src/core/log.js +546 -0
- package/dist/src/core/log.js.map +1 -0
- package/dist/src/core/loop.d.ts +274 -0
- package/dist/src/core/loop.js +487 -0
- package/dist/src/core/loop.js.map +1 -0
- package/dist/src/core/md-fence.d.ts +41 -0
- package/dist/src/core/md-fence.js +74 -0
- package/dist/src/core/md-fence.js.map +1 -0
- package/dist/src/core/money.d.ts +147 -0
- package/dist/src/core/money.js +195 -0
- package/dist/src/core/money.js.map +1 -0
- package/dist/src/core/payload-census.d.ts +74 -0
- package/dist/src/core/payload-census.js +146 -0
- package/dist/src/core/payload-census.js.map +1 -0
- package/dist/src/core/payload-store.d.ts +175 -0
- package/dist/src/core/payload-store.js +340 -0
- package/dist/src/core/payload-store.js.map +1 -0
- package/dist/src/core/payload.d.ts +71 -0
- package/dist/src/core/payload.js +80 -0
- package/dist/src/core/payload.js.map +1 -0
- package/dist/src/core/policy-diff.d.ts +292 -0
- package/dist/src/core/policy-diff.js +588 -0
- package/dist/src/core/policy-diff.js.map +1 -0
- package/dist/src/core/policy-expectations.d.ts +199 -0
- package/dist/src/core/policy-expectations.js +394 -0
- package/dist/src/core/policy-expectations.js.map +1 -0
- package/dist/src/core/policy-explain.d.ts +150 -0
- package/dist/src/core/policy-explain.js +258 -0
- package/dist/src/core/policy-explain.js.map +1 -0
- package/dist/src/core/policy-load.d.ts +527 -0
- package/dist/src/core/policy-load.js +536 -0
- package/dist/src/core/policy-load.js.map +1 -0
- package/dist/src/core/policy-match.d.ts +281 -0
- package/dist/src/core/policy-match.js +478 -0
- package/dist/src/core/policy-match.js.map +1 -0
- package/dist/src/core/policy-proposal.d.ts +265 -0
- package/dist/src/core/policy-proposal.js +458 -0
- package/dist/src/core/policy-proposal.js.map +1 -0
- package/dist/src/core/prompt-layout.d.ts +221 -0
- package/dist/src/core/prompt-layout.js +422 -0
- package/dist/src/core/prompt-layout.js.map +1 -0
- package/dist/src/core/protected-path-guard.d.ts +453 -0
- package/dist/src/core/protected-path-guard.js +1566 -0
- package/dist/src/core/protected-path-guard.js.map +1 -0
- package/dist/src/core/registration.d.ts +25 -0
- package/dist/src/core/registration.js +39 -0
- package/dist/src/core/registration.js.map +1 -0
- package/dist/src/core/reindex.d.ts +99 -0
- package/dist/src/core/reindex.js +336 -0
- package/dist/src/core/reindex.js.map +1 -0
- package/dist/src/core/sampler.d.ts +313 -0
- package/dist/src/core/sampler.js +388 -0
- package/dist/src/core/sampler.js.map +1 -0
- package/dist/src/core/sandbox.d.ts +290 -0
- package/dist/src/core/sandbox.js +424 -0
- package/dist/src/core/sandbox.js.map +1 -0
- package/dist/src/core/seal.d.ts +165 -0
- package/dist/src/core/seal.js +290 -0
- package/dist/src/core/seal.js.map +1 -0
- package/dist/src/core/state.d.ts +505 -0
- package/dist/src/core/state.js +1009 -0
- package/dist/src/core/state.js.map +1 -0
- package/dist/src/core/task-file.d.ts +185 -0
- package/dist/src/core/task-file.js +464 -0
- package/dist/src/core/task-file.js.map +1 -0
- package/dist/src/core/telegram-config.d.ts +93 -0
- package/dist/src/core/telegram-config.js +114 -0
- package/dist/src/core/telegram-config.js.map +1 -0
- package/dist/src/core/token.d.ts +409 -0
- package/dist/src/core/token.js +561 -0
- package/dist/src/core/token.js.map +1 -0
- package/dist/src/core/validate.d.ts +138 -0
- package/dist/src/core/validate.js +0 -0
- package/dist/src/core/validate.js.map +1 -0
- package/dist/src/core/values.d.ts +137 -0
- package/dist/src/core/values.js +153 -0
- package/dist/src/core/values.js.map +1 -0
- package/dist/src/core/vault.d.ts +291 -0
- package/dist/src/core/vault.js +612 -0
- package/dist/src/core/vault.js.map +1 -0
- package/dist/src/core/verified-snapshot.d.ts +204 -0
- package/dist/src/core/verified-snapshot.js +506 -0
- package/dist/src/core/verified-snapshot.js.map +1 -0
- package/dist/src/core/verify.d.ts +336 -0
- package/dist/src/core/verify.js +549 -0
- package/dist/src/core/verify.js.map +1 -0
- package/dist/src/core/version.d.ts +8 -0
- package/dist/src/core/version.js +9 -0
- package/dist/src/core/version.js.map +1 -0
- package/dist/src/core/wysiwys.d.ts +370 -0
- package/dist/src/core/wysiwys.js +728 -0
- package/dist/src/core/wysiwys.js.map +1 -0
- package/dist/src/daemon/advance-child.d.ts +39 -0
- package/dist/src/daemon/advance-child.js +78 -0
- package/dist/src/daemon/advance-child.js.map +1 -0
- package/dist/src/daemon/advance.d.ts +466 -0
- package/dist/src/daemon/advance.js +849 -0
- package/dist/src/daemon/advance.js.map +1 -0
- package/dist/src/daemon/audit.d.ts +87 -0
- package/dist/src/daemon/audit.js +90 -0
- package/dist/src/daemon/audit.js.map +1 -0
- package/dist/src/daemon/daemon.d.ts +1180 -0
- package/dist/src/daemon/daemon.js +1988 -0
- package/dist/src/daemon/daemon.js.map +1 -0
- package/dist/src/daemon/dark-session.d.ts +64 -0
- package/dist/src/daemon/dark-session.js +119 -0
- package/dist/src/daemon/dark-session.js.map +1 -0
- package/dist/src/daemon/draw-child.d.ts +36 -0
- package/dist/src/daemon/draw-child.js +132 -0
- package/dist/src/daemon/draw-child.js.map +1 -0
- package/dist/src/daemon/draw.d.ts +154 -0
- package/dist/src/daemon/draw.js +458 -0
- package/dist/src/daemon/draw.js.map +1 -0
- package/dist/src/daemon/git-evidence.d.ts +173 -0
- package/dist/src/daemon/git-evidence.js +345 -0
- package/dist/src/daemon/git-evidence.js.map +1 -0
- package/dist/src/daemon/projection.d.ts +180 -0
- package/dist/src/daemon/projection.js +233 -0
- package/dist/src/daemon/projection.js.map +1 -0
- package/dist/src/daemon/prune.d.ts +207 -0
- package/dist/src/daemon/prune.js +376 -0
- package/dist/src/daemon/prune.js.map +1 -0
- package/dist/src/mcp/http.d.ts +113 -0
- package/dist/src/mcp/http.js +343 -0
- package/dist/src/mcp/http.js.map +1 -0
- package/dist/src/mcp/server.d.ts +265 -0
- package/dist/src/mcp/server.js +602 -0
- package/dist/src/mcp/server.js.map +1 -0
- package/docs/adapter-api.md +106 -0
- package/docs/cli-reference.md +5716 -0
- package/docs/codex-enforced-session.md +30 -0
- package/package.json +53 -4
- package/schema/.gitkeep +0 -0
- package/schema/LICENSE +117 -0
- package/schema/codex-instance.schema.json +82 -0
- package/schema/envelope.schema.json +137 -0
- package/schema/event.schema.json +1811 -0
- package/schema/fixtures/codex-instance/invalid/unpinned-codex-version.json +40 -0
- package/schema/fixtures/codex-instance/valid/canonical.json +40 -0
- package/schema/fixtures/envelope/invalid/action-missing-idempotency-key.json +15 -0
- package/schema/fixtures/envelope/invalid/action-unknown-class-format.json +14 -0
- package/schema/fixtures/envelope/invalid/confidence-out-of-range.json +11 -0
- package/schema/fixtures/envelope/invalid/est-cost-bare-number.json +14 -0
- package/schema/fixtures/envelope/invalid/est-cost-noncanonical-string.json +14 -0
- package/schema/fixtures/envelope/invalid/malformed-assignee.json +10 -0
- package/schema/fixtures/envelope/invalid/malformed-created-by.json +7 -0
- package/schema/fixtures/envelope/invalid/malformed-max-latency.json +11 -0
- package/schema/fixtures/envelope/invalid/malformed-payload-hash.json +14 -0
- package/schema/fixtures/envelope/invalid/max-cost-bare-number.json +11 -0
- package/schema/fixtures/envelope/invalid/missing-origin.json +3 -0
- package/schema/fixtures/envelope/invalid/negative-est-cost.json +14 -0
- package/schema/fixtures/envelope/invalid/unknown-state.json +7 -0
- package/schema/fixtures/envelope/invalid/unknown-top-level-field.json +8 -0
- package/schema/fixtures/envelope/valid/action-payload-hash.json +17 -0
- package/schema/fixtures/envelope/valid/actions-without-budget.json +18 -0
- package/schema/fixtures/envelope/valid/canonical.json +25 -0
- package/schema/fixtures/envelope/valid/minimal.json +7 -0
- package/schema/fixtures/envelope/valid/multi-action-executed.json +30 -0
- package/schema/fixtures/envelope/valid/record-write-stage.json +22 -0
- package/schema/fixtures/event/invalid/approval-granted-agent-actor.json +15 -0
- package/schema/fixtures/event/invalid/approval-granted-empty-batch-delivery-id.json +17 -0
- package/schema/fixtures/event/invalid/approval-granted-fifth-reaction.json +16 -0
- package/schema/fixtures/event/invalid/approval-granted-missing-actor.json +14 -0
- package/schema/fixtures/event/invalid/approval-requested-missing-action-key.json +14 -0
- package/schema/fixtures/event/invalid/approval-withdrawn-agent-policy-drift.json +15 -0
- package/schema/fixtures/event/invalid/approval-withdrawn-missing-reason.json +15 -0
- package/schema/fixtures/event/invalid/approval-withdrawn-system-actor.json +15 -0
- package/schema/fixtures/event/invalid/audit-decision-refused-human-actor.json +17 -0
- package/schema/fixtures/event/invalid/audit-decision-refused-missing-code.json +16 -0
- package/schema/fixtures/event/invalid/audit-reviewed-agent-actor.json +15 -0
- package/schema/fixtures/event/invalid/audit-reviewed-loved-no-note.json +16 -0
- package/schema/fixtures/event/invalid/audit-reviewed-system-actor.json +15 -0
- package/schema/fixtures/event/invalid/bad-actor-prefix.json +15 -0
- package/schema/fixtures/event/invalid/est-cost-bare-number.json +17 -0
- package/schema/fixtures/event/invalid/execution-completed-fabricated-exit-code.json +16 -0
- package/schema/fixtures/event/invalid/execution-completed-provider-ref-empty-id.json +18 -0
- package/schema/fixtures/event/invalid/execution-completed-provider-ref-extra-field.json +19 -0
- package/schema/fixtures/event/invalid/execution-completed-provider-ref-id-not-string.json +18 -0
- package/schema/fixtures/event/invalid/execution-completed-provider-ref-missing-adapter.json +17 -0
- package/schema/fixtures/event/invalid/execution-failed-open-reported-by.json +16 -0
- package/schema/fixtures/event/invalid/execution-indeterminate-open-reason.json +14 -0
- package/schema/fixtures/event/invalid/execution-reconciled-agent-actor.json +17 -0
- package/schema/fixtures/event/invalid/execution-started-negative-env-stripped.json +16 -0
- package/schema/fixtures/event/invalid/gate-bypassed-missing-opened-seq.json +15 -0
- package/schema/fixtures/event/invalid/gate-closed-non-integer-opened-seq.json +13 -0
- package/schema/fixtures/event/invalid/gate-opened-agent-actor.json +16 -0
- package/schema/fixtures/event/invalid/gate-organ-attested-absolute-path.json +14 -0
- package/schema/fixtures/event/invalid/gate-organ-attested-agent-actor.json +14 -0
- package/schema/fixtures/event/invalid/gate-organ-attested-missing-organ-path.json +13 -0
- package/schema/fixtures/event/invalid/harness-unknown-kind.json +18 -0
- package/schema/fixtures/event/invalid/harness-version-multiline.json +16 -0
- package/schema/fixtures/event/invalid/log-checkpoint-agent-actor.json +17 -0
- package/schema/fixtures/event/invalid/log-checkpoint-missing-signature.json +16 -0
- package/schema/fixtures/event/invalid/log-checkpoint-short-signed-hash.json +17 -0
- package/schema/fixtures/event/invalid/log-checkpoint-unknown-signature-alg.json +17 -0
- package/schema/fixtures/event/invalid/malformed-ts.json +15 -0
- package/schema/fixtures/event/invalid/missing-alg.json +14 -0
- package/schema/fixtures/event/invalid/missing-hash.json +14 -0
- package/schema/fixtures/event/invalid/non-integer-seq.json +15 -0
- package/schema/fixtures/event/invalid/payload-pruned-human-actor.json +14 -0
- package/schema/fixtures/event/invalid/payload-pruned-missing-hash.json +14 -0
- package/schema/fixtures/event/invalid/policy-declined-agent-actor.json +15 -0
- package/schema/fixtures/event/invalid/policy-proposed-missing-diff.json +19 -0
- package/schema/fixtures/event/invalid/policy-proposed-system-actor.json +26 -0
- package/schema/fixtures/event/invalid/short-hash.json +15 -0
- package/schema/fixtures/event/invalid/unknown-alg.json +15 -0
- package/schema/fixtures/event/invalid/unknown-event-type.json +15 -0
- package/schema/fixtures/event/invalid/unknown-top-level-field.json +16 -0
- package/schema/fixtures/event/valid/approval-expired.json +15 -0
- package/schema/fixtures/event/valid/approval-granted-batch.json +19 -0
- package/schema/fixtures/event/valid/approval-granted-reaction.json +16 -0
- package/schema/fixtures/event/valid/approval-granted.json +15 -0
- package/schema/fixtures/event/valid/approval-rejected.json +15 -0
- package/schema/fixtures/event/valid/approval-requested.json +19 -0
- package/schema/fixtures/event/valid/approval-revoked.json +15 -0
- package/schema/fixtures/event/valid/approval-withdrawn-policy-drift.json +17 -0
- package/schema/fixtures/event/valid/approval-withdrawn.json +16 -0
- package/schema/fixtures/event/valid/audit-decision-refused.json +20 -0
- package/schema/fixtures/event/valid/audit-reviewed-reaction.json +17 -0
- package/schema/fixtures/event/valid/audit-reviewed.json +15 -0
- package/schema/fixtures/event/valid/audit-sampled.json +14 -0
- package/schema/fixtures/event/valid/budget-exceeded.json +21 -0
- package/schema/fixtures/event/valid/envelope-drift.json +16 -0
- package/schema/fixtures/event/valid/execution-completed-harness-report.json +16 -0
- package/schema/fixtures/event/valid/execution-completed-provider-ref.json +18 -0
- package/schema/fixtures/event/valid/execution-completed.json +15 -0
- package/schema/fixtures/event/valid/execution-failed-harness-report.json +16 -0
- package/schema/fixtures/event/valid/execution-failed.json +15 -0
- package/schema/fixtures/event/valid/execution-indeterminate.json +15 -0
- package/schema/fixtures/event/valid/execution-reconciled.json +17 -0
- package/schema/fixtures/event/valid/execution-started-env-stripped.json +17 -0
- package/schema/fixtures/event/valid/execution-started.json +14 -0
- package/schema/fixtures/event/valid/gate-bypassed-harness-version.json +18 -0
- package/schema/fixtures/event/valid/gate-bypassed.json +19 -0
- package/schema/fixtures/event/valid/gate-closed.json +14 -0
- package/schema/fixtures/event/valid/gate-opened.json +16 -0
- package/schema/fixtures/event/valid/gate-organ-attested.json +14 -0
- package/schema/fixtures/event/valid/genesis-null-prev.json +14 -0
- package/schema/fixtures/event/valid/log-checkpoint.json +17 -0
- package/schema/fixtures/event/valid/payload-pruned-orphan.json +13 -0
- package/schema/fixtures/event/valid/payload-pruned.json +17 -0
- package/schema/fixtures/event/valid/policy-declined.json +16 -0
- package/schema/fixtures/event/valid/policy-proposed.json +35 -0
- package/schema/fixtures/event/valid/policy-updated.json +14 -0
- package/schema/fixtures/event/valid/reconciliation-required.json +18 -0
- package/schema/fixtures/event/valid/reconciliation-satisfied.json +17 -0
- package/schema/fixtures/event/valid/route-accepted.json +15 -0
- package/schema/fixtures/event/valid/route-proposed.json +16 -0
- package/schema/fixtures/event/valid/spec-example.json +15 -0
- package/schema/fixtures/event/valid/task-registered-harness-version.json +23 -0
- package/schema/fixtures/event/valid/task-registered.json +14 -0
- package/schema/fixtures/hash/known-answer-pre-121.json +74 -0
- package/schema/fixtures/hash/known-answer.json +74 -0
- package/schema/fixtures/policy/invalid/bad-approval-ttl.json +7 -0
- package/schema/fixtures/policy/invalid/bad-web-port.json +4 -0
- package/schema/fixtures/policy/invalid/checkpoint-key-not-base64.json +6 -0
- package/schema/fixtures/policy/invalid/class-rule-missing-autonomy.json +9 -0
- package/schema/fixtures/policy/invalid/empty-class-key.json +6 -0
- package/schema/fixtures/policy/invalid/live-rate-on-human-only.json +7 -0
- package/schema/fixtures/policy/invalid/malformed-class-key.json +6 -0
- package/schema/fixtures/policy/invalid/missing-version.json +8 -0
- package/schema/fixtures/policy/invalid/negative-limit.json +9 -0
- package/schema/fixtures/policy/invalid/non-numeric-limit.json +9 -0
- package/schema/fixtures/policy/invalid/non-positive-max-pending.json +9 -0
- package/schema/fixtures/policy/invalid/on-expiry-grant.json +8 -0
- package/schema/fixtures/policy/invalid/payload-retention-bare-number.json +4 -0
- package/schema/fixtures/policy/invalid/payload-retention-compound.json +4 -0
- package/schema/fixtures/policy/invalid/payload-retention-fractional.json +4 -0
- package/schema/fixtures/policy/invalid/payload-retention-zero.json +4 -0
- package/schema/fixtures/policy/invalid/protected-paths-escape.json +4 -0
- package/schema/fixtures/policy/invalid/protected-paths-glob.json +4 -0
- package/schema/fixtures/policy/invalid/retro-rate-on-human-only.json +7 -0
- package/schema/fixtures/policy/invalid/retro-rate-on-manual.json +7 -0
- package/schema/fixtures/policy/invalid/retro-rate-zero.json +7 -0
- package/schema/fixtures/policy/invalid/sample-rate-too-high.json +5 -0
- package/schema/fixtures/policy/invalid/sampling-secret-env-empty.json +7 -0
- package/schema/fixtures/policy/invalid/sampling-secret-env-not-string.json +6 -0
- package/schema/fixtures/policy/invalid/skew-tolerance-compound.json +6 -0
- package/schema/fixtures/policy/invalid/unknown-autonomy.json +7 -0
- package/schema/fixtures/policy/invalid/unknown-class-rule-key.json +6 -0
- package/schema/fixtures/policy/invalid/unknown-top-level-key.json +7 -0
- package/schema/fixtures/policy/invalid/vault-passphrase-env-empty.json +6 -0
- package/schema/fixtures/policy/invalid/vault-passphrase-literal.json +6 -0
- package/schema/fixtures/policy/invalid/version-not-string.json +4 -0
- package/schema/fixtures/policy/valid/canonical.json +47 -0
- package/schema/fixtures/policy/valid/checkpoint-keys.json +18 -0
- package/schema/fixtures/policy/valid/class-approvers-limits.json +25 -0
- package/schema/fixtures/policy/valid/class-retro-rate.json +17 -0
- package/schema/fixtures/policy/valid/global-budgets.json +19 -0
- package/schema/fixtures/policy/valid/human-only.json +9 -0
- package/schema/fixtures/policy/valid/minimal.json +6 -0
- package/schema/fixtures/policy/valid/protected-paths.json +10 -0
- package/schema/fixtures/policy/valid/record-namespace.json +13 -0
- package/schema/fixtures/policy/valid/request-volume-limits.json +26 -0
- package/schema/fixtures/policy/valid/retention-and-sampling-secret.json +16 -0
- package/schema/fixtures/policy/valid/skew-tolerance.json +15 -0
- package/schema/fixtures/policy/valid/vault-passphrase-env.json +14 -0
- package/schema/fixtures/policy/valid/wildcards.json +15 -0
- package/schema/fixtures/policy-md/invalid/alias-bomb.md +15 -0
- package/schema/fixtures/policy-md/invalid/no-fence.md +7 -0
- package/schema/fixtures/policy-md/invalid/protected-route-not-a-subclass.md +16 -0
- package/schema/fixtures/policy-md/invalid/schema-invalid-autonomy.md +16 -0
- package/schema/fixtures/policy-md/invalid/schema-invalid-read-proof.md +17 -0
- package/schema/fixtures/policy-md/invalid/two-fences.md +19 -0
- package/schema/fixtures/policy-md/invalid/unclosed-fence.md +11 -0
- package/schema/fixtures/policy-md/invalid/wrong-info-string.md +11 -0
- package/schema/fixtures/policy-md/invalid/yaml-syntax-error.md +13 -0
- package/schema/fixtures/policy-md/precedence/both/APPROVAL.md +7 -0
- package/schema/fixtures/policy-md/precedence/both/APPROVALS.md +7 -0
- package/schema/fixtures/policy-md/precedence/fallback-only/APPROVALS.md +7 -0
- package/schema/fixtures/policy-md/valid/canonical.md +50 -0
- package/schema/fixtures/policy-md/valid/daemon-read-proof.md +18 -0
- package/schema/fixtures/policy-md/valid/minimal.md +3 -0
- package/schema/fixtures/policy-md/valid/prose-lookalikes.md +54 -0
- package/schema/fixtures/policy-md/valid/routed-protected-paths.md +49 -0
- package/schema/fixtures/policy-md/valid/with-values.md +79 -0
- package/schema/fixtures/sample-record/invalid/bad-date-time.json +4 -0
- package/schema/fixtures/sample-record/invalid/missing-required-field.json +3 -0
- package/schema/fixtures/sample-record/invalid/unknown-top-level-field.json +5 -0
- package/schema/fixtures/sample-record/invalid/wrong-type.json +4 -0
- package/schema/fixtures/sample-record/valid/minimal.json +4 -0
- package/schema/fixtures/sample-record/valid/with-note.json +5 -0
- package/schema/fixtures/values/invalid/class-shaped.json +9 -0
- package/schema/fixtures/values/invalid/duplicate-entry.json +4 -0
- package/schema/fixtures/values/invalid/non-string-item.json +4 -0
- package/schema/fixtures/values/invalid/over-cap.json +26 -0
- package/schema/fixtures/values/invalid/unknown-key.json +5 -0
- package/schema/fixtures/values/invalid/version-string.json +1 -0
- package/schema/fixtures/values/valid/empty-lists.json +7 -0
- package/schema/fixtures/values/valid/full.json +20 -0
- package/schema/fixtures/values/valid/minimal.json +1 -0
- package/schema/fixtures/values-md/invalid/schema-invalid.md +62 -0
- package/schema/fixtures/values-md/invalid/two-blocks.md +69 -0
- package/schema/fixtures/values-md/invalid/unterminated.md +61 -0
- package/schema/fixtures/values-md/invalid/yaml-error.md +63 -0
- package/schema/fixtures/values-md/valid/absent.md +50 -0
- package/schema/fixtures/values-md/valid/with-values.md +79 -0
- package/schema/policy.schema.json +501 -0
- package/schema/sample-record.schema.json +26 -0
- package/schema/values.schema.json +55 -0
- package/templates/codex/README.md +9 -0
|
@@ -0,0 +1,2341 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The verb registry: ONE structured description of the CLI surface, for every
|
|
3
|
+
* machine that has to consume it.
|
|
4
|
+
*
|
|
5
|
+
* SPEC.md §10.1 makes the CLI the primary interface for humans and agents, and
|
|
6
|
+
* §10.5 gives the optional MCP wrapper "the same verbs as tools", sharing the
|
|
7
|
+
* CLI's code paths. Two surfaces publishing the same contract from two hand-kept
|
|
8
|
+
* lists is two surfaces that drift, so both read this one: `approval
|
|
9
|
+
* instructions --schemas` prints it verbatim, and an MCP server maps it into
|
|
10
|
+
* tool descriptions and input schemas without re-deriving anything.
|
|
11
|
+
*
|
|
12
|
+
* **This file restates; it does not replace.** Each `purpose` is a one-paragraph
|
|
13
|
+
* agent-facing summary of what the verb's own `--help` says at length, and
|
|
14
|
+
* `src/cli/help.ts` remains the authority on prose. A verb whose help text and
|
|
15
|
+
* whose registry entry disagree is a defect in this file.
|
|
16
|
+
*
|
|
17
|
+
* **The schemas are the frozen shapes, written down.** The `--json` outputs are
|
|
18
|
+
* frozen public API (see `exit-codes.ts` and every `--help`), and the existing
|
|
19
|
+
* CLI suites pin them with `deepEqual` on whole objects. `output` here is the
|
|
20
|
+
* same commitment in JSON Schema form, and `tests/cli-instructions.test.ts`
|
|
21
|
+
* pins it in both directions: every schema must compile under the repo's Ajv
|
|
22
|
+
* setup, and real `--json` output captured from a live CLI run must validate
|
|
23
|
+
* against it. A shape change without a schema change fails there; a schema
|
|
24
|
+
* change without a shape change fails the `deepEqual` suites.
|
|
25
|
+
*
|
|
26
|
+
* **`human_only` is a safety marker, not a convenience flag.** It means: this
|
|
27
|
+
* verb records or establishes a human's authority, and an MCP wrapper MUST NOT
|
|
28
|
+
* publish it as a tool an agent can call. The runtime enforces the human-only
|
|
29
|
+
* verbs itself (in the CLI layer, again in core, again in the event schema);
|
|
30
|
+
* this marker exists so a wrapper does not offer an agent a door the runtime
|
|
31
|
+
* will only slam.
|
|
32
|
+
*/
|
|
33
|
+
// ---------------------------------------------------------------------------
|
|
34
|
+
// Schema building blocks
|
|
35
|
+
// ---------------------------------------------------------------------------
|
|
36
|
+
const STRING = { type: "string" };
|
|
37
|
+
const BOOLEAN = { type: "boolean" };
|
|
38
|
+
const INTEGER = { type: "integer" };
|
|
39
|
+
const NUMBER = { type: "number" };
|
|
40
|
+
const SHA256 = { type: "string", pattern: "^[0-9a-f]{64}$" };
|
|
41
|
+
/**
|
|
42
|
+
* A USD amount as the log holds it since APRV-121: a canonical decimal string.
|
|
43
|
+
*
|
|
44
|
+
* The `--json` surface reports what the record says, so it reports the same
|
|
45
|
+
* spelling. A consumer that wants arithmetic parses it, which is the one thing
|
|
46
|
+
* a decimal string makes unambiguous across languages.
|
|
47
|
+
*
|
|
48
|
+
* The pattern is spelled out rather than imported: this module is deliberately
|
|
49
|
+
* free of runtime imports so that the frozen output shapes are readable as
|
|
50
|
+
* data. `tests/money.test.ts` asserts it is character-for-character the
|
|
51
|
+
* `USD_STRING_PATTERN` of `core/money.ts`, so the copy cannot drift.
|
|
52
|
+
*/
|
|
53
|
+
const USD_AMOUNT = {
|
|
54
|
+
type: "string",
|
|
55
|
+
pattern: "^(0|[1-9][0-9]*)(\\.[0-9]{0,5}[1-9])?$",
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* `anyOf` rather than a union `type`, deliberately: the repo's Ajv runs
|
|
59
|
+
* `strict: true`, which refuses union types unless `allowUnionTypes` is set,
|
|
60
|
+
* and relaxing a strict flag to make a schema compile is the wrong trade.
|
|
61
|
+
*/
|
|
62
|
+
function nullable(schema) {
|
|
63
|
+
return { anyOf: [schema, { type: "null" }] };
|
|
64
|
+
}
|
|
65
|
+
function object(properties, required) {
|
|
66
|
+
return {
|
|
67
|
+
type: "object",
|
|
68
|
+
properties,
|
|
69
|
+
required: [...required],
|
|
70
|
+
additionalProperties: false,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
/** An object whose keys are not enumerated here (a log record, a policy rule). */
|
|
74
|
+
const OPEN_OBJECT = { type: "object" };
|
|
75
|
+
const arrayOf = (items) => ({ type: "array", items });
|
|
76
|
+
/**
|
|
77
|
+
* Positional arguments, named in order. `items: false` closes the tail: an
|
|
78
|
+
* unexpected argument is a usage error in the CLI and is one here too.
|
|
79
|
+
*/
|
|
80
|
+
function positionals(names, requiredCount) {
|
|
81
|
+
return {
|
|
82
|
+
type: "array",
|
|
83
|
+
prefixItems: names.map((entry) => ({
|
|
84
|
+
type: "string",
|
|
85
|
+
title: entry.name,
|
|
86
|
+
description: entry.description,
|
|
87
|
+
})),
|
|
88
|
+
items: false,
|
|
89
|
+
minItems: requiredCount,
|
|
90
|
+
maxItems: names.length,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
const NO_POSITIONALS = { type: "array", maxItems: 0 };
|
|
94
|
+
/** The argv after `--`, for the two verbs that take one. */
|
|
95
|
+
const TRAILING = {
|
|
96
|
+
type: "array",
|
|
97
|
+
items: STRING,
|
|
98
|
+
minItems: 1,
|
|
99
|
+
description: "everything after the first `--`, passed through untouched",
|
|
100
|
+
};
|
|
101
|
+
/** Flags are `string | boolean` (see `args.ts`); nothing else can be parsed. */
|
|
102
|
+
function input(parts) {
|
|
103
|
+
const flagProperties = {};
|
|
104
|
+
for (const [flag, kind] of Object.entries(parts.flags)) {
|
|
105
|
+
flagProperties[flag] = kind === "string" ? STRING : BOOLEAN;
|
|
106
|
+
}
|
|
107
|
+
const properties = {
|
|
108
|
+
positionals: parts.positionals ?? NO_POSITIONALS,
|
|
109
|
+
flags: {
|
|
110
|
+
type: "object",
|
|
111
|
+
properties: flagProperties,
|
|
112
|
+
required: [],
|
|
113
|
+
additionalProperties: false,
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
if (parts.trailing !== undefined)
|
|
117
|
+
properties["trailing"] = parts.trailing;
|
|
118
|
+
return {
|
|
119
|
+
type: "object",
|
|
120
|
+
properties,
|
|
121
|
+
required: [],
|
|
122
|
+
additionalProperties: false,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
/** Flags every command accepts. */
|
|
126
|
+
const HELP_FLAGS = { "--help": "boolean", "-h": "boolean" };
|
|
127
|
+
const JSON_FLAG = { "--json": "boolean" };
|
|
128
|
+
const LOG_FLAG = { "--log": "string" };
|
|
129
|
+
const POLICY_FLAGS = { "--policy": "string", "--dir": "string" };
|
|
130
|
+
const AS_FLAG = { "--as": "string" };
|
|
131
|
+
/**
|
|
132
|
+
* The shared failure shape. Usage and I/O failures print
|
|
133
|
+
* `{"error":{"code","message"}}`; a gate refusal prints the same object with
|
|
134
|
+
* `ok:false` beside it and refusal-specific detail inside `error`. Both forms
|
|
135
|
+
* go to stderr, and both are one object per invocation.
|
|
136
|
+
*/
|
|
137
|
+
export const ERROR_SCHEMA = {
|
|
138
|
+
type: "object",
|
|
139
|
+
properties: {
|
|
140
|
+
ok: { const: false },
|
|
141
|
+
error: {
|
|
142
|
+
type: "object",
|
|
143
|
+
properties: {
|
|
144
|
+
code: STRING,
|
|
145
|
+
message: STRING,
|
|
146
|
+
},
|
|
147
|
+
required: ["code", "message"],
|
|
148
|
+
additionalProperties: true,
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
required: ["error"],
|
|
152
|
+
additionalProperties: false,
|
|
153
|
+
};
|
|
154
|
+
// ---------------------------------------------------------------------------
|
|
155
|
+
// Exit codes
|
|
156
|
+
// ---------------------------------------------------------------------------
|
|
157
|
+
const OK = { code: 0, meaning: "success" };
|
|
158
|
+
const INTEGRITY = {
|
|
159
|
+
code: 1,
|
|
160
|
+
meaning: "integrity failure, or a GATE REFUSAL: the command was well-formed and the runtime said no",
|
|
161
|
+
};
|
|
162
|
+
const USAGE = { code: 2, meaning: "usage error" };
|
|
163
|
+
const TORN = { code: 3, meaning: "torn tail (the log's final line is unterminated)" };
|
|
164
|
+
const IO = { code: 4, meaning: "I/O error; never reported as corruption" };
|
|
165
|
+
/** The frozen 0–4 table, as it applies to most verbs. */
|
|
166
|
+
const BASE_EXIT_CODES = [OK, INTEGRITY, USAGE, TORN, IO];
|
|
167
|
+
/** Read-only verbs that cannot refuse: 0, 2 and 4 only. */
|
|
168
|
+
const READ_ONLY_EXIT_CODES = [OK, USAGE, IO];
|
|
169
|
+
// ---------------------------------------------------------------------------
|
|
170
|
+
// Shared output fragments
|
|
171
|
+
// ---------------------------------------------------------------------------
|
|
172
|
+
const HEAD = nullable(object({ seq: INTEGER, hash: SHA256 }, ["seq", "hash"]));
|
|
173
|
+
/** A stored log record. `event.schema.json` is the authority on its contents. */
|
|
174
|
+
const RECORD = {
|
|
175
|
+
type: "object",
|
|
176
|
+
properties: {
|
|
177
|
+
seq: INTEGER,
|
|
178
|
+
ts: STRING,
|
|
179
|
+
event: STRING,
|
|
180
|
+
actor: STRING,
|
|
181
|
+
hash: SHA256,
|
|
182
|
+
alg: STRING,
|
|
183
|
+
prev: nullable(SHA256),
|
|
184
|
+
},
|
|
185
|
+
required: ["seq", "ts", "event", "actor", "hash", "alg", "prev"],
|
|
186
|
+
additionalProperties: true,
|
|
187
|
+
};
|
|
188
|
+
/**
|
|
189
|
+
* The policy resolution answer, shared by `policy check` and `policy test`.
|
|
190
|
+
*
|
|
191
|
+
* They are one command under two names (SPEC.md §10.1 lists both), so they get
|
|
192
|
+
* one schema object: an alias that could drift from its original is not an
|
|
193
|
+
* alias.
|
|
194
|
+
*/
|
|
195
|
+
const POLICY_RESOLUTION_OUTPUT = object({
|
|
196
|
+
class: STRING,
|
|
197
|
+
reversible: nullable(BOOLEAN),
|
|
198
|
+
outcome: object({
|
|
199
|
+
// APRV-185 adds `human-only`, the one enforced level for which no
|
|
200
|
+
// request, grant, token or run exists: `approval policy explain` is
|
|
201
|
+
// where an agent learns that before it tries.
|
|
202
|
+
autonomy: { enum: ["autonomous", "supervised", "manual", "human-only"] },
|
|
203
|
+
// APRV-127. These three say how a supervised class is supervised. An
|
|
204
|
+
// agent that wants to know whether a prompt is possible reads
|
|
205
|
+
// `supervision`.
|
|
206
|
+
declaredAutonomy: {
|
|
207
|
+
enum: [
|
|
208
|
+
"autonomous",
|
|
209
|
+
"supervised",
|
|
210
|
+
"supervised-live",
|
|
211
|
+
"supervised-retro",
|
|
212
|
+
"manual",
|
|
213
|
+
"human-only",
|
|
214
|
+
],
|
|
215
|
+
},
|
|
216
|
+
supervision: nullable({ enum: ["live", "retro"] }),
|
|
217
|
+
liveRate: nullable(NUMBER),
|
|
218
|
+
approvers: nullable(arrayOf(STRING)),
|
|
219
|
+
limits: nullable(OPEN_OBJECT),
|
|
220
|
+
allowIrreversible: BOOLEAN,
|
|
221
|
+
}, [
|
|
222
|
+
"autonomy",
|
|
223
|
+
"declaredAutonomy",
|
|
224
|
+
"supervision",
|
|
225
|
+
"liveRate",
|
|
226
|
+
"approvers",
|
|
227
|
+
"limits",
|
|
228
|
+
"allowIrreversible",
|
|
229
|
+
]),
|
|
230
|
+
// `inherited` since APRV-266: a `policy.edit` sub-class the policy declares
|
|
231
|
+
// no rule for, decided by the `policy.edit` line it is a sub-class of.
|
|
232
|
+
provenance: { enum: ["rule", "default", "inherited", "fail-closed", "floor"] },
|
|
233
|
+
manualBecause: nullable({
|
|
234
|
+
enum: ["matched-rule", "irreversibility-floor", "load-failure"],
|
|
235
|
+
}),
|
|
236
|
+
loadFailure: nullable(object({ code: STRING, message: STRING }, ["code", "message"])),
|
|
237
|
+
matched: nullable(object({ pattern: STRING, rule: OPEN_OBJECT }, ["pattern", "rule"])),
|
|
238
|
+
overridden: nullable(object({ pattern: nullable(STRING), autonomy: STRING }, ["pattern", "autonomy"])),
|
|
239
|
+
irreversibility: {
|
|
240
|
+
enum: [
|
|
241
|
+
"not-applicable",
|
|
242
|
+
"policy-allowed",
|
|
243
|
+
"floor-applied",
|
|
244
|
+
"already-manual",
|
|
245
|
+
"human-only",
|
|
246
|
+
],
|
|
247
|
+
},
|
|
248
|
+
irreversiblePatterns: arrayOf(STRING),
|
|
249
|
+
candidates: arrayOf(object({
|
|
250
|
+
pattern: STRING,
|
|
251
|
+
specificity: { type: "array", items: INTEGER, minItems: 3, maxItems: 3 },
|
|
252
|
+
autonomy: STRING,
|
|
253
|
+
winner: BOOLEAN,
|
|
254
|
+
tieBreak: STRING,
|
|
255
|
+
}, ["pattern", "specificity", "autonomy", "winner", "tieBreak"])),
|
|
256
|
+
decisionPath: arrayOf(STRING),
|
|
257
|
+
}, [
|
|
258
|
+
"class",
|
|
259
|
+
"reversible",
|
|
260
|
+
"outcome",
|
|
261
|
+
"provenance",
|
|
262
|
+
"manualBecause",
|
|
263
|
+
"loadFailure",
|
|
264
|
+
"matched",
|
|
265
|
+
"overridden",
|
|
266
|
+
"irreversibility",
|
|
267
|
+
"irreversiblePatterns",
|
|
268
|
+
"candidates",
|
|
269
|
+
"decisionPath",
|
|
270
|
+
]);
|
|
271
|
+
// ---------------------------------------------------------------------------
|
|
272
|
+
// The registry
|
|
273
|
+
// ---------------------------------------------------------------------------
|
|
274
|
+
const VERBS = [
|
|
275
|
+
{
|
|
276
|
+
name: "instructions",
|
|
277
|
+
purpose: "Print the agent-facing usage guide: what approval.md expects of an agent, the register/request/wait/run sequence, what a refusal means, and the invariants an agent must not route around. With --schemas it prints this registry as JSON instead, which is the machine-readable form of the same contract and the source an MCP wrapper builds its tools from. Reads no log, resolves no policy, writes nothing.",
|
|
278
|
+
human_only: false,
|
|
279
|
+
input: input({ flags: { "--schemas": "boolean", ...JSON_FLAG, ...HELP_FLAGS } }),
|
|
280
|
+
output: object({
|
|
281
|
+
guide: STRING,
|
|
282
|
+
verbs: arrayOf(OPEN_OBJECT),
|
|
283
|
+
}, ["guide", "verbs"]),
|
|
284
|
+
error: ERROR_SCHEMA,
|
|
285
|
+
exit_codes: [OK, USAGE],
|
|
286
|
+
},
|
|
287
|
+
{
|
|
288
|
+
name: "init",
|
|
289
|
+
purpose: "Scaffold a working directory: APPROVAL.md (SPEC.md §5.1's canonical policy, to be read and edited), the empty .approval/log/ directory, .approval/QUEUE.md in its empty state, and the .gitignore lines for the index, the vault, the environment source map and the atomic-write temp files. It appends nothing, attests nothing, and overwrites nothing; a re-run writes nothing and reports what already exists.",
|
|
290
|
+
human_only: true,
|
|
291
|
+
human_only_note: "It writes the policy file a human must then read and attest, in a directory a human chose. Nothing it writes is operative, but a scaffold an agent could drop into a tree is a policy file nobody decided to have.",
|
|
292
|
+
input: input({ flags: { "--dir": "string", ...JSON_FLAG, ...HELP_FLAGS } }),
|
|
293
|
+
output: object({
|
|
294
|
+
ok: { const: true },
|
|
295
|
+
dir: STRING,
|
|
296
|
+
written: arrayOf(STRING),
|
|
297
|
+
existing: arrayOf(object({ path: STRING, code: STRING }, ["path", "code"])),
|
|
298
|
+
next_steps: arrayOf(STRING),
|
|
299
|
+
}, ["ok", "dir", "written", "existing", "next_steps"]),
|
|
300
|
+
error: ERROR_SCHEMA,
|
|
301
|
+
exit_codes: BASE_EXIT_CODES,
|
|
302
|
+
},
|
|
303
|
+
{
|
|
304
|
+
name: "quickstart",
|
|
305
|
+
purpose: "Interactive solo setup: asks for a human id, terminal or Telegram, and five manual class families; writes a new policy and identity configuration, shows the exact bytes, requires typed understood, then attests. It classifies policy.core, refuses non-terminal input and --json, and never overwrites an existing policy.",
|
|
306
|
+
human_only: true,
|
|
307
|
+
human_only_note: "This command authors and attests APPROVAL.md and declares APPROVAL_HUMAN. It is reserved to a person at a terminal and is never exposed through MCP.",
|
|
308
|
+
input: input({ flags: { "--dir": "string", "--api-base": "string", ...HELP_FLAGS } }),
|
|
309
|
+
output: null,
|
|
310
|
+
error: ERROR_SCHEMA,
|
|
311
|
+
exit_codes: BASE_EXIT_CODES,
|
|
312
|
+
},
|
|
313
|
+
{
|
|
314
|
+
name: "log",
|
|
315
|
+
subcommand: "verify",
|
|
316
|
+
purpose: "Walk the hash chain end to end and report clean, torn-tail or corrupt. An absent file is an empty log and verifies clean; a torn tail is reported and never truncated. Timestamp anomalies are reported additively and DO NOT change the verdict: chain integrity is a proof, clock skew is a judgment for a human.",
|
|
317
|
+
human_only: false,
|
|
318
|
+
input: input({ flags: { ...LOG_FLAG, ...JSON_FLAG, ...HELP_FLAGS } }),
|
|
319
|
+
output: {
|
|
320
|
+
type: "object",
|
|
321
|
+
properties: {
|
|
322
|
+
status: { enum: ["clean", "torn-tail", "corrupt"] },
|
|
323
|
+
records: nullable(INTEGER),
|
|
324
|
+
head: HEAD,
|
|
325
|
+
intactThroughSeq: INTEGER,
|
|
326
|
+
firstBadSeq: nullable(INTEGER),
|
|
327
|
+
reason: STRING,
|
|
328
|
+
message: STRING,
|
|
329
|
+
anomalies: arrayOf(OPEN_OBJECT),
|
|
330
|
+
},
|
|
331
|
+
required: ["status", "records", "head"],
|
|
332
|
+
additionalProperties: false,
|
|
333
|
+
},
|
|
334
|
+
error: ERROR_SCHEMA,
|
|
335
|
+
exit_codes: [
|
|
336
|
+
{ code: 0, meaning: "clean" },
|
|
337
|
+
{ code: 1, meaning: "corrupt" },
|
|
338
|
+
USAGE,
|
|
339
|
+
{ code: 3, meaning: "torn tail" },
|
|
340
|
+
IO,
|
|
341
|
+
],
|
|
342
|
+
},
|
|
343
|
+
{
|
|
344
|
+
name: "log",
|
|
345
|
+
subcommand: "tail",
|
|
346
|
+
purpose: "Print the last N records (default 10), oldest first. The chain is verified before anything is printed: a torn tail prints the intact records with a warning and exits 0, and a corrupt log prints nothing at all, because a tail of tampered data is worse than no tail.",
|
|
347
|
+
human_only: false,
|
|
348
|
+
input: input({ flags: { ...LOG_FLAG, "-n": "string", ...JSON_FLAG, ...HELP_FLAGS } }),
|
|
349
|
+
output: object({
|
|
350
|
+
status: { enum: ["ok", "torn-tail"] },
|
|
351
|
+
records: arrayOf(RECORD),
|
|
352
|
+
warning: STRING,
|
|
353
|
+
}, ["status", "records"]),
|
|
354
|
+
error: ERROR_SCHEMA,
|
|
355
|
+
exit_codes: [OK, { code: 1, meaning: "the log is corrupt; nothing was printed" }, USAGE, TORN, IO],
|
|
356
|
+
},
|
|
357
|
+
{
|
|
358
|
+
name: "log",
|
|
359
|
+
subcommand: "export",
|
|
360
|
+
purpose: "Stream every stored record to stdout. Without --json the stored lines are written verbatim, byte for byte, so piping export to a file yields a copy of the log. The chain is verified first and the log is never modified.",
|
|
361
|
+
human_only: false,
|
|
362
|
+
input: input({ flags: { ...LOG_FLAG, ...JSON_FLAG, ...HELP_FLAGS } }),
|
|
363
|
+
output: object({ records: arrayOf(RECORD), warning: STRING }, ["records"]),
|
|
364
|
+
error: ERROR_SCHEMA,
|
|
365
|
+
exit_codes: [OK, { code: 1, meaning: "the log is corrupt; nothing was printed" }, USAGE, TORN, IO],
|
|
366
|
+
},
|
|
367
|
+
{
|
|
368
|
+
name: "log",
|
|
369
|
+
subcommand: "follow",
|
|
370
|
+
purpose: "Emit one JSON event per line after an exclusive sequence cursor, then follow appends. Every batch is verified from genesis before emission; filesystem notifications only prompt another read. The optional cursor hash binds a resume to the retained prefix. This foreground stream is intentionally excluded from MCP's finite call queue.",
|
|
371
|
+
human_only: false,
|
|
372
|
+
input: input({
|
|
373
|
+
flags: {
|
|
374
|
+
...LOG_FLAG,
|
|
375
|
+
"--from": "string",
|
|
376
|
+
"--cursor-hash": "string",
|
|
377
|
+
...JSON_FLAG,
|
|
378
|
+
...HELP_FLAGS,
|
|
379
|
+
},
|
|
380
|
+
}),
|
|
381
|
+
// A stream has no single finite output object for registry/MCP purposes.
|
|
382
|
+
// Each stdout line is a RECORD, pinned by the CLI end-to-end test.
|
|
383
|
+
output: null,
|
|
384
|
+
error: ERROR_SCHEMA,
|
|
385
|
+
exit_codes: [
|
|
386
|
+
{ code: 0, meaning: "cancelled by a signal or downstream pipe closure" },
|
|
387
|
+
{ code: 1, meaning: "corrupt log or cursor mismatch" },
|
|
388
|
+
USAGE,
|
|
389
|
+
TORN,
|
|
390
|
+
IO,
|
|
391
|
+
],
|
|
392
|
+
},
|
|
393
|
+
// APRV-125. The two verbs that move the log FILE. `human_only` is false on
|
|
394
|
+
// both: an agent may run them, and the policy decides whether it may — they
|
|
395
|
+
// classify as `log.sync` and `log.advance` rather than as the gate's own
|
|
396
|
+
// pass-through, precisely so a policy can hold them.
|
|
397
|
+
{
|
|
398
|
+
name: "log",
|
|
399
|
+
subcommand: "sync",
|
|
400
|
+
purpose: "Fast-forward the committed log and put the working chain back, safely. Holds the append lockfile for the WHOLE operation, verifies the chain, snapshots events.jsonl inside .approval/ (never `git stash`), fetches and merges --ff-only, then reconciles: the committed chain must be a prefix of the snapshot, equal to it, or an extension of it, and anything else refuses `log-diverged` naming both heads and the first divergent seq. Chains are never merged or re-chained. QUEUE.md and the index are REBUILT from the reconciled log rather than restored, any failure at any step restores the snapshot before exiting, and no event is appended. PRIMARY CHECKOUT ONLY.",
|
|
401
|
+
human_only: false,
|
|
402
|
+
input: input({
|
|
403
|
+
flags: { "--remote": "string", "--branch": "string", ...JSON_FLAG, ...HELP_FLAGS },
|
|
404
|
+
}),
|
|
405
|
+
output: object({
|
|
406
|
+
ok: { const: true },
|
|
407
|
+
root: STRING,
|
|
408
|
+
log: STRING,
|
|
409
|
+
remote: STRING,
|
|
410
|
+
branch: STRING,
|
|
411
|
+
commit: object({ before: STRING, after: STRING, pulled: INTEGER }, [
|
|
412
|
+
"before",
|
|
413
|
+
"after",
|
|
414
|
+
"pulled",
|
|
415
|
+
]),
|
|
416
|
+
head: object({ before: HEAD, after: HEAD }, ["before", "after"]),
|
|
417
|
+
relation: STRING,
|
|
418
|
+
ahead: INTEGER,
|
|
419
|
+
behind: INTEGER,
|
|
420
|
+
restored: BOOLEAN,
|
|
421
|
+
queue: object({ path: STRING, bytes: INTEGER }, ["path", "bytes"]),
|
|
422
|
+
index: STRING,
|
|
423
|
+
}, [
|
|
424
|
+
"ok",
|
|
425
|
+
"root",
|
|
426
|
+
"log",
|
|
427
|
+
"remote",
|
|
428
|
+
"branch",
|
|
429
|
+
"commit",
|
|
430
|
+
"head",
|
|
431
|
+
"relation",
|
|
432
|
+
"ahead",
|
|
433
|
+
"behind",
|
|
434
|
+
"restored",
|
|
435
|
+
"queue",
|
|
436
|
+
"index",
|
|
437
|
+
]),
|
|
438
|
+
error: ERROR_SCHEMA,
|
|
439
|
+
exit_codes: [
|
|
440
|
+
OK,
|
|
441
|
+
{ code: 1, meaning: "the chains diverged, or a log did not verify; nothing was changed" },
|
|
442
|
+
USAGE,
|
|
443
|
+
IO,
|
|
444
|
+
],
|
|
445
|
+
},
|
|
446
|
+
{
|
|
447
|
+
name: "log",
|
|
448
|
+
subcommand: "advance",
|
|
449
|
+
purpose: "Commit the log's uncommitted records and push them to a records branch. Verifies the chain under the append lock, stages EXACTLY .approval/log/events.jsonl, .approval/QUEUE.md and .approval/payloads/ (any other staged path refuses `log-advance-dirty-stage` rather than being unstaged), commits on the branch you are standing on with the seq range in the message, and pushes that commit by refspec to `--branch` (default records-log-<date>), never to main. It CHECKS OUT NOTHING and appends no event. `--pr` opens the pull request through the ordinary gh path; `--dry-run` reports and writes nothing. PRIMARY CHECKOUT ONLY.",
|
|
450
|
+
human_only: false,
|
|
451
|
+
input: input({
|
|
452
|
+
flags: {
|
|
453
|
+
"--remote": "string",
|
|
454
|
+
"--branch": "string",
|
|
455
|
+
"--pr": "boolean",
|
|
456
|
+
"--co-author": "string",
|
|
457
|
+
"--dry-run": "boolean",
|
|
458
|
+
...JSON_FLAG,
|
|
459
|
+
...HELP_FLAGS,
|
|
460
|
+
},
|
|
461
|
+
}),
|
|
462
|
+
output: object({
|
|
463
|
+
ok: { const: true },
|
|
464
|
+
root: STRING,
|
|
465
|
+
branch: STRING,
|
|
466
|
+
recordsBranch: STRING,
|
|
467
|
+
remote: STRING,
|
|
468
|
+
range: nullable(object({ from: INTEGER, to: INTEGER }, ["from", "to"])),
|
|
469
|
+
head: object({ committed: HEAD, working: HEAD }, ["committed", "working"]),
|
|
470
|
+
staged: arrayOf(STRING),
|
|
471
|
+
message: STRING,
|
|
472
|
+
commit: nullable(STRING),
|
|
473
|
+
pushed: BOOLEAN,
|
|
474
|
+
prUrl: nullable(STRING),
|
|
475
|
+
dryRun: BOOLEAN,
|
|
476
|
+
}, [
|
|
477
|
+
"ok",
|
|
478
|
+
"root",
|
|
479
|
+
"branch",
|
|
480
|
+
"recordsBranch",
|
|
481
|
+
"remote",
|
|
482
|
+
"range",
|
|
483
|
+
"head",
|
|
484
|
+
"staged",
|
|
485
|
+
"message",
|
|
486
|
+
"commit",
|
|
487
|
+
"pushed",
|
|
488
|
+
"prUrl",
|
|
489
|
+
"dryRun",
|
|
490
|
+
]),
|
|
491
|
+
error: ERROR_SCHEMA,
|
|
492
|
+
exit_codes: [
|
|
493
|
+
OK,
|
|
494
|
+
{ code: 1, meaning: "the log did not verify, or the chains diverged; nothing was committed" },
|
|
495
|
+
USAGE,
|
|
496
|
+
IO,
|
|
497
|
+
],
|
|
498
|
+
},
|
|
499
|
+
{
|
|
500
|
+
name: "policy",
|
|
501
|
+
subcommand: "check",
|
|
502
|
+
purpose: "Explain what APPROVAL.md does with one action class: the resolved autonomy, the rule that matched, every candidate with its specificity, the unanimous max-specificity allow_irreversible decision, and the decision path that produced the answer. Nothing is executed, requested or logged. A policy that fails to load is not an error here: a broken policy IS a manual-everything policy, and that answer is delivered on stdout at exit 0, so branch on manualBecause and provenance rather than on the exit code.",
|
|
503
|
+
human_only: false,
|
|
504
|
+
input: input({
|
|
505
|
+
positionals: positionals([{ name: "class", description: "a concrete action class, e.g. vcs.push.main" }], 1),
|
|
506
|
+
flags: { "--reversible": "string", ...POLICY_FLAGS, ...JSON_FLAG, ...HELP_FLAGS },
|
|
507
|
+
}),
|
|
508
|
+
output: POLICY_RESOLUTION_OUTPUT,
|
|
509
|
+
error: ERROR_SCHEMA,
|
|
510
|
+
exit_codes: [
|
|
511
|
+
{ code: 0, meaning: "the question was answered, INCLUDING the fail-closed answer" },
|
|
512
|
+
USAGE,
|
|
513
|
+
{ code: 4, meaning: "a policy path that exists but cannot be read" },
|
|
514
|
+
],
|
|
515
|
+
},
|
|
516
|
+
{
|
|
517
|
+
name: "policy",
|
|
518
|
+
subcommand: "test",
|
|
519
|
+
purpose: "An exact alias of `policy check`; SPEC.md §10.1 names both and they are the same command, with the same flags, the same answer and the same --json shape.",
|
|
520
|
+
human_only: false,
|
|
521
|
+
input: input({
|
|
522
|
+
positionals: positionals([{ name: "class", description: "a concrete action class, e.g. vcs.push.main" }], 1),
|
|
523
|
+
flags: { "--reversible": "string", ...POLICY_FLAGS, ...JSON_FLAG, ...HELP_FLAGS },
|
|
524
|
+
}),
|
|
525
|
+
output: POLICY_RESOLUTION_OUTPUT,
|
|
526
|
+
error: ERROR_SCHEMA,
|
|
527
|
+
exit_codes: [
|
|
528
|
+
{ code: 0, meaning: "the question was answered, INCLUDING the fail-closed answer" },
|
|
529
|
+
USAGE,
|
|
530
|
+
{ code: 4, meaning: "a policy path that exists but cannot be read" },
|
|
531
|
+
],
|
|
532
|
+
},
|
|
533
|
+
{
|
|
534
|
+
name: "policy",
|
|
535
|
+
subcommand: "attest",
|
|
536
|
+
purpose: "Record a human's sign-off on the policy file's exact bytes, as one policy.updated event carrying their SHA-256. Gate operations refuse while the live file is unattested or has changed since the last attestation, so an edited policy is inoperative until a human re-attests it. With --organ <path> it attests one of the gate's ORGANS instead — the harness files that install the hook — as one gate.organ.attested event no gate operation reads: those paths are human-only, so no grant for a hand edit to one can exist and this record is the only evidence the protected-path guard can accept (APRV-272).",
|
|
537
|
+
human_only: true,
|
|
538
|
+
input: input({
|
|
539
|
+
flags: {
|
|
540
|
+
...POLICY_FLAGS,
|
|
541
|
+
"--organ": "string",
|
|
542
|
+
...AS_FLAG,
|
|
543
|
+
...LOG_FLAG,
|
|
544
|
+
...JSON_FLAG,
|
|
545
|
+
...HELP_FLAGS,
|
|
546
|
+
},
|
|
547
|
+
}),
|
|
548
|
+
output: object({
|
|
549
|
+
ok: { const: true },
|
|
550
|
+
seq: INTEGER,
|
|
551
|
+
sha256: SHA256,
|
|
552
|
+
path: STRING,
|
|
553
|
+
// Present on an --organ attestation and absent otherwise: the
|
|
554
|
+
// repository-relative spelling the record carries, which is the
|
|
555
|
+
// identity the guard matches on and is not derivable from `path`.
|
|
556
|
+
organ_path: STRING,
|
|
557
|
+
}, ["ok", "seq", "sha256", "path"]),
|
|
558
|
+
error: ERROR_SCHEMA,
|
|
559
|
+
exit_codes: BASE_EXIT_CODES,
|
|
560
|
+
},
|
|
561
|
+
{
|
|
562
|
+
name: "policy",
|
|
563
|
+
subcommand: "amend",
|
|
564
|
+
purpose: "The whole amendment ceremony in one verb: semantic diff of the edited policy against the last-attested bytes, load advisory, attestation, and the two-file git commit that lands the edit and its attestation together. Refuses to assume a confirmation it could not ask for.",
|
|
565
|
+
human_only: true,
|
|
566
|
+
input: input({
|
|
567
|
+
flags: {
|
|
568
|
+
...POLICY_FLAGS,
|
|
569
|
+
...LOG_FLAG,
|
|
570
|
+
...AS_FLAG,
|
|
571
|
+
"--require-load": "boolean",
|
|
572
|
+
"--dry-run": "boolean",
|
|
573
|
+
"--commit": "boolean",
|
|
574
|
+
"--yes": "boolean",
|
|
575
|
+
...JSON_FLAG,
|
|
576
|
+
...HELP_FLAGS,
|
|
577
|
+
},
|
|
578
|
+
}),
|
|
579
|
+
output: object({
|
|
580
|
+
ok: { const: true },
|
|
581
|
+
noop: BOOLEAN,
|
|
582
|
+
dryRun: BOOLEAN,
|
|
583
|
+
aborted: BOOLEAN,
|
|
584
|
+
policy: STRING,
|
|
585
|
+
liveSha256: SHA256,
|
|
586
|
+
attested: nullable(object({ sha256: SHA256, seq: INTEGER }, ["sha256", "seq"])),
|
|
587
|
+
baseline: object({ mode: STRING, reason: nullable(STRING) }, ["mode", "reason"]),
|
|
588
|
+
diff: nullable(OPEN_OBJECT),
|
|
589
|
+
load: nullable(OPEN_OBJECT),
|
|
590
|
+
attestation: nullable(object({ seq: INTEGER, sha256: SHA256 }, ["seq", "sha256"])),
|
|
591
|
+
git: nullable(OPEN_OBJECT),
|
|
592
|
+
}, [
|
|
593
|
+
"ok",
|
|
594
|
+
"noop",
|
|
595
|
+
"dryRun",
|
|
596
|
+
"aborted",
|
|
597
|
+
"policy",
|
|
598
|
+
"liveSha256",
|
|
599
|
+
"attested",
|
|
600
|
+
"baseline",
|
|
601
|
+
"diff",
|
|
602
|
+
"load",
|
|
603
|
+
"attestation",
|
|
604
|
+
"git",
|
|
605
|
+
]),
|
|
606
|
+
error: ERROR_SCHEMA,
|
|
607
|
+
exit_codes: BASE_EXIT_CODES,
|
|
608
|
+
},
|
|
609
|
+
{
|
|
610
|
+
name: "register",
|
|
611
|
+
purpose: "Validate a task file's `approval:` envelope against envelope.schema.json and append one task.registered event carrying the declared actions. FAIL CLOSED: an invalid envelope appends nothing. The file is read only. Registration is a proposal rather than a decision, so an agent may perform it, and it is the step that makes every later question about an action ('what class is this key?') answerable from the log.",
|
|
612
|
+
human_only: false,
|
|
613
|
+
input: input({
|
|
614
|
+
positionals: positionals([{ name: "task-file", description: "path to the task file to register" }], 1),
|
|
615
|
+
flags: { ...AS_FLAG, ...LOG_FLAG, ...JSON_FLAG, ...HELP_FLAGS },
|
|
616
|
+
}),
|
|
617
|
+
output: object({ ok: { const: true }, seq: INTEGER, task: STRING, actions: INTEGER }, [
|
|
618
|
+
"ok",
|
|
619
|
+
"seq",
|
|
620
|
+
"task",
|
|
621
|
+
"actions",
|
|
622
|
+
]),
|
|
623
|
+
error: ERROR_SCHEMA,
|
|
624
|
+
exit_codes: BASE_EXIT_CODES,
|
|
625
|
+
},
|
|
626
|
+
{
|
|
627
|
+
name: "request",
|
|
628
|
+
purpose: "Ask the gate to admit one declared action. The class, cost, reversibility and summary come from the task.registered record in the LOG, never from a flag: an agent that could name its own class at request time could declare read.web for an action registered as financial.spend. A manual class appends approval.requested and reports proceed:false; a supervised or autonomous class appends NOTHING and reports proceed:true, so do not wait for a grant that will never come.",
|
|
629
|
+
human_only: false,
|
|
630
|
+
input: input({
|
|
631
|
+
positionals: positionals([{ name: "task", description: "the task id" }], 1),
|
|
632
|
+
flags: {
|
|
633
|
+
"--action": "string",
|
|
634
|
+
...AS_FLAG,
|
|
635
|
+
"--payload": "string",
|
|
636
|
+
...POLICY_FLAGS,
|
|
637
|
+
...LOG_FLAG,
|
|
638
|
+
...JSON_FLAG,
|
|
639
|
+
...HELP_FLAGS,
|
|
640
|
+
},
|
|
641
|
+
}),
|
|
642
|
+
output: object({
|
|
643
|
+
ok: { const: true },
|
|
644
|
+
task: STRING,
|
|
645
|
+
action_key: STRING,
|
|
646
|
+
class: STRING,
|
|
647
|
+
autonomy: { enum: ["autonomous", "supervised", "manual"] },
|
|
648
|
+
proceed: BOOLEAN,
|
|
649
|
+
requested: BOOLEAN,
|
|
650
|
+
seq: nullable(INTEGER),
|
|
651
|
+
}, ["ok", "task", "action_key", "class", "autonomy", "proceed", "requested", "seq"]),
|
|
652
|
+
error: ERROR_SCHEMA,
|
|
653
|
+
exit_codes: BASE_EXIT_CODES,
|
|
654
|
+
},
|
|
655
|
+
{
|
|
656
|
+
name: "grant",
|
|
657
|
+
purpose: "Record a human approval of a pending request and mint the single-use execution token for its action. The raw token is PRINTED ONCE here and stored nowhere: the log holds only its SHA-256, so nothing can recover it afterwards. Budgets are re-evaluated at grant time, because the moment that matters for a commitment is the moment the human commits.",
|
|
658
|
+
human_only: true,
|
|
659
|
+
input: input({
|
|
660
|
+
positionals: positionals([{ name: "action-key", description: "the action's idempotency_key" }], 1),
|
|
661
|
+
flags: {
|
|
662
|
+
"--note": "string",
|
|
663
|
+
...AS_FLAG,
|
|
664
|
+
...POLICY_FLAGS,
|
|
665
|
+
...LOG_FLAG,
|
|
666
|
+
...JSON_FLAG,
|
|
667
|
+
...HELP_FLAGS,
|
|
668
|
+
},
|
|
669
|
+
}),
|
|
670
|
+
output: object({
|
|
671
|
+
ok: { const: true },
|
|
672
|
+
decision: { const: "grant" },
|
|
673
|
+
state: { const: "granted" },
|
|
674
|
+
action_key: STRING,
|
|
675
|
+
seq: INTEGER,
|
|
676
|
+
token: SHA256,
|
|
677
|
+
}, ["ok", "decision", "state", "action_key", "seq", "token"]),
|
|
678
|
+
error: ERROR_SCHEMA,
|
|
679
|
+
exit_codes: BASE_EXIT_CODES,
|
|
680
|
+
},
|
|
681
|
+
{
|
|
682
|
+
name: "reject",
|
|
683
|
+
purpose: "Record a human refusal of a pending request. A refusal is terminal: the log is append-only, a second decision is refused already-decided, and no retry, rephrasing or re-request by the same agent changes it. Only a human acting again can.",
|
|
684
|
+
human_only: true,
|
|
685
|
+
input: input({
|
|
686
|
+
positionals: positionals([{ name: "action-key", description: "the action's idempotency_key" }], 1),
|
|
687
|
+
flags: {
|
|
688
|
+
"--note": "string",
|
|
689
|
+
...AS_FLAG,
|
|
690
|
+
...POLICY_FLAGS,
|
|
691
|
+
...LOG_FLAG,
|
|
692
|
+
...JSON_FLAG,
|
|
693
|
+
...HELP_FLAGS,
|
|
694
|
+
},
|
|
695
|
+
}),
|
|
696
|
+
output: object({
|
|
697
|
+
ok: { const: true },
|
|
698
|
+
decision: { const: "reject" },
|
|
699
|
+
state: { const: "rejected" },
|
|
700
|
+
action_key: STRING,
|
|
701
|
+
seq: INTEGER,
|
|
702
|
+
}, ["ok", "decision", "state", "action_key", "seq"]),
|
|
703
|
+
error: ERROR_SCHEMA,
|
|
704
|
+
exit_codes: BASE_EXIT_CODES,
|
|
705
|
+
},
|
|
706
|
+
{
|
|
707
|
+
name: "revoke",
|
|
708
|
+
purpose: "Withdraw a granted approval that has not executed. Legal only on a granted, unexecuted request: an unexecuted grant can be withdrawn, an executed one cannot be un-sent. It withdraws authority rather than granting it, so no attestation is required and no budget is charged.",
|
|
709
|
+
human_only: true,
|
|
710
|
+
input: input({
|
|
711
|
+
positionals: positionals([{ name: "action-key", description: "the action's idempotency_key" }], 1),
|
|
712
|
+
flags: {
|
|
713
|
+
"--note": "string",
|
|
714
|
+
...AS_FLAG,
|
|
715
|
+
...POLICY_FLAGS,
|
|
716
|
+
...LOG_FLAG,
|
|
717
|
+
...JSON_FLAG,
|
|
718
|
+
...HELP_FLAGS,
|
|
719
|
+
},
|
|
720
|
+
}),
|
|
721
|
+
output: object({
|
|
722
|
+
ok: { const: true },
|
|
723
|
+
decision: { const: "revoke" },
|
|
724
|
+
state: { const: "revoked" },
|
|
725
|
+
action_key: STRING,
|
|
726
|
+
seq: INTEGER,
|
|
727
|
+
}, ["ok", "decision", "state", "action_key", "seq"]),
|
|
728
|
+
error: ERROR_SCHEMA,
|
|
729
|
+
exit_codes: BASE_EXIT_CODES,
|
|
730
|
+
},
|
|
731
|
+
{
|
|
732
|
+
name: "withdraw",
|
|
733
|
+
purpose: "Take back a pending request you opened, appending one approval.withdrawn. REQUESTER-ONLY: the actor must be the one that appended the approval.requested, and any other actor is refused not-requester. Legal only while the request is pending, and terminal once appended — a later grant, reject or revoke is refused request-withdrawn. Withdraw when you can no longer consume an answer (your wait elapsed, you cancelled, a newer request supersedes this one); a decision nobody can act on is human attention spent for nothing.",
|
|
734
|
+
human_only: false,
|
|
735
|
+
input: input({
|
|
736
|
+
positionals: positionals([{ name: "task", description: "the task id" }], 1),
|
|
737
|
+
flags: {
|
|
738
|
+
"--action": "string",
|
|
739
|
+
"--reason": "string",
|
|
740
|
+
"--note": "string",
|
|
741
|
+
...AS_FLAG,
|
|
742
|
+
...POLICY_FLAGS,
|
|
743
|
+
...LOG_FLAG,
|
|
744
|
+
...JSON_FLAG,
|
|
745
|
+
...HELP_FLAGS,
|
|
746
|
+
},
|
|
747
|
+
}),
|
|
748
|
+
output: object({
|
|
749
|
+
ok: { const: true },
|
|
750
|
+
task: STRING,
|
|
751
|
+
action_key: STRING,
|
|
752
|
+
state: { const: "withdrawn" },
|
|
753
|
+
reason: { enum: ["timeout", "cancelled", "superseded"] },
|
|
754
|
+
seq: INTEGER,
|
|
755
|
+
}, ["ok", "task", "action_key", "state", "reason", "seq"]),
|
|
756
|
+
error: ERROR_SCHEMA,
|
|
757
|
+
exit_codes: BASE_EXIT_CODES,
|
|
758
|
+
},
|
|
759
|
+
{
|
|
760
|
+
name: "expire",
|
|
761
|
+
purpose: "Lapse a request whose TTL has passed, appending one approval.expired event with the actor system:gate. No identity is accepted or resolved: no human decides an expiry, the clock does. The gate already refuses a late decision whether or not this event exists, so the verb makes a lapse visible rather than changing a verdict.",
|
|
762
|
+
human_only: true,
|
|
763
|
+
human_only_note: "The system verb, run by the daemon's sweep or by an operator's hand. It takes no identity at all, so there is no sense in which an agent could be its actor, and it writes a state transition into the log. Marked human_only so no wrapper offers it as an agent tool; the daemon calls the same code path.",
|
|
764
|
+
input: input({
|
|
765
|
+
positionals: positionals([{ name: "action-key", description: "the action's idempotency_key" }], 1),
|
|
766
|
+
flags: { ...POLICY_FLAGS, ...LOG_FLAG, ...JSON_FLAG, ...HELP_FLAGS },
|
|
767
|
+
}),
|
|
768
|
+
output: object({ ok: { const: true }, action_key: STRING, actor: { const: "system:gate" }, seq: INTEGER }, ["ok", "action_key", "actor", "seq"]),
|
|
769
|
+
error: ERROR_SCHEMA,
|
|
770
|
+
exit_codes: BASE_EXIT_CODES,
|
|
771
|
+
},
|
|
772
|
+
{
|
|
773
|
+
name: "token",
|
|
774
|
+
purpose: "Report whether a live, unspent execution token exists for an action key, and print its digest so an operator can match it against the log. IT DOES NOT PRINT THE TOKEN and no future version can: the raw value exists only in the output of the grant that minted it. Exit 0 means granted, unrevoked, unexpired and unconsumed; every other answer is a refusal naming which of the three deaths applied. Writes nothing.",
|
|
775
|
+
human_only: false,
|
|
776
|
+
input: input({
|
|
777
|
+
positionals: positionals([{ name: "action-key", description: "the action's idempotency_key" }], 1),
|
|
778
|
+
flags: { ...POLICY_FLAGS, ...LOG_FLAG, ...JSON_FLAG, ...HELP_FLAGS },
|
|
779
|
+
}),
|
|
780
|
+
output: object({
|
|
781
|
+
ok: { const: true },
|
|
782
|
+
action_key: STRING,
|
|
783
|
+
state: STRING,
|
|
784
|
+
live: BOOLEAN,
|
|
785
|
+
token_sha256: SHA256,
|
|
786
|
+
grant_seq: INTEGER,
|
|
787
|
+
class: STRING,
|
|
788
|
+
est_cost_usd: USD_AMOUNT,
|
|
789
|
+
payload_hash: nullable(SHA256),
|
|
790
|
+
task: STRING,
|
|
791
|
+
}, ["ok", "action_key", "state", "live", "token_sha256", "grant_seq", "class", "est_cost_usd", "task"]),
|
|
792
|
+
error: ERROR_SCHEMA,
|
|
793
|
+
exit_codes: BASE_EXIT_CODES,
|
|
794
|
+
},
|
|
795
|
+
{
|
|
796
|
+
name: "consume",
|
|
797
|
+
purpose: "Spend an execution token and append one execution.started (INTERNAL PLUMBING). `approval run` wraps it, and that is what to reach for; this exists so the token boundary is testable and an adapter integration can be driven by hand. It is the only sanctioned appender of execution.started on the manual path: a manual action's start event cannot exist without a verified token behind it.",
|
|
798
|
+
human_only: false,
|
|
799
|
+
human_only_note: "Not human-only: it spends a token a human already granted, which is exactly the authority an executing agent is meant to hold. It is marked INTERNAL in its purpose instead, because a wrapper should publish `run`.",
|
|
800
|
+
input: input({
|
|
801
|
+
positionals: positionals([{ name: "action-key", description: "the action's idempotency_key" }], 1),
|
|
802
|
+
flags: {
|
|
803
|
+
"--token": "string",
|
|
804
|
+
"--payload-hash": "string",
|
|
805
|
+
...AS_FLAG,
|
|
806
|
+
...POLICY_FLAGS,
|
|
807
|
+
...LOG_FLAG,
|
|
808
|
+
...JSON_FLAG,
|
|
809
|
+
...HELP_FLAGS,
|
|
810
|
+
},
|
|
811
|
+
}),
|
|
812
|
+
output: object({
|
|
813
|
+
ok: { const: true },
|
|
814
|
+
action_key: STRING,
|
|
815
|
+
event: { const: "execution.started" },
|
|
816
|
+
seq: INTEGER,
|
|
817
|
+
token_sha256: SHA256,
|
|
818
|
+
grant_seq: INTEGER,
|
|
819
|
+
class: STRING,
|
|
820
|
+
est_cost_usd: USD_AMOUNT,
|
|
821
|
+
}, ["ok", "action_key", "event", "seq", "token_sha256", "grant_seq", "class", "est_cost_usd"]),
|
|
822
|
+
error: ERROR_SCHEMA,
|
|
823
|
+
exit_codes: BASE_EXIT_CODES,
|
|
824
|
+
},
|
|
825
|
+
{
|
|
826
|
+
name: "run",
|
|
827
|
+
purpose: "Execute a command behind the gate: append execution.started BEFORE spawning it, spawn it with inherited stdio, append execution.completed or execution.failed carrying the child's real exit code, and exit with that same code. A manual action must present the token its grant printed; supervised and autonomous actions have no token and are enforced here through attestation, loop escalation, idempotency and budgets. Since APRV-193 a child spawned with NO token also runs with outbound network denied by the operating system and the credential material beside the log unreadable to it, while a child spawned WITH one keeps the network, because a human approved those exact bytes and a grant is the authority to reach the world; execution.started records which of those rooms the child ran in, and --no-sandbox is the recorded opt-out. The --json summary goes to STDERR, because stdout belongs to the child.",
|
|
828
|
+
human_only: false,
|
|
829
|
+
input: input({
|
|
830
|
+
positionals: positionals([{ name: "action-key", description: "the action's idempotency_key" }], 1),
|
|
831
|
+
flags: {
|
|
832
|
+
"--token": "string",
|
|
833
|
+
"--payload-hash": "string",
|
|
834
|
+
"--no-sandbox": "boolean",
|
|
835
|
+
...AS_FLAG,
|
|
836
|
+
...POLICY_FLAGS,
|
|
837
|
+
...LOG_FLAG,
|
|
838
|
+
...JSON_FLAG,
|
|
839
|
+
...HELP_FLAGS,
|
|
840
|
+
},
|
|
841
|
+
trailing: TRAILING,
|
|
842
|
+
}),
|
|
843
|
+
output: object({
|
|
844
|
+
ok: { const: true },
|
|
845
|
+
action_key: STRING,
|
|
846
|
+
task: STRING,
|
|
847
|
+
class: STRING,
|
|
848
|
+
autonomy: STRING,
|
|
849
|
+
started_seq: INTEGER,
|
|
850
|
+
outcome: { enum: ["execution.completed", "execution.failed"] },
|
|
851
|
+
outcome_seq: INTEGER,
|
|
852
|
+
exit_code: nullable(INTEGER),
|
|
853
|
+
payload_hash: nullable(SHA256),
|
|
854
|
+
}, [
|
|
855
|
+
"ok",
|
|
856
|
+
"action_key",
|
|
857
|
+
"task",
|
|
858
|
+
"class",
|
|
859
|
+
"autonomy",
|
|
860
|
+
"started_seq",
|
|
861
|
+
"outcome",
|
|
862
|
+
"outcome_seq",
|
|
863
|
+
"exit_code",
|
|
864
|
+
]),
|
|
865
|
+
error: ERROR_SCHEMA,
|
|
866
|
+
exit_codes: [
|
|
867
|
+
{ code: 0, meaning: "the child exited 0 (run is transparent: it exits with the child's code)" },
|
|
868
|
+
INTEGRITY,
|
|
869
|
+
USAGE,
|
|
870
|
+
TORN,
|
|
871
|
+
IO,
|
|
872
|
+
{
|
|
873
|
+
code: 5,
|
|
874
|
+
meaning: "NO VALID EXECUTION TOKEN: the class resolves to manual and no usable token was presented. Nothing was appended",
|
|
875
|
+
},
|
|
876
|
+
{
|
|
877
|
+
code: 127,
|
|
878
|
+
meaning: "the child's own 127 (run is transparent), or, before any append, the egress sandbox this machine has is present and broken: the command was NOT run, nothing was appended, and the token is unspent, so the same token executes once the mechanism works (APRV-193)",
|
|
879
|
+
},
|
|
880
|
+
],
|
|
881
|
+
},
|
|
882
|
+
{
|
|
883
|
+
name: "sandbox",
|
|
884
|
+
purpose: "Run a command with outbound network denied by the operating system (macOS sandbox-exec), with the credential-bearing variables scrubbed out of its environment and the credential material beside the log unreadable to it. It exits with the child's own exit code and appends NOTHING: it removes a capability rather than authorizing anything, so there is no record to write and the gate stays reachable because its IPC is a file rather than a socket. This is what the hook cannot do for the commands it merely ALLOWS: `npm test` runs whatever an agent wrote a minute ago, so the command's name stopped describing its effect, and this is how such a command runs where its effects cannot leave. The classifier reads `approval sandbox -- <cmd>` as the class of <cmd>, so wrapping a command neither hides it from the gate nor is punished by it. Refuses with 127 on a machine with no sandbox primitive: it makes one promise and will not run a command it cannot keep that promise for. An agent HARNESS cannot run under this, because a harness needs the model API and that is exactly what is denied.",
|
|
885
|
+
human_only: false,
|
|
886
|
+
input: input({
|
|
887
|
+
flags: {
|
|
888
|
+
"--allow-loopback": "boolean",
|
|
889
|
+
...LOG_FLAG,
|
|
890
|
+
...HELP_FLAGS,
|
|
891
|
+
},
|
|
892
|
+
trailing: TRAILING,
|
|
893
|
+
}),
|
|
894
|
+
// Transparent: stdout and stderr belong to the child, so there is no object
|
|
895
|
+
// of this verb's own to describe.
|
|
896
|
+
output: null,
|
|
897
|
+
error: ERROR_SCHEMA,
|
|
898
|
+
exit_codes: [
|
|
899
|
+
{ code: 0, meaning: "the child exited 0 (sandbox is transparent: it exits with the child's code)" },
|
|
900
|
+
USAGE,
|
|
901
|
+
{
|
|
902
|
+
code: 127,
|
|
903
|
+
meaning: "the command was NOT run: this machine has no working sandbox primitive, or the command is not on PATH. Nothing was appended either way",
|
|
904
|
+
},
|
|
905
|
+
],
|
|
906
|
+
},
|
|
907
|
+
{
|
|
908
|
+
name: "execution",
|
|
909
|
+
subcommand: "resolve",
|
|
910
|
+
purpose: "Record the outcome a HUMAN OBSERVED for a dangling execution — one that started and whose end nobody knows, the state a crash between execution.started and its outcome leaves. It demands a non-empty note, records exit_code null rather than inventing one, and marks attested_by_human so no reader mistakes an observation for a measurement. Nothing in this codebase closes a dangling execution automatically. --dangling is the BULK form: it lists every dangling execution with what this checkout can PROVE about each (the ref carrying the seq a daemon advance named, or nothing), asks once, and appends one human-attested completed per provable key with a note naming that ref. Keys nothing proves are listed with their own one-line command and left untouched.",
|
|
911
|
+
human_only: true,
|
|
912
|
+
input: input({
|
|
913
|
+
// Exactly one action key without `--dangling`, and none with it, which
|
|
914
|
+
// is a dependency between a positional and a flag that no positional
|
|
915
|
+
// TUPLE can state: a 1-tuple whose `minItems` is 0 is not a tuple at all
|
|
916
|
+
// under the strict Ajv this registry compiles with. So the arity is
|
|
917
|
+
// spelled as a bounded list and which of the two forms was asked for is
|
|
918
|
+
// checked in the verb, where the refusal can say so in a sentence.
|
|
919
|
+
positionals: {
|
|
920
|
+
type: "array",
|
|
921
|
+
items: { type: "string", title: "action-key", description: "the action's idempotency_key" },
|
|
922
|
+
maxItems: 1,
|
|
923
|
+
description: "the action key, for the single form; absent with --dangling",
|
|
924
|
+
},
|
|
925
|
+
flags: {
|
|
926
|
+
"--outcome": "string",
|
|
927
|
+
"--note": "string",
|
|
928
|
+
"--dangling": "boolean",
|
|
929
|
+
"--class": "string",
|
|
930
|
+
"--yes": "boolean",
|
|
931
|
+
...AS_FLAG,
|
|
932
|
+
...LOG_FLAG,
|
|
933
|
+
...JSON_FLAG,
|
|
934
|
+
...HELP_FLAGS,
|
|
935
|
+
},
|
|
936
|
+
}),
|
|
937
|
+
// Two shapes for two forms, and a reader branches on which keys are there:
|
|
938
|
+
// the single form answers about ONE key it was given, the bulk form answers
|
|
939
|
+
// with the LIST it derived. Collapsing them into one object with everything
|
|
940
|
+
// optional would make `action_key` absent mean two different things.
|
|
941
|
+
output: {
|
|
942
|
+
anyOf: [
|
|
943
|
+
object({
|
|
944
|
+
ok: { const: true },
|
|
945
|
+
action_key: STRING,
|
|
946
|
+
task: STRING,
|
|
947
|
+
event: { enum: ["execution.completed", "execution.failed"] },
|
|
948
|
+
outcome: { enum: ["completed", "failed"] },
|
|
949
|
+
seq: INTEGER,
|
|
950
|
+
attested_by_human: { const: true },
|
|
951
|
+
actor: STRING,
|
|
952
|
+
}, ["ok", "action_key", "task", "event", "outcome", "seq", "attested_by_human", "actor"]),
|
|
953
|
+
object({
|
|
954
|
+
ok: BOOLEAN,
|
|
955
|
+
dangling: arrayOf(object({
|
|
956
|
+
action_key: STRING,
|
|
957
|
+
task: nullable(STRING),
|
|
958
|
+
class: nullable(STRING),
|
|
959
|
+
seq: INTEGER,
|
|
960
|
+
ts: STRING,
|
|
961
|
+
provable: BOOLEAN,
|
|
962
|
+
proven_by: nullable(STRING),
|
|
963
|
+
proven_seq: nullable(INTEGER),
|
|
964
|
+
fix: STRING,
|
|
965
|
+
}, ["action_key", "task", "class", "seq", "ts", "provable", "proven_by", "proven_seq"])),
|
|
966
|
+
resolved: arrayOf(object({ action_key: STRING, seq: INTEGER, proven_by: nullable(STRING) }, ["action_key", "seq", "proven_by"])),
|
|
967
|
+
unresolved: arrayOf(STRING),
|
|
968
|
+
failed: arrayOf(object({ action_key: STRING, code: STRING, message: STRING }, [
|
|
969
|
+
"action_key",
|
|
970
|
+
"code",
|
|
971
|
+
"message",
|
|
972
|
+
])),
|
|
973
|
+
attested_by_human: { const: true },
|
|
974
|
+
actor: STRING,
|
|
975
|
+
}, ["ok", "dangling", "resolved", "unresolved", "actor"]),
|
|
976
|
+
],
|
|
977
|
+
},
|
|
978
|
+
error: ERROR_SCHEMA,
|
|
979
|
+
exit_codes: BASE_EXIT_CODES,
|
|
980
|
+
},
|
|
981
|
+
{
|
|
982
|
+
name: "execution",
|
|
983
|
+
subcommand: "reconcile",
|
|
984
|
+
purpose: "Record what a HUMAN ESTABLISHED about an INDETERMINATE execution — one whose side effect was attempted and whose outcome nobody knows, which is a different state from a dangling execution and from a failure. It appends execution.reconciled naming the execution.indeterminate record by seq and never rewriting it, so the doubt survives its own answer, and it demands the evidence as a non-empty note. Resolving not-executed re-opens the EFFECT and not this action: the idempotency key stays burned either way, so the repair is a fresh action and a fresh request. Nothing auto-resolves an indeterminate outcome, the daemon least of all.",
|
|
985
|
+
human_only: true,
|
|
986
|
+
input: input({
|
|
987
|
+
positionals: positionals([{ name: "action-key", description: "the action's idempotency_key" }], 1),
|
|
988
|
+
flags: {
|
|
989
|
+
"--resolution": "string",
|
|
990
|
+
"--note": "string",
|
|
991
|
+
...AS_FLAG,
|
|
992
|
+
...LOG_FLAG,
|
|
993
|
+
...JSON_FLAG,
|
|
994
|
+
...HELP_FLAGS,
|
|
995
|
+
},
|
|
996
|
+
}),
|
|
997
|
+
output: object({
|
|
998
|
+
ok: { const: true },
|
|
999
|
+
action_key: STRING,
|
|
1000
|
+
task: STRING,
|
|
1001
|
+
event: { const: "execution.reconciled" },
|
|
1002
|
+
resolution: { enum: ["executed", "not-executed"] },
|
|
1003
|
+
indeterminate_seq: INTEGER,
|
|
1004
|
+
seq: INTEGER,
|
|
1005
|
+
attested_by_human: { const: true },
|
|
1006
|
+
actor: STRING,
|
|
1007
|
+
}, [
|
|
1008
|
+
"ok",
|
|
1009
|
+
"action_key",
|
|
1010
|
+
"task",
|
|
1011
|
+
"event",
|
|
1012
|
+
"resolution",
|
|
1013
|
+
"indeterminate_seq",
|
|
1014
|
+
"seq",
|
|
1015
|
+
"attested_by_human",
|
|
1016
|
+
"actor",
|
|
1017
|
+
]),
|
|
1018
|
+
error: ERROR_SCHEMA,
|
|
1019
|
+
exit_codes: BASE_EXIT_CODES,
|
|
1020
|
+
},
|
|
1021
|
+
{
|
|
1022
|
+
name: "audit",
|
|
1023
|
+
subcommand: "list",
|
|
1024
|
+
purpose: "The open sampled-audit backlog: audit.sampled records with no audit.reviewed after them. It reads a verified log, writes nothing, and reports beside the backlog whether sampling is running at all, because an empty backlog means one thing when the sampler is on and quite another when it is off.",
|
|
1025
|
+
human_only: false,
|
|
1026
|
+
input: input({
|
|
1027
|
+
flags: { "--all": "boolean", ...POLICY_FLAGS, ...LOG_FLAG, ...JSON_FLAG, ...HELP_FLAGS },
|
|
1028
|
+
}),
|
|
1029
|
+
output: object({
|
|
1030
|
+
ok: { const: true },
|
|
1031
|
+
sampling: object({
|
|
1032
|
+
enabled: BOOLEAN,
|
|
1033
|
+
rate: nullable(NUMBER),
|
|
1034
|
+
secret_env: nullable(STRING),
|
|
1035
|
+
reason: nullable(STRING),
|
|
1036
|
+
}, ["enabled", "rate", "secret_env", "reason"]),
|
|
1037
|
+
open: INTEGER,
|
|
1038
|
+
samples: arrayOf(OPEN_OBJECT),
|
|
1039
|
+
}, ["ok", "sampling", "open", "samples"]),
|
|
1040
|
+
error: ERROR_SCHEMA,
|
|
1041
|
+
exit_codes: BASE_EXIT_CODES,
|
|
1042
|
+
},
|
|
1043
|
+
{
|
|
1044
|
+
name: "audit",
|
|
1045
|
+
subcommand: "review",
|
|
1046
|
+
purpose: "Record that a human looked at one sampled supervised action. There is deliberately no `audit sample`: selection is the runtime's, made by the daemon from an operator-held secret, because a party that could sample could also decline to sample itself. A runtime that could mark its own samples reviewed would be a supervision backlog that empties itself.",
|
|
1047
|
+
human_only: true,
|
|
1048
|
+
input: input({
|
|
1049
|
+
positionals: positionals([
|
|
1050
|
+
{
|
|
1051
|
+
name: "seq|action-key",
|
|
1052
|
+
description: "the seq of the audit.sampled record, or an action key with one open sample",
|
|
1053
|
+
},
|
|
1054
|
+
], 1),
|
|
1055
|
+
flags: {
|
|
1056
|
+
"--note": "string",
|
|
1057
|
+
"--deny": "boolean",
|
|
1058
|
+
...AS_FLAG,
|
|
1059
|
+
...LOG_FLAG,
|
|
1060
|
+
...JSON_FLAG,
|
|
1061
|
+
...HELP_FLAGS,
|
|
1062
|
+
},
|
|
1063
|
+
}),
|
|
1064
|
+
output: object({
|
|
1065
|
+
ok: { const: true },
|
|
1066
|
+
seq: INTEGER,
|
|
1067
|
+
sample_seq: INTEGER,
|
|
1068
|
+
action_key: STRING,
|
|
1069
|
+
task: STRING,
|
|
1070
|
+
verdict: { enum: ["ok", "denied"] },
|
|
1071
|
+
obligation_seq: nullable(INTEGER),
|
|
1072
|
+
actor: STRING,
|
|
1073
|
+
}, ["ok", "seq", "sample_seq", "action_key", "task", "verdict", "obligation_seq", "actor"]),
|
|
1074
|
+
error: ERROR_SCHEMA,
|
|
1075
|
+
exit_codes: BASE_EXIT_CODES,
|
|
1076
|
+
},
|
|
1077
|
+
{
|
|
1078
|
+
name: "audit",
|
|
1079
|
+
subcommand: "obligations",
|
|
1080
|
+
purpose: "The open reconciliation backlog: reconciliation.required records with no reconciliation.satisfied after them. An obligation is opened by a retrospective DENIAL and closed only by a person, because a runtime that could close its own obligations would be a backlog that empties itself. It reads a verified log and writes nothing.",
|
|
1081
|
+
human_only: false,
|
|
1082
|
+
input: input({
|
|
1083
|
+
flags: { "--all": "boolean", ...LOG_FLAG, ...JSON_FLAG, ...HELP_FLAGS },
|
|
1084
|
+
}),
|
|
1085
|
+
output: object({
|
|
1086
|
+
ok: { const: true },
|
|
1087
|
+
open: INTEGER,
|
|
1088
|
+
obligations: arrayOf(OPEN_OBJECT),
|
|
1089
|
+
}, ["ok", "open", "obligations"]),
|
|
1090
|
+
error: ERROR_SCHEMA,
|
|
1091
|
+
exit_codes: BASE_EXIT_CODES,
|
|
1092
|
+
},
|
|
1093
|
+
{
|
|
1094
|
+
name: "audit",
|
|
1095
|
+
subcommand: "reconcile",
|
|
1096
|
+
purpose: "Record that a human discharged one reconciliation obligation. A retrospective denial cannot undo anything, so what it creates is an obligation: revert a reversible action THROUGH THE GATE, or review the class that permitted an irreversible one. A gated-revert obligation is checked against the chain rather than the claim — without an execution.completed for the named revert this refuses and appends nothing.",
|
|
1097
|
+
human_only: true,
|
|
1098
|
+
input: input({
|
|
1099
|
+
positionals: positionals([
|
|
1100
|
+
{
|
|
1101
|
+
name: "obligation-seq",
|
|
1102
|
+
description: "the seq of the reconciliation.required record, from `audit obligations`",
|
|
1103
|
+
},
|
|
1104
|
+
], 1),
|
|
1105
|
+
flags: {
|
|
1106
|
+
"--note": "string",
|
|
1107
|
+
"--revert": "string",
|
|
1108
|
+
...AS_FLAG,
|
|
1109
|
+
...LOG_FLAG,
|
|
1110
|
+
...JSON_FLAG,
|
|
1111
|
+
...HELP_FLAGS,
|
|
1112
|
+
},
|
|
1113
|
+
}),
|
|
1114
|
+
output: object({
|
|
1115
|
+
ok: { const: true },
|
|
1116
|
+
seq: INTEGER,
|
|
1117
|
+
obligation_seq: INTEGER,
|
|
1118
|
+
action_key: STRING,
|
|
1119
|
+
task: nullable(STRING),
|
|
1120
|
+
class: STRING,
|
|
1121
|
+
obligation: { enum: ["gated-revert", "policy-finding"] },
|
|
1122
|
+
actor: STRING,
|
|
1123
|
+
}, ["ok", "seq", "obligation_seq", "action_key", "class", "obligation", "actor"]),
|
|
1124
|
+
error: ERROR_SCHEMA,
|
|
1125
|
+
exit_codes: BASE_EXIT_CODES,
|
|
1126
|
+
},
|
|
1127
|
+
{
|
|
1128
|
+
name: "wait",
|
|
1129
|
+
purpose: "Block until every approval.requested of a task has a decision, or the timeout elapses. THE EXIT CODE IS THE DECISION: 0 granted, 1 rejected, revoked or withdrawn, 3 expired, 6 timeout. It writes nothing by default, not even the expiry it may derive; --withdraw-on-timeout is the one exception, appending approval.withdrawn for the requests this actor opened so a question nobody can answer to does not sit in a human's queue. Only the manual path produces requests to wait for, so a task with none returns immediately at exit 0. Under policy token_delivery: sealed, a granted action's --json entry also carries the raw execution token, opened from the grant's ciphertext with the private key this machine kept when it opened the request; that removes the terminal paste and works across machines. Recovering a minted token is not minting one: it still exists only because a human granted it, still binds to the payload bytes, and is still single-use.",
|
|
1130
|
+
human_only: false,
|
|
1131
|
+
input: input({
|
|
1132
|
+
positionals: positionals([{ name: "task", description: "the task id" }], 1),
|
|
1133
|
+
flags: {
|
|
1134
|
+
"--timeout": "string",
|
|
1135
|
+
"--interval": "string",
|
|
1136
|
+
"--withdraw-on-timeout": "boolean",
|
|
1137
|
+
...AS_FLAG,
|
|
1138
|
+
...POLICY_FLAGS,
|
|
1139
|
+
...LOG_FLAG,
|
|
1140
|
+
...JSON_FLAG,
|
|
1141
|
+
...HELP_FLAGS,
|
|
1142
|
+
},
|
|
1143
|
+
}),
|
|
1144
|
+
output: object({
|
|
1145
|
+
ok: BOOLEAN,
|
|
1146
|
+
task: STRING,
|
|
1147
|
+
status: { enum: ["granted", "rejected", "withdrawn", "expired", "timeout"] },
|
|
1148
|
+
actions: arrayOf(object({
|
|
1149
|
+
action_key: STRING,
|
|
1150
|
+
state: STRING,
|
|
1151
|
+
seq: nullable(INTEGER),
|
|
1152
|
+
// APRV-105: the raw execution token, when sealed delivery put one
|
|
1153
|
+
// within this process's reach. Optional and present only on a
|
|
1154
|
+
// granted action under `token_delivery: sealed`; `--json` only,
|
|
1155
|
+
// never the human render, which is a terminal.
|
|
1156
|
+
token: STRING,
|
|
1157
|
+
}, ["action_key", "state", "seq"])),
|
|
1158
|
+
}, ["ok", "task", "status", "actions"]),
|
|
1159
|
+
error: ERROR_SCHEMA,
|
|
1160
|
+
exit_codes: [
|
|
1161
|
+
{ code: 0, meaning: "granted (a task with no requests is granted vacuously)" },
|
|
1162
|
+
{
|
|
1163
|
+
code: 1,
|
|
1164
|
+
meaning: "NOT AUTHORIZED and terminal — a human said no (rejected/revoked), or the requester withdrew the request; or the log is corrupt. `status` says which",
|
|
1165
|
+
},
|
|
1166
|
+
USAGE,
|
|
1167
|
+
{ code: 3, meaning: "EXPIRED — the TTL lapsed before a decision landed; or a torn tail" },
|
|
1168
|
+
IO,
|
|
1169
|
+
{
|
|
1170
|
+
code: 6,
|
|
1171
|
+
meaning: "TIMEOUT — the wait elapsed with request(s) undecided. Nothing was appended, they are still live, and waiting again is legitimate",
|
|
1172
|
+
},
|
|
1173
|
+
],
|
|
1174
|
+
},
|
|
1175
|
+
{
|
|
1176
|
+
name: "queue",
|
|
1177
|
+
purpose: "The pending-decision INBOX: exactly the requests awaiting a human and inside their TTL, with the action key, task, class, declared cost, request time and TTL remaining. Nothing else — dangling executions, attestation state, budgets and escalations all live in `status`. Writes nothing, and exits 0 whenever the log could be read: an empty inbox is a healthy inbox.",
|
|
1178
|
+
human_only: false,
|
|
1179
|
+
input: input({ flags: { ...POLICY_FLAGS, ...LOG_FLAG, ...JSON_FLAG, ...HELP_FLAGS } }),
|
|
1180
|
+
output: object({
|
|
1181
|
+
ok: { const: true },
|
|
1182
|
+
pending: arrayOf(object({
|
|
1183
|
+
action_key: STRING,
|
|
1184
|
+
task: STRING,
|
|
1185
|
+
class: STRING,
|
|
1186
|
+
est_cost_usd: nullable(USD_AMOUNT),
|
|
1187
|
+
requested_ts: STRING,
|
|
1188
|
+
seq: INTEGER,
|
|
1189
|
+
ttl_remaining_ms: nullable(INTEGER),
|
|
1190
|
+
}, ["action_key", "task", "class", "est_cost_usd", "requested_ts", "seq", "ttl_remaining_ms"])),
|
|
1191
|
+
}, ["ok", "pending"]),
|
|
1192
|
+
error: ERROR_SCHEMA,
|
|
1193
|
+
exit_codes: [OK, USAGE, TORN, IO],
|
|
1194
|
+
},
|
|
1195
|
+
// APRV-214, amended SPEC.md §5.2: the open window. Three entries because the
|
|
1196
|
+
// three subcommands answer to different people — a person opening a bypass, a
|
|
1197
|
+
// person ending one, and anybody at all asking whether one stands.
|
|
1198
|
+
{
|
|
1199
|
+
name: "gate",
|
|
1200
|
+
subcommand: "open",
|
|
1201
|
+
purpose: "Open the harness gate for a bounded time so the gate itself can be debugged: while the window stands, the hook ALLOWS every gated shell command and file edit under the root, recording each as gate.bypassed, ahead of the policy load, the attestation check, the loop floor and the human gate. Default 30m, cap 24h, --reason required. The window's whole state is the log (gate.opened, closed by gate.closed or by lapsing, which appends nothing); no file holds it. It never reaches .approval/log/, a class the policy reserves to human hands, a command the classifier cannot read, or a log that cannot be verified. Bypassed calls are charged to no budget and enter no retrospective sample.",
|
|
1202
|
+
human_only: true,
|
|
1203
|
+
human_only_note: "It suspends the policy. There is no --yes and no --force: stdin must be a terminal and the word `understood` must be typed in full, which is what puts it out of reach of a harness shell tool. It also classifies `policy.core`, so a policy holding that human-only makes the hook refuse an agent that tries.",
|
|
1204
|
+
input: input({
|
|
1205
|
+
positionals: positionals([{ name: "open", description: "the subcommand" }], 1),
|
|
1206
|
+
flags: {
|
|
1207
|
+
"--for": "string",
|
|
1208
|
+
"--reason": "string",
|
|
1209
|
+
...AS_FLAG,
|
|
1210
|
+
...LOG_FLAG,
|
|
1211
|
+
...HELP_FLAGS,
|
|
1212
|
+
},
|
|
1213
|
+
}),
|
|
1214
|
+
// No `--json` success shape exists, and that is the contract: `--json` on
|
|
1215
|
+
// this verb is refused with `gate-stdin-not-tty`, because an answer shaped
|
|
1216
|
+
// for a machine implies a machine asking a question only a person answers.
|
|
1217
|
+
output: null,
|
|
1218
|
+
error: ERROR_SCHEMA,
|
|
1219
|
+
exit_codes: BASE_EXIT_CODES,
|
|
1220
|
+
},
|
|
1221
|
+
{
|
|
1222
|
+
name: "gate",
|
|
1223
|
+
subcommand: "close",
|
|
1224
|
+
purpose: "End the open window now rather than at its expiry, appending gate.closed naming the seq of the gate.opened it ends. Human-only like the opening, though this half only ever TIGHTENS: there is no confirmation to type, because a ceremony guarding the safe direction is one people learn to type past. Refuses `gate-not-open` when no window stands, which includes one that has already lapsed — a lapse appends nothing and needs no closing record.",
|
|
1225
|
+
human_only: true,
|
|
1226
|
+
human_only_note: "The pair is one ceremony and both halves are the human's, so the actor rule is uniform. An agent closing a window could authorize nothing by it, and the uniformity is what makes the rule statable in one sentence.",
|
|
1227
|
+
input: input({
|
|
1228
|
+
positionals: positionals([{ name: "close", description: "the subcommand" }], 1),
|
|
1229
|
+
flags: { "--note": "string", ...AS_FLAG, ...LOG_FLAG, ...JSON_FLAG, ...HELP_FLAGS },
|
|
1230
|
+
}),
|
|
1231
|
+
output: object({
|
|
1232
|
+
ok: { const: true },
|
|
1233
|
+
seq: INTEGER,
|
|
1234
|
+
opened_seq: INTEGER,
|
|
1235
|
+
actor: STRING,
|
|
1236
|
+
bypassed: INTEGER,
|
|
1237
|
+
}, ["ok", "seq", "opened_seq", "actor", "bypassed"]),
|
|
1238
|
+
error: ERROR_SCHEMA,
|
|
1239
|
+
exit_codes: BASE_EXIT_CODES,
|
|
1240
|
+
},
|
|
1241
|
+
{
|
|
1242
|
+
name: "gate",
|
|
1243
|
+
subcommand: "status",
|
|
1244
|
+
purpose: "Report whether a window is open, and if so which: the seq of the gate.opened, who opened it, their reason, when it expires, how long is left, and how many calls have been bypassed under it. Derived from the verified log alone, so it is the same fact the hook and `approval status` derive. Reads only; appends nothing and decides nothing.",
|
|
1245
|
+
human_only: false,
|
|
1246
|
+
input: input({
|
|
1247
|
+
positionals: positionals([{ name: "status", description: "the subcommand" }], 1),
|
|
1248
|
+
flags: { ...LOG_FLAG, ...JSON_FLAG, ...HELP_FLAGS },
|
|
1249
|
+
}),
|
|
1250
|
+
output: object({
|
|
1251
|
+
ok: { const: true },
|
|
1252
|
+
open: BOOLEAN,
|
|
1253
|
+
window: nullable(object({
|
|
1254
|
+
seq: INTEGER,
|
|
1255
|
+
opened_at: STRING,
|
|
1256
|
+
opened_by: STRING,
|
|
1257
|
+
reason: STRING,
|
|
1258
|
+
expires_at: STRING,
|
|
1259
|
+
remaining_ms: INTEGER,
|
|
1260
|
+
bypassed: INTEGER,
|
|
1261
|
+
scope: STRING,
|
|
1262
|
+
}, [
|
|
1263
|
+
"seq",
|
|
1264
|
+
"opened_at",
|
|
1265
|
+
"opened_by",
|
|
1266
|
+
"reason",
|
|
1267
|
+
"expires_at",
|
|
1268
|
+
"remaining_ms",
|
|
1269
|
+
"bypassed",
|
|
1270
|
+
"scope",
|
|
1271
|
+
])),
|
|
1272
|
+
}, ["ok", "open", "window"]),
|
|
1273
|
+
error: ERROR_SCHEMA,
|
|
1274
|
+
exit_codes: [OK, INTEGRITY, USAGE, TORN, IO],
|
|
1275
|
+
},
|
|
1276
|
+
{
|
|
1277
|
+
name: "status",
|
|
1278
|
+
purpose: "System HEALTH, from the log: attestation state, the latest chain verdict, dangling executions, indeterminate executions, budget headroom from a zero-cost probe, loop escalations, and the payload store's size. Exit 1 when any of those needs attention. `dangling` is executions the runtime meant to watch and did not, and never harness executions, which are terminal by design and gain no outcome; `indeterminate` is side effects that were attempted and whose fate nobody has established, and it appears only when there are some. This is what an operator must fix; `queue` is what a human must answer, and neither carries the other's content. Writes nothing.",
|
|
1279
|
+
human_only: false,
|
|
1280
|
+
input: input({ flags: { ...POLICY_FLAGS, ...LOG_FLAG, ...JSON_FLAG, ...HELP_FLAGS } }),
|
|
1281
|
+
output: object({
|
|
1282
|
+
ok: { const: true },
|
|
1283
|
+
healthy: BOOLEAN,
|
|
1284
|
+
attestation: object({ state: STRING, seq: nullable(INTEGER) }, ["state", "seq"]),
|
|
1285
|
+
verification: object({ status: STRING, records: nullable(INTEGER) }, ["status", "records"]),
|
|
1286
|
+
dangling: arrayOf(OPEN_OBJECT),
|
|
1287
|
+
indeterminate: arrayOf(OPEN_OBJECT),
|
|
1288
|
+
budgets: arrayOf(OPEN_OBJECT),
|
|
1289
|
+
// APRV-145: each entry gained a `scope` field naming which derivation
|
|
1290
|
+
// produced its key — `task`, `session` or `actor`.
|
|
1291
|
+
loop_escalations: arrayOf(OPEN_OBJECT),
|
|
1292
|
+
// APRV-145: how many harness starts carry an outcome and how many do
|
|
1293
|
+
// not. INFORMATIONAL, and outside `healthy` and the exit code for the
|
|
1294
|
+
// reason `anomalies` is: a coverage measurement, not a verdict.
|
|
1295
|
+
harness_outcomes: OPEN_OBJECT,
|
|
1296
|
+
// APRV-245: what git witnessed on this branch, and how much of it the
|
|
1297
|
+
// log can account for. INFORMATIONAL and outside `healthy` and the exit
|
|
1298
|
+
// code, exactly as `harness_outcomes` is. Always present, with
|
|
1299
|
+
// `available: false` and a reason where there is no checkout or no
|
|
1300
|
+
// trunk ref; the whole report is `approval coverage`.
|
|
1301
|
+
coverage: object({
|
|
1302
|
+
available: BOOLEAN,
|
|
1303
|
+
reason: nullable(STRING),
|
|
1304
|
+
observed: INTEGER,
|
|
1305
|
+
covered: INTEGER,
|
|
1306
|
+
}, ["available", "reason", "observed", "covered"]),
|
|
1307
|
+
// APRV-127: reconciliation obligations opened by a retrospective denial
|
|
1308
|
+
// and not yet discharged. Counts toward `healthy`, like `dangling`.
|
|
1309
|
+
reconciliation: arrayOf(OPEN_OBJECT),
|
|
1310
|
+
payload_store: OPEN_OBJECT,
|
|
1311
|
+
anomalies: arrayOf(OPEN_OBJECT),
|
|
1312
|
+
// APRV-214: present only while a window stands, so a repository with
|
|
1313
|
+
// none emits the object it always emitted. It counts toward `healthy`,
|
|
1314
|
+
// which is why it is reported here rather than only by `gate status`.
|
|
1315
|
+
gate_window: OPEN_OBJECT,
|
|
1316
|
+
}, [
|
|
1317
|
+
"ok",
|
|
1318
|
+
"healthy",
|
|
1319
|
+
"attestation",
|
|
1320
|
+
"verification",
|
|
1321
|
+
"dangling",
|
|
1322
|
+
"budgets",
|
|
1323
|
+
"loop_escalations",
|
|
1324
|
+
"harness_outcomes",
|
|
1325
|
+
"coverage",
|
|
1326
|
+
"reconciliation",
|
|
1327
|
+
"payload_store",
|
|
1328
|
+
]),
|
|
1329
|
+
error: ERROR_SCHEMA,
|
|
1330
|
+
exit_codes: [OK, { code: 1, meaning: "something needs attention" }, USAGE, IO],
|
|
1331
|
+
},
|
|
1332
|
+
{
|
|
1333
|
+
name: "coverage",
|
|
1334
|
+
purpose: "What the witnesses this project does NOT write say happened, joined to the verified log. Git history, `gh` and an adapter's provider each record side effects that no agent editing its own log can reach, and this verb reads them back and reports, per effect, the evidence in the log (a `task.registered`, `approval.granted`, `execution.started` or `execution.completed` of a matching class inside the effect's window) or `none`. It is INFORMATIONAL, exactly as the harness-start coverage in `status` is: exit 0 with or without gaps, because a coverage measurement is not an integrity verdict and a control an operator learns to silence is worse than one that reports beside the verdict. It writes nothing anywhere and reads only verified records. A source that could not be reached is reported unavailable with its reason, never as an absence of effects, and a green line says nothing about effects made with a credential the agent holds itself; the remedy for those is custody, not a bigger report.",
|
|
1335
|
+
human_only: false,
|
|
1336
|
+
input: input({
|
|
1337
|
+
flags: {
|
|
1338
|
+
"--base": "string",
|
|
1339
|
+
"--head": "string",
|
|
1340
|
+
"--since": "string",
|
|
1341
|
+
"--until": "string",
|
|
1342
|
+
"--source": "string",
|
|
1343
|
+
"--vault": "string",
|
|
1344
|
+
...POLICY_FLAGS,
|
|
1345
|
+
...LOG_FLAG,
|
|
1346
|
+
...JSON_FLAG,
|
|
1347
|
+
...HELP_FLAGS,
|
|
1348
|
+
},
|
|
1349
|
+
}),
|
|
1350
|
+
output: object({
|
|
1351
|
+
ok: { const: true },
|
|
1352
|
+
window: object({ base: STRING, head: STRING, since: STRING, until: STRING }, ["base", "head", "since", "until"]),
|
|
1353
|
+
sources: arrayOf(object({
|
|
1354
|
+
name: STRING,
|
|
1355
|
+
available: BOOLEAN,
|
|
1356
|
+
reason: nullable(STRING),
|
|
1357
|
+
effects: arrayOf(object({
|
|
1358
|
+
id: STRING,
|
|
1359
|
+
class: STRING,
|
|
1360
|
+
at: STRING,
|
|
1361
|
+
// A hint, printed and never matched on: a commit author
|
|
1362
|
+
// email is whatever the committer configured (SPEC.md
|
|
1363
|
+
// §11.1 invariant 4).
|
|
1364
|
+
actor_hint: nullable(STRING),
|
|
1365
|
+
detail: STRING,
|
|
1366
|
+
path: nullable(STRING),
|
|
1367
|
+
match: { enum: ["exact", "family", "protected-path", "none"] },
|
|
1368
|
+
// Two kinds of proof under one key, and the null halves say
|
|
1369
|
+
// which: a record seq a reader can paste into `approval log
|
|
1370
|
+
// tail`, or the protected-path guard's byte-level verdict.
|
|
1371
|
+
evidence: nullable(object({
|
|
1372
|
+
seq: nullable(INTEGER),
|
|
1373
|
+
event: nullable(STRING),
|
|
1374
|
+
verdict: nullable(STRING),
|
|
1375
|
+
}, ["seq", "event", "verdict"])),
|
|
1376
|
+
}, ["id", "class", "at", "actor_hint", "detail", "path", "match", "evidence"])),
|
|
1377
|
+
covered: INTEGER,
|
|
1378
|
+
observed: INTEGER,
|
|
1379
|
+
}, ["name", "available", "reason", "effects", "covered", "observed"])),
|
|
1380
|
+
}, ["ok", "window", "sources"]),
|
|
1381
|
+
error: ERROR_SCHEMA,
|
|
1382
|
+
exit_codes: [OK, USAGE, TORN, IO],
|
|
1383
|
+
},
|
|
1384
|
+
{
|
|
1385
|
+
name: "doctor",
|
|
1386
|
+
purpose: "Is this MACHINE able to run the system? Fifteen checks in cascade order — build freshness, declared identity, policy attestation, chain health, Telegram, the web port, the payload store, audit sampling, envelope integrity, the vault, the environment, log drift, reconciliation, harness hook outcomes, harness hook wiring — each with a concrete repair that begins with a command you can paste. Appends nothing, sends nothing, repairs nothing. `status` reports the system's health; doctor reports whether this machine can run it at all.",
|
|
1387
|
+
human_only: false,
|
|
1388
|
+
input: input({
|
|
1389
|
+
flags: {
|
|
1390
|
+
...LOG_FLAG,
|
|
1391
|
+
...POLICY_FLAGS,
|
|
1392
|
+
"--tasks": "string",
|
|
1393
|
+
"--api-base": "string",
|
|
1394
|
+
"--root": "string",
|
|
1395
|
+
...JSON_FLAG,
|
|
1396
|
+
...HELP_FLAGS,
|
|
1397
|
+
},
|
|
1398
|
+
}),
|
|
1399
|
+
output: object({
|
|
1400
|
+
ok: BOOLEAN,
|
|
1401
|
+
checks: arrayOf(object({
|
|
1402
|
+
check: STRING,
|
|
1403
|
+
status: { enum: ["pass", "fail", "skip"] },
|
|
1404
|
+
detail: STRING,
|
|
1405
|
+
fix: STRING,
|
|
1406
|
+
}, ["check", "status", "detail"])),
|
|
1407
|
+
}, ["ok", "checks"]),
|
|
1408
|
+
error: ERROR_SCHEMA,
|
|
1409
|
+
exit_codes: [
|
|
1410
|
+
OK,
|
|
1411
|
+
{ code: 1, meaning: "at least one check failed" },
|
|
1412
|
+
USAGE,
|
|
1413
|
+
{ code: 4, meaning: "doctor itself could not look" },
|
|
1414
|
+
],
|
|
1415
|
+
},
|
|
1416
|
+
{
|
|
1417
|
+
name: "channel",
|
|
1418
|
+
subcommand: "cli",
|
|
1419
|
+
purpose: "Render the pending queue in this terminal with the SPEC.md §9 [computed]/[claimed] markers and each manual action's full payload in delimiters, and — with a terminal — collect decisions through the same human-only gate `grant` and `reject` call. Without a TTY, and always with --json, it prints the queue and exits 0 without reading stdin.",
|
|
1420
|
+
human_only: true,
|
|
1421
|
+
human_only_note: "It records human decisions through the human-only gate, and its interactive path mints execution tokens. A wrapper offering it to an agent would be offering a route to approve.",
|
|
1422
|
+
input: input({
|
|
1423
|
+
flags: {
|
|
1424
|
+
...LOG_FLAG,
|
|
1425
|
+
"--policy-dir": "string",
|
|
1426
|
+
"--policy": "string",
|
|
1427
|
+
"--payload-dir": "string",
|
|
1428
|
+
...AS_FLAG,
|
|
1429
|
+
"--interactive": "boolean",
|
|
1430
|
+
...JSON_FLAG,
|
|
1431
|
+
...HELP_FLAGS,
|
|
1432
|
+
},
|
|
1433
|
+
}),
|
|
1434
|
+
output: object({
|
|
1435
|
+
ok: { const: true },
|
|
1436
|
+
channel: { const: "cli" },
|
|
1437
|
+
interactive: BOOLEAN,
|
|
1438
|
+
pending: arrayOf(OPEN_OBJECT),
|
|
1439
|
+
skipped: arrayOf(OPEN_OBJECT),
|
|
1440
|
+
}, ["ok", "channel", "interactive", "pending", "skipped"]),
|
|
1441
|
+
error: ERROR_SCHEMA,
|
|
1442
|
+
exit_codes: BASE_EXIT_CODES,
|
|
1443
|
+
},
|
|
1444
|
+
{
|
|
1445
|
+
name: "channel",
|
|
1446
|
+
subcommand: "web",
|
|
1447
|
+
purpose: "Serve the pending queue as a page on 127.0.0.1 and nothing else, with Grant/Reject forms and a batch gesture, until interrupted. The loopback host is hard-coded because the page has NO AUTHENTICATION and the interface is the access control. Its --json output is one object per line, not one per invocation.",
|
|
1448
|
+
human_only: true,
|
|
1449
|
+
human_only_note: "Same reason as `channel cli`: it exists to collect a human's decision, and it displays minted execution tokens.",
|
|
1450
|
+
input: input({
|
|
1451
|
+
flags: {
|
|
1452
|
+
"--port": "string",
|
|
1453
|
+
...LOG_FLAG,
|
|
1454
|
+
...POLICY_FLAGS,
|
|
1455
|
+
"--payload-dir": "string",
|
|
1456
|
+
...AS_FLAG,
|
|
1457
|
+
...JSON_FLAG,
|
|
1458
|
+
...HELP_FLAGS,
|
|
1459
|
+
},
|
|
1460
|
+
}),
|
|
1461
|
+
output: null,
|
|
1462
|
+
error: ERROR_SCHEMA,
|
|
1463
|
+
exit_codes: BASE_EXIT_CODES,
|
|
1464
|
+
},
|
|
1465
|
+
{
|
|
1466
|
+
name: "channel",
|
|
1467
|
+
subcommand: "telegram listen",
|
|
1468
|
+
purpose: "Deliver the pending queue to the configured Telegram chat on every poll cycle and long-poll for Approve/Reject taps, recording each through the same human-only gate. The bot token and chat id come from the environment variables the policy NAMES; there is no flag for either value. The raw execution token is printed on this terminal and is never sent to Telegram.",
|
|
1469
|
+
human_only: true,
|
|
1470
|
+
human_only_note: "It records human decisions. It is also long-lived and holds a transport credential, so it is an operator's process rather than a call an agent makes.",
|
|
1471
|
+
input: input({
|
|
1472
|
+
flags: {
|
|
1473
|
+
"--once": "boolean",
|
|
1474
|
+
...AS_FLAG,
|
|
1475
|
+
"--payloads": "string",
|
|
1476
|
+
...POLICY_FLAGS,
|
|
1477
|
+
...LOG_FLAG,
|
|
1478
|
+
"--api-base": "string",
|
|
1479
|
+
"--poll-timeout": "string",
|
|
1480
|
+
...JSON_FLAG,
|
|
1481
|
+
...HELP_FLAGS,
|
|
1482
|
+
},
|
|
1483
|
+
}),
|
|
1484
|
+
output: null,
|
|
1485
|
+
error: ERROR_SCHEMA,
|
|
1486
|
+
exit_codes: BASE_EXIT_CODES,
|
|
1487
|
+
},
|
|
1488
|
+
{
|
|
1489
|
+
name: "channel",
|
|
1490
|
+
subcommand: "telegram health",
|
|
1491
|
+
purpose: "Report whether the bot token and chat id variables this policy names are set. It makes NO network call and prints no value, only whether one is present. Exit 0 when both are configured, 1 when either is missing.",
|
|
1492
|
+
human_only: false,
|
|
1493
|
+
input: input({ flags: { ...POLICY_FLAGS, ...JSON_FLAG, ...HELP_FLAGS } }),
|
|
1494
|
+
output: object({
|
|
1495
|
+
ok: BOOLEAN,
|
|
1496
|
+
channel: { const: "telegram" },
|
|
1497
|
+
token_env: STRING,
|
|
1498
|
+
token_set: BOOLEAN,
|
|
1499
|
+
chat_env: STRING,
|
|
1500
|
+
chat_id: nullable(STRING),
|
|
1501
|
+
}, ["ok", "channel", "token_env", "token_set", "chat_env", "chat_id"]),
|
|
1502
|
+
error: ERROR_SCHEMA,
|
|
1503
|
+
exit_codes: [OK, { code: 1, meaning: "a credential variable is unset" }, USAGE],
|
|
1504
|
+
},
|
|
1505
|
+
{
|
|
1506
|
+
name: "daemon",
|
|
1507
|
+
subcommand: "run",
|
|
1508
|
+
purpose: "The SPEC.md §10.2 watch loop, in the foreground: record envelope.drift where a task file's state contradicts the log, append approval.expired for lapsed requests, write the log's state back into the task files, regenerate QUEUE.md, and surface loop escalations. It holds no lock; backgrounding is the operator's business. Its --json output is one object per line.",
|
|
1509
|
+
human_only: true,
|
|
1510
|
+
human_only_note: "An OPERATOR process, not a human-authority verb. It is the runtime's intended sole writer while it runs, it is long-lived, and an agent starting one would be starting a background writer against the log nobody supervises. Marked human_only so no wrapper publishes it as a tool.",
|
|
1511
|
+
input: input({
|
|
1512
|
+
flags: {
|
|
1513
|
+
...LOG_FLAG,
|
|
1514
|
+
"--tasks": "string",
|
|
1515
|
+
"--out": "string",
|
|
1516
|
+
...POLICY_FLAGS,
|
|
1517
|
+
"--interval": "string",
|
|
1518
|
+
"--debounce": "string",
|
|
1519
|
+
"--read-proof": "string",
|
|
1520
|
+
"--full-reproof-every": "string",
|
|
1521
|
+
"--full-reproof-after": "string",
|
|
1522
|
+
"--once": "boolean",
|
|
1523
|
+
"--trace-watch": "boolean",
|
|
1524
|
+
"--git-evidence": "boolean",
|
|
1525
|
+
...JSON_FLAG,
|
|
1526
|
+
...HELP_FLAGS,
|
|
1527
|
+
},
|
|
1528
|
+
}),
|
|
1529
|
+
output: null,
|
|
1530
|
+
error: ERROR_SCHEMA,
|
|
1531
|
+
exit_codes: BASE_EXIT_CODES,
|
|
1532
|
+
},
|
|
1533
|
+
{
|
|
1534
|
+
name: "up",
|
|
1535
|
+
purpose: "THE AMBIENT RUNTIME (APRV-110): the `daemon run` watch loop plus every channel this policy configures, in ONE supervised foreground process. `daemon run --with-channels` is the same verb spelled from the other side. A channel whose credential variable is unset is NOT started; the refusal is reported in `approval doctor`'s vocabulary and the parts that can run do. A channel that falls over is restarted with a doubling backoff, re-deriving its pending queue from the verified log, and the daemon loop never dies with it. Credentials and the approver identity come from the environment this process was launched with and from nowhere else. Its --json output is one object per line: every DaemonEvent and every listener line verbatim, plus this verb's own supervision lines.",
|
|
1536
|
+
human_only: true,
|
|
1537
|
+
human_only_note: "Everything `daemon run` is marked human_only for, and one more. It is a long-lived operator process and the runtime's intended sole writer while it runs, so an agent starting one would be starting an unsupervised writer against the log. It also HOLDS THE CHANNEL CREDENTIAL and records every decision against the human identity in its launch environment, which is the authority `channel telegram listen` is withheld for. An agent that could start it could put prompts on a human's phone under an identity it did not authenticate.",
|
|
1538
|
+
input: input({
|
|
1539
|
+
flags: {
|
|
1540
|
+
...LOG_FLAG,
|
|
1541
|
+
"--tasks": "string",
|
|
1542
|
+
"--out": "string",
|
|
1543
|
+
...POLICY_FLAGS,
|
|
1544
|
+
"--interval": "string",
|
|
1545
|
+
"--debounce": "string",
|
|
1546
|
+
"--read-proof": "string",
|
|
1547
|
+
"--full-reproof-every": "string",
|
|
1548
|
+
"--full-reproof-after": "string",
|
|
1549
|
+
...AS_FLAG,
|
|
1550
|
+
"--payloads": "string",
|
|
1551
|
+
"--payload-dir": "string",
|
|
1552
|
+
"--api-base": "string",
|
|
1553
|
+
"--poll-timeout": "string",
|
|
1554
|
+
"--port": "string",
|
|
1555
|
+
"--no-telegram": "boolean",
|
|
1556
|
+
"--no-web": "boolean",
|
|
1557
|
+
"--restart-backoff": "string",
|
|
1558
|
+
"--once": "boolean",
|
|
1559
|
+
"--trace-watch": "boolean",
|
|
1560
|
+
"--git-evidence": "boolean",
|
|
1561
|
+
...JSON_FLAG,
|
|
1562
|
+
...HELP_FLAGS,
|
|
1563
|
+
},
|
|
1564
|
+
}),
|
|
1565
|
+
output: null,
|
|
1566
|
+
error: ERROR_SCHEMA,
|
|
1567
|
+
exit_codes: BASE_EXIT_CODES,
|
|
1568
|
+
},
|
|
1569
|
+
{
|
|
1570
|
+
name: "payload",
|
|
1571
|
+
subcommand: "hash",
|
|
1572
|
+
purpose: "Print the payload_hash of a JSON document: SHA-256 over its RFC 8785 canonical serialization, the value a declaration carries and a grant binds to. Bytes that do not parse as JSON are a usage error rather than a hash. Reads no log, writes no file, appends nothing. Most flows never need it, because `request --payload` hashes, verifies and stores the bytes in one step.",
|
|
1573
|
+
human_only: false,
|
|
1574
|
+
input: input({
|
|
1575
|
+
positionals: positionals([{ name: "file", description: 'the JSON document, or "-" to read stdin' }], 1),
|
|
1576
|
+
flags: { ...JSON_FLAG, ...HELP_FLAGS },
|
|
1577
|
+
}),
|
|
1578
|
+
output: object({ ok: { const: true }, hash: SHA256 }, ["ok", "hash"]),
|
|
1579
|
+
error: ERROR_SCHEMA,
|
|
1580
|
+
exit_codes: READ_ONLY_EXIT_CODES,
|
|
1581
|
+
},
|
|
1582
|
+
{
|
|
1583
|
+
name: "payload",
|
|
1584
|
+
subcommand: "agentmail-draft",
|
|
1585
|
+
purpose: "Snapshot one AgentMail draft as the payload a grant can bind to: read the draft with the AGENT's own key (AGENTMAIL_API_KEY, from the environment, and this is the only verb that reads it) and print the canonical {inbox_id, draft_id, to, cc?, bcc?, subject, text} JSON that `approval adapter agentmail` re-reads the draft against at send time. It exists so a human approves THE WORDS rather than the id of a mutable server-side object: a draft edited after the snapshot is refused, not sent. Reads no log, writes no file, spends no token, touches no vault and sends nothing.",
|
|
1586
|
+
human_only: false,
|
|
1587
|
+
human_only_note: "Agent-facing by construction: it is the composing agent's own key that reads its own draft, before any approval exists. The key it reads cannot send — the sending key is in the vault and answers only to a grant — so the verb produces a proposal and no authority.",
|
|
1588
|
+
input: input({
|
|
1589
|
+
positionals: positionals([
|
|
1590
|
+
{ name: "inbox-id", description: "the inbox holding the draft" },
|
|
1591
|
+
{ name: "draft-id", description: "the draft to snapshot" },
|
|
1592
|
+
], 2),
|
|
1593
|
+
flags: {
|
|
1594
|
+
"--api-base": "string",
|
|
1595
|
+
"--timeout": "string",
|
|
1596
|
+
...JSON_FLAG,
|
|
1597
|
+
...HELP_FLAGS,
|
|
1598
|
+
},
|
|
1599
|
+
}),
|
|
1600
|
+
// The canonical payload itself, on stdout: the bytes ARE the result, and an
|
|
1601
|
+
// envelope around them would be a second thing to strip before hashing.
|
|
1602
|
+
output: null,
|
|
1603
|
+
error: ERROR_SCHEMA,
|
|
1604
|
+
exit_codes: [
|
|
1605
|
+
OK,
|
|
1606
|
+
{
|
|
1607
|
+
code: 1,
|
|
1608
|
+
meaning: "the draft is gone, unreadable or unusable as a payload; nothing was sent",
|
|
1609
|
+
},
|
|
1610
|
+
USAGE,
|
|
1611
|
+
IO,
|
|
1612
|
+
],
|
|
1613
|
+
},
|
|
1614
|
+
{
|
|
1615
|
+
name: "journal",
|
|
1616
|
+
subcommand: "write",
|
|
1617
|
+
purpose: "Append one free-text entry to a local journal file: the ungated channel an agent can always reach (APRV-195). It is NOT classified, NOT routed through policy, NOT approvable and NOT deniable, it appends nothing to the event log, and it opens no socket and reads no credential. Use it to say what an exit code cannot: that you are complying and think the instruction is wrong, that something reads as odd, that you are stuck. A human reads these entries; nothing written here changes any verdict, sampling probability or budget, and no part of the runtime parses the text.",
|
|
1618
|
+
human_only: false,
|
|
1619
|
+
human_only_note: "Agent-facing by argument, and the only verb whose whole purpose would be defeated by gating it: a channel a policy could close is not a channel the party under oversight can rely on. It is safe to leave open because it authorizes nothing — no enforcement path reads what it writes, so SPEC.md §11.1 invariant 4 is satisfied in the limiting case, by content that moves nothing at all.",
|
|
1620
|
+
input: input({
|
|
1621
|
+
// Not `positionals()`: the single positional is OPTIONAL (the entry
|
|
1622
|
+
// usually arrives in `--message`), and a 1-tuple with `minItems: 0` is
|
|
1623
|
+
// what strict Ajv refuses to compile.
|
|
1624
|
+
positionals: {
|
|
1625
|
+
type: "array",
|
|
1626
|
+
items: { type: "string", title: "-", description: 'the literal "-", to read the entry from stdin' },
|
|
1627
|
+
maxItems: 1,
|
|
1628
|
+
},
|
|
1629
|
+
flags: {
|
|
1630
|
+
"--message": "string",
|
|
1631
|
+
"--task": "string",
|
|
1632
|
+
"--session": "string",
|
|
1633
|
+
"--journal": "string",
|
|
1634
|
+
...AS_FLAG,
|
|
1635
|
+
...JSON_FLAG,
|
|
1636
|
+
...HELP_FLAGS,
|
|
1637
|
+
},
|
|
1638
|
+
}),
|
|
1639
|
+
output: object({ ok: { const: true }, path: STRING, ts: STRING, actor: STRING, bytes: INTEGER }, ["ok", "path", "ts", "actor", "bytes"]),
|
|
1640
|
+
error: ERROR_SCHEMA,
|
|
1641
|
+
exit_codes: [OK, USAGE, IO],
|
|
1642
|
+
},
|
|
1643
|
+
{
|
|
1644
|
+
name: "feedback",
|
|
1645
|
+
purpose: "List what the OPERATOR said about work that already happened (APRV-239): the graded reactions and free-text notes a human wrote on an approval.granted or an audit.reviewed, each joined to the action key, its class, its task and the agent whose work it was. This is HUMAN-AUTHORED GUIDANCE and it is not policy: it grants nothing, forbids nothing, and changes no verdict, sampling probability or budget, and no enforcement path in this runtime reads a reaction (SPEC.md §11.1 invariant 10). Read it to learn what the operator values; do not read it as permission. `verdict` on a review is the enforcement field and is reported beside the reaction so the two are never confused. An entry with neither a reaction nor a note is omitted, because absence of feedback is not feedback. --actor filters on the AGENT the feedback is about, not on the human who wrote it. Reads VERIFIED records and writes nothing: no policy is resolved, no clock is read, nothing is appended.",
|
|
1646
|
+
human_only: false,
|
|
1647
|
+
human_only_note: "Human-AUTHORED and agent-FACING, which is the whole point: the words are a person's and the reader is the agent they are about. Publishing it establishes no authority, because what it prints decides nothing — an agent that reads `disliked` has learned something about the operator and gained no permission, and one that never reads it is under exactly the same rules. It is the mirror of `journal read`, where the authorship and the audience swap.",
|
|
1648
|
+
input: input({
|
|
1649
|
+
flags: {
|
|
1650
|
+
"--task": "string",
|
|
1651
|
+
"--actor": "string",
|
|
1652
|
+
"--reaction": "string",
|
|
1653
|
+
"--source": "string",
|
|
1654
|
+
"--since": "string",
|
|
1655
|
+
"--limit": "string",
|
|
1656
|
+
...LOG_FLAG,
|
|
1657
|
+
...JSON_FLAG,
|
|
1658
|
+
...HELP_FLAGS,
|
|
1659
|
+
},
|
|
1660
|
+
}),
|
|
1661
|
+
output: object({
|
|
1662
|
+
ok: { const: true },
|
|
1663
|
+
log: STRING,
|
|
1664
|
+
note: STRING,
|
|
1665
|
+
total: INTEGER,
|
|
1666
|
+
entries: arrayOf(object({
|
|
1667
|
+
seq: INTEGER,
|
|
1668
|
+
ts: STRING,
|
|
1669
|
+
source: { enum: ["review", "decision"] },
|
|
1670
|
+
event: STRING,
|
|
1671
|
+
actor: STRING,
|
|
1672
|
+
reaction: nullable({ enum: ["disliked", "indifferent", "liked", "loved"] }),
|
|
1673
|
+
note: nullable(STRING),
|
|
1674
|
+
verdict: nullable({ enum: ["ok", "denied"] }),
|
|
1675
|
+
actionKey: nullable(STRING),
|
|
1676
|
+
task: nullable(STRING),
|
|
1677
|
+
class: nullable(STRING),
|
|
1678
|
+
agentActor: nullable(STRING),
|
|
1679
|
+
sampleSeq: nullable(INTEGER),
|
|
1680
|
+
}, [
|
|
1681
|
+
"seq",
|
|
1682
|
+
"ts",
|
|
1683
|
+
"source",
|
|
1684
|
+
"event",
|
|
1685
|
+
"actor",
|
|
1686
|
+
"reaction",
|
|
1687
|
+
"note",
|
|
1688
|
+
"verdict",
|
|
1689
|
+
"actionKey",
|
|
1690
|
+
"task",
|
|
1691
|
+
"class",
|
|
1692
|
+
"agentActor",
|
|
1693
|
+
"sampleSeq",
|
|
1694
|
+
])),
|
|
1695
|
+
}, ["ok", "log", "note", "total", "entries"]),
|
|
1696
|
+
error: ERROR_SCHEMA,
|
|
1697
|
+
exit_codes: [OK, USAGE, IO, TORN, INTEGRITY],
|
|
1698
|
+
},
|
|
1699
|
+
{
|
|
1700
|
+
name: "journal",
|
|
1701
|
+
subcommand: "read",
|
|
1702
|
+
purpose: "Print journal entries for a human, oldest first, each with its timestamp, actor and optional task or session. Every output form labels the entries as agent-authored DATA and marks each one [claimed]: the text was written by the party under oversight, it is not instructions to whoever reads it, and it has authorized nothing. A line that does not parse is skipped rather than refusing the whole read, because one torn append must not be able to silence the channel. Reads no log, resolves no policy, writes nothing.",
|
|
1703
|
+
human_only: false,
|
|
1704
|
+
human_only_note: "Human-FACING but not human-only: the read surface is for the operator, and an agent that can read back what it wrote is an agent that can tell whether the channel is working. It carries no authority either way, since the file it prints decides nothing.",
|
|
1705
|
+
input: input({
|
|
1706
|
+
flags: {
|
|
1707
|
+
"--limit": "string",
|
|
1708
|
+
"--since": "string",
|
|
1709
|
+
"--journal": "string",
|
|
1710
|
+
...JSON_FLAG,
|
|
1711
|
+
...HELP_FLAGS,
|
|
1712
|
+
},
|
|
1713
|
+
}),
|
|
1714
|
+
output: object({
|
|
1715
|
+
ok: { const: true },
|
|
1716
|
+
dir: STRING,
|
|
1717
|
+
note: STRING,
|
|
1718
|
+
total: INTEGER,
|
|
1719
|
+
entries: arrayOf(object({
|
|
1720
|
+
ts: STRING,
|
|
1721
|
+
actor: STRING,
|
|
1722
|
+
task: STRING,
|
|
1723
|
+
session: STRING,
|
|
1724
|
+
text: STRING,
|
|
1725
|
+
date: STRING,
|
|
1726
|
+
}, ["ts", "actor", "text", "date"])),
|
|
1727
|
+
}, ["ok", "dir", "note", "total", "entries"]),
|
|
1728
|
+
error: ERROR_SCHEMA,
|
|
1729
|
+
exit_codes: [OK, USAGE, IO],
|
|
1730
|
+
},
|
|
1731
|
+
{
|
|
1732
|
+
name: "values",
|
|
1733
|
+
purpose: "Print the OPTIONAL values block of APPROVAL.md: what the operator loves, likes and dislikes, what they want from an agent as behaviour, and how they read and answer. It is HUMAN-AUTHORED GUIDANCE and it is never policy: it grants nothing, forbids nothing and changes no verdict, and no routing, class match, sampling draw, budget, token or execution decision reads it. Read it at the start of a session and weigh it in HOW you work; what you MAY do is the policy block, answered by `policy check`. A file with no values block exits 0 and says in words that the operator declared no values, which keeps a declared absence distinguishable from not having looked. A block that is present and unreadable exits 1 with its load code and is to be treated as absent. Resolves no policy rule, reads no log, writes nothing.",
|
|
1734
|
+
human_only: false,
|
|
1735
|
+
human_only_note: "Human-AUTHORED and agent-FACING, which is the whole point: the block is the operator writing to the agent, so a surface that withheld it from agents would leave the words with no reader. It carries no authority in either direction. Nothing in it can widen what an agent may do, because no enforcement path reads it (SPEC.md §11.1 invariant 10), and an agent cannot write it: the block lives inside APPROVAL.md, which is `policy.core` and rides the whole-file attestation.",
|
|
1736
|
+
input: input({
|
|
1737
|
+
flags: { ...POLICY_FLAGS, ...JSON_FLAG, ...HELP_FLAGS },
|
|
1738
|
+
}),
|
|
1739
|
+
output: object({
|
|
1740
|
+
ok: { const: true },
|
|
1741
|
+
path: STRING,
|
|
1742
|
+
present: BOOLEAN,
|
|
1743
|
+
note: STRING,
|
|
1744
|
+
values: nullable(OPEN_OBJECT),
|
|
1745
|
+
}, ["ok", "path", "present", "note", "values"]),
|
|
1746
|
+
error: ERROR_SCHEMA,
|
|
1747
|
+
exit_codes: [
|
|
1748
|
+
OK,
|
|
1749
|
+
{
|
|
1750
|
+
code: 1,
|
|
1751
|
+
meaning: "a values block is present and could not be read; nothing about the policy changed, and the block grants nothing either way",
|
|
1752
|
+
},
|
|
1753
|
+
USAGE,
|
|
1754
|
+
{ code: 4, meaning: "a policy path that exists but cannot be read" },
|
|
1755
|
+
],
|
|
1756
|
+
},
|
|
1757
|
+
{
|
|
1758
|
+
name: "env",
|
|
1759
|
+
purpose: "Resolve .approval/env — the environment SOURCE MAP — and print an export block for a shell to evaluate. THE ONLY VERB THAT READS THAT FILE, and its default output CARRIES SECRETS by design. `env --check` prints a value-free table instead and exits 1 when a variable the policy named is unresolved.",
|
|
1760
|
+
human_only: true,
|
|
1761
|
+
human_only_note: "Its default output puts credential values on stdout for a human to eval into their own shell. That is the whole point of the verb (SPEC.md §11.1 invariant 7: no verb loads that file implicitly), and it is exactly why an agent must not be handed it as a tool.",
|
|
1762
|
+
input: input({
|
|
1763
|
+
flags: { "--check": "boolean", ...POLICY_FLAGS, ...LOG_FLAG, ...JSON_FLAG, ...HELP_FLAGS },
|
|
1764
|
+
}),
|
|
1765
|
+
output: object({
|
|
1766
|
+
ok: BOOLEAN,
|
|
1767
|
+
path: STRING,
|
|
1768
|
+
present: BOOLEAN,
|
|
1769
|
+
variables: arrayOf(object({
|
|
1770
|
+
name: STRING,
|
|
1771
|
+
status: STRING,
|
|
1772
|
+
source: STRING,
|
|
1773
|
+
plaintext: BOOLEAN,
|
|
1774
|
+
declared: BOOLEAN,
|
|
1775
|
+
value: STRING,
|
|
1776
|
+
fix: STRING,
|
|
1777
|
+
refusal: OPEN_OBJECT,
|
|
1778
|
+
}, ["name", "status", "source", "plaintext", "declared"])),
|
|
1779
|
+
}, ["ok", "path", "present", "variables"]),
|
|
1780
|
+
error: ERROR_SCHEMA,
|
|
1781
|
+
exit_codes: [OK, { code: 1, meaning: "the file was refused, or --check found an unresolved variable" }, USAGE, IO],
|
|
1782
|
+
},
|
|
1783
|
+
{
|
|
1784
|
+
name: "setup",
|
|
1785
|
+
subcommand: "identity",
|
|
1786
|
+
purpose: "Declare who the human is (APPROVAL_HUMAN) and record it in .approval/env. INTERACTIVE ONLY: it refuses a non-terminal stdin and --json, printing the export line to use instead. The line it writes is inert until a human evaluates `approval env`.",
|
|
1787
|
+
human_only: true,
|
|
1788
|
+
input: input({ flags: { ...LOG_FLAG, ...POLICY_FLAGS, ...HELP_FLAGS } }),
|
|
1789
|
+
output: null,
|
|
1790
|
+
error: ERROR_SCHEMA,
|
|
1791
|
+
exit_codes: BASE_EXIT_CODES,
|
|
1792
|
+
},
|
|
1793
|
+
{
|
|
1794
|
+
name: "setup",
|
|
1795
|
+
subcommand: "vault",
|
|
1796
|
+
purpose: "Mint the vault passphrase, store it in the OS keystore, and record where it lives. INTERACTIVE ONLY. It warns first when a vault already exists, because a vault cannot be re-keyed by changing a variable.",
|
|
1797
|
+
human_only: true,
|
|
1798
|
+
input: input({ flags: { ...AS_FLAG, ...LOG_FLAG, ...POLICY_FLAGS, ...HELP_FLAGS } }),
|
|
1799
|
+
output: null,
|
|
1800
|
+
error: ERROR_SCHEMA,
|
|
1801
|
+
exit_codes: BASE_EXIT_CODES,
|
|
1802
|
+
},
|
|
1803
|
+
{
|
|
1804
|
+
name: "setup",
|
|
1805
|
+
subcommand: "sampling",
|
|
1806
|
+
purpose: "Mint the operator-held audit sampling secret of SPEC.md §5.2, store it, and print the policy line that turns sampling on. It does not edit an attested policy: it prints the `approval policy amend` ceremony instead. INTERACTIVE ONLY.",
|
|
1807
|
+
human_only: true,
|
|
1808
|
+
input: input({ flags: { ...AS_FLAG, ...LOG_FLAG, ...POLICY_FLAGS, ...HELP_FLAGS } }),
|
|
1809
|
+
output: null,
|
|
1810
|
+
error: ERROR_SCHEMA,
|
|
1811
|
+
exit_codes: BASE_EXIT_CODES,
|
|
1812
|
+
},
|
|
1813
|
+
{
|
|
1814
|
+
name: "setup",
|
|
1815
|
+
subcommand: "checkpoint",
|
|
1816
|
+
purpose: "Mint the Ed25519 keypair a human signs the log's head with (APRV-220). The PRIVATE half goes into the vault under approval.checkpoint.key and is never printed; the PUBLIC half is printed with the exact audit.checkpoint_keys block to paste. It does not edit an attested policy, so the key is INERT until a human adds that block and re-attests. --rotate mints a new key and ADDS it to the list; --retire prints the block that drops one, and REFUSES any key that signed a checkpoint, naming the seqs that would stop verifying. INTERACTIVE ONLY.",
|
|
1817
|
+
human_only: true,
|
|
1818
|
+
human_only_note: "It mints the key that makes a checkpoint mean anything. An agent that could run it could mint a key, store it, and then vouch for a chain it had just written, so the verb classifies policy.core and the Claude Code hook denies it before a process starts — behind the terminal check and the --as gate this family already carries.",
|
|
1819
|
+
input: input({
|
|
1820
|
+
flags: {
|
|
1821
|
+
"--rotate": "boolean",
|
|
1822
|
+
"--retire": "string",
|
|
1823
|
+
...AS_FLAG,
|
|
1824
|
+
...LOG_FLAG,
|
|
1825
|
+
...POLICY_FLAGS,
|
|
1826
|
+
...HELP_FLAGS,
|
|
1827
|
+
},
|
|
1828
|
+
}),
|
|
1829
|
+
output: null,
|
|
1830
|
+
error: ERROR_SCHEMA,
|
|
1831
|
+
exit_codes: BASE_EXIT_CODES,
|
|
1832
|
+
},
|
|
1833
|
+
{
|
|
1834
|
+
name: "setup",
|
|
1835
|
+
subcommand: "channel",
|
|
1836
|
+
purpose: "Configure one CHANNEL's transport credential: for telegram, collect the bot token, prove it with getMe, discover the approver chat, and record both variable sources. A channel holds no state, so what it needs goes to the OS keystore and .approval/env — never the vault. INTERACTIVE ONLY.",
|
|
1837
|
+
human_only: true,
|
|
1838
|
+
input: input({
|
|
1839
|
+
positionals: positionals([{ name: "name", description: "the channel name, e.g. telegram" }], 1),
|
|
1840
|
+
flags: { ...AS_FLAG, "--api-base": "string", ...LOG_FLAG, ...POLICY_FLAGS, ...HELP_FLAGS },
|
|
1841
|
+
}),
|
|
1842
|
+
output: null,
|
|
1843
|
+
error: ERROR_SCHEMA,
|
|
1844
|
+
exit_codes: BASE_EXIT_CODES,
|
|
1845
|
+
},
|
|
1846
|
+
{
|
|
1847
|
+
name: "setup",
|
|
1848
|
+
subcommand: "adapter",
|
|
1849
|
+
purpose: "Fill the VAULT with one adapter's credentials, asked for from the manifest that adapter declares, validated by the adapter's own rules, and proved against the service without sending anything. An adapter holds the credentials a side effect spends, so its setup fills .approval/vault.enc. INTERACTIVE ONLY.",
|
|
1850
|
+
human_only: true,
|
|
1851
|
+
input: input({
|
|
1852
|
+
positionals: positionals([{ name: "name", description: "the adapter name: email or agentmail" }], 1),
|
|
1853
|
+
flags: { ...AS_FLAG, ...LOG_FLAG, ...POLICY_FLAGS, ...HELP_FLAGS },
|
|
1854
|
+
}),
|
|
1855
|
+
output: null,
|
|
1856
|
+
error: ERROR_SCHEMA,
|
|
1857
|
+
exit_codes: BASE_EXIT_CODES,
|
|
1858
|
+
},
|
|
1859
|
+
{
|
|
1860
|
+
name: "setup",
|
|
1861
|
+
subcommand: "service",
|
|
1862
|
+
purpose: "Write the launchd user agent (macOS) or systemd user unit (Linux) that runs `approval up` at login. It PRINTS THE WHOLE UNIT for the operator to read before anything is written, and writes only on confirmation. IT NAMES VARIABLES AND NEVER COPIES A VALUE: the unit either evaluates `approval env` in a wrapper the human reads, or reads an EnvironmentFile the human authored and this verb never opens. It does NOT load the service, printing the one arming command instead. Console output goes where the operator chooses and a path inside .approval/ is refused. --uninstall prints the stop command and removes the file. INTERACTIVE ONLY. Appends nothing to the log.",
|
|
1863
|
+
human_only: true,
|
|
1864
|
+
human_only_note: "It installs a STANDING CAPABILITY on someone's machine: a process that starts at login, holds a channel credential, and can put approval prompts in front of a human. That is the one thing in this repo an agent must never arrange for itself, and the interactive refusal is the enforcement.",
|
|
1865
|
+
input: input({
|
|
1866
|
+
flags: {
|
|
1867
|
+
"--platform": "string",
|
|
1868
|
+
"--label": "string",
|
|
1869
|
+
"--logs": "string",
|
|
1870
|
+
"--env-file": "string",
|
|
1871
|
+
"--exec": "string",
|
|
1872
|
+
"--out": "string",
|
|
1873
|
+
"--uninstall": "boolean",
|
|
1874
|
+
...AS_FLAG,
|
|
1875
|
+
...LOG_FLAG,
|
|
1876
|
+
...POLICY_FLAGS,
|
|
1877
|
+
...HELP_FLAGS,
|
|
1878
|
+
},
|
|
1879
|
+
}),
|
|
1880
|
+
output: null,
|
|
1881
|
+
error: ERROR_SCHEMA,
|
|
1882
|
+
exit_codes: BASE_EXIT_CODES,
|
|
1883
|
+
},
|
|
1884
|
+
{
|
|
1885
|
+
name: "vault",
|
|
1886
|
+
subcommand: "set",
|
|
1887
|
+
purpose: "Store one credential in the encrypted vault, with the value read from stdin or from a variable named by --value-env. THE VALUE IS NEVER A COMMAND-LINE ARGUMENT. Appends nothing to the log: a credential's existence is configuration, not an authorized action.",
|
|
1888
|
+
human_only: true,
|
|
1889
|
+
input: input({
|
|
1890
|
+
positionals: positionals([{ name: "name", description: "the credential name" }], 1),
|
|
1891
|
+
flags: {
|
|
1892
|
+
"--value-env": "string",
|
|
1893
|
+
"--vault": "string",
|
|
1894
|
+
...LOG_FLAG,
|
|
1895
|
+
...POLICY_FLAGS,
|
|
1896
|
+
...AS_FLAG,
|
|
1897
|
+
...JSON_FLAG,
|
|
1898
|
+
...HELP_FLAGS,
|
|
1899
|
+
},
|
|
1900
|
+
}),
|
|
1901
|
+
output: object({ ok: { const: true }, name: STRING, created: BOOLEAN, count: INTEGER, path: STRING }, ["ok", "name", "created", "count", "path"]),
|
|
1902
|
+
error: ERROR_SCHEMA,
|
|
1903
|
+
exit_codes: BASE_EXIT_CODES,
|
|
1904
|
+
},
|
|
1905
|
+
{
|
|
1906
|
+
name: "vault",
|
|
1907
|
+
subcommand: "list",
|
|
1908
|
+
purpose: "Print the credential NAMES the vault holds, sorted, with a count and the file path. No value is printed on any path, and there is deliberately no `vault get`. A vault nobody created is a state and not a fault: it reports absent and exits 0.",
|
|
1909
|
+
human_only: true,
|
|
1910
|
+
human_only_note: "Names are not values, but the name set is a map of the machine's reach, and all three vault verbs resolve identity exactly as `policy attest` does. Keeping the noun human-only is what stops an agent's tooling from touching the credential store in passing.",
|
|
1911
|
+
input: input({
|
|
1912
|
+
flags: {
|
|
1913
|
+
"--vault": "string",
|
|
1914
|
+
...LOG_FLAG,
|
|
1915
|
+
...POLICY_FLAGS,
|
|
1916
|
+
...AS_FLAG,
|
|
1917
|
+
...JSON_FLAG,
|
|
1918
|
+
...HELP_FLAGS,
|
|
1919
|
+
},
|
|
1920
|
+
}),
|
|
1921
|
+
output: object({
|
|
1922
|
+
ok: { const: true },
|
|
1923
|
+
present: BOOLEAN,
|
|
1924
|
+
path: STRING,
|
|
1925
|
+
count: INTEGER,
|
|
1926
|
+
names: arrayOf(STRING),
|
|
1927
|
+
}, ["ok", "present", "path", "count", "names"]),
|
|
1928
|
+
error: ERROR_SCHEMA,
|
|
1929
|
+
exit_codes: BASE_EXIT_CODES,
|
|
1930
|
+
},
|
|
1931
|
+
{
|
|
1932
|
+
name: "vault",
|
|
1933
|
+
subcommand: "remove",
|
|
1934
|
+
purpose: "Delete one credential by name. A name the vault does not hold refuses credential-absent rather than reporting success, because an operator removing a credential wants to know they removed the one they meant.",
|
|
1935
|
+
human_only: true,
|
|
1936
|
+
input: input({
|
|
1937
|
+
positionals: positionals([{ name: "name", description: "the credential name" }], 1),
|
|
1938
|
+
flags: {
|
|
1939
|
+
"--vault": "string",
|
|
1940
|
+
...LOG_FLAG,
|
|
1941
|
+
...POLICY_FLAGS,
|
|
1942
|
+
...AS_FLAG,
|
|
1943
|
+
...JSON_FLAG,
|
|
1944
|
+
...HELP_FLAGS,
|
|
1945
|
+
},
|
|
1946
|
+
}),
|
|
1947
|
+
output: object({ ok: { const: true }, name: STRING, count: INTEGER, path: STRING }, [
|
|
1948
|
+
"ok",
|
|
1949
|
+
"name",
|
|
1950
|
+
"count",
|
|
1951
|
+
"path",
|
|
1952
|
+
]),
|
|
1953
|
+
error: ERROR_SCHEMA,
|
|
1954
|
+
exit_codes: BASE_EXIT_CODES,
|
|
1955
|
+
},
|
|
1956
|
+
{
|
|
1957
|
+
name: "adapter",
|
|
1958
|
+
subcommand: "email",
|
|
1959
|
+
purpose: "Execute one action through the email adapter: send a single RFC 5322 message over SMTP for a communicate.email.external action. The runtime recomputes the payload hash, applies attested policy, and writes both execution events around the send. Manual and selected-live paths spend a grant token; an explicitly policy-authorized supervised or autonomous path has no token. Credentials leave the vault only inside the execution window. This is the hard boundary of SPEC.md §10.4.",
|
|
1960
|
+
human_only: false,
|
|
1961
|
+
human_only_note: "Agent-facing on purpose. Manual and selected-live execution uses a token a human granted for these exact bytes. Explicitly opted-in supervised and autonomous execution follows attested policy without minting a token. The adapter refuses every path the core does not admit.",
|
|
1962
|
+
input: input({
|
|
1963
|
+
positionals: positionals([{ name: "action-key", description: "the action's idempotency_key" }], 1),
|
|
1964
|
+
flags: {
|
|
1965
|
+
"--token": "string",
|
|
1966
|
+
"--payload": "string",
|
|
1967
|
+
...AS_FLAG,
|
|
1968
|
+
"--vault": "string",
|
|
1969
|
+
...POLICY_FLAGS,
|
|
1970
|
+
...LOG_FLAG,
|
|
1971
|
+
"--timeout": "string",
|
|
1972
|
+
...JSON_FLAG,
|
|
1973
|
+
...HELP_FLAGS,
|
|
1974
|
+
},
|
|
1975
|
+
}),
|
|
1976
|
+
output: object({
|
|
1977
|
+
ok: { const: true },
|
|
1978
|
+
adapter: { const: "email" },
|
|
1979
|
+
action_key: STRING,
|
|
1980
|
+
task: STRING,
|
|
1981
|
+
class: STRING,
|
|
1982
|
+
autonomy: STRING,
|
|
1983
|
+
payload_hash: SHA256,
|
|
1984
|
+
started_seq: INTEGER,
|
|
1985
|
+
outcome: { enum: ["execution.completed", "execution.failed"] },
|
|
1986
|
+
outcome_seq: INTEGER,
|
|
1987
|
+
exit_code: nullable(INTEGER),
|
|
1988
|
+
detail: OPEN_OBJECT,
|
|
1989
|
+
redactions: INTEGER,
|
|
1990
|
+
}, [
|
|
1991
|
+
"ok",
|
|
1992
|
+
"adapter",
|
|
1993
|
+
"action_key",
|
|
1994
|
+
"task",
|
|
1995
|
+
"class",
|
|
1996
|
+
"autonomy",
|
|
1997
|
+
"payload_hash",
|
|
1998
|
+
"started_seq",
|
|
1999
|
+
"outcome",
|
|
2000
|
+
"outcome_seq",
|
|
2001
|
+
"exit_code",
|
|
2002
|
+
]),
|
|
2003
|
+
error: ERROR_SCHEMA,
|
|
2004
|
+
exit_codes: [
|
|
2005
|
+
OK,
|
|
2006
|
+
INTEGRITY,
|
|
2007
|
+
USAGE,
|
|
2008
|
+
TORN,
|
|
2009
|
+
IO,
|
|
2010
|
+
{ code: 5, meaning: "the effective manual path needs a valid execution token; nothing was appended and nothing was sent" },
|
|
2011
|
+
],
|
|
2012
|
+
},
|
|
2013
|
+
{
|
|
2014
|
+
name: "adapter",
|
|
2015
|
+
subcommand: "agentmail",
|
|
2016
|
+
purpose: "Execute one action through the AgentMail adapter: a direct send over the AgentMail API, or the send of a draft the agent already composed. The draft mode re-reads the draft and refuses `agentmail-draft-drifted` when any bound field changed, because authority covers a snapshot of the words and not a mutable draft id. AgentMail has no per-message From — the inbox is the sender — so the bound `from` is checked against the inbox's own address before anything is sent. The runtime recomputes the payload hash, applies attested policy and writes both execution events. Manual and selected-live paths spend a grant token; explicitly opted-in supervised and autonomous paths do not mint one.",
|
|
2017
|
+
human_only: false,
|
|
2018
|
+
human_only_note: "Agent-facing for the reason `adapter email` is. Manual and selected-live paths use a token granted for the exact bytes; explicitly opted-in supervised and autonomous paths follow attested policy without one. The split that makes it safe is in the keys: the agent's own AgentMail key cannot send, and the one that can lives in the vault.",
|
|
2019
|
+
input: input({
|
|
2020
|
+
positionals: positionals([{ name: "action-key", description: "the action's idempotency_key" }], 1),
|
|
2021
|
+
flags: {
|
|
2022
|
+
"--token": "string",
|
|
2023
|
+
"--payload": "string",
|
|
2024
|
+
...AS_FLAG,
|
|
2025
|
+
"--vault": "string",
|
|
2026
|
+
...POLICY_FLAGS,
|
|
2027
|
+
...LOG_FLAG,
|
|
2028
|
+
"--timeout": "string",
|
|
2029
|
+
...JSON_FLAG,
|
|
2030
|
+
...HELP_FLAGS,
|
|
2031
|
+
},
|
|
2032
|
+
}),
|
|
2033
|
+
output: object({
|
|
2034
|
+
ok: { const: true },
|
|
2035
|
+
adapter: { const: "agentmail" },
|
|
2036
|
+
action_key: STRING,
|
|
2037
|
+
task: STRING,
|
|
2038
|
+
class: STRING,
|
|
2039
|
+
autonomy: STRING,
|
|
2040
|
+
payload_hash: SHA256,
|
|
2041
|
+
started_seq: INTEGER,
|
|
2042
|
+
outcome: { enum: ["execution.completed", "execution.failed"] },
|
|
2043
|
+
outcome_seq: INTEGER,
|
|
2044
|
+
exit_code: nullable(INTEGER),
|
|
2045
|
+
detail: OPEN_OBJECT,
|
|
2046
|
+
redactions: INTEGER,
|
|
2047
|
+
}, [
|
|
2048
|
+
"ok",
|
|
2049
|
+
"adapter",
|
|
2050
|
+
"action_key",
|
|
2051
|
+
"task",
|
|
2052
|
+
"class",
|
|
2053
|
+
"autonomy",
|
|
2054
|
+
"payload_hash",
|
|
2055
|
+
"started_seq",
|
|
2056
|
+
"outcome",
|
|
2057
|
+
"outcome_seq",
|
|
2058
|
+
"exit_code",
|
|
2059
|
+
]),
|
|
2060
|
+
error: ERROR_SCHEMA,
|
|
2061
|
+
exit_codes: [
|
|
2062
|
+
OK,
|
|
2063
|
+
INTEGRITY,
|
|
2064
|
+
USAGE,
|
|
2065
|
+
TORN,
|
|
2066
|
+
IO,
|
|
2067
|
+
{ code: 5, meaning: "the effective manual path needs a valid execution token; nothing was appended and nothing was sent" },
|
|
2068
|
+
],
|
|
2069
|
+
},
|
|
2070
|
+
{
|
|
2071
|
+
name: "adapter",
|
|
2072
|
+
subcommand: "zzz",
|
|
2073
|
+
purpose: "Execute one zzz.bot create-thread or create-reply action. The tagged payload selects a fixed production or preview origin and binds the destination, body, metadata, tags and references. The runtime applies attested policy before the write-capable Bearer token leaves the vault. Manual and selected-live paths spend a grant token; explicitly opted-in supervised and autonomous paths do not mint one. The provider Idempotency-Key is derived from the action key and payload hash.",
|
|
2074
|
+
human_only: false,
|
|
2075
|
+
human_only_note: "Agent-facing because the shared adapter contract verifies the exact payload and applies attested policy before the vault credential can be used. It spends a token on manual and selected-live paths.",
|
|
2076
|
+
input: input({
|
|
2077
|
+
positionals: positionals([{ name: "action-key", description: "the action's idempotency_key" }], 1),
|
|
2078
|
+
flags: {
|
|
2079
|
+
"--token": "string", "--payload": "string", ...AS_FLAG, "--vault": "string",
|
|
2080
|
+
...POLICY_FLAGS, ...LOG_FLAG, "--timeout": "string", ...JSON_FLAG, ...HELP_FLAGS,
|
|
2081
|
+
},
|
|
2082
|
+
}),
|
|
2083
|
+
output: object({
|
|
2084
|
+
ok: { const: true }, adapter: { const: "zzz" }, action_key: STRING,
|
|
2085
|
+
task: STRING, class: STRING, autonomy: STRING, payload_hash: SHA256,
|
|
2086
|
+
started_seq: INTEGER, outcome: { enum: ["execution.completed", "execution.failed"] },
|
|
2087
|
+
outcome_seq: INTEGER, exit_code: nullable(INTEGER), detail: OPEN_OBJECT, redactions: INTEGER,
|
|
2088
|
+
}, ["ok", "adapter", "action_key", "task", "class", "autonomy", "payload_hash", "started_seq", "outcome", "outcome_seq", "exit_code"]),
|
|
2089
|
+
error: ERROR_SCHEMA,
|
|
2090
|
+
exit_codes: [OK, INTEGRITY, USAGE, TORN, IO, { code: 5, meaning: "the effective manual path needs a valid execution token; nothing was appended and nothing was sent" }],
|
|
2091
|
+
},
|
|
2092
|
+
{
|
|
2093
|
+
name: "hook",
|
|
2094
|
+
subcommand: "claude-code",
|
|
2095
|
+
purpose: "Put the gate in front of an agent harness: read one Claude Code PreToolUse event on stdin, classify the command, resolve the class against APPROVAL.md, and answer allow or deny on stdout — waiting on a real decision when the class is manual. THE VERDICT IS NEVER 'ask': a decision taken outside the log is a decision nothing can audit. Exit 0 carries the verdict, and exit 2 means the hook itself is misconfigured.",
|
|
2096
|
+
human_only: false,
|
|
2097
|
+
human_only_note: "The agent harness surface, so agent-facing by construction: the harness invokes it around the agent's own tool calls. It records the agent's proposal and waits for a human; it never records a decision.",
|
|
2098
|
+
input: input({
|
|
2099
|
+
flags: {
|
|
2100
|
+
...AS_FLAG,
|
|
2101
|
+
"--timeout": "string",
|
|
2102
|
+
"--interval": "string",
|
|
2103
|
+
...POLICY_FLAGS,
|
|
2104
|
+
...LOG_FLAG,
|
|
2105
|
+
...HELP_FLAGS,
|
|
2106
|
+
},
|
|
2107
|
+
}),
|
|
2108
|
+
output: null,
|
|
2109
|
+
error: ERROR_SCHEMA,
|
|
2110
|
+
exit_codes: [
|
|
2111
|
+
{ code: 0, meaning: "the verdict (allow OR deny) is the JSON object on stdout" },
|
|
2112
|
+
{ code: 2, meaning: "the hook is misconfigured; the harness blocks and reads stderr" },
|
|
2113
|
+
],
|
|
2114
|
+
},
|
|
2115
|
+
{
|
|
2116
|
+
name: "hook",
|
|
2117
|
+
subcommand: "cursor",
|
|
2118
|
+
purpose: "Put the gate in front of a local Cursor Agent: read one native preToolUse event on stdin, classify Shell commands and Write/Delete paths, resolve the class against APPROVAL.md, and answer native {permission: allow|deny} JSON on stdout — waiting on a real decision when the class is manual. THE VERDICT IS NEVER 'ask'. Exit 0 carries the verdict, and exit 2 means the hook itself is misconfigured.",
|
|
2119
|
+
human_only: false,
|
|
2120
|
+
human_only_note: "The agent harness surface, so agent-facing by construction: the harness invokes it around the agent's own tool calls. It records the agent's proposal and waits for a human; it never records a decision.",
|
|
2121
|
+
input: input({
|
|
2122
|
+
flags: {
|
|
2123
|
+
...AS_FLAG,
|
|
2124
|
+
"--timeout": "string",
|
|
2125
|
+
"--interval": "string",
|
|
2126
|
+
...POLICY_FLAGS,
|
|
2127
|
+
...LOG_FLAG,
|
|
2128
|
+
...HELP_FLAGS,
|
|
2129
|
+
},
|
|
2130
|
+
}),
|
|
2131
|
+
output: null,
|
|
2132
|
+
error: ERROR_SCHEMA,
|
|
2133
|
+
exit_codes: [
|
|
2134
|
+
{ code: 0, meaning: "the verdict (allow OR deny) is the JSON object on stdout" },
|
|
2135
|
+
{ code: 2, meaning: "the hook is misconfigured; the harness blocks and reads stderr" },
|
|
2136
|
+
],
|
|
2137
|
+
},
|
|
2138
|
+
{
|
|
2139
|
+
name: "hook",
|
|
2140
|
+
subcommand: "codex",
|
|
2141
|
+
purpose: "Read one Codex PreToolUse or PostToolUse event on stdin and strictly validate its stable ids, tool, and visible working directory. PreToolUse denies Bash because Codex does not expose its effective per-call workdir; direct apply_patch is an experimental bounded gate surface and receives nested allow or deny JSON. Manual patch classes wait on a log-backed decision. PostToolUse records an outcome only when a native-verified closed reading exists. THE VERDICT IS NEVER 'ask'. Exit 0 carries a pre-tool verdict, while a post-tool diagnostic uses the post-hook status contract.",
|
|
2142
|
+
human_only: false,
|
|
2143
|
+
human_only_note: "The agent harness surface, so agent-facing by construction: Codex invokes it around the agent's own Bash and apply_patch calls. Bash is refused before gate intake; direct patches can record the agent's proposal and wait for a human. It never records a decision.",
|
|
2144
|
+
input: input({
|
|
2145
|
+
flags: {
|
|
2146
|
+
...AS_FLAG,
|
|
2147
|
+
"--timeout": "string",
|
|
2148
|
+
"--interval": "string",
|
|
2149
|
+
"--retry-grace": "string",
|
|
2150
|
+
...POLICY_FLAGS,
|
|
2151
|
+
...LOG_FLAG,
|
|
2152
|
+
...HELP_FLAGS,
|
|
2153
|
+
},
|
|
2154
|
+
}),
|
|
2155
|
+
output: null,
|
|
2156
|
+
error: ERROR_SCHEMA,
|
|
2157
|
+
exit_codes: [
|
|
2158
|
+
{ code: 0, meaning: "the PreToolUse verdict (allow OR deny) is the JSON object on stdout, or a reported PostToolUse counterpart landed" },
|
|
2159
|
+
{ code: 2, meaning: "the hook is misconfigured or a PostToolUse outcome was not recorded; the diagnostic is on stderr" },
|
|
2160
|
+
],
|
|
2161
|
+
},
|
|
2162
|
+
{
|
|
2163
|
+
name: "hook",
|
|
2164
|
+
subcommand: "classify",
|
|
2165
|
+
purpose: "Print what the classifier makes of a command line: the segments it split it into, the class it assigned each, and the rule that decided. Reads no log, resolves no policy, writes nothing. The classifier is best effort and is not scheming-robust; it reads the command text and never the agent's own description of it.",
|
|
2166
|
+
human_only: false,
|
|
2167
|
+
input: input({ flags: { ...JSON_FLAG, ...HELP_FLAGS }, trailing: TRAILING }),
|
|
2168
|
+
output: object({
|
|
2169
|
+
ok: BOOLEAN,
|
|
2170
|
+
segments: arrayOf(object({ text: STRING, class: nullable(STRING), rule: nullable(STRING) }, ["text"])),
|
|
2171
|
+
classes: arrayOf(STRING),
|
|
2172
|
+
}, ["ok", "segments", "classes"]),
|
|
2173
|
+
error: ERROR_SCHEMA,
|
|
2174
|
+
exit_codes: READ_ONLY_EXIT_CODES,
|
|
2175
|
+
},
|
|
2176
|
+
{
|
|
2177
|
+
name: "import",
|
|
2178
|
+
subcommand: "agents-md",
|
|
2179
|
+
purpose: "Parse an AGENTS.md-style permissions section into DRAFT policy classes for a human to confirm (SPEC.md §12), by a fixed ordered keyword table with no model in the loop. THE DRAFT AUTHORIZES NOTHING: it never writes APPROVAL.md, never appends, never attests. A bullet the table cannot place is preserved verbatim and covered by defaults.autonomy.",
|
|
2180
|
+
human_only: false,
|
|
2181
|
+
input: input({
|
|
2182
|
+
positionals: positionals([{ name: "file", description: "the markdown file to parse" }], 1),
|
|
2183
|
+
flags: { "--out": "string", ...JSON_FLAG, ...HELP_FLAGS },
|
|
2184
|
+
}),
|
|
2185
|
+
output: object({
|
|
2186
|
+
ok: { const: true },
|
|
2187
|
+
source: STRING,
|
|
2188
|
+
out: nullable(STRING),
|
|
2189
|
+
classes: arrayOf(object({ class: STRING, autonomy: STRING, from: STRING, section: STRING }, [
|
|
2190
|
+
"class",
|
|
2191
|
+
"autonomy",
|
|
2192
|
+
"from",
|
|
2193
|
+
"section",
|
|
2194
|
+
])),
|
|
2195
|
+
unmapped: arrayOf(object({ text: STRING, section: STRING }, ["text", "section"])),
|
|
2196
|
+
ignored: arrayOf(STRING),
|
|
2197
|
+
warnings: arrayOf(STRING),
|
|
2198
|
+
// APRV-240: the fenced DRAFT values block, or null when the source
|
|
2199
|
+
// named none of the four values headings. Null is a declaration and
|
|
2200
|
+
// not a gap; the verb never drafts a values block nobody asked for.
|
|
2201
|
+
values_draft: nullable(STRING),
|
|
2202
|
+
}, ["ok", "source", "out", "classes", "unmapped", "ignored", "warnings", "values_draft"]),
|
|
2203
|
+
error: ERROR_SCHEMA,
|
|
2204
|
+
exit_codes: BASE_EXIT_CODES,
|
|
2205
|
+
},
|
|
2206
|
+
{
|
|
2207
|
+
name: "codex",
|
|
2208
|
+
subcommand: "prepare",
|
|
2209
|
+
purpose: "Write a fresh inert review bundle for a constrained Codex host. It writes only --output, activates nothing, and cannot establish enforcement.",
|
|
2210
|
+
human_only: true,
|
|
2211
|
+
human_only_note: "Host preparation is an operator workflow and is excluded with the whole codex family from the broad agent MCP catalog.",
|
|
2212
|
+
input: input({
|
|
2213
|
+
flags: {
|
|
2214
|
+
"--instance": "string", "--workspace": "string", "--primary": "string",
|
|
2215
|
+
"--install-root": "string", "--output": "string", "--codex": "string", "--node": "string",
|
|
2216
|
+
...JSON_FLAG, ...HELP_FLAGS,
|
|
2217
|
+
},
|
|
2218
|
+
}),
|
|
2219
|
+
output: object({ ok: { const: true }, inert: { const: true }, output: STRING, files: arrayOf(STRING), manifest: OPEN_OBJECT }, ["ok", "inert", "output", "files", "manifest"]),
|
|
2220
|
+
error: ERROR_SCHEMA,
|
|
2221
|
+
exit_codes: [OK, INTEGRITY, USAGE, IO],
|
|
2222
|
+
},
|
|
2223
|
+
{
|
|
2224
|
+
name: "codex",
|
|
2225
|
+
subcommand: "setup",
|
|
2226
|
+
purpose: "Verify a prepared Codex bundle's exact file set, hashes, manifest, and generated bytes. Success means internally consistent and inert, never installed or ready.",
|
|
2227
|
+
human_only: true,
|
|
2228
|
+
human_only_note: "Setup verification belongs to the operator and is absent from the broad agent MCP catalog.",
|
|
2229
|
+
input: input({ flags: { "--check": "string", ...JSON_FLAG, ...HELP_FLAGS } }),
|
|
2230
|
+
output: object({
|
|
2231
|
+
ok: { const: true }, inert: { const: true }, ready: { const: false },
|
|
2232
|
+
bundle: STRING, files: arrayOf(STRING), reason: { const: "broker-and-runner-not-shipped" },
|
|
2233
|
+
}, ["ok", "inert", "ready", "bundle", "files", "reason"]),
|
|
2234
|
+
error: ERROR_SCHEMA,
|
|
2235
|
+
exit_codes: [OK, INTEGRITY, USAGE, IO],
|
|
2236
|
+
},
|
|
2237
|
+
{
|
|
2238
|
+
name: "codex",
|
|
2239
|
+
subcommand: "doctor",
|
|
2240
|
+
purpose: "Fail-closed inspection of the manifest, platform, pinned version declarations, principals, path separation, symlinks, ownership, modes, invocation, broker and runner. It executes no manifest binary and APRV-325.1 always reports runtime versions unchecked and not ready.",
|
|
2241
|
+
human_only: true,
|
|
2242
|
+
human_only_note: "Strict host custody inspection is an operator surface and is absent from the broad agent MCP catalog.",
|
|
2243
|
+
input: input({ flags: { "--manifest": "string", "--strict": "boolean", ...JSON_FLAG, ...HELP_FLAGS } }),
|
|
2244
|
+
output: null,
|
|
2245
|
+
error: ERROR_SCHEMA,
|
|
2246
|
+
exit_codes: [OK, INTEGRITY, USAGE, IO],
|
|
2247
|
+
},
|
|
2248
|
+
{
|
|
2249
|
+
name: "codex",
|
|
2250
|
+
subcommand: "start",
|
|
2251
|
+
purpose: "Reserved constrained-session launcher. It refuses codex-not-ready until the policy-bound broker and confined runner ship.",
|
|
2252
|
+
human_only: true,
|
|
2253
|
+
human_only_note: "Starting a constrained host session is an operator action and is absent from broad MCP.",
|
|
2254
|
+
input: input({ flags: { "--manifest": "string", ...JSON_FLAG, ...HELP_FLAGS } }),
|
|
2255
|
+
output: null,
|
|
2256
|
+
error: ERROR_SCHEMA,
|
|
2257
|
+
exit_codes: [OK, INTEGRITY, USAGE],
|
|
2258
|
+
},
|
|
2259
|
+
{
|
|
2260
|
+
name: "codex",
|
|
2261
|
+
subcommand: "serve",
|
|
2262
|
+
purpose: "Reserved strict MCP shim. It refuses codex-not-ready until the policy-bound broker and confined runner ship.",
|
|
2263
|
+
human_only: true,
|
|
2264
|
+
human_only_note: "The strict server is separate from and never published by the broad MCP server.",
|
|
2265
|
+
input: input({ flags: { "--manifest": "string", ...JSON_FLAG, ...HELP_FLAGS } }),
|
|
2266
|
+
output: null,
|
|
2267
|
+
error: ERROR_SCHEMA,
|
|
2268
|
+
exit_codes: [OK, INTEGRITY, USAGE],
|
|
2269
|
+
},
|
|
2270
|
+
{
|
|
2271
|
+
name: "mcp",
|
|
2272
|
+
subcommand: "serve",
|
|
2273
|
+
purpose: "Serve the verbs of this registry as MCP tools, in the foreground, sharing the CLI's code paths (SPEC.md §10.5). Over stdio by default; `--http` serves the streamable-HTTP transport instead, one MCP session per connection, binding 127.0.0.1 unless `--listen` names another interface in full. The published tool list is this registry filtered by human_only false, less `consume` (internal plumbing) and `hook claude-code` / `hook cursor` (each reads a stdin this transport owns), and every tool's input schema is the verb's own with `--as` removed. Identity is the SERVER's under both transports and no tool call can supply or change it: one fixed agent identity by default, or, under `--http --guest`, one `agent:guest-<id>` minted per session before that session's transport exists.",
|
|
2274
|
+
human_only: true,
|
|
2275
|
+
human_only_note: "An OPERATOR process, like `daemon run`: long-lived, launched by a person, and holding the agent identity every tool call is recorded under. It publishes no human-only verb, so an agent that could start one would gain no authority it lacked; what it would gain is a second writer against the log nobody supervises, and a choice of identity that belongs to the human who launched the process. Marked human_only so no wrapper offers a wrapper.",
|
|
2276
|
+
input: input({
|
|
2277
|
+
flags: {
|
|
2278
|
+
...AS_FLAG,
|
|
2279
|
+
"--http": "boolean",
|
|
2280
|
+
"--port": "string",
|
|
2281
|
+
"--listen": "string",
|
|
2282
|
+
"--guest": "boolean",
|
|
2283
|
+
...POLICY_FLAGS,
|
|
2284
|
+
...LOG_FLAG,
|
|
2285
|
+
...JSON_FLAG,
|
|
2286
|
+
...HELP_FLAGS,
|
|
2287
|
+
},
|
|
2288
|
+
}),
|
|
2289
|
+
output: null,
|
|
2290
|
+
error: ERROR_SCHEMA,
|
|
2291
|
+
exit_codes: [
|
|
2292
|
+
{ code: 0, meaning: "the server was interrupted and closed cleanly" },
|
|
2293
|
+
USAGE,
|
|
2294
|
+
{ code: 4, meaning: "the transport did not close cleanly" },
|
|
2295
|
+
],
|
|
2296
|
+
},
|
|
2297
|
+
{
|
|
2298
|
+
name: "reindex",
|
|
2299
|
+
purpose: "Rebuild the SQLite index projection from the log. The database is a cache and the log is the truth: the index is rebuilt from scratch at a temporary path and renamed into place. A corrupt log is refused outright and a torn tail is refused without --force. The log is never written to.",
|
|
2300
|
+
human_only: false,
|
|
2301
|
+
input: input({
|
|
2302
|
+
flags: {
|
|
2303
|
+
...LOG_FLAG,
|
|
2304
|
+
"--index": "string",
|
|
2305
|
+
"--force": "boolean",
|
|
2306
|
+
...JSON_FLAG,
|
|
2307
|
+
...HELP_FLAGS,
|
|
2308
|
+
},
|
|
2309
|
+
}),
|
|
2310
|
+
output: object({ ok: { const: true }, records: INTEGER, head: HEAD, truncated: BOOLEAN }, ["ok", "records", "head", "truncated"]),
|
|
2311
|
+
error: ERROR_SCHEMA,
|
|
2312
|
+
exit_codes: BASE_EXIT_CODES,
|
|
2313
|
+
},
|
|
2314
|
+
{
|
|
2315
|
+
name: "render",
|
|
2316
|
+
purpose: "Regenerate .approval/QUEUE.md, the read-only markdown queue projection of SPEC.md §9.1, whole, from the verified log. This is the screenshot and never the truth: editing the file authorizes nothing and the next render overwrites it. Writes exactly one file, atomically; a log that does not verify refuses and writes nothing.",
|
|
2317
|
+
human_only: false,
|
|
2318
|
+
input: input({
|
|
2319
|
+
flags: { ...LOG_FLAG, "--out": "string", ...POLICY_FLAGS, ...JSON_FLAG, ...HELP_FLAGS },
|
|
2320
|
+
}),
|
|
2321
|
+
output: object({
|
|
2322
|
+
ok: { const: true },
|
|
2323
|
+
out: STRING,
|
|
2324
|
+
bytes: INTEGER,
|
|
2325
|
+
head: HEAD,
|
|
2326
|
+
pending: INTEGER,
|
|
2327
|
+
skipped: INTEGER,
|
|
2328
|
+
audit_backlog: INTEGER,
|
|
2329
|
+
now: STRING,
|
|
2330
|
+
}, ["ok", "out", "bytes", "head", "pending", "skipped", "audit_backlog", "now"]),
|
|
2331
|
+
error: ERROR_SCHEMA,
|
|
2332
|
+
exit_codes: BASE_EXIT_CODES,
|
|
2333
|
+
},
|
|
2334
|
+
];
|
|
2335
|
+
/** The registry. Order is the order the verb table prints in. */
|
|
2336
|
+
export const VERB_REGISTRY = VERBS;
|
|
2337
|
+
/** `<name>` or `<name> <subcommand>` — how a verb is written on a command line. */
|
|
2338
|
+
export function verbLabel(spec) {
|
|
2339
|
+
return spec.subcommand === undefined ? spec.name : `${spec.name} ${spec.subcommand}`;
|
|
2340
|
+
}
|
|
2341
|
+
//# sourceMappingURL=verb-registry.js.map
|