@anvia/studio 1.0.6 → 1.0.9

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
@@ -6480,10 +6480,11 @@ var InMemoryStudioStore = class {
6480
6480
  kind = "memory";
6481
6481
  compaction = {
6482
6482
  snapshot: ({ scope }) => {
6483
- const messages = this.sessions.get(scope.sessionId)?.messages ?? [];
6483
+ const session = this.sessions.get(scope.sessionId);
6484
+ const messages = session?.messages ?? [];
6484
6485
  return Promise.resolve({
6485
- revision: memoryRevision(messages),
6486
- messages: cloneMessages(messages)
6486
+ revision: String(session?.storeRevision ?? 0),
6487
+ messages: projectedMessages(messages, session?.compactionState)
6487
6488
  });
6488
6489
  },
6489
6490
  replacePrefix: (options) => this.replaceCompactionPrefix(options)
@@ -6504,6 +6505,7 @@ var InMemoryStudioStore = class {
6504
6505
  updatedAt: now,
6505
6506
  messageCount: 0,
6506
6507
  messages: [],
6508
+ storeRevision: 0,
6507
6509
  runs: [],
6508
6510
  logs: []
6509
6511
  };
@@ -6536,6 +6538,7 @@ var InMemoryStudioStore = class {
6536
6538
  const session = this.sessions.get(input.scope.sessionId);
6537
6539
  if (session !== void 0) {
6538
6540
  session.messages.push(...cloneMessages(input.messages));
6541
+ session.storeRevision += 1;
6539
6542
  session.messageCount = session.messages.length;
6540
6543
  session.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
6541
6544
  }
@@ -6545,6 +6548,8 @@ var InMemoryStudioStore = class {
6545
6548
  const session = this.sessions.get(scope.sessionId);
6546
6549
  if (session !== void 0) {
6547
6550
  session.messages = [];
6551
+ delete session.compactionState;
6552
+ session.storeRevision += 1;
6548
6553
  session.runs = [];
6549
6554
  session.messageCount = 0;
6550
6555
  session.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
@@ -6565,11 +6570,20 @@ var InMemoryStudioStore = class {
6565
6570
  throw new RangeError("messageCount must be a positive integer.");
6566
6571
  }
6567
6572
  const session = this.sessions.get(input.scope.sessionId);
6568
- if (session === void 0 || memoryRevision(session.messages) !== input.revision || input.messageCount > session.messages.length) {
6573
+ if (session === void 0 || String(session.storeRevision) !== input.revision) {
6569
6574
  return Promise.resolve({ status: "conflict" });
6570
6575
  }
6571
- session.messages.splice(0, input.messageCount, structuredClone(input.replacement));
6572
- session.messageCount = session.messages.length;
6576
+ const physicalPrefixCount = input.messageCount - (session.compactionState === void 0 ? 0 : 1);
6577
+ const activeMessageCount = session.messages.length - (session.compactionState?.summarizedThroughPosition ?? -1) - 1;
6578
+ if (physicalPrefixCount < 0 || physicalPrefixCount > activeMessageCount) {
6579
+ return Promise.resolve({ status: "conflict" });
6580
+ }
6581
+ session.compactionState = {
6582
+ generation: (session.compactionState?.generation ?? 0) + 1,
6583
+ summary: structuredClone(input.replacement),
6584
+ summarizedThroughPosition: (session.compactionState?.summarizedThroughPosition ?? -1) + physicalPrefixCount
6585
+ };
6586
+ session.storeRevision += 1;
6573
6587
  session.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
6574
6588
  return Promise.resolve({ status: "committed" });
6575
6589
  }
@@ -6713,8 +6727,12 @@ function studioRunId(scope) {
6713
6727
  const value = scope.metadata?.studioRunId;
6714
6728
  return typeof value === "string" && value.length > 0 ? value : void 0;
6715
6729
  }
6716
- function memoryRevision(messages) {
6717
- return JSON.stringify(messages);
6730
+ function projectedMessages(messages, state) {
6731
+ if (state === void 0) return cloneMessages(messages);
6732
+ return [
6733
+ structuredClone(state.summary),
6734
+ ...cloneMessages(messages.slice(state.summarizedThroughPosition + 1))
6735
+ ];
6718
6736
  }
6719
6737
  function serializeJsonError(error) {
6720
6738
  if (error instanceof Error) {
@@ -7496,6 +7514,50 @@ import { mkdirSync } from "fs";
7496
7514
  import { createRequire } from "module";
7497
7515
  import { dirname, resolve } from "path";
7498
7516
  import { isJsonValue as isJsonValue3 } from "@anvia/core/completion";
7517
+
7518
+ // src/storage/compaction-state.ts
7519
+ import { parseMessage } from "@anvia/core/completion";
7520
+ import { isMemoryCompactionMessage } from "@anvia/core/memory";
7521
+ function parseStudioCompactionState(value) {
7522
+ if (value === null) return void 0;
7523
+ const parsed = JSON.parse(value);
7524
+ if (typeof parsed !== "object" || parsed === null) {
7525
+ throw new Error("Stored Studio memory compaction state is invalid.");
7526
+ }
7527
+ const record = parsed;
7528
+ let summary;
7529
+ try {
7530
+ summary = parseMessage(record.summary);
7531
+ } catch (cause) {
7532
+ throw new Error("Stored Studio memory compaction state summary is invalid.", { cause });
7533
+ }
7534
+ if (record.version !== 1 || !Number.isSafeInteger(record.generation) || record.generation < 1 || !isMemoryCompactionMessage(summary) || !Number.isSafeInteger(record.summarizedThroughPosition) || record.summarizedThroughPosition < 0) {
7535
+ throw new Error("Stored Studio memory compaction state is invalid.");
7536
+ }
7537
+ return {
7538
+ version: 1,
7539
+ generation: record.generation,
7540
+ summary,
7541
+ summarizedThroughPosition: record.summarizedThroughPosition
7542
+ };
7543
+ }
7544
+ function projectStudioCompactionMessages(rows, state) {
7545
+ if (state === void 0) return rows.map((row) => row.message);
7546
+ return [state.summary, ...activeStudioCompactionMessages(rows, state).map((row) => row.message)];
7547
+ }
7548
+ function activeStudioCompactionMessages(rows, state) {
7549
+ if (state === void 0) return rows;
7550
+ const boundaryIndex = rows.findIndex((row) => row.position === state.summarizedThroughPosition);
7551
+ if (boundaryIndex === -1) {
7552
+ throw new Error("Stored Studio memory compaction state boundary is invalid.");
7553
+ }
7554
+ return rows.slice(boundaryIndex + 1);
7555
+ }
7556
+ function studioCompactionRevision(rows, state) {
7557
+ return JSON.stringify([state?.generation ?? 0, rows]);
7558
+ }
7559
+
7560
+ // src/storage/sqlite-store.ts
7499
7561
  var DatabaseSync;
7500
7562
  function createSqliteSessionStore(options = {}) {
7501
7563
  return new SqliteSessionStore(options.path ?? ":memory:");
@@ -7508,8 +7570,13 @@ var SqliteSessionStore = class {
7508
7570
  kind = "sqlite";
7509
7571
  compaction = {
7510
7572
  snapshot: ({ scope }) => {
7511
- const messages = this.listSessionMessages(scope.sessionId);
7512
- return Promise.resolve({ revision: memoryRevision2(messages), messages });
7573
+ const session = this.getCompactionSessionRow(scope.sessionId);
7574
+ const state = parseStudioCompactionState(session?.compaction_state_json ?? null);
7575
+ const rows = this.listCompactionMessages(scope.sessionId, state);
7576
+ return Promise.resolve({
7577
+ revision: studioCompactionRevision(rows, state),
7578
+ messages: projectStudioCompactionMessages(rows, state)
7579
+ });
7513
7580
  },
7514
7581
  replacePrefix: (options) => this.replaceCompactionPrefix(options)
7515
7582
  };
@@ -7628,7 +7695,8 @@ var SqliteSessionStore = class {
7628
7695
  db.exec("BEGIN IMMEDIATE");
7629
7696
  db.prepare(
7630
7697
  `UPDATE anvia_studio_sessions
7631
- SET updated_at = $updatedAt
7698
+ SET compaction_state_json = NULL,
7699
+ updated_at = $updatedAt
7632
7700
  WHERE id = $id`
7633
7701
  ).run({
7634
7702
  $id: scope.sessionId,
@@ -7668,26 +7736,41 @@ var SqliteSessionStore = class {
7668
7736
  const db = this.database();
7669
7737
  try {
7670
7738
  db.exec("BEGIN IMMEDIATE");
7671
- const messages = this.listSessionMessages(input.scope.sessionId);
7672
- if (memoryRevision2(messages) !== input.revision || input.messageCount > messages.length || this.getSessionRow(input.scope.sessionId) === void 0) {
7739
+ const session = this.getCompactionSessionRow(input.scope.sessionId);
7740
+ const state = parseStudioCompactionState(session?.compaction_state_json ?? null);
7741
+ const rows = this.listCompactionMessages(input.scope.sessionId, state);
7742
+ if (session === void 0 || studioCompactionRevision(rows, state) !== input.revision) {
7673
7743
  db.exec("ROLLBACK");
7674
7744
  return Promise.resolve({ status: "conflict" });
7675
7745
  }
7676
- db.prepare("DELETE FROM anvia_studio_session_messages WHERE session_id = $id").run({
7677
- $id: input.scope.sessionId
7678
- });
7746
+ const physicalPrefixCount = input.messageCount - (state === void 0 ? 0 : 1);
7747
+ const activeRows = activeStudioCompactionMessages(rows, state);
7748
+ if (physicalPrefixCount < 0 || physicalPrefixCount > activeRows.length) {
7749
+ db.exec("ROLLBACK");
7750
+ return Promise.resolve({ status: "conflict" });
7751
+ }
7752
+ const summarizedThroughPosition = physicalPrefixCount === 0 ? state?.summarizedThroughPosition : activeRows[physicalPrefixCount - 1]?.position;
7753
+ if (summarizedThroughPosition === void 0) {
7754
+ db.exec("ROLLBACK");
7755
+ return Promise.resolve({ status: "conflict" });
7756
+ }
7757
+ const nextState = {
7758
+ version: 1,
7759
+ generation: (state?.generation ?? 0) + 1,
7760
+ summary: input.replacement,
7761
+ summarizedThroughPosition
7762
+ };
7679
7763
  const updatedAt = (/* @__PURE__ */ new Date()).toISOString();
7680
- this.insertMessages(
7681
- input.scope.sessionId,
7682
- [input.replacement, ...messages.slice(input.messageCount)],
7683
- 0,
7684
- updatedAt
7685
- );
7686
7764
  db.prepare(
7687
7765
  `UPDATE anvia_studio_sessions
7688
- SET updated_at = $updatedAt
7766
+ SET compaction_state_json = $compactionState,
7767
+ updated_at = $updatedAt
7689
7768
  WHERE id = $id`
7690
- ).run({ $id: input.scope.sessionId, $updatedAt: updatedAt });
7769
+ ).run({
7770
+ $id: input.scope.sessionId,
7771
+ $compactionState: JSON.stringify(nextState),
7772
+ $updatedAt: updatedAt
7773
+ });
7691
7774
  db.exec("COMMIT");
7692
7775
  return Promise.resolve({ status: "committed" });
7693
7776
  } catch (error) {
@@ -8185,6 +8268,7 @@ var SqliteSessionStore = class {
8185
8268
  agent_id TEXT NOT NULL,
8186
8269
  title TEXT,
8187
8270
  metadata_json TEXT,
8271
+ compaction_state_json TEXT,
8188
8272
  created_at TEXT NOT NULL,
8189
8273
  updated_at TEXT NOT NULL
8190
8274
  ) STRICT;
@@ -8290,6 +8374,7 @@ var SqliteSessionStore = class {
8290
8374
  CREATE INDEX IF NOT EXISTS anvia_studio_traces_session_started_idx
8291
8375
  ON anvia_studio_traces(session_id, started_at DESC);
8292
8376
  `);
8377
+ ensureSessionCompactionStateColumn(db);
8293
8378
  ensureMessageMetadataColumn(db);
8294
8379
  this.db = db;
8295
8380
  return db;
@@ -8301,6 +8386,13 @@ var SqliteSessionStore = class {
8301
8386
  WHERE id = $id`
8302
8387
  ).get({ $id: id });
8303
8388
  }
8389
+ getCompactionSessionRow(id) {
8390
+ return this.database().prepare(
8391
+ `SELECT id, compaction_state_json
8392
+ FROM anvia_studio_sessions
8393
+ WHERE id = $id`
8394
+ ).get({ $id: id });
8395
+ }
8304
8396
  getSessionRun(sessionId, runId) {
8305
8397
  return this.database().prepare(
8306
8398
  `SELECT run_id, session_id, status, title, transcript_json, error_json, created_at, updated_at
@@ -8333,13 +8425,22 @@ var SqliteSessionStore = class {
8333
8425
  return row.next_sequence;
8334
8426
  }
8335
8427
  listSessionMessages(sessionId) {
8428
+ return this.listSessionMessageEntries(sessionId).map((entry) => entry.message);
8429
+ }
8430
+ listCompactionMessages(sessionId, state) {
8431
+ return this.listSessionMessageEntries(sessionId, state?.summarizedThroughPosition);
8432
+ }
8433
+ listSessionMessageEntries(sessionId, minimumIndex) {
8336
8434
  const db = this.database();
8435
+ const boundaryClause = minimumIndex === void 0 ? "" : "AND message_index >= $minimumIndex";
8436
+ const parameters = minimumIndex === void 0 ? { $sessionId: sessionId } : { $sessionId: sessionId, $minimumIndex: minimumIndex };
8337
8437
  const messageRows = db.prepare(
8338
8438
  `SELECT session_id, message_index, role, message_id, metadata_json, created_at
8339
8439
  FROM anvia_studio_session_messages
8340
8440
  WHERE session_id = $sessionId
8441
+ ${boundaryClause}
8341
8442
  ORDER BY message_index ASC`
8342
- ).all({ $sessionId: sessionId });
8443
+ ).all(parameters);
8343
8444
  if (messageRows.length === 0) {
8344
8445
  return [];
8345
8446
  }
@@ -8347,17 +8448,19 @@ var SqliteSessionStore = class {
8347
8448
  `SELECT session_id, message_index, part_index, type, part_json
8348
8449
  FROM anvia_studio_session_message_parts
8349
8450
  WHERE session_id = $sessionId
8451
+ ${boundaryClause}
8350
8452
  ORDER BY message_index ASC, part_index ASC`
8351
- ).all({ $sessionId: sessionId });
8453
+ ).all(parameters);
8352
8454
  const partsByMessage = /* @__PURE__ */ new Map();
8353
8455
  for (const partRow of partRows) {
8354
8456
  const parts = partsByMessage.get(partRow.message_index) ?? [];
8355
8457
  parts.push(partRow);
8356
8458
  partsByMessage.set(partRow.message_index, parts);
8357
8459
  }
8358
- return messageRows.map(
8359
- (row) => messageFromRows(row, partsByMessage.get(row.message_index) ?? [])
8360
- );
8460
+ return messageRows.map((row) => ({
8461
+ position: row.message_index,
8462
+ message: messageFromRows(row, partsByMessage.get(row.message_index) ?? [])
8463
+ }));
8361
8464
  }
8362
8465
  nextMessageIndex(sessionId) {
8363
8466
  const row = this.database().prepare(
@@ -8561,6 +8664,12 @@ function ensureMessageMetadataColumn(db) {
8561
8664
  db.exec("ALTER TABLE anvia_studio_session_messages ADD COLUMN metadata_json TEXT");
8562
8665
  }
8563
8666
  }
8667
+ function ensureSessionCompactionStateColumn(db) {
8668
+ const columns = db.prepare("PRAGMA table_info('anvia_studio_sessions')").all();
8669
+ if (!columns.some((column) => column.name === "compaction_state_json")) {
8670
+ db.exec("ALTER TABLE anvia_studio_sessions ADD COLUMN compaction_state_json TEXT");
8671
+ }
8672
+ }
8564
8673
  function loadDatabaseSync() {
8565
8674
  if (DatabaseSync !== void 0) {
8566
8675
  return DatabaseSync;
@@ -8624,9 +8733,6 @@ function studioRunId2(scope) {
8624
8733
  const value = scope.metadata?.studioRunId;
8625
8734
  return typeof value === "string" && value.length > 0 ? value : void 0;
8626
8735
  }
8627
- function memoryRevision2(messages) {
8628
- return JSON.stringify(messages);
8629
- }
8630
8736
  function serializeJsonError2(error) {
8631
8737
  if (error instanceof Error) {
8632
8738
  return {