opencode-swarm 7.110.0 → 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/dist/agents/explorer.d.ts +1 -1
- package/dist/cli/{config-doctor-9vjyj9m3.js → config-doctor-q5e9yzn6.js} +2 -2
- package/dist/cli/{curator-4qqzpd85.js → curator-c7dzf9rt.js} +13 -11
- package/dist/cli/{curator-llm-factory-y4kvr1ph.js → curator-llm-factory-v4gxfnhd.js} +11 -11
- package/dist/cli/{explorer-ksr3xq7n.js → explorer-t5srbq64.js} +1 -1
- package/dist/cli/{guardrail-explain-10hh57cr.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-wvxqyayt.js → hive-promoter-kr56vt08.js} +11 -11
- package/dist/cli/{index-18690p15.js → index-0g5xsrp4.js} +19 -7
- package/dist/cli/{index-xhsba2sn.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-1mc2yfc2.js → index-5d0t7npd.js} +1218 -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-khj0knr0.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/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 +1 -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",
|
|
@@ -5030,9 +5042,9 @@ var BUNDLED_PROJECT_SKILLS = [
|
|
|
5030
5042
|
];
|
|
5031
5043
|
var MAX_SKILL_FILES = 64;
|
|
5032
5044
|
var MAX_SKILL_BYTES = 512000;
|
|
5033
|
-
function
|
|
5045
|
+
function describeBundledSkillSyncFailure(err) {
|
|
5034
5046
|
const message = err instanceof Error ? err.message : String(err);
|
|
5035
|
-
|
|
5047
|
+
return `[opencode-swarm] Could not install bundled project skills; continuing without sync: ${message}`;
|
|
5036
5048
|
}
|
|
5037
5049
|
async function isSymbolicLinkAsync(p) {
|
|
5038
5050
|
try {
|
|
@@ -5180,13 +5192,17 @@ async function syncBundledProjectSkillsIfMissingAsync(projectDirectory, packageR
|
|
|
5180
5192
|
if (!await ensureNotSymlinkedDirectoryAsync(destDir))
|
|
5181
5193
|
continue;
|
|
5182
5194
|
await copyBundledDirectoryBoundedAsync(sourceDir, destDir);
|
|
5183
|
-
|
|
5184
|
-
|
|
5185
|
-
}
|
|
5195
|
+
log("synchronized bundled skill", {
|
|
5196
|
+
slug: `.opencode/skills/${slug}/SKILL.md`
|
|
5197
|
+
});
|
|
5186
5198
|
}
|
|
5187
5199
|
} catch (err) {
|
|
5188
|
-
|
|
5189
|
-
|
|
5200
|
+
const failureMsg = describeBundledSkillSyncFailure(err);
|
|
5201
|
+
if (quiet) {
|
|
5202
|
+
advisoryWarn(failureMsg);
|
|
5203
|
+
} else {
|
|
5204
|
+
console.warn(failureMsg);
|
|
5205
|
+
}
|
|
5190
5206
|
}
|
|
5191
5207
|
}
|
|
5192
5208
|
|
|
@@ -6555,790 +6571,13 @@ import { promises as fs9 } from "fs";
|
|
|
6555
6571
|
import path28 from "path";
|
|
6556
6572
|
|
|
6557
6573
|
// src/hooks/curator-postmortem.ts
|
|
6558
|
-
import { existsSync as
|
|
6559
|
-
import * as
|
|
6560
|
-
var MAX_INPUT_TEXT_CHARS = 500;
|
|
6561
|
-
var MAX_KNOWLEDGE_ENTRIES = 500;
|
|
6562
|
-
var MAX_PROPOSALS = 50;
|
|
6563
|
-
var MAX_RETROSPECTIVES = 50;
|
|
6564
|
-
var MAX_DRIFT_REPORTS = 50;
|
|
6565
|
-
var MAX_UNACTIONABLE = 1000;
|
|
6566
|
-
async function collectKnowledgeSummary(directory, scope = "project", sessionID) {
|
|
6567
|
-
const entries = await readKnowledge(resolveSwarmKnowledgePath(directory), MAX_KNOWLEDGE_ENTRIES);
|
|
6568
|
-
let events = await readKnowledgeEvents(directory, MAX_KNOWLEDGE_ENTRIES * 4);
|
|
6569
|
-
if (scope === "session" && sessionID) {
|
|
6570
|
-
events = events.filter((e) => e.session_id === sessionID);
|
|
6571
|
-
}
|
|
6572
|
-
const countsMap = new Map;
|
|
6573
|
-
for (const e of events) {
|
|
6574
|
-
if (e.type !== "applied" && e.type !== "violated" && e.type !== "ignored")
|
|
6575
|
-
continue;
|
|
6576
|
-
const kid = e.knowledge_id ?? e.entry_id;
|
|
6577
|
-
if (!kid)
|
|
6578
|
-
continue;
|
|
6579
|
-
const c = countsMap.get(kid) ?? { applied: 0, violated: 0, ignored: 0 };
|
|
6580
|
-
if (e.type === "applied")
|
|
6581
|
-
c.applied++;
|
|
6582
|
-
else if (e.type === "violated")
|
|
6583
|
-
c.violated++;
|
|
6584
|
-
else if (e.type === "ignored")
|
|
6585
|
-
c.ignored++;
|
|
6586
|
-
countsMap.set(kid, c);
|
|
6587
|
-
}
|
|
6588
|
-
return entries.map((entry) => {
|
|
6589
|
-
const c = countsMap.get(entry.id) ?? {
|
|
6590
|
-
applied: 0,
|
|
6591
|
-
violated: 0,
|
|
6592
|
-
ignored: 0
|
|
6593
|
-
};
|
|
6594
|
-
return {
|
|
6595
|
-
id: entry.id,
|
|
6596
|
-
lesson: entry.lesson,
|
|
6597
|
-
applied: c.applied,
|
|
6598
|
-
violated: c.violated,
|
|
6599
|
-
ignored: c.ignored,
|
|
6600
|
-
confidence: entry.confidence ?? 0.5,
|
|
6601
|
-
status: entry.status ?? "active"
|
|
6602
|
-
};
|
|
6603
|
-
});
|
|
6604
|
-
}
|
|
6605
|
-
function extractSection(text, sectionName) {
|
|
6606
|
-
const pattern = new RegExp(`^${sectionName}:\\s*\\n([\\s\\S]*?)(?=\\n[A-Z_]+:\\s*(?:\\n|$)|$)`, "m");
|
|
6607
|
-
const match = text.match(pattern);
|
|
6608
|
-
return match?.[1]?.trim() || null;
|
|
6609
|
-
}
|
|
6610
|
-
function normalizeRecommendationAction(raw) {
|
|
6611
|
-
const value = String(raw ?? "").toLowerCase().trim();
|
|
6612
|
-
if (value === "promote" || value === "promote_to_hive")
|
|
6613
|
-
return "promote";
|
|
6614
|
-
if (value === "archive" || value === "flag_stale")
|
|
6615
|
-
return "archive";
|
|
6616
|
-
if (value === "rewrite")
|
|
6617
|
-
return "rewrite";
|
|
6618
|
-
if (value === "flag_contradiction")
|
|
6619
|
-
return "flag_contradiction";
|
|
6620
|
-
if (value === "merge")
|
|
6621
|
-
return null;
|
|
6622
|
-
return null;
|
|
6623
|
-
}
|
|
6624
|
-
function normalizeProposalSlug(raw) {
|
|
6625
|
-
return raw.trim().replace(/^proposals[\\/]/, "").replace(/\.md$/i, "").replace(/\.json$/i, "");
|
|
6626
|
-
}
|
|
6627
|
-
function parseStructuredPostMortemActions(llmOutput) {
|
|
6628
|
-
const parsed = {
|
|
6629
|
-
summary: extractSection(llmOutput, "SUMMARY"),
|
|
6630
|
-
recommendations: [],
|
|
6631
|
-
queueTriage: [],
|
|
6632
|
-
diagnostics: []
|
|
6633
|
-
};
|
|
6634
|
-
const fence = /```(?:json|jsonc)?\s+postmortem_actions\s*\n([\s\S]*?)\n```/g;
|
|
6635
|
-
for (const match of llmOutput.matchAll(fence)) {
|
|
6636
|
-
let data;
|
|
6637
|
-
try {
|
|
6638
|
-
data = JSON.parse(match[1]);
|
|
6639
|
-
} catch (err) {
|
|
6640
|
-
parsed.diagnostics.push(`postmortem_actions malformed_json: ${err instanceof Error ? err.message : String(err)}`);
|
|
6641
|
-
continue;
|
|
6642
|
-
}
|
|
6643
|
-
if (!data || typeof data !== "object" || Array.isArray(data)) {
|
|
6644
|
-
parsed.diagnostics.push("postmortem_actions expected object");
|
|
6645
|
-
continue;
|
|
6646
|
-
}
|
|
6647
|
-
const obj = data;
|
|
6648
|
-
if (typeof obj.summary === "string" && obj.summary.trim()) {
|
|
6649
|
-
parsed.summary = obj.summary.trim().slice(0, 1000);
|
|
6650
|
-
}
|
|
6651
|
-
if (Array.isArray(obj.curation_recommendations)) {
|
|
6652
|
-
for (const [index, item] of obj.curation_recommendations.entries()) {
|
|
6653
|
-
if (!item || typeof item !== "object") {
|
|
6654
|
-
parsed.diagnostics.push(`curation_recommendations[${index}] invalid object`);
|
|
6655
|
-
continue;
|
|
6656
|
-
}
|
|
6657
|
-
const rec = item;
|
|
6658
|
-
const action = normalizeRecommendationAction(rec.action);
|
|
6659
|
-
const lesson = String(rec.lesson ?? rec.reason ?? "").trim();
|
|
6660
|
-
const reason = String(rec.reason ?? lesson).trim();
|
|
6661
|
-
if (!action || !reason) {
|
|
6662
|
-
parsed.diagnostics.push(`curation_recommendations[${index}] missing action/reason`);
|
|
6663
|
-
continue;
|
|
6664
|
-
}
|
|
6665
|
-
if (action === "rewrite" && lesson.length < 15) {
|
|
6666
|
-
parsed.diagnostics.push(`curation_recommendations[${index}] rewrite missing lesson`);
|
|
6667
|
-
continue;
|
|
6668
|
-
}
|
|
6669
|
-
parsed.recommendations.push({
|
|
6670
|
-
action,
|
|
6671
|
-
entry_id: typeof rec.entry_id === "string" && rec.entry_id.trim() ? rec.entry_id.trim() : undefined,
|
|
6672
|
-
lesson: (lesson || reason).slice(0, 280),
|
|
6673
|
-
reason: reason.slice(0, 280),
|
|
6674
|
-
category: typeof rec.category === "string" ? rec.category : undefined,
|
|
6675
|
-
confidence: typeof rec.confidence === "number" ? rec.confidence : undefined
|
|
6676
|
-
});
|
|
6677
|
-
}
|
|
6678
|
-
}
|
|
6679
|
-
if (Array.isArray(obj.queue_triage)) {
|
|
6680
|
-
for (const [index, item] of obj.queue_triage.entries()) {
|
|
6681
|
-
if (!item || typeof item !== "object") {
|
|
6682
|
-
parsed.diagnostics.push(`queue_triage[${index}] invalid object`);
|
|
6683
|
-
continue;
|
|
6684
|
-
}
|
|
6685
|
-
const triage = item;
|
|
6686
|
-
const proposalId = String(triage.proposal_id ?? "").trim();
|
|
6687
|
-
const action = String(triage.action ?? "").toLowerCase().trim();
|
|
6688
|
-
if (!proposalId || action !== "apply" && action !== "reject") {
|
|
6689
|
-
parsed.diagnostics.push(`queue_triage[${index}] missing proposal_id/action`);
|
|
6690
|
-
continue;
|
|
6691
|
-
}
|
|
6692
|
-
parsed.queueTriage.push({
|
|
6693
|
-
proposal_id: proposalId.slice(0, 120),
|
|
6694
|
-
action,
|
|
6695
|
-
reason: String(triage.reason ?? "").slice(0, 280)
|
|
6696
|
-
});
|
|
6697
|
-
}
|
|
6698
|
-
}
|
|
6699
|
-
}
|
|
6700
|
-
return parsed;
|
|
6701
|
-
}
|
|
6702
|
-
function parseLegacyPostMortemActions(llmOutput) {
|
|
6703
|
-
const parsed = {
|
|
6704
|
-
summary: extractSection(llmOutput, "SUMMARY"),
|
|
6705
|
-
recommendations: [],
|
|
6706
|
-
queueTriage: [],
|
|
6707
|
-
diagnostics: []
|
|
6708
|
-
};
|
|
6709
|
-
const curation = extractSection(llmOutput, "CURATION_RECOMMENDATIONS");
|
|
6710
|
-
if (curation) {
|
|
6711
|
-
let recIndex = 0;
|
|
6712
|
-
for (const line of curation.split(`
|
|
6713
|
-
`)) {
|
|
6714
|
-
const trimmed = line.trim();
|
|
6715
|
-
if (!trimmed.startsWith("-"))
|
|
6716
|
-
continue;
|
|
6717
|
-
const body = trimmed.replace(/^-\s*/, "");
|
|
6718
|
-
const [head2, reasonPart = ""] = body.split(/\s+\u2014\s+|\s+-\s+/, 2);
|
|
6719
|
-
const [rawAction, rest = ""] = head2.split(/:\s*/, 2);
|
|
6720
|
-
const lowerRaw = rawAction.toLowerCase().trim();
|
|
6721
|
-
const action = normalizeRecommendationAction(rawAction);
|
|
6722
|
-
if (!action) {
|
|
6723
|
-
if (lowerRaw === "merge") {
|
|
6724
|
-
parsed.diagnostics.push("legacy curation_recommendations unsupported action 'merge' \u2014 dropped");
|
|
6725
|
-
} else if (lowerRaw) {
|
|
6726
|
-
parsed.diagnostics.push(`legacy curation_recommendations unrecognized action '${lowerRaw}'`);
|
|
6727
|
-
}
|
|
6728
|
-
continue;
|
|
6729
|
-
}
|
|
6730
|
-
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);
|
|
6731
|
-
const reason = (reasonPart || rest).trim();
|
|
6732
|
-
if (!reason) {
|
|
6733
|
-
parsed.diagnostics.push(`legacy curation_recommendations[${recIndex}] missing reason`);
|
|
6734
|
-
continue;
|
|
6735
|
-
}
|
|
6736
|
-
parsed.recommendations.push({
|
|
6737
|
-
action,
|
|
6738
|
-
entry_id: idMatch?.[0],
|
|
6739
|
-
lesson: reason.slice(0, 280),
|
|
6740
|
-
reason: reason.slice(0, 280)
|
|
6741
|
-
});
|
|
6742
|
-
recIndex++;
|
|
6743
|
-
}
|
|
6744
|
-
}
|
|
6745
|
-
const queue = extractSection(llmOutput, "QUEUE_TRIAGE");
|
|
6746
|
-
if (queue) {
|
|
6747
|
-
for (const line of queue.split(`
|
|
6748
|
-
`)) {
|
|
6749
|
-
const trimmed = line.trim();
|
|
6750
|
-
if (!trimmed.startsWith("-"))
|
|
6751
|
-
continue;
|
|
6752
|
-
if (trimmed.includes(":")) {
|
|
6753
|
-
const match = trimmed.match(/^-\s*([^:]+):\s*(APPLY|REJECT)\b\s*(?:\u2014|-)?\s*(.*)$/i);
|
|
6754
|
-
if (!match) {
|
|
6755
|
-
parsed.diagnostics.push("legacy queue_triage malformed line");
|
|
6756
|
-
continue;
|
|
6757
|
-
}
|
|
6758
|
-
parsed.queueTriage.push({
|
|
6759
|
-
proposal_id: match[1].trim().slice(0, 120),
|
|
6760
|
-
action: match[2].toLowerCase(),
|
|
6761
|
-
reason: match[3].trim().slice(0, 280)
|
|
6762
|
-
});
|
|
6763
|
-
}
|
|
6764
|
-
}
|
|
6765
|
-
}
|
|
6766
|
-
return parsed;
|
|
6767
|
-
}
|
|
6768
|
-
function parsePostMortemActions(llmOutput) {
|
|
6769
|
-
const structured = parseStructuredPostMortemActions(llmOutput);
|
|
6770
|
-
const legacy = parseLegacyPostMortemActions(llmOutput);
|
|
6771
|
-
return {
|
|
6772
|
-
summary: structured.summary ?? legacy.summary,
|
|
6773
|
-
recommendations: structured.recommendations.length > 0 ? structured.recommendations : legacy.recommendations,
|
|
6774
|
-
queueTriage: structured.queueTriage.length > 0 ? structured.queueTriage : legacy.queueTriage,
|
|
6775
|
-
diagnostics: [...structured.diagnostics, ...legacy.diagnostics]
|
|
6776
|
-
};
|
|
6777
|
-
}
|
|
6778
|
-
async function executePostMortemActions(directory, parsed, options) {
|
|
6779
|
-
const warnings = [];
|
|
6780
|
-
const result = {
|
|
6781
|
-
knowledge_applied: 0,
|
|
6782
|
-
knowledge_skipped: 0,
|
|
6783
|
-
hive_promotions: 0,
|
|
6784
|
-
hive_encounters_incremented: 0,
|
|
6785
|
-
hive_advancements: 0,
|
|
6786
|
-
proposals_approved: 0,
|
|
6787
|
-
proposals_rejected: 0,
|
|
6788
|
-
proposals_skipped: 0
|
|
6789
|
-
};
|
|
6790
|
-
const knowledgeConfig = options.knowledgeConfig ?? await _internals7.loadDefaultKnowledgeConfig();
|
|
6791
|
-
if (parsed.recommendations.length > 0) {
|
|
6792
|
-
try {
|
|
6793
|
-
const knowledgeResult = await _internals7.applyCuratorKnowledgeUpdates(directory, parsed.recommendations, knowledgeConfig);
|
|
6794
|
-
result.knowledge_applied = knowledgeResult.applied;
|
|
6795
|
-
result.knowledge_skipped = knowledgeResult.skipped;
|
|
6796
|
-
} catch (err) {
|
|
6797
|
-
warnings.push(`Post-mortem knowledge actions failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
6798
|
-
}
|
|
6799
|
-
try {
|
|
6800
|
-
const entries = await _internals7.readSwarmKnowledge(directory);
|
|
6801
|
-
const hiveResult = await _internals7.checkHivePromotions(entries, knowledgeConfig);
|
|
6802
|
-
result.hive_promotions = hiveResult.new_promotions;
|
|
6803
|
-
result.hive_encounters_incremented = hiveResult.encounters_incremented;
|
|
6804
|
-
result.hive_advancements = hiveResult.advancements;
|
|
6805
|
-
} catch (err) {
|
|
6806
|
-
warnings.push(`Post-mortem hive promotion check failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
6807
|
-
}
|
|
6808
|
-
}
|
|
6809
|
-
if (parsed.queueTriage.length > 0) {
|
|
6810
|
-
try {
|
|
6811
|
-
const proposalResult = await _internals7.applyProposalTriage(directory, parsed.queueTriage);
|
|
6812
|
-
result.proposals_approved = proposalResult.approved.length;
|
|
6813
|
-
result.proposals_rejected = proposalResult.rejected.length;
|
|
6814
|
-
result.proposals_skipped = proposalResult.skipped.length;
|
|
6815
|
-
} catch (err) {
|
|
6816
|
-
warnings.push(`Post-mortem proposal triage failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
6817
|
-
}
|
|
6818
|
-
}
|
|
6819
|
-
return { result, warnings };
|
|
6820
|
-
}
|
|
6821
|
-
async function repairPostMortemActions(llmOutput, diagnostics, options) {
|
|
6822
|
-
if (!options.llmDelegate || diagnostics.length === 0)
|
|
6823
|
-
return null;
|
|
6824
|
-
const ac = new AbortController;
|
|
6825
|
-
const timer = setTimeout(() => ac.abort(), 30000);
|
|
6826
|
-
try {
|
|
6827
|
-
const repairPrompt = [
|
|
6828
|
-
"Repair the supplied CURATOR_POSTMORTEM output into one valid fenced JSON block.",
|
|
6829
|
-
"Return only this fence:",
|
|
6830
|
-
"```json postmortem_actions",
|
|
6831
|
-
'{"summary":"...","curation_recommendations":[],"queue_triage":[]}',
|
|
6832
|
-
"```",
|
|
6833
|
-
"Allowed curation action values: promote, archive, rewrite, flag_contradiction.",
|
|
6834
|
-
"Allowed queue_triage action values: apply, reject.",
|
|
6835
|
-
"Do not include unsupported actions such as merge.",
|
|
6836
|
-
"Diagnostics:",
|
|
6837
|
-
diagnostics.join("; "),
|
|
6838
|
-
"Original output:",
|
|
6839
|
-
llmOutput.slice(0, 8000)
|
|
6840
|
-
].join(`
|
|
6841
|
-
`);
|
|
6842
|
-
const repaired = await options.llmDelegate("", repairPrompt, ac.signal);
|
|
6843
|
-
const parsed = _internals7.parsePostMortemActions(repaired);
|
|
6844
|
-
if (parsed.diagnostics.length === 0) {
|
|
6845
|
-
return parsed;
|
|
6846
|
-
}
|
|
6847
|
-
return null;
|
|
6848
|
-
} catch {
|
|
6849
|
-
return null;
|
|
6850
|
-
} finally {
|
|
6851
|
-
clearTimeout(timer);
|
|
6852
|
-
}
|
|
6853
|
-
}
|
|
6854
|
-
function readJsonlFile(filePath, maxLines) {
|
|
6855
|
-
try {
|
|
6856
|
-
if (!existsSync7(filePath))
|
|
6857
|
-
return [];
|
|
6858
|
-
const content = readFileSync4(filePath, "utf-8");
|
|
6859
|
-
const results = [];
|
|
6860
|
-
const max = maxLines !== undefined && maxLines > 0 ? maxLines : Infinity;
|
|
6861
|
-
for (const line of content.split(`
|
|
6862
|
-
`)) {
|
|
6863
|
-
if (results.length >= max)
|
|
6864
|
-
break;
|
|
6865
|
-
if (!line.trim())
|
|
6866
|
-
continue;
|
|
6867
|
-
try {
|
|
6868
|
-
results.push(JSON.parse(line));
|
|
6869
|
-
} catch {}
|
|
6870
|
-
}
|
|
6871
|
-
return results;
|
|
6872
|
-
} catch {
|
|
6873
|
-
return [];
|
|
6874
|
-
}
|
|
6875
|
-
}
|
|
6876
|
-
function collectRetrospectives(directory) {
|
|
6877
|
-
const results = [];
|
|
6878
|
-
const evidenceDir = path12.join(directory, ".swarm", "evidence");
|
|
6879
|
-
try {
|
|
6880
|
-
if (!existsSync7(evidenceDir))
|
|
6881
|
-
return results;
|
|
6882
|
-
const retroDirs = readdirSync(evidenceDir, { withFileTypes: true }).filter((e) => e.isDirectory() && e.name.startsWith("retro-")).slice(0, MAX_RETROSPECTIVES);
|
|
6883
|
-
for (const entry of retroDirs) {
|
|
6884
|
-
const retroPath = path12.join(evidenceDir, entry.name, "evidence.json");
|
|
6885
|
-
if (existsSync7(retroPath)) {
|
|
6886
|
-
try {
|
|
6887
|
-
results.push(readFileSync4(retroPath, "utf-8"));
|
|
6888
|
-
} catch {}
|
|
6889
|
-
}
|
|
6890
|
-
}
|
|
6891
|
-
} catch {}
|
|
6892
|
-
return results;
|
|
6893
|
-
}
|
|
6894
|
-
async function collectDriftReports(directory) {
|
|
6895
|
-
try {
|
|
6896
|
-
const reports = await _internals7.readPriorDriftReports(directory);
|
|
6897
|
-
return reports.slice(-MAX_DRIFT_REPORTS).map((report) => JSON.stringify(report, null, 2));
|
|
6898
|
-
} catch {
|
|
6899
|
-
return [];
|
|
6900
|
-
}
|
|
6901
|
-
}
|
|
6902
|
-
function collectPendingProposals(directory) {
|
|
6903
|
-
const results = [];
|
|
6904
|
-
const insightPath = path12.join(directory, ".swarm", "insight-candidates.jsonl");
|
|
6905
|
-
if (existsSync7(insightPath)) {
|
|
6906
|
-
try {
|
|
6907
|
-
results.push({
|
|
6908
|
-
source: "insight-candidates",
|
|
6909
|
-
content: readFileSync4(insightPath, "utf-8")
|
|
6910
|
-
});
|
|
6911
|
-
} catch {}
|
|
6912
|
-
}
|
|
6913
|
-
const proposalsDir = path12.join(directory, ".swarm", "skills", "proposals");
|
|
6914
|
-
try {
|
|
6915
|
-
if (existsSync7(proposalsDir)) {
|
|
6916
|
-
const proposalFiles = readdirSync(proposalsDir).filter((e) => e.endsWith(".md") || e.endsWith(".json")).slice(0, MAX_PROPOSALS);
|
|
6917
|
-
for (const entry of proposalFiles) {
|
|
6918
|
-
try {
|
|
6919
|
-
results.push({
|
|
6920
|
-
source: `proposals/${entry}`,
|
|
6921
|
-
content: readFileSync4(path12.join(proposalsDir, entry), "utf-8")
|
|
6922
|
-
});
|
|
6923
|
-
} catch {}
|
|
6924
|
-
}
|
|
6925
|
-
}
|
|
6926
|
-
} catch {}
|
|
6927
|
-
return results;
|
|
6928
|
-
}
|
|
6929
|
-
function isReportValid(reportPath) {
|
|
6930
|
-
try {
|
|
6931
|
-
if (!existsSync7(reportPath))
|
|
6932
|
-
return false;
|
|
6933
|
-
const content = readFileSync4(reportPath, "utf-8").trim();
|
|
6934
|
-
if (content.length === 0)
|
|
6935
|
-
return false;
|
|
6936
|
-
if (!content.startsWith("# Post-Mortem Report:"))
|
|
6937
|
-
return false;
|
|
6938
|
-
return true;
|
|
6939
|
-
} catch {
|
|
6940
|
-
return false;
|
|
6941
|
-
}
|
|
6942
|
-
}
|
|
6943
|
-
async function acquirePostMortemLock(directory, planId) {
|
|
6944
|
-
const result = await tryAcquireLock(directory, `post-mortem-${planId}.lock`, "curator-postmortem", planId);
|
|
6945
|
-
if (result.acquired) {
|
|
6946
|
-
return { acquired: true, release: result.lock?._release };
|
|
6947
|
-
}
|
|
6948
|
-
return { acquired: false };
|
|
6949
|
-
}
|
|
6950
|
-
function buildDataOnlyReport(planId, planSummary, knowledgeSummary, curatorDigest, proposals, unactionable, retrospectives, driftReports) {
|
|
6951
|
-
const now = new Date().toISOString();
|
|
6952
|
-
const lines = [];
|
|
6953
|
-
lines.push(`# Post-Mortem Report: ${planId}`);
|
|
6954
|
-
lines.push(`Generated: ${now}`);
|
|
6955
|
-
lines.push("");
|
|
6956
|
-
lines.push("## Project Summary");
|
|
6957
|
-
lines.push(planSummary);
|
|
6958
|
-
lines.push("");
|
|
6959
|
-
lines.push("## Knowledge Metrics");
|
|
6960
|
-
const totalEntries = knowledgeSummary.length;
|
|
6961
|
-
const totalApplied = knowledgeSummary.reduce((s, e) => s + e.applied, 0);
|
|
6962
|
-
const totalViolated = knowledgeSummary.reduce((s, e) => s + e.violated, 0);
|
|
6963
|
-
const totalIgnored = knowledgeSummary.reduce((s, e) => s + e.ignored, 0);
|
|
6964
|
-
const neverApplied = knowledgeSummary.filter((e) => e.applied === 0 && e.violated === 0 && e.ignored === 0);
|
|
6965
|
-
lines.push(`- Total entries: ${totalEntries}`);
|
|
6966
|
-
lines.push(`- Application events: ${totalApplied} applied, ${totalViolated} violated, ${totalIgnored} ignored`);
|
|
6967
|
-
lines.push(`- Never-applied entries: ${neverApplied.length}`);
|
|
6968
|
-
if (totalApplied + totalViolated > 0) {
|
|
6969
|
-
const appRate = (totalApplied / (totalApplied + totalViolated) * 100).toFixed(1);
|
|
6970
|
-
lines.push(`- Application rate: ${appRate}%`);
|
|
6971
|
-
}
|
|
6972
|
-
lines.push("");
|
|
6973
|
-
if (neverApplied.length > 0) {
|
|
6974
|
-
lines.push("### Never-Applied Entries (review for retirement)");
|
|
6975
|
-
for (const e of neverApplied.slice(0, 10)) {
|
|
6976
|
-
lines.push(`- \`${e.id}\` (confidence: ${e.confidence.toFixed(2)}): ${e.lesson.slice(0, 80)}`);
|
|
6977
|
-
}
|
|
6978
|
-
if (neverApplied.length > 10) {
|
|
6979
|
-
lines.push(`- ... and ${neverApplied.length - 10} more`);
|
|
6980
|
-
}
|
|
6981
|
-
lines.push("");
|
|
6982
|
-
}
|
|
6983
|
-
const highViolation = knowledgeSummary.filter((e) => e.violated > 0).sort((a, b) => b.violated - a.violated).slice(0, 5);
|
|
6984
|
-
if (highViolation.length > 0) {
|
|
6985
|
-
lines.push("### High-Violation Entries");
|
|
6986
|
-
for (const e of highViolation) {
|
|
6987
|
-
lines.push(`- \`${e.id}\` \u2014 ${e.violated} violations, ${e.applied} applied: ${e.lesson.slice(0, 80)}`);
|
|
6988
|
-
}
|
|
6989
|
-
lines.push("");
|
|
6990
|
-
}
|
|
6991
|
-
lines.push("## Queue Status");
|
|
6992
|
-
lines.push(`- Pending proposals: ${proposals.length}`);
|
|
6993
|
-
lines.push(`- Unactionable quarantine: ${unactionable.length}`);
|
|
6994
|
-
for (const p of proposals) {
|
|
6995
|
-
lines.push(` - ${p.source}`);
|
|
6996
|
-
}
|
|
6997
|
-
lines.push("");
|
|
6998
|
-
if (retrospectives.length > 0) {
|
|
6999
|
-
lines.push("## Retrospectives");
|
|
7000
|
-
lines.push(`${retrospectives.length} phase retrospective(s) recorded.`);
|
|
7001
|
-
lines.push("");
|
|
7002
|
-
}
|
|
7003
|
-
if (driftReports.length > 0) {
|
|
7004
|
-
lines.push("## Drift Reports");
|
|
7005
|
-
for (const dr of driftReports) {
|
|
7006
|
-
try {
|
|
7007
|
-
const parsed = JSON.parse(dr);
|
|
7008
|
-
lines.push(`- Phase ${parsed.phase}: ${parsed.alignment} (score: ${parsed.drift_score})`);
|
|
7009
|
-
} catch {
|
|
7010
|
-
lines.push("- (unparseable drift report)");
|
|
7011
|
-
}
|
|
7012
|
-
}
|
|
7013
|
-
lines.push("");
|
|
7014
|
-
}
|
|
7015
|
-
if (curatorDigest) {
|
|
7016
|
-
lines.push("## Curator Digest Summary");
|
|
7017
|
-
const trimmed = curatorDigest.length > 1000 ? `${curatorDigest.slice(0, 1000)}...` : curatorDigest;
|
|
7018
|
-
lines.push(trimmed);
|
|
7019
|
-
lines.push("");
|
|
7020
|
-
}
|
|
7021
|
-
return lines.join(`
|
|
7022
|
-
`);
|
|
7023
|
-
}
|
|
7024
|
-
function assembleLLMInput(planId, scope, sessionID, planSummary, knowledgeSummary, curatorDigest, proposals, unactionable, retrospectives, driftReports) {
|
|
7025
|
-
const sections = [];
|
|
7026
|
-
sections.push(`TASK: CURATOR_POSTMORTEM ${planId}`);
|
|
7027
|
-
sections.push(`SCOPE: ${scope}${sessionID ? ` (${sessionID})` : ""}`);
|
|
7028
|
-
sections.push(`PLAN_SUMMARY: ${planSummary}`);
|
|
7029
|
-
sections.push(`CURATOR_DIGESTS: ${curatorDigest ?? "none"}`);
|
|
7030
|
-
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(`
|
|
7031
|
-
`);
|
|
7032
|
-
sections.push(`KNOWLEDGE_EVENTS_SUMMARY:
|
|
7033
|
-
${eventsSummary || "none"}`);
|
|
7034
|
-
const knEntries = knowledgeSummary.map((e) => JSON.stringify({
|
|
7035
|
-
id: e.id,
|
|
7036
|
-
lesson: e.lesson.slice(0, MAX_INPUT_TEXT_CHARS)
|
|
7037
|
-
})).join(`
|
|
7038
|
-
`);
|
|
7039
|
-
sections.push(`KNOWLEDGE_ENTRIES:
|
|
7040
|
-
${knEntries || "[]"}`);
|
|
7041
|
-
const proposalText = proposals.length > 0 ? proposals.map((p) => `[${p.source}]
|
|
7042
|
-
${p.content.slice(0, MAX_INPUT_TEXT_CHARS)}`).join(`
|
|
7043
|
-
---
|
|
7044
|
-
`) : "none";
|
|
7045
|
-
sections.push(`PENDING_PROPOSALS:
|
|
7046
|
-
${proposalText}`);
|
|
7047
|
-
sections.push(`UNACTIONABLE_QUARANTINE: ${unactionable.length} entries`);
|
|
7048
|
-
const retroText = retrospectives.length > 0 ? retrospectives.map((r) => r.slice(0, MAX_INPUT_TEXT_CHARS)).join(`
|
|
7049
|
-
---
|
|
7050
|
-
`) : "none";
|
|
7051
|
-
sections.push(`RETROSPECTIVES:
|
|
7052
|
-
${retroText}`);
|
|
7053
|
-
if (driftReports.length > 0) {
|
|
7054
|
-
const driftText = driftReports.map((r) => r.slice(0, MAX_INPUT_TEXT_CHARS)).join(`
|
|
7055
|
-
---
|
|
7056
|
-
`);
|
|
7057
|
-
sections.push(`DRIFT_REPORTS:
|
|
7058
|
-
${driftText}`);
|
|
7059
|
-
}
|
|
7060
|
-
return sections.join(`
|
|
7061
|
-
|
|
7062
|
-
`);
|
|
7063
|
-
}
|
|
7064
|
-
async function runCuratorPostMortem(directory, options = {}) {
|
|
7065
|
-
const warnings = [];
|
|
7066
|
-
const scope = options.scope ?? "project";
|
|
7067
|
-
let planId = "unknown";
|
|
7068
|
-
let planSummary = "Plan data unavailable.";
|
|
7069
|
-
try {
|
|
7070
|
-
const plan = await loadPlanJsonOnly(directory);
|
|
7071
|
-
if (plan) {
|
|
7072
|
-
planId = derivePlanId(plan);
|
|
7073
|
-
const phaseCount = plan.phases?.length ?? 0;
|
|
7074
|
-
const completedPhases = plan.phases?.filter((p) => p.status === "complete").length ?? 0;
|
|
7075
|
-
planSummary = `Plan "${plan.title}" (${plan.swarm}): ${completedPhases}/${phaseCount} phases complete.`;
|
|
7076
|
-
} else {
|
|
7077
|
-
warnings.push("Plan not found \u2014 using fallback plan ID.");
|
|
7078
|
-
}
|
|
7079
|
-
} catch {
|
|
7080
|
-
warnings.push("Failed to load plan data.");
|
|
7081
|
-
}
|
|
7082
|
-
const effectivePlanId = planId;
|
|
7083
|
-
const reportFilename = `post-mortem-${effectivePlanId}.md`;
|
|
7084
|
-
let reportPath;
|
|
7085
|
-
try {
|
|
7086
|
-
reportPath = validateSwarmPath(directory, reportFilename);
|
|
7087
|
-
} catch {
|
|
7088
|
-
return {
|
|
7089
|
-
success: false,
|
|
7090
|
-
planId,
|
|
7091
|
-
reportPath: null,
|
|
7092
|
-
summary: null,
|
|
7093
|
-
warnings: [...warnings, "Invalid report path."]
|
|
7094
|
-
};
|
|
7095
|
-
}
|
|
7096
|
-
if (!options.force && isReportValid(reportPath)) {
|
|
7097
|
-
return {
|
|
7098
|
-
success: true,
|
|
7099
|
-
planId: effectivePlanId,
|
|
7100
|
-
reportPath,
|
|
7101
|
-
summary: "Post-mortem report already exists (idempotent skip).",
|
|
7102
|
-
warnings
|
|
7103
|
-
};
|
|
7104
|
-
}
|
|
7105
|
-
const lock = await _internals7.acquirePostMortemLock(directory, effectivePlanId);
|
|
7106
|
-
if (!lock.acquired) {
|
|
7107
|
-
return {
|
|
7108
|
-
success: false,
|
|
7109
|
-
planId: effectivePlanId,
|
|
7110
|
-
reportPath,
|
|
7111
|
-
summary: null,
|
|
7112
|
-
warnings: [
|
|
7113
|
-
...warnings,
|
|
7114
|
-
`Concurrent post-mortem run in progress for plan ${effectivePlanId}; skipped.`
|
|
7115
|
-
]
|
|
7116
|
-
};
|
|
7117
|
-
}
|
|
7118
|
-
try {
|
|
7119
|
-
let knowledgeSummary = [];
|
|
7120
|
-
try {
|
|
7121
|
-
knowledgeSummary = await collectKnowledgeSummary(directory, scope, options.sessionID);
|
|
7122
|
-
} catch {
|
|
7123
|
-
warnings.push("Failed to collect knowledge summary.");
|
|
7124
|
-
}
|
|
7125
|
-
if (knowledgeSummary.length > MAX_KNOWLEDGE_ENTRIES) {
|
|
7126
|
-
warnings.push(`Knowledge entries capped at ${MAX_KNOWLEDGE_ENTRIES} (had ${knowledgeSummary.length}); older entries truncated.`);
|
|
7127
|
-
knowledgeSummary = knowledgeSummary.slice(0, MAX_KNOWLEDGE_ENTRIES);
|
|
7128
|
-
}
|
|
7129
|
-
let curatorDigest = null;
|
|
7130
|
-
try {
|
|
7131
|
-
const raw = await readSwarmFileAsync(directory, "curator-summary.json");
|
|
7132
|
-
if (raw) {
|
|
7133
|
-
const parsed = JSON.parse(raw);
|
|
7134
|
-
curatorDigest = parsed.digest ?? null;
|
|
7135
|
-
}
|
|
7136
|
-
} catch {
|
|
7137
|
-
warnings.push("Failed to read curator digest.");
|
|
7138
|
-
}
|
|
7139
|
-
let proposals = collectPendingProposals(directory);
|
|
7140
|
-
if (proposals.length > MAX_PROPOSALS) {
|
|
7141
|
-
warnings.push(`Pending proposals capped at ${MAX_PROPOSALS} (had ${proposals.length}); older entries truncated.`);
|
|
7142
|
-
proposals = proposals.slice(0, MAX_PROPOSALS);
|
|
7143
|
-
}
|
|
7144
|
-
const unactionablePath = path12.join(resolveKnowledgeStoreDir(directory), "knowledge-unactionable.jsonl");
|
|
7145
|
-
let unactionable = readJsonlFile(unactionablePath, MAX_UNACTIONABLE);
|
|
7146
|
-
if (unactionable.length > MAX_UNACTIONABLE) {
|
|
7147
|
-
warnings.push(`Unactionable entries capped at ${MAX_UNACTIONABLE} (had ${unactionable.length}); older entries truncated.`);
|
|
7148
|
-
unactionable = unactionable.slice(0, MAX_UNACTIONABLE);
|
|
7149
|
-
}
|
|
7150
|
-
let retrospectives = collectRetrospectives(directory);
|
|
7151
|
-
if (retrospectives.length > MAX_RETROSPECTIVES) {
|
|
7152
|
-
warnings.push(`Retrospectives capped at ${MAX_RETROSPECTIVES} (had ${retrospectives.length}); older entries truncated.`);
|
|
7153
|
-
retrospectives = retrospectives.slice(0, MAX_RETROSPECTIVES);
|
|
7154
|
-
}
|
|
7155
|
-
let driftReports = await collectDriftReports(directory);
|
|
7156
|
-
if (driftReports.length > MAX_DRIFT_REPORTS) {
|
|
7157
|
-
warnings.push(`Drift reports capped at ${MAX_DRIFT_REPORTS} (had ${driftReports.length}); older entries truncated.`);
|
|
7158
|
-
driftReports = driftReports.slice(0, MAX_DRIFT_REPORTS);
|
|
7159
|
-
}
|
|
7160
|
-
let reportContent;
|
|
7161
|
-
let llmSummary = null;
|
|
7162
|
-
let actionResult;
|
|
7163
|
-
if (options.llmDelegate) {
|
|
7164
|
-
try {
|
|
7165
|
-
const { CURATOR_POSTMORTEM_PROMPT } = await import("./explorer-ksr3xq7n.js");
|
|
7166
|
-
const userInput = assembleLLMInput(effectivePlanId, scope, options.sessionID, planSummary, knowledgeSummary, curatorDigest, proposals, unactionable, retrospectives, driftReports);
|
|
7167
|
-
const ac = new AbortController;
|
|
7168
|
-
const timer = setTimeout(() => ac.abort(), options.llmTimeoutMs ?? 300000);
|
|
7169
|
-
let llmOutput;
|
|
7170
|
-
try {
|
|
7171
|
-
const delegatePromise = options.llmDelegate(CURATOR_POSTMORTEM_PROMPT, userInput, ac.signal);
|
|
7172
|
-
delegatePromise.catch(() => {});
|
|
7173
|
-
llmOutput = await Promise.race([
|
|
7174
|
-
delegatePromise,
|
|
7175
|
-
new Promise((_, reject) => {
|
|
7176
|
-
ac.signal.addEventListener("abort", () => reject(new Error("CURATOR_LLM_TIMEOUT")));
|
|
7177
|
-
})
|
|
7178
|
-
]);
|
|
7179
|
-
} finally {
|
|
7180
|
-
clearTimeout(timer);
|
|
7181
|
-
}
|
|
7182
|
-
let parsedActions = _internals7.parsePostMortemActions(llmOutput);
|
|
7183
|
-
if (parsedActions.diagnostics.length > 0) {
|
|
7184
|
-
warnings.push(`Post-mortem structured action parse diagnostics: ${parsedActions.diagnostics.join("; ")}`);
|
|
7185
|
-
const repaired = await _internals7.repairPostMortemActions(llmOutput, parsedActions.diagnostics, options);
|
|
7186
|
-
if (repaired) {
|
|
7187
|
-
parsedActions = repaired;
|
|
7188
|
-
warnings.push("Post-mortem structured actions repaired by LLM.");
|
|
7189
|
-
}
|
|
7190
|
-
}
|
|
7191
|
-
llmSummary = parsedActions.summary;
|
|
7192
|
-
const executed = await _internals7.executePostMortemActions(directory, parsedActions, options);
|
|
7193
|
-
actionResult = executed.result;
|
|
7194
|
-
warnings.push(...executed.warnings);
|
|
7195
|
-
const actionSummary = [
|
|
7196
|
-
"## Executed Post-Mortem Actions",
|
|
7197
|
-
`- Knowledge actions: ${actionResult.knowledge_applied} applied, ${actionResult.knowledge_skipped} skipped`,
|
|
7198
|
-
`- Hive actions: ${actionResult.hive_promotions} new promotions, ${actionResult.hive_encounters_incremented} encounters incremented, ${actionResult.hive_advancements} advancements`,
|
|
7199
|
-
`- Proposal actions: ${actionResult.proposals_approved} approved, ${actionResult.proposals_rejected} rejected, ${actionResult.proposals_skipped} skipped`
|
|
7200
|
-
].join(`
|
|
7201
|
-
`);
|
|
7202
|
-
reportContent = `# Post-Mortem Report: ${effectivePlanId}
|
|
7203
|
-
Generated: ${new Date().toISOString()}
|
|
7204
|
-
Scope: ${scope}
|
|
7205
|
-
|
|
7206
|
-
${llmOutput}
|
|
7207
|
-
|
|
7208
|
-
${actionSummary}`;
|
|
7209
|
-
} catch (err) {
|
|
7210
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
7211
|
-
warnings.push(`LLM delegate failed, falling back to data-only report: ${msg}`);
|
|
7212
|
-
reportContent = _internals7.buildDataOnlyReport(effectivePlanId, planSummary, knowledgeSummary, curatorDigest, proposals, unactionable, retrospectives, driftReports);
|
|
7213
|
-
}
|
|
7214
|
-
} else {
|
|
7215
|
-
reportContent = _internals7.buildDataOnlyReport(effectivePlanId, planSummary, knowledgeSummary, curatorDigest, proposals, unactionable, retrospectives, driftReports);
|
|
7216
|
-
}
|
|
7217
|
-
try {
|
|
7218
|
-
const { mkdirSync: mkdirSync6 } = await import("fs");
|
|
7219
|
-
mkdirSync6(path12.dirname(reportPath), { recursive: true });
|
|
7220
|
-
await atomicWriteFile(reportPath, reportContent);
|
|
7221
|
-
} catch (err) {
|
|
7222
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
7223
|
-
return {
|
|
7224
|
-
success: false,
|
|
7225
|
-
planId: effectivePlanId,
|
|
7226
|
-
reportPath: null,
|
|
7227
|
-
summary: null,
|
|
7228
|
-
warnings: [...warnings, `Failed to write report: ${msg}`]
|
|
7229
|
-
};
|
|
7230
|
-
}
|
|
7231
|
-
const totalEntries = knowledgeSummary.length;
|
|
7232
|
-
const neverAppliedCount = knowledgeSummary.filter((e) => e.applied === 0 && e.violated === 0 && e.ignored === 0).length;
|
|
7233
|
-
const totalViolations = knowledgeSummary.reduce((s, e) => s + e.violated, 0);
|
|
7234
|
-
const mechanicalSummary = [
|
|
7235
|
-
`Post-mortem for plan "${effectivePlanId}": ${totalEntries} knowledge entries reviewed.`,
|
|
7236
|
-
`${neverAppliedCount} never-applied entries flagged; ${totalViolations} total violations recorded.`,
|
|
7237
|
-
`${proposals.length} pending proposals, ${unactionable.length} quarantined entries.`
|
|
7238
|
-
].join(" ");
|
|
7239
|
-
const summary = llmSummary ?? mechanicalSummary;
|
|
7240
|
-
return {
|
|
7241
|
-
success: true,
|
|
7242
|
-
planId: effectivePlanId,
|
|
7243
|
-
reportPath,
|
|
7244
|
-
summary,
|
|
7245
|
-
warnings,
|
|
7246
|
-
actions: actionResult
|
|
7247
|
-
};
|
|
7248
|
-
} finally {
|
|
7249
|
-
if (lock.release) {
|
|
7250
|
-
try {
|
|
7251
|
-
await lock.release();
|
|
7252
|
-
} catch {}
|
|
7253
|
-
}
|
|
7254
|
-
}
|
|
7255
|
-
}
|
|
7256
|
-
var _internals7 = {
|
|
7257
|
-
acquirePostMortemLock,
|
|
7258
|
-
collectKnowledgeSummary,
|
|
7259
|
-
collectRetrospectives,
|
|
7260
|
-
collectDriftReports,
|
|
7261
|
-
collectPendingProposals,
|
|
7262
|
-
readJsonlFile,
|
|
7263
|
-
buildDataOnlyReport,
|
|
7264
|
-
assembleLLMInput,
|
|
7265
|
-
isReportValid,
|
|
7266
|
-
parsePostMortemActions,
|
|
7267
|
-
executePostMortemActions,
|
|
7268
|
-
repairPostMortemActions,
|
|
7269
|
-
loadDefaultKnowledgeConfig: async () => {
|
|
7270
|
-
const { KnowledgeConfigSchema: KnowledgeConfigSchema2 } = await import("./schema-5vmb5dh8.js");
|
|
7271
|
-
return KnowledgeConfigSchema2.parse({});
|
|
7272
|
-
},
|
|
7273
|
-
applyCuratorKnowledgeUpdates: async (directory, recommendations, knowledgeConfig) => {
|
|
7274
|
-
const { applyCuratorKnowledgeUpdates } = await import("./curator-4qqzpd85.js");
|
|
7275
|
-
return applyCuratorKnowledgeUpdates(directory, recommendations, knowledgeConfig);
|
|
7276
|
-
},
|
|
7277
|
-
checkHivePromotions: async (entries, knowledgeConfig) => {
|
|
7278
|
-
const { checkHivePromotions } = await import("./hive-promoter-wvxqyayt.js");
|
|
7279
|
-
return checkHivePromotions(entries, knowledgeConfig);
|
|
7280
|
-
},
|
|
7281
|
-
applyProposalTriage: async (directory, triage) => {
|
|
7282
|
-
const {
|
|
7283
|
-
_internals: skillInternals,
|
|
7284
|
-
activateProposal,
|
|
7285
|
-
listSkills: listSkills2,
|
|
7286
|
-
sanitizeSlug
|
|
7287
|
-
} = await import("./skill-generator-mjfqvnn0.js");
|
|
7288
|
-
const result = {
|
|
7289
|
-
approved: [],
|
|
7290
|
-
rejected: [],
|
|
7291
|
-
skipped: []
|
|
7292
|
-
};
|
|
7293
|
-
const skills = await listSkills2(directory);
|
|
7294
|
-
const proposalSlugs = new Set(skills.proposals.map((p) => p.slug));
|
|
7295
|
-
for (const item of triage) {
|
|
7296
|
-
const slug = sanitizeSlug(normalizeProposalSlug(item.proposal_id));
|
|
7297
|
-
if (!slug || !proposalSlugs.has(slug)) {
|
|
7298
|
-
result.skipped.push(slug || item.proposal_id);
|
|
7299
|
-
continue;
|
|
7300
|
-
}
|
|
7301
|
-
if (item.action === "apply") {
|
|
7302
|
-
const activation = await activateProposal(directory, slug, false, {
|
|
7303
|
-
evaluate: true,
|
|
7304
|
-
operation: "post_mortem_queue_triage",
|
|
7305
|
-
confirmUnevaluated: true
|
|
7306
|
-
});
|
|
7307
|
-
if (activation.activated) {
|
|
7308
|
-
result.approved.push(slug);
|
|
7309
|
-
} else {
|
|
7310
|
-
result.skipped.push(slug);
|
|
7311
|
-
}
|
|
7312
|
-
continue;
|
|
7313
|
-
}
|
|
7314
|
-
const proposal = skills.proposals.find((p) => p.slug === slug);
|
|
7315
|
-
if (!proposal) {
|
|
7316
|
-
result.skipped.push(slug);
|
|
7317
|
-
continue;
|
|
7318
|
-
}
|
|
7319
|
-
try {
|
|
7320
|
-
skillInternals.unlinkSync(proposal.path);
|
|
7321
|
-
result.rejected.push(slug);
|
|
7322
|
-
} catch {
|
|
7323
|
-
result.skipped.push(slug);
|
|
7324
|
-
}
|
|
7325
|
-
}
|
|
7326
|
-
return result;
|
|
7327
|
-
},
|
|
7328
|
-
readPriorDriftReports: async (directory) => {
|
|
7329
|
-
const { readPriorDriftReports } = await import("./curator-drift-169wgaxn.js");
|
|
7330
|
-
return readPriorDriftReports(directory);
|
|
7331
|
-
},
|
|
7332
|
-
readSwarmKnowledge: (directory) => readKnowledge(resolveSwarmKnowledgePath(directory))
|
|
7333
|
-
};
|
|
7334
|
-
|
|
7335
|
-
// src/hooks/hive-promoter.ts
|
|
7336
|
-
import path17 from "path";
|
|
6574
|
+
import { existsSync as existsSync10, readdirSync as readdirSync2, readFileSync as readFileSync6 } from "fs";
|
|
6575
|
+
import * as path16 from "path";
|
|
7337
6576
|
|
|
7338
6577
|
// src/hooks/curator.ts
|
|
7339
6578
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
7340
6579
|
import * as fs5 from "fs";
|
|
7341
|
-
import * as
|
|
6580
|
+
import * as path15 from "path";
|
|
7342
6581
|
|
|
7343
6582
|
// src/services/learning-metrics.ts
|
|
7344
6583
|
var SEVEN_DAYS_MS = 7 * 24 * 60 * 60 * 1000;
|
|
@@ -7751,9 +6990,9 @@ import { readFile as readFile5, rename as rename4, writeFile as writeFile5 } fro
|
|
|
7751
6990
|
|
|
7752
6991
|
// src/services/skill-improver-quota.ts
|
|
7753
6992
|
var import_proper_lockfile = __toESM(require_proper_lockfile(), 1);
|
|
7754
|
-
import { existsSync as
|
|
6993
|
+
import { existsSync as existsSync7 } from "fs";
|
|
7755
6994
|
import { mkdir as mkdir4, readFile as readFile4, rename as rename3, writeFile as writeFile4 } from "fs/promises";
|
|
7756
|
-
import * as
|
|
6995
|
+
import * as path12 from "path";
|
|
7757
6996
|
var LOCK_ACQUIRE_TIMEOUT_MS = 1e4;
|
|
7758
6997
|
var LOCK_RETRY_OPTS = {
|
|
7759
6998
|
retries: {
|
|
@@ -7782,7 +7021,7 @@ async function acquireLock(dir) {
|
|
|
7782
7021
|
}
|
|
7783
7022
|
function resolveQuotaPath(directory, scope = "skill-improver") {
|
|
7784
7023
|
const fileName = scope === "knowledge-enrichment" ? "knowledge-enrichment-quota.json" : "skill-improver-quota.json";
|
|
7785
|
-
return
|
|
7024
|
+
return path12.join(directory, ".swarm", fileName);
|
|
7786
7025
|
}
|
|
7787
7026
|
function todayKey(window, now = new Date) {
|
|
7788
7027
|
if (window === "utc") {
|
|
@@ -7794,7 +7033,7 @@ function todayKey(window, now = new Date) {
|
|
|
7794
7033
|
return `${yr}-${m}-${d}`;
|
|
7795
7034
|
}
|
|
7796
7035
|
async function readState(filePath) {
|
|
7797
|
-
if (!
|
|
7036
|
+
if (!existsSync7(filePath))
|
|
7798
7037
|
return null;
|
|
7799
7038
|
try {
|
|
7800
7039
|
const raw = await readFile4(filePath, "utf-8");
|
|
@@ -7808,7 +7047,7 @@ async function readState(filePath) {
|
|
|
7808
7047
|
}
|
|
7809
7048
|
}
|
|
7810
7049
|
async function writeState(filePath, state) {
|
|
7811
|
-
await mkdir4(
|
|
7050
|
+
await mkdir4(path12.dirname(filePath), { recursive: true });
|
|
7812
7051
|
const tmp = `${filePath}.tmp-${process.pid}`;
|
|
7813
7052
|
await writeFile4(tmp, JSON.stringify(state, null, 2), "utf-8");
|
|
7814
7053
|
await rename3(tmp, filePath);
|
|
@@ -7831,10 +7070,10 @@ async function getQuotaState(directory, opts) {
|
|
|
7831
7070
|
}
|
|
7832
7071
|
async function reserveQuota(directory, opts) {
|
|
7833
7072
|
const filePath = resolveQuotaPath(directory, opts.scope);
|
|
7834
|
-
await mkdir4(
|
|
7073
|
+
await mkdir4(path12.dirname(filePath), { recursive: true });
|
|
7835
7074
|
let release = null;
|
|
7836
7075
|
try {
|
|
7837
|
-
release = await acquireLock(
|
|
7076
|
+
release = await acquireLock(path12.dirname(filePath));
|
|
7838
7077
|
const state = await getQuotaState(directory, opts);
|
|
7839
7078
|
if (state.calls_used + opts.nCalls > opts.maxCalls) {
|
|
7840
7079
|
return {
|
|
@@ -7861,10 +7100,10 @@ async function reserveQuota(directory, opts) {
|
|
|
7861
7100
|
}
|
|
7862
7101
|
async function releaseQuota(directory, opts) {
|
|
7863
7102
|
const filePath = resolveQuotaPath(directory, opts.scope);
|
|
7864
|
-
await mkdir4(
|
|
7103
|
+
await mkdir4(path12.dirname(filePath), { recursive: true });
|
|
7865
7104
|
let release = null;
|
|
7866
7105
|
try {
|
|
7867
|
-
release = await acquireLock(
|
|
7106
|
+
release = await acquireLock(path12.dirname(filePath));
|
|
7868
7107
|
const state = await getQuotaState(directory, opts);
|
|
7869
7108
|
const next = {
|
|
7870
7109
|
...state,
|
|
@@ -8015,7 +7254,7 @@ async function reviseSkill(params) {
|
|
|
8015
7254
|
}
|
|
8016
7255
|
if (!params.delegate) {
|
|
8017
7256
|
try {
|
|
8018
|
-
const revised =
|
|
7257
|
+
const revised = _internals7.buildDeterministicRevision(params.currentContent, params.currentVersion, params.violationContexts);
|
|
8019
7258
|
const validation = await validateRevisionCandidate(params, revised, "skill_reviser:deterministic");
|
|
8020
7259
|
if (!validation.passed) {
|
|
8021
7260
|
return {
|
|
@@ -8142,7 +7381,7 @@ async function reviseSkill(params) {
|
|
|
8142
7381
|
};
|
|
8143
7382
|
}
|
|
8144
7383
|
}
|
|
8145
|
-
var
|
|
7384
|
+
var _internals7 = {
|
|
8146
7385
|
reviseSkill,
|
|
8147
7386
|
getSkillVersion,
|
|
8148
7387
|
buildDeterministicRevision,
|
|
@@ -8157,7 +7396,7 @@ var _internals8 = {
|
|
|
8157
7396
|
init_logger();
|
|
8158
7397
|
|
|
8159
7398
|
// src/hooks/skill-invalidator.ts
|
|
8160
|
-
import * as
|
|
7399
|
+
import * as path13 from "path";
|
|
8161
7400
|
init_logger();
|
|
8162
7401
|
async function writeArchiveTombstoneAndInvalidateSkills(ctx) {
|
|
8163
7402
|
const label = ctx.sourceLabel ?? "skill-invalidator";
|
|
@@ -8191,7 +7430,7 @@ async function writeArchiveTombstoneAndInvalidateSkills(ctx) {
|
|
|
8191
7430
|
let retiredCount = 0;
|
|
8192
7431
|
let staleCount = 0;
|
|
8193
7432
|
for (const skillDir of allSkillDirs) {
|
|
8194
|
-
const slug =
|
|
7433
|
+
const slug = path13.basename(skillDir);
|
|
8195
7434
|
if (slugSet.has(slug))
|
|
8196
7435
|
continue;
|
|
8197
7436
|
slugSet.add(slug);
|
|
@@ -8218,14 +7457,14 @@ async function writeArchiveTombstoneAndInvalidateSkills(ctx) {
|
|
|
8218
7457
|
// src/hooks/skill-usage-log.ts
|
|
8219
7458
|
import * as crypto2 from "crypto";
|
|
8220
7459
|
import * as fs4 from "fs";
|
|
8221
|
-
import * as
|
|
7460
|
+
import * as path14 from "path";
|
|
8222
7461
|
function resolveLogPath(directory) {
|
|
8223
7462
|
return validateSwarmPath(directory, "skill-usage.jsonl");
|
|
8224
7463
|
}
|
|
8225
7464
|
function normalizeComplianceVerdict(verdict) {
|
|
8226
7465
|
return verdict === "violation" ? "violated" : verdict;
|
|
8227
7466
|
}
|
|
8228
|
-
var
|
|
7467
|
+
var _internals8 = {
|
|
8229
7468
|
generateId: () => crypto2.randomUUID(),
|
|
8230
7469
|
appendFileSync: fs4.appendFileSync.bind(fs4),
|
|
8231
7470
|
readFileSync: fs4.readFileSync.bind(fs4),
|
|
@@ -8295,9 +7534,9 @@ function parseFeedbackMarker(raw) {
|
|
|
8295
7534
|
function readFeedbackAppliedEntryIds(directory) {
|
|
8296
7535
|
const resolved = resolveLogPath(directory);
|
|
8297
7536
|
const processed = new Set;
|
|
8298
|
-
if (!
|
|
7537
|
+
if (!_internals8.existsSync(resolved))
|
|
8299
7538
|
return processed;
|
|
8300
|
-
const raw =
|
|
7539
|
+
const raw = _internals8.readFileSync(resolved, "utf-8");
|
|
8301
7540
|
for (const line of raw.split(`
|
|
8302
7541
|
`)) {
|
|
8303
7542
|
const trimmed = line.trim();
|
|
@@ -8317,16 +7556,16 @@ function appendFeedbackAppliedMarker(directory, processedEntryIds) {
|
|
|
8317
7556
|
if (processedEntryIds.length === 0)
|
|
8318
7557
|
return;
|
|
8319
7558
|
const resolved = resolveLogPath(directory);
|
|
8320
|
-
const dir =
|
|
8321
|
-
if (!
|
|
8322
|
-
|
|
7559
|
+
const dir = path14.dirname(resolved);
|
|
7560
|
+
if (!_internals8.existsSync(dir)) {
|
|
7561
|
+
_internals8.mkdirSync(dir, { recursive: true });
|
|
8323
7562
|
}
|
|
8324
7563
|
const marker = {
|
|
8325
7564
|
type: "feedback_applied",
|
|
8326
7565
|
timestamp: new Date().toISOString(),
|
|
8327
7566
|
processedEntryIds: [...new Set(processedEntryIds)]
|
|
8328
7567
|
};
|
|
8329
|
-
|
|
7568
|
+
_internals8.appendFileSync(resolved, `${JSON.stringify(marker)}
|
|
8330
7569
|
`, "utf-8");
|
|
8331
7570
|
}
|
|
8332
7571
|
function appendSkillUsageEntry(directory, entry) {
|
|
@@ -8362,12 +7601,12 @@ function appendSkillUsageEntry(directory, entry) {
|
|
|
8362
7601
|
throw new Error("sessionID is required and must be a non-empty string");
|
|
8363
7602
|
}
|
|
8364
7603
|
const resolved = validateSwarmPath(directory, "skill-usage.jsonl");
|
|
8365
|
-
const dir =
|
|
8366
|
-
if (!
|
|
8367
|
-
|
|
7604
|
+
const dir = path14.dirname(resolved);
|
|
7605
|
+
if (!_internals8.existsSync(dir)) {
|
|
7606
|
+
_internals8.mkdirSync(dir, { recursive: true });
|
|
8368
7607
|
}
|
|
8369
7608
|
const fullEntry = {
|
|
8370
|
-
id:
|
|
7609
|
+
id: _internals8.generateId(),
|
|
8371
7610
|
skillPath,
|
|
8372
7611
|
agentName,
|
|
8373
7612
|
taskID,
|
|
@@ -8377,21 +7616,21 @@ function appendSkillUsageEntry(directory, entry) {
|
|
|
8377
7616
|
...reviewerNotes !== undefined && { reviewerNotes },
|
|
8378
7617
|
...skillVersion !== undefined && { skillVersion }
|
|
8379
7618
|
};
|
|
8380
|
-
|
|
7619
|
+
_internals8.appendFileSync(resolved, `${JSON.stringify(fullEntry)}
|
|
8381
7620
|
`, "utf-8");
|
|
8382
7621
|
try {
|
|
8383
|
-
const stat3 =
|
|
7622
|
+
const stat3 = _internals8.statSync(resolved);
|
|
8384
7623
|
if (stat3.size > SKILL_USAGE_LOG_ROTATE_BYTES) {
|
|
8385
|
-
|
|
7624
|
+
_internals8.pruneSkillUsageLog(directory, SKILL_USAGE_LOG_MAX_ENTRIES_PER_SKILL);
|
|
8386
7625
|
}
|
|
8387
7626
|
} catch {}
|
|
8388
7627
|
}
|
|
8389
7628
|
function readSkillUsageEntries(directory, options) {
|
|
8390
7629
|
const resolved = resolveLogPath(directory);
|
|
8391
|
-
if (!
|
|
7630
|
+
if (!_internals8.existsSync(resolved)) {
|
|
8392
7631
|
return [];
|
|
8393
7632
|
}
|
|
8394
|
-
const raw =
|
|
7633
|
+
const raw = _internals8.readFileSync(resolved, "utf-8");
|
|
8395
7634
|
const entries = [];
|
|
8396
7635
|
for (const line of raw.split(`
|
|
8397
7636
|
`)) {
|
|
@@ -8434,20 +7673,20 @@ var SKILL_USAGE_LOG_ROTATE_BYTES = 1024 * 1024;
|
|
|
8434
7673
|
var SKILL_USAGE_LOG_MAX_ENTRIES_PER_SKILL = 500;
|
|
8435
7674
|
function readSkillUsageEntriesTail(directory, filters, maxBytes = TAIL_BYTES_DEFAULT) {
|
|
8436
7675
|
const logPath = resolveLogPath(directory);
|
|
8437
|
-
if (!
|
|
7676
|
+
if (!_internals8.existsSync(logPath))
|
|
8438
7677
|
return [];
|
|
8439
7678
|
try {
|
|
8440
7679
|
const normalizedMaxBytes = Number.isFinite(maxBytes) ? maxBytes : TAIL_BYTES_DEFAULT;
|
|
8441
7680
|
const boundedMaxBytes = Math.min(Math.max(1, normalizedMaxBytes), MAX_TAIL_BYTES);
|
|
8442
|
-
const stat3 =
|
|
7681
|
+
const stat3 = _internals8.statSync(logPath);
|
|
8443
7682
|
const start = Math.max(0, stat3.size - boundedMaxBytes);
|
|
8444
|
-
const fd =
|
|
7683
|
+
const fd = _internals8.openSync(logPath, "r");
|
|
8445
7684
|
try {
|
|
8446
7685
|
const readLen = stat3.size - start;
|
|
8447
7686
|
if (readLen === 0)
|
|
8448
7687
|
return [];
|
|
8449
7688
|
const buf = Buffer.alloc(readLen);
|
|
8450
|
-
|
|
7689
|
+
_internals8.readSync(fd, buf, 0, buf.length, start);
|
|
8451
7690
|
const content = buf.toString("utf-8");
|
|
8452
7691
|
let usable;
|
|
8453
7692
|
if (start > 0) {
|
|
@@ -8474,7 +7713,7 @@ function readSkillUsageEntriesTail(directory, filters, maxBytes = TAIL_BYTES_DEF
|
|
|
8474
7713
|
}
|
|
8475
7714
|
return entries;
|
|
8476
7715
|
} finally {
|
|
8477
|
-
|
|
7716
|
+
_internals8.closeSync(fd);
|
|
8478
7717
|
}
|
|
8479
7718
|
} catch {
|
|
8480
7719
|
return [];
|
|
@@ -8511,10 +7750,10 @@ function computeComplianceByVersion(entries, skillPath) {
|
|
|
8511
7750
|
}
|
|
8512
7751
|
function pruneSkillUsageLog(directory, maxEntriesPerSkill = 500) {
|
|
8513
7752
|
const resolved = resolveLogPath(directory);
|
|
8514
|
-
if (!
|
|
7753
|
+
if (!_internals8.existsSync(resolved)) {
|
|
8515
7754
|
return { pruned: 0, remaining: 0 };
|
|
8516
7755
|
}
|
|
8517
|
-
const raw =
|
|
7756
|
+
const raw = _internals8.readFileSync(resolved, "utf-8");
|
|
8518
7757
|
const lines = raw.split(`
|
|
8519
7758
|
`);
|
|
8520
7759
|
const entries = [];
|
|
@@ -8538,19 +7777,19 @@ function pruneSkillUsageLog(directory, maxEntriesPerSkill = 500) {
|
|
|
8538
7777
|
}
|
|
8539
7778
|
if (entries.length === 0) {
|
|
8540
7779
|
if (preservedMarkers.length > 0) {
|
|
8541
|
-
const dir2 =
|
|
8542
|
-
const tmpPath2 =
|
|
7780
|
+
const dir2 = path14.dirname(resolved);
|
|
7781
|
+
const tmpPath2 = path14.join(dir2, `skill-usage-${Date.now()}.tmp`);
|
|
8543
7782
|
const content2 = preservedMarkers.join(`
|
|
8544
7783
|
`).concat(preservedMarkers.length > 0 ? `
|
|
8545
7784
|
` : "");
|
|
8546
7785
|
try {
|
|
8547
|
-
|
|
8548
|
-
|
|
7786
|
+
_internals8.writeFileSync(tmpPath2, content2, "utf-8");
|
|
7787
|
+
_internals8.renameSync(tmpPath2, resolved);
|
|
8549
7788
|
} catch (writeErr) {
|
|
8550
7789
|
const msg = writeErr instanceof Error ? writeErr.message : String(writeErr);
|
|
8551
7790
|
try {
|
|
8552
|
-
if (
|
|
8553
|
-
|
|
7791
|
+
if (_internals8.existsSync(tmpPath2)) {
|
|
7792
|
+
_internals8.writeFileSync(tmpPath2, "", "utf-8");
|
|
8554
7793
|
}
|
|
8555
7794
|
} catch {}
|
|
8556
7795
|
return { pruned: 0, remaining: 0, error: msg };
|
|
@@ -8581,21 +7820,21 @@ function pruneSkillUsageLog(directory, maxEntriesPerSkill = 500) {
|
|
|
8581
7820
|
if (pruned === 0) {
|
|
8582
7821
|
return { pruned: 0, remaining: entries.length };
|
|
8583
7822
|
}
|
|
8584
|
-
const dir =
|
|
8585
|
-
const tmpPath =
|
|
7823
|
+
const dir = path14.dirname(resolved);
|
|
7824
|
+
const tmpPath = path14.join(dir, `skill-usage-${Date.now()}.tmp`);
|
|
8586
7825
|
const entryLines = surviving.map((e) => JSON.stringify(e));
|
|
8587
7826
|
const allLines = [...entryLines, ...preservedMarkers];
|
|
8588
7827
|
const content = allLines.join(`
|
|
8589
7828
|
`).concat(`
|
|
8590
7829
|
`);
|
|
8591
7830
|
try {
|
|
8592
|
-
|
|
8593
|
-
|
|
7831
|
+
_internals8.writeFileSync(tmpPath, content, "utf-8");
|
|
7832
|
+
_internals8.renameSync(tmpPath, resolved);
|
|
8594
7833
|
} catch (writeErr) {
|
|
8595
7834
|
const msg = writeErr instanceof Error ? writeErr.message : String(writeErr);
|
|
8596
7835
|
try {
|
|
8597
|
-
if (
|
|
8598
|
-
|
|
7836
|
+
if (_internals8.existsSync(tmpPath)) {
|
|
7837
|
+
_internals8.writeFileSync(tmpPath, "", "utf-8");
|
|
8599
7838
|
}
|
|
8600
7839
|
} catch {}
|
|
8601
7840
|
return { pruned: 0, remaining: entries.length, error: msg };
|
|
@@ -8611,16 +7850,16 @@ async function resolveSourceKnowledgeIds(directory, skillPath) {
|
|
|
8611
7850
|
if (/\.\.[/\\]/.test(cleanPath)) {
|
|
8612
7851
|
return [];
|
|
8613
7852
|
}
|
|
8614
|
-
const absolute =
|
|
8615
|
-
const baseDir =
|
|
8616
|
-
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);
|
|
8617
7856
|
if (!isContained) {
|
|
8618
7857
|
return [];
|
|
8619
7858
|
}
|
|
8620
|
-
if (!
|
|
7859
|
+
if (!_internals8.existsSync(absolute)) {
|
|
8621
7860
|
return [];
|
|
8622
7861
|
}
|
|
8623
|
-
const content =
|
|
7862
|
+
const content = _internals8.readFileSync(absolute, "utf-8");
|
|
8624
7863
|
return parseGeneratedFromKnowledge(content);
|
|
8625
7864
|
} catch (err) {
|
|
8626
7865
|
console.warn("[skill-usage-log] resolveSourceKnowledgeIds failed (fail-open):", err instanceof Error ? err.message : String(err));
|
|
@@ -8728,7 +7967,7 @@ var DEFAULT_CURATOR_LLM_TIMEOUT_MS = 300000;
|
|
|
8728
7967
|
var MAX_CURATOR_PHASE_DIGESTS = 50;
|
|
8729
7968
|
var MAX_CURATOR_COMPLIANCE_OBSERVATIONS = 200;
|
|
8730
7969
|
var MAX_CURATOR_RECOMMENDATIONS = 200;
|
|
8731
|
-
var
|
|
7970
|
+
var _internals9 = {
|
|
8732
7971
|
parseKnowledgeRecommendations,
|
|
8733
7972
|
parseKnowledgeRecommendationsWithDiagnostics,
|
|
8734
7973
|
readCuratorSummary,
|
|
@@ -8749,6 +7988,37 @@ var _internals10 = {
|
|
|
8749
7988
|
getSkillVersion,
|
|
8750
7989
|
readLatestPostMortemDigest: (directory) => readLatestPostMortemDigest(directory)
|
|
8751
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
|
+
}
|
|
8752
8022
|
function capPhaseDigests(digests) {
|
|
8753
8023
|
return digests.slice(-MAX_CURATOR_PHASE_DIGESTS);
|
|
8754
8024
|
}
|
|
@@ -8767,9 +8037,9 @@ ${digest.summary}`).join(`
|
|
|
8767
8037
|
async function autoRetireSkills(directory, _curatorKnowledgePath, excludeSlugs) {
|
|
8768
8038
|
const observations = [];
|
|
8769
8039
|
try {
|
|
8770
|
-
const skillListResult = await
|
|
8771
|
-
const usageEntries =
|
|
8772
|
-
const allArchivedIds = await
|
|
8040
|
+
const skillListResult = await _internals9.listSkills(directory);
|
|
8041
|
+
const usageEntries = _internals9.readSkillUsageEntries(directory);
|
|
8042
|
+
const allArchivedIds = await _internals9.getArchivedKnowledgeIds(directory);
|
|
8773
8043
|
for (const active of skillListResult.active) {
|
|
8774
8044
|
if (excludeSlugs?.has(active.slug))
|
|
8775
8045
|
continue;
|
|
@@ -8791,7 +8061,7 @@ async function autoRetireSkills(directory, _curatorKnowledgePath, excludeSlugs)
|
|
|
8791
8061
|
const violationRate = skillUsage.length > 0 ? violations / skillUsage.length : 0;
|
|
8792
8062
|
if (violationRate > 0.3) {
|
|
8793
8063
|
const reason = `auto-retire: violation rate ${(violationRate * 100).toFixed(0)}% exceeds 30% threshold`;
|
|
8794
|
-
await
|
|
8064
|
+
await _internals9.retireSkill(directory, active.slug, reason);
|
|
8795
8065
|
observations.push(`Skill '${active.slug}' auto-retired: ${reason}`);
|
|
8796
8066
|
warn(`[curator] ${observations[observations.length - 1]}`);
|
|
8797
8067
|
continue;
|
|
@@ -8799,15 +8069,15 @@ async function autoRetireSkills(directory, _curatorKnowledgePath, excludeSlugs)
|
|
|
8799
8069
|
let archivedSourceMatched = false;
|
|
8800
8070
|
if (allArchivedIds.size > 0) {
|
|
8801
8071
|
try {
|
|
8802
|
-
const content = await
|
|
8803
|
-
const sourceIds =
|
|
8072
|
+
const content = await _internals9.readFileAsync(active.path, "utf-8");
|
|
8073
|
+
const sourceIds = _internals9.parseDraftFrontmatter(content)?.sourceKnowledgeIds ?? [];
|
|
8804
8074
|
archivedSourceMatched = sourceIds.some((id) => allArchivedIds.has(id));
|
|
8805
8075
|
} catch {
|
|
8806
8076
|
archivedSourceMatched = false;
|
|
8807
8077
|
}
|
|
8808
8078
|
}
|
|
8809
8079
|
if (archivedSourceMatched) {
|
|
8810
|
-
const result = await
|
|
8080
|
+
const result = await _internals9.retireOrMarkStale(directory, path15.dirname(active.path), allArchivedIds);
|
|
8811
8081
|
if (result.action === "retire") {
|
|
8812
8082
|
observations.push(`Skill '${active.slug}' auto-retired: all source knowledge entries archived`);
|
|
8813
8083
|
warn(`[curator] ${observations[observations.length - 1]}`);
|
|
@@ -8828,7 +8098,6 @@ function parseKnowledgeRecommendations(llmOutput) {
|
|
|
8828
8098
|
function parseKnowledgeRecommendationsWithDiagnostics(llmOutput) {
|
|
8829
8099
|
const recommendations = [];
|
|
8830
8100
|
const diagnostics = [];
|
|
8831
|
-
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;
|
|
8832
8101
|
const obsSection = llmOutput.match(/OBSERVATIONS:\s*\n([\s\S]*?)(?:\n\n|\n[A-Z_]+:|$)/);
|
|
8833
8102
|
if (obsSection) {
|
|
8834
8103
|
const lines = obsSection[1].split(`
|
|
@@ -8856,7 +8125,7 @@ function parseKnowledgeRecommendationsWithDiagnostics(llmOutput) {
|
|
|
8856
8125
|
const text2 = staleEntry[2].trim().replace(/\s+\([^)]+\)$/, "");
|
|
8857
8126
|
recommendations.push({
|
|
8858
8127
|
action: "archive",
|
|
8859
|
-
entry_id:
|
|
8128
|
+
entry_id: normalizeRecommendationEntryIdToken(uuid2),
|
|
8860
8129
|
lesson: text2,
|
|
8861
8130
|
reason: text2
|
|
8862
8131
|
});
|
|
@@ -8872,7 +8141,7 @@ function parseKnowledgeRecommendationsWithDiagnostics(llmOutput) {
|
|
|
8872
8141
|
const uuid = match[1];
|
|
8873
8142
|
const parenthetical = match[2];
|
|
8874
8143
|
const text = match[3].trim().replace(/\s+\([^)]+\)$/, "");
|
|
8875
|
-
const entryId = uuid === "new"
|
|
8144
|
+
const entryId = uuid === "new" ? undefined : normalizeRecommendationEntryIdToken(uuid);
|
|
8876
8145
|
let action = "rewrite";
|
|
8877
8146
|
const lowerParenthetical = parenthetical.toLowerCase();
|
|
8878
8147
|
if (lowerParenthetical.includes("suggests boost confidence") || lowerParenthetical.includes("mark hive_eligible") || lowerParenthetical.includes("appears high-confidence")) {
|
|
@@ -9004,11 +8273,11 @@ function arrayOfStrings(v) {
|
|
|
9004
8273
|
}
|
|
9005
8274
|
function readLatestPostMortemDigest(directory) {
|
|
9006
8275
|
try {
|
|
9007
|
-
const swarmDir =
|
|
8276
|
+
const swarmDir = path15.join(directory, ".swarm");
|
|
9008
8277
|
if (!fs5.existsSync(swarmDir))
|
|
9009
8278
|
return null;
|
|
9010
8279
|
const candidates = fs5.readdirSync(swarmDir).filter((name) => /^post-mortem-[^/\\]+\.md$/.test(name)).map((name) => {
|
|
9011
|
-
const filePath =
|
|
8280
|
+
const filePath = path15.join(swarmDir, name);
|
|
9012
8281
|
return { name, filePath, mtimeMs: fs5.statSync(filePath).mtimeMs };
|
|
9013
8282
|
}).sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
9014
8283
|
const latest = candidates[0];
|
|
@@ -9051,7 +8320,7 @@ async function readCuratorSummary(directory) {
|
|
|
9051
8320
|
}
|
|
9052
8321
|
async function writeCuratorSummary(directory, summary) {
|
|
9053
8322
|
const resolvedPath = validateSwarmPath(directory, "curator-summary.json");
|
|
9054
|
-
fs5.mkdirSync(
|
|
8323
|
+
fs5.mkdirSync(path15.dirname(resolvedPath), { recursive: true });
|
|
9055
8324
|
await bunWrite(resolvedPath, JSON.stringify(summary, null, 2));
|
|
9056
8325
|
}
|
|
9057
8326
|
function normalizeAgentName(name) {
|
|
@@ -9086,8 +8355,8 @@ function checkPhaseCompliance(phaseEvents, agentsDispatched, requiredAgents, pha
|
|
|
9086
8355
|
const observations = [];
|
|
9087
8356
|
const timestamp = new Date().toISOString();
|
|
9088
8357
|
for (const agent of requiredAgents) {
|
|
9089
|
-
const normalizedAgent =
|
|
9090
|
-
const isDispatched = agentsDispatched.some((a) =>
|
|
8358
|
+
const normalizedAgent = _internals9.normalizeAgentName(agent);
|
|
8359
|
+
const isDispatched = agentsDispatched.some((a) => _internals9.normalizeAgentName(a) === normalizedAgent);
|
|
9091
8360
|
if (!isDispatched) {
|
|
9092
8361
|
observations.push({
|
|
9093
8362
|
phase,
|
|
@@ -9106,7 +8375,7 @@ function checkPhaseCompliance(phaseEvents, agentsDispatched, requiredAgents, pha
|
|
|
9106
8375
|
if (e.type === "agent.delegation") {
|
|
9107
8376
|
const agent = e.agent;
|
|
9108
8377
|
if (agent && typeof agent === "string") {
|
|
9109
|
-
const normalized =
|
|
8378
|
+
const normalized = _internals9.normalizeAgentName(agent);
|
|
9110
8379
|
if (normalized === "coder") {
|
|
9111
8380
|
coderDelegations.push({ event: e, index: i });
|
|
9112
8381
|
} else if (normalized === "reviewer") {
|
|
@@ -9163,7 +8432,7 @@ function checkPhaseCompliance(phaseEvents, agentsDispatched, requiredAgents, pha
|
|
|
9163
8432
|
if (e.type === "agent.delegation" && e.agent) {
|
|
9164
8433
|
const agent = e.agent;
|
|
9165
8434
|
if (agent && typeof agent === "string") {
|
|
9166
|
-
const normalized =
|
|
8435
|
+
const normalized = _internals9.normalizeAgentName(agent);
|
|
9167
8436
|
if (normalized === "sme") {
|
|
9168
8437
|
smeDelegations.push({ event: e, index: i });
|
|
9169
8438
|
}
|
|
@@ -9187,7 +8456,7 @@ function checkPhaseCompliance(phaseEvents, agentsDispatched, requiredAgents, pha
|
|
|
9187
8456
|
}
|
|
9188
8457
|
async function runCuratorInit(directory, config, llmDelegate) {
|
|
9189
8458
|
try {
|
|
9190
|
-
const priorSummary = await
|
|
8459
|
+
const priorSummary = await _internals9.readCuratorSummary(directory);
|
|
9191
8460
|
const knowledgePath = resolveSwarmKnowledgePath(directory);
|
|
9192
8461
|
const allEntries = await readKnowledge(knowledgePath);
|
|
9193
8462
|
const highConfidenceEntries = allEntries.filter((e) => typeof e.confidence === "number" && e.confidence >= config.min_knowledge_confidence);
|
|
@@ -9228,7 +8497,7 @@ async function runCuratorInit(directory, config, llmDelegate) {
|
|
|
9228
8497
|
const maxContextChars = config.max_summary_tokens * 2;
|
|
9229
8498
|
briefingParts.push(contextMd.slice(0, maxContextChars));
|
|
9230
8499
|
}
|
|
9231
|
-
const latestPostMortemDigest =
|
|
8500
|
+
const latestPostMortemDigest = _internals9.readLatestPostMortemDigest(directory);
|
|
9232
8501
|
if (latestPostMortemDigest) {
|
|
9233
8502
|
briefingParts.push(`
|
|
9234
8503
|
## Latest Post-Mortem`);
|
|
@@ -9315,7 +8584,7 @@ Could not load prior session context.`,
|
|
|
9315
8584
|
}
|
|
9316
8585
|
async function runCuratorPhase(directory, phase, agentsDispatched, config, knowledgeConfig, llmDelegate) {
|
|
9317
8586
|
try {
|
|
9318
|
-
const priorSummary = await
|
|
8587
|
+
const priorSummary = await _internals9.readCuratorSummary(directory);
|
|
9319
8588
|
if (priorSummary?.phase_digests.some((d) => d.phase === phase)) {
|
|
9320
8589
|
const existingDigest = priorSummary.phase_digests.find((d) => d.phase === phase);
|
|
9321
8590
|
return {
|
|
@@ -9328,10 +8597,10 @@ async function runCuratorPhase(directory, phase, agentsDispatched, config, knowl
|
|
|
9328
8597
|
};
|
|
9329
8598
|
}
|
|
9330
8599
|
const eventsJsonlContent = await readSwarmFileAsync(directory, "events.jsonl");
|
|
9331
|
-
const phaseEvents = eventsJsonlContent ?
|
|
8600
|
+
const phaseEvents = eventsJsonlContent ? _internals9.filterPhaseEvents(eventsJsonlContent, phase) : [];
|
|
9332
8601
|
const contextMd = await readSwarmFileAsync(directory, "context.md");
|
|
9333
8602
|
const requiredAgents = ["reviewer", "test_engineer"];
|
|
9334
|
-
const complianceObservations =
|
|
8603
|
+
const complianceObservations = _internals9.checkPhaseCompliance(phaseEvents, agentsDispatched, requiredAgents, phase);
|
|
9335
8604
|
const plan = await loadPlanJsonOnly(directory);
|
|
9336
8605
|
const phaseData = plan?.phases.find((p) => p.id === phase);
|
|
9337
8606
|
const tasksCompleted = phaseData ? phaseData.tasks.filter((t) => t.status === "completed").length : 0;
|
|
@@ -9355,7 +8624,7 @@ async function runCuratorPhase(directory, phase, agentsDispatched, config, knowl
|
|
|
9355
8624
|
timestamp: new Date().toISOString(),
|
|
9356
8625
|
summary: `Phase ${phase} completed. ${tasksCompleted}/${tasksTotal} tasks completed. ${complianceObservations.length} compliance observations.`,
|
|
9357
8626
|
agents_used: [
|
|
9358
|
-
...new Set(agentsDispatched.map((a) =>
|
|
8627
|
+
...new Set(agentsDispatched.map((a) => _internals9.normalizeAgentName(a)))
|
|
9359
8628
|
],
|
|
9360
8629
|
tasks_completed: tasksCompleted,
|
|
9361
8630
|
tasks_total: tasksTotal,
|
|
@@ -9407,24 +8676,22 @@ async function runCuratorPhase(directory, phase, agentsDispatched, config, knowl
|
|
|
9407
8676
|
clearTimeout(timer);
|
|
9408
8677
|
}
|
|
9409
8678
|
if (llmOutput?.trim()) {
|
|
9410
|
-
const parsed =
|
|
8679
|
+
const parsed = _internals9.parseKnowledgeRecommendationsWithDiagnostics(llmOutput);
|
|
9411
8680
|
for (const diagnostic of parsed.diagnostics) {
|
|
9412
8681
|
warn("[curator] skipped malformed recommendation line", {
|
|
9413
8682
|
phase,
|
|
9414
8683
|
...diagnostic
|
|
9415
8684
|
});
|
|
9416
8685
|
}
|
|
9417
|
-
|
|
9418
|
-
|
|
9419
|
-
return true;
|
|
9420
|
-
}
|
|
8686
|
+
const resolvedRecommendations = resolveKnowledgeRecommendationIds(parsed.recommendations, allKnowledgeEntries);
|
|
8687
|
+
for (const diagnostic of resolvedRecommendations.diagnostics) {
|
|
9421
8688
|
warn("[curator] skipped recommendation for unknown knowledge entry", {
|
|
9422
8689
|
phase,
|
|
9423
|
-
entry_id:
|
|
9424
|
-
|
|
8690
|
+
entry_id: diagnostic.line,
|
|
8691
|
+
reason: diagnostic.reason
|
|
9425
8692
|
});
|
|
9426
|
-
|
|
9427
|
-
|
|
8693
|
+
}
|
|
8694
|
+
knowledgeRecommendations = resolvedRecommendations.recommendations.filter((recommendation) => !recommendation.entry_id || knownKnowledgeIds.has(recommendation.entry_id));
|
|
9428
8695
|
const structured = parseStructuredCuratorBlocks(llmOutput);
|
|
9429
8696
|
for (const diagnostic of structured.diagnostics) {
|
|
9430
8697
|
warn("[curator] skipped malformed structured block", {
|
|
@@ -9488,18 +8755,18 @@ async function runCuratorPhase(directory, phase, agentsDispatched, config, knowl
|
|
|
9488
8755
|
knowledge_recommendations: capKnowledgeRecommendations(knowledgeRecommendations)
|
|
9489
8756
|
};
|
|
9490
8757
|
}
|
|
9491
|
-
await
|
|
8758
|
+
await _internals9.writeCuratorSummary(directory, updatedSummary);
|
|
9492
8759
|
if (knowledgeApplicationFindings.length > 0) {
|
|
9493
8760
|
try {
|
|
9494
|
-
const evidenceDir =
|
|
8761
|
+
const evidenceDir = path15.join(directory, ".swarm", "evidence", String(phase));
|
|
9495
8762
|
fs5.mkdirSync(evidenceDir, { recursive: true });
|
|
9496
|
-
const findingsPath =
|
|
8763
|
+
const findingsPath = path15.join(evidenceDir, "curator-findings.json");
|
|
9497
8764
|
await bunWrite(findingsPath, JSON.stringify({ findings: knowledgeApplicationFindings }, null, 2));
|
|
9498
8765
|
} catch (err) {
|
|
9499
8766
|
warn(`[curator] failed to persist application findings: ${err instanceof Error ? err.message : String(err)}`);
|
|
9500
8767
|
}
|
|
9501
8768
|
}
|
|
9502
|
-
const eventsPath =
|
|
8769
|
+
const eventsPath = path15.join(directory, ".swarm", "events.jsonl");
|
|
9503
8770
|
for (const obs of complianceObservations) {
|
|
9504
8771
|
await appendKnowledge(eventsPath, {
|
|
9505
8772
|
type: "curator_compliance",
|
|
@@ -9512,7 +8779,7 @@ async function runCuratorPhase(directory, phase, agentsDispatched, config, knowl
|
|
|
9512
8779
|
}
|
|
9513
8780
|
if (config.skill_generation_enabled === true && skillCandidates.length > 0) {
|
|
9514
8781
|
try {
|
|
9515
|
-
const skillModule = await import("./skill-generator-
|
|
8782
|
+
const skillModule = await import("./skill-generator-5rsjmmfq.js");
|
|
9516
8783
|
for (const cand of skillCandidates) {
|
|
9517
8784
|
if (cand.confidence < (config.min_skill_confidence ?? DEFAULT_SKILL_MIN_CONFIDENCE)) {
|
|
9518
8785
|
continue;
|
|
@@ -9530,8 +8797,8 @@ async function runCuratorPhase(directory, phase, agentsDispatched, config, knowl
|
|
|
9530
8797
|
}
|
|
9531
8798
|
const revisedSlugs = new Set;
|
|
9532
8799
|
try {
|
|
9533
|
-
const skillListResult = await
|
|
9534
|
-
const usageEntries =
|
|
8800
|
+
const skillListResult = await _internals9.listSkills(directory);
|
|
8801
|
+
const usageEntries = _internals9.readSkillUsageEntries(directory);
|
|
9535
8802
|
let revisionCallsThisPhase = 0;
|
|
9536
8803
|
for (const active of skillListResult.active) {
|
|
9537
8804
|
if (revisionCallsThisPhase >= MAX_REVISION_CALLS_PER_PHASE)
|
|
@@ -9555,8 +8822,8 @@ async function runCuratorPhase(directory, phase, agentsDispatched, config, knowl
|
|
|
9555
8822
|
const violations = skillUsage.filter((e) => e.complianceVerdict === "violated").length;
|
|
9556
8823
|
const violationRate = violations / skillUsage.length;
|
|
9557
8824
|
if (violationRate > REVISION_VIOLATION_THRESHOLD && violationRate <= 0.3) {
|
|
9558
|
-
const content = await
|
|
9559
|
-
const fm =
|
|
8825
|
+
const content = await _internals9.readFileAsync(active.path, "utf-8");
|
|
8826
|
+
const fm = _internals9.parseDraftFrontmatter(content);
|
|
9560
8827
|
if (fm && fm.skillOrigin === "promoted_external")
|
|
9561
8828
|
continue;
|
|
9562
8829
|
const currentVersion = fm?.version ?? 1;
|
|
@@ -9567,7 +8834,7 @@ async function runCuratorPhase(directory, phase, agentsDispatched, config, knowl
|
|
|
9567
8834
|
reviewerNotes: e.reviewerNotes,
|
|
9568
8835
|
timestamp: e.timestamp
|
|
9569
8836
|
}));
|
|
9570
|
-
const result2 = await
|
|
8837
|
+
const result2 = await _internals9.reviseSkill({
|
|
9571
8838
|
directory,
|
|
9572
8839
|
slug: active.slug,
|
|
9573
8840
|
skillPath: active.path,
|
|
@@ -9586,7 +8853,7 @@ async function runCuratorPhase(directory, phase, agentsDispatched, config, knowl
|
|
|
9586
8853
|
} catch (revisionErr) {
|
|
9587
8854
|
warn(`[curator] skill revision check failed: ${revisionErr instanceof Error ? revisionErr.message : String(revisionErr)}`);
|
|
9588
8855
|
}
|
|
9589
|
-
const autoRetireObservations = await
|
|
8856
|
+
const autoRetireObservations = await _internals9.autoRetireSkills(directory, curatorKnowledgePath, revisedSlugs);
|
|
9590
8857
|
if (autoRetireObservations.length > 0) {
|
|
9591
8858
|
const retireNote = ` [${autoRetireObservations.length} skill(s) auto-retired]`;
|
|
9592
8859
|
phaseDigest.summary += retireNote;
|
|
@@ -9676,10 +8943,11 @@ async function applyCuratorKnowledgeUpdates(directory, recommendations, knowledg
|
|
|
9676
8943
|
if (knowledgeConfig == null) {
|
|
9677
8944
|
return { applied: 0, skipped: 0 };
|
|
9678
8945
|
}
|
|
9679
|
-
|
|
8946
|
+
let validRecommendations = recommendations.filter((rec) => rec != null);
|
|
9680
8947
|
const knowledgePath = resolveSwarmKnowledgePath(directory);
|
|
9681
8948
|
const appliedIds = new Set;
|
|
9682
8949
|
const foundIds = new Set;
|
|
8950
|
+
let idResolutionSkipped = 0;
|
|
9683
8951
|
const archivedPrevStatus = new Map;
|
|
9684
8952
|
const contradictedEntries = [];
|
|
9685
8953
|
await transactKnowledge(knowledgePath, (entries) => {
|
|
@@ -9691,6 +8959,12 @@ async function applyCuratorKnowledgeUpdates(directory, recommendations, knowledg
|
|
|
9691
8959
|
let modified = false;
|
|
9692
8960
|
for (const e of entries)
|
|
9693
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
|
+
}
|
|
9694
8968
|
const updatedEntries = entries.map((entry) => {
|
|
9695
8969
|
const rec = validRecommendations.find((r) => r.entry_id === entry.id);
|
|
9696
8970
|
if (!rec)
|
|
@@ -9771,6 +9045,7 @@ async function applyCuratorKnowledgeUpdates(directory, recommendations, knowledg
|
|
|
9771
9045
|
applied += txApplied;
|
|
9772
9046
|
return modified ? updatedEntries : null;
|
|
9773
9047
|
});
|
|
9048
|
+
skipped += idResolutionSkipped;
|
|
9774
9049
|
for (const { id, reason } of contradictedEntries) {
|
|
9775
9050
|
try {
|
|
9776
9051
|
await recordKnowledgeEvent(directory, {
|
|
@@ -9784,107 +9059,1031 @@ async function applyCuratorKnowledgeUpdates(directory, recommendations, knowledg
|
|
|
9784
9059
|
});
|
|
9785
9060
|
} catch {}
|
|
9786
9061
|
}
|
|
9787
|
-
if (contradictedEntries.length > 0 && knowledgeConfig.contradiction_threshold_action === "quarantine") {
|
|
9788
|
-
const { maybeQuarantineOnContradiction } = await import("./knowledge-escalator-
|
|
9789
|
-
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;
|
|
9790
9578
|
try {
|
|
9791
|
-
|
|
9579
|
+
results.push(JSON.parse(line));
|
|
9792
9580
|
} catch {}
|
|
9793
9581
|
}
|
|
9582
|
+
return results;
|
|
9583
|
+
} catch {
|
|
9584
|
+
return [];
|
|
9794
9585
|
}
|
|
9795
|
-
|
|
9796
|
-
|
|
9797
|
-
|
|
9798
|
-
|
|
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 {}
|
|
9799
9600
|
}
|
|
9800
|
-
skipped++;
|
|
9801
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 [];
|
|
9802
9611
|
}
|
|
9803
|
-
|
|
9804
|
-
|
|
9805
|
-
|
|
9612
|
+
}
|
|
9613
|
+
function collectPendingProposals(directory) {
|
|
9614
|
+
const results = [];
|
|
9615
|
+
const insightPath = path16.join(directory, ".swarm", "insight-candidates.jsonl");
|
|
9616
|
+
if (existsSync10(insightPath)) {
|
|
9806
9617
|
try {
|
|
9807
|
-
|
|
9808
|
-
|
|
9809
|
-
|
|
9810
|
-
tier: "swarm",
|
|
9811
|
-
actor: "curator",
|
|
9812
|
-
reason: rec.reason ?? "curator archive recommendation",
|
|
9813
|
-
mode: "archive",
|
|
9814
|
-
previousStatus: archivedPrevStatus.get(rec.entry_id),
|
|
9815
|
-
sourceLabel: "curator",
|
|
9816
|
-
precomputedArchivedIds
|
|
9618
|
+
results.push({
|
|
9619
|
+
source: "insight-candidates",
|
|
9620
|
+
content: readFileSync6(insightPath, "utf-8")
|
|
9817
9621
|
});
|
|
9818
|
-
} catch
|
|
9819
|
-
|
|
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
|
+
}
|
|
9820
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;
|
|
9821
9652
|
}
|
|
9822
|
-
|
|
9823
|
-
|
|
9824
|
-
|
|
9825
|
-
|
|
9826
|
-
|
|
9827
|
-
|
|
9828
|
-
|
|
9829
|
-
|
|
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)}`);
|
|
9830
9700
|
}
|
|
9831
|
-
|
|
9832
|
-
|
|
9833
|
-
skipped++;
|
|
9834
|
-
continue;
|
|
9701
|
+
if (neverApplied.length > 10) {
|
|
9702
|
+
lines.push(`- ... and ${neverApplied.length - 10} more`);
|
|
9835
9703
|
}
|
|
9836
|
-
|
|
9837
|
-
|
|
9838
|
-
|
|
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)}`);
|
|
9839
9711
|
}
|
|
9840
|
-
|
|
9841
|
-
|
|
9842
|
-
|
|
9843
|
-
|
|
9844
|
-
|
|
9845
|
-
|
|
9846
|
-
|
|
9847
|
-
|
|
9848
|
-
|
|
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)");
|
|
9849
9734
|
}
|
|
9850
9735
|
}
|
|
9851
|
-
|
|
9852
|
-
|
|
9853
|
-
|
|
9854
|
-
|
|
9855
|
-
|
|
9856
|
-
|
|
9857
|
-
|
|
9858
|
-
|
|
9859
|
-
|
|
9860
|
-
|
|
9861
|
-
|
|
9862
|
-
|
|
9863
|
-
|
|
9864
|
-
|
|
9865
|
-
|
|
9866
|
-
|
|
9867
|
-
|
|
9868
|
-
|
|
9869
|
-
|
|
9870
|
-
|
|
9871
|
-
|
|
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."]
|
|
9872
9819
|
};
|
|
9873
|
-
|
|
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) {
|
|
9874
9889
|
try {
|
|
9875
|
-
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();
|
|
9876
9999
|
} catch {}
|
|
9877
|
-
skipped++;
|
|
9878
|
-
continue;
|
|
9879
10000
|
}
|
|
9880
|
-
await appendKnowledge(knowledgePath, newEntry);
|
|
9881
|
-
applied++;
|
|
9882
|
-
currentLessons.push(lesson);
|
|
9883
10001
|
}
|
|
9884
|
-
return { applied, skipped };
|
|
9885
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
|
+
};
|
|
9886
10084
|
|
|
9887
10085
|
// src/hooks/hive-promoter.ts
|
|
10086
|
+
import path17 from "path";
|
|
9888
10087
|
function carryActionableFields(source) {
|
|
9889
10088
|
const out = {};
|
|
9890
10089
|
if (source.triggers?.length)
|
|
@@ -14535,7 +14734,7 @@ async function runFinalizeStage(ctx) {
|
|
|
14535
14734
|
}
|
|
14536
14735
|
}
|
|
14537
14736
|
try {
|
|
14538
|
-
const { CuratorConfigSchema: CCS } = await import("./schema-
|
|
14737
|
+
const { CuratorConfigSchema: CCS } = await import("./schema-s48je8zt.js");
|
|
14539
14738
|
const { config: pmLoadedConfig } = _internals17.loadPluginConfigWithMeta(ctx.directory);
|
|
14540
14739
|
const curatorCfg = CCS.parse(pmLoadedConfig.curator ?? {});
|
|
14541
14740
|
if (curatorCfg.enabled && curatorCfg.postmortem_enabled) {
|
|
@@ -16648,9 +16847,9 @@ var _internals21 = {
|
|
|
16648
16847
|
readSwarmFileAsync,
|
|
16649
16848
|
loadCuratorDeps: async () => {
|
|
16650
16849
|
const [{ CuratorConfigSchema }, curator, { createCuratorLLMDelegate: createCuratorLLMDelegate2 }] = await Promise.all([
|
|
16651
|
-
import("./schema-
|
|
16652
|
-
import("./curator-
|
|
16653
|
-
import("./curator-llm-factory-
|
|
16850
|
+
import("./schema-s48je8zt.js"),
|
|
16851
|
+
import("./curator-c7dzf9rt.js"),
|
|
16852
|
+
import("./curator-llm-factory-v4gxfnhd.js")
|
|
16654
16853
|
]);
|
|
16655
16854
|
return { CuratorConfigSchema, curator, createCuratorLLMDelegate: createCuratorLLMDelegate2 };
|
|
16656
16855
|
}
|
|
@@ -17146,7 +17345,7 @@ import { fileURLToPath } from "url";
|
|
|
17146
17345
|
// package.json
|
|
17147
17346
|
var package_default = {
|
|
17148
17347
|
name: "opencode-swarm",
|
|
17149
|
-
version: "7.110.
|
|
17348
|
+
version: "7.110.1",
|
|
17150
17349
|
description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
|
|
17151
17350
|
main: "dist/index.js",
|
|
17152
17351
|
types: "dist/index.d.ts",
|
|
@@ -18598,7 +18797,7 @@ async function handleDoctorCommand(directory, args) {
|
|
|
18598
18797
|
const result = runConfigDoctor(config, directory);
|
|
18599
18798
|
let output;
|
|
18600
18799
|
if (enableAutoFix && result.hasAutoFixableIssues) {
|
|
18601
|
-
const { runConfigDoctorWithFixes } = await import("./config-doctor-
|
|
18800
|
+
const { runConfigDoctorWithFixes } = await import("./config-doctor-q5e9yzn6.js");
|
|
18602
18801
|
const fixResult = await runConfigDoctorWithFixes(directory, config, true);
|
|
18603
18802
|
output = formatDoctorMarkdown(fixResult.result);
|
|
18604
18803
|
} else {
|
|
@@ -31871,7 +32070,7 @@ function buildDetailedHelp(commandName, entry) {
|
|
|
31871
32070
|
async function handleHelpCommand(ctx) {
|
|
31872
32071
|
const targetCommand = ctx.args.join(" ");
|
|
31873
32072
|
if (!targetCommand) {
|
|
31874
|
-
const { buildHelpText } = await import("./index-
|
|
32073
|
+
const { buildHelpText } = await import("./index-r3ck99jh.js");
|
|
31875
32074
|
return buildHelpText();
|
|
31876
32075
|
}
|
|
31877
32076
|
const tokens = targetCommand.split(/\s+/);
|
|
@@ -31880,7 +32079,7 @@ async function handleHelpCommand(ctx) {
|
|
|
31880
32079
|
return _internals46.buildDetailedHelp(resolved.key, resolved.entry);
|
|
31881
32080
|
}
|
|
31882
32081
|
const similar = _internals46.findSimilarCommands(targetCommand);
|
|
31883
|
-
const { buildHelpText: fullHelp } = await import("./index-
|
|
32082
|
+
const { buildHelpText: fullHelp } = await import("./index-r3ck99jh.js");
|
|
31884
32083
|
if (similar.length > 0) {
|
|
31885
32084
|
return `Command '/swarm ${targetCommand}' not found.
|
|
31886
32085
|
|
|
@@ -31900,7 +32099,7 @@ Showing full help:
|
|
|
31900
32099
|
}
|
|
31901
32100
|
async function handleModeCommandWithBundledSkills(ctx, handler) {
|
|
31902
32101
|
if (ctx.packageRoot) {
|
|
31903
|
-
await syncBundledProjectSkillsIfMissingAsync(ctx.directory, ctx.packageRoot);
|
|
32102
|
+
await syncBundledProjectSkillsIfMissingAsync(ctx.directory, ctx.packageRoot, true);
|
|
31904
32103
|
}
|
|
31905
32104
|
return Promise.resolve(handler(ctx.directory, ctx.args));
|
|
31906
32105
|
}
|
|
@@ -32013,7 +32212,7 @@ var COMMAND_REGISTRY = {
|
|
|
32013
32212
|
},
|
|
32014
32213
|
"guardrail explain": {
|
|
32015
32214
|
handler: async (ctx) => {
|
|
32016
|
-
const { handleGuardrailExplain } = await import("./guardrail-explain-
|
|
32215
|
+
const { handleGuardrailExplain } = await import("./guardrail-explain-qc02w4yb.js");
|
|
32017
32216
|
return handleGuardrailExplain(ctx.directory, ctx.args);
|
|
32018
32217
|
},
|
|
32019
32218
|
description: "Dry-run: show what the guardrails would do to a command or write target (executes nothing)",
|
|
@@ -32023,7 +32222,7 @@ var COMMAND_REGISTRY = {
|
|
|
32023
32222
|
},
|
|
32024
32223
|
"guardrail-log": {
|
|
32025
32224
|
handler: async (ctx) => {
|
|
32026
|
-
const { handleGuardrailLog } = await import("./guardrail-log-
|
|
32225
|
+
const { handleGuardrailLog } = await import("./guardrail-log-973nan7m.js");
|
|
32027
32226
|
return handleGuardrailLog(ctx.directory, ctx.args);
|
|
32028
32227
|
},
|
|
32029
32228
|
description: "Read the guardrail decision log (use --blocks-only for blocks)",
|
|
@@ -33776,6 +33975,9 @@ If you see references to other agents (like @reviewer, @coder, etc.) in your ins
|
|
|
33776
33975
|
WRONG: "I'll use the Task tool to call another agent to review this code"
|
|
33777
33976
|
RIGHT: "I'll read the changed files and review them myself"
|
|
33778
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
|
+
|
|
33779
33981
|
${READ_ONLY_LANE_GUIDANCE}
|
|
33780
33982
|
|
|
33781
33983
|
## REVIEW FOCUS
|
|
@@ -37116,4 +37318,4 @@ function createCuratorLLMDelegate(directory, mode = "init", sessionId) {
|
|
|
37116
37318
|
};
|
|
37117
37319
|
}
|
|
37118
37320
|
|
|
37119
|
-
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 };
|