opencode-swarm 7.64.0 → 7.65.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.
package/dist/cli/index.js CHANGED
@@ -52,7 +52,7 @@ var package_default;
52
52
  var init_package = __esm(() => {
53
53
  package_default = {
54
54
  name: "opencode-swarm",
55
- version: "7.64.0",
55
+ version: "7.65.1",
56
56
  description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
57
57
  main: "dist/index.js",
58
58
  types: "dist/index.d.ts",
@@ -18518,7 +18518,8 @@ var init_schema = __esm(() => {
18518
18518
  max_agent_summary_words: exports_external.number().int().min(20).max(500).default(100),
18519
18519
  max_phase_summary_words: exports_external.number().int().min(50).max(1000).default(250),
18520
18520
  allow_concerns_to_complete: exports_external.boolean().default(true),
18521
- persist_knowledge_recommendations: exports_external.boolean().default(false)
18521
+ persist_knowledge_recommendations: exports_external.boolean().default(false),
18522
+ provenance_verify: exports_external.boolean().default(false)
18522
18523
  });
18523
18524
  KnowledgeApplicationConfigSchema = exports_external.object({
18524
18525
  enabled: exports_external.boolean().default(true),
@@ -37126,13 +37127,12 @@ async function enforceKnowledgeCap(filePath, maxEntries) {
37126
37127
  return entries.slice(entries.length - maxEntries);
37127
37128
  });
37128
37129
  }
37129
- async function appendRejectedLesson(directory, lesson) {
37130
+ async function appendRejectedLesson(directory, lesson, maxEntries = 20) {
37130
37131
  const filePath = resolveSwarmRejectedPath(directory);
37131
- const MAX = 20;
37132
37132
  await transactKnowledge(filePath, (existing) => {
37133
37133
  const updated = [...existing, lesson];
37134
- if (updated.length > MAX) {
37135
- return updated.slice(updated.length - MAX);
37134
+ if (updated.length > maxEntries) {
37135
+ return updated.slice(updated.length - maxEntries);
37136
37136
  }
37137
37137
  return updated;
37138
37138
  });
@@ -40697,18 +40697,20 @@ async function curateAndStoreSwarm(lessons, projectName, phaseInfo, directory, c
40697
40697
  scope: "global",
40698
40698
  confidence: computeConfidence(0, true)
40699
40699
  };
40700
- const result = validateLesson(lesson, snapshotPlusNew.map((e) => e.lesson), meta3);
40701
- if (result.valid === false || result.severity === "error") {
40702
- const rejectedLesson = {
40703
- id: crypto.randomUUID(),
40704
- lesson,
40705
- rejection_reason: result.reason ?? "unknown",
40706
- rejected_at: new Date().toISOString(),
40707
- rejection_layer: result.layer ?? 1
40708
- };
40709
- await appendRejectedLesson(directory, rejectedLesson);
40710
- rejected++;
40711
- continue;
40700
+ if (config3.validation_enabled !== false) {
40701
+ const result = validateLesson(lesson, snapshotPlusNew.map((e) => e.lesson), meta3);
40702
+ if (result.valid === false || result.severity === "error") {
40703
+ const rejectedLesson = {
40704
+ id: crypto.randomUUID(),
40705
+ lesson,
40706
+ rejection_reason: result.reason ?? "unknown",
40707
+ rejected_at: new Date().toISOString(),
40708
+ rejection_layer: result.layer ?? 1
40709
+ };
40710
+ await appendRejectedLesson(directory, rejectedLesson, config3.rejected_max_entries);
40711
+ rejected++;
40712
+ continue;
40713
+ }
40712
40714
  }
40713
40715
  const duplicate = findNearDuplicate(lesson, snapshotPlusNew, config3.dedup_threshold);
40714
40716
  if (duplicate) {
@@ -607,6 +607,7 @@ export declare const ArchitecturalSupervisionConfigSchema: z.ZodObject<{
607
607
  max_phase_summary_words: z.ZodDefault<z.ZodNumber>;
608
608
  allow_concerns_to_complete: z.ZodDefault<z.ZodBoolean>;
609
609
  persist_knowledge_recommendations: z.ZodDefault<z.ZodBoolean>;
610
+ provenance_verify: z.ZodDefault<z.ZodBoolean>;
610
611
  }, z.core.$strip>;
611
612
  export type ArchitecturalSupervisionConfig = z.infer<typeof ArchitecturalSupervisionConfigSchema>;
612
613
  export declare const KnowledgeApplicationConfigSchema: z.ZodObject<{
@@ -1528,6 +1529,7 @@ export declare const PluginConfigSchema: z.ZodObject<{
1528
1529
  max_phase_summary_words: z.ZodDefault<z.ZodNumber>;
1529
1530
  allow_concerns_to_complete: z.ZodDefault<z.ZodBoolean>;
1530
1531
  persist_knowledge_recommendations: z.ZodDefault<z.ZodBoolean>;
1532
+ provenance_verify: z.ZodDefault<z.ZodBoolean>;
1531
1533
  }, z.core.$strip>>;
1532
1534
  knowledge_application: z.ZodOptional<z.ZodObject<{
1533
1535
  enabled: z.ZodDefault<z.ZodBoolean>;
@@ -24,6 +24,7 @@ export declare function appendKnowledge<T>(filePath: string, entry: T): Promise<
24
24
  export declare function rewriteKnowledge<T>(filePath: string, entries: T[]): Promise<void>;
25
25
  export declare function transactFile<T>(filePath: string, read: (filePath: string) => Promise<T>, write: (filePath: string, data: T) => Promise<void>, mutate: (data: T) => T | null): Promise<boolean>;
26
26
  export declare function transactKnowledge<T>(filePath: string, mutate: (entries: T[]) => T[] | null): Promise<boolean>;
27
+ export declare function appendKnowledgeWithCapEnforcement<T>(filePath: string, entry: T, maxEntries: number): Promise<boolean>;
27
28
  export declare function enforceKnowledgeCap<T>(filePath: string, maxEntries: number): Promise<void>;
28
29
  export interface SweepResult {
29
30
  scanned: number;
@@ -34,7 +35,7 @@ export interface SweepResult {
34
35
  }
35
36
  export declare function sweepAgedEntries<T extends KnowledgeEntryBase>(filePath: string, defaultMaxPhases: number): Promise<SweepResult>;
36
37
  export declare function sweepStaleTodos<T extends KnowledgeEntryBase>(filePath: string, todoMaxPhases: number): Promise<SweepResult>;
37
- export declare function appendRejectedLesson(directory: string, lesson: RejectedLesson): Promise<void>;
38
+ export declare function appendRejectedLesson(directory: string, lesson: RejectedLesson, maxEntries?: number): Promise<void>;
38
39
  export declare function normalize(text: string): string;
39
40
  export declare function wordBigrams(text: string): Set<string>;
40
41
  export declare function jaccardBigram(a: Set<string>, b: Set<string>): number;
package/dist/index.js CHANGED
@@ -69,7 +69,7 @@ var package_default;
69
69
  var init_package = __esm(() => {
70
70
  package_default = {
71
71
  name: "opencode-swarm",
72
- version: "7.64.0",
72
+ version: "7.65.1",
73
73
  description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
74
74
  main: "dist/index.js",
75
75
  types: "dist/index.d.ts",
@@ -15849,7 +15849,8 @@ var init_schema = __esm(() => {
15849
15849
  max_agent_summary_words: exports_external.number().int().min(20).max(500).default(100),
15850
15850
  max_phase_summary_words: exports_external.number().int().min(50).max(1000).default(250),
15851
15851
  allow_concerns_to_complete: exports_external.boolean().default(true),
15852
- persist_knowledge_recommendations: exports_external.boolean().default(false)
15852
+ persist_knowledge_recommendations: exports_external.boolean().default(false),
15853
+ provenance_verify: exports_external.boolean().default(false)
15853
15854
  });
15854
15855
  KnowledgeApplicationConfigSchema = exports_external.object({
15855
15856
  enabled: exports_external.boolean().default(true),
@@ -58633,6 +58634,15 @@ async function transactKnowledge(filePath, mutate) {
58633
58634
  await atomicWriteFile(fp, content);
58634
58635
  }, mutate);
58635
58636
  }
58637
+ async function appendKnowledgeWithCapEnforcement(filePath, entry, maxEntries) {
58638
+ return transactKnowledge(filePath, (entries) => {
58639
+ const updated = [...entries, entry];
58640
+ if (updated.length > maxEntries) {
58641
+ return updated.slice(updated.length - maxEntries);
58642
+ }
58643
+ return updated;
58644
+ });
58645
+ }
58636
58646
  async function enforceKnowledgeCap(filePath, maxEntries) {
58637
58647
  await transactKnowledge(filePath, (entries) => {
58638
58648
  if (entries.length <= maxEntries)
@@ -58701,13 +58711,12 @@ async function sweepStaleTodos(filePath, todoMaxPhases) {
58701
58711
  });
58702
58712
  return result;
58703
58713
  }
58704
- async function appendRejectedLesson(directory, lesson) {
58714
+ async function appendRejectedLesson(directory, lesson, maxEntries = 20) {
58705
58715
  const filePath = resolveSwarmRejectedPath(directory);
58706
- const MAX = 20;
58707
58716
  await transactKnowledge(filePath, (existing) => {
58708
58717
  const updated = [...existing, lesson];
58709
- if (updated.length > MAX) {
58710
- return updated.slice(updated.length - MAX);
58718
+ if (updated.length > maxEntries) {
58719
+ return updated.slice(updated.length - maxEntries);
58711
58720
  }
58712
58721
  return updated;
58713
58722
  });
@@ -63808,18 +63817,20 @@ async function curateAndStoreSwarm(lessons, projectName, phaseInfo, directory, c
63808
63817
  scope: "global",
63809
63818
  confidence: computeConfidence(0, true)
63810
63819
  };
63811
- const result = validateLesson(lesson, snapshotPlusNew.map((e) => e.lesson), meta3);
63812
- if (result.valid === false || result.severity === "error") {
63813
- const rejectedLesson = {
63814
- id: crypto.randomUUID(),
63815
- lesson,
63816
- rejection_reason: result.reason ?? "unknown",
63817
- rejected_at: new Date().toISOString(),
63818
- rejection_layer: result.layer ?? 1
63819
- };
63820
- await appendRejectedLesson(directory, rejectedLesson);
63821
- rejected++;
63822
- continue;
63820
+ if (config3.validation_enabled !== false) {
63821
+ const result = validateLesson(lesson, snapshotPlusNew.map((e) => e.lesson), meta3);
63822
+ if (result.valid === false || result.severity === "error") {
63823
+ const rejectedLesson = {
63824
+ id: crypto.randomUUID(),
63825
+ lesson,
63826
+ rejection_reason: result.reason ?? "unknown",
63827
+ rejected_at: new Date().toISOString(),
63828
+ rejection_layer: result.layer ?? 1
63829
+ };
63830
+ await appendRejectedLesson(directory, rejectedLesson, config3.rejected_max_entries);
63831
+ rejected++;
63832
+ continue;
63833
+ }
63823
63834
  }
63824
63835
  const duplicate = findNearDuplicate(lesson, snapshotPlusNew, config3.dedup_threshold);
63825
63836
  if (duplicate) {
@@ -91650,8 +91661,17 @@ function createAgents(config3, projectContext = emptyProjectContext()) {
91650
91661
  const swarms = config3?.swarms;
91651
91662
  if (swarms && Object.keys(swarms).length > 0) {
91652
91663
  for (const swarmId of Object.keys(swarms)) {
91653
- const swarmConfig = swarms[swarmId];
91664
+ let swarmConfig = swarms[swarmId];
91654
91665
  const isDefault = swarmId === "default";
91666
+ if (config3?.agents) {
91667
+ swarmConfig = {
91668
+ ...swarmConfig,
91669
+ agents: {
91670
+ ...config3.agents,
91671
+ ...swarmConfig.agents ?? {}
91672
+ }
91673
+ };
91674
+ }
91655
91675
  const swarmAgents = createSwarmAgents(swarmId, swarmConfig, isDefault, config3, projectContext);
91656
91676
  allAgents.push(...swarmAgents);
91657
91677
  }
@@ -92672,7 +92692,7 @@ function normalizeAgentWorkSummary(input, maxSummaryWords = MAX_AGENT_SUMMARY_WO
92672
92692
  };
92673
92693
  return AgentWorkSummarySchema.parse(candidate);
92674
92694
  }
92675
- var SUMMARY_SCHEMA_VERSION = "1.0.0", MAX_AGENT_SUMMARY_WORDS = 100, MAX_PHASE_SUMMARY_WORDS = 250, MAX_LIST_ITEMS = 5, SupervisorVerdictSchema, AgentWorkSummarySchema, PhaseArchitectureSummarySchema, SupervisorFindingSchema, KnowledgeRecommendationSchema, ArchitectureSupervisorReportSchema;
92695
+ var SUMMARY_SCHEMA_VERSION = "1.0.0", MAX_AGENT_SUMMARY_WORDS = 100, MAX_PHASE_SUMMARY_WORDS = 250, MAX_LIST_ITEMS = 5, SupervisorVerdictSchema, AgentWorkSummarySchema, PhaseArchitectureSummarySchema, SupervisorFindingSchema, KnowledgeRecommendationSchema, EvidenceProvenanceSchema, ArchitectureSupervisorReportSchema;
92676
92696
  var init_schema3 = __esm(() => {
92677
92697
  init_zod();
92678
92698
  SupervisorVerdictSchema = exports_external.enum([
@@ -92726,13 +92746,19 @@ var init_schema3 = __esm(() => {
92726
92746
  confidence: exports_external.number().min(0).max(1).default(0.5),
92727
92747
  evidence_refs: exports_external.array(exports_external.string().min(1)).default([])
92728
92748
  });
92749
+ EvidenceProvenanceSchema = exports_external.object({
92750
+ agent_name: exports_external.string().min(1).optional(),
92751
+ session_id: exports_external.string().min(1).optional(),
92752
+ captured_at: exports_external.string().datetime().optional()
92753
+ });
92729
92754
  ArchitectureSupervisorReportSchema = exports_external.object({
92730
92755
  schema_version: exports_external.literal(SUMMARY_SCHEMA_VERSION),
92731
92756
  phase: exports_external.number().int().min(0).max(999),
92732
92757
  verdict: SupervisorVerdictSchema,
92733
92758
  findings: exports_external.array(SupervisorFindingSchema).default([]),
92734
92759
  knowledge_recommendations: exports_external.array(KnowledgeRecommendationSchema).default([]),
92735
- created_at: exports_external.string().datetime()
92760
+ created_at: exports_external.string().datetime(),
92761
+ provenance: EvidenceProvenanceSchema.optional()
92736
92762
  });
92737
92763
  });
92738
92764
 
@@ -92824,7 +92850,8 @@ function writeSupervisorReport(directory, report) {
92824
92850
  timestamp: report.created_at,
92825
92851
  verdict: report.verdict,
92826
92852
  findings: report.findings,
92827
- knowledge_recommendations: report.knowledge_recommendations
92853
+ knowledge_recommendations: report.knowledge_recommendations,
92854
+ ...report.provenance ? { provenance: report.provenance } : {}
92828
92855
  }
92829
92856
  ]
92830
92857
  };
@@ -96961,9 +96988,14 @@ async function readMergedKnowledge(directory, config3, context, opts) {
96961
96988
  } else if (entry.tags.length === 0) {
96962
96989
  keywordsScore = 0.5;
96963
96990
  }
96964
- const tierBoost = entry.tier === "hive" ? HIVE_TIER_BOOST : 0;
96965
- const isSameProjectSource = context?.projectName && entry.tier === "hive" && "source_project" in entry && entry.source_project === context.projectName;
96966
- const sameProjectPenalty = isSameProjectSource ? SAME_PROJECT_PENALTY : 0;
96991
+ let tierBoost = 0;
96992
+ let isSameProject = false;
96993
+ if (entry.tier === "hive" && "source_project" in entry) {
96994
+ const sourceProject = entry.source_project;
96995
+ isSameProject = !!(context?.projectName && sourceProject === context.projectName);
96996
+ tierBoost = HIVE_TIER_BOOST;
96997
+ }
96998
+ const sameProjectPenalty = isSameProject ? DEFAULT_SAME_PROJECT_PENALTY : 0;
96967
96999
  const finalScore = categoryScore * 0.4 + confidenceScore * 0.35 + keywordsScore * 0.25 + tierBoost + sameProjectPenalty;
96968
97000
  const relevanceScore = {
96969
97001
  category: categoryScore,
@@ -97112,7 +97144,7 @@ function scoreDirectiveAgainstContext(entry, ctx) {
97112
97144
  score += 0.1;
97113
97145
  return { triggerHit, actionHit, agentHit, score: Math.min(1, score) };
97114
97146
  }
97115
- var JACCARD_THRESHOLD2 = 0.6, HIVE_TIER_BOOST = 0.05, SAME_PROJECT_PENALTY = -0.05, QUARANTINED_STATUS = "quarantined";
97147
+ var JACCARD_THRESHOLD2 = 0.6, HIVE_TIER_BOOST = 0.05, DEFAULT_SAME_PROJECT_PENALTY = -0.05, QUARANTINED_STATUS = "quarantined";
97116
97148
  var init_knowledge_reader = __esm(() => {
97117
97149
  init_task_file();
97118
97150
  init_logger();
@@ -120704,8 +120736,12 @@ var knowledge_add = createSwarmTool({
120704
120736
  hive_eligible: false,
120705
120737
  ...actionable
120706
120738
  };
120739
+ let config3;
120740
+ let dedupThreshold = 0.6;
120707
120741
  try {
120708
- const { config: config3 } = loadPluginConfigWithMeta(directory);
120742
+ const loaded = loadPluginConfigWithMeta(directory);
120743
+ config3 = loaded.config;
120744
+ dedupThreshold = config3.knowledge?.dedup_threshold ?? 0.6;
120709
120745
  if (config3.knowledge?.validation_enabled !== false) {
120710
120746
  const validation2 = validateLesson(lesson, [], {
120711
120747
  category,
@@ -120722,7 +120758,7 @@ var knowledge_add = createSwarmTool({
120722
120758
  } catch {}
120723
120759
  try {
120724
120760
  const existingEntries = await readKnowledge(resolveSwarmKnowledgePath(directory));
120725
- const duplicate = findNearDuplicate(lesson, existingEntries, 0.6);
120761
+ const duplicate = findNearDuplicate(lesson, existingEntries, dedupThreshold);
120726
120762
  if (duplicate) {
120727
120763
  return JSON.stringify({
120728
120764
  success: false,
@@ -120747,7 +120783,8 @@ var knowledge_add = createSwarmTool({
120747
120783
  });
120748
120784
  }
120749
120785
  try {
120750
- await appendKnowledge(resolveSwarmKnowledgePath(directory), entry);
120786
+ const maxEntries = config3?.knowledge?.swarm_max_entries ?? 100;
120787
+ await appendKnowledgeWithCapEnforcement(resolveSwarmKnowledgePath(directory), entry, maxEntries);
120751
120788
  } catch (err2) {
120752
120789
  const message = err2 instanceof Error ? err2.message : "Unknown error";
120753
120790
  return JSON.stringify({
@@ -121283,7 +121320,7 @@ init_zod();
121283
121320
  init_knowledge_store();
121284
121321
  init_create_tool();
121285
121322
  var knowledge_remove = createSwarmTool({
121286
- description: "Delete an outdated swarm knowledge entry by ID (swarm tier only — does not affect hive). Double-deletion is idempotent — removing a non-existent entry returns a clear message without error.",
121323
+ description: "Delete an outdated swarm knowledge entry by ID (swarm tier only — does not affect hive). Promoted entries cannot be deleted. Double-deletion is idempotent — removing a non-existent entry returns a clear message without error.",
121287
121324
  args: {
121288
121325
  id: exports_external.string().min(1).describe("UUID of the knowledge entry to remove")
121289
121326
  },
@@ -121305,8 +121342,16 @@ var knowledge_remove = createSwarmTool({
121305
121342
  const swarmPath = resolveSwarmKnowledgePath(directory);
121306
121343
  let found = false;
121307
121344
  let remaining = 0;
121345
+ let isPromoted = false;
121308
121346
  try {
121309
121347
  await transactKnowledge(swarmPath, (entries) => {
121348
+ const entryToDelete = entries.find((entry) => entry.id === id);
121349
+ if (!entryToDelete)
121350
+ return null;
121351
+ if (entryToDelete.status === "promoted") {
121352
+ isPromoted = true;
121353
+ return null;
121354
+ }
121310
121355
  const filtered = entries.filter((entry) => entry.id !== id);
121311
121356
  if (filtered.length === entries.length)
121312
121357
  return null;
@@ -121321,6 +121366,12 @@ var knowledge_remove = createSwarmTool({
121321
121366
  error: message
121322
121367
  });
121323
121368
  }
121369
+ if (isPromoted) {
121370
+ return JSON.stringify({
121371
+ success: false,
121372
+ message: "cannot delete promoted entry — this entry has been promoted to cross-project consensus"
121373
+ });
121374
+ }
121324
121375
  if (!found) {
121325
121376
  return JSON.stringify({
121326
121377
  success: false,
@@ -124641,6 +124692,17 @@ Findings: ${details.join("; ")}` : "";
124641
124692
  if (typeof asEntry.phase_number !== "number" || asEntry.phase_number !== phase) {
124642
124693
  return asBlocked("ARCH_SUPERVISOR_PHASE_MISMATCH", `Phase ${phase} cannot be completed: architecture supervisor evidence is for phase ${String(asEntry.phase_number)}, not phase ${phase}.`);
124643
124694
  }
124695
+ const gateWarnings = [];
124696
+ if (asConfig?.provenance_verify === true) {
124697
+ const provenance = asEntry.provenance;
124698
+ if (!provenance || !provenance.agent_name && !provenance.session_id) {
124699
+ return asBlocked("ARCH_SUPERVISOR_MISSING_PROVENANCE", `Phase ${phase} cannot be completed: architecture supervisor evidence lacks provenance (agent_name or session_id). Evidence provenance verification is enabled.`);
124700
+ }
124701
+ } else if (!asEntry.provenance || !asEntry.provenance.agent_name && !asEntry.provenance.session_id) {
124702
+ const msg = `Architecture supervisor evidence lacks provenance for phase ${phase}. Enable 'provenance_verify' in architectural_supervision config to enforce provenance verification.`;
124703
+ gateWarnings.push(msg);
124704
+ safeWarn(`[phase_complete] ${msg}`, undefined);
124705
+ }
124644
124706
  const asVerdict = asEntry.verdict;
124645
124707
  if (asVerdict === "REJECT") {
124646
124708
  return asBlocked("ARCH_SUPERVISOR_REJECTED", `Phase ${phase} cannot be completed: architecture supervisor returned verdict 'REJECT'. Address the system-level findings before completing the phase.${summarizeFindings(asEntry.findings)}`);
@@ -124653,7 +124715,12 @@ Findings: ${details.join("; ")}` : "";
124653
124715
  } else if (asVerdict !== "APPROVE") {
124654
124716
  return asBlocked("ARCH_SUPERVISOR_INVALID", `Phase ${phase} cannot be completed: architecture supervisor evidence contains unrecognized verdict '${String(asVerdict)}'. Expected one of: APPROVE, CONCERNS, REJECT.`);
124655
124717
  }
124656
- return { blocked: false, agentsDispatched, agentsMissing: [], warnings: [] };
124718
+ return {
124719
+ blocked: false,
124720
+ agentsDispatched,
124721
+ agentsMissing: [],
124722
+ warnings: gateWarnings
124723
+ };
124657
124724
  }
124658
124725
  // src/tools/phase-complete/gates/completion-verify-gate.ts
124659
124726
  async function runCompletionVerifyGate(ctx) {
@@ -124698,6 +124765,7 @@ import * as fs106 from "node:fs";
124698
124765
  import * as path153 from "node:path";
124699
124766
  async function runDriftGate(ctx) {
124700
124767
  const { phase, dir, sessionID, agentsDispatched, safeWarn } = ctx;
124768
+ const gateWarnings = [];
124701
124769
  let driftCheckEnabled = true;
124702
124770
  let driftHasEffectiveSpec = false;
124703
124771
  try {
@@ -124757,6 +124825,11 @@ async function runDriftGate(ctx) {
124757
124825
  for (const entry of entries) {
124758
124826
  if (typeof entry.type === "string" && entry.type.includes("drift") && typeof entry.verdict === "string") {
124759
124827
  driftVerdictFound = true;
124828
+ if (!entry.provenance || !entry.provenance.agent_name && !entry.provenance.session_id) {
124829
+ const msg = `Drift verification evidence lacks provenance for phase ${phase}. Evidence should include agent_name or session_id for verification.`;
124830
+ gateWarnings.push(msg);
124831
+ safeWarn(`[phase_complete] ${msg}`, undefined);
124832
+ }
124760
124833
  if (entry.verdict === "approved") {
124761
124834
  driftVerdictApproved = true;
124762
124835
  }
@@ -124847,7 +124920,7 @@ async function runDriftGate(ctx) {
124847
124920
  blocked: false,
124848
124921
  agentsDispatched,
124849
124922
  agentsMissing: [],
124850
- warnings: []
124923
+ warnings: gateWarnings
124851
124924
  };
124852
124925
  } catch (driftError) {
124853
124926
  return {
@@ -125179,6 +125252,7 @@ import * as fs110 from "node:fs";
125179
125252
  import * as path157 from "node:path";
125180
125253
  async function runPhaseCouncilGate(ctx) {
125181
125254
  const { phase, dir, sessionID, pluginConfig, agentsDispatched, safeWarn } = ctx;
125255
+ const gateWarnings = [];
125182
125256
  let councilModeEnabled = false;
125183
125257
  try {
125184
125258
  const plan = await loadPlan(dir);
@@ -125240,6 +125314,11 @@ async function runPhaseCouncilGate(ctx) {
125240
125314
  warnings: []
125241
125315
  };
125242
125316
  }
125317
+ if (!entry.provenance || !entry.provenance.agent_name && !entry.provenance.session_id) {
125318
+ const msg = `Phase council evidence lacks provenance for phase ${phase}. Evidence should include agent_name or session_id for verification.`;
125319
+ gateWarnings.push(msg);
125320
+ safeWarn(`[phase_complete] ${msg}`, undefined);
125321
+ }
125243
125322
  if (entry.verdict === "REJECT" || entry.verdict === "reject") {
125244
125323
  const requiredFixes = entry.requiredFixes ?? entry.required_fixes ?? [];
125245
125324
  const fixesDetail = Array.isArray(requiredFixes) && requiredFixes.length > 0 ? `
@@ -125358,7 +125437,12 @@ Advisory notes: ${advisoryNotes.join("; ")}` : "";
125358
125437
  safeWarn(`[phase_complete] Phase council gate error (non-blocking):`, pcError);
125359
125438
  }
125360
125439
  }
125361
- return { blocked: false, agentsDispatched, agentsMissing: [], warnings: [] };
125440
+ return {
125441
+ blocked: false,
125442
+ agentsDispatched,
125443
+ agentsMissing: [],
125444
+ warnings: gateWarnings
125445
+ };
125362
125446
  }
125363
125447
  // src/tools/phase-complete.ts
125364
125448
  init_resolve_working_directory();
@@ -133737,7 +133821,9 @@ var ArgsSchema4 = exports_external.object({
133737
133821
  phaseSummary: exports_external.string().min(1),
133738
133822
  roundNumber: exports_external.number().int().min(1).max(10).optional(),
133739
133823
  verdicts: exports_external.array(VerdictSchema2).min(1).max(5),
133740
- working_directory: exports_external.string().optional()
133824
+ working_directory: exports_external.string().optional(),
133825
+ provenanceAgentName: exports_external.string().min(1).optional(),
133826
+ provenanceSessionId: exports_external.string().min(1).optional()
133741
133827
  });
133742
133828
  var submit_phase_council_verdicts = createSwarmTool({
133743
133829
  description: "Submit pre-collected council member verdicts for PHASE-LEVEL synthesis. " + "PREREQUISITE — you MUST dispatch each council member (critic, reviewer, sme, " + "test_engineer, explorer) as separate Agent tasks with PHASE-SCOPED context and " + "collect their verdict responses BEFORE calling this tool. This tool performs " + "synthesis only — it does NOT dispatch, invoke, or contact council members. " + "Writes .swarm/evidence/{phase}/phase-council.json which is required by " + "phase_complete Gate 5 when phase_council is enabled. " + "Architect-only. Config-gated via council.enabled.",
@@ -133768,7 +133854,9 @@ var submit_phase_council_verdicts = createSwarmTool({
133768
133854
  criteriaUnmet: exports_external.array(exports_external.string()),
133769
133855
  durationMs: exports_external.number()
133770
133856
  })).min(1).max(5).describe("Collected CouncilMemberVerdict objects from all dispatched council members"),
133771
- working_directory: exports_external.string().optional().describe("Working directory where the plan is located")
133857
+ working_directory: exports_external.string().optional().describe("Working directory where the plan is located"),
133858
+ provenanceAgentName: exports_external.string().min(1).optional().describe("Agent name that produced this evidence (optional provenance)"),
133859
+ provenanceSessionId: exports_external.string().min(1).optional().describe("Session ID of the agent that produced this evidence (optional provenance)")
133772
133860
  },
133773
133861
  async execute(args2, directory) {
133774
133862
  const parsed = ArgsSchema4.safeParse(args2);
@@ -133848,7 +133936,12 @@ var submit_phase_council_verdicts = createSwarmTool({
133848
133936
  message: `Phase council returned CONCERNS with ${synthesis.blockingConcernsCount} HIGH/CRITICAL finding(s) promoted to requiredFixes. These must be resolved before the phase can complete. Do NOT write evidence or proceed — address every requiredFix and resubmit.`
133849
133937
  }, null, 2);
133850
133938
  }
133851
- writePhaseCouncilEvidence(workingDir, synthesis);
133939
+ const provenance = input.provenanceAgentName || input.provenanceSessionId ? {
133940
+ agent_name: input.provenanceAgentName,
133941
+ session_id: input.provenanceSessionId,
133942
+ captured_at: new Date().toISOString()
133943
+ } : undefined;
133944
+ writePhaseCouncilEvidence(workingDir, synthesis, provenance);
133852
133945
  return JSON.stringify({
133853
133946
  success: true,
133854
133947
  overallVerdict: synthesis.overallVerdict,
@@ -133931,7 +134024,7 @@ function getPhaseMutationGapFinding(phaseNumber, workingDir) {
133931
134024
  };
133932
134025
  }
133933
134026
  }
133934
- function writePhaseCouncilEvidence(workingDir, synthesis) {
134027
+ function writePhaseCouncilEvidence(workingDir, synthesis, provenance) {
133935
134028
  const evidenceDir = path173.join(workingDir, ".swarm", "evidence", String(synthesis.phaseNumber));
133936
134029
  mkdirSync42(evidenceDir, { recursive: true });
133937
134030
  const evidenceFile = path173.join(evidenceDir, "phase-council.json");
@@ -133961,7 +134054,8 @@ function writePhaseCouncilEvidence(workingDir, synthesis) {
133961
134054
  evidence: finding.evidence
133962
134055
  })),
133963
134056
  roundNumber: synthesis.roundNumber,
133964
- allCriteriaMet: synthesis.allCriteriaMet
134057
+ allCriteriaMet: synthesis.allCriteriaMet,
134058
+ ...provenance ? { provenance } : {}
133965
134059
  }
133966
134060
  ]
133967
134061
  };
@@ -136791,7 +136885,9 @@ var ArgsSchema7 = exports_external.object({
136791
136885
  verdict: exports_external.enum(["APPROVE", "CONCERNS", "REJECT"]),
136792
136886
  findings: exports_external.array(FindingSchema2).default([]),
136793
136887
  knowledge_recommendations: exports_external.array(KnowledgeRecommendationSchema2).default([]),
136794
- working_directory: exports_external.string().optional()
136888
+ working_directory: exports_external.string().optional(),
136889
+ provenance_agent_name: exports_external.string().min(1).optional(),
136890
+ provenance_session_id: exports_external.string().min(1).optional()
136795
136891
  });
136796
136892
  var write_architecture_supervisor_evidence = createSwarmTool({
136797
136893
  description: "Persist the architecture supervisor verdict for a phase. PREREQUISITE: dispatch " + "critic_architecture_supervisor as an Agent task with the phase + agent summaries, " + "collect its strict-JSON verdict (verdict/findings/knowledge_recommendations), then " + "call this tool with those values. Writes .swarm/evidence/{phase}/" + "architecture-supervisor.json. This tool persists only — it does not contact the " + "supervisor. Architect-only; config-gated via architectural_supervision.enabled.",
@@ -136800,7 +136896,9 @@ var write_architecture_supervisor_evidence = createSwarmTool({
136800
136896
  verdict: exports_external.enum(["APPROVE", "CONCERNS", "REJECT"]).describe("Supervisor verdict."),
136801
136897
  findings: exports_external.array(FindingSchema2).optional().describe("System-level findings from the supervisor."),
136802
136898
  knowledge_recommendations: exports_external.array(KnowledgeRecommendationSchema2).optional().describe("Durable lessons proposed by the supervisor."),
136803
- working_directory: exports_external.string().optional()
136899
+ working_directory: exports_external.string().optional(),
136900
+ provenance_agent_name: exports_external.string().min(1).optional().describe("Agent name that produced this evidence (optional provenance)."),
136901
+ provenance_session_id: exports_external.string().min(1).optional().describe("Session ID of the agent that produced this evidence (optional provenance).")
136804
136902
  },
136805
136903
  execute: async (rawArgs, directory) => {
136806
136904
  const parsed = ArgsSchema7.safeParse(rawArgs);
@@ -136819,13 +136917,19 @@ var write_architecture_supervisor_evidence = createSwarmTool({
136819
136917
  if (!dirResult.success) {
136820
136918
  return JSON.stringify({ success: false, reason: dirResult.message }, null, 2);
136821
136919
  }
136920
+ const provenance = args2.provenance_agent_name || args2.provenance_session_id ? {
136921
+ agent_name: args2.provenance_agent_name,
136922
+ session_id: args2.provenance_session_id,
136923
+ captured_at: new Date().toISOString()
136924
+ } : undefined;
136822
136925
  const report = {
136823
136926
  schema_version: SUMMARY_SCHEMA_VERSION,
136824
136927
  phase: args2.phase,
136825
136928
  verdict: args2.verdict,
136826
136929
  findings: args2.findings,
136827
136930
  knowledge_recommendations: args2.knowledge_recommendations,
136828
- created_at: new Date().toISOString()
136931
+ created_at: new Date().toISOString(),
136932
+ ...provenance ? { provenance } : {}
136829
136933
  };
136830
136934
  const evidencePath = writeSupervisorReport(dirResult.directory, report);
136831
136935
  let knowledgeProposed = 0;
@@ -136918,12 +137022,18 @@ async function executeWriteDriftEvidence(args2, directory) {
136918
137022
  }, null, 2);
136919
137023
  }
136920
137024
  const normalizedVerdict = normalizeVerdict(args2.verdict);
137025
+ const provenance = args2.provenanceAgentName || args2.provenanceSessionId ? {
137026
+ agent_name: args2.provenanceAgentName,
137027
+ session_id: args2.provenanceSessionId,
137028
+ captured_at: new Date().toISOString()
137029
+ } : undefined;
136921
137030
  const evidenceEntry = {
136922
137031
  type: "drift-verification",
136923
137032
  verdict: normalizedVerdict,
136924
137033
  summary: summary.trim(),
136925
137034
  timestamp: new Date().toISOString(),
136926
- requirementCoverage: args2.requirementCoverage
137035
+ requirementCoverage: args2.requirementCoverage,
137036
+ ...provenance ? { provenance } : {}
136927
137037
  };
136928
137038
  const evidenceContent = {
136929
137039
  entries: [evidenceEntry]
@@ -137014,7 +137124,9 @@ var write_drift_evidence = createSwarmTool({
137014
137124
  phase: exports_external.number().int().min(1).describe("The phase number for the drift verification (e.g., 1, 2, 3)"),
137015
137125
  verdict: exports_external.enum(["APPROVED", "NEEDS_REVISION"]).describe("Verdict of the drift verification: 'APPROVED' or 'NEEDS_REVISION'"),
137016
137126
  summary: exports_external.string().describe("Human-readable summary of the drift verification"),
137017
- requirementCoverage: exports_external.string().optional().describe("Requirement coverage report from req_coverage tool (JSON string)")
137127
+ requirementCoverage: exports_external.string().optional().describe("Requirement coverage report from req_coverage tool (JSON string)"),
137128
+ provenanceAgentName: exports_external.string().min(1).optional().describe("Agent name that produced this evidence (optional provenance)"),
137129
+ provenanceSessionId: exports_external.string().min(1).optional().describe("Session ID of the agent that produced this evidence (optional provenance)")
137018
137130
  },
137019
137131
  execute: async (args2, directory) => {
137020
137132
  const rawPhase = args2.phase !== undefined ? Number(args2.phase) : 0;
@@ -137023,7 +137135,9 @@ var write_drift_evidence = createSwarmTool({
137023
137135
  phase: Number(args2.phase),
137024
137136
  verdict: String(args2.verdict),
137025
137137
  summary: String(args2.summary ?? ""),
137026
- requirementCoverage: args2.requirementCoverage !== undefined ? String(args2.requirementCoverage) : undefined
137138
+ requirementCoverage: args2.requirementCoverage !== undefined ? String(args2.requirementCoverage) : undefined,
137139
+ provenanceAgentName: args2.provenanceAgentName !== undefined ? String(args2.provenanceAgentName) : undefined,
137140
+ provenanceSessionId: args2.provenanceSessionId !== undefined ? String(args2.provenanceSessionId) : undefined
137027
137141
  };
137028
137142
  return await executeWriteDriftEvidence(writeDriftEvidenceArgs, directory);
137029
137143
  } catch (error93) {
@@ -137571,9 +137685,22 @@ var TOOL_MANIFEST = defineHandlers({
137571
137685
  });
137572
137686
 
137573
137687
  // src/tools/plugin-registration.ts
137574
- function buildPluginToolObject(agents) {
137688
+ function buildPluginToolObject(agents, config3) {
137575
137689
  const tools = {};
137690
+ const knowledgeEnabled = config3?.knowledge?.enabled !== false;
137691
+ const knowledgeTools = new Set([
137692
+ "knowledge_add",
137693
+ "knowledge_recall",
137694
+ "knowledge_remove",
137695
+ "knowledge_query",
137696
+ "knowledge_ack",
137697
+ "knowledge_receipt",
137698
+ "knowledge_archive"
137699
+ ]);
137576
137700
  for (const [name2, handler] of Object.entries(TOOL_MANIFEST)) {
137701
+ if (!knowledgeEnabled && knowledgeTools.has(name2)) {
137702
+ continue;
137703
+ }
137577
137704
  tools[name2] = handler();
137578
137705
  }
137579
137706
  tools.swarm_command = createSwarmCommandTool(agents);
@@ -137988,7 +138115,7 @@ async function initializeOpenCodeSwarm(ctx) {
137988
138115
  return {
137989
138116
  name: "opencode-swarm",
137990
138117
  agent: agents,
137991
- tool: buildPluginToolObject(agentDefinitionMap),
138118
+ tool: buildPluginToolObject(agentDefinitionMap, config3),
137992
138119
  event: safeHook(backgroundCompletionObserver.event),
137993
138120
  config: async (opencodeConfig) => {
137994
138121
  const isObjectRecord = (value) => typeof value === "object" && value !== null;
@@ -115,6 +115,16 @@ export declare const KnowledgeRecommendationSchema: z.ZodObject<{
115
115
  evidence_refs: z.ZodDefault<z.ZodArray<z.ZodString>>;
116
116
  }, z.core.$strip>;
117
117
  export type KnowledgeRecommendation = z.infer<typeof KnowledgeRecommendationSchema>;
118
+ /**
119
+ * Provenance metadata for evidence: agent identity, session binding, and capture timestamp.
120
+ * Optional for backwards compatibility; when present and in gate mode, gates verify these fields.
121
+ */
122
+ export declare const EvidenceProvenanceSchema: z.ZodObject<{
123
+ agent_name: z.ZodOptional<z.ZodString>;
124
+ session_id: z.ZodOptional<z.ZodString>;
125
+ captured_at: z.ZodOptional<z.ZodString>;
126
+ }, z.core.$strip>;
127
+ export type EvidenceProvenance = z.infer<typeof EvidenceProvenanceSchema>;
118
128
  export declare const ArchitectureSupervisorReportSchema: z.ZodObject<{
119
129
  schema_version: z.ZodLiteral<"1.0.0">;
120
130
  phase: z.ZodNumber;
@@ -144,5 +154,10 @@ export declare const ArchitectureSupervisorReportSchema: z.ZodObject<{
144
154
  evidence_refs: z.ZodDefault<z.ZodArray<z.ZodString>>;
145
155
  }, z.core.$strip>>>;
146
156
  created_at: z.ZodString;
157
+ provenance: z.ZodOptional<z.ZodObject<{
158
+ agent_name: z.ZodOptional<z.ZodString>;
159
+ session_id: z.ZodOptional<z.ZodString>;
160
+ captured_at: z.ZodOptional<z.ZodString>;
161
+ }, z.core.$strip>>;
147
162
  }, z.core.$strip>;
148
163
  export type ArchitectureSupervisorReport = z.infer<typeof ArchitectureSupervisorReportSchema>;
@@ -43,6 +43,11 @@ export interface RawSupervisorEntry {
43
43
  verdict?: string;
44
44
  findings?: unknown[];
45
45
  knowledge_recommendations?: unknown[];
46
+ provenance?: {
47
+ agent_name?: string;
48
+ session_id?: string;
49
+ captured_at?: string;
50
+ };
46
51
  }
47
52
  /**
48
53
  * Read the supervisor report sidecar raw (no zod), returning the supervisor entry or
@@ -10,6 +10,7 @@
10
10
  */
11
11
  import type { ToolDefinition } from '@opencode-ai/plugin/tool';
12
12
  import type { AgentDefinition } from '../agents/index.js';
13
+ import type { PluginConfig } from '../config/index.js';
13
14
  /**
14
15
  * Construct the plugin tool object: one handler per manifest entry, with
15
16
  * `swarm_command` overridden by its dependency-injected instance.
@@ -17,5 +18,7 @@ import type { AgentDefinition } from '../agents/index.js';
17
18
  * The manifest's `swarm_command` handler is the static (no-DI) form used only
18
19
  * for derivation; the real instance needs the agent definition map, which is
19
20
  * only available at plugin-init time.
21
+ *
22
+ * Knowledge tools are conditionally excluded when config.knowledge.enabled = false.
20
23
  */
21
- export declare function buildPluginToolObject(agents: Record<string, AgentDefinition>): Record<string, ToolDefinition>;
24
+ export declare function buildPluginToolObject(agents: Record<string, AgentDefinition>, config?: PluginConfig): Record<string, ToolDefinition>;
@@ -37,5 +37,7 @@ export declare const ArgsSchema: z.ZodObject<{
37
37
  durationMs: z.ZodNumber;
38
38
  }, z.core.$strip>>;
39
39
  working_directory: z.ZodOptional<z.ZodString>;
40
+ provenanceAgentName: z.ZodOptional<z.ZodString>;
41
+ provenanceSessionId: z.ZodOptional<z.ZodString>;
40
42
  }, z.core.$strip>;
41
43
  export declare const submit_phase_council_verdicts: ReturnType<typeof tool>;
@@ -16,6 +16,10 @@ export interface WriteDriftEvidenceArgs {
16
16
  summary: string;
17
17
  /** Requirement coverage report from req_coverage tool */
18
18
  requirementCoverage?: string;
19
+ /** Agent name that produced this evidence (optional provenance) */
20
+ provenanceAgentName?: string;
21
+ /** Session ID of the agent that produced this evidence (optional provenance) */
22
+ provenanceSessionId?: string;
19
23
  }
20
24
  /**
21
25
  * Execute the write_drift_evidence tool.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-swarm",
3
- "version": "7.64.0",
3
+ "version": "7.65.1",
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",