stitchkit 0.108.4 → 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.
- package/CHANGELOG.md +48 -0
- package/dist/browser/cancellation.d.ts +4 -5
- package/dist/browser/cancellation.d.ts.map +1 -1
- package/dist/browser/client-executors.d.ts.map +1 -1
- package/dist/browser/http.d.ts.map +1 -1
- package/dist/entrypoints/agent-runtime/harness.js +4 -3
- package/dist/entrypoints/agent-runtime.js +7 -6
- package/dist/entrypoints/cli.js +6 -5
- package/dist/entrypoints/index.js +2 -2
- package/dist/entrypoints/react.js +1 -1
- package/dist/entrypoints/remote.js +2 -2
- package/dist/entrypoints/telegram.d.ts +2 -2
- package/dist/entrypoints/telegram.d.ts.map +1 -1
- package/dist/entrypoints/telegram.js +868 -137
- package/dist/entrypoints/testing.js +2 -2
- package/dist/entrypoints/tools/connections.js +6 -1
- package/dist/entrypoints/tools/contract.js +2 -1
- package/dist/entrypoints/tools/invoker.js +5 -4
- package/dist/entrypoints/tools/mcp.js +6 -5
- package/dist/entrypoints/tools.js +9 -8
- package/dist/{index-7jvwrjqa.js → index-00v1qz76.js} +20 -17
- package/dist/{index-cx2nh595.js → index-3wm2xp2a.js} +1 -1
- package/dist/{index-fgq4w9bf.js → index-5cw390s6.js} +100 -20
- package/dist/{index-h7qms8f9.js → index-68y3n9j3.js} +3 -3
- package/dist/{index-6yxhe58g.js → index-cykxpxz5.js} +7 -0
- package/dist/{index-p5kx5vqd.js → index-e32vy8jw.js} +22 -11
- package/dist/{index-dm2fv45z.js → index-k4v2kq4q.js} +1 -1
- package/dist/{index-6y9t2fwz.js → index-kvvpw1kq.js} +2 -2
- package/dist/{index-2s6fpehp.js → index-ryv32myz.js} +1 -1
- package/dist/index-t5sjh517.js +100 -0
- package/dist/{index-yz8x0hbx.js → index-tpk71wqn.js} +1 -1
- package/dist/{index-mep4sb48.js → index-x5xs71dw.js} +3 -3
- package/dist/telegram/update-intake-work.d.ts +39 -0
- package/dist/telegram/update-intake-work.d.ts.map +1 -0
- package/dist/telegram/update-intake.d.ts +47 -9
- package/dist/telegram/update-intake.d.ts.map +1 -1
- package/dist/telegram/update-store-check.d.ts.map +1 -1
- package/dist/telegram/update-store-postgres.d.ts +2 -2
- package/dist/telegram/update-store-postgres.d.ts.map +1 -1
- package/dist/telegram/update-store-sqlite.d.ts +2 -2
- package/dist/telegram/update-store-sqlite.d.ts.map +1 -1
- package/dist/telegram/update-store.d.ts +52 -3
- package/dist/telegram/update-store.d.ts.map +1 -1
- package/dist/tools/cli/args-fields.d.ts +7 -1
- package/dist/tools/cli/args-fields.d.ts.map +1 -1
- package/dist/tools/cli/args.d.ts.map +1 -1
- package/dist/tools/connections/runtime.d.ts.map +1 -1
- package/dist/tools/schema/coerce.d.ts.map +1 -1
- package/dist/tools/schema/json-schema-origin.d.ts +14 -0
- package/dist/tools/schema/json-schema-origin.d.ts.map +1 -0
- package/llms/cli.2.txt +1 -1
- package/llms/cli.txt +12 -0
- package/llms/stitchkit.txt +10 -3
- package/llms/telegram.txt +59 -8
- package/llms/tools.3.txt +1 -1
- package/llms/{upgrading-0.100.0-0.108.0.txt → upgrading-0.101.0-0.109.0.txt} +70 -72
- package/llms/upgrading-0.37.0.txt +176 -0
- package/llms/{upgrading-0.37.0-0.58.0.txt → upgrading-0.44.0-0.60.0.txt} +229 -169
- package/llms/{upgrading-0.59.0-0.71.0.txt → upgrading-0.61.0-0.73.0.txt} +122 -229
- package/llms/{upgrading-0.72.0-0.99.0.txt → upgrading-0.74.0-0.100.0.txt} +72 -122
- package/llms/upgrading.txt +5 -4
- package/llms-full.txt +150 -13
- package/llms.txt +9 -8
- package/package.json +1 -1
package/llms/telegram.txt
CHANGED
|
@@ -337,9 +337,10 @@ import { Database } from 'bun:sqlite'
|
|
|
337
337
|
import type { Update } from 'grammy/types'
|
|
338
338
|
|
|
339
339
|
const webhook = { token: env.BOT_TOKEN, url: env.WEBHOOK_URL, secret: env.WEBHOOK_SECRET }
|
|
340
|
+
const updateStore = sqliteTelegramUpdateStore({ database: new Database(env.UPDATES_DB) })
|
|
340
341
|
|
|
341
342
|
const intake = createTelegramUpdateIntake<Update>({
|
|
342
|
-
store:
|
|
343
|
+
store: updateStore,
|
|
343
344
|
handle: (update) => bot.handleUpdate(update),
|
|
344
345
|
onFailure: (failure) => logger.warn('Update failed', { ...failure }),
|
|
345
346
|
})
|
|
@@ -383,13 +384,55 @@ fix (a refused message, a blocked user) is abandoned; `maxAttempts` (5) ends
|
|
|
383
384
|
the rest. Delivery is at least once: a handler that finished but could not be
|
|
384
385
|
recorded as finished runs again.
|
|
385
386
|
|
|
387
|
+
Use `handleAttempt` when the handler needs the immutable attempt fence and an
|
|
388
|
+
ownership-loss signal. A signal asks slow work to stop; the fence is what
|
|
389
|
+
authorizes a domain write. Check it through a transaction-bound reference store
|
|
390
|
+
inside the same database transaction as that write:
|
|
391
|
+
|
|
392
|
+
```ts
|
|
393
|
+
handleAttempt: async (update, { fence, ownerLost }) => {
|
|
394
|
+
ownerLost.throwIfAborted()
|
|
395
|
+
await database.transaction(async (tx) => {
|
|
396
|
+
const transactionStore = postgresTelegramUpdateStore({
|
|
397
|
+
query: (text, parameters) => tx.query(text, parameters),
|
|
398
|
+
createTable: false,
|
|
399
|
+
})
|
|
400
|
+
if (!(await transactionStore.owns(fence, Date.now()))) return
|
|
401
|
+
await applyDomainTransition(tx, update)
|
|
402
|
+
})
|
|
403
|
+
}
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
The fence contains the claim's own `{ updateId, attempt, claimId }`. The opaque
|
|
407
|
+
`claimId` is persisted and never reused, so an old handler cannot adopt either
|
|
408
|
+
a newer attempt or a pruned and recreated `updateId`. Existing `handle(update)`
|
|
409
|
+
remains unchanged, including direct functions whose own optional second
|
|
410
|
+
argument has a different meaning.
|
|
411
|
+
|
|
412
|
+
For a final obligation, configure `handleExhaustion`. The reference store keeps
|
|
413
|
+
the terminal record unacknowledged until this handler resolves, including
|
|
414
|
+
across restart; a throw leaves it for the next sweep. Delivery is at least once,
|
|
415
|
+
so use the complete `{ updateId, attempt, claimId }` as an idempotency key in
|
|
416
|
+
the consumer database:
|
|
417
|
+
|
|
418
|
+
```ts
|
|
419
|
+
handleExhaustion: ({ updateId, attempt, claimId, update, error }) =>
|
|
420
|
+
recordFinalFailureOnce({ key: `telegram:${updateId}:${attempt}:${claimId}`, update, error })
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
This reuses the update row and table. Without `handleExhaustion`, terminal
|
|
424
|
+
abandonment retains its previous behavior.
|
|
425
|
+
|
|
386
426
|
With grammY the handler is `bot.handleUpdate`, which wraps a middleware's error
|
|
387
427
|
in a `BotError`; the intake unwraps it, so `retry`, `onFailure` and the error
|
|
388
428
|
stored with the update are what the middleware threw — a `retry` that returns
|
|
389
429
|
`false` for the bot's own terminal error sees that error.
|
|
390
430
|
|
|
391
|
-
The store
|
|
392
|
-
|
|
431
|
+
The base store remains the six atomic steps of `TelegramUpdateStore`. A
|
|
432
|
+
`TelegramUpdateFencedStore` adds `claimOwned`, `renewOwned`, `owns` and
|
|
433
|
+
`settleOwned`; `TelegramUpdateDurableStore` adds the final-obligation methods.
|
|
434
|
+
Reference stores implement both, so the bot's own database holds the fence and
|
|
435
|
+
final obligation without a second queue:
|
|
393
436
|
|
|
394
437
|
| Store | Takes |
|
|
395
438
|
|---|---|
|
|
@@ -416,7 +459,12 @@ postgresTelegramUpdateStore({
|
|
|
416
459
|
Each rule is one conditional statement, so a claim is atomic across every
|
|
417
460
|
process sharing the table. By default the table is created on first use; where
|
|
418
461
|
migrations own the schema, `postgresTelegramUpdateStoreSchema(table)` is the
|
|
419
|
-
statement to put in one.
|
|
462
|
+
statement to put in one. Apply it before using `handleAttempt` or
|
|
463
|
+
`handleExhaustion`; it adds `claim_id` idempotently. Legacy `handle(update)`
|
|
464
|
+
uses base PostgreSQL operations that do not reference that column. SQLite adds
|
|
465
|
+
the column automatically and tolerates two rolling processes racing the
|
|
466
|
+
migration. A direct base-store claim with `durableExhaustion: true` also opts
|
|
467
|
+
into the column-backed durable capability.
|
|
420
468
|
|
|
421
469
|
A store of your own — over an ORM, another database — is put through the same
|
|
422
470
|
rules the shipped ones are, concurrent claims included:
|
|
@@ -506,13 +554,16 @@ injected grammY bot. → ADR 0143, ADR 0201 — [guide](../guide/telegram.md)
|
|
|
506
554
|
| `telegramWebhookUrl` | function | the address with an `owner` tag derived from the secret: equal for one secret, different for another, the path untouched |
|
|
507
555
|
| `checkTelegramWebhook` / `TelegramWebhookState` | function / _type_ | whether the webhook still points here: `owned`, `ownerHost`, `pendingUpdates`, Telegram's `lastError` |
|
|
508
556
|
| `receiveTelegramWebhook` / `ReceiveTelegramWebhookOptions` / `TelegramUpdateAcceptance` | function / _type_ | one webhook `Request`: 403 without the secret (compared in constant time), 405 for another method, 400 for no update, 200 once `accept` recorded it |
|
|
509
|
-
| `createTelegramUpdateIntake` | function | webhook updates recorded before Telegram is answered and handled after: chat order kept, other chats alongside up to `maxConcurrent`, a renewed lease per attempt, a sweep that takes over what a restart or a dead process left, retries, `maxAttempts`, finished updates remembered `retainMs` to refuse repeats; at least once |
|
|
510
|
-
| `TelegramUpdateIntakeConfig` / `TelegramUpdateIntake` / `TelegramUpdateEnvelope` / `TelegramUpdateFailure` / `TelegramUpdateStoreStep` | _type_ | `store`, `handle` (a grammY `BotError` is unwrapped
|
|
511
|
-
| `
|
|
557
|
+
| `createTelegramUpdateIntake` | function | webhook updates recorded before Telegram is answered and handled after: chat order kept, other chats alongside up to `maxConcurrent`, a renewed lease and immutable fence per attempt, a sweep that takes over what a restart or a dead process left, retries, `maxAttempts`, optional recoverable exhaustion delivery, finished updates remembered `retainMs` to refuse repeats; at least once |
|
|
558
|
+
| `TelegramUpdateIntakeConfig` / `TelegramUpdateAttemptIntakeConfig` / `TelegramUpdateIntakeOptions` / `TelegramUpdateIntake` / `TelegramUpdateEnvelope` / `TelegramUpdateFailure` / `TelegramUpdateStoreStep` | _type_ | `store`, unchanged `handle(update)` or attempt-aware `handleAttempt(update, context)` (a grammY `BotError` is unwrapped), shared `retry`, `maxAttempts` (5), `leaseMs` (120 000), `pendingGraceMs` (60 000), `sweepEveryMs` (30 000), `retainMs` (24 h), `maxConcurrent` (32), `onFailure`, durable `handleExhaustion`, `onExhaustionFailure`, `onStoreError`, `now`; `accept(body)`, `start()`, `sweep()`, `idle()`, `close()` |
|
|
559
|
+
| `TelegramUpdateAttemptContext` / `TelegramUpdateAttemptIdentity` / `TelegramUpdateOwnershipLostError` | _type_ / class | immutable `{ updateId, attempt, claimId }` fence plus `ownerLost`; the persisted opaque claim token prevents a stale capability from matching after prune/re-add, while a store check in the consumer's DB transaction fences a domain write |
|
|
560
|
+
| `TelegramUpdateExhaustion` / `TelegramUpdateExhaustionFailure` / `StoredTelegramUpdateExhaustion` / `TelegramUpdateExhaustionQuery` | _type_ | a terminal update/attempt/error retained in the update store until `handleExhaustion` resolves; ordered `{ limit, afterUpdateId? }` pages prevent a failing first batch from starving later rows; delivery failures are observational and leave it due |
|
|
561
|
+
| `TelegramUpdateStore` / `StoredTelegramUpdate` / `TelegramUpdateState` / `TelegramUpdateSettlement` / `TelegramUpdateClaimOptions` / `TelegramUpdateDueQuery` | _type_ | the base store an intake runs on: `add`, `claim`, `renew`, `settle`, `due`, `prune`, each atomic on one record — a claim takes `pending`, a due `failed` or a lapsed `processing`; renew and settle only by the attempt holding it |
|
|
562
|
+
| `TelegramUpdateFencedStore` / `TelegramUpdateDurableStore` | _type_ | reference-store capabilities: `claimOwned` creates and returns the persisted identity, `renewOwned`, `owns(identity, at)` and `settleOwned(identity, settlement)` act only on that exact unexpired claim; `exhaust`, `dueExhaustions`, `acknowledgeExhaustion` keep a final consumer obligation in the same row until exact acknowledgement |
|
|
512
563
|
| `memoryTelegramUpdateStore` | function | the store in this process's memory — tests, or a bot that accepts losing unhandled updates to a restart |
|
|
513
564
|
| `sqliteTelegramUpdateStore` / `SqliteTelegramUpdateStoreConfig` / `TelegramSqliteDatabase` | function / _type_ | the store in SQLite over the handle the application opened — `bun:sqlite` `Database` or `node:sqlite` `DatabaseSync`; `table` (default `telegram_updates`) created when missing |
|
|
514
565
|
| `postgresTelegramUpdateStore` / `PostgresTelegramUpdateStoreConfig` / `TelegramPostgresQuery` | function / _type_ | the store in Postgres over the client the application holds, as one `query(text, parameters)` returning rows — Bun `sql.unsafe`, Prisma `$queryRawUnsafe`, `pg` `pool.query`; each rule one conditional statement; `table`, `createTable` (default `true`) |
|
|
515
|
-
| `postgresTelegramUpdateStoreSchema` | function | the table and
|
|
566
|
+
| `postgresTelegramUpdateStoreSchema` | function | the table, idempotent `claim_id` upgrade and index as a statement for a migration, safe to run twice; required before fenced methods when `createTable: false`, while legacy base methods remain compatible with the prior table |
|
|
516
567
|
| `checkTelegramUpdateStore` | function | put any store through every rule, each on a fresh store from `make`, concurrent claims included; resolves with the violations, empty when it keeps them |
|
|
517
568
|
|
|
518
569
|
---
|
package/llms/tools.3.txt
CHANGED
|
@@ -279,7 +279,7 @@ Advanced building blocks — the shared machinery the mounts are built on.
|
|
|
279
279
|
| `TransportCounts` | _type_ | per-transport counts (`{ HTTP, MCP, AGENT, CLI }`) |
|
|
280
280
|
| `ToolSurfaceProjection` | _type_ | the one readonly `{ services?, runtimeTools? }` container shared by full, MCP-only and introspection surfaces (`buildToolManifest`, `listToolNames`, `summarizeTransports` take `ToolSurfaceProjection<RuntimeToolDefinition>`) |
|
|
281
281
|
| `ToolManifestConfig` | _type_ | mixed surface plus required model-facing `transport` and presentation options |
|
|
282
|
-
| `coerceJsonArgs` | function | coerce JSON-stringified array/object tool arguments; throws a `ZodError` naming the field when a union that keeps a string member receives a string that parses to an array its array member accepts |
|
|
282
|
+
| `coerceJsonArgs` | function | coerce JSON-stringified array/object tool arguments, including object guards restored from JSON Schema; arbitrary transform pipes stay untouched; throws a `ZodError` naming the field when a union that keeps a string member receives a string that parses to an array its array member accepts |
|
|
283
283
|
| `flattenToolJsonSchema` | function | project structurally identifiable discriminated unions into conservative object joins; scalar collisions retain provable types, object/array collisions retain structural alternatives, and field descriptions retain discriminator labels plus availability/requiredness hints; the projection never executes validation |
|
|
284
284
|
| `ToolPresentationSchema` | _type_ | immutable model-facing JSON Schema document shared by tool transports |
|
|
285
285
|
| `MountableTool` | _type_ | one operation with separate executable CLI argument schema and model-facing presentation schema |
|
|
@@ -1,14 +1,81 @@
|
|
|
1
|
-
# Upgrading 0.
|
|
1
|
+
# Upgrading 0.101.0 → 0.109.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.
|
|
6
|
+
Contains: Guide: Upgrading, 0.101.0 → 0.109.0 (docs/guide/upgrading.md)
|
|
7
7
|
|
|
8
8
|
==============================================================================
|
|
9
|
-
# Guide: Upgrading, 0.
|
|
9
|
+
# Guide: Upgrading, 0.101.0 → 0.109.0 (docs/guide/upgrading.md)
|
|
10
10
|
==============================================================================
|
|
11
11
|
|
|
12
|
+
## Released migration: 0.109.0
|
|
13
|
+
|
|
14
|
+
### free CLI record leaves are lexical
|
|
15
|
+
|
|
16
|
+
**Who must act:** scripts that pass dotted values into a free `record<string, unknown>` and rely on
|
|
17
|
+
stitchkit guessing numbers or booleans from their spelling. Declared object leaves and whole-object
|
|
18
|
+
JSON keep their typed behavior.
|
|
19
|
+
|
|
20
|
+
Free record values have no schema type to justify coercion. They now preserve argv text, so values
|
|
21
|
+
such as `007`, `1e3`, `true` and `false` arrive exactly as written.
|
|
22
|
+
See [ADR 0257](../decisions/0257-cli-dotted-values-follow-their-declared-schema.md).
|
|
23
|
+
|
|
24
|
+
1. Pass typed free-record values as one JSON object:
|
|
25
|
+
|
|
26
|
+
```sh
|
|
27
|
+
# before: produced { count: 5 } by spelling-based inference
|
|
28
|
+
app command --data.count=5
|
|
29
|
+
# after: JSON says explicitly that 5 is a number
|
|
30
|
+
app command --data='{"count":5}'
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
2. If dotted flags are the intended interface, declare their object shape; a `z.number()` or
|
|
34
|
+
`z.boolean()` leaf still coerces and validates its own CLI value.
|
|
35
|
+
|
|
36
|
+
### Opting into Telegram attempt fencing
|
|
37
|
+
|
|
38
|
+
Existing `handle(update)` callbacks and the six-method `TelegramUpdateStore` need no change. The
|
|
39
|
+
new capability is opt-in
|
|
40
|
+
([ADR 0256](../decisions/0256-a-telegram-attempt-is-fenced-by-a-persisted-claim.md)).
|
|
41
|
+
|
|
42
|
+
1. To fence a domain write, move only that handler to the explicit context-aware option:
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
createTelegramUpdateIntake({
|
|
46
|
+
store,
|
|
47
|
+
handleAttempt: async (update, { fence, ownerLost }) => {
|
|
48
|
+
ownerLost.throwIfAborted()
|
|
49
|
+
await domainTransaction(async (tx) => {
|
|
50
|
+
if (await transactionStore(tx).owns(fence, Date.now())) await apply(tx, update)
|
|
51
|
+
})
|
|
52
|
+
},
|
|
53
|
+
})
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
2. A direct grammY handler remains `handle: bot.handleUpdate.bind(bot)`; stitchkit does not pass its
|
|
57
|
+
attempt context into grammY's optional webhook-envelope parameter.
|
|
58
|
+
|
|
59
|
+
3. A custom store used by `handleAttempt` implements `claimOwned`, `renewOwned`, `owns` and
|
|
60
|
+
`settleOwned` over the full `{ updateId, attempt, claimId }` identity. If it also delivers
|
|
61
|
+
`handleExhaustion`, implement `exhaust`, `dueExhaustions` and `acknowledgeExhaustion`. Run
|
|
62
|
+
`checkTelegramUpdateStore` against a fresh database; it checks base, fenced and durable rules
|
|
63
|
+
according to the methods the store exposes.
|
|
64
|
+
|
|
65
|
+
4. SQLite stores add `claim_id` automatically. PostgreSQL stores with the default
|
|
66
|
+
`createTable: true` do the same. When migrations own a PostgreSQL table, apply the returned
|
|
67
|
+
schema before enabling `handleAttempt` or `handleExhaustion`:
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
await sql.unsafe(postgresTelegramUpdateStoreSchema('telegram_updates'))
|
|
71
|
+
// Equivalent addition for an existing table:
|
|
72
|
+
// ALTER TABLE telegram_updates ADD COLUMN IF NOT EXISTS claim_id TEXT;
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Legacy `handle(update)` continues to use base operations that do not reference `claim_id`, so
|
|
76
|
+
the migration is required only when the new capability or an explicit base
|
|
77
|
+
`claim(..., { durableExhaustion: true })` is enabled.
|
|
78
|
+
|
|
12
79
|
## Released migration: 0.108.0
|
|
13
80
|
|
|
14
81
|
### a request that never left is its own send outcome
|
|
@@ -831,72 +898,3 @@ text.replace(new RegExp(TELEGRAM_BOT_TOKEN_PATTERN.source, 'g'), '[redacted]')
|
|
|
831
898
|
createJsonLogger({})
|
|
832
899
|
redactTelegramBotToken(text)
|
|
833
900
|
```
|
|
834
|
-
|
|
835
|
-
## Released migration: 0.100.0
|
|
836
|
-
|
|
837
|
-
### watch capacity and session/live/delivery adoption
|
|
838
|
-
|
|
839
|
-
**Who must act:** watch consumers exceeding 1024 hub sources, 1024 hub subscribers,
|
|
840
|
-
1024 client keys or 128 listeners per key. These previously unbounded collections
|
|
841
|
-
now refuse excess admission. Choose explicit positive finite limits, for example:
|
|
842
|
-
|
|
843
|
-
```ts
|
|
844
|
-
// Before: no hub-wide capacity bounds.
|
|
845
|
-
createWatchHub({ read, watchable });
|
|
846
|
-
// After: explicitly budget a larger workload.
|
|
847
|
-
createWatchHub({ read, watchable, maxSources: 4096, maxSubscribers: 4096 });
|
|
848
|
-
// Client limits are configured independently.
|
|
849
|
-
createWatchClient({ ...clientConfig, maxKeys: 2048, maxListenersPerKey: 256 });
|
|
850
|
-
```
|
|
851
|
-
|
|
852
|
-
The remaining steps apply only to applications opting into session lifetimes,
|
|
853
|
-
scoped watched reads, managed subscriptions or multi-action notification delivery.
|
|
854
|
-
Existing clients within the limits and single-send outboxes need no migration.
|
|
855
|
-
|
|
856
|
-
Replace local session generation counters with one `createSessionScope`; share it
|
|
857
|
-
with `createSessionCredentials`. Guard transport attempts, complete asynchronous
|
|
858
|
-
operations and final synchronous delivery. Route credential persistence through
|
|
859
|
-
one coordinator. Keep provider-specific error classification and bounded command
|
|
860
|
-
retry in the application. Coordination is per client instance.
|
|
861
|
-
|
|
862
|
-
Upgrade both watch endpoints before enabling session-scoped watches: strict older
|
|
863
|
-
servers reject the new optional `WatchKey.instance`. Server-verified scope controls
|
|
864
|
-
sharing; the client instance is only routing identity. Bind exact watched handles
|
|
865
|
-
to session-qualified cache keys. Existing delta/reconnect behavior stays in place.
|
|
866
|
-
For applications needing more than 1024 hub sources/subscribers or client keys, set
|
|
867
|
-
explicit finite capacity options. Enable a reconciliation interval when lost hints
|
|
868
|
-
must recover without a reconnect.
|
|
869
|
-
|
|
870
|
-
For multi-action outboxes, enqueue new items with a versioned delivery plan and
|
|
871
|
-
return small JSON receipts from `send`. Existing queued single-send items should
|
|
872
|
-
be drained before enabling a delivery executor on that queue, or migrated under
|
|
873
|
-
the store's atomic transition. When a new executor version ships, list the old
|
|
874
|
-
version's projection in `delivery.retiredVersions` until its queued items drain;
|
|
875
|
-
a plan version with no executor waits, reported to `onError` as
|
|
876
|
-
`NotificationPlanVersionError` (from 0.100.1).
|
|
877
|
-
Make projection idempotent. Provider idempotency/reconciliation remains necessary
|
|
878
|
-
for a crash between remote success and checkpoint. These APIs are available starting with 0.100.0.
|
|
879
|
-
|
|
880
|
-
### exclusive lock recovery
|
|
881
|
-
|
|
882
|
-
New lock and reclaim-guard records include `owner.process`: kernel boot identity,
|
|
883
|
-
process birth token and namespace. `readDiagnosticJournalLockDiagnosis(error)`
|
|
884
|
-
adds `identity` to distinguish matched lifetime evidence, PID reuse, a different
|
|
885
|
-
boot, an absent PID, a legacy record or unavailable identity. Legacy `alive` only
|
|
886
|
-
means the PID is present; it does not prove that the recorded writer still exists.
|
|
887
|
-
|
|
888
|
-
**Who must act:** deployments recovering a legacy lock with a reused live PID,
|
|
889
|
-
or bundling file-lock consumers into a standalone Darwin executable. Legacy
|
|
890
|
-
records cannot safely infer their missing boot/start identity from `acquiredAt`.
|
|
891
|
-
Stop all writers to that resource, verify exclusive ownership, preserve the old
|
|
892
|
-
lock for incident evidence outside the active lock path, and restart with the
|
|
893
|
-
new package. Never delete a lock merely because it is old. New acquisitions
|
|
894
|
-
write the modern record; no journal data migration is needed.
|
|
895
|
-
|
|
896
|
-
Darwin distributions must retain the package's architecture-matched native
|
|
897
|
-
`.node` asset for process birth measurement, as for contained filesystem access.
|
|
898
|
-
If native/proc identity cannot be read, new locks explicitly record null and
|
|
899
|
-
recovery refuses rather than treating unknown ownership as death. From 0.100.1 a
|
|
900
|
-
Linux kernel without time namespaces (before 5.6) records the PID namespace alone
|
|
901
|
-
instead of null, so its locks recover like any other. Existing
|
|
902
|
-
`refuse` policy and the separately configured ownerless-file grace are unchanged.
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# Upgrading 0.37.0 → 0.37.0 — stitchkit docs slice
|
|
2
|
+
|
|
3
|
+
> Generated from the `docs/` tree by `bun run gen:llms`. The index of all slices is
|
|
4
|
+
> `llms.txt` in this package.
|
|
5
|
+
|
|
6
|
+
Contains: Guide: Upgrading, 0.37.0 → 0.37.0 (docs/guide/upgrading.md)
|
|
7
|
+
|
|
8
|
+
==============================================================================
|
|
9
|
+
# Guide: Upgrading, 0.37.0 → 0.37.0 (docs/guide/upgrading.md)
|
|
10
|
+
==============================================================================
|
|
11
|
+
|
|
12
|
+
## The 0.37 migration
|
|
13
|
+
|
|
14
|
+
Tool presentation is no longer an executable Zod parser. Replace the removed
|
|
15
|
+
flatten helpers with the JSON Schema compiler, and choose the explicit
|
|
16
|
+
`MountableTool` surface when using the advanced collection API:
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
// before
|
|
20
|
+
const flat = flattenUnionsDeep(zodSchema)
|
|
21
|
+
mountable.schema
|
|
22
|
+
|
|
23
|
+
// after
|
|
24
|
+
const flat = flattenToolJsonSchema(
|
|
25
|
+
z.toJSONSchema(zodSchema, { target: 'draft-07', io: 'input' }),
|
|
26
|
+
)
|
|
27
|
+
mountable.presentationSchema // model/MCP/manifest JSON Schema
|
|
28
|
+
mountable.argumentSchema // executable CLI argument adapter only
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Contract and native handlers keep their original Zod schemas; MCP and agent SDK
|
|
32
|
+
adapters now forward raw arguments so defaults, coercions, refinements and
|
|
33
|
+
transforms execute exactly once inside Stitchkit. There are no compatibility
|
|
34
|
+
exports for `flattenDiscriminatedUnion`, `flattenUnionsDeep` or
|
|
35
|
+
`MountableTool.schema`. → ADR 0050
|
|
36
|
+
|
|
37
|
+
Tool-call hooks now take one options object. Migrate all three callbacks; there
|
|
38
|
+
are no positional overloads:
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
// before
|
|
42
|
+
beforeToolCall: (toolName, args, context, endpoint) => {}
|
|
43
|
+
afterToolCall: (toolName, args, result, durationMs, context, endpoint, error) => {}
|
|
44
|
+
onToolError: (toolName, error, context, endpoint) => {}
|
|
45
|
+
|
|
46
|
+
// after
|
|
47
|
+
beforeToolCall: ({ toolName, args, context, endpoint }) => {}
|
|
48
|
+
afterToolCall: ({ toolName, args, result, durationMs, context, endpoint, error }) => {}
|
|
49
|
+
onToolError: ({ toolName, error, context, endpoint }) => {}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### MCP schema validation
|
|
53
|
+
|
|
54
|
+
The standalone validator now takes one object and live MCP configs carry the
|
|
55
|
+
same rules under `schemaValidation`. Migrate every positional call and every
|
|
56
|
+
`onIncompatibleSchema` field; there is no old-shape overload:
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
// before
|
|
60
|
+
validateMcpSchemas(services, 'throw', logger, { requireTypedProperties: true })
|
|
61
|
+
createMcpHandler({ services, onIncompatibleSchema: 'throw' })
|
|
62
|
+
|
|
63
|
+
// after
|
|
64
|
+
validateMcpSchemas({ services, policy: 'throw', logger, requireTypedProperties: true })
|
|
65
|
+
createMcpHandler({ services, schemaValidation: { policy: 'throw' } })
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Put `extend` and `flattenUnionInput` beside `services`, not inside
|
|
69
|
+
`schemaValidation`. The handler applies the profile to the exact prepared schema
|
|
70
|
+
it advertises. Add `requirePortableFormats: true` when every custom JSON Schema
|
|
71
|
+
format must be rejected before a client sees it.
|
|
72
|
+
|
|
73
|
+
Native MCP registration also changed shape in 0.37. Move protected tools to the
|
|
74
|
+
framework registrar; keep an SDK-raw tool only by naming the opt-out:
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
// before
|
|
78
|
+
nativeTools: (server, auth) => server.registerTool(name, config, handler)
|
|
79
|
+
|
|
80
|
+
// after — lifecycle, hooks and schema policy apply
|
|
81
|
+
nativeTools: ({ registerTool }, auth) => registerTool({
|
|
82
|
+
name, description, identity, input, output, handler,
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
// after — intentionally raw
|
|
86
|
+
nativeTools: ({ rawServer }, auth) => rawServer.registerTool(name, config, handler)
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
If a tool hook annotated `endpoint` as `MethodDef`, remove that annotation or
|
|
90
|
+
use `OperationIdentity`: native operations have service/action/scope/method but
|
|
91
|
+
no HTTP path.
|
|
92
|
+
|
|
93
|
+
### Node-facing server types
|
|
94
|
+
|
|
95
|
+
`stitchkit/server` remains Bun-concrete: an explicitly annotated `RawRoute`
|
|
96
|
+
still receives `BunServer`. `stitchkit/node` no longer drags Bun declarations
|
|
97
|
+
into a Node project. Its raw routes default the host server to `unknown`, and
|
|
98
|
+
its Socket.IO handle exposes only Node capabilities:
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
// before — Node entry still leaked Bun-only fields and ambient types
|
|
102
|
+
const route: RawRoute = { handler: (_req, ctx) => ctx.server?.upgrade(...) }
|
|
103
|
+
const socket = await createSocketIOServer(config)
|
|
104
|
+
socket.websocket
|
|
105
|
+
|
|
106
|
+
// after — name a custom embedding host only when one exists
|
|
107
|
+
const route: RawRoute<MyHostServer> = {
|
|
108
|
+
handler: (_req, ctx) => useHost(ctx.server),
|
|
109
|
+
}
|
|
110
|
+
const socket = await createSocketIOServer(config)
|
|
111
|
+
socket.io
|
|
112
|
+
socket.attach(nodeHttpServer)
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Node consumers can remove `@types/bun` unless another dependency independently
|
|
116
|
+
requires it.
|
|
117
|
+
|
|
118
|
+
### Managed server shutdown
|
|
119
|
+
|
|
120
|
+
`createServer()` and `serveNode()` now return the same structural managed
|
|
121
|
+
lifecycle. Replace every direct runtime stop and parallel Socket.IO close:
|
|
122
|
+
|
|
123
|
+
```ts
|
|
124
|
+
// before — split ownership
|
|
125
|
+
const socket = await createSocketIOServer(config)
|
|
126
|
+
const server = createServer({
|
|
127
|
+
services,
|
|
128
|
+
websocket: socket.websocket,
|
|
129
|
+
rawRoutes: [socket.route],
|
|
130
|
+
})
|
|
131
|
+
server.stop()
|
|
132
|
+
await socket.io.close()
|
|
133
|
+
|
|
134
|
+
// after — one owner and one total deadline
|
|
135
|
+
const socket = await createSocketIOServer(config)
|
|
136
|
+
const server = createServer({ services, socket })
|
|
137
|
+
const result = await server.shutdown({ gracePeriodMs: 30_000 })
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
On Node, keep the same `socket` field and replace `handle.close()` with
|
|
141
|
+
`handle.shutdown()`. Runtime-specific diagnostics move under `handle.runtime`;
|
|
142
|
+
do not use it as a second shutdown path. Standalone CLI/tools that create a
|
|
143
|
+
Socket.IO handle without an HTTP server call `await socket.close()`.
|
|
144
|
+
|
|
145
|
+
If Bun Socket.IO shares the port with a raw lane, keep the explicit composition
|
|
146
|
+
but let the server mount the Socket.IO route:
|
|
147
|
+
|
|
148
|
+
```ts
|
|
149
|
+
createServer({
|
|
150
|
+
services,
|
|
151
|
+
socket,
|
|
152
|
+
websocket: composeWebSocketHandlers([
|
|
153
|
+
webSocketLane({ match: isRaw, handlers: rawHandlers }),
|
|
154
|
+
socketIoLane(socket.websocket),
|
|
155
|
+
]),
|
|
156
|
+
rawRoutes: [rawUpgradeRoute],
|
|
157
|
+
})
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Move native Bun `routes` entries to `rawRoutes`. Native routes run before the
|
|
161
|
+
Fetch handler and therefore cannot participate in admission or drain. Wire
|
|
162
|
+
`SIGTERM`/`SIGINT` in the application; the first signal starts `shutdown()`, and
|
|
163
|
+
a later signal may abort the same controller. Close MCP, databases and queues
|
|
164
|
+
after the server result—those resources remain application-owned.
|
|
165
|
+
|
|
166
|
+
Move a handshake policy from the Node-only callback shape inside
|
|
167
|
+
`serverOptions` to the runtime-neutral top-level policy. It receives a Web
|
|
168
|
+
`Request`, may be async, and returns whether to admit the handshake:
|
|
169
|
+
|
|
170
|
+
```ts
|
|
171
|
+
// before
|
|
172
|
+
serverOptions: { allowRequest: (request, done) => done(null, allowed(request)) }
|
|
173
|
+
|
|
174
|
+
// after
|
|
175
|
+
allowRequest: (request) => allowed(request)
|
|
176
|
+
```
|