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,837 @@
1
+ /**
2
+ * `.approval/env` — the environment SOURCE MAP (SPEC.md §5.2, §11; APRV-73).
3
+ *
4
+ * Every secret this runtime touches is named by the policy and held in an
5
+ * environment variable: `channels.telegram.token_env` (§5.1), the chat id
6
+ * beside it, `audit.sampling_secret_env` (§5.2), `vault.passphrase_env` (§5.2),
7
+ * and `APPROVAL_HUMAN`, which is human identity itself (§11). The policy carries
8
+ * NAMES and never values, which is the right boundary and leaves an operator
9
+ * with five variables to get into a shell before any gate operation works, and
10
+ * no written-down place to say where they come from.
11
+ *
12
+ * This module is that written-down place. `.approval/env` is a source map, not
13
+ * a secret store: `KEY=VALUE` lines whose VALUE says WHERE the value lives.
14
+ *
15
+ * ```
16
+ * # one line per variable; # comments and blank lines are ignored
17
+ * APPROVAL_HUMAN=human:alice
18
+ * APPROVAL_TG_TOKEN=keychain:approval-telegram-token
19
+ * APPROVAL_VAULT_PASSPHRASE=secret-service:vault-passphrase
20
+ * APPROVAL_AUDIT_SECRET=env:
21
+ * ```
22
+ *
23
+ * Four value forms, and one of them is deliberately unpleasant:
24
+ *
25
+ * - `keychain:<service>` — macOS, `security find-generic-password -a "$USER"
26
+ * -s <service> -w`. The value comes back on stdout and is never in an argv.
27
+ * - `secret-service:<label>` — Linux desktop, `secret-tool lookup approval
28
+ * <label>`. Same property.
29
+ * - `env:` — inherited from the ambient environment. A documentation form: it
30
+ * asserts "this one comes from the shell that launched you", and resolves to
31
+ * nothing on its own.
32
+ * - a bare literal — permitted, and ALWAYS reported as plaintext. A rule people
33
+ * route around is not a control: an operator who is told plainly that their
34
+ * token is sitting in a file in the working tree can decide to accept that,
35
+ * and an operator who is forbidden from writing it there writes it into their
36
+ * shell profile instead, where nothing in this runtime can see it to say so.
37
+ *
38
+ * A value carrying some OTHER `word:` prefix is a literal, not an error, because
39
+ * `APPROVAL_HUMAN=human:alice` is the commonest line this file will ever hold.
40
+ * Near misses of the real schemes are reserved by name and refused rather than
41
+ * silently exported as text; see {@link RESERVED_SCHEMES}.
42
+ *
43
+ * ## THE LOAD-BEARING RULE: nothing loads this file implicitly
44
+ *
45
+ * No verb in this runtime reads `.approval/env` into its own environment. One
46
+ * verb, `approval env`, resolves it and prints an export block for a shell to
47
+ * evaluate; every other command sees only the environment the human who
48
+ * launched it had already established.
49
+ *
50
+ * The reason is `APPROVAL_HUMAN`. SPEC.md §11 states that human identity in v0.1
51
+ * is config-declared and that "the trust boundary is the local machine, and
52
+ * anyone who can set that configuration and write to the log is inside it". A
53
+ * working-tree file that could set `APPROVAL_HUMAN` would move that boundary to
54
+ * "anyone who can write a file in the repository": an agent that can edit source
55
+ * — which every agent working in a repository can — would thereby be able to
56
+ * attest a policy, grant its own request, and set a vault credential, on every
57
+ * invocation, as the human. The whole human-only gate would be one `echo >>`
58
+ * away from being nobody's gate.
59
+ *
60
+ * So the file is inert. A human evaluates `approval env` in their own shell,
61
+ * sees the export block that is about to run (or checks it value-free first with
62
+ * `--check`), and the process that performs a gate operation inherits an
63
+ * environment a human established. This is SPEC.md §11.1 invariant 7, and
64
+ * `tests/cli-env.test.ts` pins it by spawning `doctor`, `policy attest` and
65
+ * `channel telegram health` in a directory holding a complete `.approval/env`
66
+ * and asserting that none of them saw a byte of it.
67
+ *
68
+ * ## Mode 0600
69
+ *
70
+ * A file that may hold a literal secret is refused unless its mode is exactly
71
+ * `0600`, and the refusal prints the `chmod`. This is a lock on a door whose
72
+ * wall is missing (the same session can chmod it back), and it is worth having
73
+ * for the reason `umask` is worth having: the common failure is a
74
+ * world-readable file nobody looked at, not an adversary in the room.
75
+ *
76
+ * ## Determinism, and the one place it stops
77
+ *
78
+ * Parsing is a pure function of the bytes. Resolution is not: it shells out to
79
+ * helper binaries, which is why {@link SourceRunner} exists as an injectable
80
+ * seam and why the tests drive stub `security` / `secret-tool` scripts through
81
+ * PATH rather than touching a real Keychain.
82
+ *
83
+ * Nothing here throws. Every failure is a `{ ok: false, code, message }` from
84
+ * the frozen union {@link ENV_FILE_REFUSAL_CODES}. No credential VALUE appears
85
+ * in a refusal, a message, or a `source` label on any path.
86
+ */
87
+ import { spawnSync } from "node:child_process";
88
+ import { createHash } from "node:crypto";
89
+ import { chmodSync, mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
90
+ import { userInfo } from "node:os";
91
+ import { basename, dirname, join } from "node:path";
92
+ import { HUMAN_ACTOR_ENV } from "./attest.js";
93
+ import { telegramChatEnvFor, telegramTokenEnvFor } from "./telegram-config.js";
94
+ import { passphraseEnvFor } from "./vault.js";
95
+ // ---------------------------------------------------------------------------
96
+ // Location
97
+ // ---------------------------------------------------------------------------
98
+ /** The source map's filename, beside the log's home: `.approval/env`. */
99
+ export const ENV_FILENAME = "env";
100
+ /**
101
+ * The env file for a given log path — derived exactly as `vaultPathFor` derives
102
+ * the vault, so the log, the payload store, the vault and this file stay under
103
+ * one home: SPEC.md §9 fixes the log at `<home>/log/events.jsonl`, so the source
104
+ * map is `<home>/env`, a sibling of the log DIRECTORY and never inside it.
105
+ */
106
+ export function envFilePathFor(logPath) {
107
+ const logDir = dirname(logPath);
108
+ const home = basename(logDir) === "log" ? dirname(logDir) : logDir;
109
+ return join(home, ENV_FILENAME);
110
+ }
111
+ /** The mode the file must have. Anything else is refused. */
112
+ export const ENV_FILE_REQUIRED_MODE = 0o600;
113
+ // ---------------------------------------------------------------------------
114
+ // Refusals
115
+ // ---------------------------------------------------------------------------
116
+ /**
117
+ * Everything this module can refuse. Frozen public API, per SPEC.md §11.1(6).
118
+ *
119
+ * Each code names a different repair, which is the test of whether a code earns
120
+ * its place. The three helper codes are separate for exactly that reason: "you
121
+ * are on a machine without `secret-tool`", "the item is not in your keychain",
122
+ * and "the helper ran and failed" are three different mornings.
123
+ */
124
+ export const ENV_FILE_REFUSAL_CODES = [
125
+ /** The file's mode is not 0600. The refusal carries the `chmod` to run. */
126
+ "env-file-mode",
127
+ /**
128
+ * The file exists and could not be read, stat'd, or (APRV-74) written. A
129
+ * filesystem fact in every case, with a filesystem repair, which is why the
130
+ * write path reuses this code rather than adding a fourth I/O name to a
131
+ * frozen union: "the directory is read-only" and "the file is unreadable"
132
+ * are the same morning and the same exit code.
133
+ */
134
+ "env-file-io",
135
+ /** A line is neither blank, nor a comment, nor `KEY=VALUE`. */
136
+ "env-file-syntax",
137
+ /** A KEY does not match `[A-Z_][A-Z0-9_]*`. No `export ` prefix is accepted. */
138
+ "env-file-key-invalid",
139
+ /** The same KEY appears twice. Which one wins is not a thing to guess at. */
140
+ "env-file-duplicate-key",
141
+ /** A VALUE carries a `scheme:` prefix this build does not implement. */
142
+ "env-file-unknown-scheme",
143
+ /** A KEY with an empty VALUE. An empty secret is a configuration error. */
144
+ "env-file-empty-value",
145
+ /** The helper binary for a scheme is not on PATH. Not the operator's fault. */
146
+ "helper-binary-missing",
147
+ /** The helper ran and the named item is not there. Store it, or fix the name. */
148
+ "helper-item-missing",
149
+ /** The helper ran and failed for some other reason (locked keyring, …). */
150
+ "helper-failed",
151
+ /**
152
+ * A policy declared an `_env` NAME that is not a usable shell variable name.
153
+ * Never emitted as an `export` line: the export block is evaluated by a shell,
154
+ * and a name carrying a space or a `;` would be a policy file executing code.
155
+ */
156
+ "invalid-variable-name",
157
+ ];
158
+ function refuse(code, path, message, line) {
159
+ return line === undefined
160
+ ? { ok: false, code, message, path }
161
+ : { ok: false, code, message, path, line };
162
+ }
163
+ function detail(cause) {
164
+ return cause instanceof Error ? cause.message : String(cause);
165
+ }
166
+ /** `[A-Z_][A-Z0-9_]*` — the shape of an environment variable name. */
167
+ const KEY_PATTERN = /^[A-Z_][A-Z0-9_]*$/u;
168
+ /**
169
+ * A shell-safe variable name. Deliberately laxer than {@link KEY_PATTERN}
170
+ * (lower case is legal in a shell) and still a closed character set, because
171
+ * these names arrive from a policy file and are emitted into an `export` line.
172
+ */
173
+ const SHELL_NAME_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/u;
174
+ /**
175
+ * A `scheme:` prefix. Lower-case letters only, so a bare literal that happens to
176
+ * contain a colon (`7654321:AA…`, a Telegram token, is the case that matters) is
177
+ * a literal and not a mystery scheme.
178
+ */
179
+ const SCHEME_PATTERN = /^([a-z][a-z0-9-]*):(.*)$/su;
180
+ /** The schemes this build implements. */
181
+ const KNOWN_SCHEMES = new Set([
182
+ "keychain",
183
+ "secret-service",
184
+ "env",
185
+ "literal",
186
+ ]);
187
+ /**
188
+ * The scheme namespace is a CLOSED, RESERVED LIST, and a `word:` prefix outside
189
+ * it is a literal rather than an error.
190
+ *
191
+ * The rule earns its complexity from one line, which is the commonest line this
192
+ * file will ever hold:
193
+ *
194
+ * ```
195
+ * APPROVAL_HUMAN=human:alice
196
+ * ```
197
+ *
198
+ * `human:<id>` is the actor syntax of SPEC.md §8. A parser that treated every
199
+ * `word:` prefix as a scheme would refuse the identity line every operator
200
+ * writes first and send them to `literal:human:alice`, which nobody would ever
201
+ * guess and nobody should have to.
202
+ *
203
+ * A silent literal reading is still wrong for a MISTYPED source, though:
204
+ * `keyring:approval-token` would export the eleven characters "keyring:app…" as
205
+ * a bot token, and the failure would surface as a 401 from Telegram hours later.
206
+ * So the near misses and the plausible future schemes are reserved by name and
207
+ * refused, and everything else is a literal. Adding a scheme later means moving
208
+ * a string from this set to {@link KNOWN_SCHEMES}, which is a reviewable diff
209
+ * and never a change in meaning for an existing file: a reserved prefix has
210
+ * never been readable as a literal.
211
+ */
212
+ const RESERVED_SCHEMES = new Set([
213
+ // Misspellings and near neighbours of the two implemented helpers.
214
+ "keyring",
215
+ "keychains",
216
+ "keychain-service",
217
+ "secret_service",
218
+ "secretservice",
219
+ "secret-tool",
220
+ "secrettool",
221
+ "secrets",
222
+ // Words that could only be meant as a source.
223
+ "environment",
224
+ "plaintext",
225
+ "plain",
226
+ "file",
227
+ "exec",
228
+ "command",
229
+ "shell",
230
+ // Credential managers a future scheme would plausibly be named after.
231
+ "pass",
232
+ "gopass",
233
+ "op",
234
+ "onepassword",
235
+ "bitwarden",
236
+ "lastpass",
237
+ "vault",
238
+ "wincred",
239
+ ]);
240
+ /**
241
+ * Parse the file's text. No interpolation, no quote stripping, no `export `
242
+ * prefix, no line continuations: this is a source map, not a shell script, and
243
+ * every one of those features is a way for a file to mean something other than
244
+ * what it looks like.
245
+ *
246
+ * Quotes are NOT stripped, which is worth saying out loud because `.env` files
247
+ * elsewhere do strip them: here `A="b"` is the five-character literal `"b"`.
248
+ */
249
+ export function parseEnvFile(text, path) {
250
+ const entries = [];
251
+ const seen = new Map();
252
+ const lines = text.split("\n");
253
+ for (const [index, raw] of lines.entries()) {
254
+ const line = index + 1;
255
+ const trimmed = raw.trim();
256
+ if (trimmed.length === 0 || trimmed.startsWith("#"))
257
+ continue;
258
+ if (trimmed.startsWith("export ")) {
259
+ return refuse("env-file-syntax", path, `line ${String(line)} begins with \`export \`: this file is a source map read by \`approval env\`, not a shell script, and every line is \`KEY=VALUE\`. Drop the \`export \`.`, line);
260
+ }
261
+ const equals = trimmed.indexOf("=");
262
+ if (equals === -1) {
263
+ return refuse("env-file-syntax", path, `line ${String(line)} is neither blank, a # comment, nor KEY=VALUE`, line);
264
+ }
265
+ const key = trimmed.slice(0, equals);
266
+ const value = trimmed.slice(equals + 1);
267
+ if (!KEY_PATTERN.test(key)) {
268
+ return refuse("env-file-key-invalid", path, `line ${String(line)}: ${JSON.stringify(key)} is not an environment variable name (it must match [A-Z_][A-Z0-9_]*)`, line);
269
+ }
270
+ const first = seen.get(key);
271
+ if (first !== undefined) {
272
+ return refuse("env-file-duplicate-key", path, `${key} is set twice, on lines ${String(first)} and ${String(line)}; which one wins is not something a runtime should guess`, line);
273
+ }
274
+ seen.set(key, line);
275
+ if (value.length === 0) {
276
+ return refuse("env-file-empty-value", path, `line ${String(line)}: ${key} has an empty value. Use \`${key}=env:\` to say it is inherited from the shell, or delete the line.`, line);
277
+ }
278
+ const scheme = SCHEME_PATTERN.exec(value);
279
+ if (scheme === null) {
280
+ entries.push({ key, kind: "literal", argument: value, line });
281
+ continue;
282
+ }
283
+ const name = scheme[1];
284
+ const argument = scheme[2];
285
+ if (!KNOWN_SCHEMES.has(name)) {
286
+ if (!RESERVED_SCHEMES.has(name)) {
287
+ // Not a scheme at all: `human:alice` and every other value that merely
288
+ // contains a colon. See RESERVED_SCHEMES for why this is the default.
289
+ entries.push({ key, kind: "literal", argument: value, line });
290
+ continue;
291
+ }
292
+ return refuse("env-file-unknown-scheme", path, `line ${String(line)}: ${key} names the source scheme ${JSON.stringify(`${name}:`)}, which is reserved and which this build does not implement. Implemented: keychain:<service>, secret-service:<label>, env: (inherited), literal:<value>, or a bare value (a plaintext literal). If you really meant the literal text, write \`${key}=literal:${name}:…\` — a reserved prefix is never read as a literal by accident.`, line);
293
+ }
294
+ if (name === "literal") {
295
+ if (argument.length === 0) {
296
+ return refuse("env-file-empty-value", path, `line ${String(line)}: ${key} is \`literal:\` with nothing after it`, line);
297
+ }
298
+ entries.push({ key, kind: "literal", argument, line });
299
+ continue;
300
+ }
301
+ if (name === "env") {
302
+ // `env:whatever` is not a rename facility; the ambient variable is the
303
+ // one with this KEY, and anything after the colon would be a second,
304
+ // silent naming convention.
305
+ if (argument.length > 0) {
306
+ return refuse("env-file-syntax", path, `line ${String(line)}: \`env:\` takes nothing after the colon — it means "${key} is inherited from the shell that launched the process", and there is deliberately no way to inherit one variable under another's name`, line);
307
+ }
308
+ entries.push({ key, kind: "env", argument: "", line });
309
+ continue;
310
+ }
311
+ if (argument.length === 0) {
312
+ return refuse("env-file-syntax", path, `line ${String(line)}: \`${name}:\` needs ${name === "keychain" ? "a service name" : "a label"} after the colon`, line);
313
+ }
314
+ entries.push({ key, kind: name, argument, line });
315
+ }
316
+ return { ok: true, entries };
317
+ }
318
+ // ---------------------------------------------------------------------------
319
+ // Reading
320
+ // ---------------------------------------------------------------------------
321
+ /**
322
+ * The digest of an env file's bytes, as read.
323
+ *
324
+ * Not a secret and not derived from one in the sense that matters: the file may
325
+ * carry a plaintext literal, so this is a hash and never the text, and it is
326
+ * one-way. Its only consumer is `core/instance.ts`, which uses it to ask "was
327
+ * this exported value produced from the file as it now reads?" — a question
328
+ * about VERSIONS of a file, which needs an identifier for a version and nothing
329
+ * else (APRV-278).
330
+ */
331
+ export function envFileDigest(text) {
332
+ return createHash("sha256").update(text, "utf8").digest("hex");
333
+ }
334
+ /** An absent file's digest: the digest of the nothing that was read. */
335
+ export const ABSENT_ENV_FILE_DIGEST = envFileDigest("");
336
+ /**
337
+ * Read and parse the source map.
338
+ *
339
+ * **An absent file is not an error.** Nobody has written one, which is the state
340
+ * of every working directory that keeps its variables in a shell profile, and it
341
+ * is the state `approval init` leaves behind. Every variable then falls to
342
+ * "inherited from the environment" or "unset", which is exactly the world
343
+ * before this file existed.
344
+ */
345
+ export function readEnvFile(path) {
346
+ let stats;
347
+ try {
348
+ stats = statSync(path);
349
+ }
350
+ catch (cause) {
351
+ if (cause.code === "ENOENT") {
352
+ return { ok: true, present: false, path, entries: [], digest: ABSENT_ENV_FILE_DIGEST };
353
+ }
354
+ return refuse("env-file-io", path, `${path} could not be stat'd: ${detail(cause)}`);
355
+ }
356
+ if (!stats.isFile()) {
357
+ return refuse("env-file-io", path, `${path} is not a regular file`);
358
+ }
359
+ const mode = stats.mode & 0o777;
360
+ if (mode !== ENV_FILE_REQUIRED_MODE) {
361
+ return refuse("env-file-mode", path, `${path} has mode ${mode.toString(8).padStart(4, "0")}, and this file may carry a plaintext secret, so it is read only at 0600. Run:\n chmod 600 ${path}`);
362
+ }
363
+ let text;
364
+ try {
365
+ text = readFileSync(path, "utf8");
366
+ }
367
+ catch (cause) {
368
+ return refuse("env-file-io", path, `${path} could not be read: ${detail(cause)}`);
369
+ }
370
+ const parsed = parseEnvFile(text, path);
371
+ if (!parsed.ok)
372
+ return parsed;
373
+ return { ok: true, present: true, path, entries: parsed.entries, digest: envFileDigest(text) };
374
+ }
375
+ /**
376
+ * Add or replace `KEY=VALUE` lines, preserving everything else in the file.
377
+ *
378
+ * **Line-oriented, not a rewrite.** The file is read as text, the line whose
379
+ * KEY matches is replaced IN PLACE, and a key that is not present is appended
380
+ * at the end. Comments, blank lines, ordering, and every entry this call was
381
+ * not asked about survive byte for byte. A writer that reparsed and re-emitted
382
+ * would be simpler and would quietly delete the operator's own comments the
383
+ * first time `approval setup channel telegram` ran — this file is one a human edits by
384
+ * hand, and round-trip fidelity for a hand-edited file is the same requirement
385
+ * the Backlog.md task files carry.
386
+ *
387
+ * The file is validated before it is touched: {@link readEnvFile}'s mode check
388
+ * and full parse both run, so `setup` never appends a line to a file it could
389
+ * not have read, and never lands a valid line in a file whose earlier line is a
390
+ * syntax error. A file that does not exist is created at 0600, along with its
391
+ * directory.
392
+ *
393
+ * Callers pass values, and a value here is a SOURCE (`keychain:<service>`), a
394
+ * chat id, or an identity — never a credential, except on the one path where an
395
+ * operator explicitly chose a plaintext literal after being told what it means.
396
+ * Nothing in this function prints anything.
397
+ */
398
+ export function upsertEnvFileEntries(path, entries) {
399
+ const existing = readEnvFile(path);
400
+ if (!existing.ok)
401
+ return existing;
402
+ for (const entry of entries) {
403
+ if (!KEY_PATTERN.test(entry.key)) {
404
+ return refuse("env-file-key-invalid", path, `${JSON.stringify(entry.key)} is not an environment variable name (it must match [A-Z_][A-Z0-9_]*), so no line was written`);
405
+ }
406
+ if (entry.value.length === 0) {
407
+ return refuse("env-file-empty-value", path, `${entry.key} would be written with an empty value, which is not a source; nothing was written`);
408
+ }
409
+ if (entry.value.includes("\n")) {
410
+ return refuse("env-file-syntax", path, `the value for ${entry.key} contains a newline, and one line is one variable; nothing was written`);
411
+ }
412
+ }
413
+ let text = "";
414
+ if (existing.present) {
415
+ try {
416
+ text = readFileSync(path, "utf8");
417
+ }
418
+ catch (cause) {
419
+ return refuse("env-file-io", path, `${path} could not be re-read: ${detail(cause)}`);
420
+ }
421
+ }
422
+ // Split into lines WITHOUT the trailing terminator, so an append lands on its
423
+ // own line and the file ends with exactly one newline whichever state it was
424
+ // in. An empty (or absent) file is zero lines, not one empty one.
425
+ const lines = text.length === 0 ? [] : (text.endsWith("\n") ? text.slice(0, -1) : text).split("\n");
426
+ const changes = [];
427
+ for (const entry of entries) {
428
+ const line = `${entry.key}=${entry.value}`;
429
+ const index = lines.findIndex((candidate) => {
430
+ const trimmed = candidate.trim();
431
+ if (trimmed.startsWith("#"))
432
+ return false;
433
+ const equals = trimmed.indexOf("=");
434
+ return equals !== -1 && trimmed.slice(0, equals) === entry.key;
435
+ });
436
+ if (index === -1) {
437
+ lines.push(line);
438
+ changes.push({ key: entry.key, value: entry.value, previous: null, unchanged: false });
439
+ continue;
440
+ }
441
+ const before = lines[index].trim();
442
+ const previous = before.slice(before.indexOf("=") + 1);
443
+ lines[index] = line;
444
+ changes.push({
445
+ key: entry.key,
446
+ value: entry.value,
447
+ previous,
448
+ unchanged: previous === entry.value,
449
+ });
450
+ }
451
+ const body = lines.length === 0 ? "" : `${lines.join("\n")}\n`;
452
+ try {
453
+ mkdirSync(dirname(path), { recursive: true });
454
+ // 0600 at creation, and re-asserted after: `writeFileSync`'s mode argument
455
+ // is a request against the umask on creation and is ignored entirely for an
456
+ // existing file, so the explicit chmod is what actually holds the guarantee
457
+ // the reader depends on.
458
+ writeFileSync(path, body, { encoding: "utf8", mode: ENV_FILE_REQUIRED_MODE });
459
+ chmodSync(path, ENV_FILE_REQUIRED_MODE);
460
+ }
461
+ catch (cause) {
462
+ return refuse("env-file-io", path, `${path} could not be written: ${detail(cause)}`);
463
+ }
464
+ return { ok: true, path, created: !existing.present, changes };
465
+ }
466
+ /** One trailing newline, and nothing else, is removed. `security -w` adds one. */
467
+ function stripOneNewline(text) {
468
+ return text.endsWith("\n") ? text.slice(0, -1) : text;
469
+ }
470
+ /**
471
+ * `security find-generic-password -a "$USER" -s <service> -w` and
472
+ * `secret-tool lookup approval <label>`.
473
+ *
474
+ * THE VALUE IS NEVER IN AN ARGV, in either direction: the argv carries a service
475
+ * name or a label, and the secret comes back on stdout. An argv is world-readable
476
+ * in `ps` for the length of the call, which is the whole reason `approval vault
477
+ * set` has no `--value` flag either.
478
+ *
479
+ * The exit-status readings are documented heuristics, not contracts. `security`
480
+ * exits 44 (`errSecItemNotFound`) for a missing item; `secret-tool` exits 0 with
481
+ * empty output when the lookup matches nothing. Anything else is
482
+ * {@link "helper-failed"}, which is the honest answer for a locked keyring or a
483
+ * D-Bus that is not running: the repair is not "store the item".
484
+ */
485
+ /**
486
+ * The prefix a deferred lookup carries. See {@link NON_RESOLVING_RUNNER}.
487
+ */
488
+ export const KEYSTORE_DEFERRED = "not resolved by doctor";
489
+ /**
490
+ * A {@link SourceRunner} that looks nothing up (moved here by APRV-178).
491
+ *
492
+ * `security find-generic-password -w` can raise a keychain-unlock or ACL dialog
493
+ * and `secret-tool lookup` can block on a keyring prompt. Either would hang a
494
+ * command run over ssh or from CI, and a command that pops a keychain prompt
495
+ * also TEACHES people to click through keychain prompts. So the diagnostics —
496
+ * `approval doctor`, and `approval up`'s cross-instance report — resolve
497
+ * keystore-backed variables not at all: they report the scheme and the service
498
+ * name, which `.approval/env` already carries in the open, and leave the actual
499
+ * lookup to `approval env --check`, which a human runs deliberately and watches.
500
+ *
501
+ * It lives beside {@link defaultSourceRunner} rather than in one of its callers
502
+ * because two of them now need it and a second copy would be a second set of
503
+ * words for the same refusal.
504
+ */
505
+ export const NON_RESOLVING_RUNNER = {
506
+ keychain(service) {
507
+ return {
508
+ ok: false,
509
+ code: "helper-failed",
510
+ message: `${KEYSTORE_DEFERRED}: keychain:${service} is declared here and looked up by \`approval env --check\`. \`security find-generic-password -w\` can block on a keychain-unlock or ACL prompt, and a diagnostic must never hang or ask a human for a password`,
511
+ };
512
+ },
513
+ secretService(label) {
514
+ return {
515
+ ok: false,
516
+ code: "helper-failed",
517
+ message: `${KEYSTORE_DEFERRED}: secret-service:${label} is declared here and looked up by \`approval env --check\`. \`secret-tool lookup\` can block on a keyring-unlock prompt, and a diagnostic must never hang or ask a human for a password`,
518
+ };
519
+ },
520
+ };
521
+ export const defaultSourceRunner = {
522
+ keychain(service) {
523
+ const account = process.env["USER"] ?? userInfo().username;
524
+ const result = spawnSync("security", ["find-generic-password", "-a", account, "-s", service, "-w"], { encoding: "utf8" });
525
+ if (result.error !== undefined) {
526
+ const code = result.error.code;
527
+ if (code === "ENOENT") {
528
+ return {
529
+ ok: false,
530
+ code: "helper-binary-missing",
531
+ message: "`security` is not on PATH — keychain: sources are macOS-only; on Linux use secret-service:<label>",
532
+ };
533
+ }
534
+ return { ok: false, code: "helper-failed", message: `security could not be run: ${detail(result.error)}` };
535
+ }
536
+ if (result.status === 0) {
537
+ return { ok: true, value: stripOneNewline(result.stdout) };
538
+ }
539
+ if (result.status === 44) {
540
+ return {
541
+ ok: false,
542
+ code: "helper-item-missing",
543
+ message: `no generic password for service ${JSON.stringify(service)} and account ${JSON.stringify(account)} in the login keychain`,
544
+ };
545
+ }
546
+ return {
547
+ ok: false,
548
+ code: "helper-failed",
549
+ message: `security exited ${String(result.status)} looking up service ${JSON.stringify(service)}`,
550
+ };
551
+ },
552
+ secretService(label) {
553
+ const result = spawnSync("secret-tool", ["lookup", "approval", label], { encoding: "utf8" });
554
+ if (result.error !== undefined) {
555
+ const code = result.error.code;
556
+ if (code === "ENOENT") {
557
+ return {
558
+ ok: false,
559
+ code: "helper-binary-missing",
560
+ message: "`secret-tool` is not on PATH — install libsecret-tools (Debian/Ubuntu) or libsecret (Fedora/Arch); on macOS use keychain:<service>",
561
+ };
562
+ }
563
+ return {
564
+ ok: false,
565
+ code: "helper-failed",
566
+ message: `secret-tool could not be run: ${detail(result.error)}`,
567
+ };
568
+ }
569
+ if (result.status === 0) {
570
+ const value = stripOneNewline(result.stdout);
571
+ if (value.length === 0) {
572
+ return {
573
+ ok: false,
574
+ code: "helper-item-missing",
575
+ message: `no secret-service item with attribute approval=${JSON.stringify(label)}`,
576
+ };
577
+ }
578
+ return { ok: true, value };
579
+ }
580
+ if (result.status === 1) {
581
+ return {
582
+ ok: false,
583
+ code: "helper-item-missing",
584
+ message: `no secret-service item with attribute approval=${JSON.stringify(label)}`,
585
+ };
586
+ }
587
+ return {
588
+ ok: false,
589
+ code: "helper-failed",
590
+ message: `secret-tool exited ${String(result.status)} looking up ${JSON.stringify(label)}`,
591
+ };
592
+ },
593
+ };
594
+ /** The declared source of an entry, with a literal's value dropped. */
595
+ function declaredSourceOf(entry) {
596
+ return entry.kind === "keychain" || entry.kind === "secret-service"
597
+ ? { kind: entry.kind, service: entry.argument, line: entry.line }
598
+ : { kind: entry.kind, line: entry.line };
599
+ }
600
+ /** A declared source in words, and never a value. */
601
+ export function describeDeclaredSource(source) {
602
+ if (source.kind === "literal")
603
+ return LITERAL_SOURCE;
604
+ if (source.kind === "env")
605
+ return "env: (inherited)";
606
+ return `${source.kind}:${source.service ?? ""}`;
607
+ }
608
+ /** `approval setup <thing>`, or the generic advice for a policy-invented name. */
609
+ function setupFix(thing, name) {
610
+ return thing === null
611
+ ? `no setup verb knows this variable: export ${name}=… in your shell, or add a \`${name}=…\` line to .approval/env`
612
+ : `run \`approval setup ${thing}\` (APRV-74), or add a \`${name}=…\` line to .approval/env`;
613
+ }
614
+ /**
615
+ * Walk the loaded policy for every string-valued key whose name ends in `_env`.
616
+ *
617
+ * Depth-unbounded and key-name-driven on purpose. `_env` is this project's whole
618
+ * naming convention for "the policy carries a NAME" (§5.1 `token_env`,
619
+ * `chat_id_env`; §5.2 `sampling_secret_env`, `vault.passphrase_env`), and a
620
+ * future key that follows the convention should appear in `approval env` on the
621
+ * day it is added to the schema, not on the day someone remembers to add it
622
+ * here. The four known keys above are added by name first and win the dedupe, so
623
+ * this walk contributes only the ones nothing else knows about.
624
+ *
625
+ * Arrays are walked; non-string values under an `_env` key are skipped, because
626
+ * `token_env: 42` is a schema problem and not a variable name.
627
+ */
628
+ function walkEnvNames(node, found) {
629
+ if (Array.isArray(node)) {
630
+ for (const item of node)
631
+ walkEnvNames(item, found);
632
+ return;
633
+ }
634
+ if (node === null || typeof node !== "object")
635
+ return;
636
+ for (const [key, value] of Object.entries(node)) {
637
+ if (key.endsWith("_env") && typeof value === "string" && value.length > 0) {
638
+ found.set(value, true);
639
+ continue;
640
+ }
641
+ walkEnvNames(value, found);
642
+ }
643
+ }
644
+ /**
645
+ * The variables `approval env` answers for, in a stable order.
646
+ *
647
+ * Five by name — human identity, the Telegram token and chat id, the vault
648
+ * passphrase, and the sampling secret — plus whatever else the policy names by
649
+ * the `_env` convention.
650
+ *
651
+ * The sampling secret is the one conditional member: `audit.sampling_secret_env`
652
+ * has NO default (an unnamed one disables sampling, SPEC.md §5.2), so listing a
653
+ * made-up variable for it would invent configuration the operator never chose.
654
+ * The other four all have defaults and are always listed.
655
+ */
656
+ export function wantedVariables(load) {
657
+ const policy = load.ok ? load.policy : null;
658
+ const declaredTelegram = (key) => {
659
+ const telegram = policy?.channels?.["telegram"];
660
+ const value = telegram === undefined ? undefined : telegram[key];
661
+ return typeof value === "string" && value.length > 0;
662
+ };
663
+ const wanted = [
664
+ {
665
+ name: HUMAN_ACTOR_ENV,
666
+ declared: false,
667
+ secretBearing: false,
668
+ fix: setupFix("identity", HUMAN_ACTOR_ENV),
669
+ },
670
+ {
671
+ name: telegramTokenEnvFor(load),
672
+ declared: declaredTelegram("token_env"),
673
+ secretBearing: true,
674
+ fix: setupFix("channel telegram", telegramTokenEnvFor(load)),
675
+ },
676
+ {
677
+ name: telegramChatEnvFor(load),
678
+ declared: declaredTelegram("chat_id_env"),
679
+ secretBearing: false,
680
+ fix: setupFix("channel telegram", telegramChatEnvFor(load)),
681
+ },
682
+ {
683
+ name: passphraseEnvFor(load),
684
+ declared: typeof policy?.vault?.passphrase_env === "string" &&
685
+ policy.vault.passphrase_env.length > 0,
686
+ secretBearing: true,
687
+ fix: setupFix("vault", passphraseEnvFor(load)),
688
+ },
689
+ ];
690
+ const samplingEnv = policy?.audit?.sampling_secret_env;
691
+ if (typeof samplingEnv === "string" && samplingEnv.length > 0) {
692
+ wanted.push({
693
+ name: samplingEnv,
694
+ declared: true,
695
+ secretBearing: true,
696
+ fix: setupFix("sampling", samplingEnv),
697
+ });
698
+ }
699
+ const taken = new Set(wanted.map((entry) => entry.name));
700
+ const walked = new Map();
701
+ if (policy !== null)
702
+ walkEnvNames(policy, walked);
703
+ for (const name of [...walked.keys()].sort()) {
704
+ if (taken.has(name))
705
+ continue;
706
+ taken.add(name);
707
+ wanted.push({
708
+ // A name nothing in this build understands. Treated as secret-bearing,
709
+ // which is the stricter reading and the right default for a variable
710
+ // whose whole purpose, by the `_env` convention, is to hold a value the
711
+ // policy refused to write down.
712
+ name,
713
+ declared: true,
714
+ secretBearing: true,
715
+ fix: setupFix(null, name),
716
+ });
717
+ }
718
+ return wanted;
719
+ }
720
+ // ---------------------------------------------------------------------------
721
+ // Resolution
722
+ // ---------------------------------------------------------------------------
723
+ const LITERAL_SOURCE = "literal (plaintext in .approval/env)";
724
+ /**
725
+ * Resolve every variable the policy implies, against the ambient environment
726
+ * first and the source map second.
727
+ *
728
+ * **The ambient environment always wins.** A variable already exported in the
729
+ * calling shell is reported `set-in-environment` and its file entry is not even
730
+ * consulted: the human's shell is the authority (that is invariant 7's whole
731
+ * point), and a file that could override an exported value would be a file that
732
+ * silently redirects a gate operation's credentials.
733
+ *
734
+ * A whole-file refusal (bad mode, unreadable, unparseable) is returned as-is:
735
+ * partial resolution of a file the runtime cannot fully read is how a typo turns
736
+ * into a half-configured environment.
737
+ */
738
+ export function resolveEnvironment(load, envFilePath, runner = defaultSourceRunner, ambientEnv = process.env) {
739
+ const file = readEnvFile(envFilePath);
740
+ if (!file.ok)
741
+ return file;
742
+ const byKey = new Map();
743
+ for (const entry of file.entries)
744
+ byKey.set(entry.key, entry);
745
+ const variables = [];
746
+ for (const want of wantedVariables(load)) {
747
+ variables.push(resolveOne(want, byKey.get(want.name), runner, ambientEnv));
748
+ }
749
+ return { ok: true, present: file.present, path: file.path, digest: file.digest, variables };
750
+ }
751
+ function resolveOne(want, entry, runner, ambientEnv) {
752
+ const base = {
753
+ name: want.name,
754
+ declared: want.declared,
755
+ secretBearing: want.secretBearing,
756
+ ...(entry === undefined ? {} : { fileSource: declaredSourceOf(entry) }),
757
+ };
758
+ if (!SHELL_NAME_PATTERN.test(want.name)) {
759
+ return {
760
+ ...base,
761
+ status: "unset",
762
+ source: "unusable name",
763
+ plaintext: false,
764
+ fix: `the policy declares this as an environment variable NAME and it is not one; fix the _env key in APPROVAL.md`,
765
+ refusal: {
766
+ code: "invalid-variable-name",
767
+ message: `${JSON.stringify(want.name)} is not a usable shell variable name, so no export line is emitted for it (a name carrying a space or a ; would make a policy file executable)`,
768
+ },
769
+ };
770
+ }
771
+ const ambient = ambientEnv[want.name];
772
+ if (typeof ambient === "string" && ambient.length > 0) {
773
+ return {
774
+ ...base,
775
+ status: "set-in-environment",
776
+ value: ambient,
777
+ source: "the environment (already exported; the file was not consulted)",
778
+ plaintext: false,
779
+ };
780
+ }
781
+ if (entry === undefined) {
782
+ return { ...base, status: "unset", source: "unset", plaintext: false, fix: want.fix };
783
+ }
784
+ if (entry.kind === "literal") {
785
+ return {
786
+ ...base,
787
+ status: "resolved-literal",
788
+ value: entry.argument,
789
+ source: LITERAL_SOURCE,
790
+ plaintext: want.secretBearing,
791
+ };
792
+ }
793
+ if (entry.kind === "env") {
794
+ return {
795
+ ...base,
796
+ status: "unset",
797
+ source: "env: (inherited) — and it is not set in this environment",
798
+ plaintext: false,
799
+ fix: want.fix,
800
+ };
801
+ }
802
+ const outcome = entry.kind === "keychain"
803
+ ? runner.keychain(entry.argument)
804
+ : runner.secretService(entry.argument);
805
+ const label = `${entry.kind}:${entry.argument}`;
806
+ if (!outcome.ok) {
807
+ return {
808
+ ...base,
809
+ status: "unset",
810
+ source: label,
811
+ plaintext: false,
812
+ fix: want.fix,
813
+ refusal: { code: outcome.code, message: outcome.message },
814
+ };
815
+ }
816
+ if (outcome.value.length === 0) {
817
+ return {
818
+ ...base,
819
+ status: "unset",
820
+ source: label,
821
+ plaintext: false,
822
+ fix: want.fix,
823
+ refusal: {
824
+ code: "helper-item-missing",
825
+ message: `${label} resolved to an empty value, which is not a credential`,
826
+ },
827
+ };
828
+ }
829
+ return {
830
+ ...base,
831
+ status: entry.kind === "keychain" ? "resolved-from-keychain" : "resolved-from-secret-service",
832
+ value: outcome.value,
833
+ source: label,
834
+ plaintext: false,
835
+ };
836
+ }
837
+ //# sourceMappingURL=env-file.js.map