@rowan-agent/agent 0.15.4 → 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.
package/dist/index.js CHANGED
@@ -7618,9 +7618,83 @@ var InMemoryStore = class _InMemoryStore {
7618
7618
  eventSequence = 0;
7619
7619
  retentionFloor = 1;
7620
7620
  contextCompactions = /* @__PURE__ */ new Map();
7621
+ recorder;
7622
+ runReceiptKeys = /* @__PURE__ */ new Map();
7621
7623
  constructor(options = {}) {
7622
7624
  this.incarnation = options.incarnation ?? createId("store");
7623
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
+ }
7624
7698
  static fromState(state) {
7625
7699
  const store = new _InMemoryStore({ incarnation: state.incarnation });
7626
7700
  for (const agent of state.agents) store.agents.set(agent.id, clone(agent));
@@ -7638,6 +7712,7 @@ var InMemoryStore = class _InMemoryStore {
7638
7712
  clonedRun.updatedAt = createTimestamp();
7639
7713
  store.runs.set(clonedRun.id, clonedRun);
7640
7714
  store.appendTransition(clonedRun, "input_required", "cancelled", { reason: clonedRun.cancellationReason });
7715
+ store.dropRunOperationReceipts(clonedRun.id);
7641
7716
  } else {
7642
7717
  store.runs.set(clonedRun.id, clonedRun);
7643
7718
  }
@@ -7646,7 +7721,18 @@ var InMemoryStore = class _InMemoryStore {
7646
7721
  for (const toolCall of state.toolCalls ?? []) store.toolCalls.set(toolCall.id, clone(toolCall));
7647
7722
  store.events.push(...clone(state.events));
7648
7723
  for (const [key, receipt] of state.idempotency) store.idempotency.set(key, clone(receipt));
7649
- 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
+ }
7650
7736
  for (const [agentId, messages] of state.historySeeds ?? []) store.historySeeds.set(agentId, clone(messages));
7651
7737
  for (const [consumerId, cursor] of state.consumerCheckpoints ?? []) store.consumerCheckpoints.set(consumerId, cursor);
7652
7738
  for (const [agentId, sequence] of state.nextAgentSequence) store.nextAgentSequence.set(agentId, sequence);
@@ -7679,6 +7765,16 @@ var InMemoryStore = class _InMemoryStore {
7679
7765
  contextCompactions: [...this.contextCompactions.entries()]
7680
7766
  });
7681
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
+ }
7682
7778
  attachOwner(lease) {
7683
7779
  this.owner = clone(lease);
7684
7780
  this.ownerEpoch = Math.max(this.ownerEpoch, lease.epoch);
@@ -7701,6 +7797,7 @@ var InMemoryStore = class _InMemoryStore {
7701
7797
  run.revision += 1;
7702
7798
  run.updatedAt = createTimestamp();
7703
7799
  this.appendTransition(run, "running", "failed", { failure });
7800
+ this.dropRunOperationReceipts(run.id);
7704
7801
  }
7705
7802
  }
7706
7803
  async openOwner(input) {
@@ -7718,6 +7815,7 @@ var InMemoryStore = class _InMemoryStore {
7718
7815
  }
7719
7816
  if (this.owner) this.interruptOwner(this.owner.epoch);
7720
7817
  this.dropSettledClaimReceipts();
7818
+ this.compactTerminalReceipts();
7721
7819
  this.ownerEpoch += 1;
7722
7820
  this.owner = {
7723
7821
  ownerId: input.ownerId,
@@ -7764,6 +7862,7 @@ var InMemoryStore = class _InMemoryStore {
7764
7862
  updatedAt: timestamp
7765
7863
  };
7766
7864
  this.agents.set(agent.id, agent);
7865
+ this.touchAgent(agent);
7767
7866
  if (input.historySeed && input.historySeed.length > 0) this.historySeeds.set(agent.id, materializeHistorySeed(agent.id, input.historySeed));
7768
7867
  this.nextAgentSequence.set(agent.id, 0);
7769
7868
  this.nextReadySequence.set(agent.id, 0);
@@ -7777,6 +7876,7 @@ var InMemoryStore = class _InMemoryStore {
7777
7876
  if (configToken !== void 0) agent.currentConfigToken = configToken;
7778
7877
  if (configIdentity !== void 0) agent.currentConfigIdentity = configIdentity;
7779
7878
  agent.updatedAt = createTimestamp();
7879
+ this.touchAgent(agent);
7780
7880
  return clone(agent);
7781
7881
  }
7782
7882
  updateAgentConfigToken(lease, input) {
@@ -7789,6 +7889,7 @@ var InMemoryStore = class _InMemoryStore {
7789
7889
  agent.currentConfigToken = input.token;
7790
7890
  if (input.configIdentity !== void 0) agent.currentConfigIdentity = input.configIdentity;
7791
7891
  agent.updatedAt = createTimestamp();
7892
+ this.touchAgent(agent);
7792
7893
  this.writeReceipt(scope, payload, agent);
7793
7894
  return clone(agent);
7794
7895
  }
@@ -7810,24 +7911,48 @@ var InMemoryStore = class _InMemoryStore {
7810
7911
  }
7811
7912
  const runIds = new Set(runs.map((run) => run.id));
7812
7913
  this.agents.delete(agent.id);
7914
+ this.recorder?.onAgentDelete(agent.id);
7813
7915
  this.nextAgentSequence.delete(agent.id);
7814
7916
  this.nextReadySequence.delete(agent.id);
7815
7917
  this.historySeeds.delete(agent.id);
7816
- 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
+ }
7817
7923
  for (const [messageId, message] of this.messages) {
7818
- 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
+ }
7819
7928
  }
7820
7929
  for (const [toolCallId, toolCall] of this.toolCalls) {
7821
- 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
+ }
7822
7934
  }
7935
+ const deletedEventIds = [];
7823
7936
  for (let index = this.events.length - 1; index >= 0; index -= 1) {
7824
- 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);
7825
7944
  }
7826
7945
  for (const [key] of this.idempotency) {
7827
- 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
+ }
7828
7950
  }
7829
7951
  for (const [key] of this.operationReceipts) {
7830
- 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
+ }
7831
7956
  }
7832
7957
  }
7833
7958
  reviseMessage(lease, input) {
@@ -7935,14 +8060,19 @@ var InMemoryStore = class _InMemoryStore {
7935
8060
  run.revision += 1;
7936
8061
  run.updatedAt = timestamp;
7937
8062
  this.appendTransition(run, previousState, "cancelled", { reason: run.cancellationReason });
8063
+ this.dropRunOperationReceipts(run.id);
7938
8064
  } else {
7939
8065
  run.revision += 1;
7940
8066
  run.updatedAt = timestamp;
8067
+ this.touchRun(run);
7941
8068
  }
7942
8069
  }
7943
8070
  for (const [messageId, message] of this.messages) {
7944
8071
  if (!affectedRunSet.has(message.runId)) continue;
7945
- 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
+ }
7946
8076
  }
7947
8077
  if (isSeedMessage) {
7948
8078
  const targetIndex = seed.findIndex(({ id }) => id === original.id);
@@ -7956,10 +8086,11 @@ var InMemoryStore = class _InMemoryStore {
7956
8086
  sequenceWithinRun: 0,
7957
8087
  createdAt: timestamp
7958
8088
  };
7959
- this.messages.set(revisedMessage.id, revisedMessage);
8089
+ this.storeMessage(revisedMessage);
7960
8090
  this.runs.set(replacementRun.id, replacementRun);
8091
+ this.touchRun(replacementRun);
7961
8092
  this.appendTransition(replacementRun, null, "queued");
7962
- this.events.push({
8093
+ this.appendEvent({
7963
8094
  ...this.baseEvent(replacementRun),
7964
8095
  kind: "message_revised",
7965
8096
  message: revisedMessage,
@@ -7975,7 +8106,7 @@ var InMemoryStore = class _InMemoryStore {
7975
8106
  affectedToolCallIds: affectedTools.map((toolCall) => toolCall.id),
7976
8107
  ...effectDigest === void 0 ? {} : { effectDigest }
7977
8108
  };
7978
- this.writeOperationReceipt(operationKey, operationPayload, result);
8109
+ this.writeOperationReceipt(operationKey, operationPayload, result, replacementRun.id);
7979
8110
  return clone(result);
7980
8111
  }
7981
8112
  commitInteractionRecord(run, interaction, status, answer, reply) {
@@ -7997,7 +8128,7 @@ var InMemoryStore = class _InMemoryStore {
7997
8128
  sequenceWithinRun: this.nextMessageSequence(run.id),
7998
8129
  createdAt: now
7999
8130
  };
8000
- this.messages.set(record.id, record);
8131
+ this.storeMessage(record);
8001
8132
  this.appendMessage(run, record);
8002
8133
  return record;
8003
8134
  }
@@ -8046,12 +8177,13 @@ var InMemoryStore = class _InMemoryStore {
8046
8177
  sequenceWithinRun: this.nextMessageSequence(waitingRun.id),
8047
8178
  createdAt: now
8048
8179
  };
8049
- this.messages.set(message.id, message);
8180
+ this.storeMessage(message);
8050
8181
  this.appendMessage(waitingRun, message);
8051
8182
  waitingRun.readySequence = this.nextReady(waitingRun.agentId);
8052
8183
  waitingRun.state = "queued";
8053
8184
  waitingRun.revision += 1;
8054
8185
  waitingRun.updatedAt = now;
8186
+ this.touchRun(waitingRun);
8055
8187
  this.appendTransition(waitingRun, "input_required", "queued");
8056
8188
  this.writeReceipt(scope, payload, waitingRun);
8057
8189
  return clone(waitingRun);
@@ -8073,6 +8205,7 @@ var InMemoryStore = class _InMemoryStore {
8073
8205
  updatedAt: timestamp
8074
8206
  };
8075
8207
  this.runs.set(run.id, run);
8208
+ this.touchRun(run);
8076
8209
  this.appendTransition(run, null, "queued");
8077
8210
  this.writeReceipt(scope, payload, run);
8078
8211
  return clone(run);
@@ -8115,7 +8248,7 @@ var InMemoryStore = class _InMemoryStore {
8115
8248
  sequenceWithinRun: this.nextMessageSequence(run.id),
8116
8249
  createdAt: createTimestamp()
8117
8250
  };
8118
- this.messages.set(message.id, message);
8251
+ this.storeMessage(message);
8119
8252
  committedMessages.push(message);
8120
8253
  }
8121
8254
  const execution = {
@@ -8127,10 +8260,11 @@ var InMemoryStore = class _InMemoryStore {
8127
8260
  run.execution = execution;
8128
8261
  run.revision += 1;
8129
8262
  run.updatedAt = createTimestamp();
8263
+ this.touchRun(run);
8130
8264
  for (const message of committedMessages) this.appendMessage(run, message);
8131
8265
  this.appendTransition(run, "queued", "running");
8132
8266
  const result = { run: clone(run), execution: clone(execution), history: this.activeHistory(run.agentId, run.agentSequence) };
8133
- this.writeOperationReceipt(operationKey, operationPayload, result);
8267
+ this.writeOperationReceipt(operationKey, operationPayload, result, run.id);
8134
8268
  return result;
8135
8269
  }
8136
8270
  failQueuedRun(lease, input) {
@@ -8146,9 +8280,11 @@ var InMemoryStore = class _InMemoryStore {
8146
8280
  run.failure = clone(input.failure);
8147
8281
  run.revision += 1;
8148
8282
  run.updatedAt = createTimestamp();
8283
+ this.touchRun(run);
8149
8284
  this.appendTransition(run, "queued", "failed", { failure: input.failure });
8285
+ this.dropRunOperationReceipts(run.id);
8150
8286
  const result = clone(run);
8151
- this.writeOperationReceipt(operationKey, operationPayload, result);
8287
+ this.writeOperationReceipt(operationKey, operationPayload, result, run.id);
8152
8288
  return result;
8153
8289
  }
8154
8290
  commitPhaseEntered(lease, input) {
@@ -8164,10 +8300,11 @@ var InMemoryStore = class _InMemoryStore {
8164
8300
  run.currentPhaseId = input.phaseId;
8165
8301
  run.revision += 1;
8166
8302
  run.updatedAt = createTimestamp();
8303
+ this.touchRun(run);
8167
8304
  const visit2 = this.events.filter((event) => event.runId === run.id && event.kind === "phase_entered").length + 1;
8168
- 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 }));
8169
8306
  const result = clone(run);
8170
- this.writeOperationReceipt(operationKey, operationPayload, result);
8307
+ this.writeOperationReceipt(operationKey, operationPayload, result, run.id);
8171
8308
  return result;
8172
8309
  }
8173
8310
  /** Commit a parallel Phase's reply while its Run is still running. */
@@ -8188,12 +8325,13 @@ var InMemoryStore = class _InMemoryStore {
8188
8325
  role: "assistant",
8189
8326
  sequenceWithinRun: this.nextMessageSequence(run.id)
8190
8327
  };
8191
- this.messages.set(message.id, message);
8328
+ this.storeMessage(message);
8192
8329
  run.revision += 1;
8193
8330
  run.updatedAt = createTimestamp();
8331
+ this.touchRun(run);
8194
8332
  this.appendMessage(run, message);
8195
8333
  const result = clone(run);
8196
- this.writeOperationReceipt(operationKey, operationPayload, result);
8334
+ this.writeOperationReceipt(operationKey, operationPayload, result, run.id);
8197
8335
  return result;
8198
8336
  }
8199
8337
  commitInputRequired(lease, input) {
@@ -8215,7 +8353,7 @@ var InMemoryStore = class _InMemoryStore {
8215
8353
  ...clone(input.prompt),
8216
8354
  sequenceWithinRun: this.nextMessageSequence(run.id)
8217
8355
  };
8218
- this.messages.set(prompt.id, prompt);
8356
+ this.storeMessage(prompt);
8219
8357
  }
8220
8358
  run.state = "input_required";
8221
8359
  run.checkpoint = clone(input.checkpoint);
@@ -8230,12 +8368,13 @@ var InMemoryStore = class _InMemoryStore {
8230
8368
  delete run.execution;
8231
8369
  run.revision += 1;
8232
8370
  run.updatedAt = createTimestamp();
8371
+ this.touchRun(run);
8233
8372
  if (prompt) {
8234
8373
  this.appendMessage(run, prompt);
8235
8374
  }
8236
8375
  this.appendTransition(run, "running", "input_required", { prompt, interactions, answers: run.interactionAnswers ?? {} });
8237
8376
  const result = clone(run);
8238
- this.writeOperationReceipt(operationKey, operationPayload, result);
8377
+ this.writeOperationReceipt(operationKey, operationPayload, result, run.id);
8239
8378
  return result;
8240
8379
  }
8241
8380
  answerInteraction(lease, input) {
@@ -8283,8 +8422,9 @@ var InMemoryStore = class _InMemoryStore {
8283
8422
  run.state = "queued";
8284
8423
  this.appendTransition(run, "input_required", "queued");
8285
8424
  }
8425
+ this.touchRun(run);
8286
8426
  const result = clone(run);
8287
- this.writeOperationReceipt(operationKey, operationPayload, result);
8427
+ this.writeOperationReceipt(operationKey, operationPayload, result, run.id);
8288
8428
  return result;
8289
8429
  }
8290
8430
  reserveToolCall(lease, input) {
@@ -8358,17 +8498,18 @@ var InMemoryStore = class _InMemoryStore {
8358
8498
  sequenceWithinRun: this.nextMessageSequence(run.id),
8359
8499
  createdAt: timestamp
8360
8500
  };
8361
- for (const toolCall of toolCalls) this.toolCalls.set(toolCall.id, toolCall);
8362
- this.messages.set(requestMessage.id, requestMessage);
8501
+ for (const toolCall of toolCalls) this.storeToolCall(toolCall);
8502
+ this.storeMessage(requestMessage);
8363
8503
  run.revision += 1;
8364
8504
  run.updatedAt = timestamp;
8505
+ this.touchRun(run);
8365
8506
  this.appendMessage(run, requestMessage);
8366
8507
  for (const toolCall of toolCalls) this.appendToolTransition(run, { from: null, to: "pending" }, toolCall);
8367
8508
  const result = {
8368
8509
  run: clone(run),
8369
8510
  toolCalls: toolCalls.map((toolCall) => clone(toolCall))
8370
8511
  };
8371
- this.writeOperationReceipt(operationKey, operationPayload, result);
8512
+ this.writeOperationReceipt(operationKey, operationPayload, result, run.id);
8372
8513
  return result;
8373
8514
  }
8374
8515
  startToolCall(lease, input) {
@@ -8383,13 +8524,14 @@ var InMemoryStore = class _InMemoryStore {
8383
8524
  if (toolCall.state !== "pending") throw new RuntimeError("run_state_conflict", { runId: run.id, expected: ["running"], actual: run.state });
8384
8525
  toolCall.state = "running";
8385
8526
  toolCall.executionId = input.execution.executionId;
8386
- toolCall.executionId = input.execution.executionId;
8387
8527
  toolCall.updatedAt = createTimestamp();
8528
+ this.storeToolCall(toolCall);
8388
8529
  run.revision += 1;
8389
8530
  run.updatedAt = toolCall.updatedAt;
8531
+ this.touchRun(run);
8390
8532
  this.appendToolTransition(run, { from: "pending", to: "running" }, toolCall);
8391
8533
  const result = { run: clone(run), toolCall: clone(toolCall) };
8392
- this.writeOperationReceipt(operationKey, operationPayload, result);
8534
+ this.writeOperationReceipt(operationKey, operationPayload, result, run.id);
8393
8535
  return result;
8394
8536
  }
8395
8537
  suspendToolCall(lease, input) {
@@ -8406,11 +8548,13 @@ var InMemoryStore = class _InMemoryStore {
8406
8548
  }
8407
8549
  toolCall.state = "pending";
8408
8550
  toolCall.updatedAt = createTimestamp();
8551
+ this.storeToolCall(toolCall);
8409
8552
  run.revision += 1;
8410
8553
  run.updatedAt = toolCall.updatedAt;
8554
+ this.touchRun(run);
8411
8555
  this.appendToolTransition(run, { from: "running", to: "pending" }, toolCall);
8412
8556
  const result = { run: clone(run), toolCall: clone(toolCall) };
8413
- this.writeOperationReceipt(operationKey, operationPayload, result);
8557
+ this.writeOperationReceipt(operationKey, operationPayload, result, run.id);
8414
8558
  return result;
8415
8559
  }
8416
8560
  commitToolResult(lease, input) {
@@ -8442,15 +8586,17 @@ var InMemoryStore = class _InMemoryStore {
8442
8586
  sequenceWithinRun: this.nextMessageSequence(run.id),
8443
8587
  createdAt: createTimestamp()
8444
8588
  };
8445
- this.messages.set(message.id, message);
8589
+ this.storeMessage(message);
8446
8590
  toolCall.state = input.state;
8447
8591
  toolCall.result = durableResult;
8448
8592
  toolCall.resultMessageId = resultMessageId;
8449
8593
  if (input.state === "indeterminate") toolCall.reason = input.reason;
8450
8594
  else delete toolCall.reason;
8451
8595
  toolCall.updatedAt = message.createdAt;
8596
+ this.storeToolCall(toolCall);
8452
8597
  run.revision += 1;
8453
8598
  run.updatedAt = message.createdAt;
8599
+ this.touchRun(run);
8454
8600
  this.appendToolTransition(run, { from, to: input.state }, toolCall);
8455
8601
  this.appendMessage(run, message);
8456
8602
  if (input.state === "indeterminate") {
@@ -8471,10 +8617,12 @@ var InMemoryStore = class _InMemoryStore {
8471
8617
  delete run.execution;
8472
8618
  run.revision += 1;
8473
8619
  run.updatedAt = createTimestamp();
8620
+ this.touchRun(run);
8474
8621
  this.appendTransition(run, "running", "failed", { failure });
8622
+ this.dropRunOperationReceipts(run.id);
8475
8623
  }
8476
8624
  const result = { run: clone(run), toolCall: clone(toolCall) };
8477
- this.writeOperationReceipt(operationKey, operationPayload, result);
8625
+ this.writeOperationReceipt(operationKey, operationPayload, result, run.id);
8478
8626
  return result;
8479
8627
  }
8480
8628
  commitOutcome(lease, input) {
@@ -8492,7 +8640,7 @@ var InMemoryStore = class _InMemoryStore {
8492
8640
  if (nextState === "failed" && !input.failure) throw new TypeError("failed Run requires a failure");
8493
8641
  const output = input.output ? { ...clone(input.output), sequenceWithinRun: this.nextMessageSequence(run.id) } : void 0;
8494
8642
  if (output) {
8495
- this.messages.set(output.id, output);
8643
+ this.storeMessage(output);
8496
8644
  this.appendMessage(run, output);
8497
8645
  }
8498
8646
  run.state = nextState;
@@ -8504,9 +8652,10 @@ var InMemoryStore = class _InMemoryStore {
8504
8652
  delete run.interactionAnswers;
8505
8653
  run.revision += 1;
8506
8654
  run.updatedAt = createTimestamp();
8655
+ this.touchRun(run);
8507
8656
  this.appendTransition(run, "running", nextState, { outcome: input.outcome, failure: input.failure, output });
8657
+ this.dropRunOperationReceipts(run.id);
8508
8658
  const result = clone(run);
8509
- this.writeOperationReceipt(operationKey, operationPayload, result);
8510
8659
  return result;
8511
8660
  }
8512
8661
  cancelRun(lease, input) {
@@ -8526,7 +8675,6 @@ var InMemoryStore = class _InMemoryStore {
8526
8675
  }
8527
8676
  if (["completed", "failed", "cancelled"].includes(run.state)) {
8528
8677
  const result2 = clone(run);
8529
- if (input.expectedRevision !== void 0) this.writeOperationReceipt(operationKey, operationPayload, result2);
8530
8678
  return result2;
8531
8679
  }
8532
8680
  const from = run.state;
@@ -8541,7 +8689,7 @@ var InMemoryStore = class _InMemoryStore {
8541
8689
  for (const toolCall of activeToolCalls) this.interruptToolCall(run, toolCall, input.reason ?? "The Run was cancelled.");
8542
8690
  if (input.output && (typeof input.output.content === "string" ? input.output.content.length > 0 : input.output.content.length > 0)) {
8543
8691
  const output = { ...clone(input.output), sequenceWithinRun: this.nextMessageSequence(run.id) };
8544
- this.messages.set(output.id, output);
8692
+ this.storeMessage(output);
8545
8693
  this.appendMessage(run, output);
8546
8694
  }
8547
8695
  if (run.openInteractions && run.openInteractions.length > 0) {
@@ -8564,9 +8712,10 @@ var InMemoryStore = class _InMemoryStore {
8564
8712
  delete run.interactionAnswers;
8565
8713
  run.revision += 1;
8566
8714
  run.updatedAt = createTimestamp();
8715
+ this.touchRun(run);
8567
8716
  this.appendTransition(run, from, "failed", { failure });
8717
+ this.dropRunOperationReceipts(run.id);
8568
8718
  const result2 = clone(run);
8569
- if (input.expectedRevision !== void 0) this.writeOperationReceipt(operationKey, operationPayload, result2);
8570
8719
  return result2;
8571
8720
  }
8572
8721
  run.state = "cancelled";
@@ -8577,9 +8726,10 @@ var InMemoryStore = class _InMemoryStore {
8577
8726
  delete run.interactionAnswers;
8578
8727
  run.revision += 1;
8579
8728
  run.updatedAt = createTimestamp();
8729
+ this.touchRun(run);
8580
8730
  this.appendTransition(run, from, "cancelled", { reason: input.reason });
8731
+ this.dropRunOperationReceipts(run.id);
8581
8732
  const result = clone(run);
8582
- if (input.expectedRevision !== void 0) this.writeOperationReceipt(operationKey, operationPayload, result);
8583
8733
  return result;
8584
8734
  }
8585
8735
  snapshotRun(lease, runId) {
@@ -8660,6 +8810,7 @@ var InMemoryStore = class _InMemoryStore {
8660
8810
  const previous = this.consumerCheckpoints.get(input.consumerId);
8661
8811
  if (previous !== void 0 && next <= this.parseCursor(previous)) return;
8662
8812
  this.consumerCheckpoints.set(input.consumerId, input.cursor);
8813
+ this.recorder?.onConsumerCheckpointUpsert(input.consumerId, input.cursor);
8663
8814
  }
8664
8815
  compact(lease, input = {}) {
8665
8816
  this.assertOwner(lease);
@@ -8693,11 +8844,15 @@ var InMemoryStore = class _InMemoryStore {
8693
8844
  }
8694
8845
  const deletedEvents = deleteThrough >= this.retentionFloor ? this.events.filter((event) => this.parseCursor(event.cursor) <= deleteThrough) : [];
8695
8846
  if (deleteThrough >= this.retentionFloor) {
8847
+ const deletedEventIds = this.events.filter((event) => this.parseCursor(event.cursor) <= deleteThrough).map((event) => event.id);
8696
8848
  this.events.splice(0, this.events.length, ...this.events.filter((event) => this.parseCursor(event.cursor) > deleteThrough));
8697
8849
  this.retentionFloor = deleteThrough + 1;
8850
+ this.recorder?.onRetentionFloorChange(this.retentionFloor);
8851
+ this.recorder?.onEventsTruncate(deletedEventIds);
8698
8852
  for (const [consumerId, cursor] of this.consumerCheckpoints) {
8699
8853
  if (this.parseCursor(cursor) < this.retentionFloor - 1) {
8700
8854
  this.consumerCheckpoints.delete(consumerId);
8855
+ this.recorder?.onConsumerCheckpointDelete(consumerId);
8701
8856
  }
8702
8857
  }
8703
8858
  }
@@ -8706,11 +8861,21 @@ var InMemoryStore = class _InMemoryStore {
8706
8861
  const deletedRunIds = deletableRuns.map((run) => run.id);
8707
8862
  const deletedRunSet = new Set(deletedRunIds);
8708
8863
  const deletedTools = [...this.toolCalls.values()].filter((tool) => deletedRunSet.has(tool.runId));
8709
- 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
+ }
8710
8869
  for (const [messageId, message] of this.messages) {
8711
- 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);
8712
8878
  }
8713
- for (const tool of deletedTools) this.toolCalls.delete(tool.id);
8714
8879
  return {
8715
8880
  deletedRunIds,
8716
8881
  deletedToolCallIds: deletedTools.map((tool) => tool.id),
@@ -8732,7 +8897,10 @@ var InMemoryStore = class _InMemoryStore {
8732
8897
  return receipt.result;
8733
8898
  }
8734
8899
  writeReceipt(scope, payload, result) {
8735
- 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);
8736
8904
  }
8737
8905
  replayOperation(key, payload) {
8738
8906
  const receipt = this.operationReceipts.get(key);
@@ -8740,8 +8908,19 @@ var InMemoryStore = class _InMemoryStore {
8740
8908
  if (receipt.payload !== payload) throw new Error(`Idempotency payload conflict for ${key}.`);
8741
8909
  return receipt.result;
8742
8910
  }
8743
- writeOperationReceipt(key, payload, result) {
8744
- 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);
8745
8924
  }
8746
8925
  /**
8747
8926
  * A Claim receipt exists for idempotent replay of one live Execution Attempt
@@ -8749,7 +8928,11 @@ var InMemoryStore = class _InMemoryStore {
8749
8928
  * one history snapshot per Agent instead of one per Attempt.
8750
8929
  */
8751
8930
  dropClaimReceipt(execution) {
8752
- 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
+ }
8753
8936
  }
8754
8937
  /**
8755
8938
  * A new owner fences the Execution Attempts of the previous one, so no Claim
@@ -8765,6 +8948,7 @@ var InMemoryStore = class _InMemoryStore {
8765
8948
  for (const key of [...this.operationReceipts.keys()]) {
8766
8949
  if (key.startsWith("claim:") && !liveExecutions.has(key.slice("claim:".length))) {
8767
8950
  this.operationReceipts.delete(key);
8951
+ this.recorder?.onOperationReceiptDelete(key);
8768
8952
  }
8769
8953
  }
8770
8954
  }
@@ -8803,14 +8987,16 @@ var InMemoryStore = class _InMemoryStore {
8803
8987
  };
8804
8988
  const from = toolCall.state;
8805
8989
  const to = from === "running" ? "indeterminate" : "failed";
8806
- this.messages.set(message.id, message);
8990
+ this.storeMessage(message);
8807
8991
  toolCall.state = to;
8808
8992
  toolCall.result = durableResult;
8809
8993
  toolCall.resultMessageId = message.id;
8810
8994
  if (to === "indeterminate") toolCall.reason = reason;
8811
8995
  toolCall.updatedAt = message.createdAt;
8996
+ this.storeToolCall(toolCall);
8812
8997
  run.revision += 1;
8813
8998
  run.updatedAt = message.createdAt;
8999
+ this.touchRun(run);
8814
9000
  this.appendToolTransition(run, { from, to }, toolCall);
8815
9001
  this.appendMessage(run, message);
8816
9002
  }
@@ -8911,6 +9097,7 @@ ${record.summary}`,
8911
9097
  if (existing?.id === record.id) return clone(existing);
8912
9098
  const next = clone(record);
8913
9099
  this.contextCompactions.set(record.agentId, next);
9100
+ this.recorder?.onContextCompactionUpsert(record.agentId, next);
8914
9101
  return clone(next);
8915
9102
  }
8916
9103
  activeHistory(agentId, beforeSequence) {
@@ -8920,7 +9107,7 @@ ${record.summary}`,
8920
9107
  ].map((message) => ({ ...message, messageRevision: message.messageRevision ?? 0 })));
8921
9108
  }
8922
9109
  appendMessage(run, message) {
8923
- this.events.push(this.baseEvent(run, {
9110
+ this.appendEvent(this.baseEvent(run, {
8924
9111
  kind: "message_committed",
8925
9112
  message
8926
9113
  }));
@@ -8936,13 +9123,14 @@ ${record.summary}`,
8936
9123
  ...to === "failed" && options?.failure ? { failure: options.failure } : {},
8937
9124
  ...to === "cancelled" && options?.reason ? { reason: options.reason } : {}
8938
9125
  };
8939
- this.events.push(transition);
9126
+ this.touchRun(run);
9127
+ this.appendEvent(transition);
8940
9128
  }
8941
9129
  appendToolTransition(run, transition, toolCall) {
8942
- this.events.push(this.baseEvent(run, {
9130
+ this.appendEvent(this.baseEvent(run, {
8943
9131
  kind: "tool_state_changed",
8944
9132
  transition,
8945
- toolCall
9133
+ toolCall: clone(toolCall)
8946
9134
  }));
8947
9135
  }
8948
9136
  baseEvent(run, extra) {
@@ -9318,6 +9506,94 @@ var CURRENT_SCHEMA_SQL = `
9318
9506
  CREATE INDEX runs_active_idx ON runs (agent_id, state, agent_sequence);
9319
9507
  CREATE INDEX run_events_sequence_idx ON run_events (sequence);
9320
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
+ };
9321
9597
  var SqliteStore = class {
9322
9598
  database;
9323
9599
  archiveRoot;
@@ -9326,6 +9602,25 @@ var SqliteStore = class {
9326
9602
  mirroredAgents = /* @__PURE__ */ new Set();
9327
9603
  closed = false;
9328
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;
9329
9624
  constructor(filename = ":memory:") {
9330
9625
  this.database = new Database(filename, { create: true, readwrite: true, strict: true });
9331
9626
  this.archiveRoot = filename === ":memory:" ? join6(tmpdir2(), `rowan-context-archives-${crypto.randomUUID()}`) : join6(dirname7(resolve8(filename)), "context-archives");
@@ -9346,7 +9641,26 @@ var SqliteStore = class {
9346
9641
  }
9347
9642
  const replay = ownerLease(row);
9348
9643
  this.writeOwner({ ...row, expires_at: now + input.leaseMs });
9349
- 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;
9350
9664
  }
9351
9665
  const state = this.readState();
9352
9666
  const memory = InMemoryStore.fromState(state);
@@ -9365,22 +9679,42 @@ var SqliteStore = class {
9365
9679
  released_epoch: row.released_epoch
9366
9680
  };
9367
9681
  this.writeOwner(next);
9368
- memory.dropSettledClaimReceipts();
9369
- this.persistState(memory.exportState());
9370
- return {
9682
+ const newLease = {
9371
9683
  ownerId: input.ownerId,
9372
9684
  token,
9373
9685
  epoch,
9374
9686
  expiresAt: new Date(now + input.leaseMs).toISOString()
9375
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;
9376
9701
  });
9377
9702
  return new SqliteOwnedStore(this, lease);
9378
9703
  }
9379
9704
  close() {
9380
9705
  if (this.closed) return;
9381
9706
  this.closed = true;
9707
+ this.cachedMemory = void 0;
9708
+ this.cachedOwner = void 0;
9382
9709
  this.database.close();
9383
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
+ }
9384
9718
  /** Filesystem location owned by this Store for one Agent's readable archive. */
9385
9719
  contextArchiveDir(agentId) {
9386
9720
  return join6(this.archiveRoot, String(agentId));
@@ -9465,31 +9799,7 @@ var SqliteStore = class {
9465
9799
  }
9466
9800
  async listEvents(lease, input = {}) {
9467
9801
  this.assertOpen();
9468
- return this.readTransaction(() => {
9469
- this.requireMatchingOwner(lease, true);
9470
- const retentionRow = this.database.query(
9471
- "SELECT value FROM runtime_meta WHERE key = ?"
9472
- ).get(RETENTION_FLOOR_KEY);
9473
- const retentionFloor = Math.max(1, Number(retentionRow?.value ?? "1"));
9474
- const after = input.after ? parseEventCursor(lease, input.after) : retentionFloor - 1;
9475
- if (input.after && after < retentionFloor - 1) {
9476
- throw new RuntimeError("invalid_cursor", { cursorType: "event", reason: "expired" });
9477
- }
9478
- if (input.after) {
9479
- const latest = this.database.query(
9480
- "SELECT max(sequence) AS sequence FROM run_events"
9481
- ).get();
9482
- const waterline = latest.sequence ?? 0;
9483
- if (after > waterline) {
9484
- throw new RuntimeError("invalid_cursor", { cursorType: "event", reason: "beyond_waterline" });
9485
- }
9486
- if (after === waterline) return [];
9487
- }
9488
- const rows = this.database.query(
9489
- "SELECT payload_json FROM run_events ORDER BY sequence"
9490
- ).all();
9491
- return rows.map((row) => JSON.parse(row.payload_json)).filter((event) => parseEventCursor(lease, event.cursor) > after);
9492
- });
9802
+ return this.read(lease, (memory) => memory.listEvents(lease, input));
9493
9803
  }
9494
9804
  async openConsumer(lease, consumerId) {
9495
9805
  return this.invoke(lease, (store, current) => store.openConsumer(current, consumerId), false);
@@ -9513,7 +9823,12 @@ var SqliteStore = class {
9513
9823
  const row = this.requireMatchingOwner(lease, false);
9514
9824
  const expiresAt = Date.now() + leaseMs;
9515
9825
  this.writeOwner({ ...row, expires_at: expiresAt });
9516
- 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;
9517
9832
  });
9518
9833
  }
9519
9834
  async sealAndReleaseOwner(lease) {
@@ -9524,27 +9839,75 @@ var SqliteStore = class {
9524
9839
  if (row.owner_id !== lease.ownerId || row.owner_token !== lease.token || row.epoch !== lease.epoch) {
9525
9840
  throw ownershipLost2(lease, row);
9526
9841
  }
9527
- const memory = InMemoryStore.fromState(this.readState());
9528
- memory.attachOwner(lease);
9529
- memory.interruptOwner(lease.epoch, "The Runtime owner was sealed.");
9530
- 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);
9531
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;
9532
9859
  });
9533
9860
  }
9534
- 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) {
9535
9874
  this.assertOpen();
9536
9875
  return this.immediateTransaction(() => {
9537
9876
  this.requireMatchingOwner(lease, true);
9538
- const memory = InMemoryStore.fromState(this.readState());
9539
- memory.attachOwner(lease);
9540
- const result = operation(memory, lease);
9541
- if (persist) {
9542
- this.requireMatchingOwner(lease, true);
9543
- 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;
9544
9901
  }
9545
9902
  return result;
9546
9903
  });
9547
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
+ }
9548
9911
  ensureSchema() {
9549
9912
  const catalog = this.catalog();
9550
9913
  if (catalog.length > 0 && !this.isCurrentSchema(catalog)) {
@@ -9561,7 +9924,7 @@ var SqliteStore = class {
9561
9924
  this.database.run("INSERT INTO runtime_meta (key, value) VALUES (?, ?)", [RETENTION_FLOOR_KEY, "1"]);
9562
9925
  const state = new InMemoryStore();
9563
9926
  this.database.run("INSERT INTO runtime_owner (singleton, epoch) VALUES (?, ?)", [STATE_ROW, 0]);
9564
- 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())]);
9565
9928
  });
9566
9929
  return;
9567
9930
  }
@@ -9600,13 +9963,267 @@ var SqliteStore = class {
9600
9963
  readState() {
9601
9964
  const row = this.database.query("SELECT state_json FROM runtime_state WHERE singleton = ?").get(STATE_ROW);
9602
9965
  if (!row) throw new RuntimeError("store_unavailable", { operation: "read_state", retryable: false, reason: "runtime_state row is missing" });
9603
- 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
+ );
10100
+ }
10101
+ getStmtDeleteIdempotency() {
10102
+ return this.stmtDeleteIdempotency ??= this.database.prepare("DELETE FROM idempotency WHERE scope = ?");
10103
+ }
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
+ );
9604
10118
  }
9605
- writeState(state) {
9606
- this.database.run("UPDATE runtime_state SET state_json = ? WHERE singleton = ?", [JSON.stringify(state), STATE_ROW]);
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);
9607
10224
  }
9608
- persistState(state) {
9609
- this.writeState(state);
10225
+ writeFullStateToTables(memory) {
10226
+ const state = memory.exportState();
9610
10227
  this.database.run(
9611
10228
  "INSERT INTO runtime_meta (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value",
9612
10229
  [RETENTION_FLOOR_KEY, String(Math.max(1, state.retentionFloor ?? 1))]
@@ -9615,57 +10232,149 @@ var SqliteStore = class {
9615
10232
  this.database.run(`DELETE FROM ${table}`);
9616
10233
  }
9617
10234
  for (const agent of state.agents) {
9618
- this.database.run(
9619
- "INSERT INTO agents (id, metadata_json, config_token, config_identity, created_at, activated_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?)",
9620
- [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
9621
10243
  );
9622
10244
  }
9623
10245
  for (const run of state.runs) {
9624
- this.database.run(
9625
- "INSERT INTO runs (id, agent_id, agent_sequence, state, revision, payload_json, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
9626
- [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
9627
10255
  );
9628
10256
  }
9629
10257
  for (const message of state.messages) {
9630
- this.database.run(
9631
- "INSERT INTO messages (id, run_id, sequence_within_run, payload_json, created_at) VALUES (?, ?, ?, ?, ?)",
9632
- [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
9633
10264
  );
9634
10265
  }
9635
10266
  for (const toolCall of state.toolCalls) {
9636
- this.database.run(
9637
- "INSERT INTO tool_calls (id, run_id, execution_id, state, payload_json) VALUES (?, ?, ?, ?, ?)",
9638
- [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)
9639
10273
  );
9640
10274
  }
9641
10275
  for (const event of state.events) {
9642
- this.database.run(
9643
- "INSERT INTO run_events (id, run_id, payload_json, created_at) VALUES (?, ?, ?, ?)",
9644
- [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
9645
10281
  );
9646
10282
  }
9647
10283
  for (const [scope, receipt] of state.idempotency) {
9648
- this.database.run(
9649
- "INSERT INTO idempotency (scope, payload_json, result_json) VALUES (?, ?, ?)",
9650
- [scope, receipt.payload, JSON.stringify(receipt.result)]
10284
+ this.getStmtUpsertIdempotency().run(
10285
+ scope,
10286
+ receipt.payload,
10287
+ JSON.stringify(receipt.result)
9651
10288
  );
9652
10289
  }
9653
10290
  for (const [scope, receipt] of state.operationReceipts) {
9654
- this.database.run(
9655
- "INSERT INTO idempotency (scope, payload_json, result_json) VALUES (?, ?, ?)",
9656
- [`operation:${scope}`, receipt.payload, JSON.stringify(receipt.result)]
10291
+ this.getStmtUpsertIdempotency().run(
10292
+ `operation:${scope}`,
10293
+ receipt.payload,
10294
+ JSON.stringify(receipt.result)
9657
10295
  );
9658
10296
  }
9659
10297
  for (const [consumerId, cursor] of state.consumerCheckpoints ?? []) {
9660
- this.database.run(
9661
- "INSERT INTO consumer_checkpoints (consumer_id, sequence, event_id, updated_at) VALUES (?, ?, ?, ?)",
9662
- [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()
9663
10303
  );
9664
10304
  }
9665
- 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
+ }
9666
10375
  }
9667
10376
  /** Append the semantic durable event mirror and remove deleted Agent sidecars. */
9668
- mirrorState(state) {
10377
+ mirrorInitialState(state) {
9669
10378
  mkdirSync(this.archiveRoot, { recursive: true, mode: 448 });
9670
10379
  chmodSync(this.archiveRoot, 448);
9671
10380
  const currentAgents = new Set(state.agents.map((agent) => String(agent.id)));
@@ -9885,19 +10594,6 @@ function ownerLease(row) {
9885
10594
  expiresAt: new Date(row.expires_at).toISOString()
9886
10595
  };
9887
10596
  }
9888
- function parseEventCursor(lease, cursor) {
9889
- const value = String(cursor);
9890
- const separator = value.lastIndexOf(":");
9891
- const incarnation = separator < 0 ? "" : value.slice(0, separator);
9892
- const sequence = separator < 0 ? "" : value.slice(separator + 1);
9893
- const token = String(lease.token);
9894
- const ownerSuffix = `:${lease.epoch}:${lease.ownerId}`;
9895
- const expectedIncarnation = token.endsWith(ownerSuffix) ? token.slice(0, -ownerSuffix.length) : "";
9896
- if (incarnation !== expectedIncarnation || !/^\d+$/.test(sequence)) {
9897
- throw new RuntimeError("invalid_cursor", { cursorType: "event", reason: "wrong_store" });
9898
- }
9899
- return Number(sequence);
9900
- }
9901
10597
  function ownershipLost2(lease, row) {
9902
10598
  return new RuntimeError("runtime_ownership_lost", {
9903
10599
  reason: row.epoch > lease.epoch ? "epoch_advanced" : row.owner_id ? "released" : "released",