approval-md 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +85 -877
- package/SPEC.md +150 -19
- package/cli.js +66 -7
- package/dist/src/adapters/smtp.d.ts +18 -0
- package/dist/src/adapters/smtp.js +24 -3
- package/dist/src/adapters/smtp.js.map +1 -1
- package/dist/src/channels/contract.d.ts +53 -1
- package/dist/src/channels/contract.js +304 -7
- package/dist/src/channels/contract.js.map +1 -1
- package/dist/src/channels/relay-server.d.ts +90 -0
- package/dist/src/channels/relay-server.js +295 -0
- package/dist/src/channels/relay-server.js.map +1 -0
- package/dist/src/channels/relay.d.ts +273 -0
- package/dist/src/channels/relay.js +564 -0
- package/dist/src/channels/relay.js.map +1 -0
- package/dist/src/channels/tagging.js +6 -1
- package/dist/src/channels/tagging.js.map +1 -1
- package/dist/src/channels/telegram-webhook.d.ts +273 -0
- package/dist/src/channels/telegram-webhook.js +536 -0
- package/dist/src/channels/telegram-webhook.js.map +1 -0
- package/dist/src/channels/telegram.d.ts +394 -12
- package/dist/src/channels/telegram.js +621 -68
- package/dist/src/channels/telegram.js.map +1 -1
- package/dist/src/channels/web.d.ts +9 -0
- package/dist/src/channels/web.js +17 -0
- package/dist/src/channels/web.js.map +1 -1
- package/dist/src/cli/amend.js +564 -34
- package/dist/src/cli/amend.js.map +1 -1
- package/dist/src/cli/attest.d.ts +9 -0
- package/dist/src/cli/attest.js +177 -13
- package/dist/src/cli/attest.js.map +1 -1
- package/dist/src/cli/channel-relay.d.ts +108 -0
- package/dist/src/cli/channel-relay.js +334 -0
- package/dist/src/cli/channel-relay.js.map +1 -0
- package/dist/src/cli/channel-telegram-webhook.d.ts +273 -0
- package/dist/src/cli/channel-telegram-webhook.js +709 -0
- package/dist/src/cli/channel-telegram-webhook.js.map +1 -0
- package/dist/src/cli/channel-telegram.d.ts +347 -28
- package/dist/src/cli/channel-telegram.js +763 -28
- package/dist/src/cli/channel-telegram.js.map +1 -1
- package/dist/src/cli/channel.d.ts +10 -1
- package/dist/src/cli/channel.js +14 -1
- package/dist/src/cli/channel.js.map +1 -1
- package/dist/src/cli/codex-bridge.d.ts +819 -0
- package/dist/src/cli/codex-bridge.js +1607 -0
- package/dist/src/cli/codex-bridge.js.map +1 -0
- package/dist/src/cli/codex.d.ts +1 -1
- package/dist/src/cli/codex.js +304 -7
- package/dist/src/cli/codex.js.map +1 -1
- package/dist/src/cli/daemon.js +41 -6
- package/dist/src/cli/daemon.js.map +1 -1
- package/dist/src/cli/doctor.d.ts +63 -0
- package/dist/src/cli/doctor.js +1322 -122
- package/dist/src/cli/doctor.js.map +1 -1
- package/dist/src/cli/execute.d.ts +17 -1
- package/dist/src/cli/execute.js +366 -22
- package/dist/src/cli/execute.js.map +1 -1
- package/dist/src/cli/exit-codes.d.ts +12 -0
- package/dist/src/cli/exit-codes.js +13 -0
- package/dist/src/cli/exit-codes.js.map +1 -1
- package/dist/src/cli/gate.d.ts +2 -0
- package/dist/src/cli/gate.js +233 -2
- package/dist/src/cli/gate.js.map +1 -1
- package/dist/src/cli/help.d.ts +14 -2
- package/dist/src/cli/help.js +395 -82
- package/dist/src/cli/help.js.map +1 -1
- package/dist/src/cli/hook-codex.d.ts +49 -1
- package/dist/src/cli/hook-codex.js +60 -1
- package/dist/src/cli/hook-codex.js.map +1 -1
- package/dist/src/cli/hook.d.ts +828 -4
- package/dist/src/cli/hook.js +2972 -142
- package/dist/src/cli/hook.js.map +1 -1
- package/dist/src/cli/import.js +1 -1
- package/dist/src/cli/import.js.map +1 -1
- package/dist/src/cli/init.js +16 -7
- package/dist/src/cli/init.js.map +1 -1
- package/dist/src/cli/instructions.js +24 -3
- package/dist/src/cli/instructions.js.map +1 -1
- package/dist/src/cli/log-anchor.d.ts +55 -0
- package/dist/src/cli/log-anchor.js +199 -60
- package/dist/src/cli/log-anchor.js.map +1 -1
- package/dist/src/cli/main.d.ts +0 -12
- package/dist/src/cli/main.js +56 -3
- package/dist/src/cli/main.js.map +1 -1
- package/dist/src/cli/muse.d.ts +2 -0
- package/dist/src/cli/muse.js +62 -0
- package/dist/src/cli/muse.js.map +1 -0
- package/dist/src/cli/payload.js +79 -1
- package/dist/src/cli/payload.js.map +1 -1
- package/dist/src/cli/policy-apply.d.ts +195 -0
- package/dist/src/cli/policy-apply.js +573 -0
- package/dist/src/cli/policy-apply.js.map +1 -0
- package/dist/src/cli/policy.js +14 -1
- package/dist/src/cli/policy.js.map +1 -1
- package/dist/src/cli/preflight.d.ts +151 -13
- package/dist/src/cli/preflight.js +398 -41
- package/dist/src/cli/preflight.js.map +1 -1
- package/dist/src/cli/sandbox.js +17 -1
- package/dist/src/cli/sandbox.js.map +1 -1
- package/dist/src/cli/scaffold.d.ts +1 -1
- package/dist/src/cli/scaffold.js +7 -1
- package/dist/src/cli/scaffold.js.map +1 -1
- package/dist/src/cli/serve.d.ts +53 -0
- package/dist/src/cli/serve.js +270 -0
- package/dist/src/cli/serve.js.map +1 -0
- package/dist/src/cli/setup-channel.d.ts +9 -0
- package/dist/src/cli/setup-channel.js +28 -1
- package/dist/src/cli/setup-channel.js.map +1 -1
- package/dist/src/cli/setup-common.d.ts +3 -1
- package/dist/src/cli/setup-common.js +3 -2
- package/dist/src/cli/setup-common.js.map +1 -1
- package/dist/src/cli/setup.d.ts +2 -0
- package/dist/src/cli/setup.js +94 -2
- package/dist/src/cli/setup.js.map +1 -1
- package/dist/src/cli/up.js +134 -51
- package/dist/src/cli/up.js.map +1 -1
- package/dist/src/cli/values.js +3 -4
- package/dist/src/cli/values.js.map +1 -1
- package/dist/src/cli/verb-registry.d.ts +2 -0
- package/dist/src/cli/verb-registry.js +510 -47
- package/dist/src/cli/verb-registry.js.map +1 -1
- package/dist/src/cli/wordmark.d.ts +2 -2
- package/dist/src/cli/wordmark.js +2 -2
- package/dist/src/codex/broker.d.ts +229 -0
- package/dist/src/codex/broker.js +548 -0
- package/dist/src/codex/broker.js.map +1 -0
- package/dist/src/codex/runner.d.ts +178 -0
- package/dist/src/codex/runner.js +231 -0
- package/dist/src/codex/runner.js.map +1 -0
- package/dist/src/codex/serve.d.ts +56 -0
- package/dist/src/codex/serve.js +98 -0
- package/dist/src/codex/serve.js.map +1 -0
- package/dist/src/codex/workspace-commit.d.ts +219 -0
- package/dist/src/codex/workspace-commit.js +549 -0
- package/dist/src/codex/workspace-commit.js.map +1 -0
- package/dist/src/core/advance-cycle.d.ts +51 -0
- package/dist/src/core/advance-cycle.js +66 -2
- package/dist/src/core/advance-cycle.js.map +1 -1
- package/dist/src/core/agents-md.d.ts +20 -18
- package/dist/src/core/agents-md.js +33 -31
- package/dist/src/core/agents-md.js.map +1 -1
- package/dist/src/core/attest-bootstrap.d.ts +95 -0
- package/dist/src/core/attest-bootstrap.js +135 -0
- package/dist/src/core/attest-bootstrap.js.map +1 -0
- package/dist/src/core/attest.d.ts +234 -2
- package/dist/src/core/attest.js +342 -7
- package/dist/src/core/attest.js.map +1 -1
- package/dist/src/core/audit.d.ts +73 -3
- package/dist/src/core/audit.js +65 -5
- package/dist/src/core/audit.js.map +1 -1
- package/dist/src/core/channel-lease.d.ts +322 -0
- package/dist/src/core/channel-lease.js +663 -0
- package/dist/src/core/channel-lease.js.map +1 -0
- package/dist/src/core/channel-owner.d.ts +213 -0
- package/dist/src/core/channel-owner.js +358 -0
- package/dist/src/core/channel-owner.js.map +1 -0
- package/dist/src/core/command-class.d.ts +205 -0
- package/dist/src/core/command-class.js +1433 -28
- package/dist/src/core/command-class.js.map +1 -1
- package/dist/src/core/commit-guard.d.ts +272 -0
- package/dist/src/core/commit-guard.js +424 -0
- package/dist/src/core/commit-guard.js.map +1 -0
- package/dist/src/core/daemon-actor.d.ts +45 -0
- package/dist/src/core/daemon-actor.js +54 -0
- package/dist/src/core/daemon-actor.js.map +1 -0
- package/dist/src/core/daemon-host.d.ts +178 -0
- package/dist/src/core/daemon-host.js +228 -0
- package/dist/src/core/daemon-host.js.map +1 -0
- package/dist/src/core/daemon-identity.d.ts +238 -0
- package/dist/src/core/daemon-identity.js +252 -0
- package/dist/src/core/daemon-identity.js.map +1 -0
- package/dist/src/core/dark-session.d.ts +109 -8
- package/dist/src/core/dark-session.js +266 -82
- package/dist/src/core/dark-session.js.map +1 -1
- package/dist/src/core/decision-refusal.d.ts +23 -2
- package/dist/src/core/decision-refusal.js +24 -2
- package/dist/src/core/decision-refusal.js.map +1 -1
- package/dist/src/core/env-file.d.ts +5 -0
- package/dist/src/core/env-file.js +60 -1
- package/dist/src/core/env-file.js.map +1 -1
- package/dist/src/core/execute.d.ts +15 -2
- package/dist/src/core/execute.js +15 -2
- package/dist/src/core/execute.js.map +1 -1
- package/dist/src/core/gate.d.ts +392 -6
- package/dist/src/core/gate.js +852 -24
- package/dist/src/core/gate.js.map +1 -1
- package/dist/src/core/gesture-refusal.d.ts +166 -0
- package/dist/src/core/gesture-refusal.js +188 -0
- package/dist/src/core/gesture-refusal.js.map +1 -0
- package/dist/src/core/harness-version.d.ts +128 -4
- package/dist/src/core/harness-version.js +181 -12
- package/dist/src/core/harness-version.js.map +1 -1
- package/dist/src/core/harness-wait.d.ts +82 -3
- package/dist/src/core/harness-wait.js +89 -3
- package/dist/src/core/harness-wait.js.map +1 -1
- package/dist/src/core/instance.d.ts +59 -2
- package/dist/src/core/instance.js +113 -0
- package/dist/src/core/instance.js.map +1 -1
- package/dist/src/core/log-subscribe.d.ts +21 -0
- package/dist/src/core/log-subscribe.js +16 -6
- package/dist/src/core/log-subscribe.js.map +1 -1
- package/dist/src/core/log-write-layer.d.ts +55 -0
- package/dist/src/core/log-write-layer.js +91 -0
- package/dist/src/core/log-write-layer.js.map +1 -0
- package/dist/src/core/log.d.ts +89 -22
- package/dist/src/core/log.js +197 -19
- package/dist/src/core/log.js.map +1 -1
- package/dist/src/core/payload-census.d.ts +51 -0
- package/dist/src/core/payload-census.js +125 -2
- package/dist/src/core/payload-census.js.map +1 -1
- package/dist/src/core/payload-store.d.ts +31 -0
- package/dist/src/core/payload-store.js +113 -8
- package/dist/src/core/payload-store.js.map +1 -1
- package/dist/src/core/payload.d.ts +18 -3
- package/dist/src/core/payload.js +20 -4
- package/dist/src/core/payload.js.map +1 -1
- package/dist/src/core/policy-diff.d.ts +10 -0
- package/dist/src/core/policy-diff.js +10 -2
- package/dist/src/core/policy-diff.js.map +1 -1
- package/dist/src/core/policy-explain.d.ts +17 -1
- package/dist/src/core/policy-explain.js +48 -1
- package/dist/src/core/policy-explain.js.map +1 -1
- package/dist/src/core/policy-load.d.ts +65 -1
- package/dist/src/core/policy-load.js +21 -3
- package/dist/src/core/policy-load.js.map +1 -1
- package/dist/src/core/policy-match.d.ts +81 -0
- package/dist/src/core/policy-match.js +87 -0
- package/dist/src/core/policy-match.js.map +1 -1
- package/dist/src/core/policy-proposal.d.ts +75 -0
- package/dist/src/core/policy-proposal.js +110 -4
- package/dist/src/core/policy-proposal.js.map +1 -1
- package/dist/src/core/protected-path-guard.d.ts +117 -4
- package/dist/src/core/protected-path-guard.js +362 -48
- package/dist/src/core/protected-path-guard.js.map +1 -1
- package/dist/src/core/question-preempted.d.ts +141 -0
- package/dist/src/core/question-preempted.js +152 -0
- package/dist/src/core/question-preempted.js.map +1 -0
- package/dist/src/core/read-scope.d.ts +182 -0
- package/dist/src/core/read-scope.js +256 -0
- package/dist/src/core/read-scope.js.map +1 -0
- package/dist/src/core/run-payload.d.ts +160 -0
- package/dist/src/core/run-payload.js +304 -0
- package/dist/src/core/run-payload.js.map +1 -0
- package/dist/src/core/sandbox.d.ts +81 -0
- package/dist/src/core/sandbox.js +190 -1
- package/dist/src/core/sandbox.js.map +1 -1
- package/dist/src/core/sender-identity.d.ts +501 -0
- package/dist/src/core/sender-identity.js +609 -0
- package/dist/src/core/sender-identity.js.map +1 -0
- package/dist/src/core/shlex.d.ts +102 -0
- package/dist/src/core/shlex.js +159 -0
- package/dist/src/core/shlex.js.map +1 -0
- package/dist/src/core/state.d.ts +52 -1
- package/dist/src/core/state.js +52 -6
- package/dist/src/core/state.js.map +1 -1
- package/dist/src/core/telegram-config.d.ts +79 -0
- package/dist/src/core/telegram-config.js +108 -0
- package/dist/src/core/telegram-config.js.map +1 -1
- package/dist/src/core/values.d.ts +18 -8
- package/dist/src/core/values.js +36 -1
- package/dist/src/core/values.js.map +1 -1
- package/dist/src/core/verify.d.ts +23 -1
- package/dist/src/core/verify.js +20 -2
- package/dist/src/core/verify.js.map +1 -1
- package/dist/src/daemon/advance.d.ts +10 -0
- package/dist/src/daemon/advance.js +25 -4
- package/dist/src/daemon/advance.js.map +1 -1
- package/dist/src/daemon/audit.d.ts +80 -5
- package/dist/src/daemon/audit.js +126 -6
- package/dist/src/daemon/audit.js.map +1 -1
- package/dist/src/daemon/daemon.d.ts +174 -2
- package/dist/src/daemon/daemon.js +307 -7
- package/dist/src/daemon/daemon.js.map +1 -1
- package/dist/src/daemon/git-evidence.d.ts +2 -2
- package/dist/src/daemon/git-evidence.js +1 -1
- package/dist/src/daemon/projection.d.ts +13 -2
- package/dist/src/daemon/projection.js +39 -6
- package/dist/src/daemon/projection.js.map +1 -1
- package/dist/src/mcp/server.d.ts +39 -3
- package/dist/src/mcp/server.js +30 -3
- package/dist/src/mcp/server.js.map +1 -1
- package/dist/src/muse/server.d.ts +16 -0
- package/dist/src/muse/server.js +238 -0
- package/dist/src/muse/server.js.map +1 -0
- package/dist/src/serve/archive.d.ts +158 -0
- package/dist/src/serve/archive.js +354 -0
- package/dist/src/serve/archive.js.map +1 -0
- package/dist/src/serve/arguments.d.ts +93 -0
- package/dist/src/serve/arguments.js +228 -0
- package/dist/src/serve/arguments.js.map +1 -0
- package/dist/src/serve/credentials.d.ts +91 -0
- package/dist/src/serve/credentials.js +143 -0
- package/dist/src/serve/credentials.js.map +1 -0
- package/dist/src/serve/follow.d.ts +86 -0
- package/dist/src/serve/follow.js +101 -0
- package/dist/src/serve/follow.js.map +1 -0
- package/dist/src/serve/hook-thread.d.ts +134 -0
- package/dist/src/serve/hook-thread.js +333 -0
- package/dist/src/serve/hook-thread.js.map +1 -0
- package/dist/src/serve/hook-worker.d.ts +134 -0
- package/dist/src/serve/hook-worker.js +173 -0
- package/dist/src/serve/hook-worker.js.map +1 -0
- package/dist/src/serve/server.d.ts +412 -0
- package/dist/src/serve/server.js +1251 -0
- package/dist/src/serve/server.js.map +1 -0
- package/docs/README-extended.md +766 -0
- package/docs/cli-reference.md +2563 -73
- package/docs/codex-enforced-session.md +75 -2
- package/docs/codex-workspace-broker.md +118 -0
- package/package.json +6 -2
- package/schema/event.schema.json +595 -9
- package/schema/fixtures/event/invalid/approval-granted-sender-hashed-false.json +20 -0
- package/schema/fixtures/event/invalid/approval-granted-sender-hashed-raw-id.json +20 -0
- package/schema/fixtures/event/invalid/audit-gesture-refused-human-actor.json +16 -0
- package/schema/fixtures/event/invalid/audit-gesture-refused-no-actor-no-sender.json +15 -0
- package/schema/fixtures/event/invalid/audit-gesture-refused-unknown-gesture.json +16 -0
- package/schema/fixtures/event/invalid/audit-question-preempted-agent-actor.json +16 -0
- package/schema/fixtures/event/invalid/audit-question-preempted-no-question-id.json +16 -0
- package/schema/fixtures/event/invalid/audit-question-preempted-unknown-source.json +15 -0
- package/schema/fixtures/event/invalid/daemon-id-malformed.json +15 -0
- package/schema/fixtures/event/invalid/execution-started-truncated-policy-sha256.json +18 -0
- package/schema/fixtures/event/invalid/gate-path-signed-off-absolute-path.json +14 -0
- package/schema/fixtures/event/invalid/gate-path-signed-off-agent-actor.json +14 -0
- package/schema/fixtures/event/invalid/gate-path-signed-off-missing-path.json +13 -0
- package/schema/fixtures/event/invalid/harness-cap-at-margin.json +16 -0
- package/schema/fixtures/event/invalid/harness-cap-fractional.json +16 -0
- package/schema/fixtures/event/invalid/harness-cap-negative.json +16 -0
- package/schema/fixtures/event/invalid/harness-cap-string.json +16 -0
- package/schema/fixtures/event/invalid/harness-cap-without-harness-execution.json +16 -0
- package/schema/fixtures/event/invalid/harness-cap-zero.json +16 -0
- package/schema/fixtures/event/valid/approval-granted-sender-hashed.json +20 -0
- package/schema/fixtures/event/valid/approval-requested-harness-cap.json +18 -0
- package/schema/fixtures/event/valid/audit-gesture-refused-review-note.json +21 -0
- package/schema/fixtures/event/valid/audit-gesture-refused-sender-key-unavailable.json +19 -0
- package/schema/fixtures/event/valid/audit-gesture-refused.json +19 -0
- package/schema/fixtures/event/valid/audit-question-preempted-no-verdict.json +16 -0
- package/schema/fixtures/event/valid/audit-question-preempted.json +20 -0
- package/schema/fixtures/event/valid/daemon-identity-declared.json +17 -0
- package/schema/fixtures/event/valid/daemon-identity.json +15 -0
- package/schema/fixtures/event/valid/execution-started-policy-sha256.json +18 -0
- package/schema/fixtures/event/valid/gate-path-signed-off.json +14 -0
- package/schema/fixtures/event/valid/harness-kind-claude-code.json +23 -0
- package/schema/fixtures/event/valid/harness-kind-codex.json +23 -0
- package/schema/fixtures/event/valid/harness-kind-cursor.json +23 -0
- package/schema/fixtures/event/valid/harness-kind-grok.json +23 -0
- package/schema/fixtures/event/valid/harness-kind-hermes.json +23 -0
- package/schema/fixtures/event/valid/harness-kind-muse.json +23 -0
- package/schema/fixtures/policy/invalid/daemons-malformed-id.json +10 -0
- package/schema/fixtures/policy/invalid/senders-half-keyed.json +20 -0
- package/schema/fixtures/policy/valid/canonical.json +1 -1
- package/schema/fixtures/policy/valid/daemons-allowlist.json +19 -0
- package/schema/fixtures/policy/valid/senders-keyed.json +24 -0
- package/schema/fixtures/policy-md/invalid/schema-invalid-agent-may-request.md +21 -0
- package/schema/fixtures/policy-md/valid/agent-may-request.md +24 -0
- package/schema/fixtures/policy-md/valid/canonical.md +1 -1
- package/schema/fixtures/policy-md/valid/with-values.md +5 -7
- package/schema/fixtures/values/invalid/class-shaped.json +1 -1
- package/schema/fixtures/values/invalid/duplicate-entry.json +1 -1
- package/schema/fixtures/values/invalid/non-string-item.json +1 -1
- package/schema/fixtures/values/invalid/over-cap.json +1 -1
- package/schema/fixtures/values/invalid/unknown-key.json +1 -1
- package/schema/fixtures/values/invalid/version-float.json +1 -0
- package/schema/fixtures/values/invalid/version-integer.json +1 -0
- package/schema/fixtures/values/invalid/version-wrong-string.json +1 -0
- package/schema/fixtures/values/valid/empty-lists.json +2 -3
- package/schema/fixtures/values/valid/full.json +5 -7
- package/schema/fixtures/values/valid/minimal.json +1 -1
- package/schema/fixtures/values-md/invalid/schema-invalid.md +5 -3
- package/schema/fixtures/values-md/invalid/two-blocks.md +3 -3
- package/schema/fixtures/values-md/invalid/unterminated.md +2 -2
- package/schema/fixtures/values-md/invalid/version-1.md +69 -0
- package/schema/fixtures/values-md/invalid/version-unquoted.md +64 -0
- package/schema/fixtures/values-md/invalid/yaml-error.md +2 -2
- package/schema/fixtures/values-md/valid/absent.md +1 -1
- package/schema/fixtures/values-md/valid/with-values.md +5 -7
- package/schema/policy.schema.json +100 -4
- package/schema/values.schema.json +7 -11
- package/schema/fixtures/values/invalid/version-string.json +0 -1
|
@@ -73,6 +73,29 @@ function object(properties, required) {
|
|
|
73
73
|
/** An object whose keys are not enumerated here (a log record, a policy rule). */
|
|
74
74
|
const OPEN_OBJECT = { type: "object" };
|
|
75
75
|
const arrayOf = (items) => ({ type: "array", items });
|
|
76
|
+
/**
|
|
77
|
+
* One refusal family in `approval status` (APRV-376): how many records the log
|
|
78
|
+
* carries, and the newest few of them, newest first.
|
|
79
|
+
*
|
|
80
|
+
* `code` is the surface's own refusal code, copied verbatim from the record, and
|
|
81
|
+
* it is spelled here as a plain string rather than an enum on purpose. The two
|
|
82
|
+
* closed vocabularies live at the write boundary (`schema/event.schema.json`) and
|
|
83
|
+
* in `core/decision-refusal.ts` and `core/gesture-refusal.ts`; a third copy in a
|
|
84
|
+
* REPORT schema would refuse to describe a log it was older than, which is the
|
|
85
|
+
* wrong failure direction for a field that authorizes nothing.
|
|
86
|
+
*
|
|
87
|
+
* `sender` is the account the gesture arrived from, when the record carries one,
|
|
88
|
+
* in the form the record carries it: `hashed` marks APRV-370's keyed digest, and
|
|
89
|
+
* it is present only when the record says so.
|
|
90
|
+
*/
|
|
91
|
+
const REFUSAL_FAMILY = object({
|
|
92
|
+
count: INTEGER,
|
|
93
|
+
recent: arrayOf(object({
|
|
94
|
+
seq: INTEGER,
|
|
95
|
+
code: STRING,
|
|
96
|
+
sender: object({ channel: STRING, id: STRING, hashed: BOOLEAN }, ["channel", "id"]),
|
|
97
|
+
}, ["seq", "code"])),
|
|
98
|
+
}, ["count", "recent"]);
|
|
76
99
|
/**
|
|
77
100
|
* Positional arguments, named in order. `items: false` closes the tail: an
|
|
78
101
|
* unexpected argument is a usage error in the CLI and is one here too.
|
|
@@ -91,18 +114,44 @@ function positionals(names, requiredCount) {
|
|
|
91
114
|
};
|
|
92
115
|
}
|
|
93
116
|
const NO_POSITIONALS = { type: "array", maxItems: 0 };
|
|
94
|
-
/** The argv after `--`, for the
|
|
117
|
+
/** The argv after `--`, for the verbs that take one. */
|
|
95
118
|
const TRAILING = {
|
|
96
119
|
type: "array",
|
|
97
120
|
items: STRING,
|
|
98
121
|
minItems: 1,
|
|
99
122
|
description: "everything after the first `--`, passed through untouched",
|
|
100
123
|
};
|
|
101
|
-
/**
|
|
124
|
+
/**
|
|
125
|
+
* Path-typed flags, by the input schema they belong to (APRV-421).
|
|
126
|
+
*
|
|
127
|
+
* A flag declared `"path"` PUBLISHES as an ordinary `{"type":"string"}` — the
|
|
128
|
+
* wire contract is unchanged and no consumer sees a new keyword — while the
|
|
129
|
+
* registry retains the fact that its value NAMES A FILE. `approval serve`
|
|
130
|
+
* reads that through {@link pathFlagsOf} to confine such a value to the store
|
|
131
|
+
* the server was started for, so a flag added to this file tomorrow is
|
|
132
|
+
* confined on the day it appears rather than on the day somebody remembers to
|
|
133
|
+
* add it to a list kept somewhere else.
|
|
134
|
+
*
|
|
135
|
+
* A WeakMap keyed by the schema object, rather than a field on {@link VerbSpec},
|
|
136
|
+
* because `input()` returns that schema and all hundred-odd call sites would
|
|
137
|
+
* otherwise have to change to carry a second value.
|
|
138
|
+
*/
|
|
139
|
+
const PATH_FLAGS_BY_INPUT = new WeakMap();
|
|
140
|
+
/** The flags of `spec` whose value names a filesystem path. */
|
|
141
|
+
export function pathFlagsOf(spec) {
|
|
142
|
+
return PATH_FLAGS_BY_INPUT.get(spec.input) ?? [];
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Flags are `string | boolean` (see `args.ts`); nothing else can be parsed.
|
|
146
|
+
* `path` is a `string` the registry knows more about — see above.
|
|
147
|
+
*/
|
|
102
148
|
function input(parts) {
|
|
103
149
|
const flagProperties = {};
|
|
150
|
+
const pathFlags = [];
|
|
104
151
|
for (const [flag, kind] of Object.entries(parts.flags)) {
|
|
105
|
-
|
|
152
|
+
if (kind === "path")
|
|
153
|
+
pathFlags.push(flag);
|
|
154
|
+
flagProperties[flag] = kind === "boolean" ? BOOLEAN : STRING;
|
|
106
155
|
}
|
|
107
156
|
const properties = {
|
|
108
157
|
positionals: parts.positionals ?? NO_POSITIONALS,
|
|
@@ -115,18 +164,20 @@ function input(parts) {
|
|
|
115
164
|
};
|
|
116
165
|
if (parts.trailing !== undefined)
|
|
117
166
|
properties["trailing"] = parts.trailing;
|
|
118
|
-
|
|
167
|
+
const schema = {
|
|
119
168
|
type: "object",
|
|
120
169
|
properties,
|
|
121
170
|
required: [],
|
|
122
171
|
additionalProperties: false,
|
|
123
172
|
};
|
|
173
|
+
PATH_FLAGS_BY_INPUT.set(schema, pathFlags);
|
|
174
|
+
return schema;
|
|
124
175
|
}
|
|
125
176
|
/** Flags every command accepts. */
|
|
126
177
|
const HELP_FLAGS = { "--help": "boolean", "-h": "boolean" };
|
|
127
178
|
const JSON_FLAG = { "--json": "boolean" };
|
|
128
|
-
const LOG_FLAG = { "--log": "
|
|
129
|
-
const POLICY_FLAGS = { "--policy": "
|
|
179
|
+
const LOG_FLAG = { "--log": "path" };
|
|
180
|
+
const POLICY_FLAGS = { "--policy": "path", "--dir": "path" };
|
|
130
181
|
const AS_FLAG = { "--as": "string" };
|
|
131
182
|
/**
|
|
132
183
|
* The shared failure shape. Usage and I/O failures print
|
|
@@ -253,6 +304,8 @@ const POLICY_RESOLUTION_OUTPUT = object({
|
|
|
253
304
|
winner: BOOLEAN,
|
|
254
305
|
tieBreak: STRING,
|
|
255
306
|
}, ["pattern", "specificity", "autonomy", "winner", "tieBreak"])),
|
|
307
|
+
// APRV-445: may an agent open a request in this class with `propose`.
|
|
308
|
+
agentRequest: object({ allowed: BOOLEAN, explicit: BOOLEAN, pattern: nullable(STRING), humanOnly: BOOLEAN }, ["allowed", "explicit", "pattern", "humanOnly"]),
|
|
256
309
|
decisionPath: arrayOf(STRING),
|
|
257
310
|
}, [
|
|
258
311
|
"class",
|
|
@@ -266,6 +319,7 @@ const POLICY_RESOLUTION_OUTPUT = object({
|
|
|
266
319
|
"irreversibility",
|
|
267
320
|
"irreversiblePatterns",
|
|
268
321
|
"candidates",
|
|
322
|
+
"agentRequest",
|
|
269
323
|
"decisionPath",
|
|
270
324
|
]);
|
|
271
325
|
// ---------------------------------------------------------------------------
|
|
@@ -289,7 +343,7 @@ const VERBS = [
|
|
|
289
343
|
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
344
|
human_only: true,
|
|
291
345
|
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": "
|
|
346
|
+
input: input({ flags: { "--dir": "path", ...JSON_FLAG, ...HELP_FLAGS } }),
|
|
293
347
|
output: object({
|
|
294
348
|
ok: { const: true },
|
|
295
349
|
dir: STRING,
|
|
@@ -305,7 +359,7 @@ const VERBS = [
|
|
|
305
359
|
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
360
|
human_only: true,
|
|
307
361
|
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": "
|
|
362
|
+
input: input({ flags: { "--dir": "path", "--api-base": "string", ...HELP_FLAGS } }),
|
|
309
363
|
output: null,
|
|
310
364
|
error: ERROR_SCHEMA,
|
|
311
365
|
exit_codes: BASE_EXIT_CODES,
|
|
@@ -323,6 +377,9 @@ const VERBS = [
|
|
|
323
377
|
records: nullable(INTEGER),
|
|
324
378
|
head: HEAD,
|
|
325
379
|
intactThroughSeq: INTEGER,
|
|
380
|
+
tear: { enum: ["nul-filled", "partial-line"] },
|
|
381
|
+
tornBytes: INTEGER,
|
|
382
|
+
intactBytes: INTEGER,
|
|
326
383
|
firstBadSeq: nullable(INTEGER),
|
|
327
384
|
reason: STRING,
|
|
328
385
|
message: STRING,
|
|
@@ -533,12 +590,14 @@ const VERBS = [
|
|
|
533
590
|
{
|
|
534
591
|
name: "policy",
|
|
535
592
|
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).",
|
|
593
|
+
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). With --path <path> it signs off an ordinary PROTECTED path (policy.edit or a policy.edit.* sub-class) as one gate.path.signed_off event, which is what resolves SPEC.md's `(Amended APRV-n, pending sign-off.)` suffix: whole-file evidence that a human read those bytes, weaker than the hunk a grant binds, and read by the protected-path guard only after its grant search has failed (APRV-338). All three routes are human-only, refuse an agent actor with a machine-readable code, and compute their digest in the runtime. With --bootstrap it attests a store's FIRST policy only, reading the verified log first: a re-run over the same bytes refuses policy-already-attested and changed bytes refuse policy-amendment-required, both at exit 1 with nothing appended, which is the operator's provisioning step for a tenant with no shell (APRV-449).",
|
|
537
594
|
human_only: true,
|
|
538
595
|
input: input({
|
|
539
596
|
flags: {
|
|
540
597
|
...POLICY_FLAGS,
|
|
541
598
|
"--organ": "string",
|
|
599
|
+
"--path": "string",
|
|
600
|
+
"--bootstrap": "boolean",
|
|
542
601
|
...AS_FLAG,
|
|
543
602
|
...LOG_FLAG,
|
|
544
603
|
...JSON_FLAG,
|
|
@@ -554,6 +613,10 @@ const VERBS = [
|
|
|
554
613
|
// repository-relative spelling the record carries, which is the
|
|
555
614
|
// identity the guard matches on and is not derivable from `path`.
|
|
556
615
|
organ_path: STRING,
|
|
616
|
+
// The same fact for the --path route, under its own name so that a
|
|
617
|
+
// reader of one object can never take a sign-off for an organ
|
|
618
|
+
// attestation: the two records are different claims (APRV-338).
|
|
619
|
+
signed_path: STRING,
|
|
557
620
|
}, ["ok", "seq", "sha256", "path"]),
|
|
558
621
|
error: ERROR_SCHEMA,
|
|
559
622
|
exit_codes: BASE_EXIT_CODES,
|
|
@@ -606,6 +669,37 @@ const VERBS = [
|
|
|
606
669
|
error: ERROR_SCHEMA,
|
|
607
670
|
exit_codes: BASE_EXIT_CODES,
|
|
608
671
|
},
|
|
672
|
+
{
|
|
673
|
+
name: "policy",
|
|
674
|
+
subcommand: "apply",
|
|
675
|
+
purpose: "Apply a proposal document's quoted Current/Replace-with pairs to APPROVAL.md and then run the amendment, so the edit and its attestation stay one act. It PUBLISHES by default (APRV-360): the amendment runs with --pr, so the branch is created on the remote by refspec, the pull request is opened and auto-merge is armed, and the checkout never moves; --no-publish stops at the commit and --pr is accepted as a no-op. HUMAN-ONLY twice over: an agent identity refuses `apply-agent-actor`, and the verb classifies `policy.core`, which the reference policy holds human-only. Every pair is resolved against an in-memory copy before a byte is written, so a stale proposal writes nothing at all; a whole-file replacement is not accepted, because every byte written is anchored to a byte proved present in the live file. Fences are read by their backtick run, so a wrapper fence around a block is the wrapper it is (APRV-273).",
|
|
676
|
+
human_only: true,
|
|
677
|
+
input: input({
|
|
678
|
+
positionals: positionals([{ name: "proposal", description: "the proposal document to apply" }], 1),
|
|
679
|
+
flags: {
|
|
680
|
+
...POLICY_FLAGS,
|
|
681
|
+
...LOG_FLAG,
|
|
682
|
+
...AS_FLAG,
|
|
683
|
+
"--dry-run": "boolean",
|
|
684
|
+
"--no-amend": "boolean",
|
|
685
|
+
"--pr": "boolean",
|
|
686
|
+
"--no-publish": "boolean",
|
|
687
|
+
"--yes": "boolean",
|
|
688
|
+
...JSON_FLAG,
|
|
689
|
+
...HELP_FLAGS,
|
|
690
|
+
},
|
|
691
|
+
}),
|
|
692
|
+
output: object({
|
|
693
|
+
ok: { const: true },
|
|
694
|
+
policy: STRING,
|
|
695
|
+
proposal: STRING,
|
|
696
|
+
pairs: INTEGER,
|
|
697
|
+
noop: BOOLEAN,
|
|
698
|
+
dryRun: BOOLEAN,
|
|
699
|
+
}, ["ok", "policy", "proposal", "pairs", "noop", "dryRun"]),
|
|
700
|
+
error: ERROR_SCHEMA,
|
|
701
|
+
exit_codes: BASE_EXIT_CODES,
|
|
702
|
+
},
|
|
609
703
|
{
|
|
610
704
|
name: "register",
|
|
611
705
|
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.",
|
|
@@ -632,7 +726,7 @@ const VERBS = [
|
|
|
632
726
|
flags: {
|
|
633
727
|
"--action": "string",
|
|
634
728
|
...AS_FLAG,
|
|
635
|
-
"--payload": "
|
|
729
|
+
"--payload": "path",
|
|
636
730
|
...POLICY_FLAGS,
|
|
637
731
|
...LOG_FLAG,
|
|
638
732
|
...JSON_FLAG,
|
|
@@ -652,6 +746,66 @@ const VERBS = [
|
|
|
652
746
|
error: ERROR_SCHEMA,
|
|
653
747
|
exit_codes: BASE_EXIT_CODES,
|
|
654
748
|
},
|
|
749
|
+
{
|
|
750
|
+
name: "propose",
|
|
751
|
+
purpose: "Register and request ONE action in one call, with the payload inline as a JSON object (--payload-json, at most 262144 bytes) rather than a file, for a requester with no file on the gate's machine. Unlike request, the caller names the class, and that is bounded by the operator: the class must be an EXACT key of the policy's classes (a wildcard match does not count) and that key or a declared <prefix>.* family must set agent_may_request: true, else class-not-agent-requestable and nothing is written. --key MUST begin with <class>: (key-class-mismatch otherwise); consumers read the class from the record. --key is at most 1024 bytes and --summary at most 4096; lone surrogates and non-finite numbers are usage errors. The task id is derived from the actor, class and key (propose:<32 hex>), so a retry with the same class, key and bytes appends nothing and answers idempotent:true while the request is pending, usably granted, refused or executed (state executed); a request that can no longer be answered usefully (withdrawn, expired, a grant whose window lapsed, or one voided by a re-attested policy) is asked again by the same call. The same key with different bytes refuses duplicate-request while a request is live and payload-mismatch otherwise. decision requested: a human is asked, poll with wait <task> --timeout 0. decision autonomous or supervised: nothing is asked, act and then record it with start. The summary is stored in the log in cleartext; the payload is stored in the payload store, never in the log.",
|
|
752
|
+
human_only: false,
|
|
753
|
+
input: input({
|
|
754
|
+
flags: {
|
|
755
|
+
"--class": "string",
|
|
756
|
+
"--key": "string",
|
|
757
|
+
"--summary": "string",
|
|
758
|
+
"--payload-json": "string",
|
|
759
|
+
...AS_FLAG,
|
|
760
|
+
...POLICY_FLAGS,
|
|
761
|
+
...LOG_FLAG,
|
|
762
|
+
...JSON_FLAG,
|
|
763
|
+
...HELP_FLAGS,
|
|
764
|
+
},
|
|
765
|
+
}),
|
|
766
|
+
output: object({
|
|
767
|
+
ok: { const: true },
|
|
768
|
+
task: STRING,
|
|
769
|
+
action_key: STRING,
|
|
770
|
+
class: STRING,
|
|
771
|
+
payload_hash: SHA256,
|
|
772
|
+
decision: { enum: ["requested", "autonomous", "supervised"] },
|
|
773
|
+
state: nullable({
|
|
774
|
+
enum: ["requested", "granted", "rejected", "revoked", "expired", "withdrawn", "executed"],
|
|
775
|
+
}),
|
|
776
|
+
seq: nullable(INTEGER),
|
|
777
|
+
idempotent: BOOLEAN,
|
|
778
|
+
}, ["ok", "task", "action_key", "class", "payload_hash", "decision", "state", "seq", "idempotent"]),
|
|
779
|
+
error: ERROR_SCHEMA,
|
|
780
|
+
exit_codes: BASE_EXIT_CODES,
|
|
781
|
+
},
|
|
782
|
+
{
|
|
783
|
+
name: "start",
|
|
784
|
+
purpose: "Record execution.started for a proposed action you are about to carry out yourself, once per key. Proposals only (task-not-proposal otherwise). REQUESTER-ONLY: the actor must be the one that registered the task. --payload-json is the bytes you are about to act on and must hash to the registered payload_hash. A key whose request is pending or granted-and-unspent spends its harness grant (refused unless granted, unexpired, unspent and under the policy it was asked under); a key with no such request (none, or one withdrawn, expired, rejected or revoked) is recorded as authorized by the policy (refused for a manual or human-only class). It grants nothing: it writes only what the grant or the policy already authorized, and a second start of the same key is already-executed.",
|
|
785
|
+
human_only: false,
|
|
786
|
+
input: input({
|
|
787
|
+
positionals: positionals([{ name: "task", description: "the task id propose returned" }], 1),
|
|
788
|
+
flags: {
|
|
789
|
+
"--action": "string",
|
|
790
|
+
"--payload-json": "string",
|
|
791
|
+
...AS_FLAG,
|
|
792
|
+
...POLICY_FLAGS,
|
|
793
|
+
...LOG_FLAG,
|
|
794
|
+
...JSON_FLAG,
|
|
795
|
+
...HELP_FLAGS,
|
|
796
|
+
},
|
|
797
|
+
}),
|
|
798
|
+
output: object({
|
|
799
|
+
ok: { const: true },
|
|
800
|
+
task: STRING,
|
|
801
|
+
action_key: STRING,
|
|
802
|
+
class: STRING,
|
|
803
|
+
authorization: { enum: ["grant", "policy"] },
|
|
804
|
+
seq: INTEGER,
|
|
805
|
+
}, ["ok", "task", "action_key", "class", "authorization", "seq"]),
|
|
806
|
+
error: ERROR_SCHEMA,
|
|
807
|
+
exit_codes: BASE_EXIT_CODES,
|
|
808
|
+
},
|
|
655
809
|
{
|
|
656
810
|
name: "grant",
|
|
657
811
|
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.",
|
|
@@ -881,11 +1035,12 @@ const VERBS = [
|
|
|
881
1035
|
},
|
|
882
1036
|
{
|
|
883
1037
|
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.",
|
|
1038
|
+
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. Since APRV-347 it also confines what the child may READ: a policy declaring a `read_scope` block turns the profile deny-default for file reads and opens the gate root and whatever that block adds, and `--read-jail` applies the same confinement to one command whether the policy declares a block or not. There is no flag that turns the jail off where a policy asked for it.",
|
|
885
1039
|
human_only: false,
|
|
886
1040
|
input: input({
|
|
887
1041
|
flags: {
|
|
888
1042
|
"--allow-loopback": "boolean",
|
|
1043
|
+
"--read-jail": "boolean",
|
|
889
1044
|
...LOG_FLAG,
|
|
890
1045
|
...HELP_FLAGS,
|
|
891
1046
|
},
|
|
@@ -1126,7 +1281,7 @@ const VERBS = [
|
|
|
1126
1281
|
},
|
|
1127
1282
|
{
|
|
1128
1283
|
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
|
|
1284
|
+
purpose: "Block until every approval.requested of a task has a decision, or the timeout elapses. THE EXIT CODE IS THE DECISION: 0 granted (or nothing-to-wait-for, which `status` names and which is no grant), 1 rejected, revoked, withdrawn or not-registered, 3 expired (a grant whose request window lapsed included), 6 timeout, 7 void (a re-attest voided a pending request or a harness grant: ask again). 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 registered task with none, or whose every grant an execution has already spent, returns immediately at exit 0 with status nothing-to-wait-for, NEVER granted; a task the log has never registered is refused not-registered at exit 1 (APRV-428). --timeout 0 reads the current state once and never sleeps (an undecided request answers timeout, exit 6, at once; it refuses --withdraw-on-timeout): use it to poll through approval serve, which runs every call through one queue that a sleeping wait would hold. 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
1285
|
human_only: false,
|
|
1131
1286
|
input: input({
|
|
1132
1287
|
positionals: positionals([{ name: "task", description: "the task id" }], 1),
|
|
@@ -1144,7 +1299,18 @@ const VERBS = [
|
|
|
1144
1299
|
output: object({
|
|
1145
1300
|
ok: BOOLEAN,
|
|
1146
1301
|
task: STRING,
|
|
1147
|
-
status: {
|
|
1302
|
+
status: {
|
|
1303
|
+
enum: [
|
|
1304
|
+
"granted",
|
|
1305
|
+
"executed",
|
|
1306
|
+
"rejected",
|
|
1307
|
+
"withdrawn",
|
|
1308
|
+
"void",
|
|
1309
|
+
"expired",
|
|
1310
|
+
"nothing-to-wait-for",
|
|
1311
|
+
"timeout",
|
|
1312
|
+
],
|
|
1313
|
+
},
|
|
1148
1314
|
actions: arrayOf(object({
|
|
1149
1315
|
action_key: STRING,
|
|
1150
1316
|
state: STRING,
|
|
@@ -1158,10 +1324,13 @@ const VERBS = [
|
|
|
1158
1324
|
}, ["ok", "task", "status", "actions"]),
|
|
1159
1325
|
error: ERROR_SCHEMA,
|
|
1160
1326
|
exit_codes: [
|
|
1161
|
-
{
|
|
1327
|
+
{
|
|
1328
|
+
code: 0,
|
|
1329
|
+
meaning: "granted; or executed on the policy's own authority after its request ended; or nothing-to-wait-for, a registered task with no requests or whose every grant is already spent. `status` says which, and only `granted` is a grant",
|
|
1330
|
+
},
|
|
1162
1331
|
{
|
|
1163
1332
|
code: 1,
|
|
1164
|
-
meaning: "NOT AUTHORIZED and terminal — a human said no (rejected/revoked), or the requester withdrew the request; or the
|
|
1333
|
+
meaning: "NOT AUTHORIZED and terminal — a human said no (rejected/revoked), or the requester withdrew the request (`status` says which); or the task has no task.registered record (refusal `not-registered`); or the log is corrupt",
|
|
1165
1334
|
},
|
|
1166
1335
|
USAGE,
|
|
1167
1336
|
{ code: 3, meaning: "EXPIRED — the TTL lapsed before a decision landed; or a torn tail" },
|
|
@@ -1170,6 +1339,10 @@ const VERBS = [
|
|
|
1170
1339
|
code: 6,
|
|
1171
1340
|
meaning: "TIMEOUT — the wait elapsed with request(s) undecided. Nothing was appended, they are still live, and waiting again is legitimate",
|
|
1172
1341
|
},
|
|
1342
|
+
{
|
|
1343
|
+
code: 7,
|
|
1344
|
+
meaning: "VOID — the policy was re-attested since the request was routed, and the answer can never be used: a PENDING request (any task; no decision may land on it) or a HARNESS grant (execution: harness, every proposal included; its spend refuses policy-drift). A token grant for approval run is not void and reads granted. Ask again: for a proposal, the same propose call re-files",
|
|
1345
|
+
},
|
|
1173
1346
|
],
|
|
1174
1347
|
},
|
|
1175
1348
|
{
|
|
@@ -1187,7 +1360,20 @@ const VERBS = [
|
|
|
1187
1360
|
requested_ts: STRING,
|
|
1188
1361
|
seq: INTEGER,
|
|
1189
1362
|
ttl_remaining_ms: nullable(INTEGER),
|
|
1190
|
-
|
|
1363
|
+
// APRV-423: the whole window `ttl_remaining_ms` counts down from,
|
|
1364
|
+
// which is the policy's TTL narrowed by the requesting hook's
|
|
1365
|
+
// harness cap; null only when nothing bounds the request.
|
|
1366
|
+
ttl_ms: nullable(INTEGER),
|
|
1367
|
+
}, [
|
|
1368
|
+
"action_key",
|
|
1369
|
+
"task",
|
|
1370
|
+
"class",
|
|
1371
|
+
"est_cost_usd",
|
|
1372
|
+
"requested_ts",
|
|
1373
|
+
"seq",
|
|
1374
|
+
"ttl_remaining_ms",
|
|
1375
|
+
"ttl_ms",
|
|
1376
|
+
])),
|
|
1191
1377
|
}, ["ok", "pending"]),
|
|
1192
1378
|
error: ERROR_SCHEMA,
|
|
1193
1379
|
exit_codes: [OK, USAGE, TORN, IO],
|
|
@@ -1275,13 +1461,15 @@ const VERBS = [
|
|
|
1275
1461
|
},
|
|
1276
1462
|
{
|
|
1277
1463
|
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.",
|
|
1464
|
+
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. `refusals` counts the decisions and the gestures a decision surface would not take, with the newest few of each, and it appears only when the log carries one: it is informational, like `coverage`, because a refusal is the gate having worked. This is what an operator must fix; `queue` is what a human must answer, and neither carries the other's content. Writes nothing.",
|
|
1279
1465
|
human_only: false,
|
|
1280
1466
|
input: input({ flags: { ...POLICY_FLAGS, ...LOG_FLAG, ...JSON_FLAG, ...HELP_FLAGS } }),
|
|
1281
1467
|
output: object({
|
|
1282
1468
|
ok: { const: true },
|
|
1283
1469
|
healthy: BOOLEAN,
|
|
1284
|
-
|
|
1470
|
+
// APRV-449: `attested_by` is the actor of the attesting record at `seq`,
|
|
1471
|
+
// read from the verified log, `null` when there is none. Always present.
|
|
1472
|
+
attestation: object({ state: STRING, seq: nullable(INTEGER), attested_by: nullable(STRING) }, ["state", "seq", "attested_by"]),
|
|
1285
1473
|
verification: object({ status: STRING, records: nullable(INTEGER) }, ["status", "records"]),
|
|
1286
1474
|
dangling: arrayOf(OPEN_OBJECT),
|
|
1287
1475
|
indeterminate: arrayOf(OPEN_OBJECT),
|
|
@@ -1309,10 +1497,34 @@ const VERBS = [
|
|
|
1309
1497
|
reconciliation: arrayOf(OPEN_OBJECT),
|
|
1310
1498
|
payload_store: OPEN_OBJECT,
|
|
1311
1499
|
anomalies: arrayOf(OPEN_OBJECT),
|
|
1500
|
+
// APRV-376: the two refusal families, counted, with the newest few of
|
|
1501
|
+
// each. INFORMATIONAL and outside `healthy` and the exit code, exactly
|
|
1502
|
+
// as `anomalies` and `coverage` are: a refusal is the gate having
|
|
1503
|
+
// worked. Present only when the log carries one of either family, and
|
|
1504
|
+
// each family present only when it has one, so a repository where
|
|
1505
|
+
// nothing has been refused emits the object it always emitted. Neither
|
|
1506
|
+
// is reported anywhere else short of `approval log tail`.
|
|
1507
|
+
refusals: object({
|
|
1508
|
+
decision: REFUSAL_FAMILY,
|
|
1509
|
+
gesture: REFUSAL_FAMILY,
|
|
1510
|
+
}, []),
|
|
1312
1511
|
// APRV-214: present only while a window stands, so a repository with
|
|
1313
1512
|
// none emits the object it always emitted. It counts toward `healthy`,
|
|
1314
1513
|
// which is why it is reported here rather than only by `gate status`.
|
|
1315
1514
|
gate_window: OPEN_OBJECT,
|
|
1515
|
+
// APRV-383: the daemon identity this instance resolves — the id a daemon
|
|
1516
|
+
// started HERE would write onto every record it appends, where the id
|
|
1517
|
+
// came from, and the `daemons` allowlist in force for it. ALWAYS present,
|
|
1518
|
+
// like `coverage`, so every consumer sees the same three keys on every
|
|
1519
|
+
// log. INFORMATIONAL and outside `healthy` and the exit code: nothing
|
|
1520
|
+
// here is a fact about this log's integrity. `id: null` is an
|
|
1521
|
+
// `APPROVAL_DAEMON_ID` that is not an id; `allowed: null` is no
|
|
1522
|
+
// restriction, which is not the same fact as an empty list.
|
|
1523
|
+
daemon: object({
|
|
1524
|
+
id: nullable(STRING),
|
|
1525
|
+
source: nullable(STRING),
|
|
1526
|
+
allowed: { anyOf: [{ type: "array", items: STRING }, { type: "null" }] },
|
|
1527
|
+
}, ["id", "source", "allowed"]),
|
|
1316
1528
|
}, [
|
|
1317
1529
|
"ok",
|
|
1318
1530
|
"healthy",
|
|
@@ -1325,6 +1537,7 @@ const VERBS = [
|
|
|
1325
1537
|
"coverage",
|
|
1326
1538
|
"reconciliation",
|
|
1327
1539
|
"payload_store",
|
|
1540
|
+
"daemon",
|
|
1328
1541
|
]),
|
|
1329
1542
|
error: ERROR_SCHEMA,
|
|
1330
1543
|
exit_codes: [OK, { code: 1, meaning: "something needs attention" }, USAGE, IO],
|
|
@@ -1340,7 +1553,7 @@ const VERBS = [
|
|
|
1340
1553
|
"--since": "string",
|
|
1341
1554
|
"--until": "string",
|
|
1342
1555
|
"--source": "string",
|
|
1343
|
-
"--vault": "
|
|
1556
|
+
"--vault": "path",
|
|
1344
1557
|
...POLICY_FLAGS,
|
|
1345
1558
|
...LOG_FLAG,
|
|
1346
1559
|
...JSON_FLAG,
|
|
@@ -1389,9 +1602,9 @@ const VERBS = [
|
|
|
1389
1602
|
flags: {
|
|
1390
1603
|
...LOG_FLAG,
|
|
1391
1604
|
...POLICY_FLAGS,
|
|
1392
|
-
"--tasks": "
|
|
1605
|
+
"--tasks": "path",
|
|
1393
1606
|
"--api-base": "string",
|
|
1394
|
-
"--root": "
|
|
1607
|
+
"--root": "path",
|
|
1395
1608
|
...JSON_FLAG,
|
|
1396
1609
|
...HELP_FLAGS,
|
|
1397
1610
|
},
|
|
@@ -1423,7 +1636,7 @@ const VERBS = [
|
|
|
1423
1636
|
flags: {
|
|
1424
1637
|
...LOG_FLAG,
|
|
1425
1638
|
"--policy-dir": "string",
|
|
1426
|
-
"--policy": "
|
|
1639
|
+
"--policy": "path",
|
|
1427
1640
|
"--payload-dir": "string",
|
|
1428
1641
|
...AS_FLAG,
|
|
1429
1642
|
"--interactive": "boolean",
|
|
@@ -1502,6 +1715,28 @@ const VERBS = [
|
|
|
1502
1715
|
error: ERROR_SCHEMA,
|
|
1503
1716
|
exit_codes: [OK, { code: 1, meaning: "a credential variable is unset" }, USAGE],
|
|
1504
1717
|
},
|
|
1718
|
+
{
|
|
1719
|
+
name: "channel",
|
|
1720
|
+
subcommand: "relay",
|
|
1721
|
+
purpose: "Serve POST /relay/gesture on loopback for an operator's control plane (APRV-455): propose, attest or decline a policy by sha256, or grant or reject a request by action key, attributed to the EdgeOS account the attested policy maps. Authenticated by APPROVAL_RELAY_SECRET from the launch environment; a post without it is refused and never logged. It holds no human identity: an unmapped account is refused sender-unmapped. The raw execution token of a grant never leaves the process.",
|
|
1722
|
+
human_only: true,
|
|
1723
|
+
human_only_note: "It records human decisions and attestations on the word of whoever holds a secret its launcher chose, so starting it is the operator's ceremony: no wrapper publishes it, neither serve credential reaches it, and the hook classifier holds the command line `policy.core` (human-only in the reference policy). The attribution it records is exactly as strong as control over who launches it.",
|
|
1724
|
+
input: input({
|
|
1725
|
+
flags: {
|
|
1726
|
+
"--listen": "string",
|
|
1727
|
+
"--port": "string",
|
|
1728
|
+
"--allow-non-loopback": "boolean",
|
|
1729
|
+
"--proposer": "string",
|
|
1730
|
+
...POLICY_FLAGS,
|
|
1731
|
+
...LOG_FLAG,
|
|
1732
|
+
...JSON_FLAG,
|
|
1733
|
+
...HELP_FLAGS,
|
|
1734
|
+
},
|
|
1735
|
+
}),
|
|
1736
|
+
output: null,
|
|
1737
|
+
error: ERROR_SCHEMA,
|
|
1738
|
+
exit_codes: BASE_EXIT_CODES,
|
|
1739
|
+
},
|
|
1505
1740
|
{
|
|
1506
1741
|
name: "daemon",
|
|
1507
1742
|
subcommand: "run",
|
|
@@ -1511,8 +1746,8 @@ const VERBS = [
|
|
|
1511
1746
|
input: input({
|
|
1512
1747
|
flags: {
|
|
1513
1748
|
...LOG_FLAG,
|
|
1514
|
-
"--tasks": "
|
|
1515
|
-
"--out": "
|
|
1749
|
+
"--tasks": "path",
|
|
1750
|
+
"--out": "path",
|
|
1516
1751
|
...POLICY_FLAGS,
|
|
1517
1752
|
"--interval": "string",
|
|
1518
1753
|
"--debounce": "string",
|
|
@@ -1538,8 +1773,8 @@ const VERBS = [
|
|
|
1538
1773
|
input: input({
|
|
1539
1774
|
flags: {
|
|
1540
1775
|
...LOG_FLAG,
|
|
1541
|
-
"--tasks": "
|
|
1542
|
-
"--out": "
|
|
1776
|
+
"--tasks": "path",
|
|
1777
|
+
"--out": "path",
|
|
1543
1778
|
...POLICY_FLAGS,
|
|
1544
1779
|
"--interval": "string",
|
|
1545
1780
|
"--debounce": "string",
|
|
@@ -1579,6 +1814,33 @@ const VERBS = [
|
|
|
1579
1814
|
error: ERROR_SCHEMA,
|
|
1580
1815
|
exit_codes: READ_ONLY_EXIT_CODES,
|
|
1581
1816
|
},
|
|
1817
|
+
{
|
|
1818
|
+
name: "payload",
|
|
1819
|
+
subcommand: "run",
|
|
1820
|
+
purpose: "Print the payload `approval run` will recompute for a command before it spawns it: the argv, the cwd, and — since APRV-401 — the size and SHA-256 of the script that argv names, so a grant over a script binds the SCRIPT'S BYTES and not its path. A script is a known interpreter (the shells, node, python3 and kin) followed by a path operand, or a path at argv[0] the kernel reads a shebang from. With --hash it prints the payload_hash instead, the value a task file's declaration carries. An argv naming no script carries no `script` key and hashes exactly as it did before the rule existed. An inline program is already in the argv; the interpreter itself, a launcher this runtime does not name, whatever the script itself reads or runs, and the window between this hash and the spawn are all outside the binding. Reads that one file and nothing else: no log, no policy, no network, no token, and it executes nothing.",
|
|
1821
|
+
human_only: false,
|
|
1822
|
+
human_only_note: "Agent-facing by construction: it describes a command an agent is about to ask permission for, before any approval exists, and produces no authority of any kind. The bytes it prints are checked at intake and recomputed at execution, so a wrong answer here refuses rather than widens.",
|
|
1823
|
+
input: input({
|
|
1824
|
+
positionals: NO_POSITIONALS,
|
|
1825
|
+
flags: {
|
|
1826
|
+
// Path-typed, and it matters: the value is the directory every relative
|
|
1827
|
+
// argv word is resolved against, so a caller who could name any
|
|
1828
|
+
// directory could ask this verb to digest any file under it.
|
|
1829
|
+
"--cwd": "path",
|
|
1830
|
+
"--hash": "boolean",
|
|
1831
|
+
...JSON_FLAG,
|
|
1832
|
+
...HELP_FLAGS,
|
|
1833
|
+
},
|
|
1834
|
+
trailing: TRAILING,
|
|
1835
|
+
}),
|
|
1836
|
+
// The canonical payload itself on stdout, as `agentmail-draft` does it: the
|
|
1837
|
+
// bytes ARE the result, and an envelope around them would be a second thing
|
|
1838
|
+
// to strip before hashing. `--hash --json` prints {ok, hash}, which is the
|
|
1839
|
+
// one shape here that is a RESULT rather than the payload.
|
|
1840
|
+
output: null,
|
|
1841
|
+
error: ERROR_SCHEMA,
|
|
1842
|
+
exit_codes: READ_ONLY_EXIT_CODES,
|
|
1843
|
+
},
|
|
1582
1844
|
{
|
|
1583
1845
|
name: "payload",
|
|
1584
1846
|
subcommand: "agentmail-draft",
|
|
@@ -1630,7 +1892,7 @@ const VERBS = [
|
|
|
1630
1892
|
"--message": "string",
|
|
1631
1893
|
"--task": "string",
|
|
1632
1894
|
"--session": "string",
|
|
1633
|
-
"--journal": "
|
|
1895
|
+
"--journal": "path",
|
|
1634
1896
|
...AS_FLAG,
|
|
1635
1897
|
...JSON_FLAG,
|
|
1636
1898
|
...HELP_FLAGS,
|
|
@@ -1706,7 +1968,7 @@ const VERBS = [
|
|
|
1706
1968
|
flags: {
|
|
1707
1969
|
"--limit": "string",
|
|
1708
1970
|
"--since": "string",
|
|
1709
|
-
"--journal": "
|
|
1971
|
+
"--journal": "path",
|
|
1710
1972
|
...JSON_FLAG,
|
|
1711
1973
|
...HELP_FLAGS,
|
|
1712
1974
|
},
|
|
@@ -1730,7 +1992,7 @@ const VERBS = [
|
|
|
1730
1992
|
},
|
|
1731
1993
|
{
|
|
1732
1994
|
name: "values",
|
|
1733
|
-
purpose: "Print the OPTIONAL values block of APPROVAL.md: what the operator loves, likes
|
|
1995
|
+
purpose: "Print the OPTIONAL values block of APPROVAL.md: what the operator loves, likes (which is also where what they ask of an agent as behaviour lives) and dislikes, 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
1996
|
human_only: false,
|
|
1735
1997
|
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
1998
|
input: input({
|
|
@@ -1810,6 +2072,19 @@ const VERBS = [
|
|
|
1810
2072
|
error: ERROR_SCHEMA,
|
|
1811
2073
|
exit_codes: BASE_EXIT_CODES,
|
|
1812
2074
|
},
|
|
2075
|
+
{
|
|
2076
|
+
name: "setup",
|
|
2077
|
+
subcommand: "sender-key",
|
|
2078
|
+
purpose: "Mint the operator-held key that turns a channel account id into the value an `approvers[id].senders` mapping carries (APRV-370), store it, and record where it lives. A published policy and a published log disclose the account otherwise, and a plain unkeyed digest would not fix that: a Telegram id is a short decimal number and the whole space is enumerable. It does not edit an attested policy. INTERACTIVE ONLY — except `--id <account-id>`, which mints and stores nothing and prints the `hmac-sha256:<hex>` mapping line for one account under the key already in the environment, so it runs anywhere.",
|
|
2079
|
+
human_only: true,
|
|
2080
|
+
human_only_note: "Minting it is minting the value every sender mapping is then written against, and an agent that could mint one could re-key the mapping and make every account in the policy unresolvable. The verb classifies policy.core. `--id` is a computation over a key the caller already holds and prints a value designed to be published, so it is not the human-only half.",
|
|
2081
|
+
input: input({
|
|
2082
|
+
flags: { "--id": "string", ...AS_FLAG, ...LOG_FLAG, ...POLICY_FLAGS, ...HELP_FLAGS },
|
|
2083
|
+
}),
|
|
2084
|
+
output: null,
|
|
2085
|
+
error: ERROR_SCHEMA,
|
|
2086
|
+
exit_codes: BASE_EXIT_CODES,
|
|
2087
|
+
},
|
|
1813
2088
|
{
|
|
1814
2089
|
name: "setup",
|
|
1815
2090
|
subcommand: "checkpoint",
|
|
@@ -1869,7 +2144,7 @@ const VERBS = [
|
|
|
1869
2144
|
"--logs": "string",
|
|
1870
2145
|
"--env-file": "string",
|
|
1871
2146
|
"--exec": "string",
|
|
1872
|
-
"--out": "
|
|
2147
|
+
"--out": "path",
|
|
1873
2148
|
"--uninstall": "boolean",
|
|
1874
2149
|
...AS_FLAG,
|
|
1875
2150
|
...LOG_FLAG,
|
|
@@ -1890,7 +2165,7 @@ const VERBS = [
|
|
|
1890
2165
|
positionals: positionals([{ name: "name", description: "the credential name" }], 1),
|
|
1891
2166
|
flags: {
|
|
1892
2167
|
"--value-env": "string",
|
|
1893
|
-
"--vault": "
|
|
2168
|
+
"--vault": "path",
|
|
1894
2169
|
...LOG_FLAG,
|
|
1895
2170
|
...POLICY_FLAGS,
|
|
1896
2171
|
...AS_FLAG,
|
|
@@ -1910,7 +2185,7 @@ const VERBS = [
|
|
|
1910
2185
|
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
2186
|
input: input({
|
|
1912
2187
|
flags: {
|
|
1913
|
-
"--vault": "
|
|
2188
|
+
"--vault": "path",
|
|
1914
2189
|
...LOG_FLAG,
|
|
1915
2190
|
...POLICY_FLAGS,
|
|
1916
2191
|
...AS_FLAG,
|
|
@@ -1936,7 +2211,7 @@ const VERBS = [
|
|
|
1936
2211
|
input: input({
|
|
1937
2212
|
positionals: positionals([{ name: "name", description: "the credential name" }], 1),
|
|
1938
2213
|
flags: {
|
|
1939
|
-
"--vault": "
|
|
2214
|
+
"--vault": "path",
|
|
1940
2215
|
...LOG_FLAG,
|
|
1941
2216
|
...POLICY_FLAGS,
|
|
1942
2217
|
...AS_FLAG,
|
|
@@ -1963,9 +2238,9 @@ const VERBS = [
|
|
|
1963
2238
|
positionals: positionals([{ name: "action-key", description: "the action's idempotency_key" }], 1),
|
|
1964
2239
|
flags: {
|
|
1965
2240
|
"--token": "string",
|
|
1966
|
-
"--payload": "
|
|
2241
|
+
"--payload": "path",
|
|
1967
2242
|
...AS_FLAG,
|
|
1968
|
-
"--vault": "
|
|
2243
|
+
"--vault": "path",
|
|
1969
2244
|
...POLICY_FLAGS,
|
|
1970
2245
|
...LOG_FLAG,
|
|
1971
2246
|
"--timeout": "string",
|
|
@@ -2020,9 +2295,9 @@ const VERBS = [
|
|
|
2020
2295
|
positionals: positionals([{ name: "action-key", description: "the action's idempotency_key" }], 1),
|
|
2021
2296
|
flags: {
|
|
2022
2297
|
"--token": "string",
|
|
2023
|
-
"--payload": "
|
|
2298
|
+
"--payload": "path",
|
|
2024
2299
|
...AS_FLAG,
|
|
2025
|
-
"--vault": "
|
|
2300
|
+
"--vault": "path",
|
|
2026
2301
|
...POLICY_FLAGS,
|
|
2027
2302
|
...LOG_FLAG,
|
|
2028
2303
|
"--timeout": "string",
|
|
@@ -2076,7 +2351,7 @@ const VERBS = [
|
|
|
2076
2351
|
input: input({
|
|
2077
2352
|
positionals: positionals([{ name: "action-key", description: "the action's idempotency_key" }], 1),
|
|
2078
2353
|
flags: {
|
|
2079
|
-
"--token": "string", "--payload": "
|
|
2354
|
+
"--token": "string", "--payload": "path", ...AS_FLAG, "--vault": "path",
|
|
2080
2355
|
...POLICY_FLAGS, ...LOG_FLAG, "--timeout": "string", ...JSON_FLAG, ...HELP_FLAGS,
|
|
2081
2356
|
},
|
|
2082
2357
|
}),
|
|
@@ -2100,6 +2375,7 @@ const VERBS = [
|
|
|
2100
2375
|
...AS_FLAG,
|
|
2101
2376
|
"--timeout": "string",
|
|
2102
2377
|
"--interval": "string",
|
|
2378
|
+
"--harness-cap": "string",
|
|
2103
2379
|
...POLICY_FLAGS,
|
|
2104
2380
|
...LOG_FLAG,
|
|
2105
2381
|
...HELP_FLAGS,
|
|
@@ -2123,6 +2399,7 @@ const VERBS = [
|
|
|
2123
2399
|
...AS_FLAG,
|
|
2124
2400
|
"--timeout": "string",
|
|
2125
2401
|
"--interval": "string",
|
|
2402
|
+
"--harness-cap": "string",
|
|
2126
2403
|
...POLICY_FLAGS,
|
|
2127
2404
|
...LOG_FLAG,
|
|
2128
2405
|
...HELP_FLAGS,
|
|
@@ -2147,6 +2424,7 @@ const VERBS = [
|
|
|
2147
2424
|
"--timeout": "string",
|
|
2148
2425
|
"--interval": "string",
|
|
2149
2426
|
"--retry-grace": "string",
|
|
2427
|
+
"--harness-cap": "string",
|
|
2150
2428
|
...POLICY_FLAGS,
|
|
2151
2429
|
...LOG_FLAG,
|
|
2152
2430
|
...HELP_FLAGS,
|
|
@@ -2159,6 +2437,81 @@ const VERBS = [
|
|
|
2159
2437
|
{ code: 2, meaning: "the hook is misconfigured or a PostToolUse outcome was not recorded; the diagnostic is on stderr" },
|
|
2160
2438
|
],
|
|
2161
2439
|
},
|
|
2440
|
+
{
|
|
2441
|
+
name: "hook",
|
|
2442
|
+
subcommand: "grok",
|
|
2443
|
+
purpose: "Read one Grok Build PreToolUse event on stdin (camelCase keys), classify its Bash command and its Edit, Write, MultiEdit and NotebookEdit paths, resolve the class against APPROVAL.md, and answer {decision, reason} on stdout — waiting on a real decision when the class is manual. THE VERDICT IS NEVER 'ask'. A DENY IS EXIT 2, not exit 0 with a body: Grok reads the exit code, so the Claude dialect's exit-0 deny would be read as an allow, and Grok states that it reads .claude/settings.json for compatibility, which is how that dialect can fire under a Grok session in the first place. Exit 0 is an allow whatever stdout said.",
|
|
2444
|
+
human_only: false,
|
|
2445
|
+
human_only_note: "The agent harness surface, so agent-facing by construction: Grok Build 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.",
|
|
2446
|
+
input: input({
|
|
2447
|
+
flags: {
|
|
2448
|
+
...AS_FLAG,
|
|
2449
|
+
"--timeout": "string",
|
|
2450
|
+
"--interval": "string",
|
|
2451
|
+
"--retry-grace": "string",
|
|
2452
|
+
"--harness-cap": "string",
|
|
2453
|
+
...POLICY_FLAGS,
|
|
2454
|
+
...LOG_FLAG,
|
|
2455
|
+
...HELP_FLAGS,
|
|
2456
|
+
},
|
|
2457
|
+
}),
|
|
2458
|
+
output: null,
|
|
2459
|
+
error: ERROR_SCHEMA,
|
|
2460
|
+
exit_codes: [
|
|
2461
|
+
{ code: 0, meaning: "the tool call is allowed; the {decision, reason} object is on stdout" },
|
|
2462
|
+
{ code: 2, meaning: "the tool call is DENIED, or the hook is misconfigured; either way Grok blocks and reads the reason" },
|
|
2463
|
+
],
|
|
2464
|
+
},
|
|
2465
|
+
{
|
|
2466
|
+
name: "hook",
|
|
2467
|
+
subcommand: "muse",
|
|
2468
|
+
purpose: "Read one Meta Muse Code PreToolUse or PostToolUse event on stdin (snake_case), classify its bash command or its write_file, read_file and search paths, resolve the class against APPROVAL.md, and answer allow or deny — waiting on a real decision when the class is manual. THE VERDICT IS NEVER 'ask'. It answers in ONE dialect and nothing else, the nested permissionDecision object at exit 0, because Muse treats output carrying any unsupported key as a failed hook and a failed hook FAILS OPEN. A Contributor-tier model is refused for every tool call regardless of policy (hook-muse-contributor-model): Meta trains on that tier's prompts and completions, so every read is a disclosure. Muse fails open on hook crash, timeout and malformed output, so this is enforcement only while the hook is healthy and answers inside the timeout the human commits.",
|
|
2469
|
+
human_only: false,
|
|
2470
|
+
human_only_note: "The agent harness surface, so agent-facing by construction: Muse invokes it around the agent's own tool calls. It proposes and waits; it never records a decision.",
|
|
2471
|
+
input: input({
|
|
2472
|
+
flags: {
|
|
2473
|
+
...AS_FLAG,
|
|
2474
|
+
"--timeout": "string",
|
|
2475
|
+
"--interval": "string",
|
|
2476
|
+
"--retry-grace": "string",
|
|
2477
|
+
"--harness-cap": "string",
|
|
2478
|
+
...POLICY_FLAGS,
|
|
2479
|
+
...LOG_FLAG,
|
|
2480
|
+
...HELP_FLAGS,
|
|
2481
|
+
},
|
|
2482
|
+
}),
|
|
2483
|
+
output: null,
|
|
2484
|
+
error: ERROR_SCHEMA,
|
|
2485
|
+
exit_codes: [
|
|
2486
|
+
{ code: 0, meaning: "the PreToolUse verdict (allow OR deny) is the JSON object on stdout, or a reported PostToolUse counterpart landed" },
|
|
2487
|
+
{ code: 2, meaning: "the hook is misconfigured or a PostToolUse outcome was not recorded; the diagnostic is on stderr" },
|
|
2488
|
+
],
|
|
2489
|
+
},
|
|
2490
|
+
{
|
|
2491
|
+
name: "hook",
|
|
2492
|
+
subcommand: "hermes",
|
|
2493
|
+
purpose: "Read one Hermes Agent pre_tool_call or post_tool_call event on stdin (snake_case, and the event names are its own rather than Claude Code's), classify its terminal command or its write_file and patch paths and its read paths, resolve the class against APPROVAL.md, and answer allow or deny — waiting on a real decision when the class is manual. THE VERDICT IS NEVER 'ask'. It answers in ONE dialect and nothing else, Hermes's own {action, message} object: a deny is that object AT EXIT 2, which Hermes documents as an unconditional block whose message it reads from the stdout directive first (so body and code agree by the harness's own rule, and a refusal whose body never made it out still blocks); an allow is {} at exit 0, because Hermes has no allow directive and no directive is the allow, so the allow's reason goes to stderr. The Claude-compatible {decision, reason} form is never emitted beside it, because Muse established that a refusal hedged across dialects is read as a broken hook rather than as a stronger no. execute_code is refused outright (hook-hermes-execute-code-unbound): the call carries a program and no path, no argv and no working directory, so no class can be resolved and no payload can bind what it would do. A terminal call that names no absolute workdir, and a write_file, patch, read_file or search_files call whose path is relative or absent, are refused too (hook-unsupported-execution-context, APRV-415): the live probe found that Hermes resolves both against a per-session recorded directory that a cd moves and that no field of the event carries, so the verdict would bind bytes in a directory the hook cannot see, and the refusal names the retry — an absolute path — so the session repairs itself. A per-entry fail_closed makes a hook crash, timeout or unparseable output BLOCK, which the same probe OBSERVED on Hermes main at 118984d7 in three trials of three, making this the first harness since Claude Code whose hook is a gate rather than a backstop; its default is false and v0.21.3 (build 2026.9.14) ignores the key silently, so docs/hermes-hook.md states the version floor before it states anything else and approval doctor's harness-version row reports a build below it.",
|
|
2494
|
+
human_only: false,
|
|
2495
|
+
human_only_note: "The agent harness surface, so agent-facing by construction: Hermes invokes it around the agent's own tool calls. It proposes and waits; it never records a decision.",
|
|
2496
|
+
input: input({
|
|
2497
|
+
flags: {
|
|
2498
|
+
...AS_FLAG,
|
|
2499
|
+
"--timeout": "string",
|
|
2500
|
+
"--interval": "string",
|
|
2501
|
+
"--retry-grace": "string",
|
|
2502
|
+
"--harness-cap": "string",
|
|
2503
|
+
...POLICY_FLAGS,
|
|
2504
|
+
...LOG_FLAG,
|
|
2505
|
+
...HELP_FLAGS,
|
|
2506
|
+
},
|
|
2507
|
+
}),
|
|
2508
|
+
output: null,
|
|
2509
|
+
error: ERROR_SCHEMA,
|
|
2510
|
+
exit_codes: [
|
|
2511
|
+
{ code: 0, meaning: "an allow ({} on stdout, the reason on stderr), and every post_tool_call report, which exits 0 whatever it found because Hermes reads exit 2 as a block and the call has already run" },
|
|
2512
|
+
{ code: 2, meaning: "a deny: the {action:'block', message} object on stdout is the message and the exit code is the unconditional block under it; also a usage error before any event was read, which prints no verdict" },
|
|
2513
|
+
],
|
|
2514
|
+
},
|
|
2162
2515
|
{
|
|
2163
2516
|
name: "hook",
|
|
2164
2517
|
subcommand: "classify",
|
|
@@ -2180,7 +2533,7 @@ const VERBS = [
|
|
|
2180
2533
|
human_only: false,
|
|
2181
2534
|
input: input({
|
|
2182
2535
|
positionals: positionals([{ name: "file", description: "the markdown file to parse" }], 1),
|
|
2183
|
-
flags: { "--out": "
|
|
2536
|
+
flags: { "--out": "path", ...JSON_FLAG, ...HELP_FLAGS },
|
|
2184
2537
|
}),
|
|
2185
2538
|
output: object({
|
|
2186
2539
|
ok: { const: true },
|
|
@@ -2245,13 +2598,56 @@ const VERBS = [
|
|
|
2245
2598
|
error: ERROR_SCHEMA,
|
|
2246
2599
|
exit_codes: [OK, INTEGRITY, USAGE, IO],
|
|
2247
2600
|
},
|
|
2601
|
+
{
|
|
2602
|
+
name: "codex",
|
|
2603
|
+
subcommand: "apply",
|
|
2604
|
+
purpose: "Apply one bounded typed workspace proposal through the gate (APRV-325.2). The manifest supplies the actor, workspace root, policy and log, and the proposal file supplies only operations and the policy digest it was built against; an unknown key is refused rather than ignored. One action is registered per distinct path class and never collapsed, every leg is authorized and started before any byte moves, and the change is staged, journaled and applied under a workspace lock. The outcome reported is what reading the workspace back proved: applied, not applied, or honestly unknown.",
|
|
2605
|
+
human_only: true,
|
|
2606
|
+
human_only_note: "The broker is reached by a constrained Codex session through `codex serve`, which publishes exactly one tool. Publishing this verb on the broad agent MCP catalog would put a second door beside that one, and the whole point of the strict server is that there is only the one.",
|
|
2607
|
+
input: input({
|
|
2608
|
+
flags: {
|
|
2609
|
+
"--manifest": "string",
|
|
2610
|
+
"--proposal": "string",
|
|
2611
|
+
"--token": "string",
|
|
2612
|
+
"--require-exclusive-custody": "boolean",
|
|
2613
|
+
...JSON_FLAG,
|
|
2614
|
+
...HELP_FLAGS,
|
|
2615
|
+
},
|
|
2616
|
+
}),
|
|
2617
|
+
output: object({
|
|
2618
|
+
ok: { const: true },
|
|
2619
|
+
version: { const: "approval.codex.broker.v1" },
|
|
2620
|
+
task: STRING,
|
|
2621
|
+
payload_hash: SHA256,
|
|
2622
|
+
policy_sha256: SHA256,
|
|
2623
|
+
legs: arrayOf(object({ class: STRING, actionKey: STRING, mode: { enum: ["policy", "token"] } }, ["class", "actionKey", "mode"])),
|
|
2624
|
+
custody: object({ kind: { enum: ["os-exclusive", "advisory"] }, findings: arrayOf(STRING) }, ["kind", "findings"]),
|
|
2625
|
+
state: { const: "after" },
|
|
2626
|
+
}, ["ok", "version", "task", "payload_hash", "policy_sha256", "legs", "custody", "state"]),
|
|
2627
|
+
error: ERROR_SCHEMA,
|
|
2628
|
+
exit_codes: [OK, INTEGRITY, USAGE, IO],
|
|
2629
|
+
},
|
|
2630
|
+
{
|
|
2631
|
+
name: "codex",
|
|
2632
|
+
subcommand: "recover",
|
|
2633
|
+
purpose: "Read a workspace's retained transaction journal and report whether the workspace is in the approved before-state, the approved after-state, or neither. It changes nothing: a mixed workspace is a person's to reconcile, and a recovery that rolled one either way would be guessing which half was approved. Exits 1 on mixed.",
|
|
2634
|
+
human_only: true,
|
|
2635
|
+
human_only_note: "Reading a half-applied workspace is an operator's diagnosis and belongs beside the reconcile verb, which is human-only for the same reason.",
|
|
2636
|
+
input: input({ flags: { "--manifest": "string", ...JSON_FLAG, ...HELP_FLAGS } }),
|
|
2637
|
+
output: null,
|
|
2638
|
+
error: ERROR_SCHEMA,
|
|
2639
|
+
exit_codes: [OK, INTEGRITY, USAGE, IO],
|
|
2640
|
+
},
|
|
2248
2641
|
{
|
|
2249
2642
|
name: "codex",
|
|
2250
2643
|
subcommand: "start",
|
|
2251
|
-
purpose: "
|
|
2644
|
+
purpose: "Prepare a confined Codex session and, with `-- <command>`, run something inside it (APRV-325.3). The shell gets a disposable workspace that is the ONLY path it may write; the canonical workspace is readable and never writable; reads are jailed to exactly those two roots, so the gate home and everything else the host holds are unreadable; the environment is an allow-list rather than a filtered copy of the operator's; and outbound network is denied with loopback. There is no opt-out flag and no unwrapped fallback: a host with no sandbox mechanism refuses rather than running the shell and calling it confined. With no `-- <command>` it reports the room and runs nothing. Its exit code is the child's.",
|
|
2252
2645
|
human_only: true,
|
|
2253
2646
|
human_only_note: "Starting a constrained host session is an operator action and is absent from broad MCP.",
|
|
2254
|
-
input: input({
|
|
2647
|
+
input: input({
|
|
2648
|
+
flags: { "--manifest": "string", "--timeout": "string", ...JSON_FLAG, ...HELP_FLAGS },
|
|
2649
|
+
trailing: TRAILING,
|
|
2650
|
+
}),
|
|
2255
2651
|
output: null,
|
|
2256
2652
|
error: ERROR_SCHEMA,
|
|
2257
2653
|
exit_codes: [OK, INTEGRITY, USAGE],
|
|
@@ -2259,13 +2655,37 @@ const VERBS = [
|
|
|
2259
2655
|
{
|
|
2260
2656
|
name: "codex",
|
|
2261
2657
|
subcommand: "serve",
|
|
2262
|
-
purpose: "
|
|
2658
|
+
purpose: "Serve the workspace broker over stdio as EXACTLY ONE MCP tool, `codex_workspace_apply`, with a positive server-side allowlist checked at call time as well as at list time. Distinct from `mcp serve`, whose catalog is this whole registry: a constrained session must reach one door and the same door next month. The published input schema carries no identity, path, class, token or sandbox argument, because none exists to remove.",
|
|
2263
2659
|
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 } }),
|
|
2660
|
+
human_only_note: "The strict server is separate from and never published by the broad MCP server; starting one is an operator's act, as `mcp serve` is.",
|
|
2661
|
+
input: input({ flags: { "--manifest": "string", "--require-exclusive-custody": "boolean", ...JSON_FLAG, ...HELP_FLAGS } }),
|
|
2266
2662
|
output: null,
|
|
2267
2663
|
error: ERROR_SCHEMA,
|
|
2268
|
-
exit_codes: [OK, INTEGRITY, USAGE],
|
|
2664
|
+
exit_codes: [OK, INTEGRITY, USAGE, IO],
|
|
2665
|
+
},
|
|
2666
|
+
{
|
|
2667
|
+
name: "codex",
|
|
2668
|
+
subcommand: "bridge",
|
|
2669
|
+
purpose: "Start `codex app-server` and answer every approval request it raises through the policy and the log (APRV-361). Each exec request carries `command` and `cwd` on one frame, minted by the harness runtime, which is the pair the native hook lacks and refuses for want of; the request goes through the hook's own decision path — classify, the human-only refusal, the loop floor, register, request, wait on the verified view — and the answer is `{id, result: {decision}}` on the connection. Accept or decline only, in the vocabulary the request advertised, never `acceptForSession`, `cancel` or `abort`. The deadline is the policy's `approval_ttl`, because this transport has no timeout. An ADVISORY checkpoint and never a boundary: see docs/codex-app-server-bridge.md.",
|
|
2670
|
+
human_only: true,
|
|
2671
|
+
human_only_note: "An OPERATOR process, like `codex serve`: it launches a long-lived server, holds the acting identity every answer is recorded under, and decides questions on a connection this transport owns. An agent that could start one would gain a second writer against the log nobody supervises.",
|
|
2672
|
+
input: input({
|
|
2673
|
+
flags: {
|
|
2674
|
+
"--prompt": "string",
|
|
2675
|
+
"--workspace": "string",
|
|
2676
|
+
...AS_FLAG,
|
|
2677
|
+
...POLICY_FLAGS,
|
|
2678
|
+
...LOG_FLAG,
|
|
2679
|
+
"--wait": "string",
|
|
2680
|
+
"--interval": "string",
|
|
2681
|
+
...JSON_FLAG,
|
|
2682
|
+
...HELP_FLAGS,
|
|
2683
|
+
},
|
|
2684
|
+
trailing: TRAILING,
|
|
2685
|
+
}),
|
|
2686
|
+
output: null,
|
|
2687
|
+
error: ERROR_SCHEMA,
|
|
2688
|
+
exit_codes: [OK, USAGE, IO],
|
|
2269
2689
|
},
|
|
2270
2690
|
{
|
|
2271
2691
|
name: "mcp",
|
|
@@ -2294,6 +2714,49 @@ const VERBS = [
|
|
|
2294
2714
|
{ code: 4, meaning: "the transport did not close cleanly" },
|
|
2295
2715
|
],
|
|
2296
2716
|
},
|
|
2717
|
+
{
|
|
2718
|
+
name: "serve",
|
|
2719
|
+
purpose: "Serve the verbs of this registry over HTTP, in the foreground, for a harness in a sandbox that has no local log and no local policy. The published surface is `mcp serve`'s exactly — the registry filtered by human_only false, `--as` deleted from every schema, the launch identity appended last, no grant — plus the three things the MCP transport withheld for transport reasons: `hook/<harness>` taking the harness envelope as the request body and answering the bytes the stdin form prints, `log/follow` paged by an exclusive (seq, hash) cursor, and `export`, the store as an archive carrying the policy, the log, the payload bytes behind every payload_hash and the projections, and never the keys, the environment source map or the vault. TWO bearer credentials come from the launch environment and authorization is PER VERB: the agent one opens the hook and an allowlist of what a harness under oversight needs in order to ask and to act on a grant, the tenant one opens everything else, so the agent never reads the log it is judged by and a verb added to this registry is the tenant's until somebody widens the list. Binds loopback unless an explicit flag widens it; it terminates no TLS.",
|
|
2720
|
+
human_only: true,
|
|
2721
|
+
human_only_note: "An OPERATOR process, for `mcp serve`'s reason and one more. It is long-lived, launched by a person, and holds the agent identity every call is recorded under; on top of that it holds BOTH credentials, and the party that decides which door an agent gets is not the agent.",
|
|
2722
|
+
input: input({
|
|
2723
|
+
flags: {
|
|
2724
|
+
...AS_FLAG,
|
|
2725
|
+
"--port": "string",
|
|
2726
|
+
"--listen": "string",
|
|
2727
|
+
"--allow-non-loopback": "boolean",
|
|
2728
|
+
"--hook-timeout": "string",
|
|
2729
|
+
"--hook-harness-cap": "string",
|
|
2730
|
+
"--hook-threads": "string",
|
|
2731
|
+
"--hook-queue": "string",
|
|
2732
|
+
...POLICY_FLAGS,
|
|
2733
|
+
...LOG_FLAG,
|
|
2734
|
+
...JSON_FLAG,
|
|
2735
|
+
...HELP_FLAGS,
|
|
2736
|
+
},
|
|
2737
|
+
}),
|
|
2738
|
+
output: null,
|
|
2739
|
+
error: ERROR_SCHEMA,
|
|
2740
|
+
exit_codes: [
|
|
2741
|
+
{ code: 0, meaning: "the server was interrupted and closed cleanly" },
|
|
2742
|
+
USAGE,
|
|
2743
|
+
{ code: 4, meaning: "the listener could not bind, or did not close cleanly" },
|
|
2744
|
+
],
|
|
2745
|
+
},
|
|
2746
|
+
{
|
|
2747
|
+
name: "muse",
|
|
2748
|
+
purpose: "Start the single-tenant local Muse consumer prototype. It exposes bounded register and request proposals plus a verified pending inbox, status and canonical request detail, with distinct read and proposal launch credentials. It has no human decision, token, execution, export or generic verb route; the configured Telegram channel remains the human decision path.",
|
|
2749
|
+
human_only: true,
|
|
2750
|
+
human_only_note: "An operator launches this credential-holding listener and pins its tenant and store before accepting calls.",
|
|
2751
|
+
input: input({ flags: { "--dir": "path", "--port": "string", ...HELP_FLAGS } }),
|
|
2752
|
+
output: null,
|
|
2753
|
+
error: ERROR_SCHEMA,
|
|
2754
|
+
exit_codes: [
|
|
2755
|
+
{ code: 0, meaning: "the local listener was interrupted and closed cleanly" },
|
|
2756
|
+
USAGE,
|
|
2757
|
+
{ code: 4, meaning: "the local listener could not bind" },
|
|
2758
|
+
],
|
|
2759
|
+
},
|
|
2297
2760
|
{
|
|
2298
2761
|
name: "reindex",
|
|
2299
2762
|
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.",
|
|
@@ -2301,7 +2764,7 @@ const VERBS = [
|
|
|
2301
2764
|
input: input({
|
|
2302
2765
|
flags: {
|
|
2303
2766
|
...LOG_FLAG,
|
|
2304
|
-
"--index": "
|
|
2767
|
+
"--index": "path",
|
|
2305
2768
|
"--force": "boolean",
|
|
2306
2769
|
...JSON_FLAG,
|
|
2307
2770
|
...HELP_FLAGS,
|
|
@@ -2316,7 +2779,7 @@ const VERBS = [
|
|
|
2316
2779
|
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
2780
|
human_only: false,
|
|
2318
2781
|
input: input({
|
|
2319
|
-
flags: { ...LOG_FLAG, "--out": "
|
|
2782
|
+
flags: { ...LOG_FLAG, "--out": "path", ...POLICY_FLAGS, ...JSON_FLAG, ...HELP_FLAGS },
|
|
2320
2783
|
}),
|
|
2321
2784
|
output: object({
|
|
2322
2785
|
ok: { const: true },
|