@digitalforgestudios/openclaw-sulcus 4.2.0 → 5.3.0
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/README.md +1 -1
- package/hooks.defaults.json +6 -3
- package/index.ts +104 -26
- package/openclaw.plugin.json +4 -1
- package/package.json +2 -2
package/README.md
CHANGED
package/hooks.defaults.json
CHANGED
|
@@ -3,14 +3,17 @@
|
|
|
3
3
|
"version": 1,
|
|
4
4
|
"hooks": {
|
|
5
5
|
"before_prompt_build": {
|
|
6
|
-
"action": "
|
|
7
|
-
"enabled": false
|
|
6
|
+
"action": "auto_recall",
|
|
7
|
+
"enabled": false,
|
|
8
|
+
"limit": 5,
|
|
9
|
+
"minScore": 0.3
|
|
8
10
|
},
|
|
9
11
|
"before_agent_start": {
|
|
10
12
|
"action": "auto_recall",
|
|
11
13
|
"enabled": false,
|
|
12
14
|
"limit": 5,
|
|
13
|
-
"minScore": 0.3
|
|
15
|
+
"minScore": 0.3,
|
|
16
|
+
"_deprecated": "Use before_prompt_build instead. Legacy compat only."
|
|
14
17
|
},
|
|
15
18
|
"agent_end": {
|
|
16
19
|
"action": "none",
|
package/index.ts
CHANGED
|
@@ -91,7 +91,7 @@ const hookHandlers: Record<string, HookHandler> = {
|
|
|
91
91
|
const { sulcusMem, namespace, logger } = ctx;
|
|
92
92
|
if (!sulcusMem) return;
|
|
93
93
|
const agentLabel = (event?.agentId as string) ?? "(unknown)";
|
|
94
|
-
logger.info(`sulcus:
|
|
94
|
+
logger.info(`sulcus: auto_recall hook triggered for agent ${agentLabel}`);
|
|
95
95
|
const prompt = typeof event?.prompt === "string" ? event.prompt : "";
|
|
96
96
|
if (!prompt) return;
|
|
97
97
|
try {
|
|
@@ -410,6 +410,27 @@ class SulcusCloudClient {
|
|
|
410
410
|
return this.request("POST", "/api/v1/triggers/evaluate", body);
|
|
411
411
|
}
|
|
412
412
|
|
|
413
|
+
async embed_text(text: string, namespace?: string): Promise<{ embedding: number[]; model: string; dimensions: number } | null> {
|
|
414
|
+
// NOTE: Requires Sulcus server >= v2.4 with /api/v1/agent/embed endpoint.
|
|
415
|
+
// Falls back to null if the endpoint is not available — caller handles gracefully.
|
|
416
|
+
try {
|
|
417
|
+
const body: Record<string, unknown> = { text };
|
|
418
|
+
if (namespace) body.namespace = namespace;
|
|
419
|
+
const res = await this.request("POST", "/api/v1/agent/embed", body) as Record<string, unknown> | null;
|
|
420
|
+
if (!res || !Array.isArray(res.embedding)) return null;
|
|
421
|
+
return {
|
|
422
|
+
embedding: res.embedding as number[],
|
|
423
|
+
model: (res.model as string) ?? "bge-small-en-v1.5",
|
|
424
|
+
dimensions: (res.dimensions as number) ?? (res.embedding as number[]).length,
|
|
425
|
+
};
|
|
426
|
+
} catch (e: unknown) {
|
|
427
|
+
// 404 = endpoint not deployed yet; warn once but don't break anything
|
|
428
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
429
|
+
if (msg.includes("404")) return null; // endpoint not available on this server version
|
|
430
|
+
throw e;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
413
434
|
async probe(): Promise<boolean> {
|
|
414
435
|
try {
|
|
415
436
|
await this.search_memory("probe", 1);
|
|
@@ -631,8 +652,13 @@ function loadHooksConfig(apiConfig: Record<string, unknown>): HooksConfig {
|
|
|
631
652
|
mergedTools[name] = { ...(mergedTools[name] ?? { enabled: false }), ...override };
|
|
632
653
|
}
|
|
633
654
|
|
|
634
|
-
// Legacy compat: autoRecall flag → hooks.
|
|
655
|
+
// Legacy compat: autoRecall flag → hooks.before_prompt_build.enabled (v5.0.0+)
|
|
656
|
+
// Also keeps before_agent_start enabled for backward compat with older configs.
|
|
635
657
|
if (apiConfig?.autoRecall === true) {
|
|
658
|
+
mergedHooks["before_prompt_build"] = {
|
|
659
|
+
...(mergedHooks["before_prompt_build"] ?? { action: "auto_recall", enabled: false }),
|
|
660
|
+
enabled: true,
|
|
661
|
+
};
|
|
636
662
|
mergedHooks["before_agent_start"] = {
|
|
637
663
|
...(mergedHooks["before_agent_start"] ?? { action: "auto_recall", enabled: false }),
|
|
638
664
|
enabled: true,
|
|
@@ -664,7 +690,7 @@ function formatRelativeTime(isoTimestamp: string): string {
|
|
|
664
690
|
}
|
|
665
691
|
}
|
|
666
692
|
|
|
667
|
-
// ─── SDK RECALL HANDLER (for
|
|
693
|
+
// ─── SDK RECALL HANDLER (for before_prompt_build with prependContext) ──────────
|
|
668
694
|
|
|
669
695
|
interface ProfileCache {
|
|
670
696
|
preferences: Record<string, unknown>[];
|
|
@@ -1429,30 +1455,80 @@ const sulcusPlugin = {
|
|
|
1429
1455
|
}
|
|
1430
1456
|
}
|
|
1431
1457
|
|
|
1432
|
-
// 5.
|
|
1433
|
-
// When autoRecall=true and cloud backend available
|
|
1434
|
-
//
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1458
|
+
// 5. before_prompt_build — recall + awareness (SDK path, v5.0.0+)
|
|
1459
|
+
// When autoRecall=true and cloud backend available: recall + inject awareness via prependContext.
|
|
1460
|
+
// When autoRecall=false but cloud backend available: inject awareness only (static context block).
|
|
1461
|
+
// Replaces legacy before_agent_start for new work; legacy hook loop handles fallback.
|
|
1462
|
+
if (isCloudBackend && sulcusMem) {
|
|
1463
|
+
if (autoRecall) {
|
|
1464
|
+
const sdkRecallHandler = buildSdkRecallHandler(
|
|
1465
|
+
sulcusMem as SulcusCloudClient,
|
|
1466
|
+
namespace,
|
|
1467
|
+
maxRecallResults,
|
|
1468
|
+
profileFrequency,
|
|
1469
|
+
logger
|
|
1470
|
+
);
|
|
1471
|
+
const apiOn = api.on as (event: string, handler: unknown) => void;
|
|
1472
|
+
apiOn("before_prompt_build", async (event: Record<string, unknown>, ctx: unknown) => {
|
|
1473
|
+
try {
|
|
1474
|
+
// Recall returns prependContext with memories + awareness embedded
|
|
1475
|
+
const result = await sdkRecallHandler(event, ctx);
|
|
1476
|
+
// If recall returned nothing (no prompt), fall back to awareness-only
|
|
1477
|
+
if (!result) return { prependSystemContext: STATIC_AWARENESS };
|
|
1478
|
+
// Translate prependContext → prependSystemContext for hook shape compat
|
|
1479
|
+
const r = result as { prependContext?: string; prependSystemContext?: string };
|
|
1480
|
+
if (r.prependContext) return { prependSystemContext: r.prependContext };
|
|
1481
|
+
return result;
|
|
1482
|
+
} catch (err) {
|
|
1483
|
+
logger.warn("sulcus: before_prompt_build recall hook threw: " + err);
|
|
1484
|
+
return { prependSystemContext: STATIC_AWARENESS };
|
|
1485
|
+
}
|
|
1486
|
+
});
|
|
1487
|
+
logger.info("sulcus: registered before_prompt_build (recall + awareness)");
|
|
1488
|
+
} else {
|
|
1489
|
+
// Awareness-only path — inject static context without recall
|
|
1490
|
+
const apiOn = api.on as (event: string, handler: unknown) => void;
|
|
1491
|
+
apiOn("before_prompt_build", async (_event: Record<string, unknown>, _ctx: unknown) => {
|
|
1492
|
+
return { prependSystemContext: STATIC_AWARENESS };
|
|
1493
|
+
});
|
|
1494
|
+
logger.info("sulcus: registered before_prompt_build (awareness-only)");
|
|
1495
|
+
}
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1498
|
+
// 6. registerMemoryEmbeddingProvider — Sulcus embedding adapter
|
|
1499
|
+
if (typeof (api.registerMemoryEmbeddingProvider as unknown) === "function" && isCloudBackend && sulcusMem) {
|
|
1500
|
+
try {
|
|
1501
|
+
(api.registerMemoryEmbeddingProvider as (adapter: unknown) => void)({
|
|
1502
|
+
id: "sulcus",
|
|
1503
|
+
label: "Sulcus (BGE-small-en-v1.5)",
|
|
1504
|
+
transport: "remote",
|
|
1505
|
+
autoSelectPriority: 50,
|
|
1506
|
+
embed: async (texts: string[]) => {
|
|
1507
|
+
// Route through Sulcus cloud API for embeddings
|
|
1508
|
+
let warned = false;
|
|
1509
|
+
const results = await Promise.all(
|
|
1510
|
+
texts.map(async (text) => {
|
|
1511
|
+
const res = await (sulcusMem as SulcusCloudClient).embed_text(text, namespace);
|
|
1512
|
+
if (!res) {
|
|
1513
|
+
if (!warned) {
|
|
1514
|
+
warned = true;
|
|
1515
|
+
logger.warn("sulcus: embed_text returned null — /api/v1/agent/embed not available on this server version; embedding provider will return empty vectors");
|
|
1516
|
+
}
|
|
1517
|
+
return [];
|
|
1518
|
+
}
|
|
1519
|
+
return res.embedding;
|
|
1520
|
+
})
|
|
1521
|
+
);
|
|
1522
|
+
return { embeddings: results, model: "bge-small-en-v1.5", dimensions: 384 };
|
|
1523
|
+
},
|
|
1524
|
+
});
|
|
1525
|
+
logger.info("sulcus: registered memory embedding provider (BGE-small-en-v1.5)");
|
|
1526
|
+
} catch (e: unknown) {
|
|
1527
|
+
logger.warn(`sulcus: registerMemoryEmbeddingProvider failed: ${e instanceof Error ? e.message : e}`);
|
|
1528
|
+
}
|
|
1453
1529
|
}
|
|
1454
1530
|
|
|
1455
|
-
//
|
|
1531
|
+
// 7. auto-capture on agent_end
|
|
1456
1532
|
if (autoCapture) {
|
|
1457
1533
|
const agentEndCaptureConfig: HookConfig = {
|
|
1458
1534
|
action: "sivu_auto_capture",
|
|
@@ -1479,8 +1555,10 @@ const sulcusPlugin = {
|
|
|
1479
1555
|
for (const [hookName, hookConfig] of Object.entries(hooksConfig.hooks)) {
|
|
1480
1556
|
if (!hookConfig.enabled) continue;
|
|
1481
1557
|
|
|
1482
|
-
// Skip before_agent_start if we already registered the SDK path
|
|
1558
|
+
// Skip before_agent_start if we already registered the SDK path (v5: SDK uses before_prompt_build)
|
|
1483
1559
|
if (hookName === "before_agent_start" && autoRecall && isCloudBackend) continue;
|
|
1560
|
+
// Skip before_prompt_build if we already registered the SDK handler above
|
|
1561
|
+
if (hookName === "before_prompt_build" && isCloudBackend && sulcusMem) continue;
|
|
1484
1562
|
// Skip agent_end if autoCapture SDK path already registered
|
|
1485
1563
|
if (hookName === "agent_end" && autoCapture && hookConfig.action === "sivu_auto_capture") continue;
|
|
1486
1564
|
|
package/openclaw.plugin.json
CHANGED
|
@@ -2,7 +2,10 @@
|
|
|
2
2
|
"id": "openclaw-sulcus",
|
|
3
3
|
"kind": "memory",
|
|
4
4
|
"name": "Sulcus",
|
|
5
|
-
"description": "Thermodynamic memory + Apache AGE knowledge graph for AI agents. SIU v2 pipeline (SIVU/SICU/SILU/SITU) auto-classifies and scores every memory. Interaction-based decay (Time-only, Interaction-only, Hybrid). Curator sleep-cycle reclassifies and consolidates. Relevance-weighted recall (similarity × 0.7 + heat × 0.3). Confidence levels and conflict detection (v2.3.0). 52 API routes, 32 server modules. Runs locally via WASM + native dylibs; optional cloud sync via serverUrl/apiKey.",
|
|
5
|
+
"description": "Thermodynamic memory + Apache AGE knowledge graph for AI agents. v5: before_prompt_build hook (replaces before_agent_start), registerMemoryEmbeddingProvider (BGE-small-en-v1.5 via cloud API). SIU v2 pipeline (SIVU/SICU/SILU/SITU) auto-classifies and scores every memory. Interaction-based decay (Time-only, Interaction-only, Hybrid). Curator sleep-cycle reclassifies and consolidates. Relevance-weighted recall (similarity × 0.7 + heat × 0.3). Confidence levels and conflict detection (v2.3.0). 52 API routes, 32 server modules. Runs locally via WASM + native dylibs; optional cloud sync via serverUrl/apiKey.",
|
|
6
|
+
"contracts": {
|
|
7
|
+
"memoryEmbeddingProviders": ["sulcus"]
|
|
8
|
+
},
|
|
6
9
|
"privacy": {
|
|
7
10
|
"consentModel": "local-first",
|
|
8
11
|
"consentNote": "Plugin runs entirely in-process. No network calls. All data stays local in ~/.sulcus/. Cloud sync is opt-in via serverUrl/apiKey config, handled by sulcus-sync dylib loaded by sulcus.",
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@digitalforgestudios/openclaw-sulcus",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "Sulcus — thermodynamic memory + Apache AGE knowledge graph for OpenClaw agents.
|
|
3
|
+
"version": "5.3.0",
|
|
4
|
+
"description": "Sulcus — thermodynamic memory + Apache AGE knowledge graph for OpenClaw agents. v5: before_prompt_build hook migration (replaces before_agent_start), registerMemoryEmbeddingProvider (BGE-small-en-v1.5 via cloud API), registerMemoryRuntime, prependContext recall, registerMemoryPromptSection, registerService lifecycle, uiHints, provider-filtered auto-capture. SIU v2 pipeline auto-classifies and scores memories. Interaction-based decay (3 modes). Curator sleep-cycle. Relevance-weighted recall. Cross-agent sync.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"openclaw",
|
|
7
7
|
"openclaw-plugin",
|