@digitalforgestudios/openclaw-sulcus 6.6.3 → 6.6.4
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/index.js +173 -2
- package/index.ts +202 -3
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -6624,12 +6624,183 @@ var sulcusPlugin = {
|
|
|
6624
6624
|
}
|
|
6625
6625
|
if (typeof api.registerMemoryFlushPlan === "function") {
|
|
6626
6626
|
try {
|
|
6627
|
-
api.registerMemoryFlushPlan(() =>
|
|
6628
|
-
|
|
6627
|
+
api.registerMemoryFlushPlan(() => {
|
|
6628
|
+
if (!isAvailable || !sulcusMem) return null;
|
|
6629
|
+
return {
|
|
6630
|
+
softThresholdTokens: 15e3,
|
|
6631
|
+
forceFlushTranscriptBytes: "2mb",
|
|
6632
|
+
reserveTokensFloor: 3e4,
|
|
6633
|
+
prompt: [
|
|
6634
|
+
"Your session is approaching context limits. Before compaction, extract and save the most important information from this conversation using memory_store.",
|
|
6635
|
+
"",
|
|
6636
|
+
"Focus on:",
|
|
6637
|
+
"- Decisions made and their reasoning",
|
|
6638
|
+
"- Facts learned or confirmed",
|
|
6639
|
+
"- User preferences stated or implied",
|
|
6640
|
+
"- Procedures or workflows discussed",
|
|
6641
|
+
"- Errors encountered and their resolutions",
|
|
6642
|
+
"",
|
|
6643
|
+
"Use the appropriate memory_type for each:",
|
|
6644
|
+
"- preference: user preferences, opinions, style choices",
|
|
6645
|
+
"- fact: data points, configurations, names, values",
|
|
6646
|
+
"- semantic: knowledge, explanations, conclusions",
|
|
6647
|
+
"- procedural: how-tos, workflows, step-by-step processes",
|
|
6648
|
+
"- episodic: events, conversations, time-specific context",
|
|
6649
|
+
"",
|
|
6650
|
+
"Store 3-8 memories. Be selective \u2014 quality over quantity. Skip trivial exchanges."
|
|
6651
|
+
].join("\n"),
|
|
6652
|
+
systemPrompt: "You are a memory extraction agent. Your job is to identify and store the most valuable information from the current conversation before it gets compacted. Use memory_store with precise memory_type classification. Do not store system noise, tool outputs, or trivial exchanges."
|
|
6653
|
+
};
|
|
6654
|
+
});
|
|
6655
|
+
logger.info("sulcus: registered memory flush plan (Sulcus-aware)");
|
|
6629
6656
|
} catch (e) {
|
|
6630
6657
|
logger.warn(`sulcus: registerMemoryFlushPlan failed: ${e instanceof Error ? e.message : e}`);
|
|
6631
6658
|
}
|
|
6632
6659
|
}
|
|
6660
|
+
if (isCloudBackend && sulcusMem && typeof api.registerCompactionProvider === "function") {
|
|
6661
|
+
try {
|
|
6662
|
+
const compactionSulcusMem = sulcusMem;
|
|
6663
|
+
api.registerCompactionProvider({
|
|
6664
|
+
id: "sulcus",
|
|
6665
|
+
async summarize(params) {
|
|
6666
|
+
const msgs = params.messages ?? [];
|
|
6667
|
+
logger.info(`sulcus: compaction provider summarize() called with ${msgs.length} messages`);
|
|
6668
|
+
try {
|
|
6669
|
+
const decisions = [];
|
|
6670
|
+
const filesModified = [];
|
|
6671
|
+
const toolsUsed = [];
|
|
6672
|
+
const errorsHit = [];
|
|
6673
|
+
const userIntents = [];
|
|
6674
|
+
const assistantWork = [];
|
|
6675
|
+
const DECISION_MARKERS = ["decided", "will use", "going to", "plan is", "the fix", "conclusion", "recommend", "approach"];
|
|
6676
|
+
for (const msg of msgs) {
|
|
6677
|
+
const role = msg.role ?? msg.type;
|
|
6678
|
+
const text = typeof msg.content === "string" ? msg.content : typeof msg.text === "string" ? msg.text : Array.isArray(msg.content) ? msg.content.filter((c) => c.type === "text").map((c) => c.text).join("\n") : "";
|
|
6679
|
+
if (!text) continue;
|
|
6680
|
+
if ((role === "user" || role === "human") && text.length > 10) {
|
|
6681
|
+
userIntents.push(text.substring(0, 200));
|
|
6682
|
+
}
|
|
6683
|
+
if ((role === "assistant" || role === "ai") && text.length > 50) {
|
|
6684
|
+
const lc = text.toLowerCase();
|
|
6685
|
+
if (DECISION_MARKERS.some((m) => lc.includes(m))) {
|
|
6686
|
+
const sentences = text.split(/[.!?\n]/).filter((s) => s.trim().length > 15);
|
|
6687
|
+
for (const s of sentences) {
|
|
6688
|
+
if (DECISION_MARKERS.some((m) => s.toLowerCase().includes(m))) {
|
|
6689
|
+
decisions.push(s.trim().substring(0, 300));
|
|
6690
|
+
if (decisions.length >= 8) break;
|
|
6691
|
+
}
|
|
6692
|
+
}
|
|
6693
|
+
}
|
|
6694
|
+
if (text.length > 100) {
|
|
6695
|
+
assistantWork.push(text.substring(0, 500));
|
|
6696
|
+
}
|
|
6697
|
+
}
|
|
6698
|
+
const toolCalls = Array.isArray(msg.tool_calls) ? msg.tool_calls : [];
|
|
6699
|
+
for (const tc of toolCalls) {
|
|
6700
|
+
const name = tc.name ?? tc.function ?? "";
|
|
6701
|
+
if (name && !toolsUsed.includes(name)) toolsUsed.push(name);
|
|
6702
|
+
if (/^(write|edit)$/i.test(name)) {
|
|
6703
|
+
const input = tc.input ?? tc.arguments ?? {};
|
|
6704
|
+
const fp = input?.file_path ?? input?.path;
|
|
6705
|
+
if (fp && !filesModified.includes(fp)) filesModified.push(fp);
|
|
6706
|
+
}
|
|
6707
|
+
}
|
|
6708
|
+
if (role === "tool" && (msg.is_error === true || typeof text === "string" && /error|failed|exception/i.test(text.substring(0, 100)))) {
|
|
6709
|
+
errorsHit.push(text.substring(0, 200));
|
|
6710
|
+
}
|
|
6711
|
+
}
|
|
6712
|
+
let stored = 0;
|
|
6713
|
+
const itemsToStore = [];
|
|
6714
|
+
for (const d of decisions.slice(0, 5)) {
|
|
6715
|
+
itemsToStore.push({ text: d, suggestedType: "semantic" });
|
|
6716
|
+
}
|
|
6717
|
+
for (const u of userIntents.slice(0, 5)) {
|
|
6718
|
+
if (u.length > 50 && /\b(prefer|always|never|don't|stop|use|switch|remember)\b/i.test(u)) {
|
|
6719
|
+
itemsToStore.push({ text: u, suggestedType: "preference" });
|
|
6720
|
+
}
|
|
6721
|
+
}
|
|
6722
|
+
for (const a of assistantWork.slice(0, 3)) {
|
|
6723
|
+
if (/\b(step \d|procedure|workflow|how to|instructions)\b/i.test(a)) {
|
|
6724
|
+
itemsToStore.push({ text: a, suggestedType: "procedural" });
|
|
6725
|
+
} else {
|
|
6726
|
+
itemsToStore.push({ text: a, suggestedType: "semantic" });
|
|
6727
|
+
}
|
|
6728
|
+
}
|
|
6729
|
+
for (const item of itemsToStore) {
|
|
6730
|
+
if (isJunkMemory(item.text)) continue;
|
|
6731
|
+
if (!shouldCapture(item.text)) continue;
|
|
6732
|
+
try {
|
|
6733
|
+
let memType = item.suggestedType;
|
|
6734
|
+
try {
|
|
6735
|
+
const siuResult = await compactionSulcusMem.request(
|
|
6736
|
+
"POST",
|
|
6737
|
+
"/api/v2/siu/label",
|
|
6738
|
+
{ text: item.text }
|
|
6739
|
+
);
|
|
6740
|
+
if (siuResult?.store === false && (siuResult?.store_confidence ?? 0) < 0.3) continue;
|
|
6741
|
+
if (siuResult?.memory_type) memType = siuResult.memory_type;
|
|
6742
|
+
} catch {
|
|
6743
|
+
}
|
|
6744
|
+
const hints = buildExtractionHints(memType, namespace, "compaction_provider", item.text.substring(0, 200));
|
|
6745
|
+
await compactionSulcusMem.add_memory(item.text, memType, hints);
|
|
6746
|
+
stored++;
|
|
6747
|
+
} catch {
|
|
6748
|
+
}
|
|
6749
|
+
}
|
|
6750
|
+
logger.info(`sulcus: compaction provider \u2014 stored ${stored} memories`);
|
|
6751
|
+
const topicQuery = userIntents.slice(0, 3).join(" ").substring(0, 500) || "session summary";
|
|
6752
|
+
let relevantMemories = [];
|
|
6753
|
+
try {
|
|
6754
|
+
const searchRes = await compactionSulcusMem.search_memory(topicQuery, 15, namespace);
|
|
6755
|
+
relevantMemories = searchRes?.results ?? [];
|
|
6756
|
+
} catch (e) {
|
|
6757
|
+
logger.warn(`sulcus: compaction provider \u2014 memory search failed: ${e}`);
|
|
6758
|
+
}
|
|
6759
|
+
const sections = [];
|
|
6760
|
+
if (params.previousSummary?.trim()) {
|
|
6761
|
+
sections.push(`## Prior Context
|
|
6762
|
+
${params.previousSummary.trim()}`);
|
|
6763
|
+
}
|
|
6764
|
+
const memLines = [];
|
|
6765
|
+
const seenIds = /* @__PURE__ */ new Set();
|
|
6766
|
+
for (const mem of relevantMemories) {
|
|
6767
|
+
const id = mem.id;
|
|
6768
|
+
if (seenIds.has(id)) continue;
|
|
6769
|
+
seenIds.add(id);
|
|
6770
|
+
const mtype = mem.memory_type ?? "unknown";
|
|
6771
|
+
const label = (mem.label ?? mem.pointer_summary ?? "").trim();
|
|
6772
|
+
if (!label || label.length < 20) continue;
|
|
6773
|
+
memLines.push(`- [${mtype}] ${label.length > 400 ? label.substring(0, 400) + "..." : label}`);
|
|
6774
|
+
}
|
|
6775
|
+
if (memLines.length > 0) {
|
|
6776
|
+
sections.push(`## Key Context (from Sulcus memory)
|
|
6777
|
+
${memLines.join("\n")}`);
|
|
6778
|
+
}
|
|
6779
|
+
if (decisions.length > 0) {
|
|
6780
|
+
sections.push(`## Decisions Made
|
|
6781
|
+
${decisions.map((d) => "- " + d).join("\n")}`);
|
|
6782
|
+
}
|
|
6783
|
+
const activity = [`${msgs.length} messages in this session segment`];
|
|
6784
|
+
if (filesModified.length > 0) activity.push(`Files modified: ${filesModified.join(", ")}`);
|
|
6785
|
+
if (toolsUsed.length > 0) activity.push(`Tools used: ${toolsUsed.join(", ")}`);
|
|
6786
|
+
if (errorsHit.length > 0) activity.push(`Errors: ${errorsHit.length}`);
|
|
6787
|
+
sections.push(`## Session Activity
|
|
6788
|
+
${activity.join("\n")}`);
|
|
6789
|
+
const summary = sections.join("\n\n");
|
|
6790
|
+
if (!summary.trim()) throw new Error("Sulcus compaction produced empty summary");
|
|
6791
|
+
logger.info(`sulcus: compaction provider produced summary (${summary.length} chars)`);
|
|
6792
|
+
return summary;
|
|
6793
|
+
} catch (err) {
|
|
6794
|
+
logger.warn(`sulcus: compaction provider failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
6795
|
+
throw err;
|
|
6796
|
+
}
|
|
6797
|
+
}
|
|
6798
|
+
});
|
|
6799
|
+
logger.info('sulcus: registered compaction provider "sulcus"');
|
|
6800
|
+
} catch (e) {
|
|
6801
|
+
logger.warn(`sulcus: registerCompactionProvider failed: ${e instanceof Error ? e.message : e}`);
|
|
6802
|
+
}
|
|
6803
|
+
}
|
|
6633
6804
|
if (typeof api.registerService === "function") {
|
|
6634
6805
|
try {
|
|
6635
6806
|
api.registerService({
|
package/index.ts
CHANGED
|
@@ -5295,16 +5295,215 @@ const sulcusPlugin = {
|
|
|
5295
5295
|
}
|
|
5296
5296
|
}
|
|
5297
5297
|
|
|
5298
|
-
// 3. registerMemoryFlushPlan —
|
|
5298
|
+
// 3. registerMemoryFlushPlan — Sulcus-aware pre-compaction flush
|
|
5299
5299
|
if (typeof (api.registerMemoryFlushPlan as unknown) === "function") {
|
|
5300
5300
|
try {
|
|
5301
|
-
(api.registerMemoryFlushPlan as (r: unknown) => void)(() =>
|
|
5302
|
-
|
|
5301
|
+
(api.registerMemoryFlushPlan as (r: unknown) => void)(() => {
|
|
5302
|
+
if (!isAvailable || !sulcusMem) return null;
|
|
5303
|
+
return {
|
|
5304
|
+
softThresholdTokens: 15000,
|
|
5305
|
+
forceFlushTranscriptBytes: "2mb",
|
|
5306
|
+
reserveTokensFloor: 30000,
|
|
5307
|
+
prompt: [
|
|
5308
|
+
"Your session is approaching context limits. Before compaction, extract and save the most important information from this conversation using memory_store.",
|
|
5309
|
+
"",
|
|
5310
|
+
"Focus on:",
|
|
5311
|
+
"- Decisions made and their reasoning",
|
|
5312
|
+
"- Facts learned or confirmed",
|
|
5313
|
+
"- User preferences stated or implied",
|
|
5314
|
+
"- Procedures or workflows discussed",
|
|
5315
|
+
"- Errors encountered and their resolutions",
|
|
5316
|
+
"",
|
|
5317
|
+
"Use the appropriate memory_type for each:",
|
|
5318
|
+
"- preference: user preferences, opinions, style choices",
|
|
5319
|
+
"- fact: data points, configurations, names, values",
|
|
5320
|
+
"- semantic: knowledge, explanations, conclusions",
|
|
5321
|
+
"- procedural: how-tos, workflows, step-by-step processes",
|
|
5322
|
+
"- episodic: events, conversations, time-specific context",
|
|
5323
|
+
"",
|
|
5324
|
+
"Store 3-8 memories. Be selective — quality over quantity. Skip trivial exchanges.",
|
|
5325
|
+
].join("\n"),
|
|
5326
|
+
systemPrompt: "You are a memory extraction agent. Your job is to identify and store the most valuable information from the current conversation before it gets compacted. Use memory_store with precise memory_type classification. Do not store system noise, tool outputs, or trivial exchanges.",
|
|
5327
|
+
};
|
|
5328
|
+
});
|
|
5329
|
+
logger.info("sulcus: registered memory flush plan (Sulcus-aware)");
|
|
5303
5330
|
} catch (e: unknown) {
|
|
5304
5331
|
logger.warn(`sulcus: registerMemoryFlushPlan failed: ${e instanceof Error ? e.message : e}`);
|
|
5305
5332
|
}
|
|
5306
5333
|
}
|
|
5307
5334
|
|
|
5335
|
+
// 3b. registerCompactionProvider — Sulcus-native compaction summarization
|
|
5336
|
+
if (isCloudBackend && sulcusMem && typeof (api.registerCompactionProvider as unknown) === "function") {
|
|
5337
|
+
try {
|
|
5338
|
+
const compactionSulcusMem = sulcusMem as SulcusCloudClient;
|
|
5339
|
+
(api.registerCompactionProvider as (p: unknown) => void)({
|
|
5340
|
+
id: "sulcus",
|
|
5341
|
+
async summarize(params: {
|
|
5342
|
+
messages: Record<string, unknown>[];
|
|
5343
|
+
signal?: AbortSignal;
|
|
5344
|
+
customInstructions?: string;
|
|
5345
|
+
summarizationInstructions?: string;
|
|
5346
|
+
previousSummary?: string;
|
|
5347
|
+
}): Promise<string> {
|
|
5348
|
+
const msgs = params.messages ?? [];
|
|
5349
|
+
logger.info(`sulcus: compaction provider summarize() called with ${msgs.length} messages`);
|
|
5350
|
+
try {
|
|
5351
|
+
// 1. Extract substantive content from messages being compacted
|
|
5352
|
+
const decisions: string[] = [];
|
|
5353
|
+
const filesModified: string[] = [];
|
|
5354
|
+
const toolsUsed: string[] = [];
|
|
5355
|
+
const errorsHit: string[] = [];
|
|
5356
|
+
const userIntents: string[] = [];
|
|
5357
|
+
const assistantWork: string[] = [];
|
|
5358
|
+
|
|
5359
|
+
const DECISION_MARKERS = ["decided", "will use", "going to", "plan is", "the fix", "conclusion", "recommend", "approach"];
|
|
5360
|
+
|
|
5361
|
+
for (const msg of msgs) {
|
|
5362
|
+
const role = (msg.role ?? msg.type) as string | undefined;
|
|
5363
|
+
const text = typeof msg.content === "string" ? msg.content
|
|
5364
|
+
: typeof msg.text === "string" ? msg.text as string
|
|
5365
|
+
: Array.isArray(msg.content)
|
|
5366
|
+
? (msg.content as Record<string, unknown>[]).filter((c) => c.type === "text").map((c) => c.text as string).join("\n")
|
|
5367
|
+
: "";
|
|
5368
|
+
if (!text) continue;
|
|
5369
|
+
|
|
5370
|
+
if ((role === "user" || role === "human") && text.length > 10) {
|
|
5371
|
+
userIntents.push(text.substring(0, 200));
|
|
5372
|
+
}
|
|
5373
|
+
if ((role === "assistant" || role === "ai") && text.length > 50) {
|
|
5374
|
+
const lc = text.toLowerCase();
|
|
5375
|
+
if (DECISION_MARKERS.some((m) => lc.includes(m))) {
|
|
5376
|
+
const sentences = text.split(/[.!?\n]/).filter((s) => s.trim().length > 15);
|
|
5377
|
+
for (const s of sentences) {
|
|
5378
|
+
if (DECISION_MARKERS.some((m) => s.toLowerCase().includes(m))) {
|
|
5379
|
+
decisions.push(s.trim().substring(0, 300));
|
|
5380
|
+
if (decisions.length >= 8) break;
|
|
5381
|
+
}
|
|
5382
|
+
}
|
|
5383
|
+
}
|
|
5384
|
+
if (text.length > 100) {
|
|
5385
|
+
assistantWork.push(text.substring(0, 500));
|
|
5386
|
+
}
|
|
5387
|
+
}
|
|
5388
|
+
|
|
5389
|
+
// Extract tool usage
|
|
5390
|
+
const toolCalls = Array.isArray(msg.tool_calls) ? msg.tool_calls as Record<string, unknown>[] : [];
|
|
5391
|
+
for (const tc of toolCalls) {
|
|
5392
|
+
const name = ((tc.name ?? tc.function) as string) ?? "";
|
|
5393
|
+
if (name && !toolsUsed.includes(name)) toolsUsed.push(name);
|
|
5394
|
+
if (/^(write|edit)$/i.test(name)) {
|
|
5395
|
+
const input = (tc.input ?? tc.arguments ?? {}) as Record<string, unknown>;
|
|
5396
|
+
const fp = (input?.file_path ?? input?.path) as string | undefined;
|
|
5397
|
+
if (fp && !filesModified.includes(fp)) filesModified.push(fp);
|
|
5398
|
+
}
|
|
5399
|
+
}
|
|
5400
|
+
|
|
5401
|
+
// Track errors
|
|
5402
|
+
if (role === "tool" && (msg.is_error === true || (typeof text === "string" && /error|failed|exception/i.test(text.substring(0, 100))))) {
|
|
5403
|
+
errorsHit.push(text.substring(0, 200));
|
|
5404
|
+
}
|
|
5405
|
+
}
|
|
5406
|
+
|
|
5407
|
+
// 2. Store key items as Sulcus memories via SIU classification
|
|
5408
|
+
let stored = 0;
|
|
5409
|
+
const itemsToStore: { text: string; suggestedType: string }[] = [];
|
|
5410
|
+
|
|
5411
|
+
for (const d of decisions.slice(0, 5)) {
|
|
5412
|
+
itemsToStore.push({ text: d, suggestedType: "semantic" });
|
|
5413
|
+
}
|
|
5414
|
+
for (const u of userIntents.slice(0, 5)) {
|
|
5415
|
+
if (u.length > 50 && /\b(prefer|always|never|don't|stop|use|switch|remember)\b/i.test(u)) {
|
|
5416
|
+
itemsToStore.push({ text: u, suggestedType: "preference" });
|
|
5417
|
+
}
|
|
5418
|
+
}
|
|
5419
|
+
for (const a of assistantWork.slice(0, 3)) {
|
|
5420
|
+
if (/\b(step \d|procedure|workflow|how to|instructions)\b/i.test(a)) {
|
|
5421
|
+
itemsToStore.push({ text: a, suggestedType: "procedural" });
|
|
5422
|
+
} else {
|
|
5423
|
+
itemsToStore.push({ text: a, suggestedType: "semantic" });
|
|
5424
|
+
}
|
|
5425
|
+
}
|
|
5426
|
+
|
|
5427
|
+
for (const item of itemsToStore) {
|
|
5428
|
+
if (isJunkMemory(item.text)) continue;
|
|
5429
|
+
if (!shouldCapture(item.text)) continue;
|
|
5430
|
+
try {
|
|
5431
|
+
let memType = item.suggestedType;
|
|
5432
|
+
try {
|
|
5433
|
+
const siuResult = await compactionSulcusMem.request(
|
|
5434
|
+
"POST", "/api/v2/siu/label", { text: item.text }
|
|
5435
|
+
) as Record<string, unknown>;
|
|
5436
|
+
if (siuResult?.store === false && ((siuResult?.store_confidence as number) ?? 0) < 0.3) continue;
|
|
5437
|
+
if (siuResult?.memory_type) memType = siuResult.memory_type as string;
|
|
5438
|
+
} catch { /* SIU unavailable — use heuristic */ }
|
|
5439
|
+
const hints = buildExtractionHints(memType, namespace, "compaction_provider", item.text.substring(0, 200));
|
|
5440
|
+
await compactionSulcusMem.add_memory(item.text, memType, hints);
|
|
5441
|
+
stored++;
|
|
5442
|
+
} catch { /* best-effort */ }
|
|
5443
|
+
}
|
|
5444
|
+
logger.info(`sulcus: compaction provider — stored ${stored} memories`);
|
|
5445
|
+
|
|
5446
|
+
// 3. Query Sulcus for relevant context (including freshly stored)
|
|
5447
|
+
const topicQuery = userIntents.slice(0, 3).join(" ").substring(0, 500) || "session summary";
|
|
5448
|
+
let relevantMemories: Record<string, unknown>[] = [];
|
|
5449
|
+
try {
|
|
5450
|
+
const searchRes = await compactionSulcusMem.search_memory(topicQuery, 15, namespace);
|
|
5451
|
+
relevantMemories = searchRes?.results ?? [];
|
|
5452
|
+
} catch (e) {
|
|
5453
|
+
logger.warn(`sulcus: compaction provider — memory search failed: ${e}`);
|
|
5454
|
+
}
|
|
5455
|
+
|
|
5456
|
+
// 4. Build structured summary
|
|
5457
|
+
const sections: string[] = [];
|
|
5458
|
+
|
|
5459
|
+
if (params.previousSummary?.trim()) {
|
|
5460
|
+
sections.push(`## Prior Context\n${params.previousSummary.trim()}`);
|
|
5461
|
+
}
|
|
5462
|
+
|
|
5463
|
+
// Memory-backed context
|
|
5464
|
+
const memLines: string[] = [];
|
|
5465
|
+
const seenIds = new Set<string>();
|
|
5466
|
+
for (const mem of relevantMemories) {
|
|
5467
|
+
const id = mem.id as string;
|
|
5468
|
+
if (seenIds.has(id)) continue;
|
|
5469
|
+
seenIds.add(id);
|
|
5470
|
+
const mtype = (mem.memory_type as string) ?? "unknown";
|
|
5471
|
+
const label = ((mem.label ?? mem.pointer_summary ?? "") as string).trim();
|
|
5472
|
+
if (!label || label.length < 20) continue;
|
|
5473
|
+
memLines.push(`- [${mtype}] ${label.length > 400 ? label.substring(0, 400) + "..." : label}`);
|
|
5474
|
+
}
|
|
5475
|
+
if (memLines.length > 0) {
|
|
5476
|
+
sections.push(`## Key Context (from Sulcus memory)\n${memLines.join("\n")}`);
|
|
5477
|
+
}
|
|
5478
|
+
|
|
5479
|
+
// Decisions
|
|
5480
|
+
if (decisions.length > 0) {
|
|
5481
|
+
sections.push(`## Decisions Made\n${decisions.map((d) => "- " + d).join("\n")}`);
|
|
5482
|
+
}
|
|
5483
|
+
|
|
5484
|
+
// Session activity
|
|
5485
|
+
const activity: string[] = [`${msgs.length} messages in this session segment`];
|
|
5486
|
+
if (filesModified.length > 0) activity.push(`Files modified: ${filesModified.join(", ")}`);
|
|
5487
|
+
if (toolsUsed.length > 0) activity.push(`Tools used: ${toolsUsed.join(", ")}`);
|
|
5488
|
+
if (errorsHit.length > 0) activity.push(`Errors: ${errorsHit.length}`);
|
|
5489
|
+
sections.push(`## Session Activity\n${activity.join("\n")}`);
|
|
5490
|
+
|
|
5491
|
+
const summary = sections.join("\n\n");
|
|
5492
|
+
if (!summary.trim()) throw new Error("Sulcus compaction produced empty summary");
|
|
5493
|
+
logger.info(`sulcus: compaction provider produced summary (${summary.length} chars)`);
|
|
5494
|
+
return summary;
|
|
5495
|
+
} catch (err) {
|
|
5496
|
+
logger.warn(`sulcus: compaction provider failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
5497
|
+
throw err; // Fall back to built-in LLM summarization
|
|
5498
|
+
}
|
|
5499
|
+
},
|
|
5500
|
+
});
|
|
5501
|
+
logger.info("sulcus: registered compaction provider \"sulcus\"");
|
|
5502
|
+
} catch (e: unknown) {
|
|
5503
|
+
logger.warn(`sulcus: registerCompactionProvider failed: ${e instanceof Error ? e.message : e}`);
|
|
5504
|
+
}
|
|
5505
|
+
}
|
|
5506
|
+
|
|
5308
5507
|
// 4. registerService — lifecycle management
|
|
5309
5508
|
if (typeof (api.registerService as unknown) === "function") {
|
|
5310
5509
|
try {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@digitalforgestudios/openclaw-sulcus",
|
|
3
|
-
"version": "6.6.
|
|
3
|
+
"version": "6.6.4",
|
|
4
4
|
"description": "Sulcus \u2014 thermodynamic memory + Apache AGE knowledge graph for OpenClaw agents. v6.0: Multi-signal recall (semantic + hot-context + entity-graph + profile), configurable guardrails (outputGuard + toolGuard), token budget enforcement, context rebuild post-compaction, sulcus.toml config layer, SIRU training data logging, session-scoped memory, batch heat-boost. SIU v2 pipeline (SIVU/SICU/SILU/SITU/SIRU). Interaction-based decay. Curator sleep-cycle. Cross-agent sync.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"openclaw",
|