stitchkit 0.108.5 → 0.109.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 (43) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/dist/browser/cancellation.d.ts +4 -5
  3. package/dist/browser/cancellation.d.ts.map +1 -1
  4. package/dist/browser/client-executors.d.ts.map +1 -1
  5. package/dist/browser/http.d.ts.map +1 -1
  6. package/dist/entrypoints/cli.js +1 -1
  7. package/dist/entrypoints/index.js +2 -2
  8. package/dist/entrypoints/react.js +1 -1
  9. package/dist/entrypoints/remote.js +2 -2
  10. package/dist/entrypoints/telegram.d.ts +2 -2
  11. package/dist/entrypoints/telegram.d.ts.map +1 -1
  12. package/dist/entrypoints/telegram.js +868 -137
  13. package/dist/entrypoints/testing.js +2 -2
  14. package/dist/entrypoints/tools.js +1 -1
  15. package/dist/{index-7jvwrjqa.js → index-00v1qz76.js} +20 -17
  16. package/dist/{index-05s01c94.js → index-5cw390s6.js} +96 -16
  17. package/dist/{index-p5kx5vqd.js → index-e32vy8jw.js} +22 -11
  18. package/dist/telegram/update-intake-work.d.ts +39 -0
  19. package/dist/telegram/update-intake-work.d.ts.map +1 -0
  20. package/dist/telegram/update-intake.d.ts +47 -9
  21. package/dist/telegram/update-intake.d.ts.map +1 -1
  22. package/dist/telegram/update-store-check.d.ts.map +1 -1
  23. package/dist/telegram/update-store-postgres.d.ts +2 -2
  24. package/dist/telegram/update-store-postgres.d.ts.map +1 -1
  25. package/dist/telegram/update-store-sqlite.d.ts +2 -2
  26. package/dist/telegram/update-store-sqlite.d.ts.map +1 -1
  27. package/dist/telegram/update-store.d.ts +52 -3
  28. package/dist/telegram/update-store.d.ts.map +1 -1
  29. package/dist/tools/cli/args-fields.d.ts +7 -1
  30. package/dist/tools/cli/args-fields.d.ts.map +1 -1
  31. package/dist/tools/cli/args.d.ts.map +1 -1
  32. package/llms/cli.txt +9 -1
  33. package/llms/stitchkit.txt +10 -3
  34. package/llms/telegram.txt +59 -8
  35. package/llms/{upgrading-0.100.0-0.108.0.txt → upgrading-0.101.0-0.109.0.txt} +70 -72
  36. package/llms/upgrading-0.37.0.txt +176 -0
  37. package/llms/{upgrading-0.37.0-0.58.0.txt → upgrading-0.44.0-0.60.0.txt} +229 -169
  38. package/llms/{upgrading-0.59.0-0.71.0.txt → upgrading-0.61.0-0.73.0.txt} +122 -229
  39. package/llms/{upgrading-0.72.0-0.99.0.txt → upgrading-0.74.0-0.100.0.txt} +72 -122
  40. package/llms/upgrading.txt +5 -4
  41. package/llms-full.txt +145 -12
  42. package/llms.txt +9 -8
  43. package/package.json +1 -1
@@ -1,14 +1,133 @@
1
- # Upgrading 0.59.0 → 0.71.0 — stitchkit docs slice
1
+ # Upgrading 0.61.0 → 0.73.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.59.0 → 0.71.0 (docs/guide/upgrading.md)
6
+ Contains: Guide: Upgrading, 0.61.0 → 0.73.0 (docs/guide/upgrading.md)
7
7
 
8
8
  ==============================================================================
9
- # Guide: Upgrading, 0.59.0 → 0.71.0 (docs/guide/upgrading.md)
9
+ # Guide: Upgrading, 0.61.0 → 0.73.0 (docs/guide/upgrading.md)
10
10
  ==============================================================================
11
11
 
12
+ ## Released migration: 0.73.0
13
+
14
+ Two breaking changes. Nothing you send to a server changed, and only one of these
15
+ can reach a call site the compiler already checks.
16
+
17
+ ### If you call `withOptions` from a call site that lost its types
18
+
19
+ `withOptions` takes one argument on an endpoint with no contract input, two when
20
+ there is one. Passing two to a no-input endpoint used to succeed and silently
21
+ drop the options: the request went out **uncancelled**, the caller still received
22
+ `REQUEST_ABORTED` — cancellation is decided from the signal it was handed, not
23
+ from what the transport did — and the server ran the operation to its own
24
+ deadline. Every symptom then points at the transport, and the investigation goes
25
+ there. It now throws a `TypeError` naming the endpoint and the correct shape.
26
+
27
+ ```ts
28
+ // before — the options were dropped and the request was never cancelled
29
+ api.ping.withOptions({}, { signal })
30
+
31
+ // after
32
+ api.ping.withOptions({ signal })
33
+ ```
34
+
35
+ A typed call site already refused the wrong arity, so this reaches only code that
36
+ does not have the types: a `Record<string, …>` view of the client, a hand-written
37
+ double, a dynamic dispatch. The guard counts arguments and never reads them, so a
38
+ method handed to a callback API behaves exactly as before.
39
+
40
+ ### If you match on `ApiError.message`
41
+
42
+ An `ApiError` that nothing explained used to carry `API Error: ${code}` — a
43
+ string that reads like an explanation while only restating the code, so a caller
44
+ could not tell an origin that explained a failure from one that said nothing. It
45
+ now says which it is.
46
+
47
+ ```ts
48
+ // before
49
+ err.message === 'API Error: INVALID_INPUT'
50
+
51
+ // after
52
+ err.message === 'INVALID_INPUT (no message supplied)' // better: match err.code
53
+ ```
54
+
55
+ Match `err.code` instead: it is the contract, and it did not change. An error
56
+ carrying a real message is untouched. An empty message now counts as no message,
57
+ for the same reason — an empty string is not an explanation either.
58
+
59
+ ### If a diagnostic journal lock file is already on disk
60
+
61
+ One operator step, and only on a host that has been **renamed** since its lock was written.
62
+
63
+ A lock written before this version carries no machine identity, so there is nothing to compare and
64
+ the host name is all that is left. If the name still matches, the lock reclaims exactly as it always
65
+ did and there is nothing to do. If the name has changed, the lock is `unattributable` and is refused
66
+ — the same refusal this release exists to end, except that this particular file predates the fix and
67
+ cannot be repaired from the inside. Delete it once:
68
+
69
+ ```sh
70
+ rm <journal path>.lock # only when the host was renamed since the file was written
71
+ ```
72
+
73
+ Locks written from this version on carry the machine identity, so the situation cannot recur. To see
74
+ which case you are in without guessing, read the refusal instead of the file:
75
+ `readDiagnosticJournalLockDiagnosis(err)` returns `attribution: 'unattributable'` for exactly this
76
+ one, and `'another-machine'` for a lock that genuinely belongs elsewhere and must **not** be deleted.
77
+
78
+ Two related fixes need no migration, but change what you will see. An error the
79
+ transport raised through `createHttpClient` now carries the transport's own text
80
+ in `message` and the original error as `cause`, where before it carried neither
81
+ and filed the text under `details.message` alone; if you followed the retry rule
82
+ in [the client guide](./client.md) — inspect the adapter's `cause`, retry only
83
+ when it proves dispatch did not happen — that rule was not executable on this
84
+ transport and now is. And the client guide now states which direction validation
85
+ runs: responses are checked against `output`, arguments are **not** checked
86
+ before being sent.
87
+
88
+ ## Released migration: 0.72.0
89
+
90
+ Nothing you *pass* changed. Both items are about types you read or build, and
91
+ both stop the compiler rather than surprising you at runtime.
92
+
93
+ ### If you read `perKey` off `BoundedAdmissionPolicy`
94
+
95
+ `perKey` is now a union: the flat ceiling it always was, or `{ maxKeys, limits }`
96
+ where the ceiling is resolved from the key. Declaring one is unchanged; reading
97
+ one needs a narrowing.
98
+
99
+ ```ts
100
+ // before
101
+ const ceiling = policy.perKey?.maxConcurrent
102
+
103
+ // after
104
+ const ceiling =
105
+ policy.perKey && !('limits' in policy.perKey) ? policy.perKey.maxConcurrent : undefined
106
+ ```
107
+
108
+ A shape carrying members of both branches — `{ maxKeys, maxConcurrent, limits }` —
109
+ is refused. **In 0.72.0 and 0.72.1 that refusal came only from
110
+ `createBoundedAdmission`**, not from `tsc`: an excess-property check against a
111
+ union admits any property some member declares, so the mixed shape typechecked
112
+ and threw when the admission was built. From **0.72.2** each branch declares the
113
+ other's members as `never`, so it is a compile error. Nothing about the runtime
114
+ changed — both branches always refused it — but on a path your tests do not
115
+ construct, the earlier versions would have told you in production.
116
+
117
+ ### If you build a snapshot or a status by hand
118
+
119
+ `CreditWindowSnapshot` gained `waiting` (producers parked in the new waiting
120
+ `acquire`) and `DiagnosticJournalStatus` gained `lock`. Both are produced by the
121
+ framework, so reading them is unaffected — but a test double stops compiling:
122
+
123
+ ```ts
124
+ // before
125
+ const snapshot: CreditWindowSnapshot = { state: 'open', capacityBytes: 100, /* … */ }
126
+
127
+ // after
128
+ const snapshot: CreditWindowSnapshot = { state: 'open', capacityBytes: 100, /* … */ waiting: 0 }
129
+ ```
130
+
12
131
  ## Released migration: 0.71.0
13
132
 
14
133
  The Agent coding tools. Two of the three changes are visible to the compiler;
@@ -914,229 +1033,3 @@ The refusals now say which of the two happened: a resource that was never
914
1033
  healthy is told it "is not healthy" and pointed at `required: false`; one that
915
1034
  was healthy and stopped is told it "lost readiness" and pointed at
916
1035
  `onResourceFailure`.
917
-
918
- ## Released migration: 0.60.0
919
-
920
- ### close() says what it achieved
921
-
922
- `AgentRuntime.close()` returned `Promise<void>` and its contract promised three
923
- things that cannot hold together. It now returns
924
- `{ settled, timedOut, remaining }`.
925
-
926
- Nothing breaks if you ignore the value:
927
-
928
- ```ts
929
- await runtime.close({ gracePeriodMs: 30_000, forceTimeoutMs: 5_000 })
930
- ```
931
-
932
- What DOES change is any code that relied on the old sentence "`close()` never
933
- returns while a run is still in flight" while naming a `forceTimeoutMs`. That
934
- was never true — the force budget exists precisely to stop waiting — so if your
935
- process exits straight after `close()`, read the result:
936
-
937
- ```ts
938
- const closed = await runtime.close({ gracePeriodMs: 30_000, forceTimeoutMs: 5_000 })
939
- if (!closed.settled) {
940
- // `closed.remaining` runs were still in flight. They stay recoverable
941
- // through `scanRecoverable`; close never marks them terminal on its own.
942
- logger.warn({ remaining: closed.remaining }, 'exiting with runs in flight')
943
- }
944
- ```
945
-
946
- If you want the old guarantee, omit `forceTimeoutMs`: that is the one
947
- combination in which `close()` cannot return with a run in flight — and its wait
948
- is unbounded, which is the trade.
949
-
950
- A TypeScript consumer that wrote `const done: void = await runtime.close()` is
951
- the only shape that stops compiling.
952
-
953
- ### one name per concept
954
-
955
- `AgentModelDeclaration` was a bare alias of `AgentModelDescriptor`, so the same
956
- type reached consumers under two exported names:
957
-
958
- ```ts
959
- // before
960
- defineModelRegistry<Record<string, AgentModelDeclaration>>({ … })
961
- // after
962
- defineModelRegistry<Record<string, AgentModelDescriptor>>({ … })
963
- ```
964
-
965
- A find-and-replace covers it; there is no behavioural change. Kept name is
966
- `AgentModelDescriptor`, the one paired with `AgentModelDescriptorSchema` and
967
- returned by `registry.descriptor()`.
968
-
969
- Two names that look like the same case and are **not** being merged:
970
- `SocketEventMap` aliases Socket.IO's `EventsMap` so a vendor's name stays out of
971
- our signatures, and `AgentRun.ownerId` holds the same value a runtime publishes
972
- as `runtimeEpoch` on its events. The latter is one identity in two roles — "who
973
- owns this run" and "which runtime emitted this event" — and both declarations
974
- now say so. Renaming either would make the fencing comparison read worse.
975
-
976
- ### a reachable public surface
977
-
978
- Three exports that the public API required and did not provide, plus one option
979
- it accepted and ignored.
980
-
981
- ```ts
982
- // before — identified by string, because the class was exported nowhere
983
- if (error instanceof Error && error.name === 'AgentRuntimeConflictError') …
984
- // after
985
- import { AgentRuntimeConflictError } from 'stitchkit/agent-runtime'
986
- if (error instanceof AgentRuntimeConflictError) …
987
- ```
988
-
989
- `ActivityTokenBrand` is exported, so `ActivityProjection` can be implemented by
990
- a test double. `STITCH_ERROR_STATUS` gained `APPLICATION_NOT_ACCEPTING` (503) —
991
- only an exhaustive `satisfies Record<StitchErrorCode, …>` map stops compiling,
992
- and the fix is one line. (`GRAMMY_WEBHOOK_NOT_ACCEPTING` joined it in 0.60.1,
993
- under the same rule — see ADR 0105. This section briefly said the opposite: that
994
- an adapter's code stays out of the registry. It does not, and leaving it out was
995
- the worse of the two, because `isStitchErrorCode` then answered `false` and the
996
- code reached the wire spelled stitchkit's way, past both `codeMap` and
997
- `unmappedCode`.)
998
-
999
- `application.shutdown()` no longer accepts `retryAfterSeconds`. Delete it from
1000
- the call — the kernel never read it. If you meant the HTTP `Retry-After` a
1001
- draining server sends, that lives on `managedServerResource({ retryAfterSeconds })`,
1002
- where it always did the work.
1003
-
1004
- ### one shutdown vocabulary
1005
-
1006
- ```ts
1007
- // before
1008
- await runtime.close({ drainTimeoutMs: 30_000, forceTimeoutMs: 5_000 })
1009
- // after
1010
- await runtime.close({ gracePeriodMs: 30_000, forceTimeoutMs: 5_000 })
1011
- ```
1012
-
1013
- The rename is mechanical. The behaviour change under it is not, and it is the
1014
- reason the rename waited: two combinations were traps.
1015
- `close({ drainTimeoutMs })` with no force budget aborted the runs and returned
1016
- **without waiting for them to settle** — so naming a budget gave a weaker
1017
- guarantee than naming none — and `close({ forceTimeoutMs })` with no drain
1018
- budget never read the force budget, leaving an unbounded wait. Both now behave
1019
- as their names say.
1020
-
1021
- > **Superseded.** This section once ended "and `close()` never returns while a
1022
- > run is in flight". That was never true with a force budget — the budget exists
1023
- > to stop waiting — and `close()` now returns `{ settled, timedOut, remaining }`
1024
- > instead of promising it. See *Released migration: 0.60.0 → close() says what
1025
- > it achieved*, above.
1026
-
1027
- If your shutdown path measured how long `close()` took, expect it to take
1028
- longer in exactly the case where it used to return early — that is the fix, not
1029
- a regression. Defaults stay per-surface: `ShutdownOptions.gracePeriodMs`
1030
- defaults to 30 seconds, and the runtime's omitted budget still means "abort
1031
- immediately", the behaviour `close()` has always had.
1032
-
1033
- ### unresolved attachments are omitted
1034
-
1035
- `history.unresolvedFile` defaulted to `text`, and the placeholder it produced
1036
- carried the storage reference:
1037
-
1038
- ```ts
1039
- // before — the provider received your object key
1040
- // "[attachment: s3://bucket/tenants/42/invoice.pdf]"
1041
- history: {}
1042
- // after — omitted entirely by default; ask for a placeholder explicitly
1043
- history: { unresolvedFile: 'text' } // "[attachment: invoice.pdf]"
1044
- ```
1045
-
1046
- Two things to check. If a prompt relied on the model seeing *something* where an
1047
- unresolved file was, set `unresolvedFile: 'text'` — behaviour otherwise changes
1048
- silently, since an omitted part produces no error. And if any stored transcript
1049
- or provider log contains the old placeholder, it contains your storage layout;
1050
- treat those as disclosed. The `error` policy still names the reference, because
1051
- it is thrown into your process rather than sent upstream.
1052
-
1053
- ### one bounded recoverable scan
1054
-
1055
- `AgentRuntimeStore` had two scans: a mandatory unbounded one and an optional
1056
- paged one. The runtime only ever called the optional one, so implementing the
1057
- interface as written produced a store that threw on its first `recover()`:
1058
-
1059
- ```ts
1060
- // before — the mandatory member was dead, the needed one was optional
1061
- {
1062
- scanRecoverable: () => loadEveryRecoverableSnapshot(),
1063
- scanRecoverablePage: ({ cursor, limit }) => page(cursor, limit),
1064
- }
1065
- // after — one member, the bounded signature the driver already used
1066
- { scanRecoverable: ({ cursor, limit }) => page(cursor, limit) }
1067
- ```
1068
-
1069
- Delete the unbounded implementation rather than porting it: loading every
1070
- recoverable conversation to start is the shape 0.59.0 and ADR 0101 moved away
1071
- from, and nothing calls it now. If you built on `createAgentRuntimeStore()` you
1072
- have nothing to do **if you only pass it to the runtime** — it implements the
1073
- bounded page for you from the same driver member. If you CALL it yourself, the
1074
- member changed shape: `scanRecoverable()` took no argument and returned
1075
- snapshots; it now takes `{ cursor?, limit }` — `limit` is required — and returns
1076
- one page of descriptors.
1077
- ```ts
1078
- // before: const stale = await store.scanRecoverable()
1079
- // after: const { items, nextCursor } = await store.scanRecoverable({ limit: 100 })
1080
- ```
1081
-
1082
- ### published application status
1083
-
1084
- `createApplicationHealthHandler` and `createApplicationOperationalHandlers` no
1085
- longer serialise the whole `ApplicationSnapshot`. They publish
1086
- `ApplicationStatusProjection` — the verdict plus resource counts:
1087
-
1088
- ```ts
1089
- // before — the response named every resource and its dependency edges
1090
- const { resources } = await fetch('/status').then((r) => r.json())
1091
- resources[0].dependsOn
1092
- // after — the topology is read in-process, where it always belonged
1093
- app.getSnapshot().resources[0].dependsOn
1094
- ```
1095
-
1096
- The consequence to check is not compilation — it is whatever already consumes
1097
- these routes. A dashboard that drew the dependency graph from `/status`, or an
1098
- alert keyed on `admission.pending`, goes blank rather than red: the fields are
1099
- absent, not zero. Both are available from `getSnapshot()`, so the fix is to read
1100
- them in the process that owns the application and publish them on a channel you
1101
- control. If a route was reachable from outside your network, treat the previous
1102
- payload as disclosed and rotate nothing but assume the topology is known.
1103
-
1104
- ## Released migration: 0.59.0
1105
-
1106
- ### Normalized agent runtime persistence
1107
-
1108
- `AgentRuntimeStoreDriver` no longer reads and rewrites a lifetime `AgentStoredState` JSON
1109
- aggregate. Migrate that row and its recoverable/archive projections once:
1110
-
1111
- 1. Copy `conversationId` and `version` into the bounded runtime head.
1112
- 2. Write every `AgentStoredState.runs[]` entry as one normalized run record.
1113
- 3. Write every `AgentStoredState.admissions[]` entry as one admission receipt and copy its
1114
- canonical input into that receipt.
1115
- 4. For terminal runs, retain the canonical assistant on the run record before deleting any old
1116
- history/archive rows.
1117
- 5. Replace the recoverable projection with an index over normalized run `state`.
1118
- 6. Cut the adapter over atomically; there is no compatibility driver or dual-write mode.
1119
-
1120
- ```ts
1121
- // before
1122
- createAgentRuntimeStore({
1123
- state: { load, compareAndSwap },
1124
- history: { load, loadById, apply },
1125
- scanRecoverable,
1126
- })
1127
-
1128
- // after
1129
- createAgentRuntimeStore({
1130
- head: { load, compareAndSwap },
1131
- runs: { load, loadByAssistantMessageId, loadMany, listActive, save },
1132
- admissions: { load, loadByInputMessageId, create },
1133
- history: { load, apply },
1134
- scanRecoverable,
1135
- })
1136
- ```
1137
-
1138
- `AgentRuntimeHeadSchema`, `AgentStoredRunSchema` and `AgentAdmissionReceiptSchema` replace
1139
- `AgentStoredStateSchema` and `AgentAdmissionIdentitySchema`. Run
1140
- `runAgentStoreConformance()` against the migrated adapter before switching production traffic.
1141
- If an application implements `AgentRuntimeStore` directly, its duplicate result must also include
1142
- the canonical `run` and the retained `assistant` for a terminal run.
@@ -1,14 +1,83 @@
1
- # Upgrading 0.72.0 → 0.99.0 — stitchkit docs slice
1
+ # Upgrading 0.74.0 → 0.100.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.99.0 (docs/guide/upgrading.md)
6
+ Contains: Guide: Upgrading, 0.74.0 → 0.100.0 (docs/guide/upgrading.md)
7
7
 
8
8
  ==============================================================================
9
- # Guide: Upgrading, 0.72.0 → 0.99.0 (docs/guide/upgrading.md)
9
+ # Guide: Upgrading, 0.74.0 → 0.100.0 (docs/guide/upgrading.md)
10
10
  ==============================================================================
11
11
 
12
+ ## Released migration: 0.100.0
13
+
14
+ ### watch capacity and session/live/delivery adoption
15
+
16
+ **Who must act:** watch consumers exceeding 1024 hub sources, 1024 hub subscribers,
17
+ 1024 client keys or 128 listeners per key. These previously unbounded collections
18
+ now refuse excess admission. Choose explicit positive finite limits, for example:
19
+
20
+ ```ts
21
+ // Before: no hub-wide capacity bounds.
22
+ createWatchHub({ read, watchable });
23
+ // After: explicitly budget a larger workload.
24
+ createWatchHub({ read, watchable, maxSources: 4096, maxSubscribers: 4096 });
25
+ // Client limits are configured independently.
26
+ createWatchClient({ ...clientConfig, maxKeys: 2048, maxListenersPerKey: 256 });
27
+ ```
28
+
29
+ The remaining steps apply only to applications opting into session lifetimes,
30
+ scoped watched reads, managed subscriptions or multi-action notification delivery.
31
+ Existing clients within the limits and single-send outboxes need no migration.
32
+
33
+ Replace local session generation counters with one `createSessionScope`; share it
34
+ with `createSessionCredentials`. Guard transport attempts, complete asynchronous
35
+ operations and final synchronous delivery. Route credential persistence through
36
+ one coordinator. Keep provider-specific error classification and bounded command
37
+ retry in the application. Coordination is per client instance.
38
+
39
+ Upgrade both watch endpoints before enabling session-scoped watches: strict older
40
+ servers reject the new optional `WatchKey.instance`. Server-verified scope controls
41
+ sharing; the client instance is only routing identity. Bind exact watched handles
42
+ to session-qualified cache keys. Existing delta/reconnect behavior stays in place.
43
+ For applications needing more than 1024 hub sources/subscribers or client keys, set
44
+ explicit finite capacity options. Enable a reconciliation interval when lost hints
45
+ must recover without a reconnect.
46
+
47
+ For multi-action outboxes, enqueue new items with a versioned delivery plan and
48
+ return small JSON receipts from `send`. Existing queued single-send items should
49
+ be drained before enabling a delivery executor on that queue, or migrated under
50
+ the store's atomic transition. When a new executor version ships, list the old
51
+ version's projection in `delivery.retiredVersions` until its queued items drain;
52
+ a plan version with no executor waits, reported to `onError` as
53
+ `NotificationPlanVersionError` (from 0.100.1).
54
+ Make projection idempotent. Provider idempotency/reconciliation remains necessary
55
+ for a crash between remote success and checkpoint. These APIs are available starting with 0.100.0.
56
+
57
+ ### exclusive lock recovery
58
+
59
+ New lock and reclaim-guard records include `owner.process`: kernel boot identity,
60
+ process birth token and namespace. `readDiagnosticJournalLockDiagnosis(error)`
61
+ adds `identity` to distinguish matched lifetime evidence, PID reuse, a different
62
+ boot, an absent PID, a legacy record or unavailable identity. Legacy `alive` only
63
+ means the PID is present; it does not prove that the recorded writer still exists.
64
+
65
+ **Who must act:** deployments recovering a legacy lock with a reused live PID,
66
+ or bundling file-lock consumers into a standalone Darwin executable. Legacy
67
+ records cannot safely infer their missing boot/start identity from `acquiredAt`.
68
+ Stop all writers to that resource, verify exclusive ownership, preserve the old
69
+ lock for incident evidence outside the active lock path, and restart with the
70
+ new package. Never delete a lock merely because it is old. New acquisitions
71
+ write the modern record; no journal data migration is needed.
72
+
73
+ Darwin distributions must retain the package's architecture-matched native
74
+ `.node` asset for process birth measurement, as for contained filesystem access.
75
+ If native/proc identity cannot be read, new locks explicitly record null and
76
+ recovery refuses rather than treating unknown ownership as death. From 0.100.1 a
77
+ Linux kernel without time namespaces (before 5.6) records the PID namespace alone
78
+ instead of null, so its locks recover like any other. Existing
79
+ `refuse` policy and the separately configured ownerless-file grace are unchanged.
80
+
12
81
  ## Released migration: 0.99.0
13
82
 
14
83
  ### structural alternatives in flattened tool schemas
@@ -1018,122 +1087,3 @@ Not a migration, but worth knowing: a missing multipart file used to be reported
1018
1087
  `UNKNOWN_ERROR`, the code that means *this client cannot tell you what happened*, on the one refusal
1019
1088
  where it is certain nothing was sent. → ADR 0148 carries the reasoning, including why argument
1020
1089
  validation stays on the server.
1021
-
1022
- ## Released migration: 0.73.0
1023
-
1024
- Two breaking changes. Nothing you send to a server changed, and only one of these
1025
- can reach a call site the compiler already checks.
1026
-
1027
- ### If you call `withOptions` from a call site that lost its types
1028
-
1029
- `withOptions` takes one argument on an endpoint with no contract input, two when
1030
- there is one. Passing two to a no-input endpoint used to succeed and silently
1031
- drop the options: the request went out **uncancelled**, the caller still received
1032
- `REQUEST_ABORTED` — cancellation is decided from the signal it was handed, not
1033
- from what the transport did — and the server ran the operation to its own
1034
- deadline. Every symptom then points at the transport, and the investigation goes
1035
- there. It now throws a `TypeError` naming the endpoint and the correct shape.
1036
-
1037
- ```ts
1038
- // before — the options were dropped and the request was never cancelled
1039
- api.ping.withOptions({}, { signal })
1040
-
1041
- // after
1042
- api.ping.withOptions({ signal })
1043
- ```
1044
-
1045
- A typed call site already refused the wrong arity, so this reaches only code that
1046
- does not have the types: a `Record<string, …>` view of the client, a hand-written
1047
- double, a dynamic dispatch. The guard counts arguments and never reads them, so a
1048
- method handed to a callback API behaves exactly as before.
1049
-
1050
- ### If you match on `ApiError.message`
1051
-
1052
- An `ApiError` that nothing explained used to carry `API Error: ${code}` — a
1053
- string that reads like an explanation while only restating the code, so a caller
1054
- could not tell an origin that explained a failure from one that said nothing. It
1055
- now says which it is.
1056
-
1057
- ```ts
1058
- // before
1059
- err.message === 'API Error: INVALID_INPUT'
1060
-
1061
- // after
1062
- err.message === 'INVALID_INPUT (no message supplied)' // better: match err.code
1063
- ```
1064
-
1065
- Match `err.code` instead: it is the contract, and it did not change. An error
1066
- carrying a real message is untouched. An empty message now counts as no message,
1067
- for the same reason — an empty string is not an explanation either.
1068
-
1069
- ### If a diagnostic journal lock file is already on disk
1070
-
1071
- One operator step, and only on a host that has been **renamed** since its lock was written.
1072
-
1073
- A lock written before this version carries no machine identity, so there is nothing to compare and
1074
- the host name is all that is left. If the name still matches, the lock reclaims exactly as it always
1075
- did and there is nothing to do. If the name has changed, the lock is `unattributable` and is refused
1076
- — the same refusal this release exists to end, except that this particular file predates the fix and
1077
- cannot be repaired from the inside. Delete it once:
1078
-
1079
- ```sh
1080
- rm <journal path>.lock # only when the host was renamed since the file was written
1081
- ```
1082
-
1083
- Locks written from this version on carry the machine identity, so the situation cannot recur. To see
1084
- which case you are in without guessing, read the refusal instead of the file:
1085
- `readDiagnosticJournalLockDiagnosis(err)` returns `attribution: 'unattributable'` for exactly this
1086
- one, and `'another-machine'` for a lock that genuinely belongs elsewhere and must **not** be deleted.
1087
-
1088
- Two related fixes need no migration, but change what you will see. An error the
1089
- transport raised through `createHttpClient` now carries the transport's own text
1090
- in `message` and the original error as `cause`, where before it carried neither
1091
- and filed the text under `details.message` alone; if you followed the retry rule
1092
- in [the client guide](./client.md) — inspect the adapter's `cause`, retry only
1093
- when it proves dispatch did not happen — that rule was not executable on this
1094
- transport and now is. And the client guide now states which direction validation
1095
- runs: responses are checked against `output`, arguments are **not** checked
1096
- before being sent.
1097
-
1098
- ## Released migration: 0.72.0
1099
-
1100
- Nothing you *pass* changed. Both items are about types you read or build, and
1101
- both stop the compiler rather than surprising you at runtime.
1102
-
1103
- ### If you read `perKey` off `BoundedAdmissionPolicy`
1104
-
1105
- `perKey` is now a union: the flat ceiling it always was, or `{ maxKeys, limits }`
1106
- where the ceiling is resolved from the key. Declaring one is unchanged; reading
1107
- one needs a narrowing.
1108
-
1109
- ```ts
1110
- // before
1111
- const ceiling = policy.perKey?.maxConcurrent
1112
-
1113
- // after
1114
- const ceiling =
1115
- policy.perKey && !('limits' in policy.perKey) ? policy.perKey.maxConcurrent : undefined
1116
- ```
1117
-
1118
- A shape carrying members of both branches — `{ maxKeys, maxConcurrent, limits }` —
1119
- is refused. **In 0.72.0 and 0.72.1 that refusal came only from
1120
- `createBoundedAdmission`**, not from `tsc`: an excess-property check against a
1121
- union admits any property some member declares, so the mixed shape typechecked
1122
- and threw when the admission was built. From **0.72.2** each branch declares the
1123
- other's members as `never`, so it is a compile error. Nothing about the runtime
1124
- changed — both branches always refused it — but on a path your tests do not
1125
- construct, the earlier versions would have told you in production.
1126
-
1127
- ### If you build a snapshot or a status by hand
1128
-
1129
- `CreditWindowSnapshot` gained `waiting` (producers parked in the new waiting
1130
- `acquire`) and `DiagnosticJournalStatus` gained `lock`. Both are produced by the
1131
- framework, so reading them is unaffected — but a test double stops compiling:
1132
-
1133
- ```ts
1134
- // before
1135
- const snapshot: CreditWindowSnapshot = { state: 'open', capacityBytes: 100, /* … */ }
1136
-
1137
- // after
1138
- const snapshot: CreditWindowSnapshot = { state: 'open', capacityBytes: 100, /* … */ waiting: 0 }
1139
- ```
@@ -11,10 +11,11 @@ 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.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`
14
+ - 0.101.0 → 0.109.0: `llms/upgrading-0.101.0-0.109.0.txt`
15
+ - 0.74.0 → 0.100.0: `llms/upgrading-0.74.0-0.100.0.txt`
16
+ - 0.61.0 → 0.73.0: `llms/upgrading-0.61.0-0.73.0.txt`
17
+ - 0.44.0 → 0.60.0: `llms/upgrading-0.44.0-0.60.0.txt`
18
+ - 0.37.0 → 0.37.0: `llms/upgrading-0.37.0.txt`
18
19
 
19
20
  # Upgrading stitchkit
20
21