stitchkit 0.58.0 → 0.59.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.
- package/dist/agent-runtime/runtime.d.ts.map +1 -1
- package/dist/agent-runtime/store-driver.d.ts +199 -24
- package/dist/agent-runtime/store-driver.d.ts.map +1 -1
- package/dist/agent-runtime/store.d.ts +222 -0
- package/dist/agent-runtime/store.d.ts.map +1 -1
- package/dist/agent-runtime.d.ts +1 -1
- package/dist/agent-runtime.d.ts.map +1 -1
- package/dist/agent-runtime.js +273 -136
- package/dist/testing/agent-store-conformance.d.ts.map +1 -1
- package/dist/testing.js +29 -0
- package/llms-full.txt +56 -33
- package/package.json +1 -1
package/dist/agent-runtime.js
CHANGED
|
@@ -1724,7 +1724,7 @@ function createAgentRuntime(config) {
|
|
|
1724
1724
|
});
|
|
1725
1725
|
const acceptedSnapshot = appliedSnapshot(acceptance, "input acceptance");
|
|
1726
1726
|
const assignedRunId = acceptance.outcome === "duplicate" ? acceptance.runId : reservation?.admission.runId ?? runId;
|
|
1727
|
-
const acceptedRun = findRun(acceptedSnapshot.runs, assignedRunId);
|
|
1727
|
+
const acceptedRun = acceptance.outcome === "duplicate" ? acceptance.run : findRun(acceptedSnapshot.runs, assignedRunId);
|
|
1728
1728
|
const actualInputMessageId = acceptance.outcome === "duplicate" ? acceptance.inputMessageId : userMessage2.id;
|
|
1729
1729
|
const acceptedInput = acceptance.outcome === "duplicate" ? acceptance.input : acceptedSnapshot.messages.find((candidate) => candidate.id === actualInputMessageId);
|
|
1730
1730
|
if (!acceptedInput) {
|
|
@@ -1739,7 +1739,7 @@ function createAgentRuntime(config) {
|
|
|
1739
1739
|
createdAt: acceptedRun.createdAt,
|
|
1740
1740
|
updatedAt: acceptedRun.updatedAt
|
|
1741
1741
|
});
|
|
1742
|
-
const acceptedAssistant = acceptance.outcome === "duplicate" ?
|
|
1742
|
+
const acceptedAssistant = acceptance.outcome === "duplicate" ? acceptance.assistant ?? assistantPlaceholder : assistantPlaceholder;
|
|
1743
1743
|
const admission = {
|
|
1744
1744
|
inputMessageId: acceptedInput.id,
|
|
1745
1745
|
runId: acceptedRun.id,
|
|
@@ -1781,9 +1781,9 @@ function createAgentRuntime(config) {
|
|
|
1781
1781
|
}
|
|
1782
1782
|
return;
|
|
1783
1783
|
}
|
|
1784
|
-
const message =
|
|
1784
|
+
const message = acceptance.assistant;
|
|
1785
1785
|
if (!message) {
|
|
1786
|
-
const error = new Error("Duplicate terminal
|
|
1786
|
+
const error = new Error("Duplicate terminal admission has no retained canonical assistant");
|
|
1787
1787
|
outerResult.reject(error);
|
|
1788
1788
|
if (reservation?.shouldSchedule) {
|
|
1789
1789
|
reservation.admission.completion.reject(error);
|
|
@@ -1996,6 +1996,8 @@ var AgentStoreDuplicateSchema = z6.object({
|
|
|
1996
1996
|
inputMessageId: AgentRecordIdSchema,
|
|
1997
1997
|
runId: AgentRecordIdSchema,
|
|
1998
1998
|
assistantMessageId: AgentRecordIdSchema,
|
|
1999
|
+
run: AgentRunSchema,
|
|
2000
|
+
assistant: AgentMessageSchema.optional(),
|
|
1999
2001
|
snapshot: AgentSnapshotSchema
|
|
2000
2002
|
});
|
|
2001
2003
|
var AgentStoreMutationResultSchema = z6.discriminatedUnion("outcome", [
|
|
@@ -2055,18 +2057,23 @@ var ReplaceCompactedRangeSchema = z6.object({
|
|
|
2055
2057
|
});
|
|
2056
2058
|
// src/agent-runtime/store-driver.ts
|
|
2057
2059
|
import { z as z7 } from "zod";
|
|
2058
|
-
var
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2060
|
+
var AgentRuntimeHeadSchema = z7.object({
|
|
2061
|
+
schemaVersion: z7.literal(1),
|
|
2062
|
+
conversationId: AgentRecordIdSchema,
|
|
2063
|
+
version: AgentRecordVersionSchema
|
|
2064
|
+
});
|
|
2065
|
+
var AgentStoredRunSchema = z7.object({
|
|
2066
|
+
schemaVersion: z7.literal(1),
|
|
2067
|
+
run: AgentRunSchema,
|
|
2068
|
+
terminalAssistant: AgentMessageSchema.optional()
|
|
2063
2069
|
});
|
|
2064
|
-
var
|
|
2070
|
+
var AgentAdmissionReceiptSchema = z7.object({
|
|
2065
2071
|
schemaVersion: z7.literal(1),
|
|
2066
2072
|
conversationId: AgentRecordIdSchema,
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2073
|
+
idempotencyKey: z7.string().min(1),
|
|
2074
|
+
input: AgentMessageSchema,
|
|
2075
|
+
runId: AgentRecordIdSchema,
|
|
2076
|
+
assistantMessageId: AgentRecordIdSchema
|
|
2070
2077
|
});
|
|
2071
2078
|
var AgentHistoryMutationSchema = z7.discriminatedUnion("type", [
|
|
2072
2079
|
z7.object({ type: z7.literal("admit"), input: AgentMessageSchema }),
|
|
@@ -2092,40 +2099,40 @@ var AgentRecoverableScanInputSchema = z7.object({
|
|
|
2092
2099
|
cursor: z7.string().min(1).optional(),
|
|
2093
2100
|
limit: z7.number().int().min(1).max(1000)
|
|
2094
2101
|
});
|
|
2095
|
-
function
|
|
2096
|
-
return
|
|
2102
|
+
function emptyHead(conversationId) {
|
|
2103
|
+
return AgentRuntimeHeadSchema.parse({
|
|
2097
2104
|
schemaVersion: 1,
|
|
2098
2105
|
conversationId,
|
|
2099
|
-
version: 0
|
|
2100
|
-
runs: [],
|
|
2101
|
-
admissions: []
|
|
2106
|
+
version: 0
|
|
2102
2107
|
});
|
|
2103
2108
|
}
|
|
2104
|
-
function snapshotOf(
|
|
2105
|
-
|
|
2109
|
+
function snapshotOf(head, messages, records) {
|
|
2110
|
+
validateSnapshot(head, messages, records);
|
|
2106
2111
|
return AgentSnapshotSchema.parse({
|
|
2107
2112
|
schemaVersion: 1,
|
|
2108
|
-
conversationId:
|
|
2109
|
-
version:
|
|
2113
|
+
conversationId: head.conversationId,
|
|
2114
|
+
version: head.version,
|
|
2110
2115
|
messages,
|
|
2111
|
-
runs:
|
|
2116
|
+
runs: records.map((record) => record.run).sort((left, right) => left.createdAt.localeCompare(right.createdAt) || left.id.localeCompare(right.id))
|
|
2112
2117
|
});
|
|
2113
2118
|
}
|
|
2114
|
-
function
|
|
2119
|
+
function validateSnapshot(head, messages, records) {
|
|
2115
2120
|
const runIds = new Set;
|
|
2116
2121
|
const assistantIds = new Set;
|
|
2117
2122
|
const messageIds = new Set;
|
|
2118
|
-
const
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
+
for (const record of records) {
|
|
2124
|
+
const run = record.run;
|
|
2125
|
+
if (run.conversationId !== head.conversationId || runIds.has(run.id) || assistantIds.has(run.assistantMessageId)) {
|
|
2126
|
+
throw new TypeError("Stored agent runs contain inconsistent identities");
|
|
2127
|
+
}
|
|
2128
|
+
if (record.terminalAssistant && (record.terminalAssistant.id !== run.assistantMessageId || record.terminalAssistant.conversationId !== run.conversationId || record.terminalAssistant.runId !== run.id || record.terminalAssistant.role !== "assistant" || run.terminalReason === undefined)) {
|
|
2129
|
+
throw new TypeError("Retained terminal assistant does not match its run");
|
|
2123
2130
|
}
|
|
2124
2131
|
runIds.add(run.id);
|
|
2125
2132
|
assistantIds.add(run.assistantMessageId);
|
|
2126
2133
|
}
|
|
2127
2134
|
for (const message of messages) {
|
|
2128
|
-
if (message.conversationId !==
|
|
2135
|
+
if (message.conversationId !== head.conversationId || messageIds.has(message.id)) {
|
|
2129
2136
|
throw new TypeError("Stored agent history contains inconsistent message identities");
|
|
2130
2137
|
}
|
|
2131
2138
|
messageIds.add(message.id);
|
|
@@ -2133,23 +2140,12 @@ function validateAggregate(state, messages) {
|
|
|
2133
2140
|
throw new TypeError("Stored history occupies a reserved assistant identity");
|
|
2134
2141
|
}
|
|
2135
2142
|
if (message.runId !== undefined) {
|
|
2136
|
-
const run =
|
|
2143
|
+
const run = records.find((candidate) => candidate.run.id === message.runId)?.run;
|
|
2137
2144
|
if (!run || message.role !== "assistant" || run.assistantMessageId !== message.id) {
|
|
2138
2145
|
throw new TypeError("Stored assistant history does not match its reserved run identity");
|
|
2139
2146
|
}
|
|
2140
2147
|
}
|
|
2141
2148
|
}
|
|
2142
|
-
for (const admission of state.admissions) {
|
|
2143
|
-
const run = state.runs.find((candidate) => candidate.id === admission.runId);
|
|
2144
|
-
if (idempotencyKeys.has(admission.idempotencyKey) || admittedInputIds.has(admission.inputMessageId) || !run || run.assistantMessageId !== admission.assistantMessageId || !run.inputMessageIds.includes(admission.inputMessageId)) {
|
|
2145
|
-
throw new TypeError("Stored admission identity is inconsistent with its assigned run");
|
|
2146
|
-
}
|
|
2147
|
-
idempotencyKeys.add(admission.idempotencyKey);
|
|
2148
|
-
admittedInputIds.add(admission.inputMessageId);
|
|
2149
|
-
}
|
|
2150
|
-
}
|
|
2151
|
-
function recoverableDescriptors(state) {
|
|
2152
|
-
return state.runs.filter((run) => ["queued", "running", "interrupt_requested"].includes(run.state)).map((run) => ({ conversationId: state.conversationId, run }));
|
|
2153
2149
|
}
|
|
2154
2150
|
var RecoverableCursorSchema = z7.tuple([AgentRecordIdSchema, AgentRecordIdSchema]);
|
|
2155
2151
|
function recoverableCursor(input) {
|
|
@@ -2187,7 +2183,7 @@ function terminalMessageStatus(reason) {
|
|
|
2187
2183
|
}
|
|
2188
2184
|
return "failed";
|
|
2189
2185
|
}
|
|
2190
|
-
function applied(current,
|
|
2186
|
+
function applied(current, input, effects) {
|
|
2191
2187
|
return {
|
|
2192
2188
|
outcome: "applied",
|
|
2193
2189
|
snapshot: AgentSnapshotSchema.parse({
|
|
@@ -2196,27 +2192,14 @@ function applied(current, admissions, input, historyMutation) {
|
|
|
2196
2192
|
runs: input.runs ?? current.runs,
|
|
2197
2193
|
messages: input.messages ?? current.messages
|
|
2198
2194
|
}),
|
|
2199
|
-
|
|
2200
|
-
...
|
|
2195
|
+
...effects?.runRecord && { runRecord: effects.runRecord },
|
|
2196
|
+
...effects?.admissionReceipt && { admissionReceipt: effects.admissionReceipt },
|
|
2197
|
+
...effects?.historyMutation && { historyMutation: effects.historyMutation }
|
|
2201
2198
|
};
|
|
2202
2199
|
}
|
|
2203
|
-
function reduceStore(current,
|
|
2200
|
+
function reduceStore(current, operation) {
|
|
2204
2201
|
if (operation.type === "accept") {
|
|
2205
2202
|
const input = operation.input;
|
|
2206
|
-
const duplicate = currentAdmissions.find((candidate) => candidate.idempotencyKey === input.idempotencyKey);
|
|
2207
|
-
if (duplicate) {
|
|
2208
|
-
if (!duplicateInput) {
|
|
2209
|
-
throw new Error("Duplicate admission input is unavailable from canonical history");
|
|
2210
|
-
}
|
|
2211
|
-
return {
|
|
2212
|
-
outcome: "duplicate",
|
|
2213
|
-
input: duplicateInput,
|
|
2214
|
-
inputMessageId: duplicate.inputMessageId,
|
|
2215
|
-
runId: duplicate.runId,
|
|
2216
|
-
assistantMessageId: duplicate.assistantMessageId,
|
|
2217
|
-
snapshot: current
|
|
2218
|
-
};
|
|
2219
|
-
}
|
|
2220
2203
|
if (input.expectedVersion !== undefined && input.expectedVersion !== current.version) {
|
|
2221
2204
|
return conflict(current.version);
|
|
2222
2205
|
}
|
|
@@ -2233,16 +2216,25 @@ function reduceStore(current, currentAdmissions, operation, duplicateInput) {
|
|
|
2233
2216
|
revision: coalescedRun.revision + 1,
|
|
2234
2217
|
updatedAt: new Date().toISOString()
|
|
2235
2218
|
}) : input.run;
|
|
2236
|
-
const
|
|
2219
|
+
const admissionReceipt = AgentAdmissionReceiptSchema.parse({
|
|
2220
|
+
schemaVersion: 1,
|
|
2221
|
+
conversationId: input.input.conversationId,
|
|
2237
2222
|
idempotencyKey: input.idempotencyKey,
|
|
2238
|
-
|
|
2223
|
+
input: input.input,
|
|
2239
2224
|
runId: assignedRun.id,
|
|
2240
2225
|
assistantMessageId: assignedRun.assistantMessageId
|
|
2241
2226
|
});
|
|
2242
|
-
return applied(current,
|
|
2227
|
+
return applied(current, {
|
|
2243
2228
|
messages: [...current.messages, input.input],
|
|
2244
2229
|
runs: coalescedRun ? replaceRun(current.runs, assignedRun) : [...current.runs, assignedRun]
|
|
2245
|
-
}, {
|
|
2230
|
+
}, {
|
|
2231
|
+
runRecord: AgentStoredRunSchema.parse({
|
|
2232
|
+
schemaVersion: 1,
|
|
2233
|
+
run: assignedRun
|
|
2234
|
+
}),
|
|
2235
|
+
admissionReceipt,
|
|
2236
|
+
historyMutation: { type: "admit", input: input.input }
|
|
2237
|
+
});
|
|
2246
2238
|
}
|
|
2247
2239
|
const conversationId = operation.input.conversationId;
|
|
2248
2240
|
const run = operation.type === "compact" ? undefined : current.runs.find((candidate) => candidate.id === operation.input.runId);
|
|
@@ -2262,8 +2254,8 @@ function reduceStore(current, currentAdmissions, operation, duplicateInput) {
|
|
|
2262
2254
|
revision: run.revision + 1,
|
|
2263
2255
|
updatedAt: new Date().toISOString()
|
|
2264
2256
|
});
|
|
2265
|
-
return applied(current,
|
|
2266
|
-
|
|
2257
|
+
return applied(current, { runs: replaceRun(current.runs, next) }, {
|
|
2258
|
+
runRecord: AgentStoredRunSchema.parse({ schemaVersion: 1, run: next })
|
|
2267
2259
|
});
|
|
2268
2260
|
}
|
|
2269
2261
|
if (operation.type === "checkpoint" && run) {
|
|
@@ -2276,10 +2268,13 @@ function reduceStore(current, currentAdmissions, operation, duplicateInput) {
|
|
|
2276
2268
|
revision: run.revision + 1,
|
|
2277
2269
|
updatedAt: new Date().toISOString()
|
|
2278
2270
|
});
|
|
2279
|
-
return applied(current,
|
|
2271
|
+
return applied(current, {
|
|
2280
2272
|
runs: replaceRun(current.runs, next),
|
|
2281
2273
|
messages: replaceMessage(current.messages, input.assistant)
|
|
2282
|
-
}, {
|
|
2274
|
+
}, {
|
|
2275
|
+
runRecord: AgentStoredRunSchema.parse({ schemaVersion: 1, run: next }),
|
|
2276
|
+
historyMutation: { type: "upsert-assistant", message: input.assistant }
|
|
2277
|
+
});
|
|
2283
2278
|
}
|
|
2284
2279
|
if (operation.type === "interrupt" && run) {
|
|
2285
2280
|
if (run.revision !== operation.input.expectedRevision || run.state !== "running") {
|
|
@@ -2291,8 +2286,8 @@ function reduceStore(current, currentAdmissions, operation, duplicateInput) {
|
|
|
2291
2286
|
revision: run.revision + 1,
|
|
2292
2287
|
updatedAt: new Date().toISOString()
|
|
2293
2288
|
});
|
|
2294
|
-
return applied(current,
|
|
2295
|
-
|
|
2289
|
+
return applied(current, { runs: replaceRun(current.runs, next) }, {
|
|
2290
|
+
runRecord: AgentStoredRunSchema.parse({ schemaVersion: 1, run: next })
|
|
2296
2291
|
});
|
|
2297
2292
|
}
|
|
2298
2293
|
if (operation.type === "recover" && run) {
|
|
@@ -2330,13 +2325,20 @@ function reduceStore(current, currentAdmissions, operation, duplicateInput) {
|
|
|
2330
2325
|
status: "failed",
|
|
2331
2326
|
updatedAt: new Date().toISOString()
|
|
2332
2327
|
});
|
|
2333
|
-
return applied(current,
|
|
2328
|
+
return applied(current, {
|
|
2334
2329
|
runs: replaceRun(current.runs, next),
|
|
2335
2330
|
messages: replaceMessage(current.messages, assistant)
|
|
2336
|
-
}, {
|
|
2331
|
+
}, {
|
|
2332
|
+
runRecord: AgentStoredRunSchema.parse({
|
|
2333
|
+
schemaVersion: 1,
|
|
2334
|
+
run: next,
|
|
2335
|
+
terminalAssistant: assistant
|
|
2336
|
+
}),
|
|
2337
|
+
historyMutation: { type: "upsert-assistant", message: assistant }
|
|
2338
|
+
});
|
|
2337
2339
|
}
|
|
2338
|
-
return applied(current,
|
|
2339
|
-
|
|
2340
|
+
return applied(current, { runs: replaceRun(current.runs, next) }, {
|
|
2341
|
+
runRecord: AgentStoredRunSchema.parse({ schemaVersion: 1, run: next })
|
|
2340
2342
|
});
|
|
2341
2343
|
}
|
|
2342
2344
|
if (operation.type === "terminal" && run) {
|
|
@@ -2352,10 +2354,17 @@ function reduceStore(current, currentAdmissions, operation, duplicateInput) {
|
|
|
2352
2354
|
revision: run.revision + 1,
|
|
2353
2355
|
updatedAt: new Date().toISOString()
|
|
2354
2356
|
});
|
|
2355
|
-
return applied(current,
|
|
2357
|
+
return applied(current, {
|
|
2356
2358
|
runs: replaceRun(current.runs, next),
|
|
2357
2359
|
messages: replaceMessage(current.messages, input.assistant)
|
|
2358
|
-
}, {
|
|
2360
|
+
}, {
|
|
2361
|
+
runRecord: AgentStoredRunSchema.parse({
|
|
2362
|
+
schemaVersion: 1,
|
|
2363
|
+
run: next,
|
|
2364
|
+
terminalAssistant: input.assistant
|
|
2365
|
+
}),
|
|
2366
|
+
historyMutation: { type: "upsert-assistant", message: input.assistant }
|
|
2367
|
+
});
|
|
2359
2368
|
}
|
|
2360
2369
|
if (operation.type === "compact") {
|
|
2361
2370
|
const input = operation.input;
|
|
@@ -2377,10 +2386,12 @@ function reduceStore(current, currentAdmissions, operation, duplicateInput) {
|
|
|
2377
2386
|
input.summary,
|
|
2378
2387
|
...current.messages.slice(first + positions.length)
|
|
2379
2388
|
];
|
|
2380
|
-
return applied(current,
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2389
|
+
return applied(current, { messages }, {
|
|
2390
|
+
historyMutation: {
|
|
2391
|
+
type: "replace-compacted-range",
|
|
2392
|
+
replacedMessageIds: input.replacedMessageIds,
|
|
2393
|
+
summary: input.summary
|
|
2394
|
+
}
|
|
2384
2395
|
});
|
|
2385
2396
|
}
|
|
2386
2397
|
return { outcome: "not_found" };
|
|
@@ -2388,48 +2399,130 @@ function reduceStore(current, currentAdmissions, operation, duplicateInput) {
|
|
|
2388
2399
|
function operationConversationId(operation) {
|
|
2389
2400
|
return operation.type === "accept" ? operation.input.input.conversationId : operation.input.conversationId;
|
|
2390
2401
|
}
|
|
2402
|
+
function mergeRunRecords(...groups) {
|
|
2403
|
+
const records = new Map;
|
|
2404
|
+
for (const group of groups) {
|
|
2405
|
+
for (const rawRecord of group) {
|
|
2406
|
+
const record = AgentStoredRunSchema.parse(rawRecord);
|
|
2407
|
+
const previous = records.get(record.run.id);
|
|
2408
|
+
if (previous && previous.run.assistantMessageId !== record.run.assistantMessageId) {
|
|
2409
|
+
throw new TypeError("Stored agent run identity changed across normalized records");
|
|
2410
|
+
}
|
|
2411
|
+
records.set(record.run.id, record);
|
|
2412
|
+
}
|
|
2413
|
+
}
|
|
2414
|
+
return [...records.values()];
|
|
2415
|
+
}
|
|
2416
|
+
function referencedRunIds(messages) {
|
|
2417
|
+
return [...new Set(messages.flatMap((message) => message.runId ? [message.runId] : []))];
|
|
2418
|
+
}
|
|
2419
|
+
function validateAdmissionReceipt(receipt, record, conversationId) {
|
|
2420
|
+
const input = receipt.input;
|
|
2421
|
+
const run = record.run;
|
|
2422
|
+
if (receipt.conversationId !== conversationId || input.conversationId !== conversationId || input.role !== "user" || input.status !== "committed" || input.runId !== undefined || receipt.runId !== run.id || receipt.assistantMessageId !== run.assistantMessageId || !run.inputMessageIds.includes(input.id)) {
|
|
2423
|
+
throw new TypeError("Admission receipt does not match its canonical run assignment");
|
|
2424
|
+
}
|
|
2425
|
+
}
|
|
2391
2426
|
function createAgentRuntimeStore(driver) {
|
|
2392
2427
|
const loadSnapshot = (conversationId) => driver.transaction(async (transaction) => {
|
|
2393
|
-
const [stored, messages] = await Promise.all([
|
|
2394
|
-
driver.
|
|
2395
|
-
driver.history.load(transaction, conversationId)
|
|
2428
|
+
const [stored, messages, activeRecords] = await Promise.all([
|
|
2429
|
+
driver.head.load(transaction, conversationId),
|
|
2430
|
+
driver.history.load(transaction, conversationId),
|
|
2431
|
+
driver.runs.listActive(transaction, conversationId)
|
|
2396
2432
|
]);
|
|
2397
|
-
|
|
2433
|
+
const head = AgentRuntimeHeadSchema.parse(stored ?? emptyHead(conversationId));
|
|
2434
|
+
const referencedRecords = await driver.runs.loadMany(transaction, {
|
|
2435
|
+
conversationId,
|
|
2436
|
+
runIds: referencedRunIds(messages)
|
|
2437
|
+
});
|
|
2438
|
+
return snapshotOf(head, messages, mergeRunRecords(activeRecords, referencedRecords));
|
|
2398
2439
|
});
|
|
2399
2440
|
const mutate = (operation) => driver.transaction(async (transaction) => {
|
|
2400
2441
|
const conversationId = operationConversationId(operation);
|
|
2401
|
-
const
|
|
2402
|
-
|
|
2403
|
-
driver.
|
|
2442
|
+
const operationRunId = operation.type === "accept" ? operation.input.coalesceIntoRunId : operation.type === "compact" ? undefined : operation.input.runId;
|
|
2443
|
+
const [stored, messages, activeRecords, operationRecord, duplicateReceipt] = await Promise.all([
|
|
2444
|
+
driver.head.load(transaction, conversationId),
|
|
2445
|
+
driver.history.load(transaction, conversationId),
|
|
2446
|
+
driver.runs.listActive(transaction, conversationId),
|
|
2447
|
+
operationRunId ? driver.runs.load(transaction, { conversationId, runId: operationRunId }) : undefined,
|
|
2448
|
+
operation.type === "accept" ? driver.admissions.load(transaction, {
|
|
2449
|
+
conversationId,
|
|
2450
|
+
idempotencyKey: operation.input.idempotencyKey
|
|
2451
|
+
}) : undefined
|
|
2404
2452
|
]);
|
|
2405
|
-
const
|
|
2406
|
-
const
|
|
2407
|
-
const duplicateIdentity = operation.type === "accept" ? state.admissions.find((candidate) => candidate.idempotencyKey === operation.input.idempotencyKey) : undefined;
|
|
2408
|
-
const duplicateInput = duplicateIdentity ? await driver.history.loadById(transaction, {
|
|
2453
|
+
const head = AgentRuntimeHeadSchema.parse(stored ?? emptyHead(conversationId));
|
|
2454
|
+
const referencedRecords = await driver.runs.loadMany(transaction, {
|
|
2409
2455
|
conversationId,
|
|
2410
|
-
|
|
2411
|
-
})
|
|
2412
|
-
|
|
2413
|
-
|
|
2456
|
+
runIds: referencedRunIds(messages)
|
|
2457
|
+
});
|
|
2458
|
+
const records = mergeRunRecords(activeRecords, referencedRecords, operationRecord ? [operationRecord] : []);
|
|
2459
|
+
const current = snapshotOf(head, messages, records);
|
|
2460
|
+
if (duplicateReceipt) {
|
|
2461
|
+
const duplicateRecord = await driver.runs.load(transaction, {
|
|
2462
|
+
conversationId,
|
|
2463
|
+
runId: duplicateReceipt.runId
|
|
2464
|
+
});
|
|
2465
|
+
if (!duplicateRecord) {
|
|
2466
|
+
throw new TypeError("Admission receipt points to a missing canonical run");
|
|
2467
|
+
}
|
|
2468
|
+
validateAdmissionReceipt(duplicateReceipt, duplicateRecord, conversationId);
|
|
2469
|
+
return {
|
|
2470
|
+
outcome: "duplicate",
|
|
2471
|
+
input: duplicateReceipt.input,
|
|
2472
|
+
inputMessageId: duplicateReceipt.input.id,
|
|
2473
|
+
runId: duplicateReceipt.runId,
|
|
2474
|
+
assistantMessageId: duplicateReceipt.assistantMessageId,
|
|
2475
|
+
run: duplicateRecord.run,
|
|
2476
|
+
...duplicateRecord.terminalAssistant && {
|
|
2477
|
+
assistant: duplicateRecord.terminalAssistant
|
|
2478
|
+
},
|
|
2479
|
+
snapshot: snapshotOf(head, messages, mergeRunRecords(records, [duplicateRecord]))
|
|
2480
|
+
};
|
|
2481
|
+
}
|
|
2482
|
+
if (operation.type === "accept") {
|
|
2483
|
+
const inputCollision = await driver.admissions.loadByInputMessageId(transaction, {
|
|
2484
|
+
conversationId,
|
|
2485
|
+
inputMessageId: operation.input.input.id
|
|
2486
|
+
});
|
|
2487
|
+
if (inputCollision) {
|
|
2488
|
+
throw new TypeError("Input message identity is already assigned to an admission");
|
|
2489
|
+
}
|
|
2490
|
+
if (!operation.input.coalesceIntoRunId) {
|
|
2491
|
+
const [runCollision, assistantCollision] = await Promise.all([
|
|
2492
|
+
driver.runs.load(transaction, {
|
|
2493
|
+
conversationId,
|
|
2494
|
+
runId: operation.input.run.id
|
|
2495
|
+
}),
|
|
2496
|
+
driver.runs.loadByAssistantMessageId(transaction, {
|
|
2497
|
+
conversationId,
|
|
2498
|
+
assistantMessageId: operation.input.run.assistantMessageId
|
|
2499
|
+
})
|
|
2500
|
+
]);
|
|
2501
|
+
if (runCollision || assistantCollision) {
|
|
2502
|
+
throw new TypeError("Queued run identities are already reserved");
|
|
2503
|
+
}
|
|
2504
|
+
}
|
|
2414
2505
|
}
|
|
2415
|
-
const reduced = reduceStore(current,
|
|
2506
|
+
const reduced = reduceStore(current, operation);
|
|
2416
2507
|
if (reduced.outcome !== "applied")
|
|
2417
2508
|
return reduced;
|
|
2418
|
-
const
|
|
2509
|
+
const nextHead = AgentRuntimeHeadSchema.parse({
|
|
2419
2510
|
schemaVersion: 1,
|
|
2420
2511
|
conversationId,
|
|
2421
|
-
version: reduced.snapshot.version
|
|
2422
|
-
runs: reduced.snapshot.runs,
|
|
2423
|
-
admissions: reduced.admissions
|
|
2512
|
+
version: reduced.snapshot.version
|
|
2424
2513
|
});
|
|
2425
|
-
const outcome = await driver.
|
|
2514
|
+
const outcome = await driver.head.compareAndSwap(transaction, {
|
|
2426
2515
|
conversationId,
|
|
2427
2516
|
expectedVersion: current.version,
|
|
2428
|
-
next:
|
|
2429
|
-
recoverable: recoverableDescriptors(nextState)
|
|
2517
|
+
next: nextHead
|
|
2430
2518
|
});
|
|
2431
2519
|
if (outcome.outcome === "conflict")
|
|
2432
2520
|
return conflict(outcome.actualVersion);
|
|
2521
|
+
if (reduced.runRecord)
|
|
2522
|
+
await driver.runs.save(transaction, reduced.runRecord);
|
|
2523
|
+
if (reduced.admissionReceipt) {
|
|
2524
|
+
await driver.admissions.create(transaction, reduced.admissionReceipt);
|
|
2525
|
+
}
|
|
2433
2526
|
if (reduced.historyMutation) {
|
|
2434
2527
|
await driver.history.apply(transaction, reduced.historyMutation);
|
|
2435
2528
|
}
|
|
@@ -2481,10 +2574,16 @@ function createAgentRuntimeStore(driver) {
|
|
|
2481
2574
|
}
|
|
2482
2575
|
};
|
|
2483
2576
|
}
|
|
2484
|
-
function
|
|
2577
|
+
function cloneHeadMap(source) {
|
|
2485
2578
|
return new Map([...source].map(([key, value]) => [
|
|
2486
2579
|
key,
|
|
2487
|
-
|
|
2580
|
+
AgentRuntimeHeadSchema.parse(structuredClone(value))
|
|
2581
|
+
]));
|
|
2582
|
+
}
|
|
2583
|
+
function cloneNestedMap(source, clone) {
|
|
2584
|
+
return new Map([...source].map(([outerKey, values]) => [
|
|
2585
|
+
outerKey,
|
|
2586
|
+
new Map([...values].map(([innerKey, value]) => [innerKey, clone(value)]))
|
|
2488
2587
|
]));
|
|
2489
2588
|
}
|
|
2490
2589
|
function cloneHistoryMap(source) {
|
|
@@ -2494,9 +2593,10 @@ function cloneHistoryMap(source) {
|
|
|
2494
2593
|
]));
|
|
2495
2594
|
}
|
|
2496
2595
|
function createMemoryAgentRuntimeStore() {
|
|
2497
|
-
let
|
|
2596
|
+
let heads = new Map;
|
|
2597
|
+
let runs = new Map;
|
|
2598
|
+
let admissions = new Map;
|
|
2498
2599
|
let histories = new Map;
|
|
2499
|
-
let archivedMessages = new Map;
|
|
2500
2600
|
let transactionTail = Promise.resolve();
|
|
2501
2601
|
const driver = {
|
|
2502
2602
|
async transaction(work) {
|
|
@@ -2509,50 +2609,91 @@ function createMemoryAgentRuntimeStore() {
|
|
|
2509
2609
|
return;
|
|
2510
2610
|
});
|
|
2511
2611
|
const transaction = {
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
new Map([...messages].map(([messageId, message]) => [
|
|
2517
|
-
messageId,
|
|
2518
|
-
AgentMessageSchema.parse(structuredClone(message))
|
|
2519
|
-
]))
|
|
2520
|
-
]))
|
|
2612
|
+
heads: cloneHeadMap(heads),
|
|
2613
|
+
runs: cloneNestedMap(runs, (record) => AgentStoredRunSchema.parse(structuredClone(record))),
|
|
2614
|
+
admissions: cloneNestedMap(admissions, (receipt) => AgentAdmissionReceiptSchema.parse(structuredClone(receipt))),
|
|
2615
|
+
histories: cloneHistoryMap(histories)
|
|
2521
2616
|
};
|
|
2522
2617
|
try {
|
|
2523
2618
|
const result = await work(transaction);
|
|
2524
|
-
|
|
2619
|
+
heads = transaction.heads;
|
|
2620
|
+
runs = transaction.runs;
|
|
2621
|
+
admissions = transaction.admissions;
|
|
2525
2622
|
histories = transaction.histories;
|
|
2526
|
-
archivedMessages = transaction.archivedMessages;
|
|
2527
2623
|
return result;
|
|
2528
2624
|
} finally {
|
|
2529
2625
|
release.resolve();
|
|
2530
2626
|
}
|
|
2531
2627
|
},
|
|
2532
|
-
|
|
2628
|
+
head: {
|
|
2533
2629
|
async load(transaction, conversationId) {
|
|
2534
|
-
const
|
|
2535
|
-
return
|
|
2630
|
+
const head = transaction.heads.get(conversationId);
|
|
2631
|
+
return head ? AgentRuntimeHeadSchema.parse(structuredClone(head)) : undefined;
|
|
2536
2632
|
},
|
|
2537
2633
|
async compareAndSwap(transaction, input) {
|
|
2538
|
-
const current = transaction.
|
|
2634
|
+
const current = transaction.heads.get(input.conversationId);
|
|
2539
2635
|
const actualVersion = current?.version ?? 0;
|
|
2540
2636
|
if (actualVersion !== input.expectedVersion) {
|
|
2541
2637
|
return { outcome: "conflict", actualVersion };
|
|
2542
2638
|
}
|
|
2543
|
-
transaction.
|
|
2639
|
+
transaction.heads.set(input.conversationId, AgentRuntimeHeadSchema.parse(structuredClone(input.next)));
|
|
2544
2640
|
return { outcome: "applied" };
|
|
2545
2641
|
}
|
|
2546
2642
|
},
|
|
2643
|
+
runs: {
|
|
2644
|
+
async load(transaction, input) {
|
|
2645
|
+
const record = transaction.runs.get(input.conversationId)?.get(input.runId);
|
|
2646
|
+
return record ? AgentStoredRunSchema.parse(structuredClone(record)) : undefined;
|
|
2647
|
+
},
|
|
2648
|
+
async loadByAssistantMessageId(transaction, input) {
|
|
2649
|
+
const record = [...transaction.runs.get(input.conversationId)?.values() ?? []].find((candidate) => candidate.run.assistantMessageId === input.assistantMessageId);
|
|
2650
|
+
return record ? AgentStoredRunSchema.parse(structuredClone(record)) : undefined;
|
|
2651
|
+
},
|
|
2652
|
+
async loadMany(transaction, input) {
|
|
2653
|
+
const records = transaction.runs.get(input.conversationId);
|
|
2654
|
+
return input.runIds.flatMap((runId) => {
|
|
2655
|
+
const record = records?.get(runId);
|
|
2656
|
+
return record ? [AgentStoredRunSchema.parse(structuredClone(record))] : [];
|
|
2657
|
+
});
|
|
2658
|
+
},
|
|
2659
|
+
async listActive(transaction, conversationId) {
|
|
2660
|
+
return [...transaction.runs.get(conversationId)?.values() ?? []].filter((record) => ["queued", "running", "interrupt_requested"].includes(record.run.state)).map((record) => AgentStoredRunSchema.parse(structuredClone(record)));
|
|
2661
|
+
},
|
|
2662
|
+
async save(transaction, rawRecord) {
|
|
2663
|
+
const record = AgentStoredRunSchema.parse(structuredClone(rawRecord));
|
|
2664
|
+
const conversationRuns = transaction.runs.get(record.run.conversationId) ?? new Map;
|
|
2665
|
+
const collision = [...conversationRuns.values()].find((candidate) => candidate.run.id !== record.run.id && candidate.run.assistantMessageId === record.run.assistantMessageId);
|
|
2666
|
+
if (collision)
|
|
2667
|
+
throw new TypeError("Assistant message identity is already reserved");
|
|
2668
|
+
conversationRuns.set(record.run.id, record);
|
|
2669
|
+
transaction.runs.set(record.run.conversationId, conversationRuns);
|
|
2670
|
+
}
|
|
2671
|
+
},
|
|
2672
|
+
admissions: {
|
|
2673
|
+
async load(transaction, input) {
|
|
2674
|
+
const receipt = transaction.admissions.get(input.conversationId)?.get(input.idempotencyKey);
|
|
2675
|
+
return receipt ? AgentAdmissionReceiptSchema.parse(structuredClone(receipt)) : undefined;
|
|
2676
|
+
},
|
|
2677
|
+
async loadByInputMessageId(transaction, input) {
|
|
2678
|
+
const receipt = [
|
|
2679
|
+
...transaction.admissions.get(input.conversationId)?.values() ?? []
|
|
2680
|
+
].find((candidate) => candidate.input.id === input.inputMessageId);
|
|
2681
|
+
return receipt ? AgentAdmissionReceiptSchema.parse(structuredClone(receipt)) : undefined;
|
|
2682
|
+
},
|
|
2683
|
+
async create(transaction, rawReceipt) {
|
|
2684
|
+
const receipt = AgentAdmissionReceiptSchema.parse(structuredClone(rawReceipt));
|
|
2685
|
+
const conversationAdmissions = transaction.admissions.get(receipt.conversationId) ?? new Map;
|
|
2686
|
+
if (conversationAdmissions.has(receipt.idempotencyKey) || [...conversationAdmissions.values()].some((candidate) => candidate.input.id === receipt.input.id)) {
|
|
2687
|
+
throw new TypeError("Admission identity is already reserved");
|
|
2688
|
+
}
|
|
2689
|
+
conversationAdmissions.set(receipt.idempotencyKey, receipt);
|
|
2690
|
+
transaction.admissions.set(receipt.conversationId, conversationAdmissions);
|
|
2691
|
+
}
|
|
2692
|
+
},
|
|
2547
2693
|
history: {
|
|
2548
2694
|
async load(transaction, conversationId) {
|
|
2549
2695
|
return (transaction.histories.get(conversationId) ?? []).map((message) => AgentMessageSchema.parse(structuredClone(message)));
|
|
2550
2696
|
},
|
|
2551
|
-
async loadById(transaction, input) {
|
|
2552
|
-
const active = (transaction.histories.get(input.conversationId) ?? []).find((message2) => message2.id === input.messageId);
|
|
2553
|
-
const message = active ?? transaction.archivedMessages.get(input.conversationId)?.get(input.messageId);
|
|
2554
|
-
return message ? AgentMessageSchema.parse(structuredClone(message)) : undefined;
|
|
2555
|
-
},
|
|
2556
2697
|
async apply(transaction, rawMutation) {
|
|
2557
2698
|
const mutation = AgentHistoryMutationSchema.parse(rawMutation);
|
|
2558
2699
|
const conversationId = mutation.type === "admit" ? mutation.input.conversationId : mutation.type === "upsert-assistant" ? mutation.message.conversationId : mutation.summary.conversationId;
|
|
@@ -2570,11 +2711,6 @@ function createMemoryAgentRuntimeStore() {
|
|
|
2570
2711
|
const first = positions[0];
|
|
2571
2712
|
if (first === undefined)
|
|
2572
2713
|
throw new Error("Compaction history range disappeared");
|
|
2573
|
-
const archive = transaction.archivedMessages.get(conversationId) ?? new Map;
|
|
2574
|
-
for (const message of current.filter((candidate) => replaced.has(candidate.id))) {
|
|
2575
|
-
archive.set(message.id, AgentMessageSchema.parse(structuredClone(message)));
|
|
2576
|
-
}
|
|
2577
|
-
transaction.archivedMessages.set(conversationId, archive);
|
|
2578
2714
|
transaction.histories.set(conversationId, [
|
|
2579
2715
|
...current.slice(0, first),
|
|
2580
2716
|
mutation.summary,
|
|
@@ -2583,7 +2719,7 @@ function createMemoryAgentRuntimeStore() {
|
|
|
2583
2719
|
}
|
|
2584
2720
|
},
|
|
2585
2721
|
async scanRecoverable(input) {
|
|
2586
|
-
const descriptors = [...
|
|
2722
|
+
const descriptors = [...runs].flatMap(([conversationId, conversationRuns]) => [...conversationRuns.values()].filter((record) => ["queued", "running", "interrupt_requested"].includes(record.run.state)).map((record) => ({ conversationId, run: record.run }))).sort((left, right) => left.conversationId.localeCompare(right.conversationId) || left.run.id.localeCompare(right.run.id));
|
|
2587
2723
|
const cursorTuple = input.cursor ? parseRecoverableCursor(input.cursor) : undefined;
|
|
2588
2724
|
const start = cursorTuple ? descriptors.findIndex((item) => item.conversationId === cursorTuple[0] && item.run.id === cursorTuple[1]) + 1 : 0;
|
|
2589
2725
|
const items = descriptors.slice(start, start + input.limit);
|
|
@@ -2601,7 +2737,7 @@ export {
|
|
|
2601
2737
|
AcceptInputAndAssignRunSchema,
|
|
2602
2738
|
AcquireAgentRunSchema,
|
|
2603
2739
|
AgentAdmissionEventSchema,
|
|
2604
|
-
|
|
2740
|
+
AgentAdmissionReceiptSchema,
|
|
2605
2741
|
AgentAssistantPlaceholderSchema,
|
|
2606
2742
|
AgentCheckpointEventSchema,
|
|
2607
2743
|
AgentControlPartSchema,
|
|
@@ -2633,6 +2769,7 @@ export {
|
|
|
2633
2769
|
AgentRunStateSchema,
|
|
2634
2770
|
AgentRuntimeEventCursorSchema,
|
|
2635
2771
|
AgentRuntimeEventSchema,
|
|
2772
|
+
AgentRuntimeHeadSchema,
|
|
2636
2773
|
AgentSnapshotSchema,
|
|
2637
2774
|
AgentSourcePartSchema,
|
|
2638
2775
|
AgentStoreAppliedSchema,
|
|
@@ -2640,7 +2777,7 @@ export {
|
|
|
2640
2777
|
AgentStoreDuplicateSchema,
|
|
2641
2778
|
AgentStoreMutationResultSchema,
|
|
2642
2779
|
AgentStoreNotFoundSchema,
|
|
2643
|
-
|
|
2780
|
+
AgentStoredRunSchema,
|
|
2644
2781
|
AgentTerminalEventSchema,
|
|
2645
2782
|
AgentTerminalReasonSchema,
|
|
2646
2783
|
AgentTextPartSchema,
|