@desplega.ai/agent-swarm 1.52.1 → 1.53.0
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/openapi.json +1517 -488
- package/package.json +5 -2
- package/src/be/db.ts +530 -0
- package/src/be/events.ts +322 -0
- package/src/be/migrations/021_events.sql +24 -0
- package/src/be/migrations/022_context_usage.sql +34 -0
- package/src/be/migrations/023_mcp_servers.sql +44 -0
- package/src/commands/runner.ts +348 -1
- package/src/http/context.ts +118 -0
- package/src/http/events.ts +188 -0
- package/src/http/index.ts +6 -0
- package/src/http/mcp-servers.ts +364 -0
- package/src/http/tasks.ts +33 -0
- package/src/linear/outbound.ts +8 -1
- package/src/linear/sync.ts +3 -0
- package/src/oauth/ensure-token.ts +50 -0
- package/src/prompts/base-prompt.ts +7 -0
- package/src/providers/claude-adapter.ts +156 -15
- package/src/providers/pi-mono-adapter.ts +68 -0
- package/src/providers/pi-mono-extension.ts +56 -2
- package/src/providers/pi-mono-mcp-client.ts +10 -1
- package/src/providers/types.ts +14 -1
- package/src/server.ts +19 -0
- package/src/tests/context-window.test.ts +66 -0
- package/src/tests/ensure-token.test.ts +170 -0
- package/src/tests/events-db.test.ts +314 -0
- package/src/tests/events-http.test.ts +267 -0
- package/src/tests/prompt-template-remaining.test.ts +5 -5
- package/src/tests/tool-annotations.test.ts +2 -2
- package/src/tests/vcs-tracking.test.ts +176 -0
- package/src/tests/workflow-executors.test.ts +8 -1
- package/src/tools/mcp-servers/index.ts +7 -0
- package/src/tools/mcp-servers/mcp-server-create.ts +138 -0
- package/src/tools/mcp-servers/mcp-server-delete.ts +72 -0
- package/src/tools/mcp-servers/mcp-server-get.ts +80 -0
- package/src/tools/mcp-servers/mcp-server-install.ts +110 -0
- package/src/tools/mcp-servers/mcp-server-list.ts +67 -0
- package/src/tools/mcp-servers/mcp-server-uninstall.ts +71 -0
- package/src/tools/mcp-servers/mcp-server-update.ts +120 -0
- package/src/tools/tool-config.ts +9 -0
- package/src/types.ts +153 -0
- package/src/utils/context-window.ts +41 -0
- package/src/workflows/executors/base.ts +9 -1
package/src/be/events.ts
ADDED
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
import type { EventCategory, EventName, EventSource, EventStatus, SwarmEvent } from "../types";
|
|
2
|
+
import { getDb } from "./db";
|
|
3
|
+
|
|
4
|
+
// -- Events --
|
|
5
|
+
|
|
6
|
+
type EventRow = {
|
|
7
|
+
id: string;
|
|
8
|
+
category: string;
|
|
9
|
+
event: string;
|
|
10
|
+
status: string;
|
|
11
|
+
source: string;
|
|
12
|
+
agentId: string | null;
|
|
13
|
+
taskId: string | null;
|
|
14
|
+
sessionId: string | null;
|
|
15
|
+
parentEventId: string | null;
|
|
16
|
+
numericValue: number | null;
|
|
17
|
+
durationMs: number | null;
|
|
18
|
+
data: string | null;
|
|
19
|
+
createdAt: string;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
function rowToSwarmEvent(row: EventRow): SwarmEvent {
|
|
23
|
+
return {
|
|
24
|
+
id: row.id,
|
|
25
|
+
category: row.category as EventCategory,
|
|
26
|
+
event: row.event as EventName,
|
|
27
|
+
status: row.status as EventStatus,
|
|
28
|
+
source: row.source as EventSource,
|
|
29
|
+
agentId: row.agentId ?? undefined,
|
|
30
|
+
taskId: row.taskId ?? undefined,
|
|
31
|
+
sessionId: row.sessionId ?? undefined,
|
|
32
|
+
parentEventId: row.parentEventId ?? undefined,
|
|
33
|
+
numericValue: row.numericValue ?? undefined,
|
|
34
|
+
durationMs: row.durationMs ?? undefined,
|
|
35
|
+
data: row.data ? JSON.parse(row.data) : undefined,
|
|
36
|
+
createdAt: row.createdAt,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const eventQueries = {
|
|
41
|
+
insert: () =>
|
|
42
|
+
getDb().prepare<
|
|
43
|
+
null,
|
|
44
|
+
[
|
|
45
|
+
string,
|
|
46
|
+
string,
|
|
47
|
+
string,
|
|
48
|
+
string,
|
|
49
|
+
string,
|
|
50
|
+
string | null,
|
|
51
|
+
string | null,
|
|
52
|
+
string | null,
|
|
53
|
+
string | null,
|
|
54
|
+
number | null,
|
|
55
|
+
number | null,
|
|
56
|
+
string | null,
|
|
57
|
+
]
|
|
58
|
+
>(
|
|
59
|
+
`INSERT INTO events (id, category, event, status, source, agentId, taskId,
|
|
60
|
+
sessionId, parentEventId, numericValue, durationMs, data, createdAt)
|
|
61
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))`,
|
|
62
|
+
),
|
|
63
|
+
|
|
64
|
+
getByCategory: () =>
|
|
65
|
+
getDb().prepare<EventRow, [string, number]>(
|
|
66
|
+
"SELECT * FROM events WHERE category = ? ORDER BY createdAt DESC LIMIT ?",
|
|
67
|
+
),
|
|
68
|
+
|
|
69
|
+
getByEvent: () =>
|
|
70
|
+
getDb().prepare<EventRow, [string, number]>(
|
|
71
|
+
"SELECT * FROM events WHERE event = ? ORDER BY createdAt DESC LIMIT ?",
|
|
72
|
+
),
|
|
73
|
+
|
|
74
|
+
getByAgentId: () =>
|
|
75
|
+
getDb().prepare<EventRow, [string, number]>(
|
|
76
|
+
"SELECT * FROM events WHERE agentId = ? ORDER BY createdAt DESC LIMIT ?",
|
|
77
|
+
),
|
|
78
|
+
|
|
79
|
+
getByTaskId: () =>
|
|
80
|
+
getDb().prepare<EventRow, [string, number]>(
|
|
81
|
+
"SELECT * FROM events WHERE taskId = ? ORDER BY createdAt DESC LIMIT ?",
|
|
82
|
+
),
|
|
83
|
+
|
|
84
|
+
getBySessionId: () =>
|
|
85
|
+
getDb().prepare<EventRow, [string, number]>(
|
|
86
|
+
"SELECT * FROM events WHERE sessionId = ? ORDER BY createdAt DESC LIMIT ?",
|
|
87
|
+
),
|
|
88
|
+
|
|
89
|
+
getAll: () =>
|
|
90
|
+
getDb().prepare<EventRow, [number]>("SELECT * FROM events ORDER BY createdAt DESC LIMIT ?"),
|
|
91
|
+
|
|
92
|
+
countByEvent: () =>
|
|
93
|
+
getDb().prepare<{ event: string; count: number }, []>(
|
|
94
|
+
"SELECT event, COUNT(*) as count FROM events GROUP BY event ORDER BY count DESC",
|
|
95
|
+
),
|
|
96
|
+
|
|
97
|
+
countByEventForAgent: () =>
|
|
98
|
+
getDb().prepare<{ event: string; count: number }, [string]>(
|
|
99
|
+
"SELECT event, COUNT(*) as count FROM events WHERE agentId = ? GROUP BY event ORDER BY count DESC",
|
|
100
|
+
),
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
// ─── Create ─────────────────────────────────────────────────────────────────
|
|
104
|
+
|
|
105
|
+
export interface CreateEventInput {
|
|
106
|
+
category: EventCategory;
|
|
107
|
+
event: EventName;
|
|
108
|
+
status?: EventStatus;
|
|
109
|
+
source: EventSource;
|
|
110
|
+
agentId?: string;
|
|
111
|
+
taskId?: string;
|
|
112
|
+
sessionId?: string;
|
|
113
|
+
parentEventId?: string;
|
|
114
|
+
numericValue?: number;
|
|
115
|
+
durationMs?: number;
|
|
116
|
+
data?: Record<string, unknown>;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export function createEvent(input: CreateEventInput): SwarmEvent {
|
|
120
|
+
const id = crypto.randomUUID();
|
|
121
|
+
eventQueries
|
|
122
|
+
.insert()
|
|
123
|
+
.run(
|
|
124
|
+
id,
|
|
125
|
+
input.category,
|
|
126
|
+
input.event,
|
|
127
|
+
input.status ?? "ok",
|
|
128
|
+
input.source,
|
|
129
|
+
input.agentId ?? null,
|
|
130
|
+
input.taskId ?? null,
|
|
131
|
+
input.sessionId ?? null,
|
|
132
|
+
input.parentEventId ?? null,
|
|
133
|
+
input.numericValue ?? null,
|
|
134
|
+
input.durationMs ?? null,
|
|
135
|
+
input.data ? JSON.stringify(input.data) : null,
|
|
136
|
+
);
|
|
137
|
+
return {
|
|
138
|
+
id,
|
|
139
|
+
category: input.category,
|
|
140
|
+
event: input.event,
|
|
141
|
+
status: input.status ?? "ok",
|
|
142
|
+
source: input.source,
|
|
143
|
+
agentId: input.agentId,
|
|
144
|
+
taskId: input.taskId,
|
|
145
|
+
sessionId: input.sessionId,
|
|
146
|
+
parentEventId: input.parentEventId,
|
|
147
|
+
numericValue: input.numericValue,
|
|
148
|
+
durationMs: input.durationMs,
|
|
149
|
+
data: input.data,
|
|
150
|
+
createdAt: new Date().toISOString(),
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export function createEventsBatch(inputs: CreateEventInput[]): number {
|
|
155
|
+
const insert = eventQueries.insert();
|
|
156
|
+
const tx = getDb().transaction(() => {
|
|
157
|
+
for (const input of inputs) {
|
|
158
|
+
const id = crypto.randomUUID();
|
|
159
|
+
insert.run(
|
|
160
|
+
id,
|
|
161
|
+
input.category,
|
|
162
|
+
input.event,
|
|
163
|
+
input.status ?? "ok",
|
|
164
|
+
input.source,
|
|
165
|
+
input.agentId ?? null,
|
|
166
|
+
input.taskId ?? null,
|
|
167
|
+
input.sessionId ?? null,
|
|
168
|
+
input.parentEventId ?? null,
|
|
169
|
+
input.numericValue ?? null,
|
|
170
|
+
input.durationMs ?? null,
|
|
171
|
+
input.data ? JSON.stringify(input.data) : null,
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
tx();
|
|
176
|
+
return inputs.length;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// ─── Query ──────────────────────────────────────────────────────────────────
|
|
180
|
+
|
|
181
|
+
export function getEventsByCategory(category: EventCategory, limit = 100): SwarmEvent[] {
|
|
182
|
+
return eventQueries.getByCategory().all(category, limit).map(rowToSwarmEvent);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export function getEventsByEvent(event: EventName, limit = 100): SwarmEvent[] {
|
|
186
|
+
return eventQueries.getByEvent().all(event, limit).map(rowToSwarmEvent);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export function getEventsByAgentId(agentId: string, limit = 100): SwarmEvent[] {
|
|
190
|
+
return eventQueries.getByAgentId().all(agentId, limit).map(rowToSwarmEvent);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export function getEventsByTaskId(taskId: string, limit = 100): SwarmEvent[] {
|
|
194
|
+
return eventQueries.getByTaskId().all(taskId, limit).map(rowToSwarmEvent);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export function getEventsBySessionId(sessionId: string, limit = 100): SwarmEvent[] {
|
|
198
|
+
return eventQueries.getBySessionId().all(sessionId, limit).map(rowToSwarmEvent);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export function getAllEvents(limit = 100): SwarmEvent[] {
|
|
202
|
+
return eventQueries.getAll().all(limit).map(rowToSwarmEvent);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export function getEventCounts(): Array<{ event: string; count: number }> {
|
|
206
|
+
return eventQueries.countByEvent().all();
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
export function getEventCountsForAgent(agentId: string): Array<{ event: string; count: number }> {
|
|
210
|
+
return eventQueries.countByEventForAgent().all(agentId);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export function getEventCountsFiltered(filters: {
|
|
214
|
+
category?: EventCategory;
|
|
215
|
+
source?: EventSource;
|
|
216
|
+
agentId?: string;
|
|
217
|
+
taskId?: string;
|
|
218
|
+
sessionId?: string;
|
|
219
|
+
since?: string;
|
|
220
|
+
until?: string;
|
|
221
|
+
}): Array<{ event: string; count: number }> {
|
|
222
|
+
const conditions: string[] = [];
|
|
223
|
+
const params: (string | number)[] = [];
|
|
224
|
+
|
|
225
|
+
if (filters.category) {
|
|
226
|
+
conditions.push("category = ?");
|
|
227
|
+
params.push(filters.category);
|
|
228
|
+
}
|
|
229
|
+
if (filters.source) {
|
|
230
|
+
conditions.push("source = ?");
|
|
231
|
+
params.push(filters.source);
|
|
232
|
+
}
|
|
233
|
+
if (filters.agentId) {
|
|
234
|
+
conditions.push("agentId = ?");
|
|
235
|
+
params.push(filters.agentId);
|
|
236
|
+
}
|
|
237
|
+
if (filters.taskId) {
|
|
238
|
+
conditions.push("taskId = ?");
|
|
239
|
+
params.push(filters.taskId);
|
|
240
|
+
}
|
|
241
|
+
if (filters.sessionId) {
|
|
242
|
+
conditions.push("sessionId = ?");
|
|
243
|
+
params.push(filters.sessionId);
|
|
244
|
+
}
|
|
245
|
+
if (filters.since) {
|
|
246
|
+
conditions.push("createdAt >= ?");
|
|
247
|
+
params.push(filters.since);
|
|
248
|
+
}
|
|
249
|
+
if (filters.until) {
|
|
250
|
+
conditions.push("createdAt <= ?");
|
|
251
|
+
params.push(filters.until);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
255
|
+
const sql = `SELECT event, COUNT(*) as count FROM events ${where} GROUP BY event ORDER BY count DESC`;
|
|
256
|
+
return getDb()
|
|
257
|
+
.prepare<{ event: string; count: number }, (string | number)[]>(sql)
|
|
258
|
+
.all(...params);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
export function getEventsFiltered(filters: {
|
|
262
|
+
category?: EventCategory;
|
|
263
|
+
event?: EventName;
|
|
264
|
+
status?: EventStatus;
|
|
265
|
+
source?: EventSource;
|
|
266
|
+
agentId?: string;
|
|
267
|
+
taskId?: string;
|
|
268
|
+
sessionId?: string;
|
|
269
|
+
since?: string;
|
|
270
|
+
until?: string;
|
|
271
|
+
limit?: number;
|
|
272
|
+
}): SwarmEvent[] {
|
|
273
|
+
const conditions: string[] = [];
|
|
274
|
+
const params: (string | number)[] = [];
|
|
275
|
+
|
|
276
|
+
if (filters.category) {
|
|
277
|
+
conditions.push("category = ?");
|
|
278
|
+
params.push(filters.category);
|
|
279
|
+
}
|
|
280
|
+
if (filters.event) {
|
|
281
|
+
conditions.push("event = ?");
|
|
282
|
+
params.push(filters.event);
|
|
283
|
+
}
|
|
284
|
+
if (filters.status) {
|
|
285
|
+
conditions.push("status = ?");
|
|
286
|
+
params.push(filters.status);
|
|
287
|
+
}
|
|
288
|
+
if (filters.source) {
|
|
289
|
+
conditions.push("source = ?");
|
|
290
|
+
params.push(filters.source);
|
|
291
|
+
}
|
|
292
|
+
if (filters.agentId) {
|
|
293
|
+
conditions.push("agentId = ?");
|
|
294
|
+
params.push(filters.agentId);
|
|
295
|
+
}
|
|
296
|
+
if (filters.taskId) {
|
|
297
|
+
conditions.push("taskId = ?");
|
|
298
|
+
params.push(filters.taskId);
|
|
299
|
+
}
|
|
300
|
+
if (filters.sessionId) {
|
|
301
|
+
conditions.push("sessionId = ?");
|
|
302
|
+
params.push(filters.sessionId);
|
|
303
|
+
}
|
|
304
|
+
if (filters.since) {
|
|
305
|
+
conditions.push("createdAt >= ?");
|
|
306
|
+
params.push(filters.since);
|
|
307
|
+
}
|
|
308
|
+
if (filters.until) {
|
|
309
|
+
conditions.push("createdAt <= ?");
|
|
310
|
+
params.push(filters.until);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
314
|
+
const limit = filters.limit ?? 100;
|
|
315
|
+
params.push(limit);
|
|
316
|
+
|
|
317
|
+
const sql = `SELECT * FROM events ${where} ORDER BY createdAt DESC LIMIT ?`;
|
|
318
|
+
return getDb()
|
|
319
|
+
.prepare<EventRow, (string | number)[]>(sql)
|
|
320
|
+
.all(...params)
|
|
321
|
+
.map(rowToSwarmEvent);
|
|
322
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
CREATE TABLE IF NOT EXISTS events (
|
|
2
|
+
id TEXT PRIMARY KEY,
|
|
3
|
+
category TEXT NOT NULL,
|
|
4
|
+
event TEXT NOT NULL,
|
|
5
|
+
status TEXT NOT NULL DEFAULT 'ok',
|
|
6
|
+
source TEXT NOT NULL,
|
|
7
|
+
agentId TEXT,
|
|
8
|
+
taskId TEXT,
|
|
9
|
+
sessionId TEXT,
|
|
10
|
+
parentEventId TEXT,
|
|
11
|
+
numericValue REAL,
|
|
12
|
+
durationMs INTEGER,
|
|
13
|
+
data TEXT,
|
|
14
|
+
createdAt TEXT NOT NULL
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
CREATE INDEX IF NOT EXISTS idx_events_category ON events(category);
|
|
18
|
+
CREATE INDEX IF NOT EXISTS idx_events_event ON events(event);
|
|
19
|
+
CREATE INDEX IF NOT EXISTS idx_events_status ON events(status);
|
|
20
|
+
CREATE INDEX IF NOT EXISTS idx_events_source ON events(source);
|
|
21
|
+
CREATE INDEX IF NOT EXISTS idx_events_agentId ON events(agentId);
|
|
22
|
+
CREATE INDEX IF NOT EXISTS idx_events_taskId ON events(taskId);
|
|
23
|
+
CREATE INDEX IF NOT EXISTS idx_events_sessionId ON events(sessionId);
|
|
24
|
+
CREATE INDEX IF NOT EXISTS idx_events_createdAt ON events(createdAt);
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
-- Progressive context usage snapshots
|
|
2
|
+
CREATE TABLE IF NOT EXISTS task_context_snapshots (
|
|
3
|
+
id TEXT PRIMARY KEY,
|
|
4
|
+
taskId TEXT NOT NULL REFERENCES agent_tasks(id) ON DELETE CASCADE,
|
|
5
|
+
agentId TEXT NOT NULL REFERENCES agents(id) ON DELETE CASCADE,
|
|
6
|
+
sessionId TEXT NOT NULL,
|
|
7
|
+
|
|
8
|
+
-- Context window state
|
|
9
|
+
contextUsedTokens INTEGER,
|
|
10
|
+
contextTotalTokens INTEGER,
|
|
11
|
+
contextPercent REAL,
|
|
12
|
+
|
|
13
|
+
-- Event metadata
|
|
14
|
+
eventType TEXT NOT NULL CHECK (eventType IN ('progress', 'compaction', 'completion')),
|
|
15
|
+
|
|
16
|
+
-- Compaction-specific (NULL for non-compaction)
|
|
17
|
+
compactTrigger TEXT CHECK (compactTrigger IN ('auto', 'manual') OR compactTrigger IS NULL),
|
|
18
|
+
preCompactTokens INTEGER,
|
|
19
|
+
|
|
20
|
+
-- Cumulative counters at this point
|
|
21
|
+
cumulativeInputTokens INTEGER DEFAULT 0,
|
|
22
|
+
cumulativeOutputTokens INTEGER DEFAULT 0,
|
|
23
|
+
|
|
24
|
+
createdAt TEXT NOT NULL
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
CREATE INDEX IF NOT EXISTS idx_context_snapshots_task ON task_context_snapshots(taskId);
|
|
28
|
+
CREATE INDEX IF NOT EXISTS idx_context_snapshots_session ON task_context_snapshots(sessionId);
|
|
29
|
+
|
|
30
|
+
-- Aggregate columns on agent_tasks
|
|
31
|
+
ALTER TABLE agent_tasks ADD COLUMN compactionCount INTEGER DEFAULT 0;
|
|
32
|
+
ALTER TABLE agent_tasks ADD COLUMN peakContextPercent REAL;
|
|
33
|
+
ALTER TABLE agent_tasks ADD COLUMN totalContextTokensUsed INTEGER;
|
|
34
|
+
ALTER TABLE agent_tasks ADD COLUMN contextWindowSize INTEGER;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
-- MCP server definitions
|
|
2
|
+
CREATE TABLE IF NOT EXISTS mcp_servers (
|
|
3
|
+
id TEXT PRIMARY KEY,
|
|
4
|
+
name TEXT NOT NULL,
|
|
5
|
+
description TEXT,
|
|
6
|
+
scope TEXT NOT NULL CHECK(scope IN ('global', 'swarm', 'agent')),
|
|
7
|
+
ownerAgentId TEXT REFERENCES agents(id),
|
|
8
|
+
transport TEXT NOT NULL CHECK(transport IN ('stdio', 'http', 'sse')),
|
|
9
|
+
-- Stdio fields
|
|
10
|
+
command TEXT,
|
|
11
|
+
args TEXT, -- JSON array string
|
|
12
|
+
-- HTTP/SSE fields
|
|
13
|
+
url TEXT,
|
|
14
|
+
headers TEXT, -- JSON object string (non-secret headers only)
|
|
15
|
+
-- Secret references (keys in swarm_config, NOT actual values)
|
|
16
|
+
envConfigKeys TEXT, -- JSON object: {"ENV_VAR": "config-key-name"}
|
|
17
|
+
headerConfigKeys TEXT, -- JSON object: {"Header-Name": "config-key-name"}
|
|
18
|
+
-- Metadata
|
|
19
|
+
isEnabled INTEGER NOT NULL DEFAULT 1,
|
|
20
|
+
version INTEGER NOT NULL DEFAULT 1,
|
|
21
|
+
createdAt TEXT NOT NULL,
|
|
22
|
+
lastUpdatedAt TEXT NOT NULL
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
-- Unique constraint: name must be unique within scope+owner combination
|
|
26
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_mcp_servers_name_scope
|
|
27
|
+
ON mcp_servers(name, scope, COALESCE(ownerAgentId, ''));
|
|
28
|
+
|
|
29
|
+
CREATE INDEX IF NOT EXISTS idx_mcp_servers_scope ON mcp_servers(scope);
|
|
30
|
+
CREATE INDEX IF NOT EXISTS idx_mcp_servers_owner ON mcp_servers(ownerAgentId);
|
|
31
|
+
CREATE INDEX IF NOT EXISTS idx_mcp_servers_transport ON mcp_servers(transport);
|
|
32
|
+
|
|
33
|
+
-- Per-agent MCP server installation
|
|
34
|
+
CREATE TABLE IF NOT EXISTS agent_mcp_servers (
|
|
35
|
+
id TEXT PRIMARY KEY,
|
|
36
|
+
agentId TEXT NOT NULL REFERENCES agents(id),
|
|
37
|
+
mcpServerId TEXT NOT NULL REFERENCES mcp_servers(id) ON DELETE CASCADE,
|
|
38
|
+
isActive INTEGER NOT NULL DEFAULT 1,
|
|
39
|
+
installedAt TEXT NOT NULL,
|
|
40
|
+
UNIQUE(agentId, mcpServerId)
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
CREATE INDEX IF NOT EXISTS idx_agent_mcp_servers_agent ON agent_mcp_servers(agentId);
|
|
44
|
+
CREATE INDEX IF NOT EXISTS idx_agent_mcp_servers_server ON agent_mcp_servers(mcpServerId);
|