stitchkit 0.56.5 → 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,6 +3595,9 @@ 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)`;
3572
3603
  - `reasoning-start`, `reasoning-delta` and `reasoning-end` are transient and
@@ -3578,6 +3609,10 @@ or silently accept an unknown future version.
3578
3609
  start and output on completion; internal tool failures remain generic;
3579
3610
  - `terminal` follows the winning terminal CAS.
3580
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
+
3581
3616
  A named custom stop condition terminalizes with `policy_stop`; its `policyName`
3582
3617
  is persisted on the run and included in the terminal event/result. `max-steps`
3583
3618
  is the reserved built-in policy name. `loop.prepareStep` is the controlled AI
@@ -5132,7 +5167,7 @@ never-leak-an-internal-message rule for a raw throw):
5132
5167
 
5133
5168
  ```ts
5134
5169
  const onError = createErrorHook({
5135
- // Map the codes you have an opinion about; the rest travel as themselves.
5170
+ // Map the codes you have an opinion about.
5136
5171
  codeMap: {
5137
5172
  BAD_REQUEST: 'bad_request', VALIDATION_ERROR: 'bad_request',
5138
5173
  UNAUTHORIZED: 'unauthenticated', FORBIDDEN: 'forbidden',
@@ -5140,6 +5175,9 @@ const onError = createErrorHook({
5140
5175
  CONFLICT: 'conflict', RATE_LIMITED: 'rate_limited',
5141
5176
  INTERNAL_SERVER_ERROR: 'internal',
5142
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',
5143
5181
  // `ctx` is the request's RuntimeContext — read `ctx.traceId` for a
5144
5182
  // correlation id in the envelope. Declaring it is optional.
5145
5183
  render: (info, ctx) => ({
@@ -5152,10 +5190,13 @@ const onError = createErrorHook({
5152
5190
  createServer({ services, hooks: { onError } })
5153
5191
  ```
5154
5192
 
5155
- `codeMap` is partial: map the codes you have an opinion about. A stitchkit code
5156
- you did not list travels as itself the same thing a code you threw yourself
5157
- always did. That is what keeps a code added by a future release from being a
5158
- 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.
5159
5200
 
5160
5201
  One `satisfies` is the **opt-in** to the stricter deal:
5161
5202
 
@@ -5167,9 +5208,7 @@ That makes the map exhaustive on your side, so a release that adds a code stops
5167
5208
  your build until you decide what the new code is called on your wire. Take it
5168
5209
  when your envelope is a published contract and a code surfacing in stitchkit's
5169
5210
  spelling would violate it; leave it off when passing one through is fine.
5170
- Neither choice is silent — the changelog names every added code. For a single
5171
- catch-all instead of either, decide it in `render`, where `info.code` is the
5172
- resolved value.
5211
+ Neither choice is silent — the changelog names every added code.
5173
5212
 
5174
5213
  Both `onError` and `render` may be asynchronous and receive the matched endpoint
5175
5214
  as their final argument. The observer is awaited before rendering, so it can
@@ -6506,6 +6545,34 @@ current one *up to* your target, and apply each snippet.
6506
6545
  runtime): bootstrap the server, one HTTP request, and any feature you rely on
6507
6546
  (Socket.IO connect, an MCP tool call, a multipart upload, …).
6508
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
+
6509
6576
  ## Released migration: 0.56.0
6510
6577
 
6511
6578
  ### Surface manifests are version 2
@@ -7819,7 +7886,7 @@ Also re-exports the error helpers from `stitchkit/contract`.
7819
7886
  | `createAuthHook` | function | one scope gate for HTTP `authorize` and tool `beforeHandle` — [guide](../guide/auth-and-errors.md#createauthhook) |
7820
7887
  | `composeAuthHooks` | function | route multiple canonical auth domains by owned scope and atomically commit their typed contributions |
7821
7888
  | `createErrorHook` | function | an async-capable, endpoint-aware `onError` hook from a code map + envelope renderer — [guide](../guide/auth-and-errors.md#createerrorhook) |
7822
- | `ErrorHookConfig` | _type_ | async observer/renderer config for `createErrorHook` |
7889
+ | `ErrorHookConfig` | _type_ | async observer/renderer config with partial `codeMap` and optional typed `unmappedCode` fallback |
7823
7890
  | `ResolvedError` | _type_ | the normalised error handed to `createErrorHook`'s `render` |
7824
7891
  | `createBearerResolver` | function | a bearer-token identity resolver |
7825
7892
  | `signJwt` | function | sign an HS256 JWT |
@@ -7945,6 +8012,10 @@ Server-only optional application runtime. See the
7945
8012
  | `defineAgentProtocol` | function | declare and validate context, input metadata and canonical message parts |
7946
8013
  | `AgentMessageSchema` / `AgentRunSchema` / `AgentSnapshotSchema` | schema | versioned canonical engine records |
7947
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 |
7948
8019
  | `RecoverAgentRunSchema` | schema | explicit abandon/requeue recovery decision; acquired runs require replay-safe evidence |
7949
8020
  | `createMemoryAgentRuntimeStore` | function | process-local reference adapter, not production durability |
7950
8021
  | `projectAgentHistory` | function | asynchronously project canonical records and resolved multimodal files into provider-valid AI SDK messages |
@@ -7955,11 +8026,14 @@ Server-only optional application runtime. See the
7955
8026
  | `AgentRuntimeStopPolicy` | _type_ | named custom AI SDK stop condition persisted and published on policy stop |
7956
8027
  | `AgentRuntimePrepareStep` | _type_ | per-run controlled step callback with typed domain context and managed run signal/fence |
7957
8028
  | `AgentRuntimeRecordIds` | _type_ | optional caller-provided input, run and assistant IDs for stable application records |
7958
- | `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 |
7959
8033
  | `AgentSessionCloseOptions` | _type_ | natural `drainTimeoutMs` followed by shutdown abort and optional bounded `forceTimeoutMs` settlement wait |
7960
8034
  | `AgentHistoryProjectionOptions` | _type_ | storage-neutral file resolver and explicit unresolved-file behavior |
7961
8035
  | `createAgentToolFenceLifecycle` | function | pre-effect and post-effect run ownership fence for `mountAgent` |
7962
- | `AgentRuntimeEventSchema` | schema | transient text/reasoning/tool lifecycle, durable checkpoint/run-state and terminal event union |
8036
+ | `AgentRuntimeEventSchema` | schema | transient stream lifecycle plus post-commit admission/checkpoint/run-state/terminal projections |
7963
8037
  | `createAgentObservability` | function | separate agent-run sink over the shared bounded observability lifecycle |
7964
8038
 
7965
8039
  ## `stitchkit/agent-runtime/openrouter`
@@ -8297,6 +8371,8 @@ handler pipeline without opening a TCP port.
8297
8371
  |--------|------|---------|
8298
8372
  | `createHandlerTestClient` | function | one contract client backed by an in-process `FetchHandler` |
8299
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 |
8300
8376
  | `HandlerTestClientDefaults` | _type_ | ordinary bare-client defaults with handler-owned `baseUrl` and `fetch` removed |
8301
8377
  | `HandlerTestClientConfig` | _type_ | handler, contract, path prefix, scoped config and client request defaults |
8302
8378
  | `HandlerTestClientsConfig` | _type_ | batch helper configuration |