stitchkit 0.122.2 → 0.123.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +80 -0
- package/dist/browser/client.d.ts.map +1 -1
- package/dist/browser/contract-identity.d.ts +21 -0
- package/dist/browser/contract-identity.d.ts.map +1 -0
- package/dist/browser/unknown-operation.d.ts +3 -0
- package/dist/browser/unknown-operation.d.ts.map +1 -0
- package/dist/contract/define.d.ts +6 -2
- package/dist/contract/define.d.ts.map +1 -1
- package/dist/contract/endpoint-capabilities.d.ts.map +1 -1
- package/dist/contract/errors.d.ts +4 -0
- package/dist/contract/errors.d.ts.map +1 -1
- package/dist/contract/fingerprint.d.ts +51 -0
- package/dist/contract/fingerprint.d.ts.map +1 -0
- package/dist/contract/identity.d.ts +21 -0
- package/dist/contract/identity.d.ts.map +1 -0
- package/dist/contract/stream.d.ts +32 -0
- package/dist/contract/stream.d.ts.map +1 -1
- package/dist/entrypoints/agent-runtime/browser.js +2 -2
- package/dist/entrypoints/agent-runtime/coding-tools.js +11 -11
- package/dist/entrypoints/agent-runtime/harness.js +12 -12
- package/dist/entrypoints/agent-runtime/openrouter.js +1 -1
- package/dist/entrypoints/agent-runtime/realtime.js +2 -2
- package/dist/entrypoints/agent-runtime/sandbox.js +11 -11
- package/dist/entrypoints/agent-runtime/sqlite/bun.js +15 -15
- package/dist/entrypoints/agent-runtime/sqlite/node.js +15 -15
- package/dist/entrypoints/agent-runtime/testing.js +15 -15
- package/dist/entrypoints/agent-runtime.js +22 -22
- package/dist/entrypoints/application/diagnostic-journal.js +5 -5
- package/dist/entrypoints/application/directory-inbox.js +5 -5
- package/dist/entrypoints/application/grammy.js +1 -1
- package/dist/entrypoints/application.js +7 -7
- package/dist/entrypoints/cli/publish.js +5 -5
- package/dist/entrypoints/cli.d.ts +1 -1
- package/dist/entrypoints/cli.d.ts.map +1 -1
- package/dist/entrypoints/cli.js +20 -17
- package/dist/entrypoints/contract.d.ts +2 -0
- package/dist/entrypoints/contract.d.ts.map +1 -1
- package/dist/entrypoints/contract.js +11 -5
- package/dist/entrypoints/files/packaging.d.ts +1 -0
- package/dist/entrypoints/files/packaging.d.ts.map +1 -1
- package/dist/entrypoints/files/packaging.js +230 -61
- package/dist/entrypoints/files.js +5 -5
- package/dist/entrypoints/geo.js +3 -3
- package/dist/entrypoints/http.js +3 -3
- package/dist/entrypoints/index.d.ts +1 -0
- package/dist/entrypoints/index.d.ts.map +1 -1
- package/dist/entrypoints/index.js +45 -24
- package/dist/entrypoints/live.js +2 -2
- package/dist/entrypoints/node.js +11 -11
- package/dist/entrypoints/observability.js +4 -4
- package/dist/entrypoints/primitives.js +1 -1
- package/dist/entrypoints/process/owner-loss.js +2 -2
- package/dist/entrypoints/process.d.ts +6 -1
- package/dist/entrypoints/process.d.ts.map +1 -1
- package/dist/entrypoints/process.js +370 -9
- package/dist/entrypoints/react.js +2 -2
- package/dist/entrypoints/realtime.js +3 -3
- package/dist/entrypoints/remote.js +20 -10
- package/dist/entrypoints/server.d.ts +1 -0
- package/dist/entrypoints/server.d.ts.map +1 -1
- package/dist/entrypoints/server.js +56 -20
- package/dist/entrypoints/telegram/screens.js +4 -4
- package/dist/entrypoints/telegram.js +6 -6
- package/dist/entrypoints/testing.js +10 -10
- package/dist/entrypoints/tools/agent.js +9 -9
- package/dist/entrypoints/tools/connections.js +7 -7
- package/dist/entrypoints/tools/contract.js +1 -1
- package/dist/entrypoints/tools/invoker.js +7 -7
- package/dist/entrypoints/tools/mcp.js +9 -9
- package/dist/entrypoints/tools/toolkit.js +15 -14
- package/dist/entrypoints/tools.js +17 -17
- package/dist/entrypoints/tracking/contract.js +2 -2
- package/dist/entrypoints/tracking.js +1 -1
- package/dist/files/client-artifact.d.ts +75 -0
- package/dist/files/client-artifact.d.ts.map +1 -0
- package/dist/{index-am1h8c9v.js → index-0d3sm0m9.js} +5 -5
- package/dist/{index-72xtvcvr.js → index-0ndz5r8z.js} +1 -1
- package/dist/index-0v9xb8pz.js +66 -0
- package/dist/{index-qcn84byh.js → index-0w0hxp7n.js} +4 -0
- package/dist/{index-3za04re9.js → index-1c945ten.js} +1 -1
- package/dist/{index-r6v4mc67.js → index-1d7s7da0.js} +1 -1
- package/dist/{index-zsvzhjtk.js → index-20hn8pbh.js} +1 -1
- package/dist/{index-f6pw75e1.js → index-2x2ch0bt.js} +2 -2
- package/dist/{index-61hve0ez.js → index-303qne11.js} +1 -1
- package/dist/{index-g149e9pk.js → index-3bzk2645.js} +1 -1
- package/dist/index-45dyex0j.js +119 -0
- package/dist/{index-nwm3ww60.js → index-64b2dxqz.js} +1 -1
- package/dist/{index-aqwg2dm5.js → index-6k47xdtt.js} +2 -2
- package/dist/index-7q05ag0z.js +10 -0
- package/dist/{index-kz9734jc.js → index-96yjwwgf.js} +6 -5
- package/dist/{index-ff39f9dr.js → index-993vtv1w.js} +3 -3
- package/dist/{index-4jpykzvw.js → index-99nnsz39.js} +2 -2
- package/dist/{index-pfvde2fz.js → index-9ypczvpj.js} +1 -1
- package/dist/{index-qch1vcaa.js → index-a7ebd769.js} +2 -2
- package/dist/{index-98hv3sba.js → index-aw6kx2e6.js} +1 -1
- package/dist/{index-yj6saztf.js → index-bz9cmnqq.js} +96 -14
- package/dist/{index-mtwh9hyp.js → index-cjzaj7ev.js} +1 -1
- package/dist/{index-gtx3eknx.js → index-dv7pa9w6.js} +2 -2
- package/dist/{index-sa99p98w.js → index-eabx6qjh.js} +3 -3
- package/dist/{index-8yxgcyt6.js → index-egg0yp2j.js} +1 -1
- package/dist/{index-h1j71dr1.js → index-enkc9pem.js} +1 -1
- package/dist/{index-aqmadanx.js → index-fa53vygr.js} +2 -2
- package/dist/{index-e6c2spsd.js → index-fpwbskp2.js} +1 -1
- package/dist/{index-m8a44e64.js → index-ftk3838x.js} +1 -1
- package/dist/{index-j95jp7ee.js → index-gdqawggb.js} +1 -1
- package/dist/{index-9dyd8xzf.js → index-hkw233vx.js} +2 -2
- package/dist/{index-9jfdm5tg.js → index-hkxgennw.js} +7 -1
- package/dist/{index-qaqxdbxf.js → index-j8rda9n6.js} +7 -7
- package/dist/{index-4tyt1qya.js → index-jc5xgkpk.js} +2 -2
- package/dist/{index-s9a0fjh3.js → index-kffm4r8w.js} +1 -1
- package/dist/{index-5w6t91ze.js → index-m8ddk770.js} +1 -1
- package/dist/{index-2fc1k9k2.js → index-mdyhr36s.js} +32 -122
- package/dist/{index-r7a7r7ha.js → index-mfntvj58.js} +6 -3
- package/dist/{index-gq3fnq3f.js → index-n90tkkcv.js} +2 -2
- package/dist/{index-k42ckyzx.js → index-ndjzbt2v.js} +2 -2
- package/dist/{index-ycqhyars.js → index-qc7tq0za.js} +3 -3
- package/dist/{index-9frf01t9.js → index-r0xr7y4m.js} +1 -1
- package/dist/{index-s3yxw6kt.js → index-s512scra.js} +9 -7
- package/dist/{index-1h2zv8rx.js → index-sqc6ezrb.js} +41 -1
- package/dist/{index-2bqf0a74.js → index-sqkttz7z.js} +1 -1
- package/dist/{index-rbzq0q16.js → index-t8jz0e20.js} +1 -1
- package/dist/{index-8ryydawp.js → index-tt3ej8r3.js} +1 -1
- package/dist/{index-x4p3kw1p.js → index-tynna0n4.js} +6 -6
- package/dist/{index-72r9k0av.js → index-v62cpkx3.js} +1 -1
- package/dist/{index-10rg78vf.js → index-v8xm2s9v.js} +2 -2
- package/dist/{index-y6ye582c.js → index-w0wy05gr.js} +10 -10
- package/dist/{index-kw1ws0cs.js → index-w3nv6kr8.js} +1 -1
- package/dist/{index-eeahxwvg.js → index-ww3gss55.js} +1 -1
- package/dist/{index-6j15ktag.js → index-wzqd6pen.js} +1 -1
- package/dist/{index-aswegq1r.js → index-xjb131sc.js} +367 -131
- package/dist/{index-1fyea34p.js → index-yebkpytc.js} +5 -5
- package/dist/{index-bgcwxhxh.js → index-yfpt7sxr.js} +24 -46
- package/dist/{index-69t14s4a.js → index-yt1gg11x.js} +3 -2
- package/dist/{index-jk29rwkr.js → index-zfncctjp.js} +1 -1
- package/dist/internal/stable-digest.d.ts +5 -0
- package/dist/internal/stable-digest.d.ts.map +1 -1
- package/dist/internal/typed.d.ts +2 -0
- package/dist/internal/typed.d.ts.map +1 -1
- package/dist/process/operation-child.d.ts +38 -0
- package/dist/process/operation-child.d.ts.map +1 -0
- package/dist/process/operation-wire.d.ts +68 -0
- package/dist/process/operation-wire.d.ts.map +1 -0
- package/dist/process/operation.d.ts +74 -0
- package/dist/process/operation.d.ts.map +1 -0
- package/dist/server/contract-identity.d.ts +10 -0
- package/dist/server/contract-identity.d.ts.map +1 -0
- package/dist/server/contract-stream.d.ts.map +1 -1
- package/dist/server/endpoint-response.d.ts.map +1 -1
- package/dist/server/error-hook.d.ts +1 -0
- package/dist/server/error-hook.d.ts.map +1 -1
- package/dist/tools/cli/args-route.d.ts.map +1 -1
- package/dist/tools/cli/args-tokens.d.ts.map +1 -1
- package/dist/tools/cli/command.d.ts +39 -18
- package/dist/tools/cli/command.d.ts.map +1 -1
- package/dist/tools/cli/format.d.ts.map +1 -1
- package/dist/tools/cli/help.d.ts.map +1 -1
- package/dist/tools/cli/invoke.d.ts +3 -1
- package/dist/tools/cli/invoke.d.ts.map +1 -1
- package/dist/tools/cli/io.d.ts +6 -0
- package/dist/tools/cli/io.d.ts.map +1 -1
- package/dist/tools/cli/managed-surface.d.ts +2 -0
- package/dist/tools/cli/managed-surface.d.ts.map +1 -1
- package/dist/tools/cli/policy.d.ts +14 -0
- package/dist/tools/cli/policy.d.ts.map +1 -1
- package/dist/tools/cli/run-managed.d.ts.map +1 -1
- package/dist/tools/cli/run-native.d.ts.map +1 -1
- package/dist/tools/cli/run-stream.d.ts +23 -0
- package/dist/tools/cli/run-stream.d.ts.map +1 -0
- package/dist/tools/cli/session.d.ts +8 -0
- package/dist/tools/cli/session.d.ts.map +1 -1
- package/dist/tools/cli/stream-surface.d.ts +17 -0
- package/dist/tools/cli/stream-surface.d.ts.map +1 -0
- package/dist/tools/execute.d.ts +7 -1
- package/dist/tools/execute.d.ts.map +1 -1
- package/dist/tools/invoker.d.ts.map +1 -1
- package/dist/tools/remote.d.ts.map +1 -1
- package/llms/cli.2.txt +56 -1
- package/llms/cli.txt +68 -1
- package/llms/contract.txt +4 -1
- package/llms/files-packaging.txt +10 -0
- package/llms/process.2.txt +75 -1
- package/llms/process.txt +59 -25
- package/llms/server.4.txt +8 -4
- package/llms/server.5.txt +1 -0
- package/llms/stitchkit.2.txt +74 -0
- package/llms/stitchkit.txt +5 -5
- package/llms/tools.txt +1 -1
- package/llms/{upgrading-0.106.0-0.122.0.txt → upgrading-0.106.0-0.123.0.txt} +50 -3
- package/llms/upgrading.txt +1 -1
- package/llms-full.txt +379 -13
- package/llms.txt +7 -7
- package/package.json +1 -1
- package/dist/index-h2fhjqy7.js +0 -19
- package/dist/index-m5m9rmvt.js +0 -44
package/llms-full.txt
CHANGED
|
@@ -65,9 +65,9 @@ own, recorded as an ADR.
|
|
|
65
65
|
| `stitchkit/cli` | server | stable | `createCli` / `createCliInvoker` — runtime and strict NodeNext declarations without MCP SDK / `ai` |
|
|
66
66
|
| `stitchkit/cli/publish` | build and release tooling (Bun or Node, POSIX) | evolving | `publishCli` — commit a verified CLI distribution and move its public manifest last; see [CLI guide](cli.md#publishing-a-complete-version) |
|
|
67
67
|
| `stitchkit/remote` | browser **and** server | stable | peer-free `implementRemote` for thin HTTP proxy processes |
|
|
68
|
-
| `stitchkit/process` | server (Bun or Node, POSIX) | evolving | finite native commands, byte sinks, bounded capture, process-group cancellation and opt-in Linux/Darwin owner-loss cleanup |
|
|
68
|
+
| `stitchkit/process` | server (Bun or Node, POSIX) | evolving | finite native commands, byte sinks, bounded capture, process-group cancellation and opt-in Linux/Darwin owner-loss cleanup; contract operations executed in one-call child processes |
|
|
69
69
|
| `stitchkit/process/owner-loss` | bundled server entry (Bun, POSIX) | evolving | narrow bootstrap that keeps the process runner lazy while carrying its owner-loss guard in a single-file bundle |
|
|
70
|
-
| `stitchkit/files/packaging` | build time (Bun or Node) | evolving | installed native asset graph, integrity, Bun packaging plugin and the artifact loader check; no runtime install policy |
|
|
70
|
+
| `stitchkit/files/packaging` | build time (Bun or Node) | evolving | installed native asset graph, integrity, Bun packaging plugin and the artifact loader check; a client bundled into one self-contained, hashed and verified file; no runtime install policy |
|
|
71
71
|
| `stitchkit/files` | server (Bun or Node) | stable | peer-free managed local-file boundary |
|
|
72
72
|
| `stitchkit/telegram` | server (Bun or Node) | evolving | peer-free Telegram platform primitives — Mini App `initData` verification, send-failure classification, one outbound budget per bot token with weighted albums and per-chat 429 pauses, resumable broadcasts, the operator channel with dedupe, a Bot API transport that separates a request that never left from an unknown outcome, local Bot API files, webhook ownership and durable webhook update intake |
|
|
73
73
|
| `stitchkit/telegram/grammy` | server (Bun or Node) | evolving | the token's outbound budget as one grammY client transformer — a 429 that comes back as a response pauses its chat, and a cancelled wait is a request that never left |
|
|
@@ -84,13 +84,13 @@ own, recorded as an ADR.
|
|
|
84
84
|
| `stitchkit/xlsx` | browser **and** server | evolving | a workbook from rows and typed columns, with no dependency; text never runs as a formula |
|
|
85
85
|
| `stitchkit/oauth` | browser **and** server | evolving | provider-neutral Authorization Code + PKCE request and one-shot callback transaction mechanics |
|
|
86
86
|
| `stitchkit/google` | server (Bun or Node) | evolving | optional Google code exchange and verified OIDC identity adapter |
|
|
87
|
-
| `stitchkit/observability` | server | stable<br>_redefined in 2 of the
|
|
87
|
+
| `stitchkit/observability` | server | stable<br>_redefined in 2 of the 68 minors since 0.56.2, most recently 0.92.0_ | request/tool event projections — `createObservability`, trace context, sanitisation |
|
|
88
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 |
|
|
89
89
|
| `stitchkit/declaration` | browser + build and deployment tooling (Bun or Node) | evolving | `ProjectDeclarationSchema` — the one machine-readable statement a repository makes about itself |
|
|
90
90
|
| `stitchkit/react` | browser + server rendering | stable | `createCursorQuery`, `createCacheBridge`, QueryClient and `ApiError` retry policy |
|
|
91
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) |
|
|
92
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 |
|
|
93
|
-
| `stitchkit/agent-runtime` | server | evolving<br>_redefined in 26 of the
|
|
93
|
+
| `stitchkit/agent-runtime` | server | evolving<br>_redefined in 26 of the 68 minors since 0.56.2, most recently 0.121.0_ | optional durable conversation/run loop, history, models, prompts, fencing and events |
|
|
94
94
|
| `stitchkit/agent-runtime/sandbox` | server | evolving | optional Linux Bubblewrap sessions, durable workspaces and a host HTTP credential gateway; see [sandbox guide](sandbox.md) |
|
|
95
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 |
|
|
96
96
|
| `stitchkit/agent-runtime/harness` | server | evolving | resource-aware process-local facade over the canonical Agent runtime; supervision stays outside |
|
|
@@ -102,7 +102,7 @@ own, recorded as an ADR.
|
|
|
102
102
|
| `stitchkit/agent-runtime/sqlite/bun` | server (Bun) | evolving | durable built-in SQLite store for the agent runtime |
|
|
103
103
|
| `stitchkit/agent-runtime/sqlite/node` | server (Node ≥ 22.5) | evolving | durable built-in SQLite store for the agent runtime |
|
|
104
104
|
| `stitchkit-tui` | terminal (Bun) | evolving | optional official OpenTUI host over a caller-composed headless runtime |
|
|
105
|
-
| `stitchkit/application` | browser + server | evolving<br>_redefined in 14 of the
|
|
105
|
+
| `stitchkit/application` | browser + server | evolving<br>_redefined in 14 of the 68 minors since 0.56.2, most recently 0.110.0_ | managed resource graph, readiness, admission, schedules, subtree restart and bounded shutdown |
|
|
106
106
|
| `stitchkit/application/grammy` | server | evolving | a grammY bot as application resources — configuration, then polling with updates admitted by batch — and the webhook adapter |
|
|
107
107
|
| `stitchkit/application/opentelemetry` | server | evolving | maps application snapshots onto an injected OpenTelemetry `Meter` |
|
|
108
108
|
| `stitchkit/application/schemas` | browser + server | evolving | the application's snapshot, health and shutdown schemas alone, without the kernel |
|
|
@@ -970,7 +970,10 @@ Six intrinsic HTTP-only forms intersect an **implicit factory default** with HTT
|
|
|
970
970
|
responses and `HEAD`. Under `['HTTP', 'CLI']` they receive `['HTTP']`; under
|
|
971
971
|
`['CLI']` or `[]` they receive `[]`, not an implicitly reopened HTTP route.
|
|
972
972
|
An **explicit** non-HTTP exposure on any of them is a definition error, not a
|
|
973
|
-
silent downgrade
|
|
973
|
+
silent downgrade — with one exception: a streaming response may list `'CLI'`
|
|
974
|
+
(`['HTTP', 'CLI']`, or `['CLI']` for a feed only a terminal reads) and then runs
|
|
975
|
+
as a [command printing a line per item](./cli.md#a-contract-stream-as-a-command).
|
|
976
|
+
It is still never an MCP or agent tool. `safelistedBody` only changes HTTP parsing and remains
|
|
974
977
|
multi-transport. A factory policy applies only to contracts authored through
|
|
975
978
|
that factory, not to contracts another helper builds internally.
|
|
976
979
|
|
|
@@ -3491,6 +3494,75 @@ seventh constructor argument remains `ErrorOptions`, including `cause`; the opti
|
|
|
3491
3494
|
retryability argument is eighth. Closed error/frame schemas must accept the new optional
|
|
3492
3495
|
field before their producer declares it.
|
|
3493
3496
|
|
|
3497
|
+
## Which contract does the server speak
|
|
3498
|
+
|
|
3499
|
+
A client is built from one version of a contract and a server from another
|
|
3500
|
+
whenever the two are deployed separately. Three facts then look alike unless
|
|
3501
|
+
they are told apart: the server has no such operation, the operation ran and
|
|
3502
|
+
found nothing, and the field is simply absent.
|
|
3503
|
+
|
|
3504
|
+
**An operation the server cannot route is `UNKNOWN_OPERATION`.** A server
|
|
3505
|
+
answers an unmatched route with `404 UNKNOWN_OPERATION`
|
|
3506
|
+
(`details: { method, path }`), never `NOT_FOUND` — that code is a handler
|
|
3507
|
+
saying its object does not exist. A contract client adds the operation it was
|
|
3508
|
+
calling, and reads the router's `405` for its own call the same way, because it
|
|
3509
|
+
always calls with the method its contract declares. The router's `405` carries
|
|
3510
|
+
`details: { method, path, allow }`; a `METHOD_NOT_ALLOWED` a handler throws
|
|
3511
|
+
itself has no `allow` list and stays `METHOD_NOT_ALLOWED`.
|
|
3512
|
+
|
|
3513
|
+
```ts
|
|
3514
|
+
try {
|
|
3515
|
+
await api.search({ query })
|
|
3516
|
+
} catch (error) {
|
|
3517
|
+
if (error instanceof ApiError && error.code === 'UNKNOWN_OPERATION') {
|
|
3518
|
+
// error.details.operation === 'records.search' — this server predates it
|
|
3519
|
+
}
|
|
3520
|
+
}
|
|
3521
|
+
```
|
|
3522
|
+
|
|
3523
|
+
**The fingerprint says so before the call.** `contractFingerprint(contracts)`
|
|
3524
|
+
(`stitchkit/contract`) is a synchronous digest of the operations HTTP carries —
|
|
3525
|
+
method, route and the JSON Schemas of params, input, output and stream item —
|
|
3526
|
+
with one digest per operation under `operations['prefix.key']`. It is the same
|
|
3527
|
+
value in every process and runtime. A server answers its own through an
|
|
3528
|
+
ordinary contract it mounts by choice, and a client reads both sides in one
|
|
3529
|
+
call, over HTTP or a Unix socket alike:
|
|
3530
|
+
|
|
3531
|
+
```ts
|
|
3532
|
+
// server: the list to mount, with the identity of exactly that list
|
|
3533
|
+
import { withContractIdentity } from 'stitchkit/server'
|
|
3534
|
+
createServer({ services: withContractIdentity(services) })
|
|
3535
|
+
|
|
3536
|
+
// client
|
|
3537
|
+
import { readContractIdentity } from 'stitchkit'
|
|
3538
|
+
const { client, server } = await readContractIdentity(contracts, config)
|
|
3539
|
+
if (server === undefined) {
|
|
3540
|
+
// the server does not answer its identity: it predates it or did not mount it
|
|
3541
|
+
} else if (server.operations['records.search'] === undefined) {
|
|
3542
|
+
// this server has no `records.search`
|
|
3543
|
+
} else if (server.operations['records.get'] !== client.operations['records.get']) {
|
|
3544
|
+
// `records.get` has another schema there: a missing field may be "not sent yet"
|
|
3545
|
+
}
|
|
3546
|
+
```
|
|
3547
|
+
|
|
3548
|
+
Both fingerprints arrive as a value; a difference never throws. Stitchkit
|
|
3549
|
+
computes the two identities and compares nothing — what a mismatch means is the
|
|
3550
|
+
application's decision. The client's fingerprint is computed locally and is not
|
|
3551
|
+
sent to the server. The identity operation is public and lists operation names,
|
|
3552
|
+
which is why it is mounted explicitly. Routing alone cannot always tell the
|
|
3553
|
+
cases apart — a new `GET /records/search` reaches an older server's
|
|
3554
|
+
`GET /records/:name` and comes back `NOT_FOUND` — so a client that must know
|
|
3555
|
+
reads the fingerprint rather than inferring from a refusal.
|
|
3556
|
+
|
|
3557
|
+
What the fingerprint covers is what constrains a value. An endpoint's `desc`, a
|
|
3558
|
+
field's `.describe()` or `.meta()` annotations, and the order of an object's
|
|
3559
|
+
fields do not move it; a type, a constraint, a required field, a method or a
|
|
3560
|
+
route does. It does not cover a stream's wire framing or format, `multipart`,
|
|
3561
|
+
`rawResponse` and `contentType`, and it cannot tell apart types JSON Schema has
|
|
3562
|
+
no word for — a `Date` and a `bigint` both read as "anything". Two different schemas registered
|
|
3563
|
+
under one `.meta({ id })` are refused by Zod's own conversion, here as in OpenAPI. The digest
|
|
3564
|
+
identifies, it does not authenticate: it is not a cryptographic hash.
|
|
3565
|
+
|
|
3494
3566
|
## Auth events
|
|
3495
3567
|
|
|
3496
3568
|
The HTTP client emits events your app can react to globally:
|
|
@@ -4000,7 +4072,7 @@ presentation envelope. `invokeOrThrow` returns validated data and throws the
|
|
|
4000
4072
|
runner's normalized `AppError` on failure, preserving an application error's
|
|
4001
4073
|
code, status, message, details and hint without reconstructing it from the
|
|
4002
4074
|
model-safe envelope. Unexpected throws remain scrubbed. An unknown name throws
|
|
4003
|
-
`AppError('
|
|
4075
|
+
`AppError('UNKNOWN_OPERATION')` before the runner because there is no operation identity
|
|
4004
4076
|
against which hooks could run. Duplicate and provider-invalid names fail when
|
|
4005
4077
|
the invoker is created.
|
|
4006
4078
|
|
|
@@ -9240,6 +9312,65 @@ refused there and nothing is stopped after its leader exits. A command whose gro
|
|
|
9240
9312
|
launcher supplied by a host) has no group to stop, and `descendants` has nothing to act on there.
|
|
9241
9313
|
|
|
9242
9314
|
|
|
9315
|
+
## A contract operation in a process of its own
|
|
9316
|
+
|
|
9317
|
+
A handler that reads gigabytes holds the server's event loop while it reads, and every cheap
|
|
9318
|
+
operation on that server waits behind it. `createOperationProcesses` runs such an operation in a
|
|
9319
|
+
child process, one per call, on top of `runNativeCommand` and `streamNativeCommand`. What it
|
|
9320
|
+
returns are ordinary contract handlers, so the operation is routed, gated and validated like any
|
|
9321
|
+
other:
|
|
9322
|
+
|
|
9323
|
+
```ts
|
|
9324
|
+
import { createOperationProcesses, runOperationProcess } from 'stitchkit/process'
|
|
9325
|
+
|
|
9326
|
+
// server
|
|
9327
|
+
const processes = createOperationProcesses({
|
|
9328
|
+
command: { executable: process.execPath, args: [cliPath, '__operation'], envPolicy: 'ambient' },
|
|
9329
|
+
maxConcurrent: 2, // a third call is refused, not queued
|
|
9330
|
+
maxResponseBytes: 8 * 1024 * 1024,
|
|
9331
|
+
timeoutMs: 60_000,
|
|
9332
|
+
})
|
|
9333
|
+
const service = implement(searchContract, {
|
|
9334
|
+
search: processes.unary(searchContract, 'search'),
|
|
9335
|
+
follow: processes.stream(searchContract, 'follow'),
|
|
9336
|
+
count: ({ input }) => countCheaply(input), // stays in process
|
|
9337
|
+
})
|
|
9338
|
+
|
|
9339
|
+
// the child: the same contract, the real handlers
|
|
9340
|
+
if (process.argv[2] === '__operation') {
|
|
9341
|
+
process.exitCode = await runOperationProcess([implement(searchContract, realHandlers)])
|
|
9342
|
+
}
|
|
9343
|
+
```
|
|
9344
|
+
|
|
9345
|
+
- **The contract's schemas hold at the server.** The server validates the input before it starts
|
|
9346
|
+
a process — a bad call never costs one — and validates the answer against the output schema
|
|
9347
|
+
after; a stream item is validated against `stream.item` on its way out. The child receives the
|
|
9348
|
+
input the server already parsed and does not parse it again.
|
|
9349
|
+
- **The call and the answer cross as JSON.** A schema whose parsed value JSON cannot carry — a
|
|
9350
|
+
`Date`, a `Map`, a `bigint` — cannot be an input or an output of such an operation: the value
|
|
9351
|
+
arrives as its JSON form and the output check refuses it. A call may weigh up to 64 MiB; bytes
|
|
9352
|
+
an operation works on belong in a file it is told the path of.
|
|
9353
|
+
- **The call's end is the process's end.** The caller's abort (`ctx.signal`) stops the process
|
|
9354
|
+
group under the command's `stop` policy. A unary call also ends at `timeoutMs` with
|
|
9355
|
+
`REQUEST_TIMEOUT`. A stream's deadline is the contract's own `stream.lifetimeMs`: when it
|
|
9356
|
+
passes, the stream ends with `STREAM_LIFETIME_EXCEEDED` and the process is stopped; without it
|
|
9357
|
+
a stream lives until it ends or its reader leaves.
|
|
9358
|
+
- **The answer is bounded.** `maxResponseBytes` bounds the whole answer of a unary operation and
|
|
9359
|
+
each item of a stream; more is `OPERATION_RESPONSE_TOO_LARGE`. Only stdout counts: what the
|
|
9360
|
+
child writes to stderr does not spend the answer's budget.
|
|
9361
|
+
- **The count is bounded.** A call beyond `maxConcurrent` is refused with `503 OPERATION_BUSY`
|
|
9362
|
+
(`retryable: true`) at once. There is no queue: a queue without a bound is the same memory the
|
|
9363
|
+
process boundary was meant to protect. A call that cannot start gives its slot back.
|
|
9364
|
+
- **A stream delivers as it produces.** `processes.stream` yields each item when the child
|
|
9365
|
+
writes it.
|
|
9366
|
+
|
|
9367
|
+
A refusal the child's handler throws arrives as that same `AppError` — code, status, details,
|
|
9368
|
+
hint and retryability — for a unary call and in the middle of a stream alike. A process that
|
|
9369
|
+
dies, writes something that is not an answer, or cannot start is `OPERATION_PROCESS_FAILED`; the
|
|
9370
|
+
last 16 KiB of its stderr stay in the error's cause for the log and never reach the caller.
|
|
9371
|
+
`processes.active` reports the calls running now. The child reads one call from stdin and writes
|
|
9372
|
+
its answer to stdout, so its handlers must not print there.
|
|
9373
|
+
|
|
9243
9374
|
## Replacing hand-written wrappers
|
|
9244
9375
|
|
|
9245
9376
|
An atomic-file wrapper becomes one `writeFileAtomic` call with the chosen options. A bounded
|
|
@@ -9602,6 +9733,45 @@ A missing or corrupt companion at runtime yields the native `unavailable` result
|
|
|
9602
9733
|
stage and code from the observer; it never certifies a dead owner. Preserve the raw cause internally.
|
|
9603
9734
|
The plugin does not parse consumer JS or establish a second runtime native implementation.
|
|
9604
9735
|
|
|
9736
|
+
## A client as one self-contained file
|
|
9737
|
+
|
|
9738
|
+
A package that owns a server usually ships a typed client for it, and the client's consumers
|
|
9739
|
+
must not inherit the server's dependencies. `buildClientArtifact` (`stitchkit/files/packaging`)
|
|
9740
|
+
bundles one entry module into a single file, publishes its SHA-256 beside it and can prove an
|
|
9741
|
+
npm archive of it:
|
|
9742
|
+
|
|
9743
|
+
```ts
|
|
9744
|
+
import { buildClientArtifact } from 'stitchkit/files/packaging'
|
|
9745
|
+
|
|
9746
|
+
const artifact = await buildClientArtifact({
|
|
9747
|
+
entry: 'src/api-client.ts',
|
|
9748
|
+
outFile: 'dist/api-client.js', // + dist/api-client.js.sha256
|
|
9749
|
+
forbiddenModules: ['chalk', '@acme/terminal-ui', 'src/terminal/', /providers\//],
|
|
9750
|
+
package: {
|
|
9751
|
+
name: '@acme/api-client', version: '1.4.0', outDir: 'dist',
|
|
9752
|
+
exports: ['createApiClient'],
|
|
9753
|
+
},
|
|
9754
|
+
})
|
|
9755
|
+
// artifact.file, artifact.sha256, artifact.bytes, artifact.modules, artifact.package.tarball
|
|
9756
|
+
```
|
|
9757
|
+
|
|
9758
|
+
- **`forbiddenModules`** refuses a bundle whose module graph reaches a listed module with a
|
|
9759
|
+
`ForbiddenModuleError` naming the module and what imports it. A bare or scoped name
|
|
9760
|
+
(`'chalk'`, `'@scope/pkg'`) is a package: it is matched by the specifier that imports it, so a
|
|
9761
|
+
linked or workspace package is caught wherever it really lives, and by its installed path. A
|
|
9762
|
+
string with a slash matches a fragment of the project's own source path; a `RegExp` is tested
|
|
9763
|
+
against each module's absolute path. Nothing is left at `outFile` when the build is refused.
|
|
9764
|
+
- **`package`** packs the file as an npm archive, installs it into an empty directory, imports
|
|
9765
|
+
it by name and checks every name in `exports`. A client that still reaches for the building
|
|
9766
|
+
package's `node_modules` fails there, before it reaches a consumer; a failed proof removes the
|
|
9767
|
+
file and its hash again.
|
|
9768
|
+
- **`runtime`** is the platform the file is built for — `node` by default, which Bun also runs,
|
|
9769
|
+
or `bun` or `browser`; a module the platform does not have fails the build.
|
|
9770
|
+
|
|
9771
|
+
The build, the pack and the verification each run Bun as a bounded command: `timeoutMs`
|
|
9772
|
+
(default 120 s) is the deadline of each step, `signal` cancels, and `executable` names the Bun
|
|
9773
|
+
binary when it is not `bun` on `PATH`.
|
|
9774
|
+
|
|
9605
9775
|
## Qualification boundaries
|
|
9606
9776
|
|
|
9607
9777
|
Native command cleanup owns the child before launch subscriptions are installed. Setup
|
|
@@ -10382,6 +10552,49 @@ result on stdout and reports `CLI_PRESENTATION_FAILED` separately, as described
|
|
|
10382
10552
|
under [Aggregate views](#aggregate-views). Void native commands cannot declare
|
|
10383
10553
|
either callback.
|
|
10384
10554
|
|
|
10555
|
+
### The same policy for a managed command
|
|
10556
|
+
|
|
10557
|
+
A command that comes from a contract or a runtime tool declares the same two
|
|
10558
|
+
callbacks where the CLI is mounted — `CliConfig.results`, keyed by command name.
|
|
10559
|
+
A client and a server print nothing, so the policy belongs to the CLI, not to
|
|
10560
|
+
the contract. `defineCliResult` types both callbacks against the operation's
|
|
10561
|
+
output schema:
|
|
10562
|
+
|
|
10563
|
+
```ts
|
|
10564
|
+
import { createCli, defineCliResult } from 'stitchkit/cli'
|
|
10565
|
+
|
|
10566
|
+
await createCli({
|
|
10567
|
+
name: 'myapp',
|
|
10568
|
+
version: '1.0.0',
|
|
10569
|
+
services: [recordService],
|
|
10570
|
+
results: {
|
|
10571
|
+
lookup: defineCliResult(LookupOutput, {
|
|
10572
|
+
present: ({ result, options }) =>
|
|
10573
|
+
options.json ? `${JSON.stringify(result)}\n` : `${result.location ?? 'not found'}\n`,
|
|
10574
|
+
exitCode: (result) => (result.found ? 0 : 1),
|
|
10575
|
+
}),
|
|
10576
|
+
},
|
|
10577
|
+
})
|
|
10578
|
+
```
|
|
10579
|
+
|
|
10580
|
+
The guarantees are the native ones, because one function applies both: the
|
|
10581
|
+
callbacks run only after the operation's output is validated, `present` is
|
|
10582
|
+
called with and without `--json` — honouring it is the presenter's — and
|
|
10583
|
+
returns the exact stdout bytes, a throwing `present` reports
|
|
10584
|
+
`CLI_PRESENTATION_FAILED`, and a throwing or out-of-range `exitCode` becomes
|
|
10585
|
+
`INTERNAL_SERVER_ERROR`.
|
|
10586
|
+
|
|
10587
|
+
The schema handed to `defineCliResult` is the command's own output schema — the
|
|
10588
|
+
object the contract declares, or the item schema of a stream. A policy typed
|
|
10589
|
+
against any other schema is a startup error, as is a key naming no managed
|
|
10590
|
+
command or naming a native one.
|
|
10591
|
+
|
|
10592
|
+
The policy speaks for the command's own result only. An
|
|
10593
|
+
[aggregate view](#aggregate-views) (`--table`, `--count-by`, …) answers a
|
|
10594
|
+
different question, and after `--wait` the result is the polled command's, so
|
|
10595
|
+
both print without the policy. `createCliInvoker` applies `exitCode` — the
|
|
10596
|
+
in-process code equals the printed one — and never calls `present`.
|
|
10597
|
+
|
|
10385
10598
|
`--version`, a selected native command and its command help run before
|
|
10386
10599
|
`resolveAuth`, services, context or runtime-tool factories. Top-level help is
|
|
10387
10600
|
also credential-free when managed surfaces are static. A dynamic factory must
|
|
@@ -10580,6 +10793,7 @@ same Zod schema an HTTP or MCP call does.
|
|
|
10580
10793
|
| `--output-dir <dir>` | Download result media into a directory |
|
|
10581
10794
|
| `--quiet` | Suppress non-essential stderr output |
|
|
10582
10795
|
| `--dry-run` | Print the resolved call without executing |
|
|
10796
|
+
| `--schema` | One JSON line: the JSON Schema of a command's input and output |
|
|
10583
10797
|
| `--help`, `-h` | Usage — top-level or per-command flag table |
|
|
10584
10798
|
| `--help <text>` | List only the commands matching a substring |
|
|
10585
10799
|
| `--count-by <field>` | Count records per distinct value — see [Aggregate views](#aggregate-views) |
|
|
@@ -10595,9 +10809,32 @@ newline-terminated JSON record on its respective stream; progress and CLI usage
|
|
|
10595
10809
|
diagnostics remain ordinary stderr text. This keeps stdout pipeable and
|
|
10596
10810
|
`2>/dev/null` clean. The process exit code carries the error class (`0` ok,
|
|
10597
10811
|
`VALIDATION_ERROR → 1`, `UNAUTHORIZED → 2`, `FORBIDDEN → 3`, `NOT_FOUND → 4`,
|
|
10812
|
+
`UNKNOWN_OPERATION → 4`, `TIMEOUT → 7`, `STREAM_LIFETIME_EXCEEDED → 7`,
|
|
10598
10813
|
`CLI_PRESENTATION_FAILED → 8`,
|
|
10599
10814
|
…) — override per app with `exitCodes`.
|
|
10600
10815
|
|
|
10816
|
+
### The schema of a command — `--schema`
|
|
10817
|
+
|
|
10818
|
+
`myapp <command> --schema` prints one line of JSON and exits `0` without
|
|
10819
|
+
validating arguments or calling anything:
|
|
10820
|
+
|
|
10821
|
+
```json
|
|
10822
|
+
{"command":"lookup","input":{"type":"object","properties":{"name":{"type":"string"}},"required":["name"]},"output":{"type":"object","properties":{"found":{"type":"boolean"}},"required":["found"]}}
|
|
10823
|
+
```
|
|
10824
|
+
|
|
10825
|
+
`input` is the schema the command's arguments are read by — the one `--help`
|
|
10826
|
+
renders — and `output` is the JSON Schema of the result, both derived from the
|
|
10827
|
+
operation's Zod schemas and both in JSON Schema draft-07, so a script or another
|
|
10828
|
+
program can hold a command to its schema without a copy. A command without a
|
|
10829
|
+
declared output carries no `output`; a
|
|
10830
|
+
[stream command](#a-contract-stream-as-a-command) carries `item` instead.
|
|
10831
|
+
Managed and native commands answer alike. Like `--help`, the answer needs no
|
|
10832
|
+
identity when the managed surface is static; a surface built by a
|
|
10833
|
+
`services` or `runtimeTools` function resolves identity first to learn its
|
|
10834
|
+
commands. `schema` is a reserved option name: a command field called `schema`
|
|
10835
|
+
is a startup error for a contract command and makes a runtime-catalogue command
|
|
10836
|
+
unavailable, like every other reserved name.
|
|
10837
|
+
|
|
10601
10838
|
### A narrower question than "all of them"
|
|
10602
10839
|
|
|
10603
10840
|
On a discovered surface `--help` is the only way to learn what exists, and that
|
|
@@ -10682,7 +10919,7 @@ reason in `details.message` on stderr; the default exit code is 1. `--json`
|
|
|
10682
10919
|
compacts the error as usual; `exitCodes` and `errorHint` apply. The refusal precedes
|
|
10683
10920
|
command argument parsing, stdin, context, hooks and the handler; `--dry-run` cannot
|
|
10684
10921
|
turn it into success. A known unavailable `--wait` target returns the same refusal;
|
|
10685
|
-
an unknown target
|
|
10922
|
+
an unknown target answers `UNKNOWN_OPERATION`.
|
|
10686
10923
|
|
|
10687
10924
|
This isolates only reserved/global **field** conflicts in `runtimeTools`.
|
|
10688
10925
|
Native commands and contract-defined `services` still fail at startup for those
|
|
@@ -11224,6 +11461,59 @@ your server. A channel is two documents at two addresses — which of them a bui
|
|
|
11224
11461
|
consults is your decision, and `assertCliPublishable` is told about all of them
|
|
11225
11462
|
rather than taught the shape of any.
|
|
11226
11463
|
|
|
11464
|
+
## A contract stream as a command
|
|
11465
|
+
|
|
11466
|
+
A streaming endpoint whose `expose` lists `'CLI'` is a command that prints one
|
|
11467
|
+
line per item — `['HTTP', 'CLI']` for a stream both carry, `['CLI']` for a feed
|
|
11468
|
+
only a terminal reads:
|
|
11469
|
+
|
|
11470
|
+
```ts
|
|
11471
|
+
watch: {
|
|
11472
|
+
method: 'POST', path: '/watch', desc: 'Follow record events',
|
|
11473
|
+
expose: ['HTTP', 'CLI'],
|
|
11474
|
+
input: z.object({ record: z.string() }),
|
|
11475
|
+
stream: { item: RecordEvent },
|
|
11476
|
+
}
|
|
11477
|
+
```
|
|
11478
|
+
|
|
11479
|
+
```sh
|
|
11480
|
+
myapp watch_record --record main | head -n 5
|
|
11481
|
+
```
|
|
11482
|
+
|
|
11483
|
+
The command is named from the contract prefix and the endpoint key
|
|
11484
|
+
(`records.watch` → `watch_record`); a stream takes no `tool` group, so it has no
|
|
11485
|
+
`tool.name`. `optionAliases` and `positionals` apply as to any command.
|
|
11486
|
+
|
|
11487
|
+
The call opens through the same runner as every command — validation, the
|
|
11488
|
+
`lifecycle.beforeHandle` gate, hooks — and the handler's async iterable is read
|
|
11489
|
+
by the loop the HTTP response uses: each item is validated against
|
|
11490
|
+
`stream.item` (`STREAM_ITEM_INVALID` otherwise), a declared `terminal` item
|
|
11491
|
+
completes the command, and a stream that ends before it fails with
|
|
11492
|
+
`STREAM_TERMINAL_MISSING`. A stream proxied by `implementRemote` is a stream
|
|
11493
|
+
here too. Without a policy an item is one compact JSON line;
|
|
11494
|
+
`results[command].present` formats each item instead. A presenter that throws
|
|
11495
|
+
ends the feed with `CLI_PRESENTATION_FAILED` after the lines already printed.
|
|
11496
|
+
`exitCode` is refused for a stream at startup — a feed has no single result to
|
|
11497
|
+
classify.
|
|
11498
|
+
|
|
11499
|
+
How a feed ends:
|
|
11500
|
+
|
|
11501
|
+
| End | stdout / stderr | Exit |
|
|
11502
|
+
| --- | --- | --- |
|
|
11503
|
+
| the stream completes | every item | `0` |
|
|
11504
|
+
| stdout is closed (`head`, a pager that quit) | the items read so far | `0` |
|
|
11505
|
+
| `CliConfig.signal` aborts | nothing more, no error | `130` (`REQUEST_ABORTED`) |
|
|
11506
|
+
| `stream.lifetimeMs` passes | `STREAM_LIFETIME_EXCEEDED` on stderr | `7` |
|
|
11507
|
+
| the source or an item fails | the error on stderr | by its code |
|
|
11508
|
+
|
|
11509
|
+
In every case the handler's `ctx.signal` aborts and the source is asked to
|
|
11510
|
+
close. The command does not wait for a source that ignores its signal: the
|
|
11511
|
+
deadline and the cancellation are the command's, not the source's.
|
|
11512
|
+
|
|
11513
|
+
`--wait`, `--output-dir` and aggregate views do not apply to a stream and exit
|
|
11514
|
+
`2`; a stream command is not reachable through `createCliInvoker`, whose answer
|
|
11515
|
+
is one result per line.
|
|
11516
|
+
|
|
11227
11517
|
## One operation per line — `createCliInvoker`
|
|
11228
11518
|
|
|
11229
11519
|
The most common way an agent drives a CLI is not one command: it is a stream —
|
|
@@ -11276,7 +11566,7 @@ await createCliInvoker({ name: 'myapp', services, commands: [describe] })
|
|
|
11276
11566
|
```
|
|
11277
11567
|
|
|
11278
11568
|
A command that declares `output?: never` prints itself and returns nothing, so
|
|
11279
|
-
there is no result to hand back; it answers `
|
|
11569
|
+
there is no result to hand back; it answers `UNKNOWN_OPERATION` and stays on the command
|
|
11280
11570
|
line, where stdout exists. Your stream command is one of these, which is why a
|
|
11281
11571
|
stream still cannot invoke itself.
|
|
11282
11572
|
|
|
@@ -13559,7 +13849,8 @@ error round-trips without adding correlation data to the response body.
|
|
|
13559
13849
|
|
|
13560
13850
|
A `code` is a free string — your app codes (`BOT_NOT_FOUND`, …) are yours and the
|
|
13561
13851
|
core never models them (ADR 0002). But stitchkit itself emits a set of its own:
|
|
13562
|
-
`BAD_REQUEST`, `UNAUTHORIZED`, `FORBIDDEN`, `NOT_FOUND`, `
|
|
13852
|
+
`BAD_REQUEST`, `UNAUTHORIZED`, `FORBIDDEN`, `NOT_FOUND`, `UNKNOWN_OPERATION`,
|
|
13853
|
+
`METHOD_NOT_ALLOWED`,
|
|
13563
13854
|
`CONFLICT`, `RATE_LIMITED`, `REQUEST_ABORTED`, `REQUEST_TIMEOUT`,
|
|
13564
13855
|
`VALIDATION_ERROR`, `FILE_INVALID_PATH`,
|
|
13565
13856
|
`FILE_OUTSIDE_ROOT`, `FILE_NOT_FOUND`, `FILE_NOT_REGULAR`,
|
|
@@ -13569,7 +13860,8 @@ core never models them (ADR 0002). But stitchkit itself emits a set of its own:
|
|
|
13569
13860
|
`WAIT_FAILED`, `CLI_COMMAND_UNAVAILABLE`, `CLI_PRESENTATION_FAILED`, `DOWNLOAD_NOT_FOUND`, `VIEW_HTTP_ERROR`,
|
|
13570
13861
|
`CONNECTION_TIMEOUT`, `CONNECTION_RESPONSE_TOO_LARGE`,
|
|
13571
13862
|
`CONNECTION_REQUEST_FAILED`, `UPSTREAM_TOOL_ERROR`,
|
|
13572
|
-
`OPERATION_NOT_SUCCEEDED`, `
|
|
13863
|
+
`OPERATION_NOT_SUCCEEDED`, `OPERATION_BUSY`, `OPERATION_RESPONSE_TOO_LARGE`,
|
|
13864
|
+
`OPERATION_PROCESS_FAILED`, `STREAM_ITEM_INVALID`, `STREAM_FRAME_TOO_LARGE`,
|
|
13573
13865
|
`STREAM_TERMINAL_MISSING`, `STREAM_LIFETIME_EXCEEDED`,
|
|
13574
13866
|
`GRAMMY_WEBHOOK_NOT_ACCEPTING`, `SANDBOX_UNAVAILABLE`, `SANDBOX_INSUFFICIENT`,
|
|
13575
13867
|
`SPILL_REFERENCE_UNKNOWN`, `INTERNAL_SERVER_ERROR` — a set that grows in
|
|
@@ -13597,7 +13889,8 @@ envelope is a published contract, at the cost of an edit on those releases:
|
|
|
13597
13889
|
// a map written exhaustively stops compiling on every addition. Codes you leave
|
|
13598
13890
|
// out fall through to `unmappedCode`.
|
|
13599
13891
|
const STITCH_TO_APP: Partial<Record<StitchErrorCode, AppCode>> = {
|
|
13600
|
-
NOT_FOUND: 'NOT_FOUND',
|
|
13892
|
+
NOT_FOUND: 'NOT_FOUND', UNKNOWN_OPERATION: 'NOT_FOUND',
|
|
13893
|
+
METHOD_NOT_ALLOWED: 'METHOD_NOT_ALLOWED',
|
|
13601
13894
|
BAD_REQUEST: 'VALIDATION_ERROR', VALIDATION_ERROR: 'VALIDATION_ERROR',
|
|
13602
13895
|
UNAUTHORIZED: 'UNAUTHORIZED', FORBIDDEN: 'FORBIDDEN', CONFLICT: 'CONFLICT',
|
|
13603
13896
|
RATE_LIMITED: 'RATE_LIMITED', INTERNAL_SERVER_ERROR: 'INTERNAL_SERVER_ERROR',
|
|
@@ -13723,7 +14016,8 @@ const onError = createErrorHook({
|
|
|
13723
14016
|
codeMap: {
|
|
13724
14017
|
BAD_REQUEST: 'bad_request', VALIDATION_ERROR: 'bad_request',
|
|
13725
14018
|
UNAUTHORIZED: 'unauthenticated', FORBIDDEN: 'forbidden',
|
|
13726
|
-
NOT_FOUND: 'not_found',
|
|
14019
|
+
NOT_FOUND: 'not_found', UNKNOWN_OPERATION: 'not_found',
|
|
14020
|
+
METHOD_NOT_ALLOWED: 'not_found',
|
|
13727
14021
|
CONFLICT: 'conflict', RATE_LIMITED: 'rate_limited',
|
|
13728
14022
|
INTERNAL_SERVER_ERROR: 'internal',
|
|
13729
14023
|
},
|
|
@@ -17322,6 +17616,53 @@ charts, no merged cells and no streaming: an application that needs them takes a
|
|
|
17322
17616
|
|
|
17323
17617
|
# Upgrading stitchkit
|
|
17324
17618
|
|
|
17619
|
+
## Released migration: 0.123.0
|
|
17620
|
+
|
|
17621
|
+
### unknown-operation
|
|
17622
|
+
|
|
17623
|
+
**Who must act:** HTTP callers and `onError` code maps that read `NOT_FOUND` to mean "no such
|
|
17624
|
+
route", callers of `createCliInvoker`, `--wait` and `createToolInvoker` that read `NOT_FOUND` for
|
|
17625
|
+
an unknown command or tool name, and contract-client callers that branch on the router's
|
|
17626
|
+
`METHOD_NOT_ALLOWED`.
|
|
17627
|
+
|
|
17628
|
+
A call no surface can route is answered `UNKNOWN_OPERATION`; `NOT_FOUND` is left to handlers.
|
|
17629
|
+
|
|
17630
|
+
- The server answers an unmatched route `404 UNKNOWN_OPERATION` with `details: { method, path }`.
|
|
17631
|
+
- A contract client reports both routing refusals of its own call — that `404` and the router's
|
|
17632
|
+
`405` — as `UNKNOWN_OPERATION` with `details.operation`. A `405` thrown by a handler is
|
|
17633
|
+
unchanged.
|
|
17634
|
+
- `createCliInvoker().invoke`, an unknown `--wait` target and `createToolInvoker` answer
|
|
17635
|
+
`UNKNOWN_OPERATION` for a name they do not have. The CLI exit code for it is `4`, as it was for
|
|
17636
|
+
`NOT_FOUND`.
|
|
17637
|
+
|
|
17638
|
+
```ts
|
|
17639
|
+
// before
|
|
17640
|
+
if (error.code === 'NOT_FOUND') showMissingPage()
|
|
17641
|
+
|
|
17642
|
+
// after
|
|
17643
|
+
if (error.code === 'UNKNOWN_OPERATION') showMissingPage() // no such route, command or tool
|
|
17644
|
+
if (error.code === 'NOT_FOUND') showMissingRecord() // the handler's own answer
|
|
17645
|
+
```
|
|
17646
|
+
|
|
17647
|
+
A `createErrorHook` `codeMap` that spells `NOT_FOUND` for clients adds
|
|
17648
|
+
`UNKNOWN_OPERATION: 'not_found'` to keep the previous wire code.
|
|
17649
|
+
|
|
17650
|
+
### cli-schema-option
|
|
17651
|
+
|
|
17652
|
+
**Who must act:** applications whose CLI exposes a command with an input field named `schema`.
|
|
17653
|
+
|
|
17654
|
+
`--schema` is a framework option that prints a command's JSON Schema, so a command field of that
|
|
17655
|
+
name can no longer receive a value. A contract command declaring it fails at startup; a
|
|
17656
|
+
runtime-catalogue command becomes unavailable with the reason in help. Rename the field.
|
|
17657
|
+
|
|
17658
|
+
```ts
|
|
17659
|
+
// before
|
|
17660
|
+
input: z.object({ schema: z.string() }) // app export --schema public
|
|
17661
|
+
|
|
17662
|
+
// after
|
|
17663
|
+
input: z.object({ schemaName: z.string() }) // app export --schema-name public
|
|
17664
|
+
```
|
|
17665
|
+
|
|
17325
17666
|
## Released migration: 0.122.0
|
|
17326
17667
|
|
|
17327
17668
|
### read-view-refusal
|
|
@@ -22683,6 +23024,8 @@ The browser-and-server entrypoint. Re-exports everything from
|
|
|
22683
23024
|
| `RegistryScope` | _type_ | contract-scope union inferred from a scoped client registry value |
|
|
22684
23025
|
| `createUrlBuilder` | function | build synchronous browser-native URLs for all HTTP endpoints; body methods accept URL-bound args only — [guide](../guide/client.md#contract-url-builders) |
|
|
22685
23026
|
| `createUrlBuilders` | function | build one exact URL builder per contract in a registry |
|
|
23027
|
+
| `readContractIdentity` | function | read the server's contract fingerprint in one call and return it beside the client's own — [guide](../guide/client.md#which-contract-does-the-server-speak) |
|
|
23028
|
+
| `ContractIdentity` | _type_ | `{ client, server }` fingerprints; `server` is `undefined` when the server answers no identity |
|
|
22686
23029
|
| `UrlBuilderConfig` | _type_ | explicit `{ baseUrl }` source for a URL builder |
|
|
22687
23030
|
| `ClientConfig` | _type_ | config for `createClient`'s bare-fetch mode (2nd arg, no `HttpClient`) |
|
|
22688
23031
|
| `ClientFetch` | _type_ | injectable Fetch-compatible transport used by framework and application-owned adapters |
|
|
@@ -22735,6 +23078,9 @@ The browser-and-server entrypoint. Re-exports everything from
|
|
|
22735
23078
|
| `StreamLineLimitError` / `StreamTruncatedLineError` | class | a line past `maxLineBytes` (`limitBytes`, `lineBytes`; a `RangeError`) and a source ended inside a line under `require-newline` (`lineBytes`; a `SyntaxError`) |
|
|
22736
23079
|
| `resumableIterator` | function | re-open a long-lived stream from the last delivered cursor, with jittered backoff, a caller-owned terminal item and prompt abort — [guide](../guide/client.md#resumable-streams) |
|
|
22737
23080
|
| `createBackoff` | function | exponential backoff with subtractive jitter as a value: `next()` / `reset()` |
|
|
23081
|
+
| `contractFingerprint` | function | synchronous digest of the operations HTTP carries, whole and per `prefix.key` — [guide](../guide/client.md#which-contract-does-the-server-speak) |
|
|
23082
|
+
| `ContractFingerprintSchema` / `ContractFingerprint` | schema / _type_ | `{ fingerprint, operations }` — one digest for the set and one per operation |
|
|
23083
|
+
| `contractIdentityContract` | contract | the operation a server answers its fingerprint with; mount it with `withContractIdentity` |
|
|
22738
23084
|
| `ContractStreamFrameSchema` / `ContractStreamFrame` | schema / _type_ | default on-the-wire `data` / safe `error` / `end` envelope of a contract-first stream |
|
|
22739
23085
|
| `ContractStreamFraming` / `ContractStreamCompletion` | _types_ | opt-in item-vs-envelope framing and terminal-vs-stream-end completion policies |
|
|
22740
23086
|
| `StreamFinalLinePolicy` | _type_ | permissive or newline-required final NDJSON line policy |
|
|
@@ -23043,6 +23389,7 @@ Also re-exports the error helpers from `stitchkit/contract`.
|
|
|
23043
23389
|
| `createServer` | function | build the router and start `Bun.serve` — [guide](../guide/server.md#createserver) |
|
|
23044
23390
|
| `createHandler` | function | the router as a bare `(req) => Response` — [guide](../guide/server.md#createserver) |
|
|
23045
23391
|
| `implement` | function | bind a contract to typed handlers — [guide](../guide/server.md#implement) |
|
|
23392
|
+
| `withContractIdentity` | function | the given services plus the one answering `contractIdentityContract` with their fingerprint — the list to mount — [guide](../guide/client.md#which-contract-does-the-server-speak) |
|
|
23046
23393
|
| `createImplement` | function | fix the handler context type once |
|
|
23047
23394
|
| `createScopedImplement` | function | fix one scope→context map, then type every handler by its endpoint's effective scope; `.declare(contract)(handlers)` types without binding for the registry path, `.stream(scope, …)` covers streaming multipart — [guide](../guide/server.md#per-scope-handler-context--createscopedimplement) |
|
|
23048
23395
|
| `ScopedHandlers` | _type_ | handler map typed per endpoint by its effective scope |
|
|
@@ -25165,6 +25512,7 @@ SDK nor the `ai` peer.
|
|
|
25165
25512
|
|--------|------|---------|
|
|
25166
25513
|
| `createCli` | function | build and run a CLI from contracts; a runtime catalogue field conflicting with reserved/global argv options disables only that entry, retained in help with its reason; invocation returns `CLI_COMMAND_UNAVAILABLE` before side effects — [guide](../guide/cli.md#when-one-runtime-command-cannot-be-represented-in-argv) |
|
|
25167
25514
|
| `defineCliCommand` | function | define one Zod-typed CLI-only executable command with optional validated-result presentation/exit policy |
|
|
25515
|
+
| `defineCliResult` | function | type a managed command's `present` / `exitCode` policy against its output schema, for `CliConfig.results` — [guide](../guide/cli.md#the-same-policy-for-a-managed-command) |
|
|
25168
25516
|
| `parseCliArgs` | function | argv → typed tool args against a schema; inline `--field:=JSON` assigns a whole JSON value, including null, while ordinary string flags remain lexical; named array options together with bare positionals require an explicit `positionals` policy (advanced) |
|
|
25169
25517
|
| `routeCliArgv` | function | select the command out of argv without duplicating the global-option grammar (advanced) |
|
|
25170
25518
|
| `extractCliGlobalOptions` | function | lift the application's own global options out of argv before routing (advanced) |
|
|
@@ -25215,6 +25563,7 @@ SDK nor the `ai` peer.
|
|
|
25215
25563
|
| `CliCommandDefinitionBase` | _type_ | native command name, description and input schema |
|
|
25216
25564
|
| `CliCommandDefinitionWithOutput` | _type_ | native command with validated declared output and typed optional `present` / successful `exitCode` callbacks |
|
|
25217
25565
|
| `CliCommandDefinitionWithoutOutput` | _type_ | native void command without an output contract |
|
|
25566
|
+
| `CliResultPolicy` | _type_ | `present` / successful `exitCode` callbacks of one command, typed against its validated result; `CliConfig.results` holds one per managed command |
|
|
25218
25567
|
| `CliCommandContext` | _type_ | parsed input, framework run options, the application's `globals` and stdout/stderr writers |
|
|
25219
25568
|
| `CliRunOptions` | _type_ | parsed global flags (`--json` compacts success/error records, `--wait`, …) |
|
|
25220
25569
|
| `ParsedCliArgs` | _type_ | result of `parseCliArgs` |
|
|
@@ -25387,6 +25736,13 @@ Native POSIX Bun/Node one-shot command execution. See [native IO](../guide/nativ
|
|
|
25387
25736
|
| Export | Kind | Purpose |
|
|
25388
25737
|
|---|---|---|
|
|
25389
25738
|
| `runNativeCommand` | function | caller-lifetime streaming or explicitly bounded binary capture |
|
|
25739
|
+
| `createOperationProcesses` | function | run contract operations in one-call child processes: `unary` / `stream` return contract handlers bounded by `maxConcurrent`, `maxResponseBytes` and `timeoutMs` — [guide](../guide/native-io.md#a-contract-operation-in-a-process-of-its-own) |
|
|
25740
|
+
| `runOperationProcess` | function | the child side: read one call from stdin, run it against `implement(contract, handlers)` services, write the answer |
|
|
25741
|
+
| `OperationProcessesConfig` | _type_ | the child command and the three bounds of `createOperationProcesses` |
|
|
25742
|
+
| `OperationProcesses` | _type_ | `unary(contract, key)`, `stream(contract, key)` and the `active` count |
|
|
25743
|
+
| `OperationCallContext` | _type_ | what a process-operation handler reads of its call: `params`, `input` and the caller's `signal` |
|
|
25744
|
+
| `OperationProcessService` | _type_ | what `runOperationProcess` reads of a service; the result of `implement` fits |
|
|
25745
|
+
| `OperationProcessIo` | _type_ | injectable call source, writer and stop signal of `runOperationProcess` |
|
|
25390
25746
|
| `streamNativeCommand` | function | starts a command under the same group ownership, stop policy and bounds as `runNativeCommand` and returns `stdout` / `stderr` as `ReadableStream<Uint8Array>`, `result` and `stop()`; a slow reader slows the child, a cancelled stream drops its bytes, a failed command errors both streams |
|
|
25391
25747
|
| `NativeCommandStream` | type | what `streamNativeCommand` returns: `stdout`, `stderr`, `result` (its buffers are empty) and `stop()` |
|
|
25392
25748
|
| `NativeCommandStreamOptions` | type | `NativeCommandOptions` without `capture`, `onOutput` and `stdio`, which the streams own |
|
|
@@ -25446,6 +25802,16 @@ without splitting, preserve every output, and write each asset's `bytes` to its
|
|
|
25446
25802
|
This evolving entrypoint is a build dependency; runtime imports do not reach it.
|
|
25447
25803
|
The detailed recipe is in [native IO](../guide/native-io.md#public-native-packaging).
|
|
25448
25804
|
|
|
25805
|
+
It also owns the build of a client into one self-contained file:
|
|
25806
|
+
|
|
25807
|
+
| Export | Kind | Summary |
|
|
25808
|
+
|--------|------|---------|
|
|
25809
|
+
| `buildClientArtifact` | function | bundle a client entry into one self-contained file with its SHA-256, refuse forbidden modules in the graph, optionally pack and verify an npm archive — [guide](../guide/native-io.md#a-client-as-one-self-contained-file) |
|
|
25810
|
+
| `ClientArtifactOptions` | _type_ | entry, output file, runtime, forbidden modules and the optional package to pack and verify |
|
|
25811
|
+
| `ClientArtifact` | _type_ | the built file, its SHA-256 and size, the bundled modules and the verified archive |
|
|
25812
|
+
| `ForbiddenModule` | _type_ | a package name, a source path fragment or a `RegExp` over module paths |
|
|
25813
|
+
| `ForbiddenModuleError` | class | the refusal of a bundle that reaches a forbidden module: `module` and `importedBy` |
|
|
25814
|
+
|
|
25449
25815
|
## `stitchkit/http`
|
|
25450
25816
|
|
|
25451
25817
|
An HTTP-only entrypoint for browsers, React Native/Hermes and servers. It exports the same `createClient`, `createClients`, `createHttpClient`, URL builders, client configuration types and `ApiError` as the root, without loading its optional Socket.IO graph. Import shared contracts from `stitchkit/contract`.
|
package/llms.txt
CHANGED
|
@@ -9,7 +9,7 @@ Build with stitchkit: define a contract once, then `implement` it and serve it (
|
|
|
9
9
|
Load the slice for each entrypoint your code imports — `import … from 'stitchkit/server'` → `llms/server.txt`; the root `stitchkit` import → `llms/stitchkit.txt`. A slice holds that entrypoint's API reference section and the guide pages whose home it is; every guide lives in exactly one slice, and a slice that needs another's guide names it under "Also read". Each file is at most 50.0 KB; a longer one continues in numbered parts, each naming the next at its top. Moving across versions: load `llms/upgrading.txt` and the range slices it lists between your version and the target. `llms-full.txt` inlines everything; it is far larger than a context window, so do not read it whole.
|
|
10
10
|
|
|
11
11
|
## Entrypoint slices
|
|
12
|
-
- **stitchkit**: [llms/stitchkit.txt](llms/stitchkit.txt) 44.
|
|
12
|
+
- **stitchkit**: [llms/stitchkit.txt](llms/stitchkit.txt) 44.8 KB → [llms/stitchkit.2.txt](llms/stitchkit.2.txt) 47.4 KB
|
|
13
13
|
- **stitchkit/live**: [llms/live.txt](llms/live.txt) 21.2 KB
|
|
14
14
|
- **stitchkit/react**: [llms/react.txt](llms/react.txt) 8.7 KB
|
|
15
15
|
- **stitchkit/react/keyboard**: [llms/react-keyboard.txt](llms/react-keyboard.txt) 22.7 KB
|
|
@@ -21,12 +21,12 @@ Load the slice for each entrypoint your code imports — `import … from 'stitc
|
|
|
21
21
|
- **stitchkit/tools/contract**: [llms/tools-contract.txt](llms/tools-contract.txt) 0.2 KB
|
|
22
22
|
- **stitchkit/tools/invoker**: [llms/tools-invoker.txt](llms/tools-invoker.txt) 0.2 KB
|
|
23
23
|
- **stitchkit/tools/connections**: [llms/tools-connections.txt](llms/tools-connections.txt) 3.9 KB
|
|
24
|
-
- **stitchkit/cli**: [llms/cli.txt](llms/cli.txt)
|
|
24
|
+
- **stitchkit/cli**: [llms/cli.txt](llms/cli.txt) 40.9 KB → [llms/cli.2.txt](llms/cli.2.txt) 39.0 KB
|
|
25
25
|
- **stitchkit/cli/publish**: [llms/cli-publish.txt](llms/cli-publish.txt) 1.5 KB
|
|
26
26
|
- **stitchkit/remote**: [llms/remote.txt](llms/remote.txt) 1.0 KB
|
|
27
|
-
- **stitchkit/contract**: [llms/contract.txt](llms/contract.txt)
|
|
27
|
+
- **stitchkit/contract**: [llms/contract.txt](llms/contract.txt) 34.1 KB
|
|
28
28
|
- **stitchkit/primitives**: [llms/primitives.txt](llms/primitives.txt) 13.2 KB
|
|
29
|
-
- **stitchkit/server**: [llms/server.txt](llms/server.txt) 44.2 KB → [llms/server.2.txt](llms/server.2.txt) 23.1 KB → [llms/server.3.txt](llms/server.3.txt) 47.0 KB → [llms/server.4.txt](llms/server.4.txt) 38.
|
|
29
|
+
- **stitchkit/server**: [llms/server.txt](llms/server.txt) 44.2 KB → [llms/server.2.txt](llms/server.2.txt) 23.1 KB → [llms/server.3.txt](llms/server.3.txt) 47.0 KB → [llms/server.4.txt](llms/server.4.txt) 38.7 KB → [llms/server.5.txt](llms/server.5.txt) 23.7 KB
|
|
30
30
|
- **stitchkit/observability**: [llms/observability.txt](llms/observability.txt) 46.9 KB
|
|
31
31
|
- **stitchkit/agent-runtime**: [llms/agent-runtime.txt](llms/agent-runtime.txt) 5.7 KB → [llms/agent-runtime.2.txt](llms/agent-runtime.2.txt) 44.1 KB → [llms/agent-runtime.3.txt](llms/agent-runtime.3.txt) 42.5 KB → [llms/agent-runtime.4.txt](llms/agent-runtime.4.txt) 45.2 KB → [llms/agent-runtime.5.txt](llms/agent-runtime.5.txt) 11.8 KB
|
|
32
32
|
- **stitchkit/agent-runtime/testing**: [llms/agent-runtime-testing.txt](llms/agent-runtime-testing.txt) 2.6 KB
|
|
@@ -64,15 +64,15 @@ Load the slice for each entrypoint your code imports — `import … from 'stitc
|
|
|
64
64
|
- **stitchkit/node**: [llms/node.txt](llms/node.txt) 3.1 KB
|
|
65
65
|
- **stitchkit/agent-runtime/react**: [llms/agent-runtime-react.txt](llms/agent-runtime-react.txt) 1.0 KB
|
|
66
66
|
- **stitchkit/agent-runtime/realtime**: [llms/agent-runtime-realtime.txt](llms/agent-runtime-realtime.txt) 2.6 KB
|
|
67
|
-
- **stitchkit/process**: [llms/process.txt](llms/process.txt)
|
|
67
|
+
- **stitchkit/process**: [llms/process.txt](llms/process.txt) 46.8 KB → [llms/process.2.txt](llms/process.2.txt) 9.1 KB
|
|
68
68
|
- **stitchkit/process/owner-loss**: [llms/process-owner-loss.txt](llms/process-owner-loss.txt) 0.9 KB
|
|
69
|
-
- **stitchkit/files/packaging**: [llms/files-packaging.txt](llms/files-packaging.txt) 3
|
|
69
|
+
- **stitchkit/files/packaging**: [llms/files-packaging.txt](llms/files-packaging.txt) 4.3 KB
|
|
70
70
|
- **stitchkit/http**: [llms/http.txt](llms/http.txt) 0.9 KB
|
|
71
71
|
- **stitchkit/realtime**: [llms/realtime.txt](llms/realtime.txt) 1.4 KB
|
|
72
72
|
|
|
73
73
|
## Upgrading slices
|
|
74
74
|
- **Upgrading — how to move across versions**: [llms/upgrading.txt](llms/upgrading.txt) 12.7 KB
|
|
75
|
-
- **Upgrading 0.106.0 → 0.
|
|
75
|
+
- **Upgrading 0.106.0 → 0.123.0**: [llms/upgrading-0.106.0-0.123.0.txt](llms/upgrading-0.106.0-0.123.0.txt) 36.3 KB
|
|
76
76
|
- **Upgrading 0.96.0 → 0.105.0**: [llms/upgrading-0.96.0-0.105.0.txt](llms/upgrading-0.96.0-0.105.0.txt) 45.8 KB
|
|
77
77
|
- **Upgrading 0.69.0 → 0.95.0**: [llms/upgrading-0.69.0-0.95.0.txt](llms/upgrading-0.69.0-0.95.0.txt) 46.6 KB
|
|
78
78
|
- **Upgrading 0.55.0 → 0.68.0**: [llms/upgrading-0.55.0-0.68.0.txt](llms/upgrading-0.55.0-0.68.0.txt) 47.0 KB
|
package/package.json
CHANGED