@axiom-lattice/local-stores 1.0.2 → 1.0.4
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 +16 -0
- package/dist/index.d.mts +23 -2
- package/dist/index.d.ts +23 -2
- package/dist/index.js +251 -12
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +250 -12
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
- package/src/createLocalStoreConfig.ts +2 -0
- package/src/index.ts +1 -0
- package/src/stores/LocalAssistantStore.ts +20 -3
- package/src/stores/LocalChannelInstallationStore.ts +16 -0
- package/src/stores/LocalTaskStore.ts +180 -0
- package/src/stores/LocalThreadMessageQueueStore.ts +14 -6
- package/src/stores/LocalWorkflowTrackingStore.ts +19 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@axiom-lattice/local-stores",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "Local SQLite-based stores for Axiom Lattice framework",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -24,8 +24,8 @@
|
|
|
24
24
|
"@langchain/langgraph-checkpoint-sqlite": "^1.0.3",
|
|
25
25
|
"@types/sql.js": "^1.4.11",
|
|
26
26
|
"sql.js": "^1.14.1",
|
|
27
|
-
"@axiom-lattice/core": "2.1.
|
|
28
|
-
"@axiom-lattice/protocols": "2.1.
|
|
27
|
+
"@axiom-lattice/core": "2.1.82",
|
|
28
|
+
"@axiom-lattice/protocols": "2.1.43"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
31
|
"@types/node": "^20.11.24",
|
|
@@ -34,6 +34,7 @@ import { LocalA2AApiKeyStore } from "./stores/LocalA2AApiKeyStore";
|
|
|
34
34
|
import { LocalThreadMessageQueueStore } from "./stores/LocalThreadMessageQueueStore";
|
|
35
35
|
import { LocalSkillStore } from "./stores/LocalSkillStore";
|
|
36
36
|
import { LocalScheduleStorage } from "./stores/LocalScheduleStorage";
|
|
37
|
+
import { LocalTaskStore } from "./stores/LocalTaskStore";
|
|
37
38
|
|
|
38
39
|
export interface LocalStoreConfigOptions {
|
|
39
40
|
/**
|
|
@@ -76,6 +77,7 @@ export async function createLocalStoreConfig(options: LocalStoreConfigOptions =
|
|
|
76
77
|
threadMessageQueue: new LocalThreadMessageQueueStore(db),
|
|
77
78
|
skill: new LocalSkillStore(db),
|
|
78
79
|
schedule: new LocalScheduleStorage(db),
|
|
80
|
+
task: new LocalTaskStore(db),
|
|
79
81
|
checkpoint: SqliteSaver.fromConnString(dbPath),
|
|
80
82
|
};
|
|
81
83
|
}
|
package/src/index.ts
CHANGED
|
@@ -42,3 +42,4 @@ export { LocalA2AApiKeyStore } from "./stores/LocalA2AApiKeyStore";
|
|
|
42
42
|
export { LocalThreadMessageQueueStore } from "./stores/LocalThreadMessageQueueStore";
|
|
43
43
|
export { LocalSkillStore } from "./stores/LocalSkillStore";
|
|
44
44
|
export { LocalScheduleStorage } from "./stores/LocalScheduleStorage";
|
|
45
|
+
export { LocalTaskStore } from "./stores/LocalTaskStore";
|
|
@@ -17,6 +17,7 @@ CREATE TABLE IF NOT EXISTS lt_assistants (
|
|
|
17
17
|
name TEXT NOT NULL,
|
|
18
18
|
description TEXT,
|
|
19
19
|
graph_definition TEXT NOT NULL,
|
|
20
|
+
owner_user_id TEXT,
|
|
20
21
|
created_at TEXT NOT NULL,
|
|
21
22
|
updated_at TEXT NOT NULL,
|
|
22
23
|
PRIMARY KEY (tenant_id, id)
|
|
@@ -29,6 +30,7 @@ interface AssistantRow {
|
|
|
29
30
|
name: string;
|
|
30
31
|
description: string | null;
|
|
31
32
|
graph_definition: string;
|
|
33
|
+
owner_user_id: string | null;
|
|
32
34
|
created_at: string;
|
|
33
35
|
updated_at: string;
|
|
34
36
|
}
|
|
@@ -63,14 +65,15 @@ export class LocalAssistantStore implements AssistantStore {
|
|
|
63
65
|
const now = nowISO();
|
|
64
66
|
|
|
65
67
|
this.db.prepare(
|
|
66
|
-
`INSERT INTO lt_assistants (id, tenant_id, name, description, graph_definition, created_at, updated_at)
|
|
67
|
-
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
68
|
+
`INSERT INTO lt_assistants (id, tenant_id, name, description, graph_definition, owner_user_id, created_at, updated_at)
|
|
69
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
68
70
|
ON CONFLICT(tenant_id, id) DO UPDATE SET
|
|
69
71
|
name = excluded.name,
|
|
70
72
|
description = excluded.description,
|
|
71
73
|
graph_definition = excluded.graph_definition,
|
|
74
|
+
owner_user_id = excluded.owner_user_id,
|
|
72
75
|
updated_at = excluded.updated_at`,
|
|
73
|
-
).run(id, tenantId, data.name, data.description || null, JSON.stringify(data.graphDefinition), now, now);
|
|
76
|
+
).run(id, tenantId, data.name, data.description || null, JSON.stringify(data.graphDefinition), data.ownerUserId || null, now, now);
|
|
74
77
|
|
|
75
78
|
return {
|
|
76
79
|
id,
|
|
@@ -78,6 +81,7 @@ export class LocalAssistantStore implements AssistantStore {
|
|
|
78
81
|
name: data.name,
|
|
79
82
|
description: data.description,
|
|
80
83
|
graphDefinition: data.graphDefinition,
|
|
84
|
+
ownerUserId: data.ownerUserId,
|
|
81
85
|
createdAt: parseISO(now),
|
|
82
86
|
updatedAt: parseISO(now),
|
|
83
87
|
};
|
|
@@ -107,6 +111,11 @@ export class LocalAssistantStore implements AssistantStore {
|
|
|
107
111
|
values.push(JSON.stringify(updates.graphDefinition));
|
|
108
112
|
}
|
|
109
113
|
|
|
114
|
+
if (updates.ownerUserId !== undefined) {
|
|
115
|
+
setClauses.push("owner_user_id = ?");
|
|
116
|
+
values.push(updates.ownerUserId || null);
|
|
117
|
+
}
|
|
118
|
+
|
|
110
119
|
if (setClauses.length === 0) return existing;
|
|
111
120
|
|
|
112
121
|
const now = nowISO();
|
|
@@ -134,6 +143,13 @@ export class LocalAssistantStore implements AssistantStore {
|
|
|
134
143
|
).get(tenantId, id);
|
|
135
144
|
return row !== undefined;
|
|
136
145
|
}
|
|
146
|
+
|
|
147
|
+
async getByOwner(tenantId: string, userId: string): Promise<Assistant | null> {
|
|
148
|
+
const row = this.db.prepare(
|
|
149
|
+
`SELECT * FROM lt_assistants WHERE tenant_id = ? AND owner_user_id = ? LIMIT 1`,
|
|
150
|
+
).get(tenantId, userId) as unknown as AssistantRow | undefined;
|
|
151
|
+
return row ? mapRowToAssistant(row) : null;
|
|
152
|
+
}
|
|
137
153
|
}
|
|
138
154
|
|
|
139
155
|
function mapRowToAssistant(row: AssistantRow): Assistant {
|
|
@@ -143,6 +159,7 @@ function mapRowToAssistant(row: AssistantRow): Assistant {
|
|
|
143
159
|
name: row.name,
|
|
144
160
|
description: row.description || undefined,
|
|
145
161
|
graphDefinition: JSON.parse(row.graph_definition),
|
|
162
|
+
ownerUserId: row.owner_user_id || undefined,
|
|
146
163
|
createdAt: parseISO(row.created_at),
|
|
147
164
|
updatedAt: parseISO(row.updated_at),
|
|
148
165
|
};
|
|
@@ -73,6 +73,22 @@ export class LocalChannelInstallationStore implements ChannelInstallationStore {
|
|
|
73
73
|
return rows.map(mapRow);
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
+
async getAllInstallations(
|
|
77
|
+
channel?: ChannelInstallationType,
|
|
78
|
+
): Promise<ChannelInstallation[]> {
|
|
79
|
+
let rows: Row[];
|
|
80
|
+
if (channel) {
|
|
81
|
+
rows = this.db.prepare(
|
|
82
|
+
`SELECT * FROM lt_channel_installations WHERE channel = ? ORDER BY created_at DESC`,
|
|
83
|
+
).all(channel) as Row[];
|
|
84
|
+
} else {
|
|
85
|
+
rows = this.db.prepare(
|
|
86
|
+
`SELECT * FROM lt_channel_installations ORDER BY created_at DESC`,
|
|
87
|
+
).all() as Row[];
|
|
88
|
+
}
|
|
89
|
+
return rows.map(mapRow);
|
|
90
|
+
}
|
|
91
|
+
|
|
76
92
|
async createInstallation(
|
|
77
93
|
tenantId: string,
|
|
78
94
|
installationId: string,
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local SQLite implementation of TaskStore.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { DatabaseWrapper } from "../database";
|
|
6
|
+
import type {
|
|
7
|
+
TaskStore,
|
|
8
|
+
TaskItem,
|
|
9
|
+
CreateTaskRequest,
|
|
10
|
+
UpdateTaskRequest,
|
|
11
|
+
TaskListFilter,
|
|
12
|
+
} from "@axiom-lattice/protocols";
|
|
13
|
+
import { ensureTable, nowISO, parseISO } from "../database";
|
|
14
|
+
import { randomUUID } from "crypto";
|
|
15
|
+
|
|
16
|
+
const DDL = `
|
|
17
|
+
CREATE TABLE IF NOT EXISTS lt_tasks (
|
|
18
|
+
id TEXT NOT NULL,
|
|
19
|
+
tenant_id TEXT NOT NULL,
|
|
20
|
+
owner_type TEXT NOT NULL DEFAULT 'user',
|
|
21
|
+
owner_id TEXT NOT NULL,
|
|
22
|
+
title TEXT NOT NULL,
|
|
23
|
+
description TEXT,
|
|
24
|
+
status TEXT NOT NULL DEFAULT 'pending',
|
|
25
|
+
priority TEXT NOT NULL DEFAULT 'medium',
|
|
26
|
+
due_date TEXT,
|
|
27
|
+
metadata TEXT,
|
|
28
|
+
parent_id TEXT,
|
|
29
|
+
source_id TEXT,
|
|
30
|
+
context TEXT,
|
|
31
|
+
created_at TEXT NOT NULL,
|
|
32
|
+
updated_at TEXT NOT NULL,
|
|
33
|
+
PRIMARY KEY (tenant_id, id)
|
|
34
|
+
);
|
|
35
|
+
`;
|
|
36
|
+
|
|
37
|
+
interface TaskRow {
|
|
38
|
+
id: string;
|
|
39
|
+
tenant_id: string;
|
|
40
|
+
owner_type: string;
|
|
41
|
+
owner_id: string;
|
|
42
|
+
title: string;
|
|
43
|
+
description: string | null;
|
|
44
|
+
status: string;
|
|
45
|
+
priority: string;
|
|
46
|
+
due_date: string | null;
|
|
47
|
+
metadata: string | null;
|
|
48
|
+
parent_id: string | null;
|
|
49
|
+
source_id: string | null;
|
|
50
|
+
context: string | null;
|
|
51
|
+
created_at: string;
|
|
52
|
+
updated_at: string;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function mapRowToTask(row: TaskRow): TaskItem {
|
|
56
|
+
return {
|
|
57
|
+
id: row.id,
|
|
58
|
+
tenantId: row.tenant_id,
|
|
59
|
+
ownerType: row.owner_type as 'user' | 'agent',
|
|
60
|
+
ownerId: row.owner_id,
|
|
61
|
+
title: row.title,
|
|
62
|
+
description: row.description ?? undefined,
|
|
63
|
+
status: row.status as TaskItem['status'],
|
|
64
|
+
priority: row.priority as TaskItem['priority'],
|
|
65
|
+
dueDate: row.due_date ?? undefined,
|
|
66
|
+
metadata: row.metadata ? JSON.parse(row.metadata) : undefined,
|
|
67
|
+
parentId: row.parent_id ?? undefined,
|
|
68
|
+
sourceId: row.source_id ?? undefined,
|
|
69
|
+
context: row.context ? JSON.parse(row.context) : undefined,
|
|
70
|
+
createdAt: parseISO(row.created_at),
|
|
71
|
+
updatedAt: parseISO(row.updated_at),
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export class LocalTaskStore implements TaskStore {
|
|
76
|
+
private db: DatabaseWrapper;
|
|
77
|
+
|
|
78
|
+
constructor(db: DatabaseWrapper) {
|
|
79
|
+
this.db = db;
|
|
80
|
+
ensureTable(db, DDL);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async create(
|
|
84
|
+
params: CreateTaskRequest & { tenantId: string; ownerType: string; ownerId: string },
|
|
85
|
+
): Promise<TaskItem> {
|
|
86
|
+
const id = randomUUID();
|
|
87
|
+
const now = nowISO();
|
|
88
|
+
|
|
89
|
+
this.db.prepare(
|
|
90
|
+
`INSERT INTO lt_tasks (id, tenant_id, owner_type, owner_id, title, description, status, priority, due_date, metadata, parent_id, source_id, context, created_at, updated_at)
|
|
91
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
92
|
+
).run(
|
|
93
|
+
id, params.tenantId, params.ownerType, params.ownerId, params.title,
|
|
94
|
+
params.description || null, params.status || 'pending', params.priority || 'medium',
|
|
95
|
+
params.dueDate || null, params.metadata ? JSON.stringify(params.metadata) : null,
|
|
96
|
+
params.parentId || null, params.sourceId || null,
|
|
97
|
+
params.context ? JSON.stringify(params.context) : null, now, now,
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
return (await this.getById(params.tenantId, id))!;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async getById(tenantId: string, id: string): Promise<TaskItem | null> {
|
|
104
|
+
const row = this.db.prepare(
|
|
105
|
+
`SELECT * FROM lt_tasks WHERE tenant_id = ? AND id = ?`,
|
|
106
|
+
).get(tenantId, id) as unknown as TaskRow | undefined;
|
|
107
|
+
return row ? mapRowToTask(row) : null;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async list(filter: TaskListFilter): Promise<TaskItem[]> {
|
|
111
|
+
const conditions: string[] = ['tenant_id = ?'];
|
|
112
|
+
const params: unknown[] = [filter.tenantId];
|
|
113
|
+
|
|
114
|
+
if (filter.ownerType) { conditions.push('owner_type = ?'); params.push(filter.ownerType); }
|
|
115
|
+
if (filter.ownerId) { conditions.push('owner_id = ?'); params.push(filter.ownerId); }
|
|
116
|
+
if (filter.status) { conditions.push('status = ?'); params.push(filter.status); }
|
|
117
|
+
if (filter.priority) { conditions.push('priority = ?'); params.push(filter.priority); }
|
|
118
|
+
if (filter.parentId) { conditions.push('parent_id = ?'); params.push(filter.parentId); }
|
|
119
|
+
if (filter.sourceId) { conditions.push('source_id = ?'); params.push(filter.sourceId); }
|
|
120
|
+
if (filter.metadata) {
|
|
121
|
+
for (const [key, value] of Object.entries(filter.metadata)) {
|
|
122
|
+
const safeKey = key.replace(/[^a-zA-Z0-9_]/g, '');
|
|
123
|
+
if (safeKey) {
|
|
124
|
+
conditions.push(`json_extract(metadata, '$.${safeKey}') = ?`);
|
|
125
|
+
params.push(String(value));
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const where = conditions.join(' AND ');
|
|
131
|
+
const limit = filter.limit || 100;
|
|
132
|
+
const offset = filter.offset || 0;
|
|
133
|
+
|
|
134
|
+
const rows = this.db.prepare(
|
|
135
|
+
`SELECT * FROM lt_tasks WHERE ${where} ORDER BY created_at DESC LIMIT ? OFFSET ?`,
|
|
136
|
+
).all(...params, limit, offset) as unknown as TaskRow[];
|
|
137
|
+
return rows.map(mapRowToTask);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
async update(
|
|
141
|
+
tenantId: string,
|
|
142
|
+
id: string,
|
|
143
|
+
updates: UpdateTaskRequest,
|
|
144
|
+
): Promise<TaskItem | null> {
|
|
145
|
+
const existing = await this.getById(tenantId, id);
|
|
146
|
+
if (!existing) return null;
|
|
147
|
+
|
|
148
|
+
const now = nowISO();
|
|
149
|
+
const setClauses: string[] = ['updated_at = ?'];
|
|
150
|
+
const values: unknown[] = [now];
|
|
151
|
+
|
|
152
|
+
if (updates.title !== undefined) { setClauses.push('title = ?'); values.push(updates.title); }
|
|
153
|
+
if (updates.description !== undefined) { setClauses.push('description = ?'); values.push(updates.description); }
|
|
154
|
+
if (updates.status !== undefined) { setClauses.push('status = ?'); values.push(updates.status); }
|
|
155
|
+
if (updates.priority !== undefined) { setClauses.push('priority = ?'); values.push(updates.priority); }
|
|
156
|
+
if (updates.dueDate !== undefined) { setClauses.push('due_date = ?'); values.push(updates.dueDate); }
|
|
157
|
+
if (updates.metadata !== undefined) { setClauses.push('metadata = ?'); values.push(JSON.stringify(updates.metadata)); }
|
|
158
|
+
if (updates.parentId !== undefined) { setClauses.push('parent_id = ?'); values.push(updates.parentId); }
|
|
159
|
+
if (updates.sourceId !== undefined) { setClauses.push('source_id = ?'); values.push(updates.sourceId); }
|
|
160
|
+
if (updates.context !== undefined) { setClauses.push('context = ?'); values.push(JSON.stringify(updates.context)); }
|
|
161
|
+
if (updates.ownerType !== undefined) { setClauses.push('owner_type = ?'); values.push(updates.ownerType); }
|
|
162
|
+
if (updates.ownerId !== undefined) { setClauses.push('owner_id = ?'); values.push(updates.ownerId); }
|
|
163
|
+
|
|
164
|
+
if (setClauses.length === 1) return existing;
|
|
165
|
+
|
|
166
|
+
values.push(tenantId, id);
|
|
167
|
+
this.db.prepare(
|
|
168
|
+
`UPDATE lt_tasks SET ${setClauses.join(', ')} WHERE tenant_id = ? AND id = ?`,
|
|
169
|
+
).run(...values);
|
|
170
|
+
|
|
171
|
+
return this.getById(tenantId, id);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
async delete(tenantId: string, id: string): Promise<boolean> {
|
|
175
|
+
const result = this.db.prepare(
|
|
176
|
+
`DELETE FROM lt_tasks WHERE tenant_id = ? AND id = ?`,
|
|
177
|
+
).run(tenantId, id);
|
|
178
|
+
return result.changes > 0;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
@@ -18,6 +18,8 @@ CREATE TABLE IF NOT EXISTS lt_thread_message_queue (
|
|
|
18
18
|
thread_id TEXT NOT NULL,
|
|
19
19
|
tenant_id TEXT NOT NULL,
|
|
20
20
|
assistant_id TEXT NOT NULL,
|
|
21
|
+
workspace_id TEXT,
|
|
22
|
+
project_id TEXT,
|
|
21
23
|
message_content TEXT NOT NULL,
|
|
22
24
|
message_type TEXT NOT NULL DEFAULT 'human',
|
|
23
25
|
sequence_order INTEGER NOT NULL DEFAULT 0,
|
|
@@ -36,6 +38,8 @@ interface QueueRow {
|
|
|
36
38
|
thread_id: string;
|
|
37
39
|
tenant_id: string;
|
|
38
40
|
assistant_id: string;
|
|
41
|
+
workspace_id: string | null;
|
|
42
|
+
project_id: string | null;
|
|
39
43
|
message_content: string;
|
|
40
44
|
message_type: string;
|
|
41
45
|
sequence_order: number;
|
|
@@ -64,10 +68,11 @@ export class LocalThreadMessageQueueStore implements IMessageQueueStore {
|
|
|
64
68
|
const nextSeq = seqRow.next_seq;
|
|
65
69
|
|
|
66
70
|
this.db.prepare(
|
|
67
|
-
`INSERT INTO lt_thread_message_queue (id, thread_id, tenant_id, assistant_id, message_content, message_type, sequence_order, priority, command, custom_run_config, created_at)
|
|
68
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
71
|
+
`INSERT INTO lt_thread_message_queue (id, thread_id, tenant_id, assistant_id, workspace_id, project_id, message_content, message_type, sequence_order, priority, command, custom_run_config, created_at)
|
|
72
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
69
73
|
).run(
|
|
70
74
|
id, params.threadId, params.tenantId, params.assistantId,
|
|
75
|
+
params.workspaceId || null, params.projectId || null,
|
|
71
76
|
JSON.stringify(params.content), params.type || "human", nextSeq,
|
|
72
77
|
params.priority || 0,
|
|
73
78
|
params.command ? JSON.stringify(params.command) : null,
|
|
@@ -87,10 +92,11 @@ export class LocalThreadMessageQueueStore implements IMessageQueueStore {
|
|
|
87
92
|
).get(params.threadId) as { next_seq: number };
|
|
88
93
|
|
|
89
94
|
this.db.prepare(
|
|
90
|
-
`INSERT INTO lt_thread_message_queue (id, thread_id, tenant_id, assistant_id, message_content, message_type, sequence_order, priority, command, custom_run_config, created_at)
|
|
91
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, 100, ?, ?, ?)`,
|
|
95
|
+
`INSERT INTO lt_thread_message_queue (id, thread_id, tenant_id, assistant_id, workspace_id, project_id, message_content, message_type, sequence_order, priority, command, custom_run_config, created_at)
|
|
96
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 100, ?, ?, ?)`,
|
|
92
97
|
).run(
|
|
93
98
|
id, params.threadId, params.tenantId!, params.assistantId!,
|
|
99
|
+
params.workspaceId || null, params.projectId || null,
|
|
94
100
|
JSON.stringify(params.content), params.type || "human", seqRow.next_seq,
|
|
95
101
|
params.command ? JSON.stringify(params.command) : null,
|
|
96
102
|
params.custom_run_config ? JSON.stringify(params.custom_run_config) : null,
|
|
@@ -123,12 +129,14 @@ export class LocalThreadMessageQueueStore implements IMessageQueueStore {
|
|
|
123
129
|
|
|
124
130
|
async getThreadsWithPendingMessages(): Promise<ThreadInfo[]> {
|
|
125
131
|
const rows = this.db.prepare(
|
|
126
|
-
`SELECT DISTINCT tenant_id, assistant_id, thread_id FROM lt_thread_message_queue WHERE status IN ('pending', 'processing') ORDER BY thread_id`,
|
|
127
|
-
).all() as { tenant_id: string; assistant_id: string; thread_id: string }[];
|
|
132
|
+
`SELECT DISTINCT ON (thread_id) tenant_id, assistant_id, thread_id, workspace_id, project_id FROM lt_thread_message_queue WHERE status IN ('pending', 'processing') ORDER BY thread_id`,
|
|
133
|
+
).all() as { tenant_id: string; assistant_id: string; thread_id: string; workspace_id: string | null; project_id: string | null }[];
|
|
128
134
|
return rows.map((r) => ({
|
|
129
135
|
tenantId: r.tenant_id,
|
|
130
136
|
assistantId: r.assistant_id,
|
|
131
137
|
threadId: r.thread_id,
|
|
138
|
+
workspaceId: r.workspace_id || undefined,
|
|
139
|
+
projectId: r.project_id || undefined,
|
|
132
140
|
}));
|
|
133
141
|
}
|
|
134
142
|
|
|
@@ -42,6 +42,7 @@ CREATE TABLE IF NOT EXISTS lt_workflow_steps (
|
|
|
42
42
|
tenant_id TEXT NOT NULL,
|
|
43
43
|
step_type TEXT NOT NULL,
|
|
44
44
|
step_name TEXT NOT NULL,
|
|
45
|
+
thread_id TEXT,
|
|
45
46
|
edge_from TEXT,
|
|
46
47
|
edge_to TEXT,
|
|
47
48
|
edge_purpose TEXT,
|
|
@@ -57,6 +58,8 @@ CREATE TABLE IF NOT EXISTS lt_workflow_steps (
|
|
|
57
58
|
PRIMARY KEY (run_id, id)
|
|
58
59
|
);
|
|
59
60
|
CREATE INDEX IF NOT EXISTS idx_lt_ws_type ON lt_workflow_steps(run_id, step_type);
|
|
61
|
+
-- Backfill for existing databases that don't have the column
|
|
62
|
+
ALTER TABLE lt_workflow_steps ADD COLUMN IF NOT EXISTS thread_id TEXT;
|
|
60
63
|
`;
|
|
61
64
|
|
|
62
65
|
interface RunRow {
|
|
@@ -82,6 +85,7 @@ interface StepRow {
|
|
|
82
85
|
tenant_id: string;
|
|
83
86
|
step_type: string;
|
|
84
87
|
step_name: string;
|
|
88
|
+
thread_id: string | null;
|
|
85
89
|
edge_from: string | null;
|
|
86
90
|
edge_to: string | null;
|
|
87
91
|
edge_purpose: string | null;
|
|
@@ -187,15 +191,17 @@ export class LocalWorkflowTrackingStore implements WorkflowTrackingStore {
|
|
|
187
191
|
const id = `step_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
188
192
|
|
|
189
193
|
this.db.prepare(
|
|
190
|
-
`INSERT INTO lt_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)
|
|
191
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 'running', ?, ?, ?)`,
|
|
194
|
+
`INSERT INTO lt_workflow_steps (id, run_id, tenant_id, step_type, step_name, thread_id, edge_from, edge_to, edge_purpose, input, status, started_at, created_at, updated_at)
|
|
195
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'running', ?, ?, ?)`,
|
|
192
196
|
).run(id, request.runId, request.tenantId, request.stepType, request.stepName,
|
|
197
|
+
request.threadId || null,
|
|
193
198
|
request.edgeFrom || null, request.edgeTo || null, request.edgePurpose || null,
|
|
194
199
|
request.input ? JSON.stringify(request.input) : null, now, now, now);
|
|
195
200
|
|
|
196
201
|
return {
|
|
197
202
|
id, runId: request.runId, tenantId: request.tenantId,
|
|
198
203
|
stepType: request.stepType, stepName: request.stepName,
|
|
204
|
+
threadId: request.threadId,
|
|
199
205
|
edgeFrom: request.edgeFrom, edgeTo: request.edgeTo, edgePurpose: request.edgePurpose,
|
|
200
206
|
input: request.input, status: 'running', startedAt: parseISO(now),
|
|
201
207
|
createdAt: parseISO(now), updatedAt: parseISO(now),
|
|
@@ -207,7 +213,16 @@ export class LocalWorkflowTrackingStore implements WorkflowTrackingStore {
|
|
|
207
213
|
const row = this.db.prepare(
|
|
208
214
|
`SELECT * FROM lt_workflow_steps WHERE run_id = ? AND step_type = ? AND step_name = ?`,
|
|
209
215
|
).get(request.runId, request.stepType, request.stepName) as unknown as StepRow | undefined;
|
|
210
|
-
if (row)
|
|
216
|
+
if (row) {
|
|
217
|
+
const existing = mapRowToStep(row);
|
|
218
|
+
if (!existing.threadId && request.threadId) {
|
|
219
|
+
this.db.prepare(
|
|
220
|
+
`UPDATE lt_workflow_steps SET thread_id = ?, updated_at = ? WHERE run_id = ? AND id = ?`,
|
|
221
|
+
).run(request.threadId, nowISO(), request.runId, existing.id);
|
|
222
|
+
existing.threadId = request.threadId;
|
|
223
|
+
}
|
|
224
|
+
return existing;
|
|
225
|
+
}
|
|
211
226
|
return this.createRunStep(request);
|
|
212
227
|
}
|
|
213
228
|
|
|
@@ -292,6 +307,7 @@ function mapRowToStep(row: StepRow): RunStep {
|
|
|
292
307
|
tenantId: row.tenant_id,
|
|
293
308
|
stepType: row.step_type as StepType,
|
|
294
309
|
stepName: row.step_name,
|
|
310
|
+
threadId: row.thread_id,
|
|
295
311
|
edgeFrom: row.edge_from || undefined,
|
|
296
312
|
edgeTo: row.edge_to || undefined,
|
|
297
313
|
edgePurpose: row.edge_purpose || undefined,
|