@axiom-lattice/pg-stores 2.0.10 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/.turbo/turbo-build.log +10 -10
  2. package/CHANGELOG.md +60 -0
  3. package/dist/index.d.mts +52 -4
  4. package/dist/index.d.ts +52 -4
  5. package/dist/index.js +907 -123
  6. package/dist/index.js.map +1 -1
  7. package/dist/index.mjs +905 -123
  8. package/dist/index.mjs.map +1 -1
  9. package/package.json +3 -3
  10. package/src/__tests__/PostgreSQLA2AApiKeyStore.migrations.test.ts +76 -0
  11. package/src/__tests__/PostgreSQLA2AApiKeyStore.test.ts +133 -0
  12. package/src/__tests__/PostgreSQLAgentWebAppStore.migrations.test.ts +77 -0
  13. package/src/__tests__/PostgreSQLAgentWebAppStore.test.ts +290 -0
  14. package/src/__tests__/PostgreSQLChannelInstallationStore.test.ts +37 -2
  15. package/src/__tests__/PostgreSQLTaskStore.integration.test.ts +90 -0
  16. package/src/__tests__/PostgreSQLTaskStore.test.ts +287 -0
  17. package/src/__tests__/PostgreSQLTaskWorkItemStore.test.ts +104 -0
  18. package/src/__tests__/add_a2a_key_assistant_ids.test.ts +34 -0
  19. package/src/__tests__/workspace-project-store.test.ts +48 -0
  20. package/src/createPgStoreConfig.ts +10 -1
  21. package/src/index.ts +10 -0
  22. package/src/migrations/add_a2a_key_assistant_ids.ts +24 -0
  23. package/src/migrations/add_project_kind_column.ts +29 -0
  24. package/src/migrations/agent_web_apps_migration.ts +37 -0
  25. package/src/migrations/task_work_items_migration.ts +15 -0
  26. package/src/stores/PostgreSQLA2AApiKeyStore.ts +23 -8
  27. package/src/stores/PostgreSQLAgentWebAppStore.ts +280 -0
  28. package/src/stores/PostgreSQLProjectStore.ts +31 -8
  29. package/src/stores/PostgreSQLTaskStore.ts +272 -7
  30. package/src/stores/PostgreSQLTaskWorkItemStore.ts +42 -2
package/dist/index.js CHANGED
@@ -35,8 +35,9 @@ __export(index_exports, {
35
35
  MenuStore: () => MenuStore,
36
36
  MigrationManager: () => MigrationManager,
37
37
  PGVectorStoreProvider: () => PGVectorStoreProvider,
38
- Pool: () => import_pg26.Pool,
38
+ Pool: () => import_pg27.Pool,
39
39
  PostgreSQLA2AApiKeyStore: () => PostgreSQLA2AApiKeyStore,
40
+ PostgreSQLAgentWebAppStore: () => PostgreSQLAgentWebAppStore,
40
41
  PostgreSQLAssistantStore: () => PostgreSQLAssistantStore,
41
42
  PostgreSQLChannelInstallationStore: () => PostgreSQLChannelInstallationStore,
42
43
  PostgreSQLCollectionStore: () => PostgreSQLCollectionStore,
@@ -74,6 +75,7 @@ __export(index_exports, {
74
75
  changeSkillPrimaryKey: () => changeSkillPrimaryKey,
75
76
  changeThreadPrimaryKey: () => changeThreadPrimaryKey,
76
77
  createA2AApiKeysTable: () => createA2AApiKeysTable,
78
+ createAgentWebAppsTable: () => createAgentWebAppsTable,
77
79
  createAssistantsTable: () => createAssistantsTable,
78
80
  createChannelBindingsTable: () => createChannelBindingsTable,
79
81
  createChannelIdentityMappingTables: () => createChannelIdentityMappingTables,
@@ -107,10 +109,10 @@ __export(index_exports, {
107
109
  safeParse: () => safeParse
108
110
  });
109
111
  module.exports = __toCommonJS(index_exports);
110
- var import_pg26 = require("pg");
112
+ var import_pg27 = require("pg");
111
113
 
112
114
  // src/createPgStoreConfig.ts
113
- var import_pg23 = require("pg");
115
+ var import_pg24 = require("pg");
114
116
 
115
117
  // src/migrations/migration.ts
116
118
  var MigrationManager = class {
@@ -2460,6 +2462,24 @@ var dropProjectFolderPathColumn = {
2460
2462
  }
2461
2463
  };
2462
2464
 
2465
+ // src/migrations/add_project_kind_column.ts
2466
+ var addProjectKindColumn = {
2467
+ version: 165,
2468
+ name: "add_project_kind_column",
2469
+ up: async (client) => {
2470
+ await client.query(`
2471
+ ALTER TABLE lattice_projects
2472
+ ADD COLUMN IF NOT EXISTS kind TEXT NOT NULL DEFAULT 'business'
2473
+ `);
2474
+ },
2475
+ down: async (client) => {
2476
+ await client.query(`
2477
+ ALTER TABLE lattice_projects
2478
+ DROP COLUMN IF EXISTS kind
2479
+ `);
2480
+ }
2481
+ };
2482
+
2463
2483
  // src/stores/PostgreSQLProjectStore.ts
2464
2484
  var PostgreSQLProjectStore = class {
2465
2485
  constructor(options) {
@@ -2482,6 +2502,7 @@ var PostgreSQLProjectStore = class {
2482
2502
  this.migrationManager.register(createProjectsTable);
2483
2503
  this.migrationManager.register(addProjectConfigColumn);
2484
2504
  this.migrationManager.register(dropProjectFolderPathColumn);
2505
+ this.migrationManager.register(addProjectKindColumn);
2485
2506
  if (options.autoMigrate !== false) {
2486
2507
  this.initialize().catch((error) => {
2487
2508
  console.error("Failed to initialize PostgreSQLProjectStore:", error);
@@ -2527,6 +2548,7 @@ var PostgreSQLProjectStore = class {
2527
2548
  name: row.name,
2528
2549
  description: row.description || void 0,
2529
2550
  config: row.config || void 0,
2551
+ kind: row.kind || "business",
2530
2552
  createdAt: row.created_at,
2531
2553
  updatedAt: row.updated_at
2532
2554
  };
@@ -2534,16 +2556,22 @@ var PostgreSQLProjectStore = class {
2534
2556
  /**
2535
2557
  * Get all projects for a specific workspace
2536
2558
  */
2537
- async getProjectsByWorkspace(tenantId, workspaceId) {
2559
+ async getProjectsByWorkspace(tenantId, workspaceId, filter) {
2538
2560
  await this.ensureInitialized();
2561
+ const conditions = ["tenant_id = $1", "workspace_id = $2"];
2562
+ const values = [tenantId, workspaceId];
2563
+ if (filter?.kind !== void 0) {
2564
+ values.push(filter.kind);
2565
+ conditions.push(`kind = $${values.length}`);
2566
+ }
2539
2567
  const result = await this.pool.query(
2540
2568
  `
2541
- SELECT id, tenant_id, workspace_id, name, description, config, created_at, updated_at
2569
+ SELECT id, tenant_id, workspace_id, name, description, config, kind, created_at, updated_at
2542
2570
  FROM lattice_projects
2543
- WHERE tenant_id = $1 AND workspace_id = $2
2571
+ WHERE ${conditions.join(" AND ")}
2544
2572
  ORDER BY created_at DESC
2545
2573
  `,
2546
- [tenantId, workspaceId]
2574
+ values
2547
2575
  );
2548
2576
  return result.rows.map(this.mapRowToProject);
2549
2577
  }
@@ -2554,7 +2582,7 @@ var PostgreSQLProjectStore = class {
2554
2582
  await this.ensureInitialized();
2555
2583
  const result = await this.pool.query(
2556
2584
  `
2557
- SELECT id, tenant_id, workspace_id, name, description, config, created_at, updated_at
2585
+ SELECT id, tenant_id, workspace_id, name, description, config, kind, created_at, updated_at
2558
2586
  FROM lattice_projects
2559
2587
  WHERE id = $1 AND tenant_id = $2
2560
2588
  `,
@@ -2571,18 +2599,20 @@ var PostgreSQLProjectStore = class {
2571
2599
  async createProject(tenantId, workspaceId, id, data) {
2572
2600
  await this.ensureInitialized();
2573
2601
  const now = /* @__PURE__ */ new Date();
2602
+ const kind = data.kind || "business";
2574
2603
  await this.pool.query(
2575
2604
  `
2576
- INSERT INTO lattice_projects (id, tenant_id, workspace_id, name, description, config, created_at, updated_at)
2577
- VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
2605
+ INSERT INTO lattice_projects (id, tenant_id, workspace_id, name, description, config, kind, created_at, updated_at)
2606
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
2578
2607
  ON CONFLICT (id, tenant_id) DO UPDATE SET
2579
2608
  workspace_id = EXCLUDED.workspace_id,
2580
2609
  name = EXCLUDED.name,
2581
2610
  description = EXCLUDED.description,
2582
2611
  config = EXCLUDED.config,
2612
+ kind = EXCLUDED.kind,
2583
2613
  updated_at = EXCLUDED.updated_at
2584
2614
  `,
2585
- [id, tenantId, workspaceId, data.name, data.description || null, data.config || null, now, now]
2615
+ [id, tenantId, workspaceId, data.name, data.description || null, data.config || null, kind, now, now]
2586
2616
  );
2587
2617
  return {
2588
2618
  id,
@@ -2591,6 +2621,7 @@ var PostgreSQLProjectStore = class {
2591
2621
  name: data.name,
2592
2622
  description: data.description,
2593
2623
  config: data.config,
2624
+ kind,
2594
2625
  createdAt: now,
2595
2626
  updatedAt: now
2596
2627
  };
@@ -2623,6 +2654,10 @@ var PostgreSQLProjectStore = class {
2623
2654
  updateFields.push(`config = $${paramIndex++}`);
2624
2655
  updateValues.push(updates.config || null);
2625
2656
  }
2657
+ if (updates.kind !== void 0) {
2658
+ updateFields.push(`kind = $${paramIndex++}`);
2659
+ updateValues.push(updates.kind);
2660
+ }
2626
2661
  if (updateFields.length === 0) {
2627
2662
  return existing;
2628
2663
  }
@@ -5853,6 +5888,24 @@ var createA2AApiKeysTable = {
5853
5888
  }
5854
5889
  };
5855
5890
 
5891
+ // src/migrations/add_a2a_key_assistant_ids.ts
5892
+ var addA2AKeyAssistantIds = {
5893
+ version: 167,
5894
+ name: "add_a2a_key_assistant_ids",
5895
+ up: async (client) => {
5896
+ await client.query(`
5897
+ ALTER TABLE lattice_a2a_api_keys
5898
+ ADD COLUMN IF NOT EXISTS assistant_ids JSONB
5899
+ `);
5900
+ },
5901
+ down: async (client) => {
5902
+ await client.query(`
5903
+ ALTER TABLE lattice_a2a_api_keys
5904
+ DROP COLUMN IF EXISTS assistant_ids
5905
+ `);
5906
+ }
5907
+ };
5908
+
5856
5909
  // src/stores/PostgreSQLA2AApiKeyStore.ts
5857
5910
  var import_core5 = require("@axiom-lattice/core");
5858
5911
  var import_crypto2 = require("crypto");
@@ -5864,8 +5917,8 @@ function mapRowToRecord(row) {
5864
5917
  id: row.id,
5865
5918
  key: (0, import_core5.decrypt)(row.key_value),
5866
5919
  tenantId: row.tenant_id,
5867
- projectId: row.project_id || void 0,
5868
- workspaceId: row.workspace_id || void 0,
5920
+ projectId: row.project_id,
5921
+ assistantIds: row.assistant_ids ?? void 0,
5869
5922
  label: row.label || void 0,
5870
5923
  enabled: row.enabled,
5871
5924
  createdAt: row.created_at,
@@ -5888,6 +5941,7 @@ var PostgreSQLA2AApiKeyStore = class {
5888
5941
  })();
5889
5942
  this.migrationManager = new MigrationManager(this.pool);
5890
5943
  this.migrationManager.register(createA2AApiKeysTable);
5944
+ this.migrationManager.register(addA2AKeyAssistantIds);
5891
5945
  if (options.autoMigrate !== false) {
5892
5946
  this.initialize().catch((error) => {
5893
5947
  console.error("Failed to initialize PostgreSQLA2AApiKeyStore:", error);
@@ -5926,6 +5980,14 @@ var PostgreSQLA2AApiKeyStore = class {
5926
5980
  }
5927
5981
  return null;
5928
5982
  }
5983
+ async findById(id) {
5984
+ await this.ensureInitialized();
5985
+ const result = await this.pool.query(
5986
+ `SELECT * FROM lattice_a2a_api_keys WHERE id = $1`,
5987
+ [id]
5988
+ );
5989
+ return result.rows[0] ? mapRowToRecord(result.rows[0]) : null;
5990
+ }
5929
5991
  async list(params) {
5930
5992
  await this.ensureInitialized();
5931
5993
  const limit = params.limit || 100;
@@ -5947,9 +6009,9 @@ var PostgreSQLA2AApiKeyStore = class {
5947
6009
  await this.ensureInitialized();
5948
6010
  const key = generateApiKey();
5949
6011
  const result = await this.pool.query(
5950
- `INSERT INTO lattice_a2a_api_keys (key_value, tenant_id, project_id, workspace_id, label)
6012
+ `INSERT INTO lattice_a2a_api_keys (key_value, tenant_id, project_id, assistant_ids, label)
5951
6013
  VALUES ($1, $2, $3, $4, $5) RETURNING *`,
5952
- [(0, import_core5.encrypt)(key), input.tenantId, input.projectId || null, input.workspaceId || null, input.label || null]
6014
+ [(0, import_core5.encrypt)(key), input.tenantId, input.projectId, input.assistantIds ? JSON.stringify(input.assistantIds) : null, input.label || null]
5953
6015
  );
5954
6016
  const record = mapRowToRecord(result.rows[0]);
5955
6017
  record.key = key;
@@ -6000,16 +6062,240 @@ var PostgreSQLA2AApiKeyStore = class {
6000
6062
  map.set(key, {
6001
6063
  key,
6002
6064
  tenantId: row.tenant_id,
6003
- projectId: row.project_id || void 0,
6004
- workspaceId: row.workspace_id || void 0
6065
+ projectId: row.project_id,
6066
+ assistantIds: row.assistant_ids ?? void 0
6005
6067
  });
6006
6068
  }
6007
6069
  return map;
6008
6070
  }
6009
6071
  };
6010
6072
 
6011
- // src/stores/PostgreSQLScheduleStorage.ts
6073
+ // src/stores/PostgreSQLAgentWebAppStore.ts
6074
+ var import_crypto3 = require("crypto");
6012
6075
  var import_pg17 = require("pg");
6076
+
6077
+ // src/migrations/agent_web_apps_migration.ts
6078
+ var createAgentWebAppsTable = {
6079
+ version: 169,
6080
+ name: "create_agent_web_apps_table",
6081
+ up: async (client) => {
6082
+ await client.query(`
6083
+ CREATE TABLE IF NOT EXISTS lattice_agent_web_apps (
6084
+ id VARCHAR(255) PRIMARY KEY,
6085
+ tenant_id VARCHAR(255) NOT NULL,
6086
+ assistant_id VARCHAR(255) NOT NULL,
6087
+ name VARCHAR(255) NOT NULL,
6088
+ description TEXT,
6089
+ status VARCHAR(32) NOT NULL DEFAULT 'draft'
6090
+ CONSTRAINT chk_lattice_agent_web_apps_status CHECK (status IN ('draft', 'active', 'disabled')),
6091
+ integration JSONB NOT NULL,
6092
+ scope JSONB NOT NULL,
6093
+ features JSONB NOT NULL,
6094
+ appearance JSONB NOT NULL DEFAULT '{}'::jsonb,
6095
+ created_at TIMESTAMPTZ NOT NULL DEFAULT date_trunc('milliseconds', NOW()),
6096
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT date_trunc('milliseconds', NOW())
6097
+ )
6098
+ `);
6099
+ await client.query(`
6100
+ CREATE INDEX IF NOT EXISTS idx_lattice_agent_web_apps_tenant_assistant
6101
+ ON lattice_agent_web_apps(tenant_id, assistant_id)
6102
+ `);
6103
+ },
6104
+ down: async (client) => {
6105
+ await client.query(
6106
+ "DROP INDEX IF EXISTS idx_lattice_agent_web_apps_tenant_assistant"
6107
+ );
6108
+ await client.query("DROP TABLE IF EXISTS lattice_agent_web_apps");
6109
+ }
6110
+ };
6111
+
6112
+ // src/stores/PostgreSQLAgentWebAppStore.ts
6113
+ function isRecord(value) {
6114
+ return typeof value === "object" && value !== null && !Array.isArray(value);
6115
+ }
6116
+ function isStringArray(value) {
6117
+ return Array.isArray(value) && value.every((item) => typeof item === "string");
6118
+ }
6119
+ function isIntegration(value) {
6120
+ return isRecord(value) && value.type === "react_sdk";
6121
+ }
6122
+ function isScope(value) {
6123
+ return isRecord(value) && typeof value.defaultProjectId === "string" && isStringArray(value.allowedProjectIds) && (value.defaultModelKey === void 0 || typeof value.defaultModelKey === "string") && (value.allowedModelKeys === void 0 || isStringArray(value.allowedModelKeys));
6124
+ }
6125
+ function isFeatures(value) {
6126
+ return isRecord(value) && typeof value.projectSelector === "boolean" && typeof value.modelSelector === "boolean" && typeof value.threadManagement === "boolean" && typeof value.attachments === "boolean" && typeof value.hitl === "boolean" && typeof value.genUI === "boolean";
6127
+ }
6128
+ function isAppearance(value) {
6129
+ return isRecord(value) && (value.title === void 0 || typeof value.title === "string") && (value.welcomeMessage === void 0 || typeof value.welcomeMessage === "string") && (value.primaryColor === void 0 || typeof value.primaryColor === "string");
6130
+ }
6131
+ function isStatus(value) {
6132
+ return value === "draft" || value === "active" || value === "disabled";
6133
+ }
6134
+ function mapRow(row) {
6135
+ if (!isStatus(row.status) || !isIntegration(row.integration) || !isScope(row.scope) || !isFeatures(row.features) || !isAppearance(row.appearance)) {
6136
+ throw new Error(`Invalid agent web app row: ${row.id}`);
6137
+ }
6138
+ return {
6139
+ id: row.id,
6140
+ tenantId: row.tenant_id,
6141
+ assistantId: row.assistant_id,
6142
+ name: row.name,
6143
+ description: row.description ?? void 0,
6144
+ status: row.status,
6145
+ integration: row.integration,
6146
+ scope: row.scope,
6147
+ features: row.features,
6148
+ appearance: row.appearance,
6149
+ createdAt: row.created_at,
6150
+ updatedAt: row.updated_at
6151
+ };
6152
+ }
6153
+ var PostgreSQLAgentWebAppStore = class {
6154
+ constructor(options) {
6155
+ this.initialized = false;
6156
+ this.ownsPool = true;
6157
+ this.initPromise = null;
6158
+ if (options.pool) {
6159
+ this.pool = options.pool;
6160
+ this.ownsPool = false;
6161
+ this.initialized = true;
6162
+ return;
6163
+ }
6164
+ this.pool = typeof options.poolConfig === "string" ? new import_pg17.Pool({ connectionString: options.poolConfig }) : options.poolConfig ? new import_pg17.Pool(options.poolConfig) : (() => {
6165
+ throw new Error("Either pool or poolConfig must be provided");
6166
+ })();
6167
+ this.migrationManager = new MigrationManager(this.pool);
6168
+ this.migrationManager.register(createAgentWebAppsTable);
6169
+ if (options.autoMigrate !== false) {
6170
+ this.startInitialization();
6171
+ }
6172
+ }
6173
+ async initialize() {
6174
+ if (this.initialized) return;
6175
+ if (this.initPromise) return this.initPromise;
6176
+ return this.startInitialization();
6177
+ }
6178
+ startInitialization() {
6179
+ this.initPromise = this.migrationManager.migrate().then(() => {
6180
+ this.initialized = true;
6181
+ });
6182
+ void this.initPromise.catch(() => void 0);
6183
+ return this.initPromise;
6184
+ }
6185
+ async dispose() {
6186
+ if (this.ownsPool) await this.pool.end();
6187
+ }
6188
+ async ensureInitialized() {
6189
+ if (!this.initialized) await this.initialize();
6190
+ }
6191
+ async list(tenantId, assistantId) {
6192
+ await this.ensureInitialized();
6193
+ const result = assistantId === void 0 ? await this.pool.query(
6194
+ `SELECT * FROM lattice_agent_web_apps
6195
+ WHERE tenant_id = $1
6196
+ ORDER BY created_at DESC, id DESC`,
6197
+ [tenantId]
6198
+ ) : await this.pool.query(
6199
+ `SELECT * FROM lattice_agent_web_apps
6200
+ WHERE tenant_id = $1 AND assistant_id = $2
6201
+ ORDER BY created_at DESC, id DESC`,
6202
+ [tenantId, assistantId]
6203
+ );
6204
+ return result.rows.map(mapRow);
6205
+ }
6206
+ async getById(tenantId, webAppId) {
6207
+ await this.ensureInitialized();
6208
+ const result = await this.pool.query(
6209
+ `SELECT * FROM lattice_agent_web_apps
6210
+ WHERE tenant_id = $1 AND id = $2`,
6211
+ [tenantId, webAppId]
6212
+ );
6213
+ return result.rows[0] ? mapRow(result.rows[0]) : null;
6214
+ }
6215
+ async findById(webAppId) {
6216
+ await this.ensureInitialized();
6217
+ const result = await this.pool.query(
6218
+ `SELECT * FROM lattice_agent_web_apps
6219
+ WHERE id = $1`,
6220
+ [webAppId]
6221
+ );
6222
+ return result.rows[0] ? mapRow(result.rows[0]) : null;
6223
+ }
6224
+ async create(tenantId, input) {
6225
+ await this.ensureInitialized();
6226
+ const id = `webapp_${(0, import_crypto3.randomUUID)().replace(/-/g, "")}`;
6227
+ const result = await this.pool.query(
6228
+ `INSERT INTO lattice_agent_web_apps
6229
+ (id, tenant_id, assistant_id, name, description, status, integration, scope, features, appearance)
6230
+ VALUES ($1, $2, $3, $4, $5, $6, $7::jsonb, $8::jsonb, $9::jsonb, $10::jsonb)
6231
+ RETURNING *`,
6232
+ [
6233
+ id,
6234
+ tenantId,
6235
+ input.assistantId,
6236
+ input.name,
6237
+ input.description ?? null,
6238
+ "draft",
6239
+ JSON.stringify(input.integration),
6240
+ JSON.stringify(input.scope),
6241
+ JSON.stringify(input.features),
6242
+ JSON.stringify(input.appearance)
6243
+ ]
6244
+ );
6245
+ return mapRow(result.rows[0]);
6246
+ }
6247
+ async update(tenantId, webAppId, patch, options) {
6248
+ await this.ensureInitialized();
6249
+ const assignments = [];
6250
+ const values = [];
6251
+ const add = (column, value) => {
6252
+ values.push(value);
6253
+ assignments.push(`${column} = $${values.length}`);
6254
+ };
6255
+ if (patch.name !== void 0) add("name", patch.name);
6256
+ if (Object.prototype.hasOwnProperty.call(patch, "description")) {
6257
+ add("description", patch.description ?? null);
6258
+ }
6259
+ if (patch.scope !== void 0) {
6260
+ add("scope", JSON.stringify(patch.scope));
6261
+ }
6262
+ if (patch.features !== void 0) {
6263
+ add("features", JSON.stringify(patch.features));
6264
+ }
6265
+ if (patch.appearance !== void 0) {
6266
+ add("appearance", JSON.stringify(patch.appearance));
6267
+ }
6268
+ if (patch.status !== void 0) add("status", patch.status);
6269
+ if (assignments.length === 0) return this.getById(tenantId, webAppId);
6270
+ values.push(tenantId, webAppId);
6271
+ const tenantParam = values.length - 1;
6272
+ const idParam = values.length;
6273
+ const expectedUpdatedAtClause = options?.expectedUpdatedAt ? ` AND date_trunc('milliseconds', updated_at) = $${values.push(options.expectedUpdatedAt)}` : "";
6274
+ const result = await this.pool.query(
6275
+ `UPDATE lattice_agent_web_apps
6276
+ SET ${assignments.join(", ")}, updated_at = GREATEST(
6277
+ date_trunc('milliseconds', clock_timestamp()),
6278
+ updated_at + interval '1 millisecond'
6279
+ )
6280
+ WHERE tenant_id = $${tenantParam} AND id = $${idParam}${expectedUpdatedAtClause}
6281
+ RETURNING *`,
6282
+ values
6283
+ );
6284
+ return result.rows[0] ? mapRow(result.rows[0]) : null;
6285
+ }
6286
+ async delete(tenantId, webAppId) {
6287
+ await this.ensureInitialized();
6288
+ const result = await this.pool.query(
6289
+ `DELETE FROM lattice_agent_web_apps
6290
+ WHERE tenant_id = $1 AND id = $2`,
6291
+ [tenantId, webAppId]
6292
+ );
6293
+ return result.rowCount === 1;
6294
+ }
6295
+ };
6296
+
6297
+ // src/stores/PostgreSQLScheduleStorage.ts
6298
+ var import_pg18 = require("pg");
6013
6299
  var import_protocols = require("@axiom-lattice/protocols");
6014
6300
 
6015
6301
  // src/migrations/schedule_migrations.ts
@@ -6184,9 +6470,9 @@ var PostgreSQLScheduleStorage = class {
6184
6470
  return;
6185
6471
  }
6186
6472
  if (typeof options.poolConfig === "string") {
6187
- this.pool = new import_pg17.Pool({ connectionString: options.poolConfig });
6473
+ this.pool = new import_pg18.Pool({ connectionString: options.poolConfig });
6188
6474
  } else if (options.poolConfig) {
6189
- this.pool = new import_pg17.Pool(options.poolConfig);
6475
+ this.pool = new import_pg18.Pool(options.poolConfig);
6190
6476
  } else {
6191
6477
  throw new Error("Either pool or poolConfig must be provided");
6192
6478
  }
@@ -6624,7 +6910,7 @@ var PostgreSQLScheduleStorage = class {
6624
6910
  };
6625
6911
 
6626
6912
  // src/stores/PostgreSQLTaskStore.ts
6627
- var import_pg18 = require("pg");
6913
+ var import_pg19 = require("pg");
6628
6914
 
6629
6915
  // src/migrations/task_migration.ts
6630
6916
  var createTasksTable = {
@@ -6723,6 +7009,49 @@ var taskMigrations = [
6723
7009
 
6724
7010
  // src/stores/PostgreSQLTaskStore.ts
6725
7011
  var import_uuid3 = require("uuid");
7012
+ function nextUpdatedAtSql(column = "updated_at") {
7013
+ return `to_char(
7014
+ date_trunc('milliseconds', GREATEST(
7015
+ clock_timestamp(),
7016
+ CASE
7017
+ WHEN ${canonicalTimestampValidationSql(column)}
7018
+ THEN ${column}::timestamptz + interval '1 millisecond'
7019
+ ELSE clock_timestamp()
7020
+ END
7021
+ ))
7022
+ AT TIME ZONE 'UTC',
7023
+ 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"'
7024
+ )`;
7025
+ }
7026
+ function canonicalTimestampValidationSql(column, allowMaximum = false) {
7027
+ const year = `substring(${column} FROM 1 FOR 4)::integer`;
7028
+ const month = `substring(${column} FROM 6 FOR 2)::integer`;
7029
+ const day = `substring(${column} FROM 9 FOR 2)::integer`;
7030
+ return `CASE
7031
+ WHEN ${column} ~ '^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}[.][0-9]{3}Z$' THEN
7032
+ ${allowMaximum ? "" : `${column} <> '9999-12-31T23:59:59.999Z' AND`}
7033
+ ${year} BETWEEN 1 AND 9999
7034
+ AND ${month} BETWEEN 1 AND 12
7035
+ AND ${day} BETWEEN 1 AND CASE
7036
+ WHEN ${month} = 2 THEN CASE
7037
+ WHEN (${year} % 400 = 0) OR (${year} % 4 = 0 AND ${year} % 100 <> 0) THEN 29
7038
+ ELSE 28
7039
+ END
7040
+ WHEN ${month} IN (4, 6, 9, 11) THEN 30
7041
+ ELSE 31
7042
+ END
7043
+ AND substring(${column} FROM 12 FOR 2)::integer BETWEEN 0 AND 23
7044
+ AND substring(${column} FROM 15 FOR 2)::integer BETWEEN 0 AND 59
7045
+ AND substring(${column} FROM 18 FOR 2)::integer BETWEEN 0 AND 59
7046
+ ELSE FALSE
7047
+ END`;
7048
+ }
7049
+ function canonicalTimestampSnapshotSql(column, parameter, allowMaximum = false) {
7050
+ return `CASE WHEN ${canonicalTimestampValidationSql(column, allowMaximum)}
7051
+ THEN ${column}::timestamptz = ${parameter}::timestamptz
7052
+ ELSE FALSE
7053
+ END`;
7054
+ }
6726
7055
  function parseTaskFiles(raw) {
6727
7056
  if (!raw) return void 0;
6728
7057
  try {
@@ -6771,9 +7100,9 @@ var PostgreSQLTaskStore = class {
6771
7100
  return;
6772
7101
  }
6773
7102
  if (typeof options.poolConfig === "string") {
6774
- this.pool = new import_pg18.Pool({ connectionString: options.poolConfig });
7103
+ this.pool = new import_pg19.Pool({ connectionString: options.poolConfig });
6775
7104
  } else if (options.poolConfig) {
6776
- this.pool = new import_pg18.Pool(options.poolConfig);
7105
+ this.pool = new import_pg19.Pool(options.poolConfig);
6777
7106
  } else {
6778
7107
  throw new Error("Either pool or poolConfig must be provided");
6779
7108
  }
@@ -6808,7 +7137,7 @@ var PostgreSQLTaskStore = class {
6808
7137
  }
6809
7138
  async create(params) {
6810
7139
  await this.ensureInitialized();
6811
- const id = (0, import_uuid3.v4)();
7140
+ const id = params.id ?? (0, import_uuid3.v4)();
6812
7141
  const now = (/* @__PURE__ */ new Date()).toISOString();
6813
7142
  await this.pool.query(
6814
7143
  `INSERT INTO lattice_tasks (id, tenant_id, owner_type, owner_id, title, description, status, priority, due_date, metadata, parent_id, source_id, context, require_review, dependencies, result, failure_reason, workspace_id, project_id, files, created_at, updated_at)
@@ -6952,7 +7281,7 @@ var PostgreSQLTaskStore = class {
6952
7281
  }
6953
7282
  if (updates.context !== void 0) {
6954
7283
  setClauses.push(`context = $${paramIndex++}`);
6955
- params.push(JSON.stringify(updates.context));
7284
+ params.push(updates.context === null ? null : JSON.stringify(updates.context));
6956
7285
  }
6957
7286
  if (updates.ownerType !== void 0) {
6958
7287
  setClauses.push(`owner_type = $${paramIndex++}`);
@@ -6989,103 +7318,537 @@ var PostgreSQLTaskStore = class {
6989
7318
  if (setClauses.length === 0) {
6990
7319
  return existing;
6991
7320
  }
6992
- setClauses.push(`updated_at = $${paramIndex++}`);
6993
- params.push((/* @__PURE__ */ new Date()).toISOString());
7321
+ setClauses.push(`updated_at = ${nextUpdatedAtSql()}`);
6994
7322
  params.push(tenantId, id);
6995
- await this.pool.query(
6996
- `UPDATE lattice_tasks SET ${setClauses.join(", ")} WHERE tenant_id = $${paramIndex++} AND id = $${paramIndex++}`,
7323
+ const result = await this.pool.query(
7324
+ `UPDATE lattice_tasks SET ${setClauses.join(", ")} WHERE tenant_id = $${paramIndex++} AND id = $${paramIndex++} AND updated_at <> '9999-12-31T23:59:59.999Z' RETURNING *`,
6997
7325
  params
6998
7326
  );
6999
- return this.getById(tenantId, id);
7327
+ return result.rows[0] ? mapRowToTask(result.rows[0]) : null;
7000
7328
  }
7001
- async delete(tenantId, id) {
7329
+ /**
7330
+ * Atomically update a task unless its current status is blocked.
7331
+ *
7332
+ * @param tenantId Tenant identifier.
7333
+ * @param id Task identifier.
7334
+ * @param updates Partial task data to update.
7335
+ * @param blockedStatuses Current statuses that prevent the update.
7336
+ * @returns The updated task, or `null` when missing or blocked.
7337
+ */
7338
+ async updateIfStatusNotIn(tenantId, id, updates, blockedStatuses) {
7002
7339
  await this.ensureInitialized();
7003
- const result = await this.pool.query(
7004
- `DELETE FROM lattice_tasks WHERE tenant_id = $1 AND id = $2`,
7005
- [tenantId, id]
7006
- );
7007
- return (result.rowCount ?? 0) > 0;
7008
- }
7009
- async dispose() {
7010
- if (this.ownsPool && this.pool) {
7011
- await this.pool.end();
7340
+ const setClauses = [];
7341
+ const params = [];
7342
+ let paramIndex = 1;
7343
+ if (updates.title !== void 0) {
7344
+ setClauses.push(`title = $${paramIndex++}`);
7345
+ params.push(updates.title);
7012
7346
  }
7013
- }
7014
- async ensureInitialized() {
7015
- if (!this.initialized) {
7016
- await this.initialize();
7347
+ if (updates.description !== void 0) {
7348
+ setClauses.push(`description = $${paramIndex++}`);
7349
+ params.push(updates.description);
7017
7350
  }
7018
- }
7019
- };
7020
-
7021
- // src/stores/PostgreSQLTaskWorkItemStore.ts
7022
- var import_uuid4 = require("uuid");
7023
- var PostgreSQLTaskWorkItemStore = class {
7024
- constructor(pool) {
7025
- this.pool = pool;
7026
- }
7027
- async create(params) {
7028
- const id = (0, import_uuid4.v4)();
7029
- const result = await this.pool.query(
7030
- `INSERT INTO lattice_task_work_items
7031
- (id, tenant_id, task_id, action, actor, thread_id, summary, detail, attempt, workspace_id, project_id)
7032
- VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
7033
- RETURNING *`,
7034
- [
7035
- id,
7036
- params.tenantId,
7037
- params.taskId,
7038
- params.action,
7039
- params.actor,
7040
- params.threadId || null,
7041
- params.summary || null,
7042
- params.detail ? JSON.stringify(params.detail) : null,
7043
- params.attempt || null,
7044
- params.workspaceId || null,
7045
- params.projectId || null
7046
- ]
7047
- );
7048
- return this.rowToItem(result.rows[0]);
7049
- }
7050
- async list(filter) {
7051
- let query = `SELECT * FROM lattice_task_work_items WHERE tenant_id = $1 AND task_id = $2`;
7052
- const params = [filter.tenantId, filter.taskId];
7053
- if (filter.action) {
7054
- query += ` AND action = $${params.length + 1}`;
7055
- params.push(filter.action);
7351
+ if (updates.status !== void 0) {
7352
+ setClauses.push(`status = $${paramIndex++}`);
7353
+ params.push(updates.status);
7056
7354
  }
7057
- query += ` ORDER BY created_at ASC`;
7058
- if (filter.limit) {
7059
- query += ` LIMIT $${params.length + 1}`;
7060
- params.push(filter.limit);
7355
+ if (updates.priority !== void 0) {
7356
+ setClauses.push(`priority = $${paramIndex++}`);
7357
+ params.push(updates.priority);
7061
7358
  }
7062
- if (filter.offset) {
7063
- query += ` OFFSET $${params.length + 1}`;
7064
- params.push(filter.offset);
7359
+ if (updates.dueDate !== void 0) {
7360
+ setClauses.push(`due_date = $${paramIndex++}`);
7361
+ params.push(updates.dueDate);
7065
7362
  }
7066
- const result = await this.pool.query(query, params);
7067
- return result.rows.map((row) => this.rowToItem(row));
7068
- }
7069
- rowToItem(row) {
7070
- return {
7071
- id: row.id,
7072
- taskId: row.task_id,
7073
- tenantId: row.tenant_id,
7074
- action: row.action,
7075
- actor: row.actor,
7076
- threadId: row.thread_id,
7077
- summary: row.summary,
7078
- detail: row.detail,
7079
- attempt: row.attempt,
7080
- workspaceId: row.workspace_id,
7081
- projectId: row.project_id,
7082
- createdAt: new Date(row.created_at)
7083
- };
7084
- }
7363
+ if (updates.metadata !== void 0) {
7364
+ setClauses.push(`metadata = $${paramIndex++}`);
7365
+ params.push(JSON.stringify(updates.metadata));
7366
+ }
7367
+ if (updates.files !== void 0) {
7368
+ setClauses.push(`files = $${paramIndex++}`);
7369
+ params.push(JSON.stringify(updates.files));
7370
+ }
7371
+ if (updates.parentId !== void 0) {
7372
+ setClauses.push(`parent_id = $${paramIndex++}`);
7373
+ params.push(updates.parentId);
7374
+ }
7375
+ if (updates.sourceId !== void 0) {
7376
+ setClauses.push(`source_id = $${paramIndex++}`);
7377
+ params.push(updates.sourceId);
7378
+ }
7379
+ if (updates.context !== void 0) {
7380
+ setClauses.push(`context = $${paramIndex++}`);
7381
+ params.push(updates.context === null ? null : JSON.stringify(updates.context));
7382
+ }
7383
+ if (updates.ownerType !== void 0) {
7384
+ setClauses.push(`owner_type = $${paramIndex++}`);
7385
+ params.push(updates.ownerType);
7386
+ }
7387
+ if (updates.ownerId !== void 0) {
7388
+ setClauses.push(`owner_id = $${paramIndex++}`);
7389
+ params.push(updates.ownerId);
7390
+ }
7391
+ if (updates.requireReview !== void 0) {
7392
+ setClauses.push(`require_review = $${paramIndex++}`);
7393
+ params.push(updates.requireReview);
7394
+ }
7395
+ if (updates.dependencies !== void 0) {
7396
+ setClauses.push(`dependencies = $${paramIndex++}`);
7397
+ params.push(JSON.stringify(updates.dependencies));
7398
+ }
7399
+ if (updates.result !== void 0) {
7400
+ setClauses.push(`result = $${paramIndex++}`);
7401
+ params.push(updates.result);
7402
+ }
7403
+ if (updates.failureReason !== void 0) {
7404
+ setClauses.push(`failure_reason = $${paramIndex++}`);
7405
+ params.push(updates.failureReason);
7406
+ }
7407
+ if (updates.workspaceId !== void 0) {
7408
+ setClauses.push(`workspace_id = $${paramIndex++}`);
7409
+ params.push(updates.workspaceId);
7410
+ }
7411
+ if (updates.projectId !== void 0) {
7412
+ setClauses.push(`project_id = $${paramIndex++}`);
7413
+ params.push(updates.projectId);
7414
+ }
7415
+ setClauses.push(`updated_at = ${nextUpdatedAtSql()}`);
7416
+ const tenantParam = paramIndex++;
7417
+ const idParam = paramIndex++;
7418
+ params.push(tenantId, id);
7419
+ const blockedClause = blockedStatuses.length > 0 ? ` AND status <> ALL($${paramIndex}::text[])` : "";
7420
+ if (blockedStatuses.length > 0) params.push(blockedStatuses);
7421
+ const result = await this.pool.query(
7422
+ `UPDATE lattice_tasks SET ${setClauses.join(", ")} WHERE tenant_id = $${tenantParam} AND id = $${idParam} AND updated_at <> '9999-12-31T23:59:59.999Z'${blockedClause} RETURNING *`,
7423
+ params
7424
+ );
7425
+ return result.rows[0] ? mapRowToTask(result.rows[0]) : null;
7426
+ }
7427
+ /** Atomically update a task only when its current status is expected. */
7428
+ async updateIfStatusIn(tenantId, id, updates, expectedStatuses) {
7429
+ await this.ensureInitialized();
7430
+ if (expectedStatuses.length === 0) return null;
7431
+ const setClauses = [];
7432
+ const params = [];
7433
+ let paramIndex = 1;
7434
+ if (updates.title !== void 0) {
7435
+ setClauses.push(`title = $${paramIndex++}`);
7436
+ params.push(updates.title);
7437
+ }
7438
+ if (updates.description !== void 0) {
7439
+ setClauses.push(`description = $${paramIndex++}`);
7440
+ params.push(updates.description);
7441
+ }
7442
+ if (updates.status !== void 0) {
7443
+ setClauses.push(`status = $${paramIndex++}`);
7444
+ params.push(updates.status);
7445
+ }
7446
+ if (updates.priority !== void 0) {
7447
+ setClauses.push(`priority = $${paramIndex++}`);
7448
+ params.push(updates.priority);
7449
+ }
7450
+ if (updates.dueDate !== void 0) {
7451
+ setClauses.push(`due_date = $${paramIndex++}`);
7452
+ params.push(updates.dueDate);
7453
+ }
7454
+ if (updates.metadata !== void 0) {
7455
+ setClauses.push(`metadata = $${paramIndex++}`);
7456
+ params.push(JSON.stringify(updates.metadata));
7457
+ }
7458
+ if (updates.files !== void 0) {
7459
+ setClauses.push(`files = $${paramIndex++}`);
7460
+ params.push(JSON.stringify(updates.files));
7461
+ }
7462
+ if (updates.parentId !== void 0) {
7463
+ setClauses.push(`parent_id = $${paramIndex++}`);
7464
+ params.push(updates.parentId);
7465
+ }
7466
+ if (updates.sourceId !== void 0) {
7467
+ setClauses.push(`source_id = $${paramIndex++}`);
7468
+ params.push(updates.sourceId);
7469
+ }
7470
+ if (updates.context !== void 0) {
7471
+ setClauses.push(`context = $${paramIndex++}`);
7472
+ params.push(updates.context === null ? null : JSON.stringify(updates.context));
7473
+ }
7474
+ if (updates.ownerType !== void 0) {
7475
+ setClauses.push(`owner_type = $${paramIndex++}`);
7476
+ params.push(updates.ownerType);
7477
+ }
7478
+ if (updates.ownerId !== void 0) {
7479
+ setClauses.push(`owner_id = $${paramIndex++}`);
7480
+ params.push(updates.ownerId);
7481
+ }
7482
+ if (updates.requireReview !== void 0) {
7483
+ setClauses.push(`require_review = $${paramIndex++}`);
7484
+ params.push(updates.requireReview);
7485
+ }
7486
+ if (updates.dependencies !== void 0) {
7487
+ setClauses.push(`dependencies = $${paramIndex++}`);
7488
+ params.push(JSON.stringify(updates.dependencies));
7489
+ }
7490
+ if (updates.result !== void 0) {
7491
+ setClauses.push(`result = $${paramIndex++}`);
7492
+ params.push(updates.result);
7493
+ }
7494
+ if (updates.failureReason !== void 0) {
7495
+ setClauses.push(`failure_reason = $${paramIndex++}`);
7496
+ params.push(updates.failureReason);
7497
+ }
7498
+ if (updates.workspaceId !== void 0) {
7499
+ setClauses.push(`workspace_id = $${paramIndex++}`);
7500
+ params.push(updates.workspaceId);
7501
+ }
7502
+ if (updates.projectId !== void 0) {
7503
+ setClauses.push(`project_id = $${paramIndex++}`);
7504
+ params.push(updates.projectId);
7505
+ }
7506
+ setClauses.push(`updated_at = ${nextUpdatedAtSql()}`);
7507
+ const tenantParam = paramIndex++;
7508
+ const idParam = paramIndex++;
7509
+ const statusesParam = paramIndex++;
7510
+ params.push(tenantId, id, expectedStatuses);
7511
+ const result = await this.pool.query(
7512
+ `UPDATE lattice_tasks SET ${setClauses.join(", ")} WHERE tenant_id = $${tenantParam} AND id = $${idParam} AND updated_at <> '9999-12-31T23:59:59.999Z' AND status = ANY($${statusesParam}::text[]) RETURNING *`,
7513
+ params
7514
+ );
7515
+ return result.rows[0] ? mapRowToTask(result.rows[0]) : null;
7516
+ }
7517
+ /** Atomically update a task only when status and updatedAt match a read snapshot. */
7518
+ async updateIfStatusAndUpdatedAt(tenantId, id, updates, expectedStatuses, expectedUpdatedAt) {
7519
+ await this.ensureInitialized();
7520
+ if (expectedStatuses.length === 0) return null;
7521
+ const expectedIso = new Date(expectedUpdatedAt).toISOString();
7522
+ const setClauses = [];
7523
+ const params = [];
7524
+ let paramIndex = 1;
7525
+ if (updates.title !== void 0) {
7526
+ setClauses.push(`title = $${paramIndex++}`);
7527
+ params.push(updates.title);
7528
+ }
7529
+ if (updates.description !== void 0) {
7530
+ setClauses.push(`description = $${paramIndex++}`);
7531
+ params.push(updates.description);
7532
+ }
7533
+ if (updates.status !== void 0) {
7534
+ setClauses.push(`status = $${paramIndex++}`);
7535
+ params.push(updates.status);
7536
+ }
7537
+ if (updates.priority !== void 0) {
7538
+ setClauses.push(`priority = $${paramIndex++}`);
7539
+ params.push(updates.priority);
7540
+ }
7541
+ if (updates.dueDate !== void 0) {
7542
+ setClauses.push(`due_date = $${paramIndex++}`);
7543
+ params.push(updates.dueDate);
7544
+ }
7545
+ if (updates.metadata !== void 0) {
7546
+ setClauses.push(`metadata = $${paramIndex++}`);
7547
+ params.push(JSON.stringify(updates.metadata));
7548
+ }
7549
+ if (updates.files !== void 0) {
7550
+ setClauses.push(`files = $${paramIndex++}`);
7551
+ params.push(JSON.stringify(updates.files));
7552
+ }
7553
+ if (updates.parentId !== void 0) {
7554
+ setClauses.push(`parent_id = $${paramIndex++}`);
7555
+ params.push(updates.parentId);
7556
+ }
7557
+ if (updates.sourceId !== void 0) {
7558
+ setClauses.push(`source_id = $${paramIndex++}`);
7559
+ params.push(updates.sourceId);
7560
+ }
7561
+ if (updates.context !== void 0) {
7562
+ setClauses.push(`context = $${paramIndex++}`);
7563
+ params.push(updates.context === null ? null : JSON.stringify(updates.context));
7564
+ }
7565
+ if (updates.ownerType !== void 0) {
7566
+ setClauses.push(`owner_type = $${paramIndex++}`);
7567
+ params.push(updates.ownerType);
7568
+ }
7569
+ if (updates.ownerId !== void 0) {
7570
+ setClauses.push(`owner_id = $${paramIndex++}`);
7571
+ params.push(updates.ownerId);
7572
+ }
7573
+ if (updates.requireReview !== void 0) {
7574
+ setClauses.push(`require_review = $${paramIndex++}`);
7575
+ params.push(updates.requireReview);
7576
+ }
7577
+ if (updates.dependencies !== void 0) {
7578
+ setClauses.push(`dependencies = $${paramIndex++}`);
7579
+ params.push(JSON.stringify(updates.dependencies));
7580
+ }
7581
+ if (updates.result !== void 0) {
7582
+ setClauses.push(`result = $${paramIndex++}`);
7583
+ params.push(updates.result);
7584
+ }
7585
+ if (updates.failureReason !== void 0) {
7586
+ setClauses.push(`failure_reason = $${paramIndex++}`);
7587
+ params.push(updates.failureReason);
7588
+ }
7589
+ if (updates.workspaceId !== void 0) {
7590
+ setClauses.push(`workspace_id = $${paramIndex++}`);
7591
+ params.push(updates.workspaceId);
7592
+ }
7593
+ if (updates.projectId !== void 0) {
7594
+ setClauses.push(`project_id = $${paramIndex++}`);
7595
+ params.push(updates.projectId);
7596
+ }
7597
+ setClauses.push(`updated_at = ${nextUpdatedAtSql()}`);
7598
+ const tenantParam = paramIndex++;
7599
+ const idParam = paramIndex++;
7600
+ const statusesParam = paramIndex++;
7601
+ const updatedAtParam = paramIndex++;
7602
+ params.push(tenantId, id, expectedStatuses, expectedIso);
7603
+ const result = await this.pool.query(
7604
+ `UPDATE lattice_tasks SET ${setClauses.join(", ")} WHERE tenant_id = $${tenantParam} AND id = $${idParam} AND status = ANY($${statusesParam}::text[]) AND ${canonicalTimestampSnapshotSql("updated_at", `$${updatedAtParam}`)} RETURNING *`,
7605
+ params
7606
+ );
7607
+ return result.rows[0] ? mapRowToTask(result.rows[0]) : null;
7608
+ }
7609
+ /** Atomically update a child only when both child and parent snapshots match. */
7610
+ async updateIfStatusUpdatedAtAndParentUpdatedAt(tenantId, id, updates, expectedStatuses, expectedUpdatedAt, parentId, expectedParentUpdatedAt) {
7611
+ await this.ensureInitialized();
7612
+ if (expectedStatuses.length === 0) return null;
7613
+ const setClauses = [];
7614
+ const params = [];
7615
+ let paramIndex = 1;
7616
+ if (updates.title !== void 0) {
7617
+ setClauses.push(`title = $${paramIndex++}`);
7618
+ params.push(updates.title);
7619
+ }
7620
+ if (updates.description !== void 0) {
7621
+ setClauses.push(`description = $${paramIndex++}`);
7622
+ params.push(updates.description);
7623
+ }
7624
+ if (updates.status !== void 0) {
7625
+ setClauses.push(`status = $${paramIndex++}`);
7626
+ params.push(updates.status);
7627
+ }
7628
+ if (updates.priority !== void 0) {
7629
+ setClauses.push(`priority = $${paramIndex++}`);
7630
+ params.push(updates.priority);
7631
+ }
7632
+ if (updates.dueDate !== void 0) {
7633
+ setClauses.push(`due_date = $${paramIndex++}`);
7634
+ params.push(updates.dueDate);
7635
+ }
7636
+ if (updates.metadata !== void 0) {
7637
+ setClauses.push(`metadata = $${paramIndex++}`);
7638
+ params.push(JSON.stringify(updates.metadata));
7639
+ }
7640
+ if (updates.files !== void 0) {
7641
+ setClauses.push(`files = $${paramIndex++}`);
7642
+ params.push(JSON.stringify(updates.files));
7643
+ }
7644
+ if (updates.parentId !== void 0) {
7645
+ setClauses.push(`parent_id = $${paramIndex++}`);
7646
+ params.push(updates.parentId);
7647
+ }
7648
+ if (updates.sourceId !== void 0) {
7649
+ setClauses.push(`source_id = $${paramIndex++}`);
7650
+ params.push(updates.sourceId);
7651
+ }
7652
+ if (updates.context !== void 0) {
7653
+ setClauses.push(`context = $${paramIndex++}`);
7654
+ params.push(updates.context === null ? null : JSON.stringify(updates.context));
7655
+ }
7656
+ if (updates.ownerType !== void 0) {
7657
+ setClauses.push(`owner_type = $${paramIndex++}`);
7658
+ params.push(updates.ownerType);
7659
+ }
7660
+ if (updates.ownerId !== void 0) {
7661
+ setClauses.push(`owner_id = $${paramIndex++}`);
7662
+ params.push(updates.ownerId);
7663
+ }
7664
+ if (updates.requireReview !== void 0) {
7665
+ setClauses.push(`require_review = $${paramIndex++}`);
7666
+ params.push(updates.requireReview);
7667
+ }
7668
+ if (updates.dependencies !== void 0) {
7669
+ setClauses.push(`dependencies = $${paramIndex++}`);
7670
+ params.push(JSON.stringify(updates.dependencies));
7671
+ }
7672
+ if (updates.result !== void 0) {
7673
+ setClauses.push(`result = $${paramIndex++}`);
7674
+ params.push(updates.result);
7675
+ }
7676
+ if (updates.failureReason !== void 0) {
7677
+ setClauses.push(`failure_reason = $${paramIndex++}`);
7678
+ params.push(updates.failureReason);
7679
+ }
7680
+ if (updates.workspaceId !== void 0) {
7681
+ setClauses.push(`workspace_id = $${paramIndex++}`);
7682
+ params.push(updates.workspaceId);
7683
+ }
7684
+ if (updates.projectId !== void 0) {
7685
+ setClauses.push(`project_id = $${paramIndex++}`);
7686
+ params.push(updates.projectId);
7687
+ }
7688
+ setClauses.push(`updated_at = ${nextUpdatedAtSql("child.updated_at")}`);
7689
+ const tenantParam = paramIndex++;
7690
+ const idParam = paramIndex++;
7691
+ const statusesParam = paramIndex++;
7692
+ const updatedAtParam = paramIndex++;
7693
+ const parentIdParam = paramIndex++;
7694
+ const parentUpdatedAtParam = paramIndex++;
7695
+ params.push(
7696
+ tenantId,
7697
+ id,
7698
+ expectedStatuses,
7699
+ new Date(expectedUpdatedAt).toISOString(),
7700
+ parentId,
7701
+ new Date(expectedParentUpdatedAt).toISOString()
7702
+ );
7703
+ const result = await this.pool.query(
7704
+ `UPDATE lattice_tasks AS child SET ${setClauses.join(", ")}
7705
+ WHERE child.tenant_id = $${tenantParam} AND child.id = $${idParam}
7706
+ AND child.status = ANY($${statusesParam}::text[])
7707
+ AND ${canonicalTimestampSnapshotSql("child.updated_at", `$${updatedAtParam}`)}
7708
+ AND EXISTS (
7709
+ SELECT 1 FROM lattice_tasks AS parent
7710
+ WHERE parent.tenant_id = child.tenant_id
7711
+ AND parent.id = $${parentIdParam}
7712
+ AND ${canonicalTimestampSnapshotSql("parent.updated_at", `$${parentUpdatedAtParam}`, true)}
7713
+ )
7714
+ RETURNING child.*`,
7715
+ params
7716
+ );
7717
+ return result.rows[0] ? mapRowToTask(result.rows[0]) : null;
7718
+ }
7719
+ async delete(tenantId, id) {
7720
+ await this.ensureInitialized();
7721
+ const result = await this.pool.query(
7722
+ `DELETE FROM lattice_tasks WHERE tenant_id = $1 AND id = $2`,
7723
+ [tenantId, id]
7724
+ );
7725
+ return (result.rowCount ?? 0) > 0;
7726
+ }
7727
+ async dispose() {
7728
+ if (this.ownsPool && this.pool) {
7729
+ await this.pool.end();
7730
+ }
7731
+ }
7732
+ async ensureInitialized() {
7733
+ if (!this.initialized) {
7734
+ await this.initialize();
7735
+ }
7736
+ }
7737
+ };
7738
+
7739
+ // src/stores/PostgreSQLTaskWorkItemStore.ts
7740
+ var import_uuid4 = require("uuid");
7741
+ var PostgreSQLTaskWorkItemStore = class {
7742
+ constructor(pool) {
7743
+ this.pool = pool;
7744
+ }
7745
+ async create(params) {
7746
+ const id = (0, import_uuid4.v4)();
7747
+ const result = await this.pool.query(
7748
+ `INSERT INTO lattice_task_work_items
7749
+ (id, tenant_id, task_id, action, actor, thread_id, summary, detail, attempt, workspace_id, project_id)
7750
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
7751
+ RETURNING *`,
7752
+ [
7753
+ id,
7754
+ params.tenantId,
7755
+ params.taskId,
7756
+ params.action,
7757
+ params.actor,
7758
+ params.threadId || null,
7759
+ params.summary || null,
7760
+ params.detail ? JSON.stringify(params.detail) : null,
7761
+ params.attempt || null,
7762
+ params.workspaceId || null,
7763
+ params.projectId || null
7764
+ ]
7765
+ );
7766
+ return this.rowToItem(result.rows[0]);
7767
+ }
7768
+ /** Find an event by its tenant- and task-scoped key without pagination. */
7769
+ async findByEventKey(tenantId, taskId, eventKey) {
7770
+ const result = await this.pool.query(
7771
+ `SELECT * FROM lattice_task_work_items
7772
+ WHERE tenant_id = $1 AND task_id = $2 AND event_key = $3`,
7773
+ [tenantId, taskId, eventKey]
7774
+ );
7775
+ return result.rows[0] ? this.rowToItem(result.rows[0]) : null;
7776
+ }
7777
+ /** Atomically return an existing event or create it once. */
7778
+ async createIfAbsentByEventKey(params) {
7779
+ const result = await this.pool.query(
7780
+ `INSERT INTO lattice_task_work_items
7781
+ (id, tenant_id, task_id, action, actor, thread_id, summary, detail, attempt, workspace_id, project_id, event_key)
7782
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
7783
+ ON CONFLICT (tenant_id, task_id, event_key) WHERE event_key IS NOT NULL
7784
+ DO UPDATE SET event_key = EXCLUDED.event_key
7785
+ RETURNING *`,
7786
+ [
7787
+ (0, import_uuid4.v4)(),
7788
+ params.tenantId,
7789
+ params.taskId,
7790
+ params.action,
7791
+ params.actor,
7792
+ params.threadId || null,
7793
+ params.summary || null,
7794
+ params.detail ? JSON.stringify(params.detail) : null,
7795
+ params.attempt ?? null,
7796
+ params.workspaceId || null,
7797
+ params.projectId || null,
7798
+ params.eventKey
7799
+ ]
7800
+ );
7801
+ return this.rowToItem(result.rows[0]);
7802
+ }
7803
+ async list(filter) {
7804
+ let query = `SELECT * FROM lattice_task_work_items WHERE tenant_id = $1 AND task_id = $2`;
7805
+ const params = [filter.tenantId, filter.taskId];
7806
+ if (filter.action) {
7807
+ query += ` AND action = $${params.length + 1}`;
7808
+ params.push(filter.action);
7809
+ }
7810
+ if (filter.workspaceId) {
7811
+ query += ` AND workspace_id = $${params.length + 1}`;
7812
+ params.push(filter.workspaceId);
7813
+ }
7814
+ if (filter.projectId) {
7815
+ query += ` AND project_id = $${params.length + 1}`;
7816
+ params.push(filter.projectId);
7817
+ }
7818
+ const order = filter.order === "desc" ? "DESC" : "ASC";
7819
+ query += ` ORDER BY created_at ${order}, id ${order}`;
7820
+ if (filter.limit) {
7821
+ query += ` LIMIT $${params.length + 1}`;
7822
+ params.push(filter.limit);
7823
+ }
7824
+ if (filter.offset) {
7825
+ query += ` OFFSET $${params.length + 1}`;
7826
+ params.push(filter.offset);
7827
+ }
7828
+ const result = await this.pool.query(query, params);
7829
+ return result.rows.map((row) => this.rowToItem(row));
7830
+ }
7831
+ rowToItem(row) {
7832
+ return {
7833
+ id: row.id,
7834
+ taskId: row.task_id,
7835
+ tenantId: row.tenant_id,
7836
+ action: row.action,
7837
+ actor: row.actor,
7838
+ threadId: row.thread_id,
7839
+ summary: row.summary,
7840
+ detail: row.detail,
7841
+ attempt: row.attempt,
7842
+ workspaceId: row.workspace_id,
7843
+ projectId: row.project_id,
7844
+ eventKey: row.event_key == null ? void 0 : row.event_key,
7845
+ createdAt: new Date(row.created_at)
7846
+ };
7847
+ }
7085
7848
  };
7086
7849
 
7087
7850
  // src/stores/MenuStore.ts
7088
- var import_pg19 = require("pg");
7851
+ var import_pg20 = require("pg");
7089
7852
 
7090
7853
  // src/migrations/menu_items_migration.ts
7091
7854
  var createMenuItemsTable = {
@@ -7162,7 +7925,7 @@ var MenuStore = class {
7162
7925
  this.initialized = true;
7163
7926
  return;
7164
7927
  }
7165
- this.pool = typeof options.poolConfig === "string" ? new import_pg19.Pool({ connectionString: options.poolConfig }) : options.poolConfig ? new import_pg19.Pool(options.poolConfig) : (() => {
7928
+ this.pool = typeof options.poolConfig === "string" ? new import_pg20.Pool({ connectionString: options.poolConfig }) : options.poolConfig ? new import_pg20.Pool(options.poolConfig) : (() => {
7166
7929
  throw new Error("Either pool or poolConfig must be provided");
7167
7930
  })();
7168
7931
  this.migrationManager = new MigrationManager(this.pool);
@@ -7317,7 +8080,7 @@ var MenuStore = class {
7317
8080
  };
7318
8081
 
7319
8082
  // src/stores/PostgresSharedResourceStore.ts
7320
- var import_pg20 = require("pg");
8083
+ var import_pg21 = require("pg");
7321
8084
 
7322
8085
  // src/migrations/shared_resources_migration.ts
7323
8086
  var createSharedResourcesTable = {
@@ -7371,7 +8134,7 @@ var PostgresSharedResourceStore = class {
7371
8134
  this.initialized = true;
7372
8135
  return;
7373
8136
  }
7374
- this.pool = typeof options.poolConfig === "string" ? new import_pg20.Pool({ connectionString: options.poolConfig }) : options.poolConfig ? new import_pg20.Pool(options.poolConfig) : (() => {
8137
+ this.pool = typeof options.poolConfig === "string" ? new import_pg21.Pool({ connectionString: options.poolConfig }) : options.poolConfig ? new import_pg21.Pool(options.poolConfig) : (() => {
7375
8138
  throw new Error("Either pool or poolConfig must be provided");
7376
8139
  })();
7377
8140
  this.migrationManager = new MigrationManager(this.pool);
@@ -7540,7 +8303,7 @@ var PostgresSharedResourceStore = class {
7540
8303
  };
7541
8304
 
7542
8305
  // src/stores/PostgreSQLCollectionStore.ts
7543
- var import_pg21 = require("pg");
8306
+ var import_pg22 = require("pg");
7544
8307
  var PostgreSQLCollectionStore = class {
7545
8308
  constructor(options) {
7546
8309
  this.initialized = false;
@@ -7553,9 +8316,9 @@ var PostgreSQLCollectionStore = class {
7553
8316
  return;
7554
8317
  }
7555
8318
  if (typeof options.poolConfig === "string") {
7556
- this.pool = new import_pg21.Pool({ connectionString: options.poolConfig });
8319
+ this.pool = new import_pg22.Pool({ connectionString: options.poolConfig });
7557
8320
  } else if (options.poolConfig) {
7558
- this.pool = new import_pg21.Pool(options.poolConfig);
8321
+ this.pool = new import_pg22.Pool(options.poolConfig);
7559
8322
  } else {
7560
8323
  throw new Error("Either pool or poolConfig must be provided");
7561
8324
  }
@@ -7670,7 +8433,7 @@ var PostgreSQLCollectionStore = class {
7670
8433
  var import_langgraph_checkpoint_postgres = require("@langchain/langgraph-checkpoint-postgres");
7671
8434
 
7672
8435
  // src/PGVectorStoreProvider.ts
7673
- var import_pg22 = require("pg");
8436
+ var import_pg23 = require("pg");
7674
8437
  var import_pgvector = require("@langchain/community/vectorstores/pgvector");
7675
8438
  var import_documents = require("@langchain/core/documents");
7676
8439
  var import_core6 = require("@axiom-lattice/core");
@@ -7744,7 +8507,7 @@ var PGVectorStoreProvider = class {
7744
8507
  }
7745
8508
  };
7746
8509
  function createPGVectorStoreProvider(connectionString) {
7747
- const pool = new import_pg22.Pool({ connectionString });
8510
+ const pool = new import_pg23.Pool({ connectionString });
7748
8511
  return {
7749
8512
  provider: new PGVectorStoreProvider(pool, connectionString),
7750
8513
  pool
@@ -8078,10 +8841,24 @@ var addWorkItemProjectFieldsMigration = {
8078
8841
  `);
8079
8842
  }
8080
8843
  };
8844
+ var addTaskWorkItemEventKeyMigration = {
8845
+ version: 168,
8846
+ name: "add_task_work_item_event_key",
8847
+ up: async (client) => {
8848
+ await client.query(`
8849
+ ALTER TABLE lattice_task_work_items
8850
+ ADD COLUMN IF NOT EXISTS event_key TEXT
8851
+ `);
8852
+ await client.query(`
8853
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_task_work_items_event_key
8854
+ ON lattice_task_work_items (tenant_id, task_id, event_key) WHERE event_key IS NOT NULL
8855
+ `);
8856
+ }
8857
+ };
8081
8858
 
8082
8859
  // src/createPgStoreConfig.ts
8083
8860
  async function createPgStoreConfig(connectionString) {
8084
- const pool = new import_pg23.Pool({ connectionString });
8861
+ const pool = new import_pg24.Pool({ connectionString });
8085
8862
  const mm = new MigrationManager(pool);
8086
8863
  mm.register(createThreadsTable);
8087
8864
  mm.register(createScheduledTasksTable);
@@ -8132,6 +8909,10 @@ async function createPgStoreConfig(connectionString) {
8132
8909
  mm.register(addWorkItemProjectFieldsMigration);
8133
8910
  mm.register(createConnectionConfigsTable);
8134
8911
  mm.register(addWorkflowRunsTenantStatusUpdatedIndex);
8912
+ mm.register(addProjectKindColumn);
8913
+ mm.register(addA2AKeyAssistantIds);
8914
+ mm.register(addTaskWorkItemEventKeyMigration);
8915
+ mm.register(createAgentWebAppsTable);
8135
8916
  await mm.migrate();
8136
8917
  const checkpoint = import_langgraph_checkpoint_postgres.PostgresSaver.fromConnString(connectionString);
8137
8918
  checkpoint.setup().catch((err) => {
@@ -8159,6 +8940,7 @@ async function createPgStoreConfig(connectionString) {
8159
8940
  task: new PostgreSQLTaskStore(opts),
8160
8941
  taskWorkItem: taskWorkItemStore,
8161
8942
  a2aApiKey: new PostgreSQLA2AApiKeyStore(opts),
8943
+ agentWebApp: new PostgreSQLAgentWebAppStore(opts),
8162
8944
  schedule: new PostgreSQLScheduleStorage(opts),
8163
8945
  menu: new MenuStore(opts),
8164
8946
  sharedResource: new PostgresSharedResourceStore(opts),
@@ -8169,7 +8951,7 @@ async function createPgStoreConfig(connectionString) {
8169
8951
  }
8170
8952
 
8171
8953
  // src/stores/PostgreSQLSkillStore.ts
8172
- var import_pg24 = require("pg");
8954
+ var import_pg25 = require("pg");
8173
8955
  var PostgreSQLSkillStore = class {
8174
8956
  constructor(options) {
8175
8957
  this.initialized = false;
@@ -8182,9 +8964,9 @@ var PostgreSQLSkillStore = class {
8182
8964
  return;
8183
8965
  }
8184
8966
  if (typeof options.poolConfig === "string") {
8185
- this.pool = new import_pg24.Pool({ connectionString: options.poolConfig });
8967
+ this.pool = new import_pg25.Pool({ connectionString: options.poolConfig });
8186
8968
  } else if (options.poolConfig) {
8187
- this.pool = new import_pg24.Pool(options.poolConfig);
8969
+ this.pool = new import_pg25.Pool(options.poolConfig);
8188
8970
  } else {
8189
8971
  throw new Error("Either pool or poolConfig must be provided");
8190
8972
  }
@@ -8485,7 +9267,7 @@ var PostgreSQLSkillStore = class {
8485
9267
  };
8486
9268
 
8487
9269
  // src/stores/ChannelIdentityMappingStore.ts
8488
- var import_pg25 = require("pg");
9270
+ var import_pg26 = require("pg");
8489
9271
  var ChannelIdentityMappingStore = class {
8490
9272
  constructor(options) {
8491
9273
  this.initialized = false;
@@ -8497,7 +9279,7 @@ var ChannelIdentityMappingStore = class {
8497
9279
  this.initialized = true;
8498
9280
  return;
8499
9281
  }
8500
- this.pool = typeof options.poolConfig === "string" ? new import_pg25.Pool({ connectionString: options.poolConfig }) : options.poolConfig ? new import_pg25.Pool(options.poolConfig) : (() => {
9282
+ this.pool = typeof options.poolConfig === "string" ? new import_pg26.Pool({ connectionString: options.poolConfig }) : options.poolConfig ? new import_pg26.Pool(options.poolConfig) : (() => {
8501
9283
  throw new Error("Either pool or poolConfig must be provided");
8502
9284
  })();
8503
9285
  this.migrationManager = new MigrationManager(this.pool);
@@ -8724,6 +9506,7 @@ function mapRowToChannelIdentityMapping(row) {
8724
9506
  PGVectorStoreProvider,
8725
9507
  Pool,
8726
9508
  PostgreSQLA2AApiKeyStore,
9509
+ PostgreSQLAgentWebAppStore,
8727
9510
  PostgreSQLAssistantStore,
8728
9511
  PostgreSQLChannelInstallationStore,
8729
9512
  PostgreSQLCollectionStore,
@@ -8761,6 +9544,7 @@ function mapRowToChannelIdentityMapping(row) {
8761
9544
  changeSkillPrimaryKey,
8762
9545
  changeThreadPrimaryKey,
8763
9546
  createA2AApiKeysTable,
9547
+ createAgentWebAppsTable,
8764
9548
  createAssistantsTable,
8765
9549
  createChannelBindingsTable,
8766
9550
  createChannelIdentityMappingTables,