agent_os_kernel 3.1.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 (337) hide show
  1. agent_control_plane/__init__.py +662 -0
  2. agent_control_plane/a2a_adapter.py +543 -0
  3. agent_control_plane/adapter.py +417 -0
  4. agent_control_plane/agent_hibernation.py +394 -0
  5. agent_control_plane/agent_kernel.py +470 -0
  6. agent_control_plane/compliance.py +720 -0
  7. agent_control_plane/constraint_graphs.py +478 -0
  8. agent_control_plane/control_plane.py +854 -0
  9. agent_control_plane/example_executors.py +195 -0
  10. agent_control_plane/execution_engine.py +231 -0
  11. agent_control_plane/flight_recorder.py +846 -0
  12. agent_control_plane/governance_layer.py +435 -0
  13. agent_control_plane/hf_utils.py +563 -0
  14. agent_control_plane/interfaces/__init__.py +55 -0
  15. agent_control_plane/interfaces/kernel_interface.py +361 -0
  16. agent_control_plane/interfaces/plugin_interface.py +497 -0
  17. agent_control_plane/interfaces/protocol_interfaces.py +387 -0
  18. agent_control_plane/kernel_space.py +1009 -0
  19. agent_control_plane/langchain_adapter.py +424 -0
  20. agent_control_plane/lifecycle.py +3113 -0
  21. agent_control_plane/mcp_adapter.py +653 -0
  22. agent_control_plane/ml_safety.py +563 -0
  23. agent_control_plane/multimodal.py +727 -0
  24. agent_control_plane/mute_agent.py +422 -0
  25. agent_control_plane/observability.py +787 -0
  26. agent_control_plane/orchestrator.py +482 -0
  27. agent_control_plane/plugin_registry.py +750 -0
  28. agent_control_plane/policy_engine.py +954 -0
  29. agent_control_plane/process_isolation.py +777 -0
  30. agent_control_plane/shadow_mode.py +310 -0
  31. agent_control_plane/signals.py +493 -0
  32. agent_control_plane/supervisor_agents.py +430 -0
  33. agent_control_plane/time_travel_debugger.py +557 -0
  34. agent_control_plane/tool_registry.py +452 -0
  35. agent_control_plane/vfs.py +697 -0
  36. agent_kernel/__init__.py +69 -0
  37. agent_kernel/analyzer.py +435 -0
  38. agent_kernel/auditor.py +36 -0
  39. agent_kernel/completeness_auditor.py +237 -0
  40. agent_kernel/detector.py +203 -0
  41. agent_kernel/kernel.py +744 -0
  42. agent_kernel/memory_manager.py +85 -0
  43. agent_kernel/models.py +374 -0
  44. agent_kernel/nudge_mechanism.py +263 -0
  45. agent_kernel/outcome_analyzer.py +338 -0
  46. agent_kernel/patcher.py +582 -0
  47. agent_kernel/semantic_analyzer.py +316 -0
  48. agent_kernel/semantic_purge.py +349 -0
  49. agent_kernel/simulator.py +449 -0
  50. agent_kernel/teacher.py +85 -0
  51. agent_kernel/triage.py +152 -0
  52. agent_os/__init__.py +409 -0
  53. agent_os/_adversarial_impl.py +200 -0
  54. agent_os/_circuit_breaker_impl.py +232 -0
  55. agent_os/_mcp_metrics.py +193 -0
  56. agent_os/adversarial.py +20 -0
  57. agent_os/agents_compat.py +490 -0
  58. agent_os/audit_logger.py +135 -0
  59. agent_os/base_agent.py +651 -0
  60. agent_os/circuit_breaker.py +34 -0
  61. agent_os/cli/__init__.py +659 -0
  62. agent_os/cli/cmd_audit.py +128 -0
  63. agent_os/cli/cmd_init.py +152 -0
  64. agent_os/cli/cmd_policy.py +41 -0
  65. agent_os/cli/cmd_policy_gen.py +180 -0
  66. agent_os/cli/cmd_validate.py +258 -0
  67. agent_os/cli/mcp_scan.py +265 -0
  68. agent_os/cli/output.py +192 -0
  69. agent_os/cli/policy_checker.py +330 -0
  70. agent_os/compat.py +74 -0
  71. agent_os/constraint_graph.py +234 -0
  72. agent_os/content_governance.py +140 -0
  73. agent_os/context_budget.py +305 -0
  74. agent_os/credential_redactor.py +224 -0
  75. agent_os/diff_policy.py +89 -0
  76. agent_os/egress_policy.py +159 -0
  77. agent_os/escalation.py +276 -0
  78. agent_os/event_bus.py +124 -0
  79. agent_os/exceptions.py +180 -0
  80. agent_os/execution_context_policy.py +141 -0
  81. agent_os/github_enterprise.py +96 -0
  82. agent_os/health.py +20 -0
  83. agent_os/integrations/__init__.py +279 -0
  84. agent_os/integrations/a2a_adapter.py +279 -0
  85. agent_os/integrations/agent_lightning/__init__.py +30 -0
  86. agent_os/integrations/anthropic_adapter.py +420 -0
  87. agent_os/integrations/autogen_adapter.py +620 -0
  88. agent_os/integrations/base.py +1137 -0
  89. agent_os/integrations/compat.py +229 -0
  90. agent_os/integrations/config.py +98 -0
  91. agent_os/integrations/conversation_guardian.py +957 -0
  92. agent_os/integrations/crewai_adapter.py +467 -0
  93. agent_os/integrations/drift_detector.py +425 -0
  94. agent_os/integrations/dry_run.py +124 -0
  95. agent_os/integrations/escalation.py +582 -0
  96. agent_os/integrations/gemini_adapter.py +364 -0
  97. agent_os/integrations/google_adk_adapter.py +633 -0
  98. agent_os/integrations/guardrails_adapter.py +394 -0
  99. agent_os/integrations/health.py +197 -0
  100. agent_os/integrations/langchain_adapter.py +654 -0
  101. agent_os/integrations/llamafirewall.py +343 -0
  102. agent_os/integrations/llamaindex_adapter.py +188 -0
  103. agent_os/integrations/logging.py +191 -0
  104. agent_os/integrations/maf_adapter.py +631 -0
  105. agent_os/integrations/mistral_adapter.py +365 -0
  106. agent_os/integrations/openai_adapter.py +816 -0
  107. agent_os/integrations/openai_agents_sdk.py +406 -0
  108. agent_os/integrations/policy_compose.py +171 -0
  109. agent_os/integrations/profiling.py +144 -0
  110. agent_os/integrations/pydantic_ai_adapter.py +420 -0
  111. agent_os/integrations/rate_limiter.py +130 -0
  112. agent_os/integrations/rbac.py +143 -0
  113. agent_os/integrations/registry.py +113 -0
  114. agent_os/integrations/scope_guard.py +303 -0
  115. agent_os/integrations/semantic_kernel_adapter.py +769 -0
  116. agent_os/integrations/smolagents_adapter.py +629 -0
  117. agent_os/integrations/templates.py +178 -0
  118. agent_os/integrations/token_budget.py +134 -0
  119. agent_os/integrations/tool_aliases.py +190 -0
  120. agent_os/integrations/webhooks.py +177 -0
  121. agent_os/lite.py +208 -0
  122. agent_os/mcp_gateway.py +385 -0
  123. agent_os/mcp_message_signer.py +273 -0
  124. agent_os/mcp_protocols.py +161 -0
  125. agent_os/mcp_response_scanner.py +232 -0
  126. agent_os/mcp_security.py +924 -0
  127. agent_os/mcp_session_auth.py +231 -0
  128. agent_os/mcp_sliding_rate_limiter.py +184 -0
  129. agent_os/memory_guard.py +409 -0
  130. agent_os/metrics.py +134 -0
  131. agent_os/mute.py +428 -0
  132. agent_os/mute_agent.py +209 -0
  133. agent_os/policies/__init__.py +77 -0
  134. agent_os/policies/async_evaluator.py +275 -0
  135. agent_os/policies/backends.py +670 -0
  136. agent_os/policies/bridge.py +169 -0
  137. agent_os/policies/budget.py +85 -0
  138. agent_os/policies/cli.py +294 -0
  139. agent_os/policies/conflict_resolution.py +270 -0
  140. agent_os/policies/data_classification.py +252 -0
  141. agent_os/policies/evaluator.py +239 -0
  142. agent_os/policies/policy_schema.json +228 -0
  143. agent_os/policies/rate_limiting.py +145 -0
  144. agent_os/policies/schema.py +115 -0
  145. agent_os/policies/shared.py +331 -0
  146. agent_os/prompt_injection.py +694 -0
  147. agent_os/providers.py +182 -0
  148. agent_os/py.typed +0 -0
  149. agent_os/retry.py +81 -0
  150. agent_os/reversibility.py +251 -0
  151. agent_os/sandbox.py +432 -0
  152. agent_os/sandbox_provider.py +140 -0
  153. agent_os/secure_codegen.py +525 -0
  154. agent_os/security_skills.py +538 -0
  155. agent_os/semantic_policy.py +422 -0
  156. agent_os/server/__init__.py +15 -0
  157. agent_os/server/__main__.py +25 -0
  158. agent_os/server/app.py +277 -0
  159. agent_os/server/models.py +104 -0
  160. agent_os/shift_left_metrics.py +130 -0
  161. agent_os/stateless.py +742 -0
  162. agent_os/supervisor.py +148 -0
  163. agent_os/task_outcome.py +148 -0
  164. agent_os/transparency.py +181 -0
  165. agent_os/trust_root.py +128 -0
  166. agent_os_kernel-3.1.0.dist-info/METADATA +1269 -0
  167. agent_os_kernel-3.1.0.dist-info/RECORD +337 -0
  168. agent_os_kernel-3.1.0.dist-info/WHEEL +4 -0
  169. agent_os_kernel-3.1.0.dist-info/entry_points.txt +2 -0
  170. agent_os_kernel-3.1.0.dist-info/licenses/LICENSE +21 -0
  171. agent_os_observability/__init__.py +27 -0
  172. agent_os_observability/dashboards.py +898 -0
  173. agent_os_observability/metrics.py +398 -0
  174. agent_os_observability/server.py +223 -0
  175. agent_os_observability/tracer.py +232 -0
  176. agent_primitives/__init__.py +24 -0
  177. agent_primitives/failures.py +84 -0
  178. agent_primitives/py.typed +0 -0
  179. amb_core/__init__.py +177 -0
  180. amb_core/adapters/__init__.py +57 -0
  181. amb_core/adapters/aws_sqs_broker.py +376 -0
  182. amb_core/adapters/azure_servicebus_broker.py +340 -0
  183. amb_core/adapters/kafka_broker.py +260 -0
  184. amb_core/adapters/nats_broker.py +285 -0
  185. amb_core/adapters/rabbitmq_broker.py +235 -0
  186. amb_core/adapters/redis_broker.py +262 -0
  187. amb_core/broker.py +145 -0
  188. amb_core/bus.py +481 -0
  189. amb_core/cloudevents.py +509 -0
  190. amb_core/dlq.py +345 -0
  191. amb_core/hf_utils.py +536 -0
  192. amb_core/memory_broker.py +410 -0
  193. amb_core/models.py +141 -0
  194. amb_core/persistence.py +529 -0
  195. amb_core/schema.py +294 -0
  196. amb_core/tracing.py +358 -0
  197. atr/__init__.py +640 -0
  198. atr/access.py +348 -0
  199. atr/composition.py +645 -0
  200. atr/decorator.py +357 -0
  201. atr/executor.py +384 -0
  202. atr/health.py +557 -0
  203. atr/hf_utils.py +449 -0
  204. atr/injection.py +422 -0
  205. atr/metrics.py +440 -0
  206. atr/policies.py +403 -0
  207. atr/py.typed +2 -0
  208. atr/registry.py +452 -0
  209. atr/schema.py +480 -0
  210. atr/tools/safe/__init__.py +75 -0
  211. atr/tools/safe/calculator.py +467 -0
  212. atr/tools/safe/datetime_tool.py +443 -0
  213. atr/tools/safe/file_reader.py +402 -0
  214. atr/tools/safe/http_client.py +316 -0
  215. atr/tools/safe/json_parser.py +374 -0
  216. atr/tools/safe/text_tool.py +537 -0
  217. atr/tools/safe/toolkit.py +175 -0
  218. caas/__init__.py +162 -0
  219. caas/api/__init__.py +7 -0
  220. caas/api/server.py +1328 -0
  221. caas/caching.py +834 -0
  222. caas/cli.py +210 -0
  223. caas/conversation.py +223 -0
  224. caas/decay.py +72 -0
  225. caas/detection/__init__.py +9 -0
  226. caas/detection/detector.py +238 -0
  227. caas/enrichment.py +130 -0
  228. caas/gateway/__init__.py +27 -0
  229. caas/gateway/trust_gateway.py +474 -0
  230. caas/hf_utils.py +479 -0
  231. caas/ingestion/__init__.py +23 -0
  232. caas/ingestion/processors.py +253 -0
  233. caas/ingestion/structure_parser.py +188 -0
  234. caas/models.py +356 -0
  235. caas/pragmatic_truth.py +444 -0
  236. caas/routing/__init__.py +10 -0
  237. caas/routing/heuristic_router.py +58 -0
  238. caas/storage/__init__.py +9 -0
  239. caas/storage/store.py +389 -0
  240. caas/triad.py +213 -0
  241. caas/tuning/__init__.py +9 -0
  242. caas/tuning/tuner.py +329 -0
  243. caas/vfs/__init__.py +14 -0
  244. caas/vfs/filesystem.py +452 -0
  245. cmvk/__init__.py +218 -0
  246. cmvk/audit.py +402 -0
  247. cmvk/benchmarks.py +478 -0
  248. cmvk/constitutional.py +904 -0
  249. cmvk/hf_utils.py +301 -0
  250. cmvk/metrics.py +473 -0
  251. cmvk/profiles.py +300 -0
  252. cmvk/py.typed +0 -0
  253. cmvk/types.py +12 -0
  254. cmvk/verification.py +956 -0
  255. emk/__init__.py +89 -0
  256. emk/causal.py +352 -0
  257. emk/hf_utils.py +421 -0
  258. emk/indexer.py +83 -0
  259. emk/py.typed +0 -0
  260. emk/schema.py +204 -0
  261. emk/sleep_cycle.py +347 -0
  262. emk/store.py +281 -0
  263. iatp/__init__.py +166 -0
  264. iatp/attestation.py +461 -0
  265. iatp/cli.py +317 -0
  266. iatp/hf_utils.py +472 -0
  267. iatp/ipc_pipes.py +580 -0
  268. iatp/main.py +412 -0
  269. iatp/models/__init__.py +447 -0
  270. iatp/policy_engine.py +337 -0
  271. iatp/py.typed +2 -0
  272. iatp/recovery.py +321 -0
  273. iatp/security/__init__.py +270 -0
  274. iatp/sidecar/__init__.py +519 -0
  275. iatp/telemetry/__init__.py +164 -0
  276. iatp/tests/__init__.py +1 -0
  277. iatp/tests/test_attestation.py +370 -0
  278. iatp/tests/test_cli.py +131 -0
  279. iatp/tests/test_ed25519_attestation.py +211 -0
  280. iatp/tests/test_models.py +130 -0
  281. iatp/tests/test_policy_engine.py +347 -0
  282. iatp/tests/test_recovery.py +281 -0
  283. iatp/tests/test_security.py +222 -0
  284. iatp/tests/test_sidecar.py +167 -0
  285. iatp/tests/test_telemetry.py +175 -0
  286. mcp_kernel_server/__init__.py +28 -0
  287. mcp_kernel_server/cli.py +274 -0
  288. mcp_kernel_server/resources.py +217 -0
  289. mcp_kernel_server/server.py +564 -0
  290. mcp_kernel_server/tools.py +1174 -0
  291. mute_agent/__init__.py +68 -0
  292. mute_agent/core/__init__.py +1 -0
  293. mute_agent/core/execution_agent.py +166 -0
  294. mute_agent/core/handshake_protocol.py +201 -0
  295. mute_agent/core/reasoning_agent.py +238 -0
  296. mute_agent/knowledge_graph/__init__.py +1 -0
  297. mute_agent/knowledge_graph/graph_elements.py +65 -0
  298. mute_agent/knowledge_graph/multidimensional_graph.py +170 -0
  299. mute_agent/knowledge_graph/subgraph.py +224 -0
  300. mute_agent/listener/__init__.py +43 -0
  301. mute_agent/listener/adapters/__init__.py +31 -0
  302. mute_agent/listener/adapters/base_adapter.py +189 -0
  303. mute_agent/listener/adapters/caas_adapter.py +344 -0
  304. mute_agent/listener/adapters/control_plane_adapter.py +436 -0
  305. mute_agent/listener/adapters/iatp_adapter.py +332 -0
  306. mute_agent/listener/adapters/scak_adapter.py +251 -0
  307. mute_agent/listener/listener.py +610 -0
  308. mute_agent/listener/state_observer.py +436 -0
  309. mute_agent/listener/threshold_config.py +313 -0
  310. mute_agent/super_system/__init__.py +1 -0
  311. mute_agent/super_system/router.py +204 -0
  312. mute_agent/visualization/__init__.py +10 -0
  313. mute_agent/visualization/graph_debugger.py +502 -0
  314. nexus/README.md +60 -0
  315. nexus/__init__.py +51 -0
  316. nexus/arbiter.py +359 -0
  317. nexus/client.py +466 -0
  318. nexus/dmz.py +444 -0
  319. nexus/escrow.py +430 -0
  320. nexus/exceptions.py +286 -0
  321. nexus/pyproject.toml +36 -0
  322. nexus/registry.py +393 -0
  323. nexus/reputation.py +425 -0
  324. nexus/schemas/__init__.py +51 -0
  325. nexus/schemas/compliance.py +276 -0
  326. nexus/schemas/escrow.py +251 -0
  327. nexus/schemas/manifest.py +225 -0
  328. nexus/schemas/receipt.py +208 -0
  329. nexus/tests/__init__.py +0 -0
  330. nexus/tests/conftest.py +146 -0
  331. nexus/tests/test_arbiter.py +192 -0
  332. nexus/tests/test_dmz.py +194 -0
  333. nexus/tests/test_escrow.py +276 -0
  334. nexus/tests/test_exceptions.py +225 -0
  335. nexus/tests/test_registry.py +232 -0
  336. nexus/tests/test_reputation.py +328 -0
  337. nexus/tests/test_schemas.py +295 -0
emk/__init__.py ADDED
@@ -0,0 +1,89 @@
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
3
+ # Public Preview — basic context/memory management
4
+ """
5
+ emk - Episodic Memory Kernel.
6
+
7
+ A mutable ledger of agent experiences for AI systems with causal reasoning
8
+ and sleep-cycle memory compression.
9
+
10
+ Example:
11
+ >>> from emk import Episode, FileAdapter
12
+ >>> store = FileAdapter("memories.jsonl")
13
+ >>> episode = Episode(
14
+ ... goal="Retrieve user data",
15
+ ... action="Query database",
16
+ ... result="Success",
17
+ ... reflection="Efficient query"
18
+ ... )
19
+ >>> episode_id = store.store(episode)
20
+ """
21
+
22
+ from typing import TYPE_CHECKING, List
23
+
24
+ __version__ = "3.1.0"
25
+ __author__ = "Microsoft Corporation"
26
+ __license__ = "MIT"
27
+
28
+ # Core exports - always available
29
+ from emk.schema import Episode, SemanticRule
30
+ from emk.store import VectorStoreAdapter, FileAdapter
31
+ from emk.indexer import Indexer
32
+ from emk.sleep_cycle import MemoryCompressor
33
+
34
+ # Define explicit public API
35
+ __all__: List[str] = [
36
+ # Metadata
37
+ "__version__",
38
+ "__author__",
39
+ "__license__",
40
+ # Core classes
41
+ "Episode",
42
+ "SemanticRule",
43
+ "VectorStoreAdapter",
44
+ "FileAdapter",
45
+ "Indexer",
46
+ "MemoryCompressor",
47
+ ]
48
+
49
+ # Optional ChromaDB adapter - only import if chromadb is installed
50
+ try:
51
+ from emk.store import ChromaDBAdapter
52
+ __all__.append("ChromaDBAdapter")
53
+ except ImportError:
54
+ if TYPE_CHECKING:
55
+ from emk.store import ChromaDBAdapter # noqa: F401
56
+
57
+ # Causal memory (requires sqlite3, always available in stdlib)
58
+ from emk.causal import CausalEpisode, CausalMemoryStore
59
+ __all__.extend(["CausalEpisode", "CausalMemoryStore"])
60
+
61
+ # Optional Hugging Face utilities - only import if huggingface_hub is installed
62
+ try:
63
+ from emk.hf_utils import (
64
+ upload_episodes_to_hub,
65
+ download_episodes_from_hub,
66
+ push_experiment_results,
67
+ )
68
+ __all__.extend([
69
+ "upload_episodes_to_hub",
70
+ "download_episodes_from_hub",
71
+ "push_experiment_results",
72
+ ])
73
+ except ImportError:
74
+ pass
75
+
76
+
77
+ def get_version_info() -> dict:
78
+ """Get detailed version information about the emk package."""
79
+ features = {
80
+ "chromadb": "ChromaDBAdapter" in __all__,
81
+ "huggingface": "upload_episodes_to_hub" in __all__,
82
+ "causal": True,
83
+ }
84
+ return {
85
+ "version": __version__,
86
+ "author": __author__,
87
+ "license": __license__,
88
+ "features": features,
89
+ }
emk/causal.py ADDED
@@ -0,0 +1,352 @@
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
3
+ """
4
+ Causal Episodic Memory — Episodes with causal links.
5
+
6
+ Extends EMK's Episode schema with causal indexing: each episode knows
7
+ what caused it and what effects it triggered. This enables causal
8
+ chain retrieval for debugging, compliance auditing, and RL training.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import hashlib
14
+ import json
15
+ import sqlite3
16
+ import time
17
+ from dataclasses import dataclass, field
18
+ from datetime import datetime, timezone
19
+ from pathlib import Path
20
+ from typing import Any, Dict, List, Optional, Sequence
21
+
22
+
23
+ # ---------------------------------------------------------------------------
24
+ # Data structures
25
+ # ---------------------------------------------------------------------------
26
+
27
+ @dataclass(frozen=True)
28
+ class CausalEpisode:
29
+ """
30
+ An episode enriched with causal links.
31
+
32
+ Parameters
33
+ ----------
34
+ action : str
35
+ What was done.
36
+ params : dict
37
+ Parameters of the action.
38
+ result : dict
39
+ Outcome of the action.
40
+ caused_by : str | None
41
+ Episode ID that triggered this one (backward link).
42
+ caused_effects : tuple[str, ...]
43
+ Episode IDs that this episode subsequently triggered (forward links).
44
+ policy_context : dict
45
+ Active policies when this episode executed.
46
+ trust_context : dict
47
+ Peer trust scores when this episode executed.
48
+ agent_id : str
49
+ DID / identifier of the acting agent.
50
+ timestamp : float
51
+ Unix epoch (auto-generated).
52
+ episode_id : str
53
+ SHA-256 content hash (auto-generated).
54
+ """
55
+
56
+ action: str
57
+ params: Dict[str, Any] = field(default_factory=dict)
58
+ result: Dict[str, Any] = field(default_factory=dict)
59
+ caused_by: Optional[str] = None
60
+ caused_effects: tuple = () # tuple[str, ...]
61
+ policy_context: Dict[str, Any] = field(default_factory=dict)
62
+ trust_context: Dict[str, Any] = field(default_factory=dict)
63
+ agent_id: str = ""
64
+ timestamp: float = field(default_factory=time.time)
65
+ episode_id: str = ""
66
+
67
+ def __post_init__(self) -> None:
68
+ if not self.episode_id:
69
+ content = json.dumps(
70
+ {
71
+ "action": self.action,
72
+ "params": self.params,
73
+ "agent_id": self.agent_id,
74
+ "timestamp": self.timestamp,
75
+ },
76
+ sort_keys=True,
77
+ )
78
+ # frozen dataclass — use object.__setattr__
79
+ object.__setattr__(
80
+ self,
81
+ "episode_id",
82
+ hashlib.sha256(content.encode()).hexdigest(),
83
+ )
84
+
85
+ # -- Serialisation helpers ------------------------------------------------
86
+
87
+ def to_dict(self) -> Dict[str, Any]:
88
+ return {
89
+ "episode_id": self.episode_id,
90
+ "action": self.action,
91
+ "params": self.params,
92
+ "result": self.result,
93
+ "caused_by": self.caused_by,
94
+ "caused_effects": list(self.caused_effects),
95
+ "policy_context": self.policy_context,
96
+ "trust_context": self.trust_context,
97
+ "agent_id": self.agent_id,
98
+ "timestamp": self.timestamp,
99
+ }
100
+
101
+ @classmethod
102
+ def from_dict(cls, data: Dict[str, Any]) -> "CausalEpisode":
103
+ data = dict(data)
104
+ data["caused_effects"] = tuple(data.get("caused_effects") or ())
105
+ return cls(**data)
106
+
107
+
108
+ # ---------------------------------------------------------------------------
109
+ # Causal Memory Store (SQLite-backed)
110
+ # ---------------------------------------------------------------------------
111
+
112
+ _SCHEMA_SQL = """
113
+ CREATE TABLE IF NOT EXISTS episodes (
114
+ episode_id TEXT PRIMARY KEY,
115
+ action TEXT NOT NULL,
116
+ params TEXT NOT NULL DEFAULT '{}',
117
+ result TEXT NOT NULL DEFAULT '{}',
118
+ caused_by TEXT,
119
+ policy_ctx TEXT NOT NULL DEFAULT '{}',
120
+ trust_ctx TEXT NOT NULL DEFAULT '{}',
121
+ agent_id TEXT NOT NULL DEFAULT '',
122
+ ts REAL NOT NULL,
123
+ FOREIGN KEY (caused_by) REFERENCES episodes(episode_id)
124
+ );
125
+
126
+ CREATE TABLE IF NOT EXISTS causal_edges (
127
+ from_id TEXT NOT NULL,
128
+ to_id TEXT NOT NULL,
129
+ PRIMARY KEY (from_id, to_id),
130
+ FOREIGN KEY (from_id) REFERENCES episodes(episode_id),
131
+ FOREIGN KEY (to_id) REFERENCES episodes(episode_id)
132
+ );
133
+
134
+ CREATE INDEX IF NOT EXISTS idx_episodes_agent ON episodes(agent_id);
135
+ CREATE INDEX IF NOT EXISTS idx_episodes_action ON episodes(action);
136
+ CREATE INDEX IF NOT EXISTS idx_edges_from ON causal_edges(from_id);
137
+ CREATE INDEX IF NOT EXISTS idx_edges_to ON causal_edges(to_id);
138
+ """
139
+
140
+
141
+ class CausalMemoryStore:
142
+ """
143
+ Persistent causal episodic memory backed by SQLite.
144
+
145
+ Records episodes with causal links and supports graph traversal
146
+ to retrieve full causal chains.
147
+ """
148
+
149
+ def __init__(self, db_path: str = ":memory:") -> None:
150
+ self._db_path = db_path
151
+ self._conn = sqlite3.connect(db_path)
152
+ self._conn.execute("PRAGMA journal_mode=WAL")
153
+ self._conn.execute("PRAGMA foreign_keys=ON")
154
+ self._conn.executescript(_SCHEMA_SQL)
155
+ self._conn.commit()
156
+
157
+ # -- Core API -------------------------------------------------------------
158
+
159
+ def record(self, episode: CausalEpisode) -> str:
160
+ """
161
+ Record a causal episode.
162
+
163
+ Inserts the episode row and any causal edges (caused_by → this,
164
+ this → each effect). Returns the episode_id.
165
+ """
166
+ cur = self._conn.cursor()
167
+ cur.execute(
168
+ """
169
+ INSERT OR REPLACE INTO episodes
170
+ (episode_id, action, params, result, caused_by,
171
+ policy_ctx, trust_ctx, agent_id, ts)
172
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
173
+ """,
174
+ (
175
+ episode.episode_id,
176
+ episode.action,
177
+ json.dumps(episode.params),
178
+ json.dumps(episode.result),
179
+ episode.caused_by,
180
+ json.dumps(episode.policy_context),
181
+ json.dumps(episode.trust_context),
182
+ episode.agent_id,
183
+ episode.timestamp,
184
+ ),
185
+ )
186
+
187
+ # Backward edge: caused_by → this
188
+ if episode.caused_by:
189
+ cur.execute(
190
+ "INSERT OR IGNORE INTO causal_edges (from_id, to_id) VALUES (?, ?)",
191
+ (episode.caused_by, episode.episode_id),
192
+ )
193
+
194
+ # Forward edges: this → each effect
195
+ for effect_id in episode.caused_effects:
196
+ cur.execute(
197
+ "INSERT OR IGNORE INTO causal_edges (from_id, to_id) VALUES (?, ?)",
198
+ (episode.episode_id, effect_id),
199
+ )
200
+
201
+ self._conn.commit()
202
+ return episode.episode_id
203
+
204
+ def get(self, episode_id: str) -> Optional[CausalEpisode]:
205
+ """Retrieve a single episode by ID."""
206
+ row = self._conn.execute(
207
+ "SELECT * FROM episodes WHERE episode_id = ?", (episode_id,)
208
+ ).fetchone()
209
+ if row is None:
210
+ return None
211
+ return self._row_to_episode(row)
212
+
213
+ def get_effects(self, episode_id: str) -> List[CausalEpisode]:
214
+ """Get all episodes directly caused by *episode_id*."""
215
+ rows = self._conn.execute(
216
+ """
217
+ SELECT e.* FROM episodes e
218
+ JOIN causal_edges ce ON ce.to_id = e.episode_id
219
+ WHERE ce.from_id = ?
220
+ ORDER BY e.ts
221
+ """,
222
+ (episode_id,),
223
+ ).fetchall()
224
+ return [self._row_to_episode(r) for r in rows]
225
+
226
+ def get_causes(self, episode_id: str) -> List[CausalEpisode]:
227
+ """Get all episodes that directly caused *episode_id*."""
228
+ rows = self._conn.execute(
229
+ """
230
+ SELECT e.* FROM episodes e
231
+ JOIN causal_edges ce ON ce.from_id = e.episode_id
232
+ WHERE ce.to_id = ?
233
+ ORDER BY e.ts
234
+ """,
235
+ (episode_id,),
236
+ ).fetchall()
237
+ return [self._row_to_episode(r) for r in rows]
238
+
239
+ def get_causal_chain(
240
+ self,
241
+ episode_id: str,
242
+ *,
243
+ direction: str = "backward",
244
+ max_depth: int = 20,
245
+ ) -> List[CausalEpisode]:
246
+ """
247
+ Walk the causal graph from *episode_id*.
248
+
249
+ Parameters
250
+ ----------
251
+ direction : "backward" | "forward" | "both"
252
+ backward = follow caused_by links (why did this happen?)
253
+ forward = follow caused_effects (what did this cause?)
254
+ both = union of both directions
255
+ max_depth : int
256
+ Maximum traversal depth to prevent infinite loops.
257
+
258
+ Returns
259
+ -------
260
+ List of CausalEpisode ordered by timestamp.
261
+ """
262
+ visited: set[str] = set()
263
+ result: list[CausalEpisode] = []
264
+
265
+ def _walk(eid: str, depth: int, fwd: bool) -> None:
266
+ if depth > max_depth or eid in visited:
267
+ return
268
+ visited.add(eid)
269
+ ep = self.get(eid)
270
+ if ep is None:
271
+ return
272
+ result.append(ep)
273
+ if fwd:
274
+ for child in self.get_effects(eid):
275
+ _walk(child.episode_id, depth + 1, fwd=True)
276
+ else:
277
+ for parent in self.get_causes(eid):
278
+ _walk(parent.episode_id, depth + 1, fwd=False)
279
+
280
+ if direction in ("backward", "both"):
281
+ _walk(episode_id, 0, fwd=False)
282
+ if direction in ("forward", "both"):
283
+ # Allow re-visiting the root so the forward walk can start
284
+ visited.discard(episode_id)
285
+ _walk(episode_id, 0, fwd=True)
286
+
287
+ # Deduplicate + sort by timestamp
288
+ seen: set[str] = set()
289
+ deduped: list[CausalEpisode] = []
290
+ for ep in result:
291
+ if ep.episode_id not in seen:
292
+ seen.add(ep.episode_id)
293
+ deduped.append(ep)
294
+ deduped.sort(key=lambda e: e.timestamp)
295
+ return deduped
296
+
297
+ def query_by_agent(
298
+ self, agent_id: str, *, limit: int = 100
299
+ ) -> List[CausalEpisode]:
300
+ """Get recent episodes for an agent."""
301
+ rows = self._conn.execute(
302
+ "SELECT * FROM episodes WHERE agent_id = ? ORDER BY ts DESC LIMIT ?",
303
+ (agent_id, limit),
304
+ ).fetchall()
305
+ return [self._row_to_episode(r) for r in rows]
306
+
307
+ def query_by_action(
308
+ self, action: str, *, limit: int = 100
309
+ ) -> List[CausalEpisode]:
310
+ """Get recent episodes matching an action type."""
311
+ rows = self._conn.execute(
312
+ "SELECT * FROM episodes WHERE action = ? ORDER BY ts DESC LIMIT ?",
313
+ (action, limit),
314
+ ).fetchall()
315
+ return [self._row_to_episode(r) for r in rows]
316
+
317
+ @property
318
+ def episode_count(self) -> int:
319
+ row = self._conn.execute("SELECT COUNT(*) FROM episodes").fetchone()
320
+ return row[0] if row else 0
321
+
322
+ @property
323
+ def edge_count(self) -> int:
324
+ row = self._conn.execute("SELECT COUNT(*) FROM causal_edges").fetchone()
325
+ return row[0] if row else 0
326
+
327
+ def close(self) -> None:
328
+ self._conn.close()
329
+
330
+ # -- Internal helpers -----------------------------------------------------
331
+
332
+ def _row_to_episode(self, row: tuple) -> CausalEpisode:
333
+ (eid, action, params_json, result_json, caused_by,
334
+ policy_json, trust_json, agent_id, ts) = row
335
+
336
+ # Fetch forward edges
337
+ effects = self._conn.execute(
338
+ "SELECT to_id FROM causal_edges WHERE from_id = ?", (eid,)
339
+ ).fetchall()
340
+
341
+ return CausalEpisode(
342
+ episode_id=eid,
343
+ action=action,
344
+ params=json.loads(params_json),
345
+ result=json.loads(result_json),
346
+ caused_by=caused_by,
347
+ caused_effects=tuple(r[0] for r in effects),
348
+ policy_context=json.loads(policy_json),
349
+ trust_context=json.loads(trust_json),
350
+ agent_id=agent_id,
351
+ timestamp=ts,
352
+ )