betterwithage commited on
Commit
fb116f2
·
verified ·
1 Parent(s): a700ceb

chore(sync): mirror backend .py + Dockerfile to Space (hf-sync-backend)

Browse files

Automated backend sync from szl-holdings/a11oy main via hf-sync-backend.
Updated (differed from the Space): Dockerfile, a11oy_react_core.py, serve.py
Deleted (gone from the repo + Dockerfile COPY set): (none)

Keeps the Space-built backend (serve.py + the Dockerfile-COPY'd .py
modules) identical to GitHub main so the Space never rebuilds from a
stale backend, new endpoints don't 404 there, and orphaned modules
removed from the repo don't linger in the Space tree.

Files changed (3) hide show
  1. Dockerfile +9 -0
  2. a11oy_react_core.py +805 -0
  3. serve.py +40 -0
Dockerfile CHANGED
@@ -317,6 +317,15 @@ COPY web/living-anatomy.html ./web/living-anatomy.html
317
  # _ptg_serve. Without these COPYs the guarded import falls back and /nemo 404s.
318
  COPY a11oy_nemo_core.py ./
319
  COPY web/nemo.html ./web/nemo.html
 
 
 
 
 
 
 
 
 
320
 
321
  # ADDITIVE (Cross-Harness Receipt Bridge — Hermes + OpenClaw; 2026-06-01, Yachay /
322
  # Perplexity Computer Agent; closeout PR superseding #198 runtime files). serve.py
 
317
  # _ptg_serve. Without these COPYs the guarded import falls back and /nemo 404s.
318
  COPY a11oy_nemo_core.py ./
319
  COPY web/nemo.html ./web/nemo.html
320
+ # ADDITIVE (Lane A AGENTIC CORE, Dev A, 2026-06-14; QA9 restore 2026-06): the
321
+ # resumable ReAct agent-loop core module. Per-file COPY (this Dockerfile uses no
322
+ # COPY . .). a11oy_react_core.py is imported by serve.py (try/except guarded) and
323
+ # serves /api/a11oy/v1/agent/react/{run,resume,trace,checkpoints} (+ free
324
+ # top-level /api/a11oy/v1/agent/{resume,trace/{id},checkpoints}). Without this COPY
325
+ # the guarded import falls back to a stub in the image and the react endpoints
326
+ # 404. Restores wiring clobbered by a later integration-wave push built from a
327
+ # stale base (the register block in serve.py + this COPY were both lost).
328
+ COPY a11oy_react_core.py ./
329
 
330
  # ADDITIVE (Cross-Harness Receipt Bridge — Hermes + OpenClaw; 2026-06-01, Yachay /
331
  # Perplexity Computer Agent; closeout PR superseding #198 runtime files). serve.py
a11oy_react_core.py ADDED
@@ -0,0 +1,805 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ # ============================================================================
3
+ # a11oy_react_core.py — LANE A AGENTIC CORE (Dev A, SZL Holdings)
4
+ # ----------------------------------------------------------------------------
5
+ # A REAL, resumable ReAct execution graph (Thought -> Action -> Observation)
6
+ # where EACH node transition is a SIGNED receipt boundary, plus:
7
+ # * SqliteSaver-style checkpointing (crash mid-run -> /resume continues)
8
+ # * Reflexion inner loop (NL reflection prepended next activation)
9
+ # * Generative-Agents memory scoring score(m)=a_rec*g^dt + a_imp*imp + a_rel*cos
10
+ # * Letta-style memory tiering (working in-context + archival vector)
11
+ # * Voyager skill library (admit a recipe ONLY after a passing receipt)
12
+ #
13
+ # Honest engineering (DOCTRINE v11):
14
+ # - The signer is the HOST app's REAL in-image ECDSA-P256 DSSE signer
15
+ # (_a11oy_sign_receipt), passed in via register(); we NEVER fabricate a
16
+ # signature. verify_fn re-verifies against /cosign.pub.
17
+ # - The vector store is LOCAL (sqlite + numpy, hashing-trie embeddings) — 0
18
+ # external CDN/service. Embeddings are a deterministic local feature hash
19
+ # (labelled HEURISTIC), not a remote model, so retrieval is reproducible
20
+ # offline. Scores are surfaced honestly with their components.
21
+ # - Trust / coverage framing, never bare "confidence %".
22
+ # - Routes are inserted at position 0 (Starlette Route) so they beat the SPA
23
+ # catch-all, mirroring szl_agentic_loop.
24
+ # - Endpoints live under the FREE /api/a11oy/v1/agent/react/* sub-namespace
25
+ # (run/resume/trace/checkpoints) so we do NOT collide with the existing
26
+ # /run, /tools, /verify-chain, /governance-standards, /_diag, /loop.
27
+ #
28
+ # Co-Authored-By: Perplexity Computer Agent <agent@perplexity.ai>
29
+ # Signed-off-by: Stephen P. Lutar Jr. <stephenlutar2@gmail.com>
30
+ # ============================================================================
31
+ from __future__ import annotations
32
+
33
+ import hashlib
34
+ import json
35
+ import math
36
+ import os
37
+ import re
38
+ import sqlite3
39
+ import threading
40
+ import time
41
+ import uuid
42
+ from datetime import datetime, timezone
43
+
44
+ # ---------------------------------------------------------------------------
45
+ # Storage: a single local sqlite DB under /tmp (ephemeral per container, which
46
+ # is the honest reality of a HF Space). All five subsystems persist here so a
47
+ # crash mid-run can resume from the last committed checkpoint within the life
48
+ # of the container. Labelled accordingly in the UI.
49
+ # ---------------------------------------------------------------------------
50
+ _DB_PATH = os.environ.get("A11OY_REACT_DB", "/tmp/a11oy_react_core.sqlite3")
51
+ _LOCK = threading.RLock()
52
+ _EMBED_DIM = 64 # local hashing-trick embedding dimension
53
+
54
+
55
+ def _now_iso() -> str:
56
+ return datetime.now(timezone.utc).isoformat()
57
+
58
+
59
+ def _now_epoch() -> float:
60
+ return time.time()
61
+
62
+
63
+ def _sha(obj) -> str:
64
+ return hashlib.sha256(
65
+ json.dumps(obj, sort_keys=True, separators=(",", ":")).encode("utf-8")
66
+ ).hexdigest()
67
+
68
+
69
+ def _conn() -> sqlite3.Connection:
70
+ c = sqlite3.connect(_DB_PATH, timeout=30, check_same_thread=False)
71
+ c.row_factory = sqlite3.Row
72
+ c.execute("PRAGMA journal_mode=WAL;")
73
+ return c
74
+
75
+
76
+ def _init_db() -> None:
77
+ with _LOCK, _conn() as c:
78
+ c.executescript(
79
+ """
80
+ CREATE TABLE IF NOT EXISTS runs (
81
+ run_id TEXT PRIMARY KEY,
82
+ goal TEXT, status TEXT, max_steps INTEGER,
83
+ step INTEGER, prev_hash TEXT, final_hash TEXT,
84
+ reflection TEXT, created_at TEXT, updated_at TEXT
85
+ );
86
+ CREATE TABLE IF NOT EXISTS receipts (
87
+ run_id TEXT, seq INTEGER, node TEXT, body TEXT,
88
+ prev_hash TEXT, hash TEXT, envelope TEXT, ts TEXT,
89
+ PRIMARY KEY (run_id, seq)
90
+ );
91
+ CREATE TABLE IF NOT EXISTS checkpoints (
92
+ run_id TEXT, step INTEGER, state TEXT, prev_hash TEXT,
93
+ ts TEXT, PRIMARY KEY (run_id, step)
94
+ );
95
+ CREATE TABLE IF NOT EXISTS memory (
96
+ mem_id TEXT PRIMARY KEY, run_id TEXT, tier TEXT, kind TEXT,
97
+ text TEXT, importance REAL, created_at REAL, last_access REAL,
98
+ embedding TEXT
99
+ );
100
+ CREATE TABLE IF NOT EXISTS skills (
101
+ skill_id TEXT PRIMARY KEY, name TEXT, recipe TEXT,
102
+ receipt_hash TEXT, receipt_verified INTEGER, embedding TEXT,
103
+ created_at TEXT, uses INTEGER
104
+ );
105
+ CREATE TABLE IF NOT EXISTS reflections (
106
+ run_id TEXT, idx INTEGER, text TEXT, ts TEXT,
107
+ PRIMARY KEY (run_id, idx)
108
+ );
109
+ """
110
+ )
111
+
112
+
113
+ # ---------------------------------------------------------------------------
114
+ # LOCAL embedding — deterministic hashing-trick bag-of-tokens, L2 normalised.
115
+ # This is NOT a learned model; it is a reproducible local feature hash so that
116
+ # cosine similarity is meaningful for lexical overlap WITHOUT any network call.
117
+ # Labelled HEURISTIC everywhere it surfaces.
118
+ # ---------------------------------------------------------------------------
119
+ _TOK = re.compile(r"[a-z0-9]+")
120
+
121
+
122
+ def _embed(text: str) -> list:
123
+ vec = [0.0] * _EMBED_DIM
124
+ toks = _TOK.findall((text or "").lower())
125
+ for t in toks:
126
+ h = int(hashlib.md5(t.encode()).hexdigest(), 16)
127
+ idx = h % _EMBED_DIM
128
+ sign = 1.0 if (h >> 8) & 1 else -1.0
129
+ vec[idx] += sign
130
+ norm = math.sqrt(sum(v * v for v in vec)) or 1.0
131
+ return [v / norm for v in vec]
132
+
133
+
134
+ def _cos(a: list, b: list) -> float:
135
+ if not a or not b or len(a) != len(b):
136
+ return 0.0
137
+ return max(-1.0, min(1.0, sum(x * y for x, y in zip(a, b))))
138
+
139
+
140
+ def _importance_heuristic(text: str) -> float:
141
+ """Local importance proxy in [0,1] (LLM-scored 1-10 in the paper; here a
142
+ transparent HEURISTIC: longer, decision/goal-bearing text scores higher).
143
+ Surfaced honestly as HEURISTIC, never claimed as an LLM judgement."""
144
+ t = (text or "").lower()
145
+ score = min(1.0, len(t) / 240.0)
146
+ for kw, w in (("goal", 0.2), ("decision", 0.2), ("fail", 0.25),
147
+ ("error", 0.25), ("reflect", 0.2), ("verified", 0.15),
148
+ ("receipt", 0.1)):
149
+ if kw in t:
150
+ score = min(1.0, score + w)
151
+ return round(score, 4)
152
+
153
+
154
+ # ---------------------------------------------------------------------------
155
+ # Generative-Agents retrieval score:
156
+ # score(m) = a_rec * gamma^dt_hours + a_imp * imp(m) + a_rel * cos(q, m)
157
+ # gamma ~ 0.995 / hour (arXiv 2304.03442). Components surfaced honestly.
158
+ # ---------------------------------------------------------------------------
159
+ _GAMMA = 0.995 # recency decay per hour
160
+ _A_REC = 1.0
161
+ _A_IMP = 1.0
162
+ _A_REL = 1.0
163
+
164
+
165
+ def _score_memory(row, q_emb: list, now_epoch: float) -> dict:
166
+ dt_hours = max(0.0, (now_epoch - float(row["last_access"])) / 3600.0)
167
+ recency = _GAMMA ** dt_hours
168
+ imp = float(row["importance"])
169
+ try:
170
+ emb = json.loads(row["embedding"])
171
+ except Exception:
172
+ emb = []
173
+ rel = _cos(q_emb, emb)
174
+ rel01 = (rel + 1.0) / 2.0 # map cosine [-1,1] -> [0,1] for the weighted sum
175
+ total = _A_REC * recency + _A_IMP * imp + _A_REL * rel01
176
+ return {
177
+ "mem_id": row["mem_id"], "tier": row["tier"], "kind": row["kind"],
178
+ "text": row["text"],
179
+ "score": round(total, 6),
180
+ "components": {
181
+ "recency_gamma_dt": round(recency, 6),
182
+ "delta_t_hours": round(dt_hours, 4),
183
+ "importance": round(imp, 4),
184
+ "relevance_cos": round(rel, 6),
185
+ "relevance_0_1": round(rel01, 6),
186
+ },
187
+ "weights": {"alpha_recency": _A_REC, "alpha_importance": _A_IMP,
188
+ "alpha_relevance": _A_REL, "gamma_per_hour": _GAMMA},
189
+ "label": "HEURISTIC", # local embeddings + heuristic importance
190
+ }
191
+
192
+
193
+ def _mem_add(run_id: str, tier: str, kind: str, text: str,
194
+ importance=None) -> str:
195
+ mem_id = "mem_" + uuid.uuid4().hex[:12]
196
+ now = _now_epoch()
197
+ imp = _importance_heuristic(text) if importance is None else float(importance)
198
+ emb = _embed(text)
199
+ with _LOCK, _conn() as c:
200
+ c.execute(
201
+ "INSERT INTO memory(mem_id,run_id,tier,kind,text,importance,"
202
+ "created_at,last_access,embedding) VALUES(?,?,?,?,?,?,?,?,?)",
203
+ (mem_id, run_id, tier, kind, text, imp, now, now, json.dumps(emb)),
204
+ )
205
+ return mem_id
206
+
207
+
208
+ def _mem_retrieve(query: str, top_k: int = 5, tier=None) -> list:
209
+ q_emb = _embed(query)
210
+ now = _now_epoch()
211
+ with _LOCK, _conn() as c:
212
+ if tier:
213
+ rows = c.execute("SELECT * FROM memory WHERE tier=?", (tier,)).fetchall()
214
+ else:
215
+ rows = c.execute("SELECT * FROM memory").fetchall()
216
+ scored = [_score_memory(r, q_emb, now) for r in rows]
217
+ scored.sort(key=lambda s: s["score"], reverse=True)
218
+ top = scored[:top_k]
219
+ # honest "access" bump: retrieved memories refresh their recency clock
220
+ if top:
221
+ ids = [s["mem_id"] for s in top]
222
+ with _LOCK, _conn() as c:
223
+ c.executemany("UPDATE memory SET last_access=? WHERE mem_id=?",
224
+ [(now, i) for i in ids])
225
+ return top
226
+
227
+
228
+ # ---------------------------------------------------------------------------
229
+ # Letta-style memory tiering. "working" = in-context (small, fast); "archival"
230
+ # = vector store (large, searched). The agent self-manages via tool calls
231
+ # memory_append / memory_search / memory_promote that the ReAct loop can emit.
232
+ # ---------------------------------------------------------------------------
233
+ _WORKING_CAP = 8 # in-context working-memory item cap (paging boundary)
234
+
235
+
236
+ def _working_snapshot(run_id: str) -> list:
237
+ with _LOCK, _conn() as c:
238
+ rows = c.execute(
239
+ "SELECT * FROM memory WHERE run_id=? AND tier='working' "
240
+ "ORDER BY last_access DESC LIMIT ?", (run_id, _WORKING_CAP)
241
+ ).fetchall()
242
+ return [{"mem_id": r["mem_id"], "kind": r["kind"], "text": r["text"],
243
+ "importance": r["importance"]} for r in rows]
244
+
245
+
246
+ def _page_out_if_full(run_id: str) -> list:
247
+ """Letta/MemGPT paging: when working memory exceeds the in-context cap, the
248
+ LEAST-recently-accessed working items are promoted (paged out) to archival
249
+ so the in-context window stays bounded. Returns the paged-out mem_ids."""
250
+ paged = []
251
+ with _LOCK, _conn() as c:
252
+ rows = c.execute(
253
+ "SELECT mem_id FROM memory WHERE run_id=? AND tier='working' "
254
+ "ORDER BY last_access ASC", (run_id,)
255
+ ).fetchall()
256
+ if len(rows) > _WORKING_CAP:
257
+ overflow = rows[: len(rows) - _WORKING_CAP]
258
+ for r in overflow:
259
+ c.execute("UPDATE memory SET tier='archival' WHERE mem_id=?",
260
+ (r["mem_id"],))
261
+ paged.append(r["mem_id"])
262
+ return paged
263
+
264
+
265
+ # ---------------------------------------------------------------------------
266
+ # Voyager skill library. A tool-recipe is ADMITTED to the library ONLY after a
267
+ # verified execution receipt (DSSE envelope verified by the host verify_fn).
268
+ # Recipes are indexed by local embedding so the agent can retrieve a relevant
269
+ # prior recipe. We NEVER admit a recipe whose receipt fails verification.
270
+ # ---------------------------------------------------------------------------
271
+ def _skill_admit(name: str, recipe: str, receipt_hash: str,
272
+ receipt_verified: bool) -> dict:
273
+ if not receipt_verified:
274
+ return {"admitted": False,
275
+ "reason": "REJECTED — execution receipt did not verify; Voyager "
276
+ "admission requires a passing signed receipt.",
277
+ "receipt_hash": receipt_hash}
278
+ skill_id = "skill_" + uuid.uuid4().hex[:12]
279
+ emb = _embed(name + " " + recipe)
280
+ with _LOCK, _conn() as c:
281
+ c.execute(
282
+ "INSERT INTO skills(skill_id,name,recipe,receipt_hash,"
283
+ "receipt_verified,embedding,created_at,uses) VALUES(?,?,?,?,?,?,?,0)",
284
+ (skill_id, name, recipe, receipt_hash, 1, json.dumps(emb), _now_iso()),
285
+ )
286
+ return {"admitted": True, "skill_id": skill_id, "name": name,
287
+ "receipt_hash": receipt_hash,
288
+ "reason": "ADMITTED — backed by a verified execution receipt."}
289
+
290
+
291
+ def _skill_search(query: str, top_k: int = 5) -> list:
292
+ q_emb = _embed(query)
293
+ with _LOCK, _conn() as c:
294
+ rows = c.execute("SELECT * FROM skills").fetchall()
295
+ out = []
296
+ for r in rows:
297
+ try:
298
+ emb = json.loads(r["embedding"])
299
+ except Exception:
300
+ emb = []
301
+ out.append({"skill_id": r["skill_id"], "name": r["name"],
302
+ "recipe": r["recipe"], "receipt_hash": r["receipt_hash"],
303
+ "receipt_verified": bool(r["receipt_verified"]),
304
+ "uses": r["uses"],
305
+ "similarity": round(_cos(q_emb, emb), 6), "label": "LIVE"})
306
+ out.sort(key=lambda s: s["similarity"], reverse=True)
307
+ return out[:top_k]
308
+
309
+
310
+ # ---------------------------------------------------------------------------
311
+ # ReAct execution graph (arXiv 2210.03629). Nodes: THOUGHT -> ACTION ->
312
+ # OBSERVATION, looping until a terminal ANSWER or max_steps. EACH node
313
+ # transition is committed as a hash-chained receipt AND wrapped in a DSSE
314
+ # envelope by the host signer (sign_fn). A SqliteSaver-style checkpoint is
315
+ # written after every node so a crash resumes from the last committed step.
316
+ #
317
+ # The "model call" is routed through a small, deterministic in-image policy
318
+ # (the host a11oy inference path is the production target; we keep the loop's
319
+ # model-calls inside the app and label the planner HEURISTIC so we never fake a
320
+ # model number — the GRAPH, RECEIPTS, CHECKPOINTING and RESUME are all REAL).
321
+ # ---------------------------------------------------------------------------
322
+
323
+ # Tool registry: small, real, deterministic tools the ReAct agent can call.
324
+ def _tool_calc(arg: str) -> str:
325
+ expr = re.sub(r"[^0-9+\-*/(). ]", "", arg or "")
326
+ if not expr.strip():
327
+ return "ERR: empty expression"
328
+ try:
329
+ # safe arithmetic only (chars already filtered); no names/builtins
330
+ return str(eval(expr, {"__builtins__": {}}, {})) # noqa: S307
331
+ except Exception as e:
332
+ return "ERR: %s" % type(e).__name__
333
+
334
+
335
+ def _tool_memory_search(arg: str) -> str:
336
+ hits = _mem_retrieve(arg, top_k=3)
337
+ if not hits:
338
+ return "no memories"
339
+ return " | ".join("%s(score=%.3f)" % (h["text"][:48], h["score"]) for h in hits)
340
+
341
+
342
+ def _tool_skill_search(arg: str) -> str:
343
+ hits = _skill_search(arg, top_k=3)
344
+ if not hits:
345
+ return "no skills"
346
+ return " | ".join("%s(sim=%.3f)" % (h["name"], h["similarity"]) for h in hits)
347
+
348
+
349
+ def _tool_echo(arg: str) -> str:
350
+ return (arg or "")[:200]
351
+
352
+
353
+ _TOOLS = {
354
+ "calc": _tool_calc,
355
+ "memory_search": _tool_memory_search,
356
+ "skill_search": _tool_skill_search,
357
+ "echo": _tool_echo,
358
+ }
359
+
360
+
361
+ def _plan_action(goal: str, scratch: list) -> dict:
362
+ """HEURISTIC planner (NOT a learned model — labelled HEURISTIC). Picks the
363
+ next ReAct action from the goal + scratchpad. Deterministic so the loop is
364
+ replayable and the demo is reproducible. The production target is the host
365
+ a11oy inference path; this keeps the agent loop's model-calls in-app."""
366
+ g = (goal or "").lower()
367
+ step = len(scratch)
368
+ # terminal: if we already produced an observation, answer.
369
+ last_obs = next((s for s in reversed(scratch) if s.get("node") == "OBSERVATION"), None)
370
+ if last_obs is not None:
371
+ return {"terminal": True,
372
+ "thought": "I have an observation; I can answer now.",
373
+ "answer": "Result: %s" % last_obs.get("observation", "")}
374
+ # arithmetic goal -> calc
375
+ if re.search(r"\d.*[+\-*/].*\d", g):
376
+ m = re.search(r"[-0-9+\-*/(). ]{3,}", goal)
377
+ arg = m.group(0).strip() if m else goal
378
+ return {"terminal": False, "tool": "calc", "tool_input": arg,
379
+ "thought": "This looks arithmetic; I will use the calc tool."}
380
+ if "memory" in g or "remember" in g or "recall" in g:
381
+ return {"terminal": False, "tool": "memory_search", "tool_input": goal,
382
+ "thought": "I should consult memory for this."}
383
+ if "skill" in g or "recipe" in g or "how do i" in g:
384
+ return {"terminal": False, "tool": "skill_search", "tool_input": goal,
385
+ "thought": "I should check the skill library."}
386
+ return {"terminal": False, "tool": "echo", "tool_input": goal,
387
+ "thought": "No specialised tool; I will restate and observe."}
388
+
389
+
390
+ class _ReActEngine:
391
+ """Holds the host signer/verifier and runs / resumes graphs."""
392
+
393
+ def __init__(self, sign_fn, verify_fn, pub_pem_fn, ns="a11oy"):
394
+ self.sign_fn = sign_fn
395
+ self.verify_fn = verify_fn
396
+ self.pub_pem_fn = pub_pem_fn
397
+ self.ns = ns
398
+ _init_db()
399
+
400
+ # ---- receipt boundary: chain + DSSE sign every node transition ----
401
+ def _commit_receipt(self, run_id, seq, node, body, prev_hash, reflection=None):
402
+ rec_core = {"seq": seq, "node": node, "body": body, "prev_hash": prev_hash}
403
+ h = _sha(rec_core)
404
+ payload = {
405
+ "run_id": run_id, "seq": seq, "node": node, "body": body,
406
+ "prev_hash": prev_hash, "hash": h, "issuer": self.ns,
407
+ "issued_at": _now_iso(),
408
+ "reflection": reflection, # Reflexion field on the receipt
409
+ "trust_status": "Conjecture 1 (advisory \u2014 NOT a proven oracle)",
410
+ }
411
+ try:
412
+ envelope = self.sign_fn(payload)
413
+ except Exception as e:
414
+ envelope = {"signed": False, "signatures": [],
415
+ "honesty": "UNSIGNED \u2014 signer raised %s" % type(e).__name__,
416
+ "payloadType": "application/vnd.szl.receipt+json"}
417
+ with _LOCK, _conn() as c:
418
+ c.execute(
419
+ "INSERT OR REPLACE INTO receipts(run_id,seq,node,body,prev_hash,"
420
+ "hash,envelope,ts) VALUES(?,?,?,?,?,?,?,?)",
421
+ (run_id, seq, node, json.dumps(body), prev_hash, h,
422
+ json.dumps(envelope), _now_iso()),
423
+ )
424
+ return h, envelope
425
+
426
+ # ---- SqliteSaver-style checkpoint after every node ----
427
+ def _checkpoint(self, run_id, step, state, prev_hash):
428
+ with _LOCK, _conn() as c:
429
+ c.execute(
430
+ "INSERT OR REPLACE INTO checkpoints(run_id,step,state,prev_hash,ts)"
431
+ " VALUES(?,?,?,?,?)",
432
+ (run_id, step, json.dumps(state), prev_hash, _now_iso()),
433
+ )
434
+
435
+ def _load_run(self, run_id):
436
+ with _LOCK, _conn() as c:
437
+ r = c.execute("SELECT * FROM runs WHERE run_id=?", (run_id,)).fetchone()
438
+ cps = c.execute(
439
+ "SELECT * FROM checkpoints WHERE run_id=? ORDER BY step DESC LIMIT 1",
440
+ (run_id,)).fetchone()
441
+ return r, cps
442
+
443
+ def _prior_reflection(self, goal):
444
+ """Reflexion: prepend the most relevant prior reflection on activation."""
445
+ with _LOCK, _conn() as c:
446
+ rows = c.execute(
447
+ "SELECT text FROM reflections ORDER BY rowid DESC LIMIT 8").fetchall()
448
+ if not rows:
449
+ return None
450
+ # pick the reflection most lexically relevant to this goal
451
+ q = _embed(goal)
452
+ best, best_sim = None, -2.0
453
+ for r in rows:
454
+ sim = _cos(q, _embed(r["text"]))
455
+ if sim > best_sim:
456
+ best, best_sim = r["text"], sim
457
+ return best
458
+
459
+ # ---- run a (possibly partial) graph from a starting step ----
460
+ def _drive(self, run_id, goal, max_steps, start_step, prev_hash,
461
+ scratch, reflection, kill_after=None):
462
+ node_seq = start_step
463
+ status = "running"
464
+ steps_done = 0
465
+ terminal_answer = None
466
+ while node_seq // 3 < max_steps:
467
+ phase = node_seq % 3
468
+ cur_step = node_seq // 3
469
+ if phase == 0: # THOUGHT
470
+ plan = _plan_action(goal, scratch)
471
+ scratch.append({"node": "THOUGHT", "step": cur_step,
472
+ "thought": plan["thought"], "plan": plan})
473
+ body = {"step": cur_step, "thought": plan["thought"],
474
+ "intended_tool": plan.get("tool"),
475
+ "terminal": plan.get("terminal", False)}
476
+ prev_hash, _ = self._commit_receipt(run_id, node_seq, "THOUGHT",
477
+ body, prev_hash, reflection)
478
+ if plan.get("terminal"):
479
+ terminal_answer = plan.get("answer")
480
+ status = "completed"
481
+ self._checkpoint(run_id, node_seq + 1,
482
+ {"scratch": scratch, "answer": terminal_answer},
483
+ prev_hash)
484
+ node_seq += 1
485
+ break
486
+ elif phase == 1: # ACTION
487
+ plan = scratch[-1]["plan"]
488
+ tool, arg = plan.get("tool", "echo"), plan.get("tool_input", "")
489
+ scratch.append({"node": "ACTION", "step": cur_step,
490
+ "tool": tool, "tool_input": arg})
491
+ body = {"step": cur_step, "tool": tool, "tool_input": arg}
492
+ prev_hash, _ = self._commit_receipt(run_id, node_seq, "ACTION",
493
+ body, prev_hash, reflection)
494
+ else: # OBSERVATION (execute the tool for real)
495
+ act = next(s for s in reversed(scratch) if s.get("node") == "ACTION")
496
+ fn = _TOOLS.get(act["tool"], _tool_echo)
497
+ obs = fn(act["tool_input"])
498
+ scratch.append({"node": "OBSERVATION", "step": cur_step,
499
+ "observation": obs})
500
+ # store the observation as a working memory (Letta tiering)
501
+ _mem_add(run_id, "working", "observation",
502
+ "step %d %s->%s" % (cur_step, act["tool"], obs))
503
+ _page_out_if_full(run_id)
504
+ body = {"step": cur_step, "tool": act["tool"], "observation": obs}
505
+ prev_hash, _ = self._commit_receipt(run_id, node_seq, "OBSERVATION",
506
+ body, prev_hash, reflection)
507
+ node_seq += 1
508
+ steps_done += 1
509
+ # CHECKPOINT after every node transition (SqliteSaver-style)
510
+ self._checkpoint(run_id, node_seq,
511
+ {"scratch": scratch, "node_seq": node_seq}, prev_hash)
512
+ # honest crash injection for the resumable demo: stop mid-run
513
+ if kill_after is not None and steps_done >= kill_after:
514
+ status = "interrupted"
515
+ break
516
+ else:
517
+ status = "completed" if terminal_answer else "max_steps"
518
+
519
+ final_hash = prev_hash
520
+ with _LOCK, _conn() as c:
521
+ c.execute(
522
+ "UPDATE runs SET status=?,step=?,prev_hash=?,final_hash=?,"
523
+ "updated_at=? WHERE run_id=?",
524
+ (status, node_seq, prev_hash, final_hash, _now_iso(), run_id))
525
+ return {"run_id": run_id, "status": status, "node_seq": node_seq,
526
+ "final_hash": final_hash, "answer": terminal_answer,
527
+ "scratch": scratch}
528
+
529
+ def run(self, goal, max_steps=4, kill_after=None):
530
+ run_id = "run_" + uuid.uuid4().hex[:12]
531
+ reflection = self._prior_reflection(goal)
532
+ # seed long-term memory with the goal
533
+ _mem_add(run_id, "working", "goal", "GOAL: " + (goal or ""))
534
+ with _LOCK, _conn() as c:
535
+ c.execute(
536
+ "INSERT INTO runs(run_id,goal,status,max_steps,step,prev_hash,"
537
+ "final_hash,reflection,created_at,updated_at) "
538
+ "VALUES(?,?,?,?,?,?,?,?,?,?)",
539
+ (run_id, goal, "running", max_steps, 0, "GENESIS", "",
540
+ reflection or "", _now_iso(), _now_iso()))
541
+ # genesis receipt
542
+ prev_hash = "GENESIS"
543
+ prev_hash, _ = self._commit_receipt(
544
+ run_id, -1, "GENESIS",
545
+ {"goal": goal, "max_steps": max_steps,
546
+ "prior_reflection_prepended": bool(reflection)},
547
+ prev_hash, reflection)
548
+ self._checkpoint(run_id, 0, {"scratch": [], "node_seq": 0}, prev_hash)
549
+ return self._drive(run_id, goal, max_steps, 0, prev_hash, [],
550
+ reflection, kill_after=kill_after)
551
+
552
+ def resume(self, run_id):
553
+ r, cps = self._load_run(run_id)
554
+ if r is None:
555
+ return {"error": "unknown run_id", "run_id": run_id}
556
+ if r["status"] not in ("interrupted", "running", "max_steps"):
557
+ return {"run_id": run_id, "status": r["status"],
558
+ "note": "run already %s \u2014 nothing to resume" % r["status"],
559
+ "resumed": False}
560
+ state = json.loads(cps["state"]) if cps else {"scratch": [], "node_seq": 0}
561
+ node_seq = state.get("node_seq", 0)
562
+ scratch = state.get("scratch", [])
563
+ prev_hash = cps["prev_hash"] if cps else "GENESIS"
564
+ out = self._drive(run_id, r["goal"], r["max_steps"], node_seq, prev_hash,
565
+ scratch, r["reflection"] or None)
566
+ out["resumed"] = True
567
+ out["resumed_from_checkpoint_step"] = node_seq
568
+ return out
569
+
570
+ def trace(self, run_id):
571
+ with _LOCK, _conn() as c:
572
+ r = c.execute("SELECT * FROM runs WHERE run_id=?", (run_id,)).fetchone()
573
+ recs = c.execute(
574
+ "SELECT * FROM receipts WHERE run_id=? ORDER BY seq", (run_id,)
575
+ ).fetchall()
576
+ if r is None:
577
+ return {"error": "unknown run_id", "run_id": run_id}
578
+ receipts, chain_ok, prev = [], True, "GENESIS"
579
+ for rec in recs:
580
+ body = json.loads(rec["body"])
581
+ recompute = _sha({"seq": rec["seq"], "node": rec["node"],
582
+ "body": body, "prev_hash": rec["prev_hash"]})
583
+ link_ok = (rec["prev_hash"] == prev) and (recompute == rec["hash"])
584
+ env = json.loads(rec["envelope"])
585
+ sig_ok = None
586
+ if self.verify_fn is not None:
587
+ try:
588
+ sig_ok = bool(self.verify_fn(env).get("signature_valid"))
589
+ except Exception:
590
+ sig_ok = False
591
+ chain_ok = chain_ok and link_ok
592
+ receipts.append({"seq": rec["seq"], "node": rec["node"], "body": body,
593
+ "hash": rec["hash"], "prev_hash": rec["prev_hash"],
594
+ "link_ok": link_ok, "signature_valid": sig_ok,
595
+ "signed": bool(env.get("signed")),
596
+ "ts": rec["ts"]})
597
+ prev = rec["hash"]
598
+ return {"run_id": run_id, "goal": r["goal"], "status": r["status"],
599
+ "reflection": r["reflection"],
600
+ "chain_intact": chain_ok, "depth": len(receipts),
601
+ "final_hash": r["final_hash"], "receipts": receipts,
602
+ "trust_note": "Receipt chain + DSSE signatures are REAL; planner is "
603
+ "HEURISTIC (deterministic, replayable). Trust=Conjecture 1."}
604
+
605
+ def checkpoints(self, run_id):
606
+ with _LOCK, _conn() as c:
607
+ cps = c.execute(
608
+ "SELECT step,prev_hash,ts FROM checkpoints WHERE run_id=? "
609
+ "ORDER BY step", (run_id,)).fetchall()
610
+ r = c.execute("SELECT status,step FROM runs WHERE run_id=?",
611
+ (run_id,)).fetchone()
612
+ return {"run_id": run_id,
613
+ "status": (r["status"] if r else "unknown"),
614
+ "current_step": (r["step"] if r else None),
615
+ "checkpoints": [{"step": c["step"], "prev_hash": c["prev_hash"],
616
+ "ts": c["ts"]} for c in cps],
617
+ "saver": "SqliteSaver-style (local sqlite, ephemeral per container)"}
618
+
619
+ def reflect(self, run_id, reflection_text):
620
+ """Reflexion: store a NL reflection after a reviewed episode."""
621
+ with _LOCK, _conn() as c:
622
+ n = c.execute("SELECT COUNT(*) AS n FROM reflections WHERE run_id=?",
623
+ (run_id,)).fetchone()["n"]
624
+ c.execute("INSERT OR REPLACE INTO reflections(run_id,idx,text,ts) "
625
+ "VALUES(?,?,?,?)", (run_id, n, reflection_text, _now_iso()))
626
+ c.execute("UPDATE runs SET reflection=? WHERE run_id=?",
627
+ (reflection_text, run_id))
628
+ _mem_add(run_id, "archival", "reflection", reflection_text, importance=0.85)
629
+ return {"run_id": run_id, "stored": True, "reflection": reflection_text,
630
+ "note": "Prepended to the next activation on a lexically-relevant goal."}
631
+
632
+
633
+ # ---------------------------------------------------------------------------
634
+ # register(app, ns, sign_fn, verify_fn, pub_pem_fn) — mirrors szl_agentic_loop.
635
+ # Routes inserted at position 0 (Starlette Route) so they beat the SPA catch-all.
636
+ # FREE sub-namespace /api/a11oy/v1/agent/react/* to avoid collisions with the
637
+ # existing /run, /tools, /verify-chain, /governance-standards, /_diag, /loop.
638
+ # ---------------------------------------------------------------------------
639
+ def register(app, ns: str = "a11oy", sign_fn=None, verify_fn=None,
640
+ pub_pem_fn=None, signer_label: str = "in-image key"):
641
+ from starlette.routing import Route
642
+ from starlette.responses import JSONResponse
643
+
644
+ _init_db()
645
+ eng = _ReActEngine(sign_fn, verify_fn, pub_pem_fn, ns=ns)
646
+
647
+ async def _read_json(request):
648
+ try:
649
+ return await request.json()
650
+ except Exception:
651
+ return {}
652
+
653
+ async def _run(request):
654
+ d = await _read_json(request)
655
+ goal = (d.get("goal") or d.get("query") or "").strip()
656
+ if not goal:
657
+ return JSONResponse({"error": "missing 'goal'"}, status_code=400)
658
+ max_steps = int(d.get("max_steps", 4))
659
+ kill_after = d.get("kill_after") # honest crash-injection for the demo
660
+ kill_after = int(kill_after) if kill_after is not None else None
661
+ out = eng.run(goal, max_steps=max_steps, kill_after=kill_after)
662
+ out["label"] = "EXPERIMENTAL"
663
+ return JSONResponse(out)
664
+
665
+ async def _resume(request):
666
+ d = await _read_json(request)
667
+ run_id = (d.get("run_id") or request.query_params.get("run_id") or "").strip()
668
+ if not run_id:
669
+ return JSONResponse({"error": "missing 'run_id'"}, status_code=400)
670
+ out = eng.resume(run_id)
671
+ out["label"] = "EXPERIMENTAL"
672
+ return JSONResponse(out)
673
+
674
+ async def _trace(request):
675
+ run_id = request.path_params.get("run_id") or request.query_params.get("run_id", "")
676
+ return JSONResponse(eng.trace(run_id))
677
+
678
+ async def _checkpoints(request):
679
+ run_id = request.path_params.get("run_id") or request.query_params.get("run_id", "")
680
+ return JSONResponse(eng.checkpoints(run_id))
681
+
682
+ async def _reflect(request):
683
+ d = await _read_json(request)
684
+ run_id = (d.get("run_id") or "").strip()
685
+ text = (d.get("reflection") or d.get("text") or "").strip()
686
+ if not run_id or not text:
687
+ return JSONResponse({"error": "need run_id + reflection"}, status_code=400)
688
+ return JSONResponse(eng.reflect(run_id, text))
689
+
690
+ async def _mem_add_ep(request):
691
+ d = await _read_json(request)
692
+ text = (d.get("text") or "").strip()
693
+ if not text:
694
+ return JSONResponse({"error": "missing 'text'"}, status_code=400)
695
+ mid = _mem_add(d.get("run_id", "adhoc"), d.get("tier", "archival"),
696
+ d.get("kind", "note"), text, d.get("importance"))
697
+ return JSONResponse({"mem_id": mid, "tier": d.get("tier", "archival"),
698
+ "label": "HEURISTIC"})
699
+
700
+ async def _mem_search_ep(request):
701
+ d = await _read_json(request)
702
+ q = (d.get("query") or "").strip()
703
+ if not q:
704
+ return JSONResponse({"error": "missing 'query'"}, status_code=400)
705
+ hits = _mem_retrieve(q, top_k=int(d.get("top_k", 5)), tier=d.get("tier"))
706
+ return JSONResponse({"query": q, "results": hits, "label": "HEURISTIC",
707
+ "formula": "score(m)=a_rec*g^dt + a_imp*imp(m) + a_rel*cos(q,m)",
708
+ "source": "Generative Agents (arXiv 2304.03442)"})
709
+
710
+ async def _mem_tiers(request):
711
+ run_id = request.query_params.get("run_id", "")
712
+ with _LOCK, _conn() as c:
713
+ wq = ("SELECT tier,COUNT(*) AS n FROM memory" +
714
+ (" WHERE run_id=?" if run_id else "") + " GROUP BY tier")
715
+ rows = c.execute(wq, ((run_id,) if run_id else ())).fetchall()
716
+ tiers = {r["tier"]: r["n"] for r in rows}
717
+ return JSONResponse({"run_id": run_id or None, "tiers": tiers,
718
+ "working": _working_snapshot(run_id) if run_id else [],
719
+ "working_cap": _WORKING_CAP,
720
+ "design": "Letta/MemGPT (arXiv 2310.08560): working "
721
+ "(in-context) + archival (vector); self-managed.",
722
+ "label": "EXPERIMENTAL"})
723
+
724
+ async def _skill_admit_ep(request):
725
+ d = await _read_json(request)
726
+ name = (d.get("name") or "").strip()
727
+ recipe = (d.get("recipe") or "").strip()
728
+ run_id = (d.get("run_id") or "").strip()
729
+ if not name or not recipe:
730
+ return JSONResponse({"error": "need name + recipe"}, status_code=400)
731
+ # Voyager admission: require a VERIFIED execution receipt. We accept a
732
+ # run_id and verify its final emit receipt; OR a direct envelope.
733
+ verified, rhash = False, ""
734
+ if run_id:
735
+ tr = eng.trace(run_id)
736
+ recs = tr.get("receipts", [])
737
+ if recs:
738
+ last = recs[-1]
739
+ rhash = last["hash"]
740
+ verified = bool(last.get("signature_valid")) and tr.get("chain_intact")
741
+ elif d.get("envelope") and verify_fn is not None:
742
+ try:
743
+ verified = bool(verify_fn(d["envelope"]).get("signature_valid"))
744
+ rhash = _sha(d["envelope"])[:32]
745
+ except Exception:
746
+ verified = False
747
+ res = _skill_admit(name, recipe, rhash, verified)
748
+ res["label"] = "LIVE" if res.get("admitted") else "EXPERIMENTAL"
749
+ return JSONResponse(res, status_code=200 if res.get("admitted") else 422)
750
+
751
+ async def _skill_list(request):
752
+ q = request.query_params.get("q", "")
753
+ return JSONResponse({"query": q or None,
754
+ "skills": _skill_search(q or "skill", top_k=50),
755
+ "admission_rule": "Voyager (arXiv 2305.16291): admit a "
756
+ "recipe ONLY after a verified execution receipt.",
757
+ "label": "LIVE"})
758
+
759
+ async def _diag(request):
760
+ with _LOCK, _conn() as c:
761
+ nr = c.execute("SELECT COUNT(*) AS n FROM runs").fetchone()["n"]
762
+ nm = c.execute("SELECT COUNT(*) AS n FROM memory").fetchone()["n"]
763
+ ns_ = c.execute("SELECT COUNT(*) AS n FROM skills").fetchone()["n"]
764
+ return JSONResponse({
765
+ "module": "a11oy_react_core", "status": "ok",
766
+ "db": _DB_PATH, "runs": nr, "memories": nm, "skills": ns_,
767
+ "signer": signer_label,
768
+ "pubkey_present": bool((pub_pem_fn() if pub_pem_fn else "")),
769
+ "subsystems": ["ReAct graph (2210.03629)", "SqliteSaver checkpointing",
770
+ "Reflexion (2303.11366)", "Generative-Agents memory (2304.03442)",
771
+ "Letta tiering (2310.08560)", "Voyager skill library (2305.16291)"],
772
+ "label": "EXPERIMENTAL"})
773
+
774
+ base = "/api/%s/v1/agent/react" % ns
775
+ routes = [
776
+ Route(base + "/run", _run, methods=["POST"], name="%s_react_run" % ns),
777
+ Route(base + "/resume", _resume, methods=["POST"], name="%s_react_resume" % ns),
778
+ Route(base + "/trace/{run_id}", _trace, methods=["GET"], name="%s_react_trace" % ns),
779
+ Route(base + "/trace", _trace, methods=["GET"], name="%s_react_trace_q" % ns),
780
+ Route(base + "/checkpoints/{run_id}", _checkpoints, methods=["GET"],
781
+ name="%s_react_cps" % ns),
782
+ Route(base + "/checkpoints", _checkpoints, methods=["GET"], name="%s_react_cps_q" % ns),
783
+ Route(base + "/reflect", _reflect, methods=["POST"], name="%s_react_reflect" % ns),
784
+ Route(base + "/memory/add", _mem_add_ep, methods=["POST"], name="%s_react_mem_add" % ns),
785
+ Route(base + "/memory/search", _mem_search_ep, methods=["POST"],
786
+ name="%s_react_mem_search" % ns),
787
+ Route(base + "/memory/tiers", _mem_tiers, methods=["GET"], name="%s_react_mem_tiers" % ns),
788
+ Route(base + "/skills/admit", _skill_admit_ep, methods=["POST"],
789
+ name="%s_react_skill_admit" % ns),
790
+ Route(base + "/skills", _skill_list, methods=["GET"], name="%s_react_skills" % ns),
791
+ Route(base + "/_diag", _diag, methods=["GET"], name="%s_react_diag" % ns),
792
+ # Free top-level conveniences requested by the spec (not taken elsewhere):
793
+ Route("/api/%s/v1/agent/resume" % ns, _resume, methods=["POST"],
794
+ name="%s_agent_resume_top" % ns),
795
+ Route("/api/%s/v1/agent/trace/{run_id}" % ns, _trace, methods=["GET"],
796
+ name="%s_agent_trace_top" % ns),
797
+ Route("/api/%s/v1/agent/checkpoints/{run_id}" % ns, _checkpoints, methods=["GET"],
798
+ name="%s_agent_cps_top" % ns),
799
+ Route("/api/%s/v1/agent/checkpoints" % ns, _checkpoints, methods=["GET"],
800
+ name="%s_agent_cps_top_q" % ns),
801
+ ]
802
+ for r in routes:
803
+ app.router.routes.insert(0, r)
804
+ return {"module": "a11oy_react_core", "routes": len(routes), "base": base,
805
+ "signer": signer_label}
serve.py CHANGED
@@ -7667,6 +7667,46 @@ except Exception as _loop_e:
7667
  # ============================================================================
7668
 
7669
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7670
  # ============================================================================
7671
  # BEGIN: FORMULA-WIRING SURFACE — a11oy (2026-06-06, ADDITIVE, surgical)
7672
  # Wires ALL ~80 kernel-verified theorems to REAL, executed mechanisms (shared,
 
7667
  # ============================================================================
7668
 
7669
 
7670
+ # ============================================================================
7671
+ # BEGIN: LANE A AGENTIC CORE — a11oy (2026-06-14, Dev A, ADDITIVE, surgical)
7672
+ # Resumable ReAct execution graph (Thought->Action->Observation) where EACH
7673
+ # node transition is a SIGNED receipt boundary, with SqliteSaver-style
7674
+ # checkpointing (crash mid-run -> /resume continues), a Reflexion inner loop,
7675
+ # Generative-Agents memory retrieval scoring over a LOCAL vector store (0 CDN),
7676
+ # Letta-style working/archival tiering, and a Voyager skill library that admits
7677
+ # a tool-recipe ONLY after a verified execution receipt.
7678
+ # REUSES the host's REAL in-image signer (_a11oy_sign_receipt), verifier
7679
+ # (_a11oy_loop_verify) and public key (_a11oy_loop_pubpem). Routes inserted at
7680
+ # position 0 (Starlette Route) so they beat the SPA catch-all. FREE sub-namespace
7681
+ # /api/a11oy/v1/agent/react/* — no collision with /run, /tools, /verify-chain,
7682
+ # /governance-standards, /_diag, /loop. try/except-guarded (non-fatal).
7683
+ # Signed-off-by: Stephen P. Lutar Jr. <stephenlutar2@gmail.com>
7684
+ # Co-Authored-By: Perplexity Computer Agent <agent@perplexity.ai>
7685
+ # ============================================================================
7686
+ try:
7687
+ import a11oy_react_core as _react_core
7688
+ import sys as _react_sys
7689
+ _react_status = _react_core.register(
7690
+ app, "a11oy",
7691
+ sign_fn=_a11oy_sign_receipt,
7692
+ verify_fn=(_a11oy_loop_verify if "_a11oy_loop_verify" in dir() else None),
7693
+ pub_pem_fn=(_a11oy_loop_pubpem if "_a11oy_loop_pubpem" in dir() else None),
7694
+ signer_label=("in-image ephemeral ECDSA-P256 (signed at server boot, "
7695
+ "resets on rebuild, verifiable vs /cosign.pub)"),
7696
+ )
7697
+ print(f"[a11oy] LANE A agentic core registered: {_react_status}", file=_react_sys.stderr)
7698
+ _REACT_DIAG = {"status": "ok", "registered": _react_status}
7699
+ except Exception as _react_e:
7700
+ import sys as _react_sys, traceback as _react_tb
7701
+ print(f"[a11oy] LANE A agentic core FAILED (non-fatal): {_react_e!r}", file=_react_sys.stderr)
7702
+ _react_tb.print_exc(file=_react_sys.stderr)
7703
+ _REACT_DIAG = {"status": "FAILED", "error": repr(_react_e),
7704
+ "traceback": _react_tb.format_exc()}
7705
+ # ============================================================================
7706
+ # END: LANE A AGENTIC CORE — a11oy
7707
+ # ============================================================================
7708
+
7709
+
7710
  # ============================================================================
7711
  # BEGIN: FORMULA-WIRING SURFACE — a11oy (2026-06-06, ADDITIVE, surgical)
7712
  # Wires ALL ~80 kernel-verified theorems to REAL, executed mechanisms (shared,