shieldcortex 4.2.3 → 4.2.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.
@@ -195,7 +195,12 @@ function preferredHookDir(hooksDir) {
195
195
  return path.join(hooksDir, HOOK_NAME);
196
196
  }
197
197
  function legacyHookDirs(hooksDir) {
198
- return [path.join(hooksDir, 'internal', HOOK_NAME)];
198
+ return [
199
+ // Legacy: top-level "shieldcortex" directory created by old installers
200
+ path.join(hooksDir, 'shieldcortex'),
201
+ // Legacy: internal/cortex-memory path from v3 and early v4
202
+ path.join(hooksDir, 'internal', HOOK_NAME),
203
+ ];
199
204
  }
200
205
  function hasRequiredHookFiles(dir) {
201
206
  return HOOK_FILES.every(file => fs.existsSync(path.join(dir, file)));
@@ -631,9 +636,8 @@ export async function installOpenClawHook(options = {}) {
631
636
  for (const hooksDir of hooksDirs) {
632
637
  const destDir = preferredHookDir(hooksDir);
633
638
  try {
639
+ // Clean up legacy paths BEFORE installing to avoid duplicate hooks
634
640
  const legacyDirsBeforeInstall = detectLegacyHookVariants(hooksDir);
635
- copyHookFiles(HOOK_SOURCE, destDir);
636
- console.log(`Installed cortex-memory hook to ${destDir}`);
637
641
  if (legacyDirsBeforeInstall.length > 0) {
638
642
  console.log(`Detected legacy OpenClaw hook layout in ${hooksDir} — migrating to ${destDir}`);
639
643
  }
@@ -641,6 +645,8 @@ export async function installOpenClawHook(options = {}) {
641
645
  console.log(`Removed legacy cortex-memory hook from ${removedDir}`);
642
646
  migratedLegacy++;
643
647
  }
648
+ copyHookFiles(HOOK_SOURCE, destDir);
649
+ console.log(`Installed cortex-memory hook to ${destDir}`);
644
650
  installed++;
645
651
  }
646
652
  catch (err) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shieldcortex",
3
- "version": "4.2.3",
3
+ "version": "4.2.4",
4
4
  "description": "Trustworthy memory and security for AI agents. Recall debugging, review queue, OpenClaw session capture, and memory poisoning defence for Claude Code, Codex, OpenClaw, LangChain, and MCP agents.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -584,95 +584,103 @@ export default {
584
584
  jsonSchema: PLUGIN_CONFIG_JSON_SCHEMA,
585
585
  },
586
586
  register(api) {
587
- applyPluginConfigOverride(api);
588
- // --- Interceptor (lazy init) ---
589
- let interceptorReady = null;
590
- let interceptorInitAttempted = false;
591
- async function initInterceptor() {
592
- if (interceptorInitAttempted)
593
- return interceptorReady;
594
- interceptorInitAttempted = true;
595
- try {
596
- const scConfig = await loadConfig();
597
- const rawInterceptorConfig = scConfig.interceptor;
598
- const interceptorConfig = {
599
- ...DEFAULT_INTERCEPTOR_CONFIG,
600
- ...(rawInterceptorConfig && typeof rawInterceptorConfig === 'object' ? {
601
- enabled: rawInterceptorConfig.enabled ?? DEFAULT_INTERCEPTOR_CONFIG.enabled,
602
- severityActions: { ...DEFAULT_INTERCEPTOR_CONFIG.severityActions, ...rawInterceptorConfig.severityActions },
603
- failurePolicy: { ...DEFAULT_INTERCEPTOR_CONFIG.failurePolicy, ...rawInterceptorConfig.failurePolicy },
604
- } : {}),
605
- logger: { info: api.logger?.info ?? console.log, warn: api.logger?.warn ?? console.warn },
606
- };
607
- if (!interceptorConfig.enabled)
608
- return null;
609
- // Dynamic import with string variable to prevent TypeScript from resolving
610
- // at compile time 'shieldcortex/defence' only exists at runtime when the
611
- // package is installed globally, not during CI builds of the plugin itself.
612
- const defenceModPath = 'shieldcortex' + '/defence';
613
- const defenceMod = await import(/* webpackIgnore: true */ defenceModPath);
614
- if (typeof defenceMod.runDefencePipeline !== 'function')
587
+ try {
588
+ applyPluginConfigOverride(api);
589
+ // --- Interceptor (lazy init) ---
590
+ let interceptorReady = null;
591
+ let interceptorInitAttempted = false;
592
+ async function initInterceptor() {
593
+ if (interceptorInitAttempted)
594
+ return interceptorReady;
595
+ interceptorInitAttempted = true;
596
+ try {
597
+ const scConfig = await loadConfig();
598
+ const rawInterceptorConfig = scConfig.interceptor;
599
+ const interceptorConfig = {
600
+ ...DEFAULT_INTERCEPTOR_CONFIG,
601
+ ...(rawInterceptorConfig && typeof rawInterceptorConfig === 'object' ? {
602
+ enabled: rawInterceptorConfig.enabled ?? DEFAULT_INTERCEPTOR_CONFIG.enabled,
603
+ severityActions: { ...DEFAULT_INTERCEPTOR_CONFIG.severityActions, ...rawInterceptorConfig.severityActions },
604
+ failurePolicy: { ...DEFAULT_INTERCEPTOR_CONFIG.failurePolicy, ...rawInterceptorConfig.failurePolicy },
605
+ } : {}),
606
+ logger: { info: api.logger?.info ?? console.log, warn: api.logger?.warn ?? console.warn },
607
+ };
608
+ if (!interceptorConfig.enabled)
609
+ return null;
610
+ // Dynamic import with string variable to prevent TypeScript from resolving
611
+ // at compile time 'shieldcortex/defence' only exists at runtime when the
612
+ // package is installed globally, not during CI builds of the plugin itself.
613
+ const defenceModPath = 'shieldcortex' + '/defence';
614
+ const defenceMod = await import(/* webpackIgnore: true */ defenceModPath);
615
+ if (typeof defenceMod.runDefencePipeline !== 'function')
616
+ return null;
617
+ interceptorReady = createInterceptor(interceptorConfig, defenceMod.runDefencePipeline, {
618
+ onAuditEntry: (entry) => syncInterceptEvent(entry, {
619
+ cloudApiKey: scConfig.cloudApiKey ?? '',
620
+ cloudBaseUrl: scConfig.cloudBaseUrl ?? 'https://api.shieldcortex.ai',
621
+ cloudEnabled: scConfig.cloudEnabled ?? false,
622
+ }),
623
+ });
624
+ api.logger?.info?.('[shieldcortex] Interceptor active — watching: remember, mcp__memory__remember');
625
+ return interceptorReady;
626
+ }
627
+ catch (err) {
628
+ api.logger?.warn?.(`[shieldcortex] Interceptor init failed: ${err instanceof Error ? err.message : err}`);
615
629
  return null;
616
- interceptorReady = createInterceptor(interceptorConfig, defenceMod.runDefencePipeline, {
617
- onAuditEntry: (entry) => syncInterceptEvent(entry, {
618
- cloudApiKey: scConfig.cloudApiKey ?? '',
619
- cloudBaseUrl: scConfig.cloudBaseUrl ?? 'https://api.shieldcortex.ai',
620
- cloudEnabled: scConfig.cloudEnabled ?? false,
621
- }),
630
+ }
631
+ }
632
+ // Register before_tool_call with lazy-init wrapper
633
+ api.registerHook('before_tool_call', async (context) => {
634
+ const interceptor = await initInterceptor();
635
+ if (interceptor)
636
+ await interceptor.handleToolCall(context);
637
+ }, {
638
+ name: 'shieldcortex-intercept-tool',
639
+ description: 'Active threat gating on tool calls',
640
+ });
641
+ // Try to register session_end for cache cleanup
642
+ try {
643
+ api.registerHook('session_end', () => { interceptorReady?.resetSession(); }, {
644
+ name: 'shieldcortex-session-cleanup',
645
+ description: 'Clear interceptor deny cache on session end',
622
646
  });
623
- api.logger?.info?.('[shieldcortex] Interceptor active — watching: remember, mcp__memory__remember');
624
- return interceptorReady;
625
647
  }
626
- catch (err) {
627
- api.logger?.warn?.(`[shieldcortex] Interceptor init failed: ${err instanceof Error ? err.message : err}`);
628
- return null;
648
+ catch {
649
+ // session_end may not be a supported hook TTL safety net handles this
629
650
  }
630
- }
631
- // Register before_tool_call with lazy-init wrapper
632
- api.registerHook('before_tool_call', async (context) => {
633
- const interceptor = await initInterceptor();
634
- if (interceptor)
635
- await interceptor.handleToolCall(context);
636
- }, {
637
- name: 'shieldcortex-intercept-tool',
638
- description: 'Active threat gating on tool calls',
639
- });
640
- // Try to register session_end for cache cleanup
641
- try {
642
- api.registerHook('session_end', () => { interceptorReady?.resetSession(); }, {
643
- name: 'shieldcortex-session-cleanup',
644
- description: 'Clear interceptor deny cache on session end',
651
+ // Explicit capability registration (replaces legacy api.on)
652
+ api.registerHook("llm_input", handleLlmInput, {
653
+ name: "shieldcortex-scan-input",
654
+ description: "Real-time threat scanning on LLM input",
655
+ });
656
+ api.registerHook("llm_output", handleLlmOutput, {
657
+ name: "shieldcortex-scan-output",
658
+ description: "Memory extraction from LLM output",
659
+ });
660
+ // Register a lightweight status command so the plugin is not hook-only
661
+ api.registerCommand({
662
+ name: "shieldcortex-status",
663
+ description: "Show ShieldCortex real-time scanner status",
664
+ async handler() {
665
+ const cfg = await loadConfig();
666
+ const autoMemory = isAutoMemoryEnabled(cfg) ? "on" : "off";
667
+ const dedupe = isAutoMemoryDedupeEnabled(cfg) ? "on" : "off";
668
+ const cloud = cfg.cloudApiKey ? "configured" : "not configured";
669
+ return {
670
+ text: `ShieldCortex v${_version}\n` +
671
+ ` Hooks: llm_input (scan), llm_output (memory)\n` +
672
+ ` Auto memory: ${autoMemory} | Dedupe: ${dedupe}\n` +
673
+ ` Cloud sync: ${cloud}`,
674
+ };
675
+ },
645
676
  });
677
+ api.logger.info(`[shieldcortex] v${_version} registered (llm_input + llm_output + before_tool_call + /shieldcortex-status)`);
646
678
  }
647
- catch {
648
- // session_end may not be a supported hook TTL safety net handles this
679
+ catch (err) {
680
+ // Plugin must never block channel startupwarn and bail gracefully
681
+ const msg = err instanceof Error ? err.message : String(err);
682
+ console.warn(`[shieldcortex] WARNING: Plugin failed to initialize: ${msg}`);
683
+ console.warn('[shieldcortex] Real-time scanning is disabled. Channels will start normally.');
649
684
  }
650
- // Explicit capability registration (replaces legacy api.on)
651
- api.registerHook("llm_input", handleLlmInput, {
652
- name: "shieldcortex-scan-input",
653
- description: "Real-time threat scanning on LLM input",
654
- });
655
- api.registerHook("llm_output", handleLlmOutput, {
656
- name: "shieldcortex-scan-output",
657
- description: "Memory extraction from LLM output",
658
- });
659
- // Register a lightweight status command so the plugin is not hook-only
660
- api.registerCommand({
661
- name: "shieldcortex-status",
662
- description: "Show ShieldCortex real-time scanner status",
663
- async handler() {
664
- const cfg = await loadConfig();
665
- const autoMemory = isAutoMemoryEnabled(cfg) ? "on" : "off";
666
- const dedupe = isAutoMemoryDedupeEnabled(cfg) ? "on" : "off";
667
- const cloud = cfg.cloudApiKey ? "configured" : "not configured";
668
- return {
669
- text: `ShieldCortex v${_version}\n` +
670
- ` Hooks: llm_input (scan), llm_output (memory)\n` +
671
- ` Auto memory: ${autoMemory} | Dedupe: ${dedupe}\n` +
672
- ` Cloud sync: ${cloud}`,
673
- };
674
- },
675
- });
676
- api.logger.info(`[shieldcortex] v${_version} registered (llm_input + llm_output + before_tool_call + /shieldcortex-status)`);
677
685
  },
678
686
  };