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.
- package/.opencode/skills/swarm/SKILL.md +186 -0
- package/.opencode/skills/swarm-ci-monitor/SKILL.md +369 -0
- package/.opencode/skills/swarm-pr-feedback/SKILL.md +86 -17
- package/dist/agents/explorer.d.ts +1 -1
- package/dist/cli/{config-doctor-9vjyj9m3.js → config-doctor-q5e9yzn6.js} +2 -2
- package/dist/cli/{curator-zn1kmwmz.js → curator-c7dzf9rt.js} +13 -11
- package/dist/cli/{curator-llm-factory-3hf9xpnd.js → curator-llm-factory-v4gxfnhd.js} +11 -11
- package/dist/cli/{explorer-ksr3xq7n.js → explorer-t5srbq64.js} +1 -1
- package/dist/cli/{guardrail-explain-hjz1k6w0.js → guardrail-explain-qc02w4yb.js} +12 -12
- package/dist/cli/{guardrail-log-5240bawg.js → guardrail-log-973nan7m.js} +3 -3
- package/dist/cli/{hive-promoter-j81yj97d.js → hive-promoter-kr56vt08.js} +11 -11
- package/dist/cli/{index-18690p15.js → index-0g5xsrp4.js} +19 -7
- package/dist/cli/{index-zh3qh6m7.js → index-1k5arp07.js} +2 -2
- package/dist/cli/{index-2nt4mq9n.js → index-40h0yp9e.js} +69 -1
- package/dist/cli/{index-d3txrx5q.js → index-53z1afgh.js} +1 -1
- package/dist/cli/{index-y3nx3ss0.js → index-5d0t7npd.js} +1222 -1016
- package/dist/cli/{index-p5vm9gy9.js → index-5rfxzgmt.js} +1 -1
- package/dist/cli/{index-41fgssqr.js → index-c483hnn4.js} +2 -2
- package/dist/cli/{index-b15qbvph.js → index-cmjq1kg7.js} +43 -34
- package/dist/cli/{index-09xpycan.js → index-jt58s751.js} +1 -1
- package/dist/cli/{index-mtzjbaaa.js → index-mvg66d9n.js} +3 -3
- package/dist/cli/{index-nvmy7xmj.js → index-r3ck99jh.js} +17 -17
- package/dist/cli/{index-8f10r7ay.js → index-v8pk35h7.js} +5 -5
- package/dist/cli/{index-zjh648z4.js → index-xddysq89.js} +1 -1
- package/dist/cli/{index-y5z2qdvk.js → index-yhmt3dw3.js} +2 -2
- package/dist/cli/index.js +13 -13
- package/dist/cli/{knowledge-escalator-9ksrgq7h.js → knowledge-escalator-0dkkenp2.js} +4 -4
- package/dist/cli/{knowledge-events-ymysgtrr.js → knowledge-events-bckbj6ah.js} +2 -2
- package/dist/cli/{knowledge-store-562fefjy.js → knowledge-store-dvjbdn00.js} +3 -2
- package/dist/cli/{knowledge-validator-b9nvmzjc.js → knowledge-validator-bqr936sg.js} +3 -3
- package/dist/cli/{schema-5vmb5dh8.js → schema-s48je8zt.js} +1 -1
- package/dist/cli/{skill-generator-mjfqvnn0.js → skill-generator-5rsjmmfq.js} +7 -5
- package/dist/config/bundled-skills.d.ts +1 -1
- package/dist/hooks/curator-postmortem.d.ts +33 -2
- package/dist/hooks/curator-types.d.ts +2 -2
- package/dist/hooks/curator.d.ts +2 -1
- package/dist/hooks/knowledge-link.d.ts +9 -0
- package/dist/hooks/knowledge-reader.d.ts +14 -0
- package/dist/index.js +81 -65
- package/dist/services/skill-generator.d.ts +1 -0
- package/dist/services/warning-buffer.d.ts +25 -0
- package/dist/tools/tool-metadata.d.ts +4 -4
- 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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
165
|
+
} from "./index-40h0yp9e.js";
|
|
166
166
|
import {
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
} from "./index-
|
|
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
|
|
5045
|
+
function describeBundledSkillSyncFailure(err) {
|
|
5032
5046
|
const message = err instanceof Error ? err.message : String(err);
|
|
5033
|
-
|
|
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
|
-
|
|
5182
|
-
|
|
5183
|
-
}
|
|
5195
|
+
log("synchronized bundled skill", {
|
|
5196
|
+
slug: `.opencode/skills/${slug}/SKILL.md`
|
|
5197
|
+
});
|
|
5184
5198
|
}
|
|
5185
5199
|
} catch (err) {
|
|
5186
|
-
|
|
5187
|
-
|
|
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
|
|
6557
|
-
import * as
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 (!
|
|
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(
|
|
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(
|
|
7073
|
+
await mkdir4(path12.dirname(filePath), { recursive: true });
|
|
7833
7074
|
let release = null;
|
|
7834
7075
|
try {
|
|
7835
|
-
release = await acquireLock(
|
|
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(
|
|
7103
|
+
await mkdir4(path12.dirname(filePath), { recursive: true });
|
|
7863
7104
|
let release = null;
|
|
7864
7105
|
try {
|
|
7865
|
-
release = await acquireLock(
|
|
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 =
|
|
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
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
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 (!
|
|
7537
|
+
if (!_internals8.existsSync(resolved))
|
|
8297
7538
|
return processed;
|
|
8298
|
-
const raw =
|
|
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 =
|
|
8319
|
-
if (!
|
|
8320
|
-
|
|
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
|
-
|
|
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 =
|
|
8364
|
-
if (!
|
|
8365
|
-
|
|
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:
|
|
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
|
-
|
|
7619
|
+
_internals8.appendFileSync(resolved, `${JSON.stringify(fullEntry)}
|
|
8379
7620
|
`, "utf-8");
|
|
8380
7621
|
try {
|
|
8381
|
-
const stat3 =
|
|
7622
|
+
const stat3 = _internals8.statSync(resolved);
|
|
8382
7623
|
if (stat3.size > SKILL_USAGE_LOG_ROTATE_BYTES) {
|
|
8383
|
-
|
|
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 (!
|
|
7630
|
+
if (!_internals8.existsSync(resolved)) {
|
|
8390
7631
|
return [];
|
|
8391
7632
|
}
|
|
8392
|
-
const raw =
|
|
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 (!
|
|
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 =
|
|
7681
|
+
const stat3 = _internals8.statSync(logPath);
|
|
8441
7682
|
const start = Math.max(0, stat3.size - boundedMaxBytes);
|
|
8442
|
-
const fd =
|
|
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
|
-
|
|
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
|
-
|
|
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 (!
|
|
7753
|
+
if (!_internals8.existsSync(resolved)) {
|
|
8513
7754
|
return { pruned: 0, remaining: 0 };
|
|
8514
7755
|
}
|
|
8515
|
-
const raw =
|
|
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 =
|
|
8540
|
-
const tmpPath2 =
|
|
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
|
-
|
|
8546
|
-
|
|
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 (
|
|
8551
|
-
|
|
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 =
|
|
8583
|
-
const tmpPath =
|
|
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
|
-
|
|
8591
|
-
|
|
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 (
|
|
8596
|
-
|
|
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 =
|
|
8613
|
-
const baseDir =
|
|
8614
|
-
const isContained = process.platform === "win32" ? absolute.toLowerCase().startsWith((baseDir +
|
|
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 (!
|
|
7859
|
+
if (!_internals8.existsSync(absolute)) {
|
|
8619
7860
|
return [];
|
|
8620
7861
|
}
|
|
8621
|
-
const content =
|
|
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
|
|
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
|
|
8769
|
-
const usageEntries =
|
|
8770
|
-
const allArchivedIds = await
|
|
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
|
|
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
|
|
8801
|
-
const sourceIds =
|
|
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
|
|
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:
|
|
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"
|
|
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 =
|
|
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 =
|
|
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(
|
|
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 =
|
|
9088
|
-
const isDispatched = agentsDispatched.some((a) =>
|
|
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 =
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
|
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 ?
|
|
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 =
|
|
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) =>
|
|
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 =
|
|
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
|
-
|
|
9416
|
-
|
|
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:
|
|
9422
|
-
|
|
8690
|
+
entry_id: diagnostic.line,
|
|
8691
|
+
reason: diagnostic.reason
|
|
9423
8692
|
});
|
|
9424
|
-
|
|
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
|
|
8758
|
+
await _internals9.writeCuratorSummary(directory, updatedSummary);
|
|
9490
8759
|
if (knowledgeApplicationFindings.length > 0) {
|
|
9491
8760
|
try {
|
|
9492
|
-
const evidenceDir =
|
|
8761
|
+
const evidenceDir = path15.join(directory, ".swarm", "evidence", String(phase));
|
|
9493
8762
|
fs5.mkdirSync(evidenceDir, { recursive: true });
|
|
9494
|
-
const findingsPath =
|
|
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 =
|
|
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-
|
|
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
|
|
9532
|
-
const usageEntries =
|
|
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
|
|
9557
|
-
const fm =
|
|
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
|
|
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
|
|
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
|
-
|
|
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-
|
|
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
|
-
|
|
9579
|
+
results.push(JSON.parse(line));
|
|
9790
9580
|
} catch {}
|
|
9791
9581
|
}
|
|
9582
|
+
return results;
|
|
9583
|
+
} catch {
|
|
9584
|
+
return [];
|
|
9792
9585
|
}
|
|
9793
|
-
|
|
9794
|
-
|
|
9795
|
-
|
|
9796
|
-
|
|
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
|
-
|
|
9802
|
-
|
|
9803
|
-
|
|
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
|
-
|
|
9806
|
-
|
|
9807
|
-
|
|
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
|
|
9817
|
-
|
|
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
|
-
|
|
9821
|
-
|
|
9822
|
-
|
|
9823
|
-
|
|
9824
|
-
|
|
9825
|
-
|
|
9826
|
-
|
|
9827
|
-
|
|
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
|
-
|
|
9830
|
-
|
|
9831
|
-
skipped++;
|
|
9832
|
-
continue;
|
|
9701
|
+
if (neverApplied.length > 10) {
|
|
9702
|
+
lines.push(`- ... and ${neverApplied.length - 10} more`);
|
|
9833
9703
|
}
|
|
9834
|
-
|
|
9835
|
-
|
|
9836
|
-
|
|
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
|
-
|
|
9839
|
-
|
|
9840
|
-
|
|
9841
|
-
|
|
9842
|
-
|
|
9843
|
-
|
|
9844
|
-
|
|
9845
|
-
|
|
9846
|
-
|
|
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
|
-
|
|
9850
|
-
|
|
9851
|
-
|
|
9852
|
-
|
|
9853
|
-
|
|
9854
|
-
|
|
9855
|
-
|
|
9856
|
-
|
|
9857
|
-
|
|
9858
|
-
|
|
9859
|
-
|
|
9860
|
-
|
|
9861
|
-
|
|
9862
|
-
|
|
9863
|
-
|
|
9864
|
-
|
|
9865
|
-
|
|
9866
|
-
|
|
9867
|
-
|
|
9868
|
-
|
|
9869
|
-
|
|
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
|
-
|
|
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
|
|
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-
|
|
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-
|
|
16650
|
-
import("./curator-
|
|
16651
|
-
import("./curator-llm-factory-
|
|
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.
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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,
|
|
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 };
|