@desplega.ai/agent-swarm 1.94.0 → 1.95.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.
@@ -41,6 +41,8 @@ describe("writeSkillsToFilesystem", () => {
41
41
  rmSync(join(FAKE_HOME, ".claude"), { recursive: true, force: true });
42
42
  rmSync(join(FAKE_HOME, ".pi"), { recursive: true, force: true });
43
43
  rmSync(join(FAKE_HOME, ".codex"), { recursive: true, force: true });
44
+ rmSync(join(FAKE_HOME, ".opencode"), { recursive: true, force: true });
45
+ rmSync(join(FAKE_HOME, ".agents"), { recursive: true, force: true });
44
46
  });
45
47
 
46
48
  afterAll(() => {
@@ -71,12 +73,16 @@ describe("writeSkillsToFilesystem", () => {
71
73
  const entries = [skillEntry({ name: "multi-skill", content: "# Multi" })];
72
74
  const result = writeSkillsToFilesystem(entries, "all", FAKE_HOME);
73
75
 
74
- expect(result.synced).toBe(3); // claude + pi + codex
76
+ expect(result.synced).toBe(5); // claude + pi + codex + opencode + .agents
75
77
  expect(existsSync(join(FAKE_HOME, ".claude", "skills", "multi-skill", "SKILL.md"))).toBe(true);
76
78
  expect(existsSync(join(FAKE_HOME, ".pi", "agent", "skills", "multi-skill", "SKILL.md"))).toBe(
77
79
  true,
78
80
  );
79
81
  expect(existsSync(join(FAKE_HOME, ".codex", "skills", "multi-skill", "SKILL.md"))).toBe(true);
82
+ expect(existsSync(join(FAKE_HOME, ".opencode", "skills", "multi-skill", "SKILL.md"))).toBe(
83
+ true,
84
+ );
85
+ expect(existsSync(join(FAKE_HOME, ".agents", "skills", "multi-skill", "SKILL.md"))).toBe(true);
80
86
  });
81
87
 
82
88
  test("writes complex skill SKILL.md plus non-binary bundled files", () => {
@@ -121,23 +121,29 @@ describe("syncSkillsToFilesystem", () => {
121
121
  expect(existsSync(piOnlyFile)).toBe(false);
122
122
  });
123
123
 
124
- test("syncs to claude, pi, and codex when harnessType is 'all'", () => {
124
+ test("syncs to all local harness skill trees when harnessType is 'all'", () => {
125
125
  // Clean up first to get accurate count
126
126
  rmSync(join(FAKE_HOME, ".claude"), { recursive: true, force: true });
127
127
  rmSync(join(FAKE_HOME, ".pi"), { recursive: true, force: true });
128
128
  rmSync(join(FAKE_HOME, ".codex"), { recursive: true, force: true });
129
+ rmSync(join(FAKE_HOME, ".opencode"), { recursive: true, force: true });
130
+ rmSync(join(FAKE_HOME, ".agents"), { recursive: true, force: true });
129
131
 
130
132
  const result = syncSkillsToFilesystem(agentId, "all", FAKE_HOME);
131
133
 
132
134
  expect(result.errors).toHaveLength(0);
133
- expect(result.synced).toBe(6); // 2 DB-backed skills × 3 dirs
135
+ expect(result.synced).toBe(10); // 2 DB-backed skills × 5 dirs
134
136
 
135
137
  const claudeFile = join(FAKE_HOME, ".claude", "skills", "test-skill", "SKILL.md");
136
138
  const piFile = join(FAKE_HOME, ".pi", "agent", "skills", "test-skill", "SKILL.md");
137
139
  const codexFile = join(FAKE_HOME, ".codex", "skills", "test-skill", "SKILL.md");
140
+ const opencodeFile = join(FAKE_HOME, ".opencode", "skills", "test-skill", "SKILL.md");
141
+ const agentsFile = join(FAKE_HOME, ".agents", "skills", "test-skill", "SKILL.md");
138
142
  expect(existsSync(claudeFile)).toBe(true);
139
143
  expect(existsSync(piFile)).toBe(true);
140
144
  expect(existsSync(codexFile)).toBe(true);
145
+ expect(existsSync(opencodeFile)).toBe(true);
146
+ expect(existsSync(agentsFile)).toBe(true);
141
147
  });
142
148
 
143
149
  test("syncs DB-backed complex skill files and skips binary placeholders", () => {
@@ -299,19 +305,25 @@ describe("syncSkillsToFilesystem", () => {
299
305
  rmSync(join(FAKE_HOME, ".claude"), { recursive: true, force: true });
300
306
  rmSync(join(FAKE_HOME, ".pi"), { recursive: true, force: true });
301
307
  rmSync(join(FAKE_HOME, ".codex"), { recursive: true, force: true });
308
+ rmSync(join(FAKE_HOME, ".opencode"), { recursive: true, force: true });
309
+ rmSync(join(FAKE_HOME, ".agents"), { recursive: true, force: true });
302
310
 
303
311
  // Use 'all' explicitly with homeOverride (default harnessType would use real home)
304
312
  const result = syncSkillsToFilesystem(agentId, "all", FAKE_HOME);
305
313
 
306
314
  expect(result.errors).toHaveLength(0);
307
- expect(result.synced).toBeGreaterThanOrEqual(6);
315
+ expect(result.synced).toBeGreaterThanOrEqual(10);
308
316
 
309
317
  const claudeFile = join(FAKE_HOME, ".claude", "skills", "test-skill", "SKILL.md");
310
318
  const piFile = join(FAKE_HOME, ".pi", "agent", "skills", "test-skill", "SKILL.md");
311
319
  const codexFile = join(FAKE_HOME, ".codex", "skills", "test-skill", "SKILL.md");
320
+ const opencodeFile = join(FAKE_HOME, ".opencode", "skills", "test-skill", "SKILL.md");
321
+ const agentsFile = join(FAKE_HOME, ".agents", "skills", "test-skill", "SKILL.md");
312
322
  expect(existsSync(claudeFile)).toBe(true);
313
323
  expect(existsSync(piFile)).toBe(true);
314
324
  expect(existsSync(codexFile)).toBe(true);
325
+ expect(existsSync(opencodeFile)).toBe(true);
326
+ expect(existsSync(agentsFile)).toBe(true);
315
327
  });
316
328
 
317
329
  test("returns empty result for agent with no skills", () => {
@@ -35,6 +35,12 @@ export const registerMcpServerCreateTool = (server: McpServer) => {
35
35
  .string()
36
36
  .optional()
37
37
  .describe("JSON object mapping header names to config key paths for secret headers"),
38
+ extraAuthorizeParams: z
39
+ .string()
40
+ .optional()
41
+ .describe(
42
+ 'JSON object string of extra OAuth authorize-request params, e.g. {"access_type":"offline","prompt":"consent"}',
43
+ ),
38
44
  }),
39
45
  outputSchema: z.object({
40
46
  yourAgentId: z.string().uuid().optional(),
@@ -108,6 +114,7 @@ export const registerMcpServerCreateTool = (server: McpServer) => {
108
114
  headers: args.headers,
109
115
  envConfigKeys: args.envConfigKeys,
110
116
  headerConfigKeys: args.headerConfigKeys,
117
+ extraAuthorizeParams: args.extraAuthorizeParams,
111
118
  });
112
119
 
113
120
  // Auto-install for the creating agent
@@ -21,6 +21,12 @@ export const registerMcpServerUpdateTool = (server: McpServer) => {
21
21
  headers: z.string().optional().describe("New JSON object of non-secret headers"),
22
22
  envConfigKeys: z.string().optional().describe("New env config key mappings"),
23
23
  headerConfigKeys: z.string().optional().describe("New header config key mappings"),
24
+ extraAuthorizeParams: z
25
+ .string()
26
+ .optional()
27
+ .describe(
28
+ 'JSON object string of extra OAuth authorize-request params, e.g. {"access_type":"offline","prompt":"consent"}',
29
+ ),
24
30
  isEnabled: z.boolean().optional().describe("Toggle enabled/disabled"),
25
31
  }),
26
32
  outputSchema: z.object({
@@ -76,6 +82,8 @@ export const registerMcpServerUpdateTool = (server: McpServer) => {
76
82
  if (args.headers !== undefined) updates.headers = args.headers;
77
83
  if (args.envConfigKeys !== undefined) updates.envConfigKeys = args.envConfigKeys;
78
84
  if (args.headerConfigKeys !== undefined) updates.headerConfigKeys = args.headerConfigKeys;
85
+ if (args.extraAuthorizeParams !== undefined)
86
+ updates.extraAuthorizeParams = args.extraAuthorizeParams;
79
87
  if (args.isEnabled !== undefined) updates.isEnabled = args.isEnabled;
80
88
 
81
89
  const updated = updateMcpServer(args.id, updates);
package/src/types.ts CHANGED
@@ -1878,6 +1878,7 @@ export const McpServerSchema = z.object({
1878
1878
  headers: z.string().nullable(),
1879
1879
  envConfigKeys: z.string().nullable(),
1880
1880
  headerConfigKeys: z.string().nullable(),
1881
+ extraAuthorizeParams: z.string().nullable(),
1881
1882
  authMethod: McpAuthMethodSchema.default("static"),
1882
1883
  isEnabled: z.boolean(),
1883
1884
  version: z.number(),
@@ -8,8 +8,8 @@
8
8
  * SkillFsEntry data from the DB then delegates here.
9
9
  * - Worker-side: refreshSkillsIfChanged (src/utils/skills-refresh.ts) which
10
10
  * fetches SkillFsEntry data over HTTP then calls writeSkillsToFilesystem
11
- * with the worker's own homedir(), writing SKILL.md files to the correct
12
- * machine instead of the API box.
11
+ * with the worker's own homedir(), writing SKILL.md files to every local
12
+ * harness tree instead of the API box.
13
13
  */
14
14
 
15
15
  import type { Dirent } from "node:fs";
@@ -32,6 +32,8 @@ export interface SkillFsEntry {
32
32
  files: { path: string; content: string; isBinary: boolean }[];
33
33
  }
34
34
 
35
+ export type SkillHarnessTarget = "claude" | "pi" | "codex" | "opencode" | "agents" | "all";
36
+
35
37
  /**
36
38
  * Marker file written into every swarm-managed skill directory. Cleanup
37
39
  * only ever removes directories that contain this marker, so unrelated
@@ -109,7 +111,7 @@ function reconcileManagedSkillFiles(skillDir: string, currentRelativeFiles: Set<
109
111
  */
110
112
  export function writeSkillsToFilesystem(
111
113
  entries: SkillFsEntry[],
112
- harnessType: "claude" | "pi" | "codex" | "all" = "all",
114
+ harnessType: SkillHarnessTarget = "all",
113
115
  home: string,
114
116
  ): SkillSyncResult {
115
117
  const errors: string[] = [];
@@ -127,6 +129,12 @@ export function writeSkillsToFilesystem(
127
129
  if (harnessType === "codex" || harnessType === "all") {
128
130
  skillDirs.push(join(home, ".codex", "skills"));
129
131
  }
132
+ if (harnessType === "opencode" || harnessType === "all") {
133
+ skillDirs.push(join(home, ".opencode", "skills"));
134
+ }
135
+ if (harnessType === "agents" || harnessType === "all") {
136
+ skillDirs.push(join(home, ".agents", "skills"));
137
+ }
130
138
 
131
139
  // Ensure base dirs exist
132
140
  for (const dir of skillDirs) {