stitchkit 0.54.0 → 0.56.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/README.md +4 -1
- package/dist/browser/socket-io.d.ts +25 -7
- package/dist/browser/socket-io.d.ts.map +1 -1
- package/dist/cli.js +14 -9
- package/dist/contract/errors.d.ts +7 -0
- package/dist/contract/errors.d.ts.map +1 -1
- package/dist/contract/file-ref.d.ts +13 -0
- package/dist/contract/file-ref.d.ts.map +1 -0
- package/dist/contract/index.d.ts +1 -0
- package/dist/contract/index.d.ts.map +1 -1
- package/dist/contract/index.js +21 -17
- package/dist/files/boundary.d.ts +55 -0
- package/dist/files/boundary.d.ts.map +1 -0
- package/dist/files.d.ts +3 -0
- package/dist/files.d.ts.map +1 -0
- package/dist/files.js +16 -0
- package/dist/index-1bx83sw4.js +4 -0
- package/dist/{index-pfqjb5xy.js → index-28cqssm3.js} +30 -5
- package/dist/index-557by2db.js +34 -0
- package/dist/index-5r13htq1.js +283 -0
- package/dist/index-6k1937bx.js +20 -0
- package/dist/index-6y759j86.js +1031 -0
- package/dist/{index-bmnhqtya.js → index-7eq9va6e.js} +1 -4
- package/dist/index-bfcpjw20.js +363 -0
- package/dist/{index-yxpe3phd.js → index-cby4ar3v.js} +1 -18
- package/dist/{index-7vb44hy8.js → index-escqg10p.js} +193 -751
- package/dist/{index-173tvqcp.js → index-j3dem06f.js} +9 -114
- package/dist/{index-gvha32jh.js → index-jcc611vh.js} +2 -2
- package/dist/{index-z7761jk2.js → index-n4nfa7gh.js} +16 -22
- package/dist/{index-e1es808r.js → index-qh4xevf5.js} +1 -1
- package/dist/index-sbdmyz75.js +18 -0
- package/dist/{index-b15rtha1.js → index-sm2tjx06.js} +98 -3
- package/dist/{index-yez419px.js → index-xy8fmh6w.js} +99 -7
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +188 -40
- package/dist/node.js +31 -27
- package/dist/observability/index.js +28 -25
- package/dist/react.js +2 -2
- package/dist/realtime/contract.d.ts +9 -0
- package/dist/realtime/contract.d.ts.map +1 -1
- package/dist/realtime/index.d.ts +2 -1
- package/dist/realtime/index.d.ts.map +1 -1
- package/dist/realtime/request.d.ts +21 -0
- package/dist/realtime/request.d.ts.map +1 -0
- package/dist/realtime/socket.d.ts +3 -0
- package/dist/realtime/socket.d.ts.map +1 -1
- package/dist/remote.d.ts +9 -0
- package/dist/remote.d.ts.map +1 -0
- package/dist/remote.js +79 -0
- package/dist/server/context-contribution.d.ts +18 -0
- package/dist/server/context-contribution.d.ts.map +1 -0
- package/dist/server/context.d.ts.map +1 -1
- package/dist/server/index.d.ts +2 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +136 -88
- package/dist/server/lifecycle.d.ts +4 -0
- package/dist/server/lifecycle.d.ts.map +1 -0
- package/dist/server/middleware/auth.d.ts +64 -9
- package/dist/server/middleware/auth.d.ts.map +1 -1
- package/dist/server/types.d.ts +1 -1
- package/dist/server/types.d.ts.map +1 -1
- package/dist/testing/surface-conformance.d.ts +180 -0
- package/dist/testing/surface-conformance.d.ts.map +1 -0
- package/dist/testing/surface-manifest.d.ts +255 -0
- package/dist/testing/surface-manifest.d.ts.map +1 -0
- package/dist/testing.d.ts +2 -0
- package/dist/testing.d.ts.map +1 -1
- package/dist/testing.js +714 -3
- package/dist/tools/agent.d.ts.map +1 -1
- package/dist/tools/async-operation.d.ts +360 -0
- package/dist/tools/async-operation.d.ts.map +1 -0
- package/dist/tools/cli-args.d.ts +1 -1
- package/dist/tools/cli-args.d.ts.map +1 -1
- package/dist/tools/cli-format.d.ts +2 -2
- package/dist/tools/cli-format.d.ts.map +1 -1
- package/dist/tools/cli-wait.d.ts +11 -2
- package/dist/tools/cli-wait.d.ts.map +1 -1
- package/dist/tools/cli.d.ts +2 -0
- package/dist/tools/cli.d.ts.map +1 -1
- package/dist/tools/define-download-tool.d.ts +6 -5
- package/dist/tools/define-download-tool.d.ts.map +1 -1
- package/dist/tools/define-upload-tool.d.ts +3 -1
- package/dist/tools/define-upload-tool.d.ts.map +1 -1
- package/dist/tools/define-wait-tool.d.ts +1 -1
- package/dist/tools/define-wait-tool.d.ts.map +1 -1
- package/dist/tools/download-core.d.ts +5 -10
- package/dist/tools/download-core.d.ts.map +1 -1
- package/dist/tools/internal/surface-projector.d.ts +101 -0
- package/dist/tools/internal/surface-projector.d.ts.map +1 -0
- package/dist/tools/lifecycle.d.ts +4 -0
- package/dist/tools/lifecycle.d.ts.map +1 -0
- package/dist/tools/managed-file-error.d.ts +6 -0
- package/dist/tools/managed-file-error.d.ts.map +1 -0
- package/dist/tools/mcp-prepare.d.ts +3 -21
- package/dist/tools/mcp-prepare.d.ts.map +1 -1
- package/dist/tools/mcp-round-policy.d.ts +9 -0
- package/dist/tools/mcp-round-policy.d.ts.map +1 -0
- package/dist/tools/mcp-round.d.ts +0 -4
- package/dist/tools/mcp-round.d.ts.map +1 -1
- package/dist/tools/mount-download.d.ts +5 -4
- package/dist/tools/mount-download.d.ts.map +1 -1
- package/dist/tools/mount-upload.d.ts +4 -2
- package/dist/tools/mount-upload.d.ts.map +1 -1
- package/dist/tools/mount.d.ts +5 -9
- package/dist/tools/mount.d.ts.map +1 -1
- package/dist/tools/runtime-tool.d.ts.map +1 -1
- package/dist/tools/surface.d.ts +7 -0
- package/dist/tools/surface.d.ts.map +1 -1
- package/dist/tools/upload-core.d.ts +3 -2
- package/dist/tools/upload-core.d.ts.map +1 -1
- package/dist/tools/view-file.d.ts +4 -3
- package/dist/tools/view-file.d.ts.map +1 -1
- package/dist/tools/wait-core.d.ts +6 -1
- package/dist/tools/wait-core.d.ts.map +1 -1
- package/dist/tools.d.ts +3 -2
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +705 -599
- package/llms-full.txt +727 -34
- package/package.json +10 -2
package/llms-full.txt
CHANGED
|
@@ -33,7 +33,7 @@ realtime, `@tanstack/react-query` + `react-query-kit` for React). See
|
|
|
33
33
|
|
|
34
34
|
## Entrypoints
|
|
35
35
|
|
|
36
|
-
stitchkit ships
|
|
36
|
+
stitchkit ships eleven entrypoints. Each is import-safe for one environment —
|
|
37
37
|
keeping server-only code (`Bun.serve`, the MCP SDK) out of browser bundles.
|
|
38
38
|
|
|
39
39
|
| Import | Use in | Holds |
|
|
@@ -44,12 +44,15 @@ keeping server-only code (`Bun.serve`, the MCP SDK) out of browser bundles.
|
|
|
44
44
|
| `stitchkit/node` | server (Node ≥ 22) | `serveNode` + the runtime-agnostic core — the Node mirror of `/server` |
|
|
45
45
|
| `stitchkit/tools` | server | `createMcpHandler`, `mountMcp`, `mountAgent`, the OAuth provider, native tools |
|
|
46
46
|
| `stitchkit/cli` | server | `createCli` — the CLI transport, light (no MCP SDK / `ai`) |
|
|
47
|
+
| `stitchkit/remote` | browser **and** server | peer-free `implementRemote` for thin HTTP proxy processes |
|
|
48
|
+
| `stitchkit/files` | server (Bun or Node) | peer-free managed local-file boundary |
|
|
47
49
|
| `stitchkit/observability` | server | request/tool event projections — `createObservability`, trace context, sanitisation |
|
|
48
50
|
| `stitchkit/testing` | tests on Bun or Node | in-process generated clients over a real Fetch handler, without a TCP port |
|
|
49
51
|
| `stitchkit/react` | browser | `createCursorQuery`, `createCacheBridge` |
|
|
50
52
|
|
|
51
53
|
Rule of thumb: browser code imports `stitchkit` and `stitchkit/react`; server
|
|
52
|
-
code adds `stitchkit/server` (or `stitchkit/node` on Node) and
|
|
54
|
+
code adds `stitchkit/server` (or `stitchkit/node` on Node) and opts into
|
|
55
|
+
`stitchkit/tools`, `stitchkit/remote` or `stitchkit/files` by capability.
|
|
53
56
|
The full export list of each is in the [API reference](../api/reference.md).
|
|
54
57
|
|
|
55
58
|
## Project layout
|
|
@@ -2777,7 +2780,8 @@ through the contract's typed client:
|
|
|
2777
2780
|
|
|
2778
2781
|
```ts
|
|
2779
2782
|
import { createHttpClient } from 'stitchkit'
|
|
2780
|
-
import {
|
|
2783
|
+
import { implementRemote } from 'stitchkit/remote'
|
|
2784
|
+
import { createStdioMcpServer } from 'stitchkit/tools'
|
|
2781
2785
|
|
|
2782
2786
|
const http = createHttpClient({
|
|
2783
2787
|
baseUrl: 'https://api.example.com',
|
|
@@ -3017,8 +3021,17 @@ import {
|
|
|
3017
3021
|
defineViewFileTool,
|
|
3018
3022
|
defineWaitTool,
|
|
3019
3023
|
} from 'stitchkit/tools'
|
|
3024
|
+
import { createManagedFileBoundary } from 'stitchkit/files'
|
|
3020
3025
|
import { z } from 'zod'
|
|
3021
3026
|
|
|
3027
|
+
const files = await createManagedFileBoundary({
|
|
3028
|
+
root: '/srv/job-media',
|
|
3029
|
+
createRoot: true, // creates only this final directory; /srv must already be trusted
|
|
3030
|
+
inspectionTimeoutMs: 15_000,
|
|
3031
|
+
inspect: ({ prefix, name, declaredMediaType, signal }) =>
|
|
3032
|
+
inspectFile(prefix, { name, declaredMediaType, signal }),
|
|
3033
|
+
})
|
|
3034
|
+
|
|
3022
3035
|
const waitForJob = defineWaitTool({
|
|
3023
3036
|
name: 'wait_for_job',
|
|
3024
3037
|
description: 'Wait for a job to finish',
|
|
@@ -3035,10 +3048,10 @@ const downloadResult = defineDownloadTool({
|
|
|
3035
3048
|
name: 'download_result',
|
|
3036
3049
|
description: 'Download a completed result',
|
|
3037
3050
|
identity: { serviceName: 'jobs', action: 'downloadResult', scope: 'user' },
|
|
3038
|
-
input: z.object({ id: z.string()
|
|
3051
|
+
input: z.object({ id: z.string() }),
|
|
3039
3052
|
resolveUrl: ({ id }) => resolveResultUrl(id),
|
|
3040
|
-
|
|
3041
|
-
|
|
3053
|
+
files,
|
|
3054
|
+
pathFromInput: ({ id }) => `results/${id}.bin`,
|
|
3042
3055
|
})
|
|
3043
3056
|
|
|
3044
3057
|
const uploadInput = defineUploadTool({
|
|
@@ -3046,16 +3059,17 @@ const uploadInput = defineUploadTool({
|
|
|
3046
3059
|
description: 'Upload a local input file',
|
|
3047
3060
|
identity: { serviceName: 'jobs', action: 'uploadInput', scope: 'user' },
|
|
3048
3061
|
output: z.object({ url: z.url() }),
|
|
3049
|
-
|
|
3062
|
+
files,
|
|
3063
|
+
upload: (source, context) => uploadBytes(source.bytes, context.signal),
|
|
3050
3064
|
})
|
|
3051
3065
|
|
|
3052
3066
|
const viewResult = defineViewFileTool({
|
|
3053
3067
|
name: 'view_result',
|
|
3054
3068
|
description: 'Inspect generated media',
|
|
3055
3069
|
identity: { serviceName: 'jobs', action: 'viewResult', scope: 'user' },
|
|
3056
|
-
// Local files are disabled when
|
|
3070
|
+
// Local files are disabled when files is omitted. URL fetches retain SSRF
|
|
3057
3071
|
// protection; use allowPrivateHosts only for an explicitly trusted network.
|
|
3058
|
-
|
|
3072
|
+
files,
|
|
3059
3073
|
})
|
|
3060
3074
|
|
|
3061
3075
|
const runtimeTools = [waitForJob, downloadResult, uploadInput, viewResult]
|
|
@@ -3078,6 +3092,142 @@ same neutral mechanics but do not gain lifecycle or hooks. The raw view-file
|
|
|
3078
3092
|
adapter preserves its content-only MCP envelope while using the same bounded
|
|
3079
3093
|
batch operation.
|
|
3080
3094
|
|
|
3095
|
+
### Async-operation protocol
|
|
3096
|
+
|
|
3097
|
+
For a complete long-running operation, define the linked runtime-only surface
|
|
3098
|
+
once. Stitchkit owns transport names, validation and wait mechanics; the
|
|
3099
|
+
application still owns storage, execution, retries and domain states:
|
|
3100
|
+
|
|
3101
|
+
```ts
|
|
3102
|
+
import {
|
|
3103
|
+
createAsyncOperationSnapshotSchema,
|
|
3104
|
+
defineAsyncOperation,
|
|
3105
|
+
} from 'stitchkit/tools'
|
|
3106
|
+
|
|
3107
|
+
const id = z.object({ id: z.string() })
|
|
3108
|
+
const state = z.object({
|
|
3109
|
+
phase: z.enum(['pending', 'running', 'succeeded']),
|
|
3110
|
+
value: z.string().optional(),
|
|
3111
|
+
})
|
|
3112
|
+
const snapshot = createAsyncOperationSnapshotSchema({
|
|
3113
|
+
failure: z.object({ code: z.string() }),
|
|
3114
|
+
progress: z.object({ current: z.number(), total: z.number() }),
|
|
3115
|
+
})
|
|
3116
|
+
|
|
3117
|
+
const exportOperation = defineAsyncOperation({
|
|
3118
|
+
mode: 'runtime-only',
|
|
3119
|
+
name: 'export',
|
|
3120
|
+
description: 'Export project data',
|
|
3121
|
+
identity: { serviceName: 'exports', action: 'export', scope: 'user' },
|
|
3122
|
+
startInput: z.object({ projectId: z.string() }),
|
|
3123
|
+
id,
|
|
3124
|
+
state,
|
|
3125
|
+
snapshot,
|
|
3126
|
+
start: ({ projectId }) => enqueueExport(projectId),
|
|
3127
|
+
authorize: ({ id }, capability, ctx) => authorizeExport(id, capability, ctx),
|
|
3128
|
+
inspect: ({ id }, ctx) => readExport(id, ctx.signal),
|
|
3129
|
+
classify: (job) => ({ phase: job.phase }),
|
|
3130
|
+
result: {
|
|
3131
|
+
output: z.object({ value: z.string() }),
|
|
3132
|
+
handler: (job) => ({ value: job.value ?? '' }),
|
|
3133
|
+
},
|
|
3134
|
+
})
|
|
3135
|
+
|
|
3136
|
+
const runtimeTools = exportOperation.runtimeTools
|
|
3137
|
+
```
|
|
3138
|
+
|
|
3139
|
+
Every follow-up repeats `authorize`; an opaque id is never authority. Aborting
|
|
3140
|
+
`wait` only stops waiting and never calls optional domain `cancel`.
|
|
3141
|
+
|
|
3142
|
+
For a new HTTP surface, derive the canonical capability contract once. The
|
|
3143
|
+
factory defines schemas and routes only; the application still supplies normal
|
|
3144
|
+
handlers to `implement` and owns the job:
|
|
3145
|
+
|
|
3146
|
+
```ts
|
|
3147
|
+
import { defineAsyncOperationContract } from 'stitchkit/tools'
|
|
3148
|
+
import { z } from 'zod'
|
|
3149
|
+
|
|
3150
|
+
const operationId = z.object({ id: z.string() })
|
|
3151
|
+
const operationSnapshot = z.object({
|
|
3152
|
+
phase: z.enum(['pending', 'running', 'succeeded', 'failed', 'cancelled']),
|
|
3153
|
+
})
|
|
3154
|
+
|
|
3155
|
+
const operations = defineAsyncOperationContract({
|
|
3156
|
+
prefix: 'exports',
|
|
3157
|
+
scope: 'user',
|
|
3158
|
+
description: 'Export project data',
|
|
3159
|
+
startInput: z.object({ projectId: z.string() }),
|
|
3160
|
+
id: operationId,
|
|
3161
|
+
snapshot: operationSnapshot,
|
|
3162
|
+
cancel: true,
|
|
3163
|
+
result: z.object({ downloadUrl: z.url() }),
|
|
3164
|
+
})
|
|
3165
|
+
|
|
3166
|
+
// Pass operations.contract and ordinary handlers to implement(...).
|
|
3167
|
+
// operations.capabilities/schemas/adapters are the canonical protocol metadata.
|
|
3168
|
+
```
|
|
3169
|
+
|
|
3170
|
+
If an existing dedicated contract has a different wire shape — for example
|
|
3171
|
+
`start` returns a snapshot while follow-ups accept separate envelopes — bind it
|
|
3172
|
+
without casts or duplicate endpoints. Inline callbacks are contextually typed;
|
|
3173
|
+
every returned id/input is parsed by the destination schema:
|
|
3174
|
+
|
|
3175
|
+
```ts
|
|
3176
|
+
import { defineContract } from 'stitchkit/contract'
|
|
3177
|
+
import { bindContractAsyncOperation } from 'stitchkit/tools'
|
|
3178
|
+
|
|
3179
|
+
const startOutput = z.object({ operation: operationId, acceptedAt: z.string() })
|
|
3180
|
+
const statusInput = z.object({ operationId: z.string() })
|
|
3181
|
+
const waitInput = z.object({ lookup: operationId })
|
|
3182
|
+
|
|
3183
|
+
const existing = defineContract({ prefix: 'exports' }, {
|
|
3184
|
+
start: {
|
|
3185
|
+
method: 'POST', path: '/', desc: 'Start export',
|
|
3186
|
+
input: z.object({ projectId: z.string() }), output: startOutput,
|
|
3187
|
+
},
|
|
3188
|
+
status: {
|
|
3189
|
+
method: 'POST', path: '/status', desc: 'Read export status',
|
|
3190
|
+
input: statusInput, output: operationSnapshot,
|
|
3191
|
+
},
|
|
3192
|
+
wait: {
|
|
3193
|
+
method: 'POST', path: '/wait', desc: 'Wait for export',
|
|
3194
|
+
input: waitInput, output: operationSnapshot,
|
|
3195
|
+
},
|
|
3196
|
+
})
|
|
3197
|
+
|
|
3198
|
+
const operationHandlers = {
|
|
3199
|
+
start: () => ({ operation: { id: 'example' }, acceptedAt: new Date().toISOString() }),
|
|
3200
|
+
status: (): z.output<typeof operationSnapshot> => ({ phase: 'pending' }),
|
|
3201
|
+
wait: (): z.output<typeof operationSnapshot> => ({ phase: 'succeeded' }),
|
|
3202
|
+
}
|
|
3203
|
+
|
|
3204
|
+
const bound = bindContractAsyncOperation({
|
|
3205
|
+
mode: 'contract-backed',
|
|
3206
|
+
binding: 'adapted',
|
|
3207
|
+
contract: existing,
|
|
3208
|
+
id: operationId,
|
|
3209
|
+
capabilities: { start: 'start', status: 'status', wait: 'wait' },
|
|
3210
|
+
adapters: {
|
|
3211
|
+
idFromStart: (output) => output.operation,
|
|
3212
|
+
inputFor: {
|
|
3213
|
+
status: (id) => ({ operationId: id.id }),
|
|
3214
|
+
wait: (id) => ({ lookup: id }),
|
|
3215
|
+
},
|
|
3216
|
+
},
|
|
3217
|
+
handlers: operationHandlers,
|
|
3218
|
+
})
|
|
3219
|
+
```
|
|
3220
|
+
|
|
3221
|
+
Direct binding remains the short form when start already returns the id and
|
|
3222
|
+
every follow-up reuses that exact schema instance. Its ID schema must be
|
|
3223
|
+
wire-stable: input and output types match and parsing has no transform,
|
|
3224
|
+
coercion, default or overwrite. This also excludes same-type transforms because
|
|
3225
|
+
the direct adapters would parse an already parsed ID again. Use adapted binding
|
|
3226
|
+
and explicitly project the parsed ID back into each follow-up input. In both
|
|
3227
|
+
modes the binder creates no router;
|
|
3228
|
+
it returns the existing contract, handlers and protocol metadata for
|
|
3229
|
+
application composition.
|
|
3230
|
+
|
|
3081
3231
|
### Explicit raw SDK registration
|
|
3082
3232
|
|
|
3083
3233
|
`rawTools` is deliberately named as an escape hatch. A tool registered there
|
|
@@ -3093,7 +3243,7 @@ const handleMcp = createMcpHandler({
|
|
|
3093
3243
|
serverInfo: { name: 'my-app', version: '1.0.0' },
|
|
3094
3244
|
auth,
|
|
3095
3245
|
services: [service],
|
|
3096
|
-
rawTools: (server) => mountViewFile(server, {
|
|
3246
|
+
rawTools: (server) => mountViewFile(server, { files }),
|
|
3097
3247
|
})
|
|
3098
3248
|
```
|
|
3099
3249
|
|
|
@@ -3234,6 +3384,12 @@ await createCli({
|
|
|
3234
3384
|
})
|
|
3235
3385
|
```
|
|
3236
3386
|
|
|
3387
|
+
`createCli({ signal })` and its wait loop honor an explicit caller
|
|
3388
|
+
`AbortSignal`. Stitchkit does not install a process-global SIGINT handler for
|
|
3389
|
+
ordinary CLI programs: if desired, the application binds SIGINT to an
|
|
3390
|
+
`AbortController` and passes its signal. Aborting wait stops polling only; it
|
|
3391
|
+
does not cancel the underlying job.
|
|
3392
|
+
|
|
3237
3393
|
```json
|
|
3238
3394
|
// package.json
|
|
3239
3395
|
{ "bin": { "myapp": "./dist/cli.js" } }
|
|
@@ -3347,7 +3503,7 @@ same Zod schema an HTTP or MCP call does.
|
|
|
3347
3503
|
|
|
3348
3504
|
| Flag | Effect |
|
|
3349
3505
|
| --------------------- | ---------------------------------------------------------- |
|
|
3350
|
-
| `--json` |
|
|
3506
|
+
| `--json` | Compact success/error JSON records for scripts |
|
|
3351
3507
|
| `--wait` | Block-poll an async result to a terminal state |
|
|
3352
3508
|
| `--wait-timeout <s>` | Override the `--wait` timeout |
|
|
3353
3509
|
| `--output-dir <dir>` | Download result media into a directory |
|
|
@@ -3355,10 +3511,18 @@ same Zod schema an HTTP or MCP call does.
|
|
|
3355
3511
|
| `--dry-run` | Print the resolved call without executing |
|
|
3356
3512
|
| `--help`, `-h` | Usage — top-level or per-command flag table |
|
|
3357
3513
|
|
|
3358
|
-
stdout carries the result; errors and progress go to stderr
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3514
|
+
stdout carries the result; structured errors and progress go to stderr. With
|
|
3515
|
+
`--json`, a success or structured failure is exactly one compact,
|
|
3516
|
+
newline-terminated JSON record on its respective stream; progress and CLI usage
|
|
3517
|
+
diagnostics remain ordinary stderr text. This keeps stdout pipeable and
|
|
3518
|
+
`2>/dev/null` clean. The process exit code carries the error class (`0` ok,
|
|
3519
|
+
`VALIDATION_ERROR → 1`, `UNAUTHORIZED → 2`, `FORBIDDEN → 3`, `NOT_FOUND → 4`,
|
|
3520
|
+
…) — override per app with `exitCodes`.
|
|
3521
|
+
|
|
3522
|
+
Per-command help derives the positional form from the same schema order as the
|
|
3523
|
+
argv parser. For example, a required `action` and optional `profile` render as
|
|
3524
|
+
`Usage: myapp skill <action> [profile] [--flags]`; the argument table also shows
|
|
3525
|
+
`<action> | --action` and `[profile] | --profile`. Boolean fields remain flags.
|
|
3362
3526
|
|
|
3363
3527
|
## `--wait` — background-friendly generation
|
|
3364
3528
|
|
|
@@ -3375,12 +3539,19 @@ await createCli({
|
|
|
3375
3539
|
generate: {
|
|
3376
3540
|
tool: 'get_generation',
|
|
3377
3541
|
poll: (r) => (isRecord(r) && typeof r.id === 'string' ? { id: r.id } : null),
|
|
3378
|
-
done: (r) => isRecord(r) &&
|
|
3542
|
+
done: (r) => isRecord(r) && ['COMPLETED', 'FAILED'].includes(String(r.status)),
|
|
3543
|
+
failed: (r) => isRecord(r) && r.status === 'FAILED',
|
|
3379
3544
|
},
|
|
3380
3545
|
},
|
|
3381
3546
|
})
|
|
3382
3547
|
```
|
|
3383
3548
|
|
|
3549
|
+
`failed` is optional. When it matches either the initial result or a later poll,
|
|
3550
|
+
polling stops and the CLI emits `WAIT_FAILED` on stderr with a non-zero exit;
|
|
3551
|
+
the terminal payload is retained under `details.result`. `failed` is checked
|
|
3552
|
+
before `done`, so overlapping predicates fail closed. A failed poll tool call
|
|
3553
|
+
keeps its own error code, and an elapsed deadline remains `TIMEOUT`.
|
|
3554
|
+
|
|
3384
3555
|
```bash
|
|
3385
3556
|
# foreground
|
|
3386
3557
|
myapp generate "a fox" --wait --output-dir ./out
|
|
@@ -3486,6 +3657,57 @@ runtime. A malformed inbound tuple never reaches the application handler;
|
|
|
3486
3657
|
invalid outbound data throws before Socket.IO publishes it. Rejections call the
|
|
3487
3658
|
optional `onRejected` hook with event, direction, phase and the Zod error.
|
|
3488
3659
|
|
|
3660
|
+
### Protocol generations
|
|
3661
|
+
|
|
3662
|
+
For a distributed producer/consumer pair, put a literal generation first in the
|
|
3663
|
+
first payload object. Zod validates object fields in declaration order, so an
|
|
3664
|
+
incompatible peer is identified before the rest of its payload is interpreted:
|
|
3665
|
+
|
|
3666
|
+
```ts
|
|
3667
|
+
const ReplicationMessage = z.object({
|
|
3668
|
+
v: z.literal(2),
|
|
3669
|
+
item: ReplicatedItem,
|
|
3670
|
+
})
|
|
3671
|
+
|
|
3672
|
+
const realtimeContract = defineRealtimeContract({
|
|
3673
|
+
serverToClient: {
|
|
3674
|
+
replicated: { args: z.tuple([ReplicationMessage]) },
|
|
3675
|
+
},
|
|
3676
|
+
clientToServer: {},
|
|
3677
|
+
})
|
|
3678
|
+
```
|
|
3679
|
+
|
|
3680
|
+
The original `z.ZodError` is retained as `RealtimeRejectedEvent.error.cause`.
|
|
3681
|
+
Because event arguments are a tuple, the first payload's `v` path is
|
|
3682
|
+
`[0, 'v']` (not `['v']`):
|
|
3683
|
+
|
|
3684
|
+
```ts
|
|
3685
|
+
import type { RealtimeRejectedEvent } from 'stitchkit'
|
|
3686
|
+
import { z } from 'zod'
|
|
3687
|
+
|
|
3688
|
+
function isProtocolGenerationMismatch(rejected: RealtimeRejectedEvent): boolean {
|
|
3689
|
+
const cause = rejected.error.cause
|
|
3690
|
+
if (!(cause instanceof z.ZodError)) return false
|
|
3691
|
+
const first = cause.issues[0]
|
|
3692
|
+
return first?.code === 'invalid_value'
|
|
3693
|
+
&& first.path.length === 2
|
|
3694
|
+
&& first.path[0] === 0
|
|
3695
|
+
&& first.path[1] === 'v'
|
|
3696
|
+
}
|
|
3697
|
+
|
|
3698
|
+
const socket = createRealtimeClient(realtimeContract, {
|
|
3699
|
+
url,
|
|
3700
|
+
onRejected: (rejected) => {
|
|
3701
|
+
if (isProtocolGenerationMismatch(rejected)) schedulePeerUpgrade()
|
|
3702
|
+
else reportMalformedRealtimePayload(rejected)
|
|
3703
|
+
},
|
|
3704
|
+
})
|
|
3705
|
+
```
|
|
3706
|
+
|
|
3707
|
+
A generation mismatch means “upgrade the peer”; another schema rejection means
|
|
3708
|
+
“fix the producer or payload”. This stays an application convention, not a core
|
|
3709
|
+
API: only the application knows which field denotes protocol compatibility.
|
|
3710
|
+
|
|
3489
3711
|
## Server — `createSocketIOServer`
|
|
3490
3712
|
|
|
3491
3713
|
```ts
|
|
@@ -3584,11 +3806,59 @@ socket.on('note:created', (note) => { /* typed note */ })
|
|
|
3584
3806
|
socket.emit('room:join', 'general', ({ joined }) => { /* typed + validated */ })
|
|
3585
3807
|
```
|
|
3586
3808
|
|
|
3809
|
+
When an application already owns the low-level Stitchkit transport, bind the
|
|
3810
|
+
contract without opening a second connection:
|
|
3811
|
+
|
|
3812
|
+
```ts
|
|
3813
|
+
import { bindRealtimeClient, createSocketIOClient } from 'stitchkit'
|
|
3814
|
+
|
|
3815
|
+
const transport = createSocketIOClient({ url: 'https://api.example.com' })
|
|
3816
|
+
const events = bindRealtimeClient(realtimeContract, transport, { onRejected })
|
|
3817
|
+
|
|
3818
|
+
transport.connect() // lifecycle stays with the transport owner
|
|
3819
|
+
events.on('note:created', handleNote)
|
|
3820
|
+
await events.request('room:join', 'general', { timeoutMs: 5_000 })
|
|
3821
|
+
```
|
|
3822
|
+
|
|
3823
|
+
The bound handle intentionally has no `connect()` or `disconnect()`. Its
|
|
3824
|
+
`on`/`emit`/`request`, rejection and timeout semantics are exactly the path used
|
|
3825
|
+
by `createRealtimeClient`; only transport construction/lifecycle differs.
|
|
3826
|
+
|
|
3827
|
+
### Request-response over realtime
|
|
3828
|
+
|
|
3829
|
+
For an event with an `ack` schema, `request()` is the Promise form of the same
|
|
3830
|
+
native Socket.IO acknowledgement. Arguments and the resolved value are inferred
|
|
3831
|
+
from the contract and validated on both sides:
|
|
3832
|
+
|
|
3833
|
+
```ts
|
|
3834
|
+
const result = await socket.request(
|
|
3835
|
+
'room:join',
|
|
3836
|
+
'general',
|
|
3837
|
+
{ timeoutMs: 5_000 },
|
|
3838
|
+
)
|
|
3839
|
+
// result: { joined: boolean }
|
|
3840
|
+
```
|
|
3841
|
+
|
|
3842
|
+
An event without `ack` cannot be passed to `request()` by type. A disconnected
|
|
3843
|
+
client rejects immediately with `RealtimeRequestDisconnectedError`; an in-flight
|
|
3844
|
+
disconnect uses the same error; an elapsed native acknowledgement timeout uses
|
|
3845
|
+
`RealtimeRequestTimeoutError`. These are distinct stable classes and codes, so
|
|
3846
|
+
never parse Socket.IO error text. An invalid acknowledgement still fires the
|
|
3847
|
+
existing `onRejected` hook with phase `acknowledgement`, then rejects with
|
|
3848
|
+
`RealtimeRequestInvalidAcknowledgementError`.
|
|
3849
|
+
|
|
3850
|
+
`timeoutMs` must be finite and greater than zero. Use acknowledgements only for
|
|
3851
|
+
bounded request-response work. A job that runs for minutes, progress streaming,
|
|
3852
|
+
or resumable delivery should use separate correlated events or the async
|
|
3853
|
+
operation protocol; keeping one acknowledgement open is not a durable RPC/job
|
|
3854
|
+
transport.
|
|
3855
|
+
|
|
3587
3856
|
## Low-level transport
|
|
3588
3857
|
|
|
3589
3858
|
`createSocketIOClient` remains the low-level Socket.IO transport wrapper for
|
|
3590
3859
|
schema-agnostic infrastructure. Application wire events should use
|
|
3591
3860
|
`createRealtimeClient`; it adds the shared contract without replacing Socket.IO.
|
|
3861
|
+
Use `bindRealtimeClient` when that low-level transport already exists.
|
|
3592
3862
|
|
|
3593
3863
|
### Durable subscriptions
|
|
3594
3864
|
|
|
@@ -4068,10 +4338,11 @@ The value of each `rules` entry, keyed by scope:
|
|
|
4068
4338
|
|
|
4069
4339
|
- **`'public'`** — always passes; the identity is attached if present.
|
|
4070
4340
|
- **`'authenticated'`** — any resolved identity passes; no identity ⇒ 401.
|
|
4071
|
-
- **a function** `(identity, ctx) => boolean |
|
|
4341
|
+
- **a function** `(identity, ctx) => boolean | contribution`, sync or async — a custom
|
|
4072
4342
|
check. It receives request metadata and validated path params, so a
|
|
4073
4343
|
resource-scoped rule can do a DB lookup. It cannot read `input` or files: the
|
|
4074
|
-
body has deliberately not been consumed yet.
|
|
4344
|
+
body has deliberately not been consumed yet. `false` denies, `true` passes,
|
|
4345
|
+
and a returned plain object passes and contributes inferred handler fields.
|
|
4075
4346
|
|
|
4076
4347
|
#### Resource-scoped rule — reading a path/prefix param
|
|
4077
4348
|
|
|
@@ -4101,6 +4372,30 @@ identity *and* any derived values (role, parent-id) on `ctx` for handlers. Read
|
|
|
4101
4372
|
them back as `ctx.tenantRole` (typed `unknown` — narrow at the read site). The
|
|
4102
4373
|
endpoints under the group declare `scope: 'tenant'`.
|
|
4103
4374
|
|
|
4375
|
+
When the async authorization lookup already produced handler data, return it
|
|
4376
|
+
instead of repeating the lookup in `inject` and a handwritten scope map:
|
|
4377
|
+
|
|
4378
|
+
```ts
|
|
4379
|
+
const auth = createAuthHook({
|
|
4380
|
+
resolve: sessionResolver,
|
|
4381
|
+
rules: {
|
|
4382
|
+
project: async (user, ctx) => {
|
|
4383
|
+
const membership = await findMembership(user.id, String(ctx.projectId))
|
|
4384
|
+
return membership
|
|
4385
|
+
? { userId: user.id, projectId: membership.projectId, role: membership.role }
|
|
4386
|
+
: false
|
|
4387
|
+
},
|
|
4388
|
+
},
|
|
4389
|
+
})
|
|
4390
|
+
|
|
4391
|
+
const implementFor = createScopedImplement<AuthScopes<typeof auth>>()
|
|
4392
|
+
```
|
|
4393
|
+
|
|
4394
|
+
Contributions are merged only after full validation. Runtime-owned context keys,
|
|
4395
|
+
arrays, class instances, accessors, symbols, unsafe prototypes and partial proxy
|
|
4396
|
+
reads fail without mutating context. `false | object` contributes required
|
|
4397
|
+
fields; `true | object` correctly makes them optional.
|
|
4398
|
+
|
|
4104
4399
|
### `AuthHookConfig`
|
|
4105
4400
|
|
|
4106
4401
|
| Field | Purpose |
|
|
@@ -4116,10 +4411,10 @@ endpoints under the group declare `scope: 'tenant'`.
|
|
|
4116
4411
|
Annotate `rules` with `satisfies Record<MyScope, AuthRule<User>>` so the compiler
|
|
4117
4412
|
catches a scope you forgot to cover. With the scoped rule objects below, widen
|
|
4118
4413
|
the annotation to
|
|
4119
|
-
`satisfies Record<MyScope, AuthRule<User> | ScopedAuthRule<User,
|
|
4120
|
-
keeps the coverage check and does not disturb the derivation. (When every
|
|
4121
|
-
comes from the derived map, the check is already implicit: a scope missing
|
|
4122
|
-
`rules` is no key of the map, and a contract using it fails to compile.)
|
|
4414
|
+
`satisfies Record<MyScope, AuthRule<User> | ScopedAuthRule<User, AuthRuleContribution>>`
|
|
4415
|
+
— it keeps the coverage check and does not disturb the derivation. (When every
|
|
4416
|
+
scope comes from the derived map, the check is already implicit: a scope missing
|
|
4417
|
+
from `rules` is no key of the map, and a contract using it fails to compile.)
|
|
4123
4418
|
|
|
4124
4419
|
### Scoped rules — the map `createScopedImplement` consumes
|
|
4125
4420
|
|
|
@@ -4165,11 +4460,58 @@ widens its inject and degrades that scope's fields to `object`.
|
|
|
4165
4460
|
|
|
4166
4461
|
Derivation needs the identity generic to be **inferred** (write no explicit
|
|
4167
4462
|
`createAuthHook<User>` — TypeScript has no partial inference); with an explicit
|
|
4168
|
-
generic, or fields injected outside the hook, keep the hand-written map.
|
|
4169
|
-
hooks guarding different scopes compose by intersection:
|
|
4170
|
-
`createScopedImplement<AuthScopes<typeof userHook> & AuthScopes<typeof botHook>>()`.
|
|
4463
|
+
generic, or fields injected outside the hook, keep the hand-written map.
|
|
4171
4464
|
→ ADR 0078
|
|
4172
4465
|
|
|
4466
|
+
### Multiple auth domains — `composeAuthHooks`
|
|
4467
|
+
|
|
4468
|
+
Independent identity domains should keep separate resolvers and rules. Compose
|
|
4469
|
+
their canonical hooks and derive one handler context without a manual dispatcher
|
|
4470
|
+
or scope-map intersection:
|
|
4471
|
+
|
|
4472
|
+
```ts
|
|
4473
|
+
const userAuth = createAuthHook({
|
|
4474
|
+
resolve: resolveUser,
|
|
4475
|
+
resolveFromContext: resolveToolUser,
|
|
4476
|
+
rules: {
|
|
4477
|
+
user: (user) => ({ userId: user.id }),
|
|
4478
|
+
workspace: (user) => ({ userId: user.id }),
|
|
4479
|
+
},
|
|
4480
|
+
})
|
|
4481
|
+
|
|
4482
|
+
const workspaceAuth = createAuthHook({
|
|
4483
|
+
resolve: resolveMembership,
|
|
4484
|
+
resolveFromContext: resolveToolMembership,
|
|
4485
|
+
rules: {
|
|
4486
|
+
workspace: (membership) => ({ workspaceId: membership.workspaceId }),
|
|
4487
|
+
},
|
|
4488
|
+
})
|
|
4489
|
+
|
|
4490
|
+
export const auth = composeAuthHooks({
|
|
4491
|
+
hooks: [userAuth, workspaceAuth],
|
|
4492
|
+
defaultScope: 'user',
|
|
4493
|
+
})
|
|
4494
|
+
export const implementFor = createScopedImplement<AuthScopes<typeof auth>>()
|
|
4495
|
+
```
|
|
4496
|
+
|
|
4497
|
+
Only hooks that declare the selected scope run. An unknown scope fails closed;
|
|
4498
|
+
when several hooks own one scope, every owner must pass in declaration order.
|
|
4499
|
+
Each owner evaluates on an isolated shadow context. Stitchkit validates all
|
|
4500
|
+
changed fields, rejects reserved/unsafe fields and cross-owner key collisions,
|
|
4501
|
+
then commits the combined contribution once. A rejected or cancelled composite
|
|
4502
|
+
therefore exposes no partial fields to the handler.
|
|
4503
|
+
|
|
4504
|
+
Isolation is per-key, not deep: a contribution is the set of context keys whose
|
|
4505
|
+
descriptor changed. Mutating an existing value **in place**
|
|
4506
|
+
(`ctx.user.role = 'admin'`) edits the object every owner already shares, so it
|
|
4507
|
+
is neither reported as a contribution nor checked for collisions — contribute
|
|
4508
|
+
new fields instead of editing another owner's object. External side effects
|
|
4509
|
+
likewise remain consumer-owned and are not rolled back.
|
|
4510
|
+
|
|
4511
|
+
The composite's `defaultScope` is the only implicit scope. A child hook's own
|
|
4512
|
+
default must be absent or equal to it, so reordering hooks cannot change which
|
|
4513
|
+
scope protects an endpoint.
|
|
4514
|
+
|
|
4173
4515
|
### Auth on the tool surface — `resolveFromContext`
|
|
4174
4516
|
|
|
4175
4517
|
The same hook guards every transport, but the lifecycle slot and identity
|
|
@@ -4968,6 +5310,25 @@ observe. (This is also why it lives on `ToolCallHooks` rather than being an
|
|
|
4968
5310
|
`Response`, which a tool call has no use for, and a whole `createServer` hooks
|
|
4969
5311
|
object must stay assignable to `ToolLifecycle`.)
|
|
4970
5312
|
|
|
5313
|
+
### Compose lifecycle policy explicitly
|
|
5314
|
+
|
|
5315
|
+
When auth, a feature policy and audit identity are independent modules, compose
|
|
5316
|
+
their existing hooks instead of forwarding each phase by hand:
|
|
5317
|
+
|
|
5318
|
+
```ts
|
|
5319
|
+
import { composeLifecycleHooks } from 'stitchkit/server'
|
|
5320
|
+
import { composeToolLifecycle } from 'stitchkit/tools'
|
|
5321
|
+
|
|
5322
|
+
const httpLifecycle = composeLifecycleHooks(authLifecycle, featurePolicy, auditIdentity)
|
|
5323
|
+
const toolLifecycle = composeToolLifecycle(authHook, featureToolPolicy, auditToolIdentity)
|
|
5324
|
+
```
|
|
5325
|
+
|
|
5326
|
+
Phases run left to right. `onRequest` stops on the first `Response`; `onError`
|
|
5327
|
+
falls through until a hook handles it; after transforms feed the current value
|
|
5328
|
+
forward and preserve it when a hook returns `undefined`. Throws remain owned by
|
|
5329
|
+
the existing outer error boundary, and the original context/signal identity is
|
|
5330
|
+
never replaced.
|
|
5331
|
+
|
|
4971
5332
|
**Do not reach for `setRequestError` here.** It writes to the *request* context,
|
|
4972
5333
|
which the built-in **tool** row does not read: a tool event takes
|
|
4973
5334
|
`errorCode` / `errorMessage` / `errorDetail` from the `ToolResult`, and only
|
|
@@ -5083,6 +5444,101 @@ contract registry. Both helpers are Fetch-only: they construct ordinary
|
|
|
5083
5444
|
absolute `Request` objects and call `createHandler` directly, so Bun and Node
|
|
5084
5445
|
exercise the same framework pipeline.
|
|
5085
5446
|
|
|
5447
|
+
### Transport conformance
|
|
5448
|
+
|
|
5449
|
+
`buildSurfaceManifest` v2 snapshots canonical operations separately from actual
|
|
5450
|
+
HTTP topology and mounted MCP/Agent/CLI projections. Named MCP surfaces keep
|
|
5451
|
+
their own advertised input digests (including `extend`, flattening and schema
|
|
5452
|
+
policy), and named realtime contracts keep directional argument/ack input and
|
|
5453
|
+
output digests. Compare only discovery you actually observed, then run explicit
|
|
5454
|
+
drivers for the transports you provide:
|
|
5455
|
+
|
|
5456
|
+
```ts
|
|
5457
|
+
import { bindRealtimeClient } from 'stitchkit'
|
|
5458
|
+
import {
|
|
5459
|
+
assertSurfaceDiscovery,
|
|
5460
|
+
buildSurfaceManifest,
|
|
5461
|
+
createRealtimeProbeDriver,
|
|
5462
|
+
defineRealtimeProbe,
|
|
5463
|
+
runSurfaceProbes,
|
|
5464
|
+
} from 'stitchkit/testing'
|
|
5465
|
+
|
|
5466
|
+
const manifest = buildSurfaceManifest({
|
|
5467
|
+
groups: [{ pathPrefix: '/api', services: httpServices }],
|
|
5468
|
+
mcpPreparation: { extend, schemaValidation, multiRound },
|
|
5469
|
+
mcpSurfaces: {
|
|
5470
|
+
member: { services: memberServices, runtimeTools },
|
|
5471
|
+
admin: { services: adminServices, runtimeTools },
|
|
5472
|
+
},
|
|
5473
|
+
toolSurfaces: {
|
|
5474
|
+
AGENT: { services: agentServices, runtimeTools },
|
|
5475
|
+
CLI: { services: cliServices, runtimeTools: cliRuntimeTools },
|
|
5476
|
+
},
|
|
5477
|
+
realtime: { primary: realtimeContract },
|
|
5478
|
+
cliCommands,
|
|
5479
|
+
})
|
|
5480
|
+
assertSurfaceDiscovery(manifest, {
|
|
5481
|
+
openApi,
|
|
5482
|
+
toolSurfaces: [{
|
|
5483
|
+
transport: 'MCP',
|
|
5484
|
+
surface: 'member',
|
|
5485
|
+
names: (await mcpClient.listTools()).tools.map((tool) => tool.name),
|
|
5486
|
+
}],
|
|
5487
|
+
AGENT: Object.keys(agentTools),
|
|
5488
|
+
CLI: cliHelpNames,
|
|
5489
|
+
realtime: {
|
|
5490
|
+
primary: { serverToClient: observedServerEvents, clientToServer: observedClientEvents },
|
|
5491
|
+
},
|
|
5492
|
+
})
|
|
5493
|
+
|
|
5494
|
+
const invalidInbound = defineRealtimeProbe({
|
|
5495
|
+
name: 'invalid inbound payload',
|
|
5496
|
+
scenario: 'invalid_arguments',
|
|
5497
|
+
fixture: invalidPayloadFixture,
|
|
5498
|
+
expected: {
|
|
5499
|
+
outcome: 'realtime_rejected',
|
|
5500
|
+
code: 'REALTIME_CONTRACT_VIOLATION',
|
|
5501
|
+
rejection: {
|
|
5502
|
+
direction: 'server-inbound', phase: 'arguments',
|
|
5503
|
+
reason: 'invalid-arguments', fault: 'peer',
|
|
5504
|
+
},
|
|
5505
|
+
handlerCalls: 0,
|
|
5506
|
+
},
|
|
5507
|
+
})
|
|
5508
|
+
|
|
5509
|
+
const realtimeDriver = createRealtimeProbeDriver({
|
|
5510
|
+
bind: (onRejected, fixture) => {
|
|
5511
|
+
const client = bindRealtimeClient(realtimeContract, existingTransport, { onRejected })
|
|
5512
|
+
const scenario = bindApplicationRealtimeScenario(client, fixture)
|
|
5513
|
+
return {
|
|
5514
|
+
connected: () => client.connected,
|
|
5515
|
+
invoke: scenario.invoke,
|
|
5516
|
+
dispose: scenario.dispose, // subscriptions only; never disconnect the transport
|
|
5517
|
+
}
|
|
5518
|
+
},
|
|
5519
|
+
handlerCalls: () => applicationRealtimeHandlerCalls,
|
|
5520
|
+
})
|
|
5521
|
+
|
|
5522
|
+
await runSurfaceProbes({
|
|
5523
|
+
probes: [invalidInbound],
|
|
5524
|
+
drivers: { REALTIME: realtimeDriver },
|
|
5525
|
+
})
|
|
5526
|
+
```
|
|
5527
|
+
|
|
5528
|
+
MCP preparation is global because the real MCP mount has one preparation
|
|
5529
|
+
policy; named surfaces select tools, while `extend.filter` decides which
|
|
5530
|
+
selected operations receive extra fields. CLI selection is deliberately plain,
|
|
5531
|
+
and Agent owns its own reachable presentation shaping.
|
|
5532
|
+
|
|
5533
|
+
The kit never starts a server, discovers Socket.IO topology remotely, invents
|
|
5534
|
+
credentials or synthesises invalid Zod values. Fixtures and observations come
|
|
5535
|
+
from the application. The realtime driver creates a rejection channel per
|
|
5536
|
+
scenario, observes connection state before invocation, and disposes only
|
|
5537
|
+
probe-owned subscriptions. Each scenario has one absolute deadline shared by
|
|
5538
|
+
signalled setup, invocation and teardown. An outer timeout stops waiting; it
|
|
5539
|
+
does not disconnect a foreign transport or retract an already emitted packet.
|
|
5540
|
+
A missing driver is unsupported, not silently marked conformant.
|
|
5541
|
+
|
|
5086
5542
|
### Test handlers with raw Requests
|
|
5087
5543
|
|
|
5088
5544
|
`createHandler` is the router as a plain `(req) => Promise<Response>` function —
|
|
@@ -5642,6 +6098,133 @@ current one *up to* your target, and apply each snippet.
|
|
|
5642
6098
|
runtime): bootstrap the server, one HTTP request, and any feature you rely on
|
|
5643
6099
|
(Socket.IO connect, an MCP tool call, a multipart upload, …).
|
|
5644
6100
|
|
|
6101
|
+
## Released migration: 0.56.0
|
|
6102
|
+
|
|
6103
|
+
### Surface manifests are version 2
|
|
6104
|
+
|
|
6105
|
+
`operation.tools` could not describe a role-selected MCP surface, a different
|
|
6106
|
+
Agent or CLI selection, or an advertised `extend` schema, so projections moved
|
|
6107
|
+
out of the canonical operation:
|
|
6108
|
+
|
|
6109
|
+
```ts
|
|
6110
|
+
// before
|
|
6111
|
+
manifest.operations[0].tools.MCP
|
|
6112
|
+
// after
|
|
6113
|
+
manifest.toolSurfaces.find((s) => s.transport === 'MCP' && s.surface === null)?.tools
|
|
6114
|
+
|
|
6115
|
+
// before
|
|
6116
|
+
buildSurfaceManifest({ mcpSurfaces: { admin: { services, extend } } })
|
|
6117
|
+
// after
|
|
6118
|
+
buildSurfaceManifest({ mcpSurfaces: { admin: { services } }, mcpPreparation: { extend } })
|
|
6119
|
+
```
|
|
6120
|
+
|
|
6121
|
+
A committed snapshot is regenerated once, deliberately: `manifestVersion` is
|
|
6122
|
+
`2`, and `ConformanceTransport` gained `REALTIME`, so any exhaustive
|
|
6123
|
+
`Record<ConformanceTransport, …>` must handle it.
|
|
6124
|
+
|
|
6125
|
+
### `FILE_*` codes joined the error registry
|
|
6126
|
+
|
|
6127
|
+
`StitchErrorCode` gained `FILE_INVALID_PATH`, `FILE_OUTSIDE_ROOT`,
|
|
6128
|
+
`FILE_NOT_FOUND`, `FILE_NOT_REGULAR`, `FILE_INSPECTION_REJECTED`,
|
|
6129
|
+
`FILE_TOO_LARGE` and `FILE_EXISTS`. Only exhaustive maps break:
|
|
6130
|
+
|
|
6131
|
+
```ts
|
|
6132
|
+
// before — compiled while the registry had no file codes
|
|
6133
|
+
const copy: Record<StitchErrorCode, string> = { …, RATE_LIMITED: '…' }
|
|
6134
|
+
// after — add the seven managed-file codes (or use Partial<Record<…>>)
|
|
6135
|
+
```
|
|
6136
|
+
|
|
6137
|
+
Unexpected IO stays scrubbed as `INTERNAL_SERVER_ERROR`: the new codes are the
|
|
6138
|
+
caller-safe ones only.
|
|
6139
|
+
|
|
6140
|
+
### `ScopedAuthHook` is nominal
|
|
6141
|
+
|
|
6142
|
+
A hand-written function shaped like an auth hook is no longer assignable —
|
|
6143
|
+
identity now comes from the factory, so scope ownership and the inferred
|
|
6144
|
+
context cannot drift apart:
|
|
6145
|
+
|
|
6146
|
+
```ts
|
|
6147
|
+
// before — a structural stand-in
|
|
6148
|
+
const auth: ScopedAuthHook<Scopes> = async (ctx, endpoint) => { … }
|
|
6149
|
+
// after — create it, then compose domains
|
|
6150
|
+
const auth = createAuthHook({ resolve, rules })
|
|
6151
|
+
const composed = composeAuthHooks({ hooks: [auth, billingAuth], defaultScope: 'public' })
|
|
6152
|
+
```
|
|
6153
|
+
|
|
6154
|
+
### Managed-file inspectors also run on reads
|
|
6155
|
+
|
|
6156
|
+
An inspector is no longer write-only, and it has a finite default deadline
|
|
6157
|
+
(15 s). Make it read-aware and idempotent — a read carries no
|
|
6158
|
+
`declaredMediaType`:
|
|
6159
|
+
|
|
6160
|
+
```ts
|
|
6161
|
+
// before
|
|
6162
|
+
inspect: ({ declaredMediaType }) => inspectDeclaredType(declaredMediaType!)
|
|
6163
|
+
// after
|
|
6164
|
+
inspect: ({ prefix, declaredMediaType, signal }) =>
|
|
6165
|
+
inspectBytes(prefix, { declaredMediaType, signal })
|
|
6166
|
+
```
|
|
6167
|
+
|
|
6168
|
+
Set `inspectionTimeoutMs` explicitly when 15 seconds is the wrong budget.
|
|
6169
|
+
|
|
6170
|
+
### Direct async-operation binding needs a wire-stable ID
|
|
6171
|
+
|
|
6172
|
+
A direct binding reuses the start output as the follow-up wire input, so the ID
|
|
6173
|
+
schema must parse to itself (`z.input` equals `z.output`, no transform,
|
|
6174
|
+
coercion, default or overwrite). Anything else is parsed twice:
|
|
6175
|
+
|
|
6176
|
+
```ts
|
|
6177
|
+
// before — a transform silently ran on start and again on every follow-up
|
|
6178
|
+
defineAsyncOperationContract({ binding: 'direct', id: z.string().transform(Number) })
|
|
6179
|
+
// after — keep the wire shape, adapt explicitly
|
|
6180
|
+
defineAsyncOperationContract({
|
|
6181
|
+
binding: 'adapted',
|
|
6182
|
+
id,
|
|
6183
|
+
adapters: { idFromStart, inputFor },
|
|
6184
|
+
})
|
|
6185
|
+
```
|
|
6186
|
+
|
|
6187
|
+
## Released migration: 0.55.0
|
|
6188
|
+
|
|
6189
|
+
### Peer-free `implementRemote`
|
|
6190
|
+
|
|
6191
|
+
`implementRemote` now has one canonical, optional-peer-free owner. This keeps
|
|
6192
|
+
MCP SDK and AI SDK modules out of CLI bundles that only proxy HTTP calls:
|
|
6193
|
+
|
|
6194
|
+
```ts
|
|
6195
|
+
// before
|
|
6196
|
+
import { implementRemote } from 'stitchkit/tools'
|
|
6197
|
+
// after
|
|
6198
|
+
import { implementRemote } from 'stitchkit/remote'
|
|
6199
|
+
```
|
|
6200
|
+
|
|
6201
|
+
### Managed file boundary and strict auth returns
|
|
6202
|
+
|
|
6203
|
+
Create one boundary during application bootstrap and pass the capability, never
|
|
6204
|
+
a per-call directory or host path:
|
|
6205
|
+
|
|
6206
|
+
```ts
|
|
6207
|
+
import { createManagedFileBoundary } from 'stitchkit/files'
|
|
6208
|
+
|
|
6209
|
+
const files = await createManagedFileBoundary({ root: '/srv/app-files' })
|
|
6210
|
+
|
|
6211
|
+
// before
|
|
6212
|
+
defineDownloadTool({ defaultDir: '/srv/app-files', resolveUrl, ...common })
|
|
6213
|
+
defineUploadTool({ upload: (path) => provider.uploadFile(path), ...common })
|
|
6214
|
+
defineViewFileTool({ baseDir: '/srv/app-files', ...common })
|
|
6215
|
+
|
|
6216
|
+
// after
|
|
6217
|
+
defineDownloadTool({ files, resolveUrl, ...common })
|
|
6218
|
+
defineUploadTool({ files, upload: ({ bytes }) => provider.upload(bytes), ...common })
|
|
6219
|
+
defineViewFileTool({ files, ...common })
|
|
6220
|
+
```
|
|
6221
|
+
|
|
6222
|
+
Downloaded `path` is now relative to the boundary and MIME metadata is named
|
|
6223
|
+
`mediaType`. Update raw `mountDownload`/`mountUpload`/`mountViewFile` configs the
|
|
6224
|
+
same way. Auth predicates must explicitly return `true`, `false`, or a plain
|
|
6225
|
+
object of context fields; replace accidental `undefined` fallthroughs with the
|
|
6226
|
+
intended boolean.
|
|
6227
|
+
|
|
5645
6228
|
## Released migration: 0.53.0
|
|
5646
6229
|
|
|
5647
6230
|
### Realtime `emit` returns `boolean` instead of `void`
|
|
@@ -6609,13 +7192,24 @@ The browser-and-server entrypoint. Re-exports everything from
|
|
|
6609
7192
|
| `createSocketIOClient` | function | low-level typed Socket.IO transport primitive — [guide](../guide/realtime.md#low-level-transport) |
|
|
6610
7193
|
| `defineRealtimeContract` | function | Zod-first shared Socket.IO event contract — [guide](../guide/realtime.md#zod-first-event-contract) |
|
|
6611
7194
|
| `createRealtimeClient` | function | inferred, runtime-validated Socket.IO client — [guide](../guide/realtime.md#client--createrealtimeclient) |
|
|
7195
|
+
| `bindRealtimeClient` | function | bind contract validation and typed acknowledgements to an existing Stitchkit client transport without owning its lifecycle |
|
|
6612
7196
|
| `createRetainedTopics` | function | retained last-value store for sticky events — [guide](../guide/realtime.md#sticky-events) |
|
|
6613
7197
|
| `parseSSE` | function | parse an SSE `Response` into an async generator — [guide](../guide/client.md#sse) |
|
|
6614
|
-
| `SocketIOClient` | _type_ |
|
|
7198
|
+
| `SocketIOClient` | _type_ | low-level client handle; `emit` reports disconnected drops and `emitWithAck` exposes the native Promise primitive used by validated `request()` |
|
|
6615
7199
|
| `SocketIOClientConfig` | _type_ | config for `createSocketIOClient` (incl. `retain`, `onConnectError`, `onDroppedEmit`) |
|
|
6616
7200
|
| `SocketEventMap` | _type_ | the shape of an event map |
|
|
6617
7201
|
| `RealtimeClient` | _type_ | validated client inferred from a realtime contract |
|
|
6618
7202
|
| `RealtimeClientOptions` | _type_ | transport options and the rejected-event hook for `createRealtimeClient` |
|
|
7203
|
+
| `BoundRealtimeClient` | _type_ | validated non-owning `on`/`emit`/`request` client with no `connect`/`disconnect` |
|
|
7204
|
+
| `RealtimeClientTransport` | _type_ | minimal existing transport capability accepted by `bindRealtimeClient` |
|
|
7205
|
+
| `BindRealtimeClientOptions` | _type_ | rejection/logger options for a bound existing transport |
|
|
7206
|
+
| `RealtimeAcknowledgedEvent` | _type_ | event-name union restricted to definitions with an `ack` schema |
|
|
7207
|
+
| `RealtimeAcknowledgement` | _type_ | validated acknowledgement output inferred from an event definition |
|
|
7208
|
+
| `RealtimeRequestArguments` | _type_ | request arguments inferred from an acknowledged event tuple |
|
|
7209
|
+
| `RealtimeRequestOptions` | _type_ | finite positive native acknowledgement `timeoutMs` |
|
|
7210
|
+
| `RealtimeRequestTimeoutError` | class | stable `REALTIME_REQUEST_TIMEOUT` rejection |
|
|
7211
|
+
| `RealtimeRequestDisconnectedError` | class | stable `REALTIME_REQUEST_DISCONNECTED` rejection, including an immediate disconnected call |
|
|
7212
|
+
| `RealtimeRequestInvalidAcknowledgementError` | class | invalid ack was reported through `onRejected` and the request rejected |
|
|
6619
7213
|
| `RealtimeContract` | _type_ | shared server-to-client and client-to-server event registries |
|
|
6620
7214
|
| `RealtimeEventRegistry` | _type_ | string-keyed registry of event definitions |
|
|
6621
7215
|
| `RealtimeEventDefinition` | _type_ | one tuple-shaped event and optional acknowledgement schema |
|
|
@@ -6796,6 +7390,7 @@ Also re-exports the error helpers from `stitchkit/contract`.
|
|
|
6796
7390
|
| `OperationIdentity` | _type_ | path-free service/action/scope/method identity shared by contract and native tool operations |
|
|
6797
7391
|
| `Handlers` | _type_ | the typed handler map `implement` expects |
|
|
6798
7392
|
| `LifecycleHooks` | _type_ | `onRequest` / pre-body `authorize` / `beforeHandle` / `afterHandle` / `onError` |
|
|
7393
|
+
| `composeLifecycleHooks` | function | compose HTTP lifecycle phases in declaration order with short-circuit/fallthrough semantics |
|
|
6799
7394
|
| `AuthorizationContext` | _type_ | HTTP pre-body context with validated params, `input: undefined` and no files |
|
|
6800
7395
|
| `RouteGroup` | _type_ | a prefixed group of services with its own hooks |
|
|
6801
7396
|
| `RawRoute` | _type_ | a non-contract `Request → Response` route with a concrete `BunServer` context |
|
|
@@ -6814,6 +7409,7 @@ Also re-exports the error helpers from `stitchkit/contract`.
|
|
|
6814
7409
|
| Export | Kind | Summary |
|
|
6815
7410
|
|--------|------|---------|
|
|
6816
7411
|
| `createAuthHook` | function | one scope gate for HTTP `authorize` and tool `beforeHandle` — [guide](../guide/auth-and-errors.md#createauthhook) |
|
|
7412
|
+
| `composeAuthHooks` | function | route multiple canonical auth domains by owned scope and atomically commit their typed contributions |
|
|
6817
7413
|
| `createErrorHook` | function | an async-capable, endpoint-aware `onError` hook from a code map + envelope renderer — [guide](../guide/auth-and-errors.md#createerrorhook) |
|
|
6818
7414
|
| `ErrorHookConfig` | _type_ | async observer/renderer config for `createErrorHook` |
|
|
6819
7415
|
| `ResolvedError` | _type_ | the normalised error handed to `createErrorHook`'s `render` |
|
|
@@ -6827,11 +7423,14 @@ Also re-exports the error helpers from `stitchkit/contract`.
|
|
|
6827
7423
|
| `AuthHook` | _type_ | the hook `createAuthHook` returns |
|
|
6828
7424
|
| `AuthHookConfig` | _type_ | config for `createAuthHook` |
|
|
6829
7425
|
| `AuthRule` | _type_ | `'public' \| 'authenticated' \| predicate` |
|
|
7426
|
+
| `AuthRuleContribution` | _type_ | plain context fields a sync/async rule may contribute after authorizing |
|
|
6830
7427
|
| `ScopedAuthRule` | _type_ | a rule plus its typed context contribution — `{ rule, inject? }` |
|
|
6831
7428
|
| `AuthRules` | _type_ | the `rules` map: bare rules or scoped rules |
|
|
6832
7429
|
| `RuleScopes` | _type_ | scope→context map derived from a `rules` object; `'public'` fields become optional |
|
|
6833
7430
|
| `ScopedAuthHook` | _type_ | an auth hook carrying its derived scope map at the type level |
|
|
6834
7431
|
| `AuthScopes` | _type_ | recover the derived map — `createScopedImplement<AuthScopes<typeof hook>>()` |
|
|
7432
|
+
| `ComposeAuthHooksConfig` | _type_ | ordered canonical hooks plus the optional explicit composite default scope |
|
|
7433
|
+
| `ComposedAuthScopes` | _type_ | scope→context map derived from every owner in a composed auth hook |
|
|
6835
7434
|
| `BearerResolverConfig` | _type_ | config for `createBearerResolver` |
|
|
6836
7435
|
| `JwtPayload` | _type_ | a decoded JWT payload |
|
|
6837
7436
|
| `SignJwtOptions` | _type_ | options for `signJwt` (expiry, claims) |
|
|
@@ -6995,6 +7594,18 @@ audit event. See the [Observability guide](../guide/observability.md).
|
|
|
6995
7594
|
|
|
6996
7595
|
---
|
|
6997
7596
|
|
|
7597
|
+
## `stitchkit/remote`
|
|
7598
|
+
|
|
7599
|
+
Peer-free remote implementation boundary. Importing it does not load the MCP
|
|
7600
|
+
SDK, `ai`, or other optional tool peers, so it is safe in a thin CLI bundle.
|
|
7601
|
+
|
|
7602
|
+
| Export | Kind | Summary |
|
|
7603
|
+
|--------|------|---------|
|
|
7604
|
+
| `implementRemote` | function | bind a contract to a remote HTTP API — [guide](../guide/mcp-and-agents.md#proxying-a-remote-api--implementremote) |
|
|
7605
|
+
| `ImplementRemoteOptions` | _type_ | optional argument-rewrite hook for `implementRemote` |
|
|
7606
|
+
|
|
7607
|
+
---
|
|
7608
|
+
|
|
6998
7609
|
## `stitchkit/tools`
|
|
6999
7610
|
|
|
7000
7611
|
Server-only. Turns contracts into MCP and AI-agent tools. MCP server surfaces
|
|
@@ -7014,7 +7625,6 @@ payload.
|
|
|
7014
7625
|
| `bindStdioProcessSignals` | function | explicitly bind OS signals to one close-only stdio handle — [guide](../guide/testing-and-deployment.md#stdio-process-signals) |
|
|
7015
7626
|
| `buildMcpServer` | function | build an `McpServer` from contract/runtime surfaces; no-auth configs omit the second argument |
|
|
7016
7627
|
| `mountMcp` | function | add contract tools to an existing `McpServer` — [guide](../guide/mcp-and-agents.md#mountmcp) |
|
|
7017
|
-
| `implementRemote` | function | bind a contract to a remote HTTP API — [guide](../guide/mcp-and-agents.md#proxying-a-remote-api--implementremote) |
|
|
7018
7628
|
| `mountAgent` | function | a Vercel AI SDK `ToolSet` from a service — [guide](../guide/mcp-and-agents.md#ai-agents--mountagent) |
|
|
7019
7629
|
| `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) |
|
|
7020
7630
|
| `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) |
|
|
@@ -7046,7 +7656,6 @@ payload.
|
|
|
7046
7656
|
| `McpSurfaceDefinition` | _type_ | one immutable `{ services, runtimeTools }` MCP surface |
|
|
7047
7657
|
| `McpSurfaceRegistry` | _type_ | finite keyed surface registry for eager preparation |
|
|
7048
7658
|
| `StdioAuthConfig` | _type_ | startup identity composed into `StdioMcpServerConfig` |
|
|
7049
|
-
| `ImplementRemoteOptions` | _type_ | options for `implementRemote` |
|
|
7050
7659
|
| `McpMountConfig` | _type_ | config for `mountMcp` |
|
|
7051
7660
|
| `McpSchemaValidationConfig` | _type_ | shared `{ policy, requireTypedProperties, allowUntyped, requirePortableFormats, allowFormats }` profile |
|
|
7052
7661
|
| `ValidateMcpSchemasConfig` | _type_ | standalone validation profile plus `services`, `extend`, flattening and logger |
|
|
@@ -7102,7 +7711,7 @@ payload.
|
|
|
7102
7711
|
| `ToolInvocationOptions` | _type_ | per-call source, context, lifecycle, hooks and output-strip reporter |
|
|
7103
7712
|
| `ToolInvokerTransport` | _type_ | invoker exposure policy: `MCP \| AGENT \| CLI` |
|
|
7104
7713
|
| `ToolCallContext` | _type_ | the context every tool hook receives — `{ source, mcp? }` plus whatever the mount's `context` added |
|
|
7105
|
-
| `ViewFileOptions` | _type_ | shared URL/
|
|
7714
|
+
| `ViewFileOptions` | _type_ | shared URL/managed-file-boundary policy for `defineViewFileTool`, `mountViewFile` and `resolveMedia` |
|
|
7106
7715
|
| `ViewFileOutput` | _type_ | neutral managed batch result with multimodal `content` and per-item `errors` |
|
|
7107
7716
|
| `ViewFileInputSchema` | constant | fixed one-or-many media path/URL input schema |
|
|
7108
7717
|
| `ViewFileOutputSchema` | constant | Zod schema for the neutral managed view-file batch result |
|
|
@@ -7138,9 +7747,35 @@ runtime-tool runner, plus deliberate raw MCP adapters over the same mechanics.
|
|
|
7138
7747
|
| `ManagedNativeToolConfig` | _type_ | shared name, description, identity, exposure and annotations for managed native factories |
|
|
7139
7748
|
| `NativeToolIdentity` | _type_ | pathless service/action/scope/meta identity; semantic method is factory-owned |
|
|
7140
7749
|
| `ManagedWaitRender` | _type_ | optional managed wait terminal text and failure classification |
|
|
7141
|
-
| `DownloadResultSchema` | constant | Zod schema for a saved download's path, byte size and MIME type |
|
|
7142
|
-
| `DownloadResult` | _type_ | validated output of `defineDownloadTool` |
|
|
7143
7750
|
| `UploadToolInputSchema` | constant | fixed `{ path: string }` input schema for `defineUploadTool` |
|
|
7751
|
+
| `defineAsyncOperation` | function | runtime-only start/status/wait plus configured cancel/result/artifacts definitions |
|
|
7752
|
+
| `defineAsyncOperationContract` | function | define one canonical Zod-first HTTP contract for start/status/wait plus optional capabilities |
|
|
7753
|
+
| `bindContractAsyncOperation` | function | bind literal methods from an existing contract without creating another HTTP surface |
|
|
7754
|
+
| `createAsyncOperationSnapshotSchema` | function | canonical pending/running/succeeded/failed/cancelled Zod snapshot |
|
|
7755
|
+
| `AsyncOperationCancelResultSchema` | constant | validated accepted/already_terminal/rejected cancellation result |
|
|
7756
|
+
| `AsyncOperationCancelResult` | _type_ | validated cancel capability result |
|
|
7757
|
+
| `AsyncOperationCancelCapability` | _type_ | optional typed domain cancellation callback |
|
|
7758
|
+
| `AsyncOperationCapability` | _type_ | generated capability-name union |
|
|
7759
|
+
| `AsyncOperationStartDefinition` / `AsyncOperationFollowDefinition` | _type_ | generated runtime definition types |
|
|
7760
|
+
| `AsyncOperationIdentity` | _type_ | shared service/action/scope/meta identity for an operation |
|
|
7761
|
+
| `AsyncOperationOutputCapability` | _type_ | optional result/artifact schema plus handler |
|
|
7762
|
+
| `RuntimeAsyncOperationConfig` | _type_ | runtime-only descriptor configuration |
|
|
7763
|
+
| `RuntimeAsyncOperation` | _type_ | inferred generated definitions and schemas |
|
|
7764
|
+
| `AsyncOperationContractConfig` | _type_ | canonical contract config where start returns the operation id |
|
|
7765
|
+
| `AsyncOperationContractWithStartOutputConfig` | _type_ | canonical contract config with an application start envelope and typed id extractor |
|
|
7766
|
+
| `DefinedAsyncOperationContract` | _type_ | generated contract, capability keys, schemas and parsed adapters |
|
|
7767
|
+
| `ContractAsyncOperationConfig` | _type_ | literal contract method binding and handlers |
|
|
7768
|
+
| `AdaptedContractAsyncOperationConfig` | _type_ | existing-contract binding with explicit id and per-capability input adapters |
|
|
7769
|
+
| `BoundAdaptedContractAsyncOperation` | _type_ | inferred adapted binding with parsed id/input projectors |
|
|
7770
|
+
| `AdaptedContractAsyncOperationStartKey` | _type_ | existing-contract keys with a declared start output |
|
|
7771
|
+
| `AdaptedContractAsyncOperationFollowKey` | _type_ | existing-contract keys with both input and output schemas |
|
|
7772
|
+
| `AdaptedContractAsyncOperationWaitKey` | _type_ | adapted follow-up keys whose output matches the selected status snapshot |
|
|
7773
|
+
| `ContractAsyncOperationInputAdapters` | _type_ | guaranteed direct-binding status/wait input adapters plus configured optional capabilities |
|
|
7774
|
+
| `ContractAsyncOperationKeys` | _type_ | literal method-key union of a bound contract |
|
|
7775
|
+
| `ContractAsyncOperationStartKey` | _type_ | direct-binding start keys whose declared ID schema has stable input/output |
|
|
7776
|
+
| `ContractAsyncOperationFollowKey` | _type_ | contract keys whose input schema type matches the selected start output |
|
|
7777
|
+
| `ContractAsyncOperationWaitKey` | _type_ | follow-up keys whose output schema type also matches the selected status output |
|
|
7778
|
+
| `composeToolLifecycle` | function | ordered composition of tool before/after phases |
|
|
7144
7779
|
| `mountDownload` | function | raw MCP "download a URL to disk" adapter (SSRF-guarded, size-capped) |
|
|
7145
7780
|
| `mountUpload` | function | raw MCP "upload a local file" adapter |
|
|
7146
7781
|
| `mountWait` | function | raw MCP generic `--wait`-style polling adapter |
|
|
@@ -7220,6 +7855,64 @@ handler pipeline without opening a TCP port.
|
|
|
7220
7855
|
| `HandlerTestClientConfig` | _type_ | handler, contract, path prefix, scoped config and client request defaults |
|
|
7221
7856
|
| `HandlerTestClientsConfig` | _type_ | batch helper configuration |
|
|
7222
7857
|
| `HandlerTestTransportConfig` | _type_ | shared in-process handler, origin, prefix, client defaults and optional server handle |
|
|
7858
|
+
| `buildSurfaceManifest` | function | deterministic manifest v2 with HTTP topology, transport-specific tool projections, named MCP surfaces and realtime schema digests |
|
|
7859
|
+
| `assertSurfaceManifestSnapshot` | function | bounded deterministic manifest drift assertion |
|
|
7860
|
+
| `assertSurfaceDiscovery` | function | compare real OpenAPI/tool/realtime caller-observed discovery to the exact manifest projection |
|
|
7861
|
+
| `runSurfaceProbes` | function | execute explicit transport drivers under one per-scenario setup/invoke/teardown deadline |
|
|
7862
|
+
| `defineRealtimeProbe` | function | declare one canonical realtime scenario for a caller-supplied real transport driver |
|
|
7863
|
+
| `createRealtimeProbeDriver` | function | bind each scenario to a caller-owned transport and normalize actual event/ack/rejection/disconnect outcomes without lifecycle ownership |
|
|
7864
|
+
| `TransportObservationSchema` | constant | normalized HTTP/tool/realtime outcome including rejection fields and handler-call evidence |
|
|
7865
|
+
| `RealtimeRejectionObservationSchema` | constant | direction/phase/reason/fault observation for a rejected realtime event |
|
|
7866
|
+
| `RealtimeDisconnectObservationSchema` | constant | observed before-invoke versus in-flight disconnect phase |
|
|
7867
|
+
| `ConformanceTransport` | _type_ | explicit probe transport union |
|
|
7868
|
+
| `RunSurfaceProbesConfig` | _type_ | probes, drivers and diagnostic byte cap |
|
|
7869
|
+
| `SurfaceDiscoveryObservation` | _type_ | real OpenAPI/tool/CLI/extension discovery values |
|
|
7870
|
+
| `SurfaceToolDiscoveryObservation` | _type_ | transport, optional named surface and caller-observed tool names |
|
|
7871
|
+
| `SurfaceRealtimeDiscoveryObservation` | _type_ | caller-observed server→client and client→server event names |
|
|
7872
|
+
| `SurfaceProbe` / `SurfaceProbeDriver` | _type_ | one bounded scenario and its consumer-supplied runner |
|
|
7873
|
+
| `DefineRealtimeProbeConfig` | _type_ | name, canonical scenario, explicit fixture and expected realtime outcome |
|
|
7874
|
+
| `CreateRealtimeProbeDriverConfig` | _type_ | per-scenario foreign-transport binder and optional handler-call counter |
|
|
7875
|
+
| `RealtimeProbeAdapter` | _type_ | connected-state observation, scenario invocation and subscription-only cleanup |
|
|
7876
|
+
| `RealtimeProbeFixture` / `RealtimeProbeScenario` | _type_ | driver input and supported event/ack/invalid/disconnect/timeout scenario vocabulary |
|
|
7877
|
+
| `RealtimeRejectionObservation` | _type_ | parsed structured realtime rejection observation |
|
|
7878
|
+
| `RealtimeDisconnectObservation` | _type_ | normalized physical timing of a realtime disconnect |
|
|
7879
|
+
| `TransportObservation` | _type_ | validated normalized driver result |
|
|
7880
|
+
| `SurfaceManifest` / `SurfaceManifestConfig` | _type_ | deterministic surface snapshot and its inputs |
|
|
7881
|
+
| `SurfaceManifestOperation` / `SurfaceManifestOperationSchema` | _type_ / schema | one contract or runtime operation row |
|
|
7882
|
+
| `SurfaceManifestTool` / `SurfaceManifestToolSchema` | _type_ / schema | one mounted tool row with advertised input digest |
|
|
7883
|
+
| `SurfaceManifestToolSurface` / `SurfaceManifestToolSurfaceSchema` | _type_ / schema | one static transport projection, optionally keyed for a finite MCP surface |
|
|
7884
|
+
| `SurfaceManifestRealtimeEvent` / `SurfaceManifestRealtimeEventSchema` | _type_ / schema | one named directional realtime event with input/output args and ack digests |
|
|
7885
|
+
| `SurfaceManifestExtension` / `SurfaceManifestExtensionSchema` | _type_ / schema | declared transport extension row |
|
|
7886
|
+
| `SurfaceManifestSchema` | schema | complete deterministic manifest schema |
|
|
7887
|
+
| `SurfaceToolDefinition` | _type_ | plain service/runtime selection used by CLI and finite named MCP surfaces |
|
|
7888
|
+
| `SurfaceAgentProjection` | _type_ | Agent selection plus its reachable extend/flatten presentation policy |
|
|
7889
|
+
| `SurfaceMcpPreparation` | _type_ | one global MCP extend/flatten/schema-validation/multi-round preparation policy |
|
|
7890
|
+
| `SurfaceRuntimeToolDefinition` | _type_ | peer-free non-executable runtime-operation descriptor for manifest projection |
|
|
7891
|
+
| `SurfaceToolExtension` | _type_ | canonical structural tool extension including schema, resolver and optional filter |
|
|
7892
|
+
| `McpSchemaValidationConfig` / `IncompatibleSchemaPolicy` | _type_ | canonical MCP schema-preparation policy used by real mounts and manifests |
|
|
7893
|
+
| `SurfaceRealtimeSchemaPairSchema` | schema | input/output digest pair for args or acknowledgements |
|
|
7894
|
+
| `SurfaceSchemaDigestsSchema` | schema | params/input/output/multipart digest object |
|
|
7895
|
+
| `serializeSurfaceValue` | function | canonical versioned serialization used for deterministic digests |
|
|
7896
|
+
|
|
7897
|
+
---
|
|
7898
|
+
|
|
7899
|
+
## `stitchkit/files`
|
|
7900
|
+
|
|
7901
|
+
Peer-free Bun/Node filesystem capability. Browser/contract-safe refs are also
|
|
7902
|
+
available from `stitchkit/contract`.
|
|
7903
|
+
|
|
7904
|
+
| Export | Kind | Summary |
|
|
7905
|
+
|--------|------|---------|
|
|
7906
|
+
| `createManagedFileBoundary` | function | bind an application-owned root, optionally creating one final directory under a trusted existing parent |
|
|
7907
|
+
| `ManagedFileBoundary` | _type_ | non-reopenable `read`/`write` capability over canonical relative paths |
|
|
7908
|
+
| `ManagedFileBoundaryConfig` | _type_ | bound root, optional `createRoot`, finite read/write/inspection limits, inspector and cleanup observer |
|
|
7909
|
+
| `ManagedFileRefSchema` / `ManagedFileRef` | schema / _type_ | transport-safe relative path, measured size and optional media metadata |
|
|
7910
|
+
| `ManagedFilePathSchema` / `ManagedFilePath` | schema / _type_ | canonical POSIX relative managed-file path |
|
|
7911
|
+
| `ManagedFileSource` | _type_ | bounded immutable bytes plus validated ref passed to upload callbacks |
|
|
7912
|
+
| `ManagedFileReadOptions` / `ManagedFileWriteOptions` | _type_ | per-operation byte cap, signal and atomic write policy |
|
|
7913
|
+
| `ManagedFileError` / `ManagedFileErrorCode` | class / _type_ | stable boundary failures; registered `FILE_*` mistakes are caller-safe while `FILE_IO_ERROR` remains internal |
|
|
7914
|
+
| `ManagedFileInspector` | _type_ | bounded-prefix read/write inspection callback with a finite cancellation signal that cannot own path or size |
|
|
7915
|
+
| `ManagedFileInspectionInput` / `ManagedFileInspection` | _type_ | inspector prefix/name/declared media/signal input and validated metadata-only result |
|
|
7223
7916
|
|
|
7224
7917
|
---
|
|
7225
7918
|
|
|
@@ -7258,7 +7951,7 @@ SDK nor the `ai` peer.
|
|
|
7258
7951
|
| `defineCliCommand` | function | define one Zod-typed CLI-only executable command |
|
|
7259
7952
|
| `parseCliArgs` | function | argv → typed tool args against a schema (advanced) |
|
|
7260
7953
|
| `pollUntilDone` | function | the generic `--wait` poller (advanced) |
|
|
7261
|
-
| `emitResult` | function | write a `ToolResult` to stdout/stderr + exit code (advanced) |
|
|
7954
|
+
| `emitResult` | function | write a pretty or compact `ToolResult` record to stdout/stderr + exit code (advanced) |
|
|
7262
7955
|
| `DEFAULT_EXIT_CODES` | const | the default `ToolResult.code` → exit-code map |
|
|
7263
7956
|
| `CliConfig` | _type_ | config for `createCli` |
|
|
7264
7957
|
| `CliSurfaceSource` | _type_ | static service/runtime array or identity-dependent factory |
|
|
@@ -7267,9 +7960,9 @@ SDK nor the `ai` peer.
|
|
|
7267
7960
|
| `CliCommandDefinitionWithOutput` | _type_ | native command with validated declared output |
|
|
7268
7961
|
| `CliCommandDefinitionWithoutOutput` | _type_ | native void command without an output contract |
|
|
7269
7962
|
| `CliCommandContext` | _type_ | parsed input, global options and stdout/stderr writers |
|
|
7270
|
-
| `CliRunOptions` | _type_ | parsed global flags (`--json
|
|
7963
|
+
| `CliRunOptions` | _type_ | parsed global flags (`--json` compacts success/error records, `--wait`, …) |
|
|
7271
7964
|
| `ParsedCliArgs` | _type_ | result of `parseCliArgs` |
|
|
7272
|
-
| `CliWaitConfig` | _type_ | per-command `--wait` polling config |
|
|
7965
|
+
| `CliWaitConfig` | _type_ | per-command `--wait` polling config; optional `failed(result)` maps a terminal domain failure to `WAIT_FAILED` and a non-zero exit |
|
|
7273
7966
|
| `ExitCodeMap` | _type_ | `ToolResult.code` → process exit code |
|
|
7274
7967
|
| `PollParams` | _type_ | params for `pollUntilDone` |
|
|
7275
7968
|
| `CliWriters` | _type_ | stdout/stderr sinks for `emitResult` |
|