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,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read-only planning for a future policy-bound Codex workspace broker (APRV-325.2.1).
|
|
3
|
+
*
|
|
4
|
+
* This module validates a closed change language, classifies every endpoint,
|
|
5
|
+
* and binds the bytes and filesystem identities it observes. It does not append
|
|
6
|
+
* to the approval log, mutate a workspace, establish OS custody, or make the
|
|
7
|
+
* supplied context trustworthy. A future broker must supply that context from
|
|
8
|
+
* its protected configuration and retain exclusive write custody while it
|
|
9
|
+
* revalidates and applies an authorized plan.
|
|
10
|
+
*/
|
|
11
|
+
import type { PolicyLoadResult } from "../core/policy-load.js";
|
|
12
|
+
export declare const WORKSPACE_PLAN_VERSION: "approval.codex.workspace-plan.v1";
|
|
13
|
+
export declare const MAX_WORKSPACE_OPERATIONS = 64;
|
|
14
|
+
export declare const MAX_WORKSPACE_PATH_BYTES = 1024;
|
|
15
|
+
export declare const MAX_WORKSPACE_IMAGE_BYTES = 1048576;
|
|
16
|
+
export declare const MAX_WORKSPACE_TOTAL_BYTES = 8388608;
|
|
17
|
+
export declare const MAX_WORKSPACE_DIRECTORY_ENTRIES = 4096;
|
|
18
|
+
export type WorkspaceOperationInput = {
|
|
19
|
+
kind: "create";
|
|
20
|
+
path: string;
|
|
21
|
+
after_base64: string;
|
|
22
|
+
} | {
|
|
23
|
+
kind: "replace";
|
|
24
|
+
path: string;
|
|
25
|
+
expected_before_sha256: string;
|
|
26
|
+
after_base64: string;
|
|
27
|
+
} | {
|
|
28
|
+
kind: "delete";
|
|
29
|
+
path: string;
|
|
30
|
+
expected_before_sha256: string;
|
|
31
|
+
} | {
|
|
32
|
+
kind: "move";
|
|
33
|
+
from: string;
|
|
34
|
+
to: string;
|
|
35
|
+
expected_before_sha256: string;
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Assertions supplied by the future trusted broker. The planner validates their
|
|
39
|
+
* shape and binds them; possession of this object proves no custody or attestation.
|
|
40
|
+
*/
|
|
41
|
+
export interface WorkspacePlanContext {
|
|
42
|
+
actor: string;
|
|
43
|
+
root: string;
|
|
44
|
+
policy_sha256: string;
|
|
45
|
+
policy: PolicyLoadResult;
|
|
46
|
+
}
|
|
47
|
+
export interface FsIdentity {
|
|
48
|
+
dev: string;
|
|
49
|
+
ino: string;
|
|
50
|
+
}
|
|
51
|
+
export interface DirectoryBinding extends FsIdentity {
|
|
52
|
+
path: string;
|
|
53
|
+
}
|
|
54
|
+
export interface FileMetadata extends FsIdentity {
|
|
55
|
+
mode: string;
|
|
56
|
+
uid: string;
|
|
57
|
+
gid: string;
|
|
58
|
+
nlink: string;
|
|
59
|
+
size: string;
|
|
60
|
+
mtime_ns: string;
|
|
61
|
+
ctime_ns: string;
|
|
62
|
+
}
|
|
63
|
+
export interface BoundImage {
|
|
64
|
+
base64: string;
|
|
65
|
+
sha256: string;
|
|
66
|
+
byte_length: number;
|
|
67
|
+
}
|
|
68
|
+
export interface BoundFileImage extends BoundImage {
|
|
69
|
+
metadata: FileMetadata;
|
|
70
|
+
}
|
|
71
|
+
export type PlannedWorkspaceOperation = {
|
|
72
|
+
kind: "create";
|
|
73
|
+
path: string;
|
|
74
|
+
class: string;
|
|
75
|
+
directories: readonly DirectoryBinding[];
|
|
76
|
+
after: BoundImage;
|
|
77
|
+
} | {
|
|
78
|
+
kind: "replace";
|
|
79
|
+
path: string;
|
|
80
|
+
class: string;
|
|
81
|
+
directories: readonly DirectoryBinding[];
|
|
82
|
+
before: BoundFileImage;
|
|
83
|
+
after: BoundImage;
|
|
84
|
+
} | {
|
|
85
|
+
kind: "delete";
|
|
86
|
+
path: string;
|
|
87
|
+
class: string;
|
|
88
|
+
directories: readonly DirectoryBinding[];
|
|
89
|
+
before: BoundFileImage;
|
|
90
|
+
} | {
|
|
91
|
+
kind: "move";
|
|
92
|
+
from: string;
|
|
93
|
+
to: string;
|
|
94
|
+
classes: readonly string[];
|
|
95
|
+
source_directories: readonly DirectoryBinding[];
|
|
96
|
+
destination_directories: readonly DirectoryBinding[];
|
|
97
|
+
before: BoundFileImage;
|
|
98
|
+
after: BoundImage;
|
|
99
|
+
};
|
|
100
|
+
export interface WorkspacePlanPayload {
|
|
101
|
+
version: typeof WORKSPACE_PLAN_VERSION;
|
|
102
|
+
actor: string;
|
|
103
|
+
root: string;
|
|
104
|
+
root_identity: FsIdentity;
|
|
105
|
+
policy_sha256: string;
|
|
106
|
+
operations: readonly PlannedWorkspaceOperation[];
|
|
107
|
+
}
|
|
108
|
+
export interface WorkspaceActionLeg {
|
|
109
|
+
class: string;
|
|
110
|
+
reversible: false;
|
|
111
|
+
payload_hash: string;
|
|
112
|
+
}
|
|
113
|
+
export interface WorkspacePlan {
|
|
114
|
+
payload: WorkspacePlanPayload;
|
|
115
|
+
payload_hash: string;
|
|
116
|
+
actions: readonly WorkspaceActionLeg[];
|
|
117
|
+
}
|
|
118
|
+
export type WorkspacePlanRefusalCode = "input-invalid" | "operation-limit" | "path-invalid" | "path-conflict" | "payload-too-large" | "policy-unavailable" | "class-human-only" | "root-unsafe" | "parent-unsafe" | "source-unsafe" | "destination-exists" | "preimage-mismatch" | "workspace-drift" | "context-drift";
|
|
119
|
+
export type WorkspacePlanResult = {
|
|
120
|
+
ok: true;
|
|
121
|
+
plan: WorkspacePlan;
|
|
122
|
+
} | {
|
|
123
|
+
ok: false;
|
|
124
|
+
code: WorkspacePlanRefusalCode;
|
|
125
|
+
message: string;
|
|
126
|
+
path?: string;
|
|
127
|
+
};
|
|
128
|
+
/** Build a deterministic immutable proposal without changing the workspace. */
|
|
129
|
+
export declare function planWorkspaceProposal(input: unknown, context: WorkspacePlanContext): WorkspacePlanResult;
|
|
130
|
+
/** Re-read and reclassify a plan. Success means only that the snapshot still matches. */
|
|
131
|
+
export declare function revalidateWorkspacePlan(plan: WorkspacePlan, context: WorkspacePlanContext): WorkspacePlanResult;
|
|
@@ -0,0 +1,561 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read-only planning for a future policy-bound Codex workspace broker (APRV-325.2.1).
|
|
3
|
+
*
|
|
4
|
+
* This module validates a closed change language, classifies every endpoint,
|
|
5
|
+
* and binds the bytes and filesystem identities it observes. It does not append
|
|
6
|
+
* to the approval log, mutate a workspace, establish OS custody, or make the
|
|
7
|
+
* supplied context trustworthy. A future broker must supply that context from
|
|
8
|
+
* its protected configuration and retain exclusive write custody while it
|
|
9
|
+
* revalidates and applies an authorized plan.
|
|
10
|
+
*/
|
|
11
|
+
import { createHash } from "node:crypto";
|
|
12
|
+
import { closeSync, constants, fstatSync, lstatSync, openSync, opendirSync, readSync, realpathSync, } from "node:fs";
|
|
13
|
+
import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
14
|
+
import { protectedPathClass } from "../core/command-class.js";
|
|
15
|
+
import { canonicalize } from "../core/jcs.js";
|
|
16
|
+
import { resolve as resolvePolicy } from "../core/policy-match.js";
|
|
17
|
+
import { payloadHash } from "../core/payload.js";
|
|
18
|
+
export const WORKSPACE_PLAN_VERSION = "approval.codex.workspace-plan.v1";
|
|
19
|
+
export const MAX_WORKSPACE_OPERATIONS = 64;
|
|
20
|
+
export const MAX_WORKSPACE_PATH_BYTES = 1_024;
|
|
21
|
+
export const MAX_WORKSPACE_IMAGE_BYTES = 1_048_576;
|
|
22
|
+
export const MAX_WORKSPACE_TOTAL_BYTES = 8_388_608;
|
|
23
|
+
export const MAX_WORKSPACE_DIRECTORY_ENTRIES = 4_096;
|
|
24
|
+
const HEX64 = /^[0-9a-f]{64}$/u;
|
|
25
|
+
const MAX_BASE64_LENGTH = 4 * Math.ceil(MAX_WORKSPACE_IMAGE_BYTES / 3);
|
|
26
|
+
const ALWAYS_HUMAN_ONLY = new Set(["policy.core", "log.mutate", "account.credential"]);
|
|
27
|
+
function refuse(code, message, path) {
|
|
28
|
+
return path === undefined ? { ok: false, code, message } : { ok: false, code, message, path };
|
|
29
|
+
}
|
|
30
|
+
function plainObject(value) {
|
|
31
|
+
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
32
|
+
return false;
|
|
33
|
+
const prototype = Object.getPrototypeOf(value);
|
|
34
|
+
return prototype === Object.prototype || prototype === null;
|
|
35
|
+
}
|
|
36
|
+
function exactKeys(value, expected) {
|
|
37
|
+
const actual = Object.keys(value).sort();
|
|
38
|
+
const wanted = [...expected].sort();
|
|
39
|
+
return actual.length === wanted.length && actual.every((key, index) => key === wanted[index]);
|
|
40
|
+
}
|
|
41
|
+
function containsControl(value) {
|
|
42
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
43
|
+
const unit = value.charCodeAt(index);
|
|
44
|
+
if (unit <= 0x1f || unit === 0x7f)
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
function hasWellFormedUtf16(value) {
|
|
50
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
51
|
+
const unit = value.charCodeAt(index);
|
|
52
|
+
if (unit >= 0xd800 && unit <= 0xdbff) {
|
|
53
|
+
if (index + 1 >= value.length)
|
|
54
|
+
return false;
|
|
55
|
+
const next = value.charCodeAt(index + 1);
|
|
56
|
+
if (next < 0xdc00 || next > 0xdfff)
|
|
57
|
+
return false;
|
|
58
|
+
index += 1;
|
|
59
|
+
}
|
|
60
|
+
else if (unit >= 0xdc00 && unit <= 0xdfff) {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
function agentActor(value) {
|
|
67
|
+
return value.startsWith("agent:") &&
|
|
68
|
+
value.length > "agent:".length &&
|
|
69
|
+
!containsControl(value) &&
|
|
70
|
+
hasWellFormedUtf16(value);
|
|
71
|
+
}
|
|
72
|
+
function safePath(value) {
|
|
73
|
+
if (typeof value !== "string" || value.length === 0 || value !== value.normalize("NFC"))
|
|
74
|
+
return null;
|
|
75
|
+
if (!hasWellFormedUtf16(value))
|
|
76
|
+
return null;
|
|
77
|
+
if (Buffer.byteLength(value, "utf8") > MAX_WORKSPACE_PATH_BYTES)
|
|
78
|
+
return null;
|
|
79
|
+
if (isAbsolute(value) || value.includes("\\") || containsControl(value))
|
|
80
|
+
return null;
|
|
81
|
+
const parts = value.split("/");
|
|
82
|
+
if (parts.some((part) => part.length === 0 || part === "." || part === ".."))
|
|
83
|
+
return null;
|
|
84
|
+
return value;
|
|
85
|
+
}
|
|
86
|
+
function decodeCanonicalBase64(value, remaining) {
|
|
87
|
+
if (typeof value !== "string" || value.length > MAX_BASE64_LENGTH || value.length % 4 !== 0) {
|
|
88
|
+
return { ok: false, aggregate: false };
|
|
89
|
+
}
|
|
90
|
+
let padding = 0;
|
|
91
|
+
if (value.endsWith("=="))
|
|
92
|
+
padding = 2;
|
|
93
|
+
else if (value.endsWith("="))
|
|
94
|
+
padding = 1;
|
|
95
|
+
const contentLength = value.length - padding;
|
|
96
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
97
|
+
const code = value.charCodeAt(index);
|
|
98
|
+
const inAlphabet = (code >= 0x41 && code <= 0x5a) ||
|
|
99
|
+
(code >= 0x61 && code <= 0x7a) ||
|
|
100
|
+
(code >= 0x30 && code <= 0x39) ||
|
|
101
|
+
code === 0x2b || code === 0x2f;
|
|
102
|
+
if ((index < contentLength && !inAlphabet) || (index >= contentLength && code !== 0x3d)) {
|
|
103
|
+
return { ok: false, aggregate: false };
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
const decodedLength = value.length === 0 ? 0 : (value.length / 4) * 3 - padding;
|
|
107
|
+
if (decodedLength > MAX_WORKSPACE_IMAGE_BYTES)
|
|
108
|
+
return { ok: false, aggregate: false };
|
|
109
|
+
if (decodedLength > remaining)
|
|
110
|
+
return { ok: false, aggregate: true };
|
|
111
|
+
const decoded = Buffer.from(value, "base64");
|
|
112
|
+
if (decoded.byteLength !== decodedLength || decoded.toString("base64") !== value) {
|
|
113
|
+
return { ok: false, aggregate: false };
|
|
114
|
+
}
|
|
115
|
+
return { ok: true, bytes: decoded };
|
|
116
|
+
}
|
|
117
|
+
function parseOperations(value) {
|
|
118
|
+
if (!Array.isArray(value) || value.length === 0) {
|
|
119
|
+
return refuse("input-invalid", "workspace proposal must be a nonempty array of operations");
|
|
120
|
+
}
|
|
121
|
+
if (value.length > MAX_WORKSPACE_OPERATIONS) {
|
|
122
|
+
return refuse("operation-limit", `workspace proposal exceeds ${String(MAX_WORKSPACE_OPERATIONS)} operations`);
|
|
123
|
+
}
|
|
124
|
+
const parsed = [];
|
|
125
|
+
let afterBytes = 0;
|
|
126
|
+
for (const candidate of value) {
|
|
127
|
+
if (!plainObject(candidate) || typeof candidate["kind"] !== "string") {
|
|
128
|
+
return refuse("input-invalid", "each workspace operation must be one strict object");
|
|
129
|
+
}
|
|
130
|
+
const kind = candidate["kind"];
|
|
131
|
+
if (kind === "create") {
|
|
132
|
+
if (!exactKeys(candidate, ["kind", "path", "after_base64"])) {
|
|
133
|
+
return refuse("input-invalid", "create accepts only kind, path and after_base64");
|
|
134
|
+
}
|
|
135
|
+
const path = safePath(candidate["path"]);
|
|
136
|
+
if (path === null)
|
|
137
|
+
return refuse("path-invalid", "create path is not a strict relative NFC POSIX path");
|
|
138
|
+
const decoded = decodeCanonicalBase64(candidate["after_base64"], MAX_WORKSPACE_TOTAL_BYTES - afterBytes);
|
|
139
|
+
if (!decoded.ok) {
|
|
140
|
+
return refuse(decoded.aggregate ? "payload-too-large" : "input-invalid", decoded.aggregate ? "proposal after-images exceed the combined byte limit" : "create after_base64 is not canonical bounded base64", path);
|
|
141
|
+
}
|
|
142
|
+
afterBytes += decoded.bytes.byteLength;
|
|
143
|
+
parsed.push({ kind, path, after: decoded.bytes });
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
if (kind === "replace") {
|
|
147
|
+
if (!exactKeys(candidate, ["kind", "path", "expected_before_sha256", "after_base64"])) {
|
|
148
|
+
return refuse("input-invalid", "replace accepts only its four documented fields");
|
|
149
|
+
}
|
|
150
|
+
const path = safePath(candidate["path"]);
|
|
151
|
+
if (path === null)
|
|
152
|
+
return refuse("path-invalid", "replace path is not a strict relative NFC POSIX path");
|
|
153
|
+
const expected = candidate["expected_before_sha256"];
|
|
154
|
+
const decoded = decodeCanonicalBase64(candidate["after_base64"], MAX_WORKSPACE_TOTAL_BYTES - afterBytes);
|
|
155
|
+
if (!decoded.ok && decoded.aggregate) {
|
|
156
|
+
return refuse("payload-too-large", "proposal after-images exceed the combined byte limit", path);
|
|
157
|
+
}
|
|
158
|
+
if (typeof expected !== "string" || !HEX64.test(expected) || !decoded.ok) {
|
|
159
|
+
return refuse("input-invalid", "replace needs a lowercase SHA-256 preimage and canonical bounded base64", path);
|
|
160
|
+
}
|
|
161
|
+
afterBytes += decoded.bytes.byteLength;
|
|
162
|
+
parsed.push({ kind, path, expected, after: decoded.bytes });
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
if (kind === "delete") {
|
|
166
|
+
if (!exactKeys(candidate, ["kind", "path", "expected_before_sha256"])) {
|
|
167
|
+
return refuse("input-invalid", "delete accepts only kind, path and expected_before_sha256");
|
|
168
|
+
}
|
|
169
|
+
const path = safePath(candidate["path"]);
|
|
170
|
+
const expected = candidate["expected_before_sha256"];
|
|
171
|
+
if (path === null)
|
|
172
|
+
return refuse("path-invalid", "delete path is not a strict relative NFC POSIX path");
|
|
173
|
+
if (typeof expected !== "string" || !HEX64.test(expected)) {
|
|
174
|
+
return refuse("input-invalid", "delete needs a lowercase SHA-256 preimage", path);
|
|
175
|
+
}
|
|
176
|
+
parsed.push({ kind, path, expected });
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
if (kind === "move") {
|
|
180
|
+
if (!exactKeys(candidate, ["kind", "from", "to", "expected_before_sha256"])) {
|
|
181
|
+
return refuse("input-invalid", "move accepts only kind, from, to and expected_before_sha256");
|
|
182
|
+
}
|
|
183
|
+
const from = safePath(candidate["from"]);
|
|
184
|
+
const to = safePath(candidate["to"]);
|
|
185
|
+
const expected = candidate["expected_before_sha256"];
|
|
186
|
+
if (from === null || to === null)
|
|
187
|
+
return refuse("path-invalid", "move endpoints must be strict relative NFC POSIX paths");
|
|
188
|
+
if (typeof expected !== "string" || !HEX64.test(expected)) {
|
|
189
|
+
return refuse("input-invalid", "move needs a lowercase SHA-256 preimage", from);
|
|
190
|
+
}
|
|
191
|
+
parsed.push({ kind, from, to, expected });
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
return refuse("input-invalid", `unknown workspace operation kind ${JSON.stringify(kind)}`);
|
|
195
|
+
}
|
|
196
|
+
return parsed;
|
|
197
|
+
}
|
|
198
|
+
function endpoints(operation) {
|
|
199
|
+
return operation.kind === "move" ? [operation.from, operation.to] : [operation.path];
|
|
200
|
+
}
|
|
201
|
+
function conflictPath(operations) {
|
|
202
|
+
const all = operations.flatMap(endpoints).map((path) => ({ path, folded: path.toLowerCase() }));
|
|
203
|
+
for (let left = 0; left < all.length; left += 1) {
|
|
204
|
+
for (let right = left + 1; right < all.length; right += 1) {
|
|
205
|
+
const a = all[left];
|
|
206
|
+
const b = all[right];
|
|
207
|
+
if (a === undefined || b === undefined)
|
|
208
|
+
continue;
|
|
209
|
+
if (a.folded === b.folded || a.folded.startsWith(`${b.folded}/`) || b.folded.startsWith(`${a.folded}/`)) {
|
|
210
|
+
return `${a.path} conflicts or overlaps with ${b.path}`;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return null;
|
|
215
|
+
}
|
|
216
|
+
function classFor(path, policy) {
|
|
217
|
+
return protectedPathClass(path, policy.policy.protected_paths ?? []) ?? "files.write.workspace";
|
|
218
|
+
}
|
|
219
|
+
function classify(operations, policy) {
|
|
220
|
+
const result = [];
|
|
221
|
+
for (const operation of operations) {
|
|
222
|
+
const paths = endpoints(operation);
|
|
223
|
+
const classes = paths.map((path) => classFor(path, policy));
|
|
224
|
+
for (let index = 0; index < classes.length; index += 1) {
|
|
225
|
+
const cls = classes[index];
|
|
226
|
+
const path = paths[index];
|
|
227
|
+
if (cls === undefined || path === undefined)
|
|
228
|
+
continue;
|
|
229
|
+
const resolution = resolvePolicy(policy, cls, { reversible: false });
|
|
230
|
+
if (ALWAYS_HUMAN_ONLY.has(cls) || resolution.autonomy === "human-only") {
|
|
231
|
+
return refuse("class-human-only", `${path} resolves to human-only class ${cls}; no preimage was read`, path);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
if (operation.kind === "move") {
|
|
235
|
+
result.push({ ...operation, classes: [...new Set(classes)].sort() });
|
|
236
|
+
}
|
|
237
|
+
else {
|
|
238
|
+
const cls = classes[0];
|
|
239
|
+
if (cls === undefined)
|
|
240
|
+
return refuse("input-invalid", "operation has no classified endpoint");
|
|
241
|
+
result.push({ ...operation, cls });
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
return result;
|
|
245
|
+
}
|
|
246
|
+
function within(root, target) {
|
|
247
|
+
const rel = relative(root, target);
|
|
248
|
+
return rel === "" || (rel !== ".." && !rel.startsWith(`..${sep}`) && !isAbsolute(rel));
|
|
249
|
+
}
|
|
250
|
+
function identity(stats) {
|
|
251
|
+
return { dev: stats.dev.toString(), ino: stats.ino.toString() };
|
|
252
|
+
}
|
|
253
|
+
function metadata(stats) {
|
|
254
|
+
return {
|
|
255
|
+
...identity(stats),
|
|
256
|
+
mode: stats.mode.toString(),
|
|
257
|
+
uid: stats.uid.toString(),
|
|
258
|
+
gid: stats.gid.toString(),
|
|
259
|
+
nlink: stats.nlink.toString(),
|
|
260
|
+
size: stats.size.toString(),
|
|
261
|
+
mtime_ns: stats.mtimeNs.toString(),
|
|
262
|
+
ctime_ns: stats.ctimeNs.toString(),
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
function sameMetadata(left, right) {
|
|
266
|
+
return canonicalize(left) === canonicalize(right);
|
|
267
|
+
}
|
|
268
|
+
function directoryIdentity(path) {
|
|
269
|
+
let fd = null;
|
|
270
|
+
try {
|
|
271
|
+
fd = openSync(path, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK | constants.O_DIRECTORY);
|
|
272
|
+
const stats = fstatSync(fd, { bigint: true });
|
|
273
|
+
return stats.isDirectory() ? identity(stats) : null;
|
|
274
|
+
}
|
|
275
|
+
catch {
|
|
276
|
+
return null;
|
|
277
|
+
}
|
|
278
|
+
finally {
|
|
279
|
+
if (fd !== null)
|
|
280
|
+
closeSync(fd);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
function checkSpelling(parent, name) {
|
|
284
|
+
let directory = null;
|
|
285
|
+
try {
|
|
286
|
+
directory = opendirSync(parent);
|
|
287
|
+
const matches = [];
|
|
288
|
+
let count = 0;
|
|
289
|
+
for (;;) {
|
|
290
|
+
const entry = directory.readSync();
|
|
291
|
+
if (entry === null)
|
|
292
|
+
break;
|
|
293
|
+
count += 1;
|
|
294
|
+
if (count > MAX_WORKSPACE_DIRECTORY_ENTRIES)
|
|
295
|
+
return "unsafe";
|
|
296
|
+
if (entry.name.toLowerCase() === name.toLowerCase())
|
|
297
|
+
matches.push(entry.name);
|
|
298
|
+
}
|
|
299
|
+
if (matches.length === 0)
|
|
300
|
+
return "absent";
|
|
301
|
+
if (matches.length === 1 && matches[0] === name)
|
|
302
|
+
return "exact";
|
|
303
|
+
return "alias";
|
|
304
|
+
}
|
|
305
|
+
catch {
|
|
306
|
+
return "unsafe";
|
|
307
|
+
}
|
|
308
|
+
finally {
|
|
309
|
+
try {
|
|
310
|
+
directory?.closeSync();
|
|
311
|
+
}
|
|
312
|
+
catch { /* already closed or unreadable */ }
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
function inspectEndpoint(root, path, mustExist) {
|
|
316
|
+
const absolute = resolve(root, path);
|
|
317
|
+
if (!within(root, absolute) || absolute === root)
|
|
318
|
+
return refuse("path-invalid", `${path} does not name a file below the workspace`, path);
|
|
319
|
+
const parts = path.split("/");
|
|
320
|
+
let current = root;
|
|
321
|
+
const directories = [];
|
|
322
|
+
for (let index = 0; index < parts.length - 1; index += 1) {
|
|
323
|
+
const part = parts[index];
|
|
324
|
+
if (part === undefined)
|
|
325
|
+
return refuse("parent-unsafe", `${path} has an unreadable parent`, path);
|
|
326
|
+
const spelling = checkSpelling(current, part);
|
|
327
|
+
if (spelling !== "exact")
|
|
328
|
+
return refuse("parent-unsafe", `${path} has a missing, aliased, or unreadable parent`, path);
|
|
329
|
+
const next = join(current, part);
|
|
330
|
+
let stats;
|
|
331
|
+
try {
|
|
332
|
+
stats = lstatSync(next, { bigint: true });
|
|
333
|
+
}
|
|
334
|
+
catch {
|
|
335
|
+
return refuse("parent-unsafe", `${path} has an unreadable parent`, path);
|
|
336
|
+
}
|
|
337
|
+
if (!stats.isDirectory() || stats.isSymbolicLink()) {
|
|
338
|
+
return refuse("parent-unsafe", `${path} has a non-directory or symbolic-link parent`, path);
|
|
339
|
+
}
|
|
340
|
+
current = next;
|
|
341
|
+
directories.push({ path: parts.slice(0, index + 1).join("/"), ...identity(stats) });
|
|
342
|
+
}
|
|
343
|
+
const parent = directoryIdentity(dirname(absolute));
|
|
344
|
+
if (parent === null)
|
|
345
|
+
return refuse("parent-unsafe", `${path} parent could not be opened safely`, path);
|
|
346
|
+
if (parts.length > 1) {
|
|
347
|
+
const bound = directories.at(-1);
|
|
348
|
+
if (bound === undefined || bound.dev !== parent.dev || bound.ino !== parent.ino) {
|
|
349
|
+
return refuse("parent-unsafe", `${path} parent identity changed during inspection`, path);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
const leaf = parts.at(-1);
|
|
353
|
+
if (leaf === undefined)
|
|
354
|
+
return refuse("path-invalid", `${path} has no leaf`, path);
|
|
355
|
+
const spelling = checkSpelling(dirname(absolute), leaf);
|
|
356
|
+
if (spelling === "alias" || spelling === "unsafe") {
|
|
357
|
+
return refuse("path-conflict", `${path} is an aliased or unreadable directory entry`, path);
|
|
358
|
+
}
|
|
359
|
+
const exists = spelling === "exact";
|
|
360
|
+
if (mustExist && !exists)
|
|
361
|
+
return refuse("source-unsafe", `${path} does not exist`, path);
|
|
362
|
+
if (!mustExist && exists)
|
|
363
|
+
return refuse("destination-exists", `${path} already exists`, path);
|
|
364
|
+
return { absolute, directories, exists };
|
|
365
|
+
}
|
|
366
|
+
function sha256(bytes) {
|
|
367
|
+
return createHash("sha256").update(bytes).digest("hex");
|
|
368
|
+
}
|
|
369
|
+
function image(bytes) {
|
|
370
|
+
return { base64: bytes.toString("base64"), sha256: sha256(bytes), byte_length: bytes.byteLength };
|
|
371
|
+
}
|
|
372
|
+
function readFileImage(path, remainingBytes) {
|
|
373
|
+
let fd = null;
|
|
374
|
+
try {
|
|
375
|
+
fd = openSync(path, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);
|
|
376
|
+
const before = fstatSync(fd, { bigint: true });
|
|
377
|
+
if (!before.isFile() || before.nlink !== 1n) {
|
|
378
|
+
return refuse("source-unsafe", `${path} is not a regular single-link file`, path);
|
|
379
|
+
}
|
|
380
|
+
const allowed = Math.min(MAX_WORKSPACE_IMAGE_BYTES, Math.max(0, remainingBytes));
|
|
381
|
+
if (before.size > BigInt(allowed)) {
|
|
382
|
+
return refuse("payload-too-large", `${path} exceeds the remaining bounded image budget`, path);
|
|
383
|
+
}
|
|
384
|
+
const buffer = Buffer.alloc(Number(before.size) + 1);
|
|
385
|
+
let length = 0;
|
|
386
|
+
while (length < buffer.byteLength) {
|
|
387
|
+
const count = readSync(fd, buffer, length, buffer.byteLength - length, length);
|
|
388
|
+
if (count === 0)
|
|
389
|
+
break;
|
|
390
|
+
length += count;
|
|
391
|
+
}
|
|
392
|
+
if (length > MAX_WORKSPACE_IMAGE_BYTES) {
|
|
393
|
+
return refuse("payload-too-large", `${path} exceeded the image limit while it was read`, path);
|
|
394
|
+
}
|
|
395
|
+
const after = fstatSync(fd, { bigint: true });
|
|
396
|
+
const beforeMetadata = metadata(before);
|
|
397
|
+
if (BigInt(length) !== before.size || !after.isFile() || after.nlink !== 1n || !sameMetadata(beforeMetadata, metadata(after))) {
|
|
398
|
+
return refuse("source-unsafe", `${path} changed identity or metadata while it was read`, path);
|
|
399
|
+
}
|
|
400
|
+
const bytes = Buffer.from(buffer.subarray(0, length));
|
|
401
|
+
return { ...image(bytes), metadata: beforeMetadata };
|
|
402
|
+
}
|
|
403
|
+
catch {
|
|
404
|
+
return refuse("source-unsafe", `${path} could not be opened and read safely`, path);
|
|
405
|
+
}
|
|
406
|
+
finally {
|
|
407
|
+
if (fd !== null)
|
|
408
|
+
closeSync(fd);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
function deepFreeze(value) {
|
|
412
|
+
if (typeof value !== "object" || value === null || Object.isFrozen(value))
|
|
413
|
+
return value;
|
|
414
|
+
Object.freeze(value);
|
|
415
|
+
for (const member of Object.values(value))
|
|
416
|
+
deepFreeze(member);
|
|
417
|
+
return value;
|
|
418
|
+
}
|
|
419
|
+
/** Build a deterministic immutable proposal without changing the workspace. */
|
|
420
|
+
export function planWorkspaceProposal(input, context) {
|
|
421
|
+
if (!agentActor(context.actor) || !HEX64.test(context.policy_sha256)) {
|
|
422
|
+
return refuse("input-invalid", "trusted context has an invalid actor or policy digest");
|
|
423
|
+
}
|
|
424
|
+
if (!context.policy.ok)
|
|
425
|
+
return refuse("policy-unavailable", "trusted policy snapshot did not load");
|
|
426
|
+
if (!isAbsolute(context.root) || resolve(context.root) !== context.root) {
|
|
427
|
+
return refuse("root-unsafe", "workspace root must be a normalized absolute path");
|
|
428
|
+
}
|
|
429
|
+
let root;
|
|
430
|
+
try {
|
|
431
|
+
const rootStats = lstatSync(context.root, { bigint: true });
|
|
432
|
+
root = realpathSync(context.root);
|
|
433
|
+
if (!rootStats.isDirectory() || rootStats.isSymbolicLink() || root !== context.root) {
|
|
434
|
+
return refuse("root-unsafe", "workspace root must be a real non-symbolic directory");
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
catch {
|
|
438
|
+
return refuse("root-unsafe", "workspace root could not be resolved safely");
|
|
439
|
+
}
|
|
440
|
+
const rootIdentity = directoryIdentity(root);
|
|
441
|
+
if (rootIdentity === null)
|
|
442
|
+
return refuse("root-unsafe", "workspace root could not be opened safely");
|
|
443
|
+
try {
|
|
444
|
+
const rootNow = lstatSync(root, { bigint: true });
|
|
445
|
+
if (rootIdentity.dev !== rootNow.dev.toString() || rootIdentity.ino !== rootNow.ino.toString()) {
|
|
446
|
+
return refuse("root-unsafe", "workspace root identity changed during inspection");
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
catch {
|
|
450
|
+
return refuse("root-unsafe", "workspace root changed during inspection");
|
|
451
|
+
}
|
|
452
|
+
const parsed = parseOperations(input);
|
|
453
|
+
if (!Array.isArray(parsed))
|
|
454
|
+
return parsed;
|
|
455
|
+
const conflict = conflictPath(parsed);
|
|
456
|
+
if (conflict !== null)
|
|
457
|
+
return refuse("path-conflict", conflict);
|
|
458
|
+
// All authority decisions precede endpoint inspection and preimage reads.
|
|
459
|
+
const classified = classify(parsed, context.policy);
|
|
460
|
+
if (!Array.isArray(classified))
|
|
461
|
+
return classified;
|
|
462
|
+
const planned = [];
|
|
463
|
+
let totalBytes = classified.reduce((sum, operation) => sum + (operation.kind === "create" || operation.kind === "replace" ? operation.after.byteLength : 0), 0);
|
|
464
|
+
for (const operation of classified) {
|
|
465
|
+
if (operation.kind === "create") {
|
|
466
|
+
const target = inspectEndpoint(root, operation.path, false);
|
|
467
|
+
if (!("absolute" in target))
|
|
468
|
+
return target;
|
|
469
|
+
planned.push({ kind: "create", path: operation.path, class: operation.cls, directories: target.directories, after: image(operation.after) });
|
|
470
|
+
continue;
|
|
471
|
+
}
|
|
472
|
+
if (operation.kind === "move") {
|
|
473
|
+
const source = inspectEndpoint(root, operation.from, true);
|
|
474
|
+
if (!("absolute" in source))
|
|
475
|
+
return source;
|
|
476
|
+
const destination = inspectEndpoint(root, operation.to, false);
|
|
477
|
+
if (!("absolute" in destination))
|
|
478
|
+
return destination;
|
|
479
|
+
const before = readFileImage(source.absolute, Math.floor((MAX_WORKSPACE_TOTAL_BYTES - totalBytes) / 2));
|
|
480
|
+
if (!("base64" in before))
|
|
481
|
+
return before;
|
|
482
|
+
if (before.sha256 !== operation.expected)
|
|
483
|
+
return refuse("preimage-mismatch", `${operation.from} does not match expected_before_sha256`, operation.from);
|
|
484
|
+
totalBytes += before.byte_length * 2;
|
|
485
|
+
if (totalBytes > MAX_WORKSPACE_TOTAL_BYTES)
|
|
486
|
+
return refuse("payload-too-large", "combined before and after images exceed the proposal limit");
|
|
487
|
+
planned.push({
|
|
488
|
+
kind: "move", from: operation.from, to: operation.to, classes: operation.classes,
|
|
489
|
+
source_directories: source.directories, destination_directories: destination.directories,
|
|
490
|
+
before, after: { base64: before.base64, sha256: before.sha256, byte_length: before.byte_length },
|
|
491
|
+
});
|
|
492
|
+
continue;
|
|
493
|
+
}
|
|
494
|
+
const source = inspectEndpoint(root, operation.path, true);
|
|
495
|
+
if (!("absolute" in source))
|
|
496
|
+
return source;
|
|
497
|
+
const before = readFileImage(source.absolute, MAX_WORKSPACE_TOTAL_BYTES - totalBytes);
|
|
498
|
+
if (!("base64" in before))
|
|
499
|
+
return before;
|
|
500
|
+
if (before.sha256 !== operation.expected)
|
|
501
|
+
return refuse("preimage-mismatch", `${operation.path} does not match expected_before_sha256`, operation.path);
|
|
502
|
+
totalBytes += before.byte_length;
|
|
503
|
+
if (totalBytes > MAX_WORKSPACE_TOTAL_BYTES)
|
|
504
|
+
return refuse("payload-too-large", "combined before and after images exceed the proposal limit");
|
|
505
|
+
if (operation.kind === "replace") {
|
|
506
|
+
planned.push({ kind: "replace", path: operation.path, class: operation.cls, directories: source.directories, before, after: image(operation.after) });
|
|
507
|
+
}
|
|
508
|
+
else {
|
|
509
|
+
planned.push({ kind: "delete", path: operation.path, class: operation.cls, directories: source.directories, before });
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
const payload = {
|
|
513
|
+
version: WORKSPACE_PLAN_VERSION,
|
|
514
|
+
actor: context.actor,
|
|
515
|
+
root,
|
|
516
|
+
root_identity: rootIdentity,
|
|
517
|
+
policy_sha256: context.policy_sha256,
|
|
518
|
+
operations: planned,
|
|
519
|
+
};
|
|
520
|
+
const hash = payloadHash(payload);
|
|
521
|
+
const classes = [...new Set(classified.flatMap((operation) => operation.kind === "move" ? operation.classes : [operation.cls]))].sort();
|
|
522
|
+
return {
|
|
523
|
+
ok: true,
|
|
524
|
+
plan: deepFreeze({
|
|
525
|
+
payload,
|
|
526
|
+
payload_hash: hash,
|
|
527
|
+
actions: classes.map((cls) => ({ class: cls, reversible: false, payload_hash: hash })),
|
|
528
|
+
}),
|
|
529
|
+
};
|
|
530
|
+
}
|
|
531
|
+
function inputFromPlan(plan) {
|
|
532
|
+
return plan.payload.operations.map((operation) => {
|
|
533
|
+
if (operation.kind === "create")
|
|
534
|
+
return { kind: "create", path: operation.path, after_base64: operation.after.base64 };
|
|
535
|
+
if (operation.kind === "replace")
|
|
536
|
+
return {
|
|
537
|
+
kind: "replace", path: operation.path,
|
|
538
|
+
expected_before_sha256: operation.before.sha256, after_base64: operation.after.base64,
|
|
539
|
+
};
|
|
540
|
+
if (operation.kind === "delete")
|
|
541
|
+
return { kind: "delete", path: operation.path, expected_before_sha256: operation.before.sha256 };
|
|
542
|
+
return { kind: "move", from: operation.from, to: operation.to, expected_before_sha256: operation.before.sha256 };
|
|
543
|
+
});
|
|
544
|
+
}
|
|
545
|
+
/** Re-read and reclassify a plan. Success means only that the snapshot still matches. */
|
|
546
|
+
export function revalidateWorkspacePlan(plan, context) {
|
|
547
|
+
if (context.actor !== plan.payload.actor ||
|
|
548
|
+
context.root !== plan.payload.root ||
|
|
549
|
+
context.policy_sha256 !== plan.payload.policy_sha256) {
|
|
550
|
+
return refuse("context-drift", "actor, workspace root, or attested policy digest changed");
|
|
551
|
+
}
|
|
552
|
+
const current = planWorkspaceProposal(inputFromPlan(plan), context);
|
|
553
|
+
if (!current.ok) {
|
|
554
|
+
return refuse("workspace-drift", `workspace snapshot no longer validates: ${current.code}: ${current.message}`, current.path);
|
|
555
|
+
}
|
|
556
|
+
if (current.plan.payload_hash !== plan.payload_hash || canonicalize(current.plan) !== canonicalize(plan)) {
|
|
557
|
+
return refuse("workspace-drift", "workspace identities, bytes, classes, or policy binding changed");
|
|
558
|
+
}
|
|
559
|
+
return current;
|
|
560
|
+
}
|
|
561
|
+
//# sourceMappingURL=workspace-plan.js.map
|