stitchkit 0.107.0 → 0.108.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 (102) hide show
  1. package/CHANGELOG.md +111 -0
  2. package/README.md +2 -0
  3. package/darwin-native.cjs +1 -0
  4. package/dist/agent-runtime/sandbox-process-owner.d.ts +3 -3
  5. package/dist/agent-runtime/sandbox-process-owner.d.ts.map +1 -1
  6. package/dist/agent-runtime/sandbox-process.d.ts +2 -2
  7. package/dist/agent-runtime/sandbox-process.d.ts.map +1 -1
  8. package/dist/agent-runtime/store-conformance-support.d.ts +5 -5
  9. package/dist/browser/stream.d.ts +38 -12
  10. package/dist/browser/stream.d.ts.map +1 -1
  11. package/dist/entrypoints/agent-runtime/coding-tools.js +2 -2
  12. package/dist/entrypoints/agent-runtime/sandbox.js +2 -2
  13. package/dist/entrypoints/application/diagnostic-journal.js +2 -2
  14. package/dist/entrypoints/application/directory-inbox.js +4 -4
  15. package/dist/entrypoints/bin/upgrade-cli.d.ts +32 -0
  16. package/dist/entrypoints/bin/upgrade-cli.d.ts.map +1 -1
  17. package/dist/entrypoints/bin/upgrade-cli.js +451 -8
  18. package/dist/entrypoints/cli/publish.js +3 -3
  19. package/dist/entrypoints/files/packaging.d.ts +3 -1
  20. package/dist/entrypoints/files/packaging.d.ts.map +1 -1
  21. package/dist/entrypoints/files/packaging.js +105 -41
  22. package/dist/entrypoints/files.js +3 -3
  23. package/dist/entrypoints/index.d.ts +1 -1
  24. package/dist/entrypoints/index.d.ts.map +1 -1
  25. package/dist/entrypoints/index.js +7 -1
  26. package/dist/entrypoints/process.js +2 -2
  27. package/dist/entrypoints/remote.js +1 -1
  28. package/dist/entrypoints/server.js +4 -4
  29. package/dist/entrypoints/telegram/screens.js +1 -1
  30. package/dist/entrypoints/telegram.d.ts +2 -0
  31. package/dist/entrypoints/telegram.d.ts.map +1 -1
  32. package/dist/entrypoints/telegram.js +337 -3
  33. package/dist/entrypoints/testing.d.ts +1 -0
  34. package/dist/entrypoints/testing.d.ts.map +1 -1
  35. package/dist/entrypoints/testing.js +131 -1
  36. package/dist/files/native-artifact.d.ts +14 -0
  37. package/dist/files/native-artifact.d.ts.map +1 -0
  38. package/dist/files/native-packaging-layout.d.ts +5 -0
  39. package/dist/files/native-packaging-layout.d.ts.map +1 -1
  40. package/dist/files/native-packaging-plugin.d.ts +53 -0
  41. package/dist/files/native-packaging-plugin.d.ts.map +1 -0
  42. package/dist/files/native-packaging.d.ts +1 -35
  43. package/dist/files/native-packaging.d.ts.map +1 -1
  44. package/dist/index-a5qr2rj6.js +26 -0
  45. package/dist/{index-7r74re56.js → index-bh2ht9y5.js} +12 -6
  46. package/dist/{index-dwf0gymy.js → index-e35py42g.js} +1 -1
  47. package/dist/{index-4q9jvqg7.js → index-epg514ys.js} +182 -13
  48. package/dist/{index-mbf4h8kp.js → index-nx49x0tw.js} +1 -1
  49. package/dist/{index-zsqt5q98.js → index-p5kx5vqd.js} +209 -54
  50. package/dist/{index-bnf8s6v3.js → index-t2bsd0hm.js} +1 -1
  51. package/dist/{index-4f37d3bg.js → index-w9mqgnz9.js} +1 -1
  52. package/dist/{index-e05cjgtf.js → index-xvj9h8yh.js} +28 -1
  53. package/dist/internal/bounded-lines.d.ts +46 -2
  54. package/dist/internal/bounded-lines.d.ts.map +1 -1
  55. package/dist/internal/process-identity.d.ts.map +1 -1
  56. package/dist/internal/until-aborted.d.ts +9 -0
  57. package/dist/internal/until-aborted.d.ts.map +1 -0
  58. package/dist/internal/upgrade-affects.d.ts +58 -0
  59. package/dist/internal/upgrade-affects.d.ts.map +1 -0
  60. package/dist/internal/upgrade-plan.d.ts +20 -0
  61. package/dist/internal/upgrade-plan.d.ts.map +1 -1
  62. package/dist/internal/upgrade-usage.d.ts +61 -0
  63. package/dist/internal/upgrade-usage.d.ts.map +1 -0
  64. package/dist/process/bun-child.d.ts +38 -0
  65. package/dist/process/bun-child.d.ts.map +1 -0
  66. package/dist/process/command-owner.d.ts +2 -2
  67. package/dist/process/command-owner.d.ts.map +1 -1
  68. package/dist/process/launch.d.ts +2 -2
  69. package/dist/process/launch.d.ts.map +1 -1
  70. package/dist/process/owned-child.d.ts +2 -1
  71. package/dist/process/owned-child.d.ts.map +1 -1
  72. package/dist/telegram/bot-api.d.ts +10 -4
  73. package/dist/telegram/bot-api.d.ts.map +1 -1
  74. package/dist/telegram/broadcast.d.ts +2 -1
  75. package/dist/telegram/broadcast.d.ts.map +1 -1
  76. package/dist/telegram/not-dispatched.d.ts +26 -0
  77. package/dist/telegram/not-dispatched.d.ts.map +1 -0
  78. package/dist/telegram/operator-channel.d.ts +2 -1
  79. package/dist/telegram/operator-channel.d.ts.map +1 -1
  80. package/dist/telegram/send-failure.d.ts +4 -3
  81. package/dist/telegram/send-failure.d.ts.map +1 -1
  82. package/dist/telegram/transport-connect.d.ts +38 -0
  83. package/dist/telegram/transport-connect.d.ts.map +1 -0
  84. package/dist/telegram/transport.d.ts +43 -0
  85. package/dist/telegram/transport.d.ts.map +1 -0
  86. package/dist/telegram/webhook.d.ts +2 -1
  87. package/dist/telegram/webhook.d.ts.map +1 -1
  88. package/dist/testing/manual-clock.d.ts +82 -0
  89. package/dist/testing/manual-clock.d.ts.map +1 -0
  90. package/llms/files-packaging.txt +4 -1
  91. package/llms/process.txt +19 -5
  92. package/llms/stitchkit.2.txt +211 -628
  93. package/llms/stitchkit.txt +635 -10
  94. package/llms/telegram.txt +36 -3
  95. package/llms/testing.txt +47 -0
  96. package/llms/{upgrading-0.98.0-0.107.0.txt → upgrading-0.100.0-0.108.0.txt} +59 -64
  97. package/llms/{upgrading-0.72.0-0.97.0.txt → upgrading-0.72.0-0.99.0.txt} +64 -3
  98. package/llms/upgrading.txt +34 -2
  99. package/llms-full.txt +246 -17
  100. package/llms.txt +8 -8
  101. package/package.json +1 -1
  102. package/llms/stitchkit.3.txt +0 -177
package/llms/telegram.txt CHANGED
@@ -234,8 +234,8 @@ and pacing/progress intervals must fit native timers. The optional
234
234
  when the signal aborts if a custom sleeper ignores it. A send already in flight
235
235
  finishes and is recorded before stopping.
236
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
237
+ Without `classify`, known Bot API 429, unreachable-recipient, malformed-message,
238
+ server-error and not-dispatched behavior remains. Unknown/no-status failures now halt as
239
239
  uncertain, rather than being automatically retried. Existing custom `send`
240
240
  implementations that relied on those retries must supply an explicit classifier
241
241
  for proven pre-effect failures. `report.halt` now carries
@@ -254,6 +254,36 @@ The installed
254
254
  fixture checks injected rate limits, ambiguity/no resend, wait budgets and abort
255
255
  on Bun and Node without a client SDK.
256
256
 
257
+ ## A transport that knows whether a request left
258
+
259
+ `fetch` folds establishing the connection and waiting for the answer into one failure. From that
260
+ failure nobody can say whether Telegram saw the message, so the senders above treat every network
261
+ failure as unknown: a broadcast records the recipient `uncertain` and the operator channel drops
262
+ the message rather than risk a duplicate. On a route that sometimes fails to connect at all, that
263
+ loses messages that never left.
264
+
265
+ `createTelegramBotTransport()` connects first and writes the request only on an established
266
+ connection. Every failure before the first byte is written is `TelegramNotDispatchedError`
267
+ (`stage`: `lookup`, `connect` or `request`), and `classifyTelegramSendFailure` reads it as
268
+ `not-dispatched` — `retryable`, with `evidence: 'transport'`. A broadcast retries it as
269
+ `transient`; the operator channel sends it again. A failure after the request was written stays
270
+ an outcome nobody can know.
271
+
272
+ ```ts
273
+ import { createTelegramBotTransport, telegramOperatorSender } from 'stitchkit/telegram'
274
+
275
+ const transport = createTelegramBotTransport()
276
+ const send = telegramOperatorSender({ token: env.BOT_TOKEN, fetch: transport })
277
+ ```
278
+
279
+ Pass it as `fetch` to `callTelegramBotApi`, `telegramBroadcastSender`, `telegramOperatorSender`
280
+ or the webhook calls. Addresses come in the system resolver's order with no family forced; the
281
+ next address starts 250 ms later (Happy Eyeballs), and a round with no connection is repeated
282
+ with new sockets inside `connectBudgetMs` (default 5 s, which also bounds the address lookup; each
283
+ round waits `connectAttemptMs`, default 2 s). The answer must arrive within `responseTimeoutMs` (default 30 s) and fit
284
+ `maxResponseBytes` (default 1 MiB). It speaks HTTP/1.1 over `https:`, and over `http:` for a
285
+ local Bot API server, and it uses no proxy: where the network needs one, keep `fetch`.
286
+
257
287
  ## Message markup
258
288
 
259
289
  `stitchkit/telegram/html` is Telegram's HTML parse mode as a tree, with no peer
@@ -453,7 +483,10 @@ injected grammY bot. → ADR 0143, ADR 0201 — [guide](../guide/telegram.md)
453
483
  | `TelegramInitDataRefusal` | _type_ | `missing-hash` / `signature-mismatch` / `malformed` / `expired` — an expired string is not a forged one |
454
484
  | `classifyTelegramSendFailure` | function | name a refused Bot API send and separate "retry this send" from "stop addressing this recipient" |
455
485
  | `TelegramSendFailure` | _type_ | reason, `status`, Telegram-stated `retryAfterSeconds`, `retryable`, `recipientUnreachable` and which evidence produced the answer |
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 |
486
+ | `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) |
487
+ | `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) |
488
+ | `TelegramNotDispatchedError` / `TelegramNotDispatchedStage` | class / _type_ | a request that provably never left (`lookup`, `connect` or `request`), so repeating it cannot duplicate a message |
489
+ | `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 |
457
490
  | `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 |
458
491
  | `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
492
  | `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 |
package/llms/testing.txt CHANGED
@@ -223,6 +223,50 @@ The suite in `packages/core/tests` is the working reference for testing each
223
223
  piece. Its size changes with the public surface, so the guide does not pin a
224
224
  count that can drift independently from the test runner.
225
225
 
226
+ ### Manual time for schedules and retries
227
+
228
+ `createManualClock()` from `stitchkit/testing` stands time still until the test moves it. It is
229
+ the `clock` of `createManagedSchedule`, a durable sleep, a revision signal or bounded admission,
230
+ and the `schedule` a retrier of your own takes:
231
+
232
+ ```ts
233
+ import { createManagedSchedule } from 'stitchkit/application'
234
+ import { createManualClock } from 'stitchkit/testing'
235
+
236
+ const clock = createManualClock()
237
+ const schedule = createManagedSchedule({ id: 'sweep', everyMs: 1_000, clock, run })
238
+ // …start and activate it…
239
+ await clock.advance(3_500) // runs at 1 s, 2 s and 3 s of manual time; the test takes milliseconds
240
+ ```
241
+
242
+ Only one `advance` (or `until`) runs at a time; a second one started before the first settled is
243
+ refused, because two interleaved moves would run time backwards.
244
+
245
+ Due timers fire in deadline order, ties in scheduling order. After each firing the clock yields
246
+ real event-loop turns, not just microtasks: a continuation that arms the next retry after an
247
+ already-readable socket or another macrotask has armed it before the clock moves on, so
248
+ `advance(10_000)` does not skip a retry armed at the fifth second. Real work of unknown length —
249
+ a request to a fake server, a hash in the thread pool — outruns any number of turns; the test
250
+ wraps the dependency it injects in a `hold`:
251
+
252
+ ```ts
253
+ const request = async (body) => {
254
+ const held = clock.hold('upload to the fake server')
255
+ try {
256
+ return await fakeServer.send(body)
257
+ } finally {
258
+ held.release()
259
+ }
260
+ }
261
+ ```
262
+
263
+ Time does not move while a hold is open; past `holdLimitMs` of real time (default 5 s)
264
+ `advance` fails with `ManualClockError` `MANUAL_CLOCK_HOLD_TIMEOUT`, naming every open hold. A
265
+ held operation that waits for the clock itself — a request timing out on a deadline — `park`s
266
+ its hold so the deadline can come. `until(done)` moves from deadline to deadline until `done()`
267
+ holds and fails with `MANUAL_CLOCK_LIMIT` past `limitMs` of manual time; `pending()` counts the
268
+ timers still waiting, so a timer leaked past `close()` shows as a number, not as a hang.
269
+
226
270
  ### Managed-resource conformance
227
271
 
228
272
  Consumer-owned resource adapters can run the framework's deterministic
@@ -617,6 +661,9 @@ handler pipeline without opening a TCP port.
617
661
  |--------|------|---------|
618
662
  | `createHandlerTestClient` | function | one contract client backed by an in-process `FetchHandler` |
619
663
  | `createHandlerTestClients` | function | exact contract-registry batch form |
664
+ | `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) |
665
+ | `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 |
666
+ | `ManualClockError` / `ManualClockErrorCode` | class / _type_ | `MANUAL_CLOCK_HOLD_TIMEOUT` naming the open holds, or `MANUAL_CLOCK_LIMIT` when `until` runs out |
620
667
  | `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 |
621
668
  | `ManagedResourceConformanceScenarioIdSchema` / `ManagedResourceConformanceScenarioId` | schema / _type_ | stable clean, rollback, readiness/completion, activation, shutdown-race and forced-cleanup scenario vocabulary |
622
669
  | `ManagedResourceConformanceScenarioSchema` / `ManagedResourceConformanceScenario` | schema / _type_ | discriminated scenario record including whether the controlled resource is required |
@@ -1,14 +1,70 @@
1
- # Upgrading 0.98.0 → 0.107.0 — stitchkit docs slice
1
+ # Upgrading 0.100.0 → 0.108.0 — stitchkit docs slice
2
2
 
3
3
  > Generated from the `docs/` tree by `bun run gen:llms`. The index of all slices is
4
4
  > `llms.txt` in this package.
5
5
 
6
- Contains: Guide: Upgrading, 0.98.0 → 0.107.0 (docs/guide/upgrading.md)
6
+ Contains: Guide: Upgrading, 0.100.0 → 0.108.0 (docs/guide/upgrading.md)
7
7
 
8
8
  ==============================================================================
9
- # Guide: Upgrading, 0.98.0 → 0.107.0 (docs/guide/upgrading.md)
9
+ # Guide: Upgrading, 0.100.0 → 0.108.0 (docs/guide/upgrading.md)
10
10
  ==============================================================================
11
11
 
12
+ ## Released migration: 0.108.0
13
+
14
+ ### a request that never left is its own send outcome
15
+
16
+ **Who must act:** code with an exhaustive `switch` or `Record` over `TelegramSendFailureReason` or
17
+ over `TelegramSendFailure['evidence']` (`stitchkit/telegram`). Code that reads `retryable` and
18
+ `recipientUnreachable` needs no change.
19
+
20
+ `classifyTelegramSendFailure` now returns `reason: 'not-dispatched'` with `evidence: 'transport'`
21
+ for a `TelegramNotDispatchedError`: the request never left, so it is `retryable` and repeating it
22
+ cannot duplicate a message
23
+ ([ADR 0253](../decisions/0253-a-telegram-request-that-never-left-is-safe-to-repeat.md)). Only
24
+ `createTelegramBotTransport` throws that error; with the default `fetch` nothing changes at run time.
25
+
26
+ 1. Add the reason and the evidence to every exhaustive mapping:
27
+
28
+ ```ts
29
+ // before
30
+ const code: Record<TelegramSendFailureReason, string> = { /* … */ unknown: 'E_UNKNOWN' }
31
+ // after
32
+ const code: Record<TelegramSendFailureReason, string> = {
33
+ /* … */ 'not-dispatched': 'E_NOT_SENT', unknown: 'E_UNKNOWN',
34
+ }
35
+ ```
36
+
37
+ Search pattern: `TelegramSendFailureReason`, `TELEGRAM_SEND_FAILURE_REASONS` and `evidence ===`.
38
+ 2. Type-check: a `Record` or an exhaustive `switch` without the new members fails to compile.
39
+ 3. To use the distinction, pass `fetch: createTelegramBotTransport()` to the senders that need it
40
+ (see the [Telegram guide](telegram.md#a-transport-that-knows-whether-a-request-left)).
41
+
42
+ ### a packaging plugin packages only its own installation
43
+
44
+ **Who must act:** builds that call `createNativePackaging` (`stitchkit/files/packaging`) from a
45
+ `stitchkit` installation other than the one the bundled entry imports — typically a build tool that
46
+ carries its own copy. Builds whose entry and plugin come from one installation are unchanged.
47
+
48
+ Such a build used to succeed and ship the other installation's default Darwin loader, which refuses
49
+ the addon on macOS at run time. It now fails, naming both versions and roots
50
+ ([ADR 0254](../decisions/0254-a-packaging-plugin-owns-one-loader-and-names-what-it-finds.md)).
51
+
52
+ 1. Create the plugin from the `stitchkit` the entry resolves, not from the build tool's copy:
53
+
54
+ ```ts
55
+ // before: the build tool's own stitchkit
56
+ import { createNativePackaging } from 'stitchkit/files/packaging'
57
+ // after: resolved from the project that owns the entry
58
+ const { createNativePackaging } = await import(
59
+ Bun.resolveSync('stitchkit/files/packaging', projectRoot)
60
+ )
61
+ ```
62
+
63
+ Search pattern: `createNativePackaging` in build tooling that is installed apart from the project.
64
+ 2. Check the artifact with `inspectNativeArtifact`: a correct build reads `packaged`. An artifact
65
+ built by 0.107.0 or earlier carries no loader marker and reads `no-loader` whatever it holds, so
66
+ rebuild before relying on the check; a test of `=== 'unpackaged'` alone passes such an artifact.
67
+
12
68
  ## Released migration: 0.107.0
13
69
 
14
70
  ### embedded native packaging names no paths
@@ -844,64 +900,3 @@ recovery refuses rather than treating unknown ownership as death. From 0.100.1 a
844
900
  Linux kernel without time namespaces (before 5.6) records the PID namespace alone
845
901
  instead of null, so its locks recover like any other. Existing
846
902
  `refuse` policy and the separately configured ownerless-file grace are unchanged.
847
-
848
- ## Released migration: 0.99.0
849
-
850
- ### structural alternatives in flattened tool schemas
851
-
852
- **Who must act:** applications using `flattenUnionInput: true` with schema
853
- walkers, snapshots or provider restrictions that assume there is no nested
854
- `anyOf`. Applications without that assumption need only update the dependency.
855
-
856
- Colliding object/array fields retain their complete structural alternatives:
857
-
858
- ```ts
859
- // Before: loses both field vocabulary and array items.
860
- { type: ['object', 'array'] }
861
- // After: structural alternatives live inside the property schema.
862
- { anyOf: [
863
- { type: 'array', items: {
864
- type: 'object', properties: { id: { type: 'string' } }, required: ['id'],
865
- } },
866
- { type: 'object', properties: { id: { type: 'string' } }, required: ['id'] },
867
- ] }
868
- ```
869
-
870
- Alternatives come in kind order, independent of variant order. The same shape
871
- covers every collision of different kinds, scalars included: `string` against
872
- `string[]` becomes `{ anyOf: [{ type: 'array', items: { type: 'string' } }, { type:
873
- 'string' }] }`. A nullable field keeps `null` in each alternative's `type`, and
874
- differing nested object unions remain nested `anyOf` inside the alternatives. Update
875
- snapshots to this order.
876
-
877
- Traverse each nested alternative when inspecting schemas and regenerate schema
878
- snapshots. Verify the actual manifest with the chosen provider. Keep flatten
879
- enabled when a top-level discriminated union needs to be advertised as an
880
- object; do not erase `items` or introduce consumer-specific schema patches.
881
- The root object, discriminator enum, requiredness hints and strict runtime
882
- contract validation are unchanged. See ADR 0205.
883
-
884
- ## Released migration: 0.98.0
885
-
886
- ### automatic stdin availability
887
-
888
- **Who must act:** CLI applications or scripts relying on automatic stdin routing
889
- from a producer that can take more than 250 ms to write its first byte.
890
-
891
- Automatic routing now probes for the first byte for 250 ms. An empty open pipe
892
- leaves the required field unset and normal validation reports its name. Once
893
- data arrives, the entire stream is read until EOF without a total deadline.
894
-
895
- ```sh
896
- # before: may now fail if the producer starts late
897
- slow-producer | myapp generate
898
- # after: provide the value explicitly after the producer completes
899
- myapp generate --prompt "$(slow-producer)"
900
- ```
901
-
902
- For large input that should stay in a pipe, supply a `CliConfig.stdin` reader
903
- that waits for the producer and EOF; this existing hook is not wrapped in the
904
- availability probe. A custom reader owns its limits and error handling.
905
- `-` remains literal data. TTY and empty EOF produce ordinary validation errors;
906
- explicit arguments skip stdin. JSONL stream/batch commands, MCP/serve and
907
- long-running handlers have no new deadline. See ADR 0203.
@@ -1,14 +1,75 @@
1
- # Upgrading 0.72.0 → 0.97.0 — stitchkit docs slice
1
+ # Upgrading 0.72.0 → 0.99.0 — stitchkit docs slice
2
2
 
3
3
  > Generated from the `docs/` tree by `bun run gen:llms`. The index of all slices is
4
4
  > `llms.txt` in this package.
5
5
 
6
- Contains: Guide: Upgrading, 0.72.0 → 0.97.0 (docs/guide/upgrading.md)
6
+ Contains: Guide: Upgrading, 0.72.0 → 0.99.0 (docs/guide/upgrading.md)
7
7
 
8
8
  ==============================================================================
9
- # Guide: Upgrading, 0.72.0 → 0.97.0 (docs/guide/upgrading.md)
9
+ # Guide: Upgrading, 0.72.0 → 0.99.0 (docs/guide/upgrading.md)
10
10
  ==============================================================================
11
11
 
12
+ ## Released migration: 0.99.0
13
+
14
+ ### structural alternatives in flattened tool schemas
15
+
16
+ **Who must act:** applications using `flattenUnionInput: true` with schema
17
+ walkers, snapshots or provider restrictions that assume there is no nested
18
+ `anyOf`. Applications without that assumption need only update the dependency.
19
+
20
+ Colliding object/array fields retain their complete structural alternatives:
21
+
22
+ ```ts
23
+ // Before: loses both field vocabulary and array items.
24
+ { type: ['object', 'array'] }
25
+ // After: structural alternatives live inside the property schema.
26
+ { anyOf: [
27
+ { type: 'array', items: {
28
+ type: 'object', properties: { id: { type: 'string' } }, required: ['id'],
29
+ } },
30
+ { type: 'object', properties: { id: { type: 'string' } }, required: ['id'] },
31
+ ] }
32
+ ```
33
+
34
+ Alternatives come in kind order, independent of variant order. The same shape
35
+ covers every collision of different kinds, scalars included: `string` against
36
+ `string[]` becomes `{ anyOf: [{ type: 'array', items: { type: 'string' } }, { type:
37
+ 'string' }] }`. A nullable field keeps `null` in each alternative's `type`, and
38
+ differing nested object unions remain nested `anyOf` inside the alternatives. Update
39
+ snapshots to this order.
40
+
41
+ Traverse each nested alternative when inspecting schemas and regenerate schema
42
+ snapshots. Verify the actual manifest with the chosen provider. Keep flatten
43
+ enabled when a top-level discriminated union needs to be advertised as an
44
+ object; do not erase `items` or introduce consumer-specific schema patches.
45
+ The root object, discriminator enum, requiredness hints and strict runtime
46
+ contract validation are unchanged. See ADR 0205.
47
+
48
+ ## Released migration: 0.98.0
49
+
50
+ ### automatic stdin availability
51
+
52
+ **Who must act:** CLI applications or scripts relying on automatic stdin routing
53
+ from a producer that can take more than 250 ms to write its first byte.
54
+
55
+ Automatic routing now probes for the first byte for 250 ms. An empty open pipe
56
+ leaves the required field unset and normal validation reports its name. Once
57
+ data arrives, the entire stream is read until EOF without a total deadline.
58
+
59
+ ```sh
60
+ # before: may now fail if the producer starts late
61
+ slow-producer | myapp generate
62
+ # after: provide the value explicitly after the producer completes
63
+ myapp generate --prompt "$(slow-producer)"
64
+ ```
65
+
66
+ For large input that should stay in a pipe, supply a `CliConfig.stdin` reader
67
+ that waits for the producer and EOF; this existing hook is not wrapped in the
68
+ availability probe. A custom reader owns its limits and error handling.
69
+ `-` remains literal data. TTY and empty EOF produce ordinary validation errors;
70
+ explicit arguments skip stdin. JSONL stream/batch commands, MCP/serve and
71
+ long-running handlers have no new deadline. See ADR 0203.
72
+
12
73
  ## Released migration: 0.97.0
13
74
 
14
75
  ### schedule delivery
@@ -11,8 +11,8 @@ 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.98.0 → 0.107.0: `llms/upgrading-0.98.0-0.107.0.txt`
15
- - 0.72.0 → 0.97.0: `llms/upgrading-0.72.0-0.97.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
16
  - 0.59.0 → 0.71.0: `llms/upgrading-0.59.0-0.71.0.txt`
17
17
  - 0.37.0 → 0.58.0: `llms/upgrading-0.37.0-0.58.0.txt`
18
18
 
@@ -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`,