@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 CHANGED
@@ -103,4 +103,4 @@ Choose the right type — decay rates differ significantly:
103
103
 
104
104
  ## License
105
105
 
106
- MIT — [Digital Forge Studios](https://dforge.ca)
106
+ MIT — [Digital Forge Studios Inc.](https://dforge.ca)
@@ -3,14 +3,17 @@
3
3
  "version": 1,
4
4
  "hooks": {
5
5
  "before_prompt_build": {
6
- "action": "inject_awareness",
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: before_agent_start hook triggered for agent ${agentLabel}`);
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.before_agent_start.enabled
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 before_agent_start with prependContext) ──────────
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. Enhanced before_agent_start with prependContext (SDK path)
1433
- // When autoRecall=true and cloud backend available, use prependContext SDK pattern.
1434
- // Falls back to legacy hook-based path when SDK is not available.
1435
- if (autoRecall && isCloudBackend && sulcusMem) {
1436
- const sdkRecallHandler = buildSdkRecallHandler(
1437
- sulcusMem as SulcusCloudClient,
1438
- namespace,
1439
- maxRecallResults,
1440
- profileFrequency,
1441
- logger
1442
- );
1443
- const apiOn = api.on as (event: string, handler: unknown) => void;
1444
- apiOn("before_agent_start", async (event: Record<string, unknown>, ctx: unknown) => {
1445
- try {
1446
- return await sdkRecallHandler(event, ctx);
1447
- } catch (err) {
1448
- logger.warn("sulcus: SDK recall hook threw: " + err);
1449
- return undefined;
1450
- }
1451
- });
1452
- logger.info("sulcus: registered SDK auto-recall (prependContext path)");
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
- // 6. auto-capture on agent_end
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
 
@@ -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.2.0",
4
- "description": "Sulcus — thermodynamic memory + Apache AGE knowledge graph for OpenClaw agents. v4: 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.",
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",