stitchkit 0.68.8 → 0.68.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/dist/agent-runtime/deferred-tool-search.d.ts +8 -0
  2. package/dist/agent-runtime/deferred-tool-search.d.ts.map +1 -0
  3. package/dist/agent-runtime/deferred-tool-selection.d.ts +15 -0
  4. package/dist/agent-runtime/deferred-tool-selection.d.ts.map +1 -0
  5. package/dist/agent-runtime/deferred-tool-types.d.ts +131 -0
  6. package/dist/agent-runtime/deferred-tool-types.d.ts.map +1 -0
  7. package/dist/agent-runtime/deferred-tools-internal.d.ts +10 -0
  8. package/dist/agent-runtime/deferred-tools-internal.d.ts.map +1 -0
  9. package/dist/agent-runtime/deferred-tools.d.ts +4 -0
  10. package/dist/agent-runtime/deferred-tools.d.ts.map +1 -0
  11. package/dist/agent-runtime/run-execution.d.ts.map +1 -1
  12. package/dist/agent-runtime-openrouter.js +1 -1
  13. package/dist/agent-runtime.d.ts +1 -0
  14. package/dist/agent-runtime.d.ts.map +1 -1
  15. package/dist/agent-runtime.js +471 -73
  16. package/dist/cli.js +7 -8
  17. package/dist/contract/index.js +2 -2
  18. package/dist/{index-vkk06pv1.js → index-22by16v6.js} +57 -9
  19. package/dist/{index-hmfpjnh7.js → index-22tjt2rk.js} +2 -2
  20. package/dist/{index-1bmpkhj2.js → index-2t6zt5cg.js} +9 -134
  21. package/dist/index-bt5179zb.js +99 -0
  22. package/dist/index-eqqyd9v8.js +130 -0
  23. package/dist/{index-51a19y3v.js → index-f24xg2cw.js} +2 -2
  24. package/dist/{index-j2nq04z6.js → index-f6n5n7nz.js} +1 -1
  25. package/dist/{index-9t2tdk1x.js → index-grgvpbch.js} +3 -5
  26. package/dist/{index-n34x0q5e.js → index-hb0mncmj.js} +3 -3
  27. package/dist/{index-6tqys26z.js → index-hcx9yypn.js} +4 -4
  28. package/dist/{index-z575awm9.js → index-hxh98zbm.js} +2 -2
  29. package/dist/{index-bmmtz6r9.js → index-pgsyp3xh.js} +1 -1
  30. package/dist/index-qyrqwr4c.js +41 -0
  31. package/dist/{index-r5s4wqb5.js → index-t23p2b68.js} +1 -1
  32. package/dist/{index-3ydx9j01.js → index-xyvxez9r.js} +3 -3
  33. package/dist/{index-04agqrs8.js → index-y2ctppmg.js} +72 -6
  34. package/dist/index.js +2 -2
  35. package/dist/internal/typed.d.ts +8 -0
  36. package/dist/internal/typed.d.ts.map +1 -1
  37. package/dist/node.js +5 -5
  38. package/dist/observability/index.js +4 -4
  39. package/dist/react.js +1 -1
  40. package/dist/remote.js +4 -4
  41. package/dist/server/index.js +7 -7
  42. package/dist/testing.js +5 -6
  43. package/dist/tool-invoker.js +6 -8
  44. package/dist/tools/internal/surface-projector.d.ts +7 -0
  45. package/dist/tools/internal/surface-projector.d.ts.map +1 -1
  46. package/dist/tools.js +34 -115
  47. package/llms-full.txt +150 -4
  48. package/package.json +1 -1
  49. package/dist/index-6djpbnda.js +0 -56
  50. package/dist/index-88yyydag.js +0 -75
  51. package/dist/index-smpbdg6k.js +0 -27
  52. /package/dist/{index-tss6bk5c.js → index-ksp6e2ye.js} +0 -0
package/dist/tools.js CHANGED
@@ -1,10 +1,14 @@
1
1
  import {
2
2
  redact
3
- } from "./index-hmfpjnh7.js";
3
+ } from "./index-22tjt2rk.js";
4
+ import {
5
+ buildToolManifest,
6
+ mountAgent
7
+ } from "./index-bt5179zb.js";
4
8
  import {
5
9
  signJwt,
6
10
  verifyPkce
7
- } from "./index-3ydx9j01.js";
11
+ } from "./index-xyvxez9r.js";
8
12
  import {
9
13
  DEFAULT_CORS_ALLOW_HEADERS,
10
14
  DEFAULT_PROCESS_SIGNALS,
@@ -12,39 +16,39 @@ import {
12
16
  defaultSignalSource,
13
17
  guardSignalCallback,
14
18
  reportSignalError
15
- } from "./index-z575awm9.js";
19
+ } from "./index-hxh98zbm.js";
16
20
  import {
17
21
  createToolInvoker
18
- } from "./index-9t2tdk1x.js";
22
+ } from "./index-grgvpbch.js";
19
23
  import {
20
24
  WaitTimeoutError,
21
- collectToolSurface,
22
25
  createCli,
23
- createRuntimeToolFactory,
24
26
  defineCliCommand,
25
- defineRuntimeTool,
26
27
  fetchGuarded,
27
28
  fetchPinnedDocument,
28
29
  readCapped,
29
30
  runWaitOperation
30
- } from "./index-1bmpkhj2.js";
31
+ } from "./index-2t6zt5cg.js";
31
32
  import {
32
- collectTools,
33
- createToolRunner,
34
- formatToolError
35
- } from "./index-88yyydag.js";
33
+ collectToolSurface,
34
+ createRuntimeToolFactory,
35
+ defineRuntimeTool
36
+ } from "./index-eqqyd9v8.js";
36
37
  import {
37
38
  ToolExecutionControlError,
38
39
  coerceJsonArgs,
40
+ collectTools,
41
+ createToolRunner,
39
42
  executeToolMethod,
43
+ formatToolError,
40
44
  isToolExecutionControlError,
41
45
  toolResultFromError
42
- } from "./index-04agqrs8.js";
46
+ } from "./index-y2ctppmg.js";
43
47
  import {
44
48
  getRequestContext,
45
49
  getTraceId,
46
50
  runWithRequestContext
47
- } from "./index-51a19y3v.js";
51
+ } from "./index-f24xg2cw.js";
48
52
  import {
49
53
  ManagedFileError
50
54
  } from "./index-bfcpjw20.js";
@@ -59,17 +63,16 @@ import {
59
63
  prepareProjectedMcpTools,
60
64
  presentationMetadata,
61
65
  validateMcpRoundPolicy
62
- } from "./index-vkk06pv1.js";
63
- import"./index-6djpbnda.js";
66
+ } from "./index-22by16v6.js";
64
67
  import"./index-cby4ar3v.js";
65
68
  import {
66
69
  defineContract,
67
70
  normalizeError,
68
71
  resolvePropagationContext
69
- } from "./index-bmmtz6r9.js";
72
+ } from "./index-pgsyp3xh.js";
70
73
  import {
71
74
  isRecord
72
- } from "./index-smpbdg6k.js";
75
+ } from "./index-qyrqwr4c.js";
73
76
  import {
74
77
  AppError,
75
78
  STITCH_ERROR_STATUS
@@ -78,77 +81,6 @@ import {
78
81
  ManagedFilePathSchema,
79
82
  ManagedFileRefSchema
80
83
  } from "./index-6k1937bx.js";
81
-
82
- // src/tools/agent.ts
83
- import { jsonSchema, tool } from "ai";
84
- function mountAgent(services, config = {}) {
85
- const serviceList = Array.isArray(services) ? services : [services];
86
- const tools = {};
87
- const runTool = createToolRunner({
88
- source: "agent",
89
- extend: config.extend,
90
- context: config.context,
91
- hooks: config.hooks,
92
- lifecycle: config.lifecycle,
93
- errorHint: config.errorHint,
94
- coerceJsonArgs: config.coerceJsonArgs,
95
- onOutputStrip: config.onOutputStrip
96
- });
97
- for (const entry of collectToolSurface({
98
- surface: { services: serviceList, runtimeTools: config.runtimeTools },
99
- transport: "AGENT",
100
- extend: config.extend,
101
- flattenUnionInput: config.flattenUnionInput
102
- })) {
103
- const { mountable } = entry;
104
- const inputSchema = jsonSchema(mountable.presentationSchema, {
105
- validate: async (value) => isRecord(value) ? { success: true, value } : { success: false, error: new Error("Tool arguments must be an object") }
106
- });
107
- const execute = async (rawArgs, options) => {
108
- const args = isRecord(rawArgs) ? rawArgs : {};
109
- try {
110
- const result = await runTool(mountable, args, { signal: options.abortSignal });
111
- if (result.ok)
112
- return result.data;
113
- return formatToolError(result, mountable.name, config.errorHint);
114
- } catch (err) {
115
- if (isToolExecutionControlError(err))
116
- throw err;
117
- return formatToolError(toolResultFromError(err), mountable.name, config.errorHint);
118
- }
119
- };
120
- const presenter = entry.kind === "runtime" ? entry.definition.present?.agent : undefined;
121
- if (entry.kind === "runtime" && presenter) {
122
- const { definition } = entry;
123
- const output = definition.output;
124
- if (!output) {
125
- throw new Error(`Runtime tool "${definition.name}" presenter requires an output schema`);
126
- }
127
- tools[definition.name] = tool({
128
- description: definition.description,
129
- inputSchema,
130
- outputSchema: output,
131
- execute,
132
- toModelOutput: async ({ output: rawOutput }) => {
133
- const parsed = output.safeParse(rawOutput);
134
- if (!parsed.success) {
135
- return { type: "text", value: JSON.stringify(rawOutput) };
136
- }
137
- return presenter(parsed.data);
138
- }
139
- });
140
- } else {
141
- const output = entry.kind === "runtime" ? entry.definition.output : undefined;
142
- tools[mountable.name] = tool({
143
- description: mountable.method.desc,
144
- inputSchema,
145
- ...output && { outputSchema: output },
146
- execute
147
- });
148
- }
149
- }
150
- return tools;
151
- }
152
84
  // src/tools/async-operation.ts
153
85
  import { z } from "zod";
154
86
 
@@ -1003,19 +935,6 @@ function listToolNames(surface) {
1003
935
  function listContractToolNames(contracts) {
1004
936
  return listToolNames({ services: contracts.map(contractOnlyService) });
1005
937
  }
1006
- // src/tools/manifest.ts
1007
- function buildToolManifest(config) {
1008
- return collectToolSurface({
1009
- surface: config,
1010
- transport: config.transport,
1011
- extend: config.extend,
1012
- flattenUnionInput: config.flattenUnionInput
1013
- }).map(({ mountable }) => ({
1014
- name: mountable.name,
1015
- description: mountable.method.desc,
1016
- inputSchema: mountable.presentationSchema
1017
- }));
1018
- }
1019
938
  // src/tools/mcp.ts
1020
939
  import {
1021
940
  createRequestStateCodec,
@@ -1178,23 +1097,23 @@ async function argumentsDigest(args) {
1178
1097
  binary += String.fromCodePoint(byte);
1179
1098
  return btoa(binary).replaceAll("+", "-").replaceAll("/", "_").replace(/=+$/, "");
1180
1099
  }
1181
- function operationIdentity(tool2) {
1100
+ function operationIdentity(tool) {
1182
1101
  return {
1183
- toolName: tool2.name,
1184
- serviceName: tool2.method.serviceName,
1185
- action: tool2.method.key,
1186
- method: tool2.method.method,
1187
- ...tool2.method.scope !== undefined && { scope: tool2.method.scope }
1102
+ toolName: tool.name,
1103
+ serviceName: tool.method.serviceName,
1104
+ action: tool.method.key,
1105
+ method: tool.method.method,
1106
+ ...tool.method.scope !== undefined && { scope: tool.method.scope }
1188
1107
  };
1189
1108
  }
1190
1109
  function sameIdentity(left, right) {
1191
1110
  return left.toolName === right.toolName && left.serviceName === right.serviceName && left.action === right.action && left.method === right.method && left.scope === right.scope;
1192
1111
  }
1193
- async function runRoundSuccess(tool2, rawArgs, runTool, context) {
1112
+ async function runRoundSuccess(tool, rawArgs, runTool, context) {
1194
1113
  return runTool({
1195
- ...tool2,
1114
+ ...tool,
1196
1115
  method: {
1197
- ...tool2.method,
1116
+ ...tool.method,
1198
1117
  outputSchema: undefined,
1199
1118
  handler: () => {
1200
1119
  return;
@@ -1202,11 +1121,11 @@ async function runRoundSuccess(tool2, rawArgs, runTool, context) {
1202
1121
  }
1203
1122
  }, rawArgs, context);
1204
1123
  }
1205
- async function runRoundFailure(tool2, rawArgs, runTool, context, code, message) {
1124
+ async function runRoundFailure(tool, rawArgs, runTool, context, code, message) {
1206
1125
  return runTool({
1207
- ...tool2,
1126
+ ...tool,
1208
1127
  method: {
1209
- ...tool2.method,
1128
+ ...tool.method,
1210
1129
  outputSchema: undefined,
1211
1130
  handler: () => {
1212
1131
  throw new AppError(code, message, 400);
@@ -1402,7 +1321,7 @@ function mountPreparedRuntimeMcp(server, tools, config) {
1402
1321
  }
1403
1322
  server.registerTool(definition.name, toolConfig, async (rawArgs, mcpContext) => runInMcpRequestContext(mcpContext, definition.name, async () => {
1404
1323
  const args = isRecord(rawArgs) ? rawArgs : {};
1405
- const runTool = (tool2, toolArgs, roundContext) => executeToolMethod(tool2.method, definition.name, toolArgs, { ...config.context, ...roundContext, source: "mcp" }, config.hooks, config.lifecycle, config.coerceJsonArgs ?? true, config.onOutputStrip ? (paths) => config.onOutputStrip?.(definition.name, paths) : undefined);
1324
+ const runTool = (tool, toolArgs, roundContext) => executeToolMethod(tool.method, definition.name, toolArgs, { ...config.context, ...roundContext, source: "mcp" }, config.hooks, config.lifecycle, config.coerceJsonArgs ?? true, config.onOutputStrip ? (paths) => config.onOutputStrip?.(definition.name, paths) : undefined);
1406
1325
  const round = await resolveMcpRound({
1407
1326
  tool: mountable,
1408
1327
  rawArgs: args,
package/llms-full.txt CHANGED
@@ -3533,6 +3533,36 @@ const exportOperation = defineAsyncOperation({
3533
3533
  const runtimeTools = exportOperation.runtimeTools
3534
3534
  ```
3535
3535
 
3536
+ #### Durable application-owned execution
3537
+
3538
+ The executable reference
3539
+ [`durable-async-operation-harness.ts`](../../packages/core/examples/durable-async-operation-harness.ts)
3540
+ shows the complete boundary behind that surface. It composes
3541
+ `defineAsyncOperationContract`, `defineAsyncOperation`, `createApplication`,
3542
+ `createBoundedAdmission`, managed shutdown and `createAgentToolFenceLifecycle`
3543
+ around an injected store and provider.
3544
+
3545
+ The store transactionally owns the idempotency key **and normalized request
3546
+ hash**. Reusing both returns the existing operation; reusing the key with a
3547
+ different hash conflicts. Every mutation is an expected-revision CAS. Before an
3548
+ external call, the application persists a unique attempt/effect key and marks
3549
+ the dispatch `possibly-dispatched`; a lost response is therefore reconciled by
3550
+ that key after restart and is never treated as permission to submit the effect
3551
+ again. Provider acknowledgement, progress, cancellation intent, result and
3552
+ artifact references remain durable application state.
3553
+
3554
+ Agent tool fencing and operation idempotency are separate guarantees. The fence
3555
+ prevents a stale run owner from crossing an effect boundary; the transactional
3556
+ idempotency record makes a repeated current call converge on one operation.
3557
+ Neither replaces the other.
3558
+
3559
+ Stitchkit deliberately does **not** own the durable queue, operation database,
3560
+ lease renewal, provider retry/reconciliation protocol, asset catalog or domain
3561
+ states. The process-local application resource stops admission and drains
3562
+ accepted work; unresolved durable records survive shutdown for the next
3563
+ recovery pass. The packed consumer lane executes this composition from the
3564
+ published package on Bun and Node.
3565
+
3536
3566
  Every follow-up repeats `authorize`; an opaque id is never authority. Aborting
3537
3567
  `wait` only stops waiting and never calls optional domain `cancel`.
3538
3568
 
@@ -3682,6 +3712,16 @@ fails first on duplicate contract/runtime names. There is no runtime-only
3682
3712
  manifest helper or public mount adapter: the framework owns merging and schema
3683
3713
  projection.
3684
3714
 
3715
+ Applications using Stitchkit's durable runtime can compose this manifest with
3716
+ bounded per-run activation through `createDeferredAgentToolSurface` from
3717
+ `stitchkit/agent-runtime`. It keeps selected calls as direct `mountAgent` tools
3718
+ rather than routing them through a generic executor. Search receipts live in
3719
+ ordinary durable tool-result history, so recovery restores activation and a
3720
+ queued successor cannot inherit it. See
3721
+ [Bounded deferred tool catalogs](./agent-runtime.md#bounded-deferred-tool-catalogs)
3722
+ and ADR 0129. Raw `mountAgent`, `mountMcp` and `buildToolManifest` behavior is
3723
+ unchanged.
3724
+
3685
3725
  `listToolNames` remains diagnostic and reports `kind: 'contract' | 'runtime'`
3686
3726
  for every identity. `summarizeTransports` returns `contractServices`,
3687
3727
  `runtimeTools`, aggregate `totals`, and a `sources` breakdown. Runtime tools
@@ -3733,7 +3773,7 @@ description: Configure Stitchkit's optional durable history, stream loop, run co
3733
3773
  type: architecture
3734
3774
  status: active
3735
3775
  created: 2026-08-22
3736
- updated: 2026-08-28
3776
+ updated: 2026-08-29
3737
3777
  ---
3738
3778
 
3739
3779
  # Agent application runtime
@@ -3888,6 +3928,69 @@ await ticket.accepted
3888
3928
  const terminal = await ticket.result
3889
3929
  ```
3890
3930
 
3931
+ ## Bounded deferred tool catalogs
3932
+
3933
+ Use `createDeferredAgentToolSurface` when a measured large catalog makes
3934
+ sending every schema on every provider step expensive. The controller mounts
3935
+ the complete executable surface in process, but initially advertises only its
3936
+ search tool, explicit `alwaysOn` tools and bounded policy pins:
3937
+
3938
+ ```ts
3939
+ import { createDeferredAgentToolSurface } from 'stitchkit/agent-runtime'
3940
+ import { composeToolLifecycle } from 'stitchkit/tools'
3941
+
3942
+ const deferred = createDeferredAgentToolSurface({
3943
+ surfaces: {
3944
+ member: { services: memberServices, runtimeTools, alwaysOn: ['ask_user'] },
3945
+ broadcast: { services: broadcastServices, runtimeTools },
3946
+ },
3947
+ selectSurface: ({ context }) => context.mode,
3948
+ pins: ({ context }) => context.skillTools,
3949
+ search: {
3950
+ name: 'tool_search',
3951
+ maxQueryBytes: 1_024,
3952
+ maxResults: 8,
3953
+ maxResultBytes: 8_192,
3954
+ select: ({ query, manifest }) => rankCatalog(query, manifest),
3955
+ },
3956
+ activation: {
3957
+ maxSelectedTools: 8,
3958
+ maxActiveTools: 16,
3959
+ maxSchemaBytes: 32_768,
3960
+ },
3961
+ observe: event => recordDeferredToolEvidence(event),
3962
+ })
3963
+
3964
+ const runtime = createAgentRuntime({
3965
+ // protocol, store, models and prompt...
3966
+ tools: runContext => deferred.mount(runContext, {
3967
+ context: runContext.context,
3968
+ lifecycle: composeToolLifecycle(authLifecycle, runContext.toolFenceLifecycle),
3969
+ }),
3970
+ loop: { prepareStep: deferred.prepareStep(applicationPrepareStep) },
3971
+ })
3972
+ ```
3973
+
3974
+ The built-in selector deterministically ranks exact name, prefix/token and
3975
+ description matches. A custom async selector returns names only; unknown and
3976
+ duplicate output is removed against the local canonical manifest. Search never
3977
+ executes the selected operation and never returns its schema. A later provider
3978
+ step receives the real selected tools by name, so their direct lifecycle,
3979
+ fencing, hooks, errors and `present.agent` output remain unchanged.
3980
+
3981
+ Selection is a versioned receipt in the ordinary durable tool-result history.
3982
+ The latest valid same-run search replaces the earlier selection; parallel
3983
+ searches in one step merge in call order. Recovery reconstructs it, while a
3984
+ different run or finite surface cannot inherit it. Known inactive calls take a
3985
+ recoverable `SEARCH_REQUIRED` search round; unknown names still fail.
3986
+
3987
+ Every ceiling is required. `maxSchemaBytes` measures UTF-8 bytes over canonical
3988
+ name, description and presentation schema. It is useful for before/after
3989
+ comparison, but provider serialization and tokenization differ: use existing
3990
+ provider-reported input usage and cost to decide whether the extra search round
3991
+ is beneficial. Controller evidence intentionally omits query text, prompts,
3992
+ arguments and application context. → ADR 0129.
3993
+
3891
3994
  ### Executable headless harness and capability map
3892
3995
 
3893
3996
  [`packages/core/examples/headless-agent-harness.ts`](../../packages/core/examples/headless-agent-harness.ts)
@@ -4348,6 +4451,43 @@ These are post-commit notifications, not a transactional outbox: a process can
4348
4451
  crash between the database commit and `publish`. Reconnect should load canonical
4349
4452
  state. Exactly-once external delivery remains an application-owned outbox.
4350
4453
 
4454
+ ### External channel ingress and delivery
4455
+
4456
+ The executable
4457
+ [`external-channel-harness.ts`](../../packages/core/examples/external-channel-harness.ts)
4458
+ is the reference composition for webhooks, polling transports and local-device
4459
+ adapters. Its generic boundary injects a runtime, durable store and delivery
4460
+ adapter; the optional
4461
+ [`external-channel-grammy.ts`](../../packages/core/examples/external-channel-grammy.ts)
4462
+ attaches the same ingress function to grammY polling or webhook lifecycle
4463
+ without putting provider types in the agent runtime.
4464
+
4465
+ Inbound update identity, principal resolution, conversation mapping and reply
4466
+ target are application policy. Persist and deduplicate the update before
4467
+ `runtime.submit`, use the stored idempotency key on crash recovery, then persist
4468
+ the admitted `runId`. A duplicate arriving after restart resolves to that same
4469
+ mapping instead of creating another model turn.
4470
+
4471
+ For output, choose `terminal-only` or `streaming` explicitly. Stable durable
4472
+ `eventId` values and transient `(runtimeEpoch, sequence)` identities become
4473
+ outbox keys; the application store assigns causal ordinals. A bounded channel
4474
+ is only a process-local wakeup and never the durable authority. Before sending,
4475
+ persist one of these states:
4476
+
4477
+ | State | Recovery action |
4478
+ | --- | --- |
4479
+ | `not-dispatched` | may dispatch once |
4480
+ | `possibly-dispatched` | reconcile with the adapter or remain unresolved |
4481
+ | `acknowledged` | deduplicate; delivery is complete |
4482
+
4483
+ The agent `terminal` event and the adapter's delivery receipt are intentionally
4484
+ different records. A successful run does not prove that a reply reached its
4485
+ channel, and a delivery failure never rewrites the canonical agent result.
4486
+ Shutdown stops ingress, drains accepted process-local work and leaves ambiguous
4487
+ outbox records visible for a later reconciliation pass. Stitchkit does not own
4488
+ a channel database, durable broker, provider payload schema or application
4489
+ identity policy.
4490
+
4351
4491
  Durable event IDs are derived from run, event type and snapshot version. Use
4352
4492
  `advanceAgentRuntimeEventCursor` to classify delivery.
4353
4493
 
@@ -11616,6 +11756,12 @@ Server-only optional application runtime. See the
11616
11756
  | Export | Kind | Summary |
11617
11757
  |--------|------|---------|
11618
11758
  | `createAgentRuntime` | function | compose durable acceptance, stream loop, checkpoints, coordination, managed tools and winner-only terminal publication; reconciles same-owner terminal/interrupt/head CAS races before releasing the lane |
11759
+ | `createDeferredAgentToolSurface` | function | bounded canonical catalog search plus durable same-run activation of direct `mountAgent` tools; supports one immutable surface or a finite identity-specific registry |
11760
+ | `DeferredAgentToolSearchInputSchema` / `DeferredAgentToolMatchSchema` / `DeferredAgentToolReceiptSchema` | schema | bounded search input, public match and versioned durable selection receipt |
11761
+ | `DeferredAgentToolSurfaceConfig` / `DeferredAgentToolCommonConfig` / `DeferredAgentToolSurfaceDefinition` / `DeferredAgentToolController` | _type_ | finite catalogs, selector, pin, budget and `mount`/`prepareStep` composition contracts |
11762
+ | `DeferredAgentRuntimeToolDefinition` / `DeferredAgentToolMountConfig` | _type_ | peer-free Agent-only runtime definition and canonical mount configuration accepted by the controller |
11763
+ | `DeferredAgentToolManifestEntry` / `DeferredAgentToolSearchContext` / `DeferredAgentToolReceipt` | _type_ | canonical selector input and inferred durable receipt records |
11764
+ | `DeferredAgentToolEvent` | _type_ | PII-free search/step evidence with surface, provenance, counts, canonical schema bytes and ceilings |
11619
11765
  | `defineAgentProtocol` | function | declare context, input metadata, canonical parts and optional pre-CAS terminal acceptance (`allow-empty`, `require-output` or callback) |
11620
11766
  | `hasAgentTerminalOutput` | function | generic `require-output` predicate for non-blank text, generated files, structured provider parts and explicit tool-only policy stops |
11621
11767
  | `AgentMessageSchema` / `AgentRunSchema` / `AgentSnapshotSchema` | schema | versioned canonical engine records |
@@ -11646,7 +11792,7 @@ Server-only optional application runtime. See the
11646
11792
  | `AgentSessionCloseOptions` | _type_ | `gracePeriodMs` for natural settlement, then abort, then `forceTimeoutMs` for bounded settlement after it |
11647
11793
  | `AgentSessionCloseResult` | _type_ | what `close()` achieved: `settled`, or `timedOut` with `remaining` runs still in flight. Only omitting `forceTimeoutMs` guarantees nothing is in flight on return |
11648
11794
  | `AgentHistoryProjectionOptions` | _type_ | storage-neutral file resolver, explicit unresolved-file behavior, and how an interrupted turn reaches the model (`interruptedAssistant`) |
11649
- | `createAgentToolFenceLifecycle` | function | pre-effect and post-effect run ownership fence for `mountAgent` |
11795
+ | `createAgentToolFenceLifecycle` | function | pre-effect and post-effect run ownership fence for `mountAgent`; compose beside application idempotency for [durable operations](../guide/mcp-and-agents.md#durable-application-owned-execution) |
11650
11796
  | `AgentRuntimeEventSchema` | schema | transient stream lifecycle plus post-commit admission/checkpoint/run-state/terminal projections |
11651
11797
  | `createAgentObservability` | function | separate agent-run sink over the shared bounded observability lifecycle |
11652
11798
 
@@ -12035,8 +12181,8 @@ runtime-tool runner, plus deliberate raw MCP adapters over the same mechanics.
12035
12181
  | `NativeToolIdentity` | _type_ | pathless service/action/scope/meta identity; semantic method is factory-owned |
12036
12182
  | `ManagedWaitRender` | _type_ | optional managed wait terminal text and failure classification |
12037
12183
  | `UploadToolInputSchema` | constant | fixed `{ path: string }` input schema for `defineUploadTool` |
12038
- | `defineAsyncOperation` | function | runtime-only start/status/wait plus configured cancel/result/artifacts definitions |
12039
- | `defineAsyncOperationContract` | function | define one canonical Zod-first HTTP contract for start/status/wait plus optional capabilities |
12184
+ | `defineAsyncOperation` | function | runtime-only start/status/wait plus configured cancel/result/artifacts definitions; see the [durable application-owned recipe](../guide/mcp-and-agents.md#durable-application-owned-execution) |
12185
+ | `defineAsyncOperationContract` | function | define one canonical Zod-first HTTP contract for start/status/wait plus optional capabilities; execution and recovery remain application-owned |
12040
12186
  | `bindContractAsyncOperation` | function | bind literal methods from an existing contract without creating another HTTP surface |
12041
12187
  | `createAsyncOperationSnapshotSchema` | function | canonical pending/running/succeeded/failed/cancelled Zod snapshot |
12042
12188
  | `AsyncOperationCancelResultSchema` | constant | validated accepted/already_terminal/rejected cancellation result |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stitchkit",
3
- "version": "0.68.8",
3
+ "version": "0.68.10",
4
4
  "description": "Contract-first backend framework — one defineContract() into an HTTP API, MCP tools, AI-agent tools and a typed client. Bun and Node.",
5
5
  "keywords": [
6
6
  "bun",
@@ -1,56 +0,0 @@
1
- import {
2
- toJsonSchema
3
- } from "./index-cby4ar3v.js";
4
-
5
- // src/tools/schema.ts
6
- import { z } from "zod";
7
- function objectShapeKeys(schema) {
8
- return schema instanceof z.ZodObject ? Object.keys(schema.shape) : [];
9
- }
10
- function keyPolicyOf(schema) {
11
- return schema.def.catchall;
12
- }
13
- function rebuildObject(source, shape) {
14
- return withKeyPolicy(z.object(shape), keyPolicyOf(source));
15
- }
16
- function withKeyPolicy(object, policy) {
17
- if (policy === undefined)
18
- return object;
19
- return object.catchall(representable(policy));
20
- }
21
- function representable(policy) {
22
- if (policy instanceof z.ZodNever || policy instanceof z.ZodUnknown)
23
- return policy;
24
- if (!(policy instanceof z.ZodType))
25
- return z.unknown();
26
- try {
27
- toJsonSchema(policy, "input");
28
- return policy;
29
- } catch {
30
- return z.unknown();
31
- }
32
- }
33
- function mergeSchemas(paramsSchema, inputSchema) {
34
- if (paramsSchema && !(paramsSchema instanceof z.ZodObject)) {
35
- throw new Error("Tool params schema must be a z.object()");
36
- }
37
- const paramsObject = paramsSchema instanceof z.ZodObject ? paramsSchema : undefined;
38
- if (!inputSchema) {
39
- return paramsObject ?? z.object({});
40
- }
41
- if (inputSchema instanceof z.ZodObject) {
42
- if (paramsObject) {
43
- const conflicts = Object.keys(paramsObject.shape).filter((key) => (key in inputSchema.shape));
44
- if (conflicts.length > 0) {
45
- throw new Error(`Schema merge conflict: ${conflicts.join(", ")} appear in both params and input`);
46
- }
47
- }
48
- return rebuildObject(inputSchema, {
49
- ...paramsObject?.shape ?? {},
50
- ...inputSchema.shape
51
- });
52
- }
53
- return paramsObject ? z.intersection(paramsObject, inputSchema) : inputSchema;
54
- }
55
-
56
- export { objectShapeKeys, rebuildObject, mergeSchemas };
@@ -1,75 +0,0 @@
1
- import {
2
- executeToolMethod
3
- } from "./index-04agqrs8.js";
4
- import {
5
- assertToolExtensionCompatible,
6
- projectToolSurface
7
- } from "./index-vkk06pv1.js";
8
- import {
9
- mergeSchemas,
10
- rebuildObject
11
- } from "./index-6djpbnda.js";
12
-
13
- // src/tools/mount.ts
14
- import { z } from "zod";
15
- function applyExtend(base, extra) {
16
- if (base instanceof z.ZodObject) {
17
- assertToolExtensionCompatible(base, extra);
18
- return rebuildObject(base, { ...extra, ...base.shape });
19
- }
20
- return z.intersection(z.object(extra), base);
21
- }
22
- function contractToolMountable(projected, extend) {
23
- const method = projected.source;
24
- const baseArgumentSchema = mergeSchemas(method.paramsSchema, method.inputSchema);
25
- const argumentSchema = projected.shouldExtend && extend ? applyExtend(baseArgumentSchema, extend.schema) : baseArgumentSchema;
26
- return {
27
- method,
28
- name: projected.name,
29
- argumentSchema,
30
- presentationSchema: projected.presentationSchema,
31
- shouldExtend: projected.shouldExtend
32
- };
33
- }
34
- function collectTools(service, transport, config = {}) {
35
- const { extend, flattenUnionInput = false, assertNames = true } = config;
36
- const tools = [];
37
- for (const projected of projectToolSurface({ services: [service] }, transport === "HTTP" ? "AGENT" : transport, {
38
- extend,
39
- flattenUnionInput,
40
- assertNames,
41
- assertUniqueNames: false
42
- })) {
43
- if (projected.kind !== "contract")
44
- continue;
45
- tools.push(contractToolMountable(projected, extend));
46
- }
47
- return tools;
48
- }
49
- function createToolRunner(config) {
50
- const extension = config.extend ? {
51
- schema: z.object(config.extend.schema),
52
- resolve: config.extend.resolve
53
- } : undefined;
54
- return async function runOneToolCall(tool, rawArgs, context) {
55
- return executeToolMethod(tool.method, tool.name, rawArgs, { ...config.context, ...context, source: config.source }, config.hooks, config.lifecycle, config.coerceJsonArgs ?? true, config.onOutputStrip ? (paths) => config.onOutputStrip?.(tool.name, paths) : undefined, tool.shouldExtend ? extension : undefined);
56
- };
57
- }
58
- function formatToolError(result, toolName, errorHint) {
59
- const err = { error: result.code };
60
- if (result.details)
61
- err.details = result.details;
62
- const hints = [];
63
- if (result.hint)
64
- hints.push(result.hint);
65
- if (errorHint && toolName) {
66
- const global = errorHint(toolName, result.code);
67
- if (global)
68
- hints.push(global);
69
- }
70
- if (hints.length > 0)
71
- err._hint = hints.join(" ");
72
- return err;
73
- }
74
-
75
- export { contractToolMountable, collectTools, createToolRunner, formatToolError };
@@ -1,27 +0,0 @@
1
- // src/internal/typed.ts
2
- function typedEntries(value) {
3
- return Object.entries(value);
4
- }
5
- function isRecord(value) {
6
- return typeof value === "object" && value !== null && !Array.isArray(value);
7
- }
8
- function callRuntimeHandler(handler, context) {
9
- if (typeof handler !== "function") {
10
- throw new TypeError("Runtime handler must be a function");
11
- }
12
- return Reflect.apply(handler, undefined, [context]);
13
- }
14
- function transportResult(value) {
15
- return value;
16
- }
17
- function mapObject(source, mapper) {
18
- const result = {};
19
- for (const [key, value] of typedEntries(source)) {
20
- const mapped = mapper(key, value);
21
- if (mapped !== undefined)
22
- result[key] = mapped;
23
- }
24
- return result;
25
- }
26
-
27
- export { typedEntries, isRecord, callRuntimeHandler, transportResult, mapObject };
File without changes