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.
Files changed (722) hide show
  1. package/LICENSE +176 -0
  2. package/NOTICE +5 -0
  3. package/README.md +940 -4
  4. package/SPEC.md +476 -34
  5. package/cli.js +29 -3
  6. package/dist/src/adapters/agentmail.d.ts +426 -0
  7. package/dist/src/adapters/agentmail.js +1200 -0
  8. package/dist/src/adapters/agentmail.js.map +1 -0
  9. package/dist/src/adapters/conformance.d.ts +149 -0
  10. package/dist/src/adapters/conformance.js +461 -0
  11. package/dist/src/adapters/conformance.js.map +1 -0
  12. package/dist/src/adapters/contract.d.ts +628 -0
  13. package/dist/src/adapters/contract.js +1035 -0
  14. package/dist/src/adapters/contract.js.map +1 -0
  15. package/dist/src/adapters/email.d.ts +324 -0
  16. package/dist/src/adapters/email.js +749 -0
  17. package/dist/src/adapters/email.js.map +1 -0
  18. package/dist/src/adapters/env-passphrase.d.ts +93 -0
  19. package/dist/src/adapters/env-passphrase.js +132 -0
  20. package/dist/src/adapters/env-passphrase.js.map +1 -0
  21. package/dist/src/adapters/public.d.ts +11 -0
  22. package/dist/src/adapters/public.js +11 -0
  23. package/dist/src/adapters/public.js.map +1 -0
  24. package/dist/src/adapters/registry.d.ts +59 -0
  25. package/dist/src/adapters/registry.js +77 -0
  26. package/dist/src/adapters/registry.js.map +1 -0
  27. package/dist/src/adapters/smtp.d.ts +213 -0
  28. package/dist/src/adapters/smtp.js +499 -0
  29. package/dist/src/adapters/smtp.js.map +1 -0
  30. package/dist/src/adapters/vault-provider.d.ts +114 -0
  31. package/dist/src/adapters/vault-provider.js +161 -0
  32. package/dist/src/adapters/vault-provider.js.map +1 -0
  33. package/dist/src/adapters/zzz.d.ts +66 -0
  34. package/dist/src/adapters/zzz.js +299 -0
  35. package/dist/src/adapters/zzz.js.map +1 -0
  36. package/dist/src/channels/batch.d.ts +109 -0
  37. package/dist/src/channels/batch.js +121 -0
  38. package/dist/src/channels/batch.js.map +1 -0
  39. package/dist/src/channels/cli.d.ts +193 -0
  40. package/dist/src/channels/cli.js +468 -0
  41. package/dist/src/channels/cli.js.map +1 -0
  42. package/dist/src/channels/conformance.d.ts +92 -0
  43. package/dist/src/channels/conformance.js +445 -0
  44. package/dist/src/channels/conformance.js.map +1 -0
  45. package/dist/src/channels/contract.d.ts +623 -0
  46. package/dist/src/channels/contract.js +494 -0
  47. package/dist/src/channels/contract.js.map +1 -0
  48. package/dist/src/channels/payload-view.d.ts +35 -0
  49. package/dist/src/channels/payload-view.js +43 -0
  50. package/dist/src/channels/payload-view.js.map +1 -0
  51. package/dist/src/channels/render-queue.d.ts +149 -0
  52. package/dist/src/channels/render-queue.js +564 -0
  53. package/dist/src/channels/render-queue.js.map +1 -0
  54. package/dist/src/channels/tagging.d.ts +196 -0
  55. package/dist/src/channels/tagging.js +723 -0
  56. package/dist/src/channels/tagging.js.map +1 -0
  57. package/dist/src/channels/telegram.d.ts +1832 -0
  58. package/dist/src/channels/telegram.js +3190 -0
  59. package/dist/src/channels/telegram.js.map +1 -0
  60. package/dist/src/channels/web.d.ts +341 -0
  61. package/dist/src/channels/web.js +903 -0
  62. package/dist/src/channels/web.js.map +1 -0
  63. package/dist/src/cli/adapter.d.ts +90 -0
  64. package/dist/src/cli/adapter.js +288 -0
  65. package/dist/src/cli/adapter.js.map +1 -0
  66. package/dist/src/cli/amend.d.ts +59 -0
  67. package/dist/src/cli/amend.js +2171 -0
  68. package/dist/src/cli/amend.js.map +1 -0
  69. package/dist/src/cli/args.d.ts +43 -0
  70. package/dist/src/cli/args.js +86 -0
  71. package/dist/src/cli/args.js.map +1 -0
  72. package/dist/src/cli/attest.d.ts +41 -0
  73. package/dist/src/cli/attest.js +307 -0
  74. package/dist/src/cli/attest.js.map +1 -0
  75. package/dist/src/cli/audit-card.d.ts +62 -0
  76. package/dist/src/cli/audit-card.js +201 -0
  77. package/dist/src/cli/audit-card.js.map +1 -0
  78. package/dist/src/cli/audit.d.ts +59 -0
  79. package/dist/src/cli/audit.js +460 -0
  80. package/dist/src/cli/audit.js.map +1 -0
  81. package/dist/src/cli/channel-telegram.d.ts +806 -0
  82. package/dist/src/cli/channel-telegram.js +2063 -0
  83. package/dist/src/cli/channel-telegram.js.map +1 -0
  84. package/dist/src/cli/channel-web.d.ts +131 -0
  85. package/dist/src/cli/channel-web.js +357 -0
  86. package/dist/src/cli/channel-web.js.map +1 -0
  87. package/dist/src/cli/channel.d.ts +71 -0
  88. package/dist/src/cli/channel.js +438 -0
  89. package/dist/src/cli/channel.js.map +1 -0
  90. package/dist/src/cli/checkpoint-tap.d.ts +169 -0
  91. package/dist/src/cli/checkpoint-tap.js +238 -0
  92. package/dist/src/cli/checkpoint-tap.js.map +1 -0
  93. package/dist/src/cli/codex.d.ts +2 -0
  94. package/dist/src/cli/codex.js +172 -0
  95. package/dist/src/cli/codex.js.map +1 -0
  96. package/dist/src/cli/coverage.d.ts +61 -0
  97. package/dist/src/cli/coverage.js +343 -0
  98. package/dist/src/cli/coverage.js.map +1 -0
  99. package/dist/src/cli/daemon.d.ts +120 -0
  100. package/dist/src/cli/daemon.js +631 -0
  101. package/dist/src/cli/daemon.js.map +1 -0
  102. package/dist/src/cli/doctor.d.ts +129 -0
  103. package/dist/src/cli/doctor.js +2762 -0
  104. package/dist/src/cli/doctor.js.map +1 -0
  105. package/dist/src/cli/env.d.ts +65 -0
  106. package/dist/src/cli/env.js +302 -0
  107. package/dist/src/cli/env.js.map +1 -0
  108. package/dist/src/cli/execute.d.ts +202 -0
  109. package/dist/src/cli/execute.js +1682 -0
  110. package/dist/src/cli/execute.js.map +1 -0
  111. package/dist/src/cli/exit-codes.d.ts +73 -0
  112. package/dist/src/cli/exit-codes.js +82 -0
  113. package/dist/src/cli/exit-codes.js.map +1 -0
  114. package/dist/src/cli/feedback.d.ts +60 -0
  115. package/dist/src/cli/feedback.js +205 -0
  116. package/dist/src/cli/feedback.js.map +1 -0
  117. package/dist/src/cli/gate-window.d.ts +40 -0
  118. package/dist/src/cli/gate-window.js +294 -0
  119. package/dist/src/cli/gate-window.js.map +1 -0
  120. package/dist/src/cli/gate.d.ts +68 -0
  121. package/dist/src/cli/gate.js +557 -0
  122. package/dist/src/cli/gate.js.map +1 -0
  123. package/dist/src/cli/git-scope.d.ts +190 -0
  124. package/dist/src/cli/git-scope.js +295 -0
  125. package/dist/src/cli/git-scope.js.map +1 -0
  126. package/dist/src/cli/gloss-attach.d.ts +85 -0
  127. package/dist/src/cli/gloss-attach.js +107 -0
  128. package/dist/src/cli/gloss-attach.js.map +1 -0
  129. package/dist/src/cli/gloss-codex-child.d.ts +9 -0
  130. package/dist/src/cli/gloss-codex-child.js +149 -0
  131. package/dist/src/cli/gloss-codex-child.js.map +1 -0
  132. package/dist/src/cli/gloss-codex.d.ts +24 -0
  133. package/dist/src/cli/gloss-codex.js +255 -0
  134. package/dist/src/cli/gloss-codex.js.map +1 -0
  135. package/dist/src/cli/gloss-options.d.ts +42 -0
  136. package/dist/src/cli/gloss-options.js +79 -0
  137. package/dist/src/cli/gloss-options.js.map +1 -0
  138. package/dist/src/cli/gloss.d.ts +265 -0
  139. package/dist/src/cli/gloss.js +362 -0
  140. package/dist/src/cli/gloss.js.map +1 -0
  141. package/dist/src/cli/help.d.ts +103 -0
  142. package/dist/src/cli/help.js +2339 -0
  143. package/dist/src/cli/help.js.map +1 -0
  144. package/dist/src/cli/hook-codex.d.ts +78 -0
  145. package/dist/src/cli/hook-codex.js +167 -0
  146. package/dist/src/cli/hook-codex.js.map +1 -0
  147. package/dist/src/cli/hook.d.ts +331 -0
  148. package/dist/src/cli/hook.js +2849 -0
  149. package/dist/src/cli/hook.js.map +1 -0
  150. package/dist/src/cli/import.d.ts +35 -0
  151. package/dist/src/cli/import.js +175 -0
  152. package/dist/src/cli/import.js.map +1 -0
  153. package/dist/src/cli/init.d.ts +84 -0
  154. package/dist/src/cli/init.js +336 -0
  155. package/dist/src/cli/init.js.map +1 -0
  156. package/dist/src/cli/instructions.d.ts +23 -0
  157. package/dist/src/cli/instructions.js +262 -0
  158. package/dist/src/cli/instructions.js.map +1 -0
  159. package/dist/src/cli/journal.d.ts +41 -0
  160. package/dist/src/cli/journal.js +238 -0
  161. package/dist/src/cli/journal.js.map +1 -0
  162. package/dist/src/cli/log-advance.d.ts +287 -0
  163. package/dist/src/cli/log-advance.js +840 -0
  164. package/dist/src/cli/log-advance.js.map +1 -0
  165. package/dist/src/cli/log-anchor.d.ts +176 -0
  166. package/dist/src/cli/log-anchor.js +387 -0
  167. package/dist/src/cli/log-anchor.js.map +1 -0
  168. package/dist/src/cli/log-checkpoint.d.ts +22 -0
  169. package/dist/src/cli/log-checkpoint.js +128 -0
  170. package/dist/src/cli/log-checkpoint.js.map +1 -0
  171. package/dist/src/cli/log-sync.d.ts +243 -0
  172. package/dist/src/cli/log-sync.js +849 -0
  173. package/dist/src/cli/log-sync.js.map +1 -0
  174. package/dist/src/cli/log-verbs.d.ts +16 -0
  175. package/dist/src/cli/log-verbs.js +360 -0
  176. package/dist/src/cli/log-verbs.js.map +1 -0
  177. package/dist/src/cli/long-help.d.ts +70 -0
  178. package/dist/src/cli/long-help.js +148 -0
  179. package/dist/src/cli/long-help.js.map +1 -0
  180. package/dist/src/cli/main.d.ts +77 -0
  181. package/dist/src/cli/main.js +1206 -0
  182. package/dist/src/cli/main.js.map +1 -0
  183. package/dist/src/cli/mcp.d.ts +52 -0
  184. package/dist/src/cli/mcp.js +306 -0
  185. package/dist/src/cli/mcp.js.map +1 -0
  186. package/dist/src/cli/paths.d.ts +56 -0
  187. package/dist/src/cli/paths.js +79 -0
  188. package/dist/src/cli/paths.js.map +1 -0
  189. package/dist/src/cli/payload.d.ts +58 -0
  190. package/dist/src/cli/payload.js +253 -0
  191. package/dist/src/cli/payload.js.map +1 -0
  192. package/dist/src/cli/policy.d.ts +43 -0
  193. package/dist/src/cli/policy.js +229 -0
  194. package/dist/src/cli/policy.js.map +1 -0
  195. package/dist/src/cli/preflight.d.ts +363 -0
  196. package/dist/src/cli/preflight.js +1175 -0
  197. package/dist/src/cli/preflight.js.map +1 -0
  198. package/dist/src/cli/progress.d.ts +78 -0
  199. package/dist/src/cli/progress.js +112 -0
  200. package/dist/src/cli/progress.js.map +1 -0
  201. package/dist/src/cli/prompt.d.ts +209 -0
  202. package/dist/src/cli/prompt.js +312 -0
  203. package/dist/src/cli/prompt.js.map +1 -0
  204. package/dist/src/cli/quickstart.d.ts +46 -0
  205. package/dist/src/cli/quickstart.js +297 -0
  206. package/dist/src/cli/quickstart.js.map +1 -0
  207. package/dist/src/cli/records.d.ts +34 -0
  208. package/dist/src/cli/records.js +66 -0
  209. package/dist/src/cli/records.js.map +1 -0
  210. package/dist/src/cli/render.d.ts +22 -0
  211. package/dist/src/cli/render.js +132 -0
  212. package/dist/src/cli/render.js.map +1 -0
  213. package/dist/src/cli/sandbox.d.ts +51 -0
  214. package/dist/src/cli/sandbox.js +150 -0
  215. package/dist/src/cli/sandbox.js.map +1 -0
  216. package/dist/src/cli/scaffold.d.ts +79 -0
  217. package/dist/src/cli/scaffold.js +137 -0
  218. package/dist/src/cli/scaffold.js.map +1 -0
  219. package/dist/src/cli/setup-adapter.d.ts +137 -0
  220. package/dist/src/cli/setup-adapter.js +509 -0
  221. package/dist/src/cli/setup-adapter.js.map +1 -0
  222. package/dist/src/cli/setup-channel.d.ts +117 -0
  223. package/dist/src/cli/setup-channel.js +635 -0
  224. package/dist/src/cli/setup-channel.js.map +1 -0
  225. package/dist/src/cli/setup-checkpoint.d.ts +57 -0
  226. package/dist/src/cli/setup-checkpoint.js +196 -0
  227. package/dist/src/cli/setup-checkpoint.js.map +1 -0
  228. package/dist/src/cli/setup-common.d.ts +275 -0
  229. package/dist/src/cli/setup-common.js +376 -0
  230. package/dist/src/cli/setup-common.js.map +1 -0
  231. package/dist/src/cli/setup-flow.d.ts +287 -0
  232. package/dist/src/cli/setup-flow.js +476 -0
  233. package/dist/src/cli/setup-flow.js.map +1 -0
  234. package/dist/src/cli/setup-service.d.ts +96 -0
  235. package/dist/src/cli/setup-service.js +308 -0
  236. package/dist/src/cli/setup-service.js.map +1 -0
  237. package/dist/src/cli/setup.d.ts +202 -0
  238. package/dist/src/cli/setup.js +473 -0
  239. package/dist/src/cli/setup.js.map +1 -0
  240. package/dist/src/cli/style.d.ts +320 -0
  241. package/dist/src/cli/style.js +469 -0
  242. package/dist/src/cli/style.js.map +1 -0
  243. package/dist/src/cli/token.d.ts +39 -0
  244. package/dist/src/cli/token.js +274 -0
  245. package/dist/src/cli/token.js.map +1 -0
  246. package/dist/src/cli/up.d.ts +155 -0
  247. package/dist/src/cli/up.js +849 -0
  248. package/dist/src/cli/up.js.map +1 -0
  249. package/dist/src/cli/usage.d.ts +37 -0
  250. package/dist/src/cli/usage.js +91 -0
  251. package/dist/src/cli/usage.js.map +1 -0
  252. package/dist/src/cli/values.d.ts +40 -0
  253. package/dist/src/cli/values.js +189 -0
  254. package/dist/src/cli/values.js.map +1 -0
  255. package/dist/src/cli/vault.d.ts +59 -0
  256. package/dist/src/cli/vault.js +362 -0
  257. package/dist/src/cli/vault.js.map +1 -0
  258. package/dist/src/cli/verb-registry.d.ts +76 -0
  259. package/dist/src/cli/verb-registry.js +2341 -0
  260. package/dist/src/cli/verb-registry.js.map +1 -0
  261. package/dist/src/cli/wordmark.d.ts +31 -0
  262. package/dist/src/cli/wordmark.js +52 -0
  263. package/dist/src/cli/wordmark.js.map +1 -0
  264. package/dist/src/codex/doctor.d.ts +13 -0
  265. package/dist/src/codex/doctor.js +41 -0
  266. package/dist/src/codex/doctor.js.map +1 -0
  267. package/dist/src/codex/manifest.d.ts +49 -0
  268. package/dist/src/codex/manifest.js +103 -0
  269. package/dist/src/codex/manifest.js.map +1 -0
  270. package/dist/src/codex/templates.d.ts +41 -0
  271. package/dist/src/codex/templates.js +319 -0
  272. package/dist/src/codex/templates.js.map +1 -0
  273. package/dist/src/codex/trust.d.ts +19 -0
  274. package/dist/src/codex/trust.js +183 -0
  275. package/dist/src/codex/trust.js.map +1 -0
  276. package/dist/src/codex/workspace-plan.d.ts +131 -0
  277. package/dist/src/codex/workspace-plan.js +561 -0
  278. package/dist/src/codex/workspace-plan.js.map +1 -0
  279. package/dist/src/core/actor.d.ts +2 -0
  280. package/dist/src/core/actor.js +5 -0
  281. package/dist/src/core/actor.js.map +1 -0
  282. package/dist/src/core/advance-cycle.d.ts +170 -0
  283. package/dist/src/core/advance-cycle.js +200 -0
  284. package/dist/src/core/advance-cycle.js.map +1 -0
  285. package/dist/src/core/agents-md.d.ts +276 -0
  286. package/dist/src/core/agents-md.js +747 -0
  287. package/dist/src/core/agents-md.js.map +1 -0
  288. package/dist/src/core/apply-patch.d.ts +49 -0
  289. package/dist/src/core/apply-patch.js +266 -0
  290. package/dist/src/core/apply-patch.js.map +1 -0
  291. package/dist/src/core/attest.d.ts +420 -0
  292. package/dist/src/core/attest.js +589 -0
  293. package/dist/src/core/attest.js.map +1 -0
  294. package/dist/src/core/audit.d.ts +492 -0
  295. package/dist/src/core/audit.js +882 -0
  296. package/dist/src/core/audit.js.map +1 -0
  297. package/dist/src/core/budgets.d.ts +238 -0
  298. package/dist/src/core/budgets.js +449 -0
  299. package/dist/src/core/budgets.js.map +1 -0
  300. package/dist/src/core/checkpoint.d.ts +500 -0
  301. package/dist/src/core/checkpoint.js +738 -0
  302. package/dist/src/core/checkpoint.js.map +1 -0
  303. package/dist/src/core/child-env.d.ts +88 -0
  304. package/dist/src/core/child-env.js +86 -0
  305. package/dist/src/core/child-env.js.map +1 -0
  306. package/dist/src/core/clock.d.ts +52 -0
  307. package/dist/src/core/clock.js +43 -0
  308. package/dist/src/core/clock.js.map +1 -0
  309. package/dist/src/core/command-class.d.ts +543 -0
  310. package/dist/src/core/command-class.js +2356 -0
  311. package/dist/src/core/command-class.js.map +1 -0
  312. package/dist/src/core/coverage-sources/adapter.d.ts +40 -0
  313. package/dist/src/core/coverage-sources/adapter.js +71 -0
  314. package/dist/src/core/coverage-sources/adapter.js.map +1 -0
  315. package/dist/src/core/coverage-sources/gh.d.ts +48 -0
  316. package/dist/src/core/coverage-sources/gh.js +136 -0
  317. package/dist/src/core/coverage-sources/gh.js.map +1 -0
  318. package/dist/src/core/coverage-sources/git.d.ts +101 -0
  319. package/dist/src/core/coverage-sources/git.js +269 -0
  320. package/dist/src/core/coverage-sources/git.js.map +1 -0
  321. package/dist/src/core/coverage.d.ts +217 -0
  322. package/dist/src/core/coverage.js +337 -0
  323. package/dist/src/core/coverage.js.map +1 -0
  324. package/dist/src/core/credential-spec.d.ts +72 -0
  325. package/dist/src/core/credential-spec.js +23 -0
  326. package/dist/src/core/credential-spec.js.map +1 -0
  327. package/dist/src/core/dark-session.d.ts +331 -0
  328. package/dist/src/core/dark-session.js +714 -0
  329. package/dist/src/core/dark-session.js.map +1 -0
  330. package/dist/src/core/decision-refusal.d.ts +185 -0
  331. package/dist/src/core/decision-refusal.js +265 -0
  332. package/dist/src/core/decision-refusal.js.map +1 -0
  333. package/dist/src/core/env-file.d.ts +450 -0
  334. package/dist/src/core/env-file.js +837 -0
  335. package/dist/src/core/env-file.js.map +1 -0
  336. package/dist/src/core/execute.d.ts +858 -0
  337. package/dist/src/core/execute.js +1271 -0
  338. package/dist/src/core/execute.js.map +1 -0
  339. package/dist/src/core/frontmatter.d.ts +78 -0
  340. package/dist/src/core/frontmatter.js +100 -0
  341. package/dist/src/core/frontmatter.js.map +1 -0
  342. package/dist/src/core/gate-window.d.ts +312 -0
  343. package/dist/src/core/gate-window.js +506 -0
  344. package/dist/src/core/gate-window.js.map +1 -0
  345. package/dist/src/core/gate.d.ts +1364 -0
  346. package/dist/src/core/gate.js +3002 -0
  347. package/dist/src/core/gate.js.map +1 -0
  348. package/dist/src/core/git-run.d.ts +73 -0
  349. package/dist/src/core/git-run.js +93 -0
  350. package/dist/src/core/git-run.js.map +1 -0
  351. package/dist/src/core/harness-version.d.ts +157 -0
  352. package/dist/src/core/harness-version.js +211 -0
  353. package/dist/src/core/harness-version.js.map +1 -0
  354. package/dist/src/core/harness-wait.d.ts +55 -0
  355. package/dist/src/core/harness-wait.js +58 -0
  356. package/dist/src/core/harness-wait.js.map +1 -0
  357. package/dist/src/core/head-retry.d.ts +107 -0
  358. package/dist/src/core/head-retry.js +121 -0
  359. package/dist/src/core/head-retry.js.map +1 -0
  360. package/dist/src/core/instance.d.ts +253 -0
  361. package/dist/src/core/instance.js +319 -0
  362. package/dist/src/core/instance.js.map +1 -0
  363. package/dist/src/core/intake-limits.d.ts +247 -0
  364. package/dist/src/core/intake-limits.js +350 -0
  365. package/dist/src/core/intake-limits.js.map +1 -0
  366. package/dist/src/core/jcs.d.ts +52 -0
  367. package/dist/src/core/jcs.js +132 -0
  368. package/dist/src/core/jcs.js.map +1 -0
  369. package/dist/src/core/journal.d.ts +144 -0
  370. package/dist/src/core/journal.js +200 -0
  371. package/dist/src/core/journal.js.map +1 -0
  372. package/dist/src/core/live-draw.d.ts +436 -0
  373. package/dist/src/core/live-draw.js +703 -0
  374. package/dist/src/core/live-draw.js.map +1 -0
  375. package/dist/src/core/log-reconcile.d.ts +89 -0
  376. package/dist/src/core/log-reconcile.js +136 -0
  377. package/dist/src/core/log-reconcile.js.map +1 -0
  378. package/dist/src/core/log-subscribe.d.ts +36 -0
  379. package/dist/src/core/log-subscribe.js +162 -0
  380. package/dist/src/core/log-subscribe.js.map +1 -0
  381. package/dist/src/core/log.d.ts +278 -0
  382. package/dist/src/core/log.js +546 -0
  383. package/dist/src/core/log.js.map +1 -0
  384. package/dist/src/core/loop.d.ts +274 -0
  385. package/dist/src/core/loop.js +487 -0
  386. package/dist/src/core/loop.js.map +1 -0
  387. package/dist/src/core/md-fence.d.ts +41 -0
  388. package/dist/src/core/md-fence.js +74 -0
  389. package/dist/src/core/md-fence.js.map +1 -0
  390. package/dist/src/core/money.d.ts +147 -0
  391. package/dist/src/core/money.js +195 -0
  392. package/dist/src/core/money.js.map +1 -0
  393. package/dist/src/core/payload-census.d.ts +74 -0
  394. package/dist/src/core/payload-census.js +146 -0
  395. package/dist/src/core/payload-census.js.map +1 -0
  396. package/dist/src/core/payload-store.d.ts +175 -0
  397. package/dist/src/core/payload-store.js +340 -0
  398. package/dist/src/core/payload-store.js.map +1 -0
  399. package/dist/src/core/payload.d.ts +71 -0
  400. package/dist/src/core/payload.js +80 -0
  401. package/dist/src/core/payload.js.map +1 -0
  402. package/dist/src/core/policy-diff.d.ts +292 -0
  403. package/dist/src/core/policy-diff.js +588 -0
  404. package/dist/src/core/policy-diff.js.map +1 -0
  405. package/dist/src/core/policy-expectations.d.ts +199 -0
  406. package/dist/src/core/policy-expectations.js +394 -0
  407. package/dist/src/core/policy-expectations.js.map +1 -0
  408. package/dist/src/core/policy-explain.d.ts +150 -0
  409. package/dist/src/core/policy-explain.js +258 -0
  410. package/dist/src/core/policy-explain.js.map +1 -0
  411. package/dist/src/core/policy-load.d.ts +527 -0
  412. package/dist/src/core/policy-load.js +536 -0
  413. package/dist/src/core/policy-load.js.map +1 -0
  414. package/dist/src/core/policy-match.d.ts +281 -0
  415. package/dist/src/core/policy-match.js +478 -0
  416. package/dist/src/core/policy-match.js.map +1 -0
  417. package/dist/src/core/policy-proposal.d.ts +265 -0
  418. package/dist/src/core/policy-proposal.js +458 -0
  419. package/dist/src/core/policy-proposal.js.map +1 -0
  420. package/dist/src/core/prompt-layout.d.ts +221 -0
  421. package/dist/src/core/prompt-layout.js +422 -0
  422. package/dist/src/core/prompt-layout.js.map +1 -0
  423. package/dist/src/core/protected-path-guard.d.ts +453 -0
  424. package/dist/src/core/protected-path-guard.js +1566 -0
  425. package/dist/src/core/protected-path-guard.js.map +1 -0
  426. package/dist/src/core/registration.d.ts +25 -0
  427. package/dist/src/core/registration.js +39 -0
  428. package/dist/src/core/registration.js.map +1 -0
  429. package/dist/src/core/reindex.d.ts +99 -0
  430. package/dist/src/core/reindex.js +336 -0
  431. package/dist/src/core/reindex.js.map +1 -0
  432. package/dist/src/core/sampler.d.ts +313 -0
  433. package/dist/src/core/sampler.js +388 -0
  434. package/dist/src/core/sampler.js.map +1 -0
  435. package/dist/src/core/sandbox.d.ts +290 -0
  436. package/dist/src/core/sandbox.js +424 -0
  437. package/dist/src/core/sandbox.js.map +1 -0
  438. package/dist/src/core/seal.d.ts +165 -0
  439. package/dist/src/core/seal.js +290 -0
  440. package/dist/src/core/seal.js.map +1 -0
  441. package/dist/src/core/state.d.ts +505 -0
  442. package/dist/src/core/state.js +1009 -0
  443. package/dist/src/core/state.js.map +1 -0
  444. package/dist/src/core/task-file.d.ts +185 -0
  445. package/dist/src/core/task-file.js +464 -0
  446. package/dist/src/core/task-file.js.map +1 -0
  447. package/dist/src/core/telegram-config.d.ts +93 -0
  448. package/dist/src/core/telegram-config.js +114 -0
  449. package/dist/src/core/telegram-config.js.map +1 -0
  450. package/dist/src/core/token.d.ts +409 -0
  451. package/dist/src/core/token.js +561 -0
  452. package/dist/src/core/token.js.map +1 -0
  453. package/dist/src/core/validate.d.ts +138 -0
  454. package/dist/src/core/validate.js +0 -0
  455. package/dist/src/core/validate.js.map +1 -0
  456. package/dist/src/core/values.d.ts +137 -0
  457. package/dist/src/core/values.js +153 -0
  458. package/dist/src/core/values.js.map +1 -0
  459. package/dist/src/core/vault.d.ts +291 -0
  460. package/dist/src/core/vault.js +612 -0
  461. package/dist/src/core/vault.js.map +1 -0
  462. package/dist/src/core/verified-snapshot.d.ts +204 -0
  463. package/dist/src/core/verified-snapshot.js +506 -0
  464. package/dist/src/core/verified-snapshot.js.map +1 -0
  465. package/dist/src/core/verify.d.ts +336 -0
  466. package/dist/src/core/verify.js +549 -0
  467. package/dist/src/core/verify.js.map +1 -0
  468. package/dist/src/core/version.d.ts +8 -0
  469. package/dist/src/core/version.js +9 -0
  470. package/dist/src/core/version.js.map +1 -0
  471. package/dist/src/core/wysiwys.d.ts +370 -0
  472. package/dist/src/core/wysiwys.js +728 -0
  473. package/dist/src/core/wysiwys.js.map +1 -0
  474. package/dist/src/daemon/advance-child.d.ts +39 -0
  475. package/dist/src/daemon/advance-child.js +78 -0
  476. package/dist/src/daemon/advance-child.js.map +1 -0
  477. package/dist/src/daemon/advance.d.ts +466 -0
  478. package/dist/src/daemon/advance.js +849 -0
  479. package/dist/src/daemon/advance.js.map +1 -0
  480. package/dist/src/daemon/audit.d.ts +87 -0
  481. package/dist/src/daemon/audit.js +90 -0
  482. package/dist/src/daemon/audit.js.map +1 -0
  483. package/dist/src/daemon/daemon.d.ts +1180 -0
  484. package/dist/src/daemon/daemon.js +1988 -0
  485. package/dist/src/daemon/daemon.js.map +1 -0
  486. package/dist/src/daemon/dark-session.d.ts +64 -0
  487. package/dist/src/daemon/dark-session.js +119 -0
  488. package/dist/src/daemon/dark-session.js.map +1 -0
  489. package/dist/src/daemon/draw-child.d.ts +36 -0
  490. package/dist/src/daemon/draw-child.js +132 -0
  491. package/dist/src/daemon/draw-child.js.map +1 -0
  492. package/dist/src/daemon/draw.d.ts +154 -0
  493. package/dist/src/daemon/draw.js +458 -0
  494. package/dist/src/daemon/draw.js.map +1 -0
  495. package/dist/src/daemon/git-evidence.d.ts +173 -0
  496. package/dist/src/daemon/git-evidence.js +345 -0
  497. package/dist/src/daemon/git-evidence.js.map +1 -0
  498. package/dist/src/daemon/projection.d.ts +180 -0
  499. package/dist/src/daemon/projection.js +233 -0
  500. package/dist/src/daemon/projection.js.map +1 -0
  501. package/dist/src/daemon/prune.d.ts +207 -0
  502. package/dist/src/daemon/prune.js +376 -0
  503. package/dist/src/daemon/prune.js.map +1 -0
  504. package/dist/src/mcp/http.d.ts +113 -0
  505. package/dist/src/mcp/http.js +343 -0
  506. package/dist/src/mcp/http.js.map +1 -0
  507. package/dist/src/mcp/server.d.ts +265 -0
  508. package/dist/src/mcp/server.js +602 -0
  509. package/dist/src/mcp/server.js.map +1 -0
  510. package/docs/adapter-api.md +106 -0
  511. package/docs/cli-reference.md +5716 -0
  512. package/docs/codex-enforced-session.md +30 -0
  513. package/package.json +53 -4
  514. package/schema/.gitkeep +0 -0
  515. package/schema/LICENSE +117 -0
  516. package/schema/codex-instance.schema.json +82 -0
  517. package/schema/envelope.schema.json +137 -0
  518. package/schema/event.schema.json +1811 -0
  519. package/schema/fixtures/codex-instance/invalid/unpinned-codex-version.json +40 -0
  520. package/schema/fixtures/codex-instance/valid/canonical.json +40 -0
  521. package/schema/fixtures/envelope/invalid/action-missing-idempotency-key.json +15 -0
  522. package/schema/fixtures/envelope/invalid/action-unknown-class-format.json +14 -0
  523. package/schema/fixtures/envelope/invalid/confidence-out-of-range.json +11 -0
  524. package/schema/fixtures/envelope/invalid/est-cost-bare-number.json +14 -0
  525. package/schema/fixtures/envelope/invalid/est-cost-noncanonical-string.json +14 -0
  526. package/schema/fixtures/envelope/invalid/malformed-assignee.json +10 -0
  527. package/schema/fixtures/envelope/invalid/malformed-created-by.json +7 -0
  528. package/schema/fixtures/envelope/invalid/malformed-max-latency.json +11 -0
  529. package/schema/fixtures/envelope/invalid/malformed-payload-hash.json +14 -0
  530. package/schema/fixtures/envelope/invalid/max-cost-bare-number.json +11 -0
  531. package/schema/fixtures/envelope/invalid/missing-origin.json +3 -0
  532. package/schema/fixtures/envelope/invalid/negative-est-cost.json +14 -0
  533. package/schema/fixtures/envelope/invalid/unknown-state.json +7 -0
  534. package/schema/fixtures/envelope/invalid/unknown-top-level-field.json +8 -0
  535. package/schema/fixtures/envelope/valid/action-payload-hash.json +17 -0
  536. package/schema/fixtures/envelope/valid/actions-without-budget.json +18 -0
  537. package/schema/fixtures/envelope/valid/canonical.json +25 -0
  538. package/schema/fixtures/envelope/valid/minimal.json +7 -0
  539. package/schema/fixtures/envelope/valid/multi-action-executed.json +30 -0
  540. package/schema/fixtures/envelope/valid/record-write-stage.json +22 -0
  541. package/schema/fixtures/event/invalid/approval-granted-agent-actor.json +15 -0
  542. package/schema/fixtures/event/invalid/approval-granted-empty-batch-delivery-id.json +17 -0
  543. package/schema/fixtures/event/invalid/approval-granted-fifth-reaction.json +16 -0
  544. package/schema/fixtures/event/invalid/approval-granted-missing-actor.json +14 -0
  545. package/schema/fixtures/event/invalid/approval-requested-missing-action-key.json +14 -0
  546. package/schema/fixtures/event/invalid/approval-withdrawn-agent-policy-drift.json +15 -0
  547. package/schema/fixtures/event/invalid/approval-withdrawn-missing-reason.json +15 -0
  548. package/schema/fixtures/event/invalid/approval-withdrawn-system-actor.json +15 -0
  549. package/schema/fixtures/event/invalid/audit-decision-refused-human-actor.json +17 -0
  550. package/schema/fixtures/event/invalid/audit-decision-refused-missing-code.json +16 -0
  551. package/schema/fixtures/event/invalid/audit-reviewed-agent-actor.json +15 -0
  552. package/schema/fixtures/event/invalid/audit-reviewed-loved-no-note.json +16 -0
  553. package/schema/fixtures/event/invalid/audit-reviewed-system-actor.json +15 -0
  554. package/schema/fixtures/event/invalid/bad-actor-prefix.json +15 -0
  555. package/schema/fixtures/event/invalid/est-cost-bare-number.json +17 -0
  556. package/schema/fixtures/event/invalid/execution-completed-fabricated-exit-code.json +16 -0
  557. package/schema/fixtures/event/invalid/execution-completed-provider-ref-empty-id.json +18 -0
  558. package/schema/fixtures/event/invalid/execution-completed-provider-ref-extra-field.json +19 -0
  559. package/schema/fixtures/event/invalid/execution-completed-provider-ref-id-not-string.json +18 -0
  560. package/schema/fixtures/event/invalid/execution-completed-provider-ref-missing-adapter.json +17 -0
  561. package/schema/fixtures/event/invalid/execution-failed-open-reported-by.json +16 -0
  562. package/schema/fixtures/event/invalid/execution-indeterminate-open-reason.json +14 -0
  563. package/schema/fixtures/event/invalid/execution-reconciled-agent-actor.json +17 -0
  564. package/schema/fixtures/event/invalid/execution-started-negative-env-stripped.json +16 -0
  565. package/schema/fixtures/event/invalid/gate-bypassed-missing-opened-seq.json +15 -0
  566. package/schema/fixtures/event/invalid/gate-closed-non-integer-opened-seq.json +13 -0
  567. package/schema/fixtures/event/invalid/gate-opened-agent-actor.json +16 -0
  568. package/schema/fixtures/event/invalid/gate-organ-attested-absolute-path.json +14 -0
  569. package/schema/fixtures/event/invalid/gate-organ-attested-agent-actor.json +14 -0
  570. package/schema/fixtures/event/invalid/gate-organ-attested-missing-organ-path.json +13 -0
  571. package/schema/fixtures/event/invalid/harness-unknown-kind.json +18 -0
  572. package/schema/fixtures/event/invalid/harness-version-multiline.json +16 -0
  573. package/schema/fixtures/event/invalid/log-checkpoint-agent-actor.json +17 -0
  574. package/schema/fixtures/event/invalid/log-checkpoint-missing-signature.json +16 -0
  575. package/schema/fixtures/event/invalid/log-checkpoint-short-signed-hash.json +17 -0
  576. package/schema/fixtures/event/invalid/log-checkpoint-unknown-signature-alg.json +17 -0
  577. package/schema/fixtures/event/invalid/malformed-ts.json +15 -0
  578. package/schema/fixtures/event/invalid/missing-alg.json +14 -0
  579. package/schema/fixtures/event/invalid/missing-hash.json +14 -0
  580. package/schema/fixtures/event/invalid/non-integer-seq.json +15 -0
  581. package/schema/fixtures/event/invalid/payload-pruned-human-actor.json +14 -0
  582. package/schema/fixtures/event/invalid/payload-pruned-missing-hash.json +14 -0
  583. package/schema/fixtures/event/invalid/policy-declined-agent-actor.json +15 -0
  584. package/schema/fixtures/event/invalid/policy-proposed-missing-diff.json +19 -0
  585. package/schema/fixtures/event/invalid/policy-proposed-system-actor.json +26 -0
  586. package/schema/fixtures/event/invalid/short-hash.json +15 -0
  587. package/schema/fixtures/event/invalid/unknown-alg.json +15 -0
  588. package/schema/fixtures/event/invalid/unknown-event-type.json +15 -0
  589. package/schema/fixtures/event/invalid/unknown-top-level-field.json +16 -0
  590. package/schema/fixtures/event/valid/approval-expired.json +15 -0
  591. package/schema/fixtures/event/valid/approval-granted-batch.json +19 -0
  592. package/schema/fixtures/event/valid/approval-granted-reaction.json +16 -0
  593. package/schema/fixtures/event/valid/approval-granted.json +15 -0
  594. package/schema/fixtures/event/valid/approval-rejected.json +15 -0
  595. package/schema/fixtures/event/valid/approval-requested.json +19 -0
  596. package/schema/fixtures/event/valid/approval-revoked.json +15 -0
  597. package/schema/fixtures/event/valid/approval-withdrawn-policy-drift.json +17 -0
  598. package/schema/fixtures/event/valid/approval-withdrawn.json +16 -0
  599. package/schema/fixtures/event/valid/audit-decision-refused.json +20 -0
  600. package/schema/fixtures/event/valid/audit-reviewed-reaction.json +17 -0
  601. package/schema/fixtures/event/valid/audit-reviewed.json +15 -0
  602. package/schema/fixtures/event/valid/audit-sampled.json +14 -0
  603. package/schema/fixtures/event/valid/budget-exceeded.json +21 -0
  604. package/schema/fixtures/event/valid/envelope-drift.json +16 -0
  605. package/schema/fixtures/event/valid/execution-completed-harness-report.json +16 -0
  606. package/schema/fixtures/event/valid/execution-completed-provider-ref.json +18 -0
  607. package/schema/fixtures/event/valid/execution-completed.json +15 -0
  608. package/schema/fixtures/event/valid/execution-failed-harness-report.json +16 -0
  609. package/schema/fixtures/event/valid/execution-failed.json +15 -0
  610. package/schema/fixtures/event/valid/execution-indeterminate.json +15 -0
  611. package/schema/fixtures/event/valid/execution-reconciled.json +17 -0
  612. package/schema/fixtures/event/valid/execution-started-env-stripped.json +17 -0
  613. package/schema/fixtures/event/valid/execution-started.json +14 -0
  614. package/schema/fixtures/event/valid/gate-bypassed-harness-version.json +18 -0
  615. package/schema/fixtures/event/valid/gate-bypassed.json +19 -0
  616. package/schema/fixtures/event/valid/gate-closed.json +14 -0
  617. package/schema/fixtures/event/valid/gate-opened.json +16 -0
  618. package/schema/fixtures/event/valid/gate-organ-attested.json +14 -0
  619. package/schema/fixtures/event/valid/genesis-null-prev.json +14 -0
  620. package/schema/fixtures/event/valid/log-checkpoint.json +17 -0
  621. package/schema/fixtures/event/valid/payload-pruned-orphan.json +13 -0
  622. package/schema/fixtures/event/valid/payload-pruned.json +17 -0
  623. package/schema/fixtures/event/valid/policy-declined.json +16 -0
  624. package/schema/fixtures/event/valid/policy-proposed.json +35 -0
  625. package/schema/fixtures/event/valid/policy-updated.json +14 -0
  626. package/schema/fixtures/event/valid/reconciliation-required.json +18 -0
  627. package/schema/fixtures/event/valid/reconciliation-satisfied.json +17 -0
  628. package/schema/fixtures/event/valid/route-accepted.json +15 -0
  629. package/schema/fixtures/event/valid/route-proposed.json +16 -0
  630. package/schema/fixtures/event/valid/spec-example.json +15 -0
  631. package/schema/fixtures/event/valid/task-registered-harness-version.json +23 -0
  632. package/schema/fixtures/event/valid/task-registered.json +14 -0
  633. package/schema/fixtures/hash/known-answer-pre-121.json +74 -0
  634. package/schema/fixtures/hash/known-answer.json +74 -0
  635. package/schema/fixtures/policy/invalid/bad-approval-ttl.json +7 -0
  636. package/schema/fixtures/policy/invalid/bad-web-port.json +4 -0
  637. package/schema/fixtures/policy/invalid/checkpoint-key-not-base64.json +6 -0
  638. package/schema/fixtures/policy/invalid/class-rule-missing-autonomy.json +9 -0
  639. package/schema/fixtures/policy/invalid/empty-class-key.json +6 -0
  640. package/schema/fixtures/policy/invalid/live-rate-on-human-only.json +7 -0
  641. package/schema/fixtures/policy/invalid/malformed-class-key.json +6 -0
  642. package/schema/fixtures/policy/invalid/missing-version.json +8 -0
  643. package/schema/fixtures/policy/invalid/negative-limit.json +9 -0
  644. package/schema/fixtures/policy/invalid/non-numeric-limit.json +9 -0
  645. package/schema/fixtures/policy/invalid/non-positive-max-pending.json +9 -0
  646. package/schema/fixtures/policy/invalid/on-expiry-grant.json +8 -0
  647. package/schema/fixtures/policy/invalid/payload-retention-bare-number.json +4 -0
  648. package/schema/fixtures/policy/invalid/payload-retention-compound.json +4 -0
  649. package/schema/fixtures/policy/invalid/payload-retention-fractional.json +4 -0
  650. package/schema/fixtures/policy/invalid/payload-retention-zero.json +4 -0
  651. package/schema/fixtures/policy/invalid/protected-paths-escape.json +4 -0
  652. package/schema/fixtures/policy/invalid/protected-paths-glob.json +4 -0
  653. package/schema/fixtures/policy/invalid/retro-rate-on-human-only.json +7 -0
  654. package/schema/fixtures/policy/invalid/retro-rate-on-manual.json +7 -0
  655. package/schema/fixtures/policy/invalid/retro-rate-zero.json +7 -0
  656. package/schema/fixtures/policy/invalid/sample-rate-too-high.json +5 -0
  657. package/schema/fixtures/policy/invalid/sampling-secret-env-empty.json +7 -0
  658. package/schema/fixtures/policy/invalid/sampling-secret-env-not-string.json +6 -0
  659. package/schema/fixtures/policy/invalid/skew-tolerance-compound.json +6 -0
  660. package/schema/fixtures/policy/invalid/unknown-autonomy.json +7 -0
  661. package/schema/fixtures/policy/invalid/unknown-class-rule-key.json +6 -0
  662. package/schema/fixtures/policy/invalid/unknown-top-level-key.json +7 -0
  663. package/schema/fixtures/policy/invalid/vault-passphrase-env-empty.json +6 -0
  664. package/schema/fixtures/policy/invalid/vault-passphrase-literal.json +6 -0
  665. package/schema/fixtures/policy/invalid/version-not-string.json +4 -0
  666. package/schema/fixtures/policy/valid/canonical.json +47 -0
  667. package/schema/fixtures/policy/valid/checkpoint-keys.json +18 -0
  668. package/schema/fixtures/policy/valid/class-approvers-limits.json +25 -0
  669. package/schema/fixtures/policy/valid/class-retro-rate.json +17 -0
  670. package/schema/fixtures/policy/valid/global-budgets.json +19 -0
  671. package/schema/fixtures/policy/valid/human-only.json +9 -0
  672. package/schema/fixtures/policy/valid/minimal.json +6 -0
  673. package/schema/fixtures/policy/valid/protected-paths.json +10 -0
  674. package/schema/fixtures/policy/valid/record-namespace.json +13 -0
  675. package/schema/fixtures/policy/valid/request-volume-limits.json +26 -0
  676. package/schema/fixtures/policy/valid/retention-and-sampling-secret.json +16 -0
  677. package/schema/fixtures/policy/valid/skew-tolerance.json +15 -0
  678. package/schema/fixtures/policy/valid/vault-passphrase-env.json +14 -0
  679. package/schema/fixtures/policy/valid/wildcards.json +15 -0
  680. package/schema/fixtures/policy-md/invalid/alias-bomb.md +15 -0
  681. package/schema/fixtures/policy-md/invalid/no-fence.md +7 -0
  682. package/schema/fixtures/policy-md/invalid/protected-route-not-a-subclass.md +16 -0
  683. package/schema/fixtures/policy-md/invalid/schema-invalid-autonomy.md +16 -0
  684. package/schema/fixtures/policy-md/invalid/schema-invalid-read-proof.md +17 -0
  685. package/schema/fixtures/policy-md/invalid/two-fences.md +19 -0
  686. package/schema/fixtures/policy-md/invalid/unclosed-fence.md +11 -0
  687. package/schema/fixtures/policy-md/invalid/wrong-info-string.md +11 -0
  688. package/schema/fixtures/policy-md/invalid/yaml-syntax-error.md +13 -0
  689. package/schema/fixtures/policy-md/precedence/both/APPROVAL.md +7 -0
  690. package/schema/fixtures/policy-md/precedence/both/APPROVALS.md +7 -0
  691. package/schema/fixtures/policy-md/precedence/fallback-only/APPROVALS.md +7 -0
  692. package/schema/fixtures/policy-md/valid/canonical.md +50 -0
  693. package/schema/fixtures/policy-md/valid/daemon-read-proof.md +18 -0
  694. package/schema/fixtures/policy-md/valid/minimal.md +3 -0
  695. package/schema/fixtures/policy-md/valid/prose-lookalikes.md +54 -0
  696. package/schema/fixtures/policy-md/valid/routed-protected-paths.md +49 -0
  697. package/schema/fixtures/policy-md/valid/with-values.md +79 -0
  698. package/schema/fixtures/sample-record/invalid/bad-date-time.json +4 -0
  699. package/schema/fixtures/sample-record/invalid/missing-required-field.json +3 -0
  700. package/schema/fixtures/sample-record/invalid/unknown-top-level-field.json +5 -0
  701. package/schema/fixtures/sample-record/invalid/wrong-type.json +4 -0
  702. package/schema/fixtures/sample-record/valid/minimal.json +4 -0
  703. package/schema/fixtures/sample-record/valid/with-note.json +5 -0
  704. package/schema/fixtures/values/invalid/class-shaped.json +9 -0
  705. package/schema/fixtures/values/invalid/duplicate-entry.json +4 -0
  706. package/schema/fixtures/values/invalid/non-string-item.json +4 -0
  707. package/schema/fixtures/values/invalid/over-cap.json +26 -0
  708. package/schema/fixtures/values/invalid/unknown-key.json +5 -0
  709. package/schema/fixtures/values/invalid/version-string.json +1 -0
  710. package/schema/fixtures/values/valid/empty-lists.json +7 -0
  711. package/schema/fixtures/values/valid/full.json +20 -0
  712. package/schema/fixtures/values/valid/minimal.json +1 -0
  713. package/schema/fixtures/values-md/invalid/schema-invalid.md +62 -0
  714. package/schema/fixtures/values-md/invalid/two-blocks.md +69 -0
  715. package/schema/fixtures/values-md/invalid/unterminated.md +61 -0
  716. package/schema/fixtures/values-md/invalid/yaml-error.md +63 -0
  717. package/schema/fixtures/values-md/valid/absent.md +50 -0
  718. package/schema/fixtures/values-md/valid/with-values.md +79 -0
  719. package/schema/policy.schema.json +501 -0
  720. package/schema/sample-record.schema.json +26 -0
  721. package/schema/values.schema.json +55 -0
  722. package/templates/codex/README.md +9 -0
@@ -0,0 +1,1206 @@
1
+ /**
2
+ * `approval` CLI entry point — the commands of SPEC.md §10.1: the log-facing
3
+ * `approval log verify | tail | export` and `approval reindex`, the policy
4
+ * verbs, and the gate verbs `register`, `request`, `grant`, `reject`, `revoke`,
5
+ * and `expire`.
6
+ *
7
+ * **The CLI holds no logic.** Chain verification lives in `core/verify.ts`, the
8
+ * projection in `core/reindex.ts`, and appends in `core/log.ts`. Everything
9
+ * here is argument parsing, path resolution, output formatting, and the mapping
10
+ * from a core result to an exit code. That boundary is deliberate: the rules
11
+ * about what counts as a clean log must have exactly one implementation, and it
12
+ * is not this one.
13
+ *
14
+ * **Two things are frozen public API**, because agents depend on them
15
+ * mechanically: the exit codes (see `exit-codes.ts`) and the `--json` shapes
16
+ * (documented in every `--help`). Both are pinned by tests.
17
+ *
18
+ * **I/O is not integrity.** `verify()` cannot tell an unreadable log from a
19
+ * broken one, so this layer stats and access-checks the path *first* and
20
+ * reports filesystem problems as {@link EXIT_IO} with a message that never uses
21
+ * the word "corrupt". Absent files are exempt: an empty log is clean.
22
+ *
23
+ * Nothing in this file writes to the log, and no command repairs a torn tail.
24
+ * The gate verbs do append — through `core/gate.ts`, which appends through
25
+ * `core/log.ts` — and their exit-code mapping lives in `gate.ts` beside them: a
26
+ * gate refusal is {@link EXIT_INTEGRITY}, because the command was well-formed
27
+ * and the runtime's answer was no.
28
+ */
29
+ import { pathToFileURL } from "node:url";
30
+ import { boolFlag, countFlag, parseFlags, stringFlag } from "./args.js";
31
+ import { EXIT_INTEGRITY, EXIT_IO, EXIT_OK, EXIT_TORN_TAIL, EXIT_USAGE, } from "./exit-codes.js";
32
+ import { EXPORT_HELP, FOLLOW_HELP, LOG_HELP, REINDEX_HELP, ROOT_HELP, TAIL_HELP, VERIFY_HELP, } from "./help.js";
33
+ import { DEFAULT_INDEX_PATH, DEFAULT_LOG_PATH, preflightLog, resolvePath, } from "./paths.js";
34
+ import { parseLines, readCompleteLines } from "./records.js";
35
+ import { helpFor, longHelp } from "./long-help.js";
36
+ import { refusal as renderRefusal, resetStyle, style, table, } from "./style.js";
37
+ import { usageErrorText } from "./usage.js";
38
+ import { VERSION, wordmark } from "./wordmark.js";
39
+ const DEFAULT_TAIL_COUNT = 10;
40
+ const HELP_FLAGS = { "--help": "boolean", "-h": "boolean" };
41
+ function defaultStreams() {
42
+ return {
43
+ out: (text) => void process.stdout.write(text),
44
+ err: (text) => void process.stderr.write(text),
45
+ };
46
+ }
47
+ function emitJson(streams, value) {
48
+ streams.out(`${JSON.stringify(value)}\n`);
49
+ }
50
+ function emitJsonError(streams, code, message) {
51
+ streams.err(`${JSON.stringify({ error: { code, message } })}\n`);
52
+ }
53
+ function usageError(streams, json, message, helpText) {
54
+ if (json)
55
+ emitJsonError(streams, "usage", message);
56
+ else
57
+ streams.err(usageErrorText(message, helpText));
58
+ return EXIT_USAGE;
59
+ }
60
+ function ioError(streams, json, message) {
61
+ if (json)
62
+ emitJsonError(streams, "io", message);
63
+ else
64
+ streams.err(`approval: ${message}\n`);
65
+ return EXIT_IO;
66
+ }
67
+ function integrityError(streams, json, message) {
68
+ if (json)
69
+ emitJsonError(streams, "integrity", message);
70
+ else
71
+ streams.err(`approval: ${message}\n`);
72
+ return EXIT_INTEGRITY;
73
+ }
74
+ /** `--json` as seen before parsing, so parse failures can still answer in JSON. */
75
+ function wantsJson(argv) {
76
+ return argv.includes("--json");
77
+ }
78
+ /** One human-readable line per record: seq, ts, event, actor, task. */
79
+ function formatRecord(record) {
80
+ const fields = (record ?? {});
81
+ const cell = (value) => value === undefined || value === null ? "-" : String(value);
82
+ return [
83
+ cell(fields["seq"]),
84
+ cell(fields["ts"]),
85
+ cell(fields["event"]),
86
+ cell(fields["actor"]),
87
+ cell(fields["task"]),
88
+ ].join("\t");
89
+ }
90
+ /** The role an actor wears in `log tail`: a human decided, a robot did not. */
91
+ function actorRole(actor) {
92
+ if (actor.startsWith("human:"))
93
+ return "ok";
94
+ if (actor.startsWith("system:"))
95
+ return "muted";
96
+ return undefined;
97
+ }
98
+ /**
99
+ * `approval log tail`'s human rendering (APRV-91 #9, APRV-102).
100
+ *
101
+ * TWO SHAPES, DELIBERATELY, and the piped one is unchanged.
102
+ *
103
+ * In a pipe (and under `NO_COLOR`) this is exactly what it always was:
104
+ * tab-separated fields, one record per line. That shape is pinned by
105
+ * `tests/cli.test.ts`, printed in three `examples/*.md` transcripts, and — the
106
+ * reason that matters more than either — it is what `cut -f2` reads. An aligned
107
+ * table is a nicer thing to look at and a worse thing to pipe, because the
108
+ * separator stops being a character and starts being "however many spaces this
109
+ * particular log needed". A log tail is the surface most likely to be on the
110
+ * left of a pipe, so the plain bytes win there.
111
+ *
112
+ * On a terminal, where nothing is parsing the output, the columns are aligned
113
+ * and the brief's roles apply: the seq right-aligned so the digits line up,
114
+ * the event name in `key`, and the actor coloured by kind (human `ok`, agent
115
+ * undressed, system `muted`). Colour is redundant with the actor prefix printed
116
+ * beside it, as everywhere. The TIMESTAMP is left undressed against the brief's
117
+ * `muted`: APRV-102's rule that a copyable value is never painted outranks it,
118
+ * and this is the surface an operator lifts timestamps out of.
119
+ *
120
+ * Both shapes carry the same fields in the same order, so this is a change of
121
+ * spacing and dressing, never of content.
122
+ */
123
+ export function renderTailHuman(records, st = style()) {
124
+ if (!st.enabled)
125
+ return records.map((record) => `${formatRecord(record)}\n`).join("");
126
+ const cellOf = (value) => value === undefined || value === null ? "-" : String(value);
127
+ const rows = records.map((record) => {
128
+ const fields = (record ?? {});
129
+ const actor = cellOf(fields["actor"]);
130
+ const role = actorRole(actor);
131
+ return [
132
+ // Not `value`-roled but genuinely undressed: a seq is the thing an
133
+ // operator retypes into `approval audit review`.
134
+ cellOf(fields["seq"]),
135
+ // The brief marks a timestamp `muted`, and APRV-102's later rule — no
136
+ // colour inside a value a human copies — outranks it. A dim timestamp is
137
+ // exactly as unpasteable as a bold one, and this is the surface an
138
+ // operator lifts timestamps out of. The alignment does the separating.
139
+ cellOf(fields["ts"]),
140
+ { text: cellOf(fields["event"]), role: "key" },
141
+ role === undefined ? actor : { text: actor, role },
142
+ cellOf(fields["task"]),
143
+ ];
144
+ });
145
+ return `${table(st, rows, { align: ["right"] })}\n`;
146
+ }
147
+ function prelude(argv, spec, helpText, streams, cwd) {
148
+ const json = wantsJson(argv);
149
+ const parsed = parseFlags(argv, { ...spec, ...HELP_FLAGS });
150
+ if (!parsed.ok) {
151
+ return { kind: "handled", code: usageError(streams, json, parsed.message, helpText) };
152
+ }
153
+ if (boolFlag(parsed.flags, "--help") || boolFlag(parsed.flags, "-h")) {
154
+ streams.out(`${helpText}\n`);
155
+ return { kind: "handled", code: EXIT_OK };
156
+ }
157
+ const extra = parsed.positionals[0];
158
+ if (extra !== undefined) {
159
+ return {
160
+ kind: "handled",
161
+ code: usageError(streams, json, `unexpected argument ${JSON.stringify(extra)}`, helpText),
162
+ };
163
+ }
164
+ const logPath = resolvePath(stringFlag(parsed.flags, "--log"), DEFAULT_LOG_PATH, cwd);
165
+ return { kind: "run", flags: parsed.flags, logPath, json };
166
+ }
167
+ /**
168
+ * The `anomalies` field, present only when there is something to report.
169
+ *
170
+ * ADDITIVE, in the strict sense the frozen `--json` shapes require: a consumer
171
+ * written against the pre-APRV-40 shape sees byte-identical output for every log
172
+ * that has no anomaly, and the key appears only when the runtime has something
173
+ * new to say. Omitting the empty case is deliberate rather than lazy — an
174
+ * always-present empty array would change the shape of every existing clean
175
+ * result, and those shapes are what agents parse.
176
+ */
177
+ function anomalyField(anomalies) {
178
+ return anomalies.length === 0 ? {} : { anomalies };
179
+ }
180
+ /**
181
+ * Print anomalies to stderr, one line each.
182
+ *
183
+ * stderr rather than stdout, and after the verdict rather than instead of it:
184
+ * the verdict is the answer to the question asked (does this chain verify?), and
185
+ * an anomaly is a note in the margin. The exit code does not move. A clean log
186
+ * with anomalies exits 0, because the chain verifies and skew is a judgment for
187
+ * a human, not a proof the runtime is entitled to enforce.
188
+ */
189
+ function reportAnomalies(streams, anomalies) {
190
+ if (anomalies.length === 0)
191
+ return;
192
+ streams.err(`approval: ${anomalies.length} timestamp anomaly(ies) — the chain verifies and NOTHING is refused; these are reported for a human to weigh\n`);
193
+ for (const anomaly of anomalies) {
194
+ streams.err(`approval: ${anomaly.kind}: ${anomaly.message}\n`);
195
+ }
196
+ }
197
+ /**
198
+ * The anchor half of `approval log verify` (APRV-219).
199
+ *
200
+ * Runs only behind `--anchor`, and only on a chain that already verified: the
201
+ * committed copy answers a different question from the one the chain walk
202
+ * answers ("does anybody else hold these records?" rather than "is this file
203
+ * self-consistent?"), and asking it about a log that does not verify would be
204
+ * deciding something from an unverified log.
205
+ *
206
+ * A divergence is an integrity refusal and exits where `corrupt` exits. A skip
207
+ * is a skip: a repository with no committed copy has said nothing about this
208
+ * log, and this verb never reports silence as a pass.
209
+ */
210
+ function anchorField(outcome) {
211
+ if (outcome.status === "skip")
212
+ return { anchor: { status: "skip", reason: outcome.reason } };
213
+ return {
214
+ anchor: {
215
+ status: outcome.status,
216
+ rev: outcome.anchor.rev,
217
+ seq: outcome.anchor.head.seq,
218
+ hash: outcome.anchor.head.hash,
219
+ bytes: outcome.anchor.byteLength,
220
+ ...(outcome.status === "diverged" ? { message: outcome.message } : {}),
221
+ },
222
+ };
223
+ }
224
+ /** The anchor line a human reads, after the chain verdict it qualifies. */
225
+ function reportAnchor(outcome, streams) {
226
+ if (outcome.status === "skip") {
227
+ streams.err(`approval: anchor skipped — ${outcome.reason}\n`);
228
+ return;
229
+ }
230
+ if (outcome.status === "diverged")
231
+ return;
232
+ streams.out(`anchor ${outcome.anchor.rev}: ${outcome.detail}\n`);
233
+ }
234
+ /**
235
+ * The anchor block on the checkpoint-refusal path, where there may be none.
236
+ *
237
+ * A separate spelling rather than a nullable {@link anchorField}, so the field
238
+ * stays ADDITIVE in the strict sense the frozen `--json` shapes require: a
239
+ * consumer that never asked for `--anchor` sees no `anchor` key, on this path as
240
+ * on every other.
241
+ */
242
+ function anchorField2(outcome) {
243
+ return outcome === null ? {} : anchorField(outcome);
244
+ }
245
+ /**
246
+ * The checkpoint half of `approval log verify` (APRV-220).
247
+ *
248
+ * The keys come from the policy, which is where the human wrote them, and an
249
+ * unloadable policy is a SKIP naming that rather than a pass: the check has
250
+ * said nothing about this log, and a verb that reported silence as a verified
251
+ * chain would be a verb that stopped verifying.
252
+ */
253
+ async function runCheckpointCheck(records, cwd) {
254
+ const { checkLogCheckpoints, checkpointPolicyOf } = await import("../core/checkpoint.js");
255
+ const configured = checkpointPolicyOf({ dir: cwd });
256
+ return checkLogCheckpoints({
257
+ records,
258
+ publicKeys: configured.publicKeys,
259
+ checkpointEveryMs: configured.checkpointEveryMs,
260
+ keysUnavailable: configured.unloadable,
261
+ });
262
+ }
263
+ function checkpointField(outcome) {
264
+ if (outcome.status === "skip") {
265
+ return { checkpoints: { status: "skip", reason: outcome.reason } };
266
+ }
267
+ if (outcome.status === "refused") {
268
+ return {
269
+ checkpoints: {
270
+ status: "refused",
271
+ code: outcome.code,
272
+ at: outcome.at,
273
+ verified: outcome.checkpoints.length,
274
+ message: outcome.message,
275
+ },
276
+ };
277
+ }
278
+ const newest = outcome.checkpoints[outcome.checkpoints.length - 1] ?? null;
279
+ return {
280
+ checkpoints: {
281
+ status: "pass",
282
+ verified: outcome.checkpoints.length,
283
+ keys: outcome.keys,
284
+ unchecked: outcome.unchecked,
285
+ newest: newest === null ? null : { at: newest.at, seq: newest.seq, hash: newest.hash },
286
+ ...(outcome.warning === null ? {} : { warning: outcome.warning }),
287
+ },
288
+ };
289
+ }
290
+ /** The checkpoint line a human reads, after the chain verdict it qualifies. */
291
+ function reportCheckpoints(outcome, streams) {
292
+ if (outcome.status === "skip") {
293
+ streams.err(`approval: checkpoints skipped — ${outcome.reason}\n`);
294
+ return;
295
+ }
296
+ if (outcome.status === "refused")
297
+ return;
298
+ streams.out(`checkpoints: ${outcome.detail}\n`);
299
+ if (outcome.warning !== null)
300
+ streams.err(`approval: ${outcome.warning}\n`);
301
+ }
302
+ async function commandVerify(argv, streams, cwd) {
303
+ const front = prelude(argv, {
304
+ "--log": "string",
305
+ "--json": "boolean",
306
+ // APRV-219. `--anchor` is the default resolution (the newest committed
307
+ // copy this checkout can see); `--anchor-rev` names one and implies it.
308
+ // Two flags rather than one optional-value flag, because this CLI's
309
+ // parser has no optional-value form and inventing one to save a word
310
+ // would make every other flag's shape a special case.
311
+ "--anchor": "boolean",
312
+ "--anchor-rev": "string",
313
+ // APRV-220. The second witness, and a separate flag from `--anchor`
314
+ // because they answer different questions and fail in different
315
+ // directions: the anchor asks whether anybody else holds these bytes, a
316
+ // checkpoint asks whether a key no agent holds signed this head. Asking
317
+ // for one has never implied the other, and neither may be weakened to
318
+ // make the other pass.
319
+ "--checkpoints": "boolean",
320
+ }, VERIFY_HELP, streams, cwd);
321
+ if (front.kind === "handled")
322
+ return front.code;
323
+ const { flags, logPath, json } = front;
324
+ const anchorRev = stringFlag(flags, "--anchor-rev");
325
+ const wantsAnchor = boolFlag(flags, "--anchor") || anchorRev !== null;
326
+ const wantsCheckpoints = boolFlag(flags, "--checkpoints");
327
+ const check = preflightLog(logPath);
328
+ if (!check.ok)
329
+ return ioError(streams, json, check.message);
330
+ // The policy is consulted for one number, `audit.skew_tolerance` (APRV-58),
331
+ // and it reaches only which anomalies are reported. The verdict below is a
332
+ // function of the log bytes and the schemas, so a missing or unloadable
333
+ // policy leaves this command's answer exactly as it was.
334
+ // `verifyWithRecords` only when the anchor was asked for (APRV-219): the
335
+ // anchor check compares against records the caller has already verified, and
336
+ // a plain run has no use for them.
337
+ const { verify, verifyWithRecords } = await import("../core/verify.js");
338
+ const walked = wantsAnchor || wantsCheckpoints
339
+ ? verifyWithRecords(logPath, { policy: { dir: cwd } })
340
+ : { result: verify(logPath, { policy: { dir: cwd } }), records: [] };
341
+ const result = walked.result;
342
+ if (result.status === "clean") {
343
+ // Loaded only when asked for (APRV-209): the anchor check pulls in git-scope
344
+ // and the chain reconciler, and a plain `log verify` has no use for either.
345
+ const anchor = wantsAnchor
346
+ ? (await import("./log-anchor.js")).checkLogAnchor({
347
+ logPath,
348
+ records: walked.records,
349
+ ...(anchorRev === null ? {} : { rev: anchorRev }),
350
+ })
351
+ : null;
352
+ // A divergence replaces the clean verdict rather than qualifying it. The
353
+ // chain walk's answer is still true and it is no longer the answer to the
354
+ // question `--anchor` asked, so printing `clean` beside it would be this
355
+ // verb reporting a pass it does not mean.
356
+ if (anchor !== null && anchor.status === "diverged") {
357
+ if (json) {
358
+ emitJson(streams, {
359
+ status: "anchor-diverged",
360
+ records: result.records,
361
+ head: result.head,
362
+ ...anchorField(anchor),
363
+ message: anchor.message,
364
+ });
365
+ }
366
+ else {
367
+ streams.err(`${renderRefusal(style({ json }), anchor.code, anchor.message)}\n`);
368
+ }
369
+ return EXIT_INTEGRITY;
370
+ }
371
+ // The second witness (APRV-220), run independently of the first and after
372
+ // it. Independently, because a checkpoint refusal and an anchor divergence
373
+ // are different facts with different repairs, and neither may be softened
374
+ // to let the other report a pass; after it, only so that a log failing both
375
+ // reports the older check's message first.
376
+ const checkpoints = wantsCheckpoints
377
+ ? await runCheckpointCheck(walked.records, cwd)
378
+ : null;
379
+ if (checkpoints !== null && checkpoints.status === "refused") {
380
+ if (json) {
381
+ emitJson(streams, {
382
+ status: "checkpoint-invalid",
383
+ records: result.records,
384
+ head: result.head,
385
+ ...anchorField2(anchor),
386
+ ...checkpointField(checkpoints),
387
+ message: checkpoints.message,
388
+ });
389
+ }
390
+ else {
391
+ streams.err(`${renderRefusal(style({ json }), checkpoints.code, checkpoints.message)}\n`);
392
+ }
393
+ return EXIT_INTEGRITY;
394
+ }
395
+ if (json) {
396
+ emitJson(streams, {
397
+ status: result.status,
398
+ records: result.records,
399
+ head: result.head,
400
+ ...anomalyField(result.anomalies),
401
+ ...(anchor === null ? {} : anchorField(anchor)),
402
+ ...(checkpoints === null ? {} : checkpointField(checkpoints)),
403
+ });
404
+ }
405
+ else {
406
+ const head = result.head === null ? "head none" : `head seq ${result.head.seq} ${result.head.hash}`;
407
+ streams.out(`clean: ${result.records} record(s), ${head}\n`);
408
+ reportAnomalies(streams, result.anomalies);
409
+ if (anchor !== null)
410
+ reportAnchor(anchor, streams);
411
+ if (checkpoints !== null)
412
+ reportCheckpoints(checkpoints, streams);
413
+ }
414
+ return EXIT_OK;
415
+ }
416
+ if (result.status === "torn-tail") {
417
+ if (json) {
418
+ emitJson(streams, {
419
+ status: result.status,
420
+ records: result.records,
421
+ head: null,
422
+ intactThroughSeq: result.intactThroughSeq,
423
+ message: result.message,
424
+ ...anomalyField(result.anomalies),
425
+ });
426
+ }
427
+ else {
428
+ reportAnomalies(streams, result.anomalies);
429
+ streams.out(`torn-tail: ${result.records} record(s), intact through seq ${result.intactThroughSeq}\n`);
430
+ streams.err(`approval: ${result.message}\n`);
431
+ }
432
+ return EXIT_TORN_TAIL;
433
+ }
434
+ if (json) {
435
+ emitJson(streams, {
436
+ status: result.status,
437
+ records: null,
438
+ head: null,
439
+ firstBadSeq: result.firstBadSeq,
440
+ reason: result.reason,
441
+ message: result.message,
442
+ });
443
+ }
444
+ else {
445
+ const where = result.firstBadSeq === null ? "unknown seq" : `seq ${result.firstBadSeq}`;
446
+ // APRV-102: the shared refusal shape. `corrupt` is the machine-readable word
447
+ // here (it is `status` in `--json`, which is unchanged), and the reason and
448
+ // the seq are the message.
449
+ streams.err(`${renderRefusal(style({ json }), "corrupt", `${result.reason} at ${where}`)}\n`);
450
+ streams.err(`approval: ${result.message}\n`);
451
+ }
452
+ return EXIT_INTEGRITY;
453
+ }
454
+ /**
455
+ * `tail` and `export` share everything but the slice and the rendering, so they
456
+ * share the verify → read → refuse-or-print sequence too.
457
+ */
458
+ async function readForOutput(logPath, streams, json) {
459
+ const { verify } = await import("../core/verify.js");
460
+ const result = verify(logPath);
461
+ if (result.status === "corrupt") {
462
+ return {
463
+ code: integrityError(streams, json, `log ${logPath} failed chain verification (${result.reason}); refusing to print records from a tampered log: ${result.message}`),
464
+ };
465
+ }
466
+ const read = readCompleteLines(logPath, result.records);
467
+ if (!read.ok)
468
+ return { code: ioError(streams, json, read.message) };
469
+ return {
470
+ lines: read.lines,
471
+ warning: result.status === "torn-tail"
472
+ ? `log ${logPath} ends with a torn line (an unterminated final record, the signature of a crashed write); the ${result.records} intact record(s) are shown and the log is left exactly as it is — nothing was repaired or truncated`
473
+ : null,
474
+ };
475
+ }
476
+ async function commandTail(argv, streams, cwd) {
477
+ const front = prelude(argv, { "--log": "string", "--json": "boolean", "-n": "string" }, TAIL_HELP, streams, cwd);
478
+ if (front.kind === "handled")
479
+ return front.code;
480
+ const { flags, logPath, json } = front;
481
+ const count = countFlag(flags, "-n");
482
+ if (!count.ok)
483
+ return usageError(streams, json, count.message, TAIL_HELP);
484
+ const limit = count.value ?? DEFAULT_TAIL_COUNT;
485
+ const check = preflightLog(logPath);
486
+ if (!check.ok)
487
+ return ioError(streams, json, check.message);
488
+ const outcome = await readForOutput(logPath, streams, json);
489
+ if ("code" in outcome)
490
+ return outcome.code;
491
+ const selected = limit === 0 ? [] : outcome.lines.slice(-limit);
492
+ const parsed = parseLines(logPath, selected);
493
+ if (!parsed.ok)
494
+ return ioError(streams, json, parsed.message);
495
+ if (json) {
496
+ const status = outcome.warning === null ? "ok" : "torn-tail";
497
+ emitJson(streams, outcome.warning === null
498
+ ? { status, records: parsed.records }
499
+ : { status, records: parsed.records, warning: outcome.warning });
500
+ }
501
+ else {
502
+ streams.out(renderTailHuman(parsed.records, style({ json })));
503
+ }
504
+ if (outcome.warning !== null && !json)
505
+ streams.err(`approval: ${outcome.warning}\n`);
506
+ return EXIT_OK;
507
+ }
508
+ async function commandExport(argv, streams, cwd) {
509
+ const front = prelude(argv, { "--log": "string", "--json": "boolean" }, EXPORT_HELP, streams, cwd);
510
+ if (front.kind === "handled")
511
+ return front.code;
512
+ const { logPath, json } = front;
513
+ const check = preflightLog(logPath);
514
+ if (!check.ok)
515
+ return ioError(streams, json, check.message);
516
+ const outcome = await readForOutput(logPath, streams, json);
517
+ if ("code" in outcome)
518
+ return outcome.code;
519
+ if (json) {
520
+ const parsed = parseLines(logPath, outcome.lines);
521
+ if (!parsed.ok)
522
+ return ioError(streams, json, parsed.message);
523
+ emitJson(streams, outcome.warning === null
524
+ ? { records: parsed.records }
525
+ : { records: parsed.records, warning: outcome.warning });
526
+ }
527
+ else {
528
+ // Verbatim: the stored line plus the newline that terminated it. No parse,
529
+ // no re-serialization — export of a clean log is a byte-for-byte copy.
530
+ for (const line of outcome.lines)
531
+ streams.out(`${line}\n`);
532
+ }
533
+ if (outcome.warning !== null && !json)
534
+ streams.err(`approval: ${outcome.warning}\n`);
535
+ return EXIT_OK;
536
+ }
537
+ /** Long-lived, line-delimited verified event stream (APRV-322). */
538
+ async function commandFollow(argv, streams, cwd, nativeOutput) {
539
+ const front = prelude(argv, { "--log": "string", "--json": "boolean", "--from": "string", "--cursor-hash": "string" }, FOLLOW_HELP, streams, cwd);
540
+ if (front.kind === "handled")
541
+ return front.code;
542
+ const { flags, logPath, json } = front;
543
+ if (!json) {
544
+ return usageError(streams, false, "log follow requires --json", FOLLOW_HELP);
545
+ }
546
+ const fromResult = countFlag(flags, "--from");
547
+ if (!fromResult.ok)
548
+ return usageError(streams, true, fromResult.message, FOLLOW_HELP);
549
+ const from = fromResult.value ?? 0;
550
+ const expectedHash = stringFlag(flags, "--cursor-hash");
551
+ if (expectedHash !== null && from === 0) {
552
+ return usageError(streams, true, "--cursor-hash requires --from greater than zero", FOLLOW_HELP);
553
+ }
554
+ if (expectedHash !== null && !/^[a-f0-9]{64}$/u.test(expectedHash)) {
555
+ return usageError(streams, true, "--cursor-hash expects a lowercase 64-character SHA-256 digest", FOLLOW_HELP);
556
+ }
557
+ const check = preflightLog(logPath);
558
+ if (!check.ok)
559
+ return ioError(streams, true, check.message);
560
+ const controller = new AbortController();
561
+ let brokenPipe = false;
562
+ let signalCancellation = false;
563
+ const outputFailures = [];
564
+ const stop = () => {
565
+ signalCancellation = true;
566
+ controller.abort();
567
+ // A stalled downstream reader can leave one bounded chunk pending. The
568
+ // foreground command is ending by signal, so close that native write side
569
+ // rather than making shutdown depend on the reader resuming.
570
+ if (nativeOutput) {
571
+ process.stdout.destroy();
572
+ // A pipe-backed stdout can retain an outstanding libuv write even after
573
+ // destroy. Give the abort microtasks one turn to close the iterator and
574
+ // its watcher, then finish the signal-requested process without waiting
575
+ // for a downstream reader that has explicitly stopped reading.
576
+ setImmediate(() => process.exit(EXIT_OK));
577
+ }
578
+ };
579
+ const outputError = (cause) => {
580
+ if (signalCancellation) {
581
+ controller.abort();
582
+ return;
583
+ }
584
+ if (cause.code === "EPIPE")
585
+ brokenPipe = true;
586
+ else
587
+ outputFailures.push(cause);
588
+ controller.abort();
589
+ };
590
+ process.once("SIGINT", stop);
591
+ process.once("SIGTERM", stop);
592
+ if (nativeOutput)
593
+ process.stdout.on("error", outputError);
594
+ const writeRecord = async (record) => {
595
+ const line = `${JSON.stringify(record)}\n`;
596
+ if (!nativeOutput) {
597
+ streams.out(line);
598
+ return;
599
+ }
600
+ await new Promise((resolve, reject) => {
601
+ let settled = false;
602
+ const finish = (cause) => {
603
+ if (settled)
604
+ return;
605
+ settled = true;
606
+ process.stdout.removeListener("drain", drained);
607
+ controller.signal.removeEventListener("abort", cancelled);
608
+ if (cause === undefined)
609
+ resolve();
610
+ else
611
+ reject(cause);
612
+ };
613
+ const drained = () => finish();
614
+ const cancelled = () => finish();
615
+ controller.signal.addEventListener("abort", cancelled, { once: true });
616
+ process.stdout.once("drain", drained);
617
+ try {
618
+ // A false return is the Writable contract's backpressure signal. Do
619
+ // not ask the iterator for another record until `drain`; otherwise
620
+ // Node accepts the whole verified snapshot into its stdout queue.
621
+ if (process.stdout.write(line))
622
+ finish();
623
+ }
624
+ catch (cause) {
625
+ finish(cause instanceof Error ? cause : new Error(String(cause)));
626
+ }
627
+ });
628
+ };
629
+ try {
630
+ const { subscribeVerifiedLog } = await import("../core/log-subscribe.js");
631
+ const options = expectedHash === null
632
+ ? { from, signal: controller.signal }
633
+ : { from, expectedHash, signal: controller.signal };
634
+ for await (const record of subscribeVerifiedLog(logPath, options)) {
635
+ try {
636
+ await writeRecord(record);
637
+ }
638
+ catch (cause) {
639
+ if (cause.code === "EPIPE") {
640
+ brokenPipe = true;
641
+ controller.abort();
642
+ break;
643
+ }
644
+ throw cause;
645
+ }
646
+ }
647
+ if (brokenPipe || signalCancellation)
648
+ return EXIT_OK;
649
+ const outputFailure = outputFailures[0];
650
+ if (outputFailure !== undefined)
651
+ return ioError(streams, true, outputFailure.message);
652
+ return EXIT_OK;
653
+ }
654
+ catch (cause) {
655
+ const { LogSubscriptionError } = await import("../core/log-subscribe.js");
656
+ if (!(cause instanceof LogSubscriptionError))
657
+ throw cause;
658
+ if (cause.kind === "torn-tail") {
659
+ streams.err(`${JSON.stringify({ error: { code: "torn-tail", message: cause.message } })}\n`);
660
+ return EXIT_TORN_TAIL;
661
+ }
662
+ return cause.kind === "io"
663
+ ? ioError(streams, true, cause.message)
664
+ : integrityError(streams, true, cause.message);
665
+ }
666
+ finally {
667
+ process.removeListener("SIGINT", stop);
668
+ process.removeListener("SIGTERM", stop);
669
+ if (nativeOutput)
670
+ process.stdout.removeListener("error", outputError);
671
+ }
672
+ }
673
+ async function commandReindex(argv, streams, cwd) {
674
+ const front = prelude(argv, {
675
+ "--log": "string",
676
+ "--index": "string",
677
+ "--force": "boolean",
678
+ "--json": "boolean",
679
+ }, REINDEX_HELP, streams, cwd);
680
+ if (front.kind === "handled")
681
+ return front.code;
682
+ const { flags, logPath, json } = front;
683
+ const indexPath = resolvePath(stringFlag(flags, "--index"), DEFAULT_INDEX_PATH, cwd);
684
+ const check = preflightLog(logPath);
685
+ if (!check.ok)
686
+ return ioError(streams, json, check.message);
687
+ // The projection is the only thing in this CLI that loads `better-sqlite3`,
688
+ // and it is loaded here rather than at the top of the file so that the verbs
689
+ // that never touch the index never pay for the native addon (APRV-209).
690
+ const { reindex } = await import("../core/reindex.js");
691
+ const result = reindex(logPath, indexPath, boolFlag(flags, "--force") ? { force: true } : {});
692
+ if (result.ok) {
693
+ if (json) {
694
+ emitJson(streams, {
695
+ ok: true,
696
+ records: result.records,
697
+ head: result.head,
698
+ truncated: result.truncated,
699
+ });
700
+ }
701
+ else {
702
+ const head = result.head === null ? "head none" : `head seq ${result.head.seq} ${result.head.hash}`;
703
+ streams.out(`indexed ${result.records} record(s) into ${indexPath}: ${head}, truncated ${result.truncated}\n`);
704
+ }
705
+ return EXIT_OK;
706
+ }
707
+ if (json) {
708
+ emitJson(streams, {
709
+ ok: false,
710
+ error: { code: result.error.code, message: result.error.message },
711
+ });
712
+ }
713
+ else {
714
+ streams.err(`approval: ${result.error.message}\n`);
715
+ }
716
+ switch (result.error.code) {
717
+ case "not-clean":
718
+ return EXIT_INTEGRITY;
719
+ case "torn-tail":
720
+ return EXIT_TORN_TAIL;
721
+ default:
722
+ return EXIT_IO;
723
+ }
724
+ }
725
+ async function commandLog(argv, streams, cwd, nativeOutput) {
726
+ const sub = argv[0];
727
+ const rest = argv.slice(1);
728
+ if (sub === undefined) {
729
+ return usageError(streams, wantsJson(argv), "missing subcommand for `approval log`", LOG_HELP);
730
+ }
731
+ if (sub === "--help" || sub === "-h" || sub === "help") {
732
+ streams.out(`${LOG_HELP}\n`);
733
+ return EXIT_OK;
734
+ }
735
+ switch (sub) {
736
+ case "verify":
737
+ return commandVerify(rest, streams, cwd);
738
+ case "tail":
739
+ return commandTail(rest, streams, cwd);
740
+ case "export":
741
+ return commandExport(rest, streams, cwd);
742
+ case "follow":
743
+ return commandFollow(rest, streams, cwd, nativeOutput);
744
+ // APRV-125. The two verbs that move the log FILE rather than reading it: a
745
+ // fast-forward pull with a chain reconcile, and the commit-and-push of what
746
+ // the chain has grown since. Neither appends an event.
747
+ case "sync": {
748
+ const { commandLogSync } = await import("./log-verbs.js");
749
+ return commandLogSync(rest, streams, cwd);
750
+ }
751
+ case "advance": {
752
+ const { commandLogAdvance } = await import("./log-verbs.js");
753
+ return commandLogAdvance(rest, streams, cwd);
754
+ }
755
+ // APRV-220. The one verb here that APPENDS: a human signing the current
756
+ // head with a key no agent process holds. Loaded lazily like the two above,
757
+ // because it reaches the vault and the signing primitives and a plain
758
+ // `approval log tail` has no use for either.
759
+ case "checkpoint": {
760
+ const { commandLogCheckpoint } = await import("./log-checkpoint.js");
761
+ return commandLogCheckpoint(rest, streams, cwd);
762
+ }
763
+ default:
764
+ return usageError(streams, wantsJson(argv), `unknown subcommand ${JSON.stringify(sub)} for \`approval log\``, LOG_HELP);
765
+ }
766
+ }
767
+ /**
768
+ * The part of a command line that belongs to `approval` itself.
769
+ *
770
+ * `approval run … -- git push` and `hook classify -- <command…>` hand the tail
771
+ * to a child, and a `--no-color` in THAT half is the child's business. Reading
772
+ * presentation flags only from the near side is what keeps this CLI from
773
+ * quietly editing the command it was asked to run.
774
+ */
775
+ function beforeSeparator(argv) {
776
+ const separator = argv.indexOf("--");
777
+ return separator === -1 ? [...argv] : argv.slice(0, separator);
778
+ }
779
+ /** Remove `--no-color`, near side only, so no verb needs it in its flag spec. */
780
+ function stripNoColor(argv) {
781
+ const separator = argv.indexOf("--");
782
+ const near = (separator === -1 ? argv : argv.slice(0, separator)).filter((word) => word !== "--no-color");
783
+ return separator === -1 ? near : [...near, ...argv.slice(separator)];
784
+ }
785
+ /** The five verbs a new operator needs, under the wordmark, and nothing else. */
786
+ function splash(theme) {
787
+ const rows = [
788
+ { left: "init", right: "scaffold APPROVAL.md and .approval/ here" },
789
+ { left: "setup", right: "declare who you are and store credentials" },
790
+ { left: "doctor", right: "can this machine run the system?" },
791
+ { left: "queue", right: "what is waiting for your decision" },
792
+ { left: "--help", right: "every verb, and the exit codes" },
793
+ ];
794
+ return `${wordmark(theme)}\n\n${theme.table(rows, { indent: 2, gap: 3 })}`;
795
+ }
796
+ /**
797
+ * The help text `--long` was asked for, or null when it was not asked for.
798
+ *
799
+ * `--long` means nothing on its own: it is a modifier on a help request, so it
800
+ * is honoured only alongside `--help`/`-h` or the `help` verb. Anywhere else it
801
+ * falls through to the verb, which will call it an unknown flag, which is the
802
+ * right answer.
803
+ */
804
+ function longHelpRequest(argv) {
805
+ const near = beforeSeparator(argv);
806
+ if (!near.includes("--long"))
807
+ return null;
808
+ const asking = near[0] === "help" || near.includes("--help") || near.includes("-h");
809
+ if (!asking)
810
+ return null;
811
+ const words = (near[0] === "help" ? near.slice(1) : near).filter((word) => !word.startsWith("-"));
812
+ return (words.length === 0 ? null : helpFor(words)) ?? ROOT_HELP;
813
+ }
814
+ /**
815
+ * Await a verb that may answer asynchronously, and return its code.
816
+ *
817
+ * Before APRV-209 the arms that call this could not return the code at all:
818
+ * `main()` was synchronous, so an asynchronous verb's promise was dropped into
819
+ * `process.exitCode` and the arm returned {@link EXIT_OK}. Awaiting is now
820
+ * possible, and it also closes a hole the drop had opened: the entry point's own
821
+ * assignment to `process.exitCode` could land after the dropped promise's and
822
+ * overwrite a usage error with a zero.
823
+ *
824
+ * `label` is the phrase that named the verb in the old rejection message
825
+ * ("doctor failed", "MCP server failed"), so those messages are unchanged.
826
+ */
827
+ async function settle(outcome, streams, label) {
828
+ try {
829
+ return await outcome;
830
+ }
831
+ catch (cause) {
832
+ streams.err(`approval: ${label}: ${cause instanceof Error ? cause.message : String(cause)}\n`);
833
+ return EXIT_IO;
834
+ }
835
+ }
836
+ /**
837
+ * Run the CLI. Resolves to the process exit code rather than calling
838
+ * `process.exit`, so buffered stdout is flushed by the normal exit path — a
839
+ * truncated JSON object would be worse than no output at all.
840
+ *
841
+ * ASYNCHRONOUS since APRV-209, and for one reason: every verb is loaded by
842
+ * `await import()` inside the switch below, and ESM has no synchronous dynamic
843
+ * import. The awaits do not make any verb concurrent — exactly one runs per
844
+ * invocation, the preamble still decides presentation once before any of them
845
+ * can print, and the long-lived verbs (`channel`, `daemon`, `up`, `mcp`) report
846
+ * their eventual code through `process.exitCode` exactly as they did.
847
+ */
848
+ export async function main(argv, options = {}) {
849
+ const streams = options.streams ?? defaultStreams();
850
+ const cwd = options.cwd ?? process.cwd();
851
+ // Presentation is decided ONCE per invocation, before any verb can print
852
+ // (APRV-91). `--no-color` is answered here and stripped, so no verb has to
853
+ // carry it in its flag spec and none can disagree about it; `--json` is a
854
+ // veto on colour, which is why it is read before the verb parses anything.
855
+ const argvForStyle = beforeSeparator(argv);
856
+ const noColor = argvForStyle.includes("--no-color");
857
+ resetStyle();
858
+ const theme = style({ json: wantsJson(argvForStyle), noColor });
859
+ const cleanArgv = noColor ? stripNoColor(argv) : argv;
860
+ const command = cleanArgv[0];
861
+ const rest = cleanArgv.slice(1);
862
+ // `--help --long` and `approval help <verb> --long` (APRV-91 #16): the short
863
+ // help verbatim, then the reference section its `why:` footer points at.
864
+ // Intercepted HERE rather than in each verb, because the alternative is the
865
+ // same three lines in sixty places and one of them getting it wrong.
866
+ const longRequest = longHelpRequest(cleanArgv);
867
+ if (longRequest !== null) {
868
+ streams.out(`${longHelp(longRequest, { style: theme })}\n`);
869
+ return EXIT_OK;
870
+ }
871
+ if (command === undefined) {
872
+ // The orientation screen (APRV-91 #7/#12). It goes to STDOUT while the
873
+ // refusal stays on stderr with today's exit 2: a bare invocation is still a
874
+ // usage error for anything scripting this CLI, and the human staring at a
875
+ // terminal still gets the wordmark and the five verbs they need.
876
+ streams.out(`${splash(theme)}\n`);
877
+ return usageError(streams, false, "no command given", ROOT_HELP);
878
+ }
879
+ if (command === "--version" || command === "-v" || command === "version") {
880
+ streams.out(`${VERSION}\n`);
881
+ return EXIT_OK;
882
+ }
883
+ if (command === "--help" || command === "-h" || command === "help") {
884
+ // `approval help <verb>` is the third spelling of `approval <verb> --help`,
885
+ // and the one a person guesses first.
886
+ const words = rest.filter((word) => !word.startsWith("-"));
887
+ const target = words.length === 0 ? null : helpFor(words);
888
+ if (target !== null) {
889
+ streams.out(`${target}\n`);
890
+ return EXIT_OK;
891
+ }
892
+ streams.out(`${wordmark(theme)}\n\n${ROOT_HELP}\n`);
893
+ return EXIT_OK;
894
+ }
895
+ switch (command) {
896
+ // The self-describing verb (APRV-85). `instructions` prints the agent-facing
897
+ // guide, and `--schemas` prints the verb registry the guide's table is
898
+ // generated from — the one source SPEC.md §10.5's MCP wrapper derives its
899
+ // tool descriptions and input schemas from, so the two surfaces cannot
900
+ // drift. It reads no log, resolves no policy, and writes nothing.
901
+ case "instructions": {
902
+ const { commandInstructions } = await import("./instructions.js");
903
+ return commandInstructions(rest, streams, cwd);
904
+ }
905
+ // The scaffolding verb (APRV-71). It is the only command that writes files
906
+ // a human has not asked for by name, and it is deliberately the least
907
+ // authoritative one in the CLI: it appends nothing, attests nothing, and
908
+ // overwrites nothing. Everything it creates is inert until a human attests.
909
+ case "init": {
910
+ const { commandInit } = await import("./init.js");
911
+ return commandInit(rest, streams, cwd);
912
+ }
913
+ case "quickstart": {
914
+ const { commandQuickstart } = await import("./quickstart.js");
915
+ return settle(commandQuickstart(rest, streams, cwd), streams, "quickstart failed");
916
+ }
917
+ case "log":
918
+ return commandLog(rest, streams, cwd, options.streams === undefined);
919
+ case "policy": {
920
+ const { commandPolicy } = await import("./policy.js");
921
+ return commandPolicy(rest, streams, cwd);
922
+ }
923
+ // The gate verbs (APRV-16). grant/reject/revoke are human-only and expire
924
+ // is the system verb; the enforcement lives in core, not in this dispatch.
925
+ case "register": {
926
+ const { commandRegister } = await import("./gate.js");
927
+ return commandRegister(rest, streams, cwd);
928
+ }
929
+ case "request": {
930
+ const { commandRequest } = await import("./gate.js");
931
+ return commandRequest(rest, streams, cwd);
932
+ }
933
+ case "grant":
934
+ case "reject":
935
+ case "revoke": {
936
+ const { commandDecide } = await import("./gate.js");
937
+ return commandDecide(command, rest, streams, cwd);
938
+ }
939
+ // APRV-106. The one terminal gate verb that is NOT human-only: withdrawal
940
+ // is the requester retracting its own question, and the requester is
941
+ // usually an agent. The gate checks the actor against the request record,
942
+ // so the verb cannot be used to clear anyone else's queue.
943
+ case "withdraw": {
944
+ const { commandWithdraw } = await import("./gate.js");
945
+ return commandWithdraw(rest, streams, cwd);
946
+ }
947
+ case "expire": {
948
+ const { commandExpire } = await import("./gate.js");
949
+ return commandExpire(rest, streams, cwd);
950
+ }
951
+ // The token verbs (APRV-17). `token` reports status and writes nothing;
952
+ // `consume` is internal plumbing for APRV-18's `approval run` and is the
953
+ // only sanctioned appender of execution.started on the manual path.
954
+ case "token": {
955
+ const { commandToken } = await import("./token.js");
956
+ return commandToken(rest, streams, cwd);
957
+ }
958
+ case "consume": {
959
+ const { commandConsume } = await import("./token.js");
960
+ return commandConsume(rest, streams, cwd);
961
+ }
962
+ // The execution verbs (APRV-18). `run` is the only command that spawns
963
+ // anything and the only one that can exit 5; `wait` the only one that can
964
+ // exit 6. `queue` is the pending-decision inbox and `status` is system
965
+ // health — deliberately two verbs, because they answer to two different
966
+ // people (the human who decides, the operator who repairs).
967
+ case "run": {
968
+ const { commandRun } = await import("./execute.js");
969
+ return commandRun(rest, streams, cwd);
970
+ }
971
+ // The starving verb (APRV-193). It authorizes nothing and appends nothing:
972
+ // it runs a command with outbound network denied, which is what the hook
973
+ // cannot do for the commands it merely allows. `approval run` is the gate;
974
+ // this is the room the code the gate never saw runs in.
975
+ case "sandbox": {
976
+ const { commandSandbox } = await import("./sandbox.js");
977
+ return commandSandbox(rest, streams, cwd);
978
+ }
979
+ // The recovery verb (APRV-20 pass two). `execution resolve` is the only
980
+ // sanctioned way to close a dangling execution, and it is human-only,
981
+ // note-mandatory, and records no invented exit code.
982
+ case "execution": {
983
+ const { commandExecution } = await import("./execute.js");
984
+ return commandExecution(rest, streams, cwd);
985
+ }
986
+ // The audit verbs (APRV-40). `audit list` reads the sampled-audit backlog
987
+ // and `audit review` closes one item of it, human-only. There is no
988
+ // `audit sample`: selection is the runtime's, made by the daemon from an
989
+ // operator-held secret, and a caller who could sample could decline to.
990
+ case "audit": {
991
+ const { commandAudit } = await import("./audit.js");
992
+ return commandAudit(rest, streams, cwd);
993
+ }
994
+ case "wait": {
995
+ const { commandWait } = await import("./execute.js");
996
+ return commandWait(rest, streams, cwd);
997
+ }
998
+ case "queue": {
999
+ const { commandQueue } = await import("./execute.js");
1000
+ return commandQueue(rest, streams, cwd);
1001
+ }
1002
+ // The open window (APRV-214, amended SPEC.md §5.2). `gate open` is the one
1003
+ // verb that SUSPENDS the policy for the harness hook, so it is human-only
1004
+ // three times over: it classifies `policy.core` (which APPROVAL.md holds
1005
+ // human-only, so the hook denies an agent running it), it refuses a stdin
1006
+ // that is not a terminal, and it reads the word `understood` with no --yes
1007
+ // and no --force. `gate close` only tightens and `gate status` decides
1008
+ // nothing. The window's whole state is in the log; no file holds it.
1009
+ case "gate": {
1010
+ const { commandGate } = await import("./gate-window.js");
1011
+ return commandGate(rest, streams, cwd);
1012
+ }
1013
+ case "status": {
1014
+ const { commandStatus } = await import("./execute.js");
1015
+ return commandStatus(rest, streams, cwd);
1016
+ }
1017
+ // The witness verb (APRV-245). `status` reports what this runtime knows
1018
+ // about itself; `coverage` asks git, `gh` and a provider what happened
1019
+ // whether or not anybody routed it through the gate, and joins the answer
1020
+ // to the verified log. Informational: gaps are questions, not verdicts.
1021
+ case "coverage": {
1022
+ const { commandCoverage } = await import("./coverage.js");
1023
+ return commandCoverage(rest, streams, cwd);
1024
+ }
1025
+ // The diagnostic verb (APRV-31). `doctor` answers for the MACHINE what
1026
+ // `status` answers for the system, and it is asynchronous for the same
1027
+ // reason `channel` is: two of its checks touch the network stack (a Bot API
1028
+ // `getMe`, a loopback bind probe). It writes nothing anywhere.
1029
+ case "doctor": {
1030
+ const { commandDoctor } = await import("./doctor.js");
1031
+ return settle(commandDoctor(rest, streams, cwd), streams, "doctor failed");
1032
+ }
1033
+ // The channel verbs (APRV-23 cli, APRV-26 telegram). `channel cli` renders
1034
+ // the pending queue over the plugin contract and, with a terminal, collects
1035
+ // decisions through `recordChannelDecision` — the same human-only gate
1036
+ // `grant` and `reject` call. `channel telegram listen` is the first of the
1037
+ // LONG-LIVED commands in this CLI: it delivers the pending queue and then
1038
+ // long-polls until it is interrupted. `main` awaits it since APRV-209, so
1039
+ // the promise stays pending for as long as the listener runs and its code
1040
+ // is returned rather than dropped into `process.exitCode`.
1041
+ case "channel": {
1042
+ const { commandChannel } = await import("./channel.js");
1043
+ return settle(commandChannel(rest, streams, cwd), streams, "channel listener failed");
1044
+ }
1045
+ // The daemon verb (APRV-39). `daemon run` is the second LONG-LIVED command
1046
+ // in this CLI and is handled exactly like `channel`. It is the only command
1047
+ // that both watches and appends, and the only one whose ordinary ending is a
1048
+ // signal (which is exit 0, not a failure).
1049
+ case "daemon": {
1050
+ const { commandDaemon } = await import("./daemon.js");
1051
+ return settle(commandDaemon(rest, streams, cwd), streams, "daemon failed");
1052
+ }
1053
+ // The ambient runtime (APRV-110). `approval up` is the daemon loop and every
1054
+ // channel the policy configures in ONE supervised process, and it is the
1055
+ // fourth LONG-LIVED command here, awaited exactly as `channel` and `daemon`
1056
+ // are. `daemon run --with-channels` reaches the same function.
1057
+ case "up": {
1058
+ const { commandUp } = await import("./up.js");
1059
+ return settle(commandUp(rest, streams, cwd), streams, "the ambient runtime failed");
1060
+ }
1061
+ // The binding verb (APRV-29). `payload hash` prints the payload_hash of a
1062
+ // JSON document through the same core function the gate uses, so nobody has
1063
+ // to import an internal module (or reinvent JCS) to fill in a declaration.
1064
+ // It reads no log and writes nothing.
1065
+ // `payload agentmail-draft` (APRV-223) reads one draft over HTTPS, so this
1066
+ // verb joins the asynchronous family and is awaited the same way; the
1067
+ // `hash` path is still synchronous, and awaiting a number is a number.
1068
+ case "payload": {
1069
+ const { commandPayload } = await import("./payload.js");
1070
+ return settle(commandPayload(rest, streams, cwd), streams, "payload failed");
1071
+ }
1072
+ // The ungated channel (APRV-195). `journal write` is the one verb in this
1073
+ // switch that reaches no policy, no log and no token: it appends free text
1074
+ // to a local file so that an agent complying perfectly can still say it
1075
+ // thinks something is wrong. Nothing in the runtime reads what it writes,
1076
+ // which is what makes leaving it ungated safe (SPEC.md §11.1 invariant 4).
1077
+ case "journal": {
1078
+ const { commandJournal } = await import("./journal.js");
1079
+ return commandJournal(rest, streams, cwd);
1080
+ }
1081
+ // The human's half of the same pair (APRV-238). `values` prints the
1082
+ // optional values block of APPROVAL.md — what the operator values, wants
1083
+ // and how they answer — and it is guidance rather than policy: it grants
1084
+ // nothing, and no path that computes a verdict, a class, a sample, a budget
1085
+ // or a token reads it (SPEC.md §11.1 invariant 10). It resolves no policy
1086
+ // rule, reads no log and appends nothing.
1087
+ case "values": {
1088
+ const { commandValues } = await import("./values.js");
1089
+ return commandValues(rest, streams, cwd);
1090
+ }
1091
+ // The other direction of the same channel (APRV-239). `journal read` is the
1092
+ // operator reading what the agents said; this is the agents reading what the
1093
+ // operator said about their work. It reads a verified log and writes
1094
+ // nothing, and every output form labels what it prints as human-authored
1095
+ // GUIDANCE: no enforcement path anywhere in this dispatch reads a reaction
1096
+ // (SPEC.md §11.1 invariant 10), so a surface that let one read as a rule
1097
+ // would be the only place the invariant could break.
1098
+ case "feedback": {
1099
+ const { commandFeedback } = await import("./feedback.js");
1100
+ return commandFeedback(rest, streams, cwd);
1101
+ }
1102
+ // The environment verb (APRV-73). `env` resolves `.approval/env` — the
1103
+ // source map naming where each *_env variable's value lives — and prints an
1104
+ // export block for a shell to evaluate. IT IS THE ONLY COMMAND IN THIS
1105
+ // SWITCH THAT READS THAT FILE, and no command in this switch loads it into
1106
+ // its own environment: human identity is one of the variables it can carry,
1107
+ // so a file a process read on its own would let anything able to write it
1108
+ // act as the human on every human-only verb (SPEC.md §11.1 invariant 7).
1109
+ case "env": {
1110
+ const { commandEnv } = await import("./env.js");
1111
+ return commandEnv(rest, streams, cwd);
1112
+ }
1113
+ // The configuration verb (APRV-74) and the only WRITER of .approval/env.
1114
+ // It is interactive by construction: every subcommand refuses a
1115
+ // non-terminal stdin and --json, because a setup a pipe could drive would
1116
+ // be a way for a CI job or an agent to declare a human identity and store
1117
+ // a credential. It appends nothing to the log, attests nothing, and edits
1118
+ // no policy file. `setup channel telegram` reaches the network, so the dispatch
1119
+ // unwraps a promise exactly as `channel`, `daemon` and `adapter` do.
1120
+ case "setup": {
1121
+ const { commandSetup } = await import("./setup.js");
1122
+ return settle(commandSetup(rest, streams, cwd), streams, "setup failed");
1123
+ }
1124
+ // The credential verbs (APRV-68). `vault set|list|remove` manage the
1125
+ // encrypted store adapters read from, and all three are human-only. There
1126
+ // is deliberately no `vault get`: a credential's only sanctioned journey is
1127
+ // from the vault into an adapter inside the verified execution window, and a
1128
+ // verb that printed one would put it in a terminal and a shell history.
1129
+ // Nothing under this verb appends to the log.
1130
+ case "vault": {
1131
+ const { commandVault } = await import("./vault.js");
1132
+ return commandVault(rest, streams, cwd);
1133
+ }
1134
+ // The side-effect verb (APRV-69). `adapter email` is the first thing in
1135
+ // this CLI that reaches the world: it executes one granted action through
1136
+ // the adapter contract, which spends the token and writes both execution
1137
+ // events around the send. It is asynchronous for the obvious reason (a
1138
+ // socket), and is unwrapped exactly as `channel` and `daemon` are.
1139
+ case "adapter": {
1140
+ const { commandAdapter } = await import("./adapter.js");
1141
+ return settle(commandAdapter(rest, streams, cwd), streams, "adapter failed");
1142
+ }
1143
+ // The harness verbs (APRV-82, APRV-133). `hook claude-code` and
1144
+ // `hook cursor` each read a pre-tool event on STDIN and answer allow or
1145
+ // deny, so a command the harness runs itself cannot skip the gate the way
1146
+ // `approval run` cannot. They are the commands whose stdout is a decision
1147
+ // object for another program rather than a report for a human, and whose
1148
+ // exit code is deliberately 0 on a refusal: the harness reads a hook's
1149
+ // verdict only on exit 0.
1150
+ case "hook": {
1151
+ // The latency-critical case (APRV-209): a session pays this load on every
1152
+ // command it runs, so `hook.ts` and its core dependencies are the only
1153
+ // verb graph a pass-through invocation brings in.
1154
+ const { commandHook } = await import("./hook.js");
1155
+ return commandHook(rest, streams, cwd);
1156
+ }
1157
+ // The interoperability verb (APRV-64). `import agents-md` reads permissions
1158
+ // PROSE and prints a draft policy block. It is the only verb whose output is
1159
+ // a proposal: it writes no policy, appends nothing, and attests nothing —
1160
+ // the human's `policy amend` is what puts any of it in force.
1161
+ case "import": {
1162
+ const { commandImport } = await import("./import.js");
1163
+ return commandImport(rest, streams, cwd);
1164
+ }
1165
+ // The wrapper verb (APRV-87). `mcp serve` publishes the agent-facing verbs
1166
+ // as MCP tools over stdio (SPEC.md §10.5) and is the third LONG-LIVED
1167
+ // command here, unwrapped exactly as `channel` and `daemon` are. It is
1168
+ // AGENT-FACING BY CONSTRUCTION: its tool list is the verb registry filtered
1169
+ // by human_only, so nothing that records a human's authority is reachable
1170
+ // through it, and the identity it runs as is fixed before the transport
1171
+ // exists. The verb itself is human-only, because starting one is an
1172
+ // operator's act.
1173
+ case "codex": {
1174
+ const { commandCodex } = await import("./codex.js");
1175
+ return commandCodex(rest, streams, cwd);
1176
+ }
1177
+ case "mcp": {
1178
+ const { commandMcp } = await import("./mcp.js");
1179
+ return settle(commandMcp(rest, streams, cwd), streams, "MCP server failed");
1180
+ }
1181
+ case "reindex":
1182
+ return commandReindex(rest, streams, cwd);
1183
+ // The projection verb (APRV-24). `render` writes .approval/QUEUE.md and
1184
+ // nothing else; the projection itself is `channels/render-queue.ts`.
1185
+ case "render": {
1186
+ const { commandRender } = await import("./render.js");
1187
+ return commandRender(rest, streams, cwd);
1188
+ }
1189
+ default:
1190
+ return usageError(streams, wantsJson(argv), `unknown command ${JSON.stringify(command)}`, ROOT_HELP);
1191
+ }
1192
+ }
1193
+ // Direct execution: `node dist/src/cli/main.js …` behaves exactly like the
1194
+ // `approval` bin, which is a thin loader around this module.
1195
+ const invoked = process.argv[1];
1196
+ if (invoked !== undefined && import.meta.url === pathToFileURL(invoked).href) {
1197
+ // `main` resolves rather than returns since APRV-209; the code still reaches
1198
+ // the process through `process.exitCode`, so stdout is flushed by the normal
1199
+ // exit path. The rejection is DELIBERATELY not caught: a throw out of the
1200
+ // dispatch used to be an uncaught exception (stack trace, exit 1) and it stays
1201
+ // one, rather than being dressed up as one of the frozen exit codes.
1202
+ void main(process.argv.slice(2)).then((code) => {
1203
+ process.exitCode = code;
1204
+ });
1205
+ }
1206
+ //# sourceMappingURL=main.js.map