@axiom-lattice/local-stores 3.1.0 → 3.1.2

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 (33) hide show
  1. package/.turbo/turbo-build.log +10 -10
  2. package/CHANGELOG.md +18 -0
  3. package/dist/index.d.mts +193 -22
  4. package/dist/index.d.ts +193 -22
  5. package/dist/index.js +1482 -242
  6. package/dist/index.js.map +1 -1
  7. package/dist/index.mjs +1479 -242
  8. package/dist/index.mjs.map +1 -1
  9. package/package.json +3 -3
  10. package/src/__tests__/LocalCapabilityBundleStore.test.ts +216 -0
  11. package/src/__tests__/LocalChannelBindingStore.test.ts +183 -0
  12. package/src/__tests__/LocalChannelInstallationStore.test.ts +23 -0
  13. package/src/__tests__/LocalProjectRoomStores.test.ts +356 -0
  14. package/src/__tests__/LocalProjectStore.test.ts +39 -2
  15. package/src/__tests__/LocalTaskStore.test.ts +58 -0
  16. package/src/__tests__/LocalTaskWorkItemStore.test.ts +199 -0
  17. package/src/__tests__/LocalThreadMessageQueueStore.test.ts +201 -0
  18. package/src/createLocalStoreConfig.ts +10 -0
  19. package/src/index.ts +5 -0
  20. package/src/migrations/migration.ts +1 -1
  21. package/src/stores/LocalCapabilityBundleStore.ts +53 -0
  22. package/src/stores/LocalChannelBindingStore.ts +75 -41
  23. package/src/stores/LocalChannelInstallationStore.ts +6 -5
  24. package/src/stores/LocalProjectBotMembershipStore.ts +105 -0
  25. package/src/stores/LocalProjectMembershipStore.ts +72 -0
  26. package/src/stores/LocalProjectRoomMessageStore.ts +63 -0
  27. package/src/stores/LocalProjectRoomStore.ts +37 -0
  28. package/src/stores/LocalProjectStore.ts +67 -14
  29. package/src/stores/LocalTaskStore.ts +82 -2
  30. package/src/stores/LocalTaskWorkItemStore.ts +136 -1
  31. package/src/stores/LocalThreadMessageQueueStore.ts +136 -28
  32. package/src/stores/project-room-schema.ts +243 -0
  33. package/src/stores/room-channel-binding-index.ts +60 -0
package/dist/index.mjs CHANGED
@@ -81,11 +81,11 @@ var StatementWrapper = class {
81
81
  if (params.length > 0) {
82
82
  stmt.bind(params);
83
83
  }
84
- const rows = [];
84
+ const rows2 = [];
85
85
  while (stmt.step()) {
86
- rows.push(stmt.getAsObject());
86
+ rows2.push(stmt.getAsObject());
87
87
  }
88
- return rows;
88
+ return rows2;
89
89
  } finally {
90
90
  stmt.free();
91
91
  }
@@ -160,8 +160,8 @@ function closeDatabase() {
160
160
  _db = null;
161
161
  }
162
162
  }
163
- function ensureTable(db, ddl) {
164
- db.exec(ddl);
163
+ function ensureTable(db, ddl2) {
164
+ db.exec(ddl2);
165
165
  }
166
166
  function nowISO() {
167
167
  return (/* @__PURE__ */ new Date()).toISOString();
@@ -205,7 +205,10 @@ var MigrationManager = class {
205
205
  this.db.run(
206
206
  `INSERT INTO lattice_schema_migrations (store_name, name, version, applied_at)
207
207
  VALUES (?, ?, ?, ?)`,
208
- [this.storeName, m.name, m.version, (/* @__PURE__ */ new Date()).toISOString()]
208
+ this.storeName,
209
+ m.name,
210
+ m.version,
211
+ (/* @__PURE__ */ new Date()).toISOString()
209
212
  );
210
213
  }
211
214
  }
@@ -255,10 +258,10 @@ var MigrationManager = class {
255
258
  `);
256
259
  }
257
260
  getAppliedNames() {
258
- const rows = this.db.prepare(
261
+ const rows2 = this.db.prepare(
259
262
  `SELECT name FROM lattice_schema_migrations WHERE store_name = ? ORDER BY version`
260
263
  ).all(this.storeName);
261
- return new Set(rows.map((r) => r.name));
264
+ return new Set(rows2.map((r) => r.name));
262
265
  }
263
266
  };
264
267
 
@@ -293,10 +296,10 @@ var LocalThreadStore = class {
293
296
  mm.migrate();
294
297
  }
295
298
  async getThreadsByAssistantId(tenantId, assistantId, metadataFilter) {
296
- const rows = this.db.prepare(
299
+ const rows2 = this.db.prepare(
297
300
  `SELECT * FROM lt_threads WHERE tenant_id = ? AND assistant_id = ? ORDER BY created_at DESC`
298
301
  ).all(tenantId, assistantId);
299
- let threads = rows.map(mapRowToThread);
302
+ let threads = rows2.map(mapRowToThread);
300
303
  if (metadataFilter && Object.keys(metadataFilter).length > 0) {
301
304
  threads = threads.filter(
302
305
  (t) => Object.entries(metadataFilter).every(
@@ -393,10 +396,10 @@ var LocalAssistantStore = class {
393
396
  ensureTable(db, DDL2);
394
397
  }
395
398
  async getAllAssistants(tenantId) {
396
- const rows = this.db.prepare(
399
+ const rows2 = this.db.prepare(
397
400
  `SELECT * FROM lt_assistants WHERE tenant_id = ? ORDER BY created_at DESC`
398
401
  ).all(tenantId);
399
- return rows.map(mapRowToAssistant);
402
+ return rows2.map(mapRowToAssistant);
400
403
  }
401
404
  async getAssistantById(tenantId, id) {
402
405
  const row = this.db.prepare(
@@ -509,10 +512,10 @@ var LocalWorkspaceStore = class {
509
512
  ensureTable(db, DDL3);
510
513
  }
511
514
  async getAllWorkspaces(tenantId) {
512
- const rows = this.db.prepare(
515
+ const rows2 = this.db.prepare(
513
516
  `SELECT * FROM lt_workspaces WHERE tenant_id = ? ORDER BY created_at DESC`
514
517
  ).all(tenantId);
515
- return rows.map(mapRowToWorkspace);
518
+ return rows2.map(mapRowToWorkspace);
516
519
  }
517
520
  async getWorkspaceById(tenantId, id) {
518
521
  const row = this.db.prepare(
@@ -588,6 +591,7 @@ function mapRowToWorkspace(row) {
588
591
  }
589
592
 
590
593
  // src/stores/LocalProjectStore.ts
594
+ import { assertGenericProjectConfig } from "@axiom-lattice/protocols";
591
595
  var DDL4 = `
592
596
  CREATE TABLE IF NOT EXISTS lt_projects (
593
597
  id TEXT NOT NULL,
@@ -611,15 +615,15 @@ var LocalProjectStore = class {
611
615
  }
612
616
  async getProjectsByWorkspace(tenantId, workspaceId, filter) {
613
617
  if (filter?.kind !== void 0) {
614
- const rows2 = this.db.prepare(
618
+ const rows3 = this.db.prepare(
615
619
  `SELECT * FROM lt_projects WHERE tenant_id = ? AND workspace_id = ? AND kind = ? ORDER BY created_at DESC`
616
620
  ).all(tenantId, workspaceId, filter.kind);
617
- return rows2.map(mapRowToProject);
621
+ return rows3.map(mapRowToProject);
618
622
  }
619
- const rows = this.db.prepare(
623
+ const rows2 = this.db.prepare(
620
624
  `SELECT * FROM lt_projects WHERE tenant_id = ? AND workspace_id = ? ORDER BY created_at DESC`
621
625
  ).all(tenantId, workspaceId);
622
- return rows.map(mapRowToProject);
626
+ return rows2.map(mapRowToProject);
623
627
  }
624
628
  async getProjectById(tenantId, id) {
625
629
  const row = this.db.prepare(
@@ -628,6 +632,7 @@ var LocalProjectStore = class {
628
632
  return row ? mapRowToProject(row) : null;
629
633
  }
630
634
  async createProject(tenantId, workspaceId, id, data) {
635
+ assertGenericProjectConfig(data.config);
631
636
  const now = nowISO();
632
637
  const kind = data.kind || "business";
633
638
  this.db.prepare(
@@ -637,23 +642,18 @@ var LocalProjectStore = class {
637
642
  workspace_id = excluded.workspace_id,
638
643
  name = excluded.name,
639
644
  description = excluded.description,
640
- config = excluded.config,
645
+ config = CASE
646
+ WHEN json_type(lt_projects.config, '$.capabilityBundleIds') IS NOT NULL
647
+ THEN json_set(COALESCE(excluded.config, '{}'), '$.capabilityBundleIds', json_extract(lt_projects.config, '$.capabilityBundleIds'))
648
+ ELSE excluded.config
649
+ END,
641
650
  kind = excluded.kind,
642
651
  updated_at = excluded.updated_at`
643
652
  ).run(id, tenantId, workspaceId, data.name, data.description || null, data.config ? JSON.stringify(data.config) : null, kind, now, now);
644
- return {
645
- id,
646
- tenantId,
647
- workspaceId,
648
- name: data.name,
649
- description: data.description,
650
- config: data.config,
651
- kind,
652
- createdAt: parseISO(now),
653
- updatedAt: parseISO(now)
654
- };
653
+ return await this.getProjectById(tenantId, id);
655
654
  }
656
655
  async updateProject(tenantId, id, updates) {
656
+ assertGenericProjectConfig(updates.config);
657
657
  const existing = await this.getProjectById(tenantId, id);
658
658
  if (!existing) return null;
659
659
  const setClauses = [];
@@ -667,8 +667,13 @@ var LocalProjectStore = class {
667
667
  values.push(updates.description || null);
668
668
  }
669
669
  if (updates.config !== void 0) {
670
- setClauses.push("config = ?");
671
- values.push(updates.config ? JSON.stringify(updates.config) : null);
670
+ setClauses.push(`config = CASE
671
+ WHEN json_type(config, '$.capabilityBundleIds') IS NOT NULL
672
+ THEN json_set(json(?), '$.capabilityBundleIds', json_extract(config, '$.capabilityBundleIds'))
673
+ ELSE ?
674
+ END`);
675
+ const config = JSON.stringify(updates.config ?? {});
676
+ values.push(config, config);
672
677
  }
673
678
  if (updates.kind !== void 0) {
674
679
  setClauses.push("kind = ?");
@@ -690,11 +695,57 @@ var LocalProjectStore = class {
690
695
  ).run(tenantId, id);
691
696
  return result.changes > 0;
692
697
  }
698
+ async updateCapabilityBundleIds(tenantId, projectId, bundleIds, expectedRevisions = {}) {
699
+ const now = nowISO();
700
+ const result = this.db.prepare(
701
+ `UPDATE lt_projects
702
+ SET config = json_set(COALESCE(config, '{}'), '$.capabilityBundleIds', json(?)), updated_at = ?
703
+ WHERE tenant_id = ? AND id = ?
704
+ AND NOT EXISTS (
705
+ SELECT 1 FROM json_each(?) AS requested
706
+ WHERE NOT EXISTS (
707
+ SELECT 1 FROM lt_capability_bundles
708
+ WHERE tenant_id = ? AND id = requested.value
709
+ )
710
+ )
711
+ AND NOT EXISTS (
712
+ SELECT 1 FROM json_each(?) AS expected
713
+ WHERE EXISTS (
714
+ SELECT 1 FROM lt_capability_bundles
715
+ WHERE tenant_id = ? AND id = expected.key AND updated_at <> expected.value
716
+ )
717
+ )`
718
+ ).run(JSON.stringify(bundleIds), now, tenantId, projectId, JSON.stringify(bundleIds), tenantId, JSON.stringify(expectedRevisions), tenantId);
719
+ if (result.changes > 0) {
720
+ const project = await this.getProjectById(tenantId, projectId);
721
+ return project ? { status: "updated", project } : { status: "project_not_found" };
722
+ }
723
+ const revisionMismatch = this.db.prepare(
724
+ "SELECT 1 FROM lt_capability_bundles WHERE tenant_id = ? AND id IN (SELECT key FROM json_each(?)) AND updated_at <> (SELECT value FROM json_each(?) WHERE key = lt_capability_bundles.id) LIMIT 1"
725
+ ).get(tenantId, JSON.stringify(expectedRevisions), JSON.stringify(expectedRevisions));
726
+ if (revisionMismatch) return { status: "bundle_conflict" };
727
+ return await this.getProjectById(tenantId, projectId) ? { status: "bundle_not_found" } : { status: "project_not_found" };
728
+ }
729
+ async isCapabilityBundleReferenced(tenantId, bundleId) {
730
+ const rows2 = this.db.prepare("SELECT config FROM lt_projects WHERE tenant_id = ?").all(tenantId);
731
+ return rows2.some((row) => {
732
+ if (!row.config) return false;
733
+ let config;
734
+ try {
735
+ config = JSON.parse(row.config);
736
+ } catch {
737
+ return false;
738
+ }
739
+ if (config === null || typeof config !== "object" || Array.isArray(config)) return false;
740
+ const ids = config.capabilityBundleIds;
741
+ return Array.isArray(ids) && ids.every((id) => typeof id === "string") && ids.includes(bundleId);
742
+ });
743
+ }
693
744
  /** Add a column if it does not exist (SQLite version compatible). */
694
- ensureColumn(table, column, ddl) {
745
+ ensureColumn(table, column, ddl2) {
695
746
  const cols = this.db.prepare(`PRAGMA table_info(${table})`).all();
696
747
  if (!cols.some((c) => c.name === column)) {
697
- this.db.exec(`ALTER TABLE ${table} ADD COLUMN ${ddl}`);
748
+ this.db.exec(`ALTER TABLE ${table} ADD COLUMN ${ddl2}`);
698
749
  }
699
750
  }
700
751
  };
@@ -712,6 +763,640 @@ function mapRowToProject(row) {
712
763
  };
713
764
  }
714
765
 
766
+ // src/stores/project-room-schema.ts
767
+ var schemaMarker = Symbol("projectRoomSchema");
768
+ var activeTransactions = /* @__PURE__ */ new WeakSet();
769
+ var migrationName = "create_project_room_tables";
770
+ var migrationTableDdl = `CREATE TABLE IF NOT EXISTS lattice_schema_migrations (
771
+ store_name TEXT NOT NULL, name TEXT NOT NULL, version INTEGER NOT NULL, applied_at TEXT NOT NULL,
772
+ PRIMARY KEY (store_name, name)
773
+ )`;
774
+ var ddl = `
775
+ CREATE TABLE IF NOT EXISTS lt_project_rooms (
776
+ id TEXT NOT NULL, tenant_id TEXT NOT NULL, workspace_id TEXT NOT NULL, project_id TEXT NOT NULL,
777
+ type TEXT NOT NULL CHECK (type = 'main'), name TEXT NOT NULL, created_at TEXT NOT NULL, updated_at TEXT NOT NULL,
778
+ PRIMARY KEY (tenant_id, id), UNIQUE (tenant_id, project_id, type)
779
+ );
780
+ CREATE TABLE IF NOT EXISTS lt_project_memberships (
781
+ id TEXT NOT NULL, tenant_id TEXT NOT NULL, project_id TEXT NOT NULL, user_id TEXT NOT NULL,
782
+ role TEXT NOT NULL CHECK (role IN ('owner','admin','member','viewer')),
783
+ status TEXT NOT NULL CHECK (status IN ('active','removed')), joined_at TEXT NOT NULL, updated_at TEXT NOT NULL,
784
+ PRIMARY KEY (tenant_id, id), UNIQUE (tenant_id, project_id, user_id)
785
+ );
786
+ CREATE INDEX IF NOT EXISTS idx_lt_project_memberships_project ON lt_project_memberships (tenant_id, project_id);
787
+ CREATE TABLE IF NOT EXISTS lt_project_bot_memberships (
788
+ id TEXT NOT NULL, tenant_id TEXT NOT NULL, workspace_id TEXT NOT NULL, project_id TEXT NOT NULL,
789
+ room_id TEXT NOT NULL, assistant_id TEXT NOT NULL, role TEXT NOT NULL CHECK (role IN ('coordinator','specialist')),
790
+ title TEXT NOT NULL, responsibility TEXT, mention_name TEXT NOT NULL,
791
+ status TEXT NOT NULL CHECK (status IN ('active','paused','removed')), room_thread_id TEXT NOT NULL,
792
+ joined_at TEXT NOT NULL, updated_at TEXT NOT NULL, PRIMARY KEY (tenant_id, id),
793
+ UNIQUE (tenant_id, project_id, assistant_id)
794
+ );
795
+ CREATE UNIQUE INDEX IF NOT EXISTS uq_lt_project_bot_memberships_coordinator
796
+ ON lt_project_bot_memberships (tenant_id, project_id) WHERE role = 'coordinator' AND status IN ('active','paused');
797
+ CREATE UNIQUE INDEX IF NOT EXISTS uq_lt_project_bot_memberships_mention
798
+ ON lt_project_bot_memberships (tenant_id, room_id, mention_name) WHERE status IN ('active','paused');
799
+ CREATE INDEX IF NOT EXISTS idx_lt_project_bot_memberships_project ON lt_project_bot_memberships (tenant_id, project_id);
800
+ CREATE INDEX IF NOT EXISTS idx_lt_project_bot_memberships_room ON lt_project_bot_memberships (tenant_id, room_id);
801
+ CREATE TABLE IF NOT EXISTS lt_project_room_messages (
802
+ id TEXT NOT NULL, tenant_id TEXT NOT NULL, workspace_id TEXT NOT NULL, project_id TEXT NOT NULL, room_id TEXT NOT NULL,
803
+ author TEXT NOT NULL, content TEXT NOT NULL, mentions TEXT NOT NULL DEFAULT '[]', reply_to_message_id TEXT,
804
+ source TEXT NOT NULL CHECK (source IN ('user','agent','task','routine','system')), source_id TEXT,
805
+ idempotency_key TEXT, created_at TEXT NOT NULL, PRIMARY KEY (tenant_id, id)
806
+ );
807
+ CREATE UNIQUE INDEX IF NOT EXISTS uq_lt_project_room_messages_idempotency
808
+ ON lt_project_room_messages (tenant_id, room_id, idempotency_key) WHERE idempotency_key IS NOT NULL;
809
+ CREATE INDEX IF NOT EXISTS idx_lt_project_room_messages_room_created
810
+ ON lt_project_room_messages (tenant_id, room_id, created_at DESC, id DESC);
811
+ `;
812
+ var tableColumns = {
813
+ lt_project_rooms: ["id", "tenant_id", "workspace_id", "project_id", "type", "name", "created_at", "updated_at"],
814
+ lt_project_memberships: ["id", "tenant_id", "project_id", "user_id", "role", "status", "joined_at", "updated_at"],
815
+ lt_project_bot_memberships: ["id", "tenant_id", "workspace_id", "project_id", "room_id", "assistant_id", "role", "title", "responsibility", "mention_name", "status", "room_thread_id", "joined_at", "updated_at"],
816
+ lt_project_room_messages: ["id", "tenant_id", "workspace_id", "project_id", "room_id", "author", "content", "mentions", "reply_to_message_id", "source", "source_id", "idempotency_key", "created_at"]
817
+ };
818
+ var indexes = {
819
+ idx_lt_project_memberships_project: { table: "lt_project_memberships", columns: ["tenant_id", "project_id"], unique: false, partial: false },
820
+ uq_lt_project_bot_memberships_coordinator: { table: "lt_project_bot_memberships", columns: ["tenant_id", "project_id"], unique: true, partial: true, sql: ["role = 'coordinator'", "status in ('active','paused')"] },
821
+ uq_lt_project_bot_memberships_mention: { table: "lt_project_bot_memberships", columns: ["tenant_id", "room_id", "mention_name"], unique: true, partial: true, sql: ["status in ('active','paused')"] },
822
+ idx_lt_project_bot_memberships_project: { table: "lt_project_bot_memberships", columns: ["tenant_id", "project_id"], unique: false, partial: false },
823
+ idx_lt_project_bot_memberships_room: { table: "lt_project_bot_memberships", columns: ["tenant_id", "room_id"], unique: false, partial: false },
824
+ uq_lt_project_room_messages_idempotency: { table: "lt_project_room_messages", columns: ["tenant_id", "room_id", "idempotency_key"], unique: true, partial: true, sql: ["idempotency_key is not null"] },
825
+ idx_lt_project_room_messages_room_created: { table: "lt_project_room_messages", columns: ["tenant_id", "room_id", "created_at", "id"], unique: false, partial: false, sql: ["created_at desc", "id desc"] }
826
+ };
827
+ var uniqueConstraints = {
828
+ lt_project_rooms: [["tenant_id", "id"], ["tenant_id", "project_id", "type"]],
829
+ lt_project_memberships: [["tenant_id", "id"], ["tenant_id", "project_id", "user_id"]],
830
+ lt_project_bot_memberships: [["tenant_id", "id"], ["tenant_id", "project_id", "assistant_id"]],
831
+ lt_project_room_messages: [["tenant_id", "id"]]
832
+ };
833
+ var checkConstraints = {
834
+ lt_project_rooms: [{ name: "type", fragment: "type='main'" }],
835
+ lt_project_memberships: [
836
+ { name: "role", fragment: "role in ('owner','admin','member','viewer')" },
837
+ { name: "status", fragment: "status in ('active','removed')" }
838
+ ],
839
+ lt_project_bot_memberships: [
840
+ { name: "role", fragment: "role in ('coordinator','specialist')" },
841
+ { name: "status", fragment: "status in ('active','paused','removed')" }
842
+ ],
843
+ lt_project_room_messages: [{ name: "source", fragment: "source in ('user','agent','task','routine','system')" }]
844
+ };
845
+ function rows(db, sql) {
846
+ const result = db.getRawDb().exec(sql);
847
+ if (!result[0]) return [];
848
+ return result[0].values.map((values) => Object.fromEntries(result[0].columns.map((column, index) => [column, values[index]])));
849
+ }
850
+ function normalizeSql(value) {
851
+ return value.toLowerCase().replace(/["`]/g, "").replace(/\[|\]/g, "").replace(/\s+/g, " ").replace(/\s*([=,])\s*/g, "$1").replace(/[()]/g, "").trim();
852
+ }
853
+ function extractCheckExpressions(sql) {
854
+ const checks = [];
855
+ let quoted = false;
856
+ for (let index = 0; index < sql.length; index += 1) {
857
+ if (sql[index] === "'") {
858
+ if (quoted && sql[index + 1] === "'") {
859
+ index += 1;
860
+ continue;
861
+ }
862
+ quoted = !quoted;
863
+ continue;
864
+ }
865
+ if (quoted || sql.slice(index, index + 5).toLowerCase() !== "check") continue;
866
+ const before = sql[index - 1];
867
+ const after = sql[index + 5];
868
+ if (before && /[a-z0-9_]/i.test(before) || after && /[a-z0-9_]/i.test(after)) continue;
869
+ let opening = index + 5;
870
+ while (/\s/.test(sql[opening] ?? "")) opening += 1;
871
+ if (sql[opening] !== "(") continue;
872
+ let depth = 1;
873
+ let expressionQuoted = false;
874
+ for (let cursor = opening + 1; cursor < sql.length; cursor += 1) {
875
+ if (sql[cursor] === "'") {
876
+ if (expressionQuoted && sql[cursor + 1] === "'") {
877
+ cursor += 1;
878
+ continue;
879
+ }
880
+ expressionQuoted = !expressionQuoted;
881
+ } else if (!expressionQuoted && sql[cursor] === "(") {
882
+ depth += 1;
883
+ } else if (!expressionQuoted && sql[cursor] === ")") {
884
+ depth -= 1;
885
+ if (depth === 0) {
886
+ checks.push(sql.slice(opening + 1, cursor));
887
+ index = cursor;
888
+ break;
889
+ }
890
+ }
891
+ }
892
+ }
893
+ return checks;
894
+ }
895
+ function validateProjectRoomSchema(db) {
896
+ for (const [table, expected] of Object.entries(tableColumns)) {
897
+ const actual = rows(db, `PRAGMA table_info(${table})`);
898
+ if (actual.map((column) => column.name).join(",") !== expected.join(",") || actual.some((column) => column.type !== "TEXT") || actual.some((column) => column.name !== "responsibility" && column.name !== "reply_to_message_id" && column.name !== "source_id" && column.name !== "idempotency_key" && column.notnull !== 1)) {
899
+ throw new Error(`Invalid project room schema: table '${table}' has incompatible columns`);
900
+ }
901
+ const uniqueIndexes = rows(db, `PRAGMA index_list(${table})`).filter((index) => index.unique === 1);
902
+ const signatures = uniqueIndexes.map((index) => rows(db, `PRAGMA index_info(${String(index.name)})`).map((column) => column.name).join(","));
903
+ if (uniqueConstraints[table].some((constraint) => !signatures.includes(constraint.join(",")))) {
904
+ throw new Error(`Invalid project room schema: table '${table}' is missing required uniqueness`);
905
+ }
906
+ const definition = rows(db, `SELECT sql FROM sqlite_master WHERE type = 'table' AND name = '${table}'`)[0]?.sql;
907
+ const checks = typeof definition === "string" ? extractCheckExpressions(definition).map(normalizeSql) : [];
908
+ const missingCheck = checkConstraints[table].find((constraint) => !checks.includes(normalizeSql(constraint.fragment)));
909
+ if (missingCheck) {
910
+ throw new Error(`Invalid project room schema: table '${table}' is missing required CHECK constraint '${missingCheck.name}'`);
911
+ }
912
+ }
913
+ for (const [name, expected] of Object.entries(indexes)) {
914
+ const listed = rows(db, `PRAGMA index_list(${expected.table})`).find((index) => index.name === name);
915
+ const columns5 = rows(db, `PRAGMA index_info(${name})`).map((column) => column.name);
916
+ const definition = rows(db, `SELECT sql FROM sqlite_master WHERE type = 'index' AND name = '${name}'`)[0]?.sql;
917
+ const normalized = typeof definition === "string" ? definition.toLowerCase().replace(/\s+/g, " ") : "";
918
+ if (!listed || listed.unique !== Number(expected.unique) || listed.partial !== Number(expected.partial) || columns5.join(",") !== expected.columns.join(",") || expected.sql?.some((fragment) => !normalized.includes(fragment))) {
919
+ throw new Error(`Invalid project room schema: index '${name}' is incompatible`);
920
+ }
921
+ }
922
+ }
923
+ function ensureProjectRoomSchema(db) {
924
+ const marked = db;
925
+ if (marked[schemaMarker]) return;
926
+ const raw = db.getRawDb();
927
+ const hasMigrationTable = rows(db, "SELECT name FROM sqlite_master WHERE type = 'table' AND name = 'lattice_schema_migrations'").length > 0;
928
+ const applied = hasMigrationTable ? rows(db, `SELECT name, version FROM lattice_schema_migrations WHERE store_name = 'project_room' AND name = '${migrationName}'`)[0] : void 0;
929
+ if (applied) {
930
+ if (applied.version !== 1) throw new Error(`Invalid project room schema migration version: ${String(applied.version)}`);
931
+ validateProjectRoomSchema(db);
932
+ marked[schemaMarker] = true;
933
+ return;
934
+ }
935
+ raw.exec("BEGIN IMMEDIATE");
936
+ try {
937
+ raw.exec(migrationTableDdl);
938
+ raw.exec(ddl);
939
+ validateProjectRoomSchema(db);
940
+ raw.run("INSERT INTO lattice_schema_migrations (store_name, name, version, applied_at) VALUES (?, ?, ?, ?)", ["project_room", migrationName, 1, (/* @__PURE__ */ new Date()).toISOString()]);
941
+ raw.exec("COMMIT");
942
+ } catch (error) {
943
+ try {
944
+ raw.exec("ROLLBACK");
945
+ } catch {
946
+ }
947
+ throw error;
948
+ }
949
+ db.save();
950
+ marked[schemaMarker] = true;
951
+ }
952
+ function transaction(db, work) {
953
+ if (activeTransactions.has(db)) throw new Error("Nested local store transactions are not supported");
954
+ activeTransactions.add(db);
955
+ let began = false;
956
+ try {
957
+ db.exec("BEGIN IMMEDIATE");
958
+ began = true;
959
+ const result = work();
960
+ db.exec("COMMIT");
961
+ db.save();
962
+ return result;
963
+ } catch (error) {
964
+ if (began) {
965
+ try {
966
+ db.exec("ROLLBACK");
967
+ } catch {
968
+ }
969
+ db.save();
970
+ }
971
+ throw error;
972
+ } finally {
973
+ activeTransactions.delete(db);
974
+ }
975
+ }
976
+ function write(db, sql, params) {
977
+ const raw = db.getRawDb();
978
+ raw.run(sql, params);
979
+ return raw.getRowsModified();
980
+ }
981
+ function timestamp(previous) {
982
+ const previousTime = previous === void 0 ? Number.NaN : Date.parse(previous);
983
+ const now = Date.now();
984
+ return new Date(Math.max(now, Number.isFinite(previousTime) ? previousTime + 1 : now)).toISOString();
985
+ }
986
+ function validDate(value) {
987
+ return value instanceof Date && !Number.isNaN(value.getTime());
988
+ }
989
+ function isUniqueError(error) {
990
+ return error instanceof Error && error.message.includes("UNIQUE constraint failed");
991
+ }
992
+
993
+ // src/stores/LocalProjectRoomStore.ts
994
+ var columns = "id, tenant_id, workspace_id, project_id, type, name, created_at, updated_at";
995
+ function mapRow(row) {
996
+ const createdAt = typeof row.created_at === "string" ? new Date(row.created_at) : void 0;
997
+ const updatedAt = typeof row.updated_at === "string" ? new Date(row.updated_at) : void 0;
998
+ if (typeof row.id !== "string" || typeof row.tenant_id !== "string" || typeof row.workspace_id !== "string" || typeof row.project_id !== "string" || row.type !== "main" || typeof row.name !== "string" || !validDate(createdAt) || !validDate(updatedAt)) throw new Error("Invalid project room row");
999
+ return {
1000
+ id: row.id,
1001
+ tenantId: row.tenant_id,
1002
+ workspaceId: row.workspace_id,
1003
+ projectId: row.project_id,
1004
+ type: "main",
1005
+ name: row.name,
1006
+ createdAt,
1007
+ updatedAt
1008
+ };
1009
+ }
1010
+ var LocalProjectRoomStore = class {
1011
+ constructor(db) {
1012
+ this.db = db;
1013
+ ensureProjectRoomSchema(db);
1014
+ }
1015
+ async ensureMainRoom(input) {
1016
+ return transaction(this.db, () => {
1017
+ const existing = this.getRow(input.tenantId, input.projectId);
1018
+ if (existing) return mapRow(existing);
1019
+ const now = timestamp();
1020
+ write(this.db, "INSERT INTO lt_project_rooms (id, tenant_id, workspace_id, project_id, type, name, created_at, updated_at) VALUES (?, ?, ?, ?, 'main', ?, ?, ?)", [input.id, input.tenantId, input.workspaceId, input.projectId, input.name, now, now]);
1021
+ return mapRow(this.getRow(input.tenantId, input.projectId));
1022
+ });
1023
+ }
1024
+ async getMainRoom(tenantId, projectId) {
1025
+ const row = this.getRow(tenantId, projectId);
1026
+ return row ? mapRow(row) : null;
1027
+ }
1028
+ getRow(tenantId, projectId) {
1029
+ return this.db.prepare(`SELECT ${columns} FROM lt_project_rooms WHERE tenant_id = ? AND project_id = ? AND type = 'main'`).get(tenantId, projectId);
1030
+ }
1031
+ };
1032
+
1033
+ // src/stores/LocalProjectMembershipStore.ts
1034
+ var columns2 = "id, tenant_id, project_id, user_id, role, status, joined_at, updated_at";
1035
+ var DuplicateProjectMembershipError = class extends Error {
1036
+ constructor() {
1037
+ super("Project membership already exists for tenant, project, and user");
1038
+ this.name = "DuplicateProjectMembershipError";
1039
+ }
1040
+ };
1041
+ var ProjectMembershipIdConflictError = class extends Error {
1042
+ constructor() {
1043
+ super("Project membership ID already exists for tenant");
1044
+ this.name = "ProjectMembershipIdConflictError";
1045
+ }
1046
+ };
1047
+ function role(value) {
1048
+ return value === "owner" || value === "admin" || value === "member" || value === "viewer";
1049
+ }
1050
+ function mapRow2(row) {
1051
+ const joinedAt = typeof row.joined_at === "string" ? new Date(row.joined_at) : void 0;
1052
+ const updatedAt = typeof row.updated_at === "string" ? new Date(row.updated_at) : void 0;
1053
+ if (typeof row.id !== "string" || typeof row.tenant_id !== "string" || typeof row.project_id !== "string" || typeof row.user_id !== "string" || !role(row.role) || row.status !== "active" && row.status !== "removed" || !validDate(joinedAt) || !validDate(updatedAt)) throw new Error("Invalid project membership row");
1054
+ return {
1055
+ id: row.id,
1056
+ tenantId: row.tenant_id,
1057
+ projectId: row.project_id,
1058
+ userId: row.user_id,
1059
+ role: row.role,
1060
+ status: row.status,
1061
+ joinedAt,
1062
+ updatedAt
1063
+ };
1064
+ }
1065
+ var LocalProjectMembershipStore = class {
1066
+ constructor(db) {
1067
+ this.db = db;
1068
+ ensureProjectRoomSchema(db);
1069
+ }
1070
+ async list(tenantId, projectId) {
1071
+ return this.db.prepare(`SELECT ${columns2} FROM lt_project_memberships WHERE tenant_id = ? AND project_id = ? ORDER BY joined_at ASC, id ASC`).all(tenantId, projectId).map(mapRow2);
1072
+ }
1073
+ async findByUser(tenantId, projectId, userId) {
1074
+ const row = this.db.prepare(`SELECT ${columns2} FROM lt_project_memberships WHERE tenant_id = ? AND project_id = ? AND user_id = ?`).get(tenantId, projectId, userId);
1075
+ return row ? mapRow2(row) : null;
1076
+ }
1077
+ async create(input) {
1078
+ return transaction(this.db, () => {
1079
+ if (this.byId(input.tenantId, input.id)) throw new ProjectMembershipIdConflictError();
1080
+ const now = timestamp();
1081
+ try {
1082
+ write(this.db, "INSERT INTO lt_project_memberships (id, tenant_id, project_id, user_id, role, status, joined_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", [input.id, input.tenantId, input.projectId, input.userId, input.role, input.status, now, now]);
1083
+ } catch (error) {
1084
+ if (isUniqueError(error)) throw this.classifyUnique(input.tenantId, input.id, input.projectId, input.userId);
1085
+ throw error;
1086
+ }
1087
+ return mapRow2(this.byId(input.tenantId, input.id));
1088
+ });
1089
+ }
1090
+ async createInitialOwner(input) {
1091
+ return transaction(this.db, () => {
1092
+ const rows2 = this.db.prepare(`SELECT ${columns2} FROM lt_project_memberships WHERE tenant_id = ? AND project_id = ?`).all(input.tenantId, input.projectId);
1093
+ if (rows2.length > 0) {
1094
+ const existing = rows2.map(mapRow2).find((item) => item.userId === input.userId && item.role === "owner" && item.status === "active");
1095
+ return existing ? { kind: "existing", membership: existing } : { kind: "already_initialized" };
1096
+ }
1097
+ if (this.byId(input.tenantId, input.id)) throw new ProjectMembershipIdConflictError();
1098
+ const now = timestamp();
1099
+ try {
1100
+ write(this.db, "INSERT INTO lt_project_memberships (id, tenant_id, project_id, user_id, role, status, joined_at, updated_at) VALUES (?, ?, ?, ?, 'owner', 'active', ?, ?)", [input.id, input.tenantId, input.projectId, input.userId, now, now]);
1101
+ } catch (error) {
1102
+ if (isUniqueError(error)) throw this.classifyUnique(input.tenantId, input.id, input.projectId, input.userId);
1103
+ throw error;
1104
+ }
1105
+ return { kind: "created", membership: mapRow2(this.byId(input.tenantId, input.id)) };
1106
+ });
1107
+ }
1108
+ async updateRole(input) {
1109
+ return this.mutate(input.tenantId, input.id, input.expectedUpdatedAt, input.role, false);
1110
+ }
1111
+ async remove(input) {
1112
+ return this.mutate(input.tenantId, input.id, input.expectedUpdatedAt, void 0, true);
1113
+ }
1114
+ mutate(tenantId, id, expected, nextRole, remove) {
1115
+ return transaction(this.db, () => {
1116
+ const row = this.byId(tenantId, id);
1117
+ if (!row) return { kind: "not_found" };
1118
+ const current = mapRow2(row);
1119
+ if (!validDate(expected) || current.updatedAt.getTime() !== expected.getTime()) return { kind: "conflict" };
1120
+ if (current.role === "owner" && current.status === "active" && (remove || nextRole !== "owner")) {
1121
+ const other = this.db.prepare("SELECT 1 FROM lt_project_memberships WHERE tenant_id = ? AND project_id = ? AND id <> ? AND role = 'owner' AND status = 'active'").get(tenantId, current.projectId, id);
1122
+ if (!other) return { kind: "last_owner" };
1123
+ }
1124
+ const updatedAt = timestamp(row.updated_at);
1125
+ const changes = remove ? write(this.db, "UPDATE lt_project_memberships SET status = 'removed', updated_at = ? WHERE tenant_id = ? AND id = ? AND updated_at = ?", [updatedAt, tenantId, id, row.updated_at]) : write(this.db, "UPDATE lt_project_memberships SET role = ?, updated_at = ? WHERE tenant_id = ? AND id = ? AND updated_at = ?", [nextRole, updatedAt, tenantId, id, row.updated_at]);
1126
+ if (changes === 0) return { kind: "conflict" };
1127
+ return { kind: remove ? "removed" : "updated", membership: mapRow2(this.byId(tenantId, id)) };
1128
+ });
1129
+ }
1130
+ byId(tenantId, id) {
1131
+ return this.db.prepare(`SELECT ${columns2} FROM lt_project_memberships WHERE tenant_id = ? AND id = ?`).get(tenantId, id);
1132
+ }
1133
+ classifyUnique(tenantId, id, projectId, userId) {
1134
+ if (this.byId(tenantId, id)) return new ProjectMembershipIdConflictError();
1135
+ if (this.db.prepare("SELECT 1 FROM lt_project_memberships WHERE tenant_id = ? AND project_id = ? AND user_id = ?").get(tenantId, projectId, userId)) return new DuplicateProjectMembershipError();
1136
+ return new Error("Unclassified project membership uniqueness conflict");
1137
+ }
1138
+ };
1139
+
1140
+ // src/stores/LocalProjectBotMembershipStore.ts
1141
+ var columns3 = "id, tenant_id, workspace_id, project_id, room_id, assistant_id, role, title, responsibility, mention_name, status, room_thread_id, joined_at, updated_at";
1142
+ var ProjectBotMembershipIdConflictError = class extends Error {
1143
+ constructor(tenantId, id) {
1144
+ super(`Project bot membership ID '${id}' already exists in tenant '${tenantId}'`);
1145
+ this.name = "ProjectBotMembershipIdConflictError";
1146
+ }
1147
+ };
1148
+ function isRole(value) {
1149
+ return value === "coordinator" || value === "specialist";
1150
+ }
1151
+ function isStatus(value) {
1152
+ return value === "active" || value === "paused" || value === "removed";
1153
+ }
1154
+ function mapRow3(row) {
1155
+ const joinedAt = typeof row.joined_at === "string" ? new Date(row.joined_at) : void 0;
1156
+ const updatedAt = typeof row.updated_at === "string" ? new Date(row.updated_at) : void 0;
1157
+ if (typeof row.id !== "string" || typeof row.tenant_id !== "string" || typeof row.workspace_id !== "string" || typeof row.project_id !== "string" || typeof row.room_id !== "string" || typeof row.assistant_id !== "string" || !isRole(row.role) || typeof row.title !== "string" || row.responsibility !== null && typeof row.responsibility !== "string" || typeof row.mention_name !== "string" || !isStatus(row.status) || typeof row.room_thread_id !== "string" || !validDate(joinedAt) || !validDate(updatedAt)) throw new Error("Invalid project bot membership row");
1158
+ return {
1159
+ id: row.id,
1160
+ tenantId: row.tenant_id,
1161
+ workspaceId: row.workspace_id,
1162
+ projectId: row.project_id,
1163
+ roomId: row.room_id,
1164
+ assistantId: row.assistant_id,
1165
+ role: row.role,
1166
+ title: row.title,
1167
+ ...row.responsibility === null ? {} : { responsibility: row.responsibility },
1168
+ mentionName: row.mention_name,
1169
+ status: row.status,
1170
+ roomThreadId: row.room_thread_id,
1171
+ joinedAt,
1172
+ updatedAt
1173
+ };
1174
+ }
1175
+ var LocalProjectBotMembershipStore = class {
1176
+ constructor(db) {
1177
+ this.db = db;
1178
+ ensureProjectRoomSchema(db);
1179
+ }
1180
+ async list(tenantId, projectId) {
1181
+ return this.db.prepare(`SELECT ${columns3} FROM lt_project_bot_memberships WHERE tenant_id = ? AND project_id = ? ORDER BY joined_at ASC, id ASC`).all(tenantId, projectId).map(mapRow3);
1182
+ }
1183
+ async findById(tenantId, id) {
1184
+ const row = this.byId(tenantId, id);
1185
+ return row ? mapRow3(row) : null;
1186
+ }
1187
+ async findByAssistant(tenantId, projectId, assistantId) {
1188
+ const row = this.db.prepare(`SELECT ${columns3} FROM lt_project_bot_memberships WHERE tenant_id = ? AND project_id = ? AND assistant_id = ?`).get(tenantId, projectId, assistantId);
1189
+ return row ? mapRow3(row) : null;
1190
+ }
1191
+ async save(input) {
1192
+ try {
1193
+ return transaction(this.db, () => {
1194
+ const existing = this.db.prepare(`SELECT ${columns3} FROM lt_project_bot_memberships WHERE tenant_id = ? AND project_id = ? AND assistant_id = ?`).get(input.tenantId, input.projectId, input.assistantId);
1195
+ if (existing) return this.reactivate(existing, input);
1196
+ if (this.byId(input.tenantId, input.id)) throw new ProjectBotMembershipIdConflictError(input.tenantId, input.id);
1197
+ const conflict = this.preflight(input.tenantId, input.projectId, input.roomId, input.role, input.mentionName, input.status, input.id);
1198
+ if (conflict) return { kind: conflict };
1199
+ const now = timestamp();
1200
+ try {
1201
+ write(this.db, "INSERT INTO lt_project_bot_memberships (id, tenant_id, workspace_id, project_id, room_id, assistant_id, role, title, responsibility, mention_name, status, room_thread_id, joined_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", [input.id, input.tenantId, input.workspaceId, input.projectId, input.roomId, input.assistantId, input.role, input.title, input.responsibility ?? null, input.mentionName, input.status, input.roomThreadId, now, now]);
1202
+ } catch (error) {
1203
+ if (!isUniqueError(error)) throw error;
1204
+ if (this.byId(input.tenantId, input.id)) throw new ProjectBotMembershipIdConflictError(input.tenantId, input.id);
1205
+ const raced = this.byAssistant(input.tenantId, input.projectId, input.assistantId);
1206
+ if (raced) return this.reactivate(raced, input);
1207
+ const racedConflict = this.preflight(input.tenantId, input.projectId, input.roomId, input.role, input.mentionName, input.status, input.id);
1208
+ if (racedConflict) return { kind: racedConflict };
1209
+ throw new Error("Unclassified project bot membership uniqueness conflict");
1210
+ }
1211
+ return { kind: "created", membership: mapRow3(this.byId(input.tenantId, input.id)) };
1212
+ });
1213
+ } catch (error) {
1214
+ const conflict = this.conflict(error);
1215
+ if (conflict) return { kind: conflict };
1216
+ throw error;
1217
+ }
1218
+ }
1219
+ async update(input) {
1220
+ try {
1221
+ return transaction(this.db, () => {
1222
+ const row = this.byId(input.tenantId, input.id);
1223
+ if (!row) return { kind: "not_found" };
1224
+ const existing = mapRow3(row);
1225
+ if (!validDate(input.expectedUpdatedAt) || existing.updatedAt.getTime() !== input.expectedUpdatedAt.getTime()) return { kind: "conflict" };
1226
+ const candidate = { ...existing, ...input.patch };
1227
+ const conflict = this.preflight(candidate.tenantId, candidate.projectId, candidate.roomId, candidate.role, candidate.mentionName, candidate.status, candidate.id);
1228
+ if (conflict) return { kind: conflict };
1229
+ let changes;
1230
+ try {
1231
+ changes = write(this.db, "UPDATE lt_project_bot_memberships SET role = ?, title = ?, responsibility = ?, mention_name = ?, status = ?, updated_at = ? WHERE tenant_id = ? AND id = ? AND updated_at = ?", [candidate.role, candidate.title, candidate.responsibility ?? null, candidate.mentionName, candidate.status, timestamp(row.updated_at), input.tenantId, input.id, row.updated_at]);
1232
+ } catch (error) {
1233
+ if (!isUniqueError(error)) throw error;
1234
+ const racedConflict = this.preflight(candidate.tenantId, candidate.projectId, candidate.roomId, candidate.role, candidate.mentionName, candidate.status, candidate.id);
1235
+ if (racedConflict) return { kind: racedConflict };
1236
+ throw new Error("Unclassified project bot membership uniqueness conflict");
1237
+ }
1238
+ if (changes === 0) return { kind: "conflict" };
1239
+ return { kind: "updated", membership: mapRow3(this.byId(input.tenantId, input.id)) };
1240
+ });
1241
+ } catch (error) {
1242
+ const conflict = this.conflict(error);
1243
+ if (conflict) return { kind: conflict };
1244
+ throw error;
1245
+ }
1246
+ }
1247
+ byId(tenantId, id) {
1248
+ return this.db.prepare(`SELECT ${columns3} FROM lt_project_bot_memberships WHERE tenant_id = ? AND id = ?`).get(tenantId, id);
1249
+ }
1250
+ byAssistant(tenantId, projectId, assistantId) {
1251
+ return this.db.prepare(`SELECT ${columns3} FROM lt_project_bot_memberships WHERE tenant_id = ? AND project_id = ? AND assistant_id = ?`).get(tenantId, projectId, assistantId);
1252
+ }
1253
+ reactivate(existing, input) {
1254
+ const persisted = mapRow3(existing);
1255
+ const wasRemoved = persisted.status === "removed";
1256
+ const conflict = this.preflight(input.tenantId, input.projectId, persisted.roomId, input.role, input.mentionName, "active", persisted.id);
1257
+ if (conflict) return { kind: conflict };
1258
+ try {
1259
+ write(this.db, "UPDATE lt_project_bot_memberships SET role = ?, title = ?, responsibility = ?, mention_name = ?, status = 'active', updated_at = ? WHERE tenant_id = ? AND id = ?", [input.role, input.title, input.responsibility ?? null, input.mentionName, timestamp(existing.updated_at), input.tenantId, persisted.id]);
1260
+ } catch (error) {
1261
+ if (!isUniqueError(error)) throw error;
1262
+ const racedConflict = this.preflight(input.tenantId, input.projectId, persisted.roomId, input.role, input.mentionName, "active", persisted.id);
1263
+ if (racedConflict) return { kind: racedConflict };
1264
+ throw new Error("Unclassified project bot membership uniqueness conflict");
1265
+ }
1266
+ return { kind: wasRemoved ? "reactivated" : "updated", membership: mapRow3(this.byId(input.tenantId, persisted.id)) };
1267
+ }
1268
+ preflight(tenantId, projectId, roomId, role2, mentionName, status, excludeId) {
1269
+ if (status === "removed") return void 0;
1270
+ if (role2 === "coordinator" && this.db.prepare("SELECT 1 FROM lt_project_bot_memberships WHERE tenant_id = ? AND project_id = ? AND id <> ? AND role = 'coordinator' AND status IN ('active', 'paused')").get(tenantId, projectId, excludeId)) return "coordinator_conflict";
1271
+ if (this.db.prepare("SELECT 1 FROM lt_project_bot_memberships WHERE tenant_id = ? AND room_id = ? AND mention_name = ? AND id <> ? AND status IN ('active', 'paused')").get(tenantId, roomId, mentionName, excludeId)) return "mention_conflict";
1272
+ return void 0;
1273
+ }
1274
+ conflict(error) {
1275
+ if (!isUniqueError(error) || !(error instanceof Error)) return void 0;
1276
+ if (error.message.includes("tenant_id, lt_project_bot_memberships.project_id")) return "coordinator_conflict";
1277
+ if (error.message.includes("room_id") && error.message.includes("mention_name")) return "mention_conflict";
1278
+ return void 0;
1279
+ }
1280
+ };
1281
+
1282
+ // src/stores/LocalProjectRoomMessageStore.ts
1283
+ var columns4 = "id, tenant_id, workspace_id, project_id, room_id, author, content, mentions, reply_to_message_id, source, source_id, idempotency_key, created_at";
1284
+ var ProjectRoomMessageIdConflictError = class extends Error {
1285
+ constructor(tenantId, id) {
1286
+ super(`Project room message ID '${id}' already exists in tenant '${tenantId}'`);
1287
+ this.name = "ProjectRoomMessageIdConflictError";
1288
+ }
1289
+ };
1290
+ var DuplicateProjectRoomMessageIdempotencyKeyError = class extends Error {
1291
+ constructor(tenantId, roomId, key) {
1292
+ super(`Project room message idempotency key '${key}' already exists in tenant '${tenantId}' room '${roomId}'`);
1293
+ this.name = "DuplicateProjectRoomMessageIdempotencyKeyError";
1294
+ }
1295
+ };
1296
+ function record(value) {
1297
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1298
+ }
1299
+ function only(value, keys) {
1300
+ return Object.keys(value).every((key) => keys.includes(key));
1301
+ }
1302
+ function parseJson(value) {
1303
+ if (typeof value !== "string") return void 0;
1304
+ try {
1305
+ return JSON.parse(value);
1306
+ } catch {
1307
+ return void 0;
1308
+ }
1309
+ }
1310
+ function author(value) {
1311
+ if (!record(value)) return void 0;
1312
+ if (value.type === "human" && only(value, ["type", "userId"]) && typeof value.userId === "string") return { type: "human", userId: value.userId };
1313
+ if (value.type === "bot" && only(value, ["type", "membershipId", "assistantId"]) && typeof value.membershipId === "string" && typeof value.assistantId === "string") return { type: "bot", membershipId: value.membershipId, assistantId: value.assistantId };
1314
+ if (value.type === "system" && only(value, ["type"])) return { type: "system" };
1315
+ return void 0;
1316
+ }
1317
+ function mention(value) {
1318
+ if (!record(value)) return void 0;
1319
+ if (value.type === "bot" && only(value, ["type", "membershipId"]) && typeof value.membershipId === "string") return { type: "bot", membershipId: value.membershipId };
1320
+ if (value.type === "team" && only(value, ["type"])) return { type: "team" };
1321
+ return void 0;
1322
+ }
1323
+ function source(value) {
1324
+ return value === "user" || value === "agent" || value === "task" || value === "routine" || value === "system";
1325
+ }
1326
+ function mapRow4(row) {
1327
+ const mappedAuthor = author(parseJson(row.author));
1328
+ const content = parseJson(row.content);
1329
+ const rawMentions = parseJson(row.mentions);
1330
+ const mappedMentions = Array.isArray(rawMentions) ? rawMentions.map(mention) : void 0;
1331
+ const createdAt = typeof row.created_at === "string" ? new Date(row.created_at) : void 0;
1332
+ if (typeof row.id !== "string" || typeof row.tenant_id !== "string" || typeof row.workspace_id !== "string" || typeof row.project_id !== "string" || typeof row.room_id !== "string" || !mappedAuthor || !record(content) || content.type !== "text" || typeof content.text !== "string" || !only(content, ["type", "text"]) || !mappedMentions || mappedMentions.some((item) => item === void 0) || row.reply_to_message_id !== null && typeof row.reply_to_message_id !== "string" || !source(row.source) || row.source_id !== null && typeof row.source_id !== "string" || row.idempotency_key !== null && typeof row.idempotency_key !== "string" || !validDate(createdAt)) throw new Error("Invalid project room message row");
1333
+ return {
1334
+ id: row.id,
1335
+ tenantId: row.tenant_id,
1336
+ workspaceId: row.workspace_id,
1337
+ projectId: row.project_id,
1338
+ roomId: row.room_id,
1339
+ author: mappedAuthor,
1340
+ content: { type: "text", text: content.text },
1341
+ mentions: mappedMentions,
1342
+ ...row.reply_to_message_id === null ? {} : { replyToMessageId: row.reply_to_message_id },
1343
+ source: row.source,
1344
+ ...row.source_id === null ? {} : { sourceId: row.source_id },
1345
+ ...row.idempotency_key === null ? {} : { idempotencyKey: row.idempotency_key },
1346
+ createdAt
1347
+ };
1348
+ }
1349
+ var LocalProjectRoomMessageStore = class {
1350
+ constructor(db) {
1351
+ this.db = db;
1352
+ ensureProjectRoomSchema(db);
1353
+ }
1354
+ async create(input) {
1355
+ return this.insert(input, false);
1356
+ }
1357
+ async createIdempotent(input) {
1358
+ return this.insert(input, true);
1359
+ }
1360
+ async list(input) {
1361
+ if (input.before && !validDate(input.before.createdAt)) throw new RangeError("Project room message cursor date is invalid");
1362
+ const limit = Math.min(100, Math.max(1, Math.trunc(Number.isFinite(input.limit) ? input.limit : 1)));
1363
+ const rows2 = input.before ? this.db.prepare(`SELECT ${columns4} FROM lt_project_room_messages WHERE tenant_id = ? AND room_id = ? AND (created_at < ? OR (created_at = ? AND id < ?)) ORDER BY created_at DESC, id DESC LIMIT ?`).all(input.tenantId, input.roomId, input.before.createdAt.toISOString(), input.before.createdAt.toISOString(), input.before.id, limit) : this.db.prepare(`SELECT ${columns4} FROM lt_project_room_messages WHERE tenant_id = ? AND room_id = ? ORDER BY created_at DESC, id DESC LIMIT ?`).all(input.tenantId, input.roomId, limit);
1364
+ return rows2.map(mapRow4);
1365
+ }
1366
+ async findById(tenantId, id) {
1367
+ const row = this.byId(tenantId, id);
1368
+ return row ? mapRow4(row) : null;
1369
+ }
1370
+ insert(input, idempotent) {
1371
+ return transaction(this.db, () => {
1372
+ if (idempotent && input.idempotencyKey) {
1373
+ const canonical = this.byKey(input.tenantId, input.roomId, input.idempotencyKey);
1374
+ if (canonical) return mapRow4(canonical);
1375
+ }
1376
+ if (this.byId(input.tenantId, input.id)) throw new ProjectRoomMessageIdConflictError(input.tenantId, input.id);
1377
+ try {
1378
+ write(this.db, "INSERT INTO lt_project_room_messages (id, tenant_id, workspace_id, project_id, room_id, author, content, mentions, reply_to_message_id, source, source_id, idempotency_key, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", [input.id, input.tenantId, input.workspaceId, input.projectId, input.roomId, JSON.stringify(input.author), JSON.stringify(input.content), JSON.stringify(input.mentions), input.replyToMessageId ?? null, input.source, input.sourceId ?? null, input.idempotencyKey ?? null, timestamp()]);
1379
+ } catch (error) {
1380
+ if (!isUniqueError(error)) throw error;
1381
+ if (this.byId(input.tenantId, input.id)) throw new ProjectRoomMessageIdConflictError(input.tenantId, input.id);
1382
+ if (input.idempotencyKey) {
1383
+ const canonical = this.byKey(input.tenantId, input.roomId, input.idempotencyKey);
1384
+ if (canonical && idempotent) return mapRow4(canonical);
1385
+ if (canonical) throw new DuplicateProjectRoomMessageIdempotencyKeyError(input.tenantId, input.roomId, input.idempotencyKey);
1386
+ }
1387
+ throw new Error("Unclassified project room message uniqueness conflict");
1388
+ }
1389
+ return mapRow4(this.byId(input.tenantId, input.id));
1390
+ });
1391
+ }
1392
+ byId(tenantId, id) {
1393
+ return this.db.prepare(`SELECT ${columns4} FROM lt_project_room_messages WHERE tenant_id = ? AND id = ?`).get(tenantId, id);
1394
+ }
1395
+ byKey(tenantId, roomId, key) {
1396
+ return this.db.prepare(`SELECT ${columns4} FROM lt_project_room_messages WHERE tenant_id = ? AND room_id = ? AND idempotency_key = ?`).get(tenantId, roomId, key);
1397
+ }
1398
+ };
1399
+
715
1400
  // src/stores/LocalUserStore.ts
716
1401
  var DDL5 = `
717
1402
  CREATE TABLE IF NOT EXISTS lt_users (
@@ -731,10 +1416,10 @@ var LocalUserStore = class {
731
1416
  ensureTable(db, DDL5);
732
1417
  }
733
1418
  async getAllUsers() {
734
- const rows = this.db.prepare(
1419
+ const rows2 = this.db.prepare(
735
1420
  `SELECT * FROM lt_users ORDER BY created_at DESC`
736
1421
  ).all();
737
- return rows.map(mapRowToUser);
1422
+ return rows2.map(mapRowToUser);
738
1423
  }
739
1424
  async getUserById(id) {
740
1425
  const row = this.db.prepare(
@@ -838,10 +1523,10 @@ var LocalTenantStore = class {
838
1523
  ensureTable(db, DDL6);
839
1524
  }
840
1525
  async getAllTenants() {
841
- const rows = this.db.prepare(
1526
+ const rows2 = this.db.prepare(
842
1527
  `SELECT * FROM lt_tenants ORDER BY created_at DESC`
843
1528
  ).all();
844
- return rows.map(mapRowToTenant);
1529
+ return rows2.map(mapRowToTenant);
845
1530
  }
846
1531
  async getTenantById(id) {
847
1532
  const row = this.db.prepare(
@@ -940,16 +1625,16 @@ var LocalUserTenantLinkStore = class {
940
1625
  ensureTable(db, DDL7);
941
1626
  }
942
1627
  async getTenantsByUser(userId) {
943
- const rows = this.db.prepare(
1628
+ const rows2 = this.db.prepare(
944
1629
  `SELECT * FROM lt_user_tenant_links WHERE user_id = ? ORDER BY joined_at DESC`
945
1630
  ).all(userId);
946
- return rows.map(mapRowToLink);
1631
+ return rows2.map(mapRowToLink);
947
1632
  }
948
1633
  async getUsersByTenant(tenantId) {
949
- const rows = this.db.prepare(
1634
+ const rows2 = this.db.prepare(
950
1635
  `SELECT * FROM lt_user_tenant_links WHERE tenant_id = ? ORDER BY joined_at DESC`
951
1636
  ).all(tenantId);
952
- return rows.map(mapRowToLink);
1637
+ return rows2.map(mapRowToLink);
953
1638
  }
954
1639
  async getLink(userId, tenantId) {
955
1640
  const row = this.db.prepare(
@@ -959,7 +1644,7 @@ var LocalUserTenantLinkStore = class {
959
1644
  }
960
1645
  async createLink(data) {
961
1646
  const now = nowISO();
962
- const role = data.role || "member";
1647
+ const role2 = data.role || "member";
963
1648
  const metadata = JSON.stringify(data.metadata || {});
964
1649
  this.db.prepare(
965
1650
  `INSERT INTO lt_user_tenant_links (user_id, tenant_id, role, joined_at, metadata)
@@ -967,11 +1652,11 @@ var LocalUserTenantLinkStore = class {
967
1652
  ON CONFLICT(user_id, tenant_id) DO UPDATE SET
968
1653
  role = excluded.role,
969
1654
  metadata = excluded.metadata`
970
- ).run(data.userId, data.tenantId, role, now, metadata);
1655
+ ).run(data.userId, data.tenantId, role2, now, metadata);
971
1656
  return {
972
1657
  userId: data.userId,
973
1658
  tenantId: data.tenantId,
974
- role,
1659
+ role: role2,
975
1660
  joinedAt: parseISO(now),
976
1661
  metadata: data.metadata
977
1662
  };
@@ -1041,16 +1726,16 @@ var LocalDatabaseConfigStore = class {
1041
1726
  ensureTable(db, DDL8);
1042
1727
  }
1043
1728
  async getAllConfigs(tenantId) {
1044
- const rows = this.db.prepare(
1729
+ const rows2 = this.db.prepare(
1045
1730
  `SELECT * FROM lt_database_configs WHERE tenant_id = ? ORDER BY created_at DESC`
1046
1731
  ).all(tenantId);
1047
- return rows.map((r) => mapRowToEntry(r));
1732
+ return rows2.map((r) => mapRowToEntry(r));
1048
1733
  }
1049
1734
  async getAllConfigsWithoutTenant() {
1050
- const rows = this.db.prepare(
1735
+ const rows2 = this.db.prepare(
1051
1736
  `SELECT * FROM lt_database_configs ORDER BY created_at DESC`
1052
1737
  ).all();
1053
- return rows.map((r) => mapRowToEntry(r));
1738
+ return rows2.map((r) => mapRowToEntry(r));
1054
1739
  }
1055
1740
  async getConfigById(tenantId, id) {
1056
1741
  const row = this.db.prepare(
@@ -1182,16 +1867,16 @@ var LocalConnectionStore = class {
1182
1867
  ensureTable(db, DDL9);
1183
1868
  }
1184
1869
  async listByTenant(tenantId) {
1185
- const rows = this.db.prepare(
1870
+ const rows2 = this.db.prepare(
1186
1871
  `SELECT * FROM connection_configs WHERE tenant_id = ? ORDER BY created_at`
1187
1872
  ).all(tenantId);
1188
- return rows.map((r) => this.mapRow(r));
1873
+ return rows2.map((r) => this.mapRow(r));
1189
1874
  }
1190
1875
  async listByType(tenantId, type) {
1191
- const rows = this.db.prepare(
1876
+ const rows2 = this.db.prepare(
1192
1877
  `SELECT * FROM connection_configs WHERE tenant_id = ? AND type = ? ORDER BY created_at`
1193
1878
  ).all(tenantId, type);
1194
- return rows.map((r) => this.mapRow(r));
1879
+ return rows2.map((r) => this.mapRow(r));
1195
1880
  }
1196
1881
  async getByKey(tenantId, type, key) {
1197
1882
  const row = this.db.prepare(
@@ -1277,16 +1962,16 @@ var LocalMetricsServerConfigStore = class {
1277
1962
  ensureTable(db, DDL10);
1278
1963
  }
1279
1964
  async getAllConfigs(tenantId) {
1280
- const rows = this.db.prepare(
1965
+ const rows2 = this.db.prepare(
1281
1966
  `SELECT * FROM lt_metrics_configs WHERE tenant_id = ? ORDER BY created_at DESC`
1282
1967
  ).all(tenantId);
1283
- return rows.map(mapRowToEntry2);
1968
+ return rows2.map(mapRowToEntry2);
1284
1969
  }
1285
1970
  async getAllConfigsWithoutTenant() {
1286
- const rows = this.db.prepare(
1971
+ const rows2 = this.db.prepare(
1287
1972
  `SELECT * FROM lt_metrics_configs ORDER BY created_at DESC`
1288
1973
  ).all();
1289
- return rows.map(mapRowToEntry2);
1974
+ return rows2.map(mapRowToEntry2);
1290
1975
  }
1291
1976
  async getConfigById(tenantId, id) {
1292
1977
  const row = this.db.prepare(
@@ -1405,16 +2090,16 @@ var LocalMcpServerConfigStore = class {
1405
2090
  ensureTable(db, DDL11);
1406
2091
  }
1407
2092
  async getAllConfigs(tenantId) {
1408
- const rows = this.db.prepare(
2093
+ const rows2 = this.db.prepare(
1409
2094
  `SELECT * FROM lt_mcp_configs WHERE tenant_id = ? ORDER BY created_at DESC`
1410
2095
  ).all(tenantId);
1411
- return rows.map(mapRowToEntry3);
2096
+ return rows2.map(mapRowToEntry3);
1412
2097
  }
1413
2098
  async getAllConfigsWithoutTenant() {
1414
- const rows = this.db.prepare(
2099
+ const rows2 = this.db.prepare(
1415
2100
  `SELECT * FROM lt_mcp_configs ORDER BY created_at DESC`
1416
2101
  ).all();
1417
- return rows.map(mapRowToEntry3);
2102
+ return rows2.map(mapRowToEntry3);
1418
2103
  }
1419
2104
  async getConfigById(tenantId, id) {
1420
2105
  const row = this.db.prepare(
@@ -1696,22 +2381,22 @@ var LocalWorkflowTrackingStore = class {
1696
2381
  this.db.prepare(`DELETE FROM lt_workflow_runs WHERE id = ?`).run(runId);
1697
2382
  }
1698
2383
  async getWorkflowRunsByThreadId(tenantId, threadId) {
1699
- const rows = this.db.prepare(
2384
+ const rows2 = this.db.prepare(
1700
2385
  `SELECT * FROM lt_workflow_runs WHERE tenant_id = ? AND thread_id = ? ORDER BY created_at DESC`
1701
2386
  ).all(tenantId, threadId);
1702
- return rows.map(mapRowToRun);
2387
+ return rows2.map(mapRowToRun);
1703
2388
  }
1704
2389
  async getWorkflowRunsByAssistantId(tenantId, assistantId) {
1705
- const rows = this.db.prepare(
2390
+ const rows2 = this.db.prepare(
1706
2391
  `SELECT * FROM lt_workflow_runs WHERE tenant_id = ? AND assistant_id = ? ORDER BY created_at DESC`
1707
2392
  ).all(tenantId, assistantId);
1708
- return rows.map(mapRowToRun);
2393
+ return rows2.map(mapRowToRun);
1709
2394
  }
1710
2395
  async getWorkflowRunsByTenantId(tenantId) {
1711
- const rows = this.db.prepare(
2396
+ const rows2 = this.db.prepare(
1712
2397
  `SELECT * FROM lt_workflow_runs WHERE tenant_id = ? ORDER BY created_at DESC`
1713
2398
  ).all(tenantId);
1714
- return rows.map(mapRowToRun);
2399
+ return rows2.map(mapRowToRun);
1715
2400
  }
1716
2401
  async queryWorkflowRuns(tenantId, options = {}) {
1717
2402
  const conditions = ["tenant_id = ?"];
@@ -1740,8 +2425,8 @@ var LocalWorkflowTrackingStore = class {
1740
2425
  sql += " OFFSET ?";
1741
2426
  selectParams.push(options.offset);
1742
2427
  }
1743
- const rows = this.db.prepare(sql).all(...selectParams);
1744
- return { records: rows.map(mapRowToRun), total: countRow.count };
2428
+ const rows2 = this.db.prepare(sql).all(...selectParams);
2429
+ return { records: rows2.map(mapRowToRun), total: countRow.count };
1745
2430
  }
1746
2431
  async createRunStep(request) {
1747
2432
  const now = nowISO();
@@ -1833,22 +2518,22 @@ var LocalWorkflowTrackingStore = class {
1833
2518
  return this.getStepById(runId, stepId);
1834
2519
  }
1835
2520
  async getRunSteps(runId) {
1836
- const rows = this.db.prepare(
2521
+ const rows2 = this.db.prepare(
1837
2522
  `SELECT * FROM lt_workflow_steps WHERE run_id = ? ORDER BY created_at ASC`
1838
2523
  ).all(runId);
1839
- return rows.map(mapRowToStep);
2524
+ return rows2.map(mapRowToStep);
1840
2525
  }
1841
2526
  async getRunStepsByType(runId, stepType) {
1842
- const rows = this.db.prepare(
2527
+ const rows2 = this.db.prepare(
1843
2528
  `SELECT * FROM lt_workflow_steps WHERE run_id = ? AND step_type = ? ORDER BY created_at ASC`
1844
2529
  ).all(runId, stepType);
1845
- return rows.map(mapRowToStep);
2530
+ return rows2.map(mapRowToStep);
1846
2531
  }
1847
2532
  async getInterruptedSteps(runId) {
1848
- const rows = this.db.prepare(
2533
+ const rows2 = this.db.prepare(
1849
2534
  `SELECT * FROM lt_workflow_steps WHERE run_id = ? AND status = 'interrupted' ORDER BY created_at ASC`
1850
2535
  ).all(runId);
1851
- return rows.map(mapRowToStep);
2536
+ return rows2.map(mapRowToStep);
1852
2537
  }
1853
2538
  getStepById(runId, id) {
1854
2539
  const row = this.db.prepare(
@@ -1993,7 +2678,7 @@ CREATE TABLE IF NOT EXISTS lt_eval_run_results (
1993
2678
  PRIMARY KEY (run_id, id)
1994
2679
  );
1995
2680
  `;
1996
- function parseJson(val, fallback) {
2681
+ function parseJson2(val, fallback) {
1997
2682
  if (val == null) return fallback;
1998
2683
  if (typeof val === "string") {
1999
2684
  try {
@@ -2085,20 +2770,20 @@ var LocalEvalStore = class {
2085
2770
  `);
2086
2771
  }
2087
2772
  /** Add a column if it does not exist (SQLite version compatible). */
2088
- ensureColumn(table, column, ddl) {
2773
+ ensureColumn(table, column, ddl2) {
2089
2774
  const cols = this.db.prepare(`PRAGMA table_info(${table})`).all();
2090
2775
  if (!cols.some((c) => c.name === column)) {
2091
- this.db.exec(`ALTER TABLE ${table} ADD COLUMN ${ddl}`);
2776
+ this.db.exec(`ALTER TABLE ${table} ADD COLUMN ${ddl2}`);
2092
2777
  }
2093
2778
  }
2094
2779
  // -------------------------------------------------------------------------
2095
2780
  // Projects
2096
2781
  // -------------------------------------------------------------------------
2097
2782
  async getProjectsByTenant(tenantId) {
2098
- const rows = this.db.prepare(
2783
+ const rows2 = this.db.prepare(
2099
2784
  `SELECT * FROM lt_eval_projects WHERE tenant_id = ? ORDER BY created_at DESC`
2100
2785
  ).all(tenantId);
2101
- return rows.map(this.mapRowToProject);
2786
+ return rows2.map(this.mapRowToProject);
2102
2787
  }
2103
2788
  async getProjectById(tenantId, id) {
2104
2789
  const row = this.db.prepare(
@@ -2201,11 +2886,11 @@ var LocalEvalStore = class {
2201
2886
  // Suites
2202
2887
  // -------------------------------------------------------------------------
2203
2888
  async getSuitesByProject(tenantId, projectId) {
2204
- const rows = this.db.prepare(
2889
+ const rows2 = this.db.prepare(
2205
2890
  `SELECT s.*, (SELECT COUNT(*) FROM lt_eval_cases c WHERE c.suite_id = s.id AND c.tenant_id = s.tenant_id) as case_count
2206
2891
  FROM lt_eval_suites s WHERE s.tenant_id = ? AND s.project_id = ? ORDER BY s.created_at DESC`
2207
2892
  ).all(tenantId, projectId);
2208
- return rows.map(this.mapRowToSuite);
2893
+ return rows2.map(this.mapRowToSuite);
2209
2894
  }
2210
2895
  async getSuiteById(tenantId, id) {
2211
2896
  const row = this.db.prepare(
@@ -2246,10 +2931,10 @@ var LocalEvalStore = class {
2246
2931
  // Cases
2247
2932
  // -------------------------------------------------------------------------
2248
2933
  async getCasesBySuite(tenantId, suiteId) {
2249
- const rows = this.db.prepare(
2934
+ const rows2 = this.db.prepare(
2250
2935
  `SELECT * FROM lt_eval_cases WHERE tenant_id = ? AND suite_id = ? ORDER BY created_at DESC`
2251
2936
  ).all(tenantId, suiteId);
2252
- return rows.map(this.mapRowToCase);
2937
+ return rows2.map(this.mapRowToCase);
2253
2938
  }
2254
2939
  async getCaseById(tenantId, id) {
2255
2940
  const row = this.db.prepare(
@@ -2338,8 +3023,8 @@ var LocalEvalStore = class {
2338
3023
  vals.push(opts.status);
2339
3024
  }
2340
3025
  query += ` ORDER BY created_at DESC`;
2341
- const rows = this.db.prepare(query).all(...vals);
2342
- return rows.map(this.mapRowToRun);
3026
+ const rows2 = this.db.prepare(query).all(...vals);
3027
+ return rows2.map(this.mapRowToRun);
2343
3028
  }
2344
3029
  async getRunById(tenantId, id) {
2345
3030
  const row = this.db.prepare(
@@ -2402,12 +3087,12 @@ var LocalEvalStore = class {
2402
3087
  // Run Results
2403
3088
  // -------------------------------------------------------------------------
2404
3089
  async getResultsByRun(tenantId, runId) {
2405
- const rows = this.db.prepare(
3090
+ const rows2 = this.db.prepare(
2406
3091
  `SELECT rr.* FROM lt_eval_run_results rr
2407
3092
  INNER JOIN lt_eval_runs r ON r.id = rr.run_id
2408
3093
  WHERE r.tenant_id = ? AND rr.run_id = ? ORDER BY rr.created_at ASC`
2409
3094
  ).all(tenantId, runId);
2410
- return rows.map(this.mapRowToRunResult);
3095
+ return rows2.map(this.mapRowToRunResult);
2411
3096
  }
2412
3097
  async getRunResultById(tenantId, id) {
2413
3098
  const row = this.db.prepare(
@@ -2525,8 +3210,8 @@ var LocalEvalStore = class {
2525
3210
  name: row.name,
2526
3211
  description: row.description,
2527
3212
  version: row.version,
2528
- judgeModelConfig: parseJson(row.judge_model_config, {}),
2529
- targetServerConfig: parseJson(row.target_server_config, {}),
3213
+ judgeModelConfig: parseJson2(row.judge_model_config, {}),
3214
+ targetServerConfig: parseJson2(row.target_server_config, {}),
2530
3215
  concurrency: row.concurrency ?? 3,
2531
3216
  reportConfig: parseOptionalJson(row.report_config),
2532
3217
  createdAt: parseISO(row.created_at),
@@ -2551,7 +3236,7 @@ var LocalEvalStore = class {
2551
3236
  suiteId: row.suite_id,
2552
3237
  inputMessage: row.input_message,
2553
3238
  inputFiles: parseOptionalJson(row.input_files),
2554
- steps: parseJson(row.steps, []),
3239
+ steps: parseJson2(row.steps, []),
2555
3240
  outputType: row.output_type || "message_content",
2556
3241
  contentAssertion: row.content_assertion || "",
2557
3242
  rubrics: parseOptionalJson(row.rubrics),
@@ -2603,7 +3288,54 @@ var LocalEvalStore = class {
2603
3288
  };
2604
3289
 
2605
3290
  // src/stores/LocalChannelBindingStore.ts
3291
+ import {
3292
+ DuplicateChannelBindingSubjectError
3293
+ } from "@axiom-lattice/protocols";
2606
3294
  import { randomUUID as randomUUID2 } from "crypto";
3295
+
3296
+ // src/stores/room-channel-binding-index.ts
3297
+ import {
3298
+ ChannelBindingMigrationConflictError
3299
+ } from "@axiom-lattice/protocols";
3300
+ var UNIQUE_INDEX_SQL = `CREATE UNIQUE INDEX IF NOT EXISTS idx_lt_cb_subject_unique
3301
+ ON lt_channel_bindings(channel, channel_installation_id, tenant_id, sender_id)`;
3302
+ var LOOKUP_INDEX_SQL = `CREATE INDEX IF NOT EXISTS idx_lt_cb_resolve
3303
+ ON lt_channel_bindings(channel, sender_id, channel_installation_id, tenant_id)`;
3304
+ function ensureChannelBindingSubjectIndex(db) {
3305
+ db.exec("BEGIN");
3306
+ try {
3307
+ const conflicts = db.prepare(`SELECT tenant_id, channel, channel_installation_id, sender_id, COUNT(*) AS count
3308
+ FROM lt_channel_bindings
3309
+ GROUP BY channel, channel_installation_id, tenant_id, sender_id
3310
+ HAVING COUNT(*) > 1
3311
+ ORDER BY tenant_id, channel, channel_installation_id, sender_id`).all();
3312
+ if (conflicts.length > 0) {
3313
+ throw new ChannelBindingMigrationConflictError(conflicts.map(mapConflict));
3314
+ }
3315
+ db.exec("DROP INDEX IF EXISTS idx_lt_cb_room_subject");
3316
+ db.exec(UNIQUE_INDEX_SQL);
3317
+ db.exec(LOOKUP_INDEX_SQL);
3318
+ db.exec("COMMIT");
3319
+ } catch (error) {
3320
+ try {
3321
+ db.exec("ROLLBACK");
3322
+ } catch {
3323
+ }
3324
+ throw error;
3325
+ }
3326
+ db.save();
3327
+ }
3328
+ function mapConflict(row) {
3329
+ return {
3330
+ tenantId: row.tenant_id,
3331
+ channel: row.channel,
3332
+ channelInstallationId: row.channel_installation_id,
3333
+ senderId: row.sender_id,
3334
+ count: row.count
3335
+ };
3336
+ }
3337
+
3338
+ // src/stores/LocalChannelBindingStore.ts
2607
3339
  var DDL14 = `
2608
3340
  CREATE TABLE IF NOT EXISTS lt_channel_bindings (
2609
3341
  id TEXT PRIMARY KEY,
@@ -2622,12 +3354,23 @@ CREATE TABLE IF NOT EXISTS lt_channel_bindings (
2622
3354
  created_at TEXT NOT NULL,
2623
3355
  updated_at TEXT NOT NULL
2624
3356
  );
2625
- CREATE INDEX IF NOT EXISTS idx_lt_cb_resolve ON lt_channel_bindings(channel, sender_id, channel_installation_id, tenant_id);
2626
3357
  `;
2627
3358
  var LocalChannelBindingStore = class {
2628
3359
  constructor(db) {
2629
3360
  this.db = db;
2630
3361
  ensureTable(db, DDL14);
3362
+ ensureChannelBindingSubjectIndex(db);
3363
+ }
3364
+ async findById(tenantId, id) {
3365
+ const row = this.db.prepare(
3366
+ "SELECT * FROM lt_channel_bindings WHERE id = ? AND tenant_id = ?"
3367
+ ).get(id, tenantId);
3368
+ return row ? mapRowToBinding(row) : null;
3369
+ }
3370
+ async findBySubject(params) {
3371
+ const row = this.db.prepare(`SELECT * FROM lt_channel_bindings
3372
+ WHERE tenant_id = ? AND channel = ? AND channel_installation_id = ? AND sender_id = ? LIMIT 1`).get(params.tenantId, params.channel, params.channelInstallationId, params.senderId);
3373
+ return row ? mapRowToBinding(row) : null;
2631
3374
  }
2632
3375
  async resolve(params) {
2633
3376
  const row = this.db.prepare(
@@ -2640,35 +3383,41 @@ var LocalChannelBindingStore = class {
2640
3383
  async create(input) {
2641
3384
  const id = randomUUID2();
2642
3385
  const now = nowISO();
2643
- this.db.prepare(
2644
- `INSERT INTO lt_channel_bindings
2645
- (id, channel, channel_installation_id, tenant_id, sender_id, agent_id,
2646
- thread_mode, sender_display_name, sender_metadata, workspace_id, project_id, created_at, updated_at)
2647
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
2648
- ).run(
2649
- id,
2650
- input.channel,
2651
- input.channelInstallationId,
2652
- input.tenantId,
2653
- input.senderId,
2654
- input.agentId,
2655
- input.threadMode || "fixed",
2656
- input.senderDisplayName || null,
2657
- input.senderMetadata ? JSON.stringify(input.senderMetadata) : null,
2658
- input.workspaceId || null,
2659
- input.projectId || null,
2660
- now,
2661
- now
2662
- );
3386
+ try {
3387
+ this.db.prepare(
3388
+ `INSERT INTO lt_channel_bindings
3389
+ (id, channel, channel_installation_id, tenant_id, sender_id, agent_id,
3390
+ thread_id, thread_mode, sender_display_name, sender_metadata, workspace_id, project_id, enabled, created_at, updated_at)
3391
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
3392
+ ).run(
3393
+ id,
3394
+ input.channel,
3395
+ input.channelInstallationId,
3396
+ input.tenantId,
3397
+ input.senderId,
3398
+ input.agentId,
3399
+ input.threadId || null,
3400
+ input.threadMode || "fixed",
3401
+ input.senderDisplayName || null,
3402
+ input.senderMetadata ? JSON.stringify(input.senderMetadata) : null,
3403
+ input.workspaceId || null,
3404
+ input.projectId || null,
3405
+ input.enabled ?? true ? 1 : 0,
3406
+ now,
3407
+ now
3408
+ );
3409
+ } catch (error) {
3410
+ if (error instanceof Error && /UNIQUE constraint failed/.test(error.message)) {
3411
+ throw new DuplicateChannelBindingSubjectError();
3412
+ }
3413
+ throw error;
3414
+ }
2663
3415
  return await this.getById(id);
2664
3416
  }
2665
- async update(id, patch) {
2666
- const existing = await this.getById(id);
3417
+ async update(tenantId, id, patch) {
3418
+ const existing = await this.findById(tenantId, id);
2667
3419
  if (!existing) throw new Error(`Binding ${id} not found`);
2668
3420
  const updated = {
2669
- channel: patch.channel ?? existing.channel,
2670
- channelInstallationId: patch.channelInstallationId ?? existing.channelInstallationId,
2671
- senderId: patch.senderId ?? existing.senderId,
2672
3421
  agentId: patch.agentId ?? existing.agentId,
2673
3422
  threadId: patch.threadId !== void 0 ? patch.threadId : existing.threadId,
2674
3423
  workspaceId: patch.workspaceId !== void 0 ? patch.workspaceId : existing.workspaceId,
@@ -2681,14 +3430,10 @@ var LocalChannelBindingStore = class {
2681
3430
  const now = nowISO();
2682
3431
  this.db.prepare(
2683
3432
  `UPDATE lt_channel_bindings SET
2684
- channel = ?, channel_installation_id = ?, sender_id = ?, agent_id = ?,
2685
- thread_id = ?, workspace_id = ?, project_id = ?, thread_mode = ?,
3433
+ agent_id = ?, thread_id = ?, workspace_id = ?, project_id = ?, thread_mode = ?,
2686
3434
  sender_display_name = ?, sender_metadata = ?, enabled = ?, updated_at = ?
2687
- WHERE id = ?`
3435
+ WHERE id = ? AND tenant_id = ?`
2688
3436
  ).run(
2689
- updated.channel,
2690
- updated.channelInstallationId,
2691
- updated.senderId,
2692
3437
  updated.agentId,
2693
3438
  updated.threadId || null,
2694
3439
  updated.workspaceId || null,
@@ -2698,12 +3443,13 @@ var LocalChannelBindingStore = class {
2698
3443
  updated.senderMetadata ? JSON.stringify(updated.senderMetadata) : null,
2699
3444
  updated.enabled ? 1 : 0,
2700
3445
  now,
2701
- id
3446
+ id,
3447
+ tenantId
2702
3448
  );
2703
- return await this.getById(id);
3449
+ return await this.findById(tenantId, id);
2704
3450
  }
2705
- async delete(id) {
2706
- this.db.prepare(`DELETE FROM lt_channel_bindings WHERE id = ?`).run(id);
3451
+ async delete(tenantId, id) {
3452
+ this.db.prepare(`DELETE FROM lt_channel_bindings WHERE id = ? AND tenant_id = ?`).run(id, tenantId);
2707
3453
  }
2708
3454
  async list(params) {
2709
3455
  const conditions = ["tenant_id = ?"];
@@ -2720,18 +3466,35 @@ var LocalChannelBindingStore = class {
2720
3466
  conditions.push("channel_installation_id = ?");
2721
3467
  values.push(params.channelInstallationId);
2722
3468
  }
3469
+ if (params.excludeChannels?.length) {
3470
+ conditions.push(`channel NOT IN (${params.excludeChannels.map(() => "?").join(", ")})`);
3471
+ values.push(...params.excludeChannels);
3472
+ }
3473
+ for (const prefix of params.excludeInstallationIdPrefixes ?? []) {
3474
+ conditions.push("substr(channel_installation_id, 1, length(?)) <> ?");
3475
+ values.push(prefix, prefix);
3476
+ }
2723
3477
  const limit = params.limit ?? 50;
2724
3478
  const offset = params.offset ?? 0;
2725
3479
  values.push(limit, offset);
2726
- const rows = this.db.prepare(
3480
+ const rows2 = this.db.prepare(
2727
3481
  `SELECT * FROM lt_channel_bindings
2728
3482
  WHERE ${conditions.join(" AND ")}
2729
3483
  ORDER BY created_at DESC
2730
3484
  LIMIT ? OFFSET ?`
2731
3485
  ).all(...values);
2732
- return rows.map(mapRowToBinding);
3486
+ return rows2.map(mapRowToBinding);
2733
3487
  }
2734
- async import(bindings) {
3488
+ async import(tenantId, bindings) {
3489
+ if (bindings.some((binding) => binding.channel === "room")) {
3490
+ throw new Error("Room bindings cannot be imported through the public store API");
3491
+ }
3492
+ if (bindings.some((binding) => binding.channelInstallationId.startsWith("room-internal:"))) {
3493
+ throw new Error("Internal bindings cannot be imported through the public store API");
3494
+ }
3495
+ if (bindings.some((binding) => binding.tenantId !== tenantId)) {
3496
+ throw new Error("Binding import tenant mismatch");
3497
+ }
2735
3498
  const result = [];
2736
3499
  for (const input of bindings) {
2737
3500
  result.push(await this.create(input));
@@ -2739,7 +3502,13 @@ var LocalChannelBindingStore = class {
2739
3502
  return result;
2740
3503
  }
2741
3504
  async export(params) {
2742
- return this.list({ tenantId: params.tenantId, limit: 1e4, offset: 0 });
3505
+ return this.list({
3506
+ tenantId: params.tenantId,
3507
+ excludeChannels: ["room"],
3508
+ excludeInstallationIdPrefixes: ["room-internal:"],
3509
+ limit: 1e4,
3510
+ offset: 0
3511
+ });
2743
3512
  }
2744
3513
  async getById(id) {
2745
3514
  const row = this.db.prepare(
@@ -2793,33 +3562,33 @@ var LocalChannelInstallationStore = class {
2793
3562
  const row = this.db.prepare(
2794
3563
  `SELECT * FROM lt_channel_installations WHERE id = ?`
2795
3564
  ).get(installationId);
2796
- return row ? mapRow(row) : null;
3565
+ return row ? mapRow5(row) : null;
2797
3566
  }
2798
3567
  async getInstallationsByTenant(tenantId, channel) {
2799
- let rows;
3568
+ let rows2;
2800
3569
  if (channel) {
2801
- rows = this.db.prepare(
3570
+ rows2 = this.db.prepare(
2802
3571
  `SELECT * FROM lt_channel_installations WHERE tenant_id = ? AND channel = ? ORDER BY created_at DESC`
2803
3572
  ).all(tenantId, channel);
2804
3573
  } else {
2805
- rows = this.db.prepare(
3574
+ rows2 = this.db.prepare(
2806
3575
  `SELECT * FROM lt_channel_installations WHERE tenant_id = ? ORDER BY created_at DESC`
2807
3576
  ).all(tenantId);
2808
3577
  }
2809
- return rows.map(mapRow);
3578
+ return rows2.map(mapRow5);
2810
3579
  }
2811
3580
  async getAllInstallations(channel) {
2812
- let rows;
3581
+ let rows2;
2813
3582
  if (channel) {
2814
- rows = this.db.prepare(
3583
+ rows2 = this.db.prepare(
2815
3584
  `SELECT * FROM lt_channel_installations WHERE channel = ? ORDER BY created_at DESC`
2816
3585
  ).all(channel);
2817
3586
  } else {
2818
- rows = this.db.prepare(
3587
+ rows2 = this.db.prepare(
2819
3588
  `SELECT * FROM lt_channel_installations ORDER BY created_at DESC`
2820
3589
  ).all();
2821
3590
  }
2822
- return rows.map(mapRow);
3591
+ return rows2.map(mapRow5);
2823
3592
  }
2824
3593
  async createInstallation(tenantId, installationId, data) {
2825
3594
  const now = nowISO();
@@ -2844,7 +3613,7 @@ var LocalChannelInstallationStore = class {
2844
3613
  tenantId,
2845
3614
  channel: data.channel,
2846
3615
  name: data.name,
2847
- config: data.config,
3616
+ config: { ...data.config },
2848
3617
  enabled: data.enabled !== false,
2849
3618
  fallbackAgentId: data.fallbackAgentId,
2850
3619
  rejectWhenNoBinding: data.rejectWhenNoBinding ?? false,
@@ -2854,7 +3623,7 @@ var LocalChannelInstallationStore = class {
2854
3623
  }
2855
3624
  async updateInstallation(tenantId, installationId, updates) {
2856
3625
  const existing = await this.getInstallationById(installationId);
2857
- if (!existing) return null;
3626
+ if (!existing || existing.tenantId !== tenantId) return null;
2858
3627
  const setClauses = [];
2859
3628
  const values = [];
2860
3629
  if (updates.name !== void 0) {
@@ -2881,9 +3650,10 @@ var LocalChannelInstallationStore = class {
2881
3650
  const now = nowISO();
2882
3651
  setClauses.push("updated_at = ?");
2883
3652
  values.push(now);
3653
+ values.push(tenantId);
2884
3654
  values.push(installationId);
2885
3655
  this.db.prepare(
2886
- `UPDATE lt_channel_installations SET ${setClauses.join(", ")} WHERE id = ?`
3656
+ `UPDATE lt_channel_installations SET ${setClauses.join(", ")} WHERE tenant_id = ? AND id = ?`
2887
3657
  ).run(...values);
2888
3658
  return this.getInstallationById(installationId);
2889
3659
  }
@@ -2894,7 +3664,7 @@ var LocalChannelInstallationStore = class {
2894
3664
  return result.changes > 0;
2895
3665
  }
2896
3666
  };
2897
- function mapRow(row) {
3667
+ function mapRow5(row) {
2898
3668
  return {
2899
3669
  id: row.id,
2900
3670
  tenantId: row.tenant_id,
@@ -2945,18 +3715,18 @@ var LocalA2AApiKeyStore = class {
2945
3715
  this.repairManagementIds();
2946
3716
  }
2947
3717
  /** Add a column if it does not exist (SQLite version compatible). */
2948
- ensureColumn(table, column, ddl) {
3718
+ ensureColumn(table, column, ddl2) {
2949
3719
  const cols = this.db.prepare(`PRAGMA table_info(${table})`).all();
2950
3720
  if (!cols.some((c) => c.name === column)) {
2951
- this.db.exec(`ALTER TABLE ${table} ADD COLUMN ${ddl}`);
3721
+ this.db.exec(`ALTER TABLE ${table} ADD COLUMN ${ddl2}`);
2952
3722
  }
2953
3723
  }
2954
3724
  repairManagementIds() {
2955
- const rows = this.db.prepare(
3725
+ const rows2 = this.db.prepare(
2956
3726
  `SELECT rowid, id FROM lt_a2a_api_keys`
2957
3727
  ).all();
2958
3728
  const uuidPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
2959
- for (const row of rows) {
3729
+ for (const row of rows2) {
2960
3730
  if (!row.id || !uuidPattern.test(row.id)) {
2961
3731
  this.db.prepare(
2962
3732
  `UPDATE lt_a2a_api_keys SET id = ? WHERE rowid = ?`
@@ -2965,10 +3735,10 @@ var LocalA2AApiKeyStore = class {
2965
3735
  }
2966
3736
  }
2967
3737
  async findByKey(key) {
2968
- const rows = this.db.prepare(
3738
+ const rows2 = this.db.prepare(
2969
3739
  `SELECT * FROM lt_a2a_api_keys WHERE enabled = 1`
2970
3740
  ).all();
2971
- for (const row of rows) {
3741
+ for (const row of rows2) {
2972
3742
  try {
2973
3743
  if (decrypt3(row.key_value) === key) return mapRowToRecord(row);
2974
3744
  } catch {
@@ -2985,17 +3755,17 @@ var LocalA2AApiKeyStore = class {
2985
3755
  async list(params) {
2986
3756
  const limit = params.limit || 100;
2987
3757
  const offset = params.offset || 0;
2988
- let rows;
3758
+ let rows2;
2989
3759
  if (params.tenantId) {
2990
- rows = this.db.prepare(
3760
+ rows2 = this.db.prepare(
2991
3761
  `SELECT * FROM lt_a2a_api_keys WHERE tenant_id = ? ORDER BY created_at DESC LIMIT ? OFFSET ?`
2992
3762
  ).all(params.tenantId, limit, offset);
2993
3763
  } else {
2994
- rows = this.db.prepare(
3764
+ rows2 = this.db.prepare(
2995
3765
  `SELECT * FROM lt_a2a_api_keys ORDER BY created_at DESC LIMIT ? OFFSET ?`
2996
3766
  ).all(limit, offset);
2997
3767
  }
2998
- return rows.map(mapRowToRecord);
3768
+ return rows2.map(mapRowToRecord);
2999
3769
  }
3000
3770
  async create(input) {
3001
3771
  const id = randomUUID3();
@@ -3005,7 +3775,7 @@ var LocalA2AApiKeyStore = class {
3005
3775
  `INSERT INTO lt_a2a_api_keys (id, key_value, tenant_id, project_id, assistant_ids, label, created_at, updated_at)
3006
3776
  VALUES (?, ?, ?, ?, ?, ?, ?, ?)`
3007
3777
  ).run(id, encrypt3(key), input.tenantId, input.projectId, input.assistantIds ? JSON.stringify(input.assistantIds) : null, input.label || null, now, now);
3008
- const record = {
3778
+ const record2 = {
3009
3779
  id,
3010
3780
  key,
3011
3781
  tenantId: input.tenantId,
@@ -3016,7 +3786,7 @@ var LocalA2AApiKeyStore = class {
3016
3786
  createdAt: parseISO(now),
3017
3787
  updatedAt: parseISO(now)
3018
3788
  };
3019
- return record;
3789
+ return record2;
3020
3790
  }
3021
3791
  async disable(id) {
3022
3792
  const now = nowISO();
@@ -3044,22 +3814,22 @@ var LocalA2AApiKeyStore = class {
3044
3814
  ).run(encrypt3(key), now, id);
3045
3815
  const row = this.db.prepare(`SELECT * FROM lt_a2a_api_keys WHERE id = ?`).get(id);
3046
3816
  if (!row) throw new Error(`A2A API key not found: ${id}`);
3047
- const record = mapRowToRecord(row);
3048
- record.key = key;
3049
- return record;
3817
+ const record2 = mapRowToRecord(row);
3818
+ record2.key = key;
3819
+ return record2;
3050
3820
  }
3051
3821
  async delete(id) {
3052
3822
  this.db.prepare(`DELETE FROM lt_a2a_api_keys WHERE id = ?`).run(id);
3053
3823
  }
3054
3824
  async loadIntoMap() {
3055
- const rows = this.db.prepare(
3825
+ const rows2 = this.db.prepare(
3056
3826
  `SELECT * FROM lt_a2a_api_keys WHERE enabled = 1`
3057
3827
  ).all();
3058
- const map = /* @__PURE__ */ new Map();
3059
- for (const row of rows) {
3828
+ const map2 = /* @__PURE__ */ new Map();
3829
+ for (const row of rows2) {
3060
3830
  try {
3061
3831
  const key = decrypt3(row.key_value);
3062
- map.set(key, {
3832
+ map2.set(key, {
3063
3833
  key,
3064
3834
  tenantId: row.tenant_id,
3065
3835
  projectId: row.project_id,
@@ -3068,7 +3838,7 @@ var LocalA2AApiKeyStore = class {
3068
3838
  } catch {
3069
3839
  }
3070
3840
  }
3071
- return map;
3841
+ return map2;
3072
3842
  }
3073
3843
  };
3074
3844
  function mapRowToRecord(row) {
@@ -3092,6 +3862,10 @@ function mapRowToRecord(row) {
3092
3862
  }
3093
3863
 
3094
3864
  // src/stores/LocalThreadMessageQueueStore.ts
3865
+ import {
3866
+ parseQueuedExecutionMode,
3867
+ parseTrustedRunContext
3868
+ } from "@axiom-lattice/protocols";
3095
3869
  import { randomUUID as randomUUID4 } from "crypto";
3096
3870
  var DDL17 = `
3097
3871
  CREATE TABLE IF NOT EXISTS lt_thread_message_queue (
@@ -3108,6 +3882,8 @@ CREATE TABLE IF NOT EXISTS lt_thread_message_queue (
3108
3882
  status TEXT NOT NULL DEFAULT 'pending',
3109
3883
  command TEXT,
3110
3884
  custom_run_config TEXT,
3885
+ trusted_run_context TEXT,
3886
+ execution_mode TEXT CHECK (execution_mode IS NULL OR execution_mode = 'followup'),
3111
3887
  created_at TEXT NOT NULL
3112
3888
  );
3113
3889
  CREATE INDEX IF NOT EXISTS idx_lt_tmq_thread ON lt_thread_message_queue(thread_id, status);
@@ -3115,10 +3891,20 @@ CREATE INDEX IF NOT EXISTS idx_lt_tmq_pending ON lt_thread_message_queue(status,
3115
3891
  `;
3116
3892
  var LocalThreadMessageQueueStore = class {
3117
3893
  constructor(db) {
3894
+ this.capacityTail = Promise.resolve();
3118
3895
  this.db = db;
3119
3896
  ensureTable(db, DDL17);
3897
+ const columns5 = db.prepare("PRAGMA table_info(lt_thread_message_queue)").all();
3898
+ const names = new Set(columns5.map(({ name }) => name));
3899
+ if (!names.has("trusted_run_context")) {
3900
+ db.run("ALTER TABLE lt_thread_message_queue ADD COLUMN trusted_run_context TEXT");
3901
+ }
3902
+ if (!names.has("execution_mode")) {
3903
+ db.run("ALTER TABLE lt_thread_message_queue ADD COLUMN execution_mode TEXT CHECK (execution_mode IS NULL OR execution_mode = 'followup')");
3904
+ }
3120
3905
  }
3121
3906
  async addMessage(params) {
3907
+ const trusted = validateQueueTrust(params);
3122
3908
  const now = nowISO();
3123
3909
  const id = params.id || randomUUID4();
3124
3910
  const seqRow = this.db.prepare(
@@ -3126,94 +3912,163 @@ var LocalThreadMessageQueueStore = class {
3126
3912
  ).get(params.threadId);
3127
3913
  const nextSeq = seqRow.next_seq;
3128
3914
  this.db.prepare(
3129
- `INSERT INTO lt_thread_message_queue (id, thread_id, tenant_id, assistant_id, workspace_id, project_id, message_content, message_type, sequence_order, priority, command, custom_run_config, created_at)
3130
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
3915
+ `INSERT INTO lt_thread_message_queue (id, thread_id, tenant_id, assistant_id, workspace_id, project_id, message_content, message_type, sequence_order, priority, command, custom_run_config, trusted_run_context, execution_mode, created_at)
3916
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
3131
3917
  ).run(
3132
3918
  id,
3133
3919
  params.threadId,
3134
3920
  params.tenantId,
3135
3921
  params.assistantId,
3136
- params.workspaceId || null,
3137
- params.projectId || null,
3922
+ params.workspaceId ?? null,
3923
+ params.projectId ?? null,
3138
3924
  JSON.stringify(params.content),
3139
3925
  params.type || "human",
3140
3926
  nextSeq,
3141
- params.priority || 0,
3927
+ params.priority ?? 0,
3142
3928
  params.command ? JSON.stringify(params.command) : null,
3143
3929
  params.custom_run_config ? JSON.stringify(params.custom_run_config) : null,
3930
+ trusted.trustedRunContext ? JSON.stringify(trusted.trustedRunContext) : null,
3931
+ trusted.executionMode ?? null,
3144
3932
  now
3145
3933
  );
3146
3934
  return this.getById(id);
3147
3935
  }
3936
+ async addMessageIfCapacity(params, maxSize) {
3937
+ const trusted = validateQueueTrust(params);
3938
+ let release;
3939
+ const previous = this.capacityTail;
3940
+ this.capacityTail = new Promise((resolve) => {
3941
+ release = resolve;
3942
+ });
3943
+ await previous;
3944
+ try {
3945
+ const scope = {
3946
+ tenantId: params.tenantId,
3947
+ assistantId: params.assistantId,
3948
+ workspaceId: params.workspaceId,
3949
+ projectId: params.projectId
3950
+ };
3951
+ const filter = scopeClause(scope);
3952
+ const id = params.id || randomUUID4();
3953
+ const limit = maxSize === Infinity ? -1 : maxSize;
3954
+ const result = this.db.prepare(
3955
+ `INSERT INTO lt_thread_message_queue (id, thread_id, tenant_id, assistant_id, workspace_id, project_id, message_content, message_type, sequence_order, priority, command, custom_run_config, trusted_run_context, execution_mode, created_at)
3956
+ SELECT ?, ?, ?, ?, ?, ?, ?, ?,
3957
+ (SELECT COALESCE(MAX(sequence_order), 0) + 1 FROM lt_thread_message_queue WHERE thread_id = ?),
3958
+ ?, ?, ?, ?, ?,
3959
+ ?
3960
+ WHERE ? = -1 OR (SELECT COUNT(*) FROM lt_thread_message_queue WHERE thread_id = ? AND status = 'pending'${filter.sql}) < ?`
3961
+ ).run(
3962
+ id,
3963
+ params.threadId,
3964
+ params.tenantId,
3965
+ params.assistantId,
3966
+ params.workspaceId ?? null,
3967
+ params.projectId ?? null,
3968
+ JSON.stringify(params.content),
3969
+ params.type || "human",
3970
+ params.threadId,
3971
+ params.priority ?? 0,
3972
+ params.command ? JSON.stringify(params.command) : null,
3973
+ params.custom_run_config ? JSON.stringify(params.custom_run_config) : null,
3974
+ trusted.trustedRunContext ? JSON.stringify(trusted.trustedRunContext) : null,
3975
+ trusted.executionMode ?? null,
3976
+ nowISO(),
3977
+ limit,
3978
+ params.threadId,
3979
+ ...filter.params,
3980
+ limit
3981
+ );
3982
+ return result.changes > 0;
3983
+ } finally {
3984
+ release();
3985
+ }
3986
+ }
3148
3987
  async addMessageAtHead(params) {
3988
+ const trusted = validateQueueTrust(params);
3149
3989
  const now = nowISO();
3150
3990
  const id = params.id || randomUUID4();
3151
3991
  const seqRow = this.db.prepare(
3152
3992
  `SELECT COALESCE(MAX(sequence_order), 0) + 1 as next_seq FROM lt_thread_message_queue WHERE thread_id = ?`
3153
3993
  ).get(params.threadId);
3154
3994
  this.db.prepare(
3155
- `INSERT INTO lt_thread_message_queue (id, thread_id, tenant_id, assistant_id, workspace_id, project_id, message_content, message_type, sequence_order, priority, command, custom_run_config, created_at)
3156
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 100, ?, ?, ?)`
3995
+ `INSERT INTO lt_thread_message_queue (id, thread_id, tenant_id, assistant_id, workspace_id, project_id, message_content, message_type, sequence_order, priority, command, custom_run_config, trusted_run_context, execution_mode, created_at)
3996
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 100, ?, ?, ?, ?, ?)`
3157
3997
  ).run(
3158
3998
  id,
3159
3999
  params.threadId,
3160
4000
  params.tenantId,
3161
4001
  params.assistantId,
3162
- params.workspaceId || null,
3163
- params.projectId || null,
4002
+ params.workspaceId ?? null,
4003
+ params.projectId ?? null,
3164
4004
  JSON.stringify(params.content),
3165
4005
  params.type || "human",
3166
4006
  seqRow.next_seq,
3167
4007
  params.command ? JSON.stringify(params.command) : null,
3168
4008
  params.custom_run_config ? JSON.stringify(params.custom_run_config) : null,
4009
+ trusted.trustedRunContext ? JSON.stringify(trusted.trustedRunContext) : null,
4010
+ trusted.executionMode ?? null,
3169
4011
  now
3170
4012
  );
3171
4013
  return this.getById(id);
3172
4014
  }
3173
- async getPendingMessages(threadId) {
3174
- const rows = this.db.prepare(
3175
- `SELECT * FROM lt_thread_message_queue WHERE thread_id = ? AND status = 'pending' ORDER BY priority DESC, sequence_order ASC`
3176
- ).all(threadId);
3177
- return rows.map(rowToMessage);
3178
- }
3179
- async getProcessingMessages(threadId) {
3180
- const rows = this.db.prepare(
3181
- `SELECT * FROM lt_thread_message_queue WHERE thread_id = ? AND status = 'processing' ORDER BY priority DESC, sequence_order ASC`
3182
- ).all(threadId);
3183
- return rows.map(rowToMessage);
3184
- }
3185
- async getQueueSize(threadId) {
4015
+ async getPendingMessages(threadId, scope) {
4016
+ const filter = scopeClause(scope);
4017
+ const rows2 = this.db.prepare(
4018
+ `SELECT * FROM lt_thread_message_queue WHERE thread_id = ? AND status = 'pending'${filter.sql} ORDER BY priority DESC, sequence_order ASC`
4019
+ ).all(threadId, ...filter.params);
4020
+ return rows2.map(rowToMessage);
4021
+ }
4022
+ async getProcessingMessages(threadId, scope) {
4023
+ const filter = scopeClause(scope);
4024
+ const rows2 = this.db.prepare(
4025
+ `SELECT * FROM lt_thread_message_queue WHERE thread_id = ? AND status = 'processing'${filter.sql} ORDER BY priority DESC, sequence_order ASC`
4026
+ ).all(threadId, ...filter.params);
4027
+ return rows2.map(rowToMessage);
4028
+ }
4029
+ async getQueueSize(threadId, scope) {
4030
+ const filter = scopeClause(scope);
3186
4031
  const row = this.db.prepare(
3187
- `SELECT COUNT(*) as count FROM lt_thread_message_queue WHERE thread_id = ? AND status = 'pending'`
3188
- ).get(threadId);
4032
+ `SELECT COUNT(*) as count FROM lt_thread_message_queue WHERE thread_id = ? AND status = 'pending'${filter.sql}`
4033
+ ).get(threadId, ...filter.params);
3189
4034
  return row.count;
3190
4035
  }
3191
4036
  async getThreadsWithPendingMessages() {
3192
- const rows = this.db.prepare(
3193
- `SELECT DISTINCT ON (thread_id) tenant_id, assistant_id, thread_id, workspace_id, project_id FROM lt_thread_message_queue WHERE status IN ('pending', 'processing') ORDER BY thread_id`
4037
+ const rows2 = this.db.prepare(
4038
+ `SELECT tenant_id, assistant_id, thread_id, workspace_id, project_id FROM lt_thread_message_queue WHERE status IN ('pending', 'processing') GROUP BY tenant_id, assistant_id, thread_id, workspace_id, project_id ORDER BY thread_id`
3194
4039
  ).all();
3195
- return rows.map((r) => ({
4040
+ return rows2.map((r) => ({
3196
4041
  tenantId: r.tenant_id,
3197
4042
  assistantId: r.assistant_id,
3198
4043
  threadId: r.thread_id,
3199
- workspaceId: r.workspace_id || void 0,
3200
- projectId: r.project_id || void 0
4044
+ workspaceId: r.workspace_id,
4045
+ projectId: r.project_id
3201
4046
  }));
3202
4047
  }
3203
- async removeMessage(messageId) {
3204
- const result = this.db.prepare(`DELETE FROM lt_thread_message_queue WHERE id = ?`).run(messageId);
4048
+ async removeMessage(messageId, scope) {
4049
+ const filter = scopeClause(scope);
4050
+ const result = this.db.prepare(`DELETE FROM lt_thread_message_queue WHERE id = ?${filter.sql}`).run(messageId, ...filter.params);
3205
4051
  return result.changes > 0;
3206
4052
  }
3207
- async clearMessages(threadId) {
3208
- this.db.prepare(`DELETE FROM lt_thread_message_queue WHERE thread_id = ?`).run(threadId);
4053
+ async clearMessages(threadId, scope) {
4054
+ const filter = scopeClause(scope);
4055
+ this.db.prepare(`DELETE FROM lt_thread_message_queue WHERE thread_id = ?${filter.sql}`).run(threadId, ...filter.params);
3209
4056
  }
3210
- async markProcessing(messageId) {
3211
- this.db.prepare(`UPDATE lt_thread_message_queue SET status = 'processing' WHERE id = ?`).run(messageId);
4057
+ async markProcessing(messageId, customRunConfig, scope) {
4058
+ const filter = scopeClause(scope);
4059
+ if (customRunConfig === void 0) {
4060
+ this.db.prepare(`UPDATE lt_thread_message_queue SET status = 'processing' WHERE id = ?${filter.sql}`).run(messageId, ...filter.params);
4061
+ return;
4062
+ }
4063
+ this.db.prepare(
4064
+ `UPDATE lt_thread_message_queue SET status = 'processing', custom_run_config = ? WHERE id = ?${filter.sql}`
4065
+ ).run(JSON.stringify(customRunConfig), messageId, ...filter.params);
3212
4066
  }
3213
- async resetProcessingToPending(threadId) {
4067
+ async resetProcessingToPending(threadId, scope) {
4068
+ const filter = scopeClause(scope);
3214
4069
  const result = this.db.prepare(
3215
- `UPDATE lt_thread_message_queue SET status = 'pending' WHERE thread_id = ? AND status = 'processing'`
3216
- ).run(threadId);
4070
+ `UPDATE lt_thread_message_queue SET status = 'pending' WHERE thread_id = ? AND status = 'processing'${filter.sql}`
4071
+ ).run(threadId, ...filter.params);
3217
4072
  return result.changes;
3218
4073
  }
3219
4074
  getById(id) {
@@ -3221,6 +4076,20 @@ var LocalThreadMessageQueueStore = class {
3221
4076
  return rowToMessage(row);
3222
4077
  }
3223
4078
  };
4079
+ function validateQueueTrust(params) {
4080
+ return {
4081
+ trustedRunContext: params.trusted_run_context === void 0 ? void 0 : parseTrustedRunContext(params.trusted_run_context),
4082
+ executionMode: params.execution_mode === void 0 ? void 0 : parseQueuedExecutionMode(params.execution_mode)
4083
+ };
4084
+ }
4085
+ function scopeClause(scope) {
4086
+ if (!scope) return { sql: "", params: [] };
4087
+ const entries = ["tenantId", "assistantId", "workspaceId", "projectId"].map((key) => [key, scope[key]]);
4088
+ return {
4089
+ sql: entries.map(([key, value]) => ` AND ${key.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`)} ${value == null ? "IS NULL" : "= ?"}`).join(""),
4090
+ params: entries.flatMap(([, value]) => value == null ? [] : [value])
4091
+ };
4092
+ }
3224
4093
  function rowToMessage(row) {
3225
4094
  return {
3226
4095
  id: row.id,
@@ -3230,7 +4099,9 @@ function rowToMessage(row) {
3230
4099
  createdAt: parseISO(row.created_at),
3231
4100
  priority: row.priority || 0,
3232
4101
  command: row.command ? JSON.parse(row.command) : void 0,
3233
- custom_run_config: row.custom_run_config ? JSON.parse(row.custom_run_config) : void 0
4102
+ custom_run_config: row.custom_run_config ? JSON.parse(row.custom_run_config) : void 0,
4103
+ trusted_run_context: row.trusted_run_context ? parseTrustedRunContext(JSON.parse(row.trusted_run_context)) : void 0,
4104
+ execution_mode: row.execution_mode == null ? void 0 : parseQueuedExecutionMode(row.execution_mode)
3234
4105
  };
3235
4106
  }
3236
4107
 
@@ -3257,10 +4128,10 @@ var LocalSkillStore = class {
3257
4128
  ensureTable(db, DDL18);
3258
4129
  }
3259
4130
  async getAllSkills(tenantId, _context) {
3260
- const rows = this.db.prepare(
4131
+ const rows2 = this.db.prepare(
3261
4132
  `SELECT * FROM lt_skills WHERE tenant_id = ? ORDER BY created_at DESC`
3262
4133
  ).all(tenantId);
3263
- return rows.map(mapRowToSkill);
4134
+ return rows2.map(mapRowToSkill);
3264
4135
  }
3265
4136
  async getSkillById(tenantId, id, _context) {
3266
4137
  const row = this.db.prepare(
@@ -3365,16 +4236,16 @@ var LocalSkillStore = class {
3365
4236
  return all.filter((s) => s.metadata?.[metadataKey] === metadataValue);
3366
4237
  }
3367
4238
  async filterByCompatibility(tenantId, compatibility, _context) {
3368
- const rows = this.db.prepare(
4239
+ const rows2 = this.db.prepare(
3369
4240
  `SELECT * FROM lt_skills WHERE tenant_id = ? AND compatibility = ? ORDER BY created_at DESC`
3370
4241
  ).all(tenantId, compatibility);
3371
- return rows.map(mapRowToSkill);
4242
+ return rows2.map(mapRowToSkill);
3372
4243
  }
3373
4244
  async filterByLicense(tenantId, license, _context) {
3374
- const rows = this.db.prepare(
4245
+ const rows2 = this.db.prepare(
3375
4246
  `SELECT * FROM lt_skills WHERE tenant_id = ? AND license = ? ORDER BY created_at DESC`
3376
4247
  ).all(tenantId, license);
3377
- return rows.map(mapRowToSkill);
4248
+ return rows2.map(mapRowToSkill);
3378
4249
  }
3379
4250
  async getSubSkills(tenantId, parentSkillName, _context) {
3380
4251
  const all = await this.getAllSkills(tenantId);
@@ -3586,40 +4457,40 @@ var LocalScheduleStorage = class {
3586
4457
  this.db.prepare(`DELETE FROM lt_scheduled_tasks WHERE task_id = ?`).run(taskId);
3587
4458
  }
3588
4459
  async getActiveTasks() {
3589
- const rows = this.db.prepare(
4460
+ const rows2 = this.db.prepare(
3590
4461
  `SELECT * FROM lt_scheduled_tasks WHERE status IN ('pending', 'paused') ORDER BY created_at ASC`
3591
4462
  ).all();
3592
- return rows.map(mapRowToTask);
4463
+ return rows2.map(mapRowToTask);
3593
4464
  }
3594
4465
  async getTasksByType(taskType) {
3595
- const rows = this.db.prepare(
4466
+ const rows2 = this.db.prepare(
3596
4467
  `SELECT * FROM lt_scheduled_tasks WHERE task_type = ? ORDER BY created_at DESC`
3597
4468
  ).all(taskType);
3598
- return rows.map(mapRowToTask);
4469
+ return rows2.map(mapRowToTask);
3599
4470
  }
3600
4471
  async getTasksByStatus(status) {
3601
- const rows = this.db.prepare(
4472
+ const rows2 = this.db.prepare(
3602
4473
  `SELECT * FROM lt_scheduled_tasks WHERE status = ? ORDER BY created_at DESC`
3603
4474
  ).all(status);
3604
- return rows.map(mapRowToTask);
4475
+ return rows2.map(mapRowToTask);
3605
4476
  }
3606
4477
  async getTasksByExecutionType(executionType) {
3607
- const rows = this.db.prepare(
4478
+ const rows2 = this.db.prepare(
3608
4479
  `SELECT * FROM lt_scheduled_tasks WHERE execution_type = ? ORDER BY created_at DESC`
3609
4480
  ).all(executionType);
3610
- return rows.map(mapRowToTask);
4481
+ return rows2.map(mapRowToTask);
3611
4482
  }
3612
4483
  async getTasksByAssistantId(assistantId) {
3613
- const rows = this.db.prepare(
4484
+ const rows2 = this.db.prepare(
3614
4485
  `SELECT * FROM lt_scheduled_tasks WHERE assistant_id = ? ORDER BY created_at DESC`
3615
4486
  ).all(assistantId);
3616
- return rows.map(mapRowToTask);
4487
+ return rows2.map(mapRowToTask);
3617
4488
  }
3618
4489
  async getTasksByThreadId(threadId) {
3619
- const rows = this.db.prepare(
4490
+ const rows2 = this.db.prepare(
3620
4491
  `SELECT * FROM lt_scheduled_tasks WHERE thread_id = ? ORDER BY created_at DESC`
3621
4492
  ).all(threadId);
3622
- return rows.map(mapRowToTask);
4493
+ return rows2.map(mapRowToTask);
3623
4494
  }
3624
4495
  async getAllTasks(filters) {
3625
4496
  const conditions = [];
@@ -3659,8 +4530,8 @@ var LocalScheduleStorage = class {
3659
4530
  query += ` OFFSET ?`;
3660
4531
  values.push(filters.offset);
3661
4532
  }
3662
- const rows = this.db.prepare(query).all(...values);
3663
- return rows.map(mapRowToTask);
4533
+ const rows2 = this.db.prepare(query).all(...values);
4534
+ return rows2.map(mapRowToTask);
3664
4535
  }
3665
4536
  async countTasks(filters) {
3666
4537
  const conditions = [];
@@ -3733,6 +4604,15 @@ function mapRowToTask(row) {
3733
4604
  // src/stores/LocalTaskStore.ts
3734
4605
  import { randomUUID as randomUUID5 } from "crypto";
3735
4606
  var MAX_CANONICAL_TASK_TIME = "9999-12-31T23:59:59.999Z";
4607
+ var TASK_STATUSES = /* @__PURE__ */ new Set([
4608
+ "pending",
4609
+ "in_progress",
4610
+ "review",
4611
+ "failed",
4612
+ "interrupted",
4613
+ "completed",
4614
+ "cancelled"
4615
+ ]);
3736
4616
  var NEXT_UPDATED_AT_SQL = `updated_at = CASE
3737
4617
  WHEN strftime('%Y-%m-%dT%H:%M:%fZ', updated_at) = updated_at AND updated_at >= ?
3738
4618
  THEN strftime('%Y-%m-%dT%H:%M:%fZ', updated_at, '+0.001 seconds')
@@ -3805,6 +4685,9 @@ function nextISO(previous) {
3805
4685
  const previousMs = new Date(previous).getTime();
3806
4686
  return new Date(Math.max(Date.now(), previousMs + 1)).toISOString();
3807
4687
  }
4688
+ function assertPage(limit, offset = 0) {
4689
+ if (!Number.isSafeInteger(limit) || limit < 1 || limit > 100 || !Number.isSafeInteger(offset) || offset < 0) throw new RangeError("Invalid task page");
4690
+ }
3808
4691
  var LocalTaskStore = class {
3809
4692
  constructor(db) {
3810
4693
  this.db = db;
@@ -3818,10 +4701,10 @@ var LocalTaskStore = class {
3818
4701
  this.ensureColumn("lt_tasks", "files", "files TEXT");
3819
4702
  }
3820
4703
  /** Add a column if it does not exist (SQLite version compatible). */
3821
- ensureColumn(table, column, ddl) {
4704
+ ensureColumn(table, column, ddl2) {
3822
4705
  const cols = this.db.prepare(`PRAGMA table_info(${table})`).all();
3823
4706
  if (!cols.some((c) => c.name === column)) {
3824
- this.db.exec(`ALTER TABLE ${table} ADD COLUMN ${ddl}`);
4707
+ this.db.exec(`ALTER TABLE ${table} ADD COLUMN ${ddl2}`);
3825
4708
  }
3826
4709
  }
3827
4710
  async create(params) {
@@ -3887,7 +4770,8 @@ var LocalTaskStore = class {
3887
4770
  conditions.push("workspace_id = ?");
3888
4771
  params.push(filter.workspaceId);
3889
4772
  }
3890
- if (filter.projectId) {
4773
+ if (filter.projectId === null) conditions.push("(project_id IS NULL OR project_id = '' OR project_id = 'default')");
4774
+ else if (filter.projectId !== void 0) {
3891
4775
  conditions.push("project_id = ?");
3892
4776
  params.push(filter.projectId);
3893
4777
  }
@@ -3917,10 +4801,44 @@ var LocalTaskStore = class {
3917
4801
  const where = conditions.join(" AND ");
3918
4802
  const limit = filter.limit || 100;
3919
4803
  const offset = filter.offset || 0;
3920
- const rows = this.db.prepare(
3921
- `SELECT * FROM lt_tasks WHERE ${where} ORDER BY created_at DESC LIMIT ? OFFSET ?`
4804
+ const rows2 = this.db.prepare(
4805
+ `SELECT * FROM lt_tasks WHERE ${where} ORDER BY created_at DESC, id DESC LIMIT ? OFFSET ?`
3922
4806
  ).all(...params, limit, offset);
3923
- return rows.map(mapRowToTask2);
4807
+ return rows2.map(mapRowToTask2);
4808
+ }
4809
+ /** Lists exact project tasks containing a string dependency. */
4810
+ async listDependents(query) {
4811
+ assertPage(query.limit, query.offset);
4812
+ if (query.statuses.length === 0 || query.statuses.some((status) => !TASK_STATUSES.has(status))) {
4813
+ throw new RangeError("Invalid task statuses");
4814
+ }
4815
+ const rows2 = this.db.prepare(
4816
+ `SELECT task.* FROM lt_tasks AS task
4817
+ WHERE task.tenant_id = ? AND task.workspace_id = ? AND task.project_id = ?
4818
+ AND task.status IN (${query.statuses.map(() => "?").join(", ")})
4819
+ AND task.dependencies IS NOT NULL
4820
+ AND json_valid(task.dependencies) = 1
4821
+ AND json_type(CASE WHEN json_valid(task.dependencies)=1
4822
+ THEN task.dependencies ELSE '[]' END) = 'array'
4823
+ AND EXISTS (
4824
+ SELECT 1 FROM json_each(
4825
+ CASE WHEN json_valid(task.dependencies)=1 THEN
4826
+ CASE WHEN json_type(task.dependencies)='array' THEN task.dependencies ELSE '[]' END
4827
+ ELSE '[]' END
4828
+ ) AS dependency
4829
+ WHERE dependency.type = 'text' AND dependency.value = ?
4830
+ )
4831
+ ORDER BY task.created_at DESC, task.id DESC LIMIT ? OFFSET ?`
4832
+ ).all(
4833
+ query.tenantId,
4834
+ query.workspaceId,
4835
+ query.projectId,
4836
+ ...query.statuses,
4837
+ query.dependencyTaskId,
4838
+ query.limit,
4839
+ query.offset
4840
+ );
4841
+ return rows2.map(mapRowToTask2);
3924
4842
  }
3925
4843
  async update(tenantId, id, updates) {
3926
4844
  const existing = await this.getById(tenantId, id);
@@ -4272,6 +5190,54 @@ var LocalTaskStore = class {
4272
5190
  if (result.changes === 0) return null;
4273
5191
  return this.getById(tenantId, id);
4274
5192
  }
5193
+ /** Atomically update only while status, timestamp, owner, and Project scope match. */
5194
+ async updateIfSnapshot(tenantId, id, updates, snapshot) {
5195
+ const expectedIso = new Date(snapshot.updatedAt).toISOString();
5196
+ if (expectedIso === MAX_CANONICAL_TASK_TIME) return null;
5197
+ const setClauses = ["updated_at = ?"];
5198
+ const values = [nextISO(expectedIso)];
5199
+ const fields = [
5200
+ ["title", "title"],
5201
+ ["description", "description"],
5202
+ ["status", "status"],
5203
+ ["priority", "priority"],
5204
+ ["dueDate", "due_date"],
5205
+ ["metadata", "metadata", true],
5206
+ ["parentId", "parent_id"],
5207
+ ["sourceId", "source_id"],
5208
+ ["context", "context", true],
5209
+ ["ownerType", "owner_type"],
5210
+ ["ownerId", "owner_id"],
5211
+ ["requireReview", "require_review"],
5212
+ ["dependencies", "dependencies", true],
5213
+ ["result", "result"],
5214
+ ["failureReason", "failure_reason"],
5215
+ ["workspaceId", "workspace_id"],
5216
+ ["projectId", "project_id"],
5217
+ ["files", "files", true]
5218
+ ];
5219
+ for (const [field, column, json] of fields) {
5220
+ const value = updates[field];
5221
+ if (value === void 0) continue;
5222
+ setClauses.push(`${column} = ?`);
5223
+ values.push(json && value !== null ? JSON.stringify(value) : field === "requireReview" ? value ? 1 : 0 : value);
5224
+ }
5225
+ values.push(
5226
+ tenantId,
5227
+ id,
5228
+ snapshot.status,
5229
+ expectedIso,
5230
+ snapshot.ownerType,
5231
+ snapshot.ownerId,
5232
+ snapshot.workspaceId,
5233
+ snapshot.projectId
5234
+ );
5235
+ const result = this.db.prepare(
5236
+ `UPDATE lt_tasks SET ${setClauses.join(", ")} WHERE tenant_id = ? AND id = ? AND status = ?
5237
+ AND updated_at = ? AND owner_type = ? AND owner_id = ? AND workspace_id IS ? AND project_id IS ?`
5238
+ ).run(...values);
5239
+ return result.changes === 0 ? null : this.getById(tenantId, id);
5240
+ }
4275
5241
  /** Atomically update a child only when both child and parent snapshots match. */
4276
5242
  async updateIfStatusUpdatedAtAndParentUpdatedAt(tenantId, id, updates, expectedStatuses, expectedUpdatedAt, parentId, expectedParentUpdatedAt) {
4277
5243
  if (expectedStatuses.length === 0) return null;
@@ -4372,10 +5338,36 @@ var LocalTaskStore = class {
4372
5338
  ).run(tenantId, id);
4373
5339
  return result.changes > 0;
4374
5340
  }
5341
+ /** Atomically delete only while status, timestamp, owner, and Project scope match. */
5342
+ async deleteIfSnapshot(tenantId, id, snapshot) {
5343
+ const result = this.db.prepare(
5344
+ `DELETE FROM lt_tasks WHERE tenant_id = ? AND id = ? AND status = ? AND updated_at = ?
5345
+ AND owner_type = ? AND owner_id = ? AND workspace_id IS ? AND project_id IS ?`
5346
+ ).run(
5347
+ tenantId,
5348
+ id,
5349
+ snapshot.status,
5350
+ new Date(snapshot.updatedAt).toISOString(),
5351
+ snapshot.ownerType,
5352
+ snapshot.ownerId,
5353
+ snapshot.workspaceId,
5354
+ snapshot.projectId
5355
+ );
5356
+ return result.changes > 0;
5357
+ }
4375
5358
  };
4376
5359
 
4377
5360
  // src/stores/LocalTaskWorkItemStore.ts
5361
+ import { MAX_PENDING_EXECUTION_RESULTS_LIMIT } from "@axiom-lattice/protocols";
4378
5362
  import { v4 as uuid2 } from "uuid";
5363
+ var PROJECT_LIFECYCLE_ACTIONS = /* @__PURE__ */ new Set([
5364
+ "in_progress",
5365
+ "interrupted",
5366
+ "failed",
5367
+ "completed",
5368
+ "cancelled",
5369
+ "reassigned"
5370
+ ]);
4379
5371
  var DDL21 = `
4380
5372
  CREATE TABLE IF NOT EXISTS lt_task_work_items (
4381
5373
  id TEXT NOT NULL,
@@ -4393,6 +5385,15 @@ CREATE TABLE IF NOT EXISTS lt_task_work_items (
4393
5385
  PRIMARY KEY (tenant_id, id)
4394
5386
  );
4395
5387
  `;
5388
+ function parseDetail(raw) {
5389
+ if (!raw) return void 0;
5390
+ try {
5391
+ const parsed = JSON.parse(raw);
5392
+ return parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : void 0;
5393
+ } catch {
5394
+ return void 0;
5395
+ }
5396
+ }
4396
5397
  function mapRowToWorkItem(row) {
4397
5398
  return {
4398
5399
  id: row.id,
@@ -4402,7 +5403,7 @@ function mapRowToWorkItem(row) {
4402
5403
  actor: row.actor,
4403
5404
  threadId: row.thread_id ?? void 0,
4404
5405
  summary: row.summary ?? void 0,
4405
- detail: row.detail ? JSON.parse(row.detail) : void 0,
5406
+ detail: parseDetail(row.detail),
4406
5407
  attempt: row.attempt ?? void 0,
4407
5408
  workspaceId: row.workspace_id ?? void 0,
4408
5409
  projectId: row.project_id ?? void 0,
@@ -4410,6 +5411,34 @@ function mapRowToWorkItem(row) {
4410
5411
  createdAt: parseISO(row.created_at)
4411
5412
  };
4412
5413
  }
5414
+ function assertAndCloneProjectLifecycleEvent(item) {
5415
+ let milliseconds;
5416
+ try {
5417
+ milliseconds = Date.prototype.getTime.call(item.createdAt);
5418
+ } catch {
5419
+ throw new Error("Invalid project lifecycle event row");
5420
+ }
5421
+ if (!Number.isFinite(milliseconds) || typeof item.id !== "string" || item.id.length === 0 || typeof item.eventKey !== "string" || item.eventKey.length === 0) {
5422
+ throw new Error("Invalid project lifecycle event row");
5423
+ }
5424
+ return { ...item, createdAt: new Date(milliseconds) };
5425
+ }
5426
+ function assertLifecyclePage(query) {
5427
+ if (!Number.isSafeInteger(query.limit) || query.limit < 1 || query.limit > 100 || query.actions.length === 0 || query.actions.some((action) => !PROJECT_LIFECYCLE_ACTIONS.has(action))) {
5428
+ throw new RangeError("Invalid project lifecycle event page");
5429
+ }
5430
+ if (!query.before) return null;
5431
+ let milliseconds;
5432
+ try {
5433
+ milliseconds = Date.prototype.getTime.call(query.before.createdAt);
5434
+ } catch {
5435
+ throw new RangeError("Invalid project lifecycle event cursor");
5436
+ }
5437
+ if (!Number.isFinite(milliseconds) || typeof query.before.id !== "string" || query.before.id.length === 0) {
5438
+ throw new RangeError("Invalid project lifecycle event cursor");
5439
+ }
5440
+ return milliseconds;
5441
+ }
4413
5442
  var LocalTaskWorkItemStore = class {
4414
5443
  constructor(db) {
4415
5444
  this.db = db;
@@ -4419,12 +5448,20 @@ var LocalTaskWorkItemStore = class {
4419
5448
  this.ensureColumn("lt_task_work_items", "event_key", "event_key TEXT");
4420
5449
  this.db.exec(`CREATE UNIQUE INDEX IF NOT EXISTS idx_task_work_items_event_key
4421
5450
  ON lt_task_work_items (tenant_id, task_id, event_key);`);
5451
+ this.db.exec(`CREATE INDEX IF NOT EXISTS idx_task_work_items_pending_order
5452
+ ON lt_task_work_items (tenant_id, task_id, action, created_at DESC, id DESC);`);
5453
+ this.db.exec(`CREATE INDEX IF NOT EXISTS idx_task_work_items_reconciled_result
5454
+ ON lt_task_work_items (tenant_id, task_id, json_extract(detail, '$.executionResultId'))
5455
+ WHERE action = 'execution_reconciled';`);
5456
+ this.db.exec(`CREATE INDEX IF NOT EXISTS idx_task_work_items_project_lifecycle
5457
+ ON lt_task_work_items (tenant_id, workspace_id, project_id, created_at DESC, id DESC)
5458
+ WHERE event_key IS NOT NULL;`);
4422
5459
  }
4423
5460
  /** Add a column if it does not exist (SQLite version compatible). */
4424
- ensureColumn(table, column, ddl) {
5461
+ ensureColumn(table, column, ddl2) {
4425
5462
  const cols = this.db.prepare(`PRAGMA table_info(${table})`).all();
4426
5463
  if (!cols.some((c) => c.name === column)) {
4427
- this.db.exec(`ALTER TABLE ${table} ADD COLUMN ${ddl}`);
5464
+ this.db.exec(`ALTER TABLE ${table} ADD COLUMN ${ddl2}`);
4428
5465
  }
4429
5466
  }
4430
5467
  async create(params) {
@@ -4452,6 +5489,41 @@ var LocalTaskWorkItemStore = class {
4452
5489
  ).get(params.tenantId, id);
4453
5490
  return mapRowToWorkItem(row);
4454
5491
  }
5492
+ /** Atomically inserts a work item by selecting one exact task snapshot. */
5493
+ async createIfTaskSnapshot(params, snapshot) {
5494
+ const id = uuid2();
5495
+ const result = this.db.prepare(
5496
+ `INSERT INTO lt_task_work_items
5497
+ (id, tenant_id, task_id, action, actor, thread_id, summary, detail, attempt, workspace_id, project_id, created_at)
5498
+ SELECT ?, task.tenant_id, task.id, ?, ?, ?, ?, ?, ?, task.workspace_id, task.project_id, ?
5499
+ FROM lt_tasks AS task
5500
+ WHERE task.tenant_id = ? AND task.id = ? AND task.status = ? AND task.updated_at = ?
5501
+ AND task.owner_type = ? AND task.owner_id = ? AND task.workspace_id IS ? AND task.project_id IS ?
5502
+ AND ? IS task.workspace_id AND ? IS task.project_id`
5503
+ ).run(
5504
+ id,
5505
+ params.action,
5506
+ params.actor,
5507
+ params.threadId || null,
5508
+ params.summary || null,
5509
+ params.detail ? JSON.stringify(params.detail) : null,
5510
+ params.attempt ?? null,
5511
+ nowISO(),
5512
+ params.tenantId,
5513
+ params.taskId,
5514
+ snapshot.status,
5515
+ new Date(snapshot.updatedAt).toISOString(),
5516
+ snapshot.ownerType,
5517
+ snapshot.ownerId,
5518
+ snapshot.workspaceId,
5519
+ snapshot.projectId,
5520
+ params.workspaceId,
5521
+ params.projectId
5522
+ );
5523
+ if (result.changes === 0) return null;
5524
+ const row = this.db.prepare(`SELECT * FROM lt_task_work_items WHERE tenant_id = ? AND id = ?`).get(params.tenantId, id);
5525
+ return row ? mapRowToWorkItem(row) : null;
5526
+ }
4455
5527
  /** Find an event by its tenant- and task-scoped key without pagination. */
4456
5528
  async findByEventKey(tenantId, taskId, eventKey) {
4457
5529
  const row = this.db.prepare(
@@ -4504,10 +5576,160 @@ var LocalTaskWorkItemStore = class {
4504
5576
  const limit = filter.limit || 100;
4505
5577
  const offset = filter.offset || 0;
4506
5578
  const order = filter.order === "asc" ? "ASC" : "DESC";
4507
- const rows = this.db.prepare(
5579
+ const rows2 = this.db.prepare(
4508
5580
  `SELECT * FROM lt_task_work_items WHERE ${where} ORDER BY created_at ${order}, id ${order} LIMIT ? OFFSET ?`
4509
5581
  ).all(...params, limit, offset);
4510
- return rows.map(mapRowToWorkItem);
5582
+ return rows2.map(mapRowToWorkItem);
5583
+ }
5584
+ /** List pending execution results using one bounded SQLite anti-join query. */
5585
+ async listPendingExecutionResults(params) {
5586
+ if (!Number.isSafeInteger(params.limit) || params.limit < 0 || params.limit > MAX_PENDING_EXECUTION_RESULTS_LIMIT) {
5587
+ const error = new RangeError(`limit must be a safe integer between 0 and ${MAX_PENDING_EXECUTION_RESULTS_LIMIT}`);
5588
+ error.code = "INVALID_LIMIT";
5589
+ throw error;
5590
+ }
5591
+ if (params.limit === 0) return [];
5592
+ const rows2 = this.db.prepare(
5593
+ `SELECT result.*
5594
+ FROM lt_task_work_items AS result
5595
+ WHERE result.tenant_id = ?
5596
+ AND result.task_id = ?
5597
+ AND result.action = 'execution_result'
5598
+ AND substr(result.event_key, 1, 17) = 'execution-result:'
5599
+ AND length(result.event_key) > 17
5600
+ AND result.event_key NOT GLOB '*[^A-Za-z0-9._:-]*'
5601
+ AND NOT EXISTS (
5602
+ SELECT 1
5603
+ FROM lt_task_work_items AS reconciled
5604
+ WHERE reconciled.tenant_id = result.tenant_id
5605
+ AND reconciled.task_id = result.task_id
5606
+ AND reconciled.action = 'execution_reconciled'
5607
+ AND json_extract(reconciled.detail, '$.executionResultId') = result.event_key
5608
+ )
5609
+ ORDER BY result.created_at DESC, result.id DESC
5610
+ LIMIT ?`
5611
+ ).all(params.tenantId, params.taskId, params.limit);
5612
+ return rows2.map(mapRowToWorkItem);
5613
+ }
5614
+ /** Lists canonical project lifecycle events with an exclusive cursor. */
5615
+ async listProjectLifecycleEvents(query) {
5616
+ const cursorMilliseconds = assertLifecyclePage(query);
5617
+ const cursor = cursorMilliseconds === null ? null : new Date(cursorMilliseconds).toISOString();
5618
+ const rows2 = this.db.prepare(
5619
+ `SELECT * FROM lt_task_work_items
5620
+ WHERE tenant_id = ? AND workspace_id = ? AND project_id = ?
5621
+ AND action IN (${query.actions.map(() => "?").join(", ")})
5622
+ AND event_key IS NOT NULL AND event_key <> ''
5623
+ AND (? IS NULL OR created_at < ? OR (created_at = ? AND id < ?))
5624
+ ORDER BY created_at DESC, id DESC LIMIT ?`
5625
+ ).all(
5626
+ query.tenantId,
5627
+ query.workspaceId,
5628
+ query.projectId,
5629
+ ...query.actions,
5630
+ cursor,
5631
+ cursor,
5632
+ cursor,
5633
+ query.before?.id ?? null,
5634
+ query.limit
5635
+ );
5636
+ return rows2.map(mapRowToWorkItem).map(assertAndCloneProjectLifecycleEvent);
5637
+ }
5638
+ };
5639
+
5640
+ // src/stores/LocalCapabilityBundleStore.ts
5641
+ import { randomUUID as randomUUID6 } from "crypto";
5642
+ var DDL22 = `CREATE TABLE IF NOT EXISTS lt_capability_bundles (id TEXT NOT NULL, tenant_id TEXT NOT NULL, bundle_key TEXT NOT NULL, name TEXT NOT NULL, description TEXT, capabilities TEXT NOT NULL DEFAULT '[]', created_at TEXT NOT NULL, updated_at TEXT NOT NULL, PRIMARY KEY (tenant_id, id), UNIQUE (tenant_id, bundle_key)); CREATE INDEX IF NOT EXISTS idx_lt_capability_bundles_tenant ON lt_capability_bundles(tenant_id);`;
5643
+ var duplicateMessage = "Capability bundle key already exists for tenant";
5644
+ function map(row) {
5645
+ return { id: row.id, tenantId: row.tenant_id, key: row.bundle_key, name: row.name, description: row.description ?? void 0, capabilities: JSON.parse(row.capabilities), createdAt: parseISO(row.created_at).toISOString(), updatedAt: parseISO(row.updated_at).toISOString() };
5646
+ }
5647
+ function isConstraint(error) {
5648
+ return error instanceof Error && /UNIQUE constraint failed/.test(error.message);
5649
+ }
5650
+ function nextTimestamp(previous) {
5651
+ const now = Date.now();
5652
+ const previousTime = previous ? Date.parse(previous) : Number.NaN;
5653
+ return new Date(Math.max(now, Number.isFinite(previousTime) ? previousTime + 1 : now)).toISOString();
5654
+ }
5655
+ var LocalCapabilityBundleStore = class {
5656
+ /**
5657
+ * Creates a capability bundle store over an initialized sql.js database.
5658
+ *
5659
+ * @param db - Shared local database wrapper used for persistence.
5660
+ */
5661
+ constructor(db) {
5662
+ this.db = db;
5663
+ ensureTable(db, DDL22);
5664
+ }
5665
+ async listByTenant(tenantId) {
5666
+ return this.db.prepare("SELECT * FROM lt_capability_bundles WHERE tenant_id = ? ORDER BY created_at").all(tenantId).map(map);
5667
+ }
5668
+ async getById(tenantId, id) {
5669
+ const row = this.db.prepare("SELECT * FROM lt_capability_bundles WHERE tenant_id = ? AND id = ?").get(tenantId, id);
5670
+ return row ? map(row) : null;
5671
+ }
5672
+ async getManyByIds(tenantId, ids) {
5673
+ return ids.flatMap((id) => {
5674
+ const row = this.db.prepare("SELECT * FROM lt_capability_bundles WHERE tenant_id = ? AND id = ?").get(tenantId, id);
5675
+ return row ? [map(row)] : [];
5676
+ });
5677
+ }
5678
+ async create(tenantId, input) {
5679
+ const now = nextTimestamp();
5680
+ const id = randomUUID6();
5681
+ try {
5682
+ this.db.prepare("INSERT INTO lt_capability_bundles (id, tenant_id, bundle_key, name, description, capabilities, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)").run(id, tenantId, input.key, input.name, input.description ?? null, JSON.stringify(input.capabilities), now, now);
5683
+ } catch (error) {
5684
+ if (isConstraint(error)) throw new Error(duplicateMessage);
5685
+ throw error;
5686
+ }
5687
+ return await this.getById(tenantId, id);
5688
+ }
5689
+ async update(tenantId, id, input) {
5690
+ const existing = await this.getById(tenantId, id);
5691
+ if (!existing) return null;
5692
+ if (input.expectedUpdatedAt !== void 0 && input.expectedUpdatedAt !== existing.updatedAt) return { status: "conflict" };
5693
+ const fields = [];
5694
+ const values = [];
5695
+ const add = (field, value) => {
5696
+ fields.push(`${field} = ?`);
5697
+ values.push(value);
5698
+ };
5699
+ if (input.name !== void 0) add("name", input.name);
5700
+ if (Object.prototype.hasOwnProperty.call(input, "description")) add("description", input.description ?? null);
5701
+ if (input.capabilities !== void 0) add("capabilities", JSON.stringify(input.capabilities));
5702
+ if (!fields.length) return existing;
5703
+ const now = nextTimestamp(existing.updatedAt);
5704
+ fields.push("updated_at = ?");
5705
+ values.push(now, tenantId, id);
5706
+ try {
5707
+ if (input.expectedUpdatedAt !== void 0) {
5708
+ const result = this.db.prepare(`UPDATE lt_capability_bundles SET ${fields.join(", ")} WHERE tenant_id = ? AND id = ? AND updated_at = ?`).run(...values, input.expectedUpdatedAt);
5709
+ if (result.changes === 0) return { status: "conflict" };
5710
+ } else {
5711
+ this.db.prepare(`UPDATE lt_capability_bundles SET ${fields.join(", ")} WHERE tenant_id = ? AND id = ?`).run(...values);
5712
+ }
5713
+ } catch (error) {
5714
+ if (isConstraint(error)) throw new Error(duplicateMessage);
5715
+ throw error;
5716
+ }
5717
+ return this.getById(tenantId, id);
5718
+ }
5719
+ async deleteIfUnreferenced(tenantId, id) {
5720
+ const result = this.db.prepare(
5721
+ `DELETE FROM lt_capability_bundles
5722
+ WHERE tenant_id = ? AND id = ?
5723
+ AND NOT EXISTS (
5724
+ SELECT 1 FROM lt_projects
5725
+ WHERE tenant_id = ?
5726
+ AND json_type(config, '$.capabilityBundleIds') = 'array'
5727
+ AND EXISTS (SELECT 1 FROM json_each(config, '$.capabilityBundleIds') WHERE value = ?)
5728
+ )`
5729
+ ).run(tenantId, id, tenantId, id);
5730
+ if (result.changes > 0) return "deleted";
5731
+ if (await this.getById(tenantId, id)) return "in_use";
5732
+ return "not_found";
4511
5733
  }
4512
5734
  };
4513
5735
 
@@ -4520,6 +5742,10 @@ async function createLocalStoreConfig(options = {}) {
4520
5742
  assistant: new LocalAssistantStore(db),
4521
5743
  workspace: new LocalWorkspaceStore(db),
4522
5744
  project: new LocalProjectStore(db),
5745
+ projectRoom: new LocalProjectRoomStore(db),
5746
+ projectMembership: new LocalProjectMembershipStore(db),
5747
+ projectBotMembership: new LocalProjectBotMembershipStore(db),
5748
+ projectRoomMessage: new LocalProjectRoomMessageStore(db),
4523
5749
  user: new LocalUserStore(db),
4524
5750
  tenant: new LocalTenantStore(db),
4525
5751
  userTenantLink: new LocalUserTenantLinkStore(db),
@@ -4537,6 +5763,7 @@ async function createLocalStoreConfig(options = {}) {
4537
5763
  schedule: new LocalScheduleStorage(db),
4538
5764
  task: new LocalTaskStore(db),
4539
5765
  taskWorkItem: new LocalTaskWorkItemStore(db),
5766
+ capabilityBundle: new LocalCapabilityBundleStore(db),
4540
5767
  checkpoint: SqliteSaver.fromConnString(dbPath)
4541
5768
  };
4542
5769
  }
@@ -4552,7 +5779,7 @@ var InMemoryConversationStore = class {
4552
5779
  }
4553
5780
  async createConversation(input) {
4554
5781
  const now = isoNow();
4555
- const record = {
5782
+ const record2 = {
4556
5783
  conversationId: input.conversationId,
4557
5784
  tenantId: input.tenantId,
4558
5785
  taskIds: [],
@@ -4561,17 +5788,17 @@ var InMemoryConversationStore = class {
4561
5788
  createdAt: now,
4562
5789
  updatedAt: now
4563
5790
  };
4564
- this.store.set(input.conversationId, record);
4565
- return record;
5791
+ this.store.set(input.conversationId, record2);
5792
+ return record2;
4566
5793
  }
4567
5794
  async addTaskToConversation(conversationId, taskId) {
4568
- const record = this.store.get(conversationId);
4569
- if (!record) {
5795
+ const record2 = this.store.get(conversationId);
5796
+ if (!record2) {
4570
5797
  throw new Error(`Conversation not found: ${conversationId}`);
4571
5798
  }
4572
- record.taskIds.push(taskId);
4573
- record.updatedAt = isoNow();
4574
- return record;
5799
+ record2.taskIds.push(taskId);
5800
+ record2.updatedAt = isoNow();
5801
+ return record2;
4575
5802
  }
4576
5803
  async deleteConversation(conversationId) {
4577
5804
  this.store.delete(conversationId);
@@ -4579,9 +5806,12 @@ var InMemoryConversationStore = class {
4579
5806
  };
4580
5807
  export {
4581
5808
  DatabaseWrapper,
5809
+ DuplicateProjectMembershipError,
5810
+ DuplicateProjectRoomMessageIdempotencyKeyError,
4582
5811
  InMemoryConversationStore,
4583
5812
  LocalA2AApiKeyStore,
4584
5813
  LocalAssistantStore,
5814
+ LocalCapabilityBundleStore,
4585
5815
  LocalChannelBindingStore,
4586
5816
  LocalChannelInstallationStore,
4587
5817
  LocalConnectionStore,
@@ -4589,6 +5819,10 @@ export {
4589
5819
  LocalEvalStore,
4590
5820
  LocalMcpServerConfigStore,
4591
5821
  LocalMetricsServerConfigStore,
5822
+ LocalProjectBotMembershipStore,
5823
+ LocalProjectMembershipStore,
5824
+ LocalProjectRoomMessageStore,
5825
+ LocalProjectRoomStore,
4592
5826
  LocalProjectStore,
4593
5827
  LocalScheduleStorage,
4594
5828
  LocalSkillStore,
@@ -4602,6 +5836,9 @@ export {
4602
5836
  LocalWorkflowTrackingStore,
4603
5837
  LocalWorkspaceStore,
4604
5838
  MigrationManager,
5839
+ ProjectBotMembershipIdConflictError,
5840
+ ProjectMembershipIdConflictError,
5841
+ ProjectRoomMessageIdConflictError,
4605
5842
  RunResult,
4606
5843
  StatementWrapper,
4607
5844
  closeDatabase,