opencode-swarm-plugin 0.32.0 → 0.33.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/dist/index.js CHANGED
@@ -26558,7 +26558,7 @@ var init_skills = __esm(() => {
26558
26558
  "skills"
26559
26559
  ];
26560
26560
  skills_list = tool({
26561
- description: `List all available skills in the project.
26561
+ description: `[DEPRECATED] List all available skills in the project.
26562
26562
 
26563
26563
  Skills are specialized instructions that help with specific domains or tasks.
26564
26564
  Use this tool to discover what skills are available, then use skills_use to
@@ -26569,6 +26569,7 @@ Returns skill names, descriptions, and whether they have executable scripts.`,
26569
26569
  tag: tool.schema.string().optional().describe("Optional tag to filter skills by")
26570
26570
  },
26571
26571
  async execute(args) {
26572
+ console.warn("[DEPRECATED] skills_list is deprecated. OpenCode now provides native skills support. This tool will be removed in a future version.");
26572
26573
  const skills = await discoverSkills();
26573
26574
  let refs = Array.from(skills.values());
26574
26575
  if (args.tag) {
@@ -26591,7 +26592,7 @@ ${formatted}`;
26591
26592
  }
26592
26593
  });
26593
26594
  skills_use = tool({
26594
- description: `Activate a skill by loading its full instructions.
26595
+ description: `[DEPRECATED] Activate a skill by loading its full instructions.
26595
26596
 
26596
26597
  After calling this tool, follow the skill's instructions for the current task.
26597
26598
  Skills provide domain-specific guidance and best practices.
@@ -26602,6 +26603,7 @@ If the skill has scripts, you can run them with skills_execute.`,
26602
26603
  include_scripts: tool.schema.boolean().optional().describe("Also list available scripts (default: true)")
26603
26604
  },
26604
26605
  async execute(args) {
26606
+ console.warn("[DEPRECATED] skills_use is deprecated. OpenCode now provides native skills support. This tool will be removed in a future version.");
26605
26607
  const skill = await getSkill(args.name);
26606
26608
  if (!skill) {
26607
26609
  const available = await listSkills();
@@ -26634,7 +26636,7 @@ Run scripts with skills_execute tool.`;
26634
26636
  }
26635
26637
  });
26636
26638
  skills_execute = tool({
26637
- description: `Execute a script from a skill's scripts/ directory.
26639
+ description: `[DEPRECATED] Execute a script from a skill's scripts/ directory.
26638
26640
 
26639
26641
  Some skills include helper scripts for common operations.
26640
26642
  Use skills_use first to see available scripts, then execute them here.
@@ -26646,6 +26648,7 @@ Scripts run in the skill's directory with the project directory as an argument.`
26646
26648
  args: tool.schema.array(tool.schema.string()).optional().describe("Additional arguments to pass to the script")
26647
26649
  },
26648
26650
  async execute(args, ctx) {
26651
+ console.warn("[DEPRECATED] skills_execute is deprecated. OpenCode now provides native skills support. This tool will be removed in a future version.");
26649
26652
  const skill = await getSkill(args.skill);
26650
26653
  if (!skill) {
26651
26654
  return `Skill '${args.skill}' not found.`;
@@ -26691,7 +26694,7 @@ ${output}`;
26691
26694
  }
26692
26695
  });
26693
26696
  skills_read = tool({
26694
- description: `Read a resource file from a skill's directory.
26697
+ description: `[DEPRECATED] Read a resource file from a skill's directory.
26695
26698
 
26696
26699
  Skills may include additional files like:
26697
26700
  - examples.md - Example usage
@@ -26704,6 +26707,7 @@ Use this to access supplementary skill resources.`,
26704
26707
  file: tool.schema.string().describe("Relative path to the file within the skill directory")
26705
26708
  },
26706
26709
  async execute(args) {
26710
+ console.warn("[DEPRECATED] skills_read is deprecated. OpenCode now provides native skills support. This tool will be removed in a future version.");
26707
26711
  const skill = await getSkill(args.skill);
26708
26712
  if (!skill) {
26709
26713
  return `Skill '${args.skill}' not found.`;
@@ -35545,6 +35549,115 @@ Other cell operations:
35545
35549
  **Memory is the swarm's collective intelligence. Query it. Feed it.**
35546
35550
 
35547
35551
  Begin now.`;
35552
+ var RESEARCHER_PROMPT = `You are a swarm researcher gathering documentation for: **{research_id}**
35553
+
35554
+ ## [IDENTITY]
35555
+ Agent: (assigned at spawn)
35556
+ Research Task: {research_id}
35557
+ Epic: {epic_id}
35558
+
35559
+ ## [MISSION]
35560
+ Gather comprehensive documentation for the specified technologies to inform task decomposition.
35561
+
35562
+ **COORDINATOR PROVIDED THESE TECHNOLOGIES TO RESEARCH:**
35563
+ {tech_stack}
35564
+
35565
+ You do NOT discover what to research - the coordinator already decided that.
35566
+ You DO discover what TOOLS are available to fetch documentation.
35567
+
35568
+ ## [OUTPUT MODE]
35569
+ {check_upgrades}
35570
+
35571
+ ## [WORKFLOW]
35572
+
35573
+ ### Step 1: Initialize (MANDATORY FIRST)
35574
+ \`\`\`
35575
+ swarmmail_init(project_path="{project_path}", task_description="{research_id}: Documentation research")
35576
+ \`\`\`
35577
+
35578
+ ### Step 2: Discover Available Documentation Tools
35579
+ Check what's available for fetching docs:
35580
+ - **next-devtools**: \`nextjs_docs\` for Next.js documentation
35581
+ - **context7**: Library documentation lookup (\`use context7\` in prompts)
35582
+ - **fetch**: General web fetching for official docs sites
35583
+ - **pdf-brain**: Internal knowledge base search
35584
+
35585
+ **Don't assume** - check which tools exist in your environment.
35586
+
35587
+ ### Step 3: Read Installed Versions
35588
+ For each technology in the tech stack:
35589
+ 1. Check package.json (or equivalent) for installed version
35590
+ 2. Record exact version numbers
35591
+ 3. Note any version constraints (^, ~, etc.)
35592
+
35593
+ ### Step 4: Fetch Documentation
35594
+ For EACH technology in the list:
35595
+ - Use the most appropriate tool (Next.js → nextjs_docs, libraries → context7, others → fetch)
35596
+ - Fetch documentation for the INSTALLED version (not latest, unless --check-upgrades)
35597
+ - Focus on: API changes, breaking changes, migration guides, best practices
35598
+ - Extract key patterns, gotchas, and compatibility notes
35599
+
35600
+ **If --check-upgrades mode:**
35601
+ - ALSO fetch docs for the LATEST version
35602
+ - Compare installed vs latest
35603
+ - Note breaking changes, new features, migration complexity
35604
+
35605
+ ### Step 5: Store Detailed Findings
35606
+ For EACH technology, store in semantic-memory:
35607
+ \`\`\`
35608
+ semantic-memory_store(
35609
+ information="<technology-name> <version>: <key patterns, gotchas, API changes, compatibility notes>",
35610
+ tags="research, <tech-name>, documentation, {epic_id}"
35611
+ )
35612
+ \`\`\`
35613
+
35614
+ **Why store individually?** Future agents can search by technology name.
35615
+
35616
+ ### Step 6: Broadcast Summary
35617
+ Send condensed findings to coordinator:
35618
+ \`\`\`
35619
+ swarmmail_send(
35620
+ to=["coordinator"],
35621
+ subject="Research Complete: {research_id}",
35622
+ body="<brief summary - see semantic-memory for details>",
35623
+ thread_id="{epic_id}"
35624
+ )
35625
+ \`\`\`
35626
+
35627
+ ### Step 7: Return Structured Output
35628
+ Output JSON with:
35629
+ \`\`\`json
35630
+ {
35631
+ "technologies": [
35632
+ {
35633
+ "name": "string",
35634
+ "installed_version": "string",
35635
+ "latest_version": "string | null", // Only if --check-upgrades
35636
+ "key_patterns": ["string"],
35637
+ "gotchas": ["string"],
35638
+ "breaking_changes": ["string"], // Only if --check-upgrades
35639
+ "memory_id": "string" // ID of semantic-memory entry
35640
+ }
35641
+ ],
35642
+ "summary": "string" // Condensed summary for shared_context
35643
+ }
35644
+ \`\`\`
35645
+
35646
+ ## [CRITICAL REQUIREMENTS]
35647
+
35648
+ **NON-NEGOTIABLE:**
35649
+ 1. Step 1 (swarmmail_init) MUST be first
35650
+ 2. Research ONLY the technologies the coordinator specified
35651
+ 3. Fetch docs for INSTALLED versions (unless --check-upgrades)
35652
+ 4. Store detailed findings in semantic-memory (one per technology)
35653
+ 5. Return condensed summary for coordinator (full details in memory)
35654
+ 6. Use appropriate doc tools (nextjs_docs for Next.js, context7 for libraries, etc.)
35655
+
35656
+ **Output goes TWO places:**
35657
+ - **semantic-memory**: Detailed findings (searchable by future agents)
35658
+ - **Return JSON**: Condensed summary (for coordinator's shared_context)
35659
+
35660
+ Begin research now.`;
35548
35661
  var COORDINATOR_POST_WORKER_CHECKLIST = `
35549
35662
  ## ⚠️ MANDATORY: Post-Worker Review (DO THIS IMMEDIATELY)
35550
35663
 
@@ -35633,6 +35746,12 @@ For each criterion, assess passed/failed and provide brief feedback:
35633
35746
 
35634
35747
  If any criterion fails, the overall evaluation fails and retry_suggestion
35635
35748
  should describe what needs to be fixed.`;
35749
+ function formatResearcherPrompt(params) {
35750
+ const techList = params.tech_stack.map((t) => `- ${t}`).join(`
35751
+ `);
35752
+ const upgradesMode = params.check_upgrades ? "**UPGRADE COMPARISON MODE**: Fetch docs for BOTH installed AND latest versions. Compare and note breaking changes." : "**DEFAULT MODE**: Fetch docs for INSTALLED versions only (from lockfiles).";
35753
+ return RESEARCHER_PROMPT.replace(/{research_id}/g, params.research_id).replace(/{epic_id}/g, params.epic_id).replace("{tech_stack}", techList).replace("{project_path}", params.project_path).replace("{check_upgrades}", upgradesMode);
35754
+ }
35636
35755
  function formatSubtaskPromptV2(params) {
35637
35756
  const fileList = params.files.length > 0 ? params.files.map((f) => `- \`${f}\``).join(`
35638
35757
  `) : "(no specific files - use judgment)";
@@ -35782,6 +35901,48 @@ var swarm_spawn_subtask = tool({
35782
35901
  }, null, 2);
35783
35902
  }
35784
35903
  });
35904
+ var swarm_spawn_researcher = tool({
35905
+ description: "Prepare a research task for spawning. Returns prompt for gathering technology documentation. Researcher fetches docs and stores findings in semantic-memory.",
35906
+ args: {
35907
+ research_id: tool.schema.string().describe("Unique ID for this research task"),
35908
+ epic_id: tool.schema.string().describe("Parent epic ID"),
35909
+ tech_stack: tool.schema.array(tool.schema.string()).describe("Explicit list of technologies to research (from coordinator)"),
35910
+ project_path: tool.schema.string().describe("Absolute project path for swarmmail_init"),
35911
+ check_upgrades: tool.schema.boolean().optional().describe("If true, compare installed vs latest versions (default: false)")
35912
+ },
35913
+ async execute(args) {
35914
+ const prompt = formatResearcherPrompt({
35915
+ research_id: args.research_id,
35916
+ epic_id: args.epic_id,
35917
+ tech_stack: args.tech_stack,
35918
+ project_path: args.project_path,
35919
+ check_upgrades: args.check_upgrades ?? false
35920
+ });
35921
+ return JSON.stringify({
35922
+ prompt,
35923
+ research_id: args.research_id,
35924
+ epic_id: args.epic_id,
35925
+ tech_stack: args.tech_stack,
35926
+ project_path: args.project_path,
35927
+ check_upgrades: args.check_upgrades ?? false,
35928
+ subagent_type: "swarm/researcher",
35929
+ expected_output: {
35930
+ technologies: [
35931
+ {
35932
+ name: "string",
35933
+ installed_version: "string",
35934
+ latest_version: "string | null",
35935
+ key_patterns: ["string"],
35936
+ gotchas: ["string"],
35937
+ breaking_changes: ["string"],
35938
+ memory_id: "string"
35939
+ }
35940
+ ],
35941
+ summary: "string"
35942
+ }
35943
+ }, null, 2);
35944
+ }
35945
+ });
35785
35946
  var swarm_evaluation_prompt = tool({
35786
35947
  description: "Generate self-evaluation prompt for a completed subtask",
35787
35948
  args: {
@@ -35893,6 +36054,7 @@ ${args.context}` : `## Additional Context
35893
36054
  var promptTools = {
35894
36055
  swarm_subtask_prompt,
35895
36056
  swarm_spawn_subtask,
36057
+ swarm_spawn_researcher,
35896
36058
  swarm_evaluation_prompt,
35897
36059
  swarm_plan_prompt
35898
36060
  };
@@ -50706,6 +50868,368 @@ var memoryTools = {
50706
50868
  "semantic-memory_check": semantic_memory_check
50707
50869
  };
50708
50870
 
50871
+ // src/observability-tools.ts
50872
+ init_dist();
50873
+ import {
50874
+ agentActivity,
50875
+ checkpointFrequency,
50876
+ failedDecompositions,
50877
+ getSwarmMailLibSQL as getSwarmMailLibSQL4,
50878
+ humanFeedback,
50879
+ lockContention,
50880
+ messageLatency,
50881
+ recoverySuccess,
50882
+ scopeViolations,
50883
+ strategySuccessRates,
50884
+ taskDuration
50885
+ } from "swarm-mail";
50886
+ function parseSince(since) {
50887
+ const now = Date.now();
50888
+ const match11 = since.match(/^(\d+)([dhm])$/);
50889
+ if (!match11) {
50890
+ throw new Error(`Invalid since format: ${since}. Use "7d", "24h", or "1h"`);
50891
+ }
50892
+ const [, value, unit] = match11;
50893
+ const num = Number.parseInt(value, 10);
50894
+ switch (unit) {
50895
+ case "d":
50896
+ return now - num * 24 * 60 * 60 * 1000;
50897
+ case "h":
50898
+ return now - num * 60 * 60 * 1000;
50899
+ case "m":
50900
+ return now - num * 60 * 1000;
50901
+ default:
50902
+ throw new Error(`Unknown unit: ${unit}`);
50903
+ }
50904
+ }
50905
+ async function executeQuery(swarmMail, query) {
50906
+ const db = await swarmMail.getDatabase();
50907
+ const result = await db.query(query.sql, Object.values(query.parameters || {}));
50908
+ return result.rows;
50909
+ }
50910
+ function formatSummary(queryType, results) {
50911
+ if (results.length === 0) {
50912
+ return `No ${queryType} data found.`;
50913
+ }
50914
+ const count = results.length;
50915
+ const preview = results.slice(0, 3);
50916
+ return `${queryType}: ${count} result(s). Top 3: ${JSON.stringify(preview, null, 2).slice(0, 400)}`;
50917
+ }
50918
+ function capResults(results) {
50919
+ return results.slice(0, 50);
50920
+ }
50921
+ var swarm_analytics = tool({
50922
+ description: "Query pre-built analytics for swarm coordination. Returns structured data about failed decompositions, strategy success rates, lock contention, agent activity, message latency, scope violations, task duration, checkpoint frequency, recovery success, and human feedback.",
50923
+ args: {
50924
+ query: tool.schema.enum([
50925
+ "failed-decompositions",
50926
+ "strategy-success-rates",
50927
+ "lock-contention",
50928
+ "agent-activity",
50929
+ "message-latency",
50930
+ "scope-violations",
50931
+ "task-duration",
50932
+ "checkpoint-frequency",
50933
+ "recovery-success",
50934
+ "human-feedback"
50935
+ ]).describe("Type of analytics query to run"),
50936
+ since: tool.schema.string().optional().describe("Time filter: '7d', '24h', '1h' (optional)"),
50937
+ format: tool.schema.enum(["json", "summary"]).optional().describe("Output format: 'json' (default) or 'summary' (context-efficient)")
50938
+ },
50939
+ async execute(args2) {
50940
+ try {
50941
+ const projectPath = process.cwd();
50942
+ const db = await getSwarmMailLibSQL4(projectPath);
50943
+ const filters = {
50944
+ project_key: projectPath
50945
+ };
50946
+ if (args2.since) {
50947
+ filters.since = parseSince(args2.since);
50948
+ }
50949
+ let query;
50950
+ switch (args2.query) {
50951
+ case "failed-decompositions":
50952
+ query = failedDecompositions(filters);
50953
+ break;
50954
+ case "strategy-success-rates":
50955
+ query = strategySuccessRates(filters);
50956
+ break;
50957
+ case "lock-contention":
50958
+ query = lockContention(filters);
50959
+ break;
50960
+ case "agent-activity":
50961
+ query = agentActivity(filters);
50962
+ break;
50963
+ case "message-latency":
50964
+ query = messageLatency(filters);
50965
+ break;
50966
+ case "scope-violations":
50967
+ query = scopeViolations.buildQuery ? scopeViolations.buildQuery(filters) : scopeViolations;
50968
+ break;
50969
+ case "task-duration":
50970
+ query = taskDuration.buildQuery ? taskDuration.buildQuery(filters) : taskDuration;
50971
+ break;
50972
+ case "checkpoint-frequency":
50973
+ query = checkpointFrequency.buildQuery ? checkpointFrequency.buildQuery(filters) : checkpointFrequency;
50974
+ break;
50975
+ case "recovery-success":
50976
+ query = recoverySuccess.buildQuery ? recoverySuccess.buildQuery(filters) : recoverySuccess;
50977
+ break;
50978
+ case "human-feedback":
50979
+ query = humanFeedback.buildQuery ? humanFeedback.buildQuery(filters) : humanFeedback;
50980
+ break;
50981
+ default:
50982
+ return JSON.stringify({
50983
+ error: `Unknown query type: ${args2.query}`
50984
+ });
50985
+ }
50986
+ const results = await executeQuery(db, query);
50987
+ if (args2.format === "summary") {
50988
+ return formatSummary(args2.query, results);
50989
+ }
50990
+ return JSON.stringify({
50991
+ query: args2.query,
50992
+ filters,
50993
+ count: results.length,
50994
+ results
50995
+ }, null, 2);
50996
+ } catch (error45) {
50997
+ return JSON.stringify({
50998
+ error: error45 instanceof Error ? error45.message : String(error45)
50999
+ });
51000
+ }
51001
+ }
51002
+ });
51003
+ var swarm_query = tool({
51004
+ description: "Execute raw SQL queries against the swarm event store. Context-safe: results capped at 50 rows. Useful for custom analytics and debugging.",
51005
+ args: {
51006
+ sql: tool.schema.string().describe("SQL query to execute (SELECT only for safety)"),
51007
+ format: tool.schema.enum(["json", "table"]).optional().describe("Output format: 'json' (default) or 'table' (visual)")
51008
+ },
51009
+ async execute(args2) {
51010
+ try {
51011
+ const projectPath = process.cwd();
51012
+ const swarmMail = await getSwarmMailLibSQL4(projectPath);
51013
+ const db = await swarmMail.getDatabase();
51014
+ if (!args2.sql.trim().toLowerCase().startsWith("select")) {
51015
+ return JSON.stringify({
51016
+ error: "Only SELECT queries are allowed for safety"
51017
+ });
51018
+ }
51019
+ const result = await db.query(args2.sql, []);
51020
+ const rows = result.rows;
51021
+ const cappedRows = capResults(rows);
51022
+ if (args2.format === "table") {
51023
+ if (cappedRows.length === 0) {
51024
+ return "No results";
51025
+ }
51026
+ const headers = Object.keys(cappedRows[0]);
51027
+ const headerRow = headers.join(" | ");
51028
+ const separator = headers.map(() => "---").join(" | ");
51029
+ const dataRows = cappedRows.map((row) => headers.map((h) => row[h]).join(" | "));
51030
+ return [headerRow, separator, ...dataRows].join(`
51031
+ `);
51032
+ }
51033
+ return JSON.stringify({
51034
+ count: cappedRows.length,
51035
+ total: rows.length,
51036
+ capped: rows.length > 50,
51037
+ results: cappedRows
51038
+ }, null, 2);
51039
+ } catch (error45) {
51040
+ return JSON.stringify({
51041
+ error: error45 instanceof Error ? error45.message : String(error45)
51042
+ });
51043
+ }
51044
+ }
51045
+ });
51046
+ var swarm_diagnose = tool({
51047
+ description: "Auto-diagnose issues for a specific epic or task. Returns structured diagnosis with blockers, conflicts, slow tasks, errors, and timeline.",
51048
+ args: {
51049
+ epic_id: tool.schema.string().optional().describe("Epic ID to diagnose"),
51050
+ bead_id: tool.schema.string().optional().describe("Task ID to diagnose"),
51051
+ include: tool.schema.array(tool.schema.enum([
51052
+ "blockers",
51053
+ "conflicts",
51054
+ "slow_tasks",
51055
+ "errors",
51056
+ "timeline"
51057
+ ])).optional().describe("What to include in diagnosis (default: all)")
51058
+ },
51059
+ async execute(args2) {
51060
+ try {
51061
+ const projectPath = process.cwd();
51062
+ const swarmMail = await getSwarmMailLibSQL4(projectPath);
51063
+ const db = await swarmMail.getDatabase();
51064
+ const diagnosis = [];
51065
+ const include = args2.include || [
51066
+ "blockers",
51067
+ "conflicts",
51068
+ "slow_tasks",
51069
+ "errors",
51070
+ "timeline"
51071
+ ];
51072
+ if (include.includes("blockers")) {
51073
+ const blockerQuery = `
51074
+ SELECT json_extract(data, '$.agent_name') as agent,
51075
+ json_extract(data, '$.bead_id') as bead_id,
51076
+ timestamp
51077
+ FROM events
51078
+ WHERE type = 'task_blocked'
51079
+ ${args2.epic_id ? "AND json_extract(data, '$.epic_id') = ?" : ""}
51080
+ ${args2.bead_id ? "AND json_extract(data, '$.bead_id') = ?" : ""}
51081
+ ORDER BY timestamp DESC
51082
+ LIMIT 10
51083
+ `;
51084
+ const params = [];
51085
+ if (args2.epic_id)
51086
+ params.push(args2.epic_id);
51087
+ if (args2.bead_id)
51088
+ params.push(args2.bead_id);
51089
+ const blockers = await db.query(blockerQuery, params);
51090
+ if (blockers.rows.length > 0) {
51091
+ diagnosis.push({
51092
+ type: "blockers",
51093
+ message: `Found ${blockers.rows.length} blocked task(s)`,
51094
+ severity: "high"
51095
+ });
51096
+ }
51097
+ }
51098
+ if (include.includes("errors")) {
51099
+ const errorQuery = `
51100
+ SELECT type, json_extract(data, '$.error_count') as error_count
51101
+ FROM events
51102
+ WHERE type = 'subtask_outcome'
51103
+ AND json_extract(data, '$.success') = 'false'
51104
+ ${args2.epic_id ? "AND json_extract(data, '$.epic_id') = ?" : ""}
51105
+ ${args2.bead_id ? "AND json_extract(data, '$.bead_id') = ?" : ""}
51106
+ LIMIT 10
51107
+ `;
51108
+ const params = [];
51109
+ if (args2.epic_id)
51110
+ params.push(args2.epic_id);
51111
+ if (args2.bead_id)
51112
+ params.push(args2.bead_id);
51113
+ const errors3 = await db.query(errorQuery, params);
51114
+ if (errors3.rows.length > 0) {
51115
+ diagnosis.push({
51116
+ type: "errors",
51117
+ message: `Found ${errors3.rows.length} failed task(s)`,
51118
+ severity: "high"
51119
+ });
51120
+ }
51121
+ }
51122
+ let timeline = [];
51123
+ if (include.includes("timeline")) {
51124
+ const timelineQuery = `
51125
+ SELECT timestamp, type, json_extract(data, '$.agent_name') as agent
51126
+ FROM events
51127
+ ${args2.epic_id ? "WHERE json_extract(data, '$.epic_id') = ?" : ""}
51128
+ ${args2.bead_id ? (args2.epic_id ? "AND" : "WHERE") + " json_extract(data, '$.bead_id') = ?" : ""}
51129
+ ORDER BY timestamp DESC
51130
+ LIMIT 20
51131
+ `;
51132
+ const params = [];
51133
+ if (args2.epic_id)
51134
+ params.push(args2.epic_id);
51135
+ if (args2.bead_id)
51136
+ params.push(args2.bead_id);
51137
+ const events = await db.query(timelineQuery, params);
51138
+ timeline = events.rows;
51139
+ }
51140
+ return JSON.stringify({
51141
+ epic_id: args2.epic_id,
51142
+ bead_id: args2.bead_id,
51143
+ diagnosis,
51144
+ timeline: include.includes("timeline") ? timeline : undefined
51145
+ }, null, 2);
51146
+ } catch (error45) {
51147
+ return JSON.stringify({
51148
+ error: error45 instanceof Error ? error45.message : String(error45)
51149
+ });
51150
+ }
51151
+ }
51152
+ });
51153
+ var swarm_insights = tool({
51154
+ description: "Generate learning insights from swarm coordination metrics. Analyzes success rates, duration, conflicts, and retries to provide actionable recommendations.",
51155
+ args: {
51156
+ scope: tool.schema.enum(["epic", "project", "recent"]).describe("Scope of analysis: 'epic', 'project', or 'recent'"),
51157
+ epic_id: tool.schema.string().optional().describe("Epic ID (required if scope='epic')"),
51158
+ metrics: tool.schema.array(tool.schema.enum([
51159
+ "success_rate",
51160
+ "avg_duration",
51161
+ "conflict_rate",
51162
+ "retry_rate"
51163
+ ])).describe("Metrics to analyze")
51164
+ },
51165
+ async execute(args2) {
51166
+ try {
51167
+ if (args2.scope === "epic" && !args2.epic_id) {
51168
+ return JSON.stringify({
51169
+ error: "epic_id is required when scope='epic'"
51170
+ });
51171
+ }
51172
+ const projectPath = process.cwd();
51173
+ const swarmMail = await getSwarmMailLibSQL4(projectPath);
51174
+ const db = await swarmMail.getDatabase();
51175
+ const insights = [];
51176
+ if (args2.metrics.includes("success_rate")) {
51177
+ const query = `
51178
+ SELECT
51179
+ SUM(CASE WHEN json_extract(data, '$.success') = 'true' THEN 1 ELSE 0 END) as successes,
51180
+ COUNT(*) as total
51181
+ FROM events
51182
+ WHERE type = 'subtask_outcome'
51183
+ ${args2.epic_id ? "AND json_extract(data, '$.epic_id') = ?" : ""}
51184
+ `;
51185
+ const result = await db.query(query, args2.epic_id ? [args2.epic_id] : []);
51186
+ const row = result.rows[0];
51187
+ if (row && row.total > 0) {
51188
+ const rate = row.successes / row.total * 100;
51189
+ insights.push({
51190
+ metric: "success_rate",
51191
+ value: `${rate.toFixed(1)}%`,
51192
+ insight: rate < 50 ? "Low success rate - review decomposition strategy" : rate < 80 ? "Moderate success rate - monitor for patterns" : "Good success rate - maintain current approach"
51193
+ });
51194
+ }
51195
+ }
51196
+ if (args2.metrics.includes("avg_duration")) {
51197
+ const query = `
51198
+ SELECT AVG(CAST(json_extract(data, '$.duration_ms') AS REAL)) as avg_duration
51199
+ FROM events
51200
+ WHERE type = 'subtask_outcome'
51201
+ ${args2.epic_id ? "AND json_extract(data, '$.epic_id') = ?" : ""}
51202
+ `;
51203
+ const result = await db.query(query, args2.epic_id ? [args2.epic_id] : []);
51204
+ const row = result.rows[0];
51205
+ if (row?.avg_duration) {
51206
+ const avgMinutes = (row.avg_duration / 60000).toFixed(1);
51207
+ insights.push({
51208
+ metric: "avg_duration",
51209
+ value: `${avgMinutes} min`,
51210
+ insight: row.avg_duration > 600000 ? "Tasks taking >10min - consider smaller decomposition" : "Task duration is reasonable"
51211
+ });
51212
+ }
51213
+ }
51214
+ return JSON.stringify({
51215
+ scope: args2.scope,
51216
+ epic_id: args2.epic_id,
51217
+ insights
51218
+ }, null, 2);
51219
+ } catch (error45) {
51220
+ return JSON.stringify({
51221
+ error: error45 instanceof Error ? error45.message : String(error45)
51222
+ });
51223
+ }
51224
+ }
51225
+ });
51226
+ var observabilityTools = {
51227
+ swarm_analytics,
51228
+ swarm_query,
51229
+ swarm_diagnose,
51230
+ swarm_insights
51231
+ };
51232
+
50709
51233
  // src/output-guardrails.ts
50710
51234
  var DEFAULT_GUARDRAIL_CONFIG = {
50711
51235
  defaultMaxChars: 32000,
@@ -51647,7 +52171,8 @@ var SwarmPlugin = async (input) => {
51647
52171
  ...repoCrawlTools,
51648
52172
  ...skillsTools,
51649
52173
  ...mandateTools,
51650
- ...memoryTools
52174
+ ...memoryTools,
52175
+ ...observabilityTools
51651
52176
  },
51652
52177
  event: async ({ event }) => {
51653
52178
  if (event.type === "session.idle") {