refactor(openclaw)!: read config from plugin config instead of process.env (#974)

* refactor(openclaw)!: read config from plugin config instead of process.env

The plugin loaded credentials and runtime settings from environment
variables (HINDSIGHT_API_LLM_*, HINDSIGHT_EMBED_API_*, HINDSIGHT_BANK_ID)
plus auto-detection of OPENAI_API_KEY / ANTHROPIC_API_KEY / GEMINI_API_KEY
/ GROQ_API_KEY. That tripped OpenClaw's install-scanner env-harvesting
rule and bypassed the framework's first-class SecretRef resolution.
Switch to reading from the plugin config exclusively, with secrets
configured via 'openclaw config set ... --ref-source env|file|exec'.

Combined with the daemon lifecycle extraction in #949, this closes the
remaining install-scanner findings the 0.5.x plugin was hitting. The
plugin source now contains neither process.env nor child_process; the
former moved to plugin config (resolved by OpenClaw before the plugin
loads), and the latter lives in @vectorize-io/hindsight-all under
node_modules where the scanner's directory walker skips it. The plugin
can be installed without --dangerously-force-unsafe-install.

BREAKING CHANGE: drops the llmApiKeyEnv plugin config field along with
the HINDSIGHT_API_LLM_*, HINDSIGHT_EMBED_API_*, and HINDSIGHT_BANK_ID
environment variables. Users must now configure llmProvider and
llmApiKey explicitly via 'openclaw config set'. Migration guide is in
hindsight-docs/docs-integrations/openclaw.md and the integration
changelog.

* chore(openclaw): pin published versions of hindsight-all and hindsight-client

Phase 2 (#949) introduced @vectorize-io/hindsight-all and
@vectorize-io/hindsight-client as plugin dependencies using 'file:'
workspace paths. Those paths resolve inside the monorepo but break when
the published tarball is installed outside it — 'openclaw plugins
install @vectorize-io/hindsight-openclaw' failed with 'Cannot find
module @vectorize-io/hindsight-all' because npm could not resolve the
file: path from the extracted extension directory.

Replace both with semver ranges targeting the published versions:

  @vectorize-io/hindsight-all   ^0.1.0
  @vectorize-io/hindsight-client ^0.5.0

Verified end-to-end: 'openclaw plugins install <local-tarball>' now
succeeds without --dangerously-force-unsafe-install and without the
workspace-symlink hack. npm pulls both dependencies from the registry
into the extracted extension's node_modules, the plugin loads cleanly,
and 'openclaw plugins doctor' reports no issues.
This commit is contained in:
Nicolò Boschi 2026-04-10 18:27:28 +02:00 committed by GitHub
parent b57e337fa2
commit e22ae05f47
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 292 additions and 253 deletions

View file

@ -14,36 +14,37 @@ This plugin integrates [hindsight-embed](https://vectorize.io/hindsight/cli), a
## Quick Start ## Quick Start
**Step 1: Set up LLM for memory extraction** **Step 1: Install the plugin**
Choose one provider and set its API key:
```bash
# Option A: OpenAI
export OPENAI_API_KEY="sk-your-key"
# Option B: Anthropic
export ANTHROPIC_API_KEY="your-key"
# Option C: Gemini
export GEMINI_API_KEY="your-key"
# Option D: Groq
export GROQ_API_KEY="your-key"
# Option E: Claude Code (no API key needed)
export HINDSIGHT_API_LLM_PROVIDER=claude-code
# Option F: OpenAI Codex (no API key needed)
export HINDSIGHT_API_LLM_PROVIDER=openai-codex
```
**Step 2: Install the plugin**
```bash ```bash
openclaw plugins install @vectorize-io/hindsight-openclaw openclaw plugins install @vectorize-io/hindsight-openclaw
``` ```
**Step 2: Configure the LLM provider used for memory extraction**
The plugin reads configuration from OpenClaw's plugin config — set it
non-interactively with `openclaw config set`:
```bash
# Option A — OpenAI (set llmApiKey as a SecretRef so the value comes from
# the OPENAI_API_KEY environment variable at runtime instead of being
# stored in plaintext on disk)
openclaw config set plugins.entries.hindsight-openclaw.config.llmProvider openai
openclaw config set plugins.entries.hindsight-openclaw.config.llmApiKey \
--ref-source env --ref-provider default --ref-id OPENAI_API_KEY
# Option B — Anthropic
openclaw config set plugins.entries.hindsight-openclaw.config.llmProvider anthropic
openclaw config set plugins.entries.hindsight-openclaw.config.llmApiKey \
--ref-source env --ref-provider default --ref-id ANTHROPIC_API_KEY
# Option C — Claude Code (no API key needed)
openclaw config set plugins.entries.hindsight-openclaw.config.llmProvider claude-code
# Option D — OpenAI Codex (no API key needed)
openclaw config set plugins.entries.hindsight-openclaw.config.llmProvider openai-codex
```
**Step 3: Start OpenClaw** **Step 3: Start OpenClaw**
```bash ```bash
@ -57,6 +58,8 @@ The plugin will automatically:
**Important:** The LLM you configure above is **only for memory extraction** (background processing). Your main OpenClaw agent can use any model you configure separately. **Important:** The LLM you configure above is **only for memory extraction** (background processing). Your main OpenClaw agent can use any model you configure separately.
**Migrating from 0.5.x?** See the [Migration from 0.5.x](#migration-from-05x) section below for the env-var → SecretRef mapping.
## How It Works ## How It Works
**Auto-Capture:** Every conversation is automatically stored after each turn. Facts, entities, and relationships are extracted in the background. **Auto-Capture:** Every conversation is automatically stored after each turn. Facts, entities, and relationships are extracted in the background.
@ -94,9 +97,13 @@ Optional settings in `~/.openclaw/openclaw.json`:
- `apiPort` - Port for the openclaw profile daemon (default: `9077`) - `apiPort` - Port for the openclaw profile daemon (default: `9077`)
- `daemonIdleTimeout` - Seconds before daemon shuts down from inactivity (default: `0` = never) - `daemonIdleTimeout` - Seconds before daemon shuts down from inactivity (default: `0` = never)
- `embedVersion` - hindsight-embed version (default: `"latest"`) - `embedVersion` - hindsight-embed version (default: `"latest"`)
- `llmProvider` - LLM provider for memory extraction (`openai`, `anthropic`, `gemini`, `groq`, `ollama`, `openai-codex`, `claude-code`). Required unless `hindsightApiUrl` is set.
- `llmModel` - LLM model used with `llmProvider` (provider default if omitted)
- `llmApiKey` - API key for the LLM provider. **Sensitive** — set via `openclaw config set ... --ref-source env --ref-id OPENAI_API_KEY` to reference an env var.
- `llmBaseUrl` - Optional base URL override for OpenAI-compatible providers (e.g. `https://openrouter.ai/api/v1`)
- `bankMission` - Agent identity/purpose stored on the memory bank. Helps the memory engine understand context for better fact extraction during retain. Set once per bank on first use — not a recall prompt. - `bankMission` - Agent identity/purpose stored on the memory bank. Helps the memory engine understand context for better fact extraction during retain. Set once per bank on first use — not a recall prompt.
- `dynamicBankId` - Enable per-context memory banks (default: `true`) - `dynamicBankId` - Enable per-context memory banks (default: `true`)
- `bankId` - Static bank ID used when `dynamicBankId` is `false`. Can also be set with `HINDSIGHT_BANK_ID`. - `bankId` - Static bank ID used when `dynamicBankId` is `false`.
- `bankIdPrefix` - Optional prefix for bank IDs (e.g. `"prod"``"prod-slack-C123"` or `"prod-shared-bank"`) - `bankIdPrefix` - Optional prefix for bank IDs (e.g. `"prod"``"prod-slack-C123"` or `"prod-shared-bank"`)
- `dynamicBankGranularity` - Fields used to derive bank ID: `agent`, `channel`, `user`, `provider` (default: `["agent", "channel", "user"]`) - `dynamicBankGranularity` - Fields used to derive bank ID: `agent`, `channel`, `user`, `provider` (default: `["agent", "channel", "user"]`)
- `excludeProviders` - Message providers to skip for recall/retain (e.g. `["slack"]`, `["telegram"]`, `["discord"]`) - `excludeProviders` - Message providers to skip for recall/retain (e.g. `["slack"]`, `["telegram"]`, `["discord"]`)
@ -145,7 +152,7 @@ Available isolation fields:
- `user` - The user interacting with the agent - `user` - The user interacting with the agent
- `provider` - The message provider (e.g. Slack, Discord) - `provider` - The message provider (e.g. Slack, Discord)
Use `bankIdPrefix` to namespace bank IDs across environments (e.g. `"prod"`, `"staging"`). Set `dynamicBankId` to `false` to use a single shared bank for all conversations. In static mode, the plugin uses `bankId`, then `HINDSIGHT_BANK_ID`, then the default `openclaw` bank name. Use `bankIdPrefix` to namespace bank IDs across environments (e.g. `"prod"`, `"staging"`). Set `dynamicBankId` to `false` to use a single shared bank for all conversations. In static mode, the plugin uses `bankId` if set, otherwise the default `openclaw` bank name.
### Retention Controls ### Retention Controls
@ -172,38 +179,61 @@ By default, the plugin retains `user` and `assistant` messages after each turn.
### LLM Configuration ### LLM Configuration
The plugin auto-detects your LLM provider from these environment variables: Configure the memory-extraction LLM via OpenClaw's plugin config. API keys
should be stored as `SecretRef` values so they're resolved from env vars,
mounted files, or `exec`-style secret managers (Vault, etc.) at runtime
instead of sitting in plaintext on disk.
| Provider | Env Var | Notes | | Provider | `llmProvider` | API key |
|----------|---------|-------| |---|---|---|
| OpenAI | `OPENAI_API_KEY` | | | OpenAI | `openai` | required |
| Anthropic | `ANTHROPIC_API_KEY` | | | Anthropic | `anthropic` | required |
| Gemini | `GEMINI_API_KEY` | | | Gemini | `gemini` | required |
| Groq | `GROQ_API_KEY` | | | Groq | `groq` | required |
| Claude Code | `HINDSIGHT_API_LLM_PROVIDER=claude-code` | No API key needed | | Ollama | `ollama` | not required (local) |
| OpenAI Codex | `HINDSIGHT_API_LLM_PROVIDER=openai-codex` | No API key needed | | Claude Code | `claude-code` | not required (uses Claude Code CLI) |
| OpenAI Codex | `openai-codex` | not required (uses Codex CLI auth) |
The model is selected automatically by the Hindsight API. To override, set `HINDSIGHT_API_LLM_MODEL`. **Set provider + API key:**
**Override with explicit config:**
```bash ```bash
export HINDSIGHT_API_LLM_PROVIDER=openai openclaw config set plugins.entries.hindsight-openclaw.config.llmProvider openai
export HINDSIGHT_API_LLM_API_KEY=sk-your-key openclaw config set plugins.entries.hindsight-openclaw.config.llmApiKey \
--ref-source env --ref-provider default --ref-id OPENAI_API_KEY
# Optional: custom base URL (OpenRouter, Azure, vLLM, etc.)
export HINDSIGHT_API_LLM_BASE_URL=https://openrouter.ai/api/v1
``` ```
**Example: Free OpenRouter model** **Override the model (optional — Hindsight picks a sensible default per provider):**
```bash ```bash
export HINDSIGHT_API_LLM_PROVIDER=openai openclaw config set plugins.entries.hindsight-openclaw.config.llmModel gpt-4o-mini
export HINDSIGHT_API_LLM_MODEL=xiaomi/mimo-v2-flash # FREE!
export HINDSIGHT_API_LLM_API_KEY=sk-or-v1-your-openrouter-key
export HINDSIGHT_API_LLM_BASE_URL=https://openrouter.ai/api/v1
``` ```
**OpenAI-compatible providers (OpenRouter, Azure OpenAI, vLLM, ...):**
```bash
openclaw config set plugins.entries.hindsight-openclaw.config.llmProvider openai
openclaw config set plugins.entries.hindsight-openclaw.config.llmBaseUrl https://openrouter.ai/api/v1
openclaw config set plugins.entries.hindsight-openclaw.config.llmApiKey \
--ref-source env --ref-provider default --ref-id OPENROUTER_API_KEY
openclaw config set plugins.entries.hindsight-openclaw.config.llmModel xiaomi/mimo-v2-flash
```
**Use a file or exec source instead of env (for K8s secrets, Vault, etc.):**
```bash
# File source (e.g. mounted Docker/K8s secret)
openclaw config set plugins.entries.hindsight-openclaw.config.llmApiKey \
--ref-source file --ref-provider mounted-json --ref-id /providers/openai/apiKey
# Exec source (e.g. HashiCorp Vault)
openclaw config set plugins.entries.hindsight-openclaw.config.llmApiKey \
--ref-source exec --ref-provider vault --ref-id openai/api-key
```
The corresponding secret provider needs to be configured under `secrets.providers`
in your OpenClaw config — see `openclaw config set --help` for the
`--provider-source`/`--provider-path`/`--provider-command` builder flags.
### External API (Advanced) ### External API (Advanced)
Connect to a remote Hindsight API server instead of running a local daemon. This is useful for: Connect to a remote Hindsight API server instead of running a local daemon. This is useful for:
@ -234,21 +264,13 @@ Configure in `~/.openclaw/openclaw.json`:
**Options:** **Options:**
- `hindsightApiUrl` - Full URL to external Hindsight API (e.g., `https://mcp.hindsight.example.com`) - `hindsightApiUrl` - Full URL to external Hindsight API (e.g., `https://mcp.hindsight.example.com`)
- `hindsightApiToken` - API token for authentication (optional, only if API requires auth) - `hindsightApiToken` - API token for authentication (optional). **Sensitive** — set as a SecretRef:
#### Environment Variables (Alternative)
You can also configure via environment variables:
```bash ```bash
export HINDSIGHT_EMBED_API_URL=https://your-hindsight-server.com openclaw config set plugins.entries.hindsight-openclaw.config.hindsightApiToken \
export HINDSIGHT_EMBED_API_TOKEN=your-api-token # Optional --ref-source env --ref-provider default --ref-id HINDSIGHT_API_TOKEN
openclaw gateway
``` ```
**Note:** Plugin config takes precedence over environment variables.
#### Behavior #### Behavior
When external API mode is enabled: When external API mode is enabled:
@ -336,27 +358,36 @@ uvx hindsight-embed@latest profile list
### No API key error ### No API key error
Make sure you've set one of the provider API keys (or use a provider that doesn't require one): Make sure you've configured the LLM provider through `openclaw config set`
(or use a provider that doesn't require a key):
```bash ```bash
# Option 1: OpenAI # Option 1 — OpenAI (requires OPENAI_API_KEY in your env)
export OPENAI_API_KEY="sk-your-key" openclaw config set plugins.entries.hindsight-openclaw.config.llmProvider openai
openclaw config set plugins.entries.hindsight-openclaw.config.llmApiKey \
--ref-source env --ref-provider default --ref-id OPENAI_API_KEY
# Option 2: Anthropic # Option 2 — Anthropic
export ANTHROPIC_API_KEY="your-key" openclaw config set plugins.entries.hindsight-openclaw.config.llmProvider anthropic
openclaw config set plugins.entries.hindsight-openclaw.config.llmApiKey \
--ref-source env --ref-provider default --ref-id ANTHROPIC_API_KEY
# Option 3: Claude Code (no API key needed) # Option 3 Claude Code (no API key needed)
export HINDSIGHT_API_LLM_PROVIDER=claude-code openclaw config set plugins.entries.hindsight-openclaw.config.llmProvider claude-code
# Option 4: OpenAI Codex (no API key needed) # Option 4 OpenAI Codex (no API key needed)
export HINDSIGHT_API_LLM_PROVIDER=openai-codex openclaw config set plugins.entries.hindsight-openclaw.config.llmProvider openai-codex
# Verify it's set # Verify the config is valid
echo $OPENAI_API_KEY openclaw config validate
# or
echo $HINDSIGHT_API_LLM_PROVIDER # Inspect the current value
openclaw config get plugins.entries.hindsight-openclaw.config.llmProvider
``` ```
If you used `--ref-source env`, double-check that the referenced env var
(e.g. `OPENAI_API_KEY`) is exported in the shell that runs `openclaw gateway`.
### Verify it's working ### Verify it's working
Check gateway logs for memory operations: Check gateway logs for memory operations:
@ -373,3 +404,29 @@ tail -f /tmp/openclaw/openclaw-*.log | grep Hindsight
# [Hindsight] Retained X messages for session ... # [Hindsight] Retained X messages for session ...
# [Hindsight] Auto-recall: Injecting X memories # [Hindsight] Auto-recall: Injecting X memories
``` ```
## Migration from 0.5.x
0.6.0 removes all process-environment reads from the plugin. Configuration that
previously came from shell env vars must now go through OpenClaw's plugin config
(with `SecretRef` for credentials). The plugin no longer auto-detects providers
from `OPENAI_API_KEY` / `ANTHROPIC_API_KEY` / etc. — you must set
`llmProvider` explicitly.
| Old (0.5.x) | New (0.6.0) |
|---|---|
| `OPENAI_API_KEY=…` (auto-detected) | `openclaw config set plugins.entries.hindsight-openclaw.config.llmProvider openai` <br/> `openclaw config set plugins.entries.hindsight-openclaw.config.llmApiKey --ref-source env --ref-id OPENAI_API_KEY` |
| `HINDSIGHT_API_LLM_PROVIDER=…` | `openclaw config set plugins.entries.hindsight-openclaw.config.llmProvider …` |
| `HINDSIGHT_API_LLM_MODEL=…` | `openclaw config set plugins.entries.hindsight-openclaw.config.llmModel …` |
| `HINDSIGHT_API_LLM_API_KEY=…` | `openclaw config set plugins.entries.hindsight-openclaw.config.llmApiKey --ref-source env --ref-id …` |
| `HINDSIGHT_API_LLM_BASE_URL=…` | `openclaw config set plugins.entries.hindsight-openclaw.config.llmBaseUrl …` |
| `HINDSIGHT_EMBED_API_URL=…` | `openclaw config set plugins.entries.hindsight-openclaw.config.hindsightApiUrl …` |
| `HINDSIGHT_EMBED_API_TOKEN=…` | `openclaw config set plugins.entries.hindsight-openclaw.config.hindsightApiToken --ref-source env --ref-id …` |
| `HINDSIGHT_BANK_ID=…` | `openclaw config set plugins.entries.hindsight-openclaw.config.bankId …` |
| `llmApiKeyEnv: "MY_KEY"` (plugin config) | `llmApiKey` configured as a SecretRef with `--ref-id MY_KEY` |
If your shell already exports `OPENAI_API_KEY`, the SecretRef config above
resolves to the same value at startup — you don't need to change your shell
setup, just point the plugin at the variable explicitly. Run
`openclaw config validate` after migrating to confirm the new shape parses
cleanly.

View file

@ -8,6 +8,21 @@ import PageHero from '@site/src/components/PageHero';
[← OpenClaw integration](/sdks/integrations/openclaw) [← OpenClaw integration](/sdks/integrations/openclaw)
## 0.6.0 (Unreleased)
**Breaking Changes**
- The plugin no longer reads any configuration from process environment variables. All settings — including the LLM provider, model, API key, base URL, external Hindsight API URL/token, and bank ID — must now be set through OpenClaw's plugin config (e.g. `openclaw config set plugins.entries.hindsight-openclaw.config.<field> <value>`). API keys and other secrets should be configured as `SecretRef` values via `--ref-source env|file|exec` so they're resolved from your secret store at runtime instead of being stored in plaintext on disk.
- Removed the `llmApiKeyEnv` plugin config field. Use the new `llmApiKey` field configured as a SecretRef instead (e.g. `openclaw config set plugins.entries.hindsight-openclaw.config.llmApiKey --ref-source env --ref-id OPENAI_API_KEY`).
- Removed automatic LLM provider detection from `OPENAI_API_KEY` / `ANTHROPIC_API_KEY` / `GEMINI_API_KEY` / `GROQ_API_KEY`. Set `llmProvider` and `llmApiKey` explicitly via `openclaw config set`.
- Removed support for the `HINDSIGHT_API_LLM_PROVIDER`, `HINDSIGHT_API_LLM_MODEL`, `HINDSIGHT_API_LLM_API_KEY`, `HINDSIGHT_API_LLM_BASE_URL`, `HINDSIGHT_EMBED_API_URL`, `HINDSIGHT_EMBED_API_TOKEN`, and `HINDSIGHT_BANK_ID` environment variables. The same values now live in plugin config — see the [migration guide](/sdks/integrations/openclaw#migration-from-05x).
**Features**
- Added the `llmApiKey` plugin config field, marked as a sensitive field so OpenClaw resolves it as a `SecretRef` from env, file, or exec sources.
- Added the `llmBaseUrl` plugin config field for OpenAI-compatible endpoint overrides (OpenRouter, Azure OpenAI, vLLM, etc.).
- Marked `hindsightApiToken` as a sensitive field — it can now be configured as a `SecretRef` the same way as `llmApiKey`.
## [0.5.1](https://github.com/vectorize-io/hindsight/tree/integrations/openclaw/v0.5.1) ## [0.5.1](https://github.com/vectorize-io/hindsight/tree/integrations/openclaw/v0.5.1)
**Bug Fixes** **Bug Fixes**

View file

@ -5,25 +5,56 @@ Biomimetic long-term memory for [OpenClaw](https://openclaw.ai) using [Hindsight
## Quick Start ## Quick Start
```bash ```bash
# 1. Configure your LLM provider for memory extraction # 1. Install the plugin
# Option A: OpenAI
export OPENAI_API_KEY="sk-your-key"
# Option B: Claude Code (no API key needed)
export HINDSIGHT_API_LLM_PROVIDER=claude-code
# Option C: OpenAI Codex (no API key needed)
export HINDSIGHT_API_LLM_PROVIDER=openai-codex
# 2. Install and enable the plugin
openclaw plugins install @vectorize-io/hindsight-openclaw openclaw plugins install @vectorize-io/hindsight-openclaw
# 2. Configure the LLM provider used for memory extraction.
# Option A — OpenAI (or any OpenAI-compatible provider)
openclaw config set plugins.entries.hindsight-openclaw.config.llmProvider openai
openclaw config set plugins.entries.hindsight-openclaw.config.llmApiKey \
--ref-source env --ref-provider default --ref-id OPENAI_API_KEY
# Option B — Claude Code (no API key needed)
openclaw config set plugins.entries.hindsight-openclaw.config.llmProvider claude-code
# Option C — OpenAI Codex (no API key needed)
openclaw config set plugins.entries.hindsight-openclaw.config.llmProvider openai-codex
# 3. Start OpenClaw # 3. Start OpenClaw
openclaw gateway openclaw gateway
``` ```
That's it! The plugin will automatically start capturing and recalling memories. That's it! The plugin will automatically start capturing and recalling memories.
`llmApiKey` is marked sensitive — `openclaw config set ... --ref-source env` writes a
SecretRef that resolves the value from your `OPENAI_API_KEY` environment variable at
runtime, so the key is never stored in plaintext on disk. `--ref-source file` and
`--ref-source exec` are also supported for mounted-secret and Vault-style setups.
## Migrating from 0.5.x
0.6.0 removes all process-environment reads from the plugin. Configuration that
previously came from shell env vars must now go through OpenClaw's plugin config
(with SecretRef for credentials). Concrete mappings:
| Old (0.5.x) | New (0.6.0) |
|---|---|
| `OPENAI_API_KEY=…` (auto-detected) | `openclaw config set plugins.entries.hindsight-openclaw.config.llmProvider openai` <br> `openclaw config set plugins.entries.hindsight-openclaw.config.llmApiKey --ref-source env --ref-id OPENAI_API_KEY` |
| `HINDSIGHT_API_LLM_PROVIDER=…` | `openclaw config set plugins.entries.hindsight-openclaw.config.llmProvider …` |
| `HINDSIGHT_API_LLM_MODEL=…` | `openclaw config set plugins.entries.hindsight-openclaw.config.llmModel …` |
| `HINDSIGHT_API_LLM_API_KEY=…` | `openclaw config set plugins.entries.hindsight-openclaw.config.llmApiKey --ref-source env --ref-id …` |
| `HINDSIGHT_API_LLM_BASE_URL=…` | `openclaw config set plugins.entries.hindsight-openclaw.config.llmBaseUrl …` |
| `HINDSIGHT_EMBED_API_URL=…` | `openclaw config set plugins.entries.hindsight-openclaw.config.hindsightApiUrl …` |
| `HINDSIGHT_EMBED_API_TOKEN=…` | `openclaw config set plugins.entries.hindsight-openclaw.config.hindsightApiToken --ref-source env --ref-id …` |
| `HINDSIGHT_BANK_ID=…` | `openclaw config set plugins.entries.hindsight-openclaw.config.bankId …` |
| `llmApiKeyEnv: "MY_KEY"` (plugin config) | `llmApiKey` configured as a SecretRef with `--ref-id MY_KEY` |
If your shell already exports `OPENAI_API_KEY`, the SecretRef config above resolves
to the same value at startup — no need to change your shell setup, just point the
plugin at the variable explicitly. Run `openclaw config validate` after migrating
to confirm the new shape parses cleanly.
## Features ## Features
- **Auto-capture** and **auto-recall** of memories each turn, injected into system prompt space so recalled memories stay out of the visible chat transcript - **Auto-capture** and **auto-recall** of memories each turn, injected into system prompt space so recalled memories stay out of the visible chat transcript
@ -43,11 +74,12 @@ Optional settings in `~/.openclaw/openclaw.json` under `plugins.entries.hindsigh
| `embedVersion` | `"latest"` | hindsight-embed version | | `embedVersion` | `"latest"` | hindsight-embed version |
| `embedPackagePath` | — | Local path to `hindsight-embed` package for development | | `embedPackagePath` | — | Local path to `hindsight-embed` package for development |
| `bankMission` | — | Agent identity/purpose stored on the memory bank. Helps the engine understand context for better fact extraction. Set once per bank — not a recall prompt. | | `bankMission` | — | Agent identity/purpose stored on the memory bank. Helps the engine understand context for better fact extraction. Set once per bank — not a recall prompt. |
| `llmProvider` | auto-detect | LLM provider override for memory extraction (`openai`, `anthropic`, `gemini`, `groq`, `ollama`, `openai-codex`, `claude-code`) | | `llmProvider` | — | LLM provider for memory extraction (`openai`, `anthropic`, `gemini`, `groq`, `ollama`, `openai-codex`, `claude-code`). Required unless `hindsightApiUrl` is set. |
| `llmModel` | provider default | LLM model override used with `llmProvider` | | `llmModel` | provider default | LLM model used with `llmProvider` |
| `llmApiKeyEnv` | provider standard env var | Custom env var name for the provider API key | | `llmApiKey` | — | API key for the LLM provider. **Sensitive** — set via `openclaw config set ... --ref-source env --ref-id OPENAI_API_KEY` to reference an env var (or `--ref-source file`/`exec` for mounted-secret/Vault sources). |
| `llmBaseUrl` | — | Optional base URL override for OpenAI-compatible providers (e.g. `https://openrouter.ai/api/v1`) |
| `dynamicBankId` | `true` | Enable per-context memory banks | | `dynamicBankId` | `true` | Enable per-context memory banks |
| `bankId` | — | Static bank ID used when `dynamicBankId` is `false`. Can also be set with `HINDSIGHT_BANK_ID`. | | `bankId` | — | Static bank ID used when `dynamicBankId` is `false`. |
| `bankIdPrefix` | — | Prefix for bank IDs (e.g. `"prod"`) | | `bankIdPrefix` | — | Prefix for bank IDs (e.g. `"prod"`) |
| `retainTags` | `[]` | Tags applied to every retained document, useful for cross-agent/source labeling (e.g. `source_system:openclaw`, `agent:agentname`) | | `retainTags` | `[]` | Tags applied to every retained document, useful for cross-agent/source labeling (e.g. `source_system:openclaw`, `agent:agentname`) |
| `retainSource` | `"openclaw"` | `source` value written into retained document metadata | | `retainSource` | `"openclaw"` | `source` value written into retained document metadata |
@ -67,7 +99,7 @@ Optional settings in `~/.openclaw/openclaw.json` under `plugins.entries.hindsigh
| `recallMaxQueryChars` | `800` | Maximum character length for the composed recall query before calling recall. | | `recallMaxQueryChars` | `800` | Maximum character length for the composed recall query before calling recall. |
| `recallPromptPreamble` | built-in string | Prompt text placed above recalled memories in the injected `<hindsight_memories>` system-context block. | | `recallPromptPreamble` | built-in string | Prompt text placed above recalled memories in the injected `<hindsight_memories>` system-context block. |
| `hindsightApiUrl` | — | External Hindsight API URL (skips local daemon) | | `hindsightApiUrl` | — | External Hindsight API URL (skips local daemon) |
| `hindsightApiToken` | — | Auth token for external API | | `hindsightApiToken` | — | Auth token for external API. **Sensitive** — set via `openclaw config set ... --ref-source env --ref-id HINDSIGHT_API_TOKEN`. |
| `ignoreSessionPatterns` | `[]` | Session key glob patterns to skip entirely — no recall, no retain (e.g. `["agent:*:cron:**"]`) | | `ignoreSessionPatterns` | `[]` | Session key glob patterns to skip entirely — no recall, no retain (e.g. `["agent:*:cron:**"]`) |
| `statelessSessionPatterns` | `[]` | Session key glob patterns for read-only sessions — retain is always skipped; recall is skipped when `skipStatelessSessions` is `true` (e.g. `["agent:*:subagent:**", "agent:*:heartbeat:**"]`) | | `statelessSessionPatterns` | `[]` | Session key glob patterns for read-only sessions — retain is always skipped; recall is skipped when `skipStatelessSessions` is `true` (e.g. `["agent:*:subagent:**", "agent:*:heartbeat:**"]`) |
| `skipStatelessSessions` | `true` | When `true`, sessions matching `statelessSessionPatterns` also skip recall. Set to `false` to allow recall but still skip retain. | | `skipStatelessSessions` | `true` | When `true`, sessions matching `statelessSessionPatterns` also skip recall. Set to `false` to allow recall but still skip retain. |

View file

@ -2,6 +2,14 @@
"id": "hindsight-openclaw", "id": "hindsight-openclaw",
"name": "Hindsight Memory", "name": "Hindsight Memory",
"kind": "memory", "kind": "memory",
"configContracts": {
"secretInputs": {
"paths": [
{ "path": "llmApiKey", "expected": "string" },
{ "path": "hindsightApiToken", "expected": "string" }
]
}
},
"configSchema": { "configSchema": {
"type": "object", "type": "object",
"properties": { "properties": {
@ -27,7 +35,7 @@
}, },
"llmProvider": { "llmProvider": {
"type": "string", "type": "string",
"description": "LLM provider for Hindsight memory (e.g. 'openai', 'anthropic', 'gemini', 'groq', 'ollama', 'openai-codex', 'claude-code'). Takes priority over auto-detection but not over HINDSIGHT_API_LLM_PROVIDER env var.", "description": "LLM provider for Hindsight memory (e.g. 'openai', 'anthropic', 'gemini', 'groq', 'ollama', 'openai-codex', 'claude-code').",
"enum": [ "enum": [
"openai", "openai",
"anthropic", "anthropic",
@ -42,9 +50,13 @@
"type": "string", "type": "string",
"description": "LLM model to use (e.g. 'gpt-4o-mini', 'claude-3-5-haiku-20241022'). Used with llmProvider." "description": "LLM model to use (e.g. 'gpt-4o-mini', 'claude-3-5-haiku-20241022'). Used with llmProvider."
}, },
"llmApiKeyEnv": { "llmApiKey": {
"type": ["string", "object"],
"description": "API key for the LLM provider used by the Hindsight memory daemon. Set via 'openclaw config set ... --ref-source env --ref-id OPENAI_API_KEY' to reference an env var without storing plaintext."
},
"llmBaseUrl": {
"type": "string", "type": "string",
"description": "Name of the env var holding the API key (e.g. 'MY_CUSTOM_KEY'). If not set, uses the standard env var for the chosen provider." "description": "Optional base URL override for OpenAI-compatible providers (e.g. 'https://openrouter.ai/api/v1')."
}, },
"embedPackagePath": { "embedPackagePath": {
"type": "string", "type": "string",
@ -60,7 +72,7 @@
"description": "External Hindsight API URL (e.g. 'https://mcp.hindsight.devcraft.team'). When set, skips local daemon and connects directly to this API." "description": "External Hindsight API URL (e.g. 'https://mcp.hindsight.devcraft.team'). When set, skips local daemon and connects directly to this API."
}, },
"hindsightApiToken": { "hindsightApiToken": {
"type": "string", "type": ["string", "object"],
"description": "API token for external Hindsight API authentication. Required if the external API has authentication enabled." "description": "API token for external Hindsight API authentication. Required if the external API has authentication enabled."
}, },
"dynamicBankId": { "dynamicBankId": {
@ -70,7 +82,7 @@
}, },
"bankId": { "bankId": {
"type": "string", "type": "string",
"description": "Static bank ID used when dynamicBankId is false. Can also be provided via HINDSIGHT_BANK_ID." "description": "Static bank ID used when dynamicBankId is false."
}, },
"bankIdPrefix": { "bankIdPrefix": {
"type": "string", "type": "string",
@ -290,9 +302,14 @@
"label": "LLM Model", "label": "LLM Model",
"placeholder": "e.g. gpt-4o-mini, claude-3-5-haiku-20241022" "placeholder": "e.g. gpt-4o-mini, claude-3-5-haiku-20241022"
}, },
"llmApiKeyEnv": { "llmApiKey": {
"label": "API Key Env Var", "label": "LLM API Key",
"placeholder": "e.g. MY_CUSTOM_API_KEY (optional)" "placeholder": "API key for the chosen LLM provider",
"sensitive": true
},
"llmBaseUrl": {
"label": "LLM Base URL",
"placeholder": "e.g. https://openrouter.ai/api/v1 (optional)"
}, },
"embedPackagePath": { "embedPackagePath": {
"label": "Local Package Path (Dev)", "label": "Local Package Path (Dev)",
@ -308,7 +325,8 @@
}, },
"hindsightApiToken": { "hindsightApiToken": {
"label": "External API Token", "label": "External API Token",
"placeholder": "API token if external API requires authentication" "placeholder": "API token if external API requires authentication",
"sensitive": true
}, },
"dynamicBankId": { "dynamicBankId": {
"label": "Dynamic Bank IDs", "label": "Dynamic Bank IDs",

View file

@ -44,8 +44,8 @@
"prepublishOnly": "npm run clean && npm run build" "prepublishOnly": "npm run clean && npm run build"
}, },
"dependencies": { "dependencies": {
"@vectorize-io/hindsight-client": "file:../../hindsight-clients/typescript", "@vectorize-io/hindsight-client": "^0.5.0",
"@vectorize-io/hindsight-all": "file:../../hindsight-all-npm" "@vectorize-io/hindsight-all": "^0.1.0"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^20.0.0", "@types/node": "^20.0.0",

View file

@ -173,12 +173,9 @@ function parseArgs(argv: string[]): ParsedArgs {
function inferApiSettings(pluginConfig: PluginConfig, explicitApiUrl?: string, explicitApiToken?: string): { apiUrl: string; apiToken?: string } { function inferApiSettings(pluginConfig: PluginConfig, explicitApiUrl?: string, explicitApiToken?: string): { apiUrl: string; apiToken?: string } {
const apiUrl = explicitApiUrl const apiUrl = explicitApiUrl
|| process.env.HINDSIGHT_EMBED_API_URL
|| pluginConfig.hindsightApiUrl || pluginConfig.hindsightApiUrl
|| `http://127.0.0.1:${pluginConfig.apiPort || 9077}`; || `http://127.0.0.1:${pluginConfig.apiPort || 9077}`;
const apiToken = explicitApiToken const apiToken = explicitApiToken || pluginConfig.hindsightApiToken;
|| process.env.HINDSIGHT_EMBED_API_TOKEN
|| pluginConfig.hindsightApiToken;
return { apiUrl, apiToken: apiToken || undefined }; return { apiUrl, apiToken: apiToken || undefined };
} }

View file

@ -245,12 +245,6 @@ async function lazyReinit(configOverride?: PluginConfig): Promise<void> {
try { try {
await checkExternalApiHealth(externalApi.apiUrl, externalApi.apiToken); await checkExternalApiHealth(externalApi.apiUrl, externalApi.apiToken);
// Health check passed — set up env vars and create client
process.env.HINDSIGHT_EMBED_API_URL = externalApi.apiUrl;
if (externalApi.apiToken) {
process.env.HINDSIGHT_EMBED_API_TOKEN = externalApi.apiToken;
}
const llmConfig = detectLLMConfig(config); const llmConfig = detectLLMConfig(config);
clientOptions = buildClientOptions(llmConfig, config, externalApi); clientOptions = buildClientOptions(llmConfig, config, externalApi);
banksWithMissionSet.clear(); banksWithMissionSet.clear();
@ -667,16 +661,8 @@ export function formatMemories(results: MemoryResult[]): string {
} }
// Provider detection from standard env vars // Providers that authenticate via OAuth or run locally — no API key needed.
const PROVIDER_DETECTION = [ const NO_KEY_REQUIRED_PROVIDERS = new Set(['ollama', 'openai-codex', 'claude-code']);
{ name: 'openai', keyEnv: 'OPENAI_API_KEY' },
{ name: 'anthropic', keyEnv: 'ANTHROPIC_API_KEY' },
{ name: 'gemini', keyEnv: 'GEMINI_API_KEY' },
{ name: 'groq', keyEnv: 'GROQ_API_KEY' },
{ name: 'ollama', keyEnv: '' },
{ name: 'openai-codex', keyEnv: '' },
{ name: 'claude-code', keyEnv: '' },
];
export function detectLLMConfig(pluginConfig?: PluginConfig): { export function detectLLMConfig(pluginConfig?: PluginConfig): {
provider?: string; provider?: string;
@ -685,88 +671,7 @@ export function detectLLMConfig(pluginConfig?: PluginConfig): {
baseUrl?: string; baseUrl?: string;
source: string; source: string;
} { } {
// Override values from HINDSIGHT_API_LLM_* env vars (highest priority) // External API mode: the daemon handles LLM credentials, plugin doesn't need them.
const overrideProvider = process.env.HINDSIGHT_API_LLM_PROVIDER;
const overrideModel = process.env.HINDSIGHT_API_LLM_MODEL;
const overrideKey = process.env.HINDSIGHT_API_LLM_API_KEY;
const overrideBaseUrl = process.env.HINDSIGHT_API_LLM_BASE_URL;
// Priority 1: If provider is explicitly set via env var, use that
if (overrideProvider) {
// Providers that don't require an API key (use OAuth or local models)
const noKeyRequired = ['ollama', 'openai-codex', 'claude-code'];
if (!overrideKey && !noKeyRequired.includes(overrideProvider)) {
throw new Error(
`HINDSIGHT_API_LLM_PROVIDER is set to "${overrideProvider}" but HINDSIGHT_API_LLM_API_KEY is not set.\n` +
`Please set: export HINDSIGHT_API_LLM_API_KEY=your-api-key`
);
}
return {
provider: overrideProvider,
apiKey: overrideKey || '',
model: overrideModel,
baseUrl: overrideBaseUrl,
source: 'HINDSIGHT_API_LLM_PROVIDER override',
};
}
// Priority 2: Plugin config llmProvider/llmModel
if (pluginConfig?.llmProvider) {
const providerInfo = PROVIDER_DETECTION.find(p => p.name === pluginConfig.llmProvider);
// Resolve API key: llmApiKeyEnv > provider's standard keyEnv
let apiKey = '';
if (pluginConfig.llmApiKeyEnv) {
apiKey = process.env[pluginConfig.llmApiKeyEnv] || '';
} else if (providerInfo?.keyEnv) {
apiKey = process.env[providerInfo.keyEnv] || '';
}
// Providers that don't require an API key (use OAuth or local models)
const noKeyRequired = ['ollama', 'openai-codex', 'claude-code'];
if (!apiKey && !noKeyRequired.includes(pluginConfig.llmProvider)) {
const keySource = pluginConfig.llmApiKeyEnv || providerInfo?.keyEnv || 'unknown';
throw new Error(
`Plugin config llmProvider is set to "${pluginConfig.llmProvider}" but no API key found.\n` +
`Expected env var: ${keySource}\n` +
`Set the env var or use llmApiKeyEnv in plugin config to specify a custom env var name.`
);
}
return {
provider: pluginConfig.llmProvider,
apiKey,
model: pluginConfig.llmModel || overrideModel,
baseUrl: overrideBaseUrl,
source: 'plugin config',
};
}
// Priority 3: Auto-detect from standard provider env vars
for (const providerInfo of PROVIDER_DETECTION) {
const apiKey = providerInfo.keyEnv ? process.env[providerInfo.keyEnv] : '';
// Skip providers that don't use API keys in auto-detection (must be explicitly requested)
const noKeyRequired = ['ollama', 'openai-codex', 'claude-code'];
if (noKeyRequired.includes(providerInfo.name)) {
continue;
}
if (apiKey) {
return {
provider: providerInfo.name,
apiKey,
model: overrideModel,
baseUrl: overrideBaseUrl,
source: `auto-detected from ${providerInfo.keyEnv}`,
};
}
}
// No configuration found - show helpful error
// Allow empty LLM config if using external Hindsight API (server handles LLM)
const externalApiCheck = detectExternalApi(pluginConfig); const externalApiCheck = detectExternalApi(pluginConfig);
if (externalApiCheck.apiUrl) { if (externalApiCheck.apiUrl) {
return { return {
@ -778,37 +683,51 @@ export function detectLLMConfig(pluginConfig?: PluginConfig): {
}; };
} }
const provider = pluginConfig?.llmProvider;
if (!provider) {
throw new Error( throw new Error(
`No LLM configuration found for Hindsight memory plugin.\n\n` + `No LLM provider configured for the Hindsight memory plugin.\n\n` +
`Option 1: Set a standard provider API key (auto-detect):\n` + `Set the provider via 'openclaw config set':\n` +
` export OPENAI_API_KEY=sk-your-key\n` + ` openclaw config set plugins.entries.hindsight-openclaw.config.llmProvider openai\n\n` +
` export ANTHROPIC_API_KEY=your-key\n` + `For providers that need an API key, configure it as a SecretRef so the value\n` +
` export GEMINI_API_KEY=your-key\n` + `is read from an env var (or file/exec source) at runtime instead of stored in plain text:\n` +
` export GROQ_API_KEY=your-key\n\n` + ` openclaw config set plugins.entries.hindsight-openclaw.config.llmApiKey \\\n` +
`Option 2: Use Codex or Claude Code (no API key needed):\n` + ` --ref-source env --ref-provider default --ref-id OPENAI_API_KEY\n\n` +
` export HINDSIGHT_API_LLM_PROVIDER=openai-codex # Requires 'codex auth login'\n` + `Providers that don't need an API key: ${[...NO_KEY_REQUIRED_PROVIDERS].join(', ')}.\n` +
` export HINDSIGHT_API_LLM_PROVIDER=claude-code # Requires Claude Code CLI\n\n` + `Or point the plugin at an external Hindsight API by setting hindsightApiUrl instead.`
`Option 3: Set llmProvider in openclaw.json plugin config:\n` +
` "llmProvider": "openai"\n\n` +
`Option 4: Override with Hindsight-specific env vars:\n` +
` export HINDSIGHT_API_LLM_PROVIDER=openai\n` +
` export HINDSIGHT_API_LLM_API_KEY=sk-your-key\n` +
` export HINDSIGHT_API_LLM_BASE_URL=https://openrouter.ai/api/v1 # Optional\n\n` +
`The model will be selected automatically by Hindsight. To override: export HINDSIGHT_API_LLM_MODEL=your-model`
); );
} }
const apiKey = pluginConfig?.llmApiKey ?? '';
if (!apiKey && !NO_KEY_REQUIRED_PROVIDERS.has(provider)) {
throw new Error(
`llmProvider is set to "${provider}" but llmApiKey is empty.\n\n` +
`Configure it via 'openclaw config set' as a SecretRef:\n` +
` openclaw config set plugins.entries.hindsight-openclaw.config.llmApiKey \\\n` +
` --ref-source env --ref-provider default --ref-id OPENAI_API_KEY`
);
}
return {
provider,
apiKey,
model: pluginConfig?.llmModel,
baseUrl: pluginConfig?.llmBaseUrl,
source: 'plugin config',
};
}
/** /**
* Detect external Hindsight API configuration. * Detect external Hindsight API configuration from plugin config.
* Priority: env vars > plugin config
*/ */
export function detectExternalApi(pluginConfig?: PluginConfig): { export function detectExternalApi(pluginConfig?: PluginConfig): {
apiUrl: string | null; apiUrl: string | null;
apiToken: string | null; apiToken: string | null;
} { } {
const apiUrl = process.env.HINDSIGHT_EMBED_API_URL || pluginConfig?.hindsightApiUrl || null; return {
const apiToken = process.env.HINDSIGHT_EMBED_API_TOKEN || pluginConfig?.hindsightApiToken || null; apiUrl: pluginConfig?.hindsightApiUrl ?? null,
return { apiUrl, apiToken }; apiToken: pluginConfig?.hindsightApiToken ?? null,
};
} }
/** /**
@ -867,9 +786,6 @@ async function checkExternalApiHealth(apiUrl: string, apiToken?: string | null):
function getPluginConfig(api: MoltbotPluginAPI): PluginConfig { function getPluginConfig(api: MoltbotPluginAPI): PluginConfig {
const config = api.config.plugins?.entries?.['hindsight-openclaw']?.config || {}; const config = api.config.plugins?.entries?.['hindsight-openclaw']?.config || {};
const defaultMission = 'You are an AI assistant helping users across multiple communication channels (Telegram, Slack, Discord, etc.). Remember user preferences, instructions, and important context from conversations to provide personalized assistance.'; const defaultMission = 'You are an AI assistant helping users across multiple communication channels (Telegram, Slack, Discord, etc.). Remember user preferences, instructions, and important context from conversations to provide personalized assistance.';
const envBankId = typeof process.env.HINDSIGHT_BANK_ID === 'string' && process.env.HINDSIGHT_BANK_ID.trim().length > 0
? process.env.HINDSIGHT_BANK_ID.trim()
: undefined;
return { return {
bankMission: config.bankMission || defaultMission, bankMission: config.bankMission || defaultMission,
@ -879,13 +795,14 @@ function getPluginConfig(api: MoltbotPluginAPI): PluginConfig {
embedPackagePath: config.embedPackagePath, embedPackagePath: config.embedPackagePath,
llmProvider: config.llmProvider, llmProvider: config.llmProvider,
llmModel: config.llmModel, llmModel: config.llmModel,
llmApiKeyEnv: config.llmApiKeyEnv, llmApiKey: config.llmApiKey,
llmBaseUrl: config.llmBaseUrl,
hindsightApiUrl: config.hindsightApiUrl, hindsightApiUrl: config.hindsightApiUrl,
hindsightApiToken: config.hindsightApiToken, hindsightApiToken: config.hindsightApiToken,
apiPort: config.apiPort || 9077, apiPort: config.apiPort || 9077,
// Dynamic bank ID options (default: enabled) // Dynamic bank ID options (default: enabled)
dynamicBankId: config.dynamicBankId !== false, dynamicBankId: config.dynamicBankId !== false,
bankId: envBankId || (typeof config.bankId === 'string' && config.bankId.trim().length > 0 ? config.bankId.trim() : undefined), bankId: typeof config.bankId === 'string' && config.bankId.trim().length > 0 ? config.bankId.trim() : undefined,
bankIdPrefix: config.bankIdPrefix, bankIdPrefix: config.bankIdPrefix,
retainTags: Array.isArray(config.retainTags) ? config.retainTags.filter((tag): tag is string => typeof tag === 'string') : undefined, retainTags: Array.isArray(config.retainTags) ? config.retainTags.filter((tag): tag is string => typeof tag === 'string') : undefined,
retainSource: typeof config.retainSource === 'string' && config.retainSource.trim().length > 0 ? config.retainSource.trim() : undefined, retainSource: typeof config.retainSource === 'string' && config.retainSource.trim().length > 0 ? config.retainSource.trim() : undefined,
@ -1013,10 +930,7 @@ export default function (api: MoltbotPluginAPI) {
log.warn(`could not initialize retain queue: ${error}`); log.warn(`could not initialize retain queue: ${error}`);
} }
// Set env vars so CLI commands (uvx hindsight-embed) use external API
process.env.HINDSIGHT_EMBED_API_URL = externalApi.apiUrl;
if (externalApi.apiToken) { if (externalApi.apiToken) {
process.env.HINDSIGHT_EMBED_API_TOKEN = externalApi.apiToken;
debug('[Hindsight] API token configured'); debug('[Hindsight] API token configured');
} }
} else { } else {
@ -1176,10 +1090,6 @@ export default function (api: MoltbotPluginAPI) {
if (externalApi.apiUrl) { if (externalApi.apiUrl) {
// External API mode // External API mode
usingExternalApi = true; usingExternalApi = true;
process.env.HINDSIGHT_EMBED_API_URL = externalApi.apiUrl;
if (externalApi.apiToken) {
process.env.HINDSIGHT_EMBED_API_TOKEN = externalApi.apiToken;
}
await checkExternalApiHealth(externalApi.apiUrl, externalApi.apiToken); await checkExternalApiHealth(externalApi.apiUrl, externalApi.apiToken);

View file

@ -55,14 +55,15 @@ export interface PluginConfig {
daemonIdleTimeout?: number; // Seconds before daemon shuts down (0 = never) daemonIdleTimeout?: number; // Seconds before daemon shuts down (0 = never)
embedVersion?: string; // hindsight-embed version (default: "latest") embedVersion?: string; // hindsight-embed version (default: "latest")
embedPackagePath?: string; // Local path to hindsight package (e.g. '/path/to/hindsight') embedPackagePath?: string; // Local path to hindsight package (e.g. '/path/to/hindsight')
llmProvider?: string; // LLM provider override (e.g. 'openai', 'anthropic', 'gemini', 'groq', 'ollama') llmProvider?: string; // LLM provider (e.g. 'openai', 'anthropic', 'gemini', 'groq', 'ollama', 'openai-codex', 'claude-code')
llmModel?: string; // LLM model override (e.g. 'gpt-4o-mini', 'claude-3-5-haiku-20241022') llmModel?: string; // LLM model (e.g. 'gpt-4o-mini', 'claude-3-5-haiku-20241022')
llmApiKeyEnv?: string; // Env var name holding the API key (e.g. 'MY_CUSTOM_KEY') llmApiKey?: string; // LLM provider API key. Configure via SecretRef: openclaw config set ... --ref-source env --ref-id OPENAI_API_KEY
llmBaseUrl?: string; // Optional base URL override for OpenAI-compatible providers (e.g. OpenRouter)
apiPort?: number; // Port for openclaw profile daemon (default: 9077) apiPort?: number; // Port for openclaw profile daemon (default: 9077)
hindsightApiUrl?: string; // External Hindsight API URL (skips local daemon when set) hindsightApiUrl?: string; // External Hindsight API URL (skips local daemon when set)
hindsightApiToken?: string; // API token for external Hindsight API authentication hindsightApiToken?: string; // API token for external Hindsight API. Configure via SecretRef.
dynamicBankId?: boolean; // Enable per-channel memory banks (default: true) dynamicBankId?: boolean; // Enable per-channel memory banks (default: true)
bankId?: string; // Static bank ID used when dynamicBankId is false. Can also be set via HINDSIGHT_BANK_ID. bankId?: string; // Static bank ID used when dynamicBankId is false.
bankIdPrefix?: string; // Prefix for bank IDs (e.g. 'prod' -> 'prod-slack-C123') bankIdPrefix?: string; // Prefix for bank IDs (e.g. 'prod' -> 'prod-slack-C123')
retainTags?: string[]; // Tags applied to all retained documents (e.g. ['source_system:openclaw', 'agent:agentname']) retainTags?: string[]; // Tags applied to all retained documents (e.g. ['source_system:openclaw', 'agent:agentname'])
retainSource?: string; // Source written into retained document metadata (default: 'openclaw') retainSource?: string; // Source written into retained document metadata (default: 'openclaw')

View file

@ -148,18 +148,15 @@ beforeAll(async () => {
// Reset module registry so we get a fresh module with clean state. // Reset module registry so we get a fresh module with clean state.
vi.resetModules(); vi.resetModules();
// Provide LLM config — used by plugin init even in HTTP mode.
process.env.HINDSIGHT_API_LLM_PROVIDER = 'openai';
process.env.HINDSIGHT_API_LLM_API_KEY = 'test-key-hooks';
// Point the plugin at the running test API.
process.env.HINDSIGHT_EMBED_API_URL = HINDSIGHT_API_URL;
const mod = await import('../src/index.js'); const mod = await import('../src/index.js');
const { HindsightClient } = await import('@vectorize-io/hindsight-client'); const { HindsightClient } = await import('@vectorize-io/hindsight-client');
const pluginFn = mod.default; const pluginFn = mod.default;
const getClient = mod.getClient; const getClient = mod.getClient;
// Plugin runs in external API mode (talks to the running test API), so no LLM
// credentials are needed in the plugin config — the daemon handles them.
const handle = createMockApi({ const handle = createMockApi({
hindsightApiUrl: HINDSIGHT_API_URL,
dynamicBankId: true, dynamicBankId: true,
excludeProviders: ['slack'], excludeProviders: ['slack'],
retainEveryNTurns: 1, // retain every turn so individual tests aren't affected by chunking retainEveryNTurns: 1, // retain every turn so individual tests aren't affected by chunking
@ -188,9 +185,6 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
vi.restoreAllMocks(); vi.restoreAllMocks();
delete process.env.HINDSIGHT_API_LLM_PROVIDER;
delete process.env.HINDSIGHT_API_LLM_API_KEY;
delete process.env.HINDSIGHT_EMBED_API_URL;
if (stopServicesFn) await stopServicesFn().catch(() => {}); if (stopServicesFn) await stopServicesFn().catch(() => {});
}, 15_000); }, 15_000);

View file

@ -8,6 +8,21 @@ import PageHero from '@site/src/components/PageHero';
← OpenClaw integration ← OpenClaw integration
## 0.6.0 (Unreleased)
**Breaking Changes**
- The plugin no longer reads any configuration from process environment variables. All settings — including the LLM provider, model, API key, base URL, external Hindsight API URL/token, and bank ID — must now be set through OpenClaw's plugin config (e.g. `openclaw config set plugins.entries.hindsight-openclaw.config.<field> <value>`). API keys and other secrets should be configured as `SecretRef` values via `--ref-source env|file|exec` so they're resolved from your secret store at runtime instead of being stored in plaintext on disk.
- Removed the `llmApiKeyEnv` plugin config field. Use the new `llmApiKey` field configured as a SecretRef instead (e.g. `openclaw config set plugins.entries.hindsight-openclaw.config.llmApiKey --ref-source env --ref-id OPENAI_API_KEY`).
- Removed automatic LLM provider detection from `OPENAI_API_KEY` / `ANTHROPIC_API_KEY` / `GEMINI_API_KEY` / `GROQ_API_KEY`. Set `llmProvider` and `llmApiKey` explicitly via `openclaw config set`.
- Removed support for the `HINDSIGHT_API_LLM_PROVIDER`, `HINDSIGHT_API_LLM_MODEL`, `HINDSIGHT_API_LLM_API_KEY`, `HINDSIGHT_API_LLM_BASE_URL`, `HINDSIGHT_EMBED_API_URL`, `HINDSIGHT_EMBED_API_TOKEN`, and `HINDSIGHT_BANK_ID` environment variables. The same values now live in plugin config — see the migration guide.
**Features**
- Added the `llmApiKey` plugin config field, marked as a sensitive field so OpenClaw resolves it as a `SecretRef` from env, file, or exec sources.
- Added the `llmBaseUrl` plugin config field for OpenAI-compatible endpoint overrides (OpenRouter, Azure OpenAI, vLLM, etc.).
- Marked `hindsightApiToken` as a sensitive field — it can now be configured as a `SecretRef` the same way as `llmApiKey`.
## [0.5.1](https://github.com/vectorize-io/hindsight/tree/integrations/openclaw/v0.5.1) ## [0.5.1](https://github.com/vectorize-io/hindsight/tree/integrations/openclaw/v0.5.1)
**Bug Fixes** **Bug Fixes**