@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.
Files changed (4) hide show
  1. package/README.md +132 -55
  2. package/dist/index.d.ts +209 -145
  3. package/dist/index.js +455 -175
  4. 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, toolUseId) {
524
+ function buildPhaseDirectiveMessage(phase, output) {
525
525
  const parts = [];
526
- parts.push(`<phase name="${escapeXml(phase.name)}">`);
527
- parts.push(` <content>${phase.content}</content>`);
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(`</phase>`);
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
- if (oldMessages.length < minCompact) {
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(oldMessages);
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: oldMessages.length
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: oldMessages.length,
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, messageManager) {
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 msgId = messageManager.start("tool", content, { phase: phase.id });
1429
- messageManager.end(msgId);
1430
- return msgId;
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
- let currentPhaseId = registry.entryPhaseId;
1594
- let isContinuing = false;
1595
- let previousPayload = void 0;
1596
- let previousPhaseMsgId = void 0;
1597
- let previousResults = [];
1598
- let pendingInstruction = void 0;
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
- removePhaseMessage(config.context.messages, previousPhaseMsgId);
1677
- previousPhaseMsgId = void 0;
1678
- if (enteringNewPhase) {
1679
- previousPhaseMsgId = injectPhaseContent(phase, { results: previousResults, instruction: pendingInstruction }, messageManager);
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
- config.emit?.({ type: "user_prompt_requested", phase: currentPhaseId, ts: createTimestamp() });
1721
- const userMessages = await config.waitForInput();
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(config, state, registry, pt, payload, context, availablePhases, currentPhaseId);
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
- if (routeToolCalled) removePhaseMessage(config.context.messages, previousPhaseMsgId);
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, currentPhaseId) {
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 = phase.isolated ? readPhaseContent(phase) : config.context.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 = phase.isolated ? void 0 : injectPhaseContent(
2092
+ const phaseMsgId = injectPhaseContent(
2022
2093
  phase,
2023
- { results: payload !== void 0 ? [{ name: currentPhaseId, output: payload }] : [] },
2024
- messageManager
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: "", phaseId: phase.id, payload: resultPayload, content: output.message };
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.appendMessage(message);
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
- const internal = { onSuspend: control.suspend };
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
- if (!suspensionRequested) {
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 entryPhaseId = this.state.initialized ? DEFAULT_PHASE_ID : runContext.phases?.entryPhaseId ?? DEFAULT_PHASE_ID;
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
- execute: (message, runId, control) => execution.executeAgentInput(message, runId, control)
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/provider.ts
4244
- var InMemorySessionProvider = class {
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 LocalJsonlSessionProvider = class {
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
- if (options.factoryId !== void 0 && options.factoryId.trim().length === 0) {
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: options.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
- for (const binding of this.bindings.values()) {
4634
- binding.abort("Agent Runtime stopped.");
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
- void this.deliverEvents(subscription).catch(() => void 0);
4740
- return () => {
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
- await this.stateStore.acknowledgeEvent(subscription.consumerId, event.id);
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, factory_id, state, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)",
5583
- [agent.id, agent.sessionId, agent.factoryId ?? null, agent.state, agent.createdAt, agent.updatedAt]
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
- const enqueue = this.database.transaction(() => {
5641
- this.insertMessage(message);
5642
- if (suspendedRow) {
5643
- this.database.run(
5644
- "UPDATE agent_runs SET message_id = ?, state = 'queued', lease_id = NULL, suspension_reason = NULL, updated_at = ? WHERE id = ?",
5645
- [messageId, timestamp, runId]
5646
- );
5647
- } else {
5648
- this.database.run(
5649
- "INSERT INTO agent_runs (id, agent_id, message_id, state, attempt, lease_id, outcome_json, suspension_reason, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
5650
- [run.id, run.agentId, run.messageId, run.state, run.attempt, null, null, null, run.createdAt, run.updatedAt]
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 checkpoint = this.database.transaction(() => {
6044
- const event = this.requireEvent(eventId);
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
- InMemorySessionProvider,
6239
- LocalJsonlSessionProvider,
6518
+ InMemorySessionStore,
6519
+ JsonlSessionStore,
6240
6520
  SqliteRuntimeStateStore,
6241
6521
  createCoreTools,
6242
6522
  createMessage