stitchkit 0.64.0 → 0.65.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 +6 -7
- package/dist/agent-runtime/coordinator.d.ts.map +1 -1
- package/dist/agent-runtime/events.d.ts +12 -18
- package/dist/agent-runtime/events.d.ts.map +1 -1
- package/dist/agent-runtime/history.d.ts +13 -0
- package/dist/agent-runtime/history.d.ts.map +1 -1
- package/dist/agent-runtime/observability.d.ts +2 -8
- package/dist/agent-runtime/observability.d.ts.map +1 -1
- package/dist/agent-runtime/prompt.d.ts +3 -1
- package/dist/agent-runtime/prompt.d.ts.map +1 -1
- package/dist/agent-runtime/run-execution.d.ts +0 -4
- package/dist/agent-runtime/run-execution.d.ts.map +1 -1
- package/dist/agent-runtime/runtime.d.ts +11 -1
- package/dist/agent-runtime/runtime.d.ts.map +1 -1
- package/dist/agent-runtime/schemas.d.ts +24 -10
- package/dist/agent-runtime/schemas.d.ts.map +1 -1
- package/dist/agent-runtime/store-driver.d.ts +15 -10
- package/dist/agent-runtime/store-driver.d.ts.map +1 -1
- package/dist/agent-runtime/store.d.ts +8 -43
- package/dist/agent-runtime/store.d.ts.map +1 -1
- package/dist/agent-runtime/terminal-commit.d.ts +2 -3
- package/dist/agent-runtime/terminal-commit.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 +77 -173
- package/dist/{index-b1k33127.js → index-sbkyvacf.js} +54 -6
- package/dist/testing/agent-store-conformance.d.ts.map +1 -1
- package/dist/testing.js +64 -3
- package/llms-full.txt +146 -47
- package/package.json +1 -1
package/dist/agent-runtime.js
CHANGED
|
@@ -28,8 +28,9 @@ import {
|
|
|
28
28
|
AgentToolCallPartSchema,
|
|
29
29
|
AgentToolResultPartSchema,
|
|
30
30
|
AgentUsageSchema,
|
|
31
|
-
AgentUsageValueSchema
|
|
32
|
-
|
|
31
|
+
AgentUsageValueSchema,
|
|
32
|
+
runStateForTerminalReason
|
|
33
|
+
} from "./index-sbkyvacf.js";
|
|
33
34
|
import"./index-6djpbnda.js";
|
|
34
35
|
import"./index-cby4ar3v.js";
|
|
35
36
|
import {
|
|
@@ -395,7 +396,7 @@ function advanceAgentRuntimeEventCursor(rawCursor, event) {
|
|
|
395
396
|
return { status: "duplicate", cursor };
|
|
396
397
|
}
|
|
397
398
|
return {
|
|
398
|
-
status:
|
|
399
|
+
status: "accepted",
|
|
399
400
|
cursor: {
|
|
400
401
|
...cursor,
|
|
401
402
|
snapshotVersion: event.snapshotVersion,
|
|
@@ -561,13 +562,7 @@ function interruptedSystemNote(message) {
|
|
|
561
562
|
return { rendered };
|
|
562
563
|
rendered.add("text");
|
|
563
564
|
rendered.add("control");
|
|
564
|
-
return {
|
|
565
|
-
message: modelMessageSchema.parse({
|
|
566
|
-
role: "system",
|
|
567
|
-
content: `[interrupted] partial response: ${text}`
|
|
568
|
-
}),
|
|
569
|
-
rendered
|
|
570
|
-
};
|
|
565
|
+
return { text: `[interrupted] partial response: ${text}`, rendered };
|
|
571
566
|
}
|
|
572
567
|
function completeToolChronology(message) {
|
|
573
568
|
const calls = new Set(message.parts.filter((part) => part.type === "tool-call").map((part) => part.callId));
|
|
@@ -576,12 +571,13 @@ function completeToolChronology(message) {
|
|
|
576
571
|
}
|
|
577
572
|
async function projectAgentHistoryDetailed(messages, options = {}) {
|
|
578
573
|
const projected = [];
|
|
574
|
+
const system = [];
|
|
579
575
|
const decisions = [];
|
|
580
576
|
const interruptedRule = options.interruptedAssistant ?? "assistant-marked";
|
|
581
577
|
let observedUser = false;
|
|
582
578
|
for (const message of messages) {
|
|
583
|
-
if (message.
|
|
584
|
-
decisions.push(decide(message, "omitted", "superseded"));
|
|
579
|
+
if (message.role === "assistant" && !isSpeakableAssistantStatus(message.status)) {
|
|
580
|
+
decisions.push(decide(message, "omitted", message.status === "superseded" ? "superseded" : "draft-or-failed"));
|
|
585
581
|
continue;
|
|
586
582
|
}
|
|
587
583
|
if (message.status === "streaming" || message.status === "failed") {
|
|
@@ -602,7 +598,7 @@ async function projectAgentHistoryDetailed(messages, options = {}) {
|
|
|
602
598
|
if (message.role === "system" || message.role === "summary") {
|
|
603
599
|
const content = textContent(message.parts);
|
|
604
600
|
if (content) {
|
|
605
|
-
|
|
601
|
+
system.push(content);
|
|
606
602
|
decisions.push(decide(message, "projected", "projected", new Set(["text"])));
|
|
607
603
|
} else {
|
|
608
604
|
decisions.push(decide(message, "omitted", "empty"));
|
|
@@ -623,8 +619,8 @@ async function projectAgentHistoryDetailed(messages, options = {}) {
|
|
|
623
619
|
}
|
|
624
620
|
if (interrupted && interruptedRule === "system-note") {
|
|
625
621
|
const note = interruptedSystemNote(message);
|
|
626
|
-
if (note.
|
|
627
|
-
|
|
622
|
+
if (note.text) {
|
|
623
|
+
system.push(note.text);
|
|
628
624
|
decisions.push(decide(message, "projected", "projected", note.rendered));
|
|
629
625
|
} else {
|
|
630
626
|
decisions.push(decide(message, "omitted", "empty"));
|
|
@@ -642,7 +638,7 @@ async function projectAgentHistoryDetailed(messages, options = {}) {
|
|
|
642
638
|
projected.push(...assistant.messages);
|
|
643
639
|
decisions.push(assistant.messages.length > 0 ? decide(message, "projected", "projected", assistant.rendered) : decide(message, "omitted", "empty"));
|
|
644
640
|
}
|
|
645
|
-
return { messages: projected, decisions };
|
|
641
|
+
return { messages: projected, decisions, system };
|
|
646
642
|
}
|
|
647
643
|
async function projectAgentHistory(messages, options = {}) {
|
|
648
644
|
return [...(await projectAgentHistoryDetailed(messages, options)).messages];
|
|
@@ -882,7 +878,7 @@ async function selectAgentHistory(options) {
|
|
|
882
878
|
if (!Number.isSafeInteger(keepRecentTurns) || keepRecentTurns < 0) {
|
|
883
879
|
throw new TypeError("keepRecentTurns must be a non-negative safe integer");
|
|
884
880
|
}
|
|
885
|
-
const spoken = options.messages.filter((message) => message.
|
|
881
|
+
const spoken = options.messages.filter((message) => message.role !== "assistant" || isSpeakableAssistantStatus(message.status));
|
|
886
882
|
const counts = new Map;
|
|
887
883
|
let total = 0;
|
|
888
884
|
let estimated = false;
|
|
@@ -895,8 +891,8 @@ async function selectAgentHistory(options) {
|
|
|
895
891
|
messages: [...spoken],
|
|
896
892
|
decisions: options.messages.map((candidate) => ({
|
|
897
893
|
messageId: candidate.id,
|
|
898
|
-
action: candidate.
|
|
899
|
-
reason: candidate.
|
|
894
|
+
action: candidate.role === "assistant" && !isSpeakableAssistantStatus(candidate.status) ? "removed" : "kept",
|
|
895
|
+
reason: candidate.role === "assistant" && !isSpeakableAssistantStatus(candidate.status) ? "unspeakable" : "token-count-unavailable",
|
|
900
896
|
tokens: counts.get(candidate.id) ?? { provenance: "unavailable" }
|
|
901
897
|
})),
|
|
902
898
|
totalTokens: { provenance: "unavailable" },
|
|
@@ -922,11 +918,11 @@ async function selectAgentHistory(options) {
|
|
|
922
918
|
}
|
|
923
919
|
const messages = spoken.filter((message) => !removed.has(message.id));
|
|
924
920
|
const decisions = options.messages.map((message) => {
|
|
925
|
-
if (message.
|
|
921
|
+
if (message.role === "assistant" && !isSpeakableAssistantStatus(message.status)) {
|
|
926
922
|
return {
|
|
927
923
|
messageId: message.id,
|
|
928
924
|
action: "removed",
|
|
929
|
-
reason: "
|
|
925
|
+
reason: "unspeakable",
|
|
930
926
|
tokens: { provenance: "unavailable" }
|
|
931
927
|
};
|
|
932
928
|
}
|
|
@@ -1126,6 +1122,7 @@ function interruptedCandidate(candidate, run, now) {
|
|
|
1126
1122
|
AgentMessagePartSchema.parse({ type: "control", reason: "run-interrupted" })
|
|
1127
1123
|
];
|
|
1128
1124
|
return {
|
|
1125
|
+
...candidate.usage && { usage: candidate.usage },
|
|
1129
1126
|
run,
|
|
1130
1127
|
assistant: AgentMessageSchema.parse({
|
|
1131
1128
|
...candidate.assistant,
|
|
@@ -1394,6 +1391,7 @@ function createRunExecutor(dependencies) {
|
|
|
1394
1391
|
let terminalReason = "success";
|
|
1395
1392
|
let usage = input.acceptedRun.usage;
|
|
1396
1393
|
let sawProviderFinish = false;
|
|
1394
|
+
let contextRefusal = false;
|
|
1397
1395
|
let step = 0;
|
|
1398
1396
|
let selectedModel;
|
|
1399
1397
|
let internalCause;
|
|
@@ -1510,56 +1508,38 @@ function createRunExecutor(dependencies) {
|
|
|
1510
1508
|
config.tools(runtimeContext)
|
|
1511
1509
|
]);
|
|
1512
1510
|
if (prompt.contextDecision === "oversized") {
|
|
1511
|
+
contextRefusal = true;
|
|
1513
1512
|
throw new Error("Agent context exceeds the configured model budget");
|
|
1514
1513
|
}
|
|
1515
1514
|
if (prompt.contextDecision === "requires-compaction") {
|
|
1515
|
+
contextRefusal = true;
|
|
1516
1516
|
throw new Error("Agent context still exceeds the model budget after compaction");
|
|
1517
1517
|
}
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
return;
|
|
1531
|
-
const latest = await config.store.loadSnapshot(run.conversationId);
|
|
1532
|
-
const current = latest.runs.find((candidate) => candidate.id === run.id);
|
|
1533
|
-
if (current?.state !== "running" || current.ownerId !== runtimeEpoch) {
|
|
1534
|
-
return;
|
|
1535
|
-
}
|
|
1536
|
-
const pending = latest.runs.find((candidate) => candidate.state === "queued" && input.absorbable?.has(candidate.id));
|
|
1537
|
-
if (!pending)
|
|
1538
|
-
return;
|
|
1539
|
-
const absorbed = await config.store.absorbQueuedRun({
|
|
1540
|
-
conversationId: run.conversationId,
|
|
1541
|
-
runningRunId: current.id,
|
|
1542
|
-
runningExpectedRevision: current.revision,
|
|
1543
|
-
ownerId: runtimeEpoch,
|
|
1544
|
-
...current.fencingToken !== undefined && { fencingToken: current.fencingToken },
|
|
1545
|
-
queuedRunId: pending.id,
|
|
1546
|
-
queuedExpectedRevision: pending.revision
|
|
1547
|
-
});
|
|
1548
|
-
if (absorbed.outcome !== "applied")
|
|
1549
|
-
return;
|
|
1550
|
-
input.onAbsorbed?.(pending.id);
|
|
1551
|
-
snapshot = absorbed.snapshot;
|
|
1552
|
-
run = findRun(snapshot.runs, run.id);
|
|
1553
|
-
await publish({
|
|
1554
|
-
type: "run-state",
|
|
1555
|
-
eventId: agentDurableEventId("run-state", run.id, snapshot.version),
|
|
1556
|
-
conversationId: run.conversationId,
|
|
1557
|
-
runId: run.id,
|
|
1558
|
-
snapshotVersion: snapshot.version,
|
|
1559
|
-
state: run.state,
|
|
1560
|
-
emittedAt: now().toISOString()
|
|
1518
|
+
let carriedSystem = [];
|
|
1519
|
+
const projectHistory = async (source) => {
|
|
1520
|
+
if (config.history?.project)
|
|
1521
|
+
return config.history.project(source.messages);
|
|
1522
|
+
const detailed = await projectAgentHistoryDetailed(source.messages, {
|
|
1523
|
+
...config.history?.resolveFile && { resolveFile: config.history.resolveFile },
|
|
1524
|
+
...config.history?.unresolvedFile && {
|
|
1525
|
+
unresolvedFile: config.history.unresolvedFile
|
|
1526
|
+
},
|
|
1527
|
+
...config.history?.interruptedAssistant && {
|
|
1528
|
+
interruptedAssistant: config.history.interruptedAssistant
|
|
1529
|
+
}
|
|
1561
1530
|
});
|
|
1562
|
-
|
|
1531
|
+
carriedSystem = detailed.system;
|
|
1532
|
+
return [...detailed.messages];
|
|
1533
|
+
};
|
|
1534
|
+
const history = await projectHistory(snapshot);
|
|
1535
|
+
const withCarriedSystem = (instructions) => {
|
|
1536
|
+
if (carriedSystem.length === 0)
|
|
1537
|
+
return instructions;
|
|
1538
|
+
const composed = typeof instructions === "string" ? [{ role: "system", content: instructions }] : Array.isArray(instructions) ? instructions : [instructions];
|
|
1539
|
+
return [
|
|
1540
|
+
...composed,
|
|
1541
|
+
...carriedSystem.map((content) => ({ role: "system", content }))
|
|
1542
|
+
];
|
|
1563
1543
|
};
|
|
1564
1544
|
const maxStepCondition = stepCountIs(maxSteps);
|
|
1565
1545
|
const stopConditions = [
|
|
@@ -1581,15 +1561,13 @@ function createRunExecutor(dependencies) {
|
|
|
1581
1561
|
const result = streamText({
|
|
1582
1562
|
model: selectedModel.model,
|
|
1583
1563
|
tools,
|
|
1584
|
-
instructions: prompt.instructions,
|
|
1564
|
+
instructions: withCarriedSystem(prompt.instructions),
|
|
1585
1565
|
messages: history,
|
|
1586
1566
|
abortSignal: executionSignal,
|
|
1587
1567
|
maxRetries: 0,
|
|
1588
1568
|
stopWhen: stopConditions,
|
|
1589
|
-
prepareStep
|
|
1590
|
-
|
|
1591
|
-
const prepared = await config.loop?.prepareStep?.({ ...options, ...runtimeContext }) ?? {};
|
|
1592
|
-
return absorbed && !prepared.messages ? { ...prepared, messages: absorbed } : prepared;
|
|
1569
|
+
...config.loop?.prepareStep && {
|
|
1570
|
+
prepareStep: (options) => config.loop?.prepareStep?.({ ...options, ...runtimeContext })
|
|
1593
1571
|
}
|
|
1594
1572
|
});
|
|
1595
1573
|
for await (const part of result.stream) {
|
|
@@ -1854,7 +1832,7 @@ function createRunExecutor(dependencies) {
|
|
|
1854
1832
|
reason: isToolExecutionControlError(error) && error.reason === "stale_run" ? "stale-run" : "run-interrupted"
|
|
1855
1833
|
}));
|
|
1856
1834
|
} else {
|
|
1857
|
-
terminalReason = "provider_failure";
|
|
1835
|
+
terminalReason = contextRefusal ? "context_overflow" : "provider_failure";
|
|
1858
1836
|
}
|
|
1859
1837
|
} finally {
|
|
1860
1838
|
idleDeadline.dispose();
|
|
@@ -1963,7 +1941,8 @@ function createAgentRuntime(config) {
|
|
|
1963
1941
|
const waitForAdmissionAcceptances = admissionLanes.waitForAcceptances;
|
|
1964
1942
|
const checkpointEveryEvents = config.loop?.checkpointEveryEvents ?? 20;
|
|
1965
1943
|
const maxSteps = config.loop?.maxSteps ?? 50;
|
|
1966
|
-
const
|
|
1944
|
+
const declaredIdleTimeoutMs = config.loop?.idleTimeoutMs;
|
|
1945
|
+
const idleTimeoutMs = declaredIdleTimeoutMs === null ? undefined : declaredIdleTimeoutMs ?? 60000;
|
|
1967
1946
|
if (!Number.isSafeInteger(checkpointEveryEvents) || checkpointEveryEvents < 1) {
|
|
1968
1947
|
throw new TypeError("checkpointEveryEvents must be a positive safe integer");
|
|
1969
1948
|
}
|
|
@@ -1973,9 +1952,6 @@ function createAgentRuntime(config) {
|
|
|
1973
1952
|
if (idleTimeoutMs !== undefined && (!Number.isSafeInteger(idleTimeoutMs) || idleTimeoutMs < 1)) {
|
|
1974
1953
|
throw new TypeError("idleTimeoutMs must be a positive safe integer");
|
|
1975
1954
|
}
|
|
1976
|
-
const absorbable = new Set;
|
|
1977
|
-
const absorbedInto = new Map;
|
|
1978
|
-
const runResults = new Map;
|
|
1979
1955
|
const policyNames = new Set(["max-steps"]);
|
|
1980
1956
|
for (const policy of config.loop?.stopPolicies ?? []) {
|
|
1981
1957
|
if (!policy.name || policyNames.has(policy.name)) {
|
|
@@ -2108,8 +2084,7 @@ function createAgentRuntime(config) {
|
|
|
2108
2084
|
if (existingTicket)
|
|
2109
2085
|
return existingTicket;
|
|
2110
2086
|
const key = config.runs?.key?.(input) ?? input.conversationId;
|
|
2111
|
-
const
|
|
2112
|
-
const policy = declaredPolicy === "inject" ? "queue" : declaredPolicy;
|
|
2087
|
+
const policy = typeof config.runs?.inputPolicy === "function" ? config.runs.inputPolicy(input) : config.runs?.inputPolicy ?? "queue";
|
|
2113
2088
|
const nowIso = now().toISOString();
|
|
2114
2089
|
const inputMessageId = rawInput.recordIds?.inputMessageId ?? generateId();
|
|
2115
2090
|
const runId = rawInput.recordIds?.runId ?? generateId();
|
|
@@ -2235,8 +2210,6 @@ function createAgentRuntime(config) {
|
|
|
2235
2210
|
state: acceptedRun.state,
|
|
2236
2211
|
emittedAt: now().toISOString()
|
|
2237
2212
|
});
|
|
2238
|
-
if (declaredPolicy === "inject")
|
|
2239
|
-
absorbable.add(acceptedRun.id);
|
|
2240
2213
|
outerAccepted.resolve();
|
|
2241
2214
|
if (acceptance.outcome === "duplicate") {
|
|
2242
2215
|
if (!acceptedRun.terminalReason) {
|
|
@@ -2285,25 +2258,7 @@ function createAgentRuntime(config) {
|
|
|
2285
2258
|
await waitForAdmissionAcceptances(reservation.lane);
|
|
2286
2259
|
return {
|
|
2287
2260
|
runId: acceptedRun.id,
|
|
2288
|
-
execute: () => {
|
|
2289
|
-
const answeredBy = absorbedInto.get(acceptedRun.id);
|
|
2290
|
-
const answer = answeredBy ? runResults.get(answeredBy) : undefined;
|
|
2291
|
-
if (answer)
|
|
2292
|
-
return answer;
|
|
2293
|
-
const running = executeRun({
|
|
2294
|
-
acceptedRun,
|
|
2295
|
-
context,
|
|
2296
|
-
signal,
|
|
2297
|
-
absorbable,
|
|
2298
|
-
onAbsorbed: (absorbedRunId) => {
|
|
2299
|
-
absorbedInto.set(absorbedRunId, acceptedRun.id);
|
|
2300
|
-
}
|
|
2301
|
-
});
|
|
2302
|
-
runResults.set(acceptedRun.id, running);
|
|
2303
|
-
return running.finally(() => {
|
|
2304
|
-
absorbable.delete(acceptedRun.id);
|
|
2305
|
-
});
|
|
2306
|
-
}
|
|
2261
|
+
execute: () => executeRun({ acceptedRun, context, signal })
|
|
2307
2262
|
};
|
|
2308
2263
|
}
|
|
2309
2264
|
});
|
|
@@ -2550,15 +2505,6 @@ var CommitRunTerminalSchema = z6.object({
|
|
|
2550
2505
|
policyName: z6.string().min(1).optional(),
|
|
2551
2506
|
usage: AgentUsageSchema.optional()
|
|
2552
2507
|
});
|
|
2553
|
-
var AbsorbQueuedRunSchema = z6.object({
|
|
2554
|
-
conversationId: AgentRecordIdSchema,
|
|
2555
|
-
runningRunId: AgentRecordIdSchema,
|
|
2556
|
-
runningExpectedRevision: AgentRecordVersionSchema,
|
|
2557
|
-
ownerId: z6.string().min(1),
|
|
2558
|
-
fencingToken: AgentRecordVersionSchema.optional(),
|
|
2559
|
-
queuedRunId: AgentRecordIdSchema,
|
|
2560
|
-
queuedExpectedRevision: AgentRecordVersionSchema
|
|
2561
|
-
});
|
|
2562
2508
|
var RequestRunInterruptSchema = z6.object({
|
|
2563
2509
|
conversationId: AgentRecordIdSchema,
|
|
2564
2510
|
runId: AgentRecordIdSchema,
|
|
@@ -2699,24 +2645,17 @@ function replaceRun(runs, next) {
|
|
|
2699
2645
|
function replaceMessage(messages, next) {
|
|
2700
2646
|
return messages.some((message) => message.id === next.id) ? messages.map((message) => message.id === next.id ? next : message) : [...messages, next];
|
|
2701
2647
|
}
|
|
2648
|
+
var ACTIVE_AGENT_RUN_STATES = [
|
|
2649
|
+
"queued",
|
|
2650
|
+
"running",
|
|
2651
|
+
"interrupt_requested"
|
|
2652
|
+
];
|
|
2653
|
+
function isActiveRunState(state) {
|
|
2654
|
+
return ACTIVE_AGENT_RUN_STATES.includes(state);
|
|
2655
|
+
}
|
|
2702
2656
|
function conflict(actualVersion) {
|
|
2703
2657
|
return { outcome: "conflict", actualVersion };
|
|
2704
2658
|
}
|
|
2705
|
-
function terminalState(reason) {
|
|
2706
|
-
if (reason === "success" || reason === "policy_stop" || reason === "provider_stop") {
|
|
2707
|
-
return "completed";
|
|
2708
|
-
}
|
|
2709
|
-
if (reason === "interrupted")
|
|
2710
|
-
return "interrupted";
|
|
2711
|
-
if (reason === "superseded")
|
|
2712
|
-
return "superseded";
|
|
2713
|
-
if (reason === "cancelled" || reason === "shutdown" || reason === "timeout") {
|
|
2714
|
-
return "cancelled";
|
|
2715
|
-
}
|
|
2716
|
-
if (reason === "abandoned")
|
|
2717
|
-
return "abandoned";
|
|
2718
|
-
return "failed";
|
|
2719
|
-
}
|
|
2720
2659
|
function applied(current, input, effects) {
|
|
2721
2660
|
return {
|
|
2722
2661
|
outcome: "applied",
|
|
@@ -2727,7 +2666,6 @@ function applied(current, input, effects) {
|
|
|
2727
2666
|
messages: input.messages ?? current.messages
|
|
2728
2667
|
}),
|
|
2729
2668
|
...effects?.runRecord && { runRecord: effects.runRecord },
|
|
2730
|
-
...effects?.secondaryRunRecord && { secondaryRunRecord: effects.secondaryRunRecord },
|
|
2731
2669
|
...effects?.admissionReceipt && { admissionReceipt: effects.admissionReceipt },
|
|
2732
2670
|
...effects?.historyMutation && { historyMutation: effects.historyMutation }
|
|
2733
2671
|
};
|
|
@@ -2771,39 +2709,6 @@ function reduceStore(current, operation) {
|
|
|
2771
2709
|
historyMutation: { type: "admit", input: input.input }
|
|
2772
2710
|
});
|
|
2773
2711
|
}
|
|
2774
|
-
if (operation.type === "absorb") {
|
|
2775
|
-
const input = operation.input;
|
|
2776
|
-
if (current.conversationId !== input.conversationId)
|
|
2777
|
-
return { outcome: "not_found" };
|
|
2778
|
-
const running = current.runs.find((candidate) => candidate.id === input.runningRunId);
|
|
2779
|
-
const queued = current.runs.find((candidate) => candidate.id === input.queuedRunId);
|
|
2780
|
-
if (!running || !queued)
|
|
2781
|
-
return { outcome: "not_found" };
|
|
2782
|
-
if (running.revision !== input.runningExpectedRevision || running.state !== "running" || running.ownerId !== input.ownerId || input.fencingToken !== undefined && running.fencingToken !== input.fencingToken) {
|
|
2783
|
-
return conflict(running.revision);
|
|
2784
|
-
}
|
|
2785
|
-
if (queued.revision !== input.queuedExpectedRevision || queued.state !== "queued" || queued.ownerId !== undefined || queued.terminalReason !== undefined || queued.id === running.id) {
|
|
2786
|
-
return conflict(queued.revision);
|
|
2787
|
-
}
|
|
2788
|
-
const stamp = new Date().toISOString();
|
|
2789
|
-
const grown = AgentRunSchema.parse({
|
|
2790
|
-
...running,
|
|
2791
|
-
inputMessageIds: [...running.inputMessageIds, ...queued.inputMessageIds],
|
|
2792
|
-
revision: running.revision + 1,
|
|
2793
|
-
updatedAt: stamp
|
|
2794
|
-
});
|
|
2795
|
-
const emptied = AgentRunSchema.parse({
|
|
2796
|
-
...queued,
|
|
2797
|
-
state: "absorbed",
|
|
2798
|
-
absorbedIntoRunId: running.id,
|
|
2799
|
-
revision: queued.revision + 1,
|
|
2800
|
-
updatedAt: stamp
|
|
2801
|
-
});
|
|
2802
|
-
return applied(current, { runs: replaceRun(replaceRun(current.runs, grown), emptied) }, {
|
|
2803
|
-
runRecord: AgentStoredRunSchema.parse({ schemaVersion: 1, run: grown }),
|
|
2804
|
-
secondaryRunRecord: AgentStoredRunSchema.parse({ schemaVersion: 1, run: emptied })
|
|
2805
|
-
});
|
|
2806
|
-
}
|
|
2807
2712
|
const conversationId = operation.input.conversationId;
|
|
2808
2713
|
const run = operation.type === "compact" ? undefined : current.runs.find((candidate) => candidate.id === operation.input.runId);
|
|
2809
2714
|
if (operation.type !== "compact" && !run)
|
|
@@ -2861,22 +2766,18 @@ function reduceStore(current, operation) {
|
|
|
2861
2766
|
}
|
|
2862
2767
|
if (operation.type === "recover" && run) {
|
|
2863
2768
|
const input = operation.input;
|
|
2864
|
-
if (run.revision !== input.expectedRevision || !
|
|
2769
|
+
if (run.revision !== input.expectedRevision || !isActiveRunState(run.state)) {
|
|
2865
2770
|
return conflict(run.revision);
|
|
2866
2771
|
}
|
|
2867
2772
|
if (input.action === "requeue" && run.state !== "queued" && input.replaySafe !== true) {
|
|
2868
2773
|
throw new TypeError("Recovering an acquired run requires explicit replaySafe evidence");
|
|
2869
2774
|
}
|
|
2775
|
+
const { ownerId: _released, ...carried } = run;
|
|
2870
2776
|
const next = AgentRunSchema.parse({
|
|
2871
|
-
|
|
2872
|
-
id: run.id,
|
|
2873
|
-
conversationId: run.conversationId,
|
|
2874
|
-
inputMessageIds: run.inputMessageIds,
|
|
2875
|
-
assistantMessageId: run.assistantMessageId,
|
|
2777
|
+
...carried,
|
|
2876
2778
|
state: input.action === "requeue" ? "queued" : "abandoned",
|
|
2877
2779
|
revision: run.revision + 1,
|
|
2878
2780
|
...input.action === "abandon" && { terminalReason: "abandoned" },
|
|
2879
|
-
createdAt: run.createdAt,
|
|
2880
2781
|
updatedAt: new Date().toISOString()
|
|
2881
2782
|
});
|
|
2882
2783
|
if (input.action === "abandon") {
|
|
@@ -2917,7 +2818,7 @@ function reduceStore(current, operation) {
|
|
|
2917
2818
|
}
|
|
2918
2819
|
const next = AgentRunSchema.parse({
|
|
2919
2820
|
...run,
|
|
2920
|
-
state:
|
|
2821
|
+
state: runStateForTerminalReason(input.reason),
|
|
2921
2822
|
terminalReason: input.reason,
|
|
2922
2823
|
...input.policyName && { terminalPolicyName: input.policyName },
|
|
2923
2824
|
...input.usage && { usage: input.usage },
|
|
@@ -2946,6 +2847,10 @@ function reduceStore(current, operation) {
|
|
|
2946
2847
|
if (!input.replacedMessageIds.every((id) => current.messages.some((m) => m.id === id))) {
|
|
2947
2848
|
return { outcome: "not_found" };
|
|
2948
2849
|
}
|
|
2850
|
+
const liveAssistant = current.runs.find((candidate) => isActiveRunState(candidate.state) && replaced.has(candidate.assistantMessageId));
|
|
2851
|
+
if (liveAssistant) {
|
|
2852
|
+
throw new TypeError(`Compaction may not replace the assistant message of run ${liveAssistant.id}, which has not finished`);
|
|
2853
|
+
}
|
|
2949
2854
|
const positions = current.messages.map((message, index) => replaced.has(message.id) ? index : undefined).filter((index) => index !== undefined);
|
|
2950
2855
|
const first = positions[0];
|
|
2951
2856
|
if (first === undefined || positions.some((position, offset) => position !== first + offset) || input.summary.conversationId !== input.conversationId || input.summary.runId !== undefined || input.summary.role !== "summary" || input.summary.status !== "committed" || current.messages.some((message) => message.id === input.summary.id) || current.runs.some((candidate) => candidate.assistantMessageId === input.summary.id)) {
|
|
@@ -3009,7 +2914,7 @@ function createAgentRuntimeStore(driver) {
|
|
|
3009
2914
|
});
|
|
3010
2915
|
const mutate = (operation) => driver.transaction(async (transaction) => {
|
|
3011
2916
|
const conversationId = operationConversationId(operation);
|
|
3012
|
-
const operationRunId = operation.type === "accept" ? operation.input.coalesceIntoRunId : operation.type === "compact" ? undefined : operation.
|
|
2917
|
+
const operationRunId = operation.type === "accept" ? operation.input.coalesceIntoRunId : operation.type === "compact" ? undefined : operation.input.runId;
|
|
3013
2918
|
const [stored, messages, activeRecords, operationRecord, duplicateReceipt] = await Promise.all([
|
|
3014
2919
|
driver.head.load(transaction, conversationId),
|
|
3015
2920
|
driver.history.load(transaction, conversationId),
|
|
@@ -3090,9 +2995,6 @@ function createAgentRuntimeStore(driver) {
|
|
|
3090
2995
|
return conflict(outcome.actualVersion);
|
|
3091
2996
|
if (reduced.runRecord)
|
|
3092
2997
|
await driver.runs.save(transaction, reduced.runRecord);
|
|
3093
|
-
if (reduced.secondaryRunRecord) {
|
|
3094
|
-
await driver.runs.save(transaction, reduced.secondaryRunRecord);
|
|
3095
|
-
}
|
|
3096
2998
|
if (reduced.admissionReceipt) {
|
|
3097
2999
|
await driver.admissions.create(transaction, reduced.admissionReceipt);
|
|
3098
3000
|
}
|
|
@@ -3116,7 +3018,6 @@ function createAgentRuntimeStore(driver) {
|
|
|
3116
3018
|
type: "interrupt",
|
|
3117
3019
|
input: RequestRunInterruptSchema.parse(input)
|
|
3118
3020
|
}),
|
|
3119
|
-
absorbQueuedRun: (input) => mutate({ type: "absorb", input: AbsorbQueuedRunSchema.parse(input) }),
|
|
3120
3021
|
recoverRun: (input) => mutate({ type: "recover", input: RecoverAgentRunSchema.parse(input) }),
|
|
3121
3022
|
commitRunTerminal: (input) => mutate({ type: "terminal", input: CommitRunTerminalSchema.parse(input) }),
|
|
3122
3023
|
replaceCompactedRange: (input) => mutate({
|
|
@@ -3212,7 +3113,7 @@ function createMemoryAgentRuntimeStore() {
|
|
|
3212
3113
|
});
|
|
3213
3114
|
},
|
|
3214
3115
|
async listActive(transaction, conversationId) {
|
|
3215
|
-
return [...transaction.runs.get(conversationId)?.values() ?? []].filter((record) =>
|
|
3116
|
+
return [...transaction.runs.get(conversationId)?.values() ?? []].filter((record) => isActiveRunState(record.run.state)).map((record) => AgentStoredRunSchema.parse(structuredClone(record)));
|
|
3216
3117
|
},
|
|
3217
3118
|
async save(transaction, rawRecord) {
|
|
3218
3119
|
const record = AgentStoredRunSchema.parse(structuredClone(rawRecord));
|
|
@@ -3274,9 +3175,12 @@ function createMemoryAgentRuntimeStore() {
|
|
|
3274
3175
|
}
|
|
3275
3176
|
},
|
|
3276
3177
|
async scanRecoverable(input) {
|
|
3277
|
-
const descriptors = [...runs].flatMap(([conversationId, conversationRuns]) => [...conversationRuns.values()].filter((record) =>
|
|
3178
|
+
const descriptors = [...runs].flatMap(([conversationId, conversationRuns]) => [...conversationRuns.values()].filter((record) => isActiveRunState(record.run.state)).map((record) => ({ conversationId, run: record.run }))).sort((left, right) => left.conversationId.localeCompare(right.conversationId) || left.run.id.localeCompare(right.run.id));
|
|
3278
3179
|
const cursorTuple = input.cursor ? parseRecoverableCursor(input.cursor) : undefined;
|
|
3279
|
-
const start = cursorTuple ? descriptors.findIndex((item) => item.conversationId === cursorTuple[0] && item.run.id
|
|
3180
|
+
const start = cursorTuple ? descriptors.findIndex((item) => item.conversationId.localeCompare(cursorTuple[0]) > 0 || item.conversationId === cursorTuple[0] && item.run.id.localeCompare(cursorTuple[1]) > 0) : 0;
|
|
3181
|
+
if (start === -1) {
|
|
3182
|
+
return AgentRecoverablePageSchema.parse({ items: [] });
|
|
3183
|
+
}
|
|
3280
3184
|
const items = descriptors.slice(start, start + input.limit);
|
|
3281
3185
|
const last = items.at(-1);
|
|
3282
3186
|
const hasMore = start + items.length < descriptors.length;
|
|
@@ -3289,7 +3193,6 @@ function createMemoryAgentRuntimeStore() {
|
|
|
3289
3193
|
return createAgentRuntimeStore(driver);
|
|
3290
3194
|
}
|
|
3291
3195
|
export {
|
|
3292
|
-
AbsorbQueuedRunSchema,
|
|
3293
3196
|
AcceptInputAndAssignRunSchema,
|
|
3294
3197
|
AcquireAgentRunSchema,
|
|
3295
3198
|
AgentAdmissionEventSchema,
|
|
@@ -3368,6 +3271,7 @@ export {
|
|
|
3368
3271
|
defineModelRegistry,
|
|
3369
3272
|
projectAgentHistory,
|
|
3370
3273
|
projectAgentHistoryDetailed,
|
|
3274
|
+
runStateForTerminalReason,
|
|
3371
3275
|
selectAgentHistory,
|
|
3372
3276
|
structuredCompaction,
|
|
3373
3277
|
validateAgentModelSnapshot
|
|
@@ -99,7 +99,6 @@ var AgentRunStateSchema = z.enum([
|
|
|
99
99
|
"completed",
|
|
100
100
|
"interrupted",
|
|
101
101
|
"superseded",
|
|
102
|
-
"absorbed",
|
|
103
102
|
"failed",
|
|
104
103
|
"cancelled",
|
|
105
104
|
"abandoned"
|
|
@@ -114,9 +113,24 @@ var AgentTerminalReasonSchema = z.enum([
|
|
|
114
113
|
"timeout",
|
|
115
114
|
"shutdown",
|
|
116
115
|
"provider_failure",
|
|
117
|
-
"
|
|
116
|
+
"context_overflow",
|
|
118
117
|
"abandoned"
|
|
119
118
|
]);
|
|
119
|
+
function runStateForTerminalReason(reason) {
|
|
120
|
+
if (reason === "success" || reason === "policy_stop" || reason === "provider_stop") {
|
|
121
|
+
return "completed";
|
|
122
|
+
}
|
|
123
|
+
if (reason === "interrupted")
|
|
124
|
+
return "interrupted";
|
|
125
|
+
if (reason === "superseded")
|
|
126
|
+
return "superseded";
|
|
127
|
+
if (reason === "cancelled" || reason === "shutdown" || reason === "timeout") {
|
|
128
|
+
return "cancelled";
|
|
129
|
+
}
|
|
130
|
+
if (reason === "abandoned")
|
|
131
|
+
return "abandoned";
|
|
132
|
+
return "failed";
|
|
133
|
+
}
|
|
120
134
|
var AgentUsageValueSchema = z.object({
|
|
121
135
|
value: z.number().nonnegative().optional(),
|
|
122
136
|
provenance: z.enum(["provider-reported", "computed", "estimated", "unavailable"])
|
|
@@ -134,7 +148,7 @@ var AgentUsageSchema = z.object({
|
|
|
134
148
|
cacheWriteTokens: AgentUsageValueSchema.optional(),
|
|
135
149
|
cost: AgentCostValueSchema.optional()
|
|
136
150
|
});
|
|
137
|
-
var
|
|
151
|
+
var AgentRunFieldsSchema = z.object({
|
|
138
152
|
schemaVersion: z.literal(1),
|
|
139
153
|
id: AgentRecordIdSchema,
|
|
140
154
|
conversationId: AgentRecordIdSchema,
|
|
@@ -146,11 +160,45 @@ var AgentRunSchema = z.object({
|
|
|
146
160
|
fencingToken: AgentRecordVersionSchema.optional(),
|
|
147
161
|
terminalReason: AgentTerminalReasonSchema.optional(),
|
|
148
162
|
terminalPolicyName: z.string().min(1).optional(),
|
|
149
|
-
absorbedIntoRunId: AgentRecordIdSchema.optional(),
|
|
150
163
|
usage: AgentUsageSchema.optional(),
|
|
151
164
|
createdAt: AgentTimestampSchema,
|
|
152
165
|
updatedAt: AgentTimestampSchema
|
|
153
166
|
});
|
|
167
|
+
var AgentRunSchema = AgentRunFieldsSchema.superRefine((run, ctx) => {
|
|
168
|
+
if (run.terminalReason === undefined) {
|
|
169
|
+
if (TERMINAL_RUN_STATES.has(run.state)) {
|
|
170
|
+
ctx.addIssue({
|
|
171
|
+
code: "custom",
|
|
172
|
+
path: ["terminalReason"],
|
|
173
|
+
message: `A run in terminal state "${run.state}" must say why it ended`
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
const expected = runStateForTerminalReason(run.terminalReason);
|
|
179
|
+
if (run.state !== expected) {
|
|
180
|
+
ctx.addIssue({
|
|
181
|
+
code: "custom",
|
|
182
|
+
path: ["state"],
|
|
183
|
+
message: `Terminal reason "${run.terminalReason}" ends a run in state "${expected}", not "${run.state}"`
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
if (run.terminalReason === "policy_stop" && run.terminalPolicyName === undefined) {
|
|
187
|
+
ctx.addIssue({
|
|
188
|
+
code: "custom",
|
|
189
|
+
path: ["terminalPolicyName"],
|
|
190
|
+
message: "A policy stop names the policy that stopped the run"
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
var TERMINAL_RUN_STATES = new Set([
|
|
195
|
+
"completed",
|
|
196
|
+
"interrupted",
|
|
197
|
+
"superseded",
|
|
198
|
+
"cancelled",
|
|
199
|
+
"abandoned",
|
|
200
|
+
"failed"
|
|
201
|
+
]);
|
|
154
202
|
var AgentSnapshotSchema = z.object({
|
|
155
203
|
schemaVersion: z.literal(1),
|
|
156
204
|
conversationId: AgentRecordIdSchema,
|
|
@@ -160,9 +208,9 @@ var AgentSnapshotSchema = z.object({
|
|
|
160
208
|
});
|
|
161
209
|
var AgentRunMetricsSchema = z.object({
|
|
162
210
|
partial: z.boolean(),
|
|
163
|
-
usage: AgentUsageSchema
|
|
211
|
+
usage: AgentUsageSchema,
|
|
164
212
|
durationMs: z.number().nonnegative().optional(),
|
|
165
213
|
ttftMs: z.number().nonnegative().optional()
|
|
166
214
|
});
|
|
167
215
|
|
|
168
|
-
export { AgentRecordIdSchema, AgentRecordVersionSchema, AgentTimestampSchema, AgentJsonObjectSchema, AgentProviderEnvelopeSchema, AgentTextPartSchema, AgentReasoningPartSchema, AgentFilePartSchema, AgentSourcePartSchema, AgentToolCallPartSchema, AgentToolResultPartSchema, AgentOpaquePartSchema, AgentControlPartSchema, AgentMessagePartSchema, AgentMessageRoleSchema, AgentMessageStatusSchema, AgentMessageSchema, AgentAssistantPlaceholderSchema, AgentRunStateSchema, AgentTerminalReasonSchema, AgentUsageValueSchema, AgentCostValueSchema, AgentUsageSchema, AgentRunSchema, AgentSnapshotSchema, AgentRunMetricsSchema };
|
|
216
|
+
export { AgentRecordIdSchema, AgentRecordVersionSchema, AgentTimestampSchema, AgentJsonObjectSchema, AgentProviderEnvelopeSchema, AgentTextPartSchema, AgentReasoningPartSchema, AgentFilePartSchema, AgentSourcePartSchema, AgentToolCallPartSchema, AgentToolResultPartSchema, AgentOpaquePartSchema, AgentControlPartSchema, AgentMessagePartSchema, AgentMessageRoleSchema, AgentMessageStatusSchema, AgentMessageSchema, AgentAssistantPlaceholderSchema, AgentRunStateSchema, AgentTerminalReasonSchema, runStateForTerminalReason, AgentUsageValueSchema, AgentCostValueSchema, AgentUsageSchema, AgentRunSchema, AgentSnapshotSchema, AgentRunMetricsSchema };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-store-conformance.d.ts","sourceRoot":"","sources":["../../src/testing/agent-store-conformance.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAEhE,MAAM,WAAW,2BAA2B;IAC1C,WAAW,IAAI,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;CAC/D;AAwCD,gFAAgF;AAChF,wBAAsB,wBAAwB,CAC5C,MAAM,EAAE,2BAA2B,GAClC,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"agent-store-conformance.d.ts","sourceRoot":"","sources":["../../src/testing/agent-store-conformance.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAEhE,MAAM,WAAW,2BAA2B;IAC1C,WAAW,IAAI,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;CAC/D;AAwCD,gFAAgF;AAChF,wBAAsB,wBAAwB,CAC5C,MAAM,EAAE,2BAA2B,GAClC,OAAO,CAAC,IAAI,CAAC,CAiXf"}
|