approval-md 0.3.0 → 0.4.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 (226) hide show
  1. package/README.md +83 -914
  2. package/SPEC.md +98 -13
  3. package/cli.js +66 -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 +19 -0
  8. package/dist/src/channels/contract.js +105 -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.js +6 -1
  17. package/dist/src/channels/tagging.js.map +1 -1
  18. package/dist/src/channels/telegram-webhook.d.ts +273 -0
  19. package/dist/src/channels/telegram-webhook.js +536 -0
  20. package/dist/src/channels/telegram-webhook.js.map +1 -0
  21. package/dist/src/channels/telegram.d.ts +271 -1
  22. package/dist/src/channels/telegram.js +403 -45
  23. package/dist/src/channels/telegram.js.map +1 -1
  24. package/dist/src/cli/amend.js +350 -4
  25. package/dist/src/cli/amend.js.map +1 -1
  26. package/dist/src/cli/attest.js +43 -6
  27. package/dist/src/cli/attest.js.map +1 -1
  28. package/dist/src/cli/channel-relay.d.ts +108 -0
  29. package/dist/src/cli/channel-relay.js +334 -0
  30. package/dist/src/cli/channel-relay.js.map +1 -0
  31. package/dist/src/cli/channel-telegram-webhook.d.ts +273 -0
  32. package/dist/src/cli/channel-telegram-webhook.js +709 -0
  33. package/dist/src/cli/channel-telegram-webhook.js.map +1 -0
  34. package/dist/src/cli/channel-telegram.d.ts +272 -26
  35. package/dist/src/cli/channel-telegram.js +478 -41
  36. package/dist/src/cli/channel-telegram.js.map +1 -1
  37. package/dist/src/cli/channel.d.ts +1 -1
  38. package/dist/src/cli/channel.js +5 -1
  39. package/dist/src/cli/channel.js.map +1 -1
  40. package/dist/src/cli/daemon.js +37 -5
  41. package/dist/src/cli/daemon.js.map +1 -1
  42. package/dist/src/cli/doctor.d.ts +63 -0
  43. package/dist/src/cli/doctor.js +856 -111
  44. package/dist/src/cli/doctor.js.map +1 -1
  45. package/dist/src/cli/execute.d.ts +17 -1
  46. package/dist/src/cli/execute.js +341 -20
  47. package/dist/src/cli/execute.js.map +1 -1
  48. package/dist/src/cli/exit-codes.d.ts +12 -0
  49. package/dist/src/cli/exit-codes.js +13 -0
  50. package/dist/src/cli/exit-codes.js.map +1 -1
  51. package/dist/src/cli/gate.d.ts +2 -0
  52. package/dist/src/cli/gate.js +233 -2
  53. package/dist/src/cli/gate.js.map +1 -1
  54. package/dist/src/cli/help.d.ts +9 -1
  55. package/dist/src/cli/help.js +246 -38
  56. package/dist/src/cli/help.js.map +1 -1
  57. package/dist/src/cli/hook.d.ts +371 -3
  58. package/dist/src/cli/hook.js +1926 -44
  59. package/dist/src/cli/hook.js.map +1 -1
  60. package/dist/src/cli/init.js +16 -7
  61. package/dist/src/cli/init.js.map +1 -1
  62. package/dist/src/cli/instructions.js +24 -3
  63. package/dist/src/cli/instructions.js.map +1 -1
  64. package/dist/src/cli/log-anchor.d.ts +55 -0
  65. package/dist/src/cli/log-anchor.js +199 -60
  66. package/dist/src/cli/log-anchor.js.map +1 -1
  67. package/dist/src/cli/main.d.ts +0 -12
  68. package/dist/src/cli/main.js +51 -0
  69. package/dist/src/cli/main.js.map +1 -1
  70. package/dist/src/cli/muse.d.ts +2 -0
  71. package/dist/src/cli/muse.js +62 -0
  72. package/dist/src/cli/muse.js.map +1 -0
  73. package/dist/src/cli/payload.js +79 -1
  74. package/dist/src/cli/payload.js.map +1 -1
  75. package/dist/src/cli/scaffold.js +6 -0
  76. package/dist/src/cli/scaffold.js.map +1 -1
  77. package/dist/src/cli/serve.d.ts +53 -0
  78. package/dist/src/cli/serve.js +270 -0
  79. package/dist/src/cli/serve.js.map +1 -0
  80. package/dist/src/cli/up.js +21 -2
  81. package/dist/src/cli/up.js.map +1 -1
  82. package/dist/src/cli/verb-registry.d.ts +2 -0
  83. package/dist/src/cli/verb-registry.js +337 -39
  84. package/dist/src/cli/verb-registry.js.map +1 -1
  85. package/dist/src/cli/wordmark.d.ts +2 -2
  86. package/dist/src/cli/wordmark.js +2 -2
  87. package/dist/src/core/attest-bootstrap.d.ts +95 -0
  88. package/dist/src/core/attest-bootstrap.js +135 -0
  89. package/dist/src/core/attest-bootstrap.js.map +1 -0
  90. package/dist/src/core/attest.d.ts +20 -3
  91. package/dist/src/core/attest.js +25 -0
  92. package/dist/src/core/attest.js.map +1 -1
  93. package/dist/src/core/audit.d.ts +55 -3
  94. package/dist/src/core/audit.js +52 -5
  95. package/dist/src/core/audit.js.map +1 -1
  96. package/dist/src/core/channel-lease.d.ts +322 -0
  97. package/dist/src/core/channel-lease.js +663 -0
  98. package/dist/src/core/channel-lease.js.map +1 -0
  99. package/dist/src/core/command-class.d.ts +51 -0
  100. package/dist/src/core/command-class.js +760 -8
  101. package/dist/src/core/command-class.js.map +1 -1
  102. package/dist/src/core/daemon-host.d.ts +178 -0
  103. package/dist/src/core/daemon-host.js +228 -0
  104. package/dist/src/core/daemon-host.js.map +1 -0
  105. package/dist/src/core/daemon-identity.d.ts +238 -0
  106. package/dist/src/core/daemon-identity.js +252 -0
  107. package/dist/src/core/daemon-identity.js.map +1 -0
  108. package/dist/src/core/gate.d.ts +306 -5
  109. package/dist/src/core/gate.js +772 -24
  110. package/dist/src/core/gate.js.map +1 -1
  111. package/dist/src/core/harness-version.d.ts +128 -4
  112. package/dist/src/core/harness-version.js +179 -12
  113. package/dist/src/core/harness-version.js.map +1 -1
  114. package/dist/src/core/harness-wait.d.ts +82 -3
  115. package/dist/src/core/harness-wait.js +89 -3
  116. package/dist/src/core/harness-wait.js.map +1 -1
  117. package/dist/src/core/log-subscribe.d.ts +21 -0
  118. package/dist/src/core/log-subscribe.js +16 -6
  119. package/dist/src/core/log-subscribe.js.map +1 -1
  120. package/dist/src/core/log-write-layer.d.ts +55 -0
  121. package/dist/src/core/log-write-layer.js +91 -0
  122. package/dist/src/core/log-write-layer.js.map +1 -0
  123. package/dist/src/core/log.d.ts +50 -21
  124. package/dist/src/core/log.js +197 -19
  125. package/dist/src/core/log.js.map +1 -1
  126. package/dist/src/core/payload-census.d.ts +51 -0
  127. package/dist/src/core/payload-census.js +125 -2
  128. package/dist/src/core/payload-census.js.map +1 -1
  129. package/dist/src/core/payload-store.d.ts +31 -0
  130. package/dist/src/core/payload-store.js +113 -8
  131. package/dist/src/core/payload-store.js.map +1 -1
  132. package/dist/src/core/payload.d.ts +18 -3
  133. package/dist/src/core/payload.js +20 -4
  134. package/dist/src/core/payload.js.map +1 -1
  135. package/dist/src/core/policy-diff.d.ts +10 -0
  136. package/dist/src/core/policy-diff.js +10 -2
  137. package/dist/src/core/policy-diff.js.map +1 -1
  138. package/dist/src/core/policy-explain.d.ts +7 -1
  139. package/dist/src/core/policy-explain.js +16 -1
  140. package/dist/src/core/policy-explain.js.map +1 -1
  141. package/dist/src/core/policy-load.d.ts +24 -0
  142. package/dist/src/core/policy-load.js.map +1 -1
  143. package/dist/src/core/policy-match.d.ts +38 -0
  144. package/dist/src/core/policy-match.js +35 -0
  145. package/dist/src/core/policy-match.js.map +1 -1
  146. package/dist/src/core/policy-proposal.d.ts +23 -0
  147. package/dist/src/core/policy-proposal.js +8 -2
  148. package/dist/src/core/policy-proposal.js.map +1 -1
  149. package/dist/src/core/read-scope.d.ts +10 -0
  150. package/dist/src/core/read-scope.js +4 -0
  151. package/dist/src/core/read-scope.js.map +1 -1
  152. package/dist/src/core/run-payload.d.ts +160 -0
  153. package/dist/src/core/run-payload.js +304 -0
  154. package/dist/src/core/run-payload.js.map +1 -0
  155. package/dist/src/core/sender-identity.d.ts +35 -10
  156. package/dist/src/core/sender-identity.js +48 -11
  157. package/dist/src/core/sender-identity.js.map +1 -1
  158. package/dist/src/core/state.d.ts +52 -1
  159. package/dist/src/core/state.js +52 -6
  160. package/dist/src/core/state.js.map +1 -1
  161. package/dist/src/core/telegram-config.d.ts +79 -0
  162. package/dist/src/core/telegram-config.js +108 -0
  163. package/dist/src/core/telegram-config.js.map +1 -1
  164. package/dist/src/core/verify.d.ts +23 -1
  165. package/dist/src/core/verify.js +20 -2
  166. package/dist/src/core/verify.js.map +1 -1
  167. package/dist/src/daemon/audit.d.ts +80 -5
  168. package/dist/src/daemon/audit.js +126 -6
  169. package/dist/src/daemon/audit.js.map +1 -1
  170. package/dist/src/daemon/daemon.d.ts +174 -2
  171. package/dist/src/daemon/daemon.js +298 -7
  172. package/dist/src/daemon/daemon.js.map +1 -1
  173. package/dist/src/daemon/git-evidence.d.ts +2 -2
  174. package/dist/src/daemon/git-evidence.js +1 -1
  175. package/dist/src/daemon/projection.d.ts +13 -2
  176. package/dist/src/daemon/projection.js +39 -6
  177. package/dist/src/daemon/projection.js.map +1 -1
  178. package/dist/src/mcp/server.d.ts +39 -3
  179. package/dist/src/mcp/server.js +22 -3
  180. package/dist/src/mcp/server.js.map +1 -1
  181. package/dist/src/muse/server.d.ts +16 -0
  182. package/dist/src/muse/server.js +238 -0
  183. package/dist/src/muse/server.js.map +1 -0
  184. package/dist/src/serve/archive.d.ts +158 -0
  185. package/dist/src/serve/archive.js +354 -0
  186. package/dist/src/serve/archive.js.map +1 -0
  187. package/dist/src/serve/arguments.d.ts +93 -0
  188. package/dist/src/serve/arguments.js +228 -0
  189. package/dist/src/serve/arguments.js.map +1 -0
  190. package/dist/src/serve/credentials.d.ts +91 -0
  191. package/dist/src/serve/credentials.js +143 -0
  192. package/dist/src/serve/credentials.js.map +1 -0
  193. package/dist/src/serve/follow.d.ts +86 -0
  194. package/dist/src/serve/follow.js +101 -0
  195. package/dist/src/serve/follow.js.map +1 -0
  196. package/dist/src/serve/hook-thread.d.ts +134 -0
  197. package/dist/src/serve/hook-thread.js +333 -0
  198. package/dist/src/serve/hook-thread.js.map +1 -0
  199. package/dist/src/serve/hook-worker.d.ts +134 -0
  200. package/dist/src/serve/hook-worker.js +173 -0
  201. package/dist/src/serve/hook-worker.js.map +1 -0
  202. package/dist/src/serve/server.d.ts +412 -0
  203. package/dist/src/serve/server.js +1251 -0
  204. package/dist/src/serve/server.js.map +1 -0
  205. package/docs/README-extended.md +766 -0
  206. package/docs/cli-reference.md +1633 -43
  207. package/package.json +4 -2
  208. package/schema/event.schema.json +59 -2
  209. package/schema/fixtures/event/invalid/daemon-id-malformed.json +15 -0
  210. package/schema/fixtures/event/invalid/execution-started-truncated-policy-sha256.json +18 -0
  211. package/schema/fixtures/event/invalid/harness-cap-at-margin.json +16 -0
  212. package/schema/fixtures/event/invalid/harness-cap-fractional.json +16 -0
  213. package/schema/fixtures/event/invalid/harness-cap-negative.json +16 -0
  214. package/schema/fixtures/event/invalid/harness-cap-string.json +16 -0
  215. package/schema/fixtures/event/invalid/harness-cap-without-harness-execution.json +16 -0
  216. package/schema/fixtures/event/invalid/harness-cap-zero.json +16 -0
  217. package/schema/fixtures/event/valid/approval-requested-harness-cap.json +18 -0
  218. package/schema/fixtures/event/valid/daemon-identity-declared.json +17 -0
  219. package/schema/fixtures/event/valid/daemon-identity.json +15 -0
  220. package/schema/fixtures/event/valid/execution-started-policy-sha256.json +18 -0
  221. package/schema/fixtures/event/valid/harness-kind-hermes.json +23 -0
  222. package/schema/fixtures/policy/invalid/daemons-malformed-id.json +10 -0
  223. package/schema/fixtures/policy/valid/daemons-allowlist.json +19 -0
  224. package/schema/fixtures/policy-md/invalid/schema-invalid-agent-may-request.md +21 -0
  225. package/schema/fixtures/policy-md/valid/agent-may-request.md +24 -0
  226. package/schema/policy.schema.json +50 -6
@@ -34,6 +34,24 @@
34
34
  * notify, long-poll, callbacks, failure modes — against a local mock Bot API
35
35
  * server and never touches the real network.
36
36
  *
37
+ * ## Two transports, one update path (APRV-424)
38
+ *
39
+ * An update reaches this channel either because it long-polled for it
40
+ * ({@link TelegramChannel.pollOnce}) or because Telegram posted it to a URL the
41
+ * runtime registered ({@link TelegramChannel.deliverUpdate}, fed by
42
+ * `channels/telegram-webhook.ts`). That is the WHOLE of the difference. Both
43
+ * entry points hand the update to the same {@link TelegramChannel.handleUpdate}
44
+ * and therefore to the same chat check, the same `senderOf`, the same
45
+ * resolution ladder, the same `onDecision` handler and the same
46
+ * annotate-after-decision edit; a decision does not know which door it came
47
+ * through, and there is no second copy of any of it to drift.
48
+ *
49
+ * The two are mutually exclusive per bot, and the Bot API says so first:
50
+ * `getUpdates` is refused outright while a webhook is set.
51
+ * {@link TelegramChannel.claimTransport} refuses the in-process form of the
52
+ * mistake, and the verbs above ask `getWebhookInfo` and the ownership registry
53
+ * about the cross-process one.
54
+ *
37
55
  * ## Config-declared identity — SPEC.md §11
38
56
  *
39
57
  * > Human identity in v0.1 is config-declared (an environment variable or
@@ -361,6 +379,13 @@ export declare function decidedLine(actor: string, ts: string, seq: number): str
361
379
  export declare const TELEGRAM_DEFAULT_RETENTION_MS: number;
362
380
  /** Least time between two sweeps. A sweep is O(map); once a minute is plenty. */
363
381
  export declare const TELEGRAM_SWEEP_INTERVAL_MS = 60000;
382
+ /**
383
+ * How many refused cards' nonces a channel remembers as dead (APRV-442).
384
+ *
385
+ * A bound on memory and nothing else: a nonce that falls off the end reaches
386
+ * the action-reference fallback, and the gate judges that tap in full.
387
+ */
388
+ export declare const TELEGRAM_REFUSED_NONCE_CAP = 1024;
364
389
  /**
365
390
  * The slice of `fetch` this module uses, structurally.
366
391
  *
@@ -831,6 +856,14 @@ export interface DigestState {
831
856
  * the request's own `ts` remains the only instant a TTL is measured from.
832
857
  */
833
858
  deliveredAtMs: number;
859
+ /**
860
+ * How long after {@link deliveredAtMs} the LAST of this digest's members can
861
+ * still be decided, or `null` when some member is bounded by nothing
862
+ * (APRV-423, second review pass). The widest of the members' own windows
863
+ * ({@link Delivery.windowMs}), because a digest is droppable only once every
864
+ * member is past deciding.
865
+ */
866
+ windowMs: number | null;
834
867
  }
835
868
  /**
836
869
  * The computed facts a digest's members share, as the digest states them.
@@ -1270,6 +1303,50 @@ export interface TelegramPollResult {
1270
1303
  ok: boolean;
1271
1304
  }[];
1272
1305
  }
1306
+ /**
1307
+ * How updates reach this channel (APRV-424).
1308
+ *
1309
+ * `poll` is `getUpdates`, the transport every build before APRV-424 had.
1310
+ * `webhook` is Telegram posting each update to a URL the runtime registered.
1311
+ * They are alternatives and never a pair: the Bot API refuses `getUpdates`
1312
+ * outright while a webhook is set, so a process that did both would spend its
1313
+ * life in a 409 loop and the approver's tap would land on whichever half was
1314
+ * lucky.
1315
+ */
1316
+ export type TelegramTransport = "poll" | "webhook";
1317
+ /**
1318
+ * Why a transport claim was refused. Frozen, per SPEC §11.1 invariant 6.
1319
+ *
1320
+ * One code, because there is one fact: this channel is already receiving
1321
+ * updates by the other transport.
1322
+ */
1323
+ export declare const TELEGRAM_TRANSPORT_REFUSAL_CODES: readonly ["transport-conflict"];
1324
+ export type TelegramTransportRefusalCode = (typeof TELEGRAM_TRANSPORT_REFUSAL_CODES)[number];
1325
+ export type TelegramTransportClaim = {
1326
+ ok: true;
1327
+ transport: TelegramTransport;
1328
+ } | {
1329
+ ok: false;
1330
+ code: TelegramTransportRefusalCode;
1331
+ message: string;
1332
+ };
1333
+ /**
1334
+ * The in-process half of "long-poll and webhook are mutually exclusive"
1335
+ * (APRV-424).
1336
+ *
1337
+ * It is a belt on the braces and says so. The exclusion that matters is per
1338
+ * BOT and lives above this class, where a process can ask the Bot API
1339
+ * (`getWebhookInfo`) and the per-machine ownership registry who else is
1340
+ * receiving this bot's updates; a channel object knows only about itself.
1341
+ * What this rules out is the one failure a caller could cause here and
1342
+ * nowhere else: feeding one channel instance from both a poll loop and an
1343
+ * HTTP handler, where `handleUpdate`'s single ack slot and the delivery maps
1344
+ * would be written by two arrivals nobody ordered.
1345
+ */
1346
+ export declare class TelegramTransportError extends Error {
1347
+ readonly code: TelegramTransportRefusalCode;
1348
+ constructor(message: string);
1349
+ }
1273
1350
  export interface TelegramListenOptions {
1274
1351
  /** Process exactly one successful `getUpdates` batch, then return. */
1275
1352
  once?: boolean;
@@ -1393,11 +1470,60 @@ export declare class TelegramChannel implements TestableChannel {
1393
1470
  private readonly digests;
1394
1471
  /** "All" nonce -> the digest message it was issued for. */
1395
1472
  private readonly allNonces;
1473
+ /**
1474
+ * Action keys whose card this process disarmed on a tap the gate did NOT
1475
+ * settle (APRV-442): a refusal (`sender-unmapped`, a budget, an unattested
1476
+ * policy, anything `ok: false`) or a handler that threw. Drained by
1477
+ * {@link takeReleased}.
1478
+ *
1479
+ * The disarm is right and stays, and {@link refusedNonces} keeps it final:
1480
+ * the refused card's bytes resolve to nothing, not even through the
1481
+ * action-reference fallback to the card that replaces it. What the disarm
1482
+ * must not do is leave the listener believing the REQUEST is on the
1483
+ * approver's phone, which is what `DispatchState.delivered` goes on saying
1484
+ * until something tells it otherwise. This set is that something: it names
1485
+ * a card, never an outcome, and the listener re-derives from the verified
1486
+ * log whether the request is still pending before it offers a new card.
1487
+ * Nothing here decides, and losing it to a restart costs nothing, because a
1488
+ * restarted listener re-sends everything pending anyway.
1489
+ */
1490
+ private readonly released;
1491
+ /**
1492
+ * Nonces of cards a refused or failed tap disarmed (APRV-442), oldest first,
1493
+ * capped at {@link TELEGRAM_REFUSED_NONCE_CAP}.
1494
+ *
1495
+ * Without this, a released request's fresh card would give the refused
1496
+ * card's bytes somewhere to go: the nonce misses, the action reference
1497
+ * resolves to the new live card, and a Telegram redelivery of the refused
1498
+ * tap (a webhook retry) or a client replaying the removed buttons would be
1499
+ * refused again, append again, disarm the new card and cause another
1500
+ * re-send, once per replay. A nonce in this set never takes the fallback, so
1501
+ * a refused card stays exactly as dead as it was before APRV-442 and only
1502
+ * the REQUEST is offered again. Forgetting an entry past the cap degrades to
1503
+ * the fallback, whose tap the gate still judges in full: volume, never a
1504
+ * decision.
1505
+ */
1506
+ private readonly refusedNonces;
1396
1507
  private rendered;
1397
1508
  private offset;
1398
1509
  private counter;
1399
1510
  private stopped;
1400
1511
  private inFlight;
1512
+ /**
1513
+ * The transport this channel has started receiving updates by, or `null`
1514
+ * before the first one (APRV-424). Sticky for the life of the object: a
1515
+ * process that stopped polling has not stopped being a poller, and the
1516
+ * question this answers is which arrival path owns the maps below.
1517
+ */
1518
+ private transport;
1519
+ /**
1520
+ * Everything {@link redact} scrubs out of anything that leaves this class.
1521
+ *
1522
+ * The bot token from construction, plus any secret a later call was handed:
1523
+ * {@link registerWebhook} adds the `secret_token` before it sends it, so a
1524
+ * Bot API error quoting the request cannot put it on an operator's terminal.
1525
+ */
1526
+ private readonly secrets;
1401
1527
  private readonly counters;
1402
1528
  constructor(config: TelegramConfig);
1403
1529
  onDecision(handler: (decision: ChannelDecision) => DecisionOutcome): void;
@@ -1553,6 +1679,28 @@ export declare class TelegramChannel implements TestableChannel {
1553
1679
  * a decided one is not that.
1554
1680
  */
1555
1681
  private disarm;
1682
+ /**
1683
+ * How long after delivery the request behind a prompt can still be decided,
1684
+ * on this process's clock, or `null` when nothing bounds it (APRV-423, second
1685
+ * review pass).
1686
+ *
1687
+ * The SHORTER of two bounds, each of which is at or past the gate's own
1688
+ * lapse, so the sweep never forgets a button a decision could still reach:
1689
+ *
1690
+ * - the request's `ttl_remaining_ms`, computed by the listener from the
1691
+ * verified log against the window the gate judges by (the policy's TTL
1692
+ * narrowed by the harness cap the requesting hook declared). It was
1693
+ * measured at tagging time, and delivery is at or after tagging, so
1694
+ * counting it from delivery ends at or after the real lapse;
1695
+ * - the policy's own TTL ({@link TelegramConfig.approvalTtlMs}), counted from
1696
+ * delivery, which is at or after the `approval.requested` it really runs
1697
+ * from. This was the only bound before APRV-423 and it stays as one.
1698
+ *
1699
+ * A request neither bounds is decidable forever, and its entry is dropped
1700
+ * only on an observed settlement. This channel still reads no policy and no
1701
+ * log: both numbers arrive computed, by the verb and by the tagger.
1702
+ */
1703
+ private retentionWindowMs;
1556
1704
  /**
1557
1705
  * Drop the delivery bookkeeping no callback can still be honoured against
1558
1706
  * (APRV-135).
@@ -1646,6 +1794,28 @@ export declare class TelegramChannel implements TestableChannel {
1646
1794
  * {@link Channel} interface names. Its wording is unchanged.
1647
1795
  */
1648
1796
  retract(deliveryId: DeliveryId, reason: string, actionKey?: string): Promise<void>;
1797
+ /**
1798
+ * The action keys whose card a refused or failed tap disarmed since the last
1799
+ * call, and forget them (APRV-442).
1800
+ *
1801
+ * The listener's dispatch cycle calls this once per cycle and offers a fresh
1802
+ * card for every key the verified log still calls pending. It is a statement
1803
+ * about this process's cards and nothing else: it carries no decision, no
1804
+ * sender and no outcome, and a key in it that the log has since settled is
1805
+ * simply not offered.
1806
+ */
1807
+ takeReleased(): string[];
1808
+ /**
1809
+ * Mark `delivery`'s card dead and its request released, BEFORE the
1810
+ * annotation that disarms it (APRV-442).
1811
+ *
1812
+ * Every nonce still armed for this request on this message is recorded in
1813
+ * {@link refusedNonces}, together with the nonce the tap carried (which
1814
+ * differs when the tap came from an earlier copy through the fallback), so
1815
+ * none of those bytes can later resolve to the fresh card. On a digest only
1816
+ * this member's nonce is taken: its siblings stay armed on the digest.
1817
+ */
1818
+ private release;
1649
1819
  /**
1650
1820
  * Send one plain message that carries no question (APRV-196).
1651
1821
  *
@@ -1700,6 +1870,99 @@ export declare class TelegramChannel implements TestableChannel {
1700
1870
  * what {@link listen} catches and retries.
1701
1871
  */
1702
1872
  pollOnce(): Promise<TelegramPollResult>;
1873
+ /**
1874
+ * Which update types this channel asks for, on either transport (APRV-424).
1875
+ *
1876
+ * APRV-216. `message` is asked for only while a command handler is
1877
+ * registered: see {@link onCommand} for why a listener that reads messages
1878
+ * nobody asked for would break `approval setup channel telegram`'s chat
1879
+ * discovery. APRV-299 adds the second reason to read messages: a `loved` or
1880
+ * `disliked` on a review card collects the human's words as a REPLY, because
1881
+ * an inline keyboard has no text input.
1882
+ *
1883
+ * One function since APRV-424, because `setWebhook` takes the same list as
1884
+ * `getUpdates` and two spellings of it would be two answers to "what does
1885
+ * this listener read" — with the webhook's answer frozen at registration
1886
+ * time, where nobody would look for it.
1887
+ */
1888
+ allowedUpdates(): string[];
1889
+ /**
1890
+ * One update, from whichever transport carried it (APRV-424).
1891
+ *
1892
+ * The whole of what the webhook adds is the ARRIVAL. Everything a tap then
1893
+ * passes through — the chat check, `senderOf`, the checkpoint and review
1894
+ * parsers, the resolution ladder, the early ack, `this.handler` and the
1895
+ * annotation written from the record the gate appended — is
1896
+ * {@link handleUpdate}, called from here and from {@link pollOnce}, and
1897
+ * there is no second copy of any of it. `tests/telegram-webhook.test.ts`
1898
+ * drives one callback through both entry points and compares the records the
1899
+ * log ends up holding.
1900
+ *
1901
+ * The sweep is `pollOnce`'s, for `pollOnce`'s reason: this is the moment the
1902
+ * process is about to be idle, and a channel that only ever wakes for taps
1903
+ * would otherwise never run one.
1904
+ */
1905
+ deliverUpdate(update: unknown): Promise<TelegramPollResult>;
1906
+ /** The per-update bookkeeping both transports do, before `handleUpdate`. */
1907
+ private intake;
1908
+ /**
1909
+ * Bind this channel to one transport, or say which one already holds it
1910
+ * (APRV-424).
1911
+ *
1912
+ * Idempotent for the transport already claimed, so a webhook server that
1913
+ * claims at start-up and then delivers, or a driver calling `pollOnce` in a
1914
+ * loop, pays nothing. Not released by {@link stop}: a process that stopped
1915
+ * polling has not stopped being a poller, and re-pointing a live channel's
1916
+ * delivery maps at a second arrival path is the state this exists to refuse.
1917
+ */
1918
+ claimTransport(transport: TelegramTransport): TelegramTransportClaim;
1919
+ /** {@link claimTransport}, for the paths where a refusal is a caller's bug. */
1920
+ private claimOrThrow;
1921
+ /**
1922
+ * Point this bot's updates at `url`, and require `secret` on every post.
1923
+ *
1924
+ * Three properties, and each is a refusal to be clever:
1925
+ *
1926
+ * - **The secret is Telegram's own mechanism**, `secret_token`, echoed back
1927
+ * on every delivery in the `X-Telegram-Bot-Api-Secret-Token` header. The
1928
+ * receiver compares it and refuses anything else, so a URL that leaks (a
1929
+ * proxy log, a referrer, a screenshot) is a URL that still decides
1930
+ * nothing.
1931
+ * - **It is redacted from this moment on.** The value is added to the
1932
+ * channel's secret list BEFORE the call, so a Bot API error that quotes
1933
+ * the request cannot print it.
1934
+ * - **Pending updates are kept.** `drop_pending_updates` is left false:
1935
+ * taps that arrived while the process was down are the approver's answers,
1936
+ * and the gate is what decides whether they are still honourable.
1937
+ */
1938
+ registerWebhook(url: string, secret: string): Promise<void>;
1939
+ /**
1940
+ * Take this bot's webhook away, so `getUpdates` works again.
1941
+ *
1942
+ * Called on a clean stop of the webhook runner. Pending updates are kept for
1943
+ * {@link registerWebhook}'s reason: the next process to start — a poller or
1944
+ * another webhook — is entitled to the taps that arrived in between.
1945
+ *
1946
+ * `timeoutMs` is what the stop path passes, and it is shorter than this
1947
+ * channel's ordinary request timeout on purpose (APRV-424, second review,
1948
+ * note 6). A stop already waits for the receiver to drain, and an operator
1949
+ * pressing Ctrl-C a second time is asking for the process to end: half a
1950
+ * minute of an unreachable Bot API on top of that reads as a hang, and the
1951
+ * webhook it failed to remove is reported rather than silently retried.
1952
+ */
1953
+ deleteWebhook(timeoutMs?: number): Promise<void>;
1954
+ /**
1955
+ * What the Bot API says is receiving this bot's updates right now.
1956
+ *
1957
+ * The one authority on the question, and the reason it is asked at start-up
1958
+ * by both transports: the ownership registry knows about processes on THIS
1959
+ * machine, and a webhook registered from a laptop in another timezone is
1960
+ * still a webhook swallowing every tap this poller is waiting for.
1961
+ */
1962
+ webhookInfo(): Promise<{
1963
+ url: string;
1964
+ pendingUpdateCount: number;
1965
+ }>;
1703
1966
  /**
1704
1967
  * Exactly one `answerCallbackQuery` per callback query, on every path
1705
1968
  * (APRV-196).
@@ -1919,7 +2182,14 @@ export declare class TelegramChannel implements TestableChannel {
1919
2182
  * settled, so a member still in the map is one still armed on a live message.
1920
2183
  */
1921
2184
  private liveDeliveryFor;
1922
- /** Replace the token with a placeholder anywhere it appears in `text`. */
2185
+ /**
2186
+ * Replace every secret this channel holds with a placeholder in `text`.
2187
+ *
2188
+ * The bot token always, and since APRV-424 the webhook `secret_token` too,
2189
+ * from the moment {@link registerWebhook} is handed it. Both are values that
2190
+ * only ever travel TO the Bot API, so anything coming back that quotes one
2191
+ * is a diagnostic about to print a credential.
2192
+ */
1923
2193
  private redact;
1924
2194
  private describe;
1925
2195
  /**