agent-checkpoint-framework 0.2.0__tar.gz → 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (21) hide show
  1. {agent_checkpoint_framework-0.2.0 → agent_checkpoint_framework-0.3.0}/PKG-INFO +1 -1
  2. {agent_checkpoint_framework-0.2.0 → agent_checkpoint_framework-0.3.0}/pyproject.toml +1 -1
  3. {agent_checkpoint_framework-0.2.0 → agent_checkpoint_framework-0.3.0}/src/agent_checkpoint/__init__.py +4 -0
  4. agent_checkpoint_framework-0.3.0/src/agent_checkpoint/manifest.py +98 -0
  5. {agent_checkpoint_framework-0.2.0 → agent_checkpoint_framework-0.3.0}/src/agent_checkpoint_framework.egg-info/PKG-INFO +1 -1
  6. {agent_checkpoint_framework-0.2.0 → agent_checkpoint_framework-0.3.0}/src/agent_checkpoint_framework.egg-info/SOURCES.txt +3 -1
  7. agent_checkpoint_framework-0.3.0/tests/test_manifest.py +109 -0
  8. {agent_checkpoint_framework-0.2.0 → agent_checkpoint_framework-0.3.0}/README.md +0 -0
  9. {agent_checkpoint_framework-0.2.0 → agent_checkpoint_framework-0.3.0}/setup.cfg +0 -0
  10. {agent_checkpoint_framework-0.2.0 → agent_checkpoint_framework-0.3.0}/src/agent_checkpoint/codec.py +0 -0
  11. {agent_checkpoint_framework-0.2.0 → agent_checkpoint_framework-0.3.0}/src/agent_checkpoint/factory.py +0 -0
  12. {agent_checkpoint_framework-0.2.0 → agent_checkpoint_framework-0.3.0}/src/agent_checkpoint/manager.py +0 -0
  13. {agent_checkpoint_framework-0.2.0 → agent_checkpoint_framework-0.3.0}/src/agent_checkpoint/models.py +0 -0
  14. {agent_checkpoint_framework-0.2.0 → agent_checkpoint_framework-0.3.0}/src/agent_checkpoint/storage.py +0 -0
  15. {agent_checkpoint_framework-0.2.0 → agent_checkpoint_framework-0.3.0}/src/agent_checkpoint/validator.py +0 -0
  16. {agent_checkpoint_framework-0.2.0 → agent_checkpoint_framework-0.3.0}/src/agent_checkpoint_framework.egg-info/dependency_links.txt +0 -0
  17. {agent_checkpoint_framework-0.2.0 → agent_checkpoint_framework-0.3.0}/src/agent_checkpoint_framework.egg-info/requires.txt +0 -0
  18. {agent_checkpoint_framework-0.2.0 → agent_checkpoint_framework-0.3.0}/src/agent_checkpoint_framework.egg-info/top_level.txt +0 -0
  19. {agent_checkpoint_framework-0.2.0 → agent_checkpoint_framework-0.3.0}/tests/test_codec.py +0 -0
  20. {agent_checkpoint_framework-0.2.0 → agent_checkpoint_framework-0.3.0}/tests/test_factory.py +0 -0
  21. {agent_checkpoint_framework-0.2.0 → agent_checkpoint_framework-0.3.0}/tests/test_manager.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agent-checkpoint-framework
3
- Version: 0.2.0
3
+ Version: 0.3.0
4
4
  Summary: Never lose work when an agent fails.
5
5
  Requires-Python: >=3.11
6
6
  Description-Content-Type: text/markdown
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "agent-checkpoint-framework"
7
- version = "0.2.0"
7
+ version = "0.3.0"
8
8
  description = "Never lose work when an agent fails."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -1,6 +1,7 @@
1
1
  from .codec import ParseResult, emit, emit_json, parse
2
2
  from .factory import new_checkpoint, resume_from
3
3
  from .manager import CheckpointManager
4
+ from .manifest import AgentManifest, emit_manifest, parse_manifest
4
5
  from .models import Checkpoint, CheckpointStatus, RetryStrategy, TrustedValue, TrustTier
5
6
  from .storage import InMemoryStorage, Storage
6
7
  from .validator import ValidationError
@@ -23,4 +24,7 @@ __all__ = [
23
24
  "CheckpointManager",
24
25
  "Storage",
25
26
  "InMemoryStorage",
27
+ "AgentManifest",
28
+ "parse_manifest",
29
+ "emit_manifest",
26
30
  ]
@@ -0,0 +1,98 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ from dataclasses import asdict, dataclass, field
5
+ from typing import Any, Dict, List, Optional, Union
6
+
7
+
8
+ @dataclass
9
+ class ConfidenceRange:
10
+ """Range of confidence scores the agent operates within."""
11
+
12
+ min: float = 0.0
13
+ max: float = 1.0
14
+
15
+ def __post_init__(self) -> None:
16
+ if not (0.0 <= self.min <= 1.0):
17
+ raise ValueError(f"min must be between 0.0 and 1.0; got {self.min}")
18
+ if not (0.0 <= self.max <= 1.0):
19
+ raise ValueError(f"max must be between 0.0 and 1.0; got {self.max}")
20
+ if self.min > self.max:
21
+ raise ValueError(f"min ({self.min}) cannot be greater than max ({self.max})")
22
+
23
+
24
+ @dataclass
25
+ class AgentManifest:
26
+ """Manifest describing an agent's capabilities and constraints."""
27
+
28
+ ac_version: str
29
+ agent_id: str
30
+ capabilities: List[str]
31
+ refuses: List[str] = field(default_factory=list)
32
+ confidence_range: Optional[ConfidenceRange] = None
33
+ max_context_tokens: Optional[int] = None
34
+ contact: Optional[str] = None
35
+
36
+
37
+ def parse_manifest(data: Union[str, Dict[str, Any]]) -> Optional[AgentManifest]:
38
+ """
39
+ Parses a JSON string or dictionary into an AgentManifest object.
40
+ Returns None on any error or if required fields are missing.
41
+ """
42
+ try:
43
+ if isinstance(data, str):
44
+ raw_dict = json.loads(data)
45
+ else:
46
+ raw_dict = data
47
+
48
+ if not isinstance(raw_dict, dict):
49
+ return None
50
+
51
+ # Check required fields
52
+ if (
53
+ "ac_version" not in raw_dict
54
+ or "agent_id" not in raw_dict
55
+ or "capabilities" not in raw_dict
56
+ ):
57
+ return None
58
+
59
+ conf_range = None
60
+ raw_conf = raw_dict.get("confidence_range")
61
+ if isinstance(raw_conf, dict):
62
+ try:
63
+ conf_range = ConfidenceRange(
64
+ min=float(raw_conf.get("min", 0.0)),
65
+ max=float(raw_conf.get("max", 1.0)),
66
+ )
67
+ except (ValueError, TypeError):
68
+ # If confidence range is invalid, we continue without it
69
+ pass
70
+
71
+ return AgentManifest(
72
+ ac_version=str(raw_dict["ac_version"]),
73
+ agent_id=str(raw_dict["agent_id"]),
74
+ capabilities=list(raw_dict["capabilities"]),
75
+ refuses=list(raw_dict.get("refuses", [])),
76
+ confidence_range=conf_range,
77
+ max_context_tokens=raw_dict.get("max_context_tokens"),
78
+ contact=raw_dict.get("contact"),
79
+ )
80
+ except Exception:
81
+ # Never raise, return None on any failure
82
+ return None
83
+
84
+
85
+ def emit_manifest(manifest: AgentManifest) -> str:
86
+ """
87
+ Converts an AgentManifest to a JSON string.
88
+ Omits fields with None values.
89
+ """
90
+ raw = asdict(manifest)
91
+
92
+ def _remove_none(obj: Any) -> Any:
93
+ if isinstance(obj, dict):
94
+ return {k: _remove_none(v) for k, v in obj.items() if v is not None}
95
+ return obj
96
+
97
+ clean = _remove_none(raw)
98
+ return json.dumps(clean, indent=2)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agent-checkpoint-framework
3
- Version: 0.2.0
3
+ Version: 0.3.0
4
4
  Summary: Never lose work when an agent fails.
5
5
  Requires-Python: >=3.11
6
6
  Description-Content-Type: text/markdown
@@ -4,6 +4,7 @@ src/agent_checkpoint/__init__.py
4
4
  src/agent_checkpoint/codec.py
5
5
  src/agent_checkpoint/factory.py
6
6
  src/agent_checkpoint/manager.py
7
+ src/agent_checkpoint/manifest.py
7
8
  src/agent_checkpoint/models.py
8
9
  src/agent_checkpoint/storage.py
9
10
  src/agent_checkpoint/validator.py
@@ -14,4 +15,5 @@ src/agent_checkpoint_framework.egg-info/requires.txt
14
15
  src/agent_checkpoint_framework.egg-info/top_level.txt
15
16
  tests/test_codec.py
16
17
  tests/test_factory.py
17
- tests/test_manager.py
18
+ tests/test_manager.py
19
+ tests/test_manifest.py
@@ -0,0 +1,109 @@
1
+ import json
2
+
3
+ import pytest
4
+
5
+ from agent_checkpoint import AgentManifest, emit_manifest, parse_manifest
6
+ from agent_checkpoint.manifest import ConfidenceRange
7
+
8
+
9
+ @pytest.fixture
10
+ def valid_manifest_dict():
11
+ return {
12
+ "ac_version": "0.1.0",
13
+ "agent_id": "reviewer-gpt-4",
14
+ "capabilities": ["code-review", "unit-testing"],
15
+ "confidence_range": {"min": 0.6, "max": 0.9},
16
+ "contact": "support@example.com",
17
+ }
18
+
19
+
20
+ def test_parse_manifest_valid_dict(valid_manifest_dict):
21
+ manifest = parse_manifest(valid_manifest_dict)
22
+ assert isinstance(manifest, AgentManifest)
23
+ assert manifest.agent_id == "reviewer-gpt-4"
24
+ assert manifest.capabilities == ["code-review", "unit-testing"]
25
+ assert manifest.confidence_range.min == 0.6
26
+
27
+
28
+ def test_parse_manifest_valid_json_string(valid_manifest_dict):
29
+ json_str = json.dumps(valid_manifest_dict)
30
+ manifest = parse_manifest(json_str)
31
+ assert isinstance(manifest, AgentManifest)
32
+ assert manifest.agent_id == "reviewer-gpt-4"
33
+
34
+
35
+ def test_parse_manifest_missing_agent_id(valid_manifest_dict):
36
+ data = valid_manifest_dict.copy()
37
+ del data["agent_id"]
38
+ # Should return None, never raise
39
+ manifest = parse_manifest(data)
40
+ assert manifest is None
41
+
42
+
43
+ def test_parse_manifest_invalid_json():
44
+ manifest = parse_manifest("{invalid json")
45
+ assert manifest is None
46
+
47
+
48
+ def test_emit_manifest_produces_valid_json(valid_manifest_dict):
49
+ manifest = parse_manifest(valid_manifest_dict)
50
+ json_str = emit_manifest(manifest)
51
+ data = json.loads(json_str)
52
+ assert data["agent_id"] == "reviewer-gpt-4"
53
+
54
+
55
+ def test_emit_manifest_omits_none_fields(valid_manifest_dict):
56
+ data = valid_manifest_dict.copy()
57
+ if "contact" in data:
58
+ del data["contact"]
59
+ manifest = parse_manifest(data)
60
+ json_str = emit_manifest(manifest)
61
+ assert "contact" not in json_str
62
+
63
+
64
+ def test_manifest_round_trip(valid_manifest_dict):
65
+ manifest = parse_manifest(valid_manifest_dict)
66
+ json_str = emit_manifest(manifest)
67
+ round_tripped = parse_manifest(json_str)
68
+ assert manifest == round_tripped
69
+
70
+
71
+ def test_confidence_range_validation():
72
+ # Valid
73
+ ConfidenceRange(min=0.1, max=0.5)
74
+ # Invalid: min > max
75
+ with pytest.raises(ValueError, match="cannot be greater than max"):
76
+ ConfidenceRange(min=0.8, max=0.2)
77
+ # Invalid: out of bounds
78
+ with pytest.raises(ValueError, match="between 0.0 and 1.0"):
79
+ ConfidenceRange(min=-0.1, max=0.5)
80
+
81
+
82
+ def test_agent_agnostic_usage():
83
+ """
84
+ Demonstrates that an agent doesn't need to know about manifests
85
+ to receive and use checkpoints.
86
+ """
87
+ # A checkpoint with a capability hint
88
+ checkpoint_data = {
89
+ "ac_version": "0.1.0",
90
+ "checkpoint_id": "a1b2c3d4-e5f6-4a5b-8c9d-0e1f2a3b4c5d",
91
+ "task_id": "b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e",
92
+ "agent_id": "source-agent",
93
+ "emitted_at": "2026-06-16T14:30:00Z",
94
+ "status": "partial",
95
+ "task_summary": "Review code",
96
+ "executor_hint": "capability:code-review",
97
+ }
98
+
99
+ # An agent receives this. It doesn't call parse_manifest.
100
+ # It just looks at the hint.
101
+ hint = checkpoint_data.get("executor_hint")
102
+ assert hint == "capability:code-review"
103
+
104
+ # It can decide to handle it based on its own internal logic
105
+ # without ever seeing an agent-caps.json manifest.
106
+ if hint.startswith("capability:"):
107
+ capability = hint.split(":")[1]
108
+ assert capability == "code-review"
109
+ # Proceed to handle...