helix-agent-sdk 0.1.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.
@@ -0,0 +1,95 @@
1
+ Metadata-Version: 2.4
2
+ Name: helix-agent-sdk
3
+ Version: 0.1.0
4
+ Summary: Python SDK for Helix — self-healing infrastructure for AI agent payments
5
+ Author: Helix Team
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/adrianhihi/helix
8
+ Project-URL: Repository, https://github.com/adrianhihi/helix
9
+ Keywords: helix,ai-agents,self-healing,payments,web3
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Topic :: Software Development :: Libraries
15
+ Requires-Python: >=3.9
16
+ Description-Content-Type: text/markdown
17
+ Requires-Dist: requests>=2.28.0
18
+ Provides-Extra: dev
19
+ Requires-Dist: pytest>=7.0; extra == "dev"
20
+ Requires-Dist: responses>=0.23; extra == "dev"
21
+
22
+ # helix-agent
23
+
24
+ Python SDK for [Helix](https://github.com/adrianhihi/helix) — self-healing infrastructure for AI agent payments.
25
+
26
+ ## Install
27
+
28
+ ```bash
29
+ pip install helix-agent
30
+ ```
31
+
32
+ Requires a running Helix server:
33
+
34
+ ```bash
35
+ npm install -g @helix-agent/core
36
+ npx helix serve --port 7842
37
+ ```
38
+
39
+ ## Quick Start
40
+
41
+ ### Method 1: Client
42
+
43
+ ```python
44
+ from helix_agent import HelixClient
45
+
46
+ client = HelixClient(platform="coinbase")
47
+ result = client.repair("AA25 invalid account nonce")
48
+ print(f"Strategy: {result.strategy}") # refresh_nonce
49
+ print(f"Immune: {result.immune}") # True on 2nd call
50
+ ```
51
+
52
+ ### Method 2: Decorator
53
+
54
+ ```python
55
+ from helix_agent import helix_wrap
56
+
57
+ @helix_wrap(platform="coinbase", max_retries=3)
58
+ def send_payment(to: str, amount: float):
59
+ return agent.transfer(to, amount)
60
+
61
+ result = send_payment("0x...", 1.5)
62
+ ```
63
+
64
+ ### Method 3: Context Manager
65
+
66
+ ```python
67
+ from helix_agent import helix_guard
68
+
69
+ with helix_guard("tempo") as guard:
70
+ try:
71
+ result = agent.transfer(to, amount)
72
+ except Exception as e:
73
+ repair = guard.repair(str(e))
74
+ if repair.immune:
75
+ result = agent.transfer(to, amount)
76
+ ```
77
+
78
+ ## Configuration
79
+
80
+ ```python
81
+ HELIX_URL=http://localhost:7842 # env var
82
+
83
+ client = HelixClient(
84
+ base_url="http://localhost:7842",
85
+ platform="coinbase",
86
+ agent_id="my-agent-1",
87
+ )
88
+ ```
89
+
90
+ ## Supported Platforms
91
+
92
+ - `tempo` — Tempo L1 blockchain
93
+ - `privy` — Privy embedded wallets
94
+ - `coinbase` — Coinbase CDP + AgentKit
95
+ - `generic` — Any HTTP/RPC service
@@ -0,0 +1,74 @@
1
+ # helix-agent
2
+
3
+ Python SDK for [Helix](https://github.com/adrianhihi/helix) — self-healing infrastructure for AI agent payments.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ pip install helix-agent
9
+ ```
10
+
11
+ Requires a running Helix server:
12
+
13
+ ```bash
14
+ npm install -g @helix-agent/core
15
+ npx helix serve --port 7842
16
+ ```
17
+
18
+ ## Quick Start
19
+
20
+ ### Method 1: Client
21
+
22
+ ```python
23
+ from helix_agent import HelixClient
24
+
25
+ client = HelixClient(platform="coinbase")
26
+ result = client.repair("AA25 invalid account nonce")
27
+ print(f"Strategy: {result.strategy}") # refresh_nonce
28
+ print(f"Immune: {result.immune}") # True on 2nd call
29
+ ```
30
+
31
+ ### Method 2: Decorator
32
+
33
+ ```python
34
+ from helix_agent import helix_wrap
35
+
36
+ @helix_wrap(platform="coinbase", max_retries=3)
37
+ def send_payment(to: str, amount: float):
38
+ return agent.transfer(to, amount)
39
+
40
+ result = send_payment("0x...", 1.5)
41
+ ```
42
+
43
+ ### Method 3: Context Manager
44
+
45
+ ```python
46
+ from helix_agent import helix_guard
47
+
48
+ with helix_guard("tempo") as guard:
49
+ try:
50
+ result = agent.transfer(to, amount)
51
+ except Exception as e:
52
+ repair = guard.repair(str(e))
53
+ if repair.immune:
54
+ result = agent.transfer(to, amount)
55
+ ```
56
+
57
+ ## Configuration
58
+
59
+ ```python
60
+ HELIX_URL=http://localhost:7842 # env var
61
+
62
+ client = HelixClient(
63
+ base_url="http://localhost:7842",
64
+ platform="coinbase",
65
+ agent_id="my-agent-1",
66
+ )
67
+ ```
68
+
69
+ ## Supported Platforms
70
+
71
+ - `tempo` — Tempo L1 blockchain
72
+ - `privy` — Privy embedded wallets
73
+ - `coinbase` — Coinbase CDP + AgentKit
74
+ - `generic` — Any HTTP/RPC service
@@ -0,0 +1,13 @@
1
+ """
2
+ Helix Agent — Python SDK for self-healing AI agent payments.
3
+
4
+ Usage:
5
+ from helix_agent import HelixClient, helix_wrap, helix_guard
6
+ """
7
+
8
+ from .client import HelixClient
9
+ from .decorator import helix_wrap
10
+ from .guard import helix_guard
11
+
12
+ __version__ = "0.1.0"
13
+ __all__ = ["HelixClient", "helix_wrap", "helix_guard"]
@@ -0,0 +1,146 @@
1
+ """
2
+ HelixClient — Core client for Helix REST API.
3
+ """
4
+
5
+ import os
6
+ import time
7
+ import logging
8
+ from typing import Optional
9
+ from dataclasses import dataclass, field
10
+
11
+ import requests
12
+
13
+ logger = logging.getLogger("helix")
14
+
15
+
16
+ @dataclass
17
+ class RepairResult:
18
+ """Result of a repair attempt."""
19
+ success: bool
20
+ immune: bool = False
21
+ strategy: Optional[str] = None
22
+ strategy_params: dict = field(default_factory=dict)
23
+ confidence: float = 0.0
24
+ q_value: float = 0.0
25
+ llm_used: bool = False
26
+ repair_time_ms: float = 0.0
27
+ error: Optional[str] = None
28
+ raw: dict = field(default_factory=dict)
29
+
30
+ @classmethod
31
+ def from_api(cls, data: dict) -> "RepairResult":
32
+ strategy = data.get("strategy") or {}
33
+ failure = data.get("failure") or {}
34
+ return cls(
35
+ success=True,
36
+ immune=data.get("immune", False),
37
+ strategy=strategy.get("name") if strategy else None,
38
+ strategy_params=strategy.get("params", {}) if strategy else {},
39
+ confidence=strategy.get("confidence", 0) if strategy else 0,
40
+ llm_used=failure.get("llmClassified", False),
41
+ repair_time_ms=data.get("repairMs", 0),
42
+ raw=data,
43
+ )
44
+
45
+ @classmethod
46
+ def failed(cls, error: str) -> "RepairResult":
47
+ return cls(success=False, error=error)
48
+
49
+
50
+ class HelixClient:
51
+ """
52
+ Client for Helix self-healing server.
53
+
54
+ Args:
55
+ base_url: Helix server URL. Default: http://localhost:7842
56
+ timeout: Request timeout in seconds. Default: 10
57
+ agent_id: Identifier for this agent instance.
58
+ platform: Default platform (tempo/privy/coinbase/generic).
59
+ """
60
+
61
+ def __init__(
62
+ self,
63
+ base_url: Optional[str] = None,
64
+ timeout: int = 10,
65
+ agent_id: Optional[str] = None,
66
+ platform: str = "generic",
67
+ ):
68
+ self.base_url = (
69
+ base_url or os.environ.get("HELIX_URL") or "http://localhost:7842"
70
+ )
71
+ self.timeout = timeout
72
+ self.agent_id = agent_id or f"py-{os.getpid()}"
73
+ self.platform = platform
74
+ self._session = requests.Session()
75
+ self._session.headers.update({"Content-Type": "application/json"})
76
+
77
+ def repair(
78
+ self,
79
+ error: str,
80
+ *,
81
+ platform: Optional[str] = None,
82
+ agent_id: Optional[str] = None,
83
+ context: Optional[dict] = None,
84
+ ) -> RepairResult:
85
+ """Send an error to Helix for diagnosis and repair strategy."""
86
+ start = time.monotonic()
87
+ try:
88
+ payload = {
89
+ "error": error,
90
+ "platform": platform or self.platform,
91
+ "agentId": agent_id or self.agent_id,
92
+ }
93
+ if context:
94
+ payload["context"] = context
95
+
96
+ resp = self._session.post(
97
+ f"{self.base_url}/repair", json=payload, timeout=self.timeout
98
+ )
99
+ resp.raise_for_status()
100
+ result = RepairResult.from_api(resp.json())
101
+ result.repair_time_ms = (time.monotonic() - start) * 1000
102
+
103
+ status = "IMMUNE" if result.immune else f"REPAIR -> {result.strategy}"
104
+ logger.info(f"[helix] {status} ({result.repair_time_ms:.0f}ms)")
105
+ return result
106
+
107
+ except requests.RequestException as e:
108
+ logger.warning(f"[helix] Server unreachable: {e}")
109
+ return RepairResult.failed(f"Server unreachable: {e}")
110
+ except Exception as e:
111
+ logger.warning(f"[helix] Repair failed: {e}")
112
+ return RepairResult.failed(str(e))
113
+
114
+ def health(self) -> dict:
115
+ """Check Helix server health."""
116
+ try:
117
+ return self._session.get(f"{self.base_url}/health", timeout=self.timeout).json()
118
+ except Exception as e:
119
+ return {"status": "unreachable", "error": str(e)}
120
+
121
+ def genes(self) -> dict:
122
+ """List all genes in the Gene Map."""
123
+ try:
124
+ return self._session.get(f"{self.base_url}/genes", timeout=self.timeout).json()
125
+ except Exception as e:
126
+ return {"error": str(e)}
127
+
128
+ def status(self) -> dict:
129
+ """Get full server status."""
130
+ try:
131
+ return self._session.get(f"{self.base_url}/status", timeout=self.timeout).json()
132
+ except Exception as e:
133
+ return {"error": str(e)}
134
+
135
+ def is_healthy(self) -> bool:
136
+ """Quick check if server is reachable."""
137
+ return self.health().get("status") in ("ok", "running")
138
+
139
+ def close(self):
140
+ self._session.close()
141
+
142
+ def __enter__(self):
143
+ return self
144
+
145
+ def __exit__(self, *args):
146
+ self.close()
@@ -0,0 +1,75 @@
1
+ """
2
+ @helix_wrap — Decorator for automatic error repair.
3
+
4
+ Catches exceptions, sends to Helix, applies repair strategy, retries.
5
+ """
6
+
7
+ import functools
8
+ import logging
9
+ import time
10
+ from typing import Optional, Callable, Any
11
+
12
+ from .client import HelixClient, RepairResult
13
+
14
+ logger = logging.getLogger("helix")
15
+
16
+
17
+ def helix_wrap(
18
+ platform: str = "generic",
19
+ *,
20
+ base_url: Optional[str] = None,
21
+ max_retries: int = 3,
22
+ retry_delay: float = 1.0,
23
+ on_repair: Optional[Callable[[RepairResult], None]] = None,
24
+ on_failure: Optional[Callable[[Exception, RepairResult], Any]] = None,
25
+ ):
26
+ """
27
+ Decorator that adds self-healing to any function.
28
+
29
+ Example:
30
+ @helix_wrap(platform="coinbase", max_retries=3)
31
+ def send_payment(to, amount):
32
+ return agent.transfer(to, amount)
33
+ """
34
+ client = HelixClient(base_url=base_url, platform=platform)
35
+
36
+ def decorator(func):
37
+ @functools.wraps(func)
38
+ def wrapper(*args, **kwargs):
39
+ last_error = None
40
+ repair = RepairResult.failed("no repair attempted")
41
+ for attempt in range(1, max_retries + 1):
42
+ try:
43
+ return func(*args, **kwargs)
44
+ except Exception as e:
45
+ last_error = e
46
+ logger.info(f"[helix] Attempt {attempt}/{max_retries} failed: {str(e)[:100]}")
47
+
48
+ repair = client.repair(str(e))
49
+
50
+ if not repair.success:
51
+ time.sleep(retry_delay * attempt)
52
+ continue
53
+
54
+ if on_repair:
55
+ on_repair(repair)
56
+
57
+ if repair.strategy == "backoff_retry":
58
+ delay = retry_delay * (2 ** attempt)
59
+ elif repair.immune:
60
+ delay = 0.1
61
+ else:
62
+ delay = retry_delay * attempt
63
+
64
+ time.sleep(delay)
65
+
66
+ if on_failure:
67
+ result = on_failure(last_error, repair)
68
+ if result is not None:
69
+ return result
70
+ raise last_error
71
+
72
+ wrapper.helix_client = client
73
+ return wrapper
74
+
75
+ return decorator
@@ -0,0 +1,60 @@
1
+ """
2
+ helix_guard — Context manager for block-level protection.
3
+ """
4
+
5
+ import logging
6
+ from typing import Optional, List
7
+
8
+ from .client import HelixClient, RepairResult
9
+
10
+ logger = logging.getLogger("helix")
11
+
12
+
13
+ class helix_guard:
14
+ """
15
+ Context manager for Helix self-healing.
16
+
17
+ Example:
18
+ with helix_guard("coinbase") as guard:
19
+ try:
20
+ result = agent.transfer(to, amount)
21
+ except Exception as e:
22
+ repair = guard.repair(str(e))
23
+ if repair.immune:
24
+ result = agent.transfer(to, amount)
25
+ """
26
+
27
+ def __init__(
28
+ self,
29
+ platform: str = "generic",
30
+ *,
31
+ base_url: Optional[str] = None,
32
+ agent_id: Optional[str] = None,
33
+ ):
34
+ self._client = HelixClient(
35
+ base_url=base_url, platform=platform, agent_id=agent_id
36
+ )
37
+ self.repairs: List[RepairResult] = []
38
+
39
+ def repair(self, error: str, **kwargs) -> RepairResult:
40
+ result = self._client.repair(error, **kwargs)
41
+ self.repairs.append(result)
42
+ return result
43
+
44
+ def health(self) -> dict:
45
+ return self._client.health()
46
+
47
+ @property
48
+ def total_repairs(self) -> int:
49
+ return len(self.repairs)
50
+
51
+ @property
52
+ def immune_count(self) -> int:
53
+ return sum(1 for r in self.repairs if r.immune)
54
+
55
+ def __enter__(self):
56
+ return self
57
+
58
+ def __exit__(self, exc_type, exc_val, exc_tb):
59
+ self._client.close()
60
+ return False
File without changes
@@ -0,0 +1,95 @@
1
+ Metadata-Version: 2.4
2
+ Name: helix-agent-sdk
3
+ Version: 0.1.0
4
+ Summary: Python SDK for Helix — self-healing infrastructure for AI agent payments
5
+ Author: Helix Team
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/adrianhihi/helix
8
+ Project-URL: Repository, https://github.com/adrianhihi/helix
9
+ Keywords: helix,ai-agents,self-healing,payments,web3
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Topic :: Software Development :: Libraries
15
+ Requires-Python: >=3.9
16
+ Description-Content-Type: text/markdown
17
+ Requires-Dist: requests>=2.28.0
18
+ Provides-Extra: dev
19
+ Requires-Dist: pytest>=7.0; extra == "dev"
20
+ Requires-Dist: responses>=0.23; extra == "dev"
21
+
22
+ # helix-agent
23
+
24
+ Python SDK for [Helix](https://github.com/adrianhihi/helix) — self-healing infrastructure for AI agent payments.
25
+
26
+ ## Install
27
+
28
+ ```bash
29
+ pip install helix-agent
30
+ ```
31
+
32
+ Requires a running Helix server:
33
+
34
+ ```bash
35
+ npm install -g @helix-agent/core
36
+ npx helix serve --port 7842
37
+ ```
38
+
39
+ ## Quick Start
40
+
41
+ ### Method 1: Client
42
+
43
+ ```python
44
+ from helix_agent import HelixClient
45
+
46
+ client = HelixClient(platform="coinbase")
47
+ result = client.repair("AA25 invalid account nonce")
48
+ print(f"Strategy: {result.strategy}") # refresh_nonce
49
+ print(f"Immune: {result.immune}") # True on 2nd call
50
+ ```
51
+
52
+ ### Method 2: Decorator
53
+
54
+ ```python
55
+ from helix_agent import helix_wrap
56
+
57
+ @helix_wrap(platform="coinbase", max_retries=3)
58
+ def send_payment(to: str, amount: float):
59
+ return agent.transfer(to, amount)
60
+
61
+ result = send_payment("0x...", 1.5)
62
+ ```
63
+
64
+ ### Method 3: Context Manager
65
+
66
+ ```python
67
+ from helix_agent import helix_guard
68
+
69
+ with helix_guard("tempo") as guard:
70
+ try:
71
+ result = agent.transfer(to, amount)
72
+ except Exception as e:
73
+ repair = guard.repair(str(e))
74
+ if repair.immune:
75
+ result = agent.transfer(to, amount)
76
+ ```
77
+
78
+ ## Configuration
79
+
80
+ ```python
81
+ HELIX_URL=http://localhost:7842 # env var
82
+
83
+ client = HelixClient(
84
+ base_url="http://localhost:7842",
85
+ platform="coinbase",
86
+ agent_id="my-agent-1",
87
+ )
88
+ ```
89
+
90
+ ## Supported Platforms
91
+
92
+ - `tempo` — Tempo L1 blockchain
93
+ - `privy` — Privy embedded wallets
94
+ - `coinbase` — Coinbase CDP + AgentKit
95
+ - `generic` — Any HTTP/RPC service
@@ -0,0 +1,15 @@
1
+ README.md
2
+ pyproject.toml
3
+ helix_agent/__init__.py
4
+ helix_agent/client.py
5
+ helix_agent/decorator.py
6
+ helix_agent/guard.py
7
+ helix_agent/py.typed
8
+ helix_agent_sdk.egg-info/PKG-INFO
9
+ helix_agent_sdk.egg-info/SOURCES.txt
10
+ helix_agent_sdk.egg-info/dependency_links.txt
11
+ helix_agent_sdk.egg-info/requires.txt
12
+ helix_agent_sdk.egg-info/top_level.txt
13
+ tests/test_client.py
14
+ tests/test_decorator.py
15
+ tests/test_guard.py
@@ -0,0 +1,5 @@
1
+ requests>=2.28.0
2
+
3
+ [dev]
4
+ pytest>=7.0
5
+ responses>=0.23
@@ -0,0 +1 @@
1
+ helix_agent
@@ -0,0 +1,36 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "helix-agent-sdk"
7
+ version = "0.1.0"
8
+ description = "Python SDK for Helix — self-healing infrastructure for AI agent payments"
9
+ readme = "README.md"
10
+ license = {text = "MIT"}
11
+ requires-python = ">=3.9"
12
+ authors = [
13
+ {name = "Helix Team"}
14
+ ]
15
+ keywords = ["helix", "ai-agents", "self-healing", "payments", "web3"]
16
+ classifiers = [
17
+ "Development Status :: 4 - Beta",
18
+ "Intended Audience :: Developers",
19
+ "License :: OSI Approved :: MIT License",
20
+ "Programming Language :: Python :: 3",
21
+ "Topic :: Software Development :: Libraries",
22
+ ]
23
+ dependencies = [
24
+ "requests>=2.28.0",
25
+ ]
26
+
27
+ [project.optional-dependencies]
28
+ dev = ["pytest>=7.0", "responses>=0.23"]
29
+
30
+ [project.urls]
31
+ Homepage = "https://github.com/adrianhihi/helix"
32
+ Repository = "https://github.com/adrianhihi/helix"
33
+
34
+ [tool.setuptools.packages.find]
35
+ where = ["."]
36
+ include = ["helix_agent*"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,46 @@
1
+ """Tests for HelixClient — uses a real running server."""
2
+ import pytest
3
+ from helix_agent import HelixClient
4
+
5
+ SERVER_PORT = 17842
6
+
7
+
8
+ @pytest.fixture
9
+ def client():
10
+ return HelixClient(base_url=f"http://localhost:{SERVER_PORT}", platform="tempo")
11
+
12
+
13
+ class TestHelixClient:
14
+ def test_health(self, client):
15
+ h = client.health()
16
+ assert h.get("status") in ("ok", "running")
17
+
18
+ def test_repair_known_error(self, client):
19
+ result = client.repair("nonce mismatch: expected 5, got 3")
20
+ assert result.success
21
+ assert result.strategy is not None
22
+
23
+ def test_immune_on_repeat(self, client):
24
+ r1 = client.repair("session expired, please re-authenticate")
25
+ assert r1.success
26
+ r2 = client.repair("session expired, please re-authenticate")
27
+ assert r2.success
28
+ assert r2.immune
29
+
30
+ def test_genes_list(self, client):
31
+ genes = client.genes()
32
+ assert "genes" in genes or "total" in genes
33
+
34
+ def test_platform_override(self, client):
35
+ result = client.repair("AA25 invalid account nonce", platform="coinbase")
36
+ assert result.success
37
+
38
+ def test_unreachable_server(self):
39
+ bad_client = HelixClient(base_url="http://localhost:19999", timeout=2)
40
+ result = bad_client.repair("test error")
41
+ assert not result.success
42
+ assert "unreachable" in result.error.lower()
43
+
44
+ def test_context_manager(self):
45
+ with HelixClient(base_url=f"http://localhost:{SERVER_PORT}") as c:
46
+ assert c.is_healthy()
@@ -0,0 +1,58 @@
1
+ """Tests for @helix_wrap decorator."""
2
+ import pytest
3
+ from helix_agent import helix_wrap
4
+
5
+ SERVER_PORT = 17842
6
+
7
+
8
+ class TestHelixWrap:
9
+ def test_decorator_retries_on_error(self):
10
+ call_count = 0
11
+
12
+ @helix_wrap(platform="tempo", base_url=f"http://localhost:{SERVER_PORT}", max_retries=3, retry_delay=0.1)
13
+ def flaky_function():
14
+ nonlocal call_count
15
+ call_count += 1
16
+ if call_count < 3:
17
+ raise Exception("nonce mismatch: expected 5, got 3")
18
+ return "success"
19
+
20
+ result = flaky_function()
21
+ assert result == "success"
22
+ assert call_count == 3
23
+
24
+ def test_decorator_exposes_client(self):
25
+ @helix_wrap(platform="tempo", base_url=f"http://localhost:{SERVER_PORT}")
26
+ def my_func():
27
+ pass
28
+
29
+ assert hasattr(my_func, "helix_client")
30
+ assert my_func.helix_client.is_healthy()
31
+
32
+ def test_decorator_raises_after_max_retries(self):
33
+ @helix_wrap(platform="tempo", base_url=f"http://localhost:{SERVER_PORT}", max_retries=2, retry_delay=0.1)
34
+ def always_fails():
35
+ raise Exception("permanent error that never resolves")
36
+
37
+ with pytest.raises(Exception, match="permanent error"):
38
+ always_fails()
39
+
40
+ def test_on_repair_callback(self):
41
+ repairs = []
42
+
43
+ @helix_wrap(
44
+ platform="tempo",
45
+ base_url=f"http://localhost:{SERVER_PORT}",
46
+ max_retries=2,
47
+ retry_delay=0.1,
48
+ on_repair=lambda r: repairs.append(r),
49
+ )
50
+ def fails_once():
51
+ if len(repairs) == 0:
52
+ raise Exception("nonce mismatch: expected 1, got 0")
53
+ return "ok"
54
+
55
+ result = fails_once()
56
+ assert result == "ok"
57
+ assert len(repairs) >= 1
58
+ assert repairs[0].success
@@ -0,0 +1,24 @@
1
+ """Tests for helix_guard context manager."""
2
+ from helix_agent import helix_guard
3
+
4
+ SERVER_PORT = 17842
5
+
6
+
7
+ class TestHelixGuard:
8
+ def test_basic_guard(self):
9
+ with helix_guard("tempo", base_url=f"http://localhost:{SERVER_PORT}") as guard:
10
+ result = guard.repair("nonce mismatch: expected 5, got 3")
11
+ assert result.success
12
+ assert result.strategy is not None
13
+
14
+ def test_repair_count(self):
15
+ with helix_guard("tempo", base_url=f"http://localhost:{SERVER_PORT}") as guard:
16
+ guard.repair("error 1")
17
+ guard.repair("error 2")
18
+ assert guard.total_repairs == 2
19
+
20
+ def test_immune_tracking(self):
21
+ with helix_guard("tempo", base_url=f"http://localhost:{SERVER_PORT}") as guard:
22
+ guard.repair("session expired, please re-authenticate")
23
+ guard.repair("session expired, please re-authenticate")
24
+ assert guard.immune_count >= 1