#!/usr/bin/env python3 """avalosai — the Avalos AI console: sovereign assistant + ensemble-verified coder, in your terminal. avalosai open the interactive console (talk to it, or code) avalosai login avl_live_... save your key (~/.avalos/config.json) avalosai "" one-shot: fix/edit a file (verified before applying) avalosai doctor check connection + key avalosai agent "" the Coder Engine works in this folder (asks before every change) avalosai agent --yes [--yes-shell] [--json] [--budget N] "" headless: --yes approves edits/writes inside this folder (and the plan), --yes-shell also approves shell commands, --json prints ONE JSON report on stdout (progress goes to stderr), --budget N stops at N tokens avalosai remember "" add a line to this folder's .avalos/MEMORY.md (loaded into every task) avalosai delete [all] [--yes] delete this conversation, or ALL history (server + this PC; `all` asks y/N) avalosai --aura Aura hands: take spoken tasks from Aura on avalos.ai (owner only) avalosai aura allow|disallow enable/disable THIS folder for Aura tasks avalosai remote off|on kill switch for all remote (Aura) tasks on this PC In the console: /effort low|medium|high|max spend level (quick=T1 star=T2 galaxy=T3 quantum=T5) /mode sota|harness coder verification depth (sota = 2-pass critic; harness = cheap, unverified) /research [| item ...] COAGULA: east+west frontier research, quote-verified, homegrown concept per item /probatio on|off build a new file by trial: drafts + spec-only testers in the Avalos sandbox (default off) Just type. The Coder Engine answers, and when the task needs it, it uses its hands on THIS folder: reads, edits, runs commands on your machine. Every edit/command shows a diff and asks y/n (a = allow in-folder edits). The conversation is kept on this PC only. /new start a fresh conversation /delete delete this conversation: its session on the server and its copy on this PC /delete all delete ALL history: every session on the server + the local history files (asks y/N first; your key, settings and .avalos/MEMORY.md are kept) /remember remember a fact or preference for THIS folder (.avalos/MEMORY.md) /auto on|off skip the y/n for file edits inside this folder; shell commands and anything outside the folder always ask /aura on|off|allow|disallow serve spoken tasks from Aura here (Ctrl+C stops); tasks from Aura NEVER auto-approve: every edit and command asks here /remote off|on kill switch for remote (Aura) tasks Install: irm https://avalos.ai/install.ps1 | iex (registers the `avalosai` command). Env: AVALOS_API_KEY, AVALOS_BASE (default https://avalos.ai).""" import sys, os, json, time, threading, random, urllib.request, urllib.error, urllib.parse, socket import http.client from pathlib import Path __version__ = "1.4.2" GOLD = "#FFD600" COBALT = "#6f93ef" NAVY = "#0d2b5e" CFG = Path.home() / ".avalos" / "config.json" try: from rich.console import Console from rich.panel import Panel from rich.markdown import Markdown from rich.text import Text from rich import box _con = Console() _RICH = True except Exception: _RICH = False _con = None try: from prompt_toolkit import PromptSession from prompt_toolkit.history import FileHistory from prompt_toolkit.formatted_text import FormattedText from prompt_toolkit.styles import Style as _PTStyle _ptk_style = _PTStyle.from_dict({ "bolt": "bold " + GOLD, "name": "bold " + COBALT, "bar": "bold " + GOLD, "frame": COBALT, "bottom-toolbar": "bg:" + GOLD, "tb": "bold #0d2b5e bg:" + GOLD, "tbsep": "#0d2b5e bg:" + GOLD, "tbdim": "#2b3d66 bg:" + GOLD, }) _PTK = True except Exception: _PTK = False _ptk_style = None _ptk = None _CUR_SID = None def _p(msg="", style=None): if _RICH: _con.print(msg, style=style) else: print(msg) def _cfg(): try: return json.loads(CFG.read_text()) except Exception: return {} def _save(d): CFG.parent.mkdir(parents=True, exist_ok=True) CFG.write_text(json.dumps(d, indent=2)) def _base(): return (os.environ.get("AVALOS_BASE") or _cfg().get("base") or "https://avalos.ai").rstrip("/") EFFORT = { "low": ("quick", "T1", "cheapest; 1 sample, no verify passes"), "medium": ("star", "T2", "default — balanced"), "high": ("galaxy", "T3", "more samples + verification"), "max": ("quantum", "T5", "top rung; deepest answers, most tokens"), } _EFFORT_ALIAS = {} for _k, (_srv, _t, _d) in EFFORT.items(): _EFFORT_ALIAS[_k] = _k _EFFORT_ALIAS[_srv] = _k _EFFORT_ALIAS[_t.lower()] = _k _EFFORT_ALIAS.update({"cosmos": "high", "t4": "high", "med": "medium", "maximum": "max", "lo": "low", "hi": "high"}) def _effort(): lvl = _EFFORT_ALIAS.get(str(_cfg().get("effort") or "").lower().strip(), "medium") srv, tier, _ = EFFORT[lvl] return lvl, srv, tier def _coder_mode(): m = str(_cfg().get("coder_mode") or "sota").lower().strip() return m if m in ("sota", "harness") else "sota" def _probatio_on(): env = os.environ.get("AVALOS_PROBATIO", "").strip().lower() if env in ("0", "off", "false"): return False return env in ("1", "on", "true") or bool(_cfg().get("probatio")) def set_probatio(arg): a = str(arg or "").lower().strip() if a not in ("on", "off"): _p(" probatio: %s" % ("on" if _probatio_on() else "off (default)"), style=GOLD) _p(" on a new file from a spec is built by trial: several drafts, testers that see only the spec,", style="dim") _p(" all run in the Avalos isolated sandbox (a copy of your code files, no network); the best", style="dim") _p(" draft is kept. Slower (~3-5 min) and uses more brain calls. Nothing runs on this machine.", style="dim") _p(" off the normal Coder Engine loop (default)", style="dim") _p(" usage: /probatio on|off", style="dim") return c = _cfg(); c["probatio"] = (a == "on"); _save(c) _p(" ✓ probatio %s" % a, style="green") def set_effort(arg): lvl_in = str(arg or "").lower().strip() if not lvl_in: cur, srv, tier = _effort() _p(" effort: %s (%s / %s)" % (cur, srv, tier), style=GOLD) for k in ("low", "medium", "high", "max"): s, t, d = EFFORT[k] mark = "→ " if k == cur else " " _p(" %s%-7s %-8s %-3s %s" % (mark, k, s, t, d), style=(GOLD if k == cur else "dim")) _p(" usage: /effort low|medium|high|max (also accepts quick/star/galaxy/quantum, T1..T5)", style="dim") _p(" coder mode: %s — /mode sota|harness (this is what cuts CODER token spend today)" % _coder_mode(), style="dim") return lvl = _EFFORT_ALIAS.get(lvl_in) if not lvl: _p(" unknown effort %r — try low, medium, high or max" % arg, style="yellow"); return c = _cfg(); c["effort"] = lvl; _save(c) srv, tier, desc = EFFORT[lvl] _p(" ✓ effort %s (%s / %s) — %s" % (lvl, srv, tier, desc), style="green") def set_coder_mode(arg): m = str(arg or "").lower().strip() if not m: _p(" coder mode: %s" % _coder_mode(), style=GOLD) _p(" sota gen_test + behavioural test + mutant check + 2-pass critic (default)", style="dim") _p(" harness ast.parse only — far cheaper, and NOT verified", style="dim") return if m not in ("sota", "harness"): _p(" unknown mode %r — sota or harness" % arg, style="yellow"); return c = _cfg(); c["coder_mode"] = m; _save(c) _p(" ✓ coder mode %s" % m, style="green") if m == "harness": _p(" harness skips the critic and the behavioural test: cheap, not verified.", style="yellow") def _key(): return os.environ.get("AVALOS_API_KEY") or _cfg().get("api_key") or "" _SESSION_WARNED = False def _session_token(): global _SESSION_WARNED c = _cfg() tok = os.environ.get("AVALOS_SESSION_TOKEN") or c.get("session_token") or "" exp = float(c.get("session_expires") or 0) if tok and exp and exp < time.time(): if not _SESSION_WARNED: _SESSION_WARNED = True print(" operator session EXPIRED -- run: python3 avalos_auth.py login", file=sys.stderr) print(" continuing on the paid tier; file and shell tools stay unavailable.", file=sys.stderr) return "" return tok def _hdrs(has_data): hdrs = {"X-Avalos-Key": _key()} _tok = _session_token() if _tok: hdrs["Authorization"] = "Bearer " + _tok if has_data: hdrs["Content-Type"] = "application/json" return hdrs def _http(path, body=None, timeout=45): data = json.dumps(body).encode() if body is not None else None r = urllib.request.Request(_base() + path, data=data, headers=_hdrs(bool(data))) try: with urllib.request.urlopen(r, timeout=timeout) as resp: raw = resp.read().decode("utf-8", "replace") return resp.status, (json.loads(raw) if raw.strip() else {}) except urllib.error.HTTPError as e: try: return e.code, json.loads(e.read().decode("utf-8", "replace") or "{}") except Exception: return e.code, {} # ---- retry policy (industry standard: AWS / Google / Stripe client guidance) ---------------------------------------------- _RETRY_ATTEMPTS = 6 # 1 try + 5 retries def _retry_after_s(headers): try: return max(0.0, min(float(headers.get("Retry-After")), 60.0)) except (TypeError, ValueError, AttributeError): return None def _retry_wait(attempt, headers=None): """Exponential backoff, step capped at 16 s, EQUAL jitter (>= half a step): >= 23 s total over 5 retries, longer than a gateway restart; honors Retry-After.""" step = min(16.0, 2.0 * (2 ** attempt)) wait = step / 2 + random.uniform(0, step / 2) ra = _retry_after_s(headers) if headers is not None else None return max(wait, ra) if ra is not None else wait _SAFE_POST = ("/v1/chat/completions", "/chat", "/v1/vision") # no side effect until a finished answer is metered def _retry_safe(method, path): """True when re-sending cannot duplicate work: reads/deletes, and the completion endpoints. Other POSTs (coder runs, scheduled tasks, projects, jobs) are retried only on errors that prove the request never reached the server, or on 502/503/504.""" return method in ("GET", "HEAD", "DELETE", "PUT") or path.split("?")[0] in _SAFE_POST def _retry_status(code, headers=None, safe=True): if code in (502, 503, 504) or (code == 500 and safe): return True if code == 429: # short rate limits only; quota / trial limits go back to the caller ra = _retry_after_s(headers) if headers is not None else None return ra is not None and ra <= 30 return False def _retry_conn_error(e, safe=True): """Never reached the server (refused / DNS / CONNECT timeout): always retry. Reset / dropped after the send: the server may have started the work, so retry only when `safe`. A READ timeout is never retried (the server may still be working).""" r = getattr(e, "reason", e) if isinstance(r, (ConnectionRefusedError, socket.gaierror)): return True if isinstance(e, urllib.error.URLError) and isinstance(r, (socket.timeout, TimeoutError)): return True if isinstance(r, (ConnectionError, http.client.RemoteDisconnected, http.client.BadStatusLine)): return safe return False def _req(path, body=None): data = json.dumps(body).encode() if body is not None else None r = urllib.request.Request(_base() + path, data=data, headers=_hdrs(bool(data))) safe = _retry_safe("POST" if data is not None else "GET", path) for attempt in range(_RETRY_ATTEMPTS): try: with urllib.request.urlopen(r, timeout=600) as resp: return json.loads(resp.read().decode()) # HTTPError MUST be caught before URLError/OSError -- it is a subclass of both. except urllib.error.HTTPError as e: if attempt + 1 < _RETRY_ATTEMPTS and _retry_status(e.code, e.headers, safe): wait = _retry_wait(attempt, e.headers) print(f" {path}: HTTP {e.code}, retrying in {wait:.0f}s (attempt {attempt + 2}/{_RETRY_ATTEMPTS})... (stderr)", file=sys.stderr) time.sleep(wait) continue if e.code == 429 or 400 <= e.code < 500: raise # permanent / quota: surfaced unchanged raise RuntimeError("all %d attempts failed for %s: %s" % (attempt + 1, path, e)) from e except (OSError, http.client.HTTPException) as e: # URLError, ConnectionError, RemoteDisconnected, socket.timeout, IncompleteRead if attempt + 1 < _RETRY_ATTEMPTS and _retry_conn_error(e, safe): wait = _retry_wait(attempt) print(f" {path}: connection problem, retrying in {wait:.0f}s (attempt {attempt + 2}/{_RETRY_ATTEMPTS})... (stderr)", file=sys.stderr) time.sleep(wait) continue raise RuntimeError("request failed for %s (%s)%s" % (path, e, "" if _retry_conn_error(e, safe) else " [not retried: the server may still be working]")) from e _VERBS = ["Thinking", "Weaving", "Pondering", "Reasoning", "Composing", "Divining", "Reflecting", "Synthesizing", "Considering", "Kindling"] class _Working: def __init__(self, label=None): self.label = label self.stop = False self.t0 = time.time() self._tick = threading.Event() def __enter__(self): if not _RICH: sys.stdout.write(" …working "); sys.stdout.flush(); return self self.verb = self.label or random.choice(_VERBS) self._live_thread = threading.Thread(target=self._spin, daemon=True) self._live_thread.start() return self def _spin(self): from rich.live import Live shimmer = [GOLD, "#FFF0A6"] i = 0 with Live(console=_con, refresh_per_second=8, transient=True) as live: while not self.stop: el = int(time.time() - self.t0) c = shimmer[i % 2]; i += 1 t = Text.assemble(("⚡ ", "bold " + c), (self.verb + "… ", c), ("(%ds · esc to interrupt)" % el, "dim")) live.update(t) self._tick.wait(0.45) def __exit__(self, *a): self.stop = True try: self._tick.set() except Exception: pass if _RICH: t = getattr(self, "_live_thread", None) if t is not None: t.join(timeout=1.5) else: print() def _cols(s): import unicodedata n = 0 for ch in s: if unicodedata.combining(ch): continue n += 2 if unicodedata.east_asian_width(ch) in ("W", "F") else 1 return n def _frame_w(): import shutil as _sh try: c = _sh.get_terminal_size((80, 24)).columns except Exception: c = 80 return max(32, min(c - 2, 110)) def welcome(): if not _RICH: print("\n Avalos AI — sovereign assistant + coder (%s)" % _base()) print(" Talk to me, or edit code: file.py : add tests") print(" /help · /effort low|medium|high|max · /mode sota|harness · /login avl_live_... · /clear · /exit") print(" effort %s · coder mode %s\n" % (_effort()[0], _coder_mode())); return body = Text() body.append("A V A L O S A I", style="bold " + GOLD) body.append("\n") body.append("sovereign · ensemble-verified · self-hosted", style=COBALT) body.append("\n") body.append("─" * max(20, _frame_w() - 8) + "\n", style=NAVY) body.append("cwd ", style="dim"); body.append(os.getcwd() + "\n") body.append("server ", style="dim"); body.append(_base() + "\n") body.append("key ", style="dim"); body.append((_key()[:10] + "…") if _key() else "not set — /login avl_live_...", style=("" if _key() else "yellow")) _con.print(Panel(body, box=box.DOUBLE, border_style=COBALT, padding=(1, 3), title="[bold %s]⚡ avalos.ai[/]" % GOLD, title_align="left")) hint = Text() hint.append(" talk to me, or edit code: ", style="dim") hint.append("app.py : add input validation", style=COBALT) hint.append(" · /help /effort /mode /login /clear /exit", style="dim") _con.print(hint); _con.print() def render_reply(text): if _RICH: try: _con.print(Markdown(text)) except Exception: _con.print(text) _con.print() else: print(" " + (text or "").replace("\n", "\n ") + "\n") def login(key): key = key.strip().strip("<>\"'` ").strip() if not key.startswith("avl_live_"): _p(" that doesn't look like an Avalos key (they start with avl_live_) — not saved", style="yellow"); return c = _cfg(); c["api_key"] = key; _save(c) try: _req("/v1/chat/completions", {"messages": [{"role": "user", "content": "ping"}], "effort": "quick", "max_tokens": 1}) _p(" ✓ key saved and accepted", style="green") except urllib.error.HTTPError as e: _p(" key saved, but the server answered HTTP %d — check the key" % e.code, style="yellow") except Exception: _p(" ✓ key saved (couldn't reach the server to verify)", style="green") def doctor(): _p(" avalos-cli " + __version__, style="dim") _p(" server " + _base() + " · key " + ((_key()[:10] + "…") if _key() else "(none)"), style="dim") _p(" operator session " + ("active" if _session_token() else "none — run: avalos_auth.py login"), style="dim") try: _req("/v1/coder", {"task": "", "content": ""}) _p(" gateway accepted the coder probe; inference not tested", style="green") except urllib.error.HTTPError as e: if e.code in (401, 403): _p(" ✗ gateway contacted but credential rejected (HTTP %s)" % e.code, style="red") elif e.code == 429: _p(" ✗ gateway contacted but quota exhausted (HTTP 429)", style="red") else: _p(" gateway contacted; empty coder probe rejected (HTTP %s). " "Inference not verified." % e.code, style="yellow") except Exception as e: _p(" ✗ cannot reach: %s" % e, style="red") def _new_session(title=""): r = _req("/v1/sessions", {"title": title}) sid = r.get("session_id") c = _cfg(); c["session_id"] = sid; _save(c) return sid def _ensure_session(): sid = _cfg().get("session_id") if sid: try: _req("/v1/sessions/" + sid) return sid, True except Exception: pass return _new_session(), False def _session_history(sid, n=6): try: s = _req("/v1/sessions/" + sid) return s.get("messages", [])[-n:], len(s.get("messages", [])) except Exception: return [], 0 def research(arg): if not _key(): _p(" no API key — /login avl_live_...", style="yellow"); return 2 words, regions, depth = [], [], "galaxy" for w in (arg or "").split(): lw = w.lower().strip(",") if lw in ("east", "west"): regions.append(lw) elif lw in ("quick", "star"): depth = "star" elif lw in ("deep", "galaxy"): depth = "galaxy" else: words.append(w) parts = [p.strip() for p in " ".join(words).split("|") if p.strip()] if not parts: _p(" usage: /research [| item | item ...] [east] [west] [quick|deep]", style="yellow"); return 2 goal, items = parts[0], parts[1:] body = {"goal": goal, "items": items, "regions": regions or ["east", "west"], "depth": depth} try: with _Working("Researching"): r = _req("/v1/coagula", body) rid = r.get("run_id") if not rid: _p(" error: %s" % r, style="red"); return 2 st, s = "running", {} while st == "running": time.sleep(6) s = _req("/v1/coagula/status?rid=" + rid) st = s.get("status", "running") except urllib.error.HTTPError as e: if e.code == 402: _p(" research needs a paid key or a signed-in paid plan", style="yellow") elif e.code == 404: _p(" research is not available on this server yet", style="yellow") else: _p(" error: HTTP %s" % e.code, style="red") return 2 except Exception as e: _p(" error: %s" % e, style="red"); return 2 memo = s.get("memo_md") or "" if st == "DONE": _p(memo if memo else " (empty memo)") b = s.get("budget") or {} _p(" ✓ DONE in %ss · searches %s · fetches %s · model calls %s" % ( s.get("seconds"), b.get("searches"), b.get("fetches"), b.get("model_calls")), style="green") return 0 _p(" ✗ %s — %s" % (st, s.get("error") or "no reason given"), style="red") if memo: _p(memo, style="dim") return 1 def list_sessions(): try: rows = _req("/v1/sessions").get("sessions", []) except Exception as e: _p(" couldn't list: %s" % e, style="red"); return if not rows: _p(" no sessions yet", style="dim"); return cur = _cfg().get("session_id") for r in rows[:12]: mark = "→ " if r["sid"] == cur else " " _p(" %s%s %s [%d msgs]" % (mark, r["sid"], (r.get("title") or "")[:44], r.get("messages", 0)), style=(GOLD if r["sid"] == cur else "dim")) _DEL_LOCAL_ALL = ("conversation.json", "history", "escalations.jsonl", "aura_audit.jsonl") _DEL_LOCAL_ONE = ("conversation.json",) def _del_result(outcome, message, http=None, forget_sid=False, count=None): return {"outcome": outcome, "message": message, "http": http, "forget_sid": forget_sid, "count": count} def _del_why(body): e = body.get("error") if isinstance(body, dict) else None return (": %s" % str(e)[:120]) if e else "" def _del_receipt(body): v = body.get("deleted") if isinstance(body, dict) else None return v if isinstance(v, int) and not isinstance(v, bool) else None def _del_http_outcome(status, body, deleted_msg, gone_msg): if status == 401: return _del_result("unauthorized", "not signed in: the server did not accept this key", http=status) if status == 403: return _del_result("unauthorized", "refused: this sign-in may not delete these sessions%s" % _del_why(body), http=status) if 200 <= status <= 299: if _del_receipt(body) is None: return _del_result("error", "unexpected answer: HTTP %s without a deletion receipt, so NOT confirmed deleted" % status, http=status) return _del_result("deleted", deleted_msg, http=status, forget_sid=True) if status == 404: return _del_result("gone", gone_msg, http=status, forget_sid=True) return _del_result("error", "server error, HTTP %s%s" % (status, _del_why(body)), http=status) def _del_session(call, sid, has_key): sid = "" if sid is None else str(sid) if sid == "": return _del_result("none", "no server session is attached to this conversation (nothing to delete there)") if not has_key: return _del_result("unauthorized", "not signed in (no API key): nothing was sent to the server") path = "/v1/sessions/" + urllib.parse.quote(sid, safe="") try: status, body = call("DELETE", path) except Exception as e: return _del_result("network", "could not reach the server (%s: %s)" % (type(e).__name__, str(e)[:160])) return _del_http_outcome(status, body, "deleted session %s" % sid, "already gone: session %s is not on the server" % sid) def _del_all_sessions(call, has_key): if not has_key: return _del_result("unauthorized", "not signed in (no API key): nothing was sent to the server") try: status, body = call("DELETE", "/v1/sessions") except Exception as e: return _del_result("network", "could not reach the server (%s: %s)" % (type(e).__name__, str(e)[:160])) count = _del_receipt(body) if 200 <= status <= 299 else None r = _del_http_outcome(status, body, "deleted %s session(s) on the server" % count, "already gone: no sessions left on the server") r["count"] = count if r["outcome"] == "deleted" else None return r def _del_wipe_local(root, names): removed, missing, failed = [], [], [] for name in names: name = str(name) if name == "" or os.path.isabs(name) or "/" in name or "\\" in name or ".." in name: failed.append((name, "refused: not a plain file name")) continue full_path = os.path.join(root, name) if not os.path.exists(full_path): missing.append(name) continue try: os.remove(full_path) removed.append(name) except OSError as e: failed.append((name, str(e))) return {"removed": removed, "missing": missing, "failed": failed} def _del_confirm(ask, prompt): try: answer = ask(prompt) except (EOFError, KeyboardInterrupt): return False return str(answer or "").strip().lower() in ("y", "yes") def _del_call(method, path, timeout=30): r = urllib.request.Request(_base() + path, headers=_hdrs(False), method=method) try: with urllib.request.urlopen(r, timeout=timeout) as resp: status, raw = resp.status, resp.read() except urllib.error.HTTPError as e: status = e.code try: raw = e.read() except Exception: raw = b"" try: body = json.loads(raw.decode("utf-8", "replace") or "{}") except ValueError: body = {} return status, (body if isinstance(body, dict) else {}) def delete_cmd(arg, ask=None, yes=False): global _ptk a = str(arg or "").strip().lower() if a not in ("", "all"): _p(" usage: /delete this conversation (its session on the server and its copy on this PC)\n" " /delete all ALL sessions on the server + the local history files (asks y/N)", style="yellow") return 2 root = CFG.parent signed_in = bool(_key() or _session_token()) if a == "all": _p(" This deletes ALL your Avalos history:", style="bold yellow") _p(" · every session stored on %s for this key" % _base(), style="yellow") _p(" · on this PC (%s): %s" % (root, ", ".join(_DEL_LOCAL_ALL)), style="yellow") _p(" kept: your key and settings (config.json) and project memory (.avalos/MEMORY.md)", style="dim") if not yes and not _del_confirm(ask or input, " delete ALL history? [y/N]: "): _p(" · cancelled — nothing was deleted", style="dim") if ask is None and not _stdin_tty(): _p(" (no terminal to answer on; to delete without the question: avalosai delete all --yes)", style="dim") return 1 try: if a == "all": r, names = _del_all_sessions(_del_call, signed_in), _DEL_LOCAL_ALL else: r, names = _del_session(_del_call, _cfg().get("session_id"), signed_in), _DEL_LOCAL_ONE except KeyboardInterrupt: _p(" · interrupted: the server's answer is unknown and nothing was removed on this PC -- run /delete%s again" % (" all" if a else ""), style="yellow") return 1 ok = r["outcome"] in ("deleted", "gone", "none") style = {"deleted": "green", "gone": "dim", "none": "dim", "unauthorized": "yellow"}.get(r["outcome"], "red") _p(" server: %s%s" % (_esc(r["message"]), "" if r["http"] is None else " (HTTP %s)" % r["http"]), style=style) if r["outcome"] == "unauthorized": _p(" /login avl_live_... then run /delete%s again" % (" all" if a else ""), style="dim") elif not ok: _p(" the server copy is NOT confirmed deleted — run /delete%s again when the server answers" % (" all" if a else ""), style="dim") if r["forget_sid"]: c = _cfg() if "session_id" in c: c.pop("session_id", None) _save(c) w = _del_wipe_local(str(root), names) if a == "all": _ptk = None if w["removed"]: _p(" this PC: removed %s" % ", ".join(w["removed"]), style="green") elif not w["failed"]: _p(" this PC: nothing was stored", style="dim") for n, why in w["failed"]: _p(" this PC: could NOT remove %s (%s)" % (n, _esc(str(why))), style="red") return 0 if ok and not w["failed"] else 1 def chat_session(sid, msg): try: _lvl, _srv, _tier = _effort() r = _req("/v1/sessions/%s/chat" % sid, {"message": msg, "effort": _srv, "tier": _tier}) _qw = ((r.get("avalos") or {}).get("quota") or {}).get("warning") if _qw: _p(" ⚠ %s" % _qw, style="yellow") return r.get("reply", "(no reply)") except Exception as e: return "(couldn't reach Avalos AI: %s)" % e def _nearby_tests(p, max_files=5, max_bytes=200000): out, total = {}, 0 for t in sorted(p.parent.glob("test_%s*.py" % p.stem))[:max_files]: try: text = t.read_text(encoding="utf-8") except (OSError, UnicodeDecodeError): continue if total + len(text) > max_bytes: break out[t.name] = text total += len(text) return out def code(fp, task): p = Path(fp) if not p.exists(): _p(" no such file: %s" % fp, style="red"); return 2 if not _key(): _p(" no API key — /login avl_live_...", style="yellow"); return 2 content = p.read_text(encoding="utf-8") try: with _Working("Coding"): _lvl, _srv, _tier = _effort() _body = {"task": task, "filename": p.name, "content": content, "mode": _coder_mode(), "effort": _srv, "tier": _tier} _tf = _nearby_tests(p) if p.suffix == ".py" else {} if _tf: _body["test_files"] = _tf _p(" · grading against your tests: %s" % ", ".join(sorted(_tf)), style="dim") r = _req("/v1/coder", _body) rid = r.get("run_id") st, s = "running", {} while st == "running": time.sleep(5) s = _req("/v1/coder/status?rid=" + rid) st = s.get("status", "running") except Exception as e: _p(" error: %s" % e, style="red"); return 2 if st in ("SUCCESS", "DONE") and s.get("content"): import avalos_agent as _aa if not _approve("edit", "%s\n%s" % (p, _aa.Hands._diff(content, s["content"], p.name)), allow_always=False): _p(" · not applied (you declined)", style="dim"); return 0 bak = p.with_suffix(p.suffix + ".bak"); bak.write_text(content, encoding="utf-8") p.write_text(s["content"], encoding="utf-8") b = s.get("billing") or {} _p(" ✓ %s — applied to %s (backup %s)" % (st, p.name, bak.name), style="green") if b: _p(" tokens %s · plan %s · api %s" % (b.get("total_tokens", 0), b.get("billed_to_plan", 0), b.get("billed_to_api", 0)), style="dim") _qw = s.get("quota_warning") if _qw: _p(" ⚠ %s" % _qw, style="yellow") if s.get("verified") and s.get("proof"): if _RICH: _con.print(Panel(s["proof"], title="⚡ VERIFIED — passed a generated test in the sandbox", border_style="green", box=box.ROUNDED, padding=(0, 1))) else: _p(" ⚡ VERIFIED — passed a generated test in the sandbox:\n" + s["proof"]) elif st in ("SUCCESS", "DONE"): _p(" (applied; the behavioral test wasn't conclusively verified — review the diff)", style="dim") elif st == "UNVERIFIED" and s.get("content") and s["content"] != content: _ox = s.get("opifex") or {} _p(" ✗ UNVERIFIED — not proven correct%s" % ( ": %s/%s tests pass" % (_ox.get("passed"), _ox.get("live_tests")) if _ox else ""), style="yellow") if _ox.get("reason"): _p(" %s" % _ox["reason"], style="yellow") if _ox.get("evidence"): _p(" evidence (last lines):\n" + "\n".join(" " + l for l in _ox["evidence"].strip().splitlines()[-12:]), style="dim") import avalos_agent as _aa if _approve("edit", "UNVERIFIED candidate — apply anyway?\n%s\n%s" % ( p, _aa.Hands._diff(content, s["content"], p.name)), allow_always=False): bak = p.with_suffix(p.suffix + ".bak"); bak.write_text(content, encoding="utf-8") p.write_text(s["content"], encoding="utf-8") _p(" ! applied UNVERIFIED code to %s (backup %s) — test it before relying on it" % (p.name, bak.name), style="yellow") else: _p(" · not applied", style="dim") else: _p(" ✗ %s %s" % (st, s.get("error", "")), style="red") return 0 _AUTO = False CONV = Path.home() / ".avalos" / "conversation.json" CONV_MAX_TOKENS = 15000 CONV_MAX_CHARS = CONV_MAX_TOKENS * 4 def _conv_load(): try: t = json.loads(CONV.read_text(encoding="utf-8")) return t if isinstance(t, list) else [] except (OSError, ValueError): return [] def _conv_save(turns): while len(turns) > 1 and (len(json.dumps(turns)) + 3) // 4 > CONV_MAX_TOKENS: turns.pop(0) try: CONV.parent.mkdir(parents=True, exist_ok=True) CONV.write_text(json.dumps(turns), encoding="utf-8") except OSError: pass def _visible(s): return "".join(ch if ch in "\n\t" or ord(ch) >= 32 and ord(ch) != 127 else "\\x%02x" % ord(ch) for ch in s) def _approve(kind, preview, allow_always=True): title = {"edit": "edit file", "write": "write file", "bash": "run command", "read": "read outside this folder", "plan": "follow this plan"}.get(kind, kind) preview = _visible(preview) if _RICH: from rich.syntax import Syntax body = Syntax(preview, "bash" if kind == "bash" else "diff", word_wrap=True) _con.print(Panel(body, title="⚡ Coder Engine wants to %s" % title, border_style=GOLD, box=box.ROUNDED, padding=(0, 1))) else: print("--- Coder Engine wants to %s ---\n%s" % (title, preview)) opts = "[y]es / [n]o" + (" / [a]lways for edits in this folder" if allow_always and kind in ("edit", "write") else "") try: a = input(" allow? %s: " % opts).strip().lower() except EOFError: _p(" · denied: no terminal to ask on (stdin is not interactive). Run headless with " "`avalosai agent --yes` (edits) and `--yes-shell` (commands).", style="yellow") return False except KeyboardInterrupt: return False if a in ("a", "always") and allow_always and kind in ("edit", "write"): _p(" · auto-approve ON for edits inside %s this run (commands still ask)" % os.getcwd(), style=GOLD) return "always" return a in ("y", "yes") def _esc(s): if _RICH: from rich.markup import escape return escape(s) return s def _agent_event(kind, d): if kind == "brain": if d["calls"]: _p(" ⚡ step %d · %s" % (d["step"], _esc(", ".join(d["calls"]))), style="dim") elif kind == "tool": first = (d["result"] or "").splitlines()[:1] _p(" ↳ %s → %s" % (_esc(d["name"]), _esc(_visible(first[0] if first else "")[:110])), style="dim") elif kind == "janitor": _p(" · context %s (%s tokens of %s)" % (d.get("action"), d.get("tokens"), d.get("budget")), style="dim") elif kind == "escalation": _p(" ⚡ stall (%s) -- asked the Avalos expert for advice (~%s tokens)" % (d.get("reason"), d.get("tokens")), style=GOLD) elif kind == "notice": _p(" ! %s" % _esc(str(d.get("text", ""))), style="yellow") elif kind == "probatio": st = d.get("stage") if st == "start": _p(" ⚡ probatio: building %s by trial (drafts + spec-only testers, in the Avalos sandbox)" % _esc(str(d.get("file"))), style=GOLD) elif st == "written": _p(" · probatio: best draft passes %s of %s checks; written" % (d.get("final_live_passed"), d.get("tests_live")), style="dim") elif st == "keep_best": _p(" · probatio: after the loop %s checks pass (build: %s)" % (d.get("after_loop"), d.get("build_winner")), style="dim") elif st == "error": _p(" ! probatio failed (%s); continuing with the normal loop" % _esc(str(d.get("error"))), style="yellow") def _stdin_tty(): try: return bool(sys.stdin and sys.stdin.isatty()) except Exception: return False def agent(task, auto=None, keep_history=False, yes=False, yes_shell=False, as_json=False, budget=None): global _AUTO import avalos_agent as _aa real_out = sys.stdout if as_json: sys.stdout = sys.stderr stats = _aa.RunStats(budget) final = "" try: if not _key(): _p(" no API key — /login avl_live_...", style="yellow") stats.status, final = "error", "no API key" return _agent_done(_aa, stats, final, as_json, real_out, code=2) _lvl, _srv, _tier = _effort() headless = bool(yes or yes_shell or as_json) if headless: hands = _aa.Hands(os.getcwd(), _aa.make_headless_approver(bool(yes), stats.denials), auto=bool(yes), auto_bash=bool(yes_shell)) elif _probatio_on(): hands = _aa.Hands(os.getcwd(), _approve, auto=_AUTO if auto is None else auto, guards=True, remote_run=lambda files, argv: _http("/v1/coder/sandbox", {"files": files, "argv": argv}, timeout=90)) else: hands = _aa.Hands(os.getcwd(), _approve, auto=_AUTO if auto is None else auto) turns = _conv_load() if keep_history else [] history = [m for t in turns for m in t] memory = _aa.load_memory(os.getcwd()) if not keep_history: _p(" ⚡ Coder Engine · hands on %s · brain: Avalos AI (%s)%s%s" % ( os.getcwd(), _lvl, " · headless" if headless else "", " · budget %d tok" % budget if budget else ""), style=GOLD) if memory: _p(" · project memory loaded (.avalos/MEMORY.md, %d chars)" % len(memory), style="dim") try: final, _msgs = _aa.run_task(task, _req, hands, on_event=_agent_event, history=history, effort={"effort": _srv, "tier": _tier}, budget=budget, stats=stats, memory=memory, escalate=True) except KeyboardInterrupt: _p("\n · interrupted — nothing further was run", style="yellow") stats.status, final = "error", "interrupted" return _agent_done(_aa, stats, final, as_json, real_out, code=130) except Exception as e: _p(" error: %s" % e, style="red") stats.status, final = "error", "error: %s" % e return _agent_done(_aa, stats, final, as_json, real_out, code=2) finally: _AUTO = _AUTO or (hands.auto and not headless) if keep_history: turns.append(_aa.strip_reasoning(_msgs[stats.turn_start:]) + [{"role": "assistant", "content": final}]) _conv_save(turns) if stats.status == "budget_exceeded": _p(" ! stopped at the token budget (%d of %d)" % (stats.total_tokens, stats.budget), style="yellow") render_reply(final) return _agent_done(_aa, stats, final, as_json, real_out, code=0) finally: sys.stdout = real_out def _agent_done(_aa, stats, final, as_json, real_out, code): if not as_json: return code real_out.write(json.dumps(_aa.build_report(stats, final, __version__)) + "\n") real_out.flush() return _aa.exit_code(stats.status) if code in (0, 1) else code def remember_cmd(text): import avalos_agent as _aa try: line = _aa.remember(os.getcwd(), text) except (ValueError, TypeError) as e: _p(" nothing remembered: %s — usage: remember " % e, style="yellow"); return 2 except OSError as e: _p(" could not write .avalos/MEMORY.md: %s" % e, style="red"); return 2 _p(" ✓ remembered in %s: %s" % (_aa.memory_path(os.getcwd()), _esc(line)), style="green") return 0 AURA_AUDIT = Path.home() / ".avalos" / "aura_audit.jsonl" AURA_SEEN = Path.home() / ".avalos" / "aura_seen.json" AURA_TTL = 60.0 AURA_SKEW_TOL = 30.0 AURA_WAIT = 25 AURA_ALARM_AFTER = 5 AURA_RESULT_MAX = 8000 def _aura_audit(event, **kw): row = {"ts": round(time.time(), 3), "event": event, "cwd": os.getcwd()} row.update(kw) try: AURA_AUDIT.parent.mkdir(parents=True, exist_ok=True) with open(AURA_AUDIT, "a", encoding="utf-8") as f: f.write(json.dumps(row, ensure_ascii=False) + "\n") except OSError: pass def _norm_dir(p): return os.path.normcase(os.path.realpath(str(p))) def _aura_allowed(folder=None): want = _norm_dir(folder or os.getcwd()) return any(_norm_dir(x) == want for x in (_cfg().get("aura_allow") or []) if isinstance(x, str)) def _remote_enabled(): return str(_cfg().get("remote", "on")).lower() != "off" def set_remote(arg): a = str(arg or "").lower().strip() if a in ("on", "off"): c = _cfg(); c["remote"] = a; _save(c) _aura_audit("remote_" + a) on = _remote_enabled() _p(" remote (Aura) tasks on this PC: %s%s" % ("ON" if on else "OFF", "" if on else " — a running `avalosai --aura` stops at its next poll"), style=(GOLD if on else "yellow")) def aura_cmd(arg): a = str(arg or "").lower().strip() here = os.getcwd() if a == "on": return aura_serve() if a == "off": _p(" Aura hands are not running here (while they run, Ctrl+C stops them; " "`avalosai remote off` stops every terminal)", style="dim"); return 0 if a == "allow": c = _cfg(); lst = [x for x in (c.get("aura_allow") or []) if isinstance(x, str)] if not _aura_allowed(here): lst.append(os.path.realpath(here)) c["aura_allow"] = lst; _save(c); _aura_audit("allow", folder=here) _p(" ✓ Aura may send tasks to %s (edits and commands still ask here)" % here, style="green"); return 0 if a in ("disallow", "deny", "remove"): c = _cfg() c["aura_allow"] = [x for x in (c.get("aura_allow") or []) if isinstance(x, str) and _norm_dir(x) != _norm_dir(here)] _save(c); _aura_audit("disallow", folder=here) _p(" ✓ %s removed from the Aura allowlist" % here, style="green"); return 0 allowed = [x for x in (_cfg().get("aura_allow") or []) if isinstance(x, str)] _p(" Aura hands · remote %s · this folder %s" % ("ON" if _remote_enabled() else "OFF", "ALLOWED" if _aura_allowed(here) else "not allowed (/aura allow)"), style=GOLD) for x in allowed: _p(" allowed: %s" % x, style="dim") _p(" usage: /aura on | off | allow | disallow audit log: %s" % AURA_AUDIT, style="dim") return 0 def _aura_seen_load(): try: d = json.loads(AURA_SEEN.read_text(encoding="utf-8")) cut = time.time() - 86400 return {k: v for k, v in d.items() if isinstance(v, dict) and float(v.get("ts", 0)) > cut} except (OSError, ValueError, AttributeError): return {} def _aura_seen_save(seen): try: AURA_SEEN.parent.mkdir(parents=True, exist_ok=True) AURA_SEEN.write_text(json.dumps(seen), encoding="utf-8") except OSError: pass def _aura_check(env, server_time, seen, me="", now=None, skew=None): now = time.time() if now is None else now if not isinstance(env, dict) or env.get("v") != 1: return "malformed" tid, nonce, text = env.get("task_id"), env.get("nonce"), env.get("text") c, x = env.get("created_at"), env.get("expires_at") if not (isinstance(tid, str) and tid and isinstance(nonce, str) and len(nonce) >= 16 and isinstance(text, str) and text.strip() and isinstance(c, (int, float)) and isinstance(x, (int, float))): return "malformed" if tid in seen or any(v.get("nonce") == nonce for v in seen.values()): return "replay" if not (0 < x - c <= AURA_TTL + 1): return "bad_expiry" if not isinstance(server_time, (int, float)): return "no_server_time" if server_time >= x: return "expired" if skew is not None and abs((server_time - now) - skew) > AURA_SKEW_TOL: return "stale_response" if me and str(env.get("user") or "").lower() != me.lower(): return "wrong_user" return None def _aura_post(env, status, result="", note=""): body = {"task_id": env["task_id"], "nonce": env["nonce"], "status": status, "result": (result or "")[:AURA_RESULT_MAX], "note": (note or "")[:300]} try: st, d = _http("/v1/aura/bridge/result", body, timeout=20) except Exception as e: st, d = 0, {"error": str(e)[:120]} if st != 200 or not isinstance(d, dict) or d.get("ok") is not True: _aura_audit("result_not_accepted", task_id=env["task_id"], status=status, http=st, error=str((d or {}).get("error", ""))[:120] if isinstance(d, dict) else "") if status in ("done", "error", "rejected"): _p(" ⚠ the server did not accept the %s report (HTTP %s) — Aura will not hear it" % (status, st), style="yellow") return False return True def _aura_approver(env): def approve(kind, preview): first = (preview or "").splitlines()[:1] _aura_post(env, "awaiting_approval", note="%s: %s" % (kind, first[0][:200] if first else "")) _p(" ⚡ Aura task wants to %s — approve or decline HERE" % kind, style=GOLD) a = _approve(kind, preview, allow_always=False) ok = a is True _aura_audit("approval", task_id=env["task_id"], kind=kind, approved=ok, preview=_visible(preview or "")[:500]) _aura_post(env, "running") return ok return approve class _Heartbeat(object): def __init__(self, cli_id): self.cli_id, self.stop = cli_id, threading.Event() def __enter__(self): def beat(): while not self.stop.wait(15): try: _http("/v1/aura/bridge/poll", {"cli_id": self.cli_id, "wait": 0, "drain": False}, timeout=10) except Exception: pass threading.Thread(target=beat, daemon=True).start() return self def __exit__(self, *a): self.stop.set() def _aura_run(env, cli_id, history): import avalos_agent as _aa text = env["text"] _p("\n ⚡ Aura task: %s" % _esc(_visible(text)[:400]), style="bold " + GOLD) _aura_audit("task_start", task_id=env["task_id"], text=text[:500]) _aura_post(env, "running") hands = _aa.Hands(os.getcwd(), _aura_approver(env), auto=False, auto_bash=False, strict=True) _lvl, _srv, _tier = _effort() status, final = "error", "" try: with _Heartbeat(cli_id): final, _m = _aa.run_task(text, _req, hands, on_event=_agent_event, history=list(history), effort={"effort": _srv, "tier": _tier}, memory=_aa.load_memory(os.getcwd()), escalate=True) status = "done" except KeyboardInterrupt: final = "Stopped at the terminal before it finished." _aura_post(env, "error", final) _aura_audit("task_end", task_id=env["task_id"], status="interrupted") raise except Exception as e: final = "The terminal hit an error: %s" % str(e)[:300] if hands.auto or hands.auto_bash: _aura_audit("alarm", task_id=env["task_id"], why="auto-approval became enabled during an Aura task") render_reply(final) _aura_post(env, status, final) _aura_audit("task_end", task_id=env["task_id"], status=status, result_chars=len(final or "")) history.extend([{"role": "user", "content": text}, {"role": "assistant", "content": final}]) del history[:-12] return status def aura_serve(max_polls=None): import uuid here = os.getcwd() if not _key(): _p(" no API key — /login avl_live_...", style="yellow"); return 2 if not _session_token(): _p(" Aura hands need the owner's operator session on this PC. Run once:\n" " python \"%s\" login" % (Path.home() / ".avalos" / "avalos_auth.py"), style="yellow"); return 2 if not _remote_enabled(): _p(" remote tasks are OFF on this PC (kill switch). Turn on with: avalosai remote on", style="yellow"); return 2 if not _aura_allowed(here): _p(" %s is not enabled for Aura. Enable it with: /aura allow (or: avalosai aura allow)" % here, style="yellow"); return 2 cli_id = uuid.uuid4().hex seen = _aura_seen_load() me = str(_cfg().get("session_email") or "") skew, fails, polls, standby_said, history = None, 0, 0, False, [] _p(" ⚡ Aura hands ON · %s · say \"terminal: …\" to Aura on avalos.ai · Ctrl+C stops\n" " reads run on their own; every edit and command asks HERE first" % here, style=GOLD) _aura_audit("start", cli_id=cli_id[:8]) rc = 0 try: while max_polls is None or polls < max_polls: polls += 1 if not _remote_enabled(): _p(" remote turned OFF — Aura hands stopped", style="yellow"); break if not _aura_allowed(here): _p(" this folder was removed from the Aura allowlist — stopped", style="yellow"); break if not _session_token(): _p(" operator session expired — stopped (run avalos_auth.py login)", style="yellow"); break try: st, r = _http("/v1/aura/bridge/poll", {"cli_id": cli_id, "wait": AURA_WAIT, "where": os.path.basename(here) or here}, timeout=AURA_WAIT + 20) except Exception as e: st, r = 0, {"error": str(e)[:120]} if st == 403: _p(" the server refused this terminal: it is not the owner's signed-in operator session " "(run avalos_auth.py login)", style="red") _aura_audit("refused_by_server", http=403); rc = 2; break if st != 200 or not isinstance(r, dict) or not isinstance(r.get("server_time"), (int, float)): fails += 1 if fails == AURA_ALARM_AFTER or (fails > AURA_ALARM_AFTER and fails % 20 == 0): _p(" ⚠ ALARM: %d polls in a row failed (last: HTTP %s %s) — Aura cannot reach this terminal" % (fails, st, str((r or {}).get("error", ""))[:80]), style="red") _aura_audit("alarm", why="poll failing", consecutive=fails, http=st) time.sleep(min(30, 2 ** min(fails, 5))) continue if fails >= AURA_ALARM_AFTER: _p(" ✓ reconnected after %d failed polls" % fails, style="green") fails = 0 if skew is None or not r.get("tasks"): skew = r["server_time"] - time.time() if r.get("standby") and not standby_said: standby_said = True _p(" · standby: another terminal is serving Aura right now", style="dim") for env in r.get("tasks") or []: why = _aura_check(env, r["server_time"], seen, me, time.time(), skew) if why: tid = env.get("task_id") if isinstance(env, dict) else None _aura_audit("rejected", task_id=str(tid)[:64], why=why) _p(" ✗ refused an Aura task (%s)" % why, style="yellow") if why not in ("replay", "malformed", "wrong_user"): _aura_post(env, "rejected", note="refused by the terminal: " + why) continue seen[env["task_id"]] = {"nonce": env["nonce"], "ts": time.time()} _aura_seen_save(seen) if not _remote_enabled() or not _aura_allowed(here): _aura_post(env, "rejected", note="remote tasks were turned off on the PC") continue if time.time() + skew >= env["expires_at"]: _aura_audit("rejected", task_id=env["task_id"], why="expired_before_start") _aura_post(env, "rejected", note="it expired while the terminal was busy") continue _aura_run(env, cli_id, history) except KeyboardInterrupt: _p("\n · Aura hands stopped", style="dim") finally: try: _http("/v1/aura/bridge/bye", {"cli_id": cli_id}, timeout=5) except Exception: pass _aura_seen_save(seen) _aura_audit("stop", cli_id=cli_id[:8], polls=polls) return rc def index_dir(path): root = Path(path) if not root.exists(): _p(" no such path: %s" % path, style="red"); return exts = {".py", ".js", ".ts", ".tsx", ".jsx", ".go", ".java", ".rs", ".c", ".cpp", ".h", ".hpp", ".rb", ".php", ".cs", ".kt", ".swift", ".md", ".sh"} skip = {"node_modules", ".git", "venv", ".venv", "__pycache__", "dist", "build", ".next", "target"} files = [] for f in root.rglob("*"): if any(part in skip for part in f.parts): continue if f.is_file() and f.suffix in exts: try: if f.stat().st_size < 200000: files.append({"path": str(f.relative_to(root)), "content": f.read_text(encoding="utf-8", errors="ignore")}) except Exception: pass if len(files) >= 800: break if not files: _p(" no code files found under %s" % path, style="yellow"); return with _Working("Indexing"): try: r = _req("/v1/index", {"files": files}) except Exception as e: _p(" index error: %s" % e, style="red"); return _p(" ⚡ indexed %d files → %d chunks (server-side · your codebase is now in-context for chat)" % (r.get("files", 0), r.get("chunks", 0)), style="green") def _toolbar(): sess = _CUR_SID or "no session · /login to persist" return FormattedText([ ("", "\n\n"), ("class:tb", " ⚡ avalos AI "), ("class:tbsep", "│"), ("class:tb", " hands on %s " % (os.path.basename(os.getcwd()) or os.getcwd())), ("class:tbsep", "│"), ("class:tbdim", " ↑↓ history · file.py : task to edit · /help /effort /exit "), ("class:tbsep", "│"), ("class:tb", " effort %s " % _effort()[0]), ]) def _prompt(): global _ptk if _PTK: try: if _ptk is None: _h = Path.home() / ".avalos" / "history"; _h.parent.mkdir(parents=True, exist_ok=True) _ptk = PromptSession(history=FileHistory(str(_h))) w = _frame_w() head = "⚡ message " top = head + "─" * max(4, w - _cols(head)) msg = FormattedText([("class:frame", top + "\n"), ("class:frame", " ")]) _out = _ptk.prompt(msg, style=_ptk_style, bottom_toolbar=_toolbar).strip() try: _cl = "\x1b[2m" + ("─" * w) + "\x1b[0m\n" _con.file.write(_cl) if _RICH else sys.stdout.write(_cl) except Exception: pass return _out except Exception: pass if _RICH: return _con.input(Text.assemble(("⚡ ", "bold " + GOLD), ("avalos ", "bold " + COBALT), ("┃ ", "bold " + GOLD))).strip() return input("avalosai> ").strip() def _is_file_task(line): if ":" not in line: return None left, right = line.split(":", 1) left, right = left.strip(), right.strip() if left and right and " " not in left and ("." in left or os.path.exists(left)): return left, right return None def _show_hist(sid): hist, total = _session_history(sid) if total: _p(" ⤾ resumed %s — %d messages (server-side · survived disconnect)" % (sid, total), style=GOLD) for m in hist: who = "you " if m["role"] == "user" else "avalos" _p(" %s %s" % (who, (m.get("content") or "")[:96].replace("\n", " ")), style="dim") _p("") def repl(): global _CUR_SID, _AUTO welcome() sid = None if not _key(): _p(" · /login avl_live_... first\n", style="yellow") else: n = len(_conv_load()) _p(" · %s — hands on %s · every edit and command asks first\n" % ("continuing your conversation (%d turns, /new to start fresh)" % n if n else "new conversation", os.getcwd()), style="dim") while True: _CUR_SID = sid try: line = _prompt() except (EOFError, KeyboardInterrupt): _p("\n bye 🌱 (conversation kept on this PC — /delete removes it)", style="dim"); return 0 if not line: continue low = line.lower().lstrip("/") if low in ("exit", "quit", "q"): _p(" bye 🌱 (conversation kept on this PC — /delete removes it)", style="dim"); return 0 if low in ("help", "?"): _p(__doc__, style="dim"); continue if low == "doctor": doctor(); continue if low == "effort" or low.startswith("effort "): set_effort(line.lstrip("/")[6:].strip()); continue if low == "mode" or low.startswith("mode "): set_coder_mode(line.lstrip("/")[4:].strip()); continue if low == "probatio" or low.startswith("probatio "): set_probatio(line.lstrip("/")[8:].strip()); continue if low == "sessions": list_sessions(); continue if low == "delete" or low.startswith("delete "): delete_cmd(line.lstrip("/")[6:].strip()) if sid and not _cfg().get("session_id"): sid = None continue if low == "research" or low.startswith("research "): research(line.lstrip("/")[8:].strip()); continue if low in ("agent", "chat") or low.startswith("agent "): rest = line.split(None, 1)[1] if " " in line.strip() else "" if rest: agent(rest, keep_history=True) else: _p(" · no modes needed — just type; the Coder Engine uses its hands when " "the task needs them", style="dim") continue if low == "auto" or low.startswith("auto "): arg = line.lstrip("/")[4:].strip().lower() if arg in ("on", "off"): _AUTO = (arg == "on") _p(" auto-approve file edits inside %s: %s (commands and anything outside this " "folder always ask)" % (os.getcwd(), "ON" if _AUTO else "off"), style=GOLD); continue if low == "aura" or low.startswith("aura "): aura_cmd(line.lstrip("/")[4:].strip()); continue if low == "remote" or low.startswith("remote "): set_remote(line.lstrip("/")[6:].strip()); continue if low == "new": _conv_save([]); _p(" · new conversation", style=GOLD); continue if low == "remember" or low.startswith("remember "): remember_cmd(line.lstrip("/")[8:].strip()); continue if low.startswith("index "): index_dir(line.split(None, 1)[1]); continue if low.startswith("resume "): sid = line.split(None, 1)[1].strip() c = _cfg(); c["session_id"] = sid; _save(c); _show_hist(sid); continue if low == "clear": if _RICH: _con.clear() welcome(); continue if low.startswith("login "): login(line.split(None, 1)[1]) if not sid: try: sid, _r = _ensure_session() except Exception: pass continue if line.startswith("/"): _p(" unknown command %s — /help lists them" % line.split()[0], style="yellow"); continue if not _key(): _p(" no API key — /login avl_live_...", style="yellow"); continue agent(line, keep_history=True) def main(): a = sys.argv[1:] if not a: return repl() if a[0] in ("-h", "--help", "help"): _p(__doc__); return 0 if a[0] == "login" and len(a) >= 2: login(a[1]); return 0 if a[0] == "doctor": doctor(); return 0 if a[0] == "index" and len(a) >= 2: index_dir(a[1]); return 0 if a[0] == "agent" and len(a) >= 2: import avalos_agent as _aa try: o = _aa.parse_agent_args(a[1:]) except ValueError as e: _p(" %s\n usage: avalosai agent [--yes] [--yes-shell] [--json] [--budget N] \"\"" % e, style="yellow") return 2 return agent(o["task"], yes=o["yes"], yes_shell=o["yes_shell"], as_json=o["json"], budget=o["budget"]) if a[0] == "remember": return remember_cmd(" ".join(a[1:])) if a[0] == "delete": rest = [x.lower() for x in a[1:]] yes = any(x in ("--yes", "-y") for x in rest) rest = [x for x in rest if x not in ("--yes", "-y")] return delete_cmd(" ".join(rest), yes=yes) if a[0] == "--aura": return aura_serve() if a[0] == "aura": return aura_cmd(a[1] if len(a) >= 2 else "") if a[0] == "remote": set_remote(a[1] if len(a) >= 2 else ""); return 0 if len(a) >= 2: return code(a[0], " ".join(a[1:])) _p(__doc__); return 0 if __name__ == "__main__": sys.exit(main())