opencode-swarm 7.73.0 → 7.73.2

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.73.0",
55
+ version: "7.73.2",
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",
@@ -18752,7 +18752,8 @@ var init_schema = __esm(() => {
18752
18752
  allowedPrefix: exports_external.array(exports_external.string()).optional(),
18753
18753
  blockedZones: exports_external.array(exports_external.enum(["production", "test", "config", "generated", "docs", "build"])).optional(),
18754
18754
  blockedGlobs: exports_external.array(exports_external.string()).optional(),
18755
- allowedGlobs: exports_external.array(exports_external.string()).optional()
18755
+ allowedGlobs: exports_external.array(exports_external.string()).optional(),
18756
+ allowedCaseSensitiveGlobs: exports_external.array(exports_external.string()).optional()
18756
18757
  });
18757
18758
  AuthorityConfigSchema = exports_external.object({
18758
18759
  enabled: exports_external.boolean().default(true),
@@ -40920,6 +40921,49 @@ var init_synonym_map = __esm(() => {
40920
40921
  MIN_READ_CEILING_BYTES = 64 * 1024;
40921
40922
  });
40922
40923
 
40924
+ // src/hooks/knowledge-reinforcement.ts
40925
+ function isActiveSwarmKnowledgeEntry(entry) {
40926
+ return !INACTIVE_STATUSES.has(entry.status);
40927
+ }
40928
+ function findActiveSwarmNearDuplicate(lesson, entries, threshold) {
40929
+ return findNearDuplicate(lesson, entries.filter(isActiveSwarmKnowledgeEntry), threshold);
40930
+ }
40931
+ function distinctPhaseCount(records) {
40932
+ const phases = new Set;
40933
+ for (const record3 of records ?? []) {
40934
+ if (Number.isInteger(record3.phase_number)) {
40935
+ phases.add(record3.phase_number);
40936
+ }
40937
+ }
40938
+ return phases.size;
40939
+ }
40940
+ function reinforceSwarmKnowledgeEntry(entry, confirmation) {
40941
+ if (!isActiveSwarmKnowledgeEntry(entry)) {
40942
+ return { entryId: entry.id, reinforced: false, reason: "inactive" };
40943
+ }
40944
+ if ((entry.confirmed_by ?? []).some((record3) => record3.phase_number === confirmation.phase_number)) {
40945
+ return {
40946
+ entryId: entry.id,
40947
+ reinforced: false,
40948
+ reason: "already_confirmed_phase"
40949
+ };
40950
+ }
40951
+ entry.confirmed_by = [...entry.confirmed_by ?? [], confirmation];
40952
+ entry.updated_at = confirmation.confirmed_at;
40953
+ entry.phases_alive = 0;
40954
+ entry.confidence = computeConfidence(distinctPhaseCount(entry.confirmed_by), entry.auto_generated ?? false);
40955
+ return { entryId: entry.id, reinforced: true, reason: "reinforced" };
40956
+ }
40957
+ var INACTIVE_STATUSES;
40958
+ var init_knowledge_reinforcement = __esm(() => {
40959
+ init_knowledge_store();
40960
+ INACTIVE_STATUSES = new Set([
40961
+ "archived",
40962
+ "quarantined",
40963
+ "quarantined_unactionable"
40964
+ ]);
40965
+ });
40966
+
40923
40967
  // src/hooks/skill-scoring.ts
40924
40968
  import * as fs10 from "fs";
40925
40969
  import * as path22 from "path";
@@ -42243,6 +42287,7 @@ async function curateAndStoreSwarm(lessons, projectName, phaseInfo, directory, c
42243
42287
  ]);
42244
42288
  const snapshotPlusNew = [...snapshot];
42245
42289
  const toAdd = [];
42290
+ const pendingReinforcementIds = new Set;
42246
42291
  for (const lesson of lessons) {
42247
42292
  const tags = inferTags(lesson);
42248
42293
  let category = "process";
@@ -42272,8 +42317,9 @@ async function curateAndStoreSwarm(lessons, projectName, phaseInfo, directory, c
42272
42317
  continue;
42273
42318
  }
42274
42319
  }
42275
- const duplicate = findNearDuplicate(lesson, snapshotPlusNew, config3.dedup_threshold);
42320
+ const duplicate = findActiveSwarmNearDuplicate(lesson, snapshotPlusNew, config3.dedup_threshold);
42276
42321
  if (duplicate) {
42322
+ pendingReinforcementIds.add(duplicate.id);
42277
42323
  skipped++;
42278
42324
  continue;
42279
42325
  }
@@ -42354,7 +42400,9 @@ async function curateAndStoreSwarm(lessons, projectName, phaseInfo, directory, c
42354
42400
  } catch {}
42355
42401
  continue;
42356
42402
  }
42357
- if (findNearDuplicate(entry.lesson, snapshotPlusNew, config3.dedup_threshold)) {
42403
+ const duplicate = findActiveSwarmNearDuplicate(entry.lesson, snapshotPlusNew, config3.dedup_threshold);
42404
+ if (duplicate) {
42405
+ pendingReinforcementIds.add(duplicate.id);
42358
42406
  skipped++;
42359
42407
  continue;
42360
42408
  }
@@ -42363,15 +42411,48 @@ async function curateAndStoreSwarm(lessons, projectName, phaseInfo, directory, c
42363
42411
  }
42364
42412
  } catch {}
42365
42413
  let stored = 0;
42366
- if (toAdd.length > 0) {
42414
+ let reinforced = 0;
42415
+ if (toAdd.length > 0 || pendingReinforcementIds.size > 0) {
42367
42416
  await transactKnowledge(knowledgePath, (current) => {
42368
- const trulyNew = toAdd.filter((e) => !findNearDuplicate(e.lesson, current, config3.dedup_threshold));
42369
- const extraDups = toAdd.length - trulyNew.length;
42370
- skipped += extraDups;
42371
- if (trulyNew.length === 0)
42372
- return null;
42373
- stored = trulyNew.length;
42374
- return [...current, ...trulyNew];
42417
+ let changed = false;
42418
+ for (const id of pendingReinforcementIds) {
42419
+ const existing = current.find((entry) => entry.id === id);
42420
+ if (!existing)
42421
+ continue;
42422
+ const result = reinforceSwarmKnowledgeEntry(existing, {
42423
+ phase_number: phaseInfo.phase_number,
42424
+ confirmed_at: new Date().toISOString(),
42425
+ project_name: projectName
42426
+ });
42427
+ if (result.reinforced) {
42428
+ reinforced++;
42429
+ changed = true;
42430
+ }
42431
+ }
42432
+ const trulyNew = [];
42433
+ for (const entry of toAdd) {
42434
+ const duplicate = findActiveSwarmNearDuplicate(entry.lesson, current, config3.dedup_threshold);
42435
+ if (duplicate) {
42436
+ skipped++;
42437
+ const result = reinforceSwarmKnowledgeEntry(duplicate, {
42438
+ phase_number: phaseInfo.phase_number,
42439
+ confirmed_at: new Date().toISOString(),
42440
+ project_name: projectName
42441
+ });
42442
+ if (result.reinforced) {
42443
+ reinforced++;
42444
+ changed = true;
42445
+ }
42446
+ continue;
42447
+ }
42448
+ trulyNew.push(entry);
42449
+ }
42450
+ if (trulyNew.length > 0) {
42451
+ current.push(...trulyNew);
42452
+ stored = trulyNew.length;
42453
+ changed = true;
42454
+ }
42455
+ return changed ? current : null;
42375
42456
  });
42376
42457
  }
42377
42458
  await enforceKnowledgeCap(knowledgePath, config3.swarm_max_entries);
@@ -42389,7 +42470,7 @@ async function curateAndStoreSwarm(lessons, projectName, phaseInfo, directory, c
42389
42470
  if (!options?.skipAutoPromotion) {
42390
42471
  await _internals19.runAutoPromotion(directory, config3);
42391
42472
  }
42392
- return { stored, skipped, rejected, quarantined };
42473
+ return { stored, reinforced, skipped, rejected, quarantined };
42393
42474
  }
42394
42475
  async function runAutoPromotion(directory, config3) {
42395
42476
  const knowledgePath = resolveSwarmKnowledgePath(directory);
@@ -42502,6 +42583,7 @@ var init_knowledge_curator = __esm(() => {
42502
42583
  init_synonym_map();
42503
42584
  init_logger();
42504
42585
  init_knowledge_events();
42586
+ init_knowledge_reinforcement();
42505
42587
  init_knowledge_store();
42506
42588
  init_knowledge_validator();
42507
42589
  init_micro_reflector();
@@ -762,6 +762,7 @@ export declare const AgentAuthorityRuleSchema: z.ZodObject<{
762
762
  }>>>;
763
763
  blockedGlobs: z.ZodOptional<z.ZodArray<z.ZodString>>;
764
764
  allowedGlobs: z.ZodOptional<z.ZodArray<z.ZodString>>;
765
+ allowedCaseSensitiveGlobs: z.ZodOptional<z.ZodArray<z.ZodString>>;
765
766
  }, z.core.$strip>;
766
767
  export type AgentAuthorityRule = z.infer<typeof AgentAuthorityRuleSchema>;
767
768
  export declare const AuthorityConfigSchema: z.ZodObject<{
@@ -782,6 +783,7 @@ export declare const AuthorityConfigSchema: z.ZodObject<{
782
783
  }>>>;
783
784
  blockedGlobs: z.ZodOptional<z.ZodArray<z.ZodString>>;
784
785
  allowedGlobs: z.ZodOptional<z.ZodArray<z.ZodString>>;
786
+ allowedCaseSensitiveGlobs: z.ZodOptional<z.ZodArray<z.ZodString>>;
785
787
  }, z.core.$strip>>>;
786
788
  universal_deny_prefixes: z.ZodDefault<z.ZodArray<z.ZodString>>;
787
789
  verifier_config_paths: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -1405,6 +1407,7 @@ export declare const PluginConfigSchema: z.ZodObject<{
1405
1407
  }>>>;
1406
1408
  blockedGlobs: z.ZodOptional<z.ZodArray<z.ZodString>>;
1407
1409
  allowedGlobs: z.ZodOptional<z.ZodArray<z.ZodString>>;
1410
+ allowedCaseSensitiveGlobs: z.ZodOptional<z.ZodArray<z.ZodString>>;
1408
1411
  }, z.core.$strip>>>;
1409
1412
  universal_deny_prefixes: z.ZodDefault<z.ZodArray<z.ZodString>>;
1410
1413
  verifier_config_paths: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -76,6 +76,7 @@ export type AgentRule = {
76
76
  blockedZones?: FileZone[];
77
77
  blockedGlobs?: string[];
78
78
  allowedGlobs?: string[];
79
+ allowedCaseSensitiveGlobs?: string[];
79
80
  };
80
81
  export declare const DEFAULT_AGENT_AUTHORITY_RULES: Record<string, AgentRule>;
81
82
  /**
@@ -81,6 +81,7 @@ export declare function curateAndStoreSwarm(lessons: string[], projectName: stri
81
81
  enrichmentQuota?: EnrichmentQuotaOptions;
82
82
  }): Promise<{
83
83
  stored: number;
84
+ reinforced: number;
84
85
  skipped: number;
85
86
  rejected: number;
86
87
  quarantined: number;
@@ -0,0 +1,10 @@
1
+ import type { PhaseConfirmationRecord, SwarmKnowledgeEntry } from './knowledge-types.js';
2
+ export type ReinforcementReason = 'reinforced' | 'already_confirmed_phase' | 'inactive';
3
+ export interface ReinforcementResult {
4
+ entryId: string;
5
+ reinforced: boolean;
6
+ reason: ReinforcementReason;
7
+ }
8
+ export declare function isActiveSwarmKnowledgeEntry(entry: SwarmKnowledgeEntry): boolean;
9
+ export declare function findActiveSwarmNearDuplicate(lesson: string, entries: SwarmKnowledgeEntry[], threshold: number): SwarmKnowledgeEntry | undefined;
10
+ export declare function reinforceSwarmKnowledgeEntry(entry: SwarmKnowledgeEntry, confirmation: PhaseConfirmationRecord): ReinforcementResult;
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.73.0",
72
+ version: "7.73.2",
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",
@@ -16085,7 +16085,8 @@ var init_schema = __esm(() => {
16085
16085
  allowedPrefix: exports_external.array(exports_external.string()).optional(),
16086
16086
  blockedZones: exports_external.array(exports_external.enum(["production", "test", "config", "generated", "docs", "build"])).optional(),
16087
16087
  blockedGlobs: exports_external.array(exports_external.string()).optional(),
16088
- allowedGlobs: exports_external.array(exports_external.string()).optional()
16088
+ allowedGlobs: exports_external.array(exports_external.string()).optional(),
16089
+ allowedCaseSensitiveGlobs: exports_external.array(exports_external.string()).optional()
16089
16090
  });
16090
16091
  AuthorityConfigSchema = exports_external.object({
16091
16092
  enabled: exports_external.boolean().default(true),
@@ -25482,7 +25483,8 @@ function normalizePathWithCache(filePath, cwd) {
25482
25483
  }
25483
25484
  }
25484
25485
  function getGlobMatcher(pattern, caseInsensitive = process.platform === "win32" || process.platform === "darwin") {
25485
- const cached2 = globMatcherCache.get(pattern);
25486
+ const cacheKey = `${caseInsensitive ? "nocase" : "case"}\x00${pattern}`;
25487
+ const cached2 = globMatcherCache.get(cacheKey);
25486
25488
  if (cached2 !== undefined) {
25487
25489
  return cached2;
25488
25490
  }
@@ -25491,7 +25493,7 @@ function getGlobMatcher(pattern, caseInsensitive = process.platform === "win32"
25491
25493
  dot: true,
25492
25494
  nocase: caseInsensitive
25493
25495
  });
25494
- globMatcherCache.set(pattern, matcher);
25496
+ globMatcherCache.set(cacheKey, matcher);
25495
25497
  return matcher;
25496
25498
  } catch (err2) {
25497
25499
  warn(`picomatch error for pattern "${pattern}": ${err2}`);
@@ -25553,7 +25555,8 @@ function buildEffectiveRules(authorityConfig) {
25553
25555
  allowedPrefix: userRule.allowedPrefix ?? existing.allowedPrefix,
25554
25556
  blockedZones: userRule.blockedZones ?? existing.blockedZones,
25555
25557
  blockedGlobs: userRule.blockedGlobs ?? existing.blockedGlobs,
25556
- allowedGlobs: userRule.allowedGlobs ?? existing.allowedGlobs
25558
+ allowedGlobs: userRule.allowedGlobs ?? existing.allowedGlobs,
25559
+ allowedCaseSensitiveGlobs: userRule.allowedCaseSensitiveGlobs ?? existing.allowedCaseSensitiveGlobs
25557
25560
  };
25558
25561
  }
25559
25562
  return merged;
@@ -25657,6 +25660,15 @@ function checkFileAuthorityWithRules(agentName, filePath, cwd, effectiveRules, o
25657
25660
  return { allowed: true };
25658
25661
  }
25659
25662
  }
25663
+ if (rules.allowedCaseSensitiveGlobs && rules.allowedCaseSensitiveGlobs.length > 0) {
25664
+ const isCaseSensitiveGlobAllowed = rules.allowedCaseSensitiveGlobs.some((glob) => {
25665
+ const matcher = getGlobMatcher(glob, false);
25666
+ return matcher(normalizedPath);
25667
+ });
25668
+ if (isCaseSensitiveGlobAllowed) {
25669
+ return { allowed: true };
25670
+ }
25671
+ }
25660
25672
  if (rules.blockedPrefix && rules.blockedPrefix.length > 0) {
25661
25673
  for (const prefix of rules.blockedPrefix) {
25662
25674
  if (normalizedPath.startsWith(prefix)) {
@@ -25745,7 +25757,25 @@ var init_file_authority = __esm(() => {
25745
25757
  "**/test/**",
25746
25758
  "**/__tests__/**",
25747
25759
  "**/*.test.*",
25748
- "**/*.spec.*"
25760
+ "**/*.spec.*",
25761
+ "test_*.py",
25762
+ "**/test_*.py",
25763
+ "*_test.py",
25764
+ "**/*_test.py",
25765
+ "*_test.go",
25766
+ "**/*_test.go",
25767
+ "*_spec.rb",
25768
+ "**/*_spec.rb",
25769
+ "*.Tests.ps1",
25770
+ "**/*.Tests.ps1"
25771
+ ],
25772
+ allowedCaseSensitiveGlobs: [
25773
+ "*Test.java",
25774
+ "**/*Test.java",
25775
+ "*Test.kt",
25776
+ "**/*Test.kt",
25777
+ "*Tests.cs",
25778
+ "**/*Tests.cs"
25749
25779
  ],
25750
25780
  blockedZones: ["generated"]
25751
25781
  },
@@ -64255,6 +64285,49 @@ var init_synonym_map = __esm(() => {
64255
64285
  MIN_READ_CEILING_BYTES = 64 * 1024;
64256
64286
  });
64257
64287
 
64288
+ // src/hooks/knowledge-reinforcement.ts
64289
+ function isActiveSwarmKnowledgeEntry(entry) {
64290
+ return !INACTIVE_STATUSES.has(entry.status);
64291
+ }
64292
+ function findActiveSwarmNearDuplicate(lesson, entries, threshold) {
64293
+ return findNearDuplicate(lesson, entries.filter(isActiveSwarmKnowledgeEntry), threshold);
64294
+ }
64295
+ function distinctPhaseCount(records) {
64296
+ const phases = new Set;
64297
+ for (const record3 of records ?? []) {
64298
+ if (Number.isInteger(record3.phase_number)) {
64299
+ phases.add(record3.phase_number);
64300
+ }
64301
+ }
64302
+ return phases.size;
64303
+ }
64304
+ function reinforceSwarmKnowledgeEntry(entry, confirmation) {
64305
+ if (!isActiveSwarmKnowledgeEntry(entry)) {
64306
+ return { entryId: entry.id, reinforced: false, reason: "inactive" };
64307
+ }
64308
+ if ((entry.confirmed_by ?? []).some((record3) => record3.phase_number === confirmation.phase_number)) {
64309
+ return {
64310
+ entryId: entry.id,
64311
+ reinforced: false,
64312
+ reason: "already_confirmed_phase"
64313
+ };
64314
+ }
64315
+ entry.confirmed_by = [...entry.confirmed_by ?? [], confirmation];
64316
+ entry.updated_at = confirmation.confirmed_at;
64317
+ entry.phases_alive = 0;
64318
+ entry.confidence = computeConfidence(distinctPhaseCount(entry.confirmed_by), entry.auto_generated ?? false);
64319
+ return { entryId: entry.id, reinforced: true, reason: "reinforced" };
64320
+ }
64321
+ var INACTIVE_STATUSES;
64322
+ var init_knowledge_reinforcement = __esm(() => {
64323
+ init_knowledge_store();
64324
+ INACTIVE_STATUSES = new Set([
64325
+ "archived",
64326
+ "quarantined",
64327
+ "quarantined_unactionable"
64328
+ ]);
64329
+ });
64330
+
64258
64331
  // src/hooks/skill-scoring.ts
64259
64332
  import * as fs21 from "node:fs";
64260
64333
  import * as path41 from "node:path";
@@ -65808,6 +65881,7 @@ async function curateAndStoreSwarm(lessons, projectName, phaseInfo, directory, c
65808
65881
  ]);
65809
65882
  const snapshotPlusNew = [...snapshot];
65810
65883
  const toAdd = [];
65884
+ const pendingReinforcementIds = new Set;
65811
65885
  for (const lesson of lessons) {
65812
65886
  const tags = inferTags(lesson);
65813
65887
  let category = "process";
@@ -65837,8 +65911,9 @@ async function curateAndStoreSwarm(lessons, projectName, phaseInfo, directory, c
65837
65911
  continue;
65838
65912
  }
65839
65913
  }
65840
- const duplicate = findNearDuplicate(lesson, snapshotPlusNew, config3.dedup_threshold);
65914
+ const duplicate = findActiveSwarmNearDuplicate(lesson, snapshotPlusNew, config3.dedup_threshold);
65841
65915
  if (duplicate) {
65916
+ pendingReinforcementIds.add(duplicate.id);
65842
65917
  skipped++;
65843
65918
  continue;
65844
65919
  }
@@ -65919,7 +65994,9 @@ async function curateAndStoreSwarm(lessons, projectName, phaseInfo, directory, c
65919
65994
  } catch {}
65920
65995
  continue;
65921
65996
  }
65922
- if (findNearDuplicate(entry.lesson, snapshotPlusNew, config3.dedup_threshold)) {
65997
+ const duplicate = findActiveSwarmNearDuplicate(entry.lesson, snapshotPlusNew, config3.dedup_threshold);
65998
+ if (duplicate) {
65999
+ pendingReinforcementIds.add(duplicate.id);
65923
66000
  skipped++;
65924
66001
  continue;
65925
66002
  }
@@ -65928,15 +66005,48 @@ async function curateAndStoreSwarm(lessons, projectName, phaseInfo, directory, c
65928
66005
  }
65929
66006
  } catch {}
65930
66007
  let stored = 0;
65931
- if (toAdd.length > 0) {
66008
+ let reinforced = 0;
66009
+ if (toAdd.length > 0 || pendingReinforcementIds.size > 0) {
65932
66010
  await transactKnowledge(knowledgePath, (current) => {
65933
- const trulyNew = toAdd.filter((e) => !findNearDuplicate(e.lesson, current, config3.dedup_threshold));
65934
- const extraDups = toAdd.length - trulyNew.length;
65935
- skipped += extraDups;
65936
- if (trulyNew.length === 0)
65937
- return null;
65938
- stored = trulyNew.length;
65939
- return [...current, ...trulyNew];
66011
+ let changed = false;
66012
+ for (const id of pendingReinforcementIds) {
66013
+ const existing = current.find((entry) => entry.id === id);
66014
+ if (!existing)
66015
+ continue;
66016
+ const result = reinforceSwarmKnowledgeEntry(existing, {
66017
+ phase_number: phaseInfo.phase_number,
66018
+ confirmed_at: new Date().toISOString(),
66019
+ project_name: projectName
66020
+ });
66021
+ if (result.reinforced) {
66022
+ reinforced++;
66023
+ changed = true;
66024
+ }
66025
+ }
66026
+ const trulyNew = [];
66027
+ for (const entry of toAdd) {
66028
+ const duplicate = findActiveSwarmNearDuplicate(entry.lesson, current, config3.dedup_threshold);
66029
+ if (duplicate) {
66030
+ skipped++;
66031
+ const result = reinforceSwarmKnowledgeEntry(duplicate, {
66032
+ phase_number: phaseInfo.phase_number,
66033
+ confirmed_at: new Date().toISOString(),
66034
+ project_name: projectName
66035
+ });
66036
+ if (result.reinforced) {
66037
+ reinforced++;
66038
+ changed = true;
66039
+ }
66040
+ continue;
66041
+ }
66042
+ trulyNew.push(entry);
66043
+ }
66044
+ if (trulyNew.length > 0) {
66045
+ current.push(...trulyNew);
66046
+ stored = trulyNew.length;
66047
+ changed = true;
66048
+ }
66049
+ return changed ? current : null;
65940
66050
  });
65941
66051
  }
65942
66052
  await enforceKnowledgeCap(knowledgePath, config3.swarm_max_entries);
@@ -65954,7 +66064,7 @@ async function curateAndStoreSwarm(lessons, projectName, phaseInfo, directory, c
65954
66064
  if (!options?.skipAutoPromotion) {
65955
66065
  await _internals30.runAutoPromotion(directory, config3);
65956
66066
  }
65957
- return { stored, skipped, rejected, quarantined };
66067
+ return { stored, reinforced, skipped, rejected, quarantined };
65958
66068
  }
65959
66069
  async function runAutoPromotion(directory, config3) {
65960
66070
  const knowledgePath = resolveSwarmKnowledgePath(directory);
@@ -66067,6 +66177,7 @@ var init_knowledge_curator = __esm(() => {
66067
66177
  init_synonym_map();
66068
66178
  init_logger();
66069
66179
  init_knowledge_events();
66180
+ init_knowledge_reinforcement();
66070
66181
  init_knowledge_store();
66071
66182
  init_knowledge_validator();
66072
66183
  init_micro_reflector();
@@ -124912,10 +125023,10 @@ var knowledge_ack = createSwarmTool({
124912
125023
  // src/tools/knowledge-add.ts
124913
125024
  init_zod();
124914
125025
  init_config();
125026
+ init_knowledge_reinforcement();
124915
125027
  init_knowledge_store();
124916
125028
  init_knowledge_validator();
124917
125029
  init_manager();
124918
- init_utils();
124919
125030
  init_create_tool();
124920
125031
  import { randomUUID as randomUUID15 } from "node:crypto";
124921
125032
  var VALID_CATEGORIES2 = [
@@ -125007,9 +125118,13 @@ var knowledge_add = createSwarmTool({
125007
125118
  });
125008
125119
  }
125009
125120
  let project_name = "";
125121
+ let phase_number = 1;
125010
125122
  try {
125011
125123
  const plan = await loadPlan(directory);
125012
125124
  project_name = plan?.title ?? "";
125125
+ if (typeof plan?.current_phase === "number") {
125126
+ phase_number = plan.current_phase;
125127
+ }
125013
125128
  } catch {}
125014
125129
  const entry = {
125015
125130
  id: randomUUID15(),
@@ -125054,19 +125169,6 @@ var knowledge_add = createSwarmTool({
125054
125169
  }
125055
125170
  }
125056
125171
  } catch {}
125057
- try {
125058
- const existingEntries = await readKnowledge(resolveSwarmKnowledgePath(directory));
125059
- const duplicate = findNearDuplicate(lesson, existingEntries, dedupThreshold);
125060
- if (duplicate) {
125061
- return JSON.stringify({
125062
- success: false,
125063
- id: duplicate.id,
125064
- message: "near-duplicate of existing entry"
125065
- });
125066
- }
125067
- } catch (err2) {
125068
- warn("knowledge_add: dedup check failed — skipping near-duplicate detection", err2);
125069
- }
125070
125172
  const actionability = validateActionability(entry);
125071
125173
  if (!actionability.actionable) {
125072
125174
  try {
@@ -125082,7 +125184,55 @@ var knowledge_add = createSwarmTool({
125082
125184
  }
125083
125185
  try {
125084
125186
  const maxEntries = config3?.knowledge?.swarm_max_entries ?? 100;
125085
- await appendKnowledgeWithCapEnforcement(resolveSwarmKnowledgePath(directory), entry, maxEntries);
125187
+ let duplicateResponse;
125188
+ await transactKnowledge(resolveSwarmKnowledgePath(directory), (existingEntries) => {
125189
+ const activeDuplicate = findActiveSwarmNearDuplicate(lesson, existingEntries, dedupThreshold);
125190
+ if (activeDuplicate) {
125191
+ const result = reinforceSwarmKnowledgeEntry(activeDuplicate, {
125192
+ phase_number,
125193
+ confirmed_at: new Date().toISOString(),
125194
+ project_name
125195
+ });
125196
+ duplicateResponse = {
125197
+ id: activeDuplicate.id,
125198
+ reinforced: result.reinforced,
125199
+ idempotent: result.reason === "already_confirmed_phase",
125200
+ inactive: false
125201
+ };
125202
+ return result.reinforced ? existingEntries : null;
125203
+ }
125204
+ const inactiveDuplicate = findNearDuplicate(lesson, existingEntries, dedupThreshold);
125205
+ if (inactiveDuplicate) {
125206
+ duplicateResponse = {
125207
+ id: inactiveDuplicate.id,
125208
+ reinforced: false,
125209
+ idempotent: false,
125210
+ inactive: true
125211
+ };
125212
+ return null;
125213
+ }
125214
+ const updated = [...existingEntries, entry];
125215
+ if (updated.length > maxEntries) {
125216
+ return updated.slice(updated.length - maxEntries);
125217
+ }
125218
+ return updated;
125219
+ });
125220
+ if (duplicateResponse) {
125221
+ if (duplicateResponse.inactive) {
125222
+ return JSON.stringify({
125223
+ success: false,
125224
+ id: duplicateResponse.id,
125225
+ message: "near-duplicate of inactive existing entry"
125226
+ });
125227
+ }
125228
+ return JSON.stringify({
125229
+ success: true,
125230
+ id: duplicateResponse.id,
125231
+ reinforced: duplicateResponse.reinforced,
125232
+ idempotent: duplicateResponse.idempotent,
125233
+ message: duplicateResponse.reinforced ? "near-duplicate reinforced existing entry" : "near-duplicate already confirmed for this phase"
125234
+ });
125235
+ }
125086
125236
  } catch (err2) {
125087
125237
  const message = err2 instanceof Error ? err2.message : "Unknown error";
125088
125238
  return JSON.stringify({
@@ -130167,7 +130317,7 @@ async function executePhaseComplete(args2, workingDirectory, directory) {
130167
130317
  const sessionState = swarmState.agentSessions.get(sessionID);
130168
130318
  if (sessionState) {
130169
130319
  sessionState.pendingAdvisoryMessages ??= [];
130170
- sessionState.pendingAdvisoryMessages.push(`[CURATOR] Knowledge curation: ${curationResult.stored} stored, ${curationResult.skipped} skipped, ${curationResult.rejected} rejected, ${curationResult.quarantined} quarantined (unactionable).`);
130320
+ sessionState.pendingAdvisoryMessages.push(`[CURATOR] Knowledge curation: ${curationResult.stored} stored, ${curationResult.reinforced} reinforced, ${curationResult.skipped} skipped, ${curationResult.rejected} rejected, ${curationResult.quarantined} quarantined (unactionable).`);
130171
130321
  }
130172
130322
  }
130173
130323
  await updateRetrievalOutcome(dir, `Phase ${phase}`, true);
@@ -141926,6 +142076,7 @@ var write_architecture_supervisor_evidence = createSwarmTool({
141926
142076
  };
141927
142077
  const evidencePath = writeSupervisorReport(dirResult.directory, report);
141928
142078
  let knowledgeProposed = 0;
142079
+ let knowledgeReinforced = 0;
141929
142080
  let knowledgeQuarantined = 0;
141930
142081
  try {
141931
142082
  const config3 = loadPluginConfig(dirResult.directory);
@@ -141942,6 +142093,7 @@ var write_architecture_supervisor_evidence = createSwarmTool({
141942
142093
  }
141943
142094
  });
141944
142095
  knowledgeProposed = result.stored;
142096
+ knowledgeReinforced = result.reinforced;
141945
142097
  knowledgeQuarantined = result.quarantined;
141946
142098
  }
141947
142099
  } catch {}
@@ -141963,6 +142115,7 @@ var write_architecture_supervisor_evidence = createSwarmTool({
141963
142115
  verdict: args2.verdict,
141964
142116
  findings_count: args2.findings.length,
141965
142117
  knowledge_proposed: knowledgeProposed,
142118
+ knowledge_reinforced: knowledgeReinforced,
141966
142119
  knowledge_quarantined: knowledgeQuarantined,
141967
142120
  skills_proposed: skillsProposed,
141968
142121
  evidence_path: evidencePath
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-swarm",
3
- "version": "7.73.0",
3
+ "version": "7.73.2",
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",