@rowan-agent/agent 0.15.3 → 0.15.5

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 (3) hide show
  1. package/dist/index.d.ts +117 -4
  2. package/dist/index.js +912 -136
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -3264,6 +3264,13 @@ function createPhaseExecution(config, state, phase, messageManager, toolExecutio
3264
3264
  }
3265
3265
  };
3266
3266
  }
3267
+ function parallelPhaseReply(produced) {
3268
+ const last = [...produced].reverse().find((message) => message.role === "assistant");
3269
+ if (!last) return void 0;
3270
+ if (typeof last.content === "string") return last.content.trim() ? last : void 0;
3271
+ if (last.content.some((part) => part.type === "tool_use")) return void 0;
3272
+ return last.content.some((part) => part.type === "text" && part.text.trim()) ? last : void 0;
3273
+ }
3267
3274
  async function executeParallelPhase(config, state, registry, phase, payload, instruction, context, availablePhases, instanceId, groupId, index, count, sourcePhaseId) {
3268
3275
  const messages = [...context];
3269
3276
  const effectivePayload = preparePhasePayload(phase.input, payload);
@@ -3305,7 +3312,12 @@ async function executeParallelPhase(config, state, registry, phase, payload, ins
3305
3312
  const toolExecutionManager = createToolExecutionManager();
3306
3313
  const execution = createPhaseExecution(config, state, phase, messageManager, toolExecutionManager, registry);
3307
3314
  const runtime = { phase, config, state, execution, messageManager, registry, context: phaseContext };
3315
+ const phaseStart = messages.length;
3308
3316
  let output = await executePhase(runtime);
3317
+ const reply = parallelPhaseReply(messages.slice(phaseStart));
3318
+ if (reply && config.onParallelPhaseOutput) {
3319
+ await config.onParallelPhaseOutput(reply, { mode: "parallel", instanceId, groupId, index, count, sourcePhaseId });
3320
+ }
3309
3321
  if (phaseMsgId) removePhaseMessage(messages, phaseMsgId);
3310
3322
  if (config.afterPhase) {
3311
3323
  const extAfter = await config.afterPhase(phase.name, output);
@@ -3483,6 +3495,7 @@ async function executeOnce(input) {
3483
3495
  afterPhase: input.afterPhase,
3484
3496
  beforePrompt: input.beforePrompt,
3485
3497
  onMessage: input.onMessage,
3498
+ onParallelPhaseOutput: input.onParallelPhaseOutput,
3486
3499
  onMessageDelta: input.onMessageDelta,
3487
3500
  onThinkingDelta: input.onThinkingDelta,
3488
3501
  onOutcome: input.onOutcome,
@@ -4307,6 +4320,10 @@ function renderInteractionText(record) {
4307
4320
  const replyStr = record.reply ?? "";
4308
4321
  return template.replaceAll("{{prompt}}", record.prompt).replaceAll("{{answer}}", answerStr).replaceAll("{{reply}}", replyStr);
4309
4322
  }
4323
+ var PARALLEL_PHASE_METADATA_KEY = "parallelPhase";
4324
+ function isParallelPhaseOutput(message) {
4325
+ return message.role === "assistant" && message.metadata?.[PARALLEL_PHASE_METADATA_KEY] !== void 0;
4326
+ }
4310
4327
  function projectModelContext(input) {
4311
4328
  const toolInteractionTexts = /* @__PURE__ */ new Map();
4312
4329
  for (const message of input.messages) {
@@ -4322,6 +4339,7 @@ function projectModelContext(input) {
4322
4339
  }
4323
4340
  const projectedMessages = [];
4324
4341
  for (const message of input.messages) {
4342
+ if (isParallelPhaseOutput(message)) continue;
4325
4343
  if (message.role === "interaction") {
4326
4344
  if (message.toolCallId !== void 0) {
4327
4345
  continue;
@@ -6613,6 +6631,33 @@ var AgentRuntime = class _AgentRuntime {
6613
6631
  status
6614
6632
  });
6615
6633
  },
6634
+ onParallelPhaseOutput: (message, invocation) => {
6635
+ const task = toolQueue.then(async () => {
6636
+ const active = this.executions.get(run.id);
6637
+ if (this.closed || controller.signal.aborted || active?.executionId !== claim.execution.executionId) return;
6638
+ const projected = projectAssistantMessage(message, run.agentId, run.id, 0);
6639
+ const committed = await this.owned.commitPhaseOutput({
6640
+ runId: run.id,
6641
+ execution: claim.execution,
6642
+ expectedRevision: executionRevision,
6643
+ message: {
6644
+ ...projected,
6645
+ metadata: {
6646
+ ...projected.metadata,
6647
+ [PARALLEL_PHASE_METADATA_KEY]: {
6648
+ groupId: invocation.groupId,
6649
+ instanceId: invocation.instanceId,
6650
+ index: invocation.index,
6651
+ count: invocation.count
6652
+ }
6653
+ }
6654
+ }
6655
+ });
6656
+ executionRevision = committed.revision;
6657
+ });
6658
+ toolQueue = task.then(() => void 0, () => void 0);
6659
+ return task;
6660
+ },
6616
6661
  onMessageDelta: (event) => {
6617
6662
  const active = this.executions.get(run.id);
6618
6663
  if (this.closed || controller.signal.aborted || active?.executionId !== claim.execution.executionId) return;
@@ -6700,7 +6745,7 @@ var AgentRuntime = class _AgentRuntime {
6700
6745
  }
6701
6746
  }
6702
6747
  if (this.cancellationRequested.has(run.id) || controller.signal.aborted) {
6703
- const output = latestAssistant(run, result.messages, modelMessages.length, true);
6748
+ const output = latestAssistant(run, result.messages, executionContext.messages.length, true);
6704
6749
  const reason = this.cancellationReasons.get(run.id) ?? "Agent run stopped.";
6705
6750
  await this.owned.cancelRun({
6706
6751
  runId: run.id,
@@ -6723,7 +6768,7 @@ var AgentRuntime = class _AgentRuntime {
6723
6768
  const output = isToolCallInteraction ? void 0 : latestAssistant(
6724
6769
  run,
6725
6770
  result.messages,
6726
- modelMessages.length
6771
+ executionContext.messages.length
6727
6772
  );
6728
6773
  const prompt = isToolCallInteraction ? void 0 : output ?? (result.interactions[0]?.prompt ? promptMessage(run, result.interactions[0].prompt, result.messages.length) : void 0);
6729
6774
  await this.owned.commitInputRequired({
@@ -6741,7 +6786,7 @@ var AgentRuntime = class _AgentRuntime {
6741
6786
  return;
6742
6787
  }
6743
6788
  if (result.type === "completed") {
6744
- const output = latestAssistant(run, result.messages, modelMessages.length);
6789
+ const output = latestAssistant(run, result.messages, executionContext.messages.length);
6745
6790
  await this.owned.commitOutcome({ runId: run.id, execution: claim.execution, expectedRevision: executionRevision, outcome: durableOutcome(result.outcome), ...output ? { output } : {} });
6746
6791
  this.transientEvents.clear(run.id, claim.execution.executionId, true);
6747
6792
  return;
@@ -7573,9 +7618,83 @@ var InMemoryStore = class _InMemoryStore {
7573
7618
  eventSequence = 0;
7574
7619
  retentionFloor = 1;
7575
7620
  contextCompactions = /* @__PURE__ */ new Map();
7621
+ recorder;
7622
+ runReceiptKeys = /* @__PURE__ */ new Map();
7576
7623
  constructor(options = {}) {
7577
7624
  this.incarnation = options.incarnation ?? createId("store");
7578
7625
  }
7626
+ setChangeRecorder(recorder) {
7627
+ this.recorder = recorder;
7628
+ }
7629
+ touchAgent(agent) {
7630
+ this.recorder?.onAgentUpsert(agent);
7631
+ }
7632
+ touchRun(run) {
7633
+ this.recorder?.onRunUpsert(run);
7634
+ }
7635
+ storeMessage(message) {
7636
+ this.messages.set(message.id, message);
7637
+ this.recorder?.onMessageInsert(message);
7638
+ }
7639
+ storeToolCall(toolCall) {
7640
+ this.toolCalls.set(toolCall.id, toolCall);
7641
+ this.recorder?.onToolCallUpsert(toolCall);
7642
+ }
7643
+ appendEvent(event) {
7644
+ this.events.push(event);
7645
+ this.recorder?.onEventAppend(event);
7646
+ }
7647
+ inferRunIdForReceipt(key, payload) {
7648
+ const parts = key.split(":");
7649
+ const prefix = parts[0];
7650
+ if (["queued_failure", "phase_entered", "input_required", "interaction_answer", "outcome", "cancel"].includes(prefix) && parts[1]) {
7651
+ return parts[1];
7652
+ }
7653
+ const match = payload.match(/^\[\s*"([^"]+)"/);
7654
+ if (match && match[1]) {
7655
+ const candidate = match[1];
7656
+ if (this.runs.has(candidate) || candidate.startsWith("run_") || candidate.startsWith("run-")) {
7657
+ return candidate;
7658
+ }
7659
+ }
7660
+ return void 0;
7661
+ }
7662
+ dropRunOperationReceipts(runId) {
7663
+ const keys = this.runReceiptKeys.get(runId);
7664
+ if (keys && keys.size > 0) {
7665
+ for (const key of keys) {
7666
+ this.operationReceipts.delete(key);
7667
+ this.recorder?.onOperationReceiptDelete(key);
7668
+ }
7669
+ this.runReceiptKeys.delete(runId);
7670
+ }
7671
+ for (const [key] of this.operationReceipts.entries()) {
7672
+ if (key.includes(`:${runId}:`) || key.endsWith(`:${runId}`) || key.startsWith(`phase_output:${runId}`) || key.startsWith(`tool_reserve_batch:${runId}`)) {
7673
+ this.operationReceipts.delete(key);
7674
+ this.recorder?.onOperationReceiptDelete(key);
7675
+ }
7676
+ }
7677
+ }
7678
+ compactTerminalReceipts() {
7679
+ let count = 0;
7680
+ for (const [key, receipt] of [...this.operationReceipts.entries()]) {
7681
+ const runId = this.inferRunIdForReceipt(key, receipt.payload);
7682
+ if (runId) {
7683
+ const run = this.runs.get(runId);
7684
+ if (!run || ["completed", "failed", "cancelled"].includes(run.state)) {
7685
+ this.operationReceipts.delete(key);
7686
+ const keys = this.runReceiptKeys.get(runId);
7687
+ if (keys) {
7688
+ keys.delete(key);
7689
+ if (keys.size === 0) this.runReceiptKeys.delete(runId);
7690
+ }
7691
+ this.recorder?.onOperationReceiptDelete(key);
7692
+ count += 1;
7693
+ }
7694
+ }
7695
+ }
7696
+ return count;
7697
+ }
7579
7698
  static fromState(state) {
7580
7699
  const store = new _InMemoryStore({ incarnation: state.incarnation });
7581
7700
  for (const agent of state.agents) store.agents.set(agent.id, clone(agent));
@@ -7593,6 +7712,7 @@ var InMemoryStore = class _InMemoryStore {
7593
7712
  clonedRun.updatedAt = createTimestamp();
7594
7713
  store.runs.set(clonedRun.id, clonedRun);
7595
7714
  store.appendTransition(clonedRun, "input_required", "cancelled", { reason: clonedRun.cancellationReason });
7715
+ store.dropRunOperationReceipts(clonedRun.id);
7596
7716
  } else {
7597
7717
  store.runs.set(clonedRun.id, clonedRun);
7598
7718
  }
@@ -7601,7 +7721,18 @@ var InMemoryStore = class _InMemoryStore {
7601
7721
  for (const toolCall of state.toolCalls ?? []) store.toolCalls.set(toolCall.id, clone(toolCall));
7602
7722
  store.events.push(...clone(state.events));
7603
7723
  for (const [key, receipt] of state.idempotency) store.idempotency.set(key, clone(receipt));
7604
- for (const [key, receipt] of state.operationReceipts) store.operationReceipts.set(key, clone(receipt));
7724
+ for (const [key, receipt] of state.operationReceipts) {
7725
+ store.operationReceipts.set(key, clone(receipt));
7726
+ const runId = store.inferRunIdForReceipt(key, receipt.payload);
7727
+ if (runId) {
7728
+ let keys = store.runReceiptKeys.get(runId);
7729
+ if (!keys) {
7730
+ keys = /* @__PURE__ */ new Set();
7731
+ store.runReceiptKeys.set(runId, keys);
7732
+ }
7733
+ keys.add(key);
7734
+ }
7735
+ }
7605
7736
  for (const [agentId, messages] of state.historySeeds ?? []) store.historySeeds.set(agentId, clone(messages));
7606
7737
  for (const [consumerId, cursor] of state.consumerCheckpoints ?? []) store.consumerCheckpoints.set(consumerId, cursor);
7607
7738
  for (const [agentId, sequence] of state.nextAgentSequence) store.nextAgentSequence.set(agentId, sequence);
@@ -7634,6 +7765,16 @@ var InMemoryStore = class _InMemoryStore {
7634
7765
  contextCompactions: [...this.contextCompactions.entries()]
7635
7766
  });
7636
7767
  }
7768
+ exportMetadata() {
7769
+ return {
7770
+ incarnation: this.incarnation,
7771
+ eventSequence: this.eventSequence,
7772
+ nextAgentSequence: [...this.nextAgentSequence.entries()],
7773
+ nextReadySequence: [...this.nextReadySequence.entries()],
7774
+ historySeeds: [...this.historySeeds.entries()],
7775
+ contextCompactions: [...this.contextCompactions.entries()]
7776
+ };
7777
+ }
7637
7778
  attachOwner(lease) {
7638
7779
  this.owner = clone(lease);
7639
7780
  this.ownerEpoch = Math.max(this.ownerEpoch, lease.epoch);
@@ -7656,6 +7797,7 @@ var InMemoryStore = class _InMemoryStore {
7656
7797
  run.revision += 1;
7657
7798
  run.updatedAt = createTimestamp();
7658
7799
  this.appendTransition(run, "running", "failed", { failure });
7800
+ this.dropRunOperationReceipts(run.id);
7659
7801
  }
7660
7802
  }
7661
7803
  async openOwner(input) {
@@ -7673,6 +7815,7 @@ var InMemoryStore = class _InMemoryStore {
7673
7815
  }
7674
7816
  if (this.owner) this.interruptOwner(this.owner.epoch);
7675
7817
  this.dropSettledClaimReceipts();
7818
+ this.compactTerminalReceipts();
7676
7819
  this.ownerEpoch += 1;
7677
7820
  this.owner = {
7678
7821
  ownerId: input.ownerId,
@@ -7719,6 +7862,7 @@ var InMemoryStore = class _InMemoryStore {
7719
7862
  updatedAt: timestamp
7720
7863
  };
7721
7864
  this.agents.set(agent.id, agent);
7865
+ this.touchAgent(agent);
7722
7866
  if (input.historySeed && input.historySeed.length > 0) this.historySeeds.set(agent.id, materializeHistorySeed(agent.id, input.historySeed));
7723
7867
  this.nextAgentSequence.set(agent.id, 0);
7724
7868
  this.nextReadySequence.set(agent.id, 0);
@@ -7732,6 +7876,7 @@ var InMemoryStore = class _InMemoryStore {
7732
7876
  if (configToken !== void 0) agent.currentConfigToken = configToken;
7733
7877
  if (configIdentity !== void 0) agent.currentConfigIdentity = configIdentity;
7734
7878
  agent.updatedAt = createTimestamp();
7879
+ this.touchAgent(agent);
7735
7880
  return clone(agent);
7736
7881
  }
7737
7882
  updateAgentConfigToken(lease, input) {
@@ -7744,6 +7889,7 @@ var InMemoryStore = class _InMemoryStore {
7744
7889
  agent.currentConfigToken = input.token;
7745
7890
  if (input.configIdentity !== void 0) agent.currentConfigIdentity = input.configIdentity;
7746
7891
  agent.updatedAt = createTimestamp();
7892
+ this.touchAgent(agent);
7747
7893
  this.writeReceipt(scope, payload, agent);
7748
7894
  return clone(agent);
7749
7895
  }
@@ -7765,24 +7911,48 @@ var InMemoryStore = class _InMemoryStore {
7765
7911
  }
7766
7912
  const runIds = new Set(runs.map((run) => run.id));
7767
7913
  this.agents.delete(agent.id);
7914
+ this.recorder?.onAgentDelete(agent.id);
7768
7915
  this.nextAgentSequence.delete(agent.id);
7769
7916
  this.nextReadySequence.delete(agent.id);
7770
7917
  this.historySeeds.delete(agent.id);
7771
- for (const run of runs) this.runs.delete(run.id);
7918
+ for (const run of runs) {
7919
+ this.runs.delete(run.id);
7920
+ this.runReceiptKeys.delete(run.id);
7921
+ this.recorder?.onRunDelete(run.id);
7922
+ }
7772
7923
  for (const [messageId, message] of this.messages) {
7773
- if (runIds.has(message.runId)) this.messages.delete(messageId);
7924
+ if (runIds.has(message.runId)) {
7925
+ this.messages.delete(messageId);
7926
+ this.recorder?.onMessageDelete(messageId);
7927
+ }
7774
7928
  }
7775
7929
  for (const [toolCallId, toolCall] of this.toolCalls) {
7776
- if (runIds.has(toolCall.runId)) this.toolCalls.delete(toolCallId);
7930
+ if (runIds.has(toolCall.runId)) {
7931
+ this.toolCalls.delete(toolCallId);
7932
+ this.recorder?.onToolCallDelete(toolCallId);
7933
+ }
7777
7934
  }
7935
+ const deletedEventIds = [];
7778
7936
  for (let index = this.events.length - 1; index >= 0; index -= 1) {
7779
- if (this.events[index].agentId === agent.id) this.events.splice(index, 1);
7937
+ if (this.events[index].agentId === agent.id) {
7938
+ deletedEventIds.push(this.events[index].id);
7939
+ this.events.splice(index, 1);
7940
+ }
7941
+ }
7942
+ if (deletedEventIds.length > 0) {
7943
+ this.recorder?.onEventsTruncate(deletedEventIds);
7780
7944
  }
7781
7945
  for (const [key] of this.idempotency) {
7782
- if (key.includes(String(agent.id)) || [...runIds].some((runId) => key.includes(String(runId)))) this.idempotency.delete(key);
7946
+ if (key.includes(String(agent.id)) || [...runIds].some((runId) => key.includes(String(runId)))) {
7947
+ this.idempotency.delete(key);
7948
+ this.recorder?.onIdempotencyDelete(key);
7949
+ }
7783
7950
  }
7784
7951
  for (const [key] of this.operationReceipts) {
7785
- if (key.includes(String(agent.id)) || [...runIds].some((runId) => key.includes(String(runId)))) this.operationReceipts.delete(key);
7952
+ if (key.includes(String(agent.id)) || [...runIds].some((runId) => key.includes(String(runId)))) {
7953
+ this.operationReceipts.delete(key);
7954
+ this.recorder?.onOperationReceiptDelete(key);
7955
+ }
7786
7956
  }
7787
7957
  }
7788
7958
  reviseMessage(lease, input) {
@@ -7890,14 +8060,19 @@ var InMemoryStore = class _InMemoryStore {
7890
8060
  run.revision += 1;
7891
8061
  run.updatedAt = timestamp;
7892
8062
  this.appendTransition(run, previousState, "cancelled", { reason: run.cancellationReason });
8063
+ this.dropRunOperationReceipts(run.id);
7893
8064
  } else {
7894
8065
  run.revision += 1;
7895
8066
  run.updatedAt = timestamp;
8067
+ this.touchRun(run);
7896
8068
  }
7897
8069
  }
7898
8070
  for (const [messageId, message] of this.messages) {
7899
8071
  if (!affectedRunSet.has(message.runId)) continue;
7900
- if (message.runId !== original.runId || message.sequenceWithinRun > original.sequenceWithinRun) this.messages.delete(messageId);
8072
+ if (message.runId !== original.runId || message.sequenceWithinRun > original.sequenceWithinRun) {
8073
+ this.messages.delete(messageId);
8074
+ this.recorder?.onMessageDelete(messageId);
8075
+ }
7901
8076
  }
7902
8077
  if (isSeedMessage) {
7903
8078
  const targetIndex = seed.findIndex(({ id }) => id === original.id);
@@ -7911,10 +8086,11 @@ var InMemoryStore = class _InMemoryStore {
7911
8086
  sequenceWithinRun: 0,
7912
8087
  createdAt: timestamp
7913
8088
  };
7914
- this.messages.set(revisedMessage.id, revisedMessage);
8089
+ this.storeMessage(revisedMessage);
7915
8090
  this.runs.set(replacementRun.id, replacementRun);
8091
+ this.touchRun(replacementRun);
7916
8092
  this.appendTransition(replacementRun, null, "queued");
7917
- this.events.push({
8093
+ this.appendEvent({
7918
8094
  ...this.baseEvent(replacementRun),
7919
8095
  kind: "message_revised",
7920
8096
  message: revisedMessage,
@@ -7930,7 +8106,7 @@ var InMemoryStore = class _InMemoryStore {
7930
8106
  affectedToolCallIds: affectedTools.map((toolCall) => toolCall.id),
7931
8107
  ...effectDigest === void 0 ? {} : { effectDigest }
7932
8108
  };
7933
- this.writeOperationReceipt(operationKey, operationPayload, result);
8109
+ this.writeOperationReceipt(operationKey, operationPayload, result, replacementRun.id);
7934
8110
  return clone(result);
7935
8111
  }
7936
8112
  commitInteractionRecord(run, interaction, status, answer, reply) {
@@ -7952,7 +8128,7 @@ var InMemoryStore = class _InMemoryStore {
7952
8128
  sequenceWithinRun: this.nextMessageSequence(run.id),
7953
8129
  createdAt: now
7954
8130
  };
7955
- this.messages.set(record.id, record);
8131
+ this.storeMessage(record);
7956
8132
  this.appendMessage(run, record);
7957
8133
  return record;
7958
8134
  }
@@ -8001,12 +8177,13 @@ var InMemoryStore = class _InMemoryStore {
8001
8177
  sequenceWithinRun: this.nextMessageSequence(waitingRun.id),
8002
8178
  createdAt: now
8003
8179
  };
8004
- this.messages.set(message.id, message);
8180
+ this.storeMessage(message);
8005
8181
  this.appendMessage(waitingRun, message);
8006
8182
  waitingRun.readySequence = this.nextReady(waitingRun.agentId);
8007
8183
  waitingRun.state = "queued";
8008
8184
  waitingRun.revision += 1;
8009
8185
  waitingRun.updatedAt = now;
8186
+ this.touchRun(waitingRun);
8010
8187
  this.appendTransition(waitingRun, "input_required", "queued");
8011
8188
  this.writeReceipt(scope, payload, waitingRun);
8012
8189
  return clone(waitingRun);
@@ -8028,6 +8205,7 @@ var InMemoryStore = class _InMemoryStore {
8028
8205
  updatedAt: timestamp
8029
8206
  };
8030
8207
  this.runs.set(run.id, run);
8208
+ this.touchRun(run);
8031
8209
  this.appendTransition(run, null, "queued");
8032
8210
  this.writeReceipt(scope, payload, run);
8033
8211
  return clone(run);
@@ -8070,7 +8248,7 @@ var InMemoryStore = class _InMemoryStore {
8070
8248
  sequenceWithinRun: this.nextMessageSequence(run.id),
8071
8249
  createdAt: createTimestamp()
8072
8250
  };
8073
- this.messages.set(message.id, message);
8251
+ this.storeMessage(message);
8074
8252
  committedMessages.push(message);
8075
8253
  }
8076
8254
  const execution = {
@@ -8082,10 +8260,11 @@ var InMemoryStore = class _InMemoryStore {
8082
8260
  run.execution = execution;
8083
8261
  run.revision += 1;
8084
8262
  run.updatedAt = createTimestamp();
8263
+ this.touchRun(run);
8085
8264
  for (const message of committedMessages) this.appendMessage(run, message);
8086
8265
  this.appendTransition(run, "queued", "running");
8087
8266
  const result = { run: clone(run), execution: clone(execution), history: this.activeHistory(run.agentId, run.agentSequence) };
8088
- this.writeOperationReceipt(operationKey, operationPayload, result);
8267
+ this.writeOperationReceipt(operationKey, operationPayload, result, run.id);
8089
8268
  return result;
8090
8269
  }
8091
8270
  failQueuedRun(lease, input) {
@@ -8101,9 +8280,11 @@ var InMemoryStore = class _InMemoryStore {
8101
8280
  run.failure = clone(input.failure);
8102
8281
  run.revision += 1;
8103
8282
  run.updatedAt = createTimestamp();
8283
+ this.touchRun(run);
8104
8284
  this.appendTransition(run, "queued", "failed", { failure: input.failure });
8285
+ this.dropRunOperationReceipts(run.id);
8105
8286
  const result = clone(run);
8106
- this.writeOperationReceipt(operationKey, operationPayload, result);
8287
+ this.writeOperationReceipt(operationKey, operationPayload, result, run.id);
8107
8288
  return result;
8108
8289
  }
8109
8290
  commitPhaseEntered(lease, input) {
@@ -8119,10 +8300,38 @@ var InMemoryStore = class _InMemoryStore {
8119
8300
  run.currentPhaseId = input.phaseId;
8120
8301
  run.revision += 1;
8121
8302
  run.updatedAt = createTimestamp();
8303
+ this.touchRun(run);
8122
8304
  const visit2 = this.events.filter((event) => event.runId === run.id && event.kind === "phase_entered").length + 1;
8123
- this.events.push(this.baseEvent(run, { kind: "phase_entered", executionId: input.execution.executionId, phaseId: input.phaseId, visit: visit2 }));
8305
+ this.appendEvent(this.baseEvent(run, { kind: "phase_entered", executionId: input.execution.executionId, phaseId: input.phaseId, visit: visit2 }));
8124
8306
  const result = clone(run);
8125
- this.writeOperationReceipt(operationKey, operationPayload, result);
8307
+ this.writeOperationReceipt(operationKey, operationPayload, result, run.id);
8308
+ return result;
8309
+ }
8310
+ /** Commit a parallel Phase's reply while its Run is still running. */
8311
+ commitPhaseOutput(lease, input) {
8312
+ this.assertOwner(lease);
8313
+ const operationKey = `phase_output:${input.message.id}`;
8314
+ const operationPayload = canonicalJson([input.runId, input.execution.executionId, input.expectedRevision, input.message]);
8315
+ const replay = this.replayOperation(operationKey, operationPayload);
8316
+ if (replay) return clone(replay);
8317
+ const run = this.requireRun(input.runId);
8318
+ this.assertExecution(run, input.execution, input.expectedRevision);
8319
+ this.assertState(run, ["running"]);
8320
+ if (this.messages.has(input.message.id)) throw new RuntimeError("run_state_conflict", { runId: run.id, expected: ["running"], actual: run.state });
8321
+ const message = {
8322
+ ...clone(input.message),
8323
+ agentId: run.agentId,
8324
+ runId: run.id,
8325
+ role: "assistant",
8326
+ sequenceWithinRun: this.nextMessageSequence(run.id)
8327
+ };
8328
+ this.storeMessage(message);
8329
+ run.revision += 1;
8330
+ run.updatedAt = createTimestamp();
8331
+ this.touchRun(run);
8332
+ this.appendMessage(run, message);
8333
+ const result = clone(run);
8334
+ this.writeOperationReceipt(operationKey, operationPayload, result, run.id);
8126
8335
  return result;
8127
8336
  }
8128
8337
  commitInputRequired(lease, input) {
@@ -8144,7 +8353,7 @@ var InMemoryStore = class _InMemoryStore {
8144
8353
  ...clone(input.prompt),
8145
8354
  sequenceWithinRun: this.nextMessageSequence(run.id)
8146
8355
  };
8147
- this.messages.set(prompt.id, prompt);
8356
+ this.storeMessage(prompt);
8148
8357
  }
8149
8358
  run.state = "input_required";
8150
8359
  run.checkpoint = clone(input.checkpoint);
@@ -8159,12 +8368,13 @@ var InMemoryStore = class _InMemoryStore {
8159
8368
  delete run.execution;
8160
8369
  run.revision += 1;
8161
8370
  run.updatedAt = createTimestamp();
8371
+ this.touchRun(run);
8162
8372
  if (prompt) {
8163
8373
  this.appendMessage(run, prompt);
8164
8374
  }
8165
8375
  this.appendTransition(run, "running", "input_required", { prompt, interactions, answers: run.interactionAnswers ?? {} });
8166
8376
  const result = clone(run);
8167
- this.writeOperationReceipt(operationKey, operationPayload, result);
8377
+ this.writeOperationReceipt(operationKey, operationPayload, result, run.id);
8168
8378
  return result;
8169
8379
  }
8170
8380
  answerInteraction(lease, input) {
@@ -8212,8 +8422,9 @@ var InMemoryStore = class _InMemoryStore {
8212
8422
  run.state = "queued";
8213
8423
  this.appendTransition(run, "input_required", "queued");
8214
8424
  }
8425
+ this.touchRun(run);
8215
8426
  const result = clone(run);
8216
- this.writeOperationReceipt(operationKey, operationPayload, result);
8427
+ this.writeOperationReceipt(operationKey, operationPayload, result, run.id);
8217
8428
  return result;
8218
8429
  }
8219
8430
  reserveToolCall(lease, input) {
@@ -8287,17 +8498,18 @@ var InMemoryStore = class _InMemoryStore {
8287
8498
  sequenceWithinRun: this.nextMessageSequence(run.id),
8288
8499
  createdAt: timestamp
8289
8500
  };
8290
- for (const toolCall of toolCalls) this.toolCalls.set(toolCall.id, toolCall);
8291
- this.messages.set(requestMessage.id, requestMessage);
8501
+ for (const toolCall of toolCalls) this.storeToolCall(toolCall);
8502
+ this.storeMessage(requestMessage);
8292
8503
  run.revision += 1;
8293
8504
  run.updatedAt = timestamp;
8505
+ this.touchRun(run);
8294
8506
  this.appendMessage(run, requestMessage);
8295
8507
  for (const toolCall of toolCalls) this.appendToolTransition(run, { from: null, to: "pending" }, toolCall);
8296
8508
  const result = {
8297
8509
  run: clone(run),
8298
8510
  toolCalls: toolCalls.map((toolCall) => clone(toolCall))
8299
8511
  };
8300
- this.writeOperationReceipt(operationKey, operationPayload, result);
8512
+ this.writeOperationReceipt(operationKey, operationPayload, result, run.id);
8301
8513
  return result;
8302
8514
  }
8303
8515
  startToolCall(lease, input) {
@@ -8312,13 +8524,14 @@ var InMemoryStore = class _InMemoryStore {
8312
8524
  if (toolCall.state !== "pending") throw new RuntimeError("run_state_conflict", { runId: run.id, expected: ["running"], actual: run.state });
8313
8525
  toolCall.state = "running";
8314
8526
  toolCall.executionId = input.execution.executionId;
8315
- toolCall.executionId = input.execution.executionId;
8316
8527
  toolCall.updatedAt = createTimestamp();
8528
+ this.storeToolCall(toolCall);
8317
8529
  run.revision += 1;
8318
8530
  run.updatedAt = toolCall.updatedAt;
8531
+ this.touchRun(run);
8319
8532
  this.appendToolTransition(run, { from: "pending", to: "running" }, toolCall);
8320
8533
  const result = { run: clone(run), toolCall: clone(toolCall) };
8321
- this.writeOperationReceipt(operationKey, operationPayload, result);
8534
+ this.writeOperationReceipt(operationKey, operationPayload, result, run.id);
8322
8535
  return result;
8323
8536
  }
8324
8537
  suspendToolCall(lease, input) {
@@ -8335,11 +8548,13 @@ var InMemoryStore = class _InMemoryStore {
8335
8548
  }
8336
8549
  toolCall.state = "pending";
8337
8550
  toolCall.updatedAt = createTimestamp();
8551
+ this.storeToolCall(toolCall);
8338
8552
  run.revision += 1;
8339
8553
  run.updatedAt = toolCall.updatedAt;
8554
+ this.touchRun(run);
8340
8555
  this.appendToolTransition(run, { from: "running", to: "pending" }, toolCall);
8341
8556
  const result = { run: clone(run), toolCall: clone(toolCall) };
8342
- this.writeOperationReceipt(operationKey, operationPayload, result);
8557
+ this.writeOperationReceipt(operationKey, operationPayload, result, run.id);
8343
8558
  return result;
8344
8559
  }
8345
8560
  commitToolResult(lease, input) {
@@ -8371,15 +8586,17 @@ var InMemoryStore = class _InMemoryStore {
8371
8586
  sequenceWithinRun: this.nextMessageSequence(run.id),
8372
8587
  createdAt: createTimestamp()
8373
8588
  };
8374
- this.messages.set(message.id, message);
8589
+ this.storeMessage(message);
8375
8590
  toolCall.state = input.state;
8376
8591
  toolCall.result = durableResult;
8377
8592
  toolCall.resultMessageId = resultMessageId;
8378
8593
  if (input.state === "indeterminate") toolCall.reason = input.reason;
8379
8594
  else delete toolCall.reason;
8380
8595
  toolCall.updatedAt = message.createdAt;
8596
+ this.storeToolCall(toolCall);
8381
8597
  run.revision += 1;
8382
8598
  run.updatedAt = message.createdAt;
8599
+ this.touchRun(run);
8383
8600
  this.appendToolTransition(run, { from, to: input.state }, toolCall);
8384
8601
  this.appendMessage(run, message);
8385
8602
  if (input.state === "indeterminate") {
@@ -8400,10 +8617,12 @@ var InMemoryStore = class _InMemoryStore {
8400
8617
  delete run.execution;
8401
8618
  run.revision += 1;
8402
8619
  run.updatedAt = createTimestamp();
8620
+ this.touchRun(run);
8403
8621
  this.appendTransition(run, "running", "failed", { failure });
8622
+ this.dropRunOperationReceipts(run.id);
8404
8623
  }
8405
8624
  const result = { run: clone(run), toolCall: clone(toolCall) };
8406
- this.writeOperationReceipt(operationKey, operationPayload, result);
8625
+ this.writeOperationReceipt(operationKey, operationPayload, result, run.id);
8407
8626
  return result;
8408
8627
  }
8409
8628
  commitOutcome(lease, input) {
@@ -8421,7 +8640,7 @@ var InMemoryStore = class _InMemoryStore {
8421
8640
  if (nextState === "failed" && !input.failure) throw new TypeError("failed Run requires a failure");
8422
8641
  const output = input.output ? { ...clone(input.output), sequenceWithinRun: this.nextMessageSequence(run.id) } : void 0;
8423
8642
  if (output) {
8424
- this.messages.set(output.id, output);
8643
+ this.storeMessage(output);
8425
8644
  this.appendMessage(run, output);
8426
8645
  }
8427
8646
  run.state = nextState;
@@ -8433,9 +8652,10 @@ var InMemoryStore = class _InMemoryStore {
8433
8652
  delete run.interactionAnswers;
8434
8653
  run.revision += 1;
8435
8654
  run.updatedAt = createTimestamp();
8655
+ this.touchRun(run);
8436
8656
  this.appendTransition(run, "running", nextState, { outcome: input.outcome, failure: input.failure, output });
8657
+ this.dropRunOperationReceipts(run.id);
8437
8658
  const result = clone(run);
8438
- this.writeOperationReceipt(operationKey, operationPayload, result);
8439
8659
  return result;
8440
8660
  }
8441
8661
  cancelRun(lease, input) {
@@ -8455,7 +8675,6 @@ var InMemoryStore = class _InMemoryStore {
8455
8675
  }
8456
8676
  if (["completed", "failed", "cancelled"].includes(run.state)) {
8457
8677
  const result2 = clone(run);
8458
- if (input.expectedRevision !== void 0) this.writeOperationReceipt(operationKey, operationPayload, result2);
8459
8678
  return result2;
8460
8679
  }
8461
8680
  const from = run.state;
@@ -8470,7 +8689,7 @@ var InMemoryStore = class _InMemoryStore {
8470
8689
  for (const toolCall of activeToolCalls) this.interruptToolCall(run, toolCall, input.reason ?? "The Run was cancelled.");
8471
8690
  if (input.output && (typeof input.output.content === "string" ? input.output.content.length > 0 : input.output.content.length > 0)) {
8472
8691
  const output = { ...clone(input.output), sequenceWithinRun: this.nextMessageSequence(run.id) };
8473
- this.messages.set(output.id, output);
8692
+ this.storeMessage(output);
8474
8693
  this.appendMessage(run, output);
8475
8694
  }
8476
8695
  if (run.openInteractions && run.openInteractions.length > 0) {
@@ -8493,9 +8712,10 @@ var InMemoryStore = class _InMemoryStore {
8493
8712
  delete run.interactionAnswers;
8494
8713
  run.revision += 1;
8495
8714
  run.updatedAt = createTimestamp();
8715
+ this.touchRun(run);
8496
8716
  this.appendTransition(run, from, "failed", { failure });
8717
+ this.dropRunOperationReceipts(run.id);
8497
8718
  const result2 = clone(run);
8498
- if (input.expectedRevision !== void 0) this.writeOperationReceipt(operationKey, operationPayload, result2);
8499
8719
  return result2;
8500
8720
  }
8501
8721
  run.state = "cancelled";
@@ -8506,9 +8726,10 @@ var InMemoryStore = class _InMemoryStore {
8506
8726
  delete run.interactionAnswers;
8507
8727
  run.revision += 1;
8508
8728
  run.updatedAt = createTimestamp();
8729
+ this.touchRun(run);
8509
8730
  this.appendTransition(run, from, "cancelled", { reason: input.reason });
8731
+ this.dropRunOperationReceipts(run.id);
8510
8732
  const result = clone(run);
8511
- if (input.expectedRevision !== void 0) this.writeOperationReceipt(operationKey, operationPayload, result);
8512
8733
  return result;
8513
8734
  }
8514
8735
  snapshotRun(lease, runId) {
@@ -8589,6 +8810,7 @@ var InMemoryStore = class _InMemoryStore {
8589
8810
  const previous = this.consumerCheckpoints.get(input.consumerId);
8590
8811
  if (previous !== void 0 && next <= this.parseCursor(previous)) return;
8591
8812
  this.consumerCheckpoints.set(input.consumerId, input.cursor);
8813
+ this.recorder?.onConsumerCheckpointUpsert(input.consumerId, input.cursor);
8592
8814
  }
8593
8815
  compact(lease, input = {}) {
8594
8816
  this.assertOwner(lease);
@@ -8622,11 +8844,15 @@ var InMemoryStore = class _InMemoryStore {
8622
8844
  }
8623
8845
  const deletedEvents = deleteThrough >= this.retentionFloor ? this.events.filter((event) => this.parseCursor(event.cursor) <= deleteThrough) : [];
8624
8846
  if (deleteThrough >= this.retentionFloor) {
8847
+ const deletedEventIds = this.events.filter((event) => this.parseCursor(event.cursor) <= deleteThrough).map((event) => event.id);
8625
8848
  this.events.splice(0, this.events.length, ...this.events.filter((event) => this.parseCursor(event.cursor) > deleteThrough));
8626
8849
  this.retentionFloor = deleteThrough + 1;
8850
+ this.recorder?.onRetentionFloorChange(this.retentionFloor);
8851
+ this.recorder?.onEventsTruncate(deletedEventIds);
8627
8852
  for (const [consumerId, cursor] of this.consumerCheckpoints) {
8628
8853
  if (this.parseCursor(cursor) < this.retentionFloor - 1) {
8629
8854
  this.consumerCheckpoints.delete(consumerId);
8855
+ this.recorder?.onConsumerCheckpointDelete(consumerId);
8630
8856
  }
8631
8857
  }
8632
8858
  }
@@ -8635,11 +8861,21 @@ var InMemoryStore = class _InMemoryStore {
8635
8861
  const deletedRunIds = deletableRuns.map((run) => run.id);
8636
8862
  const deletedRunSet = new Set(deletedRunIds);
8637
8863
  const deletedTools = [...this.toolCalls.values()].filter((tool) => deletedRunSet.has(tool.runId));
8638
- for (const runId of deletedRunIds) this.runs.delete(runId);
8864
+ for (const runId of deletedRunIds) {
8865
+ this.runs.delete(runId);
8866
+ this.runReceiptKeys.delete(runId);
8867
+ this.recorder?.onRunDelete(runId);
8868
+ }
8639
8869
  for (const [messageId, message] of this.messages) {
8640
- if (deletedRunSet.has(message.runId)) this.messages.delete(messageId);
8870
+ if (deletedRunSet.has(message.runId)) {
8871
+ this.messages.delete(messageId);
8872
+ this.recorder?.onMessageDelete(messageId);
8873
+ }
8874
+ }
8875
+ for (const tool of deletedTools) {
8876
+ this.toolCalls.delete(tool.id);
8877
+ this.recorder?.onToolCallDelete(tool.id);
8641
8878
  }
8642
- for (const tool of deletedTools) this.toolCalls.delete(tool.id);
8643
8879
  return {
8644
8880
  deletedRunIds,
8645
8881
  deletedToolCallIds: deletedTools.map((tool) => tool.id),
@@ -8661,7 +8897,10 @@ var InMemoryStore = class _InMemoryStore {
8661
8897
  return receipt.result;
8662
8898
  }
8663
8899
  writeReceipt(scope, payload, result) {
8664
- this.idempotency.set(encodeIdempotencyScope(this.incarnation, scope), { payload, result: clone(result) });
8900
+ const key = encodeIdempotencyScope(this.incarnation, scope);
8901
+ const receipt = { payload, result: clone(result) };
8902
+ this.idempotency.set(key, receipt);
8903
+ this.recorder?.onIdempotencyUpsert(key, receipt);
8665
8904
  }
8666
8905
  replayOperation(key, payload) {
8667
8906
  const receipt = this.operationReceipts.get(key);
@@ -8669,8 +8908,19 @@ var InMemoryStore = class _InMemoryStore {
8669
8908
  if (receipt.payload !== payload) throw new Error(`Idempotency payload conflict for ${key}.`);
8670
8909
  return receipt.result;
8671
8910
  }
8672
- writeOperationReceipt(key, payload, result) {
8673
- this.operationReceipts.set(key, { payload, result: clone(result) });
8911
+ writeOperationReceipt(key, payload, result, runId) {
8912
+ const receipt = { payload, result: clone(result) };
8913
+ this.operationReceipts.set(key, receipt);
8914
+ const resolvedRunId = runId ?? this.inferRunIdForReceipt(key, payload);
8915
+ if (resolvedRunId) {
8916
+ let keys = this.runReceiptKeys.get(resolvedRunId);
8917
+ if (!keys) {
8918
+ keys = /* @__PURE__ */ new Set();
8919
+ this.runReceiptKeys.set(resolvedRunId, keys);
8920
+ }
8921
+ keys.add(key);
8922
+ }
8923
+ this.recorder?.onOperationReceiptUpsert(key, receipt);
8674
8924
  }
8675
8925
  /**
8676
8926
  * A Claim receipt exists for idempotent replay of one live Execution Attempt
@@ -8678,7 +8928,11 @@ var InMemoryStore = class _InMemoryStore {
8678
8928
  * one history snapshot per Agent instead of one per Attempt.
8679
8929
  */
8680
8930
  dropClaimReceipt(execution) {
8681
- if (execution) this.operationReceipts.delete(`claim:${execution.executionId}`);
8931
+ if (execution) {
8932
+ const key = `claim:${execution.executionId}`;
8933
+ this.operationReceipts.delete(key);
8934
+ this.recorder?.onOperationReceiptDelete(key);
8935
+ }
8682
8936
  }
8683
8937
  /**
8684
8938
  * A new owner fences the Execution Attempts of the previous one, so no Claim
@@ -8694,6 +8948,7 @@ var InMemoryStore = class _InMemoryStore {
8694
8948
  for (const key of [...this.operationReceipts.keys()]) {
8695
8949
  if (key.startsWith("claim:") && !liveExecutions.has(key.slice("claim:".length))) {
8696
8950
  this.operationReceipts.delete(key);
8951
+ this.recorder?.onOperationReceiptDelete(key);
8697
8952
  }
8698
8953
  }
8699
8954
  }
@@ -8732,14 +8987,16 @@ var InMemoryStore = class _InMemoryStore {
8732
8987
  };
8733
8988
  const from = toolCall.state;
8734
8989
  const to = from === "running" ? "indeterminate" : "failed";
8735
- this.messages.set(message.id, message);
8990
+ this.storeMessage(message);
8736
8991
  toolCall.state = to;
8737
8992
  toolCall.result = durableResult;
8738
8993
  toolCall.resultMessageId = message.id;
8739
8994
  if (to === "indeterminate") toolCall.reason = reason;
8740
8995
  toolCall.updatedAt = message.createdAt;
8996
+ this.storeToolCall(toolCall);
8741
8997
  run.revision += 1;
8742
8998
  run.updatedAt = message.createdAt;
8999
+ this.touchRun(run);
8743
9000
  this.appendToolTransition(run, { from, to }, toolCall);
8744
9001
  this.appendMessage(run, message);
8745
9002
  }
@@ -8840,6 +9097,7 @@ ${record.summary}`,
8840
9097
  if (existing?.id === record.id) return clone(existing);
8841
9098
  const next = clone(record);
8842
9099
  this.contextCompactions.set(record.agentId, next);
9100
+ this.recorder?.onContextCompactionUpsert(record.agentId, next);
8843
9101
  return clone(next);
8844
9102
  }
8845
9103
  activeHistory(agentId, beforeSequence) {
@@ -8849,7 +9107,7 @@ ${record.summary}`,
8849
9107
  ].map((message) => ({ ...message, messageRevision: message.messageRevision ?? 0 })));
8850
9108
  }
8851
9109
  appendMessage(run, message) {
8852
- this.events.push(this.baseEvent(run, {
9110
+ this.appendEvent(this.baseEvent(run, {
8853
9111
  kind: "message_committed",
8854
9112
  message
8855
9113
  }));
@@ -8865,13 +9123,14 @@ ${record.summary}`,
8865
9123
  ...to === "failed" && options?.failure ? { failure: options.failure } : {},
8866
9124
  ...to === "cancelled" && options?.reason ? { reason: options.reason } : {}
8867
9125
  };
8868
- this.events.push(transition);
9126
+ this.touchRun(run);
9127
+ this.appendEvent(transition);
8869
9128
  }
8870
9129
  appendToolTransition(run, transition, toolCall) {
8871
- this.events.push(this.baseEvent(run, {
9130
+ this.appendEvent(this.baseEvent(run, {
8872
9131
  kind: "tool_state_changed",
8873
9132
  transition,
8874
- toolCall
9133
+ toolCall: clone(toolCall)
8875
9134
  }));
8876
9135
  }
8877
9136
  baseEvent(run, extra) {
@@ -8944,6 +9203,9 @@ var MemoryOwnedStore = class {
8944
9203
  async commitPhaseEntered(input) {
8945
9204
  return this.store.commitPhaseEntered(this.lease, input);
8946
9205
  }
9206
+ async commitPhaseOutput(input) {
9207
+ return this.store.commitPhaseOutput(this.lease, input);
9208
+ }
8947
9209
  async commitInputRequired(input) {
8948
9210
  return this.store.commitInputRequired(this.lease, input);
8949
9211
  }
@@ -9244,6 +9506,94 @@ var CURRENT_SCHEMA_SQL = `
9244
9506
  CREATE INDEX runs_active_idx ON runs (agent_id, state, agent_sequence);
9245
9507
  CREATE INDEX run_events_sequence_idx ON run_events (sequence);
9246
9508
  `;
9509
+ var ChangeTracker = class {
9510
+ upsertedAgents = /* @__PURE__ */ new Map();
9511
+ deletedAgentIds = /* @__PURE__ */ new Set();
9512
+ upsertedRuns = /* @__PURE__ */ new Map();
9513
+ deletedRunIds = /* @__PURE__ */ new Set();
9514
+ insertedMessages = /* @__PURE__ */ new Map();
9515
+ deletedMessageIds = /* @__PURE__ */ new Set();
9516
+ upsertedToolCalls = /* @__PURE__ */ new Map();
9517
+ deletedToolCallIds = /* @__PURE__ */ new Set();
9518
+ appendedEvents = [];
9519
+ truncatedEventIds = /* @__PURE__ */ new Set();
9520
+ upsertedIdempotency = /* @__PURE__ */ new Map();
9521
+ deletedIdempotencyScopes = /* @__PURE__ */ new Set();
9522
+ upsertedOperationReceipts = /* @__PURE__ */ new Map();
9523
+ deletedOperationReceiptKeys = /* @__PURE__ */ new Set();
9524
+ upsertedConsumerCheckpoints = /* @__PURE__ */ new Map();
9525
+ deletedConsumerCheckpoints = /* @__PURE__ */ new Set();
9526
+ upsertedContextCompactions = /* @__PURE__ */ new Map();
9527
+ retentionFloorChanged;
9528
+ onAgentUpsert(agent) {
9529
+ this.deletedAgentIds.delete(agent.id);
9530
+ this.upsertedAgents.set(agent.id, agent);
9531
+ }
9532
+ onAgentDelete(agentId) {
9533
+ this.upsertedAgents.delete(agentId);
9534
+ this.deletedAgentIds.add(agentId);
9535
+ }
9536
+ onRunUpsert(run) {
9537
+ this.deletedRunIds.delete(run.id);
9538
+ this.upsertedRuns.set(run.id, run);
9539
+ }
9540
+ onRunDelete(runId) {
9541
+ this.upsertedRuns.delete(runId);
9542
+ this.deletedRunIds.add(runId);
9543
+ }
9544
+ onMessageInsert(message) {
9545
+ this.deletedMessageIds.delete(message.id);
9546
+ this.insertedMessages.set(message.id, message);
9547
+ }
9548
+ onMessageDelete(messageId) {
9549
+ this.insertedMessages.delete(messageId);
9550
+ this.deletedMessageIds.add(messageId);
9551
+ }
9552
+ onToolCallUpsert(toolCall) {
9553
+ this.deletedToolCallIds.delete(toolCall.id);
9554
+ this.upsertedToolCalls.set(toolCall.id, toolCall);
9555
+ }
9556
+ onToolCallDelete(toolCallId) {
9557
+ this.upsertedToolCalls.delete(toolCallId);
9558
+ this.deletedToolCallIds.add(toolCallId);
9559
+ }
9560
+ onEventAppend(event) {
9561
+ this.appendedEvents.push(event);
9562
+ }
9563
+ onEventsTruncate(deletedEventIds) {
9564
+ for (const id of deletedEventIds) this.truncatedEventIds.add(id);
9565
+ }
9566
+ onIdempotencyUpsert(scope, receipt) {
9567
+ this.deletedIdempotencyScopes.delete(scope);
9568
+ this.upsertedIdempotency.set(scope, receipt);
9569
+ }
9570
+ onIdempotencyDelete(scope) {
9571
+ this.upsertedIdempotency.delete(scope);
9572
+ this.deletedIdempotencyScopes.add(scope);
9573
+ }
9574
+ onOperationReceiptUpsert(key, receipt) {
9575
+ this.deletedOperationReceiptKeys.delete(key);
9576
+ this.upsertedOperationReceipts.set(key, receipt);
9577
+ }
9578
+ onOperationReceiptDelete(key) {
9579
+ this.upsertedOperationReceipts.delete(key);
9580
+ this.deletedOperationReceiptKeys.add(key);
9581
+ }
9582
+ onConsumerCheckpointUpsert(consumerId, cursor) {
9583
+ this.deletedConsumerCheckpoints.delete(consumerId);
9584
+ this.upsertedConsumerCheckpoints.set(consumerId, cursor);
9585
+ }
9586
+ onConsumerCheckpointDelete(consumerId) {
9587
+ this.upsertedConsumerCheckpoints.delete(consumerId);
9588
+ this.deletedConsumerCheckpoints.add(consumerId);
9589
+ }
9590
+ onContextCompactionUpsert(agentId, record) {
9591
+ this.upsertedContextCompactions.set(agentId, record);
9592
+ }
9593
+ onRetentionFloorChange(floor) {
9594
+ this.retentionFloorChanged = floor;
9595
+ }
9596
+ };
9247
9597
  var SqliteStore = class {
9248
9598
  database;
9249
9599
  archiveRoot;
@@ -9252,6 +9602,25 @@ var SqliteStore = class {
9252
9602
  mirroredAgents = /* @__PURE__ */ new Set();
9253
9603
  closed = false;
9254
9604
  configured = false;
9605
+ cachedMemory;
9606
+ cachedOwner;
9607
+ isLegacyState = false;
9608
+ stmtUpsertAgent;
9609
+ stmtDeleteAgent;
9610
+ stmtUpsertRun;
9611
+ stmtDeleteRun;
9612
+ stmtInsertMessage;
9613
+ stmtDeleteMessage;
9614
+ stmtUpsertToolCall;
9615
+ stmtDeleteToolCall;
9616
+ stmtInsertEvent;
9617
+ stmtDeleteEvent;
9618
+ stmtUpsertIdempotency;
9619
+ stmtDeleteIdempotency;
9620
+ stmtUpsertCheckpoint;
9621
+ stmtDeleteCheckpoint;
9622
+ stmtUpdateMeta;
9623
+ stmtUpdateState;
9255
9624
  constructor(filename = ":memory:") {
9256
9625
  this.database = new Database(filename, { create: true, readwrite: true, strict: true });
9257
9626
  this.archiveRoot = filename === ":memory:" ? join6(tmpdir2(), `rowan-context-archives-${crypto.randomUUID()}`) : join6(dirname7(resolve8(filename)), "context-archives");
@@ -9272,7 +9641,26 @@ var SqliteStore = class {
9272
9641
  }
9273
9642
  const replay = ownerLease(row);
9274
9643
  this.writeOwner({ ...row, expires_at: now + input.leaseMs });
9275
- return { ...replay, expiresAt: new Date(now + input.leaseMs).toISOString() };
9644
+ const newLease2 = { ...replay, expiresAt: new Date(now + input.leaseMs).toISOString() };
9645
+ if (!this.cachedMemory || this.cachedOwner?.epoch !== replay.epoch) {
9646
+ const state2 = this.readState();
9647
+ const memory2 = InMemoryStore.fromState(state2);
9648
+ memory2.attachOwner(newLease2);
9649
+ const tracker2 = new ChangeTracker();
9650
+ memory2.setChangeRecorder(tracker2);
9651
+ try {
9652
+ memory2.dropSettledClaimReceipts();
9653
+ memory2.compactTerminalReceipts();
9654
+ } finally {
9655
+ memory2.setChangeRecorder(void 0);
9656
+ }
9657
+ this.ensureTableBackedStorage(memory2, tracker2, this.isLegacyState);
9658
+ this.cachedMemory = memory2;
9659
+ } else {
9660
+ this.cachedMemory.attachOwner(newLease2);
9661
+ }
9662
+ this.cachedOwner = newLease2;
9663
+ return newLease2;
9276
9664
  }
9277
9665
  const state = this.readState();
9278
9666
  const memory = InMemoryStore.fromState(state);
@@ -9291,22 +9679,42 @@ var SqliteStore = class {
9291
9679
  released_epoch: row.released_epoch
9292
9680
  };
9293
9681
  this.writeOwner(next);
9294
- memory.dropSettledClaimReceipts();
9295
- this.persistState(memory.exportState());
9296
- return {
9682
+ const newLease = {
9297
9683
  ownerId: input.ownerId,
9298
9684
  token,
9299
9685
  epoch,
9300
9686
  expiresAt: new Date(now + input.leaseMs).toISOString()
9301
9687
  };
9688
+ memory.attachOwner(newLease);
9689
+ const tracker = new ChangeTracker();
9690
+ memory.setChangeRecorder(tracker);
9691
+ try {
9692
+ memory.dropSettledClaimReceipts();
9693
+ memory.compactTerminalReceipts();
9694
+ } finally {
9695
+ memory.setChangeRecorder(void 0);
9696
+ }
9697
+ this.ensureTableBackedStorage(memory, tracker, this.isLegacyState);
9698
+ this.cachedMemory = memory;
9699
+ this.cachedOwner = newLease;
9700
+ return newLease;
9302
9701
  });
9303
9702
  return new SqliteOwnedStore(this, lease);
9304
9703
  }
9305
9704
  close() {
9306
9705
  if (this.closed) return;
9307
9706
  this.closed = true;
9707
+ this.cachedMemory = void 0;
9708
+ this.cachedOwner = void 0;
9308
9709
  this.database.close();
9309
9710
  }
9711
+ exportState() {
9712
+ if (this.cachedMemory) {
9713
+ return this.cachedMemory.exportState();
9714
+ }
9715
+ const state = this.readState();
9716
+ return InMemoryStore.fromState(state).exportState();
9717
+ }
9310
9718
  /** Filesystem location owned by this Store for one Agent's readable archive. */
9311
9719
  contextArchiveDir(agentId) {
9312
9720
  return join6(this.archiveRoot, String(agentId));
@@ -9347,6 +9755,9 @@ var SqliteStore = class {
9347
9755
  async commitPhaseEntered(lease, input) {
9348
9756
  return this.invoke(lease, (store, current) => store.commitPhaseEntered(current, input));
9349
9757
  }
9758
+ async commitPhaseOutput(lease, input) {
9759
+ return this.invoke(lease, (store, current) => store.commitPhaseOutput(current, input));
9760
+ }
9350
9761
  async commitInputRequired(lease, input) {
9351
9762
  return this.invoke(lease, (store, current) => store.commitInputRequired(current, input));
9352
9763
  }
@@ -9388,31 +9799,7 @@ var SqliteStore = class {
9388
9799
  }
9389
9800
  async listEvents(lease, input = {}) {
9390
9801
  this.assertOpen();
9391
- return this.readTransaction(() => {
9392
- this.requireMatchingOwner(lease, true);
9393
- const retentionRow = this.database.query(
9394
- "SELECT value FROM runtime_meta WHERE key = ?"
9395
- ).get(RETENTION_FLOOR_KEY);
9396
- const retentionFloor = Math.max(1, Number(retentionRow?.value ?? "1"));
9397
- const after = input.after ? parseEventCursor(lease, input.after) : retentionFloor - 1;
9398
- if (input.after && after < retentionFloor - 1) {
9399
- throw new RuntimeError("invalid_cursor", { cursorType: "event", reason: "expired" });
9400
- }
9401
- if (input.after) {
9402
- const latest = this.database.query(
9403
- "SELECT max(sequence) AS sequence FROM run_events"
9404
- ).get();
9405
- const waterline = latest.sequence ?? 0;
9406
- if (after > waterline) {
9407
- throw new RuntimeError("invalid_cursor", { cursorType: "event", reason: "beyond_waterline" });
9408
- }
9409
- if (after === waterline) return [];
9410
- }
9411
- const rows = this.database.query(
9412
- "SELECT payload_json FROM run_events ORDER BY sequence"
9413
- ).all();
9414
- return rows.map((row) => JSON.parse(row.payload_json)).filter((event) => parseEventCursor(lease, event.cursor) > after);
9415
- });
9802
+ return this.read(lease, (memory) => memory.listEvents(lease, input));
9416
9803
  }
9417
9804
  async openConsumer(lease, consumerId) {
9418
9805
  return this.invoke(lease, (store, current) => store.openConsumer(current, consumerId), false);
@@ -9436,7 +9823,12 @@ var SqliteStore = class {
9436
9823
  const row = this.requireMatchingOwner(lease, false);
9437
9824
  const expiresAt = Date.now() + leaseMs;
9438
9825
  this.writeOwner({ ...row, expires_at: expiresAt });
9439
- return { ...lease, expiresAt: new Date(expiresAt).toISOString() };
9826
+ const nextLease = { ...lease, expiresAt: new Date(expiresAt).toISOString() };
9827
+ if (this.cachedOwner && this.cachedOwner.token === lease.token) {
9828
+ this.cachedOwner = nextLease;
9829
+ this.cachedMemory?.attachOwner(nextLease);
9830
+ }
9831
+ return nextLease;
9440
9832
  });
9441
9833
  }
9442
9834
  async sealAndReleaseOwner(lease) {
@@ -9447,27 +9839,75 @@ var SqliteStore = class {
9447
9839
  if (row.owner_id !== lease.ownerId || row.owner_token !== lease.token || row.epoch !== lease.epoch) {
9448
9840
  throw ownershipLost2(lease, row);
9449
9841
  }
9450
- const memory = InMemoryStore.fromState(this.readState());
9451
- memory.attachOwner(lease);
9452
- memory.interruptOwner(lease.epoch, "The Runtime owner was sealed.");
9453
- this.persistState(memory.exportState());
9842
+ if (!this.cachedMemory || this.cachedOwner?.epoch !== lease.epoch) {
9843
+ this.cachedMemory = InMemoryStore.fromState(this.readState());
9844
+ this.cachedOwner = lease;
9845
+ }
9846
+ const tracker = new ChangeTracker();
9847
+ this.cachedMemory.setChangeRecorder(tracker);
9848
+ try {
9849
+ this.cachedMemory.attachOwner(lease);
9850
+ this.cachedMemory.interruptOwner(lease.epoch, "The Runtime owner was sealed.");
9851
+ } finally {
9852
+ this.cachedMemory.setChangeRecorder(void 0);
9853
+ }
9854
+ this.persistChanges(tracker, this.cachedMemory);
9855
+ this.mirrorChanges(tracker);
9454
9856
  this.writeOwner({ ...row, owner_id: null, owner_token: null, expires_at: null, released_epoch: lease.epoch });
9857
+ this.cachedMemory = void 0;
9858
+ this.cachedOwner = void 0;
9455
9859
  });
9456
9860
  }
9457
- invoke(lease, operation, persist = true) {
9861
+ read(lease, operation) {
9862
+ this.assertOpen();
9863
+ this.requireMatchingOwner(lease, true);
9864
+ if (!this.cachedMemory || this.cachedOwner?.epoch !== lease.epoch) {
9865
+ this.cachedMemory = InMemoryStore.fromState(this.readState());
9866
+ this.cachedMemory.attachOwner(lease);
9867
+ this.cachedOwner = lease;
9868
+ } else {
9869
+ this.cachedMemory.attachOwner(lease);
9870
+ }
9871
+ return operation(this.cachedMemory);
9872
+ }
9873
+ write(lease, operation) {
9458
9874
  this.assertOpen();
9459
9875
  return this.immediateTransaction(() => {
9460
9876
  this.requireMatchingOwner(lease, true);
9461
- const memory = InMemoryStore.fromState(this.readState());
9462
- memory.attachOwner(lease);
9463
- const result = operation(memory, lease);
9464
- if (persist) {
9465
- this.requireMatchingOwner(lease, true);
9466
- this.persistState(memory.exportState());
9877
+ if (!this.cachedMemory || this.cachedOwner?.epoch !== lease.epoch) {
9878
+ this.cachedMemory = InMemoryStore.fromState(this.readState());
9879
+ this.cachedMemory.attachOwner(lease);
9880
+ this.cachedOwner = lease;
9881
+ } else {
9882
+ this.cachedMemory.attachOwner(lease);
9883
+ }
9884
+ const tracker = new ChangeTracker();
9885
+ this.cachedMemory.setChangeRecorder(tracker);
9886
+ let result;
9887
+ try {
9888
+ result = operation(this.cachedMemory);
9889
+ } catch (error) {
9890
+ this.cachedMemory.setChangeRecorder(void 0);
9891
+ this.cachedMemory = void 0;
9892
+ throw error;
9893
+ }
9894
+ this.cachedMemory.setChangeRecorder(void 0);
9895
+ try {
9896
+ this.persistChanges(tracker, this.cachedMemory);
9897
+ this.mirrorChanges(tracker);
9898
+ } catch (error) {
9899
+ this.cachedMemory = void 0;
9900
+ throw error;
9467
9901
  }
9468
9902
  return result;
9469
9903
  });
9470
9904
  }
9905
+ invoke(lease, operation, persist = true) {
9906
+ if (persist) {
9907
+ return this.write(lease, (memory) => operation(memory, lease));
9908
+ }
9909
+ return this.read(lease, (memory) => operation(memory, lease));
9910
+ }
9471
9911
  ensureSchema() {
9472
9912
  const catalog = this.catalog();
9473
9913
  if (catalog.length > 0 && !this.isCurrentSchema(catalog)) {
@@ -9484,7 +9924,7 @@ var SqliteStore = class {
9484
9924
  this.database.run("INSERT INTO runtime_meta (key, value) VALUES (?, ?)", [RETENTION_FLOOR_KEY, "1"]);
9485
9925
  const state = new InMemoryStore();
9486
9926
  this.database.run("INSERT INTO runtime_owner (singleton, epoch) VALUES (?, ?)", [STATE_ROW, 0]);
9487
- this.database.run("INSERT INTO runtime_state (singleton, state_json) VALUES (?, ?)", [STATE_ROW, JSON.stringify(state.exportState())]);
9927
+ this.database.run("INSERT INTO runtime_state (singleton, state_json) VALUES (?, ?)", [STATE_ROW, JSON.stringify(state.exportMetadata())]);
9488
9928
  });
9489
9929
  return;
9490
9930
  }
@@ -9523,13 +9963,267 @@ var SqliteStore = class {
9523
9963
  readState() {
9524
9964
  const row = this.database.query("SELECT state_json FROM runtime_state WHERE singleton = ?").get(STATE_ROW);
9525
9965
  if (!row) throw new RuntimeError("store_unavailable", { operation: "read_state", retryable: false, reason: "runtime_state row is missing" });
9526
- return JSON.parse(row.state_json);
9966
+ const parsed = JSON.parse(row.state_json);
9967
+ if (parsed.agents && Array.isArray(parsed.agents)) {
9968
+ this.isLegacyState = true;
9969
+ return parsed;
9970
+ }
9971
+ this.isLegacyState = false;
9972
+ return this.loadTableBackedState(parsed);
9973
+ }
9974
+ loadTableBackedState(metadata) {
9975
+ const agentRows = this.database.query(
9976
+ "SELECT id, metadata_json, config_token, config_identity, created_at, activated_at, updated_at FROM agents ORDER BY created_at"
9977
+ ).all();
9978
+ const agents = agentRows.map((row) => ({
9979
+ id: row.id,
9980
+ ...row.metadata_json ? { metadata: JSON.parse(row.metadata_json) } : {},
9981
+ ...row.config_token ? { currentConfigToken: row.config_token } : {},
9982
+ ...row.config_identity ? { currentConfigIdentity: row.config_identity } : {},
9983
+ createdAt: row.created_at,
9984
+ ...row.activated_at ? { activatedAt: row.activated_at } : {},
9985
+ updatedAt: row.updated_at
9986
+ }));
9987
+ const runRows = this.database.query("SELECT payload_json FROM runs").all();
9988
+ const runs = runRows.map((r) => JSON.parse(r.payload_json));
9989
+ const messageRows = this.database.query("SELECT payload_json FROM messages ORDER BY run_id, sequence_within_run").all();
9990
+ const messages = messageRows.map((m) => JSON.parse(m.payload_json));
9991
+ const toolRows = this.database.query("SELECT payload_json FROM tool_calls").all();
9992
+ const toolCalls = toolRows.map((t) => JSON.parse(t.payload_json));
9993
+ const eventRows = this.database.query("SELECT payload_json FROM run_events ORDER BY sequence").all();
9994
+ const events = eventRows.map((e) => JSON.parse(e.payload_json));
9995
+ const idempotencyRows = this.database.query("SELECT scope, payload_json, result_json FROM idempotency").all();
9996
+ const idempotency = [];
9997
+ const operationReceipts = [];
9998
+ for (const row of idempotencyRows) {
9999
+ const receipt = { payload: row.payload_json, result: JSON.parse(row.result_json) };
10000
+ if (row.scope.startsWith("operation:")) {
10001
+ operationReceipts.push([row.scope.slice("operation:".length), receipt]);
10002
+ } else {
10003
+ idempotency.push([row.scope, receipt]);
10004
+ }
10005
+ }
10006
+ const cpRows = this.database.query("SELECT consumer_id, sequence FROM consumer_checkpoints").all();
10007
+ const consumerCheckpoints = cpRows.map((r) => [r.consumer_id, `${metadata.incarnation}:${r.sequence}`]);
10008
+ const retentionRow = this.database.query("SELECT value FROM runtime_meta WHERE key = ?").get(RETENTION_FLOOR_KEY);
10009
+ const retentionFloor = Math.max(1, Number(retentionRow?.value ?? metadata.retentionFloor ?? "1"));
10010
+ return {
10011
+ incarnation: metadata.incarnation,
10012
+ agents,
10013
+ runs,
10014
+ messages,
10015
+ toolCalls,
10016
+ events,
10017
+ idempotency,
10018
+ operationReceipts,
10019
+ historySeeds: metadata.historySeeds ?? [],
10020
+ consumerCheckpoints,
10021
+ retentionFloor,
10022
+ nextAgentSequence: metadata.nextAgentSequence ?? [],
10023
+ nextReadySequence: metadata.nextReadySequence ?? [],
10024
+ eventSequence: metadata.eventSequence ?? 0,
10025
+ contextCompactions: metadata.contextCompactions ?? []
10026
+ };
10027
+ }
10028
+ ensureTableBackedStorage(memory, tracker, isLegacy) {
10029
+ if (isLegacy) {
10030
+ this.writeFullStateToTables(memory);
10031
+ this.isLegacyState = false;
10032
+ } else {
10033
+ this.persistChanges(tracker, memory);
10034
+ this.mirrorChanges(tracker);
10035
+ }
10036
+ }
10037
+ getStmtUpsertAgent() {
10038
+ return this.stmtUpsertAgent ??= this.database.prepare(
10039
+ `INSERT INTO agents (id, metadata_json, config_token, config_identity, created_at, activated_at, updated_at)
10040
+ VALUES (?, ?, ?, ?, ?, ?, ?)
10041
+ ON CONFLICT(id) DO UPDATE SET
10042
+ metadata_json = excluded.metadata_json,
10043
+ config_token = excluded.config_token,
10044
+ config_identity = excluded.config_identity,
10045
+ activated_at = excluded.activated_at,
10046
+ updated_at = excluded.updated_at`
10047
+ );
10048
+ }
10049
+ getStmtDeleteAgent() {
10050
+ return this.stmtDeleteAgent ??= this.database.prepare("DELETE FROM agents WHERE id = ?");
10051
+ }
10052
+ getStmtUpsertRun() {
10053
+ return this.stmtUpsertRun ??= this.database.prepare(
10054
+ `INSERT INTO runs (id, agent_id, agent_sequence, state, revision, payload_json, created_at, updated_at)
10055
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
10056
+ ON CONFLICT(id) DO UPDATE SET
10057
+ state = excluded.state,
10058
+ revision = excluded.revision,
10059
+ payload_json = excluded.payload_json,
10060
+ updated_at = excluded.updated_at`
10061
+ );
10062
+ }
10063
+ getStmtDeleteRun() {
10064
+ return this.stmtDeleteRun ??= this.database.prepare("DELETE FROM runs WHERE id = ?");
10065
+ }
10066
+ getStmtInsertMessage() {
10067
+ return this.stmtInsertMessage ??= this.database.prepare(
10068
+ `INSERT INTO messages (id, run_id, sequence_within_run, payload_json, created_at)
10069
+ VALUES (?, ?, ?, ?, ?)
10070
+ ON CONFLICT(id) DO UPDATE SET payload_json = excluded.payload_json`
10071
+ );
10072
+ }
10073
+ getStmtDeleteMessage() {
10074
+ return this.stmtDeleteMessage ??= this.database.prepare("DELETE FROM messages WHERE id = ?");
10075
+ }
10076
+ getStmtUpsertToolCall() {
10077
+ return this.stmtUpsertToolCall ??= this.database.prepare(
10078
+ `INSERT INTO tool_calls (id, run_id, execution_id, state, payload_json)
10079
+ VALUES (?, ?, ?, ?, ?)
10080
+ ON CONFLICT(id) DO UPDATE SET state = excluded.state, payload_json = excluded.payload_json`
10081
+ );
10082
+ }
10083
+ getStmtDeleteToolCall() {
10084
+ return this.stmtDeleteToolCall ??= this.database.prepare("DELETE FROM tool_calls WHERE id = ?");
10085
+ }
10086
+ getStmtInsertEvent() {
10087
+ return this.stmtInsertEvent ??= this.database.prepare(
10088
+ "INSERT INTO run_events (id, run_id, payload_json, created_at) VALUES (?, ?, ?, ?)"
10089
+ );
10090
+ }
10091
+ getStmtDeleteEvent() {
10092
+ return this.stmtDeleteEvent ??= this.database.prepare("DELETE FROM run_events WHERE id = ?");
10093
+ }
10094
+ getStmtUpsertIdempotency() {
10095
+ return this.stmtUpsertIdempotency ??= this.database.prepare(
10096
+ `INSERT INTO idempotency (scope, payload_json, result_json)
10097
+ VALUES (?, ?, ?)
10098
+ ON CONFLICT(scope) DO UPDATE SET payload_json = excluded.payload_json, result_json = excluded.result_json`
10099
+ );
9527
10100
  }
9528
- writeState(state) {
9529
- this.database.run("UPDATE runtime_state SET state_json = ? WHERE singleton = ?", [JSON.stringify(state), STATE_ROW]);
10101
+ getStmtDeleteIdempotency() {
10102
+ return this.stmtDeleteIdempotency ??= this.database.prepare("DELETE FROM idempotency WHERE scope = ?");
9530
10103
  }
9531
- persistState(state) {
9532
- this.writeState(state);
10104
+ getStmtUpsertCheckpoint() {
10105
+ return this.stmtUpsertCheckpoint ??= this.database.prepare(
10106
+ `INSERT INTO consumer_checkpoints (consumer_id, sequence, event_id, updated_at)
10107
+ VALUES (?, ?, ?, ?)
10108
+ ON CONFLICT(consumer_id) DO UPDATE SET sequence = excluded.sequence, updated_at = excluded.updated_at`
10109
+ );
10110
+ }
10111
+ getStmtDeleteCheckpoint() {
10112
+ return this.stmtDeleteCheckpoint ??= this.database.prepare("DELETE FROM consumer_checkpoints WHERE consumer_id = ?");
10113
+ }
10114
+ getStmtUpdateMeta() {
10115
+ return this.stmtUpdateMeta ??= this.database.prepare(
10116
+ "INSERT INTO runtime_meta (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value"
10117
+ );
10118
+ }
10119
+ getStmtUpdateState() {
10120
+ return this.stmtUpdateState ??= this.database.prepare(
10121
+ "UPDATE runtime_state SET state_json = ? WHERE singleton = ?"
10122
+ );
10123
+ }
10124
+ persistChanges(tracker, memory) {
10125
+ for (const agent of tracker.upsertedAgents.values()) {
10126
+ this.getStmtUpsertAgent().run(
10127
+ agent.id,
10128
+ agent.metadata === void 0 ? null : JSON.stringify(agent.metadata),
10129
+ agent.currentConfigToken ?? null,
10130
+ agent.currentConfigIdentity ?? null,
10131
+ agent.createdAt,
10132
+ agent.activatedAt ?? null,
10133
+ agent.updatedAt
10134
+ );
10135
+ }
10136
+ for (const agentId of tracker.deletedAgentIds) {
10137
+ this.getStmtDeleteAgent().run(agentId);
10138
+ }
10139
+ for (const run of tracker.upsertedRuns.values()) {
10140
+ this.getStmtUpsertRun().run(
10141
+ run.id,
10142
+ run.agentId,
10143
+ run.agentSequence,
10144
+ run.state,
10145
+ run.revision,
10146
+ JSON.stringify(run),
10147
+ run.createdAt,
10148
+ run.updatedAt
10149
+ );
10150
+ }
10151
+ for (const runId of tracker.deletedRunIds) {
10152
+ this.getStmtDeleteRun().run(runId);
10153
+ }
10154
+ for (const message of tracker.insertedMessages.values()) {
10155
+ this.getStmtInsertMessage().run(
10156
+ message.id,
10157
+ message.runId,
10158
+ message.sequenceWithinRun,
10159
+ JSON.stringify(message),
10160
+ message.createdAt
10161
+ );
10162
+ }
10163
+ for (const messageId of tracker.deletedMessageIds) {
10164
+ this.getStmtDeleteMessage().run(messageId);
10165
+ }
10166
+ for (const toolCall of tracker.upsertedToolCalls.values()) {
10167
+ this.getStmtUpsertToolCall().run(
10168
+ toolCall.id,
10169
+ toolCall.runId,
10170
+ toolCall.executionId,
10171
+ toolCall.state,
10172
+ JSON.stringify(toolCall)
10173
+ );
10174
+ }
10175
+ for (const toolCallId of tracker.deletedToolCallIds) {
10176
+ this.getStmtDeleteToolCall().run(toolCallId);
10177
+ }
10178
+ for (const event of tracker.appendedEvents) {
10179
+ this.getStmtInsertEvent().run(
10180
+ event.id,
10181
+ event.runId,
10182
+ JSON.stringify(event),
10183
+ event.createdAt
10184
+ );
10185
+ }
10186
+ for (const eventId of tracker.truncatedEventIds) {
10187
+ this.getStmtDeleteEvent().run(eventId);
10188
+ }
10189
+ for (const [scope, receipt] of tracker.upsertedIdempotency) {
10190
+ this.getStmtUpsertIdempotency().run(
10191
+ scope,
10192
+ receipt.payload,
10193
+ JSON.stringify(receipt.result)
10194
+ );
10195
+ }
10196
+ for (const scope of tracker.deletedIdempotencyScopes) {
10197
+ this.getStmtDeleteIdempotency().run(scope);
10198
+ }
10199
+ for (const [key, receipt] of tracker.upsertedOperationReceipts) {
10200
+ this.getStmtUpsertIdempotency().run(
10201
+ `operation:${key}`,
10202
+ receipt.payload,
10203
+ JSON.stringify(receipt.result)
10204
+ );
10205
+ }
10206
+ for (const key of tracker.deletedOperationReceiptKeys) {
10207
+ this.getStmtDeleteIdempotency().run(`operation:${key}`);
10208
+ }
10209
+ for (const [consumerId, cursor] of tracker.upsertedConsumerCheckpoints) {
10210
+ this.getStmtUpsertCheckpoint().run(
10211
+ consumerId,
10212
+ Number(String(cursor).split(":").at(-1)),
10213
+ null,
10214
+ (/* @__PURE__ */ new Date()).toISOString()
10215
+ );
10216
+ }
10217
+ for (const consumerId of tracker.deletedConsumerCheckpoints) {
10218
+ this.getStmtDeleteCheckpoint().run(consumerId);
10219
+ }
10220
+ if (tracker.retentionFloorChanged !== void 0) {
10221
+ this.getStmtUpdateMeta().run(RETENTION_FLOOR_KEY, String(tracker.retentionFloorChanged));
10222
+ }
10223
+ this.getStmtUpdateState().run(JSON.stringify(memory.exportMetadata()), STATE_ROW);
10224
+ }
10225
+ writeFullStateToTables(memory) {
10226
+ const state = memory.exportState();
9533
10227
  this.database.run(
9534
10228
  "INSERT INTO runtime_meta (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value",
9535
10229
  [RETENTION_FLOOR_KEY, String(Math.max(1, state.retentionFloor ?? 1))]
@@ -9538,57 +10232,149 @@ var SqliteStore = class {
9538
10232
  this.database.run(`DELETE FROM ${table}`);
9539
10233
  }
9540
10234
  for (const agent of state.agents) {
9541
- this.database.run(
9542
- "INSERT INTO agents (id, metadata_json, config_token, config_identity, created_at, activated_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?)",
9543
- [agent.id, agent.metadata === void 0 ? null : JSON.stringify(agent.metadata), agent.currentConfigToken ?? null, agent.currentConfigIdentity ?? null, agent.createdAt, agent.activatedAt ?? null, agent.updatedAt]
10235
+ this.getStmtUpsertAgent().run(
10236
+ agent.id,
10237
+ agent.metadata === void 0 ? null : JSON.stringify(agent.metadata),
10238
+ agent.currentConfigToken ?? null,
10239
+ agent.currentConfigIdentity ?? null,
10240
+ agent.createdAt,
10241
+ agent.activatedAt ?? null,
10242
+ agent.updatedAt
9544
10243
  );
9545
10244
  }
9546
10245
  for (const run of state.runs) {
9547
- this.database.run(
9548
- "INSERT INTO runs (id, agent_id, agent_sequence, state, revision, payload_json, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
9549
- [run.id, run.agentId, run.agentSequence, run.state, run.revision, JSON.stringify(run), run.createdAt, run.updatedAt]
10246
+ this.getStmtUpsertRun().run(
10247
+ run.id,
10248
+ run.agentId,
10249
+ run.agentSequence,
10250
+ run.state,
10251
+ run.revision,
10252
+ JSON.stringify(run),
10253
+ run.createdAt,
10254
+ run.updatedAt
9550
10255
  );
9551
10256
  }
9552
10257
  for (const message of state.messages) {
9553
- this.database.run(
9554
- "INSERT INTO messages (id, run_id, sequence_within_run, payload_json, created_at) VALUES (?, ?, ?, ?, ?)",
9555
- [message.id, message.runId, message.sequenceWithinRun, JSON.stringify(message), message.createdAt]
10258
+ this.getStmtInsertMessage().run(
10259
+ message.id,
10260
+ message.runId,
10261
+ message.sequenceWithinRun,
10262
+ JSON.stringify(message),
10263
+ message.createdAt
9556
10264
  );
9557
10265
  }
9558
10266
  for (const toolCall of state.toolCalls) {
9559
- this.database.run(
9560
- "INSERT INTO tool_calls (id, run_id, execution_id, state, payload_json) VALUES (?, ?, ?, ?, ?)",
9561
- [toolCall.id, toolCall.runId, toolCall.executionId, toolCall.state, JSON.stringify(toolCall)]
10267
+ this.getStmtUpsertToolCall().run(
10268
+ toolCall.id,
10269
+ toolCall.runId,
10270
+ toolCall.executionId,
10271
+ toolCall.state,
10272
+ JSON.stringify(toolCall)
9562
10273
  );
9563
10274
  }
9564
10275
  for (const event of state.events) {
9565
- this.database.run(
9566
- "INSERT INTO run_events (id, run_id, payload_json, created_at) VALUES (?, ?, ?, ?)",
9567
- [event.id, event.runId, JSON.stringify(event), event.createdAt]
10276
+ this.getStmtInsertEvent().run(
10277
+ event.id,
10278
+ event.runId,
10279
+ JSON.stringify(event),
10280
+ event.createdAt
9568
10281
  );
9569
10282
  }
9570
10283
  for (const [scope, receipt] of state.idempotency) {
9571
- this.database.run(
9572
- "INSERT INTO idempotency (scope, payload_json, result_json) VALUES (?, ?, ?)",
9573
- [scope, receipt.payload, JSON.stringify(receipt.result)]
10284
+ this.getStmtUpsertIdempotency().run(
10285
+ scope,
10286
+ receipt.payload,
10287
+ JSON.stringify(receipt.result)
9574
10288
  );
9575
10289
  }
9576
10290
  for (const [scope, receipt] of state.operationReceipts) {
9577
- this.database.run(
9578
- "INSERT INTO idempotency (scope, payload_json, result_json) VALUES (?, ?, ?)",
9579
- [`operation:${scope}`, receipt.payload, JSON.stringify(receipt.result)]
10291
+ this.getStmtUpsertIdempotency().run(
10292
+ `operation:${scope}`,
10293
+ receipt.payload,
10294
+ JSON.stringify(receipt.result)
9580
10295
  );
9581
10296
  }
9582
10297
  for (const [consumerId, cursor] of state.consumerCheckpoints ?? []) {
9583
- this.database.run(
9584
- "INSERT INTO consumer_checkpoints (consumer_id, sequence, event_id, updated_at) VALUES (?, ?, ?, ?)",
9585
- [consumerId, Number(String(cursor).split(":").at(-1)), null, (/* @__PURE__ */ new Date()).toISOString()]
10298
+ this.getStmtUpsertCheckpoint().run(
10299
+ consumerId,
10300
+ Number(String(cursor).split(":").at(-1)),
10301
+ null,
10302
+ (/* @__PURE__ */ new Date()).toISOString()
9586
10303
  );
9587
10304
  }
9588
- this.mirrorState(state);
10305
+ this.getStmtUpdateState().run(JSON.stringify(memory.exportMetadata()), STATE_ROW);
10306
+ this.mirrorInitialState(state);
10307
+ }
10308
+ mirrorChanges(tracker) {
10309
+ for (const agentId of tracker.deletedAgentIds) {
10310
+ const idStr = String(agentId);
10311
+ rmSync(join6(this.archiveRoot, idStr), { recursive: true, force: true });
10312
+ this.mirroredEventIds.delete(idStr);
10313
+ this.mirroredCompactionIds.delete(idStr);
10314
+ this.mirroredAgents.delete(idStr);
10315
+ }
10316
+ for (const agent of tracker.upsertedAgents.values()) {
10317
+ const idStr = String(agent.id);
10318
+ this.mirroredAgents.add(idStr);
10319
+ const archiveDir = join6(this.archiveRoot, idStr);
10320
+ const toolDir = join6(archiveDir, "tool-results");
10321
+ mkdirSync(toolDir, { recursive: true, mode: 448 });
10322
+ chmodSync(archiveDir, 448);
10323
+ chmodSync(toolDir, 448);
10324
+ }
10325
+ if (tracker.appendedEvents.length > 0) {
10326
+ mkdirSync(this.archiveRoot, { recursive: true, mode: 448 });
10327
+ chmodSync(this.archiveRoot, 448);
10328
+ const eventsByAgent = /* @__PURE__ */ new Map();
10329
+ for (const event of tracker.appendedEvents) {
10330
+ const agentId = String(event.agentId);
10331
+ let list = eventsByAgent.get(agentId);
10332
+ if (!list) {
10333
+ list = [];
10334
+ eventsByAgent.set(agentId, list);
10335
+ }
10336
+ list.push(event);
10337
+ }
10338
+ for (const [agentId, events] of eventsByAgent) {
10339
+ const archiveDir = join6(this.archiveRoot, agentId);
10340
+ const toolDir = join6(archiveDir, "tool-results");
10341
+ mkdirSync(toolDir, { recursive: true, mode: 448 });
10342
+ chmodSync(archiveDir, 448);
10343
+ chmodSync(toolDir, 448);
10344
+ const sessionPath = join6(archiveDir, "session.jsonl");
10345
+ try {
10346
+ chmodSync(sessionPath, 384);
10347
+ } catch {
10348
+ }
10349
+ const known = this.mirroredEventIds.get(agentId) ?? /* @__PURE__ */ new Set();
10350
+ const newEvents = events.filter((e) => !known.has(String(e.id)));
10351
+ if (newEvents.length > 0) {
10352
+ const lines = newEvents.map((e) => JSON.stringify({ schemaVersion: 1, kind: "rowan_event", event: e }) + "\n").join("");
10353
+ appendFileSync(sessionPath, lines, { encoding: "utf8", mode: 384 });
10354
+ chmodSync(sessionPath, 384);
10355
+ for (const e of newEvents) known.add(String(e.id));
10356
+ this.mirroredEventIds.set(agentId, known);
10357
+ }
10358
+ }
10359
+ }
10360
+ if (tracker.upsertedContextCompactions.size > 0) {
10361
+ for (const [agentId, record] of tracker.upsertedContextCompactions) {
10362
+ const idStr = String(agentId);
10363
+ const archiveDir = join6(this.archiveRoot, idStr);
10364
+ const sessionPath = join6(archiveDir, "session.jsonl");
10365
+ const knownCompactions = this.mirroredCompactionIds.get(idStr) ?? /* @__PURE__ */ new Set();
10366
+ if (!knownCompactions.has(record.id)) {
10367
+ const line = JSON.stringify({ schemaVersion: 1, kind: "context_compacted", record }) + "\n";
10368
+ appendFileSync(sessionPath, line, { encoding: "utf8", mode: 384 });
10369
+ chmodSync(sessionPath, 384);
10370
+ knownCompactions.add(record.id);
10371
+ this.mirroredCompactionIds.set(idStr, knownCompactions);
10372
+ }
10373
+ }
10374
+ }
9589
10375
  }
9590
10376
  /** Append the semantic durable event mirror and remove deleted Agent sidecars. */
9591
- mirrorState(state) {
10377
+ mirrorInitialState(state) {
9592
10378
  mkdirSync(this.archiveRoot, { recursive: true, mode: 448 });
9593
10379
  chmodSync(this.archiveRoot, 448);
9594
10380
  const currentAgents = new Set(state.agents.map((agent) => String(agent.id)));
@@ -9740,6 +10526,9 @@ var SqliteOwnedStore = class {
9740
10526
  commitPhaseEntered(input) {
9741
10527
  return this.store.commitPhaseEntered(this.lease, input);
9742
10528
  }
10529
+ commitPhaseOutput(input) {
10530
+ return this.store.commitPhaseOutput(this.lease, input);
10531
+ }
9743
10532
  commitInputRequired(input) {
9744
10533
  return this.store.commitInputRequired(this.lease, input);
9745
10534
  }
@@ -9805,19 +10594,6 @@ function ownerLease(row) {
9805
10594
  expiresAt: new Date(row.expires_at).toISOString()
9806
10595
  };
9807
10596
  }
9808
- function parseEventCursor(lease, cursor) {
9809
- const value = String(cursor);
9810
- const separator = value.lastIndexOf(":");
9811
- const incarnation = separator < 0 ? "" : value.slice(0, separator);
9812
- const sequence = separator < 0 ? "" : value.slice(separator + 1);
9813
- const token = String(lease.token);
9814
- const ownerSuffix = `:${lease.epoch}:${lease.ownerId}`;
9815
- const expectedIncarnation = token.endsWith(ownerSuffix) ? token.slice(0, -ownerSuffix.length) : "";
9816
- if (incarnation !== expectedIncarnation || !/^\d+$/.test(sequence)) {
9817
- throw new RuntimeError("invalid_cursor", { cursorType: "event", reason: "wrong_store" });
9818
- }
9819
- return Number(sequence);
9820
- }
9821
10597
  function ownershipLost2(lease, row) {
9822
10598
  return new RuntimeError("runtime_ownership_lost", {
9823
10599
  reason: row.epoch > lease.epoch ? "epoch_advanced" : row.owner_id ? "released" : "released",