stitchkit 0.56.4 → 0.57.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.
@@ -0,0 +1,160 @@
1
+ // src/agent-runtime/schemas.ts
2
+ import { z } from "zod";
3
+ var AgentRecordIdSchema = z.string().min(1);
4
+ var AgentRecordVersionSchema = z.int().nonnegative();
5
+ var AgentTimestampSchema = z.iso.datetime({ offset: true });
6
+ var AgentJsonObjectSchema = z.record(z.string(), z.json());
7
+ var AgentProviderEnvelopeSchema = z.object({
8
+ schemaVersion: z.int().positive(),
9
+ provider: z.string().min(1),
10
+ data: AgentJsonObjectSchema
11
+ });
12
+ var AgentTextPartSchema = z.object({
13
+ type: z.literal("text"),
14
+ text: z.string()
15
+ });
16
+ var AgentReasoningPartSchema = z.object({
17
+ type: z.literal("reasoning"),
18
+ text: z.string(),
19
+ provider: AgentProviderEnvelopeSchema.optional()
20
+ });
21
+ var AgentFilePartSchema = z.object({
22
+ type: z.literal("file"),
23
+ mediaType: z.string().min(1),
24
+ reference: z.string().min(1),
25
+ filename: z.string().min(1).optional()
26
+ });
27
+ var AgentSourcePartSchema = z.object({
28
+ type: z.literal("source"),
29
+ sourceId: z.string().min(1),
30
+ url: z.url().optional(),
31
+ title: z.string().optional()
32
+ });
33
+ var AgentToolCallPartSchema = z.object({
34
+ type: z.literal("tool-call"),
35
+ callId: z.string().min(1),
36
+ toolName: z.string().min(1),
37
+ input: z.json(),
38
+ provider: AgentProviderEnvelopeSchema.optional()
39
+ });
40
+ var AgentToolResultPartSchema = z.object({
41
+ type: z.literal("tool-result"),
42
+ callId: z.string().min(1),
43
+ toolName: z.string().min(1),
44
+ outcome: z.enum(["success", "error", "interrupted"]),
45
+ output: z.json().optional()
46
+ });
47
+ var AgentOpaquePartSchema = z.object({
48
+ type: z.literal("provider"),
49
+ envelope: AgentProviderEnvelopeSchema
50
+ });
51
+ var AgentControlPartSchema = z.object({
52
+ type: z.literal("control"),
53
+ reason: z.enum(["run-interrupted", "stale-run"])
54
+ });
55
+ var AgentMessagePartSchema = z.discriminatedUnion("type", [
56
+ AgentTextPartSchema,
57
+ AgentReasoningPartSchema,
58
+ AgentFilePartSchema,
59
+ AgentSourcePartSchema,
60
+ AgentToolCallPartSchema,
61
+ AgentToolResultPartSchema,
62
+ AgentOpaquePartSchema,
63
+ AgentControlPartSchema
64
+ ]);
65
+ var AgentMessageRoleSchema = z.enum(["user", "assistant", "system", "summary"]);
66
+ var AgentMessageStatusSchema = z.enum([
67
+ "committed",
68
+ "streaming",
69
+ "completed",
70
+ "interrupted",
71
+ "failed"
72
+ ]);
73
+ var AgentMessageSchema = z.object({
74
+ schemaVersion: z.literal(1),
75
+ id: AgentRecordIdSchema,
76
+ conversationId: AgentRecordIdSchema,
77
+ runId: AgentRecordIdSchema.optional(),
78
+ role: AgentMessageRoleSchema,
79
+ status: AgentMessageStatusSchema,
80
+ parts: z.array(AgentMessagePartSchema),
81
+ metadata: AgentJsonObjectSchema.optional(),
82
+ createdAt: AgentTimestampSchema,
83
+ updatedAt: AgentTimestampSchema
84
+ });
85
+ var AgentAssistantPlaceholderSchema = z.object({
86
+ schemaVersion: z.literal(1),
87
+ id: AgentRecordIdSchema,
88
+ conversationId: AgentRecordIdSchema,
89
+ runId: AgentRecordIdSchema,
90
+ status: z.literal("pending"),
91
+ createdAt: AgentTimestampSchema,
92
+ updatedAt: AgentTimestampSchema
93
+ });
94
+ var AgentRunStateSchema = z.enum([
95
+ "queued",
96
+ "running",
97
+ "interrupt_requested",
98
+ "completed",
99
+ "interrupted",
100
+ "failed",
101
+ "cancelled",
102
+ "abandoned"
103
+ ]);
104
+ var AgentTerminalReasonSchema = z.enum([
105
+ "success",
106
+ "policy_stop",
107
+ "interrupted",
108
+ "cancelled",
109
+ "timeout",
110
+ "shutdown",
111
+ "provider_failure",
112
+ "tool_failure",
113
+ "abandoned"
114
+ ]);
115
+ var AgentRunSchema = z.object({
116
+ schemaVersion: z.literal(1),
117
+ id: AgentRecordIdSchema,
118
+ conversationId: AgentRecordIdSchema,
119
+ inputMessageIds: z.array(AgentRecordIdSchema).min(1),
120
+ assistantMessageId: AgentRecordIdSchema,
121
+ state: AgentRunStateSchema,
122
+ revision: AgentRecordVersionSchema,
123
+ ownerId: z.string().min(1).optional(),
124
+ terminalReason: AgentTerminalReasonSchema.optional(),
125
+ terminalPolicyName: z.string().min(1).optional(),
126
+ createdAt: AgentTimestampSchema,
127
+ updatedAt: AgentTimestampSchema
128
+ });
129
+ var AgentSnapshotSchema = z.object({
130
+ schemaVersion: z.literal(1),
131
+ conversationId: AgentRecordIdSchema,
132
+ version: AgentRecordVersionSchema,
133
+ messages: z.array(AgentMessageSchema),
134
+ runs: z.array(AgentRunSchema)
135
+ });
136
+ var AgentUsageValueSchema = z.object({
137
+ value: z.number().nonnegative().optional(),
138
+ provenance: z.enum(["provider-reported", "computed", "estimated", "unavailable"])
139
+ });
140
+ var AgentCostValueSchema = z.object({
141
+ value: z.number().nonnegative().optional(),
142
+ currency: z.string().length(3).optional(),
143
+ provenance: z.enum(["provider-reported", "computed", "estimated", "unavailable"])
144
+ });
145
+ var AgentUsageSchema = z.object({
146
+ inputTokens: AgentUsageValueSchema,
147
+ outputTokens: AgentUsageValueSchema,
148
+ reasoningTokens: AgentUsageValueSchema.optional(),
149
+ cacheReadTokens: AgentUsageValueSchema.optional(),
150
+ cacheWriteTokens: AgentUsageValueSchema.optional(),
151
+ cost: AgentCostValueSchema.optional()
152
+ });
153
+ var AgentRunMetricsSchema = z.object({
154
+ partial: z.boolean(),
155
+ usage: AgentUsageSchema.optional(),
156
+ durationMs: z.number().nonnegative().optional(),
157
+ ttftMs: z.number().nonnegative().optional()
158
+ });
159
+
160
+ export { AgentRecordIdSchema, AgentRecordVersionSchema, AgentTimestampSchema, AgentJsonObjectSchema, AgentProviderEnvelopeSchema, AgentTextPartSchema, AgentReasoningPartSchema, AgentFilePartSchema, AgentSourcePartSchema, AgentToolCallPartSchema, AgentToolResultPartSchema, AgentOpaquePartSchema, AgentControlPartSchema, AgentMessagePartSchema, AgentMessageRoleSchema, AgentMessageStatusSchema, AgentMessageSchema, AgentAssistantPlaceholderSchema, AgentRunStateSchema, AgentTerminalReasonSchema, AgentRunSchema, AgentSnapshotSchema, AgentUsageValueSchema, AgentCostValueSchema, AgentUsageSchema, AgentRunMetricsSchema };
@@ -5,7 +5,8 @@
5
5
  * Every project writes the same `onError`: turn a thrown value into one wire
6
6
  * shape, mapping stitchkit's own error codes to the app's public codes. The map
7
7
  * is partial — list the codes you have an opinion about, and an unlisted one
8
- * travels as itself, exactly like a code the project threw on its own. A
8
+ * travels as itself unless `unmappedCode` supplies one declarative fallback.
9
+ * Codes the project throws on its own always pass through unchanged. A
9
10
  * project whose envelope is a published contract adds `satisfies
10
11
  * Record<StitchErrorCode, …>` to its own map and buys the stricter deal: a code
11
12
  * added by a later release then breaks the build instead of reaching the wire
@@ -58,6 +59,12 @@ export interface ErrorHookConfig<TWireCode extends string = string> {
58
59
  * error here. Codes you threw yourself (not stitchkit's) pass through as-is.
59
60
  */
60
61
  codeMap?: Partial<Record<StitchErrorCode, TWireCode>>;
62
+ /**
63
+ * Wire code for every unmapped stitchkit code, or a resolver for grouping
64
+ * them. Explicit `codeMap` entries win. Project-owned codes never use this
65
+ * fallback and continue to pass through unchanged.
66
+ */
67
+ unmappedCode?: TWireCode | ((code: StitchErrorCode) => TWireCode);
61
68
  /** Build the response body from the resolved error. */
62
69
  /**
63
70
  * Build the response body from the resolved error. `ctx` is the request's
@@ -1 +1 @@
1
- {"version":3,"file":"error-hook.d.ts","sourceRoot":"","sources":["../../src/server/error-hook.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAqB,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;AAEtE,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEzD,uEAAuE;AACvE,MAAM,WAAW,aAAa;IAC5B,wFAAwF;IACxF,IAAI,EAAE,MAAM,CAAC;IACb,mBAAmB;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,+FAA+F;IAC/F,OAAO,EAAE,MAAM,CAAC;IAChB,mEAAmE;IACnE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,6DAA6D;IAC7D,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,oCAAoC;AACpC,MAAM,WAAW,eAAe,CAAC,SAAS,SAAS,MAAM,GAAG,MAAM;IAChE;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,eAAe,EAAE,SAAS,CAAC,CAAC,CAAC;IACtD,uDAAuD;IACvD;;;;;OAKG;IACH,MAAM,EAAE,CACN,IAAI,EAAE,aAAa,EACnB,GAAG,EAAE,cAAc,EACnB,QAAQ,CAAC,EAAE,SAAS,KACjB,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAChC;;;;OAIG;IACH,OAAO,CAAC,EAAE,CACR,KAAK,EAAE,OAAO,EACd,IAAI,EAAE,aAAa,EACnB,GAAG,EAAE,cAAc,EACnB,QAAQ,CAAC,EAAE,SAAS,KACjB,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACjC;AAED,mEAAmE;AACnE,wBAAgB,eAAe,CAAC,SAAS,SAAS,MAAM,GAAG,MAAM,EAC/D,MAAM,EAAE,eAAe,CAAC,SAAS,CAAC,GACjC,WAAW,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC,CAgCxC"}
1
+ {"version":3,"file":"error-hook.d.ts","sourceRoot":"","sources":["../../src/server/error-hook.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAqB,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;AAEtE,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEzD,uEAAuE;AACvE,MAAM,WAAW,aAAa;IAC5B,wFAAwF;IACxF,IAAI,EAAE,MAAM,CAAC;IACb,mBAAmB;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,+FAA+F;IAC/F,OAAO,EAAE,MAAM,CAAC;IAChB,mEAAmE;IACnE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,6DAA6D;IAC7D,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,oCAAoC;AACpC,MAAM,WAAW,eAAe,CAAC,SAAS,SAAS,MAAM,GAAG,MAAM;IAChE;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,eAAe,EAAE,SAAS,CAAC,CAAC,CAAC;IACtD;;;;OAIG;IACH,YAAY,CAAC,EAAE,SAAS,GAAG,CAAC,CAAC,IAAI,EAAE,eAAe,KAAK,SAAS,CAAC,CAAC;IAClE,uDAAuD;IACvD;;;;;OAKG;IACH,MAAM,EAAE,CACN,IAAI,EAAE,aAAa,EACnB,GAAG,EAAE,cAAc,EACnB,QAAQ,CAAC,EAAE,SAAS,KACjB,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAChC;;;;OAIG;IACH,OAAO,CAAC,EAAE,CACR,KAAK,EAAE,OAAO,EACd,IAAI,EAAE,aAAa,EACnB,GAAG,EAAE,cAAc,EACnB,QAAQ,CAAC,EAAE,SAAS,KACjB,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACjC;AAED,mEAAmE;AACnE,wBAAgB,eAAe,CAAC,SAAS,SAAS,MAAM,GAAG,MAAM,EAC/D,MAAM,EAAE,eAAe,CAAC,SAAS,CAAC,GACjC,WAAW,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC,CAkCxC"}
@@ -351,8 +351,10 @@ function cacheHeaders(maxAge, scope = "public") {
351
351
  function createErrorHook(config) {
352
352
  return async (ctx, error, endpoint) => {
353
353
  const appErr = normalizeError(error);
354
- const mapped = config.codeMap && isStitchErrorCode(appErr.code) ? config.codeMap[appErr.code] : undefined;
355
- const code = mapped ?? appErr.code;
354
+ const stitchCode = isStitchErrorCode(appErr.code) ? appErr.code : undefined;
355
+ const mapped = stitchCode === undefined ? undefined : config.codeMap?.[stitchCode];
356
+ const fallback = mapped === undefined && stitchCode !== undefined ? typeof config.unmappedCode === "function" ? config.unmappedCode(stitchCode) : config.unmappedCode : undefined;
357
+ const code = mapped ?? fallback ?? appErr.code;
356
358
  const info = {
357
359
  code,
358
360
  status: appErr.status,
@@ -0,0 +1,7 @@
1
+ import type { AgentRuntimeStore } from '../agent-runtime/store';
2
+ export interface AgentStoreConformanceConfig {
3
+ createStore(): AgentRuntimeStore | Promise<AgentRuntimeStore>;
4
+ }
5
+ /** Black-box contract shared by memory and third-party durable agent stores. */
6
+ export declare function runAgentStoreConformance(config: AgentStoreConformanceConfig): Promise<void>;
7
+ //# sourceMappingURL=agent-store-conformance.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-store-conformance.d.ts","sourceRoot":"","sources":["../../src/testing/agent-store-conformance.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAEhE,MAAM,WAAW,2BAA2B;IAC1C,WAAW,IAAI,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;CAC/D;AAwCD,gFAAgF;AAChF,wBAAsB,wBAAwB,CAC5C,MAAM,EAAE,2BAA2B,GAClC,OAAO,CAAC,IAAI,CAAC,CAuJf"}
package/dist/testing.d.ts CHANGED
@@ -31,6 +31,7 @@ export declare function createHandlerTestClient<T extends Record<string, Endpoin
31
31
  export declare function createHandlerTestClients<T extends Record<string, ContractDef<Record<string, EndpointDef>, string>>, TServer = unknown, const K extends string = never>(config: HandlerTestClientsConfig<T, TServer, K>): {
32
32
  [P in keyof T]: ScopedHttpClient<T[P]['endpoints'], ScopedKeys<K>>;
33
33
  };
34
+ export { type AgentStoreConformanceConfig, runAgentStoreConformance, } from './testing/agent-store-conformance';
34
35
  export { assertSurfaceDiscovery, type ConformanceTransport, type CreateRealtimeProbeDriverConfig, createRealtimeProbeDriver, type DefineRealtimeProbeConfig, defineRealtimeProbe, type RealtimeDisconnectObservation, RealtimeDisconnectObservationSchema, type RealtimeProbeAdapter, type RealtimeProbeFixture, type RealtimeProbeScenario, type RealtimeRejectionObservation, RealtimeRejectionObservationSchema, type RunSurfaceProbesConfig, runSurfaceProbes, type SurfaceDiscoveryObservation, type SurfaceProbe, type SurfaceProbeDriver, type SurfaceRealtimeDiscoveryObservation, type SurfaceToolDiscoveryObservation, type TransportObservation, TransportObservationSchema, } from './testing/surface-conformance';
35
36
  export { assertSurfaceManifestSnapshot, buildSurfaceManifest, type IncompatibleSchemaPolicy, type McpSchemaValidationConfig, type SurfaceAgentProjection, type SurfaceManifest, type SurfaceManifestConfig, type SurfaceManifestExtension, SurfaceManifestExtensionSchema, type SurfaceManifestOperation, SurfaceManifestOperationSchema, type SurfaceManifestRealtimeEvent, SurfaceManifestRealtimeEventSchema, SurfaceManifestSchema, type SurfaceManifestTool, SurfaceManifestToolSchema, type SurfaceManifestToolSurface, SurfaceManifestToolSurfaceSchema, type SurfaceMcpPreparation, SurfaceRealtimeSchemaPairSchema, type SurfaceRuntimeToolDefinition, SurfaceSchemaDigestsSchema, type SurfaceToolDefinition, type SurfaceToolExtension, serializeSurfaceValue, } from './testing/surface-manifest';
36
37
  //# sourceMappingURL=testing.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"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"}
package/dist/testing.js CHANGED
@@ -1,3 +1,7 @@
1
+ import {
2
+ AgentMessageSchema,
3
+ AgentRunSchema
4
+ } from "./index-1f4fcj0b.js";
1
5
  import {
2
6
  RealtimeRequestDisconnectedError,
3
7
  RealtimeRequestInvalidAcknowledgementError,
@@ -23,6 +27,167 @@ import {
23
27
  } from "./index-smpbdg6k.js";
24
28
  import"./index-1bx83sw4.js";
25
29
 
30
+ // src/testing/agent-store-conformance.ts
31
+ function userMessage(conversationId, id) {
32
+ return AgentMessageSchema.parse({
33
+ schemaVersion: 1,
34
+ id,
35
+ conversationId,
36
+ role: "user",
37
+ status: "committed",
38
+ parts: [{ type: "text", text: id }],
39
+ createdAt: "2026-08-22T00:00:00.000Z",
40
+ updatedAt: "2026-08-22T00:00:00.000Z"
41
+ });
42
+ }
43
+ function queuedRun(conversationId, inputMessageId, id) {
44
+ return AgentRunSchema.parse({
45
+ schemaVersion: 1,
46
+ id,
47
+ conversationId,
48
+ inputMessageIds: [inputMessageId],
49
+ assistantMessageId: `${id}-assistant`,
50
+ state: "queued",
51
+ revision: 0,
52
+ createdAt: "2026-08-22T00:00:00.000Z",
53
+ updatedAt: "2026-08-22T00:00:00.000Z"
54
+ });
55
+ }
56
+ function requireOutcome(actual, expected) {
57
+ if (actual.outcome !== expected) {
58
+ throw new Error(`Agent store conformance expected ${expected}, received ${actual.outcome}`);
59
+ }
60
+ }
61
+ async function runAgentStoreConformance(config) {
62
+ const store = await config.createStore();
63
+ const conversationId = `conformance-${crypto.randomUUID()}`;
64
+ const firstInput = userMessage(conversationId, "input-1");
65
+ const firstRun = queuedRun(conversationId, firstInput.id, "run-1");
66
+ const accepted = await store.acceptInputAndAssignRun({
67
+ idempotencyKey: "request-1",
68
+ input: firstInput,
69
+ run: firstRun
70
+ });
71
+ requireOutcome(accepted, "applied");
72
+ const duplicate = await store.acceptInputAndAssignRun({
73
+ idempotencyKey: "request-1",
74
+ input: userMessage(conversationId, "discarded-input"),
75
+ run: queuedRun(conversationId, "discarded-input", "discarded-run")
76
+ });
77
+ requireOutcome(duplicate, "duplicate");
78
+ if (duplicate.input.id !== firstInput.id || duplicate.inputMessageId !== firstInput.id || duplicate.runId !== firstRun.id || duplicate.assistantMessageId !== firstRun.assistantMessageId) {
79
+ throw new Error("Duplicate admission did not return its original durable identity");
80
+ }
81
+ const secondInput = userMessage(conversationId, "input-2");
82
+ const coalesced = await store.acceptInputAndAssignRun({
83
+ idempotencyKey: "request-2",
84
+ input: secondInput,
85
+ run: queuedRun(conversationId, secondInput.id, "discarded-coalesced-run"),
86
+ coalesceIntoRunId: firstRun.id
87
+ });
88
+ requireOutcome(coalesced, "applied");
89
+ const assigned = coalesced.snapshot.runs.find((run) => run.id === firstRun.id);
90
+ if (assigned?.inputMessageIds.join(",") !== "input-1,input-2") {
91
+ throw new Error("Coalesced admission did not preserve ordered input identities");
92
+ }
93
+ const collidingInput = userMessage(conversationId, assigned.assistantMessageId);
94
+ await store.acceptInputAndAssignRun({
95
+ idempotencyKey: "request-collision",
96
+ input: collidingInput,
97
+ run: queuedRun(conversationId, collidingInput.id, "discarded-collision-run"),
98
+ coalesceIntoRunId: assigned.id
99
+ }).then(() => {
100
+ throw new Error("Coalesced input reused the reserved assistant identity");
101
+ }, (error) => {
102
+ if (!(error instanceof TypeError))
103
+ throw error;
104
+ });
105
+ await store.replaceCompactedRange({
106
+ conversationId,
107
+ expectedVersion: coalesced.snapshot.version,
108
+ replacedMessageIds: [firstInput.id, secondInput.id],
109
+ summary: AgentMessageSchema.parse({
110
+ schemaVersion: 1,
111
+ id: assigned.assistantMessageId,
112
+ conversationId,
113
+ role: "summary",
114
+ status: "committed",
115
+ parts: [{ type: "text", text: "invalid reserved identity" }],
116
+ createdAt: "2026-08-22T00:00:02.000Z",
117
+ updatedAt: "2026-08-22T00:00:02.000Z"
118
+ })
119
+ }).then(() => {
120
+ throw new Error("Compaction reused a reserved assistant identity");
121
+ }, (error) => {
122
+ if (!(error instanceof TypeError))
123
+ throw error;
124
+ });
125
+ const compacted = await store.replaceCompactedRange({
126
+ conversationId,
127
+ expectedVersion: coalesced.snapshot.version,
128
+ replacedMessageIds: [firstInput.id, secondInput.id],
129
+ summary: AgentMessageSchema.parse({
130
+ schemaVersion: 1,
131
+ id: "summary-1",
132
+ conversationId,
133
+ role: "summary",
134
+ status: "committed",
135
+ parts: [{ type: "text", text: "two inputs" }],
136
+ createdAt: "2026-08-22T00:00:02.000Z",
137
+ updatedAt: "2026-08-22T00:00:02.000Z"
138
+ })
139
+ });
140
+ requireOutcome(compacted, "applied");
141
+ const duplicateAfterCompaction = await store.acceptInputAndAssignRun({
142
+ idempotencyKey: "request-1",
143
+ input: userMessage(conversationId, "discarded-after-compaction"),
144
+ run: queuedRun(conversationId, "discarded-after-compaction", "discarded-run-2")
145
+ });
146
+ requireOutcome(duplicateAfterCompaction, "duplicate");
147
+ if (duplicateAfterCompaction.input.id !== firstInput.id) {
148
+ throw new Error("Compaction discarded the canonical duplicate admission input");
149
+ }
150
+ const acquired = await store.acquireRun({
151
+ conversationId,
152
+ runId: assigned.id,
153
+ expectedRevision: assigned.revision,
154
+ ownerId: "conformance-owner"
155
+ });
156
+ requireOutcome(acquired, "applied");
157
+ const running = acquired.snapshot.runs.find((run) => run.id === assigned.id);
158
+ if (!running)
159
+ throw new Error("Acquired run disappeared");
160
+ const stale = await store.checkpointRunAssistant({
161
+ conversationId,
162
+ runId: running.id,
163
+ expectedRevision: running.revision - 1,
164
+ ownerId: "conformance-owner",
165
+ assistant: AgentMessageSchema.parse({
166
+ schemaVersion: 1,
167
+ id: running.assistantMessageId,
168
+ conversationId,
169
+ runId: running.id,
170
+ role: "assistant",
171
+ status: "streaming",
172
+ parts: [],
173
+ createdAt: "2026-08-22T00:00:00.000Z",
174
+ updatedAt: "2026-08-22T00:00:01.000Z"
175
+ })
176
+ });
177
+ requireOutcome(stale, "conflict");
178
+ const abandoned = await store.recoverRun({
179
+ conversationId,
180
+ runId: running.id,
181
+ expectedRevision: running.revision,
182
+ action: "abandon"
183
+ });
184
+ requireOutcome(abandoned, "applied");
185
+ const terminalRun = abandoned.snapshot.runs.find((run) => run.id === running.id);
186
+ const terminalMessage = abandoned.snapshot.messages.find((message) => message.id === running.assistantMessageId);
187
+ if (terminalRun?.state !== "abandoned" || terminalMessage?.status !== "failed") {
188
+ throw new Error("Abandon recovery did not atomically terminalize its assistant record");
189
+ }
190
+ }
26
191
  // src/testing/surface-conformance.ts
27
192
  import { z as z2 } from "zod";
28
193
 
@@ -744,6 +909,7 @@ export {
744
909
  createHandlerTestClients,
745
910
  createRealtimeProbeDriver,
746
911
  defineRealtimeProbe,
912
+ runAgentStoreConformance,
747
913
  runSurfaceProbes,
748
914
  serializeSurfaceValue
749
915
  };
package/llms-full.txt CHANGED
@@ -3482,7 +3482,8 @@ const terminal = await ticket.result
3482
3482
 
3483
3483
  `recordIds` is optional. Supply stable application record IDs when an accepted-response transport must
3484
3484
  return durable placeholders before the run finishes. `ticket.admission` resolves after the store
3485
- acceptance CAS and reports the actually assigned `runId`, `assistantMessageId` and snapshot version.
3485
+ acceptance CAS and reports the canonical committed `input`, assigned `run`, a typed `pending`
3486
+ assistant projection, compatibility IDs and snapshot version.
3486
3487
  Those assigned IDs can differ from the proposal when an input coalesces into an existing queued
3487
3488
  successor. Reuse the same `inputMessageId` for retries carrying the same idempotency key; input
3488
3489
  identity is caller-stable, while the receipt reports the run/assistant identities that assignment
@@ -3490,8 +3491,35 @@ may change. Await `admission` first on the immediate accepted-response path. `ti
3490
3491
  remains the signal-only compatibility surface and additionally waits for admission publication.
3491
3492
 
3492
3493
  The in-memory store is a reference adapter and has process-local durability
3493
- only. Production applications implement `AgentRuntimeStore` with their own
3494
- database transaction and, when needed, distributed lease/fencing token.
3494
+ only. Production applications normally call `createAgentRuntimeStore()` and
3495
+ provide one database transaction driver:
3496
+
3497
+ ```ts
3498
+ const store = createAgentRuntimeStore({
3499
+ transaction: work => db.transaction(tx => work(tx)),
3500
+ state: {
3501
+ load: (tx, conversationId) => loadRuntimeState(tx, conversationId),
3502
+ compareAndSwap: (tx, operation) => casRuntimeState(tx, operation),
3503
+ },
3504
+ history: {
3505
+ load: (tx, conversationId) => loadCanonicalMessages(tx, conversationId),
3506
+ loadById: (tx, identity) => loadActiveOrArchivedMessage(tx, identity),
3507
+ apply: (tx, mutation) => applyCanonicalHistoryMutation(tx, mutation),
3508
+ },
3509
+ scanRecoverable: page => scanRecoverableRuns(page),
3510
+ })
3511
+ ```
3512
+
3513
+ The same opaque `tx` reaches state and history callbacks. The adapter maps rows
3514
+ and supplies atomicity; Stitchkit owns transition validation and revision
3515
+ arithmetic. The executable reference is
3516
+ [`examples/agent-store-prisma/adapter.ts`](../../examples/agent-store-prisma/adapter.ts).
3517
+ `compareAndSwap` returns either `{ outcome: 'applied' }` or
3518
+ `{ outcome: 'conflict', actualVersion }`; on a winning write it also persists the
3519
+ framework-provided `recoverable` descriptors in the same transaction. Recovery
3520
+ scans that bounded index instead of loading every aggregate. Compaction may hide
3521
+ rows from `history.load`, but `history.loadById` must retain canonical admitted
3522
+ inputs for durable duplicate receipts.
3495
3523
 
3496
3524
  ## Durable order
3497
3525
 
@@ -3533,8 +3561,8 @@ hatch.
3533
3561
 
3534
3562
  ## Store operations
3535
3563
 
3536
- An adapter implements the aggregate `AgentRuntimeStore`, not separate message
3537
- and run CRUD stores:
3564
+ `AgentRuntimeStore` remains the runtime-facing aggregate. Application adapters
3565
+ implement the smaller `AgentRuntimeStoreDriver`, not these eight transitions:
3538
3566
 
3539
3567
  - `acceptInputAndAssignRun`
3540
3568
  - `acquireRun`
@@ -3549,13 +3577,13 @@ Every mutation carries an expected run revision or snapshot version. Input
3549
3577
  assignment additionally carries an idempotency identity. A conflict is a
3550
3578
  control outcome; stale data is never silently overwritten.
3551
3579
 
3552
- On startup, `scanRecoverable` returns queued/acquired records. `recoverRun`
3553
- may abandon them, or requeue an already acquired run only with explicit
3554
- `replaySafe: true` evidence. The framework never guesses that an external
3555
- side effect is replayable. After an application reconstructs its typed context,
3556
- `runtime.resume({ conversationId, runId, context })` admits that queued record
3557
- through the same acquisition CAS and coordinator lane; it never creates a
3558
- second input message.
3580
+ On startup, `runtime.recover({ resolveContext })` consumes bounded lightweight
3581
+ pages. Its safe default resumes queued runs and reports acquired or
3582
+ `interrupt_requested` runs as skipped. A policy may requeue acquired work only
3583
+ with explicit replay-safe evidence, or abandon it only with stale-owner
3584
+ evidence. Each attempted run returns its own outcome/error; `pageSize`,
3585
+ `maxRuns`, and `signal` bound the pass. `runtime.resume(...)` remains available
3586
+ for one known queued record.
3559
3587
 
3560
3588
  Canonical records currently write `schemaVersion: 1`. A durable adapter owns
3561
3589
  read-time migration of older rows: migrate to the current shape at its storage
@@ -3567,14 +3595,24 @@ or silently accept an unknown future version.
3567
3595
 
3568
3596
  `publish` receives event classes with different guarantees:
3569
3597
 
3598
+ - `admission` follows a successful acceptance CAS and carries the same complete
3599
+ projection as `ticket.admission`;
3600
+
3570
3601
  - `assistant-delta` is transient and ordered by
3571
3602
  `(runId, runtimeEpoch, sequence)`;
3603
+ - `reasoning-start`, `reasoning-delta` and `reasoning-end` are transient and
3604
+ ordered by the same identity; delta carries only the current text fragment,
3605
+ while provider metadata stays inside a validated canonical envelope;
3572
3606
  - `assistant-checkpoint` follows a successful checkpoint CAS;
3573
3607
  - `run-state` follows durable queue/acquire/interrupt transitions;
3574
3608
  - `tool-status` is transient lifecycle presentation with JSON-safe input on
3575
3609
  start and output on completion; internal tool failures remain generic;
3576
3610
  - `terminal` follows the winning terminal CAS.
3577
3611
 
3612
+ These are post-commit notifications, not a transactional outbox: a process can
3613
+ crash between the database commit and `publish`. Reconnect should load canonical
3614
+ state. Exactly-once external delivery remains an application-owned outbox.
3615
+
3578
3616
  A named custom stop condition terminalizes with `policy_stop`; its `policyName`
3579
3617
  is persisted on the run and included in the terminal event/result. `max-steps`
3580
3618
  is the reserved built-in policy name. `loop.prepareStep` is the controlled AI
@@ -5129,7 +5167,7 @@ never-leak-an-internal-message rule for a raw throw):
5129
5167
 
5130
5168
  ```ts
5131
5169
  const onError = createErrorHook({
5132
- // Map the codes you have an opinion about; the rest travel as themselves.
5170
+ // Map the codes you have an opinion about.
5133
5171
  codeMap: {
5134
5172
  BAD_REQUEST: 'bad_request', VALIDATION_ERROR: 'bad_request',
5135
5173
  UNAUTHORIZED: 'unauthenticated', FORBIDDEN: 'forbidden',
@@ -5137,6 +5175,9 @@ const onError = createErrorHook({
5137
5175
  CONFLICT: 'conflict', RATE_LIMITED: 'rate_limited',
5138
5176
  INTERNAL_SERVER_ERROR: 'internal',
5139
5177
  },
5178
+ // Optional: one public vocabulary entry for every other stitchkit code.
5179
+ // A resolver `(code) => ...` is also accepted for grouping code families.
5180
+ unmappedCode: 'framework_error',
5140
5181
  // `ctx` is the request's RuntimeContext — read `ctx.traceId` for a
5141
5182
  // correlation id in the envelope. Declaring it is optional.
5142
5183
  render: (info, ctx) => ({
@@ -5149,10 +5190,13 @@ const onError = createErrorHook({
5149
5190
  createServer({ services, hooks: { onError } })
5150
5191
  ```
5151
5192
 
5152
- `codeMap` is partial: map the codes you have an opinion about. A stitchkit code
5153
- you did not list travels as itself the same thing a code you threw yourself
5154
- always did. That is what keeps a code added by a future release from being a
5155
- compile break for every project that translates codes at all.
5193
+ `codeMap` is partial: map the codes you have an opinion about. By default a
5194
+ stitchkit code you did not list travels as itself. Set `unmappedCode` to one
5195
+ wire-code when your public vocabulary has a catch-all, or to a function such as
5196
+ `(code) => code.startsWith('FILE_') ? 'storage_error' : 'framework_error'` when
5197
+ framework code families need different buckets. An explicit `codeMap` entry
5198
+ always wins. Codes your project throws itself do not belong to Stitchkit's
5199
+ vocabulary, so they never pass through this fallback and remain unchanged.
5156
5200
 
5157
5201
  One `satisfies` is the **opt-in** to the stricter deal:
5158
5202
 
@@ -5164,9 +5208,7 @@ That makes the map exhaustive on your side, so a release that adds a code stops
5164
5208
  your build until you decide what the new code is called on your wire. Take it
5165
5209
  when your envelope is a published contract and a code surfacing in stitchkit's
5166
5210
  spelling would violate it; leave it off when passing one through is fine.
5167
- Neither choice is silent — the changelog names every added code. For a single
5168
- catch-all instead of either, decide it in `render`, where `info.code` is the
5169
- resolved value.
5211
+ Neither choice is silent — the changelog names every added code.
5170
5212
 
5171
5213
  Both `onError` and `render` may be asynchronous and receive the matched endpoint
5172
5214
  as their final argument. The observer is awaited before rendering, so it can
@@ -6503,6 +6545,34 @@ current one *up to* your target, and apply each snippet.
6503
6545
  runtime): bootstrap the server, one HTTP request, and any feature you rely on
6504
6546
  (Socket.IO connect, an MCP tool call, a multipart upload, …).
6505
6547
 
6548
+ ## Unreleased migration: complete agent admission identity
6549
+
6550
+ Custom `AgentRuntimeStore` adapters must persist and return the input and
6551
+ assistant identities associated with an idempotency key:
6552
+
6553
+ ```ts
6554
+ // before
6555
+ return { outcome: 'duplicate', runId, snapshot }
6556
+
6557
+ // after
6558
+ return { outcome: 'duplicate', input, inputMessageId, runId, assistantMessageId, snapshot }
6559
+ ```
6560
+
6561
+ Prefer replacing the custom aggregate reducer with `createAgentRuntimeStore()`;
6562
+ its `AgentStoredState.admissions` record and transaction driver implement this
6563
+ contract automatically. `history.loadById()` must retain access to compacted
6564
+ admitted inputs so the framework can return the canonical record.
6565
+
6566
+ `AgentRuntimeEvent` also adds a post-commit `admission` variant. Add it to any
6567
+ exhaustive publisher switch. Its `assistant` is either the pending placeholder
6568
+ for a new assignment or the canonical persisted assistant for a duplicate:
6569
+
6570
+ ```ts
6571
+ case 'admission':
6572
+ await persistProductProjection(event.input, event.run, event.assistant)
6573
+ break
6574
+ ```
6575
+
6506
6576
  ## Released migration: 0.56.0
6507
6577
 
6508
6578
  ### Surface manifests are version 2
@@ -7816,7 +7886,7 @@ Also re-exports the error helpers from `stitchkit/contract`.
7816
7886
  | `createAuthHook` | function | one scope gate for HTTP `authorize` and tool `beforeHandle` — [guide](../guide/auth-and-errors.md#createauthhook) |
7817
7887
  | `composeAuthHooks` | function | route multiple canonical auth domains by owned scope and atomically commit their typed contributions |
7818
7888
  | `createErrorHook` | function | an async-capable, endpoint-aware `onError` hook from a code map + envelope renderer — [guide](../guide/auth-and-errors.md#createerrorhook) |
7819
- | `ErrorHookConfig` | _type_ | async observer/renderer config for `createErrorHook` |
7889
+ | `ErrorHookConfig` | _type_ | async observer/renderer config with partial `codeMap` and optional typed `unmappedCode` fallback |
7820
7890
  | `ResolvedError` | _type_ | the normalised error handed to `createErrorHook`'s `render` |
7821
7891
  | `createBearerResolver` | function | a bearer-token identity resolver |
7822
7892
  | `signJwt` | function | sign an HS256 JWT |
@@ -7942,6 +8012,10 @@ Server-only optional application runtime. See the
7942
8012
  | `defineAgentProtocol` | function | declare and validate context, input metadata and canonical message parts |
7943
8013
  | `AgentMessageSchema` / `AgentRunSchema` / `AgentSnapshotSchema` | schema | versioned canonical engine records |
7944
8014
  | `AgentRuntimeStore` | _type_ | aggregate CAS transaction boundary for message, run and compaction mutations |
8015
+ | `createAgentRuntimeStore` | function | build the aggregate store from one coherent transaction driver; framework owns every state transition |
8016
+ | `AgentRuntimeStoreDriver` | _type_ | ORM-neutral transactional state load/exact-version CAS, active-plus-archived history codec and bounded recoverable-run index scan |
8017
+ | `AgentStoredStateSchema` | schema | versioned runs and full idempotency admission identities without duplicated message history |
8018
+ | `AgentHistoryMutationSchema` | schema | typed canonical message mutation applied inside the winning state transaction |
7945
8019
  | `RecoverAgentRunSchema` | schema | explicit abandon/requeue recovery decision; acquired runs require replay-safe evidence |
7946
8020
  | `createMemoryAgentRuntimeStore` | function | process-local reference adapter, not production durability |
7947
8021
  | `projectAgentHistory` | function | asynchronously project canonical records and resolved multimodal files into provider-valid AI SDK messages |
@@ -7952,11 +8026,14 @@ Server-only optional application runtime. See the
7952
8026
  | `AgentRuntimeStopPolicy` | _type_ | named custom AI SDK stop condition persisted and published on policy stop |
7953
8027
  | `AgentRuntimePrepareStep` | _type_ | per-run controlled step callback with typed domain context and managed run signal/fence |
7954
8028
  | `AgentRuntimeRecordIds` | _type_ | optional caller-provided input, run and assistant IDs for stable application records |
7955
- | `AgentRuntimeAdmission` | _type_ | actual assigned run/assistant identity and snapshot version after durable admission |
8029
+ | `AgentRuntimeAdmission` | _type_ | canonical committed input, assigned run, pending assistant projection, compatibility IDs and snapshot version |
8030
+ | `AgentAdmissionEventSchema` | schema | post-commit admission projection; removes store rereads but does not imply exactly-once delivery |
8031
+ | `AgentRunMetricsSchema` | schema | optional provenance-aware usage and timings; `partial` distinguishes checkpoint from terminal totals |
8032
+ | `AgentRuntimeRecoverOptions` | _type_ | bounded paged startup recovery with context resolver and explicit evidence policy |
7956
8033
  | `AgentSessionCloseOptions` | _type_ | natural `drainTimeoutMs` followed by shutdown abort and optional bounded `forceTimeoutMs` settlement wait |
7957
8034
  | `AgentHistoryProjectionOptions` | _type_ | storage-neutral file resolver and explicit unresolved-file behavior |
7958
8035
  | `createAgentToolFenceLifecycle` | function | pre-effect and post-effect run ownership fence for `mountAgent` |
7959
- | `AgentRuntimeEventSchema` | schema | transient delta, durable checkpoint/run-state/tool and terminal event union |
8036
+ | `AgentRuntimeEventSchema` | schema | transient stream lifecycle plus post-commit admission/checkpoint/run-state/terminal projections |
7960
8037
  | `createAgentObservability` | function | separate agent-run sink over the shared bounded observability lifecycle |
7961
8038
 
7962
8039
  ## `stitchkit/agent-runtime/openrouter`
@@ -8294,6 +8371,8 @@ handler pipeline without opening a TCP port.
8294
8371
  |--------|------|---------|
8295
8372
  | `createHandlerTestClient` | function | one contract client backed by an in-process `FetchHandler` |
8296
8373
  | `createHandlerTestClients` | function | exact contract-registry batch form |
8374
+ | `runAgentStoreConformance` | function | reusable black-box duplicate/coalescing/stale/recovery contract for durable agent-store adapters |
8375
+ | `AgentStoreConformanceConfig` | _type_ | factory configuration for running the same contract against a fresh adapter |
8297
8376
  | `HandlerTestClientDefaults` | _type_ | ordinary bare-client defaults with handler-owned `baseUrl` and `fetch` removed |
8298
8377
  | `HandlerTestClientConfig` | _type_ | handler, contract, path prefix, scoped config and client request defaults |
8299
8378
  | `HandlerTestClientsConfig` | _type_ | batch helper configuration |