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,749 @@
1
+ /**
2
+ * The email adapter (SPEC.md §6.1, §6.2, §7, §10.4; APRV-69).
3
+ *
4
+ * SPEC.md §6.1 makes the point in its second sentence: "the canonical example is
5
+ * an email, deliberately. This spec governs agent actions in the world." So this
6
+ * is the first adapter, and the class it serves —
7
+ * `communicate.email.external` — is the one the canonical task envelope
8
+ * declares. It implements exactly one method, {@link Adapter.act}, and the
9
+ * contract in `adapters/contract.ts` owns everything around it: the hash
10
+ * recomputation, the token spend, `execution.started`, the credential window,
11
+ * the outcome event, and the redaction sweep. **Nothing in this file touches a
12
+ * token or the log**, and nothing in it can, because it is never handed either.
13
+ *
14
+ * ## The payload, and what the grant therefore approved
15
+ *
16
+ * ```ts
17
+ * {
18
+ * from: string, // one addr-spec
19
+ * to: string[], // at least one
20
+ * cc?: string[],
21
+ * bcc?: string[], // RCPT TO only; never a header
22
+ * subject: string,
23
+ * body: string,
24
+ * content_type?: "text/plain" | "text/html" // default "text/plain"
25
+ * }
26
+ * ```
27
+ *
28
+ * §6.2 says a message send's `payload_hash` covers "the full body and
29
+ * recipients", and this shape is that sentence made concrete. `bcc` is inside
30
+ * the hash even though it appears in no header: a blind recipient is still a
31
+ * recipient, and an approval that did not cover them would be an approval of a
32
+ * different act than the one performed.
33
+ *
34
+ * {@link validateEmailPayload} re-checks the shape inside `act` and refuses
35
+ * `email-payload-invalid`. This is defence in depth and is *not* the binding —
36
+ * the hash already bound the bytes before `act` was called. What it catches is a
37
+ * grant over bytes that are well-formed JSON and not a well-formed email, which
38
+ * is a human approving a mistake rather than an attacker substituting one.
39
+ * Unknown keys are refused too: a payload carrying `attachments` that this
40
+ * adapter silently dropped would send something other than what a human read.
41
+ *
42
+ * ## The two fields the grant does NOT bind
43
+ *
44
+ * A wire message needs a `Date` and a `Message-ID`, and neither can live in the
45
+ * payload without making the hash depend on when the send happened.
46
+ *
47
+ * - **`Date`** is stamped by the runtime from {@link EmailAdapterOptions.clock}
48
+ * (default: the wall clock) at the moment of the send. The grant binds the
49
+ * *message content*; the runtime records *when it went*. A human who approved
50
+ * a chaser on Tuesday and watched it execute on Wednesday approved the words,
51
+ * not the timestamp, and a `Date` inside the payload would additionally make
52
+ * every grant expire into a `payload-mismatch` the moment the clock moved.
53
+ * - **`Message-ID`** is derived, not random: SHA-256 over the action key and the
54
+ * payload hash, at the From address's domain. It is therefore reproducible
55
+ * from the log alone — an operator holding `action_key` and the grant's
56
+ * `payload_hash` can recompute the exact Message-ID the far side saw, which is
57
+ * what makes a delivery receipt or a bounce traceable back to an approval. A
58
+ * random one would have to be logged to be useful, and the log is not where an
59
+ * adapter writes.
60
+ *
61
+ * Both are stated in the module header rather than only in code because they are
62
+ * the two places this adapter puts bytes on the wire that no human approved.
63
+ *
64
+ * ## Encoding
65
+ *
66
+ * Quoted-printable, implemented here (RFC 2045 §6.7), rather than refusing
67
+ * non-ASCII. Refusing would mean an adapter whose canonical example — a chaser
68
+ * to a British letting agency about a £1,200 deposit — cannot be sent, and
69
+ * "your approval was fine, the runtime cannot spell your currency" is not a
70
+ * refusal any operator would accept. An all-ASCII body is sent `8bit`
71
+ * unchanged, so the common case is byte-for-byte what the human read.
72
+ *
73
+ * A non-ASCII `subject` is RFC 2047 `B` encoded-words. Addresses must be ASCII:
74
+ * an internationalized address needs SMTPUTF8 negotiation (RFC 6531), this
75
+ * client does not implement it, and encoding one anyway would hand the server an
76
+ * address it may or may not interpret as intended. That is
77
+ * `email-payload-invalid` with the rule quoted.
78
+ *
79
+ * ## Configuration comes from the vault
80
+ *
81
+ * All five settings — host, port, security, user, password — are read through
82
+ * {@link ActInput.credentials} inside `act`, under the names in
83
+ * {@link DEFAULT_CREDENTIAL_NAMES}. Host and port are not secrets, and reading
84
+ * them from the vault anyway buys one thing worth having: there is exactly one
85
+ * place an adapter reads deployment configuration from, and it is not the
86
+ * environment, not a config file an agent can write, and not the policy an agent
87
+ * can read. A future task may let policy supply host/port (they are the sort of
88
+ * thing an operator would like to see in `APPROVAL.md`), at which point the
89
+ * vault stays the source for the two that are actually secret; that is a SPEC
90
+ * §5.2 change and therefore its own task, not a flag here.
91
+ *
92
+ * The same five are also DECLARED, as {@link EMAIL_CREDENTIAL_SPECS}, so that
93
+ * `approval setup adapter email` can ask for them without knowing what SMTP is
94
+ * (APRV-78). The manifest is derived from {@link DEFAULT_CREDENTIAL_NAMES} and
95
+ * its validation borrows `act`'s own refusal sentences, so a value setup accepts
96
+ * is a value `act` accepts, and the words are the same on both paths.
97
+ *
98
+ * Deterministic given its clock: no randomness anywhere, no environment reads,
99
+ * and `act` never throws (every path returns an {@link ActOutcome}).
100
+ */
101
+ import { createHash } from "node:crypto";
102
+ import { payloadHash } from "../core/payload.js";
103
+ import { CREDENTIAL_REFUSAL_CODES, redactSecrets, } from "./contract.js";
104
+ import { DEFAULT_SMTP_TIMEOUT_MS, SMTP_TRANSPORT_FAILURE_CODES, isSmtpSecurity, sendMail, } from "./smtp.js";
105
+ /** Every key the payload may carry. Anything else is refused. */
106
+ const PAYLOAD_KEYS = ["from", "to", "cc", "bcc", "subject", "body", "content_type"];
107
+ /**
108
+ * A deliberately conservative addr-spec: `local@domain`, ASCII, no display
109
+ * name, no angle brackets, no comments, no quoted local part.
110
+ *
111
+ * RFC 5322's real grammar admits far more than this, and every extra form is
112
+ * another way for a string to mean one thing to a reader and another to a
113
+ * server. `"alice@example.com" <attacker@evil.example>` is a valid mailbox
114
+ * whose display name is an address, and a human skimming a rendered approval
115
+ * would read the wrong one. So the adapter accepts the boring form only, and an
116
+ * operator who needs a display name gets a refusal explaining why rather than a
117
+ * message whose recipient nobody can be sure of.
118
+ */
119
+ const ADDR_SPEC = /^[A-Za-z0-9!#$%&'*+/=?^_`{|}~.-]+@[A-Za-z0-9](?:[A-Za-z0-9.-]*[A-Za-z0-9])?$/u;
120
+ /** ASCII only, and specifically no CR or LF: header injection's whole trick. */
121
+ function isCleanAscii(value) {
122
+ return /^[ -~]*$/u.test(value);
123
+ }
124
+ function badAddresses(field, values) {
125
+ for (const value of values) {
126
+ if (typeof value !== "string" || !ADDR_SPEC.test(value) || !isCleanAscii(value)) {
127
+ return `${field} must hold plain ASCII addresses of the form local@domain (no display name, no angle brackets, no internationalized addresses — this client does not negotiate SMTPUTF8, RFC 6531); ${JSON.stringify(String(value))} is not one`;
128
+ }
129
+ }
130
+ return null;
131
+ }
132
+ /**
133
+ * Structural validation. Never throws; returns the reason instead.
134
+ *
135
+ * Exported so a caller can check a payload *before* requesting approval for it,
136
+ * which is the only place a shape error can still be fixed cheaply.
137
+ */
138
+ export function validateEmailPayload(value) {
139
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
140
+ return { ok: false, message: "the payload must be a JSON object" };
141
+ }
142
+ const record = value;
143
+ const unknown = Object.keys(record).filter((key) => !PAYLOAD_KEYS.includes(key));
144
+ if (unknown.length > 0) {
145
+ return {
146
+ ok: false,
147
+ message: `the payload carries ${unknown.map((key) => JSON.stringify(key)).join(", ")}, which this adapter does not implement. An unknown key is refused rather than ignored: a human approved every byte of this payload, and sending a message that silently dropped one of them would send something other than what they read. Supported keys: ${PAYLOAD_KEYS.join(", ")}`,
148
+ };
149
+ }
150
+ const from = record["from"];
151
+ if (typeof from !== "string" || from.length === 0) {
152
+ return { ok: false, message: "from must be a non-empty string" };
153
+ }
154
+ const fromBad = badAddresses("from", [from]);
155
+ if (fromBad !== null)
156
+ return { ok: false, message: fromBad };
157
+ const to = record["to"];
158
+ if (!Array.isArray(to) || to.length === 0) {
159
+ return { ok: false, message: "to must be a non-empty array of addresses" };
160
+ }
161
+ const toBad = badAddresses("to", to);
162
+ if (toBad !== null)
163
+ return { ok: false, message: toBad };
164
+ const lists = {};
165
+ for (const field of ["cc", "bcc"]) {
166
+ const list = record[field];
167
+ if (list === undefined)
168
+ continue;
169
+ if (!Array.isArray(list)) {
170
+ return { ok: false, message: `${field}, when present, must be an array of addresses` };
171
+ }
172
+ const bad = badAddresses(field, list);
173
+ if (bad !== null)
174
+ return { ok: false, message: bad };
175
+ lists[field] = list;
176
+ }
177
+ const subject = record["subject"];
178
+ if (typeof subject !== "string")
179
+ return { ok: false, message: "subject must be a string" };
180
+ if (/[\r\n]/u.test(subject)) {
181
+ return {
182
+ ok: false,
183
+ message: "subject must not contain CR or LF: a newline in a header is how an extra header (a second Bcc, a different From) is smuggled into a message a human approved",
184
+ };
185
+ }
186
+ const body = record["body"];
187
+ if (typeof body !== "string")
188
+ return { ok: false, message: "body must be a string" };
189
+ const contentType = record["content_type"];
190
+ if (contentType !== undefined &&
191
+ contentType !== "text/plain" &&
192
+ contentType !== "text/html") {
193
+ return {
194
+ ok: false,
195
+ message: `content_type, when present, must be "text/plain" or "text/html", got ${JSON.stringify(contentType)}`,
196
+ };
197
+ }
198
+ const recipients = [...to, ...(lists.cc ?? []), ...(lists.bcc ?? [])];
199
+ if (recipients.length === 0) {
200
+ return { ok: false, message: "the payload names no recipients" };
201
+ }
202
+ return {
203
+ ok: true,
204
+ payload: {
205
+ from,
206
+ to: to,
207
+ ...(lists.cc === undefined ? {} : { cc: lists.cc }),
208
+ ...(lists.bcc === undefined ? {} : { bcc: lists.bcc }),
209
+ subject,
210
+ body,
211
+ ...(contentType === undefined ? {} : { content_type: contentType }),
212
+ },
213
+ };
214
+ }
215
+ /** To, then Cc, then Bcc: every address the envelope will name, in order. */
216
+ export function envelopeRecipients(payload) {
217
+ return [...payload.to, ...(payload.cc ?? []), ...(payload.bcc ?? [])];
218
+ }
219
+ // ---------------------------------------------------------------------------
220
+ // Rendering (RFC 5322, RFC 2045, RFC 2047)
221
+ // ---------------------------------------------------------------------------
222
+ const CRLF = "\r\n";
223
+ const MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
224
+ const DAYS = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
225
+ /** RFC 5322 §3.3, in UTC. `Tue, 18 Aug 2026 09:14:02 +0000`. */
226
+ export function rfc5322Date(when) {
227
+ const pad = (value) => String(value).padStart(2, "0");
228
+ return [
229
+ `${DAYS[when.getUTCDay()] ?? "Mon"},`,
230
+ pad(when.getUTCDate()),
231
+ MONTHS[when.getUTCMonth()] ?? "Jan",
232
+ String(when.getUTCFullYear()),
233
+ `${pad(when.getUTCHours())}:${pad(when.getUTCMinutes())}:${pad(when.getUTCSeconds())}`,
234
+ "+0000",
235
+ ].join(" ");
236
+ }
237
+ /**
238
+ * The deterministic Message-ID: SHA-256 over a domain-separated tuple of the
239
+ * action key and the payload hash, at the sender's own domain.
240
+ *
241
+ * Domain separation (the literal prefix) so this digest can never collide with
242
+ * a payload hash computed over the same strings by something else. Truncated to
243
+ * 40 hex characters, which keeps the header inside a sensible width and leaves
244
+ * 160 bits — the value needs to be unique, not unforgeable, since forging it
245
+ * accomplishes nothing an attacker who can already send mail could not do.
246
+ */
247
+ export function deterministicMessageId(actionKey, hash, from) {
248
+ const digest = createHash("sha256")
249
+ .update(`approval.md/message-id\n${actionKey}\n${hash}`, "utf8")
250
+ .digest("hex")
251
+ .slice(0, 40);
252
+ const domain = from.slice(from.lastIndexOf("@") + 1);
253
+ return `<${digest}@${domain}>`;
254
+ }
255
+ /** Does `text` need an encoding that is not plain ASCII? */
256
+ function isAsciiOnly(text) {
257
+ for (const character of text) {
258
+ if (character.codePointAt(0) !== undefined && character.codePointAt(0) > 0x7f) {
259
+ return false;
260
+ }
261
+ }
262
+ return true;
263
+ }
264
+ function hex(byte) {
265
+ return `=${byte.toString(16).toUpperCase().padStart(2, "0")}`;
266
+ }
267
+ /**
268
+ * Quoted-printable (RFC 2045 §6.7).
269
+ *
270
+ * Line endings are normalized to CRLF hard breaks first, and each resulting
271
+ * line is encoded and then soft-wrapped at 76 characters with a trailing `=`.
272
+ * Trailing space and tab are always encoded, because a mail server that strips
273
+ * trailing whitespace would otherwise silently alter bytes a human approved.
274
+ */
275
+ export function quotedPrintable(text) {
276
+ const lines = text.replace(/\r\n/gu, "\n").replace(/\r/gu, "\n").split("\n");
277
+ const out = [];
278
+ for (const line of lines) {
279
+ const bytes = Buffer.from(line, "utf8");
280
+ let encoded = "";
281
+ for (let index = 0; index < bytes.length; index += 1) {
282
+ const byte = bytes[index];
283
+ const last = index === bytes.length - 1;
284
+ if (byte === 0x20 || byte === 0x09)
285
+ encoded += last ? hex(byte) : String.fromCharCode(byte);
286
+ else if (byte === 0x3d)
287
+ encoded += "=3D";
288
+ else if (byte >= 0x21 && byte <= 0x7e)
289
+ encoded += String.fromCharCode(byte);
290
+ else
291
+ encoded += hex(byte);
292
+ }
293
+ out.push(...softWrap(encoded));
294
+ }
295
+ return out.join(CRLF);
296
+ }
297
+ /** Break an encoded line at 76 characters, never splitting an `=XX` triplet. */
298
+ function softWrap(encoded) {
299
+ if (encoded.length <= 76)
300
+ return [encoded];
301
+ const pieces = [];
302
+ let rest = encoded;
303
+ while (rest.length > 76) {
304
+ let cut = 75;
305
+ // An `=` within the last two characters would be the head of a triplet.
306
+ while (cut > 1 && rest.slice(Math.max(0, cut - 2), cut).includes("="))
307
+ cut -= 1;
308
+ pieces.push(`${rest.slice(0, cut)}=`);
309
+ rest = rest.slice(cut);
310
+ }
311
+ pieces.push(rest);
312
+ return pieces;
313
+ }
314
+ /**
315
+ * RFC 2047 `B` encoded-words for a non-ASCII header value, chunked so no
316
+ * encoded-word exceeds 75 characters and no chunk splits a code point.
317
+ */
318
+ export function encodeHeaderValue(value) {
319
+ if (isAsciiOnly(value))
320
+ return value;
321
+ const prefix = "=?UTF-8?B?";
322
+ const suffix = "?=";
323
+ // 75 total, minus the delimiters, rounded down to a whole base64 quantum
324
+ // (4 characters, which is 3 source bytes).
325
+ const budget = 75 - prefix.length - suffix.length;
326
+ const perChunk = Math.floor(budget / 4) * 3;
327
+ const words = [];
328
+ let chunk = Buffer.alloc(0);
329
+ for (const character of value) {
330
+ const bytes = Buffer.from(character, "utf8");
331
+ if (chunk.length + bytes.length > perChunk) {
332
+ words.push(`${prefix}${chunk.toString("base64")}${suffix}`);
333
+ chunk = Buffer.alloc(0);
334
+ }
335
+ chunk = Buffer.concat([chunk, bytes]);
336
+ }
337
+ if (chunk.length > 0)
338
+ words.push(`${prefix}${chunk.toString("base64")}${suffix}`);
339
+ // A folded continuation: CRLF plus one space, which RFC 5322 §2.2.3 requires
340
+ // and RFC 2047 §2 relies on to keep adjacent encoded-words concatenated.
341
+ return words.join(`${CRLF} `);
342
+ }
343
+ /** `Name: value`, folded at commas when the line would run past 78. */
344
+ function header(name, value) {
345
+ const line = `${name}: ${value}`;
346
+ if (line.length <= 78 || !value.includes(", "))
347
+ return line;
348
+ const parts = value.split(", ");
349
+ const folded = [];
350
+ let current = `${name}:`;
351
+ for (const [index, part] of parts.entries()) {
352
+ const piece = index === parts.length - 1 ? part : `${part},`;
353
+ if (`${current} ${piece}`.length > 78) {
354
+ folded.push(current);
355
+ current = ` ${piece}`;
356
+ }
357
+ else
358
+ current = `${current} ${piece}`;
359
+ }
360
+ folded.push(current);
361
+ return folded.join(CRLF);
362
+ }
363
+ /**
364
+ * The complete RFC 5322 message, CRLF throughout, **not** dot-stuffed: stuffing
365
+ * is RFC 5321 transport framing and belongs to the SMTP layer, so what this
366
+ * function returns is exactly the message the recipient's server stores.
367
+ *
368
+ * Bcc appears nowhere in it. That is the whole meaning of blind: the address is
369
+ * in the envelope (`RCPT TO`) and in the payload a human approved, and in no
370
+ * byte the other recipients receive.
371
+ */
372
+ export function renderEmailMessage(payload, stamp) {
373
+ const contentType = payload.content_type ?? "text/plain";
374
+ const asciiBody = isAsciiOnly(payload.body);
375
+ const body = asciiBody
376
+ ? payload.body.replace(/\r\n/gu, "\n").replace(/\r/gu, "\n").split("\n").join(CRLF)
377
+ : quotedPrintable(payload.body);
378
+ const headers = [
379
+ header("Date", rfc5322Date(stamp.date)),
380
+ header("From", payload.from),
381
+ header("To", payload.to.join(", ")),
382
+ ...(payload.cc === undefined || payload.cc.length === 0
383
+ ? []
384
+ : [header("Cc", payload.cc.join(", "))]),
385
+ header("Subject", encodeHeaderValue(payload.subject)),
386
+ header("Message-ID", stamp.messageId),
387
+ header("MIME-Version", "1.0"),
388
+ header("Content-Type", `${contentType}; charset=utf-8`),
389
+ header("Content-Transfer-Encoding", asciiBody ? "8bit" : "quoted-printable"),
390
+ ];
391
+ return `${headers.join(CRLF)}${CRLF}${CRLF}${body}${CRLF}`;
392
+ }
393
+ // ---------------------------------------------------------------------------
394
+ // The adapter
395
+ // ---------------------------------------------------------------------------
396
+ /** The class SPEC.md §6.1's canonical envelope declares. */
397
+ export const EMAIL_CLASS = "communicate.email.external";
398
+ /** The vault names this adapter reads, all of them inside `act`. */
399
+ export const DEFAULT_CREDENTIAL_NAMES = {
400
+ host: "smtp.host",
401
+ port: "smtp.port",
402
+ user: "smtp.user",
403
+ password: "smtp.password",
404
+ security: "smtp.security",
405
+ };
406
+ // ---------------------------------------------------------------------------
407
+ // The credential manifest (APRV-78)
408
+ // ---------------------------------------------------------------------------
409
+ /**
410
+ * The refusal sentences, written once and used twice.
411
+ *
412
+ * `act` refuses a bad port or a bad security setting at send time; `approval
413
+ * setup adapter email` refuses the same values at collection time. They must
414
+ * say the same thing in the same words, or an operator learns at 2am that the
415
+ * port they were allowed to type six weeks earlier was never a port. Each
416
+ * sentence takes the NAME because `credentialNames` may rename any of them.
417
+ */
418
+ function portSentence(name) {
419
+ return `the vault's ${name} is not a TCP port number (1-65535)`;
420
+ }
421
+ function securitySentence(name) {
422
+ return `the vault's ${name} must be "implicit", "starttls" or "none"; it is none of those, and this adapter will not guess a transport security setting`;
423
+ }
424
+ /** The both-or-neither sentence. `held` is the one present; `missing` is not. */
425
+ function pairSentence(held, missing) {
426
+ return `the vault holds ${held} but not ${missing}. An SMTP login needs both; sending unauthenticated because half the credential is missing would put the message on a path nobody configured`;
427
+ }
428
+ /**
429
+ * What this adapter reads from the vault, declared rather than discovered.
430
+ *
431
+ * DERIVED from {@link DEFAULT_CREDENTIAL_NAMES} rather than restating the five
432
+ * strings: a manifest that could drift from the names `act` asks for would be a
433
+ * setup wizard that fills a vault the adapter then cannot read, and
434
+ * `tests/adapter-email.test.ts` pins the two key for key.
435
+ *
436
+ * Order is the order an operator is asked, and it is not alphabetical: the
437
+ * password is last, so that everything a mistyped host or port can waste is
438
+ * answered before the one value that is unpleasant to re-enter.
439
+ */
440
+ export const EMAIL_CREDENTIAL_SPECS = [
441
+ {
442
+ name: DEFAULT_CREDENTIAL_NAMES.host,
443
+ kind: "config",
444
+ label: "SMTP host",
445
+ describe: "the submission server this runtime connects to",
446
+ required: true,
447
+ validate(value) {
448
+ const trimmed = value.trim();
449
+ if (trimmed.length === 0)
450
+ return { ok: false, message: "the host is empty" };
451
+ if (/\s/u.test(trimmed)) {
452
+ return { ok: false, message: "a host name contains no whitespace" };
453
+ }
454
+ return { ok: true };
455
+ },
456
+ },
457
+ {
458
+ name: DEFAULT_CREDENTIAL_NAMES.port,
459
+ kind: "config",
460
+ label: "SMTP port",
461
+ describe: "the TCP port: 587 for STARTTLS submission, 465 for implicit TLS",
462
+ required: true,
463
+ default: "587",
464
+ validate(value) {
465
+ const port = Number(value.trim());
466
+ if (!Number.isInteger(port) || port < 1 || port > 65_535) {
467
+ return { ok: false, message: portSentence(DEFAULT_CREDENTIAL_NAMES.port) };
468
+ }
469
+ return { ok: true };
470
+ },
471
+ },
472
+ {
473
+ name: DEFAULT_CREDENTIAL_NAMES.security,
474
+ kind: "choice",
475
+ label: "transport security",
476
+ describe: "how the connection is protected; this adapter never guesses it",
477
+ required: true,
478
+ default: "starttls",
479
+ choices: [
480
+ { value: "implicit", describe: "TLS from the first byte (the submissions port, 465)" },
481
+ { value: "starttls", describe: "plaintext, then a mandatory STARTTLS upgrade (port 587)" },
482
+ { value: "none", describe: "plaintext throughout; the adapter refuses to AUTH over it" },
483
+ ],
484
+ validate(value) {
485
+ if (!isSmtpSecurity(value.trim())) {
486
+ return { ok: false, message: securitySentence(DEFAULT_CREDENTIAL_NAMES.security) };
487
+ }
488
+ return { ok: true };
489
+ },
490
+ },
491
+ {
492
+ name: DEFAULT_CREDENTIAL_NAMES.user,
493
+ kind: "config",
494
+ label: "SMTP username",
495
+ describe: "the login name, when the relay wants one; leave empty for a relay that does not",
496
+ required: false,
497
+ },
498
+ {
499
+ name: DEFAULT_CREDENTIAL_NAMES.password,
500
+ kind: "secret",
501
+ label: "SMTP password",
502
+ describe: "the login secret, required exactly when a username is given",
503
+ required: false,
504
+ },
505
+ ];
506
+ /**
507
+ * The names this adapter cannot act without, for the contract's pre-token
508
+ * resolution (APRV-169).
509
+ *
510
+ * DERIVED from {@link EMAIL_CREDENTIAL_SPECS}, which already states which values
511
+ * are required, and mapped through `names` so a deployment that renamed one gets
512
+ * the name it actually stored. The login pair is deliberately absent: a relay
513
+ * that wants no login is a supported configuration, and listing `smtp.user`
514
+ * here would turn an optional value into a precondition and refuse a
515
+ * deployment that works today.
516
+ */
517
+ export function requiredEmailCredentials(names = DEFAULT_CREDENTIAL_NAMES) {
518
+ const keyOfDefault = new Map(Object.entries(DEFAULT_CREDENTIAL_NAMES).map(([key, value]) => [
519
+ value,
520
+ key,
521
+ ]));
522
+ return EMAIL_CREDENTIAL_SPECS.filter((spec) => spec.required === true).map((spec) => {
523
+ const key = keyOfDefault.get(spec.name);
524
+ return key === undefined ? spec.name : names[key];
525
+ });
526
+ }
527
+ /**
528
+ * The cross-field rule: a username and a password are both-or-neither.
529
+ *
530
+ * Returns the refusal sentence, or `null` when the set is coherent. Exported so
531
+ * that `approval setup adapter email` refuses the half-configured pair at the
532
+ * moment the operator could still fix it, saying exactly what `act` would have
533
+ * said at send time — and `act` itself calls this, so there is one sentence and
534
+ * not two that drift.
535
+ *
536
+ * An absent value and an empty one are the same thing here: `setCredential`
537
+ * refuses an empty credential outright, so "" can only ever mean "not given".
538
+ */
539
+ export function checkEmailCredentialSet(values, names = DEFAULT_CREDENTIAL_NAMES, kept = []) {
540
+ // A name the operator kept from an earlier run is present in the vault even
541
+ // though this run never saw its value (APRV-98). Presence is all the pair
542
+ // rule asks about.
543
+ const has = (name) => {
544
+ if (kept.includes(name))
545
+ return true;
546
+ const value = values[name];
547
+ return typeof value === "string" && value.length > 0;
548
+ };
549
+ const user = has(names.user);
550
+ const password = has(names.password);
551
+ if (user === password)
552
+ return null;
553
+ return user ? pairSentence(names.user, names.password) : pairSentence(names.password, names.user);
554
+ }
555
+ /**
556
+ * Everything this adapter can report. Frozen union, additive only
557
+ * (SPEC.md §11.1(6)), and note the three families it is a union OF:
558
+ *
559
+ * - the adapter's own two (`email-payload-invalid`, `email-config-invalid`);
560
+ * - the credential refusals, passed through verbatim from the provider, because
561
+ * "the vault is locked" and "nobody stored an SMTP password" are two different
562
+ * repairs and collapsing them into one adapter code would hide which; and
563
+ * - the SMTP transport codes.
564
+ *
565
+ * A server's own refusal produces `smtp-<NNN>`, which is a family rather than a
566
+ * member (see `smtp.ts`); {@link isEmailFailureCode} accepts both.
567
+ */
568
+ export const EMAIL_FAILURE_CODES = [
569
+ /** The payload is not a well-formed email. Nothing was connected to. */
570
+ "email-payload-invalid",
571
+ /** The vault answered, and what it holds is not usable configuration. */
572
+ "email-config-invalid",
573
+ ...CREDENTIAL_REFUSAL_CODES,
574
+ ...SMTP_TRANSPORT_FAILURE_CODES,
575
+ ];
576
+ export function isEmailFailureCode(value) {
577
+ return (EMAIL_FAILURE_CODES.includes(value) || /^smtp-[1-5]\d\d$/u.test(value));
578
+ }
579
+ function readCredential(credentials, name) {
580
+ const got = credentials.get(name);
581
+ if (got.ok)
582
+ return { ok: true, value: got.value };
583
+ return {
584
+ ok: false,
585
+ code: got.code,
586
+ message: `the email adapter needs the credential ${JSON.stringify(name)}: ${got.message}`,
587
+ absent: got.code === "credential-unavailable",
588
+ };
589
+ }
590
+ /**
591
+ * Read this adapter's whole configuration from a credential provider.
592
+ *
593
+ * Extracted from `act` (APRV-99) so that there is exactly ONE piece of code in
594
+ * the repository that turns a {@link CredentialProvider} into SMTP settings: the
595
+ * names it asks for, the order it asks in, the port and security validation,
596
+ * and the both-or-neither pair rule. `act` calls it inside the verified-token
597
+ * window, and `setup adapter email` calls it to probe a configuration it only
598
+ * partly typed, over a provider built the same way. Neither one re-derives the
599
+ * rules, and a second reader would be a second opinion about what "configured"
600
+ * means.
601
+ *
602
+ * Values never leave the process on either path: what comes back is the
603
+ * configuration a session is opened with, and every string this function
604
+ * RETURNS as a message has been through the scrub built from what it read.
605
+ */
606
+ export function readEmailSmtpConfig(credentials, names = DEFAULT_CREDENTIAL_NAMES) {
607
+ const secrets = [];
608
+ const scrub = (text) => redactSecrets(text, secrets).text;
609
+ const read = (name) => {
610
+ const got = readCredential(credentials, name);
611
+ if (got.ok && got.value.length > 0)
612
+ secrets.push(got.value);
613
+ return got;
614
+ };
615
+ const fail = (code, message) => ({
616
+ ok: false,
617
+ code,
618
+ message,
619
+ secrets,
620
+ });
621
+ const host = read(names.host);
622
+ if (!host.ok)
623
+ return fail(host.code, scrub(host.message));
624
+ const port = read(names.port);
625
+ if (!port.ok)
626
+ return fail(port.code, scrub(port.message));
627
+ const security = read(names.security);
628
+ if (!security.ok)
629
+ return fail(security.code, scrub(security.message));
630
+ const portNumber = Number(port.value);
631
+ if (!Number.isInteger(portNumber) || portNumber < 1 || portNumber > 65_535) {
632
+ return fail("email-config-invalid", portSentence(names.port));
633
+ }
634
+ if (!isSmtpSecurity(security.value)) {
635
+ return fail("email-config-invalid", securitySentence(names.security));
636
+ }
637
+ // The credential pair. Both absent is a relay that needs no login; exactly
638
+ // one absent is a half-configured deployment, and sending unauthenticated
639
+ // because the password happens to be missing is how a message goes out over a
640
+ // path nobody intended.
641
+ const user = read(names.user);
642
+ const password = read(names.password);
643
+ if (!user.ok && !user.absent)
644
+ return fail(user.code, scrub(user.message));
645
+ if (!password.ok && !password.absent)
646
+ return fail(password.code, scrub(password.message));
647
+ const pairProblem = checkEmailCredentialSet({
648
+ [names.user]: user.ok ? user.value : undefined,
649
+ [names.password]: password.ok ? password.value : undefined,
650
+ }, names);
651
+ if (pairProblem !== null)
652
+ return fail("email-config-invalid", pairProblem);
653
+ return {
654
+ ok: true,
655
+ secrets,
656
+ config: {
657
+ host: host.value,
658
+ port: portNumber,
659
+ security: security.value,
660
+ ...(user.ok ? { user: user.value } : {}),
661
+ ...(password.ok ? { password: password.value } : {}),
662
+ },
663
+ };
664
+ }
665
+ /**
666
+ * A fresh email adapter.
667
+ *
668
+ * Stateless and reusable: it holds no socket, no connection pool, and nothing
669
+ * from a previous send. Two concurrent executions through the same instance
670
+ * share nothing.
671
+ */
672
+ export function emailAdapter(options = {}) {
673
+ const names = { ...DEFAULT_CREDENTIAL_NAMES, ...options.credentialNames };
674
+ const classes = [EMAIL_CLASS, ...(options.classes ?? []).filter((cls) => cls !== EMAIL_CLASS)];
675
+ const clock = options.clock ?? (() => new Date());
676
+ return {
677
+ name: "email",
678
+ classes,
679
+ requiredCredentials: requiredEmailCredentials(names),
680
+ async act(input) {
681
+ // (1) The payload. Refused before any credential is read and long before
682
+ // any socket is opened: a malformed payload is not a reason to touch
683
+ // the vault.
684
+ const validated = validateEmailPayload(input.payload);
685
+ if (!validated.ok) {
686
+ return {
687
+ ok: false,
688
+ code: "email-payload-invalid",
689
+ message: `the approved payload for ${input.actionKey} is not a well-formed email: ${validated.message}. Nothing was connected to and nothing was sent`,
690
+ };
691
+ }
692
+ const payload = validated.payload;
693
+ // (2) The configuration and the credential pair, from the vault, inside
694
+ // the window — through {@link readEmailSmtpConfig}, which is the only
695
+ // code in this repository that turns a provider into SMTP settings.
696
+ // Every value it read joins the local redaction corpus, so a
697
+ // diagnostic this adapter builds is scrubbed before the contract
698
+ // scrubs it again.
699
+ const configured = readEmailSmtpConfig(input.credentials, names);
700
+ const scrub = (text) => redactSecrets(text, configured.secrets).text;
701
+ if (!configured.ok) {
702
+ // Already scrubbed with everything that had been read when it failed.
703
+ return { ok: false, code: configured.code, message: configured.message };
704
+ }
705
+ const { host, port: portNumber, security: transport } = configured.config;
706
+ const { user, password } = configured.config;
707
+ // (4) The two stamped fields, and the message.
708
+ const hash = payloadHash(payload);
709
+ const stamp = {
710
+ date: clock(),
711
+ messageId: deterministicMessageId(input.actionKey, hash, payload.from),
712
+ };
713
+ const message = renderEmailMessage(payload, stamp);
714
+ const recipients = envelopeRecipients(payload);
715
+ // (5) The send.
716
+ const sent = await sendMail({
717
+ host,
718
+ port: portNumber,
719
+ security: transport,
720
+ ...(user === undefined ? {} : { user }),
721
+ ...(password === undefined ? {} : { password }),
722
+ timeoutMs: options.timeoutMs ?? DEFAULT_SMTP_TIMEOUT_MS,
723
+ ...(options.clientName === undefined ? {} : { clientName: options.clientName }),
724
+ tlsRejectUnauthorized: options.tlsRejectUnauthorized ?? true,
725
+ redact: scrub,
726
+ }, { from: payload.from, recipients }, message);
727
+ if (!sent.ok) {
728
+ return { ok: false, code: sent.code, message: scrub(sent.message) };
729
+ }
730
+ // The detail is the receipt: what went where, and nothing about how the
731
+ // session authenticated beyond the mechanism's name.
732
+ return {
733
+ ok: true,
734
+ detail: {
735
+ message_id: stamp.messageId,
736
+ payload_hash: hash,
737
+ date: stamp.date.toISOString(),
738
+ recipients: recipients.length,
739
+ bytes: Buffer.byteLength(message, "utf8"),
740
+ secure: sent.secure,
741
+ auth: sent.authenticated,
742
+ smtp_code: sent.reply.code,
743
+ transcript: sent.transcript,
744
+ },
745
+ };
746
+ },
747
+ };
748
+ }
749
+ //# sourceMappingURL=email.js.map