activeloopai / activeloopai/hivemind

openclaw: auto-capture INSERT misses message_embedding column (parity gap with claude-code/codex)

未關閉
#80 1 則留言 0 個 reaction 已指派 0 人 在 GitHub 檢視
bug
主要語言
TypeScript
星號
1.6k
分支
107
平均合併
17 小時 30 分鐘
30 天內合併 PR
6

描述

## Summary

OpenClaw's auto-capture path writes rows to the `sessions` table **without populating the `message_embedding` column**. Recall against OpenClaw-captured sessions is therefore permanently lexical-only, even when `@huggingface/transformers` is installed and the embedding daemon would otherwise be reachable. This is the same oversight that affected the codex hooks until [`4dd1c32`](https://github.com/activeloopai/hivemind/commit/4dd1c32), and that affected the claude-code capture path until [`bfff7be`](https://github.com/activeloopai/hivemind/commit/bfff7be).

## Where the gap is

`openclaw/src/index.ts:893` — auto-capture INSERT:

```ts
`INSERT INTO "${sessionsTable}" (id, path, filename, message, author, size_bytes, project, description, agent, creation_date, last_update_date) `
```

The column tuple does **not** include `message_embedding` and there is no `EmbedClient` call site anywhere in `openclaw/src/index.ts`. Confirmed by grep: `EmbedClient | embed-daemon | embeddingsDisabled | embeddingSqlLiteral` → 0 hits in the openclaw source. `openclaw/dist/` does not ship `embeddings/embed-daemon.js` either.

The comment one line above (`// Auto-capture: store new messages in sessions table (same format as CC capture.ts)`) makes the intent clear — feature parity with claude-code's capture — but the embedding step never made it across.

## Impact

- OpenClaw users who install `@huggingface/transformers` get **no benefit** from it for capture. Every captured row lands with `message_embedding = NULL`.
- The `sessions` schema migration (ALTER ADD COLUMN) is still triggered on every SessionStart by the shared `DeeplakeApi.ensureSessionsTable()`, so the column exists — it's just never written.
- Hybrid semantic + lexical recall via `searchDeeplakeTables` falls back to lexical-only on OpenClaw-originated rows.

## Plugin matrix today

| Capability | claude-code | codex | openclaw |
|---|---|---|---|
| Embed in capture-path | ✅ | ✅ (4dd1c32) | ❌ this issue |
| `@huggingface/transformers` detection / graceful fallback | ✅ | ✅ | n/a (no embed call) |
| Schema migration (ALTER ADD COLUMN) | ✅ | ✅ | ✅ |
| Fail-fast on 500 "already exists" | ✅ | ✅ | ✅ (shared `DeeplakeApi`) |

## What needs to be done

Mirror of `4dd1c32` against `openclaw/src/index.ts`:

1. **Imports** in `openclaw/src/index.ts` (top of file):
```ts
import { EmbedClient } from "../../src/embeddings/client.js";
import { embeddingSqlLiteral } from "../../src/embeddings/sql.js";
import { embeddingsDisabled } from "../../src/embeddings/disable.js";
import { fileURLToPath } from "node:url";
import { dirname, join } from "node:path";
```
2. **Helper** alongside the existing helpers:
```ts
function resolveEmbedDaemonPath(): string {
return join(dirname(fileURLToPath(import.meta.url)), "embeddings", "embed-daemon.js");
}
```
3. **Embed before INSERT**, immediately above `openclaw/src/index.ts:893`:
```ts
const embedding = embeddingsDisabled()
? null
: await new EmbedClient({ daemonEntry: resolveEmbedDaemonPath() }).embed(line, "document");
const embeddingSql = embeddingSqlLiteral(embedding);
```
4. **Update the INSERT** to list the column and value:
```ts
`INSERT INTO "${sessionsTable}" (id, path, filename, message, message_embedding, author, size_bytes, project, description, agent, creation_date, last_update_date) `
```
…and put `${embeddingSql}` in the corresponding VALUES slot (right after the JSONB message literal, before the author).
5. **`esbuild.config.mjs`**: add the embed-daemon entry to the openclaw build target so the shipped bundle has `embeddings/embed-daemon.js` to spawn (today only claude-code and codex ship it).
6. **Tests**: update `openclaw/tests/auto-recall.test.ts` (or the auto-capture-covering test in that suite) to mock `EmbedClient` the same way `claude-code/tests/codex-capture-hook.test.ts` does after `4dd1c32`:
```ts
vi.mock("../../src/embeddings/client.js", () => ({
EmbedClient: class {
embed(_t: string, _k?: string) { return Promise.resolve(null); }
warmup() { return Promise.resolve(false); }
},
}));
```

## How to verify

After the fix, run the existing real-table scenario harness against the openclaw bundle (the `scenario-matrix.sh` and `scenario-matrix-codex.sh` scripts in the `embedding_generation` worktree are reusable templates — point them at the openclaw bundle's `auto-capture` entry point). Expected behaviour, mirroring claude-code and codex:

- 4 of 7 scenarios (greenfield, half-legacy-memory, fully-migrated, mixed-sess-emb) → `rows=1`, `emb_len=768` with `@huggingface/transformers` reachable, `emb_len=null` without it.
- 3 of 7 scenarios (full-legacy, half-legacy-sessions, mixed-mem-emb) → first capture lost to the Deeplake post-ALTER `vector::at` window (~30s), recovers from the second capture onward. This is a backend bug, not a plugin one (see `repro-vector-at-bug.sh` and `recovery-window-probe.sh` in the worktree).

## Acceptance criteria

- [ ] `grep -c message_embedding openclaw/src/index.ts` returns ≥ 1.
- [ ] `openclaw/dist/embeddings/embed-daemon.js` exists after `npm run build`.
- [ ] OpenClaw-captured rows on a workspace with `@huggingface/transformers` installed land with `ARRAY_LENGTH(message_embedding, 1) = 768`.
- [ ] OpenClaw-captured rows on a workspace **without** `@huggingface/transformers` land with `message_embedding = NULL` and the daemon is never spawned (graceful degradation parity with codex/claude-code).
- [ ] Existing openclaw test suite passes; auto-capture test asserts `message_embedding` is in the INSERT column tuple.

## References

- `bfff7be` — feat(capture): embed message inline before sessions INSERT (claude-code)
- `4dd1c32` — feat(codex): embed message inline before sessions INSERT (codex)
- `6b77d6d` — feat(embeddings): degrade to lexical-only when transformers is missing
- `0aef2df` — refactor(session-start): log specific reason embeddings are off

貢獻指南

這個儲存庫沒有索引到貢獻指南

評估

這個 Issue 還沒有評估資料。

把新 issue 寄到你的電子郵件信箱

精選適合新手參與的 GitHub issue 摘要。