memgraph-context-graph 0.5.0__py3-none-any.whl

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 (33) hide show
  1. memgraph_context_graph/__init__.py +71 -0
  2. memgraph_context_graph/adapters/__init__.py +0 -0
  3. memgraph_context_graph/adapters/_identity.py +520 -0
  4. memgraph_context_graph/adapters/_opencode_plugin.js +112 -0
  5. memgraph_context_graph/adapters/_spec.py +441 -0
  6. memgraph_context_graph/adapters/antigravity_cli.py +152 -0
  7. memgraph_context_graph/adapters/claude.py +297 -0
  8. memgraph_context_graph/adapters/claude_code.py +216 -0
  9. memgraph_context_graph/adapters/claude_code_memory.py +181 -0
  10. memgraph_context_graph/adapters/codex.py +118 -0
  11. memgraph_context_graph/adapters/codex_memory.py +185 -0
  12. memgraph_context_graph/adapters/copilot_cli.py +236 -0
  13. memgraph_context_graph/adapters/cursor.py +189 -0
  14. memgraph_context_graph/adapters/grok.py +187 -0
  15. memgraph_context_graph/adapters/openai.py +240 -0
  16. memgraph_context_graph/adapters/opencode.py +168 -0
  17. memgraph_context_graph/cli.py +845 -0
  18. memgraph_context_graph/events.py +210 -0
  19. memgraph_context_graph/hooks/__init__.py +1 -0
  20. memgraph_context_graph/hooks/cli.py +184 -0
  21. memgraph_context_graph/hooks/runner.py +311 -0
  22. memgraph_context_graph/hooks/runtime_plugin.py +85 -0
  23. memgraph_context_graph/link.py +50 -0
  24. memgraph_context_graph/mcp_server.py +102 -0
  25. memgraph_context_graph/memory_backend.py +144 -0
  26. memgraph_context_graph/protocols.py +67 -0
  27. memgraph_context_graph/spaces.py +214 -0
  28. memgraph_context_graph/tools.py +130 -0
  29. memgraph_context_graph-0.5.0.dist-info/METADATA +718 -0
  30. memgraph_context_graph-0.5.0.dist-info/RECORD +33 -0
  31. memgraph_context_graph-0.5.0.dist-info/WHEEL +4 -0
  32. memgraph_context_graph-0.5.0.dist-info/entry_points.txt +11 -0
  33. memgraph_context_graph-0.5.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,210 @@
1
+ """Common event protocol for agent runtime actions.
2
+
3
+ This module defines runtime-agnostic event types that any adapter can emit
4
+ and any graph connector can consume. Events capture the essential
5
+ information about agent lifecycle moments without coupling to a specific runtime.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from dataclasses import dataclass, field
11
+ from datetime import datetime, timezone
12
+ from enum import Enum
13
+ from typing import Any
14
+
15
+
16
+ class EventType(str, Enum):
17
+ """Types of events that can occur during an agent run."""
18
+
19
+ SESSION_START = "session_start"
20
+ SESSION_END = "session_end"
21
+ TURN_END = "turn_end"
22
+ TOOL_START = "tool_start"
23
+ TOOL_END = "tool_end"
24
+ AGENT_START = "agent_start"
25
+ AGENT_END = "agent_end"
26
+ LLM_START = "llm_start"
27
+ LLM_END = "llm_end"
28
+ HANDOFF = "handoff"
29
+ MESSAGE = "message"
30
+ ERROR = "error"
31
+
32
+
33
+ @dataclass
34
+ class Event:
35
+ """Base event emitted by runtime adapters.
36
+
37
+ Every concrete event subclass carries a fixed ``event_type`` and
38
+ runtime-agnostic payload fields. Adapters populate these from their
39
+ respective runtime callbacks or hook payloads, and connectors decide which fields are
40
+ relevant to them.
41
+ """
42
+
43
+ event_type: EventType
44
+ session_id: str
45
+ timestamp: str = field(default_factory=lambda: datetime.now(timezone.utc).isoformat())
46
+ metadata: dict[str, Any] = field(default_factory=dict)
47
+
48
+ #: The SDK that produced this event (e.g. "claude", "openai").
49
+ source_sdk: str = ""
50
+
51
+
52
+ # ------------------------------------------------------------------
53
+ # Session events
54
+ # ------------------------------------------------------------------
55
+
56
+
57
+ @dataclass
58
+ class SessionStartEvent(Event):
59
+ """Fired when an agent session begins."""
60
+
61
+ event_type: EventType = field(default=EventType.SESSION_START, init=False)
62
+ model: str | None = None
63
+ working_directory: str | None = None
64
+ user_id: str | None = None
65
+
66
+
67
+ @dataclass
68
+ class SessionEndEvent(Event):
69
+ """Fired when an agent session ends."""
70
+
71
+ event_type: EventType = field(default=EventType.SESSION_END, init=False)
72
+ status: str = "completed"
73
+ total_cost_usd: float | None = None
74
+ total_input_tokens: int | None = None
75
+ total_output_tokens: int | None = None
76
+
77
+
78
+ @dataclass
79
+ class TurnEndEvent(Event):
80
+ """Fired when the agent finishes one turn and the session stays open.
81
+
82
+ Runtimes without a session-end hook (Codex, Antigravity, ``opencode run``)
83
+ only ever signal this, so connectors must not treat it as the session
84
+ ending. ``reason`` is the runtime's own stop reason, when it gives one.
85
+ """
86
+
87
+ event_type: EventType = field(default=EventType.TURN_END, init=False)
88
+ reason: str | None = None
89
+
90
+
91
+ # ------------------------------------------------------------------
92
+ # Tool events
93
+ # ------------------------------------------------------------------
94
+
95
+
96
+ @dataclass
97
+ class ToolStartEvent(Event):
98
+ """Fired before a tool/function is invoked."""
99
+
100
+ event_type: EventType = field(default=EventType.TOOL_START, init=False)
101
+ tool_name: str = ""
102
+ tool_input: Any = None
103
+ tool_use_id: str | None = None
104
+ agent_name: str | None = None
105
+
106
+
107
+ @dataclass
108
+ class ToolEndEvent(Event):
109
+ """Fired after a tool/function returns."""
110
+
111
+ event_type: EventType = field(default=EventType.TOOL_END, init=False)
112
+ tool_name: str = ""
113
+ tool_use_id: str | None = None
114
+ result: Any = None
115
+ is_error: bool = False
116
+ error_message: str | None = None
117
+ agent_name: str | None = None
118
+
119
+
120
+ # ------------------------------------------------------------------
121
+ # Agent events
122
+ # ------------------------------------------------------------------
123
+
124
+
125
+ @dataclass
126
+ class AgentStartEvent(Event):
127
+ """Fired when an agent (or subagent) begins execution."""
128
+
129
+ event_type: EventType = field(default=EventType.AGENT_START, init=False)
130
+ agent_name: str = ""
131
+ agent_type: str = ""
132
+ parent_agent_name: str | None = None
133
+
134
+
135
+ @dataclass
136
+ class AgentEndEvent(Event):
137
+ """Fired when an agent (or subagent) finishes execution."""
138
+
139
+ event_type: EventType = field(default=EventType.AGENT_END, init=False)
140
+ agent_name: str = ""
141
+ agent_type: str = ""
142
+ output: Any = None
143
+
144
+
145
+ # ------------------------------------------------------------------
146
+ # LLM events
147
+ # ------------------------------------------------------------------
148
+
149
+
150
+ @dataclass
151
+ class LLMStartEvent(Event):
152
+ """Fired before an LLM call is made."""
153
+
154
+ event_type: EventType = field(default=EventType.LLM_START, init=False)
155
+ agent_name: str | None = None
156
+ system_prompt: str | None = None
157
+ input_items: list[Any] = field(default_factory=list)
158
+
159
+
160
+ @dataclass
161
+ class LLMEndEvent(Event):
162
+ """Fired after an LLM call returns."""
163
+
164
+ event_type: EventType = field(default=EventType.LLM_END, init=False)
165
+ agent_name: str | None = None
166
+ model: str | None = None
167
+ input_tokens: int | None = None
168
+ output_tokens: int | None = None
169
+ response: Any = None
170
+
171
+
172
+ # ------------------------------------------------------------------
173
+ # Communication events
174
+ # ------------------------------------------------------------------
175
+
176
+
177
+ @dataclass
178
+ class HandoffEvent(Event):
179
+ """Fired when one agent hands off to another."""
180
+
181
+ event_type: EventType = field(default=EventType.HANDOFF, init=False)
182
+ from_agent: str = ""
183
+ to_agent: str = ""
184
+
185
+
186
+ @dataclass
187
+ class MessageEvent(Event):
188
+ """Fired when a message is sent (user, assistant, system)."""
189
+
190
+ event_type: EventType = field(default=EventType.MESSAGE, init=False)
191
+ role: str = ""
192
+ content: Any = None
193
+ model: str | None = None
194
+ agent_name: str | None = None
195
+
196
+
197
+ # ------------------------------------------------------------------
198
+ # Error events
199
+ # ------------------------------------------------------------------
200
+
201
+
202
+ @dataclass
203
+ class ErrorOccurredEvent(Event):
204
+ """Fired when an error occurs during agent execution."""
205
+
206
+ event_type: EventType = field(default=EventType.ERROR, init=False)
207
+ error_type: str = ""
208
+ error_message: str = ""
209
+ error_details: dict[str, Any] = field(default_factory=dict)
210
+ recoverable: bool = True
@@ -0,0 +1 @@
1
+ """Command hook entry points for memgraph-context-graph runtimes."""
@@ -0,0 +1,184 @@
1
+ """Generic command hook CLI for memgraph-context-graph runtimes."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import sys
7
+ from pathlib import Path
8
+ from typing import TYPE_CHECKING, Any
9
+
10
+ if TYPE_CHECKING:
11
+ from collections.abc import Sequence
12
+
13
+ _HELP = """usage: memgraph-context-graph hook <command> [options]
14
+
15
+ Bridge agent command hooks to memgraph-context-graph.
16
+
17
+ Commands:
18
+ init <runtime> Generate a runtime's private hook config, if it supports one.
19
+ run <runtime> Run a runtime's command-hook adapter.
20
+
21
+ Runtimes are discovered via the `memgraph_context_graph.runtimes` entry point --
22
+ run `memgraph-context-graph hook run --help` (or see `runtime_plugin.py`) to see
23
+ what's registered in this environment.
24
+ """
25
+
26
+
27
+ def main(argv: Sequence[str] | None = None) -> int:
28
+ """Dispatch to a runtime-specific command hook adapter."""
29
+ args = list(argv) if argv is not None else sys.argv[1:]
30
+ if not args:
31
+ print(_HELP)
32
+ return 2
33
+ if args[0] in {"-h", "--help"}:
34
+ print(_HELP)
35
+ return 0
36
+
37
+ command = args[0]
38
+ if command == "init":
39
+ return _init(args[1:])
40
+
41
+ if command == "run":
42
+ if len(args) == 1:
43
+ print("usage: memgraph-context-graph hook run <runtime> [options]", file=sys.stderr)
44
+ return 2
45
+ runtime = args[1]
46
+ runtime_args = args[2:]
47
+ return _run_runtime(runtime, runtime_args)
48
+
49
+ # Backward-compatible module form:
50
+ # python -m memgraph_context_graph.hooks.cli codex --connector skills-graph
51
+ runtime = command
52
+ runtime_args = args[1:]
53
+ return _run_runtime(runtime, runtime_args)
54
+
55
+
56
+ def _run_runtime(runtime: str, runtime_args: list[str]) -> int:
57
+ from memgraph_context_graph.hooks.runner import run_hook
58
+ from memgraph_context_graph.hooks.runtime_plugin import UnknownRuntimeError, get_runtime_plugin
59
+
60
+ try:
61
+ plugin = get_runtime_plugin(runtime)
62
+ except UnknownRuntimeError as exc:
63
+ print(str(exc), file=sys.stderr)
64
+ return 2
65
+
66
+ return run_hook(plugin, runtime_args)
67
+
68
+
69
+ def _init(argv: list[str]) -> int:
70
+ if not argv:
71
+ print("usage: memgraph-context-graph hook init <runtime> [options]", file=sys.stderr)
72
+ return 2
73
+
74
+ from memgraph_context_graph.hooks.runtime_plugin import UnknownRuntimeError, get_runtime_plugin
75
+
76
+ runtime, init_argv = argv[0], argv[1:]
77
+ try:
78
+ plugin = get_runtime_plugin(runtime)
79
+ except UnknownRuntimeError as exc:
80
+ print(str(exc), file=sys.stderr)
81
+ return 2
82
+
83
+ if any(arg.split("=", 1)[0] == "--memory-backend" for arg in init_argv):
84
+ return _switch_memory_backend(plugin, init_argv)
85
+
86
+ init = getattr(plugin, "init", None)
87
+ if init is None:
88
+ print(f"{plugin.name} hook setup is not implemented yet.", file=sys.stderr)
89
+ return 2
90
+
91
+ return _run_generic_init(plugin.name, init, init_argv)
92
+
93
+
94
+ def _switch_memory_backend(plugin: Any, argv: list[str]) -> int:
95
+ """``setup <runtime> --memory-backend context-graph|native``: whose memory the harness's model uses."""
96
+ from memgraph_context_graph.adapters._identity import MEMORY_BACKEND_GRAPH, MEMORY_BACKEND_NATIVE
97
+ from memgraph_context_graph.memory_backend import MemoryBackendError, use_graph_memory, use_native_memory
98
+
99
+ parser = argparse.ArgumentParser(
100
+ prog=f"memgraph-context-graph setup {plugin.name}",
101
+ description=(
102
+ f"Make Context Graph {plugin.name}'s memory (turning its built-in memory off and importing it), "
103
+ "or hand memory back to the built-in one."
104
+ ),
105
+ )
106
+ parser.add_argument("--memory-backend", required=True, choices=[MEMORY_BACKEND_GRAPH, MEMORY_BACKEND_NATIVE])
107
+ args = parser.parse_args(argv)
108
+ switch = use_graph_memory if args.memory_backend == MEMORY_BACKEND_GRAPH else use_native_memory
109
+ try:
110
+ lines = switch(plugin)
111
+ except MemoryBackendError as exc:
112
+ print(str(exc), file=sys.stderr)
113
+ return 1
114
+ print("\n".join(lines))
115
+ return 0
116
+
117
+
118
+ def _run_generic_init(runtime_name: str, init: Any, argv: list[str]) -> int:
119
+ """Parse the shared ``hook init`` flags and delegate to *init*.
120
+
121
+ The flag set is generic across runtimes. Each plugin's ``init`` writes its
122
+ own config from the project dir, connectors, hook command, timeout, and
123
+ force flag; ``--setup-schema`` (with the ``--memgraph-*`` overrides) is
124
+ handled here, identically for every runtime.
125
+ """
126
+ parser = argparse.ArgumentParser(description=f"Generate a private {runtime_name} hook config.")
127
+ parser.add_argument(
128
+ "--connector",
129
+ action="append",
130
+ default=None,
131
+ help="Graph connector to enable. Defaults to skills-graph, actions-graph, sessions-graph.",
132
+ )
133
+ parser.add_argument(
134
+ "--project-dir",
135
+ default=".",
136
+ help="Project directory where the runtime's config should be generated.",
137
+ )
138
+ parser.add_argument(
139
+ "--hook-command",
140
+ default=None,
141
+ help="Full command to place in the runtime's hook config. Defaults to this installed CLI.",
142
+ )
143
+ parser.add_argument("--memgraph-url", default=None, help="Memgraph Bolt URL for --setup-schema.")
144
+ parser.add_argument("--memgraph-user", default=None, help="Memgraph username for --setup-schema.")
145
+ parser.add_argument(
146
+ "--memgraph-password", default=None, help="Memgraph password for --setup-schema. Never written to disk."
147
+ )
148
+ parser.add_argument("--memgraph-database", default=None, help="Memgraph database for --setup-schema.")
149
+ parser.add_argument(
150
+ "--setup-schema",
151
+ action="store_true",
152
+ help="Connect to Memgraph now and initialize enabled graph connector schemas.",
153
+ )
154
+ parser.add_argument("--timeout", type=int, default=30, help="Hook timeout in seconds.")
155
+ parser.add_argument("--force", action="store_true", help="Overwrite existing generated config.")
156
+ args = parser.parse_args(argv)
157
+
158
+ connectors = args.connector or ["skills-graph", "actions-graph", "sessions-graph"]
159
+ project_dir = Path(args.project_dir).expanduser().resolve()
160
+
161
+ try:
162
+ init(project_dir, connectors, hook_command=args.hook_command, timeout=args.timeout, force=args.force)
163
+ except FileExistsError as exc:
164
+ print(str(exc), file=sys.stderr)
165
+ print("Re-run with --force to replace generated files.", file=sys.stderr)
166
+ return 1
167
+
168
+ if args.setup_schema:
169
+ from memgraph_context_graph.adapters._identity import resolve_memgraph_env
170
+ from memgraph_context_graph.hooks.runner import setup_connector_schemas
171
+
172
+ memgraph_env = resolve_memgraph_env(
173
+ url=args.memgraph_url,
174
+ user=args.memgraph_user,
175
+ password=args.memgraph_password,
176
+ database=args.memgraph_database,
177
+ )
178
+ setup_connector_schemas(connectors, memgraph_env=memgraph_env)
179
+ print(f"Initialized schema on {memgraph_env['MEMGRAPH_URL']} ({memgraph_env['MEMGRAPH_DATABASE']})")
180
+ return 0
181
+
182
+
183
+ if __name__ == "__main__":
184
+ raise SystemExit(main())