stitchkit 0.65.0 → 0.66.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/coordinator.d.ts +8 -5
- package/dist/agent-runtime/coordinator.d.ts.map +1 -1
- package/dist/agent-runtime/events.d.ts +36 -30
- package/dist/agent-runtime/events.d.ts.map +1 -1
- package/dist/agent-runtime/injection.d.ts +28 -0
- package/dist/agent-runtime/injection.d.ts.map +1 -0
- package/dist/agent-runtime/observability.d.ts +22 -20
- package/dist/agent-runtime/observability.d.ts.map +1 -1
- package/dist/agent-runtime/prompt.d.ts +11 -1
- package/dist/agent-runtime/prompt.d.ts.map +1 -1
- package/dist/agent-runtime/run-execution.d.ts +11 -0
- package/dist/agent-runtime/run-execution.d.ts.map +1 -1
- package/dist/agent-runtime/runtime-internals.d.ts.map +1 -1
- package/dist/agent-runtime/runtime.d.ts.map +1 -1
- package/dist/agent-runtime/schemas.d.ts +75 -21
- package/dist/agent-runtime/schemas.d.ts.map +1 -1
- package/dist/agent-runtime/store-driver.d.ts +21 -15
- package/dist/agent-runtime/store-driver.d.ts.map +1 -1
- package/dist/agent-runtime/store.d.ts +280 -46
- package/dist/agent-runtime/store.d.ts.map +1 -1
- package/dist/agent-runtime/terminal-commit.d.ts +33 -3
- package/dist/agent-runtime/terminal-commit.d.ts.map +1 -1
- package/dist/agent-runtime/terminal-status.d.ts.map +1 -1
- package/dist/agent-runtime-openrouter.js +1 -1
- package/dist/agent-runtime.d.ts +2 -2
- package/dist/agent-runtime.d.ts.map +1 -1
- package/dist/agent-runtime.js +385 -97
- package/dist/{index-sbkyvacf.js → index-fhsmrzj7.js} +45 -5
- package/dist/testing/agent-store-conformance.d.ts.map +1 -1
- package/dist/testing.js +149 -1
- package/llms-full.txt +252 -18
- package/package.json +1 -1
package/dist/agent-runtime.js
CHANGED
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
AgentMessageSchema,
|
|
14
14
|
AgentMessageStatusSchema,
|
|
15
15
|
AgentOpaquePartSchema,
|
|
16
|
+
AgentProvenanceSchema,
|
|
16
17
|
AgentProviderEnvelopeSchema,
|
|
17
18
|
AgentReasoningPartSchema,
|
|
18
19
|
AgentRecordIdSchema,
|
|
@@ -30,7 +31,7 @@ import {
|
|
|
30
31
|
AgentUsageSchema,
|
|
31
32
|
AgentUsageValueSchema,
|
|
32
33
|
runStateForTerminalReason
|
|
33
|
-
} from "./index-
|
|
34
|
+
} from "./index-fhsmrzj7.js";
|
|
34
35
|
import"./index-6djpbnda.js";
|
|
35
36
|
import"./index-cby4ar3v.js";
|
|
36
37
|
import {
|
|
@@ -56,6 +57,8 @@ function assistantStatus(reason) {
|
|
|
56
57
|
}
|
|
57
58
|
if (reason === "superseded")
|
|
58
59
|
return "superseded";
|
|
60
|
+
if (reason === "absorbed")
|
|
61
|
+
return "superseded";
|
|
59
62
|
if (reason === "interrupted" || reason === "cancelled" || reason === "shutdown") {
|
|
60
63
|
return "interrupted";
|
|
61
64
|
}
|
|
@@ -836,7 +839,12 @@ function createAgentObservability(config) {
|
|
|
836
839
|
import { z as z4 } from "zod";
|
|
837
840
|
var AgentTokenCountSchema = z4.object({
|
|
838
841
|
value: z4.int().nonnegative().optional(),
|
|
839
|
-
provenance:
|
|
842
|
+
provenance: AgentProvenanceSchema.extract([
|
|
843
|
+
"measured",
|
|
844
|
+
"computed",
|
|
845
|
+
"estimated",
|
|
846
|
+
"unavailable"
|
|
847
|
+
])
|
|
840
848
|
});
|
|
841
849
|
function completeTurn(messages) {
|
|
842
850
|
if (messages[0]?.role !== "user")
|
|
@@ -947,7 +955,7 @@ async function selectAgentHistory(options) {
|
|
|
947
955
|
return {
|
|
948
956
|
messages,
|
|
949
957
|
decisions,
|
|
950
|
-
totalTokens: { value: total, provenance: estimated ? "estimated" : "
|
|
958
|
+
totalTokens: { value: total, provenance: estimated ? "estimated" : "computed" },
|
|
951
959
|
outcome: total > options.availableTokens ? "oversized" : removed.size > 0 ? "truncated" : "fits"
|
|
952
960
|
};
|
|
953
961
|
}
|
|
@@ -964,11 +972,11 @@ function composeAgentPrompt(sections) {
|
|
|
964
972
|
const text = await section.render({ context: options.context, signal: options.signal });
|
|
965
973
|
rendered.push({ name: section.name, stability: section.stability, text });
|
|
966
974
|
let count;
|
|
967
|
-
if (section.estimateTokens)
|
|
968
|
-
count = await section.estimateTokens(text);
|
|
969
|
-
else if (options.estimateFallback)
|
|
970
|
-
count = await options.estimateFallback(text);
|
|
971
|
-
else
|
|
975
|
+
if (section.estimateTokens) {
|
|
976
|
+
count = AgentTokenCountSchema.parse(await section.estimateTokens(text));
|
|
977
|
+
} else if (options.estimateFallback) {
|
|
978
|
+
count = AgentTokenCountSchema.parse(await options.estimateFallback(text));
|
|
979
|
+
} else
|
|
972
980
|
count = { provenance: "unavailable" };
|
|
973
981
|
const value = knownValue(count);
|
|
974
982
|
if (value === undefined)
|
|
@@ -978,20 +986,23 @@ function composeAgentPrompt(sections) {
|
|
|
978
986
|
if (count.provenance === "estimated")
|
|
979
987
|
estimated = true;
|
|
980
988
|
}
|
|
981
|
-
const instructionTokens = unavailable ? { provenance: "unavailable" } : { value: total, provenance: estimated ? "estimated" : "
|
|
989
|
+
const instructionTokens = unavailable ? { provenance: "unavailable" } : { value: total, provenance: estimated ? "estimated" : "computed" };
|
|
982
990
|
let availableHistoryTokens;
|
|
983
991
|
let contextDecision = "unavailable";
|
|
984
992
|
if (options.budget) {
|
|
993
|
+
if (!Number.isSafeInteger(options.budget.contextWindow) || options.budget.contextWindow < 0 || !Number.isSafeInteger(options.budget.reservedOutput) || options.budget.reservedOutput < 0) {
|
|
994
|
+
throw new TypeError("contextWindow and reservedOutput must be non-negative safe integers");
|
|
995
|
+
}
|
|
985
996
|
const reserveValues = [
|
|
986
997
|
options.budget.reservedOutput,
|
|
987
|
-
knownValue(options.budget.toolSchemas),
|
|
988
|
-
knownValue(options.budget.attachments),
|
|
989
|
-
knownValue(options.budget.providerOverhead),
|
|
998
|
+
knownValue(AgentTokenCountSchema.parse(options.budget.toolSchemas)),
|
|
999
|
+
knownValue(AgentTokenCountSchema.parse(options.budget.attachments)),
|
|
1000
|
+
knownValue(AgentTokenCountSchema.parse(options.budget.providerOverhead)),
|
|
990
1001
|
knownValue(instructionTokens)
|
|
991
1002
|
];
|
|
992
1003
|
if (reserveValues.every((value) => value !== undefined)) {
|
|
993
1004
|
availableHistoryTokens = Math.max(0, options.budget.contextWindow - reserveValues.reduce((sum, value) => sum + (value ?? 0), 0));
|
|
994
|
-
const historyTokens = options.historyTokens ? knownValue(options.historyTokens) : undefined;
|
|
1005
|
+
const historyTokens = options.historyTokens ? knownValue(AgentTokenCountSchema.parse(options.historyTokens)) : undefined;
|
|
995
1006
|
if (historyTokens !== undefined) {
|
|
996
1007
|
if (historyTokens <= availableHistoryTokens)
|
|
997
1008
|
contextDecision = "fits";
|
|
@@ -1075,6 +1086,48 @@ function createRuntimeAdmissionLanes() {
|
|
|
1075
1086
|
};
|
|
1076
1087
|
}
|
|
1077
1088
|
|
|
1089
|
+
// src/agent-runtime/injection.ts
|
|
1090
|
+
function createAgentInjectionRegistry() {
|
|
1091
|
+
const offered = new Map;
|
|
1092
|
+
return {
|
|
1093
|
+
offer(key, entry) {
|
|
1094
|
+
const existing = offered.get(key);
|
|
1095
|
+
if (!existing) {
|
|
1096
|
+
offered.set(key, [entry]);
|
|
1097
|
+
return;
|
|
1098
|
+
}
|
|
1099
|
+
if (existing.some((candidate) => candidate.input.id === entry.input.id))
|
|
1100
|
+
return;
|
|
1101
|
+
existing.push(entry);
|
|
1102
|
+
},
|
|
1103
|
+
take(key, excludeRunId) {
|
|
1104
|
+
const existing = offered.get(key);
|
|
1105
|
+
if (!existing)
|
|
1106
|
+
return [];
|
|
1107
|
+
const taken = existing.filter((candidate) => candidate.runId !== excludeRunId);
|
|
1108
|
+
const kept = existing.filter((candidate) => candidate.runId === excludeRunId);
|
|
1109
|
+
if (kept.length === 0)
|
|
1110
|
+
offered.delete(key);
|
|
1111
|
+
else
|
|
1112
|
+
offered.set(key, kept);
|
|
1113
|
+
return taken;
|
|
1114
|
+
},
|
|
1115
|
+
withdraw(key, runId) {
|
|
1116
|
+
const existing = offered.get(key);
|
|
1117
|
+
if (!existing)
|
|
1118
|
+
return;
|
|
1119
|
+
const kept = existing.filter((candidate) => candidate.runId !== runId);
|
|
1120
|
+
if (kept.length === 0)
|
|
1121
|
+
offered.delete(key);
|
|
1122
|
+
else
|
|
1123
|
+
offered.set(key, kept);
|
|
1124
|
+
},
|
|
1125
|
+
clear() {
|
|
1126
|
+
offered.clear();
|
|
1127
|
+
}
|
|
1128
|
+
};
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1078
1131
|
// src/agent-runtime/run-execution.ts
|
|
1079
1132
|
import {
|
|
1080
1133
|
stepCountIs,
|
|
@@ -1096,16 +1149,33 @@ function appliedSnapshot(result, operation) {
|
|
|
1096
1149
|
return result.snapshot;
|
|
1097
1150
|
throw new AgentRuntimeConflictError(operation);
|
|
1098
1151
|
}
|
|
1099
|
-
function
|
|
1152
|
+
function viewOfSnapshot(snapshot, runId, retainedAssistant) {
|
|
1100
1153
|
const run = snapshot.runs.find((candidate) => candidate.id === runId);
|
|
1154
|
+
const assistant = snapshot.messages.find((message) => message.id === run?.assistantMessageId) ?? retainedAssistant;
|
|
1155
|
+
return {
|
|
1156
|
+
snapshotVersion: snapshot.version,
|
|
1157
|
+
conversationId: snapshot.conversationId,
|
|
1158
|
+
...run && { run },
|
|
1159
|
+
...assistant && { assistant }
|
|
1160
|
+
};
|
|
1161
|
+
}
|
|
1162
|
+
function viewOfRun(view, conversationId) {
|
|
1163
|
+
return {
|
|
1164
|
+
snapshotVersion: view.snapshotVersion,
|
|
1165
|
+
conversationId,
|
|
1166
|
+
run: view.run,
|
|
1167
|
+
...view.assistant && { assistant: view.assistant }
|
|
1168
|
+
};
|
|
1169
|
+
}
|
|
1170
|
+
function canonicalTerminal(view) {
|
|
1171
|
+
const { run, assistant } = view;
|
|
1101
1172
|
if (!run?.terminalReason)
|
|
1102
1173
|
return;
|
|
1103
|
-
|
|
1104
|
-
if (snapshot.conversationId !== run.conversationId || !assistant || assistant.id !== run.assistantMessageId || assistant.conversationId !== run.conversationId || assistant.runId !== run.id || assistant.role !== "assistant" || assistant.status !== assistantStatus(run.terminalReason)) {
|
|
1174
|
+
if (view.conversationId !== run.conversationId || !assistant || assistant.id !== run.assistantMessageId || assistant.conversationId !== run.conversationId || assistant.runId !== run.id || assistant.role !== "assistant" || assistant.status !== assistantStatus(run.terminalReason)) {
|
|
1105
1175
|
throw new AgentRuntimeConflictError("terminal result projection");
|
|
1106
1176
|
}
|
|
1107
1177
|
return {
|
|
1108
|
-
|
|
1178
|
+
snapshotVersion: view.snapshotVersion,
|
|
1109
1179
|
run,
|
|
1110
1180
|
assistant,
|
|
1111
1181
|
reason: run.terminalReason,
|
|
@@ -1136,9 +1206,10 @@ function interruptedCandidate(candidate, run, now) {
|
|
|
1136
1206
|
function canRetryTerminal(current, previous, runtimeEpoch) {
|
|
1137
1207
|
return (current.state === "running" || current.state === "interrupt_requested") && current.ownerId === runtimeEpoch && current.fencingToken === previous.fencingToken;
|
|
1138
1208
|
}
|
|
1209
|
+
var TERMINAL_COMMIT_ATTEMPTS = 32;
|
|
1139
1210
|
async function commitAgentRunTerminal(input) {
|
|
1140
1211
|
let candidate = input.candidate;
|
|
1141
|
-
|
|
1212
|
+
for (let attempt = 0;attempt < TERMINAL_COMMIT_ATTEMPTS; attempt += 1) {
|
|
1142
1213
|
const committed = await input.store.commitRunTerminal({
|
|
1143
1214
|
conversationId: candidate.run.conversationId,
|
|
1144
1215
|
runId: candidate.run.id,
|
|
@@ -1150,16 +1221,22 @@ async function commitAgentRunTerminal(input) {
|
|
|
1150
1221
|
assistant: candidate.assistant,
|
|
1151
1222
|
reason: candidate.reason,
|
|
1152
1223
|
...candidate.policyName && { policyName: candidate.policyName },
|
|
1153
|
-
...candidate.usage && { usage: candidate.usage }
|
|
1224
|
+
...candidate.usage && { usage: candidate.usage },
|
|
1225
|
+
...candidate.absorb?.length && { absorb: candidate.absorb }
|
|
1154
1226
|
});
|
|
1155
1227
|
if (committed.outcome === "applied") {
|
|
1156
|
-
const terminal2 = canonicalTerminal(committed.snapshot, candidate.run.id);
|
|
1228
|
+
const terminal2 = canonicalTerminal(viewOfSnapshot(committed.snapshot, candidate.run.id));
|
|
1157
1229
|
if (!terminal2)
|
|
1158
1230
|
throw new AgentRuntimeConflictError("terminal result projection");
|
|
1159
|
-
|
|
1231
|
+
const absorbed = committed.snapshot.runs.filter((run) => run.absorbedIntoRunId === candidate.run.id);
|
|
1232
|
+
return {
|
|
1233
|
+
...terminal2,
|
|
1234
|
+
committedByCaller: true,
|
|
1235
|
+
...absorbed.length > 0 && { absorbed }
|
|
1236
|
+
};
|
|
1160
1237
|
}
|
|
1161
1238
|
if (committed.outcome === "duplicate") {
|
|
1162
|
-
const terminal2 = canonicalTerminal(committed.snapshot, candidate.run.id, committed.assistant);
|
|
1239
|
+
const terminal2 = canonicalTerminal(viewOfSnapshot(committed.snapshot, candidate.run.id, committed.assistant));
|
|
1163
1240
|
if (!terminal2)
|
|
1164
1241
|
throw new AgentRuntimeConflictError("terminal result projection");
|
|
1165
1242
|
return terminal2;
|
|
@@ -1167,16 +1244,20 @@ async function commitAgentRunTerminal(input) {
|
|
|
1167
1244
|
if (committed.outcome !== "conflict") {
|
|
1168
1245
|
throw new AgentRuntimeConflictError("terminal commit");
|
|
1169
1246
|
}
|
|
1170
|
-
const latest = await input.store.
|
|
1171
|
-
|
|
1247
|
+
const latest = await input.store.loadRun({
|
|
1248
|
+
conversationId: candidate.run.conversationId,
|
|
1249
|
+
runId: candidate.run.id
|
|
1250
|
+
});
|
|
1251
|
+
const terminal = latest ? canonicalTerminal(viewOfRun(latest, candidate.run.conversationId)) : undefined;
|
|
1172
1252
|
if (terminal)
|
|
1173
1253
|
return terminal;
|
|
1174
|
-
const current = latest
|
|
1254
|
+
const current = latest?.run;
|
|
1175
1255
|
if (!current || !canRetryTerminal(current, candidate.run, input.runtimeEpoch)) {
|
|
1176
1256
|
throw new AgentRuntimeConflictError("terminal commit");
|
|
1177
1257
|
}
|
|
1178
1258
|
candidate = current.state === "interrupt_requested" ? interruptedCandidate(candidate, current, input.now) : { ...candidate, run: current };
|
|
1179
1259
|
}
|
|
1260
|
+
throw new AgentRuntimeConflictError("terminal commit");
|
|
1180
1261
|
}
|
|
1181
1262
|
|
|
1182
1263
|
// src/agent-runtime/runtime-internals.ts
|
|
@@ -1285,7 +1366,7 @@ function unknownUsage() {
|
|
|
1285
1366
|
};
|
|
1286
1367
|
}
|
|
1287
1368
|
function normalizeSdkUsage(value) {
|
|
1288
|
-
const reported = (tokens) => tokens === undefined ? { provenance: "unavailable" } : { value: tokens, provenance: "provider-reported" };
|
|
1369
|
+
const reported = (tokens) => tokens === undefined || !Number.isSafeInteger(tokens) || tokens < 0 ? { provenance: "unavailable" } : { value: tokens, provenance: "provider-reported" };
|
|
1289
1370
|
return {
|
|
1290
1371
|
inputTokens: reported(value.inputTokens),
|
|
1291
1372
|
outputTokens: reported(value.outputTokens),
|
|
@@ -1329,11 +1410,36 @@ function createRunExecutor(dependencies) {
|
|
|
1329
1410
|
now,
|
|
1330
1411
|
checkpointEveryEvents,
|
|
1331
1412
|
maxSteps,
|
|
1332
|
-
idleTimeoutMs
|
|
1413
|
+
idleTimeoutMs,
|
|
1414
|
+
injection
|
|
1333
1415
|
} = dependencies;
|
|
1334
1416
|
return async function executeRun(input) {
|
|
1335
|
-
|
|
1336
|
-
const
|
|
1417
|
+
injection?.withdraw(input.key, input.acceptedRun.id);
|
|
1418
|
+
const queued = await config.store.loadRun({
|
|
1419
|
+
conversationId: input.acceptedRun.conversationId,
|
|
1420
|
+
runId: input.acceptedRun.id
|
|
1421
|
+
});
|
|
1422
|
+
if (!queued)
|
|
1423
|
+
throw new AgentRuntimeConflictError("run lookup");
|
|
1424
|
+
if (queued.run.terminalReason === "absorbed") {
|
|
1425
|
+
const absorbing = queued.run.absorbedIntoRunId ? await config.store.loadRun({
|
|
1426
|
+
conversationId: queued.run.conversationId,
|
|
1427
|
+
runId: queued.run.absorbedIntoRunId
|
|
1428
|
+
}) : undefined;
|
|
1429
|
+
if (!absorbing?.assistant || !absorbing.run.terminalReason) {
|
|
1430
|
+
throw new AgentRuntimeConflictError("absorbed run resolution");
|
|
1431
|
+
}
|
|
1432
|
+
return {
|
|
1433
|
+
run: absorbing.run,
|
|
1434
|
+
message: absorbing.assistant,
|
|
1435
|
+
reason: absorbing.run.terminalReason,
|
|
1436
|
+
snapshotVersion: absorbing.snapshotVersion,
|
|
1437
|
+
...absorbing.run.terminalPolicyName && {
|
|
1438
|
+
policyName: absorbing.run.terminalPolicyName
|
|
1439
|
+
}
|
|
1440
|
+
};
|
|
1441
|
+
}
|
|
1442
|
+
const queuedRun = queued.run;
|
|
1337
1443
|
const acquired = appliedSnapshot(await config.store.acquireRun({
|
|
1338
1444
|
conversationId: queuedRun.conversationId,
|
|
1339
1445
|
runId: queuedRun.id,
|
|
@@ -1385,7 +1491,9 @@ function createRunExecutor(dependencies) {
|
|
|
1385
1491
|
assistant
|
|
1386
1492
|
}), "assistant draft");
|
|
1387
1493
|
run = findRun(snapshot.runs, run.id);
|
|
1494
|
+
let observedVersion = snapshot.version;
|
|
1388
1495
|
const parts = [];
|
|
1496
|
+
const absorbed = new Map;
|
|
1389
1497
|
let eventCount = 0;
|
|
1390
1498
|
let sequence = 0;
|
|
1391
1499
|
let terminalReason = "success";
|
|
@@ -1436,6 +1544,7 @@ function createRunExecutor(dependencies) {
|
|
|
1436
1544
|
assistant,
|
|
1437
1545
|
usage: statedUsage(usage)
|
|
1438
1546
|
}), "assistant checkpoint");
|
|
1547
|
+
observedVersion = snapshot.version;
|
|
1439
1548
|
run = findRun(snapshot.runs, run.id);
|
|
1440
1549
|
const checkpointMetrics = {
|
|
1441
1550
|
partial: true,
|
|
@@ -1445,10 +1554,10 @@ function createRunExecutor(dependencies) {
|
|
|
1445
1554
|
};
|
|
1446
1555
|
await publish({
|
|
1447
1556
|
type: "assistant-checkpoint",
|
|
1448
|
-
eventId: agentDurableEventId("assistant-checkpoint", run.id,
|
|
1557
|
+
eventId: agentDurableEventId("assistant-checkpoint", run.id, observedVersion),
|
|
1449
1558
|
conversationId: run.conversationId,
|
|
1450
1559
|
runId: run.id,
|
|
1451
|
-
snapshotVersion:
|
|
1560
|
+
snapshotVersion: observedVersion,
|
|
1452
1561
|
message: assistant,
|
|
1453
1562
|
metrics: checkpointMetrics,
|
|
1454
1563
|
emittedAt: now().toISOString()
|
|
@@ -1462,6 +1571,7 @@ function createRunExecutor(dependencies) {
|
|
|
1462
1571
|
signal: executionSignal
|
|
1463
1572
|
});
|
|
1464
1573
|
snapshot = compacted.snapshot;
|
|
1574
|
+
observedVersion = snapshot.version;
|
|
1465
1575
|
run = findRun(snapshot.runs, run.id);
|
|
1466
1576
|
if (compacted.usage)
|
|
1467
1577
|
usage = addUsage(usage, compacted.usage);
|
|
@@ -1469,8 +1579,11 @@ function createRunExecutor(dependencies) {
|
|
|
1469
1579
|
const assertCurrent = async () => {
|
|
1470
1580
|
if (executionSignal.aborted)
|
|
1471
1581
|
return "run_interrupted";
|
|
1472
|
-
const current = await config.store.
|
|
1473
|
-
|
|
1582
|
+
const current = await config.store.loadRun({
|
|
1583
|
+
conversationId: run.conversationId,
|
|
1584
|
+
runId: run.id
|
|
1585
|
+
});
|
|
1586
|
+
const currentRun = current?.run;
|
|
1474
1587
|
if (!currentRun || currentRun.ownerId !== runtimeEpoch)
|
|
1475
1588
|
return "stale_run";
|
|
1476
1589
|
if (currentRun.fencingToken !== run.fencingToken)
|
|
@@ -1531,6 +1644,20 @@ function createRunExecutor(dependencies) {
|
|
|
1531
1644
|
carriedSystem = detailed.system;
|
|
1532
1645
|
return [...detailed.messages];
|
|
1533
1646
|
};
|
|
1647
|
+
const projectInputs = async (source) => {
|
|
1648
|
+
if (config.history?.project)
|
|
1649
|
+
return config.history.project(source);
|
|
1650
|
+
const detailed = await projectAgentHistoryDetailed(source, {
|
|
1651
|
+
...config.history?.resolveFile && { resolveFile: config.history.resolveFile },
|
|
1652
|
+
...config.history?.unresolvedFile && {
|
|
1653
|
+
unresolvedFile: config.history.unresolvedFile
|
|
1654
|
+
},
|
|
1655
|
+
...config.history?.interruptedAssistant && {
|
|
1656
|
+
interruptedAssistant: config.history.interruptedAssistant
|
|
1657
|
+
}
|
|
1658
|
+
});
|
|
1659
|
+
return [...detailed.messages];
|
|
1660
|
+
};
|
|
1534
1661
|
const history = await projectHistory(snapshot);
|
|
1535
1662
|
const withCarriedSystem = (instructions) => {
|
|
1536
1663
|
if (carriedSystem.length === 0)
|
|
@@ -1558,6 +1685,19 @@ function createRunExecutor(dependencies) {
|
|
|
1558
1685
|
return stopped;
|
|
1559
1686
|
});
|
|
1560
1687
|
}
|
|
1688
|
+
const takeInjectedMessages = async () => {
|
|
1689
|
+
const taken = injection?.take(input.key, run.id) ?? [];
|
|
1690
|
+
if (taken.length === 0)
|
|
1691
|
+
return [];
|
|
1692
|
+
const messages = [];
|
|
1693
|
+
for (const entry of taken) {
|
|
1694
|
+
messages.push(...await projectInputs([entry.input]));
|
|
1695
|
+
const ids = absorbed.get(entry.runId) ?? [];
|
|
1696
|
+
ids.push(entry.input.id);
|
|
1697
|
+
absorbed.set(entry.runId, ids);
|
|
1698
|
+
}
|
|
1699
|
+
return messages;
|
|
1700
|
+
};
|
|
1561
1701
|
const result = streamText({
|
|
1562
1702
|
model: selectedModel.model,
|
|
1563
1703
|
tools,
|
|
@@ -1566,8 +1706,18 @@ function createRunExecutor(dependencies) {
|
|
|
1566
1706
|
abortSignal: executionSignal,
|
|
1567
1707
|
maxRetries: 0,
|
|
1568
1708
|
stopWhen: stopConditions,
|
|
1569
|
-
...config.loop?.prepareStep && {
|
|
1570
|
-
prepareStep: (options) =>
|
|
1709
|
+
...(config.loop?.prepareStep || injection) && {
|
|
1710
|
+
prepareStep: async (options) => {
|
|
1711
|
+
const prepared = await config.loop?.prepareStep?.({
|
|
1712
|
+
...options,
|
|
1713
|
+
...runtimeContext
|
|
1714
|
+
});
|
|
1715
|
+
const injected = await takeInjectedMessages();
|
|
1716
|
+
if (injected.length === 0)
|
|
1717
|
+
return prepared;
|
|
1718
|
+
const base = prepared?.messages ?? options.messages;
|
|
1719
|
+
return { ...prepared, messages: [...base, ...injected] };
|
|
1720
|
+
}
|
|
1571
1721
|
}
|
|
1572
1722
|
});
|
|
1573
1723
|
for await (const part of result.stream) {
|
|
@@ -1818,11 +1968,14 @@ function createRunExecutor(dependencies) {
|
|
|
1818
1968
|
terminalReason = abortTerminalReason(executionSignal);
|
|
1819
1969
|
} catch (error) {
|
|
1820
1970
|
internalCause = error;
|
|
1821
|
-
const latest = await config.store.
|
|
1822
|
-
|
|
1971
|
+
const latest = await config.store.loadRun({
|
|
1972
|
+
conversationId: run.conversationId,
|
|
1973
|
+
runId: run.id
|
|
1974
|
+
});
|
|
1975
|
+
const latestRun = latest?.run;
|
|
1823
1976
|
const durableInterrupt = latestRun?.ownerId === runtimeEpoch && latestRun.state === "interrupt_requested";
|
|
1824
|
-
if (durableInterrupt && latestRun) {
|
|
1825
|
-
|
|
1977
|
+
if (durableInterrupt && latest && latestRun) {
|
|
1978
|
+
observedVersion = latest.snapshotVersion;
|
|
1826
1979
|
run = latestRun;
|
|
1827
1980
|
}
|
|
1828
1981
|
if (isToolExecutionControlError(error) || executionSignal.aborted || durableInterrupt) {
|
|
@@ -1875,19 +2028,25 @@ function createRunExecutor(dependencies) {
|
|
|
1875
2028
|
assistant,
|
|
1876
2029
|
reason: terminalReason,
|
|
1877
2030
|
...terminalPolicyName && { policyName: terminalPolicyName },
|
|
1878
|
-
usage: spent
|
|
2031
|
+
usage: spent,
|
|
2032
|
+
...absorbed.size > 0 && runStateForTerminalReason(terminalReason) === "completed" && {
|
|
2033
|
+
absorb: [...absorbed].map(([runId, inputMessageIds]) => ({
|
|
2034
|
+
runId,
|
|
2035
|
+
inputMessageIds
|
|
2036
|
+
}))
|
|
2037
|
+
}
|
|
1879
2038
|
},
|
|
1880
2039
|
now
|
|
1881
2040
|
});
|
|
1882
2041
|
} catch (error) {
|
|
1883
2042
|
emitSpend({
|
|
1884
|
-
eventId: unsettledEventId(
|
|
2043
|
+
eventId: unsettledEventId(observedVersion),
|
|
1885
2044
|
state: run.state,
|
|
1886
2045
|
reason: terminalReason
|
|
1887
2046
|
});
|
|
1888
2047
|
throw error;
|
|
1889
2048
|
}
|
|
1890
|
-
|
|
2049
|
+
observedVersion = terminal.snapshotVersion;
|
|
1891
2050
|
run = terminal.run;
|
|
1892
2051
|
assistant = terminal.assistant;
|
|
1893
2052
|
terminalReason = terminal.reason;
|
|
@@ -1899,17 +2058,28 @@ function createRunExecutor(dependencies) {
|
|
|
1899
2058
|
...firstOutputAt !== undefined && { ttftMs: firstOutputAt - runStartedAt }
|
|
1900
2059
|
} : undefined;
|
|
1901
2060
|
emitSpend({
|
|
1902
|
-
eventId: terminal.committedByCaller ? agentDurableEventId("terminal", run.id,
|
|
2061
|
+
eventId: terminal.committedByCaller ? agentDurableEventId("terminal", run.id, observedVersion) : unsettledEventId(observedVersion),
|
|
1903
2062
|
state: run.state,
|
|
1904
2063
|
reason: terminalReason
|
|
1905
2064
|
});
|
|
2065
|
+
for (const settled of terminal.absorbed ?? []) {
|
|
2066
|
+
await publish({
|
|
2067
|
+
type: "run-state",
|
|
2068
|
+
eventId: agentDurableEventId("run-state", settled.id, observedVersion),
|
|
2069
|
+
conversationId: settled.conversationId,
|
|
2070
|
+
runId: settled.id,
|
|
2071
|
+
snapshotVersion: observedVersion,
|
|
2072
|
+
state: settled.state,
|
|
2073
|
+
emittedAt: now().toISOString()
|
|
2074
|
+
});
|
|
2075
|
+
}
|
|
1906
2076
|
if (terminalMetrics) {
|
|
1907
2077
|
await publish({
|
|
1908
2078
|
type: "terminal",
|
|
1909
|
-
eventId: agentDurableEventId("terminal", run.id,
|
|
2079
|
+
eventId: agentDurableEventId("terminal", run.id, observedVersion),
|
|
1910
2080
|
conversationId: run.conversationId,
|
|
1911
2081
|
runId: run.id,
|
|
1912
|
-
snapshotVersion:
|
|
2082
|
+
snapshotVersion: observedVersion,
|
|
1913
2083
|
reason: terminalReason,
|
|
1914
2084
|
...terminalPolicyName && { policyName: terminalPolicyName },
|
|
1915
2085
|
message: assistant,
|
|
@@ -1921,7 +2091,7 @@ function createRunExecutor(dependencies) {
|
|
|
1921
2091
|
run,
|
|
1922
2092
|
message: assistant,
|
|
1923
2093
|
reason: terminalReason,
|
|
1924
|
-
snapshotVersion:
|
|
2094
|
+
snapshotVersion: observedVersion,
|
|
1925
2095
|
...terminalMetrics && { metrics: terminalMetrics },
|
|
1926
2096
|
...terminalPolicyName && { policyName: terminalPolicyName }
|
|
1927
2097
|
};
|
|
@@ -1936,6 +2106,8 @@ function createAgentRuntime(config) {
|
|
|
1936
2106
|
const now = config.now ?? (() => new Date);
|
|
1937
2107
|
const runtimeEpoch = generateId();
|
|
1938
2108
|
const admissionLanes = createRuntimeAdmissionLanes();
|
|
2109
|
+
const injectionPossible = typeof config.runs?.inputPolicy === "function" || config.runs?.inputPolicy === "inject";
|
|
2110
|
+
const injection = injectionPossible ? createAgentInjectionRegistry() : undefined;
|
|
1939
2111
|
const reserveAdmission = admissionLanes.reserve;
|
|
1940
2112
|
const settleAdmission = admissionLanes.settle;
|
|
1941
2113
|
const waitForAdmissionAcceptances = admissionLanes.waitForAcceptances;
|
|
@@ -1976,7 +2148,8 @@ function createAgentRuntime(config) {
|
|
|
1976
2148
|
now,
|
|
1977
2149
|
checkpointEveryEvents,
|
|
1978
2150
|
maxSteps,
|
|
1979
|
-
...idleTimeoutMs !== undefined && { idleTimeoutMs }
|
|
2151
|
+
...idleTimeoutMs !== undefined && { idleTimeoutMs },
|
|
2152
|
+
...injection && { injection }
|
|
1980
2153
|
});
|
|
1981
2154
|
let admissionClosed = false;
|
|
1982
2155
|
const closedError = () => new Error("[stitchkit] agent runtime is closed and admits no further work");
|
|
@@ -2033,8 +2206,13 @@ function createAgentRuntime(config) {
|
|
|
2033
2206
|
const handedOff = beginAdmission();
|
|
2034
2207
|
(async () => {
|
|
2035
2208
|
try {
|
|
2036
|
-
const
|
|
2037
|
-
|
|
2209
|
+
const recovered = await config.store.loadRun({
|
|
2210
|
+
conversationId: rawInput.conversationId,
|
|
2211
|
+
runId: rawInput.runId
|
|
2212
|
+
});
|
|
2213
|
+
if (!recovered)
|
|
2214
|
+
throw new AgentRuntimeConflictError("run lookup");
|
|
2215
|
+
const recoveredRun = recovered.run;
|
|
2038
2216
|
if (recoveredRun.state !== "queued") {
|
|
2039
2217
|
throw new Error("Only a queued recovered agent run can be resumed");
|
|
2040
2218
|
}
|
|
@@ -2044,7 +2222,12 @@ function createAgentRuntime(config) {
|
|
|
2044
2222
|
policy: "queue",
|
|
2045
2223
|
create: (signal) => ({
|
|
2046
2224
|
runId: recoveredRun.id,
|
|
2047
|
-
execute: () => executeRun({
|
|
2225
|
+
execute: () => executeRun({
|
|
2226
|
+
acceptedRun: recoveredRun,
|
|
2227
|
+
context,
|
|
2228
|
+
signal,
|
|
2229
|
+
key: rawInput.conversationKey ?? rawInput.conversationId
|
|
2230
|
+
})
|
|
2048
2231
|
})
|
|
2049
2232
|
});
|
|
2050
2233
|
ticket.accepted.catch(() => {
|
|
@@ -2134,7 +2317,10 @@ function createAgentRuntime(config) {
|
|
|
2134
2317
|
currentConversationTickets.set(input.idempotencyKey, publicTicket);
|
|
2135
2318
|
if (!conversationTickets)
|
|
2136
2319
|
tickets.set(input.conversationId, currentConversationTickets);
|
|
2320
|
+
let offeredRunId;
|
|
2137
2321
|
const forgetTicket = () => {
|
|
2322
|
+
if (offeredRunId !== undefined)
|
|
2323
|
+
injection?.withdraw(key, offeredRunId);
|
|
2138
2324
|
if (currentConversationTickets.get(input.idempotencyKey) !== publicTicket)
|
|
2139
2325
|
return;
|
|
2140
2326
|
currentConversationTickets.delete(input.idempotencyKey);
|
|
@@ -2246,6 +2432,10 @@ function createAgentRuntime(config) {
|
|
|
2246
2432
|
}
|
|
2247
2433
|
return;
|
|
2248
2434
|
}
|
|
2435
|
+
if (policy === "inject") {
|
|
2436
|
+
offeredRunId = acceptedRun.id;
|
|
2437
|
+
injection?.offer(key, { runId: acceptedRun.id, input: acceptedInput });
|
|
2438
|
+
}
|
|
2249
2439
|
if (reservation && !reservation.shouldSchedule) {
|
|
2250
2440
|
reservation.admission.completion.promise.then(outerResult.resolve, outerResult.reject);
|
|
2251
2441
|
return;
|
|
@@ -2258,7 +2448,7 @@ function createAgentRuntime(config) {
|
|
|
2258
2448
|
await waitForAdmissionAcceptances(reservation.lane);
|
|
2259
2449
|
return {
|
|
2260
2450
|
runId: acceptedRun.id,
|
|
2261
|
-
execute: () => executeRun({ acceptedRun, context, signal })
|
|
2451
|
+
execute: () => executeRun({ acceptedRun, context, signal, key })
|
|
2262
2452
|
};
|
|
2263
2453
|
}
|
|
2264
2454
|
});
|
|
@@ -2294,12 +2484,16 @@ function createAgentRuntime(config) {
|
|
|
2294
2484
|
},
|
|
2295
2485
|
resume,
|
|
2296
2486
|
async interrupt(input) {
|
|
2297
|
-
const
|
|
2298
|
-
|
|
2487
|
+
const view = await config.store.loadRun({
|
|
2488
|
+
conversationId: input.conversationId,
|
|
2489
|
+
runId: input.runId
|
|
2490
|
+
});
|
|
2491
|
+
if (!view)
|
|
2492
|
+
throw new AgentRuntimeConflictError("run lookup");
|
|
2299
2493
|
const requested = await config.store.requestRunInterrupt({
|
|
2300
2494
|
conversationId: input.conversationId,
|
|
2301
2495
|
runId: input.runId,
|
|
2302
|
-
expectedRevision: run.revision
|
|
2496
|
+
expectedRevision: view.run.revision
|
|
2303
2497
|
});
|
|
2304
2498
|
if (requested.outcome === "applied") {
|
|
2305
2499
|
const interruptedRun = findRun(requested.snapshot.runs, input.runId);
|
|
@@ -2342,8 +2536,8 @@ function createAgentRuntime(config) {
|
|
|
2342
2536
|
const handedOff = beginAdmission();
|
|
2343
2537
|
try {
|
|
2344
2538
|
if (item.run.state === "queued") {
|
|
2345
|
-
const
|
|
2346
|
-
const blockedByAcquiredPredecessor =
|
|
2539
|
+
const active = await config.store.listActiveRuns(item.conversationId);
|
|
2540
|
+
const blockedByAcquiredPredecessor = active.some((run) => run.id !== item.run.id && (run.state === "running" || run.state === "interrupt_requested"));
|
|
2347
2541
|
if (blockedByAcquiredPredecessor) {
|
|
2348
2542
|
outcomes.push({
|
|
2349
2543
|
conversationId: item.conversationId,
|
|
@@ -2430,6 +2624,7 @@ function createAgentRuntime(config) {
|
|
|
2430
2624
|
assertCloseBudgets(options);
|
|
2431
2625
|
admissionClosed = true;
|
|
2432
2626
|
const startedAt = performance.now();
|
|
2627
|
+
injection?.clear();
|
|
2433
2628
|
const stranded = await drainAdmissions(options.forceTimeoutMs);
|
|
2434
2629
|
const spent = Math.max(0, Math.round(performance.now() - startedAt));
|
|
2435
2630
|
const remainingBudget = (value) => value === undefined ? undefined : Math.max(0, value - spent);
|
|
@@ -2472,6 +2667,11 @@ var AgentStoreMutationResultSchema = z6.discriminatedUnion("outcome", [
|
|
|
2472
2667
|
AgentStoreConflictSchema,
|
|
2473
2668
|
AgentStoreNotFoundSchema
|
|
2474
2669
|
]);
|
|
2670
|
+
var AgentRunViewSchema = z6.object({
|
|
2671
|
+
snapshotVersion: AgentRecordVersionSchema,
|
|
2672
|
+
run: AgentRunSchema,
|
|
2673
|
+
assistant: AgentMessageSchema.optional()
|
|
2674
|
+
});
|
|
2475
2675
|
var AcceptInputAndAssignRunSchema = z6.object({
|
|
2476
2676
|
idempotencyKey: z6.string().min(1),
|
|
2477
2677
|
expectedVersion: AgentRecordVersionSchema.optional(),
|
|
@@ -2503,7 +2703,11 @@ var CommitRunTerminalSchema = z6.object({
|
|
|
2503
2703
|
assistant: AgentMessageSchema,
|
|
2504
2704
|
reason: AgentTerminalReasonSchema,
|
|
2505
2705
|
policyName: z6.string().min(1).optional(),
|
|
2506
|
-
usage: AgentUsageSchema.optional()
|
|
2706
|
+
usage: AgentUsageSchema.optional(),
|
|
2707
|
+
absorb: z6.array(z6.object({
|
|
2708
|
+
runId: AgentRecordIdSchema,
|
|
2709
|
+
inputMessageIds: z6.array(AgentRecordIdSchema).min(1)
|
|
2710
|
+
})).min(1).optional()
|
|
2507
2711
|
});
|
|
2508
2712
|
var RequestRunInterruptSchema = z6.object({
|
|
2509
2713
|
conversationId: AgentRecordIdSchema,
|
|
@@ -2665,9 +2869,9 @@ function applied(current, input, effects) {
|
|
|
2665
2869
|
runs: input.runs ?? current.runs,
|
|
2666
2870
|
messages: input.messages ?? current.messages
|
|
2667
2871
|
}),
|
|
2668
|
-
...effects?.
|
|
2872
|
+
...effects?.runRecords?.length && { runRecords: effects.runRecords },
|
|
2669
2873
|
...effects?.admissionReceipt && { admissionReceipt: effects.admissionReceipt },
|
|
2670
|
-
...effects?.
|
|
2874
|
+
...effects?.historyMutations?.length && { historyMutations: effects.historyMutations }
|
|
2671
2875
|
};
|
|
2672
2876
|
}
|
|
2673
2877
|
function reduceStore(current, operation) {
|
|
@@ -2701,12 +2905,9 @@ function reduceStore(current, operation) {
|
|
|
2701
2905
|
messages: [...current.messages, input.input],
|
|
2702
2906
|
runs: coalescedRun ? replaceRun(current.runs, assignedRun) : [...current.runs, assignedRun]
|
|
2703
2907
|
}, {
|
|
2704
|
-
|
|
2705
|
-
schemaVersion: 1,
|
|
2706
|
-
run: assignedRun
|
|
2707
|
-
}),
|
|
2908
|
+
runRecords: [AgentStoredRunSchema.parse({ schemaVersion: 1, run: assignedRun })],
|
|
2708
2909
|
admissionReceipt,
|
|
2709
|
-
|
|
2910
|
+
historyMutations: [{ type: "admit", input: input.input }]
|
|
2710
2911
|
});
|
|
2711
2912
|
}
|
|
2712
2913
|
const conversationId = operation.input.conversationId;
|
|
@@ -2728,7 +2929,7 @@ function reduceStore(current, operation) {
|
|
|
2728
2929
|
updatedAt: new Date().toISOString()
|
|
2729
2930
|
});
|
|
2730
2931
|
return applied(current, { runs: replaceRun(current.runs, next) }, {
|
|
2731
|
-
|
|
2932
|
+
runRecords: [AgentStoredRunSchema.parse({ schemaVersion: 1, run: next })]
|
|
2732
2933
|
});
|
|
2733
2934
|
}
|
|
2734
2935
|
if (operation.type === "checkpoint" && run) {
|
|
@@ -2746,8 +2947,8 @@ function reduceStore(current, operation) {
|
|
|
2746
2947
|
runs: replaceRun(current.runs, next),
|
|
2747
2948
|
messages: replaceMessage(current.messages, input.assistant)
|
|
2748
2949
|
}, {
|
|
2749
|
-
|
|
2750
|
-
|
|
2950
|
+
runRecords: [AgentStoredRunSchema.parse({ schemaVersion: 1, run: next })],
|
|
2951
|
+
historyMutations: [{ type: "upsert-assistant", message: input.assistant }]
|
|
2751
2952
|
});
|
|
2752
2953
|
}
|
|
2753
2954
|
if (operation.type === "interrupt" && run) {
|
|
@@ -2761,7 +2962,7 @@ function reduceStore(current, operation) {
|
|
|
2761
2962
|
updatedAt: new Date().toISOString()
|
|
2762
2963
|
});
|
|
2763
2964
|
return applied(current, { runs: replaceRun(current.runs, next) }, {
|
|
2764
|
-
|
|
2965
|
+
runRecords: [AgentStoredRunSchema.parse({ schemaVersion: 1, run: next })]
|
|
2765
2966
|
});
|
|
2766
2967
|
}
|
|
2767
2968
|
if (operation.type === "recover" && run) {
|
|
@@ -2799,42 +3000,82 @@ function reduceStore(current, operation) {
|
|
|
2799
3000
|
runs: replaceRun(current.runs, next),
|
|
2800
3001
|
messages: replaceMessage(current.messages, assistant)
|
|
2801
3002
|
}, {
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
3003
|
+
runRecords: [
|
|
3004
|
+
AgentStoredRunSchema.parse({
|
|
3005
|
+
schemaVersion: 1,
|
|
3006
|
+
run: next,
|
|
3007
|
+
terminalAssistant: assistant
|
|
3008
|
+
})
|
|
3009
|
+
],
|
|
3010
|
+
historyMutations: [{ type: "upsert-assistant", message: assistant }]
|
|
2808
3011
|
});
|
|
2809
3012
|
}
|
|
2810
3013
|
return applied(current, { runs: replaceRun(current.runs, next) }, {
|
|
2811
|
-
|
|
3014
|
+
runRecords: [AgentStoredRunSchema.parse({ schemaVersion: 1, run: next })]
|
|
2812
3015
|
});
|
|
2813
3016
|
}
|
|
2814
3017
|
if (operation.type === "terminal" && run) {
|
|
2815
3018
|
const input = operation.input;
|
|
3019
|
+
if (input.reason === "absorbed") {
|
|
3020
|
+
throw new TypeError("A run is absorbed by another run, never terminalized as absorbed");
|
|
3021
|
+
}
|
|
2816
3022
|
if (run.revision !== input.expectedRevision || run.state !== "running" && run.state !== "interrupt_requested" || run.ownerId !== input.ownerId || input.fencingToken !== undefined && run.fencingToken !== input.fencingToken || input.assistant.runId !== run.id || input.assistant.id !== run.assistantMessageId || input.assistant.conversationId !== run.conversationId || input.assistant.role !== "assistant" || input.assistant.status !== assistantStatus(input.reason)) {
|
|
2817
3023
|
return conflict(run.revision);
|
|
2818
3024
|
}
|
|
3025
|
+
if (input.absorb && runStateForTerminalReason(input.reason) !== "completed") {
|
|
3026
|
+
throw new TypeError("Only a completing run may absorb a queued successor");
|
|
3027
|
+
}
|
|
3028
|
+
const named = new Set((input.absorb ?? []).map((entry) => entry.runId));
|
|
3029
|
+
if (named.size !== (input.absorb ?? []).length || named.has(run.id)) {
|
|
3030
|
+
throw new TypeError("An absorption names each successor once, and never the absorbing run");
|
|
3031
|
+
}
|
|
3032
|
+
const absorbable = (input.absorb ?? []).filter((entry) => {
|
|
3033
|
+
const candidate = current.runs.find((item) => item.id === entry.runId);
|
|
3034
|
+
return candidate !== undefined && candidate.id !== run.id && candidate.state === "queued" && candidate.conversationId === run.conversationId && candidate.inputMessageIds.length === entry.inputMessageIds.length && candidate.inputMessageIds.every((id, index) => id === entry.inputMessageIds[index]);
|
|
3035
|
+
});
|
|
3036
|
+
const absorbedIds = new Set(absorbable.flatMap((entry) => entry.inputMessageIds));
|
|
2819
3037
|
const next = AgentRunSchema.parse({
|
|
2820
3038
|
...run,
|
|
2821
3039
|
state: runStateForTerminalReason(input.reason),
|
|
2822
3040
|
terminalReason: input.reason,
|
|
2823
3041
|
...input.policyName && { terminalPolicyName: input.policyName },
|
|
2824
3042
|
...input.usage && { usage: input.usage },
|
|
3043
|
+
...absorbedIds.size > 0 && {
|
|
3044
|
+
inputMessageIds: [
|
|
3045
|
+
...run.inputMessageIds,
|
|
3046
|
+
...[...absorbedIds].filter((id) => !run.inputMessageIds.includes(id))
|
|
3047
|
+
]
|
|
3048
|
+
},
|
|
2825
3049
|
revision: run.revision + 1,
|
|
2826
3050
|
updatedAt: new Date().toISOString()
|
|
2827
3051
|
});
|
|
3052
|
+
const absorbedRuns = absorbable.map((entry) => {
|
|
3053
|
+
const candidate = current.runs.find((item) => item.id === entry.runId);
|
|
3054
|
+
if (!candidate)
|
|
3055
|
+
throw new TypeError("Absorbed run disappeared inside the reducer");
|
|
3056
|
+
return AgentRunSchema.parse({
|
|
3057
|
+
...candidate,
|
|
3058
|
+
state: runStateForTerminalReason("absorbed"),
|
|
3059
|
+
terminalReason: "absorbed",
|
|
3060
|
+
absorbedIntoRunId: next.id,
|
|
3061
|
+
revision: candidate.revision + 1,
|
|
3062
|
+
updatedAt: new Date().toISOString()
|
|
3063
|
+
});
|
|
3064
|
+
});
|
|
3065
|
+
const runs = absorbedRuns.reduce((accumulated, absorbed) => replaceRun(accumulated, absorbed), replaceRun(current.runs, next));
|
|
2828
3066
|
return applied(current, {
|
|
2829
|
-
runs
|
|
3067
|
+
runs,
|
|
2830
3068
|
messages: replaceMessage(current.messages, input.assistant)
|
|
2831
3069
|
}, {
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
3070
|
+
runRecords: [
|
|
3071
|
+
AgentStoredRunSchema.parse({
|
|
3072
|
+
schemaVersion: 1,
|
|
3073
|
+
run: next,
|
|
3074
|
+
terminalAssistant: input.assistant
|
|
3075
|
+
}),
|
|
3076
|
+
...absorbedRuns.map((absorbed) => AgentStoredRunSchema.parse({ schemaVersion: 1, run: absorbed }))
|
|
3077
|
+
],
|
|
3078
|
+
historyMutations: [{ type: "upsert-assistant", message: input.assistant }]
|
|
2838
3079
|
});
|
|
2839
3080
|
}
|
|
2840
3081
|
if (operation.type === "compact") {
|
|
@@ -2862,11 +3103,13 @@ function reduceStore(current, operation) {
|
|
|
2862
3103
|
...current.messages.slice(first + positions.length)
|
|
2863
3104
|
];
|
|
2864
3105
|
return applied(current, { messages }, {
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
3106
|
+
historyMutations: [
|
|
3107
|
+
{
|
|
3108
|
+
type: "replace-compacted-range",
|
|
3109
|
+
replacedMessageIds: input.replacedMessageIds,
|
|
3110
|
+
summary: input.summary
|
|
3111
|
+
}
|
|
3112
|
+
]
|
|
2870
3113
|
});
|
|
2871
3114
|
}
|
|
2872
3115
|
return { outcome: "not_found" };
|
|
@@ -2912,6 +3155,37 @@ function createAgentRuntimeStore(driver) {
|
|
|
2912
3155
|
});
|
|
2913
3156
|
return snapshotOf(head, messages, mergeRunRecords(activeRecords, referencedRecords));
|
|
2914
3157
|
});
|
|
3158
|
+
const loadRun = (input) => driver.transaction(async (transaction) => {
|
|
3159
|
+
const [stored, record] = await Promise.all([
|
|
3160
|
+
driver.head.load(transaction, input.conversationId),
|
|
3161
|
+
driver.runs.load(transaction, input)
|
|
3162
|
+
]);
|
|
3163
|
+
if (!record)
|
|
3164
|
+
return;
|
|
3165
|
+
const parsed = AgentStoredRunSchema.parse(record);
|
|
3166
|
+
if (parsed.run.conversationId !== input.conversationId || parsed.run.id !== input.runId) {
|
|
3167
|
+
throw new TypeError("Stored run does not match the identity it was loaded by");
|
|
3168
|
+
}
|
|
3169
|
+
const head = AgentRuntimeHeadSchema.parse(stored ?? emptyHead(input.conversationId));
|
|
3170
|
+
return AgentRunViewSchema.parse({
|
|
3171
|
+
snapshotVersion: head.version,
|
|
3172
|
+
run: parsed.run,
|
|
3173
|
+
...parsed.terminalAssistant && { assistant: parsed.terminalAssistant }
|
|
3174
|
+
});
|
|
3175
|
+
});
|
|
3176
|
+
const listActiveRuns = (conversationId) => driver.transaction(async (transaction) => {
|
|
3177
|
+
const records = await driver.runs.listActive(transaction, conversationId);
|
|
3178
|
+
const runs = records.map((record) => AgentStoredRunSchema.parse(record).run);
|
|
3179
|
+
for (const run of runs) {
|
|
3180
|
+
if (run.conversationId !== conversationId) {
|
|
3181
|
+
throw new TypeError("Active run belongs to another conversation");
|
|
3182
|
+
}
|
|
3183
|
+
if (!isActiveRunState(run.state)) {
|
|
3184
|
+
throw new TypeError("Active run listing returned a terminal run");
|
|
3185
|
+
}
|
|
3186
|
+
}
|
|
3187
|
+
return runs.sort((left, right) => left.createdAt === right.createdAt ? left.id.localeCompare(right.id) : left.createdAt < right.createdAt ? -1 : 1);
|
|
3188
|
+
});
|
|
2915
3189
|
const mutate = (operation) => driver.transaction(async (transaction) => {
|
|
2916
3190
|
const conversationId = operationConversationId(operation);
|
|
2917
3191
|
const operationRunId = operation.type === "accept" ? operation.input.coalesceIntoRunId : operation.type === "compact" ? undefined : operation.input.runId;
|
|
@@ -2941,17 +3215,25 @@ function createAgentRuntimeStore(driver) {
|
|
|
2941
3215
|
throw new TypeError("Admission receipt points to a missing canonical run");
|
|
2942
3216
|
}
|
|
2943
3217
|
validateAdmissionReceipt(duplicateReceipt, duplicateRecord, conversationId);
|
|
3218
|
+
const answering = duplicateRecord.run.absorbedIntoRunId ? await driver.runs.load(transaction, {
|
|
3219
|
+
conversationId,
|
|
3220
|
+
runId: duplicateRecord.run.absorbedIntoRunId
|
|
3221
|
+
}) : undefined;
|
|
3222
|
+
if (duplicateRecord.run.absorbedIntoRunId && !answering) {
|
|
3223
|
+
throw new TypeError("Absorbed run points to a missing absorbing run");
|
|
3224
|
+
}
|
|
3225
|
+
const canonical = answering ?? duplicateRecord;
|
|
2944
3226
|
return {
|
|
2945
3227
|
outcome: "duplicate",
|
|
2946
3228
|
input: duplicateReceipt.input,
|
|
2947
3229
|
inputMessageId: duplicateReceipt.input.id,
|
|
2948
|
-
runId:
|
|
2949
|
-
assistantMessageId:
|
|
2950
|
-
run:
|
|
2951
|
-
...
|
|
2952
|
-
assistant:
|
|
3230
|
+
runId: canonical.run.id,
|
|
3231
|
+
assistantMessageId: canonical.run.assistantMessageId,
|
|
3232
|
+
run: canonical.run,
|
|
3233
|
+
...canonical.terminalAssistant && {
|
|
3234
|
+
assistant: canonical.terminalAssistant
|
|
2953
3235
|
},
|
|
2954
|
-
snapshot: snapshotOf(head, messages, mergeRunRecords(records, [duplicateRecord]))
|
|
3236
|
+
snapshot: snapshotOf(head, messages, mergeRunRecords(records, [duplicateRecord], answering ? [answering] : []))
|
|
2955
3237
|
};
|
|
2956
3238
|
}
|
|
2957
3239
|
if (operation.type === "accept") {
|
|
@@ -2993,18 +3275,21 @@ function createAgentRuntimeStore(driver) {
|
|
|
2993
3275
|
});
|
|
2994
3276
|
if (outcome.outcome === "conflict")
|
|
2995
3277
|
return conflict(outcome.actualVersion);
|
|
2996
|
-
|
|
2997
|
-
await driver.runs.save(transaction,
|
|
3278
|
+
for (const record of reduced.runRecords ?? []) {
|
|
3279
|
+
await driver.runs.save(transaction, record);
|
|
3280
|
+
}
|
|
2998
3281
|
if (reduced.admissionReceipt) {
|
|
2999
3282
|
await driver.admissions.create(transaction, reduced.admissionReceipt);
|
|
3000
3283
|
}
|
|
3001
|
-
|
|
3002
|
-
await driver.history.apply(transaction,
|
|
3284
|
+
for (const mutation of reduced.historyMutations ?? []) {
|
|
3285
|
+
await driver.history.apply(transaction, mutation);
|
|
3003
3286
|
}
|
|
3004
3287
|
return { outcome: "applied", snapshot: reduced.snapshot };
|
|
3005
3288
|
});
|
|
3006
3289
|
return {
|
|
3007
3290
|
loadSnapshot,
|
|
3291
|
+
loadRun,
|
|
3292
|
+
listActiveRuns,
|
|
3008
3293
|
acceptInputAndAssignRun: (input) => mutate({
|
|
3009
3294
|
type: "accept",
|
|
3010
3295
|
input: AcceptInputAndAssignRunSchema.parse(input)
|
|
@@ -3193,6 +3478,7 @@ function createMemoryAgentRuntimeStore() {
|
|
|
3193
3478
|
return createAgentRuntimeStore(driver);
|
|
3194
3479
|
}
|
|
3195
3480
|
export {
|
|
3481
|
+
ACTIVE_AGENT_RUN_STATES,
|
|
3196
3482
|
AcceptInputAndAssignRunSchema,
|
|
3197
3483
|
AcquireAgentRunSchema,
|
|
3198
3484
|
AgentAdmissionEventSchema,
|
|
@@ -3212,6 +3498,7 @@ export {
|
|
|
3212
3498
|
AgentModelDescriptorSchema,
|
|
3213
3499
|
AgentModelRegistrySnapshotSchema,
|
|
3214
3500
|
AgentOpaquePartSchema,
|
|
3501
|
+
AgentProvenanceSchema,
|
|
3215
3502
|
AgentProviderEnvelopeSchema,
|
|
3216
3503
|
AgentReasoningDeltaEventSchema,
|
|
3217
3504
|
AgentReasoningEndEventSchema,
|
|
@@ -3228,6 +3515,7 @@ export {
|
|
|
3228
3515
|
AgentRunStateEventSchema,
|
|
3229
3516
|
AgentRunStateSchema,
|
|
3230
3517
|
AgentRunTerminalEventSchema,
|
|
3518
|
+
AgentRunViewSchema,
|
|
3231
3519
|
AgentRuntimeConflictError,
|
|
3232
3520
|
AgentRuntimeEventCursorSchema,
|
|
3233
3521
|
AgentRuntimeEventSchema,
|