approval-md 0.0.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +176 -0
- package/NOTICE +5 -0
- package/README.md +940 -4
- package/SPEC.md +476 -34
- package/cli.js +29 -3
- package/dist/src/adapters/agentmail.d.ts +426 -0
- package/dist/src/adapters/agentmail.js +1200 -0
- package/dist/src/adapters/agentmail.js.map +1 -0
- package/dist/src/adapters/conformance.d.ts +149 -0
- package/dist/src/adapters/conformance.js +461 -0
- package/dist/src/adapters/conformance.js.map +1 -0
- package/dist/src/adapters/contract.d.ts +628 -0
- package/dist/src/adapters/contract.js +1035 -0
- package/dist/src/adapters/contract.js.map +1 -0
- package/dist/src/adapters/email.d.ts +324 -0
- package/dist/src/adapters/email.js +749 -0
- package/dist/src/adapters/email.js.map +1 -0
- package/dist/src/adapters/env-passphrase.d.ts +93 -0
- package/dist/src/adapters/env-passphrase.js +132 -0
- package/dist/src/adapters/env-passphrase.js.map +1 -0
- package/dist/src/adapters/public.d.ts +11 -0
- package/dist/src/adapters/public.js +11 -0
- package/dist/src/adapters/public.js.map +1 -0
- package/dist/src/adapters/registry.d.ts +59 -0
- package/dist/src/adapters/registry.js +77 -0
- package/dist/src/adapters/registry.js.map +1 -0
- package/dist/src/adapters/smtp.d.ts +213 -0
- package/dist/src/adapters/smtp.js +499 -0
- package/dist/src/adapters/smtp.js.map +1 -0
- package/dist/src/adapters/vault-provider.d.ts +114 -0
- package/dist/src/adapters/vault-provider.js +161 -0
- package/dist/src/adapters/vault-provider.js.map +1 -0
- package/dist/src/adapters/zzz.d.ts +66 -0
- package/dist/src/adapters/zzz.js +299 -0
- package/dist/src/adapters/zzz.js.map +1 -0
- package/dist/src/channels/batch.d.ts +109 -0
- package/dist/src/channels/batch.js +121 -0
- package/dist/src/channels/batch.js.map +1 -0
- package/dist/src/channels/cli.d.ts +193 -0
- package/dist/src/channels/cli.js +468 -0
- package/dist/src/channels/cli.js.map +1 -0
- package/dist/src/channels/conformance.d.ts +92 -0
- package/dist/src/channels/conformance.js +445 -0
- package/dist/src/channels/conformance.js.map +1 -0
- package/dist/src/channels/contract.d.ts +623 -0
- package/dist/src/channels/contract.js +494 -0
- package/dist/src/channels/contract.js.map +1 -0
- package/dist/src/channels/payload-view.d.ts +35 -0
- package/dist/src/channels/payload-view.js +43 -0
- package/dist/src/channels/payload-view.js.map +1 -0
- package/dist/src/channels/render-queue.d.ts +149 -0
- package/dist/src/channels/render-queue.js +564 -0
- package/dist/src/channels/render-queue.js.map +1 -0
- package/dist/src/channels/tagging.d.ts +196 -0
- package/dist/src/channels/tagging.js +723 -0
- package/dist/src/channels/tagging.js.map +1 -0
- package/dist/src/channels/telegram.d.ts +1832 -0
- package/dist/src/channels/telegram.js +3190 -0
- package/dist/src/channels/telegram.js.map +1 -0
- package/dist/src/channels/web.d.ts +341 -0
- package/dist/src/channels/web.js +903 -0
- package/dist/src/channels/web.js.map +1 -0
- package/dist/src/cli/adapter.d.ts +90 -0
- package/dist/src/cli/adapter.js +288 -0
- package/dist/src/cli/adapter.js.map +1 -0
- package/dist/src/cli/amend.d.ts +59 -0
- package/dist/src/cli/amend.js +2171 -0
- package/dist/src/cli/amend.js.map +1 -0
- package/dist/src/cli/args.d.ts +43 -0
- package/dist/src/cli/args.js +86 -0
- package/dist/src/cli/args.js.map +1 -0
- package/dist/src/cli/attest.d.ts +41 -0
- package/dist/src/cli/attest.js +307 -0
- package/dist/src/cli/attest.js.map +1 -0
- package/dist/src/cli/audit-card.d.ts +62 -0
- package/dist/src/cli/audit-card.js +201 -0
- package/dist/src/cli/audit-card.js.map +1 -0
- package/dist/src/cli/audit.d.ts +59 -0
- package/dist/src/cli/audit.js +460 -0
- package/dist/src/cli/audit.js.map +1 -0
- package/dist/src/cli/channel-telegram.d.ts +806 -0
- package/dist/src/cli/channel-telegram.js +2063 -0
- package/dist/src/cli/channel-telegram.js.map +1 -0
- package/dist/src/cli/channel-web.d.ts +131 -0
- package/dist/src/cli/channel-web.js +357 -0
- package/dist/src/cli/channel-web.js.map +1 -0
- package/dist/src/cli/channel.d.ts +71 -0
- package/dist/src/cli/channel.js +438 -0
- package/dist/src/cli/channel.js.map +1 -0
- package/dist/src/cli/checkpoint-tap.d.ts +169 -0
- package/dist/src/cli/checkpoint-tap.js +238 -0
- package/dist/src/cli/checkpoint-tap.js.map +1 -0
- package/dist/src/cli/codex.d.ts +2 -0
- package/dist/src/cli/codex.js +172 -0
- package/dist/src/cli/codex.js.map +1 -0
- package/dist/src/cli/coverage.d.ts +61 -0
- package/dist/src/cli/coverage.js +343 -0
- package/dist/src/cli/coverage.js.map +1 -0
- package/dist/src/cli/daemon.d.ts +120 -0
- package/dist/src/cli/daemon.js +631 -0
- package/dist/src/cli/daemon.js.map +1 -0
- package/dist/src/cli/doctor.d.ts +129 -0
- package/dist/src/cli/doctor.js +2762 -0
- package/dist/src/cli/doctor.js.map +1 -0
- package/dist/src/cli/env.d.ts +65 -0
- package/dist/src/cli/env.js +302 -0
- package/dist/src/cli/env.js.map +1 -0
- package/dist/src/cli/execute.d.ts +202 -0
- package/dist/src/cli/execute.js +1682 -0
- package/dist/src/cli/execute.js.map +1 -0
- package/dist/src/cli/exit-codes.d.ts +73 -0
- package/dist/src/cli/exit-codes.js +82 -0
- package/dist/src/cli/exit-codes.js.map +1 -0
- package/dist/src/cli/feedback.d.ts +60 -0
- package/dist/src/cli/feedback.js +205 -0
- package/dist/src/cli/feedback.js.map +1 -0
- package/dist/src/cli/gate-window.d.ts +40 -0
- package/dist/src/cli/gate-window.js +294 -0
- package/dist/src/cli/gate-window.js.map +1 -0
- package/dist/src/cli/gate.d.ts +68 -0
- package/dist/src/cli/gate.js +557 -0
- package/dist/src/cli/gate.js.map +1 -0
- package/dist/src/cli/git-scope.d.ts +190 -0
- package/dist/src/cli/git-scope.js +295 -0
- package/dist/src/cli/git-scope.js.map +1 -0
- package/dist/src/cli/gloss-attach.d.ts +85 -0
- package/dist/src/cli/gloss-attach.js +107 -0
- package/dist/src/cli/gloss-attach.js.map +1 -0
- package/dist/src/cli/gloss-codex-child.d.ts +9 -0
- package/dist/src/cli/gloss-codex-child.js +149 -0
- package/dist/src/cli/gloss-codex-child.js.map +1 -0
- package/dist/src/cli/gloss-codex.d.ts +24 -0
- package/dist/src/cli/gloss-codex.js +255 -0
- package/dist/src/cli/gloss-codex.js.map +1 -0
- package/dist/src/cli/gloss-options.d.ts +42 -0
- package/dist/src/cli/gloss-options.js +79 -0
- package/dist/src/cli/gloss-options.js.map +1 -0
- package/dist/src/cli/gloss.d.ts +265 -0
- package/dist/src/cli/gloss.js +362 -0
- package/dist/src/cli/gloss.js.map +1 -0
- package/dist/src/cli/help.d.ts +103 -0
- package/dist/src/cli/help.js +2339 -0
- package/dist/src/cli/help.js.map +1 -0
- package/dist/src/cli/hook-codex.d.ts +78 -0
- package/dist/src/cli/hook-codex.js +167 -0
- package/dist/src/cli/hook-codex.js.map +1 -0
- package/dist/src/cli/hook.d.ts +331 -0
- package/dist/src/cli/hook.js +2849 -0
- package/dist/src/cli/hook.js.map +1 -0
- package/dist/src/cli/import.d.ts +35 -0
- package/dist/src/cli/import.js +175 -0
- package/dist/src/cli/import.js.map +1 -0
- package/dist/src/cli/init.d.ts +84 -0
- package/dist/src/cli/init.js +336 -0
- package/dist/src/cli/init.js.map +1 -0
- package/dist/src/cli/instructions.d.ts +23 -0
- package/dist/src/cli/instructions.js +262 -0
- package/dist/src/cli/instructions.js.map +1 -0
- package/dist/src/cli/journal.d.ts +41 -0
- package/dist/src/cli/journal.js +238 -0
- package/dist/src/cli/journal.js.map +1 -0
- package/dist/src/cli/log-advance.d.ts +287 -0
- package/dist/src/cli/log-advance.js +840 -0
- package/dist/src/cli/log-advance.js.map +1 -0
- package/dist/src/cli/log-anchor.d.ts +176 -0
- package/dist/src/cli/log-anchor.js +387 -0
- package/dist/src/cli/log-anchor.js.map +1 -0
- package/dist/src/cli/log-checkpoint.d.ts +22 -0
- package/dist/src/cli/log-checkpoint.js +128 -0
- package/dist/src/cli/log-checkpoint.js.map +1 -0
- package/dist/src/cli/log-sync.d.ts +243 -0
- package/dist/src/cli/log-sync.js +849 -0
- package/dist/src/cli/log-sync.js.map +1 -0
- package/dist/src/cli/log-verbs.d.ts +16 -0
- package/dist/src/cli/log-verbs.js +360 -0
- package/dist/src/cli/log-verbs.js.map +1 -0
- package/dist/src/cli/long-help.d.ts +70 -0
- package/dist/src/cli/long-help.js +148 -0
- package/dist/src/cli/long-help.js.map +1 -0
- package/dist/src/cli/main.d.ts +77 -0
- package/dist/src/cli/main.js +1206 -0
- package/dist/src/cli/main.js.map +1 -0
- package/dist/src/cli/mcp.d.ts +52 -0
- package/dist/src/cli/mcp.js +306 -0
- package/dist/src/cli/mcp.js.map +1 -0
- package/dist/src/cli/paths.d.ts +56 -0
- package/dist/src/cli/paths.js +79 -0
- package/dist/src/cli/paths.js.map +1 -0
- package/dist/src/cli/payload.d.ts +58 -0
- package/dist/src/cli/payload.js +253 -0
- package/dist/src/cli/payload.js.map +1 -0
- package/dist/src/cli/policy.d.ts +43 -0
- package/dist/src/cli/policy.js +229 -0
- package/dist/src/cli/policy.js.map +1 -0
- package/dist/src/cli/preflight.d.ts +363 -0
- package/dist/src/cli/preflight.js +1175 -0
- package/dist/src/cli/preflight.js.map +1 -0
- package/dist/src/cli/progress.d.ts +78 -0
- package/dist/src/cli/progress.js +112 -0
- package/dist/src/cli/progress.js.map +1 -0
- package/dist/src/cli/prompt.d.ts +209 -0
- package/dist/src/cli/prompt.js +312 -0
- package/dist/src/cli/prompt.js.map +1 -0
- package/dist/src/cli/quickstart.d.ts +46 -0
- package/dist/src/cli/quickstart.js +297 -0
- package/dist/src/cli/quickstart.js.map +1 -0
- package/dist/src/cli/records.d.ts +34 -0
- package/dist/src/cli/records.js +66 -0
- package/dist/src/cli/records.js.map +1 -0
- package/dist/src/cli/render.d.ts +22 -0
- package/dist/src/cli/render.js +132 -0
- package/dist/src/cli/render.js.map +1 -0
- package/dist/src/cli/sandbox.d.ts +51 -0
- package/dist/src/cli/sandbox.js +150 -0
- package/dist/src/cli/sandbox.js.map +1 -0
- package/dist/src/cli/scaffold.d.ts +79 -0
- package/dist/src/cli/scaffold.js +137 -0
- package/dist/src/cli/scaffold.js.map +1 -0
- package/dist/src/cli/setup-adapter.d.ts +137 -0
- package/dist/src/cli/setup-adapter.js +509 -0
- package/dist/src/cli/setup-adapter.js.map +1 -0
- package/dist/src/cli/setup-channel.d.ts +117 -0
- package/dist/src/cli/setup-channel.js +635 -0
- package/dist/src/cli/setup-channel.js.map +1 -0
- package/dist/src/cli/setup-checkpoint.d.ts +57 -0
- package/dist/src/cli/setup-checkpoint.js +196 -0
- package/dist/src/cli/setup-checkpoint.js.map +1 -0
- package/dist/src/cli/setup-common.d.ts +275 -0
- package/dist/src/cli/setup-common.js +376 -0
- package/dist/src/cli/setup-common.js.map +1 -0
- package/dist/src/cli/setup-flow.d.ts +287 -0
- package/dist/src/cli/setup-flow.js +476 -0
- package/dist/src/cli/setup-flow.js.map +1 -0
- package/dist/src/cli/setup-service.d.ts +96 -0
- package/dist/src/cli/setup-service.js +308 -0
- package/dist/src/cli/setup-service.js.map +1 -0
- package/dist/src/cli/setup.d.ts +202 -0
- package/dist/src/cli/setup.js +473 -0
- package/dist/src/cli/setup.js.map +1 -0
- package/dist/src/cli/style.d.ts +320 -0
- package/dist/src/cli/style.js +469 -0
- package/dist/src/cli/style.js.map +1 -0
- package/dist/src/cli/token.d.ts +39 -0
- package/dist/src/cli/token.js +274 -0
- package/dist/src/cli/token.js.map +1 -0
- package/dist/src/cli/up.d.ts +155 -0
- package/dist/src/cli/up.js +849 -0
- package/dist/src/cli/up.js.map +1 -0
- package/dist/src/cli/usage.d.ts +37 -0
- package/dist/src/cli/usage.js +91 -0
- package/dist/src/cli/usage.js.map +1 -0
- package/dist/src/cli/values.d.ts +40 -0
- package/dist/src/cli/values.js +189 -0
- package/dist/src/cli/values.js.map +1 -0
- package/dist/src/cli/vault.d.ts +59 -0
- package/dist/src/cli/vault.js +362 -0
- package/dist/src/cli/vault.js.map +1 -0
- package/dist/src/cli/verb-registry.d.ts +76 -0
- package/dist/src/cli/verb-registry.js +2341 -0
- package/dist/src/cli/verb-registry.js.map +1 -0
- package/dist/src/cli/wordmark.d.ts +31 -0
- package/dist/src/cli/wordmark.js +52 -0
- package/dist/src/cli/wordmark.js.map +1 -0
- package/dist/src/codex/doctor.d.ts +13 -0
- package/dist/src/codex/doctor.js +41 -0
- package/dist/src/codex/doctor.js.map +1 -0
- package/dist/src/codex/manifest.d.ts +49 -0
- package/dist/src/codex/manifest.js +103 -0
- package/dist/src/codex/manifest.js.map +1 -0
- package/dist/src/codex/templates.d.ts +41 -0
- package/dist/src/codex/templates.js +319 -0
- package/dist/src/codex/templates.js.map +1 -0
- package/dist/src/codex/trust.d.ts +19 -0
- package/dist/src/codex/trust.js +183 -0
- package/dist/src/codex/trust.js.map +1 -0
- package/dist/src/codex/workspace-plan.d.ts +131 -0
- package/dist/src/codex/workspace-plan.js +561 -0
- package/dist/src/codex/workspace-plan.js.map +1 -0
- package/dist/src/core/actor.d.ts +2 -0
- package/dist/src/core/actor.js +5 -0
- package/dist/src/core/actor.js.map +1 -0
- package/dist/src/core/advance-cycle.d.ts +170 -0
- package/dist/src/core/advance-cycle.js +200 -0
- package/dist/src/core/advance-cycle.js.map +1 -0
- package/dist/src/core/agents-md.d.ts +276 -0
- package/dist/src/core/agents-md.js +747 -0
- package/dist/src/core/agents-md.js.map +1 -0
- package/dist/src/core/apply-patch.d.ts +49 -0
- package/dist/src/core/apply-patch.js +266 -0
- package/dist/src/core/apply-patch.js.map +1 -0
- package/dist/src/core/attest.d.ts +420 -0
- package/dist/src/core/attest.js +589 -0
- package/dist/src/core/attest.js.map +1 -0
- package/dist/src/core/audit.d.ts +492 -0
- package/dist/src/core/audit.js +882 -0
- package/dist/src/core/audit.js.map +1 -0
- package/dist/src/core/budgets.d.ts +238 -0
- package/dist/src/core/budgets.js +449 -0
- package/dist/src/core/budgets.js.map +1 -0
- package/dist/src/core/checkpoint.d.ts +500 -0
- package/dist/src/core/checkpoint.js +738 -0
- package/dist/src/core/checkpoint.js.map +1 -0
- package/dist/src/core/child-env.d.ts +88 -0
- package/dist/src/core/child-env.js +86 -0
- package/dist/src/core/child-env.js.map +1 -0
- package/dist/src/core/clock.d.ts +52 -0
- package/dist/src/core/clock.js +43 -0
- package/dist/src/core/clock.js.map +1 -0
- package/dist/src/core/command-class.d.ts +543 -0
- package/dist/src/core/command-class.js +2356 -0
- package/dist/src/core/command-class.js.map +1 -0
- package/dist/src/core/coverage-sources/adapter.d.ts +40 -0
- package/dist/src/core/coverage-sources/adapter.js +71 -0
- package/dist/src/core/coverage-sources/adapter.js.map +1 -0
- package/dist/src/core/coverage-sources/gh.d.ts +48 -0
- package/dist/src/core/coverage-sources/gh.js +136 -0
- package/dist/src/core/coverage-sources/gh.js.map +1 -0
- package/dist/src/core/coverage-sources/git.d.ts +101 -0
- package/dist/src/core/coverage-sources/git.js +269 -0
- package/dist/src/core/coverage-sources/git.js.map +1 -0
- package/dist/src/core/coverage.d.ts +217 -0
- package/dist/src/core/coverage.js +337 -0
- package/dist/src/core/coverage.js.map +1 -0
- package/dist/src/core/credential-spec.d.ts +72 -0
- package/dist/src/core/credential-spec.js +23 -0
- package/dist/src/core/credential-spec.js.map +1 -0
- package/dist/src/core/dark-session.d.ts +331 -0
- package/dist/src/core/dark-session.js +714 -0
- package/dist/src/core/dark-session.js.map +1 -0
- package/dist/src/core/decision-refusal.d.ts +185 -0
- package/dist/src/core/decision-refusal.js +265 -0
- package/dist/src/core/decision-refusal.js.map +1 -0
- package/dist/src/core/env-file.d.ts +450 -0
- package/dist/src/core/env-file.js +837 -0
- package/dist/src/core/env-file.js.map +1 -0
- package/dist/src/core/execute.d.ts +858 -0
- package/dist/src/core/execute.js +1271 -0
- package/dist/src/core/execute.js.map +1 -0
- package/dist/src/core/frontmatter.d.ts +78 -0
- package/dist/src/core/frontmatter.js +100 -0
- package/dist/src/core/frontmatter.js.map +1 -0
- package/dist/src/core/gate-window.d.ts +312 -0
- package/dist/src/core/gate-window.js +506 -0
- package/dist/src/core/gate-window.js.map +1 -0
- package/dist/src/core/gate.d.ts +1364 -0
- package/dist/src/core/gate.js +3002 -0
- package/dist/src/core/gate.js.map +1 -0
- package/dist/src/core/git-run.d.ts +73 -0
- package/dist/src/core/git-run.js +93 -0
- package/dist/src/core/git-run.js.map +1 -0
- package/dist/src/core/harness-version.d.ts +157 -0
- package/dist/src/core/harness-version.js +211 -0
- package/dist/src/core/harness-version.js.map +1 -0
- package/dist/src/core/harness-wait.d.ts +55 -0
- package/dist/src/core/harness-wait.js +58 -0
- package/dist/src/core/harness-wait.js.map +1 -0
- package/dist/src/core/head-retry.d.ts +107 -0
- package/dist/src/core/head-retry.js +121 -0
- package/dist/src/core/head-retry.js.map +1 -0
- package/dist/src/core/instance.d.ts +253 -0
- package/dist/src/core/instance.js +319 -0
- package/dist/src/core/instance.js.map +1 -0
- package/dist/src/core/intake-limits.d.ts +247 -0
- package/dist/src/core/intake-limits.js +350 -0
- package/dist/src/core/intake-limits.js.map +1 -0
- package/dist/src/core/jcs.d.ts +52 -0
- package/dist/src/core/jcs.js +132 -0
- package/dist/src/core/jcs.js.map +1 -0
- package/dist/src/core/journal.d.ts +144 -0
- package/dist/src/core/journal.js +200 -0
- package/dist/src/core/journal.js.map +1 -0
- package/dist/src/core/live-draw.d.ts +436 -0
- package/dist/src/core/live-draw.js +703 -0
- package/dist/src/core/live-draw.js.map +1 -0
- package/dist/src/core/log-reconcile.d.ts +89 -0
- package/dist/src/core/log-reconcile.js +136 -0
- package/dist/src/core/log-reconcile.js.map +1 -0
- package/dist/src/core/log-subscribe.d.ts +36 -0
- package/dist/src/core/log-subscribe.js +162 -0
- package/dist/src/core/log-subscribe.js.map +1 -0
- package/dist/src/core/log.d.ts +278 -0
- package/dist/src/core/log.js +546 -0
- package/dist/src/core/log.js.map +1 -0
- package/dist/src/core/loop.d.ts +274 -0
- package/dist/src/core/loop.js +487 -0
- package/dist/src/core/loop.js.map +1 -0
- package/dist/src/core/md-fence.d.ts +41 -0
- package/dist/src/core/md-fence.js +74 -0
- package/dist/src/core/md-fence.js.map +1 -0
- package/dist/src/core/money.d.ts +147 -0
- package/dist/src/core/money.js +195 -0
- package/dist/src/core/money.js.map +1 -0
- package/dist/src/core/payload-census.d.ts +74 -0
- package/dist/src/core/payload-census.js +146 -0
- package/dist/src/core/payload-census.js.map +1 -0
- package/dist/src/core/payload-store.d.ts +175 -0
- package/dist/src/core/payload-store.js +340 -0
- package/dist/src/core/payload-store.js.map +1 -0
- package/dist/src/core/payload.d.ts +71 -0
- package/dist/src/core/payload.js +80 -0
- package/dist/src/core/payload.js.map +1 -0
- package/dist/src/core/policy-diff.d.ts +292 -0
- package/dist/src/core/policy-diff.js +588 -0
- package/dist/src/core/policy-diff.js.map +1 -0
- package/dist/src/core/policy-expectations.d.ts +199 -0
- package/dist/src/core/policy-expectations.js +394 -0
- package/dist/src/core/policy-expectations.js.map +1 -0
- package/dist/src/core/policy-explain.d.ts +150 -0
- package/dist/src/core/policy-explain.js +258 -0
- package/dist/src/core/policy-explain.js.map +1 -0
- package/dist/src/core/policy-load.d.ts +527 -0
- package/dist/src/core/policy-load.js +536 -0
- package/dist/src/core/policy-load.js.map +1 -0
- package/dist/src/core/policy-match.d.ts +281 -0
- package/dist/src/core/policy-match.js +478 -0
- package/dist/src/core/policy-match.js.map +1 -0
- package/dist/src/core/policy-proposal.d.ts +265 -0
- package/dist/src/core/policy-proposal.js +458 -0
- package/dist/src/core/policy-proposal.js.map +1 -0
- package/dist/src/core/prompt-layout.d.ts +221 -0
- package/dist/src/core/prompt-layout.js +422 -0
- package/dist/src/core/prompt-layout.js.map +1 -0
- package/dist/src/core/protected-path-guard.d.ts +453 -0
- package/dist/src/core/protected-path-guard.js +1566 -0
- package/dist/src/core/protected-path-guard.js.map +1 -0
- package/dist/src/core/registration.d.ts +25 -0
- package/dist/src/core/registration.js +39 -0
- package/dist/src/core/registration.js.map +1 -0
- package/dist/src/core/reindex.d.ts +99 -0
- package/dist/src/core/reindex.js +336 -0
- package/dist/src/core/reindex.js.map +1 -0
- package/dist/src/core/sampler.d.ts +313 -0
- package/dist/src/core/sampler.js +388 -0
- package/dist/src/core/sampler.js.map +1 -0
- package/dist/src/core/sandbox.d.ts +290 -0
- package/dist/src/core/sandbox.js +424 -0
- package/dist/src/core/sandbox.js.map +1 -0
- package/dist/src/core/seal.d.ts +165 -0
- package/dist/src/core/seal.js +290 -0
- package/dist/src/core/seal.js.map +1 -0
- package/dist/src/core/state.d.ts +505 -0
- package/dist/src/core/state.js +1009 -0
- package/dist/src/core/state.js.map +1 -0
- package/dist/src/core/task-file.d.ts +185 -0
- package/dist/src/core/task-file.js +464 -0
- package/dist/src/core/task-file.js.map +1 -0
- package/dist/src/core/telegram-config.d.ts +93 -0
- package/dist/src/core/telegram-config.js +114 -0
- package/dist/src/core/telegram-config.js.map +1 -0
- package/dist/src/core/token.d.ts +409 -0
- package/dist/src/core/token.js +561 -0
- package/dist/src/core/token.js.map +1 -0
- package/dist/src/core/validate.d.ts +138 -0
- package/dist/src/core/validate.js +0 -0
- package/dist/src/core/validate.js.map +1 -0
- package/dist/src/core/values.d.ts +137 -0
- package/dist/src/core/values.js +153 -0
- package/dist/src/core/values.js.map +1 -0
- package/dist/src/core/vault.d.ts +291 -0
- package/dist/src/core/vault.js +612 -0
- package/dist/src/core/vault.js.map +1 -0
- package/dist/src/core/verified-snapshot.d.ts +204 -0
- package/dist/src/core/verified-snapshot.js +506 -0
- package/dist/src/core/verified-snapshot.js.map +1 -0
- package/dist/src/core/verify.d.ts +336 -0
- package/dist/src/core/verify.js +549 -0
- package/dist/src/core/verify.js.map +1 -0
- package/dist/src/core/version.d.ts +8 -0
- package/dist/src/core/version.js +9 -0
- package/dist/src/core/version.js.map +1 -0
- package/dist/src/core/wysiwys.d.ts +370 -0
- package/dist/src/core/wysiwys.js +728 -0
- package/dist/src/core/wysiwys.js.map +1 -0
- package/dist/src/daemon/advance-child.d.ts +39 -0
- package/dist/src/daemon/advance-child.js +78 -0
- package/dist/src/daemon/advance-child.js.map +1 -0
- package/dist/src/daemon/advance.d.ts +466 -0
- package/dist/src/daemon/advance.js +849 -0
- package/dist/src/daemon/advance.js.map +1 -0
- package/dist/src/daemon/audit.d.ts +87 -0
- package/dist/src/daemon/audit.js +90 -0
- package/dist/src/daemon/audit.js.map +1 -0
- package/dist/src/daemon/daemon.d.ts +1180 -0
- package/dist/src/daemon/daemon.js +1988 -0
- package/dist/src/daemon/daemon.js.map +1 -0
- package/dist/src/daemon/dark-session.d.ts +64 -0
- package/dist/src/daemon/dark-session.js +119 -0
- package/dist/src/daemon/dark-session.js.map +1 -0
- package/dist/src/daemon/draw-child.d.ts +36 -0
- package/dist/src/daemon/draw-child.js +132 -0
- package/dist/src/daemon/draw-child.js.map +1 -0
- package/dist/src/daemon/draw.d.ts +154 -0
- package/dist/src/daemon/draw.js +458 -0
- package/dist/src/daemon/draw.js.map +1 -0
- package/dist/src/daemon/git-evidence.d.ts +173 -0
- package/dist/src/daemon/git-evidence.js +345 -0
- package/dist/src/daemon/git-evidence.js.map +1 -0
- package/dist/src/daemon/projection.d.ts +180 -0
- package/dist/src/daemon/projection.js +233 -0
- package/dist/src/daemon/projection.js.map +1 -0
- package/dist/src/daemon/prune.d.ts +207 -0
- package/dist/src/daemon/prune.js +376 -0
- package/dist/src/daemon/prune.js.map +1 -0
- package/dist/src/mcp/http.d.ts +113 -0
- package/dist/src/mcp/http.js +343 -0
- package/dist/src/mcp/http.js.map +1 -0
- package/dist/src/mcp/server.d.ts +265 -0
- package/dist/src/mcp/server.js +602 -0
- package/dist/src/mcp/server.js.map +1 -0
- package/docs/adapter-api.md +106 -0
- package/docs/cli-reference.md +5716 -0
- package/docs/codex-enforced-session.md +30 -0
- package/package.json +53 -4
- package/schema/.gitkeep +0 -0
- package/schema/LICENSE +117 -0
- package/schema/codex-instance.schema.json +82 -0
- package/schema/envelope.schema.json +137 -0
- package/schema/event.schema.json +1811 -0
- package/schema/fixtures/codex-instance/invalid/unpinned-codex-version.json +40 -0
- package/schema/fixtures/codex-instance/valid/canonical.json +40 -0
- package/schema/fixtures/envelope/invalid/action-missing-idempotency-key.json +15 -0
- package/schema/fixtures/envelope/invalid/action-unknown-class-format.json +14 -0
- package/schema/fixtures/envelope/invalid/confidence-out-of-range.json +11 -0
- package/schema/fixtures/envelope/invalid/est-cost-bare-number.json +14 -0
- package/schema/fixtures/envelope/invalid/est-cost-noncanonical-string.json +14 -0
- package/schema/fixtures/envelope/invalid/malformed-assignee.json +10 -0
- package/schema/fixtures/envelope/invalid/malformed-created-by.json +7 -0
- package/schema/fixtures/envelope/invalid/malformed-max-latency.json +11 -0
- package/schema/fixtures/envelope/invalid/malformed-payload-hash.json +14 -0
- package/schema/fixtures/envelope/invalid/max-cost-bare-number.json +11 -0
- package/schema/fixtures/envelope/invalid/missing-origin.json +3 -0
- package/schema/fixtures/envelope/invalid/negative-est-cost.json +14 -0
- package/schema/fixtures/envelope/invalid/unknown-state.json +7 -0
- package/schema/fixtures/envelope/invalid/unknown-top-level-field.json +8 -0
- package/schema/fixtures/envelope/valid/action-payload-hash.json +17 -0
- package/schema/fixtures/envelope/valid/actions-without-budget.json +18 -0
- package/schema/fixtures/envelope/valid/canonical.json +25 -0
- package/schema/fixtures/envelope/valid/minimal.json +7 -0
- package/schema/fixtures/envelope/valid/multi-action-executed.json +30 -0
- package/schema/fixtures/envelope/valid/record-write-stage.json +22 -0
- package/schema/fixtures/event/invalid/approval-granted-agent-actor.json +15 -0
- package/schema/fixtures/event/invalid/approval-granted-empty-batch-delivery-id.json +17 -0
- package/schema/fixtures/event/invalid/approval-granted-fifth-reaction.json +16 -0
- package/schema/fixtures/event/invalid/approval-granted-missing-actor.json +14 -0
- package/schema/fixtures/event/invalid/approval-requested-missing-action-key.json +14 -0
- package/schema/fixtures/event/invalid/approval-withdrawn-agent-policy-drift.json +15 -0
- package/schema/fixtures/event/invalid/approval-withdrawn-missing-reason.json +15 -0
- package/schema/fixtures/event/invalid/approval-withdrawn-system-actor.json +15 -0
- package/schema/fixtures/event/invalid/audit-decision-refused-human-actor.json +17 -0
- package/schema/fixtures/event/invalid/audit-decision-refused-missing-code.json +16 -0
- package/schema/fixtures/event/invalid/audit-reviewed-agent-actor.json +15 -0
- package/schema/fixtures/event/invalid/audit-reviewed-loved-no-note.json +16 -0
- package/schema/fixtures/event/invalid/audit-reviewed-system-actor.json +15 -0
- package/schema/fixtures/event/invalid/bad-actor-prefix.json +15 -0
- package/schema/fixtures/event/invalid/est-cost-bare-number.json +17 -0
- package/schema/fixtures/event/invalid/execution-completed-fabricated-exit-code.json +16 -0
- package/schema/fixtures/event/invalid/execution-completed-provider-ref-empty-id.json +18 -0
- package/schema/fixtures/event/invalid/execution-completed-provider-ref-extra-field.json +19 -0
- package/schema/fixtures/event/invalid/execution-completed-provider-ref-id-not-string.json +18 -0
- package/schema/fixtures/event/invalid/execution-completed-provider-ref-missing-adapter.json +17 -0
- package/schema/fixtures/event/invalid/execution-failed-open-reported-by.json +16 -0
- package/schema/fixtures/event/invalid/execution-indeterminate-open-reason.json +14 -0
- package/schema/fixtures/event/invalid/execution-reconciled-agent-actor.json +17 -0
- package/schema/fixtures/event/invalid/execution-started-negative-env-stripped.json +16 -0
- package/schema/fixtures/event/invalid/gate-bypassed-missing-opened-seq.json +15 -0
- package/schema/fixtures/event/invalid/gate-closed-non-integer-opened-seq.json +13 -0
- package/schema/fixtures/event/invalid/gate-opened-agent-actor.json +16 -0
- package/schema/fixtures/event/invalid/gate-organ-attested-absolute-path.json +14 -0
- package/schema/fixtures/event/invalid/gate-organ-attested-agent-actor.json +14 -0
- package/schema/fixtures/event/invalid/gate-organ-attested-missing-organ-path.json +13 -0
- package/schema/fixtures/event/invalid/harness-unknown-kind.json +18 -0
- package/schema/fixtures/event/invalid/harness-version-multiline.json +16 -0
- package/schema/fixtures/event/invalid/log-checkpoint-agent-actor.json +17 -0
- package/schema/fixtures/event/invalid/log-checkpoint-missing-signature.json +16 -0
- package/schema/fixtures/event/invalid/log-checkpoint-short-signed-hash.json +17 -0
- package/schema/fixtures/event/invalid/log-checkpoint-unknown-signature-alg.json +17 -0
- package/schema/fixtures/event/invalid/malformed-ts.json +15 -0
- package/schema/fixtures/event/invalid/missing-alg.json +14 -0
- package/schema/fixtures/event/invalid/missing-hash.json +14 -0
- package/schema/fixtures/event/invalid/non-integer-seq.json +15 -0
- package/schema/fixtures/event/invalid/payload-pruned-human-actor.json +14 -0
- package/schema/fixtures/event/invalid/payload-pruned-missing-hash.json +14 -0
- package/schema/fixtures/event/invalid/policy-declined-agent-actor.json +15 -0
- package/schema/fixtures/event/invalid/policy-proposed-missing-diff.json +19 -0
- package/schema/fixtures/event/invalid/policy-proposed-system-actor.json +26 -0
- package/schema/fixtures/event/invalid/short-hash.json +15 -0
- package/schema/fixtures/event/invalid/unknown-alg.json +15 -0
- package/schema/fixtures/event/invalid/unknown-event-type.json +15 -0
- package/schema/fixtures/event/invalid/unknown-top-level-field.json +16 -0
- package/schema/fixtures/event/valid/approval-expired.json +15 -0
- package/schema/fixtures/event/valid/approval-granted-batch.json +19 -0
- package/schema/fixtures/event/valid/approval-granted-reaction.json +16 -0
- package/schema/fixtures/event/valid/approval-granted.json +15 -0
- package/schema/fixtures/event/valid/approval-rejected.json +15 -0
- package/schema/fixtures/event/valid/approval-requested.json +19 -0
- package/schema/fixtures/event/valid/approval-revoked.json +15 -0
- package/schema/fixtures/event/valid/approval-withdrawn-policy-drift.json +17 -0
- package/schema/fixtures/event/valid/approval-withdrawn.json +16 -0
- package/schema/fixtures/event/valid/audit-decision-refused.json +20 -0
- package/schema/fixtures/event/valid/audit-reviewed-reaction.json +17 -0
- package/schema/fixtures/event/valid/audit-reviewed.json +15 -0
- package/schema/fixtures/event/valid/audit-sampled.json +14 -0
- package/schema/fixtures/event/valid/budget-exceeded.json +21 -0
- package/schema/fixtures/event/valid/envelope-drift.json +16 -0
- package/schema/fixtures/event/valid/execution-completed-harness-report.json +16 -0
- package/schema/fixtures/event/valid/execution-completed-provider-ref.json +18 -0
- package/schema/fixtures/event/valid/execution-completed.json +15 -0
- package/schema/fixtures/event/valid/execution-failed-harness-report.json +16 -0
- package/schema/fixtures/event/valid/execution-failed.json +15 -0
- package/schema/fixtures/event/valid/execution-indeterminate.json +15 -0
- package/schema/fixtures/event/valid/execution-reconciled.json +17 -0
- package/schema/fixtures/event/valid/execution-started-env-stripped.json +17 -0
- package/schema/fixtures/event/valid/execution-started.json +14 -0
- package/schema/fixtures/event/valid/gate-bypassed-harness-version.json +18 -0
- package/schema/fixtures/event/valid/gate-bypassed.json +19 -0
- package/schema/fixtures/event/valid/gate-closed.json +14 -0
- package/schema/fixtures/event/valid/gate-opened.json +16 -0
- package/schema/fixtures/event/valid/gate-organ-attested.json +14 -0
- package/schema/fixtures/event/valid/genesis-null-prev.json +14 -0
- package/schema/fixtures/event/valid/log-checkpoint.json +17 -0
- package/schema/fixtures/event/valid/payload-pruned-orphan.json +13 -0
- package/schema/fixtures/event/valid/payload-pruned.json +17 -0
- package/schema/fixtures/event/valid/policy-declined.json +16 -0
- package/schema/fixtures/event/valid/policy-proposed.json +35 -0
- package/schema/fixtures/event/valid/policy-updated.json +14 -0
- package/schema/fixtures/event/valid/reconciliation-required.json +18 -0
- package/schema/fixtures/event/valid/reconciliation-satisfied.json +17 -0
- package/schema/fixtures/event/valid/route-accepted.json +15 -0
- package/schema/fixtures/event/valid/route-proposed.json +16 -0
- package/schema/fixtures/event/valid/spec-example.json +15 -0
- package/schema/fixtures/event/valid/task-registered-harness-version.json +23 -0
- package/schema/fixtures/event/valid/task-registered.json +14 -0
- package/schema/fixtures/hash/known-answer-pre-121.json +74 -0
- package/schema/fixtures/hash/known-answer.json +74 -0
- package/schema/fixtures/policy/invalid/bad-approval-ttl.json +7 -0
- package/schema/fixtures/policy/invalid/bad-web-port.json +4 -0
- package/schema/fixtures/policy/invalid/checkpoint-key-not-base64.json +6 -0
- package/schema/fixtures/policy/invalid/class-rule-missing-autonomy.json +9 -0
- package/schema/fixtures/policy/invalid/empty-class-key.json +6 -0
- package/schema/fixtures/policy/invalid/live-rate-on-human-only.json +7 -0
- package/schema/fixtures/policy/invalid/malformed-class-key.json +6 -0
- package/schema/fixtures/policy/invalid/missing-version.json +8 -0
- package/schema/fixtures/policy/invalid/negative-limit.json +9 -0
- package/schema/fixtures/policy/invalid/non-numeric-limit.json +9 -0
- package/schema/fixtures/policy/invalid/non-positive-max-pending.json +9 -0
- package/schema/fixtures/policy/invalid/on-expiry-grant.json +8 -0
- package/schema/fixtures/policy/invalid/payload-retention-bare-number.json +4 -0
- package/schema/fixtures/policy/invalid/payload-retention-compound.json +4 -0
- package/schema/fixtures/policy/invalid/payload-retention-fractional.json +4 -0
- package/schema/fixtures/policy/invalid/payload-retention-zero.json +4 -0
- package/schema/fixtures/policy/invalid/protected-paths-escape.json +4 -0
- package/schema/fixtures/policy/invalid/protected-paths-glob.json +4 -0
- package/schema/fixtures/policy/invalid/retro-rate-on-human-only.json +7 -0
- package/schema/fixtures/policy/invalid/retro-rate-on-manual.json +7 -0
- package/schema/fixtures/policy/invalid/retro-rate-zero.json +7 -0
- package/schema/fixtures/policy/invalid/sample-rate-too-high.json +5 -0
- package/schema/fixtures/policy/invalid/sampling-secret-env-empty.json +7 -0
- package/schema/fixtures/policy/invalid/sampling-secret-env-not-string.json +6 -0
- package/schema/fixtures/policy/invalid/skew-tolerance-compound.json +6 -0
- package/schema/fixtures/policy/invalid/unknown-autonomy.json +7 -0
- package/schema/fixtures/policy/invalid/unknown-class-rule-key.json +6 -0
- package/schema/fixtures/policy/invalid/unknown-top-level-key.json +7 -0
- package/schema/fixtures/policy/invalid/vault-passphrase-env-empty.json +6 -0
- package/schema/fixtures/policy/invalid/vault-passphrase-literal.json +6 -0
- package/schema/fixtures/policy/invalid/version-not-string.json +4 -0
- package/schema/fixtures/policy/valid/canonical.json +47 -0
- package/schema/fixtures/policy/valid/checkpoint-keys.json +18 -0
- package/schema/fixtures/policy/valid/class-approvers-limits.json +25 -0
- package/schema/fixtures/policy/valid/class-retro-rate.json +17 -0
- package/schema/fixtures/policy/valid/global-budgets.json +19 -0
- package/schema/fixtures/policy/valid/human-only.json +9 -0
- package/schema/fixtures/policy/valid/minimal.json +6 -0
- package/schema/fixtures/policy/valid/protected-paths.json +10 -0
- package/schema/fixtures/policy/valid/record-namespace.json +13 -0
- package/schema/fixtures/policy/valid/request-volume-limits.json +26 -0
- package/schema/fixtures/policy/valid/retention-and-sampling-secret.json +16 -0
- package/schema/fixtures/policy/valid/skew-tolerance.json +15 -0
- package/schema/fixtures/policy/valid/vault-passphrase-env.json +14 -0
- package/schema/fixtures/policy/valid/wildcards.json +15 -0
- package/schema/fixtures/policy-md/invalid/alias-bomb.md +15 -0
- package/schema/fixtures/policy-md/invalid/no-fence.md +7 -0
- package/schema/fixtures/policy-md/invalid/protected-route-not-a-subclass.md +16 -0
- package/schema/fixtures/policy-md/invalid/schema-invalid-autonomy.md +16 -0
- package/schema/fixtures/policy-md/invalid/schema-invalid-read-proof.md +17 -0
- package/schema/fixtures/policy-md/invalid/two-fences.md +19 -0
- package/schema/fixtures/policy-md/invalid/unclosed-fence.md +11 -0
- package/schema/fixtures/policy-md/invalid/wrong-info-string.md +11 -0
- package/schema/fixtures/policy-md/invalid/yaml-syntax-error.md +13 -0
- package/schema/fixtures/policy-md/precedence/both/APPROVAL.md +7 -0
- package/schema/fixtures/policy-md/precedence/both/APPROVALS.md +7 -0
- package/schema/fixtures/policy-md/precedence/fallback-only/APPROVALS.md +7 -0
- package/schema/fixtures/policy-md/valid/canonical.md +50 -0
- package/schema/fixtures/policy-md/valid/daemon-read-proof.md +18 -0
- package/schema/fixtures/policy-md/valid/minimal.md +3 -0
- package/schema/fixtures/policy-md/valid/prose-lookalikes.md +54 -0
- package/schema/fixtures/policy-md/valid/routed-protected-paths.md +49 -0
- package/schema/fixtures/policy-md/valid/with-values.md +79 -0
- package/schema/fixtures/sample-record/invalid/bad-date-time.json +4 -0
- package/schema/fixtures/sample-record/invalid/missing-required-field.json +3 -0
- package/schema/fixtures/sample-record/invalid/unknown-top-level-field.json +5 -0
- package/schema/fixtures/sample-record/invalid/wrong-type.json +4 -0
- package/schema/fixtures/sample-record/valid/minimal.json +4 -0
- package/schema/fixtures/sample-record/valid/with-note.json +5 -0
- package/schema/fixtures/values/invalid/class-shaped.json +9 -0
- package/schema/fixtures/values/invalid/duplicate-entry.json +4 -0
- package/schema/fixtures/values/invalid/non-string-item.json +4 -0
- package/schema/fixtures/values/invalid/over-cap.json +26 -0
- package/schema/fixtures/values/invalid/unknown-key.json +5 -0
- package/schema/fixtures/values/invalid/version-string.json +1 -0
- package/schema/fixtures/values/valid/empty-lists.json +7 -0
- package/schema/fixtures/values/valid/full.json +20 -0
- package/schema/fixtures/values/valid/minimal.json +1 -0
- package/schema/fixtures/values-md/invalid/schema-invalid.md +62 -0
- package/schema/fixtures/values-md/invalid/two-blocks.md +69 -0
- package/schema/fixtures/values-md/invalid/unterminated.md +61 -0
- package/schema/fixtures/values-md/invalid/yaml-error.md +63 -0
- package/schema/fixtures/values-md/valid/absent.md +50 -0
- package/schema/fixtures/values-md/valid/with-values.md +79 -0
- package/schema/policy.schema.json +501 -0
- package/schema/sample-record.schema.json +26 -0
- package/schema/values.schema.json +55 -0
- package/templates/codex/README.md +9 -0
|
@@ -0,0 +1,723 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The runtime-side tagger: log + policy in, render-ready request out
|
|
3
|
+
* (SPEC.md §9, §10.3, §10.4).
|
|
4
|
+
*
|
|
5
|
+
* This is the only place a {@link ChannelRequest} is built, and it is on the
|
|
6
|
+
* runtime side of the boundary on purpose. Every `computed` field here is
|
|
7
|
+
* derived by the same modules the gate itself uses — `core/state.ts` for the
|
|
8
|
+
* verified read and the approval derivation, `core/policy-match.ts` /
|
|
9
|
+
* `policy-explain.ts` for autonomy, `core/budgets.ts` for the verdicts,
|
|
10
|
+
* `core/attest.ts` for the attestation status, `core/payload.ts` for the
|
|
11
|
+
* content binding — so what a channel displays as "computed" is the same answer
|
|
12
|
+
* the gate would give, not a second implementation that could disagree with it.
|
|
13
|
+
* Every `claimed` field is copied out of a log record and stamped with the actor
|
|
14
|
+
* who authored that record.
|
|
15
|
+
*
|
|
16
|
+
* ## Why this takes a log *path*, not records
|
|
17
|
+
*
|
|
18
|
+
* SPEC.md §11.1(1): enforcement paths read only verified records. A channel
|
|
19
|
+
* request is what a human's decision is made from, so it is an enforcement
|
|
20
|
+
* surface in the sense that matters — a request built from a spliced log would
|
|
21
|
+
* put a fabricated action in front of an approver. Accepting an
|
|
22
|
+
* `EventRecord[]` would let a caller hand over anything; taking the path and
|
|
23
|
+
* calling {@link readVerifiedRecords} here means the chain is walked, the hashes
|
|
24
|
+
* recomputed and the schemas checked before a single field is tagged, and a log
|
|
25
|
+
* that does not verify produces a refusal rather than a queue entry.
|
|
26
|
+
*
|
|
27
|
+
* ## Where the full payload comes from — flagged for human review
|
|
28
|
+
*
|
|
29
|
+
* v0.1's log records a `payload_hash`, never the payload bytes: the binding is
|
|
30
|
+
* a commitment, and putting the bytes in an append-only log would make every
|
|
31
|
+
* approved payload permanent and world-readable to anyone with the log. So the
|
|
32
|
+
* material to render comes from the payload store beside the log
|
|
33
|
+
* (`core/payload-store.ts`, APRV-28), or from a caller-supplied override
|
|
34
|
+
* ({@link TagOptions.payload}) where an operator holds the bytes somewhere else,
|
|
35
|
+
* and this module **verifies it against the recorded hash** before tagging it
|
|
36
|
+
* `computed` — material that does not hash to the bound value is refused
|
|
37
|
+
* `payload-mismatch` and never reaches a channel. That verification is what
|
|
38
|
+
* makes `fullPayload` a computed field rather than one more agent claim, and it
|
|
39
|
+
* is the same check `core/token.ts` makes at spend time.
|
|
40
|
+
*
|
|
41
|
+
* Determinism: no clock. `now` is a parameter, so a queue rendering is
|
|
42
|
+
* replayable exactly as it was rendered.
|
|
43
|
+
*/
|
|
44
|
+
import { createHash } from "node:crypto";
|
|
45
|
+
import { existsSync } from "node:fs";
|
|
46
|
+
import { join } from "node:path";
|
|
47
|
+
import { checkAttestation, policyFileHash, } from "../core/attest.js";
|
|
48
|
+
import { evaluateBudgetsWithTask, } from "../core/budgets.js";
|
|
49
|
+
import { usdNumber } from "../core/money.js";
|
|
50
|
+
import { payloadHash } from "../core/payload.js";
|
|
51
|
+
import { loadPayload, payloadStoreDirFor } from "../core/payload-store.js";
|
|
52
|
+
import { explain } from "../core/policy-explain.js";
|
|
53
|
+
import { loadPolicy, POLICY_FILENAMES, } from "../core/policy-load.js";
|
|
54
|
+
import { resolve } from "../core/policy-match.js";
|
|
55
|
+
import { ATTESTATION_CLASS, isAttestationActionKey, openProposals, } from "../core/policy-proposal.js";
|
|
56
|
+
import { isRecipientKey, RECIPIENT_KEY_FIELD } from "../core/seal.js";
|
|
57
|
+
import { payloadOf, readVerifiedRecords, requestState, } from "../core/state.js";
|
|
58
|
+
import { claimed, computed, createChannelRequest, } from "./contract.js";
|
|
59
|
+
import { commandBreakdown, commandPayloadView, protectedPathView } from "./payload-view.js";
|
|
60
|
+
/** Why the tagger refused. Frozen per SPEC.md §11.1(6). */
|
|
61
|
+
export const CHANNEL_TAG_REFUSAL_CODES = [
|
|
62
|
+
/** The log could not be opened. */
|
|
63
|
+
"log-unreadable",
|
|
64
|
+
/** The log's final line is unterminated (a crashed write). */
|
|
65
|
+
"log-torn-tail",
|
|
66
|
+
/** The chain does not verify; nothing may be rendered from it. */
|
|
67
|
+
"log-corrupt",
|
|
68
|
+
/** No `approval.requested` record for this action key. */
|
|
69
|
+
"not-requested",
|
|
70
|
+
/** There is a request, but it is decided or expired — nothing to approve. */
|
|
71
|
+
"not-awaiting",
|
|
72
|
+
/** The request carries no usable `payload.class`; policy cannot be resolved. */
|
|
73
|
+
"class-missing",
|
|
74
|
+
/** The request carries no `payload_hash`; there is no binding to display. */
|
|
75
|
+
"payload-hash-missing",
|
|
76
|
+
/** No payload material was supplied for a manual request (§10.4). */
|
|
77
|
+
"payload-unavailable",
|
|
78
|
+
/** The supplied material does not hash to the bound `payload_hash`. */
|
|
79
|
+
"payload-mismatch",
|
|
80
|
+
/** The supplied material cannot be canonicalized (a cycle, a NaN, …). */
|
|
81
|
+
"payload-unrenderable",
|
|
82
|
+
/** {@link createChannelRequest} refused the assembled request. */
|
|
83
|
+
"request-invalid",
|
|
84
|
+
/**
|
|
85
|
+
* An attestation prompt's policy bytes are no longer the bytes on disk
|
|
86
|
+
* (APRV-109).
|
|
87
|
+
*
|
|
88
|
+
* Skipped rather than rendered: the hash, the diff and the advisory the
|
|
89
|
+
* proposal recorded all describe a file that has since changed, and a prompt
|
|
90
|
+
* showing them would ask a human to sign for bytes nobody is holding. The
|
|
91
|
+
* repair is to propose the amendment again, which re-derives all three from
|
|
92
|
+
* the file as it now stands.
|
|
93
|
+
*/
|
|
94
|
+
"proposal-stale",
|
|
95
|
+
];
|
|
96
|
+
function refuse(code, message) {
|
|
97
|
+
return { ok: false, code, message };
|
|
98
|
+
}
|
|
99
|
+
// ---------------------------------------------------------------------------
|
|
100
|
+
// Policy plumbing (mirrors `core/gate.ts`, deliberately)
|
|
101
|
+
// ---------------------------------------------------------------------------
|
|
102
|
+
/**
|
|
103
|
+
* The policy file that will be hashed for attestation.
|
|
104
|
+
*
|
|
105
|
+
* Same discovery order as `core/gate.ts` and `loadPolicy`, so the file the
|
|
106
|
+
* channel reports as attested is the file the gate will enforce. Duplicated
|
|
107
|
+
* rather than imported because the gate's copy is private; the alternative —
|
|
108
|
+
* widening the gate's API for a display path — trades a nine-line function for
|
|
109
|
+
* a new public surface on the module that decides things.
|
|
110
|
+
*/
|
|
111
|
+
function policyPathOf(options) {
|
|
112
|
+
const policy = options.policy ?? {};
|
|
113
|
+
if (policy.file !== undefined)
|
|
114
|
+
return policy.file;
|
|
115
|
+
const dir = policy.dir ?? process.cwd();
|
|
116
|
+
for (const filename of POLICY_FILENAMES) {
|
|
117
|
+
const candidate = join(dir, filename);
|
|
118
|
+
if (existsSync(candidate))
|
|
119
|
+
return candidate;
|
|
120
|
+
}
|
|
121
|
+
return join(dir, POLICY_FILENAMES[0] ?? "APPROVAL.md");
|
|
122
|
+
}
|
|
123
|
+
function loadOptionsOf(options) {
|
|
124
|
+
const policy = options.policy ?? {};
|
|
125
|
+
const load = {};
|
|
126
|
+
if (policy.file !== undefined)
|
|
127
|
+
load.file = policy.file;
|
|
128
|
+
else
|
|
129
|
+
load.dir = policy.dir ?? process.cwd();
|
|
130
|
+
if (options.schemaDir !== undefined)
|
|
131
|
+
load.schemaDir = options.schemaDir;
|
|
132
|
+
return load;
|
|
133
|
+
}
|
|
134
|
+
function budgetScopeOf(load, resolution) {
|
|
135
|
+
return {
|
|
136
|
+
classLimits: resolution.limits,
|
|
137
|
+
classPattern: resolution.matched === null ? null : resolution.matched.pattern,
|
|
138
|
+
globalBudgets: load.ok ? load.policy.budgets ?? null : null,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
function ttlOf(load) {
|
|
142
|
+
return load.ok ? load.durations.approvalTtlMs : null;
|
|
143
|
+
}
|
|
144
|
+
// ---------------------------------------------------------------------------
|
|
145
|
+
// Log lookups
|
|
146
|
+
// ---------------------------------------------------------------------------
|
|
147
|
+
/** The `approval.requested` record at `seq`, or `null`. */
|
|
148
|
+
function recordAt(records, seq) {
|
|
149
|
+
if (seq === null)
|
|
150
|
+
return null;
|
|
151
|
+
for (const record of records) {
|
|
152
|
+
if (record.seq === seq)
|
|
153
|
+
return record;
|
|
154
|
+
}
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* The latest `task.registered` payload for `task`, or `{}`.
|
|
159
|
+
*
|
|
160
|
+
* Read for `route.rationale` / `route.confidence` only. v0.1's `register`
|
|
161
|
+
* copies `actions`, `state` and `budget` onto the record and *not* `route`, so
|
|
162
|
+
* these are usually absent — the fields are wired anyway, tagged claimed with
|
|
163
|
+
* the registering actor, so a log that carries them (a later register, an
|
|
164
|
+
* imported log) renders them on the correct side of the boundary rather than
|
|
165
|
+
* being quietly dropped or, worse, shown as fact.
|
|
166
|
+
*/
|
|
167
|
+
function registrationOf(records, task) {
|
|
168
|
+
if (task === null)
|
|
169
|
+
return null;
|
|
170
|
+
let found = null;
|
|
171
|
+
for (const record of records) {
|
|
172
|
+
if (record.event === "task.registered" && record.task === task)
|
|
173
|
+
found = record;
|
|
174
|
+
}
|
|
175
|
+
return found;
|
|
176
|
+
}
|
|
177
|
+
function routeOf(registration) {
|
|
178
|
+
if (registration === null)
|
|
179
|
+
return {};
|
|
180
|
+
const route = payloadOf(registration)["route"];
|
|
181
|
+
return typeof route === "object" && route !== null && !Array.isArray(route)
|
|
182
|
+
? route
|
|
183
|
+
: {};
|
|
184
|
+
}
|
|
185
|
+
// ---------------------------------------------------------------------------
|
|
186
|
+
// Payload rendering
|
|
187
|
+
// ---------------------------------------------------------------------------
|
|
188
|
+
/**
|
|
189
|
+
* Render the payload material and check it against the recorded binding.
|
|
190
|
+
*
|
|
191
|
+
* The hash is recomputed here, never taken on trust: `payload-mismatch` is the
|
|
192
|
+
* same refusal `core/token.ts` raises at spend time, raised earlier, so a human
|
|
193
|
+
* is never shown bytes that the token would later refuse to execute.
|
|
194
|
+
*/
|
|
195
|
+
function renderPayload(material, boundHash, maxChars) {
|
|
196
|
+
let hash;
|
|
197
|
+
let text;
|
|
198
|
+
try {
|
|
199
|
+
hash = payloadHash(material);
|
|
200
|
+
text = JSON.stringify(material, null, 2) ?? String(material);
|
|
201
|
+
}
|
|
202
|
+
catch (cause) {
|
|
203
|
+
return refuse("payload-unrenderable", `the payload material could not be canonicalized: ${cause instanceof Error ? cause.message : String(cause)}. A payload that cannot be serialized cannot be bound to, so it cannot be approved.`);
|
|
204
|
+
}
|
|
205
|
+
if (hash !== boundHash) {
|
|
206
|
+
return refuse("payload-mismatch", `the supplied payload material hashes to ${hash} but the request is bound to ${boundHash} (amended SPEC.md §6.2/§10). A grant approves specific bytes; showing a human different bytes than the token will execute is the exact failure the binding exists to prevent.`);
|
|
207
|
+
}
|
|
208
|
+
const truncated = maxChars !== undefined && text.length > maxChars;
|
|
209
|
+
return {
|
|
210
|
+
ok: true,
|
|
211
|
+
rendering: {
|
|
212
|
+
value: material,
|
|
213
|
+
text: truncated ? text.slice(0, maxChars) : text,
|
|
214
|
+
hash,
|
|
215
|
+
truncated,
|
|
216
|
+
},
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* The material for one action: the caller's override, else the payload store.
|
|
221
|
+
*
|
|
222
|
+
* Three outcomes, and the middle one is the point of the whole store. `none`
|
|
223
|
+
* means nobody holds the bytes — the caller passed no source (or it returned
|
|
224
|
+
* nothing) and the store has no file — which is `payload-unavailable` for a
|
|
225
|
+
* manual request. `refusal` means the store *does* hold a file and it does not
|
|
226
|
+
* verify: a tampered `<hash>.json` is reported as `payload-mismatch` and its
|
|
227
|
+
* contents are never returned, let alone rendered. `material` is the ordinary
|
|
228
|
+
* case, and it is still hash-checked downstream by {@link renderPayload}, so the
|
|
229
|
+
* store is verified twice on the path to a human and trusted at neither step.
|
|
230
|
+
*
|
|
231
|
+
* A stored external reference is `none` with a reason: the pointer is reported
|
|
232
|
+
* in the refusal message, and no bytes are invented for it.
|
|
233
|
+
*/
|
|
234
|
+
function materialFor(options, actionKey, boundHash) {
|
|
235
|
+
const supplied = options.payload === undefined ? undefined : options.payload(actionKey, boundHash);
|
|
236
|
+
if (supplied !== undefined)
|
|
237
|
+
return { kind: "material", value: supplied };
|
|
238
|
+
const storeDir = options.payloadStoreDir;
|
|
239
|
+
if (storeDir === undefined || storeDir === null)
|
|
240
|
+
return { kind: "none", reason: null };
|
|
241
|
+
const loaded = loadPayload(storeDir, boundHash);
|
|
242
|
+
if (loaded.ok)
|
|
243
|
+
return { kind: "material", value: loaded.value };
|
|
244
|
+
if (loaded.code === "hash-mismatch") {
|
|
245
|
+
return { kind: "refusal", refusal: refuse("payload-mismatch", loaded.message) };
|
|
246
|
+
}
|
|
247
|
+
return { kind: "none", reason: loaded.code === "absent" ? null : loaded.message };
|
|
248
|
+
}
|
|
249
|
+
// ---------------------------------------------------------------------------
|
|
250
|
+
// buildChannelRequest
|
|
251
|
+
// ---------------------------------------------------------------------------
|
|
252
|
+
/**
|
|
253
|
+
* The options a build runs with: the caller's, plus the store convention.
|
|
254
|
+
*
|
|
255
|
+
* Resolved from the log path the caller already passes, so every surface —
|
|
256
|
+
* `approval render`, the CLI channel, web, Telegram — reads the same store
|
|
257
|
+
* without a flag, and a caller that wants no store at all says
|
|
258
|
+
* `payloadStoreDir: null` rather than being unable to say it.
|
|
259
|
+
*/
|
|
260
|
+
function withStore(options, logPath) {
|
|
261
|
+
return options.payloadStoreDir === undefined
|
|
262
|
+
? { ...options, payloadStoreDir: payloadStoreDirFor(logPath) }
|
|
263
|
+
: options;
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Build the render-ready request for one action key.
|
|
267
|
+
*
|
|
268
|
+
* Computed, and what derives each:
|
|
269
|
+
*
|
|
270
|
+
* | field | derivation | `source` |
|
|
271
|
+
* | --- | --- | --- |
|
|
272
|
+
* | `class`, `task`, `payload_hash`, `requested_ts`, `chain`, `state` | the verified log | `log` |
|
|
273
|
+
* | `autonomy`, `provenance` | `explain()` over the attested policy | `policy-match` |
|
|
274
|
+
* | `budgets` | `evaluateBudgetsWithTask()` at `now` | `budgets` |
|
|
275
|
+
* | `attestation` | `checkAttestation()` against the live policy file | `attestation` |
|
|
276
|
+
* | `fullPayload` | supplied material, hash-checked | `payload-binding` |
|
|
277
|
+
* | `command_breakdown`, `protected_path` | the classifier, re-run over the hash-checked material | `classifier` |
|
|
278
|
+
* | `ttl_remaining_ms`, `waiting` | arithmetic on `now` | `clock` |
|
|
279
|
+
*
|
|
280
|
+
* Claimed, and who authored each: `summary` and `est_cost_usd` carry the actor
|
|
281
|
+
* of the `approval.requested` record — the party that submitted the declaration
|
|
282
|
+
* — and `rationale` / `confidence` carry the registering actor.
|
|
283
|
+
*
|
|
284
|
+
* Refuses an unknown or undecidable key rather than rendering a partial one:
|
|
285
|
+
* `not-requested` (no such request), `not-awaiting` (already granted, rejected,
|
|
286
|
+
* revoked or expired), and the payload codes above. An unverifiable log refuses
|
|
287
|
+
* `log-corrupt` before anything is derived.
|
|
288
|
+
*/
|
|
289
|
+
export function buildChannelRequest(logPath, actionKey, options, now) {
|
|
290
|
+
const read = readVerifiedRecords(logPath, options.schemaDir === undefined ? {} : { schemaDir: options.schemaDir });
|
|
291
|
+
if (!read.ok)
|
|
292
|
+
return refuse(read.code, read.message);
|
|
293
|
+
const load = loadPolicy(loadOptionsOf(options));
|
|
294
|
+
// APRV-109. An attestation prompt has no `approval.requested` to derive from,
|
|
295
|
+
// so it is resolved from its own open proposal before the approval path runs.
|
|
296
|
+
if (isAttestationActionKey(actionKey)) {
|
|
297
|
+
const proposal = openProposals(read.records, now).find((entry) => entry.actionKey === actionKey);
|
|
298
|
+
if (proposal === undefined) {
|
|
299
|
+
return refuse("not-awaiting", `${actionKey} has no open policy.proposed record; the attestation prompt was answered, superseded, or its proposer's deadline passed, and a channel may only collect a gesture on a live question`);
|
|
300
|
+
}
|
|
301
|
+
return tagProposal(read.records, read.head?.seq ?? 0, proposal, load, withStore(options, logPath), now);
|
|
302
|
+
}
|
|
303
|
+
const derivation = requestState(read.records, actionKey, now, ttlOf(load));
|
|
304
|
+
return tagDerivation(read.records, read.head?.seq ?? 0, derivation, load, withStore(options, logPath), now);
|
|
305
|
+
}
|
|
306
|
+
/** Month names for the dated form of {@link clockText}. UTC, like everything here. */
|
|
307
|
+
const MONTHS = [
|
|
308
|
+
"Jan",
|
|
309
|
+
"Feb",
|
|
310
|
+
"Mar",
|
|
311
|
+
"Apr",
|
|
312
|
+
"May",
|
|
313
|
+
"Jun",
|
|
314
|
+
"Jul",
|
|
315
|
+
"Aug",
|
|
316
|
+
"Sep",
|
|
317
|
+
"Oct",
|
|
318
|
+
"Nov",
|
|
319
|
+
"Dec",
|
|
320
|
+
];
|
|
321
|
+
/** Whole UTC days since the epoch. The day boundary is the displayed zone's. */
|
|
322
|
+
function utcDay(ms) {
|
|
323
|
+
return Math.floor(ms / 86_400_000);
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* A deadline, said the way a reader on a phone can act on it (APRV-143).
|
|
327
|
+
*
|
|
328
|
+
* ```
|
|
329
|
+
* 13:09 UTC the deadline is later today
|
|
330
|
+
* tomorrow 13:09 UTC the deadline is the next UTC day
|
|
331
|
+
* 27 Aug 13:09 UTC anything further out, and anything already past
|
|
332
|
+
* ```
|
|
333
|
+
*
|
|
334
|
+
* The observed failure is a 24h TTL rendering as `expires 13:09 UTC` beside
|
|
335
|
+
* `requested 1 min ago`: a reader does the arithmetic, gets "nine minutes ago",
|
|
336
|
+
* and concludes the question is dead. Time of day alone can only be read
|
|
337
|
+
* against a day the line never states.
|
|
338
|
+
*
|
|
339
|
+
* Day boundaries are computed in UTC because the clock is printed in UTC, and a
|
|
340
|
+
* "tomorrow" measured in one zone against a time printed in another is worse
|
|
341
|
+
* than no word at all. `now` unreadable degrades to the time-only form: a
|
|
342
|
+
* renderer that cannot place today must not name a relative day.
|
|
343
|
+
*/
|
|
344
|
+
function clockText(ms, nowMs) {
|
|
345
|
+
const at = new Date(ms);
|
|
346
|
+
const hh = String(at.getUTCHours()).padStart(2, "0");
|
|
347
|
+
const mm = String(at.getUTCMinutes()).padStart(2, "0");
|
|
348
|
+
const clock = `${hh}:${mm} UTC`;
|
|
349
|
+
if (Number.isNaN(nowMs))
|
|
350
|
+
return clock;
|
|
351
|
+
const days = utcDay(ms) - utcDay(nowMs);
|
|
352
|
+
if (days === 0)
|
|
353
|
+
return clock;
|
|
354
|
+
if (days === 1)
|
|
355
|
+
return `tomorrow ${clock}`;
|
|
356
|
+
return `${String(at.getUTCDate())} ${MONTHS[at.getUTCMonth()] ?? "?"} ${clock}`;
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* `just now`, `4 min ago`, `2h 05m ago` — how long the question has waited.
|
|
360
|
+
*
|
|
361
|
+
* Exported since APRV-216 so the Telegram queue summary states a request's age
|
|
362
|
+
* in the same words the request's own prompt does. One phrasing and one
|
|
363
|
+
* rounding: a summary calling a question `2h 05m` old beside a prompt that
|
|
364
|
+
* calls it something else leaves a reader deciding which of the two the
|
|
365
|
+
* runtime meant.
|
|
366
|
+
*/
|
|
367
|
+
export function ageText(ms) {
|
|
368
|
+
const minutes = Math.floor(ms / 60_000);
|
|
369
|
+
if (minutes < 1)
|
|
370
|
+
return "just now";
|
|
371
|
+
if (minutes < 60)
|
|
372
|
+
return `${String(minutes)} min ago`;
|
|
373
|
+
return `${String(Math.floor(minutes / 60))}h ${String(minutes % 60).padStart(2, "0")}m ago`;
|
|
374
|
+
}
|
|
375
|
+
/**
|
|
376
|
+
* The age/deadline line of APRV-106. See `ChannelRequest.waiting` for why it is
|
|
377
|
+
* computed and why the requester's own `wait_until` cannot lower scrutiny.
|
|
378
|
+
*
|
|
379
|
+
* Determinism: `now` is the parameter every other field here is derived
|
|
380
|
+
* against, so a queue rendered twice at the same instant produces the same
|
|
381
|
+
* line. Unparseable instants degrade to the honest half rather than to a
|
|
382
|
+
* guess — a channel that cannot say when a request was made must not imply it
|
|
383
|
+
* was made recently.
|
|
384
|
+
*/
|
|
385
|
+
function waitingLine(requestTs, waitUntil, ttlRemainingMs, now) {
|
|
386
|
+
const requestedAt = Date.parse(requestTs);
|
|
387
|
+
const nowMs = Date.parse(now);
|
|
388
|
+
const age = Number.isNaN(requestedAt) || Number.isNaN(nowMs)
|
|
389
|
+
? `requested at ${requestTs}`
|
|
390
|
+
: `requested ${ageText(Math.max(0, nowMs - requestedAt))}`;
|
|
391
|
+
const waitMs = waitUntil === null ? Number.NaN : Date.parse(waitUntil);
|
|
392
|
+
if (!Number.isNaN(waitMs)) {
|
|
393
|
+
return `${age} · requester waits until ${clockText(waitMs, nowMs)}`;
|
|
394
|
+
}
|
|
395
|
+
if (ttlRemainingMs !== null && !Number.isNaN(nowMs)) {
|
|
396
|
+
return `${age} · expires ${clockText(nowMs + ttlRemainingMs, nowMs)}`;
|
|
397
|
+
}
|
|
398
|
+
// No TTL and no declared wait: the policy bounded nothing, and inventing a
|
|
399
|
+
// deadline here would be the renderer stating a fact the log does not carry.
|
|
400
|
+
return `${age} · no deadline (the policy declares no approval_ttl)`;
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* `policy.protected_paths`, or nothing when the policy did not load.
|
|
404
|
+
*
|
|
405
|
+
* Narrower on a failed load, never wider: an unreadable policy under-reports
|
|
406
|
+
* which paths are protected rather than inventing a protection, and autonomy
|
|
407
|
+
* has already failed closed to `manual` by the time this is read.
|
|
408
|
+
*/
|
|
409
|
+
function protectedPathsOf(load) {
|
|
410
|
+
return load.ok ? (load.policy.protected_paths ?? []) : [];
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* The two payload-derived computed lines: what the command does (APRV-144 #1)
|
|
414
|
+
* and which protected path selected the class (APRV-143 #3).
|
|
415
|
+
*
|
|
416
|
+
* Both are derived from the payload material this function was already handed —
|
|
417
|
+
* material that {@link renderPayload} has hash-checked against the recorded
|
|
418
|
+
* binding — and both go through `core/command-class.ts` rather than reading a
|
|
419
|
+
* claim off the bytes. A payload nobody holds, or one of a shape neither
|
|
420
|
+
* derivation recognises, produces neither line: an aid that cannot be derived
|
|
421
|
+
* is absent, never guessed.
|
|
422
|
+
*/
|
|
423
|
+
/**
|
|
424
|
+
* The `token_delivery` line, for a request that published a delivery address
|
|
425
|
+
* (APRV-105), and nothing at all for one that did not.
|
|
426
|
+
*
|
|
427
|
+
* Computed from `log`: the key comes off the verified `approval.requested`
|
|
428
|
+
* record and is digested here. A malformed value produces no line rather than a
|
|
429
|
+
* line saying something is wrong — the delivery address is a convenience, and a
|
|
430
|
+
* broken one costs the requester its own convenience and costs the approver
|
|
431
|
+
* nothing they need for the decision.
|
|
432
|
+
*/
|
|
433
|
+
function deliveryDerivation(recipientKey) {
|
|
434
|
+
if (!isRecipientKey(recipientKey))
|
|
435
|
+
return {};
|
|
436
|
+
const digest = createHash("sha256").update(recipientKey, "utf8").digest("hex").slice(0, 16);
|
|
437
|
+
return {
|
|
438
|
+
token_delivery: computed(`sealed to x25519:${digest} — granting delivers the token to the requesting process, not only to this screen`, "log"),
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
function payloadDerivations(rendering, load) {
|
|
442
|
+
if (rendering === null)
|
|
443
|
+
return {};
|
|
444
|
+
const fields = {};
|
|
445
|
+
const command = commandPayloadView(rendering.value);
|
|
446
|
+
if (command !== null) {
|
|
447
|
+
const breakdown = commandBreakdown(command.command);
|
|
448
|
+
if (breakdown !== null)
|
|
449
|
+
fields.command_breakdown = computed(breakdown, "classifier");
|
|
450
|
+
}
|
|
451
|
+
const guarded = protectedPathView(rendering.value, protectedPathsOf(load));
|
|
452
|
+
if (guarded !== null) {
|
|
453
|
+
fields.protected_path = computed(`${guarded.path} (rule ${guarded.rule})`, "classifier");
|
|
454
|
+
}
|
|
455
|
+
return fields;
|
|
456
|
+
}
|
|
457
|
+
/** The shared body of {@link buildChannelRequest} and {@link buildPendingQueue}. */
|
|
458
|
+
function tagDerivation(records, headSeq, derivation, load, options, now) {
|
|
459
|
+
const actionKey = derivation.actionKey;
|
|
460
|
+
if (derivation.state === "none") {
|
|
461
|
+
return refuse("not-requested", `action ${actionKey} has no approval.requested record; there is nothing pending to render`);
|
|
462
|
+
}
|
|
463
|
+
if (derivation.state !== "requested") {
|
|
464
|
+
return refuse("not-awaiting", `action ${actionKey} is ${derivation.state}, not awaiting a decision; a channel may only collect a gesture on a live request`);
|
|
465
|
+
}
|
|
466
|
+
const cls = derivation.declared.class;
|
|
467
|
+
if (cls === null || cls.length === 0) {
|
|
468
|
+
return refuse("class-missing", `the approval.requested record for ${actionKey} carries no usable payload.class; autonomy and every class-scoped budget are resolved from it, so nothing can be shown as computed`);
|
|
469
|
+
}
|
|
470
|
+
const boundHash = derivation.declared.payload_hash;
|
|
471
|
+
if (boundHash === null) {
|
|
472
|
+
return refuse("payload-hash-missing", `the approval.requested record for ${actionKey} carries no payload_hash; amended SPEC.md §6.2 makes the binding mandatory for manual actions, so a request without one cannot be presented for decision`);
|
|
473
|
+
}
|
|
474
|
+
const requestRecord = recordAt(records, derivation.requestSeq);
|
|
475
|
+
if (requestRecord === null || derivation.requestTs === null) {
|
|
476
|
+
return refuse("not-requested", `the approval.requested record for ${actionKey} could not be located in the verified log`);
|
|
477
|
+
}
|
|
478
|
+
const reversible = derivation.declared.reversible;
|
|
479
|
+
const explanation = explain(load, cls, reversible === null ? {} : { reversible });
|
|
480
|
+
const resolution = resolve(load, cls, reversible === null ? {} : { reversible });
|
|
481
|
+
// The declared amount in its canonical decimal form (APRV-121) for the
|
|
482
|
+
// budget evaluation, and as a number for the card. A channel card is a
|
|
483
|
+
// display surface: nothing it renders is hashed, so the float lives here and
|
|
484
|
+
// goes no further.
|
|
485
|
+
const cost = derivation.declared.est_cost_usd ?? "0";
|
|
486
|
+
const budgets = evaluateBudgetsWithTask(records, budgetScopeOf(load, resolution), { class: cls, est_cost_usd: cost }, now, derivation.task).verdicts;
|
|
487
|
+
const attestation = checkAttestation(records, policyPathOf(options));
|
|
488
|
+
const material = materialFor(options, actionKey, boundHash);
|
|
489
|
+
if (material.kind === "refusal")
|
|
490
|
+
return material.refusal;
|
|
491
|
+
let rendering = null;
|
|
492
|
+
if (material.kind === "material") {
|
|
493
|
+
const rendered = renderPayload(material.value, boundHash, options.maxPayloadChars);
|
|
494
|
+
if (!rendered.ok)
|
|
495
|
+
return rendered;
|
|
496
|
+
rendering = rendered.rendering;
|
|
497
|
+
}
|
|
498
|
+
else if (explanation.outcome.autonomy === "manual") {
|
|
499
|
+
return refuse("payload-unavailable", `no payload material is held for manual action ${actionKey}: the caller supplied none and the payload store has none${material.reason === null ? "" : ` (${material.reason})`}. SPEC.md §10.4 requires a channel to present the full payload or a faithful rendering of it before collecting a decision, so the request is refused here rather than delivered as a summary alone.`);
|
|
500
|
+
}
|
|
501
|
+
const ttlMs = ttlOf(load);
|
|
502
|
+
const requestedAt = Date.parse(derivation.requestTs);
|
|
503
|
+
const nowMs = Date.parse(now);
|
|
504
|
+
const ttlRemaining = ttlMs === null || Number.isNaN(requestedAt) || Number.isNaN(nowMs)
|
|
505
|
+
? null
|
|
506
|
+
: Math.max(0, requestedAt + ttlMs - nowMs);
|
|
507
|
+
const route = routeOf(registrationOf(records, derivation.task));
|
|
508
|
+
const registrationActor = registrationOf(records, derivation.task)?.actor ?? requestRecord.actor;
|
|
509
|
+
const fields = {
|
|
510
|
+
action_key: computed(actionKey, "log"),
|
|
511
|
+
task: computed(derivation.task, "log"),
|
|
512
|
+
class: computed(cls, "log"),
|
|
513
|
+
autonomy: computed(explanation.outcome.autonomy, "policy-match"),
|
|
514
|
+
provenance: computed(explanation.provenance, "policy-match"),
|
|
515
|
+
est_cost_usd: claimed(usdNumber(cost), requestRecord.actor),
|
|
516
|
+
summary: claimed(derivation.declared.summary, requestRecord.actor),
|
|
517
|
+
...payloadDerivations(rendering, load),
|
|
518
|
+
payload_hash: computed(boundHash, "log"),
|
|
519
|
+
// APRV-105. Present only when the request published a delivery address, so
|
|
520
|
+
// an approver is told when granting will hand a readable token to the
|
|
521
|
+
// requesting process rather than only to this screen. Digested rather than
|
|
522
|
+
// printed whole: the key is public and harmless, but several hundred
|
|
523
|
+
// characters of base64 in a prompt is several hundred characters nobody
|
|
524
|
+
// reads, and a digest is enough to tell two requests' addresses apart.
|
|
525
|
+
...deliveryDerivation(payloadOf(requestRecord)[RECIPIENT_KEY_FIELD]),
|
|
526
|
+
fullPayload: computed(rendering, "payload-binding"),
|
|
527
|
+
budgets: computed(budgets, "budgets"),
|
|
528
|
+
attestation: computed(attestation, "attestation"),
|
|
529
|
+
requested_ts: computed(derivation.requestTs, "log"),
|
|
530
|
+
ttl_remaining_ms: computed(ttlRemaining, "clock"),
|
|
531
|
+
waiting: computed(waitingLine(derivation.requestTs, derivation.declared.wait_until, ttlRemaining, now), "clock"),
|
|
532
|
+
chain: computed({ seq: requestRecord.seq, hash: requestRecord.hash, head_seq: headSeq }, "log"),
|
|
533
|
+
state: computed(derivation.state, "log"),
|
|
534
|
+
...(typeof route["rationale"] === "string"
|
|
535
|
+
? { rationale: claimed(route["rationale"], registrationActor) }
|
|
536
|
+
: {}),
|
|
537
|
+
...(typeof route["confidence"] === "number"
|
|
538
|
+
? { confidence: claimed(route["confidence"], registrationActor) }
|
|
539
|
+
: {}),
|
|
540
|
+
};
|
|
541
|
+
const created = createChannelRequest(fields);
|
|
542
|
+
if (!created.ok)
|
|
543
|
+
return refuse("request-invalid", created.message);
|
|
544
|
+
return { ok: true, request: created.request };
|
|
545
|
+
}
|
|
546
|
+
// ---------------------------------------------------------------------------
|
|
547
|
+
// Attestation prompts (APRV-109)
|
|
548
|
+
// ---------------------------------------------------------------------------
|
|
549
|
+
/** The diff summary a proposal recorded, or `null` when it recorded none. */
|
|
550
|
+
function diffSummaryOf(record) {
|
|
551
|
+
const value = payloadOf(record)["diff"];
|
|
552
|
+
if (typeof value !== "object" || value === null)
|
|
553
|
+
return null;
|
|
554
|
+
const summary = value;
|
|
555
|
+
const lines = Array.isArray(summary["lines"])
|
|
556
|
+
? summary["lines"].filter((line) => typeof line === "string")
|
|
557
|
+
: [];
|
|
558
|
+
return {
|
|
559
|
+
available: summary["available"] === true,
|
|
560
|
+
reason: typeof summary["reason"] === "string" ? summary["reason"] : null,
|
|
561
|
+
lines,
|
|
562
|
+
headline: typeof summary["headline"] === "string" ? summary["headline"] : "",
|
|
563
|
+
baseline_sha256: typeof summary["baseline_sha256"] === "string" ? summary["baseline_sha256"] : null,
|
|
564
|
+
};
|
|
565
|
+
}
|
|
566
|
+
/** The diff, as the one block of text every channel prints. */
|
|
567
|
+
function diffText(summary) {
|
|
568
|
+
if (!summary.available) {
|
|
569
|
+
return `HASH-ONLY: no semantic diff. ${summary.reason ?? "no verifiable baseline"}. What changed in MEANING is not shown, so read the file diff at a terminal before deciding.`;
|
|
570
|
+
}
|
|
571
|
+
return [`${summary.headline}:`, ...summary.lines].join("\n");
|
|
572
|
+
}
|
|
573
|
+
/** The load advisory, recomputed here from the live bytes at display time. */
|
|
574
|
+
function loadText(load) {
|
|
575
|
+
return load.ok
|
|
576
|
+
? "loads clean"
|
|
577
|
+
: `DOES NOT LOAD (${load.code}): ${load.message}. Attesting it is allowed — an attestation records bytes, not correctness — but every class will FAIL CLOSED to manual.`;
|
|
578
|
+
}
|
|
579
|
+
/**
|
|
580
|
+
* Tag one open `policy.proposed` as a manual prompt (APRV-109).
|
|
581
|
+
*
|
|
582
|
+
* The prompt is an ordinary {@link ChannelRequest} and that is the whole design:
|
|
583
|
+
* telegram, web and cli render it through the paths they already have, the
|
|
584
|
+
* computed/claimed split applies unchanged, and `channels/conformance.ts` checks
|
|
585
|
+
* it like any other request. Two additive computed members carry what an
|
|
586
|
+
* attestation needs and an approval does not — the semantic diff and the load
|
|
587
|
+
* advisory — and everything else lines up with a field that already existed.
|
|
588
|
+
*
|
|
589
|
+
* Three of those alignments are worth stating, because a reader will ask:
|
|
590
|
+
*
|
|
591
|
+
* - **`autonomy` is `manual` with provenance `floor`.** No `classes` rule
|
|
592
|
+
* decides this and none could: attestation is human-only in code (SPEC.md
|
|
593
|
+
* §11) and the act is irreversible, since the log is append-only and attested
|
|
594
|
+
* bytes are operative from that record onward. The prompt reports a floor the
|
|
595
|
+
* policy cannot lower rather than a rule it does not have.
|
|
596
|
+
* - **`est_cost_usd` is COMPUTED zero, not a claim.** An attestation buys
|
|
597
|
+
* nothing and spends nothing; there is no estimate for a proposer to author,
|
|
598
|
+
* so there is nothing here to label claimed.
|
|
599
|
+
* - **`attestation` is the LIVE status**, which on an open prompt is
|
|
600
|
+
* `hash-mismatch` or `not-attested`. That is the fact the approver is being
|
|
601
|
+
* asked to change, so it belongs on the screen exactly as it belongs on every
|
|
602
|
+
* other prompt.
|
|
603
|
+
*
|
|
604
|
+
* The live file is re-hashed here and a prompt whose bytes have moved is refused
|
|
605
|
+
* `proposal-stale` rather than rendered: `core/policy-proposal.ts` would refuse
|
|
606
|
+
* the tap for the same reason, and a queue that showed the question anyway would
|
|
607
|
+
* be inviting a gesture the gate has already decided to reject.
|
|
608
|
+
*/
|
|
609
|
+
function tagProposal(records, headSeq, proposal, load, options, now) {
|
|
610
|
+
const record = proposal.record;
|
|
611
|
+
const payload = payloadOf(record);
|
|
612
|
+
const policyPath = policyPathOf(options);
|
|
613
|
+
let liveSha256;
|
|
614
|
+
try {
|
|
615
|
+
liveSha256 = policyFileHash(policyPath);
|
|
616
|
+
}
|
|
617
|
+
catch (cause) {
|
|
618
|
+
return refuse("proposal-stale", `the attestation prompt at seq ${String(record.seq)} proposes ${proposal.sha256} and ${policyPath} could not be read to confirm it (${cause instanceof Error ? cause.message : String(cause)}); a prompt whose bytes nobody can read is not a prompt anybody can answer`);
|
|
619
|
+
}
|
|
620
|
+
if (liveSha256 !== proposal.sha256) {
|
|
621
|
+
return refuse("proposal-stale", `the attestation prompt at seq ${String(record.seq)} proposes ${proposal.sha256} and ${policyPath} now hashes ${liveSha256}; the file changed after the question was asked, so the hash, the diff and the advisory on this prompt all describe bytes that are no longer there. Propose the amendment again.`);
|
|
622
|
+
}
|
|
623
|
+
const boundHash = payload["payload_hash"];
|
|
624
|
+
if (typeof boundHash !== "string" || boundHash.length === 0) {
|
|
625
|
+
return refuse("payload-hash-missing", `the policy.proposed record at seq ${String(record.seq)} carries no payload_hash, so the full policy text it binds cannot be located; SPEC.md §10.4 requires the bytes to be presentable before a decision is collected`);
|
|
626
|
+
}
|
|
627
|
+
const material = materialFor(options, proposal.actionKey, boundHash);
|
|
628
|
+
if (material.kind === "refusal")
|
|
629
|
+
return material.refusal;
|
|
630
|
+
if (material.kind !== "material") {
|
|
631
|
+
return refuse("payload-unavailable", `no policy text is held for the attestation prompt at seq ${String(record.seq)}: the caller supplied none and the payload store has none${material.reason === null ? "" : ` (${material.reason})`}. SPEC.md §10.4 requires a channel to present the full payload before collecting a decision, and for an attestation that payload is the policy file itself.`);
|
|
632
|
+
}
|
|
633
|
+
const rendered = renderPayload(material.value, boundHash, options.maxPayloadChars);
|
|
634
|
+
if (!rendered.ok)
|
|
635
|
+
return rendered;
|
|
636
|
+
const summary = diffSummaryOf(record);
|
|
637
|
+
if (summary === null) {
|
|
638
|
+
return refuse("request-invalid", `the policy.proposed record at seq ${String(record.seq)} carries no diff summary; a prompt that showed only a hash would ask a human to sign for sixty-four characters, which is the failure APRV-109 exists to prevent`);
|
|
639
|
+
}
|
|
640
|
+
const waitUntil = typeof payload["wait_until"] === "string" ? payload["wait_until"] : null;
|
|
641
|
+
const nowMs = Date.parse(now);
|
|
642
|
+
const waitMs = waitUntil === null ? Number.NaN : Date.parse(waitUntil);
|
|
643
|
+
const remaining = Number.isNaN(waitMs) || Number.isNaN(nowMs) ? null : Math.max(0, waitMs - nowMs);
|
|
644
|
+
const fields = {
|
|
645
|
+
action_key: computed(proposal.actionKey, "log"),
|
|
646
|
+
task: computed(null, "log"),
|
|
647
|
+
class: computed(ATTESTATION_CLASS, "log"),
|
|
648
|
+
autonomy: computed("manual", "attestation"),
|
|
649
|
+
provenance: computed("floor", "attestation"),
|
|
650
|
+
est_cost_usd: computed(0, "attestation"),
|
|
651
|
+
summary: claimed(typeof payload["note"] === "string" ? payload["note"] : null, record.actor),
|
|
652
|
+
policy_diff: computed(diffText(summary), "log"),
|
|
653
|
+
policy_load: computed(loadText(load), "policy-load"),
|
|
654
|
+
payload_hash: computed(boundHash, "log"),
|
|
655
|
+
fullPayload: computed(rendered.rendering, "payload-binding"),
|
|
656
|
+
budgets: computed([], "budgets"),
|
|
657
|
+
attestation: computed(checkAttestation(records, policyPath), "attestation"),
|
|
658
|
+
requested_ts: computed(record.ts, "log"),
|
|
659
|
+
ttl_remaining_ms: computed(remaining, "clock"),
|
|
660
|
+
waiting: computed(waitingLine(record.ts, waitUntil, remaining, now), "clock"),
|
|
661
|
+
chain: computed({ seq: record.seq, hash: record.hash, head_seq: headSeq }, "log"),
|
|
662
|
+
state: computed("requested", "log"),
|
|
663
|
+
};
|
|
664
|
+
const created = createChannelRequest(fields);
|
|
665
|
+
if (!created.ok)
|
|
666
|
+
return refuse("request-invalid", created.message);
|
|
667
|
+
return { ok: true, request: created.request };
|
|
668
|
+
}
|
|
669
|
+
/**
|
|
670
|
+
* Build the pending queue: every action key with a live `approval.requested`.
|
|
671
|
+
*
|
|
672
|
+
* Order is log order (oldest request first), which is the order SPEC.md §9's
|
|
673
|
+
* queue projection asks for. One verified read serves the whole queue, and one
|
|
674
|
+
* policy load serves every entry, so an entry cannot disagree with its
|
|
675
|
+
* neighbour about what the policy says.
|
|
676
|
+
*/
|
|
677
|
+
export function buildPendingQueue(logPath, options, now) {
|
|
678
|
+
const read = readVerifiedRecords(logPath, options.schemaDir === undefined ? {} : { schemaDir: options.schemaDir });
|
|
679
|
+
if (!read.ok)
|
|
680
|
+
return refuse(read.code, read.message);
|
|
681
|
+
const load = loadPolicy(loadOptionsOf(options));
|
|
682
|
+
const ttlMs = ttlOf(load);
|
|
683
|
+
const headSeq = read.head?.seq ?? 0;
|
|
684
|
+
const withStoreOptions = withStore(options, logPath);
|
|
685
|
+
const keys = [];
|
|
686
|
+
for (const record of read.records) {
|
|
687
|
+
if (record.event !== "approval.requested")
|
|
688
|
+
continue;
|
|
689
|
+
const key = record.action_key;
|
|
690
|
+
if (key === undefined || keys.includes(key))
|
|
691
|
+
continue;
|
|
692
|
+
keys.push(key);
|
|
693
|
+
}
|
|
694
|
+
const requests = [];
|
|
695
|
+
const skipped = [];
|
|
696
|
+
for (const key of keys) {
|
|
697
|
+
const derivation = requestState(read.records, key, now, ttlMs);
|
|
698
|
+
if (derivation.state !== "requested")
|
|
699
|
+
continue;
|
|
700
|
+
const built = tagDerivation(read.records, headSeq, derivation, load, withStoreOptions, now);
|
|
701
|
+
if (built.ok)
|
|
702
|
+
requests.push(built.request);
|
|
703
|
+
else
|
|
704
|
+
skipped.push({ action_key: key, code: built.code, message: built.message });
|
|
705
|
+
}
|
|
706
|
+
// APRV-109. Attestation prompts join the same queue, so every channel that
|
|
707
|
+
// already dispatches pending approvals dispatches these too — no channel
|
|
708
|
+
// learns a second verb, and the one that cannot render a prompt reports it in
|
|
709
|
+
// `skipped` exactly as it reports an approval it cannot render. They come
|
|
710
|
+
// AFTER the approvals: a policy amendment changes the rules the entries above
|
|
711
|
+
// it were routed by, and an approver reading top to bottom should answer the
|
|
712
|
+
// questions asked under the current policy before changing what the current
|
|
713
|
+
// policy is.
|
|
714
|
+
for (const proposal of openProposals(read.records, now)) {
|
|
715
|
+
const built = tagProposal(read.records, headSeq, proposal, load, withStoreOptions, now);
|
|
716
|
+
if (built.ok)
|
|
717
|
+
requests.push(built.request);
|
|
718
|
+
else
|
|
719
|
+
skipped.push({ action_key: proposal.actionKey, code: built.code, message: built.message });
|
|
720
|
+
}
|
|
721
|
+
return { ok: true, requests, skipped };
|
|
722
|
+
}
|
|
723
|
+
//# sourceMappingURL=tagging.js.map
|