opencode-swarm-plugin 0.12.31 → 0.13.1

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.
Files changed (49) hide show
  1. package/.beads/issues.jsonl +204 -10
  2. package/.opencode/skills/tdd/SKILL.md +182 -0
  3. package/README.md +165 -17
  4. package/bun.lock +23 -0
  5. package/dist/index.js +4082 -457
  6. package/dist/pglite.data +0 -0
  7. package/dist/pglite.wasm +0 -0
  8. package/dist/plugin.js +4070 -533
  9. package/examples/commands/swarm.md +100 -28
  10. package/examples/skills/beads-workflow/SKILL.md +75 -28
  11. package/examples/skills/swarm-coordination/SKILL.md +165 -21
  12. package/global-skills/swarm-coordination/SKILL.md +116 -58
  13. package/global-skills/testing-patterns/SKILL.md +430 -0
  14. package/global-skills/testing-patterns/references/dependency-breaking-catalog.md +586 -0
  15. package/package.json +11 -5
  16. package/src/index.ts +44 -5
  17. package/src/streams/agent-mail.test.ts +777 -0
  18. package/src/streams/agent-mail.ts +535 -0
  19. package/src/streams/debug.test.ts +500 -0
  20. package/src/streams/debug.ts +629 -0
  21. package/src/streams/effect/ask.integration.test.ts +314 -0
  22. package/src/streams/effect/ask.ts +202 -0
  23. package/src/streams/effect/cursor.integration.test.ts +418 -0
  24. package/src/streams/effect/cursor.ts +288 -0
  25. package/src/streams/effect/deferred.test.ts +357 -0
  26. package/src/streams/effect/deferred.ts +445 -0
  27. package/src/streams/effect/index.ts +17 -0
  28. package/src/streams/effect/layers.ts +73 -0
  29. package/src/streams/effect/lock.test.ts +385 -0
  30. package/src/streams/effect/lock.ts +399 -0
  31. package/src/streams/effect/mailbox.test.ts +260 -0
  32. package/src/streams/effect/mailbox.ts +318 -0
  33. package/src/streams/events.test.ts +628 -0
  34. package/src/streams/events.ts +214 -0
  35. package/src/streams/index.test.ts +229 -0
  36. package/src/streams/index.ts +492 -0
  37. package/src/streams/migrations.test.ts +355 -0
  38. package/src/streams/migrations.ts +269 -0
  39. package/src/streams/projections.test.ts +611 -0
  40. package/src/streams/projections.ts +302 -0
  41. package/src/streams/store.integration.test.ts +548 -0
  42. package/src/streams/store.ts +546 -0
  43. package/src/streams/swarm-mail.ts +552 -0
  44. package/src/swarm-mail.integration.test.ts +970 -0
  45. package/src/swarm-mail.ts +739 -0
  46. package/src/swarm.integration.test.ts +16 -10
  47. package/src/swarm.ts +146 -78
  48. package/src/tool-availability.ts +35 -2
  49. package/global-skills/mcp-tool-authoring/SKILL.md +0 -695
@@ -1315,24 +1315,29 @@ describe("Swarm Prompt V2 (with Agent Mail/Beads)", () => {
1315
1315
  describe("SUBTASK_PROMPT_V2", () => {
1316
1316
  it("contains expected sections", () => {
1317
1317
  // Check all main sections are present in the template
1318
- expect(SUBTASK_PROMPT_V2).toContain("## Task");
1318
+ expect(SUBTASK_PROMPT_V2).toContain("[TASK]");
1319
1319
  expect(SUBTASK_PROMPT_V2).toContain("{subtask_title}");
1320
1320
  expect(SUBTASK_PROMPT_V2).toContain("{subtask_description}");
1321
1321
 
1322
- expect(SUBTASK_PROMPT_V2).toContain("## Files");
1322
+ expect(SUBTASK_PROMPT_V2).toContain("[FILES]");
1323
1323
  expect(SUBTASK_PROMPT_V2).toContain("{file_list}");
1324
1324
 
1325
- expect(SUBTASK_PROMPT_V2).toContain("## Context");
1325
+ expect(SUBTASK_PROMPT_V2).toContain("[CONTEXT]");
1326
1326
  expect(SUBTASK_PROMPT_V2).toContain("{shared_context}");
1327
1327
 
1328
- expect(SUBTASK_PROMPT_V2).toContain("## Workflow");
1328
+ expect(SUBTASK_PROMPT_V2).toContain("[WORKFLOW]");
1329
1329
  });
1330
1330
 
1331
- it("DOES contain Agent Mail instructions", () => {
1332
- // V2 prompt tells agents to USE Agent Mail
1333
- expect(SUBTASK_PROMPT_V2).toContain("Agent Mail");
1334
- expect(SUBTASK_PROMPT_V2).toContain("agentmail_send");
1331
+ it("DOES contain Swarm Mail instructions (MANDATORY)", () => {
1332
+ // V2 prompt tells agents to USE Swarm Mail - this is non-negotiable
1333
+ expect(SUBTASK_PROMPT_V2).toContain("SWARM MAIL");
1334
+ expect(SUBTASK_PROMPT_V2).toContain("swarmmail_init");
1335
+ expect(SUBTASK_PROMPT_V2).toContain("swarmmail_send");
1336
+ expect(SUBTASK_PROMPT_V2).toContain("swarmmail_inbox");
1337
+ expect(SUBTASK_PROMPT_V2).toContain("swarmmail_reserve");
1338
+ expect(SUBTASK_PROMPT_V2).toContain("swarmmail_release");
1335
1339
  expect(SUBTASK_PROMPT_V2).toContain("thread_id");
1340
+ expect(SUBTASK_PROMPT_V2).toContain("non-negotiable");
1336
1341
  });
1337
1342
 
1338
1343
  it("DOES contain beads instructions", () => {
@@ -1344,10 +1349,11 @@ describe("Swarm Prompt V2 (with Agent Mail/Beads)", () => {
1344
1349
  expect(SUBTASK_PROMPT_V2).toContain("swarm_complete");
1345
1350
  });
1346
1351
 
1347
- it("instructs agents to communicate", () => {
1352
+ it("instructs agents to communicate via swarmmail", () => {
1348
1353
  expect(SUBTASK_PROMPT_V2).toContain("Never work silently");
1349
- expect(SUBTASK_PROMPT_V2).toContain("Report progress");
1354
+ expect(SUBTASK_PROMPT_V2).toContain("progress");
1350
1355
  expect(SUBTASK_PROMPT_V2).toContain("coordinator");
1356
+ expect(SUBTASK_PROMPT_V2).toContain("CRITICAL");
1351
1357
  });
1352
1358
  });
1353
1359
  });
package/src/swarm.ts CHANGED
@@ -25,7 +25,12 @@ import {
25
25
  type SpawnedAgent,
26
26
  type Bead,
27
27
  } from "./schemas";
28
- import { mcpCall, mcpCallWithAutoInit, requireState } from "./agent-mail";
28
+ import {
29
+ sendSwarmMessage,
30
+ getSwarmInbox,
31
+ readSwarmMessage,
32
+ releaseSwarmFiles,
33
+ } from "./streams/swarm-mail";
29
34
  import {
30
35
  OutcomeSignalsSchema,
31
36
  DecompositionStrategySchema,
@@ -704,21 +709,64 @@ Only modify these files. Need others? Message the coordinator.
704
709
 
705
710
  {error_context}
706
711
 
707
- ## [TOOLS]
708
- ### Beads
709
- - beads_update (status: blocked)
710
- - beads_create (new bugs)
711
- - beads_close (via swarm_complete)
712
+ ## [MANDATORY: SWARM MAIL]
713
+
714
+ **YOU MUST USE SWARM MAIL FOR ALL COORDINATION.** This is non-negotiable.
715
+
716
+ ### Initialize FIRST (before any work)
717
+ \`\`\`
718
+ swarmmail_init(project_path="$PWD", task_description="{subtask_title}")
719
+ \`\`\`
720
+
721
+ ### Reserve Files (if not already reserved by coordinator)
722
+ \`\`\`
723
+ swarmmail_reserve(paths=[...files...], reason="{bead_id}: {subtask_title}")
724
+ \`\`\`
725
+
726
+ ### Check Inbox Regularly
727
+ \`\`\`
728
+ swarmmail_inbox() # Check for coordinator messages
729
+ swarmmail_read_message(message_id=N) # Read specific message
730
+ \`\`\`
731
+
732
+ ### Report Progress (REQUIRED - don't work silently)
733
+ \`\`\`
734
+ swarmmail_send(
735
+ to=["coordinator"],
736
+ subject="Progress: {bead_id}",
737
+ body="<what you did, blockers, questions>",
738
+ thread_id="{epic_id}"
739
+ )
740
+ \`\`\`
741
+
742
+ ### When Blocked
743
+ \`\`\`
744
+ swarmmail_send(
745
+ to=["coordinator"],
746
+ subject="BLOCKED: {bead_id}",
747
+ body="<blocker description, what you need>",
748
+ importance="high",
749
+ thread_id="{epic_id}"
750
+ )
751
+ beads_update(id="{bead_id}", status="blocked")
752
+ \`\`\`
753
+
754
+ ### Release Files When Done
755
+ \`\`\`
756
+ swarmmail_release() # Or let swarm_complete handle it
757
+ \`\`\`
712
758
 
713
- ### Agent Mail
714
- - agentmail_send (thread_id: {epic_id})
759
+ ## [OTHER TOOLS]
760
+ ### Beads
761
+ - beads_update(id, status) - Mark blocked if stuck
762
+ - beads_create(title, type) - Log new bugs found
715
763
 
716
764
  ### Skills (if available)
717
- - skills_list (discover available skills)
718
- - skills_use (activate a skill for specialized guidance)
765
+ - skills_list() - Discover available skills
766
+ - skills_use(name) - Activate skill for specialized guidance
719
767
 
720
- ### Completion
721
- - swarm_complete (REQUIRED when done)
768
+ ### Completion (REQUIRED)
769
+ - swarm_complete(project_key, agent_name, bead_id, summary, files_touched)
722
770
 
723
771
  ## [LEARNING]
724
772
  As you work, note reusable patterns, best practices, or domain insights:
@@ -727,15 +775,15 @@ As you work, note reusable patterns, best practices, or domain insights:
727
775
  - Good skills have clear "when to use" descriptions with actionable instructions
728
776
  - Skills make swarms smarter over time
729
777
 
730
- ## [OUTPUT]
731
- 1. Read files first
732
- 2. Implement changes
733
- 3. Verify (typecheck)
734
- 4. Complete with swarm_complete
778
+ ## [WORKFLOW]
779
+ 1. **swarmmail_init** - Initialize session FIRST
780
+ 2. Read assigned files
781
+ 3. Implement changes
782
+ 4. **swarmmail_send** - Report progress to coordinator
783
+ 5. Verify (typecheck)
784
+ 6. **swarm_complete** - Mark done, release reservations
735
785
 
736
- Return: Summary of changes made. Note any patterns worth preserving as skills.
737
-
738
- **Never work silently.** Communicate progress and blockers immediately.
786
+ **CRITICAL: Never work silently. Send progress updates via swarmmail_send every significant milestone.**
739
787
 
740
788
  Begin now.`;
741
789
 
@@ -959,15 +1007,19 @@ async function querySwarmMessages(
959
1007
  }
960
1008
 
961
1009
  try {
962
- interface ThreadSummary {
963
- summary: { total_messages: number };
964
- }
965
- const summary = await mcpCall<ThreadSummary>("summarize_thread", {
966
- project_key: projectKey,
967
- thread_id: threadId,
968
- llm_mode: false, // Just need the count
1010
+ // Use embedded swarm-mail inbox to count messages in thread
1011
+ const inbox = await getSwarmInbox({
1012
+ projectPath: projectKey,
1013
+ agentName: "coordinator", // Dummy agent name for thread query
1014
+ limit: 5,
1015
+ includeBodies: false,
969
1016
  });
970
- return summary.summary.total_messages;
1017
+
1018
+ // Count messages that match the thread ID
1019
+ const threadMessages = inbox.messages.filter(
1020
+ (m) => m.thread_id === threadId,
1021
+ );
1022
+ return threadMessages.length;
971
1023
  } catch (error) {
972
1024
  // Thread might not exist yet, or query failed
973
1025
  console.warn(
@@ -1312,9 +1364,10 @@ export const swarm_plan_prompt = tool({
1312
1364
 
1313
1365
  // Fetch skills context
1314
1366
  let skillsContext = "";
1315
- let skillsInfo: { included: boolean; count?: number; relevant?: string[] } = {
1316
- included: false,
1317
- };
1367
+ let skillsInfo: { included: boolean; count?: number; relevant?: string[] } =
1368
+ {
1369
+ included: false,
1370
+ };
1318
1371
 
1319
1372
  if (args.include_skills !== false) {
1320
1373
  const allSkills = await listSkills();
@@ -1769,15 +1822,14 @@ export const swarm_progress = tool({
1769
1822
  ? args.bead_id.split(".")[0]
1770
1823
  : args.bead_id;
1771
1824
 
1772
- // Send progress message to thread (with auto-reinit on server restart)
1773
- await mcpCallWithAutoInit("send_message", {
1774
- project_key: args.project_key,
1775
- agent_name: args.agent_name,
1776
- sender_name: args.agent_name,
1777
- to: [], // Coordinator will pick it up from thread
1825
+ // Send progress message to thread using embedded swarm-mail
1826
+ await sendSwarmMessage({
1827
+ projectPath: args.project_key,
1828
+ fromAgent: args.agent_name,
1829
+ toAgents: [], // Coordinator will pick it up from thread
1778
1830
  subject: `Progress: ${args.bead_id} - ${args.status}`,
1779
- body_md: formatProgressMessage(validated),
1780
- thread_id: epicId,
1831
+ body: formatProgressMessage(validated),
1832
+ threadId: epicId,
1781
1833
  importance: args.status === "blocked" ? "high" : "normal",
1782
1834
  });
1783
1835
 
@@ -1895,6 +1947,12 @@ export const swarm_broadcast = tool({
1895
1947
  description:
1896
1948
  "Broadcast context update to all agents working on the same epic",
1897
1949
  args: {
1950
+ project_path: tool.schema
1951
+ .string()
1952
+ .describe("Absolute path to project root"),
1953
+ agent_name: tool.schema
1954
+ .string()
1955
+ .describe("Name of the agent broadcasting the message"),
1898
1956
  epic_id: tool.schema.string().describe("Epic ID (e.g., bd-abc123)"),
1899
1957
  message: tool.schema
1900
1958
  .string()
@@ -1909,18 +1967,14 @@ export const swarm_broadcast = tool({
1909
1967
  .describe("Files this context relates to"),
1910
1968
  },
1911
1969
  async execute(args, ctx) {
1912
- // Get agent state - requires prior initialization
1913
- const state = requireState(ctx.sessionID);
1914
-
1915
1970
  // Extract bead_id from context if available (for traceability)
1916
- // In the swarm flow, ctx might have the current bead being worked on
1917
1971
  const beadId = (ctx as { beadId?: string }).beadId || "unknown";
1918
1972
 
1919
1973
  // Format the broadcast message
1920
1974
  const body = [
1921
1975
  `## Context Update`,
1922
1976
  "",
1923
- `**From**: ${state.agentName} (${beadId})`,
1977
+ `**From**: ${args.agent_name} (${beadId})`,
1924
1978
  `**Priority**: ${args.importance.toUpperCase()}`,
1925
1979
  "",
1926
1980
  args.message,
@@ -1940,25 +1994,23 @@ export const swarm_broadcast = tool({
1940
1994
  ? "high"
1941
1995
  : "normal";
1942
1996
 
1943
- // Send as broadcast to thread (empty 'to' = all agents in thread)
1944
- // Uses auto-reinit wrapper to handle server restarts gracefully
1945
- await mcpCallWithAutoInit("send_message", {
1946
- project_key: state.projectKey,
1947
- agent_name: state.agentName,
1948
- sender_name: state.agentName,
1949
- to: [], // Broadcast to thread
1950
- subject: `[${args.importance.toUpperCase()}] Context update from ${state.agentName}`,
1951
- body_md: body,
1952
- thread_id: args.epic_id,
1997
+ // Send as broadcast to thread using embedded swarm-mail
1998
+ await sendSwarmMessage({
1999
+ projectPath: args.project_path,
2000
+ fromAgent: args.agent_name,
2001
+ toAgents: [], // Broadcast to thread
2002
+ subject: `[${args.importance.toUpperCase()}] Context update from ${args.agent_name}`,
2003
+ body,
2004
+ threadId: args.epic_id,
1953
2005
  importance: mailImportance,
1954
- ack_required: args.importance === "blocker", // Require ack for blockers
2006
+ ackRequired: args.importance === "blocker",
1955
2007
  });
1956
2008
 
1957
2009
  return JSON.stringify(
1958
2010
  {
1959
2011
  broadcast: true,
1960
2012
  epic_id: args.epic_id,
1961
- from: state.agentName,
2013
+ from: args.agent_name,
1962
2014
  bead_id: beadId,
1963
2015
  importance: args.importance,
1964
2016
  recipients: "all agents in epic",
@@ -2070,16 +2122,15 @@ export const swarm_complete = tool({
2070
2122
  );
2071
2123
  }
2072
2124
 
2073
- // Release file reservations for this agent
2074
- // Uses auto-reinit wrapper to handle server restarts - this was the original
2075
- // failure point that prompted the self-healing implementation
2125
+ // Release file reservations for this agent using embedded swarm-mail
2076
2126
  try {
2077
- await mcpCallWithAutoInit("release_file_reservations", {
2078
- project_key: args.project_key,
2079
- agent_name: args.agent_name,
2127
+ await releaseSwarmFiles({
2128
+ projectPath: args.project_key,
2129
+ agentName: args.agent_name,
2130
+ // Release all reservations for this agent
2080
2131
  });
2081
2132
  } catch (error) {
2082
- // Even with auto-reinit, release might fail (e.g., no reservations existed)
2133
+ // Release might fail (e.g., no reservations existed)
2083
2134
  // This is non-fatal - log and continue
2084
2135
  console.warn(
2085
2136
  `[swarm] Failed to release file reservations for ${args.agent_name}:`,
@@ -2092,7 +2143,7 @@ export const swarm_complete = tool({
2092
2143
  ? args.bead_id.split(".")[0]
2093
2144
  : args.bead_id;
2094
2145
 
2095
- // Send completion message
2146
+ // Send completion message using embedded swarm-mail
2096
2147
  const completionBody = [
2097
2148
  `## Subtask Complete: ${args.bead_id}`,
2098
2149
  "",
@@ -2108,14 +2159,13 @@ export const swarm_complete = tool({
2108
2159
  .filter(Boolean)
2109
2160
  .join("\n");
2110
2161
 
2111
- await mcpCallWithAutoInit("send_message", {
2112
- project_key: args.project_key,
2113
- agent_name: args.agent_name,
2114
- sender_name: args.agent_name,
2115
- to: [], // Thread broadcast
2162
+ await sendSwarmMessage({
2163
+ projectPath: args.project_key,
2164
+ fromAgent: args.agent_name,
2165
+ toAgents: [], // Thread broadcast
2116
2166
  subject: `Complete: ${args.bead_id}`,
2117
- body_md: completionBody,
2118
- thread_id: epicId,
2167
+ body: completionBody,
2168
+ threadId: epicId,
2119
2169
  importance: "normal",
2120
2170
  });
2121
2171
 
@@ -2650,7 +2700,14 @@ This tool helps you formalize learnings into a skill that future agents can disc
2650
2700
  .string()
2651
2701
  .describe("Brief summary of what was learned (1-2 sentences)"),
2652
2702
  pattern_type: tool.schema
2653
- .enum(["code-pattern", "best-practice", "gotcha", "tool-usage", "domain-knowledge", "workflow"])
2703
+ .enum([
2704
+ "code-pattern",
2705
+ "best-practice",
2706
+ "gotcha",
2707
+ "tool-usage",
2708
+ "domain-knowledge",
2709
+ "workflow",
2710
+ ])
2654
2711
  .describe("Category of the learning"),
2655
2712
  details: tool.schema
2656
2713
  .string()
@@ -2669,7 +2726,9 @@ This tool helps you formalize learnings into a skill that future agents can disc
2669
2726
  create_skill: tool.schema
2670
2727
  .boolean()
2671
2728
  .optional()
2672
- .describe("Create a skill from this learning (default: false, just document)"),
2729
+ .describe(
2730
+ "Create a skill from this learning (default: false, just document)",
2731
+ ),
2673
2732
  skill_name: tool.schema
2674
2733
  .string()
2675
2734
  .regex(/^[a-z0-9-]+$/)
@@ -2737,7 +2796,8 @@ ${args.files_context && args.files_context.length > 0 ? `## Reference Files\n\n$
2737
2796
  error: `Skill '${args.skill_name}' already exists`,
2738
2797
  existing_path: existing.path,
2739
2798
  learning: learning,
2740
- suggestion: "Use skills_update to add to existing skill, or choose a different name",
2799
+ suggestion:
2800
+ "Use skills_update to add to existing skill, or choose a different name",
2741
2801
  },
2742
2802
  null,
2743
2803
  2,
@@ -2745,7 +2805,12 @@ ${args.files_context && args.files_context.length > 0 ? `## Reference Files\n\n$
2745
2805
  }
2746
2806
 
2747
2807
  // Create skill directory and file
2748
- const skillDir = join(process.cwd(), ".opencode", "skills", args.skill_name);
2808
+ const skillDir = join(
2809
+ process.cwd(),
2810
+ ".opencode",
2811
+ "skills",
2812
+ args.skill_name,
2813
+ );
2749
2814
  const skillPath = join(skillDir, "SKILL.md");
2750
2815
 
2751
2816
  const frontmatter = [
@@ -2798,8 +2863,10 @@ ${args.files_context && args.files_context.length > 0 ? `## Reference Files\n\n$
2798
2863
  success: true,
2799
2864
  skill_created: false,
2800
2865
  learning: learning,
2801
- message: "Learning documented. Use create_skill=true to persist as a skill for future agents.",
2802
- suggested_skill_name: args.skill_name ||
2866
+ message:
2867
+ "Learning documented. Use create_skill=true to persist as a skill for future agents.",
2868
+ suggested_skill_name:
2869
+ args.skill_name ||
2803
2870
  args.summary
2804
2871
  .toLowerCase()
2805
2872
  .replace(/[^a-z0-9\s-]/g, "")
@@ -3029,7 +3096,8 @@ export const swarm_init = tool({
3029
3096
  if (availableSkills.length > 0) {
3030
3097
  skillsGuidance = `Found ${availableSkills.length} skill(s). Use skills_list to see details, skills_use to activate.`;
3031
3098
  } else {
3032
- skillsGuidance = "No skills found. Add skills to .opencode/skills/ or .claude/skills/ for specialized guidance.";
3099
+ skillsGuidance =
3100
+ "No skills found. Add skills to .opencode/skills/ or .claude/skills/ for specialized guidance.";
3033
3101
  }
3034
3102
 
3035
3103
  return JSON.stringify(
@@ -9,14 +9,18 @@
9
9
  * - cass: Cross-agent session search for historical context
10
10
  * - ubs: Universal bug scanner for pre-commit checks
11
11
  * - beads (bd): Git-backed issue tracking
12
- * - agent-mail: Multi-agent coordination server
12
+ * - swarm-mail: Embedded multi-agent coordination (PGLite-based)
13
+ * - agent-mail: DEPRECATED - Legacy MCP server (use swarm-mail instead)
13
14
  */
14
15
 
16
+ import { checkSwarmHealth } from "./streams/swarm-mail";
17
+
15
18
  export type ToolName =
16
19
  | "semantic-memory"
17
20
  | "cass"
18
21
  | "ubs"
19
22
  | "beads"
23
+ | "swarm-mail"
20
24
  | "agent-mail";
21
25
 
22
26
  export interface ToolStatus {
@@ -199,6 +203,32 @@ const toolCheckers: Record<ToolName, () => Promise<ToolStatus>> = {
199
203
  }
200
204
  },
201
205
 
206
+ "swarm-mail": async () => {
207
+ try {
208
+ // Note: checkSwarmHealth() accepts optional projectPath parameter.
209
+ // For tool availability checking, we call it without args to check global health.
210
+ // This is intentional - we're verifying the embedded Swarm Mail system is functional,
211
+ // not checking health for a specific project.
212
+ const healthResult = await checkSwarmHealth();
213
+ return {
214
+ available: healthResult.healthy,
215
+ checkedAt: new Date().toISOString(),
216
+ error: healthResult.healthy
217
+ ? undefined
218
+ : "Swarm Mail database not healthy",
219
+ version: "embedded",
220
+ };
221
+ } catch (e) {
222
+ return {
223
+ available: false,
224
+ checkedAt: new Date().toISOString(),
225
+ error: String(e),
226
+ };
227
+ }
228
+ },
229
+
230
+ // DEPRECATED: Use swarm-mail instead
231
+ // Kept for backward compatibility only
202
232
  "agent-mail": async () => {
203
233
  const reachable = await urlReachable(
204
234
  "http://127.0.0.1:8765/health/liveness",
@@ -220,8 +250,10 @@ const fallbackBehaviors: Record<ToolName, string> = {
220
250
  cass: "Decomposition proceeds without historical context from past sessions",
221
251
  ubs: "Subtask completion skips bug scanning - manual review recommended",
222
252
  beads: "Swarm cannot track issues - task coordination will be less reliable",
223
- "agent-mail":
253
+ "swarm-mail":
224
254
  "Multi-agent coordination disabled - file conflicts possible if multiple agents active",
255
+ "agent-mail":
256
+ "DEPRECATED: Use swarm-mail instead. Legacy MCP server mode - file conflicts possible if multiple agents active",
225
257
  };
226
258
 
227
259
  /**
@@ -276,6 +308,7 @@ export async function checkAllTools(): Promise<
276
308
  "cass",
277
309
  "ubs",
278
310
  "beads",
311
+ "swarm-mail",
279
312
  "agent-mail",
280
313
  ];
281
314