@bcmyguest/openmem 0.6.3 → 0.9.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/{skills → .agents/skills}/openmem/SKILL.md +5 -0
- package/README.md +81 -76
- package/cli/coding-agent/capabilities.mjs +14 -3
- package/cli/coding-agent/context-store.mjs +81 -0
- package/cli/coding-agent/context-trim.mjs +899 -0
- package/cli/coding-agent/file-io.mjs +72 -0
- package/cli/coding-agent/hook-harnesses.mjs +2 -1
- package/cli/coding-agent/hook-runner.mjs +28 -4
- package/cli/coding-agent/install-access.mjs +43 -17
- 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 +119 -15
- package/cli/coding-agent/opencode-config.mjs +21 -5
- package/cli/coding-agent/opencode-plugin.mjs +39 -3
- 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 +2 -1
- package/cli/coding-agent/session-db.mjs +626 -0
- package/cli/coding-agent/session-facade.mjs +30 -6
- package/cli/coding-agent/toml-edit.mjs +161 -0
- package/cli/context-management.mjs +254 -0
- package/cli/feed-sync.mjs +18 -2
- package/cli/install-coding-agent.mjs +173 -40
- package/cli/mcp/context-sql-runner.mjs +58 -0
- package/cli/mcp/context-store.mjs +670 -0
- package/cli/mcp/knowledge.mjs +333 -0
- package/cli/mcp/memory-facade.mjs +99 -33
- package/cli/openmem-mcp.mjs +3 -0
- package/cli/openmem.mjs +18 -1
- package/cli/schedule.mjs +176 -0
- package/cli/stack.mjs +2 -0
- package/cli/switch-memory-provider.mjs +1 -1
- package/compose.yaml +67 -4
- package/config/hindsight.env.example +2 -0
- package/config/openmem.toml.example +64 -2
- package/docs/architecture.md +35 -9
- package/docs/coding-agent-hooks.md +26 -2
- package/docs/coding-agents.md +27 -7
- package/docs/compatibility.md +80 -0
- package/docs/concepts.md +30 -7
- package/docs/context-management.md +230 -0
- package/docs/context-proxy.md +115 -0
- package/docs/context-research.md +226 -0
- package/docs/context-store.md +277 -0
- package/docs/evidence-plan.md +216 -0
- package/docs/hindsight.md +5 -2
- package/docs/mcp-setup.md +5 -2
- package/docs/mem0.md +39 -1
- package/docs/memory-providers.md +2 -2
- package/docs/meridian-litellm.md +198 -0
- package/docs/model-configuration.md +22 -0
- package/docs/npx-install.md +6 -4
- package/docs/openmem-config.md +2 -2
- package/docs/quickstart.md +12 -6
- package/docs/setup-wizard.md +5 -4
- package/docs/sources-feeds.md +9 -22
- package/package.json +5 -4
- package/providers/cognee/OPERATOR.md +2 -2
- package/providers/hindsight/lib/clack.mjs +122 -20
- package/providers/hindsight/lib/coding-agent.mjs +1 -1
- package/providers/hindsight/lib/config.mjs +6 -0
- package/providers/hindsight/lib/env-setup.mjs +2 -1
- package/providers/hindsight/lib/litellm-routes.mjs +21 -12
- package/providers/hindsight/lib/meridian.mjs +307 -0
- package/providers/hindsight/lib/model-backend.mjs +38 -9
- package/providers/hindsight/lib/probe.mjs +21 -0
- package/providers/hindsight/lib/stack-env.mjs +5 -12
- package/providers/hindsight/lib/state.mjs +6 -1
- package/providers/hindsight/lib/summary.mjs +16 -2
- package/providers/hindsight/lib/validate.mjs +6 -0
- package/providers/hindsight/wizard.mjs +3 -2
- package/providers/mem0/OPERATOR.md +21 -2
- package/providers/mem0/compose.yaml +24 -2
- package/providers/mem0/lib/coding-agent.mjs +1 -1
- package/providers/mem0/lib/configure.mjs +66 -0
- package/providers/mem0/lib/litellm.mjs +457 -0
- package/providers/mem0/lib/stack-env.mjs +40 -1
- package/providers/mem0/lib/summary.mjs +30 -2
- package/providers/mem0/manifest.json +1 -1
- package/providers/mem0/wizard.mjs +15 -2
- 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 +608 -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 +2 -1
- package/scripts/run-setup.sh +14 -5
- package/scripts/setup.mjs +14 -1
- package/server.json +2 -2
- package/wizard/lib/context-step.mjs +140 -0
- package/wizard/lib/litellm-env.mjs +55 -0
- package/wizard/lib/pm-config.mjs +212 -4
- package/wizard/lib/prompts.mjs +28 -0
- package/wizard/lib/run-wizard.mjs +26 -31
package/docs/npx-install.md
CHANGED
|
@@ -5,7 +5,7 @@ checkout. The package name lives in one place, the `name` field of `package.json
|
|
|
5
5
|
command stays `openmem` if you switch to a scoped name such as
|
|
6
6
|
`openmem`; older installs may still see `openmem`).
|
|
7
7
|
|
|
8
|
-
Requirements: Node ≥
|
|
8
|
+
Requirements: Node ≥ 24 (`node:sqlite` with FTS5, for the session context store), Docker with the compose v2
|
|
9
9
|
plugin (`docker compose version`), `curl`. Linux and macOS are supported; there is no
|
|
10
10
|
bash dependency in the CLI (macOS ships bash 3.2).
|
|
11
11
|
|
|
@@ -23,11 +23,12 @@ npx @bcmyguest/openmem switch mem0 --yes
|
|
|
23
23
|
npm i -g openmem # then: openmem setup | up | down | ...
|
|
24
24
|
# From a git checkout (package not required on the registry):
|
|
25
25
|
git clone https://github.com/bcmyguest/openmem && cd openmem
|
|
26
|
-
npm install &&
|
|
26
|
+
npm install && npm run setup # same wizard; also: ./setup
|
|
27
|
+
node cli/openmem.mjs doctor # any other command
|
|
27
28
|
```
|
|
28
29
|
|
|
29
30
|
No global `pm` command is installed: it collides with Android's `pm`. `./pm` and `npm run pm`
|
|
30
|
-
exist only inside a checkout; prefer `npx @bcmyguest/openmem …` (or `
|
|
31
|
+
exist only inside a checkout; prefer `npx @bcmyguest/openmem …` (or `node cli/openmem.mjs …` in a checkout).
|
|
31
32
|
|
|
32
33
|
### Commands
|
|
33
34
|
|
|
@@ -38,7 +39,8 @@ exist only inside a checkout; prefer `npx @bcmyguest/openmem …` (or `npx . …
|
|
|
38
39
|
| `up` / `down` | start / stop the stack with the saved configuration |
|
|
39
40
|
| `doctor` | validate the configuration without the smoke test |
|
|
40
41
|
| `install-mcp` | merge **openmem** unified MCP (`memory_*`) by default; NotebookLM when sync is on or `--notebooklm`; native mem0/Cognee with `--provider-only`; `--no-unified-mcp` to skip |
|
|
41
|
-
| `install-coding-agent` | `install-mcp` + session hooks for all providers;
|
|
42
|
+
| `install-coding-agent` | `install-mcp` + session hooks for all providers; `--provider-plugin` runs `@vectorize-io/hindsight-coding-agents` instead of OpenMem hooks on Hindsight stacks (saved as `[agents] integration`; `--no-provider-plugin` reverts) |
|
|
43
|
+
| `context <on\|off\|status>` | context management (smart compaction) per agent: saves `[context_proxy.agents]` (`--agents claude-code,opencode1,codex,opencode2`, default all), re-runs `install-coding-agent`, which starts or stops the `context-proxy` service (`--dry-run`); `status` lists each agent, proxy health and pointed agents ([Context management](context-management.md)) |
|
|
42
44
|
| `wiring` | report `MEMORY_PROVIDER`, API URLs, and `mcp.json` alignment (`--json`) |
|
|
43
45
|
| `switch <provider>` | change `MEMORY_PROVIDER` (optional stack restart + MCP; no data migration) |
|
|
44
46
|
| `providers [--json]` | list the provider manifests shipped in the package with their status |
|
package/docs/openmem-config.md
CHANGED
|
@@ -162,7 +162,7 @@ Interactive `./setup` uses this file for **defaults** (Clack pre-selects); your
|
|
|
162
162
|
Flags: `--yes`, `--dry-run`, `--no-stack`, `--no-mcp`. Does not migrate data between
|
|
163
163
|
backends. See [README — Switch memory provider](../README.md#switch-memory-provider).
|
|
164
164
|
- `feed-sync` — runs `cli/feed-sync.mjs` (RSS + OpenAlex → `feeds.out_dir` in `index.yml`).
|
|
165
|
-
- `install-coding-agent` — unified MCP + session hooks (all providers);
|
|
165
|
+
- `install-coding-agent` — unified MCP + session hooks (all providers); `--provider-plugin` / `--no-provider-plugin` set `[agents] integration`; see
|
|
166
166
|
[coding-agent hooks](coding-agent-hooks.md).
|
|
167
167
|
|
|
168
168
|
## MCP (unified `memory_*` tools)
|
|
@@ -179,7 +179,7 @@ After changing MCP code, re-run `npx @bcmyguest/openmem install-coding-agent --y
|
|
|
179
179
|
cp config/openmem.toml.example ~/.config/personal-memory/config.toml
|
|
180
180
|
# edit provider, ports, model backend
|
|
181
181
|
npx @bcmyguest/openmem configure --yes
|
|
182
|
-
#
|
|
182
|
+
# from a checkout: node cli/openmem.mjs configure --yes
|
|
183
183
|
```
|
|
184
184
|
|
|
185
185
|
After a successful configure, the wizard **writes back** `~/.config/personal-memory/config.toml` so
|
package/docs/quickstart.md
CHANGED
|
@@ -7,8 +7,8 @@ Get from zero to a running stack in one wizard run. **Choose your memory provide
|
|
|
7
7
|
| Requirement | Why |
|
|
8
8
|
| --- | --- |
|
|
9
9
|
| Docker + Compose v2 | Runs the stack; your user must run `docker` without sudo |
|
|
10
|
-
| Node.js ≥
|
|
11
|
-
| LLM for Hindsight (default provider) | lemond, Ollama,
|
|
10
|
+
| Node.js ≥ 24 | CLI, setup wizard, and the session context store (`node:sqlite` with FTS5) |
|
|
11
|
+
| LLM for Hindsight (default provider) | lemond, Ollama, OpenAI-compatible, or a Claude subscription through [Meridian](meridian-litellm.md) — wizard wires LiteLLM |
|
|
12
12
|
| git, curl | Wizard preflight |
|
|
13
13
|
|
|
14
14
|
Default stack is [Hindsight](hindsight.md). Other providers: wizard or `npx @bcmyguest/openmem providers` ([provider status](provider-status.md)).
|
|
@@ -27,9 +27,11 @@ npx @bcmyguest/openmem
|
|
|
27
27
|
git clone https://github.com/bcmyguest/openmem.git
|
|
28
28
|
cd openmem
|
|
29
29
|
npm install
|
|
30
|
-
|
|
30
|
+
npm run setup
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
+
Run other commands from the checkout with `node cli/openmem.mjs <command>`.
|
|
34
|
+
|
|
33
35
|
The wizard asks for a memory provider, provider settings, what to index, then starts Docker and runs a smoke test.
|
|
34
36
|
|
|
35
37
|
Useful variants:
|
|
@@ -41,7 +43,9 @@ npx @bcmyguest/openmem configure --yes # defaults + start, no prompts
|
|
|
41
43
|
|
|
42
44
|
Wizard dry-run (what first setup plans — no writes):
|
|
43
45
|
|
|
44
|
-
|
|
46
|
+
<p align="center">
|
|
47
|
+
<img src="https://bcmyguest.github.io/openmem/setup-demo.gif" alt="Terminal recording of the setup wizard dry run: preflight checks, memory provider, model backend, secrets, sources and stack steps, each planned write printed as a [dry-run] line" width="720" />
|
|
48
|
+
</p>
|
|
45
49
|
|
|
46
50
|
## 2. Confirm it is up
|
|
47
51
|
|
|
@@ -52,7 +56,7 @@ curl -fsS http://127.0.0.1:8888/health # Hindsight default; see wizard output
|
|
|
52
56
|
|
|
53
57
|
## 3. Wire your coding agent (optional)
|
|
54
58
|
|
|
55
|
-
**Session memory
|
|
59
|
+
**Session memory** (after step 1 — MCP and hooks need the running stack): `npx @bcmyguest/openmem install-coding-agent --yes` — unified MCP **plus** session hooks for Claude Code, Codex, OpenCode, Cursor CLI (any active provider). On Hindsight, `--provider-plugin` uses the upstream Hindsight plugin hooks instead (one hook set at a time). Not the same as `install-mcp` alone.
|
|
56
60
|
|
|
57
61
|
**`install-mcp`** merges the **`openmem` unified MCP** (`memory_add`, `memory_search`, `memory_recall`, `memory_reflect`, `memory_consolidate`, `memory_forget` on whichever provider you picked) and, when NotebookLM sync is on, the **NotebookLM MCP**:
|
|
58
62
|
|
|
@@ -61,7 +65,7 @@ npx @bcmyguest/openmem install-mcp --yes
|
|
|
61
65
|
npx @bcmyguest/openmem install-mcp --yes --notebooklm # force NotebookLM entry
|
|
62
66
|
```
|
|
63
67
|
|
|
64
|
-
Session hooks use the in-repo shim by default;
|
|
68
|
+
Session hooks use the in-repo shim by default (`[agents] integration = "openmem"`); `--provider-plugin` replaces them with the upstream Hindsight plugin and `--no-provider-plugin` switches back. Optional native mem0/Cognee MCP: `install-mcp --provider-only`. Full guide: [MCP setup](mcp-setup.md).
|
|
65
69
|
|
|
66
70
|
Restart Cursor Agent after MCP changes. Move memories between providers with [Portable memory (PMM)](memory-portability.md) — `switch` alone does not migrate data.
|
|
67
71
|
|
|
@@ -79,6 +83,7 @@ npx @bcmyguest/openmem doctor
|
|
|
79
83
|
| `up` / `down` | Start or stop the stack (down keeps data volumes) |
|
|
80
84
|
| `switch <id> --yes` | Change provider (no memory migration) |
|
|
81
85
|
| `doctor` | Validate config without the smoke test |
|
|
86
|
+
| `schedule install` | Refresh configured RSS/OpenAlex feeds hourly with the OS scheduler |
|
|
82
87
|
|
|
83
88
|
In a checkout, use `npx .` instead of `npx @bcmyguest/openmem`.
|
|
84
89
|
|
|
@@ -97,5 +102,6 @@ Secrets and index config: `~/.config/hindsight/` (and optional `~/.config/person
|
|
|
97
102
|
- [Portable memory (PMM)](memory-portability.md) — export/import
|
|
98
103
|
- [Architecture](architecture.md) — data flow, indexing, MCP by provider
|
|
99
104
|
- [Index sources](index-sources.md) — git, Obsidian, notes/papers/bookmarks, feed cache
|
|
105
|
+
- [Feed sync](sources-feeds.md) — configure RSS/OpenAlex and schedule refreshes
|
|
100
106
|
- [Install with npx](npx-install.md) — upgrades, troubleshooting
|
|
101
107
|
- [Configuration file](openmem-config.md) — declarative `config.toml`
|
package/docs/setup-wizard.md
CHANGED
|
@@ -5,7 +5,7 @@ The wizard is a Node program (`wizard/bin/setup.mjs`). It is exposed as:
|
|
|
5
5
|
| How you run it | When |
|
|
6
6
|
| --- | --- |
|
|
7
7
|
| `npx @bcmyguest/openmem` / `npx @bcmyguest/openmem setup` | Normal install ([Install with npx](npx-install.md)) |
|
|
8
|
-
| `
|
|
8
|
+
| `npm run setup` / `./setup` | Git checkout (stack root stays in the repo); other commands: `node cli/openmem.mjs <command>` |
|
|
9
9
|
|
|
10
10
|
Interactive runs use [@clack/prompts](https://github.com/bombshell-dev/clack). `--yes` and `--dry-run` skip Clack.
|
|
11
11
|
|
|
@@ -40,8 +40,9 @@ Configure runs these steps in order (provider hooks replace or extend some steps
|
|
|
40
40
|
| 5 | Sources to index | Writes `~/.config/hindsight/index.yml`; lists sources with a dry indexer run |
|
|
41
41
|
| 6 | NotebookLM sync | Opt-in; Google login when enabled |
|
|
42
42
|
| 7 | Start the stack | Build indexer image if inputs changed; health wait; smoke retain/recall (unless `--no-smoke`) |
|
|
43
|
-
| 8 |
|
|
44
|
-
| 9 |
|
|
43
|
+
| 8 | Context management (smart compaction) | Multi-select of the installed agents (Claude Code, OpenCode 1, OpenCode 2). Defaults: saved `[context_proxy.agents]` keys, else `[context_proxy] enabled`, else OpenCode 2 on and the proxy-routed agents off. Saves all four keys to `config.toml`. Claude Code or OpenCode 1 on: writes the proxy key file and store directories, then starts the `context-proxy` compose service. Neither on: stops a running `context-proxy` after step 9, unless an agent still points at it ([Context management](context-management.md)) |
|
|
44
|
+
| 9 | Coding agents (optional) | For detected CLIs (claude, codex, opencode, cursor-agent), offers `install-coding-agent --yes`: unified MCP, OpenMem skill, session hooks; honours `[agents] integration` (OpenMem hooks or the provider plugin) and `[context_proxy.agents]`. Default answer is no |
|
|
45
|
+
| 10 | Validate memory setup | Provider checks + shared validation |
|
|
45
46
|
|
|
46
47
|
The last line is `wizard: changed=N` (files and containers this run touched). An unchanged re-run prints `changed=0`.
|
|
47
48
|
|
|
@@ -49,7 +50,7 @@ The last line is `wizard: changed=N` (files and containers this run touched). An
|
|
|
49
50
|
|
|
50
51
|
| Provider | Step 3 asks for |
|
|
51
52
|
| --- | --- |
|
|
52
|
-
| Hindsight | Model backend: lemond, Ollama (host or compose profile),
|
|
53
|
+
| Hindsight | Model backend: lemond, Ollama (host or compose profile), custom OpenAI-compatible URL, or a Claude subscription through a running Meridian ([guide](meridian-litellm.md#with-the-wizard)); LiteLLM routes for `memory-retain`, `memory-reflect`, `memory-consolidate` |
|
|
53
54
|
| mem0 | Self-hosted REST base URL and API key file path |
|
|
54
55
|
| Cognee | Loopback port and `LLM_API_KEY` for graph build |
|
|
55
56
|
|
package/docs/sources-feeds.md
CHANGED
|
@@ -22,31 +22,18 @@ Then ensure the stack indexer is running. Each run writes markdown files under `
|
|
|
22
22
|
|
|
23
23
|
Network use: feed-sync calls public RSS/OpenAlex URLs from the host (not from CI by default).
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
`feed-sync` needs Python 3 with PyYAML on the host. On Windows it uses `py -3`, then `python`; on macOS and Linux it uses `python3`, then `python`. Set `PYTHON` to choose a specific executable.
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
## Schedule feed sync (optional)
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
# ~/.config/systemd/user/openmem-feed-sync.timer
|
|
31
|
-
[Unit]
|
|
32
|
-
Description=Refresh openmem feed cache
|
|
29
|
+
Install an hourly job with the current user's native scheduler. The GitHub npx source works before the next npm release includes `schedule`:
|
|
33
30
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
[Install]
|
|
39
|
-
WantedBy=timers.target
|
|
31
|
+
```sh
|
|
32
|
+
npx --yes github:bcmyguest/openmem schedule install --dry-run
|
|
33
|
+
npx --yes github:bcmyguest/openmem schedule install
|
|
34
|
+
npx --yes github:bcmyguest/openmem schedule remove
|
|
40
35
|
```
|
|
41
36
|
|
|
42
|
-
|
|
43
|
-
# ~/.config/systemd/user/openmem-feed-sync.service
|
|
44
|
-
[Unit]
|
|
45
|
-
Description=openmem feed-sync
|
|
46
|
-
|
|
47
|
-
[Service]
|
|
48
|
-
Type=oneshot
|
|
49
|
-
ExecStart=/usr/bin/env npx @bcmyguest/openmem feed-sync
|
|
50
|
-
```
|
|
37
|
+
After that release, `npx @bcmyguest/openmem schedule ...` runs the same command from npm.
|
|
51
38
|
|
|
52
|
-
|
|
39
|
+
OpenMem installs a systemd user timer on Linux, a launchd agent on macOS, or a Task Scheduler task on Windows. The job calls the published npm package through a stable runner in your user config directory. Installation records the Python interpreter with PyYAML, so the job does not depend on your interactive shell's PATH. It runs when your user scheduler is active; the Linux timer catches up after downtime. Re-run `schedule install` if you move or upgrade Node.js or Python.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bcmyguest/openmem",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "OpenMem
|
|
3
|
+
"version": "0.9.0",
|
|
4
|
+
"description": "OpenMem — memory interface for coding agents: pluggable providers (Hindsight, mem0, Cognee, …), shared wizard/indexer, multi-format import/export (PMM and more).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
7
|
"author": "B Guest",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
},
|
|
41
41
|
"files": [
|
|
42
42
|
"cli/",
|
|
43
|
-
"skills/",
|
|
43
|
+
".agents/skills/openmem/",
|
|
44
44
|
"wizard/bin/",
|
|
45
45
|
"wizard/lib/",
|
|
46
46
|
"providers/",
|
|
@@ -49,6 +49,7 @@
|
|
|
49
49
|
"indexer/",
|
|
50
50
|
"bin/",
|
|
51
51
|
"sync/",
|
|
52
|
+
"proxy/",
|
|
52
53
|
"config/*.example",
|
|
53
54
|
"config/index.yml",
|
|
54
55
|
"config/notebooklm.yml",
|
|
@@ -71,7 +72,7 @@
|
|
|
71
72
|
"!**/.pytest_cache"
|
|
72
73
|
],
|
|
73
74
|
"engines": {
|
|
74
|
-
"node": ">=
|
|
75
|
+
"node": ">=24.0.0"
|
|
75
76
|
},
|
|
76
77
|
"publishConfig": {
|
|
77
78
|
"access": "public"
|
|
@@ -43,7 +43,7 @@ When `MEMORY_PROVIDER=cognee`:
|
|
|
43
43
|
|
|
44
44
|
```sh
|
|
45
45
|
npx openmem install-mcp --yes --provider-only
|
|
46
|
-
# checkout:
|
|
46
|
+
# checkout: node cli/openmem.mjs install-mcp --yes --provider-only
|
|
47
47
|
# or during setup: coding-agent step registers the same entry
|
|
48
48
|
```
|
|
49
49
|
|
|
@@ -74,7 +74,7 @@ v1 MCP is read-only (no add/cognify). Ingest stays with the indexer or curl.
|
|
|
74
74
|
```sh
|
|
75
75
|
npx openmem switch cognee --yes
|
|
76
76
|
# later: npx openmem switch hindsight --yes
|
|
77
|
-
# checkout:
|
|
77
|
+
# checkout: node cli/openmem.mjs switch cognee --yes
|
|
78
78
|
```
|
|
79
79
|
|
|
80
80
|
Updates `wizard.conf` (and `config.toml` `[memory].provider` when present), restarts the stack
|
|
@@ -1,10 +1,24 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Hindsight's questions for the Clack setup UI: model backend, models, reflect model
|
|
2
|
+
* Hindsight's questions for the Clack setup UI: model backend, models, reflect model
|
|
3
|
+
* (or, for Meridian, its URL, key and the two Claude models).
|
|
3
4
|
* The meta UI asks the provider-neutral questions (action, provider) and then calls
|
|
4
5
|
* clackSetup; the returned environment is handed to the wizard so it skips its own
|
|
5
6
|
* model prompts (WIZARD_SKIP_BACKEND_STEP).
|
|
6
7
|
*/
|
|
7
8
|
import { applyConfigEnv } from "./config.mjs";
|
|
9
|
+
import {
|
|
10
|
+
bridgeReport,
|
|
11
|
+
containerApiBase,
|
|
12
|
+
dockerBridgeIp,
|
|
13
|
+
existingMeridianKey,
|
|
14
|
+
MERIDIAN_DEFAULT_BASE,
|
|
15
|
+
MERIDIAN_GUIDE,
|
|
16
|
+
MERIDIAN_REFLECT_DEFAULT,
|
|
17
|
+
MERIDIAN_RETAIN_DEFAULT,
|
|
18
|
+
normalizeMeridianBase,
|
|
19
|
+
pickModel,
|
|
20
|
+
probeMeridian,
|
|
21
|
+
} from "./meridian.mjs";
|
|
8
22
|
import { fetchJson, normalizeOpenAiBase, ollamaModelNames, openAiModelIds } from "./probe.mjs";
|
|
9
23
|
import {
|
|
10
24
|
LEMOND_DEFAULT_MODEL,
|
|
@@ -51,10 +65,15 @@ export async function clackSetup(ctx, ui, h) {
|
|
|
51
65
|
{
|
|
52
66
|
value: "custom",
|
|
53
67
|
label: "OpenAI-compatible URL",
|
|
54
|
-
hint: "
|
|
68
|
+
hint: "hosted API, other",
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
value: "meridian",
|
|
72
|
+
label: "Claude subscription (Meridian)",
|
|
73
|
+
hint: "a Meridian proxy you already run on this host",
|
|
55
74
|
},
|
|
56
75
|
],
|
|
57
|
-
initialValue: ["lemond", "ollama", "custom"].includes(backendDefault)
|
|
76
|
+
initialValue: ["lemond", "ollama", "custom", "meridian"].includes(backendDefault)
|
|
58
77
|
? backendDefault
|
|
59
78
|
: "lemond",
|
|
60
79
|
});
|
|
@@ -67,7 +86,6 @@ export async function clackSetup(ctx, ui, h) {
|
|
|
67
86
|
let apiBase = "";
|
|
68
87
|
let reflectModel =
|
|
69
88
|
process.env.PM_REFLECT_MODEL ||
|
|
70
|
-
machine.PM_REFLECT_MODEL ||
|
|
71
89
|
h.envValue(wizardConf, "MEMORY_REFLECT_MODEL") ||
|
|
72
90
|
"";
|
|
73
91
|
|
|
@@ -85,8 +103,6 @@ export async function clackSetup(ctx, ui, h) {
|
|
|
85
103
|
);
|
|
86
104
|
}
|
|
87
105
|
const defaultModel =
|
|
88
|
-
machine.PM_DEFAULT_MODEL ||
|
|
89
|
-
machine.MEMORY_MODEL ||
|
|
90
106
|
h.envValue(wizardConf, "MEMORY_MODEL") ||
|
|
91
107
|
LEMOND_DEFAULT_MODEL;
|
|
92
108
|
if (ids.length > 0) {
|
|
@@ -108,7 +124,7 @@ export async function clackSetup(ctx, ui, h) {
|
|
|
108
124
|
}
|
|
109
125
|
apiBase = "http://host.docker.internal:13305/v1";
|
|
110
126
|
} else if (backend === "ollama") {
|
|
111
|
-
const ollamaBase =
|
|
127
|
+
const ollamaBase = "http://127.0.0.1:11434";
|
|
112
128
|
const spin = ui.spinner();
|
|
113
129
|
spin.start(`Checking Ollama at ${ollamaBase}`);
|
|
114
130
|
const body = await fetchJson(`${ollamaBase}/api/tags`);
|
|
@@ -137,8 +153,6 @@ export async function clackSetup(ctx, ui, h) {
|
|
|
137
153
|
}
|
|
138
154
|
}
|
|
139
155
|
const defaultModel =
|
|
140
|
-
machine.PM_DEFAULT_MODEL ||
|
|
141
|
-
machine.MEMORY_MODEL ||
|
|
142
156
|
h.envValue(wizardConf, "MEMORY_MODEL") ||
|
|
143
157
|
OLLAMA_DEFAULT_MODEL;
|
|
144
158
|
if (names.length > 0) {
|
|
@@ -160,12 +174,15 @@ export async function clackSetup(ctx, ui, h) {
|
|
|
160
174
|
}
|
|
161
175
|
apiBase = ollamaInCompose ? OLLAMA_COMPOSE_API_BASE : OLLAMA_HOST_API_BASE;
|
|
162
176
|
if (ollamaInCompose) handoff.MEMORY_OLLAMA_IN_COMPOSE = "1";
|
|
177
|
+
} else if (backend === "meridian") {
|
|
178
|
+
const m = await askMeridian(ctx, ui, h);
|
|
179
|
+
model = m.model;
|
|
180
|
+
reflectModel = m.reflectModel;
|
|
181
|
+
apiBase = m.apiBase;
|
|
182
|
+
Object.assign(handoff, m.env);
|
|
163
183
|
} else {
|
|
164
|
-
const defaultBase =
|
|
165
|
-
machine.CUSTOM_DEFAULT_BASE || "http://host.docker.internal:3456/v1";
|
|
184
|
+
const defaultBase = "http://host.docker.internal:3456/v1";
|
|
166
185
|
const defaultModel =
|
|
167
|
-
machine.CUSTOM_DEFAULT_MODEL ||
|
|
168
|
-
machine.PM_DEFAULT_MODEL ||
|
|
169
186
|
h.envValue(wizardConf, "MEMORY_MODEL") ||
|
|
170
187
|
LEMOND_DEFAULT_MODEL;
|
|
171
188
|
const baseInput = await ui.text({
|
|
@@ -205,6 +222,24 @@ export async function clackSetup(ctx, ui, h) {
|
|
|
205
222
|
}
|
|
206
223
|
}
|
|
207
224
|
|
|
225
|
+
if (backend !== "meridian") reflectModel = await askReflect(ui, model, reflectModel);
|
|
226
|
+
|
|
227
|
+
ui.note("Next: Docker, secrets, indexing, and starting the stack.", "Backend configured");
|
|
228
|
+
|
|
229
|
+
handoff.WIZARD_SKIP_BACKEND_STEP = "1";
|
|
230
|
+
handoff.PM_BACKEND = /** @type {string} */ (backend);
|
|
231
|
+
handoff.PM_DEFAULT_MODEL = model;
|
|
232
|
+
handoff.MEMORY_API_BASE = apiBase;
|
|
233
|
+
handoff.PM_REFLECT_MODEL = reflectModel;
|
|
234
|
+
return handoff;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* @param {typeof import("@clack/prompts")} ui
|
|
239
|
+
* @param {string} model retain model
|
|
240
|
+
* @param {string} reflectModel stored or env reflect model
|
|
241
|
+
*/
|
|
242
|
+
async function askReflect(ui, model, reflectModel) {
|
|
208
243
|
const reflectChoice = await ui.select({
|
|
209
244
|
message: "Reflect model (needs tool calls; used for memory-reflect)",
|
|
210
245
|
options: [
|
|
@@ -233,13 +268,80 @@ export async function clackSetup(ctx, ui, h) {
|
|
|
233
268
|
} else {
|
|
234
269
|
reflectModel = "";
|
|
235
270
|
}
|
|
271
|
+
return reflectModel;
|
|
272
|
+
}
|
|
236
273
|
|
|
237
|
-
|
|
274
|
+
/**
|
|
275
|
+
* Meridian URL, key and models. Detect only: Meridian must already run.
|
|
276
|
+
* @param {{ home: string, wizardConf: string }} ctx
|
|
277
|
+
* @param {typeof import("@clack/prompts")} ui
|
|
278
|
+
* @param {Helpers} h
|
|
279
|
+
*/
|
|
280
|
+
async function askMeridian(ctx, ui, h) {
|
|
281
|
+
ui.log.info(`The wizard does not install or start Meridian; see ${MERIDIAN_GUIDE}.`);
|
|
282
|
+
const stored =
|
|
283
|
+
h.envValue(ctx.wizardConf, "MEMORY_BACKEND") === "meridian"
|
|
284
|
+
? h.envValue(ctx.wizardConf, "MEMORY_MERIDIAN_URL")
|
|
285
|
+
: "";
|
|
286
|
+
const defaultBase = process.env.MERIDIAN_BASE || stored || MERIDIAN_DEFAULT_BASE;
|
|
287
|
+
const baseInput = await ui.text({
|
|
288
|
+
message: "Meridian URL from this host",
|
|
289
|
+
placeholder: defaultBase,
|
|
290
|
+
defaultValue: defaultBase,
|
|
291
|
+
});
|
|
292
|
+
if (ui.isCancel(baseInput)) process.exit(0);
|
|
293
|
+
const meridianBase = normalizeMeridianBase(/** @type {string} */ (baseInput));
|
|
294
|
+
const paths = {
|
|
295
|
+
meridianEnvFile: `${ctx.home}/.config/meridian/meridian.env`,
|
|
296
|
+
litellmEnv: `${ctx.home}/.config/hindsight/litellm.env`,
|
|
297
|
+
};
|
|
298
|
+
const existing = existingMeridianKey(paths, h);
|
|
299
|
+
const keyInput = await ui.password({
|
|
300
|
+
message: `MERIDIAN_API_KEY (blank uses ${existing.source || "no key"})`,
|
|
301
|
+
});
|
|
302
|
+
if (ui.isCancel(keyInput)) process.exit(0);
|
|
303
|
+
const typedKey = String(keyInput || "").trim();
|
|
304
|
+
const key = typedKey || existing.key;
|
|
238
305
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
306
|
+
const bridgeIp = dockerBridgeIp();
|
|
307
|
+
const spin = ui.spinner();
|
|
308
|
+
spin.start(`Checking Meridian at ${meridianBase}`);
|
|
309
|
+
const probe = await probeMeridian(meridianBase, key, bridgeIp);
|
|
310
|
+
spin.stop(
|
|
311
|
+
probe.answered ? `Meridian answered (${probe.ids.length} models)` : "Meridian not reachable",
|
|
312
|
+
);
|
|
313
|
+
if (!probe.answered) {
|
|
314
|
+
ui.log.warn(`Start Meridian first (${MERIDIAN_GUIDE}); the route uses the guide's default models.`);
|
|
315
|
+
} else if (probe.modelsStatus === 401 || probe.modelsStatus === 403) {
|
|
316
|
+
ui.log.warn(`Meridian rejected the key on /v1/models (HTTP ${probe.modelsStatus}).`);
|
|
317
|
+
}
|
|
318
|
+
const apiBase = containerApiBase(meridianBase);
|
|
319
|
+
const report = bridgeReport({ ...paths, meridianBase, apiBase, dockerBridgeIp: bridgeIp }, h.envValue, probe);
|
|
320
|
+
if (report) (report.level === "warn" ? ui.log.warn : ui.log.info)(report.text);
|
|
321
|
+
|
|
322
|
+
const choose = async (message, preferred, family, candidates) => {
|
|
323
|
+
const def = pickModel(probe.ids, preferred, family, candidates);
|
|
324
|
+
const picked = probe.ids.length
|
|
325
|
+
? await ui.select({
|
|
326
|
+
message,
|
|
327
|
+
options: probe.ids.map((id) => ({ value: id, label: id })),
|
|
328
|
+
initialValue: def,
|
|
329
|
+
})
|
|
330
|
+
: await ui.text({ message, placeholder: def, defaultValue: def });
|
|
331
|
+
if (ui.isCancel(picked)) process.exit(0);
|
|
332
|
+
return /** @type {string} */ (picked);
|
|
333
|
+
};
|
|
334
|
+
const sameBackend = h.envValue(ctx.wizardConf, "MEMORY_BACKEND") === "meridian";
|
|
335
|
+
const model = await choose("Retain / consolidate model", MERIDIAN_RETAIN_DEFAULT, /haiku/, [
|
|
336
|
+
process.env.PM_DEFAULT_MODEL,
|
|
337
|
+
sameBackend ? h.envValue(ctx.wizardConf, "MEMORY_MODEL") : "",
|
|
338
|
+
]);
|
|
339
|
+
const reflectModel = await choose("Reflect model (needs tool calls)", MERIDIAN_REFLECT_DEFAULT, /sonnet/, [
|
|
340
|
+
process.env.PM_REFLECT_MODEL,
|
|
341
|
+
sameBackend ? h.envValue(ctx.wizardConf, "MEMORY_REFLECT_MODEL") : "",
|
|
342
|
+
]);
|
|
343
|
+
/** @type {Record<string, string>} */
|
|
344
|
+
const env = { MERIDIAN_BASE: meridianBase };
|
|
345
|
+
if (typedKey) env.MERIDIAN_API_KEY = typedKey;
|
|
346
|
+
return { model, reflectModel, apiBase, env };
|
|
245
347
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Step "
|
|
2
|
+
* Step "9. Coding-agent plugin", Hindsight half: ensure ~/.hindsight/coding-agent.json
|
|
3
3
|
* exists (seeded from the tracked template when missing) and check that its optInPaths
|
|
4
4
|
* cover the indexed roots. Machine-local keys such as notebooklmAccess must live in a
|
|
5
5
|
* real file — never in the repo template — so we do not replace a materialised config
|
|
@@ -12,6 +12,7 @@ export function defaultsFromConfig(cfg) {
|
|
|
12
12
|
const model = /** @type {Record<string, unknown>} */ (cfg.model || {});
|
|
13
13
|
const lemond = /** @type {Record<string, unknown>} */ (model.lemond || {});
|
|
14
14
|
const ollama = /** @type {Record<string, unknown>} */ (model.ollama || {});
|
|
15
|
+
const meridian = /** @type {Record<string, unknown>} */ (model.meridian || {});
|
|
15
16
|
return {
|
|
16
17
|
apiPort: stack.api_port != null ? String(stack.api_port) : "",
|
|
17
18
|
cpPort: stack.control_plane_port != null ? String(stack.control_plane_port) : "",
|
|
@@ -21,6 +22,7 @@ export function defaultsFromConfig(cfg) {
|
|
|
21
22
|
modelApiBase: String(model.api_base || ""),
|
|
22
23
|
lemondHost: String(lemond.host || ""),
|
|
23
24
|
ollamaHost: String(ollama.host || ""),
|
|
25
|
+
meridianHost: String(meridian.host || ""),
|
|
24
26
|
};
|
|
25
27
|
}
|
|
26
28
|
|
|
@@ -43,6 +45,7 @@ export function applyConfigEnv(cfg) {
|
|
|
43
45
|
process.env.MEMORY_API_BASE = d.modelApiBase;
|
|
44
46
|
}
|
|
45
47
|
if (d.lemondHost && !process.env.LEMOND_BASE) process.env.LEMOND_BASE = d.lemondHost;
|
|
48
|
+
if (d.meridianHost && !process.env.MERIDIAN_BASE) process.env.MERIDIAN_BASE = d.meridianHost;
|
|
46
49
|
}
|
|
47
50
|
|
|
48
51
|
/**
|
|
@@ -63,6 +66,9 @@ export function configToml(ctx) {
|
|
|
63
66
|
if (ctx.backend === "ollama" || ctx.ollamaBase) {
|
|
64
67
|
model.ollama = { host: ctx.ollamaBase };
|
|
65
68
|
}
|
|
69
|
+
if (ctx.backend === "meridian" && ctx.meridianBase) {
|
|
70
|
+
model.meridian = { host: ctx.meridianBase };
|
|
71
|
+
}
|
|
66
72
|
return {
|
|
67
73
|
stack: {
|
|
68
74
|
api_port: Number(ctx.portApi || 8888),
|
|
@@ -30,7 +30,8 @@ export function llmWired(ctx, h) {
|
|
|
30
30
|
export function stackEnv(ctx) {
|
|
31
31
|
ensureState(ctx);
|
|
32
32
|
let backend = ctx.backend;
|
|
33
|
-
|
|
33
|
+
// Own-route backends run from litellm.yaml; setup.mjs only knows lemond and ollama.
|
|
34
|
+
if (backend === "custom" || backend === "meridian") backend = "lemond";
|
|
34
35
|
/** @type {Record<string, string>} */
|
|
35
36
|
const env = { MEMORY_BACKEND: backend };
|
|
36
37
|
if (ctx.ollamaInCompose) env.MEMORY_OLLAMA_IN_COMPOSE = "1";
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { configToml } from "./config.mjs";
|
|
4
|
+
import { renderMeridianRoutes } from "./meridian.mjs";
|
|
4
5
|
import { OLLAMA_HOST_API_BASE } from "./state.mjs";
|
|
5
6
|
|
|
6
7
|
/** @typedef {import("../../../wizard/lib/provider-wizard/helpers.mjs").ProviderWizardHelpers} Helpers */
|
|
@@ -80,6 +81,7 @@ function renderShipped(ctx, litellmModel, reflectLitellmModel) {
|
|
|
80
81
|
* @returns {string}
|
|
81
82
|
*/
|
|
82
83
|
export function litellmConfig(ctx) {
|
|
84
|
+
if (ctx.backend === "meridian") return renderMeridianRoutes(ctx);
|
|
83
85
|
let prefix = "openai/";
|
|
84
86
|
if (ctx.backend === "ollama") prefix = "ollama_chat/";
|
|
85
87
|
if (ctx.backend === "custom") return renderOverride(ctx, `${prefix}${ctx.model}`);
|
|
@@ -112,6 +114,13 @@ export async function removeOverrideIfAsked(ctx, h) {
|
|
|
112
114
|
* @param {Helpers} h
|
|
113
115
|
*/
|
|
114
116
|
export async function writeOverride(ctx, h) {
|
|
117
|
+
if (ctx.dry && ctx.backend === "meridian") {
|
|
118
|
+
const backup = fs.existsSync(ctx.litellmOverride) ? " (backs up the existing file)" : "";
|
|
119
|
+
h.info(
|
|
120
|
+
`[dry-run] write ${ctx.litellmOverride}${backup}: Meridian routes, retain and consolidate -> anthropic/${ctx.model}, reflect -> anthropic/${ctx.reflectModel || ctx.model}, at ${ctx.apiBase}`,
|
|
121
|
+
);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
115
124
|
if (ctx.dry) {
|
|
116
125
|
h.info(
|
|
117
126
|
`[dry-run] write ${ctx.litellmOverride} from config/litellm.${ctx.backend}.yaml: retain and consolidate -> ${ctx.model}, reflect -> ${ctx.reflectModel || "shipped"}, at ${ctx.apiBase}`,
|
|
@@ -150,7 +159,8 @@ export async function applyRoute(ctx, h) {
|
|
|
150
159
|
if (ctx.backend === "ollama") shipped = ctx.ollamaDefaultModel;
|
|
151
160
|
const defaultBase = shippedApiBase(ctx.backend);
|
|
152
161
|
const apiMatchesShipped = !ctx.apiBase || ctx.apiBase === defaultBase;
|
|
153
|
-
|
|
162
|
+
const ownRoute = ctx.backend === "custom" || ctx.backend === "meridian";
|
|
163
|
+
if (!ownRoute && ctx.model === shipped && !ctx.reflectModel && apiMatchesShipped) {
|
|
154
164
|
await removeOverrideIfAsked(ctx, h);
|
|
155
165
|
} else {
|
|
156
166
|
await writeOverride(ctx, h);
|
|
@@ -173,15 +183,14 @@ export function saveConf(ctx, h) {
|
|
|
173
183
|
h.info(`[dry-run] record backend ${ctx.backend} in ${ctx.wizardConf}`);
|
|
174
184
|
return;
|
|
175
185
|
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
);
|
|
186
|
+
/** @type {Record<string, string>} */
|
|
187
|
+
const keys = {
|
|
188
|
+
MEMORY_BACKEND: ctx.backend,
|
|
189
|
+
MEMORY_MODEL: ctx.model,
|
|
190
|
+
MEMORY_API_BASE: ctx.apiBase,
|
|
191
|
+
MEMORY_REFLECT_MODEL: ctx.reflectModel,
|
|
192
|
+
MEMORY_OLLAMA_IN_COMPOSE: ctx.ollamaInCompose ? "1" : "0",
|
|
193
|
+
};
|
|
194
|
+
if (ctx.backend === "meridian") keys.MEMORY_MERIDIAN_URL = ctx.meridianBase;
|
|
195
|
+
h.saveWizardConf(ctx, keys, { pmSections: configToml(ctx) });
|
|
187
196
|
}
|