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,247 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Request-volume limits at intake (SPEC.md §5.2, APRV-173).
|
|
3
|
+
*
|
|
4
|
+
* > **Request-volume limits.** `limits.max_pending` (per class or global):
|
|
5
|
+
* > maximum simultaneously pending requests; further requests are refused at
|
|
6
|
+
* > intake with reason `queue-full`. `limits.requests_per_hour` (per origin):
|
|
7
|
+
* > rolling-window ceiling on request creation; excess is refused with reason
|
|
8
|
+
* > `rate-limited`.
|
|
9
|
+
*
|
|
10
|
+
* This module is that computation. It was policy vocabulary that no runtime
|
|
11
|
+
* read until this task: a policy could declare either key, the schema would
|
|
12
|
+
* accept it, and nothing enforced it.
|
|
13
|
+
*
|
|
14
|
+
* ## What these limits protect
|
|
15
|
+
*
|
|
16
|
+
* Budgets meter money and action counts, which is the world's exposure to the
|
|
17
|
+
* agent. These meter the QUEUE, which is the human's exposure to the agent. An
|
|
18
|
+
* agent that floods the queue with cheap, in-budget requests spends nothing and
|
|
19
|
+
* still defeats the gate, because an approver facing two hundred prompts stops
|
|
20
|
+
* reading them and starts clearing them. So the ceiling here is on attention,
|
|
21
|
+
* and a refusal costs the requester one exit code and the approver nothing.
|
|
22
|
+
*
|
|
23
|
+
* That difference is why a refusal on this path appends NOTHING (Carter's
|
|
24
|
+
* approved reading, 2026-08-31). `budget.exceeded` exists because a budget
|
|
25
|
+
* refusal is a fact about a commitment that audit must be able to reconstruct;
|
|
26
|
+
* a queue-flood refusal is a fact about a flood, and writing one log record per
|
|
27
|
+
* refused request would hand the flooder the log growth it was refused the
|
|
28
|
+
* queue for. The refusals are machine-readable and distinct (§11.1 invariant
|
|
29
|
+
* 6), which is what an agent and an auditor each need, and the events that WERE
|
|
30
|
+
* admitted are still all in the log to count from.
|
|
31
|
+
*
|
|
32
|
+
* ## Pure, deterministic, injected time
|
|
33
|
+
*
|
|
34
|
+
* Same discipline as `core/budgets.ts`: no I/O, no clock, no randomness. The
|
|
35
|
+
* evaluation instant and the TTL are parameters, so a verdict is replayable
|
|
36
|
+
* from the log alone. Nothing here re-runs class matching; the gate hands in
|
|
37
|
+
* the limits the matcher already resolved and the pattern that produced them.
|
|
38
|
+
*
|
|
39
|
+
* ## What "pending" means
|
|
40
|
+
*
|
|
41
|
+
* A request is pending when {@link requestState} derives `requested` for its
|
|
42
|
+
* action key at the evaluation instant: no grant, rejection, revocation,
|
|
43
|
+
* withdrawal, expiry event or lapsed TTL, and no execution. That derivation is
|
|
44
|
+
* `core/state.ts`'s and is the same one the gate refuses `duplicate-request`
|
|
45
|
+
* from and the channels build the queue from. Counting pending requests with a
|
|
46
|
+
* second, private definition is the one thing this module must not do — the
|
|
47
|
+
* cap a policy writes is a cap on the queue a human is shown, so it counts the
|
|
48
|
+
* queue a human is shown.
|
|
49
|
+
*
|
|
50
|
+
* ## What "per origin" means at v0.1
|
|
51
|
+
*
|
|
52
|
+
* **Origin is the record's `actor`.** The approved reading (Carter,
|
|
53
|
+
* 2026-08-31): the actor on `approval.requested` is assigned by the runtime
|
|
54
|
+
* from its own configuration rather than from the request, and under MCP the
|
|
55
|
+
* `--as` argument is appended last, so a caller cannot choose the identity its
|
|
56
|
+
* requests are counted under. Per-guest actors therefore make the ceiling
|
|
57
|
+
* per-client, which is the useful shape. This is a v0.1 reading of the spec's
|
|
58
|
+
* word "origin" rather than a claim that actor and origin are the same thing
|
|
59
|
+
* forever: an origin richer than the actor (a session, a remote address) would
|
|
60
|
+
* be a spec amendment, and it would only ever partition the count further.
|
|
61
|
+
*
|
|
62
|
+
* ## The rolling window
|
|
63
|
+
*
|
|
64
|
+
* `requests_per_hour` is measured over the hour preceding the evaluation
|
|
65
|
+
* instant, tiled exactly as `core/budgets.ts` tiles its 24 hours: a request
|
|
66
|
+
* consumes iff
|
|
67
|
+
*
|
|
68
|
+
* evaluationTs - 1h < record.ts <= evaluationTs
|
|
69
|
+
*
|
|
70
|
+
* half-open at the bottom, closed at the top, so consecutive windows tile the
|
|
71
|
+
* timeline and no request is counted in two of them. A request stamped exactly
|
|
72
|
+
* one hour before the evaluation instant has aged out; one stamped at the
|
|
73
|
+
* instant itself is in.
|
|
74
|
+
*
|
|
75
|
+
* ## Fail-closed, and what "unset" means
|
|
76
|
+
*
|
|
77
|
+
* A limit that is DECLARED and cannot be evaluated fails: a value that is not a
|
|
78
|
+
* positive finite integer, or an evaluation timestamp no window can be computed
|
|
79
|
+
* from, yields `pass: false` with a note. Silence is never a grant.
|
|
80
|
+
*
|
|
81
|
+
* A limit that is NOT declared enforces nothing, and that is the conservative
|
|
82
|
+
* answer rather than an exception to it. SPEC.md §5.2 says these ceilings are a
|
|
83
|
+
* tripwire whose "defaults are generous"; a runtime that invented one would
|
|
84
|
+
* refuse requests under a policy the human attested and read, and the refusal
|
|
85
|
+
* would name a number that appears nowhere in the file they signed. The
|
|
86
|
+
* fail-closed direction for an ABSENT request-volume limit is the same one the
|
|
87
|
+
* rest of the runtime takes for an absent `approval_ttl`: the policy declares
|
|
88
|
+
* no ceiling, so no ceiling binds, and the manual gate that binds every request
|
|
89
|
+
* on this path is what stands between the queue and the flood in the meantime.
|
|
90
|
+
* Where an operator wants a tripwire, one line of policy arms it.
|
|
91
|
+
*
|
|
92
|
+
* ## Division of labour with `core/budgets.ts`
|
|
93
|
+
*
|
|
94
|
+
* These two names are evaluated HERE and nowhere else. `core/budgets.ts` skips
|
|
95
|
+
* them by name rather than refusing them as unknown limits, and this module
|
|
96
|
+
* ignores every limit name that is not one of them. The two skip lists are
|
|
97
|
+
* complements, and each module's comment names the other: a limit that both
|
|
98
|
+
* skipped would be a ceiling in the policy file that no code enforces, which is
|
|
99
|
+
* the exact defect this task exists to close.
|
|
100
|
+
*/
|
|
101
|
+
import type { EventRecord } from "./log.js";
|
|
102
|
+
import type { Policy } from "./policy-load.js";
|
|
103
|
+
/** Length of the `requests_per_hour` window: one hour, in milliseconds. */
|
|
104
|
+
export declare const REQUEST_WINDOW_MS: number;
|
|
105
|
+
/** The limit name capping simultaneously pending requests (SPEC.md §5.2). */
|
|
106
|
+
export declare const MAX_PENDING = "max_pending";
|
|
107
|
+
/** The limit name capping request creation per origin (SPEC.md §5.2). */
|
|
108
|
+
export declare const REQUESTS_PER_HOUR = "requests_per_hour";
|
|
109
|
+
/**
|
|
110
|
+
* The two names this module owns.
|
|
111
|
+
*
|
|
112
|
+
* Exported so `core/budgets.ts` skips exactly these and no others: one array,
|
|
113
|
+
* read by both modules, so the two skip lists cannot drift apart into a limit
|
|
114
|
+
* nobody evaluates.
|
|
115
|
+
*/
|
|
116
|
+
export declare const INTAKE_LIMIT_NAMES: readonly ["max_pending", "requests_per_hour"];
|
|
117
|
+
/** Is `name` a request-volume limit (and therefore not a budget's business)? */
|
|
118
|
+
export declare function isIntakeLimitName(name: string): boolean;
|
|
119
|
+
/** The refusal a failing limit produces at intake. Mirrors SPEC.md §5.2. */
|
|
120
|
+
export type IntakeRefusal = "queue-full" | "rate-limited";
|
|
121
|
+
/**
|
|
122
|
+
* Which limits apply, as resolved by the policy matcher — this module does not
|
|
123
|
+
* re-run matching. Shaped like `BudgetScope` deliberately.
|
|
124
|
+
*
|
|
125
|
+
* - `classLimits` is `Resolution.limits`: the winning rule's `limits` map.
|
|
126
|
+
* - `classPattern` is that rule's pattern. Class-scoped counting attributes a
|
|
127
|
+
* record by matching its `payload.class` against this pattern, exactly as
|
|
128
|
+
* budgets attribute, so one `financial.*` rule is one queue ceiling shared by
|
|
129
|
+
* every class it governs rather than a separate invisible queue per class.
|
|
130
|
+
* - `globalBudgets` is `policy.budgets`: named scopes, each conjunctive, each
|
|
131
|
+
* counting every live request whatever its class. That is what makes it
|
|
132
|
+
* global, and it is the same reading `core/budgets.ts` gives `daily_actions`.
|
|
133
|
+
*/
|
|
134
|
+
export interface IntakeScope {
|
|
135
|
+
classLimits: Record<string, number> | null;
|
|
136
|
+
classPattern: string | null;
|
|
137
|
+
globalBudgets: Policy["budgets"] | null;
|
|
138
|
+
}
|
|
139
|
+
/** The request being admitted: its class, and the origin it is counted under. */
|
|
140
|
+
export interface IntakeAction {
|
|
141
|
+
class: string;
|
|
142
|
+
/** The requesting actor. See "What per origin means" in the header. */
|
|
143
|
+
origin: string;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* One limit's outcome.
|
|
147
|
+
*
|
|
148
|
+
* `observed` is what the log already holds (pending requests, or requests in
|
|
149
|
+
* the window), `requested` is always `1` — the request being admitted — and
|
|
150
|
+
* `remaining` is the headroom left after admitting it, so a failing verdict
|
|
151
|
+
* shows how far over the line the queue is.
|
|
152
|
+
*
|
|
153
|
+
* Plain integers rather than the decimal strings `BudgetVerdict` carries. Those
|
|
154
|
+
* are strings because a failing budget verdict is copied into `budget.exceeded`
|
|
155
|
+
* and becomes hashed material; these verdicts are never appended anywhere, and
|
|
156
|
+
* a count has no fractional part to serialize differently in another language.
|
|
157
|
+
*/
|
|
158
|
+
export interface IntakeVerdict {
|
|
159
|
+
/** `max_pending`, `requests_per_hour`, or `<scope>.max_pending`. */
|
|
160
|
+
limit: string;
|
|
161
|
+
scope: "class" | "global";
|
|
162
|
+
window: "simultaneous" | "rolling-1h";
|
|
163
|
+
/** The refusal code this verdict produces when it fails. */
|
|
164
|
+
refusal: IntakeRefusal;
|
|
165
|
+
observed: number;
|
|
166
|
+
requested: number;
|
|
167
|
+
remaining: number;
|
|
168
|
+
/** The declared ceiling, or `null` when it could not be read as one. */
|
|
169
|
+
ceiling: number | null;
|
|
170
|
+
pass: boolean;
|
|
171
|
+
/** Present only when the verdict needs explaining (fail-closed refusals). */
|
|
172
|
+
note?: string;
|
|
173
|
+
}
|
|
174
|
+
/** Outcome of {@link evaluateIntakeLimits}. Conjunctive: all must pass. */
|
|
175
|
+
export interface IntakeVerdicts {
|
|
176
|
+
pass: boolean;
|
|
177
|
+
verdicts: IntakeVerdict[];
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* How many requests are simultaneously pending at `evaluationTs`.
|
|
181
|
+
*
|
|
182
|
+
* Derived through {@link requestState}, one action key at a time, so every
|
|
183
|
+
* exit from the queue is honoured by the definition that owns it: a decision,
|
|
184
|
+
* a revocation, a withdrawal (APRV-106), an `approval.expired` record, a TTL
|
|
185
|
+
* lapsed by arithmetic with no record at all, and an execution. Nothing here
|
|
186
|
+
* re-implements any of that.
|
|
187
|
+
*
|
|
188
|
+
* `pattern` scopes the count: `null` counts the whole queue (the global
|
|
189
|
+
* scopes), a pattern counts the requests attributed to the winning rule.
|
|
190
|
+
* Attribution reads the class off the `approval.requested` record rather than
|
|
191
|
+
* off the derivation, because the pending set is the set of requests, and the
|
|
192
|
+
* class a request was routed under is the class it was recorded with.
|
|
193
|
+
*
|
|
194
|
+
* Cost is one derivation per requested key, which is what
|
|
195
|
+
* `channels/tagging.ts` already pays to build the same queue. Sharing that
|
|
196
|
+
* cost is the point: a faster private walk would be a second definition of
|
|
197
|
+
* pending, and the two would agree until the day they did not.
|
|
198
|
+
*/
|
|
199
|
+
export declare function pendingCount(records: EventRecord[], pattern: string | null, evaluationTs: string, ttlMs: number | null): number;
|
|
200
|
+
/**
|
|
201
|
+
* How many `approval.requested` records `origin` created inside the window.
|
|
202
|
+
*
|
|
203
|
+
* Counts records, not live requests: the ceiling is on request CREATION, so a
|
|
204
|
+
* request that was granted, rejected or withdrawn a minute after it was made
|
|
205
|
+
* still consumed the origin's share of the hour. A ceiling that forgot a
|
|
206
|
+
* request the moment it was answered would be no ceiling at all — an agent
|
|
207
|
+
* could withdraw each request as it made it and create them without bound.
|
|
208
|
+
*
|
|
209
|
+
* A record whose `ts` cannot be parsed is COUNTED: it cannot be shown to lie
|
|
210
|
+
* outside the window, and the fail-closed reading of an unplaceable request is
|
|
211
|
+
* that it is inside. Same rule, and the same reason, as `core/budgets.ts`.
|
|
212
|
+
*
|
|
213
|
+
* Returns `null` when `evaluationTs` is not a parseable instant: no window can
|
|
214
|
+
* be computed, so nothing can be counted, and the caller fails the limit closed
|
|
215
|
+
* rather than reporting a zero that reads as headroom.
|
|
216
|
+
*/
|
|
217
|
+
export declare function requestsInWindow(records: EventRecord[], origin: string, pattern: string | null, evaluationTs: string): number | null;
|
|
218
|
+
/**
|
|
219
|
+
* Evaluate every applicable request-volume limit against the log.
|
|
220
|
+
*
|
|
221
|
+
* Conjunctive, exactly as budgets are: `pass` is true only when every verdict
|
|
222
|
+
* passes. Verdicts are emitted class limits first (limit names ascending, which
|
|
223
|
+
* puts `max_pending` before `requests_per_hour`), then global scopes (scope
|
|
224
|
+
* name ascending), so the list is byte-stable regardless of policy key order.
|
|
225
|
+
*
|
|
226
|
+
* **The refusal code is the first failing verdict's**, in exactly that order,
|
|
227
|
+
* and the order is normative: a caller told `queue-full` learns that the
|
|
228
|
+
* approver's queue is at its ceiling, which is a standing condition it must
|
|
229
|
+
* wait out or escalate; a caller told `rate-limited` learns that its own recent
|
|
230
|
+
* volume is the problem, which is a condition that clears on its own. Where
|
|
231
|
+
* both are true the standing condition is the one worth reporting, because an
|
|
232
|
+
* agent that backs off for a minute on a `rate-limited` and retries into a full
|
|
233
|
+
* queue has been told the smaller of the two facts.
|
|
234
|
+
*
|
|
235
|
+
* `records` may be the whole log; the caller is never asked to pre-filter, for
|
|
236
|
+
* the reason `core/budgets.ts` gives: a caller that filtered wrongly would
|
|
237
|
+
* silently widen the ceiling.
|
|
238
|
+
*/
|
|
239
|
+
export declare function evaluateIntakeLimits(records: EventRecord[], scope: IntakeScope, action: IntakeAction, evaluationTs: string, ttlMs: number | null): IntakeVerdicts;
|
|
240
|
+
/**
|
|
241
|
+
* The refusal code a failing evaluation produces, or `null` when it passed.
|
|
242
|
+
*
|
|
243
|
+
* One place, so the gate and any later surface answer with the same code for
|
|
244
|
+
* the same verdict list. See {@link evaluateIntakeLimits} for why the first
|
|
245
|
+
* failing verdict in verdict order is the one that speaks.
|
|
246
|
+
*/
|
|
247
|
+
export declare function intakeRefusalOf(verdicts: IntakeVerdicts): IntakeRefusal | null;
|
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Request-volume limits at intake (SPEC.md §5.2, APRV-173).
|
|
3
|
+
*
|
|
4
|
+
* > **Request-volume limits.** `limits.max_pending` (per class or global):
|
|
5
|
+
* > maximum simultaneously pending requests; further requests are refused at
|
|
6
|
+
* > intake with reason `queue-full`. `limits.requests_per_hour` (per origin):
|
|
7
|
+
* > rolling-window ceiling on request creation; excess is refused with reason
|
|
8
|
+
* > `rate-limited`.
|
|
9
|
+
*
|
|
10
|
+
* This module is that computation. It was policy vocabulary that no runtime
|
|
11
|
+
* read until this task: a policy could declare either key, the schema would
|
|
12
|
+
* accept it, and nothing enforced it.
|
|
13
|
+
*
|
|
14
|
+
* ## What these limits protect
|
|
15
|
+
*
|
|
16
|
+
* Budgets meter money and action counts, which is the world's exposure to the
|
|
17
|
+
* agent. These meter the QUEUE, which is the human's exposure to the agent. An
|
|
18
|
+
* agent that floods the queue with cheap, in-budget requests spends nothing and
|
|
19
|
+
* still defeats the gate, because an approver facing two hundred prompts stops
|
|
20
|
+
* reading them and starts clearing them. So the ceiling here is on attention,
|
|
21
|
+
* and a refusal costs the requester one exit code and the approver nothing.
|
|
22
|
+
*
|
|
23
|
+
* That difference is why a refusal on this path appends NOTHING (Carter's
|
|
24
|
+
* approved reading, 2026-08-31). `budget.exceeded` exists because a budget
|
|
25
|
+
* refusal is a fact about a commitment that audit must be able to reconstruct;
|
|
26
|
+
* a queue-flood refusal is a fact about a flood, and writing one log record per
|
|
27
|
+
* refused request would hand the flooder the log growth it was refused the
|
|
28
|
+
* queue for. The refusals are machine-readable and distinct (§11.1 invariant
|
|
29
|
+
* 6), which is what an agent and an auditor each need, and the events that WERE
|
|
30
|
+
* admitted are still all in the log to count from.
|
|
31
|
+
*
|
|
32
|
+
* ## Pure, deterministic, injected time
|
|
33
|
+
*
|
|
34
|
+
* Same discipline as `core/budgets.ts`: no I/O, no clock, no randomness. The
|
|
35
|
+
* evaluation instant and the TTL are parameters, so a verdict is replayable
|
|
36
|
+
* from the log alone. Nothing here re-runs class matching; the gate hands in
|
|
37
|
+
* the limits the matcher already resolved and the pattern that produced them.
|
|
38
|
+
*
|
|
39
|
+
* ## What "pending" means
|
|
40
|
+
*
|
|
41
|
+
* A request is pending when {@link requestState} derives `requested` for its
|
|
42
|
+
* action key at the evaluation instant: no grant, rejection, revocation,
|
|
43
|
+
* withdrawal, expiry event or lapsed TTL, and no execution. That derivation is
|
|
44
|
+
* `core/state.ts`'s and is the same one the gate refuses `duplicate-request`
|
|
45
|
+
* from and the channels build the queue from. Counting pending requests with a
|
|
46
|
+
* second, private definition is the one thing this module must not do — the
|
|
47
|
+
* cap a policy writes is a cap on the queue a human is shown, so it counts the
|
|
48
|
+
* queue a human is shown.
|
|
49
|
+
*
|
|
50
|
+
* ## What "per origin" means at v0.1
|
|
51
|
+
*
|
|
52
|
+
* **Origin is the record's `actor`.** The approved reading (Carter,
|
|
53
|
+
* 2026-08-31): the actor on `approval.requested` is assigned by the runtime
|
|
54
|
+
* from its own configuration rather than from the request, and under MCP the
|
|
55
|
+
* `--as` argument is appended last, so a caller cannot choose the identity its
|
|
56
|
+
* requests are counted under. Per-guest actors therefore make the ceiling
|
|
57
|
+
* per-client, which is the useful shape. This is a v0.1 reading of the spec's
|
|
58
|
+
* word "origin" rather than a claim that actor and origin are the same thing
|
|
59
|
+
* forever: an origin richer than the actor (a session, a remote address) would
|
|
60
|
+
* be a spec amendment, and it would only ever partition the count further.
|
|
61
|
+
*
|
|
62
|
+
* ## The rolling window
|
|
63
|
+
*
|
|
64
|
+
* `requests_per_hour` is measured over the hour preceding the evaluation
|
|
65
|
+
* instant, tiled exactly as `core/budgets.ts` tiles its 24 hours: a request
|
|
66
|
+
* consumes iff
|
|
67
|
+
*
|
|
68
|
+
* evaluationTs - 1h < record.ts <= evaluationTs
|
|
69
|
+
*
|
|
70
|
+
* half-open at the bottom, closed at the top, so consecutive windows tile the
|
|
71
|
+
* timeline and no request is counted in two of them. A request stamped exactly
|
|
72
|
+
* one hour before the evaluation instant has aged out; one stamped at the
|
|
73
|
+
* instant itself is in.
|
|
74
|
+
*
|
|
75
|
+
* ## Fail-closed, and what "unset" means
|
|
76
|
+
*
|
|
77
|
+
* A limit that is DECLARED and cannot be evaluated fails: a value that is not a
|
|
78
|
+
* positive finite integer, or an evaluation timestamp no window can be computed
|
|
79
|
+
* from, yields `pass: false` with a note. Silence is never a grant.
|
|
80
|
+
*
|
|
81
|
+
* A limit that is NOT declared enforces nothing, and that is the conservative
|
|
82
|
+
* answer rather than an exception to it. SPEC.md §5.2 says these ceilings are a
|
|
83
|
+
* tripwire whose "defaults are generous"; a runtime that invented one would
|
|
84
|
+
* refuse requests under a policy the human attested and read, and the refusal
|
|
85
|
+
* would name a number that appears nowhere in the file they signed. The
|
|
86
|
+
* fail-closed direction for an ABSENT request-volume limit is the same one the
|
|
87
|
+
* rest of the runtime takes for an absent `approval_ttl`: the policy declares
|
|
88
|
+
* no ceiling, so no ceiling binds, and the manual gate that binds every request
|
|
89
|
+
* on this path is what stands between the queue and the flood in the meantime.
|
|
90
|
+
* Where an operator wants a tripwire, one line of policy arms it.
|
|
91
|
+
*
|
|
92
|
+
* ## Division of labour with `core/budgets.ts`
|
|
93
|
+
*
|
|
94
|
+
* These two names are evaluated HERE and nowhere else. `core/budgets.ts` skips
|
|
95
|
+
* them by name rather than refusing them as unknown limits, and this module
|
|
96
|
+
* ignores every limit name that is not one of them. The two skip lists are
|
|
97
|
+
* complements, and each module's comment names the other: a limit that both
|
|
98
|
+
* skipped would be a ceiling in the policy file that no code enforces, which is
|
|
99
|
+
* the exact defect this task exists to close.
|
|
100
|
+
*/
|
|
101
|
+
import { matchesPattern } from "./policy-match.js";
|
|
102
|
+
import { payloadOf, requestState } from "./state.js";
|
|
103
|
+
/** Length of the `requests_per_hour` window: one hour, in milliseconds. */
|
|
104
|
+
export const REQUEST_WINDOW_MS = 60 * 60 * 1000;
|
|
105
|
+
/** The limit name capping simultaneously pending requests (SPEC.md §5.2). */
|
|
106
|
+
export const MAX_PENDING = "max_pending";
|
|
107
|
+
/** The limit name capping request creation per origin (SPEC.md §5.2). */
|
|
108
|
+
export const REQUESTS_PER_HOUR = "requests_per_hour";
|
|
109
|
+
/**
|
|
110
|
+
* The two names this module owns.
|
|
111
|
+
*
|
|
112
|
+
* Exported so `core/budgets.ts` skips exactly these and no others: one array,
|
|
113
|
+
* read by both modules, so the two skip lists cannot drift apart into a limit
|
|
114
|
+
* nobody evaluates.
|
|
115
|
+
*/
|
|
116
|
+
export const INTAKE_LIMIT_NAMES = [MAX_PENDING, REQUESTS_PER_HOUR];
|
|
117
|
+
/** Is `name` a request-volume limit (and therefore not a budget's business)? */
|
|
118
|
+
export function isIntakeLimitName(name) {
|
|
119
|
+
return INTAKE_LIMIT_NAMES.includes(name);
|
|
120
|
+
}
|
|
121
|
+
/** `payload.class` when it is a string, else `null`. */
|
|
122
|
+
function classOf(record) {
|
|
123
|
+
const value = payloadOf(record)["class"];
|
|
124
|
+
return typeof value === "string" ? value : null;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Does this record belong to the scope being counted?
|
|
128
|
+
*
|
|
129
|
+
* `pattern === null` is the global scope: everything belongs. Otherwise the
|
|
130
|
+
* record's own declared class must match the winning rule's pattern. A record
|
|
131
|
+
* carrying no usable class is invisible to a class-scoped count for the reason
|
|
132
|
+
* `core/budgets.ts` gives: it cannot be SHOWN to belong to the class. It is
|
|
133
|
+
* still counted by every global scope.
|
|
134
|
+
*/
|
|
135
|
+
function inScope(record, pattern) {
|
|
136
|
+
if (pattern === null)
|
|
137
|
+
return true;
|
|
138
|
+
const cls = classOf(record);
|
|
139
|
+
return cls !== null && matchesPattern(pattern, cls);
|
|
140
|
+
}
|
|
141
|
+
/** Every action key with an `approval.requested`, in log order, deduplicated. */
|
|
142
|
+
function requestedKeys(records) {
|
|
143
|
+
const keys = [];
|
|
144
|
+
for (const record of records) {
|
|
145
|
+
if (record.event !== "approval.requested")
|
|
146
|
+
continue;
|
|
147
|
+
const key = record.action_key;
|
|
148
|
+
if (key === undefined || keys.includes(key))
|
|
149
|
+
continue;
|
|
150
|
+
keys.push(key);
|
|
151
|
+
}
|
|
152
|
+
return keys;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* How many requests are simultaneously pending at `evaluationTs`.
|
|
156
|
+
*
|
|
157
|
+
* Derived through {@link requestState}, one action key at a time, so every
|
|
158
|
+
* exit from the queue is honoured by the definition that owns it: a decision,
|
|
159
|
+
* a revocation, a withdrawal (APRV-106), an `approval.expired` record, a TTL
|
|
160
|
+
* lapsed by arithmetic with no record at all, and an execution. Nothing here
|
|
161
|
+
* re-implements any of that.
|
|
162
|
+
*
|
|
163
|
+
* `pattern` scopes the count: `null` counts the whole queue (the global
|
|
164
|
+
* scopes), a pattern counts the requests attributed to the winning rule.
|
|
165
|
+
* Attribution reads the class off the `approval.requested` record rather than
|
|
166
|
+
* off the derivation, because the pending set is the set of requests, and the
|
|
167
|
+
* class a request was routed under is the class it was recorded with.
|
|
168
|
+
*
|
|
169
|
+
* Cost is one derivation per requested key, which is what
|
|
170
|
+
* `channels/tagging.ts` already pays to build the same queue. Sharing that
|
|
171
|
+
* cost is the point: a faster private walk would be a second definition of
|
|
172
|
+
* pending, and the two would agree until the day they did not.
|
|
173
|
+
*/
|
|
174
|
+
export function pendingCount(records, pattern, evaluationTs, ttlMs) {
|
|
175
|
+
let count = 0;
|
|
176
|
+
for (const key of requestedKeys(records)) {
|
|
177
|
+
const derivation = requestState(records, key, evaluationTs, ttlMs);
|
|
178
|
+
if (derivation.state !== "requested")
|
|
179
|
+
continue;
|
|
180
|
+
if (derivation.requestSeq === null)
|
|
181
|
+
continue;
|
|
182
|
+
const record = records.find((candidate) => candidate.seq === derivation.requestSeq);
|
|
183
|
+
if (record === undefined)
|
|
184
|
+
continue;
|
|
185
|
+
if (!inScope(record, pattern))
|
|
186
|
+
continue;
|
|
187
|
+
count += 1;
|
|
188
|
+
}
|
|
189
|
+
return count;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* How many `approval.requested` records `origin` created inside the window.
|
|
193
|
+
*
|
|
194
|
+
* Counts records, not live requests: the ceiling is on request CREATION, so a
|
|
195
|
+
* request that was granted, rejected or withdrawn a minute after it was made
|
|
196
|
+
* still consumed the origin's share of the hour. A ceiling that forgot a
|
|
197
|
+
* request the moment it was answered would be no ceiling at all — an agent
|
|
198
|
+
* could withdraw each request as it made it and create them without bound.
|
|
199
|
+
*
|
|
200
|
+
* A record whose `ts` cannot be parsed is COUNTED: it cannot be shown to lie
|
|
201
|
+
* outside the window, and the fail-closed reading of an unplaceable request is
|
|
202
|
+
* that it is inside. Same rule, and the same reason, as `core/budgets.ts`.
|
|
203
|
+
*
|
|
204
|
+
* Returns `null` when `evaluationTs` is not a parseable instant: no window can
|
|
205
|
+
* be computed, so nothing can be counted, and the caller fails the limit closed
|
|
206
|
+
* rather than reporting a zero that reads as headroom.
|
|
207
|
+
*/
|
|
208
|
+
export function requestsInWindow(records, origin, pattern, evaluationTs) {
|
|
209
|
+
const evaluationMs = Date.parse(evaluationTs);
|
|
210
|
+
if (Number.isNaN(evaluationMs))
|
|
211
|
+
return null;
|
|
212
|
+
const floor = evaluationMs - REQUEST_WINDOW_MS;
|
|
213
|
+
let count = 0;
|
|
214
|
+
for (const record of records) {
|
|
215
|
+
if (record.event !== "approval.requested")
|
|
216
|
+
continue;
|
|
217
|
+
if (record.actor !== origin)
|
|
218
|
+
continue;
|
|
219
|
+
if (!inScope(record, pattern))
|
|
220
|
+
continue;
|
|
221
|
+
const ms = Date.parse(record.ts);
|
|
222
|
+
if (!Number.isNaN(ms) && !(ms > floor && ms <= evaluationMs))
|
|
223
|
+
continue;
|
|
224
|
+
count += 1;
|
|
225
|
+
}
|
|
226
|
+
return count;
|
|
227
|
+
}
|
|
228
|
+
/** A declared ceiling as an exact positive integer, or `null`. */
|
|
229
|
+
function ceilingOf(value) {
|
|
230
|
+
if (typeof value !== "number")
|
|
231
|
+
return null;
|
|
232
|
+
if (!Number.isInteger(value) || value <= 0)
|
|
233
|
+
return null;
|
|
234
|
+
return value;
|
|
235
|
+
}
|
|
236
|
+
function verdict(limit, scope, window, refusal, observed, ceiling) {
|
|
237
|
+
const remaining = ceiling - observed - 1;
|
|
238
|
+
return {
|
|
239
|
+
limit,
|
|
240
|
+
scope,
|
|
241
|
+
window,
|
|
242
|
+
refusal,
|
|
243
|
+
observed,
|
|
244
|
+
requested: 1,
|
|
245
|
+
remaining,
|
|
246
|
+
ceiling,
|
|
247
|
+
pass: remaining >= 0,
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
/** A fail-closed refusal: nothing measured, nothing admitted, reason attached. */
|
|
251
|
+
function refuse(limit, scope, window, refusal, note) {
|
|
252
|
+
return {
|
|
253
|
+
limit,
|
|
254
|
+
scope,
|
|
255
|
+
window,
|
|
256
|
+
refusal,
|
|
257
|
+
observed: 0,
|
|
258
|
+
requested: 1,
|
|
259
|
+
remaining: -1,
|
|
260
|
+
ceiling: null,
|
|
261
|
+
pass: false,
|
|
262
|
+
note,
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Evaluate every applicable request-volume limit against the log.
|
|
267
|
+
*
|
|
268
|
+
* Conjunctive, exactly as budgets are: `pass` is true only when every verdict
|
|
269
|
+
* passes. Verdicts are emitted class limits first (limit names ascending, which
|
|
270
|
+
* puts `max_pending` before `requests_per_hour`), then global scopes (scope
|
|
271
|
+
* name ascending), so the list is byte-stable regardless of policy key order.
|
|
272
|
+
*
|
|
273
|
+
* **The refusal code is the first failing verdict's**, in exactly that order,
|
|
274
|
+
* and the order is normative: a caller told `queue-full` learns that the
|
|
275
|
+
* approver's queue is at its ceiling, which is a standing condition it must
|
|
276
|
+
* wait out or escalate; a caller told `rate-limited` learns that its own recent
|
|
277
|
+
* volume is the problem, which is a condition that clears on its own. Where
|
|
278
|
+
* both are true the standing condition is the one worth reporting, because an
|
|
279
|
+
* agent that backs off for a minute on a `rate-limited` and retries into a full
|
|
280
|
+
* queue has been told the smaller of the two facts.
|
|
281
|
+
*
|
|
282
|
+
* `records` may be the whole log; the caller is never asked to pre-filter, for
|
|
283
|
+
* the reason `core/budgets.ts` gives: a caller that filtered wrongly would
|
|
284
|
+
* silently widen the ceiling.
|
|
285
|
+
*/
|
|
286
|
+
export function evaluateIntakeLimits(records, scope, action, evaluationTs, ttlMs) {
|
|
287
|
+
const classLimits = scope.classLimits ?? {};
|
|
288
|
+
const globalBudgets = scope.globalBudgets ?? {};
|
|
289
|
+
const verdicts = [];
|
|
290
|
+
// --- Class limits (the winning rule's `limits`) --------------------------
|
|
291
|
+
for (const name of Object.keys(classLimits).sort()) {
|
|
292
|
+
if (!isIntakeLimitName(name))
|
|
293
|
+
continue; // `core/budgets.ts` owns the rest.
|
|
294
|
+
const declared = classLimits[name];
|
|
295
|
+
const isPending = name === MAX_PENDING;
|
|
296
|
+
const window = isPending ? "simultaneous" : "rolling-1h";
|
|
297
|
+
const refusal = isPending ? "queue-full" : "rate-limited";
|
|
298
|
+
const ceiling = ceilingOf(declared);
|
|
299
|
+
if (ceiling === null) {
|
|
300
|
+
verdicts.push(refuse(name, "class", window, refusal, `class limit "${name}" is not a positive whole number; a ceiling that cannot be compared cannot be proven satisfied, so the request is refused rather than admitted against a limit nobody can evaluate`));
|
|
301
|
+
continue;
|
|
302
|
+
}
|
|
303
|
+
if (scope.classPattern === null) {
|
|
304
|
+
verdicts.push(refuse(name, "class", window, refusal, `class limit "${name}" was supplied without the class pattern that scopes its count; the queue cannot be attributed, so the limit cannot be proven satisfied`));
|
|
305
|
+
continue;
|
|
306
|
+
}
|
|
307
|
+
if (isPending) {
|
|
308
|
+
verdicts.push(verdict(name, "class", window, refusal, pendingCount(records, scope.classPattern, evaluationTs, ttlMs), ceiling));
|
|
309
|
+
continue;
|
|
310
|
+
}
|
|
311
|
+
const observed = requestsInWindow(records, action.origin, scope.classPattern, evaluationTs);
|
|
312
|
+
if (observed === null) {
|
|
313
|
+
verdicts.push(refuse(name, "class", window, refusal, `evaluation timestamp "${evaluationTs}" is not a parseable RFC 3339 instant; the rolling window cannot be computed, so no request-volume limit can be proven satisfied`));
|
|
314
|
+
continue;
|
|
315
|
+
}
|
|
316
|
+
verdicts.push(verdict(name, "class", window, refusal, observed, ceiling));
|
|
317
|
+
}
|
|
318
|
+
// --- Global scopes (`policy.budgets`) -----------------------------------
|
|
319
|
+
// Only `max_pending` lives here: `policy.schema.json` gives a budget scope
|
|
320
|
+
// `daily_usd`, `daily_actions` and `max_pending`, and a rate limit has no
|
|
321
|
+
// global spelling because it is measured per origin and a scope names none.
|
|
322
|
+
for (const scopeName of Object.keys(globalBudgets).sort()) {
|
|
323
|
+
const budget = globalBudgets[scopeName];
|
|
324
|
+
if (budget === undefined)
|
|
325
|
+
continue;
|
|
326
|
+
const declared = budget[MAX_PENDING];
|
|
327
|
+
if (declared === undefined)
|
|
328
|
+
continue;
|
|
329
|
+
const label = `${scopeName}.${MAX_PENDING}`;
|
|
330
|
+
const ceiling = ceilingOf(declared);
|
|
331
|
+
if (ceiling === null) {
|
|
332
|
+
verdicts.push(refuse(label, "global", "simultaneous", "queue-full", `budget "${label}" is not a positive whole number; a ceiling that cannot be compared cannot be proven satisfied`));
|
|
333
|
+
continue;
|
|
334
|
+
}
|
|
335
|
+
verdicts.push(verdict(label, "global", "simultaneous", "queue-full", pendingCount(records, null, evaluationTs, ttlMs), ceiling));
|
|
336
|
+
}
|
|
337
|
+
return { pass: verdicts.every((entry) => entry.pass), verdicts };
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* The refusal code a failing evaluation produces, or `null` when it passed.
|
|
341
|
+
*
|
|
342
|
+
* One place, so the gate and any later surface answer with the same code for
|
|
343
|
+
* the same verdict list. See {@link evaluateIntakeLimits} for why the first
|
|
344
|
+
* failing verdict in verdict order is the one that speaks.
|
|
345
|
+
*/
|
|
346
|
+
export function intakeRefusalOf(verdicts) {
|
|
347
|
+
const failed = verdicts.verdicts.find((entry) => !entry.pass);
|
|
348
|
+
return failed === undefined ? null : failed.refusal;
|
|
349
|
+
}
|
|
350
|
+
//# sourceMappingURL=intake-limits.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"intake-limits.js","sourceRoot":"","sources":["../../../src/core/intake-limits.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RFC 8785 — JSON Canonicalization Scheme (JCS).
|
|
3
|
+
*
|
|
4
|
+
* SPEC.md §8 fixes the hash scheme at `sha256/jcs`: SHA-256 over the RFC 8785
|
|
5
|
+
* canonical serialization of an event record with `prev` included. The digest
|
|
6
|
+
* is a permanent wire commitment, so this module is part of the deterministic
|
|
7
|
+
* core: same input, same bytes, forever. No clock, no locale, no randomness,
|
|
8
|
+
* no configuration knobs.
|
|
9
|
+
*
|
|
10
|
+
* Implemented by hand — zero dependencies (CLAUDE.md: minimal dependencies,
|
|
11
|
+
* and a canonicalizer is exactly the kind of code that must not drift beneath
|
|
12
|
+
* us). Three rules do all the work:
|
|
13
|
+
*
|
|
14
|
+
* 1. **Object keys** are sorted by their UTF-16 code units (RFC 8785 §3.2.3).
|
|
15
|
+
* That is JavaScript's own `<` on strings, which is why a naive code-point
|
|
16
|
+
* sort is *wrong*: a key starting U+10000 (surrogate pair D800 DC00) sorts
|
|
17
|
+
* *before* a key starting U+FFFF under code units and *after* it under code
|
|
18
|
+
* points. The comparator below is deliberately code-unit based.
|
|
19
|
+
* 2. **Numbers** use ECMAScript `Number::toString` (RFC 8785 §3.2.2.3), which
|
|
20
|
+
* is precisely what `JSON.stringify` emits for a finite number — including
|
|
21
|
+
* `1e+21`, `1e-7`, and `-0` collapsing to `0`. Delegating is not a shortcut
|
|
22
|
+
* around the RFC; the RFC's normative reference *is* the ECMAScript
|
|
23
|
+
* algorithm.
|
|
24
|
+
* 3. **Strings** use the ECMAScript `QuoteJSONString` escaping (RFC 8785
|
|
25
|
+
* §3.2.2.2): shortest form for `\b \t \n \f \r \" \\`, `\u00xx` with
|
|
26
|
+
* lowercase hex for the remaining C0 controls, and every other code point
|
|
27
|
+
* literal. `JSON.stringify` on a string implements exactly this, including
|
|
28
|
+
* the well-formed-stringify escaping of lone surrogates (ES2019+).
|
|
29
|
+
*
|
|
30
|
+
* Everything JCS cannot represent is rejected loudly rather than coerced:
|
|
31
|
+
* `undefined`, functions, symbols, `BigInt`, `NaN`, `±Infinity`, and non-plain
|
|
32
|
+
* objects (a `Date` or `Map` would silently canonicalize to `{}`). Silent
|
|
33
|
+
* coercion in a hash input is a tamper-evidence hole, so this module fails
|
|
34
|
+
* closed by throwing {@link JcsError}.
|
|
35
|
+
*/
|
|
36
|
+
/** Reason a value could not be canonicalized. */
|
|
37
|
+
export type JcsErrorCode = "undefined" | "function" | "symbol" | "bigint" | "non-finite-number" | "unsupported-object" | "cycle";
|
|
38
|
+
/** Thrown when a value has no RFC 8785 canonical form. */
|
|
39
|
+
export declare class JcsError extends Error {
|
|
40
|
+
/** Machine-readable reason. */
|
|
41
|
+
readonly code: JcsErrorCode;
|
|
42
|
+
/** JSON Pointer-ish location of the offending value ("" for the root). */
|
|
43
|
+
readonly path: string;
|
|
44
|
+
constructor(code: JcsErrorCode, path: string, message: string);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Canonicalize `value` to its RFC 8785 (JCS) serialization: sorted object
|
|
48
|
+
* keys, no whitespace, ECMAScript number and string formatting.
|
|
49
|
+
*
|
|
50
|
+
* @throws {JcsError} if the value contains anything JCS cannot represent.
|
|
51
|
+
*/
|
|
52
|
+
export declare function canonicalize(value: unknown): string;
|