memgraph-context-graph 0.7.4__tar.gz → 0.9.0__tar.gz
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.
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/PKG-INFO +77 -11
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/README.md +76 -10
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/docs/command-hooks.md +14 -13
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/pyproject.toml +1 -1
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/adapters/_identity.py +52 -5
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/adapters/_opencode_plugin.js +36 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/adapters/_spec.py +26 -1
- memgraph_context_graph-0.9.0/src/memgraph_context_graph/adapters/agent_mcp.py +326 -0
- memgraph_context_graph-0.9.0/src/memgraph_context_graph/adapters/agent_memory.py +201 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/adapters/antigravity_cli.py +77 -17
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/adapters/claude_code.py +2 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/adapters/codex.py +2 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/adapters/copilot_cli.py +6 -1
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/adapters/cursor.py +6 -1
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/adapters/grok.py +2 -1
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/adapters/opencode.py +5 -1
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/cli.py +89 -78
- memgraph_context_graph-0.9.0/src/memgraph_context_graph/connectors.py +71 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/hooks/cli.py +6 -3
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/hooks/runner.py +46 -121
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/mcp_server.py +20 -6
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/setup/agents.py +82 -3
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/setup/cli.py +41 -2
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/setup/engine.py +31 -10
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/setup/prompts.py +4 -8
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/setup/tool.py +29 -12
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/setup/uninstall.py +11 -5
- memgraph_context_graph-0.9.0/src/memgraph_context_graph/setup/update.py +124 -0
- memgraph_context_graph-0.9.0/src/memgraph_context_graph/terminal.py +38 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/tests/conftest.py +24 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/tests/fake_agent_cli.py +6 -1
- memgraph_context_graph-0.9.0/tests/test_agent_memory_wiring.py +215 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/tests/test_antigravity_cli_adapter.py +46 -7
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/tests/test_capture_log.py +5 -2
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/tests/test_config_cli.py +22 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/tests/test_hook_cli.py +37 -4
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/tests/test_identity.py +32 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/tests/test_setup.py +120 -8
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/tests/test_setup_docker.py +3 -2
- memgraph_context_graph-0.9.0/tests/test_terminal.py +35 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/tests/test_tools.py +2 -1
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/.gitignore +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/CONTEXT.md +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/LICENSE +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/docs/adr/0001-runtime-adapter-terminology.md +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/docs/adr/0002-config-file-only-hook-resolution.md +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/docs/adr/0003-config-path-override.md +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/docs/adr/0004-user-id-in-session-start-event.md +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/docs/adr/0005-config-file-covers-llm-key-and-subprocess-env.md +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/docs/adr/0006-memory-tool-writes-explicit-memory.md +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/docs/research/2026-10-codex-memory.md +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/__init__.py +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/adapters/__init__.py +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/adapters/claude.py +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/adapters/claude_code_memory.py +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/adapters/codex_memory.py +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/adapters/openai.py +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/capture_log.py +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/events.py +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/hooks/__init__.py +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/hooks/runtime_plugin.py +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/link.py +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/memory_backend.py +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/protocols.py +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/setup/__init__.py +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/setup/memgraph.py +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/spaces.py +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/tools.py +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/tests/__init__.py +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/tests/test_claude_code_adapter.py +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/tests/test_codex_adapter.py +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/tests/test_context_before_tool.py +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/tests/test_copilot_cli_adapter.py +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/tests/test_cursor_adapter.py +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/tests/test_dev_scripts.py +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/tests/test_events.py +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/tests/test_grok_adapter.py +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/tests/test_link.py +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/tests/test_opencode_adapter.py +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/tests/test_plugin_hooks.py +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/tests/test_runner_sessions_graph_connector.py +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/tests/test_runtime_plugin.py +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.9.0}/tests/test_spaces.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: memgraph-context-graph
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.9.0
|
|
4
4
|
Summary: Connect agent SDKs to context-graph components (actions-graph, skills-graph, etc.)
|
|
5
5
|
License: MIT
|
|
6
6
|
License-File: LICENSE
|
|
@@ -202,6 +202,7 @@ memgraph-context-graph setup # interactive on a terminal; pl
|
|
|
202
202
|
memgraph-context-graph setup claude-code codex # just these agents
|
|
203
203
|
memgraph-context-graph setup cursor --project-dir ~/work/billing
|
|
204
204
|
memgraph-context-graph setup --plan # print the plan, change nothing
|
|
205
|
+
memgraph-context-graph update [runtime ...] # upgrade the package, refresh the wiring and the schema
|
|
205
206
|
memgraph-context-graph uninstall [runtime ...] # remove the wiring; the graph and config stay
|
|
206
207
|
```
|
|
207
208
|
|
|
@@ -211,6 +212,15 @@ wires each agent, and runs `doctor`'s checks. Every step checks before it acts,
|
|
|
211
212
|
missing. The TUI starts only when stdin and stdout are both terminals; the plugins' `scripts/bootstrap.sh`
|
|
212
213
|
runs `setup <runtime> --no-tui`.
|
|
213
214
|
|
|
215
|
+
**Updating.** Upgrading the package moves the code hooks and the MCP server run, but what setup wrote once stays
|
|
216
|
+
as it was. `update` upgrades the uv tool and its connectors, then, with the new release, refreshes everything
|
|
217
|
+
that is wired: Claude Code's and Codex's plugin (marketplace refreshed, plugin updated), each per-project agent's
|
|
218
|
+
hook file in `--project-dir` (default: the current directory) and its MCP registration, and the graph's indexes
|
|
219
|
+
and constraints. It then runs `doctor`'s checks. Name runtimes to refresh only those; `--no-upgrade` skips the
|
|
220
|
+
package step. Your own hooks, the config and the graph's data are untouched, and running it again changes nothing.
|
|
221
|
+
Running agent sessions keep the hooks they loaded: restart them. Outside the uv tool (a virtualenv, say) the
|
|
222
|
+
package step only warns, and you upgrade it yourself first.
|
|
223
|
+
|
|
214
224
|
**Identity and connection.** setup writes `~/.config/context-graph/config.toml`; hooks read their configuration from that file at runtime (see [Configuration](#configuration) — env vars are **not** read at hook time). To change your identity later:
|
|
215
225
|
|
|
216
226
|
```bash
|
|
@@ -268,6 +278,10 @@ database = "memgraph"
|
|
|
268
278
|
[llm]
|
|
269
279
|
openai_api_key = ""
|
|
270
280
|
anthropic_api_key = ""
|
|
281
|
+
derive_model = "claude-sonnet-5-5"
|
|
282
|
+
|
|
283
|
+
[capture]
|
|
284
|
+
connectors = "skills-graph, actions-graph, sessions-graph"
|
|
271
285
|
|
|
272
286
|
[reconcile]
|
|
273
287
|
auto_reconcile = true
|
|
@@ -275,12 +289,21 @@ auto_reconcile = true
|
|
|
275
289
|
[recall]
|
|
276
290
|
embedding_model = "BAAI/bge-small-en-v1.5"
|
|
277
291
|
turns_k = "8"
|
|
292
|
+
|
|
293
|
+
[extraction]
|
|
294
|
+
model = "fastino/gliner2.5-base-v1"
|
|
295
|
+
entity_threshold = "0.3"
|
|
278
296
|
```
|
|
279
297
|
|
|
280
298
|
`[reconcile]` turns on sessions-graph's auto-trigger reconciliation (see
|
|
281
299
|
[sessions-graph § reconciliation](../sessions-graph/README.md#session-reconciliation)),
|
|
282
300
|
which needs no LLM. `[llm]` is optional: a key only lets reconciliation learn
|
|
283
|
-
the user's own ontology (`sessions-graph derive`)
|
|
301
|
+
the user's own ontology (`sessions-graph derive`), and `derive_model` picks
|
|
302
|
+
the model it calls (default `claude-sonnet-5-5` with Anthropic, `gpt-5` with OpenAI).
|
|
303
|
+
`[capture] connectors` is which graph connectors a hook enables when its
|
|
304
|
+
command names no `--connector`, from `skills-graph`, `actions-graph`,
|
|
305
|
+
`sessions-graph` and `resources-graph`; unset means the first three, and
|
|
306
|
+
an empty value enables none. Setup installs the ones it names.
|
|
284
307
|
`[reconcile]` is omitted entirely from a file setup wrote — absent
|
|
285
308
|
means "never configured," distinct from an explicit `auto_reconcile = false`.
|
|
286
309
|
`[recall]` is optional too: without it, recall runs with the widths the
|
|
@@ -288,6 +311,10 @@ benchmark measured; see [Recall](#recall-memory-for-the-harnesss-model).
|
|
|
288
311
|
`[ontology]` is optional as well: `path` names a schema file the user's
|
|
289
312
|
sessions are extracted under and `derive` is `extend` (the default) or
|
|
290
313
|
`off`; see sessions-graph's [Ontology versions](../sessions-graph/README.md#ontology-versions).
|
|
314
|
+
`[extraction]` tunes how reconciliation, re-extraction and derivation read
|
|
315
|
+
sessions, all optional: `model` (a GLiNER2 checkpoint), `entity_threshold` and
|
|
316
|
+
`relation_threshold` (drop extractions below that confidence, 0 to 1), and
|
|
317
|
+
`chunk_size`/`chunk_overlap` (words per window); unset keys keep GLiNER2's defaults.
|
|
291
318
|
|
|
292
319
|
Manage it with:
|
|
293
320
|
|
|
@@ -296,8 +323,9 @@ memgraph-context-graph config show
|
|
|
296
323
|
memgraph-context-graph config get memgraph.url
|
|
297
324
|
memgraph-context-graph config set <key> <value>
|
|
298
325
|
# keys: identity.user_id, memgraph.{url,user,password,database},
|
|
299
|
-
# llm.{openai_api_key,anthropic_api_key},
|
|
300
|
-
# recall.embedding_model, ontology.{path,derive}, memory.backend
|
|
326
|
+
# llm.{openai_api_key,anthropic_api_key,derive_model}, capture.connectors,
|
|
327
|
+
# reconcile.auto_reconcile, recall.embedding_model, ontology.{path,derive}, memory.backend
|
|
328
|
+
# [recall] widths and [extraction] are edited in the file.
|
|
301
329
|
```
|
|
302
330
|
|
|
303
331
|
Hooks never read environment variables for these values, and neither does setup: it writes only
|
|
@@ -308,15 +336,23 @@ Hooks never read environment variables for these values, and neither does setup:
|
|
|
308
336
|
|
|
309
337
|
The hooks write sessions into Memgraph; recall reads them back. With
|
|
310
338
|
sessions-graph installed, `memgraph-context-graph mcp` serves a `recall(question)`
|
|
311
|
-
tool over stdio MCP
|
|
312
|
-
|
|
339
|
+
tool over stdio MCP. The Codex and Claude Code plugins bundle that server, so
|
|
340
|
+
installing a plugin is all it takes; for the other agents, `setup` registers it
|
|
341
|
+
in each one's own user-level MCP config (see
|
|
342
|
+
[every agent](#every-agent-registration-switch-and-session-start)). The tool returns context, not an answer:
|
|
313
343
|
the dated messages and facts from the user's own sessions that match the
|
|
314
344
|
question, behind a header with the rules for reading them. The harness's model
|
|
315
345
|
answers from those rows.
|
|
316
346
|
|
|
317
|
-
At session start the hook adds
|
|
318
|
-
exists
|
|
319
|
-
|
|
347
|
+
At session start the hook adds a line to the model's context saying the tool
|
|
348
|
+
exists, then where the user left off in this project: the last session's date,
|
|
349
|
+
agent, first and last ask and last reply, and the first ask of the two before
|
|
350
|
+
it, quoted from captured turns. Sessions from any agent count, so a session in
|
|
351
|
+
Grok picks up where one in Codex stopped. It is a pointer, not a recall result:
|
|
352
|
+
nothing is ranked against a question, and the model still calls `recall` when
|
|
353
|
+
one needs memory. A session's project is its checkout's key (the same one
|
|
354
|
+
memory folders use), recorded when it starts. Turn it off with
|
|
355
|
+
`memgraph-context-graph config set session_start.resume false`.
|
|
320
356
|
|
|
321
357
|
A connector may also add a line just before a tool runs
|
|
322
358
|
(`GraphConnector.context_before_tool`) — resources-graph's Nudge that what the
|
|
@@ -347,6 +383,7 @@ from the tool call, so a model can only read its own user's sessions.
|
|
|
347
383
|
| `facts_k`, `fact_turns_k` | `15`, `8` | Facts matched, and the messages they were read from |
|
|
348
384
|
| `user_fact_types`, `user_facts_k` | `2`, `30` | Relation types gathered whole, for counting questions |
|
|
349
385
|
| `turn_chars` | `1500` | Characters shown per message |
|
|
386
|
+
| `query_instruction` | per model | Prefix embedded with the question; bge's English models get their retrieval instruction, other models none |
|
|
350
387
|
|
|
351
388
|
Recall needs Memgraph with MAGE (`memgraph/memgraph-mage`) for its vector lanes;
|
|
352
389
|
without it, it answers from text search and says so. `doctor` checks both
|
|
@@ -359,8 +396,9 @@ was benchmarked in; the JSON is what `--json` prints.
|
|
|
359
396
|
|
|
360
397
|
### Memory: Context Graph as the harness's memory
|
|
361
398
|
|
|
362
|
-
|
|
363
|
-
for a user who
|
|
399
|
+
Every agent `setup` knows can use Context Graph as its memory. Claude Code and
|
|
400
|
+
Codex each keep their own memory, and Context Graph replaces it for a user who
|
|
401
|
+
opts in:
|
|
364
402
|
|
|
365
403
|
```bash
|
|
366
404
|
memgraph-context-graph setup claude-code --memory-backend context-graph
|
|
@@ -431,6 +469,34 @@ name is used. See
|
|
|
431
469
|
[ADR 0006](docs/adr/0006-memory-tool-writes-explicit-memory.md) for why this is
|
|
432
470
|
the one write the model makes over MCP.
|
|
433
471
|
|
|
472
|
+
#### Every agent: registration, switch and session start
|
|
473
|
+
|
|
474
|
+
The other five have no plugin, so `setup <runtime>` registers the
|
|
475
|
+
`context-graph` MCP server in the agent's own user-level config: memory belongs
|
|
476
|
+
to the user, not to one project. It adds the server's tools to the agent's
|
|
477
|
+
persistent allow-list where it keeps one, because a headless run refuses a tool
|
|
478
|
+
that needs approval. `--memory-backend context-graph` then turns the agent's
|
|
479
|
+
own memory off, and `uninstall` undoes both. Every edited file keeps the rest of
|
|
480
|
+
its content and is backed up once to `<name>.context-graph.bak`.
|
|
481
|
+
|
|
482
|
+
| Agent | Built-in memory, and the switch | Server registered in | Session-start text reaches the model through |
|
|
483
|
+
|---|---|---|---|
|
|
484
|
+
| Claude Code | auto memory; `autoMemoryEnabled: false` | the plugin | `SessionStart` `additionalContext` |
|
|
485
|
+
| Codex | memories; three keys in `config.toml` | the plugin | `SessionStart` `additionalContext` (~2,500-token cap) |
|
|
486
|
+
| Copilot CLI | Copilot Memory (on GitHub, not importable); `"memory": false` in `~/.copilot/settings.json` | `~/.copilot/mcp-config.json` (no allow-list: approve once, or `--allow-tool=context-graph`) | `sessionStart` top-level `additionalContext` |
|
|
487
|
+
| Cursor | none (Memories was removed) | `~/.cursor/mcp.json`; allow `Mcp(context-graph:*)` in `~/.cursor/cli-config.json` | `sessionStart` `additional_context` (the IDE's agent window can drop it; untested here) |
|
|
488
|
+
| OpenCode | none | `mcp.servers` in `~/.config/opencode/opencode.json`, `codemode: false`; an allow rule in `permissions` | the plugin's `context` hook, added to the system prompt of every request |
|
|
489
|
+
| Antigravity CLI | none (Knowledge is off in the CLI) | `~/.gemini/config/mcp_config.json`; allow `mcp(context-graph/*)` in `~/.gemini/antigravity-cli/settings.json` | `SessionStart` `injectSteps` system message |
|
|
490
|
+
| Grok Build | topic notes under `~/.grok/memory-v2/`, imported to `/memories/grok/`; `[memory_v2] enabled = false` | `~/.grok/config.toml`; allow `MCPTool(context-graph__*)` | the MCP server's `instructions` (`mcp --session-context`): Grok's start hooks can't reach the model |
|
|
491
|
+
|
|
492
|
+
Antigravity's hooks carry no prompt or reply text, so its turns are read from
|
|
493
|
+
the transcript its `Stop` payload points at; that is what gives its sessions a
|
|
494
|
+
place in recall and in the next session's "where you left off".
|
|
495
|
+
|
|
496
|
+
`live-hooks-e2e.sh memory` checks this against each installed agent: a memory
|
|
497
|
+
written, one read back in a new session, and the next session's index and last
|
|
498
|
+
ask.
|
|
499
|
+
|
|
434
500
|
### Codex and Claude Code Plugins
|
|
435
501
|
|
|
436
502
|
Both runtimes have a distributable Runtime Plugin that wires hooks and the MCP server; marketplace
|
|
@@ -177,6 +177,7 @@ memgraph-context-graph setup # interactive on a terminal; pl
|
|
|
177
177
|
memgraph-context-graph setup claude-code codex # just these agents
|
|
178
178
|
memgraph-context-graph setup cursor --project-dir ~/work/billing
|
|
179
179
|
memgraph-context-graph setup --plan # print the plan, change nothing
|
|
180
|
+
memgraph-context-graph update [runtime ...] # upgrade the package, refresh the wiring and the schema
|
|
180
181
|
memgraph-context-graph uninstall [runtime ...] # remove the wiring; the graph and config stay
|
|
181
182
|
```
|
|
182
183
|
|
|
@@ -186,6 +187,15 @@ wires each agent, and runs `doctor`'s checks. Every step checks before it acts,
|
|
|
186
187
|
missing. The TUI starts only when stdin and stdout are both terminals; the plugins' `scripts/bootstrap.sh`
|
|
187
188
|
runs `setup <runtime> --no-tui`.
|
|
188
189
|
|
|
190
|
+
**Updating.** Upgrading the package moves the code hooks and the MCP server run, but what setup wrote once stays
|
|
191
|
+
as it was. `update` upgrades the uv tool and its connectors, then, with the new release, refreshes everything
|
|
192
|
+
that is wired: Claude Code's and Codex's plugin (marketplace refreshed, plugin updated), each per-project agent's
|
|
193
|
+
hook file in `--project-dir` (default: the current directory) and its MCP registration, and the graph's indexes
|
|
194
|
+
and constraints. It then runs `doctor`'s checks. Name runtimes to refresh only those; `--no-upgrade` skips the
|
|
195
|
+
package step. Your own hooks, the config and the graph's data are untouched, and running it again changes nothing.
|
|
196
|
+
Running agent sessions keep the hooks they loaded: restart them. Outside the uv tool (a virtualenv, say) the
|
|
197
|
+
package step only warns, and you upgrade it yourself first.
|
|
198
|
+
|
|
189
199
|
**Identity and connection.** setup writes `~/.config/context-graph/config.toml`; hooks read their configuration from that file at runtime (see [Configuration](#configuration) — env vars are **not** read at hook time). To change your identity later:
|
|
190
200
|
|
|
191
201
|
```bash
|
|
@@ -243,6 +253,10 @@ database = "memgraph"
|
|
|
243
253
|
[llm]
|
|
244
254
|
openai_api_key = ""
|
|
245
255
|
anthropic_api_key = ""
|
|
256
|
+
derive_model = "claude-sonnet-5-5"
|
|
257
|
+
|
|
258
|
+
[capture]
|
|
259
|
+
connectors = "skills-graph, actions-graph, sessions-graph"
|
|
246
260
|
|
|
247
261
|
[reconcile]
|
|
248
262
|
auto_reconcile = true
|
|
@@ -250,12 +264,21 @@ auto_reconcile = true
|
|
|
250
264
|
[recall]
|
|
251
265
|
embedding_model = "BAAI/bge-small-en-v1.5"
|
|
252
266
|
turns_k = "8"
|
|
267
|
+
|
|
268
|
+
[extraction]
|
|
269
|
+
model = "fastino/gliner2.5-base-v1"
|
|
270
|
+
entity_threshold = "0.3"
|
|
253
271
|
```
|
|
254
272
|
|
|
255
273
|
`[reconcile]` turns on sessions-graph's auto-trigger reconciliation (see
|
|
256
274
|
[sessions-graph § reconciliation](../sessions-graph/README.md#session-reconciliation)),
|
|
257
275
|
which needs no LLM. `[llm]` is optional: a key only lets reconciliation learn
|
|
258
|
-
the user's own ontology (`sessions-graph derive`)
|
|
276
|
+
the user's own ontology (`sessions-graph derive`), and `derive_model` picks
|
|
277
|
+
the model it calls (default `claude-sonnet-5-5` with Anthropic, `gpt-5` with OpenAI).
|
|
278
|
+
`[capture] connectors` is which graph connectors a hook enables when its
|
|
279
|
+
command names no `--connector`, from `skills-graph`, `actions-graph`,
|
|
280
|
+
`sessions-graph` and `resources-graph`; unset means the first three, and
|
|
281
|
+
an empty value enables none. Setup installs the ones it names.
|
|
259
282
|
`[reconcile]` is omitted entirely from a file setup wrote — absent
|
|
260
283
|
means "never configured," distinct from an explicit `auto_reconcile = false`.
|
|
261
284
|
`[recall]` is optional too: without it, recall runs with the widths the
|
|
@@ -263,6 +286,10 @@ benchmark measured; see [Recall](#recall-memory-for-the-harnesss-model).
|
|
|
263
286
|
`[ontology]` is optional as well: `path` names a schema file the user's
|
|
264
287
|
sessions are extracted under and `derive` is `extend` (the default) or
|
|
265
288
|
`off`; see sessions-graph's [Ontology versions](../sessions-graph/README.md#ontology-versions).
|
|
289
|
+
`[extraction]` tunes how reconciliation, re-extraction and derivation read
|
|
290
|
+
sessions, all optional: `model` (a GLiNER2 checkpoint), `entity_threshold` and
|
|
291
|
+
`relation_threshold` (drop extractions below that confidence, 0 to 1), and
|
|
292
|
+
`chunk_size`/`chunk_overlap` (words per window); unset keys keep GLiNER2's defaults.
|
|
266
293
|
|
|
267
294
|
Manage it with:
|
|
268
295
|
|
|
@@ -271,8 +298,9 @@ memgraph-context-graph config show
|
|
|
271
298
|
memgraph-context-graph config get memgraph.url
|
|
272
299
|
memgraph-context-graph config set <key> <value>
|
|
273
300
|
# keys: identity.user_id, memgraph.{url,user,password,database},
|
|
274
|
-
# llm.{openai_api_key,anthropic_api_key},
|
|
275
|
-
# recall.embedding_model, ontology.{path,derive}, memory.backend
|
|
301
|
+
# llm.{openai_api_key,anthropic_api_key,derive_model}, capture.connectors,
|
|
302
|
+
# reconcile.auto_reconcile, recall.embedding_model, ontology.{path,derive}, memory.backend
|
|
303
|
+
# [recall] widths and [extraction] are edited in the file.
|
|
276
304
|
```
|
|
277
305
|
|
|
278
306
|
Hooks never read environment variables for these values, and neither does setup: it writes only
|
|
@@ -283,15 +311,23 @@ Hooks never read environment variables for these values, and neither does setup:
|
|
|
283
311
|
|
|
284
312
|
The hooks write sessions into Memgraph; recall reads them back. With
|
|
285
313
|
sessions-graph installed, `memgraph-context-graph mcp` serves a `recall(question)`
|
|
286
|
-
tool over stdio MCP
|
|
287
|
-
|
|
314
|
+
tool over stdio MCP. The Codex and Claude Code plugins bundle that server, so
|
|
315
|
+
installing a plugin is all it takes; for the other agents, `setup` registers it
|
|
316
|
+
in each one's own user-level MCP config (see
|
|
317
|
+
[every agent](#every-agent-registration-switch-and-session-start)). The tool returns context, not an answer:
|
|
288
318
|
the dated messages and facts from the user's own sessions that match the
|
|
289
319
|
question, behind a header with the rules for reading them. The harness's model
|
|
290
320
|
answers from those rows.
|
|
291
321
|
|
|
292
|
-
At session start the hook adds
|
|
293
|
-
exists
|
|
294
|
-
|
|
322
|
+
At session start the hook adds a line to the model's context saying the tool
|
|
323
|
+
exists, then where the user left off in this project: the last session's date,
|
|
324
|
+
agent, first and last ask and last reply, and the first ask of the two before
|
|
325
|
+
it, quoted from captured turns. Sessions from any agent count, so a session in
|
|
326
|
+
Grok picks up where one in Codex stopped. It is a pointer, not a recall result:
|
|
327
|
+
nothing is ranked against a question, and the model still calls `recall` when
|
|
328
|
+
one needs memory. A session's project is its checkout's key (the same one
|
|
329
|
+
memory folders use), recorded when it starts. Turn it off with
|
|
330
|
+
`memgraph-context-graph config set session_start.resume false`.
|
|
295
331
|
|
|
296
332
|
A connector may also add a line just before a tool runs
|
|
297
333
|
(`GraphConnector.context_before_tool`) — resources-graph's Nudge that what the
|
|
@@ -322,6 +358,7 @@ from the tool call, so a model can only read its own user's sessions.
|
|
|
322
358
|
| `facts_k`, `fact_turns_k` | `15`, `8` | Facts matched, and the messages they were read from |
|
|
323
359
|
| `user_fact_types`, `user_facts_k` | `2`, `30` | Relation types gathered whole, for counting questions |
|
|
324
360
|
| `turn_chars` | `1500` | Characters shown per message |
|
|
361
|
+
| `query_instruction` | per model | Prefix embedded with the question; bge's English models get their retrieval instruction, other models none |
|
|
325
362
|
|
|
326
363
|
Recall needs Memgraph with MAGE (`memgraph/memgraph-mage`) for its vector lanes;
|
|
327
364
|
without it, it answers from text search and says so. `doctor` checks both
|
|
@@ -334,8 +371,9 @@ was benchmarked in; the JSON is what `--json` prints.
|
|
|
334
371
|
|
|
335
372
|
### Memory: Context Graph as the harness's memory
|
|
336
373
|
|
|
337
|
-
|
|
338
|
-
for a user who
|
|
374
|
+
Every agent `setup` knows can use Context Graph as its memory. Claude Code and
|
|
375
|
+
Codex each keep their own memory, and Context Graph replaces it for a user who
|
|
376
|
+
opts in:
|
|
339
377
|
|
|
340
378
|
```bash
|
|
341
379
|
memgraph-context-graph setup claude-code --memory-backend context-graph
|
|
@@ -406,6 +444,34 @@ name is used. See
|
|
|
406
444
|
[ADR 0006](docs/adr/0006-memory-tool-writes-explicit-memory.md) for why this is
|
|
407
445
|
the one write the model makes over MCP.
|
|
408
446
|
|
|
447
|
+
#### Every agent: registration, switch and session start
|
|
448
|
+
|
|
449
|
+
The other five have no plugin, so `setup <runtime>` registers the
|
|
450
|
+
`context-graph` MCP server in the agent's own user-level config: memory belongs
|
|
451
|
+
to the user, not to one project. It adds the server's tools to the agent's
|
|
452
|
+
persistent allow-list where it keeps one, because a headless run refuses a tool
|
|
453
|
+
that needs approval. `--memory-backend context-graph` then turns the agent's
|
|
454
|
+
own memory off, and `uninstall` undoes both. Every edited file keeps the rest of
|
|
455
|
+
its content and is backed up once to `<name>.context-graph.bak`.
|
|
456
|
+
|
|
457
|
+
| Agent | Built-in memory, and the switch | Server registered in | Session-start text reaches the model through |
|
|
458
|
+
|---|---|---|---|
|
|
459
|
+
| Claude Code | auto memory; `autoMemoryEnabled: false` | the plugin | `SessionStart` `additionalContext` |
|
|
460
|
+
| Codex | memories; three keys in `config.toml` | the plugin | `SessionStart` `additionalContext` (~2,500-token cap) |
|
|
461
|
+
| Copilot CLI | Copilot Memory (on GitHub, not importable); `"memory": false` in `~/.copilot/settings.json` | `~/.copilot/mcp-config.json` (no allow-list: approve once, or `--allow-tool=context-graph`) | `sessionStart` top-level `additionalContext` |
|
|
462
|
+
| Cursor | none (Memories was removed) | `~/.cursor/mcp.json`; allow `Mcp(context-graph:*)` in `~/.cursor/cli-config.json` | `sessionStart` `additional_context` (the IDE's agent window can drop it; untested here) |
|
|
463
|
+
| OpenCode | none | `mcp.servers` in `~/.config/opencode/opencode.json`, `codemode: false`; an allow rule in `permissions` | the plugin's `context` hook, added to the system prompt of every request |
|
|
464
|
+
| Antigravity CLI | none (Knowledge is off in the CLI) | `~/.gemini/config/mcp_config.json`; allow `mcp(context-graph/*)` in `~/.gemini/antigravity-cli/settings.json` | `SessionStart` `injectSteps` system message |
|
|
465
|
+
| Grok Build | topic notes under `~/.grok/memory-v2/`, imported to `/memories/grok/`; `[memory_v2] enabled = false` | `~/.grok/config.toml`; allow `MCPTool(context-graph__*)` | the MCP server's `instructions` (`mcp --session-context`): Grok's start hooks can't reach the model |
|
|
466
|
+
|
|
467
|
+
Antigravity's hooks carry no prompt or reply text, so its turns are read from
|
|
468
|
+
the transcript its `Stop` payload points at; that is what gives its sessions a
|
|
469
|
+
place in recall and in the next session's "where you left off".
|
|
470
|
+
|
|
471
|
+
`live-hooks-e2e.sh memory` checks this against each installed agent: a memory
|
|
472
|
+
written, one read back in a new session, and the next session's index and last
|
|
473
|
+
ask.
|
|
474
|
+
|
|
409
475
|
### Codex and Claude Code Plugins
|
|
410
476
|
|
|
411
477
|
Both runtimes have a distributable Runtime Plugin that wires hooks and the MCP server; marketplace
|
|
@@ -5,12 +5,13 @@ Protocol, then routes those events to graph connectors. The installed command
|
|
|
5
5
|
is the same for every runtime:
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
memgraph-context-graph hook run <runtime>
|
|
9
|
-
--connector skills-graph \
|
|
10
|
-
--connector actions-graph \
|
|
11
|
-
--connector sessions-graph
|
|
8
|
+
memgraph-context-graph hook run <runtime>
|
|
12
9
|
```
|
|
13
10
|
|
|
11
|
+
It enables the graph connectors the config file's `[capture] connectors`
|
|
12
|
+
names (unset: `skills-graph`, `actions-graph`, `sessions-graph`). Repeated
|
|
13
|
+
`--connector` flags override that for one hook command.
|
|
14
|
+
|
|
14
15
|
Runtime registrations are discovered through the
|
|
15
16
|
`memgraph_context_graph.runtimes` Python entry-point group.
|
|
16
17
|
|
|
@@ -36,8 +37,9 @@ memgraph-context-graph hook init antigravity-cli
|
|
|
36
37
|
memgraph-context-graph hook init grok
|
|
37
38
|
```
|
|
38
39
|
|
|
39
|
-
`hook init`
|
|
40
|
-
`--connector` flags
|
|
40
|
+
`hook init` writes hook commands without `--connector` flags, so they enable
|
|
41
|
+
`[capture] connectors`. Repeated `--connector` flags pin a set into the
|
|
42
|
+
generated commands instead. Existing Copilot, Cursor, Antigravity, and Grok
|
|
41
43
|
JSON documents are merged without removing unrelated settings or hooks.
|
|
42
44
|
OpenCode's generated plugin is replaced only with `--force`.
|
|
43
45
|
|
|
@@ -139,15 +141,15 @@ never deletes its session, so those sessions only ever see turn ends.
|
|
|
139
141
|
Antigravity CLI (`agy`, the successor to Gemini CLI) reads named hook groups
|
|
140
142
|
from `.agents/hooks.json`; `hook init` owns the `memgraph-context-graph` group and
|
|
141
143
|
merges it beside any others. Only the tool events (`PreToolUse`,
|
|
142
|
-
`PostToolUse`) take matcher groups. `
|
|
144
|
+
`PostToolUse`) take matcher groups. `SessionStart` and `Stop` are written as
|
|
143
145
|
flat handlers, because `agy` rejects the whole file when a lifecycle event is
|
|
144
146
|
nested. Timeouts are in seconds.
|
|
145
147
|
|
|
146
148
|
```json
|
|
147
149
|
{
|
|
148
150
|
"memgraph-context-graph": {
|
|
149
|
-
"
|
|
150
|
-
{"type": "command", "command": "memgraph-context-graph hook run antigravity-cli --event-name
|
|
151
|
+
"SessionStart": [
|
|
152
|
+
{"type": "command", "command": "memgraph-context-graph hook run antigravity-cli --event-name SessionStart", "timeout": 30}
|
|
151
153
|
],
|
|
152
154
|
"PreToolUse": [
|
|
153
155
|
{"hooks": [{"type": "command", "command": "memgraph-context-graph hook run antigravity-cli --event-name PreToolUse", "timeout": 30}]}
|
|
@@ -246,12 +248,11 @@ memgraph-context-graph setup antigravity-cli --project-dir "$PWD"
|
|
|
246
248
|
Smoke-test a registration and its configured connectors:
|
|
247
249
|
|
|
248
250
|
```bash
|
|
249
|
-
memgraph-context-graph doctor --runtime cursor
|
|
250
|
-
--connector skills-graph \
|
|
251
|
-
--connector actions-graph \
|
|
252
|
-
--connector sessions-graph
|
|
251
|
+
memgraph-context-graph doctor --runtime cursor
|
|
253
252
|
```
|
|
254
253
|
|
|
254
|
+
`doctor` checks the `[capture] connectors` set unless `--connector` flags name others.
|
|
255
|
+
|
|
255
256
|
Each runtime provides its own native probe payload; `doctor` does not assume
|
|
256
257
|
that every runtime calls its session-end event `Stop`.
|
|
257
258
|
|
|
@@ -85,6 +85,11 @@ class HookConfig:
|
|
|
85
85
|
memgraph_database: str = _MEMGRAPH_DEFAULTS["database"]
|
|
86
86
|
openai_api_key: str = _LLM_DEFAULTS["openai_api_key"]
|
|
87
87
|
anthropic_api_key: str = _LLM_DEFAULTS["anthropic_api_key"]
|
|
88
|
+
#: ``[llm] derive_model``: the model ontology derivation calls; None means the provider's default.
|
|
89
|
+
derive_model: str | None = None
|
|
90
|
+
#: ``[capture] connectors``: the graph connectors a hook enables when its command names none;
|
|
91
|
+
#: None (never set) means memgraph_context_graph.connectors.DEFAULT_CONNECTORS.
|
|
92
|
+
connectors: tuple[str, ...] | None = None
|
|
88
93
|
auto_reconcile: bool | None = None
|
|
89
94
|
#: The model recall embeds with; None means the consumer's own default.
|
|
90
95
|
embedding_model: str | None = None
|
|
@@ -95,12 +100,18 @@ class HookConfig:
|
|
|
95
100
|
ontology_path: str | None = None
|
|
96
101
|
#: ``[ontology] derive``: "extend" (the default) or "off"; see sessions-graph's ontology module.
|
|
97
102
|
ontology_derive: str | None = None
|
|
103
|
+
#: ``[extraction]`` keys (GLiNER2 checkpoint, thresholds, chunking), as written: sessions-graph
|
|
104
|
+
#: validates them, this module only keeps them across rewrites.
|
|
105
|
+
extraction_settings: dict[str, str] = field(default_factory=dict)
|
|
98
106
|
#: ``[github] token``: overrides the ``gh`` login resources-graph's Sweep otherwise fetches with.
|
|
99
107
|
github_token: str | None = None
|
|
100
108
|
#: ``[memory] backend``: ``"context-graph"`` when the user has handed their
|
|
101
109
|
#: harness memory to the graph (the ``memory`` tool and session-start index
|
|
102
110
|
#: are on); ``"native"`` or None leaves the harness's own memory in charge.
|
|
103
111
|
memory_backend: str | None = None
|
|
112
|
+
#: ``[session_start] resume``: whether a session starts with a brief of the
|
|
113
|
+
#: previous sessions in its project. None (never set) means on.
|
|
114
|
+
session_resume: bool | None = None
|
|
104
115
|
#: The ``section.key`` names the file actually sets (top-level keys as ``key``), so a check can tell
|
|
105
116
|
#: "left at the default" from "written": Enterprise refuses an explicit ``identity.user_id`` or
|
|
106
117
|
#: ``memgraph.database``, which would quietly override what the login decides.
|
|
@@ -183,7 +194,7 @@ def resolve_auto_reconcile() -> bool:
|
|
|
183
194
|
"""Resolve whether sessions-graph should auto-trigger reconciliation on SESSION_END.
|
|
184
195
|
|
|
185
196
|
Config file only (ADR 0002); never configured means ``False``, matching
|
|
186
|
-
``SessionsGraphConnector``'s default
|
|
197
|
+
``SessionsGraphConnector``'s default.
|
|
187
198
|
"""
|
|
188
199
|
return bool(load_config().auto_reconcile)
|
|
189
200
|
|
|
@@ -193,6 +204,16 @@ def resolve_embedding_model() -> str | None:
|
|
|
193
204
|
return load_config().embedding_model
|
|
194
205
|
|
|
195
206
|
|
|
207
|
+
def resolve_extraction() -> dict[str, str]:
|
|
208
|
+
"""The configured ``[extraction]`` keys, as written; sessions-graph owns their meaning."""
|
|
209
|
+
return dict(load_config().extraction_settings)
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def resolve_derive_model() -> str | None:
|
|
213
|
+
"""The configured ``[llm] derive_model``, or None to leave the provider's default."""
|
|
214
|
+
return load_config().derive_model
|
|
215
|
+
|
|
216
|
+
|
|
196
217
|
def resolve_ontology() -> tuple[str | None, str | None]:
|
|
197
218
|
"""The configured ``[ontology]`` (path, derive), each None when unset; sessions-graph owns their meaning."""
|
|
198
219
|
config = load_config()
|
|
@@ -216,12 +237,15 @@ def write_config(
|
|
|
216
237
|
memgraph_database: str | None = None,
|
|
217
238
|
openai_api_key: str | None = None,
|
|
218
239
|
anthropic_api_key: str | None = None,
|
|
240
|
+
derive_model: str | None = None,
|
|
241
|
+
connectors: tuple[str, ...] | None = None,
|
|
219
242
|
auto_reconcile: bool | None = None,
|
|
220
243
|
embedding_model: str | None = None,
|
|
221
244
|
ontology_path: str | None = None,
|
|
222
245
|
ontology_derive: str | None = None,
|
|
223
246
|
github_token: str | None = None,
|
|
224
247
|
memory_backend: str | None = None,
|
|
248
|
+
session_resume: bool | None = None,
|
|
225
249
|
) -> Path:
|
|
226
250
|
"""Write or update the config file. Returns the path written to.
|
|
227
251
|
|
|
@@ -237,12 +261,15 @@ def write_config(
|
|
|
237
261
|
"memgraph_database": memgraph_database,
|
|
238
262
|
"openai_api_key": openai_api_key,
|
|
239
263
|
"anthropic_api_key": anthropic_api_key,
|
|
264
|
+
"derive_model": derive_model,
|
|
265
|
+
"connectors": connectors,
|
|
240
266
|
"auto_reconcile": auto_reconcile,
|
|
241
267
|
"embedding_model": embedding_model,
|
|
242
268
|
"ontology_path": ontology_path,
|
|
243
269
|
"ontology_derive": ontology_derive,
|
|
244
270
|
"github_token": github_token,
|
|
245
271
|
"memory_backend": memory_backend,
|
|
272
|
+
"session_resume": session_resume,
|
|
246
273
|
}
|
|
247
274
|
config = replace(_read_config_file(), **{key: value for key, value in updates.items() if value is not None})
|
|
248
275
|
return _write_config_file(_render_config(config))
|
|
@@ -261,8 +288,8 @@ def write_full_config(
|
|
|
261
288
|
) -> Path:
|
|
262
289
|
"""Write a complete config file, resetting identity, Memgraph and LLM settings to what is given.
|
|
263
290
|
|
|
264
|
-
Every other section (``[
|
|
265
|
-
edition are kept from the existing file, since only ``config set`` or a hand edit ever sets them.
|
|
291
|
+
Every other section (``[capture]``, ``[reconcile]``, ``[recall]``, ``[ontology]``, ``[extraction]``,
|
|
292
|
+
``[github]``, ``[memory]``, ``[session_start]``), ``[llm] derive_model`` and the edition are kept from the existing file, since only ``config set`` or a hand edit ever sets them.
|
|
266
293
|
``auto_reconcile`` is kept likewise unless given here. :func:`write_config` is the partial update.
|
|
267
294
|
"""
|
|
268
295
|
existing = _read_config_file()
|
|
@@ -308,11 +335,14 @@ def _read_config_file() -> HookConfig:
|
|
|
308
335
|
identity = sections.get("identity", {})
|
|
309
336
|
memgraph = sections.get("memgraph", {})
|
|
310
337
|
llm = sections.get("llm", {})
|
|
338
|
+
capture = sections.get("capture", {})
|
|
311
339
|
reconcile = sections.get("reconcile", {})
|
|
312
340
|
recall = sections.get("recall", {})
|
|
313
341
|
ontology = sections.get("ontology", {})
|
|
342
|
+
extraction = sections.get("extraction", {})
|
|
314
343
|
github = sections.get("github", {})
|
|
315
344
|
memory = sections.get("memory", {})
|
|
345
|
+
session_start = sections.get("session_start", {})
|
|
316
346
|
auto_reconcile_raw = reconcile.get("auto_reconcile")
|
|
317
347
|
|
|
318
348
|
return HookConfig(
|
|
@@ -324,13 +354,17 @@ def _read_config_file() -> HookConfig:
|
|
|
324
354
|
memgraph_database=memgraph.get("database") or _MEMGRAPH_DEFAULTS["database"],
|
|
325
355
|
openai_api_key=llm.get("openai_api_key", _LLM_DEFAULTS["openai_api_key"]),
|
|
326
356
|
anthropic_api_key=llm.get("anthropic_api_key", _LLM_DEFAULTS["anthropic_api_key"]),
|
|
357
|
+
derive_model=llm.get("derive_model") or None,
|
|
358
|
+
connectors=_parse_list(capture["connectors"]) if "connectors" in capture else None,
|
|
327
359
|
auto_reconcile=parse_bool_flag(auto_reconcile_raw) if auto_reconcile_raw is not None else None,
|
|
328
360
|
embedding_model=recall.get("embedding_model") or None,
|
|
329
361
|
recall_settings={key: value for key, value in recall.items() if key != "embedding_model"},
|
|
330
362
|
ontology_path=ontology.get("path") or None,
|
|
331
363
|
ontology_derive=ontology.get("derive") or None,
|
|
364
|
+
extraction_settings=dict(extraction),
|
|
332
365
|
github_token=github.get("token") or None,
|
|
333
366
|
memory_backend=memory.get("backend") or None,
|
|
367
|
+
session_resume=parse_bool_flag(session_start["resume"]) if session_start.get("resume") else None,
|
|
334
368
|
present=frozenset(
|
|
335
369
|
f"{section}.{key}" if section else key
|
|
336
370
|
for section, keys in sections.items()
|
|
@@ -341,6 +375,11 @@ def _read_config_file() -> HookConfig:
|
|
|
341
375
|
)
|
|
342
376
|
|
|
343
377
|
|
|
378
|
+
def _parse_list(value: str) -> tuple[str, ...]:
|
|
379
|
+
"""A comma-separated value (``"a, b"``) as its non-blank items; the parser has no arrays."""
|
|
380
|
+
return tuple(item.strip() for item in value.split(",") if item.strip())
|
|
381
|
+
|
|
382
|
+
|
|
344
383
|
def _parse_toml(path: Path, problems: list[str] | None = None) -> dict[str, dict[str, str]]:
|
|
345
384
|
"""Minimal TOML parser — handles [section] and key = "value" pairs.
|
|
346
385
|
|
|
@@ -379,8 +418,9 @@ def _render_config(config: HookConfig) -> str:
|
|
|
379
418
|
"""Render the full config file content.
|
|
380
419
|
|
|
381
420
|
``[reconcile]`` is omitted while ``auto_reconcile`` is ``None`` so a re-read keeps "never configured"
|
|
382
|
-
distinct from an explicit ``false
|
|
383
|
-
|
|
421
|
+
distinct from an explicit ``false``, and ``[session_start]`` likewise while ``session_resume`` is;
|
|
422
|
+
``[capture]`` likewise while ``connectors`` is; ``[recall]``, ``[ontology]``, ``[extraction]``, ``[github]``
|
|
423
|
+
and ``[memory]`` are omitted while they hold nothing.
|
|
384
424
|
"""
|
|
385
425
|
user_id = config.user_id or ""
|
|
386
426
|
lines = [
|
|
@@ -400,7 +440,10 @@ def _render_config(config: HookConfig) -> str:
|
|
|
400
440
|
"[llm]",
|
|
401
441
|
f'openai_api_key = "{config.openai_api_key}"',
|
|
402
442
|
f'anthropic_api_key = "{config.anthropic_api_key}"',
|
|
443
|
+
*([f'derive_model = "{config.derive_model}"'] if config.derive_model else []),
|
|
403
444
|
]
|
|
445
|
+
if config.connectors is not None:
|
|
446
|
+
lines += ["", "[capture]", f'connectors = "{", ".join(config.connectors)}"']
|
|
404
447
|
if config.auto_reconcile is not None:
|
|
405
448
|
lines += ["", "[reconcile]", f"auto_reconcile = {'true' if config.auto_reconcile else 'false'}"]
|
|
406
449
|
recall = (
|
|
@@ -415,10 +458,14 @@ def _render_config(config: HookConfig) -> str:
|
|
|
415
458
|
}
|
|
416
459
|
if ontology:
|
|
417
460
|
lines += ["", "[ontology]", *(f'{key} = "{value}"' for key, value in ontology.items())]
|
|
461
|
+
if config.extraction_settings:
|
|
462
|
+
lines += ["", "[extraction]", *(f'{key} = "{value}"' for key, value in config.extraction_settings.items())]
|
|
418
463
|
if config.github_token:
|
|
419
464
|
lines += ["", "[github]", f'token = "{config.github_token}"']
|
|
420
465
|
if config.memory_backend:
|
|
421
466
|
lines += ["", "[memory]", f'backend = "{config.memory_backend}"']
|
|
467
|
+
if config.session_resume is not None:
|
|
468
|
+
lines += ["", "[session_start]", f"resume = {'true' if config.session_resume else 'false'}"]
|
|
422
469
|
lines.append("")
|
|
423
470
|
return "\n".join(lines)
|
|
424
471
|
|
|
@@ -25,6 +25,24 @@ function capture(payload) {
|
|
|
25
25
|
})
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
// The hook's stdout, parsed: what the hook wants added to the session, or null.
|
|
29
|
+
function ask(payload) {
|
|
30
|
+
return new Promise((resolve) => {
|
|
31
|
+
const child = spawn(command[0], command.slice(1), { stdio: ["pipe", "pipe", "inherit"] })
|
|
32
|
+
let out = ""
|
|
33
|
+
child.stdout.on("data", (chunk) => (out += chunk))
|
|
34
|
+
child.on("error", () => resolve(null))
|
|
35
|
+
child.on("close", () => {
|
|
36
|
+
try {
|
|
37
|
+
resolve(out.trim() ? JSON.parse(out) : null)
|
|
38
|
+
} catch {
|
|
39
|
+
resolve(null)
|
|
40
|
+
}
|
|
41
|
+
})
|
|
42
|
+
child.stdin.end(JSON.stringify(payload))
|
|
43
|
+
})
|
|
44
|
+
}
|
|
45
|
+
|
|
28
46
|
export default {
|
|
29
47
|
id: "memgraph.memgraph-context-graph",
|
|
30
48
|
async setup(ctx) {
|
|
@@ -43,6 +61,24 @@ export default {
|
|
|
43
61
|
return capture({ hook_event_name: "session.created", session_id: sessionID, cwd, model })
|
|
44
62
|
}
|
|
45
63
|
|
|
64
|
+
// The session-start text (memory index, where the user left off), asked for once per session. The context
|
|
65
|
+
// hook runs before every model request and rebuilds the system prompt each time, so it is re-added from
|
|
66
|
+
// this cache on each one; compaction and title generation build their own requests and get it too.
|
|
67
|
+
const sessionContext = new Map()
|
|
68
|
+
const addSessionContext = async (event) => {
|
|
69
|
+
if (!sessionContext.has(event.sessionID)) {
|
|
70
|
+
sessionContext.set(
|
|
71
|
+
event.sessionID,
|
|
72
|
+
ask({ hook_event_name: "session.context", session_id: event.sessionID, cwd: ctx.location?.directory }),
|
|
73
|
+
)
|
|
74
|
+
}
|
|
75
|
+
const response = await sessionContext.get(event.sessionID)
|
|
76
|
+
if (typeof response?.system === "string" && response.system) event.system.push({ type: "text", text: response.system })
|
|
77
|
+
}
|
|
78
|
+
for (const name of ["context", "compaction", "generate"]) {
|
|
79
|
+
registrations.push(await ctx.session.hook(name, addSessionContext))
|
|
80
|
+
}
|
|
81
|
+
|
|
46
82
|
registrations.push(await ctx.session.hook("prompt", async (event) => {
|
|
47
83
|
openTurns.add(event.sessionID)
|
|
48
84
|
await start(event.sessionID, ctx.location?.directory)
|