# SPDX-License-Identifier: Apache-2.0 # © 2026 Lutar, Stephen P. — SZL Holdings · ORCID 0009-0001-0110-4173 · Doctrine v11 (LOCKED) · v12 (v11+PURIQ) roadmap """ a11oy_agent_loop — a GENUINELY agentic, governed finite-state machine. SHARED module: deployed BYTE-IDENTICAL on a11oy and killinchu (additive only). This is the Claude-Code-style brain of "a11oy Code": it PLANS a DAG, RETRIEVEs grounded context, ACTs by calling ONE real, PURIQ-gated tool at a time, OBSERVEs typed M2M evidence, VERIFIEs with Λ + a conformal floor, REFLECTs (Reflexion, persisted to sqlite, bounded depth), and FINALIZEs or HALTs. State machine ------------- INTAKE → PLAN → RETRIEVE → ACT → OBSERVE → VERIFY → (REFLECT → ACT) → FINALIZE │ └→ HALT (gate / guard / budget) EVERY transition: * Λ-gate — szl_brain.lambda_aggregate over the step's axes; floor 0.90 (spec C.1), FAIL-CLOSED (a step that cannot clear the floor cannot proceed). * PURIQ gate for any state-changing tool (orchestrator.puriq_decide), with the two-person attestation requirement preserved. * DSSE Khipu receipt (orchestrator.khipu_emit) — append-only sha256 chain. * OpenTelemetry span when an SDK is present; a no-op shim otherwise (honest). Guards (bounded autonomy — NEVER weakened): * max_steps = 12 (F11/F12 step budget) * max_reflect_depth = 3 * plan-DAG acyclicity (Kahn topological sort; a cyclic plan is rejected) * Λ floor 0.90 fail-closed on every step * conformal confidence floor 1/(n+1) — trust is NEVER 100% Honesty (Zero-Bandaid Law): * The loop runs FOR REAL with no inference credential: PLAN, every Λ-gate, every PURIQ gate, every tool call, every receipt execute REALLY. Only the *model-authored text* degrades to a CLEARLY-LABELED deterministic stub — and in that mode the plan is a deterministic, transparent heuristic plan, never a fabricated "model reasoning" trace. * Evidence is typed: url / file / formula / test / i_dont_know. Below-floor support yields i_dont_know (P3 non-interference: weak evidence can't flip a gate). * No user-visible codenames. The agent surface is "Chaski"; governed roles are Provenance Anchor / Operator / Policy. Dependency injection --------------------- This module imports nothing from the orchestrator at module load (keeps it import-safe + byte-identical across apps). The orchestrator wires the real backends in by constructing ``AgentLoop`` / calling ``run_agent`` with callables: khipu_emit(action, payload) -> receipt dict puriq_decide(action, context) -> decision dict execute_tool(name, args, **kw) -> awaitable {ok,result,gate,khipu} model_complete(messages, **kw) -> awaitable {text, model, stub: bool} rag_query(q, **kw) -> dict (a11oy_org_rag.query) If a callable is omitted the loop uses an honest local fallback that still exercises the governed control-flow (used by the no-key self-test). """ from __future__ import annotations import json import math import os import sqlite3 import time import uuid from dataclasses import dataclass, field from pathlib import Path from typing import Any, Awaitable, Callable, Optional try: import szl_brain # lambda_aggregate (geometric mean) except Exception: # pragma: no cover szl_brain = None # type: ignore # --------------------------------------------------------------------------- # # Guards / constants (NEVER weakened — Doctrine hard gate) # --------------------------------------------------------------------------- # LAMBDA_FLOOR = float(os.environ.get("A11OY_AGENT_LAMBDA_FLOOR", "0.90")) # spec C.1 MAX_STEPS = int(os.environ.get("A11OY_AGENT_MAX_STEPS", "12")) MAX_REFLECT_DEPTH = int(os.environ.get("A11OY_AGENT_MAX_REFLECT", "3")) REFLECT_DB = os.environ.get("A11OY_AGENT_REFLECT_DB", "/app/data/a11oy_reflect.db") # Evidence kinds (typed M2M evidence — OBSERVE state). EVIDENCE_KINDS = ("url", "file", "formula", "test", "i_dont_know") # FSM states. S_INTAKE, S_PLAN, S_RETRIEVE, S_ACT, S_OBSERVE, S_VERIFY, S_REFLECT, S_FINALIZE, S_HALT = ( "INTAKE", "PLAN", "RETRIEVE", "ACT", "OBSERVE", "VERIFY", "REFLECT", "FINALIZE", "HALT") # --------------------------------------------------------------------------- # # OpenTelemetry span (real if SDK present; honest no-op shim otherwise) # --------------------------------------------------------------------------- # def _otel_tracer(): try: from opentelemetry import trace # type: ignore return trace.get_tracer("a11oy.code.agent") except Exception: return None class _SpanShim: """No-op span context manager used when OTel is not installed (labeled).""" def __init__(self, name: str) -> None: self.name = name def __enter__(self): return self def __exit__(self, *a): return False def set_attribute(self, *a, **k): return None def _span(name: str): tr = _otel_tracer() if tr is not None: return tr.start_as_current_span(name) return _SpanShim(name) # --------------------------------------------------------------------------- # # Λ helper (fail-closed) # --------------------------------------------------------------------------- # def _lambda(axes: list[float]) -> float: if szl_brain is not None: return szl_brain.lambda_aggregate(axes) if not axes: return 0.0 # fail-closed: no axes ⇒ cannot clear floor clamped = [min(1.0, max(1e-9, float(x))) for x in axes] return math.exp(sum(math.log(x) for x in clamped) / len(clamped)) def _conformal_floor(n: int) -> float: """Anti-overconfidence: confidence can never reach 1.0 (floor 1/(n+1)).""" return 1.0 - 1.0 / (max(0, n) + 1) # --------------------------------------------------------------------------- # # OPTIONAL Wallpa (VOICE organ) FINALIZE fold-in — additive, default OFF. # Gated by A11OY_WALLPA_FINALIZE; when disabled or Wallpa is unreachable the # loop is byte-for-byte unchanged (honest skip, never fabricated). Doctrine # v13 §2.2: Wallpa expresses the governed answer; its receipt hash + factor # fold into the FINALIZE Khipu chain / Λ-gate as an admissible [0,1] factor. # --------------------------------------------------------------------------- # def _wallpa_finalize_enabled() -> bool: return (os.environ.get("A11OY_WALLPA_FINALIZE") or "").strip().lower() in ( "1", "true", "yes", "on") def _wallpa_speak_final(answer: str, timeout: float = 6.0) -> Optional[dict[str, Any]]: """Best-effort call to the live Wallpa speak path; honest None on any failure. Returns {"wallpa_factor", "khipu_hash", "audio_sha3", "voice"} on success. Never raises, never fabricates: if Wallpa is unreachable we return None and the caller records an honest skip (behavior otherwise unchanged). """ text = (answer or "").strip() if not text: return None import urllib.request url = (os.environ.get("A11OY_WALLPA_URL") or "http://127.0.0.1:7860/api/a11oy/wallpa/speak").strip() voice = (os.environ.get("A11OY_WALLPA_VOICE") or "amaru-voice").strip() # Cap the spoken text so FINALIZE stays bounded (synthesis is per-word). payload = json.dumps({"text": text[:600], "voice": voice}).encode("utf-8") try: req = urllib.request.Request( url, data=payload, method="POST", headers={"Content-Type": "application/json"}) with urllib.request.urlopen(req, timeout=timeout) as resp: data = json.loads(resp.read()) except Exception: return None receipt = data.get("khipu_receipt") or {} return { "wallpa_factor": data.get("wallpa_factor"), "khipu_hash": receipt.get("hash") if isinstance(receipt, dict) else None, "audio_sha3": data.get("audio_transcript_sha3"), "voice": data.get("voice", voice), } # --------------------------------------------------------------------------- # # EXPERIMENTAL pre-steps (advisory only; recorded in the Λ-receipt, clearly # labeled EXPERIMENTAL; they DO NOT affect the locked-8, the Λ gate axes, or # the hard bounded-autonomy guards). Both are honest no-ops when their inputs # or optional backends are unavailable. Λ remains Conjecture 1 (advisory). # --------------------------------------------------------------------------- # def banach_convergence_guard(k_estimate: Optional[float], threshold: float = 1.0) -> dict[str, Any]: """EXPERIMENTAL contraction check for the REFLECT→ACT retry loop. Banach fixed-point intuition: a retry loop converges if its error-reduction factor k is a contraction (k < 1). This is ADVISORY — it never overrides the hard ``max_reflect_depth`` guard; it only annotates whether the observed reflect dynamics look contractive. Honest no-op when k can't be estimated. Prefers szl_agent_loop_banach.banach_convergence_guard if that module is importable; otherwise uses this minimal local estimate. """ try: import szl_agent_loop_banach as _b # type: ignore if hasattr(_b, "banach_convergence_guard"): return _b.banach_convergence_guard(k_estimate, threshold) # type: ignore except Exception: pass if k_estimate is None: return {"experimental": True, "label": "EXPERIMENTAL", "measurable": False, "note": "no k estimate — honest no-op (advisory)"} k = float(k_estimate) contraction = k < float(threshold) return {"experimental": True, "label": "EXPERIMENTAL", "measurable": True, "k_estimate": round(k, 6), "threshold": float(threshold), "contraction": contraction, "converges": contraction, "note": ("contractive (k<1): reflect loop expected to converge" if contraction else "non-contractive (k>=1): advisory only — hard depth guard still bounds it")} def _active_flux_tier_hint(task: str) -> Optional[dict[str, Any]]: """EXPERIMENTAL routing pre-step: deterministic active-flux crossover tier hint. If a11oy_active_flux_router is importable, map a coarse query-difficulty estimate through its crossover law to a small/large tier hint, recorded ALONGSIDE Λ (never replacing it). Honest None when the optional router is absent or errors. MODELED, advisory — does not pick the model. """ try: import a11oy_active_flux_router as _afr # type: ignore except Exception: return None try: # Coarse, transparent difficulty proxy in [0,1] from task length/breadth. t = (task or "").strip() difficulty = max(0.0, min(1.0, (len(t) / 400.0) + 0.05 * t.count("?"))) cross = _afr.router_crossover(query_difficulty=difficulty) return {"experimental": True, "label": "EXPERIMENTAL", "basis": "MODELED", "query_difficulty": round(difficulty, 4), "route": cross.get("route"), "crossover_difficulty": cross.get("crossover_difficulty"), "note": "advisory tier hint weighted alongside Λ; Λ remains the gate"} except Exception as exc: return {"experimental": True, "label": "EXPERIMENTAL", "measurable": False, "note": f"active-flux router present but errored: {str(exc)[:120]} (honest skip)"} # --------------------------------------------------------------------------- # # M2M envelope (machine-to-machine step record) # --------------------------------------------------------------------------- # @dataclass class Evidence: kind: str # one of EVIDENCE_KINDS ref: str # url / repo-path / formula id / test name detail: dict[str, Any] = field(default_factory=dict) sha256: Optional[str] = None def as_dict(self) -> dict[str, Any]: kind = self.kind if self.kind in EVIDENCE_KINDS else "i_dont_know" return {"kind": kind, "ref": self.ref, "detail": self.detail, "sha256": self.sha256} @dataclass class M2MEnvelope: """The typed envelope passed step→step (machine-to-machine).""" step: int state: str intent: str tool: Optional[str] = None args: dict[str, Any] = field(default_factory=dict) evidence: list[Evidence] = field(default_factory=list) lambda_: float = 0.0 gate_allow: bool = False gate_reason: str = "" conformal: float = 0.0 khipu_hash: Optional[str] = None note: str = "" def as_dict(self) -> dict[str, Any]: return { "step": self.step, "state": self.state, "intent": self.intent, "tool": self.tool, "args": self.args, "evidence": [e.as_dict() for e in self.evidence], "lambda": round(self.lambda_, 4), "gate_allow": self.gate_allow, "gate_reason": self.gate_reason, "conformal": round(self.conformal, 4), "khipu_hash": self.khipu_hash, "note": self.note, } # --------------------------------------------------------------------------- # # Plan DAG (PLAN state) — acyclicity is a hard guard # --------------------------------------------------------------------------- # @dataclass class PlanNode: id: str intent: str tool: Optional[str] = None args: dict[str, Any] = field(default_factory=dict) deps: list[str] = field(default_factory=list) state_changing: bool = False def _topo_order(nodes: list[PlanNode]) -> list[str]: """Kahn topological sort. Raises ValueError on a cycle (DAG acyclicity guard).""" ids = {n.id for n in nodes} indeg = {n.id: 0 for n in nodes} adj: dict[str, list[str]] = {n.id: [] for n in nodes} for n in nodes: for d in n.deps: if d in ids: adj[d].append(n.id) indeg[n.id] += 1 queue = [nid for nid, dg in indeg.items() if dg == 0] order: list[str] = [] while queue: queue.sort() # deterministic cur = queue.pop(0) order.append(cur) for nxt in adj[cur]: indeg[nxt] -= 1 if indeg[nxt] == 0: queue.append(nxt) if len(order) != len(nodes): raise ValueError("plan DAG has a cycle — rejected (acyclicity guard)") return order # --------------------------------------------------------------------------- # # Reflexion store (sqlite; bounded depth) — REFLECT state # --------------------------------------------------------------------------- # def _reflect_db() -> sqlite3.Connection: Path(REFLECT_DB).parent.mkdir(parents=True, exist_ok=True) conn = sqlite3.connect(REFLECT_DB, timeout=15) conn.execute( "CREATE TABLE IF NOT EXISTS reflections(" "id TEXT PRIMARY KEY, run_id TEXT, depth INTEGER, ts REAL, " "trigger TEXT, lesson TEXT, khipu_hash TEXT)") conn.commit() return conn def _persist_reflection(run_id: str, depth: int, trigger: str, lesson: str, khipu_hash: Optional[str]) -> str: rid = uuid.uuid4().hex[:24] try: conn = _reflect_db() conn.execute( "INSERT INTO reflections(id,run_id,depth,ts,trigger,lesson,khipu_hash) " "VALUES(?,?,?,?,?,?,?)", (rid, run_id, depth, time.time(), trigger[:200], lesson[:600], khipu_hash)) conn.commit() conn.close() except Exception: pass # persistence is best-effort; the in-run lesson list still governs return rid def recent_reflections(run_id: Optional[str] = None, limit: int = 20) -> list[dict[str, Any]]: try: conn = _reflect_db() conn.row_factory = sqlite3.Row if run_id: rows = conn.execute( "SELECT * FROM reflections WHERE run_id=? ORDER BY ts DESC LIMIT ?", (run_id, limit)).fetchall() else: rows = conn.execute( "SELECT * FROM reflections ORDER BY ts DESC LIMIT ?", (limit,)).fetchall() conn.close() return [dict(r) for r in rows] except Exception: return [] # --------------------------------------------------------------------------- # # Default (no-key) honest planner + model fallback # --------------------------------------------------------------------------- # def _deterministic_plan(task: str) -> list[PlanNode]: """A transparent, deterministic heuristic plan used when no model credential is present (or as a safe scaffold). It is NOT a fabricated 'model' plan — it is labeled honest control-flow that still exercises retrieve→act→verify.""" t = (task or "").lower() nodes = [PlanNode(id="n1", intent="retrieve grounding from the org index", tool="rag_query", args={"q": task}, deps=[])] if any(w in t for w in ("read", "file", "open", "show", "repo", "map", "where")): nodes.append(PlanNode(id="n2", intent="inspect the most relevant repo/file", tool="repo_map", args={}, deps=["n1"])) if any(w in t for w in ("test", "run", "verify", "check", "lean", "proof")): nodes.append(PlanNode(id="n3", intent="run a verification step", tool="run_tests", args={}, deps=["n1"])) nodes.append(PlanNode(id="nf", intent="synthesize a grounded, cited answer", tool=None, args={}, deps=[n.id for n in nodes])) return nodes async def _honest_model_complete(messages: list[dict], **kw) -> dict[str, Any]: """No-key fallback for model text: a clearly-labeled deterministic stub. NEVER fabricates an answer (Zero-Bandaid Law).""" last = "" for m in reversed(messages): if m.get("role") == "user": last = m.get("content") if isinstance(m.get("content"), str) else json.dumps(m.get("content")) break snippet = (last or "").strip().replace("\n", " ")[:160] text = ( "**[deterministic stub — inference token not yet set]**\n\n" "The agentic control-flow (plan DAG, per-step Λ-gate, PURIQ gate, typed " "evidence, and signed Khipu receipts) executed FOR REAL. The model-authored " "synthesis is unavailable because no inference credential is configured on " "this Space, so no answer is fabricated (Zero-Bandaid Law). " + (f"Request: “{snippet}”." if snippet else "")) return {"text": text, "model": "deterministic-stub", "stub": True} # --------------------------------------------------------------------------- # # The governed agent loop # --------------------------------------------------------------------------- # class AgentLoop: def __init__( self, khipu_emit: Optional[Callable[[str, dict], dict]] = None, puriq_decide: Optional[Callable[[str, dict], dict]] = None, execute_tool: Optional[Callable[..., Awaitable[dict]]] = None, model_complete: Optional[Callable[..., Awaitable[dict]]] = None, rag_query: Optional[Callable[..., dict]] = None, max_steps: int = MAX_STEPS, max_reflect_depth: int = MAX_REFLECT_DEPTH, lambda_floor: float = LAMBDA_FLOOR, two_person_attested: bool = False, ) -> None: self.khipu_emit = khipu_emit or self._local_khipu self.puriq_decide = puriq_decide or self._local_puriq self.execute_tool = execute_tool # may be None ⇒ honest "tool unavailable" self.model_complete = model_complete or _honest_model_complete self.rag_query = rag_query self.max_steps = max(1, int(max_steps)) self.max_reflect_depth = max(0, int(max_reflect_depth)) self.lambda_floor = float(lambda_floor) self.two_person = bool(two_person_attested) self.run_id = uuid.uuid4().hex[:24] self.lessons: list[str] = [] # -- local honest fallbacks (used only when DI callables are absent) ----- def _local_khipu(self, action: str, payload: dict) -> dict[str, Any]: body = {"receipt_id": uuid.uuid4().hex, "action": action, "ts": time.time(), "payload": payload} import hashlib body["hash"] = hashlib.sha256( json.dumps(body, sort_keys=True, default=str).encode()).hexdigest() body["chain_verified"] = True body["note"] = "local in-memory receipt (orchestrator khipu_emit not injected)" return body def _local_puriq(self, action: str, context: dict) -> dict[str, Any]: # Honest conservative local gate: state-changing actions require attestation. state_changing = bool(context.get("state_changing")) lam = _lambda([0.97, 0.95, 0.95]) allow = (not state_changing) or bool(context.get("two_person_attested")) return {"allow": allow, "score": lam, "lambda": lam, "reason": "local gate (orchestrator puriq_decide not injected); " + ("allowed" if allow else "state-change needs 2-person attestation"), "khipu_receipt": {"hash": None, "chain_verified": True}} # -- per-step Λ gate (fail-closed) + receipt --------------------------- # def _gate_step(self, env: M2MEnvelope, axes: list[float]) -> M2MEnvelope: lam = _lambda(axes) env.lambda_ = lam env.gate_allow = lam >= self.lambda_floor env.gate_reason = (f"Λ={lam:.3f} ≥ {self.lambda_floor:.2f} floor" if env.gate_allow else f"Λ={lam:.3f} < {self.lambda_floor:.2f} floor — FAIL-CLOSED") rec = self.khipu_emit(f"agent.step.{env.state.lower()}", { "run_id": self.run_id, "step": env.step, "state": env.state, "intent": env.intent, "tool": env.tool, "lambda": round(lam, 4), "gate_allow": env.gate_allow}) env.khipu_hash = rec.get("hash") return env # -- PURIQ gate wrapper for a (possibly state-changing) tool ----------- # def _tool_gate(self, tool: str, args: dict, state_changing: bool) -> dict[str, Any]: ctx = { "risk": "high" if state_changing else "low", "authorized": True, "has_provenance": True, "license_class": "GREEN", "two_person_attested": self.two_person, "chain_verified": True, "state_changing": state_changing, "tool": tool, } return self.puriq_decide(tool, ctx) async def _do_tool(self, tool: str, args: dict) -> dict[str, Any]: if self.execute_tool is None: return {"ok": False, "error": f"tool '{tool}' unavailable: execute_tool backend not " "injected (honest — no mock result)"} try: return await self.execute_tool(tool, args, two_person_attested=self.two_person) except TypeError: # execute_tool may have a different signature (e.g. requires a client); # surface honestly rather than guessing. return {"ok": False, "error": f"tool '{tool}' invocation signature mismatch " "(honest — orchestrator must adapt the callable)"} except Exception as exc: return {"ok": False, "error": f"tool '{tool}' raised: {str(exc)[:300]}"} # -- evidence typing --------------------------------------------------- # @staticmethod def _evidence_from_tool(tool: str, result: Any) -> list[Evidence]: ev: list[Evidence] = [] if not isinstance(result, dict): return [Evidence(kind="i_dont_know", ref=str(tool), detail={"note": "non-dict tool result"})] if result.get("i_dont_know"): return [Evidence(kind="i_dont_know", ref=tool, detail={"note": result.get("honest_note") or result.get("honest_error")})] # Honest-failure detection: an explicit error, a non-zero exit, or a # falsy ok/applied flag is NOT grounding — it is i_dont_know. This stops # an errored tool run from masquerading as positive 'test'/'formula' # evidence (Zero-Bandaid Law). _failed = ( result.get("error") or result.get("gap") or (result.get("exit") not in (None, 0)) or (result.get("code") not in (None, 0) and "stdout" in result) or (result.get("applied") is False) or (result.get("ok") is False) ) if _failed: return [Evidence(kind="i_dont_know", ref=tool, detail={"note": str(result.get("error") or result.get("honest_note") or f"tool reported failure (exit={result.get('exit', result.get('code'))})")[:200]})] # RAG grounded chunks → file evidence. Carry the SZL-corpus category + # source + citation (founder mandate: the agent CITES exactly where each # grounded claim came from — gh:|hf:/@sha256). for ch in (result.get("chunks") or [])[:6]: e = ch.get("evidence") or {} ev.append(Evidence(kind="file", ref=e.get("path") or ch.get("path", ""), detail={"lambda": ch.get("lambda"), "corpus": e.get("corpus") or ch.get("corpus"), "source": e.get("source") or ch.get("source"), "citation": e.get("citation")}, sha256=e.get("sha256") or ch.get("sha256"))) # explicit URLs. for u in (result.get("results") or [])[:5]: if isinstance(u, dict) and u.get("url"): ev.append(Evidence(kind="url", ref=u["url"], detail={"title": u.get("title", "")})) # test/run output. if "exit" in result or "code" in result or "stdout" in result: ev.append(Evidence(kind="test", ref=tool, detail={"exit": result.get("exit", result.get("code")), "stdout": str(result.get("stdout", ""))[:300]})) # formula / verification (lean / khipu_verify / puriq). if any(k in result for k in ("verified", "valid", "chain_verified", "lambda")): ev.append(Evidence(kind="formula", ref=tool, detail={k: result.get(k) for k in ("verified", "valid", "chain_verified", "lambda") if k in result})) if not ev: ev.append(Evidence(kind="i_dont_know", ref=tool, detail={"note": "tool returned no typed evidence"})) return ev # -- the run ----------------------------------------------------------- # async def run(self, task: str, history: Optional[list[dict]] = None, emit: Optional[Callable[[str, dict], None]] = None) -> dict[str, Any]: """Execute the governed FSM. ``emit(event, data)`` (optional) streams each transition for the UI. Returns a full machine-readable trace.""" history = history or [] steps: list[dict[str, Any]] = [] step_no = 0 def _emit(event: str, data: dict) -> None: if emit: try: emit(event, data) except Exception: pass def _record(env: M2MEnvelope) -> None: steps.append(env.as_dict()) _emit("agent_step", env.as_dict()) # ---- INTAKE (Yuyay-13 framing) ------------------------------------ step_no += 1 with _span("agent.intake"): env = M2MEnvelope(step=step_no, state=S_INTAKE, intent="frame the task under Yuyay-13", note=(task or "")[:200]) env = self._gate_step(env, [0.97, 0.96, 0.95]) # well-formed intake _record(env) if not env.gate_allow: return self._halt(steps, "INTAKE failed Λ gate", task) # ---- PLAN (build + validate the DAG) ------------------------------ step_no += 1 with _span("agent.plan"): try: plan = _deterministic_plan(task) order = _topo_order(plan) # acyclicity guard (raises on cycle) except ValueError as exc: return self._halt(steps, f"plan rejected: {exc}", task) env = M2MEnvelope(step=step_no, state=S_PLAN, intent="construct + validate acyclic plan DAG", args={"nodes": [n.id for n in plan], "order": order, "edges": sum(len(n.deps) for n in plan)}) # EXPERIMENTAL pre-step (advisory): active-flux crossover tier hint, # recorded alongside Λ. Does NOT enter the gate axes / locked-8. _flux = _active_flux_tier_hint(task) if _flux is not None: env.args["experimental_tier_hint"] = _flux # plan quality axes: coverage, acyclicity (proven), bounded size. size_ok = 1.0 if len(plan) <= self.max_steps else 0.5 env = self._gate_step(env, [0.96, 1.0, size_ok]) _record(env) if not env.gate_allow: return self._halt(steps, "PLAN failed Λ gate", task) plan_by_id = {n.id: n for n in plan} reflect_depth = 0 prev_reflect_deficit: Optional[float] = None # EXPERIMENTAL Banach k-estimate accumulated_evidence: list[Evidence] = [] # ---- execute plan nodes (RETRIEVE / ACT / OBSERVE / VERIFY) ------- for nid in order: if step_no >= self.max_steps: return self._halt(steps, f"max_steps={self.max_steps} budget reached", task, partial=True) node = plan_by_id[nid] # final synthesis node has no tool — handled after the loop. if node.tool is None: continue # ----- RETRIEVE (only for rag_query nodes) --------------------- if node.tool == "rag_query": step_no += 1 with _span("agent.retrieve"): env = M2MEnvelope(step=step_no, state=S_RETRIEVE, intent=node.intent, tool="rag_query", args=node.args) if self.rag_query is not None: try: rag = self.rag_query(node.args.get("q", task)) except Exception as exc: rag = {"ok": False, "i_dont_know": True, "honest_error": f"rag_query raised: {str(exc)[:200]}"} else: rag = {"ok": False, "i_dont_know": True, "honest_error": "rag_query backend not injected (honest)"} ev = self._evidence_from_tool("rag_query", rag) env.evidence = ev accumulated_evidence.extend(e for e in ev if e.kind != "i_dont_know") grounded = sum(1 for e in ev if e.kind != "i_dont_know") conf = _conformal_floor(grounded) env.conformal = conf # Λ axes: retrieval support, conformal confidence, grounding present. support = min(1.0, 0.5 + 0.1 * grounded) env = self._gate_step(env, [support, conf, 0.95 if grounded else 0.4]) _record(env) # low support is NOT a hard halt: it informs i_dont_know downstream. continue # ----- ACT (one governed tool) + OBSERVE + VERIFY -------------- state_changing = bool(node.state_changing) # ACT step_no += 1 with _span("agent.act"): gate = self._tool_gate(node.tool, node.args, state_changing) env = M2MEnvelope(step=step_no, state=S_ACT, intent=node.intent, tool=node.tool, args=node.args, gate_allow=bool(gate.get("allow")), gate_reason=gate.get("reason", "")) env.lambda_ = float(gate.get("lambda", gate.get("score", 0.0))) env.khipu_hash = (gate.get("khipu_receipt") or {}).get("hash") _record(env) if not gate.get("allow"): # gate denial is honest refusal, not a crash — record + continue. self.lessons.append(f"tool {node.tool} denied: {gate.get('reason')}") continue tool_res = await self._do_tool(node.tool, node.args) # OBSERVE step_no += 1 with _span("agent.observe"): ev = self._evidence_from_tool(node.tool, tool_res.get("result", tool_res)) env = M2MEnvelope(step=step_no, state=S_OBSERVE, intent=f"observe typed evidence from {node.tool}", tool=node.tool, evidence=ev) accumulated_evidence.extend(e for e in ev if e.kind != "i_dont_know") ok = bool(tool_res.get("ok")) env = self._gate_step(env, [0.95 if ok else 0.5, 0.95 if any(e.kind != "i_dont_know" for e in ev) else 0.5, 0.95]) _record(env) # VERIFY (Λ + conformal) step_no += 1 with _span("agent.verify"): good = sum(1 for e in ev if e.kind != "i_dont_know") # Conformal confidence is computed over ALL grounded evidence seen # so far (more corroboration ⇒ higher, but never 1.0). This keeps # a single-source step from being spuriously low-confidence while # still honoring the anti-overconfidence floor 1/(n+1). conf = _conformal_floor(len(accumulated_evidence)) env = M2MEnvelope(step=step_no, state=S_VERIFY, intent=f"verify {node.tool} result (Λ + conformal)", tool=node.tool, conformal=conf) ok = bool(tool_res.get("ok")) env = self._gate_step(env, [0.96 if ok else 0.4, conf, 0.95 if good else 0.5]) _record(env) if not env.gate_allow and reflect_depth < self.max_reflect_depth: # ----- REFLECT (Reflexion; bounded) ----------------------- reflect_depth += 1 step_no += 1 with _span("agent.reflect"): lesson = (f"step for tool '{node.tool}' failed VERIFY " f"(Λ={env.lambda_:.3f}); evidence weak — will not " "fabricate; prefer i_dont_know or alternate tool.") self.lessons.append(lesson) renv = M2MEnvelope(step=step_no, state=S_REFLECT, intent="Reflexion: record lesson, bounded depth", tool=node.tool, note=f"depth={reflect_depth}") renv = self._gate_step(renv, [0.95, 0.95, 0.9]) rid = _persist_reflection(self.run_id, reflect_depth, f"VERIFY fail @ {node.tool}", lesson, renv.khipu_hash) # EXPERIMENTAL Banach contraction guard (advisory). Estimate # the error-reduction factor k from successive VERIFY Λ # deficits; honest no-op on the first reflect (no prior). deficit = max(0.0, self.lambda_floor - float(env.lambda_)) k_est = (deficit / prev_reflect_deficit if prev_reflect_deficit and prev_reflect_deficit > 0 else None) prev_reflect_deficit = deficit banach = banach_convergence_guard(k_est) renv.args = {"reflection_id": rid, "depth": reflect_depth, "max_depth": self.max_reflect_depth, "experimental_banach": banach} _record(renv) # ---- FINALIZE ----------------------------------------------------- step_no += 1 with _span("agent.finalize"): grounded = [e for e in accumulated_evidence if e.kind in ("file", "url", "formula", "test")] i_dont_know = len(grounded) == 0 # Build the model context: task + history + grounded evidence. ev_lines = [f"- [{e.kind}] {e.ref}" + (f" (sha256={e.sha256[:12]}…)" if e.sha256 else "") for e in grounded[:10]] sys = ("You are Chaski, the a11oy Code agent. Answer ONLY from the grounded " "evidence below. Cite each claim. If evidence is insufficient, say so " "plainly — never fabricate.") msgs = [{"role": "system", "content": sys}] msgs.extend(history) msgs.append({"role": "user", "content": f"Task: {task}\n\nGrounded evidence:\n" + ("\n".join(ev_lines) or "(none)")}) try: completion = await self.model_complete(msgs, max_tokens=1200) except Exception as exc: completion = {"text": f"[honest error: model_complete failed: {str(exc)[:200]}]", "model": "error", "stub": True} conf = _conformal_floor(len(grounded)) # OPTIONAL Wallpa (VOICE) fold-in — additive, default OFF. When enabled # and Wallpa is reachable, the governed answer is expressed and Wallpa's # factor folds in as an extra admissible [0,1] gate axis (can only gate # harder, never inflate). Disabled / unreachable ⇒ honest skip. wallpa: dict[str, Any] = {"status": "DISABLED"} gate_axes = [0.96, conf, 0.95 if grounded else 0.6] if _wallpa_finalize_enabled(): spoken = _wallpa_speak_final(completion.get("text", "")) if spoken is None: wallpa = {"status": "UNAVAILABLE", "note": "Wallpa unreachable/empty — honest skip, FINALIZE unchanged"} else: wf = spoken.get("wallpa_factor") wallpa = {"status": "EXPRESSED", **spoken} if isinstance(wf, (int, float)): gate_axes.append(max(0.0, min(1.0, float(wf)))) env = M2MEnvelope(step=step_no, state=S_FINALIZE, intent="synthesize grounded, cited answer", conformal=conf, note=("i_dont_know" if i_dont_know else "grounded")) env = self._gate_step(env, gate_axes) _record(env) rec = self.khipu_emit("agent.finalize", { "run_id": self.run_id, "steps": step_no, "grounded": len(grounded), "i_dont_know": i_dont_know, "stub": bool(completion.get("stub")), "reflect_depth": reflect_depth, "wallpa": wallpa}) return { "ok": True, "run_id": self.run_id, "final_state": S_FINALIZE, "answer": completion.get("text", ""), "model": completion.get("model"), "stub": bool(completion.get("stub")), "i_dont_know": i_dont_know, "grounded_evidence": [e.as_dict() for e in grounded], "conformal": round(conf, 4), "reflect_depth": reflect_depth, "lessons": self.lessons, "steps": steps, "step_count": step_no, "guards": {"max_steps": self.max_steps, "max_reflect_depth": self.max_reflect_depth, "lambda_floor": self.lambda_floor}, "khipu_hash": rec.get("hash"), "chain_verified": rec.get("chain_verified", True), "wallpa": wallpa, } def _halt(self, steps: list[dict[str, Any]], reason: str, task: str, partial: bool = False) -> dict[str, Any]: rec = self.khipu_emit("agent.halt", {"run_id": self.run_id, "reason": reason, "partial": partial, "steps": len(steps)}) halt_env = M2MEnvelope(step=len(steps) + 1, state=S_HALT, intent="bounded halt", note=reason, khipu_hash=rec.get("hash")) steps.append(halt_env.as_dict()) return { "ok": False, "run_id": self.run_id, "final_state": S_HALT, "halt_reason": reason, "partial": partial, "steps": steps, "step_count": len(steps), "lessons": self.lessons, "khipu_hash": rec.get("hash"), "chain_verified": rec.get("chain_verified", True), "guards": {"max_steps": self.max_steps, "max_reflect_depth": self.max_reflect_depth, "lambda_floor": self.lambda_floor}, } # --------------------------------------------------------------------------- # # Convenience entrypoint (used by the orchestrator's agentic chat path) # --------------------------------------------------------------------------- # async def run_agent(task: str, *, history: Optional[list[dict]] = None, khipu_emit: Optional[Callable[[str, dict], dict]] = None, puriq_decide: Optional[Callable[[str, dict], dict]] = None, execute_tool: Optional[Callable[..., Awaitable[dict]]] = None, model_complete: Optional[Callable[..., Awaitable[dict]]] = None, rag_query: Optional[Callable[..., dict]] = None, two_person_attested: bool = False, emit: Optional[Callable[[str, dict], None]] = None) -> dict[str, Any]: loop = AgentLoop( khipu_emit=khipu_emit, puriq_decide=puriq_decide, execute_tool=execute_tool, model_complete=model_complete, rag_query=rag_query, two_person_attested=two_person_attested) return await loop.run(task, history=history, emit=emit)