stitchkit 0.115.0 → 0.116.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 +25 -0
  2. package/dist/agent-runtime/coding-tool-paths.d.ts.map +1 -1
  3. package/dist/entrypoints/agent-runtime/coding-tools.js +4 -4
  4. package/dist/entrypoints/agent-runtime/harness.js +2 -2
  5. package/dist/entrypoints/agent-runtime/sandbox.js +4 -4
  6. package/dist/entrypoints/agent-runtime/sqlite/bun.js +6 -6
  7. package/dist/entrypoints/agent-runtime/sqlite/node.js +6 -6
  8. package/dist/entrypoints/agent-runtime/testing.js +6 -6
  9. package/dist/entrypoints/agent-runtime.js +6 -6
  10. package/dist/entrypoints/cli.js +3 -3
  11. package/dist/entrypoints/telegram/grammy.js +2 -2
  12. package/dist/entrypoints/telegram.d.ts +1 -1
  13. package/dist/entrypoints/telegram.d.ts.map +1 -1
  14. package/dist/entrypoints/telegram.js +22 -9
  15. package/dist/entrypoints/tools.js +3 -3
  16. package/dist/{index-dx5cm9pc.js → index-4g6qmgdx.js} +1 -1
  17. package/dist/{index-nregbbsc.js → index-6f49krb5.js} +1 -1
  18. package/dist/{index-x0ddbj85.js → index-arjmb6de.js} +36 -1
  19. package/dist/{index-crmswbrt.js → index-fa435yq2.js} +1 -1
  20. package/dist/{index-944x38nv.js → index-jq2ky8fw.js} +1 -1
  21. package/dist/{index-7yx8j6ay.js → index-n5sv4z0w.js} +12 -3
  22. package/dist/{index-j8kf56wx.js → index-q968hy6y.js} +1 -1
  23. package/dist/{index-3qqdqwxe.js → index-w7jq3fz9.js} +25 -5
  24. package/dist/internal/contained-files.d.ts +10 -0
  25. package/dist/internal/contained-files.d.ts.map +1 -1
  26. package/dist/telegram/budget-transformer.d.ts +5 -2
  27. package/dist/telegram/budget-transformer.d.ts.map +1 -1
  28. package/dist/telegram/outbound-budget.d.ts +26 -11
  29. package/dist/telegram/outbound-budget.d.ts.map +1 -1
  30. package/dist/tools/cli/view.d.ts +7 -1
  31. package/dist/tools/cli/view.d.ts.map +1 -1
  32. package/llms/agent-runtime.txt +2 -2
  33. package/llms/cli.txt +3 -1
  34. package/llms/stitchkit.txt +3 -3
  35. package/llms/telegram.txt +10 -3
  36. package/llms/{upgrading-0.104.0-0.115.0.txt → upgrading-0.105.0-0.116.0.txt} +26 -124
  37. package/llms/{upgrading-0.44.0-0.61.0.txt → upgrading-0.44.0-0.62.0.txt} +120 -3
  38. package/llms/{upgrading-0.62.0-0.78.0.txt → upgrading-0.63.0-0.83.0.txt} +181 -120
  39. package/llms/{upgrading-0.79.0-0.103.13.txt → upgrading-0.84.0-0.104.0.txt} +124 -181
  40. package/llms/upgrading.txt +4 -4
  41. package/llms-full.txt +41 -9
  42. package/llms.txt +6 -6
  43. package/package.json +1 -1
@@ -1,14 +1,192 @@
1
- # Upgrading 0.62.0 → 0.78.0 — stitchkit docs slice
1
+ # Upgrading 0.63.0 → 0.83.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.62.0 → 0.78.0 (docs/guide/upgrading.md)
6
+ Contains: Guide: Upgrading, 0.63.0 → 0.83.0 (docs/guide/upgrading.md)
7
7
 
8
8
  ==============================================================================
9
- # Guide: Upgrading, 0.62.0 → 0.78.0 (docs/guide/upgrading.md)
9
+ # Guide: Upgrading, 0.63.0 → 0.83.0 (docs/guide/upgrading.md)
10
10
  ==============================================================================
11
11
 
12
+ ## Released migration: 0.83.0
13
+
14
+ Two things, both mechanical, and only if you touch an observability sink.
15
+
16
+ ```bash
17
+ rg -n "createObservability|createApplicationEventSink|createApplicationSnapshotSink|createAgentRuntimeEventSink|createAgentObservability"
18
+ ```
19
+
20
+ **1. `flush()` returns `Promise<boolean>`.** `await sink.flush()` needs no
21
+ change. Only a declared type does:
22
+
23
+ ```ts
24
+ // before
25
+ const flushAudit: () => Promise<void> = observability.flush
26
+ // after
27
+ const flushAudit: () => Promise<boolean> = observability.flush
28
+ ```
29
+
30
+ **2. `ObservabilityDrainReport` carries `drained`.** The framework builds it, so
31
+ reading code is unchanged. A hand-built report — a test double, or one persisted
32
+ and re-parsed through `ObservabilityDrainReportSchema` — needs the field:
33
+
34
+ ```ts
35
+ // before
36
+ { request, tools, total, durationMs }
37
+ // after
38
+ { request, tools, total, durationMs, drained: true }
39
+ ```
40
+
41
+ Then take the thing this release exists for. Every drain now accepts
42
+ `{ timeoutMs?, signal? }`, so a shutdown can give the audit drain the same
43
+ deadline it gives every other step:
44
+
45
+ ```ts
46
+ const report = await observability.close({ timeoutMs: 5_000 })
47
+ if (!report.drained) {
48
+ logger.warn('audit drain incomplete', {
49
+ unwritten: report.total.received - report.total.filtered - report.total.completed,
50
+ })
51
+ }
52
+ ```
53
+
54
+ The bound ends the **waiting**, not the writes: a sink's `write` is handed no
55
+ cancellation, so an outstanding one keeps running against whatever you close
56
+ next. Racing `close()` against your own timer looks equivalent and is not — it
57
+ discards the report along with the wait.
58
+
59
+ ## Released migration: 0.82.0
60
+
61
+ One thing, and only if you mount MCP or agent tools from a `createTrackingContract` contract.
62
+
63
+ ```bash
64
+ rg -n "createTrackingContract"
65
+ ```
66
+
67
+ The event ingest (`track`, `POST /events`) is now `expose: ['HTTP']`, which is
68
+ what its sibling `bootstrap` always declared. Before, it declared nothing — and
69
+ an endpoint with no `expose` is a tool on MCP and AGENT by default, so the
70
+ mounted surface carried a `track_<prefix>` tool. Since this contract is built
71
+ inside the framework, an application that had made every tool opt-in for the
72
+ endpoints *it* authors could not have opted this one out.
73
+
74
+ **If you pin a tool-surface digest or count**, it moves by one; re-record it.
75
+ **If you assert on tool names**, `track_<prefix>` is gone. **If nothing in your
76
+ project mounts tools from this contract**, there is nothing to do — the HTTP
77
+ routes, the client and the schemas are unchanged.
78
+
79
+ Un-declaring the tool was never the intent, so there is no option to keep it. If
80
+ an agent in your system genuinely needs to record something, give it your own
81
+ endpoint with your own schema: the browser ingest trusts a client-minted event
82
+ id and a visit lease, and an agent holds neither.
83
+
84
+ ## Released migration: 0.81.0
85
+
86
+ **Path literals own string params.**
87
+
88
+ An endpoint whose path contains `:id` or a terminal `*rest` no longer needs a
89
+ duplicate `params: z.object({ …string() })` declaration. Remove schemas that do
90
+ nothing beyond repeating path names:
91
+
92
+ ```ts
93
+ // before
94
+ { path: '/projects/:projectId/*filePath',
95
+ params: z.object({ projectId: z.string(), filePath: z.string() }) }
96
+
97
+ // after
98
+ { path: '/projects/:projectId/*filePath' }
99
+ ```
100
+
101
+ Keep an explicit schema when it validates or coerces. It must cover every name
102
+ in the path — an explicit schema that misses one now fails at `defineContract`
103
+ with the path and the missing field named. Client calls and handler contexts
104
+ infer the string fields from the literal, so a call that omitted one becomes a
105
+ compile error instead of building a URL with an unresolved segment. Measured
106
+ across three consuming applications before release (373 endpoints with path
107
+ segments), no explicit schema missed a field: the migration is deleting
108
+ schemas that only repeated the path, or nothing.
109
+
110
+ ## Released migration: 0.80.0
111
+
112
+ One thing, and only if you parse an async-operation snapshot.
113
+
114
+ ```bash
115
+ rg -n "createAsyncOperationSnapshotSchema"
116
+ ```
117
+
118
+ `progress` on a parsed snapshot used to be `unknown`. The factory built its
119
+ object shapes with a conditional spread, and the narrowing inside it widened
120
+ your schema back to a bare `ZodType`, so the emitted declaration threw the type
121
+ argument away. It is now the output of the schema you configured.
122
+
123
+ ```ts
124
+ const schema = createAsyncOperationSnapshotSchema({
125
+ progress: z.object({ done: z.number(), total: z.number() }),
126
+ failure: z.object({ code: z.string() }),
127
+ })
128
+ const snap = schema.parse(body)
129
+ if (snap.phase === 'running') {
130
+ // before: `snap.progress` was `unknown` — this needed a cast or a narrow
131
+ // after: it is `{ done: number; total: number } | undefined`
132
+ console.log(snap.progress?.done)
133
+ }
134
+ ```
135
+
136
+ **If you cast it**, delete the cast — it was working around the erased type.
137
+ **If you assigned something else to it**, the compiler now refuses, and it was
138
+ already a value the schema rejected at run time.
139
+
140
+ **If you pass explicit type arguments**, the no-progress form takes one now, not
141
+ two: the factory is two overloads. Pass none.
142
+
143
+ ```ts
144
+ // before: createAsyncOperationSnapshotSchema<undefined, typeof Failure>({ failure: Failure })
145
+ // after: createAsyncOperationSnapshotSchema({ failure: Failure })
146
+ ```
147
+
148
+ Nothing about parsing moves. A snapshot configured without `progress` still
149
+ strips an unexpected `progress` rather than refusing it — that behaviour is why
150
+ this is overloads and not one normalised key.
151
+
152
+ **If you compile with `skipLibCheck: false`**, this is also the release that
153
+ makes `stitchkit/tools/contract` compile at all: it carried five `TS2344`
154
+ errors, one per phase, from the same conditional spread.
155
+
156
+ ## Released migration: 0.79.0
157
+
158
+ One thing, and only if you assert on the whole snapshot.
159
+
160
+ ```bash
161
+ rg -n "getSnapshot\(\)|projectApplicationStatus|status\(\)" --glob '*.test.ts' --glob '*.spec.ts'
162
+ ```
163
+
164
+ `ApplicationSnapshot` and `ApplicationStatusProjection` each gained a
165
+ `restarting` field — ids in the snapshot, a count in the projection. A reader
166
+ that names the fields it wants is unaffected. A test that compares the whole
167
+ object with `toEqual` fails until it adds the field:
168
+
169
+ ```ts
170
+ // before
171
+ expect(body).toEqual({ id, lifecycle: 'ready', /* … */ resources: { total: 1, ready: 1, degraded: 0, failed: 0 } })
172
+ // after
173
+ expect(body).toEqual({ id, lifecycle: 'ready', /* … */ resources: { total: 1, ready: 1, degraded: 0, failed: 0 }, restarting: 0 })
174
+ ```
175
+
176
+ It is zero except while a restart is running, and then it names the subtree being
177
+ replaced. That is the point: a snapshot taken mid-restart used to be
178
+ indistinguishable from a resource that had failed on its own, so a dashboard read
179
+ a planned replacement as an outage.
180
+
181
+ **If you watch a hub across a restart**, nothing to change, but the behaviour is
182
+ different and better: closing a hub now tells its subscribers `unavailable` /
183
+ `source-unavailable` instead of dropping them, so a page stops showing a value
184
+ that will never update again.
185
+
186
+ **If you need a tool contract in a browser**, `stitchkit/tools/contract` now
187
+ carries the async-operation helpers, its schemas and the view-file input/output
188
+ without the runtime. `stitchkit/tools` still exports all of them.
189
+
12
190
  ## Released migration: 0.78.0
13
191
 
14
192
  Two mechanical edits. Find them both:
@@ -974,120 +1152,3 @@ const spent = checkpoints.at(-1)?.metrics.usage?.cost?.value
974
1152
  `AgentRun.usage` is written at every checkpoint and with the terminal record, so
975
1153
  a dropped observability event no longer loses the number, and a process that dies
976
1154
  mid-stream leaves behind what it had already spent.
977
-
978
- ## Released migration: 0.62.0
979
-
980
- Two groups of behaviour changes. Nothing moves an export — the surface is
981
- strictly additive — and every item changes what a running system reports or
982
- sends, which is what this heading is for. One of them changes a number you may
983
- already be billing against.
984
-
985
- ### A run reports what it spent
986
-
987
- Three changes to what the runtime says about cost and tokens. Nothing moves an
988
- export; all three change numbers a running system reports, and one of them
989
- changes a number you may already be billing against.
990
-
991
- #### Multi-step cost was under-reported and is now summed
992
-
993
- No code change is needed to get the fix — but check any predicate that reads
994
- `provenance`:
995
-
996
- ```ts
997
- // before — accepted a number that was one step's cost, not the run's
998
- if (usage.cost?.provenance === 'provider-reported') bill(usage.cost.value)
999
-
1000
- // after — a sum stitchkit performed says so
1001
- if (usage.cost && usage.cost.provenance !== 'unavailable') bill(usage.cost.value)
1002
- ```
1003
-
1004
- `'computed'` means stitchkit added up provider-reported parts. It is not a guess
1005
- — `'estimated'` is the word for that — but it is deliberately not
1006
- `'provider-reported'`, because that label is what a caller filters on when it
1007
- wants a figure it can bill against unchanged, and a sum is not one.
1008
-
1009
- Token totals moved with it. The AI SDK's `totalUsage` is a sum *it* performed
1010
- over per-step provider figures — not a run total any provider handed over — so
1011
- labelling it `provider-reported` was the same overstatement. **A run total on a
1012
- terminal event is always `computed`.** If you want a figure with the provider's
1013
- own word on it, read `step-finished`: each step carries what that call reported.
1014
-
1015
- #### `usage` is always present on a terminal event
1016
-
1017
- ```ts
1018
- // before — absent when the run ended before the provider's `finish`
1019
- const spent = event.usage?.cost?.value ?? 0 // silently 0 for a real spend
1020
-
1021
- // after — present, and it says what it does not know
1022
- event.usage?.cost?.provenance === 'unavailable' // we spent, and cannot say how much
1023
- ```
1024
-
1025
- Keep the optional chaining: one `AgentRunEvent` shape covers `run-started`,
1026
- `step-finished` and `run-terminal`, so `usage` stays optional on the type. The
1027
- guarantee is about terminal events, and a schema shared with `run-started`
1028
- cannot express it.
1029
-
1030
- **Do not read `unavailable` as zero.** A run aborted mid-stream has spent real
1031
- money that nobody has counted; a run that never reached the provider has not.
1032
- Both used to look the same and now do not.
1033
-
1034
- #### A losing executor reports its own spend
1035
-
1036
- An execution that loses the terminal compare-and-swap now emits an operator
1037
- `run-terminal` event, because it ran and it spent. If a sink treated those events
1038
- as "runs this process committed", that is no longer true — `AgentRuntimeResult.metrics`
1039
- is still `undefined` for a losing executor and remains the way to tell.
1040
-
1041
- The *delivery* `terminal` event is unchanged and still fires only for the winner,
1042
- so nothing delivers a turn twice.
1043
-
1044
- ### An interrupted answer stops passing as a finished one
1045
-
1046
- Two behaviour changes, one shared cause: a run ended by a newer input used to
1047
- leave its half-written answer in the conversation with no sign that it was cut
1048
- off, and the next request to the provider carried it as an ordinary assistant
1049
- turn.
1050
-
1051
- #### The projection marks an interrupted turn
1052
-
1053
- Nothing to change to adopt the fix — the default is the fixed behaviour. What to
1054
- check is whether the marker is the *right* form for your surface, and the
1055
- question that decides it is not "was the run interrupted" but **"did anyone see
1056
- what it produced"**.
1057
-
1058
- ```ts
1059
- // after — pick the form; the default is 'assistant-marked'
1060
- createAgentRuntime({ history: { interruptedAssistant: 'system-note' } })
1061
- ```
1062
-
1063
- - **The user pressed stop and the text was on their screen** — keep
1064
- `'assistant-marked'`. The assistant turn is the truthful record of what the
1065
- human read, and the model should stay consistent with it.
1066
- - **The partial never reached anyone** — a surface that sends nothing until the
1067
- run is done — prefer `'system-note'`. An assistant turn in provider history is
1068
- a commitment the model stays consistent with; a system line is context.
1069
-
1070
- If you pass `history.project` you own the projection outright and none of this
1071
- applies — but the same question does.
1072
-
1073
- #### A run ended by a newer input can now say so
1074
-
1075
- ```ts
1076
- // before
1077
- runs: { inputPolicy: 'interrupt' } // ends the run, keeps its partial answer
1078
-
1079
- // after — for a surface where a follow-up message invalidates the answer
1080
- // in flight rather than merely stopping it
1081
- runs: { inputPolicy: 'supersede' } // ends the run, discards its partial answer
1082
- ```
1083
-
1084
- `inputPolicy` also accepts `(input) => policy`, which is how one application
1085
- gives two conversation surfaces different rules without the runtime learning
1086
- which is which.
1087
-
1088
- A superseded run terminates with `terminalReason: 'superseded'`, state
1089
- `'superseded'` and an assistant message of status `'superseded'`. **If you switch
1090
- exhaustively on any of those enums, add the arm** — that is the part of this
1091
- release that can break a build rather than a behaviour. The record itself is
1092
- kept: it is excluded from the projection, not deleted, so an operator can still
1093
- see what was thrown away.
@@ -1,14 +1,135 @@
1
- # Upgrading 0.79.0 → 0.103.13 — stitchkit docs slice
1
+ # Upgrading 0.84.0 → 0.104.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.79.0 → 0.103.13 (docs/guide/upgrading.md)
6
+ Contains: Guide: Upgrading, 0.84.0 → 0.104.0 (docs/guide/upgrading.md)
7
7
 
8
8
  ==============================================================================
9
- # Guide: Upgrading, 0.79.0 → 0.103.13 (docs/guide/upgrading.md)
9
+ # Guide: Upgrading, 0.84.0 → 0.104.0 (docs/guide/upgrading.md)
10
10
  ==============================================================================
11
11
 
12
+ ## Released migration: 0.104.0
13
+
14
+ JS bundles carrying Darwin native capabilities also need the
15
+ [delivery migration first affected in 0.103.13](#released-migration-010313).
16
+ Consumers upgrading from 0.103.12 or earlier must apply it even if their TypeScript API is unchanged.
17
+
18
+ ### CLI runtime-tool construction
19
+
20
+ **Who must act:** callers writing a schema-dependent handler or MCP/AI `present` directly
21
+ inside an annotated `CliConfig.runtimeTools` or `CliInvokerConfig.runtimeTools` object.
22
+ Native `commands` and values returned by `defineRuntimeTool` / `createRuntimeToolFactory`
23
+ remain valid. Execution, auth, validation and the invocation result envelope keep their behavior.
24
+
25
+ Construct a CLI-only managed definition with the neutral schema-aware type before registration:
26
+
27
+ ```ts
28
+ import type { CliInvokerConfig, RuntimeToolExecutionWithOutput } from 'stitchkit/cli'
29
+ import { z } from 'zod'
30
+
31
+ const input = z.object({ text: z.string() })
32
+ const output = z.object({ size: z.number() })
33
+ // Before: handler receives the heterogeneous registry context.
34
+ const config: CliInvokerConfig = {
35
+ name: 'app',
36
+ runtimeTools: [{
37
+ name: 'measure', description: 'Measure text',
38
+ identity: { serviceName: 'text', action: 'measure', method: 'POST' },
39
+ transports: ['CLI'], input, output,
40
+ handler: ({ input }) => ({ size: String(input.text).length }),
41
+ }],
42
+ }
43
+ // After: handler input and output derive from the construction schemas.
44
+ const measure = {
45
+ name: 'measure', description: 'Measure text',
46
+ identity: { serviceName: 'text', action: 'measure', method: 'POST' },
47
+ transports: ['CLI'], input, output,
48
+ handler: ({ input }) => ({ size: input.text.length }),
49
+ } satisfies RuntimeToolExecutionWithOutput<typeof input, typeof output>
50
+ const nextConfig: CliInvokerConfig = { name: 'app', runtimeTools: [measure] }
51
+ ```
52
+
53
+ For a shared MCP/AI tool, construct the definition with `defineRuntimeTool` from
54
+ `stitchkit/tools`, including its typed SDK `present` callbacks, then pass the returned value
55
+ to `runtimeTools`. Install the corresponding SDK peers for that integration. A CLI-only
56
+ consumer needs `stitchkit`, `zod` and its ordinary Node type tooling.
57
+
58
+ `RuntimeToolExecution` names the heterogeneous registration contract. Call a registered
59
+ operation through `createCli` or `createCliInvoker`; its erased handler input is `never`
60
+ because the managed runner owns schema validation before execution. For a managed void
61
+ definition, use `RuntimeToolDefinitionWithoutOutput<typeof input>`.
62
+
63
+ ### MCP construction, state stores and delivery
64
+
65
+ **Who must act:** schema-dependent runtime tool literals written directly inside MCP mount
66
+ configuration, MCP presenters returning malformed content or metadata, custom `StateStore`
67
+ implementations, file-store consumers using retired lock
68
+ options, broadcast consumers reading `halt` or retrying unknown send outcomes, and consumers
69
+ that validate HTTP error envelopes with a closed schema. Applications collecting raw tool
70
+ causes from `console.error` must configure an internal tool hook sink.
71
+
72
+ MCP-only code can import the existing server factories from `stitchkit/tools/mcp` and
73
+ construct its schema-aware tool before heterogeneous registration:
74
+
75
+ ```ts
76
+ import { buildMcpServer, type RuntimeMcpToolDefinitionWithOutput } from 'stitchkit/tools/mcp'
77
+ import { z } from 'zod'
78
+ const input = z.object({ value: z.number() })
79
+ const output = z.object({ doubled: z.number() })
80
+ // Before: runtimeTools: [{ input, output, handler, present }]
81
+ const double = {
82
+ name: 'double', description: 'Double a number',
83
+ identity: { serviceName: 'numbers', action: 'double', method: 'POST' },
84
+ input, output,
85
+ handler: ({ input }) => ({ doubled: input.value * 2 }),
86
+ present: { mcp: ({ doubled }) => ({ content: [{ type: 'text', text: String(doubled) }] }) },
87
+ } satisfies RuntimeMcpToolDefinitionWithOutput<typeof input, typeof output>
88
+ const server = buildMcpServer({ serverInfo: { name: 'numbers', version: '1' }, services: [], runtimeTools: [double] })
89
+ ```
90
+
91
+ Full MCP/AI integrations keep `defineRuntimeTool` from `stitchkit/tools`; those typed values
92
+ remain accepted by the MCP leaf and CLI. The leaf requires the MCP SDK and Zod, with no AI peer.
93
+
94
+ `RuntimeMcpPresentation` preserves the official SDK's required `content` and typed `_meta`
95
+ fields. A presenter returning `{}` must return `{ content: [] }`; text blocks require string
96
+ text, and metadata must be an object. `structuredContent` and `isError` remain framework-owned.
97
+ This restores SDK checks that an indexed mapped type previously erased.
98
+
99
+ Unexpected tool causes remain in `hooks.onToolError` and the `error` field of
100
+ `hooks.afterToolCall`. Protocol stderr carries safe errors. MCP resolver failures use those
101
+ same observers without executing the handler or its lifecycle again. HTTP's default
102
+ unexpected-error logging retains its behavior.
103
+
104
+ A custom state adapter's `update` must invoke `transition(current, context)` while it still
105
+ owns the serialized transaction. `context.assertHeld()` checks the actual transaction or lock
106
+ generation and refuses after completion, rollback or ownership loss. A no-op guard cannot
107
+ satisfy this contract. Existing transition callbacks may keep one parameter. For the owning
108
+ file adapter, replace `{ staleLockMs, retryMs, lockTimeoutMs }` with `{ lockTimeoutMs }`:
109
+ the remaining budget controls acquisition waiting, never expiry of a live transaction.
110
+ Unknown legacy PID locks and historical temporary files are preserved; recover them only
111
+ after independently proving their owner is gone.
112
+
113
+ Broadcast consumers change `report.halt.reason` checks to the closed `halt.kind` union:
114
+ `retry-after`, `transient`, `permanent` or `ambiguous`. A custom MTProto adapter can classify
115
+ an acknowledged flood refusal as `{ kind: 'retry-after', retryAfterMs: wait }`; the exact
116
+ provider wait is respected. A wait over `maxRetryDelayMs` halts with the recipient pending.
117
+ Unknown write outcome becomes `uncertain`; restarting the same broadcast does not send it
118
+ again. A retry classification must certify that the attempt did not apply the external effect.
119
+
120
+ HTTP errors gain optional boolean `error.retryable`; framed contract streams use the same
121
+ field. Update closed consumer schemas before producers declaring it. `false` on 502 and
122
+ `true` on 409 remain explicit recommendations through the remote tool path. An absent field
123
+ retains prior status-derived defaults; the field does not change the HTTP client's retry policy.
124
+ The existing seventh `ApiError` constructor argument remains `ErrorOptions`; retryability is
125
+ the new eighth argument.
126
+
127
+ Caller cancellation now reaches `implementRemote`'s existing HTTP call. Applications bind
128
+ signals to `createCli` / `createCliInvoker` and cooperative handlers observe `ctx.signal`.
129
+ `REQUEST_ABORTED` is nonretryable and exits 130. A disconnected caller does not prove that
130
+ an origin ignoring cancellation or an external provider stopped; keep resource leases until
131
+ that work actually completes.
132
+
12
133
  ## Released migration: 0.103.13
13
134
 
14
135
  ### Darwin native assets in JS distributions
@@ -884,181 +1005,3 @@ The workspace root is not asked about — it is the boundary the tools already o
884
1005
  **3. The policy is called more often.** A path of depth N costs N+1 calls (`.`, then each ancestor,
885
1006
  then the leaf), outermost first, stopping at the first refusal. A policy that logs or meters will
886
1007
  see a different sequence, and will no longer see the leaf once an ancestor has refused.
887
-
888
- ## Released migration: 0.83.0
889
-
890
- Two things, both mechanical, and only if you touch an observability sink.
891
-
892
- ```bash
893
- rg -n "createObservability|createApplicationEventSink|createApplicationSnapshotSink|createAgentRuntimeEventSink|createAgentObservability"
894
- ```
895
-
896
- **1. `flush()` returns `Promise<boolean>`.** `await sink.flush()` needs no
897
- change. Only a declared type does:
898
-
899
- ```ts
900
- // before
901
- const flushAudit: () => Promise<void> = observability.flush
902
- // after
903
- const flushAudit: () => Promise<boolean> = observability.flush
904
- ```
905
-
906
- **2. `ObservabilityDrainReport` carries `drained`.** The framework builds it, so
907
- reading code is unchanged. A hand-built report — a test double, or one persisted
908
- and re-parsed through `ObservabilityDrainReportSchema` — needs the field:
909
-
910
- ```ts
911
- // before
912
- { request, tools, total, durationMs }
913
- // after
914
- { request, tools, total, durationMs, drained: true }
915
- ```
916
-
917
- Then take the thing this release exists for. Every drain now accepts
918
- `{ timeoutMs?, signal? }`, so a shutdown can give the audit drain the same
919
- deadline it gives every other step:
920
-
921
- ```ts
922
- const report = await observability.close({ timeoutMs: 5_000 })
923
- if (!report.drained) {
924
- logger.warn('audit drain incomplete', {
925
- unwritten: report.total.received - report.total.filtered - report.total.completed,
926
- })
927
- }
928
- ```
929
-
930
- The bound ends the **waiting**, not the writes: a sink's `write` is handed no
931
- cancellation, so an outstanding one keeps running against whatever you close
932
- next. Racing `close()` against your own timer looks equivalent and is not — it
933
- discards the report along with the wait.
934
-
935
- ## Released migration: 0.82.0
936
-
937
- One thing, and only if you mount MCP or agent tools from a `createTrackingContract` contract.
938
-
939
- ```bash
940
- rg -n "createTrackingContract"
941
- ```
942
-
943
- The event ingest (`track`, `POST /events`) is now `expose: ['HTTP']`, which is
944
- what its sibling `bootstrap` always declared. Before, it declared nothing — and
945
- an endpoint with no `expose` is a tool on MCP and AGENT by default, so the
946
- mounted surface carried a `track_<prefix>` tool. Since this contract is built
947
- inside the framework, an application that had made every tool opt-in for the
948
- endpoints *it* authors could not have opted this one out.
949
-
950
- **If you pin a tool-surface digest or count**, it moves by one; re-record it.
951
- **If you assert on tool names**, `track_<prefix>` is gone. **If nothing in your
952
- project mounts tools from this contract**, there is nothing to do — the HTTP
953
- routes, the client and the schemas are unchanged.
954
-
955
- Un-declaring the tool was never the intent, so there is no option to keep it. If
956
- an agent in your system genuinely needs to record something, give it your own
957
- endpoint with your own schema: the browser ingest trusts a client-minted event
958
- id and a visit lease, and an agent holds neither.
959
-
960
- ## Released migration: 0.81.0
961
-
962
- **Path literals own string params.**
963
-
964
- An endpoint whose path contains `:id` or a terminal `*rest` no longer needs a
965
- duplicate `params: z.object({ …string() })` declaration. Remove schemas that do
966
- nothing beyond repeating path names:
967
-
968
- ```ts
969
- // before
970
- { path: '/projects/:projectId/*filePath',
971
- params: z.object({ projectId: z.string(), filePath: z.string() }) }
972
-
973
- // after
974
- { path: '/projects/:projectId/*filePath' }
975
- ```
976
-
977
- Keep an explicit schema when it validates or coerces. It must cover every name
978
- in the path — an explicit schema that misses one now fails at `defineContract`
979
- with the path and the missing field named. Client calls and handler contexts
980
- infer the string fields from the literal, so a call that omitted one becomes a
981
- compile error instead of building a URL with an unresolved segment. Measured
982
- across three consuming applications before release (373 endpoints with path
983
- segments), no explicit schema missed a field: the migration is deleting
984
- schemas that only repeated the path, or nothing.
985
-
986
- ## Released migration: 0.80.0
987
-
988
- One thing, and only if you parse an async-operation snapshot.
989
-
990
- ```bash
991
- rg -n "createAsyncOperationSnapshotSchema"
992
- ```
993
-
994
- `progress` on a parsed snapshot used to be `unknown`. The factory built its
995
- object shapes with a conditional spread, and the narrowing inside it widened
996
- your schema back to a bare `ZodType`, so the emitted declaration threw the type
997
- argument away. It is now the output of the schema you configured.
998
-
999
- ```ts
1000
- const schema = createAsyncOperationSnapshotSchema({
1001
- progress: z.object({ done: z.number(), total: z.number() }),
1002
- failure: z.object({ code: z.string() }),
1003
- })
1004
- const snap = schema.parse(body)
1005
- if (snap.phase === 'running') {
1006
- // before: `snap.progress` was `unknown` — this needed a cast or a narrow
1007
- // after: it is `{ done: number; total: number } | undefined`
1008
- console.log(snap.progress?.done)
1009
- }
1010
- ```
1011
-
1012
- **If you cast it**, delete the cast — it was working around the erased type.
1013
- **If you assigned something else to it**, the compiler now refuses, and it was
1014
- already a value the schema rejected at run time.
1015
-
1016
- **If you pass explicit type arguments**, the no-progress form takes one now, not
1017
- two: the factory is two overloads. Pass none.
1018
-
1019
- ```ts
1020
- // before: createAsyncOperationSnapshotSchema<undefined, typeof Failure>({ failure: Failure })
1021
- // after: createAsyncOperationSnapshotSchema({ failure: Failure })
1022
- ```
1023
-
1024
- Nothing about parsing moves. A snapshot configured without `progress` still
1025
- strips an unexpected `progress` rather than refusing it — that behaviour is why
1026
- this is overloads and not one normalised key.
1027
-
1028
- **If you compile with `skipLibCheck: false`**, this is also the release that
1029
- makes `stitchkit/tools/contract` compile at all: it carried five `TS2344`
1030
- errors, one per phase, from the same conditional spread.
1031
-
1032
- ## Released migration: 0.79.0
1033
-
1034
- One thing, and only if you assert on the whole snapshot.
1035
-
1036
- ```bash
1037
- rg -n "getSnapshot\(\)|projectApplicationStatus|status\(\)" --glob '*.test.ts' --glob '*.spec.ts'
1038
- ```
1039
-
1040
- `ApplicationSnapshot` and `ApplicationStatusProjection` each gained a
1041
- `restarting` field — ids in the snapshot, a count in the projection. A reader
1042
- that names the fields it wants is unaffected. A test that compares the whole
1043
- object with `toEqual` fails until it adds the field:
1044
-
1045
- ```ts
1046
- // before
1047
- expect(body).toEqual({ id, lifecycle: 'ready', /* … */ resources: { total: 1, ready: 1, degraded: 0, failed: 0 } })
1048
- // after
1049
- expect(body).toEqual({ id, lifecycle: 'ready', /* … */ resources: { total: 1, ready: 1, degraded: 0, failed: 0 }, restarting: 0 })
1050
- ```
1051
-
1052
- It is zero except while a restart is running, and then it names the subtree being
1053
- replaced. That is the point: a snapshot taken mid-restart used to be
1054
- indistinguishable from a resource that had failed on its own, so a dashboard read
1055
- a planned replacement as an outage.
1056
-
1057
- **If you watch a hub across a restart**, nothing to change, but the behaviour is
1058
- different and better: closing a hub now tells its subscribers `unavailable` /
1059
- `source-unavailable` instead of dropping them, so a page stops showing a value
1060
- that will never update again.
1061
-
1062
- **If you need a tool contract in a browser**, `stitchkit/tools/contract` now
1063
- carries the async-operation helpers, its schemas and the view-file input/output
1064
- without the runtime. `stitchkit/tools` still exports all of them.
@@ -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.104.0 → 0.115.0: `llms/upgrading-0.104.0-0.115.0.txt`
15
- - 0.79.0 → 0.103.13: `llms/upgrading-0.79.0-0.103.13.txt`
16
- - 0.62.0 → 0.78.0: `llms/upgrading-0.62.0-0.78.0.txt`
17
- - 0.44.0 → 0.61.0: `llms/upgrading-0.44.0-0.61.0.txt`
14
+ - 0.105.0 → 0.116.0: `llms/upgrading-0.105.0-0.116.0.txt`
15
+ - 0.84.0 → 0.104.0: `llms/upgrading-0.84.0-0.104.0.txt`
16
+ - 0.63.0 → 0.83.0: `llms/upgrading-0.63.0-0.83.0.txt`
17
+ - 0.44.0 → 0.62.0: `llms/upgrading-0.44.0-0.62.0.txt`
18
18
  - 0.37.0 → 0.44.0: `llms/upgrading-0.37.0-0.44.0.txt`
19
19
 
20
20
  # Upgrading stitchkit