@bcmyguest/openmem 0.0.0-stage → 0.8.0
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.
- package/.agents/skills/openmem/SKILL.md +34 -0
- package/LICENSE +190 -0
- package/README.md +140 -2
- package/bin/hindsight-project-index +530 -0
- package/cli/coding-agent/access-policy.mjs +38 -0
- package/cli/coding-agent/agents.mjs +34 -0
- package/cli/coding-agent/bank-from-cwd.mjs +140 -0
- package/cli/coding-agent/capabilities.mjs +34 -0
- package/cli/coding-agent/context-store.mjs +81 -0
- package/cli/coding-agent/context-trim.mjs +897 -0
- package/cli/coding-agent/file-io.mjs +72 -0
- package/cli/coding-agent/hook-harnesses.mjs +88 -0
- package/cli/coding-agent/hook-runner.mjs +284 -0
- package/cli/coding-agent/install-access.mjs +119 -0
- package/cli/coding-agent/install-context-proxy.mjs +333 -0
- package/cli/coding-agent/install-rtk.mjs +60 -0
- package/cli/coding-agent/install-shim.mjs +201 -0
- package/cli/coding-agent/opencode-config.mjs +107 -0
- package/cli/coding-agent/opencode-plugin.mjs +132 -0
- package/cli/coding-agent/opencode2-plugin/index.js +299 -0
- package/cli/coding-agent/recall-frame.mjs +29 -0
- package/cli/coding-agent/remove-upstream-access.mjs +122 -0
- package/cli/coding-agent/runtime.mjs +40 -0
- package/cli/coding-agent/session-db.mjs +626 -0
- package/cli/coding-agent/session-facade.mjs +86 -0
- package/cli/coding-agent/toml-edit.mjs +161 -0
- package/cli/cognee-mcp.mjs +260 -0
- package/cli/context-management.mjs +254 -0
- package/cli/feed-sync.mjs +27 -0
- package/cli/install-coding-agent.mjs +298 -0
- package/cli/install-memory-mcp.mjs +426 -0
- package/cli/is-main.mjs +18 -0
- package/cli/mcp/context-sql-runner.mjs +58 -0
- package/cli/mcp/context-store.mjs +670 -0
- package/cli/mcp/context.mjs +61 -0
- package/cli/mcp/export/cognee-dataset-name.mjs +12 -0
- package/cli/mcp/export/cognee.mjs +119 -0
- package/cli/mcp/export/hindsight.mjs +48 -0
- package/cli/mcp/export/index.mjs +26 -0
- package/cli/mcp/export/mem0.mjs +31 -0
- package/cli/mcp/knowledge.mjs +333 -0
- package/cli/mcp/llm-synthesize.mjs +50 -0
- package/cli/mcp/mem0-scope-id.mjs +22 -0
- package/cli/mcp/memory-facade.mjs +521 -0
- package/cli/mcp/memory-reflect-consolidate.mjs +120 -0
- package/cli/mcp/notebooklm-banks.mjs +21 -0
- package/cli/mcp/notebooklm-hindsight-rest.mjs +45 -0
- package/cli/mcp/notebooklm-provider.mjs +225 -0
- package/cli/mcp/pmm-records.mjs +23 -0
- package/cli/mcp/portability.mjs +158 -0
- package/cli/mcp/stdio.mjs +65 -0
- package/cli/mcp/unified-mcp-install.mjs +65 -0
- package/cli/mem0-mcp.mjs +108 -0
- package/cli/notebooklm-mcp.mjs +236 -0
- package/cli/openmem-mcp.mjs +38 -0
- package/cli/openmem.mjs +222 -0
- package/cli/roots.mjs +85 -0
- package/cli/schedule.mjs +176 -0
- package/cli/stack.mjs +438 -0
- package/cli/switch-memory-provider.mjs +358 -0
- package/cli/wiring-report.mjs +125 -0
- package/compose.bench.yaml +12 -0
- package/compose.yaml +227 -0
- package/config/coding-agent.json +13 -0
- package/config/hindsight.env.example +26 -0
- package/config/index.yml +54 -0
- package/config/litellm.lemond.yaml +40 -0
- package/config/litellm.ollama.yaml +33 -0
- package/config/notebooklm.yml +33 -0
- package/config/openmem.toml.example +124 -0
- package/docs/OPENMEM.md +23 -0
- package/docs/architecture.md +245 -0
- package/docs/assets/README.md +10 -0
- package/docs/assets/setup-demo.gif +0 -0
- package/docs/coding-agent-hooks.md +75 -0
- package/docs/coding-agent-scopes.md +22 -0
- package/docs/coding-agents.md +71 -0
- package/docs/cognee.md +37 -0
- package/docs/compatibility.md +80 -0
- package/docs/concepts.md +135 -0
- package/docs/context-management.md +255 -0
- package/docs/context-proxy.md +115 -0
- package/docs/context-research.md +215 -0
- package/docs/context-store.md +277 -0
- package/docs/hindsight.md +77 -0
- package/docs/index-sources-roadmap.md +38 -0
- package/docs/index-sources.md +39 -0
- package/docs/indexer-sinks.md +122 -0
- package/docs/indexing.md +57 -0
- package/docs/manual-stack-setup.md +74 -0
- package/docs/mcp-install-guide.md +4 -0
- package/docs/mcp-setup.md +104 -0
- package/docs/mem0.md +141 -0
- package/docs/memory-portability.md +73 -0
- package/docs/memory-providers.md +246 -0
- package/docs/meridian-litellm.md +198 -0
- package/docs/model-configuration.md +106 -0
- package/docs/notebooklm.md +83 -0
- package/docs/npx-install.md +172 -0
- package/docs/openmem-config.md +194 -0
- package/docs/provider-wizard-hooks.md +222 -0
- package/docs/quickstart.md +107 -0
- package/docs/setup-wizard.md +117 -0
- package/docs/sources-feeds.md +39 -0
- package/docs/sources-readwise.md +23 -0
- package/docs/sources-zotero.md +22 -0
- package/glama.json +4 -0
- package/indexer/Dockerfile +38 -0
- package/indexer/code_outline.py +146 -0
- package/indexer/feed_sync.py +107 -0
- package/indexer/index_config.py +290 -0
- package/indexer/pytest.ini +5 -0
- package/indexer/sinks/__init__.py +148 -0
- package/indexer/sinks/_http.py +44 -0
- package/indexer/sinks/cognee.py +329 -0
- package/indexer/sinks/hindsight.py +107 -0
- package/indexer/sinks/mem0.py +247 -0
- package/indexer/watch.py +240 -0
- package/package.json +96 -4
- package/providers/_template/compose.yaml +52 -0
- package/providers/cognee/OPERATOR.md +82 -0
- package/providers/cognee/compose.yaml +89 -0
- package/providers/cognee/lib/clack.mjs +157 -0
- package/providers/cognee/lib/coding-agent.mjs +26 -0
- package/providers/cognee/lib/cognee-mcp-install.mjs +153 -0
- package/providers/cognee/lib/config.mjs +98 -0
- package/providers/cognee/lib/env-file.mjs +194 -0
- package/providers/cognee/lib/env-setup.mjs +83 -0
- package/providers/cognee/lib/llm-env.mjs +382 -0
- package/providers/cognee/lib/ports.mjs +25 -0
- package/providers/cognee/lib/smoke.mjs +246 -0
- package/providers/cognee/lib/stack-env.mjs +134 -0
- package/providers/cognee/lib/state.mjs +101 -0
- package/providers/cognee/lib/summary.mjs +56 -0
- package/providers/cognee/lib/validate.mjs +97 -0
- package/providers/cognee/manifest.json +56 -0
- package/providers/cognee/wizard.mjs +134 -0
- package/providers/graphiti/manifest.json +15 -0
- package/providers/hindsight/lib/clack.mjs +347 -0
- package/providers/hindsight/lib/coding-agent.mjs +204 -0
- package/providers/hindsight/lib/config.mjs +79 -0
- package/providers/hindsight/lib/env-setup.mjs +72 -0
- package/providers/hindsight/lib/litellm-routes.mjs +196 -0
- package/providers/hindsight/lib/meridian.mjs +307 -0
- package/providers/hindsight/lib/model-backend.mjs +243 -0
- package/providers/hindsight/lib/notebooklm-mcp-install.mjs +127 -0
- package/providers/hindsight/lib/ollama-compose.mjs +51 -0
- package/providers/hindsight/lib/ports.mjs +30 -0
- package/providers/hindsight/lib/probe.mjs +54 -0
- package/providers/hindsight/lib/stack-env.mjs +125 -0
- package/providers/hindsight/lib/state.mjs +80 -0
- package/providers/hindsight/lib/summary.mjs +57 -0
- package/providers/hindsight/lib/validate.mjs +81 -0
- package/providers/hindsight/manifest.json +58 -0
- package/providers/hindsight/wizard.mjs +150 -0
- package/providers/letta/manifest.json +29 -0
- package/providers/mem0/OPERATOR.md +143 -0
- package/providers/mem0/compose.yaml +79 -0
- package/providers/mem0/lib/coding-agent.mjs +58 -0
- package/providers/mem0/lib/config.mjs +45 -0
- package/providers/mem0/lib/configure.mjs +201 -0
- package/providers/mem0/lib/dotenv.mjs +71 -0
- package/providers/mem0/lib/env-file.mjs +110 -0
- package/providers/mem0/lib/http.mjs +69 -0
- package/providers/mem0/lib/litellm.mjs +457 -0
- package/providers/mem0/lib/mcp-install.mjs +165 -0
- package/providers/mem0/lib/secret.mjs +30 -0
- package/providers/mem0/lib/smoke.mjs +179 -0
- package/providers/mem0/lib/stack-env.mjs +255 -0
- package/providers/mem0/lib/state.mjs +67 -0
- package/providers/mem0/lib/summary.mjs +88 -0
- package/providers/mem0/lib/url.mjs +106 -0
- package/providers/mem0/lib/validate.mjs +158 -0
- package/providers/mem0/manifest.json +47 -0
- package/providers/mem0/wizard.mjs +146 -0
- package/providers/zep/manifest.json +29 -0
- package/proxy/config.yaml +33 -0
- package/proxy/openmem_context/__init__.py +6 -0
- package/proxy/openmem_context/adapters.py +504 -0
- package/proxy/openmem_context/compress.py +53 -0
- package/proxy/openmem_context/core.py +606 -0
- package/proxy/openmem_context/embed.py +212 -0
- package/proxy/openmem_context/recall.py +49 -0
- package/proxy/openmem_context/session.py +70 -0
- package/proxy/openmem_context/sessiondb.py +276 -0
- package/proxy/openmem_context/settings.py +134 -0
- package/proxy/openmem_context/store.py +129 -0
- package/proxy/openmem_context/tokens.py +123 -0
- package/proxy/openmem_context/ttl.py +78 -0
- package/proxy/openmem_context_hook.py +34 -0
- package/scripts/check-pack.mjs +219 -0
- package/scripts/gen-masks.mjs +83 -0
- package/scripts/gen-masks.sh +4 -0
- package/scripts/promotion-provider-smoke.mjs +34 -0
- package/scripts/run-setup.sh +30 -0
- package/scripts/setup.mjs +107 -0
- package/scripts/setup.sh +5 -0
- package/scripts/spin-up.sh +45 -0
- package/scripts/wizard.sh +5 -0
- package/server.json +34 -0
- package/sync/Dockerfile +27 -0
- package/sync/memory_backend.py +87 -0
- package/sync/notebooklm_sync.py +664 -0
- package/wizard/bin/pm.mjs +97 -0
- package/wizard/bin/setup.mjs +33 -0
- package/wizard/lib/compose-version.mjs +32 -0
- package/wizard/lib/compose.mjs +70 -0
- package/wizard/lib/context-step.mjs +140 -0
- package/wizard/lib/curl.mjs +103 -0
- package/wizard/lib/docker-build.mjs +148 -0
- package/wizard/lib/exec.mjs +41 -0
- package/wizard/lib/indexer.mjs +6 -0
- package/wizard/lib/litellm-env.mjs +55 -0
- package/wizard/lib/load-machine-env.mjs +4 -0
- package/wizard/lib/log.mjs +32 -0
- package/wizard/lib/notebooklm-agent-access.mjs +92 -0
- package/wizard/lib/notebooklm.mjs +214 -0
- package/wizard/lib/parse-args.mjs +87 -0
- package/wizard/lib/paths.mjs +39 -0
- package/wizard/lib/pm-config.mjs +371 -0
- package/wizard/lib/ports.mjs +94 -0
- package/wizard/lib/prompts.mjs +85 -0
- package/wizard/lib/provider-wizard/helpers.mjs +98 -0
- package/wizard/lib/provider-wizard/run-steps.mjs +129 -0
- package/wizard/lib/providers/registry.mjs +262 -0
- package/wizard/lib/run-clack.mjs +108 -0
- package/wizard/lib/run-wizard.mjs +750 -0
- package/wizard/lib/validate-setup.mjs +62 -0
- package/wizard/lib/wizard-action.mjs +34 -0
- package/wizard/lib/wizard-conf.mjs +71 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Portable memory (PMM)
|
|
2
|
+
|
|
3
|
+
OpenMem’s **portability layer** moves memories between providers without baking in a vendor dump format. **PMM** (Personal Memory Manifest) is the first interchange format: export what a backend stores, import into another (or the same) provider, and keep a portable copy for backup or “give me my data” requests.
|
|
4
|
+
|
|
5
|
+
`npx @bcmyguest/openmem switch` rewires the stack only — it does **not** migrate content. Use export → switch → import (or re-index) when you change backends.
|
|
6
|
+
|
|
7
|
+
Status: **`memory_export` / `memory_import`** on the unified MCP are **beta**. PMM v1 is stable; adapters live under `cli/mcp/export/` (orchestrator: `cli/mcp/portability.mjs`). MCP install: [MCP setup](mcp-setup.md).
|
|
8
|
+
|
|
9
|
+
## PMM format (version 1)
|
|
10
|
+
|
|
11
|
+
JSON object, or JSONL with one header line followed by one normalized record per line:
|
|
12
|
+
|
|
13
|
+
| Field | Type | Meaning |
|
|
14
|
+
| --- | --- | --- |
|
|
15
|
+
| `pmm_version` | `1` | Schema version |
|
|
16
|
+
| `exported_at` | ISO-8601 string | When the export was taken |
|
|
17
|
+
| `source_provider` | string | `hindsight` \| `mem0` \| `cognee` |
|
|
18
|
+
| `scope` | object | Provider-specific filter (bank, `user_id`, datasets, …) |
|
|
19
|
+
| `records` | array | Normalized memory records (see below) |
|
|
20
|
+
|
|
21
|
+
Each **record**:
|
|
22
|
+
|
|
23
|
+
| Field | Type | Meaning |
|
|
24
|
+
| --- | --- | --- |
|
|
25
|
+
| `id` | string \| null | Provider id when known |
|
|
26
|
+
| `content` | string | Primary text |
|
|
27
|
+
| `metadata` | object | Optional tags, timestamps, provider payload |
|
|
28
|
+
| `scope_key` | string | Bank name, mem0 `user_id`, Cognee dataset, etc. |
|
|
29
|
+
|
|
30
|
+
Import accepts PMM with `merge_strategy`:
|
|
31
|
+
|
|
32
|
+
| Strategy | Behavior |
|
|
33
|
+
| --- | --- |
|
|
34
|
+
| `skip_duplicates` (default) | For mem0, skip records whose id or content fingerprint already exists in the same bank |
|
|
35
|
+
| `overwrite` | Re-import all records (may duplicate on backends without idempotent upsert) |
|
|
36
|
+
|
|
37
|
+
**Cross-provider import** (e.g. PMM from mem0 into Hindsight) is best-effort: we map `content` + `scope_key` into the active provider’s add API; embeddings and graph structure are **not** preserved.
|
|
38
|
+
|
|
39
|
+
The MCP export currently collects all records in memory before returning them, including with `format: "jsonl"`. Use a bank filter for large stores; the MCP response is not a file stream. Hindsight export reads the memory-list endpoint and does not include knowledge pages, bank configuration, or source documents. Confirm migrated data before switching providers.
|
|
40
|
+
|
|
41
|
+
## Implementation (orchestrator + adapters)
|
|
42
|
+
|
|
43
|
+
openmem stays a **meta orchestrator**: PMM shape and import live in `cli/mcp/portability.mjs`; each backend implements **list/map** in `cli/mcp/export/` (`hindsight.mjs`, `mem0.mjs`, `cognee.mjs`). New providers add an adapter and one branch in `cli/mcp/export/index.mjs` — no changes to the MCP tool names agents call.
|
|
44
|
+
|
|
45
|
+
## Unified MCP tools
|
|
46
|
+
|
|
47
|
+
On the `openmem` MCP server ([MCP setup](mcp-setup.md), [Architecture](architecture.md)):
|
|
48
|
+
|
|
49
|
+
```text
|
|
50
|
+
memory_export { format?: "json"|"jsonl", bank?: string, user_id?: string }
|
|
51
|
+
memory_import { data: string, merge_strategy?: "skip_duplicates"|"overwrite" }
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Example flow (Cursor Agent / any host with the unified server):
|
|
55
|
+
|
|
56
|
+
1. `memory_export` with `format: "json"` and optional `bank`
|
|
57
|
+
2. `npx @bcmyguest/openmem switch <other> --yes`
|
|
58
|
+
3. `memory_import` with the exported JSON and `merge_strategy: "skip_duplicates"`
|
|
59
|
+
|
|
60
|
+
Restart Cursor Agent after `npx @bcmyguest/openmem install-mcp --yes`.
|
|
61
|
+
|
|
62
|
+
## Provider behavior
|
|
63
|
+
|
|
64
|
+
| Provider | Export | Import |
|
|
65
|
+
| --- | --- | --- |
|
|
66
|
+
| **mem0** | `GET /memories` with `user_id` from `bank` arg (API key from env file) | `POST /memories` per record (`infer: false`) |
|
|
67
|
+
| **Hindsight** | `GET /v1/default/banks` then paginated `GET …/memories/list` per bank (`bank` arg = one bank; omit = all banks) | `POST …/banks/{bank}/memories` per record |
|
|
68
|
+
| **Cognee** | Resolve indexer dataset name from `bank`, `GET /api/v1/datasets`, paginate `GET …/data` (label/name as content; not full file bytes) | `POST /api/v1/add` per record when dataset in scope |
|
|
69
|
+
|
|
70
|
+
## Related
|
|
71
|
+
|
|
72
|
+
- [Architecture — provider interface](architecture.md#how-openmem-interfaces-with-providers)
|
|
73
|
+
- [mem0](mem0.md) · [Cognee](cognee.md)
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
# Pluggable memory providers
|
|
2
|
+
|
|
3
|
+
This repo ships **one** supported backend: [Hindsight](https://github.com/vectorize-io/hindsight)
|
|
4
|
+
(API + retain/reflect/consolidate, LiteLLM in front of local models). **mem0** and **Cognee** are
|
|
5
|
+
**experimental** plugins (indexer sink, compose profile, wizard hooks, stub tests). Wave 3 hardened
|
|
6
|
+
operator DX (coding-agent steps, secrets, Ollama wiring); live Cognee cognify+search with a small
|
|
7
|
+
local model is not yet a promotion bar — see `docs/WAVE3-orchestrator.md`. Manifests under
|
|
8
|
+
`providers/` show `experimental` vs `supported`.
|
|
9
|
+
|
|
10
|
+
## Meta setup wizard (what this is — and is not)
|
|
11
|
+
|
|
12
|
+
`npm run setup` (checkout), `npx @bcmyguest/openmem setup`, or `./setup` is a **meta** wizard: it
|
|
13
|
+
orchestrates **machine-local plumbing** that is the same idea across providers (Docker project,
|
|
14
|
+
ports, indexer sources, smoke “is the API alive?”, coding-agent config file, compose up/down). It does
|
|
15
|
+
**not** replace each provider’s **native** settings UI, CLI, or config format.
|
|
16
|
+
|
|
17
|
+
| This wizard owns (cross-cutting) | Provider still owns (native) |
|
|
18
|
+
| --- | --- |
|
|
19
|
+
| Pick provider + stack action (configure / up / down) | API keys, org/project ids, mem0 graph settings |
|
|
20
|
+
| Host ports, `HOST_UID`/`HOST_GID`, compose project name | Hindsight retain/reflect models beyond our LiteLLM routes |
|
|
21
|
+
| Shared `index.yml` (what to index) | Provider-specific memory schema, filters, user ids |
|
|
22
|
+
| Wiring Hindsight → LiteLLM proxy (when provider is Hindsight) | mem0 dashboard, cloud vs self-hosted choice |
|
|
23
|
+
| Health/smoke probes declared in manifest | Official provider docs, upgrades, migrations |
|
|
24
|
+
|
|
25
|
+
When a provider needs extra fields, the manifest should grow **provider-specific wizard steps** (or
|
|
26
|
+
point to “run `mem0 …` / edit `~/.mem0/…`”) — not cram mem0 into Hindsight’s LiteLLM screens.
|
|
27
|
+
|
|
28
|
+
### Similar patterns elsewhere
|
|
29
|
+
|
|
30
|
+
These are the usual analogues people mean by “meta setup”:
|
|
31
|
+
|
|
32
|
+
- **[mise](https://mise.jdx.dev/) / [asdf](https://asdf-vm.com/)** — one CLI to install and select
|
|
33
|
+
runtimes; each plugin wraps the tool’s own install rules. mise does not replace `.npmrc` or
|
|
34
|
+
`Cargo.toml`.
|
|
35
|
+
- **[Chezmoi](https://www.chezmoi.io/)** — applies dotfiles from a repo; Vim, Git, and SSH keep their
|
|
36
|
+
normal config files on disk.
|
|
37
|
+
- **[Supabase CLI](https://supabase.com/docs/guides/cli)** — `supabase start` brings up Postgres,
|
|
38
|
+
Auth, Studio; each service still has its own env and tuning.
|
|
39
|
+
- **LiteLLM (in this stack)** — meta proxy in front of lemond/Ollama; model servers keep their own
|
|
40
|
+
configs. `./setup`’s “model backend” step is really “how LiteLLM reaches your LLM,” not a full
|
|
41
|
+
lemond replacement.
|
|
42
|
+
- **[Home Assistant](https://www.home-assistant.io/)** — core + integrations; Philips Hue still configured
|
|
43
|
+
in the Hue app, HA only links it.
|
|
44
|
+
- **`npx skills`-style installers** — discover and install artifacts; each skill or package owns its
|
|
45
|
+
content afterward.
|
|
46
|
+
|
|
47
|
+
openmem sits in that category: **one front door** for “memory on this machine for coding agents,”
|
|
48
|
+
not a reimplementation of mem0 or Hindsight settings.
|
|
49
|
+
|
|
50
|
+
Declarative config is **[mise-like](openmem-config.md)**: `~/.config/personal-memory/config.toml`
|
|
51
|
+
plus `./pm` tasks — same *shape* as mise (`[tasks]`, merged config files), different domain.
|
|
52
|
+
|
|
53
|
+
Provider-specific setup runs via **[wizard hooks](provider-wizard-hooks.md)** (`providers/*/wizard.mjs`)
|
|
54
|
+
after meta steps — like asdf plugin install scripts, not reimplemented npm/mem0 CLIs.
|
|
55
|
+
|
|
56
|
+
## Layers (what varies per provider)
|
|
57
|
+
|
|
58
|
+
| Layer | Hindsight today | Should become |
|
|
59
|
+
| --- | --- | --- |
|
|
60
|
+
| **Compose stack** | `litellm`, `hindsight`, `indexer`, optional `notebooklm` | Provider manifest lists services / profiles; shared `indexer` where possible |
|
|
61
|
+
| **Model routing** | LiteLLM + `config/litellm.*.yaml` | Hindsight-specific; other providers may use their own LLM config or none |
|
|
62
|
+
| **Indexer sink** | `bin/hindsight-project-index` → REST `/v1/default/banks/{bank}/memories` | Python **sink** interface; one implementation per provider |
|
|
63
|
+
| **Setup wizard** | Node wizard: preflight, env, smoke retain/recall | Provider manifest: health URL, smoke steps, env file names |
|
|
64
|
+
| **Coding-agent UX** | `@vectorize-io/hindsight-coding-agents` + `coding-agent.json` | Per-provider plugin package or generic adapter |
|
|
65
|
+
|
|
66
|
+
NotebookLM sync is **orthogonal** to which provider you picked: enable the `notebooklm` profile and the sync container uses the same `MEMORY_SINK` as the indexer (Hindsight, mem0, or Cognee).
|
|
67
|
+
|
|
68
|
+
Coding-agent session memory uses the **same** in-repo hooks and unified MCP for every provider; on Hindsight stacks `--provider-plugin` swaps the in-repo hooks for the upstream Hindsight plugin (`[agents] integration = "provider"`).
|
|
69
|
+
|
|
70
|
+
## Provider manifest
|
|
71
|
+
|
|
72
|
+
Each provider lives under `providers/<id>/manifest.json`. The wizard loads manifests via
|
|
73
|
+
`wizard/lib/providers/registry.mjs`. Fields are intentionally small; expand only when a second
|
|
74
|
+
provider needs them.
|
|
75
|
+
|
|
76
|
+
- **`status`**: `supported` | `experimental` | `planned`
|
|
77
|
+
- **`compose`**: which services belong to this provider, health check, default host port env var
|
|
78
|
+
- **`indexer`**: env var for API base URL, `sink`, and `sinkDriver` (see the table below)
|
|
79
|
+
- **`codingAgent`**: optional npx package and config template path
|
|
80
|
+
- **`wizard`**: smoke test bank name and HTTP paths (templates may include `{bank}`)
|
|
81
|
+
|
|
82
|
+
| `indexer` field | Meaning | Required |
|
|
83
|
+
| --- | --- | --- |
|
|
84
|
+
| `apiUrlEnv` | Env var that holds the provider's API base URL for the wizard | Per provider |
|
|
85
|
+
| `sink` | The `MEMORY_SINK` value, which equals the `indexer/sinks/<sink>.py` module name. Must match `^[a-z][a-z0-9_]*$` (the same rule as `indexer/sinks/__init__.py`). | `supported` and `experimental`; optional but validated for `planned` |
|
|
86
|
+
| `sinkDriver` | Human-readable wire-dialect label (for example `hindsight-rest-v1`). It names no module and drives no dispatch. | `supported` and `experimental` |
|
|
87
|
+
|
|
88
|
+
`scripts/tests/test_provider_manifests.mjs` checks that every `supported` or `experimental` manifest
|
|
89
|
+
with an `indexer.sink` has a matching `indexer/sinks/<sink>.py`.
|
|
90
|
+
|
|
91
|
+
## Selecting a sink
|
|
92
|
+
|
|
93
|
+
The indexer picks its provider with `--sink NAME`, falling back to the `MEMORY_SINK` environment
|
|
94
|
+
variable, then `hindsight`. Compose fragments for alternate providers set both `MEMORY_SINK` and
|
|
95
|
+
`MEMORY_API_URL` on the `indexer` service from stack `.env` values.
|
|
96
|
+
|
|
97
|
+
| Variable | Used by | Meaning |
|
|
98
|
+
| --- | --- | --- |
|
|
99
|
+
| `MEMORY_SINK` | indexer | Sink module name (`indexer.sink` in the manifest). Default `hindsight`. |
|
|
100
|
+
| `MEMORY_API_URL` | indexer, every non-Hindsight sink | Generic API base URL of the provider. |
|
|
101
|
+
| `HINDSIGHT_URL` | indexer, Hindsight sink only | API base URL for Hindsight (default `http://127.0.0.1:8888`). |
|
|
102
|
+
|
|
103
|
+
`--url` overrides either URL variable. A sink reads its credentials from its own environment
|
|
104
|
+
variables inside `create`, never from the document path.
|
|
105
|
+
|
|
106
|
+
Provider secrets (for example `MEM0_API_KEY`, `COGNEE_API_KEY`, `LLM_API_KEY`) live in
|
|
107
|
+
`~/.config/personal-memory/<id>.env`, outside the repo, mode 600. A provider's compose fragment loads
|
|
108
|
+
that file through an `env_file` entry with `required: false`, so a stack without the file still
|
|
109
|
+
starts. Secrets do not belong in the stack `.env` or in `wizard.conf`, and the wizard never prints
|
|
110
|
+
them.
|
|
111
|
+
|
|
112
|
+
### Optional `flush()`
|
|
113
|
+
|
|
114
|
+
A sink for a provider that ingests asynchronously (`capabilities.async_ingest=True`, for example
|
|
115
|
+
Cognee, where `cognify` builds the graph after `add`) may define `flush(bank: str) -> None`. The
|
|
116
|
+
indexer calls it once per bank after that bank's batch of retains and deletes, so the provider starts
|
|
117
|
+
processing once per batch rather than once per document.
|
|
118
|
+
|
|
119
|
+
- The call happens only with `--apply`. The indexer calls `flush(bank)` with an explicit bank when the
|
|
120
|
+
batch retained or deleted at least one document for that bank, **or** when a previous flush for that
|
|
121
|
+
bank is still pending. A dry run never flushes, and `flush(None)` is never called.
|
|
122
|
+
- A sink must treat `flush(bank)` as "process this bank now", even when its own in-memory bookkeeping
|
|
123
|
+
says the bank is clean: the indexer may call it from a fresh process in which that bookkeeping is gone.
|
|
124
|
+
- Pending flushes are persisted. Per-document progress is saved to the indexer state file as it goes, so
|
|
125
|
+
a failed flush (or a run that died after some retains) would otherwise never be retried. For a sink
|
|
126
|
+
with a `flush`, the bank is recorded under the state key `::flush-pending` (a sorted list of banks)
|
|
127
|
+
before the first write of a batch and removed after `flush` succeeds. Every `--apply` run flushes the
|
|
128
|
+
banks it handles that are still pending, even when nothing changed. A bank whose source is no longer
|
|
129
|
+
indexed is left alone. Sinks without `flush` (Hindsight) never get the key, so their state file is
|
|
130
|
+
unchanged.
|
|
131
|
+
- `flush` is not part of the `Sink` protocol, so sinks without it (Hindsight among them) still satisfy
|
|
132
|
+
`isinstance(sink, Sink)` and see no change in behavior. Callers use `sinks.flush_sink(sink, bank)`,
|
|
133
|
+
which does nothing when the sink has no callable `flush`.
|
|
134
|
+
- A flush error is handled like a retain error: the source is reported as failed, the other sources
|
|
135
|
+
still run, and the pending marker stays so the next run flushes again.
|
|
136
|
+
- The watcher (`indexer/watch.py`) spawns this same CLI once per source, so flush runs there too. In
|
|
137
|
+
the indexer container the sink and URL come from `MEMORY_SINK` and `MEMORY_API_URL`.
|
|
138
|
+
|
|
139
|
+
Default provider id: **`hindsight`**. Change it in **interactive `./setup`** (Clack “Memory provider”,
|
|
140
|
+
defaults to `config.toml` and `wizard.conf`), or set `MEMORY_PROVIDER` for `--yes`. The wizard records
|
|
141
|
+
the choice in `wizard.conf`. Stack **up** / **down** are wizard actions (`--up`, `--down`, or Clack
|
|
142
|
+
first step); default action is full **configure and start**.
|
|
143
|
+
|
|
144
|
+
### Changing provider later
|
|
145
|
+
|
|
146
|
+
Prefer **`npx @bcmyguest/openmem switch <id> --yes`** (checkout: `node cli/openmem.mjs switch <id> --yes`) over a full
|
|
147
|
+
interactive setup re-run when you only need to change backends:
|
|
148
|
+
|
|
149
|
+
- Updates `MEMORY_PROVIDER` in `~/.config/hindsight/wizard.conf`
|
|
150
|
+
- Updates `[memory].provider` in `~/.config/personal-memory/config.toml` when that file exists
|
|
151
|
+
- Unless `--no-stack`: compose down + up for the new provider (with mem0/Cognee readiness checks)
|
|
152
|
+
- Unless `--no-mcp`: runs `install-mcp --provider-only` so Cursor MCP points at the new provider
|
|
153
|
+
|
|
154
|
+
Switching does **not** migrate memories between backends. The NotebookLM compose profile and
|
|
155
|
+
`notebooklm` MCP entry are left alone. Flags and examples:
|
|
156
|
+
[README — Switch memory provider](../README.md#switch-memory-provider).
|
|
157
|
+
|
|
158
|
+
## Suggested implementation phases (separate commits)
|
|
159
|
+
|
|
160
|
+
### Phase A — Registry and docs (this commitset)
|
|
161
|
+
|
|
162
|
+
- Manifests for `hindsight`, `mem0`, and `cognee` (`supported`).
|
|
163
|
+
- Wizard reads manifest for health/smoke URLs; behavior unchanged for Hindsight.
|
|
164
|
+
- Document extension points (this file).
|
|
165
|
+
|
|
166
|
+
### Phase B — Indexer sink interface (implemented)
|
|
167
|
+
|
|
168
|
+
- HTTP retain/delete/health live in `indexer/sinks/`; default sink is `hindsight`.
|
|
169
|
+
- CLI: `--sink <name>` or env `MEMORY_SINK`; non-Hindsight sinks use `MEMORY_API_URL` for the API base.
|
|
170
|
+
- Contract tests: `indexer/tests/sink_contract.py` and `test_sinks.py`. Per-provider bank mapping and compose env: [indexer-sinks.md](indexer-sinks.md).
|
|
171
|
+
|
|
172
|
+
### Phase D — Compose profiles (implemented for Hindsight)
|
|
173
|
+
|
|
174
|
+
- `litellm` and `hindsight` use compose profile `hindsight`; the indexer always starts.
|
|
175
|
+
- `./setup` passes `--profile hindsight` on compose commands; `scripts/setup.mjs` writes `COMPOSE_PROFILES=hindsight` into the stack `.env`.
|
|
176
|
+
- Alternate providers layer `providers/<id>/compose.yaml` with their own profile (`MEMORY_SINK`, `MEMORY_API_URL`, `depends_on`).
|
|
177
|
+
|
|
178
|
+
### Phase C — mem0 and Cognee (Wave 2 + Wave 3 DX)
|
|
179
|
+
|
|
180
|
+
- mem0 is **URL-only** in v1: the indexer talks to a mem0 server the operator already runs, and this
|
|
181
|
+
repo ships no mem0 container yet. A mem0 service built from a pinned upstream ref is a follow-up.
|
|
182
|
+
Operator runbook: `providers/mem0/OPERATOR.md`. Coding-agent: wizard `setupCodingAgent` prints
|
|
183
|
+
SDK/MCP steps (no fake `pluginPackage`); register Cursor MCP with
|
|
184
|
+
`npx @bcmyguest/openmem install-mcp --yes` / `npx @bcmyguest/openmem switch mem0 --yes`.
|
|
185
|
+
- Cognee runs in **docker**: a pinned `cognee/cognee` image on a loopback port with a named volume and
|
|
186
|
+
an optional env file (`~/.config/personal-memory/cognee.env`, mode 600).
|
|
187
|
+
Operator notes: `providers/cognee/OPERATOR.md`. Coding-agent: REST on the loopback port plus a
|
|
188
|
+
read-only stdio MCP (`cli/cognee-mcp.mjs`: `cognee_health` / `cognee_search`) registered by
|
|
189
|
+
`setupCodingAgent` / `npm run install-mcp` when `MEMORY_PROVIDER=cognee`.
|
|
190
|
+
- **Cognee + Ollama:** default endpoints use `host.docker.internal:11434` (host Ollama on the Docker
|
|
191
|
+
bridge). When this stack’s compose profile `ollama` publishes **loopback-only**
|
|
192
|
+
(`127.0.0.1:11434`), that default is refused from the Cognee container. Set
|
|
193
|
+
`PM_COGNEE_OLLAMA_COMPOSE=1` or `MEMORY_OLLAMA_IN_COMPOSE=1` (wizard Clack also detects
|
|
194
|
+
`MEMORY_OLLAMA_IN_COMPOSE=1` in `wizard.conf`) so defaults become `http://ollama:11434` and attach
|
|
195
|
+
Cognee to the same Docker network as the `ollama` service. Cognify still needs a model that can
|
|
196
|
+
emit Cognee’s structured extraction schemas; small local models (for example `gemma4:e2b`) may
|
|
197
|
+
pass HTTP add/cognify start but never finish the graph — treat as an operator/LLM tuning issue,
|
|
198
|
+
not a provider status gate.
|
|
199
|
+
- `hindsight`, `mem0`, and `cognee` manifests are **`supported`**; new backends still land as
|
|
200
|
+
`experimental` until wizard + indexer + smoke are wired.
|
|
201
|
+
- `indexer/sinks/mem0.py` and `indexer/sinks/cognee.py` implementing the sink protocol (Cognee also
|
|
202
|
+
implements the optional `flush()`).
|
|
203
|
+
- Wizard branch: provider select (Clack) before model backend when multiple providers are supported.
|
|
204
|
+
- Coding-agent story: mem0’s own SDK/plugin or a thin local proxy—document limitations.
|
|
205
|
+
|
|
206
|
+
### Phase D — Compose profiles
|
|
207
|
+
|
|
208
|
+
- `docker compose --profile hindsight` vs `--profile mem0` generated or documented from manifests.
|
|
209
|
+
- Avoid duplicating indexer image; only swap `depends_on` and indexer `environment`.
|
|
210
|
+
|
|
211
|
+
## mem0 notes (non-goals for Phase A)
|
|
212
|
+
|
|
213
|
+
mem0’s API and deployment model differ from Hindsight banks and retain/reflect aliases. Expect:
|
|
214
|
+
|
|
215
|
+
- Different **namespace** model instead of `bank` strings.
|
|
216
|
+
- Possibly **no** LiteLLM sidecar in this repo (mem0 cloud or separate compose).
|
|
217
|
+
- Coding-agent integration likely **not** the Hindsight npx package; manifest should name the real install path when known.
|
|
218
|
+
|
|
219
|
+
## Developer deep dives (repo only)
|
|
220
|
+
|
|
221
|
+
These stay under `docs/` and are **not** synced to the Starlight site (see `site/scripts/lib/docs.mjs` —
|
|
222
|
+
only user-facing guides like [Architecture](architecture.md) are listed in `PAGES`).
|
|
223
|
+
|
|
224
|
+
- [Indexer sink drivers](indexer-sinks.md) — `sink` vs `sinkDriver`, env vars, bank/scope mapping for Hindsight, mem0, Cognee
|
|
225
|
+
- [MCP setup](mcp-setup.md) — unified MCP vs `--provider-only` vs NotebookLM MCP
|
|
226
|
+
|
|
227
|
+
## Index sources
|
|
228
|
+
|
|
229
|
+
See [index-sources.md](index-sources.md), [sources-feeds.md](sources-feeds.md), [sources-zotero.md](sources-zotero.md), [sources-readwise.md](sources-readwise.md), and [index-sources-roadmap.md](index-sources-roadmap.md).
|
|
230
|
+
|
|
231
|
+
## Coding-agent session hooks
|
|
232
|
+
|
|
233
|
+
See [coding-agent-hooks.md](coding-agent-hooks.md), [coding-agent-scopes.md](coding-agent-scopes.md), and `npx @bcmyguest/openmem install-coding-agent`.
|
|
234
|
+
|
|
235
|
+
## Site and docs
|
|
236
|
+
|
|
237
|
+
Published pages sync from `docs/*.md` at build time. If you change mermaid in a synced doc (for example `docs/architecture.md`), run `cd site && pnpm render-diagrams` and commit `site/public/diagrams/*.svg`. Then run `SITE_URL=https://bcmyguest.github.io BASE_PATH=/openmem/ pnpm build` in `site/` — the same check as CI and GitHub Pages.
|
|
238
|
+
|
|
239
|
+
## Adding a provider (checklist)
|
|
240
|
+
|
|
241
|
+
1. Add `providers/<id>/manifest.json` with accurate `status`.
|
|
242
|
+
2. Implement indexer sink (Phase B+).
|
|
243
|
+
3. Add compose fragment or profile and document ports/env.
|
|
244
|
+
4. Extend wizard: provider selection, provider-specific steps, smoke test.
|
|
245
|
+
5. Document opt-in / agent config for that provider.
|
|
246
|
+
6. Extend `scripts/tests/test_wizard.sh` or add provider-specific tests.
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
# Meridian route (Claude subscription)
|
|
2
|
+
|
|
3
|
+
Route the three Hindsight aliases through [Meridian](https://github.com/rynfar/meridian), a local proxy that serves a signed-in Claude subscription over the Anthropic Messages and OpenAI Chat Completions APIs. Hindsight still calls `http://litellm:4000/v1`; only the override YAML changes.
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
hindsight ──openai──▶ litellm:4000 ──anthropic/──▶ host.docker.internal:3456 (Meridian) ──Agent SDK──▶ Claude subscription
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
Checked against Meridian 1.87.0 docs and LiteLLM docs on 2026-10-29. Nothing below was run end to end; steps marked **unverified** rest on docs alone.
|
|
10
|
+
|
|
11
|
+
## Prerequisites
|
|
12
|
+
|
|
13
|
+
| Need | Why |
|
|
14
|
+
| --- | --- |
|
|
15
|
+
| Node.js 22+ | [Meridian quick start](https://github.com/rynfar/meridian#quick-start) |
|
|
16
|
+
| Claude Code CLI, signed in (`claude login`) | Meridian runs requests through the Agent SDK with those credentials. Haiku 5.5 needs Claude Code 2.1.293+ ([Haiku 5.5](https://github.com/rynfar/meridian/blob/main/docs/configuration.md#haiku-55)) |
|
|
17
|
+
| Claude Pro or Max plan | Requests count against plan limits |
|
|
18
|
+
| OpenMem stack set up | `node scripts/setup.mjs` has run once |
|
|
19
|
+
|
|
20
|
+
## Run Meridian
|
|
21
|
+
|
|
22
|
+
```sh
|
|
23
|
+
npm install -g @rynfar/meridian
|
|
24
|
+
claude login
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Meridian binds `127.0.0.1:3456` by default. The LiteLLM container reaches the host through `host.docker.internal` (`extra_hosts: host-gateway` in `compose.yaml`), which resolves to the Docker bridge address, not loopback, so a loopback-only Meridian is unreachable from the container on Linux. Bind all interfaces and require a key:
|
|
28
|
+
|
|
29
|
+
| Variable | Value | Source |
|
|
30
|
+
| --- | --- | --- |
|
|
31
|
+
| `MERIDIAN_HOST` | `0.0.0.0` (default `127.0.0.1`) | [Configuration table](https://github.com/rynfar/meridian/blob/main/docs/configuration.md#configuration) |
|
|
32
|
+
| `MERIDIAN_PORT` | `3456` (default) | same |
|
|
33
|
+
| `MERIDIAN_API_KEY` | long random secret. Clients send it as `x-api-key` or `Authorization: Bearer`; `/` and `/health` stay open | [API Key Authentication](https://github.com/rynfar/meridian/blob/main/docs/configuration.md#api-key-authentication) |
|
|
34
|
+
|
|
35
|
+
`0.0.0.0` also exposes port 3456 on every host interface; firewall it to the Docker bridge if the machine sits on a shared network.
|
|
36
|
+
|
|
37
|
+
```sh
|
|
38
|
+
install -d -m 700 ~/.config/meridian
|
|
39
|
+
printf 'MERIDIAN_HOST=0.0.0.0\nMERIDIAN_API_KEY=%s\n' "$(openssl rand -hex 32)" > ~/.config/meridian/meridian.env
|
|
40
|
+
chmod 600 ~/.config/meridian/meridian.env
|
|
41
|
+
set -a; . ~/.config/meridian/meridian.env; set +a
|
|
42
|
+
meridian
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Keep it running
|
|
46
|
+
|
|
47
|
+
Meridian documents a Nix Home Manager user service and a [systemd socket-activation example](https://github.com/rynfar/meridian/blob/main/docs/configuration.md#systemd-socket-activation). For an npm install it ships `bin/meridian-launchd.sh`, a POSIX launcher it documents as usable "as a systemd `ExecStart`" ([Running as a service without drift](https://github.com/rynfar/meridian/blob/main/docs/configuration.md#running-as-a-service-without-drift)); the launcher self-updates at start unless `MERIDIAN_NO_SELF_UPDATE=1`.
|
|
48
|
+
|
|
49
|
+
The unit below is **unverified** (written from those docs, not tested). Replace `ExecStart` with the output of `echo "$(npm root -g)/@rynfar/meridian/bin/meridian-launchd.sh"`, and set `PATH` so the unit finds the same `node` and `npm` as your shell (nvm and volta installs live outside the systemd default `PATH`).
|
|
50
|
+
|
|
51
|
+
```ini
|
|
52
|
+
# ~/.config/systemd/user/meridian.service
|
|
53
|
+
[Unit]
|
|
54
|
+
Description=Meridian proxy (Claude subscription)
|
|
55
|
+
|
|
56
|
+
[Service]
|
|
57
|
+
EnvironmentFile=%h/.config/meridian/meridian.env
|
|
58
|
+
Environment=PATH=/usr/local/bin:/usr/bin:/bin
|
|
59
|
+
ExecStart=/usr/lib/node_modules/@rynfar/meridian/bin/meridian-launchd.sh
|
|
60
|
+
Restart=on-failure
|
|
61
|
+
|
|
62
|
+
[Install]
|
|
63
|
+
WantedBy=default.target
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
```sh
|
|
67
|
+
systemctl --user daemon-reload
|
|
68
|
+
systemctl --user enable --now meridian
|
|
69
|
+
loginctl enable-linger "$USER" # keep it running while logged out
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
**Docker alternative (unverified).** Meridian publishes no image; build it from the repo (`docker build -t meridian .`) and mount `~/.claude` ([Docker, single account](https://github.com/rynfar/meridian/blob/main/docs/deployment.md#single-account)). The image already sets `CLAUDE_PROXY_HOST=0.0.0.0`. Attach it to the stack network with `docker network connect openmem_default meridian` and use `api_base: http://meridian:3456`; nothing is then published to the host. Set `MERIDIAN_HOST_ID` and persist the volumes listed under [Persistence and host identity](https://github.com/rynfar/meridian/blob/main/docs/deployment.md#persistence-and-host-identity).
|
|
73
|
+
|
|
74
|
+
## Provider choice: `anthropic/`
|
|
75
|
+
|
|
76
|
+
| | `anthropic/<model>` → `/v1/messages` | `openai/<model>` → `/v1/chat/completions` |
|
|
77
|
+
| --- | --- | --- |
|
|
78
|
+
| Meridian path | Primary API; Meridian's docs describe LiteLLM on this route (`passthrough` adapter, [Claude Code behind a gateway](https://github.com/rynfar/meridian/blob/main/docs/configuration.md#claude-code-behind-a-gateway)) | Supported; headerless tool loops get a derived session key ([OpenAI-compatible tools](https://github.com/rynfar/meridian/blob/main/docs/agents.md#openai-compatible-tools-open-webui-continue-etc)) |
|
|
79
|
+
| Tool calls (`memory-reflect`) | LiteLLM translates OpenAI `tools` to Anthropic `tool_use` ([function calling](https://docs.litellm.ai/docs/providers/anthropic#functiontool-calling)); Meridian returns calls to the client in passthrough mode ([Passthrough Mode](https://github.com/rynfar/meridian/blob/main/docs/configuration.md#passthrough-mode-and-tool-calling)) | Meridian translates OpenAI tools itself |
|
|
80
|
+
| `reasoning_effort` | LiteLLM maps it to `thinking` (budget 1024/2048/4096, or adaptive + `output_config.effort` on 4.6+ models) ([thinking](https://docs.litellm.ai/docs/providers/anthropic#usage---thinking--reasoning)). Drop it, below | Meridian's handling undocumented |
|
|
81
|
+
| `response_format` | LiteLLM converts it to native `output_format` on models it knows, else to a forced tool call ([structured outputs](https://docs.litellm.ai/docs/providers/anthropic#structured-outputs)). Meridian's handling of `output_format` is **unverified** | Meridian's handling undocumented |
|
|
82
|
+
|
|
83
|
+
Use `anthropic/`: it is the route Meridian documents for LiteLLM and gives the request shape LiteLLM translates most predictably. Fall back to `openai/<model>` with `api_base: http://host.docker.internal:3456/v1` if retain fails on structured output.
|
|
84
|
+
|
|
85
|
+
LiteLLM appends `/v1/messages` to `api_base`, so `api_base` has no `/v1` ([Custom API Base](https://docs.litellm.ai/docs/providers/anthropic#custom-api-base)). LiteLLM sends `api_key` as `x-api-key`, which Meridian accepts.
|
|
86
|
+
|
|
87
|
+
## Thinking and token limits
|
|
88
|
+
|
|
89
|
+
The shipped `enable_thinking: false` is a llama.cpp chat-template flag; Claude ignores it. Remove it.
|
|
90
|
+
|
|
91
|
+
| Setting | Behavior through Meridian |
|
|
92
|
+
| --- | --- |
|
|
93
|
+
| Thinking off | Meridian refuses explicit `thinking: disabled` on Haiku 5.5 with HTTP 400 before generation ([Haiku 5.5](https://github.com/rynfar/meridian/blob/main/docs/configuration.md#haiku-55)). Send no thinking parameter; `additional_drop_params: ["reasoning_effort"]` stops LiteLLM generating one from Hindsight's `reasoning_effort` ([Specify params to drop](https://docs.litellm.ai/docs/completion/drop_params#specify-params-to-drop)) |
|
|
94
|
+
| `HINDSIGHT_API_*_MAX_COMPLETION_TOKENS`, LiteLLM `max_tokens` | Ignored by default: the Agent SDK has no output cap, so Meridian does not bound the answer ([Known limitations](https://github.com/rynfar/meridian/blob/main/docs/configuration.md#known-limitations)). Reflect tool calls are never truncated, and output is unbounded |
|
|
95
|
+
| `MERIDIAN_ENFORCE_MAX_TOKENS=1` | Enforces the cap on thinking plus text. With reflect at 2048, adaptive thinking can consume the budget, and a cap that lands mid tool call fails the request. Leave unset |
|
|
96
|
+
| `temperature`, `top_p` | Meridian's Claude docs do not say whether these reach the model; assume ignored (**unverified**). The README's "does not expose … sampling controls" sentence describes the Antigravity CLI backend, not Claude |
|
|
97
|
+
|
|
98
|
+
## Route file
|
|
99
|
+
|
|
100
|
+
`~/.config/hindsight/litellm.yaml` replaces the backend route entirely. Model ids must appear in Meridian's `GET /v1/models`; `claude-haiku-5-5` is Meridian's `haiku` default and `claude-sonnet-5-5` appears in its client examples ([Pi](https://github.com/rynfar/meridian/blob/main/docs/agents.md#pi)). Reflect gets the larger model, as in the shipped routes.
|
|
101
|
+
|
|
102
|
+
```yaml
|
|
103
|
+
# LiteLLM routes for Hindsight, backend: Meridian on the host (Claude subscription).
|
|
104
|
+
# See docs/meridian-litellm.md. MERIDIAN_API_KEY lives in ~/.config/hindsight/litellm.env.
|
|
105
|
+
model_list:
|
|
106
|
+
- model_name: memory-retain
|
|
107
|
+
litellm_params:
|
|
108
|
+
model: anthropic/claude-haiku-5-5
|
|
109
|
+
api_base: http://host.docker.internal:3456
|
|
110
|
+
api_key: os.environ/MERIDIAN_API_KEY
|
|
111
|
+
max_tokens: 4096
|
|
112
|
+
timeout: 300
|
|
113
|
+
additional_drop_params: ["reasoning_effort"]
|
|
114
|
+
- model_name: memory-reflect
|
|
115
|
+
litellm_params:
|
|
116
|
+
model: anthropic/claude-sonnet-5-5
|
|
117
|
+
api_base: http://host.docker.internal:3456
|
|
118
|
+
api_key: os.environ/MERIDIAN_API_KEY
|
|
119
|
+
max_tokens: 4096
|
|
120
|
+
timeout: 300
|
|
121
|
+
additional_drop_params: ["reasoning_effort"]
|
|
122
|
+
- model_name: memory-consolidate
|
|
123
|
+
litellm_params:
|
|
124
|
+
model: anthropic/claude-haiku-5-5
|
|
125
|
+
api_base: http://host.docker.internal:3456
|
|
126
|
+
api_key: os.environ/MERIDIAN_API_KEY
|
|
127
|
+
max_tokens: 4096
|
|
128
|
+
timeout: 300
|
|
129
|
+
additional_drop_params: ["reasoning_effort"]
|
|
130
|
+
|
|
131
|
+
general_settings:
|
|
132
|
+
master_key: os.environ/LITELLM_MASTER_KEY
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## With the wizard
|
|
136
|
+
|
|
137
|
+
`./setup` step "3. Model backend" option 4, **Claude subscription (Meridian)**, automates [Route file](#route-file) and [Apply](#apply). It does not install or start Meridian; run it first as in [Run Meridian](#run-meridian).
|
|
138
|
+
|
|
139
|
+
| Step | What the wizard does |
|
|
140
|
+
| --- | --- |
|
|
141
|
+
| Ask | Meridian URL from the host (default `http://127.0.0.1:3456`, port from `MERIDIAN_PORT` in `~/.config/meridian/meridian.env`) and an optional `MERIDIAN_API_KEY` (blank reuses the environment, `meridian.env`, or `litellm.env`) |
|
|
142
|
+
| Probe | `GET /health` and `GET /v1/models` (with `x-api-key`) from the host, then `/health` on the `docker0` address. Answering on 127.0.0.1 but not on `docker0` means a loopback-only bind: it warns to set `MERIDIAN_HOST=0.0.0.0`. Without `docker0` it states the requirement |
|
|
143
|
+
| Models | Retain and consolidate: `claude-haiku-5-5`, reflect: `claude-sonnet-5-5`, when listed; otherwise the first listed Haiku and Sonnet ids. Unreachable Meridian: the guide defaults |
|
|
144
|
+
| Write | The route file above as `~/.config/hindsight/litellm.yaml` (an existing one is copied to `litellm.yaml.bak-<timestamp>`), `MERIDIAN_API_KEY` in `litellm.env` (mode 600; `placeholder` when Meridian runs without a key), and `[model] backend = "meridian"` in `wizard.conf` and `config.toml` |
|
|
145
|
+
|
|
146
|
+
Non-interactive: `PM_BACKEND=meridian MERIDIAN_BASE=http://127.0.0.1:3456 MERIDIAN_API_KEY=... ./setup --yes`. `[model.meridian] host` in `config.toml` sets `MERIDIAN_BASE`; `PM_DEFAULT_MODEL` and `PM_REFLECT_MODEL` pick models. `--dry-run` probes and prints the plan without writing. Then run the [Smoke test](#smoke-test); the wizard's host-side probe does not prove the container reaches Meridian through a firewall.
|
|
147
|
+
|
|
148
|
+
## Apply
|
|
149
|
+
|
|
150
|
+
```sh
|
|
151
|
+
cd ~/personal-memory # this checkout
|
|
152
|
+
[ -f ~/.config/hindsight/litellm.yaml ] && cp ~/.config/hindsight/litellm.yaml ~/.config/hindsight/litellm.yaml.bak-$(date +%Y%m%d%H%M%S)
|
|
153
|
+
$EDITOR ~/.config/hindsight/litellm.yaml # paste the route file
|
|
154
|
+
grep -h '^MERIDIAN_API_KEY' ~/.config/meridian/meridian.env >> ~/.config/hindsight/litellm.env
|
|
155
|
+
node scripts/setup.mjs
|
|
156
|
+
docker compose up -d --force-recreate litellm
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
`--force-recreate` matters: Compose does not see edits inside the mounted YAML, so a plain `up -d` can keep the old routes. Revert by moving the override aside and rerunning the last two commands.
|
|
160
|
+
|
|
161
|
+
## Smoke test
|
|
162
|
+
|
|
163
|
+
LiteLLM publishes no host port; run curl on the stack network (`openmem_default`, from `name: openmem`).
|
|
164
|
+
|
|
165
|
+
```sh
|
|
166
|
+
set -a; . ~/.config/hindsight/litellm.env; set +a
|
|
167
|
+
net="--network openmem_default --add-host host.docker.internal:host-gateway"
|
|
168
|
+
|
|
169
|
+
# 1. Meridian reachable from a container, models listed
|
|
170
|
+
docker run --rm $net curlimages/curl -s http://host.docker.internal:3456/health
|
|
171
|
+
docker run --rm $net curlimages/curl -s -H "x-api-key: $MERIDIAN_API_KEY" http://host.docker.internal:3456/v1/models
|
|
172
|
+
|
|
173
|
+
# 2. Alias answers through LiteLLM
|
|
174
|
+
docker run --rm $net curlimages/curl -s http://litellm:4000/v1/chat/completions \
|
|
175
|
+
-H "Authorization: Bearer $LITELLM_MASTER_KEY" -H 'Content-Type: application/json' \
|
|
176
|
+
-d '{"model":"memory-retain","messages":[{"role":"user","content":"Reply with OK."}]}'
|
|
177
|
+
|
|
178
|
+
# 3. Reflect returns a tool call (expect finish_reason "tool_calls")
|
|
179
|
+
docker run --rm $net curlimages/curl -s http://litellm:4000/v1/chat/completions \
|
|
180
|
+
-H "Authorization: Bearer $LITELLM_MASTER_KEY" -H 'Content-Type: application/json' \
|
|
181
|
+
-d '{"model":"memory-reflect","reasoning_effort":"low","messages":[{"role":"user","content":"What is the weather in Paris? Use the tool."}],
|
|
182
|
+
"tools":[{"type":"function","function":{"name":"get_weather","description":"Weather for a city",
|
|
183
|
+
"parameters":{"type":"object","properties":{"city":{"type":"string"}},"required":["city"]}}}]}'
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Then run a smoke retain and reflect through Hindsight, as for any route change ([Model configuration](model-configuration.md#verify-and-migrate)).
|
|
187
|
+
|
|
188
|
+
## Caveats
|
|
189
|
+
|
|
190
|
+
| Topic | Detail |
|
|
191
|
+
| --- | --- |
|
|
192
|
+
| Terms | Subscription use through third-party proxies is governed by Anthropic's [Consumer Terms](https://www.anthropic.com/legal/consumer-terms); see also Claude Code's [Authentication and credential use](https://code.claude.com/docs/en/legal-and-compliance#authentication-and-credential-use) |
|
|
193
|
+
| Third-party detection | Meridian reports Claude refusing Pi traffic as third-party ("Third-party apps now draw from your extra usage, not your plan limits") until Pi's prompt fingerprint is scrubbed ([Pi](https://github.com/rynfar/meridian/blob/main/docs/agents.md#pi)). Whether Hindsight's prompts trigger the same is **unverified**; watch Meridian's `/telemetry` |
|
|
194
|
+
| Plan limits | Retain runs on every ingest; indexing a large repo spends plan quota. `GET /v1/usage/quota` shows the windows |
|
|
195
|
+
| Concurrency | Meridian serializes requests that share a session identity and can return `400 This session advanced while the request was waiting` ([Concurrent requests](https://github.com/rynfar/meridian/blob/main/docs/configuration.md#concurrent-requests-to-the-same-session)). Keep `HINDSIGHT_API_LLM_MAX_CONCURRENT=1` (shipped default) |
|
|
196
|
+
| System prompt | Meridian can prepend Claude Code's own system prompt per adapter (SDK feature toggles at `/settings`). An [adapter instance](https://github.com/rynfar/meridian/blob/main/docs/agents.md#adapter-instances) matching `userAgentPrefix: "litellm/"` can turn it off; effect on Hindsight output is **unverified** |
|
|
197
|
+
| Exposure | `MERIDIAN_HOST=0.0.0.0` without `MERIDIAN_API_KEY` gives anyone on the network your subscription |
|
|
198
|
+
| Server tools | Anthropic server tools (`web_search_*`, `web_fetch_*`) return 400 through Meridian; Hindsight does not use them |
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# Model configuration
|
|
2
|
+
|
|
3
|
+
Hindsight never names a model server. It calls the LiteLLM proxy at `http://litellm:4000/v1` with three fixed aliases, and the proxy's YAML maps each alias to a model:
|
|
4
|
+
|
|
5
|
+
| Alias | Hindsight operation |
|
|
6
|
+
| --- | --- |
|
|
7
|
+
| `memory-retain` | retain (extraction on ingest) |
|
|
8
|
+
| `memory-reflect` | reflect, the base `HINDSIGHT_API_LLM_MODEL`, and mental-model refresh. Needs a model that makes tool calls |
|
|
9
|
+
| `memory-consolidate` | consolidation |
|
|
10
|
+
|
|
11
|
+
## Shipped routes
|
|
12
|
+
|
|
13
|
+
Edit the YAML (or a per-machine override) to change models.
|
|
14
|
+
|
|
15
|
+
| File | Backend | Default split |
|
|
16
|
+
| --- | --- | --- |
|
|
17
|
+
| `config/litellm.lemond.yaml` | lemond on the host | Reflect: Gemma 4 E4B GGUF (`unsloth/`); retain and consolidate: E2B |
|
|
18
|
+
| `config/litellm.ollama.yaml` | Ollama on the host | Used when `MEMORY_BACKEND=ollama`; `gemma4:e4b` needs Ollama newer than 0.20.3 |
|
|
19
|
+
| `~/.config/hindsight/litellm.yaml` (written by `./setup`, backend `meridian`) | Meridian on the host (Claude subscription) | Retain and consolidate: `claude-haiku-5-5`; reflect: `claude-sonnet-5-5`. See [Meridian route](meridian-litellm.md#with-the-wizard) |
|
|
20
|
+
|
|
21
|
+
**Why reflect uses a larger model:** E2B often returns no usable tool call on Hindsight reflect (`finish_reason=length`). The shipped lemond route uses E4B for `memory-reflect` only. Qwen3.5-4B works as an alternative if you change the route; pick any model that passes a direct tool-call check and a smoke reflect.
|
|
22
|
+
|
|
23
|
+
## Route selection
|
|
24
|
+
|
|
25
|
+
| Priority | Path |
|
|
26
|
+
| --- | --- |
|
|
27
|
+
| 1 | `~/.config/hindsight/litellm.yaml` — full override, mounted read-only into the proxy |
|
|
28
|
+
| 2 | `config/litellm.$MEMORY_BACKEND.yaml` — `lemond` (default) or `ollama` |
|
|
29
|
+
|
|
30
|
+
`scripts/setup.mjs` writes `LITELLM_CONFIG` in the stack `.env`. After changing backend, override, or routes:
|
|
31
|
+
|
|
32
|
+
```sh
|
|
33
|
+
node scripts/setup.mjs
|
|
34
|
+
docker compose up -d litellm
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Optional Ollama backend:
|
|
38
|
+
|
|
39
|
+
```sh
|
|
40
|
+
MEMORY_BACKEND=ollama node scripts/setup.mjs
|
|
41
|
+
docker compose up -d litellm
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Thinking and token limits
|
|
45
|
+
|
|
46
|
+
Thinking is off on all three aliases in the route YAML (`enable_thinking: false` / `think: false`) so replies are not empty when Hindsight sends low `reasoning_effort`.
|
|
47
|
+
|
|
48
|
+
| Layer | Role |
|
|
49
|
+
| --- | --- |
|
|
50
|
+
| LiteLLM `litellm_params.max_tokens` | Default when the client omits `max_tokens` (4096 in shipped routes) |
|
|
51
|
+
| `HINDSIGHT_API_*_MAX_COMPLETION_TOKENS` in `hindsight.env` | Hard cap Hindsight enforces (retain 16384; reflect and consolidation 2048) |
|
|
52
|
+
|
|
53
|
+
Retain cap must exceed each retain strategy's chunk size (coding-agent strategies use 12000) or Hindsight returns `400 Invalid retain strategy`.
|
|
54
|
+
|
|
55
|
+
## Retain extraction mode
|
|
56
|
+
|
|
57
|
+
OpenMem’s shipped `config/hindsight.env.example` sets:
|
|
58
|
+
|
|
59
|
+
```env
|
|
60
|
+
HINDSIGHT_API_RETAIN_EXTRACTION_MODE=verbatim
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**verbatim** keeps each chunk’s text as the memory unit and still calls the retain LLM for entities, time, and location (cheaper than default `concise` fact rewriting). Alternatives: `concise` / `verbose` (LLM facts), `chunks` (no LLM on ingest; pass entities yourself if needed), or `HINDSIGHT_API_LLM_PROVIDER=none` (chunks-only; reflect disabled). Per-bank strategies can override this (the indexer already uses `chunks` for `project-index`).
|
|
64
|
+
|
|
65
|
+
## Secrets
|
|
66
|
+
|
|
67
|
+
`~/.config/hindsight/litellm.env` (mode 600): `LITELLM_MASTER_KEY` (Hindsight uses this as `HINDSIGHT_API_LLM_API_KEY`), plus provider keys referenced as `os.environ/NAME` in the YAML.
|
|
68
|
+
|
|
69
|
+
Example hosted reflect model:
|
|
70
|
+
|
|
71
|
+
```yaml
|
|
72
|
+
- model_name: memory-reflect
|
|
73
|
+
litellm_params:
|
|
74
|
+
model: anthropic/claude-haiku-4-5
|
|
75
|
+
api_key: os.environ/ANTHROPIC_API_KEY
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Hindsight always sends the alias names above; you do not edit model ids in `hindsight.env` for route changes.
|
|
79
|
+
|
|
80
|
+
## mem0 through the same LiteLLM
|
|
81
|
+
|
|
82
|
+
The `litellm` service carries its own compose profile `litellm` next to `hindsight` and `notebooklm`, so Hindsight's stack renders and starts as before. mem0's wizard can enable that profile: LiteLLM then runs without Hindsight, published on `127.0.0.1:${MEM0_LITELLM_PORT:-4000}` only, from `~/.config/personal-memory/mem0-litellm.yaml`, with the same `litellm.env` and master key.
|
|
83
|
+
|
|
84
|
+
| Alias | mem0 use |
|
|
85
|
+
| --- | --- |
|
|
86
|
+
| `memory-llm` | mem0's LLM (fact extraction and memory updates) |
|
|
87
|
+
| `memory-embed` | mem0's embedder, only for backends with an embedding model (Ollama `nomic-embed-text`, an OpenAI-compatible server) |
|
|
88
|
+
|
|
89
|
+
Claude (Anthropic API key or Meridian) has no embedding model; with those backends mem0 keeps its own embedder. The mem0 config keys and their sources are in [mem0 — Models through the stack LiteLLM](mem0.md#models-through-the-stack-litellm-optional).
|
|
90
|
+
|
|
91
|
+
## Verify and migrate
|
|
92
|
+
|
|
93
|
+
Health check from the compose network:
|
|
94
|
+
|
|
95
|
+
```sh
|
|
96
|
+
docker compose exec litellm python3 -c "import urllib.request;print(urllib.request.urlopen('http://127.0.0.1:4000/health/liveliness').read())"
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Older `hindsight.env` files that pointed at lemond/Ollama directly:
|
|
100
|
+
|
|
101
|
+
```sh
|
|
102
|
+
node scripts/setup.mjs --migrate-env
|
|
103
|
+
docker compose up -d
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Migration rewrites LLM lines to use `http://litellm:4000/v1` and the three aliases; other lines stay as they are.
|