stitchkit 0.100.2 → 0.101.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 (242) hide show
  1. package/CHANGELOG.md +153 -0
  2. package/README.md +2 -1
  3. package/dist/entrypoints/agent-runtime/browser.js +119 -119
  4. package/dist/entrypoints/agent-runtime/coding-tools.js +15 -20
  5. package/dist/entrypoints/agent-runtime/harness.js +55 -63
  6. package/dist/entrypoints/agent-runtime/openrouter.js +6 -6
  7. package/dist/entrypoints/agent-runtime/sandbox.js +20 -25
  8. package/dist/entrypoints/agent-runtime/sqlite/bun.js +11 -13
  9. package/dist/entrypoints/agent-runtime/sqlite/node.js +11 -13
  10. package/dist/entrypoints/agent-runtime/testing.js +32 -34
  11. package/dist/entrypoints/agent-runtime.js +395 -402
  12. package/dist/entrypoints/application/diagnostic-journal.js +26 -27
  13. package/dist/entrypoints/application/directory-inbox.js +16 -17
  14. package/dist/entrypoints/application/grammy.js +13 -13
  15. package/dist/entrypoints/application/opentelemetry.js +13 -13
  16. package/dist/entrypoints/application/schemas.js +24 -24
  17. package/dist/entrypoints/application.js +209 -210
  18. package/dist/entrypoints/bin/upgrade-cli.js +2 -2
  19. package/dist/entrypoints/cli.js +81 -84
  20. package/dist/entrypoints/contract.js +54 -57
  21. package/dist/entrypoints/declaration.js +22 -22
  22. package/dist/entrypoints/files.js +18 -21
  23. package/dist/entrypoints/geo.js +7 -8
  24. package/dist/entrypoints/google.js +2 -2
  25. package/dist/entrypoints/index.js +150 -154
  26. package/dist/entrypoints/live.js +45 -47
  27. package/dist/entrypoints/node.js +68 -74
  28. package/dist/entrypoints/oauth.js +6 -6
  29. package/dist/entrypoints/observability.d.ts +1 -0
  30. package/dist/entrypoints/observability.d.ts.map +1 -1
  31. package/dist/entrypoints/observability.js +116 -87
  32. package/dist/entrypoints/primitives.js +35 -35
  33. package/dist/entrypoints/react/keyboard.d.ts +4 -0
  34. package/dist/entrypoints/react/keyboard.d.ts.map +1 -0
  35. package/dist/entrypoints/react/keyboard.js +397 -0
  36. package/dist/entrypoints/react.js +10 -10
  37. package/dist/entrypoints/release.js +7 -7
  38. package/dist/entrypoints/remote.js +15 -17
  39. package/dist/entrypoints/server.js +198 -200
  40. package/dist/entrypoints/telegram/html.d.ts +5 -0
  41. package/dist/entrypoints/telegram/html.d.ts.map +1 -0
  42. package/dist/entrypoints/telegram/html.js +26 -0
  43. package/dist/entrypoints/telegram/screens.d.ts +14 -0
  44. package/dist/entrypoints/telegram/screens.d.ts.map +1 -0
  45. package/dist/entrypoints/telegram/screens.js +2028 -0
  46. package/dist/entrypoints/telegram.d.ts +11 -3
  47. package/dist/entrypoints/telegram.d.ts.map +1 -1
  48. package/dist/entrypoints/telegram.js +779 -159
  49. package/dist/entrypoints/testing.js +75 -79
  50. package/dist/entrypoints/tools/connections.js +30 -32
  51. package/dist/entrypoints/tools/contract.js +18 -20
  52. package/dist/entrypoints/tools/invoker.js +12 -17
  53. package/dist/entrypoints/tools.js +224 -229
  54. package/dist/entrypoints/tracking/server.js +6 -6
  55. package/dist/entrypoints/tracking.js +40 -42
  56. package/dist/entrypoints/voice.js +3 -3
  57. package/dist/{index-eaysmdtq.js → index-0ndk4555.js} +56 -56
  58. package/dist/{index-2bxbyab0.js → index-1bx1xv1k.js} +1 -1
  59. package/dist/{index-p5r7qpdd.js → index-1eeqpadm.js} +4 -4
  60. package/dist/{index-hadmp193.js → index-3ct7hhq6.js} +210 -210
  61. package/dist/{index-e01r054k.js → index-45tdvqa3.js} +26 -26
  62. package/dist/{index-dce5wxcq.js → index-4s0fep5q.js} +8 -8
  63. package/dist/index-50ejfz75.js +9 -0
  64. package/dist/{index-m9zn6cpq.js → index-5107q189.js} +12 -12
  65. package/dist/{index-sbdmyz75.js → index-5cc6r990.js} +3 -3
  66. package/dist/{index-c6bxbsnc.js → index-7kxw1awn.js} +2 -2
  67. package/dist/{index-0r0fbk7z.js → index-7z9ym7t8.js} +28 -28
  68. package/dist/{index-p7hgdyhb.js → index-814xpr6d.js} +2 -2
  69. package/dist/{index-gtj3zc53.js → index-8kzct1d6.js} +99 -99
  70. package/dist/{index-1zbbzgfh.js → index-9pw0a51a.js} +4 -4
  71. package/dist/{index-79z7m7dt.js → index-a9pd417t.js} +35 -35
  72. package/dist/{index-ham5sz8m.js → index-aaj97r4y.js} +45 -45
  73. package/dist/{index-s812ycat.js → index-aan6hqb9.js} +3 -3
  74. package/dist/{index-10x2hbx0.js → index-adhgd4pe.js} +44 -44
  75. package/dist/{index-2gxzjvyr.js → index-afjvjyck.js} +23 -23
  76. package/dist/{index-qmy6zq75.js → index-ahvsksw5.js} +16 -16
  77. package/dist/{index-hr4tzbwq.js → index-ap3gqe56.js} +3 -3
  78. package/dist/{index-znfs20ss.js → index-b2bz0kdm.js} +16 -16
  79. package/dist/{index-r066jx76.js → index-c6aagrbd.js} +148 -120
  80. package/dist/{index-k30h66yd.js → index-ck71tbcf.js} +9 -9
  81. package/dist/{index-eqqvgdb3.js → index-ct75c3at.js} +2 -2
  82. package/dist/{index-gw8bmyaj.js → index-djjhhgjk.js} +41 -41
  83. package/dist/{index-8qt2m66q.js → index-dvwj1f43.js} +43 -43
  84. package/dist/index-e05cjgtf.js +144 -0
  85. package/dist/{index-yw4fzmdz.js → index-e6c2spsd.js} +79 -30
  86. package/dist/{index-fhwqwxe0.js → index-ej331hah.js} +1 -1
  87. package/dist/{index-9t3dffv2.js → index-ewbaywk5.js} +5 -5
  88. package/dist/{index-7p1f0vp2.js → index-fyphpknh.js} +25 -25
  89. package/dist/{index-83bdg97s.js → index-g149e9pk.js} +1 -1
  90. package/dist/{index-ngfhvfpd.js → index-g6a3na27.js} +4 -4
  91. package/dist/{index-qwhynv6x.js → index-h124epcf.js} +20 -20
  92. package/dist/{index-h6eeys1v.js → index-h97tqqp1.js} +9 -9
  93. package/dist/{index-zcgf3gqf.js → index-hby4qhax.js} +10 -10
  94. package/dist/{index-5jrd0yng.js → index-j8aw2c0e.js} +4 -4
  95. package/dist/{index-d8m8zpd8.js → index-jzczzcsb.js} +14 -14
  96. package/dist/index-kqnd6a85.js +26 -0
  97. package/dist/{index-35mh5bhj.js → index-kw1ws0cs.js} +12 -12
  98. package/dist/{index-4gcnakhx.js → index-m38ej7e5.js} +2 -2
  99. package/dist/{index-ndddxtyx.js → index-m79yr938.js} +19 -19
  100. package/dist/{index-5p2m7we1.js → index-m9fndta6.js} +11 -11
  101. package/dist/index-mnkk1ffz.js +150 -0
  102. package/dist/index-n15jny5b.js +4 -0
  103. package/dist/{index-2vtcmrj9.js → index-n53mswt3.js} +20 -20
  104. package/dist/{index-bt14acb5.js → index-n5yqxd3d.js} +21 -21
  105. package/dist/{index-bm5rx7n6.js → index-n9tyz8z3.js} +12 -12
  106. package/dist/index-nmaqxdx7.js +463 -0
  107. package/dist/{index-mqv3b5rh.js → index-nx6eazt0.js} +30 -30
  108. package/dist/{index-3z73fh2c.js → index-q0th8q0z.js} +9 -9
  109. package/dist/{index-v260df89.js → index-qjeggp8w.js} +20 -20
  110. package/dist/{index-zfrhfn82.js → index-r1z0rd9c.js} +20 -20
  111. package/dist/{index-wqgjfstn.js → index-r5c4fedv.js} +3 -3
  112. package/dist/{index-cyktc4bp.js → index-r5hsgkz5.js} +118 -118
  113. package/dist/{index-0b1ess5e.js → index-r7a7r7ha.js} +1 -1
  114. package/dist/{index-1j8pm5t0.js → index-rfkh44ss.js} +15 -15
  115. package/dist/index-rz3z13e9.js +47 -0
  116. package/dist/{index-k6gpggb9.js → index-s80awwsw.js} +35 -35
  117. package/dist/{index-vqr7f7v8.js → index-ssmd41kh.js} +124 -124
  118. package/dist/index-sznqf8fx.js +20 -0
  119. package/dist/{index-ensd0r74.js → index-v14qqmys.js} +41 -41
  120. package/dist/{index-paz6sgdp.js → index-v4a2fjtk.js} +36 -36
  121. package/dist/{index-d9pk3td4.js → index-w0eqdhyd.js} +14 -14
  122. package/dist/{index-5qn1kx34.js → index-w575evhq.js} +29 -29
  123. package/dist/{index-3gye9wzb.js → index-w5s3xftz.js} +26 -26
  124. package/dist/{index-tm88avnj.js → index-wykebpn9.js} +3 -3
  125. package/dist/{index-sh3s86na.js → index-xkhk5d6g.js} +17 -17
  126. package/dist/{index-6k1937bx.js → index-xqhede6x.js} +4 -4
  127. package/dist/{index-f4fx2tza.js → index-zs4ms3th.js} +181 -181
  128. package/dist/{index-k5hsfc3j.js → index-zsvzhjtk.js} +1 -1
  129. package/dist/internal/secret-shapes.d.ts +21 -0
  130. package/dist/internal/secret-shapes.d.ts.map +1 -0
  131. package/dist/observability/bounded-logger.d.ts +2 -0
  132. package/dist/observability/bounded-logger.d.ts.map +1 -1
  133. package/dist/observability/sanitize.d.ts +6 -0
  134. package/dist/observability/sanitize.d.ts.map +1 -1
  135. package/dist/observability/secret-values.d.ts +25 -0
  136. package/dist/observability/secret-values.d.ts.map +1 -0
  137. package/dist/react/keyboard/hooks.d.ts +71 -0
  138. package/dist/react/keyboard/hooks.d.ts.map +1 -0
  139. package/dist/react/keyboard/layers.d.ts +98 -0
  140. package/dist/react/keyboard/layers.d.ts.map +1 -0
  141. package/dist/react/keyboard/zone.d.ts +139 -0
  142. package/dist/react/keyboard/zone.d.ts.map +1 -0
  143. package/dist/telegram/bot-api.d.ts +7 -4
  144. package/dist/telegram/bot-api.d.ts.map +1 -1
  145. package/dist/telegram/crypto.d.ts +16 -0
  146. package/dist/telegram/crypto.d.ts.map +1 -0
  147. package/dist/telegram/html/check.d.ts +26 -0
  148. package/dist/telegram/html/check.d.ts.map +1 -0
  149. package/dist/telegram/html/nodes.d.ts +70 -0
  150. package/dist/telegram/html/nodes.d.ts.map +1 -0
  151. package/dist/telegram/html/parse.d.ts +35 -0
  152. package/dist/telegram/html/parse.d.ts.map +1 -0
  153. package/dist/telegram/html/render.d.ts +39 -0
  154. package/dist/telegram/html/render.d.ts.map +1 -0
  155. package/dist/telegram/init-data.d.ts +3 -44
  156. package/dist/telegram/init-data.d.ts.map +1 -1
  157. package/dist/telegram/operator-channel.d.ts +19 -3
  158. package/dist/telegram/operator-channel.d.ts.map +1 -1
  159. package/dist/telegram/operator-dedupe.d.ts +44 -0
  160. package/dist/telegram/operator-dedupe.d.ts.map +1 -0
  161. package/dist/telegram/screens/callback-codec.d.ts +34 -0
  162. package/dist/telegram/screens/callback-codec.d.ts.map +1 -0
  163. package/dist/telegram/screens/dispatch.d.ts +22 -0
  164. package/dist/telegram/screens/dispatch.d.ts.map +1 -0
  165. package/dist/telegram/screens/display.d.ts +70 -0
  166. package/dist/telegram/screens/display.d.ts.map +1 -0
  167. package/dist/telegram/screens/execute.d.ts +57 -0
  168. package/dist/telegram/screens/execute.d.ts.map +1 -0
  169. package/dist/telegram/screens/html.d.ts +36 -0
  170. package/dist/telegram/screens/html.d.ts.map +1 -0
  171. package/dist/telegram/screens/outcome.d.ts +74 -0
  172. package/dist/telegram/screens/outcome.d.ts.map +1 -0
  173. package/dist/telegram/screens/path.d.ts +29 -0
  174. package/dist/telegram/screens/path.d.ts.map +1 -0
  175. package/dist/telegram/screens/reconcile.d.ts +67 -0
  176. package/dist/telegram/screens/reconcile.d.ts.map +1 -0
  177. package/dist/telegram/screens/registry.d.ts +23 -0
  178. package/dist/telegram/screens/registry.d.ts.map +1 -0
  179. package/dist/telegram/screens/render.d.ts +70 -0
  180. package/dist/telegram/screens/render.d.ts.map +1 -0
  181. package/dist/telegram/screens/runtime.d.ts +133 -0
  182. package/dist/telegram/screens/runtime.d.ts.map +1 -0
  183. package/dist/telegram/screens/screen-types.d.ts +130 -0
  184. package/dist/telegram/screens/screen-types.d.ts.map +1 -0
  185. package/dist/telegram/screens/screen.d.ts +138 -0
  186. package/dist/telegram/screens/screen.d.ts.map +1 -0
  187. package/dist/telegram/screens/state.d.ts +115 -0
  188. package/dist/telegram/screens/state.d.ts.map +1 -0
  189. package/dist/telegram/screens/telegram-screens.d.ts +18 -0
  190. package/dist/telegram/screens/telegram-screens.d.ts.map +1 -0
  191. package/dist/telegram/screens/test-chat-telegram.d.ts +47 -0
  192. package/dist/telegram/screens/test-chat-telegram.d.ts.map +1 -0
  193. package/dist/telegram/screens/test-chat.d.ts +66 -0
  194. package/dist/telegram/screens/test-chat.d.ts.map +1 -0
  195. package/dist/telegram/screens/view.d.ts +89 -0
  196. package/dist/telegram/screens/view.d.ts.map +1 -0
  197. package/dist/telegram/send-failure.d.ts +25 -0
  198. package/dist/telegram/send-failure.d.ts.map +1 -1
  199. package/dist/telegram/update-intake.d.ts +82 -0
  200. package/dist/telegram/update-intake.d.ts.map +1 -0
  201. package/dist/telegram/update-store-check.d.ts +22 -0
  202. package/dist/telegram/update-store-check.d.ts.map +1 -0
  203. package/dist/telegram/update-store-postgres.d.ts +33 -0
  204. package/dist/telegram/update-store-postgres.d.ts.map +1 -0
  205. package/dist/telegram/update-store-sqlite.d.ts +26 -0
  206. package/dist/telegram/update-store-sqlite.d.ts.map +1 -0
  207. package/dist/telegram/update-store.d.ts +67 -0
  208. package/dist/telegram/update-store.d.ts.map +1 -0
  209. package/dist/telegram/user.d.ts +51 -0
  210. package/dist/telegram/user.d.ts.map +1 -0
  211. package/dist/telegram/webhook.d.ts +105 -0
  212. package/dist/telegram/webhook.d.ts.map +1 -0
  213. package/dist/tools/cli/args-fields.d.ts +8 -0
  214. package/dist/tools/cli/args-fields.d.ts.map +1 -1
  215. package/dist/tools/cli/args-route.d.ts.map +1 -1
  216. package/dist/tools/cli/args.d.ts.map +1 -1
  217. package/dist/tools/cli/help.d.ts.map +1 -1
  218. package/llms/cli.txt +8 -2
  219. package/llms/observability.txt +15 -2
  220. package/llms/react-keyboard.txt +193 -0
  221. package/llms/server.4.txt +16 -0
  222. package/llms/stitchkit.txt +10 -4
  223. package/llms/telegram-html.txt +35 -0
  224. package/llms/telegram-screens.txt +365 -0
  225. package/llms/telegram.txt +245 -11
  226. package/llms/{upgrading-0.74.0-0.100.0.txt → upgrading-0.74.0-0.101.0.txt} +39 -3
  227. package/llms/upgrading.txt +1 -1
  228. package/llms-full.txt +889 -19
  229. package/llms.txt +12 -7
  230. package/package.json +17 -2
  231. package/dist/index-2k4yrqkc.js +0 -9
  232. package/dist/index-crykmqkr.js +0 -26
  233. package/dist/index-y64k3s0h.js +0 -150
  234. package/dist/index-y91ry9h2.js +0 -4
  235. /package/dist/{index-hffjpmq9.js → index-9jfdm5tg.js} +0 -0
  236. /package/dist/{index-zjjyynm3.js → index-d4pa2bgs.js} +0 -0
  237. /package/dist/{index-rxfy4cq7.js → index-dwfv9a85.js} +0 -0
  238. /package/dist/{index-5jm36zv1.js → index-gfk7nrht.js} +0 -0
  239. /package/dist/{index-b9aefrqd.js → index-n1dqr7gf.js} +0 -0
  240. /package/dist/{index-q3pyb9n0.js → index-sykb5ag7.js} +0 -0
  241. /package/dist/{index-48ffdxgk.js → index-xhzq2qvj.js} +0 -0
  242. /package/dist/{index-kzfs85xp.js → index-zsarb5xp.js} +0 -0
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Where received updates wait, and the rules a store keeps for them.
3
+ *
4
+ * The intake decides; the store only has to make each decision atomic, so two
5
+ * processes sharing one store — an old release still draining beside a new one
6
+ * — never run one update at once. Every rule is a condition on one record:
7
+ *
8
+ * - **claimable** at `now`: `pending`; `failed` whose `dueAt` has come;
9
+ * `processing` whose lease (`dueAt`) has lapsed — its process died;
10
+ * - `claim` takes a claimable record whose `attempts` are under the limit —
11
+ * `processing`, `attempts + 1`, lease until `leaseUntil` — and settles one
12
+ * whose attempts are spent as `abandoned`;
13
+ * - `renew` and `settle` act only while the record is still `processing` under
14
+ * the same attempt, so a process that lost its lease cannot overwrite the
15
+ * one that took it over.
16
+ */
17
+ export type TelegramUpdateState = 'pending' | 'processing' | 'failed' | 'completed' | 'abandoned';
18
+ export type TelegramUpdateSettlement = {
19
+ readonly state: 'completed';
20
+ readonly at: number;
21
+ } | {
22
+ readonly state: 'failed';
23
+ readonly at: number;
24
+ readonly retryAt: number;
25
+ readonly error: string;
26
+ } | {
27
+ readonly state: 'abandoned';
28
+ readonly at: number;
29
+ readonly error: string;
30
+ };
31
+ export interface TelegramUpdateClaimOptions {
32
+ readonly now: number;
33
+ readonly leaseUntil: number;
34
+ readonly maxAttempts: number;
35
+ }
36
+ export interface TelegramUpdateDueQuery {
37
+ readonly now: number;
38
+ /** A `pending` record is due once received at or before this — its own process had its chance. */
39
+ readonly pendingBefore: number;
40
+ readonly limit: number;
41
+ }
42
+ export interface StoredTelegramUpdate {
43
+ readonly updateId: number;
44
+ readonly body: string;
45
+ }
46
+ export interface TelegramUpdateStore {
47
+ /** Record a received update as `pending`; `false` when this `update_id` is already recorded. */
48
+ add(update: StoredTelegramUpdate & {
49
+ readonly receivedAt: number;
50
+ }): Promise<boolean>;
51
+ /** Take a claimable update for one attempt; its number, or `undefined` when not taken. */
52
+ claim(updateId: number, options: TelegramUpdateClaimOptions): Promise<number | undefined>;
53
+ /** Extend the lease of this attempt; `false` when the attempt no longer holds the update. */
54
+ renew(updateId: number, attempt: number, leaseUntil: number): Promise<boolean>;
55
+ /** End this attempt; ignored when the attempt no longer holds the update. */
56
+ settle(updateId: number, attempt: number, settlement: TelegramUpdateSettlement): Promise<void>;
57
+ /** Claimable updates in `update_id` order. */
58
+ due(query: TelegramUpdateDueQuery): Promise<readonly StoredTelegramUpdate[]>;
59
+ /** Forget `completed` and `abandoned` updates settled before `before`; how many were forgotten. */
60
+ prune(before: number): Promise<number>;
61
+ }
62
+ /**
63
+ * A store in this process's memory — for tests, and for a bot that accepts
64
+ * losing what was received but not handled when it restarts.
65
+ */
66
+ export declare function memoryTelegramUpdateStore(): TelegramUpdateStore;
67
+ //# sourceMappingURL=update-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update-store.d.ts","sourceRoot":"","sources":["../../src/telegram/update-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,MAAM,MAAM,mBAAmB,GAC3B,SAAS,GACT,YAAY,GACZ,QAAQ,GACR,WAAW,GACX,WAAW,CAAC;AAEhB,MAAM,MAAM,wBAAwB,GAChC;IAAE,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GACpD;IACE,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC;IACzB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB,GACD;IAAE,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEjF,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,kGAAkG;IAClG,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,mBAAmB;IAClC,gGAAgG;IAChG,GAAG,CAAC,MAAM,EAAE,oBAAoB,GAAG;QAAE,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACtF,0FAA0F;IAC1F,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,0BAA0B,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IAC1F,6FAA6F;IAC7F,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/E,6EAA6E;IAC7E,MAAM,CACJ,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,wBAAwB,GACnC,OAAO,CAAC,IAAI,CAAC,CAAC;IACjB,8CAA8C;IAC9C,GAAG,CAAC,KAAK,EAAE,sBAAsB,GAAG,OAAO,CAAC,SAAS,oBAAoB,EAAE,CAAC,CAAC;IAC7E,mGAAmG;IACnG,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CACxC;AAsBD;;;GAGG;AACH,wBAAgB,yBAAyB,IAAI,mBAAmB,CAsE/D"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * A Telegram user in one shape, wherever it came from.
3
+ *
4
+ * Telegram sends a user in `snake_case` twice over — the Bot API's `from` on
5
+ * every update, the Mini App's signed `user` — and every other name this
6
+ * package publishes is `camelCase`. An application with one `ensureAccount`
7
+ * receives both, and a surface that switched convention by source would make
8
+ * it write the same translation twice. So both pass through this schema and
9
+ * arrive as the same `TelegramUser`.
10
+ */
11
+ import { z } from 'zod';
12
+ declare const TelegramUserSchema: z.ZodPipe<z.ZodObject<{
13
+ id: z.ZodInt;
14
+ is_bot: z.ZodOptional<z.ZodBoolean>;
15
+ first_name: z.ZodString;
16
+ last_name: z.ZodOptional<z.ZodString>;
17
+ username: z.ZodOptional<z.ZodString>;
18
+ language_code: z.ZodOptional<z.ZodString>;
19
+ is_premium: z.ZodOptional<z.ZodBoolean>;
20
+ allows_write_to_pm: z.ZodOptional<z.ZodBoolean>;
21
+ photo_url: z.ZodOptional<z.ZodString>;
22
+ }, z.core.$strip>, z.ZodTransform<{
23
+ id: number;
24
+ firstName: string;
25
+ isBot?: boolean | undefined;
26
+ lastName?: string | undefined;
27
+ username?: string | undefined;
28
+ languageCode?: string | undefined;
29
+ isPremium?: boolean | undefined;
30
+ allowsWriteToPm?: boolean | undefined;
31
+ photoUrl?: string | undefined;
32
+ }, {
33
+ id: number;
34
+ is_bot?: boolean | undefined;
35
+ first_name: string;
36
+ last_name?: string | undefined;
37
+ username?: string | undefined;
38
+ language_code?: string | undefined;
39
+ is_premium?: boolean | undefined;
40
+ allows_write_to_pm?: boolean | undefined;
41
+ photo_url?: string | undefined;
42
+ }>>;
43
+ /** A Telegram user as this package names it; fields Telegram did not send are absent. */
44
+ export type TelegramUser = z.infer<typeof TelegramUserSchema>;
45
+ /**
46
+ * Telegram's user record — grammY's `ctx.from`, a Bot API `User`, a Mini App's
47
+ * `user` — as a `TelegramUser`; `undefined` when it is not one.
48
+ */
49
+ export declare function parseTelegramUser(value: unknown): TelegramUser | undefined;
50
+ export {};
51
+ //# sourceMappingURL=user.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"user.d.ts","sourceRoot":"","sources":["../../src/telegram/user.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,QAAA,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwBnB,CAAC;AAEN,yFAAyF;AACzF,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAE9D;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,YAAY,GAAG,SAAS,CAG1E"}
@@ -0,0 +1,105 @@
1
+ /**
2
+ * A bot's webhook: one place in all of Telegram, and who may take it.
3
+ *
4
+ * `setWebhook` moves a bot's updates without asking whose they were. A process
5
+ * started anywhere with the production token — a developer's machine, a
6
+ * staging host, a rehearsal — takes the production bot with one call, and the
7
+ * production process simply stops hearing from Telegram. So a claim sets the
8
+ * webhook only where it already points here, or where the caller named the
9
+ * current owner's host as the one it takes over from (`'none'` when there is no
10
+ * webhook — a bot on long polling is somebody's bot too).
11
+ *
12
+ * "Here" is the address *and* the secret. Telegram never returns the secret,
13
+ * so the address carries a tag derived from it: an `owner` query parameter
14
+ * holding an HMAC of the secret. A process with the production address but a
15
+ * different secret has a different address and is refused, instead of quietly
16
+ * installing its secret and leaving production answering 403 to every update.
17
+ * Routers match the path, which the tag leaves alone; the secret cannot be
18
+ * recovered from it. Errors and reports name hosts only — a foreign webhook's
19
+ * path may itself be a secret.
20
+ */
21
+ /** `takeoverFrom` when there is no webhook to take over — the bot polls, or is new. */
22
+ export declare const TELEGRAM_WEBHOOK_NONE = "none";
23
+ export interface TelegramWebhookConfig {
24
+ readonly token: string;
25
+ /** Where Telegram should post updates, without the owner tag. */
26
+ readonly url: string;
27
+ /** Telegram's `secret_token`: 1–256 of `A-Z a-z 0-9 _ -`. */
28
+ readonly secret: string;
29
+ /** A local Bot API server. Default: Telegram's. */
30
+ readonly apiRoot?: string;
31
+ readonly fetch?: typeof fetch;
32
+ readonly signal?: AbortSignal;
33
+ }
34
+ export interface ClaimTelegramWebhookConfig extends TelegramWebhookConfig {
35
+ /**
36
+ * The host the webhook may be taken from — the current owner's, as the
37
+ * refusal names it — or `'none'` to set one where there is none. Set it for
38
+ * one switch and remove it: left in place it is a standing permission.
39
+ */
40
+ readonly takeoverFrom?: string;
41
+ /** Telegram's `allowed_updates`. Default: whatever the webhook had. */
42
+ readonly allowedUpdates?: readonly string[];
43
+ /** Telegram's `max_connections`, 1–100. */
44
+ readonly maxConnections?: number;
45
+ /** Default `false`: updates waiting for the previous owner are this owner's now. */
46
+ readonly dropPendingUpdates?: boolean;
47
+ }
48
+ export interface TelegramWebhookClaim {
49
+ /** The address set, owner tag included. */
50
+ readonly url: string;
51
+ /** The host it was taken over from; absent when it was already this one's. */
52
+ readonly takenFrom?: string;
53
+ readonly pendingUpdates: number;
54
+ }
55
+ export type TelegramWebhookRefusal =
56
+ /** Another host holds the webhook. */
57
+ 'owned-elsewhere'
58
+ /** This address holds it, registered with another secret. */
59
+ | 'other-secret'
60
+ /** Telegram accepted `setWebhook` but reports another address after it. */
61
+ | 'not-confirmed';
62
+ export declare class TelegramWebhookClaimError extends Error {
63
+ readonly reason: TelegramWebhookRefusal;
64
+ /** Where the webhook points, as a host; `'none'` when nowhere. */
65
+ readonly ownerHost: string;
66
+ constructor(reason: TelegramWebhookRefusal, ownerHost: string);
67
+ }
68
+ export interface TelegramWebhookState {
69
+ /** The webhook points to this address with this secret. */
70
+ readonly owned: boolean;
71
+ readonly ownerHost: string;
72
+ readonly pendingUpdates: number;
73
+ /** Telegram's last failure to deliver, if it reported one. */
74
+ readonly lastError?: {
75
+ readonly at: number;
76
+ readonly message: string;
77
+ };
78
+ }
79
+ /** The address with its owner tag: the same for every process with this secret. */
80
+ export declare function telegramWebhookUrl(url: string, secret: string): Promise<string>;
81
+ /**
82
+ * Set the webhook if it is this process's to set, and confirm Telegram holds
83
+ * it. Refused — without `setWebhook` — when another host or another secret
84
+ * holds it and `takeoverFrom` does not name that holder.
85
+ */
86
+ export declare function claimTelegramWebhook(config: ClaimTelegramWebhookConfig): Promise<TelegramWebhookClaim>;
87
+ /**
88
+ * Whether the webhook still points here. A webhook moved to another host, or
89
+ * re-registered with another secret, stops this process hearing from Telegram
90
+ * without any error of its own — a periodic check is how it is noticed.
91
+ */
92
+ export declare function checkTelegramWebhook(config: TelegramWebhookConfig): Promise<TelegramWebhookState>;
93
+ /** What becomes of an update body: recorded, seen before, or not an update. */
94
+ export type TelegramUpdateAcceptance = 'accepted' | 'duplicate' | 'invalid';
95
+ export interface ReceiveTelegramWebhookOptions {
96
+ readonly secret: string;
97
+ /** Records the update; answered to Telegram as soon as this resolves. */
98
+ readonly accept: (body: string) => Promise<TelegramUpdateAcceptance>;
99
+ }
100
+ /**
101
+ * Answer one webhook request: 403 without this secret, 400 for a body that is
102
+ * not an update, 200 once the update is recorded — before it is handled.
103
+ */
104
+ export declare function receiveTelegramWebhook(request: Request, options: ReceiveTelegramWebhookOptions): Promise<Response>;
105
+ //# sourceMappingURL=webhook.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webhook.d.ts","sourceRoot":"","sources":["../../src/telegram/webhook.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAWH,uFAAuF;AACvF,eAAO,MAAM,qBAAqB,SAAS,CAAC;AAE5C,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,iEAAiE;IACjE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,6DAA6D;IAC7D,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,mDAAmD;IACnD,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IAC9B,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;CAC/B;AAED,MAAM,WAAW,0BAA2B,SAAQ,qBAAqB;IACvE;;;;OAIG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,uEAAuE;IACvE,QAAQ,CAAC,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5C,2CAA2C;IAC3C,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,oFAAoF;IACpF,QAAQ,CAAC,kBAAkB,CAAC,EAAE,OAAO,CAAC;CACvC;AAED,MAAM,WAAW,oBAAoB;IACnC,2CAA2C;IAC3C,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,8EAA8E;IAC9E,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;CACjC;AAED,MAAM,MAAM,sBAAsB;AAChC,sCAAsC;AACpC,iBAAiB;AACnB,6DAA6D;GAC3D,cAAc;AAChB,2EAA2E;GACzE,eAAe,CAAC;AAEpB,qBAAa,yBAA0B,SAAQ,KAAK;IAClD,QAAQ,CAAC,MAAM,EAAE,sBAAsB,CAAC;IACxC,kEAAkE;IAClE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAE3B,YAAY,MAAM,EAAE,sBAAsB,EAAE,SAAS,EAAE,MAAM,EAa5D;CACF;AAED,MAAM,WAAW,oBAAoB;IACnC,2DAA2D;IAC3D,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,8DAA8D;IAC9D,QAAQ,CAAC,SAAS,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CACxE;AAcD,mFAAmF;AACnF,wBAAsB,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAUrF;AA+BD;;;;GAIG;AACH,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,0BAA0B,GACjC,OAAO,CAAC,oBAAoB,CAAC,CAgC/B;AAED;;;;GAIG;AACH,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,qBAAqB,GAC5B,OAAO,CAAC,oBAAoB,CAAC,CAS/B;AAED,+EAA+E;AAC/E,MAAM,MAAM,wBAAwB,GAAG,UAAU,GAAG,WAAW,GAAG,SAAS,CAAC;AAE5E,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,yEAAyE;IACzE,QAAQ,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,wBAAwB,CAAC,CAAC;CACtE;AAED;;;GAGG;AACH,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,6BAA6B,GACrC,OAAO,CAAC,QAAQ,CAAC,CAUnB"}
@@ -20,6 +20,14 @@ export interface FieldInfo {
20
20
  */
21
21
  export declare function describeSchemaFields(schema: z.ZodType | undefined): Map<string, FieldInfo>;
22
22
  /** Strict boolean for a RESERVED option — an unrecognised value is a usage error, never a silent `true`. */
23
+ /**
24
+ * The token after a bare boolean flag, when it is the flag's value: exactly
25
+ * `true` or `false` — `--verbose false` is a common habit, and reading it as
26
+ * "on, plus a positional `false`" silently does the opposite of what was said.
27
+ * Only those two words: `1`, `yes` or `no` there stay positionals, since a
28
+ * flag followed by an ordinary value must not swallow it.
29
+ */
30
+ export declare function separateBoolValue(next: string | undefined): boolean | undefined;
23
31
  /** True for a value the reserved-boolean grammar already claims. */
24
32
  export declare function isReservedBoolWord(value: string): boolean;
25
33
  export declare function parseReservedBool(name: string, value: string): boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"args-fields.d.ts","sourceRoot":"","sources":["../../../src/tools/cli/args-fields.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAKxB,MAAM,MAAM,SAAS,GACjB,SAAS,GACT,QAAQ,GACR,QAAQ,GACR,MAAM,GACN,QAAQ,GACR,MAAM,GACN,OAAO,GACP,QAAQ,GACR,OAAO,CAAC;AAEZ,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,SAAS,CAAC;IAChB,uEAAuE;IACvE,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,iFAAiF;IACjF,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AA6ED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,GAAG,SAAS,GAAG,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC,CAI1F;AAKD,4GAA4G;AAC5G,oEAAoE;AACpE,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAGzD;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAKtE;AA8BD,qFAAqF;AACrF,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAKlD;AAuCD,gFAAgF;AAChF,wBAAgB,WAAW,CACzB,IAAI,EAAE,SAAS,GAAG,SAAS,EAC3B,MAAM,EAAE,MAAM,EAAE,EAChB,KAAK,EAAE,MAAM,GACZ,OAAO,CAaT;AAED,wBAAgB,SAAS,CACvB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,IAAI,EAAE,MAAM,EAAE,EACd,KAAK,EAAE,OAAO,GACb,IAAI,CAsBN"}
1
+ {"version":3,"file":"args-fields.d.ts","sourceRoot":"","sources":["../../../src/tools/cli/args-fields.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAKxB,MAAM,MAAM,SAAS,GACjB,SAAS,GACT,QAAQ,GACR,QAAQ,GACR,MAAM,GACN,QAAQ,GACR,MAAM,GACN,OAAO,GACP,QAAQ,GACR,OAAO,CAAC;AAEZ,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,SAAS,CAAC;IAChB,uEAAuE;IACvE,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,iFAAiF;IACjF,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AA6ED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,GAAG,SAAS,GAAG,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC,CAI1F;AAKD,4GAA4G;AAC5G;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,CAI/E;AAED,oEAAoE;AACpE,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAGzD;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAKtE;AA8BD,qFAAqF;AACrF,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAKlD;AAuCD,gFAAgF;AAChF,wBAAgB,WAAW,CACzB,IAAI,EAAE,SAAS,GAAG,SAAS,EAC3B,MAAM,EAAE,MAAM,EAAE,EAChB,KAAK,EAAE,MAAM,GACZ,OAAO,CAaT;AAED,wBAAgB,SAAS,CACvB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,IAAI,EAAE,MAAM,EAAE,EACd,KAAK,EAAE,OAAO,GACb,IAAI,CAsBN"}
@@ -1 +1 @@
1
- {"version":3,"file":"args-route.d.ts","sourceRoot":"","sources":["../../../src/tools/cli/args-route.ts"],"names":[],"mappings":"AAOA;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEpD;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEtD;AAGD,eAAO,MAAM,YAAY,aAA6D,CAAC;AAEvF,eAAO,MAAM,oBAAoB,aAA+C,CAAC;AAEjF,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,OAAO,CAAC;IAChB,UAAU,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC;CAClC;AAED,sFAAsF;AACtF,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS,CAcrF;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,YAAY,EAAE,OAAO,CAAC;IACtB,OAAO,EAAE,OAAO,CAAC;IACjB,mFAAmF;IACnF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AA+BD,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,cAAc,CAAC,EAAE,MAAM,GAAG,YAAY,CA6GlF"}
1
+ {"version":3,"file":"args-route.d.ts","sourceRoot":"","sources":["../../../src/tools/cli/args-route.ts"],"names":[],"mappings":"AAOA;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEpD;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEtD;AAGD,eAAO,MAAM,YAAY,aAA6D,CAAC;AAEvF,eAAO,MAAM,oBAAoB,aAA+C,CAAC;AAEjF,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,OAAO,CAAC;IAChB,UAAU,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC;CAClC;AAED,sFAAsF;AACtF,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS,CAcrF;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,YAAY,EAAE,OAAO,CAAC;IACtB,OAAO,EAAE,OAAO,CAAC;IACjB,mFAAmF;IACnF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AA+BD,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,cAAc,CAAC,EAAE,MAAM,GAAG,YAAY,CAuHlF"}
@@ -1 +1 @@
1
- {"version":3,"file":"args.d.ts","sourceRoot":"","sources":["../../../src/tools/cli/args.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAgB7B,OAAO,EAAE,KAAK,aAAa,EAAkB,MAAM,aAAa,CAAC;AAGjE,yEAAyE;AACzE,MAAM,WAAW,aAAa;IAC5B,sEAAsE;IACtE,IAAI,EAAE,OAAO,CAAC;IACd,iEAAiE;IACjE,IAAI,EAAE,OAAO,CAAC;IACd,8DAA8D;IAC9D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,wEAAwE;IACxE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,yDAAyD;IACzD,KAAK,EAAE,OAAO,CAAC;IACf,qEAAqE;IACrE,MAAM,EAAE,OAAO,CAAC;IAChB,qDAAqD;IACrD,IAAI,EAAE,OAAO,CAAC;IACd,0FAA0F;IAC1F,IAAI,CAAC,EAAE,aAAa,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC5B,mEAAmE;IACnE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,+BAA+B;IAC/B,OAAO,EAAE,aAAa,CAAC;CACxB;AAkRD,wBAAgB,YAAY,CAC1B,IAAI,EAAE,MAAM,EAAE,EACd,MAAM,EAAE,CAAC,CAAC,OAAO,GAAG,SAAS,EAC7B,MAAM,GAAE;IACN,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAChC,aAAa,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5C,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC5B,GACL,aAAa,CAUf;AAED,iFAAiF;AACjF,MAAM,WAAW,qBAAqB;IACpC,0EAA0E;IAC1E,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,wEAAwE;IACxE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,MAAM,EAAE,CAAC,CAAC,SAAS,GAAG,SAAS,GAC9B,qBAAqB,CAgDvB"}
1
+ {"version":3,"file":"args.d.ts","sourceRoot":"","sources":["../../../src/tools/cli/args.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAiB7B,OAAO,EAAE,KAAK,aAAa,EAAkB,MAAM,aAAa,CAAC;AAGjE,yEAAyE;AACzE,MAAM,WAAW,aAAa;IAC5B,sEAAsE;IACtE,IAAI,EAAE,OAAO,CAAC;IACd,iEAAiE;IACjE,IAAI,EAAE,OAAO,CAAC;IACd,8DAA8D;IAC9D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,wEAAwE;IACxE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,yDAAyD;IACzD,KAAK,EAAE,OAAO,CAAC;IACf,qEAAqE;IACrE,MAAM,EAAE,OAAO,CAAC;IAChB,qDAAqD;IACrD,IAAI,EAAE,OAAO,CAAC;IACd,0FAA0F;IAC1F,IAAI,CAAC,EAAE,aAAa,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC5B,mEAAmE;IACnE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,+BAA+B;IAC/B,OAAO,EAAE,aAAa,CAAC;CACxB;AA4RD,wBAAgB,YAAY,CAC1B,IAAI,EAAE,MAAM,EAAE,EACd,MAAM,EAAE,CAAC,CAAC,OAAO,GAAG,SAAS,EAC7B,MAAM,GAAE;IACN,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAChC,aAAa,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5C,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC5B,GACL,aAAa,CAUf;AAED,iFAAiF;AACjF,MAAM,WAAW,qBAAqB;IACpC,0EAA0E;IAC1E,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,wEAAwE;IACxE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,MAAM,EAAE,CAAC,CAAC,SAAS,GAAG,SAAS,GAC9B,qBAAqB,CAkDvB"}
@@ -1 +1 @@
1
- {"version":3,"file":"help.d.ts","sourceRoot":"","sources":["../../../src/tools/cli/help.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,eAAe,EAAoB,MAAM,+BAA+B,CAAC;AAEvF,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAC;AA6DvD;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,sBAAsB,CAAC,EAC7C,MAAM,EAAE,MAAM,GACb,GAAG,CAAC,MAAM,EAAE,sBAAsB,CAAC,CASrC;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,sBAAsB,CAAC,CAAC;IAC9C,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,kBAAkB,EAAE,SAAS,eAAe,EAAE,CAAC;IAC/C,wEAAwE;IACxE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mEAAmE;IACnE,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,GAAG,MAAM,CAqCT;AAED,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,sBAAsB,EAClC,kBAAkB,GAAE,SAAS,eAAe,EAAO,GAClD,MAAM,CAsDR"}
1
+ {"version":3,"file":"help.d.ts","sourceRoot":"","sources":["../../../src/tools/cli/help.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,eAAe,EAAoB,MAAM,+BAA+B,CAAC;AAEvF,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAC;AA6DvD;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,sBAAsB,CAAC,EAC7C,MAAM,EAAE,MAAM,GACb,GAAG,CAAC,MAAM,EAAE,sBAAsB,CAAC,CASrC;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,sBAAsB,CAAC,CAAC;IAC9C,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,kBAAkB,EAAE,SAAS,eAAe,EAAE,CAAC;IAC/C,wEAAwE;IACxE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mEAAmE;IACnE,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,GAAG,MAAM,CAqCT;AAED,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,sBAAsB,EAClC,kBAAkB,GAAE,SAAS,eAAe,EAAO,GAClD,MAAM,CA6DR"}
package/llms/cli.txt CHANGED
@@ -194,12 +194,18 @@ string, the schema says what it should be:
194
194
  | -------------------- | ---------------------------------------------- |
195
195
  | `z.string()` | `--name "box"` or a positional |
196
196
  | `z.number()` | `--count 3` → `3` |
197
- | `z.boolean()` | `--active` (presence) / `--no-active` |
197
+ | `z.boolean()` | `--active` (presence) / `--active false` / `--no-active` |
198
198
  | `z.enum([...])` | `--size large` |
199
199
  | `z.array(z.string())`| `--tag a --tag b` → `["a","b"]` |
200
200
  | `z.object({...})` | `--opts '{"k":"v"}'` (JSON) or `--opts.k v` |
201
201
  | `.optional()` / `.default()` | not required |
202
202
 
203
+ After a bare boolean flag, the next token is its value when it is exactly
204
+ `true` or `false` — `--active false` turns it off, as it reads. Any other word
205
+ there stays a positional, and `--` makes a literal `false` one too. The same
206
+ holds for a short alias, a framework flag such as `--json false` and an
207
+ application option.
208
+
203
209
  Without presentation configuration, positional arguments fill non-boolean
204
210
  fields in declaration order, so `myapp generate "a fox"` is
205
211
  `--prompt "a fox"`. A piped value fills the first required unset field:
@@ -258,7 +264,7 @@ framework globals may precede that explicit command. Top-level `--help`, `-h`
258
264
  and `--version` never execute the default, and top-level help marks it.
259
265
 
260
266
  Aliases are command-local, one ASCII letter and validated against the resolved
261
- command schema. `-f` / `-f=false` are boolean forms; values accept `-n 100` and
267
+ command schema. `-f` / `-f=false` / `-f false` are boolean forms; values accept `-n 100` and
262
268
  `-n=100`. Arrays accumulate across short and long forms. `-h` is reserved,
263
269
  bundles such as `-fn`, attached values such as `-n100`, `--no-f` and unknown
264
270
  short flags are rejected. Canonical `--no-follow` remains available.
@@ -577,6 +577,18 @@ A payload goes into an audit row only after `sanitizePayload`:
577
577
  `tokenizer`) survive. This applies wherever a key exists — an object field and
578
578
  a `Map` entry alike, at any depth. A `Set` member has no key, so nothing there
579
579
  is masked by name;
580
+ - **secrets are masked by their shape** in every string, whatever key holds
581
+ it — the secret half of a Telegram bot token (`123456:[redacted]`: the bot's
582
+ id stays, it tells two bots apart), the password in
583
+ `postgresql://user:password@host` (user, host and path stay), and the value
584
+ of a query parameter named as a secret (`?access_token=`, `?api-key=`,
585
+ `?key=`, `?sig=`). A URL in an error message or a request log line is the
586
+ usual way a token reaches a journal;
587
+ - **an error keeps what it says about itself** — its own fields (`error_code`,
588
+ `parameters`, a provider's `status`) are written beside `name`, `message`,
589
+ `stack` and `cause`, masked by the same rules; a field holding a class
590
+ instance other than an error (grammY's `BotError.ctx`) is named, not written,
591
+ so one error cannot push its line past the bound and lose the stack;
580
592
  - **binary blobs** (`Uint8Array`, `Blob`, `FormData`) collapse to metadata —
581
593
  never the bytes;
582
594
  - the result is **capped** — anything over the byte limit becomes a preview.
@@ -835,7 +847,8 @@ audit event. See the [Observability guide](../guide/observability.md).
835
847
  | `createBoundedLogger` | function | decorate a `StitchLogger` with request context, shared sanitisation, redaction and total bounds |
836
848
  | `DEFAULT_REDACT_PATHS` | constant | baseline credential/token paths added to the sanitizer's sensitive-key policy |
837
849
  | `createJsonLogger` | function | the process journal: one JSON line per call in pino's shape (numeric `level`, epoch `time`, `msg`) on standard output, through `createBoundedLogger` — an `Error` in any field keeps name, message, stack and cause; secrets masked; `level` threshold, static `fields`, injectable `write` |
838
- | `JsonLoggerOptions` / `JsonLogLevel` / `JSON_LOG_LEVELS` | _type_ / constant | the bounded-logger options plus `level`, `fields`, `write`, `now`; pino's numbers `debug` 20 … `error` 50, which a line reader matches on |
850
+ | `JsonLoggerOptions` / `JsonLogLevel` / `JSON_LOG_LEVELS` | _type_ / constant | the bounded-logger options plus `level`, `fields`, `write`, `now`; pino's numbers `debug` 20 … `error` 50, which a line reader matches on; `sensitiveValues` masks exact secrets in every string |
851
+ | `secretValuesFromEnv` / `SecretValuesOptions` | function / _type_ | every secret an environment holds, for `sensitiveValues`: variables named as a token, secret, key, password or credential, the password and secret query values in any URL variable, each URL-encoded too, longest first; values under `minLength` (8) skipped |
839
852
  | `BoundedLoggerBounds` / `BoundedLoggerOptions` | _type_ | per-value and total record ceilings (`stringLengthByKey` raises the string bound for named keys such as `stack`) plus sink/redaction configuration |
840
853
  | `RequestEvent` | _type_ | the normalised audit event handed to the sink; opt-in HTTP cancellation rows carry `outcome: 'cancelled'` |
841
854
  | `runUnitOfWork` / `RunUnitOfWorkOptions` | function / _type_ | run work that did not arrive over a transport — an agent loop, a scheduled broadcast — inside one context, with the same completion record a request gets |
@@ -888,7 +901,7 @@ audit event. See the [Observability guide](../guide/observability.md).
888
901
  | Export | Kind | Summary |
889
902
  |--------|------|---------|
890
903
  | `sanitizePayload` | function | redact secrets and cap size — [guide](../guide/observability.md#sanitisation) |
891
- | `redact` | function | mask secret-named keys, drop binary blobs |
904
+ | `redact` | function | mask secret-named keys and secrets recognised by shape in every string — the secret half of a bot token, a password in an address, a query parameter named as a secret; drop binary blobs; an error keeps its own fields (`error_code`, `parameters`), a class instance on one is named, not written |
892
905
  | `truncatePreview` | function | cap a value by serialised size |
893
906
  | `measureSize` | function | item count + byte size of a result |
894
907
  | `JsonValue` | _type_ | a JSON-serialisable value |
@@ -0,0 +1,193 @@
1
+ # stitchkit/react/keyboard — stitchkit docs slice
2
+
3
+ > Generated from the `docs/` tree by `bun run gen:llms`. The index of all slices is
4
+ > `llms.txt` in this package.
5
+
6
+ Contains: Guide: Keyboard layers and list zones (docs/guide/keyboard.md) · API reference: `stitchkit/react/keyboard` (docs/api/reference.md)
7
+
8
+ ==============================================================================
9
+ # Guide: Keyboard layers and list zones (docs/guide/keyboard.md)
10
+ ==============================================================================
11
+
12
+ ---
13
+ title: Keyboard layers and list zones
14
+ description: One listener decides which part of a screen gets a key; lists move real focus with the arrows
15
+ type: guide
16
+ status: active
17
+ created: 2026-09-29
18
+ updated: 2026-09-29
19
+ ---
20
+
21
+ # Keyboard layers and list zones
22
+
23
+ `stitchkit/react/keyboard` answers one question for a React screen: **who gets
24
+ this key?** It is headless — three hooks and no component, no styles, no
25
+ router. The application keeps its URL, its sections and its markup; the hooks
26
+ decide which part of the screen hears a key and move focus through lists.
27
+
28
+ ```bash
29
+ bun add react # the only peer
30
+ ```
31
+
32
+ ## Why layers
33
+
34
+ A screen with a menu, a dialog, a list and section shortcuts usually grows a
35
+ `keydown` listener per concern, and they run in the order they mounted:
36
+ Escape closes the dialog *and* the page behind it, an arrow in a dialog also
37
+ switches the section underneath, a hidden panel still reacts. Here every
38
+ concern is a layer on one stack, and the stack asks them in a fixed order until
39
+ one takes the key:
40
+
41
+ | Kind | What it is | Example |
42
+ |---|---|---|
43
+ | `overlay` | something modal; while one is live nothing below hears any key | a dialog |
44
+ | `local` | a component with the user's attention, not modal | an open command menu |
45
+ | `route` | the page's "up one level" | Escape closes the open entity |
46
+ | `zone` | a list the arrows move through (see below) | the chat list |
47
+ | `global` | shortcuts of the whole screen | ← / → switch sections |
48
+
49
+ Within a kind the innermost layer wins: of two layers whose elements nest, the
50
+ inner one, otherwise the one activated last. One press of Escape closes one
51
+ level, and holding Escape does not close several.
52
+
53
+ The stack listens on the window, in the bubbling phase. Everything closer to the
54
+ key hears it first — a widget's own `onKeyDown`, a text field, a popover that
55
+ closes on Escape — and keeps it with `preventDefault()` or `stopPropagation()`.
56
+ A layer only gets what nobody nearer wanted, so third-party components keep
57
+ working unchanged.
58
+
59
+ ## Shortcuts and Escape
60
+
61
+ ```tsx
62
+ import { useRef } from 'react'
63
+ import { useEscapeLayer, useKeyLayer } from 'stitchkit/react/keyboard'
64
+
65
+ function Workspace({ next, previous, children }) {
66
+ useKeyLayer({
67
+ kind: 'global',
68
+ onKey: (event) => {
69
+ if (event.key === 'ArrowRight') next()
70
+ else if (event.key === 'ArrowLeft') previous()
71
+ else return false // not taken: the key keeps its default
72
+ return true
73
+ },
74
+ })
75
+ return children
76
+ }
77
+
78
+ function EntityPanel({ close }) {
79
+ const panel = useRef<HTMLElement>(null)
80
+ useEscapeLayer({ kind: 'route', scope: panel, onEscape: close })
81
+ return <section ref={panel}>…</section>
82
+ }
83
+ ```
84
+
85
+ `onKey` returns `true` when it took the key: its default is prevented and no
86
+ layer below hears it. `onEscape` returns `false` when there was nothing to close,
87
+ and the next layer is asked.
88
+
89
+ **`scope`** ties a layer to an element. A scoped layer is heard only while the
90
+ element is mounted, visible and outside any `inert` subtree — a panel kept
91
+ mounted in the background stops hearing keys without an `active` flag — and it
92
+ outranks layers of its kind whose elements contain it. React runs a child's
93
+ effects before its parent's, so when a page and the panel inside it mount
94
+ together, only `scope` tells the stack which one is inside.
95
+
96
+ **`active`** (default `true`) switches a layer off without unmounting it;
97
+ becoming active again puts it on top of its kind.
98
+
99
+ **Fields keep their keys.** Keys pressed in an input, a text area, a select, an
100
+ editable region, a combobox, a slider or any area marked `data-local-keys` do not
101
+ reach layers — except Escape, which a field that wants it keeps with
102
+ `preventDefault()`. `fields: 'include'` lets a layer hear them anyway.
103
+ `isKeyFieldTarget(event.target)` is the same test, for a handler of your own.
104
+
105
+ ## List zones
106
+
107
+ A zone is a list the arrows move through with **real focus**: one item is the
108
+ tab stop (roving `tabIndex`), Tab enters the list at it and leaves the list, the
109
+ arrows move DOM focus between items. Screen readers follow it, `:focus-visible`
110
+ draws it, the browser scrolls to it.
111
+
112
+ ```tsx
113
+ import { useNavZone } from 'stitchkit/react/keyboard'
114
+
115
+ function ChatList({ chats, openId, open }) {
116
+ const zone = useNavZone({
117
+ items: chats.map((chat) => chat.id),
118
+ value: openId,
119
+ onChange: (id) => open(id),
120
+ label: 'Chats',
121
+ })
122
+ return (
123
+ <ul {...zone.zoneProps}>
124
+ {chats.map((chat) => (
125
+ <li key={chat.id} {...zone.itemProps(chat.id)}>
126
+ {chat.title}
127
+ </li>
128
+ ))}
129
+ </ul>
130
+ )
131
+ }
132
+ ```
133
+
134
+ | Option | Default | Meaning |
135
+ |---|---|---|
136
+ | `items`, `value`, `onChange` | — | the ids in order, the chosen one, and the report of a choice; the zone never holds the value |
137
+ | `mode` | `'select'` | `'select'`: an arrow chooses (`onChange(id, 'arrow')`). `'highlight'`: an arrow only moves focus, Enter chooses (`onChange(id, 'confirm')`) |
138
+ | `orientation` | `'vertical'` | which arrows move; the other axis enters and exits |
139
+ | `role` | `'listbox'` | `'listbox'` of options or `'tablist'` of tabs, marked `aria-selected`; `'none'` keeps the items' own semantics (links, buttons) and marks the chosen one `aria-current` |
140
+ | `loop` | `false` | wrap past the ends instead of calling `onBoundary` |
141
+ | `onEnter(id)` | — | → in a vertical zone (↓ in a horizontal one), and Enter in select mode: go into what the item opens |
142
+ | `onExit()` | — | ← in a vertical zone (↑ in a horizontal one) |
143
+ | `onBoundary(side)` | — | a move past `'start'` or `'end'`: hand focus to the neighbouring zone |
144
+ | `active` | `true` | whether arrows pressed outside every zone land here |
145
+ | `label` | — | the zone's accessible name |
146
+ | `onReveal(id)` | — | focus went to an item with no element yet — a row of a virtualized list outside the rendered range; scroll it in, and focus lands on it when it mounts |
147
+
148
+ The callbacks return `false` to leave the key to the page, anything else takes
149
+ it. A key the zone does not take bubbles on to the layers — in a vertical list
150
+ ← and → are not the list's keys, so the section switch above hears them.
151
+
152
+ Each zone keeps its own focused item; two zones never share one. When focus is
153
+ in no zone at all — right after the page loads — the first arrow on the zone's
154
+ axis puts focus on the active zone's current item. To move between zones
155
+ (sidebar → table), call `zone.focus(id?)` of the other zone from `onEnter` or
156
+ `onBoundary`. `zone.element` is the zone's element, for a layer scoped to it.
157
+
158
+ ## What stays in the application
159
+
160
+ The URL, the router, sections and which screen is open. A layer calls the
161
+ application's own functions — `router.push`, `close()` — and nothing here reads
162
+ or writes a location. Keep entity coordinates in the path and filters in the
163
+ query, parse and build them in one pure module with tests, and use explicit
164
+ `push` / `replace` rather than a positional flag.
165
+
166
+ ==============================================================================
167
+ # API reference: `stitchkit/react/keyboard` (docs/api/reference.md)
168
+ ==============================================================================
169
+
170
+ ## `stitchkit/react/keyboard`
171
+
172
+ Browser-only, headless keyboard handling for React: which part of a screen gets a
173
+ key, and lists the arrows move through with real focus. Needs the `react` peer;
174
+ no component, no styles, no router — see the [keyboard guide](../guide/keyboard.md).
175
+
176
+ | Export | Kind | Summary |
177
+ |--------|------|---------|
178
+ | `useKeyLayer` | hook | put a layer on the window's one key stack — asked `overlay` → `local` → `route` → `zone` → `global`, innermost first within a kind |
179
+ | `useEscapeLayer` | hook | an `overlay`, `local` or `route` layer for Escape: one press closes one level |
180
+ | `useNavZone` | hook | a list zone — roving tab stop, arrows move DOM focus, select or highlight mode, cross-axis enter/exit, boundaries |
181
+ | `nextZoneIndex` | function | where a move lands in a list of a given length: an index, `'before-start'` or `'after-end'` |
182
+ | `isKeyFieldTarget` | function | whether a key was pressed in a field or a `data-local-keys` area, the test layers use |
183
+ | `KeyLayerOptions` | _type_ | `kind`, `onKey`, `active`, `scope`, `fields` |
184
+ | `KeyLayerKind` | _type_ | `'overlay' \| 'local' \| 'route' \| 'zone' \| 'global'` |
185
+ | `EscapeLayerOptions` | _type_ | `kind`, `onEscape`, `active`, `scope` |
186
+ | `NavZoneOptions` | _type_ | `items`, `value`, `onChange`, `mode`, `orientation`, `role`, `loop`, `onEnter`, `onExit`, `onBoundary`, `active`, `label`, `onReveal` |
187
+ | `NavZone` | _type_ | what `useNavZone` returns — `zoneProps`, `itemProps(id)`, `element`, `current`, `focus(id?)` |
188
+ | `NavZoneProps` / `NavZoneItemProps` | _type_ | the props spread on the zone's element and on each item |
189
+ | `NextZoneIndexInput` | _type_ | `length`, `index`, `move`, `loop` |
190
+ | `ZoneItemId` | _type_ | an item id — `string \| number` |
191
+ | `ZoneChangeCause` | _type_ | `'arrow' \| 'confirm'` |
192
+ | `ZoneMove` | _type_ | `'next' \| 'previous' \| 'first' \| 'last'` |
193
+ | `ZoneOrientation` | _type_ | `'vertical' \| 'horizontal'` |
package/llms/server.4.txt CHANGED
@@ -357,6 +357,11 @@ Three things it does that a hand-written check usually does not:
357
357
  - `raw` keeps every signed pair, so a field Telegram adds after this release
358
358
  does not need a release to reach.
359
359
 
360
+ The user arrives `camelCase` — `firstName`, `languageCode` — as `TelegramUser`.
361
+ A bot meets the same person as grammY's `ctx.from`, in Telegram's `snake_case`;
362
+ `parseTelegramUser(ctx.from)` gives the same `TelegramUser`, so one
363
+ `ensureAccount(user)` serves the Mini App and the chat.
364
+
360
365
  `maxAgeSeconds` is optional and there is no default: only the application knows
361
366
  how long its own session is worth. Omitting it means the string never expires,
362
367
  which is a decision rather than an oversight.
@@ -386,6 +391,17 @@ and our payload is wrong, which is the case a list of substrings quietly counts
386
391
  against the user. An unrecognised refusal leaves the recipient reachable: losing
387
392
  a working subscriber forever costs more than one wasted send.
388
393
 
394
+ `TELEGRAM_SEND_FAILURE_REASONS` lists every reason. A table keyed by
395
+ `TelegramSendFailureReason` — error codes, the text a person reads — fails to
396
+ compile when a release adds one, instead of falling through to a default:
397
+
398
+ ```ts
399
+ const codes: Record<TelegramSendFailureReason, string> = {
400
+ 'blocked-by-user': 'E_BLOCKED',
401
+ /* … every reason … */
402
+ }
403
+ ```
404
+
389
405
  The rest of what a bot is assembled from — its resources, journal, operator
390
406
  channel, broadcasts and local Bot API files — is in [Telegram bots](telegram.md).
391
407
 
@@ -69,7 +69,9 @@ own, recorded as an ADR.
69
69
  | `stitchkit/cli` | server | stable | `createCli` — the CLI transport, light (no MCP SDK / `ai`) |
70
70
  | `stitchkit/remote` | browser **and** server | stable | peer-free `implementRemote` for thin HTTP proxy processes |
71
71
  | `stitchkit/files` | server (Bun or Node) | stable | peer-free managed local-file boundary |
72
- | `stitchkit/telegram` | server (Bun or Node) | evolving | peer-free Telegram platform primitives — Mini App `initData` verification, send-failure classification, resumable broadcasts, the operator channel and local Bot API files |
72
+ | `stitchkit/telegram` | server (Bun or Node) | evolving | peer-free Telegram platform primitives — Mini App `initData` verification, send-failure classification, resumable broadcasts, the operator channel with dedupe, local Bot API files, webhook ownership and durable webhook update intake |
73
+ | `stitchkit/telegram/html` | browser and server | evolving | Telegram's HTML as a tree — clean any markup to what Telegram accepts, cut long text into valid parts by what the reader sees, check markup before it is sent |
74
+ | `stitchkit/telegram/screens` | server (Bun or Node) | evolving | a bot's menus as declared screens over grammY — typed paths, actions and input, message reconciliation, state in any `StorageAdapter`, a test chat |
73
75
  | `stitchkit/tracking` | browser **and** server | evolving | visitor-tracking mechanics — `createTrackingClient`, the tab-shared outbox, the page-leave beacon, attribution, the contract factory; no event vocabulary, no React |
74
76
  | `stitchkit/tracking/server` | server (Bun or Node) | evolving | the decisions a tracking backend makes — dispositions, visit lease over an application-owned store, active intervals, presence; no database |
75
77
  | `stitchkit/release` | browser **and** server | evolving | a page follows the release it was built for — `createReleaseMarker` on the server, `createReleaseWatcher` in the browser, the `X-Build-Id` header and a socket event between them |
@@ -77,11 +79,12 @@ own, recorded as an ADR.
77
79
  | `stitchkit/geo` | server (Bun or Node) | evolving | managed GeoIP reader generations, last-known-good reload and the optional MaxMind adapter |
78
80
  | `stitchkit/oauth` | browser **and** server | evolving | provider-neutral Authorization Code + PKCE request and one-shot callback transaction mechanics |
79
81
  | `stitchkit/google` | server (Bun or Node) | evolving | optional Google code exchange and verified OIDC identity adapter |
80
- | `stitchkit/observability` | server | stable<br>_redefined in 2 of the 45 minors since 0.56.2, most recently 0.92.0_ | request/tool event projections — `createObservability`, trace context, sanitisation |
82
+ | `stitchkit/observability` | server | stable<br>_redefined in 2 of the 46 minors since 0.56.2, most recently 0.92.0_ | request/tool event projections — `createObservability`, trace context, sanitisation |
81
83
  | `stitchkit/testing` | tests on Bun or Node | stable | in-process generated clients over a real Fetch handler, plus the managed-resource conformance kit |
82
84
  | `stitchkit/declaration` | browser + build and deployment tooling (Bun or Node) | evolving | `ProjectDeclarationSchema` — the one machine-readable statement a repository makes about itself |
83
85
  | `stitchkit/react` | browser + server rendering | stable | `createCursorQuery`, `createCacheBridge`, QueryClient and `ApiError` retry policy |
84
- | `stitchkit/agent-runtime` | server | evolving<br>_redefined in 22 of the 45 minors since 0.56.2, most recently 0.97.0_ | optional durable conversation/run loop, history, models, prompts, fencing and events |
86
+ | `stitchkit/react/keyboard` | browser | evolving | which part of a screen gets a key — ordered layers, one Escape per level, list zones with real focus; see [keyboard guide](keyboard.md) |
87
+ | `stitchkit/agent-runtime` | server | evolving<br>_redefined in 22 of the 46 minors since 0.56.2, most recently 0.97.0_ | optional durable conversation/run loop, history, models, prompts, fencing and events |
85
88
  | `stitchkit/agent-runtime/sandbox` | server | evolving | optional Linux Bubblewrap sessions, durable workspaces and a host HTTP credential gateway; see [sandbox guide](sandbox.md) |
86
89
  | `stitchkit/agent-runtime/testing` | tests on Bun or Node | evolving | credential-free replay, scripted provider faults, deterministic race controls and the agent-store conformance kit |
87
90
  | `stitchkit/agent-runtime/harness` | server | evolving | resource-aware process-local facade over the canonical Agent runtime; supervision stays outside |
@@ -91,7 +94,7 @@ own, recorded as an ADR.
91
94
  | `stitchkit/agent-runtime/sqlite/bun` | server (Bun) | evolving | durable built-in SQLite store for the agent runtime |
92
95
  | `stitchkit/agent-runtime/sqlite/node` | server (Node ≥ 22.5) | evolving | durable built-in SQLite store for the agent runtime |
93
96
  | `stitchkit-tui` | terminal (Bun) | evolving | optional official OpenTUI host over a caller-composed headless runtime |
94
- | `stitchkit/application` | browser + server | evolving<br>_redefined in 10 of the 45 minors since 0.56.2, most recently 0.100.0_ | managed resource graph, readiness, admission, schedules, subtree restart and bounded shutdown |
97
+ | `stitchkit/application` | browser + server | evolving<br>_redefined in 10 of the 46 minors since 0.56.2, most recently 0.100.0_ | managed resource graph, readiness, admission, schedules, subtree restart and bounded shutdown |
95
98
  | `stitchkit/application/grammy` | server | evolving | a grammY bot as application resources — configuration, then polling with updates admitted by batch — and the webhook adapter |
96
99
  | `stitchkit/application/opentelemetry` | server | evolving | maps application snapshots onto an injected OpenTelemetry `Meter` |
97
100
  | `stitchkit/application/schemas` | browser + server | evolving | the application's snapshot, health and shutdown schemas alone, without the kernel |
@@ -203,6 +206,7 @@ map — feature → packages:
203
206
  | MCP host/client tests | `@modelcontextprotocol/client` |
204
207
  | MCP Apps UI widgets | `@modelcontextprotocol/ext-apps` |
205
208
  | React data layer (`stitchkit/react`) | `@tanstack/react-query` `react-query-kit` |
209
+ | Keyboard layers and list zones (`stitchkit/react/keyboard`) | `react` |
206
210
  | MaxMind GeoIP (`stitchkit/geo`) | `maxmind` |
207
211
  | Browser OAuth client (`stitchkit/oauth`) | — |
208
212
  | Google OIDC verifier (`stitchkit/google`) | `google-auth-library` |
@@ -211,6 +215,8 @@ map — feature → packages:
211
215
  | Socket.IO client | `socket.io-client` (runtime peer; unrelated root declarations remain peer-free) |
212
216
  | grammY lifecycle adapters (`stitchkit/application/grammy`) | `grammy` |
213
217
  | Telegram platform primitives (`stitchkit/telegram`) | — (peer-free) |
218
+ | Telegram message markup (`stitchkit/telegram/html`) | — (peer-free) |
219
+ | Telegram screens (`stitchkit/telegram/screens`) | `grammy` (types only; the bot brings it) |
214
220
  | OpenTelemetry gauges (`stitchkit/application/opentelemetry`) | `@opentelemetry/api` |
215
221
 
216
222
  ```bash