shieldcortex 2.10.6 → 2.10.7

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.
@@ -540,6 +540,108 @@ async function onKeywordTrigger(event) {
540
540
  await checkAndSaveKeywordTrigger(lastMessage, event);
541
541
  }
542
542
 
543
+ // ==================== SELF-CHECK & SELF-HEAL ====================
544
+
545
+ /**
546
+ * One-shot self-check that runs on first bootstrap per process.
547
+ * Detects legacy hook paths and attempts self-heal by copying files.
548
+ *
549
+ * Safety:
550
+ * - _selfCheckDone flag prevents re-runs (no loops)
551
+ * - All fs ops are sync-safe copies (no recursive watchers, no intervals)
552
+ * - Fails silently on any error — never blocks bootstrap
553
+ */
554
+ let _selfCheckDone = false;
555
+
556
+ async function selfCheckAndHeal(event) {
557
+ if (_selfCheckDone) return;
558
+ _selfCheckDone = true; // Set immediately to prevent re-entry
559
+
560
+ try {
561
+ const path = await import("node:path");
562
+ const { homedir } = await import("node:os");
563
+ const home = homedir();
564
+
565
+ // Where am I running from?
566
+ const myDir = path.dirname(new URL(import.meta.url).pathname);
567
+
568
+ // Expected locations (newest first)
569
+ const expectedDirs = [
570
+ path.join(home, ".openclaw", "hooks", "internal", "cortex-memory"),
571
+ path.join(home, ".openclaw", "hooks", "cortex-memory"),
572
+ ];
573
+
574
+ const isInExpectedLocation = expectedDirs.some(d => myDir.startsWith(d));
575
+
576
+ if (isInExpectedLocation) {
577
+ // Check for stale legacy copies that could cause confusion
578
+ const legacyDirs = [
579
+ path.join(home, ".clawdbot", "hooks", "cortex-memory"),
580
+ path.join(home, ".clawdbot", "hooks", "internal", "cortex-memory"),
581
+ ];
582
+
583
+ // Only check real directories, not symlinks pointing back to .openclaw
584
+ const clawdbotBase = path.join(home, ".clawdbot");
585
+ let isSymlink = false;
586
+ try {
587
+ const stat = await fs.lstat(clawdbotBase);
588
+ isSymlink = stat.isSymbolicLink();
589
+ } catch { /* doesn't exist */ }
590
+
591
+ if (!isSymlink) {
592
+ for (const legacyDir of legacyDirs) {
593
+ try {
594
+ await fs.access(legacyDir);
595
+ // Legacy dir exists and isn't a symlink — clean it up
596
+ await fs.rm(legacyDir, { recursive: true });
597
+ console.log(`[cortex-memory] Self-heal: removed stale legacy hook at ${legacyDir}`);
598
+ } catch { /* doesn't exist — good */ }
599
+ }
600
+ }
601
+
602
+ return; // All good
603
+ }
604
+
605
+ // We're running from an unexpected location — try to copy ourselves to the right place
606
+ const targetDir = expectedDirs[0]; // prefer hooks/internal/cortex-memory
607
+ const targetParent = path.dirname(targetDir);
608
+
609
+ // Ensure parent exists
610
+ await fs.mkdir(targetParent, { recursive: true });
611
+ await fs.mkdir(targetDir, { recursive: true });
612
+
613
+ // Copy our files to the expected location
614
+ const filesToCopy = ["HOOK.md", "handler.ts"];
615
+ let copiedCount = 0;
616
+
617
+ for (const file of filesToCopy) {
618
+ const src = path.join(myDir, file);
619
+ const dest = path.join(targetDir, file);
620
+ try {
621
+ await fs.access(src);
622
+ await fs.copyFile(src, dest);
623
+ copiedCount++;
624
+ } catch { /* source file missing — skip */ }
625
+ }
626
+
627
+ if (copiedCount > 0) {
628
+ console.log(`[cortex-memory] Self-heal: copied ${copiedCount} file(s) to ${targetDir}`);
629
+ console.log(`[cortex-memory] Hook will load from correct path on next restart`);
630
+
631
+ // Inject a warning into bootstrap context so the agent knows
632
+ if (event?.context?.bootstrapFiles && Array.isArray(event.context.bootstrapFiles)) {
633
+ event.context.bootstrapFiles.push({
634
+ name: "SHIELDCORTEX_HOOK_MIGRATED.md",
635
+ content: `# ShieldCortex Hook Self-Healed\n\nThe cortex-memory hook was running from an unexpected path (${myDir}).\nIt has been copied to ${targetDir}.\nA gateway restart will pick up the new location.\n\nNo action needed — this is informational.`,
636
+ });
637
+ }
638
+ }
639
+ } catch (err) {
640
+ // Self-check must NEVER break the hook — fail silently
641
+ console.warn("[cortex-memory] Self-check failed (non-fatal):", err instanceof Error ? err.message : String(err));
642
+ }
643
+ }
644
+
543
645
  // ==================== MAIN HANDLER ====================
544
646
 
545
647
  const cortexMemoryHandler = async (event) => {
@@ -552,6 +654,7 @@ const cortexMemoryHandler = async (event) => {
552
654
  // Also save on clear/exit - these also end the session context
553
655
  await onSessionStop(event);
554
656
  } else if (event.type === "agent" && event.action === "bootstrap") {
657
+ await selfCheckAndHeal(event);
555
658
  await onBootstrap(event);
556
659
  } else if (event.type === "message") {
557
660
  // FIX: Check for keyword triggers on message events (not just commands)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shieldcortex",
3
- "version": "2.10.6",
3
+ "version": "2.10.7",
4
4
  "description": "Persistent brain for AI agents. Knowledge graphs, memory decay, contradiction detection, consolidation — plus the only defence pipeline that stops memory poisoning. Works with Claude Code, OpenClaw, LangChain, and any MCP agent.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",