memgraph-context-graph 0.7.4__tar.gz → 0.8.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.8.0}/PKG-INFO +45 -8
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.8.0}/README.md +44 -7
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.8.0}/docs/command-hooks.md +3 -3
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.8.0}/pyproject.toml +1 -1
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.8.0}/src/memgraph_context_graph/adapters/_identity.py +13 -4
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.8.0}/src/memgraph_context_graph/adapters/_opencode_plugin.js +36 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.8.0}/src/memgraph_context_graph/adapters/_spec.py +26 -1
- memgraph_context_graph-0.8.0/src/memgraph_context_graph/adapters/agent_mcp.py +326 -0
- memgraph_context_graph-0.8.0/src/memgraph_context_graph/adapters/agent_memory.py +201 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.8.0}/src/memgraph_context_graph/adapters/antigravity_cli.py +77 -17
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.8.0}/src/memgraph_context_graph/adapters/claude_code.py +2 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.8.0}/src/memgraph_context_graph/adapters/codex.py +2 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.8.0}/src/memgraph_context_graph/adapters/copilot_cli.py +6 -1
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.8.0}/src/memgraph_context_graph/adapters/cursor.py +6 -1
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.8.0}/src/memgraph_context_graph/adapters/grok.py +2 -1
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.8.0}/src/memgraph_context_graph/adapters/opencode.py +5 -1
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.8.0}/src/memgraph_context_graph/cli.py +16 -4
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.8.0}/src/memgraph_context_graph/hooks/runner.py +26 -14
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.8.0}/src/memgraph_context_graph/mcp_server.py +20 -6
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.8.0}/src/memgraph_context_graph/setup/agents.py +58 -1
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.8.0}/src/memgraph_context_graph/setup/cli.py +1 -1
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.8.0}/src/memgraph_context_graph/setup/engine.py +25 -6
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.8.0}/src/memgraph_context_graph/setup/prompts.py +4 -8
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.8.0}/src/memgraph_context_graph/setup/tool.py +1 -1
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.8.0}/src/memgraph_context_graph/setup/uninstall.py +11 -5
- memgraph_context_graph-0.8.0/src/memgraph_context_graph/terminal.py +38 -0
- memgraph_context_graph-0.8.0/tests/test_agent_memory_wiring.py +215 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.8.0}/tests/test_antigravity_cli_adapter.py +46 -7
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.8.0}/tests/test_setup.py +6 -4
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.8.0}/tests/test_setup_docker.py +3 -2
- memgraph_context_graph-0.8.0/tests/test_terminal.py +35 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.8.0}/.gitignore +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.8.0}/CONTEXT.md +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.8.0}/LICENSE +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.8.0}/docs/adr/0001-runtime-adapter-terminology.md +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.8.0}/docs/adr/0002-config-file-only-hook-resolution.md +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.8.0}/docs/adr/0003-config-path-override.md +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.8.0}/docs/adr/0004-user-id-in-session-start-event.md +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.8.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.8.0}/docs/adr/0006-memory-tool-writes-explicit-memory.md +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.8.0}/docs/research/2026-10-codex-memory.md +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.8.0}/src/memgraph_context_graph/__init__.py +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.8.0}/src/memgraph_context_graph/adapters/__init__.py +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.8.0}/src/memgraph_context_graph/adapters/claude.py +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.8.0}/src/memgraph_context_graph/adapters/claude_code_memory.py +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.8.0}/src/memgraph_context_graph/adapters/codex_memory.py +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.8.0}/src/memgraph_context_graph/adapters/openai.py +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.8.0}/src/memgraph_context_graph/capture_log.py +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.8.0}/src/memgraph_context_graph/events.py +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.8.0}/src/memgraph_context_graph/hooks/__init__.py +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.8.0}/src/memgraph_context_graph/hooks/cli.py +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.8.0}/src/memgraph_context_graph/hooks/runtime_plugin.py +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.8.0}/src/memgraph_context_graph/link.py +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.8.0}/src/memgraph_context_graph/memory_backend.py +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.8.0}/src/memgraph_context_graph/protocols.py +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.8.0}/src/memgraph_context_graph/setup/__init__.py +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.8.0}/src/memgraph_context_graph/setup/memgraph.py +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.8.0}/src/memgraph_context_graph/spaces.py +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.8.0}/src/memgraph_context_graph/tools.py +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.8.0}/tests/__init__.py +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.8.0}/tests/conftest.py +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.8.0}/tests/fake_agent_cli.py +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.8.0}/tests/test_capture_log.py +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.8.0}/tests/test_claude_code_adapter.py +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.8.0}/tests/test_codex_adapter.py +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.8.0}/tests/test_config_cli.py +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.8.0}/tests/test_context_before_tool.py +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.8.0}/tests/test_copilot_cli_adapter.py +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.8.0}/tests/test_cursor_adapter.py +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.8.0}/tests/test_dev_scripts.py +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.8.0}/tests/test_events.py +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.8.0}/tests/test_grok_adapter.py +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.8.0}/tests/test_hook_cli.py +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.8.0}/tests/test_identity.py +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.8.0}/tests/test_link.py +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.8.0}/tests/test_opencode_adapter.py +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.8.0}/tests/test_plugin_hooks.py +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.8.0}/tests/test_runner_sessions_graph_connector.py +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.8.0}/tests/test_runtime_plugin.py +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.8.0}/tests/test_spaces.py +0 -0
- {memgraph_context_graph-0.7.4 → memgraph_context_graph-0.8.0}/tests/test_tools.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.8.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
|
|
@@ -308,15 +308,23 @@ Hooks never read environment variables for these values, and neither does setup:
|
|
|
308
308
|
|
|
309
309
|
The hooks write sessions into Memgraph; recall reads them back. With
|
|
310
310
|
sessions-graph installed, `memgraph-context-graph mcp` serves a `recall(question)`
|
|
311
|
-
tool over stdio MCP
|
|
312
|
-
|
|
311
|
+
tool over stdio MCP. The Codex and Claude Code plugins bundle that server, so
|
|
312
|
+
installing a plugin is all it takes; for the other agents, `setup` registers it
|
|
313
|
+
in each one's own user-level MCP config (see
|
|
314
|
+
[every agent](#every-agent-registration-switch-and-session-start)). The tool returns context, not an answer:
|
|
313
315
|
the dated messages and facts from the user's own sessions that match the
|
|
314
316
|
question, behind a header with the rules for reading them. The harness's model
|
|
315
317
|
answers from those rows.
|
|
316
318
|
|
|
317
|
-
At session start the hook adds
|
|
318
|
-
exists
|
|
319
|
-
|
|
319
|
+
At session start the hook adds a line to the model's context saying the tool
|
|
320
|
+
exists, then where the user left off in this project: the last session's date,
|
|
321
|
+
agent, first and last ask and last reply, and the first ask of the two before
|
|
322
|
+
it, quoted from captured turns. Sessions from any agent count, so a session in
|
|
323
|
+
Grok picks up where one in Codex stopped. It is a pointer, not a recall result:
|
|
324
|
+
nothing is ranked against a question, and the model still calls `recall` when
|
|
325
|
+
one needs memory. A session's project is its checkout's key (the same one
|
|
326
|
+
memory folders use), recorded when it starts. Turn it off with
|
|
327
|
+
`memgraph-context-graph config set session_start.resume false`.
|
|
320
328
|
|
|
321
329
|
A connector may also add a line just before a tool runs
|
|
322
330
|
(`GraphConnector.context_before_tool`) — resources-graph's Nudge that what the
|
|
@@ -359,8 +367,9 @@ was benchmarked in; the JSON is what `--json` prints.
|
|
|
359
367
|
|
|
360
368
|
### Memory: Context Graph as the harness's memory
|
|
361
369
|
|
|
362
|
-
|
|
363
|
-
for a user who
|
|
370
|
+
Every agent `setup` knows can use Context Graph as its memory. Claude Code and
|
|
371
|
+
Codex each keep their own memory, and Context Graph replaces it for a user who
|
|
372
|
+
opts in:
|
|
364
373
|
|
|
365
374
|
```bash
|
|
366
375
|
memgraph-context-graph setup claude-code --memory-backend context-graph
|
|
@@ -431,6 +440,34 @@ name is used. See
|
|
|
431
440
|
[ADR 0006](docs/adr/0006-memory-tool-writes-explicit-memory.md) for why this is
|
|
432
441
|
the one write the model makes over MCP.
|
|
433
442
|
|
|
443
|
+
#### Every agent: registration, switch and session start
|
|
444
|
+
|
|
445
|
+
The other five have no plugin, so `setup <runtime>` registers the
|
|
446
|
+
`context-graph` MCP server in the agent's own user-level config: memory belongs
|
|
447
|
+
to the user, not to one project. It adds the server's tools to the agent's
|
|
448
|
+
persistent allow-list where it keeps one, because a headless run refuses a tool
|
|
449
|
+
that needs approval. `--memory-backend context-graph` then turns the agent's
|
|
450
|
+
own memory off, and `uninstall` undoes both. Every edited file keeps the rest of
|
|
451
|
+
its content and is backed up once to `<name>.context-graph.bak`.
|
|
452
|
+
|
|
453
|
+
| Agent | Built-in memory, and the switch | Server registered in | Session-start text reaches the model through |
|
|
454
|
+
|---|---|---|---|
|
|
455
|
+
| Claude Code | auto memory; `autoMemoryEnabled: false` | the plugin | `SessionStart` `additionalContext` |
|
|
456
|
+
| Codex | memories; three keys in `config.toml` | the plugin | `SessionStart` `additionalContext` (~2,500-token cap) |
|
|
457
|
+
| 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` |
|
|
458
|
+
| 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) |
|
|
459
|
+
| 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 |
|
|
460
|
+
| 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 |
|
|
461
|
+
| 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 |
|
|
462
|
+
|
|
463
|
+
Antigravity's hooks carry no prompt or reply text, so its turns are read from
|
|
464
|
+
the transcript its `Stop` payload points at; that is what gives its sessions a
|
|
465
|
+
place in recall and in the next session's "where you left off".
|
|
466
|
+
|
|
467
|
+
`live-hooks-e2e.sh memory` checks this against each installed agent: a memory
|
|
468
|
+
written, one read back in a new session, and the next session's index and last
|
|
469
|
+
ask.
|
|
470
|
+
|
|
434
471
|
### Codex and Claude Code Plugins
|
|
435
472
|
|
|
436
473
|
Both runtimes have a distributable Runtime Plugin that wires hooks and the MCP server; marketplace
|
|
@@ -283,15 +283,23 @@ Hooks never read environment variables for these values, and neither does setup:
|
|
|
283
283
|
|
|
284
284
|
The hooks write sessions into Memgraph; recall reads them back. With
|
|
285
285
|
sessions-graph installed, `memgraph-context-graph mcp` serves a `recall(question)`
|
|
286
|
-
tool over stdio MCP
|
|
287
|
-
|
|
286
|
+
tool over stdio MCP. The Codex and Claude Code plugins bundle that server, so
|
|
287
|
+
installing a plugin is all it takes; for the other agents, `setup` registers it
|
|
288
|
+
in each one's own user-level MCP config (see
|
|
289
|
+
[every agent](#every-agent-registration-switch-and-session-start)). The tool returns context, not an answer:
|
|
288
290
|
the dated messages and facts from the user's own sessions that match the
|
|
289
291
|
question, behind a header with the rules for reading them. The harness's model
|
|
290
292
|
answers from those rows.
|
|
291
293
|
|
|
292
|
-
At session start the hook adds
|
|
293
|
-
exists
|
|
294
|
-
|
|
294
|
+
At session start the hook adds a line to the model's context saying the tool
|
|
295
|
+
exists, then where the user left off in this project: the last session's date,
|
|
296
|
+
agent, first and last ask and last reply, and the first ask of the two before
|
|
297
|
+
it, quoted from captured turns. Sessions from any agent count, so a session in
|
|
298
|
+
Grok picks up where one in Codex stopped. It is a pointer, not a recall result:
|
|
299
|
+
nothing is ranked against a question, and the model still calls `recall` when
|
|
300
|
+
one needs memory. A session's project is its checkout's key (the same one
|
|
301
|
+
memory folders use), recorded when it starts. Turn it off with
|
|
302
|
+
`memgraph-context-graph config set session_start.resume false`.
|
|
295
303
|
|
|
296
304
|
A connector may also add a line just before a tool runs
|
|
297
305
|
(`GraphConnector.context_before_tool`) — resources-graph's Nudge that what the
|
|
@@ -334,8 +342,9 @@ was benchmarked in; the JSON is what `--json` prints.
|
|
|
334
342
|
|
|
335
343
|
### Memory: Context Graph as the harness's memory
|
|
336
344
|
|
|
337
|
-
|
|
338
|
-
for a user who
|
|
345
|
+
Every agent `setup` knows can use Context Graph as its memory. Claude Code and
|
|
346
|
+
Codex each keep their own memory, and Context Graph replaces it for a user who
|
|
347
|
+
opts in:
|
|
339
348
|
|
|
340
349
|
```bash
|
|
341
350
|
memgraph-context-graph setup claude-code --memory-backend context-graph
|
|
@@ -406,6 +415,34 @@ name is used. See
|
|
|
406
415
|
[ADR 0006](docs/adr/0006-memory-tool-writes-explicit-memory.md) for why this is
|
|
407
416
|
the one write the model makes over MCP.
|
|
408
417
|
|
|
418
|
+
#### Every agent: registration, switch and session start
|
|
419
|
+
|
|
420
|
+
The other five have no plugin, so `setup <runtime>` registers the
|
|
421
|
+
`context-graph` MCP server in the agent's own user-level config: memory belongs
|
|
422
|
+
to the user, not to one project. It adds the server's tools to the agent's
|
|
423
|
+
persistent allow-list where it keeps one, because a headless run refuses a tool
|
|
424
|
+
that needs approval. `--memory-backend context-graph` then turns the agent's
|
|
425
|
+
own memory off, and `uninstall` undoes both. Every edited file keeps the rest of
|
|
426
|
+
its content and is backed up once to `<name>.context-graph.bak`.
|
|
427
|
+
|
|
428
|
+
| Agent | Built-in memory, and the switch | Server registered in | Session-start text reaches the model through |
|
|
429
|
+
|---|---|---|---|
|
|
430
|
+
| Claude Code | auto memory; `autoMemoryEnabled: false` | the plugin | `SessionStart` `additionalContext` |
|
|
431
|
+
| Codex | memories; three keys in `config.toml` | the plugin | `SessionStart` `additionalContext` (~2,500-token cap) |
|
|
432
|
+
| 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` |
|
|
433
|
+
| 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) |
|
|
434
|
+
| 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 |
|
|
435
|
+
| 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 |
|
|
436
|
+
| 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 |
|
|
437
|
+
|
|
438
|
+
Antigravity's hooks carry no prompt or reply text, so its turns are read from
|
|
439
|
+
the transcript its `Stop` payload points at; that is what gives its sessions a
|
|
440
|
+
place in recall and in the next session's "where you left off".
|
|
441
|
+
|
|
442
|
+
`live-hooks-e2e.sh memory` checks this against each installed agent: a memory
|
|
443
|
+
written, one read back in a new session, and the next session's index and last
|
|
444
|
+
ask.
|
|
445
|
+
|
|
409
446
|
### Codex and Claude Code Plugins
|
|
410
447
|
|
|
411
448
|
Both runtimes have a distributable Runtime Plugin that wires hooks and the MCP server; marketplace
|
|
@@ -139,15 +139,15 @@ never deletes its session, so those sessions only ever see turn ends.
|
|
|
139
139
|
Antigravity CLI (`agy`, the successor to Gemini CLI) reads named hook groups
|
|
140
140
|
from `.agents/hooks.json`; `hook init` owns the `memgraph-context-graph` group and
|
|
141
141
|
merges it beside any others. Only the tool events (`PreToolUse`,
|
|
142
|
-
`PostToolUse`) take matcher groups. `
|
|
142
|
+
`PostToolUse`) take matcher groups. `SessionStart` and `Stop` are written as
|
|
143
143
|
flat handlers, because `agy` rejects the whole file when a lifecycle event is
|
|
144
144
|
nested. Timeouts are in seconds.
|
|
145
145
|
|
|
146
146
|
```json
|
|
147
147
|
{
|
|
148
148
|
"memgraph-context-graph": {
|
|
149
|
-
"
|
|
150
|
-
{"type": "command", "command": "memgraph-context-graph hook run antigravity-cli --event-name
|
|
149
|
+
"SessionStart": [
|
|
150
|
+
{"type": "command", "command": "memgraph-context-graph hook run antigravity-cli --event-name SessionStart", "timeout": 30}
|
|
151
151
|
],
|
|
152
152
|
"PreToolUse": [
|
|
153
153
|
{"hooks": [{"type": "command", "command": "memgraph-context-graph hook run antigravity-cli --event-name PreToolUse", "timeout": 30}]}
|
|
@@ -101,6 +101,9 @@ class HookConfig:
|
|
|
101
101
|
#: harness memory to the graph (the ``memory`` tool and session-start index
|
|
102
102
|
#: are on); ``"native"`` or None leaves the harness's own memory in charge.
|
|
103
103
|
memory_backend: str | None = None
|
|
104
|
+
#: ``[session_start] resume``: whether a session starts with a brief of the
|
|
105
|
+
#: previous sessions in its project. None (never set) means on.
|
|
106
|
+
session_resume: bool | None = None
|
|
104
107
|
#: The ``section.key`` names the file actually sets (top-level keys as ``key``), so a check can tell
|
|
105
108
|
#: "left at the default" from "written": Enterprise refuses an explicit ``identity.user_id`` or
|
|
106
109
|
#: ``memgraph.database``, which would quietly override what the login decides.
|
|
@@ -222,6 +225,7 @@ def write_config(
|
|
|
222
225
|
ontology_derive: str | None = None,
|
|
223
226
|
github_token: str | None = None,
|
|
224
227
|
memory_backend: str | None = None,
|
|
228
|
+
session_resume: bool | None = None,
|
|
225
229
|
) -> Path:
|
|
226
230
|
"""Write or update the config file. Returns the path written to.
|
|
227
231
|
|
|
@@ -243,6 +247,7 @@ def write_config(
|
|
|
243
247
|
"ontology_derive": ontology_derive,
|
|
244
248
|
"github_token": github_token,
|
|
245
249
|
"memory_backend": memory_backend,
|
|
250
|
+
"session_resume": session_resume,
|
|
246
251
|
}
|
|
247
252
|
config = replace(_read_config_file(), **{key: value for key, value in updates.items() if value is not None})
|
|
248
253
|
return _write_config_file(_render_config(config))
|
|
@@ -261,8 +266,8 @@ def write_full_config(
|
|
|
261
266
|
) -> Path:
|
|
262
267
|
"""Write a complete config file, resetting identity, Memgraph and LLM settings to what is given.
|
|
263
268
|
|
|
264
|
-
Every other section (``[reconcile]``, ``[recall]``, ``[ontology]``, ``[github]``, ``[memory]
|
|
265
|
-
edition are kept from the existing file, since only ``config set`` or a hand edit ever sets them.
|
|
269
|
+
Every other section (``[reconcile]``, ``[recall]``, ``[ontology]``, ``[github]``, ``[memory]``,
|
|
270
|
+
``[session_start]``) and the edition are kept from the existing file, since only ``config set`` or a hand edit ever sets them.
|
|
266
271
|
``auto_reconcile`` is kept likewise unless given here. :func:`write_config` is the partial update.
|
|
267
272
|
"""
|
|
268
273
|
existing = _read_config_file()
|
|
@@ -313,6 +318,7 @@ def _read_config_file() -> HookConfig:
|
|
|
313
318
|
ontology = sections.get("ontology", {})
|
|
314
319
|
github = sections.get("github", {})
|
|
315
320
|
memory = sections.get("memory", {})
|
|
321
|
+
session_start = sections.get("session_start", {})
|
|
316
322
|
auto_reconcile_raw = reconcile.get("auto_reconcile")
|
|
317
323
|
|
|
318
324
|
return HookConfig(
|
|
@@ -331,6 +337,7 @@ def _read_config_file() -> HookConfig:
|
|
|
331
337
|
ontology_derive=ontology.get("derive") or None,
|
|
332
338
|
github_token=github.get("token") or None,
|
|
333
339
|
memory_backend=memory.get("backend") or None,
|
|
340
|
+
session_resume=parse_bool_flag(session_start["resume"]) if session_start.get("resume") else None,
|
|
334
341
|
present=frozenset(
|
|
335
342
|
f"{section}.{key}" if section else key
|
|
336
343
|
for section, keys in sections.items()
|
|
@@ -379,8 +386,8 @@ def _render_config(config: HookConfig) -> str:
|
|
|
379
386
|
"""Render the full config file content.
|
|
380
387
|
|
|
381
388
|
``[reconcile]`` is omitted while ``auto_reconcile`` is ``None`` so a re-read keeps "never configured"
|
|
382
|
-
distinct from an explicit ``false
|
|
383
|
-
omitted while they hold nothing.
|
|
389
|
+
distinct from an explicit ``false``, and ``[session_start]`` likewise while ``session_resume`` is;
|
|
390
|
+
``[recall]``, ``[ontology]``, ``[github]`` and ``[memory]`` are omitted while they hold nothing.
|
|
384
391
|
"""
|
|
385
392
|
user_id = config.user_id or ""
|
|
386
393
|
lines = [
|
|
@@ -419,6 +426,8 @@ def _render_config(config: HookConfig) -> str:
|
|
|
419
426
|
lines += ["", "[github]", f'token = "{config.github_token}"']
|
|
420
427
|
if config.memory_backend:
|
|
421
428
|
lines += ["", "[memory]", f'backend = "{config.memory_backend}"']
|
|
429
|
+
if config.session_resume is not None:
|
|
430
|
+
lines += ["", "[session_start]", f"resume = {'true' if config.session_resume else 'false'}"]
|
|
422
431
|
lines.append("")
|
|
423
432
|
return "\n".join(lines)
|
|
424
433
|
|
|
@@ -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)
|
|
@@ -103,7 +103,10 @@ class RuntimeSpec:
|
|
|
103
103
|
hook file, and is ignored. ``context_before_tool`` renders the stdout JSON
|
|
104
104
|
that shows the model a line of context before a tool runs, leaving the call
|
|
105
105
|
itself untouched; None where the runtime can't do that without denying or
|
|
106
|
-
modifying the call.
|
|
106
|
+
modifying the call. ``session_context`` renders the stdout JSON that puts
|
|
107
|
+
the session-start text (memory index, where the user left off) in front of
|
|
108
|
+
the model when ``session_context_event`` fires; None where no hook output
|
|
109
|
+
reaches the model at session start.
|
|
107
110
|
"""
|
|
108
111
|
|
|
109
112
|
name: str
|
|
@@ -119,6 +122,8 @@ class RuntimeSpec:
|
|
|
119
122
|
foreign_payload_keys: frozenset[str] = frozenset()
|
|
120
123
|
probe_payload: Mapping[str, Any] = field(default_factory=dict)
|
|
121
124
|
context_before_tool: Callable[[str], Mapping[str, Any]] | None = None
|
|
125
|
+
session_context: Callable[[str], Mapping[str, Any]] | None = None
|
|
126
|
+
session_context_event: str = "SessionStart"
|
|
122
127
|
|
|
123
128
|
def event_name(self, payload: Mapping[str, Any]) -> str:
|
|
124
129
|
"""Return *payload*'s canonical event name."""
|
|
@@ -202,6 +207,11 @@ class SpecAdapter(RuntimeAdapter):
|
|
|
202
207
|
return ""
|
|
203
208
|
|
|
204
209
|
|
|
210
|
+
def hook_specific_session_context(text: str) -> dict[str, Any]:
|
|
211
|
+
"""``SessionStart`` output that adds *text* to the model's context; Claude Code and Codex both read it."""
|
|
212
|
+
return {"hookSpecificOutput": {"hookEventName": "SessionStart", "additionalContext": text}}
|
|
213
|
+
|
|
214
|
+
|
|
205
215
|
def pre_tool_use_context(text: str) -> dict[str, Any]:
|
|
206
216
|
"""``PreToolUse`` output that adds *text* to the model's context and leaves the call alone.
|
|
207
217
|
|
|
@@ -247,6 +257,21 @@ class SpecPlugin:
|
|
|
247
257
|
render = self.spec.context_before_tool
|
|
248
258
|
return None if render is None else dict(render(text))
|
|
249
259
|
|
|
260
|
+
def session_context_response(
|
|
261
|
+
self, payload: Mapping[str, Any], text: Callable[[], str | None]
|
|
262
|
+
) -> dict[str, Any] | None:
|
|
263
|
+
"""The stdout JSON that shows the model ``text()`` at session start; None for any other payload.
|
|
264
|
+
|
|
265
|
+
*text* is only called for this runtime's own session-start payload, since building it queries Memgraph.
|
|
266
|
+
"""
|
|
267
|
+
render = self.spec.session_context
|
|
268
|
+
if render is None or self.spec.event_name(payload) != self.spec.session_context_event:
|
|
269
|
+
return None
|
|
270
|
+
if self.spec.foreign_payload_keys & payload.keys():
|
|
271
|
+
return None
|
|
272
|
+
content = text()
|
|
273
|
+
return dict(render(content)) if content else None
|
|
274
|
+
|
|
250
275
|
def build_hooks_config(self, command: str, *, timeout: int = 30) -> dict[str, Any]:
|
|
251
276
|
"""Render the runtime's hook map using *command*."""
|
|
252
277
|
return build_hooks_config(self.spec, command, timeout=timeout)
|