@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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@desplega.ai/agent-swarm",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.53.0",
|
|
4
4
|
"description": "Multi-agent orchestration for Claude Code, Codex, Gemini CLI, and other AI coding assistants",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "desplega.sh <contact@desplega.sh>",
|
|
@@ -79,10 +79,13 @@
|
|
|
79
79
|
"pm2-stop": "pm2 stop ecosystem.config.cjs",
|
|
80
80
|
"pm2-restart": "pm2 restart ecosystem.config.cjs",
|
|
81
81
|
"pm2-logs": "pm2 logs swarm-api swarm-ui swarm-lead swarm-worker",
|
|
82
|
-
"pm2-status": "pm2 status swarm-api swarm-ui swarm-lead swarm-worker"
|
|
82
|
+
"pm2-status": "pm2 status swarm-api swarm-ui swarm-lead swarm-worker",
|
|
83
|
+
"seed": "bun scripts/seed.ts",
|
|
84
|
+
"seed:clean": "bun scripts/seed.ts --clean"
|
|
83
85
|
},
|
|
84
86
|
"devDependencies": {
|
|
85
87
|
"@biomejs/biome": "^2.3.10",
|
|
88
|
+
"@faker-js/faker": "^10.4.0",
|
|
86
89
|
"@types/bun": "latest"
|
|
87
90
|
},
|
|
88
91
|
"peerDependencies": {
|
package/src/be/db.ts
CHANGED
|
@@ -5,6 +5,7 @@ import type {
|
|
|
5
5
|
Agent,
|
|
6
6
|
AgentLog,
|
|
7
7
|
AgentLogEventType,
|
|
8
|
+
AgentMcpServer,
|
|
8
9
|
AgentMemory,
|
|
9
10
|
AgentMemoryScope,
|
|
10
11
|
AgentMemorySource,
|
|
@@ -18,6 +19,8 @@ import type {
|
|
|
18
19
|
Channel,
|
|
19
20
|
ChannelMessage,
|
|
20
21
|
ChannelType,
|
|
22
|
+
ContextSnapshot,
|
|
23
|
+
ContextSnapshotEventType,
|
|
21
24
|
ContextVersion,
|
|
22
25
|
CooldownConfig,
|
|
23
26
|
Epic,
|
|
@@ -26,6 +29,10 @@ import type {
|
|
|
26
29
|
InboxMessage,
|
|
27
30
|
InboxMessageStatus,
|
|
28
31
|
InputValue,
|
|
32
|
+
McpServer,
|
|
33
|
+
McpServerScope,
|
|
34
|
+
McpServerTransport,
|
|
35
|
+
McpServerWithInstallInfo,
|
|
29
36
|
PromptTemplate,
|
|
30
37
|
PromptTemplateHistory,
|
|
31
38
|
ScheduledTask,
|
|
@@ -717,6 +724,10 @@ type AgentTaskRow = {
|
|
|
717
724
|
failureReason: string | null;
|
|
718
725
|
output: string | null;
|
|
719
726
|
progress: string | null;
|
|
727
|
+
compactionCount: number | null;
|
|
728
|
+
peakContextPercent: number | null;
|
|
729
|
+
totalContextTokensUsed: number | null;
|
|
730
|
+
contextWindowSize: number | null;
|
|
720
731
|
};
|
|
721
732
|
|
|
722
733
|
function rowToAgentTask(row: AgentTaskRow): AgentTask {
|
|
@@ -759,6 +770,10 @@ function rowToAgentTask(row: AgentTaskRow): AgentTask {
|
|
|
759
770
|
workflowRunId: row.workflowRunId ?? undefined,
|
|
760
771
|
workflowRunStepId: row.workflowRunStepId ?? undefined,
|
|
761
772
|
outputSchema: row.outputSchema ? JSON.parse(row.outputSchema) : undefined,
|
|
773
|
+
compactionCount: row.compactionCount ?? undefined,
|
|
774
|
+
peakContextPercent: row.peakContextPercent ?? undefined,
|
|
775
|
+
totalContextTokensUsed: row.totalContextTokensUsed ?? undefined,
|
|
776
|
+
contextWindowSize: row.contextWindowSize ?? undefined,
|
|
762
777
|
createdAt: row.createdAt,
|
|
763
778
|
lastUpdatedAt: row.lastUpdatedAt,
|
|
764
779
|
finishedAt: row.finishedAt ?? undefined,
|
|
@@ -936,6 +951,25 @@ export function updateTaskClaudeSessionId(
|
|
|
936
951
|
return row ? rowToAgentTask(row) : null;
|
|
937
952
|
}
|
|
938
953
|
|
|
954
|
+
export function updateTaskVcs(
|
|
955
|
+
taskId: string,
|
|
956
|
+
vcs: {
|
|
957
|
+
vcsProvider: "github" | "gitlab";
|
|
958
|
+
vcsRepo: string;
|
|
959
|
+
vcsNumber: number;
|
|
960
|
+
vcsUrl: string;
|
|
961
|
+
},
|
|
962
|
+
): AgentTask | null {
|
|
963
|
+
const row = getDb()
|
|
964
|
+
.prepare<AgentTaskRow, [string, string, number, string, string, string]>(
|
|
965
|
+
`UPDATE agent_tasks
|
|
966
|
+
SET vcsProvider = ?, vcsRepo = ?, vcsNumber = ?, vcsUrl = ?, lastUpdatedAt = ?
|
|
967
|
+
WHERE id = ? RETURNING *`,
|
|
968
|
+
)
|
|
969
|
+
.get(vcs.vcsProvider, vcs.vcsRepo, vcs.vcsNumber, vcs.vcsUrl, new Date().toISOString(), taskId);
|
|
970
|
+
return row ? rowToAgentTask(row) : null;
|
|
971
|
+
}
|
|
972
|
+
|
|
939
973
|
export function getTasksByAgentId(agentId: string): AgentTask[] {
|
|
940
974
|
return taskQueries.getByAgentId().all(agentId).map(rowToAgentTask);
|
|
941
975
|
}
|
|
@@ -7391,3 +7425,499 @@ export function toggleAgentSkill(agentId: string, skillId: string, isActive: boo
|
|
|
7391
7425
|
.run(isActive ? 1 : 0, agentId, skillId);
|
|
7392
7426
|
return result.changes > 0;
|
|
7393
7427
|
}
|
|
7428
|
+
|
|
7429
|
+
// ── MCP Servers ──────────────────────────────────────────────────────────
|
|
7430
|
+
|
|
7431
|
+
type McpServerRow = {
|
|
7432
|
+
id: string;
|
|
7433
|
+
name: string;
|
|
7434
|
+
description: string | null;
|
|
7435
|
+
scope: string;
|
|
7436
|
+
ownerAgentId: string | null;
|
|
7437
|
+
transport: string;
|
|
7438
|
+
command: string | null;
|
|
7439
|
+
args: string | null;
|
|
7440
|
+
url: string | null;
|
|
7441
|
+
headers: string | null;
|
|
7442
|
+
envConfigKeys: string | null;
|
|
7443
|
+
headerConfigKeys: string | null;
|
|
7444
|
+
isEnabled: number;
|
|
7445
|
+
version: number;
|
|
7446
|
+
createdAt: string;
|
|
7447
|
+
lastUpdatedAt: string;
|
|
7448
|
+
};
|
|
7449
|
+
|
|
7450
|
+
type AgentMcpServerRow = {
|
|
7451
|
+
id: string;
|
|
7452
|
+
agentId: string;
|
|
7453
|
+
mcpServerId: string;
|
|
7454
|
+
isActive: number;
|
|
7455
|
+
installedAt: string;
|
|
7456
|
+
};
|
|
7457
|
+
|
|
7458
|
+
type McpServerWithInstallRow = McpServerRow & { isActive: number; installedAt: string };
|
|
7459
|
+
|
|
7460
|
+
function rowToMcpServer(row: McpServerRow): McpServer {
|
|
7461
|
+
return {
|
|
7462
|
+
id: row.id,
|
|
7463
|
+
name: row.name,
|
|
7464
|
+
description: row.description,
|
|
7465
|
+
scope: row.scope as McpServerScope,
|
|
7466
|
+
ownerAgentId: row.ownerAgentId,
|
|
7467
|
+
transport: row.transport as McpServerTransport,
|
|
7468
|
+
command: row.command,
|
|
7469
|
+
args: row.args,
|
|
7470
|
+
url: row.url,
|
|
7471
|
+
headers: row.headers,
|
|
7472
|
+
envConfigKeys: row.envConfigKeys,
|
|
7473
|
+
headerConfigKeys: row.headerConfigKeys,
|
|
7474
|
+
isEnabled: row.isEnabled === 1,
|
|
7475
|
+
version: row.version,
|
|
7476
|
+
createdAt: row.createdAt,
|
|
7477
|
+
lastUpdatedAt: row.lastUpdatedAt,
|
|
7478
|
+
};
|
|
7479
|
+
}
|
|
7480
|
+
|
|
7481
|
+
function rowToAgentMcpServer(row: AgentMcpServerRow): AgentMcpServer {
|
|
7482
|
+
return {
|
|
7483
|
+
id: row.id,
|
|
7484
|
+
agentId: row.agentId,
|
|
7485
|
+
mcpServerId: row.mcpServerId,
|
|
7486
|
+
isActive: row.isActive === 1,
|
|
7487
|
+
installedAt: row.installedAt,
|
|
7488
|
+
};
|
|
7489
|
+
}
|
|
7490
|
+
|
|
7491
|
+
function rowToMcpServerWithInstall(row: McpServerWithInstallRow): McpServerWithInstallInfo {
|
|
7492
|
+
return {
|
|
7493
|
+
...rowToMcpServer(row),
|
|
7494
|
+
isActive: row.isActive === 1,
|
|
7495
|
+
installedAt: row.installedAt,
|
|
7496
|
+
};
|
|
7497
|
+
}
|
|
7498
|
+
|
|
7499
|
+
export interface McpServerInsert {
|
|
7500
|
+
name: string;
|
|
7501
|
+
transport: McpServerTransport;
|
|
7502
|
+
description?: string;
|
|
7503
|
+
scope?: McpServerScope;
|
|
7504
|
+
ownerAgentId?: string;
|
|
7505
|
+
command?: string;
|
|
7506
|
+
args?: string;
|
|
7507
|
+
url?: string;
|
|
7508
|
+
headers?: string;
|
|
7509
|
+
envConfigKeys?: string;
|
|
7510
|
+
headerConfigKeys?: string;
|
|
7511
|
+
}
|
|
7512
|
+
|
|
7513
|
+
export function createMcpServer(data: McpServerInsert): McpServer {
|
|
7514
|
+
const id = crypto.randomUUID();
|
|
7515
|
+
const now = new Date().toISOString();
|
|
7516
|
+
|
|
7517
|
+
const row = getDb()
|
|
7518
|
+
.prepare<McpServerRow, (string | number | null)[]>(
|
|
7519
|
+
`INSERT INTO mcp_servers (
|
|
7520
|
+
id, name, description, scope, ownerAgentId, transport,
|
|
7521
|
+
command, args, url, headers,
|
|
7522
|
+
envConfigKeys, headerConfigKeys,
|
|
7523
|
+
isEnabled, version, createdAt, lastUpdatedAt
|
|
7524
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, 1, ?, ?) RETURNING *`,
|
|
7525
|
+
)
|
|
7526
|
+
.get(
|
|
7527
|
+
id,
|
|
7528
|
+
data.name,
|
|
7529
|
+
data.description ?? null,
|
|
7530
|
+
data.scope ?? "agent",
|
|
7531
|
+
data.ownerAgentId ?? null,
|
|
7532
|
+
data.transport,
|
|
7533
|
+
data.command ?? null,
|
|
7534
|
+
data.args ?? null,
|
|
7535
|
+
data.url ?? null,
|
|
7536
|
+
data.headers ?? null,
|
|
7537
|
+
data.envConfigKeys ?? null,
|
|
7538
|
+
data.headerConfigKeys ?? null,
|
|
7539
|
+
now,
|
|
7540
|
+
now,
|
|
7541
|
+
);
|
|
7542
|
+
|
|
7543
|
+
if (!row) throw new Error("Failed to create MCP server");
|
|
7544
|
+
return rowToMcpServer(row);
|
|
7545
|
+
}
|
|
7546
|
+
|
|
7547
|
+
export function updateMcpServer(
|
|
7548
|
+
id: string,
|
|
7549
|
+
updates: Partial<McpServerInsert> & { isEnabled?: boolean },
|
|
7550
|
+
): McpServer | null {
|
|
7551
|
+
const existing = getMcpServerById(id);
|
|
7552
|
+
if (!existing) return null;
|
|
7553
|
+
|
|
7554
|
+
const now = new Date().toISOString();
|
|
7555
|
+
const sets: string[] = ["lastUpdatedAt = ?"];
|
|
7556
|
+
const params: (string | number | null)[] = [now];
|
|
7557
|
+
|
|
7558
|
+
if (updates.name !== undefined) {
|
|
7559
|
+
sets.push("name = ?");
|
|
7560
|
+
params.push(updates.name);
|
|
7561
|
+
}
|
|
7562
|
+
if (updates.description !== undefined) {
|
|
7563
|
+
sets.push("description = ?");
|
|
7564
|
+
params.push(updates.description ?? null);
|
|
7565
|
+
}
|
|
7566
|
+
if (updates.scope !== undefined) {
|
|
7567
|
+
sets.push("scope = ?");
|
|
7568
|
+
params.push(updates.scope);
|
|
7569
|
+
}
|
|
7570
|
+
if (updates.transport !== undefined) {
|
|
7571
|
+
sets.push("transport = ?");
|
|
7572
|
+
params.push(updates.transport);
|
|
7573
|
+
}
|
|
7574
|
+
if (updates.command !== undefined) {
|
|
7575
|
+
sets.push("command = ?");
|
|
7576
|
+
params.push(updates.command ?? null);
|
|
7577
|
+
}
|
|
7578
|
+
if (updates.args !== undefined) {
|
|
7579
|
+
sets.push("args = ?");
|
|
7580
|
+
params.push(updates.args ?? null);
|
|
7581
|
+
}
|
|
7582
|
+
if (updates.url !== undefined) {
|
|
7583
|
+
sets.push("url = ?");
|
|
7584
|
+
params.push(updates.url ?? null);
|
|
7585
|
+
}
|
|
7586
|
+
if (updates.headers !== undefined) {
|
|
7587
|
+
sets.push("headers = ?");
|
|
7588
|
+
params.push(updates.headers ?? null);
|
|
7589
|
+
}
|
|
7590
|
+
if (updates.envConfigKeys !== undefined) {
|
|
7591
|
+
sets.push("envConfigKeys = ?");
|
|
7592
|
+
params.push(updates.envConfigKeys ?? null);
|
|
7593
|
+
}
|
|
7594
|
+
if (updates.headerConfigKeys !== undefined) {
|
|
7595
|
+
sets.push("headerConfigKeys = ?");
|
|
7596
|
+
params.push(updates.headerConfigKeys ?? null);
|
|
7597
|
+
}
|
|
7598
|
+
if (updates.isEnabled !== undefined) {
|
|
7599
|
+
sets.push("isEnabled = ?");
|
|
7600
|
+
params.push(updates.isEnabled ? 1 : 0);
|
|
7601
|
+
}
|
|
7602
|
+
if (updates.ownerAgentId !== undefined) {
|
|
7603
|
+
sets.push("ownerAgentId = ?");
|
|
7604
|
+
params.push(updates.ownerAgentId ?? null);
|
|
7605
|
+
}
|
|
7606
|
+
|
|
7607
|
+
// Bump version on config changes
|
|
7608
|
+
const configFields = [
|
|
7609
|
+
"command",
|
|
7610
|
+
"args",
|
|
7611
|
+
"url",
|
|
7612
|
+
"headers",
|
|
7613
|
+
"envConfigKeys",
|
|
7614
|
+
"headerConfigKeys",
|
|
7615
|
+
"transport",
|
|
7616
|
+
];
|
|
7617
|
+
if (configFields.some((f) => (updates as Record<string, unknown>)[f] !== undefined)) {
|
|
7618
|
+
sets.push("version = version + 1");
|
|
7619
|
+
}
|
|
7620
|
+
|
|
7621
|
+
params.push(id);
|
|
7622
|
+
const row = getDb()
|
|
7623
|
+
.prepare<McpServerRow, (string | number | null)[]>(
|
|
7624
|
+
`UPDATE mcp_servers SET ${sets.join(", ")} WHERE id = ? RETURNING *`,
|
|
7625
|
+
)
|
|
7626
|
+
.get(...params);
|
|
7627
|
+
|
|
7628
|
+
return row ? rowToMcpServer(row) : null;
|
|
7629
|
+
}
|
|
7630
|
+
|
|
7631
|
+
export function deleteMcpServer(id: string): boolean {
|
|
7632
|
+
const result = getDb().prepare("DELETE FROM mcp_servers WHERE id = ?").run(id);
|
|
7633
|
+
return result.changes > 0;
|
|
7634
|
+
}
|
|
7635
|
+
|
|
7636
|
+
export function getMcpServerById(id: string): McpServer | null {
|
|
7637
|
+
const row = getDb()
|
|
7638
|
+
.prepare<McpServerRow, [string]>("SELECT * FROM mcp_servers WHERE id = ?")
|
|
7639
|
+
.get(id);
|
|
7640
|
+
return row ? rowToMcpServer(row) : null;
|
|
7641
|
+
}
|
|
7642
|
+
|
|
7643
|
+
export function getMcpServerByName(
|
|
7644
|
+
name: string,
|
|
7645
|
+
scope: McpServerScope,
|
|
7646
|
+
ownerAgentId: string | null,
|
|
7647
|
+
): McpServer | null {
|
|
7648
|
+
const row = getDb()
|
|
7649
|
+
.prepare<McpServerRow, [string, string, string]>(
|
|
7650
|
+
"SELECT * FROM mcp_servers WHERE name = ? AND scope = ? AND COALESCE(ownerAgentId, '') = ?",
|
|
7651
|
+
)
|
|
7652
|
+
.get(name, scope, ownerAgentId ?? "");
|
|
7653
|
+
return row ? rowToMcpServer(row) : null;
|
|
7654
|
+
}
|
|
7655
|
+
|
|
7656
|
+
export interface McpServerFilters {
|
|
7657
|
+
scope?: McpServerScope;
|
|
7658
|
+
ownerAgentId?: string;
|
|
7659
|
+
transport?: McpServerTransport;
|
|
7660
|
+
isEnabled?: boolean;
|
|
7661
|
+
search?: string;
|
|
7662
|
+
}
|
|
7663
|
+
|
|
7664
|
+
export function listMcpServers(filters?: McpServerFilters): McpServer[] {
|
|
7665
|
+
let query = "SELECT * FROM mcp_servers WHERE 1=1";
|
|
7666
|
+
const params: (string | number)[] = [];
|
|
7667
|
+
|
|
7668
|
+
if (filters?.scope) {
|
|
7669
|
+
query += " AND scope = ?";
|
|
7670
|
+
params.push(filters.scope);
|
|
7671
|
+
}
|
|
7672
|
+
if (filters?.ownerAgentId) {
|
|
7673
|
+
query += " AND ownerAgentId = ?";
|
|
7674
|
+
params.push(filters.ownerAgentId);
|
|
7675
|
+
}
|
|
7676
|
+
if (filters?.transport) {
|
|
7677
|
+
query += " AND transport = ?";
|
|
7678
|
+
params.push(filters.transport);
|
|
7679
|
+
}
|
|
7680
|
+
if (filters?.isEnabled !== undefined) {
|
|
7681
|
+
query += " AND isEnabled = ?";
|
|
7682
|
+
params.push(filters.isEnabled ? 1 : 0);
|
|
7683
|
+
}
|
|
7684
|
+
if (filters?.search) {
|
|
7685
|
+
query += " AND (name LIKE ? OR description LIKE ?)";
|
|
7686
|
+
const term = `%${filters.search}%`;
|
|
7687
|
+
params.push(term, term);
|
|
7688
|
+
}
|
|
7689
|
+
|
|
7690
|
+
query += " ORDER BY name ASC";
|
|
7691
|
+
|
|
7692
|
+
return getDb()
|
|
7693
|
+
.prepare<McpServerRow, (string | number)[]>(query)
|
|
7694
|
+
.all(...params)
|
|
7695
|
+
.map(rowToMcpServer);
|
|
7696
|
+
}
|
|
7697
|
+
|
|
7698
|
+
export function installMcpServer(agentId: string, mcpServerId: string): AgentMcpServer {
|
|
7699
|
+
const id = crypto.randomUUID();
|
|
7700
|
+
const now = new Date().toISOString();
|
|
7701
|
+
|
|
7702
|
+
const row = getDb()
|
|
7703
|
+
.prepare<AgentMcpServerRow, [string, string, string, string]>(
|
|
7704
|
+
`INSERT INTO agent_mcp_servers (id, agentId, mcpServerId, isActive, installedAt)
|
|
7705
|
+
VALUES (?, ?, ?, 1, ?)
|
|
7706
|
+
ON CONFLICT(agentId, mcpServerId) DO UPDATE SET isActive = 1
|
|
7707
|
+
RETURNING *`,
|
|
7708
|
+
)
|
|
7709
|
+
.get(id, agentId, mcpServerId, now);
|
|
7710
|
+
|
|
7711
|
+
if (!row) throw new Error("Failed to install MCP server");
|
|
7712
|
+
return rowToAgentMcpServer(row);
|
|
7713
|
+
}
|
|
7714
|
+
|
|
7715
|
+
export function uninstallMcpServer(agentId: string, mcpServerId: string): boolean {
|
|
7716
|
+
const result = getDb()
|
|
7717
|
+
.prepare("DELETE FROM agent_mcp_servers WHERE agentId = ? AND mcpServerId = ?")
|
|
7718
|
+
.run(agentId, mcpServerId);
|
|
7719
|
+
return result.changes > 0;
|
|
7720
|
+
}
|
|
7721
|
+
|
|
7722
|
+
export function getAgentMcpServers(agentId: string, activeOnly = true): McpServerWithInstallInfo[] {
|
|
7723
|
+
const query = `
|
|
7724
|
+
SELECT ms.*, ams.isActive, ams.installedAt
|
|
7725
|
+
FROM mcp_servers ms
|
|
7726
|
+
JOIN agent_mcp_servers ams ON ms.id = ams.mcpServerId
|
|
7727
|
+
WHERE ams.agentId = ?
|
|
7728
|
+
${activeOnly ? "AND ams.isActive = 1" : ""}
|
|
7729
|
+
AND ms.isEnabled = 1
|
|
7730
|
+
ORDER BY ms.name ASC
|
|
7731
|
+
`;
|
|
7732
|
+
|
|
7733
|
+
return getDb()
|
|
7734
|
+
.prepare<McpServerWithInstallRow, [string]>(query)
|
|
7735
|
+
.all(agentId)
|
|
7736
|
+
.map(rowToMcpServerWithInstall);
|
|
7737
|
+
}
|
|
7738
|
+
|
|
7739
|
+
// ============================================================================
|
|
7740
|
+
// Context Usage Snapshots
|
|
7741
|
+
// ============================================================================
|
|
7742
|
+
|
|
7743
|
+
type ContextSnapshotRow = {
|
|
7744
|
+
id: string;
|
|
7745
|
+
taskId: string;
|
|
7746
|
+
agentId: string;
|
|
7747
|
+
sessionId: string;
|
|
7748
|
+
contextUsedTokens: number | null;
|
|
7749
|
+
contextTotalTokens: number | null;
|
|
7750
|
+
contextPercent: number | null;
|
|
7751
|
+
eventType: ContextSnapshotEventType;
|
|
7752
|
+
compactTrigger: string | null;
|
|
7753
|
+
preCompactTokens: number | null;
|
|
7754
|
+
cumulativeInputTokens: number;
|
|
7755
|
+
cumulativeOutputTokens: number;
|
|
7756
|
+
createdAt: string;
|
|
7757
|
+
};
|
|
7758
|
+
|
|
7759
|
+
function rowToContextSnapshot(row: ContextSnapshotRow): ContextSnapshot {
|
|
7760
|
+
return {
|
|
7761
|
+
id: row.id,
|
|
7762
|
+
taskId: row.taskId,
|
|
7763
|
+
agentId: row.agentId,
|
|
7764
|
+
sessionId: row.sessionId,
|
|
7765
|
+
contextUsedTokens: row.contextUsedTokens ?? undefined,
|
|
7766
|
+
contextTotalTokens: row.contextTotalTokens ?? undefined,
|
|
7767
|
+
contextPercent: row.contextPercent ?? undefined,
|
|
7768
|
+
eventType: row.eventType,
|
|
7769
|
+
compactTrigger: (row.compactTrigger as "auto" | "manual" | null) ?? undefined,
|
|
7770
|
+
preCompactTokens: row.preCompactTokens ?? undefined,
|
|
7771
|
+
cumulativeInputTokens: row.cumulativeInputTokens,
|
|
7772
|
+
cumulativeOutputTokens: row.cumulativeOutputTokens,
|
|
7773
|
+
createdAt: row.createdAt,
|
|
7774
|
+
};
|
|
7775
|
+
}
|
|
7776
|
+
|
|
7777
|
+
const contextSnapshotQueries = {
|
|
7778
|
+
insert: () =>
|
|
7779
|
+
getDb().prepare<
|
|
7780
|
+
ContextSnapshotRow,
|
|
7781
|
+
[
|
|
7782
|
+
string,
|
|
7783
|
+
string,
|
|
7784
|
+
string,
|
|
7785
|
+
string,
|
|
7786
|
+
number | null,
|
|
7787
|
+
number | null,
|
|
7788
|
+
number | null,
|
|
7789
|
+
string,
|
|
7790
|
+
string | null,
|
|
7791
|
+
number | null,
|
|
7792
|
+
number,
|
|
7793
|
+
number,
|
|
7794
|
+
string,
|
|
7795
|
+
]
|
|
7796
|
+
>(
|
|
7797
|
+
`INSERT INTO task_context_snapshots (id, taskId, agentId, sessionId, contextUsedTokens, contextTotalTokens, contextPercent, eventType, compactTrigger, preCompactTokens, cumulativeInputTokens, cumulativeOutputTokens, createdAt)
|
|
7798
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
7799
|
+
),
|
|
7800
|
+
|
|
7801
|
+
getByTaskId: () =>
|
|
7802
|
+
getDb().prepare<ContextSnapshotRow, [string, number]>(
|
|
7803
|
+
"SELECT * FROM task_context_snapshots WHERE taskId = ? ORDER BY createdAt ASC LIMIT ?",
|
|
7804
|
+
),
|
|
7805
|
+
|
|
7806
|
+
getBySessionId: () =>
|
|
7807
|
+
getDb().prepare<ContextSnapshotRow, [string, number]>(
|
|
7808
|
+
"SELECT * FROM task_context_snapshots WHERE sessionId = ? ORDER BY createdAt ASC LIMIT ?",
|
|
7809
|
+
),
|
|
7810
|
+
};
|
|
7811
|
+
|
|
7812
|
+
export interface CreateContextSnapshotInput {
|
|
7813
|
+
taskId: string;
|
|
7814
|
+
agentId: string;
|
|
7815
|
+
sessionId: string;
|
|
7816
|
+
contextUsedTokens?: number;
|
|
7817
|
+
contextTotalTokens?: number;
|
|
7818
|
+
contextPercent?: number;
|
|
7819
|
+
eventType: ContextSnapshotEventType;
|
|
7820
|
+
compactTrigger?: "auto" | "manual";
|
|
7821
|
+
preCompactTokens?: number;
|
|
7822
|
+
cumulativeInputTokens?: number;
|
|
7823
|
+
cumulativeOutputTokens?: number;
|
|
7824
|
+
}
|
|
7825
|
+
|
|
7826
|
+
export function createContextSnapshot(input: CreateContextSnapshotInput): ContextSnapshot {
|
|
7827
|
+
const id = crypto.randomUUID();
|
|
7828
|
+
const now = new Date().toISOString();
|
|
7829
|
+
|
|
7830
|
+
contextSnapshotQueries
|
|
7831
|
+
.insert()
|
|
7832
|
+
.run(
|
|
7833
|
+
id,
|
|
7834
|
+
input.taskId,
|
|
7835
|
+
input.agentId,
|
|
7836
|
+
input.sessionId,
|
|
7837
|
+
input.contextUsedTokens ?? null,
|
|
7838
|
+
input.contextTotalTokens ?? null,
|
|
7839
|
+
input.contextPercent ?? null,
|
|
7840
|
+
input.eventType,
|
|
7841
|
+
input.compactTrigger ?? null,
|
|
7842
|
+
input.preCompactTokens ?? null,
|
|
7843
|
+
input.cumulativeInputTokens ?? 0,
|
|
7844
|
+
input.cumulativeOutputTokens ?? 0,
|
|
7845
|
+
now,
|
|
7846
|
+
);
|
|
7847
|
+
|
|
7848
|
+
// Update aggregate columns on agent_tasks
|
|
7849
|
+
if (input.contextPercent != null) {
|
|
7850
|
+
getDb()
|
|
7851
|
+
.prepare(
|
|
7852
|
+
`UPDATE agent_tasks SET peakContextPercent = MAX(COALESCE(peakContextPercent, 0), ?)
|
|
7853
|
+
WHERE id = ?`,
|
|
7854
|
+
)
|
|
7855
|
+
.run(input.contextPercent, input.taskId);
|
|
7856
|
+
}
|
|
7857
|
+
|
|
7858
|
+
if (input.eventType === "compaction") {
|
|
7859
|
+
getDb()
|
|
7860
|
+
.prepare(
|
|
7861
|
+
"UPDATE agent_tasks SET compactionCount = COALESCE(compactionCount, 0) + 1 WHERE id = ?",
|
|
7862
|
+
)
|
|
7863
|
+
.run(input.taskId);
|
|
7864
|
+
}
|
|
7865
|
+
|
|
7866
|
+
if (input.eventType === "completion") {
|
|
7867
|
+
getDb()
|
|
7868
|
+
.prepare(
|
|
7869
|
+
`UPDATE agent_tasks SET totalContextTokensUsed = ?, contextWindowSize = ?
|
|
7870
|
+
WHERE id = ?`,
|
|
7871
|
+
)
|
|
7872
|
+
.run(input.contextUsedTokens ?? null, input.contextTotalTokens ?? null, input.taskId);
|
|
7873
|
+
}
|
|
7874
|
+
|
|
7875
|
+
return {
|
|
7876
|
+
id,
|
|
7877
|
+
taskId: input.taskId,
|
|
7878
|
+
agentId: input.agentId,
|
|
7879
|
+
sessionId: input.sessionId,
|
|
7880
|
+
contextUsedTokens: input.contextUsedTokens,
|
|
7881
|
+
contextTotalTokens: input.contextTotalTokens,
|
|
7882
|
+
contextPercent: input.contextPercent,
|
|
7883
|
+
eventType: input.eventType,
|
|
7884
|
+
compactTrigger: input.compactTrigger,
|
|
7885
|
+
preCompactTokens: input.preCompactTokens,
|
|
7886
|
+
cumulativeInputTokens: input.cumulativeInputTokens ?? 0,
|
|
7887
|
+
cumulativeOutputTokens: input.cumulativeOutputTokens ?? 0,
|
|
7888
|
+
createdAt: now,
|
|
7889
|
+
};
|
|
7890
|
+
}
|
|
7891
|
+
|
|
7892
|
+
export function getContextSnapshotsByTaskId(taskId: string, limit = 500): ContextSnapshot[] {
|
|
7893
|
+
return contextSnapshotQueries.getByTaskId().all(taskId, limit).map(rowToContextSnapshot);
|
|
7894
|
+
}
|
|
7895
|
+
|
|
7896
|
+
export function getContextSnapshotsBySessionId(sessionId: string, limit = 500): ContextSnapshot[] {
|
|
7897
|
+
return contextSnapshotQueries.getBySessionId().all(sessionId, limit).map(rowToContextSnapshot);
|
|
7898
|
+
}
|
|
7899
|
+
|
|
7900
|
+
export interface ContextSummary {
|
|
7901
|
+
compactionCount: number;
|
|
7902
|
+
peakContextPercent: number | null;
|
|
7903
|
+
totalContextTokensUsed: number | null;
|
|
7904
|
+
contextWindowSize: number | null;
|
|
7905
|
+
snapshotCount: number;
|
|
7906
|
+
}
|
|
7907
|
+
|
|
7908
|
+
export function getContextSummaryByTaskId(taskId: string): ContextSummary {
|
|
7909
|
+
const task = getTaskById(taskId);
|
|
7910
|
+
const countRow = getDb()
|
|
7911
|
+
.prepare<{ cnt: number }, [string]>(
|
|
7912
|
+
"SELECT COUNT(*) as cnt FROM task_context_snapshots WHERE taskId = ?",
|
|
7913
|
+
)
|
|
7914
|
+
.get(taskId);
|
|
7915
|
+
|
|
7916
|
+
return {
|
|
7917
|
+
compactionCount: task?.compactionCount ?? 0,
|
|
7918
|
+
peakContextPercent: task?.peakContextPercent ?? null,
|
|
7919
|
+
totalContextTokensUsed: task?.totalContextTokensUsed ?? null,
|
|
7920
|
+
contextWindowSize: task?.contextWindowSize ?? null,
|
|
7921
|
+
snapshotCount: countRow?.cnt ?? 0,
|
|
7922
|
+
};
|
|
7923
|
+
}
|