@desplega.ai/agent-swarm 1.90.0 → 1.91.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 +74 -1
- package/package.json +5 -5
- package/src/artifact-sdk/server.ts +2 -1
- package/src/be/memory/providers/sqlite-store.ts +6 -1
- package/src/be/memory/types.ts +1 -0
- package/src/be/scripts/typecheck.ts +132 -1
- package/src/be/seed-scripts/catalog/compound-insights.ts +188 -0
- package/src/be/seed-scripts/catalog/schedule-health.ts +73 -0
- package/src/be/seed-scripts/catalog/smart-recall.ts +65 -0
- package/src/be/seed-scripts/catalog/tool-usage.ts +56 -0
- package/src/be/seed-scripts/index.ts +36 -0
- package/src/commands/artifact.ts +3 -2
- package/src/commands/profile-sync.ts +310 -0
- package/src/commands/runner.ts +91 -1
- package/src/hooks/hook.ts +32 -9
- package/src/http/index.ts +47 -0
- package/src/http/integrations.ts +6 -1
- package/src/http/mcp-bridge.ts +117 -0
- package/src/http/mcp-oauth.ts +97 -39
- package/src/http/memory.ts +5 -2
- package/src/http/openapi.ts +2 -2
- package/src/http/pages-public.ts +10 -11
- package/src/http/pages.ts +7 -11
- package/src/http/scripts.ts +24 -1
- package/src/http/utils.ts +11 -4
- package/src/jira/app.ts +2 -3
- package/src/jira/webhook-lifecycle.ts +2 -1
- package/src/linear/app.ts +2 -3
- package/src/providers/claude-adapter.ts +26 -0
- package/src/scripts-runtime/executors/native.ts +1 -0
- package/src/scripts-runtime/sdk-allowlist.ts +121 -0
- package/src/scripts-runtime/swarm-sdk.ts +198 -3
- package/src/scripts-runtime/types/stdlib.d.ts +227 -0
- package/src/scripts-runtime/types/swarm-sdk.d.ts +227 -0
- package/src/tests/claude-adapter-otel.test.ts +85 -1
- package/src/tests/hook-registration-nudge.test.ts +69 -0
- package/src/tests/mcp-oauth-manual-client.test.ts +213 -0
- package/src/tests/pages-public-html.test.ts +41 -0
- package/src/tests/pages-public-json-redirect.test.ts +37 -2
- package/src/tests/profile-sync.test.ts +282 -0
- package/src/tests/scripts-runtime.test.ts +33 -0
- package/src/tests/seed-scripts.test.ts +2 -2
- package/src/tools/create-metric.ts +2 -3
- package/src/tools/create-page.ts +3 -6
- package/src/tools/memory-rate.ts +2 -1
- package/src/tools/memory-search.ts +1 -0
- package/src/tools/register-kapso-number.ts +2 -4
- package/src/tools/request-human-input.ts +2 -1
- package/src/tools/script-common.ts +2 -4
- package/src/tools/script-run.ts +7 -0
- package/src/utils/constants.ts +58 -8
- package/templates/skills/swarm-scripts/content.md +46 -7
package/openapi.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"openapi": "3.1.0",
|
|
3
3
|
"info": {
|
|
4
4
|
"title": "Agent Swarm API",
|
|
5
|
-
"version": "1.
|
|
5
|
+
"version": "1.91.0",
|
|
6
6
|
"description": "Multi-agent orchestration API for Claude Code, Codex, and Gemini CLI. Enables task distribution, agent communication, and service discovery.\n\nMCP tools are documented separately in [MCP.md](./MCP.md)."
|
|
7
7
|
},
|
|
8
8
|
"servers": [
|
|
@@ -4105,6 +4105,24 @@
|
|
|
4105
4105
|
"minimum": 1,
|
|
4106
4106
|
"maximum": 20,
|
|
4107
4107
|
"default": 5
|
|
4108
|
+
},
|
|
4109
|
+
"scope": {
|
|
4110
|
+
"type": "string",
|
|
4111
|
+
"enum": [
|
|
4112
|
+
"agent",
|
|
4113
|
+
"swarm",
|
|
4114
|
+
"all"
|
|
4115
|
+
],
|
|
4116
|
+
"default": "all"
|
|
4117
|
+
},
|
|
4118
|
+
"source": {
|
|
4119
|
+
"type": "string",
|
|
4120
|
+
"enum": [
|
|
4121
|
+
"manual",
|
|
4122
|
+
"file_index",
|
|
4123
|
+
"session_summary",
|
|
4124
|
+
"task_completion"
|
|
4125
|
+
]
|
|
4108
4126
|
}
|
|
4109
4127
|
},
|
|
4110
4128
|
"required": [
|
|
@@ -8152,6 +8170,10 @@
|
|
|
8152
8170
|
"workspace-rw"
|
|
8153
8171
|
],
|
|
8154
8172
|
"default": "none"
|
|
8173
|
+
},
|
|
8174
|
+
"idempotencyKey": {
|
|
8175
|
+
"type": "string",
|
|
8176
|
+
"maxLength": 200
|
|
8155
8177
|
}
|
|
8156
8178
|
}
|
|
8157
8179
|
}
|
|
@@ -8321,6 +8343,57 @@
|
|
|
8321
8343
|
}
|
|
8322
8344
|
}
|
|
8323
8345
|
},
|
|
8346
|
+
"/api/mcp-bridge": {
|
|
8347
|
+
"post": {
|
|
8348
|
+
"summary": "Generic MCP tool proxy for the scripts SDK bridge",
|
|
8349
|
+
"tags": [
|
|
8350
|
+
"Scripts"
|
|
8351
|
+
],
|
|
8352
|
+
"security": [
|
|
8353
|
+
{
|
|
8354
|
+
"bearerAuth": []
|
|
8355
|
+
}
|
|
8356
|
+
],
|
|
8357
|
+
"requestBody": {
|
|
8358
|
+
"content": {
|
|
8359
|
+
"application/json": {
|
|
8360
|
+
"schema": {
|
|
8361
|
+
"type": "object",
|
|
8362
|
+
"properties": {
|
|
8363
|
+
"tool": {
|
|
8364
|
+
"type": "string",
|
|
8365
|
+
"minLength": 1,
|
|
8366
|
+
"maxLength": 200
|
|
8367
|
+
},
|
|
8368
|
+
"args": {
|
|
8369
|
+
"type": "object",
|
|
8370
|
+
"additionalProperties": {},
|
|
8371
|
+
"default": {}
|
|
8372
|
+
}
|
|
8373
|
+
},
|
|
8374
|
+
"required": [
|
|
8375
|
+
"tool"
|
|
8376
|
+
]
|
|
8377
|
+
}
|
|
8378
|
+
}
|
|
8379
|
+
}
|
|
8380
|
+
},
|
|
8381
|
+
"responses": {
|
|
8382
|
+
"200": {
|
|
8383
|
+
"description": "Tool result"
|
|
8384
|
+
},
|
|
8385
|
+
"400": {
|
|
8386
|
+
"description": "Invalid tool name or args"
|
|
8387
|
+
},
|
|
8388
|
+
"403": {
|
|
8389
|
+
"description": "Tool not in SDK allowlist"
|
|
8390
|
+
},
|
|
8391
|
+
"404": {
|
|
8392
|
+
"description": "Tool not found"
|
|
8393
|
+
}
|
|
8394
|
+
}
|
|
8395
|
+
}
|
|
8396
|
+
},
|
|
8324
8397
|
"/api/mcp-oauth/{mcpServerId}/metadata": {
|
|
8325
8398
|
"get": {
|
|
8326
8399
|
"summary": "Probe OAuth metadata (PRMD + AS) for an MCP server",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@desplega.ai/agent-swarm",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.91.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>",
|
|
@@ -60,15 +60,15 @@
|
|
|
60
60
|
"dev": "bun --hot src/stdio.ts",
|
|
61
61
|
"dev:http": "portless api.swarm bun --hot src/http.ts",
|
|
62
62
|
"start": "bun src/stdio.ts",
|
|
63
|
-
"start:http": "bun src/http.ts",
|
|
64
|
-
"start:portless": "portless api.swarm bun src/http.ts",
|
|
63
|
+
"start:http": "bun --expose-gc src/http.ts",
|
|
64
|
+
"start:portless": "portless api.swarm bun --expose-gc src/http.ts",
|
|
65
65
|
"inspector": "bunx @modelcontextprotocol/inspector --transport stdio bun src/stdio.ts",
|
|
66
66
|
"inspector:http": "bunx @modelcontextprotocol/inspector --transport http https://api.swarm.localhost:1355/mcp",
|
|
67
67
|
"lint": "biome check src",
|
|
68
68
|
"lint:fix": "biome check --write src",
|
|
69
69
|
"format": "biome format --write src",
|
|
70
|
-
"build:binary": "bun build ./src/cli.tsx --compile --target=bun-linux-x64 --outfile ./dist/agent-swarm",
|
|
71
|
-
"build:binary:arm64": "bun build ./src/cli.tsx --compile --target=bun-linux-arm64 --outfile ./dist/agent-swarm",
|
|
70
|
+
"build:binary": "bun build ./src/cli.tsx --compile --compile-exec-argv='--expose-gc' --target=bun-linux-x64 --outfile ./dist/agent-swarm",
|
|
71
|
+
"build:binary:arm64": "bun build ./src/cli.tsx --compile --compile-exec-argv='--expose-gc' --target=bun-linux-arm64 --outfile ./dist/agent-swarm",
|
|
72
72
|
"docker:build:worker": "docker build -f Dockerfile.worker -t agent-swarm-worker:latest .",
|
|
73
73
|
"docker:run:worker": "docker run --rm -it --env-file .env.docker -p 3202:3000 -v ./logs:/logs -v ./work/shared:/workspace/shared -v ./work/worker-1:/workspace/personal agent-swarm-worker:latest",
|
|
74
74
|
"docker:run:lead": "docker run --rm -it --env-file .env.docker-lead -e AGENT_ROLE=lead -p 3201:3000 -v ./logs:/logs -v ./work/shared:/workspace/shared -v ./work/lead:/workspace/personal agent-swarm-worker:latest",
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Hono } from "hono";
|
|
2
2
|
import { serveStatic } from "hono/bun";
|
|
3
3
|
import { getApiKey } from "../utils/api-key";
|
|
4
|
+
import { getMcpBaseUrl } from "../utils/constants";
|
|
4
5
|
import { BROWSER_SDK_JS } from "./browser-sdk";
|
|
5
6
|
import { getAvailablePort } from "./port";
|
|
6
7
|
import { createTunnel } from "./tunnel";
|
|
@@ -47,7 +48,7 @@ export function createBunHonoFetchHandler(app: Hono): (req: Request) => Promise<
|
|
|
47
48
|
export function createArtifactServer(opts: ArtifactServerOptions): ArtifactServer {
|
|
48
49
|
const agentId = process.env.AGENT_ID || "unknown";
|
|
49
50
|
const apiKey = getApiKey();
|
|
50
|
-
const mcpBaseUrl =
|
|
51
|
+
const mcpBaseUrl = getMcpBaseUrl();
|
|
51
52
|
|
|
52
53
|
const app = new Hono();
|
|
53
54
|
|
|
@@ -383,7 +383,7 @@ export class SqliteMemoryStore implements MemoryStore {
|
|
|
383
383
|
}
|
|
384
384
|
|
|
385
385
|
list(agentId: string, options: MemoryListOptions = {}): AgentMemory[] {
|
|
386
|
-
const { scope = "all", limit = 20, offset = 0, isLead = false } = options;
|
|
386
|
+
const { scope = "all", limit = 20, offset = 0, isLead = false, source } = options;
|
|
387
387
|
const db = getDb();
|
|
388
388
|
|
|
389
389
|
const conditions: string[] = [];
|
|
@@ -407,6 +407,11 @@ export class SqliteMemoryStore implements MemoryStore {
|
|
|
407
407
|
}
|
|
408
408
|
}
|
|
409
409
|
|
|
410
|
+
if (source) {
|
|
411
|
+
conditions.push("source = ?");
|
|
412
|
+
params.push(source);
|
|
413
|
+
}
|
|
414
|
+
|
|
410
415
|
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
411
416
|
params.push(limit, offset);
|
|
412
417
|
|
package/src/be/memory/types.ts
CHANGED
|
@@ -50,21 +50,152 @@ export interface SwarmConfig {
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
export interface SwarmSdk {
|
|
53
|
+
// --- memory ---
|
|
53
54
|
memory_search(args: { query: string; scope?: "all" | "agent" | "swarm"; limit?: number; source?: string }): Promise<unknown>;
|
|
54
55
|
memory_get(args: { memoryId: string }): Promise<unknown>;
|
|
55
56
|
memory_rate(args: { id: string; useful: boolean; note?: string }): Promise<unknown>;
|
|
57
|
+
// --- tasks ---
|
|
56
58
|
task_list(args?: Record<string, unknown>): Promise<unknown>;
|
|
57
59
|
task_get(args: { taskId: string }): Promise<unknown>;
|
|
58
60
|
task_storeProgress(args: Record<string, unknown>): Promise<unknown>;
|
|
61
|
+
task_poll(args?: Record<string, unknown>): Promise<unknown>;
|
|
62
|
+
// --- kv ---
|
|
59
63
|
kv_get(args: { key: string; namespace?: string }): Promise<unknown>;
|
|
60
64
|
kv_set(args: { key: string; value: unknown; namespace?: string; ttlSeconds?: number; valueType?: "string" | "json" | "integer" }): Promise<unknown>;
|
|
61
65
|
kv_del(args: { key: string; namespace?: string }): Promise<unknown>;
|
|
62
66
|
kv_incr(args: { key: string; by?: number; namespace?: string }): Promise<unknown>;
|
|
63
67
|
kv_list(args?: { prefix?: string; namespace?: string; limit?: number }): Promise<unknown>;
|
|
68
|
+
// --- repos ---
|
|
64
69
|
repo_list(args?: Record<string, unknown>): Promise<unknown>;
|
|
70
|
+
// --- schedules ---
|
|
65
71
|
schedule_list(args?: Record<string, unknown>): Promise<unknown>;
|
|
72
|
+
// --- scripts ---
|
|
66
73
|
script_search(args: { query?: string; scope?: ScriptScope; limit?: number }): Promise<unknown>;
|
|
67
|
-
script_run(args: { name?: string; source?: string; args?: unknown; intent?: string; scope?: ScriptScope; fsMode?: ScriptFsMode }): Promise<unknown>;
|
|
74
|
+
script_run(args: { name?: string; source?: string; args?: unknown; intent?: string; scope?: ScriptScope; fsMode?: ScriptFsMode; idempotencyKey?: string }): Promise<unknown>;
|
|
75
|
+
// --- swarm / agent ---
|
|
76
|
+
swarm_get(args?: { includeFull?: boolean }): Promise<unknown>;
|
|
77
|
+
agent_info(args?: Record<string, unknown>): Promise<unknown>;
|
|
78
|
+
metrics_get(args?: Record<string, unknown>): Promise<unknown>;
|
|
79
|
+
user_resolve(args?: { kind?: string; externalId?: string; email?: string; userId?: string }): Promise<unknown>;
|
|
80
|
+
db_query(args: { sql: string; params?: unknown[] }): Promise<unknown>;
|
|
81
|
+
// --- config ---
|
|
82
|
+
config_get(args?: { agentId?: string; repoId?: string; key?: string; includeSecrets?: boolean }): Promise<unknown>;
|
|
83
|
+
config_list(args?: { scope?: "global" | "agent" | "repo"; scopeId?: string; key?: string; includeSecrets?: boolean }): Promise<unknown>;
|
|
84
|
+
// --- slack ---
|
|
85
|
+
slack_read(args?: { inboxMessageId?: string; taskId?: string; channelId?: string; threadTs?: string; limit?: number; includeFiles?: boolean }): Promise<unknown>;
|
|
86
|
+
slack_listChannels(args?: { types?: Array<"public" | "private" | "dm" | "mpim">; limit?: number }): Promise<unknown>;
|
|
87
|
+
// --- messaging ---
|
|
88
|
+
message_read(args?: { channel?: string; limit?: number; since?: string; unreadOnly?: boolean; mentionsOnly?: boolean; markAsRead?: boolean }): Promise<unknown>;
|
|
89
|
+
// --- services ---
|
|
90
|
+
service_list(args?: { agentId?: string; name?: string; status?: "starting" | "healthy" | "unhealthy" | "stopped"; includeOwn?: boolean }): Promise<unknown>;
|
|
91
|
+
// --- context / profiles ---
|
|
92
|
+
context_history(args?: { agentId?: string; field?: "soulMd" | "identityMd" | "toolsMd" | "claudeMd" | "setupScript"; limit?: number }): Promise<unknown>;
|
|
93
|
+
context_diff(args: { versionId: string; compareToVersionId?: string }): Promise<unknown>;
|
|
94
|
+
// --- workflows ---
|
|
95
|
+
workflow_list(args?: { enabled?: boolean; includeFull?: boolean }): Promise<unknown>;
|
|
96
|
+
workflow_get(args: { id: string }): Promise<unknown>;
|
|
97
|
+
workflow_listRuns(args: { workflowId: string; status?: "running" | "waiting" | "completed" | "failed" | "skipped" | "cancelled" }): Promise<unknown>;
|
|
98
|
+
workflow_getRun(args: { id: string }): Promise<unknown>;
|
|
99
|
+
// --- prompt templates ---
|
|
100
|
+
prompt_list(args?: { eventType?: string; scope?: "global" | "agent" | "repo"; scopeId?: string; isDefault?: boolean }): Promise<unknown>;
|
|
101
|
+
prompt_get(args: { id: string }): Promise<unknown>;
|
|
102
|
+
// --- tracker ---
|
|
103
|
+
tracker_status(args?: Record<string, unknown>): Promise<unknown>;
|
|
104
|
+
tracker_syncStatus(args?: Record<string, unknown>): Promise<unknown>;
|
|
105
|
+
tracker_linkTask(args: { taskId: string; externalId: string; provider?: string }): Promise<unknown>;
|
|
106
|
+
tracker_unlink(args: { taskId: string }): Promise<unknown>;
|
|
107
|
+
tracker_mapAgent(args: { agentId: string; externalId: string; provider?: string }): Promise<unknown>;
|
|
108
|
+
|
|
109
|
+
// --- write: memory ---
|
|
110
|
+
memory_delete(args: { id: string }): Promise<unknown>;
|
|
111
|
+
inject_learning(args: { content: string; name?: string; scope?: "agent" | "swarm"; source?: string; tags?: string[] }): Promise<unknown>;
|
|
112
|
+
|
|
113
|
+
// --- write: tasks ---
|
|
114
|
+
task_send(args: Record<string, unknown>): Promise<unknown>;
|
|
115
|
+
task_cancel(args: { taskId: string }): Promise<unknown>;
|
|
116
|
+
task_action(args: Record<string, unknown>): Promise<unknown>;
|
|
117
|
+
|
|
118
|
+
// --- write: config ---
|
|
119
|
+
config_set(args: { key: string; value: unknown; scope?: "global" | "agent" | "repo"; scopeId?: string; isSecret?: boolean }): Promise<unknown>;
|
|
120
|
+
config_delete(args: { id: string }): Promise<unknown>;
|
|
121
|
+
|
|
122
|
+
// --- write: slack ---
|
|
123
|
+
slack_post(args: { channelId: string; message: string; blocks?: unknown }): Promise<unknown>;
|
|
124
|
+
slack_reply(args: { channelId?: string; threadTs?: string; message: string; taskId?: string }): Promise<unknown>;
|
|
125
|
+
slack_startThread(args: { channelId: string; message: string }): Promise<unknown>;
|
|
126
|
+
slack_uploadFile(args: Record<string, unknown>): Promise<unknown>;
|
|
127
|
+
slack_downloadFile(args: { url: string }): Promise<unknown>;
|
|
128
|
+
|
|
129
|
+
// --- write: messaging (internal) ---
|
|
130
|
+
message_post(args: { channel?: string; content: string; to?: string }): Promise<unknown>;
|
|
131
|
+
|
|
132
|
+
// --- write: profiles ---
|
|
133
|
+
profile_update(args: Record<string, unknown>): Promise<unknown>;
|
|
134
|
+
|
|
135
|
+
// --- write: services ---
|
|
136
|
+
service_register(args: Record<string, unknown>): Promise<unknown>;
|
|
137
|
+
service_unregister(args: { name: string }): Promise<unknown>;
|
|
138
|
+
service_updateStatus(args: { name: string; status: "starting" | "healthy" | "unhealthy" | "stopped" }): Promise<unknown>;
|
|
139
|
+
|
|
140
|
+
// --- write: schedules ---
|
|
141
|
+
schedule_create(args: Record<string, unknown>): Promise<unknown>;
|
|
142
|
+
schedule_update(args: Record<string, unknown>): Promise<unknown>;
|
|
143
|
+
schedule_delete(args: { id: string }): Promise<unknown>;
|
|
144
|
+
schedule_runNow(args: { id: string }): Promise<unknown>;
|
|
145
|
+
|
|
146
|
+
// --- write: workflows ---
|
|
147
|
+
workflow_create(args: Record<string, unknown>): Promise<unknown>;
|
|
148
|
+
workflow_update(args: Record<string, unknown>): Promise<unknown>;
|
|
149
|
+
workflow_patch(args: Record<string, unknown>): Promise<unknown>;
|
|
150
|
+
workflow_patchNode(args: Record<string, unknown>): Promise<unknown>;
|
|
151
|
+
workflow_delete(args: { id: string }): Promise<unknown>;
|
|
152
|
+
workflow_trigger(args: { id: string; input?: Record<string, unknown> }): Promise<unknown>;
|
|
153
|
+
workflow_retryRun(args: { id: string }): Promise<unknown>;
|
|
154
|
+
workflow_cancelRun(args: { id: string }): Promise<unknown>;
|
|
155
|
+
|
|
156
|
+
// --- write: prompt templates ---
|
|
157
|
+
prompt_set(args: Record<string, unknown>): Promise<unknown>;
|
|
158
|
+
prompt_delete(args: { id: string }): Promise<unknown>;
|
|
159
|
+
prompt_preview(args: Record<string, unknown>): Promise<unknown>;
|
|
160
|
+
|
|
161
|
+
// --- write: scripts ---
|
|
162
|
+
script_upsert(args: { name: string; source: string; description?: string; intent?: string; scope?: ScriptScope; fsMode?: ScriptFsMode }): Promise<unknown>;
|
|
163
|
+
script_delete(args: { name: string; scope?: ScriptScope }): Promise<unknown>;
|
|
164
|
+
script_queryTypes(args: { name: string; scope?: ScriptScope }): Promise<unknown>;
|
|
165
|
+
|
|
166
|
+
// --- write: repos ---
|
|
167
|
+
repo_update(args: Record<string, unknown>): Promise<unknown>;
|
|
168
|
+
|
|
169
|
+
// --- write: agent ---
|
|
170
|
+
agent_join(args: { name: string; role?: string; description?: string; capabilities?: string[]; requestedId?: string; lead?: boolean }): Promise<unknown>;
|
|
171
|
+
user_manage(args: Record<string, unknown>): Promise<unknown>;
|
|
172
|
+
|
|
173
|
+
// --- skills ---
|
|
174
|
+
skill_list(args?: { scope?: string; scopeId?: string; includeBuiltin?: boolean }): Promise<unknown>;
|
|
175
|
+
skill_get(args: { id: string }): Promise<unknown>;
|
|
176
|
+
skill_search(args: { query: string; limit?: number }): Promise<unknown>;
|
|
177
|
+
skill_create(args: Record<string, unknown>): Promise<unknown>;
|
|
178
|
+
skill_update(args: Record<string, unknown>): Promise<unknown>;
|
|
179
|
+
skill_delete(args: { id: string }): Promise<unknown>;
|
|
180
|
+
skill_install(args: Record<string, unknown>): Promise<unknown>;
|
|
181
|
+
skill_uninstall(args: Record<string, unknown>): Promise<unknown>;
|
|
182
|
+
skill_publish(args: Record<string, unknown>): Promise<unknown>;
|
|
183
|
+
|
|
184
|
+
// --- mcp servers ---
|
|
185
|
+
mcpServer_list(args?: Record<string, unknown>): Promise<unknown>;
|
|
186
|
+
mcpServer_get(args: { id: string }): Promise<unknown>;
|
|
187
|
+
mcpServer_create(args: Record<string, unknown>): Promise<unknown>;
|
|
188
|
+
mcpServer_update(args: Record<string, unknown>): Promise<unknown>;
|
|
189
|
+
mcpServer_delete(args: { id: string }): Promise<unknown>;
|
|
190
|
+
mcpServer_install(args: Record<string, unknown>): Promise<unknown>;
|
|
191
|
+
mcpServer_uninstall(args: Record<string, unknown>): Promise<unknown>;
|
|
192
|
+
|
|
193
|
+
// --- pages & metrics ---
|
|
194
|
+
page_create(args: Record<string, unknown>): Promise<unknown>;
|
|
195
|
+
metric_create(args: Record<string, unknown>): Promise<unknown>;
|
|
196
|
+
|
|
197
|
+
// --- human input ---
|
|
198
|
+
request_humanInput(args: Record<string, unknown>): Promise<unknown>;
|
|
68
199
|
}
|
|
69
200
|
|
|
70
201
|
export interface ScriptStdlib {
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
export const argsSchema = z.object({
|
|
4
|
+
days: z
|
|
5
|
+
.number()
|
|
6
|
+
.int()
|
|
7
|
+
.positive()
|
|
8
|
+
.optional()
|
|
9
|
+
.describe("Look back this many days (default 3)"),
|
|
10
|
+
includeToolUsage: z.boolean().optional().describe("Include tool usage histogram (default true)"),
|
|
11
|
+
includeScheduleHealth: z
|
|
12
|
+
.boolean()
|
|
13
|
+
.optional()
|
|
14
|
+
.describe("Include schedule health flags (default true)"),
|
|
15
|
+
includeMemoryHealth: z.boolean().optional().describe("Include memory health stats (default true)"),
|
|
16
|
+
includeByAgent: z
|
|
17
|
+
.boolean()
|
|
18
|
+
.optional()
|
|
19
|
+
.describe("Include per-agent task/completion/failure breakdown (default true)"),
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Failure reasons that are swarm bookkeeping, not real failures. Excluded from
|
|
24
|
+
* failureClusters, scheduleHealth and byAgent failure counts (Lead Rule #16):
|
|
25
|
+
* the run engine collapses redundant sibling tasks into these statuses, so
|
|
26
|
+
* counting them produces phantom failure spikes.
|
|
27
|
+
*/
|
|
28
|
+
const EXCLUDED_FAIL = ["superseded_workflow_task", "cancelled"];
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* `db_query` returns positional rows (`rows: unknown[][]`) plus a `columns`
|
|
32
|
+
* array — NOT an array of objects. Zip them back into objects so callers can
|
|
33
|
+
* read by column name.
|
|
34
|
+
*/
|
|
35
|
+
function rowsToObjects(res: any): any[] {
|
|
36
|
+
const p = res?.data ?? res;
|
|
37
|
+
const cols: string[] = p?.columns ?? [];
|
|
38
|
+
return (p?.rows ?? []).map((r: any) =>
|
|
39
|
+
Array.isArray(r) ? Object.fromEntries(cols.map((c, i) => [c, r[i]])) : r,
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Daily compounding insights — compressed JSON for Phase 0 evolution.
|
|
45
|
+
*
|
|
46
|
+
* Swarm-wide by design: every section aggregates across ALL agents via direct
|
|
47
|
+
* read-only SQL (no per-agent scoping), so a single call replaces ~25 raw tool
|
|
48
|
+
* roundtrips. Parametric via `days` + the `include*` flags.
|
|
49
|
+
*/
|
|
50
|
+
export default async function compoundInsights(args: any, ctx: any) {
|
|
51
|
+
const parsed = argsSchema.safeParse(args || {});
|
|
52
|
+
if (!parsed.success) return { error: "invalid args: " + parsed.error.message };
|
|
53
|
+
const days = parsed.data.days || 3;
|
|
54
|
+
const includeToolUsage = parsed.data.includeToolUsage !== false;
|
|
55
|
+
const includeScheduleHealth = parsed.data.includeScheduleHealth !== false;
|
|
56
|
+
const includeMemoryHealth = parsed.data.includeMemoryHealth !== false;
|
|
57
|
+
const includeByAgent = parsed.data.includeByAgent !== false;
|
|
58
|
+
|
|
59
|
+
// `days` is a validated positive int, so it is safe to interpolate into the
|
|
60
|
+
// SQLite datetime modifier. EXCLUDED_FAIL is a fixed constant list.
|
|
61
|
+
const w = `datetime('now','-${days} days')`;
|
|
62
|
+
const exclList = EXCLUDED_FAIL.map((r) => `'${r}'`).join(",");
|
|
63
|
+
// A "real" failure = status failed AND not one of the bookkeeping reasons.
|
|
64
|
+
const realFail = `t.status='failed' AND (t.failureReason IS NULL OR t.failureReason NOT IN (${exclList}))`;
|
|
65
|
+
|
|
66
|
+
const insights: any = { days, generatedAt: new Date().toISOString() };
|
|
67
|
+
|
|
68
|
+
// Task summary (all agents, direct SQL).
|
|
69
|
+
const statusRows = rowsToObjects(
|
|
70
|
+
await ctx.swarm.db_query({
|
|
71
|
+
sql: `SELECT status, count(*) as cnt FROM agent_tasks t WHERE t.createdAt > ${w} GROUP BY status`,
|
|
72
|
+
}),
|
|
73
|
+
);
|
|
74
|
+
const statusCounts: Record<string, number> = {};
|
|
75
|
+
let total = 0;
|
|
76
|
+
for (const r of statusRows) {
|
|
77
|
+
statusCounts[r.status] = r.cnt;
|
|
78
|
+
total += r.cnt;
|
|
79
|
+
}
|
|
80
|
+
const completed = statusCounts.completed ?? 0;
|
|
81
|
+
const failed = statusCounts.failed ?? 0;
|
|
82
|
+
insights.taskSummary = {
|
|
83
|
+
total,
|
|
84
|
+
completed,
|
|
85
|
+
failed,
|
|
86
|
+
completionRate: total > 0 ? Math.round((completed / total) * 1000) / 10 : 0,
|
|
87
|
+
failureRate: total > 0 ? Math.round((failed / total) * 1000) / 10 : 0,
|
|
88
|
+
statusCounts,
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
// Failure clusters (real failures only, normalized to a 60-char lowercased prefix).
|
|
92
|
+
insights.failureClusters = rowsToObjects(
|
|
93
|
+
await ctx.swarm.db_query({
|
|
94
|
+
sql: `SELECT substr(lower(t.failureReason),1,60) as reason, count(*) as count
|
|
95
|
+
FROM agent_tasks t
|
|
96
|
+
WHERE ${realFail} AND t.failureReason IS NOT NULL AND t.createdAt > ${w}
|
|
97
|
+
GROUP BY reason ORDER BY count DESC LIMIT 10`,
|
|
98
|
+
}),
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
// Schedule health (>= 2 runs, > 20% real-failure rate).
|
|
102
|
+
if (includeScheduleHealth) {
|
|
103
|
+
const sh = rowsToObjects(
|
|
104
|
+
await ctx.swarm.db_query({
|
|
105
|
+
sql: `SELECT s.name as name, s.id as id, count(t.id) as runs,
|
|
106
|
+
sum(case when ${realFail} then 1 else 0 end) as failed
|
|
107
|
+
FROM scheduled_tasks s
|
|
108
|
+
JOIN agent_tasks t ON t.scheduleId = s.id
|
|
109
|
+
WHERE t.createdAt > ${w} AND t.status != 'cancelled'
|
|
110
|
+
GROUP BY s.id, s.name HAVING runs >= 2`,
|
|
111
|
+
}),
|
|
112
|
+
);
|
|
113
|
+
insights.scheduleHealth = sh
|
|
114
|
+
.map((r: any) => ({
|
|
115
|
+
name: r.name,
|
|
116
|
+
id: r.id,
|
|
117
|
+
runs: r.runs,
|
|
118
|
+
failureRate: r.runs > 0 ? Math.round((r.failed / r.runs) * 100) : 0,
|
|
119
|
+
}))
|
|
120
|
+
.filter((r: any) => r.failureRate > 20)
|
|
121
|
+
.sort((a: any, b: any) => b.failureRate - a.failureRate);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Tool usage (top 25). Tool names live inside the `content` JSON of
|
|
125
|
+
// session_logs (no dedicated column), so extract the name SQL-side: the
|
|
126
|
+
// `'%"type":"tool_use"%'` filter excludes tool_result rows (which only carry
|
|
127
|
+
// `tool_use_id`), and instr/substr pull the first tool name per log line.
|
|
128
|
+
// Approximate: a log line with parallel tool_use blocks counts only its first.
|
|
129
|
+
if (includeToolUsage) {
|
|
130
|
+
insights.toolUsage = rowsToObjects(
|
|
131
|
+
await ctx.swarm.db_query({
|
|
132
|
+
sql: `WITH tu AS (
|
|
133
|
+
SELECT substr(content, instr(content,'"type":"tool_use"')) AS tail
|
|
134
|
+
FROM session_logs
|
|
135
|
+
WHERE content LIKE '%"type":"tool_use"%' AND createdAt > ${w}
|
|
136
|
+
),
|
|
137
|
+
nm AS (
|
|
138
|
+
SELECT substr(tail, instr(tail,'"name":"')+8) AS rest
|
|
139
|
+
FROM tu WHERE instr(tail,'"name":"') > 0
|
|
140
|
+
)
|
|
141
|
+
SELECT substr(rest,1,instr(rest,'"')-1) AS tool, count(*) AS calls
|
|
142
|
+
FROM nm GROUP BY tool ORDER BY calls DESC LIMIT 25`,
|
|
143
|
+
}),
|
|
144
|
+
).map((r: any) => ({ tool: r.tool, calls: r.calls }));
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Memory health (whole store, by scope + source).
|
|
148
|
+
if (includeMemoryHealth) {
|
|
149
|
+
const memRows = rowsToObjects(
|
|
150
|
+
await ctx.swarm.db_query({
|
|
151
|
+
sql: `SELECT scope, source, count(*) as cnt FROM agent_memory GROUP BY scope, source`,
|
|
152
|
+
}),
|
|
153
|
+
);
|
|
154
|
+
const totalMem = memRows.reduce((s: number, r: any) => s + (r.cnt ?? 0), 0);
|
|
155
|
+
insights.memoryHealth = {
|
|
156
|
+
total: totalMem,
|
|
157
|
+
byScope: memRows.reduce((m: any, r: any) => {
|
|
158
|
+
m[r.scope] = (m[r.scope] ?? 0) + r.cnt;
|
|
159
|
+
return m;
|
|
160
|
+
}, {}),
|
|
161
|
+
bySource: memRows.reduce((m: any, r: any) => {
|
|
162
|
+
m[r.source] = (m[r.source] ?? 0) + r.cnt;
|
|
163
|
+
return m;
|
|
164
|
+
}, {}),
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Per-agent breakdown — covers every agent that ran a task in the window.
|
|
169
|
+
if (includeByAgent) {
|
|
170
|
+
insights.byAgent = rowsToObjects(
|
|
171
|
+
await ctx.swarm.db_query({
|
|
172
|
+
sql: `SELECT a.name as agent, count(*) as total,
|
|
173
|
+
sum(case when t.status='completed' then 1 else 0 end) as completed,
|
|
174
|
+
sum(case when ${realFail} then 1 else 0 end) as failed
|
|
175
|
+
FROM agent_tasks t LEFT JOIN agents a ON a.id = t.agentId
|
|
176
|
+
WHERE t.createdAt > ${w} AND t.agentId IS NOT NULL
|
|
177
|
+
GROUP BY t.agentId, a.name ORDER BY total DESC LIMIT 30`,
|
|
178
|
+
}),
|
|
179
|
+
).map((r: any) => ({
|
|
180
|
+
agent: r.agent,
|
|
181
|
+
total: r.total,
|
|
182
|
+
completed: r.completed,
|
|
183
|
+
failed: r.failed,
|
|
184
|
+
}));
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return insights;
|
|
188
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
export const argsSchema = z.object({
|
|
4
|
+
days: z
|
|
5
|
+
.number()
|
|
6
|
+
.int()
|
|
7
|
+
.positive()
|
|
8
|
+
.optional()
|
|
9
|
+
.describe("Look back this many days (default 7)"),
|
|
10
|
+
failureThreshold: z
|
|
11
|
+
.number()
|
|
12
|
+
.optional()
|
|
13
|
+
.describe("Flag schedules with failure rate above this (0-1, default 0.2)"),
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
/** Per-schedule health check: failure rates and flagging unhealthy schedules. */
|
|
17
|
+
export default async function scheduleHealth(args: any, ctx: any) {
|
|
18
|
+
const parsed = argsSchema.safeParse(args);
|
|
19
|
+
if (!parsed.success) return { error: "invalid args: " + parsed.error.message };
|
|
20
|
+
const days = parsed.data.days || 7;
|
|
21
|
+
const threshold = parsed.data.failureThreshold ?? 0.2;
|
|
22
|
+
const since = new Date(Date.now() - days * 86400000).toISOString();
|
|
23
|
+
|
|
24
|
+
// Get schedules
|
|
25
|
+
const schedRes: any = await ctx.swarm.schedule_list({});
|
|
26
|
+
const schedPayload = schedRes?.data ?? schedRes;
|
|
27
|
+
const schedules: any[] = schedPayload?.schedules ?? [];
|
|
28
|
+
|
|
29
|
+
if (!schedules.length) return { days, schedules: [], flagged: [] };
|
|
30
|
+
|
|
31
|
+
// Get recent tasks to correlate with schedules
|
|
32
|
+
const taskRes: any = await ctx.swarm.task_list({ createdAfter: since, limit: 2000 });
|
|
33
|
+
const taskPayload = taskRes?.data ?? taskRes;
|
|
34
|
+
const tasks: any[] = taskPayload?.tasks ?? [];
|
|
35
|
+
|
|
36
|
+
// Group by scheduleId
|
|
37
|
+
const bySchedule = new Map<string, { total: number; failed: number; completed: number }>();
|
|
38
|
+
for (const t of tasks) {
|
|
39
|
+
if (!t.scheduleId) continue;
|
|
40
|
+
const entry = bySchedule.get(t.scheduleId) ?? { total: 0, failed: 0, completed: 0 };
|
|
41
|
+
entry.total++;
|
|
42
|
+
if (t.status === "failed") entry.failed++;
|
|
43
|
+
if (t.status === "completed") entry.completed++;
|
|
44
|
+
bySchedule.set(t.scheduleId, entry);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const results = schedules.map((s: any) => {
|
|
48
|
+
const stats = bySchedule.get(s.id) ?? { total: 0, failed: 0, completed: 0 };
|
|
49
|
+
const failureRate = stats.total > 0 ? stats.failed / stats.total : 0;
|
|
50
|
+
return {
|
|
51
|
+
id: s.id,
|
|
52
|
+
name: s.name,
|
|
53
|
+
enabled: s.enabled,
|
|
54
|
+
targetAgentId: s.targetAgentId,
|
|
55
|
+
runs: stats.total,
|
|
56
|
+
failed: stats.failed,
|
|
57
|
+
completed: stats.completed,
|
|
58
|
+
failureRate: Math.round(failureRate * 1000) / 1000,
|
|
59
|
+
flagged: stats.total >= 3 && failureRate > threshold,
|
|
60
|
+
};
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
const flagged = results.filter((r: any) => r.flagged);
|
|
64
|
+
|
|
65
|
+
return {
|
|
66
|
+
days,
|
|
67
|
+
threshold,
|
|
68
|
+
totalSchedules: schedules.length,
|
|
69
|
+
flaggedCount: flagged.length,
|
|
70
|
+
schedules: results.sort((a: any, b: any) => b.failureRate - a.failureRate),
|
|
71
|
+
flagged,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
export const argsSchema = z.object({
|
|
4
|
+
queries: z
|
|
5
|
+
.array(z.string())
|
|
6
|
+
.min(1)
|
|
7
|
+
.describe("One or more search queries to fan out against the memory store"),
|
|
8
|
+
scope: z
|
|
9
|
+
.enum(["all", "agent", "swarm"])
|
|
10
|
+
.optional()
|
|
11
|
+
.describe("Memory scope filter (default all)"),
|
|
12
|
+
limit: z
|
|
13
|
+
.number()
|
|
14
|
+
.int()
|
|
15
|
+
.positive()
|
|
16
|
+
.optional()
|
|
17
|
+
.describe("Max results per query (default 10)"),
|
|
18
|
+
dedupThreshold: z
|
|
19
|
+
.number()
|
|
20
|
+
.optional()
|
|
21
|
+
.describe("Similarity threshold for dedup — memories above this are merged (default 0.92)"),
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
/** Multi-query fan-out memory recall with dedup and composite reranking. */
|
|
25
|
+
export default async function smartRecall(args: any, ctx: any) {
|
|
26
|
+
const parsed = argsSchema.safeParse(args);
|
|
27
|
+
if (!parsed.success) return { error: "invalid args: " + parsed.error.message };
|
|
28
|
+
const { queries, scope = "all", limit = 10, dedupThreshold = 0.92 } = parsed.data;
|
|
29
|
+
|
|
30
|
+
const allResults: any[] = [];
|
|
31
|
+
for (const q of queries) {
|
|
32
|
+
const res: any = await ctx.swarm.memory_search({ query: q, limit, scope });
|
|
33
|
+
const payload = res?.data ?? res;
|
|
34
|
+
const results = payload?.results ?? [];
|
|
35
|
+
for (const r of results) {
|
|
36
|
+
allResults.push({ ...r, querySource: q });
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Dedup by ID, keeping best similarity per memory
|
|
41
|
+
const seen = new Map<string, any>();
|
|
42
|
+
const hitCounts = new Map<string, number>();
|
|
43
|
+
for (const r of allResults) {
|
|
44
|
+
hitCounts.set(r.id, (hitCounts.get(r.id) ?? 0) + 1);
|
|
45
|
+
const existing = seen.get(r.id);
|
|
46
|
+
if (!existing || r.similarity > existing.similarity) {
|
|
47
|
+
seen.set(r.id, r);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Composite rerank: bestSimilarity + 0.05 * hitCount
|
|
52
|
+
const deduped = Array.from(seen.values()).map((r) => ({
|
|
53
|
+
...r,
|
|
54
|
+
hits: hitCounts.get(r.id) ?? 1,
|
|
55
|
+
compositeScore: r.similarity + 0.05 * (hitCounts.get(r.id) ?? 1),
|
|
56
|
+
}));
|
|
57
|
+
deduped.sort((a: any, b: any) => b.compositeScore - a.compositeScore);
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
queriesRun: queries.length,
|
|
61
|
+
totalCandidates: allResults.length,
|
|
62
|
+
uniqueMemories: deduped.length,
|
|
63
|
+
memories: deduped.slice(0, limit * 2),
|
|
64
|
+
};
|
|
65
|
+
}
|