approval-md 0.3.0 → 0.4.1

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 (270) hide show
  1. package/README.md +83 -914
  2. package/SPEC.md +101 -13
  3. package/cli.js +134 -7
  4. package/dist/src/adapters/smtp.d.ts +18 -0
  5. package/dist/src/adapters/smtp.js +24 -3
  6. package/dist/src/adapters/smtp.js.map +1 -1
  7. package/dist/src/channels/contract.d.ts +72 -0
  8. package/dist/src/channels/contract.js +161 -1
  9. package/dist/src/channels/contract.js.map +1 -1
  10. package/dist/src/channels/relay-server.d.ts +90 -0
  11. package/dist/src/channels/relay-server.js +295 -0
  12. package/dist/src/channels/relay-server.js.map +1 -0
  13. package/dist/src/channels/relay.d.ts +273 -0
  14. package/dist/src/channels/relay.js +564 -0
  15. package/dist/src/channels/relay.js.map +1 -0
  16. package/dist/src/channels/tagging.d.ts +25 -0
  17. package/dist/src/channels/tagging.js +63 -6
  18. package/dist/src/channels/tagging.js.map +1 -1
  19. package/dist/src/channels/telegram-minimal.d.ts +251 -0
  20. package/dist/src/channels/telegram-minimal.js +618 -0
  21. package/dist/src/channels/telegram-minimal.js.map +1 -0
  22. package/dist/src/channels/telegram-webhook.d.ts +273 -0
  23. package/dist/src/channels/telegram-webhook.js +536 -0
  24. package/dist/src/channels/telegram-webhook.js.map +1 -0
  25. package/dist/src/channels/telegram.d.ts +725 -20
  26. package/dist/src/channels/telegram.js +1332 -123
  27. package/dist/src/channels/telegram.js.map +1 -1
  28. package/dist/src/cli/amend.js +358 -5
  29. package/dist/src/cli/amend.js.map +1 -1
  30. package/dist/src/cli/attest.js +43 -6
  31. package/dist/src/cli/attest.js.map +1 -1
  32. package/dist/src/cli/audit-card.d.ts +1 -1
  33. package/dist/src/cli/audit-card.js +40 -1
  34. package/dist/src/cli/audit-card.js.map +1 -1
  35. package/dist/src/cli/audit.d.ts +7 -5
  36. package/dist/src/cli/audit.js +22 -10
  37. package/dist/src/cli/audit.js.map +1 -1
  38. package/dist/src/cli/channel-relay.d.ts +108 -0
  39. package/dist/src/cli/channel-relay.js +334 -0
  40. package/dist/src/cli/channel-relay.js.map +1 -0
  41. package/dist/src/cli/channel-telegram-webhook.d.ts +275 -0
  42. package/dist/src/cli/channel-telegram-webhook.js +716 -0
  43. package/dist/src/cli/channel-telegram-webhook.js.map +1 -0
  44. package/dist/src/cli/channel-telegram.d.ts +347 -28
  45. package/dist/src/cli/channel-telegram.js +648 -56
  46. package/dist/src/cli/channel-telegram.js.map +1 -1
  47. package/dist/src/cli/channel.d.ts +1 -1
  48. package/dist/src/cli/channel.js +5 -1
  49. package/dist/src/cli/channel.js.map +1 -1
  50. package/dist/src/cli/daemon.js +37 -5
  51. package/dist/src/cli/daemon.js.map +1 -1
  52. package/dist/src/cli/doctor.d.ts +63 -0
  53. package/dist/src/cli/doctor.js +856 -111
  54. package/dist/src/cli/doctor.js.map +1 -1
  55. package/dist/src/cli/execute.d.ts +29 -1
  56. package/dist/src/cli/execute.js +387 -21
  57. package/dist/src/cli/execute.js.map +1 -1
  58. package/dist/src/cli/exit-codes.d.ts +12 -0
  59. package/dist/src/cli/exit-codes.js +13 -0
  60. package/dist/src/cli/exit-codes.js.map +1 -1
  61. package/dist/src/cli/gate.d.ts +2 -0
  62. package/dist/src/cli/gate.js +233 -2
  63. package/dist/src/cli/gate.js.map +1 -1
  64. package/dist/src/cli/help.d.ts +10 -1
  65. package/dist/src/cli/help.js +283 -54
  66. package/dist/src/cli/help.js.map +1 -1
  67. package/dist/src/cli/hook.d.ts +485 -6
  68. package/dist/src/cli/hook.js +2370 -70
  69. package/dist/src/cli/hook.js.map +1 -1
  70. package/dist/src/cli/init.js +16 -7
  71. package/dist/src/cli/init.js.map +1 -1
  72. package/dist/src/cli/instructions.js +24 -3
  73. package/dist/src/cli/instructions.js.map +1 -1
  74. package/dist/src/cli/log-anchor.d.ts +55 -0
  75. package/dist/src/cli/log-anchor.js +199 -60
  76. package/dist/src/cli/log-anchor.js.map +1 -1
  77. package/dist/src/cli/log-unlock.d.ts +23 -0
  78. package/dist/src/cli/log-unlock.js +109 -0
  79. package/dist/src/cli/log-unlock.js.map +1 -0
  80. package/dist/src/cli/main.d.ts +0 -12
  81. package/dist/src/cli/main.js +72 -4
  82. package/dist/src/cli/main.js.map +1 -1
  83. package/dist/src/cli/muse.d.ts +2 -0
  84. package/dist/src/cli/muse.js +62 -0
  85. package/dist/src/cli/muse.js.map +1 -0
  86. package/dist/src/cli/payload.js +79 -1
  87. package/dist/src/cli/payload.js.map +1 -1
  88. package/dist/src/cli/scaffold.js +6 -0
  89. package/dist/src/cli/scaffold.js.map +1 -1
  90. package/dist/src/cli/serve.d.ts +53 -0
  91. package/dist/src/cli/serve.js +270 -0
  92. package/dist/src/cli/serve.js.map +1 -0
  93. package/dist/src/cli/up.js +26 -2
  94. package/dist/src/cli/up.js.map +1 -1
  95. package/dist/src/cli/verb-registry.d.ts +2 -0
  96. package/dist/src/cli/verb-registry.js +338 -39
  97. package/dist/src/cli/verb-registry.js.map +1 -1
  98. package/dist/src/cli/wordmark.d.ts +2 -2
  99. package/dist/src/cli/wordmark.js +2 -2
  100. package/dist/src/core/attest-bootstrap.d.ts +95 -0
  101. package/dist/src/core/attest-bootstrap.js +135 -0
  102. package/dist/src/core/attest-bootstrap.js.map +1 -0
  103. package/dist/src/core/attest.d.ts +20 -3
  104. package/dist/src/core/attest.js +25 -0
  105. package/dist/src/core/attest.js.map +1 -1
  106. package/dist/src/core/audit.d.ts +153 -12
  107. package/dist/src/core/audit.js +415 -20
  108. package/dist/src/core/audit.js.map +1 -1
  109. package/dist/src/core/channel-lease.d.ts +322 -0
  110. package/dist/src/core/channel-lease.js +663 -0
  111. package/dist/src/core/channel-lease.js.map +1 -0
  112. package/dist/src/core/command-class.d.ts +51 -0
  113. package/dist/src/core/command-class.js +766 -8
  114. package/dist/src/core/command-class.js.map +1 -1
  115. package/dist/src/core/daemon-host.d.ts +178 -0
  116. package/dist/src/core/daemon-host.js +228 -0
  117. package/dist/src/core/daemon-host.js.map +1 -0
  118. package/dist/src/core/daemon-identity.d.ts +238 -0
  119. package/dist/src/core/daemon-identity.js +252 -0
  120. package/dist/src/core/daemon-identity.js.map +1 -0
  121. package/dist/src/core/gate.d.ts +364 -6
  122. package/dist/src/core/gate.js +802 -27
  123. package/dist/src/core/gate.js.map +1 -1
  124. package/dist/src/core/harness-version.d.ts +128 -4
  125. package/dist/src/core/harness-version.js +179 -12
  126. package/dist/src/core/harness-version.js.map +1 -1
  127. package/dist/src/core/harness-wait.d.ts +82 -3
  128. package/dist/src/core/harness-wait.js +89 -3
  129. package/dist/src/core/harness-wait.js.map +1 -1
  130. package/dist/src/core/log-lock.d.ts +435 -0
  131. package/dist/src/core/log-lock.js +1093 -0
  132. package/dist/src/core/log-lock.js.map +1 -0
  133. package/dist/src/core/log-subscribe.d.ts +21 -0
  134. package/dist/src/core/log-subscribe.js +16 -6
  135. package/dist/src/core/log-subscribe.js.map +1 -1
  136. package/dist/src/core/log-write-layer.d.ts +55 -0
  137. package/dist/src/core/log-write-layer.js +91 -0
  138. package/dist/src/core/log-write-layer.js.map +1 -0
  139. package/dist/src/core/log.d.ts +130 -22
  140. package/dist/src/core/log.js +473 -50
  141. package/dist/src/core/log.js.map +1 -1
  142. package/dist/src/core/payload-census.d.ts +51 -0
  143. package/dist/src/core/payload-census.js +125 -2
  144. package/dist/src/core/payload-census.js.map +1 -1
  145. package/dist/src/core/payload-store.d.ts +31 -0
  146. package/dist/src/core/payload-store.js +113 -8
  147. package/dist/src/core/payload-store.js.map +1 -1
  148. package/dist/src/core/payload.d.ts +18 -3
  149. package/dist/src/core/payload.js +20 -4
  150. package/dist/src/core/payload.js.map +1 -1
  151. package/dist/src/core/policy-diff.d.ts +10 -0
  152. package/dist/src/core/policy-diff.js +10 -2
  153. package/dist/src/core/policy-diff.js.map +1 -1
  154. package/dist/src/core/policy-explain.d.ts +7 -1
  155. package/dist/src/core/policy-explain.js +16 -1
  156. package/dist/src/core/policy-explain.js.map +1 -1
  157. package/dist/src/core/policy-load.d.ts +24 -0
  158. package/dist/src/core/policy-load.js.map +1 -1
  159. package/dist/src/core/policy-match.d.ts +38 -0
  160. package/dist/src/core/policy-match.js +35 -0
  161. package/dist/src/core/policy-match.js.map +1 -1
  162. package/dist/src/core/policy-proposal.d.ts +40 -0
  163. package/dist/src/core/policy-proposal.js +37 -16
  164. package/dist/src/core/policy-proposal.js.map +1 -1
  165. package/dist/src/core/prompt-layout.d.ts +186 -1
  166. package/dist/src/core/prompt-layout.js +345 -3
  167. package/dist/src/core/prompt-layout.js.map +1 -1
  168. package/dist/src/core/read-scope.d.ts +10 -0
  169. package/dist/src/core/read-scope.js +4 -0
  170. package/dist/src/core/read-scope.js.map +1 -1
  171. package/dist/src/core/run-payload.d.ts +160 -0
  172. package/dist/src/core/run-payload.js +304 -0
  173. package/dist/src/core/run-payload.js.map +1 -0
  174. package/dist/src/core/sender-identity.d.ts +59 -11
  175. package/dist/src/core/sender-identity.js +79 -11
  176. package/dist/src/core/sender-identity.js.map +1 -1
  177. package/dist/src/core/state.d.ts +52 -1
  178. package/dist/src/core/state.js +52 -6
  179. package/dist/src/core/state.js.map +1 -1
  180. package/dist/src/core/telegram-config.d.ts +79 -0
  181. package/dist/src/core/telegram-config.js +108 -0
  182. package/dist/src/core/telegram-config.js.map +1 -1
  183. package/dist/src/core/validate.d.ts +20 -6
  184. package/dist/src/core/validate.js +0 -0
  185. package/dist/src/core/validate.js.map +1 -1
  186. package/dist/src/core/verify.d.ts +23 -1
  187. package/dist/src/core/verify.js +20 -2
  188. package/dist/src/core/verify.js.map +1 -1
  189. package/dist/src/daemon/audit.d.ts +80 -5
  190. package/dist/src/daemon/audit.js +126 -6
  191. package/dist/src/daemon/audit.js.map +1 -1
  192. package/dist/src/daemon/daemon.d.ts +174 -2
  193. package/dist/src/daemon/daemon.js +298 -7
  194. package/dist/src/daemon/daemon.js.map +1 -1
  195. package/dist/src/daemon/git-evidence.d.ts +2 -2
  196. package/dist/src/daemon/git-evidence.js +1 -1
  197. package/dist/src/daemon/projection.d.ts +13 -2
  198. package/dist/src/daemon/projection.js +39 -6
  199. package/dist/src/daemon/projection.js.map +1 -1
  200. package/dist/src/mcp/server.d.ts +39 -3
  201. package/dist/src/mcp/server.js +22 -3
  202. package/dist/src/mcp/server.js.map +1 -1
  203. package/dist/src/muse/server.d.ts +16 -0
  204. package/dist/src/muse/server.js +238 -0
  205. package/dist/src/muse/server.js.map +1 -0
  206. package/dist/src/serve/archive.d.ts +166 -0
  207. package/dist/src/serve/archive.js +363 -0
  208. package/dist/src/serve/archive.js.map +1 -0
  209. package/dist/src/serve/arguments.d.ts +93 -0
  210. package/dist/src/serve/arguments.js +228 -0
  211. package/dist/src/serve/arguments.js.map +1 -0
  212. package/dist/src/serve/credentials.d.ts +91 -0
  213. package/dist/src/serve/credentials.js +143 -0
  214. package/dist/src/serve/credentials.js.map +1 -0
  215. package/dist/src/serve/follow.d.ts +86 -0
  216. package/dist/src/serve/follow.js +101 -0
  217. package/dist/src/serve/follow.js.map +1 -0
  218. package/dist/src/serve/hook-thread.d.ts +134 -0
  219. package/dist/src/serve/hook-thread.js +333 -0
  220. package/dist/src/serve/hook-thread.js.map +1 -0
  221. package/dist/src/serve/hook-worker.d.ts +134 -0
  222. package/dist/src/serve/hook-worker.js +173 -0
  223. package/dist/src/serve/hook-worker.js.map +1 -0
  224. package/dist/src/serve/server.d.ts +412 -0
  225. package/dist/src/serve/server.js +1251 -0
  226. package/dist/src/serve/server.js.map +1 -0
  227. package/docs/README-extended.md +768 -0
  228. package/docs/cli-reference.md +2139 -67
  229. package/package.json +4 -2
  230. package/schema/event.schema.json +413 -30
  231. package/schema/fixtures/event/invalid/audit-lock-reclaimed-age-without-created.json +19 -0
  232. package/schema/fixtures/event/invalid/audit-lock-reclaimed-agent-actor.json +14 -0
  233. package/schema/fixtures/event/invalid/audit-lock-reclaimed-lockfile-path.json +14 -0
  234. package/schema/fixtures/event/invalid/audit-lock-reclaimed-operator-cleared-system-actor.json +14 -0
  235. package/schema/fixtures/event/invalid/audit-lock-reclaimed-unknown-reason.json +14 -0
  236. package/schema/fixtures/event/invalid/audit-lock-reclaimed-without-age.json +13 -0
  237. package/schema/fixtures/event/invalid/audit-reviewed-agent-actor.json +6 -1
  238. package/schema/fixtures/event/invalid/audit-reviewed-bad-payload-hash.json +21 -0
  239. package/schema/fixtures/event/invalid/audit-reviewed-loved-no-note.json +5 -0
  240. package/schema/fixtures/event/invalid/audit-reviewed-no-subject-hash.json +19 -0
  241. package/schema/fixtures/event/invalid/audit-reviewed-no-subject-seq.json +19 -0
  242. package/schema/fixtures/event/invalid/audit-reviewed-no-verdict-source.json +19 -0
  243. package/schema/fixtures/event/invalid/audit-reviewed-no-verdict.json +19 -0
  244. package/schema/fixtures/event/invalid/audit-reviewed-system-actor.json +6 -1
  245. package/schema/fixtures/event/invalid/audit-sampled-no-policy-hash.json +14 -0
  246. package/schema/fixtures/event/invalid/daemon-id-malformed.json +15 -0
  247. package/schema/fixtures/event/invalid/execution-started-truncated-policy-sha256.json +18 -0
  248. package/schema/fixtures/event/invalid/harness-cap-at-margin.json +16 -0
  249. package/schema/fixtures/event/invalid/harness-cap-fractional.json +16 -0
  250. package/schema/fixtures/event/invalid/harness-cap-negative.json +16 -0
  251. package/schema/fixtures/event/invalid/harness-cap-string.json +16 -0
  252. package/schema/fixtures/event/invalid/harness-cap-without-harness-execution.json +16 -0
  253. package/schema/fixtures/event/invalid/harness-cap-zero.json +16 -0
  254. package/schema/fixtures/event/valid/approval-requested-harness-cap.json +18 -0
  255. package/schema/fixtures/event/valid/audit-lock-reclaimed-implausible-created.json +18 -0
  256. package/schema/fixtures/event/valid/audit-lock-reclaimed-legacy.json +14 -0
  257. package/schema/fixtures/event/valid/audit-lock-reclaimed-unlock.json +19 -0
  258. package/schema/fixtures/event/valid/audit-lock-reclaimed.json +19 -0
  259. package/schema/fixtures/event/valid/audit-reviewed-reaction.json +5 -0
  260. package/schema/fixtures/event/valid/audit-reviewed.json +6 -1
  261. package/schema/fixtures/event/valid/audit-sampled.json +2 -1
  262. package/schema/fixtures/event/valid/daemon-identity-declared.json +17 -0
  263. package/schema/fixtures/event/valid/daemon-identity.json +16 -0
  264. package/schema/fixtures/event/valid/execution-started-policy-sha256.json +18 -0
  265. package/schema/fixtures/event/valid/harness-kind-hermes.json +23 -0
  266. package/schema/fixtures/policy/invalid/daemons-malformed-id.json +10 -0
  267. package/schema/fixtures/policy/valid/daemons-allowlist.json +19 -0
  268. package/schema/fixtures/policy-md/invalid/schema-invalid-agent-may-request.md +21 -0
  269. package/schema/fixtures/policy-md/valid/agent-may-request.md +24 -0
  270. package/schema/policy.schema.json +102 -6
@@ -47,6 +47,10 @@ Usage:
47
47
  [--yes] [--json]
48
48
  approval register <task-file> [--as <id>] [--log <path>] [--json]
49
49
  approval request <task> --action <key> [--as <id>] [--json]
50
+ approval propose --class <c> --key <k> --summary <s> --payload-json <j>
51
+ [--as <id>] [--json]
52
+ approval start <task> --action <key> --payload-json <j> [--as <id>]
53
+ [--json]
50
54
  approval grant|reject|revoke <action-key> [--note <text>] [--as human:<id>] [--json]
51
55
  approval expire <action-key> [--json]
52
56
  approval token <action-key> [--policy <path>] [--dir <path>] [--json]
@@ -93,6 +97,7 @@ Usage:
93
97
  approval doctor [--log <path>] [--policy <path>] [--dir <path>]
94
98
  [--api-base <url>] [--json]
95
99
  approval payload hash <file|-> [--json]
100
+ approval payload run [--cwd <dir>] [--hash] [--json] -- <cmd…>
96
101
  approval payload agentmail-draft <inbox-id> <draft-id> [--api-base <url>]
97
102
  [--json]
98
103
  approval journal write --message "<text>" | - [--task <id>] [--session <id>]
@@ -121,6 +126,11 @@ Usage:
121
126
  approval codex prepare|setup|doctor|start|serve (strict host workflow)
122
127
  approval mcp serve --as agent:<id> [--dir <path>] [--log <path>]
123
128
  [--policy <path>] (MCP over stdio; foreground)
129
+ approval serve --as agent:<id> [--dir <path>] [--log <path>]
130
+ [--policy <path>] [--port <n> | --listen <host:port>
131
+ | --listen unix:<path>]
132
+ [--allow-non-loopback] (HTTP; foreground)
133
+ approval muse [--dir <path>] [--port <n>] (local prototype; foreground)
124
134
  approval reindex [--log <path>] [--index <path>] [--force] [--json]
125
135
  approval render [--log <path>] [--out <path>] [--policy <path>]
126
136
  [--dir <path>] [--json]
@@ -184,6 +194,12 @@ Ask — an agent declares an action and acts on the answer:
184
194
  request ask the gate to admit a declared action (manual classes append
185
195
  approval.requested; supervised/autonomous append nothing and
186
196
  proceed straight to execution, per amended SPEC.md §6.3)
197
+ propose register and request one action in one call, payload inline as
198
+ JSON, in a class the policy declares by name and opens to agents
199
+ (agent_may_request); retry-safe on the same class, key and bytes
200
+ start record execution.started for a proposed action its requester is
201
+ carrying out: spends the harness grant, or records the policy's
202
+ own authorization for a non-manual class. REQUESTER-ONLY
187
203
  token report whether a live single-use execution token exists for an
188
204
  action (the RAW token is printed once, by grant, and stored nowhere)
189
205
  consume spend a token and append execution.started (internal plumbing;
@@ -204,7 +220,10 @@ Ask — an agent declares an action and acts on the answer:
204
220
  draft the agent composed, refused if the draft changed after the
205
221
  snapshot a human approved
206
222
  wait block until a task's requests are decided; the exit code IS the
207
- decision (0 granted, 1 rejected/revoked/withdrawn, 3 expired, 6 timeout)
223
+ decision (0 granted or nothing-to-wait-for, 1 rejected/revoked/
224
+ withdrawn/not-registered, 3 expired, 6 timeout, 7 void: a
225
+ re-attest voided it, ask again); run only on --json status
226
+ "granted", which means an unspent grant
208
227
  withdraw take back your OWN pending request (timeout, cancelled, superseded);
209
228
  terminal, requester-only, and a late grant then authorizes nothing
210
229
  hook put the gate in front of an agent HARNESS. "hook claude-code" and
@@ -245,6 +264,15 @@ Ask — an agent declares an action and acts on the answer:
245
264
  channels are not published, because an MCP client is an agent's
246
265
  harness and SPEC.md §11 makes the agent the untrusted policy. It
247
266
  runs as ONE agent identity, fixed at startup, that nothing changes
267
+ serve the same agent-facing surface over HTTP, for a harness in a sandbox
268
+ with no local log and no local policy. It adds the three things MCP
269
+ withheld for transport reasons: hook <harness> taking the harness
270
+ envelope as the request body, log follow paged by an exclusive
271
+ (seq, hash) cursor, and the store as an archive. TWO CREDENTIALS
272
+ from the launch environment, and authorization is per verb: the
273
+ agent one opens the hook and an allowlist of what a harness under
274
+ oversight needs to ask and to act on a grant, the tenant one opens
275
+ everything else, so the agent never reads the log it is judged by
248
276
 
249
277
  Decide — a human answers, and only a human can:
250
278
  queue the pending-decision INBOX: requests awaiting a human, inside their
@@ -300,6 +328,9 @@ Inspect — what the log says, and whether anything needs repair:
300
328
  over its RFC 8785 canonical serialization), the value a declaration
301
329
  carries and a grant binds to. Most flows never need it: "request
302
330
  --payload" hashes, verifies and stores the bytes in one step.
331
+ "payload run" prints the payload a gated command binds to: its
332
+ argv, its cwd, and the digest of the script that argv names, so a
333
+ grant over a script binds the script and not its path.
303
334
  "payload agentmail-draft" snapshots one AgentMail draft with the
304
335
  AGENT's key, so a human approves the words and not a draft id
305
336
  daemon "daemon run" is the watch loop of SPEC.md §10.2, in the FOREGROUND:
@@ -410,6 +441,7 @@ Usage:
410
441
  approval log sync [--remote <name>] [--branch <name>] [--json]
411
442
  approval log advance [--branch <name>] [--pr] [--dry-run] [--json]
412
443
  approval log checkpoint --as human:<id> [--key-file <path>] [--json]
444
+ approval log unlock --pid <n|none> --as human:<id> [--json]
413
445
 
414
446
  Subcommands:
415
447
  verify walk the hash chain end to end; clean | torn-tail | corrupt
@@ -417,11 +449,10 @@ Subcommands:
417
449
  follow verified records after an exclusive cursor, then verified appends
418
450
  sync fast-forward pull, with a snapshot and a chain reconcile
419
451
  advance commit the log's new records onto a records branch
420
- checkpoint sign the current head with your own key (human-only)
452
+ checkpoint / unlock sign the head / take over a dead writer's lock (human-only)
421
453
 
422
- verify, tail, export and follow only read. sync and advance move the FILE and append no record;
423
- checkpoint appends one. Default log: .approval/log/events.jsonl
424
- JSON shapes: docs/cli-reference.md; ${EXIT_CODES_POINTER}
454
+ verify, tail, export, follow only read; sync, advance move the FILE; checkpoint, unlock append.
455
+ Default log: .approval/log/events.jsonl. JSON shapes: docs/cli-reference.md; ${EXIT_CODES_POINTER}
425
456
  ${JSON_ERRORS}
426
457
  ${why("log")}`;
427
458
  export const FOLLOW_HELP = `approval log follow — follow verified records after a cursor
@@ -544,6 +575,27 @@ hashes they replaced, which is what \`log verify --checkpoints\` then catches.
544
575
  ${EXIT_CODES_POINTER}
545
576
  ${JSON_ERRORS}
546
577
  ${why("log-checkpoint")}`;
578
+ export const LOG_UNLOCK_HELP = `approval log unlock — take over a lock a dead writer left, by hand
579
+
580
+ Usage:
581
+ approval log unlock --pid <n|none> --as human:<id> [--log <path>] [--json]
582
+
583
+ Flags:
584
+ --pid <n|none> the pid the lockfile names (a lock-timeout refusal prints
585
+ it), or none for a lockfile that names no holder
586
+ --as human:<id> who is unlocking; or set APPROVAL_HUMAN
587
+ --log <path> log whose lock to take (default .approval/log/events.jsonl)
588
+ --json / -h, --help machine-readable output / this text
589
+
590
+ A writer takes a dead holder's lock back by itself only when it can prove the
591
+ holder gone in its own pid namespace and boot. Run this for any other lock,
592
+ once you know no writer is running: it takes the lock as a writer would and
593
+ appends audit.lock_reclaimed under your name. It refuses a --pid that is not
594
+ the lockfile's and a holder it sees running. HUMAN-ONLY.
595
+
596
+ ${EXIT_CODES_POINTER}
597
+ ${JSON_ERRORS}
598
+ ${why("log-unlock")}`;
547
599
  export const TAIL_HELP = `approval log tail — print the last records of the log
548
600
 
549
601
  Usage:
@@ -677,21 +729,21 @@ export const POLICY_TEST_HELP = policyVerbHelp("test", "check");
677
729
  export const POLICY_ATTEST_HELP = `approval policy attest — record a human's sign-off on the policy file
678
730
 
679
731
  Usage:
680
- approval policy attest [--policy <path>] [--dir <path>] [--organ <path>]
681
- [--path <path>] [--as human:<id>] [--log <path>] [--json]
732
+ approval policy attest [--policy|--dir <path>] [--organ|--path <path>]
733
+ [--bootstrap] [--as human:<id>] [--log <path>] [--json]
682
734
 
683
735
  Flags:
684
736
  --policy <path> / --dir <path> the policy file, or where to discover it
685
737
  --organ <path> attest a GATE ORGAN instead; one path per call, under --dir
686
738
  --path <path> sign off a PROTECTED path's current bytes; one path per call
739
+ --bootstrap a store's FIRST policy only; re-run: policy-already-attested
687
740
  --as human:<id> the human attesting; overrides APPROVAL_HUMAN
688
741
  --log <path> log file to append to (default .approval/log/events.jsonl)
689
742
  --json / -h, --help machine-readable output / this text
690
743
 
691
- Appends one policy.updated event with the policy file's exact SHA-256; gate
692
- operations refuse while it differs ("policy-not-attested"). Bytes, not parse.
693
- Human-only, identity CONFIG-DECLARED. What attestation proves is bounded: the
694
- trust boundary is the local machine, so someone with local control, not who.
744
+ Appends policy.updated with the file's exact SHA-256; gates refuse while it
745
+ differs ("policy-not-attested"). Bytes, not parse. Human-only, CONFIG-DECLARED:
746
+ the trust boundary is the local machine (local control, not who). No TTY needed.
695
747
  --organ appends gate.organ.attested (policy.core); --path gate.path.signed_off.
696
748
 
697
749
  JSON shape: docs/cli-reference.md#policy-attest
@@ -779,6 +831,45 @@ JSON shape: docs/cli-reference.md#request
779
831
  ${GATE_CODES_POINTER}
780
832
  ${JSON_ERRORS}
781
833
  ${why("request")}`;
834
+ export const PROPOSE_HELP = `approval propose — register and request one action, payload inline
835
+
836
+ Usage:
837
+ approval propose --class <c> --key <k> --summary <text> --payload-json <json>
838
+ [--as <id>] [--policy <p>] [--dir <p>] [--log <p>] [--json]
839
+
840
+ Flags:
841
+ --class <c> an EXACT key of the policy's classes, opened to agents by
842
+ agent_may_request: true on it or a <prefix>.* family
843
+ --key <k> "<class>:<id>", recorded verbatim as action_key (≤1 KiB)
844
+ --summary <text> the line the approver reads (in the log, in cleartext)
845
+ --payload-json <j> the payload, a JSON object of at most 262144 bytes
846
+ --as <id> human:<id> or agent:<id>; else APPROVAL_HUMAN
847
+
848
+ decision "requested": poll \`approval wait <task> --timeout 0\` (7 void or 3
849
+ expired: propose again). "autonomous"/"supervised": act, then \`approval start\`.
850
+ A retry appends nothing (idempotent:true) unless the answer can't be used.
851
+
852
+ ${GATE_CODES_POINTER}
853
+ ${JSON_ERRORS}
854
+ ${why("propose")}`;
855
+ export const START_HELP = `approval start — record that you are carrying out a proposed action
856
+
857
+ Usage:
858
+ approval start <task> --action <key> --payload-json <json> [--as <id>]
859
+ [--policy <p>] [--dir <p>] [--log <p>] [--json]
860
+
861
+ Flags:
862
+ --action <key> the action's idempotency key (required)
863
+ --payload-json <j> the bytes you act on; must hash to the registered binding
864
+ --as <id> human:<id> or agent:<id>; else APPROVAL_HUMAN
865
+
866
+ Appends one execution.started, once per key. REQUESTER-ONLY. A key with a
867
+ request spends its harness grant; a key with none is recorded as authorized by
868
+ the policy, and a manual class is refused.
869
+
870
+ ${GATE_CODES_POINTER}
871
+ ${JSON_ERRORS}
872
+ ${why("start")}`;
782
873
  function decisionHelp(verb) {
783
874
  const noun = verb === "grant" ? "approval" : verb === "reject" ? "refusal" : "withdrawal";
784
875
  const body = verb === "grant"
@@ -1012,7 +1103,7 @@ Usage:
1012
1103
  [--as <id>] [--policy <p>] [--dir <p>] [--log <p>] [--json]
1013
1104
 
1014
1105
  Flags:
1015
- --timeout <d> how long to wait, in the duration grammar (e.g. 6h). Required
1106
+ --timeout <d> how long to wait (e.g. 6h; 0 reads the state once). Required
1016
1107
  --interval <d> poll interval (default 500ms)
1017
1108
  --withdraw-on-timeout on timeout, withdraw the requests THIS actor opened
1018
1109
  --as <id> the withdrawing actor; read only with the flag above
@@ -1020,12 +1111,12 @@ Flags:
1020
1111
  --json machine-readable output; -h, --help this text
1021
1112
 
1022
1113
  Polls until every approval.requested of the task has a decision, or the timeout
1023
- elapses. WRITES NOTHING unless --withdraw-on-timeout. Only the MANUAL path
1024
- produces requests to wait for; a task with none returns at once, exit 0.
1114
+ elapses. WRITES NOTHING unless --withdraw-on-timeout. Only the MANUAL path makes
1115
+ requests; none left to wait on is exit 0, nothing-to-wait-for, never granted.
1025
1116
 
1026
1117
  JSON shape: docs/cli-reference.md#wait
1027
- ${EXIT_CODES_POINTER}. THE CODE IS THE DECISION: 0 granted, 1 rejected, revoked
1028
- or withdrawn (--json status says which), 3 expired, 4 I/O, and
1118
+ ${EXIT_CODES_POINTER}. THE CODE IS THE DECISION: 0 granted or nothing-to-wait-for,
1119
+ 1 rejected/revoked/withdrawn/not-registered (--json says which), 3 expired, 4 I/O, 7 VOID, and
1029
1120
  6 TIMEOUT — the wait elapsed with request(s) still undecided.
1030
1121
  ${JSON_ERRORS}
1031
1122
  ${why("wait")}`;
@@ -1066,9 +1157,9 @@ Flags:
1066
1157
 
1067
1158
  THIS IS NOT "approval queue": queue is what a human must answer, status is what
1068
1159
  an operator must fix. Writes nothing, and reports in one object: attestation,
1069
- verification, dangling executions, budget headroom per global limit,
1070
- loop_escalations, harness_outcomes, git coverage, payload_store, and anomalies
1071
- when there are any. The coverage numbers move neither health nor the exit code.
1160
+ verification, dangling executions, budget headroom, loop_escalations,
1161
+ harness_outcomes, payload_store, git coverage, the daemon id, plus anomalies and
1162
+ refusals when there are any. The last three report rather than judge.
1072
1163
 
1073
1164
  JSON shape: docs/cli-reference.md#status
1074
1165
  ${EXIT_CODES_POINTER} (1 when anything needs attention, including a torn tail)
@@ -1126,7 +1217,7 @@ export const AUDIT_HELP = `approval audit — the retrospective review of sample
1126
1217
 
1127
1218
  Usage:
1128
1219
  approval audit list [--all] [--log <path>] [--json]
1129
- approval audit review <seq|action-key> [--deny] [--note "<text>"] […]
1220
+ approval audit review <seq|action-key> (--ok | --deny) [--note "<text>"] […]
1130
1221
  approval audit obligations [--all] [--log <path>] [--json]
1131
1222
  approval audit reconcile <obligation-seq> --note "<text>" [--revert <key>] […]
1132
1223
 
@@ -1172,23 +1263,23 @@ ${why("audit-list")}`;
1172
1263
  export const AUDIT_REVIEW_HELP = `approval audit review — record that a human reviewed a sample
1173
1264
 
1174
1265
  Usage:
1175
- approval audit review <seq|action-key> [--deny] [--note "<text>"]
1266
+ approval audit review <seq|action-key> (--ok | --deny) [--note "<text>"]
1176
1267
  [--reaction <w>] [--as human:<id>] [--log <path>] [--json]
1177
1268
 
1178
1269
  Arguments:
1179
- <seq|action-key> a bare integer is the SEQ OF THE audit.sampled RECORD; any
1180
- other value is an action key with one open sample
1270
+ <seq|action-key> SEQ of the audit.sampled record, or a key with 1 open sample
1181
1271
  Flags:
1272
+ --ok this action should have happened. One of --ok/--deny REQUIRED
1182
1273
  --deny this action should NOT have happened. Opens an obligation
1183
1274
  --note <text> what you concluded. OPTIONAL, but loved/disliked REQUIRE it
1184
1275
  --reaction <w> disliked|indifferent|liked|loved. GUIDANCE, never enforcement
1185
1276
  --as human:<id> the reviewer; else APPROVAL_HUMAN. HUMAN-ONLY
1186
1277
  --log <path> / --json / -h, --help the log / machine-readable output / help
1187
1278
 
1188
- Appends audit.reviewed. NO ATTESTATION IS REQUIRED. Refuses (exit 1) not-sampled,
1189
- already-reviewed, ambiguous-subject, actor-not-human, note-required and
1190
- reaction-conflicts-verdict (--deny with liked or loved); log untouched. --deny
1191
- ALSO appends reconciliation.required, shaped by the DECLARED reversible, not you.
1279
+ Refuses (exit 1), log untouched: actor-not-human, note-required, not-sampled,
1280
+ reaction-conflicts-verdict (--deny + liked/loved), already-reviewed,
1281
+ ambiguous-subject, actor-not-approver (reviewer or --as off the class's roster),
1282
+ policy-not-attested, policy-invalid, then verdict-required (no --ok or --deny).
1192
1283
  JSON: docs/cli-reference.md#audit-review
1193
1284
  ${EXIT_CODES_POINTER}
1194
1285
  ${JSON_ERRORS}
@@ -1319,14 +1410,13 @@ Usage:
1319
1410
  approval channel web [--port <n>] [--payload-dir <path>] [--as human:<id>]
1320
1411
  [--policy <path>] [--dir <path>] [--log <path>] [--json]
1321
1412
  approval channel telegram listen|health [--once] [--as human:<id>] [--json]
1413
+ approval channel relay [--listen [host:]port | --port <n>] [--proposer <a>] [--json]
1322
1414
 
1323
1415
  Subcommands:
1324
- cli render the pending queue in this terminal and, when it IS a
1325
- terminal, collect decisions with a prompt
1326
- web serve the pending queue as a page on 127.0.0.1 ONLY, with
1327
- Grant/Reject forms and a batch gesture
1328
- telegram deliver the queue to a Telegram chat and long-poll for
1329
- Approve/Reject taps
1416
+ cli render the pending queue here; on a terminal, collect decisions
1417
+ web the queue as a page on 127.0.0.1 ONLY, with Grant/Reject forms
1418
+ telegram deliver the queue to a Telegram chat; collect Approve/Reject taps
1419
+ relay take gestures an operator's authenticated relay carries (EdgeOS)
1330
1420
 
1331
1421
  A channel is TRANSPORT: it renders what the runtime derived and reports the
1332
1422
  gesture a human made. Every decision collected here is recorded by the same
@@ -1435,7 +1525,7 @@ ${why("quickstart")}`;
1435
1525
  export const HOOK_HELP = `approval hook — put the gate in front of an agent harness
1436
1526
 
1437
1527
  Usage:
1438
- approval hook claude-code|cursor|codex|grok|muse [--as agent:<id>] [--timeout <d>] [--interval <d>] [--retry-grace <d>] [--policy <p>] [--dir <p>] [--log <p>]
1528
+ approval hook claude-code|cursor|codex|grok|muse|hermes [--as agent:<id>] [--timeout <d>] [--interval <d>] [--retry-grace <d>] [--harness-cap <d>] [--policy <p>] [--dir <p>] [--log <p>]
1439
1529
  approval hook classify [--json] [--policy <p>] [--dir <p>] -- <command…>
1440
1530
 
1441
1531
  Commands:
@@ -1444,16 +1534,16 @@ Commands:
1444
1534
  codex Codex synchronous Pre/Post JSON; Bash denied, direct apply_patch experimentally gated
1445
1535
  grok Grok Build camelCase Pre/PostToolUse JSON in; {decision,reason} out, DENY IS EXIT 2. \`approval hook grok --help\` prints the config
1446
1536
  muse Muse Code snake_case Pre/PostToolUse; ONE dialect out or it fails open. \`approval hook muse --help\` prints the config
1537
+ hermes Hermes Agent snake_case pre_tool_call/post_tool_call; {action,message} out, DENY IS EXIT 2, allow is {}. fail_closed: true BLOCKS a broken hook (observed on main 118984d7; default false, and v0.21.3 ignores it silently). \`approval hook hermes --help\` prints the YAML
1447
1538
  classify print what the classifier makes of a command line and exit
1448
1539
  --as <id> proposing identity (default agent:<harness>)
1449
- --timeout/--interval/--retry-grace <d> wait / poll / hold for a retry (9m/1s/5m)
1540
+ --timeout/--interval/--retry-grace/--harness-cap <d> wait / poll / hold for a retry / the harness's OWN kill timeout (9m/1s/5m/none), which lapses the request 60s inside it so approval.expired lands first
1450
1541
  --dir/--policy/--log <p> policy+log root; --dir sets BOTH, default primary
1451
1542
  -h, --help this text
1452
-
1453
1543
  Codex opt-in: register exact Bash|apply_patch synchronously with timeout 600s (default wait 9m). Bash is denied because native events hide per-call workdir; direct apply_patch is experimental. PostToolUse is diagnostic.
1454
1544
 
1455
- Deny: hook-unclassified, hook-class-human-only, hook-harness-launch-unruled, hook-opaque, hook-unparseable, hook-rejected, hook-revoked, hook-expired, hook-withdrawn, hook-timeout,
1456
- hook-gate-refused:<c>, hook-grant-unverified, hook-sandbox-required, hook-policy-unavailable, hook-log-unreachable, hook-unsupported-execution-context, hook-muse-contributor-model, hook-io.
1545
+ Deny: hook-unclassified, hook-class-human-only, hook-harness-launch-unruled, hook-opaque, hook-unparseable, hook-rejected, hook-revoked, hook-expired, hook-withdrawn, hook-timeout, hook-harness-cap-too-short,
1546
+ hook-gate-refused:<c>, hook-grant-unverified, hook-sandbox-required, hook-policy-unavailable, hook-log-unreachable, hook-unsupported-execution-context, hook-muse-contributor-model, hook-hermes-execute-code-unbound, hook-io.
1457
1547
 
1458
1548
  ${EXIT_CODES_POINTER} (harness verbs use 0 and 2 only; 0 is a verdict, never "ask")
1459
1549
  ${why("hook")}`;
@@ -1461,7 +1551,7 @@ export const HOOK_GROK_HELP = `approval hook grok — the gate in front of Grok
1461
1551
 
1462
1552
  Usage:
1463
1553
  approval hook grok [--as agent:<id>] [--timeout <d>] [--interval <d>]
1464
- [--retry-grace <d>] [--policy <p>] [--dir <p>] [--log <p>]
1554
+ [--retry-grace <d>] [--harness-cap <d>] [--policy <p>] [--dir <p>] [--log <p>]
1465
1555
 
1466
1556
  camelCase in (hookEventName, sessionId, cwd, workspaceRoot, toolName, toolInput);
1467
1557
  {"decision":"allow"|"deny","reason":"…"} out, never "ask". DENY IS EXIT 2,
@@ -1486,7 +1576,7 @@ export const HOOK_MUSE_HELP = `approval hook muse — the gate in front of Meta
1486
1576
 
1487
1577
  Usage:
1488
1578
  approval hook muse [--as agent:<id>] [--timeout <d>] [--interval <d>]
1489
- [--retry-grace <d>] [--policy <p>] [--dir <p>] [--log <p>]
1579
+ [--retry-grace <d>] [--harness-cap <d>] [--policy <p>] [--dir <p>] [--log <p>]
1490
1580
  snake_case in (hook_event_name, tool_name, tool_input, cwd, model); ONE dialect
1491
1581
  out, nested {"hookSpecificOutput":{"permissionDecision":…}} at exit 0, never
1492
1582
  "ask". Tools: bash (per-call workdir), write_file, read_file, search.
@@ -1507,6 +1597,31 @@ That "timeout" MUST EXCEED --timeout (9m), or Muse abandons the call mid-wait
1507
1597
  and, failing open, runs it while a human is still deciding.
1508
1598
  ${EXIT_CODES_POINTER} (0 for every verdict; deny is the body, not the code)
1509
1599
  ${why("hook")}`;
1600
+ export const HOOK_HERMES_HELP = `approval hook hermes — the gate in front of Hermes Agent (APRV-398)
1601
+ Usage:
1602
+ approval hook hermes [--as agent:<id>] [--timeout <d>] [--interval <d>]
1603
+ [--retry-grace <d>] [--harness-cap <d>] [--policy <p>] [--dir <p>] [--log <p>]
1604
+ snake_case in (hook_event_name pre_tool_call|post_tool_call, tool_name, tool_input).
1605
+ ONE dialect out: {"action":"block","message":"…"} AT EXIT 2 deny, {} at exit 0 allow
1606
+ (no allow directive; reason on stderr). Tools: terminal (command + ABSOLUTE workdir),
1607
+ write_file, patch, read_file, search_files. REFUSED: execute_code
1608
+ (hook-hermes-execute-code-unbound), and a call with no absolute workdir or a relative
1609
+ or missing path (hook-unsupported-execution-context): Hermes resolves those against a session directory no event reports.
1610
+ OBSERVED on main 118984d7: fail_closed: true BLOCKS hook crash, timeout and garbage.
1611
+ ITS DEFAULT IS false, and v0.21.3 (2026.9.14) fails open SILENTLY: run a 2026-09-20+ build.
1612
+ No hooks_auto_accept and no TTY: Hermes SILENTLY SKIPS REGISTERING THE HOOK. --dir IS MANDATORY (a gateway session's cwd is the user HOME); docs/hermes-hook.md has the evidence.
1613
+ The file the human commits, $HERMES_HOME/config.yaml — the home's LAST SEGMENT must be .hermes or the classifier does not see the organ (export HERMES_HOME=/Users/you/dev/hermes/.hermes; policy.core):
1614
+ plugins: { hook_callback_timeout: 600 }
1615
+ hooks_auto_accept: true
1616
+ hooks:
1617
+ pre_tool_call:
1618
+ - { command: "approval hook hermes --dir <repo> --timeout 4m --harness-cap 300s", timeout: 300, fail_closed: true }
1619
+ post_tool_call:
1620
+ - { command: "approval hook hermes --dir <repo> --timeout 4m", timeout: 300 }
1621
+ The entry "timeout" caps at 300s; hook_callback_timeout (default 30s) fails closed
1622
+ by itself, so --timeout MUST BE UNDER 300s (4m). WITHOUT --harness-cap the hook assumes the 30s default and denies every manual call hook-harness-cap-too-short: raise hook_callback_timeout above the entry timeout, then pass --harness-cap <the smaller of the two>. The window is then cap-60s (240s above) or approval_ttl, whichever is shorter. Register BOTH events. Long form:
1623
+ ${EXIT_CODES_POINTER} (0 allow, 2 deny; post_tool_call always 0; never "ask")
1624
+ ${why("hook")}`;
1510
1625
  export const IMPORT_HELP = `approval import — turn existing permissions prose into a draft policy
1511
1626
 
1512
1627
  Usage:
@@ -1548,12 +1663,15 @@ export const PAYLOAD_HELP = `approval payload — work with the bytes an approva
1548
1663
 
1549
1664
  Usage:
1550
1665
  approval payload hash <file|-> [--json]
1666
+ approval payload run [--cwd <dir>] [--hash] [--json] -- <cmd…>
1551
1667
  approval payload agentmail-draft <inbox-id> <draft-id> [--api-base <url>]
1552
1668
  [--json]
1553
1669
 
1554
1670
  Commands:
1555
1671
  hash print the payload_hash of a JSON document: SHA-256 over its RFC 8785
1556
1672
  canonical serialization (SPEC.md §6.2)
1673
+ run print the payload \`approval run\` will recompute for a command: its
1674
+ argv, its cwd, and the digest of the script that argv names
1557
1675
  agentmail-draft
1558
1676
  snapshot one AgentMail draft as the payload a grant can bind to,
1559
1677
  read with the AGENT's key from AGENTMAIL_API_KEY
@@ -1585,6 +1703,30 @@ JSON shape (stdout, one object): {"ok":true,"hash":"<64hex>"}
1585
1703
  ${EXIT_CODES_POINTER}
1586
1704
  ${JSON_ERRORS}
1587
1705
  ${why("payload-hash")}`;
1706
+ export const PAYLOAD_RUN_HELP = `approval payload run — the payload a gated command binds to
1707
+
1708
+ Usage:
1709
+ approval payload run [--cwd <dir>] [--hash] [--json] -- <cmd…>
1710
+
1711
+ Flags:
1712
+ --cwd <dir> the directory the command will run in (default: this one)
1713
+ --hash print the payload_hash instead of the payload bytes
1714
+ --json / -h, --help machine-readable (with --hash) / this text
1715
+
1716
+ Prints what \`approval run\` recomputes before it spawns:
1717
+ {"argv":[…],"cwd":"…","script":{"argv_index":N,"path":…,bytes,sha256}}
1718
+
1719
+ THE SCRIPT IS THE POINT (APRV-401): a known interpreter followed by a path, or
1720
+ a path at argv[0], binds THAT FILE'S BYTES, so a script edited before the run is
1721
+ payload-mismatch rather than a silent substitution. An argv naming no script
1722
+ carries no "script" key and hashes as it always did. NOT bound: an inline
1723
+ program, the interpreter, an unlisted launcher, what the script itself runs.
1724
+
1725
+ approval payload run --hash -- bash install.sh # the declaration
1726
+ approval payload run -- bash install.sh | approval request … --payload -
1727
+ ${EXIT_CODES_POINTER}
1728
+ ${JSON_ERRORS}
1729
+ ${why("payload-run")}`;
1588
1730
  export const PAYLOAD_AGENTMAIL_DRAFT_HELP = `approval payload agentmail-draft — snapshot a draft as an approvable payload
1589
1731
 
1590
1732
  Usage:
@@ -1754,22 +1896,23 @@ ${why("render")}`;
1754
1896
  export const TELEGRAM_HELP = `approval channel telegram — the Telegram push channel
1755
1897
 
1756
1898
  Usage:
1757
- approval channel telegram listen [--once] [--as human:<id>] [--payloads <f>]
1758
- [--policy <path>] [--dir <path>]
1759
- [--log <path>] [--api-base <url>]
1760
- [--poll-timeout <seconds>] [--json]
1761
- approval channel telegram health [--dir <path>] [--log <path>] [--json]
1899
+ approval channel telegram listen [--once] [--as human:<id>] [--payloads <f>]
1900
+ [--policy <p>] [--dir <p>] [--log <p>] [--api-base <url>] [--poll-timeout <s>] [--json]
1901
+ approval channel telegram webhook --url <https://...> [--listen [host:]port]
1902
+ [--path <p>] [--cycle <d>] and every listen flag above
1903
+ approval channel telegram health [--dir <path>] [--log <path>] [--json]
1904
+
1905
+ Two transports, MUTUALLY EXCLUSIVE per bot: listen long-polls getUpdates;
1906
+ webhook has Telegram post each update to a url you register (no poller).
1762
1907
 
1763
1908
  Configuration is ENVIRONMENT-ONLY: APPROVAL_TG_TOKEN holds the bot token and
1764
- APPROVAL_TG_CHAT the approver chat id by default. APPROVAL.md carries only those
1765
- variable NAMES — a second gate on one machine declares a pair of its own — and
1766
- there is no flag that would put a bot token into a shell history.
1909
+ APPROVAL_TG_CHAT the approver chat id; APPROVAL.md carries only those NAMES,
1910
+ and no flag puts a bot token into a shell history.
1767
1911
 
1768
1912
  Anyone in the configured chat can approve as the actor this process was started
1769
1913
  with, so the chat's membership is part of your trust boundary. Use a private
1770
1914
  chat with the bot — or map senders: approvers.<id>.senders.telegram: "<id>"
1771
- records each decision against the person that Telegram account is attested to
1772
- and refuses a tap from an account the policy does not name.
1915
+ records each decision against the attested person and refuses every other account.
1773
1916
 
1774
1917
  ${EXIT_CODES_POINTER}
1775
1918
  ${JSON_ERRORS}
@@ -1779,16 +1922,16 @@ export const TELEGRAM_LISTEN_HELP = `approval channel telegram listen — delive
1779
1922
  Usage:
1780
1923
  approval channel telegram listen [--once] [--as human:<id>] [--payloads <f>]
1781
1924
  [--policy <p>] [--dir <p>] [--log <p>] [--no-gloss]
1782
- [--gloss-provider <claude|codex>] [--gloss-model <id>] [--api-base <url>] [--poll-timeout <s>] [--json]
1925
+ [--gloss-provider <claude|codex>] [--gloss-model <id>] [--api-base <url>] [--no-stale-copy] [--poll-timeout <s>] [--json]
1783
1926
 
1784
1927
  Flags:
1785
- --once / --json one getUpdates batch then exit / ONE JSON OBJECT PER LINE
1786
1928
  --no-gloss / --gloss-provider <p> drop gloss (ON by default) / choose claude|codex (default claude)
1787
1929
  --gloss-model <id> model to request; required with Codex; no fallback
1788
1930
  --as human:<id> the approver every decision is recorded against. REQUIRED
1789
1931
  --payloads <f> OPTIONAL OVERRIDE: JSON file of action key -> payload
1790
1932
  --policy <p> / --dir <p> / --log <p> the policy, its dir, the log written to
1791
1933
  --api-base <url> / --poll-timeout <s> / --allow-cross-instance Bot API base / long-poll seconds (25) / start on another instance's bot
1934
+ --no-stale-copy / --once / --json refuse a button this process is not holding (implied by any --api-base but the Bot API) / one getUpdates batch then exit / ONE JSON OBJECT PER LINE
1792
1935
  -h, --help this text
1793
1936
  Config is ENVIRONMENT-ONLY and the policy names the variables. Delivery is per cycle;
1794
1937
  a new request reaches the phone without restart. THE TOKEN IS PRINTED HERE, NEVER SENT TO TELEGRAM.
@@ -1798,6 +1941,54 @@ JSON shape: docs/cli-reference.md#channel-telegram-listen
1798
1941
  ${EXIT_CODES_POINTER}
1799
1942
  ${JSON_ERRORS}
1800
1943
  ${why("channel-telegram-listen")}`;
1944
+ export const TELEGRAM_WEBHOOK_HELP = `approval channel telegram webhook — decisions arrive by webhook, no poller
1945
+
1946
+ Usage:
1947
+ approval channel telegram webhook --url <https://host/path> [--path <p>]
1948
+ [--listen [host:]port | --port <n>] [--allow-non-loopback] [--cycle <d>]
1949
+ [--reclaim] [--as human:<id>] [--policy <p>] [--dir <p>] [--log <p>]
1950
+ [--payloads <f>] [--api-base <url>] [--no-stale-copy] [--no-gloss] [--json]
1951
+
1952
+ Flags:
1953
+ --url <https://...> REQUIRED: the PUBLIC url registered with setWebhook. https only, on port 443, 80, 88 or 8443 (Telegram's list), and no user:password
1954
+ --port <n> / --listen <[host:]port> this process's own bind. LOOPBACK by default (4683); routable also needs --allow-non-loopback. Never 0
1955
+ --path <p> / --cycle <d> must equal the url's own path / dispatch period (30s)
1956
+ --reclaim / --no-stale-copy register over the webhook already holding this bot / refuse a button this process is not holding (implied by any --api-base but the Bot API)
1957
+ -h, --help this text
1958
+
1959
+ APPROVAL_TG_WEBHOOK_SECRET is REQUIRED, from the launch environment: it is
1960
+ Telegram's secret_token, echoed on every delivery, and a post without the
1961
+ matching header is refused and never a decision. NO TLS HERE: plain HTTP
1962
+ behind a proxy or tunnel you own (--url names it); long polling then refuses.
1963
+
1964
+ JSON shape: docs/cli-reference.md#channel-telegram-webhook
1965
+ ${EXIT_CODES_POINTER}
1966
+ ${JSON_ERRORS}
1967
+ ${why("channel-telegram-webhook")}`;
1968
+ export const CHANNEL_RELAY_HELP = `approval channel relay — gestures an operator's authenticated relay carries
1969
+
1970
+ Usage:
1971
+ approval channel relay [--listen [host:]port | --port <n>] [--allow-non-loopback]
1972
+ [--proposer agent:<id>] [--policy <p>] [--dir <p>] [--log <p>] [--json]
1973
+
1974
+ Flags:
1975
+ --port <n> / --listen <[host:]port> LOOPBACK by default (4684); routable also needs --allow-non-loopback
1976
+ --proposer <actor> who a propose gesture is recorded under (default agent:edgeos-relay)
1977
+ -h, --help this text
1978
+
1979
+ APPROVAL_RELAY_SECRET is REQUIRED, from the launch environment: the control
1980
+ plane sends it in x-approval-relay-secret, and a post without it is refused
1981
+ and never written to the log. POST /relay/gesture takes one closed JSON body:
1982
+ propose, attest or decline a policy by sha256, or grant or reject a request by
1983
+ action key, each with a nonce and issued_at. The sender is an EdgeOS id, and
1984
+ it is resolved against approvers.<id>.senders.edgeos in the policy in force:
1985
+ an unmapped account is refused sender-unmapped. This process holds NO human
1986
+ identity and NO TLS. Trust: the daemon trusts the relay's attribution.
1987
+
1988
+ JSON shape: docs/cli-reference.md#channel-relay
1989
+ ${EXIT_CODES_POINTER}
1990
+ ${JSON_ERRORS}
1991
+ ${why("channel-relay")}`;
1801
1992
  export const TELEGRAM_HEALTH_HELP = `approval channel telegram health — is this runtime configured for Telegram?
1802
1993
 
1803
1994
  Usage:
@@ -1869,13 +2060,13 @@ export const UP_HELP = `approval up — the daemon and every configured channel,
1869
2060
 
1870
2061
  Usage:
1871
2062
  approval up [every "daemon run" flag] [--as human:<id>] [--port <n>]
1872
- [--payloads <f>] [--payload-dir <d>] [--api-base <url>] [--poll-timeout <s>] [--no-gloss]
2063
+ [--payloads <f>] [--payload-dir <d>] [--api-base <url>] [--no-stale-copy] [--poll-timeout <s>] [--no-gloss]
1873
2064
  [--gloss-provider <claude|codex>] [--gloss-model <id>] [--no-telegram] [--no-web] [--no-preflight] [--no-build]
1874
2065
 
1875
2066
  Flags (every "daemon run" flag, unchanged, plus):
1876
2067
  --as human:<id> the approver every decision is recorded against
1877
2068
  --payloads <f> / --payload-dir <d> payload overrides: telegram / web
1878
- --api-base <url> / --poll-timeout <s> Bot API base / long-poll seconds
2069
+ --api-base <url> / --poll-timeout <s> / --no-stale-copy Bot API base / long-poll seconds / refuse a button this process is not holding (implied by any base but the Bot API)
1879
2070
  --port <n> queue-page port. Precedence: --port, channels.web.port
1880
2071
  --no-telegram / --no-web / --allow-cross-instance leave that channel out / start on another instance's bot
1881
2072
  --no-gloss / --restart-backoff <d> drop gloss / first retry wait
@@ -2441,4 +2632,42 @@ SERIALLY, THIS SERVER READS NO .approval/env. POST-V1: tasks/elicitation.
2441
2632
  ${EXIT_CODES_POINTER} (2 is a startup refusal; 0 is a clean shutdown)
2442
2633
  ${JSON_ERRORS}
2443
2634
  ${why("mcp-serve")}`;
2635
+ export const MUSE_HELP = `approval muse — local synthetic Muse consumer prototype (FOREGROUND)
2636
+
2637
+ Usage:
2638
+ approval muse [--dir <path>] [--port <n>]
2639
+
2640
+ APPROVAL_MUSE_TENANT identifies one tenant per process. Distinct
2641
+ APPROVAL_MUSE_READ_TOKEN and APPROVAL_MUSE_PROPOSE_TOKEN (24+ characters) are
2642
+ required in the launch environment. Listens only on 127.0.0.1, port 4683 by
2643
+ default. This is a local prototype, not a verified native Muse protocol.
2644
+ Human decisions use the already configured Telegram listener. No decision,
2645
+ execution, export, or generic verb endpoint exists here.
2646
+
2647
+ ${EXIT_CODES_POINTER} (2 is a startup refusal; 0 is a clean shutdown)
2648
+ ${why("muse")}`;
2649
+ export const SERVE_HELP = `approval serve — the agent-facing surface over HTTP (FOREGROUND)
2650
+
2651
+ Usage:
2652
+ approval serve [--as agent:<id>] [--dir <p>] [--log <p>] [--policy <p>]
2653
+ [--port <n> | --listen <host:port>|unix:<path>]
2654
+
2655
+ Flags:
2656
+ --as agent:<id> the identity EVERY call is recorded under, or APPROVAL_AGENT
2657
+ --dir/--log/--policy <p> the store root, and the log and policy pinned
2658
+ --port <n>=4682 loopback. --listen <host:port> widens, non-loopback ALSO needs --allow-non-loopback; unix:<path> or APPROVAL_SERVE_LISTEN: a socket
2659
+ --hook-timeout/--hook-harness-cap <d> pinned on every hook call, as the stdin form's --timeout and --harness-cap (the CALLER's own kill timeout)
2660
+ --hook-threads <n>=16 / --hook-queue <n>=64 hook calls running at once (a thread each) / waiting; past both, refused serve-hook-saturated
2661
+
2662
+ For a harness in a sandbox with no local log and no policy. THE VERBS ARE mcp
2663
+ serve's: the registry less human_only, --as absent from every schema, the
2664
+ identity appended last, no grant. It adds hook/<harness> (envelope in, the
2665
+ bytes stdin prints out), log/follow (paged, exclusive (seq, hash) cursor) and
2666
+ export (the store, minus keys, env and vault). AUTHORIZATION IS PER VERB:
2667
+ APPROVAL_SERVE_AGENT_TOKEN opens the hook and an allowlist (ask, and act on a
2668
+ grant); APPROVAL_SERVE_TENANT_TOKEN opens the rest, records included. NO TLS.
2669
+
2670
+ ${EXIT_CODES_POINTER} (2 is a startup refusal; 0 is a clean shutdown)
2671
+ ${JSON_ERRORS}
2672
+ ${why("serve")}`;
2444
2673
  //# sourceMappingURL=help.js.map