Skip to content

fix(openclaw): shell safety, HTTP dual-mode, lazy reinit, per-user banks#388

Merged
nicoloboschi merged 1 commit intovectorize-io:mainfrom
slayoffer:fix/openclaw-shell-and-reinit
Feb 18, 2026
Merged

fix(openclaw): shell safety, HTTP dual-mode, lazy reinit, per-user banks#388
nicoloboschi merged 1 commit intovectorize-io:mainfrom
slayoffer:fix/openclaw-shell-and-reinit

Conversation

@slayoffer
Copy link
Contributor

@slayoffer slayoffer commented Feb 17, 2026

Summary

Comprehensive hardening and feature additions for the OpenClaw Hindsight plugin:

Shell safety

  • execexecFile: Bypass the shell entirely, preventing injection via special characters (quotes, backticks, $()) in chat history. All CLI invocations now use array args.
  • escapeShellArg removed: No longer needed — execFile doesn't interpret shell metacharacters.
  • sanitize(): Strip null bytes from strings — Node 22 rejects them in execFile() args.

HTTP dual-mode

  • Direct HTTP client: When apiUrl is configured, the client talks directly to the Hindsight API via HTTP (fetch), bypassing the subprocess/CLI entirely. Useful for production deployments where the API is already running.
  • HindsightClientOptions: Replace 5 positional constructor args with a typed options object for clarity and extensibility (apiUrl, apiToken fields).
  • buildClientOptions(): Centralized helper replaces 7 duplicated constructor call sites across index.ts.

Reliability

  • Recall timeout: Accept optional timeoutMs parameter for both HTTP and subprocess modes. HTTP defaults to 15s, subprocess to 30s.
  • In-flight recall dedup: Concurrent recalls for the same bank reuse one promise instead of firing duplicate requests.
  • Timeout/abort handling: Graceful warn-level logging instead of error spam when recall times out (TimeoutError, AbortError).
  • Error cause chaining: Wrap errors with { cause } for better debugging stack traces.
  • lazyReinit(): Recover from startup health check failure with 30s cooldown and concurrency guard. Only triggers in external API mode.

Per-user memory isolation

  • senderId instead of channelId: Derive bank ID from {messageProvider}-{senderId} for proper memory isolation per user, even across channels.
  • Dynamic bank ID options: Configurable via dynamicBankId (default: enabled) and bankIdPrefix in plugin config.

Test plan

  • npx vitest run — 12/12 tests pass
  • Lint passes
  • Manual test with OpenClaw + external Hindsight API
  • Manual test with OpenClaw + local daemon mode

@slayoffer slayoffer force-pushed the fix/openclaw-shell-and-reinit branch from e5d615c to 073773b Compare February 17, 2026 03:57
Copy link
Collaborator

@nicoloboschi nicoloboschi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the execFile switch is a great improvement for shell safety, but it doesn't fix E2BIG — execFile still calls execve() internally, so ARG_MAX still applies to large content passed as argv. We need to write the content to a temp file and use retain-files for retain(). The index.ts changes (lazy reinit, senderId) look good.

can you rebase?

@slayoffer slayoffer force-pushed the fix/openclaw-shell-and-reinit branch 2 times, most recently from a197aa4 to b8cdabb Compare February 18, 2026 01:23
@slayoffer slayoffer changed the title fix(openclaw): shell quoting crash, lazy reinit, per-user bank IDs fix(openclaw): shell safety, HTTP dual-mode, lazy reinit, per-user banks Feb 18, 2026
@slayoffer slayoffer force-pushed the fix/openclaw-shell-and-reinit branch 2 times, most recently from 1551e2e to 17275a2 Compare February 18, 2026 08:57
- exec→execFile: bypass shell entirely, preventing injection via
  special characters in chat history
- HTTP dual-mode: client can now talk directly to the Hindsight API
  via HTTP (setBankMission, retain, recall) when apiUrl is configured,
  bypassing the subprocess/CLI entirely for production deployments
- HindsightClientOptions: replace 5 positional constructor args with
  a typed options object for clarity and extensibility
- sanitize(): strip null bytes from strings — Node 22 rejects them
  in execFile() args
- recall timeout: accept optional timeoutMs parameter for both HTTP
  and subprocess modes; subprocess gets a longer 30s default
- In-flight recall dedup: concurrent recalls for the same bank reuse
  one promise instead of firing duplicate requests
- Timeout/abort handling: graceful warn-level logging instead of
  error spam when recall times out
- Error cause chaining: wrap errors with { cause } for better
  debugging stack traces
- lazyReinit: recover from startup health check failure with 30s
  cooldown and concurrency guard
- Per-user banks: derive bank ID from senderId (not channelId) for
  proper memory isolation per user across channels
- buildClientOptions(): centralized helper replaces 7 duplicated
  constructor call sites

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@slayoffer slayoffer force-pushed the fix/openclaw-shell-and-reinit branch from 17275a2 to dfb98d9 Compare February 18, 2026 10:39
@nicoloboschi nicoloboschi merged commit c461013 into vectorize-io:main Feb 18, 2026
14 of 29 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants