agent-coherence 0.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 (52) hide show
  1. agent_coherence-0.1.0.dist-info/METADATA +60 -0
  2. agent_coherence-0.1.0.dist-info/RECORD +52 -0
  3. agent_coherence-0.1.0.dist-info/WHEEL +5 -0
  4. agent_coherence-0.1.0.dist-info/entry_points.txt +4 -0
  5. agent_coherence-0.1.0.dist-info/top_level.txt +1 -0
  6. ccs/__init__.py +6 -0
  7. ccs/adapters/__init__.py +16 -0
  8. ccs/adapters/autogen.py +60 -0
  9. ccs/adapters/base.py +136 -0
  10. ccs/adapters/crewai.py +58 -0
  11. ccs/adapters/langgraph.py +65 -0
  12. ccs/agent/__init__.py +9 -0
  13. ccs/agent/cache.py +63 -0
  14. ccs/agent/runtime.py +151 -0
  15. ccs/artifacts/__init__.py +24 -0
  16. ccs/artifacts/diff_engine.py +82 -0
  17. ccs/bus/__init__.py +8 -0
  18. ccs/bus/event_bus.py +74 -0
  19. ccs/cli/__init__.py +4 -0
  20. ccs/cli/compare.py +57 -0
  21. ccs/cli/simulate.py +70 -0
  22. ccs/coordinator/__init__.py +5 -0
  23. ccs/coordinator/registry.py +116 -0
  24. ccs/coordinator/service.py +261 -0
  25. ccs/core/__init__.py +20 -0
  26. ccs/core/clock.py +31 -0
  27. ccs/core/exceptions.py +32 -0
  28. ccs/core/granularity.py +56 -0
  29. ccs/core/invariants.py +40 -0
  30. ccs/core/states.py +85 -0
  31. ccs/core/types.py +68 -0
  32. ccs/hardening/__init__.py +8 -0
  33. ccs/hardening/architecture.py +236 -0
  34. ccs/output/__init__.py +13 -0
  35. ccs/output/report.py +107 -0
  36. ccs/simulation/__init__.py +19 -0
  37. ccs/simulation/aggregation.py +104 -0
  38. ccs/simulation/bounds.py +22 -0
  39. ccs/simulation/consistency.py +65 -0
  40. ccs/simulation/engine.py +461 -0
  41. ccs/simulation/metrics.py +105 -0
  42. ccs/simulation/scenarios.py +328 -0
  43. ccs/strategies/__init__.py +23 -0
  44. ccs/strategies/access_count.py +47 -0
  45. ccs/strategies/base.py +77 -0
  46. ccs/strategies/broadcast.py +49 -0
  47. ccs/strategies/eager.py +46 -0
  48. ccs/strategies/lazy.py +37 -0
  49. ccs/strategies/lease.py +57 -0
  50. ccs/strategies/selector.py +48 -0
  51. ccs/transport/__init__.py +5 -0
  52. ccs/transport/network_sim.py +96 -0
@@ -0,0 +1,60 @@
1
+ Metadata-Version: 2.4
2
+ Name: agent-coherence
3
+ Version: 0.1.0
4
+ Summary: MESI-style artifact coherence for multi-agent AI systems
5
+ Author: Arbiter contributors
6
+ License: Apache-2.0
7
+ Project-URL: Homepage, https://github.com/hipvlady/agent-coherence
8
+ Project-URL: Repository, https://github.com/hipvlady/agent-coherence
9
+ Project-URL: Issues, https://github.com/hipvlady/agent-coherence/issues
10
+ Keywords: multi-agent,llm,cache-coherence,mesi,token-efficiency,agents
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Requires-Python: >=3.11
18
+ Description-Content-Type: text/markdown
19
+ Requires-Dist: pyyaml>=6.0
20
+ Provides-Extra: dev
21
+ Requires-Dist: pytest>=7.0; extra == "dev"
22
+
23
+ # agent-coherence — The Coherence Protocol for AI Agents
24
+
25
+ [![CI](https://github.com/hipvlady/agent-coherence/actions/workflows/ci.yml/badge.svg)](https://github.com/hipvlady/agent-coherence/actions/workflows/ci.yml)
26
+
27
+ `agent-coherence` implements MESI-style cache coherence for shared artifacts in multi-agent LLM systems, reducing synchronization token overhead and preventing stale-context coordination failures.
28
+
29
+ ## Install
30
+
31
+ ```bash
32
+ pip install agent-coherence
33
+ ```
34
+
35
+ ## Quick start
36
+
37
+ ```python
38
+ from ccs.simulation.engine import run_strategy_comparison
39
+ from ccs.simulation.scenarios import load_scenario
40
+
41
+ scenario = load_scenario("benchmarks/scenarios/planning_canonical.yaml")
42
+ report = run_strategy_comparison(scenario, strategies=["eager", "lazy"], runs=5, seed_start=20260305)
43
+ print(report.to_dict()["aggregated"])
44
+ ```
45
+
46
+ ## Reproduce benchmark artifacts
47
+
48
+ ```bash
49
+ bash reproduce.sh
50
+ ```
51
+
52
+ See [REPRODUCE.md](REPRODUCE.md) for full output mapping and baseline verification details.
53
+
54
+ ## Paper
55
+
56
+ Token Coherence: MESI-Style Cache Coherence for Shared Artifacts in Multi-Agent LLM Systems
57
+
58
+ ## License
59
+
60
+ Apache-2.0 (`LICENSE`)
@@ -0,0 +1,52 @@
1
+ ccs/__init__.py,sha256=2S8ad6p_CbjpSl-X3mDhJj9oXjr1AISYM1hFY3PXkxI,142
2
+ ccs/adapters/__init__.py,sha256=gyT71N9t3Fb8itew4jEdjo28T2g3hR4Lxs6viBmwyLE,404
3
+ ccs/adapters/autogen.py,sha256=Prsk1VVsgYv3ebTPaGnN_zRj8Ddj_TYuBEbinD-rxCE,2025
4
+ ccs/adapters/base.py,sha256=eXN315RUBQ6WglPQGb9Hxcj4MHFY81C5crzQdtGw17U,5027
5
+ ccs/adapters/crewai.py,sha256=WUHOn-Czs5B97RANKWiU8iDjHKD1xANnNu5oWZYTr0U,1967
6
+ ccs/adapters/langgraph.py,sha256=NvRzg25FPBjSzIBtLSJqw9E_IWvkoEZrv8MnISYsj6Q,2268
7
+ ccs/agent/__init__.py,sha256=XJfn-7psLp0Jh5RNsWEWF7ZGW57jdmtYjFWLl9HZbJk,243
8
+ ccs/agent/cache.py,sha256=xVCdW-Y2WvNyjhQlEv87txrgRwL6mOPdl3fSaDRa-eI,2142
9
+ ccs/agent/runtime.py,sha256=IxfrFUuQMufpz3kTyKfAaUQDBmQs5cOwbcP13ry3yAE,5317
10
+ ccs/artifacts/__init__.py,sha256=22WxwBkzE7duFv3QOATR31M8oUZX47U_3gz9knZJpGo,525
11
+ ccs/artifacts/diff_engine.py,sha256=THqQBvNt2jecgYSHrSrU00gnswL4PzPre_g-TQMNn8E,2505
12
+ ccs/bus/__init__.py,sha256=5lC8Ays3kjBYrCNi04V5Xan8yjppT1zielN4aDMVA1w,270
13
+ ccs/bus/event_bus.py,sha256=QId5bn_fcFqSgk1M04ChMdSKJIfx-R5563zUuEIRt6w,2511
14
+ ccs/cli/__init__.py,sha256=e1CJhIy_tAMvtQgBQg-UmJejQN9FlRIA4hIVHaI9IsI,154
15
+ ccs/cli/compare.py,sha256=do-UMWIlHjkF9fM2-Y56LMpxubaRrsVVGl7M3lMiRoc,1947
16
+ ccs/cli/simulate.py,sha256=XRlY-p-M5kuNZQllCs7twZCKcZwr158_SUfCzL5zp-k,2526
17
+ ccs/coordinator/__init__.py,sha256=PrFEsZBKkjjiLAZB73HGlwiN_VeyQpRt4tu9DUYEwuU,142
18
+ ccs/coordinator/registry.py,sha256=qbV-__pYudg0qT9oD_rh_m81fHaJiy9fPqPWjc24Rak,4733
19
+ ccs/coordinator/service.py,sha256=dk3lnKikGorZIYR8aFzxA0ClecpsEPA9AEYjiIghv0I,10131
20
+ ccs/core/__init__.py,sha256=zfMsBhGqaCoRnTVLyY-JC5ek4spreU_vg-8oMuCT5SM,426
21
+ ccs/core/clock.py,sha256=zx9pUq52YzvVF1oE4XxM6-cQ4jL26Szf-0CCFeIYC7Q,869
22
+ ccs/core/exceptions.py,sha256=o5s4AxZrt1QzMuLei9p_31mxF5sqw1OrDfc_p5WgC5I,1033
23
+ ccs/core/granularity.py,sha256=Oa_UPOO-gk6vr5qfhtHDBpx_lfz2HqojRHNFxdu3cl4,1555
24
+ ccs/core/invariants.py,sha256=L4fL7Fbt-aYr1Idr1I9UiE9DqmuKPc2jCQc3xsIuT-c,1342
25
+ ccs/core/states.py,sha256=ip5IPDHs0dtteLwVNunnIMSO91XkkpjVee5f2us0574,2712
26
+ ccs/core/types.py,sha256=2dRw1IV0SulwNxP_Dm2vIKFdxwSzaC8URmVDnLwALOw,1595
27
+ ccs/hardening/__init__.py,sha256=IfdPmq1rAcCvJK9Dmo72SDM7GAEeg4gWT8vqhKGkIGM,264
28
+ ccs/hardening/architecture.py,sha256=WW0DFWkiyc2JpzSefgceQ420lqD4R3f3YruANgCxPlw,7619
29
+ ccs/output/__init__.py,sha256=tmlFl0DjV0tjvKKmTPS39Y8OBxQEgKWxrKUs3A2Y2LU,354
30
+ ccs/output/report.py,sha256=WftfAEWpeWW_CoeMgQA8qmdkbm-YBGfNdG13zMEQBLI,3967
31
+ ccs/simulation/__init__.py,sha256=RYWDi74I39j2oOsB9mrKHTaSB77vcopJGHyg6zaE428,611
32
+ ccs/simulation/aggregation.py,sha256=_5uR93OUKMtFEJsUPZbuyFUZlsyKE8VOYW8PvPMjI50,4245
33
+ ccs/simulation/bounds.py,sha256=mn6AcflF0tJbb3rjaXPeHS-mWAA_Q3rG3qBIT26NoIw,766
34
+ ccs/simulation/consistency.py,sha256=0o6nEzEuoCBWIksQEspeb8jAaaUZCa6Xa7m0asSqzAk,2413
35
+ ccs/simulation/engine.py,sha256=fANyYoTmiIKYM3i22UYZBJulNZRgEBhe75lywfl4sBA,18617
36
+ ccs/simulation/metrics.py,sha256=3Tz3_J6-diTb-6SSjkt8W8GVmUDxdZxyv2hyZ-Vx2RU,3345
37
+ ccs/simulation/scenarios.py,sha256=F3xgrKPFuasNN_VyHV1NqvfFwPjg8sEsVUi-5YboHwc,12294
38
+ ccs/strategies/__init__.py,sha256=E_Y3BAFAEAnLPzAN7PR77dtrC-vccXNHMjftPDegELk,642
39
+ ccs/strategies/access_count.py,sha256=IZ4dWKMlPzENZzM2xlVSrOIlsFZtt9LTQoz8niA0Elw,1375
40
+ ccs/strategies/base.py,sha256=74z_TynciGT-gGMaZkGr95x1MzrHN3KiOI-PYMEkqfs,2388
41
+ ccs/strategies/broadcast.py,sha256=ACYq6sf5SFowwmZJYML5jz_MT5-xljRZv3Whe0-RfOA,1326
42
+ ccs/strategies/eager.py,sha256=CnpyK_F6OdHg2JC2B9Xg2LpJ3uPByJ2RHODiUVbATqU,1237
43
+ ccs/strategies/lazy.py,sha256=dk36rrhmvaUgREHeGpAElx3JdcHiVta1dtt600jyLtk,1025
44
+ ccs/strategies/lease.py,sha256=msTDi4RXC7BrlFV71c1Xm8srngkJs82GAYun14qsUVQ,1674
45
+ ccs/strategies/selector.py,sha256=g34sxyQHAcAWyAPVw-YqY_g0OrItO7cFiOpGtrQXUbg,1489
46
+ ccs/transport/__init__.py,sha256=yOEOoqbjv4BOeOgHKpiwBRWd-gtkUcSHAvpc9Kvfg9c,125
47
+ ccs/transport/network_sim.py,sha256=4JwcHu7Vfturctc4NBPjUX_TgJP5hsySbL5uIGB8CUw,2867
48
+ agent_coherence-0.1.0.dist-info/METADATA,sha256=BgW8EXLjkvhsEq-axrcrrq2xeD-8vMkaMrtiC9Cbe8Y,2036
49
+ agent_coherence-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
50
+ agent_coherence-0.1.0.dist-info/entry_points.txt,sha256=H1ASIW41_8EPpNxEt6--wZhg_xCJV9-fpQWp2aiyWuk,147
51
+ agent_coherence-0.1.0.dist-info/top_level.txt,sha256=B_84q_sceurFYPD4eVb3n5al34HIbMKe3LgAXojdC_g,4
52
+ agent_coherence-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,4 @@
1
+ [console_scripts]
2
+ ccs-check-architecture = ccs.hardening.architecture:main
3
+ ccs-compare = ccs.cli.compare:main
4
+ ccs-simulate = ccs.cli.simulate:main
@@ -0,0 +1 @@
1
+ ccs
ccs/__init__.py ADDED
@@ -0,0 +1,6 @@
1
+ # Copyright (c) 2026 Arbiter contributors.
2
+ # The Coherence Protocol for AI Agents
3
+
4
+ """agent-coherence core package."""
5
+
6
+ __version__ = "0.1.0"
@@ -0,0 +1,16 @@
1
+ # Copyright (c) 2026 Arbiter contributors.
2
+ # The Coherence Protocol for AI Agents
3
+
4
+ """Framework-facing integration adapters for CCS runtime."""
5
+
6
+ from .autogen import AutoGenAdapter
7
+ from .base import CoherenceAdapterCore
8
+ from .crewai import CrewAIAdapter
9
+ from .langgraph import LangGraphAdapter
10
+
11
+ __all__ = [
12
+ "CoherenceAdapterCore",
13
+ "LangGraphAdapter",
14
+ "CrewAIAdapter",
15
+ "AutoGenAdapter",
16
+ ]
@@ -0,0 +1,60 @@
1
+ # Copyright (c) 2026 Arbiter contributors.
2
+ # The Coherence Protocol for AI Agents
3
+
4
+ """AutoGen-oriented coherence adapter utilities."""
5
+
6
+ from __future__ import annotations
7
+
8
+ from typing import Mapping
9
+ from uuid import UUID
10
+
11
+ from ccs.core.types import Artifact
12
+
13
+ from .base import CoherenceAdapterCore
14
+
15
+
16
+ class AutoGenAdapter:
17
+ """Adapter exposing per-turn hooks for AutoGen-like conversations."""
18
+
19
+ def __init__(self, *, strategy_name: str = "lazy", core: CoherenceAdapterCore | None = None) -> None:
20
+ self.core = core if core is not None else CoherenceAdapterCore(strategy_name=strategy_name)
21
+
22
+ def register_agent(self, name: str) -> UUID:
23
+ """Register one conversational agent identity."""
24
+ return self.core.register_agent(name)
25
+
26
+ def register_artifact(self, *, name: str, content: str, size_tokens: int | None = None) -> Artifact:
27
+ """Register shared artifact for conversation context."""
28
+ return self.core.register_artifact(name=name, content=content, size_tokens=size_tokens)
29
+
30
+ def pre_turn_context(
31
+ self,
32
+ *,
33
+ agent_name: str,
34
+ artifact_ids: list[UUID],
35
+ now_tick: int,
36
+ ) -> dict[UUID, str]:
37
+ """Fetch current context before an agent turn."""
38
+ return {
39
+ artifact_id: self.core.read(agent_name=agent_name, artifact_id=artifact_id, now_tick=now_tick).content
40
+ for artifact_id in artifact_ids
41
+ }
42
+
43
+ def post_turn_commit(
44
+ self,
45
+ *,
46
+ agent_name: str,
47
+ updates: Mapping[UUID, str],
48
+ now_tick: int,
49
+ ) -> dict[UUID, int]:
50
+ """Commit turn updates and return updated versions."""
51
+ versions: dict[UUID, int] = {}
52
+ for artifact_id, content in updates.items():
53
+ artifact = self.core.write(
54
+ agent_name=agent_name,
55
+ artifact_id=artifact_id,
56
+ content=content,
57
+ now_tick=now_tick,
58
+ )
59
+ versions[artifact_id] = artifact.version
60
+ return versions
ccs/adapters/base.py ADDED
@@ -0,0 +1,136 @@
1
+ # Copyright (c) 2026 Arbiter contributors.
2
+ # The Coherence Protocol for AI Agents
3
+
4
+ """Common adapter runtime that wires coordinator, agents, and event bus."""
5
+
6
+ from __future__ import annotations
7
+
8
+ from dataclasses import dataclass
9
+ from uuid import NAMESPACE_URL, UUID, uuid5
10
+
11
+ from ccs.agent.runtime import AgentRuntime
12
+ from ccs.bus.event_bus import ArtifactUpdateEvent, InMemoryEventBus
13
+ from ccs.coordinator.registry import ArtifactRegistry
14
+ from ccs.coordinator.service import CoordinatorService
15
+ from ccs.core.types import Artifact, FetchResponse
16
+ from ccs.strategies.base import SyncStrategy
17
+ from ccs.strategies.selector import build_strategy
18
+
19
+
20
+ @dataclass(frozen=True)
21
+ class AgentBinding:
22
+ """Resolved identity/runtime tuple for an adapter-managed agent."""
23
+
24
+ name: str
25
+ agent_id: UUID
26
+ runtime: AgentRuntime
27
+
28
+
29
+ class CoherenceAdapterCore:
30
+ """Reusable cluster abstraction for framework adapters."""
31
+
32
+ def __init__(
33
+ self,
34
+ *,
35
+ strategy_name: str = "lazy",
36
+ lease_ttl_ticks: int = 300,
37
+ access_count_max_accesses: int = 100,
38
+ event_bus: InMemoryEventBus | None = None,
39
+ ) -> None:
40
+ self.registry = ArtifactRegistry()
41
+ self.coordinator = CoordinatorService(self.registry)
42
+ self.strategy: SyncStrategy = build_strategy(
43
+ strategy_name,
44
+ lease_ttl_ticks=lease_ttl_ticks,
45
+ access_count_max_accesses=access_count_max_accesses,
46
+ )
47
+ self.event_bus = event_bus if event_bus is not None else InMemoryEventBus()
48
+ self._agents_by_name: dict[str, AgentBinding] = {}
49
+
50
+ def register_agent(self, name: str) -> UUID:
51
+ """Register one agent runtime and subscribe it to bus events."""
52
+ existing = self._agents_by_name.get(name)
53
+ if existing is not None:
54
+ return existing.agent_id
55
+
56
+ agent_id = uuid5(NAMESPACE_URL, f"ccs-agent:{name}")
57
+ runtime = AgentRuntime(agent_id=agent_id, coordinator=self.coordinator, strategy=self.strategy)
58
+ self.event_bus.subscribe(
59
+ agent_id=agent_id,
60
+ on_invalidation=runtime.handle_invalidation,
61
+ on_update=lambda event, runtime=runtime: runtime.handle_update(
62
+ artifact_id=event.artifact_id,
63
+ version=event.version,
64
+ content=event.content,
65
+ now_tick=event.issued_at_tick,
66
+ writer_agent_id=event.issuer_agent_id,
67
+ ),
68
+ )
69
+ self._agents_by_name[name] = AgentBinding(name=name, agent_id=agent_id, runtime=runtime)
70
+ return agent_id
71
+
72
+ def register_artifact(
73
+ self,
74
+ *,
75
+ name: str,
76
+ content: str,
77
+ size_tokens: int | None = None,
78
+ ) -> Artifact:
79
+ """Register a shared artifact in the coordinator directory."""
80
+ return self.coordinator.register_artifact(name=name, content=content, size_tokens=size_tokens)
81
+
82
+ def read(self, *, agent_name: str, artifact_id: UUID, now_tick: int) -> FetchResponse:
83
+ """Read artifact through one registered runtime."""
84
+ return self._binding(agent_name).runtime.read(artifact_id, now_tick=now_tick)
85
+
86
+ def write(
87
+ self,
88
+ *,
89
+ agent_name: str,
90
+ artifact_id: UUID,
91
+ content: str,
92
+ now_tick: int,
93
+ ) -> Artifact:
94
+ """Write artifact through one runtime and dispatch peer events."""
95
+ writer = self._binding(agent_name)
96
+ updated, invalidation_signals = writer.runtime.write(
97
+ artifact_id=artifact_id,
98
+ content=content,
99
+ now_tick=now_tick,
100
+ )
101
+ peers = [binding.agent_id for binding in self._agents_by_name.values() if binding.agent_id != writer.agent_id]
102
+
103
+ for signal in invalidation_signals:
104
+ self.event_bus.publish_invalidation(signal, recipients=peers)
105
+
106
+ if self.strategy.broadcasts_content_on_commit():
107
+ self.event_bus.publish_update(
108
+ ArtifactUpdateEvent(
109
+ artifact_id=artifact_id,
110
+ version=updated.version,
111
+ content=content,
112
+ issued_at_tick=now_tick,
113
+ issuer_agent_id=writer.agent_id,
114
+ ),
115
+ recipients=peers,
116
+ )
117
+
118
+ return updated
119
+
120
+ def content(self, *, agent_name: str, artifact_id: UUID) -> str | None:
121
+ """Return local content cached by one agent runtime."""
122
+ return self._binding(agent_name).runtime.content(artifact_id)
123
+
124
+ def runtime(self, agent_name: str) -> AgentRuntime:
125
+ """Return concrete runtime for adapter extensions/testing."""
126
+ return self._binding(agent_name).runtime
127
+
128
+ def agent_names(self) -> list[str]:
129
+ """Return registered adapter agent names."""
130
+ return sorted(self._agents_by_name.keys())
131
+
132
+ def _binding(self, agent_name: str) -> AgentBinding:
133
+ binding = self._agents_by_name.get(agent_name)
134
+ if binding is None:
135
+ raise KeyError(f"unknown_agent '{agent_name}'")
136
+ return binding
ccs/adapters/crewai.py ADDED
@@ -0,0 +1,58 @@
1
+ # Copyright (c) 2026 Arbiter contributors.
2
+ # The Coherence Protocol for AI Agents
3
+
4
+ """CrewAI-oriented coherence adapter utilities."""
5
+
6
+ from __future__ import annotations
7
+
8
+ from uuid import UUID
9
+
10
+ from ccs.core.types import Artifact
11
+
12
+ from .base import CoherenceAdapterCore
13
+
14
+
15
+ class CrewAIAdapter:
16
+ """Adapter exposing task lifecycle helpers for CrewAI-style flows."""
17
+
18
+ def __init__(self, *, strategy_name: str = "lazy", core: CoherenceAdapterCore | None = None) -> None:
19
+ self.core = core if core is not None else CoherenceAdapterCore(strategy_name=strategy_name)
20
+
21
+ def register_agent(self, name: str) -> UUID:
22
+ """Register one crew member identity."""
23
+ return self.core.register_agent(name)
24
+
25
+ def register_artifact(self, *, name: str, content: str, size_tokens: int | None = None) -> Artifact:
26
+ """Register shared artifact accessible to the crew."""
27
+ return self.core.register_artifact(name=name, content=content, size_tokens=size_tokens)
28
+
29
+ def prepare_task_context(
30
+ self,
31
+ *,
32
+ agent_name: str,
33
+ artifact_ids: list[UUID],
34
+ now_tick: int,
35
+ ) -> dict[UUID, str]:
36
+ """Materialize artifact content for one task execution."""
37
+ content_by_artifact: dict[UUID, str] = {}
38
+ for artifact_id in artifact_ids:
39
+ response = self.core.read(agent_name=agent_name, artifact_id=artifact_id, now_tick=now_tick)
40
+ content_by_artifact[artifact_id] = response.content
41
+ return content_by_artifact
42
+
43
+ def commit_task_artifact(
44
+ self,
45
+ *,
46
+ agent_name: str,
47
+ artifact_id: UUID,
48
+ content: str,
49
+ now_tick: int,
50
+ ) -> int:
51
+ """Commit task output for one artifact and return resulting version."""
52
+ artifact = self.core.write(
53
+ agent_name=agent_name,
54
+ artifact_id=artifact_id,
55
+ content=content,
56
+ now_tick=now_tick,
57
+ )
58
+ return artifact.version
@@ -0,0 +1,65 @@
1
+ # Copyright (c) 2026 Arbiter contributors.
2
+ # The Coherence Protocol for AI Agents
3
+
4
+ """LangGraph-oriented coherence adapter utilities."""
5
+
6
+ from __future__ import annotations
7
+
8
+ from typing import Mapping
9
+ from uuid import UUID
10
+
11
+ from ccs.core.types import Artifact
12
+
13
+ from .base import CoherenceAdapterCore
14
+
15
+
16
+ class LangGraphAdapter:
17
+ """Adapter exposing pre/post node hooks backed by CCS runtime."""
18
+
19
+ def __init__(self, *, strategy_name: str = "lazy", core: CoherenceAdapterCore | None = None) -> None:
20
+ self.core = core if core is not None else CoherenceAdapterCore(strategy_name=strategy_name)
21
+
22
+ def register_agent(self, name: str) -> UUID:
23
+ """Register node runtime identity."""
24
+ return self.core.register_agent(name)
25
+
26
+ def register_artifact(self, *, name: str, content: str, size_tokens: int | None = None) -> Artifact:
27
+ """Register shared artifact used by LangGraph nodes."""
28
+ return self.core.register_artifact(name=name, content=content, size_tokens=size_tokens)
29
+
30
+ def before_node(
31
+ self,
32
+ *,
33
+ agent_name: str,
34
+ artifact_ids: list[UUID],
35
+ now_tick: int,
36
+ ) -> dict[UUID, dict[str, object]]:
37
+ """Read artifacts before node execution and return context payload."""
38
+ context: dict[UUID, dict[str, object]] = {}
39
+ for artifact_id in artifact_ids:
40
+ response = self.core.read(agent_name=agent_name, artifact_id=artifact_id, now_tick=now_tick)
41
+ context[artifact_id] = {
42
+ "version": response.version,
43
+ "content": response.content,
44
+ "state": response.state_grant.value,
45
+ }
46
+ return context
47
+
48
+ def commit_outputs(
49
+ self,
50
+ *,
51
+ agent_name: str,
52
+ writes: Mapping[UUID, str],
53
+ now_tick: int,
54
+ ) -> dict[UUID, int]:
55
+ """Commit node outputs and return artifact versions."""
56
+ versions: dict[UUID, int] = {}
57
+ for artifact_id, content in writes.items():
58
+ artifact = self.core.write(
59
+ agent_name=agent_name,
60
+ artifact_id=artifact_id,
61
+ content=content,
62
+ now_tick=now_tick,
63
+ )
64
+ versions[artifact_id] = artifact.version
65
+ return versions
ccs/agent/__init__.py ADDED
@@ -0,0 +1,9 @@
1
+ # Copyright (c) 2026 Arbiter contributors.
2
+ # The Coherence Protocol for AI Agents
3
+
4
+ """Agent-side cache and runtime components."""
5
+
6
+ from .cache import ArtifactCache
7
+ from .runtime import AgentRuntime
8
+
9
+ __all__ = ["ArtifactCache", "AgentRuntime"]
ccs/agent/cache.py ADDED
@@ -0,0 +1,63 @@
1
+ # Copyright (c) 2026 Arbiter contributors.
2
+ # The Coherence Protocol for AI Agents
3
+
4
+ """Local artifact cache used by agent runtime."""
5
+
6
+ from __future__ import annotations
7
+
8
+ from dataclasses import replace
9
+ from uuid import UUID
10
+
11
+ from ccs.core.states import MESIState
12
+ from ccs.core.types import ArtifactCacheEntry
13
+
14
+
15
+ class ArtifactCache:
16
+ """In-memory per-agent cache for artifact entries."""
17
+
18
+ def __init__(self) -> None:
19
+ self._entries: dict[UUID, ArtifactCacheEntry] = {}
20
+
21
+ def get(self, artifact_id: UUID) -> ArtifactCacheEntry | None:
22
+ """Return cache entry for artifact if present."""
23
+ return self._entries.get(artifact_id)
24
+
25
+ def put(self, artifact_id: UUID, entry: ArtifactCacheEntry) -> None:
26
+ """Insert or replace cache entry for artifact."""
27
+ self._entries[artifact_id] = entry
28
+
29
+ def invalidate(
30
+ self,
31
+ artifact_id: UUID,
32
+ *,
33
+ invalidated_version: int | None = None,
34
+ issued_at_tick: int = 0,
35
+ ) -> None:
36
+ """Set entry state to INVALID, creating placeholder if missing."""
37
+ entry = self._entries.get(artifact_id)
38
+ if entry is None:
39
+ self._entries[artifact_id] = ArtifactCacheEntry(
40
+ artifact_id=artifact_id,
41
+ state=MESIState.INVALID,
42
+ local_version=max(invalidated_version or 0, 0),
43
+ acquired_at_tick=issued_at_tick,
44
+ )
45
+ return
46
+
47
+ next_version = entry.local_version
48
+ if invalidated_version is not None:
49
+ next_version = min(entry.local_version, invalidated_version)
50
+ self._entries[artifact_id] = replace(
51
+ entry,
52
+ state=MESIState.INVALID,
53
+ local_version=max(next_version, 0),
54
+ )
55
+
56
+ def has_valid(self, artifact_id: UUID) -> bool:
57
+ """Return whether artifact is cached in non-invalid state."""
58
+ entry = self._entries.get(artifact_id)
59
+ return entry is not None and entry.state != MESIState.INVALID
60
+
61
+ def entries(self) -> dict[UUID, ArtifactCacheEntry]:
62
+ """Return shallow copy of cached entries."""
63
+ return dict(self._entries)