@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.mjs CHANGED
@@ -1,8 +1,8 @@
1
1
  // src/index.ts
2
- import { Pool as Pool26 } from "pg";
2
+ import { Pool as Pool27 } from "pg";
3
3
 
4
4
  // src/createPgStoreConfig.ts
5
- import { Pool as Pool23 } from "pg";
5
+ import { Pool as Pool24 } from "pg";
6
6
 
7
7
  // src/migrations/migration.ts
8
8
  var MigrationManager = class {
@@ -2352,6 +2352,24 @@ var dropProjectFolderPathColumn = {
2352
2352
  }
2353
2353
  };
2354
2354
 
2355
+ // src/migrations/add_project_kind_column.ts
2356
+ var addProjectKindColumn = {
2357
+ version: 165,
2358
+ name: "add_project_kind_column",
2359
+ up: async (client) => {
2360
+ await client.query(`
2361
+ ALTER TABLE lattice_projects
2362
+ ADD COLUMN IF NOT EXISTS kind TEXT NOT NULL DEFAULT 'business'
2363
+ `);
2364
+ },
2365
+ down: async (client) => {
2366
+ await client.query(`
2367
+ ALTER TABLE lattice_projects
2368
+ DROP COLUMN IF EXISTS kind
2369
+ `);
2370
+ }
2371
+ };
2372
+
2355
2373
  // src/stores/PostgreSQLProjectStore.ts
2356
2374
  var PostgreSQLProjectStore = class {
2357
2375
  constructor(options) {
@@ -2374,6 +2392,7 @@ var PostgreSQLProjectStore = class {
2374
2392
  this.migrationManager.register(createProjectsTable);
2375
2393
  this.migrationManager.register(addProjectConfigColumn);
2376
2394
  this.migrationManager.register(dropProjectFolderPathColumn);
2395
+ this.migrationManager.register(addProjectKindColumn);
2377
2396
  if (options.autoMigrate !== false) {
2378
2397
  this.initialize().catch((error) => {
2379
2398
  console.error("Failed to initialize PostgreSQLProjectStore:", error);
@@ -2419,6 +2438,7 @@ var PostgreSQLProjectStore = class {
2419
2438
  name: row.name,
2420
2439
  description: row.description || void 0,
2421
2440
  config: row.config || void 0,
2441
+ kind: row.kind || "business",
2422
2442
  createdAt: row.created_at,
2423
2443
  updatedAt: row.updated_at
2424
2444
  };
@@ -2426,16 +2446,22 @@ var PostgreSQLProjectStore = class {
2426
2446
  /**
2427
2447
  * Get all projects for a specific workspace
2428
2448
  */
2429
- async getProjectsByWorkspace(tenantId, workspaceId) {
2449
+ async getProjectsByWorkspace(tenantId, workspaceId, filter) {
2430
2450
  await this.ensureInitialized();
2451
+ const conditions = ["tenant_id = $1", "workspace_id = $2"];
2452
+ const values = [tenantId, workspaceId];
2453
+ if (filter?.kind !== void 0) {
2454
+ values.push(filter.kind);
2455
+ conditions.push(`kind = $${values.length}`);
2456
+ }
2431
2457
  const result = await this.pool.query(
2432
2458
  `
2433
- SELECT id, tenant_id, workspace_id, name, description, config, created_at, updated_at
2459
+ SELECT id, tenant_id, workspace_id, name, description, config, kind, created_at, updated_at
2434
2460
  FROM lattice_projects
2435
- WHERE tenant_id = $1 AND workspace_id = $2
2461
+ WHERE ${conditions.join(" AND ")}
2436
2462
  ORDER BY created_at DESC
2437
2463
  `,
2438
- [tenantId, workspaceId]
2464
+ values
2439
2465
  );
2440
2466
  return result.rows.map(this.mapRowToProject);
2441
2467
  }
@@ -2446,7 +2472,7 @@ var PostgreSQLProjectStore = class {
2446
2472
  await this.ensureInitialized();
2447
2473
  const result = await this.pool.query(
2448
2474
  `
2449
- SELECT id, tenant_id, workspace_id, name, description, config, created_at, updated_at
2475
+ SELECT id, tenant_id, workspace_id, name, description, config, kind, created_at, updated_at
2450
2476
  FROM lattice_projects
2451
2477
  WHERE id = $1 AND tenant_id = $2
2452
2478
  `,
@@ -2463,18 +2489,20 @@ var PostgreSQLProjectStore = class {
2463
2489
  async createProject(tenantId, workspaceId, id, data) {
2464
2490
  await this.ensureInitialized();
2465
2491
  const now = /* @__PURE__ */ new Date();
2492
+ const kind = data.kind || "business";
2466
2493
  await this.pool.query(
2467
2494
  `
2468
- INSERT INTO lattice_projects (id, tenant_id, workspace_id, name, description, config, created_at, updated_at)
2469
- VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
2495
+ INSERT INTO lattice_projects (id, tenant_id, workspace_id, name, description, config, kind, created_at, updated_at)
2496
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
2470
2497
  ON CONFLICT (id, tenant_id) DO UPDATE SET
2471
2498
  workspace_id = EXCLUDED.workspace_id,
2472
2499
  name = EXCLUDED.name,
2473
2500
  description = EXCLUDED.description,
2474
2501
  config = EXCLUDED.config,
2502
+ kind = EXCLUDED.kind,
2475
2503
  updated_at = EXCLUDED.updated_at
2476
2504
  `,
2477
- [id, tenantId, workspaceId, data.name, data.description || null, data.config || null, now, now]
2505
+ [id, tenantId, workspaceId, data.name, data.description || null, data.config || null, kind, now, now]
2478
2506
  );
2479
2507
  return {
2480
2508
  id,
@@ -2483,6 +2511,7 @@ var PostgreSQLProjectStore = class {
2483
2511
  name: data.name,
2484
2512
  description: data.description,
2485
2513
  config: data.config,
2514
+ kind,
2486
2515
  createdAt: now,
2487
2516
  updatedAt: now
2488
2517
  };
@@ -2515,6 +2544,10 @@ var PostgreSQLProjectStore = class {
2515
2544
  updateFields.push(`config = $${paramIndex++}`);
2516
2545
  updateValues.push(updates.config || null);
2517
2546
  }
2547
+ if (updates.kind !== void 0) {
2548
+ updateFields.push(`kind = $${paramIndex++}`);
2549
+ updateValues.push(updates.kind);
2550
+ }
2518
2551
  if (updateFields.length === 0) {
2519
2552
  return existing;
2520
2553
  }
@@ -5745,6 +5778,24 @@ var createA2AApiKeysTable = {
5745
5778
  }
5746
5779
  };
5747
5780
 
5781
+ // src/migrations/add_a2a_key_assistant_ids.ts
5782
+ var addA2AKeyAssistantIds = {
5783
+ version: 167,
5784
+ name: "add_a2a_key_assistant_ids",
5785
+ up: async (client) => {
5786
+ await client.query(`
5787
+ ALTER TABLE lattice_a2a_api_keys
5788
+ ADD COLUMN IF NOT EXISTS assistant_ids JSONB
5789
+ `);
5790
+ },
5791
+ down: async (client) => {
5792
+ await client.query(`
5793
+ ALTER TABLE lattice_a2a_api_keys
5794
+ DROP COLUMN IF EXISTS assistant_ids
5795
+ `);
5796
+ }
5797
+ };
5798
+
5748
5799
  // src/stores/PostgreSQLA2AApiKeyStore.ts
5749
5800
  import { encrypt as encrypt5, decrypt as decrypt5 } from "@axiom-lattice/core";
5750
5801
  import { randomUUID } from "crypto";
@@ -5756,8 +5807,8 @@ function mapRowToRecord(row) {
5756
5807
  id: row.id,
5757
5808
  key: decrypt5(row.key_value),
5758
5809
  tenantId: row.tenant_id,
5759
- projectId: row.project_id || void 0,
5760
- workspaceId: row.workspace_id || void 0,
5810
+ projectId: row.project_id,
5811
+ assistantIds: row.assistant_ids ?? void 0,
5761
5812
  label: row.label || void 0,
5762
5813
  enabled: row.enabled,
5763
5814
  createdAt: row.created_at,
@@ -5780,6 +5831,7 @@ var PostgreSQLA2AApiKeyStore = class {
5780
5831
  })();
5781
5832
  this.migrationManager = new MigrationManager(this.pool);
5782
5833
  this.migrationManager.register(createA2AApiKeysTable);
5834
+ this.migrationManager.register(addA2AKeyAssistantIds);
5783
5835
  if (options.autoMigrate !== false) {
5784
5836
  this.initialize().catch((error) => {
5785
5837
  console.error("Failed to initialize PostgreSQLA2AApiKeyStore:", error);
@@ -5818,6 +5870,14 @@ var PostgreSQLA2AApiKeyStore = class {
5818
5870
  }
5819
5871
  return null;
5820
5872
  }
5873
+ async findById(id) {
5874
+ await this.ensureInitialized();
5875
+ const result = await this.pool.query(
5876
+ `SELECT * FROM lattice_a2a_api_keys WHERE id = $1`,
5877
+ [id]
5878
+ );
5879
+ return result.rows[0] ? mapRowToRecord(result.rows[0]) : null;
5880
+ }
5821
5881
  async list(params) {
5822
5882
  await this.ensureInitialized();
5823
5883
  const limit = params.limit || 100;
@@ -5839,9 +5899,9 @@ var PostgreSQLA2AApiKeyStore = class {
5839
5899
  await this.ensureInitialized();
5840
5900
  const key = generateApiKey();
5841
5901
  const result = await this.pool.query(
5842
- `INSERT INTO lattice_a2a_api_keys (key_value, tenant_id, project_id, workspace_id, label)
5902
+ `INSERT INTO lattice_a2a_api_keys (key_value, tenant_id, project_id, assistant_ids, label)
5843
5903
  VALUES ($1, $2, $3, $4, $5) RETURNING *`,
5844
- [encrypt5(key), input.tenantId, input.projectId || null, input.workspaceId || null, input.label || null]
5904
+ [encrypt5(key), input.tenantId, input.projectId, input.assistantIds ? JSON.stringify(input.assistantIds) : null, input.label || null]
5845
5905
  );
5846
5906
  const record = mapRowToRecord(result.rows[0]);
5847
5907
  record.key = key;
@@ -5892,16 +5952,240 @@ var PostgreSQLA2AApiKeyStore = class {
5892
5952
  map.set(key, {
5893
5953
  key,
5894
5954
  tenantId: row.tenant_id,
5895
- projectId: row.project_id || void 0,
5896
- workspaceId: row.workspace_id || void 0
5955
+ projectId: row.project_id,
5956
+ assistantIds: row.assistant_ids ?? void 0
5897
5957
  });
5898
5958
  }
5899
5959
  return map;
5900
5960
  }
5901
5961
  };
5902
5962
 
5903
- // src/stores/PostgreSQLScheduleStorage.ts
5963
+ // src/stores/PostgreSQLAgentWebAppStore.ts
5964
+ import { randomUUID as randomUUID2 } from "crypto";
5904
5965
  import { Pool as Pool17 } from "pg";
5966
+
5967
+ // src/migrations/agent_web_apps_migration.ts
5968
+ var createAgentWebAppsTable = {
5969
+ version: 169,
5970
+ name: "create_agent_web_apps_table",
5971
+ up: async (client) => {
5972
+ await client.query(`
5973
+ CREATE TABLE IF NOT EXISTS lattice_agent_web_apps (
5974
+ id VARCHAR(255) PRIMARY KEY,
5975
+ tenant_id VARCHAR(255) NOT NULL,
5976
+ assistant_id VARCHAR(255) NOT NULL,
5977
+ name VARCHAR(255) NOT NULL,
5978
+ description TEXT,
5979
+ status VARCHAR(32) NOT NULL DEFAULT 'draft'
5980
+ CONSTRAINT chk_lattice_agent_web_apps_status CHECK (status IN ('draft', 'active', 'disabled')),
5981
+ integration JSONB NOT NULL,
5982
+ scope JSONB NOT NULL,
5983
+ features JSONB NOT NULL,
5984
+ appearance JSONB NOT NULL DEFAULT '{}'::jsonb,
5985
+ created_at TIMESTAMPTZ NOT NULL DEFAULT date_trunc('milliseconds', NOW()),
5986
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT date_trunc('milliseconds', NOW())
5987
+ )
5988
+ `);
5989
+ await client.query(`
5990
+ CREATE INDEX IF NOT EXISTS idx_lattice_agent_web_apps_tenant_assistant
5991
+ ON lattice_agent_web_apps(tenant_id, assistant_id)
5992
+ `);
5993
+ },
5994
+ down: async (client) => {
5995
+ await client.query(
5996
+ "DROP INDEX IF EXISTS idx_lattice_agent_web_apps_tenant_assistant"
5997
+ );
5998
+ await client.query("DROP TABLE IF EXISTS lattice_agent_web_apps");
5999
+ }
6000
+ };
6001
+
6002
+ // src/stores/PostgreSQLAgentWebAppStore.ts
6003
+ function isRecord(value) {
6004
+ return typeof value === "object" && value !== null && !Array.isArray(value);
6005
+ }
6006
+ function isStringArray(value) {
6007
+ return Array.isArray(value) && value.every((item) => typeof item === "string");
6008
+ }
6009
+ function isIntegration(value) {
6010
+ return isRecord(value) && value.type === "react_sdk";
6011
+ }
6012
+ function isScope(value) {
6013
+ 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));
6014
+ }
6015
+ function isFeatures(value) {
6016
+ 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";
6017
+ }
6018
+ function isAppearance(value) {
6019
+ 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");
6020
+ }
6021
+ function isStatus(value) {
6022
+ return value === "draft" || value === "active" || value === "disabled";
6023
+ }
6024
+ function mapRow(row) {
6025
+ if (!isStatus(row.status) || !isIntegration(row.integration) || !isScope(row.scope) || !isFeatures(row.features) || !isAppearance(row.appearance)) {
6026
+ throw new Error(`Invalid agent web app row: ${row.id}`);
6027
+ }
6028
+ return {
6029
+ id: row.id,
6030
+ tenantId: row.tenant_id,
6031
+ assistantId: row.assistant_id,
6032
+ name: row.name,
6033
+ description: row.description ?? void 0,
6034
+ status: row.status,
6035
+ integration: row.integration,
6036
+ scope: row.scope,
6037
+ features: row.features,
6038
+ appearance: row.appearance,
6039
+ createdAt: row.created_at,
6040
+ updatedAt: row.updated_at
6041
+ };
6042
+ }
6043
+ var PostgreSQLAgentWebAppStore = class {
6044
+ constructor(options) {
6045
+ this.initialized = false;
6046
+ this.ownsPool = true;
6047
+ this.initPromise = null;
6048
+ if (options.pool) {
6049
+ this.pool = options.pool;
6050
+ this.ownsPool = false;
6051
+ this.initialized = true;
6052
+ return;
6053
+ }
6054
+ this.pool = typeof options.poolConfig === "string" ? new Pool17({ connectionString: options.poolConfig }) : options.poolConfig ? new Pool17(options.poolConfig) : (() => {
6055
+ throw new Error("Either pool or poolConfig must be provided");
6056
+ })();
6057
+ this.migrationManager = new MigrationManager(this.pool);
6058
+ this.migrationManager.register(createAgentWebAppsTable);
6059
+ if (options.autoMigrate !== false) {
6060
+ this.startInitialization();
6061
+ }
6062
+ }
6063
+ async initialize() {
6064
+ if (this.initialized) return;
6065
+ if (this.initPromise) return this.initPromise;
6066
+ return this.startInitialization();
6067
+ }
6068
+ startInitialization() {
6069
+ this.initPromise = this.migrationManager.migrate().then(() => {
6070
+ this.initialized = true;
6071
+ });
6072
+ void this.initPromise.catch(() => void 0);
6073
+ return this.initPromise;
6074
+ }
6075
+ async dispose() {
6076
+ if (this.ownsPool) await this.pool.end();
6077
+ }
6078
+ async ensureInitialized() {
6079
+ if (!this.initialized) await this.initialize();
6080
+ }
6081
+ async list(tenantId, assistantId) {
6082
+ await this.ensureInitialized();
6083
+ const result = assistantId === void 0 ? await this.pool.query(
6084
+ `SELECT * FROM lattice_agent_web_apps
6085
+ WHERE tenant_id = $1
6086
+ ORDER BY created_at DESC, id DESC`,
6087
+ [tenantId]
6088
+ ) : await this.pool.query(
6089
+ `SELECT * FROM lattice_agent_web_apps
6090
+ WHERE tenant_id = $1 AND assistant_id = $2
6091
+ ORDER BY created_at DESC, id DESC`,
6092
+ [tenantId, assistantId]
6093
+ );
6094
+ return result.rows.map(mapRow);
6095
+ }
6096
+ async getById(tenantId, webAppId) {
6097
+ await this.ensureInitialized();
6098
+ const result = await this.pool.query(
6099
+ `SELECT * FROM lattice_agent_web_apps
6100
+ WHERE tenant_id = $1 AND id = $2`,
6101
+ [tenantId, webAppId]
6102
+ );
6103
+ return result.rows[0] ? mapRow(result.rows[0]) : null;
6104
+ }
6105
+ async findById(webAppId) {
6106
+ await this.ensureInitialized();
6107
+ const result = await this.pool.query(
6108
+ `SELECT * FROM lattice_agent_web_apps
6109
+ WHERE id = $1`,
6110
+ [webAppId]
6111
+ );
6112
+ return result.rows[0] ? mapRow(result.rows[0]) : null;
6113
+ }
6114
+ async create(tenantId, input) {
6115
+ await this.ensureInitialized();
6116
+ const id = `webapp_${randomUUID2().replace(/-/g, "")}`;
6117
+ const result = await this.pool.query(
6118
+ `INSERT INTO lattice_agent_web_apps
6119
+ (id, tenant_id, assistant_id, name, description, status, integration, scope, features, appearance)
6120
+ VALUES ($1, $2, $3, $4, $5, $6, $7::jsonb, $8::jsonb, $9::jsonb, $10::jsonb)
6121
+ RETURNING *`,
6122
+ [
6123
+ id,
6124
+ tenantId,
6125
+ input.assistantId,
6126
+ input.name,
6127
+ input.description ?? null,
6128
+ "draft",
6129
+ JSON.stringify(input.integration),
6130
+ JSON.stringify(input.scope),
6131
+ JSON.stringify(input.features),
6132
+ JSON.stringify(input.appearance)
6133
+ ]
6134
+ );
6135
+ return mapRow(result.rows[0]);
6136
+ }
6137
+ async update(tenantId, webAppId, patch, options) {
6138
+ await this.ensureInitialized();
6139
+ const assignments = [];
6140
+ const values = [];
6141
+ const add = (column, value) => {
6142
+ values.push(value);
6143
+ assignments.push(`${column} = $${values.length}`);
6144
+ };
6145
+ if (patch.name !== void 0) add("name", patch.name);
6146
+ if (Object.prototype.hasOwnProperty.call(patch, "description")) {
6147
+ add("description", patch.description ?? null);
6148
+ }
6149
+ if (patch.scope !== void 0) {
6150
+ add("scope", JSON.stringify(patch.scope));
6151
+ }
6152
+ if (patch.features !== void 0) {
6153
+ add("features", JSON.stringify(patch.features));
6154
+ }
6155
+ if (patch.appearance !== void 0) {
6156
+ add("appearance", JSON.stringify(patch.appearance));
6157
+ }
6158
+ if (patch.status !== void 0) add("status", patch.status);
6159
+ if (assignments.length === 0) return this.getById(tenantId, webAppId);
6160
+ values.push(tenantId, webAppId);
6161
+ const tenantParam = values.length - 1;
6162
+ const idParam = values.length;
6163
+ const expectedUpdatedAtClause = options?.expectedUpdatedAt ? ` AND date_trunc('milliseconds', updated_at) = $${values.push(options.expectedUpdatedAt)}` : "";
6164
+ const result = await this.pool.query(
6165
+ `UPDATE lattice_agent_web_apps
6166
+ SET ${assignments.join(", ")}, updated_at = GREATEST(
6167
+ date_trunc('milliseconds', clock_timestamp()),
6168
+ updated_at + interval '1 millisecond'
6169
+ )
6170
+ WHERE tenant_id = $${tenantParam} AND id = $${idParam}${expectedUpdatedAtClause}
6171
+ RETURNING *`,
6172
+ values
6173
+ );
6174
+ return result.rows[0] ? mapRow(result.rows[0]) : null;
6175
+ }
6176
+ async delete(tenantId, webAppId) {
6177
+ await this.ensureInitialized();
6178
+ const result = await this.pool.query(
6179
+ `DELETE FROM lattice_agent_web_apps
6180
+ WHERE tenant_id = $1 AND id = $2`,
6181
+ [tenantId, webAppId]
6182
+ );
6183
+ return result.rowCount === 1;
6184
+ }
6185
+ };
6186
+
6187
+ // src/stores/PostgreSQLScheduleStorage.ts
6188
+ import { Pool as Pool18 } from "pg";
5905
6189
  import {
5906
6190
  ScheduledTaskStatus
5907
6191
  } from "@axiom-lattice/protocols";
@@ -6078,9 +6362,9 @@ var PostgreSQLScheduleStorage = class {
6078
6362
  return;
6079
6363
  }
6080
6364
  if (typeof options.poolConfig === "string") {
6081
- this.pool = new Pool17({ connectionString: options.poolConfig });
6365
+ this.pool = new Pool18({ connectionString: options.poolConfig });
6082
6366
  } else if (options.poolConfig) {
6083
- this.pool = new Pool17(options.poolConfig);
6367
+ this.pool = new Pool18(options.poolConfig);
6084
6368
  } else {
6085
6369
  throw new Error("Either pool or poolConfig must be provided");
6086
6370
  }
@@ -6518,7 +6802,7 @@ var PostgreSQLScheduleStorage = class {
6518
6802
  };
6519
6803
 
6520
6804
  // src/stores/PostgreSQLTaskStore.ts
6521
- import { Pool as Pool18 } from "pg";
6805
+ import { Pool as Pool19 } from "pg";
6522
6806
 
6523
6807
  // src/migrations/task_migration.ts
6524
6808
  var createTasksTable = {
@@ -6617,6 +6901,49 @@ var taskMigrations = [
6617
6901
 
6618
6902
  // src/stores/PostgreSQLTaskStore.ts
6619
6903
  import { v4 as uuidv42 } from "uuid";
6904
+ function nextUpdatedAtSql(column = "updated_at") {
6905
+ return `to_char(
6906
+ date_trunc('milliseconds', GREATEST(
6907
+ clock_timestamp(),
6908
+ CASE
6909
+ WHEN ${canonicalTimestampValidationSql(column)}
6910
+ THEN ${column}::timestamptz + interval '1 millisecond'
6911
+ ELSE clock_timestamp()
6912
+ END
6913
+ ))
6914
+ AT TIME ZONE 'UTC',
6915
+ 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"'
6916
+ )`;
6917
+ }
6918
+ function canonicalTimestampValidationSql(column, allowMaximum = false) {
6919
+ const year = `substring(${column} FROM 1 FOR 4)::integer`;
6920
+ const month = `substring(${column} FROM 6 FOR 2)::integer`;
6921
+ const day = `substring(${column} FROM 9 FOR 2)::integer`;
6922
+ return `CASE
6923
+ 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
6924
+ ${allowMaximum ? "" : `${column} <> '9999-12-31T23:59:59.999Z' AND`}
6925
+ ${year} BETWEEN 1 AND 9999
6926
+ AND ${month} BETWEEN 1 AND 12
6927
+ AND ${day} BETWEEN 1 AND CASE
6928
+ WHEN ${month} = 2 THEN CASE
6929
+ WHEN (${year} % 400 = 0) OR (${year} % 4 = 0 AND ${year} % 100 <> 0) THEN 29
6930
+ ELSE 28
6931
+ END
6932
+ WHEN ${month} IN (4, 6, 9, 11) THEN 30
6933
+ ELSE 31
6934
+ END
6935
+ AND substring(${column} FROM 12 FOR 2)::integer BETWEEN 0 AND 23
6936
+ AND substring(${column} FROM 15 FOR 2)::integer BETWEEN 0 AND 59
6937
+ AND substring(${column} FROM 18 FOR 2)::integer BETWEEN 0 AND 59
6938
+ ELSE FALSE
6939
+ END`;
6940
+ }
6941
+ function canonicalTimestampSnapshotSql(column, parameter, allowMaximum = false) {
6942
+ return `CASE WHEN ${canonicalTimestampValidationSql(column, allowMaximum)}
6943
+ THEN ${column}::timestamptz = ${parameter}::timestamptz
6944
+ ELSE FALSE
6945
+ END`;
6946
+ }
6620
6947
  function parseTaskFiles(raw) {
6621
6948
  if (!raw) return void 0;
6622
6949
  try {
@@ -6665,9 +6992,9 @@ var PostgreSQLTaskStore = class {
6665
6992
  return;
6666
6993
  }
6667
6994
  if (typeof options.poolConfig === "string") {
6668
- this.pool = new Pool18({ connectionString: options.poolConfig });
6995
+ this.pool = new Pool19({ connectionString: options.poolConfig });
6669
6996
  } else if (options.poolConfig) {
6670
- this.pool = new Pool18(options.poolConfig);
6997
+ this.pool = new Pool19(options.poolConfig);
6671
6998
  } else {
6672
6999
  throw new Error("Either pool or poolConfig must be provided");
6673
7000
  }
@@ -6702,7 +7029,7 @@ var PostgreSQLTaskStore = class {
6702
7029
  }
6703
7030
  async create(params) {
6704
7031
  await this.ensureInitialized();
6705
- const id = uuidv42();
7032
+ const id = params.id ?? uuidv42();
6706
7033
  const now = (/* @__PURE__ */ new Date()).toISOString();
6707
7034
  await this.pool.query(
6708
7035
  `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)
@@ -6846,7 +7173,7 @@ var PostgreSQLTaskStore = class {
6846
7173
  }
6847
7174
  if (updates.context !== void 0) {
6848
7175
  setClauses.push(`context = $${paramIndex++}`);
6849
- params.push(JSON.stringify(updates.context));
7176
+ params.push(updates.context === null ? null : JSON.stringify(updates.context));
6850
7177
  }
6851
7178
  if (updates.ownerType !== void 0) {
6852
7179
  setClauses.push(`owner_type = $${paramIndex++}`);
@@ -6883,103 +7210,537 @@ var PostgreSQLTaskStore = class {
6883
7210
  if (setClauses.length === 0) {
6884
7211
  return existing;
6885
7212
  }
6886
- setClauses.push(`updated_at = $${paramIndex++}`);
6887
- params.push((/* @__PURE__ */ new Date()).toISOString());
7213
+ setClauses.push(`updated_at = ${nextUpdatedAtSql()}`);
6888
7214
  params.push(tenantId, id);
6889
- await this.pool.query(
6890
- `UPDATE lattice_tasks SET ${setClauses.join(", ")} WHERE tenant_id = $${paramIndex++} AND id = $${paramIndex++}`,
7215
+ const result = await this.pool.query(
7216
+ `UPDATE lattice_tasks SET ${setClauses.join(", ")} WHERE tenant_id = $${paramIndex++} AND id = $${paramIndex++} AND updated_at <> '9999-12-31T23:59:59.999Z' RETURNING *`,
6891
7217
  params
6892
7218
  );
6893
- return this.getById(tenantId, id);
7219
+ return result.rows[0] ? mapRowToTask(result.rows[0]) : null;
6894
7220
  }
6895
- async delete(tenantId, id) {
7221
+ /**
7222
+ * Atomically update a task unless its current status is blocked.
7223
+ *
7224
+ * @param tenantId Tenant identifier.
7225
+ * @param id Task identifier.
7226
+ * @param updates Partial task data to update.
7227
+ * @param blockedStatuses Current statuses that prevent the update.
7228
+ * @returns The updated task, or `null` when missing or blocked.
7229
+ */
7230
+ async updateIfStatusNotIn(tenantId, id, updates, blockedStatuses) {
6896
7231
  await this.ensureInitialized();
6897
- const result = await this.pool.query(
6898
- `DELETE FROM lattice_tasks WHERE tenant_id = $1 AND id = $2`,
6899
- [tenantId, id]
6900
- );
6901
- return (result.rowCount ?? 0) > 0;
6902
- }
6903
- async dispose() {
6904
- if (this.ownsPool && this.pool) {
6905
- await this.pool.end();
7232
+ const setClauses = [];
7233
+ const params = [];
7234
+ let paramIndex = 1;
7235
+ if (updates.title !== void 0) {
7236
+ setClauses.push(`title = $${paramIndex++}`);
7237
+ params.push(updates.title);
6906
7238
  }
6907
- }
6908
- async ensureInitialized() {
6909
- if (!this.initialized) {
6910
- await this.initialize();
7239
+ if (updates.description !== void 0) {
7240
+ setClauses.push(`description = $${paramIndex++}`);
7241
+ params.push(updates.description);
6911
7242
  }
6912
- }
6913
- };
6914
-
6915
- // src/stores/PostgreSQLTaskWorkItemStore.ts
6916
- import { v4 } from "uuid";
6917
- var PostgreSQLTaskWorkItemStore = class {
6918
- constructor(pool) {
6919
- this.pool = pool;
6920
- }
6921
- async create(params) {
6922
- const id = v4();
6923
- const result = await this.pool.query(
6924
- `INSERT INTO lattice_task_work_items
6925
- (id, tenant_id, task_id, action, actor, thread_id, summary, detail, attempt, workspace_id, project_id)
6926
- VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
6927
- RETURNING *`,
6928
- [
6929
- id,
6930
- params.tenantId,
6931
- params.taskId,
6932
- params.action,
6933
- params.actor,
6934
- params.threadId || null,
6935
- params.summary || null,
6936
- params.detail ? JSON.stringify(params.detail) : null,
6937
- params.attempt || null,
6938
- params.workspaceId || null,
6939
- params.projectId || null
6940
- ]
6941
- );
6942
- return this.rowToItem(result.rows[0]);
6943
- }
6944
- async list(filter) {
6945
- let query = `SELECT * FROM lattice_task_work_items WHERE tenant_id = $1 AND task_id = $2`;
6946
- const params = [filter.tenantId, filter.taskId];
6947
- if (filter.action) {
6948
- query += ` AND action = $${params.length + 1}`;
6949
- params.push(filter.action);
7243
+ if (updates.status !== void 0) {
7244
+ setClauses.push(`status = $${paramIndex++}`);
7245
+ params.push(updates.status);
6950
7246
  }
6951
- query += ` ORDER BY created_at ASC`;
6952
- if (filter.limit) {
6953
- query += ` LIMIT $${params.length + 1}`;
6954
- params.push(filter.limit);
7247
+ if (updates.priority !== void 0) {
7248
+ setClauses.push(`priority = $${paramIndex++}`);
7249
+ params.push(updates.priority);
6955
7250
  }
6956
- if (filter.offset) {
6957
- query += ` OFFSET $${params.length + 1}`;
6958
- params.push(filter.offset);
7251
+ if (updates.dueDate !== void 0) {
7252
+ setClauses.push(`due_date = $${paramIndex++}`);
7253
+ params.push(updates.dueDate);
6959
7254
  }
6960
- const result = await this.pool.query(query, params);
6961
- return result.rows.map((row) => this.rowToItem(row));
6962
- }
6963
- rowToItem(row) {
6964
- return {
6965
- id: row.id,
6966
- taskId: row.task_id,
6967
- tenantId: row.tenant_id,
6968
- action: row.action,
6969
- actor: row.actor,
6970
- threadId: row.thread_id,
6971
- summary: row.summary,
6972
- detail: row.detail,
6973
- attempt: row.attempt,
6974
- workspaceId: row.workspace_id,
6975
- projectId: row.project_id,
6976
- createdAt: new Date(row.created_at)
6977
- };
6978
- }
7255
+ if (updates.metadata !== void 0) {
7256
+ setClauses.push(`metadata = $${paramIndex++}`);
7257
+ params.push(JSON.stringify(updates.metadata));
7258
+ }
7259
+ if (updates.files !== void 0) {
7260
+ setClauses.push(`files = $${paramIndex++}`);
7261
+ params.push(JSON.stringify(updates.files));
7262
+ }
7263
+ if (updates.parentId !== void 0) {
7264
+ setClauses.push(`parent_id = $${paramIndex++}`);
7265
+ params.push(updates.parentId);
7266
+ }
7267
+ if (updates.sourceId !== void 0) {
7268
+ setClauses.push(`source_id = $${paramIndex++}`);
7269
+ params.push(updates.sourceId);
7270
+ }
7271
+ if (updates.context !== void 0) {
7272
+ setClauses.push(`context = $${paramIndex++}`);
7273
+ params.push(updates.context === null ? null : JSON.stringify(updates.context));
7274
+ }
7275
+ if (updates.ownerType !== void 0) {
7276
+ setClauses.push(`owner_type = $${paramIndex++}`);
7277
+ params.push(updates.ownerType);
7278
+ }
7279
+ if (updates.ownerId !== void 0) {
7280
+ setClauses.push(`owner_id = $${paramIndex++}`);
7281
+ params.push(updates.ownerId);
7282
+ }
7283
+ if (updates.requireReview !== void 0) {
7284
+ setClauses.push(`require_review = $${paramIndex++}`);
7285
+ params.push(updates.requireReview);
7286
+ }
7287
+ if (updates.dependencies !== void 0) {
7288
+ setClauses.push(`dependencies = $${paramIndex++}`);
7289
+ params.push(JSON.stringify(updates.dependencies));
7290
+ }
7291
+ if (updates.result !== void 0) {
7292
+ setClauses.push(`result = $${paramIndex++}`);
7293
+ params.push(updates.result);
7294
+ }
7295
+ if (updates.failureReason !== void 0) {
7296
+ setClauses.push(`failure_reason = $${paramIndex++}`);
7297
+ params.push(updates.failureReason);
7298
+ }
7299
+ if (updates.workspaceId !== void 0) {
7300
+ setClauses.push(`workspace_id = $${paramIndex++}`);
7301
+ params.push(updates.workspaceId);
7302
+ }
7303
+ if (updates.projectId !== void 0) {
7304
+ setClauses.push(`project_id = $${paramIndex++}`);
7305
+ params.push(updates.projectId);
7306
+ }
7307
+ setClauses.push(`updated_at = ${nextUpdatedAtSql()}`);
7308
+ const tenantParam = paramIndex++;
7309
+ const idParam = paramIndex++;
7310
+ params.push(tenantId, id);
7311
+ const blockedClause = blockedStatuses.length > 0 ? ` AND status <> ALL($${paramIndex}::text[])` : "";
7312
+ if (blockedStatuses.length > 0) params.push(blockedStatuses);
7313
+ const result = await this.pool.query(
7314
+ `UPDATE lattice_tasks SET ${setClauses.join(", ")} WHERE tenant_id = $${tenantParam} AND id = $${idParam} AND updated_at <> '9999-12-31T23:59:59.999Z'${blockedClause} RETURNING *`,
7315
+ params
7316
+ );
7317
+ return result.rows[0] ? mapRowToTask(result.rows[0]) : null;
7318
+ }
7319
+ /** Atomically update a task only when its current status is expected. */
7320
+ async updateIfStatusIn(tenantId, id, updates, expectedStatuses) {
7321
+ await this.ensureInitialized();
7322
+ if (expectedStatuses.length === 0) return null;
7323
+ const setClauses = [];
7324
+ const params = [];
7325
+ let paramIndex = 1;
7326
+ if (updates.title !== void 0) {
7327
+ setClauses.push(`title = $${paramIndex++}`);
7328
+ params.push(updates.title);
7329
+ }
7330
+ if (updates.description !== void 0) {
7331
+ setClauses.push(`description = $${paramIndex++}`);
7332
+ params.push(updates.description);
7333
+ }
7334
+ if (updates.status !== void 0) {
7335
+ setClauses.push(`status = $${paramIndex++}`);
7336
+ params.push(updates.status);
7337
+ }
7338
+ if (updates.priority !== void 0) {
7339
+ setClauses.push(`priority = $${paramIndex++}`);
7340
+ params.push(updates.priority);
7341
+ }
7342
+ if (updates.dueDate !== void 0) {
7343
+ setClauses.push(`due_date = $${paramIndex++}`);
7344
+ params.push(updates.dueDate);
7345
+ }
7346
+ if (updates.metadata !== void 0) {
7347
+ setClauses.push(`metadata = $${paramIndex++}`);
7348
+ params.push(JSON.stringify(updates.metadata));
7349
+ }
7350
+ if (updates.files !== void 0) {
7351
+ setClauses.push(`files = $${paramIndex++}`);
7352
+ params.push(JSON.stringify(updates.files));
7353
+ }
7354
+ if (updates.parentId !== void 0) {
7355
+ setClauses.push(`parent_id = $${paramIndex++}`);
7356
+ params.push(updates.parentId);
7357
+ }
7358
+ if (updates.sourceId !== void 0) {
7359
+ setClauses.push(`source_id = $${paramIndex++}`);
7360
+ params.push(updates.sourceId);
7361
+ }
7362
+ if (updates.context !== void 0) {
7363
+ setClauses.push(`context = $${paramIndex++}`);
7364
+ params.push(updates.context === null ? null : JSON.stringify(updates.context));
7365
+ }
7366
+ if (updates.ownerType !== void 0) {
7367
+ setClauses.push(`owner_type = $${paramIndex++}`);
7368
+ params.push(updates.ownerType);
7369
+ }
7370
+ if (updates.ownerId !== void 0) {
7371
+ setClauses.push(`owner_id = $${paramIndex++}`);
7372
+ params.push(updates.ownerId);
7373
+ }
7374
+ if (updates.requireReview !== void 0) {
7375
+ setClauses.push(`require_review = $${paramIndex++}`);
7376
+ params.push(updates.requireReview);
7377
+ }
7378
+ if (updates.dependencies !== void 0) {
7379
+ setClauses.push(`dependencies = $${paramIndex++}`);
7380
+ params.push(JSON.stringify(updates.dependencies));
7381
+ }
7382
+ if (updates.result !== void 0) {
7383
+ setClauses.push(`result = $${paramIndex++}`);
7384
+ params.push(updates.result);
7385
+ }
7386
+ if (updates.failureReason !== void 0) {
7387
+ setClauses.push(`failure_reason = $${paramIndex++}`);
7388
+ params.push(updates.failureReason);
7389
+ }
7390
+ if (updates.workspaceId !== void 0) {
7391
+ setClauses.push(`workspace_id = $${paramIndex++}`);
7392
+ params.push(updates.workspaceId);
7393
+ }
7394
+ if (updates.projectId !== void 0) {
7395
+ setClauses.push(`project_id = $${paramIndex++}`);
7396
+ params.push(updates.projectId);
7397
+ }
7398
+ setClauses.push(`updated_at = ${nextUpdatedAtSql()}`);
7399
+ const tenantParam = paramIndex++;
7400
+ const idParam = paramIndex++;
7401
+ const statusesParam = paramIndex++;
7402
+ params.push(tenantId, id, expectedStatuses);
7403
+ const result = await this.pool.query(
7404
+ `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 *`,
7405
+ params
7406
+ );
7407
+ return result.rows[0] ? mapRowToTask(result.rows[0]) : null;
7408
+ }
7409
+ /** Atomically update a task only when status and updatedAt match a read snapshot. */
7410
+ async updateIfStatusAndUpdatedAt(tenantId, id, updates, expectedStatuses, expectedUpdatedAt) {
7411
+ await this.ensureInitialized();
7412
+ if (expectedStatuses.length === 0) return null;
7413
+ const expectedIso = new Date(expectedUpdatedAt).toISOString();
7414
+ const setClauses = [];
7415
+ const params = [];
7416
+ let paramIndex = 1;
7417
+ if (updates.title !== void 0) {
7418
+ setClauses.push(`title = $${paramIndex++}`);
7419
+ params.push(updates.title);
7420
+ }
7421
+ if (updates.description !== void 0) {
7422
+ setClauses.push(`description = $${paramIndex++}`);
7423
+ params.push(updates.description);
7424
+ }
7425
+ if (updates.status !== void 0) {
7426
+ setClauses.push(`status = $${paramIndex++}`);
7427
+ params.push(updates.status);
7428
+ }
7429
+ if (updates.priority !== void 0) {
7430
+ setClauses.push(`priority = $${paramIndex++}`);
7431
+ params.push(updates.priority);
7432
+ }
7433
+ if (updates.dueDate !== void 0) {
7434
+ setClauses.push(`due_date = $${paramIndex++}`);
7435
+ params.push(updates.dueDate);
7436
+ }
7437
+ if (updates.metadata !== void 0) {
7438
+ setClauses.push(`metadata = $${paramIndex++}`);
7439
+ params.push(JSON.stringify(updates.metadata));
7440
+ }
7441
+ if (updates.files !== void 0) {
7442
+ setClauses.push(`files = $${paramIndex++}`);
7443
+ params.push(JSON.stringify(updates.files));
7444
+ }
7445
+ if (updates.parentId !== void 0) {
7446
+ setClauses.push(`parent_id = $${paramIndex++}`);
7447
+ params.push(updates.parentId);
7448
+ }
7449
+ if (updates.sourceId !== void 0) {
7450
+ setClauses.push(`source_id = $${paramIndex++}`);
7451
+ params.push(updates.sourceId);
7452
+ }
7453
+ if (updates.context !== void 0) {
7454
+ setClauses.push(`context = $${paramIndex++}`);
7455
+ params.push(updates.context === null ? null : JSON.stringify(updates.context));
7456
+ }
7457
+ if (updates.ownerType !== void 0) {
7458
+ setClauses.push(`owner_type = $${paramIndex++}`);
7459
+ params.push(updates.ownerType);
7460
+ }
7461
+ if (updates.ownerId !== void 0) {
7462
+ setClauses.push(`owner_id = $${paramIndex++}`);
7463
+ params.push(updates.ownerId);
7464
+ }
7465
+ if (updates.requireReview !== void 0) {
7466
+ setClauses.push(`require_review = $${paramIndex++}`);
7467
+ params.push(updates.requireReview);
7468
+ }
7469
+ if (updates.dependencies !== void 0) {
7470
+ setClauses.push(`dependencies = $${paramIndex++}`);
7471
+ params.push(JSON.stringify(updates.dependencies));
7472
+ }
7473
+ if (updates.result !== void 0) {
7474
+ setClauses.push(`result = $${paramIndex++}`);
7475
+ params.push(updates.result);
7476
+ }
7477
+ if (updates.failureReason !== void 0) {
7478
+ setClauses.push(`failure_reason = $${paramIndex++}`);
7479
+ params.push(updates.failureReason);
7480
+ }
7481
+ if (updates.workspaceId !== void 0) {
7482
+ setClauses.push(`workspace_id = $${paramIndex++}`);
7483
+ params.push(updates.workspaceId);
7484
+ }
7485
+ if (updates.projectId !== void 0) {
7486
+ setClauses.push(`project_id = $${paramIndex++}`);
7487
+ params.push(updates.projectId);
7488
+ }
7489
+ setClauses.push(`updated_at = ${nextUpdatedAtSql()}`);
7490
+ const tenantParam = paramIndex++;
7491
+ const idParam = paramIndex++;
7492
+ const statusesParam = paramIndex++;
7493
+ const updatedAtParam = paramIndex++;
7494
+ params.push(tenantId, id, expectedStatuses, expectedIso);
7495
+ const result = await this.pool.query(
7496
+ `UPDATE lattice_tasks SET ${setClauses.join(", ")} WHERE tenant_id = $${tenantParam} AND id = $${idParam} AND status = ANY($${statusesParam}::text[]) AND ${canonicalTimestampSnapshotSql("updated_at", `$${updatedAtParam}`)} RETURNING *`,
7497
+ params
7498
+ );
7499
+ return result.rows[0] ? mapRowToTask(result.rows[0]) : null;
7500
+ }
7501
+ /** Atomically update a child only when both child and parent snapshots match. */
7502
+ async updateIfStatusUpdatedAtAndParentUpdatedAt(tenantId, id, updates, expectedStatuses, expectedUpdatedAt, parentId, expectedParentUpdatedAt) {
7503
+ await this.ensureInitialized();
7504
+ if (expectedStatuses.length === 0) return null;
7505
+ const setClauses = [];
7506
+ const params = [];
7507
+ let paramIndex = 1;
7508
+ if (updates.title !== void 0) {
7509
+ setClauses.push(`title = $${paramIndex++}`);
7510
+ params.push(updates.title);
7511
+ }
7512
+ if (updates.description !== void 0) {
7513
+ setClauses.push(`description = $${paramIndex++}`);
7514
+ params.push(updates.description);
7515
+ }
7516
+ if (updates.status !== void 0) {
7517
+ setClauses.push(`status = $${paramIndex++}`);
7518
+ params.push(updates.status);
7519
+ }
7520
+ if (updates.priority !== void 0) {
7521
+ setClauses.push(`priority = $${paramIndex++}`);
7522
+ params.push(updates.priority);
7523
+ }
7524
+ if (updates.dueDate !== void 0) {
7525
+ setClauses.push(`due_date = $${paramIndex++}`);
7526
+ params.push(updates.dueDate);
7527
+ }
7528
+ if (updates.metadata !== void 0) {
7529
+ setClauses.push(`metadata = $${paramIndex++}`);
7530
+ params.push(JSON.stringify(updates.metadata));
7531
+ }
7532
+ if (updates.files !== void 0) {
7533
+ setClauses.push(`files = $${paramIndex++}`);
7534
+ params.push(JSON.stringify(updates.files));
7535
+ }
7536
+ if (updates.parentId !== void 0) {
7537
+ setClauses.push(`parent_id = $${paramIndex++}`);
7538
+ params.push(updates.parentId);
7539
+ }
7540
+ if (updates.sourceId !== void 0) {
7541
+ setClauses.push(`source_id = $${paramIndex++}`);
7542
+ params.push(updates.sourceId);
7543
+ }
7544
+ if (updates.context !== void 0) {
7545
+ setClauses.push(`context = $${paramIndex++}`);
7546
+ params.push(updates.context === null ? null : JSON.stringify(updates.context));
7547
+ }
7548
+ if (updates.ownerType !== void 0) {
7549
+ setClauses.push(`owner_type = $${paramIndex++}`);
7550
+ params.push(updates.ownerType);
7551
+ }
7552
+ if (updates.ownerId !== void 0) {
7553
+ setClauses.push(`owner_id = $${paramIndex++}`);
7554
+ params.push(updates.ownerId);
7555
+ }
7556
+ if (updates.requireReview !== void 0) {
7557
+ setClauses.push(`require_review = $${paramIndex++}`);
7558
+ params.push(updates.requireReview);
7559
+ }
7560
+ if (updates.dependencies !== void 0) {
7561
+ setClauses.push(`dependencies = $${paramIndex++}`);
7562
+ params.push(JSON.stringify(updates.dependencies));
7563
+ }
7564
+ if (updates.result !== void 0) {
7565
+ setClauses.push(`result = $${paramIndex++}`);
7566
+ params.push(updates.result);
7567
+ }
7568
+ if (updates.failureReason !== void 0) {
7569
+ setClauses.push(`failure_reason = $${paramIndex++}`);
7570
+ params.push(updates.failureReason);
7571
+ }
7572
+ if (updates.workspaceId !== void 0) {
7573
+ setClauses.push(`workspace_id = $${paramIndex++}`);
7574
+ params.push(updates.workspaceId);
7575
+ }
7576
+ if (updates.projectId !== void 0) {
7577
+ setClauses.push(`project_id = $${paramIndex++}`);
7578
+ params.push(updates.projectId);
7579
+ }
7580
+ setClauses.push(`updated_at = ${nextUpdatedAtSql("child.updated_at")}`);
7581
+ const tenantParam = paramIndex++;
7582
+ const idParam = paramIndex++;
7583
+ const statusesParam = paramIndex++;
7584
+ const updatedAtParam = paramIndex++;
7585
+ const parentIdParam = paramIndex++;
7586
+ const parentUpdatedAtParam = paramIndex++;
7587
+ params.push(
7588
+ tenantId,
7589
+ id,
7590
+ expectedStatuses,
7591
+ new Date(expectedUpdatedAt).toISOString(),
7592
+ parentId,
7593
+ new Date(expectedParentUpdatedAt).toISOString()
7594
+ );
7595
+ const result = await this.pool.query(
7596
+ `UPDATE lattice_tasks AS child SET ${setClauses.join(", ")}
7597
+ WHERE child.tenant_id = $${tenantParam} AND child.id = $${idParam}
7598
+ AND child.status = ANY($${statusesParam}::text[])
7599
+ AND ${canonicalTimestampSnapshotSql("child.updated_at", `$${updatedAtParam}`)}
7600
+ AND EXISTS (
7601
+ SELECT 1 FROM lattice_tasks AS parent
7602
+ WHERE parent.tenant_id = child.tenant_id
7603
+ AND parent.id = $${parentIdParam}
7604
+ AND ${canonicalTimestampSnapshotSql("parent.updated_at", `$${parentUpdatedAtParam}`, true)}
7605
+ )
7606
+ RETURNING child.*`,
7607
+ params
7608
+ );
7609
+ return result.rows[0] ? mapRowToTask(result.rows[0]) : null;
7610
+ }
7611
+ async delete(tenantId, id) {
7612
+ await this.ensureInitialized();
7613
+ const result = await this.pool.query(
7614
+ `DELETE FROM lattice_tasks WHERE tenant_id = $1 AND id = $2`,
7615
+ [tenantId, id]
7616
+ );
7617
+ return (result.rowCount ?? 0) > 0;
7618
+ }
7619
+ async dispose() {
7620
+ if (this.ownsPool && this.pool) {
7621
+ await this.pool.end();
7622
+ }
7623
+ }
7624
+ async ensureInitialized() {
7625
+ if (!this.initialized) {
7626
+ await this.initialize();
7627
+ }
7628
+ }
7629
+ };
7630
+
7631
+ // src/stores/PostgreSQLTaskWorkItemStore.ts
7632
+ import { v4 } from "uuid";
7633
+ var PostgreSQLTaskWorkItemStore = class {
7634
+ constructor(pool) {
7635
+ this.pool = pool;
7636
+ }
7637
+ async create(params) {
7638
+ const id = v4();
7639
+ const result = await this.pool.query(
7640
+ `INSERT INTO lattice_task_work_items
7641
+ (id, tenant_id, task_id, action, actor, thread_id, summary, detail, attempt, workspace_id, project_id)
7642
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
7643
+ RETURNING *`,
7644
+ [
7645
+ id,
7646
+ params.tenantId,
7647
+ params.taskId,
7648
+ params.action,
7649
+ params.actor,
7650
+ params.threadId || null,
7651
+ params.summary || null,
7652
+ params.detail ? JSON.stringify(params.detail) : null,
7653
+ params.attempt || null,
7654
+ params.workspaceId || null,
7655
+ params.projectId || null
7656
+ ]
7657
+ );
7658
+ return this.rowToItem(result.rows[0]);
7659
+ }
7660
+ /** Find an event by its tenant- and task-scoped key without pagination. */
7661
+ async findByEventKey(tenantId, taskId, eventKey) {
7662
+ const result = await this.pool.query(
7663
+ `SELECT * FROM lattice_task_work_items
7664
+ WHERE tenant_id = $1 AND task_id = $2 AND event_key = $3`,
7665
+ [tenantId, taskId, eventKey]
7666
+ );
7667
+ return result.rows[0] ? this.rowToItem(result.rows[0]) : null;
7668
+ }
7669
+ /** Atomically return an existing event or create it once. */
7670
+ async createIfAbsentByEventKey(params) {
7671
+ const result = await this.pool.query(
7672
+ `INSERT INTO lattice_task_work_items
7673
+ (id, tenant_id, task_id, action, actor, thread_id, summary, detail, attempt, workspace_id, project_id, event_key)
7674
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
7675
+ ON CONFLICT (tenant_id, task_id, event_key) WHERE event_key IS NOT NULL
7676
+ DO UPDATE SET event_key = EXCLUDED.event_key
7677
+ RETURNING *`,
7678
+ [
7679
+ v4(),
7680
+ params.tenantId,
7681
+ params.taskId,
7682
+ params.action,
7683
+ params.actor,
7684
+ params.threadId || null,
7685
+ params.summary || null,
7686
+ params.detail ? JSON.stringify(params.detail) : null,
7687
+ params.attempt ?? null,
7688
+ params.workspaceId || null,
7689
+ params.projectId || null,
7690
+ params.eventKey
7691
+ ]
7692
+ );
7693
+ return this.rowToItem(result.rows[0]);
7694
+ }
7695
+ async list(filter) {
7696
+ let query = `SELECT * FROM lattice_task_work_items WHERE tenant_id = $1 AND task_id = $2`;
7697
+ const params = [filter.tenantId, filter.taskId];
7698
+ if (filter.action) {
7699
+ query += ` AND action = $${params.length + 1}`;
7700
+ params.push(filter.action);
7701
+ }
7702
+ if (filter.workspaceId) {
7703
+ query += ` AND workspace_id = $${params.length + 1}`;
7704
+ params.push(filter.workspaceId);
7705
+ }
7706
+ if (filter.projectId) {
7707
+ query += ` AND project_id = $${params.length + 1}`;
7708
+ params.push(filter.projectId);
7709
+ }
7710
+ const order = filter.order === "desc" ? "DESC" : "ASC";
7711
+ query += ` ORDER BY created_at ${order}, id ${order}`;
7712
+ if (filter.limit) {
7713
+ query += ` LIMIT $${params.length + 1}`;
7714
+ params.push(filter.limit);
7715
+ }
7716
+ if (filter.offset) {
7717
+ query += ` OFFSET $${params.length + 1}`;
7718
+ params.push(filter.offset);
7719
+ }
7720
+ const result = await this.pool.query(query, params);
7721
+ return result.rows.map((row) => this.rowToItem(row));
7722
+ }
7723
+ rowToItem(row) {
7724
+ return {
7725
+ id: row.id,
7726
+ taskId: row.task_id,
7727
+ tenantId: row.tenant_id,
7728
+ action: row.action,
7729
+ actor: row.actor,
7730
+ threadId: row.thread_id,
7731
+ summary: row.summary,
7732
+ detail: row.detail,
7733
+ attempt: row.attempt,
7734
+ workspaceId: row.workspace_id,
7735
+ projectId: row.project_id,
7736
+ eventKey: row.event_key == null ? void 0 : row.event_key,
7737
+ createdAt: new Date(row.created_at)
7738
+ };
7739
+ }
6979
7740
  };
6980
7741
 
6981
7742
  // src/stores/MenuStore.ts
6982
- import { Pool as Pool19 } from "pg";
7743
+ import { Pool as Pool20 } from "pg";
6983
7744
 
6984
7745
  // src/migrations/menu_items_migration.ts
6985
7746
  var createMenuItemsTable = {
@@ -7056,7 +7817,7 @@ var MenuStore = class {
7056
7817
  this.initialized = true;
7057
7818
  return;
7058
7819
  }
7059
- this.pool = typeof options.poolConfig === "string" ? new Pool19({ connectionString: options.poolConfig }) : options.poolConfig ? new Pool19(options.poolConfig) : (() => {
7820
+ this.pool = typeof options.poolConfig === "string" ? new Pool20({ connectionString: options.poolConfig }) : options.poolConfig ? new Pool20(options.poolConfig) : (() => {
7060
7821
  throw new Error("Either pool or poolConfig must be provided");
7061
7822
  })();
7062
7823
  this.migrationManager = new MigrationManager(this.pool);
@@ -7211,7 +7972,7 @@ var MenuStore = class {
7211
7972
  };
7212
7973
 
7213
7974
  // src/stores/PostgresSharedResourceStore.ts
7214
- import { Pool as Pool20 } from "pg";
7975
+ import { Pool as Pool21 } from "pg";
7215
7976
 
7216
7977
  // src/migrations/shared_resources_migration.ts
7217
7978
  var createSharedResourcesTable = {
@@ -7265,7 +8026,7 @@ var PostgresSharedResourceStore = class {
7265
8026
  this.initialized = true;
7266
8027
  return;
7267
8028
  }
7268
- this.pool = typeof options.poolConfig === "string" ? new Pool20({ connectionString: options.poolConfig }) : options.poolConfig ? new Pool20(options.poolConfig) : (() => {
8029
+ this.pool = typeof options.poolConfig === "string" ? new Pool21({ connectionString: options.poolConfig }) : options.poolConfig ? new Pool21(options.poolConfig) : (() => {
7269
8030
  throw new Error("Either pool or poolConfig must be provided");
7270
8031
  })();
7271
8032
  this.migrationManager = new MigrationManager(this.pool);
@@ -7434,7 +8195,7 @@ var PostgresSharedResourceStore = class {
7434
8195
  };
7435
8196
 
7436
8197
  // src/stores/PostgreSQLCollectionStore.ts
7437
- import { Pool as Pool21 } from "pg";
8198
+ import { Pool as Pool22 } from "pg";
7438
8199
  var PostgreSQLCollectionStore = class {
7439
8200
  constructor(options) {
7440
8201
  this.initialized = false;
@@ -7447,9 +8208,9 @@ var PostgreSQLCollectionStore = class {
7447
8208
  return;
7448
8209
  }
7449
8210
  if (typeof options.poolConfig === "string") {
7450
- this.pool = new Pool21({ connectionString: options.poolConfig });
8211
+ this.pool = new Pool22({ connectionString: options.poolConfig });
7451
8212
  } else if (options.poolConfig) {
7452
- this.pool = new Pool21(options.poolConfig);
8213
+ this.pool = new Pool22(options.poolConfig);
7453
8214
  } else {
7454
8215
  throw new Error("Either pool or poolConfig must be provided");
7455
8216
  }
@@ -7564,7 +8325,7 @@ var PostgreSQLCollectionStore = class {
7564
8325
  import { PostgresSaver } from "@langchain/langgraph-checkpoint-postgres";
7565
8326
 
7566
8327
  // src/PGVectorStoreProvider.ts
7567
- import { Pool as Pool22 } from "pg";
8328
+ import { Pool as Pool23 } from "pg";
7568
8329
  import { PGVectorStore } from "@langchain/community/vectorstores/pgvector";
7569
8330
  import { Document } from "@langchain/core/documents";
7570
8331
  import { embeddingsLatticeManager } from "@axiom-lattice/core";
@@ -7638,7 +8399,7 @@ var PGVectorStoreProvider = class {
7638
8399
  }
7639
8400
  };
7640
8401
  function createPGVectorStoreProvider(connectionString) {
7641
- const pool = new Pool22({ connectionString });
8402
+ const pool = new Pool23({ connectionString });
7642
8403
  return {
7643
8404
  provider: new PGVectorStoreProvider(pool, connectionString),
7644
8405
  pool
@@ -7972,10 +8733,24 @@ var addWorkItemProjectFieldsMigration = {
7972
8733
  `);
7973
8734
  }
7974
8735
  };
8736
+ var addTaskWorkItemEventKeyMigration = {
8737
+ version: 168,
8738
+ name: "add_task_work_item_event_key",
8739
+ up: async (client) => {
8740
+ await client.query(`
8741
+ ALTER TABLE lattice_task_work_items
8742
+ ADD COLUMN IF NOT EXISTS event_key TEXT
8743
+ `);
8744
+ await client.query(`
8745
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_task_work_items_event_key
8746
+ ON lattice_task_work_items (tenant_id, task_id, event_key) WHERE event_key IS NOT NULL
8747
+ `);
8748
+ }
8749
+ };
7975
8750
 
7976
8751
  // src/createPgStoreConfig.ts
7977
8752
  async function createPgStoreConfig(connectionString) {
7978
- const pool = new Pool23({ connectionString });
8753
+ const pool = new Pool24({ connectionString });
7979
8754
  const mm = new MigrationManager(pool);
7980
8755
  mm.register(createThreadsTable);
7981
8756
  mm.register(createScheduledTasksTable);
@@ -8026,6 +8801,10 @@ async function createPgStoreConfig(connectionString) {
8026
8801
  mm.register(addWorkItemProjectFieldsMigration);
8027
8802
  mm.register(createConnectionConfigsTable);
8028
8803
  mm.register(addWorkflowRunsTenantStatusUpdatedIndex);
8804
+ mm.register(addProjectKindColumn);
8805
+ mm.register(addA2AKeyAssistantIds);
8806
+ mm.register(addTaskWorkItemEventKeyMigration);
8807
+ mm.register(createAgentWebAppsTable);
8029
8808
  await mm.migrate();
8030
8809
  const checkpoint = PostgresSaver.fromConnString(connectionString);
8031
8810
  checkpoint.setup().catch((err) => {
@@ -8053,6 +8832,7 @@ async function createPgStoreConfig(connectionString) {
8053
8832
  task: new PostgreSQLTaskStore(opts),
8054
8833
  taskWorkItem: taskWorkItemStore,
8055
8834
  a2aApiKey: new PostgreSQLA2AApiKeyStore(opts),
8835
+ agentWebApp: new PostgreSQLAgentWebAppStore(opts),
8056
8836
  schedule: new PostgreSQLScheduleStorage(opts),
8057
8837
  menu: new MenuStore(opts),
8058
8838
  sharedResource: new PostgresSharedResourceStore(opts),
@@ -8063,7 +8843,7 @@ async function createPgStoreConfig(connectionString) {
8063
8843
  }
8064
8844
 
8065
8845
  // src/stores/PostgreSQLSkillStore.ts
8066
- import { Pool as Pool24 } from "pg";
8846
+ import { Pool as Pool25 } from "pg";
8067
8847
  var PostgreSQLSkillStore = class {
8068
8848
  constructor(options) {
8069
8849
  this.initialized = false;
@@ -8076,9 +8856,9 @@ var PostgreSQLSkillStore = class {
8076
8856
  return;
8077
8857
  }
8078
8858
  if (typeof options.poolConfig === "string") {
8079
- this.pool = new Pool24({ connectionString: options.poolConfig });
8859
+ this.pool = new Pool25({ connectionString: options.poolConfig });
8080
8860
  } else if (options.poolConfig) {
8081
- this.pool = new Pool24(options.poolConfig);
8861
+ this.pool = new Pool25(options.poolConfig);
8082
8862
  } else {
8083
8863
  throw new Error("Either pool or poolConfig must be provided");
8084
8864
  }
@@ -8379,7 +9159,7 @@ var PostgreSQLSkillStore = class {
8379
9159
  };
8380
9160
 
8381
9161
  // src/stores/ChannelIdentityMappingStore.ts
8382
- import { Pool as Pool25 } from "pg";
9162
+ import { Pool as Pool26 } from "pg";
8383
9163
  var ChannelIdentityMappingStore = class {
8384
9164
  constructor(options) {
8385
9165
  this.initialized = false;
@@ -8391,7 +9171,7 @@ var ChannelIdentityMappingStore = class {
8391
9171
  this.initialized = true;
8392
9172
  return;
8393
9173
  }
8394
- this.pool = typeof options.poolConfig === "string" ? new Pool25({ connectionString: options.poolConfig }) : options.poolConfig ? new Pool25(options.poolConfig) : (() => {
9174
+ this.pool = typeof options.poolConfig === "string" ? new Pool26({ connectionString: options.poolConfig }) : options.poolConfig ? new Pool26(options.poolConfig) : (() => {
8395
9175
  throw new Error("Either pool or poolConfig must be provided");
8396
9176
  })();
8397
9177
  this.migrationManager = new MigrationManager(this.pool);
@@ -8615,8 +9395,9 @@ export {
8615
9395
  MenuStore,
8616
9396
  MigrationManager,
8617
9397
  PGVectorStoreProvider,
8618
- Pool26 as Pool,
9398
+ Pool27 as Pool,
8619
9399
  PostgreSQLA2AApiKeyStore,
9400
+ PostgreSQLAgentWebAppStore,
8620
9401
  PostgreSQLAssistantStore,
8621
9402
  PostgreSQLChannelInstallationStore,
8622
9403
  PostgreSQLCollectionStore,
@@ -8654,6 +9435,7 @@ export {
8654
9435
  changeSkillPrimaryKey,
8655
9436
  changeThreadPrimaryKey,
8656
9437
  createA2AApiKeysTable,
9438
+ createAgentWebAppsTable,
8657
9439
  createAssistantsTable,
8658
9440
  createChannelBindingsTable,
8659
9441
  createChannelIdentityMappingTables,