snapback-selfheal 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,87 @@
1
+ Metadata-Version: 2.4
2
+ Name: snapback-selfheal
3
+ Version: 0.1.0
4
+ Summary: Make your AI agent self-heal — auto-diagnose every error and apply safe fixes, gated. Snapback interceptor for OpenClaw and any Python agent.
5
+ Author: Snapback / RA1 Labs
6
+ License: MIT
7
+ Project-URL: Homepage, https://snapback.sh
8
+ Project-URL: For agents, https://snapback.sh/for-agents
9
+ Project-URL: Docs, https://snapback.sh/docs
10
+ Keywords: ai-agent,self-healing,agentops,openclaw,error-recovery,mcp,snapback
11
+ Requires-Python: >=3.9
12
+ Description-Content-Type: text/markdown
13
+
14
+ # Snapback self-heal interceptor for OpenClaw
15
+
16
+ **Make Snapback automatic.** Instead of remembering to call the diagnosis tool, wrap your agent's tool calls
17
+ once — and every error is auto-diagnosed, and gated-safe fixes are applied and retried *without a human*.
18
+
19
+ > "Power tools are optional. Infrastructure is not." This turns Snapback from a tool you reach for into
20
+ > infrastructure that runs on every failure.
21
+
22
+ ## What it does
23
+ On **any** tool-call error, the interceptor:
24
+ 1. Calls `diagnose_infra_error` (free, no token, no LLM, <150ms).
25
+ 2. Applies the **three-factor gate** — auto-apply a fix only when **all** are true:
26
+ - `confidence >= 0.85`
27
+ - `source == "library"` (a curated verified fix, not an LLM guess)
28
+ - `auto_safe == true` (the fix is `retry` / `refetch` / `config` — reversible, side-effect-free)
29
+ 3. If the gate passes → applies the fix and **retries once**. If it fails → **escalates to a human** (logs the
30
+ verdict). It **never** auto-applies a `mutate` or `destructive` fix (create/change state, money, auth grants).
31
+
32
+ It fails **closed**: when unsure, it escalates rather than acting.
33
+
34
+ ## Install
35
+ ```bash
36
+ # no install needed beyond the stdlib; drop the file in, or:
37
+ cp snapback_interceptor.py your_agent/
38
+ export SNAPBACK_MCP=https://api.snapback.sh/mcp # default; the free tools need no token
39
+ ```
40
+
41
+ ## Use
42
+ ```python
43
+ from snapback_interceptor import SnapbackInterceptor
44
+
45
+ heal = SnapbackInterceptor(
46
+ auto_apply=True,
47
+ on_escalate=lambda v: my_ops_inbox.log(v), # human sees non-auto-safe verdicts
48
+ on_heal=lambda v: metrics.count("snapback.autoheal", family=v["family"]),
49
+ )
50
+
51
+ # wrap any call that might raise:
52
+ result = heal.run(call_solana_rpc, wallet, amount)
53
+
54
+ # or as a decorator:
55
+ @heal.guard
56
+ def call_solana_rpc(wallet, amount): ...
57
+
58
+ # or manually on a caught error:
59
+ verdict = heal.diagnose("BlockhashNotFound")
60
+ if verdict["gate"]["auto_apply_ok"]:
61
+ ...apply verdict["fix"] and retry...
62
+ ```
63
+
64
+ ## The gate contract (returned by Snapback)
65
+ `diagnose_infra_error` returns `{matched, family, fix, confidence, source, action_class, auto_safe, gate}`.
66
+ `action_class` is one of:
67
+
68
+ | class | meaning | auto-safe? |
69
+ |---|---|---|
70
+ | `retry` | re-run with backoff (idempotent) | ✅ |
71
+ | `refetch` | re-fetch/re-price/re-sync then re-apply | ✅ |
72
+ | `config` | client-side param change (raise max_tokens, serve intermediate cert) | ✅ (to suggest) |
73
+ | `mutate` | creates/changes external state, money, auth | ❌ escalate |
74
+ | `destructive` | deletes/reverts/irreversible | ❌ never |
75
+
76
+ `gate.auto_apply_ok` is the ready-made verdict; the interceptor also re-derives it locally so a stale client
77
+ can't over-trust.
78
+
79
+ ## Privacy & safety
80
+ - 100% client-side. Talks only to the free public `diagnose_infra_error` endpoint.
81
+ - Never sends your Snapback token for the diagnosis (diagnosis is free).
82
+ - Snapback scrubs trace content server-side; this sends only the error string you pass.
83
+ - Composes with `bridge.py` (post-mortem forwarding) — this is the live auto-heal layer.
84
+
85
+ ## Roadmap
86
+ - `submit_feedback` on the retry outcome (did the fix work?) → feeds the shared library (the network effect).
87
+ - Hermes + LangChain adapters (same gate, different framework hook).
@@ -0,0 +1,74 @@
1
+ # Snapback self-heal interceptor for OpenClaw
2
+
3
+ **Make Snapback automatic.** Instead of remembering to call the diagnosis tool, wrap your agent's tool calls
4
+ once — and every error is auto-diagnosed, and gated-safe fixes are applied and retried *without a human*.
5
+
6
+ > "Power tools are optional. Infrastructure is not." This turns Snapback from a tool you reach for into
7
+ > infrastructure that runs on every failure.
8
+
9
+ ## What it does
10
+ On **any** tool-call error, the interceptor:
11
+ 1. Calls `diagnose_infra_error` (free, no token, no LLM, <150ms).
12
+ 2. Applies the **three-factor gate** — auto-apply a fix only when **all** are true:
13
+ - `confidence >= 0.85`
14
+ - `source == "library"` (a curated verified fix, not an LLM guess)
15
+ - `auto_safe == true` (the fix is `retry` / `refetch` / `config` — reversible, side-effect-free)
16
+ 3. If the gate passes → applies the fix and **retries once**. If it fails → **escalates to a human** (logs the
17
+ verdict). It **never** auto-applies a `mutate` or `destructive` fix (create/change state, money, auth grants).
18
+
19
+ It fails **closed**: when unsure, it escalates rather than acting.
20
+
21
+ ## Install
22
+ ```bash
23
+ # no install needed beyond the stdlib; drop the file in, or:
24
+ cp snapback_interceptor.py your_agent/
25
+ export SNAPBACK_MCP=https://api.snapback.sh/mcp # default; the free tools need no token
26
+ ```
27
+
28
+ ## Use
29
+ ```python
30
+ from snapback_interceptor import SnapbackInterceptor
31
+
32
+ heal = SnapbackInterceptor(
33
+ auto_apply=True,
34
+ on_escalate=lambda v: my_ops_inbox.log(v), # human sees non-auto-safe verdicts
35
+ on_heal=lambda v: metrics.count("snapback.autoheal", family=v["family"]),
36
+ )
37
+
38
+ # wrap any call that might raise:
39
+ result = heal.run(call_solana_rpc, wallet, amount)
40
+
41
+ # or as a decorator:
42
+ @heal.guard
43
+ def call_solana_rpc(wallet, amount): ...
44
+
45
+ # or manually on a caught error:
46
+ verdict = heal.diagnose("BlockhashNotFound")
47
+ if verdict["gate"]["auto_apply_ok"]:
48
+ ...apply verdict["fix"] and retry...
49
+ ```
50
+
51
+ ## The gate contract (returned by Snapback)
52
+ `diagnose_infra_error` returns `{matched, family, fix, confidence, source, action_class, auto_safe, gate}`.
53
+ `action_class` is one of:
54
+
55
+ | class | meaning | auto-safe? |
56
+ |---|---|---|
57
+ | `retry` | re-run with backoff (idempotent) | ✅ |
58
+ | `refetch` | re-fetch/re-price/re-sync then re-apply | ✅ |
59
+ | `config` | client-side param change (raise max_tokens, serve intermediate cert) | ✅ (to suggest) |
60
+ | `mutate` | creates/changes external state, money, auth | ❌ escalate |
61
+ | `destructive` | deletes/reverts/irreversible | ❌ never |
62
+
63
+ `gate.auto_apply_ok` is the ready-made verdict; the interceptor also re-derives it locally so a stale client
64
+ can't over-trust.
65
+
66
+ ## Privacy & safety
67
+ - 100% client-side. Talks only to the free public `diagnose_infra_error` endpoint.
68
+ - Never sends your Snapback token for the diagnosis (diagnosis is free).
69
+ - Snapback scrubs trace content server-side; this sends only the error string you pass.
70
+ - Composes with `bridge.py` (post-mortem forwarding) — this is the live auto-heal layer.
71
+
72
+ ## Roadmap
73
+ - `submit_feedback` on the retry outcome (did the fix work?) → feeds the shared library (the network effect).
74
+ - Hermes + LangChain adapters (same gate, different framework hook).
@@ -0,0 +1,25 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "snapback-selfheal"
7
+ version = "0.1.0"
8
+ description = "Make your AI agent self-heal — auto-diagnose every error and apply safe fixes, gated. Snapback interceptor for OpenClaw and any Python agent."
9
+ readme = "README.md"
10
+ requires-python = ">=3.9"
11
+ license = { text = "MIT" }
12
+ authors = [{ name = "Snapback / RA1 Labs" }]
13
+ keywords = ["ai-agent", "self-healing", "agentops", "openclaw", "error-recovery", "mcp", "snapback"]
14
+ dependencies = [] # stdlib only — no supply-chain surface
15
+
16
+ [project.urls]
17
+ Homepage = "https://snapback.sh"
18
+ "For agents" = "https://snapback.sh/for-agents"
19
+ Docs = "https://snapback.sh/docs"
20
+
21
+ [project.optional-dependencies]
22
+ # none required; the interceptor uses only urllib from the stdlib.
23
+
24
+ [tool.setuptools]
25
+ py-modules = ["snapback_interceptor"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,194 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Snapback self-heal interceptor for OpenClaw (Sprint M / P0 — the flagship).
4
+
5
+ Turns Snapback from a tool an agent must REMEMBER to call into infrastructure that runs on EVERY error
6
+ automatically. Wraps your agent's tool calls; on ANY error it auto-calls diagnose_infra_error (free, <150ms,
7
+ no token, no LLM), applies the THREE-FACTOR GATE, and either self-heals (retry once with the fix) or escalates.
8
+
9
+ "Power tools are optional. Infrastructure is not. Make it impossible to NOT use." — validated agent feedback.
10
+
11
+ THE THREE-FACTOR GATE (auto-apply a fix without a human ONLY when ALL are true):
12
+ 1. confidence >= 0.85 (not a low-confidence guess)
13
+ 2. source == "library" (a curated verified fix, not an LLM inference)
14
+ 3. auto_safe == true (action_class is retry|refetch|config — reversible / side-effect-free)
15
+ Snapback returns all three in the diagnose_infra_error response (+ a ready-made gate.auto_apply_ok), so this
16
+ interceptor just reads them. It NEVER auto-applies a 'mutate' or 'destructive' fix (create/change state, money,
17
+ auth grants) — those are logged + escalated to a human. Fails CLOSED: if unsure, escalate.
18
+
19
+ This is 100% CLIENT-SIDE. It talks only to the free, public diagnose_infra_error endpoint. It never touches
20
+ Snapback internals, never needs a paid token for the diagnosis, and never sends your secrets (Snapback also
21
+ scrubs server-side). Composes with bridge.py (bridge = post-mortem forwarding; this = live auto-heal).
22
+
23
+ USAGE (wrap any callable that might raise):
24
+ from snapback_interceptor import SnapbackInterceptor
25
+ heal = SnapbackInterceptor(auto_apply=True) # or auto_apply=False to only suggest/log
26
+
27
+ result = heal.run(my_tool_call, *args, **kwargs) # auto-diagnoses + retries once on a gated-safe fix
28
+
29
+ Or as a decorator:
30
+ @heal.guard
31
+ def call_solana_rpc(...): ...
32
+
33
+ Or manually on a caught error:
34
+ verdict = heal.diagnose("BlockhashNotFound") # {matched, family, fix, confidence, auto_safe, gate, ...}
35
+ if verdict["gate"]["auto_apply_ok"]:
36
+ ...apply verdict["fix"] and retry...
37
+
38
+ ENV:
39
+ SNAPBACK_MCP default https://api.snapback.sh/mcp (the free tools work with no token)
40
+ SNAPBACK_TOKEN optional; only needed if you later wire submit_feedback on outcomes
41
+ SNAPBACK_CONF_MIN default 0.85 (the gate's confidence floor)
42
+ """
43
+ from __future__ import annotations
44
+ import json, os, time, urllib.request, urllib.error
45
+ from typing import Any, Callable, Optional
46
+
47
+ _MCP = os.environ.get("SNAPBACK_MCP", "https://api.snapback.sh/mcp")
48
+ _TOKEN = os.environ.get("SNAPBACK_TOKEN") # optional
49
+ _CONF_MIN = float(os.environ.get("SNAPBACK_CONF_MIN", "0.85"))
50
+ _AUTO_SAFE_CLASSES = {"retry", "refetch", "config"}
51
+
52
+
53
+ def _mcp_call(tool: str, arguments: dict, token: Optional[str] = None, timeout: float = 8.0) -> dict:
54
+ """Call a Snapback MCP tool via tools/call. diagnose_infra_error is free + needs no token."""
55
+ body = json.dumps({
56
+ "jsonrpc": "2.0", "id": 1, "method": "tools/call",
57
+ "params": {"name": tool, "arguments": arguments},
58
+ }).encode()
59
+ headers = {"Content-Type": "application/json"}
60
+ if token:
61
+ headers["Authorization"] = f"Bearer {token}"
62
+ req = urllib.request.Request(_MCP, data=body, headers=headers, method="POST")
63
+ try:
64
+ with urllib.request.urlopen(req, timeout=timeout) as r:
65
+ payload = json.loads(r.read().decode())
66
+ except (urllib.error.URLError, TimeoutError, json.JSONDecodeError) as e:
67
+ return {"matched": False, "error": f"snapback unreachable: {type(e).__name__}"}
68
+ # unwrap the JSON-RPC + MCP content envelope -> the tool's own JSON
69
+ try:
70
+ text = payload["result"]["content"][0]["text"]
71
+ return json.loads(text)
72
+ except (KeyError, IndexError, TypeError, json.JSONDecodeError):
73
+ return payload.get("result", payload)
74
+
75
+
76
+ class SnapbackInterceptor:
77
+ """Wrap tool calls; auto-diagnose + gate-safe self-heal on error."""
78
+
79
+ def __init__(self, auto_apply: bool = True, conf_min: float = _CONF_MIN,
80
+ token: Optional[str] = _TOKEN, on_escalate: Optional[Callable[[dict], None]] = None,
81
+ on_heal: Optional[Callable[[dict], None]] = None):
82
+ """
83
+ auto_apply — if True, retry once with the fix when the gate passes; if False, only diagnose + log.
84
+ conf_min — the gate's confidence floor (default 0.85).
85
+ on_escalate — callback(verdict) when a fix is NOT auto-safe (log to your ops inbox / alert a human).
86
+ on_heal — callback(verdict) when a fix WAS auto-applied (for telemetry).
87
+ """
88
+ self.auto_apply = auto_apply
89
+ self.conf_min = conf_min
90
+ self.token = token
91
+ self.on_escalate = on_escalate or (lambda v: None)
92
+ self.on_heal = on_heal or (lambda v: None)
93
+
94
+ # ---- the core: diagnose one error string ----
95
+ def diagnose(self, error_text: str, context: str = "", action: str = "") -> dict:
96
+ """Free, no-token, <150ms library match. Returns the verdict incl. the gate contract."""
97
+ args = {"error": str(error_text)[:4000]}
98
+ if context:
99
+ args["context"] = str(context)[:2000]
100
+ if action:
101
+ args["action"] = str(action)[:500]
102
+ return _mcp_call("diagnose_infra_error", args, token=None) # diagnosis is FREE — never send the token here
103
+
104
+ # ---- the three-factor gate ----
105
+ def gate(self, verdict: dict) -> bool:
106
+ """True only when confidence>=conf_min AND source=='library' AND auto_safe. Fails CLOSED.
107
+ Prefer the server's ready-made gate.auto_apply_ok; re-check locally so a stale client can't over-trust."""
108
+ if not verdict or not verdict.get("matched"):
109
+ return False
110
+ conf = float(verdict.get("confidence") or 0.0)
111
+ source = verdict.get("source")
112
+ action_class = verdict.get("action_class")
113
+ auto_safe = verdict.get("auto_safe")
114
+ # local re-derivation of the contract (don't blindly trust a single server field)
115
+ safe = bool(auto_safe) if auto_safe is not None else (action_class in _AUTO_SAFE_CLASSES)
116
+ local_ok = (conf >= self.conf_min) and (source == "library") and safe
117
+ server_ok = bool((verdict.get("gate") or {}).get("auto_apply_ok"))
118
+ return local_ok and (server_ok or verdict.get("gate") is None)
119
+
120
+ # ---- report a gated auto-apply outcome (safety telemetry + feeds the moat). Best-effort, needs a token. ----
121
+ def _report_outcome(self, verdict: dict, succeeded: bool) -> None:
122
+ if not self.token:
123
+ return # report_outcome is token-scoped (attributes to your org); skip silently if no token
124
+ try:
125
+ _mcp_call("report_outcome", {
126
+ "failure_class": verdict.get("failure_class") or verdict.get("family"),
127
+ "family": verdict.get("family"),
128
+ "fix": verdict.get("fix"),
129
+ "confidence": verdict.get("confidence"),
130
+ "action_class": verdict.get("action_class"),
131
+ "succeeded": bool(succeeded),
132
+ }, token=self.token)
133
+ except Exception:
134
+ pass # telemetry must never break the caller
135
+
136
+ # ---- wrap a call: auto-diagnose + optionally self-heal ----
137
+ def run(self, fn: Callable, *args, _fix_kwarg: Optional[str] = None, **kwargs) -> Any:
138
+ """Run fn; on error, diagnose. If the gate passes AND auto_apply, retry ONCE. Else re-raise after logging.
139
+ _fix_kwarg: if your fn accepts a hint kwarg (e.g. retry_after / create_ata=True), name it and the fix
140
+ text is passed through; otherwise the retry is a plain re-run (correct for transient retry/refetch)."""
141
+ try:
142
+ return fn(*args, **kwargs)
143
+ except Exception as e:
144
+ verdict = self.diagnose(f"{type(e).__name__}: {e}")
145
+ verdict["_original_error"] = f"{type(e).__name__}: {e}"
146
+ if self.gate(verdict):
147
+ self.on_heal(verdict)
148
+ if self.auto_apply:
149
+ # gated-safe fixes are retry/refetch/config — a single re-run is the correct action for
150
+ # transient (retry) + re-fetch-then-reapply (refetch). For config the fix is advisory.
151
+ if _fix_kwarg:
152
+ kwargs[_fix_kwarg] = verdict.get("fix")
153
+ time.sleep(0.5) # brief backoff before the single retry
154
+ try:
155
+ result = fn(*args, **kwargs)
156
+ self._report_outcome(verdict, succeeded=True) # feeds safety telemetry + the moat
157
+ return result
158
+ except Exception:
159
+ # retry didn't fix it -> report the miss + escalate with the verdict for a human
160
+ self._report_outcome(verdict, succeeded=False)
161
+ self.on_escalate(verdict)
162
+ raise
163
+ # auto_apply off: healed-verdict available but we don't act
164
+ raise
165
+ else:
166
+ # NOT gated-safe (low conf, LLM source, or a mutate/destructive fix) -> escalate, never auto-act
167
+ self.on_escalate(verdict)
168
+ raise
169
+
170
+ def guard(self, fn: Callable) -> Callable:
171
+ """Decorator form of run()."""
172
+ def wrapped(*args, **kwargs):
173
+ return self.run(fn, *args, **kwargs)
174
+ wrapped.__name__ = getattr(fn, "__name__", "guarded")
175
+ return wrapped
176
+
177
+
178
+ # ---- tiny demo / smoke test ----
179
+ if __name__ == "__main__":
180
+ heal = SnapbackInterceptor(
181
+ auto_apply=False, # demo: diagnose + print, don't retry
182
+ on_escalate=lambda v: print(f"[ESCALATE] {v.get('family')} · conf={v.get('confidence')} · "
183
+ f"action_class={v.get('action_class')} · reason={(v.get('gate') or {}).get('reason')}"),
184
+ on_heal=lambda v: print(f"[AUTO-HEAL OK] {v.get('family')} · {v.get('fix')}"),
185
+ )
186
+ for err in ["deadlock detected 40P01",
187
+ "unable to get local issuer certificate",
188
+ "outcome type blocked do_not_try_again", # mutate -> must escalate, never auto-heal
189
+ "invalid_grant refresh token"]: # mutate -> escalate
190
+ v = heal.diagnose(err)
191
+ gated = heal.gate(v)
192
+ print(f"\n{err!r}\n family={v.get('family')} conf={v.get('confidence')} "
193
+ f"source={v.get('source')} action_class={v.get('action_class')} "
194
+ f"auto_safe={v.get('auto_safe')} -> GATE {'PASS (self-heal)' if gated else 'FAIL (escalate)'}")
@@ -0,0 +1,87 @@
1
+ Metadata-Version: 2.4
2
+ Name: snapback-selfheal
3
+ Version: 0.1.0
4
+ Summary: Make your AI agent self-heal — auto-diagnose every error and apply safe fixes, gated. Snapback interceptor for OpenClaw and any Python agent.
5
+ Author: Snapback / RA1 Labs
6
+ License: MIT
7
+ Project-URL: Homepage, https://snapback.sh
8
+ Project-URL: For agents, https://snapback.sh/for-agents
9
+ Project-URL: Docs, https://snapback.sh/docs
10
+ Keywords: ai-agent,self-healing,agentops,openclaw,error-recovery,mcp,snapback
11
+ Requires-Python: >=3.9
12
+ Description-Content-Type: text/markdown
13
+
14
+ # Snapback self-heal interceptor for OpenClaw
15
+
16
+ **Make Snapback automatic.** Instead of remembering to call the diagnosis tool, wrap your agent's tool calls
17
+ once — and every error is auto-diagnosed, and gated-safe fixes are applied and retried *without a human*.
18
+
19
+ > "Power tools are optional. Infrastructure is not." This turns Snapback from a tool you reach for into
20
+ > infrastructure that runs on every failure.
21
+
22
+ ## What it does
23
+ On **any** tool-call error, the interceptor:
24
+ 1. Calls `diagnose_infra_error` (free, no token, no LLM, <150ms).
25
+ 2. Applies the **three-factor gate** — auto-apply a fix only when **all** are true:
26
+ - `confidence >= 0.85`
27
+ - `source == "library"` (a curated verified fix, not an LLM guess)
28
+ - `auto_safe == true` (the fix is `retry` / `refetch` / `config` — reversible, side-effect-free)
29
+ 3. If the gate passes → applies the fix and **retries once**. If it fails → **escalates to a human** (logs the
30
+ verdict). It **never** auto-applies a `mutate` or `destructive` fix (create/change state, money, auth grants).
31
+
32
+ It fails **closed**: when unsure, it escalates rather than acting.
33
+
34
+ ## Install
35
+ ```bash
36
+ # no install needed beyond the stdlib; drop the file in, or:
37
+ cp snapback_interceptor.py your_agent/
38
+ export SNAPBACK_MCP=https://api.snapback.sh/mcp # default; the free tools need no token
39
+ ```
40
+
41
+ ## Use
42
+ ```python
43
+ from snapback_interceptor import SnapbackInterceptor
44
+
45
+ heal = SnapbackInterceptor(
46
+ auto_apply=True,
47
+ on_escalate=lambda v: my_ops_inbox.log(v), # human sees non-auto-safe verdicts
48
+ on_heal=lambda v: metrics.count("snapback.autoheal", family=v["family"]),
49
+ )
50
+
51
+ # wrap any call that might raise:
52
+ result = heal.run(call_solana_rpc, wallet, amount)
53
+
54
+ # or as a decorator:
55
+ @heal.guard
56
+ def call_solana_rpc(wallet, amount): ...
57
+
58
+ # or manually on a caught error:
59
+ verdict = heal.diagnose("BlockhashNotFound")
60
+ if verdict["gate"]["auto_apply_ok"]:
61
+ ...apply verdict["fix"] and retry...
62
+ ```
63
+
64
+ ## The gate contract (returned by Snapback)
65
+ `diagnose_infra_error` returns `{matched, family, fix, confidence, source, action_class, auto_safe, gate}`.
66
+ `action_class` is one of:
67
+
68
+ | class | meaning | auto-safe? |
69
+ |---|---|---|
70
+ | `retry` | re-run with backoff (idempotent) | ✅ |
71
+ | `refetch` | re-fetch/re-price/re-sync then re-apply | ✅ |
72
+ | `config` | client-side param change (raise max_tokens, serve intermediate cert) | ✅ (to suggest) |
73
+ | `mutate` | creates/changes external state, money, auth | ❌ escalate |
74
+ | `destructive` | deletes/reverts/irreversible | ❌ never |
75
+
76
+ `gate.auto_apply_ok` is the ready-made verdict; the interceptor also re-derives it locally so a stale client
77
+ can't over-trust.
78
+
79
+ ## Privacy & safety
80
+ - 100% client-side. Talks only to the free public `diagnose_infra_error` endpoint.
81
+ - Never sends your Snapback token for the diagnosis (diagnosis is free).
82
+ - Snapback scrubs trace content server-side; this sends only the error string you pass.
83
+ - Composes with `bridge.py` (post-mortem forwarding) — this is the live auto-heal layer.
84
+
85
+ ## Roadmap
86
+ - `submit_feedback` on the retry outcome (did the fix work?) → feeds the shared library (the network effect).
87
+ - Hermes + LangChain adapters (same gate, different framework hook).
@@ -0,0 +1,7 @@
1
+ README.md
2
+ pyproject.toml
3
+ snapback_interceptor.py
4
+ snapback_selfheal.egg-info/PKG-INFO
5
+ snapback_selfheal.egg-info/SOURCES.txt
6
+ snapback_selfheal.egg-info/dependency_links.txt
7
+ snapback_selfheal.egg-info/top_level.txt
@@ -0,0 +1 @@
1
+ snapback_interceptor