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.
- agent_control_plane/__init__.py +662 -0
- agent_control_plane/a2a_adapter.py +543 -0
- agent_control_plane/adapter.py +417 -0
- agent_control_plane/agent_hibernation.py +394 -0
- agent_control_plane/agent_kernel.py +470 -0
- agent_control_plane/compliance.py +720 -0
- agent_control_plane/constraint_graphs.py +478 -0
- agent_control_plane/control_plane.py +854 -0
- agent_control_plane/example_executors.py +195 -0
- agent_control_plane/execution_engine.py +231 -0
- agent_control_plane/flight_recorder.py +846 -0
- agent_control_plane/governance_layer.py +435 -0
- agent_control_plane/hf_utils.py +563 -0
- agent_control_plane/interfaces/__init__.py +55 -0
- agent_control_plane/interfaces/kernel_interface.py +361 -0
- agent_control_plane/interfaces/plugin_interface.py +497 -0
- agent_control_plane/interfaces/protocol_interfaces.py +387 -0
- agent_control_plane/kernel_space.py +1009 -0
- agent_control_plane/langchain_adapter.py +424 -0
- agent_control_plane/lifecycle.py +3113 -0
- agent_control_plane/mcp_adapter.py +653 -0
- agent_control_plane/ml_safety.py +563 -0
- agent_control_plane/multimodal.py +727 -0
- agent_control_plane/mute_agent.py +422 -0
- agent_control_plane/observability.py +787 -0
- agent_control_plane/orchestrator.py +482 -0
- agent_control_plane/plugin_registry.py +750 -0
- agent_control_plane/policy_engine.py +954 -0
- agent_control_plane/process_isolation.py +777 -0
- agent_control_plane/shadow_mode.py +310 -0
- agent_control_plane/signals.py +493 -0
- agent_control_plane/supervisor_agents.py +430 -0
- agent_control_plane/time_travel_debugger.py +557 -0
- agent_control_plane/tool_registry.py +452 -0
- agent_control_plane/vfs.py +697 -0
- agent_kernel/__init__.py +69 -0
- agent_kernel/analyzer.py +435 -0
- agent_kernel/auditor.py +36 -0
- agent_kernel/completeness_auditor.py +237 -0
- agent_kernel/detector.py +203 -0
- agent_kernel/kernel.py +744 -0
- agent_kernel/memory_manager.py +85 -0
- agent_kernel/models.py +374 -0
- agent_kernel/nudge_mechanism.py +263 -0
- agent_kernel/outcome_analyzer.py +338 -0
- agent_kernel/patcher.py +582 -0
- agent_kernel/semantic_analyzer.py +316 -0
- agent_kernel/semantic_purge.py +349 -0
- agent_kernel/simulator.py +449 -0
- agent_kernel/teacher.py +85 -0
- agent_kernel/triage.py +152 -0
- agent_os/__init__.py +409 -0
- agent_os/_adversarial_impl.py +200 -0
- agent_os/_circuit_breaker_impl.py +232 -0
- agent_os/_mcp_metrics.py +193 -0
- agent_os/adversarial.py +20 -0
- agent_os/agents_compat.py +490 -0
- agent_os/audit_logger.py +135 -0
- agent_os/base_agent.py +651 -0
- agent_os/circuit_breaker.py +34 -0
- agent_os/cli/__init__.py +659 -0
- agent_os/cli/cmd_audit.py +128 -0
- agent_os/cli/cmd_init.py +152 -0
- agent_os/cli/cmd_policy.py +41 -0
- agent_os/cli/cmd_policy_gen.py +180 -0
- agent_os/cli/cmd_validate.py +258 -0
- agent_os/cli/mcp_scan.py +265 -0
- agent_os/cli/output.py +192 -0
- agent_os/cli/policy_checker.py +330 -0
- agent_os/compat.py +74 -0
- agent_os/constraint_graph.py +234 -0
- agent_os/content_governance.py +140 -0
- agent_os/context_budget.py +305 -0
- agent_os/credential_redactor.py +224 -0
- agent_os/diff_policy.py +89 -0
- agent_os/egress_policy.py +159 -0
- agent_os/escalation.py +276 -0
- agent_os/event_bus.py +124 -0
- agent_os/exceptions.py +180 -0
- agent_os/execution_context_policy.py +141 -0
- agent_os/github_enterprise.py +96 -0
- agent_os/health.py +20 -0
- agent_os/integrations/__init__.py +279 -0
- agent_os/integrations/a2a_adapter.py +279 -0
- agent_os/integrations/agent_lightning/__init__.py +30 -0
- agent_os/integrations/anthropic_adapter.py +420 -0
- agent_os/integrations/autogen_adapter.py +620 -0
- agent_os/integrations/base.py +1137 -0
- agent_os/integrations/compat.py +229 -0
- agent_os/integrations/config.py +98 -0
- agent_os/integrations/conversation_guardian.py +957 -0
- agent_os/integrations/crewai_adapter.py +467 -0
- agent_os/integrations/drift_detector.py +425 -0
- agent_os/integrations/dry_run.py +124 -0
- agent_os/integrations/escalation.py +582 -0
- agent_os/integrations/gemini_adapter.py +364 -0
- agent_os/integrations/google_adk_adapter.py +633 -0
- agent_os/integrations/guardrails_adapter.py +394 -0
- agent_os/integrations/health.py +197 -0
- agent_os/integrations/langchain_adapter.py +654 -0
- agent_os/integrations/llamafirewall.py +343 -0
- agent_os/integrations/llamaindex_adapter.py +188 -0
- agent_os/integrations/logging.py +191 -0
- agent_os/integrations/maf_adapter.py +631 -0
- agent_os/integrations/mistral_adapter.py +365 -0
- agent_os/integrations/openai_adapter.py +816 -0
- agent_os/integrations/openai_agents_sdk.py +406 -0
- agent_os/integrations/policy_compose.py +171 -0
- agent_os/integrations/profiling.py +144 -0
- agent_os/integrations/pydantic_ai_adapter.py +420 -0
- agent_os/integrations/rate_limiter.py +130 -0
- agent_os/integrations/rbac.py +143 -0
- agent_os/integrations/registry.py +113 -0
- agent_os/integrations/scope_guard.py +303 -0
- agent_os/integrations/semantic_kernel_adapter.py +769 -0
- agent_os/integrations/smolagents_adapter.py +629 -0
- agent_os/integrations/templates.py +178 -0
- agent_os/integrations/token_budget.py +134 -0
- agent_os/integrations/tool_aliases.py +190 -0
- agent_os/integrations/webhooks.py +177 -0
- agent_os/lite.py +208 -0
- agent_os/mcp_gateway.py +385 -0
- agent_os/mcp_message_signer.py +273 -0
- agent_os/mcp_protocols.py +161 -0
- agent_os/mcp_response_scanner.py +232 -0
- agent_os/mcp_security.py +924 -0
- agent_os/mcp_session_auth.py +231 -0
- agent_os/mcp_sliding_rate_limiter.py +184 -0
- agent_os/memory_guard.py +409 -0
- agent_os/metrics.py +134 -0
- agent_os/mute.py +428 -0
- agent_os/mute_agent.py +209 -0
- agent_os/policies/__init__.py +77 -0
- agent_os/policies/async_evaluator.py +275 -0
- agent_os/policies/backends.py +670 -0
- agent_os/policies/bridge.py +169 -0
- agent_os/policies/budget.py +85 -0
- agent_os/policies/cli.py +294 -0
- agent_os/policies/conflict_resolution.py +270 -0
- agent_os/policies/data_classification.py +252 -0
- agent_os/policies/evaluator.py +239 -0
- agent_os/policies/policy_schema.json +228 -0
- agent_os/policies/rate_limiting.py +145 -0
- agent_os/policies/schema.py +115 -0
- agent_os/policies/shared.py +331 -0
- agent_os/prompt_injection.py +694 -0
- agent_os/providers.py +182 -0
- agent_os/py.typed +0 -0
- agent_os/retry.py +81 -0
- agent_os/reversibility.py +251 -0
- agent_os/sandbox.py +432 -0
- agent_os/sandbox_provider.py +140 -0
- agent_os/secure_codegen.py +525 -0
- agent_os/security_skills.py +538 -0
- agent_os/semantic_policy.py +422 -0
- agent_os/server/__init__.py +15 -0
- agent_os/server/__main__.py +25 -0
- agent_os/server/app.py +277 -0
- agent_os/server/models.py +104 -0
- agent_os/shift_left_metrics.py +130 -0
- agent_os/stateless.py +742 -0
- agent_os/supervisor.py +148 -0
- agent_os/task_outcome.py +148 -0
- agent_os/transparency.py +181 -0
- agent_os/trust_root.py +128 -0
- agent_os_kernel-3.1.0.dist-info/METADATA +1269 -0
- agent_os_kernel-3.1.0.dist-info/RECORD +337 -0
- agent_os_kernel-3.1.0.dist-info/WHEEL +4 -0
- agent_os_kernel-3.1.0.dist-info/entry_points.txt +2 -0
- agent_os_kernel-3.1.0.dist-info/licenses/LICENSE +21 -0
- agent_os_observability/__init__.py +27 -0
- agent_os_observability/dashboards.py +898 -0
- agent_os_observability/metrics.py +398 -0
- agent_os_observability/server.py +223 -0
- agent_os_observability/tracer.py +232 -0
- agent_primitives/__init__.py +24 -0
- agent_primitives/failures.py +84 -0
- agent_primitives/py.typed +0 -0
- amb_core/__init__.py +177 -0
- amb_core/adapters/__init__.py +57 -0
- amb_core/adapters/aws_sqs_broker.py +376 -0
- amb_core/adapters/azure_servicebus_broker.py +340 -0
- amb_core/adapters/kafka_broker.py +260 -0
- amb_core/adapters/nats_broker.py +285 -0
- amb_core/adapters/rabbitmq_broker.py +235 -0
- amb_core/adapters/redis_broker.py +262 -0
- amb_core/broker.py +145 -0
- amb_core/bus.py +481 -0
- amb_core/cloudevents.py +509 -0
- amb_core/dlq.py +345 -0
- amb_core/hf_utils.py +536 -0
- amb_core/memory_broker.py +410 -0
- amb_core/models.py +141 -0
- amb_core/persistence.py +529 -0
- amb_core/schema.py +294 -0
- amb_core/tracing.py +358 -0
- atr/__init__.py +640 -0
- atr/access.py +348 -0
- atr/composition.py +645 -0
- atr/decorator.py +357 -0
- atr/executor.py +384 -0
- atr/health.py +557 -0
- atr/hf_utils.py +449 -0
- atr/injection.py +422 -0
- atr/metrics.py +440 -0
- atr/policies.py +403 -0
- atr/py.typed +2 -0
- atr/registry.py +452 -0
- atr/schema.py +480 -0
- atr/tools/safe/__init__.py +75 -0
- atr/tools/safe/calculator.py +467 -0
- atr/tools/safe/datetime_tool.py +443 -0
- atr/tools/safe/file_reader.py +402 -0
- atr/tools/safe/http_client.py +316 -0
- atr/tools/safe/json_parser.py +374 -0
- atr/tools/safe/text_tool.py +537 -0
- atr/tools/safe/toolkit.py +175 -0
- caas/__init__.py +162 -0
- caas/api/__init__.py +7 -0
- caas/api/server.py +1328 -0
- caas/caching.py +834 -0
- caas/cli.py +210 -0
- caas/conversation.py +223 -0
- caas/decay.py +72 -0
- caas/detection/__init__.py +9 -0
- caas/detection/detector.py +238 -0
- caas/enrichment.py +130 -0
- caas/gateway/__init__.py +27 -0
- caas/gateway/trust_gateway.py +474 -0
- caas/hf_utils.py +479 -0
- caas/ingestion/__init__.py +23 -0
- caas/ingestion/processors.py +253 -0
- caas/ingestion/structure_parser.py +188 -0
- caas/models.py +356 -0
- caas/pragmatic_truth.py +444 -0
- caas/routing/__init__.py +10 -0
- caas/routing/heuristic_router.py +58 -0
- caas/storage/__init__.py +9 -0
- caas/storage/store.py +389 -0
- caas/triad.py +213 -0
- caas/tuning/__init__.py +9 -0
- caas/tuning/tuner.py +329 -0
- caas/vfs/__init__.py +14 -0
- caas/vfs/filesystem.py +452 -0
- cmvk/__init__.py +218 -0
- cmvk/audit.py +402 -0
- cmvk/benchmarks.py +478 -0
- cmvk/constitutional.py +904 -0
- cmvk/hf_utils.py +301 -0
- cmvk/metrics.py +473 -0
- cmvk/profiles.py +300 -0
- cmvk/py.typed +0 -0
- cmvk/types.py +12 -0
- cmvk/verification.py +956 -0
- emk/__init__.py +89 -0
- emk/causal.py +352 -0
- emk/hf_utils.py +421 -0
- emk/indexer.py +83 -0
- emk/py.typed +0 -0
- emk/schema.py +204 -0
- emk/sleep_cycle.py +347 -0
- emk/store.py +281 -0
- iatp/__init__.py +166 -0
- iatp/attestation.py +461 -0
- iatp/cli.py +317 -0
- iatp/hf_utils.py +472 -0
- iatp/ipc_pipes.py +580 -0
- iatp/main.py +412 -0
- iatp/models/__init__.py +447 -0
- iatp/policy_engine.py +337 -0
- iatp/py.typed +2 -0
- iatp/recovery.py +321 -0
- iatp/security/__init__.py +270 -0
- iatp/sidecar/__init__.py +519 -0
- iatp/telemetry/__init__.py +164 -0
- iatp/tests/__init__.py +1 -0
- iatp/tests/test_attestation.py +370 -0
- iatp/tests/test_cli.py +131 -0
- iatp/tests/test_ed25519_attestation.py +211 -0
- iatp/tests/test_models.py +130 -0
- iatp/tests/test_policy_engine.py +347 -0
- iatp/tests/test_recovery.py +281 -0
- iatp/tests/test_security.py +222 -0
- iatp/tests/test_sidecar.py +167 -0
- iatp/tests/test_telemetry.py +175 -0
- mcp_kernel_server/__init__.py +28 -0
- mcp_kernel_server/cli.py +274 -0
- mcp_kernel_server/resources.py +217 -0
- mcp_kernel_server/server.py +564 -0
- mcp_kernel_server/tools.py +1174 -0
- mute_agent/__init__.py +68 -0
- mute_agent/core/__init__.py +1 -0
- mute_agent/core/execution_agent.py +166 -0
- mute_agent/core/handshake_protocol.py +201 -0
- mute_agent/core/reasoning_agent.py +238 -0
- mute_agent/knowledge_graph/__init__.py +1 -0
- mute_agent/knowledge_graph/graph_elements.py +65 -0
- mute_agent/knowledge_graph/multidimensional_graph.py +170 -0
- mute_agent/knowledge_graph/subgraph.py +224 -0
- mute_agent/listener/__init__.py +43 -0
- mute_agent/listener/adapters/__init__.py +31 -0
- mute_agent/listener/adapters/base_adapter.py +189 -0
- mute_agent/listener/adapters/caas_adapter.py +344 -0
- mute_agent/listener/adapters/control_plane_adapter.py +436 -0
- mute_agent/listener/adapters/iatp_adapter.py +332 -0
- mute_agent/listener/adapters/scak_adapter.py +251 -0
- mute_agent/listener/listener.py +610 -0
- mute_agent/listener/state_observer.py +436 -0
- mute_agent/listener/threshold_config.py +313 -0
- mute_agent/super_system/__init__.py +1 -0
- mute_agent/super_system/router.py +204 -0
- mute_agent/visualization/__init__.py +10 -0
- mute_agent/visualization/graph_debugger.py +502 -0
- nexus/README.md +60 -0
- nexus/__init__.py +51 -0
- nexus/arbiter.py +359 -0
- nexus/client.py +466 -0
- nexus/dmz.py +444 -0
- nexus/escrow.py +430 -0
- nexus/exceptions.py +286 -0
- nexus/pyproject.toml +36 -0
- nexus/registry.py +393 -0
- nexus/reputation.py +425 -0
- nexus/schemas/__init__.py +51 -0
- nexus/schemas/compliance.py +276 -0
- nexus/schemas/escrow.py +251 -0
- nexus/schemas/manifest.py +225 -0
- nexus/schemas/receipt.py +208 -0
- nexus/tests/__init__.py +0 -0
- nexus/tests/conftest.py +146 -0
- nexus/tests/test_arbiter.py +192 -0
- nexus/tests/test_dmz.py +194 -0
- nexus/tests/test_escrow.py +276 -0
- nexus/tests/test_exceptions.py +225 -0
- nexus/tests/test_registry.py +232 -0
- nexus/tests/test_reputation.py +328 -0
- 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
|
+
)
|