approval-md 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +629 -559
- package/SPEC.md +99 -24
- package/dist/src/adapters/agentmail.d.ts +426 -0
- package/dist/src/adapters/agentmail.js +2 -2
- package/dist/src/adapters/conformance.d.ts +149 -0
- package/dist/src/adapters/contract.d.ts +628 -0
- package/dist/src/adapters/contract.js +110 -16
- package/dist/src/adapters/contract.js.map +1 -1
- package/dist/src/adapters/email.d.ts +324 -0
- package/dist/src/adapters/env-passphrase.d.ts +93 -0
- package/dist/src/adapters/public.d.ts +11 -0
- package/dist/src/adapters/public.js +11 -0
- package/dist/src/adapters/public.js.map +1 -0
- package/dist/src/adapters/registry.d.ts +59 -0
- package/dist/src/adapters/registry.js +2 -1
- package/dist/src/adapters/registry.js.map +1 -1
- package/dist/src/adapters/smtp.d.ts +213 -0
- package/dist/src/adapters/vault-provider.d.ts +114 -0
- package/dist/src/adapters/vault-provider.js +3 -3
- package/dist/src/adapters/zzz.d.ts +66 -0
- package/dist/src/adapters/zzz.js +299 -0
- package/dist/src/adapters/zzz.js.map +1 -0
- package/dist/src/channels/batch.d.ts +109 -0
- package/dist/src/channels/cli.d.ts +193 -0
- package/dist/src/channels/conformance.d.ts +92 -0
- package/dist/src/channels/contract.d.ts +656 -0
- package/dist/src/channels/contract.js +200 -7
- package/dist/src/channels/contract.js.map +1 -1
- package/dist/src/channels/payload-view.d.ts +35 -0
- package/dist/src/channels/render-queue.d.ts +149 -0
- package/dist/src/channels/tagging.d.ts +196 -0
- package/dist/src/channels/telegram.d.ts +1944 -0
- package/dist/src/channels/telegram.js +218 -23
- package/dist/src/channels/telegram.js.map +1 -1
- package/dist/src/channels/web.d.ts +350 -0
- package/dist/src/channels/web.js +17 -0
- package/dist/src/channels/web.js.map +1 -1
- package/dist/src/cli/adapter.d.ts +90 -0
- package/dist/src/cli/adapter.js +25 -15
- package/dist/src/cli/adapter.js.map +1 -1
- package/dist/src/cli/amend.d.ts +59 -0
- package/dist/src/cli/amend.js +214 -30
- package/dist/src/cli/amend.js.map +1 -1
- package/dist/src/cli/args.d.ts +43 -0
- package/dist/src/cli/attest.d.ts +50 -0
- package/dist/src/cli/attest.js +134 -7
- package/dist/src/cli/attest.js.map +1 -1
- package/dist/src/cli/audit-card.d.ts +62 -0
- package/dist/src/cli/audit.d.ts +59 -0
- package/dist/src/cli/channel-telegram.d.ts +879 -0
- package/dist/src/cli/channel-telegram.js +311 -13
- package/dist/src/cli/channel-telegram.js.map +1 -1
- package/dist/src/cli/channel-web.d.ts +131 -0
- package/dist/src/cli/channel.d.ts +80 -0
- package/dist/src/cli/channel.js +9 -0
- package/dist/src/cli/channel.js.map +1 -1
- package/dist/src/cli/checkpoint-tap.d.ts +169 -0
- package/dist/src/cli/codex-bridge.d.ts +819 -0
- package/dist/src/cli/codex-bridge.js +1607 -0
- package/dist/src/cli/codex-bridge.js.map +1 -0
- package/dist/src/cli/codex.d.ts +2 -0
- package/dist/src/cli/codex.js +469 -0
- package/dist/src/cli/codex.js.map +1 -0
- package/dist/src/cli/coverage.d.ts +61 -0
- package/dist/src/cli/daemon.d.ts +120 -0
- package/dist/src/cli/daemon.js +4 -1
- package/dist/src/cli/daemon.js.map +1 -1
- package/dist/src/cli/doctor.d.ts +129 -0
- package/dist/src/cli/doctor.js +586 -17
- package/dist/src/cli/doctor.js.map +1 -1
- package/dist/src/cli/env.d.ts +65 -0
- package/dist/src/cli/execute.d.ts +202 -0
- package/dist/src/cli/execute.js +25 -2
- package/dist/src/cli/execute.js.map +1 -1
- package/dist/src/cli/exit-codes.d.ts +73 -0
- package/dist/src/cli/feedback.d.ts +60 -0
- package/dist/src/cli/gate-window.d.ts +40 -0
- package/dist/src/cli/gate.d.ts +68 -0
- package/dist/src/cli/git-scope.d.ts +190 -0
- package/dist/src/cli/gloss-attach.d.ts +85 -0
- package/dist/src/cli/gloss-codex-child.d.ts +9 -0
- package/dist/src/cli/gloss-codex.d.ts +24 -0
- package/dist/src/cli/gloss-options.d.ts +42 -0
- package/dist/src/cli/gloss.d.ts +265 -0
- package/dist/src/cli/help.d.ts +107 -0
- package/dist/src/cli/help.js +320 -93
- package/dist/src/cli/help.js.map +1 -1
- package/dist/src/cli/hook-codex.d.ts +126 -0
- package/dist/src/cli/hook-codex.js +226 -0
- package/dist/src/cli/hook-codex.js.map +1 -0
- package/dist/src/cli/hook.d.ts +787 -0
- package/dist/src/cli/hook.js +1235 -181
- package/dist/src/cli/hook.js.map +1 -1
- package/dist/src/cli/import.d.ts +35 -0
- package/dist/src/cli/import.js +1 -1
- package/dist/src/cli/import.js.map +1 -1
- package/dist/src/cli/init.d.ts +84 -0
- package/dist/src/cli/init.js +2 -2
- package/dist/src/cli/init.js.map +1 -1
- package/dist/src/cli/instructions.d.ts +23 -0
- package/dist/src/cli/journal.d.ts +41 -0
- package/dist/src/cli/log-advance.d.ts +287 -0
- package/dist/src/cli/log-advance.js +102 -11
- package/dist/src/cli/log-advance.js.map +1 -1
- package/dist/src/cli/log-anchor.d.ts +176 -0
- package/dist/src/cli/log-checkpoint.d.ts +22 -0
- package/dist/src/cli/log-sync.d.ts +243 -0
- package/dist/src/cli/log-verbs.d.ts +16 -0
- package/dist/src/cli/log-verbs.js +7 -1
- package/dist/src/cli/log-verbs.js.map +1 -1
- package/dist/src/cli/long-help.d.ts +70 -0
- package/dist/src/cli/main.d.ts +77 -0
- package/dist/src/cli/main.js +159 -7
- package/dist/src/cli/main.js.map +1 -1
- package/dist/src/cli/mcp.d.ts +52 -0
- package/dist/src/cli/paths.d.ts +56 -0
- package/dist/src/cli/payload.d.ts +58 -0
- package/dist/src/cli/policy-apply.d.ts +195 -0
- package/dist/src/cli/policy-apply.js +573 -0
- package/dist/src/cli/policy-apply.js.map +1 -0
- package/dist/src/cli/policy.d.ts +43 -0
- package/dist/src/cli/policy.js +14 -1
- package/dist/src/cli/policy.js.map +1 -1
- package/dist/src/cli/preflight.d.ts +501 -0
- package/dist/src/cli/preflight.js +689 -45
- package/dist/src/cli/preflight.js.map +1 -1
- package/dist/src/cli/progress.d.ts +78 -0
- package/dist/src/cli/prompt.d.ts +209 -0
- package/dist/src/cli/quickstart.d.ts +46 -0
- package/dist/src/cli/quickstart.js +297 -0
- package/dist/src/cli/quickstart.js.map +1 -0
- package/dist/src/cli/records.d.ts +34 -0
- package/dist/src/cli/render.d.ts +22 -0
- package/dist/src/cli/sandbox.d.ts +51 -0
- package/dist/src/cli/sandbox.js +17 -1
- package/dist/src/cli/sandbox.js.map +1 -1
- package/dist/src/cli/scaffold.d.ts +79 -0
- package/dist/src/cli/scaffold.js +1 -1
- package/dist/src/cli/setup-adapter.d.ts +137 -0
- package/dist/src/cli/setup-adapter.js +38 -4
- package/dist/src/cli/setup-adapter.js.map +1 -1
- package/dist/src/cli/setup-channel.d.ts +126 -0
- package/dist/src/cli/setup-channel.js +28 -1
- package/dist/src/cli/setup-channel.js.map +1 -1
- package/dist/src/cli/setup-checkpoint.d.ts +57 -0
- package/dist/src/cli/setup-common.d.ts +277 -0
- package/dist/src/cli/setup-common.js +3 -2
- package/dist/src/cli/setup-common.js.map +1 -1
- package/dist/src/cli/setup-flow.d.ts +287 -0
- package/dist/src/cli/setup-service.d.ts +96 -0
- package/dist/src/cli/setup.d.ts +204 -0
- package/dist/src/cli/setup.js +94 -2
- package/dist/src/cli/setup.js.map +1 -1
- package/dist/src/cli/style.d.ts +320 -0
- package/dist/src/cli/token.d.ts +39 -0
- package/dist/src/cli/up.d.ts +155 -0
- package/dist/src/cli/up.js +119 -53
- package/dist/src/cli/up.js.map +1 -1
- package/dist/src/cli/usage.d.ts +37 -0
- package/dist/src/cli/values.d.ts +40 -0
- package/dist/src/cli/values.js +3 -4
- package/dist/src/cli/values.js.map +1 -1
- package/dist/src/cli/vault.d.ts +59 -0
- package/dist/src/cli/vault.js +2 -2
- package/dist/src/cli/vault.js.map +1 -1
- package/dist/src/cli/verb-registry.d.ts +76 -0
- package/dist/src/cli/verb-registry.js +344 -11
- package/dist/src/cli/verb-registry.js.map +1 -1
- package/dist/src/cli/wordmark.d.ts +31 -0
- package/dist/src/cli/wordmark.js +2 -2
- package/dist/src/codex/broker.d.ts +229 -0
- package/dist/src/codex/broker.js +548 -0
- package/dist/src/codex/broker.js.map +1 -0
- package/dist/src/codex/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/runner.d.ts +178 -0
- package/dist/src/codex/runner.js +231 -0
- package/dist/src/codex/runner.js.map +1 -0
- package/dist/src/codex/serve.d.ts +56 -0
- package/dist/src/codex/serve.js +98 -0
- package/dist/src/codex/serve.js.map +1 -0
- package/dist/src/codex/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-commit.d.ts +219 -0
- package/dist/src/codex/workspace-commit.js +549 -0
- package/dist/src/codex/workspace-commit.js.map +1 -0
- package/dist/src/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 +221 -0
- package/dist/src/core/advance-cycle.js +66 -2
- package/dist/src/core/advance-cycle.js.map +1 -1
- package/dist/src/core/agents-md.d.ts +278 -0
- package/dist/src/core/agents-md.js +33 -31
- package/dist/src/core/agents-md.js.map +1 -1
- 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 +635 -0
- package/dist/src/core/attest.js +326 -4
- package/dist/src/core/attest.js.map +1 -1
- package/dist/src/core/audit.d.ts +510 -0
- package/dist/src/core/audit.js +13 -0
- package/dist/src/core/audit.js.map +1 -1
- package/dist/src/core/budgets.d.ts +238 -0
- package/dist/src/core/channel-owner.d.ts +213 -0
- package/dist/src/core/channel-owner.js +358 -0
- package/dist/src/core/channel-owner.js.map +1 -0
- package/dist/src/core/checkpoint.d.ts +500 -0
- package/dist/src/core/child-env.d.ts +88 -0
- package/dist/src/core/clock.d.ts +52 -0
- package/dist/src/core/command-class.d.ts +697 -0
- package/dist/src/core/command-class.js +713 -25
- package/dist/src/core/command-class.js.map +1 -1
- package/dist/src/core/commit-guard.d.ts +272 -0
- package/dist/src/core/commit-guard.js +424 -0
- package/dist/src/core/commit-guard.js.map +1 -0
- package/dist/src/core/coverage-sources/adapter.d.ts +40 -0
- package/dist/src/core/coverage-sources/gh.d.ts +48 -0
- package/dist/src/core/coverage-sources/git.d.ts +101 -0
- package/dist/src/core/coverage.d.ts +217 -0
- package/dist/src/core/credential-spec.d.ts +72 -0
- package/dist/src/core/daemon-actor.d.ts +45 -0
- package/dist/src/core/daemon-actor.js +54 -0
- package/dist/src/core/daemon-actor.js.map +1 -0
- package/dist/src/core/dark-session.d.ts +432 -0
- package/dist/src/core/dark-session.js +266 -82
- package/dist/src/core/dark-session.js.map +1 -1
- package/dist/src/core/decision-refusal.d.ts +206 -0
- package/dist/src/core/decision-refusal.js +24 -2
- package/dist/src/core/decision-refusal.js.map +1 -1
- package/dist/src/core/env-file.d.ts +455 -0
- package/dist/src/core/env-file.js +60 -1
- package/dist/src/core/env-file.js.map +1 -1
- package/dist/src/core/execute.d.ts +871 -0
- package/dist/src/core/execute.js +59 -8
- package/dist/src/core/execute.js.map +1 -1
- package/dist/src/core/frontmatter.d.ts +78 -0
- package/dist/src/core/gate-window.d.ts +312 -0
- package/dist/src/core/gate.d.ts +1449 -0
- package/dist/src/core/gate.js +149 -14
- package/dist/src/core/gate.js.map +1 -1
- package/dist/src/core/gesture-refusal.d.ts +166 -0
- package/dist/src/core/gesture-refusal.js +188 -0
- package/dist/src/core/gesture-refusal.js.map +1 -0
- package/dist/src/core/git-run.d.ts +73 -0
- package/dist/src/core/harness-version.d.ts +157 -0
- package/dist/src/core/harness-version.js +4 -1
- package/dist/src/core/harness-version.js.map +1 -1
- package/dist/src/core/harness-wait.d.ts +55 -0
- package/dist/src/core/head-retry.d.ts +107 -0
- package/dist/src/core/instance.d.ts +310 -0
- package/dist/src/core/instance.js +113 -0
- package/dist/src/core/instance.js.map +1 -1
- package/dist/src/core/intake-limits.d.ts +247 -0
- package/dist/src/core/jcs.d.ts +52 -0
- package/dist/src/core/journal.d.ts +144 -0
- package/dist/src/core/live-draw.d.ts +436 -0
- package/dist/src/core/log-reconcile.d.ts +89 -0
- package/dist/src/core/log-subscribe.d.ts +36 -0
- package/dist/src/core/log-subscribe.js +162 -0
- package/dist/src/core/log-subscribe.js.map +1 -0
- package/dist/src/core/log.d.ts +316 -0
- package/dist/src/core/log.js.map +1 -1
- package/dist/src/core/loop.d.ts +274 -0
- package/dist/src/core/loop.js +11 -0
- package/dist/src/core/loop.js.map +1 -1
- package/dist/src/core/md-fence.d.ts +41 -0
- package/dist/src/core/money.d.ts +147 -0
- package/dist/src/core/payload-census.d.ts +74 -0
- package/dist/src/core/payload-store.d.ts +175 -0
- package/dist/src/core/payload.d.ts +71 -0
- package/dist/src/core/policy-diff.d.ts +292 -0
- package/dist/src/core/policy-diff.js +27 -4
- package/dist/src/core/policy-diff.js.map +1 -1
- package/dist/src/core/policy-expectations.d.ts +199 -0
- package/dist/src/core/policy-explain.d.ts +160 -0
- package/dist/src/core/policy-explain.js +63 -3
- package/dist/src/core/policy-explain.js.map +1 -1
- package/dist/src/core/policy-load.d.ts +567 -0
- package/dist/src/core/policy-load.js +36 -6
- package/dist/src/core/policy-load.js.map +1 -1
- package/dist/src/core/policy-match.d.ts +324 -0
- package/dist/src/core/policy-match.js +72 -9
- package/dist/src/core/policy-match.js.map +1 -1
- package/dist/src/core/policy-proposal.d.ts +317 -0
- package/dist/src/core/policy-proposal.js +102 -2
- package/dist/src/core/policy-proposal.js.map +1 -1
- package/dist/src/core/prompt-layout.d.ts +221 -0
- package/dist/src/core/protected-path-guard.d.ts +566 -0
- package/dist/src/core/protected-path-guard.js +848 -55
- package/dist/src/core/protected-path-guard.js.map +1 -1
- package/dist/src/core/question-preempted.d.ts +141 -0
- package/dist/src/core/question-preempted.js +152 -0
- package/dist/src/core/question-preempted.js.map +1 -0
- package/dist/src/core/read-scope.d.ts +172 -0
- package/dist/src/core/read-scope.js +252 -0
- package/dist/src/core/read-scope.js.map +1 -0
- package/dist/src/core/registration.d.ts +25 -0
- package/dist/src/core/reindex.d.ts +99 -0
- package/dist/src/core/sampler.d.ts +313 -0
- package/dist/src/core/sandbox.d.ts +371 -0
- package/dist/src/core/sandbox.js +190 -1
- package/dist/src/core/sandbox.js.map +1 -1
- package/dist/src/core/seal.d.ts +165 -0
- package/dist/src/core/sender-identity.d.ts +476 -0
- package/dist/src/core/sender-identity.js +572 -0
- package/dist/src/core/sender-identity.js.map +1 -0
- package/dist/src/core/shlex.d.ts +102 -0
- package/dist/src/core/shlex.js +159 -0
- package/dist/src/core/shlex.js.map +1 -0
- package/dist/src/core/state.d.ts +505 -0
- package/dist/src/core/task-file.d.ts +185 -0
- package/dist/src/core/telegram-config.d.ts +93 -0
- package/dist/src/core/token.d.ts +409 -0
- package/dist/src/core/token.js +21 -38
- package/dist/src/core/token.js.map +1 -1
- package/dist/src/core/validate.d.ts +138 -0
- package/dist/src/core/values.d.ts +147 -0
- package/dist/src/core/values.js +36 -1
- package/dist/src/core/values.js.map +1 -1
- package/dist/src/core/vault.d.ts +291 -0
- package/dist/src/core/verified-snapshot.d.ts +204 -0
- package/dist/src/core/verify.d.ts +336 -0
- package/dist/src/core/version.d.ts +8 -0
- package/dist/src/core/wysiwys.d.ts +370 -0
- package/dist/src/daemon/advance-child.d.ts +39 -0
- package/dist/src/daemon/advance.d.ts +476 -0
- package/dist/src/daemon/advance.js +25 -4
- package/dist/src/daemon/advance.js.map +1 -1
- package/dist/src/daemon/audit.d.ts +87 -0
- package/dist/src/daemon/daemon.d.ts +1180 -0
- package/dist/src/daemon/daemon.js +9 -0
- package/dist/src/daemon/daemon.js.map +1 -1
- package/dist/src/daemon/dark-session.d.ts +64 -0
- package/dist/src/daemon/draw-child.d.ts +36 -0
- package/dist/src/daemon/draw.d.ts +154 -0
- package/dist/src/daemon/git-evidence.d.ts +173 -0
- package/dist/src/daemon/git-evidence.js +1 -1
- package/dist/src/daemon/projection.d.ts +180 -0
- package/dist/src/daemon/prune.d.ts +207 -0
- package/dist/src/mcp/http.d.ts +113 -0
- package/dist/src/mcp/server.d.ts +265 -0
- package/dist/src/mcp/server.js +17 -1
- package/dist/src/mcp/server.js.map +1 -1
- package/docs/adapter-api.md +106 -0
- package/docs/cli-reference.md +1316 -63
- package/docs/codex-enforced-session.md +103 -0
- package/docs/codex-workspace-broker.md +118 -0
- package/package.json +14 -2
- package/schema/codex-instance.schema.json +82 -0
- package/schema/event.schema.json +539 -9
- 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/event/invalid/approval-granted-sender-hashed-false.json +20 -0
- package/schema/fixtures/event/invalid/approval-granted-sender-hashed-raw-id.json +20 -0
- package/schema/fixtures/event/invalid/audit-gesture-refused-human-actor.json +16 -0
- package/schema/fixtures/event/invalid/audit-gesture-refused-no-actor-no-sender.json +15 -0
- package/schema/fixtures/event/invalid/audit-gesture-refused-unknown-gesture.json +16 -0
- package/schema/fixtures/event/invalid/audit-question-preempted-agent-actor.json +16 -0
- package/schema/fixtures/event/invalid/audit-question-preempted-no-question-id.json +16 -0
- package/schema/fixtures/event/invalid/audit-question-preempted-unknown-source.json +15 -0
- package/schema/fixtures/event/invalid/gate-path-signed-off-absolute-path.json +14 -0
- package/schema/fixtures/event/invalid/gate-path-signed-off-agent-actor.json +14 -0
- package/schema/fixtures/event/invalid/gate-path-signed-off-missing-path.json +13 -0
- package/schema/fixtures/event/valid/approval-granted-sender-hashed.json +20 -0
- package/schema/fixtures/event/valid/audit-gesture-refused-review-note.json +21 -0
- package/schema/fixtures/event/valid/audit-gesture-refused-sender-key-unavailable.json +19 -0
- package/schema/fixtures/event/valid/audit-gesture-refused.json +19 -0
- package/schema/fixtures/event/valid/audit-question-preempted-no-verdict.json +16 -0
- package/schema/fixtures/event/valid/audit-question-preempted.json +20 -0
- package/schema/fixtures/event/valid/gate-path-signed-off.json +14 -0
- package/schema/fixtures/event/valid/harness-kind-claude-code.json +23 -0
- package/schema/fixtures/event/valid/harness-kind-codex.json +23 -0
- package/schema/fixtures/event/valid/harness-kind-cursor.json +23 -0
- package/schema/fixtures/event/valid/harness-kind-grok.json +23 -0
- package/schema/fixtures/event/valid/harness-kind-muse.json +23 -0
- package/schema/fixtures/policy/invalid/senders-half-keyed.json +20 -0
- package/schema/fixtures/policy/valid/canonical.json +1 -1
- package/schema/fixtures/policy/valid/senders-keyed.json +24 -0
- package/schema/fixtures/policy-md/valid/canonical.md +1 -1
- package/schema/fixtures/policy-md/valid/with-values.md +5 -7
- package/schema/fixtures/values/invalid/class-shaped.json +1 -1
- package/schema/fixtures/values/invalid/duplicate-entry.json +1 -1
- package/schema/fixtures/values/invalid/non-string-item.json +1 -1
- package/schema/fixtures/values/invalid/over-cap.json +1 -1
- package/schema/fixtures/values/invalid/unknown-key.json +1 -1
- package/schema/fixtures/values/invalid/version-float.json +1 -0
- package/schema/fixtures/values/invalid/version-integer.json +1 -0
- package/schema/fixtures/values/invalid/version-wrong-string.json +1 -0
- package/schema/fixtures/values/valid/empty-lists.json +2 -3
- package/schema/fixtures/values/valid/full.json +5 -7
- package/schema/fixtures/values/valid/minimal.json +1 -1
- package/schema/fixtures/values-md/invalid/schema-invalid.md +5 -3
- package/schema/fixtures/values-md/invalid/two-blocks.md +3 -3
- package/schema/fixtures/values-md/invalid/unterminated.md +2 -2
- package/schema/fixtures/values-md/invalid/version-1.md +69 -0
- package/schema/fixtures/values-md/invalid/version-unquoted.md +64 -0
- package/schema/fixtures/values-md/invalid/yaml-error.md +2 -2
- package/schema/fixtures/values-md/valid/absent.md +1 -1
- package/schema/fixtures/values-md/valid/with-values.md +5 -7
- package/schema/policy.schema.json +75 -3
- package/schema/values.schema.json +7 -11
- package/templates/codex/README.md +9 -0
- package/schema/fixtures/values/invalid/version-string.json +0 -1
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Budget evaluation from the log (SPEC.md §5.2, §8).
|
|
3
|
+
*
|
|
4
|
+
* "An action must pass its class limits AND global budgets. Budget consumption
|
|
5
|
+
* is computed from the log, never from a mutable counter." This module is that
|
|
6
|
+
* computation: given the records of the append-only log, the limits the policy
|
|
7
|
+
* matcher already resolved, the action about to be admitted, and the moment of
|
|
8
|
+
* evaluation, it returns a per-limit verdict and one conjunctive answer.
|
|
9
|
+
*
|
|
10
|
+
* Pure and deterministic: no I/O, no clock, no randomness, no caching. The
|
|
11
|
+
* evaluation timestamp is a **required parameter** — a budget decision that
|
|
12
|
+
* depended on ambient time could not be replayed from the log, and replay is
|
|
13
|
+
* the whole point of computing consumption from the log in the first place.
|
|
14
|
+
* This module also does not re-run class matching: the gate hands in the
|
|
15
|
+
* already-matched limits and the pattern that produced them.
|
|
16
|
+
*
|
|
17
|
+
* ## THE CONSUMPTION CONTRACT — what APRV-16 (the gate) MUST honor
|
|
18
|
+
*
|
|
19
|
+
* Budgets meter **authorization**, not completion. An authorized action
|
|
20
|
+
* consumes budget whether or not it ultimately executes, because the human's
|
|
21
|
+
* decision is the commitment; a runtime that only charged completed actions
|
|
22
|
+
* would let a crashed or hung executor mint unlimited authorizations.
|
|
23
|
+
*
|
|
24
|
+
* The evaluator therefore reads consumption from exactly two event types, and
|
|
25
|
+
* the gate MUST write them accordingly:
|
|
26
|
+
*
|
|
27
|
+
* 1. `approval.granted` — the manual path. A human said yes; budget is spent.
|
|
28
|
+
* 2. `execution.started` — the supervised/autonomous paths. Under the amended
|
|
29
|
+
* §6.3 those paths emit no approval events, so the record that authorizes
|
|
30
|
+
* execution *is* the start event.
|
|
31
|
+
*
|
|
32
|
+
* To avoid charging a manual action twice (granted, then started), an
|
|
33
|
+
* `execution.started` is counted only when the window contains no
|
|
34
|
+
* `approval.granted` bearing the same `action_key`.
|
|
35
|
+
*
|
|
36
|
+
* **The gate MUST record `payload.est_cost_usd` (a decimal USD string since
|
|
37
|
+
* APRV-121, a JSON number in records written before it) and
|
|
38
|
+
* `payload.class` (the action's dotted class string) on every
|
|
39
|
+
* `approval.granted` and `execution.started` event it appends.** Those two
|
|
40
|
+
* payload fields are the entire input to USD accounting and class scoping.
|
|
41
|
+
* A consuming event with no usable `est_cost_usd` contributes **0** to USD
|
|
42
|
+
* sums but still counts as **1** action for `daily_actions` — an authorization
|
|
43
|
+
* with no declared cost is still an authorization. A consuming event with no
|
|
44
|
+
* usable `payload.class` is invisible to class-scoped limits (it cannot be
|
|
45
|
+
* shown to belong to the class) but is still counted by global budgets, which
|
|
46
|
+
* charge every authorization regardless of class.
|
|
47
|
+
*
|
|
48
|
+
* Nothing else consumes. `approval.rejected`, `approval.expired`,
|
|
49
|
+
* `approval.revoked`, and `approval.withdrawn` (APRV-106) consume nothing: an
|
|
50
|
+
* authorization that was refused, lapsed, or never asked for in the end was
|
|
51
|
+
* never a commitment. `execution.completed` and `execution.failed`
|
|
52
|
+
* consume nothing either — they report on a commitment already charged at
|
|
53
|
+
* authorization time, and charging them again would double-count.
|
|
54
|
+
*
|
|
55
|
+
* ## The rolling window (SPEC.md §5.2, rolling-window amendment)
|
|
56
|
+
*
|
|
57
|
+
* A `daily` limit is evaluated over the 24 hours preceding the evaluation
|
|
58
|
+
* moment, not over a calendar day. An event consumes iff
|
|
59
|
+
*
|
|
60
|
+
* evaluationTs - 24h < event.ts <= evaluationTs
|
|
61
|
+
*
|
|
62
|
+
* — half-open at the bottom, closed at the top. An event exactly 24h old has
|
|
63
|
+
* aged out; an event stamped at the evaluation instant is in. The bound is
|
|
64
|
+
* half-open on exactly one side so that consecutive 24h windows tile the
|
|
65
|
+
* timeline without double-counting a boundary event. Timestamps are compared
|
|
66
|
+
* via `Date.parse` on the RFC 3339 strings the schema already guarantees.
|
|
67
|
+
*
|
|
68
|
+
* Rolling, not calendar: a burst that straddles midnight must not have its own
|
|
69
|
+
* tripwire reset underneath it.
|
|
70
|
+
*
|
|
71
|
+
* ## Fail-closed
|
|
72
|
+
*
|
|
73
|
+
* A limit the evaluator does not understand cannot be proven satisfied, so it
|
|
74
|
+
* fails: an unknown limit name yields `pass: false` with an explanatory `note`.
|
|
75
|
+
* The same applies to an unparseable evaluation timestamp (no window can be
|
|
76
|
+
* computed) and to class-scoped rolling limits offered without the class
|
|
77
|
+
* pattern that scopes their consumption. Silence is never a grant.
|
|
78
|
+
*
|
|
79
|
+
* ## What this module does NOT evaluate (APRV-173)
|
|
80
|
+
*
|
|
81
|
+
* `max_pending` and `requests_per_hour` (SPEC.md §5.2) are request-volume
|
|
82
|
+
* limits: they cap the approver's queue rather than the world's exposure, they
|
|
83
|
+
* are counted from `approval.requested` rather than from authorizations, and
|
|
84
|
+
* `core/intake-limits.ts` evaluates them at intake. They are skipped by name
|
|
85
|
+
* here rather than refused as unknown limits, and the skip is exactly the two
|
|
86
|
+
* names that module owns, read from its own exported list. Neither module's
|
|
87
|
+
* silence widens a ceiling: every limit name is evaluated by one of them, or
|
|
88
|
+
* fails closed as unknown in this one.
|
|
89
|
+
*/
|
|
90
|
+
import type { Policy } from "./policy-load.js";
|
|
91
|
+
import type { EventRecord } from "./log.js";
|
|
92
|
+
import { type UsdInput } from "./money.js";
|
|
93
|
+
/** Length of the rolling `daily` window: 24 hours, in milliseconds. */
|
|
94
|
+
export declare const WINDOW_MS: number;
|
|
95
|
+
/** Event types that authorize execution and therefore consume budget. */
|
|
96
|
+
export declare const CONSUMING_EVENTS: readonly ["approval.granted", "execution.started"];
|
|
97
|
+
/**
|
|
98
|
+
* Which limits apply, as resolved by the policy matcher — this module does not
|
|
99
|
+
* re-run matching.
|
|
100
|
+
*
|
|
101
|
+
* - `classLimits` is `Resolution.limits`: the matched rule's `limits` map.
|
|
102
|
+
* - `classPattern` is the pattern of the rule those limits came from
|
|
103
|
+
* (`Resolution.matched.pattern`). Class-scoped rolling limits count only
|
|
104
|
+
* authorizations whose `payload.class` matches this **same rule pattern** —
|
|
105
|
+
* not string equality with the action's class. A `financial.*` rule is one
|
|
106
|
+
* budget shared by every class it governs, which is what a policy author
|
|
107
|
+
* writing a single `daily_usd` under `financial.*` means; charging
|
|
108
|
+
* `financial.spend` and `financial.transfer` to separate invisible buckets
|
|
109
|
+
* would silently double the ceiling they wrote.
|
|
110
|
+
* - `globalBudgets` is `policy.budgets`: named scopes, each conjunctive.
|
|
111
|
+
*/
|
|
112
|
+
export interface BudgetScope {
|
|
113
|
+
classLimits: Record<string, number> | null;
|
|
114
|
+
classPattern: string | null;
|
|
115
|
+
globalBudgets: Policy["budgets"] | null;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* The action being admitted. `est_cost_usd` absent means "no declared cost".
|
|
119
|
+
*
|
|
120
|
+
* The amount is a canonical decimal string (APRV-121, `core/money.ts`). A JSON
|
|
121
|
+
* number is accepted here as the historical form — records written before that
|
|
122
|
+
* change carry one, and this evaluator must read them identically to before.
|
|
123
|
+
*/
|
|
124
|
+
export interface BudgetAction {
|
|
125
|
+
class: string;
|
|
126
|
+
est_cost_usd?: UsdInput;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Which window a limit is measured over.
|
|
130
|
+
*
|
|
131
|
+
* `task-total` is the envelope cap of SPEC.md §6.2 (`budget.max_cost_usd`): not
|
|
132
|
+
* a window at all but the whole life of one task, which is what "maximum total
|
|
133
|
+
* spend across this task's actions" means.
|
|
134
|
+
*/
|
|
135
|
+
export type BudgetWindow = "per-action" | "rolling-24h" | "task-total";
|
|
136
|
+
/**
|
|
137
|
+
* Where a limit came from: the matched class rule, `policy.budgets`, or the
|
|
138
|
+
* task's own registered envelope (SPEC.md §6.2 `budget`). All three are
|
|
139
|
+
* conjunctive with each other — "the stricter of the two binds".
|
|
140
|
+
*/
|
|
141
|
+
export type BudgetVerdictScope = "class" | "global" | "task";
|
|
142
|
+
/**
|
|
143
|
+
* One limit's outcome.
|
|
144
|
+
*
|
|
145
|
+
* `consumed` is what the window already holds, `requested` is what this action
|
|
146
|
+
* would add (USD for money limits, `1` for action counts), and `remaining` is
|
|
147
|
+
* `limit - consumed - requested` — the headroom left *after* admitting the
|
|
148
|
+
* action, so a `pass: false` verdict shows how far over the line it is.
|
|
149
|
+
* `note` is present only when the verdict needs explaining, which at v0.1 means
|
|
150
|
+
* only fail-closed refusals.
|
|
151
|
+
*
|
|
152
|
+
* The three figures are **decimal strings** (APRV-121). A failing verdict is
|
|
153
|
+
* copied verbatim into the `budget.exceeded` payload, which is hashed material,
|
|
154
|
+
* and a float there would be exactly the cross-language serialization hazard
|
|
155
|
+
* this project removed from `est_cost_usd`. Money is reported in canonical USD
|
|
156
|
+
* (`"0.3"`), counts as integers (`"3"`); a negative `remaining` — headroom
|
|
157
|
+
* already spent — is spelled with a leading `-`, so it is a decimal string but
|
|
158
|
+
* not a canonical amount, which only ever describes money being declared.
|
|
159
|
+
*/
|
|
160
|
+
export interface BudgetVerdict {
|
|
161
|
+
limit: string;
|
|
162
|
+
scope: BudgetVerdictScope;
|
|
163
|
+
window: BudgetWindow;
|
|
164
|
+
consumed: string;
|
|
165
|
+
requested: string;
|
|
166
|
+
remaining: string;
|
|
167
|
+
pass: boolean;
|
|
168
|
+
note?: string;
|
|
169
|
+
}
|
|
170
|
+
/** Outcome of {@link evaluateBudgets}. Conjunctive: all must pass. */
|
|
171
|
+
export interface BudgetVerdicts {
|
|
172
|
+
pass: boolean;
|
|
173
|
+
verdicts: BudgetVerdict[];
|
|
174
|
+
}
|
|
175
|
+
/** The verdict label for the envelope's own cap (SPEC.md §6.2 `budget`). */
|
|
176
|
+
export declare const TASK_MAX_COST_USD = "budget.max_cost_usd";
|
|
177
|
+
/**
|
|
178
|
+
* Evaluate every applicable budget limit against the log.
|
|
179
|
+
*
|
|
180
|
+
* Conjunctive (SPEC.md §5.2): `pass` is true only when every verdict passes.
|
|
181
|
+
* Verdicts are emitted class limits first (limit names ascending), then global
|
|
182
|
+
* budgets (scope name ascending, limit name ascending within a scope), so the
|
|
183
|
+
* list is byte-stable regardless of policy key order.
|
|
184
|
+
*
|
|
185
|
+
* `records` may be the whole log; only the rolling window is consulted, and the
|
|
186
|
+
* caller is never asked to pre-filter (a caller that filtered wrongly would
|
|
187
|
+
* silently widen the budget).
|
|
188
|
+
*/
|
|
189
|
+
export declare function evaluateBudgets(records: EventRecord[], scope: BudgetScope, action: BudgetAction, evaluationTs: string): BudgetVerdicts;
|
|
190
|
+
/**
|
|
191
|
+
* The registered envelope's `budget.max_cost_usd` for `task`, or `null`.
|
|
192
|
+
*
|
|
193
|
+
* Read from the **log**, not from the task file: the file may have been edited
|
|
194
|
+
* since registration, and an agent that could raise its own cap by editing
|
|
195
|
+
* frontmatter after the fact would be authoring the ceiling it is judged by.
|
|
196
|
+
* `register` copies the envelope's `budget` block into the `task.registered`
|
|
197
|
+
* payload for exactly this read. The last registration wins, matching
|
|
198
|
+
* `findDeclaration` in `core/execute.ts`.
|
|
199
|
+
*
|
|
200
|
+
* A cap that is not a finite non-negative number is `null` — absent rather than
|
|
201
|
+
* zero. The schema already refuses those shapes at the write boundary, and
|
|
202
|
+
* inventing a $0 ceiling for a malformed one would refuse every action of the
|
|
203
|
+
* task with a message about money nobody wrote down.
|
|
204
|
+
*/
|
|
205
|
+
export declare function taskMaxCostUsd(records: EventRecord[], task: string): string | null;
|
|
206
|
+
/**
|
|
207
|
+
* Evaluate the task's own cap: does admitting `action` keep the SUM of this
|
|
208
|
+
* task's authorized `est_cost_usd` at or under `maxCostUsd`?
|
|
209
|
+
*
|
|
210
|
+
* Commitment-based and consumption-identical to {@link evaluateBudgets}: the
|
|
211
|
+
* same two event types authorize (`approval.granted`, and `execution.started`
|
|
212
|
+
* only where no grant carries the same `action_key`), so a manual action that is
|
|
213
|
+
* granted and then started is charged once. The only differences are scope —
|
|
214
|
+
* events of *this task* — and window: there is none. A task cap is a lifetime
|
|
215
|
+
* total, so an envelope that says `max_cost_usd: 0.5` cannot be spent twice by
|
|
216
|
+
* waiting a day.
|
|
217
|
+
*
|
|
218
|
+
* `evaluationTs` is accepted for symmetry with the windowed evaluator and to
|
|
219
|
+
* keep every budget call site shaped alike; it selects no window here and the
|
|
220
|
+
* verdict does not depend on it.
|
|
221
|
+
*/
|
|
222
|
+
export declare function evaluateTaskBudget(records: EventRecord[], task: string, maxCostUsd: UsdInput, action: BudgetAction, _evaluationTs: string): BudgetVerdict;
|
|
223
|
+
/**
|
|
224
|
+
* Every applicable budget, conjunctively: class limits, global budgets, and the
|
|
225
|
+
* task envelope's own cap.
|
|
226
|
+
*
|
|
227
|
+
* This is the function the three enforcement points call (`gate.request`,
|
|
228
|
+
* `gate.decide`'s grant path, `execute.startExecution`), so the envelope cap is
|
|
229
|
+
* checked at intake, at grant, and at execution start — the same three moments
|
|
230
|
+
* policy budgets are checked, because a cap enforced at only one of them is a
|
|
231
|
+
* cap a caller can route around by choosing a different door.
|
|
232
|
+
*
|
|
233
|
+
* Verdict order is class limits, then global budgets, then the task cap: the
|
|
234
|
+
* existing byte-stable order with one deterministic addition at the end.
|
|
235
|
+
* `task` may be `null` for a call site that has no task in hand, in which case
|
|
236
|
+
* the cap simply does not apply.
|
|
237
|
+
*/
|
|
238
|
+
export declare function evaluateBudgetsWithTask(records: EventRecord[], scope: BudgetScope, action: BudgetAction, evaluationTs: string, task: string | null): BudgetVerdicts;
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which instance owns which bot (APRV-390).
|
|
3
|
+
*
|
|
4
|
+
* APRV-178 scoped the OS keystore's ITEM NAMES to an instance, which closed the
|
|
5
|
+
* half of the incident where two gates read one item. It did not close the
|
|
6
|
+
* other half: two instances can still be handed the same bot token under two
|
|
7
|
+
* perfectly distinct names. `core/instance.ts` answers its questions from names
|
|
8
|
+
* alone, on purpose, and a name cannot tell you that two different names hold
|
|
9
|
+
* one value.
|
|
10
|
+
*
|
|
11
|
+
* Observed again on 2026-09-19: the primary daemon and the demo gate in
|
|
12
|
+
* `~/demo-gate` both long-polled one bot, both printed `getUpdates` HTTP 409
|
|
13
|
+
* Conflict on every poll, and neither phone channel worked. The only thing that
|
|
14
|
+
* distinguishes those two processes is what the Bot API says the token IS, and
|
|
15
|
+
* the only call that asks is `getMe`. So this module records `getMe`'s answer
|
|
16
|
+
* and makes the second claim on one bot a refusal instead of a 409.
|
|
17
|
+
*
|
|
18
|
+
* ## The two records
|
|
19
|
+
*
|
|
20
|
+
* ```
|
|
21
|
+
* <instance>/.approval/channel-owner.json one instance's own bots
|
|
22
|
+
* <state dir>/approval/bots.json every instance's claims, per machine
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* The per-instance file is this instance's copy of what it probed, gitignored
|
|
26
|
+
* and rebuildable by one `getMe`. The registry is the part that has to be
|
|
27
|
+
* SHARED, because the question is "does anything else on this machine hold this
|
|
28
|
+
* bot", and no file inside one instance can answer that.
|
|
29
|
+
*
|
|
30
|
+
* ## Why the registry is a plain file, and why it is not under `.approval`
|
|
31
|
+
*
|
|
32
|
+
* **Not the OS keystore.** Every reader of this registry is a diagnostic or a
|
|
33
|
+
* start-up preflight, and neither may block on an unlock dialog — that is
|
|
34
|
+
* `NON_RESOLVING_RUNNER`'s rule in `core/env-file.ts`, and `approval doctor`
|
|
35
|
+
* already answers the whole keychain-scope row from names for exactly this
|
|
36
|
+
* reason. A machine with no keystore backend at all still needs this refusal,
|
|
37
|
+
* and would get nothing from a store it does not have. And there is no secret
|
|
38
|
+
* here to justify one: a bot id, a `@username`, an instance id and a directory
|
|
39
|
+
* path are all things `.approval/env` carries in the open.
|
|
40
|
+
*
|
|
41
|
+
* **Not a `.approval` directory under the home directory**, which was the other
|
|
42
|
+
* candidate. This project's own policy reserves anything under `.approval/` to
|
|
43
|
+
* the human's ceremony (`policy.core`, human-only), and the hook classifier
|
|
44
|
+
* applies that to the name wherever it appears. A file the runtime rewrites on
|
|
45
|
+
* every listener start does not belong in the directory the policy holds shut.
|
|
46
|
+
*
|
|
47
|
+
* So it goes where this runtime already puts per-user state that is derived
|
|
48
|
+
* rather than evidence: the same platform split `cli/setup-service.ts` uses for
|
|
49
|
+
* a service's console output. {@link APPROVAL_STATE_DIR_ENV} overrides it, which
|
|
50
|
+
* is what the test suite sets — a suite that wrote the operator's real registry
|
|
51
|
+
* would be the mistake the Muse probe's suite made with its live pointer.
|
|
52
|
+
*
|
|
53
|
+
* ## What it is not
|
|
54
|
+
*
|
|
55
|
+
* Not evidence, and never consulted by an enforcement path. Nothing in here
|
|
56
|
+
* widens a permission, and losing the whole file costs one re-probe. It answers
|
|
57
|
+
* one question — "is another instance on this machine already polling this
|
|
58
|
+
* bot?" — and that answer only ever produces a REFUSAL. A registry that could
|
|
59
|
+
* be edited into granting something would be a policy file; this one can be
|
|
60
|
+
* edited into letting a 409 happen, which is the state without it.
|
|
61
|
+
*/
|
|
62
|
+
/** The per-instance record's filename, inside `.approval/`. Gitignored. */
|
|
63
|
+
export declare const CHANNEL_OWNER_FILE = "channel-owner.json";
|
|
64
|
+
/** The per-machine registry's filename, inside the state directory. */
|
|
65
|
+
export declare const BOT_REGISTRY_FILE = "bots.json";
|
|
66
|
+
/**
|
|
67
|
+
* Where the per-machine registry lives, overriding the platform default.
|
|
68
|
+
*
|
|
69
|
+
* Set by the test suite, and available to an operator running two gates under
|
|
70
|
+
* one account who wants them in separate state trees. An empty or unset value
|
|
71
|
+
* means the platform default.
|
|
72
|
+
*/
|
|
73
|
+
export declare const APPROVAL_STATE_DIR_ENV = "APPROVAL_STATE_DIR";
|
|
74
|
+
/** The only format version this build writes, and the only one it reads. */
|
|
75
|
+
export declare const CHANNEL_OWNER_VERSION = 1;
|
|
76
|
+
/**
|
|
77
|
+
* The channels this registry knows about.
|
|
78
|
+
*
|
|
79
|
+
* One entry today. It is a union rather than a bare string because the registry
|
|
80
|
+
* is per-machine and long-lived: a second channel with a bot-like identity
|
|
81
|
+
* (APRV-383's hosted daemon id is the named candidate) has to be able to land
|
|
82
|
+
* beside Telegram's rows without either one's reader guessing what a row means.
|
|
83
|
+
*/
|
|
84
|
+
export type OwnedChannel = "telegram";
|
|
85
|
+
/** What one `getMe` said, reduced to the fields that identify a bot. */
|
|
86
|
+
export interface BotIdentity {
|
|
87
|
+
channel: OwnedChannel;
|
|
88
|
+
/** The Bot API's own numeric id, as a string. Stable for the life of a bot. */
|
|
89
|
+
botId: string;
|
|
90
|
+
/** `@name`, for the human reading the refusal. Never matched on. */
|
|
91
|
+
username: string;
|
|
92
|
+
/**
|
|
93
|
+
* The Bot API deployment that issued `botId`, normalised.
|
|
94
|
+
*
|
|
95
|
+
* Part of the identity rather than a note beside it: a bot id is unique
|
|
96
|
+
* within one Bot API, and nothing more. Two gates pointed at two different
|
|
97
|
+
* API bases — a self-hosted Bot API server and Telegram's own, or two local
|
|
98
|
+
* ones — hold two different bots however their ids compare, and refusing the
|
|
99
|
+
* second would be refusing a configuration that cannot conflict. Both of the
|
|
100
|
+
* gates in the incident this task is named after used the default base, so
|
|
101
|
+
* this narrows nothing that mattered there.
|
|
102
|
+
*/
|
|
103
|
+
apiBase: string;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* One Bot API base, reduced so two spellings of one deployment compare equal.
|
|
107
|
+
*
|
|
108
|
+
* Lowercased and stripped of trailing slashes, which are the two ways the same
|
|
109
|
+
* base is written. Nothing more is attempted: a host that resolves to the same
|
|
110
|
+
* server under two names is two names here, and that is the safe direction of
|
|
111
|
+
* the error — two identities, so the second gate is allowed to start, exactly
|
|
112
|
+
* as it was before this module existed.
|
|
113
|
+
*/
|
|
114
|
+
export declare function normaliseApiBase(apiBase: string): string;
|
|
115
|
+
/** One instance's claim on one bot. */
|
|
116
|
+
export interface BotClaim extends BotIdentity {
|
|
117
|
+
/** The claiming instance's short id, as `approval doctor` prints it. */
|
|
118
|
+
instanceId: string;
|
|
119
|
+
/** That instance's `.approval` directory, absolute. */
|
|
120
|
+
instanceHome: string;
|
|
121
|
+
/** When the claim was made or last re-proved, ISO-8601. */
|
|
122
|
+
claimedAt: string;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* The per-user state directory for this runtime.
|
|
126
|
+
*
|
|
127
|
+
* The split is `cli/setup-service.ts`'s `defaultLogsDir`, one level up: macOS
|
|
128
|
+
* keeps user application state under `Library/Application Support` and Linux
|
|
129
|
+
* under `.local/state` (XDG's state home, which is where "state that should
|
|
130
|
+
* persist between restarts but is not config and is not a cache" belongs).
|
|
131
|
+
*/
|
|
132
|
+
export declare function stateDirFor(env?: NodeJS.ProcessEnv): string;
|
|
133
|
+
/** The per-machine registry's path. */
|
|
134
|
+
export declare function registryPathFor(env?: NodeJS.ProcessEnv): string;
|
|
135
|
+
/** The per-instance record's path, for the instance owning `logPath`. */
|
|
136
|
+
export declare function ownerPathFor(logPath: string): string;
|
|
137
|
+
/**
|
|
138
|
+
* Every claim recorded on this machine, newest write last.
|
|
139
|
+
*
|
|
140
|
+
* An unreadable, absent or malformed registry is an EMPTY one. It is a cache of
|
|
141
|
+
* probes, so "I do not know of any claim" is the honest answer and it costs one
|
|
142
|
+
* `getMe` to rebuild — whereas failing a listener start-up because a JSON file
|
|
143
|
+
* in a state directory got truncated would take the phone channel down for a
|
|
144
|
+
* reason that has nothing to do with the gate.
|
|
145
|
+
*/
|
|
146
|
+
export declare function readRegistry(env?: NodeJS.ProcessEnv): BotClaim[];
|
|
147
|
+
/** What this instance last recorded about its own channels. */
|
|
148
|
+
export declare function readOwner(logPath: string): BotClaim[];
|
|
149
|
+
/**
|
|
150
|
+
* The claim this instance holds on `channel`, or `null`.
|
|
151
|
+
*
|
|
152
|
+
* Read from the per-instance file, which is what `approval channel telegram
|
|
153
|
+
* health` and `approval doctor` report from: both are offline, and both must
|
|
154
|
+
* answer without a network call or a keystore lookup.
|
|
155
|
+
*/
|
|
156
|
+
export declare function ownedBot(logPath: string, channel: OwnedChannel): BotClaim | null;
|
|
157
|
+
/**
|
|
158
|
+
* Every OTHER instance on this machine that has claimed `botId`.
|
|
159
|
+
*
|
|
160
|
+
* "Other" is decided by instance id, so a re-run in the same instance is never
|
|
161
|
+
* reported against itself, and two directories that reach one gate by different
|
|
162
|
+
* spellings are two instances — which is `core/instance.ts`'s deliberate choice
|
|
163
|
+
* of the safe error direction, and this module inherits it rather than adding a
|
|
164
|
+
* second rule that could disagree.
|
|
165
|
+
*/
|
|
166
|
+
export declare function otherOwnersOf(logPath: string, botId: string, apiBase: string, env?: NodeJS.ProcessEnv): BotClaim[];
|
|
167
|
+
/** Why a claim was refused. Machine-readable and distinct (SPEC §11.1 inv. 6). */
|
|
168
|
+
export type ClaimRefusalCode = "bot-owned-elsewhere";
|
|
169
|
+
export type ClaimResult = {
|
|
170
|
+
ok: true;
|
|
171
|
+
claim: BotClaim;
|
|
172
|
+
} | {
|
|
173
|
+
ok: false;
|
|
174
|
+
code: ClaimRefusalCode;
|
|
175
|
+
owners: BotClaim[];
|
|
176
|
+
message: string;
|
|
177
|
+
};
|
|
178
|
+
/**
|
|
179
|
+
* One sentence naming who else holds this bot, for a refusal or a 409 report.
|
|
180
|
+
*
|
|
181
|
+
* Exported because three surfaces print it — the listener preflight, `approval
|
|
182
|
+
* setup channel telegram`, and the runtime 409 report — and three spellings of
|
|
183
|
+
* "another instance owns this bot" is three chances for them to name different
|
|
184
|
+
* instances for one fact.
|
|
185
|
+
*/
|
|
186
|
+
export declare function describeOwners(owners: readonly BotClaim[]): string;
|
|
187
|
+
/**
|
|
188
|
+
* Record this instance as `identity`'s owner, unless somebody else already is.
|
|
189
|
+
*
|
|
190
|
+
* Read-then-write rather than compare-and-append: this is a local cache of
|
|
191
|
+
* probes and not the log, so the property it needs is "two instances racing
|
|
192
|
+
* both see a refusal or one of them wins", which a same-machine read-then-write
|
|
193
|
+
* over a file rewritten in whole gives. It is deliberately NOT passed through
|
|
194
|
+
* the log's append path — nothing here is evidence, and putting a re-probe on
|
|
195
|
+
* every listener start into the hash chain would be writing a heartbeat into
|
|
196
|
+
* the record the project's whole argument says must stay small and meaningful.
|
|
197
|
+
*
|
|
198
|
+
* The refusal names the other instance, because "this bot is taken" without
|
|
199
|
+
* saying by what is a message that sends an operator looking through `ps`.
|
|
200
|
+
*/
|
|
201
|
+
export declare function claimBot(logPath: string, identity: BotIdentity, options?: {
|
|
202
|
+
now?: () => Date;
|
|
203
|
+
env?: NodeJS.ProcessEnv;
|
|
204
|
+
}): ClaimResult;
|
|
205
|
+
/**
|
|
206
|
+
* Drop this instance's claim on `channel`, from both records.
|
|
207
|
+
*
|
|
208
|
+
* Used by nothing on the happy path. It exists because the alternative to a way
|
|
209
|
+
* out is an operator hand-editing a JSON file in a state directory to recover
|
|
210
|
+
* from a stale claim, and a recovery step that is not a command is a recovery
|
|
211
|
+
* step that gets done wrong.
|
|
212
|
+
*/
|
|
213
|
+
export declare function releaseBot(logPath: string, channel: OwnedChannel, env?: NodeJS.ProcessEnv): void;
|