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.
- {agent_context_graph-0.1.8 → agent_context_graph-0.2.0}/.gitignore +2 -0
- {agent_context_graph-0.1.8 → agent_context_graph-0.2.0}/PKG-INFO +146 -30
- {agent_context_graph-0.1.8 → agent_context_graph-0.2.0}/README.md +144 -28
- {agent_context_graph-0.1.8 → agent_context_graph-0.2.0}/docs/command-hooks.md +31 -0
- {agent_context_graph-0.1.8 → agent_context_graph-0.2.0}/pyproject.toml +5 -1
- {agent_context_graph-0.1.8 → agent_context_graph-0.2.0}/src/agent_context_graph/__init__.py +10 -2
- {agent_context_graph-0.1.8 → agent_context_graph-0.2.0}/src/agent_context_graph/adapters/_identity.py +46 -2
- {agent_context_graph-0.1.8 → agent_context_graph-0.2.0}/src/agent_context_graph/adapters/claude.py +13 -0
- {agent_context_graph-0.1.8 → agent_context_graph-0.2.0}/src/agent_context_graph/adapters/claude_code.py +34 -177
- {agent_context_graph-0.1.8 → agent_context_graph-0.2.0}/src/agent_context_graph/adapters/codex.py +100 -174
- {agent_context_graph-0.1.8 → agent_context_graph-0.2.0}/src/agent_context_graph/cli.py +56 -20
- {agent_context_graph-0.1.8 → agent_context_graph-0.2.0}/src/agent_context_graph/events.py +1 -0
- agent_context_graph-0.2.0/src/agent_context_graph/hooks/cli.py +154 -0
- agent_context_graph-0.2.0/src/agent_context_graph/hooks/runner.py +189 -0
- agent_context_graph-0.2.0/src/agent_context_graph/hooks/runtime_plugin.py +73 -0
- {agent_context_graph-0.1.8 → agent_context_graph-0.2.0}/tests/test_claude_code_adapter.py +31 -0
- {agent_context_graph-0.1.8 → agent_context_graph-0.2.0}/tests/test_hook_cli.py +12 -5
- {agent_context_graph-0.1.8 → agent_context_graph-0.2.0}/tests/test_identity.py +27 -0
- agent_context_graph-0.2.0/tests/test_runtime_plugin.py +50 -0
- agent_context_graph-0.1.8/src/agent_context_graph/hooks/cli.py +0 -249
- {agent_context_graph-0.1.8 → agent_context_graph-0.2.0}/LICENSE +0 -0
- {agent_context_graph-0.1.8 → agent_context_graph-0.2.0}/src/agent_context_graph/adapters/__init__.py +0 -0
- {agent_context_graph-0.1.8 → agent_context_graph-0.2.0}/src/agent_context_graph/adapters/openai.py +0 -0
- {agent_context_graph-0.1.8 → agent_context_graph-0.2.0}/src/agent_context_graph/hooks/__init__.py +0 -0
- {agent_context_graph-0.1.8 → agent_context_graph-0.2.0}/src/agent_context_graph/link.py +0 -0
- {agent_context_graph-0.1.8 → agent_context_graph-0.2.0}/src/agent_context_graph/protocols.py +0 -0
- {agent_context_graph-0.1.8 → agent_context_graph-0.2.0}/tests/__init__.py +0 -0
- {agent_context_graph-0.1.8 → agent_context_graph-0.2.0}/tests/test_codex_adapter.py +0 -0
- {agent_context_graph-0.1.8 → agent_context_graph-0.2.0}/tests/test_events.py +0 -0
- {agent_context_graph-0.1.8 → agent_context_graph-0.2.0}/tests/test_link.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: agent-context-graph
|
|
3
|
-
Version: 0.
|
|
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
|
|
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,
|
|
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
|
-
**
|
|
182
|
+
**1. Bootstrap all three connectors** (this is what the installed plugin wires into its hooks):
|
|
175
183
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
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
|
-
|
|
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
|
-
|
|
197
|
+
./scripts/bootstrap.sh
|
|
188
198
|
```
|
|
189
199
|
|
|
190
|
-
|
|
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
|
|
206
|
+
The Memgraph connection defaults to `bolt://localhost:7687`. For a remote or HA instance:
|
|
193
207
|
|
|
194
208
|
```bash
|
|
195
|
-
agent-context-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
|
-
|
|
215
|
+
**3. Verify:**
|
|
199
216
|
|
|
200
217
|
```bash
|
|
201
|
-
agent-context-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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
216
|
-
|
|
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
|
-
|
|
253
|
+
Manage it with:
|
|
220
254
|
|
|
221
255
|
```bash
|
|
222
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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,
|
|
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
|
-
**
|
|
161
|
+
**1. Bootstrap all three connectors** (this is what the installed plugin wires into its hooks):
|
|
154
162
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
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
|
-
|
|
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
|
-
|
|
176
|
+
./scripts/bootstrap.sh
|
|
167
177
|
```
|
|
168
178
|
|
|
169
|
-
|
|
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
|
|
185
|
+
The Memgraph connection defaults to `bolt://localhost:7687`. For a remote or HA instance:
|
|
172
186
|
|
|
173
187
|
```bash
|
|
174
|
-
agent-context-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
|
-
|
|
194
|
+
**3. Verify:**
|
|
178
195
|
|
|
179
196
|
```bash
|
|
180
|
-
agent-context-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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
195
|
-
|
|
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
|
-
|
|
232
|
+
Manage it with:
|
|
199
233
|
|
|
200
234
|
```bash
|
|
201
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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"
|