# @lacneu/hindsight-openclaw

Fork of [`@vectorize-io/hindsight-openclaw`](https://github.com/vectorize-io/hindsight/tree/main/hindsight-integrations/openclaw) with a single behavioral change: **`deriveBankId` resolves the canonical user identity from `sessionKey` when OpenClaw's `session.identityLinks` is configured**.

## Why this fork

OpenClaw supports [`session.identityLinks`](https://docs.openclaw.ai/concepts/session) to unify sessions across channels for the same human user:

```json
{
  "session": {
    "dmScope": "per-channel-peer",
    "identityLinks": {
      "alice": ["telegram:123", "discord:987"]
    }
  }
}
```

Upstream `@vectorize-io/hindsight-openclaw` **does not honor this mapping when computing bank IDs** — it reads `senderId` (the raw channel peer) instead of the canonical identity resolved into `sessionKey`. Result: Alice writing from Telegram and Discord ends up with two separate Hindsight banks, breaking cross-channel memory consolidation.

This fork extracts the canonical identity from the sessionKey's `direct:<canonical>` segment and uses it as the `user` field in `dynamicBankGranularity`. One human = one bank, regardless of channel.

See upstream issues that motivated this fork:

- [openclaw/openclaw#67735](https://github.com/openclaw/openclaw/issues/67735) — WebChat does not honor identityLinks
- [openclaw/openclaw#46652](https://github.com/openclaw/openclaw/issues/46652) — Matrix identityLinks broken
- [openclaw/openclaw#52504](https://github.com/openclaw/openclaw/issues/52504) — Cross-Platform Session Continuity feature request

## Installation

```bash
openclaw plugins install @lacneu/hindsight-openclaw
```

The plugin is a drop-in replacement — it keeps the same `id: "hindsight-openclaw"` so existing configs and slot assignments (`plugins.slots.memory: "hindsight-openclaw"`) continue to work.

## Configuration (typical)

```json
{
  "session": {
    "dmScope": "per-channel-peer",
    "identityLinks": {
      "alice": ["telegram:12345", "openwebui:alice@example.com"]
    }
  },
  "plugins": {
    "entries": {
      "hindsight-openclaw": {
        "enabled": true,
        "config": {
          "bankIdPrefix": "alice",
          "dynamicBankGranularity": ["agent", "user"],
          "hindsightApiUrl": "http://hindsight:8888"
        }
      }
    }
  }
}
```

With `dynamicBankGranularity: ["agent", "user"]` and `identityLinks` configured, all channels linked to `alice` feed the same Hindsight bank.

## TEST mode (no Hindsight backend required)

For test/staging environments where no Hindsight instance is reachable, set `testMode: true`. The plugin then serves a **mocked Hindsight backend** — no daemon, no external API, no LLM credentials — while the rest of the plugin runs for real, so you can observe its actual impact on agent replies.

```json
{
  "plugins": {
    "entries": {
      "hindsight-openclaw": {
        "enabled": true,
        "config": {
          "testMode": true,
          "provenanceReport": "full",
          "testMemories": [
            { "text": "The user prefers concise answers in French.", "score": 0.9 },
            { "text": "The user is building an OpenClaw plugin.", "score": 0.7 }
          ]
        }
      }
    }
  }
}
```

- The mock replaces only the `HindsightClient` boundary (`recall`/`retain`/`createBank`). The recall router, `recallMinRelevance` threshold filter, `recallTopK`, formatting, injection, and provenance all run unchanged against the canned data.
- `testMemories` is optional — a realistic built-in set is used when omitted. Each entry needs `text`; `score` becomes its cross-encoder score (default `0.5`, above the default `recallMinRelevance` of `0.3`).
- `retain()` is a no-op. A one-time `⚠️ TEST MODE ACTIVE` WARN is logged at startup.
- **Langfuse:** the plugin emits nothing to Langfuse itself (by design). The mocked memories flow into the prompt through the normal hook path, so they become part of whatever LLM trace the agent already produces — **if** a LiteLLM ↔ Langfuse callback is active in your environment, the injected memories show up there too. That callback is a deployment prerequisite, not something this plugin sets up. For plugin-side observability of the injection, use `provenanceReport: "full"`, which surfaces the exact injected set (with scores) on the gateway agent-event bus — verified end-to-end in TEST mode.

> ⚠️ **NOT FOR PRODUCTION.** Never ship `testMode: true` to a real deployment — it serves fabricated memories and discards everything the agent tries to retain.

## Backwards compatibility

The patch is purely additive. When `session.identityLinks` is not configured or the sessionKey doesn't contain a `direct:<canonical>` segment, the plugin falls back to the raw `senderId` — behavior is identical to upstream.

## Relation to upstream

Based on `@vectorize-io/hindsight-openclaw` 0.6.5. Starting with 0.7.0 this fork follows an independent semver track (0.7.x, 0.8.x, …); earlier releases used the now-retired `<upstream>-identity.<n>` scheme, which npm/OpenClaw treated as prereleases and required explicit version pinning at install time.

## License

MIT — inherited from upstream Hindsight.
