stitchkit 0.70.4 → 0.70.6

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 (35) hide show
  1. package/dist/agent-runtime/purge.d.ts +28 -0
  2. package/dist/agent-runtime/purge.d.ts.map +1 -0
  3. package/dist/agent-runtime/runtime.d.ts.map +1 -1
  4. package/dist/agent-runtime/sqlite-purge.d.ts +7 -0
  5. package/dist/agent-runtime/sqlite-purge.d.ts.map +1 -0
  6. package/dist/agent-runtime/sqlite.d.ts.map +1 -1
  7. package/dist/agent-runtime/store-driver.d.ts +2 -0
  8. package/dist/agent-runtime/store-driver.d.ts.map +1 -1
  9. package/dist/agent-runtime/store-purge.d.ts +13 -0
  10. package/dist/agent-runtime/store-purge.d.ts.map +1 -0
  11. package/dist/agent-runtime/store.d.ts +3 -0
  12. package/dist/agent-runtime/store.d.ts.map +1 -1
  13. package/dist/agent-runtime-harness.js +1 -1
  14. package/dist/agent-runtime-sqlite-bun.js +2 -2
  15. package/dist/agent-runtime-sqlite-node.js +2 -2
  16. package/dist/agent-runtime.d.ts +2 -0
  17. package/dist/agent-runtime.d.ts.map +1 -1
  18. package/dist/agent-runtime.js +11 -3
  19. package/dist/application.js +2 -2
  20. package/dist/{index-jw81xr75.js → index-19eet1qx.js} +127 -51
  21. package/dist/{index-7wpgrqa8.js → index-3z73fh2c.js} +93 -7
  22. package/dist/{index-35wcrke5.js → index-by57nwhc.js} +3 -0
  23. package/dist/{index-9sx8tbz2.js → index-cksjz4eg.js} +50 -1
  24. package/dist/{index-c97p90dc.js → index-cx84zg25.js} +82 -24
  25. package/dist/{index-w0445741.js → index-hvftzz91.js} +1 -1
  26. package/dist/node.js +2 -2
  27. package/dist/server/contract-stream.d.ts.map +1 -1
  28. package/dist/server/http-stream-lifetime.d.ts +19 -0
  29. package/dist/server/http-stream-lifetime.d.ts.map +1 -0
  30. package/dist/server/index.js +2 -2
  31. package/dist/server/shutdown.d.ts.map +1 -1
  32. package/dist/server/streaming-route.d.ts.map +1 -1
  33. package/dist/testing.js +2 -2
  34. package/llms-full.txt +87 -5
  35. package/package.json +1 -1
@@ -30,19 +30,49 @@ var AgentConversationMessagePageSchema = z.object({
30
30
  nextCursor: z.string().min(1).optional()
31
31
  }).strict();
32
32
 
33
- // src/agent-runtime/store.ts
33
+ // src/agent-runtime/purge.ts
34
34
  import { z as z2 } from "zod";
35
- var AgentStoreConflictSchema = z2.object({
36
- outcome: z2.literal("conflict"),
35
+ var AgentConversationPurgeInputSchema = z2.strictObject({
36
+ conversationId: AgentRecordIdSchema,
37
+ expectedVersion: AgentRecordVersionSchema.optional()
38
+ });
39
+ var AgentConversationPurgeResultSchema = z2.discriminatedUnion("outcome", [
40
+ z2.strictObject({ outcome: z2.literal("purged") }),
41
+ z2.strictObject({ outcome: z2.literal("already_purged") }),
42
+ z2.strictObject({ outcome: z2.literal("unsupported") }),
43
+ z2.strictObject({
44
+ outcome: z2.literal("active"),
45
+ runIds: z2.array(AgentRecordIdSchema).min(1)
46
+ }),
47
+ z2.strictObject({ outcome: z2.literal("conflict"), actualVersion: AgentRecordVersionSchema })
48
+ ]);
49
+
50
+ class AgentConversationPurgedError extends Error {
51
+ constructor() {
52
+ super("Agent conversation has been purged; use a new conversation ID");
53
+ this.name = "AgentConversationPurgedError";
54
+ }
55
+ }
56
+ async function purgeAgentConversation(store, input) {
57
+ const parsed = AgentConversationPurgeInputSchema.parse(input);
58
+ if (!store.purgeConversation)
59
+ return { outcome: "unsupported" };
60
+ return AgentConversationPurgeResultSchema.parse(await store.purgeConversation(parsed));
61
+ }
62
+
63
+ // src/agent-runtime/store.ts
64
+ import { z as z3 } from "zod";
65
+ var AgentStoreConflictSchema = z3.object({
66
+ outcome: z3.literal("conflict"),
37
67
  actualVersion: AgentRecordVersionSchema
38
68
  });
39
- var AgentStoreNotFoundSchema = z2.object({ outcome: z2.literal("not_found") });
40
- var AgentStoreAppliedSchema = z2.object({
41
- outcome: z2.literal("applied"),
69
+ var AgentStoreNotFoundSchema = z3.object({ outcome: z3.literal("not_found") });
70
+ var AgentStoreAppliedSchema = z3.object({
71
+ outcome: z3.literal("applied"),
42
72
  snapshot: AgentSnapshotSchema
43
73
  });
44
- var AgentStoreDuplicateSchema = z2.object({
45
- outcome: z2.literal("duplicate"),
74
+ var AgentStoreDuplicateSchema = z3.object({
75
+ outcome: z3.literal("duplicate"),
46
76
  input: AgentMessageSchema,
47
77
  inputMessageId: AgentRecordIdSchema,
48
78
  runId: AgentRecordIdSchema,
@@ -51,116 +81,141 @@ var AgentStoreDuplicateSchema = z2.object({
51
81
  assistant: AgentMessageSchema.optional(),
52
82
  snapshot: AgentSnapshotSchema
53
83
  });
54
- var AgentStoreMutationResultSchema = z2.discriminatedUnion("outcome", [
84
+ var AgentStoreMutationResultSchema = z3.discriminatedUnion("outcome", [
55
85
  AgentStoreAppliedSchema,
56
86
  AgentStoreDuplicateSchema,
57
87
  AgentStoreConflictSchema,
58
88
  AgentStoreNotFoundSchema
59
89
  ]);
60
- var AgentRunViewSchema = z2.object({
90
+ var AgentRunViewSchema = z3.object({
61
91
  snapshotVersion: AgentRecordVersionSchema,
62
92
  run: AgentRunSchema,
63
93
  assistant: AgentMessageSchema.optional()
64
94
  });
65
- var AcceptInputAndAssignRunSchema = z2.object({
66
- idempotencyKey: z2.string().min(1),
95
+ var AcceptInputAndAssignRunSchema = z3.object({
96
+ idempotencyKey: z3.string().min(1),
67
97
  expectedVersion: AgentRecordVersionSchema.optional(),
68
98
  input: AgentMessageSchema,
69
99
  run: AgentRunSchema,
70
100
  coalesceIntoRunId: AgentRecordIdSchema.optional()
71
101
  });
72
- var AcquireAgentRunSchema = z2.object({
102
+ var AcquireAgentRunSchema = z3.object({
73
103
  conversationId: AgentRecordIdSchema,
74
104
  runId: AgentRecordIdSchema,
75
105
  expectedRevision: AgentRecordVersionSchema,
76
- ownerId: z2.string().min(1)
106
+ ownerId: z3.string().min(1)
77
107
  });
78
- var CheckpointRunAssistantSchema = z2.object({
108
+ var CheckpointRunAssistantSchema = z3.object({
79
109
  conversationId: AgentRecordIdSchema,
80
110
  runId: AgentRecordIdSchema,
81
111
  expectedRevision: AgentRecordVersionSchema,
82
- ownerId: z2.string().min(1),
112
+ ownerId: z3.string().min(1),
83
113
  fencingToken: AgentRecordVersionSchema.optional(),
84
114
  assistant: AgentMessageSchema,
85
115
  usage: AgentUsageSchema.optional()
86
116
  });
87
- var CommitRunTerminalSchema = z2.object({
117
+ var CommitRunTerminalSchema = z3.object({
88
118
  conversationId: AgentRecordIdSchema,
89
119
  runId: AgentRecordIdSchema,
90
120
  expectedRevision: AgentRecordVersionSchema,
91
- ownerId: z2.string().min(1),
121
+ ownerId: z3.string().min(1),
92
122
  fencingToken: AgentRecordVersionSchema.optional(),
93
123
  assistant: AgentMessageSchema,
94
124
  reason: AgentTerminalReasonSchema,
95
- policyName: z2.string().min(1).optional(),
125
+ policyName: z3.string().min(1).optional(),
96
126
  usage: AgentUsageSchema.optional(),
97
- absorb: z2.array(z2.object({
127
+ absorb: z3.array(z3.object({
98
128
  runId: AgentRecordIdSchema,
99
- inputMessageIds: z2.array(AgentRecordIdSchema).min(1)
129
+ inputMessageIds: z3.array(AgentRecordIdSchema).min(1)
100
130
  })).min(1).optional()
101
131
  });
102
- var RequestRunInterruptSchema = z2.object({
132
+ var RequestRunInterruptSchema = z3.object({
103
133
  conversationId: AgentRecordIdSchema,
104
134
  runId: AgentRecordIdSchema,
105
135
  expectedRevision: AgentRecordVersionSchema
106
136
  });
107
- var RecoverAgentRunSchema = z2.object({
137
+ var RecoverAgentRunSchema = z3.object({
108
138
  conversationId: AgentRecordIdSchema,
109
139
  runId: AgentRecordIdSchema,
110
140
  expectedRevision: AgentRecordVersionSchema,
111
- action: z2.enum(["requeue", "abandon"]),
112
- replaySafe: z2.boolean().optional()
141
+ action: z3.enum(["requeue", "abandon"]),
142
+ replaySafe: z3.boolean().optional()
113
143
  });
114
- var ReplaceCompactedRangeSchema = z2.object({
144
+ var ReplaceCompactedRangeSchema = z3.object({
115
145
  conversationId: AgentRecordIdSchema,
116
146
  expectedVersion: AgentRecordVersionSchema,
117
- replacedMessageIds: z2.array(AgentRecordIdSchema).min(1),
147
+ replacedMessageIds: z3.array(AgentRecordIdSchema).min(1),
118
148
  summary: AgentMessageSchema
119
149
  });
120
- var AgentRecoverableDescriptorSchema = z2.object({
150
+ var AgentRecoverableDescriptorSchema = z3.object({
121
151
  conversationId: AgentRecordIdSchema,
122
152
  run: AgentRunSchema
123
153
  });
124
- var AgentRecoverablePageSchema = z2.object({
125
- items: z2.array(AgentRecoverableDescriptorSchema),
126
- nextCursor: z2.string().min(1).optional()
154
+ var AgentRecoverablePageSchema = z3.object({
155
+ items: z3.array(AgentRecoverableDescriptorSchema),
156
+ nextCursor: z3.string().min(1).optional()
127
157
  });
128
158
 
129
159
  // src/agent-runtime/store-driver.ts
130
- import { z as z3 } from "zod";
131
- var AgentRuntimeHeadSchema = z3.object({
132
- schemaVersion: z3.literal(1),
160
+ import { z as z4 } from "zod";
161
+
162
+ // src/agent-runtime/store-purge.ts
163
+ function createStoreConversationPurge(driver, conversations) {
164
+ return async (input) => {
165
+ const { conversationId, expectedVersion } = AgentConversationPurgeInputSchema.parse(input);
166
+ return driver.transaction(async (transaction) => {
167
+ if (await conversations.isPurged(transaction, conversationId)) {
168
+ return { outcome: "already_purged" };
169
+ }
170
+ const head = await driver.head.load(transaction, conversationId);
171
+ const actualVersion = head?.version ?? 0;
172
+ if (expectedVersion !== undefined && expectedVersion !== actualVersion) {
173
+ return { outcome: "conflict", actualVersion };
174
+ }
175
+ const active = await driver.runs.listActive(transaction, conversationId);
176
+ if (active.length > 0) {
177
+ return { outcome: "active", runIds: active.map((record) => record.run.id) };
178
+ }
179
+ await conversations.remove(transaction, conversationId);
180
+ return { outcome: "purged" };
181
+ });
182
+ };
183
+ }
184
+
185
+ // src/agent-runtime/store-driver.ts
186
+ var AgentRuntimeHeadSchema = z4.object({
187
+ schemaVersion: z4.literal(1),
133
188
  conversationId: AgentRecordIdSchema,
134
189
  version: AgentRecordVersionSchema
135
190
  });
136
- var AgentStoredRunSchema = z3.object({
137
- schemaVersion: z3.literal(1),
191
+ var AgentStoredRunSchema = z4.object({
192
+ schemaVersion: z4.literal(1),
138
193
  run: AgentRunSchema,
139
194
  terminalAssistant: AgentMessageSchema.optional()
140
195
  });
141
- var AgentAdmissionReceiptSchema = z3.object({
142
- schemaVersion: z3.literal(1),
196
+ var AgentAdmissionReceiptSchema = z4.object({
197
+ schemaVersion: z4.literal(1),
143
198
  conversationId: AgentRecordIdSchema,
144
- idempotencyKey: z3.string().min(1),
199
+ idempotencyKey: z4.string().min(1),
145
200
  input: AgentMessageSchema,
146
201
  runId: AgentRecordIdSchema,
147
202
  assistantMessageId: AgentRecordIdSchema
148
203
  });
149
- var AgentHistoryMutationSchema = z3.discriminatedUnion("type", [
150
- z3.object({ type: z3.literal("admit"), input: AgentMessageSchema }),
151
- z3.object({
152
- type: z3.literal("upsert-assistant"),
204
+ var AgentHistoryMutationSchema = z4.discriminatedUnion("type", [
205
+ z4.object({ type: z4.literal("admit"), input: AgentMessageSchema }),
206
+ z4.object({
207
+ type: z4.literal("upsert-assistant"),
153
208
  message: AgentMessageSchema
154
209
  }),
155
- z3.object({
156
- type: z3.literal("replace-compacted-range"),
157
- replacedMessageIds: z3.array(AgentRecordIdSchema).min(1),
210
+ z4.object({
211
+ type: z4.literal("replace-compacted-range"),
212
+ replacedMessageIds: z4.array(AgentRecordIdSchema).min(1),
158
213
  summary: AgentMessageSchema
159
214
  })
160
215
  ]);
161
- var AgentRecoverableScanInputSchema = z3.object({
162
- cursor: z3.string().min(1).optional(),
163
- limit: z3.number().int().min(1).max(1000)
216
+ var AgentRecoverableScanInputSchema = z4.object({
217
+ cursor: z4.string().min(1).optional(),
218
+ limit: z4.number().int().min(1).max(1000)
164
219
  });
165
220
  function emptyHead(conversationId) {
166
221
  return AgentRuntimeHeadSchema.parse({
@@ -264,7 +319,7 @@ function validateSnapshot(head, messages, records) {
264
319
  }
265
320
  }
266
321
  }
267
- var RecoverableCursorSchema = z3.tuple([AgentRecordIdSchema, AgentRecordIdSchema]);
322
+ var RecoverableCursorSchema = z4.tuple([AgentRecordIdSchema, AgentRecordIdSchema]);
268
323
  function recoverableCursor(input) {
269
324
  return JSON.stringify([input.conversationId, input.run.id]);
270
325
  }
@@ -622,6 +677,9 @@ function createAgentRuntimeStore(driver) {
622
677
  });
623
678
  const mutate = (operation) => driver.transaction(async (transaction) => {
624
679
  const conversationId = operationConversationId(operation);
680
+ if (await driver.conversations?.isPurged(transaction, conversationId)) {
681
+ throw new AgentConversationPurgedError;
682
+ }
625
683
  const operationRunId = operation.type === "accept" ? operation.input.coalesceIntoRunId : operation.type === "compact" ? undefined : operation.input.runId;
626
684
  const [stored, messages, activeRecords, operationRecord, duplicateReceipt] = await Promise.all([
627
685
  driver.head.load(transaction, conversationId),
@@ -724,6 +782,9 @@ function createAgentRuntimeStore(driver) {
724
782
  loadSnapshot,
725
783
  loadRun,
726
784
  listActiveRuns,
785
+ ...driver.conversations && {
786
+ purgeConversation: createStoreConversationPurge(driver, driver.conversations)
787
+ },
727
788
  acceptInputAndAssignRun: (input) => mutate({
728
789
  type: "accept",
729
790
  input: AcceptInputAndAssignRunSchema.parse(input)
@@ -768,6 +829,7 @@ function cloneHistoryMap(source) {
768
829
  ]));
769
830
  }
770
831
  function createMemoryAgentRuntimeStore() {
832
+ let purged = new Set;
771
833
  let heads = new Map;
772
834
  let runs = new Map;
773
835
  let admissions = new Map;
@@ -784,6 +846,7 @@ function createMemoryAgentRuntimeStore() {
784
846
  return;
785
847
  });
786
848
  const transaction = {
849
+ purged: new Set(purged),
787
850
  heads: cloneHeadMap(heads),
788
851
  runs: cloneNestedMap(runs, (record) => AgentStoredRunSchema.parse(structuredClone(record))),
789
852
  admissions: cloneNestedMap(admissions, (receipt) => AgentAdmissionReceiptSchema.parse(structuredClone(receipt))),
@@ -791,6 +854,7 @@ function createMemoryAgentRuntimeStore() {
791
854
  };
792
855
  try {
793
856
  const result = await work(transaction);
857
+ purged = transaction.purged;
794
858
  heads = transaction.heads;
795
859
  runs = transaction.runs;
796
860
  admissions = transaction.admissions;
@@ -800,6 +864,18 @@ function createMemoryAgentRuntimeStore() {
800
864
  release.resolve();
801
865
  }
802
866
  },
867
+ conversations: {
868
+ async isPurged(transaction, conversationId) {
869
+ return transaction.purged.has(conversationId);
870
+ },
871
+ async remove(transaction, conversationId) {
872
+ transaction.purged.add(conversationId);
873
+ transaction.heads.delete(conversationId);
874
+ transaction.runs.delete(conversationId);
875
+ transaction.admissions.delete(conversationId);
876
+ transaction.histories.delete(conversationId);
877
+ }
878
+ },
803
879
  head: {
804
880
  async load(transaction, conversationId) {
805
881
  const head = transaction.heads.get(conversationId);
@@ -912,4 +988,4 @@ function createMemoryAgentRuntimeStore() {
912
988
  return createAgentRuntimeStore(driver);
913
989
  }
914
990
 
915
- export { AgentConversationSummarySchema, AgentConversationPageSchema, AgentConversationMessagePageSchema, AgentStoreConflictSchema, AgentStoreNotFoundSchema, AgentStoreAppliedSchema, AgentStoreDuplicateSchema, AgentStoreMutationResultSchema, AgentRunViewSchema, AcceptInputAndAssignRunSchema, AcquireAgentRunSchema, CheckpointRunAssistantSchema, CommitRunTerminalSchema, RequestRunInterruptSchema, RecoverAgentRunSchema, ReplaceCompactedRangeSchema, AgentRecoverableDescriptorSchema, AgentRecoverablePageSchema, AgentRuntimeHeadSchema, AgentStoredRunSchema, AgentAdmissionReceiptSchema, AgentHistoryMutationSchema, ACTIVE_AGENT_RUN_STATES, createAgentRuntimeStore, createMemoryAgentRuntimeStore };
991
+ export { AgentConversationSummarySchema, AgentConversationPageSchema, AgentConversationMessagePageSchema, AgentConversationPurgeInputSchema, AgentConversationPurgeResultSchema, AgentConversationPurgedError, purgeAgentConversation, AgentStoreConflictSchema, AgentStoreNotFoundSchema, AgentStoreAppliedSchema, AgentStoreDuplicateSchema, AgentStoreMutationResultSchema, AgentRunViewSchema, AcceptInputAndAssignRunSchema, AcquireAgentRunSchema, CheckpointRunAssistantSchema, CommitRunTerminalSchema, RequestRunInterruptSchema, RecoverAgentRunSchema, ReplaceCompactedRangeSchema, AgentRecoverableDescriptorSchema, AgentRecoverablePageSchema, AgentRuntimeHeadSchema, AgentStoredRunSchema, AgentAdmissionReceiptSchema, AgentHistoryMutationSchema, ACTIVE_AGENT_RUN_STATES, createAgentRuntimeStore, createMemoryAgentRuntimeStore };
@@ -1,5 +1,84 @@
1
1
  // src/server/shutdown.ts
2
2
  import { z } from "zod";
3
+
4
+ // src/server/http-stream-lifetime.ts
5
+ var requests = new WeakMap;
6
+ function isStreamCancellation(error, signal) {
7
+ return signal.aborted && (error === signal.reason || error instanceof Error && error.name === "AbortError" && error.cause === signal.reason);
8
+ }
9
+ function ownHttpStream(request, stream) {
10
+ requests.get(request)?.add(stream);
11
+ }
12
+ async function settleStreamCleanup(operations) {
13
+ const results = await Promise.allSettled(operations);
14
+ const errors = results.flatMap((result) => result.status === "rejected" ? [result.reason] : []);
15
+ if (errors.length)
16
+ throw new AggregateError(errors, "HTTP stream source did not close cleanly");
17
+ }
18
+ function createHttpStreamTracker() {
19
+ const streams = new Map;
20
+ const failures = [];
21
+ let draining = false;
22
+ const assertClean = () => {
23
+ if (failures.length) {
24
+ throw new AggregateError(failures, "[stitchkit] HTTP stream cleanup failed");
25
+ }
26
+ };
27
+ return {
28
+ bind(request, onComplete) {
29
+ let handlerDone = false;
30
+ let pending = 0;
31
+ const complete = () => {
32
+ if (!handlerDone || pending !== 0)
33
+ return;
34
+ requests.delete(request);
35
+ onComplete();
36
+ };
37
+ requests.set(request, {
38
+ add(stream) {
39
+ pending += 1;
40
+ streams.set(stream, request);
41
+ stream.settled.then(() => {
42
+ streams.delete(stream);
43
+ pending -= 1;
44
+ complete();
45
+ }, (error) => {
46
+ if (draining)
47
+ failures.push(error);
48
+ else
49
+ console.error("[stitchkit] HTTP stream cleanup failed:", error);
50
+ streams.delete(stream);
51
+ pending -= 1;
52
+ complete();
53
+ });
54
+ if (draining)
55
+ stream.cancel();
56
+ }
57
+ });
58
+ return () => {
59
+ handlerDone = true;
60
+ complete();
61
+ };
62
+ },
63
+ get pendingRequests() {
64
+ return new Set(streams.values()).size;
65
+ },
66
+ cancel() {
67
+ draining = true;
68
+ for (const stream of streams.keys())
69
+ stream.cancel();
70
+ },
71
+ assertClean,
72
+ async drain() {
73
+ while (streams.size) {
74
+ await Promise.allSettled([...streams.keys()].map((stream) => stream.settled));
75
+ }
76
+ assertClean();
77
+ }
78
+ };
79
+ }
80
+
81
+ // src/server/shutdown.ts
3
82
  var ShutdownStateSchema = z.enum([
4
83
  "running",
5
84
  "draining-http",
@@ -109,13 +188,15 @@ function createServerLifecycle(getAdapter) {
109
188
  let pendingApplicationRequests = 0;
110
189
  let retryAfterSeconds = 5;
111
190
  let shutdownPromise;
191
+ const streams = createHttpStreamTracker();
192
+ const pendingRequests = () => Math.max(getAdapter().pendingRequests(), streams.pendingRequests);
112
193
  const status = () => {
113
194
  const adapter = getAdapter();
114
195
  return ShutdownStatusSchema.parse({
115
196
  state,
116
197
  acceptedRequests,
117
198
  completedRequests,
118
- pendingRequests: adapter.pendingRequests(),
199
+ pendingRequests: pendingRequests(),
119
200
  pendingWebSockets: adapter.pendingWebSockets()
120
201
  });
121
202
  };
@@ -125,11 +206,14 @@ function createServerLifecycle(getAdapter) {
125
206
  return rejectedResponse(retryAfterSeconds);
126
207
  acceptedRequests += 1;
127
208
  pendingApplicationRequests += 1;
209
+ const complete = streams.bind(request, () => {
210
+ completedRequests += 1;
211
+ });
128
212
  try {
129
213
  return await handler(request, server);
130
214
  } finally {
131
215
  pendingApplicationRequests -= 1;
132
- completedRequests += 1;
216
+ complete();
133
217
  }
134
218
  };
135
219
  };
@@ -142,6 +226,7 @@ function createServerLifecycle(getAdapter) {
142
226
  const adapter = getAdapter();
143
227
  state = "draining-http";
144
228
  adapter.beginShutdown(retryAfterSeconds);
229
+ streams.cancel();
145
230
  shutdownPromise = new Promise((resolve, reject) => {
146
231
  const phaseAbort = new AbortController;
147
232
  let forcedReason;
@@ -164,7 +249,8 @@ function createServerLifecycle(getAdapter) {
164
249
  };
165
250
  (async () => {
166
251
  try {
167
- await waitForZero(() => pendingApplicationRequests, phaseAbort.signal);
252
+ await waitForZero(() => pendingApplicationRequests + streams.pendingRequests, phaseAbort.signal);
253
+ streams.assertClean();
168
254
  if (!forcedReason) {
169
255
  state = "closing-realtime";
170
256
  const realtimeOutcome = await closeRealtimeWithin(adapter, parsed.realtimeCloseTimeoutMs, phaseAbort.signal);
@@ -189,13 +275,13 @@ function createServerLifecycle(getAdapter) {
189
275
  let pendingWebSocketsAtForce = 0;
190
276
  if (forcedReason) {
191
277
  state = "stopping-runtime";
192
- pendingRequestsAtForce = adapter.pendingRequests();
278
+ pendingRequestsAtForce = pendingRequests();
193
279
  pendingWebSocketsAtForce = adapter.pendingWebSockets();
194
280
  forcedWebSockets += pendingWebSocketsAtForce;
195
281
  let forceError;
196
282
  let forceFailed = false;
197
283
  try {
198
- await withTimeout(adapter.forceStop(), parsed.forceTimeoutMs, `[stitchkit] forced shutdown did not complete within ${parsed.forceTimeoutMs}ms`);
284
+ await withTimeout(Promise.all([adapter.forceStop(), streams.drain()]), parsed.forceTimeoutMs, `[stitchkit] forced shutdown did not complete within ${parsed.forceTimeoutMs}ms`);
199
285
  } catch (error) {
200
286
  forceFailed = true;
201
287
  forceError = error;
@@ -219,7 +305,7 @@ function createServerLifecycle(getAdapter) {
219
305
  ...forcedReason && { reason: forcedReason },
220
306
  acceptedRequests,
221
307
  completedRequests,
222
- pendingRequests: adapter.pendingRequests(),
308
+ pendingRequests: pendingRequests(),
223
309
  pendingWebSockets: adapter.pendingWebSockets(),
224
310
  pendingRequestsAtForce,
225
311
  pendingWebSocketsAtForce,
@@ -243,4 +329,4 @@ function createServerLifecycle(getAdapter) {
243
329
  };
244
330
  }
245
331
 
246
- export { ShutdownStateSchema, ShutdownOptionsSchema, ShutdownStatusSchema, ShutdownResultSchema, createServerLifecycle };
332
+ export { isStreamCancellation, ownHttpStream, settleStreamCleanup, ShutdownStateSchema, ShutdownOptionsSchema, ShutdownStatusSchema, ShutdownResultSchema, createServerLifecycle };
@@ -2182,6 +2182,9 @@ function createAgentRuntime(config) {
2182
2182
  updatedAt: nowIso
2183
2183
  });
2184
2184
  const outerAccepted = Promise.withResolvers();
2185
+ outerAccepted.promise.catch(() => {
2186
+ return;
2187
+ });
2185
2188
  const outerAdmission = Promise.withResolvers();
2186
2189
  outerAdmission.promise.catch(() => {
2187
2190
  return;
@@ -8,7 +8,7 @@ import {
8
8
  AgentRuntimeHeadSchema,
9
9
  AgentStoredRunSchema,
10
10
  createAgentRuntimeStore
11
- } from "./index-jw81xr75.js";
11
+ } from "./index-19eet1qx.js";
12
12
  import {
13
13
  AgentMessageSchema,
14
14
  AgentRunSchema
@@ -16,6 +16,52 @@ import {
16
16
 
17
17
  // src/agent-runtime/sqlite.ts
18
18
  import { z } from "zod";
19
+
20
+ // src/agent-runtime/sqlite-purge.ts
21
+ var OWNED_TABLES = [
22
+ "stitchkit_agent_runtime_messages",
23
+ "stitchkit_agent_runtime_admissions",
24
+ "stitchkit_agent_runtime_runs",
25
+ "stitchkit_agent_runtime_heads"
26
+ ];
27
+ function initializeSqliteConversationPurge(database) {
28
+ database.exec(`
29
+ CREATE TABLE IF NOT EXISTS stitchkit_agent_runtime_purged (
30
+ conversation_id TEXT PRIMARY KEY
31
+ );
32
+ `);
33
+ for (const table of OWNED_TABLES) {
34
+ for (const operation of ["INSERT", "UPDATE"]) {
35
+ database.exec(`
36
+ CREATE TRIGGER IF NOT EXISTS ${table}_purge_${operation.toLowerCase()}
37
+ BEFORE ${operation} ON ${table}
38
+ WHEN EXISTS (
39
+ SELECT 1 FROM stitchkit_agent_runtime_purged WHERE conversation_id = NEW.conversation_id
40
+ )
41
+ BEGIN SELECT RAISE(ABORT, 'Agent conversation has been purged'); END;
42
+ `);
43
+ }
44
+ }
45
+ }
46
+ function sqliteConversationPurge(database) {
47
+ const table = database.prepare("SELECT name FROM sqlite_master WHERE type = 'table' AND name = 'stitchkit_agent_runtime_purged'").get();
48
+ if (table === null || table === undefined)
49
+ return;
50
+ return {
51
+ async isPurged(transaction, conversationId) {
52
+ const row = transaction.prepare("SELECT conversation_id FROM stitchkit_agent_runtime_purged WHERE conversation_id = ?").get(conversationId);
53
+ return row !== null && row !== undefined;
54
+ },
55
+ async remove(transaction, conversationId) {
56
+ transaction.prepare("INSERT INTO stitchkit_agent_runtime_purged (conversation_id) VALUES (?)").run(conversationId);
57
+ for (const table2 of OWNED_TABLES) {
58
+ transaction.prepare(`DELETE FROM ${table2} WHERE conversation_id = ?`).run(conversationId);
59
+ }
60
+ }
61
+ };
62
+ }
63
+
64
+ // src/agent-runtime/sqlite.ts
19
65
  var HeadRowSchema = z.object({ version: z.number().int().nonnegative() });
20
66
  var RunRowSchema = z.object({
21
67
  payload: z.string(),
@@ -121,6 +167,7 @@ function initializeAgentRuntimeSqlite(database) {
121
167
  if (missingTables.length > 0) {
122
168
  throw new Error(`Refusing a partial Stitchkit agent-runtime SQLite schema; missing ${missingTables.join(", ")}`);
123
169
  }
170
+ initializeSqliteConversationPurge(database);
124
171
  database.exec("COMMIT");
125
172
  return;
126
173
  }
@@ -165,6 +212,7 @@ function initializeAgentRuntimeSqlite(database) {
165
212
  );
166
213
  INSERT INTO stitchkit_agent_runtime_meta (key, value) VALUES ('schema_version', '1');
167
214
  `);
215
+ initializeSqliteConversationPurge(database);
168
216
  database.exec("COMMIT");
169
217
  } catch (error) {
170
218
  database.exec("ROLLBACK");
@@ -197,6 +245,7 @@ function createSqliteAgentRuntimeStore(config) {
197
245
  return result;
198
246
  };
199
247
  const driver = {
248
+ conversations: sqliteConversationPurge(database),
200
249
  transaction: (work) => serial(async () => {
201
250
  database.exec("BEGIN IMMEDIATE");
202
251
  try {