stitchkit 0.118.1 → 0.120.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 (180) hide show
  1. package/CHANGELOG.md +92 -0
  2. package/README.md +10 -7
  3. package/dist/browser/http.d.ts.map +1 -1
  4. package/dist/contract/client-types.d.ts +6 -3
  5. package/dist/contract/client-types.d.ts.map +1 -1
  6. package/dist/contract/define-validation.d.ts.map +1 -1
  7. package/dist/contract/define.d.ts +18 -12
  8. package/dist/contract/define.d.ts.map +1 -1
  9. package/dist/contract/endpoint-capabilities.d.ts +28 -0
  10. package/dist/contract/endpoint-capabilities.d.ts.map +1 -0
  11. package/dist/contract/factory.d.ts +49 -19
  12. package/dist/contract/factory.d.ts.map +1 -1
  13. package/dist/contract/tool-view.d.ts +1 -1
  14. package/dist/contract/tool-view.d.ts.map +1 -1
  15. package/dist/contract/transports.d.ts +5 -0
  16. package/dist/contract/transports.d.ts.map +1 -0
  17. package/dist/entrypoints/agent-runtime/coding-tools.js +9 -8
  18. package/dist/entrypoints/agent-runtime/harness.js +9 -8
  19. package/dist/entrypoints/agent-runtime/realtime.js +3 -3
  20. package/dist/entrypoints/agent-runtime/sandbox.js +10 -9
  21. package/dist/entrypoints/agent-runtime/sqlite/bun.js +12 -11
  22. package/dist/entrypoints/agent-runtime/sqlite/node.js +12 -11
  23. package/dist/entrypoints/agent-runtime/testing.js +12 -11
  24. package/dist/entrypoints/agent-runtime.js +22 -19
  25. package/dist/entrypoints/application/diagnostic-journal.js +3 -3
  26. package/dist/entrypoints/application/directory-inbox.js +4 -4
  27. package/dist/entrypoints/application/opentelemetry.js +1 -1
  28. package/dist/entrypoints/application.js +10 -10
  29. package/dist/entrypoints/cli/publish.js +4 -4
  30. package/dist/entrypoints/cli.js +13 -12
  31. package/dist/entrypoints/contract.d.ts +1 -1
  32. package/dist/entrypoints/contract.d.ts.map +1 -1
  33. package/dist/entrypoints/contract.js +6 -4
  34. package/dist/entrypoints/files.js +3 -3
  35. package/dist/entrypoints/geo/contract.d.ts +7 -0
  36. package/dist/entrypoints/geo/contract.d.ts.map +1 -0
  37. package/dist/entrypoints/geo/contract.js +6 -0
  38. package/dist/entrypoints/geo.js +60 -23
  39. package/dist/entrypoints/http.js +3 -3
  40. package/dist/entrypoints/index.js +9 -7
  41. package/dist/entrypoints/live.js +1 -1
  42. package/dist/entrypoints/node.js +7 -6
  43. package/dist/entrypoints/primitives.js +1 -1
  44. package/dist/entrypoints/process/owner-loss.js +1 -1
  45. package/dist/entrypoints/process.js +3 -3
  46. package/dist/entrypoints/react.js +1 -1
  47. package/dist/entrypoints/remote.js +4 -3
  48. package/dist/entrypoints/server.js +16 -15
  49. package/dist/entrypoints/telegram/screens.js +5 -5
  50. package/dist/entrypoints/telegram.js +7 -7
  51. package/dist/entrypoints/testing.js +7 -6
  52. package/dist/entrypoints/tools/agent.d.ts +9 -0
  53. package/dist/entrypoints/tools/agent.d.ts.map +1 -0
  54. package/dist/entrypoints/tools/agent.js +16 -0
  55. package/dist/entrypoints/tools/connections.js +7 -6
  56. package/dist/entrypoints/tools/contract.js +1 -1
  57. package/dist/entrypoints/tools/invoker.js +5 -4
  58. package/dist/entrypoints/tools/mcp.js +110 -9
  59. package/dist/entrypoints/tools/toolkit.d.ts +9 -0
  60. package/dist/entrypoints/tools/toolkit.d.ts.map +1 -0
  61. package/dist/entrypoints/tools/toolkit.js +40 -0
  62. package/dist/entrypoints/tools.d.ts +0 -10
  63. package/dist/entrypoints/tools.d.ts.map +1 -1
  64. package/dist/entrypoints/tools.js +38 -80
  65. package/dist/entrypoints/tracking/contract.js +5 -3
  66. package/dist/entrypoints/tracking/performance.js +3 -3
  67. package/dist/entrypoints/tracking.d.ts +1 -1
  68. package/dist/entrypoints/tracking.d.ts.map +1 -1
  69. package/dist/entrypoints/tracking.js +63 -83
  70. package/dist/entrypoints/xlsx.js +1 -1
  71. package/dist/geo/index.d.ts +2 -1
  72. package/dist/geo/index.d.ts.map +1 -1
  73. package/dist/geo/record.d.ts +8 -0
  74. package/dist/geo/record.d.ts.map +1 -0
  75. package/dist/geo/resolver.d.ts +1 -3
  76. package/dist/geo/resolver.d.ts.map +1 -1
  77. package/dist/geo/schema.d.ts +44 -0
  78. package/dist/geo/schema.d.ts.map +1 -0
  79. package/dist/geo/types.d.ts +2 -13
  80. package/dist/geo/types.d.ts.map +1 -1
  81. package/dist/{index-n5sv4z0w.js → index-0qyrcjfs.js} +16 -18
  82. package/dist/index-1psbyyhj.js +21 -0
  83. package/dist/{index-tsngj2b5.js → index-2fc1k9k2.js} +206 -206
  84. package/dist/index-3prvb42w.js +30 -0
  85. package/dist/{index-073nw9qr.js → index-3yb5kft5.js} +524 -522
  86. package/dist/{index-d9hq291n.js → index-4jpykzvw.js} +70 -81
  87. package/dist/index-568w258r.js +76 -0
  88. package/dist/index-69t14s4a.js +39 -0
  89. package/dist/{index-dky0dcmf.js → index-72r9k0av.js} +6 -2
  90. package/dist/{index-6p3frkv5.js → index-7zwtbak8.js} +9 -50
  91. package/dist/{index-90p34rts.js → index-8ryydawp.js} +7 -5
  92. package/dist/{index-vv1sch8a.js → index-8vzh5wpv.js} +13 -8
  93. package/dist/{index-75ns2ybj.js → index-8xyf8h2k.js} +1 -1
  94. package/dist/{index-xx1j310k.js → index-8yxgcyt6.js} +171 -169
  95. package/dist/{index-p6apg0jg.js → index-9frf01t9.js} +16 -9
  96. package/dist/{index-q0th8q0z.js → index-9mjrnqxs.js} +1 -3
  97. package/dist/{index-6f49krb5.js → index-aa50qwv4.js} +1 -1
  98. package/dist/{index-ega3yz4c.js → index-aqwg2dm5.js} +1 -1
  99. package/dist/{index-p0rf7sq9.js → index-bgcwxhxh.js} +225 -223
  100. package/dist/index-bph35vpy.js +77 -0
  101. package/dist/{index-tdpmfks8.js → index-bz479f0x.js} +1 -1
  102. package/dist/index-d3xdyjt6.js +38 -0
  103. package/dist/{index-q968hy6y.js → index-e452syyh.js} +1 -1
  104. package/dist/{index-86nbfysb.js → index-ff39f9dr.js} +3 -3
  105. package/dist/index-h1j71dr1.js +48 -0
  106. package/dist/{index-mgb7r8b8.js → index-jswst0h3.js} +1 -1
  107. package/dist/{index-095ya5hm.js → index-knmnj11j.js} +22 -191
  108. package/dist/{index-vwhrnp67.js → index-kz9734jc.js} +3 -3
  109. package/dist/{index-txg1npmg.js → index-mtwh9hyp.js} +1 -1
  110. package/dist/{index-dr3t5n1v.js → index-nn9qvkww.js} +46 -46
  111. package/dist/{index-ghtpfz4z.js → index-nyyam9ce.js} +2 -2
  112. package/dist/{index-qezhmp51.js → index-nz06qw5a.js} +3 -1
  113. package/dist/{index-1bx1xv1k.js → index-pfvde2fz.js} +5 -2
  114. package/dist/{index-w7jq3fz9.js → index-pq2m7t46.js} +1142 -1212
  115. package/dist/{index-yk4g8805.js → index-pq7rkwkk.js} +1 -30
  116. package/dist/{index-s80awwsw.js → index-qch1vcaa.js} +3 -1
  117. package/dist/{index-hdjmdwsj.js → index-qctbzmg9.js} +521 -511
  118. package/dist/{index-n8z3rvzw.js → index-qevqn3d3.js} +3 -5
  119. package/dist/{index-nqxrm176.js → index-r3db01kh.js} +1 -1
  120. package/dist/{index-v6vb5qx8.js → index-s3yxw6kt.js} +1 -1
  121. package/dist/{index-cqmqyjax.js → index-sa99p98w.js} +1 -1
  122. package/dist/index-sb9dr3tq.js +14 -0
  123. package/dist/{index-0gxvns6s.js → index-sba3e6m4.js} +1 -0
  124. package/dist/{index-f24bdt13.js → index-v1swr26p.js} +3 -1
  125. package/dist/{index-rhzkndr5.js → index-x4p3kw1p.js} +533 -530
  126. package/dist/{index-cycp3r3e.js → index-yb9n4pgj.js} +2 -2
  127. package/dist/{index-htxh46ej.js → index-ycqhyars.js} +2 -2
  128. package/dist/{index-9bwcthxw.js → index-yj6saztf.js} +31 -13
  129. package/dist/{index-f6m4c545.js → index-zrdcmg6j.js} +4 -6
  130. package/dist/{index-tvtacc9a.js → index-zyc64nbh.js} +1 -34
  131. package/dist/process/bun-child.d.ts +3 -2
  132. package/dist/process/bun-child.d.ts.map +1 -1
  133. package/dist/server/contract-method.d.ts.map +1 -1
  134. package/dist/tools/internal/surface-projector.d.ts.map +1 -1
  135. package/dist/tools/operations/async-operation-canonical-types.d.ts +1 -1
  136. package/dist/tracking/attribution.d.ts +21 -25
  137. package/dist/tracking/attribution.d.ts.map +1 -1
  138. package/dist/tracking/client-visit.d.ts +5 -7
  139. package/dist/tracking/client-visit.d.ts.map +1 -1
  140. package/dist/tracking/client.d.ts +2 -2
  141. package/dist/tracking/client.d.ts.map +1 -1
  142. package/dist/tracking/contract.d.ts +1 -1
  143. package/dist/tracking/host.d.ts +0 -1
  144. package/dist/tracking/host.d.ts.map +1 -1
  145. package/dist/tracking/privacy.d.ts +6 -0
  146. package/dist/tracking/privacy.d.ts.map +1 -1
  147. package/dist/tracking/schemas.d.ts +2 -2
  148. package/dist/tracking/schemas.d.ts.map +1 -1
  149. package/llms/agent-runtime-harness.txt +4 -3
  150. package/llms/agent-runtime-sandbox.txt +1 -1
  151. package/llms/agent-runtime.2.txt +3 -2
  152. package/llms/agent-runtime.3.txt +4 -46
  153. package/llms/agent-runtime.4.txt +113 -0
  154. package/llms/agent-runtime.txt +11 -13
  155. package/llms/cli.txt +1 -1
  156. package/llms/contract.txt +46 -24
  157. package/llms/geo-contract.txt +23 -0
  158. package/llms/geo.txt +40 -4
  159. package/llms/stitchkit.txt +16 -9
  160. package/llms/testing.txt +4 -6
  161. package/llms/tools-agent.txt +24 -0
  162. package/llms/tools-mcp.txt +71 -6
  163. package/llms/tools-toolkit.txt +23 -0
  164. package/llms/tools.2.txt +8 -5
  165. package/llms/tools.3.txt +9 -78
  166. package/llms/tools.txt +8 -8
  167. package/llms/tracking-contract.txt +1 -1
  168. package/llms/tracking.txt +27 -5
  169. package/llms/{upgrading-0.105.0-0.118.0.txt → upgrading-0.106.0-0.120.0.txt} +152 -410
  170. package/llms/upgrading-0.37.0-0.53.0.txt +969 -0
  171. package/llms/{upgrading-0.44.0-0.62.0.txt → upgrading-0.55.0-0.68.0.txt} +509 -528
  172. package/llms/{upgrading-0.63.0-0.83.0.txt → upgrading-0.69.0-0.95.0.txt} +506 -509
  173. package/llms/{upgrading-0.84.0-0.104.0.txt → upgrading-0.96.0-0.105.0.txt} +410 -506
  174. package/llms/upgrading.txt +5 -5
  175. package/llms-full.txt +475 -154
  176. package/llms.txt +17 -14
  177. package/package.json +32 -20
  178. package/llms/upgrading-0.37.0-0.44.0.txt +0 -444
  179. package/dist/{index-arjmb6de.js → index-1jwsfkfz.js} +0 -0
  180. package/dist/{index-qndsr7f0.js → index-5efrpfmj.js} +3 -3
package/llms-full.txt CHANGED
@@ -55,8 +55,10 @@ own, recorded as an ADR.
55
55
  | `stitchkit/primitives` | browser **and** server | evolving | generic values and declarations for lifecycle, access, audit, delivery and exports, exact money and rounding tables |
56
56
  | `stitchkit/server` | server (Bun) | stable | `createServer`, `implement`, hooks, auth, Socket.IO server, server primitives |
57
57
  | `stitchkit/node` | server (Node ≥ 22) | stable | `serveNode` + the runtime-agnostic core — the Node mirror of `/server` |
58
- | `stitchkit/tools` | server | stable | `createMcpHandler`, `mountMcp`, `mountAgent`, the OAuth provider, native tools |
58
+ | `stitchkit/tools` | server | stable | the transport-neutral tool layer — registry, tool names and manifest, runtime tools, native tools, the OAuth provider, the durability engine; loads no optional peer at runtime |
59
59
  | `stitchkit/tools/mcp` | server (Bun or Node) | evolving | the canonical MCP HTTP/stdio server, schemas and typed MCP presenters; only Zod and the MCP server SDK peers |
60
+ | `stitchkit/tools/agent` | server (Bun or Node) | evolving | `mountAgent` — a contract's operations as AI SDK tools; only Zod and the `ai` peer |
61
+ | `stitchkit/tools/toolkit` | server (Bun or Node) | evolving | `createToolkit` — one tool set served over MCP and to an AI SDK agent; the MCP server SDK and `ai` peers |
60
62
  | `stitchkit/tools/contract` | browser + server | evolving | the shapes a tool surface speaks — async-operation contract, snapshot and cancel schemas, view-file input/output — without the runtime that serves them |
61
63
  | `stitchkit/tools/invoker` | server | stable | peer-free `createToolInvoker` over the canonical contract tool runner |
62
64
  | `stitchkit/tools/connections` | server (Bun or Node) | evolving | consume external MCP servers and a bounded OpenAPI subset — typed reauthorization, a per-request host fence and per-call credentials |
@@ -78,16 +80,17 @@ own, recorded as an ADR.
78
80
  | `stitchkit/release` | browser **and** server | evolving | a page follows the release it was built for — `createReleaseMarker` on the server, `createReleaseWatcher` in the browser, the `X-Build-Id` header and a socket event between them |
79
81
  | `stitchkit/voice` | browser **and** server | evolving | the mechanics of a live voice reply — `SentenceCutter`, `speakableText`, a `SpeechQueue` that synthesises ahead and stops on interruption, the conversation phases |
80
82
  | `stitchkit/geo` | server (Bun or Node) | evolving | managed GeoIP reader generations, last-known-good reload and the optional MaxMind adapter |
83
+ | `stitchkit/geo/contract` | browser **and** server | evolving | the place snapshot schema, for a shared contract that stores or sends `GeoAttribution`; brings `zod` |
81
84
  | `stitchkit/xlsx` | browser **and** server | evolving | a workbook from rows and typed columns, with no dependency; text never runs as a formula |
82
85
  | `stitchkit/oauth` | browser **and** server | evolving | provider-neutral Authorization Code + PKCE request and one-shot callback transaction mechanics |
83
86
  | `stitchkit/google` | server (Bun or Node) | evolving | optional Google code exchange and verified OIDC identity adapter |
84
- | `stitchkit/observability` | server | stable<br>_redefined in 2 of the 63 minors since 0.56.2, most recently 0.92.0_ | request/tool event projections — `createObservability`, trace context, sanitisation |
87
+ | `stitchkit/observability` | server | stable<br>_redefined in 2 of the 65 minors since 0.56.2, most recently 0.92.0_ | request/tool event projections — `createObservability`, trace context, sanitisation |
85
88
  | `stitchkit/testing` | tests on Bun or Node | stable | in-process generated clients over a real Fetch handler, the managed-resource conformance kit and manual time for schedules and retries |
86
89
  | `stitchkit/declaration` | browser + build and deployment tooling (Bun or Node) | evolving | `ProjectDeclarationSchema` — the one machine-readable statement a repository makes about itself |
87
90
  | `stitchkit/react` | browser + server rendering | stable | `createCursorQuery`, `createCacheBridge`, QueryClient and `ApiError` retry policy |
88
91
  | `stitchkit/react/keyboard` | browser | evolving | which part of a screen gets a key — ordered layers, one Escape per level, list zones with real focus; see [keyboard guide](keyboard.md) |
89
92
  | `stitchkit/react/keyboard/testing` | server (Bun or Node) | evolving | the keyboard layers and zones without a DOM — fakes for the window, elements and key events beside the two engines, to test the order of layers, `scope`/`inert` and the edges of a zone in milliseconds |
90
- | `stitchkit/agent-runtime` | server | evolving<br>_redefined in 25 of the 63 minors since 0.56.2, most recently 0.115.0_ | optional durable conversation/run loop, history, models, prompts, fencing and events |
93
+ | `stitchkit/agent-runtime` | server | evolving<br>_redefined in 25 of the 65 minors since 0.56.2, most recently 0.115.0_ | optional durable conversation/run loop, history, models, prompts, fencing and events |
91
94
  | `stitchkit/agent-runtime/sandbox` | server | evolving | optional Linux Bubblewrap sessions, durable workspaces and a host HTTP credential gateway; see [sandbox guide](sandbox.md) |
92
95
  | `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 |
93
96
  | `stitchkit/agent-runtime/harness` | server | evolving | resource-aware process-local facade over the canonical Agent runtime; supervision stays outside |
@@ -99,7 +102,7 @@ own, recorded as an ADR.
99
102
  | `stitchkit/agent-runtime/sqlite/bun` | server (Bun) | evolving | durable built-in SQLite store for the agent runtime |
100
103
  | `stitchkit/agent-runtime/sqlite/node` | server (Node ≥ 22.5) | evolving | durable built-in SQLite store for the agent runtime |
101
104
  | `stitchkit-tui` | terminal (Bun) | evolving | optional official OpenTUI host over a caller-composed headless runtime |
102
- | `stitchkit/application` | browser + server | evolving<br>_redefined in 14 of the 63 minors since 0.56.2, most recently 0.110.0_ | managed resource graph, readiness, admission, schedules, subtree restart and bounded shutdown |
105
+ | `stitchkit/application` | browser + server | evolving<br>_redefined in 14 of the 65 minors since 0.56.2, most recently 0.110.0_ | managed resource graph, readiness, admission, schedules, subtree restart and bounded shutdown |
103
106
  | `stitchkit/application/grammy` | server | evolving | a grammY bot as application resources — configuration, then polling with updates admitted by batch — and the webhook adapter |
104
107
  | `stitchkit/application/opentelemetry` | server | evolving | maps application snapshots onto an injected OpenTelemetry `Meter` |
105
108
  | `stitchkit/application/schemas` | browser + server | evolving | the application's snapshot, health and shutdown schemas alone, without the kernel |
@@ -202,7 +205,10 @@ map — feature → packages:
202
205
  | `createServer` (Bun) | — (uses `Bun.serve`) |
203
206
  | `serveNode` (Node ≥ 22) | `srvx` (+ `@types/bun` dev) |
204
207
  | In-process contract tools (`stitchkit/tools/invoker`) | — |
205
- | MCP / agent adapters (`stitchkit/tools`) | `@modelcontextprotocol/server` `ai` |
208
+ | Tool layer — registry, tool names, runtime tools (`stitchkit/tools`) | — |
209
+ | MCP server (`stitchkit/tools/mcp`) | `@modelcontextprotocol/server` |
210
+ | AI SDK agent tools (`stitchkit/tools/agent`) | `ai` |
211
+ | MCP and agent toolkit (`stitchkit/tools/toolkit`) | `@modelcontextprotocol/server` `ai` |
206
212
  | Agent application runtime (`stitchkit/agent-runtime`) | `ai` |
207
213
  | Headless Agent harness (`stitchkit/agent-runtime/harness`) | `ai` |
208
214
  | Agent coding tools (`stitchkit/agent-runtime/coding-tools`) | — |
@@ -231,15 +237,16 @@ bun add socket.io @socket.io/bun-engine # e.g. the Socket.IO server on Bun
231
237
  ```
232
238
 
233
239
  Dynamic optional-peer adapters fail with an actionable error naming the package
234
- and install command. Static entrypoints such as `stitchkit/tools` fail during
240
+ and install command. Static entrypoints such as `stitchkit/tools/mcp` fail during
235
241
  ESM resolution by naming the exact missing package; the feature-to-peer matrix
236
242
  below is the canonical install command.
237
243
 
238
- The combined tools entry owns MCP and AI-agent adapters, so install both runtime
239
- peers before importing `stitchkit/tools`:
244
+ `stitchkit/tools` itself loads no optional peer. Install the peer of the adapter
245
+ you import — both for `stitchkit/tools/toolkit`:
240
246
 
241
247
  ```bash
242
- bun add @modelcontextprotocol/server@^2 ai@^7
248
+ bun add @modelcontextprotocol/server@^2 # stitchkit/tools/mcp
249
+ bun add ai@^7 # stitchkit/tools/agent
243
250
  ```
244
251
 
245
252
  Browser, HTTP-client and React entrypoints remain usable without either MCP
@@ -445,7 +452,7 @@ Both entrypoints are **evolving**.
445
452
  | the event envelope, the batch, the dispositions, the visit entry and lease schemas | the event **types**, their metadata, their labels |
446
453
  | the tab-shared outbox with reserved sequences and a short flush lease | which storage adapter (IndexedDB in a tab) |
447
454
  | the page-leave beacon that arrives, and its queued insurance copy | the session, the router, the React provider |
448
- | visible time, scroll milestones, `data-track` clicks, UTM first/current touch | the referrer → source map, the attribute names |
455
+ | visible time, scroll milestones, `data-track` clicks, raw first/current touch | channel classification, click-marker names, the attribute names |
449
456
  | `dispositionTrackingBatch`, `issueVisitLease`, `activeIntervalOf`, presence | the tables, the transaction, adoption, geo, reports |
450
457
 
451
458
  ## Contract
@@ -492,7 +499,7 @@ const client = createTrackingClient<BrowserEvents>({
492
499
  deliver: (batch) => api.tracking.track(batch), // or a socket first, HTTP as the fallback
493
500
  unloadUrl: urls.tracking.track(),
494
501
  outbox: createTrackingOutbox(indexedDbOutboxStorage('my-app-tracking')),
495
- referrerMap: [{ pattern: /t\.me|telegram\.org/, source: 'telegram', medium: 'social' }],
502
+ clickIdParams: ['gclid', 'gbraid', 'wbraid', 'msclkid', 'yclid', 'ttclid', 'fbclid'],
496
503
  isAction: isInteractionAction,
497
504
  onVisit: (visitId) => rum.setVisit(visitId),
498
505
  })
@@ -505,6 +512,28 @@ client.track('ITEM_VIEW', { itemId, itemTitle })
505
512
  client.track('SIGN_OUT')
506
513
  ```
507
514
 
515
+ Attribution carries observations, not a channel verdict. `utm` contains only the current URL's
516
+ `utm_*` parameters, retaining case and whitespace up to 200 characters per field; tags without
517
+ `utm_source` are valid. Parameter names are case-sensitive (`UTM_SOURCE` is not a tag), a repeated
518
+ tag keeps its first value, and a whitespace-only value is kept as the fact it is. `referrer` carries
519
+ the referring URL with credentials masked. Neither an external referrer nor a click marker creates
520
+ campaign tags. Classify the channel on your server.
521
+
522
+ Attribution belongs to the visit: the bootstrap entry carries it, and an event batch carries events
523
+ only. Join events to their visit on the server to attribute them.
524
+
525
+ `clickIdParams` is the application's ordered list of query-parameter names to recognise. There is no
526
+ built-in list: the first configured name with a nonempty value becomes `clickId` in the touch and
527
+ bootstrap entry. Only the name is retained, never the click ID's value: a referrer from your own site
528
+ repeats the landing query, so the value of every configured marker in a referrer is masked too. An empty
529
+ list collects none; names must contain 1–200 characters, and `createTrackingClient` refuses a list that
530
+ does not. Bootstrap collects these facts even if browser storage is unavailable.
531
+
532
+ `client.attribution()` returns `firstTouch` and `currentTouch`. The first is persisted for 90 days
533
+ by default; the current is this capture's raw context, so an untagged page does not inherit an earlier
534
+ campaign. The server owns any policy for retaining the last non-direct channel. Stored first-touch
535
+ records carry a format version; unversioned attribution is discarded because its tags may be inferred.
536
+
508
537
  Two knobs match the client to your schema: `batchSize` is the contract's
509
538
  `maxEventsPerBatch` (a larger batch is a `400` the outbox can never retire),
510
539
  and `decorate` adds your `eventExtras` — a locale, an area, or the identity a
@@ -895,33 +924,55 @@ tools. Narrow it with `expose`:
895
924
 
896
925
  - `expose: ['HTTP']` — HTTP only. The endpoint never becomes a tool.
897
926
  - `expose: ['MCP', 'AGENT']` — a tool only; no HTTP route.
898
- - omit `expose` — all transports.
927
+ - `expose: []` — no transport; the endpoint stays hidden.
928
+ - omit `expose` — HTTP, MCP and AGENT; CLI is opt-in.
899
929
 
900
- For a curated or security-sensitive tool surface, opt into explicit tool
901
- exposure at the scoped factory:
930
+ Choose an application's default surface at its scoped factory. Scope and policy
931
+ are inferred in separate calls, so the returned endpoint types retain exact transports:
902
932
 
903
933
  ```ts
904
- const { defineContract } = createContractFactory<AppScope>({
905
- toolExposure: 'explicit',
934
+ const { defineContract } = createContractFactory<AppScope>().configure({
935
+ toolExposure: { default: ['HTTP', 'CLI'] },
906
936
  })
907
937
  ```
908
938
 
909
- With this policy, omitting `expose` materializes `['HTTP']` on the returned
910
- endpoint. MCP, Agent and CLI then require an explicit endpoint array. The plain
911
- factory and `defineContract` keep the default-on behaviour above.
939
+ An omitted `expose` (including `undefined`) materializes this array on the returned
940
+ endpoint before validation. An explicit endpoint array **replaces** it, never merges
941
+ with it: `['HTTP']` stays HTTP-only, `['MCP']` enables only MCP, and `[]` hides the
942
+ operation everywhere. Types, HTTP routes, typed clients, tools, CLI, OpenAPI and
943
+ transport summaries read that same materialized exposure.
944
+
945
+ HTTP client methods and URL builders require a **guaranteed** HTTP transport:
946
+ pass a literal policy array directly to `configure` so its const generic infers
947
+ the tuple. A stored array, even with `satisfies ContractFactoryConfig`, can widen
948
+ to `Transport[]` and contain no HTTP at runtime,
949
+ so it does not promise an HTTP method. Optional capabilities preserve both
950
+ possible effective exposures rather than claiming an unfiltered default tuple.
912
951
 
913
- Consequently, `expose: ['HTTP']` is redundant inside a factory configured with
914
- `toolExposure: 'explicit'`: omit it unless the explicit spelling is useful to a
915
- local reader. Add `MCP`, `AGENT` or `CLI` only on endpoints that intentionally
916
- join those surfaces.
952
+ The same holds for an endpoint whose own type is broad, with or without a factory:
953
+ a value annotated as `EndpointDef` or `FactoryScopedEndpoint<Scope>` carries
954
+ `expose?: readonly Transport[]`, which may hold no HTTP, so it has no client method
955
+ or URL. A helper that builds endpoints keeps their literal type with a const generic:
917
956
 
918
- Tool transports (`MCP`, `AGENT`) skip four kinds of endpoint automatically:
919
- `multipart` (a file upload is not a tool call),
920
- [`rawResponse`](./server.md#raw-response-endpoints) (its answer is bytes, which
921
- a tool result cannot carry — it would serialize to `{}`),
922
- [`responseMeta`](./server.md#typed-json-response-metadata) (outbound HTTP
923
- headers have no meaning on a tool call) and `rawBody` (HTTP-only by
924
- construction).
957
+ ```ts
958
+ const endpoint = <const Endpoint extends FactoryScopedEndpoint<AppScope>>(value: Endpoint) => value
959
+ ```
960
+
961
+ For a curated tool surface use `toolExposure: { default: ['HTTP'] }`, then name
962
+ tool transports only on endpoints that intentionally join them. An empty default
963
+ is legal. Each `configure` snapshots its array independently; it does not change
964
+ the base factory, sibling configurations or authored endpoints. The no-config
965
+ factory and plain `defineContract` keep the default-on behaviour above.
966
+
967
+ Six intrinsic HTTP-only forms intersect an **implicit factory default** with HTTP:
968
+ `multipart`, [`rawResponse`](./server.md#raw-response-endpoints),
969
+ [`responseMeta`](./server.md#typed-json-response-metadata), `rawBody`, streaming
970
+ responses and `HEAD`. Under `['HTTP', 'CLI']` they receive `['HTTP']`; under
971
+ `['CLI']` or `[]` they receive `[]`, not an implicitly reopened HTTP route.
972
+ An **explicit** non-HTTP exposure on any of them is a definition error, not a
973
+ silent downgrade. `safelistedBody` only changes HTTP parsing and remains
974
+ multi-transport. A factory policy applies only to contracts authored through
975
+ that factory, not to contracts another helper builds internally.
925
976
 
926
977
  ## Tool options (`tool`)
927
978
 
@@ -939,7 +990,7 @@ endpoint that can never be a tool (a raw response, a stream, `responseMeta`,
939
990
 
940
991
  `expose` stays outside the group: it chooses transports, HTTP included.
941
992
  Setting `tool` on an endpoint with no tool transport — `expose: ['HTTP']`, or an
942
- omitted `expose` under a factory with `toolExposure: 'explicit'` — throws at
993
+ omitted `expose` under a factory with a default containing no tool transport — throws at
943
994
  definition time. The pre-0.94 top-level keys (`toolName`, `toolView`, `ui`,
944
995
  `annotations`, `mcp`) are refused by name, with where each one went.
945
996
 
@@ -3891,10 +3942,10 @@ recursive calls without retaining request state. Every invocation gets a fresh
3891
3942
  tool-call context and runs the same extension resolution, input/output
3892
3943
  validation, lifecycle, hooks and output-strip reporter as mounted tools.
3893
3944
 
3894
- The dedicated entrypoint needs only the normal `stitchkit`/`zod` install. Use
3895
- the broader `stitchkit/tools` barrel when the same program also mounts MCP or AI
3896
- adapters and has installed their optional peers. Both imports reach the same
3897
- runner; this is dependency isolation, not a second execution implementation.
3945
+ The dedicated entrypoint needs only the normal `stitchkit`/`zod` install, and so does
3946
+ `stitchkit/tools`, which exports the same invoker beside the rest of the tool layer. Both
3947
+ imports reach the same runner; this is dependency isolation, not a second execution
3948
+ implementation.
3898
3949
 
3899
3950
  `invoke` returns the canonical discriminated `ToolResult`, not an AI SDK or MCP
3900
3951
  presentation envelope. `invokeOrThrow` returns validated data and throws the
@@ -3916,9 +3967,8 @@ get the view's defaults and projected answer, which is what `collectTools` and
3916
3967
  An MCP-only server imports `stitchkit/tools/mcp` and installs
3917
3968
  `stitchkit`, `zod` and `@modelcontextprotocol/server`. The leaf's runtime and strict
3918
3969
  NodeNext declarations require no `ai`. It exports the existing HTTP/stdio server,
3919
- schema policy, catalog and resource helpers; their implementation and security are shared
3920
- with the full `stitchkit/tools` barrel. Use that full barrel when the application also
3921
- needs its AI SDK features.
3970
+ schema policy, catalog and resource helpers, and it is the only entrypoint that does:
3971
+ `stitchkit/tools` loads no optional peer. AI SDK tools come from `stitchkit/tools/agent`.
3922
3972
 
3923
3973
  `createMcpHandler` builds a complete stateless Streamable-HTTP MCP endpoint on
3924
3974
  the official TypeScript SDK v2 server package. It owns server creation,
@@ -4642,7 +4692,8 @@ stitchkit ships the OAuth **protocol mechanics**; the app supplies only
4642
4692
  **identity and storage**. Three pieces wire it together:
4643
4693
 
4644
4694
  ```ts
4645
- import { createMcpHandler, createMcpHttpRoute, mountOAuthProvider, oauthProtectedResourceRoute } from 'stitchkit/tools'
4695
+ import { mountOAuthProvider, oauthProtectedResourceRoute } from 'stitchkit/tools'
4696
+ import { createMcpHandler, createMcpHttpRoute } from 'stitchkit/tools/mcp'
4646
4697
  import { createServer } from 'stitchkit/server'
4647
4698
 
4648
4699
  const resource = 'https://api.example.com/mcp'
@@ -4879,8 +4930,8 @@ export const users = defineContract({ prefix: 'users' }, {
4879
4930
  reshapes values inside the full schema. With `output` alone it is a slice, and
4880
4931
  the full result must fit that schema — `withToolView` refuses the endpoint at
4881
4932
  compile time when it does not.
4882
- - **Exposure.** A view needs a tool transport. With `createContractFactory({
4883
- toolExposure: 'explicit' })` an endpoint without `expose` is HTTP-only, so an
4933
+ - **Exposure.** A view needs a tool transport. With `createContractFactory<Scope>().configure({
4934
+ toolExposure: { default: ['HTTP'] } })` an endpoint without `expose` is HTTP-only, so an
4884
4935
  endpoint with a view names its tool transports in `expose`.
4885
4936
  - **What it refuses.** `defineContract` rejects a view on an endpoint with no
4886
4937
  tool transport or no full `output`, on a raw, `rawBody`, multipart,
@@ -4903,7 +4954,7 @@ see [Agent application runtime](./agent-runtime.md).
4903
4954
  `generateText` / `streamText`:
4904
4955
 
4905
4956
  ```ts
4906
- import { mountAgent } from 'stitchkit/tools'
4957
+ import { mountAgent } from 'stitchkit/tools/agent'
4907
4958
  import { generateText } from 'ai'
4908
4959
 
4909
4960
  const tools = mountAgent(usersService, { context: { userId: 'agent-1' } })
@@ -4987,7 +5038,9 @@ result. Optional presentation callbacks map that result to MCP content and AI
4987
5038
  SDK model output without coupling the handler to either SDK:
4988
5039
 
4989
5040
  ```ts
4990
- import { createMcpHandler, defineRuntimeTool, mountAgent } from 'stitchkit/tools'
5041
+ import { defineRuntimeTool } from 'stitchkit/tools'
5042
+ import { createMcpHandler } from 'stitchkit/tools/mcp'
5043
+ import { mountAgent } from 'stitchkit/tools/agent'
4991
5044
  import { z } from 'zod'
4992
5045
 
4993
5046
  const renderPreview = defineRuntimeTool({
@@ -5353,7 +5406,8 @@ hooks. The built-in `mountViewFile`, `mountWait`, `mountDownload` and
5353
5406
  media helper fetches with SSRF and path-traversal defenses:
5354
5407
 
5355
5408
  ```ts
5356
- import { createMcpHandler, mountViewFile } from 'stitchkit/tools'
5409
+ import { mountViewFile } from 'stitchkit/tools'
5410
+ import { createMcpHandler } from 'stitchkit/tools/mcp'
5357
5411
 
5358
5412
  const handleMcp = createMcpHandler({
5359
5413
  serverInfo: { name: 'my-app', version: '1.0.0' },
@@ -5461,16 +5515,16 @@ identical. Write the handler once; it serves every surface.
5461
5515
  > **Maturity: evolving.** This surface is still finding its shape and may be
5462
5516
  > redefined in any minor release — always with a `### ⚠️ Breaking changes` entry
5463
5517
  > and a migration section, never silently. If your application already owns the
5464
- > conversation loop, `mountAgent` from `stitchkit/tools` is the stable path.
5518
+ > conversation loop, `mountAgent` from `stitchkit/tools/agent` is the direct path.
5465
5519
 
5466
5520
  `stitchkit/agent-runtime` is the server-only, opinionated layer above
5467
5521
  `mountAgent`. Use it when the application wants Stitchkit to own conversation
5468
5522
  mechanics: durable acceptance, history projection, the AI SDK stream loop,
5469
5523
  checkpoints, keyed interruption, terminal commit and stable application events.
5470
5524
 
5471
- If the application already owns that loop, continue importing `mountAgent` from
5472
- `stitchkit/tools`. `stitchkit/tools` does not depend on this entrypoint at all;
5473
- this entrypoint uses the tool executor from it, but pulls no MCP peer, so
5525
+ If the application already owns that loop, import `mountAgent` from
5526
+ `stitchkit/tools/agent`. Neither `stitchkit/tools` nor `stitchkit/tools/agent` depends on
5527
+ this entrypoint; this entrypoint uses the tool executor, but pulls no MCP peer, so
5474
5528
  choosing `mountAgent` alone costs you nothing from here.
5475
5529
 
5476
5530
  UI and shared DTO code must import canonical records and delivery validation from
@@ -5494,12 +5548,8 @@ persistence, event sinks and every Node context dependency.
5494
5548
  bun add stitchkit ai zod
5495
5549
  ```
5496
5550
 
5497
- `mountAgent` from `stitchkit/tools` — used in the composition below — additionally
5498
- needs the MCP peer, which that entrypoint imports statically:
5499
-
5500
- ```sh
5501
- bun add @modelcontextprotocol/server
5502
- ```
5551
+ `mountAgent` from `stitchkit/tools/agent` — used in the composition below — needs no
5552
+ peer beyond `ai`.
5503
5553
 
5504
5554
  OpenRouter is isolated so other runtime users do not resolve its package:
5505
5555
 
@@ -5520,7 +5570,8 @@ import {
5520
5570
  defineModelRegistry,
5521
5571
  } from 'stitchkit/agent-runtime'
5522
5572
  import { openRouterProvider } from 'stitchkit/agent-runtime/openrouter'
5523
- import { composeToolLifecycle, mountAgent } from 'stitchkit/tools'
5573
+ import { composeToolLifecycle } from 'stitchkit/tools'
5574
+ import { mountAgent } from 'stitchkit/tools/agent'
5524
5575
 
5525
5576
  const protocol = defineAgentProtocol({
5526
5577
  context: z.object({ userId: z.string() }),
@@ -7234,7 +7285,8 @@ import {
7234
7285
  createAgentHarnessFileResources,
7235
7286
  createHeadlessAgentHarness,
7236
7287
  } from 'stitchkit/agent-runtime/harness';
7237
- import { composeToolLifecycle, mountAgent } from 'stitchkit/tools';
7288
+ import { composeToolLifecycle } from 'stitchkit/tools';
7289
+ import { mountAgent } from 'stitchkit/tools/agent';
7238
7290
 
7239
7291
  const resources = createAgentHarnessFileResources({
7240
7292
  roots: [{ id: 'guide', kind: 'instruction', path: instructionRoot }],
@@ -7258,8 +7310,8 @@ compose your authorization with the run's `toolFenceLifecycle` by `composeToolLi
7258
7310
  fence rejects a call from a run that no longer owns the conversation, and it must run exactly
7259
7311
  once. Every mount option remains available, including registry, runtime tools, extension,
7260
7312
  coercion, output stripping, hooks and durability, and the callback may be async. The same callback
7261
- still accepts arbitrary typed AI SDK tools and prepareStep. `stitchkit/tools` needs the same `ai` and
7262
- `@modelcontextprotocol/server` peers as any `mountAgent` use; the base harness stays AI-only.
7313
+ still accepts arbitrary typed AI SDK tools and prepareStep. `mountAgent` comes from `stitchkit/tools/agent`, which needs only the `ai` peer the
7314
+ harness already has; the base harness stays AI-only.
7263
7315
  The resource reader is explicitly selected: filtered allowlists do not gain tools merely
7264
7316
  because a loader exists. Root IDs, kinds, provenance and limits are unchanged.
7265
7317
 
@@ -8612,7 +8664,7 @@ Storage is process-durable, without a power-loss/fsync guarantee.
8612
8664
 
8613
8665
  ```ts
8614
8666
  import { createSandboxCodingTools } from 'stitchkit/agent-runtime/sandbox';
8615
- import { mountAgent } from 'stitchkit/tools';
8667
+ import { mountAgent } from 'stitchkit/tools/agent';
8616
8668
 
8617
8669
  const tools = mountAgent([], {
8618
8670
  runtimeTools: createSandboxCodingTools(handle, {
@@ -9995,7 +10047,7 @@ types; the package keeps its required dependencies separate from optional SDKs.
9995
10047
  CLI runtime tools use the one runtime-tool declaration: identity, input and output
9996
10048
  schemas, handler, and explicit CLI exposure. SDK presenters are the adapter's extension
9997
10049
  of that declaration (the fourth type argument of `RuntimeToolDefinitionWithOutput`, or
9998
- `defineRuntimeTool` in `stitchkit/tools`), which requires the corresponding peers. A typed
10050
+ `defineRuntimeTool` in `stitchkit/tools`), whose presenter types name the corresponding SDKs. A typed
9999
10051
  definition returned by `defineRuntimeTool` or `createRuntimeToolFactory` can be
10000
10052
  passed directly to both CLI and SDK mounts.
10001
10053
 
@@ -14872,8 +14924,9 @@ fixture's transitive dependency.
14872
14924
 
14873
14925
  That includes `stitchkit/tools/invoker`: a minimal packed consumer imports it
14874
14926
  without MCP/AI peers and executes validation, output projection and normalized
14875
- errors on Bun and Node. The broader `stitchkit/tools` barrel deliberately keeps
14876
- the adapter peer budget.
14927
+ errors on Bun and Node, and so does `stitchkit/tools`, which loads no optional peer at
14928
+ runtime. The adapters keep their peer budgets on their own subpaths:
14929
+ `stitchkit/tools/mcp`, `stitchkit/tools/agent` and `stitchkit/tools/toolkit`.
14877
14930
 
14878
14931
  Beside the matrix, one case is about the ARTIFACT rather than the inventory: a
14879
14932
  program that injects the Socket.IO peer loaders is bundled and started in a
@@ -15102,10 +15155,7 @@ signal and deadline. Bind its process lifecycle with the truthful close-only
15102
15155
  sibling from `stitchkit/tools`:
15103
15156
 
15104
15157
  ```ts
15105
- import {
15106
- bindStdioProcessSignals,
15107
- createStdioMcpServer,
15108
- } from 'stitchkit/tools'
15158
+ import { bindStdioProcessSignals, createStdioMcpServer } from 'stitchkit/tools/mcp'
15109
15159
 
15110
15160
  const stdio = await createStdioMcpServer(config)
15111
15161
  const binding = bindStdioProcessSignals(stdio, {
@@ -16950,7 +17000,45 @@ const attribution = await geo.resolve(requestIp);
16950
17000
  `resolve` returns `null` for invalid, loopback, private, link-local and otherwise
16951
17001
  non-public addresses without querying the reader. A database miss or lookup
16952
17002
  failure also returns `null`; `onError` receives operational failures. A result
16953
- may include country, region, city, postal, coordinates, timezone and ASN fields.
17003
+ is a sparse `GeoAttribution`: absent facts have no own property, not `null` or
17004
+ an own `undefined` value. City and ASN can be present independently. The default
17005
+ mapper preserves the complete City + ASN place snapshot:
17006
+
17007
+ | Fields | Meaning |
17008
+ |--------|---------|
17009
+ | `continentCode`, `continentName`, `continentGeonameId` | continent code, English name and GeoNames identifier |
17010
+ | `countryCode`, `countryName`, `countryGeonameId` | the country the database locates the address in; absent when the record locates none |
17011
+ | `registeredCountryCode`, `registeredCountryName`, `registeredCountryGeonameId` | the country the address block is registered to, a separate fact |
17012
+ | `regionCode`, `regionName`, `regionGeonameId` | first subdivision |
17013
+ | `city`, `cityGeonameId`, `postalCode` | city and postal facts; no city is required |
17014
+ | `latitude`, `longitude`, `timezone`, `accuracyRadiusKm` | coordinates, timezone and accuracy radius in kilometres; zero is a value |
17015
+ | `autonomousSystemNumber`, `autonomousSystemOrganization` | finite autonomous system number and organisation |
17016
+ | `localizedNames` | every available language under optional `continent`, `country`, `registeredCountry`, `region` and `city` dictionaries |
17017
+
17018
+ No locale is synthesised: without English names, localized dictionaries make a
17019
+ meaningful result, but `countryName`, `regionName` or `city` stays absent. The
17020
+ located country is never inferred from the registered one — an application that
17021
+ wants a country for every address chooses `countryCode ?? registeredCountryCode`
17022
+ itself, knowing which fact it used. Empty localized groups are omitted. Each field
17023
+ is validated on its own by the schema: text is nonempty, GeoNames identifiers and
17024
+ `accuracyRadiusKm` are nonnegative safe integers, latitude and longitude are in
17025
+ range. A malformed field is omitted without erasing the other valid facts. Unknown
17026
+ vendor fields are not projected.
17027
+
17028
+ A snapshot carries every language the database has: with the eight GeoLite2
17029
+ languages in all five groups it is 1–1.5 KB of JSON. An application that stores
17030
+ one per visit and needs fewer languages maps the result to its own shape.
17031
+
17032
+ `GeoAttributionSchema` is the strict, readonly Zod owner of the DTO. It lives in
17033
+ the browser-safe `stitchkit/geo/contract`, so a shared contract validates a stored
17034
+ or transported snapshot without the server-only resolver. Every field is optional
17035
+ and readonly; use `.unwrap()` before composing the underlying object schema. The
17036
+ mapper copies localized dictionaries, so later reader mutations do not change a
17037
+ snapshot. An empty or unrecognised record returns `null`.
17038
+
17039
+ An explicit `map` callback receives the raw reader result and owns its
17040
+ answer. The resolver does not apply `GeoAttributionSchema` after a custom mapper,
17041
+ strip its application fields or replace its returned object.
16954
17042
 
16955
17043
  When a reload succeeds, new lookups move to the new generation immediately.
16956
17044
  The previous reader closes only after its in-flight lookups finish.
@@ -17061,6 +17149,155 @@ charts, no merged cells and no streaming: an application that needs them takes a
17061
17149
 
17062
17150
  # Upgrading stitchkit
17063
17151
 
17152
+ ## Released migration: 0.120.0
17153
+
17154
+ ### tools-barrel-subpaths
17155
+
17156
+ **Who must act:** code importing the MCP server, `mountAgent` or `createToolkit` from
17157
+ `stitchkit/tools`. Imports of the registry, tool names, manifest, runtime and native tools, the
17158
+ invoker, the OAuth provider and the durability engine stay where they are.
17159
+
17160
+ `stitchkit/tools` now loads no optional peer, so an application without
17161
+ `@modelcontextprotocol/server` and `ai` can import it. Move each adapter import to its subpath:
17162
+
17163
+ ```ts
17164
+ // before
17165
+ import { createMcpHandler, listToolNames, mountAgent } from 'stitchkit/tools'
17166
+ import { createToolkit } from 'stitchkit/tools'
17167
+ // after
17168
+ import { listToolNames } from 'stitchkit/tools'
17169
+ import { createMcpHandler } from 'stitchkit/tools/mcp'
17170
+ import { mountAgent } from 'stitchkit/tools/agent'
17171
+ import { createToolkit } from 'stitchkit/tools/toolkit'
17172
+ ```
17173
+
17174
+ | Names | New subpath | Peers it needs |
17175
+ |-------|-------------|----------------|
17176
+ | `createMcpHandler`, `createMcpHttpRoute`, `buildMcpServer`, `mountMcp`, `mountMcpResource`, `validateMcpSchemas`, `createStdioMcpServer`, `bindStdioProcessSignals`, `inlineMcpAppBundle`, `mcpCatalogMeta`, `readMcpCatalogStamp`, `EXT_APPS_BUNDLE_PLACEHOLDER`, `MCP_CATALOG_META_KEY`, `RESOURCE_MIME_TYPE` and their config types, `McpServer`, `IncompatibleSchemaPolicy`, `McpSchemaValidationConfig` | `stitchkit/tools/mcp` | `@modelcontextprotocol/server` |
17177
+ | `mountAgent`, `AgentMountConfig`, `AgentContext` | `stitchkit/tools/agent` | `ai` |
17178
+ | `createToolkit`, `Toolkit` | `stitchkit/tools/toolkit` | `@modelcontextprotocol/server`, `ai` |
17179
+
17180
+ The move is mechanical: `bunx stitchkit upgrade --from 0.119.0` lists every import it touches, and
17181
+ `tsc` names any name still imported from `stitchkit/tools`. Behaviour is unchanged.
17182
+
17183
+ ## Released migration: 0.119.0
17184
+
17185
+ ### factory-default-exposure
17186
+
17187
+ **Who must act:** code passing arguments to `createContractFactory`, depending on its fixed-string
17188
+ policy types or the renamed exposure types, calling HTTP clients of endpoints typed as the broad
17189
+ `EndpointDef` / `FactoryScopedEndpoint`, or declaring multipart endpoints with non-HTTP exposure or
17190
+ tool options.
17191
+
17192
+ Scope and default transports are inferred separately. Replace the constructor configuration:
17193
+
17194
+ ```ts
17195
+ // before
17196
+ const { defineContract } = createContractFactory<AppScope>({ toolExposure: 'explicit' })
17197
+ // after: preserve the same curated HTTP-only default
17198
+ const { defineContract } = createContractFactory<AppScope>().configure({
17199
+ toolExposure: { default: ['HTTP'] },
17200
+ })
17201
+ ```
17202
+
17203
+ To deliberately expose ordinary operations through HTTP and CLI, choose `default: ['HTTP', 'CLI']`
17204
+ and remove redundant matching endpoint arrays. Explicit arrays replace, rather than extend, the
17205
+ default. Raw responses, multipart, raw bodies, response metadata, response streams and HEAD get only
17206
+ HTTP from implicit defaults; a CLI-only or empty default leaves them hidden on every transport.
17207
+ Explicit contradictions fail at definition; remove non-HTTP transports and tool options from multipart
17208
+ endpoints rather than relying on their silent omission from tool catalogs.
17209
+
17210
+ `ExplicitToolExposureEndpoints` and `ExplicitScopedDefineContract` are renamed
17211
+ `ConfiguredToolExposureEndpoints<Endpoints, Defaults>` and `ConfiguredScopedDefineContract<Scope, Defaults>`:
17212
+ they describe materialization from any default tuple, their second generic, and HTTP-only when it is
17213
+ omitted.
17214
+ An optional or conditional endpoint override remains an alternative in the returned type. It does not
17215
+ guarantee an HTTP client method unless every alternative includes HTTP.
17216
+
17217
+ HTTP clients and URL builders no longer assume a widened `Transport[]` contains
17218
+ HTTP. Pass the literal array directly to `configure` when the HTTP surface is static;
17219
+ its const generic infers the tuple. An annotation or `satisfies ContractFactoryConfig`
17220
+ on a separately stored configuration does not preserve the tuple by itself.
17221
+ Optional capabilities likewise retain the filtered alternative in endpoint types.
17222
+
17223
+ The rule reads the endpoint's type, with or without a factory. An endpoint returned by a helper
17224
+ annotated `(): EndpointDef` or `(): FactoryScopedEndpoint<Scope>`, or holding a stored
17225
+ `readonly Transport[]`, has no HTTP client method or URL, because that type allows an exposure
17226
+ without HTTP. Keep the literal type with a const generic helper:
17227
+
17228
+ ```ts
17229
+ // before
17230
+ const read = (): FactoryScopedEndpoint<AppScope> => ({ method: 'GET', path: '/', desc: 'Read', output })
17231
+ // after
17232
+ const endpoint = <const Endpoint extends FactoryScopedEndpoint<AppScope>>(value: Endpoint) => value
17233
+ const read = () => endpoint({ method: 'GET', path: '/', desc: 'Read', output })
17234
+ ```
17235
+
17236
+ If every endpoint already has an explicit array, removing the constructor configuration alone also
17237
+ preserves exposure. [ADR 0283](../decisions/0283-factory-default-exposure-is-materialized-by-capability.md).
17238
+
17239
+ Search patterns: `createContractFactory`, `toolExposure`, `ExplicitToolExposureEndpoints`,
17240
+ `ExplicitScopedDefineContract`, `: EndpointDef`, `: FactoryScopedEndpoint`, and multipart endpoints
17241
+ with `expose` or `tool`.
17242
+
17243
+ ### geo-full-snapshot
17244
+
17245
+ **Who must act:** code comparing `stitchkit/geo` results by equality, parsing them with a strict
17246
+ schema of its own, mutating a returned `GeoAttribution`, or relying on empty labels.
17247
+
17248
+ The default mapper returns everything the City + ASN record states: continent, the registered
17249
+ country as its own fields, GeoNames identifiers, `accuracyRadiusKm` and `localizedNames` with every
17250
+ language. A record that used to give `{ city: 'Bangkok' }` now also gives
17251
+ `localizedNames: { city: { en: 'Bangkok' } }`.
17252
+
17253
+ ```ts
17254
+ // before: an application's copy of the shape
17255
+ const StoredGeo = z.strictObject({ city: z.string().optional(), countryCode: z.string().optional() })
17256
+ // after: the framework's schema, importable from shared code
17257
+ import { GeoAttributionSchema } from 'stitchkit/geo/contract'
17258
+ ```
17259
+
17260
+ `GeoAttribution` is readonly to its nested dictionaries; copy before changing a field. Empty labels
17261
+ and coordinates outside ±90° / ±180° are omitted. The located country never borrows the registered
17262
+ one; write `countryCode ?? registeredCountryCode` where a country for every address is wanted.
17263
+
17264
+ Search pattern: `stitchkit/geo`, `mapGeoIpRecord`, `GeoAttribution`, `resolve(`.
17265
+
17266
+ ### raw-tracking-attribution
17267
+
17268
+ **Who must act:** code passing `referrerMap` or `hostname` to `resolveAttribution`, importing
17269
+ `ReferrerRule` or `parseReferrer`, relying on inferred, lowercased or retained UTM, or reading `utm`
17270
+ from an event batch on the server.
17271
+
17272
+ Tracking sends raw attribution facts. Remove `referrerMap`, `ReferrerRule`, `parseReferrer` and the
17273
+ resolver's `hostname`; `TrackingPageContext` and `browserTrackingHost()` no longer require or return
17274
+ `hostname` either. Classify `referrer` and campaign tags on your server instead. Configure the
17275
+ click-marker names your application recognises:
17276
+
17277
+ ```ts
17278
+ // before
17279
+ createTrackingClient({ ...config, referrerMap: [{ pattern: /search\.example/, source: 'search', medium: 'organic' }] })
17280
+ // after
17281
+ createTrackingClient({ ...config, clickIdParams: ['gclid', 'msclkid'] })
17282
+ ```
17283
+
17284
+ Bootstrap and both touches carry `clickId`, the configured parameter's name without its value. Extend
17285
+ your bootstrap DTO or stored attribution shape if you previously copied the framework's fields. No
17286
+ marker is recognised unless configured. Real UTM retains its case and whitespace, and a tag without
17287
+ `utm_source` is no longer discarded. Keep any channel normalisation in the server classifier.
17288
+
17289
+ `currentTouch` is the current capture's context; an untagged entry has no campaign even when a prior
17290
+ one did. Implement last-non-direct retention on the server if required. First-touch storage uses a
17291
+ versioned format and resets unversioned records once, because inferred and real tags in those records
17292
+ cannot be distinguished. Without browser storage, current landing facts still reach bootstrap.
17293
+
17294
+ An event batch (`TrackEventsRequest`, the `request` schema of `createTrackingSchemas`) carries
17295
+ `buildId` and `events` only. A server that read `request.utm` attributes events through their visit:
17296
+ store the bootstrap entry's `utm`, `referrer` and `clickId` with the visit and join on `visitId`.
17297
+
17298
+ Search pattern: `referrerMap`, `ReferrerRule`, `parseReferrer`, `resolveAttribution`, `currentTouch`,
17299
+ `request.utm`, `input.utm`.
17300
+
17064
17301
  ## Released migration: 0.118.0
17065
17302
 
17066
17303
  ### telegram-due-cursor
@@ -22347,11 +22584,11 @@ from the root `stitchkit`.
22347
22584
  | Export | Kind | Summary |
22348
22585
  |--------|------|---------|
22349
22586
  | `defineContract` | function | declare a contract — [guide](../guide/contracts.md#definecontract) |
22587
+ | `ConfiguredScopedDefineContract` | _type_ | scoped authoring parameterized by the exact default transport tuple; explicit endpoint exposure replaces it |
22588
+ | `ConfiguredToolExposureEndpoints` | _type_ | endpoint map with materialized exposure; a second generic selects defaults (HTTP when omitted), preserving explicit, optional and conditional overrides |
22350
22589
  | `createContractFactory` | function | a `defineContract` whose scope — on the contract **and** on any endpoint override — is required and held to your union, retaining each concrete literal — [guide](../guide/contracts.md#scope) |
22351
- | `ContractFactoryConfig` | _type_ | optional scoped-factory policy, including explicit tool exposure |
22352
- | `ContractFactoryToolExposure` | _type_ | `'explicit'` — omitted endpoint exposure materializes as HTTP-only |
22353
- | `ExplicitScopedDefineContract` | _type_ | scoped factory authoring with explicit tool opt-in |
22354
- | `ExplicitToolExposureEndpoints` | _type_ | endpoint map after missing exposure is materialized as `['HTTP']` |
22590
+ | `ContractFactoryConfig` | _type_ | configuration passed to `createContractFactory<Scope>().configure({ toolExposure: { default: [...] } })`; the constructor itself takes no arguments |
22591
+ | `ContractFactoryToolExposure` | _type_ | `default: readonly Transport[]`; omitted endpoint exposure uses the immutable policy snapshot, intersected with intrinsic HTTP-only capabilities |
22355
22592
  | `FactoryScopedEndpoint` | _type_ | an endpoint authored through the factory — its own `scope` override is held to the same union |
22356
22593
  | `ScopedContractDef` | _type_ | a factory-defined contract whose `meta.scope` is the required concrete literal |
22357
22594
  | `ScopedDefineContract` | _type_ | the `defineContract` `createContractFactory` returns — endpoint `scope` overrides join the union |
@@ -23612,8 +23849,8 @@ Separate optional evolving Bun/OpenTUI package over a caller-composed headless h
23612
23849
 
23613
23850
  | Export | Kind | Summary |
23614
23851
  |--------|------|---------|
23615
- | `defineAgentTui` | function | typed config for title/theme/status rows, model catalog, context, commands, runtime bundle and optional host-evidenced recovery policy; the default never requeues acquired effects |
23616
- | `runAgentTui` | function | start one fresh durable conversation and terminal controller, recover durable work and publish its authenticated local session; `initialConversationId` is an explicit resume override |
23852
+ | `defineAgentTui` | function | typed config for title, `theme` (a `TerminalPalette` override) and `themeMode`, status rows, model catalog, context, commands, runtime bundle and optional host-evidenced recovery policy; the default never requeues acquired effects |
23853
+ | `runAgentTui` | function | start one fresh durable conversation and terminal controller, recover durable work and publish its authenticated local session; `initialConversationId` is an explicit resume override; it closes the session and exits the process on Ctrl-C and every signal OpenTUI catches unless `exitSignals: []` |
23617
23854
  | `defineTuiCommand` / `resolveTuiCommand` | function | typed composable slash-command registry; unknown slash input remains an ordinary model prompt |
23618
23855
  | `createAgentTuiController` | function | single admission, selection, approval, interruption and conversation-switch owner over the harness |
23619
23856
  | `startAgentTuiSessionHost` / `createAgentTuiClient` | function | authenticated mode-`0600` Unix-socket host/client for status, submit and interrupt through that controller |
@@ -23622,7 +23859,7 @@ Separate optional evolving Bun/OpenTUI package over a caller-composed headless h
23622
23859
  | `defaultAgentTuiStatusLine` / `AgentTuiStatusLineFormatter` | function / _type_ | terminal-native default rows and a host formatter over model capacity, durable snapshot, activity, workspace and local identities; `statusLine: false` hides the rows |
23623
23860
  | `createAgentTuiDiagnosticRecorder` / `AgentTuiDiagnosticEventSchema` | function / schema | bounded per-session metadata journal that rejects prompt, reasoning, tool-input and provider-cause payloads before disk admission |
23624
23861
 
23625
- The `stitchkit-agent` binary loads `stitchkit.agent.ts` by default and also exposes `sessions`,
23862
+ The `stitchkit-agent` binary runs React's production build unless `NODE_ENV` says otherwise and loads `stitchkit.agent.ts` by default and also exposes `sessions`,
23626
23863
  `status`, `send` and `interrupt`. `--workspace` addresses a host outside the caller's cwd,
23627
23864
  `send --idempotency-key` accepts caller-owned retry identity, and `interrupt` defaults to the
23628
23865
  active run returned by `status`. Session descriptors and sockets are local control credentials,
@@ -23645,6 +23882,65 @@ Renderer-neutral state only. This entrypoint imports neither React/OpenTUI nor t
23645
23882
  | `createTerminalCommandPalette` / `terminalCommandMatches` | function | bounded command filtering and keyboard selection over a collision-validated registry |
23646
23883
  | `resolveExactTerminalCommand` / `validateTerminalCommands` | function | exact dispatch and fail-closed name/alias validation |
23647
23884
  | `createTerminalOperationState` / `reduceTerminalOperationState` | function | confirmation and single-pending-operation lifecycle |
23885
+ | `createTerminalTable` / `reduceTerminalTable` / `projectTerminalTable` | function | live table: typed columns, rows validated on entry, sort with key tie-break and missing values last, tree expansion pruned to present rows, filter on displayed text, selection kept across data; a row's `tone` and a column's `tone(value, row)` colour state |
23886
+ | `layoutTerminalTableColumns` / `formatTerminalTableCell` / `visibleTerminalTableRows` / `terminalTableTreePrefix` | function | fixed and flexible column widths, `—` for missing values, the virtualized window, tree indentation |
23887
+ | `createTerminalList` / `reduceTerminalList` / `visibleTerminalListLines` | function | picker with sections, fuzzy query with title highlight ranges, multi-select, disabled items and "more above/below" counts |
23888
+ | `createTerminalForm` / `reduceTerminalForm` / `readTerminalForm` / `terminalFormFieldsFromJsonSchema` | function | typed fields (text, secret, number, integer, boolean, choice, JSON) from a JSON Schema, relative text edits, validation on leave and submit |
23889
+ | `createTerminalTabs` / `reduceTerminalTabs` | function | tabs with badges, wrap-around movement, digit selection and reconcile that keeps the selected tab |
23890
+ | `createTerminalNavigation` / `reduceTerminalNavigation` / `terminalBreadcrumbs` | function | screen stack keeping each level's state, root never popped |
23891
+ | `createTerminalFocus` / `reduceTerminalFocus` | function | focus ring plus layers that take the keyboard and give it back |
23892
+ | `createTerminalNotices` / `reduceTerminalNotices` / `nextTerminalNoticeExpiry` | function | bounded toasts on a caller clock, replaced by id |
23893
+ | `createTerminalTimeSeries` / `appendTerminalSample` / `sampleTerminalTimeSeries` | function | time window bounded by duration and sample count, value-in-force sampling with explicit gaps |
23894
+ | `terminalBrailleChart` / `terminalSparkline` / `terminalTimeAxis` / `terminalChartScale` | function | multi-series braille line or area chart as toned runs, sparkline, time axis |
23895
+ | `terminalMeter` / `layoutTerminalMeterGrid` | function | eighth-block bar toned by thresholds, unmeasured as muted; column-major core grid |
23896
+ | `terminalProgress` / `terminalSpinnerFrame` / `terminalIndeterminateBlock` / `formatTerminalDuration` | function | ratio, rate and ETA only when knowable; spinner frames; indeterminate bar; elapsed time |
23897
+ | `defineTerminalKeymap` / `matchTerminalKey` / `fitTerminalKeyHints` | function | conflict-checked bindings that both answer keys and draw a footer fitted to the width |
23898
+ | `terminalTableKeymap` / `terminalTableKey` / `terminalTabsKeymap` / `terminalTabsKey` | function | htop-style table bindings and tab bindings as keymaps |
23899
+ | `matchTerminalSearch` / `searchTerminalItems` / `terminalHighlightRuns` | function | multi-word fuzzy match with ranges, stable ranking, highlight runs |
23900
+ | `sanitizeTerminalText` / `terminalTextWidth` / `truncateTerminalText` / `fitTerminalText` / `wrapTerminalText` | function | strip escape and control sequences; measure, cut, pad and wrap by terminal cells |
23901
+ | `terminalTextEdit` / `applyTerminalTextEdit` | function | one-line edits as relative actions, so fast input is never lost |
23902
+ | `terminalJsonLines` / `layoutTerminalDetails` | function | toned, sanitized JSON lines; aligned key/value pairs |
23903
+ | `terminalPalettes` / `resolveTerminalPalette` / `TerminalPalette` / `TerminalTone` | value / function / _type_ | one semantic palette (surfaces plus `primary`, `muted`, `accent`, `success`, `warning`, `danger`) for dark and light terminals |
23904
+ | `terminalLabelsEn` / `terminalLabelsRu` / `TerminalLabels` | value / _type_ | every word the widgets draw |
23905
+
23906
+ ### `stitchkit-tui/widgets`
23907
+
23908
+ OpenTUI React widgets over the core state. React, `@opentui/core` and `@opentui/react` are peers;
23909
+ this entrypoint never loads `stitchkit`.
23910
+
23911
+ | Export | Kind | Summary |
23912
+ |--------|------|---------|
23913
+ | `TerminalAppShell` / `TerminalPanel` / `TerminalStatusBar` / `TerminalKeyHints` | component | breadcrumbs, status lines, body, fitted key hints and toast overlay; titled rounded panels with a focus frame |
23914
+ | `TerminalTable` | component | virtualized live table with header sort marks, tree, filter line and htop keys; row and cell tones in the palette, bold or dim where colour is missing |
23915
+ | `terminalToneAttributes` | function | the text attribute that carries a tone without colour: `danger` and `warning` bold, `muted` dim |
23916
+ | `TerminalList` | component | sections, fuzzy search line with highlights, `›` cursor, meta, badges, multi-select, description |
23917
+ | `runTerminalApp` / `RunTerminalAppOptions` / `RunningTerminalApp` | function / _type_ | start an application: renderer, theme and labels providers, the error boundary, and an exit with 128 + signal after `onClose` on Ctrl-C and every signal OpenTUI catches — a closed window leaves no orphan |
23918
+ | `TerminalErrorBoundary` / `TERMINAL_EXIT_SIGNALS` / `TerminalExitSignal` | component / value / _type_ | draws a render error and its stack in place of the screen, where OpenTUI's own boundary renders nothing under production React |
23919
+ | `TerminalForm` | component | fields edited in place, cycling choices, masked secrets, inline errors; a form without fields says so and submits `{}` on Enter |
23920
+ | `TerminalTextView` / `TerminalJsonView` | component | pager over sanitized wrapped text or toned JSON |
23921
+ | `TerminalMeter` / `TerminalMeterGrid` / `TerminalChart` / `TerminalSparkline` | component | htop meters and core grid; btop braille charts with axis |
23922
+ | `TerminalTabs` / `TerminalDialog` / `TerminalSpinner` / `TerminalProgressBar` / `TerminalAsyncView` / `TerminalDetails` / `TerminalNotices` / `TerminalHelp` | component | views, confirmation, loading, progress, async states, details, toasts, key help |
23923
+ | `TerminalThemeProvider` / `useTerminalPalette` / `useTerminalThemeMode` | component / hook | palette from the terminal's reported background (`auto`) or a fixed mode |
23924
+ | `TerminalLabelsProvider` / `TerminalFocusProvider` / `useTerminalFocused` | component / hook | product language and shared focus |
23925
+
23926
+ The `stitchkit-tui-gallery` binary shows every widget live.
23927
+
23928
+ ### `stitchkit-tui/build`
23929
+
23930
+ Build-time only; imports Bun and no React.
23931
+
23932
+ | Export | Kind | Summary |
23933
+ |--------|------|---------|
23934
+ | `compileTerminalApp` / `CompileTerminalAppOptions` | function / _type_ | one standalone executable for any Bun compile target, from any machine, with React's production build defined |
23935
+ | `terminalNativePlugin` / `TerminalNativePluginOptions` | function / _type_ | the `Bun.build` plugin behind it: the target's `@opentui/core-<os>-<arch>` at the pinned version from the registry, verified against `bun.lock`; other platforms' packages throw; musl refused |
23936
+ | `terminalNativeTarget` / `TerminalNativeTarget` | function / _type_ | the native package a compile target needs |
23937
+
23938
+ ### `stitchkit-tui/testing`
23939
+
23940
+ | Export | Kind | Summary |
23941
+ |--------|------|---------|
23942
+ | `renderTerminalTest` / `TerminalTestScreen` / `TerminalTestOptions` | function / _type_ | a screen inside the kit's providers on a test terminal; `press`, `type`, `burst`, `advance`, `waitForText`, `resize` run inside React's `act` and wait out a lone Esc |
23943
+ | `TerminalTestKey` / `TerminalTestModifiers` | _type_ | the mock input's key names and modifiers |
23648
23944
 
23649
23945
  ## `stitchkit/agent-runtime/sqlite/node`
23650
23946
 
@@ -23754,57 +24050,25 @@ SDK, `ai`, or other optional tool peers, so it is safe in a thin CLI bundle.
23754
24050
 
23755
24051
  ## `stitchkit/tools`
23756
24052
 
23757
- Server-only. Turns contracts into MCP and AI-agent tools. MCP server surfaces
23758
- need the `@modelcontextprotocol/server` v2 peer; MCP hosts/tests use the separate
23759
- `@modelcontextprotocol/client` package. Agent surfaces need the `ai` peer.
23760
-
23761
- Framework-owned MCP tools advertise the exact declared output schema and return
23762
- the validated value unchanged as `structuredContent`, including arrays, scalars
23763
- and `null`. Tools without an output contract advertise and return no structured
23764
- payload.
24053
+ Server-only, and imported without any optional peer: the transport-neutral tool layer —
24054
+ the registry, tool names and manifest, runtime tools, native tools, the invoker, the OAuth
24055
+ provider and the durability engine. An application that serves its operations over HTTP and
24056
+ CLI lists its tool names here with neither MCP nor the AI SDK installed. The adapters that
24057
+ need a peer have their own subpaths: [`stitchkit/tools/mcp`](#stitchkittoolsmcp) (the MCP
24058
+ server SDK), [`stitchkit/tools/agent`](#stitchkittoolsagent) (the `ai` peer) and
24059
+ [`stitchkit/tools/toolkit`](#stitchkittoolstoolkit) (both). → ADR 0284.
23765
24060
 
23766
24061
  | Export | Kind | Summary |
23767
24062
  |--------|------|---------|
23768
- | `createMcpHandler` | function | a stateless dual-era Streamable-HTTP MCP handler — [guide](../guide/mcp-and-agents.md#mcp--createmcphandler) |
23769
- | `createMcpHttpRoute` | function | framework-owned `RawRoute` adapter for an MCP HTTP handler |
23770
- | `createStdioMcpServer` | function | a complete stdio MCP server — [guide](../guide/mcp-and-agents.md#mcp-over-stdio--createstdiomcpserver) |
23771
- | `bindStdioProcessSignals` | function | explicitly bind OS signals to one close-only stdio handle — [guide](../guide/testing-and-deployment.md#stdio-process-signals) |
23772
- | `buildMcpServer` | function | build an `McpServer` from contract/runtime surfaces; no-auth configs omit the second argument |
23773
- | `mountMcp` | function | add contract tools to an existing `McpServer` — [guide](../guide/mcp-and-agents.md#mountmcp) |
23774
- | `mountAgent` | function | a Vercel AI SDK `ToolSet` from a service; failed executions reject through the SDK tool-error channel with a safe typed envelope, while successful output is never classified by field name — [guide](../guide/mcp-and-agents.md#ai-agents--mountagent) |
23775
24063
  | `defineRuntimeTool` | function | define one validated pathless operation for explicit MCP, Agent and/or CLI surfaces — [guide](../guide/mcp-and-agents.md#pathless-runtime-tools-and-multimodal-results) |
23776
24064
  | `createRuntimeToolFactory` | function | bind shared identity and Zod-validated per-call context for runtime tools — [guide](../guide/mcp-and-agents.md#pathless-runtime-tools-and-multimodal-results) |
23777
- | `createToolInvoker` | function | compile an exposure-aware in-process dispatcher over the canonical tool runner; use peer-free `stitchkit/tools/invoker`, or the full `stitchkit/tools` adapter barrel — [guide](../guide/mcp-and-agents.md#in-process-calls--createtoolinvoker) |
23778
- | `createToolkit` | function | context-typed tool mounts — [guide](../guide/cli.md#typed-context) |
24065
+ | `createToolInvoker` | function | compile an exposure-aware in-process dispatcher over the canonical tool runner; use `stitchkit/tools/invoker` or `stitchkit/tools`, both peer-free — [guide](../guide/mcp-and-agents.md#in-process-calls--createtoolinvoker) |
23779
24066
  | `mountViewFile` | function | a native multimodal "view file" MCP tool |
23780
24067
  | `resolveMedia` | function | resolve a media reference for a tool result |
23781
- | `validateMcpSchemas` | function | object-shaped assertion over the exact advertised schema surface — compatibility, typed properties and portable formats ([guide](../guide/mcp-and-agents.md#mcp-schema-validation-profile)) |
23782
24068
  | `listToolNames` | function | every contract/runtime tool name with origin, identity and transports — for stable snapshots — [guide](../guide/mcp-and-agents.md#pinning-tool-names--listtoolnames) |
23783
24069
  | `listContractToolNames` | function | the same listing straight from contracts — no handlers or stub services needed |
23784
24070
  | `describeToolCatalog` | function | inspect the exact model-facing names, source identities, schema byte sizes and deferred state; an explicit total byte budget fails closed |
23785
24071
  | `ToolCatalogConfig` / `ToolCatalogEntry` | _type_ | catalog source, transport, optional explicit schema budget and one inspectable result row |
23786
- | `McpHandlerConfig` | _type_ | server surface plus stateless HTTP transport config |
23787
- | `McpHttpConfig` | _type_ | HTTP auth, protected-resource, legacy-era and security options |
23788
- | `McpHttpHandler` | _type_ | framework-owned `{ fetch(request), close() }` lifecycle |
23789
- | `McpHttpSecurityConfig` | _type_ | Fetch-boundary Host and Origin allowlists |
23790
- | `McpLegacyPolicy` | _type_ | `'serve' \| 'reject'` protocol-era compatibility policy |
23791
- | `McpStdioHandle` | _type_ | closeable official stdio transport handle |
23792
- | `StdioMcpServerConfig` | _type_ | config for `createStdioMcpServer` |
23793
- | `StdioCloseTarget` | _type_ | minimal close-only target accepted by `bindStdioProcessSignals` |
23794
- | `StdioProcessSignalsOptions` | _type_ | signal source, close callbacks and phased error reporting |
23795
- | `StdioProcessSignalsBinding` | _type_ | close-only signal binding with observed `promise` and listener `close()` |
23796
- | `StdioProcessSignalsErrorPhase` | _type_ | `'prepare' \| 'close' \| 'complete'` reporting phase |
23797
- | `McpServerBuildConfig` | _type_ | shared config for `buildMcpServer` |
23798
- | `McpServerSharedConfig` | _type_ | transport-neutral options shared by direct and finite surface configs |
23799
- | `McpServer` | _type_ | official split-SDK server instance accepted by raw extension points |
23800
- | `DirectMcpSurfaceConfig` | _type_ | static or identity-dynamic `services` / `runtimeTools` source |
23801
- | `FiniteMcpSurfaceConfig` | _type_ | bounded `surfaces` registry plus typed selector |
23802
- | `McpSurfaceDefinition` | _type_ | one immutable `{ services, runtimeTools }` MCP surface |
23803
- | `McpSurfaceRegistry` | _type_ | finite keyed surface registry for eager preparation |
23804
- | `StdioAuthConfig` | _type_ | startup identity composed into `StdioMcpServerConfig` |
23805
- | `McpMountConfig` | _type_ | config for `mountMcp` |
23806
- | `McpSchemaValidationConfig` | _type_ | shared `{ policy, requireTypedProperties, allowUntyped, requirePortableFormats, allowFormats }` profile |
23807
- | `ValidateMcpSchemasConfig` | _type_ | standalone validation profile plus `services`, `extend`, flattening and logger |
23808
24072
  | `RuntimeToolDefinition` | _type_ | heterogeneous registration of pathless operations in `runtimeTools: [...]`; an inline handler is contextually typed by the parsed object, a definition constructed with its own schema registers unchanged, and the runner validates input before the handler |
23809
24073
  | `RuntimeToolDefinitionBase` | _type_ | common name, identity, input, exposure and MCP metadata fields |
23810
24074
  | `RuntimeToolDefinitionWithOutput` | _type_ | the one neutral construction: identity, schemas and a handler typed from them; the fourth type argument names the adapter's `present` extension (`RuntimeToolPresenters<…>` here, `RuntimeMcpToolPresenters<…>` on `stitchkit/tools/mcp`, none by default) |
@@ -23830,11 +24094,8 @@ payload.
23830
24094
  | `CimdCacheEvent` | _type_ | observable CIMD cache hit, miss, revalidation and eviction event |
23831
24095
  | `createSecureClientMetadataFetcher` | function | production HTTPS, DNS/IP-pinned CIMD fetcher |
23832
24096
  | `RuntimeAgentModelOutput` | _type_ | AI SDK model-facing text/JSON/content output returned by `present.agent` |
23833
- | `AgentMountConfig` | _type_ | config for `mountAgent` |
23834
24097
  | `AgentToolRegistry` / `AgentToolRegistryBuilder` / `AgentToolRegistryInput` | _type_ | the composed runtime surface and its builder: declared defaults, `replace`/`disable` by name, and the exact `{ tools, names }` a mount receives |
23835
24098
  | `defineToolRegistry` | function | compose runtime tools over one declared default set; an unknown `replace`/`disable` name is refused instead of silently leaving the default in place |
23836
- | `AgentContext` | _type_ | the context merged into agent tool handlers |
23837
- | `Toolkit` | _type_ | the context-pinned tool surface from `createToolkit` |
23838
24099
  | `ToolExtend` | _type_ | extra-args extension for `mountMcp` / `mountAgent` |
23839
24100
  | `ToolLifecycle` | _type_ | `beforeHandle` / `afterHandle` gate for tool calls — [guide](../guide/mcp-and-agents.md#guarding-tools--lifecycle) |
23840
24101
  | `ToolExecutionControlReason` | _type_ | internal managed-execution stop reason: stale run or requested interruption |
@@ -23866,7 +24127,6 @@ payload.
23866
24127
  | `NonPortableFormat` | _type_ | one `{ path, format }` portability finding |
23867
24128
  | `PORTABLE_JSON_SCHEMA_FORMATS` | constant | portable-format baseline used by MCP validation |
23868
24129
  | `ToolNameEntry` | _type_ | one `listToolNames` row — `{ kind, name, service, method, transports }` |
23869
- | `IncompatibleSchemaPolicy` | _type_ | `'throw' \| 'skip' \| 'warn'` |
23870
24130
  | `McpMediaContent` | _type_ | a multimodal MCP content item |
23871
24131
 
23872
24132
  ### Native tools
@@ -23903,7 +24163,7 @@ runtime-tool runner, plus deliberate raw MCP adapters over the same mechanics.
23903
24163
  | `AsyncOperationOutputCapability` | _type_ | optional result/artifact schema plus handler |
23904
24164
  | `RuntimeAsyncOperationConfig` | _type_ | runtime-only descriptor configuration |
23905
24165
  | `RuntimeAsyncOperation` | _type_ | inferred generated definitions and schemas |
23906
- | `AsyncOperationContractConfig` | _type_ | canonical contract config where start returns the operation id; optional `expose` names the transports per capability, because this contract is built inside the framework and never passes through a consumer's `toolExposure: 'explicit'` factory |
24166
+ | `AsyncOperationContractConfig` | _type_ | canonical contract config where start returns the operation id; optional `expose` names the transports per capability, because this contract is built inside the framework and never passes through a consumer's configured factory |
23907
24167
  | `AsyncOperationContractWithStartOutputConfig` | _type_ | canonical contract config with an application start envelope and typed id extractor |
23908
24168
  | `DefinedAsyncOperationContract` | _type_ | generated contract, capability keys, schemas and parsed adapters |
23909
24169
  | `ContractAsyncOperationConfig` | _type_ | literal contract method binding and handlers |
@@ -23970,6 +24230,77 @@ mountAgent(services, { durability: (toolCallId) => myLedgerFor(toolCallId) });
23970
24230
  | `AgentToolError` | class | the model-safe envelope a failed agent tool throws |
23971
24231
  | `isAgentToolError` | function | brand check for it — a consumer running its own loop no longer parses the message |
23972
24232
 
24233
+ ### Introspection & internals
24234
+
24235
+ Advanced building blocks — the shared machinery the mounts are built on.
24236
+
24237
+ | Export | Kind | Summary |
24238
+ |--------|------|---------|
24239
+ | `collectTools` | function | resolve a service's methods to mountable tools (the shared resolver) |
24240
+ | `createToolLogger` | function | a ready `afterToolCall` that logs every tool call — [guide](../guide/mcp-and-agents.md#logging-tool-calls--createtoollogger) |
24241
+ | `summarizeTransports` | function | mixed contract/runtime operation counts and per-source breakdown for a boot-time summary |
24242
+ | `buildToolManifest` | function | transport-aware searchable `{ name, description, inputSchema }` rows from a mixed surface |
24243
+ | `ToolLoggerConfig` | _type_ | config for `createToolLogger` |
24244
+ | `ToolCallRecord` | _type_ | the structured record `createToolLogger` passes to `onRecord` |
24245
+ | `TransportSummary` | _type_ | `{ contractServices, runtimeTools, totals, sources }` from `summarizeTransports` |
24246
+ | `TransportCounts` | _type_ | per-transport counts (`{ HTTP, MCP, AGENT, CLI }`) |
24247
+ | `ToolSurfaceProjection` | _type_ | the one readonly `{ services?, runtimeTools? }` container shared by full, MCP-only and introspection surfaces (`buildToolManifest`, `listToolNames`, `summarizeTransports` take `ToolSurfaceProjection<RuntimeToolDefinition>`) |
24248
+ | `ToolManifestConfig` | _type_ | mixed surface plus required model-facing `transport` and presentation options |
24249
+ | `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 |
24250
+ | `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 |
24251
+ | `ToolPresentationSchema` | _type_ | immutable model-facing JSON Schema document shared by tool transports |
24252
+ | `MountableTool` | _type_ | one operation with separate executable CLI argument schema and model-facing presentation schema |
24253
+ | `ToolManifestEntry` | _type_ | one `buildToolManifest` row |
24254
+
24255
+ ---
24256
+
24257
+ ## `stitchkit/tools/mcp`
24258
+
24259
+ Server-only, and the one place the MCP server is exported. Its runtime and strict NodeNext
24260
+ declarations require Zod and the `@modelcontextprotocol/server` v2 peer, with no AI SDK peer;
24261
+ MCP hosts and tests use the separate `@modelcontextprotocol/client` package.
24262
+
24263
+ Framework-owned MCP tools advertise the exact declared output schema and return
24264
+ the validated value unchanged as `structuredContent`, including arrays, scalars
24265
+ and `null`. Tools without an output contract advertise and return no structured
24266
+ payload. See [MCP guide](../guide/mcp-and-agents.md#mcp--createmcphandler).
24267
+
24268
+ | Export | Kind | Purpose |
24269
+ |--------|------|---------|
24270
+ | `createMcpHandler` | function | a stateless dual-era Streamable-HTTP MCP handler — [guide](../guide/mcp-and-agents.md#mcp--createmcphandler) |
24271
+ | `createMcpHttpRoute` | function | framework-owned `RawRoute` adapter for an MCP HTTP handler |
24272
+ | `createStdioMcpServer` | function | a complete stdio MCP server — [guide](../guide/mcp-and-agents.md#mcp-over-stdio--createstdiomcpserver) |
24273
+ | `bindStdioProcessSignals` | function | explicitly bind OS signals to one close-only stdio handle — [guide](../guide/testing-and-deployment.md#stdio-process-signals) |
24274
+ | `buildMcpServer` | function | build an `McpServer` from contract/runtime surfaces; no-auth configs omit the second argument |
24275
+ | `mountMcp` | function | add contract tools to an existing `McpServer` — [guide](../guide/mcp-and-agents.md#mountmcp) |
24276
+ | `validateMcpSchemas` | function | object-shaped assertion over the exact advertised schema surface — compatibility, typed properties and portable formats ([guide](../guide/mcp-and-agents.md#mcp-schema-validation-profile)) |
24277
+ | `McpHandlerConfig` | _type_ | server surface plus stateless HTTP transport config |
24278
+ | `McpHttpConfig` | _type_ | HTTP auth, protected-resource, legacy-era and security options |
24279
+ | `McpHttpHandler` | _type_ | framework-owned `{ fetch(request), close() }` lifecycle |
24280
+ | `McpHttpSecurityConfig` | _type_ | Fetch-boundary Host and Origin allowlists |
24281
+ | `McpLegacyPolicy` | _type_ | `'serve' \| 'reject'` protocol-era compatibility policy |
24282
+ | `McpStdioHandle` | _type_ | closeable official stdio transport handle |
24283
+ | `StdioMcpServerConfig` | _type_ | config for `createStdioMcpServer` |
24284
+ | `StdioCloseTarget` | _type_ | minimal close-only target accepted by `bindStdioProcessSignals` |
24285
+ | `StdioProcessSignalsOptions` | _type_ | signal source, close callbacks and phased error reporting |
24286
+ | `StdioProcessSignalsBinding` | _type_ | close-only signal binding with observed `promise` and listener `close()` |
24287
+ | `StdioProcessSignalsErrorPhase` | _type_ | `'prepare' \| 'close' \| 'complete'` reporting phase |
24288
+ | `McpServerBuildConfig` | _type_ | shared config for `buildMcpServer` |
24289
+ | `McpServerSharedConfig` | _type_ | transport-neutral options shared by direct and finite surface configs |
24290
+ | `McpServer` | _type_ | official split-SDK server instance accepted by raw extension points |
24291
+ | `DirectMcpSurfaceConfig` | _type_ | static or identity-dynamic `services` / `runtimeTools` source |
24292
+ | `FiniteMcpSurfaceConfig` | _type_ | bounded `surfaces` registry plus typed selector |
24293
+ | `McpSurfaceDefinition` | _type_ | one immutable `{ services, runtimeTools }` MCP surface |
24294
+ | `McpSurfaceRegistry` | _type_ | finite keyed surface registry for eager preparation |
24295
+ | `StdioAuthConfig` | _type_ | startup identity composed into `StdioMcpServerConfig` |
24296
+ | `McpMountConfig` | _type_ | config for `mountMcp` |
24297
+ | `McpSchemaValidationConfig` | _type_ | shared `{ policy, requireTypedProperties, allowUntyped, requirePortableFormats, allowFormats }` profile |
24298
+ | `ValidateMcpSchemasConfig` | _type_ | standalone validation profile plus `services`, `extend`, flattening and logger |
24299
+ | `IncompatibleSchemaPolicy` | _type_ | `'throw' \| 'skip' \| 'warn'` |
24300
+ | `RuntimeToolDefinitionWithOutput` / `RuntimeToolDefinitionWithoutOutput` | _type_ | the one neutral construction, shared with `stitchkit/cli` and `stitchkit/tools`; MCP-only presenters are its fourth type argument, `RuntimeMcpToolPresenters<z.output<typeof output>>` |
24301
+ | `RuntimeToolDefinition` | _type_ | heterogeneous registration; the canonical runner validates input/output before invocation |
24302
+ | `RuntimeMcpToolPresenters` / `RuntimeMcpPresentation` | _type_ | official SDK content without framework-owned `structuredContent` or `isError`; one presenter owner shared with the full SDK surface |
24303
+
23973
24304
  ### Catalog stamp
23974
24305
 
23975
24306
  Every advertised tool and every tool result carries the fingerprint of the catalog it came
@@ -24003,44 +24334,26 @@ Interactive MCP resources — [ADR 0019](../decisions/0019-generic-native-tools.
24003
24334
  | `McpAppResourceMeta` | _type_ | resource `_meta` for an MCP App |
24004
24335
  | `McpAppCsp` | _type_ | the widget content-security policy |
24005
24336
 
24006
- ### Introspection & internals
24337
+ ## `stitchkit/tools/agent`
24007
24338
 
24008
- Advanced building blocks — the shared machinery the mounts are built on.
24339
+ Server-only. A contract's operations as Vercel AI SDK tools; needs Zod and the `ai`
24340
+ peer, not the MCP server SDK. → ADR 0284.
24009
24341
 
24010
24342
  | Export | Kind | Summary |
24011
24343
  |--------|------|---------|
24012
- | `collectTools` | function | resolve a service's methods to mountable tools (the shared resolver) |
24013
- | `createToolLogger` | function | a ready `afterToolCall` that logs every tool call — [guide](../guide/mcp-and-agents.md#logging-tool-calls--createtoollogger) |
24014
- | `summarizeTransports` | function | mixed contract/runtime operation counts and per-source breakdown for a boot-time summary |
24015
- | `buildToolManifest` | function | transport-aware searchable `{ name, description, inputSchema }` rows from a mixed surface |
24016
- | `ToolLoggerConfig` | _type_ | config for `createToolLogger` |
24017
- | `ToolCallRecord` | _type_ | the structured record `createToolLogger` passes to `onRecord` |
24018
- | `TransportSummary` | _type_ | `{ contractServices, runtimeTools, totals, sources }` from `summarizeTransports` |
24019
- | `TransportCounts` | _type_ | per-transport counts (`{ HTTP, MCP, AGENT, CLI }`) |
24020
- | `ToolSurfaceProjection` | _type_ | the one readonly `{ services?, runtimeTools? }` container shared by full, MCP-only and introspection surfaces (`buildToolManifest`, `listToolNames`, `summarizeTransports` take `ToolSurfaceProjection<RuntimeToolDefinition>`) |
24021
- | `ToolManifestConfig` | _type_ | mixed surface plus required model-facing `transport` and presentation options |
24022
- | `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 |
24023
- | `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 |
24024
- | `ToolPresentationSchema` | _type_ | immutable model-facing JSON Schema document shared by tool transports |
24025
- | `MountableTool` | _type_ | one operation with separate executable CLI argument schema and model-facing presentation schema |
24026
- | `ToolManifestEntry` | _type_ | one `buildToolManifest` row |
24027
-
24028
- ---
24344
+ | `mountAgent` | function | a Vercel AI SDK `ToolSet` from a service; failed executions reject through the SDK tool-error channel with a safe typed envelope, while successful output is never classified by field name — [guide](../guide/mcp-and-agents.md#ai-agents--mountagent) |
24345
+ | `AgentMountConfig` | _type_ | config for `mountAgent` |
24346
+ | `AgentContext` | _type_ | the context merged into agent tool handlers |
24029
24347
 
24030
- ## `stitchkit/tools/mcp`
24348
+ ## `stitchkit/tools/toolkit`
24031
24349
 
24032
- A server-only leaf over the same MCP owner as `stitchkit/tools`. Its runtime and strict
24033
- NodeNext declarations require Zod and `@modelcontextprotocol/server`, with no AI SDK peer.
24034
- HTTP/stdio factories, mounts, schema policy, catalog and resource helpers keep their canonical
24035
- behavior and names. See [MCP guide](../guide/mcp-and-agents.md#mcp--createmcphandler).
24350
+ Server-only. One context-typed tool set served over MCP and to an AI SDK agent, so it
24351
+ needs both the `@modelcontextprotocol/server` and `ai` peers. → ADR 0284.
24036
24352
 
24037
- | Export | Kind | Purpose |
24353
+ | Export | Kind | Summary |
24038
24354
  |--------|------|---------|
24039
- | `createMcpHandler` / `createMcpHttpRoute` / `buildMcpServer` / `mountMcp` | function | the shared authenticated HTTP handler, raw-route adapter, SDK server and contract mount |
24040
- | `createStdioMcpServer` / `bindStdioProcessSignals` | function | the shared stdio server and bounded process cleanup |
24041
- | `RuntimeToolDefinitionWithOutput` / `RuntimeToolDefinitionWithoutOutput` | _type_ | the one neutral construction, shared with `stitchkit/cli` and `stitchkit/tools`; MCP-only presenters are its fourth type argument, `RuntimeMcpToolPresenters<z.output<typeof output>>` |
24042
- | `RuntimeToolDefinition` | _type_ | heterogeneous registration; the canonical runner validates input/output before invocation |
24043
- | `RuntimeMcpToolPresenters` / `RuntimeMcpPresentation` | _type_ | official SDK content without framework-owned `structuredContent` or `isError`; one presenter owner shared with the full SDK surface |
24355
+ | `createToolkit` | function | context-typed tool mounts — [guide](../guide/cli.md#typed-context) |
24356
+ | `Toolkit` | _type_ | the context-pinned tool surface from `createToolkit` |
24044
24357
 
24045
24358
  ## `stitchkit/tools/connections`
24046
24359
 
@@ -24316,7 +24629,7 @@ shapes are `stitchkit/tracking/contract`, the server half is
24316
24629
  | Export | Kind | Summary |
24317
24630
  |--------|------|---------|
24318
24631
  | `createTrackingClient` | function | the client: visit lease and renewal, pending queue, synchronous identity from reserved sequences, outbox and flush lease, delivery, the string-body page-leave beacon plus a queued copy, visible time, scroll milestones, declarative clicks, heartbeats |
24319
- | `TrackingClient` / `TrackingClientConfig` | _type_ | `track` / `onNavigate` / `start` / `visitId` / `browserStreamId` / `attribution`, and the host, `buildId`, `builtin` names, `bootstrap`, `deliver`, optional `decorate` (your `eventExtras` on every minted event), `unload` / `unloadUrl`, `outbox`, `onUnauthorized`, `onVisit`, `onFailure`, `isAction`, `referrerMap`, `clickAttributes`, `hrefPolicy` (`path-only` by default; `full` retains click query/fragment before `decorate`), `scrollMilestones`, `batchSize`, periods and limits |
24632
+ | `TrackingClient` / `TrackingClientConfig` | _type_ | `track` / `onNavigate` / `start` / `visitId` / `browserStreamId` / `attribution`, and the host, `buildId`, `builtin` names, `bootstrap`, `deliver`, optional `decorate` (your `eventExtras` on every minted event), `unload` / `unloadUrl`, `outbox`, `onUnauthorized`, `onVisit`, `onFailure`, `isAction`, `clickIdParams` (ordered application-defined marker names; no values retained), `clickAttributes`, `hrefPolicy` (`path-only` by default; `full` retains click query/fragment before `decorate`), `scrollMilestones`, `batchSize`, periods and limits |
24320
24633
  | `TrackFn` / `EventsWithMetadata` / `EventsWithoutMetadata` | _type_ | `track(type)` / `track(type, metadata)` typed by the application's metadata map, and the two halves of that map |
24321
24634
  | `BuiltinTrackingEventTypes` / `CONVENTIONAL_TRACKING_EVENT_TYPES` | _type_ / const | the application's names for the events the client emits itself; the conventional `PAGE_VIEW` … `INTERACTION` set |
24322
24635
  | `browserTrackingHost` / `TrackingHost` / `TrackingPageContext` | function / _type_ | everything the client takes from a browser tab behind one interface — page context, visibility, scroll depth, events, intervals, clocks, storage — so a test or a non-tab host can supply its own |
@@ -24329,8 +24642,8 @@ shapes are `stitchkit/tracking/contract`, the server half is
24329
24642
  | `createVisibleTimeMeter` / `VisibleTimeMeter` / `VisibleInterval` / `VisibleHeartbeat` / `VisibleTimeMeterOptions` | function / _type_ | additive visible time — `checkpoint` / `cut` / `heartbeat`, each interval with its own id; a hidden heartbeat carries no interval |
24330
24643
  | `scrollDepthPercent` / `createScrollMilestones` / `ScrollMilestones` | function / _type_ | depth as a percentage of the scrollable range; milestones fired once each per page |
24331
24644
  | `createOncePerPage` / `OncePerPage` | function / _type_ | one fact per key per page within a window — the guard a twice-mounted component cannot keep itself |
24332
- | `resolveAttribution` / `ResolveAttributionInput` / `ResolvedAttribution` / `AttributionStorage` / `ReferrerRule` | function / _type_ | first-touch once, current-touch on every new UTM; the referrer → source/medium map is the application's, there is no built-in list |
24333
- | `parseUtmFromSearch` / `parseReferrer` | function | the two parsers `resolveAttribution` is built from |
24645
+ | `resolveAttribution` / `ResolveAttributionInput` / `ResolvedAttribution` / `AttributionStorage` | function / _type_ | persisted first-touch with TTL, raw current-touch on each capture; actual UTM, referrer and optional `clickId` name from `clickIdParams`; `storage: null` collects without persistence; no channel inference |
24646
+ | `parseUtmFromSearch` | function | reads only `utm_*`, preserving case and whitespace up to 200 characters per field; any tag is sufficient, no source required |
24334
24647
  | `redactTrackingText` / `redactTrackingValue` | functions | mask passwords, sign-in credentials and signed launch data in text or a value of any shape; the key stays, and navigation and campaign parameters are untouched |
24335
24648
  | `resolveTrackedClick` / `ResolveTrackedClickOptions` / `TrackedClick` / `TrackedClickAttributes` / `ClickTarget` | function / _type_ | `data-track` → click, `data-track-action` → interaction, a link to another origin → outbound; attribute names configurable; `hrefPolicy: 'full'` retains query/fragment (default `path-only` omits them), URL credentials excluded; `leavesPage` says which path to send on |
24336
24649
 
@@ -24346,7 +24659,7 @@ browser code that only sends events does not import it. → ADR 0280
24346
24659
  | `createTrackingContract` / `TrackingContractConfig` / `TrackingContractEndpoints` | function / _type_ | `bootstrap` (`POST /visit`) and `track` (`POST /events`, `safelistedBody`) from the application's event types, scope and optional extras |
24347
24660
  | `createTrackingSchemas` / `TrackingSchemas` / `TrackingSchemasConfig` | function / _type_ | every schema of one surface — `event`, `request`, `response`, `disposition`, `entry`, `bootstrap` — from `eventTypes`, optional `eventExtras`, `buildIdPattern`, `maxEventsPerBatch` |
24348
24661
  | `TrackingEventEnvelope` / `TrackingEventShape` | _type_ | `eventId`, `visitId`, `browserStreamId`, `browserSequence`, `type`, `page`, `metadata`, `clientTimestamp` |
24349
- | `TrackEventsRequest` / `TrackEventsResponse` / `TrackEventsResponseSchema` | _type_ / schema | a batch (`buildId`, `events`, `utm`) and its answer (`accepted`, `dispositions`) |
24662
+ | `TrackEventsRequest` / `TrackEventsResponse` / `TrackEventsResponseSchema` | _type_ / schema | a batch (`buildId`, `events`; attribution travels with the visit bootstrap) and its answer (`accepted`, `dispositions`) |
24350
24663
  | `TrackingDisposition` / `TrackingDispositionSchema` / `TrackingDispositionStatus` / `TrackingDispositionStatusSchema` | _type_ / schema | `accepted` / `duplicate` / `identity-invalid` / `excluded-bot` — every one terminal |
24351
24664
  | `VisitEntryContext` / `VisitEntryContextShape` / `VisitBootstrapResponse` / `VisitBootstrapResponseSchema` | _type_ / schema | what a browser reports to open a visit (lineage, landing, referrer, UTM, display, screen, `buildId`, outbox health) and the lease it gets back |
24352
24665
  | `TrackingOutboxState` / `TrackingOutboxStateSchema` | _type_ / schema | `available` / `unavailable` |
@@ -24437,7 +24750,15 @@ adapter is used.
24437
24750
  | `createGeoIpResolver` / `GeoIpResolver` / `GeoIpResolverOptions` / `GeoIpSnapshot` | function / _type_ | public-IP lookup over one stable reader generation; unavailable start, last-known-good degraded reload and cleanup are explicit states |
24438
24751
  | `createMaxMindGeoIpLoader` / `MaxMindGeoIpLoaderOptions` / `MaxMindGeoIpPaths` | function / _type_ | lazy optional-peer City+ASN loader with stable before/after file revision checks |
24439
24752
  | `GeoIpReader` / `GeoIpReaderLoader` | _type_ | peer-neutral reader/generation seam for adapters and tests |
24440
- | `GeoAttribution` / `mapGeoIpRecord` | _type_ / function | generic optional place and autonomous-system projection; application-specific fields remain outside |
24753
+ | `GeoAttribution` / `mapGeoIpRecord` | _type_ / function | strict readonly sparse City + ASN snapshot: continent, located and registered country as separate facts, GeoNames IDs, first region/city/postal, coordinates/timezone, `accuracyRadiusKm`, autonomous system and all source languages in five `localizedNames` groups; each malformed field is omitted on its own; custom resolver mappers remain app-owned — [guide](../guide/geo.md#lookups) |
24754
+
24755
+ ## `stitchkit/geo/contract`
24756
+
24757
+ The place snapshot's schema, browser-safe, for a shared contract. Brings `zod`.
24758
+
24759
+ | Export | Kind | Summary |
24760
+ |--------|------|---------|
24761
+ | `GeoAttributionSchema` / `GeoAttribution` | schema / _type_ | the strict readonly Zod owner of the snapshot `stitchkit/geo` resolves — [guide](../guide/geo.md#lookups) |
24441
24762
 
24442
24763
  ---
24443
24764