@axiom-lattice/pg-stores 1.0.70 → 1.0.72
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +10 -10
- package/CHANGELOG.md +18 -0
- package/dist/index.d.mts +67 -7
- package/dist/index.d.ts +67 -7
- package/dist/index.js +641 -32
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +638 -32
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -4
- package/scripts/check-migration-versions.mjs +37 -0
- package/src/createPgStoreConfig.ts +19 -16
- package/src/index.ts +6 -0
- package/src/migrations/add_workspace_project_to_queue.ts +30 -0
- package/src/migrations/assistant_owner_user_migration.ts +26 -0
- package/src/migrations/menu_items_migration.ts +38 -0
- package/src/migrations/task_migration.ts +51 -0
- package/src/migrations/workflow_tracking_migrations.ts +19 -0
- package/src/stores/MenuStore.ts +204 -0
- package/src/stores/PostgreSQLAssistantStore.ts +47 -4
- package/src/stores/PostgreSQLChannelInstallationStore.ts +26 -0
- package/src/stores/PostgreSQLTaskStore.ts +294 -0
- package/src/stores/PostgreSQLWorkflowTrackingStore.ts +29 -3
- package/src/stores/ThreadMessageQueueStore.ts +13 -9
|
@@ -132,6 +132,32 @@ export class PostgreSQLChannelInstallationStore
|
|
|
132
132
|
return result.rows.map((row) => this.mapRowToInstallation(row));
|
|
133
133
|
}
|
|
134
134
|
|
|
135
|
+
async getAllInstallations(
|
|
136
|
+
channel?: ChannelInstallationType,
|
|
137
|
+
): Promise<ChannelInstallation[]> {
|
|
138
|
+
await this.ensureInitialized();
|
|
139
|
+
|
|
140
|
+
const result = channel
|
|
141
|
+
? await this.pool.query<ChannelInstallationRow>(
|
|
142
|
+
`
|
|
143
|
+
SELECT id, tenant_id, channel, name, config, enabled, fallback_agent_id, reject_when_no_binding, created_at, updated_at
|
|
144
|
+
FROM lattice_channel_installations
|
|
145
|
+
WHERE channel = $1
|
|
146
|
+
ORDER BY created_at DESC
|
|
147
|
+
`,
|
|
148
|
+
[channel],
|
|
149
|
+
)
|
|
150
|
+
: await this.pool.query<ChannelInstallationRow>(
|
|
151
|
+
`
|
|
152
|
+
SELECT id, tenant_id, channel, name, config, enabled, fallback_agent_id, reject_when_no_binding, created_at, updated_at
|
|
153
|
+
FROM lattice_channel_installations
|
|
154
|
+
ORDER BY created_at DESC
|
|
155
|
+
`,
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
return result.rows.map((row) => this.mapRowToInstallation(row));
|
|
159
|
+
}
|
|
160
|
+
|
|
135
161
|
async createInstallation(
|
|
136
162
|
tenantId: string,
|
|
137
163
|
installationId: string,
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PostgreSQL implementation of TaskStore
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { Pool } from "pg";
|
|
6
|
+
import type { PoolConfig } from "pg";
|
|
7
|
+
import type {
|
|
8
|
+
TaskStore,
|
|
9
|
+
TaskItem,
|
|
10
|
+
CreateTaskRequest,
|
|
11
|
+
UpdateTaskRequest,
|
|
12
|
+
TaskListFilter,
|
|
13
|
+
} from "@axiom-lattice/protocols";
|
|
14
|
+
import { MigrationManager } from "../migrations/migration";
|
|
15
|
+
import { createTasksTable } from "../migrations/task_migration";
|
|
16
|
+
import { v4 as uuidv4 } from "uuid";
|
|
17
|
+
|
|
18
|
+
interface TaskRow {
|
|
19
|
+
id: string;
|
|
20
|
+
tenant_id: string;
|
|
21
|
+
owner_type: string;
|
|
22
|
+
owner_id: string;
|
|
23
|
+
title: string;
|
|
24
|
+
description: string | null;
|
|
25
|
+
status: string;
|
|
26
|
+
priority: string;
|
|
27
|
+
due_date: string | null;
|
|
28
|
+
metadata: Record<string, unknown> | null;
|
|
29
|
+
parent_id: string | null;
|
|
30
|
+
source_id: string | null;
|
|
31
|
+
context: Record<string, unknown> | null;
|
|
32
|
+
created_at: string;
|
|
33
|
+
updated_at: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function mapRowToTask(row: TaskRow): TaskItem {
|
|
37
|
+
return {
|
|
38
|
+
id: row.id,
|
|
39
|
+
tenantId: row.tenant_id,
|
|
40
|
+
ownerType: row.owner_type as "user" | "agent",
|
|
41
|
+
ownerId: row.owner_id,
|
|
42
|
+
title: row.title,
|
|
43
|
+
description: row.description ?? undefined,
|
|
44
|
+
status: row.status as TaskItem["status"],
|
|
45
|
+
priority: row.priority as TaskItem["priority"],
|
|
46
|
+
dueDate: row.due_date ?? undefined,
|
|
47
|
+
metadata: row.metadata ?? undefined,
|
|
48
|
+
parentId: row.parent_id ?? undefined,
|
|
49
|
+
sourceId: row.source_id ?? undefined,
|
|
50
|
+
context: row.context ?? undefined,
|
|
51
|
+
createdAt: new Date(row.created_at),
|
|
52
|
+
updatedAt: new Date(row.updated_at),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface PostgreSQLTaskStoreOptions {
|
|
57
|
+
poolConfig: string | PoolConfig;
|
|
58
|
+
autoMigrate?: boolean;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export class PostgreSQLTaskStore implements TaskStore {
|
|
62
|
+
private pool: Pool;
|
|
63
|
+
private migrationManager: MigrationManager;
|
|
64
|
+
private initialized: boolean = false;
|
|
65
|
+
private ownsPool: boolean = true;
|
|
66
|
+
private initPromise: Promise<void> | null = null;
|
|
67
|
+
|
|
68
|
+
constructor(options: PostgreSQLTaskStoreOptions) {
|
|
69
|
+
if (typeof options.poolConfig === "string") {
|
|
70
|
+
this.pool = new Pool({ connectionString: options.poolConfig });
|
|
71
|
+
} else {
|
|
72
|
+
this.pool = new Pool(options.poolConfig);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
this.migrationManager = new MigrationManager(this.pool);
|
|
76
|
+
this.migrationManager.register(createTasksTable);
|
|
77
|
+
|
|
78
|
+
if (options.autoMigrate !== false) {
|
|
79
|
+
this.initialize().catch((error) => {
|
|
80
|
+
console.error("Failed to initialize PostgreSQLTaskStore:", error);
|
|
81
|
+
throw error;
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async initialize(): Promise<void> {
|
|
87
|
+
if (this.initialized) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (this.initPromise) {
|
|
92
|
+
return this.initPromise;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
this.initPromise = (async () => {
|
|
96
|
+
try {
|
|
97
|
+
await this.migrationManager.migrate();
|
|
98
|
+
this.initialized = true;
|
|
99
|
+
} finally {
|
|
100
|
+
this.initPromise = null;
|
|
101
|
+
}
|
|
102
|
+
})();
|
|
103
|
+
|
|
104
|
+
return this.initPromise;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async create(
|
|
108
|
+
params: CreateTaskRequest & { tenantId: string; ownerType: string; ownerId: string },
|
|
109
|
+
): Promise<TaskItem> {
|
|
110
|
+
await this.ensureInitialized();
|
|
111
|
+
const id = uuidv4();
|
|
112
|
+
const now = new Date().toISOString();
|
|
113
|
+
|
|
114
|
+
await this.pool.query(
|
|
115
|
+
`INSERT INTO lattice_tasks (id, tenant_id, owner_type, owner_id, title, description, status, priority, due_date, metadata, parent_id, source_id, context, created_at, updated_at)
|
|
116
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15)`,
|
|
117
|
+
[
|
|
118
|
+
id,
|
|
119
|
+
params.tenantId,
|
|
120
|
+
params.ownerType,
|
|
121
|
+
params.ownerId,
|
|
122
|
+
params.title,
|
|
123
|
+
params.description || null,
|
|
124
|
+
params.status || "pending",
|
|
125
|
+
params.priority || "medium",
|
|
126
|
+
params.dueDate || null,
|
|
127
|
+
params.metadata ? JSON.stringify(params.metadata) : null,
|
|
128
|
+
params.parentId || null,
|
|
129
|
+
params.sourceId || null,
|
|
130
|
+
params.context ? JSON.stringify(params.context) : null,
|
|
131
|
+
now,
|
|
132
|
+
now,
|
|
133
|
+
],
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
return (await this.getById(params.tenantId, id))!;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
async getById(tenantId: string, id: string): Promise<TaskItem | null> {
|
|
140
|
+
await this.ensureInitialized();
|
|
141
|
+
const result = await this.pool.query<TaskRow>(
|
|
142
|
+
`SELECT * FROM lattice_tasks WHERE tenant_id = $1 AND id = $2`,
|
|
143
|
+
[tenantId, id],
|
|
144
|
+
);
|
|
145
|
+
if (result.rows.length === 0) return null;
|
|
146
|
+
return mapRowToTask(result.rows[0]);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
async list(filter: TaskListFilter): Promise<TaskItem[]> {
|
|
150
|
+
await this.ensureInitialized();
|
|
151
|
+
const conditions: string[] = [`tenant_id = $1`];
|
|
152
|
+
const params: unknown[] = [filter.tenantId];
|
|
153
|
+
let paramIndex = 2;
|
|
154
|
+
|
|
155
|
+
if (filter.ownerType) {
|
|
156
|
+
conditions.push(`owner_type = $${paramIndex++}`);
|
|
157
|
+
params.push(filter.ownerType);
|
|
158
|
+
}
|
|
159
|
+
if (filter.ownerId) {
|
|
160
|
+
conditions.push(`owner_id = $${paramIndex++}`);
|
|
161
|
+
params.push(filter.ownerId);
|
|
162
|
+
}
|
|
163
|
+
if (filter.status) {
|
|
164
|
+
conditions.push(`status = $${paramIndex++}`);
|
|
165
|
+
params.push(filter.status);
|
|
166
|
+
}
|
|
167
|
+
if (filter.priority) {
|
|
168
|
+
conditions.push(`priority = $${paramIndex++}`);
|
|
169
|
+
params.push(filter.priority);
|
|
170
|
+
}
|
|
171
|
+
if (filter.parentId) {
|
|
172
|
+
conditions.push(`parent_id = $${paramIndex++}`);
|
|
173
|
+
params.push(filter.parentId);
|
|
174
|
+
}
|
|
175
|
+
if (filter.sourceId) {
|
|
176
|
+
conditions.push(`source_id = $${paramIndex++}`);
|
|
177
|
+
params.push(filter.sourceId);
|
|
178
|
+
}
|
|
179
|
+
if (filter.metadata) {
|
|
180
|
+
for (const [key, value] of Object.entries(filter.metadata)) {
|
|
181
|
+
const safeKey = key.replace(/[^a-zA-Z0-9_]/g, '');
|
|
182
|
+
if (safeKey) {
|
|
183
|
+
conditions.push(`metadata->>'${safeKey}' = $${paramIndex++}`);
|
|
184
|
+
params.push(String(value));
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const where = conditions.join(" AND ");
|
|
190
|
+
const limit = filter.limit || 100;
|
|
191
|
+
const offset = filter.offset || 0;
|
|
192
|
+
|
|
193
|
+
const result = await this.pool.query<TaskRow>(
|
|
194
|
+
`SELECT * FROM lattice_tasks WHERE ${where} ORDER BY created_at DESC LIMIT $${paramIndex++} OFFSET $${paramIndex++}`,
|
|
195
|
+
[...params, limit, offset],
|
|
196
|
+
);
|
|
197
|
+
return result.rows.map((r) => mapRowToTask(r));
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
async update(
|
|
201
|
+
tenantId: string,
|
|
202
|
+
id: string,
|
|
203
|
+
updates: UpdateTaskRequest,
|
|
204
|
+
): Promise<TaskItem | null> {
|
|
205
|
+
await this.ensureInitialized();
|
|
206
|
+
const existing = await this.getById(tenantId, id);
|
|
207
|
+
if (!existing) return null;
|
|
208
|
+
|
|
209
|
+
const setClauses: string[] = [];
|
|
210
|
+
const params: unknown[] = [];
|
|
211
|
+
let paramIndex = 1;
|
|
212
|
+
|
|
213
|
+
if (updates.title !== undefined) {
|
|
214
|
+
setClauses.push(`title = $${paramIndex++}`);
|
|
215
|
+
params.push(updates.title);
|
|
216
|
+
}
|
|
217
|
+
if (updates.description !== undefined) {
|
|
218
|
+
setClauses.push(`description = $${paramIndex++}`);
|
|
219
|
+
params.push(updates.description);
|
|
220
|
+
}
|
|
221
|
+
if (updates.status !== undefined) {
|
|
222
|
+
setClauses.push(`status = $${paramIndex++}`);
|
|
223
|
+
params.push(updates.status);
|
|
224
|
+
}
|
|
225
|
+
if (updates.priority !== undefined) {
|
|
226
|
+
setClauses.push(`priority = $${paramIndex++}`);
|
|
227
|
+
params.push(updates.priority);
|
|
228
|
+
}
|
|
229
|
+
if (updates.dueDate !== undefined) {
|
|
230
|
+
setClauses.push(`due_date = $${paramIndex++}`);
|
|
231
|
+
params.push(updates.dueDate);
|
|
232
|
+
}
|
|
233
|
+
if (updates.metadata !== undefined) {
|
|
234
|
+
setClauses.push(`metadata = $${paramIndex++}`);
|
|
235
|
+
params.push(JSON.stringify(updates.metadata));
|
|
236
|
+
}
|
|
237
|
+
if (updates.parentId !== undefined) {
|
|
238
|
+
setClauses.push(`parent_id = $${paramIndex++}`);
|
|
239
|
+
params.push(updates.parentId);
|
|
240
|
+
}
|
|
241
|
+
if (updates.sourceId !== undefined) {
|
|
242
|
+
setClauses.push(`source_id = $${paramIndex++}`);
|
|
243
|
+
params.push(updates.sourceId);
|
|
244
|
+
}
|
|
245
|
+
if (updates.context !== undefined) {
|
|
246
|
+
setClauses.push(`context = $${paramIndex++}`);
|
|
247
|
+
params.push(JSON.stringify(updates.context));
|
|
248
|
+
}
|
|
249
|
+
if (updates.ownerType !== undefined) {
|
|
250
|
+
setClauses.push(`owner_type = $${paramIndex++}`);
|
|
251
|
+
params.push(updates.ownerType);
|
|
252
|
+
}
|
|
253
|
+
if (updates.ownerId !== undefined) {
|
|
254
|
+
setClauses.push(`owner_id = $${paramIndex++}`);
|
|
255
|
+
params.push(updates.ownerId);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
if (setClauses.length === 0) {
|
|
259
|
+
return existing;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
setClauses.push(`updated_at = $${paramIndex++}`);
|
|
263
|
+
params.push(new Date().toISOString());
|
|
264
|
+
|
|
265
|
+
params.push(tenantId, id);
|
|
266
|
+
await this.pool.query(
|
|
267
|
+
`UPDATE lattice_tasks SET ${setClauses.join(", ")} WHERE tenant_id = $${paramIndex++} AND id = $${paramIndex++}`,
|
|
268
|
+
params,
|
|
269
|
+
);
|
|
270
|
+
|
|
271
|
+
return this.getById(tenantId, id);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
async delete(tenantId: string, id: string): Promise<boolean> {
|
|
275
|
+
await this.ensureInitialized();
|
|
276
|
+
const result = await this.pool.query(
|
|
277
|
+
`DELETE FROM lattice_tasks WHERE tenant_id = $1 AND id = $2`,
|
|
278
|
+
[tenantId, id],
|
|
279
|
+
);
|
|
280
|
+
return (result.rowCount ?? 0) > 0;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
async dispose(): Promise<void> {
|
|
284
|
+
if (this.ownsPool && this.pool) {
|
|
285
|
+
await this.pool.end();
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
private async ensureInitialized(): Promise<void> {
|
|
290
|
+
if (!this.initialized) {
|
|
291
|
+
await this.initialize();
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
WorkflowRunStatus,
|
|
17
17
|
} from "@axiom-lattice/protocols";
|
|
18
18
|
import { MigrationManager } from "../migrations/migration";
|
|
19
|
-
import { createWorkflowTrackingTables } from "../migrations/workflow_tracking_migrations";
|
|
19
|
+
import { createWorkflowTrackingTables, addStepThreadId } from "../migrations/workflow_tracking_migrations";
|
|
20
20
|
|
|
21
21
|
export interface PostgreSQLWorkflowTrackingStoreOptions {
|
|
22
22
|
poolConfig: string | PoolConfig;
|
|
@@ -38,6 +38,7 @@ export class PostgreSQLWorkflowTrackingStore implements WorkflowTrackingStore {
|
|
|
38
38
|
|
|
39
39
|
this.migrationManager = new MigrationManager(this.pool);
|
|
40
40
|
this.migrationManager.register(createWorkflowTrackingTables);
|
|
41
|
+
this.migrationManager.register(addStepThreadId);
|
|
41
42
|
|
|
42
43
|
if (options.autoMigrate !== false) {
|
|
43
44
|
this.initialize().catch((error) => {
|
|
@@ -171,23 +172,47 @@ export class PostgreSQLWorkflowTrackingStore implements WorkflowTrackingStore {
|
|
|
171
172
|
const id = `step_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
172
173
|
|
|
173
174
|
await this.pool.query(
|
|
174
|
-
`INSERT INTO lattice_workflow_steps (id, run_id, tenant_id, step_type, step_name, edge_from, edge_to, edge_purpose, input, status, started_at, created_at, updated_at)
|
|
175
|
-
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)`,
|
|
175
|
+
`INSERT INTO lattice_workflow_steps (id, run_id, tenant_id, step_type, step_name, edge_from, edge_to, edge_purpose, input, thread_id, status, started_at, created_at, updated_at)
|
|
176
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)`,
|
|
176
177
|
[id, request.runId, request.tenantId, request.stepType, request.stepName,
|
|
177
178
|
request.edgeFrom || null, request.edgeTo || null, request.edgePurpose || null,
|
|
178
179
|
request.input ? JSON.stringify(request.input) : null,
|
|
180
|
+
request.threadId || null,
|
|
179
181
|
'running', now, now, now]
|
|
180
182
|
);
|
|
181
183
|
|
|
182
184
|
return {
|
|
183
185
|
id, runId: request.runId, tenantId: request.tenantId,
|
|
184
186
|
stepType: request.stepType, stepName: request.stepName,
|
|
187
|
+
threadId: request.threadId,
|
|
185
188
|
edgeFrom: request.edgeFrom, edgeTo: request.edgeTo, edgePurpose: request.edgePurpose,
|
|
186
189
|
input: request.input, status: 'running', startedAt: now,
|
|
187
190
|
createdAt: now, updatedAt: now,
|
|
188
191
|
};
|
|
189
192
|
}
|
|
190
193
|
|
|
194
|
+
async upsertRunStep(request: CreateRunStepRequest): Promise<RunStep> {
|
|
195
|
+
await this.ensureInitialized();
|
|
196
|
+
// Look up by (run_id, step_type, step_name) — the idempotency key
|
|
197
|
+
const result = await this.pool.query(
|
|
198
|
+
`SELECT * FROM lattice_workflow_steps WHERE run_id = $1 AND step_type = $2 AND step_name = $3`,
|
|
199
|
+
[request.runId, request.stepType, request.stepName]
|
|
200
|
+
);
|
|
201
|
+
if (result.rows.length > 0) {
|
|
202
|
+
const existing = this.mapRowToRunStep(result.rows[0]);
|
|
203
|
+
// Update threadId if not already set and request has one
|
|
204
|
+
if (!existing.threadId && request.threadId) {
|
|
205
|
+
await this.pool.query(
|
|
206
|
+
`UPDATE lattice_workflow_steps SET thread_id = $1, updated_at = $2 WHERE run_id = $3 AND id = $4`,
|
|
207
|
+
[request.threadId, new Date(), request.runId, existing.id]
|
|
208
|
+
);
|
|
209
|
+
existing.threadId = request.threadId;
|
|
210
|
+
}
|
|
211
|
+
return existing;
|
|
212
|
+
}
|
|
213
|
+
return this.createRunStep(request);
|
|
214
|
+
}
|
|
215
|
+
|
|
191
216
|
async updateRunStep(runId: string, stepId: string, updates: UpdateRunStepRequest): Promise<RunStep | null> {
|
|
192
217
|
await this.ensureInitialized();
|
|
193
218
|
const now = new Date();
|
|
@@ -281,6 +306,7 @@ export class PostgreSQLWorkflowTrackingStore implements WorkflowTrackingStore {
|
|
|
281
306
|
tenantId: row.tenant_id,
|
|
282
307
|
stepType: row.step_type as StepType,
|
|
283
308
|
stepName: row.step_name,
|
|
309
|
+
threadId: row.thread_id,
|
|
284
310
|
edgeFrom: row.edge_from,
|
|
285
311
|
edgeTo: row.edge_to,
|
|
286
312
|
edgePurpose: row.edge_purpose,
|
|
@@ -17,6 +17,7 @@ import { createThreadMessageQueueTable } from "../migrations/thread_message_queu
|
|
|
17
17
|
import { addPriorityAndCommandColumns } from "../migrations/add_priority_command_columns";
|
|
18
18
|
import { addCustomRunConfigColumn } from "../migrations/add_custom_run_config_column";
|
|
19
19
|
import { alterMessageQueueIdColumn } from "../migrations/alter_message_queue_id_column";
|
|
20
|
+
import { addWorkspaceProjectToQueue } from "../migrations/add_workspace_project_to_queue";
|
|
20
21
|
|
|
21
22
|
export type { PendingMessage, AddMessageParams, ThreadInfo };
|
|
22
23
|
|
|
@@ -45,6 +46,7 @@ export class ThreadMessageQueueStore implements IMessageQueueStore {
|
|
|
45
46
|
this.migrationManager.register(addPriorityAndCommandColumns);
|
|
46
47
|
this.migrationManager.register(addCustomRunConfigColumn);
|
|
47
48
|
this.migrationManager.register(alterMessageQueueIdColumn);
|
|
49
|
+
this.migrationManager.register(addWorkspaceProjectToQueue);
|
|
48
50
|
|
|
49
51
|
if (options.autoMigrate !== false) {
|
|
50
52
|
this.initialize().catch((error) => {
|
|
@@ -79,7 +81,7 @@ export class ThreadMessageQueueStore implements IMessageQueueStore {
|
|
|
79
81
|
* Add message to queue
|
|
80
82
|
*/
|
|
81
83
|
async addMessage(params: AddMessageParams): Promise<PendingMessage> {
|
|
82
|
-
const { threadId, tenantId, assistantId, content, type = "human", priority = 0, command, custom_run_config, id } = params;
|
|
84
|
+
const { threadId, tenantId, assistantId, workspaceId, projectId, content, type = "human", priority = 0, command, custom_run_config, id } = params;
|
|
83
85
|
|
|
84
86
|
// Get current max sequence (no lock needed without unique constraint)
|
|
85
87
|
const seqResult = await this.pool.query(
|
|
@@ -92,10 +94,10 @@ export class ThreadMessageQueueStore implements IMessageQueueStore {
|
|
|
92
94
|
|
|
93
95
|
const result = await this.pool.query(
|
|
94
96
|
`INSERT INTO lattice_thread_message_queue
|
|
95
|
-
(id, thread_id, tenant_id, assistant_id, message_content, message_type, sequence_order, priority, command, custom_run_config)
|
|
96
|
-
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
|
|
97
|
+
(id, thread_id, tenant_id, assistant_id, workspace_id, project_id, message_content, message_type, sequence_order, priority, command, custom_run_config)
|
|
98
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
|
|
97
99
|
RETURNING *`,
|
|
98
|
-
[id || crypto.randomUUID(), threadId, tenantId, assistantId, JSON.stringify(content), type, nextSeq, priority, command ? JSON.stringify(command) : null, custom_run_config ? JSON.stringify(custom_run_config) : null]
|
|
100
|
+
[id || crypto.randomUUID(), threadId, tenantId, assistantId, workspaceId || null, projectId || null, JSON.stringify(content), type, nextSeq, priority, command ? JSON.stringify(command) : null, custom_run_config ? JSON.stringify(custom_run_config) : null]
|
|
99
101
|
);
|
|
100
102
|
|
|
101
103
|
return this.rowToMessage(result.rows[0]);
|
|
@@ -106,7 +108,7 @@ export class ThreadMessageQueueStore implements IMessageQueueStore {
|
|
|
106
108
|
* Uses priority=100 to ensure message is processed first
|
|
107
109
|
*/
|
|
108
110
|
async addMessageAtHead(params: AddMessageParams): Promise<PendingMessage> {
|
|
109
|
-
const { threadId, tenantId, assistantId, content, type = "human", command, custom_run_config, id } = params;
|
|
111
|
+
const { threadId, tenantId, assistantId, workspaceId, projectId, content, type = "human", command, custom_run_config, id } = params;
|
|
110
112
|
|
|
111
113
|
const resolvedTenantId = tenantId!;
|
|
112
114
|
const resolvedAssistantId = assistantId!;
|
|
@@ -123,10 +125,10 @@ export class ThreadMessageQueueStore implements IMessageQueueStore {
|
|
|
123
125
|
// Insert with high priority (100)
|
|
124
126
|
const result = await this.pool.query(
|
|
125
127
|
`INSERT INTO lattice_thread_message_queue
|
|
126
|
-
(id, thread_id, tenant_id, assistant_id, message_content, message_type, sequence_order, priority, command, custom_run_config)
|
|
127
|
-
VALUES ($1, $2, $3, $4, $5, $6, $7, 100, $
|
|
128
|
+
(id, thread_id, tenant_id, assistant_id, workspace_id, project_id, message_content, message_type, sequence_order, priority, command, custom_run_config)
|
|
129
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, 100, $10, $11)
|
|
128
130
|
RETURNING *`,
|
|
129
|
-
[id || crypto.randomUUID(), threadId, resolvedTenantId, resolvedAssistantId, JSON.stringify(content), type, nextSeq, command ? JSON.stringify(command) : null, custom_run_config ? JSON.stringify(custom_run_config) : null]
|
|
131
|
+
[id || crypto.randomUUID(), threadId, resolvedTenantId, resolvedAssistantId, workspaceId || null, projectId || null, JSON.stringify(content), type, nextSeq, command ? JSON.stringify(command) : null, custom_run_config ? JSON.stringify(custom_run_config) : null]
|
|
130
132
|
);
|
|
131
133
|
|
|
132
134
|
return this.rowToMessage(result.rows[0]);
|
|
@@ -178,7 +180,7 @@ export class ThreadMessageQueueStore implements IMessageQueueStore {
|
|
|
178
180
|
*/
|
|
179
181
|
async getThreadsWithPendingMessages(): Promise<ThreadInfo[]> {
|
|
180
182
|
const result = await this.pool.query(
|
|
181
|
-
`SELECT DISTINCT tenant_id, assistant_id, thread_id
|
|
183
|
+
`SELECT DISTINCT ON (thread_id) tenant_id, assistant_id, thread_id, workspace_id, project_id
|
|
182
184
|
FROM lattice_thread_message_queue
|
|
183
185
|
WHERE status IN ('pending', 'processing')
|
|
184
186
|
ORDER BY thread_id`
|
|
@@ -188,6 +190,8 @@ export class ThreadMessageQueueStore implements IMessageQueueStore {
|
|
|
188
190
|
tenantId: row.tenant_id,
|
|
189
191
|
assistantId: row.assistant_id,
|
|
190
192
|
threadId: row.thread_id,
|
|
193
|
+
workspaceId: row.workspace_id || undefined,
|
|
194
|
+
projectId: row.project_id || undefined,
|
|
191
195
|
}));
|
|
192
196
|
}
|
|
193
197
|
|