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.
- package/dist/agent-runtime/purge.d.ts +28 -0
- package/dist/agent-runtime/purge.d.ts.map +1 -0
- package/dist/agent-runtime/runtime.d.ts.map +1 -1
- package/dist/agent-runtime/sqlite-purge.d.ts +7 -0
- package/dist/agent-runtime/sqlite-purge.d.ts.map +1 -0
- package/dist/agent-runtime/sqlite.d.ts.map +1 -1
- package/dist/agent-runtime/store-driver.d.ts +2 -0
- package/dist/agent-runtime/store-driver.d.ts.map +1 -1
- package/dist/agent-runtime/store-purge.d.ts +13 -0
- package/dist/agent-runtime/store-purge.d.ts.map +1 -0
- package/dist/agent-runtime/store.d.ts +3 -0
- package/dist/agent-runtime/store.d.ts.map +1 -1
- package/dist/agent-runtime-harness.js +1 -1
- package/dist/agent-runtime-sqlite-bun.js +2 -2
- package/dist/agent-runtime-sqlite-node.js +2 -2
- package/dist/agent-runtime.d.ts +2 -0
- package/dist/agent-runtime.d.ts.map +1 -1
- package/dist/agent-runtime.js +11 -3
- package/dist/application.js +2 -2
- package/dist/{index-jw81xr75.js → index-19eet1qx.js} +127 -51
- package/dist/{index-7wpgrqa8.js → index-3z73fh2c.js} +93 -7
- package/dist/{index-35wcrke5.js → index-by57nwhc.js} +3 -0
- package/dist/{index-9sx8tbz2.js → index-cksjz4eg.js} +50 -1
- package/dist/{index-c97p90dc.js → index-cx84zg25.js} +82 -24
- package/dist/{index-w0445741.js → index-hvftzz91.js} +1 -1
- package/dist/node.js +2 -2
- package/dist/server/contract-stream.d.ts.map +1 -1
- package/dist/server/http-stream-lifetime.d.ts +19 -0
- package/dist/server/http-stream-lifetime.d.ts.map +1 -0
- package/dist/server/index.js +2 -2
- package/dist/server/shutdown.d.ts.map +1 -1
- package/dist/server/streaming-route.d.ts.map +1 -1
- package/dist/testing.js +2 -2
- package/llms-full.txt +87 -5
- 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/
|
|
33
|
+
// src/agent-runtime/purge.ts
|
|
34
34
|
import { z as z2 } from "zod";
|
|
35
|
-
var
|
|
36
|
-
|
|
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 =
|
|
40
|
-
var AgentStoreAppliedSchema =
|
|
41
|
-
outcome:
|
|
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 =
|
|
45
|
-
outcome:
|
|
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 =
|
|
84
|
+
var AgentStoreMutationResultSchema = z3.discriminatedUnion("outcome", [
|
|
55
85
|
AgentStoreAppliedSchema,
|
|
56
86
|
AgentStoreDuplicateSchema,
|
|
57
87
|
AgentStoreConflictSchema,
|
|
58
88
|
AgentStoreNotFoundSchema
|
|
59
89
|
]);
|
|
60
|
-
var AgentRunViewSchema =
|
|
90
|
+
var AgentRunViewSchema = z3.object({
|
|
61
91
|
snapshotVersion: AgentRecordVersionSchema,
|
|
62
92
|
run: AgentRunSchema,
|
|
63
93
|
assistant: AgentMessageSchema.optional()
|
|
64
94
|
});
|
|
65
|
-
var AcceptInputAndAssignRunSchema =
|
|
66
|
-
idempotencyKey:
|
|
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 =
|
|
102
|
+
var AcquireAgentRunSchema = z3.object({
|
|
73
103
|
conversationId: AgentRecordIdSchema,
|
|
74
104
|
runId: AgentRecordIdSchema,
|
|
75
105
|
expectedRevision: AgentRecordVersionSchema,
|
|
76
|
-
ownerId:
|
|
106
|
+
ownerId: z3.string().min(1)
|
|
77
107
|
});
|
|
78
|
-
var CheckpointRunAssistantSchema =
|
|
108
|
+
var CheckpointRunAssistantSchema = z3.object({
|
|
79
109
|
conversationId: AgentRecordIdSchema,
|
|
80
110
|
runId: AgentRecordIdSchema,
|
|
81
111
|
expectedRevision: AgentRecordVersionSchema,
|
|
82
|
-
ownerId:
|
|
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 =
|
|
117
|
+
var CommitRunTerminalSchema = z3.object({
|
|
88
118
|
conversationId: AgentRecordIdSchema,
|
|
89
119
|
runId: AgentRecordIdSchema,
|
|
90
120
|
expectedRevision: AgentRecordVersionSchema,
|
|
91
|
-
ownerId:
|
|
121
|
+
ownerId: z3.string().min(1),
|
|
92
122
|
fencingToken: AgentRecordVersionSchema.optional(),
|
|
93
123
|
assistant: AgentMessageSchema,
|
|
94
124
|
reason: AgentTerminalReasonSchema,
|
|
95
|
-
policyName:
|
|
125
|
+
policyName: z3.string().min(1).optional(),
|
|
96
126
|
usage: AgentUsageSchema.optional(),
|
|
97
|
-
absorb:
|
|
127
|
+
absorb: z3.array(z3.object({
|
|
98
128
|
runId: AgentRecordIdSchema,
|
|
99
|
-
inputMessageIds:
|
|
129
|
+
inputMessageIds: z3.array(AgentRecordIdSchema).min(1)
|
|
100
130
|
})).min(1).optional()
|
|
101
131
|
});
|
|
102
|
-
var RequestRunInterruptSchema =
|
|
132
|
+
var RequestRunInterruptSchema = z3.object({
|
|
103
133
|
conversationId: AgentRecordIdSchema,
|
|
104
134
|
runId: AgentRecordIdSchema,
|
|
105
135
|
expectedRevision: AgentRecordVersionSchema
|
|
106
136
|
});
|
|
107
|
-
var RecoverAgentRunSchema =
|
|
137
|
+
var RecoverAgentRunSchema = z3.object({
|
|
108
138
|
conversationId: AgentRecordIdSchema,
|
|
109
139
|
runId: AgentRecordIdSchema,
|
|
110
140
|
expectedRevision: AgentRecordVersionSchema,
|
|
111
|
-
action:
|
|
112
|
-
replaySafe:
|
|
141
|
+
action: z3.enum(["requeue", "abandon"]),
|
|
142
|
+
replaySafe: z3.boolean().optional()
|
|
113
143
|
});
|
|
114
|
-
var ReplaceCompactedRangeSchema =
|
|
144
|
+
var ReplaceCompactedRangeSchema = z3.object({
|
|
115
145
|
conversationId: AgentRecordIdSchema,
|
|
116
146
|
expectedVersion: AgentRecordVersionSchema,
|
|
117
|
-
replacedMessageIds:
|
|
147
|
+
replacedMessageIds: z3.array(AgentRecordIdSchema).min(1),
|
|
118
148
|
summary: AgentMessageSchema
|
|
119
149
|
});
|
|
120
|
-
var AgentRecoverableDescriptorSchema =
|
|
150
|
+
var AgentRecoverableDescriptorSchema = z3.object({
|
|
121
151
|
conversationId: AgentRecordIdSchema,
|
|
122
152
|
run: AgentRunSchema
|
|
123
153
|
});
|
|
124
|
-
var AgentRecoverablePageSchema =
|
|
125
|
-
items:
|
|
126
|
-
nextCursor:
|
|
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
|
|
131
|
-
|
|
132
|
-
|
|
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 =
|
|
137
|
-
schemaVersion:
|
|
191
|
+
var AgentStoredRunSchema = z4.object({
|
|
192
|
+
schemaVersion: z4.literal(1),
|
|
138
193
|
run: AgentRunSchema,
|
|
139
194
|
terminalAssistant: AgentMessageSchema.optional()
|
|
140
195
|
});
|
|
141
|
-
var AgentAdmissionReceiptSchema =
|
|
142
|
-
schemaVersion:
|
|
196
|
+
var AgentAdmissionReceiptSchema = z4.object({
|
|
197
|
+
schemaVersion: z4.literal(1),
|
|
143
198
|
conversationId: AgentRecordIdSchema,
|
|
144
|
-
idempotencyKey:
|
|
199
|
+
idempotencyKey: z4.string().min(1),
|
|
145
200
|
input: AgentMessageSchema,
|
|
146
201
|
runId: AgentRecordIdSchema,
|
|
147
202
|
assistantMessageId: AgentRecordIdSchema
|
|
148
203
|
});
|
|
149
|
-
var AgentHistoryMutationSchema =
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
type:
|
|
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
|
-
|
|
156
|
-
type:
|
|
157
|
-
replacedMessageIds:
|
|
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 =
|
|
162
|
-
cursor:
|
|
163
|
-
limit:
|
|
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 =
|
|
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:
|
|
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
|
-
|
|
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 =
|
|
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:
|
|
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-
|
|
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 {
|