agent-context-graph 0.1.8__tar.gz → 0.2.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.
Files changed (30) hide show
  1. {agent_context_graph-0.1.8 → agent_context_graph-0.2.0}/.gitignore +2 -0
  2. {agent_context_graph-0.1.8 → agent_context_graph-0.2.0}/PKG-INFO +146 -30
  3. {agent_context_graph-0.1.8 → agent_context_graph-0.2.0}/README.md +144 -28
  4. {agent_context_graph-0.1.8 → agent_context_graph-0.2.0}/docs/command-hooks.md +31 -0
  5. {agent_context_graph-0.1.8 → agent_context_graph-0.2.0}/pyproject.toml +5 -1
  6. {agent_context_graph-0.1.8 → agent_context_graph-0.2.0}/src/agent_context_graph/__init__.py +10 -2
  7. {agent_context_graph-0.1.8 → agent_context_graph-0.2.0}/src/agent_context_graph/adapters/_identity.py +46 -2
  8. {agent_context_graph-0.1.8 → agent_context_graph-0.2.0}/src/agent_context_graph/adapters/claude.py +13 -0
  9. {agent_context_graph-0.1.8 → agent_context_graph-0.2.0}/src/agent_context_graph/adapters/claude_code.py +34 -177
  10. {agent_context_graph-0.1.8 → agent_context_graph-0.2.0}/src/agent_context_graph/adapters/codex.py +100 -174
  11. {agent_context_graph-0.1.8 → agent_context_graph-0.2.0}/src/agent_context_graph/cli.py +56 -20
  12. {agent_context_graph-0.1.8 → agent_context_graph-0.2.0}/src/agent_context_graph/events.py +1 -0
  13. agent_context_graph-0.2.0/src/agent_context_graph/hooks/cli.py +154 -0
  14. agent_context_graph-0.2.0/src/agent_context_graph/hooks/runner.py +189 -0
  15. agent_context_graph-0.2.0/src/agent_context_graph/hooks/runtime_plugin.py +73 -0
  16. {agent_context_graph-0.1.8 → agent_context_graph-0.2.0}/tests/test_claude_code_adapter.py +31 -0
  17. {agent_context_graph-0.1.8 → agent_context_graph-0.2.0}/tests/test_hook_cli.py +12 -5
  18. {agent_context_graph-0.1.8 → agent_context_graph-0.2.0}/tests/test_identity.py +27 -0
  19. agent_context_graph-0.2.0/tests/test_runtime_plugin.py +50 -0
  20. agent_context_graph-0.1.8/src/agent_context_graph/hooks/cli.py +0 -249
  21. {agent_context_graph-0.1.8 → agent_context_graph-0.2.0}/LICENSE +0 -0
  22. {agent_context_graph-0.1.8 → agent_context_graph-0.2.0}/src/agent_context_graph/adapters/__init__.py +0 -0
  23. {agent_context_graph-0.1.8 → agent_context_graph-0.2.0}/src/agent_context_graph/adapters/openai.py +0 -0
  24. {agent_context_graph-0.1.8 → agent_context_graph-0.2.0}/src/agent_context_graph/hooks/__init__.py +0 -0
  25. {agent_context_graph-0.1.8 → agent_context_graph-0.2.0}/src/agent_context_graph/link.py +0 -0
  26. {agent_context_graph-0.1.8 → agent_context_graph-0.2.0}/src/agent_context_graph/protocols.py +0 -0
  27. {agent_context_graph-0.1.8 → agent_context_graph-0.2.0}/tests/__init__.py +0 -0
  28. {agent_context_graph-0.1.8 → agent_context_graph-0.2.0}/tests/test_codex_adapter.py +0 -0
  29. {agent_context_graph-0.1.8 → agent_context_graph-0.2.0}/tests/test_events.py +0 -0
  30. {agent_context_graph-0.1.8 → agent_context_graph-0.2.0}/tests/test_link.py +0 -0
@@ -190,3 +190,5 @@ cython_debug/
190
190
 
191
191
  # Project specific files
192
192
  /enterprise-context/sic-agent/sic-scrapper/output/*
193
+ unstructured2graph/docs/adr/
194
+ unstructured2graph/CONTEXT.md
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: agent-context-graph
3
- Version: 0.1.8
3
+ Version: 0.2.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
@@ -33,6 +33,8 @@ Runtime Adapter -> Event Protocol -> Graph Connector(s)
33
33
 
34
34
  Runtime plugins are the distribution layer for host-specific hook wiring. They install hooks, skills, and setup helpers for a runtime, then call Agent Context Graph. They are not graph components and should not encode graph-specific meaning.
35
35
 
36
+ > **Just want to capture your Claude Code / Codex sessions?** Start with the [Context Graph guide](../README.md) — it walks through installing the plugin and wiring all the connectors end to end. This README covers the adapter layer itself and the in-process SDK path.
37
+
36
38
  ## Installation
37
39
 
38
40
  For command-hook runtimes such as Codex and Claude Code, prefer a user-level tool install:
@@ -109,6 +111,7 @@ from skills_graph.connector import SkillGraphConnector
109
111
  skills = SkillGraph()
110
112
  skills.setup()
111
113
 
114
+
112
115
  # 2. Define a tool whose name matches the SkillGraphConnector defaults
113
116
  @function_tool
114
117
  def get_skill(name: str) -> str:
@@ -117,6 +120,7 @@ def get_skill(name: str) -> str:
117
120
  return f"Skill '{name}' not found."
118
121
  return f"{skill.name}: {skill.description}\n{skill.content}"
119
122
 
123
+
120
124
  # 3. Wire up the link
121
125
  link = AgentLink()
122
126
  link.add_connector(SkillGraphConnector(skills))
@@ -164,64 +168,98 @@ Implemented:
164
168
 
165
169
  ### First-Time Plugin Setup
166
170
 
167
- For Codex and Claude Code plugins, the recommended first-run path is the bootstrap command. It installs the runtime package, checks Memgraph, installs the graph connector extra, and runs `doctor`.
171
+ For Codex and Claude Code plugins, the recommended first-run path is the bootstrap command. It installs the runtime package (with the connector extras), checks Memgraph, and runs `doctor`.
168
172
 
169
173
  Prerequisites:
170
174
 
171
- - `uv` on `PATH`.
172
- - Memgraph running and reachable over Bolt. Defaults are `bolt://localhost:7687`, empty user/password, and database `memgraph`.
175
+ - `uv` on `PATH`. (`uv` manages Python for the tool; if uv-managed Python downloads are blocked, install Python 3.10+ and rerun bootstrap.)
176
+ - Memgraph running and reachable over Bolt. Defaults are `bolt://localhost:7687`, empty user/password, database `memgraph`. If it isn't running locally:
177
+
178
+ ```bash
179
+ docker run --rm -p 7687:7687 memgraph/memgraph
180
+ ```
173
181
 
174
- **Environment variables:**
182
+ **1. Bootstrap all three connectors** (this is what the installed plugin wires into its hooks):
175
183
 
176
- | Variable | Default | Description |
177
- |---|---|---|
178
- | `MEMGRAPH_URL` | `bolt://localhost:7687` | Bolt URL — set to a remote host for non-local Memgraph |
179
- | `MEMGRAPH_USER` | `""` | Bolt username (service account) |
180
- | `MEMGRAPH_PASSWORD` | `""` | Bolt password or OAuth token |
181
- | `MEMGRAPH_DATABASE` | `memgraph` | Target database name |
182
- | `AGENT_CONTEXT_GRAPH_USER_ID` | _(none)_ | Human identity stored on Memory nodes — required for sessions-graph to associate memories with a user |
184
+ ```bash
185
+ # Codex
186
+ agent-context-graph bootstrap --runtime codex \
187
+ --connector skills-graph --connector actions-graph --connector sessions-graph
188
+
189
+ # Claude Code
190
+ agent-context-graph bootstrap --runtime claude-code \
191
+ --connector skills-graph --connector actions-graph --connector sessions-graph
192
+ ```
183
193
 
184
- If Memgraph is not running locally, start it first:
194
+ The plugin wrapper script runs the same command (and falls back to `uvx` if the tool isn't installed yet):
185
195
 
186
196
  ```bash
187
- docker run --rm -p 7687:7687 memgraph/memgraph
197
+ ./scripts/bootstrap.sh
188
198
  ```
189
199
 
190
- `uv` manages Python for the tool. If uv-managed Python downloads are blocked in your environment, install Python 3.10+ and rerun bootstrap.
200
+ **2. Configure identity and connection.** Bootstrap 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). Set your identity, which sessions-graph requires:
201
+
202
+ ```bash
203
+ agent-context-graph config set identity.user_id "your-name"
204
+ ```
191
205
 
192
- For Codex:
206
+ The Memgraph connection defaults to `bolt://localhost:7687`. For a remote or HA instance:
193
207
 
194
208
  ```bash
195
- agent-context-graph bootstrap --runtime codex --connector skills-graph
209
+ agent-context-graph config set memgraph.url "neo4j://<coordinator-host>:7687"
210
+ agent-context-graph config set memgraph.user "<user>"
211
+ agent-context-graph config set memgraph.password # prompts; stored 0600
212
+ agent-context-graph config set memgraph.database "memgraph"
196
213
  ```
197
214
 
198
- For Claude Code:
215
+ **3. Verify:**
199
216
 
200
217
  ```bash
201
- agent-context-graph bootstrap --runtime claude-code --connector skills-graph
218
+ agent-context-graph config show
219
+ agent-context-graph doctor --runtime claude-code \
220
+ --connector skills-graph --connector actions-graph --connector sessions-graph
202
221
  ```
203
222
 
204
- Expected successful doctor output looks like:
223
+ Expected successful doctor output (use `--runtime codex` for Codex):
205
224
 
206
225
  ```text
207
226
  OK agent-context-graph executable: ...
208
227
  OK agent-context-graph: ...
228
+ OK config: identity.user_id set
229
+ OK memgraph: reachable
209
230
  OK connector:skills-graph: installed=...; memgraph=reachable
210
- OK runtime:codex: strict hook smoke passed
231
+ OK connector:actions-graph: installed=...; memgraph=reachable
232
+ OK connector:sessions-graph: installed=...; memgraph=reachable
233
+ OK runtime:claude-code: strict hook smoke passed
211
234
  ```
212
235
 
213
- Use the matching runtime value when checking Claude Code:
236
+ > **Reconciliation is a separate step.** The connectors capture session activity, but turning a session's text into extracted entities (a `:Person`/`:Organization` graph) is done out-of-band — see [sessions-graph § reconciliation](../sessions-graph/README.md#session-reconciliation). By default a finished session is marked `reconciliation_status = 'pending'` and `sessions-graph reconcile --pending` extracts it.
214
237
 
215
- ```text
216
- OK runtime:claude-code: strict hook smoke passed
238
+ ### Configuration
239
+
240
+ Bootstrap and the `config` command write `~/.config/context-graph/config.toml` (mode `0600`). **Hook subprocesses resolve their configuration from CLI flags and this file only — never from environment variables** (they don't inherit your shell), per [ADR 0002](docs/adr/0002-config-file-only-hook-resolution.md).
241
+
242
+ ```toml
243
+ [identity]
244
+ user_id = "your-name"
245
+
246
+ [memgraph]
247
+ url = "bolt://localhost:7687"
248
+ user = ""
249
+ password = ""
250
+ database = "memgraph"
217
251
  ```
218
252
 
219
- The plugin wrapper scripts call the same bootstrap command. If `agent-context-graph` is not installed yet, they fall back to `uvx`:
253
+ Manage it with:
220
254
 
221
255
  ```bash
222
- ./scripts/bootstrap.sh
256
+ agent-context-graph config show
257
+ agent-context-graph config get memgraph.url
258
+ agent-context-graph config set <key> <value> # keys: identity.user_id, memgraph.{url,user,password,database}
223
259
  ```
224
260
 
261
+ Environment variables (`MEMGRAPH_URL`, `MEMGRAPH_USER`, `MEMGRAPH_PASSWORD`, `MEMGRAPH_DATABASE`, `AGENT_CONTEXT_GRAPH_USER_ID`) are consulted **only at bootstrap time** — if set, `bootstrap` persists them into the config file. Exporting them later has no effect on running hooks; use `config set` instead.
262
+
225
263
  ### OpenAI Codex Plugin
226
264
 
227
265
  Codex hook configuration can be installed as a user-level Codex plugin.
@@ -254,7 +292,7 @@ Check the installed hook environment with:
254
292
  agent-context-graph doctor --runtime codex --connector skills-graph --connector actions-graph --connector sessions-graph
255
293
  ```
256
294
 
257
- Keep graph credentials in the process environment, not in plugin hook files. Runtime hooks use `memgraph-toolbox` defaults unless the Codex process has `MEMGRAPH_*` variables set.
295
+ Graph credentials live in `~/.config/context-graph/config.toml` (written by `bootstrap`/`config set`), not in plugin hook files or the process environment — hooks read that file at runtime. See [Configuration](#configuration).
258
296
 
259
297
  ### Claude Code Plugin
260
298
 
@@ -353,9 +391,11 @@ All runtime adapters emit runtime-agnostic `Event` dataclasses:
353
391
 
354
392
  | Connector | Graph Component | Events Handled |
355
393
  |-----------|----------------|----------------|
356
- | `SkillGraphConnector` | skills-graph | Tool events matching skill access/search operations |
394
+ | `SkillGraphConnector` | [skills-graph](../skills-graph/) | Tool/message events matching skill access/search operations |
395
+ | `ActionsGraphConnector` | [actions-graph](../actions-graph/) | Session, tool, message, subagent, and error events → action nodes |
396
+ | `SessionsGraphConnector` | [sessions-graph](../sessions-graph/) | `SessionStartEvent`/`SessionEndEvent` → `(:User)`, `(:Session)`, `HAD_SESSION`; marks sessions for reconciliation on end |
357
397
 
358
- Additional graph connectors should live in the packages that own those graph schemas.
398
+ The installed plugin wires **all three** (`--connector skills-graph --connector actions-graph --connector sessions-graph`). Each connector lives in the package that owns its graph schema; additional connectors should too.
359
399
 
360
400
  ### Adding a New Runtime Adapter
361
401
 
@@ -365,6 +405,7 @@ Implement `RuntimeAdapter`:
365
405
  from agent_context_graph import AgentLink, ToolStartEvent
366
406
  from agent_context_graph.protocols import RuntimeAdapter
367
407
 
408
+
368
409
  class MyRuntimeAdapter(RuntimeAdapter):
369
410
  def __init__(self, link: AgentLink, session_id: str):
370
411
  self._link = link
@@ -384,6 +425,80 @@ class MyRuntimeAdapter(RuntimeAdapter):
384
425
  )
385
426
  ```
386
427
 
428
+ ### Adding a New Command-Hook Runtime Adapter
429
+
430
+ The pattern above fits **in-process** runtimes — something that calls your Python code directly (an SDK callback, an embedded framework). **Command-hook** runtimes are different: the harness invokes an external command with a JSON payload on stdin (Claude Code, Codex), rather than calling into your process.
431
+
432
+ Three pieces beyond `RuntimeAdapter` itself, exactly as `adapters/claude_code.py` and `adapters/codex.py` implement them:
433
+
434
+ 1. **A payload translator.** Same idea as `RuntimeAdapter.get_runtime_hooks()`, but the input is the harness's raw JSON payload rather than a native callback. Map each of the harness's hook event names to the matching `Event` subclass and call `link.emit(...)`.
435
+ 2. **A hook-config generator** (`build_hooks_config(command)`). Builds whatever config shape the harness expects for wiring hooks, pointing every hook at your CLI entry point.
436
+ 3. **A response function** (`response_for_payload(payload)`). Returns the JSON the harness expects back on stdout, or `None`.
437
+
438
+ The stdin-loading, connector-construction, and CLI-argument-parsing scaffolding around those three pieces is **shared** — `hooks/runner.py`'s `run_hook(plugin, argv)` does that for every registered runtime, so a new adapter doesn't write its own `main()` at all. Register your runtime as a **plugin** and the generic runner (plus `bootstrap`/`doctor`/`hook run`/`hook init`) picks it up automatically — no changes to `agent-context-graph` itself:
439
+
440
+ ```python
441
+ # my_package/adapter.py
442
+ from dataclasses import dataclass
443
+
444
+ from agent_context_graph.protocols import RuntimeAdapter
445
+
446
+
447
+ class MyCommandHookAdapter(RuntimeAdapter):
448
+ def __init__(self, link, session_id: str | None = None):
449
+ self._link = link
450
+ self._session_id = session_id
451
+
452
+ def get_runtime_hooks(self):
453
+ return build_hooks_config("my-runtime hook run my-runtime")
454
+
455
+ def handle_payload(self, payload: dict) -> None:
456
+ for event in self._events_from_payload(payload):
457
+ self._link.emit(event)
458
+
459
+ def _events_from_payload(self, payload: dict):
460
+ # Map the harness's own hook_event_name / payload shape to Event subclasses.
461
+ ...
462
+
463
+
464
+ def build_hooks_config(command: str, *, timeout: int = 30) -> dict:
465
+ # Return whatever config format your harness expects, every hook pointing at `command`.
466
+ ...
467
+
468
+
469
+ def response_for_payload(payload: dict) -> dict | None:
470
+ # Return the JSON your harness expects back, or None.
471
+ ...
472
+
473
+
474
+ @dataclass(frozen=True)
475
+ class MyRuntimePlugin:
476
+ name: str = "my-runtime"
477
+ adapter_class: type = MyCommandHookAdapter
478
+
479
+ def response_for_payload(self, payload: dict) -> dict | None:
480
+ return response_for_payload(payload)
481
+
482
+ def build_hooks_config(self, command: str, *, timeout: int = 30) -> dict:
483
+ return build_hooks_config(command, timeout=timeout)
484
+
485
+ # init(project_dir, connectors, **kwargs) is optional -- omit it if your
486
+ # runtime has no project-local hook-config file to generate (matching
487
+ # ClaudeCodeHooksAdapter's own plugin, which doesn't define one yet).
488
+
489
+
490
+ PLUGIN = MyRuntimePlugin()
491
+ ```
492
+
493
+ Then register it in your own package's `pyproject.toml` — this is the entire integration, no fork or PR against this repo required:
494
+
495
+ ```toml
496
+ [project.entry-points."agent_context_graph.runtimes"]
497
+ my-runtime = "my_package.adapter:PLUGIN"
498
+ ```
499
+
500
+ Once installed, `agent-context-graph bootstrap --runtime my-runtime`, `doctor --runtime my-runtime`, `hook run my-runtime`, and `hook init my-runtime` (if `init` is implemented) all work exactly like the built-in Codex and Claude Code plugins — see `runtime_plugin.py` for the full protocol and `pyproject.toml`'s own `[project.entry-points."agent_context_graph.runtimes"]` for how Codex/Claude Code register themselves.
501
+
387
502
  ### Adding a New Graph Component
388
503
 
389
504
  Implement `GraphConnector` in the graph package:
@@ -392,6 +507,7 @@ Implement `GraphConnector` in the graph package:
392
507
  from agent_context_graph import EventType
393
508
  from agent_context_graph.protocols import GraphConnector
394
509
 
510
+
395
511
  class MyGraphConnector(GraphConnector):
396
512
  def supports(self, event):
397
513
  return event.event_type in {EventType.TOOL_START, EventType.TOOL_END}
@@ -12,6 +12,8 @@ Runtime Adapter -> Event Protocol -> Graph Connector(s)
12
12
 
13
13
  Runtime plugins are the distribution layer for host-specific hook wiring. They install hooks, skills, and setup helpers for a runtime, then call Agent Context Graph. They are not graph components and should not encode graph-specific meaning.
14
14
 
15
+ > **Just want to capture your Claude Code / Codex sessions?** Start with the [Context Graph guide](../README.md) — it walks through installing the plugin and wiring all the connectors end to end. This README covers the adapter layer itself and the in-process SDK path.
16
+
15
17
  ## Installation
16
18
 
17
19
  For command-hook runtimes such as Codex and Claude Code, prefer a user-level tool install:
@@ -88,6 +90,7 @@ from skills_graph.connector import SkillGraphConnector
88
90
  skills = SkillGraph()
89
91
  skills.setup()
90
92
 
93
+
91
94
  # 2. Define a tool whose name matches the SkillGraphConnector defaults
92
95
  @function_tool
93
96
  def get_skill(name: str) -> str:
@@ -96,6 +99,7 @@ def get_skill(name: str) -> str:
96
99
  return f"Skill '{name}' not found."
97
100
  return f"{skill.name}: {skill.description}\n{skill.content}"
98
101
 
102
+
99
103
  # 3. Wire up the link
100
104
  link = AgentLink()
101
105
  link.add_connector(SkillGraphConnector(skills))
@@ -143,64 +147,98 @@ Implemented:
143
147
 
144
148
  ### First-Time Plugin Setup
145
149
 
146
- For Codex and Claude Code plugins, the recommended first-run path is the bootstrap command. It installs the runtime package, checks Memgraph, installs the graph connector extra, and runs `doctor`.
150
+ For Codex and Claude Code plugins, the recommended first-run path is the bootstrap command. It installs the runtime package (with the connector extras), checks Memgraph, and runs `doctor`.
147
151
 
148
152
  Prerequisites:
149
153
 
150
- - `uv` on `PATH`.
151
- - Memgraph running and reachable over Bolt. Defaults are `bolt://localhost:7687`, empty user/password, and database `memgraph`.
154
+ - `uv` on `PATH`. (`uv` manages Python for the tool; if uv-managed Python downloads are blocked, install Python 3.10+ and rerun bootstrap.)
155
+ - Memgraph running and reachable over Bolt. Defaults are `bolt://localhost:7687`, empty user/password, database `memgraph`. If it isn't running locally:
156
+
157
+ ```bash
158
+ docker run --rm -p 7687:7687 memgraph/memgraph
159
+ ```
152
160
 
153
- **Environment variables:**
161
+ **1. Bootstrap all three connectors** (this is what the installed plugin wires into its hooks):
154
162
 
155
- | Variable | Default | Description |
156
- |---|---|---|
157
- | `MEMGRAPH_URL` | `bolt://localhost:7687` | Bolt URL — set to a remote host for non-local Memgraph |
158
- | `MEMGRAPH_USER` | `""` | Bolt username (service account) |
159
- | `MEMGRAPH_PASSWORD` | `""` | Bolt password or OAuth token |
160
- | `MEMGRAPH_DATABASE` | `memgraph` | Target database name |
161
- | `AGENT_CONTEXT_GRAPH_USER_ID` | _(none)_ | Human identity stored on Memory nodes — required for sessions-graph to associate memories with a user |
163
+ ```bash
164
+ # Codex
165
+ agent-context-graph bootstrap --runtime codex \
166
+ --connector skills-graph --connector actions-graph --connector sessions-graph
167
+
168
+ # Claude Code
169
+ agent-context-graph bootstrap --runtime claude-code \
170
+ --connector skills-graph --connector actions-graph --connector sessions-graph
171
+ ```
162
172
 
163
- If Memgraph is not running locally, start it first:
173
+ The plugin wrapper script runs the same command (and falls back to `uvx` if the tool isn't installed yet):
164
174
 
165
175
  ```bash
166
- docker run --rm -p 7687:7687 memgraph/memgraph
176
+ ./scripts/bootstrap.sh
167
177
  ```
168
178
 
169
- `uv` manages Python for the tool. If uv-managed Python downloads are blocked in your environment, install Python 3.10+ and rerun bootstrap.
179
+ **2. Configure identity and connection.** Bootstrap 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). Set your identity, which sessions-graph requires:
180
+
181
+ ```bash
182
+ agent-context-graph config set identity.user_id "your-name"
183
+ ```
170
184
 
171
- For Codex:
185
+ The Memgraph connection defaults to `bolt://localhost:7687`. For a remote or HA instance:
172
186
 
173
187
  ```bash
174
- agent-context-graph bootstrap --runtime codex --connector skills-graph
188
+ agent-context-graph config set memgraph.url "neo4j://<coordinator-host>:7687"
189
+ agent-context-graph config set memgraph.user "<user>"
190
+ agent-context-graph config set memgraph.password # prompts; stored 0600
191
+ agent-context-graph config set memgraph.database "memgraph"
175
192
  ```
176
193
 
177
- For Claude Code:
194
+ **3. Verify:**
178
195
 
179
196
  ```bash
180
- agent-context-graph bootstrap --runtime claude-code --connector skills-graph
197
+ agent-context-graph config show
198
+ agent-context-graph doctor --runtime claude-code \
199
+ --connector skills-graph --connector actions-graph --connector sessions-graph
181
200
  ```
182
201
 
183
- Expected successful doctor output looks like:
202
+ Expected successful doctor output (use `--runtime codex` for Codex):
184
203
 
185
204
  ```text
186
205
  OK agent-context-graph executable: ...
187
206
  OK agent-context-graph: ...
207
+ OK config: identity.user_id set
208
+ OK memgraph: reachable
188
209
  OK connector:skills-graph: installed=...; memgraph=reachable
189
- OK runtime:codex: strict hook smoke passed
210
+ OK connector:actions-graph: installed=...; memgraph=reachable
211
+ OK connector:sessions-graph: installed=...; memgraph=reachable
212
+ OK runtime:claude-code: strict hook smoke passed
190
213
  ```
191
214
 
192
- Use the matching runtime value when checking Claude Code:
215
+ > **Reconciliation is a separate step.** The connectors capture session activity, but turning a session's text into extracted entities (a `:Person`/`:Organization` graph) is done out-of-band — see [sessions-graph § reconciliation](../sessions-graph/README.md#session-reconciliation). By default a finished session is marked `reconciliation_status = 'pending'` and `sessions-graph reconcile --pending` extracts it.
193
216
 
194
- ```text
195
- OK runtime:claude-code: strict hook smoke passed
217
+ ### Configuration
218
+
219
+ Bootstrap and the `config` command write `~/.config/context-graph/config.toml` (mode `0600`). **Hook subprocesses resolve their configuration from CLI flags and this file only — never from environment variables** (they don't inherit your shell), per [ADR 0002](docs/adr/0002-config-file-only-hook-resolution.md).
220
+
221
+ ```toml
222
+ [identity]
223
+ user_id = "your-name"
224
+
225
+ [memgraph]
226
+ url = "bolt://localhost:7687"
227
+ user = ""
228
+ password = ""
229
+ database = "memgraph"
196
230
  ```
197
231
 
198
- The plugin wrapper scripts call the same bootstrap command. If `agent-context-graph` is not installed yet, they fall back to `uvx`:
232
+ Manage it with:
199
233
 
200
234
  ```bash
201
- ./scripts/bootstrap.sh
235
+ agent-context-graph config show
236
+ agent-context-graph config get memgraph.url
237
+ agent-context-graph config set <key> <value> # keys: identity.user_id, memgraph.{url,user,password,database}
202
238
  ```
203
239
 
240
+ Environment variables (`MEMGRAPH_URL`, `MEMGRAPH_USER`, `MEMGRAPH_PASSWORD`, `MEMGRAPH_DATABASE`, `AGENT_CONTEXT_GRAPH_USER_ID`) are consulted **only at bootstrap time** — if set, `bootstrap` persists them into the config file. Exporting them later has no effect on running hooks; use `config set` instead.
241
+
204
242
  ### OpenAI Codex Plugin
205
243
 
206
244
  Codex hook configuration can be installed as a user-level Codex plugin.
@@ -233,7 +271,7 @@ Check the installed hook environment with:
233
271
  agent-context-graph doctor --runtime codex --connector skills-graph --connector actions-graph --connector sessions-graph
234
272
  ```
235
273
 
236
- Keep graph credentials in the process environment, not in plugin hook files. Runtime hooks use `memgraph-toolbox` defaults unless the Codex process has `MEMGRAPH_*` variables set.
274
+ Graph credentials live in `~/.config/context-graph/config.toml` (written by `bootstrap`/`config set`), not in plugin hook files or the process environment — hooks read that file at runtime. See [Configuration](#configuration).
237
275
 
238
276
  ### Claude Code Plugin
239
277
 
@@ -332,9 +370,11 @@ All runtime adapters emit runtime-agnostic `Event` dataclasses:
332
370
 
333
371
  | Connector | Graph Component | Events Handled |
334
372
  |-----------|----------------|----------------|
335
- | `SkillGraphConnector` | skills-graph | Tool events matching skill access/search operations |
373
+ | `SkillGraphConnector` | [skills-graph](../skills-graph/) | Tool/message events matching skill access/search operations |
374
+ | `ActionsGraphConnector` | [actions-graph](../actions-graph/) | Session, tool, message, subagent, and error events → action nodes |
375
+ | `SessionsGraphConnector` | [sessions-graph](../sessions-graph/) | `SessionStartEvent`/`SessionEndEvent` → `(:User)`, `(:Session)`, `HAD_SESSION`; marks sessions for reconciliation on end |
336
376
 
337
- Additional graph connectors should live in the packages that own those graph schemas.
377
+ The installed plugin wires **all three** (`--connector skills-graph --connector actions-graph --connector sessions-graph`). Each connector lives in the package that owns its graph schema; additional connectors should too.
338
378
 
339
379
  ### Adding a New Runtime Adapter
340
380
 
@@ -344,6 +384,7 @@ Implement `RuntimeAdapter`:
344
384
  from agent_context_graph import AgentLink, ToolStartEvent
345
385
  from agent_context_graph.protocols import RuntimeAdapter
346
386
 
387
+
347
388
  class MyRuntimeAdapter(RuntimeAdapter):
348
389
  def __init__(self, link: AgentLink, session_id: str):
349
390
  self._link = link
@@ -363,6 +404,80 @@ class MyRuntimeAdapter(RuntimeAdapter):
363
404
  )
364
405
  ```
365
406
 
407
+ ### Adding a New Command-Hook Runtime Adapter
408
+
409
+ The pattern above fits **in-process** runtimes — something that calls your Python code directly (an SDK callback, an embedded framework). **Command-hook** runtimes are different: the harness invokes an external command with a JSON payload on stdin (Claude Code, Codex), rather than calling into your process.
410
+
411
+ Three pieces beyond `RuntimeAdapter` itself, exactly as `adapters/claude_code.py` and `adapters/codex.py` implement them:
412
+
413
+ 1. **A payload translator.** Same idea as `RuntimeAdapter.get_runtime_hooks()`, but the input is the harness's raw JSON payload rather than a native callback. Map each of the harness's hook event names to the matching `Event` subclass and call `link.emit(...)`.
414
+ 2. **A hook-config generator** (`build_hooks_config(command)`). Builds whatever config shape the harness expects for wiring hooks, pointing every hook at your CLI entry point.
415
+ 3. **A response function** (`response_for_payload(payload)`). Returns the JSON the harness expects back on stdout, or `None`.
416
+
417
+ The stdin-loading, connector-construction, and CLI-argument-parsing scaffolding around those three pieces is **shared** — `hooks/runner.py`'s `run_hook(plugin, argv)` does that for every registered runtime, so a new adapter doesn't write its own `main()` at all. Register your runtime as a **plugin** and the generic runner (plus `bootstrap`/`doctor`/`hook run`/`hook init`) picks it up automatically — no changes to `agent-context-graph` itself:
418
+
419
+ ```python
420
+ # my_package/adapter.py
421
+ from dataclasses import dataclass
422
+
423
+ from agent_context_graph.protocols import RuntimeAdapter
424
+
425
+
426
+ class MyCommandHookAdapter(RuntimeAdapter):
427
+ def __init__(self, link, session_id: str | None = None):
428
+ self._link = link
429
+ self._session_id = session_id
430
+
431
+ def get_runtime_hooks(self):
432
+ return build_hooks_config("my-runtime hook run my-runtime")
433
+
434
+ def handle_payload(self, payload: dict) -> None:
435
+ for event in self._events_from_payload(payload):
436
+ self._link.emit(event)
437
+
438
+ def _events_from_payload(self, payload: dict):
439
+ # Map the harness's own hook_event_name / payload shape to Event subclasses.
440
+ ...
441
+
442
+
443
+ def build_hooks_config(command: str, *, timeout: int = 30) -> dict:
444
+ # Return whatever config format your harness expects, every hook pointing at `command`.
445
+ ...
446
+
447
+
448
+ def response_for_payload(payload: dict) -> dict | None:
449
+ # Return the JSON your harness expects back, or None.
450
+ ...
451
+
452
+
453
+ @dataclass(frozen=True)
454
+ class MyRuntimePlugin:
455
+ name: str = "my-runtime"
456
+ adapter_class: type = MyCommandHookAdapter
457
+
458
+ def response_for_payload(self, payload: dict) -> dict | None:
459
+ return response_for_payload(payload)
460
+
461
+ def build_hooks_config(self, command: str, *, timeout: int = 30) -> dict:
462
+ return build_hooks_config(command, timeout=timeout)
463
+
464
+ # init(project_dir, connectors, **kwargs) is optional -- omit it if your
465
+ # runtime has no project-local hook-config file to generate (matching
466
+ # ClaudeCodeHooksAdapter's own plugin, which doesn't define one yet).
467
+
468
+
469
+ PLUGIN = MyRuntimePlugin()
470
+ ```
471
+
472
+ Then register it in your own package's `pyproject.toml` — this is the entire integration, no fork or PR against this repo required:
473
+
474
+ ```toml
475
+ [project.entry-points."agent_context_graph.runtimes"]
476
+ my-runtime = "my_package.adapter:PLUGIN"
477
+ ```
478
+
479
+ Once installed, `agent-context-graph bootstrap --runtime my-runtime`, `doctor --runtime my-runtime`, `hook run my-runtime`, and `hook init my-runtime` (if `init` is implemented) all work exactly like the built-in Codex and Claude Code plugins — see `runtime_plugin.py` for the full protocol and `pyproject.toml`'s own `[project.entry-points."agent_context_graph.runtimes"]` for how Codex/Claude Code register themselves.
480
+
366
481
  ### Adding a New Graph Component
367
482
 
368
483
  Implement `GraphConnector` in the graph package:
@@ -371,6 +486,7 @@ Implement `GraphConnector` in the graph package:
371
486
  from agent_context_graph import EventType
372
487
  from agent_context_graph.protocols import GraphConnector
373
488
 
489
+
374
490
  class MyGraphConnector(GraphConnector):
375
491
  def supports(self, event):
376
492
  return event.event_type in {EventType.TOOL_START, EventType.TOOL_END}
@@ -51,6 +51,37 @@ The generated hook command does not embed any Memgraph connection values. At run
51
51
 
52
52
  If Memgraph requires a password, provide `MEMGRAPH_PASSWORD` to the Codex process environment. `.codex/hooks.json` should not contain Memgraph credentials.
53
53
 
54
+ For the Claude Code plugin, connection settings instead come from
55
+ `~/.config/context-graph/config.toml` (see "Persistent hook configuration"
56
+ below) rather than from the hook process's environment.
57
+
58
+ ### Persistent hook configuration
59
+
60
+ `agent-context-graph config set/get/show` read and write
61
+ `~/.config/context-graph/config.toml`, which hook subprocesses consult
62
+ directly (CLI flag > config file > default; see ADR 0002). Supported keys:
63
+
64
+ ```text
65
+ identity.user_id
66
+ memgraph.url
67
+ memgraph.user
68
+ memgraph.password
69
+ memgraph.database
70
+ llm.openai_api_key
71
+ llm.anthropic_api_key
72
+ ```
73
+
74
+ The `llm.*` keys are only needed if you enable the `sessions-graph` connector
75
+ with `auto_reconcile` (`SESSIONS_GRAPH_AUTO_RECONCILE=1` at connector
76
+ construction time): reconciliation shells out to a detached
77
+ `sessions-graph reconcile` subprocess that does LLM-backed entity extraction
78
+ via LightRAG, and needs an `OPENAI_API_KEY` (or `ANTHROPIC_API_KEY`) the same
79
+ way it needs Memgraph credentials — resolved from this config file and
80
+ injected into that subprocess's environment explicitly, not inherited from
81
+ ambient shell env (see ADR 0003). `agent-context-graph bootstrap` captures
82
+ `OPENAI_API_KEY`/`ANTHROPIC_API_KEY` from its own environment into the config
83
+ file automatically, the same way it already does for `MEMGRAPH_*`.
84
+
54
85
  To smoke test the generated command, copy the `"command"` value from `.codex/hooks.json` and run:
55
86
 
56
87
  ```bash
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "agent-context-graph"
3
- version = "0.1.8"
3
+ version = "0.2.0"
4
4
  description = "Connect agent SDKs to context-graph components (actions-graph, skills-graph, etc.)"
5
5
  readme = "README.md"
6
6
  license = { text = "MIT" }
@@ -18,6 +18,10 @@ dependencies = [
18
18
  [project.scripts]
19
19
  agent-context-graph = "agent_context_graph.cli:main"
20
20
 
21
+ [project.entry-points."agent_context_graph.runtimes"]
22
+ codex = "agent_context_graph.adapters.codex:PLUGIN"
23
+ claude-code = "agent_context_graph.adapters.claude_code:PLUGIN"
24
+
21
25
  [project.optional-dependencies]
22
26
  claude = [
23
27
  "claude-agent-sdk>=0.1.0",
@@ -21,6 +21,8 @@ Quick Start::
21
21
  hooks = adapter.get_runtime_hooks()
22
22
  """
23
23
 
24
+ from importlib import metadata
25
+
24
26
  from .events import (
25
27
  AgentEndEvent,
26
28
  AgentStartEvent,
@@ -39,6 +41,13 @@ from .events import (
39
41
  from .link import AgentLink
40
42
  from .protocols import GraphConnector, RuntimeAdapter
41
43
 
44
+ try:
45
+ __version__ = metadata.version(__package__)
46
+ except metadata.PackageNotFoundError:
47
+ # Case where package metadata is not available.
48
+ __version__ = ""
49
+ del metadata # optional, avoids polluting the results of dir(__package__)
50
+
42
51
  __all__ = [
43
52
  "AgentEndEvent",
44
53
  "AgentLink",
@@ -56,6 +65,5 @@ __all__ = [
56
65
  "SessionStartEvent",
57
66
  "ToolEndEvent",
58
67
  "ToolStartEvent",
68
+ "__version__",
59
69
  ]
60
-
61
- __version__ = "0.1.0"