opencode-swarm 7.109.4 → 7.110.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/.opencode/skills/swarm/SKILL.md +186 -0
  2. package/.opencode/skills/swarm-ci-monitor/SKILL.md +369 -0
  3. package/.opencode/skills/swarm-pr-feedback/SKILL.md +86 -17
  4. package/dist/agents/explorer.d.ts +1 -1
  5. package/dist/cli/{config-doctor-9vjyj9m3.js → config-doctor-q5e9yzn6.js} +2 -2
  6. package/dist/cli/{curator-zn1kmwmz.js → curator-c7dzf9rt.js} +13 -11
  7. package/dist/cli/{curator-llm-factory-3hf9xpnd.js → curator-llm-factory-v4gxfnhd.js} +11 -11
  8. package/dist/cli/{explorer-ksr3xq7n.js → explorer-t5srbq64.js} +1 -1
  9. package/dist/cli/{guardrail-explain-hjz1k6w0.js → guardrail-explain-qc02w4yb.js} +12 -12
  10. package/dist/cli/{guardrail-log-5240bawg.js → guardrail-log-973nan7m.js} +3 -3
  11. package/dist/cli/{hive-promoter-j81yj97d.js → hive-promoter-kr56vt08.js} +11 -11
  12. package/dist/cli/{index-18690p15.js → index-0g5xsrp4.js} +19 -7
  13. package/dist/cli/{index-zh3qh6m7.js → index-1k5arp07.js} +2 -2
  14. package/dist/cli/{index-2nt4mq9n.js → index-40h0yp9e.js} +69 -1
  15. package/dist/cli/{index-d3txrx5q.js → index-53z1afgh.js} +1 -1
  16. package/dist/cli/{index-y3nx3ss0.js → index-5d0t7npd.js} +1222 -1016
  17. package/dist/cli/{index-p5vm9gy9.js → index-5rfxzgmt.js} +1 -1
  18. package/dist/cli/{index-41fgssqr.js → index-c483hnn4.js} +2 -2
  19. package/dist/cli/{index-b15qbvph.js → index-cmjq1kg7.js} +43 -34
  20. package/dist/cli/{index-09xpycan.js → index-jt58s751.js} +1 -1
  21. package/dist/cli/{index-mtzjbaaa.js → index-mvg66d9n.js} +3 -3
  22. package/dist/cli/{index-nvmy7xmj.js → index-r3ck99jh.js} +17 -17
  23. package/dist/cli/{index-8f10r7ay.js → index-v8pk35h7.js} +5 -5
  24. package/dist/cli/{index-zjh648z4.js → index-xddysq89.js} +1 -1
  25. package/dist/cli/{index-y5z2qdvk.js → index-yhmt3dw3.js} +2 -2
  26. package/dist/cli/index.js +13 -13
  27. package/dist/cli/{knowledge-escalator-9ksrgq7h.js → knowledge-escalator-0dkkenp2.js} +4 -4
  28. package/dist/cli/{knowledge-events-ymysgtrr.js → knowledge-events-bckbj6ah.js} +2 -2
  29. package/dist/cli/{knowledge-store-562fefjy.js → knowledge-store-dvjbdn00.js} +3 -2
  30. package/dist/cli/{knowledge-validator-b9nvmzjc.js → knowledge-validator-bqr936sg.js} +3 -3
  31. package/dist/cli/{schema-5vmb5dh8.js → schema-s48je8zt.js} +1 -1
  32. package/dist/cli/{skill-generator-mjfqvnn0.js → skill-generator-5rsjmmfq.js} +7 -5
  33. package/dist/config/bundled-skills.d.ts +1 -1
  34. package/dist/hooks/curator-postmortem.d.ts +33 -2
  35. package/dist/hooks/curator-types.d.ts +2 -2
  36. package/dist/hooks/curator.d.ts +2 -1
  37. package/dist/hooks/knowledge-link.d.ts +9 -0
  38. package/dist/hooks/knowledge-reader.d.ts +14 -0
  39. package/dist/index.js +81 -65
  40. package/dist/services/skill-generator.d.ts +1 -0
  41. package/dist/services/warning-buffer.d.ts +25 -0
  42. package/dist/tools/tool-metadata.d.ts +4 -4
  43. package/package.json +3 -1
@@ -1,7 +1,4 @@
1
1
  // @bun
2
- import {
3
- readRecentEscalations
4
- } from "./index-mtzjbaaa.js";
5
2
  import {
6
3
  getGlobalEventBus
7
4
  } from "./index-c8s9a3zh.js";
@@ -10,7 +7,23 @@ import {
10
7
  readDoctorArtifact,
11
8
  removeStraySwarmDir,
12
9
  runConfigDoctor
13
- } from "./index-zjh648z4.js";
10
+ } from "./index-xddysq89.js";
11
+ import {
12
+ AGENT_TOOL_MAP,
13
+ ALL_SUBAGENT_NAMES,
14
+ ExternalSkillsConfigSchema,
15
+ GATE_CONFIG_KNOWN_SECTION_KEYS,
16
+ GateConfigSchema,
17
+ KnowledgeConfigSchema,
18
+ ORCHESTRATOR_NAME,
19
+ PluginConfigSchema,
20
+ SkillImproverConfigSchema,
21
+ TOOL_NAMES,
22
+ TOOL_NAME_SET,
23
+ getCanonicalAgentRole,
24
+ resolveExternalSkillsConfig,
25
+ stripKnownSwarmPrefix
26
+ } from "./index-0g5xsrp4.js";
14
27
  import {
15
28
  MAX_TRANSIENT_RETRIES,
16
29
  PlanSchema,
@@ -76,27 +89,6 @@ import {
76
89
  subscribe,
77
90
  unsubscribe
78
91
  } from "./index-85bacexr.js";
79
- import {
80
- CURATOR_INIT_PROMPT,
81
- CURATOR_PHASE_PROMPT,
82
- READ_ONLY_LANE_GUIDANCE
83
- } from "./index-09xpycan.js";
84
- import {
85
- AGENT_TOOL_MAP,
86
- ALL_SUBAGENT_NAMES,
87
- ExternalSkillsConfigSchema,
88
- GATE_CONFIG_KNOWN_SECTION_KEYS,
89
- GateConfigSchema,
90
- KnowledgeConfigSchema,
91
- ORCHESTRATOR_NAME,
92
- PluginConfigSchema,
93
- SkillImproverConfigSchema,
94
- TOOL_NAMES,
95
- TOOL_NAME_SET,
96
- getCanonicalAgentRole,
97
- resolveExternalSkillsConfig,
98
- stripKnownSwarmPrefix
99
- } from "./index-18690p15.js";
100
92
  import {
101
93
  DEFAULT_SKILL_MIN_CONFIDENCE,
102
94
  DEFAULT_SKILL_MIN_CONFIRMATIONS,
@@ -113,15 +105,7 @@ import {
113
105
  retireOrMarkStale,
114
106
  retireSkill,
115
107
  selectCandidateEntries
116
- } from "./index-b15qbvph.js";
117
- import {
118
- effectiveRetrievalOutcomes,
119
- readKnowledgeCounterRollups,
120
- readKnowledgeEvents,
121
- recordHiveKnowledgeEvent,
122
- recordKnowledgeEvent,
123
- resolveKnowledgeEventsPath
124
- } from "./index-y5z2qdvk.js";
108
+ } from "./index-cmjq1kg7.js";
125
109
  import {
126
110
  appendUnactionable,
127
111
  quarantineEntry,
@@ -131,7 +115,18 @@ import {
131
115
  validateActionability,
132
116
  validateActionableFields,
133
117
  validateLesson
134
- } from "./index-41fgssqr.js";
118
+ } from "./index-c483hnn4.js";
119
+ import {
120
+ readRecentEscalations
121
+ } from "./index-mvg66d9n.js";
122
+ import {
123
+ effectiveRetrievalOutcomes,
124
+ readKnowledgeCounterRollups,
125
+ readKnowledgeEvents,
126
+ recordHiveKnowledgeEvent,
127
+ recordKnowledgeEvent,
128
+ resolveKnowledgeEventsPath
129
+ } from "./index-yhmt3dw3.js";
135
130
  import {
136
131
  appendKnowledge,
137
132
  appendRejectedLesson,
@@ -153,7 +148,12 @@ import {
153
148
  rewriteKnowledge,
154
149
  transactFile,
155
150
  transactKnowledge
156
- } from "./index-8f10r7ay.js";
151
+ } from "./index-v8pk35h7.js";
152
+ import {
153
+ KNOWLEDGE_SCHEMA_VERSION,
154
+ OUTCOME_BLOCK_THRESHOLD,
155
+ isActiveStatus
156
+ } from "./index-bfwt3abw.js";
157
157
  import {
158
158
  isLinked,
159
159
  readLinkPointer,
@@ -162,12 +162,12 @@ import {
162
162
  resolveLinkDir,
163
163
  sanitizeLinkId,
164
164
  writeLinkPointer
165
- } from "./index-2nt4mq9n.js";
165
+ } from "./index-40h0yp9e.js";
166
166
  import {
167
- KNOWLEDGE_SCHEMA_VERSION,
168
- OUTCOME_BLOCK_THRESHOLD,
169
- isActiveStatus
170
- } from "./index-bfwt3abw.js";
167
+ CURATOR_INIT_PROMPT,
168
+ CURATOR_PHASE_PROMPT,
169
+ READ_ONLY_LANE_GUIDANCE
170
+ } from "./index-jt58s751.js";
171
171
  import {
172
172
  SandboxCapabilityProbe,
173
173
  init_capability_probe
@@ -4983,7 +4983,18 @@ function loadPluginConfigWithMeta(directory) {
4983
4983
  return { config, loadedFromFile, configHadErrors };
4984
4984
  }
4985
4985
  // src/services/warning-buffer.ts
4986
+ init_logger();
4986
4987
  var deferredWarnings = [];
4988
+ var MAX_DEFERRED_WARNINGS = 50;
4989
+ function addDeferredWarning(warning) {
4990
+ if (deferredWarnings.length < MAX_DEFERRED_WARNINGS) {
4991
+ deferredWarnings.push(warning);
4992
+ }
4993
+ }
4994
+ function advisoryWarn(message, data) {
4995
+ addDeferredWarning(message);
4996
+ log(message, data);
4997
+ }
4987
4998
  function getDeferredWarnings() {
4988
4999
  return [...deferredWarnings];
4989
5000
  }
@@ -4992,6 +5003,7 @@ function getDeferredWarnings() {
4992
5003
  import { randomUUID as randomUUID3 } from "crypto";
4993
5004
  import * as fsp from "fs/promises";
4994
5005
  import * as path8 from "path";
5006
+ init_logger();
4995
5007
  var BUNDLED_PROJECT_SKILLS = [
4996
5008
  "brainstorm",
4997
5009
  "specify",
@@ -5008,8 +5020,10 @@ var BUNDLED_PROJECT_SKILLS = [
5008
5020
  "swarm-implement",
5009
5021
  "design-docs",
5010
5022
  "swarm-pr-review",
5023
+ "swarm",
5011
5024
  "swarm-pr-feedback",
5012
5025
  "swarm-pr-subscribe",
5026
+ "swarm-ci-monitor",
5013
5027
  "issue-ingest",
5014
5028
  "plan",
5015
5029
  "critic-gate",
@@ -5028,9 +5042,9 @@ var BUNDLED_PROJECT_SKILLS = [
5028
5042
  ];
5029
5043
  var MAX_SKILL_FILES = 64;
5030
5044
  var MAX_SKILL_BYTES = 512000;
5031
- function warnBundledSkillSyncFailure(err) {
5045
+ function describeBundledSkillSyncFailure(err) {
5032
5046
  const message = err instanceof Error ? err.message : String(err);
5033
- console.warn(`[opencode-swarm] Could not install bundled project skills; continuing without sync: ${message}`);
5047
+ return `[opencode-swarm] Could not install bundled project skills; continuing without sync: ${message}`;
5034
5048
  }
5035
5049
  async function isSymbolicLinkAsync(p) {
5036
5050
  try {
@@ -5178,13 +5192,17 @@ async function syncBundledProjectSkillsIfMissingAsync(projectDirectory, packageR
5178
5192
  if (!await ensureNotSymlinkedDirectoryAsync(destDir))
5179
5193
  continue;
5180
5194
  await copyBundledDirectoryBoundedAsync(sourceDir, destDir);
5181
- if (!quiet) {
5182
- console.warn(`[opencode-swarm] Synchronized bundled skill .opencode/skills/${slug}/SKILL.md for first-class /swarm command support`);
5183
- }
5195
+ log("synchronized bundled skill", {
5196
+ slug: `.opencode/skills/${slug}/SKILL.md`
5197
+ });
5184
5198
  }
5185
5199
  } catch (err) {
5186
- if (!quiet)
5187
- warnBundledSkillSyncFailure(err);
5200
+ const failureMsg = describeBundledSkillSyncFailure(err);
5201
+ if (quiet) {
5202
+ advisoryWarn(failureMsg);
5203
+ } else {
5204
+ console.warn(failureMsg);
5205
+ }
5188
5206
  }
5189
5207
  }
5190
5208
 
@@ -6553,790 +6571,13 @@ import { promises as fs9 } from "fs";
6553
6571
  import path28 from "path";
6554
6572
 
6555
6573
  // src/hooks/curator-postmortem.ts
6556
- import { existsSync as existsSync7, readdirSync, readFileSync as readFileSync4 } from "fs";
6557
- import * as path12 from "path";
6558
- var MAX_INPUT_TEXT_CHARS = 500;
6559
- var MAX_KNOWLEDGE_ENTRIES = 500;
6560
- var MAX_PROPOSALS = 50;
6561
- var MAX_RETROSPECTIVES = 50;
6562
- var MAX_DRIFT_REPORTS = 50;
6563
- var MAX_UNACTIONABLE = 1000;
6564
- async function collectKnowledgeSummary(directory, scope = "project", sessionID) {
6565
- const entries = await readKnowledge(resolveSwarmKnowledgePath(directory), MAX_KNOWLEDGE_ENTRIES);
6566
- let events = await readKnowledgeEvents(directory, MAX_KNOWLEDGE_ENTRIES * 4);
6567
- if (scope === "session" && sessionID) {
6568
- events = events.filter((e) => e.session_id === sessionID);
6569
- }
6570
- const countsMap = new Map;
6571
- for (const e of events) {
6572
- if (e.type !== "applied" && e.type !== "violated" && e.type !== "ignored")
6573
- continue;
6574
- const kid = e.knowledge_id ?? e.entry_id;
6575
- if (!kid)
6576
- continue;
6577
- const c = countsMap.get(kid) ?? { applied: 0, violated: 0, ignored: 0 };
6578
- if (e.type === "applied")
6579
- c.applied++;
6580
- else if (e.type === "violated")
6581
- c.violated++;
6582
- else if (e.type === "ignored")
6583
- c.ignored++;
6584
- countsMap.set(kid, c);
6585
- }
6586
- return entries.map((entry) => {
6587
- const c = countsMap.get(entry.id) ?? {
6588
- applied: 0,
6589
- violated: 0,
6590
- ignored: 0
6591
- };
6592
- return {
6593
- id: entry.id,
6594
- lesson: entry.lesson,
6595
- applied: c.applied,
6596
- violated: c.violated,
6597
- ignored: c.ignored,
6598
- confidence: entry.confidence ?? 0.5,
6599
- status: entry.status ?? "active"
6600
- };
6601
- });
6602
- }
6603
- function extractSection(text, sectionName) {
6604
- const pattern = new RegExp(`^${sectionName}:\\s*\\n([\\s\\S]*?)(?=\\n[A-Z_]+:\\s*(?:\\n|$)|$)`, "m");
6605
- const match = text.match(pattern);
6606
- return match?.[1]?.trim() || null;
6607
- }
6608
- function normalizeRecommendationAction(raw) {
6609
- const value = String(raw ?? "").toLowerCase().trim();
6610
- if (value === "promote" || value === "promote_to_hive")
6611
- return "promote";
6612
- if (value === "archive" || value === "flag_stale")
6613
- return "archive";
6614
- if (value === "rewrite")
6615
- return "rewrite";
6616
- if (value === "flag_contradiction")
6617
- return "flag_contradiction";
6618
- if (value === "merge")
6619
- return null;
6620
- return null;
6621
- }
6622
- function normalizeProposalSlug(raw) {
6623
- return raw.trim().replace(/^proposals[\\/]/, "").replace(/\.md$/i, "").replace(/\.json$/i, "");
6624
- }
6625
- function parseStructuredPostMortemActions(llmOutput) {
6626
- const parsed = {
6627
- summary: extractSection(llmOutput, "SUMMARY"),
6628
- recommendations: [],
6629
- queueTriage: [],
6630
- diagnostics: []
6631
- };
6632
- const fence = /```(?:json|jsonc)?\s+postmortem_actions\s*\n([\s\S]*?)\n```/g;
6633
- for (const match of llmOutput.matchAll(fence)) {
6634
- let data;
6635
- try {
6636
- data = JSON.parse(match[1]);
6637
- } catch (err) {
6638
- parsed.diagnostics.push(`postmortem_actions malformed_json: ${err instanceof Error ? err.message : String(err)}`);
6639
- continue;
6640
- }
6641
- if (!data || typeof data !== "object" || Array.isArray(data)) {
6642
- parsed.diagnostics.push("postmortem_actions expected object");
6643
- continue;
6644
- }
6645
- const obj = data;
6646
- if (typeof obj.summary === "string" && obj.summary.trim()) {
6647
- parsed.summary = obj.summary.trim().slice(0, 1000);
6648
- }
6649
- if (Array.isArray(obj.curation_recommendations)) {
6650
- for (const [index, item] of obj.curation_recommendations.entries()) {
6651
- if (!item || typeof item !== "object") {
6652
- parsed.diagnostics.push(`curation_recommendations[${index}] invalid object`);
6653
- continue;
6654
- }
6655
- const rec = item;
6656
- const action = normalizeRecommendationAction(rec.action);
6657
- const lesson = String(rec.lesson ?? rec.reason ?? "").trim();
6658
- const reason = String(rec.reason ?? lesson).trim();
6659
- if (!action || !reason) {
6660
- parsed.diagnostics.push(`curation_recommendations[${index}] missing action/reason`);
6661
- continue;
6662
- }
6663
- if (action === "rewrite" && lesson.length < 15) {
6664
- parsed.diagnostics.push(`curation_recommendations[${index}] rewrite missing lesson`);
6665
- continue;
6666
- }
6667
- parsed.recommendations.push({
6668
- action,
6669
- entry_id: typeof rec.entry_id === "string" && rec.entry_id.trim() ? rec.entry_id.trim() : undefined,
6670
- lesson: (lesson || reason).slice(0, 280),
6671
- reason: reason.slice(0, 280),
6672
- category: typeof rec.category === "string" ? rec.category : undefined,
6673
- confidence: typeof rec.confidence === "number" ? rec.confidence : undefined
6674
- });
6675
- }
6676
- }
6677
- if (Array.isArray(obj.queue_triage)) {
6678
- for (const [index, item] of obj.queue_triage.entries()) {
6679
- if (!item || typeof item !== "object") {
6680
- parsed.diagnostics.push(`queue_triage[${index}] invalid object`);
6681
- continue;
6682
- }
6683
- const triage = item;
6684
- const proposalId = String(triage.proposal_id ?? "").trim();
6685
- const action = String(triage.action ?? "").toLowerCase().trim();
6686
- if (!proposalId || action !== "apply" && action !== "reject") {
6687
- parsed.diagnostics.push(`queue_triage[${index}] missing proposal_id/action`);
6688
- continue;
6689
- }
6690
- parsed.queueTriage.push({
6691
- proposal_id: proposalId.slice(0, 120),
6692
- action,
6693
- reason: String(triage.reason ?? "").slice(0, 280)
6694
- });
6695
- }
6696
- }
6697
- }
6698
- return parsed;
6699
- }
6700
- function parseLegacyPostMortemActions(llmOutput) {
6701
- const parsed = {
6702
- summary: extractSection(llmOutput, "SUMMARY"),
6703
- recommendations: [],
6704
- queueTriage: [],
6705
- diagnostics: []
6706
- };
6707
- const curation = extractSection(llmOutput, "CURATION_RECOMMENDATIONS");
6708
- if (curation) {
6709
- let recIndex = 0;
6710
- for (const line of curation.split(`
6711
- `)) {
6712
- const trimmed = line.trim();
6713
- if (!trimmed.startsWith("-"))
6714
- continue;
6715
- const body = trimmed.replace(/^-\s*/, "");
6716
- const [head2, reasonPart = ""] = body.split(/\s+\u2014\s+|\s+-\s+/, 2);
6717
- const [rawAction, rest = ""] = head2.split(/:\s*/, 2);
6718
- const lowerRaw = rawAction.toLowerCase().trim();
6719
- const action = normalizeRecommendationAction(rawAction);
6720
- if (!action) {
6721
- if (lowerRaw === "merge") {
6722
- parsed.diagnostics.push("legacy curation_recommendations unsupported action 'merge' \u2014 dropped");
6723
- } else if (lowerRaw) {
6724
- parsed.diagnostics.push(`legacy curation_recommendations unrecognized action '${lowerRaw}'`);
6725
- }
6726
- continue;
6727
- }
6728
- const idMatch = rest.match(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/i);
6729
- const reason = (reasonPart || rest).trim();
6730
- if (!reason) {
6731
- parsed.diagnostics.push(`legacy curation_recommendations[${recIndex}] missing reason`);
6732
- continue;
6733
- }
6734
- parsed.recommendations.push({
6735
- action,
6736
- entry_id: idMatch?.[0],
6737
- lesson: reason.slice(0, 280),
6738
- reason: reason.slice(0, 280)
6739
- });
6740
- recIndex++;
6741
- }
6742
- }
6743
- const queue = extractSection(llmOutput, "QUEUE_TRIAGE");
6744
- if (queue) {
6745
- for (const line of queue.split(`
6746
- `)) {
6747
- const trimmed = line.trim();
6748
- if (!trimmed.startsWith("-"))
6749
- continue;
6750
- if (trimmed.includes(":")) {
6751
- const match = trimmed.match(/^-\s*([^:]+):\s*(APPLY|REJECT)\b\s*(?:\u2014|-)?\s*(.*)$/i);
6752
- if (!match) {
6753
- parsed.diagnostics.push("legacy queue_triage malformed line");
6754
- continue;
6755
- }
6756
- parsed.queueTriage.push({
6757
- proposal_id: match[1].trim().slice(0, 120),
6758
- action: match[2].toLowerCase(),
6759
- reason: match[3].trim().slice(0, 280)
6760
- });
6761
- }
6762
- }
6763
- }
6764
- return parsed;
6765
- }
6766
- function parsePostMortemActions(llmOutput) {
6767
- const structured = parseStructuredPostMortemActions(llmOutput);
6768
- const legacy = parseLegacyPostMortemActions(llmOutput);
6769
- return {
6770
- summary: structured.summary ?? legacy.summary,
6771
- recommendations: structured.recommendations.length > 0 ? structured.recommendations : legacy.recommendations,
6772
- queueTriage: structured.queueTriage.length > 0 ? structured.queueTriage : legacy.queueTriage,
6773
- diagnostics: [...structured.diagnostics, ...legacy.diagnostics]
6774
- };
6775
- }
6776
- async function executePostMortemActions(directory, parsed, options) {
6777
- const warnings = [];
6778
- const result = {
6779
- knowledge_applied: 0,
6780
- knowledge_skipped: 0,
6781
- hive_promotions: 0,
6782
- hive_encounters_incremented: 0,
6783
- hive_advancements: 0,
6784
- proposals_approved: 0,
6785
- proposals_rejected: 0,
6786
- proposals_skipped: 0
6787
- };
6788
- const knowledgeConfig = options.knowledgeConfig ?? await _internals7.loadDefaultKnowledgeConfig();
6789
- if (parsed.recommendations.length > 0) {
6790
- try {
6791
- const knowledgeResult = await _internals7.applyCuratorKnowledgeUpdates(directory, parsed.recommendations, knowledgeConfig);
6792
- result.knowledge_applied = knowledgeResult.applied;
6793
- result.knowledge_skipped = knowledgeResult.skipped;
6794
- } catch (err) {
6795
- warnings.push(`Post-mortem knowledge actions failed: ${err instanceof Error ? err.message : String(err)}`);
6796
- }
6797
- try {
6798
- const entries = await _internals7.readSwarmKnowledge(directory);
6799
- const hiveResult = await _internals7.checkHivePromotions(entries, knowledgeConfig);
6800
- result.hive_promotions = hiveResult.new_promotions;
6801
- result.hive_encounters_incremented = hiveResult.encounters_incremented;
6802
- result.hive_advancements = hiveResult.advancements;
6803
- } catch (err) {
6804
- warnings.push(`Post-mortem hive promotion check failed: ${err instanceof Error ? err.message : String(err)}`);
6805
- }
6806
- }
6807
- if (parsed.queueTriage.length > 0) {
6808
- try {
6809
- const proposalResult = await _internals7.applyProposalTriage(directory, parsed.queueTriage);
6810
- result.proposals_approved = proposalResult.approved.length;
6811
- result.proposals_rejected = proposalResult.rejected.length;
6812
- result.proposals_skipped = proposalResult.skipped.length;
6813
- } catch (err) {
6814
- warnings.push(`Post-mortem proposal triage failed: ${err instanceof Error ? err.message : String(err)}`);
6815
- }
6816
- }
6817
- return { result, warnings };
6818
- }
6819
- async function repairPostMortemActions(llmOutput, diagnostics, options) {
6820
- if (!options.llmDelegate || diagnostics.length === 0)
6821
- return null;
6822
- const ac = new AbortController;
6823
- const timer = setTimeout(() => ac.abort(), 30000);
6824
- try {
6825
- const repairPrompt = [
6826
- "Repair the supplied CURATOR_POSTMORTEM output into one valid fenced JSON block.",
6827
- "Return only this fence:",
6828
- "```json postmortem_actions",
6829
- '{"summary":"...","curation_recommendations":[],"queue_triage":[]}',
6830
- "```",
6831
- "Allowed curation action values: promote, archive, rewrite, flag_contradiction.",
6832
- "Allowed queue_triage action values: apply, reject.",
6833
- "Do not include unsupported actions such as merge.",
6834
- "Diagnostics:",
6835
- diagnostics.join("; "),
6836
- "Original output:",
6837
- llmOutput.slice(0, 8000)
6838
- ].join(`
6839
- `);
6840
- const repaired = await options.llmDelegate("", repairPrompt, ac.signal);
6841
- const parsed = _internals7.parsePostMortemActions(repaired);
6842
- if (parsed.diagnostics.length === 0) {
6843
- return parsed;
6844
- }
6845
- return null;
6846
- } catch {
6847
- return null;
6848
- } finally {
6849
- clearTimeout(timer);
6850
- }
6851
- }
6852
- function readJsonlFile(filePath, maxLines) {
6853
- try {
6854
- if (!existsSync7(filePath))
6855
- return [];
6856
- const content = readFileSync4(filePath, "utf-8");
6857
- const results = [];
6858
- const max = maxLines !== undefined && maxLines > 0 ? maxLines : Infinity;
6859
- for (const line of content.split(`
6860
- `)) {
6861
- if (results.length >= max)
6862
- break;
6863
- if (!line.trim())
6864
- continue;
6865
- try {
6866
- results.push(JSON.parse(line));
6867
- } catch {}
6868
- }
6869
- return results;
6870
- } catch {
6871
- return [];
6872
- }
6873
- }
6874
- function collectRetrospectives(directory) {
6875
- const results = [];
6876
- const evidenceDir = path12.join(directory, ".swarm", "evidence");
6877
- try {
6878
- if (!existsSync7(evidenceDir))
6879
- return results;
6880
- const retroDirs = readdirSync(evidenceDir, { withFileTypes: true }).filter((e) => e.isDirectory() && e.name.startsWith("retro-")).slice(0, MAX_RETROSPECTIVES);
6881
- for (const entry of retroDirs) {
6882
- const retroPath = path12.join(evidenceDir, entry.name, "evidence.json");
6883
- if (existsSync7(retroPath)) {
6884
- try {
6885
- results.push(readFileSync4(retroPath, "utf-8"));
6886
- } catch {}
6887
- }
6888
- }
6889
- } catch {}
6890
- return results;
6891
- }
6892
- async function collectDriftReports(directory) {
6893
- try {
6894
- const reports = await _internals7.readPriorDriftReports(directory);
6895
- return reports.slice(-MAX_DRIFT_REPORTS).map((report) => JSON.stringify(report, null, 2));
6896
- } catch {
6897
- return [];
6898
- }
6899
- }
6900
- function collectPendingProposals(directory) {
6901
- const results = [];
6902
- const insightPath = path12.join(directory, ".swarm", "insight-candidates.jsonl");
6903
- if (existsSync7(insightPath)) {
6904
- try {
6905
- results.push({
6906
- source: "insight-candidates",
6907
- content: readFileSync4(insightPath, "utf-8")
6908
- });
6909
- } catch {}
6910
- }
6911
- const proposalsDir = path12.join(directory, ".swarm", "skills", "proposals");
6912
- try {
6913
- if (existsSync7(proposalsDir)) {
6914
- const proposalFiles = readdirSync(proposalsDir).filter((e) => e.endsWith(".md") || e.endsWith(".json")).slice(0, MAX_PROPOSALS);
6915
- for (const entry of proposalFiles) {
6916
- try {
6917
- results.push({
6918
- source: `proposals/${entry}`,
6919
- content: readFileSync4(path12.join(proposalsDir, entry), "utf-8")
6920
- });
6921
- } catch {}
6922
- }
6923
- }
6924
- } catch {}
6925
- return results;
6926
- }
6927
- function isReportValid(reportPath) {
6928
- try {
6929
- if (!existsSync7(reportPath))
6930
- return false;
6931
- const content = readFileSync4(reportPath, "utf-8").trim();
6932
- if (content.length === 0)
6933
- return false;
6934
- if (!content.startsWith("# Post-Mortem Report:"))
6935
- return false;
6936
- return true;
6937
- } catch {
6938
- return false;
6939
- }
6940
- }
6941
- async function acquirePostMortemLock(directory, planId) {
6942
- const result = await tryAcquireLock(directory, `post-mortem-${planId}.lock`, "curator-postmortem", planId);
6943
- if (result.acquired) {
6944
- return { acquired: true, release: result.lock?._release };
6945
- }
6946
- return { acquired: false };
6947
- }
6948
- function buildDataOnlyReport(planId, planSummary, knowledgeSummary, curatorDigest, proposals, unactionable, retrospectives, driftReports) {
6949
- const now = new Date().toISOString();
6950
- const lines = [];
6951
- lines.push(`# Post-Mortem Report: ${planId}`);
6952
- lines.push(`Generated: ${now}`);
6953
- lines.push("");
6954
- lines.push("## Project Summary");
6955
- lines.push(planSummary);
6956
- lines.push("");
6957
- lines.push("## Knowledge Metrics");
6958
- const totalEntries = knowledgeSummary.length;
6959
- const totalApplied = knowledgeSummary.reduce((s, e) => s + e.applied, 0);
6960
- const totalViolated = knowledgeSummary.reduce((s, e) => s + e.violated, 0);
6961
- const totalIgnored = knowledgeSummary.reduce((s, e) => s + e.ignored, 0);
6962
- const neverApplied = knowledgeSummary.filter((e) => e.applied === 0 && e.violated === 0 && e.ignored === 0);
6963
- lines.push(`- Total entries: ${totalEntries}`);
6964
- lines.push(`- Application events: ${totalApplied} applied, ${totalViolated} violated, ${totalIgnored} ignored`);
6965
- lines.push(`- Never-applied entries: ${neverApplied.length}`);
6966
- if (totalApplied + totalViolated > 0) {
6967
- const appRate = (totalApplied / (totalApplied + totalViolated) * 100).toFixed(1);
6968
- lines.push(`- Application rate: ${appRate}%`);
6969
- }
6970
- lines.push("");
6971
- if (neverApplied.length > 0) {
6972
- lines.push("### Never-Applied Entries (review for retirement)");
6973
- for (const e of neverApplied.slice(0, 10)) {
6974
- lines.push(`- \`${e.id}\` (confidence: ${e.confidence.toFixed(2)}): ${e.lesson.slice(0, 80)}`);
6975
- }
6976
- if (neverApplied.length > 10) {
6977
- lines.push(`- ... and ${neverApplied.length - 10} more`);
6978
- }
6979
- lines.push("");
6980
- }
6981
- const highViolation = knowledgeSummary.filter((e) => e.violated > 0).sort((a, b) => b.violated - a.violated).slice(0, 5);
6982
- if (highViolation.length > 0) {
6983
- lines.push("### High-Violation Entries");
6984
- for (const e of highViolation) {
6985
- lines.push(`- \`${e.id}\` \u2014 ${e.violated} violations, ${e.applied} applied: ${e.lesson.slice(0, 80)}`);
6986
- }
6987
- lines.push("");
6988
- }
6989
- lines.push("## Queue Status");
6990
- lines.push(`- Pending proposals: ${proposals.length}`);
6991
- lines.push(`- Unactionable quarantine: ${unactionable.length}`);
6992
- for (const p of proposals) {
6993
- lines.push(` - ${p.source}`);
6994
- }
6995
- lines.push("");
6996
- if (retrospectives.length > 0) {
6997
- lines.push("## Retrospectives");
6998
- lines.push(`${retrospectives.length} phase retrospective(s) recorded.`);
6999
- lines.push("");
7000
- }
7001
- if (driftReports.length > 0) {
7002
- lines.push("## Drift Reports");
7003
- for (const dr of driftReports) {
7004
- try {
7005
- const parsed = JSON.parse(dr);
7006
- lines.push(`- Phase ${parsed.phase}: ${parsed.alignment} (score: ${parsed.drift_score})`);
7007
- } catch {
7008
- lines.push("- (unparseable drift report)");
7009
- }
7010
- }
7011
- lines.push("");
7012
- }
7013
- if (curatorDigest) {
7014
- lines.push("## Curator Digest Summary");
7015
- const trimmed = curatorDigest.length > 1000 ? `${curatorDigest.slice(0, 1000)}...` : curatorDigest;
7016
- lines.push(trimmed);
7017
- lines.push("");
7018
- }
7019
- return lines.join(`
7020
- `);
7021
- }
7022
- function assembleLLMInput(planId, scope, sessionID, planSummary, knowledgeSummary, curatorDigest, proposals, unactionable, retrospectives, driftReports) {
7023
- const sections = [];
7024
- sections.push(`TASK: CURATOR_POSTMORTEM ${planId}`);
7025
- sections.push(`SCOPE: ${scope}${sessionID ? ` (${sessionID})` : ""}`);
7026
- sections.push(`PLAN_SUMMARY: ${planSummary}`);
7027
- sections.push(`CURATOR_DIGESTS: ${curatorDigest ?? "none"}`);
7028
- const eventsSummary = knowledgeSummary.map((e) => `${e.id}: applied=${e.applied} violated=${e.violated} ignored=${e.ignored} confidence=${e.confidence.toFixed(2)} status=${e.status}`).join(`
7029
- `);
7030
- sections.push(`KNOWLEDGE_EVENTS_SUMMARY:
7031
- ${eventsSummary || "none"}`);
7032
- const knEntries = knowledgeSummary.map((e) => JSON.stringify({
7033
- id: e.id,
7034
- lesson: e.lesson.slice(0, MAX_INPUT_TEXT_CHARS)
7035
- })).join(`
7036
- `);
7037
- sections.push(`KNOWLEDGE_ENTRIES:
7038
- ${knEntries || "[]"}`);
7039
- const proposalText = proposals.length > 0 ? proposals.map((p) => `[${p.source}]
7040
- ${p.content.slice(0, MAX_INPUT_TEXT_CHARS)}`).join(`
7041
- ---
7042
- `) : "none";
7043
- sections.push(`PENDING_PROPOSALS:
7044
- ${proposalText}`);
7045
- sections.push(`UNACTIONABLE_QUARANTINE: ${unactionable.length} entries`);
7046
- const retroText = retrospectives.length > 0 ? retrospectives.map((r) => r.slice(0, MAX_INPUT_TEXT_CHARS)).join(`
7047
- ---
7048
- `) : "none";
7049
- sections.push(`RETROSPECTIVES:
7050
- ${retroText}`);
7051
- if (driftReports.length > 0) {
7052
- const driftText = driftReports.map((r) => r.slice(0, MAX_INPUT_TEXT_CHARS)).join(`
7053
- ---
7054
- `);
7055
- sections.push(`DRIFT_REPORTS:
7056
- ${driftText}`);
7057
- }
7058
- return sections.join(`
7059
-
7060
- `);
7061
- }
7062
- async function runCuratorPostMortem(directory, options = {}) {
7063
- const warnings = [];
7064
- const scope = options.scope ?? "project";
7065
- let planId = "unknown";
7066
- let planSummary = "Plan data unavailable.";
7067
- try {
7068
- const plan = await loadPlanJsonOnly(directory);
7069
- if (plan) {
7070
- planId = derivePlanId(plan);
7071
- const phaseCount = plan.phases?.length ?? 0;
7072
- const completedPhases = plan.phases?.filter((p) => p.status === "complete").length ?? 0;
7073
- planSummary = `Plan "${plan.title}" (${plan.swarm}): ${completedPhases}/${phaseCount} phases complete.`;
7074
- } else {
7075
- warnings.push("Plan not found \u2014 using fallback plan ID.");
7076
- }
7077
- } catch {
7078
- warnings.push("Failed to load plan data.");
7079
- }
7080
- const effectivePlanId = planId;
7081
- const reportFilename = `post-mortem-${effectivePlanId}.md`;
7082
- let reportPath;
7083
- try {
7084
- reportPath = validateSwarmPath(directory, reportFilename);
7085
- } catch {
7086
- return {
7087
- success: false,
7088
- planId,
7089
- reportPath: null,
7090
- summary: null,
7091
- warnings: [...warnings, "Invalid report path."]
7092
- };
7093
- }
7094
- if (!options.force && isReportValid(reportPath)) {
7095
- return {
7096
- success: true,
7097
- planId: effectivePlanId,
7098
- reportPath,
7099
- summary: "Post-mortem report already exists (idempotent skip).",
7100
- warnings
7101
- };
7102
- }
7103
- const lock = await _internals7.acquirePostMortemLock(directory, effectivePlanId);
7104
- if (!lock.acquired) {
7105
- return {
7106
- success: false,
7107
- planId: effectivePlanId,
7108
- reportPath,
7109
- summary: null,
7110
- warnings: [
7111
- ...warnings,
7112
- `Concurrent post-mortem run in progress for plan ${effectivePlanId}; skipped.`
7113
- ]
7114
- };
7115
- }
7116
- try {
7117
- let knowledgeSummary = [];
7118
- try {
7119
- knowledgeSummary = await collectKnowledgeSummary(directory, scope, options.sessionID);
7120
- } catch {
7121
- warnings.push("Failed to collect knowledge summary.");
7122
- }
7123
- if (knowledgeSummary.length > MAX_KNOWLEDGE_ENTRIES) {
7124
- warnings.push(`Knowledge entries capped at ${MAX_KNOWLEDGE_ENTRIES} (had ${knowledgeSummary.length}); older entries truncated.`);
7125
- knowledgeSummary = knowledgeSummary.slice(0, MAX_KNOWLEDGE_ENTRIES);
7126
- }
7127
- let curatorDigest = null;
7128
- try {
7129
- const raw = await readSwarmFileAsync(directory, "curator-summary.json");
7130
- if (raw) {
7131
- const parsed = JSON.parse(raw);
7132
- curatorDigest = parsed.digest ?? null;
7133
- }
7134
- } catch {
7135
- warnings.push("Failed to read curator digest.");
7136
- }
7137
- let proposals = collectPendingProposals(directory);
7138
- if (proposals.length > MAX_PROPOSALS) {
7139
- warnings.push(`Pending proposals capped at ${MAX_PROPOSALS} (had ${proposals.length}); older entries truncated.`);
7140
- proposals = proposals.slice(0, MAX_PROPOSALS);
7141
- }
7142
- const unactionablePath = path12.join(resolveKnowledgeStoreDir(directory), "knowledge-unactionable.jsonl");
7143
- let unactionable = readJsonlFile(unactionablePath, MAX_UNACTIONABLE);
7144
- if (unactionable.length > MAX_UNACTIONABLE) {
7145
- warnings.push(`Unactionable entries capped at ${MAX_UNACTIONABLE} (had ${unactionable.length}); older entries truncated.`);
7146
- unactionable = unactionable.slice(0, MAX_UNACTIONABLE);
7147
- }
7148
- let retrospectives = collectRetrospectives(directory);
7149
- if (retrospectives.length > MAX_RETROSPECTIVES) {
7150
- warnings.push(`Retrospectives capped at ${MAX_RETROSPECTIVES} (had ${retrospectives.length}); older entries truncated.`);
7151
- retrospectives = retrospectives.slice(0, MAX_RETROSPECTIVES);
7152
- }
7153
- let driftReports = await collectDriftReports(directory);
7154
- if (driftReports.length > MAX_DRIFT_REPORTS) {
7155
- warnings.push(`Drift reports capped at ${MAX_DRIFT_REPORTS} (had ${driftReports.length}); older entries truncated.`);
7156
- driftReports = driftReports.slice(0, MAX_DRIFT_REPORTS);
7157
- }
7158
- let reportContent;
7159
- let llmSummary = null;
7160
- let actionResult;
7161
- if (options.llmDelegate) {
7162
- try {
7163
- const { CURATOR_POSTMORTEM_PROMPT } = await import("./explorer-ksr3xq7n.js");
7164
- const userInput = assembleLLMInput(effectivePlanId, scope, options.sessionID, planSummary, knowledgeSummary, curatorDigest, proposals, unactionable, retrospectives, driftReports);
7165
- const ac = new AbortController;
7166
- const timer = setTimeout(() => ac.abort(), options.llmTimeoutMs ?? 300000);
7167
- let llmOutput;
7168
- try {
7169
- const delegatePromise = options.llmDelegate(CURATOR_POSTMORTEM_PROMPT, userInput, ac.signal);
7170
- delegatePromise.catch(() => {});
7171
- llmOutput = await Promise.race([
7172
- delegatePromise,
7173
- new Promise((_, reject) => {
7174
- ac.signal.addEventListener("abort", () => reject(new Error("CURATOR_LLM_TIMEOUT")));
7175
- })
7176
- ]);
7177
- } finally {
7178
- clearTimeout(timer);
7179
- }
7180
- let parsedActions = _internals7.parsePostMortemActions(llmOutput);
7181
- if (parsedActions.diagnostics.length > 0) {
7182
- warnings.push(`Post-mortem structured action parse diagnostics: ${parsedActions.diagnostics.join("; ")}`);
7183
- const repaired = await _internals7.repairPostMortemActions(llmOutput, parsedActions.diagnostics, options);
7184
- if (repaired) {
7185
- parsedActions = repaired;
7186
- warnings.push("Post-mortem structured actions repaired by LLM.");
7187
- }
7188
- }
7189
- llmSummary = parsedActions.summary;
7190
- const executed = await _internals7.executePostMortemActions(directory, parsedActions, options);
7191
- actionResult = executed.result;
7192
- warnings.push(...executed.warnings);
7193
- const actionSummary = [
7194
- "## Executed Post-Mortem Actions",
7195
- `- Knowledge actions: ${actionResult.knowledge_applied} applied, ${actionResult.knowledge_skipped} skipped`,
7196
- `- Hive actions: ${actionResult.hive_promotions} new promotions, ${actionResult.hive_encounters_incremented} encounters incremented, ${actionResult.hive_advancements} advancements`,
7197
- `- Proposal actions: ${actionResult.proposals_approved} approved, ${actionResult.proposals_rejected} rejected, ${actionResult.proposals_skipped} skipped`
7198
- ].join(`
7199
- `);
7200
- reportContent = `# Post-Mortem Report: ${effectivePlanId}
7201
- Generated: ${new Date().toISOString()}
7202
- Scope: ${scope}
7203
-
7204
- ${llmOutput}
7205
-
7206
- ${actionSummary}`;
7207
- } catch (err) {
7208
- const msg = err instanceof Error ? err.message : String(err);
7209
- warnings.push(`LLM delegate failed, falling back to data-only report: ${msg}`);
7210
- reportContent = _internals7.buildDataOnlyReport(effectivePlanId, planSummary, knowledgeSummary, curatorDigest, proposals, unactionable, retrospectives, driftReports);
7211
- }
7212
- } else {
7213
- reportContent = _internals7.buildDataOnlyReport(effectivePlanId, planSummary, knowledgeSummary, curatorDigest, proposals, unactionable, retrospectives, driftReports);
7214
- }
7215
- try {
7216
- const { mkdirSync: mkdirSync6 } = await import("fs");
7217
- mkdirSync6(path12.dirname(reportPath), { recursive: true });
7218
- await atomicWriteFile(reportPath, reportContent);
7219
- } catch (err) {
7220
- const msg = err instanceof Error ? err.message : String(err);
7221
- return {
7222
- success: false,
7223
- planId: effectivePlanId,
7224
- reportPath: null,
7225
- summary: null,
7226
- warnings: [...warnings, `Failed to write report: ${msg}`]
7227
- };
7228
- }
7229
- const totalEntries = knowledgeSummary.length;
7230
- const neverAppliedCount = knowledgeSummary.filter((e) => e.applied === 0 && e.violated === 0 && e.ignored === 0).length;
7231
- const totalViolations = knowledgeSummary.reduce((s, e) => s + e.violated, 0);
7232
- const mechanicalSummary = [
7233
- `Post-mortem for plan "${effectivePlanId}": ${totalEntries} knowledge entries reviewed.`,
7234
- `${neverAppliedCount} never-applied entries flagged; ${totalViolations} total violations recorded.`,
7235
- `${proposals.length} pending proposals, ${unactionable.length} quarantined entries.`
7236
- ].join(" ");
7237
- const summary = llmSummary ?? mechanicalSummary;
7238
- return {
7239
- success: true,
7240
- planId: effectivePlanId,
7241
- reportPath,
7242
- summary,
7243
- warnings,
7244
- actions: actionResult
7245
- };
7246
- } finally {
7247
- if (lock.release) {
7248
- try {
7249
- await lock.release();
7250
- } catch {}
7251
- }
7252
- }
7253
- }
7254
- var _internals7 = {
7255
- acquirePostMortemLock,
7256
- collectKnowledgeSummary,
7257
- collectRetrospectives,
7258
- collectDriftReports,
7259
- collectPendingProposals,
7260
- readJsonlFile,
7261
- buildDataOnlyReport,
7262
- assembleLLMInput,
7263
- isReportValid,
7264
- parsePostMortemActions,
7265
- executePostMortemActions,
7266
- repairPostMortemActions,
7267
- loadDefaultKnowledgeConfig: async () => {
7268
- const { KnowledgeConfigSchema: KnowledgeConfigSchema2 } = await import("./schema-5vmb5dh8.js");
7269
- return KnowledgeConfigSchema2.parse({});
7270
- },
7271
- applyCuratorKnowledgeUpdates: async (directory, recommendations, knowledgeConfig) => {
7272
- const { applyCuratorKnowledgeUpdates } = await import("./curator-zn1kmwmz.js");
7273
- return applyCuratorKnowledgeUpdates(directory, recommendations, knowledgeConfig);
7274
- },
7275
- checkHivePromotions: async (entries, knowledgeConfig) => {
7276
- const { checkHivePromotions } = await import("./hive-promoter-j81yj97d.js");
7277
- return checkHivePromotions(entries, knowledgeConfig);
7278
- },
7279
- applyProposalTriage: async (directory, triage) => {
7280
- const {
7281
- _internals: skillInternals,
7282
- activateProposal,
7283
- listSkills: listSkills2,
7284
- sanitizeSlug
7285
- } = await import("./skill-generator-mjfqvnn0.js");
7286
- const result = {
7287
- approved: [],
7288
- rejected: [],
7289
- skipped: []
7290
- };
7291
- const skills = await listSkills2(directory);
7292
- const proposalSlugs = new Set(skills.proposals.map((p) => p.slug));
7293
- for (const item of triage) {
7294
- const slug = sanitizeSlug(normalizeProposalSlug(item.proposal_id));
7295
- if (!slug || !proposalSlugs.has(slug)) {
7296
- result.skipped.push(slug || item.proposal_id);
7297
- continue;
7298
- }
7299
- if (item.action === "apply") {
7300
- const activation = await activateProposal(directory, slug, false, {
7301
- evaluate: true,
7302
- operation: "post_mortem_queue_triage",
7303
- confirmUnevaluated: true
7304
- });
7305
- if (activation.activated) {
7306
- result.approved.push(slug);
7307
- } else {
7308
- result.skipped.push(slug);
7309
- }
7310
- continue;
7311
- }
7312
- const proposal = skills.proposals.find((p) => p.slug === slug);
7313
- if (!proposal) {
7314
- result.skipped.push(slug);
7315
- continue;
7316
- }
7317
- try {
7318
- skillInternals.unlinkSync(proposal.path);
7319
- result.rejected.push(slug);
7320
- } catch {
7321
- result.skipped.push(slug);
7322
- }
7323
- }
7324
- return result;
7325
- },
7326
- readPriorDriftReports: async (directory) => {
7327
- const { readPriorDriftReports } = await import("./curator-drift-169wgaxn.js");
7328
- return readPriorDriftReports(directory);
7329
- },
7330
- readSwarmKnowledge: (directory) => readKnowledge(resolveSwarmKnowledgePath(directory))
7331
- };
7332
-
7333
- // src/hooks/hive-promoter.ts
7334
- import path17 from "path";
6574
+ import { existsSync as existsSync10, readdirSync as readdirSync2, readFileSync as readFileSync6 } from "fs";
6575
+ import * as path16 from "path";
7335
6576
 
7336
6577
  // src/hooks/curator.ts
7337
6578
  import { randomUUID as randomUUID5 } from "crypto";
7338
6579
  import * as fs5 from "fs";
7339
- import * as path16 from "path";
6580
+ import * as path15 from "path";
7340
6581
 
7341
6582
  // src/services/learning-metrics.ts
7342
6583
  var SEVEN_DAYS_MS = 7 * 24 * 60 * 60 * 1000;
@@ -7749,9 +6990,9 @@ import { readFile as readFile5, rename as rename4, writeFile as writeFile5 } fro
7749
6990
 
7750
6991
  // src/services/skill-improver-quota.ts
7751
6992
  var import_proper_lockfile = __toESM(require_proper_lockfile(), 1);
7752
- import { existsSync as existsSync8 } from "fs";
6993
+ import { existsSync as existsSync7 } from "fs";
7753
6994
  import { mkdir as mkdir4, readFile as readFile4, rename as rename3, writeFile as writeFile4 } from "fs/promises";
7754
- import * as path13 from "path";
6995
+ import * as path12 from "path";
7755
6996
  var LOCK_ACQUIRE_TIMEOUT_MS = 1e4;
7756
6997
  var LOCK_RETRY_OPTS = {
7757
6998
  retries: {
@@ -7780,7 +7021,7 @@ async function acquireLock(dir) {
7780
7021
  }
7781
7022
  function resolveQuotaPath(directory, scope = "skill-improver") {
7782
7023
  const fileName = scope === "knowledge-enrichment" ? "knowledge-enrichment-quota.json" : "skill-improver-quota.json";
7783
- return path13.join(directory, ".swarm", fileName);
7024
+ return path12.join(directory, ".swarm", fileName);
7784
7025
  }
7785
7026
  function todayKey(window, now = new Date) {
7786
7027
  if (window === "utc") {
@@ -7792,7 +7033,7 @@ function todayKey(window, now = new Date) {
7792
7033
  return `${yr}-${m}-${d}`;
7793
7034
  }
7794
7035
  async function readState(filePath) {
7795
- if (!existsSync8(filePath))
7036
+ if (!existsSync7(filePath))
7796
7037
  return null;
7797
7038
  try {
7798
7039
  const raw = await readFile4(filePath, "utf-8");
@@ -7806,7 +7047,7 @@ async function readState(filePath) {
7806
7047
  }
7807
7048
  }
7808
7049
  async function writeState(filePath, state) {
7809
- await mkdir4(path13.dirname(filePath), { recursive: true });
7050
+ await mkdir4(path12.dirname(filePath), { recursive: true });
7810
7051
  const tmp = `${filePath}.tmp-${process.pid}`;
7811
7052
  await writeFile4(tmp, JSON.stringify(state, null, 2), "utf-8");
7812
7053
  await rename3(tmp, filePath);
@@ -7829,10 +7070,10 @@ async function getQuotaState(directory, opts) {
7829
7070
  }
7830
7071
  async function reserveQuota(directory, opts) {
7831
7072
  const filePath = resolveQuotaPath(directory, opts.scope);
7832
- await mkdir4(path13.dirname(filePath), { recursive: true });
7073
+ await mkdir4(path12.dirname(filePath), { recursive: true });
7833
7074
  let release = null;
7834
7075
  try {
7835
- release = await acquireLock(path13.dirname(filePath));
7076
+ release = await acquireLock(path12.dirname(filePath));
7836
7077
  const state = await getQuotaState(directory, opts);
7837
7078
  if (state.calls_used + opts.nCalls > opts.maxCalls) {
7838
7079
  return {
@@ -7859,10 +7100,10 @@ async function reserveQuota(directory, opts) {
7859
7100
  }
7860
7101
  async function releaseQuota(directory, opts) {
7861
7102
  const filePath = resolveQuotaPath(directory, opts.scope);
7862
- await mkdir4(path13.dirname(filePath), { recursive: true });
7103
+ await mkdir4(path12.dirname(filePath), { recursive: true });
7863
7104
  let release = null;
7864
7105
  try {
7865
- release = await acquireLock(path13.dirname(filePath));
7106
+ release = await acquireLock(path12.dirname(filePath));
7866
7107
  const state = await getQuotaState(directory, opts);
7867
7108
  const next = {
7868
7109
  ...state,
@@ -8013,7 +7254,7 @@ async function reviseSkill(params) {
8013
7254
  }
8014
7255
  if (!params.delegate) {
8015
7256
  try {
8016
- const revised = _internals8.buildDeterministicRevision(params.currentContent, params.currentVersion, params.violationContexts);
7257
+ const revised = _internals7.buildDeterministicRevision(params.currentContent, params.currentVersion, params.violationContexts);
8017
7258
  const validation = await validateRevisionCandidate(params, revised, "skill_reviser:deterministic");
8018
7259
  if (!validation.passed) {
8019
7260
  return {
@@ -8140,7 +7381,7 @@ async function reviseSkill(params) {
8140
7381
  };
8141
7382
  }
8142
7383
  }
8143
- var _internals8 = {
7384
+ var _internals7 = {
8144
7385
  reviseSkill,
8145
7386
  getSkillVersion,
8146
7387
  buildDeterministicRevision,
@@ -8155,7 +7396,7 @@ var _internals8 = {
8155
7396
  init_logger();
8156
7397
 
8157
7398
  // src/hooks/skill-invalidator.ts
8158
- import * as path14 from "path";
7399
+ import * as path13 from "path";
8159
7400
  init_logger();
8160
7401
  async function writeArchiveTombstoneAndInvalidateSkills(ctx) {
8161
7402
  const label = ctx.sourceLabel ?? "skill-invalidator";
@@ -8189,7 +7430,7 @@ async function writeArchiveTombstoneAndInvalidateSkills(ctx) {
8189
7430
  let retiredCount = 0;
8190
7431
  let staleCount = 0;
8191
7432
  for (const skillDir of allSkillDirs) {
8192
- const slug = path14.basename(skillDir);
7433
+ const slug = path13.basename(skillDir);
8193
7434
  if (slugSet.has(slug))
8194
7435
  continue;
8195
7436
  slugSet.add(slug);
@@ -8216,14 +7457,14 @@ async function writeArchiveTombstoneAndInvalidateSkills(ctx) {
8216
7457
  // src/hooks/skill-usage-log.ts
8217
7458
  import * as crypto2 from "crypto";
8218
7459
  import * as fs4 from "fs";
8219
- import * as path15 from "path";
7460
+ import * as path14 from "path";
8220
7461
  function resolveLogPath(directory) {
8221
7462
  return validateSwarmPath(directory, "skill-usage.jsonl");
8222
7463
  }
8223
7464
  function normalizeComplianceVerdict(verdict) {
8224
7465
  return verdict === "violation" ? "violated" : verdict;
8225
7466
  }
8226
- var _internals9 = {
7467
+ var _internals8 = {
8227
7468
  generateId: () => crypto2.randomUUID(),
8228
7469
  appendFileSync: fs4.appendFileSync.bind(fs4),
8229
7470
  readFileSync: fs4.readFileSync.bind(fs4),
@@ -8293,9 +7534,9 @@ function parseFeedbackMarker(raw) {
8293
7534
  function readFeedbackAppliedEntryIds(directory) {
8294
7535
  const resolved = resolveLogPath(directory);
8295
7536
  const processed = new Set;
8296
- if (!_internals9.existsSync(resolved))
7537
+ if (!_internals8.existsSync(resolved))
8297
7538
  return processed;
8298
- const raw = _internals9.readFileSync(resolved, "utf-8");
7539
+ const raw = _internals8.readFileSync(resolved, "utf-8");
8299
7540
  for (const line of raw.split(`
8300
7541
  `)) {
8301
7542
  const trimmed = line.trim();
@@ -8315,16 +7556,16 @@ function appendFeedbackAppliedMarker(directory, processedEntryIds) {
8315
7556
  if (processedEntryIds.length === 0)
8316
7557
  return;
8317
7558
  const resolved = resolveLogPath(directory);
8318
- const dir = path15.dirname(resolved);
8319
- if (!_internals9.existsSync(dir)) {
8320
- _internals9.mkdirSync(dir, { recursive: true });
7559
+ const dir = path14.dirname(resolved);
7560
+ if (!_internals8.existsSync(dir)) {
7561
+ _internals8.mkdirSync(dir, { recursive: true });
8321
7562
  }
8322
7563
  const marker = {
8323
7564
  type: "feedback_applied",
8324
7565
  timestamp: new Date().toISOString(),
8325
7566
  processedEntryIds: [...new Set(processedEntryIds)]
8326
7567
  };
8327
- _internals9.appendFileSync(resolved, `${JSON.stringify(marker)}
7568
+ _internals8.appendFileSync(resolved, `${JSON.stringify(marker)}
8328
7569
  `, "utf-8");
8329
7570
  }
8330
7571
  function appendSkillUsageEntry(directory, entry) {
@@ -8360,12 +7601,12 @@ function appendSkillUsageEntry(directory, entry) {
8360
7601
  throw new Error("sessionID is required and must be a non-empty string");
8361
7602
  }
8362
7603
  const resolved = validateSwarmPath(directory, "skill-usage.jsonl");
8363
- const dir = path15.dirname(resolved);
8364
- if (!_internals9.existsSync(dir)) {
8365
- _internals9.mkdirSync(dir, { recursive: true });
7604
+ const dir = path14.dirname(resolved);
7605
+ if (!_internals8.existsSync(dir)) {
7606
+ _internals8.mkdirSync(dir, { recursive: true });
8366
7607
  }
8367
7608
  const fullEntry = {
8368
- id: _internals9.generateId(),
7609
+ id: _internals8.generateId(),
8369
7610
  skillPath,
8370
7611
  agentName,
8371
7612
  taskID,
@@ -8375,21 +7616,21 @@ function appendSkillUsageEntry(directory, entry) {
8375
7616
  ...reviewerNotes !== undefined && { reviewerNotes },
8376
7617
  ...skillVersion !== undefined && { skillVersion }
8377
7618
  };
8378
- _internals9.appendFileSync(resolved, `${JSON.stringify(fullEntry)}
7619
+ _internals8.appendFileSync(resolved, `${JSON.stringify(fullEntry)}
8379
7620
  `, "utf-8");
8380
7621
  try {
8381
- const stat3 = _internals9.statSync(resolved);
7622
+ const stat3 = _internals8.statSync(resolved);
8382
7623
  if (stat3.size > SKILL_USAGE_LOG_ROTATE_BYTES) {
8383
- _internals9.pruneSkillUsageLog(directory, SKILL_USAGE_LOG_MAX_ENTRIES_PER_SKILL);
7624
+ _internals8.pruneSkillUsageLog(directory, SKILL_USAGE_LOG_MAX_ENTRIES_PER_SKILL);
8384
7625
  }
8385
7626
  } catch {}
8386
7627
  }
8387
7628
  function readSkillUsageEntries(directory, options) {
8388
7629
  const resolved = resolveLogPath(directory);
8389
- if (!_internals9.existsSync(resolved)) {
7630
+ if (!_internals8.existsSync(resolved)) {
8390
7631
  return [];
8391
7632
  }
8392
- const raw = _internals9.readFileSync(resolved, "utf-8");
7633
+ const raw = _internals8.readFileSync(resolved, "utf-8");
8393
7634
  const entries = [];
8394
7635
  for (const line of raw.split(`
8395
7636
  `)) {
@@ -8432,20 +7673,20 @@ var SKILL_USAGE_LOG_ROTATE_BYTES = 1024 * 1024;
8432
7673
  var SKILL_USAGE_LOG_MAX_ENTRIES_PER_SKILL = 500;
8433
7674
  function readSkillUsageEntriesTail(directory, filters, maxBytes = TAIL_BYTES_DEFAULT) {
8434
7675
  const logPath = resolveLogPath(directory);
8435
- if (!_internals9.existsSync(logPath))
7676
+ if (!_internals8.existsSync(logPath))
8436
7677
  return [];
8437
7678
  try {
8438
7679
  const normalizedMaxBytes = Number.isFinite(maxBytes) ? maxBytes : TAIL_BYTES_DEFAULT;
8439
7680
  const boundedMaxBytes = Math.min(Math.max(1, normalizedMaxBytes), MAX_TAIL_BYTES);
8440
- const stat3 = _internals9.statSync(logPath);
7681
+ const stat3 = _internals8.statSync(logPath);
8441
7682
  const start = Math.max(0, stat3.size - boundedMaxBytes);
8442
- const fd = _internals9.openSync(logPath, "r");
7683
+ const fd = _internals8.openSync(logPath, "r");
8443
7684
  try {
8444
7685
  const readLen = stat3.size - start;
8445
7686
  if (readLen === 0)
8446
7687
  return [];
8447
7688
  const buf = Buffer.alloc(readLen);
8448
- _internals9.readSync(fd, buf, 0, buf.length, start);
7689
+ _internals8.readSync(fd, buf, 0, buf.length, start);
8449
7690
  const content = buf.toString("utf-8");
8450
7691
  let usable;
8451
7692
  if (start > 0) {
@@ -8472,7 +7713,7 @@ function readSkillUsageEntriesTail(directory, filters, maxBytes = TAIL_BYTES_DEF
8472
7713
  }
8473
7714
  return entries;
8474
7715
  } finally {
8475
- _internals9.closeSync(fd);
7716
+ _internals8.closeSync(fd);
8476
7717
  }
8477
7718
  } catch {
8478
7719
  return [];
@@ -8509,10 +7750,10 @@ function computeComplianceByVersion(entries, skillPath) {
8509
7750
  }
8510
7751
  function pruneSkillUsageLog(directory, maxEntriesPerSkill = 500) {
8511
7752
  const resolved = resolveLogPath(directory);
8512
- if (!_internals9.existsSync(resolved)) {
7753
+ if (!_internals8.existsSync(resolved)) {
8513
7754
  return { pruned: 0, remaining: 0 };
8514
7755
  }
8515
- const raw = _internals9.readFileSync(resolved, "utf-8");
7756
+ const raw = _internals8.readFileSync(resolved, "utf-8");
8516
7757
  const lines = raw.split(`
8517
7758
  `);
8518
7759
  const entries = [];
@@ -8536,19 +7777,19 @@ function pruneSkillUsageLog(directory, maxEntriesPerSkill = 500) {
8536
7777
  }
8537
7778
  if (entries.length === 0) {
8538
7779
  if (preservedMarkers.length > 0) {
8539
- const dir2 = path15.dirname(resolved);
8540
- const tmpPath2 = path15.join(dir2, `skill-usage-${Date.now()}.tmp`);
7780
+ const dir2 = path14.dirname(resolved);
7781
+ const tmpPath2 = path14.join(dir2, `skill-usage-${Date.now()}.tmp`);
8541
7782
  const content2 = preservedMarkers.join(`
8542
7783
  `).concat(preservedMarkers.length > 0 ? `
8543
7784
  ` : "");
8544
7785
  try {
8545
- _internals9.writeFileSync(tmpPath2, content2, "utf-8");
8546
- _internals9.renameSync(tmpPath2, resolved);
7786
+ _internals8.writeFileSync(tmpPath2, content2, "utf-8");
7787
+ _internals8.renameSync(tmpPath2, resolved);
8547
7788
  } catch (writeErr) {
8548
7789
  const msg = writeErr instanceof Error ? writeErr.message : String(writeErr);
8549
7790
  try {
8550
- if (_internals9.existsSync(tmpPath2)) {
8551
- _internals9.writeFileSync(tmpPath2, "", "utf-8");
7791
+ if (_internals8.existsSync(tmpPath2)) {
7792
+ _internals8.writeFileSync(tmpPath2, "", "utf-8");
8552
7793
  }
8553
7794
  } catch {}
8554
7795
  return { pruned: 0, remaining: 0, error: msg };
@@ -8579,21 +7820,21 @@ function pruneSkillUsageLog(directory, maxEntriesPerSkill = 500) {
8579
7820
  if (pruned === 0) {
8580
7821
  return { pruned: 0, remaining: entries.length };
8581
7822
  }
8582
- const dir = path15.dirname(resolved);
8583
- const tmpPath = path15.join(dir, `skill-usage-${Date.now()}.tmp`);
7823
+ const dir = path14.dirname(resolved);
7824
+ const tmpPath = path14.join(dir, `skill-usage-${Date.now()}.tmp`);
8584
7825
  const entryLines = surviving.map((e) => JSON.stringify(e));
8585
7826
  const allLines = [...entryLines, ...preservedMarkers];
8586
7827
  const content = allLines.join(`
8587
7828
  `).concat(`
8588
7829
  `);
8589
7830
  try {
8590
- _internals9.writeFileSync(tmpPath, content, "utf-8");
8591
- _internals9.renameSync(tmpPath, resolved);
7831
+ _internals8.writeFileSync(tmpPath, content, "utf-8");
7832
+ _internals8.renameSync(tmpPath, resolved);
8592
7833
  } catch (writeErr) {
8593
7834
  const msg = writeErr instanceof Error ? writeErr.message : String(writeErr);
8594
7835
  try {
8595
- if (_internals9.existsSync(tmpPath)) {
8596
- _internals9.writeFileSync(tmpPath, "", "utf-8");
7836
+ if (_internals8.existsSync(tmpPath)) {
7837
+ _internals8.writeFileSync(tmpPath, "", "utf-8");
8597
7838
  }
8598
7839
  } catch {}
8599
7840
  return { pruned: 0, remaining: entries.length, error: msg };
@@ -8609,16 +7850,16 @@ async function resolveSourceKnowledgeIds(directory, skillPath) {
8609
7850
  if (/\.\.[/\\]/.test(cleanPath)) {
8610
7851
  return [];
8611
7852
  }
8612
- const absolute = path15.normalize(path15.isAbsolute(cleanPath) ? cleanPath : path15.resolve(directory, cleanPath));
8613
- const baseDir = path15.normalize(path15.resolve(directory));
8614
- const isContained = process.platform === "win32" ? absolute.toLowerCase().startsWith((baseDir + path15.sep).toLowerCase()) : absolute.startsWith(baseDir + path15.sep);
7853
+ const absolute = path14.normalize(path14.isAbsolute(cleanPath) ? cleanPath : path14.resolve(directory, cleanPath));
7854
+ const baseDir = path14.normalize(path14.resolve(directory));
7855
+ const isContained = process.platform === "win32" ? absolute.toLowerCase().startsWith((baseDir + path14.sep).toLowerCase()) : absolute.startsWith(baseDir + path14.sep);
8615
7856
  if (!isContained) {
8616
7857
  return [];
8617
7858
  }
8618
- if (!_internals9.existsSync(absolute)) {
7859
+ if (!_internals8.existsSync(absolute)) {
8619
7860
  return [];
8620
7861
  }
8621
- const content = _internals9.readFileSync(absolute, "utf-8");
7862
+ const content = _internals8.readFileSync(absolute, "utf-8");
8622
7863
  return parseGeneratedFromKnowledge(content);
8623
7864
  } catch (err) {
8624
7865
  console.warn("[skill-usage-log] resolveSourceKnowledgeIds failed (fail-open):", err instanceof Error ? err.message : String(err));
@@ -8726,7 +7967,7 @@ var DEFAULT_CURATOR_LLM_TIMEOUT_MS = 300000;
8726
7967
  var MAX_CURATOR_PHASE_DIGESTS = 50;
8727
7968
  var MAX_CURATOR_COMPLIANCE_OBSERVATIONS = 200;
8728
7969
  var MAX_CURATOR_RECOMMENDATIONS = 200;
8729
- var _internals10 = {
7970
+ var _internals9 = {
8730
7971
  parseKnowledgeRecommendations,
8731
7972
  parseKnowledgeRecommendationsWithDiagnostics,
8732
7973
  readCuratorSummary,
@@ -8747,6 +7988,37 @@ var _internals10 = {
8747
7988
  getSkillVersion,
8748
7989
  readLatestPostMortemDigest: (directory) => readLatestPostMortemDigest(directory)
8749
7990
  };
7991
+ var UUID_V4 = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
7992
+ var HEX_ID_PREFIX = /^[0-9a-f]{8,}$/i;
7993
+ function normalizeRecommendationEntryIdToken(token) {
7994
+ const trimmed = token.trim();
7995
+ if (UUID_V4.test(trimmed) || HEX_ID_PREFIX.test(trimmed))
7996
+ return trimmed;
7997
+ return;
7998
+ }
7999
+ function resolveKnowledgeRecommendationIds(recommendations, entries) {
8000
+ const diagnostics = [];
8001
+ const activeEntries = entries.filter((entry) => isActiveStatus(entry.status));
8002
+ const resolved = recommendations.map((recommendation) => {
8003
+ const entryId = typeof recommendation.entry_id === "string" ? normalizeRecommendationEntryIdToken(recommendation.entry_id) : undefined;
8004
+ if (!entryId)
8005
+ return recommendation;
8006
+ if (activeEntries.some((entry) => entry.id === entryId)) {
8007
+ return { ...recommendation, entry_id: entryId };
8008
+ }
8009
+ const matches = activeEntries.filter((entry) => entry.id.startsWith(entryId));
8010
+ if (matches.length === 1) {
8011
+ return { ...recommendation, entry_id: matches[0].id };
8012
+ }
8013
+ diagnostics.push({
8014
+ section: "RECOMMENDATION_ID",
8015
+ line: entryId,
8016
+ reason: matches.length === 0 ? "entry_id not found" : "entry_id prefix is ambiguous"
8017
+ });
8018
+ return null;
8019
+ }).filter((rec) => rec !== null);
8020
+ return { recommendations: resolved, diagnostics };
8021
+ }
8750
8022
  function capPhaseDigests(digests) {
8751
8023
  return digests.slice(-MAX_CURATOR_PHASE_DIGESTS);
8752
8024
  }
@@ -8765,9 +8037,9 @@ ${digest.summary}`).join(`
8765
8037
  async function autoRetireSkills(directory, _curatorKnowledgePath, excludeSlugs) {
8766
8038
  const observations = [];
8767
8039
  try {
8768
- const skillListResult = await _internals10.listSkills(directory);
8769
- const usageEntries = _internals10.readSkillUsageEntries(directory);
8770
- const allArchivedIds = await _internals10.getArchivedKnowledgeIds(directory);
8040
+ const skillListResult = await _internals9.listSkills(directory);
8041
+ const usageEntries = _internals9.readSkillUsageEntries(directory);
8042
+ const allArchivedIds = await _internals9.getArchivedKnowledgeIds(directory);
8771
8043
  for (const active of skillListResult.active) {
8772
8044
  if (excludeSlugs?.has(active.slug))
8773
8045
  continue;
@@ -8789,7 +8061,7 @@ async function autoRetireSkills(directory, _curatorKnowledgePath, excludeSlugs)
8789
8061
  const violationRate = skillUsage.length > 0 ? violations / skillUsage.length : 0;
8790
8062
  if (violationRate > 0.3) {
8791
8063
  const reason = `auto-retire: violation rate ${(violationRate * 100).toFixed(0)}% exceeds 30% threshold`;
8792
- await _internals10.retireSkill(directory, active.slug, reason);
8064
+ await _internals9.retireSkill(directory, active.slug, reason);
8793
8065
  observations.push(`Skill '${active.slug}' auto-retired: ${reason}`);
8794
8066
  warn(`[curator] ${observations[observations.length - 1]}`);
8795
8067
  continue;
@@ -8797,15 +8069,15 @@ async function autoRetireSkills(directory, _curatorKnowledgePath, excludeSlugs)
8797
8069
  let archivedSourceMatched = false;
8798
8070
  if (allArchivedIds.size > 0) {
8799
8071
  try {
8800
- const content = await _internals10.readFileAsync(active.path, "utf-8");
8801
- const sourceIds = _internals10.parseDraftFrontmatter(content)?.sourceKnowledgeIds ?? [];
8072
+ const content = await _internals9.readFileAsync(active.path, "utf-8");
8073
+ const sourceIds = _internals9.parseDraftFrontmatter(content)?.sourceKnowledgeIds ?? [];
8802
8074
  archivedSourceMatched = sourceIds.some((id) => allArchivedIds.has(id));
8803
8075
  } catch {
8804
8076
  archivedSourceMatched = false;
8805
8077
  }
8806
8078
  }
8807
8079
  if (archivedSourceMatched) {
8808
- const result = await _internals10.retireOrMarkStale(directory, path16.dirname(active.path), allArchivedIds);
8080
+ const result = await _internals9.retireOrMarkStale(directory, path15.dirname(active.path), allArchivedIds);
8809
8081
  if (result.action === "retire") {
8810
8082
  observations.push(`Skill '${active.slug}' auto-retired: all source knowledge entries archived`);
8811
8083
  warn(`[curator] ${observations[observations.length - 1]}`);
@@ -8826,7 +8098,6 @@ function parseKnowledgeRecommendations(llmOutput) {
8826
8098
  function parseKnowledgeRecommendationsWithDiagnostics(llmOutput) {
8827
8099
  const recommendations = [];
8828
8100
  const diagnostics = [];
8829
- const UUID_V4 = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
8830
8101
  const obsSection = llmOutput.match(/OBSERVATIONS:\s*\n([\s\S]*?)(?:\n\n|\n[A-Z_]+:|$)/);
8831
8102
  if (obsSection) {
8832
8103
  const lines = obsSection[1].split(`
@@ -8854,7 +8125,7 @@ function parseKnowledgeRecommendationsWithDiagnostics(llmOutput) {
8854
8125
  const text2 = staleEntry[2].trim().replace(/\s+\([^)]+\)$/, "");
8855
8126
  recommendations.push({
8856
8127
  action: "archive",
8857
- entry_id: UUID_V4.test(uuid2) ? uuid2 : undefined,
8128
+ entry_id: normalizeRecommendationEntryIdToken(uuid2),
8858
8129
  lesson: text2,
8859
8130
  reason: text2
8860
8131
  });
@@ -8870,7 +8141,7 @@ function parseKnowledgeRecommendationsWithDiagnostics(llmOutput) {
8870
8141
  const uuid = match[1];
8871
8142
  const parenthetical = match[2];
8872
8143
  const text = match[3].trim().replace(/\s+\([^)]+\)$/, "");
8873
- const entryId = uuid === "new" || !UUID_V4.test(uuid) ? undefined : uuid;
8144
+ const entryId = uuid === "new" ? undefined : normalizeRecommendationEntryIdToken(uuid);
8874
8145
  let action = "rewrite";
8875
8146
  const lowerParenthetical = parenthetical.toLowerCase();
8876
8147
  if (lowerParenthetical.includes("suggests boost confidence") || lowerParenthetical.includes("mark hive_eligible") || lowerParenthetical.includes("appears high-confidence")) {
@@ -9002,11 +8273,11 @@ function arrayOfStrings(v) {
9002
8273
  }
9003
8274
  function readLatestPostMortemDigest(directory) {
9004
8275
  try {
9005
- const swarmDir = path16.join(directory, ".swarm");
8276
+ const swarmDir = path15.join(directory, ".swarm");
9006
8277
  if (!fs5.existsSync(swarmDir))
9007
8278
  return null;
9008
8279
  const candidates = fs5.readdirSync(swarmDir).filter((name) => /^post-mortem-[^/\\]+\.md$/.test(name)).map((name) => {
9009
- const filePath = path16.join(swarmDir, name);
8280
+ const filePath = path15.join(swarmDir, name);
9010
8281
  return { name, filePath, mtimeMs: fs5.statSync(filePath).mtimeMs };
9011
8282
  }).sort((a, b) => b.mtimeMs - a.mtimeMs);
9012
8283
  const latest = candidates[0];
@@ -9049,7 +8320,7 @@ async function readCuratorSummary(directory) {
9049
8320
  }
9050
8321
  async function writeCuratorSummary(directory, summary) {
9051
8322
  const resolvedPath = validateSwarmPath(directory, "curator-summary.json");
9052
- fs5.mkdirSync(path16.dirname(resolvedPath), { recursive: true });
8323
+ fs5.mkdirSync(path15.dirname(resolvedPath), { recursive: true });
9053
8324
  await bunWrite(resolvedPath, JSON.stringify(summary, null, 2));
9054
8325
  }
9055
8326
  function normalizeAgentName(name) {
@@ -9084,8 +8355,8 @@ function checkPhaseCompliance(phaseEvents, agentsDispatched, requiredAgents, pha
9084
8355
  const observations = [];
9085
8356
  const timestamp = new Date().toISOString();
9086
8357
  for (const agent of requiredAgents) {
9087
- const normalizedAgent = _internals10.normalizeAgentName(agent);
9088
- const isDispatched = agentsDispatched.some((a) => _internals10.normalizeAgentName(a) === normalizedAgent);
8358
+ const normalizedAgent = _internals9.normalizeAgentName(agent);
8359
+ const isDispatched = agentsDispatched.some((a) => _internals9.normalizeAgentName(a) === normalizedAgent);
9089
8360
  if (!isDispatched) {
9090
8361
  observations.push({
9091
8362
  phase,
@@ -9104,7 +8375,7 @@ function checkPhaseCompliance(phaseEvents, agentsDispatched, requiredAgents, pha
9104
8375
  if (e.type === "agent.delegation") {
9105
8376
  const agent = e.agent;
9106
8377
  if (agent && typeof agent === "string") {
9107
- const normalized = _internals10.normalizeAgentName(agent);
8378
+ const normalized = _internals9.normalizeAgentName(agent);
9108
8379
  if (normalized === "coder") {
9109
8380
  coderDelegations.push({ event: e, index: i });
9110
8381
  } else if (normalized === "reviewer") {
@@ -9161,7 +8432,7 @@ function checkPhaseCompliance(phaseEvents, agentsDispatched, requiredAgents, pha
9161
8432
  if (e.type === "agent.delegation" && e.agent) {
9162
8433
  const agent = e.agent;
9163
8434
  if (agent && typeof agent === "string") {
9164
- const normalized = _internals10.normalizeAgentName(agent);
8435
+ const normalized = _internals9.normalizeAgentName(agent);
9165
8436
  if (normalized === "sme") {
9166
8437
  smeDelegations.push({ event: e, index: i });
9167
8438
  }
@@ -9185,7 +8456,7 @@ function checkPhaseCompliance(phaseEvents, agentsDispatched, requiredAgents, pha
9185
8456
  }
9186
8457
  async function runCuratorInit(directory, config, llmDelegate) {
9187
8458
  try {
9188
- const priorSummary = await _internals10.readCuratorSummary(directory);
8459
+ const priorSummary = await _internals9.readCuratorSummary(directory);
9189
8460
  const knowledgePath = resolveSwarmKnowledgePath(directory);
9190
8461
  const allEntries = await readKnowledge(knowledgePath);
9191
8462
  const highConfidenceEntries = allEntries.filter((e) => typeof e.confidence === "number" && e.confidence >= config.min_knowledge_confidence);
@@ -9226,7 +8497,7 @@ async function runCuratorInit(directory, config, llmDelegate) {
9226
8497
  const maxContextChars = config.max_summary_tokens * 2;
9227
8498
  briefingParts.push(contextMd.slice(0, maxContextChars));
9228
8499
  }
9229
- const latestPostMortemDigest = _internals10.readLatestPostMortemDigest(directory);
8500
+ const latestPostMortemDigest = _internals9.readLatestPostMortemDigest(directory);
9230
8501
  if (latestPostMortemDigest) {
9231
8502
  briefingParts.push(`
9232
8503
  ## Latest Post-Mortem`);
@@ -9313,7 +8584,7 @@ Could not load prior session context.`,
9313
8584
  }
9314
8585
  async function runCuratorPhase(directory, phase, agentsDispatched, config, knowledgeConfig, llmDelegate) {
9315
8586
  try {
9316
- const priorSummary = await _internals10.readCuratorSummary(directory);
8587
+ const priorSummary = await _internals9.readCuratorSummary(directory);
9317
8588
  if (priorSummary?.phase_digests.some((d) => d.phase === phase)) {
9318
8589
  const existingDigest = priorSummary.phase_digests.find((d) => d.phase === phase);
9319
8590
  return {
@@ -9326,10 +8597,10 @@ async function runCuratorPhase(directory, phase, agentsDispatched, config, knowl
9326
8597
  };
9327
8598
  }
9328
8599
  const eventsJsonlContent = await readSwarmFileAsync(directory, "events.jsonl");
9329
- const phaseEvents = eventsJsonlContent ? _internals10.filterPhaseEvents(eventsJsonlContent, phase) : [];
8600
+ const phaseEvents = eventsJsonlContent ? _internals9.filterPhaseEvents(eventsJsonlContent, phase) : [];
9330
8601
  const contextMd = await readSwarmFileAsync(directory, "context.md");
9331
8602
  const requiredAgents = ["reviewer", "test_engineer"];
9332
- const complianceObservations = _internals10.checkPhaseCompliance(phaseEvents, agentsDispatched, requiredAgents, phase);
8603
+ const complianceObservations = _internals9.checkPhaseCompliance(phaseEvents, agentsDispatched, requiredAgents, phase);
9333
8604
  const plan = await loadPlanJsonOnly(directory);
9334
8605
  const phaseData = plan?.phases.find((p) => p.id === phase);
9335
8606
  const tasksCompleted = phaseData ? phaseData.tasks.filter((t) => t.status === "completed").length : 0;
@@ -9353,7 +8624,7 @@ async function runCuratorPhase(directory, phase, agentsDispatched, config, knowl
9353
8624
  timestamp: new Date().toISOString(),
9354
8625
  summary: `Phase ${phase} completed. ${tasksCompleted}/${tasksTotal} tasks completed. ${complianceObservations.length} compliance observations.`,
9355
8626
  agents_used: [
9356
- ...new Set(agentsDispatched.map((a) => _internals10.normalizeAgentName(a)))
8627
+ ...new Set(agentsDispatched.map((a) => _internals9.normalizeAgentName(a)))
9357
8628
  ],
9358
8629
  tasks_completed: tasksCompleted,
9359
8630
  tasks_total: tasksTotal,
@@ -9405,24 +8676,22 @@ async function runCuratorPhase(directory, phase, agentsDispatched, config, knowl
9405
8676
  clearTimeout(timer);
9406
8677
  }
9407
8678
  if (llmOutput?.trim()) {
9408
- const parsed = _internals10.parseKnowledgeRecommendationsWithDiagnostics(llmOutput);
8679
+ const parsed = _internals9.parseKnowledgeRecommendationsWithDiagnostics(llmOutput);
9409
8680
  for (const diagnostic of parsed.diagnostics) {
9410
8681
  warn("[curator] skipped malformed recommendation line", {
9411
8682
  phase,
9412
8683
  ...diagnostic
9413
8684
  });
9414
8685
  }
9415
- knowledgeRecommendations = parsed.recommendations.filter((recommendation) => {
9416
- if (!recommendation.entry_id || knownKnowledgeIds.has(recommendation.entry_id)) {
9417
- return true;
9418
- }
8686
+ const resolvedRecommendations = resolveKnowledgeRecommendationIds(parsed.recommendations, allKnowledgeEntries);
8687
+ for (const diagnostic of resolvedRecommendations.diagnostics) {
9419
8688
  warn("[curator] skipped recommendation for unknown knowledge entry", {
9420
8689
  phase,
9421
- entry_id: recommendation.entry_id,
9422
- action: recommendation.action
8690
+ entry_id: diagnostic.line,
8691
+ reason: diagnostic.reason
9423
8692
  });
9424
- return false;
9425
- });
8693
+ }
8694
+ knowledgeRecommendations = resolvedRecommendations.recommendations.filter((recommendation) => !recommendation.entry_id || knownKnowledgeIds.has(recommendation.entry_id));
9426
8695
  const structured = parseStructuredCuratorBlocks(llmOutput);
9427
8696
  for (const diagnostic of structured.diagnostics) {
9428
8697
  warn("[curator] skipped malformed structured block", {
@@ -9486,18 +8755,18 @@ async function runCuratorPhase(directory, phase, agentsDispatched, config, knowl
9486
8755
  knowledge_recommendations: capKnowledgeRecommendations(knowledgeRecommendations)
9487
8756
  };
9488
8757
  }
9489
- await _internals10.writeCuratorSummary(directory, updatedSummary);
8758
+ await _internals9.writeCuratorSummary(directory, updatedSummary);
9490
8759
  if (knowledgeApplicationFindings.length > 0) {
9491
8760
  try {
9492
- const evidenceDir = path16.join(directory, ".swarm", "evidence", String(phase));
8761
+ const evidenceDir = path15.join(directory, ".swarm", "evidence", String(phase));
9493
8762
  fs5.mkdirSync(evidenceDir, { recursive: true });
9494
- const findingsPath = path16.join(evidenceDir, "curator-findings.json");
8763
+ const findingsPath = path15.join(evidenceDir, "curator-findings.json");
9495
8764
  await bunWrite(findingsPath, JSON.stringify({ findings: knowledgeApplicationFindings }, null, 2));
9496
8765
  } catch (err) {
9497
8766
  warn(`[curator] failed to persist application findings: ${err instanceof Error ? err.message : String(err)}`);
9498
8767
  }
9499
8768
  }
9500
- const eventsPath = path16.join(directory, ".swarm", "events.jsonl");
8769
+ const eventsPath = path15.join(directory, ".swarm", "events.jsonl");
9501
8770
  for (const obs of complianceObservations) {
9502
8771
  await appendKnowledge(eventsPath, {
9503
8772
  type: "curator_compliance",
@@ -9510,7 +8779,7 @@ async function runCuratorPhase(directory, phase, agentsDispatched, config, knowl
9510
8779
  }
9511
8780
  if (config.skill_generation_enabled === true && skillCandidates.length > 0) {
9512
8781
  try {
9513
- const skillModule = await import("./skill-generator-mjfqvnn0.js");
8782
+ const skillModule = await import("./skill-generator-5rsjmmfq.js");
9514
8783
  for (const cand of skillCandidates) {
9515
8784
  if (cand.confidence < (config.min_skill_confidence ?? DEFAULT_SKILL_MIN_CONFIDENCE)) {
9516
8785
  continue;
@@ -9528,8 +8797,8 @@ async function runCuratorPhase(directory, phase, agentsDispatched, config, knowl
9528
8797
  }
9529
8798
  const revisedSlugs = new Set;
9530
8799
  try {
9531
- const skillListResult = await _internals10.listSkills(directory);
9532
- const usageEntries = _internals10.readSkillUsageEntries(directory);
8800
+ const skillListResult = await _internals9.listSkills(directory);
8801
+ const usageEntries = _internals9.readSkillUsageEntries(directory);
9533
8802
  let revisionCallsThisPhase = 0;
9534
8803
  for (const active of skillListResult.active) {
9535
8804
  if (revisionCallsThisPhase >= MAX_REVISION_CALLS_PER_PHASE)
@@ -9553,8 +8822,8 @@ async function runCuratorPhase(directory, phase, agentsDispatched, config, knowl
9553
8822
  const violations = skillUsage.filter((e) => e.complianceVerdict === "violated").length;
9554
8823
  const violationRate = violations / skillUsage.length;
9555
8824
  if (violationRate > REVISION_VIOLATION_THRESHOLD && violationRate <= 0.3) {
9556
- const content = await _internals10.readFileAsync(active.path, "utf-8");
9557
- const fm = _internals10.parseDraftFrontmatter(content);
8825
+ const content = await _internals9.readFileAsync(active.path, "utf-8");
8826
+ const fm = _internals9.parseDraftFrontmatter(content);
9558
8827
  if (fm && fm.skillOrigin === "promoted_external")
9559
8828
  continue;
9560
8829
  const currentVersion = fm?.version ?? 1;
@@ -9565,7 +8834,7 @@ async function runCuratorPhase(directory, phase, agentsDispatched, config, knowl
9565
8834
  reviewerNotes: e.reviewerNotes,
9566
8835
  timestamp: e.timestamp
9567
8836
  }));
9568
- const result2 = await _internals10.reviseSkill({
8837
+ const result2 = await _internals9.reviseSkill({
9569
8838
  directory,
9570
8839
  slug: active.slug,
9571
8840
  skillPath: active.path,
@@ -9584,7 +8853,7 @@ async function runCuratorPhase(directory, phase, agentsDispatched, config, knowl
9584
8853
  } catch (revisionErr) {
9585
8854
  warn(`[curator] skill revision check failed: ${revisionErr instanceof Error ? revisionErr.message : String(revisionErr)}`);
9586
8855
  }
9587
- const autoRetireObservations = await _internals10.autoRetireSkills(directory, curatorKnowledgePath, revisedSlugs);
8856
+ const autoRetireObservations = await _internals9.autoRetireSkills(directory, curatorKnowledgePath, revisedSlugs);
9588
8857
  if (autoRetireObservations.length > 0) {
9589
8858
  const retireNote = ` [${autoRetireObservations.length} skill(s) auto-retired]`;
9590
8859
  phaseDigest.summary += retireNote;
@@ -9674,10 +8943,11 @@ async function applyCuratorKnowledgeUpdates(directory, recommendations, knowledg
9674
8943
  if (knowledgeConfig == null) {
9675
8944
  return { applied: 0, skipped: 0 };
9676
8945
  }
9677
- const validRecommendations = recommendations.filter((rec) => rec != null);
8946
+ let validRecommendations = recommendations.filter((rec) => rec != null);
9678
8947
  const knowledgePath = resolveSwarmKnowledgePath(directory);
9679
8948
  const appliedIds = new Set;
9680
8949
  const foundIds = new Set;
8950
+ let idResolutionSkipped = 0;
9681
8951
  const archivedPrevStatus = new Map;
9682
8952
  const contradictedEntries = [];
9683
8953
  await transactKnowledge(knowledgePath, (entries) => {
@@ -9689,6 +8959,12 @@ async function applyCuratorKnowledgeUpdates(directory, recommendations, knowledg
9689
8959
  let modified = false;
9690
8960
  for (const e of entries)
9691
8961
  foundIds.add(e.id);
8962
+ const resolvedRecommendations = resolveKnowledgeRecommendationIds(validRecommendations, entries);
8963
+ validRecommendations = resolvedRecommendations.recommendations;
8964
+ idResolutionSkipped = resolvedRecommendations.diagnostics.length;
8965
+ for (const diagnostic of resolvedRecommendations.diagnostics) {
8966
+ warn(`[curator] applyCuratorKnowledgeUpdates: entry_id '${diagnostic.line}' ${diagnostic.reason} \u2014 skipping`);
8967
+ }
9692
8968
  const updatedEntries = entries.map((entry) => {
9693
8969
  const rec = validRecommendations.find((r) => r.entry_id === entry.id);
9694
8970
  if (!rec)
@@ -9769,6 +9045,7 @@ async function applyCuratorKnowledgeUpdates(directory, recommendations, knowledg
9769
9045
  applied += txApplied;
9770
9046
  return modified ? updatedEntries : null;
9771
9047
  });
9048
+ skipped += idResolutionSkipped;
9772
9049
  for (const { id, reason } of contradictedEntries) {
9773
9050
  try {
9774
9051
  await recordKnowledgeEvent(directory, {
@@ -9782,107 +9059,1031 @@ async function applyCuratorKnowledgeUpdates(directory, recommendations, knowledg
9782
9059
  });
9783
9060
  } catch {}
9784
9061
  }
9785
- if (contradictedEntries.length > 0 && knowledgeConfig.contradiction_threshold_action === "quarantine") {
9786
- const { maybeQuarantineOnContradiction } = await import("./knowledge-escalator-9ksrgq7h.js");
9787
- for (const { id } of contradictedEntries) {
9062
+ if (contradictedEntries.length > 0 && knowledgeConfig.contradiction_threshold_action === "quarantine") {
9063
+ const { maybeQuarantineOnContradiction } = await import("./knowledge-escalator-0dkkenp2.js");
9064
+ for (const { id } of contradictedEntries) {
9065
+ try {
9066
+ await maybeQuarantineOnContradiction(directory, id, knowledgeConfig.contradiction_quarantine_threshold, knowledgeConfig.contradiction_quarantine_window_days);
9067
+ } catch {}
9068
+ }
9069
+ }
9070
+ for (const rec of validRecommendations) {
9071
+ if (rec.entry_id !== undefined && !appliedIds.has(rec.entry_id)) {
9072
+ if (!foundIds.has(rec.entry_id)) {
9073
+ warn(`[curator] applyCuratorKnowledgeUpdates: entry_id '${rec.entry_id}' not found \u2014 skipping`);
9074
+ }
9075
+ skipped++;
9076
+ }
9077
+ }
9078
+ const archivedRecs = validRecommendations.filter((r) => r.action === "archive" && r.entry_id && appliedIds.has(r.entry_id));
9079
+ const precomputedArchivedIds = archivedRecs.length > 0 ? await getArchivedKnowledgeIds(directory) : undefined;
9080
+ for (const rec of archivedRecs) {
9081
+ try {
9082
+ await writeArchiveTombstoneAndInvalidateSkills({
9083
+ directory,
9084
+ entryId: rec.entry_id,
9085
+ tier: "swarm",
9086
+ actor: "curator",
9087
+ reason: rec.reason ?? "curator archive recommendation",
9088
+ mode: "archive",
9089
+ previousStatus: archivedPrevStatus.get(rec.entry_id),
9090
+ sourceLabel: "curator",
9091
+ precomputedArchivedIds
9092
+ });
9093
+ } catch (err) {
9094
+ warn(`[curator] archive invalidation for entry '${rec.entry_id}' failed: ${err instanceof Error ? err.message : String(err)}`);
9095
+ }
9096
+ }
9097
+ const currentEntries = await readKnowledge(knowledgePath);
9098
+ const currentLessons = currentEntries.map((e) => e.lesson);
9099
+ for (const rec of validRecommendations) {
9100
+ if (rec.entry_id !== undefined)
9101
+ continue;
9102
+ if (rec.action !== "promote") {
9103
+ skipped++;
9104
+ continue;
9105
+ }
9106
+ const lesson = (rec.lesson?.trim() ?? "").slice(0, 280);
9107
+ if (lesson.length < 15) {
9108
+ skipped++;
9109
+ continue;
9110
+ }
9111
+ if (currentLessons.some((el) => el.toLowerCase() === lesson.toLowerCase())) {
9112
+ skipped++;
9113
+ continue;
9114
+ }
9115
+ if (knowledgeConfig.validation_enabled !== false) {
9116
+ const validation = validateLesson(lesson, currentLessons, {
9117
+ category: rec.category ?? "other",
9118
+ scope: "global",
9119
+ confidence: rec.confidence ?? 0.5
9120
+ });
9121
+ if (!validation.valid) {
9122
+ skipped++;
9123
+ continue;
9124
+ }
9125
+ }
9126
+ const now = new Date().toISOString();
9127
+ const newEntry = {
9128
+ id: randomUUID5(),
9129
+ tier: "swarm",
9130
+ lesson,
9131
+ category: rec.category ?? "other",
9132
+ tags: [],
9133
+ scope: "global",
9134
+ confidence: rec.confidence ?? 0.5,
9135
+ status: "candidate",
9136
+ confirmed_by: [],
9137
+ retrieval_outcomes: {
9138
+ applied_count: 0,
9139
+ succeeded_after_count: 0,
9140
+ failed_after_count: 0
9141
+ },
9142
+ schema_version: 1,
9143
+ created_at: now,
9144
+ updated_at: now,
9145
+ auto_generated: true,
9146
+ project_name: path15.basename(directory),
9147
+ triggers: rec.triggers,
9148
+ required_actions: rec.required_actions,
9149
+ forbidden_actions: rec.forbidden_actions,
9150
+ applies_to_agents: rec.applies_to_agents,
9151
+ applies_to_tools: rec.applies_to_tools,
9152
+ verification_checks: rec.verification_checks,
9153
+ directive_priority: rec.directive_priority
9154
+ };
9155
+ if (!validateActionability(newEntry).actionable) {
9156
+ try {
9157
+ await appendUnactionable(directory, newEntry, "curator_recommendation_unactionable");
9158
+ } catch {}
9159
+ skipped++;
9160
+ continue;
9161
+ }
9162
+ await appendKnowledge(knowledgePath, newEntry);
9163
+ applied++;
9164
+ currentLessons.push(lesson);
9165
+ }
9166
+ return { applied, skipped };
9167
+ }
9168
+
9169
+ // src/hooks/curator-postmortem.ts
9170
+ var MAX_INPUT_TEXT_CHARS = 500;
9171
+ var MAX_KNOWLEDGE_ENTRIES = 500;
9172
+ var MAX_PROPOSALS = 50;
9173
+ var MAX_RETROSPECTIVES = 50;
9174
+ var MAX_DRIFT_REPORTS = 50;
9175
+ var MAX_UNACTIONABLE = 1000;
9176
+ async function collectKnowledgeSummary(directory, scope = "project", sessionID) {
9177
+ const entries = await readKnowledge(resolveSwarmKnowledgePath(directory), MAX_KNOWLEDGE_ENTRIES);
9178
+ let events = await readKnowledgeEvents(directory, MAX_KNOWLEDGE_ENTRIES * 4);
9179
+ if (scope === "session" && sessionID) {
9180
+ events = events.filter((e) => e.session_id === sessionID);
9181
+ }
9182
+ const countsMap = new Map;
9183
+ for (const e of events) {
9184
+ if (e.type !== "applied" && e.type !== "violated" && e.type !== "ignored")
9185
+ continue;
9186
+ const kid = e.knowledge_id ?? e.entry_id;
9187
+ if (!kid)
9188
+ continue;
9189
+ const c = countsMap.get(kid) ?? { applied: 0, violated: 0, ignored: 0 };
9190
+ if (e.type === "applied")
9191
+ c.applied++;
9192
+ else if (e.type === "violated")
9193
+ c.violated++;
9194
+ else if (e.type === "ignored")
9195
+ c.ignored++;
9196
+ countsMap.set(kid, c);
9197
+ }
9198
+ return entries.map((entry) => {
9199
+ const c = countsMap.get(entry.id) ?? {
9200
+ applied: 0,
9201
+ violated: 0,
9202
+ ignored: 0
9203
+ };
9204
+ return {
9205
+ id: entry.id,
9206
+ lesson: entry.lesson,
9207
+ applied: c.applied,
9208
+ violated: c.violated,
9209
+ ignored: c.ignored,
9210
+ confidence: entry.confidence ?? 0.5,
9211
+ status: entry.status ?? "active"
9212
+ };
9213
+ });
9214
+ }
9215
+ function extractSection(text, sectionName) {
9216
+ const pattern = new RegExp(`^${sectionName}:\\s*\\n([\\s\\S]*?)(?=\\n[A-Z_]+:\\s*(?:\\n|$)|$)`, "m");
9217
+ const match = text.match(pattern);
9218
+ return match?.[1]?.trim() || null;
9219
+ }
9220
+ function normalizeRecommendationAction(raw) {
9221
+ const value = String(raw ?? "").toLowerCase().trim();
9222
+ if (value === "promote" || value === "promote_to_hive")
9223
+ return "promote";
9224
+ if (value === "archive" || value === "flag_stale")
9225
+ return "archive";
9226
+ if (value === "rewrite")
9227
+ return "rewrite";
9228
+ if (value === "flag_contradiction")
9229
+ return "flag_contradiction";
9230
+ if (value === "merge")
9231
+ return null;
9232
+ return null;
9233
+ }
9234
+ function normalizeProposalSlug(raw) {
9235
+ return raw.trim().replace(/^proposals[\\/]/, "").replace(/\.md$/i, "").replace(/\.json$/i, "");
9236
+ }
9237
+ function parseStructuredPostMortemActions(llmOutput) {
9238
+ const parsed = {
9239
+ summary: extractSection(llmOutput, "SUMMARY"),
9240
+ recommendations: [],
9241
+ queueTriage: [],
9242
+ diagnostics: []
9243
+ };
9244
+ const fence = /```(?:json|jsonc)?\s+postmortem_actions\s*\n([\s\S]*?)\n```/g;
9245
+ for (const match of llmOutput.matchAll(fence)) {
9246
+ let data;
9247
+ try {
9248
+ data = JSON.parse(match[1]);
9249
+ } catch (err) {
9250
+ parsed.diagnostics.push(`postmortem_actions malformed_json: ${err instanceof Error ? err.message : String(err)}`);
9251
+ continue;
9252
+ }
9253
+ if (!data || typeof data !== "object" || Array.isArray(data)) {
9254
+ parsed.diagnostics.push("postmortem_actions expected object");
9255
+ continue;
9256
+ }
9257
+ const obj = data;
9258
+ if (typeof obj.summary === "string" && obj.summary.trim()) {
9259
+ parsed.summary = obj.summary.trim().slice(0, 1000);
9260
+ }
9261
+ if (Array.isArray(obj.curation_recommendations)) {
9262
+ for (const [index, item] of obj.curation_recommendations.entries()) {
9263
+ if (!item || typeof item !== "object") {
9264
+ parsed.diagnostics.push(`curation_recommendations[${index}] invalid object`);
9265
+ continue;
9266
+ }
9267
+ const rec = item;
9268
+ const action = normalizeRecommendationAction(rec.action);
9269
+ const lesson = String(rec.lesson ?? rec.reason ?? "").trim();
9270
+ const reason = String(rec.reason ?? lesson).trim();
9271
+ if (!action || !reason) {
9272
+ parsed.diagnostics.push(`curation_recommendations[${index}] missing action/reason`);
9273
+ continue;
9274
+ }
9275
+ if (action === "rewrite" && lesson.length < 15) {
9276
+ parsed.diagnostics.push(`curation_recommendations[${index}] rewrite missing lesson`);
9277
+ continue;
9278
+ }
9279
+ parsed.recommendations.push({
9280
+ action,
9281
+ entry_id: typeof rec.entry_id === "string" ? normalizeRecommendationEntryIdToken(rec.entry_id) : undefined,
9282
+ lesson: (lesson || reason).slice(0, 280),
9283
+ reason: reason.slice(0, 280),
9284
+ category: typeof rec.category === "string" ? rec.category : undefined,
9285
+ confidence: typeof rec.confidence === "number" ? rec.confidence : undefined,
9286
+ triggers: stringArray(rec.triggers),
9287
+ required_actions: stringArray(rec.required_actions),
9288
+ forbidden_actions: stringArray(rec.forbidden_actions),
9289
+ applies_to_agents: stringArray(rec.applies_to_agents),
9290
+ applies_to_tools: stringArray(rec.applies_to_tools),
9291
+ verification_checks: stringArray(rec.verification_checks),
9292
+ directive_priority: directivePriority(rec.directive_priority)
9293
+ });
9294
+ }
9295
+ }
9296
+ if (Array.isArray(obj.queue_triage)) {
9297
+ for (const [index, item] of obj.queue_triage.entries()) {
9298
+ if (!item || typeof item !== "object") {
9299
+ parsed.diagnostics.push(`queue_triage[${index}] invalid object`);
9300
+ continue;
9301
+ }
9302
+ const triage = item;
9303
+ const proposalId = String(triage.proposal_id ?? "").trim();
9304
+ const action = String(triage.action ?? "").toLowerCase().trim();
9305
+ if (!proposalId || action !== "apply" && action !== "reject") {
9306
+ parsed.diagnostics.push(`queue_triage[${index}] missing proposal_id/action`);
9307
+ continue;
9308
+ }
9309
+ parsed.queueTriage.push({
9310
+ proposal_id: proposalId.slice(0, 120),
9311
+ action,
9312
+ reason: String(triage.reason ?? "").slice(0, 280)
9313
+ });
9314
+ }
9315
+ }
9316
+ }
9317
+ return parsed;
9318
+ }
9319
+ function stringArray(value) {
9320
+ if (!Array.isArray(value))
9321
+ return;
9322
+ const strings = value.filter((item) => typeof item === "string").map((item) => item.trim()).filter((item) => item.length > 0);
9323
+ return strings.length > 0 ? strings : undefined;
9324
+ }
9325
+ function directivePriority(value) {
9326
+ if (value === "low" || value === "medium" || value === "high" || value === "critical") {
9327
+ return value;
9328
+ }
9329
+ return;
9330
+ }
9331
+ function parseLegacyPostMortemActions(llmOutput) {
9332
+ const parsed = {
9333
+ summary: extractSection(llmOutput, "SUMMARY"),
9334
+ recommendations: [],
9335
+ queueTriage: [],
9336
+ diagnostics: []
9337
+ };
9338
+ const curation = extractSection(llmOutput, "CURATION_RECOMMENDATIONS");
9339
+ if (curation) {
9340
+ let recIndex = 0;
9341
+ for (const line of curation.split(`
9342
+ `)) {
9343
+ const trimmed = line.trim();
9344
+ if (!trimmed.startsWith("-"))
9345
+ continue;
9346
+ const body = trimmed.replace(/^-\s*/, "");
9347
+ const [head2, reasonPart = ""] = body.split(/\s+\u2014\s+|\s+-\s+/, 2);
9348
+ const [rawAction, rest = ""] = head2.split(/:\s*/, 2);
9349
+ const lowerRaw = rawAction.toLowerCase().trim();
9350
+ const action = normalizeRecommendationAction(rawAction);
9351
+ if (!action) {
9352
+ if (lowerRaw === "merge") {
9353
+ parsed.diagnostics.push("legacy curation_recommendations unsupported action 'merge' \u2014 dropped");
9354
+ } else if (lowerRaw) {
9355
+ parsed.diagnostics.push(`legacy curation_recommendations unrecognized action '${lowerRaw}'`);
9356
+ }
9357
+ continue;
9358
+ }
9359
+ const idMatch = rest.match(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/i);
9360
+ const reason = (reasonPart || rest).trim();
9361
+ if (!reason) {
9362
+ parsed.diagnostics.push(`legacy curation_recommendations[${recIndex}] missing reason`);
9363
+ continue;
9364
+ }
9365
+ parsed.recommendations.push({
9366
+ action,
9367
+ entry_id: idMatch?.[0],
9368
+ lesson: reason.slice(0, 280),
9369
+ reason: reason.slice(0, 280)
9370
+ });
9371
+ recIndex++;
9372
+ }
9373
+ }
9374
+ const queue = extractSection(llmOutput, "QUEUE_TRIAGE");
9375
+ if (queue) {
9376
+ for (const line of queue.split(`
9377
+ `)) {
9378
+ const trimmed = line.trim();
9379
+ if (!trimmed.startsWith("-"))
9380
+ continue;
9381
+ if (trimmed.includes(":")) {
9382
+ const match = trimmed.match(/^-\s*([^:]+):\s*(APPLY|REJECT)\b\s*(?:\u2014|-)?\s*(.*)$/i);
9383
+ if (!match) {
9384
+ parsed.diagnostics.push("legacy queue_triage malformed line");
9385
+ continue;
9386
+ }
9387
+ parsed.queueTriage.push({
9388
+ proposal_id: match[1].trim().slice(0, 120),
9389
+ action: match[2].toLowerCase(),
9390
+ reason: match[3].trim().slice(0, 280)
9391
+ });
9392
+ }
9393
+ }
9394
+ }
9395
+ return parsed;
9396
+ }
9397
+ function parsePostMortemActions(llmOutput) {
9398
+ const structured = parseStructuredPostMortemActions(llmOutput);
9399
+ const legacy = parseLegacyPostMortemActions(llmOutput);
9400
+ return {
9401
+ summary: structured.summary ?? legacy.summary,
9402
+ recommendations: structured.recommendations.length > 0 ? structured.recommendations : legacy.recommendations,
9403
+ queueTriage: structured.queueTriage.length > 0 ? structured.queueTriage : legacy.queueTriage,
9404
+ diagnostics: [...structured.diagnostics, ...legacy.diagnostics]
9405
+ };
9406
+ }
9407
+ async function executePostMortemActions(directory, parsed, options) {
9408
+ const warnings = [];
9409
+ const result = {
9410
+ knowledge_applied: 0,
9411
+ knowledge_skipped: 0,
9412
+ hive_promotions: 0,
9413
+ hive_encounters_incremented: 0,
9414
+ hive_advancements: 0,
9415
+ proposals_approved: 0,
9416
+ proposals_rejected: 0,
9417
+ proposals_skipped: 0
9418
+ };
9419
+ const knowledgeConfig = options.knowledgeConfig ?? await _internals10.loadDefaultKnowledgeConfig();
9420
+ if (parsed.recommendations.length > 0) {
9421
+ try {
9422
+ const knowledgeResult = await _internals10.applyCuratorKnowledgeUpdates(directory, parsed.recommendations, knowledgeConfig);
9423
+ result.knowledge_applied = knowledgeResult.applied;
9424
+ result.knowledge_skipped = knowledgeResult.skipped;
9425
+ } catch (err) {
9426
+ warnings.push(`Post-mortem knowledge actions failed: ${err instanceof Error ? err.message : String(err)}`);
9427
+ }
9428
+ try {
9429
+ const entries = await _internals10.readSwarmKnowledge(directory);
9430
+ const hiveResult = await _internals10.checkHivePromotions(entries, knowledgeConfig);
9431
+ result.hive_promotions = hiveResult.new_promotions;
9432
+ result.hive_encounters_incremented = hiveResult.encounters_incremented;
9433
+ result.hive_advancements = hiveResult.advancements;
9434
+ } catch (err) {
9435
+ warnings.push(`Post-mortem hive promotion check failed: ${err instanceof Error ? err.message : String(err)}`);
9436
+ }
9437
+ }
9438
+ if (parsed.queueTriage.length > 0) {
9439
+ try {
9440
+ const proposalResult = await _internals10.applyProposalTriage(directory, parsed.queueTriage);
9441
+ result.proposals_approved = proposalResult.approved.length;
9442
+ result.proposals_rejected = proposalResult.rejected.length;
9443
+ result.proposals_skipped = proposalResult.skipped.length;
9444
+ } catch (err) {
9445
+ warnings.push(`Post-mortem proposal triage failed: ${err instanceof Error ? err.message : String(err)}`);
9446
+ }
9447
+ }
9448
+ return { result, warnings };
9449
+ }
9450
+ async function verifyPostMortemKnowledgeActions(directory, recommendations) {
9451
+ if (recommendations.length === 0)
9452
+ return [];
9453
+ const entries = await _internals10.readSwarmKnowledge(directory);
9454
+ const activeEntries = entries.filter((entry) => isActiveStatus(entry.status));
9455
+ const exactIds = new Set(activeEntries.map((entry) => entry.id));
9456
+ const prefixMatches = new Map;
9457
+ return recommendations.map((rec) => {
9458
+ const inputId = typeof rec.entry_id === "string" ? normalizeRecommendationEntryIdToken(rec.entry_id) ?? null : null;
9459
+ if (!inputId) {
9460
+ const isNewPromote = rec.action === "promote";
9461
+ return {
9462
+ action: rec.action,
9463
+ input_entry_id: null,
9464
+ resolved_entry_id: null,
9465
+ status: isNewPromote ? "new_entry" : "missing_entry_id",
9466
+ reason: isNewPromote ? "promote without entry_id will create a new candidate if actionable" : `${rec.action} requires an existing entry_id`
9467
+ };
9468
+ }
9469
+ if (exactIds.has(inputId)) {
9470
+ return {
9471
+ action: rec.action,
9472
+ input_entry_id: inputId,
9473
+ resolved_entry_id: inputId,
9474
+ status: "exact_match",
9475
+ reason: "entry_id matched an existing knowledge entry exactly"
9476
+ };
9477
+ }
9478
+ let matches = prefixMatches.get(inputId);
9479
+ if (!matches) {
9480
+ matches = activeEntries.filter((entry) => entry.id.startsWith(inputId));
9481
+ prefixMatches.set(inputId, matches);
9482
+ }
9483
+ if (matches.length === 1) {
9484
+ return {
9485
+ action: rec.action,
9486
+ input_entry_id: inputId,
9487
+ resolved_entry_id: matches[0].id,
9488
+ status: "prefix_match",
9489
+ reason: "entry_id prefix resolved to one existing knowledge entry"
9490
+ };
9491
+ }
9492
+ return {
9493
+ action: rec.action,
9494
+ input_entry_id: inputId,
9495
+ resolved_entry_id: null,
9496
+ status: matches.length === 0 ? "not_found" : "ambiguous_prefix",
9497
+ reason: matches.length === 0 ? "entry_id did not match any existing knowledge entry" : `entry_id prefix matched ${matches.length} knowledge entries`
9498
+ };
9499
+ });
9500
+ }
9501
+ function buildStateFreshnessSection(args) {
9502
+ const planContext = args.planLoaded ? `loaded (${args.planId})` : "unavailable (plan_id: unknown; project-level fallback)";
9503
+ return [
9504
+ "## Generated Against State",
9505
+ `- Plan context: ${planContext}`,
9506
+ `- Scope: ${args.scope}${args.sessionID ? ` (${args.sessionID})` : ""}`,
9507
+ `- Knowledge entries summarized: ${args.knowledgeSummary.length}`,
9508
+ `- Pending proposals summarized: ${args.proposals.length}`,
9509
+ `- Unactionable entries summarized: ${args.unactionable.length}`,
9510
+ `- Retrospectives summarized: ${args.retrospectives.length}`,
9511
+ `- Drift reports summarized: ${args.driftReports.length}`
9512
+ ].join(`
9513
+ `);
9514
+ }
9515
+ function buildActionVerificationSection(verification) {
9516
+ const lines = ["## Post-Mortem Action Verification"];
9517
+ if (verification.length === 0) {
9518
+ lines.push("- Knowledge actions: none supplied.");
9519
+ return lines.join(`
9520
+ `);
9521
+ }
9522
+ for (const item of verification) {
9523
+ const input = item.input_entry_id ?? "new";
9524
+ const resolved = item.resolved_entry_id ? ` => ${item.resolved_entry_id}` : "";
9525
+ lines.push(`- ${item.action}: ${input}${resolved} [${item.status}] ${item.reason}`);
9526
+ }
9527
+ return lines.join(`
9528
+ `);
9529
+ }
9530
+ async function repairPostMortemActions(llmOutput, diagnostics, options) {
9531
+ if (!options.llmDelegate || diagnostics.length === 0)
9532
+ return null;
9533
+ const ac = new AbortController;
9534
+ const timer = setTimeout(() => ac.abort(), 30000);
9535
+ try {
9536
+ const repairPrompt = [
9537
+ "Repair the supplied CURATOR_POSTMORTEM output into one valid fenced JSON block.",
9538
+ "Return only this fence:",
9539
+ "```json postmortem_actions",
9540
+ '{"summary":"...","curation_recommendations":[],"queue_triage":[]}',
9541
+ "```",
9542
+ "Allowed curation action values: promote, archive, rewrite, flag_contradiction.",
9543
+ "For new promote recommendations, include at least one scope field (applies_to_agents or applies_to_tools) and at least one predicate field (required_actions, forbidden_actions, or verification_checks).",
9544
+ "Existing entry_id values may be full UUIDs or unique 8+ character hex prefixes copied from KNOWLEDGE_ENTRIES.",
9545
+ "Allowed queue_triage action values: apply, reject.",
9546
+ "Do not include unsupported actions such as merge.",
9547
+ "Diagnostics:",
9548
+ diagnostics.join("; "),
9549
+ "Original output:",
9550
+ llmOutput.slice(0, 8000)
9551
+ ].join(`
9552
+ `);
9553
+ const repaired = await options.llmDelegate("", repairPrompt, ac.signal);
9554
+ const parsed = _internals10.parsePostMortemActions(repaired);
9555
+ if (parsed.diagnostics.length === 0) {
9556
+ return parsed;
9557
+ }
9558
+ return null;
9559
+ } catch {
9560
+ return null;
9561
+ } finally {
9562
+ clearTimeout(timer);
9563
+ }
9564
+ }
9565
+ function readJsonlFile(filePath, maxLines) {
9566
+ try {
9567
+ if (!existsSync10(filePath))
9568
+ return [];
9569
+ const content = readFileSync6(filePath, "utf-8");
9570
+ const results = [];
9571
+ const max = maxLines !== undefined && maxLines > 0 ? maxLines : Infinity;
9572
+ for (const line of content.split(`
9573
+ `)) {
9574
+ if (results.length >= max)
9575
+ break;
9576
+ if (!line.trim())
9577
+ continue;
9788
9578
  try {
9789
- await maybeQuarantineOnContradiction(directory, id, knowledgeConfig.contradiction_quarantine_threshold, knowledgeConfig.contradiction_quarantine_window_days);
9579
+ results.push(JSON.parse(line));
9790
9580
  } catch {}
9791
9581
  }
9582
+ return results;
9583
+ } catch {
9584
+ return [];
9792
9585
  }
9793
- for (const rec of validRecommendations) {
9794
- if (rec.entry_id !== undefined && !appliedIds.has(rec.entry_id)) {
9795
- if (!foundIds.has(rec.entry_id)) {
9796
- warn(`[curator] applyCuratorKnowledgeUpdates: entry_id '${rec.entry_id}' not found \u2014 skipping`);
9586
+ }
9587
+ function collectRetrospectives(directory) {
9588
+ const results = [];
9589
+ const evidenceDir = path16.join(directory, ".swarm", "evidence");
9590
+ try {
9591
+ if (!existsSync10(evidenceDir))
9592
+ return results;
9593
+ const retroDirs = readdirSync2(evidenceDir, { withFileTypes: true }).filter((e) => e.isDirectory() && e.name.startsWith("retro-")).slice(0, MAX_RETROSPECTIVES);
9594
+ for (const entry of retroDirs) {
9595
+ const retroPath = path16.join(evidenceDir, entry.name, "evidence.json");
9596
+ if (existsSync10(retroPath)) {
9597
+ try {
9598
+ results.push(readFileSync6(retroPath, "utf-8"));
9599
+ } catch {}
9797
9600
  }
9798
- skipped++;
9799
9601
  }
9602
+ } catch {}
9603
+ return results;
9604
+ }
9605
+ async function collectDriftReports(directory) {
9606
+ try {
9607
+ const reports = await _internals10.readPriorDriftReports(directory);
9608
+ return reports.slice(-MAX_DRIFT_REPORTS).map((report) => JSON.stringify(report, null, 2));
9609
+ } catch {
9610
+ return [];
9800
9611
  }
9801
- const archivedRecs = validRecommendations.filter((r) => r.action === "archive" && r.entry_id && appliedIds.has(r.entry_id));
9802
- const precomputedArchivedIds = archivedRecs.length > 0 ? await getArchivedKnowledgeIds(directory) : undefined;
9803
- for (const rec of archivedRecs) {
9612
+ }
9613
+ function collectPendingProposals(directory) {
9614
+ const results = [];
9615
+ const insightPath = path16.join(directory, ".swarm", "insight-candidates.jsonl");
9616
+ if (existsSync10(insightPath)) {
9804
9617
  try {
9805
- await writeArchiveTombstoneAndInvalidateSkills({
9806
- directory,
9807
- entryId: rec.entry_id,
9808
- tier: "swarm",
9809
- actor: "curator",
9810
- reason: rec.reason ?? "curator archive recommendation",
9811
- mode: "archive",
9812
- previousStatus: archivedPrevStatus.get(rec.entry_id),
9813
- sourceLabel: "curator",
9814
- precomputedArchivedIds
9618
+ results.push({
9619
+ source: "insight-candidates",
9620
+ content: readFileSync6(insightPath, "utf-8")
9815
9621
  });
9816
- } catch (err) {
9817
- warn(`[curator] archive invalidation for entry '${rec.entry_id}' failed: ${err instanceof Error ? err.message : String(err)}`);
9622
+ } catch {}
9623
+ }
9624
+ const proposalsDir = path16.join(directory, ".swarm", "skills", "proposals");
9625
+ try {
9626
+ if (existsSync10(proposalsDir)) {
9627
+ const proposalFiles = readdirSync2(proposalsDir).filter((e) => e.endsWith(".md") || e.endsWith(".json")).slice(0, MAX_PROPOSALS);
9628
+ for (const entry of proposalFiles) {
9629
+ try {
9630
+ results.push({
9631
+ source: `proposals/${entry}`,
9632
+ content: readFileSync6(path16.join(proposalsDir, entry), "utf-8")
9633
+ });
9634
+ } catch {}
9635
+ }
9818
9636
  }
9637
+ } catch {}
9638
+ return results;
9639
+ }
9640
+ function isReportValid(reportPath) {
9641
+ try {
9642
+ if (!existsSync10(reportPath))
9643
+ return false;
9644
+ const content = readFileSync6(reportPath, "utf-8").trim();
9645
+ if (content.length === 0)
9646
+ return false;
9647
+ if (!content.startsWith("# Post-Mortem Report:"))
9648
+ return false;
9649
+ return true;
9650
+ } catch {
9651
+ return false;
9819
9652
  }
9820
- const currentEntries = await readKnowledge(knowledgePath);
9821
- const currentLessons = currentEntries.map((e) => e.lesson);
9822
- for (const rec of validRecommendations) {
9823
- if (rec.entry_id !== undefined)
9824
- continue;
9825
- if (rec.action !== "promote") {
9826
- skipped++;
9827
- continue;
9653
+ }
9654
+ async function acquirePostMortemLock(directory, planId) {
9655
+ const result = await tryAcquireLock(directory, `post-mortem-${planId}.lock`, "curator-postmortem", planId);
9656
+ if (result.acquired) {
9657
+ return { acquired: true, release: result.lock?._release };
9658
+ }
9659
+ return { acquired: false };
9660
+ }
9661
+ function buildDataOnlyReport(planId, planSummary, knowledgeSummary, curatorDigest, proposals, unactionable, retrospectives, driftReports, context) {
9662
+ const now = new Date().toISOString();
9663
+ const lines = [];
9664
+ lines.push(`# Post-Mortem Report: ${planId}`);
9665
+ lines.push(`Generated: ${now}`);
9666
+ lines.push("");
9667
+ lines.push(buildStateFreshnessSection({
9668
+ planId,
9669
+ scope: context?.scope ?? "project",
9670
+ sessionID: context?.sessionID,
9671
+ planLoaded: context?.planLoaded ?? planId !== "unknown",
9672
+ knowledgeSummary,
9673
+ proposals,
9674
+ unactionable,
9675
+ retrospectives,
9676
+ driftReports
9677
+ }));
9678
+ lines.push("");
9679
+ lines.push("## Project Summary");
9680
+ lines.push(planSummary);
9681
+ lines.push("");
9682
+ lines.push("## Knowledge Metrics");
9683
+ const totalEntries = knowledgeSummary.length;
9684
+ const totalApplied = knowledgeSummary.reduce((s, e) => s + e.applied, 0);
9685
+ const totalViolated = knowledgeSummary.reduce((s, e) => s + e.violated, 0);
9686
+ const totalIgnored = knowledgeSummary.reduce((s, e) => s + e.ignored, 0);
9687
+ const neverApplied = knowledgeSummary.filter((e) => e.applied === 0 && e.violated === 0 && e.ignored === 0);
9688
+ lines.push(`- Total entries: ${totalEntries}`);
9689
+ lines.push(`- Application events: ${totalApplied} applied, ${totalViolated} violated, ${totalIgnored} ignored`);
9690
+ lines.push(`- Never-applied entries: ${neverApplied.length}`);
9691
+ if (totalApplied + totalViolated > 0) {
9692
+ const appRate = (totalApplied / (totalApplied + totalViolated) * 100).toFixed(1);
9693
+ lines.push(`- Application rate: ${appRate}%`);
9694
+ }
9695
+ lines.push("");
9696
+ if (neverApplied.length > 0) {
9697
+ lines.push("### Never-Applied Entries (review for retirement)");
9698
+ for (const e of neverApplied.slice(0, 10)) {
9699
+ lines.push(`- \`${e.id}\` (confidence: ${e.confidence.toFixed(2)}): ${e.lesson.slice(0, 80)}`);
9828
9700
  }
9829
- const lesson = (rec.lesson?.trim() ?? "").slice(0, 280);
9830
- if (lesson.length < 15) {
9831
- skipped++;
9832
- continue;
9701
+ if (neverApplied.length > 10) {
9702
+ lines.push(`- ... and ${neverApplied.length - 10} more`);
9833
9703
  }
9834
- if (currentLessons.some((el) => el.toLowerCase() === lesson.toLowerCase())) {
9835
- skipped++;
9836
- continue;
9704
+ lines.push("");
9705
+ }
9706
+ const highViolation = knowledgeSummary.filter((e) => e.violated > 0).sort((a, b) => b.violated - a.violated).slice(0, 5);
9707
+ if (highViolation.length > 0) {
9708
+ lines.push("### High-Violation Entries");
9709
+ for (const e of highViolation) {
9710
+ lines.push(`- \`${e.id}\` \u2014 ${e.violated} violations, ${e.applied} applied: ${e.lesson.slice(0, 80)}`);
9837
9711
  }
9838
- if (knowledgeConfig.validation_enabled !== false) {
9839
- const validation = validateLesson(lesson, currentLessons, {
9840
- category: rec.category ?? "other",
9841
- scope: "global",
9842
- confidence: rec.confidence ?? 0.5
9843
- });
9844
- if (!validation.valid) {
9845
- skipped++;
9846
- continue;
9712
+ lines.push("");
9713
+ }
9714
+ lines.push("## Queue Status");
9715
+ lines.push(`- Pending proposals: ${proposals.length}`);
9716
+ lines.push(`- Unactionable quarantine: ${unactionable.length}`);
9717
+ for (const p of proposals) {
9718
+ lines.push(` - ${p.source}`);
9719
+ }
9720
+ lines.push("");
9721
+ if (retrospectives.length > 0) {
9722
+ lines.push("## Retrospectives");
9723
+ lines.push(`${retrospectives.length} phase retrospective(s) recorded.`);
9724
+ lines.push("");
9725
+ }
9726
+ if (driftReports.length > 0) {
9727
+ lines.push("## Drift Reports");
9728
+ for (const dr of driftReports) {
9729
+ try {
9730
+ const parsed = JSON.parse(dr);
9731
+ lines.push(`- Phase ${parsed.phase}: ${parsed.alignment} (score: ${parsed.drift_score})`);
9732
+ } catch {
9733
+ lines.push("- (unparseable drift report)");
9847
9734
  }
9848
9735
  }
9849
- const now = new Date().toISOString();
9850
- const newEntry = {
9851
- id: randomUUID5(),
9852
- tier: "swarm",
9853
- lesson,
9854
- category: rec.category ?? "other",
9855
- tags: [],
9856
- scope: "global",
9857
- confidence: rec.confidence ?? 0.5,
9858
- status: "candidate",
9859
- confirmed_by: [],
9860
- retrieval_outcomes: {
9861
- applied_count: 0,
9862
- succeeded_after_count: 0,
9863
- failed_after_count: 0
9864
- },
9865
- schema_version: 1,
9866
- created_at: now,
9867
- updated_at: now,
9868
- auto_generated: true,
9869
- project_name: path16.basename(directory)
9736
+ lines.push("");
9737
+ }
9738
+ if (curatorDigest) {
9739
+ lines.push("## Curator Digest Summary");
9740
+ const trimmed = curatorDigest.length > 1000 ? `${curatorDigest.slice(0, 1000)}...` : curatorDigest;
9741
+ lines.push(trimmed);
9742
+ lines.push("");
9743
+ }
9744
+ return lines.join(`
9745
+ `);
9746
+ }
9747
+ function assembleLLMInput(planId, scope, sessionID, planSummary, knowledgeSummary, curatorDigest, proposals, unactionable, retrospectives, driftReports) {
9748
+ const sections = [];
9749
+ sections.push(`TASK: CURATOR_POSTMORTEM ${planId}`);
9750
+ sections.push(`SCOPE: ${scope}${sessionID ? ` (${sessionID})` : ""}`);
9751
+ sections.push(`PLAN_SUMMARY: ${planSummary}`);
9752
+ sections.push(`CURATOR_DIGESTS: ${curatorDigest ?? "none"}`);
9753
+ const eventsSummary = knowledgeSummary.map((e) => `${e.id}: applied=${e.applied} violated=${e.violated} ignored=${e.ignored} confidence=${e.confidence.toFixed(2)} status=${e.status}`).join(`
9754
+ `);
9755
+ sections.push(`KNOWLEDGE_EVENTS_SUMMARY:
9756
+ ${eventsSummary || "none"}`);
9757
+ const knEntries = knowledgeSummary.map((e) => JSON.stringify({
9758
+ id: e.id,
9759
+ lesson: e.lesson.slice(0, MAX_INPUT_TEXT_CHARS)
9760
+ })).join(`
9761
+ `);
9762
+ sections.push(`KNOWLEDGE_ENTRIES:
9763
+ ${knEntries || "[]"}`);
9764
+ const proposalText = proposals.length > 0 ? proposals.map((p) => `[${p.source}]
9765
+ ${p.content.slice(0, MAX_INPUT_TEXT_CHARS)}`).join(`
9766
+ ---
9767
+ `) : "none";
9768
+ sections.push(`PENDING_PROPOSALS:
9769
+ ${proposalText}`);
9770
+ sections.push(`UNACTIONABLE_QUARANTINE: ${unactionable.length} entries`);
9771
+ const retroText = retrospectives.length > 0 ? retrospectives.map((r) => r.slice(0, MAX_INPUT_TEXT_CHARS)).join(`
9772
+ ---
9773
+ `) : "none";
9774
+ sections.push(`RETROSPECTIVES:
9775
+ ${retroText}`);
9776
+ if (driftReports.length > 0) {
9777
+ const driftText = driftReports.map((r) => r.slice(0, MAX_INPUT_TEXT_CHARS)).join(`
9778
+ ---
9779
+ `);
9780
+ sections.push(`DRIFT_REPORTS:
9781
+ ${driftText}`);
9782
+ }
9783
+ return sections.join(`
9784
+
9785
+ `);
9786
+ }
9787
+ async function runCuratorPostMortem(directory, options = {}) {
9788
+ const warnings = [];
9789
+ const scope = options.scope ?? "project";
9790
+ let planId = "unknown";
9791
+ let planSummary = "Plan data unavailable.";
9792
+ let planLoaded = false;
9793
+ try {
9794
+ const plan = await loadPlanJsonOnly(directory);
9795
+ if (plan) {
9796
+ planLoaded = true;
9797
+ planId = derivePlanId(plan);
9798
+ const phaseCount = plan.phases?.length ?? 0;
9799
+ const completedPhases = plan.phases?.filter((p) => p.status === "complete").length ?? 0;
9800
+ planSummary = `Plan "${plan.title}" (${plan.swarm}): ${completedPhases}/${phaseCount} phases complete.`;
9801
+ } else {
9802
+ warnings.push("Plan not found \u2014 using fallback plan ID.");
9803
+ }
9804
+ } catch {
9805
+ warnings.push("Failed to load plan data.");
9806
+ }
9807
+ const effectivePlanId = planId;
9808
+ const reportFilename = `post-mortem-${effectivePlanId}.md`;
9809
+ let reportPath;
9810
+ try {
9811
+ reportPath = validateSwarmPath(directory, reportFilename);
9812
+ } catch {
9813
+ return {
9814
+ success: false,
9815
+ planId,
9816
+ reportPath: null,
9817
+ summary: null,
9818
+ warnings: [...warnings, "Invalid report path."]
9870
9819
  };
9871
- if (!validateActionability(newEntry).actionable) {
9820
+ }
9821
+ if (!options.force && isReportValid(reportPath)) {
9822
+ return {
9823
+ success: true,
9824
+ planId: effectivePlanId,
9825
+ reportPath,
9826
+ summary: "Post-mortem report already exists (idempotent skip).",
9827
+ warnings
9828
+ };
9829
+ }
9830
+ const lock = await _internals10.acquirePostMortemLock(directory, effectivePlanId);
9831
+ if (!lock.acquired) {
9832
+ return {
9833
+ success: false,
9834
+ planId: effectivePlanId,
9835
+ reportPath,
9836
+ summary: null,
9837
+ warnings: [
9838
+ ...warnings,
9839
+ `Concurrent post-mortem run in progress for plan ${effectivePlanId}; skipped.`
9840
+ ]
9841
+ };
9842
+ }
9843
+ try {
9844
+ let knowledgeSummary = [];
9845
+ try {
9846
+ knowledgeSummary = await collectKnowledgeSummary(directory, scope, options.sessionID);
9847
+ } catch {
9848
+ warnings.push("Failed to collect knowledge summary.");
9849
+ }
9850
+ if (knowledgeSummary.length > MAX_KNOWLEDGE_ENTRIES) {
9851
+ warnings.push(`Knowledge entries capped at ${MAX_KNOWLEDGE_ENTRIES} (had ${knowledgeSummary.length}); older entries truncated.`);
9852
+ knowledgeSummary = knowledgeSummary.slice(0, MAX_KNOWLEDGE_ENTRIES);
9853
+ }
9854
+ let curatorDigest = null;
9855
+ try {
9856
+ const raw = await readSwarmFileAsync(directory, "curator-summary.json");
9857
+ if (raw) {
9858
+ const parsed = JSON.parse(raw);
9859
+ curatorDigest = parsed.digest ?? null;
9860
+ }
9861
+ } catch {
9862
+ warnings.push("Failed to read curator digest.");
9863
+ }
9864
+ let proposals = collectPendingProposals(directory);
9865
+ if (proposals.length > MAX_PROPOSALS) {
9866
+ warnings.push(`Pending proposals capped at ${MAX_PROPOSALS} (had ${proposals.length}); older entries truncated.`);
9867
+ proposals = proposals.slice(0, MAX_PROPOSALS);
9868
+ }
9869
+ const unactionablePath = path16.join(resolveKnowledgeStoreDir(directory), "knowledge-unactionable.jsonl");
9870
+ let unactionable = readJsonlFile(unactionablePath, MAX_UNACTIONABLE);
9871
+ if (unactionable.length > MAX_UNACTIONABLE) {
9872
+ warnings.push(`Unactionable entries capped at ${MAX_UNACTIONABLE} (had ${unactionable.length}); older entries truncated.`);
9873
+ unactionable = unactionable.slice(0, MAX_UNACTIONABLE);
9874
+ }
9875
+ let retrospectives = collectRetrospectives(directory);
9876
+ if (retrospectives.length > MAX_RETROSPECTIVES) {
9877
+ warnings.push(`Retrospectives capped at ${MAX_RETROSPECTIVES} (had ${retrospectives.length}); older entries truncated.`);
9878
+ retrospectives = retrospectives.slice(0, MAX_RETROSPECTIVES);
9879
+ }
9880
+ let driftReports = await collectDriftReports(directory);
9881
+ if (driftReports.length > MAX_DRIFT_REPORTS) {
9882
+ warnings.push(`Drift reports capped at ${MAX_DRIFT_REPORTS} (had ${driftReports.length}); older entries truncated.`);
9883
+ driftReports = driftReports.slice(0, MAX_DRIFT_REPORTS);
9884
+ }
9885
+ let reportContent;
9886
+ let llmSummary = null;
9887
+ let actionResult;
9888
+ if (options.llmDelegate) {
9872
9889
  try {
9873
- await appendUnactionable(directory, newEntry, "curator_recommendation_unactionable");
9890
+ const { CURATOR_POSTMORTEM_PROMPT } = await import("./explorer-t5srbq64.js");
9891
+ const userInput = assembleLLMInput(effectivePlanId, scope, options.sessionID, planSummary, knowledgeSummary, curatorDigest, proposals, unactionable, retrospectives, driftReports);
9892
+ const ac = new AbortController;
9893
+ const timer = setTimeout(() => ac.abort(), options.llmTimeoutMs ?? 300000);
9894
+ let llmOutput;
9895
+ try {
9896
+ const delegatePromise = options.llmDelegate(CURATOR_POSTMORTEM_PROMPT, userInput, ac.signal);
9897
+ delegatePromise.catch(() => {});
9898
+ llmOutput = await Promise.race([
9899
+ delegatePromise,
9900
+ new Promise((_, reject) => {
9901
+ ac.signal.addEventListener("abort", () => reject(new Error("CURATOR_LLM_TIMEOUT")));
9902
+ })
9903
+ ]);
9904
+ } finally {
9905
+ clearTimeout(timer);
9906
+ }
9907
+ let parsedActions = _internals10.parsePostMortemActions(llmOutput);
9908
+ if (parsedActions.diagnostics.length > 0) {
9909
+ warnings.push(`Post-mortem structured action parse diagnostics: ${parsedActions.diagnostics.join("; ")}`);
9910
+ const repaired = await _internals10.repairPostMortemActions(llmOutput, parsedActions.diagnostics, options);
9911
+ if (repaired) {
9912
+ parsedActions = repaired;
9913
+ warnings.push("Post-mortem structured actions repaired by LLM.");
9914
+ }
9915
+ }
9916
+ llmSummary = parsedActions.summary;
9917
+ const executed = await _internals10.executePostMortemActions(directory, parsedActions, options);
9918
+ actionResult = executed.result;
9919
+ warnings.push(...executed.warnings);
9920
+ const knowledgeVerification = await _internals10.verifyPostMortemKnowledgeActions(directory, parsedActions.recommendations);
9921
+ for (const item of knowledgeVerification) {
9922
+ if (item.status === "not_found" || item.status === "ambiguous_prefix" || item.status === "missing_entry_id") {
9923
+ warnings.push(`Post-mortem knowledge action ${item.action} for '${item.input_entry_id ?? "new"}' ${item.status}: ${item.reason}`);
9924
+ }
9925
+ }
9926
+ const freshnessSummary = buildStateFreshnessSection({
9927
+ planId: effectivePlanId,
9928
+ scope,
9929
+ sessionID: options.sessionID,
9930
+ planLoaded,
9931
+ knowledgeSummary,
9932
+ proposals,
9933
+ unactionable,
9934
+ retrospectives,
9935
+ driftReports
9936
+ });
9937
+ const verificationSummary = buildActionVerificationSection(knowledgeVerification);
9938
+ const actionSummary = [
9939
+ "## Executed Post-Mortem Actions",
9940
+ `- Knowledge actions: ${actionResult.knowledge_applied} applied, ${actionResult.knowledge_skipped} skipped`,
9941
+ `- Hive actions: ${actionResult.hive_promotions} new promotions, ${actionResult.hive_encounters_incremented} encounters incremented, ${actionResult.hive_advancements} advancements`,
9942
+ `- Proposal actions: ${actionResult.proposals_approved} approved, ${actionResult.proposals_rejected} rejected, ${actionResult.proposals_skipped} skipped`
9943
+ ].join(`
9944
+ `);
9945
+ reportContent = `# Post-Mortem Report: ${effectivePlanId}
9946
+ Generated: ${new Date().toISOString()}
9947
+ Scope: ${scope}
9948
+
9949
+ ${freshnessSummary}
9950
+
9951
+ ${llmOutput}
9952
+
9953
+ ${verificationSummary}
9954
+
9955
+ ${actionSummary}`;
9956
+ } catch (err) {
9957
+ const msg = err instanceof Error ? err.message : String(err);
9958
+ warnings.push(`LLM delegate failed, falling back to data-only report: ${msg}`);
9959
+ reportContent = _internals10.buildDataOnlyReport(effectivePlanId, planSummary, knowledgeSummary, curatorDigest, proposals, unactionable, retrospectives, driftReports, { scope, sessionID: options.sessionID, planLoaded });
9960
+ }
9961
+ } else {
9962
+ reportContent = _internals10.buildDataOnlyReport(effectivePlanId, planSummary, knowledgeSummary, curatorDigest, proposals, unactionable, retrospectives, driftReports, { scope, sessionID: options.sessionID, planLoaded });
9963
+ }
9964
+ try {
9965
+ const { mkdirSync: mkdirSync8 } = await import("fs");
9966
+ mkdirSync8(path16.dirname(reportPath), { recursive: true });
9967
+ await atomicWriteFile(reportPath, reportContent);
9968
+ } catch (err) {
9969
+ const msg = err instanceof Error ? err.message : String(err);
9970
+ return {
9971
+ success: false,
9972
+ planId: effectivePlanId,
9973
+ reportPath: null,
9974
+ summary: null,
9975
+ warnings: [...warnings, `Failed to write report: ${msg}`]
9976
+ };
9977
+ }
9978
+ const totalEntries = knowledgeSummary.length;
9979
+ const neverAppliedCount = knowledgeSummary.filter((e) => e.applied === 0 && e.violated === 0 && e.ignored === 0).length;
9980
+ const totalViolations = knowledgeSummary.reduce((s, e) => s + e.violated, 0);
9981
+ const mechanicalSummary = [
9982
+ `Post-mortem for plan "${effectivePlanId}": ${totalEntries} knowledge entries reviewed.`,
9983
+ `${neverAppliedCount} never-applied entries flagged; ${totalViolations} total violations recorded.`,
9984
+ `${proposals.length} pending proposals, ${unactionable.length} quarantined entries.`
9985
+ ].join(" ");
9986
+ const summary = llmSummary ?? mechanicalSummary;
9987
+ return {
9988
+ success: true,
9989
+ planId: effectivePlanId,
9990
+ reportPath,
9991
+ summary,
9992
+ warnings,
9993
+ actions: actionResult
9994
+ };
9995
+ } finally {
9996
+ if (lock.release) {
9997
+ try {
9998
+ await lock.release();
9874
9999
  } catch {}
9875
- skipped++;
9876
- continue;
9877
10000
  }
9878
- await appendKnowledge(knowledgePath, newEntry);
9879
- applied++;
9880
- currentLessons.push(lesson);
9881
10001
  }
9882
- return { applied, skipped };
9883
10002
  }
10003
+ var _internals10 = {
10004
+ acquirePostMortemLock,
10005
+ collectKnowledgeSummary,
10006
+ collectRetrospectives,
10007
+ collectDriftReports,
10008
+ collectPendingProposals,
10009
+ readJsonlFile,
10010
+ buildDataOnlyReport,
10011
+ assembleLLMInput,
10012
+ buildStateFreshnessSection,
10013
+ buildActionVerificationSection,
10014
+ isReportValid,
10015
+ parsePostMortemActions,
10016
+ executePostMortemActions,
10017
+ verifyPostMortemKnowledgeActions,
10018
+ repairPostMortemActions,
10019
+ loadDefaultKnowledgeConfig: async () => {
10020
+ const { KnowledgeConfigSchema: KnowledgeConfigSchema2 } = await import("./schema-s48je8zt.js");
10021
+ return KnowledgeConfigSchema2.parse({});
10022
+ },
10023
+ applyCuratorKnowledgeUpdates: async (directory, recommendations, knowledgeConfig) => {
10024
+ const { applyCuratorKnowledgeUpdates: applyCuratorKnowledgeUpdates2 } = await import("./curator-c7dzf9rt.js");
10025
+ return applyCuratorKnowledgeUpdates2(directory, recommendations, knowledgeConfig);
10026
+ },
10027
+ checkHivePromotions: async (entries, knowledgeConfig) => {
10028
+ const { checkHivePromotions } = await import("./hive-promoter-kr56vt08.js");
10029
+ return checkHivePromotions(entries, knowledgeConfig);
10030
+ },
10031
+ applyProposalTriage: async (directory, triage) => {
10032
+ const {
10033
+ _internals: skillInternals,
10034
+ activateProposal,
10035
+ listSkills: listSkills2,
10036
+ sanitizeSlug
10037
+ } = await import("./skill-generator-5rsjmmfq.js");
10038
+ const result = {
10039
+ approved: [],
10040
+ rejected: [],
10041
+ skipped: []
10042
+ };
10043
+ const skills = await listSkills2(directory);
10044
+ const proposalSlugs = new Set(skills.proposals.map((p) => p.slug));
10045
+ for (const item of triage) {
10046
+ const slug = sanitizeSlug(normalizeProposalSlug(item.proposal_id));
10047
+ if (!slug || !proposalSlugs.has(slug)) {
10048
+ result.skipped.push(slug || item.proposal_id);
10049
+ continue;
10050
+ }
10051
+ if (item.action === "apply") {
10052
+ const activation = await activateProposal(directory, slug, false, {
10053
+ evaluate: true,
10054
+ operation: "post_mortem_queue_triage",
10055
+ confirmUnevaluated: true
10056
+ });
10057
+ if (activation.activated) {
10058
+ result.approved.push(slug);
10059
+ } else {
10060
+ result.skipped.push(slug);
10061
+ }
10062
+ continue;
10063
+ }
10064
+ const proposal = skills.proposals.find((p) => p.slug === slug);
10065
+ if (!proposal) {
10066
+ result.skipped.push(slug);
10067
+ continue;
10068
+ }
10069
+ try {
10070
+ skillInternals.unlinkSync(proposal.path);
10071
+ result.rejected.push(slug);
10072
+ } catch {
10073
+ result.skipped.push(slug);
10074
+ }
10075
+ }
10076
+ return result;
10077
+ },
10078
+ readPriorDriftReports: async (directory) => {
10079
+ const { readPriorDriftReports } = await import("./curator-drift-169wgaxn.js");
10080
+ return readPriorDriftReports(directory);
10081
+ },
10082
+ readSwarmKnowledge: (directory) => readKnowledge(resolveSwarmKnowledgePath(directory))
10083
+ };
9884
10084
 
9885
10085
  // src/hooks/hive-promoter.ts
10086
+ import path17 from "path";
9886
10087
  function carryActionableFields(source) {
9887
10088
  const out = {};
9888
10089
  if (source.triggers?.length)
@@ -14533,7 +14734,7 @@ async function runFinalizeStage(ctx) {
14533
14734
  }
14534
14735
  }
14535
14736
  try {
14536
- const { CuratorConfigSchema: CCS } = await import("./schema-5vmb5dh8.js");
14737
+ const { CuratorConfigSchema: CCS } = await import("./schema-s48je8zt.js");
14537
14738
  const { config: pmLoadedConfig } = _internals17.loadPluginConfigWithMeta(ctx.directory);
14538
14739
  const curatorCfg = CCS.parse(pmLoadedConfig.curator ?? {});
14539
14740
  if (curatorCfg.enabled && curatorCfg.postmortem_enabled) {
@@ -16646,9 +16847,9 @@ var _internals21 = {
16646
16847
  readSwarmFileAsync,
16647
16848
  loadCuratorDeps: async () => {
16648
16849
  const [{ CuratorConfigSchema }, curator, { createCuratorLLMDelegate: createCuratorLLMDelegate2 }] = await Promise.all([
16649
- import("./schema-5vmb5dh8.js"),
16650
- import("./curator-zn1kmwmz.js"),
16651
- import("./curator-llm-factory-3hf9xpnd.js")
16850
+ import("./schema-s48je8zt.js"),
16851
+ import("./curator-c7dzf9rt.js"),
16852
+ import("./curator-llm-factory-v4gxfnhd.js")
16652
16853
  ]);
16653
16854
  return { CuratorConfigSchema, curator, createCuratorLLMDelegate: createCuratorLLMDelegate2 };
16654
16855
  }
@@ -17144,7 +17345,7 @@ import { fileURLToPath } from "url";
17144
17345
  // package.json
17145
17346
  var package_default = {
17146
17347
  name: "opencode-swarm",
17147
- version: "7.109.4",
17348
+ version: "7.110.1",
17148
17349
  description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
17149
17350
  main: "dist/index.js",
17150
17351
  types: "dist/index.d.ts",
@@ -17199,8 +17400,10 @@ var package_default = {
17199
17400
  ".opencode/skills/swarm-implement",
17200
17401
  ".opencode/skills/design-docs",
17201
17402
  ".opencode/skills/swarm-pr-review",
17403
+ ".opencode/skills/swarm",
17202
17404
  ".opencode/skills/swarm-pr-feedback",
17203
17405
  ".opencode/skills/swarm-pr-subscribe",
17406
+ ".opencode/skills/swarm-ci-monitor",
17204
17407
  ".opencode/skills/issue-ingest",
17205
17408
  ".opencode/skills/plan",
17206
17409
  ".opencode/skills/critic-gate",
@@ -18594,7 +18797,7 @@ async function handleDoctorCommand(directory, args) {
18594
18797
  const result = runConfigDoctor(config, directory);
18595
18798
  let output;
18596
18799
  if (enableAutoFix && result.hasAutoFixableIssues) {
18597
- const { runConfigDoctorWithFixes } = await import("./config-doctor-9vjyj9m3.js");
18800
+ const { runConfigDoctorWithFixes } = await import("./config-doctor-q5e9yzn6.js");
18598
18801
  const fixResult = await runConfigDoctorWithFixes(directory, config, true);
18599
18802
  output = formatDoctorMarkdown(fixResult.result);
18600
18803
  } else {
@@ -31867,7 +32070,7 @@ function buildDetailedHelp(commandName, entry) {
31867
32070
  async function handleHelpCommand(ctx) {
31868
32071
  const targetCommand = ctx.args.join(" ");
31869
32072
  if (!targetCommand) {
31870
- const { buildHelpText } = await import("./index-nvmy7xmj.js");
32073
+ const { buildHelpText } = await import("./index-r3ck99jh.js");
31871
32074
  return buildHelpText();
31872
32075
  }
31873
32076
  const tokens = targetCommand.split(/\s+/);
@@ -31876,7 +32079,7 @@ async function handleHelpCommand(ctx) {
31876
32079
  return _internals46.buildDetailedHelp(resolved.key, resolved.entry);
31877
32080
  }
31878
32081
  const similar = _internals46.findSimilarCommands(targetCommand);
31879
- const { buildHelpText: fullHelp } = await import("./index-nvmy7xmj.js");
32082
+ const { buildHelpText: fullHelp } = await import("./index-r3ck99jh.js");
31880
32083
  if (similar.length > 0) {
31881
32084
  return `Command '/swarm ${targetCommand}' not found.
31882
32085
 
@@ -31896,7 +32099,7 @@ Showing full help:
31896
32099
  }
31897
32100
  async function handleModeCommandWithBundledSkills(ctx, handler) {
31898
32101
  if (ctx.packageRoot) {
31899
- await syncBundledProjectSkillsIfMissingAsync(ctx.directory, ctx.packageRoot);
32102
+ await syncBundledProjectSkillsIfMissingAsync(ctx.directory, ctx.packageRoot, true);
31900
32103
  }
31901
32104
  return Promise.resolve(handler(ctx.directory, ctx.args));
31902
32105
  }
@@ -32009,7 +32212,7 @@ var COMMAND_REGISTRY = {
32009
32212
  },
32010
32213
  "guardrail explain": {
32011
32214
  handler: async (ctx) => {
32012
- const { handleGuardrailExplain } = await import("./guardrail-explain-hjz1k6w0.js");
32215
+ const { handleGuardrailExplain } = await import("./guardrail-explain-qc02w4yb.js");
32013
32216
  return handleGuardrailExplain(ctx.directory, ctx.args);
32014
32217
  },
32015
32218
  description: "Dry-run: show what the guardrails would do to a command or write target (executes nothing)",
@@ -32019,7 +32222,7 @@ var COMMAND_REGISTRY = {
32019
32222
  },
32020
32223
  "guardrail-log": {
32021
32224
  handler: async (ctx) => {
32022
- const { handleGuardrailLog } = await import("./guardrail-log-5240bawg.js");
32225
+ const { handleGuardrailLog } = await import("./guardrail-log-973nan7m.js");
32023
32226
  return handleGuardrailLog(ctx.directory, ctx.args);
32024
32227
  },
32025
32228
  description: "Read the guardrail decision log (use --blocks-only for blocks)",
@@ -33772,6 +33975,9 @@ If you see references to other agents (like @reviewer, @coder, etc.) in your ins
33772
33975
  WRONG: "I'll use the Task tool to call another agent to review this code"
33773
33976
  RIGHT: "I'll read the changed files and review them myself"
33774
33977
 
33978
+ ## KNOWLEDGE RECEIPTS
33979
+ If you call \`knowledge_recall\` or receive a knowledge directive block with a trace_id, file exactly one \`knowledge_receipt\` before final output: mark each relevant entry as applied, ignored, or contradicted with evidence, or set \`no_relevant_knowledge:true\`. The receipt records audit events; it does not replace any required \`KNOWLEDGE_APPLIED\`, \`KNOWLEDGE_IGNORED\`, or \`KNOWLEDGE_VIOLATED\` directive-compliance line.
33980
+
33775
33981
  ${READ_ONLY_LANE_GUIDANCE}
33776
33982
 
33777
33983
  ## REVIEW FOCUS
@@ -37112,4 +37318,4 @@ function createCuratorLLMDelegate(directory, mode = "init", sessionId) {
37112
37318
  };
37113
37319
  }
37114
37320
 
37115
- export { package_default, handleAcknowledgeSpecDriftCommand, handleAgentsCommand, handleAnalyzeCommand, handleArchiveCommand, DC_SAFE_TARGETS, dcNormalizeCommand, dcUnwrapWrappers, dcSplitSegments, dcValidateTargets, dcCheckJunctionCreation, dcExtractWindowsCmdTargets, dcExtractPowerShellTargets, normalizeSwarmCommandInput, canonicalCommandKey, formatCommandNotFound, executeSwarmCommand, SWARM_COMMAND_TOOL_COMMANDS, SWARM_COMMAND_TOOL_ALLOWLIST, HUMAN_ONLY_SWARM_COMMANDS, classifySwarmCommandToolUse, classifySwarmCommandChatFallbackUse, detectPosixWrites, detectWindowsWrites, resolveWriteTargets, handleAutoProceedCommand, handleBenchmarkCommand, handleBrainstormCommand, handleCheckpointCommand, handleClarifyCommand, createCuratorLLMDelegate, _internals10 as _internals, parseKnowledgeRecommendations, parseKnowledgeRecommendationsWithDiagnostics, parseStructuredCuratorBlocks, readCuratorSummary, writeCuratorSummary, filterPhaseEvents, checkPhaseCompliance, runCuratorInit, runCuratorPhase, applyCuratorKnowledgeUpdates, isHiveEligible, checkHivePromotions, createHivePromoterHook, promoteToHive, promoteFromSwarm, handleCloseCommand, handleCodebaseReviewCommand, handleConcurrencyCommand, handleConfigCommand, handleConsolidateCommand, handleCostsCommand, handleCouncilCommand, handleCurateCommand, handleDarkMatterCommand, handleDeepDiveCommand, handleDeepResearchCommand, getPluginConfigDir, getPluginCachePaths, getPluginLockFilePaths, handleDiagnoseCommand, handleDoctorCommand, handleEvidenceCommand, handleEvidenceSummaryCommand, handleExportCommand, handleFullAutoCommand, handleHandoffCommand, handleHistoryCommand, handleKnowledgeQuarantineCommand, handleKnowledgeRestoreCommand, handleKnowledgeMigrateCommand, handleKnowledgeListCommand, handleKnowledgeUnactionableCommand, handleKnowledgeRetryHardeningCommand, handleLearningCommand, handleLinkCommand, handleMemoryCommand, handleMemoryStatusCommand, handleMemoryValueLogCommand, handleMemoryMigrateCommand, handleMemoryImportCommand, handleMemoryExportCommand, handlePlanCommand, handlePreflightCommand, handlePromoteCommand, handleQaGatesCommand, handleResetCommand, handleResetSessionCommand, handleRetrieveCommand, handleRollbackCommand, handleSddStatusCommand, handleSddValidateCommand, handleSddProjectCommand, handleSddCommand, handleSimulateCommand, handleSpecifyCommand, handleStatusCommand, handleSyncPlanCommand, handleTurboCommand, handleUnlinkCommand, handleWriteRetroCommand, handleHelpCommand, COMMAND_REGISTRY, VALID_COMMANDS, _internals46 as _internals1, resolveCommand };
37321
+ export { package_default, handleAcknowledgeSpecDriftCommand, handleAgentsCommand, handleAnalyzeCommand, handleArchiveCommand, DC_SAFE_TARGETS, dcNormalizeCommand, dcUnwrapWrappers, dcSplitSegments, dcValidateTargets, dcCheckJunctionCreation, dcExtractWindowsCmdTargets, dcExtractPowerShellTargets, normalizeSwarmCommandInput, canonicalCommandKey, formatCommandNotFound, executeSwarmCommand, SWARM_COMMAND_TOOL_COMMANDS, SWARM_COMMAND_TOOL_ALLOWLIST, HUMAN_ONLY_SWARM_COMMANDS, classifySwarmCommandToolUse, classifySwarmCommandChatFallbackUse, detectPosixWrites, detectWindowsWrites, resolveWriteTargets, handleAutoProceedCommand, handleBenchmarkCommand, handleBrainstormCommand, handleCheckpointCommand, handleClarifyCommand, createCuratorLLMDelegate, _internals9 as _internals, normalizeRecommendationEntryIdToken, parseKnowledgeRecommendations, parseKnowledgeRecommendationsWithDiagnostics, parseStructuredCuratorBlocks, readCuratorSummary, writeCuratorSummary, filterPhaseEvents, checkPhaseCompliance, runCuratorInit, runCuratorPhase, applyCuratorKnowledgeUpdates, isHiveEligible, checkHivePromotions, createHivePromoterHook, promoteToHive, promoteFromSwarm, handleCloseCommand, handleCodebaseReviewCommand, handleConcurrencyCommand, handleConfigCommand, handleConsolidateCommand, handleCostsCommand, handleCouncilCommand, handleCurateCommand, handleDarkMatterCommand, handleDeepDiveCommand, handleDeepResearchCommand, getPluginConfigDir, getPluginCachePaths, getPluginLockFilePaths, handleDiagnoseCommand, handleDoctorCommand, handleEvidenceCommand, handleEvidenceSummaryCommand, handleExportCommand, handleFullAutoCommand, handleHandoffCommand, handleHistoryCommand, handleKnowledgeQuarantineCommand, handleKnowledgeRestoreCommand, handleKnowledgeMigrateCommand, handleKnowledgeListCommand, handleKnowledgeUnactionableCommand, handleKnowledgeRetryHardeningCommand, handleLearningCommand, handleLinkCommand, handleMemoryCommand, handleMemoryStatusCommand, handleMemoryValueLogCommand, handleMemoryMigrateCommand, handleMemoryImportCommand, handleMemoryExportCommand, handlePlanCommand, handlePreflightCommand, handlePromoteCommand, handleQaGatesCommand, handleResetCommand, handleResetSessionCommand, handleRetrieveCommand, handleRollbackCommand, handleSddStatusCommand, handleSddValidateCommand, handleSddProjectCommand, handleSddCommand, handleSimulateCommand, handleSpecifyCommand, handleStatusCommand, handleSyncPlanCommand, handleTurboCommand, handleUnlinkCommand, handleWriteRetroCommand, handleHelpCommand, COMMAND_REGISTRY, VALID_COMMANDS, _internals46 as _internals1, resolveCommand };