memgraph-context-graph 0.7.0__tar.gz → 0.7.2__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.0 → memgraph_context_graph-0.7.2}/CONTEXT.md +2 -2
- {memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/PKG-INFO +6 -59
- {memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/README.md +5 -58
- {memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/docs/command-hooks.md +1 -1
- {memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/pyproject.toml +1 -1
- {memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/src/memgraph_context_graph/__init__.py +1 -2
- {memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/src/memgraph_context_graph/adapters/_identity.py +76 -179
- {memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/src/memgraph_context_graph/adapters/_spec.py +48 -9
- {memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/src/memgraph_context_graph/adapters/claude.py +5 -23
- {memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/src/memgraph_context_graph/adapters/claude_code.py +11 -44
- {memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/src/memgraph_context_graph/adapters/codex.py +5 -4
- {memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/src/memgraph_context_graph/adapters/copilot_cli.py +2 -1
- {memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/src/memgraph_context_graph/adapters/cursor.py +5 -5
- {memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/src/memgraph_context_graph/adapters/grok.py +13 -41
- {memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/src/memgraph_context_graph/adapters/openai.py +3 -6
- {memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/src/memgraph_context_graph/adapters/opencode.py +3 -6
- {memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/src/memgraph_context_graph/cli.py +43 -67
- {memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/src/memgraph_context_graph/events.py +1 -6
- {memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/src/memgraph_context_graph/hooks/cli.py +5 -9
- {memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/src/memgraph_context_graph/hooks/runner.py +23 -29
- {memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/src/memgraph_context_graph/hooks/runtime_plugin.py +6 -13
- {memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/src/memgraph_context_graph/memory_backend.py +1 -0
- {memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/src/memgraph_context_graph/setup/cli.py +3 -5
- {memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/src/memgraph_context_graph/setup/engine.py +7 -3
- {memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/src/memgraph_context_graph/setup/memgraph.py +15 -0
- {memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/src/memgraph_context_graph/setup/prompts.py +27 -21
- {memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/src/memgraph_context_graph/setup/tool.py +5 -4
- {memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/src/memgraph_context_graph/setup/uninstall.py +2 -2
- {memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/src/memgraph_context_graph/tools.py +4 -3
- {memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/tests/test_codex_adapter.py +19 -0
- {memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/tests/test_setup.py +47 -5
- {memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/tests/test_setup_docker.py +17 -1
- {memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/.gitignore +0 -0
- {memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/LICENSE +0 -0
- {memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/docs/adr/0001-runtime-adapter-terminology.md +0 -0
- {memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/docs/adr/0002-config-file-only-hook-resolution.md +0 -0
- {memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/docs/adr/0003-config-path-override.md +0 -0
- {memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/docs/adr/0004-user-id-in-session-start-event.md +0 -0
- {memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/docs/adr/0005-config-file-covers-llm-key-and-subprocess-env.md +0 -0
- {memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/docs/adr/0006-memory-tool-writes-explicit-memory.md +0 -0
- {memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/docs/research/2026-10-codex-memory.md +0 -0
- {memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/src/memgraph_context_graph/adapters/__init__.py +0 -0
- {memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/src/memgraph_context_graph/adapters/_opencode_plugin.js +0 -0
- {memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/src/memgraph_context_graph/adapters/antigravity_cli.py +0 -0
- {memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/src/memgraph_context_graph/adapters/claude_code_memory.py +0 -0
- {memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/src/memgraph_context_graph/adapters/codex_memory.py +0 -0
- {memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/src/memgraph_context_graph/capture_log.py +0 -0
- {memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/src/memgraph_context_graph/hooks/__init__.py +0 -0
- {memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/src/memgraph_context_graph/link.py +0 -0
- {memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/src/memgraph_context_graph/mcp_server.py +0 -0
- {memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/src/memgraph_context_graph/protocols.py +0 -0
- {memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/src/memgraph_context_graph/setup/__init__.py +0 -0
- {memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/src/memgraph_context_graph/setup/agents.py +0 -0
- {memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/src/memgraph_context_graph/spaces.py +0 -0
- {memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/tests/__init__.py +0 -0
- {memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/tests/conftest.py +0 -0
- {memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/tests/fake_agent_cli.py +0 -0
- {memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/tests/test_antigravity_cli_adapter.py +0 -0
- {memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/tests/test_capture_log.py +0 -0
- {memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/tests/test_claude_code_adapter.py +0 -0
- {memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/tests/test_config_cli.py +0 -0
- {memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/tests/test_context_before_tool.py +0 -0
- {memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/tests/test_copilot_cli_adapter.py +0 -0
- {memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/tests/test_cursor_adapter.py +0 -0
- {memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/tests/test_dev_scripts.py +0 -0
- {memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/tests/test_events.py +0 -0
- {memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/tests/test_grok_adapter.py +0 -0
- {memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/tests/test_hook_cli.py +0 -0
- {memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/tests/test_identity.py +0 -0
- {memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/tests/test_link.py +0 -0
- {memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/tests/test_opencode_adapter.py +0 -0
- {memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/tests/test_plugin_hooks.py +0 -0
- {memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/tests/test_runner_sessions_graph_connector.py +0 -0
- {memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/tests/test_runtime_plugin.py +0 -0
- {memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/tests/test_spaces.py +0 -0
- {memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/tests/test_tools.py +0 -0
|
@@ -85,7 +85,7 @@ _Avoid_: MCP tool (the protocol it is served over, not the thing), Graph Connect
|
|
|
85
85
|
- **Claude Code Plugin** installs Claude Code hook wiring invoking the Claude Code runtime adapter command.
|
|
86
86
|
- **Event Protocol** carries agent activity, not graph semantics.
|
|
87
87
|
- **Agent Session** may be persisted as a session node by a graph component; **Event Protocol** only carries the session identifier.
|
|
88
|
-
- Tool/message events carry `agent_name` when runtime identifies a subagent. Memgraph Context Graph only transports this id; Graph Connectors decide how to use it. Codex adapter
|
|
88
|
+
- Tool/message events carry `agent_name` when runtime identifies a subagent. Memgraph Context Graph only transports this id; Graph Connectors decide how to use it. The Codex adapter emits no subagent lifecycle events.
|
|
89
89
|
- Runtime Plugin's generated command calls `hook run <name>`. CLI resolves name via Runtime Registration; plugin never names an adapter class directly.
|
|
90
90
|
- Built-in Runtime Registrations: `codex`, `claude-code`, `copilot-cli`, `cursor`, `opencode`, `antigravity-cli`, and `grok`. Other packages may publish more.
|
|
91
91
|
|
|
@@ -110,4 +110,4 @@ _Avoid_: MCP tool (the protocol it is served over, not the thing), Graph Connect
|
|
|
110
110
|
- "Session" = activity or graph state? Use **Agent Session** for activity, "session node" for persisted state.
|
|
111
111
|
- "Plugin" can sound like a graph extension. Use **Runtime Plugin** for host-specific distribution; Graph Connectors own graph interpretation.
|
|
112
112
|
- Entry-point discovery = **Runtime Registration**, not a runtime/CLI plugin ([#269](https://github.com/memgraph/ai-toolkit/issues/269)).
|
|
113
|
-
- Code hasn't fully caught up to **Runtime Adapter** terminology: Event Protocol's `source_sdk` field (`events.py`, set by
|
|
113
|
+
- Code hasn't fully caught up to **Runtime Adapter** terminology: Event Protocol's `source_sdk` field (`events.py`, set by every adapter, read by Actions Graph's connector) still names the rejected term (ADR 0001); a rename to e.g. `source_adapter` is not done.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: memgraph-context-graph
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.2
|
|
4
4
|
Summary: Connect agent SDKs to context-graph components (actions-graph, skills-graph, etc.)
|
|
5
5
|
License: MIT
|
|
6
6
|
License-File: LICENSE
|
|
@@ -431,34 +431,11 @@ name is used. See
|
|
|
431
431
|
[ADR 0006](docs/adr/0006-memory-tool-writes-explicit-memory.md) for why this is
|
|
432
432
|
the one write the model makes over MCP.
|
|
433
433
|
|
|
434
|
-
###
|
|
434
|
+
### Codex and Claude Code Plugins
|
|
435
435
|
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
```text
|
|
441
|
-
Codex Plugin -> Codex Runtime Adapter -> Event Protocol -> Graph Connector -> Memgraph
|
|
442
|
-
```
|
|
443
|
-
|
|
444
|
-
The plugin installs Codex hook wiring. The Codex runtime adapter normalizes the hook payload. Graph connectors such as `SkillGraphConnector` decide what those events mean in their graph.
|
|
445
|
-
|
|
446
|
-
Plugin source:
|
|
447
|
-
|
|
448
|
-
```text
|
|
449
|
-
context-graph/plugins/memgraph-context-graph-codex
|
|
450
|
-
```
|
|
451
|
-
|
|
452
|
-
Register the public Git-backed marketplace and install the plugin:
|
|
453
|
-
|
|
454
|
-
```bash
|
|
455
|
-
codex plugin marketplace add memgraph/ai-toolkit --sparse .agents/plugins
|
|
456
|
-
codex plugin add context-graph@context-graph-plugins
|
|
457
|
-
```
|
|
458
|
-
|
|
459
|
-
Both are non-interactive; `codex plugin add` installs and enables the plugin in one step (`codex plugin list --json` confirms `"enabled": true`).
|
|
460
|
-
|
|
461
|
-
Check the installed hook environment with:
|
|
436
|
+
Both runtimes have a distributable Runtime Plugin that wires hooks and the MCP server; marketplace
|
|
437
|
+
install steps are in [plugins/memgraph-context-graph-codex](../plugins/memgraph-context-graph-codex/README.md) and
|
|
438
|
+
[plugins/memgraph-context-graph-claude](../plugins/memgraph-context-graph-claude/README.md). Check an install with:
|
|
462
439
|
|
|
463
440
|
```bash
|
|
464
441
|
memgraph-context-graph doctor --runtime codex --connector skills-graph --connector actions-graph --connector sessions-graph
|
|
@@ -466,34 +443,6 @@ memgraph-context-graph doctor --runtime codex --connector skills-graph --connect
|
|
|
466
443
|
|
|
467
444
|
Graph credentials live in `~/.config/context-graph/config.toml` (written by `setup`/`config set`), not in plugin hook files or the process environment — hooks read that file at runtime. See [Configuration](#configuration).
|
|
468
445
|
|
|
469
|
-
### Claude Code Plugin
|
|
470
|
-
|
|
471
|
-
Claude Code hook configuration can be installed as a Claude Code plugin.
|
|
472
|
-
|
|
473
|
-
The runtime-plugin flow is:
|
|
474
|
-
|
|
475
|
-
```text
|
|
476
|
-
Claude Code Plugin -> Claude Code Runtime Adapter -> Event Protocol -> Graph Connector -> Memgraph
|
|
477
|
-
```
|
|
478
|
-
|
|
479
|
-
For a public Git-backed marketplace install, add the marketplace inside Claude Code:
|
|
480
|
-
|
|
481
|
-
```text
|
|
482
|
-
/plugin marketplace add memgraph/ai-toolkit
|
|
483
|
-
```
|
|
484
|
-
|
|
485
|
-
Then install:
|
|
486
|
-
|
|
487
|
-
```text
|
|
488
|
-
/plugin install context-graph@context-graph-plugins
|
|
489
|
-
```
|
|
490
|
-
|
|
491
|
-
Check the installed hook environment with:
|
|
492
|
-
|
|
493
|
-
```bash
|
|
494
|
-
memgraph-context-graph doctor --runtime claude-code --connector skills-graph --connector actions-graph --connector sessions-graph
|
|
495
|
-
```
|
|
496
|
-
|
|
497
446
|
### Source Development
|
|
498
447
|
|
|
499
448
|
For source development and per-project experiments, you can generate local Codex hook files:
|
|
@@ -665,9 +614,7 @@ class MyRuntimePlugin:
|
|
|
665
614
|
def build_hooks_config(self, command: str, *, timeout: int = 30) -> dict:
|
|
666
615
|
return build_hooks_config(command, timeout=timeout)
|
|
667
616
|
|
|
668
|
-
# init(project_dir, connectors, **kwargs) is optional
|
|
669
|
-
# runtime has no project-local hook-config file to generate (matching
|
|
670
|
-
# ClaudeCodeHooksAdapter's own plugin, which doesn't define one yet).
|
|
617
|
+
# init(project_dir, connectors, **kwargs) is optional: omit it when there is no project-local file to write.
|
|
671
618
|
|
|
672
619
|
|
|
673
620
|
PLUGIN = MyRuntimePlugin()
|
|
@@ -406,34 +406,11 @@ name is used. See
|
|
|
406
406
|
[ADR 0006](docs/adr/0006-memory-tool-writes-explicit-memory.md) for why this is
|
|
407
407
|
the one write the model makes over MCP.
|
|
408
408
|
|
|
409
|
-
###
|
|
409
|
+
### Codex and Claude Code Plugins
|
|
410
410
|
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
```text
|
|
416
|
-
Codex Plugin -> Codex Runtime Adapter -> Event Protocol -> Graph Connector -> Memgraph
|
|
417
|
-
```
|
|
418
|
-
|
|
419
|
-
The plugin installs Codex hook wiring. The Codex runtime adapter normalizes the hook payload. Graph connectors such as `SkillGraphConnector` decide what those events mean in their graph.
|
|
420
|
-
|
|
421
|
-
Plugin source:
|
|
422
|
-
|
|
423
|
-
```text
|
|
424
|
-
context-graph/plugins/memgraph-context-graph-codex
|
|
425
|
-
```
|
|
426
|
-
|
|
427
|
-
Register the public Git-backed marketplace and install the plugin:
|
|
428
|
-
|
|
429
|
-
```bash
|
|
430
|
-
codex plugin marketplace add memgraph/ai-toolkit --sparse .agents/plugins
|
|
431
|
-
codex plugin add context-graph@context-graph-plugins
|
|
432
|
-
```
|
|
433
|
-
|
|
434
|
-
Both are non-interactive; `codex plugin add` installs and enables the plugin in one step (`codex plugin list --json` confirms `"enabled": true`).
|
|
435
|
-
|
|
436
|
-
Check the installed hook environment with:
|
|
411
|
+
Both runtimes have a distributable Runtime Plugin that wires hooks and the MCP server; marketplace
|
|
412
|
+
install steps are in [plugins/memgraph-context-graph-codex](../plugins/memgraph-context-graph-codex/README.md) and
|
|
413
|
+
[plugins/memgraph-context-graph-claude](../plugins/memgraph-context-graph-claude/README.md). Check an install with:
|
|
437
414
|
|
|
438
415
|
```bash
|
|
439
416
|
memgraph-context-graph doctor --runtime codex --connector skills-graph --connector actions-graph --connector sessions-graph
|
|
@@ -441,34 +418,6 @@ memgraph-context-graph doctor --runtime codex --connector skills-graph --connect
|
|
|
441
418
|
|
|
442
419
|
Graph credentials live in `~/.config/context-graph/config.toml` (written by `setup`/`config set`), not in plugin hook files or the process environment — hooks read that file at runtime. See [Configuration](#configuration).
|
|
443
420
|
|
|
444
|
-
### Claude Code Plugin
|
|
445
|
-
|
|
446
|
-
Claude Code hook configuration can be installed as a Claude Code plugin.
|
|
447
|
-
|
|
448
|
-
The runtime-plugin flow is:
|
|
449
|
-
|
|
450
|
-
```text
|
|
451
|
-
Claude Code Plugin -> Claude Code Runtime Adapter -> Event Protocol -> Graph Connector -> Memgraph
|
|
452
|
-
```
|
|
453
|
-
|
|
454
|
-
For a public Git-backed marketplace install, add the marketplace inside Claude Code:
|
|
455
|
-
|
|
456
|
-
```text
|
|
457
|
-
/plugin marketplace add memgraph/ai-toolkit
|
|
458
|
-
```
|
|
459
|
-
|
|
460
|
-
Then install:
|
|
461
|
-
|
|
462
|
-
```text
|
|
463
|
-
/plugin install context-graph@context-graph-plugins
|
|
464
|
-
```
|
|
465
|
-
|
|
466
|
-
Check the installed hook environment with:
|
|
467
|
-
|
|
468
|
-
```bash
|
|
469
|
-
memgraph-context-graph doctor --runtime claude-code --connector skills-graph --connector actions-graph --connector sessions-graph
|
|
470
|
-
```
|
|
471
|
-
|
|
472
421
|
### Source Development
|
|
473
422
|
|
|
474
423
|
For source development and per-project experiments, you can generate local Codex hook files:
|
|
@@ -640,9 +589,7 @@ class MyRuntimePlugin:
|
|
|
640
589
|
def build_hooks_config(self, command: str, *, timeout: int = 30) -> dict:
|
|
641
590
|
return build_hooks_config(command, timeout=timeout)
|
|
642
591
|
|
|
643
|
-
# init(project_dir, connectors, **kwargs) is optional
|
|
644
|
-
# runtime has no project-local hook-config file to generate (matching
|
|
645
|
-
# ClaudeCodeHooksAdapter's own plugin, which doesn't define one yet).
|
|
592
|
+
# init(project_dir, connectors, **kwargs) is optional: omit it when there is no project-local file to write.
|
|
646
593
|
|
|
647
594
|
|
|
648
595
|
PLUGIN = MyRuntimePlugin()
|
|
@@ -205,7 +205,7 @@ session end, so adapters emit two different events:
|
|
|
205
205
|
| Runtime | Turn end | Session end |
|
|
206
206
|
|---|---|---|
|
|
207
207
|
| Claude Code | `Stop` (records `last_assistant_message`) | `SessionEnd` |
|
|
208
|
-
| Codex | `Stop` (records `last_assistant_message`) |
|
|
208
|
+
| Codex | `Stop` (records `last_assistant_message`) | `SessionEnd`, only when Codex ends the session; with its shared background server `/exit` just detaches |
|
|
209
209
|
| Copilot CLI | `agentStop` | `sessionEnd` |
|
|
210
210
|
| Cursor | `stop` | `sessionEnd` |
|
|
211
211
|
| OpenCode | `session.execution.succeeded` / `.failed` | `session.deleted` |
|
{memgraph_context_graph-0.7.0 → memgraph_context_graph-0.7.2}/src/memgraph_context_graph/__init__.py
RENAMED
|
@@ -45,9 +45,8 @@ from .protocols import GraphConnector, RuntimeAdapter
|
|
|
45
45
|
try:
|
|
46
46
|
__version__ = metadata.version(__package__ or __name__)
|
|
47
47
|
except metadata.PackageNotFoundError:
|
|
48
|
-
# Case where package metadata is not available.
|
|
49
48
|
__version__ = ""
|
|
50
|
-
del metadata
|
|
49
|
+
del metadata
|
|
51
50
|
|
|
52
51
|
__all__ = [
|
|
53
52
|
"AgentEndEvent",
|
|
@@ -25,22 +25,12 @@ from __future__ import annotations
|
|
|
25
25
|
|
|
26
26
|
import os
|
|
27
27
|
import stat
|
|
28
|
-
from dataclasses import dataclass, field
|
|
28
|
+
from dataclasses import dataclass, field, replace
|
|
29
29
|
from pathlib import Path
|
|
30
30
|
from typing import Any
|
|
31
31
|
|
|
32
|
-
#:
|
|
33
|
-
#:
|
|
34
|
-
#: hook subprocesses do not source shell profiles and relying on ambient env
|
|
35
|
-
#: caused `doctor` and hooks to disagree about what was configured.
|
|
36
|
-
#:
|
|
37
|
-
#: This is the opposite situation: a parent process that spawns a session hands
|
|
38
|
-
#: the path down explicitly to its own child, so nothing is ambient and nothing
|
|
39
|
-
#: can drift. It exists because the config file is otherwise a single global,
|
|
40
|
-
#: which means pointing ONE session at a different Memgraph silently redirects
|
|
41
|
-
#: every other Claude Code session running on the machine -- observed while
|
|
42
|
-
#: building the eval gold slice, where an unrelated session's activity landed
|
|
43
|
-
#: in the graph under test.
|
|
32
|
+
#: Selects which config *file* hooks read, never a value in it (ADR 0003): a parent
|
|
33
|
+
#: process hands the path to the session it spawns, so nothing is ambient.
|
|
44
34
|
CONFIG_PATH_ENV = "CONTEXT_GRAPH_CONFIG"
|
|
45
35
|
|
|
46
36
|
_DEFAULT_CONFIG_DIR = Path.home() / ".config" / "context-graph"
|
|
@@ -75,13 +65,7 @@ MEMORY_BACKEND_GRAPH = "context-graph"
|
|
|
75
65
|
MEMORY_BACKEND_NATIVE = "native"
|
|
76
66
|
MEMORY_BACKENDS = frozenset({MEMORY_BACKEND_GRAPH, MEMORY_BACKEND_NATIVE})
|
|
77
67
|
|
|
78
|
-
|
|
79
|
-
"auto_reconcile": None,
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
#: Shared truthy/falsy vocabulary for TOML/CLI boolean flags — kept here so
|
|
83
|
-
#: cli.py can validate `config set` input against the same set parse_bool_flag
|
|
84
|
-
#: accepts, instead of redefining it inline.
|
|
68
|
+
#: The boolean spellings parse_bool_flag accepts, shared with ``config set`` validation.
|
|
85
69
|
TRUTHY_VALUES = frozenset({"1", "true", "yes", "on"})
|
|
86
70
|
FALSY_VALUES = frozenset({"0", "false", "no", "off"})
|
|
87
71
|
|
|
@@ -101,7 +85,7 @@ class HookConfig:
|
|
|
101
85
|
memgraph_database: str = _MEMGRAPH_DEFAULTS["database"]
|
|
102
86
|
openai_api_key: str = _LLM_DEFAULTS["openai_api_key"]
|
|
103
87
|
anthropic_api_key: str = _LLM_DEFAULTS["anthropic_api_key"]
|
|
104
|
-
auto_reconcile: bool | None =
|
|
88
|
+
auto_reconcile: bool | None = None
|
|
105
89
|
#: The model recall embeds with; None means the consumer's own default.
|
|
106
90
|
embedding_model: str | None = None
|
|
107
91
|
#: Every other ``[recall]`` key (lanes and widths), as written: the recall
|
|
@@ -133,18 +117,11 @@ class HookConfig:
|
|
|
133
117
|
|
|
134
118
|
|
|
135
119
|
def load_config() -> HookConfig:
|
|
136
|
-
"""Load hook configuration from the config file.
|
|
137
|
-
|
|
138
|
-
Returns a HookConfig with defaults for any missing values.
|
|
139
|
-
Caches the result for the lifetime of the process.
|
|
140
|
-
"""
|
|
120
|
+
"""Load hook configuration from the config file, with defaults for missing values; cached per process."""
|
|
141
121
|
global _cached_config
|
|
142
|
-
if _cached_config is
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
config = _read_config_file()
|
|
146
|
-
_cached_config = config
|
|
147
|
-
return config
|
|
122
|
+
if _cached_config is None:
|
|
123
|
+
_cached_config = _read_config_file()
|
|
124
|
+
return _cached_config
|
|
148
125
|
|
|
149
126
|
|
|
150
127
|
def reload_config() -> HookConfig:
|
|
@@ -190,13 +167,10 @@ def resolve_memgraph_env(
|
|
|
190
167
|
|
|
191
168
|
|
|
192
169
|
def resolve_llm_env() -> dict[str, str]:
|
|
193
|
-
"""Resolve LLM API
|
|
170
|
+
"""Resolve LLM API keys for hook subprocesses, from the config file only.
|
|
194
171
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
Values are empty strings when not configured. Hook-spawned children must
|
|
198
|
-
overlay these empty values too so ambient credentials cannot supply a key
|
|
199
|
-
absent from the selected config file.
|
|
172
|
+
An unconfigured key is an empty string. Hook-spawned children must overlay these
|
|
173
|
+
empty values too, so ambient credentials cannot supply a key the config file lacks.
|
|
200
174
|
"""
|
|
201
175
|
config = load_config()
|
|
202
176
|
return {
|
|
@@ -208,42 +182,25 @@ def resolve_llm_env() -> dict[str, str]:
|
|
|
208
182
|
def resolve_auto_reconcile() -> bool:
|
|
209
183
|
"""Resolve whether sessions-graph should auto-trigger reconciliation on SESSION_END.
|
|
210
184
|
|
|
211
|
-
Config file only
|
|
212
|
-
|
|
213
|
-
resolution) there is no ambient env var fallback to preserve here, so
|
|
214
|
-
"never configured" collapses to ``False``, matching
|
|
215
|
-
``SessionsGraphConnector``'s own default. Off by default given LightRAG
|
|
216
|
-
entity extraction's LLM cost; opt in with
|
|
217
|
-
``memgraph-context-graph config set reconcile.auto_reconcile true``.
|
|
185
|
+
Config file only (ADR 0002); never configured means ``False``, matching
|
|
186
|
+
``SessionsGraphConnector``'s default, since reconciliation costs LLM calls.
|
|
218
187
|
"""
|
|
219
188
|
return bool(load_config().auto_reconcile)
|
|
220
189
|
|
|
221
190
|
|
|
222
191
|
def resolve_embedding_model() -> str | None:
|
|
223
|
-
"""The configured ``[recall] embedding_model``, or None
|
|
224
|
-
|
|
225
|
-
Config file only, like :func:`resolve_auto_reconcile`. None leaves the
|
|
226
|
-
choice to sessions-graph's own default rather than duplicating it here.
|
|
227
|
-
"""
|
|
192
|
+
"""The configured ``[recall] embedding_model``, or None to leave sessions-graph's default."""
|
|
228
193
|
return load_config().embedding_model
|
|
229
194
|
|
|
230
195
|
|
|
231
196
|
def resolve_ontology() -> tuple[str | None, str | None]:
|
|
232
|
-
"""The configured ``[ontology]`` (path, derive), each None when unset.
|
|
233
|
-
|
|
234
|
-
Config file only, like :func:`resolve_auto_reconcile`. sessions-graph owns
|
|
235
|
-
what the values mean and their defaults.
|
|
236
|
-
"""
|
|
197
|
+
"""The configured ``[ontology]`` (path, derive), each None when unset; sessions-graph owns their meaning."""
|
|
237
198
|
config = load_config()
|
|
238
199
|
return config.ontology_path, config.ontology_derive
|
|
239
200
|
|
|
240
201
|
|
|
241
202
|
def parse_bool_flag(value: str | bool) -> bool:
|
|
242
|
-
"""Parse a TOML/CLI boolean flag
|
|
243
|
-
|
|
244
|
-
Passing an actual ``bool`` through unchanged guards against a future
|
|
245
|
-
swap to a real TOML parser (which would hand back native booleans).
|
|
246
|
-
"""
|
|
203
|
+
"""Parse a TOML/CLI boolean flag: :data:`TRUTHY_VALUES` (case-insensitive) is true, a ``bool`` passes through."""
|
|
247
204
|
if isinstance(value, bool):
|
|
248
205
|
return value
|
|
249
206
|
return value.strip().lower() in TRUTHY_VALUES
|
|
@@ -271,52 +228,24 @@ def write_config(
|
|
|
271
228
|
Only updates supplied values; preserves existing values for unspecified keys.
|
|
272
229
|
Creates the file with 0600 permissions if it does not exist.
|
|
273
230
|
"""
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
final_memory_backend = memory_backend if memory_backend is not None else existing.memory_backend
|
|
293
|
-
|
|
294
|
-
content = _render_config(
|
|
295
|
-
edition=final_edition,
|
|
296
|
-
user_id=final_user_id or "",
|
|
297
|
-
url=final_url,
|
|
298
|
-
user=final_user,
|
|
299
|
-
password=final_password,
|
|
300
|
-
database=final_database,
|
|
301
|
-
openai_api_key=final_openai_api_key,
|
|
302
|
-
anthropic_api_key=final_anthropic_api_key,
|
|
303
|
-
auto_reconcile=final_auto_reconcile,
|
|
304
|
-
embedding_model=final_embedding_model,
|
|
305
|
-
recall_settings=existing.recall_settings,
|
|
306
|
-
ontology_path=final_ontology_path,
|
|
307
|
-
ontology_derive=final_ontology_derive,
|
|
308
|
-
github_token=final_github_token,
|
|
309
|
-
memory_backend=final_memory_backend,
|
|
310
|
-
)
|
|
311
|
-
|
|
312
|
-
path = config_file()
|
|
313
|
-
path.parent.mkdir(parents=True, exist_ok=True)
|
|
314
|
-
path.write_text(content, encoding="utf-8")
|
|
315
|
-
path.chmod(stat.S_IRUSR | stat.S_IWUSR) # 0600
|
|
316
|
-
|
|
317
|
-
# Invalidate cache.
|
|
318
|
-
_cached_config = None
|
|
319
|
-
return config_file()
|
|
231
|
+
updates = {
|
|
232
|
+
"edition": edition,
|
|
233
|
+
"user_id": user_id,
|
|
234
|
+
"memgraph_url": memgraph_url,
|
|
235
|
+
"memgraph_user": memgraph_user,
|
|
236
|
+
"memgraph_password": memgraph_password,
|
|
237
|
+
"memgraph_database": memgraph_database,
|
|
238
|
+
"openai_api_key": openai_api_key,
|
|
239
|
+
"anthropic_api_key": anthropic_api_key,
|
|
240
|
+
"auto_reconcile": auto_reconcile,
|
|
241
|
+
"embedding_model": embedding_model,
|
|
242
|
+
"ontology_path": ontology_path,
|
|
243
|
+
"ontology_derive": ontology_derive,
|
|
244
|
+
"github_token": github_token,
|
|
245
|
+
"memory_backend": memory_backend,
|
|
246
|
+
}
|
|
247
|
+
config = replace(_read_config_file(), **{key: value for key, value in updates.items() if value is not None})
|
|
248
|
+
return _write_config_file(_render_config(config))
|
|
320
249
|
|
|
321
250
|
|
|
322
251
|
def write_full_config(
|
|
@@ -328,7 +257,7 @@ def write_full_config(
|
|
|
328
257
|
memgraph_database: str = _MEMGRAPH_DEFAULTS["database"],
|
|
329
258
|
openai_api_key: str = _LLM_DEFAULTS["openai_api_key"],
|
|
330
259
|
anthropic_api_key: str = _LLM_DEFAULTS["anthropic_api_key"],
|
|
331
|
-
auto_reconcile: bool | None =
|
|
260
|
+
auto_reconcile: bool | None = None,
|
|
332
261
|
) -> Path:
|
|
333
262
|
"""Write a complete config file, resetting identity, Memgraph and LLM settings to what is given.
|
|
334
263
|
|
|
@@ -336,49 +265,29 @@ def write_full_config(
|
|
|
336
265
|
edition are kept from the existing file, since only ``config set`` or a hand edit ever sets them.
|
|
337
266
|
``auto_reconcile`` is kept likewise unless given here. :func:`write_config` is the partial update.
|
|
338
267
|
"""
|
|
339
|
-
global _cached_config
|
|
340
|
-
|
|
341
268
|
existing = _read_config_file()
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
content = _render_config(
|
|
345
|
-
edition=existing.edition,
|
|
269
|
+
config = replace(
|
|
270
|
+
existing,
|
|
346
271
|
user_id=user_id,
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
272
|
+
memgraph_url=memgraph_url,
|
|
273
|
+
memgraph_user=memgraph_user,
|
|
274
|
+
memgraph_password=memgraph_password,
|
|
275
|
+
memgraph_database=memgraph_database,
|
|
351
276
|
openai_api_key=openai_api_key,
|
|
352
277
|
anthropic_api_key=anthropic_api_key,
|
|
353
|
-
auto_reconcile=
|
|
354
|
-
embedding_model=existing.embedding_model,
|
|
355
|
-
recall_settings=existing.recall_settings,
|
|
356
|
-
ontology_path=existing.ontology_path,
|
|
357
|
-
ontology_derive=existing.ontology_derive,
|
|
358
|
-
github_token=existing.github_token,
|
|
359
|
-
memory_backend=existing.memory_backend,
|
|
278
|
+
auto_reconcile=auto_reconcile if auto_reconcile is not None else existing.auto_reconcile,
|
|
360
279
|
)
|
|
280
|
+
return _write_config_file(_render_config(config))
|
|
281
|
+
|
|
361
282
|
|
|
283
|
+
def _write_config_file(content: str) -> Path:
|
|
284
|
+
global _cached_config
|
|
362
285
|
path = config_file()
|
|
363
286
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
364
287
|
path.write_text(content, encoding="utf-8")
|
|
365
|
-
path.chmod(stat.S_IRUSR | stat.S_IWUSR) # 0600
|
|
366
|
-
|
|
288
|
+
path.chmod(stat.S_IRUSR | stat.S_IWUSR) # 0600: the file holds credentials.
|
|
367
289
|
_cached_config = None
|
|
368
|
-
return
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
def config_file_path() -> Path:
|
|
372
|
-
"""Return the config file path (for display purposes)."""
|
|
373
|
-
return config_file()
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
def config_dir_path() -> Path:
|
|
377
|
-
"""Return the directory holding the config file actually in use."""
|
|
378
|
-
return config_file().parent
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
# --- Internal helpers ---
|
|
290
|
+
return path
|
|
382
291
|
|
|
383
292
|
|
|
384
293
|
def _read_config_file() -> HookConfig:
|
|
@@ -466,62 +375,50 @@ def _parse_toml(path: Path, problems: list[str] | None = None) -> dict[str, dict
|
|
|
466
375
|
return sections
|
|
467
376
|
|
|
468
377
|
|
|
469
|
-
def _render_config(
|
|
470
|
-
*,
|
|
471
|
-
edition: str | None = None,
|
|
472
|
-
user_id: str,
|
|
473
|
-
url: str,
|
|
474
|
-
user: str,
|
|
475
|
-
password: str,
|
|
476
|
-
database: str,
|
|
477
|
-
openai_api_key: str,
|
|
478
|
-
anthropic_api_key: str,
|
|
479
|
-
auto_reconcile: bool | None,
|
|
480
|
-
embedding_model: str | None = None,
|
|
481
|
-
recall_settings: dict[str, str] | None = None,
|
|
482
|
-
ontology_path: str | None = None,
|
|
483
|
-
ontology_derive: str | None = None,
|
|
484
|
-
github_token: str | None = None,
|
|
485
|
-
memory_backend: str | None = None,
|
|
486
|
-
) -> str:
|
|
378
|
+
def _render_config(config: HookConfig) -> str:
|
|
487
379
|
"""Render the full config file content.
|
|
488
380
|
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
:func:`resolve_auto_reconcile` for why that distinction matters.
|
|
493
|
-
``[recall]``, ``[ontology]``, ``[github]`` and ``[memory]`` are likewise omitted while they hold nothing.
|
|
381
|
+
``[reconcile]`` is omitted while ``auto_reconcile`` is ``None`` so a re-read keeps "never configured"
|
|
382
|
+
distinct from an explicit ``false``; ``[recall]``, ``[ontology]``, ``[github]`` and ``[memory]`` are
|
|
383
|
+
omitted while they hold nothing.
|
|
494
384
|
"""
|
|
385
|
+
user_id = config.user_id or ""
|
|
495
386
|
lines = [
|
|
496
387
|
"# Context Graph hook configuration",
|
|
497
388
|
"# Generated by: memgraph-context-graph config set / bootstrap",
|
|
498
389
|
f"# Location: {config_file()}",
|
|
499
390
|
"",
|
|
500
|
-
*([f'edition = "{edition}"', ""] if edition else []),
|
|
391
|
+
*([f'edition = "{config.edition}"', ""] if config.edition else []),
|
|
501
392
|
# Enterprise takes the user and their database from the Memgraph login, so it writes neither.
|
|
502
|
-
*(["[identity]", f'user_id = "{user_id}"', ""] if edition != "enterprise" else []),
|
|
393
|
+
*(["[identity]", f'user_id = "{user_id}"', ""] if config.edition != "enterprise" else []),
|
|
503
394
|
"[memgraph]",
|
|
504
|
-
f'url = "{
|
|
505
|
-
f'user = "{
|
|
506
|
-
f'password = "{
|
|
507
|
-
*([f'database = "{
|
|
395
|
+
f'url = "{config.memgraph_url}"',
|
|
396
|
+
f'user = "{config.memgraph_user}"',
|
|
397
|
+
f'password = "{config.memgraph_password}"',
|
|
398
|
+
*([f'database = "{config.memgraph_database}"'] if config.edition != "enterprise" else []),
|
|
508
399
|
"",
|
|
509
400
|
"[llm]",
|
|
510
|
-
f'openai_api_key = "{openai_api_key}"',
|
|
511
|
-
f'anthropic_api_key = "{anthropic_api_key}"',
|
|
401
|
+
f'openai_api_key = "{config.openai_api_key}"',
|
|
402
|
+
f'anthropic_api_key = "{config.anthropic_api_key}"',
|
|
512
403
|
]
|
|
513
|
-
if auto_reconcile is not None:
|
|
514
|
-
lines += ["", "[reconcile]", f"auto_reconcile = {'true' if auto_reconcile else 'false'}"]
|
|
515
|
-
recall =
|
|
404
|
+
if config.auto_reconcile is not None:
|
|
405
|
+
lines += ["", "[reconcile]", f"auto_reconcile = {'true' if config.auto_reconcile else 'false'}"]
|
|
406
|
+
recall = (
|
|
407
|
+
{"embedding_model": config.embedding_model, **config.recall_settings}
|
|
408
|
+
if config.embedding_model
|
|
409
|
+
else config.recall_settings
|
|
410
|
+
)
|
|
516
411
|
if recall:
|
|
517
412
|
lines += ["", "[recall]", *(f'{key} = "{value}"' for key, value in recall.items())]
|
|
518
|
-
ontology = {
|
|
413
|
+
ontology = {
|
|
414
|
+
key: value for key, value in (("path", config.ontology_path), ("derive", config.ontology_derive)) if value
|
|
415
|
+
}
|
|
519
416
|
if ontology:
|
|
520
417
|
lines += ["", "[ontology]", *(f'{key} = "{value}"' for key, value in ontology.items())]
|
|
521
|
-
if github_token:
|
|
522
|
-
lines += ["", "[github]", f'token = "{github_token}"']
|
|
523
|
-
if memory_backend:
|
|
524
|
-
lines += ["", "[memory]", f'backend = "{memory_backend}"']
|
|
418
|
+
if config.github_token:
|
|
419
|
+
lines += ["", "[github]", f'token = "{config.github_token}"']
|
|
420
|
+
if config.memory_backend:
|
|
421
|
+
lines += ["", "[memory]", f'backend = "{config.memory_backend}"']
|
|
525
422
|
lines.append("")
|
|
526
423
|
return "\n".join(lines)
|
|
527
424
|
|