@desplega.ai/agent-swarm 1.87.0 → 1.89.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 +5 -1
- package/openapi.json +53 -1
- package/package.json +6 -5
- package/plugin/skills/composio/SKILL.md +98 -0
- package/src/be/db.ts +374 -9
- package/src/be/migrations/080_skill_system_defaults.sql +8 -0
- package/src/be/migrations/081_metrics.sql +39 -0
- package/src/be/migrations/082_user_audit_fields.sql +120 -0
- package/src/be/modelsdev-cache.json +3825 -2417
- package/src/be/seed/registry.ts +3 -2
- package/src/be/seed-skills/index.ts +179 -0
- package/src/cli.tsx +51 -4
- package/src/commands/e2b-stack-wizard.tsx +394 -0
- package/src/commands/e2b.ts +1352 -53
- package/src/commands/onboard/dashboard-url.ts +29 -0
- package/src/commands/onboard/steps/post-dashboard.tsx +3 -1
- package/src/commands/onboard.tsx +3 -1
- package/src/commands/runner.ts +154 -22
- package/src/commands/x.ts +118 -0
- package/src/e2b/dispatch.ts +234 -18
- package/src/github/handlers.ts +40 -1
- package/src/heartbeat/heartbeat.ts +26 -5
- package/src/http/active-sessions.ts +32 -1
- package/src/http/auth.ts +36 -0
- package/src/http/core.ts +20 -16
- package/src/http/db-query.ts +20 -0
- package/src/http/index.ts +2 -0
- package/src/http/memory.ts +13 -1
- package/src/http/metrics.ts +447 -0
- package/src/http/operator-actor.ts +9 -0
- package/src/http/poll.ts +11 -1
- package/src/http/skills.ts +53 -0
- package/src/http/tasks.ts +4 -1
- package/src/http/webhooks.ts +75 -0
- package/src/http/workflows.ts +5 -1
- package/src/integrations/kapso/client.ts +82 -0
- package/src/memory/automatic-task-gate.ts +47 -0
- package/src/metrics/version.ts +26 -0
- package/src/prompts/base-prompt.ts +24 -1
- package/src/prompts/session-templates.ts +74 -0
- package/src/providers/claude-adapter.ts +19 -0
- package/src/providers/codex-adapter.ts +22 -0
- package/src/providers/ctx-mode-env.ts +10 -0
- package/src/providers/opencode-adapter.ts +72 -7
- package/src/server.ts +10 -1
- package/src/slack/blocks.ts +12 -4
- package/src/slack/watcher.ts +3 -3
- package/src/telemetry.ts +14 -1
- package/src/templates.d.ts +4 -0
- package/src/tests/base-prompt.test.ts +76 -0
- package/src/tests/budget-claim-gate.test.ts +26 -0
- package/src/tests/claude-adapter.test.ts +86 -1
- package/src/tests/codex-adapter.test.ts +89 -0
- package/src/tests/core-auth.test.ts +8 -1
- package/src/tests/e2b-dispatch.test.ts +603 -11
- package/src/tests/events-http.test.ts +6 -2
- package/src/tests/github-handlers-cancel-config.test.ts +262 -0
- package/src/tests/heartbeat.test.ts +84 -3
- package/src/tests/http-api-integration.test.ts +116 -1
- package/src/tests/kapso-client.test.ts +74 -1
- package/src/tests/kapso-inbound.test.ts +60 -2
- package/src/tests/metrics-http.test.ts +247 -0
- package/src/tests/opencode-adapter.test.ts +185 -30
- package/src/tests/prompt-template-session.test.ts +4 -2
- package/src/tests/runner-repo-autostash.test.ts +117 -0
- package/src/tests/runner-requester-profile.test.ts +25 -0
- package/src/tests/runner-skills-refresh.test.ts +1 -1
- package/src/tests/self-improvement.test.ts +89 -0
- package/src/tests/skill-update-scope.test.ts +88 -1
- package/src/tests/slack-blocks.test.ts +15 -0
- package/src/tests/swarm-x-tool.test.ts +90 -0
- package/src/tests/system-default-skills.test.ts +122 -0
- package/src/tests/telemetry-init.test.ts +86 -0
- package/src/tests/ui-logs-parser.test.ts +271 -0
- package/src/tests/user-token-rest-auth.test.ts +129 -0
- package/src/tests/workflow-async-v2.test.ts +23 -0
- package/src/tests/x-composio.test.ts +122 -0
- package/src/tools/create-metric.ts +191 -0
- package/src/tools/skills/skill-delete.ts +14 -0
- package/src/tools/skills/skill-update.ts +14 -0
- package/src/tools/store-progress.ts +19 -5
- package/src/tools/swarm-x.ts +116 -0
- package/src/tools/tool-config.ts +6 -0
- package/src/types.ts +121 -0
- package/src/utils/request-auth-context.ts +28 -0
- package/src/utils/skills-refresh.ts +2 -2
- package/src/workflows/engine.ts +24 -2
- package/src/workflows/executors/agent-task.ts +2 -0
- package/src/x/composio.ts +295 -0
- package/templates/skills/artifacts/config.json +1 -0
- package/templates/skills/attio-interaction/SKILL.md +279 -0
- package/templates/skills/attio-interaction/config.json +14 -0
- package/templates/skills/attio-interaction/content.md +272 -0
- package/templates/skills/kv-storage/config.json +1 -0
- package/templates/skills/pages/config.json +1 -0
- package/templates/skills/scheduled-task-resilience/config.json +1 -0
- package/templates/skills/swarm-scripts/SKILL.md +91 -0
- package/templates/skills/swarm-scripts/config.json +14 -0
- package/templates/skills/swarm-scripts/content.md +86 -0
- package/templates/skills/workflow-iterate/config.json +1 -0
- package/templates/skills/workflow-structured-output/config.json +1 -0
- package/tsconfig.json +2 -1
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import * as z from "zod";
|
|
3
|
+
import { createMetric, getMetric, getMetricBySlug, getMetricVersions, updateMetric } from "@/be/db";
|
|
4
|
+
import { assertSelectOnlyQuery } from "@/http/db-query";
|
|
5
|
+
import { snapshotMetric } from "@/metrics/version";
|
|
6
|
+
import { createToolRegistrar } from "@/tools/utils";
|
|
7
|
+
import { MetricDefinitionSchema } from "@/types";
|
|
8
|
+
|
|
9
|
+
function slugify(input: string): string {
|
|
10
|
+
const slug = input
|
|
11
|
+
.toLowerCase()
|
|
12
|
+
.normalize("NFKD")
|
|
13
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
14
|
+
.replace(/^-+|-+$/g, "");
|
|
15
|
+
return slug || "metric";
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function getAppBaseUrl(): string {
|
|
19
|
+
const env = process.env.APP_URL?.trim();
|
|
20
|
+
if (env) return env.replace(/\/+$/, "");
|
|
21
|
+
return "http://localhost:5274";
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function metricEditCounter(metricId: string): number {
|
|
25
|
+
const versions = getMetricVersions(metricId);
|
|
26
|
+
return versions.length > 0 ? versions[0]!.version + 1 : 1;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export const registerCreateMetricTool = (server: McpServer) => {
|
|
30
|
+
createToolRegistrar(server)(
|
|
31
|
+
"create_metric",
|
|
32
|
+
{
|
|
33
|
+
title: "Create or update a metric",
|
|
34
|
+
description:
|
|
35
|
+
"Stores a config-driven dashboard backed by read-only SQL widget queries. " +
|
|
36
|
+
"Calls are upsert-by-(agent, slug), mirroring create_page: same slug updates " +
|
|
37
|
+
"the existing dashboard and snapshots the prior JSON definition.",
|
|
38
|
+
annotations: { destructiveHint: false },
|
|
39
|
+
inputSchema: z.object({
|
|
40
|
+
title: z.string().min(1).describe("Human-readable dashboard title."),
|
|
41
|
+
slug: z
|
|
42
|
+
.string()
|
|
43
|
+
.min(1)
|
|
44
|
+
.optional()
|
|
45
|
+
.describe("URL-safe slug. Defaults to the kebab-cased title."),
|
|
46
|
+
description: z.string().optional().describe("Short description shown in the dashboard."),
|
|
47
|
+
definition: MetricDefinitionSchema.describe(
|
|
48
|
+
"Dashboard JSON definition: a list of widgets, each with SELECT/WITH SQL and viz config.",
|
|
49
|
+
),
|
|
50
|
+
}),
|
|
51
|
+
outputSchema: z.object({
|
|
52
|
+
yourAgentId: z.string(),
|
|
53
|
+
id: z.string(),
|
|
54
|
+
version: z.number(),
|
|
55
|
+
app_url: z.string(),
|
|
56
|
+
success: z.boolean().optional(),
|
|
57
|
+
message: z.string().optional(),
|
|
58
|
+
}),
|
|
59
|
+
},
|
|
60
|
+
async (input, requestInfo, _meta) => {
|
|
61
|
+
if (!requestInfo.agentId) {
|
|
62
|
+
const msg = "Agent ID required. Set the X-Agent-ID header on the MCP request.";
|
|
63
|
+
return {
|
|
64
|
+
content: [{ type: "text", text: msg }],
|
|
65
|
+
structuredContent: {
|
|
66
|
+
yourAgentId: "",
|
|
67
|
+
id: "",
|
|
68
|
+
version: 0,
|
|
69
|
+
app_url: "",
|
|
70
|
+
success: false,
|
|
71
|
+
message: msg,
|
|
72
|
+
},
|
|
73
|
+
isError: true,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
try {
|
|
78
|
+
for (const widget of input.definition.widgets) {
|
|
79
|
+
assertSelectOnlyQuery(widget.query.sql);
|
|
80
|
+
}
|
|
81
|
+
} catch (err) {
|
|
82
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
83
|
+
return {
|
|
84
|
+
content: [{ type: "text", text: `Metric query rejected: ${msg}` }],
|
|
85
|
+
structuredContent: {
|
|
86
|
+
yourAgentId: requestInfo.agentId,
|
|
87
|
+
id: "",
|
|
88
|
+
version: 0,
|
|
89
|
+
app_url: "",
|
|
90
|
+
success: false,
|
|
91
|
+
message: msg,
|
|
92
|
+
},
|
|
93
|
+
isError: true,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const slug = input.slug ?? slugify(input.title);
|
|
98
|
+
const existing = getMetricBySlug(requestInfo.agentId, slug);
|
|
99
|
+
let id: string;
|
|
100
|
+
|
|
101
|
+
if (existing) {
|
|
102
|
+
try {
|
|
103
|
+
snapshotMetric(existing.id, requestInfo.agentId);
|
|
104
|
+
} catch {
|
|
105
|
+
// Snapshot failure should not block updates.
|
|
106
|
+
}
|
|
107
|
+
const updated = updateMetric(existing.id, {
|
|
108
|
+
title: input.title,
|
|
109
|
+
description: input.description,
|
|
110
|
+
definition: input.definition,
|
|
111
|
+
});
|
|
112
|
+
if (!updated) {
|
|
113
|
+
const msg = `Failed to update existing metric ${existing.id}.`;
|
|
114
|
+
return {
|
|
115
|
+
content: [{ type: "text", text: msg }],
|
|
116
|
+
structuredContent: {
|
|
117
|
+
yourAgentId: requestInfo.agentId,
|
|
118
|
+
id: existing.id,
|
|
119
|
+
version: 0,
|
|
120
|
+
app_url: "",
|
|
121
|
+
success: false,
|
|
122
|
+
message: msg,
|
|
123
|
+
},
|
|
124
|
+
isError: true,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
id = updated.id;
|
|
128
|
+
} else {
|
|
129
|
+
try {
|
|
130
|
+
const created = createMetric({
|
|
131
|
+
agentId: requestInfo.agentId,
|
|
132
|
+
slug,
|
|
133
|
+
title: input.title,
|
|
134
|
+
description: input.description,
|
|
135
|
+
definition: input.definition,
|
|
136
|
+
});
|
|
137
|
+
id = created.id;
|
|
138
|
+
} catch (err) {
|
|
139
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
140
|
+
const msg = `Failed to create metric: ${detail}`;
|
|
141
|
+
return {
|
|
142
|
+
content: [{ type: "text", text: msg }],
|
|
143
|
+
structuredContent: {
|
|
144
|
+
yourAgentId: requestInfo.agentId,
|
|
145
|
+
id: "",
|
|
146
|
+
version: 0,
|
|
147
|
+
app_url: "",
|
|
148
|
+
success: false,
|
|
149
|
+
message: msg,
|
|
150
|
+
},
|
|
151
|
+
isError: true,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const fresh = getMetric(id);
|
|
157
|
+
if (!fresh) {
|
|
158
|
+
const msg = `Metric ${id} disappeared between write and read.`;
|
|
159
|
+
return {
|
|
160
|
+
content: [{ type: "text", text: msg }],
|
|
161
|
+
structuredContent: {
|
|
162
|
+
yourAgentId: requestInfo.agentId,
|
|
163
|
+
id,
|
|
164
|
+
version: 0,
|
|
165
|
+
app_url: "",
|
|
166
|
+
success: false,
|
|
167
|
+
message: msg,
|
|
168
|
+
},
|
|
169
|
+
isError: true,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const version = metricEditCounter(id);
|
|
174
|
+
const appUrl = `${getAppBaseUrl()}/usage/metrics`;
|
|
175
|
+
return {
|
|
176
|
+
content: [
|
|
177
|
+
{
|
|
178
|
+
type: "text",
|
|
179
|
+
text: `Metric "${input.title}" saved (slug=${slug}, version=${version}).\n App: ${appUrl}`,
|
|
180
|
+
},
|
|
181
|
+
],
|
|
182
|
+
structuredContent: {
|
|
183
|
+
yourAgentId: requestInfo.agentId,
|
|
184
|
+
id,
|
|
185
|
+
version,
|
|
186
|
+
app_url: appUrl,
|
|
187
|
+
},
|
|
188
|
+
};
|
|
189
|
+
},
|
|
190
|
+
);
|
|
191
|
+
};
|
|
@@ -3,6 +3,9 @@ import * as z from "zod";
|
|
|
3
3
|
import { deleteSkill, getAgentById, getSkillById } from "@/be/db";
|
|
4
4
|
import { createToolRegistrar } from "@/tools/utils";
|
|
5
5
|
|
|
6
|
+
const SYSTEM_DEFAULT_SKILL_LOCKED_MESSAGE =
|
|
7
|
+
"This skill is system-managed and cannot be edited from the UI; it is re-seeded on each start. Fork it under a new name to customize.";
|
|
8
|
+
|
|
6
9
|
export const registerSkillDeleteTool = (server: McpServer) => {
|
|
7
10
|
createToolRegistrar(server)(
|
|
8
11
|
"skill-delete",
|
|
@@ -51,6 +54,17 @@ export const registerSkillDeleteTool = (server: McpServer) => {
|
|
|
51
54
|
};
|
|
52
55
|
}
|
|
53
56
|
|
|
57
|
+
if (existing.systemDefault) {
|
|
58
|
+
return {
|
|
59
|
+
content: [{ type: "text", text: SYSTEM_DEFAULT_SKILL_LOCKED_MESSAGE }],
|
|
60
|
+
structuredContent: {
|
|
61
|
+
yourAgentId: requestInfo.agentId,
|
|
62
|
+
success: false,
|
|
63
|
+
message: SYSTEM_DEFAULT_SKILL_LOCKED_MESSAGE,
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
54
68
|
const deleted = deleteSkill(args.skillId);
|
|
55
69
|
return {
|
|
56
70
|
content: [
|
|
@@ -4,6 +4,9 @@ import { getAgentById, getSkillById, updateSkill } from "@/be/db";
|
|
|
4
4
|
import { parseSkillContent } from "@/be/skill-parser";
|
|
5
5
|
import { createToolRegistrar } from "@/tools/utils";
|
|
6
6
|
|
|
7
|
+
const SYSTEM_DEFAULT_SKILL_LOCKED_MESSAGE =
|
|
8
|
+
"This skill is system-managed and cannot be edited from the UI; it is re-seeded on each start. Fork it under a new name to customize.";
|
|
9
|
+
|
|
7
10
|
export const registerSkillUpdateTool = (server: McpServer) => {
|
|
8
11
|
createToolRegistrar(server)(
|
|
9
12
|
"skill-update",
|
|
@@ -77,6 +80,17 @@ export const registerSkillUpdateTool = (server: McpServer) => {
|
|
|
77
80
|
};
|
|
78
81
|
}
|
|
79
82
|
|
|
83
|
+
if (existing.systemDefault && (args.content !== undefined || args.scope !== undefined)) {
|
|
84
|
+
return {
|
|
85
|
+
content: [{ type: "text", text: SYSTEM_DEFAULT_SKILL_LOCKED_MESSAGE }],
|
|
86
|
+
structuredContent: {
|
|
87
|
+
yourAgentId: requestInfo.agentId,
|
|
88
|
+
success: false,
|
|
89
|
+
message: SYSTEM_DEFAULT_SKILL_LOCKED_MESSAGE,
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
80
94
|
const updates: Parameters<typeof updateSkill>[1] = {};
|
|
81
95
|
|
|
82
96
|
if (args.content !== undefined) {
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
import { getEmbeddingProvider, getMemoryStore } from "@/be/memory";
|
|
17
17
|
import { getRetrievalsForTask } from "@/be/memory/raters/retrieval";
|
|
18
18
|
import { runServerRaters } from "@/be/memory/raters/run-server-raters";
|
|
19
|
+
import { shouldPersistTaskCompletionMemory } from "@/memory/automatic-task-gate";
|
|
19
20
|
import { createWorkerTaskFollowUp } from "@/tasks/worker-follow-up";
|
|
20
21
|
import { createToolRegistrar } from "@/tools/utils";
|
|
21
22
|
import { AgentTaskSchema, AttachmentInputSchema, isTerminalTaskStatus } from "@/types";
|
|
@@ -56,6 +57,12 @@ export const registerStoreProgressTool = (server: McpServer) => {
|
|
|
56
57
|
.describe(
|
|
57
58
|
"Pointer-based artifacts produced by this step — agent-fs path, URL, shared-fs path, or swarm Page. No inline file data; upload to agent-fs first and attach by path. May be sent on any call (progress or completion) and accumulates across calls; duplicates are de-duped by sha256 (when present) or by (kind, pointer, name).",
|
|
58
59
|
),
|
|
60
|
+
persistMemory: z
|
|
61
|
+
.boolean()
|
|
62
|
+
.optional()
|
|
63
|
+
.describe(
|
|
64
|
+
"Opt in to task_completion memory persistence for automatic/recurring tasks. Manual tasks are persisted by default; scheduled, system, heartbeat/boot-triage, monitor, and digest tasks are skipped unless this is true.",
|
|
65
|
+
),
|
|
59
66
|
// Phase 11: `costData` removed. The harness adapter is the sole
|
|
60
67
|
// writer of `session_costs` (see POST /api/session-costs in the
|
|
61
68
|
// runner). If a payload still includes the field, Zod's
|
|
@@ -75,7 +82,7 @@ export const registerStoreProgressTool = (server: McpServer) => {
|
|
|
75
82
|
}),
|
|
76
83
|
},
|
|
77
84
|
async (
|
|
78
|
-
{ taskId, progress, status, output, failureReason, attachments },
|
|
85
|
+
{ taskId, progress, status, output, failureReason, attachments, persistMemory },
|
|
79
86
|
requestInfo,
|
|
80
87
|
_meta,
|
|
81
88
|
) => {
|
|
@@ -320,14 +327,19 @@ export const registerStoreProgressTool = (server: McpServer) => {
|
|
|
320
327
|
|
|
321
328
|
const result = txn();
|
|
322
329
|
|
|
330
|
+
const shouldRunTerminalSideEffects =
|
|
331
|
+
(status === "completed" || status === "failed") &&
|
|
332
|
+
result.success &&
|
|
333
|
+
result.task &&
|
|
334
|
+
!("wasNoOp" in result && result.wasNoOp);
|
|
335
|
+
|
|
323
336
|
// Index completed and failed tasks as memory (async, non-blocking).
|
|
324
337
|
// Skip on no-op (idempotent re-call on terminal task) to avoid duplicate
|
|
325
338
|
// memory entries / vector index pollution.
|
|
339
|
+
// Automatic/recurring tasks are noisy by default; require explicit opt-in.
|
|
326
340
|
if (
|
|
327
|
-
|
|
328
|
-
result.
|
|
329
|
-
result.task &&
|
|
330
|
-
!("wasNoOp" in result && result.wasNoOp)
|
|
341
|
+
shouldRunTerminalSideEffects &&
|
|
342
|
+
shouldPersistTaskCompletionMemory(result.task, persistMemory)
|
|
331
343
|
) {
|
|
332
344
|
(async () => {
|
|
333
345
|
try {
|
|
@@ -384,7 +396,9 @@ export const registerStoreProgressTool = (server: McpServer) => {
|
|
|
384
396
|
// Non-blocking — task completion memory failure should not affect task status
|
|
385
397
|
}
|
|
386
398
|
})();
|
|
399
|
+
}
|
|
387
400
|
|
|
401
|
+
if (shouldRunTerminalSideEffects) {
|
|
388
402
|
// Memory rater v1.5 — fire server-side raters on task completion.
|
|
389
403
|
// Plan: thoughts/taras/plans/2026-05-05-memory-rater-v1.5/step-2.md §5
|
|
390
404
|
//
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import * as z from "zod";
|
|
3
|
+
import { COMPOSIO_HTTP_METHODS, composioArgsFromParts, executeComposioRequest } from "@/x/composio";
|
|
4
|
+
import { createToolRegistrar } from "./utils";
|
|
5
|
+
|
|
6
|
+
const primitiveQueryValueSchema = z.union([z.string(), z.number(), z.boolean()]);
|
|
7
|
+
|
|
8
|
+
export const registerSwarmXTool = (server: McpServer) => {
|
|
9
|
+
createToolRegistrar(server)(
|
|
10
|
+
"swarm_x",
|
|
11
|
+
{
|
|
12
|
+
title: "Swarm X",
|
|
13
|
+
description:
|
|
14
|
+
"Execute an Agent Swarm external command route. v1 supports target='composio' and mirrors `agent-swarm x composio <method> <path>` with the Composio API key injected server-side.",
|
|
15
|
+
annotations: { openWorldHint: true },
|
|
16
|
+
inputSchema: z.object({
|
|
17
|
+
target: z
|
|
18
|
+
.literal("composio")
|
|
19
|
+
.default("composio")
|
|
20
|
+
.describe("External route target. Only 'composio' is supported in v1."),
|
|
21
|
+
method: z.enum(COMPOSIO_HTTP_METHODS).describe("HTTP method to route to Composio."),
|
|
22
|
+
path: z
|
|
23
|
+
.string()
|
|
24
|
+
.min(1)
|
|
25
|
+
.describe(
|
|
26
|
+
"Composio API path relative to the configured base URL, e.g. /tool_router/session.",
|
|
27
|
+
),
|
|
28
|
+
body: z.unknown().optional().describe("Optional JSON request body."),
|
|
29
|
+
query: z
|
|
30
|
+
.record(z.string(), primitiveQueryValueSchema)
|
|
31
|
+
.optional()
|
|
32
|
+
.describe("Optional query parameters appended to the Composio path."),
|
|
33
|
+
headers: z
|
|
34
|
+
.record(z.string(), z.string())
|
|
35
|
+
.optional()
|
|
36
|
+
.describe("Optional extra headers. Auth headers are injected by the server."),
|
|
37
|
+
baseUrl: z.string().url().optional().describe("Optional Composio API base URL override."),
|
|
38
|
+
useOrgKey: z
|
|
39
|
+
.boolean()
|
|
40
|
+
.default(false)
|
|
41
|
+
.describe(
|
|
42
|
+
"Use COMPOSIO_ORG_API_KEY/x-org-api-key instead of COMPOSIO_API_KEY/x-api-key.",
|
|
43
|
+
),
|
|
44
|
+
raw: z
|
|
45
|
+
.boolean()
|
|
46
|
+
.default(false)
|
|
47
|
+
.describe("Return raw text instead of JSON-pretty output text."),
|
|
48
|
+
}),
|
|
49
|
+
outputSchema: z.object({
|
|
50
|
+
target: z.literal("composio"),
|
|
51
|
+
ok: z.boolean(),
|
|
52
|
+
status: z.number(),
|
|
53
|
+
statusText: z.string(),
|
|
54
|
+
method: z.string(),
|
|
55
|
+
url: z.string(),
|
|
56
|
+
response: z.unknown(),
|
|
57
|
+
responseText: z.string(),
|
|
58
|
+
message: z.string(),
|
|
59
|
+
}),
|
|
60
|
+
},
|
|
61
|
+
async (input) => {
|
|
62
|
+
let result: Awaited<ReturnType<typeof executeComposioRequest>>;
|
|
63
|
+
try {
|
|
64
|
+
result = await executeComposioRequest(
|
|
65
|
+
composioArgsFromParts({
|
|
66
|
+
baseUrl: input.baseUrl,
|
|
67
|
+
body: input.body,
|
|
68
|
+
endpoint: input.path,
|
|
69
|
+
headers: input.headers,
|
|
70
|
+
method: input.method,
|
|
71
|
+
query: input.query,
|
|
72
|
+
raw: input.raw,
|
|
73
|
+
useOrgKey: input.useOrgKey,
|
|
74
|
+
}),
|
|
75
|
+
);
|
|
76
|
+
} catch (err) {
|
|
77
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
78
|
+
return {
|
|
79
|
+
content: [{ type: "text", text: `swarm_x composio: ${message}` }],
|
|
80
|
+
structuredContent: {
|
|
81
|
+
target: "composio",
|
|
82
|
+
ok: false,
|
|
83
|
+
status: 0,
|
|
84
|
+
statusText: "Invalid request",
|
|
85
|
+
method: input.method,
|
|
86
|
+
url: "",
|
|
87
|
+
response: null,
|
|
88
|
+
responseText: "",
|
|
89
|
+
message,
|
|
90
|
+
},
|
|
91
|
+
isError: true,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const message =
|
|
96
|
+
result.error || result.formattedBody || `HTTP ${result.status} ${result.statusText}`.trim();
|
|
97
|
+
const structuredContent = {
|
|
98
|
+
target: "composio" as const,
|
|
99
|
+
ok: result.ok,
|
|
100
|
+
status: result.status,
|
|
101
|
+
statusText: result.statusText,
|
|
102
|
+
method: result.method,
|
|
103
|
+
url: result.url,
|
|
104
|
+
response: result.body,
|
|
105
|
+
responseText: result.text,
|
|
106
|
+
message,
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
return {
|
|
110
|
+
content: [{ type: "text", text: message }],
|
|
111
|
+
structuredContent,
|
|
112
|
+
isError: !result.ok,
|
|
113
|
+
};
|
|
114
|
+
},
|
|
115
|
+
);
|
|
116
|
+
};
|
package/src/tools/tool-config.ts
CHANGED
|
@@ -122,6 +122,9 @@ export const DEFERRED_TOOLS = new Set([
|
|
|
122
122
|
// Debug (1)
|
|
123
123
|
"db-query",
|
|
124
124
|
|
|
125
|
+
// Metrics (1)
|
|
126
|
+
"create_metric",
|
|
127
|
+
|
|
125
128
|
// Approval Requests (1)
|
|
126
129
|
"request-human-input",
|
|
127
130
|
|
|
@@ -168,6 +171,9 @@ export const DEFERRED_TOOLS = new Set([
|
|
|
168
171
|
"script-delete",
|
|
169
172
|
"script-query-types",
|
|
170
173
|
|
|
174
|
+
// External command routes (1)
|
|
175
|
+
"swarm_x",
|
|
176
|
+
|
|
171
177
|
// Other (4)
|
|
172
178
|
"cancel-task",
|
|
173
179
|
"inject-learning",
|
package/src/types.ts
CHANGED
|
@@ -1017,6 +1017,7 @@ export const ExecutorMetaSchema = z.object({
|
|
|
1017
1017
|
nodeId: z.string(),
|
|
1018
1018
|
workflowId: z.string().uuid(),
|
|
1019
1019
|
dryRun: z.boolean().default(false),
|
|
1020
|
+
requestedByUserId: z.string().optional(),
|
|
1020
1021
|
});
|
|
1021
1022
|
export type ExecutorMeta = z.infer<typeof ExecutorMetaSchema>;
|
|
1022
1023
|
|
|
@@ -1390,6 +1391,125 @@ export const PageVersionSchema = z.object({
|
|
|
1390
1391
|
});
|
|
1391
1392
|
export type PageVersion = z.infer<typeof PageVersionSchema>;
|
|
1392
1393
|
|
|
1394
|
+
// ---------------------------------------------------------------------------
|
|
1395
|
+
// Metrics
|
|
1396
|
+
// ---------------------------------------------------------------------------
|
|
1397
|
+
|
|
1398
|
+
const MetricParamSchema = z.union([z.string(), z.number(), z.boolean(), z.null()]);
|
|
1399
|
+
export type MetricParam = z.infer<typeof MetricParamSchema>;
|
|
1400
|
+
const MetricVariableTypeSchema = z.enum(["text", "number", "select"]);
|
|
1401
|
+
|
|
1402
|
+
export const MetricFormatSchema = z.enum(["number", "integer", "currency", "percent", "duration"]);
|
|
1403
|
+
export type MetricFormat = z.infer<typeof MetricFormatSchema>;
|
|
1404
|
+
|
|
1405
|
+
export const MetricVisualizationSchema = z.enum([
|
|
1406
|
+
"stat",
|
|
1407
|
+
"table",
|
|
1408
|
+
"bar",
|
|
1409
|
+
"line",
|
|
1410
|
+
"multi-bar",
|
|
1411
|
+
"multi-line",
|
|
1412
|
+
]);
|
|
1413
|
+
export type MetricVisualization = z.infer<typeof MetricVisualizationSchema>;
|
|
1414
|
+
|
|
1415
|
+
const MetricQuerySchema = z.object({
|
|
1416
|
+
sql: z.string().min(1).max(10_000),
|
|
1417
|
+
params: z.array(MetricParamSchema).optional(),
|
|
1418
|
+
maxRows: z.number().int().min(1).max(500).optional(),
|
|
1419
|
+
});
|
|
1420
|
+
|
|
1421
|
+
export const MetricVariableSchema = z.object({
|
|
1422
|
+
key: z
|
|
1423
|
+
.string()
|
|
1424
|
+
.min(1)
|
|
1425
|
+
.regex(/^[a-zA-Z][a-zA-Z0-9_]*$/),
|
|
1426
|
+
label: z.string().min(1).optional(),
|
|
1427
|
+
type: MetricVariableTypeSchema.default("text"),
|
|
1428
|
+
defaultValue: MetricParamSchema.optional(),
|
|
1429
|
+
options: z
|
|
1430
|
+
.array(
|
|
1431
|
+
z.object({
|
|
1432
|
+
label: z.string().min(1),
|
|
1433
|
+
value: MetricParamSchema,
|
|
1434
|
+
}),
|
|
1435
|
+
)
|
|
1436
|
+
.optional(),
|
|
1437
|
+
});
|
|
1438
|
+
export type MetricVariable = z.infer<typeof MetricVariableSchema>;
|
|
1439
|
+
|
|
1440
|
+
export const MetricVizConfigSchema = z.object({
|
|
1441
|
+
type: MetricVisualizationSchema,
|
|
1442
|
+
x: z.string().optional(),
|
|
1443
|
+
y: z.string().optional(),
|
|
1444
|
+
series: z.array(z.string()).optional(),
|
|
1445
|
+
label: z.string().optional(),
|
|
1446
|
+
value: z.string().optional(),
|
|
1447
|
+
columns: z
|
|
1448
|
+
.array(
|
|
1449
|
+
z.object({
|
|
1450
|
+
key: z.string(),
|
|
1451
|
+
label: z.string().optional(),
|
|
1452
|
+
format: MetricFormatSchema.optional(),
|
|
1453
|
+
}),
|
|
1454
|
+
)
|
|
1455
|
+
.optional(),
|
|
1456
|
+
format: MetricFormatSchema.optional(),
|
|
1457
|
+
});
|
|
1458
|
+
export type MetricVizConfig = z.infer<typeof MetricVizConfigSchema>;
|
|
1459
|
+
|
|
1460
|
+
export const MetricWidgetSchema = z.object({
|
|
1461
|
+
id: z.string().min(1),
|
|
1462
|
+
title: z.string().min(1),
|
|
1463
|
+
description: z.string().optional(),
|
|
1464
|
+
query: MetricQuerySchema,
|
|
1465
|
+
viz: MetricVizConfigSchema,
|
|
1466
|
+
});
|
|
1467
|
+
export type MetricWidget = z.infer<typeof MetricWidgetSchema>;
|
|
1468
|
+
|
|
1469
|
+
export const MetricDefinitionSchema = z.object({
|
|
1470
|
+
version: z.literal(1),
|
|
1471
|
+
widgets: z.array(MetricWidgetSchema).min(1).max(24),
|
|
1472
|
+
variables: z.array(MetricVariableSchema).max(12).optional(),
|
|
1473
|
+
layout: z
|
|
1474
|
+
.object({
|
|
1475
|
+
columns: z.number().int().min(1).max(4).optional(),
|
|
1476
|
+
})
|
|
1477
|
+
.optional(),
|
|
1478
|
+
refreshSeconds: z.number().int().min(5).max(3600).optional(),
|
|
1479
|
+
});
|
|
1480
|
+
export type MetricDefinition = z.infer<typeof MetricDefinitionSchema>;
|
|
1481
|
+
|
|
1482
|
+
export const MetricSnapshotSchema = z.object({
|
|
1483
|
+
title: z.string(),
|
|
1484
|
+
description: z.string().optional(),
|
|
1485
|
+
definition: MetricDefinitionSchema,
|
|
1486
|
+
});
|
|
1487
|
+
export type MetricSnapshot = z.infer<typeof MetricSnapshotSchema>;
|
|
1488
|
+
|
|
1489
|
+
export const MetricSchema = z.object({
|
|
1490
|
+
id: z.string(),
|
|
1491
|
+
agentId: z.string(),
|
|
1492
|
+
slug: z.string(),
|
|
1493
|
+
title: z.string(),
|
|
1494
|
+
description: z.string().optional(),
|
|
1495
|
+
definition: MetricDefinitionSchema,
|
|
1496
|
+
createdAt: z.string(),
|
|
1497
|
+
updatedAt: z.string(),
|
|
1498
|
+
});
|
|
1499
|
+
export type Metric = z.infer<typeof MetricSchema>;
|
|
1500
|
+
|
|
1501
|
+
export type MetricSummary = Omit<Metric, "definition">;
|
|
1502
|
+
|
|
1503
|
+
export const MetricVersionSchema = z.object({
|
|
1504
|
+
id: z.string(),
|
|
1505
|
+
metricId: z.string(),
|
|
1506
|
+
version: z.number().int().min(1),
|
|
1507
|
+
snapshot: MetricSnapshotSchema,
|
|
1508
|
+
changedByAgentId: z.string().optional(),
|
|
1509
|
+
createdAt: z.string(),
|
|
1510
|
+
});
|
|
1511
|
+
export type MetricVersion = z.infer<typeof MetricVersionSchema>;
|
|
1512
|
+
|
|
1393
1513
|
// --- Workflow Run ---
|
|
1394
1514
|
|
|
1395
1515
|
export const WorkflowRunStatusSchema = z.enum([
|
|
@@ -1600,6 +1720,7 @@ export const SkillSchema = z.object({
|
|
|
1600
1720
|
userInvocable: z.boolean(),
|
|
1601
1721
|
version: z.number(),
|
|
1602
1722
|
isEnabled: z.boolean(),
|
|
1723
|
+
systemDefault: z.boolean(),
|
|
1603
1724
|
createdAt: z.string(),
|
|
1604
1725
|
lastUpdatedAt: z.string(),
|
|
1605
1726
|
lastFetchedAt: z.string().nullable(),
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
|
+
import type { IncomingMessage } from "node:http";
|
|
3
|
+
import type { User } from "../types";
|
|
4
|
+
|
|
5
|
+
export type HttpRequestAuth =
|
|
6
|
+
| { kind: "operator"; fingerprint: string }
|
|
7
|
+
| { kind: "user"; userId: string; user: User };
|
|
8
|
+
|
|
9
|
+
const requestAuth = new WeakMap<IncomingMessage, HttpRequestAuth | null>();
|
|
10
|
+
const authStorage = new AsyncLocalStorage<HttpRequestAuth | null>();
|
|
11
|
+
|
|
12
|
+
export function setRequestAuth(req: IncomingMessage, auth: HttpRequestAuth | null): void {
|
|
13
|
+
requestAuth.set(req, auth);
|
|
14
|
+
authStorage.enterWith(auth);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function getRequestAuth(req: IncomingMessage): HttpRequestAuth | null {
|
|
18
|
+
return requestAuth.get(req) ?? null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function getCurrentRequestAuth(): HttpRequestAuth | null {
|
|
22
|
+
return authStorage.getStore() ?? null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function getCurrentRequestUserId(): string | undefined {
|
|
26
|
+
const auth = getCurrentRequestAuth();
|
|
27
|
+
return auth?.kind === "user" ? auth.userId : undefined;
|
|
28
|
+
}
|
|
@@ -28,7 +28,7 @@ export async function refreshSkillsIfChanged(
|
|
|
28
28
|
ctx: SkillsRefreshContext,
|
|
29
29
|
lastHashRef: { current: string | null },
|
|
30
30
|
): Promise<SkillsRefreshResult> {
|
|
31
|
-
const { apiUrl,
|
|
31
|
+
const { apiUrl, apiKey, agentId, role } = ctx;
|
|
32
32
|
const authHeaders: Record<string, string> = { "X-Agent-ID": agentId };
|
|
33
33
|
if (apiKey) authHeaders.Authorization = `Bearer ${apiKey}`;
|
|
34
34
|
|
|
@@ -83,7 +83,7 @@ export async function refreshSkillsIfChanged(
|
|
|
83
83
|
"X-Agent-ID": agentId,
|
|
84
84
|
};
|
|
85
85
|
if (apiKey) syncHeaders.Authorization = `Bearer ${apiKey}`;
|
|
86
|
-
const syncRes = await fetch(`${
|
|
86
|
+
const syncRes = await fetch(`${apiUrl}/api/skills/sync-filesystem`, {
|
|
87
87
|
method: "POST",
|
|
88
88
|
headers: syncHeaders,
|
|
89
89
|
});
|