stitchkit 0.106.1 → 0.108.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 (147) hide show
  1. package/CHANGELOG.md +151 -0
  2. package/README.md +2 -0
  3. package/darwin-native.cjs +17 -4
  4. package/dist/agent-runtime/deferred-tool-types.d.ts +6 -27
  5. package/dist/agent-runtime/deferred-tool-types.d.ts.map +1 -1
  6. package/dist/browser/stream.d.ts +38 -12
  7. package/dist/browser/stream.d.ts.map +1 -1
  8. package/dist/entrypoints/agent-runtime/browser.js +2 -3
  9. package/dist/entrypoints/agent-runtime/coding-tools.js +6 -8
  10. package/dist/entrypoints/agent-runtime/harness.js +7 -11
  11. package/dist/entrypoints/agent-runtime/realtime.js +2 -3
  12. package/dist/entrypoints/agent-runtime/sandbox.js +6 -8
  13. package/dist/entrypoints/agent-runtime.d.ts +2 -1
  14. package/dist/entrypoints/agent-runtime.d.ts.map +1 -1
  15. package/dist/entrypoints/agent-runtime.js +20 -23
  16. package/dist/entrypoints/application/diagnostic-journal.js +3 -3
  17. package/dist/entrypoints/application/directory-inbox.js +8 -9
  18. package/dist/entrypoints/application/grammy.js +3 -3
  19. package/dist/entrypoints/application.js +7 -8
  20. package/dist/entrypoints/bin/upgrade-cli.d.ts +32 -0
  21. package/dist/entrypoints/bin/upgrade-cli.d.ts.map +1 -1
  22. package/dist/entrypoints/bin/upgrade-cli.js +451 -8
  23. package/dist/entrypoints/cli/publish.js +10 -11
  24. package/dist/entrypoints/cli.js +12 -16
  25. package/dist/entrypoints/contract.js +1 -1
  26. package/dist/entrypoints/files/packaging.d.ts +3 -1
  27. package/dist/entrypoints/files/packaging.d.ts.map +1 -1
  28. package/dist/entrypoints/files/packaging.js +171 -55
  29. package/dist/entrypoints/files.js +16 -16
  30. package/dist/entrypoints/index.d.ts +1 -1
  31. package/dist/entrypoints/index.d.ts.map +1 -1
  32. package/dist/entrypoints/index.js +47 -41
  33. package/dist/entrypoints/node.js +27 -29
  34. package/dist/entrypoints/observability.js +10 -12
  35. package/dist/entrypoints/process.js +2 -2
  36. package/dist/entrypoints/react.js +3 -4
  37. package/dist/entrypoints/remote.js +8 -9
  38. package/dist/entrypoints/server.js +48 -51
  39. package/dist/entrypoints/telegram/html.js +3 -3
  40. package/dist/entrypoints/telegram/screens.js +6 -6
  41. package/dist/entrypoints/telegram.d.ts +2 -0
  42. package/dist/entrypoints/telegram.d.ts.map +1 -1
  43. package/dist/entrypoints/telegram.js +344 -11
  44. package/dist/entrypoints/testing.d.ts +1 -0
  45. package/dist/entrypoints/testing.d.ts.map +1 -1
  46. package/dist/entrypoints/testing.js +138 -11
  47. package/dist/entrypoints/tools/connections.js +11 -11
  48. package/dist/entrypoints/tools/invoker.js +5 -9
  49. package/dist/entrypoints/tools/mcp.js +8 -14
  50. package/dist/entrypoints/tools.js +19 -25
  51. package/dist/entrypoints/tracking.js +3 -3
  52. package/dist/files/native-artifact.d.ts +14 -0
  53. package/dist/files/native-artifact.d.ts.map +1 -0
  54. package/dist/files/native-packaging-layout.d.ts +11 -2
  55. package/dist/files/native-packaging-layout.d.ts.map +1 -1
  56. package/dist/files/native-packaging-plugin.d.ts +53 -0
  57. package/dist/files/native-packaging-plugin.d.ts.map +1 -0
  58. package/dist/files/native-packaging.d.ts +44 -43
  59. package/dist/files/native-packaging.d.ts.map +1 -1
  60. package/dist/{index-v5ee8ath.js → index-1h2zv8rx.js} +10 -40
  61. package/dist/index-2kbysk9s.js +42 -0
  62. package/dist/{index-wbhdzxar.js → index-2s6fpehp.js} +5 -5
  63. package/dist/{index-ksfagmyz.js → index-4qh90tyw.js} +8 -8
  64. package/dist/{index-yvfk3y36.js → index-636mggad.js} +4 -3
  65. package/dist/{index-f199nfq5.js → index-6y9t2fwz.js} +7 -7
  66. package/dist/{index-w8etpm8j.js → index-9j2atjte.js} +1 -1
  67. package/dist/index-a5qr2rj6.js +26 -0
  68. package/dist/{index-azx12jgv.js → index-bh2ht9y5.js} +13 -7
  69. package/dist/{index-pz3y6b6g.js → index-cx2nh595.js} +3 -3
  70. package/dist/{index-2k91mewf.js → index-dm2fv45z.js} +9 -9
  71. package/dist/{index-9bskshe3.js → index-dwf0gymy.js} +10 -10
  72. package/dist/{index-qaqbdm7s.js → index-h7qms8f9.js} +8 -8
  73. package/dist/{index-drs41arz.js → index-ktjmgq9p.js} +1 -1
  74. package/dist/{index-916yh4e2.js → index-mep4sb48.js} +14 -14
  75. package/dist/{index-wdy68sh6.js → index-njs9gvrh.js} +17 -17
  76. package/dist/{index-1ptj98e0.js → index-nx49x0tw.js} +2 -2
  77. package/dist/{index-1pwwan01.js → index-p5kx5vqd.js} +214 -59
  78. package/dist/{index-sj28htvf.js → index-pvh3z03x.js} +1 -1
  79. package/dist/{index-r393tnfp.js → index-q6crhe1n.js} +4 -4
  80. package/dist/{index-dsnjzb62.js → index-t2bsd0hm.js} +1 -1
  81. package/dist/{index-4vsby8n8.js → index-w9mqgnz9.js} +1 -1
  82. package/dist/{index-z23rfxhj.js → index-xmgjtkvr.js} +1 -1
  83. package/dist/{index-e05cjgtf.js → index-xvj9h8yh.js} +28 -1
  84. package/dist/{index-gr5g3xfw.js → index-yz8x0hbx.js} +1 -1
  85. package/dist/{index-gxm9rc97.js → index-zrtrjkbf.js} +1 -1
  86. package/dist/internal/bounded-lines.d.ts +46 -2
  87. package/dist/internal/bounded-lines.d.ts.map +1 -1
  88. package/dist/internal/darwin-binding-error.d.ts +9 -1
  89. package/dist/internal/darwin-binding-error.d.ts.map +1 -1
  90. package/dist/internal/darwin-binding.d.ts.map +1 -1
  91. package/dist/internal/process-identity.d.ts.map +1 -1
  92. package/dist/internal/until-aborted.d.ts +9 -0
  93. package/dist/internal/until-aborted.d.ts.map +1 -0
  94. package/dist/internal/upgrade-affects.d.ts +58 -0
  95. package/dist/internal/upgrade-affects.d.ts.map +1 -0
  96. package/dist/internal/upgrade-plan.d.ts +20 -0
  97. package/dist/internal/upgrade-plan.d.ts.map +1 -1
  98. package/dist/internal/upgrade-usage.d.ts +61 -0
  99. package/dist/internal/upgrade-usage.d.ts.map +1 -0
  100. package/dist/telegram/bot-api.d.ts +10 -4
  101. package/dist/telegram/bot-api.d.ts.map +1 -1
  102. package/dist/telegram/broadcast.d.ts +2 -1
  103. package/dist/telegram/broadcast.d.ts.map +1 -1
  104. package/dist/telegram/not-dispatched.d.ts +26 -0
  105. package/dist/telegram/not-dispatched.d.ts.map +1 -0
  106. package/dist/telegram/operator-channel.d.ts +2 -1
  107. package/dist/telegram/operator-channel.d.ts.map +1 -1
  108. package/dist/telegram/send-failure.d.ts +4 -3
  109. package/dist/telegram/send-failure.d.ts.map +1 -1
  110. package/dist/telegram/transport-connect.d.ts +38 -0
  111. package/dist/telegram/transport-connect.d.ts.map +1 -0
  112. package/dist/telegram/transport.d.ts +43 -0
  113. package/dist/telegram/transport.d.ts.map +1 -0
  114. package/dist/telegram/webhook.d.ts +2 -1
  115. package/dist/telegram/webhook.d.ts.map +1 -1
  116. package/dist/testing/manual-clock.d.ts +82 -0
  117. package/dist/testing/manual-clock.d.ts.map +1 -0
  118. package/dist/tools/internal/surface-projector.d.ts +0 -7
  119. package/dist/tools/internal/surface-projector.d.ts.map +1 -1
  120. package/dist/tools/runtime-tool-declaration.d.ts +11 -0
  121. package/dist/tools/runtime-tool-declaration.d.ts.map +1 -1
  122. package/dist/tools/runtime-tool.d.ts +2 -3
  123. package/dist/tools/runtime-tool.d.ts.map +1 -1
  124. package/llms/files-packaging.txt +13 -6
  125. package/llms/process.txt +71 -15
  126. package/llms/stitchkit.2.txt +211 -628
  127. package/llms/stitchkit.txt +635 -10
  128. package/llms/telegram.txt +36 -3
  129. package/llms/testing.txt +47 -0
  130. package/llms/{upgrading-0.96.0-0.106.0.txt → upgrading-0.100.0-0.108.0.txt} +112 -144
  131. package/llms/{upgrading-0.37.0-0.53.0.txt → upgrading-0.37.0-0.58.0.txt} +209 -3
  132. package/llms/{upgrading-0.55.0-0.68.0.txt → upgrading-0.59.0-0.71.0.txt} +149 -209
  133. package/llms/{upgrading-0.69.0-0.95.0.txt → upgrading-0.72.0-0.99.0.txt} +137 -149
  134. package/llms/upgrading.txt +36 -4
  135. package/llms-full.txt +360 -39
  136. package/llms.txt +10 -10
  137. package/package.json +2 -1
  138. package/llms/stitchkit.3.txt +0 -177
  139. package/dist/{index-cazts87s.js → index-07dcf2yn.js} +3 -3
  140. package/dist/{index-n1nszyk0.js → index-7jvwrjqa.js} +3 -3
  141. package/dist/{index-cmzvhhtd.js → index-85qve8mq.js} +3 -3
  142. package/dist/{index-rpzewktb.js → index-fynr93w4.js} +3 -3
  143. package/dist/{index-37asdw3a.js → index-k7qdkh3n.js} +3 -3
  144. package/dist/{index-x7w1z6pf.js → index-q4hjtyye.js} +4 -4
  145. package/dist/{index-7n68nmc6.js → index-y3nemndz.js} +3 -3
  146. package/dist/{index-12zsej7b.js → index-y70v24cw.js} +3 -3
  147. package/dist/{index-pzb63z66.js → index-ykdcnq1f.js} +3 -3
@@ -11,10 +11,10 @@ Contains: Guide: Upgrading, general (docs/guide/upgrading.md)
11
11
 
12
12
  Load this slice, then every range slice between your installed version and the target:
13
13
 
14
- - 0.96.0 → 0.106.0: `llms/upgrading-0.96.0-0.106.0.txt`
15
- - 0.69.0 → 0.95.0: `llms/upgrading-0.69.0-0.95.0.txt`
16
- - 0.55.0 → 0.68.0: `llms/upgrading-0.55.0-0.68.0.txt`
17
- - 0.37.0 → 0.53.0: `llms/upgrading-0.37.0-0.53.0.txt`
14
+ - 0.100.0 → 0.108.0: `llms/upgrading-0.100.0-0.108.0.txt`
15
+ - 0.72.0 → 0.99.0: `llms/upgrading-0.72.0-0.99.0.txt`
16
+ - 0.59.0 → 0.71.0: `llms/upgrading-0.59.0-0.71.0.txt`
17
+ - 0.37.0 → 0.58.0: `llms/upgrading-0.37.0-0.58.0.txt`
18
18
 
19
19
  # Upgrading stitchkit
20
20
 
@@ -41,6 +41,38 @@ of the range if you want a different one.
41
41
  So upgrading is: read the `### ⚠️ Breaking changes` of every version *above* your
42
42
  current one *up to* your target, and apply each snippet.
43
43
 
44
+ ### Does an item touch your project?
45
+
46
+ Every breaking item since 0.104.0 carries one machine line, `**Affects:**`, naming what it changes
47
+ in a form that is read without guessing from prose:
48
+
49
+ ```md
50
+ **Affects:** `stitchkit/files/packaging` createNativePackaging(delivery: 'embedded')
51
+ **Affects:** `stitchkit/contract`, `stitchkit/server` AppError; `stitchkit` ApiError
52
+ **Affects:** `stitchkit/process` behaviour
53
+ **Affects:** `stitchkit/agent-runtime/harness-tools` *
54
+ ```
55
+
56
+ Targets are separated by `; `. Each starts with one or more backticked entrypoints, then lists the
57
+ exports it changes (an optional parenthesised qualifier names the option or member), or says
58
+ `behaviour` for a change of semantics no import name reveals, or `*` for every import of a removed
59
+ entrypoint. `stitchkit upgrade` reads your project's imports (`--cwd`, default `.`; TypeScript, JavaScript,
60
+ Vue and Svelte files; not `node_modules`, and not a `dist`, `build`, `out` or `coverage` folder
61
+ beside a `package.json`, which is build output) and gives each item one verdict:
62
+
63
+ - **affects this project** — with every `path:line` that imports a named export, or a namespace;
64
+ - **not used here** — nothing imported matches;
65
+ - **behavioural — check by hand** — with the files that import the entrypoint;
66
+ - **not declared — check by hand** — an item from before the line existed, with the files that
67
+ import the entrypoints it leads with.
68
+
69
+ `--json` prints the same verdicts per item for a CI step. The scan reads static `import` and
70
+ `export … from` declarations, bare imports and `import()` / `require()` of a literal specifier; a
71
+ specifier built at run time is invisible to it, and so is a file of another kind (`.astro`,
72
+ `.mdx`). A directory or file the process may not read is
73
+ listed under **Not scanned** (`unscanned` in `--json`) instead of ending the plan: the verdicts do
74
+ not cover it.
75
+
44
76
  ## What your range does, and does not, do for you
45
77
 
46
78
  A caret range (`"stitchkit": "^0.71.0"`) is a real gate: it resolves `< 0.72.0`,
package/llms-full.txt CHANGED
@@ -62,9 +62,9 @@ own, recorded as an ADR.
62
62
  | `stitchkit/cli/publish` | build and release tooling (Bun or Node, POSIX) | evolving | `publishCli` — commit a verified CLI distribution and move its public manifest last; see [CLI guide](cli.md#publishing-a-complete-version) |
63
63
  | `stitchkit/remote` | browser **and** server | stable | peer-free `implementRemote` for thin HTTP proxy processes |
64
64
  | `stitchkit/process` | server (Bun or Node, POSIX) | evolving | finite native commands, byte sinks, bounded capture and process-group cancellation |
65
- | `stitchkit/files/packaging` | build time (Bun or Node) | evolving | installed native asset graph, integrity and Bun packaging plugin; no runtime install policy |
65
+ | `stitchkit/files/packaging` | build time (Bun or Node) | evolving | installed native asset graph, integrity, Bun packaging plugin and the artifact loader check; no runtime install policy |
66
66
  | `stitchkit/files` | server (Bun or Node) | stable | peer-free managed local-file boundary |
67
- | `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 |
67
+ | `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, a Bot API transport that separates a request that never left from an unknown outcome, local Bot API files, webhook ownership and durable webhook update intake |
68
68
  | `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 |
69
69
  | `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 |
70
70
  | `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,12 +74,12 @@ own, recorded as an ADR.
74
74
  | `stitchkit/geo` | server (Bun or Node) | evolving | managed GeoIP reader generations, last-known-good reload and the optional MaxMind adapter |
75
75
  | `stitchkit/oauth` | browser **and** server | evolving | provider-neutral Authorization Code + PKCE request and one-shot callback transaction mechanics |
76
76
  | `stitchkit/google` | server (Bun or Node) | evolving | optional Google code exchange and verified OIDC identity adapter |
77
- | `stitchkit/observability` | server | stable<br>_redefined in 2 of the 51 minors since 0.56.2, most recently 0.92.0_ | request/tool event projections — `createObservability`, trace context, sanitisation |
78
- | `stitchkit/testing` | tests on Bun or Node | stable | in-process generated clients over a real Fetch handler, plus the managed-resource conformance kit |
77
+ | `stitchkit/observability` | server | stable<br>_redefined in 2 of the 53 minors since 0.56.2, most recently 0.92.0_ | request/tool event projections — `createObservability`, trace context, sanitisation |
78
+ | `stitchkit/testing` | tests on Bun or Node | stable | in-process generated clients over a real Fetch handler, the managed-resource conformance kit and manual time for schedules and retries |
79
79
  | `stitchkit/declaration` | browser + build and deployment tooling (Bun or Node) | evolving | `ProjectDeclarationSchema` — the one machine-readable statement a repository makes about itself |
80
80
  | `stitchkit/react` | browser + server rendering | stable | `createCursorQuery`, `createCacheBridge`, QueryClient and `ApiError` retry policy |
81
81
  | `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) |
82
- | `stitchkit/agent-runtime` | server | evolving<br>_redefined in 23 of the 51 minors since 0.56.2, most recently 0.105.0_ | optional durable conversation/run loop, history, models, prompts, fencing and events |
82
+ | `stitchkit/agent-runtime` | server | evolving<br>_redefined in 23 of the 53 minors since 0.56.2, most recently 0.105.0_ | optional durable conversation/run loop, history, models, prompts, fencing and events |
83
83
  | `stitchkit/agent-runtime/sandbox` | server | evolving | optional Linux Bubblewrap sessions, durable workspaces and a host HTTP credential gateway; see [sandbox guide](sandbox.md) |
84
84
  | `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 |
85
85
  | `stitchkit/agent-runtime/harness` | server | evolving | resource-aware process-local facade over the canonical Agent runtime; supervision stays outside |
@@ -91,7 +91,7 @@ own, recorded as an ADR.
91
91
  | `stitchkit/agent-runtime/sqlite/bun` | server (Bun) | evolving | durable built-in SQLite store for the agent runtime |
92
92
  | `stitchkit/agent-runtime/sqlite/node` | server (Node ≥ 22.5) | evolving | durable built-in SQLite store for the agent runtime |
93
93
  | `stitchkit-tui` | terminal (Bun) | evolving | optional official OpenTUI host over a caller-composed headless runtime |
94
- | `stitchkit/application` | browser + server | evolving<br>_redefined in 13 of the 51 minors since 0.56.2, most recently 0.105.0_ | managed resource graph, readiness, admission, schedules, subtree restart and bounded shutdown |
94
+ | `stitchkit/application` | browser + server | evolving<br>_redefined in 13 of the 53 minors since 0.56.2, most recently 0.105.0_ | managed resource graph, readiness, admission, schedules, subtree restart and bounded shutdown |
95
95
  | `stitchkit/application/grammy` | server | evolving | a grammY bot as application resources — configuration, then polling with updates admitted by batch — and the webhook adapter |
96
96
  | `stitchkit/application/opentelemetry` | server | evolving | maps application snapshots onto an injected OpenTelemetry `Meter` |
97
97
  | `stitchkit/application/schemas` | browser + server | evolving | the application's snapshot, health and shutdown schemas alone, without the kernel |
@@ -3403,6 +3403,47 @@ Set `finalLine: 'require-newline'` when the final newline is part of the
3403
3403
  protocol's truncation proof. The default `allow` continues to accept one valid
3404
3404
  final JSON document without a newline.
3405
3405
 
3406
+ ### Any byte source
3407
+
3408
+ `parseNDJSON` (and `parseSSE`) read more than a `Response`: a `ReadableStream<Uint8Array>`
3409
+ (a fetch body, a Bun child's `stdout`) or any `AsyncIterable<Uint8Array>` (a Node child's
3410
+ `stdout`). `signal` stops reading and cancels or returns the source:
3411
+
3412
+ ```ts
3413
+ import { parseNDJSON } from 'stitchkit'
3414
+
3415
+ const child = Bun.spawn(['worker'], { stdout: 'pipe' })
3416
+ for await (const progress of parseNDJSON(child.stdout, {
3417
+ maxLineBytes: 64 * 1024,
3418
+ finalLine: 'require-newline',
3419
+ })) report(progress)
3420
+ ```
3421
+
3422
+ A source that hands over bytes through a callback — a `Bun.listen` or `Bun.connect` socket's
3423
+ `data` handler — uses `createNDJSONDecoder`, the same reader driven by pushes:
3424
+
3425
+ ```ts
3426
+ import { createNDJSONDecoder } from 'stitchkit'
3427
+
3428
+ const decoder = createNDJSONDecoder<ControlRequest>({ maxLineBytes: 16 * 1024 })
3429
+ Bun.listen({
3430
+ unix: socketPath,
3431
+ socket: {
3432
+ data(socket, bytes) {
3433
+ for (const request of decoder.push(bytes)) handle(socket, request)
3434
+ },
3435
+ },
3436
+ })
3437
+ ```
3438
+
3439
+ Use one decoder per connection. Lines are split in bytes and decoded only once complete, so a
3440
+ character split across chunks is never torn. A line that crosses `maxLineBytes` throws
3441
+ `StreamLineLimitError` (`limitBytes`, `lineBytes`) at the chunk that crossed it: the reader
3442
+ retains at most the limit, however long the line. With `finalLine: 'require-newline'`, a source
3443
+ that ends inside a line throws `StreamTruncatedLineError` (`lineBytes`) instead of yielding the
3444
+ fragment as a value — the right policy for a socket or a child process, where a missing newline
3445
+ means the writer died. Both errors extend the `RangeError` / `SyntaxError` they replace.
3446
+
3406
3447
  ## Resumable streams
3407
3448
 
3408
3449
  A stream that survives a dropped connection needs four things beyond opening it:
@@ -8585,23 +8626,55 @@ version: carry their reachable relative `.d.ts` closure, rather than emitting re
8585
8626
  instead emit declarations for only that DTO contract. Keep the library's manifest at runtime
8586
8627
  `dependencies: {}` and `peerDependencies: { zod: ... }` when that is its declared promise.
8587
8628
 
8588
- The packaged Darwin loader names its addons by a path computed at run time, so a bundler
8589
- never follows it: bundling `stitchkit/server`, `stitchkit/files` or `stitchkit/process` yields
8590
- the same single JS file on every operating system, with no `.node` output, and
8591
- `bun build --outfile` works. Such a bundle looks for the addon beside the original package
8592
- files, never inside itself. Where that file is absent (an artifact moved to another machine)
8593
- the Darwin backend is `unavailable`: process identity cannot be recorded, so a lock held by a
8594
- crashed owner is not reclaimed. An artifact that must carry the Darwin addon uses the packaging
8595
- plugin of the next section; it is the only way a static loader enters a bundle.
8629
+ The packaged Darwin loader computes its addon path at run time from its own file
8630
+ (`module.filename`), so a bundler never follows it: bundling `stitchkit/server`,
8631
+ `stitchkit/files` or `stitchkit/process` yields the same single JS file on every operating
8632
+ system, with no `.node` output, `bun build --outfile` works, and no path of the build machine
8633
+ becomes a literal of the artifact. A bundle has no installed package beside it, so without the
8634
+ packaging plugin it never loads the addon: the first call that needs it reports the Darwin backend
8635
+ `unavailable` with stage `packaging`, and the message names `createNativePackaging`. An artifact
8636
+ that must carry the Darwin addon uses the packaging plugin of the next section; it is the only way
8637
+ a static loader enters a bundle.
8638
+
8639
+ Only these calls load the addon, and only on macOS: process identity (`observeProcessInstance`
8640
+ and `probeProcessOwner`), every exclusive lock (`withExclusiveLock` and what is built on it, such
8641
+ as the file state store and the diagnostic journal) and the contained file operations of the Agent
8642
+ coding tools. Without the addon a lock still works, but it cannot record or check its owner's
8643
+ identity, so a lock held by a crashed owner is not reclaimed. The managed file boundary
8644
+ (`createManagedFileBoundary` and the transfer tools built on it), atomic writes and the chunk
8645
+ spool never load it, on any operating system. The package is free of import side effects
8646
+ (`sideEffects: false`), so a bundle that imports only those parts carries no Darwin loader at all.
8647
+
8648
+ Check an artifact by its loader, never by whether it holds `.node` bytes: Bun can copy the addon
8649
+ into a compiled executable even without the plugin, and the unpackaged loader does not use it.
8650
+ `inspectNativeArtifact(bytes)` from `stitchkit/files/packaging` reads the marker every generated
8651
+ loader carries and answers `packaged` (the plugin's static loader is inside), `unpackaged` (the
8652
+ default loader is inside, and every call above refuses on macOS) or `no-loader` (nothing in the
8653
+ artifact loads the addon). It reads both latin1 and UTF-16LE, because Bun stores source text with
8654
+ non-ASCII characters as UTF-16, and it does not mistake the `STITCHKIT_NATIVE_NOT_PACKAGED` error
8655
+ code, which every bundle with a lock carries, for a loader. Artifacts built by 0.107.0 or earlier
8656
+ carry no marker and read `no-loader`.
8657
+
8658
+ ```ts
8659
+ import { inspectNativeArtifact } from 'stitchkit/files/packaging'
8660
+
8661
+ const loader = inspectNativeArtifact(await Bun.file('dist/cli').bytes())
8662
+ if (loader === 'unpackaged') throw new Error('dist/cli was built without native packaging')
8663
+ ```
8596
8664
 
8597
8665
  ```sh
8598
8666
  bun build src/native.ts --target=bun --minify --outfile=dist/native.js
8599
8667
  ```
8600
8668
 
8669
+ That plain build serves Linux and the parts that never load the addon. A library whose consumers
8670
+ take locks or read process identity on macOS builds each native entry with the companion plugin of
8671
+ the next section (one entry per build, `naming.entry` equal to `entryPath`) and ships the addon it
8672
+ writes beside the entries.
8673
+
8601
8674
  Qualify the complete output outside its build tree and installed dependency graph. Test process
8602
8675
  identity, live-owner refusal, dead-owner recovery and contained file operations in artifacts built
8603
8676
  with the packaging plugin, and check that an unpackaged build reports the backend `unavailable`
8604
- rather than certifying a dead owner. Bun and Node package imports retain the same
8677
+ with stage `packaging` rather than certifying a dead owner. Bun and Node package imports retain the same
8605
8678
  lazy loader; importing a portable leaf does not load a Darwin addon on another OS. Test
8606
8679
  declarations too, and check that importing an unbundled Stitchkit leaf fails in a deliberately
8607
8680
  isolated library distribution. A separate schema-only entry must not import the native entry.
@@ -8609,7 +8682,9 @@ isolated library distribution. A separate schema-only entry must not import the
8609
8682
  If the Darwin backend cannot load, `observeProcessInstance` still returns `unavailable`.
8610
8683
  Its backend `Error` preserves the original `cause`; JSON serialization carries only the stable
8611
8684
  `DARWIN_BACKEND_UNAVAILABLE` code, architecture, safe message and failure stage
8612
- (`architecture`, `resolve`, `load` or `surface`), plus a recognized native error code when present.
8685
+ (`architecture`, `packaging`, `resolve`, `load` or `surface`), plus a recognized native error code
8686
+ when present: `packaging` is a bundle built without the packaging plugin, with native code
8687
+ `STITCHKIT_NATIVE_NOT_PACKAGED`.
8613
8688
  It excludes stack, paths and nested cause. This backend diagnosis does not reclassify kernel
8614
8689
  failures or turn unavailable identity into proof that an owner is dead. If a runtime provides
8615
8690
  no recognized native error code, the stage stays `load`; the loader never infers errno from text.
@@ -8656,7 +8731,7 @@ for (const asset of native.assets) {
8656
8731
  }
8657
8732
  ```
8658
8733
 
8659
- `entryPath` and `assetPath` are relative paths inside the application's output root, without
8734
+ In companion delivery, `entryPath` and `assetPath` are relative paths inside the application's output root, without
8660
8735
  absolute paths, traversal or overlapping file/directory paths. `assetPath` may contain `[hash]`,
8661
8736
  which becomes the first 16 hex digits of that addon's published SHA256: the digest is known before
8662
8737
  the build, so `addons/darwin-arm64-[hash].node` gives two installed versions of the package
@@ -8665,6 +8740,11 @@ addon path accepts. `entryPath` is fixed, without
8665
8740
  Bun naming templates such as `[dir]` or `[name]`. Companion JS builds require one entry, no splitting,
8666
8741
  and `naming.entry` exactly matching `entryPath`; the plugin rejects a mismatching layout.
8667
8742
  Choose any application layout; the framework does not prescribe an app directory or installer.
8743
+ The plugin packages the loader of the installation it was created from. A build whose entry
8744
+ imports another `stitchkit` installation — another version, or the same version under another
8745
+ path, as when a build tool carries its own copy — fails with an error naming both versions and
8746
+ roots: that installation's loader would refuse the addon at run time, so the artifact is never
8747
+ written. Call `createNativePackaging` from the `stitchkit` the entry imports.
8668
8748
  Keep every bundler output and write `asset.bytes`; never read the addon from `node_modules` a
8669
8749
  second time. An installed addon whose size or SHA256 differs from the published manifest refuses
8670
8750
  with `mismatch` / `NATIVE_ASSET_DIGEST_MISMATCH`, so a file substituted after installation never
@@ -8695,10 +8775,27 @@ process primitives without loading Darwin assets. Qualify the exact same JS byte
8695
8775
  Darwin arm64 and x64 machines, with both companions preserved through archive delivery.
8696
8776
  Build-machine architecture and cross-build success cannot establish universal native support.
8697
8777
 
8698
- For a standalone executable use `delivery: 'embedded'` and the same plugin in `Bun.build`
8699
- with `compile: { outfile: ... }`. Bun embeds the selected addon from the verified bytes the
8700
- plugin holds, not from a second read of the file; `assets` identifies those bytes and their
8701
- published digest for qualification, not a companion that must be installed beside the executable.
8778
+ For a standalone executable use `delivery: 'embedded'` and its plugin in `Bun.build` with
8779
+ `compile`. An executable carries the addon inside itself, so this form names no `entryPath` or
8780
+ `assetPath`; passing one is a type error and a schema refusal, never silently ignored:
8781
+
8782
+ ```ts
8783
+ const native = createNativePackaging({
8784
+ platform: 'darwin', architecture: 'arm64', delivery: 'embedded',
8785
+ })
8786
+ if (native.state !== 'ready') throw new Error(native.code)
8787
+ const result = await Bun.build({
8788
+ entrypoints: ['src/cli.ts'], minify: true,
8789
+ compile: { target: 'bun-darwin-arm64', outfile: 'dist/cli' },
8790
+ plugins: [native.plugin],
8791
+ })
8792
+ if (!result.success) throw new AggregateError(result.logs, 'Build failed')
8793
+ ```
8794
+
8795
+ Bun embeds the selected addon from the verified bytes the plugin holds, not from a second read of
8796
+ the file, and the executable loads it from `/$bunfs/root/darwin-arm64-<hash>.node`. Its `assets`
8797
+ (`NativePackagingEmbeddedAsset`: `bytes`, `size`, `sha256`) identify those bytes and their
8798
+ published digest for qualification; there is nothing to write beside the executable.
8702
8799
  Cross-builds must select the requested architecture explicitly; the artifact must run on that target.
8703
8800
  The installed package must contain that target's addon. No automatic fallback/downgrade occurs.
8704
8801
 
@@ -13880,6 +13977,50 @@ The suite in `packages/core/tests` is the working reference for testing each
13880
13977
  piece. Its size changes with the public surface, so the guide does not pin a
13881
13978
  count that can drift independently from the test runner.
13882
13979
 
13980
+ ### Manual time for schedules and retries
13981
+
13982
+ `createManualClock()` from `stitchkit/testing` stands time still until the test moves it. It is
13983
+ the `clock` of `createManagedSchedule`, a durable sleep, a revision signal or bounded admission,
13984
+ and the `schedule` a retrier of your own takes:
13985
+
13986
+ ```ts
13987
+ import { createManagedSchedule } from 'stitchkit/application'
13988
+ import { createManualClock } from 'stitchkit/testing'
13989
+
13990
+ const clock = createManualClock()
13991
+ const schedule = createManagedSchedule({ id: 'sweep', everyMs: 1_000, clock, run })
13992
+ // …start and activate it…
13993
+ await clock.advance(3_500) // runs at 1 s, 2 s and 3 s of manual time; the test takes milliseconds
13994
+ ```
13995
+
13996
+ Only one `advance` (or `until`) runs at a time; a second one started before the first settled is
13997
+ refused, because two interleaved moves would run time backwards.
13998
+
13999
+ Due timers fire in deadline order, ties in scheduling order. After each firing the clock yields
14000
+ real event-loop turns, not just microtasks: a continuation that arms the next retry after an
14001
+ already-readable socket or another macrotask has armed it before the clock moves on, so
14002
+ `advance(10_000)` does not skip a retry armed at the fifth second. Real work of unknown length —
14003
+ a request to a fake server, a hash in the thread pool — outruns any number of turns; the test
14004
+ wraps the dependency it injects in a `hold`:
14005
+
14006
+ ```ts
14007
+ const request = async (body) => {
14008
+ const held = clock.hold('upload to the fake server')
14009
+ try {
14010
+ return await fakeServer.send(body)
14011
+ } finally {
14012
+ held.release()
14013
+ }
14014
+ }
14015
+ ```
14016
+
14017
+ Time does not move while a hold is open; past `holdLimitMs` of real time (default 5 s)
14018
+ `advance` fails with `ManualClockError` `MANUAL_CLOCK_HOLD_TIMEOUT`, naming every open hold. A
14019
+ held operation that waits for the clock itself — a request timing out on a deadline — `park`s
14020
+ its hold so the deadline can come. `until(done)` moves from deadline to deadline until `done()`
14021
+ holds and fails with `MANUAL_CLOCK_LIMIT` past `limitMs` of manual time; `pending()` counts the
14022
+ timers still waiting, so a timer leaked past `close()` shows as a number, not as a hang.
14023
+
13883
14024
  ### Managed-resource conformance
13884
14025
 
13885
14026
  Consumer-owned resource adapters can run the framework's deterministic
@@ -14973,8 +15114,8 @@ and pacing/progress intervals must fit native timers. The optional
14973
15114
  when the signal aborts if a custom sleeper ignores it. A send already in flight
14974
15115
  finishes and is recorded before stopping.
14975
15116
 
14976
- Without `classify`, known Bot API 429, unreachable-recipient, malformed-message
14977
- and server-error behavior remains. Unknown/no-status failures now halt as
15117
+ Without `classify`, known Bot API 429, unreachable-recipient, malformed-message,
15118
+ server-error and not-dispatched behavior remains. Unknown/no-status failures now halt as
14978
15119
  uncertain, rather than being automatically retried. Existing custom `send`
14979
15120
  implementations that relied on those retries must supply an explicit classifier
14980
15121
  for proven pre-effect failures. `report.halt` now carries
@@ -14993,6 +15134,36 @@ The installed
14993
15134
  fixture checks injected rate limits, ambiguity/no resend, wait budgets and abort
14994
15135
  on Bun and Node without a client SDK.
14995
15136
 
15137
+ ## A transport that knows whether a request left
15138
+
15139
+ `fetch` folds establishing the connection and waiting for the answer into one failure. From that
15140
+ failure nobody can say whether Telegram saw the message, so the senders above treat every network
15141
+ failure as unknown: a broadcast records the recipient `uncertain` and the operator channel drops
15142
+ the message rather than risk a duplicate. On a route that sometimes fails to connect at all, that
15143
+ loses messages that never left.
15144
+
15145
+ `createTelegramBotTransport()` connects first and writes the request only on an established
15146
+ connection. Every failure before the first byte is written is `TelegramNotDispatchedError`
15147
+ (`stage`: `lookup`, `connect` or `request`), and `classifyTelegramSendFailure` reads it as
15148
+ `not-dispatched` — `retryable`, with `evidence: 'transport'`. A broadcast retries it as
15149
+ `transient`; the operator channel sends it again. A failure after the request was written stays
15150
+ an outcome nobody can know.
15151
+
15152
+ ```ts
15153
+ import { createTelegramBotTransport, telegramOperatorSender } from 'stitchkit/telegram'
15154
+
15155
+ const transport = createTelegramBotTransport()
15156
+ const send = telegramOperatorSender({ token: env.BOT_TOKEN, fetch: transport })
15157
+ ```
15158
+
15159
+ Pass it as `fetch` to `callTelegramBotApi`, `telegramBroadcastSender`, `telegramOperatorSender`
15160
+ or the webhook calls. Addresses come in the system resolver's order with no family forced; the
15161
+ next address starts 250 ms later (Happy Eyeballs), and a round with no connection is repeated
15162
+ with new sockets inside `connectBudgetMs` (default 5 s, which also bounds the address lookup; each
15163
+ round waits `connectAttemptMs`, default 2 s). The answer must arrive within `responseTimeoutMs` (default 30 s) and fit
15164
+ `maxResponseBytes` (default 1 MiB). It speaks HTTP/1.1 over `https:`, and over `http:` for a
15165
+ local Bot API server, and it uses no proxy: where the network needs one, keep `fetch`.
15166
+
14996
15167
  ## Message markup
14997
15168
 
14998
15169
  `stitchkit/telegram/html` is Telegram's HTML parse mode as a tree, with no peer
@@ -15580,6 +15751,94 @@ analytics policy belong to the application.
15580
15751
 
15581
15752
  # Upgrading stitchkit
15582
15753
 
15754
+ ## Released migration: 0.108.0
15755
+
15756
+ ### a request that never left is its own send outcome
15757
+
15758
+ **Who must act:** code with an exhaustive `switch` or `Record` over `TelegramSendFailureReason` or
15759
+ over `TelegramSendFailure['evidence']` (`stitchkit/telegram`). Code that reads `retryable` and
15760
+ `recipientUnreachable` needs no change.
15761
+
15762
+ `classifyTelegramSendFailure` now returns `reason: 'not-dispatched'` with `evidence: 'transport'`
15763
+ for a `TelegramNotDispatchedError`: the request never left, so it is `retryable` and repeating it
15764
+ cannot duplicate a message
15765
+ ([ADR 0253](../decisions/0253-a-telegram-request-that-never-left-is-safe-to-repeat.md)). Only
15766
+ `createTelegramBotTransport` throws that error; with the default `fetch` nothing changes at run time.
15767
+
15768
+ 1. Add the reason and the evidence to every exhaustive mapping:
15769
+
15770
+ ```ts
15771
+ // before
15772
+ const code: Record<TelegramSendFailureReason, string> = { /* … */ unknown: 'E_UNKNOWN' }
15773
+ // after
15774
+ const code: Record<TelegramSendFailureReason, string> = {
15775
+ /* … */ 'not-dispatched': 'E_NOT_SENT', unknown: 'E_UNKNOWN',
15776
+ }
15777
+ ```
15778
+
15779
+ Search pattern: `TelegramSendFailureReason`, `TELEGRAM_SEND_FAILURE_REASONS` and `evidence ===`.
15780
+ 2. Type-check: a `Record` or an exhaustive `switch` without the new members fails to compile.
15781
+ 3. To use the distinction, pass `fetch: createTelegramBotTransport()` to the senders that need it
15782
+ (see the [Telegram guide](telegram.md#a-transport-that-knows-whether-a-request-left)).
15783
+
15784
+ ### a packaging plugin packages only its own installation
15785
+
15786
+ **Who must act:** builds that call `createNativePackaging` (`stitchkit/files/packaging`) from a
15787
+ `stitchkit` installation other than the one the bundled entry imports — typically a build tool that
15788
+ carries its own copy. Builds whose entry and plugin come from one installation are unchanged.
15789
+
15790
+ Such a build used to succeed and ship the other installation's default Darwin loader, which refuses
15791
+ the addon on macOS at run time. It now fails, naming both versions and roots
15792
+ ([ADR 0254](../decisions/0254-a-packaging-plugin-owns-one-loader-and-names-what-it-finds.md)).
15793
+
15794
+ 1. Create the plugin from the `stitchkit` the entry resolves, not from the build tool's copy:
15795
+
15796
+ ```ts
15797
+ // before: the build tool's own stitchkit
15798
+ import { createNativePackaging } from 'stitchkit/files/packaging'
15799
+ // after: resolved from the project that owns the entry
15800
+ const { createNativePackaging } = await import(
15801
+ Bun.resolveSync('stitchkit/files/packaging', projectRoot)
15802
+ )
15803
+ ```
15804
+
15805
+ Search pattern: `createNativePackaging` in build tooling that is installed apart from the project.
15806
+ 2. Check the artifact with `inspectNativeArtifact`: a correct build reads `packaged`. An artifact
15807
+ built by 0.107.0 or earlier carries no loader marker and reads `no-loader` whatever it holds, so
15808
+ rebuild before relying on the check; a test of `=== 'unpackaged'` alone passes such an artifact.
15809
+
15810
+ ## Released migration: 0.107.0
15811
+
15812
+ ### embedded native packaging names no paths
15813
+
15814
+ **Who must act:** builds that call `createNativePackaging` (`stitchkit/files/packaging`) with
15815
+ `delivery: 'embedded'`. Companion delivery is unchanged.
15816
+
15817
+ A standalone executable carries the Darwin addon inside itself, so the embedded form takes only
15818
+ `platform`, `architecture` and `delivery`. `entryPath` and `assetPath` were required before but
15819
+ changed nothing in the executable; passing them now is a type error and a schema refusal. The
15820
+ ready result names the embedded addon by `NativePackagingEmbeddedAsset` (`bytes`, `size`,
15821
+ `sha256`), without `outputPath`, since nothing is written beside the executable.
15822
+
15823
+ 1. Drop the two paths from the embedded call:
15824
+
15825
+ ```ts
15826
+ // before
15827
+ createNativePackaging({
15828
+ platform: 'darwin', architecture: 'arm64', delivery: 'embedded',
15829
+ entryPath: 'cli.js', assetPath: 'native/darwin-arm64.node',
15830
+ })
15831
+ // after
15832
+ createNativePackaging({ platform: 'darwin', architecture: 'arm64', delivery: 'embedded' })
15833
+ ```
15834
+
15835
+ Search pattern: `delivery: 'embedded'`.
15836
+ 2. Code that read `asset.outputPath` from an embedded result reads `bytes`, `size` and `sha256`
15837
+ only; an annotation `NativePackagingOptions` / `NativePackagingResult` on an embedded call
15838
+ becomes `NativePackagingEmbeddedOptions` / `NativePackagingEmbeddedResult`.
15839
+ 3. Rebuild every artifact made without the plugin by 0.105.0–0.106.1: it carries the build
15840
+ machine's package path (see [Darwin native assets in JS distributions](#darwin-native-assets-in-js-distributions)).
15841
+
15583
15842
  ## Released migration: 0.106.0
15584
15843
 
15585
15844
  ### a stopped leader settles as stopped
@@ -16180,13 +16439,27 @@ Custom output layout uses the evolving build-only packaging contract in the
16180
16439
  Three deliveries have different rules: an ordinary npm install retains the package's
16181
16440
  native assets; a JS distribution must ship its complete output graph; a Bun compiled
16182
16441
  executable embeds the matching addon and may still use `--outfile`. In 0.103.13–0.104.2 a plain
16183
- `bun build` carries the addon without a plugin; later releases carry it only when the build
16184
- uses `createNativePackaging` (`delivery: 'companion'` for JS, `'embedded'` for a compiled
16185
- executable).
16186
- The [native IO guide](native-io.md#libraries-with-a-zod-only-runtime) is the canonical recipe.
16187
- Check the resulting artifact offline outside the build tree and `node_modules`, including
16188
- process identity, live-owner lock refusal, dead-owner recovery and contained IO.
16189
- A missing or corrupt addon remains `unavailable`, never evidence that an owner died.
16442
+ `bun build` carries the addon without a plugin. Later releases load it from a bundle only when
16443
+ the build uses `createNativePackaging` (`delivery: 'companion'` for JS, `'embedded'` for a
16444
+ compiled executable). Without the plugin, Bun may still copy the addon's bytes into a compiled
16445
+ executable, but the loader never uses them, so their presence proves nothing.
16446
+
16447
+ What an unpackaged bundle does depends on the release:
16448
+
16449
+ - **0.105.0–0.106.1:** the bundle carries the absolute path of the package on the build machine,
16450
+ because the loader read `__dirname`. It looks for the addon there, so it works only on that
16451
+ machine, and the path itself leaks into a distributed artifact. Rebuild with a later release.
16452
+ - **0.107.0 and later:** no build path is in the artifact, and the first call that needs the
16453
+ addon reports the Darwin backend `unavailable` with stage `packaging`.
16454
+
16455
+ Only macOS is affected, and only process identity (`observeProcessInstance`,
16456
+ `probeProcessOwner`), exclusive locks and contained Agent file operations load the addon. The
16457
+ managed file boundary, atomic writes and the chunk spool never do. The
16458
+ [native IO guide](native-io.md#libraries-with-a-zod-only-runtime) is the canonical recipe and
16459
+ shows how to check an artifact by its loader without running it on a Mac. Check the resulting
16460
+ artifact offline outside the build tree and `node_modules`, including process identity,
16461
+ live-owner lock refusal, dead-owner recovery and contained IO. A missing or corrupt addon
16462
+ remains `unavailable`, never evidence that an owner died.
16190
16463
 
16191
16464
  ### CLI: repeated options are refused
16192
16465
 
@@ -16783,6 +17056,38 @@ of the range if you want a different one.
16783
17056
  So upgrading is: read the `### ⚠️ Breaking changes` of every version *above* your
16784
17057
  current one *up to* your target, and apply each snippet.
16785
17058
 
17059
+ ### Does an item touch your project?
17060
+
17061
+ Every breaking item since 0.104.0 carries one machine line, `**Affects:**`, naming what it changes
17062
+ in a form that is read without guessing from prose:
17063
+
17064
+ ```md
17065
+ **Affects:** `stitchkit/files/packaging` createNativePackaging(delivery: 'embedded')
17066
+ **Affects:** `stitchkit/contract`, `stitchkit/server` AppError; `stitchkit` ApiError
17067
+ **Affects:** `stitchkit/process` behaviour
17068
+ **Affects:** `stitchkit/agent-runtime/harness-tools` *
17069
+ ```
17070
+
17071
+ Targets are separated by `; `. Each starts with one or more backticked entrypoints, then lists the
17072
+ exports it changes (an optional parenthesised qualifier names the option or member), or says
17073
+ `behaviour` for a change of semantics no import name reveals, or `*` for every import of a removed
17074
+ entrypoint. `stitchkit upgrade` reads your project's imports (`--cwd`, default `.`; TypeScript, JavaScript,
17075
+ Vue and Svelte files; not `node_modules`, and not a `dist`, `build`, `out` or `coverage` folder
17076
+ beside a `package.json`, which is build output) and gives each item one verdict:
17077
+
17078
+ - **affects this project** — with every `path:line` that imports a named export, or a namespace;
17079
+ - **not used here** — nothing imported matches;
17080
+ - **behavioural — check by hand** — with the files that import the entrypoint;
17081
+ - **not declared — check by hand** — an item from before the line existed, with the files that
17082
+ import the entrypoints it leads with.
17083
+
17084
+ `--json` prints the same verdicts per item for a CI step. The scan reads static `import` and
17085
+ `export … from` declarations, bare imports and `import()` / `require()` of a literal specifier; a
17086
+ specifier built at run time is invisible to it, and so is a file of another kind (`.astro`,
17087
+ `.mdx`). A directory or file the process may not read is
17088
+ listed under **Not scanned** (`unscanned` in `--json`) instead of ending the plan: the verdicts do
17089
+ not cover it.
17090
+
16786
17091
  ## Released migration: 0.88.0
16787
17092
 
16788
17093
  Two mechanical edits, and only if your project builds these values rather than only reading them.
@@ -20270,8 +20575,11 @@ The browser-and-server entrypoint. Re-exports everything from
20270
20575
  | `LiveStateSource` / `LiveStateSourceOpenInput` / `LiveStateSourceOpenResult` | _types_ | host binding for one continuous snapshot/event boundary; transport retry and cursor semantics remain host-owned |
20271
20576
  | `LiveStateControllerError` / `LiveStateSubscriberError` | _types_ | isolated observer failure payloads that do not change source or subscriber truth |
20272
20577
  | `createRetainedTopics` | function | retained last-value store for sticky events — [guide](../guide/realtime.md#sticky-events) |
20273
- | `parseSSE` | function | parse an SSE `Response` into an async generator — [guide](../guide/client.md#sse) |
20274
- | `parseNDJSON` | function | parse bounded fatal-UTF-8 NDJSON; blank keep-alives are skipped and `finalLine: 'require-newline'` can make the delimiter mandatory — [guide](../guide/client.md#ndjson) |
20578
+ | `parseSSE` | function | parse SSE from a `Response`, a web stream or an async byte iterable into an async generator — [guide](../guide/client.md#sse) |
20579
+ | `parseNDJSON` | function | parse bounded fatal-UTF-8 NDJSON from a `Response`, a `ReadableStream<Uint8Array>` or an `AsyncIterable<Uint8Array>` (a child's stdout); blank keep-alives are skipped, `finalLine: 'require-newline'` makes the delimiter mandatory and `signal` cancels the source — [guide](../guide/client.md#any-byte-source) |
20580
+ | `createNDJSONDecoder` / `NDJSONDecoder` | function / _type_ | the same bounded NDJSON reading driven by `push(chunk)` / `end()`, for a socket `data` callback |
20581
+ | `StreamByteSource` | _type_ | `Response \| ReadableStream<Uint8Array> \| AsyncIterable<Uint8Array>`, what the NDJSON and SSE readers accept |
20582
+ | `StreamLineLimitError` / `StreamTruncatedLineError` | class | a line past `maxLineBytes` (`limitBytes`, `lineBytes`; a `RangeError`) and a source ended inside a line under `require-newline` (`lineBytes`; a `SyntaxError`) |
20275
20583
  | `resumableIterator` | function | re-open a long-lived stream from the last delivered cursor, with jittered backoff, a caller-owned terminal item and prompt abort — [guide](../guide/client.md#resumable-streams) |
20276
20584
  | `createBackoff` | function | exponential backoff with subtractive jitter as a value: `next()` / `reset()` |
20277
20585
  | `ContractStreamFrameSchema` / `ContractStreamFrame` | schema / _type_ | default on-the-wire `data` / safe `error` / `end` envelope of a contract-first stream |
@@ -22085,6 +22393,9 @@ handler pipeline without opening a TCP port.
22085
22393
  |--------|------|---------|
22086
22394
  | `createHandlerTestClient` | function | one contract client backed by an in-process `FetchHandler` |
22087
22395
  | `createHandlerTestClients` | function | exact contract-registry batch form |
22396
+ | `createManualClock` / `ManualClock` | function / _type_ | manual time for schedules, retries and deadlines: `now`, `wallNow`, `schedule`, `advance(ms)` firing due timers in order with real event-loop yields between them, `until`, `pending` and `hold`; a `ManagedScheduleClock`, `DurabilityClock`, `RevisionSignalClock` and `BoundedAdmissionClock` — [guide](../guide/testing-and-deployment.md#manual-time-for-schedules-and-retries) |
22397
+ | `ManualClockOptions` / `ManualClockUntilOptions` / `ManualClockTimer` / `ManualClockHold` | _types_ | start and wall instants, event-loop turns per firing and the real-time hold limit; `until` step and manual limit; a cancellable timer; a hold that is released or parked |
22398
+ | `ManualClockError` / `ManualClockErrorCode` | class / _type_ | `MANUAL_CLOCK_HOLD_TIMEOUT` naming the open holds, or `MANUAL_CLOCK_LIMIT` when `until` runs out |
22088
22399
  | `runManagedResourceConformance` | function | run the canonical deterministic lifecycle matrix against a fresh consumer-owned `ManagedResource` fixture; resolves `void` or throws `ManagedResourceConformanceError` with a stable scenario ID and normalized trace |
22089
22400
  | `ManagedResourceConformanceScenarioIdSchema` / `ManagedResourceConformanceScenarioId` | schema / _type_ | stable clean, rollback, readiness/completion, activation, shutdown-race and forced-cleanup scenario vocabulary |
22090
22401
  | `ManagedResourceConformanceScenarioSchema` / `ManagedResourceConformanceScenario` | schema / _type_ | discriminated scenario record including whether the controlled resource is required |
@@ -22194,7 +22505,10 @@ injected grammY bot. → ADR 0143, ADR 0201 — [guide](../guide/telegram.md)
22194
22505
  | `TelegramInitDataRefusal` | _type_ | `missing-hash` / `signature-mismatch` / `malformed` / `expired` — an expired string is not a forged one |
22195
22506
  | `classifyTelegramSendFailure` | function | name a refused Bot API send and separate "retry this send" from "stop addressing this recipient" |
22196
22507
  | `TelegramSendFailure` | _type_ | reason, `status`, Telegram-stated `retryAfterSeconds`, `retryable`, `recipientUnreachable` and which evidence produced the answer |
22197
- | `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 |
22508
+ | `createTelegramBotTransport` / `TelegramBotTransportOptions` | function / _type_ | a `fetch` for the Bot API senders that connects before it writes, so a failure before the first byte is `TelegramNotDispatchedError`; connection attempt and budget, answer timeout and size, injectable resolver and socket factory — [guide](../guide/telegram.md#a-transport-that-knows-whether-a-request-left) |
22509
+ | `TelegramFetch` / `TelegramBotTransportOpen` / `TelegramBotTransportResolve` / `TelegramBotTransportSocket` | _types_ | the `(url, init) => Promise<Response>` the senders take; the transport's socket factory, resolver and the structural socket it needs (a Node `net.Socket` or `tls.TLSSocket` fits) |
22510
+ | `TelegramNotDispatchedError` / `TelegramNotDispatchedStage` | class / _type_ | a request that provably never left (`lookup`, `connect` or `request`), so repeating it cannot duplicate a message |
22511
+ | `TelegramSendFailureReason` / `TELEGRAM_SEND_FAILURE_REASONS` | _type_ / constant | `blocked-by-user` / `user-deactivated` / `chat-not-found` / `not-started` / `rate-limited` / `message-invalid` / `server-error` / `not-dispatched` / `unknown`, and the same as a list; a `Record<TelegramSendFailureReason, …>` over them fails to compile when a reason is added |
22198
22512
  | `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 |
22199
22513
  | `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` |
22200
22514
  | `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 |
@@ -22743,13 +23057,19 @@ Native POSIX Bun/Node one-shot command execution. See [native IO](../guide/nativ
22743
23057
  ## `stitchkit/files/packaging`
22744
23058
 
22745
23059
  `stitchkit/files/packaging` owns `createNativePackaging` and the types
22746
- `NativePackagingOptions`, `NativePackagingAsset`, `NativePackagingPlugin`, `NativePackagingResult`.
23060
+ `NativePackagingOptions`, `NativePackagingAsset`, `NativePackagingPlugin`, `NativePackagingResult`,
23061
+ and for a standalone executable `NativePackagingEmbeddedOptions`, `NativePackagingEmbeddedAsset`,
23062
+ `NativePackagingEmbeddedResult`. `inspectNativeArtifact` classifies a built artifact's bytes by the
23063
+ loader it carries as a `NativeArtifactLoader`: `packaged`, `unpackaged` or `no-loader`. The plugin
23064
+ fails a build whose entry imports the default loader of another `stitchkit` installation, naming
23065
+ both versions and roots.
22747
23066
  It resolves the installed package's target/version, reads each selected addon once and checks it
22748
23067
  against the `size` and `sha256` published in the package's `native-assets.json` (`formatVersion: 2`,
22749
23068
  written when Stitchkit is built). Call it once per build and reuse the result.
22750
- `platform`, `architecture`, `delivery`, `entryPath` and `assetPath` are explicit public inputs;
22751
- `assetPath` accepts one template, `[hash]`, replaced by the first 16 hex digits of the addon's
22752
- published SHA256;
23069
+ `platform`, `architecture`, `delivery`, `entryPath` and `assetPath` are the explicit inputs of
23070
+ companion delivery; embedded delivery takes `platform`, one `architecture` and `delivery` only, and
23071
+ refuses a path. `assetPath` accepts one template, `[hash]`, replaced by the first 16 hex digits of
23072
+ the addon's published SHA256;
22753
23073
  `platform` is the closed set `'darwin'`, and another name is a type error and a schema refusal.
22754
23074
  The result is `ready` with verified assets and a structural Bun plugin, or `unsupported` /
22755
23075
  `missing` / `mismatch` with the safe code `NATIVE_TARGET_UNSUPPORTED`, `NATIVE_ASSET_MISSING` or
@@ -22766,7 +23086,8 @@ and the published `size` and `sha256`.
22766
23086
  Targets must be unique; entry and all asset paths must be disjoint. One lazy loader chooses only
22767
23087
  the actual runtime architecture, with no fallback.
22768
23088
  Companion mode places external addons relative to the emitted entry; embedded mode gives
22769
- Bun compile the verified bytes of the matching addon. Keep JS builds without splitting, preserve
22770
- every output, and write each asset's `bytes` to its `outputPath`.
23089
+ Bun compile the verified bytes of the matching addon, and its asset (`bytes`, `size`, `sha256`)
23090
+ has no `outputPath`, because nothing is written beside the executable. Keep companion JS builds
23091
+ without splitting, preserve every output, and write each asset's `bytes` to its `outputPath`.
22771
23092
  This evolving entrypoint is a build dependency; runtime imports do not reach it.
22772
23093
  The detailed recipe is in [native IO](../guide/native-io.md#public-native-packaging).