stitchkit 0.98.2 → 0.100.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 (143) hide show
  1. package/CHANGELOG.md +63 -0
  2. package/dist/agent-runtime/contained-darwin.d.ts +0 -17
  3. package/dist/agent-runtime/contained-darwin.d.ts.map +1 -1
  4. package/dist/agent-runtime/contained-write.d.ts.map +1 -1
  5. package/dist/agent-runtime/invocation-attempt.d.ts +22 -0
  6. package/dist/agent-runtime/invocation-attempt.d.ts.map +1 -0
  7. package/dist/agent-runtime/invocation-failure.d.ts +6 -0
  8. package/dist/agent-runtime/invocation-failure.d.ts.map +1 -0
  9. package/dist/agent-runtime/invocation-payload.d.ts +18 -0
  10. package/dist/agent-runtime/invocation-payload.d.ts.map +1 -0
  11. package/dist/agent-runtime/invocation-read.d.ts +131 -0
  12. package/dist/agent-runtime/invocation-read.d.ts.map +1 -0
  13. package/dist/agent-runtime/invocation-schema.d.ts +378 -0
  14. package/dist/agent-runtime/invocation-schema.d.ts.map +1 -0
  15. package/dist/agent-runtime/invocations.d.ts +46 -0
  16. package/dist/agent-runtime/invocations.d.ts.map +1 -0
  17. package/dist/agent-runtime/run-acquisition.d.ts.map +1 -1
  18. package/dist/agent-runtime/run-execution-state.d.ts +4 -2
  19. package/dist/agent-runtime/run-execution-state.d.ts.map +1 -1
  20. package/dist/agent-runtime/run-execution.d.ts.map +1 -1
  21. package/dist/agent-runtime/run-failure.d.ts.map +1 -1
  22. package/dist/agent-runtime/run-model-attempts.d.ts.map +1 -1
  23. package/dist/agent-runtime/run-operation-lifecycle.d.ts +2 -2
  24. package/dist/agent-runtime/run-operation-lifecycle.d.ts.map +1 -1
  25. package/dist/agent-runtime/run-provider-ledger.d.ts +1 -0
  26. package/dist/agent-runtime/run-provider-ledger.d.ts.map +1 -1
  27. package/dist/agent-runtime/runtime.d.ts +11 -0
  28. package/dist/agent-runtime/runtime.d.ts.map +1 -1
  29. package/dist/agent-runtime/store-create.d.ts.map +1 -1
  30. package/dist/agent-runtime/store-event-once.d.ts +29 -0
  31. package/dist/agent-runtime/store-event-once.d.ts.map +1 -0
  32. package/dist/agent-runtime/store.d.ts +5 -0
  33. package/dist/agent-runtime/store.d.ts.map +1 -1
  34. package/dist/application/change-subscription.d.ts +36 -0
  35. package/dist/application/change-subscription.d.ts.map +1 -0
  36. package/dist/application/diagnostic-journal-contract.d.ts +3 -6
  37. package/dist/application/diagnostic-journal-contract.d.ts.map +1 -1
  38. package/dist/application/notification-delivery.d.ts +32 -0
  39. package/dist/application/notification-delivery.d.ts.map +1 -0
  40. package/dist/application/notification-outbox-state.d.ts +37 -0
  41. package/dist/application/notification-outbox-state.d.ts.map +1 -0
  42. package/dist/application/notification-outbox.d.ts +7 -1
  43. package/dist/application/notification-outbox.d.ts.map +1 -1
  44. package/dist/application/watch-hub-core.d.ts +10 -15
  45. package/dist/application/watch-hub-core.d.ts.map +1 -1
  46. package/dist/application/watch-hub.d.ts +10 -2
  47. package/dist/application/watch-hub.d.ts.map +1 -1
  48. package/dist/browser/session-credentials.d.ts +20 -0
  49. package/dist/browser/session-credentials.d.ts.map +1 -0
  50. package/dist/browser/session.d.ts +31 -0
  51. package/dist/browser/session.d.ts.map +1 -0
  52. package/dist/durability/events.d.ts +6 -0
  53. package/dist/durability/events.d.ts.map +1 -1
  54. package/dist/entrypoints/agent-runtime/coding-tools.js +5 -4
  55. package/dist/entrypoints/agent-runtime/harness.js +8 -6
  56. package/dist/entrypoints/agent-runtime/sandbox.js +5 -4
  57. package/dist/entrypoints/agent-runtime/sqlite/bun.js +3 -3
  58. package/dist/entrypoints/agent-runtime/sqlite/node.js +3 -3
  59. package/dist/entrypoints/agent-runtime/testing.js +3 -3
  60. package/dist/entrypoints/agent-runtime.d.ts +3 -0
  61. package/dist/entrypoints/agent-runtime.d.ts.map +1 -1
  62. package/dist/entrypoints/agent-runtime.js +21 -10
  63. package/dist/entrypoints/application/diagnostic-journal.js +2 -1
  64. package/dist/entrypoints/application.d.ts +3 -1
  65. package/dist/entrypoints/application.d.ts.map +1 -1
  66. package/dist/entrypoints/application.js +629 -189
  67. package/dist/entrypoints/cli.js +6 -6
  68. package/dist/entrypoints/files.js +2 -1
  69. package/dist/entrypoints/index.d.ts +2 -0
  70. package/dist/entrypoints/index.d.ts.map +1 -1
  71. package/dist/entrypoints/index.js +172 -0
  72. package/dist/entrypoints/live.js +68 -13
  73. package/dist/entrypoints/react.d.ts +1 -1
  74. package/dist/entrypoints/react.d.ts.map +1 -1
  75. package/dist/entrypoints/react.js +59 -16
  76. package/dist/entrypoints/testing.js +1 -1
  77. package/dist/entrypoints/tools/connections.js +2 -2
  78. package/dist/entrypoints/tools/invoker.js +4 -4
  79. package/dist/entrypoints/tools.js +8 -8
  80. package/dist/{index-wsm5w4ee.js → index-0b1cnze2.js} +35 -3
  81. package/dist/{index-np4m8hd3.js → index-0r0fbk7z.js} +9 -7
  82. package/dist/{index-p9sh0xx4.js → index-5p2m7we1.js} +3 -3
  83. package/dist/{index-55hkhm67.js → index-8qt2m66q.js} +1 -1
  84. package/dist/{index-hmp4800j.js → index-9t3dffv2.js} +3 -3
  85. package/dist/{index-khtgb21r.js → index-bm5rx7n6.js} +2 -2
  86. package/dist/{index-gxrt5h6v.js → index-cyktc4bp.js} +3 -0
  87. package/dist/{index-xw3s75nj.js → index-d8m8zpd8.js} +1 -1
  88. package/dist/{index-px2gm2jp.js → index-fhwqwxe0.js} +17 -55
  89. package/dist/{index-het2he5g.js → index-gw8bmyaj.js} +5 -3
  90. package/dist/{index-8p52kd5p.js → index-js7gqrb1.js} +96 -14
  91. package/dist/{index-wt743f7f.js → index-k6gpggb9.js} +2 -1
  92. package/dist/index-q3pyb9n0.js +22 -0
  93. package/dist/{index-f1nb70ae.js → index-r066jx76.js} +4 -4
  94. package/dist/{index-sp1tzwnb.js → index-s84he4v7.js} +2 -2
  95. package/dist/{index-863ea9jb.js → index-sh3s86na.js} +3 -3
  96. package/dist/{index-nts4a2br.js → index-xwhjjw2x.js} +879 -280
  97. package/dist/{index-1m2thnq0.js → index-y9qn426m.js} +2 -2
  98. package/dist/index-zjjyynm3.js +42 -0
  99. package/dist/internal/coalesced-task.d.ts +15 -0
  100. package/dist/internal/coalesced-task.d.ts.map +1 -0
  101. package/dist/internal/darwin-binding.d.ts +19 -0
  102. package/dist/internal/darwin-binding.d.ts.map +1 -0
  103. package/dist/internal/exclusive-lock.d.ts +20 -7
  104. package/dist/internal/exclusive-lock.d.ts.map +1 -1
  105. package/dist/internal/mutation-queue.d.ts +4 -0
  106. package/dist/internal/mutation-queue.d.ts.map +1 -0
  107. package/dist/internal/process-identity.d.ts +1 -1
  108. package/dist/internal/process-identity.d.ts.map +1 -1
  109. package/dist/internal/process-instance.d.ts +26 -0
  110. package/dist/internal/process-instance.d.ts.map +1 -0
  111. package/dist/live/watch-client-state.d.ts +71 -0
  112. package/dist/live/watch-client-state.d.ts.map +1 -0
  113. package/dist/live/watch-client.d.ts +5 -1
  114. package/dist/live/watch-client.d.ts.map +1 -1
  115. package/dist/live/watch-contract.d.ts +12 -0
  116. package/dist/live/watch-contract.d.ts.map +1 -1
  117. package/dist/react/cache-bridge.d.ts +10 -0
  118. package/dist/react/cache-bridge.d.ts.map +1 -1
  119. package/dist/tools/schema/flatten-join.d.ts.map +1 -1
  120. package/llms/agent-runtime.2.txt +94 -0
  121. package/llms/agent-runtime.3.txt +24 -0
  122. package/llms/application.2.txt +68 -2
  123. package/llms/application.txt +32 -2
  124. package/llms/files.txt +1 -1
  125. package/llms/live.txt +7 -0
  126. package/llms/react.txt +8 -0
  127. package/llms/server.3.txt +35 -0
  128. package/llms/stitchkit.2.txt +41 -0
  129. package/llms/stitchkit.3.txt +22 -0
  130. package/llms/stitchkit.txt +3 -3
  131. package/llms/tools.txt +9 -8
  132. package/llms/upgrading-0.37.0.txt +176 -0
  133. package/llms/{upgrading-0.37.0-0.58.0.txt → upgrading-0.44.0-0.60.0.txt} +229 -169
  134. package/llms/{upgrading-0.59.0-0.71.0.txt → upgrading-0.61.0-0.73.0.txt} +122 -229
  135. package/llms/{upgrading-0.72.0-0.98.0.txt → upgrading-0.74.0-0.100.0.txt} +96 -122
  136. package/llms/upgrading.txt +4 -3
  137. package/llms-full.txt +431 -14
  138. package/llms.txt +11 -10
  139. package/native/darwin-arm64.node +0 -0
  140. package/native/darwin-x64.node +0 -0
  141. package/package.json +1 -1
  142. package/dist/agent-runtime/run-mutation-queue.d.ts +0 -21
  143. package/dist/agent-runtime/run-mutation-queue.d.ts.map +0 -1
package/llms-full.txt CHANGED
@@ -68,11 +68,11 @@ own, recorded as an ADR.
68
68
  | `stitchkit/geo` | server (Bun or Node) | evolving | managed GeoIP reader generations, last-known-good reload and the optional MaxMind adapter |
69
69
  | `stitchkit/oauth` | browser **and** server | evolving | provider-neutral Authorization Code + PKCE request and one-shot callback transaction mechanics |
70
70
  | `stitchkit/google` | server (Bun or Node) | evolving | optional Google code exchange and verified OIDC identity adapter |
71
- | `stitchkit/observability` | server | stable<br>_redefined in 2 of the 43 minors since 0.56.2, most recently 0.92.0_ | request/tool event projections — `createObservability`, trace context, sanitisation |
71
+ | `stitchkit/observability` | server | stable<br>_redefined in 2 of the 45 minors since 0.56.2, most recently 0.92.0_ | request/tool event projections — `createObservability`, trace context, sanitisation |
72
72
  | `stitchkit/testing` | tests on Bun or Node | stable | in-process generated clients over a real Fetch handler, plus the managed-resource conformance kit |
73
73
  | `stitchkit/declaration` | browser + build and deployment tooling (Bun or Node) | evolving | `ProjectDeclarationSchema` — the one machine-readable statement a repository makes about itself |
74
74
  | `stitchkit/react` | browser + server rendering | stable | `createCursorQuery`, `createCacheBridge`, QueryClient and `ApiError` retry policy |
75
- | `stitchkit/agent-runtime` | server | evolving<br>_redefined in 22 of the 43 minors since 0.56.2, most recently 0.97.0_ | optional durable conversation/run loop, history, models, prompts, fencing and events |
75
+ | `stitchkit/agent-runtime` | server | evolving<br>_redefined in 22 of the 45 minors since 0.56.2, most recently 0.97.0_ | optional durable conversation/run loop, history, models, prompts, fencing and events |
76
76
  | `stitchkit/agent-runtime/sandbox` | server | evolving | optional Linux Bubblewrap sessions, durable workspaces and a host HTTP credential gateway; see [sandbox guide](sandbox.md) |
77
77
  | `stitchkit/agent-runtime/testing` | tests on Bun or Node | evolving | credential-free replay, scripted provider faults, deterministic race controls and the agent-store conformance kit |
78
78
  | `stitchkit/agent-runtime/harness` | server | evolving | resource-aware process-local facade over the canonical Agent runtime; supervision stays outside |
@@ -82,7 +82,7 @@ own, recorded as an ADR.
82
82
  | `stitchkit/agent-runtime/sqlite/bun` | server (Bun) | evolving | durable built-in SQLite store for the agent runtime |
83
83
  | `stitchkit/agent-runtime/sqlite/node` | server (Node ≥ 22.5) | evolving | durable built-in SQLite store for the agent runtime |
84
84
  | `stitchkit-tui` | terminal (Bun) | evolving | optional official OpenTUI host over a caller-composed headless runtime |
85
- | `stitchkit/application` | browser + server | evolving<br>_redefined in 9 of the 43 minors since 0.56.2, most recently 0.96.0_ | managed resource graph, readiness, admission, schedules, subtree restart and bounded shutdown |
85
+ | `stitchkit/application` | browser + server | evolving<br>_redefined in 10 of the 45 minors since 0.56.2, most recently 0.100.0_ | managed resource graph, readiness, admission, schedules, subtree restart and bounded shutdown |
86
86
  | `stitchkit/application/grammy` | server | evolving | a grammY bot as application resources — configuration, then polling with updates admitted by batch — and the webhook adapter |
87
87
  | `stitchkit/application/opentelemetry` | server | evolving | maps application snapshots onto an injected OpenTelemetry `Meter` |
88
88
  | `stitchkit/application/schemas` | browser + server | evolving | the application's snapshot, health and shutdown schemas alone, without the kernel |
@@ -3245,6 +3245,47 @@ hour and drops again starts its next retry at `minDelayMs` rather than the
3245
3245
  ceiling it reached last time. Aborting the signal ends the iteration promptly,
3246
3246
  including in the middle of a wait.
3247
3247
 
3248
+ ## Operations belong to a login lifetime
3249
+
3250
+ Use `createSessionScope` when a client can log out or switch accounts. The scope is
3251
+ host-owned and context is immutable. Replace it for **every** login, even to the
3252
+ same account. Capture once for an entire compound operation:
3253
+
3254
+ ```ts
3255
+ import { createClient, createSessionScope } from 'stitchkit'
3256
+
3257
+ const sessions = createSessionScope<{ accountId: string }>()
3258
+ const operation = sessions.replace({ accountId: 'account-a' })
3259
+ const client = createClient(contract, {
3260
+ baseUrl,
3261
+ fetch: operation.bindFetch(fetch, () => ({ Authorization: tokenFor(operation) })),
3262
+ })
3263
+ const value = await operation.run(() => client.read())
3264
+ operation.deliver(() => applyResult(value))
3265
+ ```
3266
+
3267
+ Capture before the first request, keep the same operation for a second request or
3268
+ one explicitly allowed auth retry, and guard the **actual** final transport send
3269
+ or local state change. An async delivery callback needs another check after its
3270
+ await; `deliver` itself is synchronous. Guard error delivery too. Cancellation is
3271
+ additional protection and cannot undo server effects. `SessionExpiredError` means
3272
+ discard the stale operation, not refresh a new user's credentials. For raw/stream
3273
+ responses, guard each consumed chunk and final delivery; `run` returning a stream
3274
+ does not mean its future contents have been consumed.
3275
+
3276
+ `createSessionCredentials({ scope, refresh, storage })` coordinates the same scope.
3277
+ `login(context, credentials)` creates a generation, `read(operation)` resolves only
3278
+ that generation's credentials, and concurrent `refresh(operation)` shares one
3279
+ flight. `logout()` invalidates memory immediately and resolves after its ordered
3280
+ storage clear (or a newer login superseding that clear). Handle storage and capacity
3281
+ errors explicitly; a rejected write is not proof of durable login/logout. Refresh
3282
+ network work never holds the storage queue. Callers decide whether a failed command
3283
+ may be retried once; do not replay a multi-step callback or treat any `403` as expiry.
3284
+
3285
+ This is per-instance coordination. Route all writes through it; shared tabs/processes
3286
+ need storage-level locking. Replace the local session counter and refresh queue when
3287
+ adopting these primitives instead of retaining two independent lifetimes.
3288
+
3248
3289
 
3249
3290
  ==============================================================================
3250
3291
  # Guide: MCP & agents (docs/guide/mcp-and-agents.md)
@@ -3959,14 +4000,15 @@ as one thing. Where they agree, that is what you get; where they disagree — a
3959
4000
  `.refine()` only one of them carries, two different bounds on the same number, an
3960
4001
  enum against a free string — the *constraint* is dropped and the **type** is not.
3961
4002
  A field that is a number in every variant is advertised as a number, not as a
3962
- bare description. When the kinds genuinely differ, the flat projection keeps
3963
- every provable kind in a deterministic JSON Schema type array — for example
3964
- `type: ['string', 'array']` — while dropping constraints that are not sound for
3965
- all branches. Nested `oneOf` / `anyOf` values still contribute their provable
3966
- base kinds without reintroducing union keywords. Only a branch whose kind is
4003
+ bare description. Scalar collisions keep every provable kind in a deterministic
4004
+ JSON Schema type array. If any alternative is an object or array, the property
4005
+ retains distinct structural alternatives in `anyOf`: object properties, typed
4006
+ array items, bounds and nullable values remain available to the model. The
4007
+ containing discriminated union still becomes one object; flat mode does not
4008
+ promise to remove every nested union keyword. Only a branch whose kind is
3967
4009
  actually unknowable, such as a free-form schema or unresolved reference, leaves
3968
4010
  the collision unconstrained. → ADRs [0044](../decisions/0044-a-collided-field-keeps-its-type.md)
3969
- and [0065](../decisions/0065-flat-collisions-preserve-every-known-kind.md)
4011
+ and [0205](../decisions/0205-flat-tool-collisions-retain-structural-alternatives.md).
3970
4012
 
3971
4013
  It is **deep** because the projection walks the generated JSON Schema document,
3972
4014
  including objects, arrays, tuples and schema-definition nodes. Structurally
@@ -3975,8 +4017,8 @@ Plain unions and unions hidden behind unresolved external references remain
3975
4017
  unions because Stitchkit cannot soundly invent a discriminator.
3976
4018
 
3977
4019
  Use the default `flattenUnionInput: false` when the model must see the exact
3978
- relationship between a discriminator and each branch. The flat type array is a
3979
- sound set of possible JSON kinds, not a reconstruction of those correlations.
4020
+ relationship between a discriminator and each branch. Joined property schemas
4021
+ describe allowed values without reconstructing those correlations.
3980
4022
 
3981
4023
  The flattened form is **lossy but never executable**. Per-variant refinements
3982
4024
  and incompatible constraints are widened in the presentation document; the
@@ -6272,6 +6314,100 @@ can provision those parents; the optional `cleanup(context)` removes them again,
6272
6314
  whether the scenario passed or failed. A factory that owns no fixture state ignores the argument and
6273
6315
  keeps working unchanged.
6274
6316
 
6317
+ ## Plain completions and shared invocation receipts
6318
+
6319
+ Use `createModelInvocationLedger` from `stitchkit/agent-runtime` when a direct text
6320
+ completion and an agent must share causal provider evidence. It uses the existing
6321
+ runtime store, not a second database. The completion path sends exactly one user
6322
+ prompt, no framework instructions and no tools. It never starts an agent loop.
6323
+
6324
+ ```ts
6325
+ import { createModelInvocationLedger } from 'stitchkit/agent-runtime';
6326
+
6327
+ const invocations = createModelInvocationLedger({
6328
+ store, // the same AgentRuntimeStore object passed to createAgentRuntime
6329
+ models, // an existing defineModelRegistry result
6330
+ payloadKey, // 32 bytes from the host's secret store; retain separately from archives
6331
+ authorize: async ({ context, conversationId, action, trace }) => {
6332
+ const session = await authenticate(context);
6333
+ await authorizeConversation(session, conversationId, action, trace);
6334
+ return { subject: session.subject }; // verified identity, never a body field
6335
+ },
6336
+ });
6337
+
6338
+ const completion = await invocations.complete({
6339
+ conversationId: 'evaluation-42',
6340
+ idempotencyKey: 'summary-case-3',
6341
+ trace: {
6342
+ operationId: 'summary-42', purpose: 'summarize', project: 'example',
6343
+ parent: { traceId: 'trace-42' }, experiment: 'evaluation', case: 'case-3',
6344
+ },
6345
+ prompt: 'Summarize these supplied facts.',
6346
+ models: ['primary', 'fallback'], // registry keys; repeat a key for an explicit retry
6347
+ settings: { maxOutputTokens: 500 },
6348
+ timeoutMs: 30_000,
6349
+ }, authenticatedContext);
6350
+
6351
+ // Add to the existing createAgentRuntime configuration:
6352
+ const receiptConfig = {
6353
+ ledger: invocations,
6354
+ trace: ({ run }) => ({
6355
+ operationId: run.id, purpose: 'assistant-turn', project: 'example',
6356
+ }),
6357
+ };
6358
+ // createAgentRuntime({ ...existingConfig, invocations: receiptConfig });
6359
+
6360
+ const page = await invocations.read({
6361
+ conversationId: 'evaluation-42', limit: 100,
6362
+ }, authenticatedContext);
6363
+ // Continue with fromSeq: page.nextSeq while it is defined.
6364
+ ```
6365
+
6366
+ `complete` returns `invocationId`, `outcome` and, on success, `text`. `duplicate`
6367
+ returns the existing ID without provider execution or replaying plaintext. Changed
6368
+ input, trace or caller under the same key is a conflict. Model attempts are explicit
6369
+ and bounded to eight; SDK retries are disabled. Only provider failures trigger the
6370
+ next attempt. Abort, audit refusal and configuration errors do not. An admitted call
6371
+ whose process died stays claimed: inspect the receipt before choosing a new key.
6372
+
6373
+ The agent retains its existing prompt, tools and loop. Both modes produce
6374
+ `invocation/started`, `provider/request`, `provider/response` and
6375
+ `invocation/finished` records through `read`. Each attempt has its own ID; response
6376
+ usage is counted once and keeps `unavailable` distinct from measured zero. Requested
6377
+ registry selection, sent adapter identity and effective provider identity are
6378
+ separate. Effective identity is null when not reported. A gateway's hidden retries
6379
+ are not visible as additional attempts.
6380
+
6381
+ `currentModelInvocationAttempt()` returns the causal context inside a provider's
6382
+ `fetch` callback or streamed read. Use it to join a host transport audit to
6383
+ `operationId`, `invocationId`, `attemptId`, optional `runId` and `step`.
6384
+ `onAttempt` is an awaited admission hook; a rejected hook prevents the provider call.
6385
+ SDK payload hashes are not represented as hashes of HTTP wire bytes.
6386
+
6387
+ Prompts and output are AES-256-GCM payload artifacts. `read` returns references and
6388
+ hashes; `readPayload({ conversationId, artifactId }, context)` separately authorizes
6389
+ `read-payload` and checks authentication tags and hashes. `payloadKey` must be 32
6390
+ bytes and is copied at construction. Keep it outside the event store/backups; retain
6391
+ the appropriate key when restoring an archive. `maxPayloadBytes` defaults to 4 MiB
6392
+ (range 1 KiB–64 MiB); streaming output evidence is capped at 1 MiB. Request headers
6393
+ are excluded. Protect existing agent history independently: enabling receipts does
6394
+ not encrypt its ordinary conversation records.
6395
+
6396
+ The built-in memory and SQLite stores implement `appendEventOnce`. Custom stores
6397
+ must implement its once-only check and append atomically, returning the original
6398
+ event on a duplicate key scoped by conversation and kind. A factory without this
6399
+ capability refuses receipt setup. The driver-backed implementation reads bounded
6400
+ pages under one transaction, without another table or database.
6401
+
6402
+ ### Migration from direct SDK completions
6403
+
6404
+ **Who must act:** applications opting into shared completion/agent receipts.
6405
+ Replace the direct `generateText` call with `invocations.complete`, configure host
6406
+ authorization and a retained payload key, and pass the same ledger in the agent's
6407
+ `invocations` option. Use the returned invocation ID and the paginated `read` method
6408
+ for receipt queries; do not import runtime internals. This API is unreleased; it is
6409
+ not present in 0.99.0. Existing runtime configurations need no migration.
6410
+
6275
6411
 
6276
6412
  ==============================================================================
6277
6413
  # Guide: Managed application kernel (docs/guide/application-kernel.md)
@@ -6921,7 +7057,12 @@ refusals are explicit; accepted ordered frames are never evicted.
6921
7057
  The absolute path's parent must already exist and be operator-controlled. One manager owns it via
6922
7058
  an exclusive `.lock`; new files use mode `0600` by default. `maxFiles` includes the active file,
6923
7059
  and a non-newline startup tail is rotated intact rather than guessed or repaired. An abrupt process
6924
- death may leave the lock for an operator to remove only after proving the former owner is gone.
7060
+ death may leave a lock. The default `refuse` policy leaves recovery to the operator;
7061
+ `lock: 'reclaim-stale'` compares machine, boot and process birth identity before
7062
+ recovery. A reused PID cannot impersonate a modern owner. Legacy records with a
7063
+ live PID, foreign namespaces and unavailable identity refuse automatic recovery;
7064
+ see the upgrading guide for quiescent legacy recovery. The same mechanism owns the
7065
+ short-lived reclaim guard.
6925
7066
 
6926
7067
  `flush()` means every accepted append through that call's boundary settled. It is not `fsync`, a
6927
7068
  durable receipt, exactly-once execution or remote delivery. Timeout/cancellation bound only the
@@ -7252,6 +7393,55 @@ holder — after which the heartbeat wins and the lock is abandoned. Temporary f
7252
7393
  the store's first update. Ledger corruption may be declared reconstructable;
7253
7394
  an outbox must fail closed rather than silently discard pending delivery.
7254
7395
 
7396
+ ## Recovering change subscriptions
7397
+
7398
+ `changeSubscriptionResource` is an optional resource over a consumer-owned driver.
7399
+ It uses a finite declared key set, subscribes before reading, reconciles after
7400
+ reconnect and periodically repairs lost hints. `reconcileIntervalMs` is explicit;
7401
+ notifications alone cannot prove freshness. Within a key, hints coalesce into one
7402
+ trailing read and a failed read retries with backoff. Use the supplied
7403
+ `context.commit(() => publish(value))` after awaits to reject an obsolete connection.
7404
+
7405
+ Drivers implement `connect(signal)` and return `subscribe({ signal, hint,
7406
+ disconnected })` plus `close()`. Subscribe must establish the notification boundary
7407
+ before resolving. A database LISTEN driver owns its connection, channels, decoding
7408
+ and transaction ordering; no SQL or database driver is included here. The resource
7409
+ owns reconnect, deadlines and shutdown. Drivers must honor abort. If one ignores it,
7410
+ its slot remains occupied until settlement; deadlines cannot safely manufacture
7411
+ successful cleanup or permit an overlapping connection.
7412
+
7413
+ For watched reads, set `reconcileIntervalMs` on the existing `createWatchHub` rather
7414
+ than building a second read loop. Its per-key machinery is shared with managed
7415
+ subscriptions. Limits default to 1024 sources and 1024 subscribers per hub; set
7416
+ explicit finite values for larger workloads. Supply a server-verified admission
7417
+ scope to `attach` and abort it on logout, permission revocation or session replacement.
7418
+ The read callback receives the trusted scope as its third argument. A client owner
7419
+ ID in arguments is not authentication.
7420
+
7421
+ ## Multi-action notification delivery
7422
+
7423
+ Opt into `delivery: { version, actions, project }` on `createNotificationOutbox`.
7424
+ The pure `actions(payload)` selector runs at enqueue; the versioned plan remains
7425
+ fixed throughout retry. Each `send` receives its action and a stable idempotency
7426
+ key; return a small JSON receipt such as a remote message or uploaded object ID.
7427
+ `project(notification, receipts)` applies all confirmed results locally and must
7428
+ be idempotent. Checkpoints and the final projection timestamp live in the existing
7429
+ atomic StateStore; completed receipts retain them within retention/byte limits.
7430
+
7431
+ This supports, for example, text plus attachment delivery with a local timeline
7432
+ projection, or upload plus publication with a local asset index. These are protocol
7433
+ examples, not claims of deployed consumer adoption. A failure in projection does
7434
+ not repeat confirmed sends. A crash after remote success but before receipt commit
7435
+ is ambiguous: the provider must deduplicate the action key or reconcile its result.
7436
+ A projection can also repeat if its completion checkpoint is lost.
7437
+
7438
+ A new executor version must explicitly migrate or terminally classify incompatible
7439
+ queued plans. Existing `classify`/`onDropped` handles terminal items; a durable
7440
+ quarantine is the application's responsibility. Whole-state file and memory stores
7441
+ remain supported. Atomic enqueue with an unrelated business transaction is not
7442
+ provided by this contract; retain the application's transactional adapter when that
7443
+ boundary is required. Send and projection callbacks own their external deadlines.
7444
+
7255
7445
 
7256
7446
  ==============================================================================
7257
7447
  # Guide: Optional process sandbox (docs/guide/sandbox.md)
@@ -9727,6 +9917,41 @@ through `emitTo`; a broadcast through `realtime.to(room).emit` reaches the
9727
9917
  socket immediately, replay or not, so keep room traffic whose order matters
9728
9918
  relative to the snapshot on the registry.
9729
9919
 
9920
+ ## Session-scoped watches and query cache
9921
+
9922
+ Pass the captured login operation as `createWatchClient(contract, { transport,
9923
+ session })`. It adds a lifetime nonce to watch keys and detaches on invalidation;
9924
+ a late frame for a previous login cannot match a new client's key on the same socket.
9925
+ Both client and server must support the new optional `WatchKey.instance` field.
9926
+ The nonce is not authorization: the server still admits a verified identity and
9927
+ uses `hub.attach(subscriber, { key: verifiedScope, signal: revocationSignal })`.
9928
+ The scope key must change with session and permission boundaries.
9929
+
9930
+ `createCacheBridge` can bind watched handles directly:
9931
+
9932
+ ```ts
9933
+ const bridge = createCacheBridge({
9934
+ socket, queryClient, handlers: {}, session,
9935
+ watched: [{
9936
+ handle: watches.list({ folderId }),
9937
+ queryKey: ['items', session.id, folderId],
9938
+ state: frame => showWatchState(frame),
9939
+ }],
9940
+ })
9941
+ bridge.connect()
9942
+ ```
9943
+
9944
+ A value updates that exact query; non-live state marks it stale while retaining its
9945
+ value. The existing phases (`opening`, `live`, `unavailable`, `resync-required`)
9946
+ remain the status model. Do not replace unavailable data with an empty collection.
9947
+ Session invalidation removes bound queries. Include the session identity in query
9948
+ keys and guard any application-level side effects with that same operation.
9949
+
9950
+ Use existing shared watches for paginated lists and selected-detail queries; bind
9951
+ each exact argument set to its query key. No global domain/query registry or new
9952
+ UI components are needed. Reconnect and revision/delta recovery remain owned by the
9953
+ watch client; silent lost notifications are repaired by hub reconciliation.
9954
+
9730
9955
 
9731
9956
  ==============================================================================
9732
9957
  # Guide: Live data (docs/guide/live.md)
@@ -13034,6 +13259,99 @@ analytics policy belong to the application.
13034
13259
 
13035
13260
  # Upgrading stitchkit
13036
13261
 
13262
+ ## Released migration: 0.100.0
13263
+
13264
+ ### watch capacity and session/live/delivery adoption
13265
+
13266
+ **Who must act:** watch consumers exceeding 1024 hub sources, 1024 hub subscribers,
13267
+ 1024 client keys or 128 listeners per key. These previously unbounded collections
13268
+ now refuse excess admission. Choose explicit positive finite limits, for example:
13269
+
13270
+ ```ts
13271
+ // Before: no hub-wide capacity bounds.
13272
+ createWatchHub({ read, watchable });
13273
+ // After: explicitly budget a larger workload.
13274
+ createWatchHub({ read, watchable, maxSources: 4096, maxSubscribers: 4096 });
13275
+ // Client limits are configured independently.
13276
+ createWatchClient({ ...clientConfig, maxKeys: 2048, maxListenersPerKey: 256 });
13277
+ ```
13278
+
13279
+ The remaining steps apply only to applications opting into session lifetimes,
13280
+ scoped watched reads, managed subscriptions or multi-action notification delivery.
13281
+ Existing clients within the limits and single-send outboxes need no migration.
13282
+
13283
+ Replace local session generation counters with one `createSessionScope`; share it
13284
+ with `createSessionCredentials`. Guard transport attempts, complete asynchronous
13285
+ operations and final synchronous delivery. Route credential persistence through
13286
+ one coordinator. Keep provider-specific error classification and bounded command
13287
+ retry in the application. Coordination is per client instance.
13288
+
13289
+ Upgrade both watch endpoints before enabling session-scoped watches: strict older
13290
+ servers reject the new optional `WatchKey.instance`. Server-verified scope controls
13291
+ sharing; the client instance is only routing identity. Bind exact watched handles
13292
+ to session-qualified cache keys. Existing delta/reconnect behavior stays in place.
13293
+ For applications needing more than 1024 hub sources/subscribers or client keys, set
13294
+ explicit finite capacity options. Enable a reconciliation interval when lost hints
13295
+ must recover without a reconnect.
13296
+
13297
+ For multi-action outboxes, enqueue new items with a versioned delivery plan and
13298
+ return small JSON receipts from `send`. Existing queued single-send items should
13299
+ be drained before enabling a delivery executor on that queue, or migrated under
13300
+ the store's atomic transition. Preserve executor versions until their queues drain.
13301
+ Make projection idempotent. Provider idempotency/reconciliation remains necessary
13302
+ for a crash between remote success and checkpoint. These APIs are available starting with 0.100.0.
13303
+
13304
+ ### exclusive lock recovery
13305
+
13306
+ New lock and reclaim-guard records include `owner.process`: kernel boot identity,
13307
+ process birth token and namespace. `readDiagnosticJournalLockDiagnosis(error)`
13308
+ adds `identity` to distinguish matched lifetime evidence, PID reuse, a different
13309
+ boot, an absent PID, a legacy record or unavailable identity. Legacy `alive` only
13310
+ means the PID is present; it does not prove that the recorded writer still exists.
13311
+
13312
+ **Who must act:** deployments recovering a legacy lock with a reused live PID,
13313
+ or bundling file-lock consumers into a standalone Darwin executable. Legacy
13314
+ records cannot safely infer their missing boot/start identity from `acquiredAt`.
13315
+ Stop all writers to that resource, verify exclusive ownership, preserve the old
13316
+ lock for incident evidence outside the active lock path, and restart with the
13317
+ new package. Never delete a lock merely because it is old. New acquisitions
13318
+ write the modern record; no journal data migration is needed.
13319
+
13320
+ Darwin distributions must retain the package's architecture-matched native
13321
+ `.node` asset for process birth measurement, as for contained filesystem access.
13322
+ If native/proc identity cannot be read, new locks explicitly record null and
13323
+ recovery refuses rather than treating unknown ownership as death. Existing
13324
+ `refuse` policy and the separately configured ownerless-file grace are unchanged.
13325
+
13326
+ ## Released migration: 0.99.0
13327
+
13328
+ ### structural alternatives in flattened tool schemas
13329
+
13330
+ **Who must act:** applications using `flattenUnionInput: true` with schema
13331
+ walkers, snapshots or provider restrictions that assume there is no nested
13332
+ `anyOf`. Applications without that assumption need only update the dependency.
13333
+
13334
+ Colliding object/array fields retain their complete structural alternatives:
13335
+
13336
+ ```ts
13337
+ // Before: loses both field vocabulary and array items.
13338
+ { type: ['object', 'array'] }
13339
+ // After: structural alternatives live inside the property schema.
13340
+ { anyOf: [
13341
+ { type: 'object', properties: { id: { type: 'string' } }, required: ['id'] },
13342
+ { type: 'array', items: {
13343
+ type: 'object', properties: { id: { type: 'string' } }, required: ['id'],
13344
+ } },
13345
+ ] }
13346
+ ```
13347
+
13348
+ Traverse each nested alternative when inspecting schemas and regenerate schema
13349
+ snapshots. Verify the actual manifest with the chosen provider. Keep flatten
13350
+ enabled when a top-level discriminated union needs to be advertised as an
13351
+ object; do not erase `items` or introduce consumer-specific schema patches.
13352
+ The root object, discriminator enum, requiredness hints and strict runtime
13353
+ contract validation are unchanged. See ADR 0205.
13354
+
13037
13355
  ## Released migration: 0.98.0
13038
13356
 
13039
13357
  ### automatic stdin availability
@@ -16970,6 +17288,28 @@ can emit / propagate a `traceparent` (see `HttpClientConfig.trace`).
16970
17288
 
16971
17289
  ---
16972
17290
 
17291
+ ### Session lifetime and credential coordination
17292
+
17293
+ `createSessionScope` (`stitchkit`) returns the generic `SessionScope`: `replace(context)`
17294
+ creates a new login lifetime (including same-account relogin), `capture()` returns
17295
+ its `SessionOperation<T>`, `clear()` invalidates it, and `stop()` permanently closes
17296
+ the host. Operations expose `id`, `generation`, `context`, `signal`, `current()` and
17297
+ `assertCurrent()`. `run(callback)` checks both success and failure across an await;
17298
+ `deliver(callback)` admits a synchronous final send/state change or returns false.
17299
+ `bindFetch(fetch, headers?)` guards every attempt and scopes its abort signal. Wrap
17300
+ the complete client call in `run` too: headers are not the response body boundary.
17301
+ Expired work throws `SessionExpiredError`, not a refresh instruction.
17302
+
17303
+ `createSessionCredentials` accepts `SessionCredentialsConfig`:
17304
+ `scope`, `refresh(credentials, signal)`, `storage.write(session, credentials)`,
17305
+ `storage.clear()`, `maxPendingWrites` (128), and `refreshTimeoutMs` (30000, at most one
17306
+ hour). `SessionCredentials` provides `login`, `logout`, `read(operation)` and
17307
+ `refresh(operation)`. Concurrent refresh calls in one generation share the same
17308
+ promise. Logout invalidates memory before its returned persistence promise settles.
17309
+ Storage errors and queue-capacity refusals reject; callers must handle them. All
17310
+ writes must pass through the same instance. There is no cross-process/tab lock,
17311
+ provider error classifier or automatic retry of arbitrary application callbacks.
17312
+
16973
17313
  ## `stitchkit/contract`
16974
17314
 
16975
17315
  The contract layer alone — browser-and-server safe. All of this is also exported
@@ -17110,6 +17450,13 @@ realtime contract from `stitchkit`, and the server halves live in `stitchkit/app
17110
17450
 
17111
17451
  ---
17112
17452
 
17453
+ ### Session-scoped watch lifetime
17454
+
17455
+ `WatchClientConfig.session` captures a `SessionOperation`; `maxKeys` (1024) and
17456
+ `maxListenersPerKey` (128) bound retained subscriptions. Its lifetime nonce travels
17457
+ in optional `WatchKey.instance`, preventing old-session frames from matching a new
17458
+ client on the same socket. This is routing identity, never authorization.
17459
+
17113
17460
  ## `stitchkit/primitives`
17114
17461
 
17115
17462
  Browser-and-server-safe declarations for facts an application wants to define once. See the
@@ -17498,7 +17845,7 @@ cutovers are covered by the executable
17498
17845
  | `DiagnosticJournalConfig` / `DiagnosticJournal` | _type_ | owner schema/path/limits/failure observer and the synchronous `submit`, bounded-wait `flush`/`close`, status handle |
17499
17846
  | `DiagnosticJournalLimitsSchema` / `DiagnosticJournalLimits` | schema / _type_ | positive event, pending-item, pending-byte, file-byte and retained-file limits |
17500
17847
  | `DiagnosticJournalLockPolicySchema` / `DiagnosticJournalLockPolicy` | schema / _type_ | `refuse` (default) or `reclaim-stale`, which reclaims only a lock whose recorded owner is provably gone |
17501
- | `readDiagnosticJournalLockDiagnosis` / `DiagnosticJournalLockDiagnosis` | function / _type_ | why a `reclaim-stale` acquisition refused — owner alive, another machine, or a lock this host cannot attribute — read off the thrown `EEXIST` |
17848
+ | `readDiagnosticJournalLockDiagnosis` / `DiagnosticJournalLockDiagnosis` | function / _type_ | why a `reclaim-stale` acquisition refused — attribution, PID liveness and optional process `identity` evidence (`matched`, `different-boot`, `reused-pid`, `pid-gone`, `legacy`, `unavailable`) — read off the thrown `EEXIST` |
17502
17849
  | `DiagnosticJournalSubmitResultSchema` / `DiagnosticJournalSubmitResult` | schema / _type_ | accepted epoch/sequence or explicit invalid, oversized, capacity, closed or failed refusal |
17503
17850
  | `DiagnosticJournalStatusSchema` / `DiagnosticJournalStatus` | schema / _type_ | state, limits, exact admission/write/failure counters, pending ownership, rotations, partial tails and last safe sequences |
17504
17851
  | `DiagnosticJournalFrameSchema` / `DiagnosticJournalFrame` | schema / _type_ | version-1 JSONL frame carrying process epoch, contiguous accepted sequence and schema-validated JSON event |
@@ -17601,6 +17948,44 @@ vocabulary with an event topic declared `mode: 'decision'`.
17601
17948
  → [ADR 0152](../decisions/0152-a-keyspace-is-memory-that-nothing-reaches-before-it-is-durable.md),
17602
17949
  [ADR 0153](../decisions/0153-a-watched-read-is-one-read-per-question.md).
17603
17950
 
17951
+ ### Managed change subscriptions and scoped watches
17952
+
17953
+ `changeSubscriptionResource` (`stitchkit/application`) accepts
17954
+ `ChangeSubscriptionConfig`: `id`, unique `keys` (1–128, at most 256 characters each),
17955
+ `connect(signal)`, `reconcile(key, context)`, required `reconcileIntervalMs` (10 ms–1 h),
17956
+ optional `timeoutMs` (30000; 1 ms–1 h), `backoff`, and `onError`.
17957
+ `ChangeConnection` supplies `subscribe({ signal, hint, disconnected })`, which completes subscription
17958
+ before the first read; `close()` releases it. `ChangeReconciliation` exposes `signal`
17959
+ and a synchronous `commit(callback)` guard against late publication. Drivers must
17960
+ honor cancellation. Uncooperative work retains its slot until settled instead of
17961
+ allowing overlapping connections/reads. Errors reach `onError`; no raw payload or
17962
+ connection string is logged by this resource. Recovery runs after reconnect and
17963
+ periodically even when no notification arrives.
17964
+
17965
+ `WatchHubConfig` adds `maxSources` (1024), `maxSubscribers` (1024), and optional
17966
+ `reconcileIntervalMs` (10 ms–1 h). `hub.attach(subscriber, scope?)` accepts a
17967
+ `WatchAdmissionScope` containing a **server-verified** `key` (1–512 characters) and
17968
+ revocation `signal`. The key separates sharing and is the optional third argument
17969
+ of `read(operation, args, scope)`. It must represent account, session and permissions;
17970
+ never trust a client-supplied owner ID as that scope.
17971
+
17972
+
17973
+
17974
+ ### Notification action and projection receipts
17975
+
17976
+ `NotificationOutboxConfig.delivery` accepts `NotificationDeliveryConfig`:
17977
+ immutable executor `version`, pure `actions(payload)` plan selector (1–128 unique
17978
+ names, each at most 128 characters), and idempotent `project(notification, receipts)`.
17979
+ `send` receives optional `action` and stable `idempotencyKey`, and returns a JSON
17980
+ remote receipt (void becomes null). `NotificationDeliveryStateSchema` validates the
17981
+ optional `NotificationDeliveryState` on queued items and completed receipts:
17982
+ `version`, `actions`, `receipts` and optional `projectedAt`. Checkpoints share the
17983
+ existing atomic StateStore and lease fence. Receipt retention and serialized byte
17984
+ budgets still apply. Single-send configuration remains valid without `delivery`.
17985
+ A remote result lost before its checkpoint may be sent again; provider idempotency
17986
+ or reconciliation is required. A projection may repeat after a crash before its
17987
+ checkpoint. Business-transaction enqueue and durable quarantine are not implied.
17988
+
17604
17989
  ## `stitchkit/application/grammy`
17605
17990
 
17606
17991
  Isolated optional-peer lifecycle adapters. Importing `stitchkit/application`
@@ -17933,6 +18318,30 @@ Managed effects and operator telemetry additionally export `AgentToolFenceConfig
17933
18318
  may accompany checkpoint/terminal writes and tool context; internal causes are redacted unless an
17934
18319
  operator-only observability sink explicitly opts in.
17935
18320
 
18321
+ ### Completion and agent invocation receipts
18322
+
18323
+ `createModelInvocationLedger` constructs a `ModelInvocationLedger` from
18324
+ `ModelInvocationConfig`: the existing store and model registry, a 32-byte payload
18325
+ key, required host authorization, optional bounded payload size and `onAttempt`.
18326
+ `complete(input, context, signal?)` executes only the supplied prompt, with no tools
18327
+ or framework instructions; repeated keys return the existing invocation ID.
18328
+ `read(input, context)` returns a bounded event page shared by agent and completion;
18329
+ `readPayload(input, context)` requires a separate authorization action to decrypt
18330
+ one protected artifact. See the [guide and migration example](../guide/agent-runtime.md#plain-completions-and-shared-invocation-receipts).
18331
+
18332
+ | Export | Kind | Purpose |
18333
+ | --- | --- | --- |
18334
+ | `CompletionInvocationInputSchema` / `CompletionInvocationInput` | schema/type | strict prompt, model-attempt list, deadline, idempotency key and trace input |
18335
+ | `ModelInvocationTraceSchema` / `ModelInvocationTrace` | schema/type | operation, purpose, project, parent trace and optional experiment/case/profile |
18336
+ | `ModelInvocationRecordSchema` / `ModelInvocationRecord` | schema/type | discriminated invocation start/finish and per-attempt requested/sent/effective evidence |
18337
+ | `ModelInvocationAttemptContext` | type | conversation, operation, invocation, attempt, step and optional agent run identity |
18338
+ | `currentModelInvocationAttempt` | function | returns causal IDs inside the provider transport; undefined outside an invocation |
18339
+
18340
+ The runtime `invocations` option takes this ledger and a trace callback; it requires
18341
+ the same store and preserves the existing tool loop. `AgentRuntimeStore.appendEventOnce`
18342
+ is an optional atomic capability required by receipt admission. Built-in stores
18343
+ supply it; custom adapters must honor its once-only transaction contract.
18344
+
17936
18345
  ## `stitchkit/agent-runtime/testing`
17937
18346
 
17938
18347
  Credential-free provider and concurrency fixtures for Bun and Node tests.
@@ -18609,7 +19018,7 @@ available from `stitchkit/contract`.
18609
19018
  | `ManagedFileInspector` | _type_ | bounded-prefix read/write inspection callback with a finite cancellation signal that cannot own path or size |
18610
19019
  | `ManagedFileInspectionInput` / `ManagedFileInspection` | _type_ | inspector prefix/name/declared media/signal input and validated metadata-only result |
18611
19020
  | `writeFileAtomic` / `writeFileAtomicSync` / `WriteFileAtomicOptions` | function / _type_ | replace a file atomically: a random staging name created exclusively (never through a planted link), the mode set on the descriptor before the file is visible (default `0o600`, not masked by the umask), `fsync`, rename; a failure leaves the target and no staging file. The asynchronous form keeps the event loop running |
18612
- | `withExclusiveLock` / `ExclusiveLockOptions` / `ExclusiveLock` / `ExclusiveLockOwner` | function / _type_ | run work under an exclusive lock between processes — a file recording its owner (pid, host, machine identity, time); waits up to `timeoutMs` (default 10 s) and stops on `signal`; a dead owner on this machine is taken over, a live, slow or foreign one never is; an ownerless lock only after `ownerlessGraceMs` |
19021
+ | `withExclusiveLock` / `ExclusiveLockOptions` / `ExclusiveLock` / `ExclusiveLockOwner` | function / _type_ | run work under an exclusive lock between processes — a file recording its owner (pid, host, machine identity, time and nullable kernel process identity); waits up to `timeoutMs` (default 10 s) and stops on `signal`; a dead owner on this machine is taken over, a live, slow or foreign one never is; an ownerless lock only after `ownerlessGraceMs` |
18613
19022
  | `ExclusiveLockError` | class | `LOCK_TIMEOUT` naming the resource and its holder, or `LOCK_ABORTED` with the signal's reason |
18614
19023
 
18615
19024
  ---
@@ -18989,6 +19398,14 @@ For the rationale behind these APIs — why `Bun.serve` and not a framework, why
18989
19398
  two context types, why thin wrappers — see the
18990
19399
  [Architecture Decisions](../decisions/).
18991
19400
 
19401
+ ### Watched handles in the query cache
19402
+
19403
+ `CacheBridgeConfig.watched` accepts `WatchCacheBinding` entries with `handle`,
19404
+ `queryKey` and optional `state(frame)` observer. Include the session in query keys.
19405
+ `CacheBridgeConfig.session` fences events and removes bound queries when that login
19406
+ ends. Watch loss marks a query stale without triggering an independent refetch;
19407
+ recovery belongs to the watched read.
19408
+
18992
19409
  ## `stitchkit/agent-runtime/sandbox`
18993
19410
 
18994
19411
  Optional server-only namespace sandbox. Exports:
package/llms.txt CHANGED
@@ -9,10 +9,10 @@ Build with stitchkit: define a contract once, then `implement` it and serve it (
9
9
  Load the slice for each entrypoint your code imports — `import … from 'stitchkit/server'` → `llms/server.txt`; the root `stitchkit` import → `llms/stitchkit.txt`. A slice holds that entrypoint's API reference section and the guide pages whose home it is; every guide lives in exactly one slice, and a slice that needs another's guide names it under "Also read". Each file is at most 50.0 KB; a longer one continues in numbered parts, each naming the next at its top. Moving across versions: load `llms/upgrading.txt` and the range slices it lists between your version and the target. `llms-full.txt` inlines everything; it is far larger than a context window, so do not read it whole.
10
10
 
11
11
  ## Entrypoint slices
12
- - **stitchkit**: [llms/stitchkit.txt](llms/stitchkit.txt) 15.6 KB → [llms/stitchkit.2.txt](llms/stitchkit.2.txt) 35.0 KB → [llms/stitchkit.3.txt](llms/stitchkit.3.txt) 14.2 KB
13
- - **stitchkit/live**: [llms/live.txt](llms/live.txt) 21.2 KB
14
- - **stitchkit/react**: [llms/react.txt](llms/react.txt) 8.6 KB
15
- - **stitchkit/tools**: [llms/tools.txt](llms/tools.txt) 44.9 KB → [llms/tools.2.txt](llms/tools.2.txt) 26.3 KB → [llms/tools.3.txt](llms/tools.3.txt) 25.4 KB
12
+ - **stitchkit**: [llms/stitchkit.txt](llms/stitchkit.txt) 15.6 KB → [llms/stitchkit.2.txt](llms/stitchkit.2.txt) 37.2 KB → [llms/stitchkit.3.txt](llms/stitchkit.3.txt) 15.7 KB
13
+ - **stitchkit/live**: [llms/live.txt](llms/live.txt) 21.6 KB
14
+ - **stitchkit/react**: [llms/react.txt](llms/react.txt) 9.0 KB
15
+ - **stitchkit/tools**: [llms/tools.txt](llms/tools.txt) 45.0 KB → [llms/tools.2.txt](llms/tools.2.txt) 26.3 KB → [llms/tools.3.txt](llms/tools.3.txt) 25.4 KB
16
16
  - **stitchkit/tools/contract**: [llms/tools-contract.txt](llms/tools-contract.txt) 0.2 KB
17
17
  - **stitchkit/tools/invoker**: [llms/tools-invoker.txt](llms/tools-invoker.txt) 0.2 KB
18
18
  - **stitchkit/tools/connections**: [llms/tools-connections.txt](llms/tools-connections.txt) 3.1 KB
@@ -20,9 +20,9 @@ Load the slice for each entrypoint your code imports — `import … from 'stitc
20
20
  - **stitchkit/remote**: [llms/remote.txt](llms/remote.txt) 1.0 KB
21
21
  - **stitchkit/contract**: [llms/contract.txt](llms/contract.txt) 31.9 KB
22
22
  - **stitchkit/primitives**: [llms/primitives.txt](llms/primitives.txt) 9.3 KB
23
- - **stitchkit/server**: [llms/server.txt](llms/server.txt) 45.3 KB → [llms/server.2.txt](llms/server.2.txt) 14.6 KB → [llms/server.3.txt](llms/server.3.txt) 42.7 KB → [llms/server.4.txt](llms/server.4.txt) 37.5 KB → [llms/server.5.txt](llms/server.5.txt) 21.8 KB
23
+ - **stitchkit/server**: [llms/server.txt](llms/server.txt) 45.3 KB → [llms/server.2.txt](llms/server.2.txt) 14.6 KB → [llms/server.3.txt](llms/server.3.txt) 44.3 KB → [llms/server.4.txt](llms/server.4.txt) 37.5 KB → [llms/server.5.txt](llms/server.5.txt) 21.8 KB
24
24
  - **stitchkit/observability**: [llms/observability.txt](llms/observability.txt) 43.4 KB
25
- - **stitchkit/agent-runtime**: [llms/agent-runtime.txt](llms/agent-runtime.txt) 36.4 KB → [llms/agent-runtime.2.txt](llms/agent-runtime.2.txt) 41.3 KB → [llms/agent-runtime.3.txt](llms/agent-runtime.3.txt) 32.0 KB
25
+ - **stitchkit/agent-runtime**: [llms/agent-runtime.txt](llms/agent-runtime.txt) 36.4 KB → [llms/agent-runtime.2.txt](llms/agent-runtime.2.txt) 46.1 KB → [llms/agent-runtime.3.txt](llms/agent-runtime.3.txt) 33.8 KB
26
26
  - **stitchkit/agent-runtime/testing**: [llms/agent-runtime-testing.txt](llms/agent-runtime-testing.txt) 2.2 KB
27
27
  - **stitchkit/agent-runtime/harness**: [llms/agent-runtime-harness.txt](llms/agent-runtime-harness.txt) 4.1 KB
28
28
  - **stitchkit/agent-runtime/coding-tools**: [llms/agent-runtime-coding-tools.txt](llms/agent-runtime-coding-tools.txt) 4.9 KB
@@ -30,7 +30,7 @@ Load the slice for each entrypoint your code imports — `import … from 'stitc
30
30
  - **stitchkit/agent-runtime/openrouter**: [llms/agent-runtime-openrouter.txt](llms/agent-runtime-openrouter.txt) 1.1 KB
31
31
  - **stitchkit/agent-runtime/sqlite/bun**: [llms/agent-runtime-sqlite-bun.txt](llms/agent-runtime-sqlite-bun.txt) 1.8 KB
32
32
  - **stitchkit/agent-runtime/sqlite/node**: [llms/agent-runtime-sqlite-node.txt](llms/agent-runtime-sqlite-node.txt) 1.7 KB
33
- - **stitchkit/application**: [llms/application.txt](llms/application.txt) 44.6 KB → [llms/application.2.txt](llms/application.2.txt) 32.9 KB
33
+ - **stitchkit/application**: [llms/application.txt](llms/application.txt) 46.6 KB → [llms/application.2.txt](llms/application.2.txt) 37.2 KB
34
34
  - **stitchkit/application/grammy**: [llms/application-grammy.txt](llms/application-grammy.txt) 2.3 KB
35
35
  - **stitchkit/application/opentelemetry**: [llms/application-opentelemetry.txt](llms/application-opentelemetry.txt) 1.6 KB
36
36
  - **stitchkit/application/directory-inbox**: [llms/application-directory-inbox.txt](llms/application-directory-inbox.txt) 0.3 KB
@@ -52,9 +52,10 @@ Load the slice for each entrypoint your code imports — `import … from 'stitc
52
52
 
53
53
  ## Upgrading slices
54
54
  - **Upgrading — how to move across versions**: [llms/upgrading.txt](llms/upgrading.txt) 10.3 KB
55
- - **Upgrading 0.72.0 → 0.98.0**: [llms/upgrading-0.72.0-0.98.0.txt](llms/upgrading-0.72.0-0.98.0.txt) 44.8 KB
56
- - **Upgrading 0.59.0 → 0.71.0**: [llms/upgrading-0.59.0-0.71.0.txt](llms/upgrading-0.59.0-0.71.0.txt) 45.6 KB
57
- - **Upgrading 0.37.0 → 0.58.0**: [llms/upgrading-0.37.0-0.58.0.txt](llms/upgrading-0.37.0-0.58.0.txt) 40.0 KB
55
+ - **Upgrading 0.74.0 → 0.100.0**: [llms/upgrading-0.74.0-0.100.0.txt](llms/upgrading-0.74.0-0.100.0.txt) 44.3 KB
56
+ - **Upgrading 0.61.0 → 0.73.0**: [llms/upgrading-0.61.0-0.73.0.txt](llms/upgrading-0.61.0-0.73.0.txt) 41.2 KB
57
+ - **Upgrading 0.44.0 → 0.60.0**: [llms/upgrading-0.44.0-0.60.0.txt](llms/upgrading-0.44.0-0.60.0.txt) 43.9 KB
58
+ - **Upgrading 0.37.0 → 0.37.0**: [llms/upgrading-0.37.0.txt](llms/upgrading-0.37.0.txt) 6.2 KB
58
59
 
59
60
  ## Guide (web)
60
61
  - [Getting started](https://github.com/max-listov/stitchkit/blob/master/docs/guide/getting-started.md): install, entrypoints, and a first contract → server → client app
Binary file
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stitchkit",
3
- "version": "0.98.2",
3
+ "version": "0.100.0",
4
4
  "description": "Contract-first backend framework — one defineContract() into an HTTP API, MCP tools, AI-agent tools and a typed client. Bun and Node.",
5
5
  "keywords": [
6
6
  "bun",