stitchkit 0.103.13 → 0.104.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (223) hide show
  1. package/CHANGELOG.md +120 -0
  2. package/darwin-native.cjs +1 -3
  3. package/dist/agent-runtime/store-conformance-lifecycle.d.ts +10 -10
  4. package/dist/agent-runtime/store-conformance-support.d.ts +5 -5
  5. package/dist/application/directory-inbox-accept.d.ts +13 -0
  6. package/dist/application/directory-inbox-accept.d.ts.map +1 -0
  7. package/dist/application/directory-inbox-contract.d.ts +42 -5
  8. package/dist/application/directory-inbox-contract.d.ts.map +1 -1
  9. package/dist/application/directory-inbox-delivery.d.ts +14 -0
  10. package/dist/application/directory-inbox-delivery.d.ts.map +1 -0
  11. package/dist/application/directory-inbox-files.d.ts +28 -0
  12. package/dist/application/directory-inbox-files.d.ts.map +1 -0
  13. package/dist/application/directory-inbox-state.d.ts +14 -0
  14. package/dist/application/directory-inbox-state.d.ts.map +1 -0
  15. package/dist/application/directory-inbox.d.ts +2 -1
  16. package/dist/application/directory-inbox.d.ts.map +1 -1
  17. package/dist/application/file-state-store.d.ts +2 -8
  18. package/dist/application/file-state-store.d.ts.map +1 -1
  19. package/dist/application/state-store.d.ts +6 -1
  20. package/dist/application/state-store.d.ts.map +1 -1
  21. package/dist/browser/client-arguments.d.ts +4 -0
  22. package/dist/browser/client-arguments.d.ts.map +1 -0
  23. package/dist/browser/client-executors.d.ts.map +1 -1
  24. package/dist/browser/client.d.ts.map +1 -1
  25. package/dist/browser/contract-stream.d.ts.map +1 -1
  26. package/dist/browser/http.d.ts +2 -1
  27. package/dist/browser/http.d.ts.map +1 -1
  28. package/dist/browser/native-fetch.d.ts +8 -0
  29. package/dist/browser/native-fetch.d.ts.map +1 -0
  30. package/dist/contract/errors.d.ts +16 -12
  31. package/dist/contract/errors.d.ts.map +1 -1
  32. package/dist/contract/normalize.d.ts +5 -1
  33. package/dist/contract/normalize.d.ts.map +1 -1
  34. package/dist/contract/stream.d.ts +1 -0
  35. package/dist/contract/stream.d.ts.map +1 -1
  36. package/dist/entrypoints/agent-runtime/browser.js +3 -3
  37. package/dist/entrypoints/agent-runtime/coding-tools.js +6 -6
  38. package/dist/entrypoints/agent-runtime/harness-tools.js +8 -8
  39. package/dist/entrypoints/agent-runtime/harness.js +11 -11
  40. package/dist/entrypoints/agent-runtime/realtime.js +3 -3
  41. package/dist/entrypoints/agent-runtime/sandbox.js +6 -6
  42. package/dist/entrypoints/agent-runtime.js +13 -13
  43. package/dist/entrypoints/application/diagnostic-journal.js +1 -1
  44. package/dist/entrypoints/application/directory-inbox.js +424 -119
  45. package/dist/entrypoints/application/grammy.js +1 -1
  46. package/dist/entrypoints/application.d.ts +2 -2
  47. package/dist/entrypoints/application.d.ts.map +1 -1
  48. package/dist/entrypoints/application.js +7 -3
  49. package/dist/entrypoints/cli.d.ts +2 -0
  50. package/dist/entrypoints/cli.d.ts.map +1 -1
  51. package/dist/entrypoints/cli.js +757 -136
  52. package/dist/entrypoints/contract.js +3 -3
  53. package/dist/entrypoints/files/packaging.d.ts +3 -0
  54. package/dist/entrypoints/files/packaging.d.ts.map +1 -0
  55. package/dist/entrypoints/files/packaging.js +133 -0
  56. package/dist/entrypoints/files.js +10 -107
  57. package/dist/entrypoints/index.js +7 -7
  58. package/dist/entrypoints/node.js +8 -8
  59. package/dist/entrypoints/observability.js +2 -2
  60. package/dist/entrypoints/react.js +1 -1
  61. package/dist/entrypoints/remote.js +19 -6
  62. package/dist/entrypoints/server.d.ts +1 -1
  63. package/dist/entrypoints/server.d.ts.map +1 -1
  64. package/dist/entrypoints/server.js +23 -17
  65. package/dist/entrypoints/telegram.d.ts +1 -0
  66. package/dist/entrypoints/telegram.d.ts.map +1 -1
  67. package/dist/entrypoints/telegram.js +214 -98
  68. package/dist/entrypoints/testing.js +7 -7
  69. package/dist/entrypoints/tools/connections.d.ts +2 -2
  70. package/dist/entrypoints/tools/connections.d.ts.map +1 -1
  71. package/dist/entrypoints/tools/connections.js +427 -294
  72. package/dist/entrypoints/tools/invoker.js +6 -6
  73. package/dist/entrypoints/tools/mcp.d.ts +12 -0
  74. package/dist/entrypoints/tools/mcp.d.ts.map +1 -0
  75. package/dist/entrypoints/tools/mcp.js +45 -0
  76. package/dist/entrypoints/tools.d.ts +1 -0
  77. package/dist/entrypoints/tools.d.ts.map +1 -1
  78. package/dist/entrypoints/tools.js +82 -1147
  79. package/dist/entrypoints/tracking.js +1 -1
  80. package/dist/files/boundary.d.ts +2 -8
  81. package/dist/files/boundary.d.ts.map +1 -1
  82. package/dist/files/file-io.d.ts +0 -5
  83. package/dist/files/file-io.d.ts.map +1 -1
  84. package/dist/files/native-packaging-layout.d.ts +12 -0
  85. package/dist/files/native-packaging-layout.d.ts.map +1 -0
  86. package/dist/files/native-packaging.d.ts +72 -0
  87. package/dist/files/native-packaging.d.ts.map +1 -0
  88. package/dist/files/read.d.ts +2 -2
  89. package/dist/files/read.d.ts.map +1 -1
  90. package/dist/index-1a6jtnd9.js +88 -0
  91. package/dist/index-269zqj8m.js +77 -0
  92. package/dist/{index-r34mvse1.js → index-27a2pj09.js} +1 -1
  93. package/dist/{index-r956rndn.js → index-2hyavjjj.js} +3 -3
  94. package/dist/{index-m2xqkv55.js → index-2vnh05w4.js} +83 -14
  95. package/dist/{index-pw99k9h5.js → index-43p08g7t.js} +1 -1
  96. package/dist/{index-xwq9j1sf.js → index-4e0ah203.js} +9 -7
  97. package/dist/{index-qapzv4p5.js → index-5z6jn897.js} +1 -1
  98. package/dist/{index-ahvsksw5.js → index-6d9xaxw1.js} +1 -1
  99. package/dist/{index-8zfsgar0.js → index-6drst9sg.js} +11 -5
  100. package/dist/{index-x87j7csh.js → index-77nxk6ay.js} +2 -2
  101. package/dist/{index-dnn9z96q.js → index-a0zzh8ak.js} +3 -3
  102. package/dist/{index-kmpzj1cq.js → index-a3wma1hk.js} +1 -1
  103. package/dist/{index-j8aw2c0e.js → index-bakgedh8.js} +35 -7
  104. package/dist/index-bhb950qs.js +1145 -0
  105. package/dist/{index-tgzhq07n.js → index-cvda079a.js} +1 -1
  106. package/dist/{index-n2gre124.js → index-fa4wg312.js} +4 -4
  107. package/dist/{index-zp4jpkp0.js → index-fk18k56k.js} +2 -2
  108. package/dist/{index-afjvjyck.js → index-g2j7pnak.js} +2 -134
  109. package/dist/index-g95fk660.js +116 -0
  110. package/dist/{index-r5c4fedv.js → index-h2fhjqy7.js} +2 -1
  111. package/dist/{index-sybedc8b.js → index-hbd32c6a.js} +23 -94
  112. package/dist/{index-fyphpknh.js → index-hp9d77wk.js} +1 -1
  113. package/dist/index-jd470k6q.js +109 -0
  114. package/dist/{index-b2bz0kdm.js → index-kb8styz3.js} +4 -3
  115. package/dist/{index-0c419yx7.js → index-kemm0ej5.js} +12 -9
  116. package/dist/{index-db2spkv1.js → index-ptn0g0gj.js} +3 -3
  117. package/dist/{index-w575evhq.js → index-r4r70zmh.js} +1 -1
  118. package/dist/{index-12m2v2a9.js → index-s4evrmp5.js} +1 -1
  119. package/dist/{index-1khy8y7n.js → index-t1sr4aks.js} +3 -3
  120. package/dist/{index-45tdvqa3.js → index-wh0ecvyk.js} +1 -1
  121. package/dist/{index-n53mswt3.js → index-x928baz2.js} +8 -1
  122. package/dist/index-yk4g8805.js +133 -0
  123. package/dist/{index-ygkka6zn.js → index-ywppsaby.js} +28 -5
  124. package/dist/{index-9853ymwb.js → index-za6j7nwy.js} +18 -4
  125. package/dist/internal/bounded-file-read.d.ts +41 -0
  126. package/dist/internal/bounded-file-read.d.ts.map +1 -0
  127. package/dist/internal/exclusive-lock.d.ts +2 -0
  128. package/dist/internal/exclusive-lock.d.ts.map +1 -1
  129. package/dist/internal/with-exclusive-lock.d.ts +4 -2
  130. package/dist/internal/with-exclusive-lock.d.ts.map +1 -1
  131. package/dist/telegram/broadcast-delivery.d.ts +23 -0
  132. package/dist/telegram/broadcast-delivery.d.ts.map +1 -0
  133. package/dist/telegram/broadcast-failure.d.ts +22 -0
  134. package/dist/telegram/broadcast-failure.d.ts.map +1 -0
  135. package/dist/telegram/broadcast-state.d.ts +6 -3
  136. package/dist/telegram/broadcast-state.d.ts.map +1 -1
  137. package/dist/telegram/broadcast.d.ts +10 -5
  138. package/dist/telegram/broadcast.d.ts.map +1 -1
  139. package/dist/tools/cli/format.d.ts.map +1 -1
  140. package/dist/tools/cli/invoke.d.ts +2 -2
  141. package/dist/tools/cli/invoke.d.ts.map +1 -1
  142. package/dist/tools/cli/publication-assets.d.ts +10 -0
  143. package/dist/tools/cli/publication-assets.d.ts.map +1 -0
  144. package/dist/tools/cli/publication-contract.d.ts +75 -0
  145. package/dist/tools/cli/publication-contract.d.ts.map +1 -0
  146. package/dist/tools/cli/publication-control.d.ts +4 -0
  147. package/dist/tools/cli/publication-control.d.ts.map +1 -0
  148. package/dist/tools/cli/publication-storage.d.ts +32 -0
  149. package/dist/tools/cli/publication-storage.d.ts.map +1 -0
  150. package/dist/tools/cli/publication.d.ts +35 -0
  151. package/dist/tools/cli/publication.d.ts.map +1 -0
  152. package/dist/tools/cli/update.d.ts.map +1 -1
  153. package/dist/tools/connections/define.d.ts.map +1 -1
  154. package/dist/tools/connections/error-projection.d.ts +4 -0
  155. package/dist/tools/connections/error-projection.d.ts.map +1 -0
  156. package/dist/tools/connections/errors.d.ts +24 -8
  157. package/dist/tools/connections/errors.d.ts.map +1 -1
  158. package/dist/tools/connections/limits.d.ts +11 -31
  159. package/dist/tools/connections/limits.d.ts.map +1 -1
  160. package/dist/tools/connections/mcp-client.d.ts +6 -4
  161. package/dist/tools/connections/mcp-client.d.ts.map +1 -1
  162. package/dist/tools/connections/mcp.d.ts.map +1 -1
  163. package/dist/tools/connections/openapi.d.ts.map +1 -1
  164. package/dist/tools/connections/operation-limits.d.ts +25 -0
  165. package/dist/tools/connections/operation-limits.d.ts.map +1 -0
  166. package/dist/tools/connections/sse-frames.d.ts +11 -2
  167. package/dist/tools/connections/sse-frames.d.ts.map +1 -1
  168. package/dist/tools/connections/sse-session.d.ts +15 -2
  169. package/dist/tools/connections/sse-session.d.ts.map +1 -1
  170. package/dist/tools/connections/types.d.ts +19 -1
  171. package/dist/tools/connections/types.d.ts.map +1 -1
  172. package/dist/tools/execute-result.d.ts.map +1 -1
  173. package/dist/tools/execute.d.ts.map +1 -1
  174. package/dist/tools/internal/observe-hook.d.ts +3 -0
  175. package/dist/tools/internal/observe-hook.d.ts.map +1 -0
  176. package/dist/tools/internal/surface-projector.d.ts +2 -1
  177. package/dist/tools/internal/surface-projector.d.ts.map +1 -1
  178. package/dist/tools/mcp/mount.d.ts +2 -2
  179. package/dist/tools/mcp/mount.d.ts.map +1 -1
  180. package/dist/tools/mcp/prepare.d.ts +4 -5
  181. package/dist/tools/mcp/prepare.d.ts.map +1 -1
  182. package/dist/tools/mcp/register.d.ts +4 -1
  183. package/dist/tools/mcp/register.d.ts.map +1 -1
  184. package/dist/tools/mcp/runtime-tools.d.ts.map +1 -1
  185. package/dist/tools/remote.d.ts.map +1 -1
  186. package/dist/tools/runtime-tool-execution.d.ts +65 -0
  187. package/dist/tools/runtime-tool-execution.d.ts.map +1 -0
  188. package/dist/tools/runtime-tool-mcp.d.ts +27 -0
  189. package/dist/tools/runtime-tool-mcp.d.ts.map +1 -0
  190. package/dist/tools/runtime-tool.d.ts +9 -60
  191. package/dist/tools/runtime-tool.d.ts.map +1 -1
  192. package/dist/tools/surface.d.ts +12 -14
  193. package/dist/tools/surface.d.ts.map +1 -1
  194. package/llms/application.2.txt +221 -44
  195. package/llms/application.3.txt +3 -1
  196. package/llms/application.txt +1 -1
  197. package/llms/cli.2.txt +144 -1
  198. package/llms/cli.txt +123 -104
  199. package/llms/contract.txt +1 -1
  200. package/llms/files-packaging.txt +27 -0
  201. package/llms/process.txt +63 -1
  202. package/llms/server.4.txt +4 -1
  203. package/llms/server.5.txt +1 -0
  204. package/llms/stitchkit.2.txt +14 -0
  205. package/llms/stitchkit.txt +6 -4
  206. package/llms/telegram.txt +64 -2
  207. package/llms/tools-connections.txt +3 -0
  208. package/llms/tools-mcp.txt +29 -0
  209. package/llms/tools.2.txt +59 -0
  210. package/llms/tools.3.txt +1 -0
  211. package/llms/tools.txt +11 -52
  212. package/llms/upgrading-0.37.0-0.44.0.txt +444 -0
  213. package/llms/{upgrading-0.44.0-0.60.0.txt → upgrading-0.44.0-0.61.0.txt} +140 -271
  214. package/llms/{upgrading-0.61.0-0.74.0.txt → upgrading-0.62.0-0.80.0.txt} +217 -140
  215. package/llms/{upgrading-0.75.0-0.103.0.txt → upgrading-0.81.0-0.104.0.txt} +154 -217
  216. package/llms/upgrading.txt +4 -4
  217. package/llms-full.txt +745 -60
  218. package/llms.txt +14 -12
  219. package/native-assets.json +8 -0
  220. package/package.json +12 -3
  221. package/dist/index-wykebpn9.js +0 -221
  222. package/llms/upgrading-0.37.0.txt +0 -176
  223. package/dist/{index-69nvze1q.js → index-pptyv13k.js} +3 -3
package/llms/cli.txt CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
  Parts of this slice:
8
8
  - `llms/cli.txt`: Guide: CLI (docs/guide/cli.md)
9
- - `llms/cli.2.txt`: API reference: `stitchkit/cli` (docs/api/reference.md)
9
+ - `llms/cli.2.txt`: Guide: CLI (docs/guide/cli.md) · API reference: `stitchkit/cli` (docs/api/reference.md)
10
10
 
11
11
  ==============================================================================
12
12
  # Guide: CLI (docs/guide/cli.md)
@@ -26,6 +26,46 @@ It exists for what the other three surfaces cannot do: a generation kicked off
26
26
  with `Bash(run_in_background)` that notifies on exit, a `SKILL.md` that shells
27
27
  out in one line, a pipeable terminal command.
28
28
 
29
+ ## Installation and types
30
+
31
+ Install `stitchkit` and `zod` for a CLI. The public `stitchkit/cli` runtime and
32
+ declarations do not require `ai`, the MCP SDK or Bun globals. Node consumers can
33
+ use strict `NodeNext` resolution with `skipLibCheck: false` and ordinary Node
34
+ types; the package keeps its required dependencies separate from optional SDKs.
35
+
36
+ CLI runtime tools use the shared executable contract: identity, input and output
37
+ schemas, handler, and explicit CLI exposure. SDK presenters belong to the MCP/AI
38
+ definitions in `stitchkit/tools`, which require the corresponding peers. A typed
39
+ definition returned by `defineRuntimeTool` or `createRuntimeToolFactory` can be
40
+ passed directly to both CLI and SDK mounts.
41
+
42
+ Keep transport-specific `present` on that typed SDK definition, rather than on
43
+ an inline object in `CliConfig.runtimeTools`. CLI never executes those presenters;
44
+ its neutral configuration does not validate SDK presentation. Native CLI command
45
+ presentation remains on `defineCliCommand({ present })` and retains its inferred
46
+ output type.
47
+
48
+ For a CLI-only managed tool, use the neutral generic construction contract to
49
+ infer the handler from its schemas:
50
+
51
+ ```ts
52
+ import type { RuntimeToolExecutionWithOutput } from 'stitchkit/cli'
53
+ import { z } from 'zod'
54
+
55
+ const input = z.object({ text: z.string() })
56
+ const output = z.object({ size: z.number() })
57
+ const measure = {
58
+ name: 'measure', description: 'Measure text',
59
+ identity: { serviceName: 'text', action: 'measure', method: 'POST' },
60
+ transports: ['CLI'], input, output,
61
+ handler: ({ input }) => ({ size: input.text.length }),
62
+ } satisfies RuntimeToolExecutionWithOutput<typeof input, typeof output>
63
+ ```
64
+
65
+ Pass `measure` to `runtimeTools`. The heterogeneous registration type preserves
66
+ the definition without inventing a common handler input; execute it through
67
+ `createCli` or `createCliInvoker` so validation remains on the canonical path.
68
+
29
69
  ## Exposure is opt-in
30
70
 
31
71
  Unlike MCP and agent — where an endpoint with no `expose` is a tool by default —
@@ -666,6 +706,88 @@ manifest URL is configuration you wrote, while an asset URL arrives inside a
666
706
  document the endpoint served, and a document is exactly what an SSRF boundary
667
707
  exists to distrust.
668
708
 
709
+ ### Publishing a complete version
710
+
711
+ `publishCli` is an opt-in publisher for a local application-owned distribution
712
+ directory. It commits every target and its version manifest before atomically
713
+ replacing the public `manifest.json`. Your application selects the CLI source
714
+ identity and admits that snapshot at each phase:
715
+
716
+ ```ts
717
+ import { publishCli } from 'stitchkit/cli'
718
+
719
+ const result = await publishCli({
720
+ storageRoot: '/srv/myapp/cli',
721
+ name: 'myapp', version: cliVersion, commit: selectedCliCommit,
722
+ baseUrl: 'https://downloads.example.com/cli/',
723
+ targets: [{ platform: 'linux', arch: 'x64' }],
724
+ signal,
725
+ admit: ({ phase, identity, signal }) => assertSelectedSource({ phase, identity, signal }),
726
+ build: ({ target, stamp, signal }) => compileSelectedSource({ target, stamp, signal }),
727
+ })
728
+ // result.outcome: 'published' | 'existing'; result.manifest is ready to serve.
729
+ ```
730
+
731
+ The builder returns `Uint8Array` or `ReadableStream<Uint8Array>` containing the
732
+ executable. Every target receives the same immutable `{ version, commit,
733
+ builtAt }` stamp; carry it inside the binary. The publisher owns staging,
734
+ gzip assets, paths and manifests. Callbacks receive the cancellation signal and
735
+ run under the publication lock. Admission phases are `prepare`, each target's
736
+ `build`, `commit`, and `promote`; validate the selected source through commit
737
+ and promotion. A backend release can explicitly select an unchanged older CLI
738
+ identity. The helper never substitutes a different commit or rebuilds an
739
+ existing version under a new identity.
740
+
741
+ Assets live at `<version>/<name>-<platform>-<arch>.gz`, with an immutable
742
+ `<version>/manifest.json` and a public `manifest.json`. A verified repeat keeps
743
+ the exact bytes and `builtAt`, requires the same target set and base URL, and
744
+ returns `existing` without invoking the builder. A version committed before a
745
+ failed promotion is recovered and promoted without rebuilding. Conflicting
746
+ same-version commits, downgrades, unsafe components, corrupt manifests and
747
+ assets are refused. Version/name/target components are validated before any
748
+ build; `storageRoot` is absolute and its existing parent must be trusted.
749
+
750
+ All limits are finite positive safe integers; `limits` overrides these defaults:
751
+
752
+ | Field | Default | Enforced boundary |
753
+ | --- | --- | --- |
754
+ | `maxTargets` | 16 | Requested and verified target count |
755
+ | `maxManifestBytes` | 256 KiB | Read and written manifest bytes |
756
+ | `maxAssetBytes` | 256 MiB | Builder and decompressed executable bytes |
757
+ | `maxCompressedBytes` | 256 MiB | Archived asset bytes |
758
+ | `maxDirectoryEntries` | 128 | Version layout, root scan and commit space |
759
+ | `maxStoredVersions` | 16 | Verified publisher-owned history |
760
+ | `lockTimeoutMs` | 10,000 | Wait to acquire the lock |
761
+ | `timeoutMs` | 600,000 | Whole publication deadline |
762
+
763
+ `retention` defaults to 2 and keeps the current and previous published versions.
764
+ It must be at least 2 and less than `maxStoredVersions` (at least 3), leaving a
765
+ recovery slot. Older versions are pruned only after their own identity, exact
766
+ layout, raw size and digest are verified. Runtime folders and foreign semver
767
+ directories remain untouched. A full or unverifiable store refuses further
768
+ publication; repeated completed candidates stay inside the owned history cap.
769
+ Cancelled or failed incomplete staging is removed only while its root and
770
+ directory generations still match. The shared asynchronous gzip decoder also
771
+ bounds updater expansion by the declared raw size and configured byte cap.
772
+
773
+ The publisher rejects symlink roots/versions/assets, non-regular files and
774
+ multiply linked files, and checks held-lock, root and prior-pointer generations
775
+ before commit and promotion. This is cooperative publication under a trusted
776
+ parent directory. Portable path checks do not provide an atomic comparison with
777
+ a hostile process replacing ancestors between syscalls. Durability uses file
778
+ and directory sync on POSIX; Windows publication is refused. A deadline stops
779
+ callback/stream waits and blocks late promotion; trusted callbacks remain
780
+ responsible for cancelling their own subprocesses and external effects.
781
+
782
+ A failure after an immutable commit reports that the publication outcome must
783
+ be checked. A failure after public-pointer rename may already have published;
784
+ `AtomicFilePublicationError.published` remains available when the atomic writer
785
+ reports that boundary. Read the durable manifest and retry the admitted same
786
+ identity to recover; an error does not promise rollback. Optional `signing` and
787
+ `trust` reuse the Ed25519 contract below. The generated shell installer checks
788
+ raw digest and size; the updater checks manifest authorship when given a trust
789
+ root.
790
+
669
791
  ### Proving who built it — signing the manifest
670
792
 
671
793
  The asset digest proves the bytes that arrived are the bytes the manifest named.
@@ -889,106 +1011,3 @@ asked.
889
1011
  What stays yours: what makes a repeat safe (an idempotency key derived from the
890
1012
  line id), which operations a batch may contain, and how much of it runs at once.
891
1013
  That is product knowledge, and the framework has no business guessing it.
892
-
893
- ## Commands discovered from a running server
894
-
895
- A CLI compiled from contracts carries the surface of the build it was compiled
896
- from. One built from discovery carries the surface the server has *right now* —
897
- which matters, because a long-lived MCP client freezes schemas at connect time
898
- and then refuses the server's own newer fields:
899
-
900
- ```ts
901
- const discovered = await mountConnections([
902
- defineMcpClientConnection({
903
- name: 'api',
904
- transport: { url },
905
- token: () => key,
906
- transports: ['CLI'], // the opt-in: this server's tools are commands
907
- }),
908
- ])
909
- await createCli({ name: 'myapp', version, runtimeTools: discovered, commands: [...] })
910
- ```
911
-
912
- `transports` is where the opt-in belongs: a whole server becomes a set of
913
- commands, and a connection without it still contributes nothing to the CLI —
914
- exposure stays explicit, as it is everywhere else in the framework.
915
-
916
- One unconvertible schema no longer takes the connection down with it. The tool
917
- is skipped and **named** (`onSkippedTool`, or a stderr line by default), so a
918
- surface of two hundred tools is not lost to one.
919
-
920
- **A remote refusal keeps its code.** A failed `tools/call` used to become a
921
- one-sentence `Error` with the result discarded, which cost three things at once:
922
- the code (so `exitCodes` had nothing to map and every remote failure exited `1`),
923
- the message the operator needed, and the error's own class — a plain `Error` is
924
- an *unexpected* error to the runner, so it printed a code frame of the framework
925
- bundle before the JSON failure and was then scrubbed to `INTERNAL_SERVER_ERROR`.
926
- A structured `{ error, details }` body is now relayed as the contract error it
927
- is, on every transport; anything else fails as `UPSTREAM_TOOL_ERROR` carrying
928
- what the server did send. Not `INTERNAL_SERVER_ERROR`, because nothing of ours
929
- broke.
930
-
931
- **On the CLI a discovered command prints the answer, not the envelope.**
932
- `tools/call` returns `{ content: [...], structuredContent? }`, and an agent mount
933
- needs exactly that — the parts are what a model is shown. The CLI is different in
934
- kind, because the handler's value is what gets printed, piped and aggregated:
935
- handed the envelope, `--count-by status` groups the *content parts* and answers
936
- `no record carries the field "status" — available: text, type`. So the CLI
937
- transport unwraps, and only it: `structuredContent` when the server sent one, a
938
- lone text part when it parses as JSON, its text when it does not, and `null` when
939
- there are no parts at all — a tool without an output contract answers that way,
940
- and the `_meta` beside an empty `content` is protocol metadata, never the answer.
941
- Several parts, an image or audio pass through whole — picking one of many would
942
- be inventing an answer.
943
-
944
-
945
- ## Auth parity
946
-
947
- A scoped command is guarded by the same `createAuthHook` your HTTP server uses —
948
- pass it as `lifecycle`, and inject the identity through `context` so
949
- `resolveFromContext` can read it:
950
-
951
- ```ts
952
- const authHook = createAuthHook({ /* resolve, resolveFromContext, rules */ })
953
-
954
- await createCli({
955
- name: 'myapp',
956
- version: '1.0.0',
957
- resolveAuth: () => resolveIdentityFromToken(process.env.MYAPP_TOKEN),
958
- context: (identity) => ({ user: identity }), // resolveFromContext reads this
959
- lifecycle: { beforeHandle: authHook }, // same policy; HTTP wires it as authorize
960
- services,
961
- })
962
- ```
963
-
964
- Without `lifecycle`, a scoped command runs **unguarded** — the scope check lives
965
- entirely inside the `createAuthHook` result, so with no hook wired in there is
966
- nothing to enforce a method's `scope`. This matches the MCP / agent surfaces
967
- exactly ([ADR 0014](../decisions/0014-tool-http-parity.md)): on every tool
968
- transport the auth gate is opt-in, so a contract with scoped methods **must** be
969
- given a `lifecycle` (and `context` identity) to be protected. The gate only
970
- *fails closed* once the hook **is** present but `resolveFromContext` is missing —
971
- then a scoped call has no identity and is rejected.
972
-
973
- ## Typed context
974
-
975
- Use `createToolkit<AppContext>()` to type the injected `context` against your
976
- app's context shape — the tool-side mirror of `createImplement`
977
- ([ADR 0017](../decisions/0017-typed-tool-context.md)):
978
-
979
- ```ts
980
- const tools = createToolkit<{ user: User }>()
981
- await tools.createCli({
982
- name: 'myapp',
983
- version: '1.0.0',
984
- services,
985
- context: (identity) => ({ user: identity }), // checked against { user: User }
986
- })
987
- ```
988
-
989
- ## Remaining boundary
990
-
991
- File-upload (`multipart`) contract endpoints remain CLI-invisible, the same as
992
- on MCP/Agent: their wire body is not a JSON tool form. Model file-oriented
993
- application behavior as a managed pathless command, or binary-only behavior as
994
- a native command. Streaming (SSE) output is not yet piped to stdout.
package/llms/contract.txt CHANGED
@@ -570,7 +570,7 @@ from the root `stitchkit`.
570
570
  | Export | Kind | Summary |
571
571
  |--------|------|---------|
572
572
  | `AppError` | class | the framework error — `code` / `status` / `details` / `hint` |
573
- | `ErrorEnvelope` | _type_ | the JSON shape of an error response |
573
+ | `ErrorEnvelope` | _type_ | the JSON shape of an error response, preserving optional declared boolean `error.retryable` without changing HTTP retry policy |
574
574
  | `notFound` | function | throw `404 NOT_FOUND` — [guide](../guide/auth-and-errors.md#throwing-errors) |
575
575
  | `badRequest` | function | throw `400 BAD_REQUEST` |
576
576
  | `unauthorized` | function | throw `401 UNAUTHORIZED` |
@@ -0,0 +1,27 @@
1
+ # stitchkit/files/packaging — 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: API reference: `stitchkit/files/packaging` (docs/api/reference.md)
7
+
8
+ Also read:
9
+ - see `llms/process.txt` for the Native IO guide
10
+
11
+ ==============================================================================
12
+ # API reference: `stitchkit/files/packaging` (docs/api/reference.md)
13
+ ==============================================================================
14
+
15
+ ## `stitchkit/files/packaging`
16
+
17
+ `stitchkit/files/packaging` owns `createNativePackaging` and the types
18
+ `NativePackagingOptions`, `NativePackagingAsset`, `NativePackagingPlugin`, `NativePackagingResult`.
19
+ It resolves the installed package's target/version and original addon SHA256.
20
+ `platform`, `architecture`, `delivery`, `entryPath` and `assetPath` are explicit public inputs.
21
+ The result is `ready` with assets and a structural Bun plugin, or `unsupported`/`missing` with a safe code.
22
+ Malformed options/metadata and other filesystem errors fail rather than masquerade as missing.
23
+ Companion mode places an external addon relative to the emitted entry; embedded mode lets
24
+ Bun compile retain the matching addon. Keep JS builds without splitting, preserve every output,
25
+ and copy each original asset to its `outputPath` after verifying `sha256`.
26
+ This evolving entrypoint is a build dependency; runtime imports do not reach it.
27
+ The detailed recipe is in [native IO](../guide/native-io.md#public-native-packaging).
package/llms/process.txt CHANGED
@@ -15,7 +15,7 @@ description: Native IO для Bun и Node, с явно выбранной durabi
15
15
  type: guide
16
16
  status: active
17
17
  created: 2026-10-01 20:44 +07:00
18
- updated: 2026-10-04 09:40 +07:00
18
+ updated: 2026-10-04 20:04 +07:00
19
19
  participants:
20
20
  - role: authored
21
21
  harness: Codex
@@ -293,6 +293,68 @@ no recognized native error code, the stage stays `load`; the loader never infers
293
293
  This delivery removes a runtime kernel installation; it does not remove the build-time
294
294
  Stitchkit installation or create an independent lightweight npm package.
295
295
 
296
+ ## Public native packaging
297
+
298
+ Custom archives and installers use `stitchkit/files/packaging` at **build time**.
299
+ `createNativePackaging` resolves the installed version and target addon with its original SHA256;
300
+ its Bun-compatible plugin integrates that asset with the same lazy native loader.
301
+ The leaf is evolving. It requires no optional peer or Bun ambient declarations for Node imports.
302
+ It must run unbundled from its installed package, never from an application's runtime artifact.
303
+ This contract is available from 0.104.1; versions 0.103.13–0.104.0 use the complete-output recipe
304
+ in the previous section.
305
+
306
+ ```ts
307
+ import { createNativePackaging } from 'stitchkit/files/packaging'
308
+ import { createHash } from 'node:crypto'
309
+ import { copyFileSync, mkdirSync, readFileSync } from 'node:fs'
310
+ import { dirname, join } from 'node:path'
311
+
312
+ const native = createNativePackaging({
313
+ platform: 'darwin', architecture: 'arm64', delivery: 'companion',
314
+ entryPath: 'app/native.js', assetPath: 'addons/owner.node',
315
+ })
316
+ if (native.state !== 'ready') throw new Error(native.code)
317
+ const result = await Bun.build({
318
+ entrypoints: ['src/native.ts'], target: 'node', format: 'esm',
319
+ outdir: 'dist', naming: { entry: 'app/native.js' }, splitting: false,
320
+ plugins: [native.plugin],
321
+ })
322
+ if (!result.success) throw new AggregateError(result.logs, 'Build failed')
323
+ // Preserve every result.outputs file under dist, then add the selected companion.
324
+ for (const asset of native.assets) {
325
+ const bytes = readFileSync(asset.sourcePath)
326
+ if (createHash('sha256').update(bytes).digest('hex') !== asset.sha256)
327
+ throw new Error('Native asset changed during build')
328
+ const destination = join('dist', asset.outputPath)
329
+ mkdirSync(dirname(destination), { recursive: true })
330
+ copyFileSync(asset.sourcePath, destination)
331
+ }
332
+ ```
333
+
334
+ `entryPath` and `assetPath` are relative paths inside the application's output root, without
335
+ absolute paths, traversal or overlapping file/directory paths. `entryPath` is fixed, without
336
+ Bun naming templates such as `[dir]` or `[name]`. Companion JS builds require one entry, no splitting,
337
+ and `naming.entry` exactly matching `entryPath`; the plugin rejects a mismatching layout.
338
+ Choose any application layout; the framework does not prescribe an app directory or installer.
339
+ Keep every bundler output, then verify copied addon bytes against the original SHA256.
340
+ Archive the complete output directory with an integrity manifest, unpack to a clean directory,
341
+ verify the same hashes there, and run offline without the build tree or `node_modules`.
342
+ A digest provides integrity, not authenticity: signature and trust policy remain application-owned.
343
+
344
+ For a standalone executable use `delivery: 'embedded'` and the same plugin in `Bun.build`
345
+ with `compile: { outfile: ... }`. Bun embeds the selected addon; `assets` identifies its
346
+ original bytes for qualification, not a companion that must be installed beside the executable.
347
+ Cross-builds must select the requested architecture explicitly; the artifact must run on that target.
348
+ The installed package must contain that target's addon. No automatic fallback/downgrade occurs.
349
+
350
+ The API returns `unsupported` / `NATIVE_TARGET_UNSUPPORTED` for non-Darwin or unsupported
351
+ architectures, and `missing` / `NATIVE_ASSET_MISSING` for a missing addon. Portable Linux libraries
352
+ use their normal build, without a Darwin plugin; merely importing the build leaf loads no addon.
353
+ Malformed inputs, unsupported metadata versions and other IO errors throw before packaging.
354
+ Runtime missing/corrupt companions preserve the native `unavailable` result and safe stage/code
355
+ from the existing observer; they never certify a dead owner. Preserve the raw cause internally.
356
+ The plugin does not parse consumer JS or establish a second runtime native implementation.
357
+
296
358
  ## Qualification boundaries
297
359
 
298
360
  Native command cleanup owns the child before launch subscriptions are installed. Setup
package/llms/server.4.txt CHANGED
@@ -547,12 +547,15 @@ error round-trips without adding correlation data to the response body.
547
547
  A `code` is a free string — your app codes (`BOT_NOT_FOUND`, …) are yours and the
548
548
  core never models them (ADR 0002). But stitchkit itself emits a set of its own:
549
549
  `BAD_REQUEST`, `UNAUTHORIZED`, `FORBIDDEN`, `NOT_FOUND`, `METHOD_NOT_ALLOWED`,
550
- `CONFLICT`, `RATE_LIMITED`, `VALIDATION_ERROR`, `FILE_INVALID_PATH`,
550
+ `CONFLICT`, `RATE_LIMITED`, `REQUEST_ABORTED`, `REQUEST_TIMEOUT`,
551
+ `VALIDATION_ERROR`, `FILE_INVALID_PATH`,
551
552
  `FILE_OUTSIDE_ROOT`, `FILE_NOT_FOUND`, `FILE_NOT_REGULAR`,
552
553
  `FILE_INSPECTION_REJECTED`, `FILE_TOO_LARGE`, `FILE_EXISTS`,
553
554
  `REALTIME_CONTRACT_VIOLATION`, `APPLICATION_NOT_ACCEPTING`, `NOT_IMPLEMENTED`,
554
555
  `WAIT_TIMEOUT`,
555
556
  `WAIT_FAILED`, `DOWNLOAD_NOT_FOUND`, `VIEW_HTTP_ERROR`,
557
+ `CONNECTION_TIMEOUT`, `CONNECTION_RESPONSE_TOO_LARGE`,
558
+ `CONNECTION_REQUEST_FAILED`, `UPSTREAM_TOOL_ERROR`,
556
559
  `OPERATION_NOT_SUCCEEDED`, `STREAM_ITEM_INVALID`, `STREAM_FRAME_TOO_LARGE`,
557
560
  `STREAM_TERMINAL_MISSING`, `STREAM_LIFETIME_EXCEEDED`,
558
561
  `GRAMMY_WEBHOOK_NOT_ACCEPTING`, `SANDBOX_UNAVAILABLE`, `SANDBOX_INSUFFICIENT`,
package/llms/server.5.txt CHANGED
@@ -57,6 +57,7 @@ Also re-exports the error helpers from `stitchkit/contract`.
57
57
  | `respondJson` | function | a raw route's JSON response (`204` for null/undefined) |
58
58
  | `errorResponse` | function | any thrown value → the framework error envelope + `x-request-id` |
59
59
  | `normalizeError` | function | any thrown value → an `AppError` (`ZodError` → `VALIDATION_ERROR` 400 with structured `details.issues`, else generic 500) — the framework's canonical classification, for a bespoke `onError` |
60
+ | `NormalizeErrorOptions` | _type_ | `logUnexpected` defaults to logging the raw server-side cause; tool adapters disable process dumps while their existing observers retain that cause |
60
61
  | `errorCode` | function | the stable error code for a thrown value (side-effect-free — for log attribution) |
61
62
  | `formatZodError` | function | a `ZodError` → a readable, field-summarised string |
62
63
  | `zodIssues` | function | a `ZodError` → structured `{ path, code, message }[]` — the machine-readable sibling of `formatZodError` |
@@ -710,6 +710,13 @@ URLs.
710
710
 
711
711
  ## `ApiError`
712
712
 
713
+ On native Bun, both client paths disable Bun's separate socket idle timer while preserving
714
+ the framework deadline and caller signal. A long declared deadline therefore remains the
715
+ request policy. Explicit or patched fetch implementations, including Next, keep their own
716
+ transport behavior; Node and browsers receive no Bun-specific fetch options. Raw responses
717
+ and contract streams retain their existing header timeout and caller-owned body lifetime.
718
+ See [Bun's idle timeout contract](https://bun.sh/reference/globals/BunFetchRequestInit/timeout).
719
+
713
720
  A non-2xx response is thrown as an `ApiError`:
714
721
 
715
722
  ```ts
@@ -724,6 +731,7 @@ try {
724
731
  err.message // 'Note not found'
725
732
  err.details // structured details, if any
726
733
  err.hint // optional hint
734
+ err.retryable // optional explicit recommendation, true or false
727
735
  err.traceId // x-request-id — correlate this failure with backend logs
728
736
  err.cause // concrete injected transport failure, when delivery failed
729
737
  }
@@ -737,6 +745,12 @@ browser code can read it through Stitchkit's default CORS expose list; a custom
737
745
 
738
746
  The error model is shared with the server — see [Auth & errors](./auth-and-errors.md).
739
747
 
748
+ `retryable` preserves an explicit boolean in the server error envelope; an absent declaration
749
+ remains absent. This metadata does not change the HTTP transport retry policy. The existing
750
+ seventh constructor argument remains `ErrorOptions`, including `cause`; the optional
751
+ retryability argument is eighth. Closed error/frame schemas must accept the new optional
752
+ field before their producer declares it.
753
+
740
754
  ## Auth events
741
755
 
742
756
  The HTTP client emits events your app can react to globally:
@@ -63,12 +63,14 @@ own, recorded as an ADR.
63
63
  | `stitchkit/server` | server (Bun) | stable | `createServer`, `implement`, hooks, auth, Socket.IO server, server primitives |
64
64
  | `stitchkit/node` | server (Node ≥ 22) | stable | `serveNode` + the runtime-agnostic core — the Node mirror of `/server` |
65
65
  | `stitchkit/tools` | server | stable | `createMcpHandler`, `mountMcp`, `mountAgent`, the OAuth provider, native tools |
66
+ | `stitchkit/tools/mcp` | server (Bun or Node) | evolving | the canonical MCP HTTP/stdio server, schemas and typed MCP presenters; only Zod and the MCP server SDK peers |
66
67
  | `stitchkit/tools/contract` | browser + server | evolving | the shapes a tool surface speaks — async-operation contract, snapshot and cancel schemas, view-file input/output — without the runtime that serves them |
67
68
  | `stitchkit/tools/invoker` | server | stable | peer-free `createToolInvoker` over the canonical contract tool runner |
68
69
  | `stitchkit/tools/connections` | server (Bun or Node) | evolving | consume external MCP servers and a bounded OpenAPI subset — typed reauthorization, a per-request host fence and per-call credentials |
69
- | `stitchkit/cli` | server | stable | `createCli` — the CLI transport, light (no MCP SDK / `ai`) |
70
+ | `stitchkit/cli` | server | stable | `createCli` / `createCliInvoker` — runtime and strict NodeNext declarations without MCP SDK / `ai` |
70
71
  | `stitchkit/remote` | browser **and** server | stable | peer-free `implementRemote` for thin HTTP proxy processes |
71
72
  | `stitchkit/process` | server (Bun or Node, POSIX) | evolving | finite native commands, byte sinks, bounded capture and process-group cancellation |
73
+ | `stitchkit/files/packaging` | build time (Bun or Node) | evolving | installed native asset graph, integrity and Bun packaging plugin; no runtime install policy |
72
74
  | `stitchkit/files` | server (Bun or Node) | stable | peer-free managed local-file boundary |
73
75
  | `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 |
74
76
  | `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 |
@@ -80,12 +82,12 @@ own, recorded as an ADR.
80
82
  | `stitchkit/geo` | server (Bun or Node) | evolving | managed GeoIP reader generations, last-known-good reload and the optional MaxMind adapter |
81
83
  | `stitchkit/oauth` | browser **and** server | evolving | provider-neutral Authorization Code + PKCE request and one-shot callback transaction mechanics |
82
84
  | `stitchkit/google` | server (Bun or Node) | evolving | optional Google code exchange and verified OIDC identity adapter |
83
- | `stitchkit/observability` | server | stable<br>_redefined in 2 of the 48 minors since 0.56.2, most recently 0.92.0_ | request/tool event projections — `createObservability`, trace context, sanitisation |
85
+ | `stitchkit/observability` | server | stable<br>_redefined in 2 of the 49 minors since 0.56.2, most recently 0.92.0_ | request/tool event projections — `createObservability`, trace context, sanitisation |
84
86
  | `stitchkit/testing` | tests on Bun or Node | stable | in-process generated clients over a real Fetch handler, plus the managed-resource conformance kit |
85
87
  | `stitchkit/declaration` | browser + build and deployment tooling (Bun or Node) | evolving | `ProjectDeclarationSchema` — the one machine-readable statement a repository makes about itself |
86
88
  | `stitchkit/react` | browser + server rendering | stable | `createCursorQuery`, `createCacheBridge`, QueryClient and `ApiError` retry policy |
87
89
  | `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) |
88
- | `stitchkit/agent-runtime` | server | evolving<br>_redefined in 22 of the 48 minors since 0.56.2, most recently 0.97.0_ | optional durable conversation/run loop, history, models, prompts, fencing and events |
90
+ | `stitchkit/agent-runtime` | server | evolving<br>_redefined in 22 of the 49 minors since 0.56.2, most recently 0.97.0_ | optional durable conversation/run loop, history, models, prompts, fencing and events |
89
91
  | `stitchkit/agent-runtime/sandbox` | server | evolving | optional Linux Bubblewrap sessions, durable workspaces and a host HTTP credential gateway; see [sandbox guide](sandbox.md) |
90
92
  | `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 |
91
93
  | `stitchkit/agent-runtime/harness` | server | evolving | resource-aware process-local facade over the canonical Agent runtime; supervision stays outside |
@@ -98,7 +100,7 @@ own, recorded as an ADR.
98
100
  | `stitchkit/agent-runtime/sqlite/bun` | server (Bun) | evolving | durable built-in SQLite store for the agent runtime |
99
101
  | `stitchkit/agent-runtime/sqlite/node` | server (Node ≥ 22.5) | evolving | durable built-in SQLite store for the agent runtime |
100
102
  | `stitchkit-tui` | terminal (Bun) | evolving | optional official OpenTUI host over a caller-composed headless runtime |
101
- | `stitchkit/application` | browser + server | evolving<br>_redefined in 11 of the 48 minors since 0.56.2, most recently 0.103.0_ | managed resource graph, readiness, admission, schedules, subtree restart and bounded shutdown |
103
+ | `stitchkit/application` | browser + server | evolving<br>_redefined in 12 of the 49 minors since 0.56.2, most recently 0.104.0_ | managed resource graph, readiness, admission, schedules, subtree restart and bounded shutdown |
102
104
  | `stitchkit/application/grammy` | server | evolving | a grammY bot as application resources — configuration, then polling with updates admitted by batch — and the webhook adapter |
103
105
  | `stitchkit/application/opentelemetry` | server | evolving | maps application snapshots onto an injected OpenTelemetry `Meter` |
104
106
  | `stitchkit/application/schemas` | browser + server | evolving | the application's snapshot, health and shutdown schemas alone, without the kernel |
package/llms/telegram.txt CHANGED
@@ -184,7 +184,8 @@ const report = await runTelegramBroadcast({
184
184
  `retry_after` and sends the same recipient again; a server error is retried
185
185
  with backoff up to `maxAttempts`, then `failed`. A message Telegram cannot
186
186
  parse is not the recipient's fault: the run **halts** with them still pending,
187
- so the run after the fix reaches them. So does Telegram being unreachable.
187
+ so the run after the fix reaches them. An unknown transport outcome becomes
188
+ `uncertain` immediately and halts the run; resume never sends to that recipient again.
188
189
  - **Pacing** is `ratePerSecond`, 25 by default. `dryRun` counts and writes
189
190
  nothing. One runner per name: a second concurrent run is refused.
190
191
 
@@ -193,6 +194,66 @@ application (the application's signal, with admission held by the caller).
193
194
  The recipient state contains Telegram ids: keep it in the state directory, out
194
195
  of git.
195
196
 
197
+ ### Injecting another transport's failure policy
198
+
199
+ `classify(error)` returns the public `TelegramBroadcastFailure` union. It lets
200
+ an application use its own client without adding that SDK to Stitchkit:
201
+
202
+ ```ts
203
+ const report = await runTelegramBroadcast({
204
+ name: 'announcement',
205
+ directory: env.STATE_DIRECTORY,
206
+ recipients: () => audience.activeSubscribers(),
207
+ send: ({ recipient }) => client.sendMessage(recipient, message),
208
+ classify(error) {
209
+ if (isFloodWait(error))
210
+ return { kind: 'retry-after', retryAfterMs: error.seconds * 1000,
211
+ reason: 'provider-rate-limit' }
212
+ if (isRecipientGone(error))
213
+ return { kind: 'permanent', recipientUnreachable: true,
214
+ reason: 'recipient-gone' }
215
+ return { kind: 'ambiguous', reason: 'unknown-provider-outcome' }
216
+ },
217
+ maxRetryDelayMs: 60_000,
218
+ signal: stop.signal,
219
+ })
220
+ ```
221
+
222
+ The four variants are `retry-after` (exact `retryAfterMs`), `transient` (bounded
223
+ backoff), `permanent` (optionally `recipientUnreachable` or `stopBroadcast`), and
224
+ `ambiguous`. Retry variants certify that the failed attempt had no external
225
+ effect. Lack of an HTTP status does not establish that fact: a server may have
226
+ accepted the message before the connection failed. `ambiguous` durably records
227
+ `uncertain`, then halts; resuming the name skips that recipient.
228
+
229
+ `maxRetryDelayMs` defaults to 60 seconds. A provider wait above it halts with the
230
+ recipient pending; it is never shortened to send early. `maxAttempts` is an
231
+ integer from 1 to 100, `maxRetryDelayMs` is an integer from 0 to 2,147,483,647,
232
+ and pacing/progress intervals must fit native timers. The optional
233
+ `sleep(milliseconds, signal)` receives cancellation; the runner also stops waiting
234
+ when the signal aborts if a custom sleeper ignores it. A send already in flight
235
+ finishes and is recorded before stopping.
236
+
237
+ Without `classify`, known Bot API 429, unreachable-recipient, malformed-message
238
+ and server-error behavior remains. Unknown/no-status failures now halt as
239
+ uncertain, rather than being automatically retried. Existing custom `send`
240
+ implementations that relied on those retries must supply an explicit classifier
241
+ for proven pre-effect failures. `report.halt` now carries
242
+ `TelegramBroadcastFailure`; inspect `halt.kind` instead of the old send-failure
243
+ `scope`/`retryable` fields.
244
+
245
+ The runner uses the shared process-instance lock and checks its generation before
246
+ sends and journal writes. The audience and initial journal directory entry are
247
+ synced before sending. Lost ownership cannot write a stale delivered receipt or
248
+ start another send; the persisted intent becomes uncertain on recovery. Old
249
+ pid-only broadcast locks have unknown ownership and require recovery after their
250
+ writers are verified stopped, rather than automatic age-based deletion.
251
+
252
+ The installed
253
+ [`telegram-broadcast-classifier.mjs`](../../packages/core/scripts/consumer-lane/fixtures/minimal/src/telegram-broadcast-classifier.mjs)
254
+ fixture checks injected rate limits, ambiguity/no resend, wait budgets and abort
255
+ on Bun and Node without a client SDK.
256
+
196
257
  ## Message markup
197
258
 
198
259
  `stitchkit/telegram/html` is Telegram's HTML parse mode as a tree, with no peer
@@ -393,8 +454,9 @@ injected grammY bot. → ADR 0143, ADR 0201 — [guide](../guide/telegram.md)
393
454
  | `classifyTelegramSendFailure` | function | name a refused Bot API send and separate "retry this send" from "stop addressing this recipient" |
394
455
  | `TelegramSendFailure` | _type_ | reason, `status`, Telegram-stated `retryAfterSeconds`, `retryable`, `recipientUnreachable` and which evidence produced the answer |
395
456
  | `TelegramSendFailureReason` / `TELEGRAM_SEND_FAILURE_REASONS` | _type_ / constant | `blocked-by-user` / `user-deactivated` / `chat-not-found` / `not-started` / `rate-limited` / `message-invalid` / `server-error` / `unknown`, and the same as a list; a `Record<TelegramSendFailureReason, …>` over them fails to compile when a reason is added |
396
- | `runTelegramBroadcast` | function | a resumable broadcast by `name` under a state `directory`: audience written once, an append-only journal, pacing at `ratePerSecond` (25), 429 waits `retry_after`, unreachable recipients never addressed again, a `message-invalid` or unreachable Telegram halts without charging the recipient, a send in flight at a crash is `uncertain` and not repeated, `dryRun`, one runner per name |
457
+ | `runTelegramBroadcast` | function | a resumable broadcast by `name` under a state `directory`: audience and journal durably recorded, pacing at `ratePerSecond` (25), exact retry delays within the finite declared budget, injected classifier, uncertain send outcomes never replayed, `dryRun`, one fenced runner per name |
397
458
  | `TelegramBroadcastConfig` / `TelegramBroadcastSend` / `TelegramBroadcastReport` / `TelegramBroadcastRunOutcome` / `TelegramBroadcastOutcome` / `TelegramBroadcastRecipient` | _type_ | `name`, `directory`, `recipients()`, `send({ recipient, attempt })`, `maxAttempts`, `signal`, `onProgress`; counts `delivered` / `unreachable` / `failed` / `uncertain` / `pending` and `finished` / `stopped` / `halted` / `dry-run` |
459
+ | `TelegramBroadcastFailureSchema` / `TelegramBroadcastFailure` | schema / _type_ | injected `classify` answers retry-after/transient/permanent/ambiguous; exact provider delay is bounded by `maxRetryDelayMs` without shortening, unknown outcome remains uncertain and is never replayed |
398
460
  | `telegramBroadcastSender` / `TelegramBroadcastSenderConfig` / `TelegramBroadcastMessage` | function / _type_ | the standard `send`: `sendMessage` with a text or `copyMessage` of a prepared message, through the Bot API |
399
461
  | `createTelegramOperatorChannel` | function | the operator chat, apart from the journal: `post(text, topic?)` returns at once and never throws; paced sends into forum topics, 429 honoured, oldest dropped on overflow, `drain(signal)` and `close()` for the way down |
400
462
  | `TelegramOperatorChannelConfig` / `TelegramOperatorChannel` / `TelegramOperatorMessage` / `TelegramOperatorDrop` / `TelegramChatId` | _type_ | `chatId`, `topics`, `send`, `maxQueued`, `minIntervalMs`, `maxAttempts`, `onDropped`, `sensitivePatterns` and `sensitiveValues` (a bot token is always masked), `dedupe`; a drop is `overflow` / `refused` / `attempts` / `closed` / `repeated` / `over-budget` with its classification |
@@ -23,6 +23,9 @@ and approval path.
23
23
  | Export | Kind | Summary |
24
24
  |--------|------|---------|
25
25
  | `defineMcpClientConnection` | function | declare one external MCP server connection over Streamable HTTP with an optional SSE fallback |
26
+ | `ConnectionOperationLimits` / `McpConnectionLimits` | _type_ | positive finite deadline/raw byte ceilings with independent discovery/call overrides; omitted fields inherit the shared bounds |
27
+ | `ConnectionPhase` / `ConnectionOperation` / `ConnectionFailureContext` | _type_ | closed operation/phase vocabulary and measured read context for existing typed connection errors |
28
+ | `ConnectionTimeoutError` / `ConnectionResponseTooLargeError` | class | bounded operation refusal, with phase, operation and finite observed bytes; safe tool projection retains the raw cause locally |
26
29
  | `defineOpenApiConnection` | function | declare one OpenAPI document (object, JSON text or JSON URL) as a tool surface |
27
30
  | `mountConnections` | function | discover every connection and return readonly `RuntimeToolDefinition`s for `mountAgent` |
28
31
  | `ConnectionAuthorizationRequiredError` | class | typed reauthorization signal raised on `401` with credentials resolved again on the next call |
@@ -0,0 +1,29 @@
1
+ # stitchkit/tools/mcp — 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: API reference: `stitchkit/tools/mcp` (docs/api/reference.md)
7
+
8
+ Also read:
9
+ - see `llms/tools.txt` for the MCP & agents guide
10
+
11
+ ==============================================================================
12
+ # API reference: `stitchkit/tools/mcp` (docs/api/reference.md)
13
+ ==============================================================================
14
+
15
+ ## `stitchkit/tools/mcp`
16
+
17
+ A server-only leaf over the same MCP owner as `stitchkit/tools`. Its runtime and strict
18
+ NodeNext declarations require Zod and `@modelcontextprotocol/server`, with no AI SDK peer.
19
+ HTTP/stdio factories, mounts, schema policy, catalog and resource helpers keep their canonical
20
+ behavior and names. See [MCP guide](../guide/mcp-and-agents.md#mcp--createmcphandler).
21
+
22
+ | Export | Kind | Purpose |
23
+ |--------|------|---------|
24
+ | `createMcpHandler` / `createMcpHttpRoute` / `buildMcpServer` / `mountMcp` | function | the shared authenticated HTTP handler, raw-route adapter, SDK server and contract mount |
25
+ | `createStdioMcpServer` / `bindStdioProcessSignals` | function | the shared stdio server and bounded process cleanup |
26
+ | `RuntimeMcpToolDefinitionWithOutput` | _type_ | schema-aware MCP-only construction with concrete handler output and presenter input |
27
+ | `RuntimeMcpToolDefinition` | _type_ | heterogeneous MCP registration; the canonical runner validates input/output before invocation |
28
+ | `RuntimeMcpToolPresenters` / `RuntimeMcpPresentation` | _type_ | official SDK content without framework-owned `structuredContent` or `isError`; one presenter owner shared with the full SDK surface |
29
+ | `RuntimeToolDefinitionWithoutOutput` | _type_ | the shared neutral void definition |