@joshuaswarren/openclaw-engram 8.3.19 → 8.3.21
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/index.js +461 -9
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -10562,7 +10562,7 @@ title: ${opts.title.replace(/\n/g, " ").slice(0, 200)}
|
|
|
10562
10562
|
};
|
|
10563
10563
|
|
|
10564
10564
|
// src/compounding/engine.ts
|
|
10565
|
-
import { mkdir as mkdir15, readFile as readFile15, writeFile as writeFile14 } from "fs/promises";
|
|
10565
|
+
import { mkdir as mkdir15, readFile as readFile15, readdir as readdir10, writeFile as writeFile14 } from "fs/promises";
|
|
10566
10566
|
import path21 from "path";
|
|
10567
10567
|
import os4 from "os";
|
|
10568
10568
|
function isoWeekId(d) {
|
|
@@ -10574,6 +10574,22 @@ function isoWeekId(d) {
|
|
|
10574
10574
|
const yyyy = dt.getUTCFullYear();
|
|
10575
10575
|
return `${yyyy}-W${String(week).padStart(2, "0")}`;
|
|
10576
10576
|
}
|
|
10577
|
+
function isoMonthId(d) {
|
|
10578
|
+
return `${d.getUTCFullYear()}-${String(d.getUTCMonth() + 1).padStart(2, "0")}`;
|
|
10579
|
+
}
|
|
10580
|
+
function monthIdFromIsoWeek(weekId) {
|
|
10581
|
+
const match = weekId.match(/^(\d{4})-W(\d{2})$/);
|
|
10582
|
+
if (!match) return isoMonthId(/* @__PURE__ */ new Date());
|
|
10583
|
+
const year = Number(match[1]);
|
|
10584
|
+
const week = Number(match[2]);
|
|
10585
|
+
const jan4 = new Date(Date.UTC(year, 0, 4));
|
|
10586
|
+
const jan4Day = jan4.getUTCDay() || 7;
|
|
10587
|
+
const isoWeekOneMonday = new Date(jan4);
|
|
10588
|
+
isoWeekOneMonday.setUTCDate(jan4.getUTCDate() - (jan4Day - 1));
|
|
10589
|
+
const monday = new Date(isoWeekOneMonday);
|
|
10590
|
+
monday.setUTCDate(isoWeekOneMonday.getUTCDate() + (week - 1) * 7);
|
|
10591
|
+
return isoMonthId(monday);
|
|
10592
|
+
}
|
|
10577
10593
|
function sharedContextDir(config) {
|
|
10578
10594
|
if (typeof config.sharedContextDir === "string" && config.sharedContextDir.length > 0) {
|
|
10579
10595
|
return config.sharedContextDir;
|
|
@@ -10586,10 +10602,20 @@ var CompoundingEngine = class {
|
|
|
10586
10602
|
this.weeklyDir = path21.join(config.memoryDir, "compounding", "weekly");
|
|
10587
10603
|
this.mistakesPath = path21.join(config.memoryDir, "compounding", "mistakes.json");
|
|
10588
10604
|
this.feedbackInboxPath = path21.join(sharedContextDir(config), "feedback", "inbox.jsonl");
|
|
10605
|
+
this.identityAnchorPath = path21.join(config.memoryDir, "identity", "identity-anchor.md");
|
|
10606
|
+
this.identityIncidentsDir = path21.join(config.memoryDir, "identity", "incidents");
|
|
10607
|
+
this.identityAuditWeeklyDir = path21.join(config.memoryDir, "identity", "audits", "weekly");
|
|
10608
|
+
this.identityAuditMonthlyDir = path21.join(config.memoryDir, "identity", "audits", "monthly");
|
|
10609
|
+
this.identityImprovementLoopsPath = path21.join(config.memoryDir, "identity", "improvement-loops.md");
|
|
10589
10610
|
}
|
|
10590
10611
|
weeklyDir;
|
|
10591
10612
|
mistakesPath;
|
|
10592
10613
|
feedbackInboxPath;
|
|
10614
|
+
identityAnchorPath;
|
|
10615
|
+
identityIncidentsDir;
|
|
10616
|
+
identityAuditWeeklyDir;
|
|
10617
|
+
identityAuditMonthlyDir;
|
|
10618
|
+
identityImprovementLoopsPath;
|
|
10593
10619
|
async ensureDirs() {
|
|
10594
10620
|
await mkdir15(this.weeklyDir, { recursive: true });
|
|
10595
10621
|
await mkdir15(path21.dirname(this.mistakesPath), { recursive: true });
|
|
@@ -10599,13 +10625,77 @@ var CompoundingEngine = class {
|
|
|
10599
10625
|
const weekId = opts?.weekId ?? isoWeekId(/* @__PURE__ */ new Date());
|
|
10600
10626
|
const entries = await this.readFeedbackEntriesForWeek(weekId);
|
|
10601
10627
|
const mistakes = this.buildMistakes(entries);
|
|
10628
|
+
const continuity = await this.readContinuityAuditReferences(weekId);
|
|
10602
10629
|
const reportPath = path21.join(this.weeklyDir, `${weekId}.md`);
|
|
10603
|
-
const md = this.formatWeeklyReport(weekId, entries, mistakes.patterns);
|
|
10630
|
+
const md = this.formatWeeklyReport(weekId, entries, mistakes.patterns, continuity);
|
|
10604
10631
|
await writeFile14(reportPath, md, "utf-8");
|
|
10605
10632
|
await writeFile14(this.mistakesPath, JSON.stringify(mistakes, null, 2) + "\n", "utf-8");
|
|
10606
10633
|
log.info(`compounding: wrote weekly=${reportPath} mistakes=${this.mistakesPath}`);
|
|
10607
10634
|
return { weekId, reportPath, mistakesCount: mistakes.patterns.length };
|
|
10608
10635
|
}
|
|
10636
|
+
async synthesizeContinuityAudit(opts) {
|
|
10637
|
+
const period = opts?.period === "monthly" ? "monthly" : "weekly";
|
|
10638
|
+
const key = opts?.key?.trim() || (period === "weekly" ? isoWeekId(/* @__PURE__ */ new Date()) : isoMonthId(/* @__PURE__ */ new Date()));
|
|
10639
|
+
const nowIso = (/* @__PURE__ */ new Date()).toISOString();
|
|
10640
|
+
const [anchorPresent, improvementLoopPresent, incidents, mistakes] = await Promise.all([
|
|
10641
|
+
this.readNonEmptyFile(this.identityAnchorPath),
|
|
10642
|
+
this.readNonEmptyFile(this.identityImprovementLoopsPath),
|
|
10643
|
+
this.readContinuityIncidents(),
|
|
10644
|
+
this.readMistakes()
|
|
10645
|
+
]);
|
|
10646
|
+
const openIncidents = incidents.filter((i) => i.state === "open");
|
|
10647
|
+
const closedIncidents = incidents.filter((i) => i.state === "closed");
|
|
10648
|
+
const hardeningCandidates = [];
|
|
10649
|
+
if (!anchorPresent) {
|
|
10650
|
+
hardeningCandidates.push("Create/update identity anchor baseline and verify recovery injection path.");
|
|
10651
|
+
}
|
|
10652
|
+
if (openIncidents.length > 0) {
|
|
10653
|
+
hardeningCandidates.push(
|
|
10654
|
+
`Close or downgrade ${openIncidents.length} open continuity incident${openIncidents.length === 1 ? "" : "s"}.`
|
|
10655
|
+
);
|
|
10656
|
+
}
|
|
10657
|
+
if (!improvementLoopPresent) {
|
|
10658
|
+
hardeningCandidates.push("Initialize continuity improvement-loops register with cadence and kill conditions.");
|
|
10659
|
+
}
|
|
10660
|
+
if ((mistakes?.patterns.length ?? 0) > 0) {
|
|
10661
|
+
hardeningCandidates.push("Review latest compounding mistakes and convert one pattern into preventive continuity rule.");
|
|
10662
|
+
}
|
|
10663
|
+
const nextAction = hardeningCandidates[0] ?? "No critical drift detected; keep weekly/monthly continuity audit cadence.";
|
|
10664
|
+
const lines = [
|
|
10665
|
+
`# Continuity Audit \u2014 ${period} ${key}`,
|
|
10666
|
+
"",
|
|
10667
|
+
`Generated: ${nowIso}`,
|
|
10668
|
+
`Scope: ${period}`,
|
|
10669
|
+
"",
|
|
10670
|
+
"## Signal Summary",
|
|
10671
|
+
`- Identity anchor present: ${anchorPresent ? "yes" : "no"}`,
|
|
10672
|
+
`- Improvement-loop register present: ${improvementLoopPresent ? "yes" : "no"}`,
|
|
10673
|
+
`- Open incidents: ${openIncidents.length}`,
|
|
10674
|
+
`- Closed incidents: ${closedIncidents.length}`,
|
|
10675
|
+
`- Compounding mistake patterns: ${mistakes?.patterns.length ?? 0}`,
|
|
10676
|
+
"",
|
|
10677
|
+
"## Drift Checks",
|
|
10678
|
+
`- Identity anchor drift: ${anchorPresent ? "pass" : "needs attention"}`,
|
|
10679
|
+
`- Incident backlog: ${openIncidents.length === 0 ? "pass" : "needs attention"}`,
|
|
10680
|
+
`- Improvement-loop coverage: ${improvementLoopPresent ? "pass" : "needs attention"}`,
|
|
10681
|
+
"",
|
|
10682
|
+
"## Stale Rule Detection",
|
|
10683
|
+
`- Open incidents older than closure window: ${openIncidents.length > 0 ? "possible" : "none detected"}`,
|
|
10684
|
+
`- Preventive rule coverage on closed incidents: ${closedIncidents.some((i) => (i.preventiveRule ?? "").trim().length > 0) ? "present" : "not detected"}`,
|
|
10685
|
+
"",
|
|
10686
|
+
"## Next Hardening Action",
|
|
10687
|
+
`- ${nextAction}`,
|
|
10688
|
+
"",
|
|
10689
|
+
"## Open Incident IDs",
|
|
10690
|
+
...openIncidents.length > 0 ? openIncidents.slice(0, 20).map((i) => `- ${i.id}`) : ["- (none)"],
|
|
10691
|
+
""
|
|
10692
|
+
];
|
|
10693
|
+
const dir = period === "weekly" ? this.identityAuditWeeklyDir : this.identityAuditMonthlyDir;
|
|
10694
|
+
await mkdir15(dir, { recursive: true });
|
|
10695
|
+
const reportPath = path21.join(dir, `${key}.md`);
|
|
10696
|
+
await writeFile14(reportPath, lines.join("\n"), "utf-8");
|
|
10697
|
+
return { period, key, reportPath };
|
|
10698
|
+
}
|
|
10609
10699
|
async readMistakes() {
|
|
10610
10700
|
try {
|
|
10611
10701
|
const raw = await readFile15(this.mistakesPath, "utf-8");
|
|
@@ -10650,7 +10740,7 @@ var CompoundingEngine = class {
|
|
|
10650
10740
|
const uniq = Array.from(new Set(patterns)).slice(0, 500);
|
|
10651
10741
|
return { updatedAt: (/* @__PURE__ */ new Date()).toISOString(), patterns: uniq };
|
|
10652
10742
|
}
|
|
10653
|
-
formatWeeklyReport(weekId, entries, patterns) {
|
|
10743
|
+
formatWeeklyReport(weekId, entries, patterns, continuity) {
|
|
10654
10744
|
const byAgent = /* @__PURE__ */ new Map();
|
|
10655
10745
|
for (const e of entries) {
|
|
10656
10746
|
const list = byAgent.get(e.agent) ?? [];
|
|
@@ -10689,8 +10779,61 @@ var CompoundingEngine = class {
|
|
|
10689
10779
|
for (const p of patterns.slice(0, 100)) lines.push(`- ${p}`);
|
|
10690
10780
|
}
|
|
10691
10781
|
lines.push("");
|
|
10782
|
+
if (this.config.continuityAuditEnabled) {
|
|
10783
|
+
lines.push("## Continuity Audits");
|
|
10784
|
+
if (continuity.weeklyPath) {
|
|
10785
|
+
lines.push(`- weekly: ${continuity.weeklyPath}`);
|
|
10786
|
+
} else {
|
|
10787
|
+
lines.push(`- weekly: (missing for ${weekId})`);
|
|
10788
|
+
}
|
|
10789
|
+
if (continuity.monthlyPath) {
|
|
10790
|
+
lines.push(`- monthly: ${continuity.monthlyPath}`);
|
|
10791
|
+
} else {
|
|
10792
|
+
lines.push(`- monthly: (missing for ${continuity.monthId})`);
|
|
10793
|
+
}
|
|
10794
|
+
lines.push("");
|
|
10795
|
+
}
|
|
10692
10796
|
return lines.join("\n");
|
|
10693
10797
|
}
|
|
10798
|
+
async readNonEmptyFile(filePath) {
|
|
10799
|
+
try {
|
|
10800
|
+
const raw = await readFile15(filePath, "utf-8");
|
|
10801
|
+
return raw.trim().length > 0;
|
|
10802
|
+
} catch {
|
|
10803
|
+
return false;
|
|
10804
|
+
}
|
|
10805
|
+
}
|
|
10806
|
+
async readContinuityIncidents() {
|
|
10807
|
+
const incidents = [];
|
|
10808
|
+
try {
|
|
10809
|
+
const names = await readdir10(this.identityIncidentsDir);
|
|
10810
|
+
const files = names.filter((n) => n.endsWith(".md")).sort().reverse();
|
|
10811
|
+
for (const file of files) {
|
|
10812
|
+
const filePath = path21.join(this.identityIncidentsDir, file);
|
|
10813
|
+
try {
|
|
10814
|
+
const raw = await readFile15(filePath, "utf-8");
|
|
10815
|
+
const parsed = parseContinuityIncident(raw);
|
|
10816
|
+
if (parsed) incidents.push(parsed);
|
|
10817
|
+
} catch {
|
|
10818
|
+
}
|
|
10819
|
+
}
|
|
10820
|
+
} catch {
|
|
10821
|
+
}
|
|
10822
|
+
return incidents;
|
|
10823
|
+
}
|
|
10824
|
+
async readContinuityAuditReferences(weekId) {
|
|
10825
|
+
const monthId = monthIdFromIsoWeek(weekId);
|
|
10826
|
+
const weeklyPath = path21.join(this.identityAuditWeeklyDir, `${weekId}.md`);
|
|
10827
|
+
const monthlyPath = path21.join(this.identityAuditMonthlyDir, `${monthId}.md`);
|
|
10828
|
+
const weeklyExists = await this.readNonEmptyFile(weeklyPath);
|
|
10829
|
+
const monthlyExists = await this.readNonEmptyFile(monthlyPath);
|
|
10830
|
+
return {
|
|
10831
|
+
weekId,
|
|
10832
|
+
monthId,
|
|
10833
|
+
weeklyPath: weeklyExists ? weeklyPath : null,
|
|
10834
|
+
monthlyPath: monthlyExists ? monthlyPath : null
|
|
10835
|
+
};
|
|
10836
|
+
}
|
|
10694
10837
|
};
|
|
10695
10838
|
|
|
10696
10839
|
// src/orchestrator.ts
|
|
@@ -13914,6 +14057,22 @@ function mergeIdentityAnchor(existingRaw, updates) {
|
|
|
13914
14057
|
}
|
|
13915
14058
|
return lines.join("\n").replace(/\n{3,}/g, "\n\n").trimEnd() + "\n";
|
|
13916
14059
|
}
|
|
14060
|
+
function formatContinuityIncidentSummary(incident, index) {
|
|
14061
|
+
const prefix = typeof index === "number" ? `### [${index + 1}] ` : "### ";
|
|
14062
|
+
const lines = [
|
|
14063
|
+
`${prefix}${incident.id} (${incident.state})`,
|
|
14064
|
+
`Opened: ${incident.openedAt}`
|
|
14065
|
+
];
|
|
14066
|
+
if (incident.closedAt) lines.push(`Closed: ${incident.closedAt}`);
|
|
14067
|
+
if (incident.triggerWindow) lines.push(`Window: ${incident.triggerWindow}`);
|
|
14068
|
+
lines.push("", `Symptom: ${incident.symptom}`);
|
|
14069
|
+
if (incident.suspectedCause) lines.push(`Suspected Cause: ${incident.suspectedCause}`);
|
|
14070
|
+
if (incident.fixApplied) lines.push(`Fix Applied: ${incident.fixApplied}`);
|
|
14071
|
+
if (incident.verificationResult) lines.push(`Verification: ${incident.verificationResult}`);
|
|
14072
|
+
if (incident.preventiveRule) lines.push(`Preventive Rule: ${incident.preventiveRule}`);
|
|
14073
|
+
if (incident.filePath) lines.push(`Path: ${incident.filePath}`);
|
|
14074
|
+
return lines.join("\n");
|
|
14075
|
+
}
|
|
13917
14076
|
function registerTools(api, orchestrator) {
|
|
13918
14077
|
const actionTypes = [
|
|
13919
14078
|
"store_episode",
|
|
@@ -13995,6 +14154,200 @@ ${formatted}`
|
|
|
13995
14154
|
},
|
|
13996
14155
|
{ name: "memory_search" }
|
|
13997
14156
|
);
|
|
14157
|
+
api.registerTool(
|
|
14158
|
+
{
|
|
14159
|
+
name: "continuity_audit_generate",
|
|
14160
|
+
label: "Generate Continuity Audit",
|
|
14161
|
+
description: "Generate a deterministic identity continuity audit report (weekly/monthly) and persist it under identity/audits.",
|
|
14162
|
+
parameters: Type.Object({
|
|
14163
|
+
period: Type.Optional(
|
|
14164
|
+
Type.String({
|
|
14165
|
+
enum: ["weekly", "monthly"],
|
|
14166
|
+
description: "Audit period. Defaults to weekly."
|
|
14167
|
+
})
|
|
14168
|
+
),
|
|
14169
|
+
key: Type.Optional(
|
|
14170
|
+
Type.String({
|
|
14171
|
+
description: "Optional period key (weekly: YYYY-Www, monthly: YYYY-MM). Defaults to current period."
|
|
14172
|
+
})
|
|
14173
|
+
)
|
|
14174
|
+
}),
|
|
14175
|
+
async execute(_toolCallId, params) {
|
|
14176
|
+
if (!orchestrator.config.identityContinuityEnabled) {
|
|
14177
|
+
return toolResult(
|
|
14178
|
+
"Identity continuity is disabled. Enable `identityContinuityEnabled: true` to generate continuity audits."
|
|
14179
|
+
);
|
|
14180
|
+
}
|
|
14181
|
+
if (!orchestrator.config.continuityAuditEnabled) {
|
|
14182
|
+
return toolResult(
|
|
14183
|
+
"Continuity audits are disabled. Enable `continuityAuditEnabled: true` to generate continuity audits."
|
|
14184
|
+
);
|
|
14185
|
+
}
|
|
14186
|
+
if (!orchestrator.compounding) {
|
|
14187
|
+
return toolResult(
|
|
14188
|
+
"Compounding engine is disabled. Enable `compoundingEnabled: true` to generate continuity audits."
|
|
14189
|
+
);
|
|
14190
|
+
}
|
|
14191
|
+
const period = params.period === "monthly" ? "monthly" : "weekly";
|
|
14192
|
+
const key = typeof params.key === "string" ? params.key : void 0;
|
|
14193
|
+
const audit = await orchestrator.compounding.synthesizeContinuityAudit({
|
|
14194
|
+
period,
|
|
14195
|
+
key
|
|
14196
|
+
});
|
|
14197
|
+
return toolResult(
|
|
14198
|
+
`OK
|
|
14199
|
+
|
|
14200
|
+
period: ${audit.period}
|
|
14201
|
+
key: ${audit.key}
|
|
14202
|
+
report: ${audit.reportPath}`
|
|
14203
|
+
);
|
|
14204
|
+
}
|
|
14205
|
+
},
|
|
14206
|
+
{ name: "continuity_audit_generate" }
|
|
14207
|
+
);
|
|
14208
|
+
api.registerTool(
|
|
14209
|
+
{
|
|
14210
|
+
name: "continuity_incident_open",
|
|
14211
|
+
label: "Open Continuity Incident",
|
|
14212
|
+
description: "Create a new continuity incident record in append-only storage.",
|
|
14213
|
+
parameters: Type.Object({
|
|
14214
|
+
symptom: Type.String({
|
|
14215
|
+
description: "Observed continuity failure symptom."
|
|
14216
|
+
}),
|
|
14217
|
+
triggerWindow: Type.Optional(
|
|
14218
|
+
Type.String({
|
|
14219
|
+
description: "Optional time window when incident occurred."
|
|
14220
|
+
})
|
|
14221
|
+
),
|
|
14222
|
+
suspectedCause: Type.Optional(
|
|
14223
|
+
Type.String({
|
|
14224
|
+
description: "Optional suspected root cause."
|
|
14225
|
+
})
|
|
14226
|
+
)
|
|
14227
|
+
}),
|
|
14228
|
+
async execute(_toolCallId, params) {
|
|
14229
|
+
if (!orchestrator.config.identityContinuityEnabled) {
|
|
14230
|
+
return toolResult(
|
|
14231
|
+
"Identity continuity is disabled. Enable `identityContinuityEnabled: true` to open incidents."
|
|
14232
|
+
);
|
|
14233
|
+
}
|
|
14234
|
+
if (!orchestrator.config.continuityIncidentLoggingEnabled) {
|
|
14235
|
+
return toolResult(
|
|
14236
|
+
"Continuity incident logging is disabled. Enable `continuityIncidentLoggingEnabled: true` to open incidents."
|
|
14237
|
+
);
|
|
14238
|
+
}
|
|
14239
|
+
const symptom = typeof params.symptom === "string" ? params.symptom.trim() : "";
|
|
14240
|
+
if (!symptom) {
|
|
14241
|
+
return toolResult("Missing required field: symptom");
|
|
14242
|
+
}
|
|
14243
|
+
const created = await orchestrator.storage.appendContinuityIncident({
|
|
14244
|
+
symptom,
|
|
14245
|
+
triggerWindow: typeof params.triggerWindow === "string" ? params.triggerWindow : void 0,
|
|
14246
|
+
suspectedCause: typeof params.suspectedCause === "string" ? params.suspectedCause : void 0
|
|
14247
|
+
});
|
|
14248
|
+
log.info(`continuity-incident open id=${created.id}`);
|
|
14249
|
+
return toolResult(`Continuity incident opened.
|
|
14250
|
+
|
|
14251
|
+
${formatContinuityIncidentSummary(created)}`);
|
|
14252
|
+
}
|
|
14253
|
+
},
|
|
14254
|
+
{ name: "continuity_incident_open" }
|
|
14255
|
+
);
|
|
14256
|
+
api.registerTool(
|
|
14257
|
+
{
|
|
14258
|
+
name: "continuity_incident_close",
|
|
14259
|
+
label: "Close Continuity Incident",
|
|
14260
|
+
description: "Close an open continuity incident with required verification details.",
|
|
14261
|
+
parameters: Type.Object({
|
|
14262
|
+
id: Type.String({
|
|
14263
|
+
description: "Incident ID to close."
|
|
14264
|
+
}),
|
|
14265
|
+
fixApplied: Type.String({
|
|
14266
|
+
description: "What fix was applied."
|
|
14267
|
+
}),
|
|
14268
|
+
verificationResult: Type.String({
|
|
14269
|
+
description: "How closure was verified."
|
|
14270
|
+
}),
|
|
14271
|
+
preventiveRule: Type.Optional(
|
|
14272
|
+
Type.String({
|
|
14273
|
+
description: "Optional preventive follow-up rule."
|
|
14274
|
+
})
|
|
14275
|
+
)
|
|
14276
|
+
}),
|
|
14277
|
+
async execute(_toolCallId, params) {
|
|
14278
|
+
if (!orchestrator.config.identityContinuityEnabled) {
|
|
14279
|
+
return toolResult(
|
|
14280
|
+
"Identity continuity is disabled. Enable `identityContinuityEnabled: true` to close incidents."
|
|
14281
|
+
);
|
|
14282
|
+
}
|
|
14283
|
+
if (!orchestrator.config.continuityIncidentLoggingEnabled) {
|
|
14284
|
+
return toolResult(
|
|
14285
|
+
"Continuity incident logging is disabled. Enable `continuityIncidentLoggingEnabled: true` to close incidents."
|
|
14286
|
+
);
|
|
14287
|
+
}
|
|
14288
|
+
const id = typeof params.id === "string" ? params.id.trim() : "";
|
|
14289
|
+
const fixApplied = typeof params.fixApplied === "string" ? params.fixApplied.trim() : "";
|
|
14290
|
+
const verificationResult = typeof params.verificationResult === "string" ? params.verificationResult.trim() : "";
|
|
14291
|
+
const preventiveRule = typeof params.preventiveRule === "string" ? params.preventiveRule.trim() : void 0;
|
|
14292
|
+
if (!id) return toolResult("Missing required field: id");
|
|
14293
|
+
if (!fixApplied) return toolResult("Missing required field: fixApplied");
|
|
14294
|
+
if (!verificationResult) return toolResult("Missing required field: verificationResult");
|
|
14295
|
+
const closed = await orchestrator.storage.closeContinuityIncident(id, {
|
|
14296
|
+
fixApplied,
|
|
14297
|
+
verificationResult,
|
|
14298
|
+
preventiveRule
|
|
14299
|
+
});
|
|
14300
|
+
if (!closed) return toolResult(`Incident not found: ${id}`);
|
|
14301
|
+
log.info(`continuity-incident close id=${id}`);
|
|
14302
|
+
return toolResult(`Continuity incident closed.
|
|
14303
|
+
|
|
14304
|
+
${formatContinuityIncidentSummary(closed)}`);
|
|
14305
|
+
}
|
|
14306
|
+
},
|
|
14307
|
+
{ name: "continuity_incident_close" }
|
|
14308
|
+
);
|
|
14309
|
+
api.registerTool(
|
|
14310
|
+
{
|
|
14311
|
+
name: "continuity_incident_list",
|
|
14312
|
+
label: "List Continuity Incidents",
|
|
14313
|
+
description: "List continuity incidents and optionally filter by state.",
|
|
14314
|
+
parameters: Type.Object({
|
|
14315
|
+
state: Type.Optional(
|
|
14316
|
+
Type.String({
|
|
14317
|
+
enum: ["open", "closed", "all"],
|
|
14318
|
+
description: "Incident state filter (default: open)."
|
|
14319
|
+
})
|
|
14320
|
+
),
|
|
14321
|
+
limit: Type.Optional(
|
|
14322
|
+
Type.Number({
|
|
14323
|
+
description: "Max incidents to return (default: 25, max: 200).",
|
|
14324
|
+
minimum: 1,
|
|
14325
|
+
maximum: 200
|
|
14326
|
+
})
|
|
14327
|
+
)
|
|
14328
|
+
}),
|
|
14329
|
+
async execute(_toolCallId, params) {
|
|
14330
|
+
if (!orchestrator.config.identityContinuityEnabled) {
|
|
14331
|
+
return toolResult(
|
|
14332
|
+
"Identity continuity is disabled. Enable `identityContinuityEnabled: true` to list incidents."
|
|
14333
|
+
);
|
|
14334
|
+
}
|
|
14335
|
+
const state = params.state === "closed" || params.state === "all" ? params.state : "open";
|
|
14336
|
+
const limitRaw = typeof params.limit === "number" ? params.limit : 25;
|
|
14337
|
+
const limit = Math.max(1, Math.min(200, Math.floor(limitRaw)));
|
|
14338
|
+
const incidents = await orchestrator.storage.readContinuityIncidents(limit);
|
|
14339
|
+
const filtered = state === "all" ? incidents : incidents.filter((incident) => incident.state === state);
|
|
14340
|
+
if (filtered.length === 0) {
|
|
14341
|
+
return toolResult(`No continuity incidents found for state=${state}.`);
|
|
14342
|
+
}
|
|
14343
|
+
const body = filtered.map((incident, index) => formatContinuityIncidentSummary(incident, index)).join("\n\n");
|
|
14344
|
+
return toolResult(`## Continuity Incidents (${filtered.length}, state=${state})
|
|
14345
|
+
|
|
14346
|
+
${body}`);
|
|
14347
|
+
}
|
|
14348
|
+
},
|
|
14349
|
+
{ name: "continuity_incident_list" }
|
|
14350
|
+
);
|
|
13998
14351
|
api.registerTool(
|
|
13999
14352
|
{
|
|
14000
14353
|
name: "identity_anchor_get",
|
|
@@ -14886,7 +15239,7 @@ mistakes: ${res.mistakesCount} patterns`
|
|
|
14886
15239
|
|
|
14887
15240
|
// src/cli.ts
|
|
14888
15241
|
import path33 from "path";
|
|
14889
|
-
import { access as access2, readFile as readFile22, readdir as
|
|
15242
|
+
import { access as access2, readFile as readFile22, readdir as readdir13, unlink as unlink3 } from "fs/promises";
|
|
14890
15243
|
|
|
14891
15244
|
// src/transfer/export-json.ts
|
|
14892
15245
|
import path25 from "path";
|
|
@@ -14898,7 +15251,7 @@ var EXPORT_SCHEMA_VERSION = 1;
|
|
|
14898
15251
|
|
|
14899
15252
|
// src/transfer/fs-utils.ts
|
|
14900
15253
|
import { createHash as createHash6 } from "crypto";
|
|
14901
|
-
import { mkdir as mkdir17, readdir as
|
|
15254
|
+
import { mkdir as mkdir17, readdir as readdir11, readFile as readFile17, stat as stat3, writeFile as writeFile16 } from "fs/promises";
|
|
14902
15255
|
import path24 from "path";
|
|
14903
15256
|
async function sha256File(filePath) {
|
|
14904
15257
|
const buf = await readFile17(filePath);
|
|
@@ -14921,7 +15274,7 @@ async function readJsonFile(filePath) {
|
|
|
14921
15274
|
async function listFilesRecursive(rootDir) {
|
|
14922
15275
|
const out = [];
|
|
14923
15276
|
async function walk(dir) {
|
|
14924
|
-
const entries = await
|
|
15277
|
+
const entries = await readdir11(dir, { withFileTypes: true });
|
|
14925
15278
|
for (const ent of entries) {
|
|
14926
15279
|
const fp = path24.join(dir, ent.name);
|
|
14927
15280
|
if (ent.isDirectory()) {
|
|
@@ -15049,7 +15402,7 @@ async function looksLikeEngramMdExport(fromDir) {
|
|
|
15049
15402
|
|
|
15050
15403
|
// src/transfer/backup.ts
|
|
15051
15404
|
import path27 from "path";
|
|
15052
|
-
import { mkdir as mkdir20, readdir as
|
|
15405
|
+
import { mkdir as mkdir20, readdir as readdir12, rm as rm2 } from "fs/promises";
|
|
15053
15406
|
function timestampDirName(now) {
|
|
15054
15407
|
return now.toISOString().replace(/[:.]/g, "-");
|
|
15055
15408
|
}
|
|
@@ -15070,7 +15423,7 @@ async function backupMemoryDir(opts) {
|
|
|
15070
15423
|
return backupDir;
|
|
15071
15424
|
}
|
|
15072
15425
|
async function enforceRetention(outDirAbs, retentionDays) {
|
|
15073
|
-
const entries = await
|
|
15426
|
+
const entries = await readdir12(outDirAbs, { withFileTypes: true });
|
|
15074
15427
|
const cutoffMs = Date.now() - retentionDays * 24 * 60 * 60 * 1e3;
|
|
15075
15428
|
for (const ent of entries) {
|
|
15076
15429
|
if (!ent.isDirectory()) continue;
|
|
@@ -15439,7 +15792,7 @@ async function readAllMemoryFiles(memoryDir) {
|
|
|
15439
15792
|
const walk = async (dir) => {
|
|
15440
15793
|
let entries;
|
|
15441
15794
|
try {
|
|
15442
|
-
entries = await
|
|
15795
|
+
entries = await readdir13(dir, { withFileTypes: true });
|
|
15443
15796
|
} catch {
|
|
15444
15797
|
return;
|
|
15445
15798
|
}
|
|
@@ -15482,6 +15835,21 @@ async function readAllMemoryFiles(memoryDir) {
|
|
|
15482
15835
|
}
|
|
15483
15836
|
return out;
|
|
15484
15837
|
}
|
|
15838
|
+
function formatContinuityIncidentCli(incident) {
|
|
15839
|
+
const lines = [
|
|
15840
|
+
`${incident.id} [${incident.state}]`,
|
|
15841
|
+
` opened: ${incident.openedAt}`
|
|
15842
|
+
];
|
|
15843
|
+
if (incident.closedAt) lines.push(` closed: ${incident.closedAt}`);
|
|
15844
|
+
if (incident.triggerWindow) lines.push(` window: ${incident.triggerWindow}`);
|
|
15845
|
+
lines.push(` symptom: ${incident.symptom}`);
|
|
15846
|
+
if (incident.suspectedCause) lines.push(` suspected-cause: ${incident.suspectedCause}`);
|
|
15847
|
+
if (incident.fixApplied) lines.push(` fix-applied: ${incident.fixApplied}`);
|
|
15848
|
+
if (incident.verificationResult) lines.push(` verification: ${incident.verificationResult}`);
|
|
15849
|
+
if (incident.preventiveRule) lines.push(` preventive-rule: ${incident.preventiveRule}`);
|
|
15850
|
+
if (incident.filePath) lines.push(` path: ${incident.filePath}`);
|
|
15851
|
+
return lines.join("\n");
|
|
15852
|
+
}
|
|
15485
15853
|
function registerCli(api, orchestrator) {
|
|
15486
15854
|
api.registerCli(
|
|
15487
15855
|
({ program }) => {
|
|
@@ -15868,6 +16236,90 @@ function registerCli(api, orchestrator) {
|
|
|
15868
16236
|
}
|
|
15869
16237
|
console.log(identity);
|
|
15870
16238
|
});
|
|
16239
|
+
const continuityCmd = cmd.command("continuity").description("Identity continuity incident workflow commands");
|
|
16240
|
+
continuityCmd.command("incidents").description("List continuity incidents").option("--state <state>", "Filter by state: open|closed|all", "open").option("--limit <number>", "Maximum incidents to list", "25").action(async (...args) => {
|
|
16241
|
+
if (!orchestrator.config.identityContinuityEnabled) {
|
|
16242
|
+
console.log("Identity continuity is disabled.");
|
|
16243
|
+
return;
|
|
16244
|
+
}
|
|
16245
|
+
const options = args[0] ?? {};
|
|
16246
|
+
const stateRaw = String(options.state ?? "open").toLowerCase();
|
|
16247
|
+
const state = stateRaw === "closed" || stateRaw === "all" ? stateRaw : "open";
|
|
16248
|
+
const limit = Math.max(1, Math.min(200, parseInt(String(options.limit ?? "25"), 10) || 25));
|
|
16249
|
+
const incidents = await orchestrator.storage.readContinuityIncidents(limit);
|
|
16250
|
+
const filtered = state === "all" ? incidents : incidents.filter((incident) => incident.state === state);
|
|
16251
|
+
if (filtered.length === 0) {
|
|
16252
|
+
console.log(`No continuity incidents found for state=${state}.`);
|
|
16253
|
+
return;
|
|
16254
|
+
}
|
|
16255
|
+
console.log(`=== Continuity Incidents (${filtered.length}, state=${state}) ===
|
|
16256
|
+
`);
|
|
16257
|
+
for (const incident of filtered) {
|
|
16258
|
+
console.log(formatContinuityIncidentCli(incident));
|
|
16259
|
+
console.log();
|
|
16260
|
+
}
|
|
16261
|
+
});
|
|
16262
|
+
continuityCmd.command("incident-open").description("Open a continuity incident").option("--symptom <text>", "Required symptom description").option("--trigger-window <window>", "Optional incident trigger window").option("--suspected-cause <text>", "Optional suspected cause").action(async (...args) => {
|
|
16263
|
+
if (!orchestrator.config.identityContinuityEnabled) {
|
|
16264
|
+
console.log("Identity continuity is disabled.");
|
|
16265
|
+
return;
|
|
16266
|
+
}
|
|
16267
|
+
if (!orchestrator.config.continuityIncidentLoggingEnabled) {
|
|
16268
|
+
console.log("Continuity incident logging is disabled.");
|
|
16269
|
+
return;
|
|
16270
|
+
}
|
|
16271
|
+
const options = args[0] ?? {};
|
|
16272
|
+
const symptom = String(options.symptom ?? "").trim();
|
|
16273
|
+
if (!symptom) {
|
|
16274
|
+
console.log("Missing required --symptom.");
|
|
16275
|
+
return;
|
|
16276
|
+
}
|
|
16277
|
+
const created = await orchestrator.storage.appendContinuityIncident({
|
|
16278
|
+
symptom,
|
|
16279
|
+
triggerWindow: options.triggerWindow ? String(options.triggerWindow) : void 0,
|
|
16280
|
+
suspectedCause: options.suspectedCause ? String(options.suspectedCause) : void 0
|
|
16281
|
+
});
|
|
16282
|
+
console.log("Opened continuity incident:\n");
|
|
16283
|
+
console.log(formatContinuityIncidentCli(created));
|
|
16284
|
+
});
|
|
16285
|
+
continuityCmd.command("incident-close").description("Close a continuity incident").option("--id <id>", "Required incident ID").option("--fix-applied <text>", "Required fix description").option("--verification-result <text>", "Required verification result").option("--preventive-rule <text>", "Optional preventive rule").action(async (...args) => {
|
|
16286
|
+
if (!orchestrator.config.identityContinuityEnabled) {
|
|
16287
|
+
console.log("Identity continuity is disabled.");
|
|
16288
|
+
return;
|
|
16289
|
+
}
|
|
16290
|
+
if (!orchestrator.config.continuityIncidentLoggingEnabled) {
|
|
16291
|
+
console.log("Continuity incident logging is disabled.");
|
|
16292
|
+
return;
|
|
16293
|
+
}
|
|
16294
|
+
const options = args[0] ?? {};
|
|
16295
|
+
const id = String(options.id ?? "").trim();
|
|
16296
|
+
const fixApplied = String(options.fixApplied ?? "").trim();
|
|
16297
|
+
const verificationResult = String(options.verificationResult ?? "").trim();
|
|
16298
|
+
const preventiveRule = options.preventiveRule ? String(options.preventiveRule).trim() : void 0;
|
|
16299
|
+
if (!id) {
|
|
16300
|
+
console.log("Missing required --id.");
|
|
16301
|
+
return;
|
|
16302
|
+
}
|
|
16303
|
+
if (!fixApplied) {
|
|
16304
|
+
console.log("Missing required --fix-applied.");
|
|
16305
|
+
return;
|
|
16306
|
+
}
|
|
16307
|
+
if (!verificationResult) {
|
|
16308
|
+
console.log("Missing required --verification-result.");
|
|
16309
|
+
return;
|
|
16310
|
+
}
|
|
16311
|
+
const closed = await orchestrator.storage.closeContinuityIncident(id, {
|
|
16312
|
+
fixApplied,
|
|
16313
|
+
verificationResult,
|
|
16314
|
+
preventiveRule
|
|
16315
|
+
});
|
|
16316
|
+
if (!closed) {
|
|
16317
|
+
console.log(`Incident not found: ${id}`);
|
|
16318
|
+
return;
|
|
16319
|
+
}
|
|
16320
|
+
console.log("Closed continuity incident:\n");
|
|
16321
|
+
console.log(formatContinuityIncidentCli(closed));
|
|
16322
|
+
});
|
|
15871
16323
|
cmd.command("access").description("Show memory access statistics").option("-n, --top <number>", "Show top N most accessed", "20").action(async (...args) => {
|
|
15872
16324
|
const options = args[0] ?? {};
|
|
15873
16325
|
const top = parseInt(options.top ?? "20", 10);
|