approval-md 0.1.0 → 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/README.md +584 -553
- package/SPEC.md +42 -13
- package/dist/src/adapters/agentmail.d.ts +426 -0
- package/dist/src/adapters/agentmail.js +2 -2
- package/dist/src/adapters/conformance.d.ts +149 -0
- package/dist/src/adapters/contract.d.ts +628 -0
- package/dist/src/adapters/contract.js +110 -16
- package/dist/src/adapters/contract.js.map +1 -1
- package/dist/src/adapters/email.d.ts +324 -0
- package/dist/src/adapters/env-passphrase.d.ts +93 -0
- package/dist/src/adapters/public.d.ts +11 -0
- package/dist/src/adapters/public.js +11 -0
- package/dist/src/adapters/public.js.map +1 -0
- package/dist/src/adapters/registry.d.ts +59 -0
- package/dist/src/adapters/registry.js +2 -1
- package/dist/src/adapters/registry.js.map +1 -1
- package/dist/src/adapters/smtp.d.ts +213 -0
- package/dist/src/adapters/vault-provider.d.ts +114 -0
- package/dist/src/adapters/vault-provider.js +3 -3
- package/dist/src/adapters/zzz.d.ts +66 -0
- package/dist/src/adapters/zzz.js +299 -0
- package/dist/src/adapters/zzz.js.map +1 -0
- package/dist/src/channels/batch.d.ts +109 -0
- package/dist/src/channels/cli.d.ts +193 -0
- package/dist/src/channels/conformance.d.ts +92 -0
- package/dist/src/channels/contract.d.ts +623 -0
- package/dist/src/channels/payload-view.d.ts +35 -0
- package/dist/src/channels/render-queue.d.ts +149 -0
- package/dist/src/channels/tagging.d.ts +196 -0
- package/dist/src/channels/telegram.d.ts +1832 -0
- package/dist/src/channels/web.d.ts +341 -0
- package/dist/src/cli/adapter.d.ts +90 -0
- package/dist/src/cli/adapter.js +25 -15
- package/dist/src/cli/adapter.js.map +1 -1
- package/dist/src/cli/amend.d.ts +59 -0
- package/dist/src/cli/args.d.ts +43 -0
- package/dist/src/cli/attest.d.ts +41 -0
- package/dist/src/cli/audit-card.d.ts +62 -0
- package/dist/src/cli/audit.d.ts +59 -0
- package/dist/src/cli/channel-telegram.d.ts +806 -0
- package/dist/src/cli/channel-web.d.ts +131 -0
- package/dist/src/cli/channel.d.ts +71 -0
- package/dist/src/cli/checkpoint-tap.d.ts +169 -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/daemon.d.ts +120 -0
- package/dist/src/cli/doctor.d.ts +129 -0
- package/dist/src/cli/doctor.js +119 -5
- package/dist/src/cli/doctor.js.map +1 -1
- package/dist/src/cli/env.d.ts +65 -0
- package/dist/src/cli/execute.d.ts +202 -0
- package/dist/src/cli/exit-codes.d.ts +73 -0
- package/dist/src/cli/feedback.d.ts +60 -0
- package/dist/src/cli/gate-window.d.ts +40 -0
- package/dist/src/cli/gate.d.ts +68 -0
- package/dist/src/cli/git-scope.d.ts +190 -0
- package/dist/src/cli/gloss-attach.d.ts +85 -0
- package/dist/src/cli/gloss-codex-child.d.ts +9 -0
- package/dist/src/cli/gloss-codex.d.ts +24 -0
- package/dist/src/cli/gloss-options.d.ts +42 -0
- package/dist/src/cli/gloss.d.ts +265 -0
- package/dist/src/cli/help.d.ts +103 -0
- package/dist/src/cli/help.js +173 -51
- package/dist/src/cli/help.js.map +1 -1
- 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 +186 -80
- package/dist/src/cli/hook.js.map +1 -1
- package/dist/src/cli/import.d.ts +35 -0
- package/dist/src/cli/init.d.ts +84 -0
- package/dist/src/cli/init.js +2 -2
- package/dist/src/cli/init.js.map +1 -1
- package/dist/src/cli/instructions.d.ts +23 -0
- package/dist/src/cli/journal.d.ts +41 -0
- package/dist/src/cli/log-advance.d.ts +287 -0
- package/dist/src/cli/log-advance.js +102 -11
- package/dist/src/cli/log-advance.js.map +1 -1
- package/dist/src/cli/log-anchor.d.ts +176 -0
- package/dist/src/cli/log-checkpoint.d.ts +22 -0
- package/dist/src/cli/log-sync.d.ts +243 -0
- package/dist/src/cli/log-verbs.d.ts +16 -0
- package/dist/src/cli/log-verbs.js +7 -1
- package/dist/src/cli/log-verbs.js.map +1 -1
- package/dist/src/cli/long-help.d.ts +70 -0
- package/dist/src/cli/main.d.ts +77 -0
- package/dist/src/cli/main.js +155 -5
- package/dist/src/cli/main.js.map +1 -1
- package/dist/src/cli/mcp.d.ts +52 -0
- package/dist/src/cli/paths.d.ts +56 -0
- package/dist/src/cli/payload.d.ts +58 -0
- package/dist/src/cli/policy.d.ts +43 -0
- package/dist/src/cli/preflight.d.ts +363 -0
- package/dist/src/cli/preflight.js +294 -7
- package/dist/src/cli/preflight.js.map +1 -1
- package/dist/src/cli/progress.d.ts +78 -0
- package/dist/src/cli/prompt.d.ts +209 -0
- package/dist/src/cli/quickstart.d.ts +46 -0
- package/dist/src/cli/quickstart.js +297 -0
- package/dist/src/cli/quickstart.js.map +1 -0
- package/dist/src/cli/records.d.ts +34 -0
- package/dist/src/cli/render.d.ts +22 -0
- package/dist/src/cli/sandbox.d.ts +51 -0
- package/dist/src/cli/scaffold.d.ts +79 -0
- package/dist/src/cli/setup-adapter.d.ts +137 -0
- package/dist/src/cli/setup-adapter.js +38 -4
- package/dist/src/cli/setup-adapter.js.map +1 -1
- package/dist/src/cli/setup-channel.d.ts +117 -0
- package/dist/src/cli/setup-checkpoint.d.ts +57 -0
- package/dist/src/cli/setup-common.d.ts +275 -0
- package/dist/src/cli/setup-flow.d.ts +287 -0
- package/dist/src/cli/setup-service.d.ts +96 -0
- package/dist/src/cli/setup.d.ts +202 -0
- package/dist/src/cli/style.d.ts +320 -0
- package/dist/src/cli/token.d.ts +39 -0
- package/dist/src/cli/up.d.ts +155 -0
- package/dist/src/cli/up.js +4 -2
- package/dist/src/cli/up.js.map +1 -1
- package/dist/src/cli/usage.d.ts +37 -0
- package/dist/src/cli/values.d.ts +40 -0
- package/dist/src/cli/vault.d.ts +59 -0
- package/dist/src/cli/vault.js +2 -2
- package/dist/src/cli/vault.js.map +1 -1
- package/dist/src/cli/verb-registry.d.ts +76 -0
- package/dist/src/cli/verb-registry.js +176 -8
- package/dist/src/cli/verb-registry.js.map +1 -1
- package/dist/src/cli/wordmark.d.ts +31 -0
- package/dist/src/cli/wordmark.js +2 -2
- package/dist/src/codex/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/agents-md.d.ts +276 -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 +13 -1
- package/dist/src/core/attest.js.map +1 -1
- package/dist/src/core/audit.d.ts +492 -0
- package/dist/src/core/budgets.d.ts +238 -0
- package/dist/src/core/checkpoint.d.ts +500 -0
- package/dist/src/core/child-env.d.ts +88 -0
- package/dist/src/core/clock.d.ts +52 -0
- package/dist/src/core/command-class.d.ts +543 -0
- package/dist/src/core/command-class.js +43 -8
- package/dist/src/core/command-class.js.map +1 -1
- package/dist/src/core/coverage-sources/adapter.d.ts +40 -0
- package/dist/src/core/coverage-sources/gh.d.ts +48 -0
- package/dist/src/core/coverage-sources/git.d.ts +101 -0
- package/dist/src/core/coverage.d.ts +217 -0
- package/dist/src/core/credential-spec.d.ts +72 -0
- package/dist/src/core/dark-session.d.ts +331 -0
- package/dist/src/core/decision-refusal.d.ts +185 -0
- package/dist/src/core/env-file.d.ts +450 -0
- package/dist/src/core/execute.d.ts +858 -0
- package/dist/src/core/execute.js +44 -6
- package/dist/src/core/execute.js.map +1 -1
- package/dist/src/core/frontmatter.d.ts +78 -0
- package/dist/src/core/gate-window.d.ts +312 -0
- package/dist/src/core/gate.d.ts +1364 -0
- package/dist/src/core/gate.js +68 -13
- package/dist/src/core/gate.js.map +1 -1
- package/dist/src/core/git-run.d.ts +73 -0
- package/dist/src/core/harness-version.d.ts +157 -0
- package/dist/src/core/harness-version.js +2 -1
- package/dist/src/core/harness-version.js.map +1 -1
- package/dist/src/core/harness-wait.d.ts +55 -0
- package/dist/src/core/head-retry.d.ts +107 -0
- package/dist/src/core/instance.d.ts +253 -0
- package/dist/src/core/intake-limits.d.ts +247 -0
- package/dist/src/core/jcs.d.ts +52 -0
- package/dist/src/core/journal.d.ts +144 -0
- package/dist/src/core/live-draw.d.ts +436 -0
- package/dist/src/core/log-reconcile.d.ts +89 -0
- package/dist/src/core/log-subscribe.d.ts +36 -0
- package/dist/src/core/log-subscribe.js +162 -0
- package/dist/src/core/log-subscribe.js.map +1 -0
- package/dist/src/core/log.d.ts +278 -0
- package/dist/src/core/loop.d.ts +274 -0
- package/dist/src/core/loop.js +11 -0
- package/dist/src/core/loop.js.map +1 -1
- package/dist/src/core/md-fence.d.ts +41 -0
- package/dist/src/core/money.d.ts +147 -0
- package/dist/src/core/payload-census.d.ts +74 -0
- package/dist/src/core/payload-store.d.ts +175 -0
- package/dist/src/core/payload.d.ts +71 -0
- package/dist/src/core/policy-diff.d.ts +292 -0
- package/dist/src/core/policy-diff.js +27 -4
- package/dist/src/core/policy-diff.js.map +1 -1
- package/dist/src/core/policy-expectations.d.ts +199 -0
- package/dist/src/core/policy-explain.d.ts +150 -0
- package/dist/src/core/policy-explain.js +31 -3
- package/dist/src/core/policy-explain.js.map +1 -1
- package/dist/src/core/policy-load.d.ts +527 -0
- package/dist/src/core/policy-load.js +15 -3
- package/dist/src/core/policy-load.js.map +1 -1
- package/dist/src/core/policy-match.d.ts +281 -0
- package/dist/src/core/policy-match.js +20 -9
- package/dist/src/core/policy-match.js.map +1 -1
- package/dist/src/core/policy-proposal.d.ts +265 -0
- package/dist/src/core/prompt-layout.d.ts +221 -0
- package/dist/src/core/protected-path-guard.d.ts +453 -0
- package/dist/src/core/protected-path-guard.js +514 -35
- package/dist/src/core/protected-path-guard.js.map +1 -1
- package/dist/src/core/registration.d.ts +25 -0
- package/dist/src/core/reindex.d.ts +99 -0
- package/dist/src/core/sampler.d.ts +313 -0
- package/dist/src/core/sandbox.d.ts +290 -0
- package/dist/src/core/seal.d.ts +165 -0
- package/dist/src/core/state.d.ts +505 -0
- package/dist/src/core/task-file.d.ts +185 -0
- package/dist/src/core/telegram-config.d.ts +93 -0
- package/dist/src/core/token.d.ts +409 -0
- package/dist/src/core/token.js +21 -38
- package/dist/src/core/token.js.map +1 -1
- package/dist/src/core/validate.d.ts +138 -0
- package/dist/src/core/values.d.ts +137 -0
- package/dist/src/core/vault.d.ts +291 -0
- package/dist/src/core/verified-snapshot.d.ts +204 -0
- package/dist/src/core/verify.d.ts +336 -0
- package/dist/src/core/version.d.ts +8 -0
- package/dist/src/core/wysiwys.d.ts +370 -0
- package/dist/src/daemon/advance-child.d.ts +39 -0
- package/dist/src/daemon/advance.d.ts +466 -0
- package/dist/src/daemon/audit.d.ts +87 -0
- package/dist/src/daemon/daemon.d.ts +1180 -0
- package/dist/src/daemon/dark-session.d.ts +64 -0
- package/dist/src/daemon/draw-child.d.ts +36 -0
- package/dist/src/daemon/draw.d.ts +154 -0
- package/dist/src/daemon/git-evidence.d.ts +173 -0
- package/dist/src/daemon/git-evidence.js +1 -1
- package/dist/src/daemon/projection.d.ts +180 -0
- package/dist/src/daemon/prune.d.ts +207 -0
- package/dist/src/mcp/http.d.ts +113 -0
- package/dist/src/mcp/server.d.ts +265 -0
- package/dist/src/mcp/server.js +9 -1
- package/dist/src/mcp/server.js.map +1 -1
- package/docs/adapter-api.md +106 -0
- package/docs/cli-reference.md +389 -36
- package/docs/codex-enforced-session.md +30 -0
- package/package.json +12 -2
- package/schema/codex-instance.schema.json +82 -0
- package/schema/event.schema.json +2 -1
- package/schema/fixtures/codex-instance/invalid/unpinned-codex-version.json +40 -0
- package/schema/fixtures/codex-instance/valid/canonical.json +40 -0
- package/schema/policy.schema.json +21 -1
- package/templates/codex/README.md +9 -0
package/docs/cli-reference.md
CHANGED
|
@@ -14,6 +14,10 @@ a gate refusal is exit 1 and not 2, approval events are exclusive to the manual
|
|
|
14
14
|
path, the raw token is shown once, a channel is transport — are stated once at
|
|
15
15
|
the top of `approval --help` and are not repeated here.
|
|
16
16
|
|
|
17
|
+
`approval --version`, `approval -v`, and `approval version` print the package
|
|
18
|
+
version and exit 0. These aliases apply only at the top level; a version-looking
|
|
19
|
+
flag after a verb remains that verb's argument.
|
|
20
|
+
|
|
17
21
|
Each section below is what the corresponding `--help` points at with its
|
|
18
22
|
`why: docs/cli-reference.md#…` footer.
|
|
19
23
|
|
|
@@ -35,11 +39,12 @@ resolved, and nothing is written.
|
|
|
35
39
|
|
|
36
40
|
## log
|
|
37
41
|
|
|
38
|
-
|
|
42
|
+
Four subcommands open the log for reading only. `verify` walks the hash chain
|
|
39
43
|
end to end and reports clean | torn-tail | corrupt, `tail` prints the last N
|
|
40
|
-
records (default 10),
|
|
41
|
-
byte
|
|
42
|
-
|
|
44
|
+
records (default 10), `export` streams every stored line to stdout byte for
|
|
45
|
+
byte, and `follow` emits verified records after an exclusive cursor before
|
|
46
|
+
waiting for appends. The default log path is `.approval/log/events.jsonl`,
|
|
47
|
+
relative to the working directory.
|
|
43
48
|
|
|
44
49
|
## log verify
|
|
45
50
|
|
|
@@ -297,6 +302,47 @@ that is due to a refusal of anything: it is a warning on `log verify`, a
|
|
|
297
302
|
doctor`'s `checkpoint` row. A gate that held up an action for want of a tap is a
|
|
298
303
|
gate whose operator turns the check off.
|
|
299
304
|
|
|
305
|
+
## log follow
|
|
306
|
+
|
|
307
|
+
`approval log follow --from <seq> --json` is the channel-independent decision
|
|
308
|
+
listener. `--from` is exclusive and defaults to zero, so a new consumer first
|
|
309
|
+
receives the entire verified log. Output is JSON Lines: one complete stored
|
|
310
|
+
event object per line. It emits every event type in chain order; a refund or
|
|
311
|
+
queue consumer selects the decisions relevant to its own work. The command then
|
|
312
|
+
remains in the foreground and exits 0 on SIGINT, SIGTERM, or a closed downstream
|
|
313
|
+
pipe. Signal cancellation may interrupt the final native stdout write. Consumers
|
|
314
|
+
must process only newline-terminated JSON records, discard any incomplete final
|
|
315
|
+
fragment, and reconnect from the cursor of the last complete record they
|
|
316
|
+
processed.
|
|
317
|
+
|
|
318
|
+
Filesystem notifications only prompt another read. On every notification wake
|
|
319
|
+
and every bounded 500ms fallback poll, the runtime rereads and verifies the
|
|
320
|
+
complete chain from genesis through the observed head, even when the log has not
|
|
321
|
+
changed. It emits nothing from a corrupt, torn, unreadable, truncated, or
|
|
322
|
+
cursor-mismatched snapshot. Pull backpressure means a slow consumer holds one
|
|
323
|
+
verified snapshot and no growing notification or event queue. Each check costs
|
|
324
|
+
O(N) verification time and O(N) snapshot memory for a log of N records. This
|
|
325
|
+
bounded initial implementation is not an incremental, low-overhead tail.
|
|
326
|
+
|
|
327
|
+
`--cursor-hash <64hex>` supplies the hash of record `--from`. Store both the
|
|
328
|
+
sequence and hash outside the log and pass both on reconnect. That binding
|
|
329
|
+
detects a truncated or replaced prefix. A sequence alone is a weaker bootstrap:
|
|
330
|
+
an internally valid rewritten prefix with the same sequence numbers cannot be
|
|
331
|
+
distinguished from the original, for the same reason an unanchored hash chain
|
|
332
|
+
cannot detect a fully recomputed forgery.
|
|
333
|
+
|
|
334
|
+
Delivery across reconnects is at least once. Apply the external effect first,
|
|
335
|
+
then persist the emitted event's `seq` and `hash`; a crash between those steps
|
|
336
|
+
replays the event. Consumers that require exactly-once external effects must
|
|
337
|
+
make their own effect idempotent or transact it with cursor storage. Persisting
|
|
338
|
+
the cursor before the effect instead risks silently losing that effect.
|
|
339
|
+
|
|
340
|
+
The stream uses the existing exit classes: 1 for corrupt or mismatched cursor,
|
|
341
|
+
2 for usage, 3 for a torn tail, and 4 for I/O. Its error object is written to
|
|
342
|
+
stderr and no event from the refused batch is written to stdout. `log follow`
|
|
343
|
+
is deliberately absent from MCP because one unbounded call would occupy the
|
|
344
|
+
finite MCP call queue; run it as a separate CLI process.
|
|
345
|
+
|
|
300
346
|
## log tail
|
|
301
347
|
|
|
302
348
|
The chain is verified first. On a torn tail the intact records are printed and the
|
|
@@ -439,6 +485,20 @@ range they cover, and pushed to a short-lived records branch that exists for
|
|
|
439
485
|
exactly that commit. Main is protected here, so the commit reaches it through a
|
|
440
486
|
pull request; `--pr` opens that pull request through the ordinary `gh` path.
|
|
441
487
|
|
|
488
|
+
`--co-author "Name <email>"` adds one validated `Co-authored-by` trailer to the
|
|
489
|
+
generated records commit and to the pull request body. When the day's pull
|
|
490
|
+
request already exists, the verb preserves its body and adds the trailer once.
|
|
491
|
+
This is display credit only. It does not set an event actor, name an approver,
|
|
492
|
+
grant authority, or derive an identity from the log. Omitting the flag preserves
|
|
493
|
+
the existing commit message, pull request body, and merge command byte for byte.
|
|
494
|
+
|
|
495
|
+
The merge queue ignored the custom auto-merge commit body observed on PR 378.
|
|
496
|
+
For a queued merge commit to retain this credit, the repository must use GitHub's
|
|
497
|
+
PR-body merge-message setting (`merge_commit_message=PR_BODY`); the package does
|
|
498
|
+
not change repository settings. The PR body is therefore the durable source the
|
|
499
|
+
queue can copy, rather than a claim that `gh pr merge --body` controls the final
|
|
500
|
+
queued merge.
|
|
501
|
+
|
|
442
502
|
**You do not fetch or reset first (APRV-203).** The verb owns its own git
|
|
443
503
|
preconditions: it fetches the base branch (the one you are standing on, or
|
|
444
504
|
`--base <name>`), builds the commit on `origin/<base>` in a scratch index rather
|
|
@@ -536,7 +596,8 @@ is not manual:
|
|
|
536
596
|
policy was read and understood, and it says ask.
|
|
537
597
|
- `irreversibility-floor` — policy granted autonomous or supervised and SPEC §7's
|
|
538
598
|
floor overrode it because `--reversible false` was given. `overridden` records
|
|
539
|
-
what policy actually said.
|
|
599
|
+
what policy actually said. The floor remains the default when a class rule
|
|
600
|
+
omits `allow_irreversible` or writes `false`.
|
|
540
601
|
- `load-failure` — the policy could not be loaded at all, so every class is
|
|
541
602
|
manual. `loadFailure` carries a code and a message.
|
|
542
603
|
|
|
@@ -561,14 +622,26 @@ The exit codes, at length. `policy check|test` uses only 0, 2 and 4:
|
|
|
561
622
|
key may contain, never something an agent can do.
|
|
562
623
|
|
|
563
624
|
`--reversible` takes an explicit value because "unstated", "reversible" and
|
|
564
|
-
"irreversible" are three different questions.
|
|
565
|
-
|
|
625
|
+
"irreversible" are three different questions. Explicit `false` asks for the
|
|
626
|
+
effective irreversible answer. It resolves to `manual` by default. A nonmanual
|
|
627
|
+
class retains its autonomy only when every equally most-specific matching rule
|
|
628
|
+
sets `allow_irreversible: true`. Lower-specificity rules do not vote, defaults
|
|
629
|
+
cannot opt in, and neither this flag nor any other action metadata can create
|
|
630
|
+
the permission. Manual remains manual and human-only remains human-only.
|
|
631
|
+
|
|
632
|
+
For an allowed `supervised-live` class, the existing live sampler decides
|
|
633
|
+
whether this action prompts before execution. Allowed `supervised` and
|
|
634
|
+
`supervised-retro` actions execute first and remain eligible for retrospective
|
|
635
|
+
review. Allowed autonomous actions proceed without either review path. Telegram
|
|
636
|
+
appears only when the effective path actually requests approval through that
|
|
637
|
+
channel; policy-authorized execution is never represented as a human grant.
|
|
566
638
|
|
|
567
639
|
**`--json`** (one object on stdout):
|
|
568
640
|
|
|
569
641
|
```
|
|
570
642
|
{"class":"vcs.push.main","reversible":null,
|
|
571
|
-
"outcome":{"autonomy":"supervised","approvers":null,"limits":null
|
|
643
|
+
"outcome":{"autonomy":"supervised","approvers":null,"limits":null,
|
|
644
|
+
"allowIrreversible":false},
|
|
572
645
|
"provenance":"rule"|"default"|"inherited"|"fail-closed"|"floor",
|
|
573
646
|
"manualBecause":null|"matched-rule"|"irreversibility-floor"|"load-failure",
|
|
574
647
|
"loadFailure":null|{"code":"file-missing"|"no-block"|"multiple-blocks"|
|
|
@@ -576,6 +649,9 @@ SPEC §7's irreversibility floor.
|
|
|
576
649
|
"message":"..."},
|
|
577
650
|
"matched":null|{"pattern":"vcs.push.main","rule":{"autonomy":"supervised"}},
|
|
578
651
|
"overridden":null|{"pattern":"read.web"|null,"autonomy":"autonomous"},
|
|
652
|
+
"irreversibility":"not-applicable"|"policy-allowed"|"floor-applied"|
|
|
653
|
+
"already-manual"|"human-only",
|
|
654
|
+
"irreversiblePatterns":["read.*"],
|
|
579
655
|
"candidates":[{"pattern":"read.*","specificity":[1,1,2],
|
|
580
656
|
"autonomy":"autonomous","winner":true,
|
|
581
657
|
"tieBreak":"specificity"|"strictest-autonomy"|
|
|
@@ -1155,6 +1231,13 @@ hash must equal the declared `payload_hash` and it is filed in
|
|
|
1155
1231
|
bytes from. Supply it here once and no channel needs `--payload-dir` or
|
|
1156
1232
|
`--payloads` at all.
|
|
1157
1233
|
|
|
1234
|
+
When the policy permits an unattended action, supplied material is still checked
|
|
1235
|
+
against the registered action's task, class and payload hash and retained before
|
|
1236
|
+
`proceed:true` is returned. This creates no approval event and does not require
|
|
1237
|
+
an extra human grant. The retained bytes let later audits match the execution
|
|
1238
|
+
record to the actual edit. Missing bindings, mismatched material or a storage
|
|
1239
|
+
failure refuse the request; an existing valid payload is preserved.
|
|
1240
|
+
|
|
1158
1241
|
**`--json`** (one object on stdout):
|
|
1159
1242
|
|
|
1160
1243
|
```
|
|
@@ -2529,6 +2612,16 @@ The checks, at length:
|
|
|
2529
2612
|
where there is nothing to commit a key to. Neither fix deletes nor commits:
|
|
2530
2613
|
`git rm --cached` for a key already in the index is named in the prose and
|
|
2531
2614
|
left to you, along with revoking every action whose token is still unspent.
|
|
2615
|
+
- **codex-hook-wiring** — whether this checkout's `.codex/hooks.json` carries
|
|
2616
|
+
the reviewed approval.md profile for both `PreToolUse` and `PostToolUse`: the
|
|
2617
|
+
exact `Bash|apply_patch` matcher, a direct synchronous `approval hook codex`
|
|
2618
|
+
command, and a `600` second outer timeout. A PASS establishes only those JSON
|
|
2619
|
+
bytes on disk. Codex trust, loading, and observed execution remain separate
|
|
2620
|
+
facts checked through `/hooks` and the bounded smoke test. TOML-only hook
|
|
2621
|
+
configuration, or JSON combined with `.codex/config.toml`, SKIPS because
|
|
2622
|
+
doctor does not interpret or merge the TOML hook tables. Malformed JSON
|
|
2623
|
+
FAILS; a different valid Codex hook profile SKIPS as undetermined rather than
|
|
2624
|
+
being called broken.
|
|
2532
2625
|
|
|
2533
2626
|
**`--json`** (one object on stdout):
|
|
2534
2627
|
|
|
@@ -2684,9 +2777,10 @@ and cannot do. It selects the SHAPE of an obligation that exists either way; it
|
|
|
2684
2777
|
cannot remove one, delay one, or decide whether the denial happened. The only
|
|
2685
2778
|
thing a false `reversible: true` buys is the shape whose discharge this runtime
|
|
2686
2779
|
checks against the chain, which makes the claimant's own exit harder rather than
|
|
2687
|
-
easier. The same reading applies to the irreversibility floor
|
|
2688
|
-
`reversible: false` action out of `supervised-retro
|
|
2689
|
-
|
|
2780
|
+
easier. The same reading applies to the irreversibility floor. By default it
|
|
2781
|
+
keeps a `reversible: false` action out of `supervised-retro`; an attested class
|
|
2782
|
+
rule may explicitly accept that consequence with `allow_irreversible: true`.
|
|
2783
|
+
The field acts on the acting party's own claim, so it catches the honest
|
|
2690
2784
|
declaration and never the lie. What answers the lie is writing `manual` for the
|
|
2691
2785
|
class, which no declaration can loosen.
|
|
2692
2786
|
|
|
@@ -3192,6 +3286,11 @@ would put a bot token into a shell history or a process listing.
|
|
|
3192
3286
|
|
|
3193
3287
|
## channel telegram listen
|
|
3194
3288
|
|
|
3289
|
+
This starts the standalone Telegram component. For normal operation, use
|
|
3290
|
+
[`approval up`](#up), which also runs the daemon. Do not run this listener beside
|
|
3291
|
+
`up` or another listener polling the same bot, even for a different policy
|
|
3292
|
+
project: competing `getUpdates` calls produce Telegram HTTP 409.
|
|
3293
|
+
|
|
3195
3294
|
**Delivery is per cycle, not only at startup.** Before every `getUpdates` the
|
|
3196
3295
|
listener re-derives the pending queue from the verified log and sends whatever
|
|
3197
3296
|
it has not already sent, so a request appended while this listener is running
|
|
@@ -3456,6 +3555,43 @@ belong to a RUNNING listener: they are on its stderr as they happen, in its
|
|
|
3456
3555
|
Which variables are read comes from the policy, so a renamed variable reads back
|
|
3457
3556
|
as the name you set.
|
|
3458
3557
|
|
|
3558
|
+
## quickstart
|
|
3559
|
+
|
|
3560
|
+
`approval quickstart [--dir <path>] [--api-base <url>]` is the human-only solo setup ceremony. It
|
|
3561
|
+
asks three decisions: the human identifier, terminal or Telegram, and which of
|
|
3562
|
+
five class families always ask. The default checklist selects `communicate.*`,
|
|
3563
|
+
`financial.*`, `files.delete.*`, `public.*`, and `vcs.push.main`.
|
|
3564
|
+
|
|
3565
|
+
The command validates the complete generated policy before writing it. It
|
|
3566
|
+
creates the same log directory, queue projection, and gitignore entries as
|
|
3567
|
+
`init`, writes `APPROVAL_HUMAN` through the existing `.approval/env` writer,
|
|
3568
|
+
and uses the existing Telegram setup path when selected. A token therefore
|
|
3569
|
+
follows the OS-keystore or no-echo path already documented under [setup channel
|
|
3570
|
+
telegram](#setup-channel-telegram). It refuses before prompting when a policy or
|
|
3571
|
+
`.approval` instance state already exists, so it cannot silently reuse a log,
|
|
3572
|
+
queue, environment map, vault, or channel setup from another ceremony.
|
|
3573
|
+
|
|
3574
|
+
Quickstart resolves this new instance's environment map explicitly, without
|
|
3575
|
+
borrowing ambient approval credentials, and runs a bounded doctor preflight.
|
|
3576
|
+
When `--api-base` is present, the same endpoint is used for Telegram setup and
|
|
3577
|
+
that preflight; a local or private Bot API selection never falls through to the
|
|
3578
|
+
public endpoint.
|
|
3579
|
+
The expected `attestation` failure is the only failed row accepted before the
|
|
3580
|
+
ceremony; any other failed row is printed and stops before attestation. It then
|
|
3581
|
+
prints the exact policy bytes and requires the operator to type `understood`.
|
|
3582
|
+
The append rechecks the live digest and refuses if the file changed after it was
|
|
3583
|
+
shown. An abort or failed step therefore leaves the generated policy unattested.
|
|
3584
|
+
The final `activate:` line includes `approval env --dir` with the absolute,
|
|
3585
|
+
shell-quoted target directory. It is required because no ordinary runtime
|
|
3586
|
+
command loads `.approval/env` implicitly, and it still names the right instance
|
|
3587
|
+
when the operator starts the next shell elsewhere.
|
|
3588
|
+
|
|
3589
|
+
This verb classifies `policy.core` and is omitted from MCP. Piped stdin and
|
|
3590
|
+
`--json` exit 2 before any write and print the manual sequence. The generated
|
|
3591
|
+
`defaults.autonomy: autonomous` applies to other classified reversible actions.
|
|
3592
|
+
Protected controls, fail-closed policy loading, the irreversibility floor, and
|
|
3593
|
+
unclassified-command refusal continue to apply.
|
|
3594
|
+
|
|
3459
3595
|
## init
|
|
3460
3596
|
|
|
3461
3597
|
`init` holds no authority: the policy it writes authorizes nothing until a human
|
|
@@ -3541,7 +3677,11 @@ gate.self the "approval" CLI itself is pass-through
|
|
|
3541
3677
|
```
|
|
3542
3678
|
|
|
3543
3679
|
Bash (Claude Code) and Shell (Cursor) commands are classified into SPEC.md §7
|
|
3544
|
-
action classes.
|
|
3680
|
+
action classes. A `git push` that names `refs/tags/*`, a bare `v`-prefixed
|
|
3681
|
+
semantic-version-shaped tag, `tag <name>`, `--tags`, or `--follow-tags` is
|
|
3682
|
+
`release.publish`; force and mirror pushes remain `vcs.history.rewrite`, and
|
|
3683
|
+
ordinary branch pushes retain their branch or trunk class. Claude file tools
|
|
3684
|
+
(Edit, Write, MultiEdit, NotebookEdit) and
|
|
3545
3685
|
Cursor Write/Delete are gated only when the file is policy-protected
|
|
3546
3686
|
(`APPROVAL.md`, `.approval/`, `CLAUDE.md`, `AGENTS.md`, `.claude/settings*`,
|
|
3547
3687
|
`.cursor/hooks.json`, `.cursor/hooks/`, `.cursor/agents/`,
|
|
@@ -4302,6 +4442,47 @@ when the log does not.
|
|
|
4302
4442
|
|
|
4303
4443
|
## up
|
|
4304
4444
|
|
|
4445
|
+
**Normal startup after setup and human attestation.** Run from the intended
|
|
4446
|
+
policy project's directory, load its environment explicitly, then leave this
|
|
4447
|
+
foreground process running:
|
|
4448
|
+
|
|
4449
|
+
```sh
|
|
4450
|
+
cd /path/to/your/project
|
|
4451
|
+
eval "$(approval env)"
|
|
4452
|
+
approval up
|
|
4453
|
+
```
|
|
4454
|
+
|
|
4455
|
+
For a source build, replace `approval` in both commands with
|
|
4456
|
+
`node /path/to/approval.md/cli.js`. `--as human:<id>` may name the configured
|
|
4457
|
+
approver explicitly; it does not perform identity setup or policy attestation.
|
|
4458
|
+
`up` reads credentials from its launch environment and does not load
|
|
4459
|
+
`.approval/env`. Existing exported approval variables take precedence over that
|
|
4460
|
+
map, so use a clean shell or unset another instance's variables first. Changing
|
|
4461
|
+
only `--dir` selects the policy; it does not relocate every log, environment or
|
|
4462
|
+
task path. Starting in the intended project keeps the default paths together.
|
|
4463
|
+
|
|
4464
|
+
**Startup messages describe separate parts.** The default task directory is
|
|
4465
|
+
`backlog/tasks/`. If envelopes live elsewhere, pass
|
|
4466
|
+
`--tasks /path/to/existing/task-folder`. The daemon scans regular `.md` files
|
|
4467
|
+
immediately inside that directory, without recursion; an empty default folder
|
|
4468
|
+
does not cover envelopes in nested bundle directories. A missing default folder
|
|
4469
|
+
warns about absent drift coverage while TTL sweeping and queue rendering still
|
|
4470
|
+
run. An explicitly supplied missing directory is an error. `watch-unavailable`
|
|
4471
|
+
reports a failed watcher; the daemon still scans on its interval. Neither
|
|
4472
|
+
warning proves Telegram failed: check the channel's own startup line.
|
|
4473
|
+
|
|
4474
|
+
A `live-draw` doctor failure needs a daemon serving this instance's draw socket.
|
|
4475
|
+
`up` starts that daemon, but serving draws also requires a `supervised-live`
|
|
4476
|
+
policy class and the configured sampling secret resolved in this process's
|
|
4477
|
+
launch environment, with `--no-draw` absent. Starting `up` alone cannot supply a
|
|
4478
|
+
missing secret. Without usable draws, every supervised-live action gates to a
|
|
4479
|
+
human. A policy with no `channels.web.port` and no explicit `--port` serves no
|
|
4480
|
+
web queue; that informational message is legitimate configuration.
|
|
4481
|
+
|
|
4482
|
+
Stop `up` before running Telegram setup or a standalone listener for its bot.
|
|
4483
|
+
One bot must have one polling runtime. After setup changes, reload the instance
|
|
4484
|
+
environment before starting `up` again.
|
|
4485
|
+
|
|
4305
4486
|
**The ambient runtime: the daemon loop and every configured channel in one
|
|
4306
4487
|
supervised foreground process.** `approval daemon run --with-channels` is the
|
|
4307
4488
|
same verb spelled from the other side, and it reaches the same function before
|
|
@@ -4324,7 +4505,9 @@ because `git status` does not say what the upstream range changed. So the verb
|
|
|
4324
4505
|
does all four, and `approval daemon run` runs the identical preflight from the
|
|
4325
4506
|
identical module, printing the identical two lines.
|
|
4326
4507
|
|
|
4327
|
-
It is allowed exactly
|
|
4508
|
+
It is allowed exactly three writes: a `--ff-only` merge, `npm run build`, and
|
|
4509
|
+
clearing an untracked `backlog/tasks/` file the incoming commit already contains
|
|
4510
|
+
out of the merge's way (APRV-300, described below). It
|
|
4328
4511
|
never resets, never stashes, never checks anything out, and never touches the
|
|
4329
4512
|
working log. That list is not caution for its own sake: a working `events.jsonl`
|
|
4330
4513
|
rewound through git underneath a live appender is fork 2 of 2026-08-20, the
|
|
@@ -4340,8 +4523,42 @@ running. When it is not, it refuses, and changes nothing:
|
|
|
4340
4523
|
| `up-preflight-behind-ahead` | `origin/<branch>..HEAD` is non-empty: this checkout carries commits the remote has never seen. A fast-forward is not the operation for that state, and choosing a side is a decision. | look at them (`git log --oneline origin/main..HEAD`), then push them or `git reset --keep` |
|
|
4341
4524
|
| `up-preflight-log-diverged` | the upstream range rewrites `.approval/log/events.jsonl` or `.approval/QUEUE.md`, and this working copy has uncommitted changes to one of them. The judgment a human could not make by eye. | `approval log sync` |
|
|
4342
4525
|
| `up-preflight-dirty-protected` | some other path the upstream range changes is locally modified, so `git merge --ff-only` would refuse rather than overwrite it. | look at the diff, or `approval up --no-preflight` |
|
|
4526
|
+
| `up-preflight-task-file-conflict` | an untracked file under `backlog/tasks/` stopped the fast-forward and it holds lines the incoming copy does not. Which version is wanted is a question, and no verb here will pick. | read the two copies, move yours aside, run `approval up` again |
|
|
4343
4527
|
| `up-preflight-failed` | a write the preflight attempted did not complete: the fast-forward, or the rebuild. Not a judgment, so it is not in the union above; the message names the step, and for a build it names the exit code `npm run build` came back with. | `npm run build` to see the whole error, or `approval up --no-build` if you mean to run the stale one |
|
|
4344
4528
|
|
|
4529
|
+
**An untracked task file no longer stops it (APRV-300).** A lane files
|
|
4530
|
+
`backlog/tasks/aprv-299` on its branch and its pull request merges, while the
|
|
4531
|
+
primary checkout holds the same path untracked from its own `backlog task
|
|
4532
|
+
create`. `git merge --ff-only` will not write over an untracked file, so on
|
|
4533
|
+
2026-09-07 the preflight refused, and its next-steps text pointed at `git
|
|
4534
|
+
status`, which cannot say whether the local copy holds anything the incoming one
|
|
4535
|
+
does not. That question is answerable, so it is answered. When the merge fails
|
|
4536
|
+
over untracked files and every path git names sits under `backlog/tasks/`, each
|
|
4537
|
+
one is read alongside `git show <target>:<path>` and given one of three
|
|
4538
|
+
verdicts:
|
|
4539
|
+
|
|
4540
|
+
- **byte-identical** — the local copy says nothing the incoming copy does not,
|
|
4541
|
+
so it is removed and the merge is retried once;
|
|
4542
|
+
- **every line also in the incoming copy** — the ordinary shape, a hand-filed
|
|
4543
|
+
stub against a lane copy that added a plan and criteria. Nothing is lost by
|
|
4544
|
+
letting the incoming copy land, but that is a judgment about an operator's
|
|
4545
|
+
file, so the bytes are moved to a sibling of the checkout named
|
|
4546
|
+
`<repo>-preflight-aside-<YYYY-MM-DD>` (outside the repository, so the next
|
|
4547
|
+
fast-forward cannot collide with it again), the destination is printed on the
|
|
4548
|
+
`preflight_warning` line, and the merge is retried once;
|
|
4549
|
+
- **anything else** — `up-preflight-task-file-conflict`, naming your path, the
|
|
4550
|
+
incoming spelling, and how many lines only yours has.
|
|
4551
|
+
|
|
4552
|
+
Every file is judged before any file is touched, the same two-pass shape as
|
|
4553
|
+
`approval log sync`'s payload reconciliation, so a refusal over the last
|
|
4554
|
+
collision cannot have already removed the first. One path outside
|
|
4555
|
+
`backlog/tasks/` and the whole set is declined: the merge keeps its old
|
|
4556
|
+
`up-preflight-failed` refusal and nothing is cleared, because clearing what was
|
|
4557
|
+
understood and then refusing anyway would have moved files for a merge that was
|
|
4558
|
+
never going to run. A path git chose to quote (`core.quotePath`) is declined for
|
|
4559
|
+
the same reason: guessing the spelling of a file about to be moved is the
|
|
4560
|
+
mistake the whole check exists to avoid.
|
|
4561
|
+
|
|
4345
4562
|
**`git reset --hard` is printed on no path, ever**, and a test asserts it. The
|
|
4346
4563
|
one reset that appears is `--keep`, which refuses rather than discarding
|
|
4347
4564
|
uncommitted work, and it is the third step of a runbook whose first step is to
|
|
@@ -4498,9 +4715,11 @@ is the verb that hands them the unit.
|
|
|
4498
4715
|
**There is no `approval vault get`**, and it is not an oversight. A verb that
|
|
4499
4716
|
printed a credential would put it in a terminal, a scrollback buffer, a CI log
|
|
4500
4717
|
and — through the shell that ran it — a history file. A credential's only
|
|
4501
|
-
sanctioned journey is from the vault into an adapter, inside the verified
|
|
4502
|
-
window the adapter contract holds open
|
|
4503
|
-
|
|
4718
|
+
sanctioned journey is from the vault into an adapter, inside the verified
|
|
4719
|
+
execution window the adapter contract holds open. Manual and selected-live
|
|
4720
|
+
paths verify and consume a token; an attested class rule may explicitly
|
|
4721
|
+
authorize an irreversible supervised or autonomous path. Names are visible;
|
|
4722
|
+
values are not.
|
|
4504
4723
|
|
|
4505
4724
|
**What the vault DEFENDS:** credentials at rest, and casual reads by an agent
|
|
4506
4725
|
that can read files in the working tree — the ciphertext hides the NAMES as well
|
|
@@ -4607,26 +4826,47 @@ the check would require this verb to know every adapter a machine might run.
|
|
|
4607
4826
|
|
|
4608
4827
|
## adapter
|
|
4609
4828
|
|
|
4610
|
-
An adapter is the hard boundary of SPEC.md §10.4: it holds the credentials
|
|
4611
|
-
|
|
4612
|
-
|
|
4613
|
-
|
|
4829
|
+
An adapter is the hard boundary of SPEC.md §10.4: it holds the credentials while
|
|
4830
|
+
the runtime recomputes the payload hash and applies attested policy. Manual and
|
|
4831
|
+
selected-live paths require a valid, unexpired, single-use execution token bound
|
|
4832
|
+
to the action's `idempotency_key` and `payload_hash`. An explicitly opted-in
|
|
4833
|
+
supervised or autonomous path has no grant and mints no token, so `--token` is
|
|
4834
|
+
optional at the command boundary.
|
|
4614
4835
|
|
|
4615
|
-
|
|
4616
|
-
|
|
4617
|
-
|
|
4618
|
-
|
|
4619
|
-
|
|
4620
|
-
|
|
4836
|
+
Credential custody does not become implicit on the no-token path. The vault
|
|
4837
|
+
passphrase must already be present in the adapter process environment. The
|
|
4838
|
+
`.approval/env` source-map fallback remains available only inside a real token
|
|
4839
|
+
window: it rejects a null grant and a consumed nonmanual execution carrying no
|
|
4840
|
+
token digest. This lets an operator authorize nonmanual execution without
|
|
4841
|
+
giving an agent a new way to load credentials.
|
|
4621
4842
|
|
|
4622
|
-
The
|
|
4843
|
+
The runtime, not the adapter, owns the sequence: recompute the payload hash,
|
|
4844
|
+
read the declared class from the verified log, check policy eligibility or the
|
|
4845
|
+
manual token without appending a start, resolve the credentials the adapter says
|
|
4846
|
+
it cannot act without, run the adapter's own pre-token check, then recheck policy
|
|
4847
|
+
and, on a manual path, verify and consume the token, append
|
|
4848
|
+
`execution.started`, call the adapter, append the outcome. The adapter implements
|
|
4849
|
+
one method and cannot skip a step, because it never holds the sequence.
|
|
4850
|
+
|
|
4851
|
+
`supervised-live` selection still belongs to `approval request`. On a direct
|
|
4852
|
+
no-token invocation with no earlier approval cycle, the adapter contract runs
|
|
4853
|
+
that existing intake path from the verified declaration before it touches a
|
|
4854
|
+
credential. A selected or unavailable draw records the ordinary pending cycle
|
|
4855
|
+
and stops for its token. The request retains the exact already-hashed payload
|
|
4856
|
+
through the normal payload store, so the selected human sees the bound bytes.
|
|
4857
|
+
An unselected draw appends no approval event and continues, bound to the same
|
|
4858
|
+
attested policy digest through eligibility and start. Once any cycle exists,
|
|
4859
|
+
including a rejected or expired one, the contract never redraws it.
|
|
4860
|
+
|
|
4861
|
+
The two steps that sit before authorization starts the execution are there for one reason: a
|
|
4623
4862
|
condition that makes the side effect impossible, and that the runtime can
|
|
4624
4863
|
establish without attempting it, must not cost a human's single-use grant to
|
|
4625
4864
|
discover. A credential nobody stored refuses `credential-unavailable`; whatever
|
|
4626
4865
|
the adapter's own check refuses arrives as `adapter-precheck-refused` with the
|
|
4627
4866
|
adapter's reason in `adapter_code`. Both leave the log exactly as they found it,
|
|
4628
4867
|
`acted` is `false`, there is no `started_seq` and no `outcome`, and the same
|
|
4629
|
-
token executes once the condition is repaired.
|
|
4868
|
+
token executes once the condition is repaired. On an admitted nonmanual path,
|
|
4869
|
+
there is no token to preserve and the same preflight still appends nothing.
|
|
4630
4870
|
|
|
4631
4871
|
The pre-token check is offered only bytes the log binds to the action: the
|
|
4632
4872
|
grant's `payload_hash` on the manual path, the registered declaration's off it.
|
|
@@ -4739,8 +4979,10 @@ The enforcement model it assumes is a split pair of keys. AgentMail keys carry
|
|
|
4739
4979
|
`draft_create`, `draft_update`, `draft_read`, `draft_send` and `message_send`
|
|
4740
4980
|
separately. The agent gets a key WITHOUT the two send permissions, so it can
|
|
4741
4981
|
compose all day and cannot send; the key WITH them goes in the vault under
|
|
4742
|
-
`agentmail.api_key`, readable only inside the
|
|
4743
|
-
|
|
4982
|
+
`agentmail.api_key`, readable only inside the contract's execution window. On a
|
|
4983
|
+
nonmanual path, opening the encrypted vault requires its passphrase in the
|
|
4984
|
+
adapter process environment; the token-scoped source-map fallback does not run.
|
|
4985
|
+
`approval setup adapter agentmail` stores that pair, and
|
|
4744
4986
|
`approval payload agentmail-draft` is the composing side's own verb.
|
|
4745
4987
|
|
|
4746
4988
|
Two payload modes, told apart by the keys they carry, and a payload carrying
|
|
@@ -4811,6 +5053,82 @@ string as `message_id`, under the one key the adapter contract lifts onto
|
|
|
4811
5053
|
as `"provider_ref":{"adapter":"agentmail","id":…}` beside the detail. A send
|
|
4812
5054
|
whose answer names no id carries neither.
|
|
4813
5055
|
|
|
5056
|
+
## adapter zzz
|
|
5057
|
+
|
|
5058
|
+
Creates a zzz.bot thread or reply for `communicate.zzz.external`. The only
|
|
5059
|
+
credential is `zzz.agent_token`, an invited principal token with write scope.
|
|
5060
|
+
It is read from the vault inside the shared execution window and sent only as an
|
|
5061
|
+
Authorization Bearer header. `--token` is required for manual or selected-live
|
|
5062
|
+
execution and omitted for an explicitly policy-authorized supervised or
|
|
5063
|
+
autonomous execution. On the no-token path, the vault passphrase must already be
|
|
5064
|
+
present in the adapter process environment.
|
|
5065
|
+
|
|
5066
|
+
This verb is available from a source checkout containing APRV-320 until the
|
|
5067
|
+
next approval.md package release. npm `approval-md@0.1.0` predates the adapter;
|
|
5068
|
+
this change does not publish a package.
|
|
5069
|
+
|
|
5070
|
+
The contract implemented here is zzz.bot API v0.1.0: [quickstart](https://zzz.bot/quickstart),
|
|
5071
|
+
[API guide](https://zzz.bot/api), [approval semantics](https://zzz.bot/approval),
|
|
5072
|
+
and [OpenAPI](https://zzz.bot/openapi.json).
|
|
5073
|
+
|
|
5074
|
+
The payload is a strict tagged union:
|
|
5075
|
+
|
|
5076
|
+
```json
|
|
5077
|
+
{"environment":"production","operation":"create_thread",
|
|
5078
|
+
"room_id":"<room-id-from-GET-api-v1-rooms>","title":"…","body":"…",
|
|
5079
|
+
"metadata":{},"tags":["…"],
|
|
5080
|
+
"references":[{"kind":"external","target":"https://example.com/source",
|
|
5081
|
+
"label":"Source","relationship":"source"}]}
|
|
5082
|
+
{"environment":"preview","operation":"create_reply",
|
|
5083
|
+
"thread_id":"…","body":"…","metadata":{},"tags":["…"],"references":[]}
|
|
5084
|
+
```
|
|
5085
|
+
|
|
5086
|
+
`environment` is exactly `production` or `preview`; operation is exactly
|
|
5087
|
+
`create_thread` or `create_reply`. Reference `kind` and `relationship` use
|
|
5088
|
+
the alternatives shown above. Unknown keys are refused. Bodies are 1 to 65,536
|
|
5089
|
+
characters, titles 1 to 200, tags at most 10 strings of 1 to 40 characters, and
|
|
5090
|
+
references at most 20. The entire canonical message JSON must fit 65,536 UTF-8
|
|
5091
|
+
bytes, so a maximum-length body can exceed the request limit once its other
|
|
5092
|
+
fields and JSON encoding are included.
|
|
5093
|
+
External reference targets must be HTTP or HTTPS URLs. All optional values that
|
|
5094
|
+
are present remain inside the bound payload and are sent unchanged.
|
|
5095
|
+
|
|
5096
|
+
The message fields are deliberately flat beside the operation tag and target.
|
|
5097
|
+
This keeps the payload a person reviews close to ZZZ's request body. The adapter
|
|
5098
|
+
removes only `environment`, `operation` and the target id when building the
|
|
5099
|
+
POST body; every content field remains byte-for-byte represented in the
|
|
5100
|
+
canonical JSON sent to ZZZ.
|
|
5101
|
+
|
|
5102
|
+
`production` routes to `https://zzz.bot` and `preview` to the fixed preview
|
|
5103
|
+
service. There is no API-base flag. Thread creation posts to
|
|
5104
|
+
`/api/v1/rooms/{room}/threads`; replies post to
|
|
5105
|
+
`/api/v1/threads/{thread}/posts`. Redirects are rejected. The
|
|
5106
|
+
`Idempotency-Key` is deterministic SHA-256 over the RFC 8785 form of the
|
|
5107
|
+
approval action key and payload hash, so the provider's retry identity binds the
|
|
5108
|
+
same action and exact bytes.
|
|
5109
|
+
|
|
5110
|
+
HTTP 201 is accepted only with `{"id":"thr_…"|"pst_…","replayed":false}`,
|
|
5111
|
+
and HTTP 200 only with the same operation-appropriate id and `replayed:true`.
|
|
5112
|
+
The validated service id becomes `provider_ref`. A malformed or inconsistent
|
|
5113
|
+
success, transport error, redirect, or 5xx is `execution.indeterminate` because
|
|
5114
|
+
the POST may have committed. No response text is recorded. Definite refusals
|
|
5115
|
+
map to `zzz-invalid-request` (400), `zzz-unauthorized` (401),
|
|
5116
|
+
`zzz-forbidden` (403), `zzz-not-found` (404),
|
|
5117
|
+
`zzz-idempotency-conflict` (409), `zzz-payload-too-large` (413),
|
|
5118
|
+
`zzz-rejected` (422), or `zzz-rate-limited` (429).
|
|
5119
|
+
|
|
5120
|
+
Public writes require an invited credential with write scope. Private rooms
|
|
5121
|
+
also require current membership carrying write and accepted, unexpired
|
|
5122
|
+
approval.md workflow evidence. zzz.bot intentionally returns 404 when private
|
|
5123
|
+
access is absent, so the adapter does not guess which prerequisite failed.
|
|
5124
|
+
`approval setup adapter zzz` verifies only credential acceptance through one
|
|
5125
|
+
read-only `GET /api/v1/rooms` and posts nothing.
|
|
5126
|
+
|
|
5127
|
+
The local non-guest MCP server publishes this same verb from the registry. MCP
|
|
5128
|
+
invocation remains voluntary. Mechanical enforcement comes from keeping the
|
|
5129
|
+
write credential solely in the vault; an agent that also holds the credential
|
|
5130
|
+
can bypass the adapter.
|
|
5131
|
+
|
|
4814
5132
|
## env
|
|
4815
5133
|
|
|
4816
5134
|
This command is the only thing that reads `.approval/env`, and its default output
|
|
@@ -4909,7 +5227,7 @@ channel surfaces requests and collects decisions and holds no state, so its setu
|
|
|
4909
5227
|
fills the OS keystore and `.approval/env` — the map of where the values that
|
|
4910
5228
|
unlock the machine live. An adapter executes side effects and holds credentials,
|
|
4911
5229
|
so its setup fills `.approval/vault.enc`, which holds the values a gated adapter
|
|
4912
|
-
SPENDS, read inside the verified
|
|
5230
|
+
SPENDS, read inside the verified execution window and by nothing else. There is no
|
|
4913
5231
|
verb that prints one back. (An older build spelled the Telegram one without the
|
|
4914
5232
|
`channel` noun. That form exits 2 and names this one; there is no alias, because
|
|
4915
5233
|
two spellings of a distinction the SPEC draws on purpose is how the distinction
|
|
@@ -5080,7 +5398,7 @@ unset, nothing is stored and no vault is created.
|
|
|
5080
5398
|
|
|
5081
5399
|
The values go into the vault, not into the OS keystore and not into
|
|
5082
5400
|
`.approval/env`: what this verb stores is what a gated adapter spends inside a
|
|
5083
|
-
verified
|
|
5401
|
+
verified execution window.
|
|
5084
5402
|
|
|
5085
5403
|
What it reports: the path, the count, the names written and the names left alone.
|
|
5086
5404
|
Never a value, on any path, including a failed probe. Exit 1 means the service
|
|
@@ -5157,7 +5475,7 @@ agentmail.api_key the key that carries draft_send and message_send
|
|
|
5157
5475
|
Store the SENDING key here and give the agent a different one. An AgentMail key
|
|
5158
5476
|
is a mailbox in one string, so a deployment that hands the agent the sending key
|
|
5159
5477
|
has an agent that can send without asking anybody, and the gate in front of it is
|
|
5160
|
-
decoration. The key in the vault is read only inside the verified
|
|
5478
|
+
decoration. The key in the vault is read only inside the verified execution window
|
|
5161
5479
|
the adapter contract opens.
|
|
5162
5480
|
|
|
5163
5481
|
The probe sends nothing. It is `GET /v0/inboxes/{inbox_id}`, the same read a
|
|
@@ -5179,6 +5497,20 @@ adapter's does. A re-run that replaced only one name is offered the same probe
|
|
|
5179
5497
|
over the stored pair, read through `readAgentmailConfig` over the vault: the
|
|
5180
5498
|
exact path `approval adapter agentmail` takes at send time, printed by nothing.
|
|
5181
5499
|
|
|
5500
|
+
## setup adapter zzz
|
|
5501
|
+
|
|
5502
|
+
The manifest contains one secret, `zzz.agent_token`. It must be an invited
|
|
5503
|
+
zzz.bot principal credential with write scope. Store the write-capable token in
|
|
5504
|
+
the vault and keep it out of the agent environment; otherwise the agent can post
|
|
5505
|
+
without passing through the adapter.
|
|
5506
|
+
|
|
5507
|
+
The optional probe sends nothing. It makes one authenticated
|
|
5508
|
+
`GET /api/v1/rooms` against production and reports success only when zzz.bot
|
|
5509
|
+
accepts the credential. That endpoint does not disclose the principal's write
|
|
5510
|
+
scope, room memberships, or accepted private-room workflow evidence, so setup
|
|
5511
|
+
states those limits instead of claiming the token can publish. The actual
|
|
5512
|
+
approved POST remains the first proof of all write prerequisites.
|
|
5513
|
+
|
|
5182
5514
|
## setup channel
|
|
5183
5515
|
|
|
5184
5516
|
A channel is not an adapter, and the two setup verbs fill different stores.
|
|
@@ -5189,9 +5521,11 @@ credentials, so `approval setup adapter <name>` fills the vault instead.
|
|
|
5189
5521
|
|
|
5190
5522
|
## setup channel telegram
|
|
5191
5523
|
|
|
5192
|
-
Stop `approval channel telegram listen`
|
|
5193
|
-
|
|
5194
|
-
|
|
5524
|
+
Stop any `approval up` process or `approval channel telegram listen` polling
|
|
5525
|
+
this bot first. Setup also uses `getUpdates` to discover the approver chat.
|
|
5526
|
+
Competing polls produce HTTP 409 from the Bot API. This is a configuration verb;
|
|
5527
|
+
after it finishes, reload the instance environment and use `approval up` for
|
|
5528
|
+
normal operation.
|
|
5195
5529
|
|
|
5196
5530
|
The token is never typed into this process on a machine with a keystore: the
|
|
5197
5531
|
helper's own no-echo prompt collects it, and this runtime reads it back on stdout
|
|
@@ -5361,3 +5695,22 @@ queue, so an unbounded guest wait is one stranger stalling every other session.
|
|
|
5361
5695
|
The guest instructions string says so, tells the caller to poll `status`, and
|
|
5362
5696
|
states plainly that a granted request executes nowhere: the demo is the approval
|
|
5363
5697
|
flow itself.
|
|
5698
|
+
|
|
5699
|
+
## Constrained Codex preparation
|
|
5700
|
+
|
|
5701
|
+
approval codex prepare is an artifact generator. It writes one fresh review
|
|
5702
|
+
directory and has no activation path. Its requirements, managed config,
|
|
5703
|
+
launchers and launchd files are text for a human or MDM workflow to inspect.
|
|
5704
|
+
|
|
5705
|
+
approval codex setup --check proves only that those artifacts match their
|
|
5706
|
+
closed manifest and hashes. approval codex doctor --strict asks the separate
|
|
5707
|
+
host question: are the package and its ancestors root-owned and immutable, are
|
|
5708
|
+
the three principals distinct, are the roots disjoint and canonical, and are
|
|
5709
|
+
the broker and runner present? POSIX ownership does not establish ACL custody,
|
|
5710
|
+
so this slice executes no manifest-selected binary and reports runtime versions
|
|
5711
|
+
unchecked. Unknown evidence is a refusal.
|
|
5712
|
+
|
|
5713
|
+
The first slice deliberately makes start and serve return codex-not-ready. An
|
|
5714
|
+
npm install, generated config, or passing bundle check does not create a
|
|
5715
|
+
mandatory boundary. The whole approval codex family is operator-only and absent
|
|
5716
|
+
from the ordinary broad MCP catalog.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Constrained Codex preparation
|
|
2
|
+
|
|
3
|
+
approval codex prepare creates a review bundle for a future constrained Codex
|
|
4
|
+
session. It is packaging and diagnostics, not activation.
|
|
5
|
+
|
|
6
|
+
The bundle pins one macOS/Codex version, distinct Codex, broker and runner
|
|
7
|
+
principals, disjoint active-worktree, primary-gate and root-owned installation
|
|
8
|
+
roots, and one exact MCP executable plus argument vector. Its requirements
|
|
9
|
+
template makes native Codex tools read-only, disables command network and the
|
|
10
|
+
browser, computer-use, plugin and web-search surfaces, and allowlists only the
|
|
11
|
+
strict MCP identity.
|
|
12
|
+
|
|
13
|
+
Preparation writes only a new output directory. approval codex setup --check
|
|
14
|
+
verifies the exact file set, SHA-256 index, instance schema and every generated
|
|
15
|
+
template byte. A passing check means the inert bundle is internally consistent.
|
|
16
|
+
|
|
17
|
+
approval codex doctor --strict --manifest PATH inspects host custody. It rejects
|
|
18
|
+
unsupported manifest platforms or version declarations, missing or overlapping principals,
|
|
19
|
+
overlapping roots, symlinks, path drift, non-root-owned or writable trusted
|
|
20
|
+
ancestors, invocation drift, missing executables and a workspace writable by
|
|
21
|
+
the Codex principal. APRV-325.1 also unconditionally reports broker-not-ready
|
|
22
|
+
and runner-not-ready. POSIX ownership and mode cannot prove the absence of
|
|
23
|
+
writable ACLs, so this slice executes no manifest-selected binary and reports
|
|
24
|
+
both installed runtime versions unchecked.
|
|
25
|
+
|
|
26
|
+
The package does not run a postinstall script, call sudo, edit Codex
|
|
27
|
+
configuration, create accounts, load services, access credentials, start a
|
|
28
|
+
model, or alter APPROVAL.md. A human or MDM system must eventually install and
|
|
29
|
+
own the reviewed artifacts. APRV-325.2 and APRV-325.3 must provide the broker,
|
|
30
|
+
runner and end-to-end denial evidence before the session can be called enforced.
|