@desplega.ai/agent-swarm 1.87.0 → 1.88.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 +2 -1
- package/openapi.json +13 -1
- package/package.json +5 -5
- package/src/be/db.ts +49 -7
- package/src/be/migrations/080_skill_system_defaults.sql +8 -0
- package/src/be/modelsdev-cache.json +1123 -1034
- package/src/be/seed/registry.ts +3 -2
- package/src/be/seed-skills/index.ts +172 -0
- package/src/cli.tsx +33 -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 +1 -0
- package/src/e2b/dispatch.ts +234 -18
- package/src/http/memory.ts +13 -1
- package/src/http/skills.ts +53 -0
- package/src/http/webhooks.ts +75 -0
- package/src/integrations/kapso/client.ts +82 -0
- package/src/memory/automatic-task-gate.ts +47 -0
- package/src/prompts/base-prompt.ts +16 -1
- package/src/prompts/session-templates.ts +51 -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 +50 -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 +41 -0
- package/src/tests/claude-adapter.test.ts +86 -1
- package/src/tests/codex-adapter.test.ts +89 -0
- package/src/tests/e2b-dispatch.test.ts +603 -11
- package/src/tests/http-api-integration.test.ts +113 -0
- package/src/tests/kapso-client.test.ts +74 -1
- package/src/tests/kapso-inbound.test.ts +60 -2
- package/src/tests/opencode-adapter.test.ts +95 -0
- package/src/tests/prompt-template-session.test.ts +4 -2
- 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/system-default-skills.test.ts +119 -0
- package/src/tests/telemetry-init.test.ts +86 -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/types.ts +1 -0
- package/templates/skills/artifacts/config.json +1 -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
|
@@ -246,6 +246,23 @@ describe("mergeMcpConfig (issue #369)", () => {
|
|
|
246
246
|
const merged = mergeMcpConfig({ mcpServers: {} }, {}, TASK_ID);
|
|
247
247
|
expect(Object.keys(merged.mcpServers)).toHaveLength(0);
|
|
248
248
|
});
|
|
249
|
+
|
|
250
|
+
test("preserves a context-mode entry through the merge", () => {
|
|
251
|
+
const base = {
|
|
252
|
+
mcpServers: {
|
|
253
|
+
"agent-swarm": {
|
|
254
|
+
type: "http",
|
|
255
|
+
url: "http://localhost:3013/mcp",
|
|
256
|
+
headers: { Authorization: "Bearer KEY", "X-Agent-ID": "a1" },
|
|
257
|
+
},
|
|
258
|
+
"plugin_context-mode_context-mode": { command: "context-mode" },
|
|
259
|
+
},
|
|
260
|
+
};
|
|
261
|
+
const merged = mergeMcpConfig(base, null, TASK_ID);
|
|
262
|
+
expect(merged.mcpServers["plugin_context-mode_context-mode"]).toEqual({
|
|
263
|
+
command: "context-mode",
|
|
264
|
+
});
|
|
265
|
+
});
|
|
249
266
|
});
|
|
250
267
|
|
|
251
268
|
describe("createSessionMcpConfig", () => {
|
|
@@ -323,7 +340,13 @@ describe("createSessionMcpConfig", () => {
|
|
|
323
340
|
const written = await readWritten(path!);
|
|
324
341
|
expect(written.mcpServers["agent-swarm"]).toBeDefined();
|
|
325
342
|
expect(written.mcpServers.Datadog).toBeDefined();
|
|
326
|
-
|
|
343
|
+
// context-mode is injected by default (see CONTEXT_MODE_DISABLED gate); the
|
|
344
|
+
// two differently-named .mcp.json servers still merge alongside it.
|
|
345
|
+
expect(Object.keys(written.mcpServers).sort()).toEqual([
|
|
346
|
+
"Datadog",
|
|
347
|
+
"agent-swarm",
|
|
348
|
+
"plugin_context-mode_context-mode",
|
|
349
|
+
]);
|
|
327
350
|
});
|
|
328
351
|
|
|
329
352
|
test("ancestor wins over repo-local on agent-swarm key conflict", async () => {
|
|
@@ -402,4 +425,66 @@ describe("createSessionMcpConfig", () => {
|
|
|
402
425
|
const written = await readWritten(path!);
|
|
403
426
|
expect(written.mcpServers["from-api"]).toBeDefined();
|
|
404
427
|
});
|
|
428
|
+
|
|
429
|
+
test("includes context-mode entry when CONTEXT_MODE_DISABLED is unset", async () => {
|
|
430
|
+
const prev = process.env.CONTEXT_MODE_DISABLED;
|
|
431
|
+
delete process.env.CONTEXT_MODE_DISABLED;
|
|
432
|
+
try {
|
|
433
|
+
await writeFile(
|
|
434
|
+
join(sandbox, ".mcp.json"),
|
|
435
|
+
JSON.stringify({
|
|
436
|
+
mcpServers: {
|
|
437
|
+
"agent-swarm": {
|
|
438
|
+
type: "http",
|
|
439
|
+
url: "http://swarm/mcp",
|
|
440
|
+
headers: { Authorization: "Bearer SWARM", "X-Agent-ID": "a1" },
|
|
441
|
+
},
|
|
442
|
+
},
|
|
443
|
+
}),
|
|
444
|
+
);
|
|
445
|
+
const cwd = join(sandbox, "repos", "foo");
|
|
446
|
+
await mkdir(cwd, { recursive: true });
|
|
447
|
+
|
|
448
|
+
const path = await createSessionMcpConfig(cwd, "task-ctx-on");
|
|
449
|
+
const written = await readWritten(path!);
|
|
450
|
+
expect(written.mcpServers["plugin_context-mode_context-mode"]).toEqual({
|
|
451
|
+
command: "context-mode",
|
|
452
|
+
});
|
|
453
|
+
// Coexists with the swarm entry.
|
|
454
|
+
expect(written.mcpServers["agent-swarm"]).toBeDefined();
|
|
455
|
+
} finally {
|
|
456
|
+
if (prev === undefined) delete process.env.CONTEXT_MODE_DISABLED;
|
|
457
|
+
else process.env.CONTEXT_MODE_DISABLED = prev;
|
|
458
|
+
}
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
test("excludes context-mode entry when CONTEXT_MODE_DISABLED='true'", async () => {
|
|
462
|
+
const prev = process.env.CONTEXT_MODE_DISABLED;
|
|
463
|
+
process.env.CONTEXT_MODE_DISABLED = "true";
|
|
464
|
+
try {
|
|
465
|
+
await writeFile(
|
|
466
|
+
join(sandbox, ".mcp.json"),
|
|
467
|
+
JSON.stringify({
|
|
468
|
+
mcpServers: {
|
|
469
|
+
"agent-swarm": {
|
|
470
|
+
type: "http",
|
|
471
|
+
url: "http://swarm/mcp",
|
|
472
|
+
headers: { Authorization: "Bearer SWARM", "X-Agent-ID": "a1" },
|
|
473
|
+
},
|
|
474
|
+
},
|
|
475
|
+
}),
|
|
476
|
+
);
|
|
477
|
+
const cwd = join(sandbox, "repos", "foo");
|
|
478
|
+
await mkdir(cwd, { recursive: true });
|
|
479
|
+
|
|
480
|
+
const path = await createSessionMcpConfig(cwd, "task-ctx-off");
|
|
481
|
+
const written = await readWritten(path!);
|
|
482
|
+
expect(written.mcpServers["plugin_context-mode_context-mode"]).toBeUndefined();
|
|
483
|
+
// The swarm entry is still present.
|
|
484
|
+
expect(written.mcpServers["agent-swarm"]).toBeDefined();
|
|
485
|
+
} finally {
|
|
486
|
+
if (prev === undefined) delete process.env.CONTEXT_MODE_DISABLED;
|
|
487
|
+
else process.env.CONTEXT_MODE_DISABLED = prev;
|
|
488
|
+
}
|
|
489
|
+
});
|
|
405
490
|
});
|
|
@@ -872,9 +872,21 @@ describe("computeCodexCostUsd", () => {
|
|
|
872
872
|
describe("buildCodexConfig", () => {
|
|
873
873
|
// Save and restore the global fetch so we don't leak mocks between tests.
|
|
874
874
|
const originalFetch = globalThis.fetch;
|
|
875
|
+
// These tests assert the EXACT set of mcp_servers keys, which is only the
|
|
876
|
+
// installed-server merge logic. Disable the always-on context-mode entry so
|
|
877
|
+
// those exact-key assertions stay valid; a dedicated block below verifies
|
|
878
|
+
// the context-mode + features behavior. Save/restore the env to avoid leaks.
|
|
879
|
+
let prevContextModeDisabled: string | undefined;
|
|
880
|
+
|
|
881
|
+
beforeEach(() => {
|
|
882
|
+
prevContextModeDisabled = process.env.CONTEXT_MODE_DISABLED;
|
|
883
|
+
process.env.CONTEXT_MODE_DISABLED = "true";
|
|
884
|
+
});
|
|
875
885
|
|
|
876
886
|
afterEach(() => {
|
|
877
887
|
globalThis.fetch = originalFetch;
|
|
888
|
+
if (prevContextModeDisabled === undefined) delete process.env.CONTEXT_MODE_DISABLED;
|
|
889
|
+
else process.env.CONTEXT_MODE_DISABLED = prevContextModeDisabled;
|
|
878
890
|
});
|
|
879
891
|
|
|
880
892
|
// Helper: build a ProviderSessionConfig pointed at a mock endpoint.
|
|
@@ -1098,6 +1110,83 @@ describe("buildCodexConfig", () => {
|
|
|
1098
1110
|
});
|
|
1099
1111
|
});
|
|
1100
1112
|
|
|
1113
|
+
// ─── Phase 3: buildCodexConfig — context-mode MCP + hook feature flags ───────
|
|
1114
|
+
|
|
1115
|
+
describe("buildCodexConfig — context-mode + features", () => {
|
|
1116
|
+
const originalFetch = globalThis.fetch;
|
|
1117
|
+
// Explicitly own CONTEXT_MODE_DISABLED here. Save the ambient value up front
|
|
1118
|
+
// and restore it after every test so we never leak the mutation to siblings.
|
|
1119
|
+
let prevContextModeDisabled: string | undefined;
|
|
1120
|
+
|
|
1121
|
+
beforeEach(() => {
|
|
1122
|
+
prevContextModeDisabled = process.env.CONTEXT_MODE_DISABLED;
|
|
1123
|
+
});
|
|
1124
|
+
|
|
1125
|
+
afterEach(() => {
|
|
1126
|
+
globalThis.fetch = originalFetch;
|
|
1127
|
+
if (prevContextModeDisabled === undefined) delete process.env.CONTEXT_MODE_DISABLED;
|
|
1128
|
+
else process.env.CONTEXT_MODE_DISABLED = prevContextModeDisabled;
|
|
1129
|
+
});
|
|
1130
|
+
|
|
1131
|
+
function cfg(overrides: Partial<ProviderSessionConfig> = {}): ProviderSessionConfig {
|
|
1132
|
+
return {
|
|
1133
|
+
prompt: "hello",
|
|
1134
|
+
systemPrompt: "",
|
|
1135
|
+
model: "gpt-5.4",
|
|
1136
|
+
role: "worker",
|
|
1137
|
+
agentId: "agent-mcp-test",
|
|
1138
|
+
taskId: "task-mcp-test",
|
|
1139
|
+
apiUrl: "http://test.invalid",
|
|
1140
|
+
apiKey: "test-key",
|
|
1141
|
+
cwd: "",
|
|
1142
|
+
logFile: `/tmp/codex-ctx-test-${Date.now()}-${Math.random().toString(36).slice(2)}.log`,
|
|
1143
|
+
...overrides,
|
|
1144
|
+
};
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
function stubFetch(body: unknown, status = 200): typeof globalThis.fetch {
|
|
1148
|
+
return async (): Promise<Response> => {
|
|
1149
|
+
return new Response(JSON.stringify(body), {
|
|
1150
|
+
status,
|
|
1151
|
+
headers: { "Content-Type": "application/json" },
|
|
1152
|
+
});
|
|
1153
|
+
};
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
test("includes the 'context-mode' mcp_servers entry by default", async () => {
|
|
1157
|
+
delete process.env.CONTEXT_MODE_DISABLED;
|
|
1158
|
+
globalThis.fetch = stubFetch({ servers: [], total: 0 });
|
|
1159
|
+
const merged = await buildCodexConfig(cfg(), "gpt-5.4", () => {});
|
|
1160
|
+
const mcp = merged.mcp_servers as Record<string, Record<string, unknown>>;
|
|
1161
|
+
|
|
1162
|
+
expect(Object.keys(mcp).sort()).toEqual(["agent-swarm", "context-mode"]);
|
|
1163
|
+
expect(mcp["context-mode"]?.command).toBe("context-mode");
|
|
1164
|
+
expect(mcp["context-mode"]?.enabled).toBe(true);
|
|
1165
|
+
expect(mcp["context-mode"]?.startup_timeout_sec).toBe(30);
|
|
1166
|
+
expect(mcp["context-mode"]?.tool_timeout_sec).toBe(120);
|
|
1167
|
+
});
|
|
1168
|
+
|
|
1169
|
+
test("excludes the 'context-mode' entry when CONTEXT_MODE_DISABLED=true", async () => {
|
|
1170
|
+
process.env.CONTEXT_MODE_DISABLED = "true";
|
|
1171
|
+
globalThis.fetch = stubFetch({ servers: [], total: 0 });
|
|
1172
|
+
const merged = await buildCodexConfig(cfg(), "gpt-5.4", () => {});
|
|
1173
|
+
const mcp = merged.mcp_servers as Record<string, Record<string, unknown>>;
|
|
1174
|
+
|
|
1175
|
+
expect(Object.keys(mcp)).toEqual(["agent-swarm"]);
|
|
1176
|
+
expect(mcp["context-mode"]).toBeUndefined();
|
|
1177
|
+
});
|
|
1178
|
+
|
|
1179
|
+
test("sets features.hooks and features.plugin_hooks to true", async () => {
|
|
1180
|
+
delete process.env.CONTEXT_MODE_DISABLED;
|
|
1181
|
+
globalThis.fetch = stubFetch({ servers: [], total: 0 });
|
|
1182
|
+
const merged = await buildCodexConfig(cfg(), "gpt-5.4", () => {});
|
|
1183
|
+
|
|
1184
|
+
const features = merged.features as Record<string, unknown>;
|
|
1185
|
+
expect(features.hooks).toBe(true);
|
|
1186
|
+
expect(features.plugin_hooks).toBe(true);
|
|
1187
|
+
});
|
|
1188
|
+
});
|
|
1189
|
+
|
|
1101
1190
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
1102
1191
|
// Phase 3 — session-end summarization
|
|
1103
1192
|
// ─────────────────────────────────────────────────────────────────────────────
|