opencode-swarm 6.86.10 → 6.86.12

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/cli/index.js CHANGED
@@ -18580,7 +18580,7 @@ import * as path33 from "path";
18580
18580
  // package.json
18581
18581
  var package_default = {
18582
18582
  name: "opencode-swarm",
18583
- version: "6.86.10",
18583
+ version: "6.86.12",
18584
18584
  description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
18585
18585
  main: "dist/index.js",
18586
18586
  types: "dist/index.d.ts",
@@ -19091,13 +19091,6 @@ var AgentOverrideConfigSchema = exports_external.object({
19091
19091
  temperature: exports_external.number().min(0).max(2).optional(),
19092
19092
  disabled: exports_external.boolean().optional(),
19093
19093
  fallback_models: exports_external.array(exports_external.string()).max(3).optional()
19094
- }).refine((data) => {
19095
- if (data.model && !data.fallback_models) {
19096
- console.warn(`[opencode-swarm] WARNING: Agent configured with custom model "${data.model}" but no fallback_models. This means if the custom model fails, there is no fallback protection. Consider adding fallback_models for reliability.`);
19097
- }
19098
- return true;
19099
- }, {
19100
- message: "Agent configuration warning: Custom model without fallback protection"
19101
19094
  });
19102
19095
  var SwarmConfigSchema = exports_external.object({
19103
19096
  name: exports_external.string().optional(),
@@ -39487,6 +39480,9 @@ async function handleHistoryCommand(directory, _args) {
39487
39480
  const historyData = await getHistoryData(directory);
39488
39481
  return formatHistoryMarkdown(historyData);
39489
39482
  }
39483
+ // src/commands/knowledge.ts
39484
+ import { join as join21 } from "path";
39485
+
39490
39486
  // src/hooks/knowledge-migrator.ts
39491
39487
  init_logger();
39492
39488
  import { randomUUID as randomUUID2 } from "crypto";
@@ -39717,34 +39713,65 @@ async function writeSentinel(sentinelPath, migrated, dropped) {
39717
39713
  }
39718
39714
 
39719
39715
  // src/commands/knowledge.ts
39716
+ function resolveEntryByPrefix(entries, inputId) {
39717
+ const exact = entries.find((e) => e.id === inputId);
39718
+ if (exact)
39719
+ return { entry: exact };
39720
+ const matches = entries.filter((e) => e.id.startsWith(inputId));
39721
+ if (matches.length === 0) {
39722
+ return { error: `No entry found matching '${inputId}'.` };
39723
+ }
39724
+ if (matches.length === 1) {
39725
+ return { entry: matches[0] };
39726
+ }
39727
+ const candidates = matches.map((e) => e.id).join(`
39728
+ `);
39729
+ return {
39730
+ error: `Ambiguous prefix '${inputId}' matches ${matches.length} entries:
39731
+ ${candidates}`
39732
+ };
39733
+ }
39720
39734
  async function handleKnowledgeQuarantineCommand(directory, args) {
39721
- const entryId = args[0];
39722
- if (!entryId) {
39735
+ const inputId = args[0];
39736
+ if (!inputId) {
39723
39737
  return "Usage: /swarm knowledge quarantine <id> [reason]";
39724
39738
  }
39725
- if (!/^[a-zA-Z0-9_-]{1,64}$/.test(entryId)) {
39739
+ if (!/^[a-zA-Z0-9_-]{1,64}$/.test(inputId)) {
39726
39740
  return "Invalid entry ID. IDs must be 1-64 characters: letters, digits, hyphens, underscores only.";
39727
39741
  }
39728
39742
  const reason = args.slice(1).join(" ") || "Quarantined via /swarm knowledge quarantine command";
39729
39743
  try {
39730
- await quarantineEntry(directory, entryId, reason, "user");
39731
- return `\u2705 Entry ${entryId} quarantined successfully.`;
39744
+ const entries = await readKnowledge(resolveSwarmKnowledgePath(directory));
39745
+ const resolved = resolveEntryByPrefix(entries, inputId);
39746
+ if ("error" in resolved) {
39747
+ return `\u274C ${resolved.error}`;
39748
+ }
39749
+ const fullId = resolved.entry.id;
39750
+ await quarantineEntry(directory, fullId, reason, "user");
39751
+ return `\u2705 Entry ${fullId} quarantined successfully.`;
39732
39752
  } catch (error93) {
39733
39753
  console.warn("[knowledge-command] quarantineEntry error:", error93 instanceof Error ? error93.message : String(error93));
39734
39754
  return `\u274C Failed to quarantine entry. Check the entry ID and try again.`;
39735
39755
  }
39736
39756
  }
39737
39757
  async function handleKnowledgeRestoreCommand(directory, args) {
39738
- const entryId = args[0];
39739
- if (!entryId) {
39758
+ const inputId = args[0];
39759
+ if (!inputId) {
39740
39760
  return "Usage: /swarm knowledge restore <id>";
39741
39761
  }
39742
- if (!/^[a-zA-Z0-9_-]{1,64}$/.test(entryId)) {
39762
+ if (!/^[a-zA-Z0-9_-]{1,64}$/.test(inputId)) {
39743
39763
  return "Invalid entry ID. IDs must be 1-64 characters: letters, digits, hyphens, underscores only.";
39744
39764
  }
39745
39765
  try {
39746
- await restoreEntry(directory, entryId);
39747
- return `\u2705 Entry ${entryId} restored successfully.`;
39766
+ const quarantinePath = join21(directory, ".swarm", "knowledge-quarantined.jsonl");
39767
+ const entries = await readKnowledge(quarantinePath);
39768
+ const resolved = resolveEntryByPrefix(entries, inputId);
39769
+ if ("error" in resolved) {
39770
+ return `\u274C ${resolved.error}`;
39771
+ }
39772
+ const fullId = resolved.entry.id;
39773
+ await restoreEntry(directory, fullId);
39774
+ return `\u2705 Entry ${fullId} restored successfully.`;
39748
39775
  } catch (error93) {
39749
39776
  console.warn("[knowledge-command] restoreEntry error:", error93 instanceof Error ? error93.message : String(error93));
39750
39777
  return `\u274C Failed to restore entry. Check the entry ID and try again.`;
@@ -39782,16 +39809,16 @@ async function handleKnowledgeListCommand(directory, _args) {
39782
39809
  const lines = [
39783
39810
  `## Knowledge Entries (${entries.length} total)`,
39784
39811
  "",
39785
- "| ID | Category | Confidence | Lesson (truncated) |",
39786
- "|------|----------|------------|---------------------|"
39812
+ "| ID (prefix) | Category | Confidence | Lesson (truncated) |",
39813
+ "|--------------|----------|------------|---------------------|"
39787
39814
  ];
39788
39815
  for (const entry of entries) {
39789
39816
  const truncatedLesson = entry.lesson.length > 60 ? `${entry.lesson.slice(0, 57)}...` : entry.lesson;
39790
39817
  const confidencePct = Math.round(entry.confidence * 100);
39791
- lines.push(`| ${entry.id.slice(0, 8)}... | ${entry.category} | ${confidencePct}% | ${truncatedLesson} |`);
39818
+ lines.push(`| ${entry.id.slice(0, 12)}\u2026 | ${entry.category} | ${confidencePct}% | ${truncatedLesson} |`);
39792
39819
  }
39793
39820
  lines.push("");
39794
- lines.push("Use `/swarm knowledge quarantine <id>` to hide an entry.");
39821
+ lines.push("Use `/swarm knowledge quarantine <id-prefix>` to hide an entry. Prefix matching is supported \u2014 the 12-character prefix shown is unique in most stores.");
39795
39822
  return lines.join(`
39796
39823
  `);
39797
39824
  } catch (error93) {
@@ -1,11 +1,13 @@
1
1
  /**
2
2
  * Handles /swarm knowledge quarantine <id> [reason] command.
3
3
  * Moves a knowledge entry to quarantine with optional reason.
4
+ * Accepts a full ID or a unique prefix.
4
5
  */
5
6
  export declare function handleKnowledgeQuarantineCommand(directory: string, args: string[]): Promise<string>;
6
7
  /**
7
8
  * Handles /swarm knowledge restore <id> command.
8
9
  * Restores a quarantined knowledge entry.
10
+ * Accepts a full ID or a unique prefix.
9
11
  */
10
12
  export declare function handleKnowledgeRestoreCommand(directory: string, args: string[]): Promise<string>;
11
13
  /**
package/dist/index.js CHANGED
@@ -33,7 +33,7 @@ var package_default;
33
33
  var init_package = __esm(() => {
34
34
  package_default = {
35
35
  name: "opencode-swarm",
36
- version: "6.86.10",
36
+ version: "6.86.12",
37
37
  description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
38
38
  main: "dist/index.js",
39
39
  types: "dist/index.d.ts",
@@ -14798,13 +14798,6 @@ var init_schema = __esm(() => {
14798
14798
  temperature: exports_external.number().min(0).max(2).optional(),
14799
14799
  disabled: exports_external.boolean().optional(),
14800
14800
  fallback_models: exports_external.array(exports_external.string()).max(3).optional()
14801
- }).refine((data) => {
14802
- if (data.model && !data.fallback_models) {
14803
- console.warn(`[opencode-swarm] WARNING: Agent configured with custom model "${data.model}" but no fallback_models. This means if the custom model fails, there is no fallback protection. Consider adding fallback_models for reliability.`);
14804
- }
14805
- return true;
14806
- }, {
14807
- message: "Agent configuration warning: Custom model without fallback protection"
14808
14801
  });
14809
14802
  SwarmConfigSchema = exports_external.object({
14810
14803
  name: exports_external.string().optional(),
@@ -48403,34 +48396,66 @@ var init_knowledge_migrator = __esm(() => {
48403
48396
  });
48404
48397
 
48405
48398
  // src/commands/knowledge.ts
48399
+ import { join as join28 } from "node:path";
48400
+ function resolveEntryByPrefix(entries, inputId) {
48401
+ const exact = entries.find((e) => e.id === inputId);
48402
+ if (exact)
48403
+ return { entry: exact };
48404
+ const matches = entries.filter((e) => e.id.startsWith(inputId));
48405
+ if (matches.length === 0) {
48406
+ return { error: `No entry found matching '${inputId}'.` };
48407
+ }
48408
+ if (matches.length === 1) {
48409
+ return { entry: matches[0] };
48410
+ }
48411
+ const candidates = matches.map((e) => e.id).join(`
48412
+ `);
48413
+ return {
48414
+ error: `Ambiguous prefix '${inputId}' matches ${matches.length} entries:
48415
+ ${candidates}`
48416
+ };
48417
+ }
48406
48418
  async function handleKnowledgeQuarantineCommand(directory, args2) {
48407
- const entryId = args2[0];
48408
- if (!entryId) {
48419
+ const inputId = args2[0];
48420
+ if (!inputId) {
48409
48421
  return "Usage: /swarm knowledge quarantine <id> [reason]";
48410
48422
  }
48411
- if (!/^[a-zA-Z0-9_-]{1,64}$/.test(entryId)) {
48423
+ if (!/^[a-zA-Z0-9_-]{1,64}$/.test(inputId)) {
48412
48424
  return "Invalid entry ID. IDs must be 1-64 characters: letters, digits, hyphens, underscores only.";
48413
48425
  }
48414
48426
  const reason = args2.slice(1).join(" ") || "Quarantined via /swarm knowledge quarantine command";
48415
48427
  try {
48416
- await quarantineEntry(directory, entryId, reason, "user");
48417
- return `✅ Entry ${entryId} quarantined successfully.`;
48428
+ const entries = await readKnowledge(resolveSwarmKnowledgePath(directory));
48429
+ const resolved = resolveEntryByPrefix(entries, inputId);
48430
+ if ("error" in resolved) {
48431
+ return `❌ ${resolved.error}`;
48432
+ }
48433
+ const fullId = resolved.entry.id;
48434
+ await quarantineEntry(directory, fullId, reason, "user");
48435
+ return `✅ Entry ${fullId} quarantined successfully.`;
48418
48436
  } catch (error93) {
48419
48437
  console.warn("[knowledge-command] quarantineEntry error:", error93 instanceof Error ? error93.message : String(error93));
48420
48438
  return `❌ Failed to quarantine entry. Check the entry ID and try again.`;
48421
48439
  }
48422
48440
  }
48423
48441
  async function handleKnowledgeRestoreCommand(directory, args2) {
48424
- const entryId = args2[0];
48425
- if (!entryId) {
48442
+ const inputId = args2[0];
48443
+ if (!inputId) {
48426
48444
  return "Usage: /swarm knowledge restore <id>";
48427
48445
  }
48428
- if (!/^[a-zA-Z0-9_-]{1,64}$/.test(entryId)) {
48446
+ if (!/^[a-zA-Z0-9_-]{1,64}$/.test(inputId)) {
48429
48447
  return "Invalid entry ID. IDs must be 1-64 characters: letters, digits, hyphens, underscores only.";
48430
48448
  }
48431
48449
  try {
48432
- await restoreEntry(directory, entryId);
48433
- return `✅ Entry ${entryId} restored successfully.`;
48450
+ const quarantinePath = join28(directory, ".swarm", "knowledge-quarantined.jsonl");
48451
+ const entries = await readKnowledge(quarantinePath);
48452
+ const resolved = resolveEntryByPrefix(entries, inputId);
48453
+ if ("error" in resolved) {
48454
+ return `❌ ${resolved.error}`;
48455
+ }
48456
+ const fullId = resolved.entry.id;
48457
+ await restoreEntry(directory, fullId);
48458
+ return `✅ Entry ${fullId} restored successfully.`;
48434
48459
  } catch (error93) {
48435
48460
  console.warn("[knowledge-command] restoreEntry error:", error93 instanceof Error ? error93.message : String(error93));
48436
48461
  return `❌ Failed to restore entry. Check the entry ID and try again.`;
@@ -48468,16 +48493,16 @@ async function handleKnowledgeListCommand(directory, _args) {
48468
48493
  const lines = [
48469
48494
  `## Knowledge Entries (${entries.length} total)`,
48470
48495
  "",
48471
- "| ID | Category | Confidence | Lesson (truncated) |",
48472
- "|------|----------|------------|---------------------|"
48496
+ "| ID (prefix) | Category | Confidence | Lesson (truncated) |",
48497
+ "|--------------|----------|------------|---------------------|"
48473
48498
  ];
48474
48499
  for (const entry of entries) {
48475
48500
  const truncatedLesson = entry.lesson.length > 60 ? `${entry.lesson.slice(0, 57)}...` : entry.lesson;
48476
48501
  const confidencePct = Math.round(entry.confidence * 100);
48477
- lines.push(`| ${entry.id.slice(0, 8)}... | ${entry.category} | ${confidencePct}% | ${truncatedLesson} |`);
48502
+ lines.push(`| ${entry.id.slice(0, 12)} | ${entry.category} | ${confidencePct}% | ${truncatedLesson} |`);
48478
48503
  }
48479
48504
  lines.push("");
48480
- lines.push("Use `/swarm knowledge quarantine <id>` to hide an entry.");
48505
+ lines.push("Use `/swarm knowledge quarantine <id-prefix>` to hide an entry. Prefix matching is supported — the 12-character prefix shown is unique in most stores.");
48481
48506
  return lines.join(`
48482
48507
  `);
48483
48508
  } catch (error93) {
@@ -63848,7 +63873,7 @@ ${content.substring(endIndex + 1)}`;
63848
63873
  init_manager();
63849
63874
  init_utils2();
63850
63875
  import * as fs31 from "node:fs";
63851
- import { join as join41 } from "node:path";
63876
+ import { join as join42 } from "node:path";
63852
63877
  function createCompactionCustomizerHook(config3, directory) {
63853
63878
  const enabled = config3.hooks?.compaction !== false;
63854
63879
  if (!enabled) {
@@ -63893,7 +63918,7 @@ function createCompactionCustomizerHook(config3, directory) {
63893
63918
  }
63894
63919
  }
63895
63920
  try {
63896
- const summariesDir = join41(directory, ".swarm", "summaries");
63921
+ const summariesDir = join42(directory, ".swarm", "summaries");
63897
63922
  const files = await fs31.promises.readdir(summariesDir);
63898
63923
  if (files.length > 0) {
63899
63924
  const count = files.length;
@@ -74626,7 +74651,7 @@ import {
74626
74651
  readFileSync as readFileSync36,
74627
74652
  writeFileSync as writeFileSync11
74628
74653
  } from "node:fs";
74629
- import { join as join65 } from "node:path";
74654
+ import { join as join66 } from "node:path";
74630
74655
  var EVIDENCE_DIR2 = ".swarm/evidence";
74631
74656
  var VALID_TASK_ID = /^\d+\.\d+(\.\d+)*$/;
74632
74657
  var COUNCIL_GATE_NAME = "council";
@@ -74660,9 +74685,9 @@ function writeCouncilEvidence(workingDir, synthesis) {
74660
74685
  if (!VALID_TASK_ID.test(synthesis.taskId)) {
74661
74686
  throw new Error(`writeCouncilEvidence: invalid taskId "${synthesis.taskId}" — must match N.M or N.M.P format`);
74662
74687
  }
74663
- const dir = join65(workingDir, EVIDENCE_DIR2);
74688
+ const dir = join66(workingDir, EVIDENCE_DIR2);
74664
74689
  mkdirSync18(dir, { recursive: true });
74665
- const filePath = join65(dir, `${synthesis.taskId}.json`);
74690
+ const filePath = join66(dir, `${synthesis.taskId}.json`);
74666
74691
  const existingRoot = Object.create(null);
74667
74692
  if (existsSync37(filePath)) {
74668
74693
  try {
@@ -74696,7 +74721,7 @@ function writeCouncilEvidence(workingDir, synthesis) {
74696
74721
  updated.required_gates = [];
74697
74722
  writeFileSync11(filePath, JSON.stringify(updated, null, 2));
74698
74723
  try {
74699
- const councilDir = join65(workingDir, ".swarm", "council");
74724
+ const councilDir = join66(workingDir, ".swarm", "council");
74700
74725
  mkdirSync18(councilDir, { recursive: true });
74701
74726
  const auditLine = JSON.stringify({
74702
74727
  round: synthesis.roundNumber,
@@ -74704,7 +74729,7 @@ function writeCouncilEvidence(workingDir, synthesis) {
74704
74729
  timestamp: synthesis.timestamp,
74705
74730
  vetoedBy: synthesis.vetoedBy
74706
74731
  });
74707
- appendFileSync6(join65(councilDir, `${synthesis.taskId}.rounds.jsonl`), `${auditLine}
74732
+ appendFileSync6(join66(councilDir, `${synthesis.taskId}.rounds.jsonl`), `${auditLine}
74708
74733
  `);
74709
74734
  } catch (auditError) {
74710
74735
  console.warn(`writeCouncilEvidence: failed to append round-history audit log: ${auditError instanceof Error ? auditError.message : String(auditError)}`);
@@ -74837,20 +74862,20 @@ function buildUnifiedFeedback(taskId, verdict, vetoedBy, requiredFixes, advisory
74837
74862
 
74838
74863
  // src/council/criteria-store.ts
74839
74864
  import { existsSync as existsSync38, mkdirSync as mkdirSync19, readFileSync as readFileSync37, writeFileSync as writeFileSync12 } from "node:fs";
74840
- import { join as join66 } from "node:path";
74865
+ import { join as join67 } from "node:path";
74841
74866
  var COUNCIL_DIR = ".swarm/council";
74842
74867
  function writeCriteria(workingDir, taskId, criteria) {
74843
- const dir = join66(workingDir, COUNCIL_DIR);
74868
+ const dir = join67(workingDir, COUNCIL_DIR);
74844
74869
  mkdirSync19(dir, { recursive: true });
74845
74870
  const payload = {
74846
74871
  taskId,
74847
74872
  criteria,
74848
74873
  declaredAt: new Date().toISOString()
74849
74874
  };
74850
- writeFileSync12(join66(dir, `${safeId(taskId)}.json`), JSON.stringify(payload, null, 2));
74875
+ writeFileSync12(join67(dir, `${safeId(taskId)}.json`), JSON.stringify(payload, null, 2));
74851
74876
  }
74852
74877
  function readCriteria(workingDir, taskId) {
74853
- const filePath = join66(workingDir, COUNCIL_DIR, `${safeId(taskId)}.json`);
74878
+ const filePath = join67(workingDir, COUNCIL_DIR, `${safeId(taskId)}.json`);
74854
74879
  if (!existsSync38(filePath))
74855
74880
  return null;
74856
74881
  try {
@@ -77395,6 +77420,7 @@ var VALID_CATEGORIES2 = [
77395
77420
  "debugging",
77396
77421
  "performance",
77397
77422
  "integration",
77423
+ "todo",
77398
77424
  "other"
77399
77425
  ];
77400
77426
  var knowledge_add = createSwarmTool({
@@ -77539,6 +77565,7 @@ var VALID_CATEGORIES3 = [
77539
77565
  "debugging",
77540
77566
  "performance",
77541
77567
  "integration",
77568
+ "todo",
77542
77569
  "other"
77543
77570
  ];
77544
77571
  var VALID_STATUSES = ["candidate", "established", "promoted"];
@@ -88776,6 +88803,34 @@ async function initializeOpenCodeSwarm(ctx) {
88776
88803
  }
88777
88804
  }
88778
88805
  }
88806
+ {
88807
+ const noFallback = [];
88808
+ const hasNoFallback = (cfg) => cfg.model && (!cfg.fallback_models || cfg.fallback_models.length === 0);
88809
+ if (config3.agents) {
88810
+ for (const [name2, cfg] of Object.entries(config3.agents)) {
88811
+ if (hasNoFallback(cfg))
88812
+ noFallback.push(`${name2}(${cfg.model})`);
88813
+ }
88814
+ }
88815
+ if (config3.swarms) {
88816
+ for (const [swarmId, swarm] of Object.entries(config3.swarms)) {
88817
+ if (swarm.agents) {
88818
+ for (const [name2, cfg] of Object.entries(swarm.agents)) {
88819
+ if (hasNoFallback(cfg))
88820
+ noFallback.push(`${swarmId}/${name2}(${cfg.model})`);
88821
+ }
88822
+ }
88823
+ }
88824
+ }
88825
+ if (noFallback.length > 0) {
88826
+ const msg = `[opencode-swarm] WARNING: ${noFallback.length} agent(s) use a custom model without fallback_models: ` + noFallback.join(", ") + '. Add "fallback_models": ["model-a"] to each agent config for reliability.';
88827
+ if (!config3.quiet) {
88828
+ console.warn(msg);
88829
+ } else {
88830
+ addDeferredWarning(msg);
88831
+ }
88832
+ }
88833
+ }
88779
88834
  swarmState.fullAutoEnabledInConfig = config3.full_auto?.enabled === true;
88780
88835
  swarmState.opencodeClient = ctx.client;
88781
88836
  await loadSnapshot(ctx.directory);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-swarm",
3
- "version": "6.86.10",
3
+ "version": "6.86.12",
4
4
  "description": "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",