@rowan-agent/agent 0.6.0 → 0.6.1
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/README.md +132 -55
- package/dist/index.d.ts +209 -145
- package/dist/index.js +455 -175
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -521,10 +521,10 @@ ${indent}</${tag}>`;
|
|
|
521
521
|
return `${indent}<${tag}>${escapeXml(String(val))}</${tag}>`;
|
|
522
522
|
}).join("\n");
|
|
523
523
|
}
|
|
524
|
-
function buildPhaseDirectiveMessage(phase, output
|
|
524
|
+
function buildPhaseDirectiveMessage(phase, output) {
|
|
525
525
|
const parts = [];
|
|
526
|
-
parts.push(`<
|
|
527
|
-
parts.push(
|
|
526
|
+
parts.push(`<phase_content name="${escapeXml(phase.name)}">`);
|
|
527
|
+
parts.push(phase.content);
|
|
528
528
|
if (output.results && output.results.length > 0) {
|
|
529
529
|
parts.push(` <prev_phase_outputs>`);
|
|
530
530
|
if (output.instruction) {
|
|
@@ -539,13 +539,8 @@ function buildPhaseDirectiveMessage(phase, output, toolUseId) {
|
|
|
539
539
|
}
|
|
540
540
|
parts.push(` </prev_phase_outputs>`);
|
|
541
541
|
}
|
|
542
|
-
parts.push(`</
|
|
543
|
-
return
|
|
544
|
-
type: "tool_result",
|
|
545
|
-
toolUseId,
|
|
546
|
-
content: parts.join("\n"),
|
|
547
|
-
isError: false
|
|
548
|
-
}];
|
|
542
|
+
parts.push(`</phase_content>`);
|
|
543
|
+
return parts.join("\n");
|
|
549
544
|
}
|
|
550
545
|
|
|
551
546
|
// src/harness/phases/loader.ts
|
|
@@ -1366,11 +1361,17 @@ function compactMessages(messages, options = {}) {
|
|
|
1366
1361
|
);
|
|
1367
1362
|
const recentStart = Math.max(messages.length - keepRecent, firstUserIdx + 1);
|
|
1368
1363
|
const oldMessages = messages.slice(firstUserIdx >= 0 ? firstUserIdx + 1 : 0, recentStart);
|
|
1369
|
-
|
|
1364
|
+
const protectedPhaseMessages = oldMessages.filter(
|
|
1365
|
+
(message) => message.metadata?.kind === "phase_prompt"
|
|
1366
|
+
);
|
|
1367
|
+
const summarizableMessages = oldMessages.filter(
|
|
1368
|
+
(message) => message.metadata?.kind !== "phase_prompt"
|
|
1369
|
+
);
|
|
1370
|
+
if (summarizableMessages.length < minCompact) {
|
|
1370
1371
|
return { compacted: false, messages };
|
|
1371
1372
|
}
|
|
1372
1373
|
const recentMessages = messages.slice(recentStart);
|
|
1373
|
-
const summary = buildSummary(
|
|
1374
|
+
const summary = buildSummary(summarizableMessages);
|
|
1374
1375
|
const result = [];
|
|
1375
1376
|
if (firstUserIdx >= 0) {
|
|
1376
1377
|
result.push(messages[firstUserIdx]);
|
|
@@ -1380,14 +1381,15 @@ function compactMessages(messages, options = {}) {
|
|
|
1380
1381
|
|
|
1381
1382
|
${summary}`, {
|
|
1382
1383
|
type: "compaction_summary",
|
|
1383
|
-
compactedCount:
|
|
1384
|
+
compactedCount: summarizableMessages.length
|
|
1384
1385
|
})
|
|
1385
1386
|
);
|
|
1387
|
+
result.push(...protectedPhaseMessages);
|
|
1386
1388
|
result.push(...recentMessages);
|
|
1387
1389
|
return {
|
|
1388
1390
|
compacted: true,
|
|
1389
1391
|
messages: result,
|
|
1390
|
-
summarizedCount:
|
|
1392
|
+
summarizedCount: summarizableMessages.length,
|
|
1391
1393
|
summary
|
|
1392
1394
|
};
|
|
1393
1395
|
}
|
|
@@ -1417,17 +1419,20 @@ function applyFirstDecision(route, output) {
|
|
|
1417
1419
|
if (first.reason) output.routeReason = first.reason;
|
|
1418
1420
|
if (first.payload !== void 0) output.payload = normalizePayload(first.payload);
|
|
1419
1421
|
}
|
|
1420
|
-
function injectPhaseContent(phase, output,
|
|
1422
|
+
function injectPhaseContent(phase, output, messages, mirror) {
|
|
1421
1423
|
try {
|
|
1422
1424
|
const phaseContent = phase.filePath ? readPhaseContent(phase) : phase.content ?? phase.description ?? "";
|
|
1423
1425
|
const content = buildPhaseDirectiveMessage(
|
|
1424
1426
|
{ name: phase.id, content: phaseContent },
|
|
1425
|
-
output
|
|
1426
|
-
`phase_${phase.id}`
|
|
1427
|
+
output
|
|
1427
1428
|
);
|
|
1428
|
-
const
|
|
1429
|
-
|
|
1430
|
-
|
|
1429
|
+
const message = createMessage("user", content, {
|
|
1430
|
+
kind: "phase_prompt",
|
|
1431
|
+
phase: phase.id
|
|
1432
|
+
});
|
|
1433
|
+
messages.push(message);
|
|
1434
|
+
if (mirror && mirror !== messages) mirror.push(message);
|
|
1435
|
+
return message.id;
|
|
1431
1436
|
} catch {
|
|
1432
1437
|
return void 0;
|
|
1433
1438
|
}
|
|
@@ -1590,12 +1595,16 @@ async function startPhaseLoop(config, state) {
|
|
|
1590
1595
|
return runPhaseLoop(config, state, registry);
|
|
1591
1596
|
}
|
|
1592
1597
|
async function runPhaseLoop(config, state, registry) {
|
|
1593
|
-
|
|
1594
|
-
let
|
|
1595
|
-
let
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1598
|
+
const resumingSuspendedRun = state.status === "suspended" && Boolean(state.currentPhase);
|
|
1599
|
+
let currentPhaseId = resumingSuspendedRun ? state.currentPhase : registry.entryPhaseId;
|
|
1600
|
+
let isContinuing = resumingSuspendedRun ? state.continuation?.isContinuing ?? true : false;
|
|
1601
|
+
if (resumingSuspendedRun) {
|
|
1602
|
+
state.status = "running";
|
|
1603
|
+
}
|
|
1604
|
+
let previousPayload = resumingSuspendedRun ? state.continuation?.previousPayload : void 0;
|
|
1605
|
+
let previousPhaseMsgId = resumingSuspendedRun ? state.continuation?.previousPhaseMessageId : void 0;
|
|
1606
|
+
let previousResults = resumingSuspendedRun ? state.continuation?.previousResults?.map((result) => ({ ...result })) ?? [] : [];
|
|
1607
|
+
let pendingInstruction = resumingSuspendedRun ? state.continuation?.pendingInstruction : void 0;
|
|
1599
1608
|
while (currentPhaseId) {
|
|
1600
1609
|
const availablePhases = [];
|
|
1601
1610
|
for (const [, phase2] of registry.phases) {
|
|
@@ -1603,6 +1612,8 @@ async function runPhaseLoop(config, state, registry) {
|
|
|
1603
1612
|
}
|
|
1604
1613
|
const abortResult = LoopGuard.checkAbort(config.signal);
|
|
1605
1614
|
if (abortResult.stopReason !== "none") {
|
|
1615
|
+
removePhaseMessage(config.context.messages, previousPhaseMsgId);
|
|
1616
|
+
previousPhaseMsgId = void 0;
|
|
1606
1617
|
return completeRun(config, state, createOutcome.aborted());
|
|
1607
1618
|
}
|
|
1608
1619
|
state.metrics.iterations++;
|
|
@@ -1640,6 +1651,10 @@ async function runPhaseLoop(config, state, registry) {
|
|
|
1640
1651
|
messages: messageManager.visible(),
|
|
1641
1652
|
tools: phaseTools,
|
|
1642
1653
|
skills: phaseSkills,
|
|
1654
|
+
invocation: {
|
|
1655
|
+
mode: "serial",
|
|
1656
|
+
instanceId: currentPhaseId
|
|
1657
|
+
},
|
|
1643
1658
|
state: {
|
|
1644
1659
|
current: currentPhaseId,
|
|
1645
1660
|
available: Array.from(registry.phases.keys()),
|
|
@@ -1656,10 +1671,14 @@ async function runPhaseLoop(config, state, registry) {
|
|
|
1656
1671
|
const extBefore = await config.beforePhase(currentPhaseId, phaseContext);
|
|
1657
1672
|
if (extBefore.abort) {
|
|
1658
1673
|
config.emit?.({ type: "phase_end", phase: currentPhaseId, ts: createTimestamp() });
|
|
1674
|
+
removePhaseMessage(config.context.messages, previousPhaseMsgId);
|
|
1675
|
+
previousPhaseMsgId = void 0;
|
|
1659
1676
|
return completeRun(config, state, extBefore.abort);
|
|
1660
1677
|
}
|
|
1661
1678
|
if (extBefore.skip) {
|
|
1662
1679
|
config.emit?.({ type: "phase_end", phase: currentPhaseId, ts: createTimestamp() });
|
|
1680
|
+
removePhaseMessage(config.context.messages, previousPhaseMsgId);
|
|
1681
|
+
previousPhaseMsgId = void 0;
|
|
1663
1682
|
if (extBefore.skip.route === "stop") {
|
|
1664
1683
|
return completeRun(config, state, {
|
|
1665
1684
|
id: "skip",
|
|
@@ -1673,21 +1692,26 @@ async function runPhaseLoop(config, state, registry) {
|
|
|
1673
1692
|
phaseContext = extBefore.input;
|
|
1674
1693
|
}
|
|
1675
1694
|
}
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1695
|
+
const phaseMessageIsPresent = previousPhaseMsgId !== void 0 && config.context.messages.some((message) => message.id === previousPhaseMsgId);
|
|
1696
|
+
if (enteringNewPhase || !phaseMessageIsPresent) {
|
|
1697
|
+
removePhaseMessage(config.context.messages, previousPhaseMsgId);
|
|
1698
|
+
removePhaseMessage(phaseContext.messages, previousPhaseMsgId);
|
|
1699
|
+
previousPhaseMsgId = void 0;
|
|
1700
|
+
previousPhaseMsgId = injectPhaseContent(
|
|
1701
|
+
phase,
|
|
1702
|
+
{ results: previousResults, instruction: pendingInstruction },
|
|
1703
|
+
config.context.messages,
|
|
1704
|
+
phaseContext.messages
|
|
1705
|
+
);
|
|
1680
1706
|
previousResults = [];
|
|
1681
1707
|
pendingInstruction = void 0;
|
|
1682
1708
|
}
|
|
1683
1709
|
const runtime = { phase, config, state, execution, messageManager, registry, context: phaseContext };
|
|
1684
1710
|
let output = await executePhase(runtime);
|
|
1685
|
-
let routeToolCalled = false;
|
|
1686
1711
|
let routeDecision;
|
|
1687
1712
|
if (output.toolCalls && output.toolCalls.length > 0) {
|
|
1688
1713
|
routeDecision = extractRouteCall(output.toolCalls);
|
|
1689
1714
|
if (routeDecision) {
|
|
1690
|
-
routeToolCalled = true;
|
|
1691
1715
|
applyFirstDecision(routeDecision, output);
|
|
1692
1716
|
}
|
|
1693
1717
|
}
|
|
@@ -1695,6 +1719,8 @@ async function runPhaseLoop(config, state, registry) {
|
|
|
1695
1719
|
const extAfter = await config.afterPhase(currentPhaseId, output);
|
|
1696
1720
|
if (extAfter.abort) {
|
|
1697
1721
|
config.emit?.({ type: "phase_end", phase: currentPhaseId, ts: createTimestamp() });
|
|
1722
|
+
removePhaseMessage(config.context.messages, previousPhaseMsgId);
|
|
1723
|
+
previousPhaseMsgId = void 0;
|
|
1698
1724
|
return completeRun(config, state, extAfter.abort);
|
|
1699
1725
|
}
|
|
1700
1726
|
if (extAfter.retry && (phase.run || phase.factory)) {
|
|
@@ -1717,8 +1743,27 @@ async function runPhaseLoop(config, state, registry) {
|
|
|
1717
1743
|
previousPhaseMsgId = void 0;
|
|
1718
1744
|
return completeRun(config, state, createOutcome.aborted());
|
|
1719
1745
|
}
|
|
1720
|
-
|
|
1721
|
-
const
|
|
1746
|
+
const requestedAt = createTimestamp();
|
|
1747
|
+
const inputRequest = {
|
|
1748
|
+
phase: currentPhaseId,
|
|
1749
|
+
prompt: output.message,
|
|
1750
|
+
requestedAt
|
|
1751
|
+
};
|
|
1752
|
+
config.emit?.({
|
|
1753
|
+
type: "user_prompt_requested",
|
|
1754
|
+
phase: currentPhaseId,
|
|
1755
|
+
ts: requestedAt
|
|
1756
|
+
});
|
|
1757
|
+
state.status = "suspended";
|
|
1758
|
+
state.continuation = {
|
|
1759
|
+
isContinuing,
|
|
1760
|
+
previousPayload,
|
|
1761
|
+
previousResults: previousResults.map((result) => ({ ...result })),
|
|
1762
|
+
pendingInstruction,
|
|
1763
|
+
previousPhaseMessageId: previousPhaseMsgId
|
|
1764
|
+
};
|
|
1765
|
+
const userMessages = await config.waitForInput(state, inputRequest);
|
|
1766
|
+
state.status = "running";
|
|
1722
1767
|
const abortResult2 = LoopGuard.checkAbort(config.signal);
|
|
1723
1768
|
if (abortResult2.stopReason !== "none") {
|
|
1724
1769
|
config.emit?.({ type: "phase_end", phase: currentPhaseId, ts: createTimestamp() });
|
|
@@ -1729,13 +1774,12 @@ async function runPhaseLoop(config, state, registry) {
|
|
|
1729
1774
|
for (const message of userMessages) {
|
|
1730
1775
|
config.context.messages.push(message);
|
|
1731
1776
|
}
|
|
1732
|
-
removePhaseMessage(config.context.messages, previousPhaseMsgId);
|
|
1733
|
-
previousPhaseMsgId = void 0;
|
|
1734
1777
|
isContinuing = true;
|
|
1735
1778
|
continue;
|
|
1736
1779
|
}
|
|
1737
1780
|
config.emit?.({ type: "phase_end", phase: currentPhaseId, ts: createTimestamp() });
|
|
1738
1781
|
removePhaseMessage(config.context.messages, previousPhaseMsgId);
|
|
1782
|
+
previousPhaseMsgId = void 0;
|
|
1739
1783
|
return completeRun(config, state, createOutcome.default(output, config.context.messages));
|
|
1740
1784
|
}
|
|
1741
1785
|
if (output.route === "continue") {
|
|
@@ -1747,6 +1791,8 @@ async function runPhaseLoop(config, state, registry) {
|
|
|
1747
1791
|
const contextSnapshot = snapshotMessages(config.context.messages);
|
|
1748
1792
|
const parallelTasks = /* @__PURE__ */ new Map();
|
|
1749
1793
|
const instanceIds = buildInstanceIds(routeDecision.decision.map((t) => t.phase));
|
|
1794
|
+
const groupId = createId("phase-group");
|
|
1795
|
+
const count = routeDecision.decision.length;
|
|
1750
1796
|
for (let i = 0; i < routeDecision.decision.length; i++) {
|
|
1751
1797
|
const target = routeDecision.decision[i];
|
|
1752
1798
|
const pt = registry.phases.get(target.phase);
|
|
@@ -1754,7 +1800,20 @@ async function runPhaseLoop(config, state, registry) {
|
|
|
1754
1800
|
const instanceId = instanceIds[i];
|
|
1755
1801
|
const context = pt.isolated ? [] : contextSnapshot;
|
|
1756
1802
|
const payload = target.payload !== void 0 ? normalizePayload(target.payload) : void 0;
|
|
1757
|
-
const promise = executeParallelPhase(
|
|
1803
|
+
const promise = executeParallelPhase(
|
|
1804
|
+
config,
|
|
1805
|
+
state,
|
|
1806
|
+
registry,
|
|
1807
|
+
pt,
|
|
1808
|
+
payload,
|
|
1809
|
+
context,
|
|
1810
|
+
availablePhases,
|
|
1811
|
+
instanceId,
|
|
1812
|
+
groupId,
|
|
1813
|
+
i,
|
|
1814
|
+
count,
|
|
1815
|
+
currentPhaseId
|
|
1816
|
+
);
|
|
1758
1817
|
parallelTasks.set(instanceId, { promise, phaseId: target.phase });
|
|
1759
1818
|
}
|
|
1760
1819
|
const successfulResults = await waitForBackgroundTasks(parallelTasks);
|
|
@@ -1762,6 +1821,8 @@ async function runPhaseLoop(config, state, registry) {
|
|
|
1762
1821
|
pendingInstruction = routeDecision.instruction;
|
|
1763
1822
|
const entryPhaseId = phase.target ?? registry.entryPhaseId;
|
|
1764
1823
|
if (entryPhaseId === "stop") {
|
|
1824
|
+
removePhaseMessage(config.context.messages, previousPhaseMsgId);
|
|
1825
|
+
previousPhaseMsgId = void 0;
|
|
1765
1826
|
return completeRun(config, state, createTerminalOutcome(phase, output, config.context.messages));
|
|
1766
1827
|
}
|
|
1767
1828
|
currentPhaseId = entryPhaseId;
|
|
@@ -1777,12 +1838,14 @@ async function runPhaseLoop(config, state, registry) {
|
|
|
1777
1838
|
nextRoute = "stop";
|
|
1778
1839
|
}
|
|
1779
1840
|
if (nextRoute === "stop") {
|
|
1780
|
-
|
|
1841
|
+
removePhaseMessage(config.context.messages, previousPhaseMsgId);
|
|
1842
|
+
previousPhaseMsgId = void 0;
|
|
1781
1843
|
return completeRun(config, state, createTerminalOutcome(phase, output, config.context.messages));
|
|
1782
1844
|
}
|
|
1783
1845
|
const targetPhaseId = nextRoute;
|
|
1784
1846
|
if (!registry.phases.has(targetPhaseId)) {
|
|
1785
1847
|
removePhaseMessage(config.context.messages, previousPhaseMsgId);
|
|
1848
|
+
previousPhaseMsgId = void 0;
|
|
1786
1849
|
return completeRun(config, state, createOutcome.phaseNotFound(output));
|
|
1787
1850
|
}
|
|
1788
1851
|
state.metrics.phaseTransitions.push({
|
|
@@ -1999,17 +2062,25 @@ function createPhaseExecution(config, state, allTools, phase, messageManager, to
|
|
|
1999
2062
|
}
|
|
2000
2063
|
};
|
|
2001
2064
|
}
|
|
2002
|
-
async function executeParallelPhase(config, state, registry, phase, payload, context, availablePhases,
|
|
2065
|
+
async function executeParallelPhase(config, state, registry, phase, payload, context, availablePhases, instanceId, groupId, index, count, sourcePhaseId) {
|
|
2003
2066
|
const messages = [...context];
|
|
2004
2067
|
const allTools = buildToolsWithRouting(config, availablePhases);
|
|
2005
2068
|
const phaseTools = phase.tools ? allTools.filter((t) => t.name === PhaseRouteTool || phase.tools.includes(t.name)) : allTools;
|
|
2006
2069
|
const phaseSkills = phase.skills ? config.context.skills.filter((s) => phase.skills.includes(s.name)) : config.context.skills;
|
|
2007
|
-
const systemPrompt =
|
|
2070
|
+
const systemPrompt = config.context.systemPrompt;
|
|
2008
2071
|
const phaseContext = {
|
|
2009
2072
|
systemPrompt,
|
|
2010
2073
|
messages,
|
|
2011
2074
|
tools: phaseTools,
|
|
2012
2075
|
skills: phaseSkills,
|
|
2076
|
+
invocation: {
|
|
2077
|
+
mode: "parallel",
|
|
2078
|
+
instanceId,
|
|
2079
|
+
groupId,
|
|
2080
|
+
index,
|
|
2081
|
+
count,
|
|
2082
|
+
sourcePhaseId
|
|
2083
|
+
},
|
|
2013
2084
|
state: {
|
|
2014
2085
|
current: phase.id,
|
|
2015
2086
|
available: Array.from(registry.phases.keys()),
|
|
@@ -2018,10 +2089,10 @@ async function executeParallelPhase(config, state, registry, phase, payload, con
|
|
|
2018
2089
|
}
|
|
2019
2090
|
};
|
|
2020
2091
|
const messageManager = createMessageManager({ messages }, config.emit, config.onMessage);
|
|
2021
|
-
const phaseMsgId =
|
|
2092
|
+
const phaseMsgId = injectPhaseContent(
|
|
2022
2093
|
phase,
|
|
2023
|
-
{ results: payload !== void 0 ? [{ name:
|
|
2024
|
-
|
|
2094
|
+
{ results: payload !== void 0 ? [{ name: sourcePhaseId, output: payload }] : [] },
|
|
2095
|
+
messages
|
|
2025
2096
|
);
|
|
2026
2097
|
const toolExecutionManager = createToolExecutionManager(config.emit);
|
|
2027
2098
|
const execution = createPhaseExecution(config, state, phaseTools, phase, messageManager, toolExecutionManager, registry);
|
|
@@ -2030,7 +2101,7 @@ async function executeParallelPhase(config, state, registry, phase, payload, con
|
|
|
2030
2101
|
if (phaseMsgId) removePhaseMessage(messages, phaseMsgId);
|
|
2031
2102
|
const decision = output.toolCalls ? extractRouteCall(output.toolCalls) : void 0;
|
|
2032
2103
|
const resultPayload = decision?.decision[0]?.payload !== void 0 ? normalizePayload(decision.decision[0].payload) : output.payload;
|
|
2033
|
-
return { instanceId
|
|
2104
|
+
return { instanceId, phaseId: phase.id, payload: resultPayload, content: output.message };
|
|
2034
2105
|
}
|
|
2035
2106
|
async function waitForBackgroundTasks(backgroundTasks) {
|
|
2036
2107
|
const entries = Array.from(backgroundTasks.entries());
|
|
@@ -3224,19 +3295,29 @@ var AgentExecution = class {
|
|
|
3224
3295
|
runWithMessage(message, options, internalHooks = {}) {
|
|
3225
3296
|
const activeRun = this.activeRun;
|
|
3226
3297
|
if (activeRun?.resume) {
|
|
3298
|
+
if (internalHooks.onSuspend) activeRun.onSuspend = internalHooks.onSuspend;
|
|
3227
3299
|
activeRun.resume([message]);
|
|
3228
3300
|
return activeRun.promise;
|
|
3229
3301
|
}
|
|
3230
3302
|
if (activeRun) {
|
|
3231
3303
|
return Promise.reject(new Error("Agent is already running."));
|
|
3232
3304
|
}
|
|
3233
|
-
this.
|
|
3305
|
+
if (!this.options.context.messages.some((candidate) => candidate.id === message.id)) {
|
|
3306
|
+
this.appendMessage(message);
|
|
3307
|
+
}
|
|
3234
3308
|
return this.run(options, internalHooks);
|
|
3235
3309
|
}
|
|
3236
3310
|
/** Internal Runtime seam: executes one leased Agent Input. */
|
|
3237
|
-
async executeAgentInput(input, runId, control) {
|
|
3311
|
+
async executeAgentInput(input, runId, control, executionState) {
|
|
3238
3312
|
this.runtimeRunId = runId;
|
|
3239
|
-
|
|
3313
|
+
this.options.sessionState = executionState ? restoreSessionState(executionState) : void 0;
|
|
3314
|
+
const internal = {
|
|
3315
|
+
onSuspend: (reason, state, inputRequest) => control.suspend(
|
|
3316
|
+
reason,
|
|
3317
|
+
state ? snapshotExecutionState(state) : void 0,
|
|
3318
|
+
inputRequest
|
|
3319
|
+
)
|
|
3320
|
+
};
|
|
3240
3321
|
try {
|
|
3241
3322
|
return (await this.runWithMessage(input, void 0, internal)).outcome;
|
|
3242
3323
|
} finally {
|
|
@@ -3473,18 +3554,20 @@ var AgentExecution = class {
|
|
|
3473
3554
|
}
|
|
3474
3555
|
const abortController = new AbortController();
|
|
3475
3556
|
let suspensionRequested = false;
|
|
3476
|
-
const waitForInput = () => {
|
|
3477
|
-
|
|
3478
|
-
suspensionRequested = true;
|
|
3479
|
-
void hooks.onSuspend?.("Agent requested input.");
|
|
3480
|
-
}
|
|
3481
|
-
return new Promise((resolve7) => {
|
|
3557
|
+
const waitForInput = (state, inputRequest) => {
|
|
3558
|
+
const waiting = new Promise((resolve7) => {
|
|
3482
3559
|
const activeRun = this.activeRun;
|
|
3483
3560
|
activeRun.resume = (messages) => {
|
|
3484
3561
|
activeRun.resume = void 0;
|
|
3562
|
+
suspensionRequested = false;
|
|
3485
3563
|
resolve7(messages);
|
|
3486
3564
|
};
|
|
3487
3565
|
});
|
|
3566
|
+
if (!suspensionRequested) {
|
|
3567
|
+
suspensionRequested = true;
|
|
3568
|
+
void this.activeRun?.onSuspend?.("Agent requested input.", state, inputRequest);
|
|
3569
|
+
}
|
|
3570
|
+
return waiting;
|
|
3488
3571
|
};
|
|
3489
3572
|
const promise = Promise.resolve().then(() => executor({ signal: abortController.signal, waitForInput })).catch((error) => {
|
|
3490
3573
|
this.handleRunFailure(error, abortController.signal.aborted);
|
|
@@ -3492,7 +3575,7 @@ var AgentExecution = class {
|
|
|
3492
3575
|
}).finally(() => {
|
|
3493
3576
|
this.finishRun();
|
|
3494
3577
|
});
|
|
3495
|
-
this.activeRun = { promise, abortController };
|
|
3578
|
+
this.activeRun = { promise, abortController, onSuspend: hooks.onSuspend };
|
|
3496
3579
|
this.state.running = true;
|
|
3497
3580
|
return promise;
|
|
3498
3581
|
}
|
|
@@ -3525,7 +3608,8 @@ var AgentExecution = class {
|
|
|
3525
3608
|
const extensions = resolved.extensions ?? [];
|
|
3526
3609
|
await this.loadExtensions(extensions, resolved.cwd);
|
|
3527
3610
|
const runContext = prepareAgentContext(resolved.context, this.extensionRunner);
|
|
3528
|
-
const
|
|
3611
|
+
const resumingSuspendedRun = resolved.sessionState?.status === "suspended";
|
|
3612
|
+
const entryPhaseId = resumingSuspendedRun ? resolved.sessionState?.currentPhase || runContext.phases?.entryPhaseId || DEFAULT_PHASE_ID : this.state.initialized ? DEFAULT_PHASE_ID : runContext.phases?.entryPhaseId ?? DEFAULT_PHASE_ID;
|
|
3529
3613
|
const loopContext = {
|
|
3530
3614
|
...runContext,
|
|
3531
3615
|
phases: {
|
|
@@ -3570,6 +3654,7 @@ var AgentExecution = class {
|
|
|
3570
3654
|
model: resolved.model,
|
|
3571
3655
|
stream: resolved.stream,
|
|
3572
3656
|
maxAttempts: resolved.maxAttempts,
|
|
3657
|
+
sessionState: resolved.sessionState,
|
|
3573
3658
|
signal,
|
|
3574
3659
|
beforeToolCall,
|
|
3575
3660
|
afterToolCall,
|
|
@@ -3594,7 +3679,8 @@ var AgentExecution = class {
|
|
|
3594
3679
|
this.options = {
|
|
3595
3680
|
...resolved,
|
|
3596
3681
|
sessionId: result.sessionId,
|
|
3597
|
-
context: nextContext
|
|
3682
|
+
context: nextContext,
|
|
3683
|
+
sessionState: void 0
|
|
3598
3684
|
};
|
|
3599
3685
|
return result;
|
|
3600
3686
|
}, internalHooks);
|
|
@@ -3602,6 +3688,9 @@ var AgentExecution = class {
|
|
|
3602
3688
|
abort(reason = "Aborted by caller.") {
|
|
3603
3689
|
this.abortLocal(reason);
|
|
3604
3690
|
}
|
|
3691
|
+
isSuspended() {
|
|
3692
|
+
return Boolean(this.activeRun?.resume);
|
|
3693
|
+
}
|
|
3605
3694
|
abortLocal(reason) {
|
|
3606
3695
|
const activeRun = this.activeRun;
|
|
3607
3696
|
activeRun?.resume?.([]);
|
|
@@ -3676,6 +3765,50 @@ ${additionalInstructions}` : content;
|
|
|
3676
3765
|
};
|
|
3677
3766
|
}
|
|
3678
3767
|
};
|
|
3768
|
+
function restoreSessionState(state) {
|
|
3769
|
+
const persisted = parsePersistedExecutionState(state);
|
|
3770
|
+
return {
|
|
3771
|
+
...persisted,
|
|
3772
|
+
status: "suspended",
|
|
3773
|
+
metrics: {
|
|
3774
|
+
...persisted.metrics,
|
|
3775
|
+
phaseTransitions: persisted.metrics.phaseTransitions.map((transition) => ({ ...transition }))
|
|
3776
|
+
},
|
|
3777
|
+
...persisted.continuation ? {
|
|
3778
|
+
continuation: {
|
|
3779
|
+
...persisted.continuation,
|
|
3780
|
+
previousResults: persisted.continuation.previousResults.map((result) => ({ ...result }))
|
|
3781
|
+
}
|
|
3782
|
+
} : {}
|
|
3783
|
+
};
|
|
3784
|
+
}
|
|
3785
|
+
function snapshotExecutionState(state) {
|
|
3786
|
+
return {
|
|
3787
|
+
version: 1,
|
|
3788
|
+
currentPhase: state.currentPhase,
|
|
3789
|
+
attempt: state.attempt,
|
|
3790
|
+
metrics: {
|
|
3791
|
+
...state.metrics,
|
|
3792
|
+
phaseTransitions: state.metrics.phaseTransitions.map((transition) => ({ ...transition }))
|
|
3793
|
+
},
|
|
3794
|
+
...state.continuation ? {
|
|
3795
|
+
continuation: {
|
|
3796
|
+
...state.continuation,
|
|
3797
|
+
previousResults: state.continuation.previousResults.map((result) => ({ ...result }))
|
|
3798
|
+
}
|
|
3799
|
+
} : {}
|
|
3800
|
+
};
|
|
3801
|
+
}
|
|
3802
|
+
function parsePersistedExecutionState(state) {
|
|
3803
|
+
if (!state || state.version !== 1 || typeof state.currentPhase !== "string" || typeof state.attempt !== "number") {
|
|
3804
|
+
throw new Error("Invalid Agent Run execution checkpoint.");
|
|
3805
|
+
}
|
|
3806
|
+
const metrics = state.metrics;
|
|
3807
|
+
if (!metrics || !Array.isArray(metrics.phaseTransitions)) {
|
|
3808
|
+
throw new Error("Invalid Agent Run execution checkpoint metrics.");
|
|
3809
|
+
}
|
|
3810
|
+
return state;
|
|
3811
|
+
}
|
|
3679
3812
|
function clonePhaseRegistry(registry) {
|
|
3680
3813
|
return {
|
|
3681
3814
|
phases: new Map(registry.phases),
|
|
@@ -3755,6 +3888,8 @@ async function attachAgent(input) {
|
|
|
3755
3888
|
phases: input.options.context.phases
|
|
3756
3889
|
};
|
|
3757
3890
|
const persistInput = async (message) => {
|
|
3891
|
+
const context2 = await input.manager.buildAgentContext();
|
|
3892
|
+
if (context2.messages.some((candidate) => candidate.id === message.id)) return;
|
|
3758
3893
|
await input.manager.appendMessage(message);
|
|
3759
3894
|
};
|
|
3760
3895
|
let execution;
|
|
@@ -3813,7 +3948,9 @@ async function attachAgent(input) {
|
|
|
3813
3948
|
agent,
|
|
3814
3949
|
binding: {
|
|
3815
3950
|
abort: (reason) => execution.abortFromRuntime(reason),
|
|
3816
|
-
|
|
3951
|
+
isSuspended: () => execution.isSuspended(),
|
|
3952
|
+
persistInput,
|
|
3953
|
+
execute: (message, runId, control, executionState) => execution.executeAgentInput(message, runId, control, executionState)
|
|
3817
3954
|
}
|
|
3818
3955
|
};
|
|
3819
3956
|
}
|
|
@@ -4240,8 +4377,8 @@ var LocalJsonlSessionManager = class _LocalJsonlSessionManager {
|
|
|
4240
4377
|
}
|
|
4241
4378
|
};
|
|
4242
4379
|
|
|
4243
|
-
// src/harness/session/
|
|
4244
|
-
var
|
|
4380
|
+
// src/harness/session/store.ts
|
|
4381
|
+
var InMemorySessionStore = class {
|
|
4245
4382
|
sessions = /* @__PURE__ */ new Map();
|
|
4246
4383
|
async create(input) {
|
|
4247
4384
|
const session = InMemorySessionManager.create(input);
|
|
@@ -4252,7 +4389,7 @@ var InMemorySessionProvider = class {
|
|
|
4252
4389
|
return this.sessions.get(sessionId);
|
|
4253
4390
|
}
|
|
4254
4391
|
};
|
|
4255
|
-
var
|
|
4392
|
+
var JsonlSessionStore = class {
|
|
4256
4393
|
constructor(sessionsDir) {
|
|
4257
4394
|
this.sessionsDir = sessionsDir;
|
|
4258
4395
|
}
|
|
@@ -4268,6 +4405,9 @@ var LocalJsonlSessionProvider = class {
|
|
|
4268
4405
|
}
|
|
4269
4406
|
};
|
|
4270
4407
|
|
|
4408
|
+
// src/runtime/agent-runtime.ts
|
|
4409
|
+
import { createModelStream } from "@rowan-agent/models";
|
|
4410
|
+
|
|
4271
4411
|
// src/runtime/tool-runtime.ts
|
|
4272
4412
|
function failed(toolCall, message) {
|
|
4273
4413
|
return {
|
|
@@ -4469,6 +4609,9 @@ var AgentRun = class _AgentRun {
|
|
|
4469
4609
|
get state() {
|
|
4470
4610
|
return this.current.state;
|
|
4471
4611
|
}
|
|
4612
|
+
get inputRequest() {
|
|
4613
|
+
return this.current.inputRequest ? structuredClone(this.current.inputRequest) : void 0;
|
|
4614
|
+
}
|
|
4472
4615
|
async getStatus() {
|
|
4473
4616
|
const run = await this.host.getRun(this.id);
|
|
4474
4617
|
if (run) this.notify(run);
|
|
@@ -4533,8 +4676,8 @@ var AgentRuntime = class _AgentRuntime {
|
|
|
4533
4676
|
leaseDurationMs;
|
|
4534
4677
|
leaseRenewalIntervalMs;
|
|
4535
4678
|
toolRuntime;
|
|
4536
|
-
factories;
|
|
4537
4679
|
bindings = /* @__PURE__ */ new Map();
|
|
4680
|
+
agents = /* @__PURE__ */ new Map();
|
|
4538
4681
|
runHandles = /* @__PURE__ */ new Map();
|
|
4539
4682
|
runWaiters = /* @__PURE__ */ new Map();
|
|
4540
4683
|
pendingRuns = [];
|
|
@@ -4555,10 +4698,6 @@ var AgentRuntime = class _AgentRuntime {
|
|
|
4555
4698
|
this.leaseDurationMs = options.leaseDurationMs ?? 6e4;
|
|
4556
4699
|
this.leaseRenewalIntervalMs = options.leaseRenewalIntervalMs ?? Math.max(1, Math.floor(this.leaseDurationMs / 2));
|
|
4557
4700
|
this.toolRuntime = new ToolRuntime(options.stateStore, options.toolPolicy, () => this.publishEvents());
|
|
4558
|
-
this.factories = options.factories instanceof Map ? new Map([...options.factories.entries()].map(([id, factory]) => [String(id), factory])) : new Map(Object.entries(options.factories ?? {}));
|
|
4559
|
-
for (const id of this.factories.keys()) {
|
|
4560
|
-
if (id.trim().length === 0) throw new Error("Factory ID must not be empty.");
|
|
4561
|
-
}
|
|
4562
4701
|
}
|
|
4563
4702
|
static async start(options) {
|
|
4564
4703
|
if (activeRuntime) {
|
|
@@ -4593,20 +4732,16 @@ var AgentRuntime = class _AgentRuntime {
|
|
|
4593
4732
|
}
|
|
4594
4733
|
async createAgent(options) {
|
|
4595
4734
|
this.assertRunning();
|
|
4596
|
-
|
|
4597
|
-
throw new Error("Factory ID must not be empty.");
|
|
4598
|
-
}
|
|
4735
|
+
assertAgentOptions(options);
|
|
4599
4736
|
const provider = this.sessionProvider;
|
|
4600
4737
|
if (!provider) throw new Error("Agent Runtime requires a SessionManager provider to create an Agent.");
|
|
4601
4738
|
const manager = await provider.create({
|
|
4602
4739
|
systemPrompt: options.context.systemPrompt,
|
|
4603
|
-
input:
|
|
4740
|
+
input: "",
|
|
4604
4741
|
skills: options.context.skills
|
|
4605
4742
|
});
|
|
4606
|
-
if (options.input) await manager.appendMessage(createMessage("user", options.input));
|
|
4607
4743
|
const record = await this.stateStore.createAgent({
|
|
4608
|
-
sessionId: manager.getSessionId()
|
|
4609
|
-
...options.factoryId ? { factoryId: options.factoryId } : {}
|
|
4744
|
+
sessionId: manager.getSessionId()
|
|
4610
4745
|
});
|
|
4611
4746
|
this.pausedAgents.delete(record.id);
|
|
4612
4747
|
const agent = await this.attachAgent(record, manager, options);
|
|
@@ -4615,6 +4750,7 @@ var AgentRuntime = class _AgentRuntime {
|
|
|
4615
4750
|
}
|
|
4616
4751
|
async reconstructAgent(agentId, options) {
|
|
4617
4752
|
this.assertRunning();
|
|
4753
|
+
assertAgentOptions(options);
|
|
4618
4754
|
const record = await this.stateStore.getAgent(agentId);
|
|
4619
4755
|
if (!record) throw new Error(`Agent not found: ${agentId}.`);
|
|
4620
4756
|
if (record.state === "paused") this.pausedAgents.add(record.id);
|
|
@@ -4630,10 +4766,19 @@ var AgentRuntime = class _AgentRuntime {
|
|
|
4630
4766
|
async stop() {
|
|
4631
4767
|
if (this.stopped) return;
|
|
4632
4768
|
this.stopped = true;
|
|
4633
|
-
|
|
4634
|
-
|
|
4769
|
+
let suspendedAgents;
|
|
4770
|
+
try {
|
|
4771
|
+
suspendedAgents = new Set(
|
|
4772
|
+
(await this.stateStore.listRuns({ states: ["suspended"] })).map((run) => run.agentId)
|
|
4773
|
+
);
|
|
4774
|
+
} catch {
|
|
4775
|
+
suspendedAgents = /* @__PURE__ */ new Set();
|
|
4776
|
+
}
|
|
4777
|
+
for (const [agentId, binding] of this.bindings) {
|
|
4778
|
+
if (!suspendedAgents.has(agentId) && !binding.isSuspended()) binding.abort("Agent Runtime stopped.");
|
|
4635
4779
|
}
|
|
4636
4780
|
this.bindings.clear();
|
|
4781
|
+
this.agents.clear();
|
|
4637
4782
|
this.pendingRuns.length = 0;
|
|
4638
4783
|
this.scheduledRunIds.clear();
|
|
4639
4784
|
this.pausedAgents.clear();
|
|
@@ -4653,6 +4798,7 @@ var AgentRuntime = class _AgentRuntime {
|
|
|
4653
4798
|
throw new Error(`Agent ${agentId} is already bound to a live Agent.`);
|
|
4654
4799
|
}
|
|
4655
4800
|
this.bindings.set(agentId, agent);
|
|
4801
|
+
void this.pump();
|
|
4656
4802
|
if (!this.pausedAgents.has(agentId)) void this.scheduleQueuedRuns(agentId);
|
|
4657
4803
|
}
|
|
4658
4804
|
registerRunHandle(runId, handle) {
|
|
@@ -4702,6 +4848,24 @@ var AgentRuntime = class _AgentRuntime {
|
|
|
4702
4848
|
void this.pump();
|
|
4703
4849
|
this.publishEvents();
|
|
4704
4850
|
}
|
|
4851
|
+
async getRun(runId) {
|
|
4852
|
+
return this.stateStore.getRun(runId);
|
|
4853
|
+
}
|
|
4854
|
+
async listActiveRuns() {
|
|
4855
|
+
return this.stateStore.listActiveRuns();
|
|
4856
|
+
}
|
|
4857
|
+
async listRuns(input) {
|
|
4858
|
+
return this.stateStore.listRuns(input);
|
|
4859
|
+
}
|
|
4860
|
+
getAgent(agentId) {
|
|
4861
|
+
return this.agents.get(agentId);
|
|
4862
|
+
}
|
|
4863
|
+
async getMessage(messageId) {
|
|
4864
|
+
return this.stateStore.getMessage(messageId);
|
|
4865
|
+
}
|
|
4866
|
+
async getToolCall(toolCallId) {
|
|
4867
|
+
return this.stateStore.getToolCall(toolCallId);
|
|
4868
|
+
}
|
|
4705
4869
|
async abortRun(runId, reason = "Agent Run aborted by caller.") {
|
|
4706
4870
|
const run = await this.stateStore.getRun(runId);
|
|
4707
4871
|
if (!run || ["completed", "failed", "cancelled"].includes(run.state)) return;
|
|
@@ -4722,6 +4886,11 @@ var AgentRuntime = class _AgentRuntime {
|
|
|
4722
4886
|
this.publishEvents();
|
|
4723
4887
|
}
|
|
4724
4888
|
consumeEvents(consumerId, listener) {
|
|
4889
|
+
const subscription = this.registerEventConsumer(consumerId, listener);
|
|
4890
|
+
void subscription.caughtUp.catch(() => void 0);
|
|
4891
|
+
return subscription;
|
|
4892
|
+
}
|
|
4893
|
+
registerEventConsumer(consumerId, listener) {
|
|
4725
4894
|
this.assertRunning();
|
|
4726
4895
|
if (consumerId.trim().length === 0) {
|
|
4727
4896
|
throw new Error("Runtime Event Consumer ID must not be empty.");
|
|
@@ -4736,12 +4905,39 @@ var AgentRuntime = class _AgentRuntime {
|
|
|
4736
4905
|
redeliver: false
|
|
4737
4906
|
};
|
|
4738
4907
|
this.eventSubscriptions.set(consumerId, subscription);
|
|
4739
|
-
|
|
4740
|
-
|
|
4908
|
+
let caughtUpSettled = false;
|
|
4909
|
+
let resolveCaughtUp;
|
|
4910
|
+
let rejectCaughtUp;
|
|
4911
|
+
const caughtUp = new Promise((resolve7, reject) => {
|
|
4912
|
+
resolveCaughtUp = resolve7;
|
|
4913
|
+
rejectCaughtUp = reject;
|
|
4914
|
+
});
|
|
4915
|
+
const stop = () => {
|
|
4741
4916
|
if (this.eventSubscriptions.get(consumerId) === subscription) {
|
|
4742
4917
|
this.eventSubscriptions.delete(consumerId);
|
|
4743
4918
|
}
|
|
4919
|
+
if (!caughtUpSettled) {
|
|
4920
|
+
caughtUpSettled = true;
|
|
4921
|
+
const error = new Error("Runtime Event Consumer stopped before catch-up completed.");
|
|
4922
|
+
error.name = "AbortError";
|
|
4923
|
+
rejectCaughtUp(error);
|
|
4924
|
+
}
|
|
4744
4925
|
};
|
|
4926
|
+
void this.deliverEvents(subscription).then(
|
|
4927
|
+
() => {
|
|
4928
|
+
if (!caughtUpSettled) {
|
|
4929
|
+
caughtUpSettled = true;
|
|
4930
|
+
resolveCaughtUp();
|
|
4931
|
+
}
|
|
4932
|
+
},
|
|
4933
|
+
(error) => {
|
|
4934
|
+
if (!caughtUpSettled) {
|
|
4935
|
+
caughtUpSettled = true;
|
|
4936
|
+
rejectCaughtUp(error);
|
|
4937
|
+
}
|
|
4938
|
+
}
|
|
4939
|
+
);
|
|
4940
|
+
return { caughtUp, stop };
|
|
4745
4941
|
}
|
|
4746
4942
|
async listEvents(cursor) {
|
|
4747
4943
|
return this.stateStore.listEvents(cursor);
|
|
@@ -4752,32 +4948,6 @@ var AgentRuntime = class _AgentRuntime {
|
|
|
4752
4948
|
for (const record of agents) {
|
|
4753
4949
|
if (record.state === "paused") this.pausedAgents.add(record.id);
|
|
4754
4950
|
}
|
|
4755
|
-
for (const record of agents) {
|
|
4756
|
-
if (record.state !== "active" || !record.factoryId) continue;
|
|
4757
|
-
const factory = this.factories.get(String(record.factoryId));
|
|
4758
|
-
if (!factory) {
|
|
4759
|
-
await this.stateStore.recordEvent({ kind: "factory_missing", related: { agentId: record.id }, payload: { factoryId: record.factoryId } });
|
|
4760
|
-
continue;
|
|
4761
|
-
}
|
|
4762
|
-
try {
|
|
4763
|
-
const options = await factory({ agentId: record.id, sessionId: record.sessionId, factoryId: record.factoryId });
|
|
4764
|
-
if (!options) {
|
|
4765
|
-
await this.stateStore.recordEvent({
|
|
4766
|
-
kind: "agent_recovery_failed",
|
|
4767
|
-
related: { agentId: record.id },
|
|
4768
|
-
payload: { factoryId: record.factoryId, error: "Factory returned no reconstruction options." }
|
|
4769
|
-
});
|
|
4770
|
-
continue;
|
|
4771
|
-
}
|
|
4772
|
-
await this.reconstructAgent(record.id, options);
|
|
4773
|
-
await this.stateStore.recordEvent({ kind: "agent_recovered", related: { agentId: record.id }, payload: { factoryId: record.factoryId } });
|
|
4774
|
-
} catch (error) {
|
|
4775
|
-
await this.stateStore.recordEvent({ kind: "agent_recovery_failed", related: { agentId: record.id }, payload: { factoryId: record.factoryId, error: error instanceof Error ? error.message : String(error) } });
|
|
4776
|
-
}
|
|
4777
|
-
}
|
|
4778
|
-
for (const record of await this.stateStore.listAgents()) {
|
|
4779
|
-
if (this.bindings.has(record.id)) await this.scheduleQueuedRuns(record.id);
|
|
4780
|
-
}
|
|
4781
4951
|
this.publishEvents();
|
|
4782
4952
|
}
|
|
4783
4953
|
async scheduleQueuedRuns(agentId) {
|
|
@@ -4786,8 +4956,14 @@ var AgentRuntime = class _AgentRuntime {
|
|
|
4786
4956
|
for (const run of runs) this.scheduleRun(agentId, run.id);
|
|
4787
4957
|
}
|
|
4788
4958
|
async attachAgent(record, manager, options) {
|
|
4959
|
+
const model = { provider: options.model.provider, id: options.model.id };
|
|
4960
|
+
const resolvedOptions = {
|
|
4961
|
+
...options,
|
|
4962
|
+
model,
|
|
4963
|
+
stream: options.stream ?? createModelStream(options.model)
|
|
4964
|
+
};
|
|
4789
4965
|
const attached = await attachAgent({
|
|
4790
|
-
options,
|
|
4966
|
+
options: resolvedOptions,
|
|
4791
4967
|
agentId: record.id,
|
|
4792
4968
|
sessionId: record.sessionId,
|
|
4793
4969
|
manager,
|
|
@@ -4795,6 +4971,7 @@ var AgentRuntime = class _AgentRuntime {
|
|
|
4795
4971
|
executeTool: (input) => this.toolRuntime.execute(input)
|
|
4796
4972
|
});
|
|
4797
4973
|
this.bindAgent(record.id, attached.binding);
|
|
4974
|
+
this.agents.set(record.id, attached.agent);
|
|
4798
4975
|
return attached.agent;
|
|
4799
4976
|
}
|
|
4800
4977
|
async submitAgentInput(agentId, input, persist) {
|
|
@@ -4802,6 +4979,7 @@ var AgentRuntime = class _AgentRuntime {
|
|
|
4802
4979
|
if (!this.bindings.has(agentId)) throw new Error(`Agent ${agentId} has no live Agent Binding.`);
|
|
4803
4980
|
const enqueued = await this.stateStore.enqueueAgentInput({ agentId, input });
|
|
4804
4981
|
await persist();
|
|
4982
|
+
if (enqueued.resumed) this.notifyRun(enqueued.run);
|
|
4805
4983
|
const run = createAgentRun({
|
|
4806
4984
|
register: (handle) => this.registerRunHandle(enqueued.run.id, handle),
|
|
4807
4985
|
getRun: (runId) => this.stateStore.getRun(runId),
|
|
@@ -4849,10 +5027,10 @@ var AgentRuntime = class _AgentRuntime {
|
|
|
4849
5027
|
suspendedResolve = resolve7;
|
|
4850
5028
|
});
|
|
4851
5029
|
const control = {
|
|
4852
|
-
suspend: async (reason) => {
|
|
5030
|
+
suspend: async (reason, executionState, inputRequest) => {
|
|
4853
5031
|
if (suspended) return;
|
|
4854
5032
|
suspended = true;
|
|
4855
|
-
const current = await this.stateStore.suspendRun({ runId: job.runId, reason });
|
|
5033
|
+
const current = await this.stateStore.suspendRun({ runId: job.runId, reason, executionState, inputRequest });
|
|
4856
5034
|
this.notifyRun(current);
|
|
4857
5035
|
this.publishEvents();
|
|
4858
5036
|
suspendedResolve?.();
|
|
@@ -4871,6 +5049,11 @@ var AgentRuntime = class _AgentRuntime {
|
|
|
4871
5049
|
leaseDurationMs: this.leaseDurationMs
|
|
4872
5050
|
});
|
|
4873
5051
|
this.notifyRun(leased.run);
|
|
5052
|
+
try {
|
|
5053
|
+
await binding.persistInput(leased.message.input);
|
|
5054
|
+
} catch (error) {
|
|
5055
|
+
throw new InfrastructureFailureError("Agent Input persistence failed.", { cause: error });
|
|
5056
|
+
}
|
|
4874
5057
|
renewalTimer = setInterval(() => {
|
|
4875
5058
|
void this.stateStore.renewLease({
|
|
4876
5059
|
runId: job.runId,
|
|
@@ -4884,7 +5067,7 @@ var AgentRuntime = class _AgentRuntime {
|
|
|
4884
5067
|
failRenewal(failure);
|
|
4885
5068
|
});
|
|
4886
5069
|
}, this.leaseRenewalIntervalMs);
|
|
4887
|
-
const execution = binding.execute(leased.message.input, job.runId, control);
|
|
5070
|
+
const execution = binding.execute(leased.message.input, job.runId, control, leased.run.executionState);
|
|
4888
5071
|
const outcome = await Promise.race([
|
|
4889
5072
|
execution.then((value) => ({ kind: "terminal", value })).catch((error) => ({ kind: "error", error })),
|
|
4890
5073
|
suspendedSignal.then(() => ({ kind: "suspended" })),
|
|
@@ -4976,8 +5159,21 @@ var AgentRuntime = class _AgentRuntime {
|
|
|
4976
5159
|
);
|
|
4977
5160
|
for (const event of events) {
|
|
4978
5161
|
if (this.eventSubscriptions.get(subscription.consumerId) !== subscription) return;
|
|
4979
|
-
await subscription.listener(structuredClone(event));
|
|
4980
|
-
|
|
5162
|
+
const disposition = await subscription.listener(structuredClone(event));
|
|
5163
|
+
if (disposition?.type === "enqueue") {
|
|
5164
|
+
const delivered = await this.stateStore.acknowledgeEventAndEnqueueAgentInput({
|
|
5165
|
+
consumerId: subscription.consumerId,
|
|
5166
|
+
eventId: event.id,
|
|
5167
|
+
agentId: disposition.agentId,
|
|
5168
|
+
input: disposition.input
|
|
5169
|
+
});
|
|
5170
|
+
if (delivered.enqueued) {
|
|
5171
|
+
this.scheduleRun(delivered.enqueued.run.agentId, delivered.enqueued.run.id);
|
|
5172
|
+
this.publishEvents();
|
|
5173
|
+
}
|
|
5174
|
+
} else {
|
|
5175
|
+
await this.stateStore.acknowledgeEvent(subscription.consumerId, event.id);
|
|
5176
|
+
}
|
|
4981
5177
|
}
|
|
4982
5178
|
} while (subscription.redeliver);
|
|
4983
5179
|
} finally {
|
|
@@ -4988,11 +5184,25 @@ var AgentRuntime = class _AgentRuntime {
|
|
|
4988
5184
|
if (this.stopped || activeRuntime !== this) throw new Error("Agent Runtime is stopped.");
|
|
4989
5185
|
}
|
|
4990
5186
|
};
|
|
5187
|
+
function isModelConfig(value) {
|
|
5188
|
+
if (typeof value !== "object" || value === null) return false;
|
|
5189
|
+
return "protocol" in value && "baseUrl" in value && "apiKey" in value;
|
|
5190
|
+
}
|
|
5191
|
+
function assertAgentOptions(options) {
|
|
5192
|
+
if (options.stream && isModelConfig(options.model)) {
|
|
5193
|
+
throw new Error("AgentOptions accepts either a complete model config or a custom stream, not both.");
|
|
5194
|
+
}
|
|
5195
|
+
}
|
|
4991
5196
|
function isRetryableInfrastructureError(error) {
|
|
4992
5197
|
if (error instanceof InfrastructureFailureError) return true;
|
|
4993
5198
|
return typeof error === "object" && error !== null && "retryable" in error && error.retryable === true;
|
|
4994
5199
|
}
|
|
4995
5200
|
|
|
5201
|
+
// src/runtime/domain.ts
|
|
5202
|
+
function runEventPayload(metadata, payload = {}) {
|
|
5203
|
+
return metadata ? { ...payload, metadata: structuredClone(metadata) } : payload;
|
|
5204
|
+
}
|
|
5205
|
+
|
|
4996
5206
|
// src/runtime/memory-store.ts
|
|
4997
5207
|
function clone2(value) {
|
|
4998
5208
|
return structuredClone(value);
|
|
@@ -5021,7 +5231,6 @@ var InMemoryRuntimeStateStore = class {
|
|
|
5021
5231
|
const agent = {
|
|
5022
5232
|
id: createId("agt"),
|
|
5023
5233
|
sessionId: input.sessionId,
|
|
5024
|
-
...input.factoryId ? { factoryId: input.factoryId } : {},
|
|
5025
5234
|
state: "active",
|
|
5026
5235
|
createdAt: timestamp,
|
|
5027
5236
|
updatedAt: timestamp
|
|
@@ -5047,6 +5256,9 @@ var InMemoryRuntimeStateStore = class {
|
|
|
5047
5256
|
return clone2(agent);
|
|
5048
5257
|
}
|
|
5049
5258
|
async enqueueAgentInput(input) {
|
|
5259
|
+
return this.enqueueAgentInputState(input);
|
|
5260
|
+
}
|
|
5261
|
+
enqueueAgentInputState(input) {
|
|
5050
5262
|
this.requireAgent(input.agentId);
|
|
5051
5263
|
const suspended = [...this.runs.values()].filter((run2) => run2.agentId === input.agentId && run2.state === "suspended").sort((a, b) => a.createdAt.localeCompare(b.createdAt))[0];
|
|
5052
5264
|
const { timestamp } = now();
|
|
@@ -5067,6 +5279,7 @@ var InMemoryRuntimeStateStore = class {
|
|
|
5067
5279
|
suspended.state = "queued";
|
|
5068
5280
|
suspended.messageId = messageId;
|
|
5069
5281
|
delete suspended.suspensionReason;
|
|
5282
|
+
delete suspended.inputRequest;
|
|
5070
5283
|
suspended.updatedAt = timestamp;
|
|
5071
5284
|
message.runId = suspended.id;
|
|
5072
5285
|
}
|
|
@@ -5076,13 +5289,14 @@ var InMemoryRuntimeStateStore = class {
|
|
|
5076
5289
|
messageId,
|
|
5077
5290
|
state: "queued",
|
|
5078
5291
|
attempt: 0,
|
|
5292
|
+
...suspended?.metadata ? { metadata: clone2(suspended.metadata) } : input.input.metadata ? { metadata: clone2(input.input.metadata) } : {},
|
|
5079
5293
|
createdAt: timestamp,
|
|
5080
5294
|
updatedAt: timestamp
|
|
5081
5295
|
};
|
|
5082
5296
|
this.messages.set(message.id, message);
|
|
5083
5297
|
this.runs.set(run.id, suspended ?? run);
|
|
5084
5298
|
this.recordEvent("message_enqueued", { agentId: input.agentId, messageId });
|
|
5085
|
-
this.recordEvent("run_enqueued", { agentId: input.agentId, messageId, runId });
|
|
5299
|
+
this.recordEvent("run_enqueued", { agentId: input.agentId, messageId, runId }, runEventPayload(suspended?.metadata ?? input.input.metadata));
|
|
5086
5300
|
return { message: clone2(message), run: clone2(suspended ?? run), resumed: Boolean(suspended) };
|
|
5087
5301
|
}
|
|
5088
5302
|
async getMessage(messageId) {
|
|
@@ -5094,7 +5308,10 @@ var InMemoryRuntimeStateStore = class {
|
|
|
5094
5308
|
return run ? clone2(run) : void 0;
|
|
5095
5309
|
}
|
|
5096
5310
|
async listRuns(input = {}) {
|
|
5097
|
-
return clone2([...this.runs.values()].filter((run) => input.agentId === void 0 || run.agentId === input.agentId).filter((run) => !input.states || input.states.includes(run.state)).sort((a, b) => a.createdAt.localeCompare(b.createdAt)));
|
|
5311
|
+
return clone2([...this.runs.values()].filter((run) => input.agentId === void 0 || run.agentId === input.agentId).filter((run) => !input.states || input.states.includes(run.state)).sort((a, b) => a.createdAt.localeCompare(b.createdAt) || String(a.id).localeCompare(String(b.id))));
|
|
5312
|
+
}
|
|
5313
|
+
async listActiveRuns() {
|
|
5314
|
+
return this.listRuns({ states: ["queued", "running", "suspended"] });
|
|
5098
5315
|
}
|
|
5099
5316
|
async leaseRun(input) {
|
|
5100
5317
|
const run = this.requireRun(input.runId);
|
|
@@ -5159,15 +5376,17 @@ var InMemoryRuntimeStateStore = class {
|
|
|
5159
5376
|
const { timestamp } = now();
|
|
5160
5377
|
run.state = "suspended";
|
|
5161
5378
|
run.suspensionReason = input.reason;
|
|
5379
|
+
if (input.inputRequest) run.inputRequest = clone2(input.inputRequest);
|
|
5380
|
+
if (input.executionState) run.executionState = clone2(input.executionState);
|
|
5162
5381
|
delete run.leaseId;
|
|
5163
5382
|
run.updatedAt = timestamp;
|
|
5164
5383
|
requireTransition("Runtime Message", message.id, message.state, "acknowledge", ["leased"]);
|
|
5165
5384
|
message.state = "acknowledged";
|
|
5166
5385
|
delete message.lease;
|
|
5167
5386
|
message.updatedAt = timestamp;
|
|
5168
|
-
this.recordEvent("run_suspended", { agentId: run.agentId, messageId: message.id, runId: run.id }, {
|
|
5387
|
+
this.recordEvent("run_suspended", { agentId: run.agentId, messageId: message.id, runId: run.id }, runEventPayload(run.metadata, {
|
|
5169
5388
|
reason: input.reason
|
|
5170
|
-
});
|
|
5389
|
+
}));
|
|
5171
5390
|
return clone2(run);
|
|
5172
5391
|
}
|
|
5173
5392
|
async completeRun(input) {
|
|
@@ -5179,15 +5398,17 @@ var InMemoryRuntimeStateStore = class {
|
|
|
5179
5398
|
const state = input.state ?? "completed";
|
|
5180
5399
|
run.state = state;
|
|
5181
5400
|
run.outcome = clone2(input.outcome);
|
|
5401
|
+
delete run.inputRequest;
|
|
5402
|
+
delete run.executionState;
|
|
5182
5403
|
delete run.leaseId;
|
|
5183
5404
|
run.updatedAt = timestamp;
|
|
5184
5405
|
message.state = "acknowledged";
|
|
5185
5406
|
delete message.lease;
|
|
5186
5407
|
message.updatedAt = timestamp;
|
|
5187
|
-
this.recordEvent("run_completed", { agentId: run.agentId, messageId: run.messageId, runId: run.id }, {
|
|
5408
|
+
this.recordEvent("run_completed", { agentId: run.agentId, messageId: run.messageId, runId: run.id }, runEventPayload(run.metadata, {
|
|
5188
5409
|
state,
|
|
5189
5410
|
outcome: input.outcome
|
|
5190
|
-
});
|
|
5411
|
+
}));
|
|
5191
5412
|
this.recordEvent("message_acknowledged", { agentId: message.agentId, messageId: message.id });
|
|
5192
5413
|
return clone2(run);
|
|
5193
5414
|
}
|
|
@@ -5218,16 +5439,18 @@ var InMemoryRuntimeStateStore = class {
|
|
|
5218
5439
|
const { timestamp } = now();
|
|
5219
5440
|
run.state = "failed";
|
|
5220
5441
|
run.outcome = clone2(input.outcome);
|
|
5442
|
+
delete run.inputRequest;
|
|
5443
|
+
delete run.executionState;
|
|
5221
5444
|
delete run.leaseId;
|
|
5222
5445
|
run.updatedAt = timestamp;
|
|
5223
5446
|
message.state = "dead_lettered";
|
|
5224
5447
|
message.deadLetterReason = input.reason;
|
|
5225
5448
|
delete message.lease;
|
|
5226
5449
|
message.updatedAt = timestamp;
|
|
5227
|
-
this.recordEvent("run_completed", { agentId: run.agentId, messageId: message.id, runId: run.id }, {
|
|
5450
|
+
this.recordEvent("run_completed", { agentId: run.agentId, messageId: message.id, runId: run.id }, runEventPayload(run.metadata, {
|
|
5228
5451
|
state: "failed",
|
|
5229
5452
|
outcome: input.outcome
|
|
5230
|
-
});
|
|
5453
|
+
}));
|
|
5231
5454
|
this.recordEvent("message_dead_lettered", { agentId: run.agentId, messageId: message.id }, {
|
|
5232
5455
|
reason: input.reason
|
|
5233
5456
|
});
|
|
@@ -5240,6 +5463,8 @@ var InMemoryRuntimeStateStore = class {
|
|
|
5240
5463
|
const { timestamp } = now();
|
|
5241
5464
|
run.state = "cancelled";
|
|
5242
5465
|
run.outcome = clone2(input.outcome);
|
|
5466
|
+
delete run.inputRequest;
|
|
5467
|
+
delete run.executionState;
|
|
5243
5468
|
delete run.leaseId;
|
|
5244
5469
|
run.updatedAt = timestamp;
|
|
5245
5470
|
if (message.state === "queued" || message.state === "leased") {
|
|
@@ -5247,9 +5472,10 @@ var InMemoryRuntimeStateStore = class {
|
|
|
5247
5472
|
delete message.lease;
|
|
5248
5473
|
message.updatedAt = timestamp;
|
|
5249
5474
|
}
|
|
5250
|
-
this.recordEvent("run_aborted", { agentId: run.agentId, messageId: message.id, runId: run.id }, {
|
|
5475
|
+
this.recordEvent("run_aborted", { agentId: run.agentId, messageId: message.id, runId: run.id }, runEventPayload(run.metadata, {
|
|
5476
|
+
state: "cancelled",
|
|
5251
5477
|
outcome: input.outcome
|
|
5252
|
-
});
|
|
5478
|
+
}));
|
|
5253
5479
|
return clone2(run);
|
|
5254
5480
|
}
|
|
5255
5481
|
async recoverExpiredLeases(input) {
|
|
@@ -5390,6 +5616,29 @@ var InMemoryRuntimeStateStore = class {
|
|
|
5390
5616
|
this.eventCheckpoints.set(consumerId, checkpoint);
|
|
5391
5617
|
return clone2(checkpoint);
|
|
5392
5618
|
}
|
|
5619
|
+
async acknowledgeEventAndEnqueueAgentInput(input) {
|
|
5620
|
+
const event = this.events.find((candidate) => candidate.id === input.eventId);
|
|
5621
|
+
if (!event) throw new Error(`Runtime Event not found: ${input.eventId}.`);
|
|
5622
|
+
const current = this.eventCheckpoints.get(input.consumerId) ?? {
|
|
5623
|
+
consumerId: input.consumerId,
|
|
5624
|
+
sequence: 0,
|
|
5625
|
+
updatedAt: createTimestamp(/* @__PURE__ */ new Date(0))
|
|
5626
|
+
};
|
|
5627
|
+
if (event.sequence <= current.sequence) return { checkpoint: clone2(current) };
|
|
5628
|
+
if (event.sequence !== current.sequence + 1) {
|
|
5629
|
+
throw new Error(`Runtime Event Consumer Checkpoint cannot advance from sequence ${current.sequence} to ${event.sequence}.`);
|
|
5630
|
+
}
|
|
5631
|
+
this.requireAgent(input.agentId);
|
|
5632
|
+
const enqueued = this.enqueueAgentInputState(input);
|
|
5633
|
+
const checkpoint = {
|
|
5634
|
+
consumerId: input.consumerId,
|
|
5635
|
+
sequence: event.sequence,
|
|
5636
|
+
eventId: event.id,
|
|
5637
|
+
updatedAt: createTimestamp()
|
|
5638
|
+
};
|
|
5639
|
+
this.eventCheckpoints.set(input.consumerId, checkpoint);
|
|
5640
|
+
return { checkpoint: clone2(checkpoint), enqueued };
|
|
5641
|
+
}
|
|
5393
5642
|
requireAgent(agentId) {
|
|
5394
5643
|
const agent = this.agents.get(agentId);
|
|
5395
5644
|
if (!agent) {
|
|
@@ -5438,7 +5687,6 @@ var RUNTIME_SCHEMA_SQL = `
|
|
|
5438
5687
|
CREATE TABLE IF NOT EXISTS agents (
|
|
5439
5688
|
id TEXT PRIMARY KEY NOT NULL,
|
|
5440
5689
|
session_id TEXT NOT NULL,
|
|
5441
|
-
factory_id TEXT,
|
|
5442
5690
|
state TEXT NOT NULL CHECK (state IN ('active', 'paused')),
|
|
5443
5691
|
created_at TEXT NOT NULL,
|
|
5444
5692
|
updated_at TEXT NOT NULL
|
|
@@ -5466,6 +5714,9 @@ var RUNTIME_SCHEMA_SQL = `
|
|
|
5466
5714
|
lease_id TEXT,
|
|
5467
5715
|
outcome_json TEXT,
|
|
5468
5716
|
suspension_reason TEXT,
|
|
5717
|
+
input_request_json TEXT,
|
|
5718
|
+
execution_state_json TEXT,
|
|
5719
|
+
metadata_json TEXT,
|
|
5469
5720
|
created_at TEXT NOT NULL,
|
|
5470
5721
|
updated_at TEXT NOT NULL
|
|
5471
5722
|
);
|
|
@@ -5528,6 +5779,11 @@ function initializeRuntimeSchema(database) {
|
|
|
5528
5779
|
database.run(sql);
|
|
5529
5780
|
}
|
|
5530
5781
|
}
|
|
5782
|
+
try {
|
|
5783
|
+
database.run("ALTER TABLE agent_runs ADD COLUMN metadata_json TEXT");
|
|
5784
|
+
} catch (error) {
|
|
5785
|
+
if (!(error instanceof Error) || !/duplicate column name/i.test(error.message)) throw error;
|
|
5786
|
+
}
|
|
5531
5787
|
});
|
|
5532
5788
|
initialize();
|
|
5533
5789
|
}
|
|
@@ -5572,15 +5828,14 @@ var SqliteRuntimeStateStore = class {
|
|
|
5572
5828
|
const agent = {
|
|
5573
5829
|
id: createId("agt"),
|
|
5574
5830
|
sessionId: input.sessionId,
|
|
5575
|
-
...input.factoryId ? { factoryId: input.factoryId } : {},
|
|
5576
5831
|
state: "active",
|
|
5577
5832
|
createdAt: timestamp,
|
|
5578
5833
|
updatedAt: timestamp
|
|
5579
5834
|
};
|
|
5580
5835
|
const create = this.database.transaction(() => {
|
|
5581
5836
|
this.database.run(
|
|
5582
|
-
"INSERT INTO agents (id, session_id,
|
|
5583
|
-
[agent.id, agent.sessionId, agent.
|
|
5837
|
+
"INSERT INTO agents (id, session_id, state, created_at, updated_at) VALUES (?, ?, ?, ?, ?)",
|
|
5838
|
+
[agent.id, agent.sessionId, agent.state, agent.createdAt, agent.updatedAt]
|
|
5584
5839
|
);
|
|
5585
5840
|
this.recordEvent("agent_created", { agentId: agent.id });
|
|
5586
5841
|
});
|
|
@@ -5607,6 +5862,10 @@ var SqliteRuntimeStateStore = class {
|
|
|
5607
5862
|
return clone3(updated);
|
|
5608
5863
|
}
|
|
5609
5864
|
async enqueueAgentInput(input) {
|
|
5865
|
+
const enqueue = this.database.transaction(() => this.enqueueAgentInputState(input));
|
|
5866
|
+
return clone3(enqueue());
|
|
5867
|
+
}
|
|
5868
|
+
enqueueAgentInputState(input) {
|
|
5610
5869
|
this.requireAgent(input.agentId);
|
|
5611
5870
|
const { timestamp } = timestampFor();
|
|
5612
5871
|
const messageId = createId("rmsg");
|
|
@@ -5631,30 +5890,28 @@ var SqliteRuntimeStateStore = class {
|
|
|
5631
5890
|
messageId,
|
|
5632
5891
|
state: "queued",
|
|
5633
5892
|
attempt: 0,
|
|
5893
|
+
...suspendedRow?.metadata_json ? { metadata: parse(suspendedRow.metadata_json) } : input.input.metadata ? { metadata: clone3(input.input.metadata) } : {},
|
|
5634
5894
|
createdAt: timestamp,
|
|
5635
5895
|
updatedAt: timestamp
|
|
5636
5896
|
};
|
|
5637
5897
|
if (suspendedRow) {
|
|
5638
5898
|
message.runId = runId;
|
|
5639
5899
|
}
|
|
5640
|
-
|
|
5641
|
-
|
|
5642
|
-
|
|
5643
|
-
|
|
5644
|
-
|
|
5645
|
-
|
|
5646
|
-
|
|
5647
|
-
|
|
5648
|
-
|
|
5649
|
-
|
|
5650
|
-
|
|
5651
|
-
|
|
5652
|
-
|
|
5653
|
-
this.recordEvent("message_enqueued", { agentId: input.agentId, messageId });
|
|
5654
|
-
this.recordEvent("run_enqueued", { agentId: input.agentId, messageId, runId });
|
|
5655
|
-
});
|
|
5656
|
-
enqueue();
|
|
5900
|
+
this.insertMessage(message);
|
|
5901
|
+
if (suspendedRow) {
|
|
5902
|
+
this.database.run(
|
|
5903
|
+
"UPDATE agent_runs SET message_id = ?, state = 'queued', lease_id = NULL, suspension_reason = NULL, input_request_json = NULL, updated_at = ? WHERE id = ?",
|
|
5904
|
+
[messageId, timestamp, runId]
|
|
5905
|
+
);
|
|
5906
|
+
} else {
|
|
5907
|
+
this.database.run(
|
|
5908
|
+
"INSERT INTO agent_runs (id, agent_id, message_id, state, attempt, lease_id, outcome_json, suspension_reason, input_request_json, execution_state_json, metadata_json, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
|
5909
|
+
[run.id, run.agentId, run.messageId, run.state, run.attempt, null, null, null, null, null, run.metadata ? json(run.metadata) : null, run.createdAt, run.updatedAt]
|
|
5910
|
+
);
|
|
5911
|
+
}
|
|
5912
|
+
this.recordEventInternal("message_enqueued", { agentId: input.agentId, messageId });
|
|
5657
5913
|
const storedRun = this.requireRun(runId);
|
|
5914
|
+
this.recordEventInternal("run_enqueued", { agentId: input.agentId, messageId, runId }, runEventPayload(storedRun.metadata));
|
|
5658
5915
|
return { message: clone3(message), run: clone3(storedRun), resumed: Boolean(suspendedRow) };
|
|
5659
5916
|
}
|
|
5660
5917
|
async getMessage(messageId) {
|
|
@@ -5680,6 +5937,12 @@ var SqliteRuntimeStateStore = class {
|
|
|
5680
5937
|
const rows = this.database.query(`SELECT * FROM agent_runs ${where} ORDER BY created_at, id`).all(...params);
|
|
5681
5938
|
return rows.map((row) => this.runFromRow(row));
|
|
5682
5939
|
}
|
|
5940
|
+
async listActiveRuns() {
|
|
5941
|
+
const rows = this.database.query(
|
|
5942
|
+
"SELECT * FROM agent_runs WHERE state IN ('queued', 'running', 'suspended') ORDER BY created_at, id"
|
|
5943
|
+
).all();
|
|
5944
|
+
return rows.map((row) => this.runFromRow(row));
|
|
5945
|
+
}
|
|
5683
5946
|
async leaseRun(input) {
|
|
5684
5947
|
const lease = this.database.transaction(() => {
|
|
5685
5948
|
const run2 = this.requireRun(input.runId);
|
|
@@ -5754,17 +6017,17 @@ var SqliteRuntimeStateStore = class {
|
|
|
5754
6017
|
requireTransition2("Runtime Message", message.id, message.state, "acknowledge", ["leased"]);
|
|
5755
6018
|
const { timestamp } = timestampFor();
|
|
5756
6019
|
this.database.run(
|
|
5757
|
-
"UPDATE agent_runs SET state = ?, lease_id = NULL, suspension_reason = ?, updated_at = ? WHERE id = ?",
|
|
5758
|
-
["suspended", input.reason ?? null, timestamp, current.id]
|
|
6020
|
+
"UPDATE agent_runs SET state = ?, lease_id = NULL, suspension_reason = ?, input_request_json = ?, execution_state_json = ?, updated_at = ? WHERE id = ?",
|
|
6021
|
+
["suspended", input.reason ?? null, input.inputRequest ? json(input.inputRequest) : null, input.executionState ? json(input.executionState) : null, timestamp, current.id]
|
|
5759
6022
|
);
|
|
5760
6023
|
this.database.run(
|
|
5761
6024
|
"UPDATE runtime_messages SET state = ?, updated_at = ? WHERE id = ?",
|
|
5762
6025
|
["acknowledged", timestamp, message.id]
|
|
5763
6026
|
);
|
|
5764
6027
|
this.database.run("DELETE FROM runtime_leases WHERE run_id = ?", [current.id]);
|
|
5765
|
-
this.recordEvent("run_suspended", { agentId: current.agentId, messageId: message.id, runId: current.id }, {
|
|
6028
|
+
this.recordEvent("run_suspended", { agentId: current.agentId, messageId: message.id, runId: current.id }, runEventPayload(current.metadata, {
|
|
5766
6029
|
reason: input.reason
|
|
5767
|
-
});
|
|
6030
|
+
}));
|
|
5768
6031
|
return this.requireRun(current.id);
|
|
5769
6032
|
})();
|
|
5770
6033
|
return clone3(run);
|
|
@@ -5778,7 +6041,7 @@ var SqliteRuntimeStateStore = class {
|
|
|
5778
6041
|
const { timestamp } = timestampFor();
|
|
5779
6042
|
const state = input.state ?? "completed";
|
|
5780
6043
|
this.database.run(
|
|
5781
|
-
"UPDATE agent_runs SET state = ?, lease_id = NULL, outcome_json = ?, updated_at = ? WHERE id = ?",
|
|
6044
|
+
"UPDATE agent_runs SET state = ?, lease_id = NULL, outcome_json = ?, input_request_json = NULL, execution_state_json = NULL, updated_at = ? WHERE id = ?",
|
|
5782
6045
|
[state, json(input.outcome), timestamp, current.id]
|
|
5783
6046
|
);
|
|
5784
6047
|
this.database.run(
|
|
@@ -5786,10 +6049,10 @@ var SqliteRuntimeStateStore = class {
|
|
|
5786
6049
|
[timestamp, message.id]
|
|
5787
6050
|
);
|
|
5788
6051
|
this.database.run("DELETE FROM runtime_leases WHERE run_id = ?", [current.id]);
|
|
5789
|
-
this.recordEvent("run_completed", { agentId: current.agentId, messageId: current.messageId, runId: current.id }, {
|
|
6052
|
+
this.recordEvent("run_completed", { agentId: current.agentId, messageId: current.messageId, runId: current.id }, runEventPayload(current.metadata, {
|
|
5790
6053
|
state,
|
|
5791
6054
|
outcome: input.outcome
|
|
5792
|
-
});
|
|
6055
|
+
}));
|
|
5793
6056
|
this.recordEvent("message_acknowledged", { agentId: message.agentId, messageId: message.id });
|
|
5794
6057
|
return this.requireRun(current.id);
|
|
5795
6058
|
})();
|
|
@@ -5828,7 +6091,7 @@ var SqliteRuntimeStateStore = class {
|
|
|
5828
6091
|
requireTransition2("Runtime Message", message.id, message.state, "dead-letter", ["leased"]);
|
|
5829
6092
|
const { timestamp } = timestampFor();
|
|
5830
6093
|
this.database.run(
|
|
5831
|
-
"UPDATE agent_runs SET state = 'failed', lease_id = NULL, outcome_json = ?, updated_at = ? WHERE id = ?",
|
|
6094
|
+
"UPDATE agent_runs SET state = 'failed', lease_id = NULL, outcome_json = ?, input_request_json = NULL, execution_state_json = NULL, updated_at = ? WHERE id = ?",
|
|
5832
6095
|
[json(input.outcome), timestamp, current.id]
|
|
5833
6096
|
);
|
|
5834
6097
|
this.database.run(
|
|
@@ -5836,10 +6099,10 @@ var SqliteRuntimeStateStore = class {
|
|
|
5836
6099
|
[input.reason, timestamp, message.id]
|
|
5837
6100
|
);
|
|
5838
6101
|
this.database.run("DELETE FROM runtime_leases WHERE run_id = ?", [current.id]);
|
|
5839
|
-
this.recordEvent("run_completed", { agentId: current.agentId, messageId: message.id, runId: current.id }, {
|
|
6102
|
+
this.recordEvent("run_completed", { agentId: current.agentId, messageId: message.id, runId: current.id }, runEventPayload(current.metadata, {
|
|
5840
6103
|
state: "failed",
|
|
5841
6104
|
outcome: input.outcome
|
|
5842
|
-
});
|
|
6105
|
+
}));
|
|
5843
6106
|
this.recordEvent("message_dead_lettered", { agentId: current.agentId, messageId: message.id }, {
|
|
5844
6107
|
reason: input.reason
|
|
5845
6108
|
});
|
|
@@ -5854,7 +6117,7 @@ var SqliteRuntimeStateStore = class {
|
|
|
5854
6117
|
const message = this.requireMessage(current.messageId);
|
|
5855
6118
|
const { timestamp } = timestampFor();
|
|
5856
6119
|
this.database.run(
|
|
5857
|
-
"UPDATE agent_runs SET state = 'cancelled', lease_id = NULL, outcome_json = ?, updated_at = ? WHERE id = ?",
|
|
6120
|
+
"UPDATE agent_runs SET state = 'cancelled', lease_id = NULL, outcome_json = ?, input_request_json = NULL, execution_state_json = NULL, updated_at = ? WHERE id = ?",
|
|
5858
6121
|
[json(input.outcome), timestamp, current.id]
|
|
5859
6122
|
);
|
|
5860
6123
|
if (message.state === "queued" || message.state === "leased") {
|
|
@@ -5864,9 +6127,10 @@ var SqliteRuntimeStateStore = class {
|
|
|
5864
6127
|
]);
|
|
5865
6128
|
}
|
|
5866
6129
|
this.database.run("DELETE FROM runtime_leases WHERE run_id = ?", [current.id]);
|
|
5867
|
-
this.recordEvent("run_aborted", { agentId: current.agentId, messageId: message.id, runId: current.id }, {
|
|
6130
|
+
this.recordEvent("run_aborted", { agentId: current.agentId, messageId: message.id, runId: current.id }, runEventPayload(current.metadata, {
|
|
6131
|
+
state: "cancelled",
|
|
5868
6132
|
outcome: input.outcome
|
|
5869
|
-
});
|
|
6133
|
+
}));
|
|
5870
6134
|
return this.requireRun(current.id);
|
|
5871
6135
|
})();
|
|
5872
6136
|
return clone3(run);
|
|
@@ -6040,27 +6304,41 @@ var SqliteRuntimeStateStore = class {
|
|
|
6040
6304
|
return clone3(this.eventCheckpoint(consumerId));
|
|
6041
6305
|
}
|
|
6042
6306
|
async acknowledgeEvent(consumerId, eventId) {
|
|
6043
|
-
const
|
|
6044
|
-
|
|
6045
|
-
const current = this.eventCheckpoint(consumerId);
|
|
6046
|
-
if (event.sequence <= current.sequence) return current;
|
|
6047
|
-
if (event.sequence !== current.sequence + 1) {
|
|
6048
|
-
throw new Error(`Runtime Event Consumer Checkpoint cannot advance from sequence ${current.sequence} to ${event.sequence}.`);
|
|
6049
|
-
}
|
|
6050
|
-
const updatedAt = createTimestamp();
|
|
6051
|
-
this.database.run(
|
|
6052
|
-
`INSERT INTO runtime_event_consumers (consumer_id, sequence, event_id, updated_at)
|
|
6053
|
-
VALUES (?, ?, ?, ?)
|
|
6054
|
-
ON CONFLICT(consumer_id) DO UPDATE SET
|
|
6055
|
-
sequence = excluded.sequence,
|
|
6056
|
-
event_id = excluded.event_id,
|
|
6057
|
-
updated_at = excluded.updated_at`,
|
|
6058
|
-
[consumerId, event.sequence, event.id, updatedAt]
|
|
6059
|
-
);
|
|
6060
|
-
return this.eventCheckpoint(consumerId);
|
|
6061
|
-
})();
|
|
6307
|
+
const acknowledge = this.database.transaction(() => this.acknowledgeEventState(consumerId, eventId));
|
|
6308
|
+
const checkpoint = acknowledge();
|
|
6062
6309
|
return clone3(checkpoint);
|
|
6063
6310
|
}
|
|
6311
|
+
async acknowledgeEventAndEnqueueAgentInput(input) {
|
|
6312
|
+
const deliver = this.database.transaction(() => {
|
|
6313
|
+
const event = this.requireEvent(input.eventId);
|
|
6314
|
+
const current = this.eventCheckpoint(input.consumerId);
|
|
6315
|
+
if (event.sequence <= current.sequence) return { checkpoint: current };
|
|
6316
|
+
this.requireAgent(input.agentId);
|
|
6317
|
+
const enqueued = this.enqueueAgentInputState(input);
|
|
6318
|
+
const checkpoint = this.acknowledgeEventState(input.consumerId, input.eventId);
|
|
6319
|
+
return { checkpoint, enqueued };
|
|
6320
|
+
});
|
|
6321
|
+
return clone3(deliver());
|
|
6322
|
+
}
|
|
6323
|
+
acknowledgeEventState(consumerId, eventId) {
|
|
6324
|
+
const event = this.requireEvent(eventId);
|
|
6325
|
+
const current = this.eventCheckpoint(consumerId);
|
|
6326
|
+
if (event.sequence <= current.sequence) return current;
|
|
6327
|
+
if (event.sequence !== current.sequence + 1) {
|
|
6328
|
+
throw new Error(`Runtime Event Consumer Checkpoint cannot advance from sequence ${current.sequence} to ${event.sequence}.`);
|
|
6329
|
+
}
|
|
6330
|
+
const updatedAt = createTimestamp();
|
|
6331
|
+
this.database.run(
|
|
6332
|
+
`INSERT INTO runtime_event_consumers (consumer_id, sequence, event_id, updated_at)
|
|
6333
|
+
VALUES (?, ?, ?, ?)
|
|
6334
|
+
ON CONFLICT(consumer_id) DO UPDATE SET
|
|
6335
|
+
sequence = excluded.sequence,
|
|
6336
|
+
event_id = excluded.event_id,
|
|
6337
|
+
updated_at = excluded.updated_at`,
|
|
6338
|
+
[consumerId, event.sequence, event.id, updatedAt]
|
|
6339
|
+
);
|
|
6340
|
+
return this.eventCheckpoint(consumerId);
|
|
6341
|
+
}
|
|
6064
6342
|
insertMessage(message) {
|
|
6065
6343
|
this.database.run(
|
|
6066
6344
|
"INSERT INTO runtime_messages (id, agent_id, kind, payload_json, state, attempts, run_id, dead_letter_reason, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
|
@@ -6071,7 +6349,6 @@ var SqliteRuntimeStateStore = class {
|
|
|
6071
6349
|
return {
|
|
6072
6350
|
id: row.id,
|
|
6073
6351
|
sessionId: row.session_id,
|
|
6074
|
-
...row.factory_id ? { factoryId: row.factory_id } : {},
|
|
6075
6352
|
state: row.state,
|
|
6076
6353
|
createdAt: row.created_at,
|
|
6077
6354
|
updatedAt: row.updated_at
|
|
@@ -6111,6 +6388,9 @@ var SqliteRuntimeStateStore = class {
|
|
|
6111
6388
|
...row.lease_id ? { leaseId: row.lease_id } : {},
|
|
6112
6389
|
...row.outcome_json ? { outcome: parse(row.outcome_json) } : {},
|
|
6113
6390
|
...row.suspension_reason ? { suspensionReason: row.suspension_reason } : {},
|
|
6391
|
+
...row.input_request_json ? { inputRequest: parse(row.input_request_json) } : {},
|
|
6392
|
+
...row.execution_state_json ? { executionState: parse(row.execution_state_json) } : {},
|
|
6393
|
+
...row.metadata_json ? { metadata: parse(row.metadata_json) } : {},
|
|
6114
6394
|
createdAt: row.created_at,
|
|
6115
6395
|
updatedAt: row.updated_at
|
|
6116
6396
|
};
|
|
@@ -6235,8 +6515,8 @@ export {
|
|
|
6235
6515
|
AgentRun,
|
|
6236
6516
|
AgentRuntime,
|
|
6237
6517
|
InMemoryRuntimeStateStore,
|
|
6238
|
-
|
|
6239
|
-
|
|
6518
|
+
InMemorySessionStore,
|
|
6519
|
+
JsonlSessionStore,
|
|
6240
6520
|
SqliteRuntimeStateStore,
|
|
6241
6521
|
createCoreTools,
|
|
6242
6522
|
createMessage
|