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,635 @@
1
+ /**
2
+ * `approval setup channel <name>` — configure one channel (APRV-79).
3
+ *
4
+ * SPEC.md §4 draws a line this verb's NAME now carries: a **channel** surfaces
5
+ * requests and collects decisions and holds no state; an **adapter** executes
6
+ * side effects and holds credentials. `approval setup adapter <name>` (APRV-78)
7
+ * fills the VAULT from an adapter's manifest. This verb fills the OS keystore
8
+ * and `.approval/env` from a channel's, because what a channel needs is not a
9
+ * credential an action spends inside a token window: it is the transport
10
+ * credential that lets the runtime ask a human anything at all.
11
+ *
12
+ * An older build spelled the Telegram one without the `channel` noun. That form
13
+ * is gone rather than aliased — the dispatch in `cli/setup.ts` answers it at
14
+ * exit 2 with the new one, in {@link RENAMED_NOTICE} — because two spellings of
15
+ * a distinction the SPEC draws on purpose is exactly how the distinction stops
16
+ * being drawn.
17
+ *
18
+ * ## What Telegram's flow keeps, unchanged
19
+ *
20
+ * The conversation is now `cli/setup-flow.ts`'s, the same one `setup adapter`
21
+ * runs, over the manifest `core/telegram-config.ts` declares. Everything that
22
+ * was a decision rather than a phrasing survives it, and each of these is
23
+ * asserted in `tests/cli-setup.test.ts`:
24
+ *
25
+ * - **`getMe` happens before anything is written.** It runs at the END of the
26
+ * token's collection (step five of the flow), not after the write (step
27
+ * seven), so a token the Bot API refuses costs the operator one line of
28
+ * output and leaves `.approval/env` untouched. The chat discovery below it
29
+ * also needs the bot's username, and asking `getMe` for it is the same call.
30
+ * - **No `getUpdates` from this verb carries an offset, ever.** An offset is an
31
+ * ACKNOWLEDGEMENT: it tells the Bot API that everything below it may be
32
+ * discarded, and a decision tap consumed here would never reach the listener
33
+ * that was waiting for it. `allowed_updates` is `["message"]`, so a pending
34
+ * `callback_query` is not even delivered to this process.
35
+ * - **The token is never typed into this process** on a machine with a
36
+ * keystore: the helper's own no-echo prompt collects it and this runtime
37
+ * learns it by reading the item back on stdout.
38
+ * - **The chat id is a literal.** A chat id is not a secret; the token is.
39
+ * - "send the bot a message", the 409 hint, the manual-curl refusal when nothing
40
+ * arrives, and the optional send-a-test-message proof that defaults to NO.
41
+ * What changed in APRV-96 is only WHEN the read happens: the verb long-polls
42
+ * continuously until a message arrives or {@link DISCOVERY_DEADLINE_MS}
43
+ * passes, instead of asking the operator to press Enter between reads.
44
+ *
45
+ * ## Where the two hooks sit, and why
46
+ *
47
+ * The flow asks its questions in one fixed order, and Telegram's shape falls
48
+ * out of it: the token is `collect`, because a human supplies it; the chat id is
49
+ * `discover`, because the SERVICE supplies it and the human's only part is
50
+ * sending a message and picking from what arrived. That is the hook APRV-78
51
+ * reserved and did not call. The proof is `verify`, which runs after the write —
52
+ * later than the old hand-rolled flow ran it, and correct in the same way the
53
+ * adapter's SMTP probe is: what is being proved is the stored configuration.
54
+ */
55
+ import { LEGACY_SERVICE_TELEGRAM_TOKEN, instanceHomeFor, instanceIdFor } from "../core/instance.js";
56
+ import { telegramChatEnvFor, telegramCredentialSpecs, telegramTokenEnvFor, } from "../core/telegram-config.js";
57
+ import { EXIT_INTEGRITY, EXIT_IO, EXIT_OK } from "./exit-codes.js";
58
+ import { SETUP_CHANNEL_HELP, SETUP_CHANNEL_TELEGRAM_HELP } from "./help.js";
59
+ import { PROBE_TIMEOUT_MS, detail, front, offerLiteral, requireHuman, retrievalCommand, schemeFor, storageCommand, usageError, } from "./setup-common.js";
60
+ import { envFileDestination, pickOne, runCredentialFlow, } from "./setup-flow.js";
61
+ /**
62
+ * How long the verb waits for a message before it gives up (APRV-96).
63
+ *
64
+ * It replaces the three "send one and press Enter" attempts, which made the
65
+ * operator's TIMING part of whether the verb worked: a message sent while the
66
+ * 10s long poll was not running was simply not seen, and the verb had no way to
67
+ * say so. Ninety seconds is long enough to unlock a phone and find the chat,
68
+ * and short enough that a run left in a forgotten terminal ends by itself.
69
+ */
70
+ const DISCOVERY_DEADLINE_MS = 90_000;
71
+ /** A deadline in whole seconds, for the two lines that state it. Never "0s". */
72
+ function statedSeconds(ms) {
73
+ return `${String(Math.max(1, Math.round(ms / 1000)))}s`;
74
+ }
75
+ /**
76
+ * The refusal for the OLD spelling, as one constant on one line.
77
+ *
78
+ * One line and one identifier, because `tests/setup-rename.test.ts` sweeps
79
+ * `src/`, `docs/`, `examples/`, `README.md` and `SPEC.md` for the bare phrase
80
+ * and exempts exactly the lines that also name `RENAMED_NOTICE`. That is the
81
+ * whole exemption mechanism: a future line that reintroduces the old spelling
82
+ * cannot claim the exemption without saying this constant's name out loud.
83
+ */
84
+ export const RENAMED_NOTICE = `\`approval setup telegram\` is now \`approval setup channel telegram\`, and there is no alias. A channel surfaces requests and collects decisions and holds no state; an adapter executes side effects and holds credentials. The two setup verbs fill different stores, so a channel's name belongs under \`channel\` (the OS keystore and .approval/env) and an adapter's under \`adapter\` (the vault)`;
85
+ // ---------------------------------------------------------------------------
86
+ // Telegram: the Bot API calls
87
+ // ---------------------------------------------------------------------------
88
+ /** Replace the token wherever it appears. Nothing leaves this file with it. */
89
+ function redact(text, token) {
90
+ return token.length === 0 ? text : text.split(token).join("<token redacted>");
91
+ }
92
+ /** One Bot API call, with doctor's probe shape: the token is in the URL only. */
93
+ async function call(fetchImpl, apiBase, token, method, body, timeoutMs) {
94
+ const controller = new AbortController();
95
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
96
+ try {
97
+ const response = await fetchImpl(`${apiBase}/bot${token}/${method}`, {
98
+ method: "POST",
99
+ headers: { "content-type": "application/json" },
100
+ body: JSON.stringify(body),
101
+ signal: controller.signal,
102
+ });
103
+ const raw = await response.text();
104
+ let envelope = {};
105
+ try {
106
+ const parsed = JSON.parse(raw);
107
+ if (typeof parsed === "object" && parsed !== null)
108
+ envelope = parsed;
109
+ }
110
+ catch {
111
+ /* a non-JSON body is not an ok envelope; handled by the caller */
112
+ }
113
+ return { ok: response.ok, status: response.status, envelope };
114
+ }
115
+ catch (cause) {
116
+ return { failed: redact(detail(cause), token) };
117
+ }
118
+ finally {
119
+ clearTimeout(timer);
120
+ }
121
+ }
122
+ /**
123
+ * The chats in a `getUpdates` result, newest first and deduplicated by id.
124
+ *
125
+ * `title ?? username ?? first_name` is Telegram's own precedence for what to
126
+ * call a chat: groups and channels carry a title, a private chat carries the
127
+ * user's username or, for a user who has set none, their first name.
128
+ */
129
+ function candidatesFrom(updates) {
130
+ const found = new Map();
131
+ const list = Array.isArray(updates) ? [...updates].reverse() : [];
132
+ for (const update of list) {
133
+ if (typeof update !== "object" || update === null)
134
+ continue;
135
+ const message = update["message"];
136
+ if (typeof message !== "object" || message === null)
137
+ continue;
138
+ const chat = message["chat"];
139
+ if (typeof chat !== "object" || chat === null)
140
+ continue;
141
+ const record = chat;
142
+ const id = record["id"];
143
+ if (typeof id !== "number" && typeof id !== "string")
144
+ continue;
145
+ const key = String(id);
146
+ if (found.has(key))
147
+ continue;
148
+ const name = typeof record["title"] === "string"
149
+ ? record["title"]
150
+ : typeof record["username"] === "string"
151
+ ? `@${record["username"]}`
152
+ : typeof record["first_name"] === "string"
153
+ ? record["first_name"]
154
+ : "unnamed";
155
+ found.set(key, {
156
+ id: key,
157
+ type: typeof record["type"] === "string" ? record["type"] : "unknown",
158
+ name,
159
+ });
160
+ }
161
+ return [...found.values()];
162
+ }
163
+ // ---------------------------------------------------------------------------
164
+ // Telegram: the hooks
165
+ // ---------------------------------------------------------------------------
166
+ const TELEGRAM_HINT = (where) => ` # 1. store the bot token (the helper prompts for it with NO ECHO; the token is\n # never an argument, so it never reaches your shell history or \`ps\`):\n ${storageCommand(where.kind === "none" ? "keychain" : where.kind, where.services.telegramToken)}\n\n # 2. find the chat id — send your bot a message first, then:\n curl -s "https://api.telegram.org/bot<token>/getUpdates" \\\n | grep -o '"chat":{"id":[-0-9]*' | head -1\n\n # 3. record both (a chat id is not a secret; the item name carries THIS\n # instance's id, so a second gate on this machine gets its own token):\n printf '%s\\n' '${where.tokenEnv}=${schemeFor(where.kind === "none" ? "keychain" : where.kind, where.services.telegramToken) ?? ""}' '${where.chatEnv}=<id>' >> ${where.envPath}\n chmod 600 ${where.envPath}`;
167
+ /**
168
+ * The Telegram conversation, as three hooks over one run.
169
+ *
170
+ * The mutable state is the token and the bot's username, both learned in
171
+ * `collect` and both needed by `discover` and `verify`. It is a closure and not
172
+ * a field on the entry, so two runs in one process (which is what the test
173
+ * suite is) cannot see each other's token.
174
+ */
175
+ function telegramHooks(context, deps, streams, helpText) {
176
+ const tokenEnv = telegramTokenEnvFor(context.load);
177
+ const fetchImpl = deps.fetch ?? globalThis.fetch;
178
+ const apiBase = context.apiBase.replace(/\/+$/u, "");
179
+ /** The operator's token. Held for the length of one run and printed nowhere. */
180
+ let token = null;
181
+ /** `@botname`, from getMe. Also the proof that the token works. */
182
+ let username = null;
183
+ /** The chat the operator picked, for the proof's report line. */
184
+ let chosen = null;
185
+ /** The keystore item this run reads and writes. Decided once, by {@link chooseService}. */
186
+ let service = null;
187
+ /**
188
+ * Which keystore item this instance's token lives in (APRV-178).
189
+ *
190
+ * The name it WANTS is the scoped one, `approval-tg-token-<instance id>`, and
191
+ * on a machine with one gate and a clean keystore that is the end of it. Two
192
+ * other situations exist, and the difference between them is the whole task:
193
+ *
194
+ * - **The scoped item is already there.** This instance provisioned itself
195
+ * before. Nothing to ask: the item is this instance's by construction, and
196
+ * storing over it replaces this gate's own token, which is what a re-run is
197
+ * for.
198
+ * - **The scoped item is absent and the UNSCOPED legacy item exists.** That
199
+ * item is the pre-APRV-178 name every gate on this machine resolves to the
200
+ * same value, so it may be this instance's token from before the rename, or
201
+ * it may be another instance's production bot — which is the incident. This
202
+ * runtime cannot tell those apart, and guessing is what consumed a human's
203
+ * approval tap in the wrong listener, so it ASKS, naming the item and this
204
+ * instance's directory, and adopts the legacy name only on a typed `yes`.
205
+ *
206
+ * The reads here can, on a locked keychain, put the OS's own unlock prompt in
207
+ * front of the operator. That is acceptable HERE and nowhere else: this is an
208
+ * interactive verb with a human at the machine who is about to be asked for a
209
+ * password by `security` anyway. `approval doctor` answers the same question
210
+ * from names alone, precisely so that a diagnostic never blocks on a dialog.
211
+ */
212
+ function chooseService() {
213
+ if (service !== null)
214
+ return service;
215
+ const mine = context.services.telegramToken;
216
+ if (context.backend === "none") {
217
+ service = mine;
218
+ return service;
219
+ }
220
+ // The legacy item is probed FIRST and the scoped one only if it is there,
221
+ // so the ordinary machine — the one with no legacy item at all — costs one
222
+ // extra keystore lookup rather than two, and asks nothing.
223
+ if (!context.keystore.read(LEGACY_SERVICE_TELEGRAM_TOKEN).ok) {
224
+ service = mine;
225
+ return service;
226
+ }
227
+ if (context.keystore.read(mine).ok) {
228
+ service = mine;
229
+ return service;
230
+ }
231
+ streams.out(`\nA bot token is already stored under ${LEGACY_SERVICE_TELEGRAM_TOKEN}, which is the name this\n` +
232
+ `runtime used before item names were scoped to an instance. EVERY gate on this machine\n` +
233
+ `resolves that one name to that one item, so it belongs to whichever instance stored it\n` +
234
+ `last — and nothing here can tell whether that was this one.\n\n` +
235
+ `This instance is ${instanceHomeFor(context.logPath)} (id ${instanceIdFor(context.logPath)}).\n` +
236
+ `Its own item name is ${mine}.\n\n` +
237
+ `Answer NO unless you are certain that stored token is this instance's bot. Sharing one\n` +
238
+ `token between two gates makes both of them long-poll the same bot, and a human's\n` +
239
+ `approval tap is then delivered to whichever listener asked for updates first.\n\n`);
240
+ const answer = context.prompter.readLine(`does ${LEGACY_SERVICE_TELEGRAM_TOKEN} belong to THIS instance? type \`yes\` in full to reuse it: `);
241
+ if ((answer ?? "").trim() === "yes") {
242
+ service = LEGACY_SERVICE_TELEGRAM_TOKEN;
243
+ streams.out(`reusing ${LEGACY_SERVICE_TELEGRAM_TOKEN} for this instance; nothing else on this machine may name it\n`);
244
+ return service;
245
+ }
246
+ service = mine;
247
+ streams.out(`not reused: this instance will store its own token as ${mine}, and ${LEGACY_SERVICE_TELEGRAM_TOKEN} is left untouched\n`);
248
+ return service;
249
+ }
250
+ /**
251
+ * getMe — doctor's probe, verbatim in shape: it mutates nothing, sends
252
+ * nothing, and acknowledges nothing. Runs once per run, and always BEFORE the
253
+ * flow reaches its write step.
254
+ */
255
+ async function probeIdentity() {
256
+ if (username !== null)
257
+ return { ok: true };
258
+ const held = token ?? "";
259
+ const identity = await call(fetchImpl, apiBase, held, "getMe", {}, PROBE_TIMEOUT_MS);
260
+ if ("failed" in identity) {
261
+ streams.err(`approval: getMe on ${apiBase} failed: ${identity.failed}\n`);
262
+ streams.err(` check network reachability of ${apiBase}\n`);
263
+ return { ok: false, code: EXIT_IO };
264
+ }
265
+ if (!identity.ok || identity.envelope["ok"] !== true) {
266
+ const description = redact(String(identity.envelope["description"] ?? "no description"), held);
267
+ streams.err(`approval: getMe on ${apiBase} was refused: HTTP ${String(identity.status)} (${description})\n`);
268
+ streams.err(identity.status === 401 || /unauthorized/iu.test(description)
269
+ ? ` the bot token is not valid: re-copy it from @BotFather into ${tokenEnv}\n`
270
+ : ` check the token and that ${apiBase} is the right Bot API base\n`);
271
+ streams.err(` nothing was written to ${context.envPath}\n`);
272
+ return { ok: false, code: EXIT_INTEGRITY };
273
+ }
274
+ const result = (identity.envelope["result"] ?? {});
275
+ username = typeof result["username"] === "string" ? `@${result["username"]}` : "the bot";
276
+ streams.out(`\ntoken valid: ${username} via ${apiBase}\n`);
277
+ return { ok: true };
278
+ }
279
+ /**
280
+ * The token, and then the identity probe.
281
+ *
282
+ * On a machine with a keystore the HELPER's prompt collects it and this
283
+ * process learns it only by reading the item back on stdout; the value that
284
+ * lands in `.approval/env` is the SOURCE and never the token. With no
285
+ * keystore it is read with no echo and — after a typed `yes` — written as a
286
+ * plaintext literal, which is the one path on which the file holds the token.
287
+ */
288
+ async function collectToken() {
289
+ if (context.backend === "none") {
290
+ const read = context.prompter.readSecret(`bot token from @BotFather (not echoed): `);
291
+ if (!read.ok) {
292
+ return {
293
+ kind: "refused",
294
+ code: usageError(streams, false, "the token entry was aborted; nothing was stored and nothing was written", helpText),
295
+ };
296
+ }
297
+ token = read.value.trim();
298
+ if (token.length === 0) {
299
+ return {
300
+ kind: "refused",
301
+ code: usageError(streams, false, "no token was entered; nothing was written", helpText),
302
+ };
303
+ }
304
+ if (!offerLiteral(streams, context.prompter, context.envPath, "bot token")) {
305
+ return { kind: "refused", code: EXIT_OK };
306
+ }
307
+ const probed = await probeIdentity();
308
+ if (!probed.ok)
309
+ return { kind: "refused", code: probed.code };
310
+ return { kind: "value", value: token };
311
+ }
312
+ const helper = context.backend === "keychain" ? "macOS `security`" : "`secret-tool`";
313
+ const helperPrompt = context.backend === "keychain"
314
+ ? '"password data for new item:" and then "retype password for new item:"'
315
+ : '"Password:"';
316
+ const item = chooseService();
317
+ streams.out(`Next: paste the BOT TOKEN from @BotFather (Telegram: /mybots, pick the bot, "API Token"; it looks like 123456789:AAH...).\n` +
318
+ `${helper} asks for it with its own prompt, ${helperPrompt}. Nothing is echoed as\n` +
319
+ `you paste, and the value goes straight into the keystore as ${item};\n` +
320
+ `this process never sees you type it. There is nothing to look up first: this creates the item.\n` +
321
+ `Already saved it under that name from an earlier run? Pasting the same value updates the item in\n` +
322
+ `place; print it in another window with: ${retrievalCommand(context.backend, item)}\n\n`);
323
+ const stored = context.keystore.storePrompted(item);
324
+ if (!stored.ok) {
325
+ streams.err(`approval: the token could not be stored (${stored.message}); nothing was written to ${context.envPath}\n`);
326
+ return { kind: "refused", code: EXIT_IO };
327
+ }
328
+ const read = context.keystore.read(item);
329
+ if (!read.ok) {
330
+ streams.err(`approval: the token was stored but could not be read back (${read.message}); nothing was written to ${context.envPath}\n`);
331
+ return { kind: "refused", code: EXIT_IO };
332
+ }
333
+ token = read.value.trim();
334
+ const scheme = schemeFor(context.backend, item);
335
+ streams.out(`stored the token as ${scheme}\n`);
336
+ streams.out(` read it back with: ${retrievalCommand(context.backend, item)}\n`);
337
+ const probed = await probeIdentity();
338
+ if (!probed.ok)
339
+ return { kind: "refused", code: probed.code };
340
+ return { kind: "value", value: scheme };
341
+ }
342
+ /**
343
+ * The token this run needs, when the token LINE was left alone.
344
+ *
345
+ * A re-run that replaces only the chat id still has to talk to the Bot API,
346
+ * and the token it should use is the one already stored — so it is read back
347
+ * out of the keystore, with no prompt and no write. `.approval/env` is not
348
+ * consulted for it on any path: reading a value out of that file is the
349
+ * resolution §11.1 invariant 7 forbids, which is also why the no-keystore
350
+ * machine (whose token IS the file's literal) cannot take this path.
351
+ *
352
+ * The scoped item is tried first and the unscoped legacy one second (APRV-178),
353
+ * because an instance provisioned before the rename has its token only under
354
+ * the old name and a re-run that replaced just the chat id must not start
355
+ * demanding a token the operator already stored. The fallback is announced on
356
+ * stderr every time it is taken: adopting a machine-global item silently is
357
+ * the behaviour that put a demo gate on the production bot.
358
+ */
359
+ function recoverToken() {
360
+ if (token !== null)
361
+ return { ok: true };
362
+ if (context.backend === "none") {
363
+ streams.err(`approval: the ${tokenEnv} line was left alone, and this machine has no keystore to read the token back from — the only copy is the literal in ${context.envPath}, and no verb resolves that file on its own. Re-run and replace both lines. Nothing was written\n`);
364
+ return { ok: false, code: EXIT_IO };
365
+ }
366
+ const mine = context.services.telegramToken;
367
+ let read = context.keystore.read(mine);
368
+ if (!read.ok) {
369
+ const legacy = context.keystore.read(LEGACY_SERVICE_TELEGRAM_TOKEN);
370
+ if (legacy.ok) {
371
+ streams.err(`approval: no ${mine} item for this instance, so the token was read from ${LEGACY_SERVICE_TELEGRAM_TOKEN} — the unscoped name every gate on this machine shares. Re-run \`approval setup channel telegram\` and replace the ${tokenEnv} line to give this instance its own item\n`);
372
+ }
373
+ read = legacy;
374
+ }
375
+ if (!read.ok) {
376
+ streams.err(`approval: the ${tokenEnv} line was left alone, so the stored token is what this run would ask the Bot API with, and it could not be read (${read.message}); nothing was written to ${context.envPath}\n`);
377
+ return { ok: false, code: EXIT_IO };
378
+ }
379
+ token = read.value.trim();
380
+ return { ok: true };
381
+ }
382
+ /**
383
+ * What Telegram itself says about this bot's update stream (APRV-96).
384
+ *
385
+ * Read ONLY on the give-up path, and read for one reason: when no message
386
+ * arrives, the three explanations an operator cannot tell apart are "you
387
+ * messaged a different bot", "a webhook is set, so getUpdates returns nothing
388
+ * ever", and "another poller acknowledged it with an offset". `getWebhookInfo`
389
+ * answers the last two directly and `getMe`'s username answers the first, so
390
+ * the refusal prints all three rather than "no message seen yet".
391
+ *
392
+ * It mutates nothing and acknowledges nothing, exactly like `getMe`.
393
+ */
394
+ async function webhookReport(held) {
395
+ const info = await call(fetchImpl, apiBase, held, "getWebhookInfo", {}, PROBE_TIMEOUT_MS);
396
+ if ("failed" in info) {
397
+ return [` getWebhookInfo could not be reached (${info.failed}), so Telegram's own view of this bot is unknown\n`];
398
+ }
399
+ if (!info.ok || info.envelope["ok"] !== true) {
400
+ const description = redact(String(info.envelope["description"] ?? "no description"), held);
401
+ return [` getWebhookInfo was refused: HTTP ${String(info.status)} (${description})\n`];
402
+ }
403
+ const result = (info.envelope["result"] ?? {});
404
+ const pending = typeof result["pending_update_count"] === "number" ? result["pending_update_count"] : 0;
405
+ const hook = typeof result["url"] === "string" ? redact(result["url"], held) : "";
406
+ const lines = [];
407
+ lines.push(pending > 0
408
+ ? ` Telegram holds ${String(pending)} update(s) for this bot that no poller has consumed; another\n process may be long-polling with an offset — stop \`approval channel telegram listen\`\n (here and on any other machine) and retry.\n`
409
+ : ` Telegram holds no pending updates for this bot. If you did send one, something else\n acknowledged it with an offset (a listener or daemon, possibly on another machine); that\n process will also fight \`approval channel telegram listen\` with 409s.\n`);
410
+ lines.push(hook.length > 0
411
+ ? ` a webhook is registered at ${hook}; getUpdates returns nothing while a webhook is set —\n remove it with deleteWebhook, or read the chat id off the webhook instead.\n`
412
+ : ` no webhook is registered, so getUpdates is the right way to read this bot.\n`);
413
+ return lines;
414
+ }
415
+ /**
416
+ * The chat, discovered from the bot's own updates.
417
+ *
418
+ * THE getUpdates BELOW CARRIES NO OFFSET, EVER. See this file's module doc.
419
+ *
420
+ * ## Waiting, rather than asking (APRV-96)
421
+ *
422
+ * The loop re-issues the same offset-less read back to back until a message
423
+ * turns up or the deadline passes, and asks the operator for nothing while it
424
+ * does. The old shape asked for Enter between reads, which made the OPERATOR
425
+ * responsible for overlapping their message with a 10s window: a message sent
426
+ * a second late was consumed by nothing, seen by nothing, and reported as "no
427
+ * message seen yet" (observed 2026-08-18 running `examples/email-demo.md`).
428
+ *
429
+ * **Ctrl-C is the abort, and it is the terminal's own.** Nothing here reads
430
+ * the keyboard, so stdin is not in raw mode and this process installs no
431
+ * SIGINT handler: the signal reaches Node's default disposition and the
432
+ * process dies between two HTTP calls. That is safe precisely because of
433
+ * where this hook sits — `.approval/env` is written by the flow only after
434
+ * every hook has returned, so an interrupted wait leaves the file exactly as
435
+ * it found it. (The keystore item the token step created stays, as it does on
436
+ * the give-up path below; it is a stored credential, not a half-written
437
+ * record.) A handler would add a way to be wrong about that and no capability.
438
+ */
439
+ async function discoverChat() {
440
+ const recovered = recoverToken();
441
+ if (!recovered.ok)
442
+ return { kind: "refused", code: recovered.code };
443
+ const probed = await probeIdentity();
444
+ if (!probed.ok)
445
+ return { kind: "refused", code: probed.code };
446
+ const bot = username ?? "the bot";
447
+ const held = token ?? "";
448
+ const deadlineMs = deps.discoveryDeadlineMs ?? DISCOVERY_DEADLINE_MS;
449
+ const giveUpAt = Date.now() + deadlineMs;
450
+ streams.out(`\nwaiting for a message to ${bot} (up to ${statedSeconds(deadlineMs)}, Ctrl-C to stop):\nopen Telegram and send it anything. No Enter is needed here — this keeps reading\nuntil your message lands, so it does not matter when you send it.\n`);
451
+ let candidates = [];
452
+ for (;;) {
453
+ // NO OFFSET, EVER. An `offset` is an ACKNOWLEDGEMENT: it tells the Bot API
454
+ // that everything below it may be discarded. A running
455
+ // `approval channel telegram listen` owns that acknowledgement, and a
456
+ // decision tap consumed here would never reach the listener that was
457
+ // waiting for it — which is exactly why `approval doctor` refuses to call
458
+ // getUpdates at all. Reading WITHOUT an offset confirms nothing: the
459
+ // pending callback_query updates a listener is waiting for are still
460
+ // pending when this returns. `allowed_updates: ["message"]` narrows the
461
+ // read to the only kind this verb has any use for, so a callback is not
462
+ // even delivered here.
463
+ const updates = await call(fetchImpl, apiBase, held, "getUpdates", { timeout: context.pollTimeoutSeconds, allowed_updates: ["message"] }, context.pollTimeoutSeconds * 1000 + PROBE_TIMEOUT_MS);
464
+ if ("failed" in updates) {
465
+ streams.err(`approval: getUpdates on ${apiBase} failed: ${updates.failed}\n`);
466
+ streams.err(` nothing was written to ${context.envPath}\n`);
467
+ return { kind: "refused", code: EXIT_IO };
468
+ }
469
+ if (!updates.ok || updates.envelope["ok"] !== true) {
470
+ const description = redact(String(updates.envelope["description"] ?? "no description"), held);
471
+ streams.err(`approval: getUpdates on ${apiBase} was refused: HTTP ${String(updates.status)} (${description})\n`);
472
+ streams.err(` a 409 here means another process is long-polling this bot: stop \`approval channel telegram listen\` and re-run\n`);
473
+ return { kind: "refused", code: EXIT_INTEGRITY };
474
+ }
475
+ candidates = candidatesFrom(updates.envelope["result"]);
476
+ if (candidates.length > 0)
477
+ break;
478
+ if (Date.now() >= giveUpAt)
479
+ break;
480
+ }
481
+ if (candidates.length === 0) {
482
+ const said = await webhookReport(held);
483
+ streams.err(`approval: no message reached ${bot} in ${statedSeconds(deadlineMs)}, so there is no chat id to record.\n\nWhat to check first, and what Telegram says about this bot right now:\n\n did you message ${bot}? That is the bot getMe answered for, and the chat header on\n your phone must read exactly that — a message to a different bot lands nowhere here.\n${said.join("")}\nThe token is stored; only the two ${context.envPath} lines are missing. Find the id\nby hand — send the bot a message, then:\n\n curl -s "${apiBase}/bot<token>/getUpdates" | grep -o '"chat":{"id":[-0-9]*'\n\n(the <token> is yours to substitute; it is deliberately not printed here). Then:\n\n printf '%s\\n' '${telegramChatEnvFor(context.load)}=<id>' >> ${context.envPath}\n\nIf the bot is in a GROUP, check that privacy mode is off in @BotFather, or the\nbot never sees plain group messages at all.\n`);
484
+ return { kind: "refused", code: EXIT_INTEGRITY };
485
+ }
486
+ if (candidates.length === 1) {
487
+ const only = candidates[0];
488
+ if (!context.prompter.confirm(`use chat ${only.id} (${only.type}, ${only.name})?`)) {
489
+ streams.out("aborted: nothing was written\n");
490
+ return { kind: "refused", code: EXIT_OK };
491
+ }
492
+ chosen = only;
493
+ }
494
+ else {
495
+ const picked = pickOne(streams, context.prompter, {
496
+ heading: `\n${String(candidates.length)} chats have messaged ${bot}, newest first:\n`,
497
+ items: candidates,
498
+ label: (candidate) => `${candidate.id} (${candidate.type}, ${candidate.name})`,
499
+ prompt: `which one? [1-${String(candidates.length)}]: `,
500
+ defaultIndex: null,
501
+ });
502
+ if (!picked.ok) {
503
+ return {
504
+ kind: "refused",
505
+ code: usageError(streams, false, `${picked.message}; nothing was written`, helpText),
506
+ };
507
+ }
508
+ chosen = picked.item;
509
+ }
510
+ return { kind: "value", value: chosen.id };
511
+ }
512
+ /**
513
+ * The optional proof. Default NO: a configuration verb that buzzes a phone by
514
+ * default is one an operator runs once and then avoids, which is doctor's
515
+ * argument for calling getMe and nothing else.
516
+ *
517
+ * A send that fails is reported as DECLINED rather than as a failure, because
518
+ * the configuration it would have proved is stored and correct as far as
519
+ * anything here knows: a chat the bot may not post into is a Telegram-side
520
+ * fact the operator fixes in Telegram, and exiting 1 over it would make the
521
+ * verb look like it had refused to write the lines it had just written.
522
+ */
523
+ async function verifyChat(values) {
524
+ const chatId = values[telegramChatEnvFor(context.load)];
525
+ if (chatId === undefined)
526
+ return { ok: true, declined: true, detail: "" };
527
+ if (!context.prompter.confirm(`send a test message to ${chatId} to prove it?`)) {
528
+ return { ok: true, declined: true, detail: "" };
529
+ }
530
+ const held = token ?? "";
531
+ const sent = await call(fetchImpl, apiBase, held, "sendMessage", { chat_id: chatId, text: "approval.md: setup test message. Nothing is pending." }, PROBE_TIMEOUT_MS);
532
+ if ("failed" in sent || !sent.ok || sent.envelope["ok"] !== true) {
533
+ const why = "failed" in sent
534
+ ? sent.failed
535
+ : redact(String(sent.envelope["description"] ?? "no description"), held);
536
+ return {
537
+ ok: true,
538
+ declined: true,
539
+ detail: ` the test message did not send (${why}); the chat id is recorded either way\n`,
540
+ };
541
+ }
542
+ return { ok: true, detail: ` sent — check ${chosen?.name ?? chatId}\n` };
543
+ }
544
+ return {
545
+ collect: async (spec) => spec.name === tokenEnv ? await collectToken() : { kind: "skip" },
546
+ discover: async (spec) => spec.name === telegramChatEnvFor(context.load)
547
+ ? await discoverChat()
548
+ : { kind: "skip" },
549
+ verify: async (values) => verifyChat(values),
550
+ };
551
+ }
552
+ /** Every channel this verb can configure. Keyed by the `channel <name>` name. */
553
+ export const CHANNEL_SETUPS = {
554
+ telegram: {
555
+ specs: (load) => telegramCredentialSpecs(load),
556
+ summary: "the bot token and the approver chat, recorded where each of them lives",
557
+ prereq: `IF \`approval channel telegram listen\` IS RUNNING, STOP IT FIRST. Two processes\nlong-polling one bot is a 409 from the Bot API, and the loser is whichever asked\nsecond. This verb is a configuration verb; it is not meant to run beside the\nlistener.\n\nThe token goes into the OS KEYSTORE and the chat id into .approval/env as a\nliteral: a channel holds no state, and what this file records is where the\ntransport credential lives. Nothing here appends to the log or\nattests anything.`,
558
+ hint: TELEGRAM_HINT,
559
+ hooks: telegramHooks,
560
+ nextSteps: [
561
+ `No update was acknowledged by this verb: every getUpdates above carried no`,
562
+ `offset, so a running listener's pending callbacks are exactly where they were.`,
563
+ ``,
564
+ `Establish the variables and check the channel:`,
565
+ ``,
566
+ ` eval "$(approval env)"`,
567
+ ` approval channel telegram health`,
568
+ ],
569
+ help: SETUP_CHANNEL_TELEGRAM_HELP,
570
+ },
571
+ };
572
+ /** The known names, sorted, for a usage error and for the help text. */
573
+ export function knownChannelNames() {
574
+ return Object.keys(CHANNEL_SETUPS).sort();
575
+ }
576
+ // ---------------------------------------------------------------------------
577
+ // The verb
578
+ // ---------------------------------------------------------------------------
579
+ /**
580
+ * `approval setup channel <name>` — the interactive writer for one channel's
581
+ * transport credentials. HUMAN-ONLY.
582
+ *
583
+ * The human-only gate is NEW in APRV-79. The Telegram help text had said
584
+ * HUMAN-ONLY since APRV-74 and nothing enforced it: the verb stores a bot
585
+ * credential and writes `.approval/env`, which is exactly what `vault` and
586
+ * `sampling` are gated for, and a help text that claims a control the code does
587
+ * not apply is worse than no claim at all.
588
+ *
589
+ * `argv` starts at the channel's name: `commandSetup` has already eaten
590
+ * `channel`.
591
+ */
592
+ export async function commandSetupChannel(argv, streams, cwd, deps = {}) {
593
+ const json = argv.includes("--json");
594
+ const name = argv[0];
595
+ // The name is resolved BEFORE the terminal check, so that a typo is answered
596
+ // with "here are the channels" rather than with a lecture about pipes.
597
+ if (name === "--help" || name === "-h" || name === "help") {
598
+ streams.out(`${SETUP_CHANNEL_HELP}\n`);
599
+ return EXIT_OK;
600
+ }
601
+ if (name === undefined || name.startsWith("-")) {
602
+ return usageError(streams, json, `missing <name> for \`approval setup channel\`; known channels: ${knownChannelNames().join(", ")}`, SETUP_CHANNEL_HELP);
603
+ }
604
+ const entry = CHANNEL_SETUPS[name];
605
+ if (entry === undefined) {
606
+ return usageError(streams, json, `unknown channel ${JSON.stringify(name)}; known channels: ${knownChannelNames().join(", ")}`, SETUP_CHANNEL_HELP);
607
+ }
608
+ const helpText = entry.help;
609
+ const outcome = front(`channel ${name}`, argv.slice(1), streams, cwd, deps, helpText, (context) => entry.hint(context));
610
+ if (outcome.kind === "handled")
611
+ return outcome.code;
612
+ const context = outcome;
613
+ const extra = context.positionals[0];
614
+ if (extra !== undefined) {
615
+ return usageError(streams, false, `unexpected argument ${JSON.stringify(extra)}`, helpText);
616
+ }
617
+ const human = requireHuman(context.flags, streams, helpText, `channel ${name}`);
618
+ if (!human.ok)
619
+ return human.code;
620
+ const result = await runCredentialFlow({
621
+ streams,
622
+ prompter: context.prompter,
623
+ specs: entry.specs(context.load),
624
+ destination: envFileDestination(context.envPath),
625
+ labels: {
626
+ title: `approval setup channel ${name} — ${entry.summary}.`,
627
+ prereq: entry.prereq,
628
+ nextSteps: entry.nextSteps,
629
+ },
630
+ hooks: entry.hooks(context, deps, streams, helpText),
631
+ });
632
+ // The flow decided the code; this verb adds nothing to it.
633
+ return result.code;
634
+ }
635
+ //# sourceMappingURL=setup-channel.js.map