@desplega.ai/agent-swarm 1.89.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/README.md +4 -0
- package/openapi.json +74 -1
- package/package.json +6 -6
- package/plugin/skills/composio/SKILL.md +138 -63
- package/plugin/skills/composio-gmail/SKILL.md +83 -0
- package/plugin/skills/composio-google-calendar/SKILL.md +81 -0
- package/plugin/skills/composio-google-docs/SKILL.md +71 -0
- package/src/artifact-sdk/server.ts +2 -1
- package/src/be/db.ts +28 -0
- package/src/be/memory/providers/sqlite-store.ts +6 -1
- package/src/be/memory/types.ts +1 -0
- package/src/be/modelsdev-cache.json +752 -81
- 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/heartbeat/heartbeat.ts +54 -7
- 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/tasks.ts +2 -0
- 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/tasks/worker-follow-up.ts +19 -1
- package/src/tests/claude-adapter-otel.test.ts +85 -1
- package/src/tests/heartbeat-supersede-resume.test.ts +91 -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/src/jira/app.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { upsertOAuthApp } from "../be/db-queries/oauth";
|
|
2
|
+
import { getPublicMcpBaseUrl } from "../utils/constants";
|
|
2
3
|
import { initJiraOutboundSync, teardownJiraOutboundSync } from "./outbound";
|
|
3
4
|
// Side-effect import: registers all Jira event templates in the in-memory
|
|
4
5
|
// registry at module load time (mirrors `src/linear/templates.ts`).
|
|
@@ -41,9 +42,7 @@ export function initJira(): boolean {
|
|
|
41
42
|
// Atlassian. Prefer MCP_BASE_URL over the localhost dev default; in prod
|
|
42
43
|
// with no JIRA_REDIRECT_URI set, this is what stops Atlassian from sending
|
|
43
44
|
// the user back to localhost.
|
|
44
|
-
const apiBaseUrl =
|
|
45
|
-
process.env.MCP_BASE_URL?.trim().replace(/\/+$/, "") ||
|
|
46
|
-
`http://localhost:${process.env.PORT || "3013"}`;
|
|
45
|
+
const apiBaseUrl = getPublicMcpBaseUrl();
|
|
47
46
|
const redirectUri = process.env.JIRA_REDIRECT_URI ?? `${apiBaseUrl}/api/trackers/jira/callback`;
|
|
48
47
|
|
|
49
48
|
upsertOAuthApp("jira", {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { getPublicMcpBaseUrl } from "../utils/constants";
|
|
1
2
|
import { jiraFetch } from "./client";
|
|
2
3
|
import { getJiraMetadata, updateJiraMetadata } from "./metadata";
|
|
3
4
|
|
|
@@ -23,7 +24,7 @@ let keepaliveInterval: ReturnType<typeof setInterval> | null = null;
|
|
|
23
24
|
// ─── URL helpers ─────────────────────────────────────────────────────────────
|
|
24
25
|
|
|
25
26
|
function getWebhookBaseUrl(): string {
|
|
26
|
-
return
|
|
27
|
+
return getPublicMcpBaseUrl();
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
function getRegisteredWebhookUrl(): string {
|
package/src/linear/app.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { upsertOAuthApp } from "../be/db-queries/oauth";
|
|
2
|
+
import { getPublicMcpBaseUrl } from "../utils/constants";
|
|
2
3
|
import { initLinearOutboundSync, teardownLinearOutboundSync } from "./outbound";
|
|
3
4
|
|
|
4
5
|
let initialized = false;
|
|
@@ -31,9 +32,7 @@ export function initLinear(): boolean {
|
|
|
31
32
|
// verbatim by the OAuth flow. Prefer MCP_BASE_URL over the localhost default
|
|
32
33
|
// so prod doesn't send users back to localhost when LINEAR_REDIRECT_URI is
|
|
33
34
|
// unset.
|
|
34
|
-
const apiBaseUrl =
|
|
35
|
-
process.env.MCP_BASE_URL?.trim().replace(/\/+$/, "") ||
|
|
36
|
-
`http://localhost:${process.env.PORT || "3013"}`;
|
|
35
|
+
const apiBaseUrl = getPublicMcpBaseUrl();
|
|
37
36
|
const redirectUri =
|
|
38
37
|
process.env.LINEAR_REDIRECT_URI ?? `${apiBaseUrl}/api/trackers/linear/callback`;
|
|
39
38
|
|
|
@@ -336,6 +336,30 @@ export function buildClaudeCodeOtelEnv(
|
|
|
336
336
|
return otelEnv;
|
|
337
337
|
}
|
|
338
338
|
|
|
339
|
+
/**
|
|
340
|
+
* Claude Code runtime defaults for ephemeral swarm harness sessions.
|
|
341
|
+
*
|
|
342
|
+
* These are plain subprocess env vars, not prompt content. They are injected
|
|
343
|
+
* after the resolved swarm config so the worker enforces the memory/privacy
|
|
344
|
+
* guardrails consistently per spawn. Statsig/DNT opt-out is intentionally
|
|
345
|
+
* separate from our Claude Code OTel export path, which is controlled by
|
|
346
|
+
* buildClaudeCodeOtelEnv.
|
|
347
|
+
*/
|
|
348
|
+
export function buildClaudeCodeRuntimeEnv(
|
|
349
|
+
_sourceEnv: Record<string, string | undefined>,
|
|
350
|
+
): Record<string, string> {
|
|
351
|
+
return {
|
|
352
|
+
ENABLE_TOOL_SEARCH: "true",
|
|
353
|
+
CLAUDE_CODE_DISABLE_FILE_CHECKPOINTING: "1",
|
|
354
|
+
CLAUDE_CODE_SKIP_PROMPT_HISTORY: "1",
|
|
355
|
+
CLAUDE_CODE_DISABLE_ATTACHMENTS: "1",
|
|
356
|
+
DISABLE_TELEMETRY: "1",
|
|
357
|
+
DO_NOT_TRACK: "1",
|
|
358
|
+
DISABLE_FEEDBACK_COMMAND: "1",
|
|
359
|
+
DISABLE_BUG_COMMAND: "1",
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
|
|
339
363
|
/**
|
|
340
364
|
* Resolve the path at which the per-task system prompt is staged on disk.
|
|
341
365
|
*
|
|
@@ -398,11 +422,13 @@ class ClaudeSession implements ProviderSession {
|
|
|
398
422
|
// so the freshly-computed TRACEPARENT wins over any stale value the
|
|
399
423
|
// container env might carry.
|
|
400
424
|
const otelEnv = buildClaudeCodeOtelEnv(sourceEnv);
|
|
425
|
+
const runtimeEnv = buildClaudeCodeRuntimeEnv(sourceEnv);
|
|
401
426
|
this.proc = Bun.spawn(cmd, {
|
|
402
427
|
cwd: this.config.cwd,
|
|
403
428
|
env: {
|
|
404
429
|
ENABLE_PROMPT_CACHING_1H: "1",
|
|
405
430
|
...sourceEnv,
|
|
431
|
+
...runtimeEnv,
|
|
406
432
|
...otelEnv,
|
|
407
433
|
TASK_FILE: taskFilePath,
|
|
408
434
|
// Belt-and-braces: TASK_FILE on disk can disappear mid-session (race
|
|
@@ -90,6 +90,7 @@ async function writeBareImportShims(tmpdir: string): Promise<void> {
|
|
|
90
90
|
const shims: [string, string][] = [
|
|
91
91
|
["stdlib", `${runtimeDir}/stdlib.bundle.js`],
|
|
92
92
|
["swarm-sdk", `${runtimeDir}/swarm-sdk.bundle.js`],
|
|
93
|
+
["zod", `${runtimeDir}/zod.bundle.js`],
|
|
93
94
|
];
|
|
94
95
|
for (const [name, bundlePath] of shims) {
|
|
95
96
|
const dir = `${tmpdir}/node_modules/${name}`;
|
|
@@ -1,19 +1,140 @@
|
|
|
1
1
|
export const SDK_TOOL_NAME_MAP = {
|
|
2
|
+
// ── memory ──
|
|
2
3
|
memory_search: "memory-search",
|
|
3
4
|
memory_get: "memory-get",
|
|
4
5
|
memory_rate: "memory_rate",
|
|
6
|
+
memory_delete: "memory-delete", // destructive
|
|
7
|
+
inject_learning: "inject-learning",
|
|
8
|
+
|
|
9
|
+
// ── tasks ──
|
|
5
10
|
task_list: "get-tasks",
|
|
6
11
|
task_get: "get-task-details",
|
|
7
12
|
task_storeProgress: "store-progress",
|
|
13
|
+
task_poll: "poll-task",
|
|
14
|
+
task_send: "send-task",
|
|
15
|
+
task_cancel: "cancel-task", // destructive
|
|
16
|
+
task_action: "task-action",
|
|
17
|
+
|
|
18
|
+
// ── kv ──
|
|
8
19
|
kv_get: "kv-get",
|
|
9
20
|
kv_set: "kv-set",
|
|
10
21
|
kv_del: "kv-delete",
|
|
11
22
|
kv_incr: "kv-incr",
|
|
12
23
|
kv_list: "kv-list",
|
|
24
|
+
|
|
25
|
+
// ── repos ──
|
|
13
26
|
repo_list: "get-repos",
|
|
27
|
+
repo_update: "update-repo",
|
|
28
|
+
|
|
29
|
+
// ── schedules ──
|
|
14
30
|
schedule_list: "list-schedules",
|
|
31
|
+
schedule_create: "create-schedule",
|
|
32
|
+
schedule_update: "update-schedule",
|
|
33
|
+
schedule_delete: "delete-schedule", // destructive
|
|
34
|
+
schedule_runNow: "run-schedule-now",
|
|
35
|
+
|
|
36
|
+
// ── scripts ──
|
|
15
37
|
script_search: "script-search",
|
|
16
38
|
script_run: "script-run",
|
|
39
|
+
script_upsert: "script-upsert",
|
|
40
|
+
script_delete: "script-delete", // destructive
|
|
41
|
+
script_queryTypes: "script-query-types",
|
|
42
|
+
|
|
43
|
+
// ── swarm / agent ──
|
|
44
|
+
swarm_get: "get-swarm",
|
|
45
|
+
agent_info: "my-agent-info",
|
|
46
|
+
agent_join: "join-swarm",
|
|
47
|
+
metrics_get: "get-metrics",
|
|
48
|
+
user_resolve: "resolve-user",
|
|
49
|
+
user_manage: "manage-user",
|
|
50
|
+
db_query: "db-query",
|
|
51
|
+
|
|
52
|
+
// ── config ──
|
|
53
|
+
config_get: "get-config",
|
|
54
|
+
config_list: "list-config",
|
|
55
|
+
config_set: "set-config",
|
|
56
|
+
config_delete: "delete-config", // destructive
|
|
57
|
+
|
|
58
|
+
// ── slack ──
|
|
59
|
+
slack_read: "slack-read",
|
|
60
|
+
slack_listChannels: "slack-list-channels",
|
|
61
|
+
slack_post: "slack-post", // external: sends to Slack
|
|
62
|
+
slack_reply: "slack-reply", // external: sends to Slack
|
|
63
|
+
slack_startThread: "slack-start-thread", // external: sends to Slack
|
|
64
|
+
slack_uploadFile: "slack-upload-file", // external: sends to Slack
|
|
65
|
+
slack_downloadFile: "slack-download-file",
|
|
66
|
+
|
|
67
|
+
// ── messaging (internal) ──
|
|
68
|
+
message_read: "read-messages",
|
|
69
|
+
message_post: "post-message",
|
|
70
|
+
|
|
71
|
+
// ── profiles ──
|
|
72
|
+
profile_update: "update-profile",
|
|
73
|
+
|
|
74
|
+
// ── context / profiles ──
|
|
75
|
+
context_history: "context-history",
|
|
76
|
+
context_diff: "context-diff",
|
|
77
|
+
|
|
78
|
+
// ── services ──
|
|
79
|
+
service_list: "list-services",
|
|
80
|
+
service_register: "register-service",
|
|
81
|
+
service_unregister: "unregister-service", // destructive
|
|
82
|
+
service_updateStatus: "update-service-status",
|
|
83
|
+
|
|
84
|
+
// ── workflows ──
|
|
85
|
+
workflow_list: "list-workflows",
|
|
86
|
+
workflow_get: "get-workflow",
|
|
87
|
+
workflow_create: "create-workflow",
|
|
88
|
+
workflow_update: "update-workflow",
|
|
89
|
+
workflow_patch: "patch-workflow",
|
|
90
|
+
workflow_patchNode: "patch-workflow-node",
|
|
91
|
+
workflow_delete: "delete-workflow", // destructive
|
|
92
|
+
workflow_trigger: "trigger-workflow",
|
|
93
|
+
workflow_listRuns: "list-workflow-runs",
|
|
94
|
+
workflow_getRun: "get-workflow-run",
|
|
95
|
+
workflow_retryRun: "retry-workflow-run",
|
|
96
|
+
workflow_cancelRun: "cancel-workflow-run", // destructive
|
|
97
|
+
|
|
98
|
+
// ── prompt templates ──
|
|
99
|
+
prompt_list: "list-prompt-templates",
|
|
100
|
+
prompt_get: "get-prompt-template",
|
|
101
|
+
prompt_set: "set-prompt-template",
|
|
102
|
+
prompt_delete: "delete-prompt-template", // destructive
|
|
103
|
+
prompt_preview: "preview-prompt-template",
|
|
104
|
+
|
|
105
|
+
// ── tracker ──
|
|
106
|
+
tracker_status: "tracker-status",
|
|
107
|
+
tracker_syncStatus: "tracker-sync-status",
|
|
108
|
+
tracker_linkTask: "tracker-link-task",
|
|
109
|
+
tracker_unlink: "tracker-unlink", // destructive
|
|
110
|
+
tracker_mapAgent: "tracker-map-agent",
|
|
111
|
+
|
|
112
|
+
// ── skills ──
|
|
113
|
+
skill_list: "skill-list",
|
|
114
|
+
skill_get: "skill-get",
|
|
115
|
+
skill_search: "skill-search",
|
|
116
|
+
skill_create: "skill-create",
|
|
117
|
+
skill_update: "skill-update",
|
|
118
|
+
skill_delete: "skill-delete", // destructive
|
|
119
|
+
skill_install: "skill-install",
|
|
120
|
+
skill_uninstall: "skill-uninstall", // destructive
|
|
121
|
+
skill_publish: "skill-publish",
|
|
122
|
+
|
|
123
|
+
// ── mcp servers ──
|
|
124
|
+
mcpServer_list: "mcp-server-list",
|
|
125
|
+
mcpServer_get: "mcp-server-get",
|
|
126
|
+
mcpServer_create: "mcp-server-create",
|
|
127
|
+
mcpServer_update: "mcp-server-update",
|
|
128
|
+
mcpServer_delete: "mcp-server-delete", // destructive
|
|
129
|
+
mcpServer_install: "mcp-server-install",
|
|
130
|
+
mcpServer_uninstall: "mcp-server-uninstall", // destructive
|
|
131
|
+
|
|
132
|
+
// ── pages & metrics ──
|
|
133
|
+
page_create: "create_page",
|
|
134
|
+
metric_create: "create_metric",
|
|
135
|
+
|
|
136
|
+
// ── human input ──
|
|
137
|
+
request_humanInput: "request-human-input",
|
|
17
138
|
} as const;
|
|
18
139
|
|
|
19
140
|
export const SDK_ALLOWLIST = Object.keys(SDK_TOOL_NAME_MAP) as Array<
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { scrubObject } from "../utils/secret-scrubber";
|
|
2
2
|
import { Redacted } from "./redacted";
|
|
3
|
-
import { isSdkToolAllowed } from "./sdk-allowlist";
|
|
3
|
+
import { isSdkToolAllowed, mcpToolNameForSdkMethod } from "./sdk-allowlist";
|
|
4
4
|
import type { SwarmConfig } from "./swarm-config";
|
|
5
5
|
|
|
6
6
|
type BridgeRequest = {
|
|
@@ -43,9 +43,14 @@ function kvPath(args: Record<string, unknown>, keyRequired = true): string {
|
|
|
43
43
|
return key ? `/api/kv/${encodeURIComponent(key)}` : "/api/kv";
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
/**
|
|
47
|
+
* Maps SDK method names to specific REST endpoints where they exist.
|
|
48
|
+
* Returns null for tools that should fall through to the generic MCP bridge.
|
|
49
|
+
*/
|
|
50
|
+
function bridgeRequestFor(name: string, args: unknown): BridgeRequest | null {
|
|
47
51
|
const body = argsRecord(args);
|
|
48
52
|
switch (name) {
|
|
53
|
+
// ── memory ──
|
|
49
54
|
case "memory_search":
|
|
50
55
|
return { method: "POST", path: "/api/memory/search", body };
|
|
51
56
|
case "memory_get": {
|
|
@@ -67,6 +72,13 @@ function bridgeRequestFor(name: string, args: unknown): BridgeRequest {
|
|
|
67
72
|
};
|
|
68
73
|
return { method: "POST", path: "/api/memory/rate", body: { events: [event] } };
|
|
69
74
|
}
|
|
75
|
+
case "memory_delete": {
|
|
76
|
+
const id = typeof body.id === "string" ? body.id : undefined;
|
|
77
|
+
if (!id) throw new Error("memory_delete requires string `id`");
|
|
78
|
+
return { method: "DELETE", path: `/api/memory/${encodeURIComponent(id)}` };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// ── tasks ──
|
|
70
82
|
case "task_list":
|
|
71
83
|
return { method: "GET", path: appendQuery("/api/tasks", body) };
|
|
72
84
|
case "task_get": {
|
|
@@ -94,6 +106,13 @@ function bridgeRequestFor(name: string, args: unknown): BridgeRequest {
|
|
|
94
106
|
body: { progress: body.progress ?? "" },
|
|
95
107
|
};
|
|
96
108
|
}
|
|
109
|
+
case "task_cancel": {
|
|
110
|
+
const taskId = typeof body.taskId === "string" ? body.taskId : undefined;
|
|
111
|
+
if (!taskId) throw new Error("task_cancel requires string `taskId`");
|
|
112
|
+
return { method: "POST", path: `/api/tasks/${encodeURIComponent(taskId)}/cancel` };
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// ── kv ──
|
|
97
116
|
case "kv_get":
|
|
98
117
|
return { method: "GET", path: kvPath(body) };
|
|
99
118
|
case "kv_set":
|
|
@@ -119,11 +138,15 @@ function bridgeRequestFor(name: string, args: unknown): BridgeRequest {
|
|
|
119
138
|
offset: body.offset,
|
|
120
139
|
}),
|
|
121
140
|
};
|
|
141
|
+
|
|
142
|
+
// ── repos ──
|
|
122
143
|
case "repo_list":
|
|
123
144
|
return {
|
|
124
145
|
method: "GET",
|
|
125
146
|
path: appendQuery("/api/repos", { autoClone: body.autoClone, name: body.name }),
|
|
126
147
|
};
|
|
148
|
+
|
|
149
|
+
// ── schedules ──
|
|
127
150
|
case "schedule_list":
|
|
128
151
|
return {
|
|
129
152
|
method: "GET",
|
|
@@ -134,12 +157,164 @@ function bridgeRequestFor(name: string, args: unknown): BridgeRequest {
|
|
|
134
157
|
hideCompleted: body.hideCompleted,
|
|
135
158
|
}),
|
|
136
159
|
};
|
|
160
|
+
|
|
161
|
+
// ── scripts ──
|
|
137
162
|
case "script_search":
|
|
138
163
|
return { method: "POST", path: "/api/scripts/search", body };
|
|
139
164
|
case "script_run":
|
|
140
165
|
return { method: "POST", path: "/api/scripts/run", body };
|
|
166
|
+
|
|
167
|
+
// ── swarm / agent ──
|
|
168
|
+
case "db_query":
|
|
169
|
+
return { method: "POST", path: "/api/db-query", body };
|
|
170
|
+
case "swarm_get":
|
|
171
|
+
return {
|
|
172
|
+
method: "GET",
|
|
173
|
+
path: appendQuery("/api/agents", {
|
|
174
|
+
fields: body.includeFull ? "full" : "slim",
|
|
175
|
+
}),
|
|
176
|
+
};
|
|
177
|
+
case "agent_info":
|
|
178
|
+
return { method: "GET", path: "/me" };
|
|
179
|
+
case "metrics_get":
|
|
180
|
+
return { method: "GET", path: "/api/metrics" };
|
|
181
|
+
case "task_poll":
|
|
182
|
+
return { method: "GET", path: "/api/poll" };
|
|
183
|
+
|
|
184
|
+
// ── config ──
|
|
185
|
+
case "config_get":
|
|
186
|
+
return {
|
|
187
|
+
method: "GET",
|
|
188
|
+
path: appendQuery("/api/config/resolved", {
|
|
189
|
+
agentId: body.agentId,
|
|
190
|
+
repoId: body.repoId,
|
|
191
|
+
key: body.key,
|
|
192
|
+
includeSecrets: body.includeSecrets,
|
|
193
|
+
}),
|
|
194
|
+
};
|
|
195
|
+
case "config_list":
|
|
196
|
+
return {
|
|
197
|
+
method: "GET",
|
|
198
|
+
path: appendQuery("/api/config", {
|
|
199
|
+
scope: body.scope,
|
|
200
|
+
scopeId: body.scopeId,
|
|
201
|
+
key: body.key,
|
|
202
|
+
includeSecrets: body.includeSecrets,
|
|
203
|
+
}),
|
|
204
|
+
};
|
|
205
|
+
case "config_delete": {
|
|
206
|
+
const id = typeof body.id === "string" ? body.id : undefined;
|
|
207
|
+
if (!id) throw new Error("config_delete requires string `id`");
|
|
208
|
+
return { method: "DELETE", path: `/api/config/${encodeURIComponent(id)}` };
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// ── services ──
|
|
212
|
+
case "service_list":
|
|
213
|
+
return {
|
|
214
|
+
method: "GET",
|
|
215
|
+
path: appendQuery("/api/services", {
|
|
216
|
+
agentId: body.agentId,
|
|
217
|
+
name: body.name,
|
|
218
|
+
status: body.status,
|
|
219
|
+
}),
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
// ── workflows ──
|
|
223
|
+
case "workflow_list":
|
|
224
|
+
return {
|
|
225
|
+
method: "GET",
|
|
226
|
+
path: appendQuery("/api/workflows", {
|
|
227
|
+
fields: body.includeFull ? "full" : "slim",
|
|
228
|
+
}),
|
|
229
|
+
};
|
|
230
|
+
case "workflow_get": {
|
|
231
|
+
const id = typeof body.id === "string" ? body.id : undefined;
|
|
232
|
+
if (!id) throw new Error("workflow_get requires string `id`");
|
|
233
|
+
return { method: "GET", path: `/api/workflows/${encodeURIComponent(id)}` };
|
|
234
|
+
}
|
|
235
|
+
case "workflow_listRuns": {
|
|
236
|
+
const wfId = typeof body.workflowId === "string" ? body.workflowId : undefined;
|
|
237
|
+
if (!wfId) throw new Error("workflow_listRuns requires string `workflowId`");
|
|
238
|
+
return {
|
|
239
|
+
method: "GET",
|
|
240
|
+
path: appendQuery(`/api/workflows/${encodeURIComponent(wfId)}/runs`, {
|
|
241
|
+
status: body.status,
|
|
242
|
+
}),
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
case "workflow_getRun": {
|
|
246
|
+
const id = typeof body.id === "string" ? body.id : undefined;
|
|
247
|
+
if (!id) throw new Error("workflow_getRun requires string `id`");
|
|
248
|
+
return { method: "GET", path: `/api/workflow-runs/${encodeURIComponent(id)}` };
|
|
249
|
+
}
|
|
250
|
+
case "workflow_delete": {
|
|
251
|
+
const id = typeof body.id === "string" ? body.id : undefined;
|
|
252
|
+
if (!id) throw new Error("workflow_delete requires string `id`");
|
|
253
|
+
return { method: "DELETE", path: `/api/workflows/${encodeURIComponent(id)}` };
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// ── prompt templates ──
|
|
257
|
+
case "prompt_list":
|
|
258
|
+
return {
|
|
259
|
+
method: "GET",
|
|
260
|
+
path: appendQuery("/api/prompt-templates", {
|
|
261
|
+
eventType: body.eventType,
|
|
262
|
+
scope: body.scope,
|
|
263
|
+
scopeId: body.scopeId,
|
|
264
|
+
isDefault: body.isDefault,
|
|
265
|
+
}),
|
|
266
|
+
};
|
|
267
|
+
case "prompt_get": {
|
|
268
|
+
const id = typeof body.id === "string" ? body.id : undefined;
|
|
269
|
+
if (!id) throw new Error("prompt_get requires string `id`");
|
|
270
|
+
return { method: "GET", path: `/api/prompt-templates/${encodeURIComponent(id)}` };
|
|
271
|
+
}
|
|
272
|
+
case "prompt_delete": {
|
|
273
|
+
const id = typeof body.id === "string" ? body.id : undefined;
|
|
274
|
+
if (!id) throw new Error("prompt_delete requires string `id`");
|
|
275
|
+
return { method: "DELETE", path: `/api/prompt-templates/${encodeURIComponent(id)}` };
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// ── skills ──
|
|
279
|
+
case "skill_list":
|
|
280
|
+
return {
|
|
281
|
+
method: "GET",
|
|
282
|
+
path: appendQuery("/api/skills", {
|
|
283
|
+
scope: body.scope,
|
|
284
|
+
scopeId: body.scopeId,
|
|
285
|
+
includeBuiltin: body.includeBuiltin,
|
|
286
|
+
}),
|
|
287
|
+
};
|
|
288
|
+
case "skill_get": {
|
|
289
|
+
const id = typeof body.id === "string" ? body.id : undefined;
|
|
290
|
+
if (!id) throw new Error("skill_get requires string `id`");
|
|
291
|
+
return { method: "GET", path: `/api/skills/${encodeURIComponent(id)}` };
|
|
292
|
+
}
|
|
293
|
+
case "skill_search":
|
|
294
|
+
return { method: "POST", path: "/api/skills/search", body };
|
|
295
|
+
case "skill_delete": {
|
|
296
|
+
const id = typeof body.id === "string" ? body.id : undefined;
|
|
297
|
+
if (!id) throw new Error("skill_delete requires string `id`");
|
|
298
|
+
return { method: "DELETE", path: `/api/skills/${encodeURIComponent(id)}` };
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// ── mcp servers ──
|
|
302
|
+
case "mcpServer_list":
|
|
303
|
+
return { method: "GET", path: "/api/mcp-servers" };
|
|
304
|
+
case "mcpServer_get": {
|
|
305
|
+
const id = typeof body.id === "string" ? body.id : undefined;
|
|
306
|
+
if (!id) throw new Error("mcpServer_get requires string `id`");
|
|
307
|
+
return { method: "GET", path: `/api/mcp-servers/${encodeURIComponent(id)}` };
|
|
308
|
+
}
|
|
309
|
+
case "mcpServer_delete": {
|
|
310
|
+
const id = typeof body.id === "string" ? body.id : undefined;
|
|
311
|
+
if (!id) throw new Error("mcpServer_delete requires string `id`");
|
|
312
|
+
return { method: "DELETE", path: `/api/mcp-servers/${encodeURIComponent(id)}` };
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// ── fallthrough: proxy via generic MCP bridge ──
|
|
141
316
|
default:
|
|
142
|
-
|
|
317
|
+
return null;
|
|
143
318
|
}
|
|
144
319
|
}
|
|
145
320
|
|
|
@@ -152,6 +327,26 @@ async function callBridgeApi(
|
|
|
152
327
|
const baseUrl = Redacted.value(config.mcpBaseUrl).replace(/\/$/, "");
|
|
153
328
|
const request = bridgeRequestFor(name, args);
|
|
154
329
|
|
|
330
|
+
// Tools without a specific REST route go through the generic MCP bridge
|
|
331
|
+
if (!request) {
|
|
332
|
+
const mcpToolName = mcpToolNameForSdkMethod(name);
|
|
333
|
+
const res = await fetch(`${baseUrl}/api/mcp-bridge`, {
|
|
334
|
+
method: "POST",
|
|
335
|
+
headers: headers(config),
|
|
336
|
+
body: JSON.stringify({ tool: mcpToolName, args: args ?? {} }),
|
|
337
|
+
});
|
|
338
|
+
const text = await res.text();
|
|
339
|
+
const data = text ? JSON.parse(text) : {};
|
|
340
|
+
if (!res.ok && options.throwOnError) {
|
|
341
|
+
const message =
|
|
342
|
+
data && typeof data === "object" && "error" in data
|
|
343
|
+
? String((data as { error: unknown }).error)
|
|
344
|
+
: `api failed with ${res.status}`;
|
|
345
|
+
throw new Error(`swarm-sdk: ${name} failed with ${res.status}: ${message}`);
|
|
346
|
+
}
|
|
347
|
+
return scrubObject({ success: res.ok, status: res.status, data });
|
|
348
|
+
}
|
|
349
|
+
|
|
155
350
|
const res = await fetch(`${baseUrl}${request.path}`, {
|
|
156
351
|
method: request.method,
|
|
157
352
|
headers: headers(config),
|