Files
hack/tools/scripts/llm-key-hunter/results/_cache/content/aitherium_aither-adk.json
T
OpenCode 68a9071cf1 feat: reconcile local master with origin (exa/hot-platform hunters, leaks ledger, latest results)
Local branch had diverged from origin/master (sibling commits on the same base). Rewrote local history linearly on top of origin/master, folding in all local content: exa/hot-platform discovery hunters, leaks ledger, nightly sweep orchestrator, updated .gitignore and skill docs, plus the latest hunt output and vault state. Remote-only files (vault keys, channel scripts) were restored rather than dropped, so the resulting tree is a full union of both sides.
2026-08-07 12:53:47 +08:00

1 line
2.5 MiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{"repo": "aitherium/aither-adk", "files": {"adk/llm/onboarding.py": {"6130f720cc2fde22e8e71b3e6155268d0fcf7c3f": {"t": 1785674092.6127229, "n": 9721, "h": "1dcb2fc0cb666be2", "c": "\"\"\"Guided backend onboarding — actionable setup help for every LLM provider.\n\nThe owner directive behind this module: when someone tries to use a backend that\nisn't configured yet (no API key, server not running, model not pulled), the\nerror must GUIDE them step-by-step instead of dead-ending. Every \"no key\" /\n\"no backend available\" raise site in adk should route its message through here.\n\nUsage:\n from adk.llm.onboarding import guide, missing_key_message, provider_menu\n raise RuntimeError(missing_key_message(\"moonshot\"))\n print(guide(\"ollama\"))\n print(provider_menu())\n\nKeep guides SHORT (a terminal error, not a manual): where to get the key/server,\nthe exact command to configure it, the exact command to test it.\n\"\"\"\nfrom __future__ import annotations\n\nfrom typing import Dict, List, Optional\n\n# ── Guide registry ──────────────────────────────────────────────────────────\n# kind: \"cloud\" (hosted API, needs key) | \"gateway\" (Aitherium hosted) |\n# \"local\" (self-hosted server/runtime, needs a running endpoint)\nPROVIDER_GUIDES: Dict[str, Dict[str, object]] = {\n \"moonshot\": {\n \"label\": \"Moonshot Kimi (kimi-k3)\",\n \"kind\": \"cloud\",\n \"key_env\": \"MOONSHOT_API_KEY\",\n \"key_url\": \"https://platform.moonshot.ai/console/api-keys\",\n \"steps\": [\n \"Create an account + API key: https://platform.moonshot.ai (Console → API Keys)\",\n \"Configure: adk keys set moonshot sk-... (stores + vault-syncs; or export MOONSHOT_API_KEY=sk-...)\",\n \"Test: adk ask \\\"hello\\\" --backend moonshot\",\n ],\n \"aliases\": [\"kimi\", \"kimi-k3\"],\n },\n \"deepseek\": {\n \"label\": \"DeepSeek (deepseek-chat / deepseek-reasoner)\",\n \"kind\": \"cloud\",\n \"key_env\": \"DEEPSEEK_API_KEY\",\n \"key_url\": \"https://platform.deepseek.com/api_keys\",\n \"steps\": [\n \"Create an API key: https://platform.deepseek.com/api_keys\",\n \"Configure: adk keys set deepseek sk-... (stores + vault-syncs; or export DEEPSEEK_API_KEY=sk-...)\",\n \"Test: adk ask \\\"hello\\\" --backend deepseek\",\n ],\n },\n \"anthropic\": {\n \"label\": \"Anthropic Claude\",\n \"kind\": \"cloud\",\n \"key_env\": \"ANTHROPIC_API_KEY\",\n \"key_url\": \"https://console.anthropic.com/settings/keys\",\n \"steps\": [\n \"Create an API key: https://console.anthropic.com/settings/keys\",\n \"Configure: adk keys set anthropic sk-ant-... (stores + vault-syncs; or export ANTHROPIC_API_KEY=...)\",\n \"Test: adk ask \\\"hello\\\" --backend anthropic\",\n ],\n \"aliases\": [\"claude\"],\n },\n \"openai\": {\n \"label\": \"OpenAI\",\n \"kind\": \"cloud\",\n \"key_env\": \"OPENAI_API_KEY\",\n \"key_url\": \"https://platform.openai.com/api-keys\",\n \"steps\": [\n \"Create an API key: https://platform.openai.com/api-keys\",\n \"Configure: adk keys set openai sk-... (stores + vault-syncs; or export OPENAI_API_KEY=sk-...)\",\n \"Test: adk ask \\\"hello\\\" --backend openai\",\n ],\n },\n \"gemini\": {\n \"label\": \"Google Gemini\",\n \"kind\": \"cloud\",\n \"key_env\": \"GEMINI_API_KEY\",\n \"key_url\": \"https://aistudio.google.com/apikey\",\n \"steps\": [\n \"Create an API key: https://aistudio.google.com/apikey\",\n \"Configure: export GEMINI_API_KEY=...\",\n \"Test: adk ask \\\"hello\\\" --backend gemini\",\n ],\n \"aliases\": [\"google\"],\n },\n \"groq\": {\n \"label\": \"Groq\",\n \"kind\": \"cloud\",\n \"key_env\": \"GROQ_API_KEY\",\n \"key_url\": \"https://console.groq.com/keys\",\n \"steps\": [\n \"Create an API key: https://console.groq.com/keys\",\n \"Configure: adk keys set groq gsk_... (stores + vault-syncs; or export GROQ_API_KEY=gsk_...)\",\n ],\n },\n \"together\": {\n \"label\": \"Together AI\",\n \"kind\": \"cloud\",\n \"key_env\": \"TOGETHER_API_KEY\",\n \"key_url\": \"https://api.together.xyz/settings/api-keys\",\n \"steps\": [\n \"Create an API key: https://api.together.xyz/settings/api-keys\",\n \"Configure: adk keys set together <key> (stores + vault-syncs)\",\n ],\n },\n \"gateway\": {\n \"label\": \"Aitherium gateway (hosted brain — free tier)\",\n \"kind\": \"gateway\",\n \"key_env\": \"AITHER_API_KEY\",\n \"key_url\": \"https://gateway.aitherium.com\",\n \"steps\": [\n \"Register (free): aither register (device flow, no card)\",\n \"Or grab a key at https://gateway.aitherium.com then: export AITHER_API_KEY=aither_sk_...\",\n \"Test: adk ask \\\"hello\\\" (gateway is the default fallback)\",\n ],\n \"aliases\": [\"aitherium\", \"aither\"],\n },\n \"ollama\": {\n \"label\": \"Ollama (local, easiest self-hosted)\",\n \"kind\": \"local\",\n \"steps\": [\n \"Install: https://ollama.com/download (winget install Ollama.Ollama / brew install ollama)\",\n \"Start + pull a model: ollama serve then ollama pull gemma4:4b\",\n \"Configure: adk backend set ollama (auto-detects localhost:11434)\",\n \"Test: adk ask \\\"hello\\\" --backend ollama\",\n ],\n },\n \"vllm\": {\n \"label\": \"vLLM (local GPU server, OpenAI-compatible)\",\n \"kind\": \"local\",\n \"steps\": [\n \"Easiest: let adk provision it — adk node bootstrap (detects your GPU, serves a fitting model)\",\n \"Manual: docker run --gpus all -p 8000:8000 vllm/vllm-openai --model <hf-model>\",\n \"Configure: adk backend set vllm --base-url http://localhost:8000/v1 --model <served-model>\",\n ],\n },\n \"llamacpp\": {\n \"label\": \"llama.cpp (CPU-friendly local server)\",\n \"kind\": \"local\",\n \"steps\": [\n \"Get llama.cpp: https://github.com/ggml-org/llama.cpp/releases (or adk node bootstrap on CPU boxes)\",\n \"Serve OpenAI-compatible: llama-server -m model.gguf --port 8080\",\n \"Configure: adk backend set llamacpp --base-url http://localhost:8080/v1\",\n ],\n },\n \"lmstudio\": {\n \"label\": \"LM Studio (desktop app, local server)\",\n \"kind\": \"local\",\n \"steps\": [\n \"Install LM Studio: https://lmstudio.ai — download a model, enable the local server\",\n \"Configure: adk backend set lmstudio --base-url http://localhost:1234/v1\",\n ],\n },\n \"genesis\": {\n \"label\": \"AitherOS Genesis (your own fleet)\",\n \"kind\": \"local\",\n \"steps\": [\n \"Requires a running AitherOS deployment (Genesis on :8001).\",\n \"Configure: adk backend set genesis --base-url https://localhost:8001/v1\",\n ],\n },\n}\n\n# Fleet/local MODEL names people try as if they were providers — map each to the\n# provider guide that actually serves it, with a hint line.\nMODEL_HINTS: Dict[str, str] = {\n \"bonsai\": \"vllm\",\n \"nemotron\": \"gateway\",\n \"nemotron-orchestrator\": \"gateway\",\n \"aither-orchestrator\": \"gateway\",\n \"gemma4\": \"ollama\",\n \"gemma4-12b\": \"gateway\",\n \"qwen3.6\": \"gateway\",\n \"qwen36\": \"gateway\",\n \"kimi-k3\": \"moonshot\",\n \"kimi\": \"moonshot\",\n}\n\n_ALIAS_INDEX: Dict[str, str] = {}\nfor _name, _g in PROVIDER_GUIDES.items():\n _ALIAS_INDEX[_name] = _name\n for _a in _g.get(\"aliases\", []): # type: ignore[union-attr]\n _ALIAS_INDEX[str(_a)] = _name\n\n\ndef resolve_name(name: str) -> Optional[str]:\n \"\"\"Resolve a provider name, alias, or known model name to a guide key.\"\"\"\n n = (name or \"\").strip().lower()\n if n in _ALIAS_INDEX:\n return _ALIAS_INDEX[n]\n if n in MODEL_HINTS:\n return MODEL_HINTS[n]\n # Longest prefix wins so \"gemma4-12b\" (gateway) beats \"gemma4\" (ollama).\n for prefix in sorted(MODEL_HINTS, key=len, reverse=True):\n if n.startswith(prefix):\n return MODEL_HINTS[prefix]\n return None\n\n\ndef guide(name: str) -> str:\n \"\"\"Full setup guide for a provider (or a model name that maps to one).\"\"\"\n key = resolve_name(name)\n if not key:\n return provider_menu(header=f\"Unknown backend '{name}'. Available backends:\")\n g = PROVIDER_GUIDES[key]\n lines: List[str] = [f\"{g['label']} — setup:\"]\n n = (name or \"\").strip().lower()\n if n in MODEL_HINTS and key != n:\n lines.insert(0, f\"'{name}' is a model served via the '{key}' backend.\")\n for i, step in enumerate(g[\"steps\"], 1): # type: ignore[index]\n lines.append(f\" {i}. {step}\")\n return \"\\n\".join(lines)\n\n\ndef missing_key_message(name: str) -> str:\n \"\"\"Error message for a cloud provider selected without its API key —\n states the problem AND the exact fix, never a bare 'no key'.\"\"\"\n key = resolve_name(name) or name\n g = PROVIDER_GUIDES.get(key)\n if not g or g.get(\"kind\") == \"local\":\n return guide(name)\n env = g.get(\"key_env\", f\"{key.upper()}_API_KEY\")\n return (\n f\"{g['label']}: no API key configured ({env} not set).\\n\" + guide(key)\n )\n\n\ndef provider_menu(header: str = \"Available LLM backends:\") -> str:\n \"\"\"One-line-per-provider menu, grouped cloud/gateway/local, with the\n guide command so the user always has a next step.\"\"\"\n groups = {\"gateway\": [], \"cloud\": [], \"local\": []} # type: Dict[str, List[str]]\n for name, g in PROVIDER_GUIDES.items():\n groups[str(g[\"kind\"])].append(f\" {name:<10} {g['label']}\")\n out = [header]\n for kind, title in ((\"gateway\", \"Hosted (free tier)\"), (\"cloud\", \"Cloud APIs\"), (\"local\", \"Self-hosted / local\")):\n out.append(f\"{title}:\")\n out.extend(sorted(groups[kind]))\n out.append(\"Setup help for any of them: adk backend guide <name>\")\n return \"\\n\".join(out)\n"}, "ba68a8c507f455796092602b928f9b7f39aa0b05": {"t": 1785846829.2963746, "n": 9721, "h": "1dcb2fc0cb666be2", "c": "\"\"\"Guided backend onboarding — actionable setup help for every LLM provider.\n\nThe owner directive behind this module: when someone tries to use a backend that\nisn't configured yet (no API key, server not running, model not pulled), the\nerror must GUIDE them step-by-step instead of dead-ending. Every \"no key\" /\n\"no backend available\" raise site in adk should route its message through here.\n\nUsage:\n from adk.llm.onboarding import guide, missing_key_message, provider_menu\n raise RuntimeError(missing_key_message(\"moonshot\"))\n print(guide(\"ollama\"))\n print(provider_menu())\n\nKeep guides SHORT (a terminal error, not a manual): where to get the key/server,\nthe exact command to configure it, the exact command to test it.\n\"\"\"\nfrom __future__ import annotations\n\nfrom typing import Dict, List, Optional\n\n# ── Guide registry ──────────────────────────────────────────────────────────\n# kind: \"cloud\" (hosted API, needs key) | \"gateway\" (Aitherium hosted) |\n# \"local\" (self-hosted server/runtime, needs a running endpoint)\nPROVIDER_GUIDES: Dict[str, Dict[str, object]] = {\n \"moonshot\": {\n \"label\": \"Moonshot Kimi (kimi-k3)\",\n \"kind\": \"cloud\",\n \"key_env\": \"MOONSHOT_API_KEY\",\n \"key_url\": \"https://platform.moonshot.ai/console/api-keys\",\n \"steps\": [\n \"Create an account + API key: https://platform.moonshot.ai (Console → API Keys)\",\n \"Configure: adk keys set moonshot sk-... (stores + vault-syncs; or export MOONSHOT_API_KEY=sk-...)\",\n \"Test: adk ask \\\"hello\\\" --backend moonshot\",\n ],\n \"aliases\": [\"kimi\", \"kimi-k3\"],\n },\n \"deepseek\": {\n \"label\": \"DeepSeek (deepseek-chat / deepseek-reasoner)\",\n \"kind\": \"cloud\",\n \"key_env\": \"DEEPSEEK_API_KEY\",\n \"key_url\": \"https://platform.deepseek.com/api_keys\",\n \"steps\": [\n \"Create an API key: https://platform.deepseek.com/api_keys\",\n \"Configure: adk keys set deepseek sk-... (stores + vault-syncs; or export DEEPSEEK_API_KEY=sk-...)\",\n \"Test: adk ask \\\"hello\\\" --backend deepseek\",\n ],\n },\n \"anthropic\": {\n \"label\": \"Anthropic Claude\",\n \"kind\": \"cloud\",\n \"key_env\": \"ANTHROPIC_API_KEY\",\n \"key_url\": \"https://console.anthropic.com/settings/keys\",\n \"steps\": [\n \"Create an API key: https://console.anthropic.com/settings/keys\",\n \"Configure: adk keys set anthropic sk-ant-... (stores + vault-syncs; or export ANTHROPIC_API_KEY=...)\",\n \"Test: adk ask \\\"hello\\\" --backend anthropic\",\n ],\n \"aliases\": [\"claude\"],\n },\n \"openai\": {\n \"label\": \"OpenAI\",\n \"kind\": \"cloud\",\n \"key_env\": \"OPENAI_API_KEY\",\n \"key_url\": \"https://platform.openai.com/api-keys\",\n \"steps\": [\n \"Create an API key: https://platform.openai.com/api-keys\",\n \"Configure: adk keys set openai sk-... (stores + vault-syncs; or export OPENAI_API_KEY=sk-...)\",\n \"Test: adk ask \\\"hello\\\" --backend openai\",\n ],\n },\n \"gemini\": {\n \"label\": \"Google Gemini\",\n \"kind\": \"cloud\",\n \"key_env\": \"GEMINI_API_KEY\",\n \"key_url\": \"https://aistudio.google.com/apikey\",\n \"steps\": [\n \"Create an API key: https://aistudio.google.com/apikey\",\n \"Configure: export GEMINI_API_KEY=...\",\n \"Test: adk ask \\\"hello\\\" --backend gemini\",\n ],\n \"aliases\": [\"google\"],\n },\n \"groq\": {\n \"label\": \"Groq\",\n \"kind\": \"cloud\",\n \"key_env\": \"GROQ_API_KEY\",\n \"key_url\": \"https://console.groq.com/keys\",\n \"steps\": [\n \"Create an API key: https://console.groq.com/keys\",\n \"Configure: adk keys set groq gsk_... (stores + vault-syncs; or export GROQ_API_KEY=gsk_...)\",\n ],\n },\n \"together\": {\n \"label\": \"Together AI\",\n \"kind\": \"cloud\",\n \"key_env\": \"TOGETHER_API_KEY\",\n \"key_url\": \"https://api.together.xyz/settings/api-keys\",\n \"steps\": [\n \"Create an API key: https://api.together.xyz/settings/api-keys\",\n \"Configure: adk keys set together <key> (stores + vault-syncs)\",\n ],\n },\n \"gateway\": {\n \"label\": \"Aitherium gateway (hosted brain — free tier)\",\n \"kind\": \"gateway\",\n \"key_env\": \"AITHER_API_KEY\",\n \"key_url\": \"https://gateway.aitherium.com\",\n \"steps\": [\n \"Register (free): aither register (device flow, no card)\",\n \"Or grab a key at https://gateway.aitherium.com then: export AITHER_API_KEY=aither_sk_...\",\n \"Test: adk ask \\\"hello\\\" (gateway is the default fallback)\",\n ],\n \"aliases\": [\"aitherium\", \"aither\"],\n },\n \"ollama\": {\n \"label\": \"Ollama (local, easiest self-hosted)\",\n \"kind\": \"local\",\n \"steps\": [\n \"Install: https://ollama.com/download (winget install Ollama.Ollama / brew install ollama)\",\n \"Start + pull a model: ollama serve then ollama pull gemma4:4b\",\n \"Configure: adk backend set ollama (auto-detects localhost:11434)\",\n \"Test: adk ask \\\"hello\\\" --backend ollama\",\n ],\n },\n \"vllm\": {\n \"label\": \"vLLM (local GPU server, OpenAI-compatible)\",\n \"kind\": \"local\",\n \"steps\": [\n \"Easiest: let adk provision it — adk node bootstrap (detects your GPU, serves a fitting model)\",\n \"Manual: docker run --gpus all -p 8000:8000 vllm/vllm-openai --model <hf-model>\",\n \"Configure: adk backend set vllm --base-url http://localhost:8000/v1 --model <served-model>\",\n ],\n },\n \"llamacpp\": {\n \"label\": \"llama.cpp (CPU-friendly local server)\",\n \"kind\": \"local\",\n \"steps\": [\n \"Get llama.cpp: https://github.com/ggml-org/llama.cpp/releases (or adk node bootstrap on CPU boxes)\",\n \"Serve OpenAI-compatible: llama-server -m model.gguf --port 8080\",\n \"Configure: adk backend set llamacpp --base-url http://localhost:8080/v1\",\n ],\n },\n \"lmstudio\": {\n \"label\": \"LM Studio (desktop app, local server)\",\n \"kind\": \"local\",\n \"steps\": [\n \"Install LM Studio: https://lmstudio.ai — download a model, enable the local server\",\n \"Configure: adk backend set lmstudio --base-url http://localhost:1234/v1\",\n ],\n },\n \"genesis\": {\n \"label\": \"AitherOS Genesis (your own fleet)\",\n \"kind\": \"local\",\n \"steps\": [\n \"Requires a running AitherOS deployment (Genesis on :8001).\",\n \"Configure: adk backend set genesis --base-url https://localhost:8001/v1\",\n ],\n },\n}\n\n# Fleet/local MODEL names people try as if they were providers — map each to the\n# provider guide that actually serves it, with a hint line.\nMODEL_HINTS: Dict[str, str] = {\n \"bonsai\": \"vllm\",\n \"nemotron\": \"gateway\",\n \"nemotron-orchestrator\": \"gateway\",\n \"aither-orchestrator\": \"gateway\",\n \"gemma4\": \"ollama\",\n \"gemma4-12b\": \"gateway\",\n \"qwen3.6\": \"gateway\",\n \"qwen36\": \"gateway\",\n \"kimi-k3\": \"moonshot\",\n \"kimi\": \"moonshot\",\n}\n\n_ALIAS_INDEX: Dict[str, str] = {}\nfor _name, _g in PROVIDER_GUIDES.items():\n _ALIAS_INDEX[_name] = _name\n for _a in _g.get(\"aliases\", []): # type: ignore[union-attr]\n _ALIAS_INDEX[str(_a)] = _name\n\n\ndef resolve_name(name: str) -> Optional[str]:\n \"\"\"Resolve a provider name, alias, or known model name to a guide key.\"\"\"\n n = (name or \"\").strip().lower()\n if n in _ALIAS_INDEX:\n return _ALIAS_INDEX[n]\n if n in MODEL_HINTS:\n return MODEL_HINTS[n]\n # Longest prefix wins so \"gemma4-12b\" (gateway) beats \"gemma4\" (ollama).\n for prefix in sorted(MODEL_HINTS, key=len, reverse=True):\n if n.startswith(prefix):\n return MODEL_HINTS[prefix]\n return None\n\n\ndef guide(name: str) -> str:\n \"\"\"Full setup guide for a provider (or a model name that maps to one).\"\"\"\n key = resolve_name(name)\n if not key:\n return provider_menu(header=f\"Unknown backend '{name}'. Available backends:\")\n g = PROVIDER_GUIDES[key]\n lines: List[str] = [f\"{g['label']} — setup:\"]\n n = (name or \"\").strip().lower()\n if n in MODEL_HINTS and key != n:\n lines.insert(0, f\"'{name}' is a model served via the '{key}' backend.\")\n for i, step in enumerate(g[\"steps\"], 1): # type: ignore[index]\n lines.append(f\" {i}. {step}\")\n return \"\\n\".join(lines)\n\n\ndef missing_key_message(name: str) -> str:\n \"\"\"Error message for a cloud provider selected without its API key —\n states the problem AND the exact fix, never a bare 'no key'.\"\"\"\n key = resolve_name(name) or name\n g = PROVIDER_GUIDES.get(key)\n if not g or g.get(\"kind\") == \"local\":\n return guide(name)\n env = g.get(\"key_env\", f\"{key.upper()}_API_KEY\")\n return (\n f\"{g['label']}: no API key configured ({env} not set).\\n\" + guide(key)\n )\n\n\ndef provider_menu(header: str = \"Available LLM backends:\") -> str:\n \"\"\"One-line-per-provider menu, grouped cloud/gateway/local, with the\n guide command so the user always has a next step.\"\"\"\n groups = {\"gateway\": [], \"cloud\": [], \"local\": []} # type: Dict[str, List[str]]\n for name, g in PROVIDER_GUIDES.items():\n groups[str(g[\"kind\"])].append(f\" {name:<10} {g['label']}\")\n out = [header]\n for kind, title in ((\"gateway\", \"Hosted (free tier)\"), (\"cloud\", \"Cloud APIs\"), (\"local\", \"Self-hosted / local\")):\n out.append(f\"{title}:\")\n out.extend(sorted(groups[kind]))\n out.append(\"Setup help for any of them: adk backend guide <name>\")\n return \"\\n\".join(out)\n"}}, "adk/templates/site-ingest/.env.example": {"ba68a8c507f455796092602b928f9b7f39aa0b05": {"t": 1785745144.8995976, "n": 811, "h": "c0927806f16848af", "c": "# Site Ingest Studio — environment configuration\n# Copy this to .env and fill in your API keys for your chosen LLM provider.\n\n# LLM Provider selection (anthropic, openai, deepseek, ollama)\n# SITE_INGEST_PROVIDER=anthropic\n# SITE_INGEST_MODEL=claude-opus-4-1 # optional; uses provider default if unset\n\n# Anthropic (Claude)\n# ANTHROPIC_API_KEY=sk-ant-...\n\n# OpenAI\n# OPENAI_API_KEY=sk-...\n\n# DeepSeek\n# DEEPSEEK_API_KEY=sk-...\n\n# Ollama (local, no key needed — set OLLAMA_HOST if not localhost:11434)\n# OLLAMA_HOST=http://localhost:11434\n\n# Optional: AitherBrowser for screenshot capture (if available)\n# AITHER_BROWSER_URL=http://localhost:8200\n\n# Optional: custom data directory (defaults to .data/ next to serve.py)\n# AITHER_DATA_DIR=/path/to/data\n\n# Log level (DEBUG, INFO, WARNING, ERROR)\n# LOG_LEVEL=INFO\n"}, "ea8a01bb2d4b352c47ad086182725eb7ff2cb612": {"t": 1785748149.6041527, "n": 811, "h": "c0927806f16848af", "c": "# Site Ingest Studio — environment configuration\n# Copy this to .env and fill in your API keys for your chosen LLM provider.\n\n# LLM Provider selection (anthropic, openai, deepseek, ollama)\n# SITE_INGEST_PROVIDER=anthropic\n# SITE_INGEST_MODEL=claude-opus-4-1 # optional; uses provider default if unset\n\n# Anthropic (Claude)\n# ANTHROPIC_API_KEY=sk-ant-...\n\n# OpenAI\n# OPENAI_API_KEY=sk-...\n\n# DeepSeek\n# DEEPSEEK_API_KEY=sk-...\n\n# Ollama (local, no key needed — set OLLAMA_HOST if not localhost:11434)\n# OLLAMA_HOST=http://localhost:11434\n\n# Optional: AitherBrowser for screenshot capture (if available)\n# AITHER_BROWSER_URL=http://localhost:8200\n\n# Optional: custom data directory (defaults to .data/ next to serve.py)\n# AITHER_DATA_DIR=/path/to/data\n\n# Log level (DEBUG, INFO, WARNING, ERROR)\n# LOG_LEVEL=INFO\n"}, "1ecca3179d1afd140a4b79085ac02803f682883b": {"t": 1785748151.7591693, "n": 811, "h": "c0927806f16848af", "c": "# Site Ingest Studio — environment configuration\n# Copy this to .env and fill in your API keys for your chosen LLM provider.\n\n# LLM Provider selection (anthropic, openai, deepseek, ollama)\n# SITE_INGEST_PROVIDER=anthropic\n# SITE_INGEST_MODEL=claude-opus-4-1 # optional; uses provider default if unset\n\n# Anthropic (Claude)\n# ANTHROPIC_API_KEY=sk-ant-...\n\n# OpenAI\n# OPENAI_API_KEY=sk-...\n\n# DeepSeek\n# DEEPSEEK_API_KEY=sk-...\n\n# Ollama (local, no key needed — set OLLAMA_HOST if not localhost:11434)\n# OLLAMA_HOST=http://localhost:11434\n\n# Optional: AitherBrowser for screenshot capture (if available)\n# AITHER_BROWSER_URL=http://localhost:8200\n\n# Optional: custom data directory (defaults to .data/ next to serve.py)\n# AITHER_DATA_DIR=/path/to/data\n\n# Log level (DEBUG, INFO, WARNING, ERROR)\n# LOG_LEVEL=INFO\n"}, "56b20a2050c33760e93aad40be962d2fda772a91": {"t": 1785748153.9378529, "n": 811, "h": "c0927806f16848af", "c": "# Site Ingest Studio — environment configuration\n# Copy this to .env and fill in your API keys for your chosen LLM provider.\n\n# LLM Provider selection (anthropic, openai, deepseek, ollama)\n# SITE_INGEST_PROVIDER=anthropic\n# SITE_INGEST_MODEL=claude-opus-4-1 # optional; uses provider default if unset\n\n# Anthropic (Claude)\n# ANTHROPIC_API_KEY=sk-ant-...\n\n# OpenAI\n# OPENAI_API_KEY=sk-...\n\n# DeepSeek\n# DEEPSEEK_API_KEY=sk-...\n\n# Ollama (local, no key needed — set OLLAMA_HOST if not localhost:11434)\n# OLLAMA_HOST=http://localhost:11434\n\n# Optional: AitherBrowser for screenshot capture (if available)\n# AITHER_BROWSER_URL=http://localhost:8200\n\n# Optional: custom data directory (defaults to .data/ next to serve.py)\n# AITHER_DATA_DIR=/path/to/data\n\n# Log level (DEBUG, INFO, WARNING, ERROR)\n# LOG_LEVEL=INFO\n"}, "9c6f6fb160e7b9b4fc276902be60bf38179cb3f0": {"t": 1785748157.1084387, "n": 811, "h": "c0927806f16848af", "c": "# Site Ingest Studio — environment configuration\n# Copy this to .env and fill in your API keys for your chosen LLM provider.\n\n# LLM Provider selection (anthropic, openai, deepseek, ollama)\n# SITE_INGEST_PROVIDER=anthropic\n# SITE_INGEST_MODEL=claude-opus-4-1 # optional; uses provider default if unset\n\n# Anthropic (Claude)\n# ANTHROPIC_API_KEY=sk-ant-...\n\n# OpenAI\n# OPENAI_API_KEY=sk-...\n\n# DeepSeek\n# DEEPSEEK_API_KEY=sk-...\n\n# Ollama (local, no key needed — set OLLAMA_HOST if not localhost:11434)\n# OLLAMA_HOST=http://localhost:11434\n\n# Optional: AitherBrowser for screenshot capture (if available)\n# AITHER_BROWSER_URL=http://localhost:8200\n\n# Optional: custom data directory (defaults to .data/ next to serve.py)\n# AITHER_DATA_DIR=/path/to/data\n\n# Log level (DEBUG, INFO, WARNING, ERROR)\n# LOG_LEVEL=INFO\n"}, "69a5175b59cfad85081af9f09a77d1356306fc29": {"t": 1785748159.5757675, "n": 811, "h": "c0927806f16848af", "c": "# Site Ingest Studio — environment configuration\n# Copy this to .env and fill in your API keys for your chosen LLM provider.\n\n# LLM Provider selection (anthropic, openai, deepseek, ollama)\n# SITE_INGEST_PROVIDER=anthropic\n# SITE_INGEST_MODEL=claude-opus-4-1 # optional; uses provider default if unset\n\n# Anthropic (Claude)\n# ANTHROPIC_API_KEY=sk-ant-...\n\n# OpenAI\n# OPENAI_API_KEY=sk-...\n\n# DeepSeek\n# DEEPSEEK_API_KEY=sk-...\n\n# Ollama (local, no key needed — set OLLAMA_HOST if not localhost:11434)\n# OLLAMA_HOST=http://localhost:11434\n\n# Optional: AitherBrowser for screenshot capture (if available)\n# AITHER_BROWSER_URL=http://localhost:8200\n\n# Optional: custom data directory (defaults to .data/ next to serve.py)\n# AITHER_DATA_DIR=/path/to/data\n\n# Log level (DEBUG, INFO, WARNING, ERROR)\n# LOG_LEVEL=INFO\n"}, "ddb9269595b4f21a88bad214cb02e928ebfe856e": {"t": 1785748161.8690965, "n": 811, "h": "c0927806f16848af", "c": "# Site Ingest Studio — environment configuration\n# Copy this to .env and fill in your API keys for your chosen LLM provider.\n\n# LLM Provider selection (anthropic, openai, deepseek, ollama)\n# SITE_INGEST_PROVIDER=anthropic\n# SITE_INGEST_MODEL=claude-opus-4-1 # optional; uses provider default if unset\n\n# Anthropic (Claude)\n# ANTHROPIC_API_KEY=sk-ant-...\n\n# OpenAI\n# OPENAI_API_KEY=sk-...\n\n# DeepSeek\n# DEEPSEEK_API_KEY=sk-...\n\n# Ollama (local, no key needed — set OLLAMA_HOST if not localhost:11434)\n# OLLAMA_HOST=http://localhost:11434\n\n# Optional: AitherBrowser for screenshot capture (if available)\n# AITHER_BROWSER_URL=http://localhost:8200\n\n# Optional: custom data directory (defaults to .data/ next to serve.py)\n# AITHER_DATA_DIR=/path/to/data\n\n# Log level (DEBUG, INFO, WARNING, ERROR)\n# LOG_LEVEL=INFO\n"}, "96ee06ac840a862f048c172ce5c682eb178a19f8": {"t": 1785748163.981622, "n": 811, "h": "c0927806f16848af", "c": "# Site Ingest Studio — environment configuration\n# Copy this to .env and fill in your API keys for your chosen LLM provider.\n\n# LLM Provider selection (anthropic, openai, deepseek, ollama)\n# SITE_INGEST_PROVIDER=anthropic\n# SITE_INGEST_MODEL=claude-opus-4-1 # optional; uses provider default if unset\n\n# Anthropic (Claude)\n# ANTHROPIC_API_KEY=sk-ant-...\n\n# OpenAI\n# OPENAI_API_KEY=sk-...\n\n# DeepSeek\n# DEEPSEEK_API_KEY=sk-...\n\n# Ollama (local, no key needed — set OLLAMA_HOST if not localhost:11434)\n# OLLAMA_HOST=http://localhost:11434\n\n# Optional: AitherBrowser for screenshot capture (if available)\n# AITHER_BROWSER_URL=http://localhost:8200\n\n# Optional: custom data directory (defaults to .data/ next to serve.py)\n# AITHER_DATA_DIR=/path/to/data\n\n# Log level (DEBUG, INFO, WARNING, ERROR)\n# LOG_LEVEL=INFO\n"}, "4493d5341a0aaf14d5c9c2a8634a5d3b6ba6ddff": {"t": 1785748166.0929675, "n": 811, "h": "c0927806f16848af", "c": "# Site Ingest Studio — environment configuration\n# Copy this to .env and fill in your API keys for your chosen LLM provider.\n\n# LLM Provider selection (anthropic, openai, deepseek, ollama)\n# SITE_INGEST_PROVIDER=anthropic\n# SITE_INGEST_MODEL=claude-opus-4-1 # optional; uses provider default if unset\n\n# Anthropic (Claude)\n# ANTHROPIC_API_KEY=sk-ant-...\n\n# OpenAI\n# OPENAI_API_KEY=sk-...\n\n# DeepSeek\n# DEEPSEEK_API_KEY=sk-...\n\n# Ollama (local, no key needed — set OLLAMA_HOST if not localhost:11434)\n# OLLAMA_HOST=http://localhost:11434\n\n# Optional: AitherBrowser for screenshot capture (if available)\n# AITHER_BROWSER_URL=http://localhost:8200\n\n# Optional: custom data directory (defaults to .data/ next to serve.py)\n# AITHER_DATA_DIR=/path/to/data\n\n# Log level (DEBUG, INFO, WARNING, ERROR)\n# LOG_LEVEL=INFO\n"}, "6eb435633dc15b5a4241f9729ad3bac72fa8c60c": {"t": 1785748168.2691846, "n": 811, "h": "c0927806f16848af", "c": "# Site Ingest Studio — environment configuration\n# Copy this to .env and fill in your API keys for your chosen LLM provider.\n\n# LLM Provider selection (anthropic, openai, deepseek, ollama)\n# SITE_INGEST_PROVIDER=anthropic\n# SITE_INGEST_MODEL=claude-opus-4-1 # optional; uses provider default if unset\n\n# Anthropic (Claude)\n# ANTHROPIC_API_KEY=sk-ant-...\n\n# OpenAI\n# OPENAI_API_KEY=sk-...\n\n# DeepSeek\n# DEEPSEEK_API_KEY=sk-...\n\n# Ollama (local, no key needed — set OLLAMA_HOST if not localhost:11434)\n# OLLAMA_HOST=http://localhost:11434\n\n# Optional: AitherBrowser for screenshot capture (if available)\n# AITHER_BROWSER_URL=http://localhost:8200\n\n# Optional: custom data directory (defaults to .data/ next to serve.py)\n# AITHER_DATA_DIR=/path/to/data\n\n# Log level (DEBUG, INFO, WARNING, ERROR)\n# LOG_LEVEL=INFO\n"}, "ff6ea0e02e924cccebb33929465bc5f3cc6b2b89": {"t": 1785748170.6023517, "n": 811, "h": "c0927806f16848af", "c": "# Site Ingest Studio — environment configuration\n# Copy this to .env and fill in your API keys for your chosen LLM provider.\n\n# LLM Provider selection (anthropic, openai, deepseek, ollama)\n# SITE_INGEST_PROVIDER=anthropic\n# SITE_INGEST_MODEL=claude-opus-4-1 # optional; uses provider default if unset\n\n# Anthropic (Claude)\n# ANTHROPIC_API_KEY=sk-ant-...\n\n# OpenAI\n# OPENAI_API_KEY=sk-...\n\n# DeepSeek\n# DEEPSEEK_API_KEY=sk-...\n\n# Ollama (local, no key needed — set OLLAMA_HOST if not localhost:11434)\n# OLLAMA_HOST=http://localhost:11434\n\n# Optional: AitherBrowser for screenshot capture (if available)\n# AITHER_BROWSER_URL=http://localhost:8200\n\n# Optional: custom data directory (defaults to .data/ next to serve.py)\n# AITHER_DATA_DIR=/path/to/data\n\n# Log level (DEBUG, INFO, WARNING, ERROR)\n# LOG_LEVEL=INFO\n"}, "91071ca57db3ab979b234198038fc9e32b34fdc4": {"t": 1785748172.8101473, "n": 811, "h": "c0927806f16848af", "c": "# Site Ingest Studio — environment configuration\n# Copy this to .env and fill in your API keys for your chosen LLM provider.\n\n# LLM Provider selection (anthropic, openai, deepseek, ollama)\n# SITE_INGEST_PROVIDER=anthropic\n# SITE_INGEST_MODEL=claude-opus-4-1 # optional; uses provider default if unset\n\n# Anthropic (Claude)\n# ANTHROPIC_API_KEY=sk-ant-...\n\n# OpenAI\n# OPENAI_API_KEY=sk-...\n\n# DeepSeek\n# DEEPSEEK_API_KEY=sk-...\n\n# Ollama (local, no key needed — set OLLAMA_HOST if not localhost:11434)\n# OLLAMA_HOST=http://localhost:11434\n\n# Optional: AitherBrowser for screenshot capture (if available)\n# AITHER_BROWSER_URL=http://localhost:8200\n\n# Optional: custom data directory (defaults to .data/ next to serve.py)\n# AITHER_DATA_DIR=/path/to/data\n\n# Log level (DEBUG, INFO, WARNING, ERROR)\n# LOG_LEVEL=INFO\n"}, "3ca1167a923725a0853ce1197f425dbefd8efd92": {"t": 1785748175.9647696, "n": 811, "h": "c0927806f16848af", "c": "# Site Ingest Studio — environment configuration\n# Copy this to .env and fill in your API keys for your chosen LLM provider.\n\n# LLM Provider selection (anthropic, openai, deepseek, ollama)\n# SITE_INGEST_PROVIDER=anthropic\n# SITE_INGEST_MODEL=claude-opus-4-1 # optional; uses provider default if unset\n\n# Anthropic (Claude)\n# ANTHROPIC_API_KEY=sk-ant-...\n\n# OpenAI\n# OPENAI_API_KEY=sk-...\n\n# DeepSeek\n# DEEPSEEK_API_KEY=sk-...\n\n# Ollama (local, no key needed — set OLLAMA_HOST if not localhost:11434)\n# OLLAMA_HOST=http://localhost:11434\n\n# Optional: AitherBrowser for screenshot capture (if available)\n# AITHER_BROWSER_URL=http://localhost:8200\n\n# Optional: custom data directory (defaults to .data/ next to serve.py)\n# AITHER_DATA_DIR=/path/to/data\n\n# Log level (DEBUG, INFO, WARNING, ERROR)\n# LOG_LEVEL=INFO\n"}, "6130f720cc2fde22e8e71b3e6155268d0fcf7c3f": {"t": 1786067292.3609927, "n": 811, "h": "c0927806f16848af", "c": "# Site Ingest Studio — environment configuration\n# Copy this to .env and fill in your API keys for your chosen LLM provider.\n\n# LLM Provider selection (anthropic, openai, deepseek, ollama)\n# SITE_INGEST_PROVIDER=anthropic\n# SITE_INGEST_MODEL=claude-opus-4-1 # optional; uses provider default if unset\n\n# Anthropic (Claude)\n# ANTHROPIC_API_KEY=sk-ant-...\n\n# OpenAI\n# OPENAI_API_KEY=sk-...\n\n# DeepSeek\n# DEEPSEEK_API_KEY=sk-...\n\n# Ollama (local, no key needed — set OLLAMA_HOST if not localhost:11434)\n# OLLAMA_HOST=http://localhost:11434\n\n# Optional: AitherBrowser for screenshot capture (if available)\n# AITHER_BROWSER_URL=http://localhost:8200\n\n# Optional: custom data directory (defaults to .data/ next to serve.py)\n# AITHER_DATA_DIR=/path/to/data\n\n# Log level (DEBUG, INFO, WARNING, ERROR)\n# LOG_LEVEL=INFO\n"}}, "CHANGELOG.md": {"ba68a8c507f455796092602b928f9b7f39aa0b05": {"t": 1785746552.0252745, "n": 103440, "h": "197c535a410e6dc1", "c": "# Changelog\n\nAll notable changes to aither-adk will be documented in this file.\n\n## [2.47.0] - 2026-08-02\n\n### Added — Bonsai as a first-class default backend for low-resource hardware\n\n- **`pick_backend()` now auto-detects and recommends Bonsai** for CPU-only and\n <6GB VRAM hardware. If the Bonsai container is already running or the GGUF is\n downloaded, it is selected automatically — no explicit `--backend bonsai-local`\n needed. This means `adk quickstart` on a phone, a Pi, or a laptop without a GPU\n now defaults to local inference instead of requiring cloud fallback.\n- **New `bonsai` hardware profile** (`profiles/bonsai.yaml`) — targets 0 GPU,\n 4GB RAM minimum. Documents platform-specific binaries and expected throughput\n for Android (Termux), iOS, Raspberry Pi, Windows, macOS, and Linux.\n- **`bonsai-4b` variant** for ultra-constrained devices (2GB RAM, Android phones).\n- **README hardware profiles table updated** — Bonsai leads the table as the\n lowest-resource option. `adk setup --tier bonsai` documented.\n\n## [2.46.0] - 2026-07-31\n\n## [2.45.0] - 2026-07-29\n\n### Fixed — streaming against an AitherOS-typed SSE backend returned a silent EMPTY answer\n\n- **`OpenAIProvider.chat_stream` now parses BOTH stream dialects.** MicroScheduler's\n `/v1/chat/completions` facade (and Genesis) answer `stream=true` with AitherOS-typed\n SSE events (`event: token`, `data: {\"t\": \"...\"}`) rather than OpenAI chunks\n (`choices[].delta.content`). Parsing only the OpenAI shape yielded **zero chunks and\n no error** — the agent loop (`stream_react`) completed \"successfully\" with an empty\n answer while non-stream `chat()` worked against the same endpoint, which made every\n `/chat/stream` turn on an MS-backed daemon silently empty. The provider now accepts\n typed `token`/`answer`/`complete`/`error` events alongside OpenAI chunks; a trailing\n `answer` event never duplicates already-streamed tokens. Two regression tests pin\n both dialect paths (`tests/test_llm_providers.py`).\n\n## [2.44.0] - 2026-07-27\n\n### Fixed — `adk enroll` silently registered devices into the WRONG TENANT\n\n- **`adk enroll` succeeded without an identity and bound the device to the tenant\n `\"personal\"` instead of the caller's workspace.** `_extract_tenant_slug()`\n (`fleet_enroll.py:217`) falls back to `\"personal\"` when `~/.aither/auth.json` is\n absent, so enrolling a fresh machine reported success while registering it\n somewhere the owner cannot see. Nothing failed, nothing warned, and the endpoint\n simply never appeared in their workspace — the fail-open shape where the\n operation reports success and does the wrong thing.\n\n This bites hardest on unattended installs (phones, headless VMs, cloud images),\n which are exactly the machines nobody is watching closely enough to notice.\n\n `cmd_enroll` now **refuses** when no identity is present, and names the two ways\n to supply one:\n\n adk login # browser flow\n adk login --api-key <key> # headless / phone / VM\n\n Verified by running it with an empty `HOME`: refuses, **exit 1**. (`AITHER_NODE_TOKEN`\n is accepted as an identity, so token-based fleet enrolment is unaffected.)\n\n Found while building a one-command self-host installer for Android's Linux\n terminal, by running the installer rather than reasoning about it.\n\n## [2.39.0] - 2026-07-25\n\n### Fixed — containerized agents were unreachable\n\n- **`docker` runtime: added `-i` to `docker run`.** Without it Docker does not\n attach the container's stdin, so *every* containerized stdio agent (acp, mcp)\n silently never received a request. Measured against a real container: no `-i` →\n the child read nothing; with `-i` → the request round-trips. A real customer\n pack *is* a container, so this runtime was effectively unusable.\n- **`runtime.cmd` is now `shlex`-split** for the `docker` and `node` runtimes.\n `cmd: \"python -u server.py\"` was passed as a single argv element, so docker\n tried to exec a binary literally named `python -u server.py`.\n- `Supervisor._build_command()` extracted from `spawn()` so argv is testable\n without launching a process — the missing `-i` survived precisely because it\n wasn't.\n- Env is deliberately **not** forwarded as `-e KEY=value`: that puts credentials\n in argv, readable by any local `ps` / `docker inspect`.\n\n### Fixed — CodeActLoop `cell_timeout` did not bound CPU-bound cells\n\n- `asyncio.wait_for` cannot preempt a coroutine that never yields, so\n `while True: pass` was never interrupted and hung the agent indefinitely. Now\n also bounded by a line-level trace deadline (`CellTimeout`), scoped to the\n cell's own frames so a cell that awaits cannot fire the deadline inside\n unrelated coroutines.\n- The `CodeActLoop` docstring now states plainly that **it is not a sandbox**, and\n names what a cell can actually do (`import os`, `subprocess`, `open(..., \"w\")`).\n Do not run cells from an untrusted source.\n\n### Added — real containment for CodeAct cells\n\n- **`CodeActLoop(executor=DockerCellExecutor())`** runs each cell in a container\n with `--network none`, a read-only root filesystem plus a noexec/nosuid tmpfs,\n `--cap-drop ALL`, `no-new-privileges`, memory and pid caps, a non-root user, and\n `python -I`. The escapes that succeed in-process — writing a host file, network\n egress, running as root — all fail there, and an infinite loop's container is\n **killed** rather than merely interrupted.\n- `return_result()` crosses the process boundary via a stdout sentinel and is\n deliberately **not** `eval`'d back into the host.\n- Trade-off stated plainly: an isolated cell is a fresh process, so there is no\n live host namespace and no cross-cell state. Containment *and* a live namespace\n together need fork-from-warm snapshotting (`adk/forkd_client.py`'s Firecracker\n target), which requires Linux ≥ 5.7 + KVM.\n- The in-process default is unchanged. Do not point CodeAct at untrusted input\n without `executor=`.\n\n### Fixed — TLS and test-suite integrity\n\n- **`adk/cli.py` sent an `Authorization: Bearer` API key over TLS with\n verification disabled**, exposing it to a machine-in-the-middle. Now routed\n through `adk._tls.tls_verify()`.\n- The `verify=False` ratchet regex-scanned raw lines, so docstrings that merely\n *named* the antipattern were reported as violations — noise that hid the real\n hole above. It now walks the AST for actual keyword arguments.\n- Five `test_inference_proxy` failures were stale assertions pinning the\n pre-MicroScheduler routing and a 5-model roster (it is 6 since `aither-bonsai`).\n Assertions now derive from the roster and encode the tier-access rule, so a\n future model addition doesn't break them.\n\n### Fixed — MCP stdio hung silently on Windows\n\n- `adk/mcp_stdio.py` and `adk/shell/mcp_bridge.py` attached stdin with\n `loop.connect_read_pipe`, which fails inside the Proactor loop's read handler\n such that the read future never resolves — the server didn't crash, it just\n never answered. Both now use the new shared `adk.stdio_compat`\n (`ThreadStdinReader` / `ThreadStdoutWriter`), which `acp_server.py` also uses.\n\n### Added — per-install pack credentials are now actually used\n\n- `adk/pack_credentials.py` was orphaned: mint/revoke were never called, so pack\n installs kept using the shared credential the module was written to replace.\n Both install paths now mint, and a reinstall revokes the outgoing install's\n credential before its metadata becomes unreachable.\n- **New `/packs uninstall <pack-id>`** (aliases `remove`, `rm`) — revokes the\n per-install credential first, then removes the pack. Previously the only revoke\n path was a reinstall, so deleting a pack any other way left a live,\n unrevocable credential. The target is confined to the pack root.\n\n### Fixed — packaging\n\n- The brew formula's own `sha256` was left as `PLACEHOLDER_SHA256` on every\n release, so `brew install` could never verify the download.\n `sync_versions.py` now fills it from PyPI and `--check` **fails** when a\n published version still carries a placeholder or a stale digest.\n- `sync_brew` rewrote *every* `url` in the formula, repointing the `httpx`,\n `pyyaml`, `fastapi` and `uvicorn` `resource` blocks at the aither-adk tarball.\n Fixed with `count=1`; the resource digests are now real.\n- `adk/__init__.py`'s source-checkout fallback `__version__` had been stale since\n 2.32.0 because the canonical release path never ran `sync_versions.py`. That\n check now runs in `sync-adk.yml`.\n\n## [2.38.1] - 2026-07-24\n\nRelease plumbing only — 2.38.0 was tagged before the version manifests\n(npm/brew/winget) were synced, so its publish gate failed and it never reached\nPyPI. 2.38.1 is the same code with the manifests in sync. See 2.38.0 below for\nwhat actually changed.\n\n## [2.38.0] - 2026-07-24 (never published — superseded by 2.38.1)\n\n### Fixed — `adk join` could only ever have worked for us\n\n- **`adk join` defaulted identity to `https://localhost:8115`** — a fleet-INTERNAL\n address. `adk join` is the one command a stranger runs on their own GPU box, so on\n any machine not already running AitherOS locally, step 1 (the GitHub device flow)\n hit a refused connection, and so did node registration and the mesh-key issue. The\n conductor default was already public, which is exactly why this went unnoticed.\n Identity now defaults to `https://idp.aitherium.com`.\n\n This fix landed *after* the 2.37.0 tag, so **2.37.0 and every earlier release on\n PyPI still ship the broken default** — 2.38.0 is the first release a new\n contributor can actually run. Verified live from a non-fleet machine (DGX):\n `POST /auth/github/device/start` → HTTP 200 with a real `user_code` and\n `verification_uri`.\n\n- **Upgrade check compared versions as strings** — `latest != current` reported a\n downgrade as an available upgrade. Now uses `packaging.version` with a numeric-tuple\n fallback.\n\n## [2.37.0] - 2026-07-24\n\n### Added — Agent Onboarding Fabric: bring your own agent, or run a managed one\n\nOnboard an agent built on **any** framework, or let Aitherium run it for you.\n\n- **ACP (Agent Client Protocol), both directions** — `ACPClient` drives any ACP agent;\n `ACPServer`/`serve_stdio` exposes an adk agent to any ACP host (Zed, VS Code,\n JetBrains). Both proven live against Zed's reference `agent-client-protocol`.\n- **Universal Agent Pack** — `AgentPackManifest` + `Supervisor` describe and run an\n external agent across 6 frameworks × 6 protocols, fail-closed on anything unknown.\n- **Protocol drivers** — `get_driver()` returns an `http`, `langgraph_rest`, `a2a`, or\n `mcp` driver, so REST/graph-orchestrated agents can be *managed*, not just connected.\n- **Pack registry** — `PackRegistry.publish/browse/versions/get/yank/verify_digest`,\n SHA256-digested, semver-ordered, validation fail-closed before anything is written.\n- **Managed identity** — `ManagedAgentIdentityProvider` with a real gateway minter and a\n PROVISIONED→REGISTERED→ACTIVE→ROTATED→REVOKED lifecycle; default-deny `authorize()`.\n- **Zero-code connect templates** — `render_connect(framework, …)` emits config pinned to\n each framework's real schema.\n\n### Added — NOOA-style agent capabilities\n\n- **`CodeActLoop`** — code as action: the model writes Python cells that run against a\n persistent namespace over live objects, each cell AST-validated before execution, with\n per-cell timeouts and bounded observations.\n- **`ObjectRegistry` / `render_observation`** — pass by reference: large values become\n handles instead of flooding the context with serialized text.\n- **`EventLog` / `ContextBlocks`** — harness APIs the model itself can call to query its\n own event history and manage named context blocks in KV-cache-stable order.\n- **`has_ellipsis_body` / `@strategy`** — NOOA's `async def f(...) -> X: ...` ergonomic.\n- **`CodeValidator`** — best-effort AST validation (ported from NVIDIA's OO-Agents,\n Apache-2.0; NOTICE included). A defense-in-depth layer, not a sandbox boundary.\n- **`PredictLoop`** — single-shot strategy with structured output (`output_model=`) and\n validation retry; `Strategy` is now a first-class alias of `AgentLoop`, and both loops\n accept a per-call `model=` override.\n- **Typed I/O** — `AgentResult` validates its fields at construction and at the\n `Agent.run` boundary.\n\nAll new capabilities are exported lazily from the top-level `adk` namespace.\n\n## [2.27.0] - 2026-07-18\n\n### Added — Moonshot Kimi K3 provider (DeepSeek-equivalent cloud backend)\n\n- **New `moonshot` provider** across the LLM router: OpenAI-compatible\n `https://api.moonshot.ai/v1`, default model `kimi-k3` (131K context default, up to 1M;\n tool calls, JSON mode, structured output). Wired everywhere `deepseek` is:\n - `LLMRouter(provider=\"moonshot\")` + `_COMPAT_URLS`/`_COMPAT_MODELS`/model tiers\n (small/medium/large → `kimi-k3`)\n - Config: `moonshot_api_key` field (env `MOONSHOT_API_KEY`), saved-config load,\n provider-keys map (`provider_keys.json` key `moonshot`)\n - Auto-detect chain: tried after DeepSeek (config key, then env key)\n - Hybrid reasoning backend: `AITHER_REASONING_BACKEND=moonshot` for effort 7+\n - Reasoning tiers: `backend: moonshot` (alias `kimi`) via new `MoonshotBackend`\n - CLI: `adk backend set moonshot --api-key …`, `adk backend list` shows moonshot\n- No breaking changes; DeepSeek and all other providers unchanged.\n\n## [2.26.0] - 2026-07-16\n\n### Added — Aeon group-chat web UI pack + POST /aeon/stream SSE endpoint\n\n- **New `aeon` UI pack** (`adk/webui/packs/aeon/index.html`) — group chat for multi-agent\n discussion. Mirrors the `llamacpp` pack's auth + SSE pattern (bearer from URL fragment #k=),\n with a preset selector (balanced/creative/technical/security/minimal/duo_code/research) and\n distinct colored agent messages. Synthesis block highlighted. Agent content is HTML-escaped\n before render. Fully self-contained HTML+CSS+JS, light+dark, no external assets. (No\n temperature/max-tokens controls — Aeon runs a fixed multi-agent round and doesn't thread\n per-turn generation params, so exposing them would be a no-op.)\n- **POST /aeon/stream SSE endpoint** — accepts body {message, preset?, agents?, rounds?,\n session_id?}, emits SSE with: `session_start` (participants + orchestrator), `agent_message`\n per non-orchestrator, `synthesis` (if enabled), `error` on exception, `complete` (total tokens\n + latency). Mirrors /chat/stream structure — like every streaming endpoint here it is gated by\n the server auth middleware **when `AITHER_SERVER_API_KEY` is set** (a local `adk up` with no key\n is unauthenticated by design, same as /chat/stream).\n- **Default UI pack flipped to `llamacpp`** — `resolve_ui_pack_name()` now returns `llamacpp`\n instead of `console` when no env/config is set (new users see the clean chat, not the admin\n SPA; existing users with AITHER_AGENT_UI or saved config unaffected).\n\n### Added — swappable agent web UI packs (llama.cpp-style built-in chat)\n\n- The page an agent serves at `/` is now a **swappable UI pack**, so you can\n drop in, test, and deploy different chat frontends without touching the agent.\n Select with `adk ui set <name>` (persists to `~/.aither/config.json`) or the\n `$AITHER_AGENT_UI` env; `adk ui ls` lists packs, `adk ui path` shows what\n resolves.\n- **New built-in `llamacpp` pack** — a clean, self-contained llama.cpp-style\n chat UI (centered conversation, streaming token-by-token with a cursor, a\n Stop button, a settings drawer for temperature / max-tokens that is actually\n honored end-to-end — `/chat/stream` now forwards them to the provider —\n minimal safe markdown, light+dark). Hits the agent's own gated `/chat/stream`\n with the bearer read from the URL fragment (never sent in the URL). Built-ins\n also include `console` (the full admin SPA, still the default) and `minimal`.\n- **Drop-in custom packs**: a UI pack is just a folder with an `index.html` in\n `~/.aither/ui-packs/<name>/` (override via `$AITHER_UI_PACKS_DIR`) — no rebuild.\n- Resolution is **fail-soft**: an unknown/missing pack falls back\n console → minimal, so `/` is never blank.\n\n### Added — MCP-gateway-first platform access + mesh A2A trust\n\n- **Gateway-first platform access** (`adk/client/_gateway_mcp.py`, `GatewayMCPClient`):\n a self-hosted agent connects its MCP **client** to `mcp.aitherium.com` authenticated\n as the OWNER (device-flow / PAT / ACTA token), RBAC-scoped to that identity — so it can\n use platform tools + `get_secret` **whether or not the node runs local AitherOS\n services**. Fail-closed: no token → client disabled, the agent still runs. Wired into\n the server lifespan (`_connect_gateway_mcp`).\n- **`genesis` LLM backend**: `adk backend set genesis` points the agent brain at a local\n Genesis (`http://localhost:8001/v1`, model `workflow`) — the 5090/controller pattern\n where inference is served by the local fleet, not the cloud gateway or a raw vLLM.\n- **`adk mesh` command**: `adk mesh onboard` drives the Conductor 5-step onboard\n (`/v1/mesh/onboard` → registers this node's public key, returns the `endpoint:mesh`\n trust token); `adk mesh ls` lists peer nodes/agents (AitherMesh `/nodes` + Directory)\n with their A2A cards + invoke URLs for discovery.\n\n### Security — A2A inter-agent trust enforcement (fail-closed)\n\n- **`adk/a2a_trust.py`** (new): Ed25519 signature verification + trusted-key check\n (`AITHER_A2A_TRUSTED_KEYS`), opt-in via `AITHER_A2A_REQUIRE_TRUST` (default `false` for\n backward compatibility; `audit` logs untrusted, `true` rejects).\n- **Fixed a fail-OPEN in the inbound A2A handler** (`adk/a2a.py`): the trust check\n previously ran *only when signature headers were present*, so an unsigned request\n bypassed `AITHER_A2A_REQUIRE_TRUST` entirely. Now genuinely fail-closed — required mode\n with no/invalid signature → `403`.\n- **Sovereign template no longer scaffolds `verify=False`**: the generated\n `services/llm.py` now uses the project's own `tls_verify()` policy helper for every\n provider call instead of hardcoding disabled TLS verification.\n- **`adk/_tls.py`**: disabling verification via `AITHER_TLS_VERIFY=false` now emits a\n one-time loud warning (was silent) — the escape hatch stays, the silence goes.\n\n## [2.25.1] - 2026-07-15\n\n### Fixed — self-hosted OpenAI-compatible backends honor a custom base URL\n\n- `adk backend set llamacpp --base-url http://localhost:8090/v1 --model <id>` (and\n `vllm`/`lmstudio`, and the `AITHER_LLM_BASE_URL` env) now actually point the agent\n at YOUR server. Previously the base URL was saved but never read, so an explicit\n `--backend llamacpp` fell back to the provider's public API default (openai.com).\n New `Config.llm_base_url` field + saved-config `inference_url` wiring; the explicit\n backend path in `LLMRouter` now passes it for the local OpenAI-compatible family.\n This is what lets a self-hosted node run its OWN local model (e.g. a PrismML\n Bonsai llama.cpp) as the agent brain instead of ollama/cloud.\n\n## [2.25.0] - 2026-07-15\n\n### Added — node_bootstrap tool pack: bootstrap LLM inference on any hardware\n\n- **New bundled tool pack `node-bootstrap`** (`adk/toolpacks/node_bootstrap/`):\n seven agent tools — `node_detect_hardware`, `node_resolve_recipe`,\n `node_plan_deployment`, `node_apply`, `node_enroll`, `node_register_backend`,\n `node_verify` — plus a CLI shim (`python -m adk.toolpacks.node_bootstrap\n detect|resolve|plan|apply|enroll|register|verify`). Detect a box's hardware,\n resolve a deployment recipe, actually deploy the inference engine (docker\n compose / native / delegate), enroll with a control plane, register the\n backend, and live-verify a completion.\n- **9 hardware recipes** shipped in the wheel: `cpu-1bit-llamacpp`, `cpu-ollama`,\n `cuda-vllm-8gb/24gb/40gb`, `cuda-dual-stack-32gb` (32GB-card co-resident\n stack), `unified-memory-vllm` (DGX/Grace-class), `metal-ollama` (native —\n Docker on macOS has no Metal passthrough), `cloud-api` fallback. Resolution\n scores hardware against min/max VRAM bands, RAM, cores and unified memory;\n ties prefer higher tiers and self-contained recipes over fleet delegates.\n- Security posture: enrollment and backend registration are fail-closed\n (missing token/URL → error dict, never anonymous, never `verify=False`);\n tokens are redacted in outputs; all endpoints come from args/env\n (`AITHER_CONTROL_PLANE_URL`, `AITHER_GENESIS_URL`, `AITHER_AUTH_TOKEN`).\n\n## [2.24.0] - 2026-07-14\n\n### Changed — sync-family consolidation\n\n- **Removed dead code:** `adk/sync.py` (528 lines) deleted — it was unreachable,\n shadowed by the `adk/sync/` package (`import adk.sync` always resolved to the\n package; nothing loaded the module by path — verified).\n- **Grouped the sync domain modules into the `adk.sync` package:**\n `brain_sync`→`adk.sync.brain`, `files_sync`→`adk.sync.files`,\n `lockbox_sync`→`adk.sync.lockbox`, `secrets_sync`→`adk.sync.secrets`,\n `session_sync`→`adk.sync.sessions`, `settings_sync`→`adk.sync.settings`,\n `sync_watermark`→`adk.sync.watermark`. All in-tree importers updated. No\n back-compat shims were left because **zero external/monorepo code imported the\n old root paths** (verified) — a clean move, not a shimmed one. **Breaking only\n for code that imported `adk.<name>_sync` directly** (none known).\n\n### Docs — MCP module layering clarified\n\n- `adk.mcp` (enterprise AitherOS gateway client: auth/billing/token-tracking) and\n `adk.core.mcp` (minimal generic JSON-RPC adapter for any MCP server) are\n DISTINCT layers, not duplicates — each now carries a LAYER/ROLE/See-also\n docstring so they are not mistakenly consolidated.\n\n## [2.23.2] - 2026-07-14\n\n### Fixed — CompletionGate no longer false-fails on correct results\n\n- **Tolerant judge JSON extraction.** The LLM judge parsed its reply with `json.loads(text[first\"{\":last\"}\"])`, which raised `JSONDecodeError` (\"Extra data\") whenever the model appended prose after the verdict or emitted a second object — and `verify()` turned that into a fail-closed verdict, so a genuinely completed task reported `unverified`. New `_first_json_object()` uses `JSONDecoder.raw_decode` from the first `{` that begins a valid object and ignores trailing content (prose, markdown fences, extra objects). A judge that answers with no parseable JSON still fails closed (never a soft pass), now with a clear reason. The same extractor backs `_derive_criteria`.\n- **Bare code-like tokens are hard-checked.** A natural criterion like `output contains ADK_AUTO_OK` (no quotes) previously fell through to the judge; `hard_checks` now also matches bare identifier/code shapes (contains an underscore, or 4+ CAPS/digit chars), with a stopword guard so a descriptive uppercase word (`OUTPUT`, `CONTAIN`) never becomes a false required token. Quote a token to force it regardless.\n- +9 tests (24 total), including the exact trailing-data regression.\n\n## [2.23.0] - 2026-07-14\n\n### Removed — dead modules and internal integrations\n\n- **`adk.swarm`, `adk.provisioning_tools`, `adk.session_sync_integration`, `adk.addon_metering`** — four orphan modules with zero public usage have been deleted. These were internal prototypes kept for legacy monorepo consumers; public packages have not imported them since 2.21.0 (tracked as D-348). **Breaking for anyone directly importing these modules** (import will fail). They are no longer exempt from the orphan-module check.\n- **`adk.aither_bridge`** — the AitherOS-internal IRC ↔ chat gateway bridge is no longer shipped in the public aither-adk package (stripped at sync time). All public importers already degrade behind `except ImportError` (in `adk.server` and `adk.builtin_tools`), so public packages remain unaffected. The module stays in the private AitherOS monorepo for internal use.\n- **Identity document trim** — removed internal agent names from `adk/identities/aither.yaml` delegation guidance, replacing with role-generic wording (\"delegate code review, refactoring, security analysis, performance and testing to specialist agents when available\"). Effort-tier routing philosophy remains unchanged and is product design, not a leak.\n\n### Changed — genericized customer/product names in help text\n\n- CLI help/examples no longer name specific customer deployments (`--scope` template list and `deploy agent` name example use generic placeholders). Functional deploy paths are unchanged.\n\n## [2.22.1] - 2026-07-14\n\n### Fixed — real bugs surfaced by reviving the public CI (red since Jul 5)\n\n- **TLS**: bundled toolpacks (arc-brainpack, cloudflare) disabled certificate\n verification on gateway/vault calls; all now route `adk._tls.tls_verify()`\n (verified by default, internal CA bundle when present).\n- **`system_prompt` contract**: bundled-pack `[PACK DIRECTIVES]` were appended\n even to an explicit `system_prompt`; an explicit prompt now wins wholesale.\n- **Headless secrets**: `_derive_key()` used `os.getlogin()`, which raises\n `OSError` without a controlling terminal (CI, daemons, containers) — falls\n back to `getpass.getuser()` (same value on interactive boxes, so existing\n secret files keep decrypting).\n- **`pack_verifier`**: missing `cryptography` crashed with `UnboundLocalError`\n in the exception handler instead of failing closed; now degrades gracefully\n (and `cryptography` was added to the dev extra so CI tests the real path).\n- **`fleet_manager._kill_pid` (POSIX)**: reported success immediately after an\n asynchronous SIGTERM and never reaped children (zombies read as alive); now\n polls, escalates to SIGKILL, and reaps.\n- **Identity**: `aither` regained the `creative` tool category (dropped\n accidentally in a 2.7.0 WIP sweep).\n\n## [2.22.0] - 2026-07-14\n\n### Changed — internal toolkit relocation\n\n- **`adk.platform` relocated** — the internal aither-platform toolkit (ComfyUI/image-gen\n pipelines, fleet agent clients, parallel CLI/UI/tools stack) has been moved out of the\n adk SDK tree to the standalone internal `aither-platform` package at\n `AitherOS/agents/aither_platform/`. The public package remains unaffected (it already\n excluded `adk.platform` since 2.21.1). Internal AitherOS agent builds now install\n `aither-platform` separately. `adk platform` falls through to the same \"not available\"\n message on public installs; internal builds work via the relocated package.\n\n### Removed — backward-compat re-export deleted\n\n- **`adk.gateway` module removed** — the backward-compatibility re-export of `GatewayClient`\n from `adk.gateway` has been removed. Use `from adk.client import GatewayClient` instead\n (the established public API since 2.21.0). All internal importers have been updated.\n\n## [2.21.1] - 2026-07-14\n\n### Removed — internal toolkit and license-gated product no longer ship publicly\n\n- **`adk.platform`** — the internal aither-platform toolkit (ComfyUI/image-gen\n pipelines, fleet agent clients, a parallel cli/ui/tools stack) is no longer\n included in the public repo or the PyPI wheel. It was internal-only code that\n had been riding along since the aither-platform merge. `adk platform` now\n reports \"Platform toolkit not available\" on public installs; internal AitherOS\n builds are unaffected.\n- **`adk.formbridge`** — the FormBridge form-automation product is license-gated\n (PROFESSIONAL+); its implementation no longer ships in the free package. All\n import sites (`builtin_tools`, `server` route mount) already degrade behind\n `except ImportError`, so nothing else changes behavior.\n- Product test suites (`test_formbridge.py`, `test_encounter_keying.py`,\n `test_flow_tools.py`) are excluded from the public sync alongside them.\n\n## [2.21.0] - 2026-07-14\n\n### Added — Completion gate: verified-or-retried task execution\n\n- **`adk.gate.CompletionGate`** — a composable wrapper that closes the \"silent\n success\" gap: an agent's ReAct loop returns whatever the model produced and\n calls it done (`finish_reason = stop/max_steps/length`), which is a *liveness*\n signal, not a *completion* one — so an agent can \"succeed\" while doing nothing.\n `CompletionGate` runs the agent, verifies the result against acceptance\n criteria, retries with the failure fed back, and returns the response with\n `finish_reason` set to `verified` or an honest `unverified`. Never a false pass.\n - **Hard-first, un-soft-passable checks** (`adk.gate.hard_checks`): a filesystem\n path a criterion says must exist is checked on disk; a required quoted token\n must appear in the output. Only genuinely subjective criteria fall through to\n an optional LLM judge (reuses the agent's own model), which **fails closed**\n if unavailable.\n - **`gated_run(agent, task, criteria=..., max_retries=2)`** — one-shot helper.\n - `auto_criteria` (LLM writes the machine-checkable definition-of-done from the\n task), a `verifier=` override for custom checks (tests-pass / HTTP probe), and\n a `requires_action` short-circuit so it never fights human-approval pauses.\n - Compose it onto any agent exposing `run(task) -> resp{.content, .finish_reason}`\n (`AitherAgent`, `core.Agent`, or a thin adapter over a remote dispatch).\n - Exported from the top-level package: `from adk import CompletionGate, gated_run`.\n\n## [2.19.0] - 2026-07-12\n\n### Added — Eve agent importer + durable A2A tasks + canonical agent discovery\n\n- **`adk pack import <eve_agent_path>`** — convert external Eve agents to AitherADK packs\n (from GitHub or local filesystem). Maps Eve structure (instructions → system prompt,\n agent.ts → agent.yaml, skills/*.md → skills, connections/*.ts → MCP, tools/*.ts →\n tools/node verbatim) to AitherADK format with a single command. Eve agents (compiled\n manifest format, schema v35) become installable packs compatible with `adk pack install`.\n- **Durable A2A task lifecycle (`TaskManager` backed by FileStore)** — agent-to-agent\n tasks now survive container restarts. Tasks persist as append-only JSONL\n (`.adk/tasks.jsonl`); in-memory dict for fast access; `create_task()` / `update_status()` /\n `add_artifact()` / `add_message()` all save to disk. Google A2A v0.3.0 spec compliant.\n- **Canonical agent card discovery** — `A2AServer.mount()` now exposes:\n - `GET /.well-known/agent-card.json` — Agent Card (canonical; contains agent identity,\n capabilities, skills, endpoints)\n - `GET /.well-known/agent.json` — Agent Card (legacy, redirects with 308 to canonical path)\n - `POST /a2a` — JSON-RPC 2.0 for message send/task lifecycle\n - `GET /a2a/tasks/{id}/subscribe` — SSE streaming task updates\n- **Four built-in agent packs now complete and catalogued**:\n - `hermes` — architecture review & trade-off analysis\n - `openclaw` — web research & source verification\n - `claude-code` — feature development & debugging\n - `analyst` — structured data inference & anomaly detection\n All discoverable via `adk packs` / `adk install pack:<name>`.\n- **Tolerant tool-call JSON parsing** — malformed tool JSON (extra properties, missing\n name/arguments, string-encoded nested args, missing keys) is now gracefully degraded\n instead of silently failing. `_parse_tool_json()` accepts both `\"arguments\"` and\n `\"parameters\"` keys, coerces string-encoded args, and skips unparseable calls instead\n of crashing the turn.\n\n### Known limitations\n\n- Eve agent importer: remote GitHub fetching not yet implemented (local paths only).\n- Durable task store: currently JSONL (suitable for thousands of records, not millions).\n\n## [2.18.0] - 2026-07-10\n\n### Added — AitherShell command center + Claude Code session management\n\n- **`aither sessions`** — Claude Code session manager: interactive full-screen\n browser (type-to-filter, transcript preview, deep full-text `search`, resume\n **in the current terminal** or as Windows Terminal tabs), crash guard\n (`guard install` = at-logon watchdog that snapshots live sessions and\n auto-restores the whole set after a terminal crash), and `ingest [--watch]`\n (incremental, secret-guarded sync of session conversations into the local KB\n / CompanyBrain via the standard ingest pipeline).\n- **`aither hq`** — command-center dashboard: per-service fleet health, LLM\n queue (depth/VRAM/models), Pulse alerts, live session count, inbox unread —\n auto-refreshing, with one-key jumps into chat, sessions, inbox, agents,\n brief, watchtower, and docker recovery.\n- **`aither inbox`** — unified queue over CommCore mail, Relay\n mentions/notifications, and Pulse alerts; open/mark-read/DM-reply from the\n terminal. Every source degrades independently.\n- **`aither agents`** — roster console + `agents ask -e <1-10> <agent> <q>`\n (effort-tiered ask via Genesis, with automatic `/chat/stream` fallback when\n the `/agent/sync` execute gate rejects host callers) + forge dispatch\n inspection + routine health.\n- **`aither palette`** — universal fuzzy picker across actions, sessions, and\n services; **`aither brief`** — renders Atlas's executive briefing in the\n terminal; **`aither watch`** — fleet watchtower with named wedge-signature\n detection (docker-WSL wedge, LLM-queue stall, crash-loop uptime regression)\n and optional docker auto-recovery.\n- All fleet reads go through a shared fail-soft `FleetClient` (internal-CA\n TLS via `adk._tls`, dashboard-grade timeouts, per-source degraded states).\n- Node shell-cli (`@aitherium/shell-cli` 1.13.0): `aither\n sessions|hq|inbox|palette|brief|watch|agents|docker` now pass through to the\n Python shell instead of being intent-classified as chat prompts.\n\n## [2.17.1] - 2026-07-05\n\n### Fully self-service mesh join — no customer key handling\n\n- **Auto-join headscale off the onboard response.** The conductor now auto-issues a\n headscale pre-auth key for NAT'd nodes and returns it in the `/v1/mesh/onboard`\n response; `mesh.join` consumes it and brings up the tunnel automatically. A customer\n runs one command — they never mint, fetch, or set a headscale key. Explicit\n `--headscale-key`/`AITHER_HEADSCALE_AUTH_KEY` still take precedence.\n- **Security:** `_tailscale_up` now redacts the auth key from all logs and exceptions\n (a failed `tailscale up` previously echoed the full argv, leaking the key into logs).\n\n## [2.17.0] - 2026-07-05\n\n### Added — Headscale mesh transport + local Qdrant + AitherConnect onboarding\n\n- **Headscale NAT-friendly mesh transport.** When raw WireGuard UDP:51820 is not\n viable (customer boxes behind NAT, corporate firewalls, CGNAT), `adk mesh join\n --headscale` routes the mesh tunnel through Tailscale's Headscale control plane.\n The overlay IP is still Conductor-assigned (10.77.0.0/16); Headscale provides\n only the transport layer. Configured via `AITHER_MESH_TRANSPORT=headscale`,\n `AITHER_HEADSCALE_URL` (default: https://headscale.aitherium.com), and\n `AITHER_HEADSCALE_AUTH_KEY` (pre-generated key). Automatic fallback to raw\n WireGuard if Headscale setup fails.\n- **Local Qdrant vector DB provisioning.** `adk stack` now provisions Qdrant\n locally when `AITHER_VECTOR_DB=qdrant` is set, enabling offline-first RAG\n without external dependencies. Tiered: local Qdrant in-fleet, cloud Nexus\n for enterprise multi-tenant deployments.\n- **Conductor URL public fallback.** `adk mesh join` resolves the internal\n `aitheros-conductor:8193` hostname; if unreachable (e.g., self-hosted nodes),\n it falls back to the public `conductor.aitherium.com:8193` endpoint,\n eliminating bootstrap configuration friction.\n\n### Documentation\n\n- **Self-hosting runbook.** New `docs/SELF_HOSTING.md` covers deploying AitherOS\n on customer infrastructure with Qdrant + mesh + AitherConnect onboarding.\n- **Mesh transport constraints documented.** Raw WireGuard requires public\n UDP:51820 endpoint (cloud instances, dedicated hardware); Headscale for\n NAT'd networks. Noted in CLI help and module docstrings.\n\n## [2.15.0] - 2026-07-04\n\n### Added — built-in web admin console + portal-profile settings sync\n\n- **Web admin console.** The page `adk up` opens (served at `/` and `/ui`) is now a full,\n self-contained console — Chat plus six admin tabs: **Backend** (view/switch LLM provider,\n set keys, test connection), **Packs** (list/enable/disable/reload tool packs),\n **Sessions** (browse/delete conversations), **Logs** (redacted agent-log tail),\n **Graph** (knowledge-graph search/neighborhood/stats), and **MCP** (add/remove external\n MCP servers). No build step, no CDN — a single packaged HTML asset. The original minimal\n streaming chat page stays at `/chat` for back-compat.\n- **Admin API (`adk/admin_api.py`).** ~20 endpoints under `/admin/*`, all bearer-gated by the\n existing middleware (never in `_skip_auth_paths`). Operates on the live agent — LLM backend\n swap (`LLMRouter.switch_backend`), pack reload, and MCP registration take effect without a\n restart.\n- **Settings sync (`adk/settings_sync.py`).** The user's portal profile is the source of\n truth: on startup the agent pulls `preferences.adk` and applies it over the local\n `~/.aither/config.yaml` cache; admin-console edits push a debounced snapshot back to\n `PUT /api/settings/preferences`. Provider API keys and MCP auth headers are device-local\n secrets and are never included in the pushed snapshot. Opt out with `AITHER_SETTINGS_SYNC=false`.\n\n### Security\n\n- **Timing-safe token comparison.** The server bearer check now uses `hmac.compare_digest`\n (the token is reachable over the public tunnel; a byte-wise `!=` leaked it to timing attacks).\n- **MCP add is SSRF-guarded + confirmed.** Adding an external MCP server is a two-step\n prepare→confirm flow; URLs resolving to loopback / private / link-local / metadata addresses\n are rejected (override with `AITHER_ALLOW_PRIVATE_MCP=1` for trusted local servers).\n- **Config editor is allowlisted.** `PATCH /admin/config` writes only safe scalar prefs and\n hard-denies fields that could execute code or redirect traffic at startup (e.g.\n `aither_toolpack_dirs`, backend base URLs).\n- **Log tail is scrubbed.** Bearer tokens, `#k=` fragments, and `sk-`/`aither_sk_` keys are\n redacted from the log-tail endpoint; the tunnel log is excluded.\n\n## [2.14.8] - 2026-07-04\n\n### Added — `adk up` for normal humans (no API key, no prereqs, live chat)\n\n- **Hosted-brain default — no API key required.** When there's no local model and no\n provider key, `adk up` signs in (device-flow) and routes inference through the gateway\n using the portal token (`AITHER_LLM_BACKEND=gateway` + bearer). A non-technical user\n never needs to obtain or paste an LLM API key. Explicit `--provider`/local model still\n take precedence; unattended `--yes` with no token/backend fails cleanly (exit 3).\n- **cloudflared auto-download.** If the tunnel binary is missing, `adk up` downloads the\n right platform asset to `~/.aither/bin` (SHA256-verified against the release; opt out with\n `AITHER_CLOUDFLARED_REQUIRE_CHECKSUM=0`) instead of exiting with an install hint.\n- **Built-in streaming chat page.** The agent now serves a self-contained chat UI at `/`\n (and `/chat`) with a live \"thinking…\" indicator and token-by-token streaming — so a human\n has somewhere to talk to it with immediate feedback instead of a blank wait. `adk up` opens\n it locally and passes the callback bearer in the URL fragment (`#k=…`), so `/chat/stream`\n stays authenticated (not an open proxy) with nothing to paste.\n- **Human-readable failures.** `adk up` errors now carry a plain-English `next_action`\n (kept machine-readable in `--yes` JSON).\n\n### Fixed\n\n- `adk up` chat link pointed at a non-existent `/settings/agent` route; it now points at the\n agent's own streaming chat page.\n\n## [2.14.7] - 2026-07-04\n\n### Added\n\n- **`adk up` — one command to run a persistent, fleet-connected agent.** Collapses\n the scattered onboarding surface (`host`/`quickstart`/`connect`/`login`/`setup`)\n into a single command that starts `aither-serve` (single agent, identity `aither`\n by default), opens a Cloudflare quick-tunnel for fleet reachability, registers with\n the portal, and installs a reboot-persistent autostart entry (Windows Task Scheduler\n / systemd-user / launchd). Detaches by default so the terminal is freed; `--foreground`\n blocks. Reuses the existing device-flow login, cloudflared discovery, provider-key\n store, and `_preflight_check` backend detection.\n- **Fully non-interactive path for autonomous agents.** `adk up --yes` (or any\n non-TTY invocation) takes ZERO prompts — everything resolves from flags, env, and\n saved config — and emits a single machine-readable JSON status line with meaningful\n exit codes (`0` ok, `2` bad-input, `3` no-backend, `4` tunnel-missing, `5` register-failed).\n Degrades to local-only (warn, not fail) when a portal token is absent unless\n `--require-register` is set.\n- **`adk down` / `adk status --json` companions.** `down` stops the agent + tunnel,\n best-effort deregisters, and removes the autostart entry; `status` reports the running\n agent (liveness + `/health` + tunnel + registration), with `--json` for agents/CI.\n- **`adk stack`** — the previous `adk up` behaviour (supervise the Room + Ollama consumer\n stack) now lives here; `adk up` is the connected-agent command.\n\n### Internal\n\n- New `adk/agent_daemon.py`: detached process spawn, status file\n (`~/.aither/adk-up.json`), pid liveness/teardown, and cross-platform autostart install.\n\n## [2.14.6] - 2026-07-04\n\n### Added\n\n- **`adk.preflight` — self-bootstrap capability probe.** `run_preflight(agent,\n spec)` wakes an agent up knowing what it is actually plugged into: a bounded\n (1.5s) parallel `LivenessProbe` over the router's resolved backends\n (primary/reasoning), the embeddings winner, remote MCP tools, the structured-ML\n / `/ml/teach` endpoints, and voice — returning an honest `CapabilityReport`.\n Status vocabulary distinguishes reachability from entitlement\n (`OK`/`MISSING`/`UNREACHABLE`/`AUTH`/`TIER_DENIED`/`UNSUPPORTED`), a hosted slot\n never prints a bare `OK`, and a required-but-unsatisfied slot aborts before the\n loop instead of blind-firing. Headless task/rules resolution + an allowed-roots\n policy binding (`builtin_tools.set_allowed_roots`, which resets the memoized\n cache so the override actually enforces).\n- **Multimodal (image) messages.** `Message.content` now accepts an OpenAI-style\n content-part list (`str | list[dict]`), passed through verbatim to the\n OpenAI-compatible body (gateway / vLLM / OpenAI). New `adk.llm.multimodal`\n helpers — `image_message()`, `image_content_parts()`, `to_image_url()` — build\n the parts and encode local image bytes/paths into `data:` URIs.\n- **Real vision preflight probe.** The vision slot is no longer unconditionally\n `UNSUPPORTED`: it sends a discriminable image (a solid red square) and requires\n the model to name the color, so a blind/text-only model cannot false-pass. `OK`\n only on a real round-trip; `AUTH` on 401/403; `UNSUPPORTED` when there is no\n OpenAI-compatible provider or the model cannot see the image.\n\n## [2.14.5] - 2026-07-04\n\n### Added\n\n- **`adk.reasoning.mcts` — generic Monte-Carlo Tree Search library.** A reusable\n MCTS engine (ported from the internal `UnifiedMCTS`, with all internal couplings\n removed) that any agent can drive over its own environment:\n - `MCTSEnvironment` protocol (`get_state_hash` / `get_actions` / `step` /\n `evaluate` / `clone`) — implement it and search.\n - Three **optional, default-off** model seams — `TransitionModel`, `PolicyModel`\n (PUCT priors), `ValueModel` — so a learned world-model / policy / value can be\n injected without touching the core; with all three `None` the engine is a plain\n UCT search.\n - `ObservedTransitionModel` adapter — a cheap online lookup world-model (hit =\n exact stored transition; miss = identity + negative \"unknown\" reward +\n `is_uncertain`, biasing exploration toward learning), with JSONL save/load for\n cross-run reuse.\n - `UnifiedMCTS(config).search(env)` returns an `MCTSResult` (best action, value,\n confidence, principal-variation path) and an optional `MCTSTrace`\n (visit-distribution policy target) for distillation.\n- **Self-bootstrapping agent machinery.** Build an agent from a declarative spec\n instead of bespoke wiring:\n - `adk.bootstrap.build_agent_from_spec(path) -> (AitherAgent, RunCtx)` — resolves\n prompts, activates required/optional tool packs, and carries a `memory_map`\n (a `node_type -> (role, tier)` override for memory writes, never a gate).\n - `adk.prompts.PromptBridge` — resolves an agent's prompt bindings from files\n (relative to the spec) or `AitherPrompts` dot-keys (optional import).\n - `adk.packs.PackActivator` — eager activation of installed tool packs by name\n over `register_tool_packs`; a required pack that registers zero tools raises\n `PackUnavailable`, optional packs soft-degrade.\n - `python -m adk.bootstrap.generic_agent <spec.yaml>` inspects a spec (resolved\n name, activated packs + tool counts, prompt keys, memory map) without running a\n live loop.\n- **`deep_research` tool pack** — a full multi-engine web-research workflow as\n `dr_*` agent tools (keyless search, page fetch + cache, site mirror, a citable\n findings knowledge graph, and markdown/PDF/DOCX report generation), activatable\n by name via the tool-pack loader.\n\n## [2.14.4] - 2026-07-04\n\n### Added\n\n- **`structured_ml` tool domain** — zero-shot structured-data inference for agents,\n backed by TabFM (tabular classification/regression) and TimesFM (time-series\n forecasting). Three tools:\n - `tabular_classify(support_rows, target, query_rows)` — classify rows from a\n labeled support set, in-context (no training step; up to 10 classes).\n - `tabular_regress(support_rows, target, query_rows)` — predict a numeric target.\n - `timeseries_forecast(series, horizon)` — forecast future values of a series.\n\n The tools POST to a structured-ML inference service resolved from\n `AITHER_STRUCTURED_ML_URL` (default `http://localhost:8192`), reject non-http(s)\n URLs, and summarise oversized responses to protect the agent's context.\n- **`Capability.STRUCTURED_INFERENCE`** capability token and a new **`analyst`**\n identity/pack that ships the `structured_ml` domain by default. The domain is\n opt-in — only identities that enable the `structured_ml` category get the tools.\n\n## [2.14.3] - 2026-07-03\n\n### Fixed\n\nAll four bugs below were found by actually booting `adk run` against a real\nchat model and driving a real tool-calling request end-to-end (not inferred\nfrom reading the code) — each one silently broke the \"local agent uses its\nconfigured model and its tools actually work\" path.\n\n- **`AITHER_MODEL` / `config.model` silently ignored by the OpenAI-compatible\n backend**: `LLMRouter.__init__` never read `config.model` into `self._model`\n unless the caller passed `model=` directly to the constructor — which nothing\n did. Every explicit model choice (env var, config file) was dropped, the\n provider fell back to a hardcoded per-backend default (`gpt-4o-mini` for the\n `openai` backend), and an unrecognized model name got silently remapped\n server-side to the wrong backing model. Concretely: setting\n `AITHER_MODEL=aither-orchestrator` had no effect — every request actually\n went to a different model that was never meant to be called directly and\n can't reliably use tools.\n- **`OPENAI_BASE_URL` ignored for explicit `--backend`/`AITHER_LLM_BACKEND`\n selection**: `LLMRouter._auto_detect()`'s explicit-backend branch hardcoded\n `base_url=None` when constructing the provider, instead of reading\n `config.openai_base_url` (which two other call sites in the same file\n already did correctly). Any self-hosted OpenAI-compatible endpoint\n (MicroScheduler, vLLM, LM Studio) configured via `OPENAI_BASE_URL` was\n silently ignored in favor of `https://api.openai.com/v1`.\n- **Tool-call steering never engaged on default-effort chat**: the retry that\n nudges a model back into structured tool-calling when it responds with prose\n instead of a function call was gated behind `effort >= 6`. Default `effort`\n for a plain `agent.chat(message)` call resolves to `5`, so the safety net —\n built specifically for this failure mode — never fired for default requests.\n Also now forces `tool_choice=\"required\"` on the retry instead of relying on a\n text nudge alone.\n- **Every non-string builtin-tool parameter silently typed as `\"string\"` in\n the generated tool schema**: `builtin_tools.py` uses\n `from __future__ import annotations`, so `_extract_parameters()` in\n `tools.py` was reading postponed (string) type hints (`\"int\"`, not `int`)\n straight out of `fn.__annotations__`, which never matched\n `_type_to_schema`'s type-object keys and silently fell through to\n `{\"type\": \"string\"}`. Every builtin tool with an `int`/`float`/`bool`/`list`\n parameter was affected — e.g. `file_read(start_line: int, end_line: int)`\n advertised both as strings, so a model correctly calling the tool with\n `\"start_line\": \"0\"` crashed the actual implementation with\n `unsupported operand type(s) for -: 'str' and 'int'`. Fixed by resolving\n hints through `typing.get_type_hints()` instead of the raw annotations dict.\n `_coerce_arguments()` — the runtime safety net that's supposed to coerce a\n model-supplied `\"0\"` string into `0` before a tool call executes — had the\n exact same raw-`__annotations__` bug and silently no-opped for every builtin\n tool; fixed the same way.\n- **Streaming tool-loop could hang a connection forever**: `chat_stream()`'s\n fallback to the sync tool loop (tool calls can't stream token-by-token) had\n no timeout, so a model that didn't cleanly terminate a tool-calling round\n trip left the SSE connection open indefinitely with zero bytes sent. Bounded\n with a 60s `asyncio.wait_for`; on timeout the stream yields a clear message\n instead of hanging.\n\n## [2.14.2] - 2026-07-03\n\n### Fixed\n- **AitherShell session amnesia**: the REPL generated a fresh `session_id` for\n EVERY message, so conversation history never followed across turns (the\n server stored each turn under a different session). One stable id is now\n minted per shell run; `/resume` and `/new` semantics unchanged, and the\n cross-restart auto-restore of the last session now actually works.\n\n## [2.14.1] - 2026-07-02\n\n### Added\n- **Account-tied licensing via device-flow login** — `adk login` now persists the account\n license returned by the device-flow handshake to `~/.aither/license.json` (base64\n `license_key` → `{payload, signature}` envelope), so entitlements from the authenticated\n account carry into every later `adk` invocation without re-auth. Pairs with the fleet-side\n node tool-pack gate that grants packs by plan-tier rank.\n- **`can_use_formbridge` / `can_use_untether` entitlements** (PROFESSIONAL+ rank ≥ 3) backing\n the FormBridge and UNTETHER agent tool-packs.\n\n### Fixed\n- `adk` CLI: guard the optional `memory_graph` singleton (was a latent `NameError` on the\n `/stats`, `/memory`, and save paths when memory was not constructed).\n\n## [2.14.0] - 2026-07-02\n\n### Added\n- **`adk.memory_wiki` — LyraWiki-style self-managed semantic knowledge** (opt-in; nothing constructs it by default): `MemoryWiki(graph_memory)` maintains curated wiki ARTICLES as `wiki_article` nodes (title/slug, LLM-owned markdown with `[[wikilinks]]`, embedding, durable tier, `CONSOLIDATED_FROM` edges to source memories, governance-ledgered revisions).\n - `consolidate(llm, since=, budget=)` clusters unconsolidated episodic/fact nodes (embedding + keyword), drafts/UPDATEs articles via the INJECTED llm callable (`str -> str` or `messages -> str`, sync or async — no hard model dependency), cites source node ids, marks contradicted sources superseded, then DEMOTES consolidated sources to a fast-decay tier via `promote()` (represented knowledge doesn't need raw copies).\n - `lint(llm=None)` — deterministic health checks (orphan wikilinks, empty/stale articles, live-but-contradicted pairs) + an optional LLM audit pass.\n - `prune(relevance_floor=0.05, hard_delete_after_days=14)` — article relevance = tier freshness × reinforcement (reinforce-on-recall) + link-degree to LIVE memories; below the floor → reversible governance tombstone; entombed past the window → **HARD-DELETE** (the tombstone snapshot itself is purged — content and embedding irrecoverable; the true-deletion path). A node is never deleted without a tombstone first.\n - `recall(query, limit)` — article-first RAG (curated articles rank above raw nodes) that reinforces returned articles.\n- **`adk.routines` — agent heartbeat + self-programmed routines**: `RoutineStore` is a durable registry (`~/.aither/routines/{agent}.json`, `AITHER_DATA_DIR`-aware, injectable path) of cron-scheduled self-prompts `{name, cron, instruction, enabled, last_run, last_result, tags}` that rehydrates into the existing `adk.cron.CronScheduler` on `start()`. A fire runs `agent.chat(instruction)` with the agent's full toolset — the agent programs its future self with its own adk. Guardrails: `max_routines` (12), a 5-minute min fire interval per routine, a per-fire timeout, and every result ledgered (truncated) to `last_result`. Direct-callable routines (`register_direct`) fire a bound method instead of a self-prompt. Self-management tools (`routine_create/list/update/pause/resume/delete/run_now`) ship as OpenAI tool defs + handlers (`build_routine_tools` / `register_routine_tools` / `routine_tool_defs`); the handlers ONLY touch the RoutineStore — they can never modify agent config or safety-relevant settings (the leash principle).\n- **`AitherAgent(routines=True, memory_maintenance=True)`** (both default **False** → the default agent is byte-identical): `routines=True` builds the RoutineStore + scheduler, registers the self-management tools, and starts the heartbeat lazily on the first `chat()` (or via explicit `start_routines()`). `memory_maintenance=True` registers DEFAULT routines — `wiki_consolidate` (every 2 h), `wiki_lint` / `wiki_prune` / `graph_sweep` (daily) — as DIRECT method fires so memory upkeep runs even on tiny models, while staying visible/manageable via `routine_list`.\n- `TombstoneStore.purge(tombstone_id)` — the hard-delete endpoint: irrecoverably removes a tombstone snapshot and rewrites the persisted store without it (consumed by `memory_wiki.prune`).\n\n## [2.13.6] - 2026-07-02\n\n### Added\n- **Qdrant backend for GraphMemory dataplane sync — TRUE two-way, verified live.** Set `AITHER_FLEET_QDRANT_URL` and the sync uses Qdrant (`upsert` + `scroll` + filtered `search`) instead of Nexus. This is what actually delivers cross-agent memory sharing: a node is upserted with its OWN embedding under a deterministic per-(tenant,node) point id (idempotent re-push), and `fleet_pull` reliably SCROLLS a tenant's points back — the enumerate Nexus lacks (its `/search` doesn't return ingested docs and `/export` needs lancedb, which has a hard dependency conflict in the fleet). **Proven live with 2 agents:** agent-1 ingests → auto-syncs to the tenant dataplane; a fresh agent-2 in the same tenant `fleet_pull`s all of agent-1's nodes and can search them; a different tenant pulls nothing (isolation). Dimension-safe (never mixes vector dims in a collection). Local SQLite stays source of truth; best-effort, never raises.\n\n## [2.13.5] - 2026-07-02\n\n### Fixed\n- **CRITICAL — GraphMemory dataplane sync push was 100% failing.** `_fleet_push_node` sent `source_type=\"graph\"` / `content_type=\"graph_node\"`, which AitherNexus rejects as invalid enums (HTTP 500) — so NO graph node ever replicated in 2.13.4. Now sends the accepted `source_type=\"manual\"` / `content_type=\"text\"` (graph provenance is carried in `metadata.synced_from`). **Verified live** against a fleet Nexus: a 4-node ingest replicates with `pending==0` and the collection count reflects it.\n\n### Added\n- **Swarm-awareness notification** — on a successful sync batch, `GraphMemory` emits a best-effort `graph.synced` event (tenant/workspace/agent/collection/count) to `AITHER_FLEET_EVENTS_URL` so OTHER agents in the tenant/swarm can pull the fresh data and deconflict in-flight work (push-based awareness instead of polling). No-op unless the env is set.\n\n### Known limitation\n- Cross-agent **rehydration** (`fleet_pull`) is only reliable when the fleet Nexus is backed by lancedb (persistent + exportable). An in-memory Nexus stores on `/ingest` but neither its `/search` nor `/export` returns the docs — two-way read needs the persistent Nexus.\n\n## [2.13.4] - 2026-07-02\n\n### Added\n- **Memory-maintenance primitives on `GraphMemory`** (all opt-in; default behaviour byte-identical):\n - **Reinforce-on-recall**: `recall_with_activation(..., reinforce=True)` bumps `reinforcement_count`/`last_reinforced` on every RETURNED node's metadata mirror (one UPDATE per node, WAL-safe) — making the `MemoryRecord` reinforcement fields the activation scorer reads LIVE. Default `False` = zero writes.\n - **`sweep(now, archive_below=0.05, max_nodes=None, dry_run=False)`**: read-time decay enforced at rest — archives nodes past their tier TTL with freshness×reinforcement score below `archive_below` and `reinforcement_count <= 1`; `max_nodes` additionally archives the lowest-scored overflow. PERMANENT tier immune. Every archive is a reversible governance tombstone (`TombstoneStore.recover`) + a FORGET ledger entry; a node is never deleted without a tombstone. `dry_run` returns the would-archive list without acting.\n - **`promote(node_id, tier=?, role=?)`**: re-tier/re-role a node IN PLACE (tier/role columns + metadata mirror) — edges, embedding and ledger history survive; ledgers an UPDATE entry when governance is on.\n- `MutationType.UPDATE` ledger entry kind (in-place re-tier/re-role).\n- **Per-tenant dataplane sync for `GraphMemory`** — replicate the local graph to a per-tenant vector dataplane (Nexus/Qdrant) so a sovereign agent's memory is durable + rehydratable across restarts and hosting boundaries. `fleet_push_all_nodes()` (catch-up), `fleet_pull()` (best-effort semantic top-up), `fleet_sync_pending()`, and auto-sync on ingest (`_maybe_autosync` + `drain_sync()`). **On by default** (`AITHER_FLEET_SYNC=auto`; only `false/0/off` disables) with an inferred target (local Nexus `:8122` or the gateway in cloud mode) and a SEPARATE collection (`AITHER_FLEET_GRAPH_COLLECTION`, default `graph_memory`) from the KV memory sync. New `GraphMemory(tenant_id=, workspace_id=, fleet_url=, auto_sync=)` constructor args override env (required for multi-tenant host processes). Node `synced` column + schema v4 migration. TLS verify is CA-bundle-aware (`AITHER_CA_BUNDLE`) for the internal-TLS mesh. Local SQLite stays the source of truth; every sync path is best-effort and never raises into ingest.\n\n## [2.13.3] - 2026-07-02\n\n### Added\n- **Canonical self-deploying embeddings provider** (`adk.embeddings`): one embedding provider for the whole SDK so vectors are portable across scopes (platform / tenant / sovereign) — same model + dimension everywhere. Pinned to `nomic-embed-text` (768-d; the vLLM `--served-model-name` for nomic-embed-text-v1.5). Lazy, single-flight resolution chain: explicit `AITHER_EMBEDDINGS_URL` → local vLLM (`:8209` then `:8120`, HTTPS-then-HTTP) → local Ollama → gateway (`AITHER_GATEWAY_EMBEDDINGS_URL`) → auto-deploy a local vLLM embeddings container if a GPU + Docker are present (opt out with `AITHER_EMBED_AUTODEPLOY=0`) → CPU sentence-transformers (384-d, degraded) → feature-hash (384-d, degraded, always works). Every batch is dimension-tagged so callers never silently mix 768-d and 384-d vectors. `get_default_embedder()`, `embed_texts()`, `embed_one()`, `get_provider()`, `reset_provider()`.\n\n### Changed\n- **`GraphMemory` now defaults its embedder to the canonical provider** (opt out with `AITHER_GRAPH_EMBEDDER=legacy`), so every adk agent shares one 768-d space. Added a meta-table dimension guard: the index is pinned to its first embedding's dimension and later different-dimension vectors are refused (kills 768↔384 mixing that silently poisons cosine similarity).\n\n## [2.13.2] - 2026-07-01\n\n### Added\n- **Session persistence**: AitherShell now auto-saves the active session_id to `~/.aither/config.yaml` after each chat turn and auto-restores it on the next shell launch. Multi-turn conversations survive across restarts — the messages were always persisted in SQLite + JSON, only the session pointer was missing.\n- `/new` command to start a fresh session (clears saved session_id).\n- `/sessions` command to list recent sessions with agent name, message count, and timestamp. Resume any with `/resume <id>`.\n\n## [2.13.1] - 2026-07-01\n\n### Fixed\n- **CRITICAL:** `deploy_connect()` now sends a HEAD request to verify the GitHub release asset exists before downloading, with a clear error if the asset is missing.\n- **CRITICAL:** Compose file URLs are now pinned to a stable commit hash (configurable via `AITHER_COMPOSE_PIN` env) instead of tracking the mutable `main` branch.\n- **CRITICAL:** `deploy_adk_node()` now falls back to starting `adk-serve` natively when Docker is unavailable, instead of blocking with \"Docker not installed\".\n- **HIGH:** GHCR login failure now blocks deployment immediately with actionable guidance, instead of warning and continuing to a confusing image-pull failure.\n- **HIGH:** Health checks now stream the last 30 lines of Docker Compose logs on failure, so the user can see why a container didn't start.\n- **HIGH:** vLLM port allocation now probes for conflicts before assigning; if port 8200 is busy, workers are automatically remapped to the next free port.\n- **MEDIUM:** Compose file downloads are now cached with ETag; re-running `adk deploy node` skips re-downloading an unchanged file.\n- **MEDIUM:** DGX Spark discovery now respects `AITHER_DGX_HOSTS` env (comma-separated) for custom network topologies, instead of only checking `spark.local` and `192.168.0.33`.\n- **MEDIUM:** Cloud API keys (Anthropic, OpenAI, DeepSeek) are now validated with a lightweight probe during `adk setup` infra scan, so invalid keys surface early.\n- **MEDIUM:** `deploy_connect()` now validates the extracted extension has a `manifest.json` and warns if it's missing.\n\n## [2.13.0] - 2026-07-01\n\n### Removed\n- **`adk.faculties.memory_graph`** — the pickle-based `MemoryGraph` module is deleted. `from adk.faculties import MemoryGraph` now resolves to the canonical `adk.graph_memory.GraphMemory` for back-compat.\n- **`adk.platform.memory`** — the entire legacy memory subsystem (`MemoryManager`, `UnifiedMemorySystem`, `GameEngine`, `StoryboardEngine`, `AnchorGenerator`) is deleted. Agent memory is `adk.memory.Memory` + `adk.graph_memory.GraphMemory`.\n\n### Changed\n- `adk work` command now uses `GraphMemory` instead of the removed pickle-based faculty graph.\n- `adk.MemoryGraph` and `adk.faculties.MemoryGraph` lazy exports now resolve to `adk.graph_memory.GraphMemory`.\n- `agent.set_memory_graph()` kept for back-compat but documented as deprecated; agents already wire `GraphMemory` automatically in `__init__`.\n- Platform startup (`adk.platform.infrastructure.startup`) gracefully handles missing `MemoryManager`.\n- Platform CLI module listing and health check updated to reflect removed memory module.\n\n## [2.12.7] - 2026-07-01\n\n### Deprecated\n- `adk.faculties.MemoryGraph` — emits `DeprecationWarning` on instantiation; use `adk.graph_memory.GraphMemory` (SQLite-backed, governed, embedding-native) instead.\n- `adk.platform.memory` package — the legacy `MemoryManager`, `UnifiedMemorySystem`, `GameEngine`, and `StoryboardEngine` modules now emit `DeprecationWarning` on import; use `adk.memory.Memory` and `adk.graph_memory.GraphMemory` for new code.\n\n### Changed\n- Updated `adk.faculties` docstring and usage examples to point at the canonical memory surface.\n\n## [2.12.6] - 2026-07-01\n\n### Changed\n- Trimmed public package debt by removing grid runbook artifacts and shell/room binary placeholder directories from published artifacts.\n- Removed stale public-sync allowlist entries for deleted phase test files and added explicit public-sync guards for binary placeholder directories and grid artifacts.\n- Updated the public README grid note so it no longer links to runbook files excluded from the SDK package.\n\n## [2.12.5] - 2026-07-01\n\n### Added\n- Added best-effort fleet memory sync from local ADK memory into Nexus/Qdrant-compatible RAG stores, including batch catch-up and vector-search recall hooks.\n- Added saved config/env support for fleet memory URL, collection, and sync mode.\n\n### Changed\n- Excluded FormBridge runtime/test internals from public ADK packaging and public repo sync.\n\n## [2.12.4] - 2026-07-01\n\n### Added\n- Added a hot-reload endpoint for installed agent packs so newly applied pack tools can be picked up without restarting `aither-serve`.\n\n### Fixed\n- Fixed synchronous `ChatRelay` calls on Python 3.12 when no event loop is running; async WebSocket delivery is still scheduled when a loop exists.\n- Aligned gateway inference tier tests with the current public ladder: free, starter, pro, enterprise, platform.\n\n## [2.12.0] - 2026-06-28\n\n### Added — one-command local inference + upgrade path (click-to-run)\n- **`adk quickstart-local`** — detects hardware, picks a backend\n (Ollama / llama.cpp / vLLM), installs it, downloads a model, verifies, and\n prints next steps. Bootstraps Ollama itself via winget/brew/install.sh when\n missing (no \"go install it\" dead-ends). Default model `gemma4:e2b`.\n- **`adk backend switch <ollama|llamacpp|vllm>`** (+ `backend status`) — migrate\n the active deployment between engines; re-points config and smoke-tests.\n- **`adk install pack:<name>`** (+ `adk install list` / `adk packs`) — install\n ready-made agent packs (openclaw, hermes, claude-code) into `~/.aither/agents`.\n\n### Fixed (found via live end-to-end testing on Windows + CUDA)\n- quickstart-local reported success on a dead endpoint (smoke test was warn-only);\n a failed smoke now fails the command, with a service-readiness wait.\n- `--model` resolved to `None` (getattr default never fired) → uses the real default.\n- Smoke test: 30s→180s timeout for cold model load; accepts reasoning-model output\n (empty `content` at low token budgets is no longer a false failure).\n- `adk install pack:<name>` colon syntax (argparse rejected it before dispatch).\n- `adk backend status` health probe used a nonexistent `/health` path → 404; now\n probes `/v1/models` (uniform OpenAI-compatible liveness for all three backends).\n- Windows: removed non-ASCII glyphs from printed strings that crashed the cp1252\n console (a `✓` even made a successful install report failure).\n\n## [2.11.1] - 2026-06-14\n\n### Fixed — `adk host` / `adk login` device flow (autonomous onboarding)\nFound driving `adk host` against production; all broke real device-flow login:\n- **Device flow hit the portal FRONTEND, not AitherIdentity.** `adk login`/`adk host`\n POSTed `/auth/device/code` to portal/veil.aitherium.com, which 307-redirects API auth\n to `/login`. Added `_resolve_identity_url()` mapping the aitherium.com topology to\n `idp.aitherium.com` (the Identity API); localhost/bespoke pass through unchanged.\n- **Cloudflare 403'd urllib.** `idp.*` sits behind Cloudflare, which blocks urllib's\n default `Python-urllib/3.x` UA. Device-code + poll requests now send a real\n User-Agent + Accept (verified urllib→idp 200, was 403).\n- **`adk host` now self-authenticates via device flow** when there's no token, and\n **retries via device flow on a 401/403** (a stale saved token no longer dead-ends\n registration).\n\n## [2.11.0] - 2026-06-14\n\n### Added — attach your own MCP \"hands\" to a self-hosted agent\nA self-hosted agent now has the full brain (your model) · body (this loop) · **hands**\n(your tools) trifecta. The AitherOS platform relays the MCP servers you registered for\nyour agent in the `/stream` request body as `mcp_endpoints`, and the loop wires their\ntools into the ReAct turn:\n- **`adk/mcp_endpoint_tools.register_mcp_endpoint_tools(agent, endpoints)`** — speaks MCP\n JSON-RPC (`tools/list` to discover, `tools/call` to invoke) and registers a namespaced\n proxy tool (`{endpoint}__{tool}`) for each advertised tool. Mirrors `app_proxy_tools`.\n Best-effort: an unreachable endpoint registers nothing and never raises.\n- **`POST /stream` and `POST /chat/stream`** accept an optional `mcp_endpoints` list\n (`[{name, url, [headers]}]`); `_aitheros_stream` attaches them to the agent before the\n turn. Existing callers are unaffected (the field is optional).\n\n## [2.10.1] - 2026-06-14\n\n### Fixed — self-hosted `aither-serve` was crash-on-startup (2.10.0 regression)\nA fresh `pip install aither-adk && aither-serve --backend deepseek` could not boot. Found\nand fixed while standing up a real self-hosted DeepSeek agent end-to-end:\n- **Startup `NameError: name 'port' is not defined`** in three lifespan helpers\n (`_join_aithernet`, `_init_a2a_server`, `_start_mesh_hosting`) — they referenced a bare\n `port` no longer in scope. Now use `config.server_port`, and `main()` writes the resolved\n `--port`/`--host` back to `config` so every lifespan helper and the invoke_url see the\n actual bound port.\n- **Fleet self-registration crashed the whole server** — the registration handlers caught\n `(ImportError, RuntimeError, OSError, ConnectionError)` but **not** `httpx.HTTPError`, so a\n `RemoteProtocolError`/connect error talking to the control plane took the agent down. All\n network-facing lifespan handlers now also catch `httpx.HTTPError` (non-fatal, as intended).\n- **`--backend` was ignored when `AITHER_API_KEY` was set** — `_auto_detect` picked the cloud\n gateway before the explicit backend, and `_try_elysium_fallback` then replaced the chosen\n provider. An explicit backend (deepseek/openai/anthropic/your own vllm/ollama) now always\n wins; \"auto\"/\"gateway\" still flow through detection. The operator's own brain is never\n silently hijacked.\n- **Approved-tool trace truncated after `/sessions/{id}/confirm`** — the SSE relay treated\n `AgentResponse.tool_calls_made` (a `list[str]`) as dicts (`tc.get(...)`), raised\n `AttributeError`, and an over-narrow `except` swallowed it so the client saw only\n `session_start` + heartbeats. The relay now handles string tool names, the `except` is\n broad (always emits a typed `error` + terminal `complete`), and the module-level\n `_strata_ingest` `NameError` + Chronicle `session_id` kwarg mismatch are fixed.\n\n## [2.10.0] - 2026-06-13\n\n### Added — human-in-the-loop tool approval (`adk/approval.py`)\n- **Pause before a gated tool, resume on decision.** An agent can now pause its turn before\n executing a sensitive tool and wait for a human's allow/deny — so a managed control plane\n (or any operator) can gate tool use without forking the loop.\n- **Policy via `AITHER_TOOL_APPROVAL`** — a comma-separated list of tool names (optionally\n `agent:tool`), or `*` for every tool. Empty = no gating (unchanged behavior).\n- **Survives restarts / days-later approvals.** The paused turn (user message + pending tool\n calls) is persisted to disk keyed by `session_id`; resume is a fresh request, not a held\n connection. Decisions are recorded per `(session_id, tool_name)`.\n- **`AgentResponse.requires_action` / `.pending`** — set when a turn paused; `pending` lists\n the gated tool calls awaiting a decision.\n- **`AitherAgent.resume(session_id, decisions)`** — records the decisions and re-runs the\n paused turn (the gate consumes them: allowed tools execute, denied tools get a denial\n observation, the turn continues — and may pause again on a different gated tool).\n- **Server:** `POST /sessions/{id}/confirm` records decisions and streams the resumed turn;\n `/stream` emits a `requires_action` event when a turn pauses.\n\n## [2.9.0] - 2026-06-13\n\n### Added — universal LLM continuation primitive (`adk/llm/continuation.py`)\n- **`run_continuation()` + `stitch()`** — one shared continue-until-complete primitive. When a\n completion stops because it hit the output token cap (`finish_reason == \"length\"`), it continues\n the generation and STITCHES the chunks (overlap-dedup + restart-detect) into a complete answer,\n instead of every call-site hand-rolling its own retry. Bounded three ways (max rounds AND total\n output ceiling AND a no-growth break), and it never mutates the caller's message history.\n- **`LLMProvider.chat_with_continuation()`** and **`LLMRouter.chat_with_continuation()`** — every\n provider AND the router inherit the primitive, so any call-site gets continuation for free.\n- Kill-switch `ADK_LLM_CONTINUATION=off`; tunables `ADK_LLM_MAX_CONTINUATIONS`,\n `ADK_LLM_MAX_TOTAL_OUTPUT_TOKENS` (defaults: 2 rounds / 8192 tokens).\n\n### Changed\n- The ReAct loop's truncation recovery now uses the shared primitive. The previous inline\n doubling-retry REPLACED the partial response (losing earlier text in `resp.content`) and injected\n continuation scaffolding into the live message history; it now STITCHES and leaves history clean.\n Tool-call turns are never continued as text.\n\n### Included — previously-committed-but-unreleased changes\n- 2.8.2: TLS verification on by default (`verify=False` removed).\n- 2.8.1: baked license + pack-signing public keys.\n- 2.8.0: version-bump release.\n\n## [2.7.0] - 2026-06-05\n\n### Added — provider-agnostic LLM cost levers (at the `LLMProvider` ABC)\n- **Prompt caching.** New `ProviderCapabilities` (`prompt_cache: explicit|automatic|none`, `batch`) and\n normalized `LLMResponse.cache_read_tokens`/`cache_write_tokens`, so the \"tokens saved\" meter never\n depends on which backend served the turn. Anthropic inserts `cache_control` breakpoints over the stable\n system+tools prefix; OpenAI/DeepSeek caching is automatic and their `cached_tokens` are now surfaced\n (previously discarded). Default-on in `AitherAgent.chat`; override with `cache=False`.\n- **ResponseCache** (`adk/llm/cache.py`) — exact-match response cache with a pluggable backend, wired into\n `LLMRouter` as an opt-in (`LLMRouter(response_cache=…)` + per-call `cacheable=True`). OFF by default — no\n determinism change unless opted in.\n- **Gemini provider** (`adk/llm/gemini.py`) with explicit `cachedContents` caching; registered in the router.\n- **Async batch runner** (`adk/llm/batch_runner.py`) — `run_batch()` over the Anthropic Message Batches /\n OpenAI Batch APIs (~50% off) with a transparent fallback to concurrent `chat()` where unsupported.\n- **Graph-RAG retriever interface** (`adk/graph_retriever.py`).\n\n### Added — principal authority (prompt-injection defense enforced outside the model)\n- `adk/auth.py`: `Principal` / `AuthContext` / `PrincipalResolver` (+ deny-all default).\n- `ToolRegistry.execute(name, args, auth=…)` — a default-deny authorization gate at the tool boundary, so a\n forbidden tool call is blocked regardless of what the LLM emits. `ToolDef` gains `required_clearance` /\n `action_class`. Threaded through `AitherAgent.chat`; `auth=None` is fully backward-compatible.\n- `adk/channel_auth.py`: Telegram webhook-secret + CEO-id checks and HMAC-signed approvals.\n\n## [2.6.4] - 2026-06-05\n\n### Fixes\n- **Loop-guard nudges corrupted the message history → DeepSeek 400 mid-run.**\n When the ReAct loop emitted multiple tool calls in one assistant turn and the\n loop guard fired a WARN/BLOCK/CIRCUIT_BREAK on one of them, it spliced a\n standalone `system` nudge message *between* the tool results. DeepSeek (and\n strict vLLM chat templates) require an assistant `tool_calls` message to be\n followed by exactly one `tool` result per `tool_call_id`, contiguously — so the\n interleaved message produced `400 … insufficient tool messages following\n tool_calls message` and aborted the turn (this is reliably hit after crossing\n the 15-call circuit-break threshold, where every subsequent call WARNs).\n Per-call guidance is now folded into that call's own `tool` result, and any\n standalone steering is deferred to a single message AFTER the whole batch —\n preserving the `tool_calls`↔`tool_results` pairing every backend requires.\n Regression test: `deep-research-agent/tests/test_msg_structure.py`\n (loopguard-flood scenario).\n\n## [2.6.3] - 2026-06-04\n\n### Fixes\n- **Transient provider errors killed a run on the first blip.** A single\n `502 Bad Gateway` / `503` / `529 overloaded` / `429` from the upstream API\n aborted the whole request — the Anthropic provider had **no retry at all**, and\n the OpenAI-compatible provider only retried `429`. Both now retry transient\n statuses (`408/429/500/502/503/504/529`) with `Retry-After`-aware exponential\n backoff (3 attempts), and surface the provider's body if all retries are\n exhausted. Non-transient `4xx` (e.g. `400`) still fails fast. Applies to\n `chat` and `chat_stream` on both providers.\n\n## [2.6.2] - 2026-06-04\n\n### Fixes\n- **DeepSeek (and strict OpenAI-compatible backends) 400'd on long tool\n conversations.** The ReAct loop legitimately injects mid-conversation\n `system` steering messages (the \"[DIMINISHING RETURNS]\" hint after several\n low-output tool iterations, loop-guard nudges). OpenAI tolerates a non-leading\n `system` message; DeepSeek rejects it with a 400. Long research runs\n (many `fetch_url` calls) reliably tripped this. Fix: non-leading `system`\n messages are demoted to `user` at the OpenAI-compatible provider boundary\n (`_demote_nonleading_system`) — steering intent preserved, payload valid on\n every backend; leading system prompt(s) untouched.\n- **Opaque HTTP errors.** `resp.raise_for_status()` discarded the provider's\n response body, so a 400/422 surfaced as a bare `Client error '400 Bad\n Request'` with no cause. `_ensure_ok` now includes the provider's actual\n message (e.g. `context_length_exceeded`, `Invalid 'messages'`) in the raised\n error, for both `chat` and `chat_stream`.\n\n## [2.6.1] - 2026-06-04\n\n### Fixes\n- **`MCPServer.mount()` returned HTTP 422 for every `/mcp` call.** Under\n `from __future__ import annotations` the route handler's `request: Request`\n annotation is a string that FastAPI resolves against the *module* globals, but\n `Request` was imported locally inside `mount()` — so FastAPI couldn't resolve\n it and treated `request` as a required query param. The MCP server endpoint\n (`initialize`, `tools/list`, `tools/call`) was unusable when mounted. FastAPI\n symbols are now imported at module level (guarded; the `[node]` extra still\n gates actual use). Any agent's tools can now be exposed as a working MCP\n server with `MCPServer(...).mount(app)`.\n\n## [2.6.0] - 2026-06-04\n\n### Streaming + scaffolding\n- **`AitherAgent.stream_chat`** — reliable streaming sibling of `chat()`: native\n tool-calling loop (concludes on its own, unlike the text-protocol `stream_react`)\n that emits live `tool`/`tool_result` events and streams the answer as `token`\n events. `token_delay` paces tokens for a typing effect.\n- **`adk new <template>`** — scaffold a full, runnable template app. Ships the\n `deep-research` template (a cited web-research agent: pack + server + web UI):\n `adk new deep-research`.\n\n### Fixes\n- **BYO-key no longer community-capped.** The free-tier monthly token cap now applies\n only to the metered Aitherium gateway backend, never to a user's own provider key\n (Anthropic/OpenAI/DeepSeek/Ollama/vLLM). Previously it could brick a self-hosted\n agent after ~100k tokens on the user's own key.\n- **Stronger, grounded memory recall.** `chat()` now injects the top-6 knowledge-graph\n hits (untruncated) with a hard no-fabrication instruction — was top-3 truncated to\n 200 chars, which surfaced partial facts and let weaker models invent the rest.\n- **Tool-arg coercion.** `ToolRegistry.execute` coerces LLM-supplied args toward each\n parameter's annotated type, so a tool no longer crashes when a model passes a list\n or string where an int/float/bool was expected.\n\n### Docs\n- New `docs/AGENT_DEV_GUIDE.md` — the golden path for building agents/packs.\n\n## [2.5.0] - 2026-06-04\n\n### Continual learning — agents that learn from runs and reuse memory\n\nAgents now improve the second time they do a task instead of starting from scratch.\n\n### Added\n- **Recall-before**: `AitherAgent.chat()` searches the local `SkillStore` and injects the\n top matching learned skills as a system block, so the model reuses proven procedures.\n- **Learn-after**: `_learn_after()` runs after each response — reinforces (`success_count++`)\n any skills that were recalled+reused, and extracts + saves a NEW skill from successful\n multi-tool runs via `SkillExtractor`. Wired at both `chat()` return paths.\n- Gated by `AITHER_SKILLS` (default on). The previously-unused `SkillStore`/`SkillExtractor`\n are now connected to the run loop.\n\n### Notes\n- Pairs with the platform-side LearnedProcedure substrate (recipes → scored procedures →\n skills/tools/A2A/packs → MCTS priors → evolution). The SDK shares memory via the Spirit\n bridge when configured.\n\n## [2.0.0] - 2026-06-02\n\n### Open-core boundary (BREAKING)\n\nThis release draws a single, enforced free/paid line. The free tier stays\ngenuinely useful (real agent, typed + graph + code memory, ReAct, ~essential\ntools); paid tiers unlock the *scale* capabilities via portal.aitherium.com.\n\n### Added\n- **`adk/licensing.py`** — the entitlement keystone. Resolves a tier\n (COMMUNITY by default), verifies optional portal-signed `~/.aither/license.json`\n (Ed25519), and answers `can_use_fleet/channels/cron/swarm/auto_neurons`,\n `is_agent_licensed`, `max_effort`, etc. **Fail-closed**: no/invalid/expired\n license → free tier; an unsigned license never grants premium.\n `AITHER_TENANT_SLUG=aitherium` → unrestricted INTERNAL tier.\n- **Moat guard** (`scripts/check_moat_boundary.py`) wired into the publish\n workflow — blocks any wheel that leaks the moat or drops the keystone.\n- Tests: `tests/test_licensing.py`, `tests/test_moat_boundary.py`.\n\n### Changed (gating — fail-closed)\n- Free tier effort is capped at 3 (reasoning effort 7–10 needs Professional).\n- Monthly token budget hard-block on the free tier (no silent overspend).\n- Gated behind a paid tier: fleet mode (`FleetConfig`), cross-agent delegation\n (`AgentForge.delegate`), channel adapters, cron scheduler, proactive\n auto-neurons, and `swarm`/`swarm_code` dispatch.\n\n### Removed\n- **`adk/nanogpt.py`** (on-device training IP) removed from the published SDK\n and the public repo; relocated to the internal AitherOS moat. Excluded from\n the wheel as defense-in-depth.\n- Pre-2.0 releases shipped this IP and ungated capabilities and are being\n yanked (PyPI) / removed (GitHub releases). See `scripts/yank_leaky_releases.py`\n and `scripts/purge_public_leaks.sh`.\n\n## [1.20.0] - 2026-05-27\n\n### Fixed\n- **Pack discovery path mismatch** — `_find_local_packs()` now scans both\n `~/.aither/packs/` and `~/.aitheros/packs/`, so packs installed via\n `adk pack install` are discovered by `adk-workspace`.\n\n### Added\n- **Auto-register on pack install** — `adk pack install` now auto-registers\n with the portal if the pack contains an `agent.yaml` with a `portal` section.\n One command does download + extract + register.\n- **Flux event on agent registration** — `developer_portal.py` emits\n `agent.registered` Flux event so the portal fleet UI updates in real-time.\n\n## [1.15.0] - 2026-05-26\n\n### Added\n- **Cloud memory** — `adk quickstart --cloud` now tests gateway memory and\n auto-configures Spirit URL so memories persist across devices via\n `gateway.aitherium.com/v1/memory/teach` and `/v1/memory/recall`.\n- **Config→env export for cloud mode** — `Config.from_env()` now exports\n `AITHER_CLOUD_MODE`, `AITHER_SPIRIT_URL`, and spirit path env vars from\n saved config so Memory and other modules pick them up without needing\n explicit env vars set by the user.\n- **Cloud Quick Start section** in README — 3-command setup for users who\n just want a Claude/GPT-4/DeepSeek API key and the full agent harness.\n- **`--memory` profile for node deployment** — `adk deploy node --memory`\n adds Spirit (8087) + WorkingMemory (8101) containers for persistent\n vector memory. Without this, agents use local SQLite only.\n- **Configurable vLLM in node compose** — `AITHER_VLLM_MODEL`,\n `AITHER_VLLM_GPU_UTIL`, `AITHER_VLLM_CTX_LEN`, `AITHER_VLLM_QUANT_ARGS`\n env vars let `adk setup` write GPU-detected tier settings to `.env`\n and have the node compose pick them up (TQ4 for small GPUs, BNB for large).\n\n### Fixed\n- **Memory cloud routing** — `Memory._spirit_teach()` and `_spirit_recall()`\n now use configurable paths and auth headers for gateway proxy. Previously\n cloud-only users silently lost all Spirit memory calls.\n\n## [1.9.0] - 2026-05-24\n\n### Added\n- **Composable ToolPack system** — agents can load tool packs from\n `.toolpack.yaml` directories. Drop a pack in `~/.aitheros/packs/` or\n set `AITHER_PACKS_DIR`, and any agent discovers it automatically.\n- **`tools.packs` in agent.yaml** — list tool pack IDs to auto-load\n (e.g. `packs: [crm-tools, calendar-pro]`).\n- **`register_tool_packs(agent, pack_ids, packs_dir)`** in `builtin_tools` —\n programmatic pack loading for agents.\n- **`AITHER_TOOL_PACKS` env var** — comma-separated pack IDs loaded on\n agent init (alternative to YAML config).\n- **Unknown builtin categories resolve as pack IDs** — `tools.builtin:\n [file_io, crm-tools]` now works: `crm-tools` is looked up as a pack.\n\n## [1.8.0] - 2026-05-24\n\n### Removed\n- **11 dead modules** (3,023 lines) — `agent_deploy`, `cloud_deploy`, `create`,\n `phonehome`, `sdk_bridge`, `sovereign`, `telemetry`, `telemetry_config`,\n `error_reporter`, `anonymizer`, `package`. None were imported anywhere.\n- **29 ghost exports** from `__init__.py` — `secrets`, `otel`, `fs_sandbox`,\n and `vector_memory` modules were listed in `__all__` and `__getattr__` but\n the backing files never existed. Accessing them raised `ModuleNotFoundError`.\n- **Stale dist/ artifacts** — removed old wheels/tarballs (v0.4.0–v1.4.1, 11 MB).\n- **Orphan test scripts** — `test_phase1.py` and `test_phase23.py` (one-off\n validation scripts not part of the pytest suite).\n\n### Fixed\n- **Version mismatch** — `__init__.__version__` now matches `pyproject.toml`\n (was stuck at 1.6.0).\n\n## [1.5.0] - 2026-05-23\n\n### Added\n- **`--sovereign` flag for `adk deploy node`** — registers the node with the\n Aitherium hub via federation after deployment. Saves federation credentials\n to `~/.aither/.env.federation`. Accepts `--hub` (custom hub URL) and\n `--tenant` (tenant slug) options. Non-fatal: node runs standalone if\n registration fails.\n- **Federation → fleet cross-post** (Genesis) — sovereign nodes that register\n or heartbeat via `/federation/register` and `/federation/heartbeat` now\n automatically appear in the fleet dashboard (`/fleet/deployments`) with\n `node_type: \"sovereign\"` and live status/metrics.\n- **\"Deploy Sovereign Node\" section on Connect page** (AitherVeil) — Section 8\n with ADK deploy commands, fleet dashboard links, and provision page links.\n- **`deploy-node` action in tunnel proxy** (AitherVeil) — Connect page and\n external callers can now request bootstrap scripts via\n `POST /api/tunnel/devworkspace?action=deploy-node`.\n\n### Fixed\n- **Fleet provision page** — API URL corrected from `/api/fleet/provision` to\n `/api/bridge/genesis/fleet/provision` (matches bridge proxy pattern).\n- **Fleet detail page** — API URL corrected from `/api/fleet/deployments/` to\n `/api/bridge/genesis/fleet/deployments/` (same fix).\n- **Dockerfile** — entry point corrected from `aither-serve` to `adk-serve`\n (matches `pyproject.toml` script registration). Version label updated.\n\n## [1.4.1] - 2026-05-22\n\n### Fixed\n- **macOS executable builds** — dropped `--target-arch universal2` from\n `packaging/build_executable.py`. PyPI wheels for `pydantic_core` and other\n native deps are arch-specific, not fat binaries, so universal2 always failed\n with `IncompatibleBinaryArchError`. Now builds for the runner's native arch\n (`aither-macos-arm64`).\n- **Release workflow strategy** — added `fail-fast: false` so a single\n executable build failure no longer cancels the other OS jobs.\n- **PyPI publish** — added `skip-existing: true` so re-runs of the same\n version don't fail when the artifact already exists.\n- **GitHub Release job** — now runs whenever `pypi` succeeds even if some\n executable matrix entries fail, so the release page is always created with\n whatever binaries did build successfully.\n\n## [1.4.0] - 2026-05-22\n\n### Added\n- **Sovereign template now ships with `agent_core` 5-tier memory** — generated\n backends include the canonical `agent_core/` package (working → episodic →\n semantic → procedural → identity) plus `agent_memory.py` helper, vendored\n from `portal-kit-backend`.\n- `aiosqlite>=0.20.0` added to sovereign template `requirements.txt` for\n local agent_core SQLite storage.\n- Document tombstoning: `evict_cached_conversations_referencing` is wired\n through document-delete handlers in consumer apps so memories citing\n deleted documents are blocked from recall.\n- New tests: 13 smoke tests for `portal-kit-backend/agent_core` covering\n promotion thresholds, decay rates, store roundtrip, tombstone recall\n blocking, circuit breaker state machine, and chat-turn recording.\n\n### Changed\n- `scripts/vendor_agent_core.py` now syncs canonical `agent_core` to 4\n targets: WorkspaceRuntime, ADK sovereign template, and the GargBot +\n Chelle consumer mirrors (previously only 2).\n\n## [1.2.1] - 2026-05-21\n\n### Added\n- `adk train` command group — 6 subcommands: status, launch, logs, cancel, runs, register-gpu\n- `/slash-commands` endpoint — ADK server auto-exposes all 35 CLI commands as structured JSON manifest for AitherShell\n- `/cli/execute` endpoint — AitherShell can run any CLI command through the server\n- `build_command_manifest()` — introspects full argparse tree (commands, args, choices, subcommands)\n- `_register_commands()` extracted from main() for shared parser construction\n\n### Changed\n- `_get_genesis_url()` now probes: Genesis → ADK server → Aitherium cloud gateway (works without Genesis)\n\n## [1.2.0] - 2026-05-21\n\n### Added\n- **Runtime backend switching** — `agent.switch_backend(\"deepseek\")` and `agent.llm.switch_backend()` change provider without recreating agent\n- **Hybrid reasoning** — `agent.set_reasoning_backend(\"anthropic\")` routes effort 7+ to cloud API while keeping local orchestrator\n- **First-class DeepSeek provider** — `deepseek` in effort model table with `deepseek-chat` and `deepseek-reasoner`\n- **TQ4 quantization tiers** — `nano` (6GB, TurboQuant 4-bit), `standard-tq4` (12GB, both models TQ4), `hybrid-tq4` (6GB + cloud reasoning)\n- **`adk backend` CLI** — `list`, `set`, `set-reasoning`, `test` subcommands\n- **`adk quickstart`** — unified first-run wizard (setup + auth + shell)\n- **`adk tools`** — list local + MCP tools with tier markers\n- **`adk backup`** — tarball export of `~/.aither/`\n- **`adk ingest`** — feed docs/files into knowledge graph\n- **`adk setup nemotron`** shortcut — alias for `--tier lite`\n- **`--reasoning-api`** flag in setup — `anthropic`, `openai`, `deepseek`, `gateway`\n- **`--dgx-spark URL`** flag — remote vLLM endpoint configuration\n- **DGX Spark auto-detection** — scans `spark.local`, `192.168.0.33`, `AITHER_DGX_URL` env\n- **Post-setup smoke test** — sends real inference request after container startup\n- **Schema migration system** — version table + migration runner in `memory.py` and `graph_memory.py`\n- **Configurable vLLM port scan** — `AITHER_VLLM_PORTS` env var for custom ports\n- **`aithershell` entry point** — alias for `adk` in pyproject.toml\n- Shell auto-downloads binary on first `adk shell` (no `--install` needed)\n- Shell pre-flight check warns if no backend detected\n- Shell passes full config via env vars (API key, tenant, inference URL)\n- DGX/Remote and Cloud API checks added to `adk doctor`\n- Config fields: `reasoning_backend`, `reasoning_api_key`, `deepseek_api_key`, `dgx_url`, `vllm_extra_ports`\n\n### Fixed\n- `test_aeon_chat_basic` timeout — mock scope was exiting before request\n- `adk doctor` check_disk timeout — cap rglob at 5000 files\n\n## [1.1.7] - 2026-05-19\n\n### Added\n- Remote agent pipeline — `adk onboard` → fleet dispatch → remote inference\n- Agent-to-agent federation with mesh relay\n- `adk connect` for desktop mesh enrollment\n\n## [1.1.6] - 2026-05-19\n\n### Fixed\n- Tool call execution recovery across all backends\n- Hermes tool call fallback parsing for vLLM\n\n## [1.1.5] - 2026-05-19\n\n### Fixed\n- Tool call execution + Hermes fallback parsing improvements\n\n## [1.1.0] - 2026-05-18\n\n### Added\n- Multi-channel gateway (Telegram, Discord, Slack, webhook)\n- Aeon group chat with 7 presets\n- Skills auto-extraction from multi-step sessions\n- MCP stdio server for Claude Code integration\n- `adk gateway`, `adk cron`, `adk skills`, `adk soul` commands\n- SOUL.md import/export for portable agent identity\n- Elysium cloud device-flow authentication\n\n## [1.0.0] - 2026-05-18\n\n### Breaking\n- Package consolidated — `aither_adk` namespace replaced by `adk` + `adk.platform`\n- 93 duplicate files removed from `AitherOS/packages/aither_adk/`\n\n### Added\n- Standalone core primitives (`adk.core`: Agent, Tool, Memory, ModelBackend, Capability, Trace)\n- `adk.platform` sub-package merging all platform toolkit modules\n- Compatibility shims for legacy `from aither_adk.*` imports\n\n### Changed\n- Version synced across pyproject.toml, `adk/__init__.py`, `adk/platform/__init__.py`, npm package\n- Classifier promoted from Alpha to Beta\n\n## [0.16.0] - 2026-04-16\n\n### Swarm Coding Engine & Repowise Integration\n- **`repowise_search` tool** — Semantic + keyword hybrid code search via Repowise, with ripgrep fallback\n- **`swarm_code` tool** — Dispatch complex tasks to 11-agent swarm pipeline (ARCHITECT->SWARM->REVIEW->JUDGE)\n- **`agent.swarm()`** — Async convenience method with configurable mode, effort, timeout\n- **`agent.code_search()`** — Async convenience method returning structured results\n- New tool categories: `repowise` and `swarm` in TOOL_CATEGORIES\n- `repowise_search` and `swarm_code` added to `__init__.py` exports with lazy loading\n- `IDENTITY_DEFAULTS` updated: repowise for code-focused agents, swarm for orchestration agents\n- Standalone graceful degradation: repowise falls back to ripgrep, swarm returns structured error\n\n## [0.13.0] - 2026-04-02\n\n### Graph Faculties — Local Knowledge for Every Agent\n- **CodeGraph** (2,799 lines) — Full Python AST indexer with call graph, keyword/semantic/hybrid query, embedding matrix cache, incremental re-indexing, multi-hop chain expansion\n- **MemoryGraph** (1,339 lines) — Graph-based persistent agent memory with 10 edge types, hybrid query (keyword + semantic + graph expansion), multi-hop recall, pickle persistence\n- **EmbeddingProvider** — 4-backend fallback chain: sentence-transformers (GPU/CPU) -> Ollama -> Elysium cloud -> feature hashing (zero deps)\n- **BaseFacultyGraph** — Abstract base with HMAC-SHA256 validated pickle persistence\n\n### Agent Integration\n- `agent.set_code_graph(cg)` — auto-registers `code_search` + `code_context` tools\n- `agent.set_memory_graph(mg)` — auto-registers `remember` + `recall` + `memory_stats` tools\n- Both graphs inject context into LLM prompts automatically during chat\n\n### Zero-Config Onboarding\n- `adk start` / `adk` (no args) — auto-detect project, index code, detect LLM, persistent memory, interactive chat\n- Works for any directory: Python codebases, doc folders, mixed workspaces\n- Auto-detects LLM: Ollama -> vLLM -> Elysium -> OpenAI -> Anthropic\n- Per-project persistent memory in `~/.aither/memory/<project>`\n- `adk index <path>` — standalone indexing with progress bar and stats\n\n### MCP Gateway\n- `POST /v1/embeddings` — OpenAI-compatible embedding proxy to vLLM-embeddings:8209\n- Available to all tiers (embeddings are free)\n- ADK EmbeddingProvider uses this as Elysium cloud fallback\n\n### Optional Dependencies\n- `pip install aither-adk[graphs]` — numpy for 10x cosine similarity speedup\n- `pip install aither-adk[embedding]` — sentence-transformers + torch for local GPU embeddings\n- `pip install aither-adk` alone — graphs work with feature hashing (zero deps)\n\n## [0.12.0] - 2026-04-01\n\n### Bootstrap & Service Discovery\n- Version handshake -- ADK checks major.minor compatibility with Genesis/Node\n- Background reconnect loop -- ServiceBridge re-probes every 30s in standalone mode\n- Port 8080/8090 documented -- MCP vs OpenAI-compat clearly separated\n- Genesis URL configurable via GENESIS_URL env var (was hardcoded)\n- Standalone mode warning -- visible stderr alert when AitherOS not detected\n- Auto-reconnect on startup when services come online\n\n### Elysium Cloud Inference\n- Unified gateway URL -- gateway.aitherium.com handles auth + billing + inference\n- Streaming inference -- SSE passthrough for /v1/chat/completions with stream=true\n- Auth proxy routes -- /v1/auth/register, /v1/auth/login, /v1/auth/me\n- Billing proxy -- /v1/billing/balance through gateway\n- AitherConnect Elysium fallback -- cloud inference when local Genesis is down\n- AitherDesktop Elysium fallback -- third-tier chat fallback after Node\n\n### Infrastructure\n- /discovery endpoint on Genesis -- unified service URLs/versions/health\n- /api/config/services on Veil -- runtime port config for client-side JS\n- Veil healthcheck gates on Genesis -- unhealthy when backend is down\n- Desktop crash detection 90s->60s (threshold 3->2)\n\n## [0.11.0] - 2026-04-01\n\n### Agent Execution Quality (Claude Code Parity)\n- Raise loop guard block threshold from 3 to 4 -- agents get more room for iterative search\n- Soft synthesis nudge for effort >= 4 (no tool stripping, trust the model)\n- max_output_tokens escalation -- retry up to 3x when response is truncated\n- Tool result pairing guarantee -- synthetic error for orphaned tool_use blocks\n- Micro-compaction of old tool results -- save context tokens on long sessions\n- First-turn tool forcing only for effort >= 6 (trust model for lower effort)\n- Diminishing returns detection -- nudge agent when 3+ turns produce < 500 tokens\n- Message normalization -- merge consecutive same-role messages, strip empties\n- LLM retry with exponential backoff (5 retries, 500ms-16s, jitter)\n\n## [0.9.0] - 2026-03-16\n\nThe \"connected world\" release. Cross-platform identity pairing, voice capabilities, and multi-channel integration.\n\n### Added\n- **Pairing**: Cross-platform identity linking (`adk/pairing.py`)\n - `PairingManager` — SQLite-backed identity linking with 6-char pairing codes\n - Link users across Telegram, Discord, Slack, WhatsApp with 10min TTL codes\n - Canonical session IDs for cross-channel conversation continuity\n - `get_session_id()` returns \"user-{id}\" for paired users\n- **Voice**: Speech-to-text and text-to-speech client (`adk/voice.py`)\n - `VoiceClient` — async STT/TTS/emotion via AitherVoice service\n - Convenience functions: `hear()`, `say()`, `feel()`\n - 6 voice options: alloy, echo, fable, nova, onyx, shimmer\n - Emotion detection with intensity scoring\n- New exports: `PairingManager`, `PairingResult`, `PlatformIdentity`, `VoiceClient`, `TranscriptionResult`, `SynthesisResult`, `EmotionResult`\n\n### Changed\n- `__init__.py` exports expanded with pairing and voice symbols\n\n## [0.6.0] - 2026-03-13\n\nThe \"group mind\" release. Multi-agent group chat, creative tools, and Iris identity.\n\n### Added\n- **Aeon**: Multi-agent group chat engine (`adk/aeon.py`)\n - `AeonSession` — persistent group chat with parallel agent execution\n - 7 presets: balanced, creative, technical, security, minimal, duo_code, research\n - Orchestrator synthesis: Aither summarizes all agent responses\n - Serial execution for Ollama, parallel for vLLM/cloud\n - ConversationStore persistence with `type: \"aeon\"` metadata\n - `group_chat()` one-shot convenience function\n- `aither aeon` CLI command — interactive terminal group chat with color-coded agents\n - `-p/--preset`, `-a/--agents`, `-r/--rounds`, `--no-synthesize` flags\n - `reset` and `quit` commands\n- Server endpoints: `POST /aeon/chat`, `GET /aeon/presets`, `GET /aeon/sessions/{id}`\n- Creative tools in builtin_tools: `image_generate`, `image_refine`, `image_search`, `video_generate`\n- Iris agent identity with visual generation capabilities\n- 48 Aeon tests (data models, presets, chat, context, persistence, server, exports)\n\n### Changed\n- `__init__.py` exports: `AeonSession`, `AeonResponse`, `AeonMessage`, `group_chat`, `AEON_PRESETS`\n\n## [0.5.0] - 2026-03-13\n\nThe \"tenant-ready\" release. Multi-tenant admin, permission grants, safety profiles, and a full setup wizard.\n\n### Added\n- `aither setup` interactive setup wizard with hardware detection, model selection, identity config\n- Strata storage backend: SQLite WAL persistence for conversations, memories, knowledge graphs\n- CLI test runner: `aither test` with auto-discovery and parallel execution\n- Permission grants system for MCP tool access control\n- MCP account management tools\n- LLM provider auto-detection for Ollama and vLLM endpoints\n- Apache-2.0 LICENSE file\n- Elysium desktop sync module\n- Comprehensive CLI test suite (493 tests)\n- Strata storage test suite (972 tests)\n- LLM provider tests (82 tests)\n\n### Changed\n- CLI expanded: `aither setup`, `aither test`, `aither bugreport`, `aither doctor`\n- Strata module rewritten as full local-first storage engine\n- Server startup includes Strata initialization\n- README rewritten with clearer quickstart and architecture docs\n\n### Fixed\n- Dead documentation links\n- Python 3.11 compatibility issues\n- Ruff per-file-ignores configuration\n- Identity provisioning edge cases\n- Docker node-gyp native dependency builds (python3 + build tools)\n\n## [0.4.0] - 2026-03-13\n\nThe \"own your AI\" release. Self-hosted agent OS for people who don't want their data on someone else's servers.\n\n### Added\n- Muse agent identity (creative/artistic generation)\n- Port 8120 to vLLM scan for ExoNodes discovery\n- Public roadmap with milestone-based porting schedule\n- Competitive positioning: self-hosted alternative to cloud-locked AI appliances\n\n### Changed\n- LLM router: compute-aware effort routing (replaces effort-level context gating)\n- README rewritten for sovereignty-first messaging\n- Package description updated\n- Promoted from alpha to stable release\n\n### Fixed\n- Version string consistency between pyproject.toml and __init__.py\n- vLLM port scanning now includes port 8120 (ExoNodes)\n\n## [0.3.1] - 2026-03-09\n\n### Added\n- GraphMemory: SQLite knowledge graph with Ollama embeddings and hybrid search\n- NeuronPool: auto-fire pre-LLM data gathering agents\n- NanoGPT: pure Python char-level transformer with LoRA fine-tuning\n- Safety gates: IntakeGuard (input), LoopGuard (recursion), Sandbox (code exec)\n- Event system: EventEmitter with chat/tool/forge event types\n- Builtin tools: identity-based tool selection\n- ServiceBridge: auto-discovery of AitherOS services\n- Streaming: chat_stream with safety gate integration\n- Auth middleware: Bearer token authentication\n- CLI: `aither init` and `aither serve` commands\n\n### Changed\n- Wired 5 previously disconnected modules into agent loop\n- 522 total passing tests (up from 85)\n\n## [0.3.0] - 2026-03-07\n\n### Added\n- Clean-room agent development kit\n- Multi-backend LLM providers (Ollama, OpenAI, Anthropic)\n- AitherAgent class with @tool decorator\n- SQLite conversation memory\n- OpenAI-compatible server (`aither-serve`)\n- 16 agent identities as package data\n- MCP bridge to mcp.aitherium.com\n- Privacy-centric opt-in telemetry\n- Bug reporting CLI and API\n- FastAPI server with OpenAI-compatible endpoints\n- Hardware auto-detection (5 tiers, 11 profiles)\n- Fleet orchestration and multi-agent coordination\n- A2A mesh protocol\n- Federation protocol for cross-instance agent dispatch\n\n### Initial release\n- 85 passing tests\n- Apache-2.0 license\n"}, "1b62e4c41cd3f9208c92a91807e21dcc9cfd2137": {"t": 1785748149.9935164, "n": 103440, "h": "197c535a410e6dc1", "c": "# Changelog\n\nAll notable changes to aither-adk will be documented in this file.\n\n## [2.47.0] - 2026-08-02\n\n### Added — Bonsai as a first-class default backend for low-resource hardware\n\n- **`pick_backend()` now auto-detects and recommends Bonsai** for CPU-only and\n <6GB VRAM hardware. If the Bonsai container is already running or the GGUF is\n downloaded, it is selected automatically — no explicit `--backend bonsai-local`\n needed. This means `adk quickstart` on a phone, a Pi, or a laptop without a GPU\n now defaults to local inference instead of requiring cloud fallback.\n- **New `bonsai` hardware profile** (`profiles/bonsai.yaml`) — targets 0 GPU,\n 4GB RAM minimum. Documents platform-specific binaries and expected throughput\n for Android (Termux), iOS, Raspberry Pi, Windows, macOS, and Linux.\n- **`bonsai-4b` variant** for ultra-constrained devices (2GB RAM, Android phones).\n- **README hardware profiles table updated** — Bonsai leads the table as the\n lowest-resource option. `adk setup --tier bonsai` documented.\n\n## [2.46.0] - 2026-07-31\n\n## [2.45.0] - 2026-07-29\n\n### Fixed — streaming against an AitherOS-typed SSE backend returned a silent EMPTY answer\n\n- **`OpenAIProvider.chat_stream` now parses BOTH stream dialects.** MicroScheduler's\n `/v1/chat/completions` facade (and Genesis) answer `stream=true` with AitherOS-typed\n SSE events (`event: token`, `data: {\"t\": \"...\"}`) rather than OpenAI chunks\n (`choices[].delta.content`). Parsing only the OpenAI shape yielded **zero chunks and\n no error** — the agent loop (`stream_react`) completed \"successfully\" with an empty\n answer while non-stream `chat()` worked against the same endpoint, which made every\n `/chat/stream` turn on an MS-backed daemon silently empty. The provider now accepts\n typed `token`/`answer`/`complete`/`error` events alongside OpenAI chunks; a trailing\n `answer` event never duplicates already-streamed tokens. Two regression tests pin\n both dialect paths (`tests/test_llm_providers.py`).\n\n## [2.44.0] - 2026-07-27\n\n### Fixed — `adk enroll` silently registered devices into the WRONG TENANT\n\n- **`adk enroll` succeeded without an identity and bound the device to the tenant\n `\"personal\"` instead of the caller's workspace.** `_extract_tenant_slug()`\n (`fleet_enroll.py:217`) falls back to `\"personal\"` when `~/.aither/auth.json` is\n absent, so enrolling a fresh machine reported success while registering it\n somewhere the owner cannot see. Nothing failed, nothing warned, and the endpoint\n simply never appeared in their workspace — the fail-open shape where the\n operation reports success and does the wrong thing.\n\n This bites hardest on unattended installs (phones, headless VMs, cloud images),\n which are exactly the machines nobody is watching closely enough to notice.\n\n `cmd_enroll` now **refuses** when no identity is present, and names the two ways\n to supply one:\n\n adk login # browser flow\n adk login --api-key <key> # headless / phone / VM\n\n Verified by running it with an empty `HOME`: refuses, **exit 1**. (`AITHER_NODE_TOKEN`\n is accepted as an identity, so token-based fleet enrolment is unaffected.)\n\n Found while building a one-command self-host installer for Android's Linux\n terminal, by running the installer rather than reasoning about it.\n\n## [2.39.0] - 2026-07-25\n\n### Fixed — containerized agents were unreachable\n\n- **`docker` runtime: added `-i` to `docker run`.** Without it Docker does not\n attach the container's stdin, so *every* containerized stdio agent (acp, mcp)\n silently never received a request. Measured against a real container: no `-i` →\n the child read nothing; with `-i` → the request round-trips. A real customer\n pack *is* a container, so this runtime was effectively unusable.\n- **`runtime.cmd` is now `shlex`-split** for the `docker` and `node` runtimes.\n `cmd: \"python -u server.py\"` was passed as a single argv element, so docker\n tried to exec a binary literally named `python -u server.py`.\n- `Supervisor._build_command()` extracted from `spawn()` so argv is testable\n without launching a process — the missing `-i` survived precisely because it\n wasn't.\n- Env is deliberately **not** forwarded as `-e KEY=value`: that puts credentials\n in argv, readable by any local `ps` / `docker inspect`.\n\n### Fixed — CodeActLoop `cell_timeout` did not bound CPU-bound cells\n\n- `asyncio.wait_for` cannot preempt a coroutine that never yields, so\n `while True: pass` was never interrupted and hung the agent indefinitely. Now\n also bounded by a line-level trace deadline (`CellTimeout`), scoped to the\n cell's own frames so a cell that awaits cannot fire the deadline inside\n unrelated coroutines.\n- The `CodeActLoop` docstring now states plainly that **it is not a sandbox**, and\n names what a cell can actually do (`import os`, `subprocess`, `open(..., \"w\")`).\n Do not run cells from an untrusted source.\n\n### Added — real containment for CodeAct cells\n\n- **`CodeActLoop(executor=DockerCellExecutor())`** runs each cell in a container\n with `--network none`, a read-only root filesystem plus a noexec/nosuid tmpfs,\n `--cap-drop ALL`, `no-new-privileges`, memory and pid caps, a non-root user, and\n `python -I`. The escapes that succeed in-process — writing a host file, network\n egress, running as root — all fail there, and an infinite loop's container is\n **killed** rather than merely interrupted.\n- `return_result()` crosses the process boundary via a stdout sentinel and is\n deliberately **not** `eval`'d back into the host.\n- Trade-off stated plainly: an isolated cell is a fresh process, so there is no\n live host namespace and no cross-cell state. Containment *and* a live namespace\n together need fork-from-warm snapshotting (`adk/forkd_client.py`'s Firecracker\n target), which requires Linux ≥ 5.7 + KVM.\n- The in-process default is unchanged. Do not point CodeAct at untrusted input\n without `executor=`.\n\n### Fixed — TLS and test-suite integrity\n\n- **`adk/cli.py` sent an `Authorization: Bearer` API key over TLS with\n verification disabled**, exposing it to a machine-in-the-middle. Now routed\n through `adk._tls.tls_verify()`.\n- The `verify=False` ratchet regex-scanned raw lines, so docstrings that merely\n *named* the antipattern were reported as violations — noise that hid the real\n hole above. It now walks the AST for actual keyword arguments.\n- Five `test_inference_proxy` failures were stale assertions pinning the\n pre-MicroScheduler routing and a 5-model roster (it is 6 since `aither-bonsai`).\n Assertions now derive from the roster and encode the tier-access rule, so a\n future model addition doesn't break them.\n\n### Fixed — MCP stdio hung silently on Windows\n\n- `adk/mcp_stdio.py` and `adk/shell/mcp_bridge.py` attached stdin with\n `loop.connect_read_pipe`, which fails inside the Proactor loop's read handler\n such that the read future never resolves — the server didn't crash, it just\n never answered. Both now use the new shared `adk.stdio_compat`\n (`ThreadStdinReader` / `ThreadStdoutWriter`), which `acp_server.py` also uses.\n\n### Added — per-install pack credentials are now actually used\n\n- `adk/pack_credentials.py` was orphaned: mint/revoke were never called, so pack\n installs kept using the shared credential the module was written to replace.\n Both install paths now mint, and a reinstall revokes the outgoing install's\n credential before its metadata becomes unreachable.\n- **New `/packs uninstall <pack-id>`** (aliases `remove`, `rm`) — revokes the\n per-install credential first, then removes the pack. Previously the only revoke\n path was a reinstall, so deleting a pack any other way left a live,\n unrevocable credential. The target is confined to the pack root.\n\n### Fixed — packaging\n\n- The brew formula's own `sha256` was left as `PLACEHOLDER_SHA256` on every\n release, so `brew install` could never verify the download.\n `sync_versions.py` now fills it from PyPI and `--check` **fails** when a\n published version still carries a placeholder or a stale digest.\n- `sync_brew` rewrote *every* `url` in the formula, repointing the `httpx`,\n `pyyaml`, `fastapi` and `uvicorn` `resource` blocks at the aither-adk tarball.\n Fixed with `count=1`; the resource digests are now real.\n- `adk/__init__.py`'s source-checkout fallback `__version__` had been stale since\n 2.32.0 because the canonical release path never ran `sync_versions.py`. That\n check now runs in `sync-adk.yml`.\n\n## [2.38.1] - 2026-07-24\n\nRelease plumbing only — 2.38.0 was tagged before the version manifests\n(npm/brew/winget) were synced, so its publish gate failed and it never reached\nPyPI. 2.38.1 is the same code with the manifests in sync. See 2.38.0 below for\nwhat actually changed.\n\n## [2.38.0] - 2026-07-24 (never published — superseded by 2.38.1)\n\n### Fixed — `adk join` could only ever have worked for us\n\n- **`adk join` defaulted identity to `https://localhost:8115`** — a fleet-INTERNAL\n address. `adk join` is the one command a stranger runs on their own GPU box, so on\n any machine not already running AitherOS locally, step 1 (the GitHub device flow)\n hit a refused connection, and so did node registration and the mesh-key issue. The\n conductor default was already public, which is exactly why this went unnoticed.\n Identity now defaults to `https://idp.aitherium.com`.\n\n This fix landed *after* the 2.37.0 tag, so **2.37.0 and every earlier release on\n PyPI still ship the broken default** — 2.38.0 is the first release a new\n contributor can actually run. Verified live from a non-fleet machine (DGX):\n `POST /auth/github/device/start` → HTTP 200 with a real `user_code` and\n `verification_uri`.\n\n- **Upgrade check compared versions as strings** — `latest != current` reported a\n downgrade as an available upgrade. Now uses `packaging.version` with a numeric-tuple\n fallback.\n\n## [2.37.0] - 2026-07-24\n\n### Added — Agent Onboarding Fabric: bring your own agent, or run a managed one\n\nOnboard an agent built on **any** framework, or let Aitherium run it for you.\n\n- **ACP (Agent Client Protocol), both directions** — `ACPClient` drives any ACP agent;\n `ACPServer`/`serve_stdio` exposes an adk agent to any ACP host (Zed, VS Code,\n JetBrains). Both proven live against Zed's reference `agent-client-protocol`.\n- **Universal Agent Pack** — `AgentPackManifest` + `Supervisor` describe and run an\n external agent across 6 frameworks × 6 protocols, fail-closed on anything unknown.\n- **Protocol drivers** — `get_driver()` returns an `http`, `langgraph_rest`, `a2a`, or\n `mcp` driver, so REST/graph-orchestrated agents can be *managed*, not just connected.\n- **Pack registry** — `PackRegistry.publish/browse/versions/get/yank/verify_digest`,\n SHA256-digested, semver-ordered, validation fail-closed before anything is written.\n- **Managed identity** — `ManagedAgentIdentityProvider` with a real gateway minter and a\n PROVISIONED→REGISTERED→ACTIVE→ROTATED→REVOKED lifecycle; default-deny `authorize()`.\n- **Zero-code connect templates** — `render_connect(framework, …)` emits config pinned to\n each framework's real schema.\n\n### Added — NOOA-style agent capabilities\n\n- **`CodeActLoop`** — code as action: the model writes Python cells that run against a\n persistent namespace over live objects, each cell AST-validated before execution, with\n per-cell timeouts and bounded observations.\n- **`ObjectRegistry` / `render_observation`** — pass by reference: large values become\n handles instead of flooding the context with serialized text.\n- **`EventLog` / `ContextBlocks`** — harness APIs the model itself can call to query its\n own event history and manage named context blocks in KV-cache-stable order.\n- **`has_ellipsis_body` / `@strategy`** — NOOA's `async def f(...) -> X: ...` ergonomic.\n- **`CodeValidator`** — best-effort AST validation (ported from NVIDIA's OO-Agents,\n Apache-2.0; NOTICE included). A defense-in-depth layer, not a sandbox boundary.\n- **`PredictLoop`** — single-shot strategy with structured output (`output_model=`) and\n validation retry; `Strategy` is now a first-class alias of `AgentLoop`, and both loops\n accept a per-call `model=` override.\n- **Typed I/O** — `AgentResult` validates its fields at construction and at the\n `Agent.run` boundary.\n\nAll new capabilities are exported lazily from the top-level `adk` namespace.\n\n## [2.27.0] - 2026-07-18\n\n### Added — Moonshot Kimi K3 provider (DeepSeek-equivalent cloud backend)\n\n- **New `moonshot` provider** across the LLM router: OpenAI-compatible\n `https://api.moonshot.ai/v1`, default model `kimi-k3` (131K context default, up to 1M;\n tool calls, JSON mode, structured output). Wired everywhere `deepseek` is:\n - `LLMRouter(provider=\"moonshot\")` + `_COMPAT_URLS`/`_COMPAT_MODELS`/model tiers\n (small/medium/large → `kimi-k3`)\n - Config: `moonshot_api_key` field (env `MOONSHOT_API_KEY`), saved-config load,\n provider-keys map (`provider_keys.json` key `moonshot`)\n - Auto-detect chain: tried after DeepSeek (config key, then env key)\n - Hybrid reasoning backend: `AITHER_REASONING_BACKEND=moonshot` for effort 7+\n - Reasoning tiers: `backend: moonshot` (alias `kimi`) via new `MoonshotBackend`\n - CLI: `adk backend set moonshot --api-key …`, `adk backend list` shows moonshot\n- No breaking changes; DeepSeek and all other providers unchanged.\n\n## [2.26.0] - 2026-07-16\n\n### Added — Aeon group-chat web UI pack + POST /aeon/stream SSE endpoint\n\n- **New `aeon` UI pack** (`adk/webui/packs/aeon/index.html`) — group chat for multi-agent\n discussion. Mirrors the `llamacpp` pack's auth + SSE pattern (bearer from URL fragment #k=),\n with a preset selector (balanced/creative/technical/security/minimal/duo_code/research) and\n distinct colored agent messages. Synthesis block highlighted. Agent content is HTML-escaped\n before render. Fully self-contained HTML+CSS+JS, light+dark, no external assets. (No\n temperature/max-tokens controls — Aeon runs a fixed multi-agent round and doesn't thread\n per-turn generation params, so exposing them would be a no-op.)\n- **POST /aeon/stream SSE endpoint** — accepts body {message, preset?, agents?, rounds?,\n session_id?}, emits SSE with: `session_start` (participants + orchestrator), `agent_message`\n per non-orchestrator, `synthesis` (if enabled), `error` on exception, `complete` (total tokens\n + latency). Mirrors /chat/stream structure — like every streaming endpoint here it is gated by\n the server auth middleware **when `AITHER_SERVER_API_KEY` is set** (a local `adk up` with no key\n is unauthenticated by design, same as /chat/stream).\n- **Default UI pack flipped to `llamacpp`** — `resolve_ui_pack_name()` now returns `llamacpp`\n instead of `console` when no env/config is set (new users see the clean chat, not the admin\n SPA; existing users with AITHER_AGENT_UI or saved config unaffected).\n\n### Added — swappable agent web UI packs (llama.cpp-style built-in chat)\n\n- The page an agent serves at `/` is now a **swappable UI pack**, so you can\n drop in, test, and deploy different chat frontends without touching the agent.\n Select with `adk ui set <name>` (persists to `~/.aither/config.json`) or the\n `$AITHER_AGENT_UI` env; `adk ui ls` lists packs, `adk ui path` shows what\n resolves.\n- **New built-in `llamacpp` pack** — a clean, self-contained llama.cpp-style\n chat UI (centered conversation, streaming token-by-token with a cursor, a\n Stop button, a settings drawer for temperature / max-tokens that is actually\n honored end-to-end — `/chat/stream` now forwards them to the provider —\n minimal safe markdown, light+dark). Hits the agent's own gated `/chat/stream`\n with the bearer read from the URL fragment (never sent in the URL). Built-ins\n also include `console` (the full admin SPA, still the default) and `minimal`.\n- **Drop-in custom packs**: a UI pack is just a folder with an `index.html` in\n `~/.aither/ui-packs/<name>/` (override via `$AITHER_UI_PACKS_DIR`) — no rebuild.\n- Resolution is **fail-soft**: an unknown/missing pack falls back\n console → minimal, so `/` is never blank.\n\n### Added — MCP-gateway-first platform access + mesh A2A trust\n\n- **Gateway-first platform access** (`adk/client/_gateway_mcp.py`, `GatewayMCPClient`):\n a self-hosted agent connects its MCP **client** to `mcp.aitherium.com` authenticated\n as the OWNER (device-flow / PAT / ACTA token), RBAC-scoped to that identity — so it can\n use platform tools + `get_secret` **whether or not the node runs local AitherOS\n services**. Fail-closed: no token → client disabled, the agent still runs. Wired into\n the server lifespan (`_connect_gateway_mcp`).\n- **`genesis` LLM backend**: `adk backend set genesis` points the agent brain at a local\n Genesis (`http://localhost:8001/v1`, model `workflow`) — the 5090/controller pattern\n where inference is served by the local fleet, not the cloud gateway or a raw vLLM.\n- **`adk mesh` command**: `adk mesh onboard` drives the Conductor 5-step onboard\n (`/v1/mesh/onboard` → registers this node's public key, returns the `endpoint:mesh`\n trust token); `adk mesh ls` lists peer nodes/agents (AitherMesh `/nodes` + Directory)\n with their A2A cards + invoke URLs for discovery.\n\n### Security — A2A inter-agent trust enforcement (fail-closed)\n\n- **`adk/a2a_trust.py`** (new): Ed25519 signature verification + trusted-key check\n (`AITHER_A2A_TRUSTED_KEYS`), opt-in via `AITHER_A2A_REQUIRE_TRUST` (default `false` for\n backward compatibility; `audit` logs untrusted, `true` rejects).\n- **Fixed a fail-OPEN in the inbound A2A handler** (`adk/a2a.py`): the trust check\n previously ran *only when signature headers were present*, so an unsigned request\n bypassed `AITHER_A2A_REQUIRE_TRUST` entirely. Now genuinely fail-closed — required mode\n with no/invalid signature → `403`.\n- **Sovereign template no longer scaffolds `verify=False`**: the generated\n `services/llm.py` now uses the project's own `tls_verify()` policy helper for every\n provider call instead of hardcoding disabled TLS verification.\n- **`adk/_tls.py`**: disabling verification via `AITHER_TLS_VERIFY=false` now emits a\n one-time loud warning (was silent) — the escape hatch stays, the silence goes.\n\n## [2.25.1] - 2026-07-15\n\n### Fixed — self-hosted OpenAI-compatible backends honor a custom base URL\n\n- `adk backend set llamacpp --base-url http://localhost:8090/v1 --model <id>` (and\n `vllm`/`lmstudio`, and the `AITHER_LLM_BASE_URL` env) now actually point the agent\n at YOUR server. Previously the base URL was saved but never read, so an explicit\n `--backend llamacpp` fell back to the provider's public API default (openai.com).\n New `Config.llm_base_url` field + saved-config `inference_url` wiring; the explicit\n backend path in `LLMRouter` now passes it for the local OpenAI-compatible family.\n This is what lets a self-hosted node run its OWN local model (e.g. a PrismML\n Bonsai llama.cpp) as the agent brain instead of ollama/cloud.\n\n## [2.25.0] - 2026-07-15\n\n### Added — node_bootstrap tool pack: bootstrap LLM inference on any hardware\n\n- **New bundled tool pack `node-bootstrap`** (`adk/toolpacks/node_bootstrap/`):\n seven agent tools — `node_detect_hardware`, `node_resolve_recipe`,\n `node_plan_deployment`, `node_apply`, `node_enroll`, `node_register_backend`,\n `node_verify` — plus a CLI shim (`python -m adk.toolpacks.node_bootstrap\n detect|resolve|plan|apply|enroll|register|verify`). Detect a box's hardware,\n resolve a deployment recipe, actually deploy the inference engine (docker\n compose / native / delegate), enroll with a control plane, register the\n backend, and live-verify a completion.\n- **9 hardware recipes** shipped in the wheel: `cpu-1bit-llamacpp`, `cpu-ollama`,\n `cuda-vllm-8gb/24gb/40gb`, `cuda-dual-stack-32gb` (32GB-card co-resident\n stack), `unified-memory-vllm` (DGX/Grace-class), `metal-ollama` (native —\n Docker on macOS has no Metal passthrough), `cloud-api` fallback. Resolution\n scores hardware against min/max VRAM bands, RAM, cores and unified memory;\n ties prefer higher tiers and self-contained recipes over fleet delegates.\n- Security posture: enrollment and backend registration are fail-closed\n (missing token/URL → error dict, never anonymous, never `verify=False`);\n tokens are redacted in outputs; all endpoints come from args/env\n (`AITHER_CONTROL_PLANE_URL`, `AITHER_GENESIS_URL`, `AITHER_AUTH_TOKEN`).\n\n## [2.24.0] - 2026-07-14\n\n### Changed — sync-family consolidation\n\n- **Removed dead code:** `adk/sync.py` (528 lines) deleted — it was unreachable,\n shadowed by the `adk/sync/` package (`import adk.sync` always resolved to the\n package; nothing loaded the module by path — verified).\n- **Grouped the sync domain modules into the `adk.sync` package:**\n `brain_sync`→`adk.sync.brain`, `files_sync`→`adk.sync.files`,\n `lockbox_sync`→`adk.sync.lockbox`, `secrets_sync`→`adk.sync.secrets`,\n `session_sync`→`adk.sync.sessions`, `settings_sync`→`adk.sync.settings`,\n `sync_watermark`→`adk.sync.watermark`. All in-tree importers updated. No\n back-compat shims were left because **zero external/monorepo code imported the\n old root paths** (verified) — a clean move, not a shimmed one. **Breaking only\n for code that imported `adk.<name>_sync` directly** (none known).\n\n### Docs — MCP module layering clarified\n\n- `adk.mcp` (enterprise AitherOS gateway client: auth/billing/token-tracking) and\n `adk.core.mcp` (minimal generic JSON-RPC adapter for any MCP server) are\n DISTINCT layers, not duplicates — each now carries a LAYER/ROLE/See-also\n docstring so they are not mistakenly consolidated.\n\n## [2.23.2] - 2026-07-14\n\n### Fixed — CompletionGate no longer false-fails on correct results\n\n- **Tolerant judge JSON extraction.** The LLM judge parsed its reply with `json.loads(text[first\"{\":last\"}\"])`, which raised `JSONDecodeError` (\"Extra data\") whenever the model appended prose after the verdict or emitted a second object — and `verify()` turned that into a fail-closed verdict, so a genuinely completed task reported `unverified`. New `_first_json_object()` uses `JSONDecoder.raw_decode` from the first `{` that begins a valid object and ignores trailing content (prose, markdown fences, extra objects). A judge that answers with no parseable JSON still fails closed (never a soft pass), now with a clear reason. The same extractor backs `_derive_criteria`.\n- **Bare code-like tokens are hard-checked.** A natural criterion like `output contains ADK_AUTO_OK` (no quotes) previously fell through to the judge; `hard_checks` now also matches bare identifier/code shapes (contains an underscore, or 4+ CAPS/digit chars), with a stopword guard so a descriptive uppercase word (`OUTPUT`, `CONTAIN`) never becomes a false required token. Quote a token to force it regardless.\n- +9 tests (24 total), including the exact trailing-data regression.\n\n## [2.23.0] - 2026-07-14\n\n### Removed — dead modules and internal integrations\n\n- **`adk.swarm`, `adk.provisioning_tools`, `adk.session_sync_integration`, `adk.addon_metering`** — four orphan modules with zero public usage have been deleted. These were internal prototypes kept for legacy monorepo consumers; public packages have not imported them since 2.21.0 (tracked as D-348). **Breaking for anyone directly importing these modules** (import will fail). They are no longer exempt from the orphan-module check.\n- **`adk.aither_bridge`** — the AitherOS-internal IRC ↔ chat gateway bridge is no longer shipped in the public aither-adk package (stripped at sync time). All public importers already degrade behind `except ImportError` (in `adk.server` and `adk.builtin_tools`), so public packages remain unaffected. The module stays in the private AitherOS monorepo for internal use.\n- **Identity document trim** — removed internal agent names from `adk/identities/aither.yaml` delegation guidance, replacing with role-generic wording (\"delegate code review, refactoring, security analysis, performance and testing to specialist agents when available\"). Effort-tier routing philosophy remains unchanged and is product design, not a leak.\n\n### Changed — genericized customer/product names in help text\n\n- CLI help/examples no longer name specific customer deployments (`--scope` template list and `deploy agent` name example use generic placeholders). Functional deploy paths are unchanged.\n\n## [2.22.1] - 2026-07-14\n\n### Fixed — real bugs surfaced by reviving the public CI (red since Jul 5)\n\n- **TLS**: bundled toolpacks (arc-brainpack, cloudflare) disabled certificate\n verification on gateway/vault calls; all now route `adk._tls.tls_verify()`\n (verified by default, internal CA bundle when present).\n- **`system_prompt` contract**: bundled-pack `[PACK DIRECTIVES]` were appended\n even to an explicit `system_prompt`; an explicit prompt now wins wholesale.\n- **Headless secrets**: `_derive_key()` used `os.getlogin()`, which raises\n `OSError` without a controlling terminal (CI, daemons, containers) — falls\n back to `getpass.getuser()` (same value on interactive boxes, so existing\n secret files keep decrypting).\n- **`pack_verifier`**: missing `cryptography` crashed with `UnboundLocalError`\n in the exception handler instead of failing closed; now degrades gracefully\n (and `cryptography` was added to the dev extra so CI tests the real path).\n- **`fleet_manager._kill_pid` (POSIX)**: reported success immediately after an\n asynchronous SIGTERM and never reaped children (zombies read as alive); now\n polls, escalates to SIGKILL, and reaps.\n- **Identity**: `aither` regained the `creative` tool category (dropped\n accidentally in a 2.7.0 WIP sweep).\n\n## [2.22.0] - 2026-07-14\n\n### Changed — internal toolkit relocation\n\n- **`adk.platform` relocated** — the internal aither-platform toolkit (ComfyUI/image-gen\n pipelines, fleet agent clients, parallel CLI/UI/tools stack) has been moved out of the\n adk SDK tree to the standalone internal `aither-platform` package at\n `AitherOS/agents/aither_platform/`. The public package remains unaffected (it already\n excluded `adk.platform` since 2.21.1). Internal AitherOS agent builds now install\n `aither-platform` separately. `adk platform` falls through to the same \"not available\"\n message on public installs; internal builds work via the relocated package.\n\n### Removed — backward-compat re-export deleted\n\n- **`adk.gateway` module removed** — the backward-compatibility re-export of `GatewayClient`\n from `adk.gateway` has been removed. Use `from adk.client import GatewayClient` instead\n (the established public API since 2.21.0). All internal importers have been updated.\n\n## [2.21.1] - 2026-07-14\n\n### Removed — internal toolkit and license-gated product no longer ship publicly\n\n- **`adk.platform`** — the internal aither-platform toolkit (ComfyUI/image-gen\n pipelines, fleet agent clients, a parallel cli/ui/tools stack) is no longer\n included in the public repo or the PyPI wheel. It was internal-only code that\n had been riding along since the aither-platform merge. `adk platform` now\n reports \"Platform toolkit not available\" on public installs; internal AitherOS\n builds are unaffected.\n- **`adk.formbridge`** — the FormBridge form-automation product is license-gated\n (PROFESSIONAL+); its implementation no longer ships in the free package. All\n import sites (`builtin_tools`, `server` route mount) already degrade behind\n `except ImportError`, so nothing else changes behavior.\n- Product test suites (`test_formbridge.py`, `test_encounter_keying.py`,\n `test_flow_tools.py`) are excluded from the public sync alongside them.\n\n## [2.21.0] - 2026-07-14\n\n### Added — Completion gate: verified-or-retried task execution\n\n- **`adk.gate.CompletionGate`** — a composable wrapper that closes the \"silent\n success\" gap: an agent's ReAct loop returns whatever the model produced and\n calls it done (`finish_reason = stop/max_steps/length`), which is a *liveness*\n signal, not a *completion* one — so an agent can \"succeed\" while doing nothing.\n `CompletionGate` runs the agent, verifies the result against acceptance\n criteria, retries with the failure fed back, and returns the response with\n `finish_reason` set to `verified` or an honest `unverified`. Never a false pass.\n - **Hard-first, un-soft-passable checks** (`adk.gate.hard_checks`): a filesystem\n path a criterion says must exist is checked on disk; a required quoted token\n must appear in the output. Only genuinely subjective criteria fall through to\n an optional LLM judge (reuses the agent's own model), which **fails closed**\n if unavailable.\n - **`gated_run(agent, task, criteria=..., max_retries=2)`** — one-shot helper.\n - `auto_criteria` (LLM writes the machine-checkable definition-of-done from the\n task), a `verifier=` override for custom checks (tests-pass / HTTP probe), and\n a `requires_action` short-circuit so it never fights human-approval pauses.\n - Compose it onto any agent exposing `run(task) -> resp{.content, .finish_reason}`\n (`AitherAgent`, `core.Agent`, or a thin adapter over a remote dispatch).\n - Exported from the top-level package: `from adk import CompletionGate, gated_run`.\n\n## [2.19.0] - 2026-07-12\n\n### Added — Eve agent importer + durable A2A tasks + canonical agent discovery\n\n- **`adk pack import <eve_agent_path>`** — convert external Eve agents to AitherADK packs\n (from GitHub or local filesystem). Maps Eve structure (instructions → system prompt,\n agent.ts → agent.yaml, skills/*.md → skills, connections/*.ts → MCP, tools/*.ts →\n tools/node verbatim) to AitherADK format with a single command. Eve agents (compiled\n manifest format, schema v35) become installable packs compatible with `adk pack install`.\n- **Durable A2A task lifecycle (`TaskManager` backed by FileStore)** — agent-to-agent\n tasks now survive container restarts. Tasks persist as append-only JSONL\n (`.adk/tasks.jsonl`); in-memory dict for fast access; `create_task()` / `update_status()` /\n `add_artifact()` / `add_message()` all save to disk. Google A2A v0.3.0 spec compliant.\n- **Canonical agent card discovery** — `A2AServer.mount()` now exposes:\n - `GET /.well-known/agent-card.json` — Agent Card (canonical; contains agent identity,\n capabilities, skills, endpoints)\n - `GET /.well-known/agent.json` — Agent Card (legacy, redirects with 308 to canonical path)\n - `POST /a2a` — JSON-RPC 2.0 for message send/task lifecycle\n - `GET /a2a/tasks/{id}/subscribe` — SSE streaming task updates\n- **Four built-in agent packs now complete and catalogued**:\n - `hermes` — architecture review & trade-off analysis\n - `openclaw` — web research & source verification\n - `claude-code` — feature development & debugging\n - `analyst` — structured data inference & anomaly detection\n All discoverable via `adk packs` / `adk install pack:<name>`.\n- **Tolerant tool-call JSON parsing** — malformed tool JSON (extra properties, missing\n name/arguments, string-encoded nested args, missing keys) is now gracefully degraded\n instead of silently failing. `_parse_tool_json()` accepts both `\"arguments\"` and\n `\"parameters\"` keys, coerces string-encoded args, and skips unparseable calls instead\n of crashing the turn.\n\n### Known limitations\n\n- Eve agent importer: remote GitHub fetching not yet implemented (local paths only).\n- Durable task store: currently JSONL (suitable for thousands of records, not millions).\n\n## [2.18.0] - 2026-07-10\n\n### Added — AitherShell command center + Claude Code session management\n\n- **`aither sessions`** — Claude Code session manager: interactive full-screen\n browser (type-to-filter, transcript preview, deep full-text `search`, resume\n **in the current terminal** or as Windows Terminal tabs), crash guard\n (`guard install` = at-logon watchdog that snapshots live sessions and\n auto-restores the whole set after a terminal crash), and `ingest [--watch]`\n (incremental, secret-guarded sync of session conversations into the local KB\n / CompanyBrain via the standard ingest pipeline).\n- **`aither hq`** — command-center dashboard: per-service fleet health, LLM\n queue (depth/VRAM/models), Pulse alerts, live session count, inbox unread —\n auto-refreshing, with one-key jumps into chat, sessions, inbox, agents,\n brief, watchtower, and docker recovery.\n- **`aither inbox`** — unified queue over CommCore mail, Relay\n mentions/notifications, and Pulse alerts; open/mark-read/DM-reply from the\n terminal. Every source degrades independently.\n- **`aither agents`** — roster console + `agents ask -e <1-10> <agent> <q>`\n (effort-tiered ask via Genesis, with automatic `/chat/stream` fallback when\n the `/agent/sync` execute gate rejects host callers) + forge dispatch\n inspection + routine health.\n- **`aither palette`** — universal fuzzy picker across actions, sessions, and\n services; **`aither brief`** — renders Atlas's executive briefing in the\n terminal; **`aither watch`** — fleet watchtower with named wedge-signature\n detection (docker-WSL wedge, LLM-queue stall, crash-loop uptime regression)\n and optional docker auto-recovery.\n- All fleet reads go through a shared fail-soft `FleetClient` (internal-CA\n TLS via `adk._tls`, dashboard-grade timeouts, per-source degraded states).\n- Node shell-cli (`@aitherium/shell-cli` 1.13.0): `aither\n sessions|hq|inbox|palette|brief|watch|agents|docker` now pass through to the\n Python shell instead of being intent-classified as chat prompts.\n\n## [2.17.1] - 2026-07-05\n\n### Fully self-service mesh join — no customer key handling\n\n- **Auto-join headscale off the onboard response.** The conductor now auto-issues a\n headscale pre-auth key for NAT'd nodes and returns it in the `/v1/mesh/onboard`\n response; `mesh.join` consumes it and brings up the tunnel automatically. A customer\n runs one command — they never mint, fetch, or set a headscale key. Explicit\n `--headscale-key`/`AITHER_HEADSCALE_AUTH_KEY` still take precedence.\n- **Security:** `_tailscale_up` now redacts the auth key from all logs and exceptions\n (a failed `tailscale up` previously echoed the full argv, leaking the key into logs).\n\n## [2.17.0] - 2026-07-05\n\n### Added — Headscale mesh transport + local Qdrant + AitherConnect onboarding\n\n- **Headscale NAT-friendly mesh transport.** When raw WireGuard UDP:51820 is not\n viable (customer boxes behind NAT, corporate firewalls, CGNAT), `adk mesh join\n --headscale` routes the mesh tunnel through Tailscale's Headscale control plane.\n The overlay IP is still Conductor-assigned (10.77.0.0/16); Headscale provides\n only the transport layer. Configured via `AITHER_MESH_TRANSPORT=headscale`,\n `AITHER_HEADSCALE_URL` (default: https://headscale.aitherium.com), and\n `AITHER_HEADSCALE_AUTH_KEY` (pre-generated key). Automatic fallback to raw\n WireGuard if Headscale setup fails.\n- **Local Qdrant vector DB provisioning.** `adk stack` now provisions Qdrant\n locally when `AITHER_VECTOR_DB=qdrant` is set, enabling offline-first RAG\n without external dependencies. Tiered: local Qdrant in-fleet, cloud Nexus\n for enterprise multi-tenant deployments.\n- **Conductor URL public fallback.** `adk mesh join` resolves the internal\n `aitheros-conductor:8193` hostname; if unreachable (e.g., self-hosted nodes),\n it falls back to the public `conductor.aitherium.com:8193` endpoint,\n eliminating bootstrap configuration friction.\n\n### Documentation\n\n- **Self-hosting runbook.** New `docs/SELF_HOSTING.md` covers deploying AitherOS\n on customer infrastructure with Qdrant + mesh + AitherConnect onboarding.\n- **Mesh transport constraints documented.** Raw WireGuard requires public\n UDP:51820 endpoint (cloud instances, dedicated hardware); Headscale for\n NAT'd networks. Noted in CLI help and module docstrings.\n\n## [2.15.0] - 2026-07-04\n\n### Added — built-in web admin console + portal-profile settings sync\n\n- **Web admin console.** The page `adk up` opens (served at `/` and `/ui`) is now a full,\n self-contained console — Chat plus six admin tabs: **Backend** (view/switch LLM provider,\n set keys, test connection), **Packs** (list/enable/disable/reload tool packs),\n **Sessions** (browse/delete conversations), **Logs** (redacted agent-log tail),\n **Graph** (knowledge-graph search/neighborhood/stats), and **MCP** (add/remove external\n MCP servers). No build step, no CDN — a single packaged HTML asset. The original minimal\n streaming chat page stays at `/chat` for back-compat.\n- **Admin API (`adk/admin_api.py`).** ~20 endpoints under `/admin/*`, all bearer-gated by the\n existing middleware (never in `_skip_auth_paths`). Operates on the live agent — LLM backend\n swap (`LLMRouter.switch_backend`), pack reload, and MCP registration take effect without a\n restart.\n- **Settings sync (`adk/settings_sync.py`).** The user's portal profile is the source of\n truth: on startup the agent pulls `preferences.adk` and applies it over the local\n `~/.aither/config.yaml` cache; admin-console edits push a debounced snapshot back to\n `PUT /api/settings/preferences`. Provider API keys and MCP auth headers are device-local\n secrets and are never included in the pushed snapshot. Opt out with `AITHER_SETTINGS_SYNC=false`.\n\n### Security\n\n- **Timing-safe token comparison.** The server bearer check now uses `hmac.compare_digest`\n (the token is reachable over the public tunnel; a byte-wise `!=` leaked it to timing attacks).\n- **MCP add is SSRF-guarded + confirmed.** Adding an external MCP server is a two-step\n prepare→confirm flow; URLs resolving to loopback / private / link-local / metadata addresses\n are rejected (override with `AITHER_ALLOW_PRIVATE_MCP=1` for trusted local servers).\n- **Config editor is allowlisted.** `PATCH /admin/config` writes only safe scalar prefs and\n hard-denies fields that could execute code or redirect traffic at startup (e.g.\n `aither_toolpack_dirs`, backend base URLs).\n- **Log tail is scrubbed.** Bearer tokens, `#k=` fragments, and `sk-`/`aither_sk_` keys are\n redacted from the log-tail endpoint; the tunnel log is excluded.\n\n## [2.14.8] - 2026-07-04\n\n### Added — `adk up` for normal humans (no API key, no prereqs, live chat)\n\n- **Hosted-brain default — no API key required.** When there's no local model and no\n provider key, `adk up` signs in (device-flow) and routes inference through the gateway\n using the portal token (`AITHER_LLM_BACKEND=gateway` + bearer). A non-technical user\n never needs to obtain or paste an LLM API key. Explicit `--provider`/local model still\n take precedence; unattended `--yes` with no token/backend fails cleanly (exit 3).\n- **cloudflared auto-download.** If the tunnel binary is missing, `adk up` downloads the\n right platform asset to `~/.aither/bin` (SHA256-verified against the release; opt out with\n `AITHER_CLOUDFLARED_REQUIRE_CHECKSUM=0`) instead of exiting with an install hint.\n- **Built-in streaming chat page.** The agent now serves a self-contained chat UI at `/`\n (and `/chat`) with a live \"thinking…\" indicator and token-by-token streaming — so a human\n has somewhere to talk to it with immediate feedback instead of a blank wait. `adk up` opens\n it locally and passes the callback bearer in the URL fragment (`#k=…`), so `/chat/stream`\n stays authenticated (not an open proxy) with nothing to paste.\n- **Human-readable failures.** `adk up` errors now carry a plain-English `next_action`\n (kept machine-readable in `--yes` JSON).\n\n### Fixed\n\n- `adk up` chat link pointed at a non-existent `/settings/agent` route; it now points at the\n agent's own streaming chat page.\n\n## [2.14.7] - 2026-07-04\n\n### Added\n\n- **`adk up` — one command to run a persistent, fleet-connected agent.** Collapses\n the scattered onboarding surface (`host`/`quickstart`/`connect`/`login`/`setup`)\n into a single command that starts `aither-serve` (single agent, identity `aither`\n by default), opens a Cloudflare quick-tunnel for fleet reachability, registers with\n the portal, and installs a reboot-persistent autostart entry (Windows Task Scheduler\n / systemd-user / launchd). Detaches by default so the terminal is freed; `--foreground`\n blocks. Reuses the existing device-flow login, cloudflared discovery, provider-key\n store, and `_preflight_check` backend detection.\n- **Fully non-interactive path for autonomous agents.** `adk up --yes` (or any\n non-TTY invocation) takes ZERO prompts — everything resolves from flags, env, and\n saved config — and emits a single machine-readable JSON status line with meaningful\n exit codes (`0` ok, `2` bad-input, `3` no-backend, `4` tunnel-missing, `5` register-failed).\n Degrades to local-only (warn, not fail) when a portal token is absent unless\n `--require-register` is set.\n- **`adk down` / `adk status --json` companions.** `down` stops the agent + tunnel,\n best-effort deregisters, and removes the autostart entry; `status` reports the running\n agent (liveness + `/health` + tunnel + registration), with `--json` for agents/CI.\n- **`adk stack`** — the previous `adk up` behaviour (supervise the Room + Ollama consumer\n stack) now lives here; `adk up` is the connected-agent command.\n\n### Internal\n\n- New `adk/agent_daemon.py`: detached process spawn, status file\n (`~/.aither/adk-up.json`), pid liveness/teardown, and cross-platform autostart install.\n\n## [2.14.6] - 2026-07-04\n\n### Added\n\n- **`adk.preflight` — self-bootstrap capability probe.** `run_preflight(agent,\n spec)` wakes an agent up knowing what it is actually plugged into: a bounded\n (1.5s) parallel `LivenessProbe` over the router's resolved backends\n (primary/reasoning), the embeddings winner, remote MCP tools, the structured-ML\n / `/ml/teach` endpoints, and voice — returning an honest `CapabilityReport`.\n Status vocabulary distinguishes reachability from entitlement\n (`OK`/`MISSING`/`UNREACHABLE`/`AUTH`/`TIER_DENIED`/`UNSUPPORTED`), a hosted slot\n never prints a bare `OK`, and a required-but-unsatisfied slot aborts before the\n loop instead of blind-firing. Headless task/rules resolution + an allowed-roots\n policy binding (`builtin_tools.set_allowed_roots`, which resets the memoized\n cache so the override actually enforces).\n- **Multimodal (image) messages.** `Message.content` now accepts an OpenAI-style\n content-part list (`str | list[dict]`), passed through verbatim to the\n OpenAI-compatible body (gateway / vLLM / OpenAI). New `adk.llm.multimodal`\n helpers — `image_message()`, `image_content_parts()`, `to_image_url()` — build\n the parts and encode local image bytes/paths into `data:` URIs.\n- **Real vision preflight probe.** The vision slot is no longer unconditionally\n `UNSUPPORTED`: it sends a discriminable image (a solid red square) and requires\n the model to name the color, so a blind/text-only model cannot false-pass. `OK`\n only on a real round-trip; `AUTH` on 401/403; `UNSUPPORTED` when there is no\n OpenAI-compatible provider or the model cannot see the image.\n\n## [2.14.5] - 2026-07-04\n\n### Added\n\n- **`adk.reasoning.mcts` — generic Monte-Carlo Tree Search library.** A reusable\n MCTS engine (ported from the internal `UnifiedMCTS`, with all internal couplings\n removed) that any agent can drive over its own environment:\n - `MCTSEnvironment` protocol (`get_state_hash` / `get_actions` / `step` /\n `evaluate` / `clone`) — implement it and search.\n - Three **optional, default-off** model seams — `TransitionModel`, `PolicyModel`\n (PUCT priors), `ValueModel` — so a learned world-model / policy / value can be\n injected without touching the core; with all three `None` the engine is a plain\n UCT search.\n - `ObservedTransitionModel` adapter — a cheap online lookup world-model (hit =\n exact stored transition; miss = identity + negative \"unknown\" reward +\n `is_uncertain`, biasing exploration toward learning), with JSONL save/load for\n cross-run reuse.\n - `UnifiedMCTS(config).search(env)` returns an `MCTSResult` (best action, value,\n confidence, principal-variation path) and an optional `MCTSTrace`\n (visit-distribution policy target) for distillation.\n- **Self-bootstrapping agent machinery.** Build an agent from a declarative spec\n instead of bespoke wiring:\n - `adk.bootstrap.build_agent_from_spec(path) -> (AitherAgent, RunCtx)` — resolves\n prompts, activates required/optional tool packs, and carries a `memory_map`\n (a `node_type -> (role, tier)` override for memory writes, never a gate).\n - `adk.prompts.PromptBridge` — resolves an agent's prompt bindings from files\n (relative to the spec) or `AitherPrompts` dot-keys (optional import).\n - `adk.packs.PackActivator` — eager activation of installed tool packs by name\n over `register_tool_packs`; a required pack that registers zero tools raises\n `PackUnavailable`, optional packs soft-degrade.\n - `python -m adk.bootstrap.generic_agent <spec.yaml>` inspects a spec (resolved\n name, activated packs + tool counts, prompt keys, memory map) without running a\n live loop.\n- **`deep_research` tool pack** — a full multi-engine web-research workflow as\n `dr_*` agent tools (keyless search, page fetch + cache, site mirror, a citable\n findings knowledge graph, and markdown/PDF/DOCX report generation), activatable\n by name via the tool-pack loader.\n\n## [2.14.4] - 2026-07-04\n\n### Added\n\n- **`structured_ml` tool domain** — zero-shot structured-data inference for agents,\n backed by TabFM (tabular classification/regression) and TimesFM (time-series\n forecasting). Three tools:\n - `tabular_classify(support_rows, target, query_rows)` — classify rows from a\n labeled support set, in-context (no training step; up to 10 classes).\n - `tabular_regress(support_rows, target, query_rows)` — predict a numeric target.\n - `timeseries_forecast(series, horizon)` — forecast future values of a series.\n\n The tools POST to a structured-ML inference service resolved from\n `AITHER_STRUCTURED_ML_URL` (default `http://localhost:8192`), reject non-http(s)\n URLs, and summarise oversized responses to protect the agent's context.\n- **`Capability.STRUCTURED_INFERENCE`** capability token and a new **`analyst`**\n identity/pack that ships the `structured_ml` domain by default. The domain is\n opt-in — only identities that enable the `structured_ml` category get the tools.\n\n## [2.14.3] - 2026-07-03\n\n### Fixed\n\nAll four bugs below were found by actually booting `adk run` against a real\nchat model and driving a real tool-calling request end-to-end (not inferred\nfrom reading the code) — each one silently broke the \"local agent uses its\nconfigured model and its tools actually work\" path.\n\n- **`AITHER_MODEL` / `config.model` silently ignored by the OpenAI-compatible\n backend**: `LLMRouter.__init__` never read `config.model` into `self._model`\n unless the caller passed `model=` directly to the constructor — which nothing\n did. Every explicit model choice (env var, config file) was dropped, the\n provider fell back to a hardcoded per-backend default (`gpt-4o-mini` for the\n `openai` backend), and an unrecognized model name got silently remapped\n server-side to the wrong backing model. Concretely: setting\n `AITHER_MODEL=aither-orchestrator` had no effect — every request actually\n went to a different model that was never meant to be called directly and\n can't reliably use tools.\n- **`OPENAI_BASE_URL` ignored for explicit `--backend`/`AITHER_LLM_BACKEND`\n selection**: `LLMRouter._auto_detect()`'s explicit-backend branch hardcoded\n `base_url=None` when constructing the provider, instead of reading\n `config.openai_base_url` (which two other call sites in the same file\n already did correctly). Any self-hosted OpenAI-compatible endpoint\n (MicroScheduler, vLLM, LM Studio) configured via `OPENAI_BASE_URL` was\n silently ignored in favor of `https://api.openai.com/v1`.\n- **Tool-call steering never engaged on default-effort chat**: the retry that\n nudges a model back into structured tool-calling when it responds with prose\n instead of a function call was gated behind `effort >= 6`. Default `effort`\n for a plain `agent.chat(message)` call resolves to `5`, so the safety net —\n built specifically for this failure mode — never fired for default requests.\n Also now forces `tool_choice=\"required\"` on the retry instead of relying on a\n text nudge alone.\n- **Every non-string builtin-tool parameter silently typed as `\"string\"` in\n the generated tool schema**: `builtin_tools.py` uses\n `from __future__ import annotations`, so `_extract_parameters()` in\n `tools.py` was reading postponed (string) type hints (`\"int\"`, not `int`)\n straight out of `fn.__annotations__`, which never matched\n `_type_to_schema`'s type-object keys and silently fell through to\n `{\"type\": \"string\"}`. Every builtin tool with an `int`/`float`/`bool`/`list`\n parameter was affected — e.g. `file_read(start_line: int, end_line: int)`\n advertised both as strings, so a model correctly calling the tool with\n `\"start_line\": \"0\"` crashed the actual implementation with\n `unsupported operand type(s) for -: 'str' and 'int'`. Fixed by resolving\n hints through `typing.get_type_hints()` instead of the raw annotations dict.\n `_coerce_arguments()` — the runtime safety net that's supposed to coerce a\n model-supplied `\"0\"` string into `0` before a tool call executes — had the\n exact same raw-`__annotations__` bug and silently no-opped for every builtin\n tool; fixed the same way.\n- **Streaming tool-loop could hang a connection forever**: `chat_stream()`'s\n fallback to the sync tool loop (tool calls can't stream token-by-token) had\n no timeout, so a model that didn't cleanly terminate a tool-calling round\n trip left the SSE connection open indefinitely with zero bytes sent. Bounded\n with a 60s `asyncio.wait_for`; on timeout the stream yields a clear message\n instead of hanging.\n\n## [2.14.2] - 2026-07-03\n\n### Fixed\n- **AitherShell session amnesia**: the REPL generated a fresh `session_id` for\n EVERY message, so conversation history never followed across turns (the\n server stored each turn under a different session). One stable id is now\n minted per shell run; `/resume` and `/new` semantics unchanged, and the\n cross-restart auto-restore of the last session now actually works.\n\n## [2.14.1] - 2026-07-02\n\n### Added\n- **Account-tied licensing via device-flow login** — `adk login` now persists the account\n license returned by the device-flow handshake to `~/.aither/license.json` (base64\n `license_key` → `{payload, signature}` envelope), so entitlements from the authenticated\n account carry into every later `adk` invocation without re-auth. Pairs with the fleet-side\n node tool-pack gate that grants packs by plan-tier rank.\n- **`can_use_formbridge` / `can_use_untether` entitlements** (PROFESSIONAL+ rank ≥ 3) backing\n the FormBridge and UNTETHER agent tool-packs.\n\n### Fixed\n- `adk` CLI: guard the optional `memory_graph` singleton (was a latent `NameError` on the\n `/stats`, `/memory`, and save paths when memory was not constructed).\n\n## [2.14.0] - 2026-07-02\n\n### Added\n- **`adk.memory_wiki` — LyraWiki-style self-managed semantic knowledge** (opt-in; nothing constructs it by default): `MemoryWiki(graph_memory)` maintains curated wiki ARTICLES as `wiki_article` nodes (title/slug, LLM-owned markdown with `[[wikilinks]]`, embedding, durable tier, `CONSOLIDATED_FROM` edges to source memories, governance-ledgered revisions).\n - `consolidate(llm, since=, budget=)` clusters unconsolidated episodic/fact nodes (embedding + keyword), drafts/UPDATEs articles via the INJECTED llm callable (`str -> str` or `messages -> str`, sync or async — no hard model dependency), cites source node ids, marks contradicted sources superseded, then DEMOTES consolidated sources to a fast-decay tier via `promote()` (represented knowledge doesn't need raw copies).\n - `lint(llm=None)` — deterministic health checks (orphan wikilinks, empty/stale articles, live-but-contradicted pairs) + an optional LLM audit pass.\n - `prune(relevance_floor=0.05, hard_delete_after_days=14)` — article relevance = tier freshness × reinforcement (reinforce-on-recall) + link-degree to LIVE memories; below the floor → reversible governance tombstone; entombed past the window → **HARD-DELETE** (the tombstone snapshot itself is purged — content and embedding irrecoverable; the true-deletion path). A node is never deleted without a tombstone first.\n - `recall(query, limit)` — article-first RAG (curated articles rank above raw nodes) that reinforces returned articles.\n- **`adk.routines` — agent heartbeat + self-programmed routines**: `RoutineStore` is a durable registry (`~/.aither/routines/{agent}.json`, `AITHER_DATA_DIR`-aware, injectable path) of cron-scheduled self-prompts `{name, cron, instruction, enabled, last_run, last_result, tags}` that rehydrates into the existing `adk.cron.CronScheduler` on `start()`. A fire runs `agent.chat(instruction)` with the agent's full toolset — the agent programs its future self with its own adk. Guardrails: `max_routines` (12), a 5-minute min fire interval per routine, a per-fire timeout, and every result ledgered (truncated) to `last_result`. Direct-callable routines (`register_direct`) fire a bound method instead of a self-prompt. Self-management tools (`routine_create/list/update/pause/resume/delete/run_now`) ship as OpenAI tool defs + handlers (`build_routine_tools` / `register_routine_tools` / `routine_tool_defs`); the handlers ONLY touch the RoutineStore — they can never modify agent config or safety-relevant settings (the leash principle).\n- **`AitherAgent(routines=True, memory_maintenance=True)`** (both default **False** → the default agent is byte-identical): `routines=True` builds the RoutineStore + scheduler, registers the self-management tools, and starts the heartbeat lazily on the first `chat()` (or via explicit `start_routines()`). `memory_maintenance=True` registers DEFAULT routines — `wiki_consolidate` (every 2 h), `wiki_lint` / `wiki_prune` / `graph_sweep` (daily) — as DIRECT method fires so memory upkeep runs even on tiny models, while staying visible/manageable via `routine_list`.\n- `TombstoneStore.purge(tombstone_id)` — the hard-delete endpoint: irrecoverably removes a tombstone snapshot and rewrites the persisted store without it (consumed by `memory_wiki.prune`).\n\n## [2.13.6] - 2026-07-02\n\n### Added\n- **Qdrant backend for GraphMemory dataplane sync — TRUE two-way, verified live.** Set `AITHER_FLEET_QDRANT_URL` and the sync uses Qdrant (`upsert` + `scroll` + filtered `search`) instead of Nexus. This is what actually delivers cross-agent memory sharing: a node is upserted with its OWN embedding under a deterministic per-(tenant,node) point id (idempotent re-push), and `fleet_pull` reliably SCROLLS a tenant's points back — the enumerate Nexus lacks (its `/search` doesn't return ingested docs and `/export` needs lancedb, which has a hard dependency conflict in the fleet). **Proven live with 2 agents:** agent-1 ingests → auto-syncs to the tenant dataplane; a fresh agent-2 in the same tenant `fleet_pull`s all of agent-1's nodes and can search them; a different tenant pulls nothing (isolation). Dimension-safe (never mixes vector dims in a collection). Local SQLite stays source of truth; best-effort, never raises.\n\n## [2.13.5] - 2026-07-02\n\n### Fixed\n- **CRITICAL — GraphMemory dataplane sync push was 100% failing.** `_fleet_push_node` sent `source_type=\"graph\"` / `content_type=\"graph_node\"`, which AitherNexus rejects as invalid enums (HTTP 500) — so NO graph node ever replicated in 2.13.4. Now sends the accepted `source_type=\"manual\"` / `content_type=\"text\"` (graph provenance is carried in `metadata.synced_from`). **Verified live** against a fleet Nexus: a 4-node ingest replicates with `pending==0` and the collection count reflects it.\n\n### Added\n- **Swarm-awareness notification** — on a successful sync batch, `GraphMemory` emits a best-effort `graph.synced` event (tenant/workspace/agent/collection/count) to `AITHER_FLEET_EVENTS_URL` so OTHER agents in the tenant/swarm can pull the fresh data and deconflict in-flight work (push-based awareness instead of polling). No-op unless the env is set.\n\n### Known limitation\n- Cross-agent **rehydration** (`fleet_pull`) is only reliable when the fleet Nexus is backed by lancedb (persistent + exportable). An in-memory Nexus stores on `/ingest` but neither its `/search` nor `/export` returns the docs — two-way read needs the persistent Nexus.\n\n## [2.13.4] - 2026-07-02\n\n### Added\n- **Memory-maintenance primitives on `GraphMemory`** (all opt-in; default behaviour byte-identical):\n - **Reinforce-on-recall**: `recall_with_activation(..., reinforce=True)` bumps `reinforcement_count`/`last_reinforced` on every RETURNED node's metadata mirror (one UPDATE per node, WAL-safe) — making the `MemoryRecord` reinforcement fields the activation scorer reads LIVE. Default `False` = zero writes.\n - **`sweep(now, archive_below=0.05, max_nodes=None, dry_run=False)`**: read-time decay enforced at rest — archives nodes past their tier TTL with freshness×reinforcement score below `archive_below` and `reinforcement_count <= 1`; `max_nodes` additionally archives the lowest-scored overflow. PERMANENT tier immune. Every archive is a reversible governance tombstone (`TombstoneStore.recover`) + a FORGET ledger entry; a node is never deleted without a tombstone. `dry_run` returns the would-archive list without acting.\n - **`promote(node_id, tier=?, role=?)`**: re-tier/re-role a node IN PLACE (tier/role columns + metadata mirror) — edges, embedding and ledger history survive; ledgers an UPDATE entry when governance is on.\n- `MutationType.UPDATE` ledger entry kind (in-place re-tier/re-role).\n- **Per-tenant dataplane sync for `GraphMemory`** — replicate the local graph to a per-tenant vector dataplane (Nexus/Qdrant) so a sovereign agent's memory is durable + rehydratable across restarts and hosting boundaries. `fleet_push_all_nodes()` (catch-up), `fleet_pull()` (best-effort semantic top-up), `fleet_sync_pending()`, and auto-sync on ingest (`_maybe_autosync` + `drain_sync()`). **On by default** (`AITHER_FLEET_SYNC=auto`; only `false/0/off` disables) with an inferred target (local Nexus `:8122` or the gateway in cloud mode) and a SEPARATE collection (`AITHER_FLEET_GRAPH_COLLECTION`, default `graph_memory`) from the KV memory sync. New `GraphMemory(tenant_id=, workspace_id=, fleet_url=, auto_sync=)` constructor args override env (required for multi-tenant host processes). Node `synced` column + schema v4 migration. TLS verify is CA-bundle-aware (`AITHER_CA_BUNDLE`) for the internal-TLS mesh. Local SQLite stays the source of truth; every sync path is best-effort and never raises into ingest.\n\n## [2.13.3] - 2026-07-02\n\n### Added\n- **Canonical self-deploying embeddings provider** (`adk.embeddings`): one embedding provider for the whole SDK so vectors are portable across scopes (platform / tenant / sovereign) — same model + dimension everywhere. Pinned to `nomic-embed-text` (768-d; the vLLM `--served-model-name` for nomic-embed-text-v1.5). Lazy, single-flight resolution chain: explicit `AITHER_EMBEDDINGS_URL` → local vLLM (`:8209` then `:8120`, HTTPS-then-HTTP) → local Ollama → gateway (`AITHER_GATEWAY_EMBEDDINGS_URL`) → auto-deploy a local vLLM embeddings container if a GPU + Docker are present (opt out with `AITHER_EMBED_AUTODEPLOY=0`) → CPU sentence-transformers (384-d, degraded) → feature-hash (384-d, degraded, always works). Every batch is dimension-tagged so callers never silently mix 768-d and 384-d vectors. `get_default_embedder()`, `embed_texts()`, `embed_one()`, `get_provider()`, `reset_provider()`.\n\n### Changed\n- **`GraphMemory` now defaults its embedder to the canonical provider** (opt out with `AITHER_GRAPH_EMBEDDER=legacy`), so every adk agent shares one 768-d space. Added a meta-table dimension guard: the index is pinned to its first embedding's dimension and later different-dimension vectors are refused (kills 768↔384 mixing that silently poisons cosine similarity).\n\n## [2.13.2] - 2026-07-01\n\n### Added\n- **Session persistence**: AitherShell now auto-saves the active session_id to `~/.aither/config.yaml` after each chat turn and auto-restores it on the next shell launch. Multi-turn conversations survive across restarts — the messages were always persisted in SQLite + JSON, only the session pointer was missing.\n- `/new` command to start a fresh session (clears saved session_id).\n- `/sessions` command to list recent sessions with agent name, message count, and timestamp. Resume any with `/resume <id>`.\n\n## [2.13.1] - 2026-07-01\n\n### Fixed\n- **CRITICAL:** `deploy_connect()` now sends a HEAD request to verify the GitHub release asset exists before downloading, with a clear error if the asset is missing.\n- **CRITICAL:** Compose file URLs are now pinned to a stable commit hash (configurable via `AITHER_COMPOSE_PIN` env) instead of tracking the mutable `main` branch.\n- **CRITICAL:** `deploy_adk_node()` now falls back to starting `adk-serve` natively when Docker is unavailable, instead of blocking with \"Docker not installed\".\n- **HIGH:** GHCR login failure now blocks deployment immediately with actionable guidance, instead of warning and continuing to a confusing image-pull failure.\n- **HIGH:** Health checks now stream the last 30 lines of Docker Compose logs on failure, so the user can see why a container didn't start.\n- **HIGH:** vLLM port allocation now probes for conflicts before assigning; if port 8200 is busy, workers are automatically remapped to the next free port.\n- **MEDIUM:** Compose file downloads are now cached with ETag; re-running `adk deploy node` skips re-downloading an unchanged file.\n- **MEDIUM:** DGX Spark discovery now respects `AITHER_DGX_HOSTS` env (comma-separated) for custom network topologies, instead of only checking `spark.local` and `192.168.0.33`.\n- **MEDIUM:** Cloud API keys (Anthropic, OpenAI, DeepSeek) are now validated with a lightweight probe during `adk setup` infra scan, so invalid keys surface early.\n- **MEDIUM:** `deploy_connect()` now validates the extracted extension has a `manifest.json` and warns if it's missing.\n\n## [2.13.0] - 2026-07-01\n\n### Removed\n- **`adk.faculties.memory_graph`** — the pickle-based `MemoryGraph` module is deleted. `from adk.faculties import MemoryGraph` now resolves to the canonical `adk.graph_memory.GraphMemory` for back-compat.\n- **`adk.platform.memory`** — the entire legacy memory subsystem (`MemoryManager`, `UnifiedMemorySystem`, `GameEngine`, `StoryboardEngine`, `AnchorGenerator`) is deleted. Agent memory is `adk.memory.Memory` + `adk.graph_memory.GraphMemory`.\n\n### Changed\n- `adk work` command now uses `GraphMemory` instead of the removed pickle-based faculty graph.\n- `adk.MemoryGraph` and `adk.faculties.MemoryGraph` lazy exports now resolve to `adk.graph_memory.GraphMemory`.\n- `agent.set_memory_graph()` kept for back-compat but documented as deprecated; agents already wire `GraphMemory` automatically in `__init__`.\n- Platform startup (`adk.platform.infrastructure.startup`) gracefully handles missing `MemoryManager`.\n- Platform CLI module listing and health check updated to reflect removed memory module.\n\n## [2.12.7] - 2026-07-01\n\n### Deprecated\n- `adk.faculties.MemoryGraph` — emits `DeprecationWarning` on instantiation; use `adk.graph_memory.GraphMemory` (SQLite-backed, governed, embedding-native) instead.\n- `adk.platform.memory` package — the legacy `MemoryManager`, `UnifiedMemorySystem`, `GameEngine`, and `StoryboardEngine` modules now emit `DeprecationWarning` on import; use `adk.memory.Memory` and `adk.graph_memory.GraphMemory` for new code.\n\n### Changed\n- Updated `adk.faculties` docstring and usage examples to point at the canonical memory surface.\n\n## [2.12.6] - 2026-07-01\n\n### Changed\n- Trimmed public package debt by removing grid runbook artifacts and shell/room binary placeholder directories from published artifacts.\n- Removed stale public-sync allowlist entries for deleted phase test files and added explicit public-sync guards for binary placeholder directories and grid artifacts.\n- Updated the public README grid note so it no longer links to runbook files excluded from the SDK package.\n\n## [2.12.5] - 2026-07-01\n\n### Added\n- Added best-effort fleet memory sync from local ADK memory into Nexus/Qdrant-compatible RAG stores, including batch catch-up and vector-search recall hooks.\n- Added saved config/env support for fleet memory URL, collection, and sync mode.\n\n### Changed\n- Excluded FormBridge runtime/test internals from public ADK packaging and public repo sync.\n\n## [2.12.4] - 2026-07-01\n\n### Added\n- Added a hot-reload endpoint for installed agent packs so newly applied pack tools can be picked up without restarting `aither-serve`.\n\n### Fixed\n- Fixed synchronous `ChatRelay` calls on Python 3.12 when no event loop is running; async WebSocket delivery is still scheduled when a loop exists.\n- Aligned gateway inference tier tests with the current public ladder: free, starter, pro, enterprise, platform.\n\n## [2.12.0] - 2026-06-28\n\n### Added — one-command local inference + upgrade path (click-to-run)\n- **`adk quickstart-local`** — detects hardware, picks a backend\n (Ollama / llama.cpp / vLLM), installs it, downloads a model, verifies, and\n prints next steps. Bootstraps Ollama itself via winget/brew/install.sh when\n missing (no \"go install it\" dead-ends). Default model `gemma4:e2b`.\n- **`adk backend switch <ollama|llamacpp|vllm>`** (+ `backend status`) — migrate\n the active deployment between engines; re-points config and smoke-tests.\n- **`adk install pack:<name>`** (+ `adk install list` / `adk packs`) — install\n ready-made agent packs (openclaw, hermes, claude-code) into `~/.aither/agents`.\n\n### Fixed (found via live end-to-end testing on Windows + CUDA)\n- quickstart-local reported success on a dead endpoint (smoke test was warn-only);\n a failed smoke now fails the command, with a service-readiness wait.\n- `--model` resolved to `None` (getattr default never fired) → uses the real default.\n- Smoke test: 30s→180s timeout for cold model load; accepts reasoning-model output\n (empty `content` at low token budgets is no longer a false failure).\n- `adk install pack:<name>` colon syntax (argparse rejected it before dispatch).\n- `adk backend status` health probe used a nonexistent `/health` path → 404; now\n probes `/v1/models` (uniform OpenAI-compatible liveness for all three backends).\n- Windows: removed non-ASCII glyphs from printed strings that crashed the cp1252\n console (a `✓` even made a successful install report failure).\n\n## [2.11.1] - 2026-06-14\n\n### Fixed — `adk host` / `adk login` device flow (autonomous onboarding)\nFound driving `adk host` against production; all broke real device-flow login:\n- **Device flow hit the portal FRONTEND, not AitherIdentity.** `adk login`/`adk host`\n POSTed `/auth/device/code` to portal/veil.aitherium.com, which 307-redirects API auth\n to `/login`. Added `_resolve_identity_url()` mapping the aitherium.com topology to\n `idp.aitherium.com` (the Identity API); localhost/bespoke pass through unchanged.\n- **Cloudflare 403'd urllib.** `idp.*` sits behind Cloudflare, which blocks urllib's\n default `Python-urllib/3.x` UA. Device-code + poll requests now send a real\n User-Agent + Accept (verified urllib→idp 200, was 403).\n- **`adk host` now self-authenticates via device flow** when there's no token, and\n **retries via device flow on a 401/403** (a stale saved token no longer dead-ends\n registration).\n\n## [2.11.0] - 2026-06-14\n\n### Added — attach your own MCP \"hands\" to a self-hosted agent\nA self-hosted agent now has the full brain (your model) · body (this loop) · **hands**\n(your tools) trifecta. The AitherOS platform relays the MCP servers you registered for\nyour agent in the `/stream` request body as `mcp_endpoints`, and the loop wires their\ntools into the ReAct turn:\n- **`adk/mcp_endpoint_tools.register_mcp_endpoint_tools(agent, endpoints)`** — speaks MCP\n JSON-RPC (`tools/list` to discover, `tools/call` to invoke) and registers a namespaced\n proxy tool (`{endpoint}__{tool}`) for each advertised tool. Mirrors `app_proxy_tools`.\n Best-effort: an unreachable endpoint registers nothing and never raises.\n- **`POST /stream` and `POST /chat/stream`** accept an optional `mcp_endpoints` list\n (`[{name, url, [headers]}]`); `_aitheros_stream` attaches them to the agent before the\n turn. Existing callers are unaffected (the field is optional).\n\n## [2.10.1] - 2026-06-14\n\n### Fixed — self-hosted `aither-serve` was crash-on-startup (2.10.0 regression)\nA fresh `pip install aither-adk && aither-serve --backend deepseek` could not boot. Found\nand fixed while standing up a real self-hosted DeepSeek agent end-to-end:\n- **Startup `NameError: name 'port' is not defined`** in three lifespan helpers\n (`_join_aithernet`, `_init_a2a_server`, `_start_mesh_hosting`) — they referenced a bare\n `port` no longer in scope. Now use `config.server_port`, and `main()` writes the resolved\n `--port`/`--host` back to `config` so every lifespan helper and the invoke_url see the\n actual bound port.\n- **Fleet self-registration crashed the whole server** — the registration handlers caught\n `(ImportError, RuntimeError, OSError, ConnectionError)` but **not** `httpx.HTTPError`, so a\n `RemoteProtocolError`/connect error talking to the control plane took the agent down. All\n network-facing lifespan handlers now also catch `httpx.HTTPError` (non-fatal, as intended).\n- **`--backend` was ignored when `AITHER_API_KEY` was set** — `_auto_detect` picked the cloud\n gateway before the explicit backend, and `_try_elysium_fallback` then replaced the chosen\n provider. An explicit backend (deepseek/openai/anthropic/your own vllm/ollama) now always\n wins; \"auto\"/\"gateway\" still flow through detection. The operator's own brain is never\n silently hijacked.\n- **Approved-tool trace truncated after `/sessions/{id}/confirm`** — the SSE relay treated\n `AgentResponse.tool_calls_made` (a `list[str]`) as dicts (`tc.get(...)`), raised\n `AttributeError`, and an over-narrow `except` swallowed it so the client saw only\n `session_start` + heartbeats. The relay now handles string tool names, the `except` is\n broad (always emits a typed `error` + terminal `complete`), and the module-level\n `_strata_ingest` `NameError` + Chronicle `session_id` kwarg mismatch are fixed.\n\n## [2.10.0] - 2026-06-13\n\n### Added — human-in-the-loop tool approval (`adk/approval.py`)\n- **Pause before a gated tool, resume on decision.** An agent can now pause its turn before\n executing a sensitive tool and wait for a human's allow/deny — so a managed control plane\n (or any operator) can gate tool use without forking the loop.\n- **Policy via `AITHER_TOOL_APPROVAL`** — a comma-separated list of tool names (optionally\n `agent:tool`), or `*` for every tool. Empty = no gating (unchanged behavior).\n- **Survives restarts / days-later approvals.** The paused turn (user message + pending tool\n calls) is persisted to disk keyed by `session_id`; resume is a fresh request, not a held\n connection. Decisions are recorded per `(session_id, tool_name)`.\n- **`AgentResponse.requires_action` / `.pending`** — set when a turn paused; `pending` lists\n the gated tool calls awaiting a decision.\n- **`AitherAgent.resume(session_id, decisions)`** — records the decisions and re-runs the\n paused turn (the gate consumes them: allowed tools execute, denied tools get a denial\n observation, the turn continues — and may pause again on a different gated tool).\n- **Server:** `POST /sessions/{id}/confirm` records decisions and streams the resumed turn;\n `/stream` emits a `requires_action` event when a turn pauses.\n\n## [2.9.0] - 2026-06-13\n\n### Added — universal LLM continuation primitive (`adk/llm/continuation.py`)\n- **`run_continuation()` + `stitch()`** — one shared continue-until-complete primitive. When a\n completion stops because it hit the output token cap (`finish_reason == \"length\"`), it continues\n the generation and STITCHES the chunks (overlap-dedup + restart-detect) into a complete answer,\n instead of every call-site hand-rolling its own retry. Bounded three ways (max rounds AND total\n output ceiling AND a no-growth break), and it never mutates the caller's message history.\n- **`LLMProvider.chat_with_continuation()`** and **`LLMRouter.chat_with_continuation()`** — every\n provider AND the router inherit the primitive, so any call-site gets continuation for free.\n- Kill-switch `ADK_LLM_CONTINUATION=off`; tunables `ADK_LLM_MAX_CONTINUATIONS`,\n `ADK_LLM_MAX_TOTAL_OUTPUT_TOKENS` (defaults: 2 rounds / 8192 tokens).\n\n### Changed\n- The ReAct loop's truncation recovery now uses the shared primitive. The previous inline\n doubling-retry REPLACED the partial response (losing earlier text in `resp.content`) and injected\n continuation scaffolding into the live message history; it now STITCHES and leaves history clean.\n Tool-call turns are never continued as text.\n\n### Included — previously-committed-but-unreleased changes\n- 2.8.2: TLS verification on by default (`verify=False` removed).\n- 2.8.1: baked license + pack-signing public keys.\n- 2.8.0: version-bump release.\n\n## [2.7.0] - 2026-06-05\n\n### Added — provider-agnostic LLM cost levers (at the `LLMProvider` ABC)\n- **Prompt caching.** New `ProviderCapabilities` (`prompt_cache: explicit|automatic|none`, `batch`) and\n normalized `LLMResponse.cache_read_tokens`/`cache_write_tokens`, so the \"tokens saved\" meter never\n depends on which backend served the turn. Anthropic inserts `cache_control` breakpoints over the stable\n system+tools prefix; OpenAI/DeepSeek caching is automatic and their `cached_tokens` are now surfaced\n (previously discarded). Default-on in `AitherAgent.chat`; override with `cache=False`.\n- **ResponseCache** (`adk/llm/cache.py`) — exact-match response cache with a pluggable backend, wired into\n `LLMRouter` as an opt-in (`LLMRouter(response_cache=…)` + per-call `cacheable=True`). OFF by default — no\n determinism change unless opted in.\n- **Gemini provider** (`adk/llm/gemini.py`) with explicit `cachedContents` caching; registered in the router.\n- **Async batch runner** (`adk/llm/batch_runner.py`) — `run_batch()` over the Anthropic Message Batches /\n OpenAI Batch APIs (~50% off) with a transparent fallback to concurrent `chat()` where unsupported.\n- **Graph-RAG retriever interface** (`adk/graph_retriever.py`).\n\n### Added — principal authority (prompt-injection defense enforced outside the model)\n- `adk/auth.py`: `Principal` / `AuthContext` / `PrincipalResolver` (+ deny-all default).\n- `ToolRegistry.execute(name, args, auth=…)` — a default-deny authorization gate at the tool boundary, so a\n forbidden tool call is blocked regardless of what the LLM emits. `ToolDef` gains `required_clearance` /\n `action_class`. Threaded through `AitherAgent.chat`; `auth=None` is fully backward-compatible.\n- `adk/channel_auth.py`: Telegram webhook-secret + CEO-id checks and HMAC-signed approvals.\n\n## [2.6.4] - 2026-06-05\n\n### Fixes\n- **Loop-guard nudges corrupted the message history → DeepSeek 400 mid-run.**\n When the ReAct loop emitted multiple tool calls in one assistant turn and the\n loop guard fired a WARN/BLOCK/CIRCUIT_BREAK on one of them, it spliced a\n standalone `system` nudge message *between* the tool results. DeepSeek (and\n strict vLLM chat templates) require an assistant `tool_calls` message to be\n followed by exactly one `tool` result per `tool_call_id`, contiguously — so the\n interleaved message produced `400 … insufficient tool messages following\n tool_calls message` and aborted the turn (this is reliably hit after crossing\n the 15-call circuit-break threshold, where every subsequent call WARNs).\n Per-call guidance is now folded into that call's own `tool` result, and any\n standalone steering is deferred to a single message AFTER the whole batch —\n preserving the `tool_calls`↔`tool_results` pairing every backend requires.\n Regression test: `deep-research-agent/tests/test_msg_structure.py`\n (loopguard-flood scenario).\n\n## [2.6.3] - 2026-06-04\n\n### Fixes\n- **Transient provider errors killed a run on the first blip.** A single\n `502 Bad Gateway` / `503` / `529 overloaded` / `429` from the upstream API\n aborted the whole request — the Anthropic provider had **no retry at all**, and\n the OpenAI-compatible provider only retried `429`. Both now retry transient\n statuses (`408/429/500/502/503/504/529`) with `Retry-After`-aware exponential\n backoff (3 attempts), and surface the provider's body if all retries are\n exhausted. Non-transient `4xx` (e.g. `400`) still fails fast. Applies to\n `chat` and `chat_stream` on both providers.\n\n## [2.6.2] - 2026-06-04\n\n### Fixes\n- **DeepSeek (and strict OpenAI-compatible backends) 400'd on long tool\n conversations.** The ReAct loop legitimately injects mid-conversation\n `system` steering messages (the \"[DIMINISHING RETURNS]\" hint after several\n low-output tool iterations, loop-guard nudges). OpenAI tolerates a non-leading\n `system` message; DeepSeek rejects it with a 400. Long research runs\n (many `fetch_url` calls) reliably tripped this. Fix: non-leading `system`\n messages are demoted to `user` at the OpenAI-compatible provider boundary\n (`_demote_nonleading_system`) — steering intent preserved, payload valid on\n every backend; leading system prompt(s) untouched.\n- **Opaque HTTP errors.** `resp.raise_for_status()` discarded the provider's\n response body, so a 400/422 surfaced as a bare `Client error '400 Bad\n Request'` with no cause. `_ensure_ok` now includes the provider's actual\n message (e.g. `context_length_exceeded`, `Invalid 'messages'`) in the raised\n error, for both `chat` and `chat_stream`.\n\n## [2.6.1] - 2026-06-04\n\n### Fixes\n- **`MCPServer.mount()` returned HTTP 422 for every `/mcp` call.** Under\n `from __future__ import annotations` the route handler's `request: Request`\n annotation is a string that FastAPI resolves against the *module* globals, but\n `Request` was imported locally inside `mount()` — so FastAPI couldn't resolve\n it and treated `request` as a required query param. The MCP server endpoint\n (`initialize`, `tools/list`, `tools/call`) was unusable when mounted. FastAPI\n symbols are now imported at module level (guarded; the `[node]` extra still\n gates actual use). Any agent's tools can now be exposed as a working MCP\n server with `MCPServer(...).mount(app)`.\n\n## [2.6.0] - 2026-06-04\n\n### Streaming + scaffolding\n- **`AitherAgent.stream_chat`** — reliable streaming sibling of `chat()`: native\n tool-calling loop (concludes on its own, unlike the text-protocol `stream_react`)\n that emits live `tool`/`tool_result` events and streams the answer as `token`\n events. `token_delay` paces tokens for a typing effect.\n- **`adk new <template>`** — scaffold a full, runnable template app. Ships the\n `deep-research` template (a cited web-research agent: pack + server + web UI):\n `adk new deep-research`.\n\n### Fixes\n- **BYO-key no longer community-capped.** The free-tier monthly token cap now applies\n only to the metered Aitherium gateway backend, never to a user's own provider key\n (Anthropic/OpenAI/DeepSeek/Ollama/vLLM). Previously it could brick a self-hosted\n agent after ~100k tokens on the user's own key.\n- **Stronger, grounded memory recall.** `chat()` now injects the top-6 knowledge-graph\n hits (untruncated) with a hard no-fabrication instruction — was top-3 truncated to\n 200 chars, which surfaced partial facts and let weaker models invent the rest.\n- **Tool-arg coercion.** `ToolRegistry.execute` coerces LLM-supplied args toward each\n parameter's annotated type, so a tool no longer crashes when a model passes a list\n or string where an int/float/bool was expected.\n\n### Docs\n- New `docs/AGENT_DEV_GUIDE.md` — the golden path for building agents/packs.\n\n## [2.5.0] - 2026-06-04\n\n### Continual learning — agents that learn from runs and reuse memory\n\nAgents now improve the second time they do a task instead of starting from scratch.\n\n### Added\n- **Recall-before**: `AitherAgent.chat()` searches the local `SkillStore` and injects the\n top matching learned skills as a system block, so the model reuses proven procedures.\n- **Learn-after**: `_learn_after()` runs after each response — reinforces (`success_count++`)\n any skills that were recalled+reused, and extracts + saves a NEW skill from successful\n multi-tool runs via `SkillExtractor`. Wired at both `chat()` return paths.\n- Gated by `AITHER_SKILLS` (default on). The previously-unused `SkillStore`/`SkillExtractor`\n are now connected to the run loop.\n\n### Notes\n- Pairs with the platform-side LearnedProcedure substrate (recipes → scored procedures →\n skills/tools/A2A/packs → MCTS priors → evolution). The SDK shares memory via the Spirit\n bridge when configured.\n\n## [2.0.0] - 2026-06-02\n\n### Open-core boundary (BREAKING)\n\nThis release draws a single, enforced free/paid line. The free tier stays\ngenuinely useful (real agent, typed + graph + code memory, ReAct, ~essential\ntools); paid tiers unlock the *scale* capabilities via portal.aitherium.com.\n\n### Added\n- **`adk/licensing.py`** — the entitlement keystone. Resolves a tier\n (COMMUNITY by default), verifies optional portal-signed `~/.aither/license.json`\n (Ed25519), and answers `can_use_fleet/channels/cron/swarm/auto_neurons`,\n `is_agent_licensed`, `max_effort`, etc. **Fail-closed**: no/invalid/expired\n license → free tier; an unsigned license never grants premium.\n `AITHER_TENANT_SLUG=aitherium` → unrestricted INTERNAL tier.\n- **Moat guard** (`scripts/check_moat_boundary.py`) wired into the publish\n workflow — blocks any wheel that leaks the moat or drops the keystone.\n- Tests: `tests/test_licensing.py`, `tests/test_moat_boundary.py`.\n\n### Changed (gating — fail-closed)\n- Free tier effort is capped at 3 (reasoning effort 7–10 needs Professional).\n- Monthly token budget hard-block on the free tier (no silent overspend).\n- Gated behind a paid tier: fleet mode (`FleetConfig`), cross-agent delegation\n (`AgentForge.delegate`), channel adapters, cron scheduler, proactive\n auto-neurons, and `swarm`/`swarm_code` dispatch.\n\n### Removed\n- **`adk/nanogpt.py`** (on-device training IP) removed from the published SDK\n and the public repo; relocated to the internal AitherOS moat. Excluded from\n the wheel as defense-in-depth.\n- Pre-2.0 releases shipped this IP and ungated capabilities and are being\n yanked (PyPI) / removed (GitHub releases). See `scripts/yank_leaky_releases.py`\n and `scripts/purge_public_leaks.sh`.\n\n## [1.20.0] - 2026-05-27\n\n### Fixed\n- **Pack discovery path mismatch** — `_find_local_packs()` now scans both\n `~/.aither/packs/` and `~/.aitheros/packs/`, so packs installed via\n `adk pack install` are discovered by `adk-workspace`.\n\n### Added\n- **Auto-register on pack install** — `adk pack install` now auto-registers\n with the portal if the pack contains an `agent.yaml` with a `portal` section.\n One command does download + extract + register.\n- **Flux event on agent registration** — `developer_portal.py` emits\n `agent.registered` Flux event so the portal fleet UI updates in real-time.\n\n## [1.15.0] - 2026-05-26\n\n### Added\n- **Cloud memory** — `adk quickstart --cloud` now tests gateway memory and\n auto-configures Spirit URL so memories persist across devices via\n `gateway.aitherium.com/v1/memory/teach` and `/v1/memory/recall`.\n- **Config→env export for cloud mode** — `Config.from_env()` now exports\n `AITHER_CLOUD_MODE`, `AITHER_SPIRIT_URL`, and spirit path env vars from\n saved config so Memory and other modules pick them up without needing\n explicit env vars set by the user.\n- **Cloud Quick Start section** in README — 3-command setup for users who\n just want a Claude/GPT-4/DeepSeek API key and the full agent harness.\n- **`--memory` profile for node deployment** — `adk deploy node --memory`\n adds Spirit (8087) + WorkingMemory (8101) containers for persistent\n vector memory. Without this, agents use local SQLite only.\n- **Configurable vLLM in node compose** — `AITHER_VLLM_MODEL`,\n `AITHER_VLLM_GPU_UTIL`, `AITHER_VLLM_CTX_LEN`, `AITHER_VLLM_QUANT_ARGS`\n env vars let `adk setup` write GPU-detected tier settings to `.env`\n and have the node compose pick them up (TQ4 for small GPUs, BNB for large).\n\n### Fixed\n- **Memory cloud routing** — `Memory._spirit_teach()` and `_spirit_recall()`\n now use configurable paths and auth headers for gateway proxy. Previously\n cloud-only users silently lost all Spirit memory calls.\n\n## [1.9.0] - 2026-05-24\n\n### Added\n- **Composable ToolPack system** — agents can load tool packs from\n `.toolpack.yaml` directories. Drop a pack in `~/.aitheros/packs/` or\n set `AITHER_PACKS_DIR`, and any agent discovers it automatically.\n- **`tools.packs` in agent.yaml** — list tool pack IDs to auto-load\n (e.g. `packs: [crm-tools, calendar-pro]`).\n- **`register_tool_packs(agent, pack_ids, packs_dir)`** in `builtin_tools` —\n programmatic pack loading for agents.\n- **`AITHER_TOOL_PACKS` env var** — comma-separated pack IDs loaded on\n agent init (alternative to YAML config).\n- **Unknown builtin categories resolve as pack IDs** — `tools.builtin:\n [file_io, crm-tools]` now works: `crm-tools` is looked up as a pack.\n\n## [1.8.0] - 2026-05-24\n\n### Removed\n- **11 dead modules** (3,023 lines) — `agent_deploy`, `cloud_deploy`, `create`,\n `phonehome`, `sdk_bridge`, `sovereign`, `telemetry`, `telemetry_config`,\n `error_reporter`, `anonymizer`, `package`. None were imported anywhere.\n- **29 ghost exports** from `__init__.py` — `secrets`, `otel`, `fs_sandbox`,\n and `vector_memory` modules were listed in `__all__` and `__getattr__` but\n the backing files never existed. Accessing them raised `ModuleNotFoundError`.\n- **Stale dist/ artifacts** — removed old wheels/tarballs (v0.4.0–v1.4.1, 11 MB).\n- **Orphan test scripts** — `test_phase1.py` and `test_phase23.py` (one-off\n validation scripts not part of the pytest suite).\n\n### Fixed\n- **Version mismatch** — `__init__.__version__` now matches `pyproject.toml`\n (was stuck at 1.6.0).\n\n## [1.5.0] - 2026-05-23\n\n### Added\n- **`--sovereign` flag for `adk deploy node`** — registers the node with the\n Aitherium hub via federation after deployment. Saves federation credentials\n to `~/.aither/.env.federation`. Accepts `--hub` (custom hub URL) and\n `--tenant` (tenant slug) options. Non-fatal: node runs standalone if\n registration fails.\n- **Federation → fleet cross-post** (Genesis) — sovereign nodes that register\n or heartbeat via `/federation/register` and `/federation/heartbeat` now\n automatically appear in the fleet dashboard (`/fleet/deployments`) with\n `node_type: \"sovereign\"` and live status/metrics.\n- **\"Deploy Sovereign Node\" section on Connect page** (AitherVeil) — Section 8\n with ADK deploy commands, fleet dashboard links, and provision page links.\n- **`deploy-node` action in tunnel proxy** (AitherVeil) — Connect page and\n external callers can now request bootstrap scripts via\n `POST /api/tunnel/devworkspace?action=deploy-node`.\n\n### Fixed\n- **Fleet provision page** — API URL corrected from `/api/fleet/provision` to\n `/api/bridge/genesis/fleet/provision` (matches bridge proxy pattern).\n- **Fleet detail page** — API URL corrected from `/api/fleet/deployments/` to\n `/api/bridge/genesis/fleet/deployments/` (same fix).\n- **Dockerfile** — entry point corrected from `aither-serve` to `adk-serve`\n (matches `pyproject.toml` script registration). Version label updated.\n\n## [1.4.1] - 2026-05-22\n\n### Fixed\n- **macOS executable builds** — dropped `--target-arch universal2` from\n `packaging/build_executable.py`. PyPI wheels for `pydantic_core` and other\n native deps are arch-specific, not fat binaries, so universal2 always failed\n with `IncompatibleBinaryArchError`. Now builds for the runner's native arch\n (`aither-macos-arm64`).\n- **Release workflow strategy** — added `fail-fast: false` so a single\n executable build failure no longer cancels the other OS jobs.\n- **PyPI publish** — added `skip-existing: true` so re-runs of the same\n version don't fail when the artifact already exists.\n- **GitHub Release job** — now runs whenever `pypi` succeeds even if some\n executable matrix entries fail, so the release page is always created with\n whatever binaries did build successfully.\n\n## [1.4.0] - 2026-05-22\n\n### Added\n- **Sovereign template now ships with `agent_core` 5-tier memory** — generated\n backends include the canonical `agent_core/` package (working → episodic →\n semantic → procedural → identity) plus `agent_memory.py` helper, vendored\n from `portal-kit-backend`.\n- `aiosqlite>=0.20.0` added to sovereign template `requirements.txt` for\n local agent_core SQLite storage.\n- Document tombstoning: `evict_cached_conversations_referencing` is wired\n through document-delete handlers in consumer apps so memories citing\n deleted documents are blocked from recall.\n- New tests: 13 smoke tests for `portal-kit-backend/agent_core` covering\n promotion thresholds, decay rates, store roundtrip, tombstone recall\n blocking, circuit breaker state machine, and chat-turn recording.\n\n### Changed\n- `scripts/vendor_agent_core.py` now syncs canonical `agent_core` to 4\n targets: WorkspaceRuntime, ADK sovereign template, and the GargBot +\n Chelle consumer mirrors (previously only 2).\n\n## [1.2.1] - 2026-05-21\n\n### Added\n- `adk train` command group — 6 subcommands: status, launch, logs, cancel, runs, register-gpu\n- `/slash-commands` endpoint — ADK server auto-exposes all 35 CLI commands as structured JSON manifest for AitherShell\n- `/cli/execute` endpoint — AitherShell can run any CLI command through the server\n- `build_command_manifest()` — introspects full argparse tree (commands, args, choices, subcommands)\n- `_register_commands()` extracted from main() for shared parser construction\n\n### Changed\n- `_get_genesis_url()` now probes: Genesis → ADK server → Aitherium cloud gateway (works without Genesis)\n\n## [1.2.0] - 2026-05-21\n\n### Added\n- **Runtime backend switching** — `agent.switch_backend(\"deepseek\")` and `agent.llm.switch_backend()` change provider without recreating agent\n- **Hybrid reasoning** — `agent.set_reasoning_backend(\"anthropic\")` routes effort 7+ to cloud API while keeping local orchestrator\n- **First-class DeepSeek provider** — `deepseek` in effort model table with `deepseek-chat` and `deepseek-reasoner`\n- **TQ4 quantization tiers** — `nano` (6GB, TurboQuant 4-bit), `standard-tq4` (12GB, both models TQ4), `hybrid-tq4` (6GB + cloud reasoning)\n- **`adk backend` CLI** — `list`, `set`, `set-reasoning`, `test` subcommands\n- **`adk quickstart`** — unified first-run wizard (setup + auth + shell)\n- **`adk tools`** — list local + MCP tools with tier markers\n- **`adk backup`** — tarball export of `~/.aither/`\n- **`adk ingest`** — feed docs/files into knowledge graph\n- **`adk setup nemotron`** shortcut — alias for `--tier lite`\n- **`--reasoning-api`** flag in setup — `anthropic`, `openai`, `deepseek`, `gateway`\n- **`--dgx-spark URL`** flag — remote vLLM endpoint configuration\n- **DGX Spark auto-detection** — scans `spark.local`, `192.168.0.33`, `AITHER_DGX_URL` env\n- **Post-setup smoke test** — sends real inference request after container startup\n- **Schema migration system** — version table + migration runner in `memory.py` and `graph_memory.py`\n- **Configurable vLLM port scan** — `AITHER_VLLM_PORTS` env var for custom ports\n- **`aithershell` entry point** — alias for `adk` in pyproject.toml\n- Shell auto-downloads binary on first `adk shell` (no `--install` needed)\n- Shell pre-flight check warns if no backend detected\n- Shell passes full config via env vars (API key, tenant, inference URL)\n- DGX/Remote and Cloud API checks added to `adk doctor`\n- Config fields: `reasoning_backend`, `reasoning_api_key`, `deepseek_api_key`, `dgx_url`, `vllm_extra_ports`\n\n### Fixed\n- `test_aeon_chat_basic` timeout — mock scope was exiting before request\n- `adk doctor` check_disk timeout — cap rglob at 5000 files\n\n## [1.1.7] - 2026-05-19\n\n### Added\n- Remote agent pipeline — `adk onboard` → fleet dispatch → remote inference\n- Agent-to-agent federation with mesh relay\n- `adk connect` for desktop mesh enrollment\n\n## [1.1.6] - 2026-05-19\n\n### Fixed\n- Tool call execution recovery across all backends\n- Hermes tool call fallback parsing for vLLM\n\n## [1.1.5] - 2026-05-19\n\n### Fixed\n- Tool call execution + Hermes fallback parsing improvements\n\n## [1.1.0] - 2026-05-18\n\n### Added\n- Multi-channel gateway (Telegram, Discord, Slack, webhook)\n- Aeon group chat with 7 presets\n- Skills auto-extraction from multi-step sessions\n- MCP stdio server for Claude Code integration\n- `adk gateway`, `adk cron`, `adk skills`, `adk soul` commands\n- SOUL.md import/export for portable agent identity\n- Elysium cloud device-flow authentication\n\n## [1.0.0] - 2026-05-18\n\n### Breaking\n- Package consolidated — `aither_adk` namespace replaced by `adk` + `adk.platform`\n- 93 duplicate files removed from `AitherOS/packages/aither_adk/`\n\n### Added\n- Standalone core primitives (`adk.core`: Agent, Tool, Memory, ModelBackend, Capability, Trace)\n- `adk.platform` sub-package merging all platform toolkit modules\n- Compatibility shims for legacy `from aither_adk.*` imports\n\n### Changed\n- Version synced across pyproject.toml, `adk/__init__.py`, `adk/platform/__init__.py`, npm package\n- Classifier promoted from Alpha to Beta\n\n## [0.16.0] - 2026-04-16\n\n### Swarm Coding Engine & Repowise Integration\n- **`repowise_search` tool** — Semantic + keyword hybrid code search via Repowise, with ripgrep fallback\n- **`swarm_code` tool** — Dispatch complex tasks to 11-agent swarm pipeline (ARCHITECT->SWARM->REVIEW->JUDGE)\n- **`agent.swarm()`** — Async convenience method with configurable mode, effort, timeout\n- **`agent.code_search()`** — Async convenience method returning structured results\n- New tool categories: `repowise` and `swarm` in TOOL_CATEGORIES\n- `repowise_search` and `swarm_code` added to `__init__.py` exports with lazy loading\n- `IDENTITY_DEFAULTS` updated: repowise for code-focused agents, swarm for orchestration agents\n- Standalone graceful degradation: repowise falls back to ripgrep, swarm returns structured error\n\n## [0.13.0] - 2026-04-02\n\n### Graph Faculties — Local Knowledge for Every Agent\n- **CodeGraph** (2,799 lines) — Full Python AST indexer with call graph, keyword/semantic/hybrid query, embedding matrix cache, incremental re-indexing, multi-hop chain expansion\n- **MemoryGraph** (1,339 lines) — Graph-based persistent agent memory with 10 edge types, hybrid query (keyword + semantic + graph expansion), multi-hop recall, pickle persistence\n- **EmbeddingProvider** — 4-backend fallback chain: sentence-transformers (GPU/CPU) -> Ollama -> Elysium cloud -> feature hashing (zero deps)\n- **BaseFacultyGraph** — Abstract base with HMAC-SHA256 validated pickle persistence\n\n### Agent Integration\n- `agent.set_code_graph(cg)` — auto-registers `code_search` + `code_context` tools\n- `agent.set_memory_graph(mg)` — auto-registers `remember` + `recall` + `memory_stats` tools\n- Both graphs inject context into LLM prompts automatically during chat\n\n### Zero-Config Onboarding\n- `adk start` / `adk` (no args) — auto-detect project, index code, detect LLM, persistent memory, interactive chat\n- Works for any directory: Python codebases, doc folders, mixed workspaces\n- Auto-detects LLM: Ollama -> vLLM -> Elysium -> OpenAI -> Anthropic\n- Per-project persistent memory in `~/.aither/memory/<project>`\n- `adk index <path>` — standalone indexing with progress bar and stats\n\n### MCP Gateway\n- `POST /v1/embeddings` — OpenAI-compatible embedding proxy to vLLM-embeddings:8209\n- Available to all tiers (embeddings are free)\n- ADK EmbeddingProvider uses this as Elysium cloud fallback\n\n### Optional Dependencies\n- `pip install aither-adk[graphs]` — numpy for 10x cosine similarity speedup\n- `pip install aither-adk[embedding]` — sentence-transformers + torch for local GPU embeddings\n- `pip install aither-adk` alone — graphs work with feature hashing (zero deps)\n\n## [0.12.0] - 2026-04-01\n\n### Bootstrap & Service Discovery\n- Version handshake -- ADK checks major.minor compatibility with Genesis/Node\n- Background reconnect loop -- ServiceBridge re-probes every 30s in standalone mode\n- Port 8080/8090 documented -- MCP vs OpenAI-compat clearly separated\n- Genesis URL configurable via GENESIS_URL env var (was hardcoded)\n- Standalone mode warning -- visible stderr alert when AitherOS not detected\n- Auto-reconnect on startup when services come online\n\n### Elysium Cloud Inference\n- Unified gateway URL -- gateway.aitherium.com handles auth + billing + inference\n- Streaming inference -- SSE passthrough for /v1/chat/completions with stream=true\n- Auth proxy routes -- /v1/auth/register, /v1/auth/login, /v1/auth/me\n- Billing proxy -- /v1/billing/balance through gateway\n- AitherConnect Elysium fallback -- cloud inference when local Genesis is down\n- AitherDesktop Elysium fallback -- third-tier chat fallback after Node\n\n### Infrastructure\n- /discovery endpoint on Genesis -- unified service URLs/versions/health\n- /api/config/services on Veil -- runtime port config for client-side JS\n- Veil healthcheck gates on Genesis -- unhealthy when backend is down\n- Desktop crash detection 90s->60s (threshold 3->2)\n\n## [0.11.0] - 2026-04-01\n\n### Agent Execution Quality (Claude Code Parity)\n- Raise loop guard block threshold from 3 to 4 -- agents get more room for iterative search\n- Soft synthesis nudge for effort >= 4 (no tool stripping, trust the model)\n- max_output_tokens escalation -- retry up to 3x when response is truncated\n- Tool result pairing guarantee -- synthetic error for orphaned tool_use blocks\n- Micro-compaction of old tool results -- save context tokens on long sessions\n- First-turn tool forcing only for effort >= 6 (trust model for lower effort)\n- Diminishing returns detection -- nudge agent when 3+ turns produce < 500 tokens\n- Message normalization -- merge consecutive same-role messages, strip empties\n- LLM retry with exponential backoff (5 retries, 500ms-16s, jitter)\n\n## [0.9.0] - 2026-03-16\n\nThe \"connected world\" release. Cross-platform identity pairing, voice capabilities, and multi-channel integration.\n\n### Added\n- **Pairing**: Cross-platform identity linking (`adk/pairing.py`)\n - `PairingManager` — SQLite-backed identity linking with 6-char pairing codes\n - Link users across Telegram, Discord, Slack, WhatsApp with 10min TTL codes\n - Canonical session IDs for cross-channel conversation continuity\n - `get_session_id()` returns \"user-{id}\" for paired users\n- **Voice**: Speech-to-text and text-to-speech client (`adk/voice.py`)\n - `VoiceClient` — async STT/TTS/emotion via AitherVoice service\n - Convenience functions: `hear()`, `say()`, `feel()`\n - 6 voice options: alloy, echo, fable, nova, onyx, shimmer\n - Emotion detection with intensity scoring\n- New exports: `PairingManager`, `PairingResult`, `PlatformIdentity`, `VoiceClient`, `TranscriptionResult`, `SynthesisResult`, `EmotionResult`\n\n### Changed\n- `__init__.py` exports expanded with pairing and voice symbols\n\n## [0.6.0] - 2026-03-13\n\nThe \"group mind\" release. Multi-agent group chat, creative tools, and Iris identity.\n\n### Added\n- **Aeon**: Multi-agent group chat engine (`adk/aeon.py`)\n - `AeonSession` — persistent group chat with parallel agent execution\n - 7 presets: balanced, creative, technical, security, minimal, duo_code, research\n - Orchestrator synthesis: Aither summarizes all agent responses\n - Serial execution for Ollama, parallel for vLLM/cloud\n - ConversationStore persistence with `type: \"aeon\"` metadata\n - `group_chat()` one-shot convenience function\n- `aither aeon` CLI command — interactive terminal group chat with color-coded agents\n - `-p/--preset`, `-a/--agents`, `-r/--rounds`, `--no-synthesize` flags\n - `reset` and `quit` commands\n- Server endpoints: `POST /aeon/chat`, `GET /aeon/presets`, `GET /aeon/sessions/{id}`\n- Creative tools in builtin_tools: `image_generate`, `image_refine`, `image_search`, `video_generate`\n- Iris agent identity with visual generation capabilities\n- 48 Aeon tests (data models, presets, chat, context, persistence, server, exports)\n\n### Changed\n- `__init__.py` exports: `AeonSession`, `AeonResponse`, `AeonMessage`, `group_chat`, `AEON_PRESETS`\n\n## [0.5.0] - 2026-03-13\n\nThe \"tenant-ready\" release. Multi-tenant admin, permission grants, safety profiles, and a full setup wizard.\n\n### Added\n- `aither setup` interactive setup wizard with hardware detection, model selection, identity config\n- Strata storage backend: SQLite WAL persistence for conversations, memories, knowledge graphs\n- CLI test runner: `aither test` with auto-discovery and parallel execution\n- Permission grants system for MCP tool access control\n- MCP account management tools\n- LLM provider auto-detection for Ollama and vLLM endpoints\n- Apache-2.0 LICENSE file\n- Elysium desktop sync module\n- Comprehensive CLI test suite (493 tests)\n- Strata storage test suite (972 tests)\n- LLM provider tests (82 tests)\n\n### Changed\n- CLI expanded: `aither setup`, `aither test`, `aither bugreport`, `aither doctor`\n- Strata module rewritten as full local-first storage engine\n- Server startup includes Strata initialization\n- README rewritten with clearer quickstart and architecture docs\n\n### Fixed\n- Dead documentation links\n- Python 3.11 compatibility issues\n- Ruff per-file-ignores configuration\n- Identity provisioning edge cases\n- Docker node-gyp native dependency builds (python3 + build tools)\n\n## [0.4.0] - 2026-03-13\n\nThe \"own your AI\" release. Self-hosted agent OS for people who don't want their data on someone else's servers.\n\n### Added\n- Muse agent identity (creative/artistic generation)\n- Port 8120 to vLLM scan for ExoNodes discovery\n- Public roadmap with milestone-based porting schedule\n- Competitive positioning: self-hosted alternative to cloud-locked AI appliances\n\n### Changed\n- LLM router: compute-aware effort routing (replaces effort-level context gating)\n- README rewritten for sovereignty-first messaging\n- Package description updated\n- Promoted from alpha to stable release\n\n### Fixed\n- Version string consistency between pyproject.toml and __init__.py\n- vLLM port scanning now includes port 8120 (ExoNodes)\n\n## [0.3.1] - 2026-03-09\n\n### Added\n- GraphMemory: SQLite knowledge graph with Ollama embeddings and hybrid search\n- NeuronPool: auto-fire pre-LLM data gathering agents\n- NanoGPT: pure Python char-level transformer with LoRA fine-tuning\n- Safety gates: IntakeGuard (input), LoopGuard (recursion), Sandbox (code exec)\n- Event system: EventEmitter with chat/tool/forge event types\n- Builtin tools: identity-based tool selection\n- ServiceBridge: auto-discovery of AitherOS services\n- Streaming: chat_stream with safety gate integration\n- Auth middleware: Bearer token authentication\n- CLI: `aither init` and `aither serve` commands\n\n### Changed\n- Wired 5 previously disconnected modules into agent loop\n- 522 total passing tests (up from 85)\n\n## [0.3.0] - 2026-03-07\n\n### Added\n- Clean-room agent development kit\n- Multi-backend LLM providers (Ollama, OpenAI, Anthropic)\n- AitherAgent class with @tool decorator\n- SQLite conversation memory\n- OpenAI-compatible server (`aither-serve`)\n- 16 agent identities as package data\n- MCP bridge to mcp.aitherium.com\n- Privacy-centric opt-in telemetry\n- Bug reporting CLI and API\n- FastAPI server with OpenAI-compatible endpoints\n- Hardware auto-detection (5 tiers, 11 profiles)\n- Fleet orchestration and multi-agent coordination\n- A2A mesh protocol\n- Federation protocol for cross-instance agent dispatch\n\n### Initial release\n- 85 passing tests\n- Apache-2.0 license\n"}, "a511d1956a0e86881edc4510aa52af5ad876472e": {"t": 1785748151.4556184, "n": 102462, "h": "2518ca4f257c5ee0", "c": "# Changelog\n\nAll notable changes to aither-adk will be documented in this file.\n\n## [2.45.0] - 2026-07-29\n\n### Fixed — streaming against an AitherOS-typed SSE backend returned a silent EMPTY answer\n\n- **`OpenAIProvider.chat_stream` now parses BOTH stream dialects.** MicroScheduler's\n `/v1/chat/completions` facade (and Genesis) answer `stream=true` with AitherOS-typed\n SSE events (`event: token`, `data: {\"t\": \"...\"}`) rather than OpenAI chunks\n (`choices[].delta.content`). Parsing only the OpenAI shape yielded **zero chunks and\n no error** — the agent loop (`stream_react`) completed \"successfully\" with an empty\n answer while non-stream `chat()` worked against the same endpoint, which made every\n `/chat/stream` turn on an MS-backed daemon silently empty. The provider now accepts\n typed `token`/`answer`/`complete`/`error` events alongside OpenAI chunks; a trailing\n `answer` event never duplicates already-streamed tokens. Two regression tests pin\n both dialect paths (`tests/test_llm_providers.py`).\n\n## [2.44.0] - 2026-07-27\n\n### Fixed — `adk enroll` silently registered devices into the WRONG TENANT\n\n- **`adk enroll` succeeded without an identity and bound the device to the tenant\n `\"personal\"` instead of the caller's workspace.** `_extract_tenant_slug()`\n (`fleet_enroll.py:217`) falls back to `\"personal\"` when `~/.aither/auth.json` is\n absent, so enrolling a fresh machine reported success while registering it\n somewhere the owner cannot see. Nothing failed, nothing warned, and the endpoint\n simply never appeared in their workspace — the fail-open shape where the\n operation reports success and does the wrong thing.\n\n This bites hardest on unattended installs (phones, headless VMs, cloud images),\n which are exactly the machines nobody is watching closely enough to notice.\n\n `cmd_enroll` now **refuses** when no identity is present, and names the two ways\n to supply one:\n\n adk login # browser flow\n adk login --api-key <key> # headless / phone / VM\n\n Verified by running it with an empty `HOME`: refuses, **exit 1**. (`AITHER_NODE_TOKEN`\n is accepted as an identity, so token-based fleet enrolment is unaffected.)\n\n Found while building a one-command self-host installer for Android's Linux\n terminal, by running the installer rather than reasoning about it.\n\n## [2.39.0] - 2026-07-25\n\n### Fixed — containerized agents were unreachable\n\n- **`docker` runtime: added `-i` to `docker run`.** Without it Docker does not\n attach the container's stdin, so *every* containerized stdio agent (acp, mcp)\n silently never received a request. Measured against a real container: no `-i` →\n the child read nothing; with `-i` → the request round-trips. A real customer\n pack *is* a container, so this runtime was effectively unusable.\n- **`runtime.cmd` is now `shlex`-split** for the `docker` and `node` runtimes.\n `cmd: \"python -u server.py\"` was passed as a single argv element, so docker\n tried to exec a binary literally named `python -u server.py`.\n- `Supervisor._build_command()` extracted from `spawn()` so argv is testable\n without launching a process — the missing `-i` survived precisely because it\n wasn't.\n- Env is deliberately **not** forwarded as `-e KEY=value`: that puts credentials\n in argv, readable by any local `ps` / `docker inspect`.\n\n### Fixed — CodeActLoop `cell_timeout` did not bound CPU-bound cells\n\n- `asyncio.wait_for` cannot preempt a coroutine that never yields, so\n `while True: pass` was never interrupted and hung the agent indefinitely. Now\n also bounded by a line-level trace deadline (`CellTimeout`), scoped to the\n cell's own frames so a cell that awaits cannot fire the deadline inside\n unrelated coroutines.\n- The `CodeActLoop` docstring now states plainly that **it is not a sandbox**, and\n names what a cell can actually do (`import os`, `subprocess`, `open(..., \"w\")`).\n Do not run cells from an untrusted source.\n\n### Added — real containment for CodeAct cells\n\n- **`CodeActLoop(executor=DockerCellExecutor())`** runs each cell in a container\n with `--network none`, a read-only root filesystem plus a noexec/nosuid tmpfs,\n `--cap-drop ALL`, `no-new-privileges`, memory and pid caps, a non-root user, and\n `python -I`. The escapes that succeed in-process — writing a host file, network\n egress, running as root — all fail there, and an infinite loop's container is\n **killed** rather than merely interrupted.\n- `return_result()` crosses the process boundary via a stdout sentinel and is\n deliberately **not** `eval`'d back into the host.\n- Trade-off stated plainly: an isolated cell is a fresh process, so there is no\n live host namespace and no cross-cell state. Containment *and* a live namespace\n together need fork-from-warm snapshotting (`adk/forkd_client.py`'s Firecracker\n target), which requires Linux ≥ 5.7 + KVM.\n- The in-process default is unchanged. Do not point CodeAct at untrusted input\n without `executor=`.\n\n### Fixed — TLS and test-suite integrity\n\n- **`adk/cli.py` sent an `Authorization: Bearer` API key over TLS with\n verification disabled**, exposing it to a machine-in-the-middle. Now routed\n through `adk._tls.tls_verify()`.\n- The `verify=False` ratchet regex-scanned raw lines, so docstrings that merely\n *named* the antipattern were reported as violations — noise that hid the real\n hole above. It now walks the AST for actual keyword arguments.\n- Five `test_inference_proxy` failures were stale assertions pinning the\n pre-MicroScheduler routing and a 5-model roster (it is 6 since `aither-bonsai`).\n Assertions now derive from the roster and encode the tier-access rule, so a\n future model addition doesn't break them.\n\n### Fixed — MCP stdio hung silently on Windows\n\n- `adk/mcp_stdio.py` and `adk/shell/mcp_bridge.py` attached stdin with\n `loop.connect_read_pipe`, which fails inside the Proactor loop's read handler\n such that the read future never resolves — the server didn't crash, it just\n never answered. Both now use the new shared `adk.stdio_compat`\n (`ThreadStdinReader` / `ThreadStdoutWriter`), which `acp_server.py` also uses.\n\n### Added — per-install pack credentials are now actually used\n\n- `adk/pack_credentials.py` was orphaned: mint/revoke were never called, so pack\n installs kept using the shared credential the module was written to replace.\n Both install paths now mint, and a reinstall revokes the outgoing install's\n credential before its metadata becomes unreachable.\n- **New `/packs uninstall <pack-id>`** (aliases `remove`, `rm`) — revokes the\n per-install credential first, then removes the pack. Previously the only revoke\n path was a reinstall, so deleting a pack any other way left a live,\n unrevocable credential. The target is confined to the pack root.\n\n### Fixed — packaging\n\n- The brew formula's own `sha256` was left as `PLACEHOLDER_SHA256` on every\n release, so `brew install` could never verify the download.\n `sync_versions.py` now fills it from PyPI and `--check` **fails** when a\n published version still carries a placeholder or a stale digest.\n- `sync_brew` rewrote *every* `url` in the formula, repointing the `httpx`,\n `pyyaml`, `fastapi` and `uvicorn` `resource` blocks at the aither-adk tarball.\n Fixed with `count=1`; the resource digests are now real.\n- `adk/__init__.py`'s source-checkout fallback `__version__` had been stale since\n 2.32.0 because the canonical release path never ran `sync_versions.py`. That\n check now runs in `sync-adk.yml`.\n\n## [2.38.1] - 2026-07-24\n\nRelease plumbing only — 2.38.0 was tagged before the version manifests\n(npm/brew/winget) were synced, so its publish gate failed and it never reached\nPyPI. 2.38.1 is the same code with the manifests in sync. See 2.38.0 below for\nwhat actually changed.\n\n## [2.38.0] - 2026-07-24 (never published — superseded by 2.38.1)\n\n### Fixed — `adk join` could only ever have worked for us\n\n- **`adk join` defaulted identity to `https://localhost:8115`** — a fleet-INTERNAL\n address. `adk join` is the one command a stranger runs on their own GPU box, so on\n any machine not already running AitherOS locally, step 1 (the GitHub device flow)\n hit a refused connection, and so did node registration and the mesh-key issue. The\n conductor default was already public, which is exactly why this went unnoticed.\n Identity now defaults to `https://idp.aitherium.com`.\n\n This fix landed *after* the 2.37.0 tag, so **2.37.0 and every earlier release on\n PyPI still ship the broken default** — 2.38.0 is the first release a new\n contributor can actually run. Verified live from a non-fleet machine (DGX):\n `POST /auth/github/device/start` → HTTP 200 with a real `user_code` and\n `verification_uri`.\n\n- **Upgrade check compared versions as strings** — `latest != current` reported a\n downgrade as an available upgrade. Now uses `packaging.version` with a numeric-tuple\n fallback.\n\n## [2.37.0] - 2026-07-24\n\n### Added — Agent Onboarding Fabric: bring your own agent, or run a managed one\n\nOnboard an agent built on **any** framework, or let Aitherium run it for you.\n\n- **ACP (Agent Client Protocol), both directions** — `ACPClient` drives any ACP agent;\n `ACPServer`/`serve_stdio` exposes an adk agent to any ACP host (Zed, VS Code,\n JetBrains). Both proven live against Zed's reference `agent-client-protocol`.\n- **Universal Agent Pack** — `AgentPackManifest` + `Supervisor` describe and run an\n external agent across 6 frameworks × 6 protocols, fail-closed on anything unknown.\n- **Protocol drivers** — `get_driver()` returns an `http`, `langgraph_rest`, `a2a`, or\n `mcp` driver, so REST/graph-orchestrated agents can be *managed*, not just connected.\n- **Pack registry** — `PackRegistry.publish/browse/versions/get/yank/verify_digest`,\n SHA256-digested, semver-ordered, validation fail-closed before anything is written.\n- **Managed identity** — `ManagedAgentIdentityProvider` with a real gateway minter and a\n PROVISIONED→REGISTERED→ACTIVE→ROTATED→REVOKED lifecycle; default-deny `authorize()`.\n- **Zero-code connect templates** — `render_connect(framework, …)` emits config pinned to\n each framework's real schema.\n\n### Added — NOOA-style agent capabilities\n\n- **`CodeActLoop`** — code as action: the model writes Python cells that run against a\n persistent namespace over live objects, each cell AST-validated before execution, with\n per-cell timeouts and bounded observations.\n- **`ObjectRegistry` / `render_observation`** — pass by reference: large values become\n handles instead of flooding the context with serialized text.\n- **`EventLog` / `ContextBlocks`** — harness APIs the model itself can call to query its\n own event history and manage named context blocks in KV-cache-stable order.\n- **`has_ellipsis_body` / `@strategy`** — NOOA's `async def f(...) -> X: ...` ergonomic.\n- **`CodeValidator`** — best-effort AST validation (ported from NVIDIA's OO-Agents,\n Apache-2.0; NOTICE included). A defense-in-depth layer, not a sandbox boundary.\n- **`PredictLoop`** — single-shot strategy with structured output (`output_model=`) and\n validation retry; `Strategy` is now a first-class alias of `AgentLoop`, and both loops\n accept a per-call `model=` override.\n- **Typed I/O** — `AgentResult` validates its fields at construction and at the\n `Agent.run` boundary.\n\nAll new capabilities are exported lazily from the top-level `adk` namespace.\n\n## [2.27.0] - 2026-07-18\n\n### Added — Moonshot Kimi K3 provider (DeepSeek-equivalent cloud backend)\n\n- **New `moonshot` provider** across the LLM router: OpenAI-compatible\n `https://api.moonshot.ai/v1`, default model `kimi-k3` (131K context default, up to 1M;\n tool calls, JSON mode, structured output). Wired everywhere `deepseek` is:\n - `LLMRouter(provider=\"moonshot\")` + `_COMPAT_URLS`/`_COMPAT_MODELS`/model tiers\n (small/medium/large → `kimi-k3`)\n - Config: `moonshot_api_key` field (env `MOONSHOT_API_KEY`), saved-config load,\n provider-keys map (`provider_keys.json` key `moonshot`)\n - Auto-detect chain: tried after DeepSeek (config key, then env key)\n - Hybrid reasoning backend: `AITHER_REASONING_BACKEND=moonshot` for effort 7+\n - Reasoning tiers: `backend: moonshot` (alias `kimi`) via new `MoonshotBackend`\n - CLI: `adk backend set moonshot --api-key …`, `adk backend list` shows moonshot\n- No breaking changes; DeepSeek and all other providers unchanged.\n\n## [2.26.0] - 2026-07-16\n\n### Added — Aeon group-chat web UI pack + POST /aeon/stream SSE endpoint\n\n- **New `aeon` UI pack** (`adk/webui/packs/aeon/index.html`) — group chat for multi-agent\n discussion. Mirrors the `llamacpp` pack's auth + SSE pattern (bearer from URL fragment #k=),\n with a preset selector (balanced/creative/technical/security/minimal/duo_code/research) and\n distinct colored agent messages. Synthesis block highlighted. Agent content is HTML-escaped\n before render. Fully self-contained HTML+CSS+JS, light+dark, no external assets. (No\n temperature/max-tokens controls — Aeon runs a fixed multi-agent round and doesn't thread\n per-turn generation params, so exposing them would be a no-op.)\n- **POST /aeon/stream SSE endpoint** — accepts body {message, preset?, agents?, rounds?,\n session_id?}, emits SSE with: `session_start` (participants + orchestrator), `agent_message`\n per non-orchestrator, `synthesis` (if enabled), `error` on exception, `complete` (total tokens\n + latency). Mirrors /chat/stream structure — like every streaming endpoint here it is gated by\n the server auth middleware **when `AITHER_SERVER_API_KEY` is set** (a local `adk up` with no key\n is unauthenticated by design, same as /chat/stream).\n- **Default UI pack flipped to `llamacpp`** — `resolve_ui_pack_name()` now returns `llamacpp`\n instead of `console` when no env/config is set (new users see the clean chat, not the admin\n SPA; existing users with AITHER_AGENT_UI or saved config unaffected).\n\n### Added — swappable agent web UI packs (llama.cpp-style built-in chat)\n\n- The page an agent serves at `/` is now a **swappable UI pack**, so you can\n drop in, test, and deploy different chat frontends without touching the agent.\n Select with `adk ui set <name>` (persists to `~/.aither/config.json`) or the\n `$AITHER_AGENT_UI` env; `adk ui ls` lists packs, `adk ui path` shows what\n resolves.\n- **New built-in `llamacpp` pack** — a clean, self-contained llama.cpp-style\n chat UI (centered conversation, streaming token-by-token with a cursor, a\n Stop button, a settings drawer for temperature / max-tokens that is actually\n honored end-to-end — `/chat/stream` now forwards them to the provider —\n minimal safe markdown, light+dark). Hits the agent's own gated `/chat/stream`\n with the bearer read from the URL fragment (never sent in the URL). Built-ins\n also include `console` (the full admin SPA, still the default) and `minimal`.\n- **Drop-in custom packs**: a UI pack is just a folder with an `index.html` in\n `~/.aither/ui-packs/<name>/` (override via `$AITHER_UI_PACKS_DIR`) — no rebuild.\n- Resolution is **fail-soft**: an unknown/missing pack falls back\n console → minimal, so `/` is never blank.\n\n### Added — MCP-gateway-first platform access + mesh A2A trust\n\n- **Gateway-first platform access** (`adk/client/_gateway_mcp.py`, `GatewayMCPClient`):\n a self-hosted agent connects its MCP **client** to `mcp.aitherium.com` authenticated\n as the OWNER (device-flow / PAT / ACTA token), RBAC-scoped to that identity — so it can\n use platform tools + `get_secret` **whether or not the node runs local AitherOS\n services**. Fail-closed: no token → client disabled, the agent still runs. Wired into\n the server lifespan (`_connect_gateway_mcp`).\n- **`genesis` LLM backend**: `adk backend set genesis` points the agent brain at a local\n Genesis (`http://localhost:8001/v1`, model `workflow`) — the 5090/controller pattern\n where inference is served by the local fleet, not the cloud gateway or a raw vLLM.\n- **`adk mesh` command**: `adk mesh onboard` drives the Conductor 5-step onboard\n (`/v1/mesh/onboard` → registers this node's public key, returns the `endpoint:mesh`\n trust token); `adk mesh ls` lists peer nodes/agents (AitherMesh `/nodes` + Directory)\n with their A2A cards + invoke URLs for discovery.\n\n### Security — A2A inter-agent trust enforcement (fail-closed)\n\n- **`adk/a2a_trust.py`** (new): Ed25519 signature verification + trusted-key check\n (`AITHER_A2A_TRUSTED_KEYS`), opt-in via `AITHER_A2A_REQUIRE_TRUST` (default `false` for\n backward compatibility; `audit` logs untrusted, `true` rejects).\n- **Fixed a fail-OPEN in the inbound A2A handler** (`adk/a2a.py`): the trust check\n previously ran *only when signature headers were present*, so an unsigned request\n bypassed `AITHER_A2A_REQUIRE_TRUST` entirely. Now genuinely fail-closed — required mode\n with no/invalid signature → `403`.\n- **Sovereign template no longer scaffolds `verify=False`**: the generated\n `services/llm.py` now uses the project's own `tls_verify()` policy helper for every\n provider call instead of hardcoding disabled TLS verification.\n- **`adk/_tls.py`**: disabling verification via `AITHER_TLS_VERIFY=false` now emits a\n one-time loud warning (was silent) — the escape hatch stays, the silence goes.\n\n## [2.25.1] - 2026-07-15\n\n### Fixed — self-hosted OpenAI-compatible backends honor a custom base URL\n\n- `adk backend set llamacpp --base-url http://localhost:8090/v1 --model <id>` (and\n `vllm`/`lmstudio`, and the `AITHER_LLM_BASE_URL` env) now actually point the agent\n at YOUR server. Previously the base URL was saved but never read, so an explicit\n `--backend llamacpp` fell back to the provider's public API default (openai.com).\n New `Config.llm_base_url` field + saved-config `inference_url` wiring; the explicit\n backend path in `LLMRouter` now passes it for the local OpenAI-compatible family.\n This is what lets a self-hosted node run its OWN local model (e.g. a PrismML\n Bonsai llama.cpp) as the agent brain instead of ollama/cloud.\n\n## [2.25.0] - 2026-07-15\n\n### Added — node_bootstrap tool pack: bootstrap LLM inference on any hardware\n\n- **New bundled tool pack `node-bootstrap`** (`adk/toolpacks/node_bootstrap/`):\n seven agent tools — `node_detect_hardware`, `node_resolve_recipe`,\n `node_plan_deployment`, `node_apply`, `node_enroll`, `node_register_backend`,\n `node_verify` — plus a CLI shim (`python -m adk.toolpacks.node_bootstrap\n detect|resolve|plan|apply|enroll|register|verify`). Detect a box's hardware,\n resolve a deployment recipe, actually deploy the inference engine (docker\n compose / native / delegate), enroll with a control plane, register the\n backend, and live-verify a completion.\n- **9 hardware recipes** shipped in the wheel: `cpu-1bit-llamacpp`, `cpu-ollama`,\n `cuda-vllm-8gb/24gb/40gb`, `cuda-dual-stack-32gb` (32GB-card co-resident\n stack), `unified-memory-vllm` (DGX/Grace-class), `metal-ollama` (native —\n Docker on macOS has no Metal passthrough), `cloud-api` fallback. Resolution\n scores hardware against min/max VRAM bands, RAM, cores and unified memory;\n ties prefer higher tiers and self-contained recipes over fleet delegates.\n- Security posture: enrollment and backend registration are fail-closed\n (missing token/URL → error dict, never anonymous, never `verify=False`);\n tokens are redacted in outputs; all endpoints come from args/env\n (`AITHER_CONTROL_PLANE_URL`, `AITHER_GENESIS_URL`, `AITHER_AUTH_TOKEN`).\n\n## [2.24.0] - 2026-07-14\n\n### Changed — sync-family consolidation\n\n- **Removed dead code:** `adk/sync.py` (528 lines) deleted — it was unreachable,\n shadowed by the `adk/sync/` package (`import adk.sync` always resolved to the\n package; nothing loaded the module by path — verified).\n- **Grouped the sync domain modules into the `adk.sync` package:**\n `brain_sync`→`adk.sync.brain`, `files_sync`→`adk.sync.files`,\n `lockbox_sync`→`adk.sync.lockbox`, `secrets_sync`→`adk.sync.secrets`,\n `session_sync`→`adk.sync.sessions`, `settings_sync`→`adk.sync.settings`,\n `sync_watermark`→`adk.sync.watermark`. All in-tree importers updated. No\n back-compat shims were left because **zero external/monorepo code imported the\n old root paths** (verified) — a clean move, not a shimmed one. **Breaking only\n for code that imported `adk.<name>_sync` directly** (none known).\n\n### Docs — MCP module layering clarified\n\n- `adk.mcp` (enterprise AitherOS gateway client: auth/billing/token-tracking) and\n `adk.core.mcp` (minimal generic JSON-RPC adapter for any MCP server) are\n DISTINCT layers, not duplicates — each now carries a LAYER/ROLE/See-also\n docstring so they are not mistakenly consolidated.\n\n## [2.23.2] - 2026-07-14\n\n### Fixed — CompletionGate no longer false-fails on correct results\n\n- **Tolerant judge JSON extraction.** The LLM judge parsed its reply with `json.loads(text[first\"{\":last\"}\"])`, which raised `JSONDecodeError` (\"Extra data\") whenever the model appended prose after the verdict or emitted a second object — and `verify()` turned that into a fail-closed verdict, so a genuinely completed task reported `unverified`. New `_first_json_object()` uses `JSONDecoder.raw_decode` from the first `{` that begins a valid object and ignores trailing content (prose, markdown fences, extra objects). A judge that answers with no parseable JSON still fails closed (never a soft pass), now with a clear reason. The same extractor backs `_derive_criteria`.\n- **Bare code-like tokens are hard-checked.** A natural criterion like `output contains ADK_AUTO_OK` (no quotes) previously fell through to the judge; `hard_checks` now also matches bare identifier/code shapes (contains an underscore, or 4+ CAPS/digit chars), with a stopword guard so a descriptive uppercase word (`OUTPUT`, `CONTAIN`) never becomes a false required token. Quote a token to force it regardless.\n- +9 tests (24 total), including the exact trailing-data regression.\n\n## [2.23.0] - 2026-07-14\n\n### Removed — dead modules and internal integrations\n\n- **`adk.swarm`, `adk.provisioning_tools`, `adk.session_sync_integration`, `adk.addon_metering`** — four orphan modules with zero public usage have been deleted. These were internal prototypes kept for legacy monorepo consumers; public packages have not imported them since 2.21.0 (tracked as D-348). **Breaking for anyone directly importing these modules** (import will fail). They are no longer exempt from the orphan-module check.\n- **`adk.aither_bridge`** — the AitherOS-internal IRC ↔ chat gateway bridge is no longer shipped in the public aither-adk package (stripped at sync time). All public importers already degrade behind `except ImportError` (in `adk.server` and `adk.builtin_tools`), so public packages remain unaffected. The module stays in the private AitherOS monorepo for internal use.\n- **Identity document trim** — removed internal agent names from `adk/identities/aither.yaml` delegation guidance, replacing with role-generic wording (\"delegate code review, refactoring, security analysis, performance and testing to specialist agents when available\"). Effort-tier routing philosophy remains unchanged and is product design, not a leak.\n\n### Changed — genericized customer/product names in help text\n\n- CLI help/examples no longer name specific customer deployments (`--scope` template list and `deploy agent` name example use generic placeholders). Functional deploy paths are unchanged.\n\n## [2.22.1] - 2026-07-14\n\n### Fixed — real bugs surfaced by reviving the public CI (red since Jul 5)\n\n- **TLS**: bundled toolpacks (arc-brainpack, cloudflare) disabled certificate\n verification on gateway/vault calls; all now route `adk._tls.tls_verify()`\n (verified by default, internal CA bundle when present).\n- **`system_prompt` contract**: bundled-pack `[PACK DIRECTIVES]` were appended\n even to an explicit `system_prompt`; an explicit prompt now wins wholesale.\n- **Headless secrets**: `_derive_key()` used `os.getlogin()`, which raises\n `OSError` without a controlling terminal (CI, daemons, containers) — falls\n back to `getpass.getuser()` (same value on interactive boxes, so existing\n secret files keep decrypting).\n- **`pack_verifier`**: missing `cryptography` crashed with `UnboundLocalError`\n in the exception handler instead of failing closed; now degrades gracefully\n (and `cryptography` was added to the dev extra so CI tests the real path).\n- **`fleet_manager._kill_pid` (POSIX)**: reported success immediately after an\n asynchronous SIGTERM and never reaped children (zombies read as alive); now\n polls, escalates to SIGKILL, and reaps.\n- **Identity**: `aither` regained the `creative` tool category (dropped\n accidentally in a 2.7.0 WIP sweep).\n\n## [2.22.0] - 2026-07-14\n\n### Changed — internal toolkit relocation\n\n- **`adk.platform` relocated** — the internal aither-platform toolkit (ComfyUI/image-gen\n pipelines, fleet agent clients, parallel CLI/UI/tools stack) has been moved out of the\n adk SDK tree to the standalone internal `aither-platform` package at\n `AitherOS/agents/aither_platform/`. The public package remains unaffected (it already\n excluded `adk.platform` since 2.21.1). Internal AitherOS agent builds now install\n `aither-platform` separately. `adk platform` falls through to the same \"not available\"\n message on public installs; internal builds work via the relocated package.\n\n### Removed — backward-compat re-export deleted\n\n- **`adk.gateway` module removed** — the backward-compatibility re-export of `GatewayClient`\n from `adk.gateway` has been removed. Use `from adk.client import GatewayClient` instead\n (the established public API since 2.21.0). All internal importers have been updated.\n\n## [2.21.1] - 2026-07-14\n\n### Removed — internal toolkit and license-gated product no longer ship publicly\n\n- **`adk.platform`** — the internal aither-platform toolkit (ComfyUI/image-gen\n pipelines, fleet agent clients, a parallel cli/ui/tools stack) is no longer\n included in the public repo or the PyPI wheel. It was internal-only code that\n had been riding along since the aither-platform merge. `adk platform` now\n reports \"Platform toolkit not available\" on public installs; internal AitherOS\n builds are unaffected.\n- **`adk.formbridge`** — the FormBridge form-automation product is license-gated\n (PROFESSIONAL+); its implementation no longer ships in the free package. All\n import sites (`builtin_tools`, `server` route mount) already degrade behind\n `except ImportError`, so nothing else changes behavior.\n- Product test suites (`test_formbridge.py`, `test_encounter_keying.py`,\n `test_flow_tools.py`) are excluded from the public sync alongside them.\n\n## [2.21.0] - 2026-07-14\n\n### Added — Completion gate: verified-or-retried task execution\n\n- **`adk.gate.CompletionGate`** — a composable wrapper that closes the \"silent\n success\" gap: an agent's ReAct loop returns whatever the model produced and\n calls it done (`finish_reason = stop/max_steps/length`), which is a *liveness*\n signal, not a *completion* one — so an agent can \"succeed\" while doing nothing.\n `CompletionGate` runs the agent, verifies the result against acceptance\n criteria, retries with the failure fed back, and returns the response with\n `finish_reason` set to `verified` or an honest `unverified`. Never a false pass.\n - **Hard-first, un-soft-passable checks** (`adk.gate.hard_checks`): a filesystem\n path a criterion says must exist is checked on disk; a required quoted token\n must appear in the output. Only genuinely subjective criteria fall through to\n an optional LLM judge (reuses the agent's own model), which **fails closed**\n if unavailable.\n - **`gated_run(agent, task, criteria=..., max_retries=2)`** — one-shot helper.\n - `auto_criteria` (LLM writes the machine-checkable definition-of-done from the\n task), a `verifier=` override for custom checks (tests-pass / HTTP probe), and\n a `requires_action` short-circuit so it never fights human-approval pauses.\n - Compose it onto any agent exposing `run(task) -> resp{.content, .finish_reason}`\n (`AitherAgent`, `core.Agent`, or a thin adapter over a remote dispatch).\n - Exported from the top-level package: `from adk import CompletionGate, gated_run`.\n\n## [2.19.0] - 2026-07-12\n\n### Added — Eve agent importer + durable A2A tasks + canonical agent discovery\n\n- **`adk pack import <eve_agent_path>`** — convert external Eve agents to AitherADK packs\n (from GitHub or local filesystem). Maps Eve structure (instructions → system prompt,\n agent.ts → agent.yaml, skills/*.md → skills, connections/*.ts → MCP, tools/*.ts →\n tools/node verbatim) to AitherADK format with a single command. Eve agents (compiled\n manifest format, schema v35) become installable packs compatible with `adk pack install`.\n- **Durable A2A task lifecycle (`TaskManager` backed by FileStore)** — agent-to-agent\n tasks now survive container restarts. Tasks persist as append-only JSONL\n (`.adk/tasks.jsonl`); in-memory dict for fast access; `create_task()` / `update_status()` /\n `add_artifact()` / `add_message()` all save to disk. Google A2A v0.3.0 spec compliant.\n- **Canonical agent card discovery** — `A2AServer.mount()` now exposes:\n - `GET /.well-known/agent-card.json` — Agent Card (canonical; contains agent identity,\n capabilities, skills, endpoints)\n - `GET /.well-known/agent.json` — Agent Card (legacy, redirects with 308 to canonical path)\n - `POST /a2a` — JSON-RPC 2.0 for message send/task lifecycle\n - `GET /a2a/tasks/{id}/subscribe` — SSE streaming task updates\n- **Four built-in agent packs now complete and catalogued**:\n - `hermes` — architecture review & trade-off analysis\n - `openclaw` — web research & source verification\n - `claude-code` — feature development & debugging\n - `analyst` — structured data inference & anomaly detection\n All discoverable via `adk packs` / `adk install pack:<name>`.\n- **Tolerant tool-call JSON parsing** — malformed tool JSON (extra properties, missing\n name/arguments, string-encoded nested args, missing keys) is now gracefully degraded\n instead of silently failing. `_parse_tool_json()` accepts both `\"arguments\"` and\n `\"parameters\"` keys, coerces string-encoded args, and skips unparseable calls instead\n of crashing the turn.\n\n### Known limitations\n\n- Eve agent importer: remote GitHub fetching not yet implemented (local paths only).\n- Durable task store: currently JSONL (suitable for thousands of records, not millions).\n\n## [2.18.0] - 2026-07-10\n\n### Added — AitherShell command center + Claude Code session management\n\n- **`aither sessions`** — Claude Code session manager: interactive full-screen\n browser (type-to-filter, transcript preview, deep full-text `search`, resume\n **in the current terminal** or as Windows Terminal tabs), crash guard\n (`guard install` = at-logon watchdog that snapshots live sessions and\n auto-restores the whole set after a terminal crash), and `ingest [--watch]`\n (incremental, secret-guarded sync of session conversations into the local KB\n / CompanyBrain via the standard ingest pipeline).\n- **`aither hq`** — command-center dashboard: per-service fleet health, LLM\n queue (depth/VRAM/models), Pulse alerts, live session count, inbox unread —\n auto-refreshing, with one-key jumps into chat, sessions, inbox, agents,\n brief, watchtower, and docker recovery.\n- **`aither inbox`** — unified queue over CommCore mail, Relay\n mentions/notifications, and Pulse alerts; open/mark-read/DM-reply from the\n terminal. Every source degrades independently.\n- **`aither agents`** — roster console + `agents ask -e <1-10> <agent> <q>`\n (effort-tiered ask via Genesis, with automatic `/chat/stream` fallback when\n the `/agent/sync` execute gate rejects host callers) + forge dispatch\n inspection + routine health.\n- **`aither palette`** — universal fuzzy picker across actions, sessions, and\n services; **`aither brief`** — renders Atlas's executive briefing in the\n terminal; **`aither watch`** — fleet watchtower with named wedge-signature\n detection (docker-WSL wedge, LLM-queue stall, crash-loop uptime regression)\n and optional docker auto-recovery.\n- All fleet reads go through a shared fail-soft `FleetClient` (internal-CA\n TLS via `adk._tls`, dashboard-grade timeouts, per-source degraded states).\n- Node shell-cli (`@aitherium/shell-cli` 1.13.0): `aither\n sessions|hq|inbox|palette|brief|watch|agents|docker` now pass through to the\n Python shell instead of being intent-classified as chat prompts.\n\n## [2.17.1] - 2026-07-05\n\n### Fully self-service mesh join — no customer key handling\n\n- **Auto-join headscale off the onboard response.** The conductor now auto-issues a\n headscale pre-auth key for NAT'd nodes and returns it in the `/v1/mesh/onboard`\n response; `mesh.join` consumes it and brings up the tunnel automatically. A customer\n runs one command — they never mint, fetch, or set a headscale key. Explicit\n `--headscale-key`/`AITHER_HEADSCALE_AUTH_KEY` still take precedence.\n- **Security:** `_tailscale_up` now redacts the auth key from all logs and exceptions\n (a failed `tailscale up` previously echoed the full argv, leaking the key into logs).\n\n## [2.17.0] - 2026-07-05\n\n### Added — Headscale mesh transport + local Qdrant + AitherConnect onboarding\n\n- **Headscale NAT-friendly mesh transport.** When raw WireGuard UDP:51820 is not\n viable (customer boxes behind NAT, corporate firewalls, CGNAT), `adk mesh join\n --headscale` routes the mesh tunnel through Tailscale's Headscale control plane.\n The overlay IP is still Conductor-assigned (10.77.0.0/16); Headscale provides\n only the transport layer. Configured via `AITHER_MESH_TRANSPORT=headscale`,\n `AITHER_HEADSCALE_URL` (default: https://headscale.aitherium.com), and\n `AITHER_HEADSCALE_AUTH_KEY` (pre-generated key). Automatic fallback to raw\n WireGuard if Headscale setup fails.\n- **Local Qdrant vector DB provisioning.** `adk stack` now provisions Qdrant\n locally when `AITHER_VECTOR_DB=qdrant` is set, enabling offline-first RAG\n without external dependencies. Tiered: local Qdrant in-fleet, cloud Nexus\n for enterprise multi-tenant deployments.\n- **Conductor URL public fallback.** `adk mesh join` resolves the internal\n `aitheros-conductor:8193` hostname; if unreachable (e.g., self-hosted nodes),\n it falls back to the public `conductor.aitherium.com:8193` endpoint,\n eliminating bootstrap configuration friction.\n\n### Documentation\n\n- **Self-hosting runbook.** New `docs/SELF_HOSTING.md` covers deploying AitherOS\n on customer infrastructure with Qdrant + mesh + AitherConnect onboarding.\n- **Mesh transport constraints documented.** Raw WireGuard requires public\n UDP:51820 endpoint (cloud instances, dedicated hardware); Headscale for\n NAT'd networks. Noted in CLI help and module docstrings.\n\n## [2.15.0] - 2026-07-04\n\n### Added — built-in web admin console + portal-profile settings sync\n\n- **Web admin console.** The page `adk up` opens (served at `/` and `/ui`) is now a full,\n self-contained console — Chat plus six admin tabs: **Backend** (view/switch LLM provider,\n set keys, test connection), **Packs** (list/enable/disable/reload tool packs),\n **Sessions** (browse/delete conversations), **Logs** (redacted agent-log tail),\n **Graph** (knowledge-graph search/neighborhood/stats), and **MCP** (add/remove external\n MCP servers). No build step, no CDN — a single packaged HTML asset. The original minimal\n streaming chat page stays at `/chat` for back-compat.\n- **Admin API (`adk/admin_api.py`).** ~20 endpoints under `/admin/*`, all bearer-gated by the\n existing middleware (never in `_skip_auth_paths`). Operates on the live agent — LLM backend\n swap (`LLMRouter.switch_backend`), pack reload, and MCP registration take effect without a\n restart.\n- **Settings sync (`adk/settings_sync.py`).** The user's portal profile is the source of\n truth: on startup the agent pulls `preferences.adk` and applies it over the local\n `~/.aither/config.yaml` cache; admin-console edits push a debounced snapshot back to\n `PUT /api/settings/preferences`. Provider API keys and MCP auth headers are device-local\n secrets and are never included in the pushed snapshot. Opt out with `AITHER_SETTINGS_SYNC=false`.\n\n### Security\n\n- **Timing-safe token comparison.** The server bearer check now uses `hmac.compare_digest`\n (the token is reachable over the public tunnel; a byte-wise `!=` leaked it to timing attacks).\n- **MCP add is SSRF-guarded + confirmed.** Adding an external MCP server is a two-step\n prepare→confirm flow; URLs resolving to loopback / private / link-local / metadata addresses\n are rejected (override with `AITHER_ALLOW_PRIVATE_MCP=1` for trusted local servers).\n- **Config editor is allowlisted.** `PATCH /admin/config` writes only safe scalar prefs and\n hard-denies fields that could execute code or redirect traffic at startup (e.g.\n `aither_toolpack_dirs`, backend base URLs).\n- **Log tail is scrubbed.** Bearer tokens, `#k=` fragments, and `sk-`/`aither_sk_` keys are\n redacted from the log-tail endpoint; the tunnel log is excluded.\n\n## [2.14.8] - 2026-07-04\n\n### Added — `adk up` for normal humans (no API key, no prereqs, live chat)\n\n- **Hosted-brain default — no API key required.** When there's no local model and no\n provider key, `adk up` signs in (device-flow) and routes inference through the gateway\n using the portal token (`AITHER_LLM_BACKEND=gateway` + bearer). A non-technical user\n never needs to obtain or paste an LLM API key. Explicit `--provider`/local model still\n take precedence; unattended `--yes` with no token/backend fails cleanly (exit 3).\n- **cloudflared auto-download.** If the tunnel binary is missing, `adk up` downloads the\n right platform asset to `~/.aither/bin` (SHA256-verified against the release; opt out with\n `AITHER_CLOUDFLARED_REQUIRE_CHECKSUM=0`) instead of exiting with an install hint.\n- **Built-in streaming chat page.** The agent now serves a self-contained chat UI at `/`\n (and `/chat`) with a live \"thinking…\" indicator and token-by-token streaming — so a human\n has somewhere to talk to it with immediate feedback instead of a blank wait. `adk up` opens\n it locally and passes the callback bearer in the URL fragment (`#k=…`), so `/chat/stream`\n stays authenticated (not an open proxy) with nothing to paste.\n- **Human-readable failures.** `adk up` errors now carry a plain-English `next_action`\n (kept machine-readable in `--yes` JSON).\n\n### Fixed\n\n- `adk up` chat link pointed at a non-existent `/settings/agent` route; it now points at the\n agent's own streaming chat page.\n\n## [2.14.7] - 2026-07-04\n\n### Added\n\n- **`adk up` — one command to run a persistent, fleet-connected agent.** Collapses\n the scattered onboarding surface (`host`/`quickstart`/`connect`/`login`/`setup`)\n into a single command that starts `aither-serve` (single agent, identity `aither`\n by default), opens a Cloudflare quick-tunnel for fleet reachability, registers with\n the portal, and installs a reboot-persistent autostart entry (Windows Task Scheduler\n / systemd-user / launchd). Detaches by default so the terminal is freed; `--foreground`\n blocks. Reuses the existing device-flow login, cloudflared discovery, provider-key\n store, and `_preflight_check` backend detection.\n- **Fully non-interactive path for autonomous agents.** `adk up --yes` (or any\n non-TTY invocation) takes ZERO prompts — everything resolves from flags, env, and\n saved config — and emits a single machine-readable JSON status line with meaningful\n exit codes (`0` ok, `2` bad-input, `3` no-backend, `4` tunnel-missing, `5` register-failed).\n Degrades to local-only (warn, not fail) when a portal token is absent unless\n `--require-register` is set.\n- **`adk down` / `adk status --json` companions.** `down` stops the agent + tunnel,\n best-effort deregisters, and removes the autostart entry; `status` reports the running\n agent (liveness + `/health` + tunnel + registration), with `--json` for agents/CI.\n- **`adk stack`** — the previous `adk up` behaviour (supervise the Room + Ollama consumer\n stack) now lives here; `adk up` is the connected-agent command.\n\n### Internal\n\n- New `adk/agent_daemon.py`: detached process spawn, status file\n (`~/.aither/adk-up.json`), pid liveness/teardown, and cross-platform autostart install.\n\n## [2.14.6] - 2026-07-04\n\n### Added\n\n- **`adk.preflight` — self-bootstrap capability probe.** `run_preflight(agent,\n spec)` wakes an agent up knowing what it is actually plugged into: a bounded\n (1.5s) parallel `LivenessProbe` over the router's resolved backends\n (primary/reasoning), the embeddings winner, remote MCP tools, the structured-ML\n / `/ml/teach` endpoints, and voice — returning an honest `CapabilityReport`.\n Status vocabulary distinguishes reachability from entitlement\n (`OK`/`MISSING`/`UNREACHABLE`/`AUTH`/`TIER_DENIED`/`UNSUPPORTED`), a hosted slot\n never prints a bare `OK`, and a required-but-unsatisfied slot aborts before the\n loop instead of blind-firing. Headless task/rules resolution + an allowed-roots\n policy binding (`builtin_tools.set_allowed_roots`, which resets the memoized\n cache so the override actually enforces).\n- **Multimodal (image) messages.** `Message.content` now accepts an OpenAI-style\n content-part list (`str | list[dict]`), passed through verbatim to the\n OpenAI-compatible body (gateway / vLLM / OpenAI). New `adk.llm.multimodal`\n helpers — `image_message()`, `image_content_parts()`, `to_image_url()` — build\n the parts and encode local image bytes/paths into `data:` URIs.\n- **Real vision preflight probe.** The vision slot is no longer unconditionally\n `UNSUPPORTED`: it sends a discriminable image (a solid red square) and requires\n the model to name the color, so a blind/text-only model cannot false-pass. `OK`\n only on a real round-trip; `AUTH` on 401/403; `UNSUPPORTED` when there is no\n OpenAI-compatible provider or the model cannot see the image.\n\n## [2.14.5] - 2026-07-04\n\n### Added\n\n- **`adk.reasoning.mcts` — generic Monte-Carlo Tree Search library.** A reusable\n MCTS engine (ported from the internal `UnifiedMCTS`, with all internal couplings\n removed) that any agent can drive over its own environment:\n - `MCTSEnvironment` protocol (`get_state_hash` / `get_actions` / `step` /\n `evaluate` / `clone`) — implement it and search.\n - Three **optional, default-off** model seams — `TransitionModel`, `PolicyModel`\n (PUCT priors), `ValueModel` — so a learned world-model / policy / value can be\n injected without touching the core; with all three `None` the engine is a plain\n UCT search.\n - `ObservedTransitionModel` adapter — a cheap online lookup world-model (hit =\n exact stored transition; miss = identity + negative \"unknown\" reward +\n `is_uncertain`, biasing exploration toward learning), with JSONL save/load for\n cross-run reuse.\n - `UnifiedMCTS(config).search(env)` returns an `MCTSResult` (best action, value,\n confidence, principal-variation path) and an optional `MCTSTrace`\n (visit-distribution policy target) for distillation.\n- **Self-bootstrapping agent machinery.** Build an agent from a declarative spec\n instead of bespoke wiring:\n - `adk.bootstrap.build_agent_from_spec(path) -> (AitherAgent, RunCtx)` — resolves\n prompts, activates required/optional tool packs, and carries a `memory_map`\n (a `node_type -> (role, tier)` override for memory writes, never a gate).\n - `adk.prompts.PromptBridge` — resolves an agent's prompt bindings from files\n (relative to the spec) or `AitherPrompts` dot-keys (optional import).\n - `adk.packs.PackActivator` — eager activation of installed tool packs by name\n over `register_tool_packs`; a required pack that registers zero tools raises\n `PackUnavailable`, optional packs soft-degrade.\n - `python -m adk.bootstrap.generic_agent <spec.yaml>` inspects a spec (resolved\n name, activated packs + tool counts, prompt keys, memory map) without running a\n live loop.\n- **`deep_research` tool pack** — a full multi-engine web-research workflow as\n `dr_*` agent tools (keyless search, page fetch + cache, site mirror, a citable\n findings knowledge graph, and markdown/PDF/DOCX report generation), activatable\n by name via the tool-pack loader.\n\n## [2.14.4] - 2026-07-04\n\n### Added\n\n- **`structured_ml` tool domain** — zero-shot structured-data inference for agents,\n backed by TabFM (tabular classification/regression) and TimesFM (time-series\n forecasting). Three tools:\n - `tabular_classify(support_rows, target, query_rows)` — classify rows from a\n labeled support set, in-context (no training step; up to 10 classes).\n - `tabular_regress(support_rows, target, query_rows)` — predict a numeric target.\n - `timeseries_forecast(series, horizon)` — forecast future values of a series.\n\n The tools POST to a structured-ML inference service resolved from\n `AITHER_STRUCTURED_ML_URL` (default `http://localhost:8192`), reject non-http(s)\n URLs, and summarise oversized responses to protect the agent's context.\n- **`Capability.STRUCTURED_INFERENCE`** capability token and a new **`analyst`**\n identity/pack that ships the `structured_ml` domain by default. The domain is\n opt-in — only identities that enable the `structured_ml` category get the tools.\n\n## [2.14.3] - 2026-07-03\n\n### Fixed\n\nAll four bugs below were found by actually booting `adk run` against a real\nchat model and driving a real tool-calling request end-to-end (not inferred\nfrom reading the code) — each one silently broke the \"local agent uses its\nconfigured model and its tools actually work\" path.\n\n- **`AITHER_MODEL` / `config.model` silently ignored by the OpenAI-compatible\n backend**: `LLMRouter.__init__` never read `config.model` into `self._model`\n unless the caller passed `model=` directly to the constructor — which nothing\n did. Every explicit model choice (env var, config file) was dropped, the\n provider fell back to a hardcoded per-backend default (`gpt-4o-mini` for the\n `openai` backend), and an unrecognized model name got silently remapped\n server-side to the wrong backing model. Concretely: setting\n `AITHER_MODEL=aither-orchestrator` had no effect — every request actually\n went to a different model that was never meant to be called directly and\n can't reliably use tools.\n- **`OPENAI_BASE_URL` ignored for explicit `--backend`/`AITHER_LLM_BACKEND`\n selection**: `LLMRouter._auto_detect()`'s explicit-backend branch hardcoded\n `base_url=None` when constructing the provider, instead of reading\n `config.openai_base_url` (which two other call sites in the same file\n already did correctly). Any self-hosted OpenAI-compatible endpoint\n (MicroScheduler, vLLM, LM Studio) configured via `OPENAI_BASE_URL` was\n silently ignored in favor of `https://api.openai.com/v1`.\n- **Tool-call steering never engaged on default-effort chat**: the retry that\n nudges a model back into structured tool-calling when it responds with prose\n instead of a function call was gated behind `effort >= 6`. Default `effort`\n for a plain `agent.chat(message)` call resolves to `5`, so the safety net —\n built specifically for this failure mode — never fired for default requests.\n Also now forces `tool_choice=\"required\"` on the retry instead of relying on a\n text nudge alone.\n- **Every non-string builtin-tool parameter silently typed as `\"string\"` in\n the generated tool schema**: `builtin_tools.py` uses\n `from __future__ import annotations`, so `_extract_parameters()` in\n `tools.py` was reading postponed (string) type hints (`\"int\"`, not `int`)\n straight out of `fn.__annotations__`, which never matched\n `_type_to_schema`'s type-object keys and silently fell through to\n `{\"type\": \"string\"}`. Every builtin tool with an `int`/`float`/`bool`/`list`\n parameter was affected — e.g. `file_read(start_line: int, end_line: int)`\n advertised both as strings, so a model correctly calling the tool with\n `\"start_line\": \"0\"` crashed the actual implementation with\n `unsupported operand type(s) for -: 'str' and 'int'`. Fixed by resolving\n hints through `typing.get_type_hints()` instead of the raw annotations dict.\n `_coerce_arguments()` — the runtime safety net that's supposed to coerce a\n model-supplied `\"0\"` string into `0` before a tool call executes — had the\n exact same raw-`__annotations__` bug and silently no-opped for every builtin\n tool; fixed the same way.\n- **Streaming tool-loop could hang a connection forever**: `chat_stream()`'s\n fallback to the sync tool loop (tool calls can't stream token-by-token) had\n no timeout, so a model that didn't cleanly terminate a tool-calling round\n trip left the SSE connection open indefinitely with zero bytes sent. Bounded\n with a 60s `asyncio.wait_for`; on timeout the stream yields a clear message\n instead of hanging.\n\n## [2.14.2] - 2026-07-03\n\n### Fixed\n- **AitherShell session amnesia**: the REPL generated a fresh `session_id` for\n EVERY message, so conversation history never followed across turns (the\n server stored each turn under a different session). One stable id is now\n minted per shell run; `/resume` and `/new` semantics unchanged, and the\n cross-restart auto-restore of the last session now actually works.\n\n## [2.14.1] - 2026-07-02\n\n### Added\n- **Account-tied licensing via device-flow login** — `adk login` now persists the account\n license returned by the device-flow handshake to `~/.aither/license.json` (base64\n `license_key` → `{payload, signature}` envelope), so entitlements from the authenticated\n account carry into every later `adk` invocation without re-auth. Pairs with the fleet-side\n node tool-pack gate that grants packs by plan-tier rank.\n- **`can_use_formbridge` / `can_use_untether` entitlements** (PROFESSIONAL+ rank ≥ 3) backing\n the FormBridge and UNTETHER agent tool-packs.\n\n### Fixed\n- `adk` CLI: guard the optional `memory_graph` singleton (was a latent `NameError` on the\n `/stats`, `/memory`, and save paths when memory was not constructed).\n\n## [2.14.0] - 2026-07-02\n\n### Added\n- **`adk.memory_wiki` — LyraWiki-style self-managed semantic knowledge** (opt-in; nothing constructs it by default): `MemoryWiki(graph_memory)` maintains curated wiki ARTICLES as `wiki_article` nodes (title/slug, LLM-owned markdown with `[[wikilinks]]`, embedding, durable tier, `CONSOLIDATED_FROM` edges to source memories, governance-ledgered revisions).\n - `consolidate(llm, since=, budget=)` clusters unconsolidated episodic/fact nodes (embedding + keyword), drafts/UPDATEs articles via the INJECTED llm callable (`str -> str` or `messages -> str`, sync or async — no hard model dependency), cites source node ids, marks contradicted sources superseded, then DEMOTES consolidated sources to a fast-decay tier via `promote()` (represented knowledge doesn't need raw copies).\n - `lint(llm=None)` — deterministic health checks (orphan wikilinks, empty/stale articles, live-but-contradicted pairs) + an optional LLM audit pass.\n - `prune(relevance_floor=0.05, hard_delete_after_days=14)` — article relevance = tier freshness × reinforcement (reinforce-on-recall) + link-degree to LIVE memories; below the floor → reversible governance tombstone; entombed past the window → **HARD-DELETE** (the tombstone snapshot itself is purged — content and embedding irrecoverable; the true-deletion path). A node is never deleted without a tombstone first.\n - `recall(query, limit)` — article-first RAG (curated articles rank above raw nodes) that reinforces returned articles.\n- **`adk.routines` — agent heartbeat + self-programmed routines**: `RoutineStore` is a durable registry (`~/.aither/routines/{agent}.json`, `AITHER_DATA_DIR`-aware, injectable path) of cron-scheduled self-prompts `{name, cron, instruction, enabled, last_run, last_result, tags}` that rehydrates into the existing `adk.cron.CronScheduler` on `start()`. A fire runs `agent.chat(instruction)` with the agent's full toolset — the agent programs its future self with its own adk. Guardrails: `max_routines` (12), a 5-minute min fire interval per routine, a per-fire timeout, and every result ledgered (truncated) to `last_result`. Direct-callable routines (`register_direct`) fire a bound method instead of a self-prompt. Self-management tools (`routine_create/list/update/pause/resume/delete/run_now`) ship as OpenAI tool defs + handlers (`build_routine_tools` / `register_routine_tools` / `routine_tool_defs`); the handlers ONLY touch the RoutineStore — they can never modify agent config or safety-relevant settings (the leash principle).\n- **`AitherAgent(routines=True, memory_maintenance=True)`** (both default **False** → the default agent is byte-identical): `routines=True` builds the RoutineStore + scheduler, registers the self-management tools, and starts the heartbeat lazily on the first `chat()` (or via explicit `start_routines()`). `memory_maintenance=True` registers DEFAULT routines — `wiki_consolidate` (every 2 h), `wiki_lint` / `wiki_prune` / `graph_sweep` (daily) — as DIRECT method fires so memory upkeep runs even on tiny models, while staying visible/manageable via `routine_list`.\n- `TombstoneStore.purge(tombstone_id)` — the hard-delete endpoint: irrecoverably removes a tombstone snapshot and rewrites the persisted store without it (consumed by `memory_wiki.prune`).\n\n## [2.13.6] - 2026-07-02\n\n### Added\n- **Qdrant backend for GraphMemory dataplane sync — TRUE two-way, verified live.** Set `AITHER_FLEET_QDRANT_URL` and the sync uses Qdrant (`upsert` + `scroll` + filtered `search`) instead of Nexus. This is what actually delivers cross-agent memory sharing: a node is upserted with its OWN embedding under a deterministic per-(tenant,node) point id (idempotent re-push), and `fleet_pull` reliably SCROLLS a tenant's points back — the enumerate Nexus lacks (its `/search` doesn't return ingested docs and `/export` needs lancedb, which has a hard dependency conflict in the fleet). **Proven live with 2 agents:** agent-1 ingests → auto-syncs to the tenant dataplane; a fresh agent-2 in the same tenant `fleet_pull`s all of agent-1's nodes and can search them; a different tenant pulls nothing (isolation). Dimension-safe (never mixes vector dims in a collection). Local SQLite stays source of truth; best-effort, never raises.\n\n## [2.13.5] - 2026-07-02\n\n### Fixed\n- **CRITICAL — GraphMemory dataplane sync push was 100% failing.** `_fleet_push_node` sent `source_type=\"graph\"` / `content_type=\"graph_node\"`, which AitherNexus rejects as invalid enums (HTTP 500) — so NO graph node ever replicated in 2.13.4. Now sends the accepted `source_type=\"manual\"` / `content_type=\"text\"` (graph provenance is carried in `metadata.synced_from`). **Verified live** against a fleet Nexus: a 4-node ingest replicates with `pending==0` and the collection count reflects it.\n\n### Added\n- **Swarm-awareness notification** — on a successful sync batch, `GraphMemory` emits a best-effort `graph.synced` event (tenant/workspace/agent/collection/count) to `AITHER_FLEET_EVENTS_URL` so OTHER agents in the tenant/swarm can pull the fresh data and deconflict in-flight work (push-based awareness instead of polling). No-op unless the env is set.\n\n### Known limitation\n- Cross-agent **rehydration** (`fleet_pull`) is only reliable when the fleet Nexus is backed by lancedb (persistent + exportable). An in-memory Nexus stores on `/ingest` but neither its `/search` nor `/export` returns the docs — two-way read needs the persistent Nexus.\n\n## [2.13.4] - 2026-07-02\n\n### Added\n- **Memory-maintenance primitives on `GraphMemory`** (all opt-in; default behaviour byte-identical):\n - **Reinforce-on-recall**: `recall_with_activation(..., reinforce=True)` bumps `reinforcement_count`/`last_reinforced` on every RETURNED node's metadata mirror (one UPDATE per node, WAL-safe) — making the `MemoryRecord` reinforcement fields the activation scorer reads LIVE. Default `False` = zero writes.\n - **`sweep(now, archive_below=0.05, max_nodes=None, dry_run=False)`**: read-time decay enforced at rest — archives nodes past their tier TTL with freshness×reinforcement score below `archive_below` and `reinforcement_count <= 1`; `max_nodes` additionally archives the lowest-scored overflow. PERMANENT tier immune. Every archive is a reversible governance tombstone (`TombstoneStore.recover`) + a FORGET ledger entry; a node is never deleted without a tombstone. `dry_run` returns the would-archive list without acting.\n - **`promote(node_id, tier=?, role=?)`**: re-tier/re-role a node IN PLACE (tier/role columns + metadata mirror) — edges, embedding and ledger history survive; ledgers an UPDATE entry when governance is on.\n- `MutationType.UPDATE` ledger entry kind (in-place re-tier/re-role).\n- **Per-tenant dataplane sync for `GraphMemory`** — replicate the local graph to a per-tenant vector dataplane (Nexus/Qdrant) so a sovereign agent's memory is durable + rehydratable across restarts and hosting boundaries. `fleet_push_all_nodes()` (catch-up), `fleet_pull()` (best-effort semantic top-up), `fleet_sync_pending()`, and auto-sync on ingest (`_maybe_autosync` + `drain_sync()`). **On by default** (`AITHER_FLEET_SYNC=auto`; only `false/0/off` disables) with an inferred target (local Nexus `:8122` or the gateway in cloud mode) and a SEPARATE collection (`AITHER_FLEET_GRAPH_COLLECTION`, default `graph_memory`) from the KV memory sync. New `GraphMemory(tenant_id=, workspace_id=, fleet_url=, auto_sync=)` constructor args override env (required for multi-tenant host processes). Node `synced` column + schema v4 migration. TLS verify is CA-bundle-aware (`AITHER_CA_BUNDLE`) for the internal-TLS mesh. Local SQLite stays the source of truth; every sync path is best-effort and never raises into ingest.\n\n## [2.13.3] - 2026-07-02\n\n### Added\n- **Canonical self-deploying embeddings provider** (`adk.embeddings`): one embedding provider for the whole SDK so vectors are portable across scopes (platform / tenant / sovereign) — same model + dimension everywhere. Pinned to `nomic-embed-text` (768-d; the vLLM `--served-model-name` for nomic-embed-text-v1.5). Lazy, single-flight resolution chain: explicit `AITHER_EMBEDDINGS_URL` → local vLLM (`:8209` then `:8120`, HTTPS-then-HTTP) → local Ollama → gateway (`AITHER_GATEWAY_EMBEDDINGS_URL`) → auto-deploy a local vLLM embeddings container if a GPU + Docker are present (opt out with `AITHER_EMBED_AUTODEPLOY=0`) → CPU sentence-transformers (384-d, degraded) → feature-hash (384-d, degraded, always works). Every batch is dimension-tagged so callers never silently mix 768-d and 384-d vectors. `get_default_embedder()`, `embed_texts()`, `embed_one()`, `get_provider()`, `reset_provider()`.\n\n### Changed\n- **`GraphMemory` now defaults its embedder to the canonical provider** (opt out with `AITHER_GRAPH_EMBEDDER=legacy`), so every adk agent shares one 768-d space. Added a meta-table dimension guard: the index is pinned to its first embedding's dimension and later different-dimension vectors are refused (kills 768↔384 mixing that silently poisons cosine similarity).\n\n## [2.13.2] - 2026-07-01\n\n### Added\n- **Session persistence**: AitherShell now auto-saves the active session_id to `~/.aither/config.yaml` after each chat turn and auto-restores it on the next shell launch. Multi-turn conversations survive across restarts — the messages were always persisted in SQLite + JSON, only the session pointer was missing.\n- `/new` command to start a fresh session (clears saved session_id).\n- `/sessions` command to list recent sessions with agent name, message count, and timestamp. Resume any with `/resume <id>`.\n\n## [2.13.1] - 2026-07-01\n\n### Fixed\n- **CRITICAL:** `deploy_connect()` now sends a HEAD request to verify the GitHub release asset exists before downloading, with a clear error if the asset is missing.\n- **CRITICAL:** Compose file URLs are now pinned to a stable commit hash (configurable via `AITHER_COMPOSE_PIN` env) instead of tracking the mutable `main` branch.\n- **CRITICAL:** `deploy_adk_node()` now falls back to starting `adk-serve` natively when Docker is unavailable, instead of blocking with \"Docker not installed\".\n- **HIGH:** GHCR login failure now blocks deployment immediately with actionable guidance, instead of warning and continuing to a confusing image-pull failure.\n- **HIGH:** Health checks now stream the last 30 lines of Docker Compose logs on failure, so the user can see why a container didn't start.\n- **HIGH:** vLLM port allocation now probes for conflicts before assigning; if port 8200 is busy, workers are automatically remapped to the next free port.\n- **MEDIUM:** Compose file downloads are now cached with ETag; re-running `adk deploy node` skips re-downloading an unchanged file.\n- **MEDIUM:** DGX Spark discovery now respects `AITHER_DGX_HOSTS` env (comma-separated) for custom network topologies, instead of only checking `spark.local` and `192.168.0.33`.\n- **MEDIUM:** Cloud API keys (Anthropic, OpenAI, DeepSeek) are now validated with a lightweight probe during `adk setup` infra scan, so invalid keys surface early.\n- **MEDIUM:** `deploy_connect()` now validates the extracted extension has a `manifest.json` and warns if it's missing.\n\n## [2.13.0] - 2026-07-01\n\n### Removed\n- **`adk.faculties.memory_graph`** — the pickle-based `MemoryGraph` module is deleted. `from adk.faculties import MemoryGraph` now resolves to the canonical `adk.graph_memory.GraphMemory` for back-compat.\n- **`adk.platform.memory`** — the entire legacy memory subsystem (`MemoryManager`, `UnifiedMemorySystem`, `GameEngine`, `StoryboardEngine`, `AnchorGenerator`) is deleted. Agent memory is `adk.memory.Memory` + `adk.graph_memory.GraphMemory`.\n\n### Changed\n- `adk work` command now uses `GraphMemory` instead of the removed pickle-based faculty graph.\n- `adk.MemoryGraph` and `adk.faculties.MemoryGraph` lazy exports now resolve to `adk.graph_memory.GraphMemory`.\n- `agent.set_memory_graph()` kept for back-compat but documented as deprecated; agents already wire `GraphMemory` automatically in `__init__`.\n- Platform startup (`adk.platform.infrastructure.startup`) gracefully handles missing `MemoryManager`.\n- Platform CLI module listing and health check updated to reflect removed memory module.\n\n## [2.12.7] - 2026-07-01\n\n### Deprecated\n- `adk.faculties.MemoryGraph` — emits `DeprecationWarning` on instantiation; use `adk.graph_memory.GraphMemory` (SQLite-backed, governed, embedding-native) instead.\n- `adk.platform.memory` package — the legacy `MemoryManager`, `UnifiedMemorySystem`, `GameEngine`, and `StoryboardEngine` modules now emit `DeprecationWarning` on import; use `adk.memory.Memory` and `adk.graph_memory.GraphMemory` for new code.\n\n### Changed\n- Updated `adk.faculties` docstring and usage examples to point at the canonical memory surface.\n\n## [2.12.6] - 2026-07-01\n\n### Changed\n- Trimmed public package debt by removing grid runbook artifacts and shell/room binary placeholder directories from published artifacts.\n- Removed stale public-sync allowlist entries for deleted phase test files and added explicit public-sync guards for binary placeholder directories and grid artifacts.\n- Updated the public README grid note so it no longer links to runbook files excluded from the SDK package.\n\n## [2.12.5] - 2026-07-01\n\n### Added\n- Added best-effort fleet memory sync from local ADK memory into Nexus/Qdrant-compatible RAG stores, including batch catch-up and vector-search recall hooks.\n- Added saved config/env support for fleet memory URL, collection, and sync mode.\n\n### Changed\n- Excluded FormBridge runtime/test internals from public ADK packaging and public repo sync.\n\n## [2.12.4] - 2026-07-01\n\n### Added\n- Added a hot-reload endpoint for installed agent packs so newly applied pack tools can be picked up without restarting `aither-serve`.\n\n### Fixed\n- Fixed synchronous `ChatRelay` calls on Python 3.12 when no event loop is running; async WebSocket delivery is still scheduled when a loop exists.\n- Aligned gateway inference tier tests with the current public ladder: free, starter, pro, enterprise, platform.\n\n## [2.12.0] - 2026-06-28\n\n### Added — one-command local inference + upgrade path (click-to-run)\n- **`adk quickstart-local`** — detects hardware, picks a backend\n (Ollama / llama.cpp / vLLM), installs it, downloads a model, verifies, and\n prints next steps. Bootstraps Ollama itself via winget/brew/install.sh when\n missing (no \"go install it\" dead-ends). Default model `gemma4:e2b`.\n- **`adk backend switch <ollama|llamacpp|vllm>`** (+ `backend status`) — migrate\n the active deployment between engines; re-points config and smoke-tests.\n- **`adk install pack:<name>`** (+ `adk install list` / `adk packs`) — install\n ready-made agent packs (openclaw, hermes, claude-code) into `~/.aither/agents`.\n\n### Fixed (found via live end-to-end testing on Windows + CUDA)\n- quickstart-local reported success on a dead endpoint (smoke test was warn-only);\n a failed smoke now fails the command, with a service-readiness wait.\n- `--model` resolved to `None` (getattr default never fired) → uses the real default.\n- Smoke test: 30s→180s timeout for cold model load; accepts reasoning-model output\n (empty `content` at low token budgets is no longer a false failure).\n- `adk install pack:<name>` colon syntax (argparse rejected it before dispatch).\n- `adk backend status` health probe used a nonexistent `/health` path → 404; now\n probes `/v1/models` (uniform OpenAI-compatible liveness for all three backends).\n- Windows: removed non-ASCII glyphs from printed strings that crashed the cp1252\n console (a `✓` even made a successful install report failure).\n\n## [2.11.1] - 2026-06-14\n\n### Fixed — `adk host` / `adk login` device flow (autonomous onboarding)\nFound driving `adk host` against production; all broke real device-flow login:\n- **Device flow hit the portal FRONTEND, not AitherIdentity.** `adk login`/`adk host`\n POSTed `/auth/device/code` to portal/veil.aitherium.com, which 307-redirects API auth\n to `/login`. Added `_resolve_identity_url()` mapping the aitherium.com topology to\n `idp.aitherium.com` (the Identity API); localhost/bespoke pass through unchanged.\n- **Cloudflare 403'd urllib.** `idp.*` sits behind Cloudflare, which blocks urllib's\n default `Python-urllib/3.x` UA. Device-code + poll requests now send a real\n User-Agent + Accept (verified urllib→idp 200, was 403).\n- **`adk host` now self-authenticates via device flow** when there's no token, and\n **retries via device flow on a 401/403** (a stale saved token no longer dead-ends\n registration).\n\n## [2.11.0] - 2026-06-14\n\n### Added — attach your own MCP \"hands\" to a self-hosted agent\nA self-hosted agent now has the full brain (your model) · body (this loop) · **hands**\n(your tools) trifecta. The AitherOS platform relays the MCP servers you registered for\nyour agent in the `/stream` request body as `mcp_endpoints`, and the loop wires their\ntools into the ReAct turn:\n- **`adk/mcp_endpoint_tools.register_mcp_endpoint_tools(agent, endpoints)`** — speaks MCP\n JSON-RPC (`tools/list` to discover, `tools/call` to invoke) and registers a namespaced\n proxy tool (`{endpoint}__{tool}`) for each advertised tool. Mirrors `app_proxy_tools`.\n Best-effort: an unreachable endpoint registers nothing and never raises.\n- **`POST /stream` and `POST /chat/stream`** accept an optional `mcp_endpoints` list\n (`[{name, url, [headers]}]`); `_aitheros_stream` attaches them to the agent before the\n turn. Existing callers are unaffected (the field is optional).\n\n## [2.10.1] - 2026-06-14\n\n### Fixed — self-hosted `aither-serve` was crash-on-startup (2.10.0 regression)\nA fresh `pip install aither-adk && aither-serve --backend deepseek` could not boot. Found\nand fixed while standing up a real self-hosted DeepSeek agent end-to-end:\n- **Startup `NameError: name 'port' is not defined`** in three lifespan helpers\n (`_join_aithernet`, `_init_a2a_server`, `_start_mesh_hosting`) — they referenced a bare\n `port` no longer in scope. Now use `config.server_port`, and `main()` writes the resolved\n `--port`/`--host` back to `config` so every lifespan helper and the invoke_url see the\n actual bound port.\n- **Fleet self-registration crashed the whole server** — the registration handlers caught\n `(ImportError, RuntimeError, OSError, ConnectionError)` but **not** `httpx.HTTPError`, so a\n `RemoteProtocolError`/connect error talking to the control plane took the agent down. All\n network-facing lifespan handlers now also catch `httpx.HTTPError` (non-fatal, as intended).\n- **`--backend` was ignored when `AITHER_API_KEY` was set** — `_auto_detect` picked the cloud\n gateway before the explicit backend, and `_try_elysium_fallback` then replaced the chosen\n provider. An explicit backend (deepseek/openai/anthropic/your own vllm/ollama) now always\n wins; \"auto\"/\"gateway\" still flow through detection. The operator's own brain is never\n silently hijacked.\n- **Approved-tool trace truncated after `/sessions/{id}/confirm`** — the SSE relay treated\n `AgentResponse.tool_calls_made` (a `list[str]`) as dicts (`tc.get(...)`), raised\n `AttributeError`, and an over-narrow `except` swallowed it so the client saw only\n `session_start` + heartbeats. The relay now handles string tool names, the `except` is\n broad (always emits a typed `error` + terminal `complete`), and the module-level\n `_strata_ingest` `NameError` + Chronicle `session_id` kwarg mismatch are fixed.\n\n## [2.10.0] - 2026-06-13\n\n### Added — human-in-the-loop tool approval (`adk/approval.py`)\n- **Pause before a gated tool, resume on decision.** An agent can now pause its turn before\n executing a sensitive tool and wait for a human's allow/deny — so a managed control plane\n (or any operator) can gate tool use without forking the loop.\n- **Policy via `AITHER_TOOL_APPROVAL`** — a comma-separated list of tool names (optionally\n `agent:tool`), or `*` for every tool. Empty = no gating (unchanged behavior).\n- **Survives restarts / days-later approvals.** The paused turn (user message + pending tool\n calls) is persisted to disk keyed by `session_id`; resume is a fresh request, not a held\n connection. Decisions are recorded per `(session_id, tool_name)`.\n- **`AgentResponse.requires_action` / `.pending`** — set when a turn paused; `pending` lists\n the gated tool calls awaiting a decision.\n- **`AitherAgent.resume(session_id, decisions)`** — records the decisions and re-runs the\n paused turn (the gate consumes them: allowed tools execute, denied tools get a denial\n observation, the turn continues — and may pause again on a different gated tool).\n- **Server:** `POST /sessions/{id}/confirm` records decisions and streams the resumed turn;\n `/stream` emits a `requires_action` event when a turn pauses.\n\n## [2.9.0] - 2026-06-13\n\n### Added — universal LLM continuation primitive (`adk/llm/continuation.py`)\n- **`run_continuation()` + `stitch()`** — one shared continue-until-complete primitive. When a\n completion stops because it hit the output token cap (`finish_reason == \"length\"`), it continues\n the generation and STITCHES the chunks (overlap-dedup + restart-detect) into a complete answer,\n instead of every call-site hand-rolling its own retry. Bounded three ways (max rounds AND total\n output ceiling AND a no-growth break), and it never mutates the caller's message history.\n- **`LLMProvider.chat_with_continuation()`** and **`LLMRouter.chat_with_continuation()`** — every\n provider AND the router inherit the primitive, so any call-site gets continuation for free.\n- Kill-switch `ADK_LLM_CONTINUATION=off`; tunables `ADK_LLM_MAX_CONTINUATIONS`,\n `ADK_LLM_MAX_TOTAL_OUTPUT_TOKENS` (defaults: 2 rounds / 8192 tokens).\n\n### Changed\n- The ReAct loop's truncation recovery now uses the shared primitive. The previous inline\n doubling-retry REPLACED the partial response (losing earlier text in `resp.content`) and injected\n continuation scaffolding into the live message history; it now STITCHES and leaves history clean.\n Tool-call turns are never continued as text.\n\n### Included — previously-committed-but-unreleased changes\n- 2.8.2: TLS verification on by default (`verify=False` removed).\n- 2.8.1: baked license + pack-signing public keys.\n- 2.8.0: version-bump release.\n\n## [2.7.0] - 2026-06-05\n\n### Added — provider-agnostic LLM cost levers (at the `LLMProvider` ABC)\n- **Prompt caching.** New `ProviderCapabilities` (`prompt_cache: explicit|automatic|none`, `batch`) and\n normalized `LLMResponse.cache_read_tokens`/`cache_write_tokens`, so the \"tokens saved\" meter never\n depends on which backend served the turn. Anthropic inserts `cache_control` breakpoints over the stable\n system+tools prefix; OpenAI/DeepSeek caching is automatic and their `cached_tokens` are now surfaced\n (previously discarded). Default-on in `AitherAgent.chat`; override with `cache=False`.\n- **ResponseCache** (`adk/llm/cache.py`) — exact-match response cache with a pluggable backend, wired into\n `LLMRouter` as an opt-in (`LLMRouter(response_cache=…)` + per-call `cacheable=True`). OFF by default — no\n determinism change unless opted in.\n- **Gemini provider** (`adk/llm/gemini.py`) with explicit `cachedContents` caching; registered in the router.\n- **Async batch runner** (`adk/llm/batch_runner.py`) — `run_batch()` over the Anthropic Message Batches /\n OpenAI Batch APIs (~50% off) with a transparent fallback to concurrent `chat()` where unsupported.\n- **Graph-RAG retriever interface** (`adk/graph_retriever.py`).\n\n### Added — principal authority (prompt-injection defense enforced outside the model)\n- `adk/auth.py`: `Principal` / `AuthContext` / `PrincipalResolver` (+ deny-all default).\n- `ToolRegistry.execute(name, args, auth=…)` — a default-deny authorization gate at the tool boundary, so a\n forbidden tool call is blocked regardless of what the LLM emits. `ToolDef` gains `required_clearance` /\n `action_class`. Threaded through `AitherAgent.chat`; `auth=None` is fully backward-compatible.\n- `adk/channel_auth.py`: Telegram webhook-secret + CEO-id checks and HMAC-signed approvals.\n\n## [2.6.4] - 2026-06-05\n\n### Fixes\n- **Loop-guard nudges corrupted the message history → DeepSeek 400 mid-run.**\n When the ReAct loop emitted multiple tool calls in one assistant turn and the\n loop guard fired a WARN/BLOCK/CIRCUIT_BREAK on one of them, it spliced a\n standalone `system` nudge message *between* the tool results. DeepSeek (and\n strict vLLM chat templates) require an assistant `tool_calls` message to be\n followed by exactly one `tool` result per `tool_call_id`, contiguously — so the\n interleaved message produced `400 … insufficient tool messages following\n tool_calls message` and aborted the turn (this is reliably hit after crossing\n the 15-call circuit-break threshold, where every subsequent call WARNs).\n Per-call guidance is now folded into that call's own `tool` result, and any\n standalone steering is deferred to a single message AFTER the whole batch —\n preserving the `tool_calls`↔`tool_results` pairing every backend requires.\n Regression test: `deep-research-agent/tests/test_msg_structure.py`\n (loopguard-flood scenario).\n\n## [2.6.3] - 2026-06-04\n\n### Fixes\n- **Transient provider errors killed a run on the first blip.** A single\n `502 Bad Gateway` / `503` / `529 overloaded` / `429` from the upstream API\n aborted the whole request — the Anthropic provider had **no retry at all**, and\n the OpenAI-compatible provider only retried `429`. Both now retry transient\n statuses (`408/429/500/502/503/504/529`) with `Retry-After`-aware exponential\n backoff (3 attempts), and surface the provider's body if all retries are\n exhausted. Non-transient `4xx` (e.g. `400`) still fails fast. Applies to\n `chat` and `chat_stream` on both providers.\n\n## [2.6.2] - 2026-06-04\n\n### Fixes\n- **DeepSeek (and strict OpenAI-compatible backends) 400'd on long tool\n conversations.** The ReAct loop legitimately injects mid-conversation\n `system` steering messages (the \"[DIMINISHING RETURNS]\" hint after several\n low-output tool iterations, loop-guard nudges). OpenAI tolerates a non-leading\n `system` message; DeepSeek rejects it with a 400. Long research runs\n (many `fetch_url` calls) reliably tripped this. Fix: non-leading `system`\n messages are demoted to `user` at the OpenAI-compatible provider boundary\n (`_demote_nonleading_system`) — steering intent preserved, payload valid on\n every backend; leading system prompt(s) untouched.\n- **Opaque HTTP errors.** `resp.raise_for_status()` discarded the provider's\n response body, so a 400/422 surfaced as a bare `Client error '400 Bad\n Request'` with no cause. `_ensure_ok` now includes the provider's actual\n message (e.g. `context_length_exceeded`, `Invalid 'messages'`) in the raised\n error, for both `chat` and `chat_stream`.\n\n## [2.6.1] - 2026-06-04\n\n### Fixes\n- **`MCPServer.mount()` returned HTTP 422 for every `/mcp` call.** Under\n `from __future__ import annotations` the route handler's `request: Request`\n annotation is a string that FastAPI resolves against the *module* globals, but\n `Request` was imported locally inside `mount()` — so FastAPI couldn't resolve\n it and treated `request` as a required query param. The MCP server endpoint\n (`initialize`, `tools/list`, `tools/call`) was unusable when mounted. FastAPI\n symbols are now imported at module level (guarded; the `[node]` extra still\n gates actual use). Any agent's tools can now be exposed as a working MCP\n server with `MCPServer(...).mount(app)`.\n\n## [2.6.0] - 2026-06-04\n\n### Streaming + scaffolding\n- **`AitherAgent.stream_chat`** — reliable streaming sibling of `chat()`: native\n tool-calling loop (concludes on its own, unlike the text-protocol `stream_react`)\n that emits live `tool`/`tool_result` events and streams the answer as `token`\n events. `token_delay` paces tokens for a typing effect.\n- **`adk new <template>`** — scaffold a full, runnable template app. Ships the\n `deep-research` template (a cited web-research agent: pack + server + web UI):\n `adk new deep-research`.\n\n### Fixes\n- **BYO-key no longer community-capped.** The free-tier monthly token cap now applies\n only to the metered Aitherium gateway backend, never to a user's own provider key\n (Anthropic/OpenAI/DeepSeek/Ollama/vLLM). Previously it could brick a self-hosted\n agent after ~100k tokens on the user's own key.\n- **Stronger, grounded memory recall.** `chat()` now injects the top-6 knowledge-graph\n hits (untruncated) with a hard no-fabrication instruction — was top-3 truncated to\n 200 chars, which surfaced partial facts and let weaker models invent the rest.\n- **Tool-arg coercion.** `ToolRegistry.execute` coerces LLM-supplied args toward each\n parameter's annotated type, so a tool no longer crashes when a model passes a list\n or string where an int/float/bool was expected.\n\n### Docs\n- New `docs/AGENT_DEV_GUIDE.md` — the golden path for building agents/packs.\n\n## [2.5.0] - 2026-06-04\n\n### Continual learning — agents that learn from runs and reuse memory\n\nAgents now improve the second time they do a task instead of starting from scratch.\n\n### Added\n- **Recall-before**: `AitherAgent.chat()` searches the local `SkillStore` and injects the\n top matching learned skills as a system block, so the model reuses proven procedures.\n- **Learn-after**: `_learn_after()` runs after each response — reinforces (`success_count++`)\n any skills that were recalled+reused, and extracts + saves a NEW skill from successful\n multi-tool runs via `SkillExtractor`. Wired at both `chat()` return paths.\n- Gated by `AITHER_SKILLS` (default on). The previously-unused `SkillStore`/`SkillExtractor`\n are now connected to the run loop.\n\n### Notes\n- Pairs with the platform-side LearnedProcedure substrate (recipes → scored procedures →\n skills/tools/A2A/packs → MCTS priors → evolution). The SDK shares memory via the Spirit\n bridge when configured.\n\n## [2.0.0] - 2026-06-02\n\n### Open-core boundary (BREAKING)\n\nThis release draws a single, enforced free/paid line. The free tier stays\ngenuinely useful (real agent, typed + graph + code memory, ReAct, ~essential\ntools); paid tiers unlock the *scale* capabilities via portal.aitherium.com.\n\n### Added\n- **`adk/licensing.py`** — the entitlement keystone. Resolves a tier\n (COMMUNITY by default), verifies optional portal-signed `~/.aither/license.json`\n (Ed25519), and answers `can_use_fleet/channels/cron/swarm/auto_neurons`,\n `is_agent_licensed`, `max_effort`, etc. **Fail-closed**: no/invalid/expired\n license → free tier; an unsigned license never grants premium.\n `AITHER_TENANT_SLUG=aitherium` → unrestricted INTERNAL tier.\n- **Moat guard** (`scripts/check_moat_boundary.py`) wired into the publish\n workflow — blocks any wheel that leaks the moat or drops the keystone.\n- Tests: `tests/test_licensing.py`, `tests/test_moat_boundary.py`.\n\n### Changed (gating — fail-closed)\n- Free tier effort is capped at 3 (reasoning effort 7–10 needs Professional).\n- Monthly token budget hard-block on the free tier (no silent overspend).\n- Gated behind a paid tier: fleet mode (`FleetConfig`), cross-agent delegation\n (`AgentForge.delegate`), channel adapters, cron scheduler, proactive\n auto-neurons, and `swarm`/`swarm_code` dispatch.\n\n### Removed\n- **`adk/nanogpt.py`** (on-device training IP) removed from the published SDK\n and the public repo; relocated to the internal AitherOS moat. Excluded from\n the wheel as defense-in-depth.\n- Pre-2.0 releases shipped this IP and ungated capabilities and are being\n yanked (PyPI) / removed (GitHub releases). See `scripts/yank_leaky_releases.py`\n and `scripts/purge_public_leaks.sh`.\n\n## [1.20.0] - 2026-05-27\n\n### Fixed\n- **Pack discovery path mismatch** — `_find_local_packs()` now scans both\n `~/.aither/packs/` and `~/.aitheros/packs/`, so packs installed via\n `adk pack install` are discovered by `adk-workspace`.\n\n### Added\n- **Auto-register on pack install** — `adk pack install` now auto-registers\n with the portal if the pack contains an `agent.yaml` with a `portal` section.\n One command does download + extract + register.\n- **Flux event on agent registration** — `developer_portal.py` emits\n `agent.registered` Flux event so the portal fleet UI updates in real-time.\n\n## [1.15.0] - 2026-05-26\n\n### Added\n- **Cloud memory** — `adk quickstart --cloud` now tests gateway memory and\n auto-configures Spirit URL so memories persist across devices via\n `gateway.aitherium.com/v1/memory/teach` and `/v1/memory/recall`.\n- **Config→env export for cloud mode** — `Config.from_env()` now exports\n `AITHER_CLOUD_MODE`, `AITHER_SPIRIT_URL`, and spirit path env vars from\n saved config so Memory and other modules pick them up without needing\n explicit env vars set by the user.\n- **Cloud Quick Start section** in README — 3-command setup for users who\n just want a Claude/GPT-4/DeepSeek API key and the full agent harness.\n- **`--memory` profile for node deployment** — `adk deploy node --memory`\n adds Spirit (8087) + WorkingMemory (8101) containers for persistent\n vector memory. Without this, agents use local SQLite only.\n- **Configurable vLLM in node compose** — `AITHER_VLLM_MODEL`,\n `AITHER_VLLM_GPU_UTIL`, `AITHER_VLLM_CTX_LEN`, `AITHER_VLLM_QUANT_ARGS`\n env vars let `adk setup` write GPU-detected tier settings to `.env`\n and have the node compose pick them up (TQ4 for small GPUs, BNB for large).\n\n### Fixed\n- **Memory cloud routing** — `Memory._spirit_teach()` and `_spirit_recall()`\n now use configurable paths and auth headers for gateway proxy. Previously\n cloud-only users silently lost all Spirit memory calls.\n\n## [1.9.0] - 2026-05-24\n\n### Added\n- **Composable ToolPack system** — agents can load tool packs from\n `.toolpack.yaml` directories. Drop a pack in `~/.aitheros/packs/` or\n set `AITHER_PACKS_DIR`, and any agent discovers it automatically.\n- **`tools.packs` in agent.yaml** — list tool pack IDs to auto-load\n (e.g. `packs: [crm-tools, calendar-pro]`).\n- **`register_tool_packs(agent, pack_ids, packs_dir)`** in `builtin_tools` —\n programmatic pack loading for agents.\n- **`AITHER_TOOL_PACKS` env var** — comma-separated pack IDs loaded on\n agent init (alternative to YAML config).\n- **Unknown builtin categories resolve as pack IDs** — `tools.builtin:\n [file_io, crm-tools]` now works: `crm-tools` is looked up as a pack.\n\n## [1.8.0] - 2026-05-24\n\n### Removed\n- **11 dead modules** (3,023 lines) — `agent_deploy`, `cloud_deploy`, `create`,\n `phonehome`, `sdk_bridge`, `sovereign`, `telemetry`, `telemetry_config`,\n `error_reporter`, `anonymizer`, `package`. None were imported anywhere.\n- **29 ghost exports** from `__init__.py` — `secrets`, `otel`, `fs_sandbox`,\n and `vector_memory` modules were listed in `__all__` and `__getattr__` but\n the backing files never existed. Accessing them raised `ModuleNotFoundError`.\n- **Stale dist/ artifacts** — removed old wheels/tarballs (v0.4.0–v1.4.1, 11 MB).\n- **Orphan test scripts** — `test_phase1.py` and `test_phase23.py` (one-off\n validation scripts not part of the pytest suite).\n\n### Fixed\n- **Version mismatch** — `__init__.__version__` now matches `pyproject.toml`\n (was stuck at 1.6.0).\n\n## [1.5.0] - 2026-05-23\n\n### Added\n- **`--sovereign` flag for `adk deploy node`** — registers the node with the\n Aitherium hub via federation after deployment. Saves federation credentials\n to `~/.aither/.env.federation`. Accepts `--hub` (custom hub URL) and\n `--tenant` (tenant slug) options. Non-fatal: node runs standalone if\n registration fails.\n- **Federation → fleet cross-post** (Genesis) — sovereign nodes that register\n or heartbeat via `/federation/register` and `/federation/heartbeat` now\n automatically appear in the fleet dashboard (`/fleet/deployments`) with\n `node_type: \"sovereign\"` and live status/metrics.\n- **\"Deploy Sovereign Node\" section on Connect page** (AitherVeil) — Section 8\n with ADK deploy commands, fleet dashboard links, and provision page links.\n- **`deploy-node` action in tunnel proxy** (AitherVeil) — Connect page and\n external callers can now request bootstrap scripts via\n `POST /api/tunnel/devworkspace?action=deploy-node`.\n\n### Fixed\n- **Fleet provision page** — API URL corrected from `/api/fleet/provision` to\n `/api/bridge/genesis/fleet/provision` (matches bridge proxy pattern).\n- **Fleet detail page** — API URL corrected from `/api/fleet/deployments/` to\n `/api/bridge/genesis/fleet/deployments/` (same fix).\n- **Dockerfile** — entry point corrected from `aither-serve` to `adk-serve`\n (matches `pyproject.toml` script registration). Version label updated.\n\n## [1.4.1] - 2026-05-22\n\n### Fixed\n- **macOS executable builds** — dropped `--target-arch universal2` from\n `packaging/build_executable.py`. PyPI wheels for `pydantic_core` and other\n native deps are arch-specific, not fat binaries, so universal2 always failed\n with `IncompatibleBinaryArchError`. Now builds for the runner's native arch\n (`aither-macos-arm64`).\n- **Release workflow strategy** — added `fail-fast: false` so a single\n executable build failure no longer cancels the other OS jobs.\n- **PyPI publish** — added `skip-existing: true` so re-runs of the same\n version don't fail when the artifact already exists.\n- **GitHub Release job** — now runs whenever `pypi` succeeds even if some\n executable matrix entries fail, so the release page is always created with\n whatever binaries did build successfully.\n\n## [1.4.0] - 2026-05-22\n\n### Added\n- **Sovereign template now ships with `agent_core` 5-tier memory** — generated\n backends include the canonical `agent_core/` package (working → episodic →\n semantic → procedural → identity) plus `agent_memory.py` helper, vendored\n from `portal-kit-backend`.\n- `aiosqlite>=0.20.0` added to sovereign template `requirements.txt` for\n local agent_core SQLite storage.\n- Document tombstoning: `evict_cached_conversations_referencing` is wired\n through document-delete handlers in consumer apps so memories citing\n deleted documents are blocked from recall.\n- New tests: 13 smoke tests for `portal-kit-backend/agent_core` covering\n promotion thresholds, decay rates, store roundtrip, tombstone recall\n blocking, circuit breaker state machine, and chat-turn recording.\n\n### Changed\n- `scripts/vendor_agent_core.py` now syncs canonical `agent_core` to 4\n targets: WorkspaceRuntime, ADK sovereign template, and the GargBot +\n Chelle consumer mirrors (previously only 2).\n\n## [1.2.1] - 2026-05-21\n\n### Added\n- `adk train` command group — 6 subcommands: status, launch, logs, cancel, runs, register-gpu\n- `/slash-commands` endpoint — ADK server auto-exposes all 35 CLI commands as structured JSON manifest for AitherShell\n- `/cli/execute` endpoint — AitherShell can run any CLI command through the server\n- `build_command_manifest()` — introspects full argparse tree (commands, args, choices, subcommands)\n- `_register_commands()` extracted from main() for shared parser construction\n\n### Changed\n- `_get_genesis_url()` now probes: Genesis → ADK server → Aitherium cloud gateway (works without Genesis)\n\n## [1.2.0] - 2026-05-21\n\n### Added\n- **Runtime backend switching** — `agent.switch_backend(\"deepseek\")` and `agent.llm.switch_backend()` change provider without recreating agent\n- **Hybrid reasoning** — `agent.set_reasoning_backend(\"anthropic\")` routes effort 7+ to cloud API while keeping local orchestrator\n- **First-class DeepSeek provider** — `deepseek` in effort model table with `deepseek-chat` and `deepseek-reasoner`\n- **TQ4 quantization tiers** — `nano` (6GB, TurboQuant 4-bit), `standard-tq4` (12GB, both models TQ4), `hybrid-tq4` (6GB + cloud reasoning)\n- **`adk backend` CLI** — `list`, `set`, `set-reasoning`, `test` subcommands\n- **`adk quickstart`** — unified first-run wizard (setup + auth + shell)\n- **`adk tools`** — list local + MCP tools with tier markers\n- **`adk backup`** — tarball export of `~/.aither/`\n- **`adk ingest`** — feed docs/files into knowledge graph\n- **`adk setup nemotron`** shortcut — alias for `--tier lite`\n- **`--reasoning-api`** flag in setup — `anthropic`, `openai`, `deepseek`, `gateway`\n- **`--dgx-spark URL`** flag — remote vLLM endpoint configuration\n- **DGX Spark auto-detection** — scans `spark.local`, `192.168.0.33`, `AITHER_DGX_URL` env\n- **Post-setup smoke test** — sends real inference request after container startup\n- **Schema migration system** — version table + migration runner in `memory.py` and `graph_memory.py`\n- **Configurable vLLM port scan** — `AITHER_VLLM_PORTS` env var for custom ports\n- **`aithershell` entry point** — alias for `adk` in pyproject.toml\n- Shell auto-downloads binary on first `adk shell` (no `--install` needed)\n- Shell pre-flight check warns if no backend detected\n- Shell passes full config via env vars (API key, tenant, inference URL)\n- DGX/Remote and Cloud API checks added to `adk doctor`\n- Config fields: `reasoning_backend`, `reasoning_api_key`, `deepseek_api_key`, `dgx_url`, `vllm_extra_ports`\n\n### Fixed\n- `test_aeon_chat_basic` timeout — mock scope was exiting before request\n- `adk doctor` check_disk timeout — cap rglob at 5000 files\n\n## [1.1.7] - 2026-05-19\n\n### Added\n- Remote agent pipeline — `adk onboard` → fleet dispatch → remote inference\n- Agent-to-agent federation with mesh relay\n- `adk connect` for desktop mesh enrollment\n\n## [1.1.6] - 2026-05-19\n\n### Fixed\n- Tool call execution recovery across all backends\n- Hermes tool call fallback parsing for vLLM\n\n## [1.1.5] - 2026-05-19\n\n### Fixed\n- Tool call execution + Hermes fallback parsing improvements\n\n## [1.1.0] - 2026-05-18\n\n### Added\n- Multi-channel gateway (Telegram, Discord, Slack, webhook)\n- Aeon group chat with 7 presets\n- Skills auto-extraction from multi-step sessions\n- MCP stdio server for Claude Code integration\n- `adk gateway`, `adk cron`, `adk skills`, `adk soul` commands\n- SOUL.md import/export for portable agent identity\n- Elysium cloud device-flow authentication\n\n## [1.0.0] - 2026-05-18\n\n### Breaking\n- Package consolidated — `aither_adk` namespace replaced by `adk` + `adk.platform`\n- 93 duplicate files removed from `AitherOS/packages/aither_adk/`\n\n### Added\n- Standalone core primitives (`adk.core`: Agent, Tool, Memory, ModelBackend, Capability, Trace)\n- `adk.platform` sub-package merging all platform toolkit modules\n- Compatibility shims for legacy `from aither_adk.*` imports\n\n### Changed\n- Version synced across pyproject.toml, `adk/__init__.py`, `adk/platform/__init__.py`, npm package\n- Classifier promoted from Alpha to Beta\n\n## [0.16.0] - 2026-04-16\n\n### Swarm Coding Engine & Repowise Integration\n- **`repowise_search` tool** — Semantic + keyword hybrid code search via Repowise, with ripgrep fallback\n- **`swarm_code` tool** — Dispatch complex tasks to 11-agent swarm pipeline (ARCHITECT->SWARM->REVIEW->JUDGE)\n- **`agent.swarm()`** — Async convenience method with configurable mode, effort, timeout\n- **`agent.code_search()`** — Async convenience method returning structured results\n- New tool categories: `repowise` and `swarm` in TOOL_CATEGORIES\n- `repowise_search` and `swarm_code` added to `__init__.py` exports with lazy loading\n- `IDENTITY_DEFAULTS` updated: repowise for code-focused agents, swarm for orchestration agents\n- Standalone graceful degradation: repowise falls back to ripgrep, swarm returns structured error\n\n## [0.13.0] - 2026-04-02\n\n### Graph Faculties — Local Knowledge for Every Agent\n- **CodeGraph** (2,799 lines) — Full Python AST indexer with call graph, keyword/semantic/hybrid query, embedding matrix cache, incremental re-indexing, multi-hop chain expansion\n- **MemoryGraph** (1,339 lines) — Graph-based persistent agent memory with 10 edge types, hybrid query (keyword + semantic + graph expansion), multi-hop recall, pickle persistence\n- **EmbeddingProvider** — 4-backend fallback chain: sentence-transformers (GPU/CPU) -> Ollama -> Elysium cloud -> feature hashing (zero deps)\n- **BaseFacultyGraph** — Abstract base with HMAC-SHA256 validated pickle persistence\n\n### Agent Integration\n- `agent.set_code_graph(cg)` — auto-registers `code_search` + `code_context` tools\n- `agent.set_memory_graph(mg)` — auto-registers `remember` + `recall` + `memory_stats` tools\n- Both graphs inject context into LLM prompts automatically during chat\n\n### Zero-Config Onboarding\n- `adk start` / `adk` (no args) — auto-detect project, index code, detect LLM, persistent memory, interactive chat\n- Works for any directory: Python codebases, doc folders, mixed workspaces\n- Auto-detects LLM: Ollama -> vLLM -> Elysium -> OpenAI -> Anthropic\n- Per-project persistent memory in `~/.aither/memory/<project>`\n- `adk index <path>` — standalone indexing with progress bar and stats\n\n### MCP Gateway\n- `POST /v1/embeddings` — OpenAI-compatible embedding proxy to vLLM-embeddings:8209\n- Available to all tiers (embeddings are free)\n- ADK EmbeddingProvider uses this as Elysium cloud fallback\n\n### Optional Dependencies\n- `pip install aither-adk[graphs]` — numpy for 10x cosine similarity speedup\n- `pip install aither-adk[embedding]` — sentence-transformers + torch for local GPU embeddings\n- `pip install aither-adk` alone — graphs work with feature hashing (zero deps)\n\n## [0.12.0] - 2026-04-01\n\n### Bootstrap & Service Discovery\n- Version handshake -- ADK checks major.minor compatibility with Genesis/Node\n- Background reconnect loop -- ServiceBridge re-probes every 30s in standalone mode\n- Port 8080/8090 documented -- MCP vs OpenAI-compat clearly separated\n- Genesis URL configurable via GENESIS_URL env var (was hardcoded)\n- Standalone mode warning -- visible stderr alert when AitherOS not detected\n- Auto-reconnect on startup when services come online\n\n### Elysium Cloud Inference\n- Unified gateway URL -- gateway.aitherium.com handles auth + billing + inference\n- Streaming inference -- SSE passthrough for /v1/chat/completions with stream=true\n- Auth proxy routes -- /v1/auth/register, /v1/auth/login, /v1/auth/me\n- Billing proxy -- /v1/billing/balance through gateway\n- AitherConnect Elysium fallback -- cloud inference when local Genesis is down\n- AitherDesktop Elysium fallback -- third-tier chat fallback after Node\n\n### Infrastructure\n- /discovery endpoint on Genesis -- unified service URLs/versions/health\n- /api/config/services on Veil -- runtime port config for client-side JS\n- Veil healthcheck gates on Genesis -- unhealthy when backend is down\n- Desktop crash detection 90s->60s (threshold 3->2)\n\n## [0.11.0] - 2026-04-01\n\n### Agent Execution Quality (Claude Code Parity)\n- Raise loop guard block threshold from 3 to 4 -- agents get more room for iterative search\n- Soft synthesis nudge for effort >= 4 (no tool stripping, trust the model)\n- max_output_tokens escalation -- retry up to 3x when response is truncated\n- Tool result pairing guarantee -- synthetic error for orphaned tool_use blocks\n- Micro-compaction of old tool results -- save context tokens on long sessions\n- First-turn tool forcing only for effort >= 6 (trust model for lower effort)\n- Diminishing returns detection -- nudge agent when 3+ turns produce < 500 tokens\n- Message normalization -- merge consecutive same-role messages, strip empties\n- LLM retry with exponential backoff (5 retries, 500ms-16s, jitter)\n\n## [0.9.0] - 2026-03-16\n\nThe \"connected world\" release. Cross-platform identity pairing, voice capabilities, and multi-channel integration.\n\n### Added\n- **Pairing**: Cross-platform identity linking (`adk/pairing.py`)\n - `PairingManager` — SQLite-backed identity linking with 6-char pairing codes\n - Link users across Telegram, Discord, Slack, WhatsApp with 10min TTL codes\n - Canonical session IDs for cross-channel conversation continuity\n - `get_session_id()` returns \"user-{id}\" for paired users\n- **Voice**: Speech-to-text and text-to-speech client (`adk/voice.py`)\n - `VoiceClient` — async STT/TTS/emotion via AitherVoice service\n - Convenience functions: `hear()`, `say()`, `feel()`\n - 6 voice options: alloy, echo, fable, nova, onyx, shimmer\n - Emotion detection with intensity scoring\n- New exports: `PairingManager`, `PairingResult`, `PlatformIdentity`, `VoiceClient`, `TranscriptionResult`, `SynthesisResult`, `EmotionResult`\n\n### Changed\n- `__init__.py` exports expanded with pairing and voice symbols\n\n## [0.6.0] - 2026-03-13\n\nThe \"group mind\" release. Multi-agent group chat, creative tools, and Iris identity.\n\n### Added\n- **Aeon**: Multi-agent group chat engine (`adk/aeon.py`)\n - `AeonSession` — persistent group chat with parallel agent execution\n - 7 presets: balanced, creative, technical, security, minimal, duo_code, research\n - Orchestrator synthesis: Aither summarizes all agent responses\n - Serial execution for Ollama, parallel for vLLM/cloud\n - ConversationStore persistence with `type: \"aeon\"` metadata\n - `group_chat()` one-shot convenience function\n- `aither aeon` CLI command — interactive terminal group chat with color-coded agents\n - `-p/--preset`, `-a/--agents`, `-r/--rounds`, `--no-synthesize` flags\n - `reset` and `quit` commands\n- Server endpoints: `POST /aeon/chat`, `GET /aeon/presets`, `GET /aeon/sessions/{id}`\n- Creative tools in builtin_tools: `image_generate`, `image_refine`, `image_search`, `video_generate`\n- Iris agent identity with visual generation capabilities\n- 48 Aeon tests (data models, presets, chat, context, persistence, server, exports)\n\n### Changed\n- `__init__.py` exports: `AeonSession`, `AeonResponse`, `AeonMessage`, `group_chat`, `AEON_PRESETS`\n\n## [0.5.0] - 2026-03-13\n\nThe \"tenant-ready\" release. Multi-tenant admin, permission grants, safety profiles, and a full setup wizard.\n\n### Added\n- `aither setup` interactive setup wizard with hardware detection, model selection, identity config\n- Strata storage backend: SQLite WAL persistence for conversations, memories, knowledge graphs\n- CLI test runner: `aither test` with auto-discovery and parallel execution\n- Permission grants system for MCP tool access control\n- MCP account management tools\n- LLM provider auto-detection for Ollama and vLLM endpoints\n- Apache-2.0 LICENSE file\n- Elysium desktop sync module\n- Comprehensive CLI test suite (493 tests)\n- Strata storage test suite (972 tests)\n- LLM provider tests (82 tests)\n\n### Changed\n- CLI expanded: `aither setup`, `aither test`, `aither bugreport`, `aither doctor`\n- Strata module rewritten as full local-first storage engine\n- Server startup includes Strata initialization\n- README rewritten with clearer quickstart and architecture docs\n\n### Fixed\n- Dead documentation links\n- Python 3.11 compatibility issues\n- Ruff per-file-ignores configuration\n- Identity provisioning edge cases\n- Docker node-gyp native dependency builds (python3 + build tools)\n\n## [0.4.0] - 2026-03-13\n\nThe \"own your AI\" release. Self-hosted agent OS for people who don't want their data on someone else's servers.\n\n### Added\n- Muse agent identity (creative/artistic generation)\n- Port 8120 to vLLM scan for ExoNodes discovery\n- Public roadmap with milestone-based porting schedule\n- Competitive positioning: self-hosted alternative to cloud-locked AI appliances\n\n### Changed\n- LLM router: compute-aware effort routing (replaces effort-level context gating)\n- README rewritten for sovereignty-first messaging\n- Package description updated\n- Promoted from alpha to stable release\n\n### Fixed\n- Version string consistency between pyproject.toml and __init__.py\n- vLLM port scanning now includes port 8120 (ExoNodes)\n\n## [0.3.1] - 2026-03-09\n\n### Added\n- GraphMemory: SQLite knowledge graph with Ollama embeddings and hybrid search\n- NeuronPool: auto-fire pre-LLM data gathering agents\n- NanoGPT: pure Python char-level transformer with LoRA fine-tuning\n- Safety gates: IntakeGuard (input), LoopGuard (recursion), Sandbox (code exec)\n- Event system: EventEmitter with chat/tool/forge event types\n- Builtin tools: identity-based tool selection\n- ServiceBridge: auto-discovery of AitherOS services\n- Streaming: chat_stream with safety gate integration\n- Auth middleware: Bearer token authentication\n- CLI: `aither init` and `aither serve` commands\n\n### Changed\n- Wired 5 previously disconnected modules into agent loop\n- 522 total passing tests (up from 85)\n\n## [0.3.0] - 2026-03-07\n\n### Added\n- Clean-room agent development kit\n- Multi-backend LLM providers (Ollama, OpenAI, Anthropic)\n- AitherAgent class with @tool decorator\n- SQLite conversation memory\n- OpenAI-compatible server (`aither-serve`)\n- 16 agent identities as package data\n- MCP bridge to mcp.aitherium.com\n- Privacy-centric opt-in telemetry\n- Bug reporting CLI and API\n- FastAPI server with OpenAI-compatible endpoints\n- Hardware auto-detection (5 tiers, 11 profiles)\n- Fleet orchestration and multi-agent coordination\n- A2A mesh protocol\n- Federation protocol for cross-instance agent dispatch\n\n### Initial release\n- 85 passing tests\n- Apache-2.0 license\n"}, "8ef6e9ca1b73b3f423dbe84fd13b4751e932621c": {"t": 1785748153.2686954, "n": 101523, "h": "f60a2557564b2b91", "c": "# Changelog\n\nAll notable changes to aither-adk will be documented in this file.\n\n## [2.44.0] - 2026-07-27\n\n### Fixed — `adk enroll` silently registered devices into the WRONG TENANT\n\n- **`adk enroll` succeeded without an identity and bound the device to the tenant\n `\"personal\"` instead of the caller's workspace.** `_extract_tenant_slug()`\n (`fleet_enroll.py:217`) falls back to `\"personal\"` when `~/.aither/auth.json` is\n absent, so enrolling a fresh machine reported success while registering it\n somewhere the owner cannot see. Nothing failed, nothing warned, and the endpoint\n simply never appeared in their workspace — the fail-open shape where the\n operation reports success and does the wrong thing.\n\n This bites hardest on unattended installs (phones, headless VMs, cloud images),\n which are exactly the machines nobody is watching closely enough to notice.\n\n `cmd_enroll` now **refuses** when no identity is present, and names the two ways\n to supply one:\n\n adk login # browser flow\n adk login --api-key <key> # headless / phone / VM\n\n Verified by running it with an empty `HOME`: refuses, **exit 1**. (`AITHER_NODE_TOKEN`\n is accepted as an identity, so token-based fleet enrolment is unaffected.)\n\n Found while building a one-command self-host installer for Android's Linux\n terminal, by running the installer rather than reasoning about it.\n\n## [2.39.0] - 2026-07-25\n\n### Fixed — containerized agents were unreachable\n\n- **`docker` runtime: added `-i` to `docker run`.** Without it Docker does not\n attach the container's stdin, so *every* containerized stdio agent (acp, mcp)\n silently never received a request. Measured against a real container: no `-i` →\n the child read nothing; with `-i` → the request round-trips. A real customer\n pack *is* a container, so this runtime was effectively unusable.\n- **`runtime.cmd` is now `shlex`-split** for the `docker` and `node` runtimes.\n `cmd: \"python -u server.py\"` was passed as a single argv element, so docker\n tried to exec a binary literally named `python -u server.py`.\n- `Supervisor._build_command()` extracted from `spawn()` so argv is testable\n without launching a process — the missing `-i` survived precisely because it\n wasn't.\n- Env is deliberately **not** forwarded as `-e KEY=value`: that puts credentials\n in argv, readable by any local `ps` / `docker inspect`.\n\n### Fixed — CodeActLoop `cell_timeout` did not bound CPU-bound cells\n\n- `asyncio.wait_for` cannot preempt a coroutine that never yields, so\n `while True: pass` was never interrupted and hung the agent indefinitely. Now\n also bounded by a line-level trace deadline (`CellTimeout`), scoped to the\n cell's own frames so a cell that awaits cannot fire the deadline inside\n unrelated coroutines.\n- The `CodeActLoop` docstring now states plainly that **it is not a sandbox**, and\n names what a cell can actually do (`import os`, `subprocess`, `open(..., \"w\")`).\n Do not run cells from an untrusted source.\n\n### Added — real containment for CodeAct cells\n\n- **`CodeActLoop(executor=DockerCellExecutor())`** runs each cell in a container\n with `--network none`, a read-only root filesystem plus a noexec/nosuid tmpfs,\n `--cap-drop ALL`, `no-new-privileges`, memory and pid caps, a non-root user, and\n `python -I`. The escapes that succeed in-process — writing a host file, network\n egress, running as root — all fail there, and an infinite loop's container is\n **killed** rather than merely interrupted.\n- `return_result()` crosses the process boundary via a stdout sentinel and is\n deliberately **not** `eval`'d back into the host.\n- Trade-off stated plainly: an isolated cell is a fresh process, so there is no\n live host namespace and no cross-cell state. Containment *and* a live namespace\n together need fork-from-warm snapshotting (`adk/forkd_client.py`'s Firecracker\n target), which requires Linux ≥ 5.7 + KVM.\n- The in-process default is unchanged. Do not point CodeAct at untrusted input\n without `executor=`.\n\n### Fixed — TLS and test-suite integrity\n\n- **`adk/cli.py` sent an `Authorization: Bearer` API key over TLS with\n verification disabled**, exposing it to a machine-in-the-middle. Now routed\n through `adk._tls.tls_verify()`.\n- The `verify=False` ratchet regex-scanned raw lines, so docstrings that merely\n *named* the antipattern were reported as violations — noise that hid the real\n hole above. It now walks the AST for actual keyword arguments.\n- Five `test_inference_proxy` failures were stale assertions pinning the\n pre-MicroScheduler routing and a 5-model roster (it is 6 since `aither-bonsai`).\n Assertions now derive from the roster and encode the tier-access rule, so a\n future model addition doesn't break them.\n\n### Fixed — MCP stdio hung silently on Windows\n\n- `adk/mcp_stdio.py` and `adk/shell/mcp_bridge.py` attached stdin with\n `loop.connect_read_pipe`, which fails inside the Proactor loop's read handler\n such that the read future never resolves — the server didn't crash, it just\n never answered. Both now use the new shared `adk.stdio_compat`\n (`ThreadStdinReader` / `ThreadStdoutWriter`), which `acp_server.py` also uses.\n\n### Added — per-install pack credentials are now actually used\n\n- `adk/pack_credentials.py` was orphaned: mint/revoke were never called, so pack\n installs kept using the shared credential the module was written to replace.\n Both install paths now mint, and a reinstall revokes the outgoing install's\n credential before its metadata becomes unreachable.\n- **New `/packs uninstall <pack-id>`** (aliases `remove`, `rm`) — revokes the\n per-install credential first, then removes the pack. Previously the only revoke\n path was a reinstall, so deleting a pack any other way left a live,\n unrevocable credential. The target is confined to the pack root.\n\n### Fixed — packaging\n\n- The brew formula's own `sha256` was left as `PLACEHOLDER_SHA256` on every\n release, so `brew install` could never verify the download.\n `sync_versions.py` now fills it from PyPI and `--check` **fails** when a\n published version still carries a placeholder or a stale digest.\n- `sync_brew` rewrote *every* `url` in the formula, repointing the `httpx`,\n `pyyaml`, `fastapi` and `uvicorn` `resource` blocks at the aither-adk tarball.\n Fixed with `count=1`; the resource digests are now real.\n- `adk/__init__.py`'s source-checkout fallback `__version__` had been stale since\n 2.32.0 because the canonical release path never ran `sync_versions.py`. That\n check now runs in `sync-adk.yml`.\n\n## [2.38.1] - 2026-07-24\n\nRelease plumbing only — 2.38.0 was tagged before the version manifests\n(npm/brew/winget) were synced, so its publish gate failed and it never reached\nPyPI. 2.38.1 is the same code with the manifests in sync. See 2.38.0 below for\nwhat actually changed.\n\n## [2.38.0] - 2026-07-24 (never published — superseded by 2.38.1)\n\n### Fixed — `adk join` could only ever have worked for us\n\n- **`adk join` defaulted identity to `https://localhost:8115`** — a fleet-INTERNAL\n address. `adk join` is the one command a stranger runs on their own GPU box, so on\n any machine not already running AitherOS locally, step 1 (the GitHub device flow)\n hit a refused connection, and so did node registration and the mesh-key issue. The\n conductor default was already public, which is exactly why this went unnoticed.\n Identity now defaults to `https://idp.aitherium.com`.\n\n This fix landed *after* the 2.37.0 tag, so **2.37.0 and every earlier release on\n PyPI still ship the broken default** — 2.38.0 is the first release a new\n contributor can actually run. Verified live from a non-fleet machine (DGX):\n `POST /auth/github/device/start` → HTTP 200 with a real `user_code` and\n `verification_uri`.\n\n- **Upgrade check compared versions as strings** — `latest != current` reported a\n downgrade as an available upgrade. Now uses `packaging.version` with a numeric-tuple\n fallback.\n\n## [2.37.0] - 2026-07-24\n\n### Added — Agent Onboarding Fabric: bring your own agent, or run a managed one\n\nOnboard an agent built on **any** framework, or let Aitherium run it for you.\n\n- **ACP (Agent Client Protocol), both directions** — `ACPClient` drives any ACP agent;\n `ACPServer`/`serve_stdio` exposes an adk agent to any ACP host (Zed, VS Code,\n JetBrains). Both proven live against Zed's reference `agent-client-protocol`.\n- **Universal Agent Pack** — `AgentPackManifest` + `Supervisor` describe and run an\n external agent across 6 frameworks × 6 protocols, fail-closed on anything unknown.\n- **Protocol drivers** — `get_driver()` returns an `http`, `langgraph_rest`, `a2a`, or\n `mcp` driver, so REST/graph-orchestrated agents can be *managed*, not just connected.\n- **Pack registry** — `PackRegistry.publish/browse/versions/get/yank/verify_digest`,\n SHA256-digested, semver-ordered, validation fail-closed before anything is written.\n- **Managed identity** — `ManagedAgentIdentityProvider` with a real gateway minter and a\n PROVISIONED→REGISTERED→ACTIVE→ROTATED→REVOKED lifecycle; default-deny `authorize()`.\n- **Zero-code connect templates** — `render_connect(framework, …)` emits config pinned to\n each framework's real schema.\n\n### Added — NOOA-style agent capabilities\n\n- **`CodeActLoop`** — code as action: the model writes Python cells that run against a\n persistent namespace over live objects, each cell AST-validated before execution, with\n per-cell timeouts and bounded observations.\n- **`ObjectRegistry` / `render_observation`** — pass by reference: large values become\n handles instead of flooding the context with serialized text.\n- **`EventLog` / `ContextBlocks`** — harness APIs the model itself can call to query its\n own event history and manage named context blocks in KV-cache-stable order.\n- **`has_ellipsis_body` / `@strategy`** — NOOA's `async def f(...) -> X: ...` ergonomic.\n- **`CodeValidator`** — best-effort AST validation (ported from NVIDIA's OO-Agents,\n Apache-2.0; NOTICE included). A defense-in-depth layer, not a sandbox boundary.\n- **`PredictLoop`** — single-shot strategy with structured output (`output_model=`) and\n validation retry; `Strategy` is now a first-class alias of `AgentLoop`, and both loops\n accept a per-call `model=` override.\n- **Typed I/O** — `AgentResult` validates its fields at construction and at the\n `Agent.run` boundary.\n\nAll new capabilities are exported lazily from the top-level `adk` namespace.\n\n## [2.27.0] - 2026-07-18\n\n### Added — Moonshot Kimi K3 provider (DeepSeek-equivalent cloud backend)\n\n- **New `moonshot` provider** across the LLM router: OpenAI-compatible\n `https://api.moonshot.ai/v1`, default model `kimi-k3` (131K context default, up to 1M;\n tool calls, JSON mode, structured output). Wired everywhere `deepseek` is:\n - `LLMRouter(provider=\"moonshot\")` + `_COMPAT_URLS`/`_COMPAT_MODELS`/model tiers\n (small/medium/large → `kimi-k3`)\n - Config: `moonshot_api_key` field (env `MOONSHOT_API_KEY`), saved-config load,\n provider-keys map (`provider_keys.json` key `moonshot`)\n - Auto-detect chain: tried after DeepSeek (config key, then env key)\n - Hybrid reasoning backend: `AITHER_REASONING_BACKEND=moonshot` for effort 7+\n - Reasoning tiers: `backend: moonshot` (alias `kimi`) via new `MoonshotBackend`\n - CLI: `adk backend set moonshot --api-key …`, `adk backend list` shows moonshot\n- No breaking changes; DeepSeek and all other providers unchanged.\n\n## [2.26.0] - 2026-07-16\n\n### Added — Aeon group-chat web UI pack + POST /aeon/stream SSE endpoint\n\n- **New `aeon` UI pack** (`adk/webui/packs/aeon/index.html`) — group chat for multi-agent\n discussion. Mirrors the `llamacpp` pack's auth + SSE pattern (bearer from URL fragment #k=),\n with a preset selector (balanced/creative/technical/security/minimal/duo_code/research) and\n distinct colored agent messages. Synthesis block highlighted. Agent content is HTML-escaped\n before render. Fully self-contained HTML+CSS+JS, light+dark, no external assets. (No\n temperature/max-tokens controls — Aeon runs a fixed multi-agent round and doesn't thread\n per-turn generation params, so exposing them would be a no-op.)\n- **POST /aeon/stream SSE endpoint** — accepts body {message, preset?, agents?, rounds?,\n session_id?}, emits SSE with: `session_start` (participants + orchestrator), `agent_message`\n per non-orchestrator, `synthesis` (if enabled), `error` on exception, `complete` (total tokens\n + latency). Mirrors /chat/stream structure — like every streaming endpoint here it is gated by\n the server auth middleware **when `AITHER_SERVER_API_KEY` is set** (a local `adk up` with no key\n is unauthenticated by design, same as /chat/stream).\n- **Default UI pack flipped to `llamacpp`** — `resolve_ui_pack_name()` now returns `llamacpp`\n instead of `console` when no env/config is set (new users see the clean chat, not the admin\n SPA; existing users with AITHER_AGENT_UI or saved config unaffected).\n\n### Added — swappable agent web UI packs (llama.cpp-style built-in chat)\n\n- The page an agent serves at `/` is now a **swappable UI pack**, so you can\n drop in, test, and deploy different chat frontends without touching the agent.\n Select with `adk ui set <name>` (persists to `~/.aither/config.json`) or the\n `$AITHER_AGENT_UI` env; `adk ui ls` lists packs, `adk ui path` shows what\n resolves.\n- **New built-in `llamacpp` pack** — a clean, self-contained llama.cpp-style\n chat UI (centered conversation, streaming token-by-token with a cursor, a\n Stop button, a settings drawer for temperature / max-tokens that is actually\n honored end-to-end — `/chat/stream` now forwards them to the provider —\n minimal safe markdown, light+dark). Hits the agent's own gated `/chat/stream`\n with the bearer read from the URL fragment (never sent in the URL). Built-ins\n also include `console` (the full admin SPA, still the default) and `minimal`.\n- **Drop-in custom packs**: a UI pack is just a folder with an `index.html` in\n `~/.aither/ui-packs/<name>/` (override via `$AITHER_UI_PACKS_DIR`) — no rebuild.\n- Resolution is **fail-soft**: an unknown/missing pack falls back\n console → minimal, so `/` is never blank.\n\n### Added — MCP-gateway-first platform access + mesh A2A trust\n\n- **Gateway-first platform access** (`adk/client/_gateway_mcp.py`, `GatewayMCPClient`):\n a self-hosted agent connects its MCP **client** to `mcp.aitherium.com` authenticated\n as the OWNER (device-flow / PAT / ACTA token), RBAC-scoped to that identity — so it can\n use platform tools + `get_secret` **whether or not the node runs local AitherOS\n services**. Fail-closed: no token → client disabled, the agent still runs. Wired into\n the server lifespan (`_connect_gateway_mcp`).\n- **`genesis` LLM backend**: `adk backend set genesis` points the agent brain at a local\n Genesis (`http://localhost:8001/v1`, model `workflow`) — the 5090/controller pattern\n where inference is served by the local fleet, not the cloud gateway or a raw vLLM.\n- **`adk mesh` command**: `adk mesh onboard` drives the Conductor 5-step onboard\n (`/v1/mesh/onboard` → registers this node's public key, returns the `endpoint:mesh`\n trust token); `adk mesh ls` lists peer nodes/agents (AitherMesh `/nodes` + Directory)\n with their A2A cards + invoke URLs for discovery.\n\n### Security — A2A inter-agent trust enforcement (fail-closed)\n\n- **`adk/a2a_trust.py`** (new): Ed25519 signature verification + trusted-key check\n (`AITHER_A2A_TRUSTED_KEYS`), opt-in via `AITHER_A2A_REQUIRE_TRUST` (default `false` for\n backward compatibility; `audit` logs untrusted, `true` rejects).\n- **Fixed a fail-OPEN in the inbound A2A handler** (`adk/a2a.py`): the trust check\n previously ran *only when signature headers were present*, so an unsigned request\n bypassed `AITHER_A2A_REQUIRE_TRUST` entirely. Now genuinely fail-closed — required mode\n with no/invalid signature → `403`.\n- **Sovereign template no longer scaffolds `verify=False`**: the generated\n `services/llm.py` now uses the project's own `tls_verify()` policy helper for every\n provider call instead of hardcoding disabled TLS verification.\n- **`adk/_tls.py`**: disabling verification via `AITHER_TLS_VERIFY=false` now emits a\n one-time loud warning (was silent) — the escape hatch stays, the silence goes.\n\n## [2.25.1] - 2026-07-15\n\n### Fixed — self-hosted OpenAI-compatible backends honor a custom base URL\n\n- `adk backend set llamacpp --base-url http://localhost:8090/v1 --model <id>` (and\n `vllm`/`lmstudio`, and the `AITHER_LLM_BASE_URL` env) now actually point the agent\n at YOUR server. Previously the base URL was saved but never read, so an explicit\n `--backend llamacpp` fell back to the provider's public API default (openai.com).\n New `Config.llm_base_url` field + saved-config `inference_url` wiring; the explicit\n backend path in `LLMRouter` now passes it for the local OpenAI-compatible family.\n This is what lets a self-hosted node run its OWN local model (e.g. a PrismML\n Bonsai llama.cpp) as the agent brain instead of ollama/cloud.\n\n## [2.25.0] - 2026-07-15\n\n### Added — node_bootstrap tool pack: bootstrap LLM inference on any hardware\n\n- **New bundled tool pack `node-bootstrap`** (`adk/toolpacks/node_bootstrap/`):\n seven agent tools — `node_detect_hardware`, `node_resolve_recipe`,\n `node_plan_deployment`, `node_apply`, `node_enroll`, `node_register_backend`,\n `node_verify` — plus a CLI shim (`python -m adk.toolpacks.node_bootstrap\n detect|resolve|plan|apply|enroll|register|verify`). Detect a box's hardware,\n resolve a deployment recipe, actually deploy the inference engine (docker\n compose / native / delegate), enroll with a control plane, register the\n backend, and live-verify a completion.\n- **9 hardware recipes** shipped in the wheel: `cpu-1bit-llamacpp`, `cpu-ollama`,\n `cuda-vllm-8gb/24gb/40gb`, `cuda-dual-stack-32gb` (32GB-card co-resident\n stack), `unified-memory-vllm` (DGX/Grace-class), `metal-ollama` (native —\n Docker on macOS has no Metal passthrough), `cloud-api` fallback. Resolution\n scores hardware against min/max VRAM bands, RAM, cores and unified memory;\n ties prefer higher tiers and self-contained recipes over fleet delegates.\n- Security posture: enrollment and backend registration are fail-closed\n (missing token/URL → error dict, never anonymous, never `verify=False`);\n tokens are redacted in outputs; all endpoints come from args/env\n (`AITHER_CONTROL_PLANE_URL`, `AITHER_GENESIS_URL`, `AITHER_AUTH_TOKEN`).\n\n## [2.24.0] - 2026-07-14\n\n### Changed — sync-family consolidation\n\n- **Removed dead code:** `adk/sync.py` (528 lines) deleted — it was unreachable,\n shadowed by the `adk/sync/` package (`import adk.sync` always resolved to the\n package; nothing loaded the module by path — verified).\n- **Grouped the sync domain modules into the `adk.sync` package:**\n `brain_sync`→`adk.sync.brain`, `files_sync`→`adk.sync.files`,\n `lockbox_sync`→`adk.sync.lockbox`, `secrets_sync`→`adk.sync.secrets`,\n `session_sync`→`adk.sync.sessions`, `settings_sync`→`adk.sync.settings`,\n `sync_watermark`→`adk.sync.watermark`. All in-tree importers updated. No\n back-compat shims were left because **zero external/monorepo code imported the\n old root paths** (verified) — a clean move, not a shimmed one. **Breaking only\n for code that imported `adk.<name>_sync` directly** (none known).\n\n### Docs — MCP module layering clarified\n\n- `adk.mcp` (enterprise AitherOS gateway client: auth/billing/token-tracking) and\n `adk.core.mcp` (minimal generic JSON-RPC adapter for any MCP server) are\n DISTINCT layers, not duplicates — each now carries a LAYER/ROLE/See-also\n docstring so they are not mistakenly consolidated.\n\n## [2.23.2] - 2026-07-14\n\n### Fixed — CompletionGate no longer false-fails on correct results\n\n- **Tolerant judge JSON extraction.** The LLM judge parsed its reply with `json.loads(text[first\"{\":last\"}\"])`, which raised `JSONDecodeError` (\"Extra data\") whenever the model appended prose after the verdict or emitted a second object — and `verify()` turned that into a fail-closed verdict, so a genuinely completed task reported `unverified`. New `_first_json_object()` uses `JSONDecoder.raw_decode` from the first `{` that begins a valid object and ignores trailing content (prose, markdown fences, extra objects). A judge that answers with no parseable JSON still fails closed (never a soft pass), now with a clear reason. The same extractor backs `_derive_criteria`.\n- **Bare code-like tokens are hard-checked.** A natural criterion like `output contains ADK_AUTO_OK` (no quotes) previously fell through to the judge; `hard_checks` now also matches bare identifier/code shapes (contains an underscore, or 4+ CAPS/digit chars), with a stopword guard so a descriptive uppercase word (`OUTPUT`, `CONTAIN`) never becomes a false required token. Quote a token to force it regardless.\n- +9 tests (24 total), including the exact trailing-data regression.\n\n## [2.23.0] - 2026-07-14\n\n### Removed — dead modules and internal integrations\n\n- **`adk.swarm`, `adk.provisioning_tools`, `adk.session_sync_integration`, `adk.addon_metering`** — four orphan modules with zero public usage have been deleted. These were internal prototypes kept for legacy monorepo consumers; public packages have not imported them since 2.21.0 (tracked as D-348). **Breaking for anyone directly importing these modules** (import will fail). They are no longer exempt from the orphan-module check.\n- **`adk.aither_bridge`** — the AitherOS-internal IRC ↔ chat gateway bridge is no longer shipped in the public aither-adk package (stripped at sync time). All public importers already degrade behind `except ImportError` (in `adk.server` and `adk.builtin_tools`), so public packages remain unaffected. The module stays in the private AitherOS monorepo for internal use.\n- **Identity document trim** — removed internal agent names from `adk/identities/aither.yaml` delegation guidance, replacing with role-generic wording (\"delegate code review, refactoring, security analysis, performance and testing to specialist agents when available\"). Effort-tier routing philosophy remains unchanged and is product design, not a leak.\n\n### Changed — genericized customer/product names in help text\n\n- CLI help/examples no longer name specific customer deployments (`--scope` template list and `deploy agent` name example use generic placeholders). Functional deploy paths are unchanged.\n\n## [2.22.1] - 2026-07-14\n\n### Fixed — real bugs surfaced by reviving the public CI (red since Jul 5)\n\n- **TLS**: bundled toolpacks (arc-brainpack, cloudflare) disabled certificate\n verification on gateway/vault calls; all now route `adk._tls.tls_verify()`\n (verified by default, internal CA bundle when present).\n- **`system_prompt` contract**: bundled-pack `[PACK DIRECTIVES]` were appended\n even to an explicit `system_prompt`; an explicit prompt now wins wholesale.\n- **Headless secrets**: `_derive_key()` used `os.getlogin()`, which raises\n `OSError` without a controlling terminal (CI, daemons, containers) — falls\n back to `getpass.getuser()` (same value on interactive boxes, so existing\n secret files keep decrypting).\n- **`pack_verifier`**: missing `cryptography` crashed with `UnboundLocalError`\n in the exception handler instead of failing closed; now degrades gracefully\n (and `cryptography` was added to the dev extra so CI tests the real path).\n- **`fleet_manager._kill_pid` (POSIX)**: reported success immediately after an\n asynchronous SIGTERM and never reaped children (zombies read as alive); now\n polls, escalates to SIGKILL, and reaps.\n- **Identity**: `aither` regained the `creative` tool category (dropped\n accidentally in a 2.7.0 WIP sweep).\n\n## [2.22.0] - 2026-07-14\n\n### Changed — internal toolkit relocation\n\n- **`adk.platform` relocated** — the internal aither-platform toolkit (ComfyUI/image-gen\n pipelines, fleet agent clients, parallel CLI/UI/tools stack) has been moved out of the\n adk SDK tree to the standalone internal `aither-platform` package at\n `AitherOS/agents/aither_platform/`. The public package remains unaffected (it already\n excluded `adk.platform` since 2.21.1). Internal AitherOS agent builds now install\n `aither-platform` separately. `adk platform` falls through to the same \"not available\"\n message on public installs; internal builds work via the relocated package.\n\n### Removed — backward-compat re-export deleted\n\n- **`adk.gateway` module removed** — the backward-compatibility re-export of `GatewayClient`\n from `adk.gateway` has been removed. Use `from adk.client import GatewayClient` instead\n (the established public API since 2.21.0). All internal importers have been updated.\n\n## [2.21.1] - 2026-07-14\n\n### Removed — internal toolkit and license-gated product no longer ship publicly\n\n- **`adk.platform`** — the internal aither-platform toolkit (ComfyUI/image-gen\n pipelines, fleet agent clients, a parallel cli/ui/tools stack) is no longer\n included in the public repo or the PyPI wheel. It was internal-only code that\n had been riding along since the aither-platform merge. `adk platform` now\n reports \"Platform toolkit not available\" on public installs; internal AitherOS\n builds are unaffected.\n- **`adk.formbridge`** — the FormBridge form-automation product is license-gated\n (PROFESSIONAL+); its implementation no longer ships in the free package. All\n import sites (`builtin_tools`, `server` route mount) already degrade behind\n `except ImportError`, so nothing else changes behavior.\n- Product test suites (`test_formbridge.py`, `test_encounter_keying.py`,\n `test_flow_tools.py`) are excluded from the public sync alongside them.\n\n## [2.21.0] - 2026-07-14\n\n### Added — Completion gate: verified-or-retried task execution\n\n- **`adk.gate.CompletionGate`** — a composable wrapper that closes the \"silent\n success\" gap: an agent's ReAct loop returns whatever the model produced and\n calls it done (`finish_reason = stop/max_steps/length`), which is a *liveness*\n signal, not a *completion* one — so an agent can \"succeed\" while doing nothing.\n `CompletionGate` runs the agent, verifies the result against acceptance\n criteria, retries with the failure fed back, and returns the response with\n `finish_reason` set to `verified` or an honest `unverified`. Never a false pass.\n - **Hard-first, un-soft-passable checks** (`adk.gate.hard_checks`): a filesystem\n path a criterion says must exist is checked on disk; a required quoted token\n must appear in the output. Only genuinely subjective criteria fall through to\n an optional LLM judge (reuses the agent's own model), which **fails closed**\n if unavailable.\n - **`gated_run(agent, task, criteria=..., max_retries=2)`** — one-shot helper.\n - `auto_criteria` (LLM writes the machine-checkable definition-of-done from the\n task), a `verifier=` override for custom checks (tests-pass / HTTP probe), and\n a `requires_action` short-circuit so it never fights human-approval pauses.\n - Compose it onto any agent exposing `run(task) -> resp{.content, .finish_reason}`\n (`AitherAgent`, `core.Agent`, or a thin adapter over a remote dispatch).\n - Exported from the top-level package: `from adk import CompletionGate, gated_run`.\n\n## [2.19.0] - 2026-07-12\n\n### Added — Eve agent importer + durable A2A tasks + canonical agent discovery\n\n- **`adk pack import <eve_agent_path>`** — convert external Eve agents to AitherADK packs\n (from GitHub or local filesystem). Maps Eve structure (instructions → system prompt,\n agent.ts → agent.yaml, skills/*.md → skills, connections/*.ts → MCP, tools/*.ts →\n tools/node verbatim) to AitherADK format with a single command. Eve agents (compiled\n manifest format, schema v35) become installable packs compatible with `adk pack install`.\n- **Durable A2A task lifecycle (`TaskManager` backed by FileStore)** — agent-to-agent\n tasks now survive container restarts. Tasks persist as append-only JSONL\n (`.adk/tasks.jsonl`); in-memory dict for fast access; `create_task()` / `update_status()` /\n `add_artifact()` / `add_message()` all save to disk. Google A2A v0.3.0 spec compliant.\n- **Canonical agent card discovery** — `A2AServer.mount()` now exposes:\n - `GET /.well-known/agent-card.json` — Agent Card (canonical; contains agent identity,\n capabilities, skills, endpoints)\n - `GET /.well-known/agent.json` — Agent Card (legacy, redirects with 308 to canonical path)\n - `POST /a2a` — JSON-RPC 2.0 for message send/task lifecycle\n - `GET /a2a/tasks/{id}/subscribe` — SSE streaming task updates\n- **Four built-in agent packs now complete and catalogued**:\n - `hermes` — architecture review & trade-off analysis\n - `openclaw` — web research & source verification\n - `claude-code` — feature development & debugging\n - `analyst` — structured data inference & anomaly detection\n All discoverable via `adk packs` / `adk install pack:<name>`.\n- **Tolerant tool-call JSON parsing** — malformed tool JSON (extra properties, missing\n name/arguments, string-encoded nested args, missing keys) is now gracefully degraded\n instead of silently failing. `_parse_tool_json()` accepts both `\"arguments\"` and\n `\"parameters\"` keys, coerces string-encoded args, and skips unparseable calls instead\n of crashing the turn.\n\n### Known limitations\n\n- Eve agent importer: remote GitHub fetching not yet implemented (local paths only).\n- Durable task store: currently JSONL (suitable for thousands of records, not millions).\n\n## [2.18.0] - 2026-07-10\n\n### Added — AitherShell command center + Claude Code session management\n\n- **`aither sessions`** — Claude Code session manager: interactive full-screen\n browser (type-to-filter, transcript preview, deep full-text `search`, resume\n **in the current terminal** or as Windows Terminal tabs), crash guard\n (`guard install` = at-logon watchdog that snapshots live sessions and\n auto-restores the whole set after a terminal crash), and `ingest [--watch]`\n (incremental, secret-guarded sync of session conversations into the local KB\n / CompanyBrain via the standard ingest pipeline).\n- **`aither hq`** — command-center dashboard: per-service fleet health, LLM\n queue (depth/VRAM/models), Pulse alerts, live session count, inbox unread —\n auto-refreshing, with one-key jumps into chat, sessions, inbox, agents,\n brief, watchtower, and docker recovery.\n- **`aither inbox`** — unified queue over CommCore mail, Relay\n mentions/notifications, and Pulse alerts; open/mark-read/DM-reply from the\n terminal. Every source degrades independently.\n- **`aither agents`** — roster console + `agents ask -e <1-10> <agent> <q>`\n (effort-tiered ask via Genesis, with automatic `/chat/stream` fallback when\n the `/agent/sync` execute gate rejects host callers) + forge dispatch\n inspection + routine health.\n- **`aither palette`** — universal fuzzy picker across actions, sessions, and\n services; **`aither brief`** — renders Atlas's executive briefing in the\n terminal; **`aither watch`** — fleet watchtower with named wedge-signature\n detection (docker-WSL wedge, LLM-queue stall, crash-loop uptime regression)\n and optional docker auto-recovery.\n- All fleet reads go through a shared fail-soft `FleetClient` (internal-CA\n TLS via `adk._tls`, dashboard-grade timeouts, per-source degraded states).\n- Node shell-cli (`@aitherium/shell-cli` 1.13.0): `aither\n sessions|hq|inbox|palette|brief|watch|agents|docker` now pass through to the\n Python shell instead of being intent-classified as chat prompts.\n\n## [2.17.1] - 2026-07-05\n\n### Fully self-service mesh join — no customer key handling\n\n- **Auto-join headscale off the onboard response.** The conductor now auto-issues a\n headscale pre-auth key for NAT'd nodes and returns it in the `/v1/mesh/onboard`\n response; `mesh.join` consumes it and brings up the tunnel automatically. A customer\n runs one command — they never mint, fetch, or set a headscale key. Explicit\n `--headscale-key`/`AITHER_HEADSCALE_AUTH_KEY` still take precedence.\n- **Security:** `_tailscale_up` now redacts the auth key from all logs and exceptions\n (a failed `tailscale up` previously echoed the full argv, leaking the key into logs).\n\n## [2.17.0] - 2026-07-05\n\n### Added — Headscale mesh transport + local Qdrant + AitherConnect onboarding\n\n- **Headscale NAT-friendly mesh transport.** When raw WireGuard UDP:51820 is not\n viable (customer boxes behind NAT, corporate firewalls, CGNAT), `adk mesh join\n --headscale` routes the mesh tunnel through Tailscale's Headscale control plane.\n The overlay IP is still Conductor-assigned (10.77.0.0/16); Headscale provides\n only the transport layer. Configured via `AITHER_MESH_TRANSPORT=headscale`,\n `AITHER_HEADSCALE_URL` (default: https://headscale.aitherium.com), and\n `AITHER_HEADSCALE_AUTH_KEY` (pre-generated key). Automatic fallback to raw\n WireGuard if Headscale setup fails.\n- **Local Qdrant vector DB provisioning.** `adk stack` now provisions Qdrant\n locally when `AITHER_VECTOR_DB=qdrant` is set, enabling offline-first RAG\n without external dependencies. Tiered: local Qdrant in-fleet, cloud Nexus\n for enterprise multi-tenant deployments.\n- **Conductor URL public fallback.** `adk mesh join` resolves the internal\n `aitheros-conductor:8193` hostname; if unreachable (e.g., self-hosted nodes),\n it falls back to the public `conductor.aitherium.com:8193` endpoint,\n eliminating bootstrap configuration friction.\n\n### Documentation\n\n- **Self-hosting runbook.** New `docs/SELF_HOSTING.md` covers deploying AitherOS\n on customer infrastructure with Qdrant + mesh + AitherConnect onboarding.\n- **Mesh transport constraints documented.** Raw WireGuard requires public\n UDP:51820 endpoint (cloud instances, dedicated hardware); Headscale for\n NAT'd networks. Noted in CLI help and module docstrings.\n\n## [2.15.0] - 2026-07-04\n\n### Added — built-in web admin console + portal-profile settings sync\n\n- **Web admin console.** The page `adk up` opens (served at `/` and `/ui`) is now a full,\n self-contained console — Chat plus six admin tabs: **Backend** (view/switch LLM provider,\n set keys, test connection), **Packs** (list/enable/disable/reload tool packs),\n **Sessions** (browse/delete conversations), **Logs** (redacted agent-log tail),\n **Graph** (knowledge-graph search/neighborhood/stats), and **MCP** (add/remove external\n MCP servers). No build step, no CDN — a single packaged HTML asset. The original minimal\n streaming chat page stays at `/chat` for back-compat.\n- **Admin API (`adk/admin_api.py`).** ~20 endpoints under `/admin/*`, all bearer-gated by the\n existing middleware (never in `_skip_auth_paths`). Operates on the live agent — LLM backend\n swap (`LLMRouter.switch_backend`), pack reload, and MCP registration take effect without a\n restart.\n- **Settings sync (`adk/settings_sync.py`).** The user's portal profile is the source of\n truth: on startup the agent pulls `preferences.adk` and applies it over the local\n `~/.aither/config.yaml` cache; admin-console edits push a debounced snapshot back to\n `PUT /api/settings/preferences`. Provider API keys and MCP auth headers are device-local\n secrets and are never included in the pushed snapshot. Opt out with `AITHER_SETTINGS_SYNC=false`.\n\n### Security\n\n- **Timing-safe token comparison.** The server bearer check now uses `hmac.compare_digest`\n (the token is reachable over the public tunnel; a byte-wise `!=` leaked it to timing attacks).\n- **MCP add is SSRF-guarded + confirmed.** Adding an external MCP server is a two-step\n prepare→confirm flow; URLs resolving to loopback / private / link-local / metadata addresses\n are rejected (override with `AITHER_ALLOW_PRIVATE_MCP=1` for trusted local servers).\n- **Config editor is allowlisted.** `PATCH /admin/config` writes only safe scalar prefs and\n hard-denies fields that could execute code or redirect traffic at startup (e.g.\n `aither_toolpack_dirs`, backend base URLs).\n- **Log tail is scrubbed.** Bearer tokens, `#k=` fragments, and `sk-`/`aither_sk_` keys are\n redacted from the log-tail endpoint; the tunnel log is excluded.\n\n## [2.14.8] - 2026-07-04\n\n### Added — `adk up` for normal humans (no API key, no prereqs, live chat)\n\n- **Hosted-brain default — no API key required.** When there's no local model and no\n provider key, `adk up` signs in (device-flow) and routes inference through the gateway\n using the portal token (`AITHER_LLM_BACKEND=gateway` + bearer). A non-technical user\n never needs to obtain or paste an LLM API key. Explicit `--provider`/local model still\n take precedence; unattended `--yes` with no token/backend fails cleanly (exit 3).\n- **cloudflared auto-download.** If the tunnel binary is missing, `adk up` downloads the\n right platform asset to `~/.aither/bin` (SHA256-verified against the release; opt out with\n `AITHER_CLOUDFLARED_REQUIRE_CHECKSUM=0`) instead of exiting with an install hint.\n- **Built-in streaming chat page.** The agent now serves a self-contained chat UI at `/`\n (and `/chat`) with a live \"thinking…\" indicator and token-by-token streaming — so a human\n has somewhere to talk to it with immediate feedback instead of a blank wait. `adk up` opens\n it locally and passes the callback bearer in the URL fragment (`#k=…`), so `/chat/stream`\n stays authenticated (not an open proxy) with nothing to paste.\n- **Human-readable failures.** `adk up` errors now carry a plain-English `next_action`\n (kept machine-readable in `--yes` JSON).\n\n### Fixed\n\n- `adk up` chat link pointed at a non-existent `/settings/agent` route; it now points at the\n agent's own streaming chat page.\n\n## [2.14.7] - 2026-07-04\n\n### Added\n\n- **`adk up` — one command to run a persistent, fleet-connected agent.** Collapses\n the scattered onboarding surface (`host`/`quickstart`/`connect`/`login`/`setup`)\n into a single command that starts `aither-serve` (single agent, identity `aither`\n by default), opens a Cloudflare quick-tunnel for fleet reachability, registers with\n the portal, and installs a reboot-persistent autostart entry (Windows Task Scheduler\n / systemd-user / launchd). Detaches by default so the terminal is freed; `--foreground`\n blocks. Reuses the existing device-flow login, cloudflared discovery, provider-key\n store, and `_preflight_check` backend detection.\n- **Fully non-interactive path for autonomous agents.** `adk up --yes` (or any\n non-TTY invocation) takes ZERO prompts — everything resolves from flags, env, and\n saved config — and emits a single machine-readable JSON status line with meaningful\n exit codes (`0` ok, `2` bad-input, `3` no-backend, `4` tunnel-missing, `5` register-failed).\n Degrades to local-only (warn, not fail) when a portal token is absent unless\n `--require-register` is set.\n- **`adk down` / `adk status --json` companions.** `down` stops the agent + tunnel,\n best-effort deregisters, and removes the autostart entry; `status` reports the running\n agent (liveness + `/health` + tunnel + registration), with `--json` for agents/CI.\n- **`adk stack`** — the previous `adk up` behaviour (supervise the Room + Ollama consumer\n stack) now lives here; `adk up` is the connected-agent command.\n\n### Internal\n\n- New `adk/agent_daemon.py`: detached process spawn, status file\n (`~/.aither/adk-up.json`), pid liveness/teardown, and cross-platform autostart install.\n\n## [2.14.6] - 2026-07-04\n\n### Added\n\n- **`adk.preflight` — self-bootstrap capability probe.** `run_preflight(agent,\n spec)` wakes an agent up knowing what it is actually plugged into: a bounded\n (1.5s) parallel `LivenessProbe` over the router's resolved backends\n (primary/reasoning), the embeddings winner, remote MCP tools, the structured-ML\n / `/ml/teach` endpoints, and voice — returning an honest `CapabilityReport`.\n Status vocabulary distinguishes reachability from entitlement\n (`OK`/`MISSING`/`UNREACHABLE`/`AUTH`/`TIER_DENIED`/`UNSUPPORTED`), a hosted slot\n never prints a bare `OK`, and a required-but-unsatisfied slot aborts before the\n loop instead of blind-firing. Headless task/rules resolution + an allowed-roots\n policy binding (`builtin_tools.set_allowed_roots`, which resets the memoized\n cache so the override actually enforces).\n- **Multimodal (image) messages.** `Message.content` now accepts an OpenAI-style\n content-part list (`str | list[dict]`), passed through verbatim to the\n OpenAI-compatible body (gateway / vLLM / OpenAI). New `adk.llm.multimodal`\n helpers — `image_message()`, `image_content_parts()`, `to_image_url()` — build\n the parts and encode local image bytes/paths into `data:` URIs.\n- **Real vision preflight probe.** The vision slot is no longer unconditionally\n `UNSUPPORTED`: it sends a discriminable image (a solid red square) and requires\n the model to name the color, so a blind/text-only model cannot false-pass. `OK`\n only on a real round-trip; `AUTH` on 401/403; `UNSUPPORTED` when there is no\n OpenAI-compatible provider or the model cannot see the image.\n\n## [2.14.5] - 2026-07-04\n\n### Added\n\n- **`adk.reasoning.mcts` — generic Monte-Carlo Tree Search library.** A reusable\n MCTS engine (ported from the internal `UnifiedMCTS`, with all internal couplings\n removed) that any agent can drive over its own environment:\n - `MCTSEnvironment` protocol (`get_state_hash` / `get_actions` / `step` /\n `evaluate` / `clone`) — implement it and search.\n - Three **optional, default-off** model seams — `TransitionModel`, `PolicyModel`\n (PUCT priors), `ValueModel` — so a learned world-model / policy / value can be\n injected without touching the core; with all three `None` the engine is a plain\n UCT search.\n - `ObservedTransitionModel` adapter — a cheap online lookup world-model (hit =\n exact stored transition; miss = identity + negative \"unknown\" reward +\n `is_uncertain`, biasing exploration toward learning), with JSONL save/load for\n cross-run reuse.\n - `UnifiedMCTS(config).search(env)` returns an `MCTSResult` (best action, value,\n confidence, principal-variation path) and an optional `MCTSTrace`\n (visit-distribution policy target) for distillation.\n- **Self-bootstrapping agent machinery.** Build an agent from a declarative spec\n instead of bespoke wiring:\n - `adk.bootstrap.build_agent_from_spec(path) -> (AitherAgent, RunCtx)` — resolves\n prompts, activates required/optional tool packs, and carries a `memory_map`\n (a `node_type -> (role, tier)` override for memory writes, never a gate).\n - `adk.prompts.PromptBridge` — resolves an agent's prompt bindings from files\n (relative to the spec) or `AitherPrompts` dot-keys (optional import).\n - `adk.packs.PackActivator` — eager activation of installed tool packs by name\n over `register_tool_packs`; a required pack that registers zero tools raises\n `PackUnavailable`, optional packs soft-degrade.\n - `python -m adk.bootstrap.generic_agent <spec.yaml>` inspects a spec (resolved\n name, activated packs + tool counts, prompt keys, memory map) without running a\n live loop.\n- **`deep_research` tool pack** — a full multi-engine web-research workflow as\n `dr_*` agent tools (keyless search, page fetch + cache, site mirror, a citable\n findings knowledge graph, and markdown/PDF/DOCX report generation), activatable\n by name via the tool-pack loader.\n\n## [2.14.4] - 2026-07-04\n\n### Added\n\n- **`structured_ml` tool domain** — zero-shot structured-data inference for agents,\n backed by TabFM (tabular classification/regression) and TimesFM (time-series\n forecasting). Three tools:\n - `tabular_classify(support_rows, target, query_rows)` — classify rows from a\n labeled support set, in-context (no training step; up to 10 classes).\n - `tabular_regress(support_rows, target, query_rows)` — predict a numeric target.\n - `timeseries_forecast(series, horizon)` — forecast future values of a series.\n\n The tools POST to a structured-ML inference service resolved from\n `AITHER_STRUCTURED_ML_URL` (default `http://localhost:8192`), reject non-http(s)\n URLs, and summarise oversized responses to protect the agent's context.\n- **`Capability.STRUCTURED_INFERENCE`** capability token and a new **`analyst`**\n identity/pack that ships the `structured_ml` domain by default. The domain is\n opt-in — only identities that enable the `structured_ml` category get the tools.\n\n## [2.14.3] - 2026-07-03\n\n### Fixed\n\nAll four bugs below were found by actually booting `adk run` against a real\nchat model and driving a real tool-calling request end-to-end (not inferred\nfrom reading the code) — each one silently broke the \"local agent uses its\nconfigured model and its tools actually work\" path.\n\n- **`AITHER_MODEL` / `config.model` silently ignored by the OpenAI-compatible\n backend**: `LLMRouter.__init__` never read `config.model` into `self._model`\n unless the caller passed `model=` directly to the constructor — which nothing\n did. Every explicit model choice (env var, config file) was dropped, the\n provider fell back to a hardcoded per-backend default (`gpt-4o-mini` for the\n `openai` backend), and an unrecognized model name got silently remapped\n server-side to the wrong backing model. Concretely: setting\n `AITHER_MODEL=aither-orchestrator` had no effect — every request actually\n went to a different model that was never meant to be called directly and\n can't reliably use tools.\n- **`OPENAI_BASE_URL` ignored for explicit `--backend`/`AITHER_LLM_BACKEND`\n selection**: `LLMRouter._auto_detect()`'s explicit-backend branch hardcoded\n `base_url=None` when constructing the provider, instead of reading\n `config.openai_base_url` (which two other call sites in the same file\n already did correctly). Any self-hosted OpenAI-compatible endpoint\n (MicroScheduler, vLLM, LM Studio) configured via `OPENAI_BASE_URL` was\n silently ignored in favor of `https://api.openai.com/v1`.\n- **Tool-call steering never engaged on default-effort chat**: the retry that\n nudges a model back into structured tool-calling when it responds with prose\n instead of a function call was gated behind `effort >= 6`. Default `effort`\n for a plain `agent.chat(message)` call resolves to `5`, so the safety net —\n built specifically for this failure mode — never fired for default requests.\n Also now forces `tool_choice=\"required\"` on the retry instead of relying on a\n text nudge alone.\n- **Every non-string builtin-tool parameter silently typed as `\"string\"` in\n the generated tool schema**: `builtin_tools.py` uses\n `from __future__ import annotations`, so `_extract_parameters()` in\n `tools.py` was reading postponed (string) type hints (`\"int\"`, not `int`)\n straight out of `fn.__annotations__`, which never matched\n `_type_to_schema`'s type-object keys and silently fell through to\n `{\"type\": \"string\"}`. Every builtin tool with an `int`/`float`/`bool`/`list`\n parameter was affected — e.g. `file_read(start_line: int, end_line: int)`\n advertised both as strings, so a model correctly calling the tool with\n `\"start_line\": \"0\"` crashed the actual implementation with\n `unsupported operand type(s) for -: 'str' and 'int'`. Fixed by resolving\n hints through `typing.get_type_hints()` instead of the raw annotations dict.\n `_coerce_arguments()` — the runtime safety net that's supposed to coerce a\n model-supplied `\"0\"` string into `0` before a tool call executes — had the\n exact same raw-`__annotations__` bug and silently no-opped for every builtin\n tool; fixed the same way.\n- **Streaming tool-loop could hang a connection forever**: `chat_stream()`'s\n fallback to the sync tool loop (tool calls can't stream token-by-token) had\n no timeout, so a model that didn't cleanly terminate a tool-calling round\n trip left the SSE connection open indefinitely with zero bytes sent. Bounded\n with a 60s `asyncio.wait_for`; on timeout the stream yields a clear message\n instead of hanging.\n\n## [2.14.2] - 2026-07-03\n\n### Fixed\n- **AitherShell session amnesia**: the REPL generated a fresh `session_id` for\n EVERY message, so conversation history never followed across turns (the\n server stored each turn under a different session). One stable id is now\n minted per shell run; `/resume` and `/new` semantics unchanged, and the\n cross-restart auto-restore of the last session now actually works.\n\n## [2.14.1] - 2026-07-02\n\n### Added\n- **Account-tied licensing via device-flow login** — `adk login` now persists the account\n license returned by the device-flow handshake to `~/.aither/license.json` (base64\n `license_key` → `{payload, signature}` envelope), so entitlements from the authenticated\n account carry into every later `adk` invocation without re-auth. Pairs with the fleet-side\n node tool-pack gate that grants packs by plan-tier rank.\n- **`can_use_formbridge` / `can_use_untether` entitlements** (PROFESSIONAL+ rank ≥ 3) backing\n the FormBridge and UNTETHER agent tool-packs.\n\n### Fixed\n- `adk` CLI: guard the optional `memory_graph` singleton (was a latent `NameError` on the\n `/stats`, `/memory`, and save paths when memory was not constructed).\n\n## [2.14.0] - 2026-07-02\n\n### Added\n- **`adk.memory_wiki` — LyraWiki-style self-managed semantic knowledge** (opt-in; nothing constructs it by default): `MemoryWiki(graph_memory)` maintains curated wiki ARTICLES as `wiki_article` nodes (title/slug, LLM-owned markdown with `[[wikilinks]]`, embedding, durable tier, `CONSOLIDATED_FROM` edges to source memories, governance-ledgered revisions).\n - `consolidate(llm, since=, budget=)` clusters unconsolidated episodic/fact nodes (embedding + keyword), drafts/UPDATEs articles via the INJECTED llm callable (`str -> str` or `messages -> str`, sync or async — no hard model dependency), cites source node ids, marks contradicted sources superseded, then DEMOTES consolidated sources to a fast-decay tier via `promote()` (represented knowledge doesn't need raw copies).\n - `lint(llm=None)` — deterministic health checks (orphan wikilinks, empty/stale articles, live-but-contradicted pairs) + an optional LLM audit pass.\n - `prune(relevance_floor=0.05, hard_delete_after_days=14)` — article relevance = tier freshness × reinforcement (reinforce-on-recall) + link-degree to LIVE memories; below the floor → reversible governance tombstone; entombed past the window → **HARD-DELETE** (the tombstone snapshot itself is purged — content and embedding irrecoverable; the true-deletion path). A node is never deleted without a tombstone first.\n - `recall(query, limit)` — article-first RAG (curated articles rank above raw nodes) that reinforces returned articles.\n- **`adk.routines` — agent heartbeat + self-programmed routines**: `RoutineStore` is a durable registry (`~/.aither/routines/{agent}.json`, `AITHER_DATA_DIR`-aware, injectable path) of cron-scheduled self-prompts `{name, cron, instruction, enabled, last_run, last_result, tags}` that rehydrates into the existing `adk.cron.CronScheduler` on `start()`. A fire runs `agent.chat(instruction)` with the agent's full toolset — the agent programs its future self with its own adk. Guardrails: `max_routines` (12), a 5-minute min fire interval per routine, a per-fire timeout, and every result ledgered (truncated) to `last_result`. Direct-callable routines (`register_direct`) fire a bound method instead of a self-prompt. Self-management tools (`routine_create/list/update/pause/resume/delete/run_now`) ship as OpenAI tool defs + handlers (`build_routine_tools` / `register_routine_tools` / `routine_tool_defs`); the handlers ONLY touch the RoutineStore — they can never modify agent config or safety-relevant settings (the leash principle).\n- **`AitherAgent(routines=True, memory_maintenance=True)`** (both default **False** → the default agent is byte-identical): `routines=True` builds the RoutineStore + scheduler, registers the self-management tools, and starts the heartbeat lazily on the first `chat()` (or via explicit `start_routines()`). `memory_maintenance=True` registers DEFAULT routines — `wiki_consolidate` (every 2 h), `wiki_lint` / `wiki_prune` / `graph_sweep` (daily) — as DIRECT method fires so memory upkeep runs even on tiny models, while staying visible/manageable via `routine_list`.\n- `TombstoneStore.purge(tombstone_id)` — the hard-delete endpoint: irrecoverably removes a tombstone snapshot and rewrites the persisted store without it (consumed by `memory_wiki.prune`).\n\n## [2.13.6] - 2026-07-02\n\n### Added\n- **Qdrant backend for GraphMemory dataplane sync — TRUE two-way, verified live.** Set `AITHER_FLEET_QDRANT_URL` and the sync uses Qdrant (`upsert` + `scroll` + filtered `search`) instead of Nexus. This is what actually delivers cross-agent memory sharing: a node is upserted with its OWN embedding under a deterministic per-(tenant,node) point id (idempotent re-push), and `fleet_pull` reliably SCROLLS a tenant's points back — the enumerate Nexus lacks (its `/search` doesn't return ingested docs and `/export` needs lancedb, which has a hard dependency conflict in the fleet). **Proven live with 2 agents:** agent-1 ingests → auto-syncs to the tenant dataplane; a fresh agent-2 in the same tenant `fleet_pull`s all of agent-1's nodes and can search them; a different tenant pulls nothing (isolation). Dimension-safe (never mixes vector dims in a collection). Local SQLite stays source of truth; best-effort, never raises.\n\n## [2.13.5] - 2026-07-02\n\n### Fixed\n- **CRITICAL — GraphMemory dataplane sync push was 100% failing.** `_fleet_push_node` sent `source_type=\"graph\"` / `content_type=\"graph_node\"`, which AitherNexus rejects as invalid enums (HTTP 500) — so NO graph node ever replicated in 2.13.4. Now sends the accepted `source_type=\"manual\"` / `content_type=\"text\"` (graph provenance is carried in `metadata.synced_from`). **Verified live** against a fleet Nexus: a 4-node ingest replicates with `pending==0` and the collection count reflects it.\n\n### Added\n- **Swarm-awareness notification** — on a successful sync batch, `GraphMemory` emits a best-effort `graph.synced` event (tenant/workspace/agent/collection/count) to `AITHER_FLEET_EVENTS_URL` so OTHER agents in the tenant/swarm can pull the fresh data and deconflict in-flight work (push-based awareness instead of polling). No-op unless the env is set.\n\n### Known limitation\n- Cross-agent **rehydration** (`fleet_pull`) is only reliable when the fleet Nexus is backed by lancedb (persistent + exportable). An in-memory Nexus stores on `/ingest` but neither its `/search` nor `/export` returns the docs — two-way read needs the persistent Nexus.\n\n## [2.13.4] - 2026-07-02\n\n### Added\n- **Memory-maintenance primitives on `GraphMemory`** (all opt-in; default behaviour byte-identical):\n - **Reinforce-on-recall**: `recall_with_activation(..., reinforce=True)` bumps `reinforcement_count`/`last_reinforced` on every RETURNED node's metadata mirror (one UPDATE per node, WAL-safe) — making the `MemoryRecord` reinforcement fields the activation scorer reads LIVE. Default `False` = zero writes.\n - **`sweep(now, archive_below=0.05, max_nodes=None, dry_run=False)`**: read-time decay enforced at rest — archives nodes past their tier TTL with freshness×reinforcement score below `archive_below` and `reinforcement_count <= 1`; `max_nodes` additionally archives the lowest-scored overflow. PERMANENT tier immune. Every archive is a reversible governance tombstone (`TombstoneStore.recover`) + a FORGET ledger entry; a node is never deleted without a tombstone. `dry_run` returns the would-archive list without acting.\n - **`promote(node_id, tier=?, role=?)`**: re-tier/re-role a node IN PLACE (tier/role columns + metadata mirror) — edges, embedding and ledger history survive; ledgers an UPDATE entry when governance is on.\n- `MutationType.UPDATE` ledger entry kind (in-place re-tier/re-role).\n- **Per-tenant dataplane sync for `GraphMemory`** — replicate the local graph to a per-tenant vector dataplane (Nexus/Qdrant) so a sovereign agent's memory is durable + rehydratable across restarts and hosting boundaries. `fleet_push_all_nodes()` (catch-up), `fleet_pull()` (best-effort semantic top-up), `fleet_sync_pending()`, and auto-sync on ingest (`_maybe_autosync` + `drain_sync()`). **On by default** (`AITHER_FLEET_SYNC=auto`; only `false/0/off` disables) with an inferred target (local Nexus `:8122` or the gateway in cloud mode) and a SEPARATE collection (`AITHER_FLEET_GRAPH_COLLECTION`, default `graph_memory`) from the KV memory sync. New `GraphMemory(tenant_id=, workspace_id=, fleet_url=, auto_sync=)` constructor args override env (required for multi-tenant host processes). Node `synced` column + schema v4 migration. TLS verify is CA-bundle-aware (`AITHER_CA_BUNDLE`) for the internal-TLS mesh. Local SQLite stays the source of truth; every sync path is best-effort and never raises into ingest.\n\n## [2.13.3] - 2026-07-02\n\n### Added\n- **Canonical self-deploying embeddings provider** (`adk.embeddings`): one embedding provider for the whole SDK so vectors are portable across scopes (platform / tenant / sovereign) — same model + dimension everywhere. Pinned to `nomic-embed-text` (768-d; the vLLM `--served-model-name` for nomic-embed-text-v1.5). Lazy, single-flight resolution chain: explicit `AITHER_EMBEDDINGS_URL` → local vLLM (`:8209` then `:8120`, HTTPS-then-HTTP) → local Ollama → gateway (`AITHER_GATEWAY_EMBEDDINGS_URL`) → auto-deploy a local vLLM embeddings container if a GPU + Docker are present (opt out with `AITHER_EMBED_AUTODEPLOY=0`) → CPU sentence-transformers (384-d, degraded) → feature-hash (384-d, degraded, always works). Every batch is dimension-tagged so callers never silently mix 768-d and 384-d vectors. `get_default_embedder()`, `embed_texts()`, `embed_one()`, `get_provider()`, `reset_provider()`.\n\n### Changed\n- **`GraphMemory` now defaults its embedder to the canonical provider** (opt out with `AITHER_GRAPH_EMBEDDER=legacy`), so every adk agent shares one 768-d space. Added a meta-table dimension guard: the index is pinned to its first embedding's dimension and later different-dimension vectors are refused (kills 768↔384 mixing that silently poisons cosine similarity).\n\n## [2.13.2] - 2026-07-01\n\n### Added\n- **Session persistence**: AitherShell now auto-saves the active session_id to `~/.aither/config.yaml` after each chat turn and auto-restores it on the next shell launch. Multi-turn conversations survive across restarts — the messages were always persisted in SQLite + JSON, only the session pointer was missing.\n- `/new` command to start a fresh session (clears saved session_id).\n- `/sessions` command to list recent sessions with agent name, message count, and timestamp. Resume any with `/resume <id>`.\n\n## [2.13.1] - 2026-07-01\n\n### Fixed\n- **CRITICAL:** `deploy_connect()` now sends a HEAD request to verify the GitHub release asset exists before downloading, with a clear error if the asset is missing.\n- **CRITICAL:** Compose file URLs are now pinned to a stable commit hash (configurable via `AITHER_COMPOSE_PIN` env) instead of tracking the mutable `main` branch.\n- **CRITICAL:** `deploy_adk_node()` now falls back to starting `adk-serve` natively when Docker is unavailable, instead of blocking with \"Docker not installed\".\n- **HIGH:** GHCR login failure now blocks deployment immediately with actionable guidance, instead of warning and continuing to a confusing image-pull failure.\n- **HIGH:** Health checks now stream the last 30 lines of Docker Compose logs on failure, so the user can see why a container didn't start.\n- **HIGH:** vLLM port allocation now probes for conflicts before assigning; if port 8200 is busy, workers are automatically remapped to the next free port.\n- **MEDIUM:** Compose file downloads are now cached with ETag; re-running `adk deploy node` skips re-downloading an unchanged file.\n- **MEDIUM:** DGX Spark discovery now respects `AITHER_DGX_HOSTS` env (comma-separated) for custom network topologies, instead of only checking `spark.local` and `192.168.0.33`.\n- **MEDIUM:** Cloud API keys (Anthropic, OpenAI, DeepSeek) are now validated with a lightweight probe during `adk setup` infra scan, so invalid keys surface early.\n- **MEDIUM:** `deploy_connect()` now validates the extracted extension has a `manifest.json` and warns if it's missing.\n\n## [2.13.0] - 2026-07-01\n\n### Removed\n- **`adk.faculties.memory_graph`** — the pickle-based `MemoryGraph` module is deleted. `from adk.faculties import MemoryGraph` now resolves to the canonical `adk.graph_memory.GraphMemory` for back-compat.\n- **`adk.platform.memory`** — the entire legacy memory subsystem (`MemoryManager`, `UnifiedMemorySystem`, `GameEngine`, `StoryboardEngine`, `AnchorGenerator`) is deleted. Agent memory is `adk.memory.Memory` + `adk.graph_memory.GraphMemory`.\n\n### Changed\n- `adk work` command now uses `GraphMemory` instead of the removed pickle-based faculty graph.\n- `adk.MemoryGraph` and `adk.faculties.MemoryGraph` lazy exports now resolve to `adk.graph_memory.GraphMemory`.\n- `agent.set_memory_graph()` kept for back-compat but documented as deprecated; agents already wire `GraphMemory` automatically in `__init__`.\n- Platform startup (`adk.platform.infrastructure.startup`) gracefully handles missing `MemoryManager`.\n- Platform CLI module listing and health check updated to reflect removed memory module.\n\n## [2.12.7] - 2026-07-01\n\n### Deprecated\n- `adk.faculties.MemoryGraph` — emits `DeprecationWarning` on instantiation; use `adk.graph_memory.GraphMemory` (SQLite-backed, governed, embedding-native) instead.\n- `adk.platform.memory` package — the legacy `MemoryManager`, `UnifiedMemorySystem`, `GameEngine`, and `StoryboardEngine` modules now emit `DeprecationWarning` on import; use `adk.memory.Memory` and `adk.graph_memory.GraphMemory` for new code.\n\n### Changed\n- Updated `adk.faculties` docstring and usage examples to point at the canonical memory surface.\n\n## [2.12.6] - 2026-07-01\n\n### Changed\n- Trimmed public package debt by removing grid runbook artifacts and shell/room binary placeholder directories from published artifacts.\n- Removed stale public-sync allowlist entries for deleted phase test files and added explicit public-sync guards for binary placeholder directories and grid artifacts.\n- Updated the public README grid note so it no longer links to runbook files excluded from the SDK package.\n\n## [2.12.5] - 2026-07-01\n\n### Added\n- Added best-effort fleet memory sync from local ADK memory into Nexus/Qdrant-compatible RAG stores, including batch catch-up and vector-search recall hooks.\n- Added saved config/env support for fleet memory URL, collection, and sync mode.\n\n### Changed\n- Excluded FormBridge runtime/test internals from public ADK packaging and public repo sync.\n\n## [2.12.4] - 2026-07-01\n\n### Added\n- Added a hot-reload endpoint for installed agent packs so newly applied pack tools can be picked up without restarting `aither-serve`.\n\n### Fixed\n- Fixed synchronous `ChatRelay` calls on Python 3.12 when no event loop is running; async WebSocket delivery is still scheduled when a loop exists.\n- Aligned gateway inference tier tests with the current public ladder: free, starter, pro, enterprise, platform.\n\n## [2.12.0] - 2026-06-28\n\n### Added — one-command local inference + upgrade path (click-to-run)\n- **`adk quickstart-local`** — detects hardware, picks a backend\n (Ollama / llama.cpp / vLLM), installs it, downloads a model, verifies, and\n prints next steps. Bootstraps Ollama itself via winget/brew/install.sh when\n missing (no \"go install it\" dead-ends). Default model `gemma4:e2b`.\n- **`adk backend switch <ollama|llamacpp|vllm>`** (+ `backend status`) — migrate\n the active deployment between engines; re-points config and smoke-tests.\n- **`adk install pack:<name>`** (+ `adk install list` / `adk packs`) — install\n ready-made agent packs (openclaw, hermes, claude-code) into `~/.aither/agents`.\n\n### Fixed (found via live end-to-end testing on Windows + CUDA)\n- quickstart-local reported success on a dead endpoint (smoke test was warn-only);\n a failed smoke now fails the command, with a service-readiness wait.\n- `--model` resolved to `None` (getattr default never fired) → uses the real default.\n- Smoke test: 30s→180s timeout for cold model load; accepts reasoning-model output\n (empty `content` at low token budgets is no longer a false failure).\n- `adk install pack:<name>` colon syntax (argparse rejected it before dispatch).\n- `adk backend status` health probe used a nonexistent `/health` path → 404; now\n probes `/v1/models` (uniform OpenAI-compatible liveness for all three backends).\n- Windows: removed non-ASCII glyphs from printed strings that crashed the cp1252\n console (a `✓` even made a successful install report failure).\n\n## [2.11.1] - 2026-06-14\n\n### Fixed — `adk host` / `adk login` device flow (autonomous onboarding)\nFound driving `adk host` against production; all broke real device-flow login:\n- **Device flow hit the portal FRONTEND, not AitherIdentity.** `adk login`/`adk host`\n POSTed `/auth/device/code` to portal/veil.aitherium.com, which 307-redirects API auth\n to `/login`. Added `_resolve_identity_url()` mapping the aitherium.com topology to\n `idp.aitherium.com` (the Identity API); localhost/bespoke pass through unchanged.\n- **Cloudflare 403'd urllib.** `idp.*` sits behind Cloudflare, which blocks urllib's\n default `Python-urllib/3.x` UA. Device-code + poll requests now send a real\n User-Agent + Accept (verified urllib→idp 200, was 403).\n- **`adk host` now self-authenticates via device flow** when there's no token, and\n **retries via device flow on a 401/403** (a stale saved token no longer dead-ends\n registration).\n\n## [2.11.0] - 2026-06-14\n\n### Added — attach your own MCP \"hands\" to a self-hosted agent\nA self-hosted agent now has the full brain (your model) · body (this loop) · **hands**\n(your tools) trifecta. The AitherOS platform relays the MCP servers you registered for\nyour agent in the `/stream` request body as `mcp_endpoints`, and the loop wires their\ntools into the ReAct turn:\n- **`adk/mcp_endpoint_tools.register_mcp_endpoint_tools(agent, endpoints)`** — speaks MCP\n JSON-RPC (`tools/list` to discover, `tools/call` to invoke) and registers a namespaced\n proxy tool (`{endpoint}__{tool}`) for each advertised tool. Mirrors `app_proxy_tools`.\n Best-effort: an unreachable endpoint registers nothing and never raises.\n- **`POST /stream` and `POST /chat/stream`** accept an optional `mcp_endpoints` list\n (`[{name, url, [headers]}]`); `_aitheros_stream` attaches them to the agent before the\n turn. Existing callers are unaffected (the field is optional).\n\n## [2.10.1] - 2026-06-14\n\n### Fixed — self-hosted `aither-serve` was crash-on-startup (2.10.0 regression)\nA fresh `pip install aither-adk && aither-serve --backend deepseek` could not boot. Found\nand fixed while standing up a real self-hosted DeepSeek agent end-to-end:\n- **Startup `NameError: name 'port' is not defined`** in three lifespan helpers\n (`_join_aithernet`, `_init_a2a_server`, `_start_mesh_hosting`) — they referenced a bare\n `port` no longer in scope. Now use `config.server_port`, and `main()` writes the resolved\n `--port`/`--host` back to `config` so every lifespan helper and the invoke_url see the\n actual bound port.\n- **Fleet self-registration crashed the whole server** — the registration handlers caught\n `(ImportError, RuntimeError, OSError, ConnectionError)` but **not** `httpx.HTTPError`, so a\n `RemoteProtocolError`/connect error talking to the control plane took the agent down. All\n network-facing lifespan handlers now also catch `httpx.HTTPError` (non-fatal, as intended).\n- **`--backend` was ignored when `AITHER_API_KEY` was set** — `_auto_detect` picked the cloud\n gateway before the explicit backend, and `_try_elysium_fallback` then replaced the chosen\n provider. An explicit backend (deepseek/openai/anthropic/your own vllm/ollama) now always\n wins; \"auto\"/\"gateway\" still flow through detection. The operator's own brain is never\n silently hijacked.\n- **Approved-tool trace truncated after `/sessions/{id}/confirm`** — the SSE relay treated\n `AgentResponse.tool_calls_made` (a `list[str]`) as dicts (`tc.get(...)`), raised\n `AttributeError`, and an over-narrow `except` swallowed it so the client saw only\n `session_start` + heartbeats. The relay now handles string tool names, the `except` is\n broad (always emits a typed `error` + terminal `complete`), and the module-level\n `_strata_ingest` `NameError` + Chronicle `session_id` kwarg mismatch are fixed.\n\n## [2.10.0] - 2026-06-13\n\n### Added — human-in-the-loop tool approval (`adk/approval.py`)\n- **Pause before a gated tool, resume on decision.** An agent can now pause its turn before\n executing a sensitive tool and wait for a human's allow/deny — so a managed control plane\n (or any operator) can gate tool use without forking the loop.\n- **Policy via `AITHER_TOOL_APPROVAL`** — a comma-separated list of tool names (optionally\n `agent:tool`), or `*` for every tool. Empty = no gating (unchanged behavior).\n- **Survives restarts / days-later approvals.** The paused turn (user message + pending tool\n calls) is persisted to disk keyed by `session_id`; resume is a fresh request, not a held\n connection. Decisions are recorded per `(session_id, tool_name)`.\n- **`AgentResponse.requires_action` / `.pending`** — set when a turn paused; `pending` lists\n the gated tool calls awaiting a decision.\n- **`AitherAgent.resume(session_id, decisions)`** — records the decisions and re-runs the\n paused turn (the gate consumes them: allowed tools execute, denied tools get a denial\n observation, the turn continues — and may pause again on a different gated tool).\n- **Server:** `POST /sessions/{id}/confirm` records decisions and streams the resumed turn;\n `/stream` emits a `requires_action` event when a turn pauses.\n\n## [2.9.0] - 2026-06-13\n\n### Added — universal LLM continuation primitive (`adk/llm/continuation.py`)\n- **`run_continuation()` + `stitch()`** — one shared continue-until-complete primitive. When a\n completion stops because it hit the output token cap (`finish_reason == \"length\"`), it continues\n the generation and STITCHES the chunks (overlap-dedup + restart-detect) into a complete answer,\n instead of every call-site hand-rolling its own retry. Bounded three ways (max rounds AND total\n output ceiling AND a no-growth break), and it never mutates the caller's message history.\n- **`LLMProvider.chat_with_continuation()`** and **`LLMRouter.chat_with_continuation()`** — every\n provider AND the router inherit the primitive, so any call-site gets continuation for free.\n- Kill-switch `ADK_LLM_CONTINUATION=off`; tunables `ADK_LLM_MAX_CONTINUATIONS`,\n `ADK_LLM_MAX_TOTAL_OUTPUT_TOKENS` (defaults: 2 rounds / 8192 tokens).\n\n### Changed\n- The ReAct loop's truncation recovery now uses the shared primitive. The previous inline\n doubling-retry REPLACED the partial response (losing earlier text in `resp.content`) and injected\n continuation scaffolding into the live message history; it now STITCHES and leaves history clean.\n Tool-call turns are never continued as text.\n\n### Included — previously-committed-but-unreleased changes\n- 2.8.2: TLS verification on by default (`verify=False` removed).\n- 2.8.1: baked license + pack-signing public keys.\n- 2.8.0: version-bump release.\n\n## [2.7.0] - 2026-06-05\n\n### Added — provider-agnostic LLM cost levers (at the `LLMProvider` ABC)\n- **Prompt caching.** New `ProviderCapabilities` (`prompt_cache: explicit|automatic|none`, `batch`) and\n normalized `LLMResponse.cache_read_tokens`/`cache_write_tokens`, so the \"tokens saved\" meter never\n depends on which backend served the turn. Anthropic inserts `cache_control` breakpoints over the stable\n system+tools prefix; OpenAI/DeepSeek caching is automatic and their `cached_tokens` are now surfaced\n (previously discarded). Default-on in `AitherAgent.chat`; override with `cache=False`.\n- **ResponseCache** (`adk/llm/cache.py`) — exact-match response cache with a pluggable backend, wired into\n `LLMRouter` as an opt-in (`LLMRouter(response_cache=…)` + per-call `cacheable=True`). OFF by default — no\n determinism change unless opted in.\n- **Gemini provider** (`adk/llm/gemini.py`) with explicit `cachedContents` caching; registered in the router.\n- **Async batch runner** (`adk/llm/batch_runner.py`) — `run_batch()` over the Anthropic Message Batches /\n OpenAI Batch APIs (~50% off) with a transparent fallback to concurrent `chat()` where unsupported.\n- **Graph-RAG retriever interface** (`adk/graph_retriever.py`).\n\n### Added — principal authority (prompt-injection defense enforced outside the model)\n- `adk/auth.py`: `Principal` / `AuthContext` / `PrincipalResolver` (+ deny-all default).\n- `ToolRegistry.execute(name, args, auth=…)` — a default-deny authorization gate at the tool boundary, so a\n forbidden tool call is blocked regardless of what the LLM emits. `ToolDef` gains `required_clearance` /\n `action_class`. Threaded through `AitherAgent.chat`; `auth=None` is fully backward-compatible.\n- `adk/channel_auth.py`: Telegram webhook-secret + CEO-id checks and HMAC-signed approvals.\n\n## [2.6.4] - 2026-06-05\n\n### Fixes\n- **Loop-guard nudges corrupted the message history → DeepSeek 400 mid-run.**\n When the ReAct loop emitted multiple tool calls in one assistant turn and the\n loop guard fired a WARN/BLOCK/CIRCUIT_BREAK on one of them, it spliced a\n standalone `system` nudge message *between* the tool results. DeepSeek (and\n strict vLLM chat templates) require an assistant `tool_calls` message to be\n followed by exactly one `tool` result per `tool_call_id`, contiguously — so the\n interleaved message produced `400 … insufficient tool messages following\n tool_calls message` and aborted the turn (this is reliably hit after crossing\n the 15-call circuit-break threshold, where every subsequent call WARNs).\n Per-call guidance is now folded into that call's own `tool` result, and any\n standalone steering is deferred to a single message AFTER the whole batch —\n preserving the `tool_calls`↔`tool_results` pairing every backend requires.\n Regression test: `deep-research-agent/tests/test_msg_structure.py`\n (loopguard-flood scenario).\n\n## [2.6.3] - 2026-06-04\n\n### Fixes\n- **Transient provider errors killed a run on the first blip.** A single\n `502 Bad Gateway` / `503` / `529 overloaded` / `429` from the upstream API\n aborted the whole request — the Anthropic provider had **no retry at all**, and\n the OpenAI-compatible provider only retried `429`. Both now retry transient\n statuses (`408/429/500/502/503/504/529`) with `Retry-After`-aware exponential\n backoff (3 attempts), and surface the provider's body if all retries are\n exhausted. Non-transient `4xx` (e.g. `400`) still fails fast. Applies to\n `chat` and `chat_stream` on both providers.\n\n## [2.6.2] - 2026-06-04\n\n### Fixes\n- **DeepSeek (and strict OpenAI-compatible backends) 400'd on long tool\n conversations.** The ReAct loop legitimately injects mid-conversation\n `system` steering messages (the \"[DIMINISHING RETURNS]\" hint after several\n low-output tool iterations, loop-guard nudges). OpenAI tolerates a non-leading\n `system` message; DeepSeek rejects it with a 400. Long research runs\n (many `fetch_url` calls) reliably tripped this. Fix: non-leading `system`\n messages are demoted to `user` at the OpenAI-compatible provider boundary\n (`_demote_nonleading_system`) — steering intent preserved, payload valid on\n every backend; leading system prompt(s) untouched.\n- **Opaque HTTP errors.** `resp.raise_for_status()` discarded the provider's\n response body, so a 400/422 surfaced as a bare `Client error '400 Bad\n Request'` with no cause. `_ensure_ok` now includes the provider's actual\n message (e.g. `context_length_exceeded`, `Invalid 'messages'`) in the raised\n error, for both `chat` and `chat_stream`.\n\n## [2.6.1] - 2026-06-04\n\n### Fixes\n- **`MCPServer.mount()` returned HTTP 422 for every `/mcp` call.** Under\n `from __future__ import annotations` the route handler's `request: Request`\n annotation is a string that FastAPI resolves against the *module* globals, but\n `Request` was imported locally inside `mount()` — so FastAPI couldn't resolve\n it and treated `request` as a required query param. The MCP server endpoint\n (`initialize`, `tools/list`, `tools/call`) was unusable when mounted. FastAPI\n symbols are now imported at module level (guarded; the `[node]` extra still\n gates actual use). Any agent's tools can now be exposed as a working MCP\n server with `MCPServer(...).mount(app)`.\n\n## [2.6.0] - 2026-06-04\n\n### Streaming + scaffolding\n- **`AitherAgent.stream_chat`** — reliable streaming sibling of `chat()`: native\n tool-calling loop (concludes on its own, unlike the text-protocol `stream_react`)\n that emits live `tool`/`tool_result` events and streams the answer as `token`\n events. `token_delay` paces tokens for a typing effect.\n- **`adk new <template>`** — scaffold a full, runnable template app. Ships the\n `deep-research` template (a cited web-research agent: pack + server + web UI):\n `adk new deep-research`.\n\n### Fixes\n- **BYO-key no longer community-capped.** The free-tier monthly token cap now applies\n only to the metered Aitherium gateway backend, never to a user's own provider key\n (Anthropic/OpenAI/DeepSeek/Ollama/vLLM). Previously it could brick a self-hosted\n agent after ~100k tokens on the user's own key.\n- **Stronger, grounded memory recall.** `chat()` now injects the top-6 knowledge-graph\n hits (untruncated) with a hard no-fabrication instruction — was top-3 truncated to\n 200 chars, which surfaced partial facts and let weaker models invent the rest.\n- **Tool-arg coercion.** `ToolRegistry.execute` coerces LLM-supplied args toward each\n parameter's annotated type, so a tool no longer crashes when a model passes a list\n or string where an int/float/bool was expected.\n\n### Docs\n- New `docs/AGENT_DEV_GUIDE.md` — the golden path for building agents/packs.\n\n## [2.5.0] - 2026-06-04\n\n### Continual learning — agents that learn from runs and reuse memory\n\nAgents now improve the second time they do a task instead of starting from scratch.\n\n### Added\n- **Recall-before**: `AitherAgent.chat()` searches the local `SkillStore` and injects the\n top matching learned skills as a system block, so the model reuses proven procedures.\n- **Learn-after**: `_learn_after()` runs after each response — reinforces (`success_count++`)\n any skills that were recalled+reused, and extracts + saves a NEW skill from successful\n multi-tool runs via `SkillExtractor`. Wired at both `chat()` return paths.\n- Gated by `AITHER_SKILLS` (default on). The previously-unused `SkillStore`/`SkillExtractor`\n are now connected to the run loop.\n\n### Notes\n- Pairs with the platform-side LearnedProcedure substrate (recipes → scored procedures →\n skills/tools/A2A/packs → MCTS priors → evolution). The SDK shares memory via the Spirit\n bridge when configured.\n\n## [2.0.0] - 2026-06-02\n\n### Open-core boundary (BREAKING)\n\nThis release draws a single, enforced free/paid line. The free tier stays\ngenuinely useful (real agent, typed + graph + code memory, ReAct, ~essential\ntools); paid tiers unlock the *scale* capabilities via portal.aitherium.com.\n\n### Added\n- **`adk/licensing.py`** — the entitlement keystone. Resolves a tier\n (COMMUNITY by default), verifies optional portal-signed `~/.aither/license.json`\n (Ed25519), and answers `can_use_fleet/channels/cron/swarm/auto_neurons`,\n `is_agent_licensed`, `max_effort`, etc. **Fail-closed**: no/invalid/expired\n license → free tier; an unsigned license never grants premium.\n `AITHER_TENANT_SLUG=aitherium` → unrestricted INTERNAL tier.\n- **Moat guard** (`scripts/check_moat_boundary.py`) wired into the publish\n workflow — blocks any wheel that leaks the moat or drops the keystone.\n- Tests: `tests/test_licensing.py`, `tests/test_moat_boundary.py`.\n\n### Changed (gating — fail-closed)\n- Free tier effort is capped at 3 (reasoning effort 7–10 needs Professional).\n- Monthly token budget hard-block on the free tier (no silent overspend).\n- Gated behind a paid tier: fleet mode (`FleetConfig`), cross-agent delegation\n (`AgentForge.delegate`), channel adapters, cron scheduler, proactive\n auto-neurons, and `swarm`/`swarm_code` dispatch.\n\n### Removed\n- **`adk/nanogpt.py`** (on-device training IP) removed from the published SDK\n and the public repo; relocated to the internal AitherOS moat. Excluded from\n the wheel as defense-in-depth.\n- Pre-2.0 releases shipped this IP and ungated capabilities and are being\n yanked (PyPI) / removed (GitHub releases). See `scripts/yank_leaky_releases.py`\n and `scripts/purge_public_leaks.sh`.\n\n## [1.20.0] - 2026-05-27\n\n### Fixed\n- **Pack discovery path mismatch** — `_find_local_packs()` now scans both\n `~/.aither/packs/` and `~/.aitheros/packs/`, so packs installed via\n `adk pack install` are discovered by `adk-workspace`.\n\n### Added\n- **Auto-register on pack install** — `adk pack install` now auto-registers\n with the portal if the pack contains an `agent.yaml` with a `portal` section.\n One command does download + extract + register.\n- **Flux event on agent registration** — `developer_portal.py` emits\n `agent.registered` Flux event so the portal fleet UI updates in real-time.\n\n## [1.15.0] - 2026-05-26\n\n### Added\n- **Cloud memory** — `adk quickstart --cloud` now tests gateway memory and\n auto-configures Spirit URL so memories persist across devices via\n `gateway.aitherium.com/v1/memory/teach` and `/v1/memory/recall`.\n- **Config→env export for cloud mode** — `Config.from_env()` now exports\n `AITHER_CLOUD_MODE`, `AITHER_SPIRIT_URL`, and spirit path env vars from\n saved config so Memory and other modules pick them up without needing\n explicit env vars set by the user.\n- **Cloud Quick Start section** in README — 3-command setup for users who\n just want a Claude/GPT-4/DeepSeek API key and the full agent harness.\n- **`--memory` profile for node deployment** — `adk deploy node --memory`\n adds Spirit (8087) + WorkingMemory (8101) containers for persistent\n vector memory. Without this, agents use local SQLite only.\n- **Configurable vLLM in node compose** — `AITHER_VLLM_MODEL`,\n `AITHER_VLLM_GPU_UTIL`, `AITHER_VLLM_CTX_LEN`, `AITHER_VLLM_QUANT_ARGS`\n env vars let `adk setup` write GPU-detected tier settings to `.env`\n and have the node compose pick them up (TQ4 for small GPUs, BNB for large).\n\n### Fixed\n- **Memory cloud routing** — `Memory._spirit_teach()` and `_spirit_recall()`\n now use configurable paths and auth headers for gateway proxy. Previously\n cloud-only users silently lost all Spirit memory calls.\n\n## [1.9.0] - 2026-05-24\n\n### Added\n- **Composable ToolPack system** — agents can load tool packs from\n `.toolpack.yaml` directories. Drop a pack in `~/.aitheros/packs/` or\n set `AITHER_PACKS_DIR`, and any agent discovers it automatically.\n- **`tools.packs` in agent.yaml** — list tool pack IDs to auto-load\n (e.g. `packs: [crm-tools, calendar-pro]`).\n- **`register_tool_packs(agent, pack_ids, packs_dir)`** in `builtin_tools` —\n programmatic pack loading for agents.\n- **`AITHER_TOOL_PACKS` env var** — comma-separated pack IDs loaded on\n agent init (alternative to YAML config).\n- **Unknown builtin categories resolve as pack IDs** — `tools.builtin:\n [file_io, crm-tools]` now works: `crm-tools` is looked up as a pack.\n\n## [1.8.0] - 2026-05-24\n\n### Removed\n- **11 dead modules** (3,023 lines) — `agent_deploy`, `cloud_deploy`, `create`,\n `phonehome`, `sdk_bridge`, `sovereign`, `telemetry`, `telemetry_config`,\n `error_reporter`, `anonymizer`, `package`. None were imported anywhere.\n- **29 ghost exports** from `__init__.py` — `secrets`, `otel`, `fs_sandbox`,\n and `vector_memory` modules were listed in `__all__` and `__getattr__` but\n the backing files never existed. Accessing them raised `ModuleNotFoundError`.\n- **Stale dist/ artifacts** — removed old wheels/tarballs (v0.4.0–v1.4.1, 11 MB).\n- **Orphan test scripts** — `test_phase1.py` and `test_phase23.py` (one-off\n validation scripts not part of the pytest suite).\n\n### Fixed\n- **Version mismatch** — `__init__.__version__` now matches `pyproject.toml`\n (was stuck at 1.6.0).\n\n## [1.5.0] - 2026-05-23\n\n### Added\n- **`--sovereign` flag for `adk deploy node`** — registers the node with the\n Aitherium hub via federation after deployment. Saves federation credentials\n to `~/.aither/.env.federation`. Accepts `--hub` (custom hub URL) and\n `--tenant` (tenant slug) options. Non-fatal: node runs standalone if\n registration fails.\n- **Federation → fleet cross-post** (Genesis) — sovereign nodes that register\n or heartbeat via `/federation/register` and `/federation/heartbeat` now\n automatically appear in the fleet dashboard (`/fleet/deployments`) with\n `node_type: \"sovereign\"` and live status/metrics.\n- **\"Deploy Sovereign Node\" section on Connect page** (AitherVeil) — Section 8\n with ADK deploy commands, fleet dashboard links, and provision page links.\n- **`deploy-node` action in tunnel proxy** (AitherVeil) — Connect page and\n external callers can now request bootstrap scripts via\n `POST /api/tunnel/devworkspace?action=deploy-node`.\n\n### Fixed\n- **Fleet provision page** — API URL corrected from `/api/fleet/provision` to\n `/api/bridge/genesis/fleet/provision` (matches bridge proxy pattern).\n- **Fleet detail page** — API URL corrected from `/api/fleet/deployments/` to\n `/api/bridge/genesis/fleet/deployments/` (same fix).\n- **Dockerfile** — entry point corrected from `aither-serve` to `adk-serve`\n (matches `pyproject.toml` script registration). Version label updated.\n\n## [1.4.1] - 2026-05-22\n\n### Fixed\n- **macOS executable builds** — dropped `--target-arch universal2` from\n `packaging/build_executable.py`. PyPI wheels for `pydantic_core` and other\n native deps are arch-specific, not fat binaries, so universal2 always failed\n with `IncompatibleBinaryArchError`. Now builds for the runner's native arch\n (`aither-macos-arm64`).\n- **Release workflow strategy** — added `fail-fast: false` so a single\n executable build failure no longer cancels the other OS jobs.\n- **PyPI publish** — added `skip-existing: true` so re-runs of the same\n version don't fail when the artifact already exists.\n- **GitHub Release job** — now runs whenever `pypi` succeeds even if some\n executable matrix entries fail, so the release page is always created with\n whatever binaries did build successfully.\n\n## [1.4.0] - 2026-05-22\n\n### Added\n- **Sovereign template now ships with `agent_core` 5-tier memory** — generated\n backends include the canonical `agent_core/` package (working → episodic →\n semantic → procedural → identity) plus `agent_memory.py` helper, vendored\n from `portal-kit-backend`.\n- `aiosqlite>=0.20.0` added to sovereign template `requirements.txt` for\n local agent_core SQLite storage.\n- Document tombstoning: `evict_cached_conversations_referencing` is wired\n through document-delete handlers in consumer apps so memories citing\n deleted documents are blocked from recall.\n- New tests: 13 smoke tests for `portal-kit-backend/agent_core` covering\n promotion thresholds, decay rates, store roundtrip, tombstone recall\n blocking, circuit breaker state machine, and chat-turn recording.\n\n### Changed\n- `scripts/vendor_agent_core.py` now syncs canonical `agent_core` to 4\n targets: WorkspaceRuntime, ADK sovereign template, and the GargBot +\n Chelle consumer mirrors (previously only 2).\n\n## [1.2.1] - 2026-05-21\n\n### Added\n- `adk train` command group — 6 subcommands: status, launch, logs, cancel, runs, register-gpu\n- `/slash-commands` endpoint — ADK server auto-exposes all 35 CLI commands as structured JSON manifest for AitherShell\n- `/cli/execute` endpoint — AitherShell can run any CLI command through the server\n- `build_command_manifest()` — introspects full argparse tree (commands, args, choices, subcommands)\n- `_register_commands()` extracted from main() for shared parser construction\n\n### Changed\n- `_get_genesis_url()` now probes: Genesis → ADK server → Aitherium cloud gateway (works without Genesis)\n\n## [1.2.0] - 2026-05-21\n\n### Added\n- **Runtime backend switching** — `agent.switch_backend(\"deepseek\")` and `agent.llm.switch_backend()` change provider without recreating agent\n- **Hybrid reasoning** — `agent.set_reasoning_backend(\"anthropic\")` routes effort 7+ to cloud API while keeping local orchestrator\n- **First-class DeepSeek provider** — `deepseek` in effort model table with `deepseek-chat` and `deepseek-reasoner`\n- **TQ4 quantization tiers** — `nano` (6GB, TurboQuant 4-bit), `standard-tq4` (12GB, both models TQ4), `hybrid-tq4` (6GB + cloud reasoning)\n- **`adk backend` CLI** — `list`, `set`, `set-reasoning`, `test` subcommands\n- **`adk quickstart`** — unified first-run wizard (setup + auth + shell)\n- **`adk tools`** — list local + MCP tools with tier markers\n- **`adk backup`** — tarball export of `~/.aither/`\n- **`adk ingest`** — feed docs/files into knowledge graph\n- **`adk setup nemotron`** shortcut — alias for `--tier lite`\n- **`--reasoning-api`** flag in setup — `anthropic`, `openai`, `deepseek`, `gateway`\n- **`--dgx-spark URL`** flag — remote vLLM endpoint configuration\n- **DGX Spark auto-detection** — scans `spark.local`, `192.168.0.33`, `AITHER_DGX_URL` env\n- **Post-setup smoke test** — sends real inference request after container startup\n- **Schema migration system** — version table + migration runner in `memory.py` and `graph_memory.py`\n- **Configurable vLLM port scan** — `AITHER_VLLM_PORTS` env var for custom ports\n- **`aithershell` entry point** — alias for `adk` in pyproject.toml\n- Shell auto-downloads binary on first `adk shell` (no `--install` needed)\n- Shell pre-flight check warns if no backend detected\n- Shell passes full config via env vars (API key, tenant, inference URL)\n- DGX/Remote and Cloud API checks added to `adk doctor`\n- Config fields: `reasoning_backend`, `reasoning_api_key`, `deepseek_api_key`, `dgx_url`, `vllm_extra_ports`\n\n### Fixed\n- `test_aeon_chat_basic` timeout — mock scope was exiting before request\n- `adk doctor` check_disk timeout — cap rglob at 5000 files\n\n## [1.1.7] - 2026-05-19\n\n### Added\n- Remote agent pipeline — `adk onboard` → fleet dispatch → remote inference\n- Agent-to-agent federation with mesh relay\n- `adk connect` for desktop mesh enrollment\n\n## [1.1.6] - 2026-05-19\n\n### Fixed\n- Tool call execution recovery across all backends\n- Hermes tool call fallback parsing for vLLM\n\n## [1.1.5] - 2026-05-19\n\n### Fixed\n- Tool call execution + Hermes fallback parsing improvements\n\n## [1.1.0] - 2026-05-18\n\n### Added\n- Multi-channel gateway (Telegram, Discord, Slack, webhook)\n- Aeon group chat with 7 presets\n- Skills auto-extraction from multi-step sessions\n- MCP stdio server for Claude Code integration\n- `adk gateway`, `adk cron`, `adk skills`, `adk soul` commands\n- SOUL.md import/export for portable agent identity\n- Elysium cloud device-flow authentication\n\n## [1.0.0] - 2026-05-18\n\n### Breaking\n- Package consolidated — `aither_adk` namespace replaced by `adk` + `adk.platform`\n- 93 duplicate files removed from `AitherOS/packages/aither_adk/`\n\n### Added\n- Standalone core primitives (`adk.core`: Agent, Tool, Memory, ModelBackend, Capability, Trace)\n- `adk.platform` sub-package merging all platform toolkit modules\n- Compatibility shims for legacy `from aither_adk.*` imports\n\n### Changed\n- Version synced across pyproject.toml, `adk/__init__.py`, `adk/platform/__init__.py`, npm package\n- Classifier promoted from Alpha to Beta\n\n## [0.16.0] - 2026-04-16\n\n### Swarm Coding Engine & Repowise Integration\n- **`repowise_search` tool** — Semantic + keyword hybrid code search via Repowise, with ripgrep fallback\n- **`swarm_code` tool** — Dispatch complex tasks to 11-agent swarm pipeline (ARCHITECT->SWARM->REVIEW->JUDGE)\n- **`agent.swarm()`** — Async convenience method with configurable mode, effort, timeout\n- **`agent.code_search()`** — Async convenience method returning structured results\n- New tool categories: `repowise` and `swarm` in TOOL_CATEGORIES\n- `repowise_search` and `swarm_code` added to `__init__.py` exports with lazy loading\n- `IDENTITY_DEFAULTS` updated: repowise for code-focused agents, swarm for orchestration agents\n- Standalone graceful degradation: repowise falls back to ripgrep, swarm returns structured error\n\n## [0.13.0] - 2026-04-02\n\n### Graph Faculties — Local Knowledge for Every Agent\n- **CodeGraph** (2,799 lines) — Full Python AST indexer with call graph, keyword/semantic/hybrid query, embedding matrix cache, incremental re-indexing, multi-hop chain expansion\n- **MemoryGraph** (1,339 lines) — Graph-based persistent agent memory with 10 edge types, hybrid query (keyword + semantic + graph expansion), multi-hop recall, pickle persistence\n- **EmbeddingProvider** — 4-backend fallback chain: sentence-transformers (GPU/CPU) -> Ollama -> Elysium cloud -> feature hashing (zero deps)\n- **BaseFacultyGraph** — Abstract base with HMAC-SHA256 validated pickle persistence\n\n### Agent Integration\n- `agent.set_code_graph(cg)` — auto-registers `code_search` + `code_context` tools\n- `agent.set_memory_graph(mg)` — auto-registers `remember` + `recall` + `memory_stats` tools\n- Both graphs inject context into LLM prompts automatically during chat\n\n### Zero-Config Onboarding\n- `adk start` / `adk` (no args) — auto-detect project, index code, detect LLM, persistent memory, interactive chat\n- Works for any directory: Python codebases, doc folders, mixed workspaces\n- Auto-detects LLM: Ollama -> vLLM -> Elysium -> OpenAI -> Anthropic\n- Per-project persistent memory in `~/.aither/memory/<project>`\n- `adk index <path>` — standalone indexing with progress bar and stats\n\n### MCP Gateway\n- `POST /v1/embeddings` — OpenAI-compatible embedding proxy to vLLM-embeddings:8209\n- Available to all tiers (embeddings are free)\n- ADK EmbeddingProvider uses this as Elysium cloud fallback\n\n### Optional Dependencies\n- `pip install aither-adk[graphs]` — numpy for 10x cosine similarity speedup\n- `pip install aither-adk[embedding]` — sentence-transformers + torch for local GPU embeddings\n- `pip install aither-adk` alone — graphs work with feature hashing (zero deps)\n\n## [0.12.0] - 2026-04-01\n\n### Bootstrap & Service Discovery\n- Version handshake -- ADK checks major.minor compatibility with Genesis/Node\n- Background reconnect loop -- ServiceBridge re-probes every 30s in standalone mode\n- Port 8080/8090 documented -- MCP vs OpenAI-compat clearly separated\n- Genesis URL configurable via GENESIS_URL env var (was hardcoded)\n- Standalone mode warning -- visible stderr alert when AitherOS not detected\n- Auto-reconnect on startup when services come online\n\n### Elysium Cloud Inference\n- Unified gateway URL -- gateway.aitherium.com handles auth + billing + inference\n- Streaming inference -- SSE passthrough for /v1/chat/completions with stream=true\n- Auth proxy routes -- /v1/auth/register, /v1/auth/login, /v1/auth/me\n- Billing proxy -- /v1/billing/balance through gateway\n- AitherConnect Elysium fallback -- cloud inference when local Genesis is down\n- AitherDesktop Elysium fallback -- third-tier chat fallback after Node\n\n### Infrastructure\n- /discovery endpoint on Genesis -- unified service URLs/versions/health\n- /api/config/services on Veil -- runtime port config for client-side JS\n- Veil healthcheck gates on Genesis -- unhealthy when backend is down\n- Desktop crash detection 90s->60s (threshold 3->2)\n\n## [0.11.0] - 2026-04-01\n\n### Agent Execution Quality (Claude Code Parity)\n- Raise loop guard block threshold from 3 to 4 -- agents get more room for iterative search\n- Soft synthesis nudge for effort >= 4 (no tool stripping, trust the model)\n- max_output_tokens escalation -- retry up to 3x when response is truncated\n- Tool result pairing guarantee -- synthetic error for orphaned tool_use blocks\n- Micro-compaction of old tool results -- save context tokens on long sessions\n- First-turn tool forcing only for effort >= 6 (trust model for lower effort)\n- Diminishing returns detection -- nudge agent when 3+ turns produce < 500 tokens\n- Message normalization -- merge consecutive same-role messages, strip empties\n- LLM retry with exponential backoff (5 retries, 500ms-16s, jitter)\n\n## [0.9.0] - 2026-03-16\n\nThe \"connected world\" release. Cross-platform identity pairing, voice capabilities, and multi-channel integration.\n\n### Added\n- **Pairing**: Cross-platform identity linking (`adk/pairing.py`)\n - `PairingManager` — SQLite-backed identity linking with 6-char pairing codes\n - Link users across Telegram, Discord, Slack, WhatsApp with 10min TTL codes\n - Canonical session IDs for cross-channel conversation continuity\n - `get_session_id()` returns \"user-{id}\" for paired users\n- **Voice**: Speech-to-text and text-to-speech client (`adk/voice.py`)\n - `VoiceClient` — async STT/TTS/emotion via AitherVoice service\n - Convenience functions: `hear()`, `say()`, `feel()`\n - 6 voice options: alloy, echo, fable, nova, onyx, shimmer\n - Emotion detection with intensity scoring\n- New exports: `PairingManager`, `PairingResult`, `PlatformIdentity`, `VoiceClient`, `TranscriptionResult`, `SynthesisResult`, `EmotionResult`\n\n### Changed\n- `__init__.py` exports expanded with pairing and voice symbols\n\n## [0.6.0] - 2026-03-13\n\nThe \"group mind\" release. Multi-agent group chat, creative tools, and Iris identity.\n\n### Added\n- **Aeon**: Multi-agent group chat engine (`adk/aeon.py`)\n - `AeonSession` — persistent group chat with parallel agent execution\n - 7 presets: balanced, creative, technical, security, minimal, duo_code, research\n - Orchestrator synthesis: Aither summarizes all agent responses\n - Serial execution for Ollama, parallel for vLLM/cloud\n - ConversationStore persistence with `type: \"aeon\"` metadata\n - `group_chat()` one-shot convenience function\n- `aither aeon` CLI command — interactive terminal group chat with color-coded agents\n - `-p/--preset`, `-a/--agents`, `-r/--rounds`, `--no-synthesize` flags\n - `reset` and `quit` commands\n- Server endpoints: `POST /aeon/chat`, `GET /aeon/presets`, `GET /aeon/sessions/{id}`\n- Creative tools in builtin_tools: `image_generate`, `image_refine`, `image_search`, `video_generate`\n- Iris agent identity with visual generation capabilities\n- 48 Aeon tests (data models, presets, chat, context, persistence, server, exports)\n\n### Changed\n- `__init__.py` exports: `AeonSession`, `AeonResponse`, `AeonMessage`, `group_chat`, `AEON_PRESETS`\n\n## [0.5.0] - 2026-03-13\n\nThe \"tenant-ready\" release. Multi-tenant admin, permission grants, safety profiles, and a full setup wizard.\n\n### Added\n- `aither setup` interactive setup wizard with hardware detection, model selection, identity config\n- Strata storage backend: SQLite WAL persistence for conversations, memories, knowledge graphs\n- CLI test runner: `aither test` with auto-discovery and parallel execution\n- Permission grants system for MCP tool access control\n- MCP account management tools\n- LLM provider auto-detection for Ollama and vLLM endpoints\n- Apache-2.0 LICENSE file\n- Elysium desktop sync module\n- Comprehensive CLI test suite (493 tests)\n- Strata storage test suite (972 tests)\n- LLM provider tests (82 tests)\n\n### Changed\n- CLI expanded: `aither setup`, `aither test`, `aither bugreport`, `aither doctor`\n- Strata module rewritten as full local-first storage engine\n- Server startup includes Strata initialization\n- README rewritten with clearer quickstart and architecture docs\n\n### Fixed\n- Dead documentation links\n- Python 3.11 compatibility issues\n- Ruff per-file-ignores configuration\n- Identity provisioning edge cases\n- Docker node-gyp native dependency builds (python3 + build tools)\n\n## [0.4.0] - 2026-03-13\n\nThe \"own your AI\" release. Self-hosted agent OS for people who don't want their data on someone else's servers.\n\n### Added\n- Muse agent identity (creative/artistic generation)\n- Port 8120 to vLLM scan for ExoNodes discovery\n- Public roadmap with milestone-based porting schedule\n- Competitive positioning: self-hosted alternative to cloud-locked AI appliances\n\n### Changed\n- LLM router: compute-aware effort routing (replaces effort-level context gating)\n- README rewritten for sovereignty-first messaging\n- Package description updated\n- Promoted from alpha to stable release\n\n### Fixed\n- Version string consistency between pyproject.toml and __init__.py\n- vLLM port scanning now includes port 8120 (ExoNodes)\n\n## [0.3.1] - 2026-03-09\n\n### Added\n- GraphMemory: SQLite knowledge graph with Ollama embeddings and hybrid search\n- NeuronPool: auto-fire pre-LLM data gathering agents\n- NanoGPT: pure Python char-level transformer with LoRA fine-tuning\n- Safety gates: IntakeGuard (input), LoopGuard (recursion), Sandbox (code exec)\n- Event system: EventEmitter with chat/tool/forge event types\n- Builtin tools: identity-based tool selection\n- ServiceBridge: auto-discovery of AitherOS services\n- Streaming: chat_stream with safety gate integration\n- Auth middleware: Bearer token authentication\n- CLI: `aither init` and `aither serve` commands\n\n### Changed\n- Wired 5 previously disconnected modules into agent loop\n- 522 total passing tests (up from 85)\n\n## [0.3.0] - 2026-03-07\n\n### Added\n- Clean-room agent development kit\n- Multi-backend LLM providers (Ollama, OpenAI, Anthropic)\n- AitherAgent class with @tool decorator\n- SQLite conversation memory\n- OpenAI-compatible server (`aither-serve`)\n- 16 agent identities as package data\n- MCP bridge to mcp.aitherium.com\n- Privacy-centric opt-in telemetry\n- Bug reporting CLI and API\n- FastAPI server with OpenAI-compatible endpoints\n- Hardware auto-detection (5 tiers, 11 profiles)\n- Fleet orchestration and multi-agent coordination\n- A2A mesh protocol\n- Federation protocol for cross-instance agent dispatch\n\n### Initial release\n- 85 passing tests\n- Apache-2.0 license\n"}, "259d31177b297f284440823b3f0b94f0cc997876": {"t": 1785748154.8700438, "n": 100201, "h": "ee334dda9a153b9f", "c": "# Changelog\n\nAll notable changes to aither-adk will be documented in this file.\n\n## [2.39.0] - 2026-07-25\n\n### Fixed — containerized agents were unreachable\n\n- **`docker` runtime: added `-i` to `docker run`.** Without it Docker does not\n attach the container's stdin, so *every* containerized stdio agent (acp, mcp)\n silently never received a request. Measured against a real container: no `-i` →\n the child read nothing; with `-i` → the request round-trips. A real customer\n pack *is* a container, so this runtime was effectively unusable.\n- **`runtime.cmd` is now `shlex`-split** for the `docker` and `node` runtimes.\n `cmd: \"python -u server.py\"` was passed as a single argv element, so docker\n tried to exec a binary literally named `python -u server.py`.\n- `Supervisor._build_command()` extracted from `spawn()` so argv is testable\n without launching a process — the missing `-i` survived precisely because it\n wasn't.\n- Env is deliberately **not** forwarded as `-e KEY=value`: that puts credentials\n in argv, readable by any local `ps` / `docker inspect`.\n\n### Fixed — CodeActLoop `cell_timeout` did not bound CPU-bound cells\n\n- `asyncio.wait_for` cannot preempt a coroutine that never yields, so\n `while True: pass` was never interrupted and hung the agent indefinitely. Now\n also bounded by a line-level trace deadline (`CellTimeout`), scoped to the\n cell's own frames so a cell that awaits cannot fire the deadline inside\n unrelated coroutines.\n- The `CodeActLoop` docstring now states plainly that **it is not a sandbox**, and\n names what a cell can actually do (`import os`, `subprocess`, `open(..., \"w\")`).\n Do not run cells from an untrusted source.\n\n### Added — real containment for CodeAct cells\n\n- **`CodeActLoop(executor=DockerCellExecutor())`** runs each cell in a container\n with `--network none`, a read-only root filesystem plus a noexec/nosuid tmpfs,\n `--cap-drop ALL`, `no-new-privileges`, memory and pid caps, a non-root user, and\n `python -I`. The escapes that succeed in-process — writing a host file, network\n egress, running as root — all fail there, and an infinite loop's container is\n **killed** rather than merely interrupted.\n- `return_result()` crosses the process boundary via a stdout sentinel and is\n deliberately **not** `eval`'d back into the host.\n- Trade-off stated plainly: an isolated cell is a fresh process, so there is no\n live host namespace and no cross-cell state. Containment *and* a live namespace\n together need fork-from-warm snapshotting (`adk/forkd_client.py`'s Firecracker\n target), which requires Linux ≥ 5.7 + KVM.\n- The in-process default is unchanged. Do not point CodeAct at untrusted input\n without `executor=`.\n\n### Fixed — TLS and test-suite integrity\n\n- **`adk/cli.py` sent an `Authorization: Bearer` API key over TLS with\n verification disabled**, exposing it to a machine-in-the-middle. Now routed\n through `adk._tls.tls_verify()`.\n- The `verify=False` ratchet regex-scanned raw lines, so docstrings that merely\n *named* the antipattern were reported as violations — noise that hid the real\n hole above. It now walks the AST for actual keyword arguments.\n- Five `test_inference_proxy` failures were stale assertions pinning the\n pre-MicroScheduler routing and a 5-model roster (it is 6 since `aither-bonsai`).\n Assertions now derive from the roster and encode the tier-access rule, so a\n future model addition doesn't break them.\n\n### Fixed — MCP stdio hung silently on Windows\n\n- `adk/mcp_stdio.py` and `adk/shell/mcp_bridge.py` attached stdin with\n `loop.connect_read_pipe`, which fails inside the Proactor loop's read handler\n such that the read future never resolves — the server didn't crash, it just\n never answered. Both now use the new shared `adk.stdio_compat`\n (`ThreadStdinReader` / `ThreadStdoutWriter`), which `acp_server.py` also uses.\n\n### Added — per-install pack credentials are now actually used\n\n- `adk/pack_credentials.py` was orphaned: mint/revoke were never called, so pack\n installs kept using the shared credential the module was written to replace.\n Both install paths now mint, and a reinstall revokes the outgoing install's\n credential before its metadata becomes unreachable.\n- **New `/packs uninstall <pack-id>`** (aliases `remove`, `rm`) — revokes the\n per-install credential first, then removes the pack. Previously the only revoke\n path was a reinstall, so deleting a pack any other way left a live,\n unrevocable credential. The target is confined to the pack root.\n\n### Fixed — packaging\n\n- The brew formula's own `sha256` was left as `PLACEHOLDER_SHA256` on every\n release, so `brew install` could never verify the download.\n `sync_versions.py` now fills it from PyPI and `--check` **fails** when a\n published version still carries a placeholder or a stale digest.\n- `sync_brew` rewrote *every* `url` in the formula, repointing the `httpx`,\n `pyyaml`, `fastapi` and `uvicorn` `resource` blocks at the aither-adk tarball.\n Fixed with `count=1`; the resource digests are now real.\n- `adk/__init__.py`'s source-checkout fallback `__version__` had been stale since\n 2.32.0 because the canonical release path never ran `sync_versions.py`. That\n check now runs in `sync-adk.yml`.\n\n## [2.38.1] - 2026-07-24\n\nRelease plumbing only — 2.38.0 was tagged before the version manifests\n(npm/brew/winget) were synced, so its publish gate failed and it never reached\nPyPI. 2.38.1 is the same code with the manifests in sync. See 2.38.0 below for\nwhat actually changed.\n\n## [2.38.0] - 2026-07-24 (never published — superseded by 2.38.1)\n\n### Fixed — `adk join` could only ever have worked for us\n\n- **`adk join` defaulted identity to `https://localhost:8115`** — a fleet-INTERNAL\n address. `adk join` is the one command a stranger runs on their own GPU box, so on\n any machine not already running AitherOS locally, step 1 (the GitHub device flow)\n hit a refused connection, and so did node registration and the mesh-key issue. The\n conductor default was already public, which is exactly why this went unnoticed.\n Identity now defaults to `https://idp.aitherium.com`.\n\n This fix landed *after* the 2.37.0 tag, so **2.37.0 and every earlier release on\n PyPI still ship the broken default** — 2.38.0 is the first release a new\n contributor can actually run. Verified live from a non-fleet machine (DGX):\n `POST /auth/github/device/start` → HTTP 200 with a real `user_code` and\n `verification_uri`.\n\n- **Upgrade check compared versions as strings** — `latest != current` reported a\n downgrade as an available upgrade. Now uses `packaging.version` with a numeric-tuple\n fallback.\n\n## [2.37.0] - 2026-07-24\n\n### Added — Agent Onboarding Fabric: bring your own agent, or run a managed one\n\nOnboard an agent built on **any** framework, or let Aitherium run it for you.\n\n- **ACP (Agent Client Protocol), both directions** — `ACPClient` drives any ACP agent;\n `ACPServer`/`serve_stdio` exposes an adk agent to any ACP host (Zed, VS Code,\n JetBrains). Both proven live against Zed's reference `agent-client-protocol`.\n- **Universal Agent Pack** — `AgentPackManifest` + `Supervisor` describe and run an\n external agent across 6 frameworks × 6 protocols, fail-closed on anything unknown.\n- **Protocol drivers** — `get_driver()` returns an `http`, `langgraph_rest`, `a2a`, or\n `mcp` driver, so REST/graph-orchestrated agents can be *managed*, not just connected.\n- **Pack registry** — `PackRegistry.publish/browse/versions/get/yank/verify_digest`,\n SHA256-digested, semver-ordered, validation fail-closed before anything is written.\n- **Managed identity** — `ManagedAgentIdentityProvider` with a real gateway minter and a\n PROVISIONED→REGISTERED→ACTIVE→ROTATED→REVOKED lifecycle; default-deny `authorize()`.\n- **Zero-code connect templates** — `render_connect(framework, …)` emits config pinned to\n each framework's real schema.\n\n### Added — NOOA-style agent capabilities\n\n- **`CodeActLoop`** — code as action: the model writes Python cells that run against a\n persistent namespace over live objects, each cell AST-validated before execution, with\n per-cell timeouts and bounded observations.\n- **`ObjectRegistry` / `render_observation`** — pass by reference: large values become\n handles instead of flooding the context with serialized text.\n- **`EventLog` / `ContextBlocks`** — harness APIs the model itself can call to query its\n own event history and manage named context blocks in KV-cache-stable order.\n- **`has_ellipsis_body` / `@strategy`** — NOOA's `async def f(...) -> X: ...` ergonomic.\n- **`CodeValidator`** — best-effort AST validation (ported from NVIDIA's OO-Agents,\n Apache-2.0; NOTICE included). A defense-in-depth layer, not a sandbox boundary.\n- **`PredictLoop`** — single-shot strategy with structured output (`output_model=`) and\n validation retry; `Strategy` is now a first-class alias of `AgentLoop`, and both loops\n accept a per-call `model=` override.\n- **Typed I/O** — `AgentResult` validates its fields at construction and at the\n `Agent.run` boundary.\n\nAll new capabilities are exported lazily from the top-level `adk` namespace.\n\n## [2.27.0] - 2026-07-18\n\n### Added — Moonshot Kimi K3 provider (DeepSeek-equivalent cloud backend)\n\n- **New `moonshot` provider** across the LLM router: OpenAI-compatible\n `https://api.moonshot.ai/v1`, default model `kimi-k3` (131K context default, up to 1M;\n tool calls, JSON mode, structured output). Wired everywhere `deepseek` is:\n - `LLMRouter(provider=\"moonshot\")` + `_COMPAT_URLS`/`_COMPAT_MODELS`/model tiers\n (small/medium/large → `kimi-k3`)\n - Config: `moonshot_api_key` field (env `MOONSHOT_API_KEY`), saved-config load,\n provider-keys map (`provider_keys.json` key `moonshot`)\n - Auto-detect chain: tried after DeepSeek (config key, then env key)\n - Hybrid reasoning backend: `AITHER_REASONING_BACKEND=moonshot` for effort 7+\n - Reasoning tiers: `backend: moonshot` (alias `kimi`) via new `MoonshotBackend`\n - CLI: `adk backend set moonshot --api-key …`, `adk backend list` shows moonshot\n- No breaking changes; DeepSeek and all other providers unchanged.\n\n## [2.26.0] - 2026-07-16\n\n### Added — Aeon group-chat web UI pack + POST /aeon/stream SSE endpoint\n\n- **New `aeon` UI pack** (`adk/webui/packs/aeon/index.html`) — group chat for multi-agent\n discussion. Mirrors the `llamacpp` pack's auth + SSE pattern (bearer from URL fragment #k=),\n with a preset selector (balanced/creative/technical/security/minimal/duo_code/research) and\n distinct colored agent messages. Synthesis block highlighted. Agent content is HTML-escaped\n before render. Fully self-contained HTML+CSS+JS, light+dark, no external assets. (No\n temperature/max-tokens controls — Aeon runs a fixed multi-agent round and doesn't thread\n per-turn generation params, so exposing them would be a no-op.)\n- **POST /aeon/stream SSE endpoint** — accepts body {message, preset?, agents?, rounds?,\n session_id?}, emits SSE with: `session_start` (participants + orchestrator), `agent_message`\n per non-orchestrator, `synthesis` (if enabled), `error` on exception, `complete` (total tokens\n + latency). Mirrors /chat/stream structure — like every streaming endpoint here it is gated by\n the server auth middleware **when `AITHER_SERVER_API_KEY` is set** (a local `adk up` with no key\n is unauthenticated by design, same as /chat/stream).\n- **Default UI pack flipped to `llamacpp`** — `resolve_ui_pack_name()` now returns `llamacpp`\n instead of `console` when no env/config is set (new users see the clean chat, not the admin\n SPA; existing users with AITHER_AGENT_UI or saved config unaffected).\n\n### Added — swappable agent web UI packs (llama.cpp-style built-in chat)\n\n- The page an agent serves at `/` is now a **swappable UI pack**, so you can\n drop in, test, and deploy different chat frontends without touching the agent.\n Select with `adk ui set <name>` (persists to `~/.aither/config.json`) or the\n `$AITHER_AGENT_UI` env; `adk ui ls` lists packs, `adk ui path` shows what\n resolves.\n- **New built-in `llamacpp` pack** — a clean, self-contained llama.cpp-style\n chat UI (centered conversation, streaming token-by-token with a cursor, a\n Stop button, a settings drawer for temperature / max-tokens that is actually\n honored end-to-end — `/chat/stream` now forwards them to the provider —\n minimal safe markdown, light+dark). Hits the agent's own gated `/chat/stream`\n with the bearer read from the URL fragment (never sent in the URL). Built-ins\n also include `console` (the full admin SPA, still the default) and `minimal`.\n- **Drop-in custom packs**: a UI pack is just a folder with an `index.html` in\n `~/.aither/ui-packs/<name>/` (override via `$AITHER_UI_PACKS_DIR`) — no rebuild.\n- Resolution is **fail-soft**: an unknown/missing pack falls back\n console → minimal, so `/` is never blank.\n\n### Added — MCP-gateway-first platform access + mesh A2A trust\n\n- **Gateway-first platform access** (`adk/client/_gateway_mcp.py`, `GatewayMCPClient`):\n a self-hosted agent connects its MCP **client** to `mcp.aitherium.com` authenticated\n as the OWNER (device-flow / PAT / ACTA token), RBAC-scoped to that identity — so it can\n use platform tools + `get_secret` **whether or not the node runs local AitherOS\n services**. Fail-closed: no token → client disabled, the agent still runs. Wired into\n the server lifespan (`_connect_gateway_mcp`).\n- **`genesis` LLM backend**: `adk backend set genesis` points the agent brain at a local\n Genesis (`http://localhost:8001/v1`, model `workflow`) — the 5090/controller pattern\n where inference is served by the local fleet, not the cloud gateway or a raw vLLM.\n- **`adk mesh` command**: `adk mesh onboard` drives the Conductor 5-step onboard\n (`/v1/mesh/onboard` → registers this node's public key, returns the `endpoint:mesh`\n trust token); `adk mesh ls` lists peer nodes/agents (AitherMesh `/nodes` + Directory)\n with their A2A cards + invoke URLs for discovery.\n\n### Security — A2A inter-agent trust enforcement (fail-closed)\n\n- **`adk/a2a_trust.py`** (new): Ed25519 signature verification + trusted-key check\n (`AITHER_A2A_TRUSTED_KEYS`), opt-in via `AITHER_A2A_REQUIRE_TRUST` (default `false` for\n backward compatibility; `audit` logs untrusted, `true` rejects).\n- **Fixed a fail-OPEN in the inbound A2A handler** (`adk/a2a.py`): the trust check\n previously ran *only when signature headers were present*, so an unsigned request\n bypassed `AITHER_A2A_REQUIRE_TRUST` entirely. Now genuinely fail-closed — required mode\n with no/invalid signature → `403`.\n- **Sovereign template no longer scaffolds `verify=False`**: the generated\n `services/llm.py` now uses the project's own `tls_verify()` policy helper for every\n provider call instead of hardcoding disabled TLS verification.\n- **`adk/_tls.py`**: disabling verification via `AITHER_TLS_VERIFY=false` now emits a\n one-time loud warning (was silent) — the escape hatch stays, the silence goes.\n\n## [2.25.1] - 2026-07-15\n\n### Fixed — self-hosted OpenAI-compatible backends honor a custom base URL\n\n- `adk backend set llamacpp --base-url http://localhost:8090/v1 --model <id>` (and\n `vllm`/`lmstudio`, and the `AITHER_LLM_BASE_URL` env) now actually point the agent\n at YOUR server. Previously the base URL was saved but never read, so an explicit\n `--backend llamacpp` fell back to the provider's public API default (openai.com).\n New `Config.llm_base_url` field + saved-config `inference_url` wiring; the explicit\n backend path in `LLMRouter` now passes it for the local OpenAI-compatible family.\n This is what lets a self-hosted node run its OWN local model (e.g. a PrismML\n Bonsai llama.cpp) as the agent brain instead of ollama/cloud.\n\n## [2.25.0] - 2026-07-15\n\n### Added — node_bootstrap tool pack: bootstrap LLM inference on any hardware\n\n- **New bundled tool pack `node-bootstrap`** (`adk/toolpacks/node_bootstrap/`):\n seven agent tools — `node_detect_hardware`, `node_resolve_recipe`,\n `node_plan_deployment`, `node_apply`, `node_enroll`, `node_register_backend`,\n `node_verify` — plus a CLI shim (`python -m adk.toolpacks.node_bootstrap\n detect|resolve|plan|apply|enroll|register|verify`). Detect a box's hardware,\n resolve a deployment recipe, actually deploy the inference engine (docker\n compose / native / delegate), enroll with a control plane, register the\n backend, and live-verify a completion.\n- **9 hardware recipes** shipped in the wheel: `cpu-1bit-llamacpp`, `cpu-ollama`,\n `cuda-vllm-8gb/24gb/40gb`, `cuda-dual-stack-32gb` (32GB-card co-resident\n stack), `unified-memory-vllm` (DGX/Grace-class), `metal-ollama` (native —\n Docker on macOS has no Metal passthrough), `cloud-api` fallback. Resolution\n scores hardware against min/max VRAM bands, RAM, cores and unified memory;\n ties prefer higher tiers and self-contained recipes over fleet delegates.\n- Security posture: enrollment and backend registration are fail-closed\n (missing token/URL → error dict, never anonymous, never `verify=False`);\n tokens are redacted in outputs; all endpoints come from args/env\n (`AITHER_CONTROL_PLANE_URL`, `AITHER_GENESIS_URL`, `AITHER_AUTH_TOKEN`).\n\n## [2.24.0] - 2026-07-14\n\n### Changed — sync-family consolidation\n\n- **Removed dead code:** `adk/sync.py` (528 lines) deleted — it was unreachable,\n shadowed by the `adk/sync/` package (`import adk.sync` always resolved to the\n package; nothing loaded the module by path — verified).\n- **Grouped the sync domain modules into the `adk.sync` package:**\n `brain_sync`→`adk.sync.brain`, `files_sync`→`adk.sync.files`,\n `lockbox_sync`→`adk.sync.lockbox`, `secrets_sync`→`adk.sync.secrets`,\n `session_sync`→`adk.sync.sessions`, `settings_sync`→`adk.sync.settings`,\n `sync_watermark`→`adk.sync.watermark`. All in-tree importers updated. No\n back-compat shims were left because **zero external/monorepo code imported the\n old root paths** (verified) — a clean move, not a shimmed one. **Breaking only\n for code that imported `adk.<name>_sync` directly** (none known).\n\n### Docs — MCP module layering clarified\n\n- `adk.mcp` (enterprise AitherOS gateway client: auth/billing/token-tracking) and\n `adk.core.mcp` (minimal generic JSON-RPC adapter for any MCP server) are\n DISTINCT layers, not duplicates — each now carries a LAYER/ROLE/See-also\n docstring so they are not mistakenly consolidated.\n\n## [2.23.2] - 2026-07-14\n\n### Fixed — CompletionGate no longer false-fails on correct results\n\n- **Tolerant judge JSON extraction.** The LLM judge parsed its reply with `json.loads(text[first\"{\":last\"}\"])`, which raised `JSONDecodeError` (\"Extra data\") whenever the model appended prose after the verdict or emitted a second object — and `verify()` turned that into a fail-closed verdict, so a genuinely completed task reported `unverified`. New `_first_json_object()` uses `JSONDecoder.raw_decode` from the first `{` that begins a valid object and ignores trailing content (prose, markdown fences, extra objects). A judge that answers with no parseable JSON still fails closed (never a soft pass), now with a clear reason. The same extractor backs `_derive_criteria`.\n- **Bare code-like tokens are hard-checked.** A natural criterion like `output contains ADK_AUTO_OK` (no quotes) previously fell through to the judge; `hard_checks` now also matches bare identifier/code shapes (contains an underscore, or 4+ CAPS/digit chars), with a stopword guard so a descriptive uppercase word (`OUTPUT`, `CONTAIN`) never becomes a false required token. Quote a token to force it regardless.\n- +9 tests (24 total), including the exact trailing-data regression.\n\n## [2.23.0] - 2026-07-14\n\n### Removed — dead modules and internal integrations\n\n- **`adk.swarm`, `adk.provisioning_tools`, `adk.session_sync_integration`, `adk.addon_metering`** — four orphan modules with zero public usage have been deleted. These were internal prototypes kept for legacy monorepo consumers; public packages have not imported them since 2.21.0 (tracked as D-348). **Breaking for anyone directly importing these modules** (import will fail). They are no longer exempt from the orphan-module check.\n- **`adk.aither_bridge`** — the AitherOS-internal IRC ↔ chat gateway bridge is no longer shipped in the public aither-adk package (stripped at sync time). All public importers already degrade behind `except ImportError` (in `adk.server` and `adk.builtin_tools`), so public packages remain unaffected. The module stays in the private AitherOS monorepo for internal use.\n- **Identity document trim** — removed internal agent names from `adk/identities/aither.yaml` delegation guidance, replacing with role-generic wording (\"delegate code review, refactoring, security analysis, performance and testing to specialist agents when available\"). Effort-tier routing philosophy remains unchanged and is product design, not a leak.\n\n### Changed — genericized customer/product names in help text\n\n- CLI help/examples no longer name specific customer deployments (`--scope` template list and `deploy agent` name example use generic placeholders). Functional deploy paths are unchanged.\n\n## [2.22.1] - 2026-07-14\n\n### Fixed — real bugs surfaced by reviving the public CI (red since Jul 5)\n\n- **TLS**: bundled toolpacks (arc-brainpack, cloudflare) disabled certificate\n verification on gateway/vault calls; all now route `adk._tls.tls_verify()`\n (verified by default, internal CA bundle when present).\n- **`system_prompt` contract**: bundled-pack `[PACK DIRECTIVES]` were appended\n even to an explicit `system_prompt`; an explicit prompt now wins wholesale.\n- **Headless secrets**: `_derive_key()` used `os.getlogin()`, which raises\n `OSError` without a controlling terminal (CI, daemons, containers) — falls\n back to `getpass.getuser()` (same value on interactive boxes, so existing\n secret files keep decrypting).\n- **`pack_verifier`**: missing `cryptography` crashed with `UnboundLocalError`\n in the exception handler instead of failing closed; now degrades gracefully\n (and `cryptography` was added to the dev extra so CI tests the real path).\n- **`fleet_manager._kill_pid` (POSIX)**: reported success immediately after an\n asynchronous SIGTERM and never reaped children (zombies read as alive); now\n polls, escalates to SIGKILL, and reaps.\n- **Identity**: `aither` regained the `creative` tool category (dropped\n accidentally in a 2.7.0 WIP sweep).\n\n## [2.22.0] - 2026-07-14\n\n### Changed — internal toolkit relocation\n\n- **`adk.platform` relocated** — the internal aither-platform toolkit (ComfyUI/image-gen\n pipelines, fleet agent clients, parallel CLI/UI/tools stack) has been moved out of the\n adk SDK tree to the standalone internal `aither-platform` package at\n `AitherOS/agents/aither_platform/`. The public package remains unaffected (it already\n excluded `adk.platform` since 2.21.1). Internal AitherOS agent builds now install\n `aither-platform` separately. `adk platform` falls through to the same \"not available\"\n message on public installs; internal builds work via the relocated package.\n\n### Removed — backward-compat re-export deleted\n\n- **`adk.gateway` module removed** — the backward-compatibility re-export of `GatewayClient`\n from `adk.gateway` has been removed. Use `from adk.client import GatewayClient` instead\n (the established public API since 2.21.0). All internal importers have been updated.\n\n## [2.21.1] - 2026-07-14\n\n### Removed — internal toolkit and license-gated product no longer ship publicly\n\n- **`adk.platform`** — the internal aither-platform toolkit (ComfyUI/image-gen\n pipelines, fleet agent clients, a parallel cli/ui/tools stack) is no longer\n included in the public repo or the PyPI wheel. It was internal-only code that\n had been riding along since the aither-platform merge. `adk platform` now\n reports \"Platform toolkit not available\" on public installs; internal AitherOS\n builds are unaffected.\n- **`adk.formbridge`** — the FormBridge form-automation product is license-gated\n (PROFESSIONAL+); its implementation no longer ships in the free package. All\n import sites (`builtin_tools`, `server` route mount) already degrade behind\n `except ImportError`, so nothing else changes behavior.\n- Product test suites (`test_formbridge.py`, `test_encounter_keying.py`,\n `test_flow_tools.py`) are excluded from the public sync alongside them.\n\n## [2.21.0] - 2026-07-14\n\n### Added — Completion gate: verified-or-retried task execution\n\n- **`adk.gate.CompletionGate`** — a composable wrapper that closes the \"silent\n success\" gap: an agent's ReAct loop returns whatever the model produced and\n calls it done (`finish_reason = stop/max_steps/length`), which is a *liveness*\n signal, not a *completion* one — so an agent can \"succeed\" while doing nothing.\n `CompletionGate` runs the agent, verifies the result against acceptance\n criteria, retries with the failure fed back, and returns the response with\n `finish_reason` set to `verified` or an honest `unverified`. Never a false pass.\n - **Hard-first, un-soft-passable checks** (`adk.gate.hard_checks`): a filesystem\n path a criterion says must exist is checked on disk; a required quoted token\n must appear in the output. Only genuinely subjective criteria fall through to\n an optional LLM judge (reuses the agent's own model), which **fails closed**\n if unavailable.\n - **`gated_run(agent, task, criteria=..., max_retries=2)`** — one-shot helper.\n - `auto_criteria` (LLM writes the machine-checkable definition-of-done from the\n task), a `verifier=` override for custom checks (tests-pass / HTTP probe), and\n a `requires_action` short-circuit so it never fights human-approval pauses.\n - Compose it onto any agent exposing `run(task) -> resp{.content, .finish_reason}`\n (`AitherAgent`, `core.Agent`, or a thin adapter over a remote dispatch).\n - Exported from the top-level package: `from adk import CompletionGate, gated_run`.\n\n## [2.19.0] - 2026-07-12\n\n### Added — Eve agent importer + durable A2A tasks + canonical agent discovery\n\n- **`adk pack import <eve_agent_path>`** — convert external Eve agents to AitherADK packs\n (from GitHub or local filesystem). Maps Eve structure (instructions → system prompt,\n agent.ts → agent.yaml, skills/*.md → skills, connections/*.ts → MCP, tools/*.ts →\n tools/node verbatim) to AitherADK format with a single command. Eve agents (compiled\n manifest format, schema v35) become installable packs compatible with `adk pack install`.\n- **Durable A2A task lifecycle (`TaskManager` backed by FileStore)** — agent-to-agent\n tasks now survive container restarts. Tasks persist as append-only JSONL\n (`.adk/tasks.jsonl`); in-memory dict for fast access; `create_task()` / `update_status()` /\n `add_artifact()` / `add_message()` all save to disk. Google A2A v0.3.0 spec compliant.\n- **Canonical agent card discovery** — `A2AServer.mount()` now exposes:\n - `GET /.well-known/agent-card.json` — Agent Card (canonical; contains agent identity,\n capabilities, skills, endpoints)\n - `GET /.well-known/agent.json` — Agent Card (legacy, redirects with 308 to canonical path)\n - `POST /a2a` — JSON-RPC 2.0 for message send/task lifecycle\n - `GET /a2a/tasks/{id}/subscribe` — SSE streaming task updates\n- **Four built-in agent packs now complete and catalogued**:\n - `hermes` — architecture review & trade-off analysis\n - `openclaw` — web research & source verification\n - `claude-code` — feature development & debugging\n - `analyst` — structured data inference & anomaly detection\n All discoverable via `adk packs` / `adk install pack:<name>`.\n- **Tolerant tool-call JSON parsing** — malformed tool JSON (extra properties, missing\n name/arguments, string-encoded nested args, missing keys) is now gracefully degraded\n instead of silently failing. `_parse_tool_json()` accepts both `\"arguments\"` and\n `\"parameters\"` keys, coerces string-encoded args, and skips unparseable calls instead\n of crashing the turn.\n\n### Known limitations\n\n- Eve agent importer: remote GitHub fetching not yet implemented (local paths only).\n- Durable task store: currently JSONL (suitable for thousands of records, not millions).\n\n## [2.18.0] - 2026-07-10\n\n### Added — AitherShell command center + Claude Code session management\n\n- **`aither sessions`** — Claude Code session manager: interactive full-screen\n browser (type-to-filter, transcript preview, deep full-text `search`, resume\n **in the current terminal** or as Windows Terminal tabs), crash guard\n (`guard install` = at-logon watchdog that snapshots live sessions and\n auto-restores the whole set after a terminal crash), and `ingest [--watch]`\n (incremental, secret-guarded sync of session conversations into the local KB\n / CompanyBrain via the standard ingest pipeline).\n- **`aither hq`** — command-center dashboard: per-service fleet health, LLM\n queue (depth/VRAM/models), Pulse alerts, live session count, inbox unread —\n auto-refreshing, with one-key jumps into chat, sessions, inbox, agents,\n brief, watchtower, and docker recovery.\n- **`aither inbox`** — unified queue over CommCore mail, Relay\n mentions/notifications, and Pulse alerts; open/mark-read/DM-reply from the\n terminal. Every source degrades independently.\n- **`aither agents`** — roster console + `agents ask -e <1-10> <agent> <q>`\n (effort-tiered ask via Genesis, with automatic `/chat/stream` fallback when\n the `/agent/sync` execute gate rejects host callers) + forge dispatch\n inspection + routine health.\n- **`aither palette`** — universal fuzzy picker across actions, sessions, and\n services; **`aither brief`** — renders Atlas's executive briefing in the\n terminal; **`aither watch`** — fleet watchtower with named wedge-signature\n detection (docker-WSL wedge, LLM-queue stall, crash-loop uptime regression)\n and optional docker auto-recovery.\n- All fleet reads go through a shared fail-soft `FleetClient` (internal-CA\n TLS via `adk._tls`, dashboard-grade timeouts, per-source degraded states).\n- Node shell-cli (`@aitherium/shell-cli` 1.13.0): `aither\n sessions|hq|inbox|palette|brief|watch|agents|docker` now pass through to the\n Python shell instead of being intent-classified as chat prompts.\n\n## [2.17.1] - 2026-07-05\n\n### Fully self-service mesh join — no customer key handling\n\n- **Auto-join headscale off the onboard response.** The conductor now auto-issues a\n headscale pre-auth key for NAT'd nodes and returns it in the `/v1/mesh/onboard`\n response; `mesh.join` consumes it and brings up the tunnel automatically. A customer\n runs one command — they never mint, fetch, or set a headscale key. Explicit\n `--headscale-key`/`AITHER_HEADSCALE_AUTH_KEY` still take precedence.\n- **Security:** `_tailscale_up` now redacts the auth key from all logs and exceptions\n (a failed `tailscale up` previously echoed the full argv, leaking the key into logs).\n\n## [2.17.0] - 2026-07-05\n\n### Added — Headscale mesh transport + local Qdrant + AitherConnect onboarding\n\n- **Headscale NAT-friendly mesh transport.** When raw WireGuard UDP:51820 is not\n viable (customer boxes behind NAT, corporate firewalls, CGNAT), `adk mesh join\n --headscale` routes the mesh tunnel through Tailscale's Headscale control plane.\n The overlay IP is still Conductor-assigned (10.77.0.0/16); Headscale provides\n only the transport layer. Configured via `AITHER_MESH_TRANSPORT=headscale`,\n `AITHER_HEADSCALE_URL` (default: https://headscale.aitherium.com), and\n `AITHER_HEADSCALE_AUTH_KEY` (pre-generated key). Automatic fallback to raw\n WireGuard if Headscale setup fails.\n- **Local Qdrant vector DB provisioning.** `adk stack` now provisions Qdrant\n locally when `AITHER_VECTOR_DB=qdrant` is set, enabling offline-first RAG\n without external dependencies. Tiered: local Qdrant in-fleet, cloud Nexus\n for enterprise multi-tenant deployments.\n- **Conductor URL public fallback.** `adk mesh join` resolves the internal\n `aitheros-conductor:8193` hostname; if unreachable (e.g., self-hosted nodes),\n it falls back to the public `conductor.aitherium.com:8193` endpoint,\n eliminating bootstrap configuration friction.\n\n### Documentation\n\n- **Self-hosting runbook.** New `docs/SELF_HOSTING.md` covers deploying AitherOS\n on customer infrastructure with Qdrant + mesh + AitherConnect onboarding.\n- **Mesh transport constraints documented.** Raw WireGuard requires public\n UDP:51820 endpoint (cloud instances, dedicated hardware); Headscale for\n NAT'd networks. Noted in CLI help and module docstrings.\n\n## [2.15.0] - 2026-07-04\n\n### Added — built-in web admin console + portal-profile settings sync\n\n- **Web admin console.** The page `adk up` opens (served at `/` and `/ui`) is now a full,\n self-contained console — Chat plus six admin tabs: **Backend** (view/switch LLM provider,\n set keys, test connection), **Packs** (list/enable/disable/reload tool packs),\n **Sessions** (browse/delete conversations), **Logs** (redacted agent-log tail),\n **Graph** (knowledge-graph search/neighborhood/stats), and **MCP** (add/remove external\n MCP servers). No build step, no CDN — a single packaged HTML asset. The original minimal\n streaming chat page stays at `/chat` for back-compat.\n- **Admin API (`adk/admin_api.py`).** ~20 endpoints under `/admin/*`, all bearer-gated by the\n existing middleware (never in `_skip_auth_paths`). Operates on the live agent — LLM backend\n swap (`LLMRouter.switch_backend`), pack reload, and MCP registration take effect without a\n restart.\n- **Settings sync (`adk/settings_sync.py`).** The user's portal profile is the source of\n truth: on startup the agent pulls `preferences.adk` and applies it over the local\n `~/.aither/config.yaml` cache; admin-console edits push a debounced snapshot back to\n `PUT /api/settings/preferences`. Provider API keys and MCP auth headers are device-local\n secrets and are never included in the pushed snapshot. Opt out with `AITHER_SETTINGS_SYNC=false`.\n\n### Security\n\n- **Timing-safe token comparison.** The server bearer check now uses `hmac.compare_digest`\n (the token is reachable over the public tunnel; a byte-wise `!=` leaked it to timing attacks).\n- **MCP add is SSRF-guarded + confirmed.** Adding an external MCP server is a two-step\n prepare→confirm flow; URLs resolving to loopback / private / link-local / metadata addresses\n are rejected (override with `AITHER_ALLOW_PRIVATE_MCP=1` for trusted local servers).\n- **Config editor is allowlisted.** `PATCH /admin/config` writes only safe scalar prefs and\n hard-denies fields that could execute code or redirect traffic at startup (e.g.\n `aither_toolpack_dirs`, backend base URLs).\n- **Log tail is scrubbed.** Bearer tokens, `#k=` fragments, and `sk-`/`aither_sk_` keys are\n redacted from the log-tail endpoint; the tunnel log is excluded.\n\n## [2.14.8] - 2026-07-04\n\n### Added — `adk up` for normal humans (no API key, no prereqs, live chat)\n\n- **Hosted-brain default — no API key required.** When there's no local model and no\n provider key, `adk up` signs in (device-flow) and routes inference through the gateway\n using the portal token (`AITHER_LLM_BACKEND=gateway` + bearer). A non-technical user\n never needs to obtain or paste an LLM API key. Explicit `--provider`/local model still\n take precedence; unattended `--yes` with no token/backend fails cleanly (exit 3).\n- **cloudflared auto-download.** If the tunnel binary is missing, `adk up` downloads the\n right platform asset to `~/.aither/bin` (SHA256-verified against the release; opt out with\n `AITHER_CLOUDFLARED_REQUIRE_CHECKSUM=0`) instead of exiting with an install hint.\n- **Built-in streaming chat page.** The agent now serves a self-contained chat UI at `/`\n (and `/chat`) with a live \"thinking…\" indicator and token-by-token streaming — so a human\n has somewhere to talk to it with immediate feedback instead of a blank wait. `adk up` opens\n it locally and passes the callback bearer in the URL fragment (`#k=…`), so `/chat/stream`\n stays authenticated (not an open proxy) with nothing to paste.\n- **Human-readable failures.** `adk up` errors now carry a plain-English `next_action`\n (kept machine-readable in `--yes` JSON).\n\n### Fixed\n\n- `adk up` chat link pointed at a non-existent `/settings/agent` route; it now points at the\n agent's own streaming chat page.\n\n## [2.14.7] - 2026-07-04\n\n### Added\n\n- **`adk up` — one command to run a persistent, fleet-connected agent.** Collapses\n the scattered onboarding surface (`host`/`quickstart`/`connect`/`login`/`setup`)\n into a single command that starts `aither-serve` (single agent, identity `aither`\n by default), opens a Cloudflare quick-tunnel for fleet reachability, registers with\n the portal, and installs a reboot-persistent autostart entry (Windows Task Scheduler\n / systemd-user / launchd). Detaches by default so the terminal is freed; `--foreground`\n blocks. Reuses the existing device-flow login, cloudflared discovery, provider-key\n store, and `_preflight_check` backend detection.\n- **Fully non-interactive path for autonomous agents.** `adk up --yes` (or any\n non-TTY invocation) takes ZERO prompts — everything resolves from flags, env, and\n saved config — and emits a single machine-readable JSON status line with meaningful\n exit codes (`0` ok, `2` bad-input, `3` no-backend, `4` tunnel-missing, `5` register-failed).\n Degrades to local-only (warn, not fail) when a portal token is absent unless\n `--require-register` is set.\n- **`adk down` / `adk status --json` companions.** `down` stops the agent + tunnel,\n best-effort deregisters, and removes the autostart entry; `status` reports the running\n agent (liveness + `/health` + tunnel + registration), with `--json` for agents/CI.\n- **`adk stack`** — the previous `adk up` behaviour (supervise the Room + Ollama consumer\n stack) now lives here; `adk up` is the connected-agent command.\n\n### Internal\n\n- New `adk/agent_daemon.py`: detached process spawn, status file\n (`~/.aither/adk-up.json`), pid liveness/teardown, and cross-platform autostart install.\n\n## [2.14.6] - 2026-07-04\n\n### Added\n\n- **`adk.preflight` — self-bootstrap capability probe.** `run_preflight(agent,\n spec)` wakes an agent up knowing what it is actually plugged into: a bounded\n (1.5s) parallel `LivenessProbe` over the router's resolved backends\n (primary/reasoning), the embeddings winner, remote MCP tools, the structured-ML\n / `/ml/teach` endpoints, and voice — returning an honest `CapabilityReport`.\n Status vocabulary distinguishes reachability from entitlement\n (`OK`/`MISSING`/`UNREACHABLE`/`AUTH`/`TIER_DENIED`/`UNSUPPORTED`), a hosted slot\n never prints a bare `OK`, and a required-but-unsatisfied slot aborts before the\n loop instead of blind-firing. Headless task/rules resolution + an allowed-roots\n policy binding (`builtin_tools.set_allowed_roots`, which resets the memoized\n cache so the override actually enforces).\n- **Multimodal (image) messages.** `Message.content` now accepts an OpenAI-style\n content-part list (`str | list[dict]`), passed through verbatim to the\n OpenAI-compatible body (gateway / vLLM / OpenAI). New `adk.llm.multimodal`\n helpers — `image_message()`, `image_content_parts()`, `to_image_url()` — build\n the parts and encode local image bytes/paths into `data:` URIs.\n- **Real vision preflight probe.** The vision slot is no longer unconditionally\n `UNSUPPORTED`: it sends a discriminable image (a solid red square) and requires\n the model to name the color, so a blind/text-only model cannot false-pass. `OK`\n only on a real round-trip; `AUTH` on 401/403; `UNSUPPORTED` when there is no\n OpenAI-compatible provider or the model cannot see the image.\n\n## [2.14.5] - 2026-07-04\n\n### Added\n\n- **`adk.reasoning.mcts` — generic Monte-Carlo Tree Search library.** A reusable\n MCTS engine (ported from the internal `UnifiedMCTS`, with all internal couplings\n removed) that any agent can drive over its own environment:\n - `MCTSEnvironment` protocol (`get_state_hash` / `get_actions` / `step` /\n `evaluate` / `clone`) — implement it and search.\n - Three **optional, default-off** model seams — `TransitionModel`, `PolicyModel`\n (PUCT priors), `ValueModel` — so a learned world-model / policy / value can be\n injected without touching the core; with all three `None` the engine is a plain\n UCT search.\n - `ObservedTransitionModel` adapter — a cheap online lookup world-model (hit =\n exact stored transition; miss = identity + negative \"unknown\" reward +\n `is_uncertain`, biasing exploration toward learning), with JSONL save/load for\n cross-run reuse.\n - `UnifiedMCTS(config).search(env)` returns an `MCTSResult` (best action, value,\n confidence, principal-variation path) and an optional `MCTSTrace`\n (visit-distribution policy target) for distillation.\n- **Self-bootstrapping agent machinery.** Build an agent from a declarative spec\n instead of bespoke wiring:\n - `adk.bootstrap.build_agent_from_spec(path) -> (AitherAgent, RunCtx)` — resolves\n prompts, activates required/optional tool packs, and carries a `memory_map`\n (a `node_type -> (role, tier)` override for memory writes, never a gate).\n - `adk.prompts.PromptBridge` — resolves an agent's prompt bindings from files\n (relative to the spec) or `AitherPrompts` dot-keys (optional import).\n - `adk.packs.PackActivator` — eager activation of installed tool packs by name\n over `register_tool_packs`; a required pack that registers zero tools raises\n `PackUnavailable`, optional packs soft-degrade.\n - `python -m adk.bootstrap.generic_agent <spec.yaml>` inspects a spec (resolved\n name, activated packs + tool counts, prompt keys, memory map) without running a\n live loop.\n- **`deep_research` tool pack** — a full multi-engine web-research workflow as\n `dr_*` agent tools (keyless search, page fetch + cache, site mirror, a citable\n findings knowledge graph, and markdown/PDF/DOCX report generation), activatable\n by name via the tool-pack loader.\n\n## [2.14.4] - 2026-07-04\n\n### Added\n\n- **`structured_ml` tool domain** — zero-shot structured-data inference for agents,\n backed by TabFM (tabular classification/regression) and TimesFM (time-series\n forecasting). Three tools:\n - `tabular_classify(support_rows, target, query_rows)` — classify rows from a\n labeled support set, in-context (no training step; up to 10 classes).\n - `tabular_regress(support_rows, target, query_rows)` — predict a numeric target.\n - `timeseries_forecast(series, horizon)` — forecast future values of a series.\n\n The tools POST to a structured-ML inference service resolved from\n `AITHER_STRUCTURED_ML_URL` (default `http://localhost:8192`), reject non-http(s)\n URLs, and summarise oversized responses to protect the agent's context.\n- **`Capability.STRUCTURED_INFERENCE`** capability token and a new **`analyst`**\n identity/pack that ships the `structured_ml` domain by default. The domain is\n opt-in — only identities that enable the `structured_ml` category get the tools.\n\n## [2.14.3] - 2026-07-03\n\n### Fixed\n\nAll four bugs below were found by actually booting `adk run` against a real\nchat model and driving a real tool-calling request end-to-end (not inferred\nfrom reading the code) — each one silently broke the \"local agent uses its\nconfigured model and its tools actually work\" path.\n\n- **`AITHER_MODEL` / `config.model` silently ignored by the OpenAI-compatible\n backend**: `LLMRouter.__init__` never read `config.model` into `self._model`\n unless the caller passed `model=` directly to the constructor — which nothing\n did. Every explicit model choice (env var, config file) was dropped, the\n provider fell back to a hardcoded per-backend default (`gpt-4o-mini` for the\n `openai` backend), and an unrecognized model name got silently remapped\n server-side to the wrong backing model. Concretely: setting\n `AITHER_MODEL=aither-orchestrator` had no effect — every request actually\n went to a different model that was never meant to be called directly and\n can't reliably use tools.\n- **`OPENAI_BASE_URL` ignored for explicit `--backend`/`AITHER_LLM_BACKEND`\n selection**: `LLMRouter._auto_detect()`'s explicit-backend branch hardcoded\n `base_url=None` when constructing the provider, instead of reading\n `config.openai_base_url` (which two other call sites in the same file\n already did correctly). Any self-hosted OpenAI-compatible endpoint\n (MicroScheduler, vLLM, LM Studio) configured via `OPENAI_BASE_URL` was\n silently ignored in favor of `https://api.openai.com/v1`.\n- **Tool-call steering never engaged on default-effort chat**: the retry that\n nudges a model back into structured tool-calling when it responds with prose\n instead of a function call was gated behind `effort >= 6`. Default `effort`\n for a plain `agent.chat(message)` call resolves to `5`, so the safety net —\n built specifically for this failure mode — never fired for default requests.\n Also now forces `tool_choice=\"required\"` on the retry instead of relying on a\n text nudge alone.\n- **Every non-string builtin-tool parameter silently typed as `\"string\"` in\n the generated tool schema**: `builtin_tools.py` uses\n `from __future__ import annotations`, so `_extract_parameters()` in\n `tools.py` was reading postponed (string) type hints (`\"int\"`, not `int`)\n straight out of `fn.__annotations__`, which never matched\n `_type_to_schema`'s type-object keys and silently fell through to\n `{\"type\": \"string\"}`. Every builtin tool with an `int`/`float`/`bool`/`list`\n parameter was affected — e.g. `file_read(start_line: int, end_line: int)`\n advertised both as strings, so a model correctly calling the tool with\n `\"start_line\": \"0\"` crashed the actual implementation with\n `unsupported operand type(s) for -: 'str' and 'int'`. Fixed by resolving\n hints through `typing.get_type_hints()` instead of the raw annotations dict.\n `_coerce_arguments()` — the runtime safety net that's supposed to coerce a\n model-supplied `\"0\"` string into `0` before a tool call executes — had the\n exact same raw-`__annotations__` bug and silently no-opped for every builtin\n tool; fixed the same way.\n- **Streaming tool-loop could hang a connection forever**: `chat_stream()`'s\n fallback to the sync tool loop (tool calls can't stream token-by-token) had\n no timeout, so a model that didn't cleanly terminate a tool-calling round\n trip left the SSE connection open indefinitely with zero bytes sent. Bounded\n with a 60s `asyncio.wait_for`; on timeout the stream yields a clear message\n instead of hanging.\n\n## [2.14.2] - 2026-07-03\n\n### Fixed\n- **AitherShell session amnesia**: the REPL generated a fresh `session_id` for\n EVERY message, so conversation history never followed across turns (the\n server stored each turn under a different session). One stable id is now\n minted per shell run; `/resume` and `/new` semantics unchanged, and the\n cross-restart auto-restore of the last session now actually works.\n\n## [2.14.1] - 2026-07-02\n\n### Added\n- **Account-tied licensing via device-flow login** — `adk login` now persists the account\n license returned by the device-flow handshake to `~/.aither/license.json` (base64\n `license_key` → `{payload, signature}` envelope), so entitlements from the authenticated\n account carry into every later `adk` invocation without re-auth. Pairs with the fleet-side\n node tool-pack gate that grants packs by plan-tier rank.\n- **`can_use_formbridge` / `can_use_untether` entitlements** (PROFESSIONAL+ rank ≥ 3) backing\n the FormBridge and UNTETHER agent tool-packs.\n\n### Fixed\n- `adk` CLI: guard the optional `memory_graph` singleton (was a latent `NameError` on the\n `/stats`, `/memory`, and save paths when memory was not constructed).\n\n## [2.14.0] - 2026-07-02\n\n### Added\n- **`adk.memory_wiki` — LyraWiki-style self-managed semantic knowledge** (opt-in; nothing constructs it by default): `MemoryWiki(graph_memory)` maintains curated wiki ARTICLES as `wiki_article` nodes (title/slug, LLM-owned markdown with `[[wikilinks]]`, embedding, durable tier, `CONSOLIDATED_FROM` edges to source memories, governance-ledgered revisions).\n - `consolidate(llm, since=, budget=)` clusters unconsolidated episodic/fact nodes (embedding + keyword), drafts/UPDATEs articles via the INJECTED llm callable (`str -> str` or `messages -> str`, sync or async — no hard model dependency), cites source node ids, marks contradicted sources superseded, then DEMOTES consolidated sources to a fast-decay tier via `promote()` (represented knowledge doesn't need raw copies).\n - `lint(llm=None)` — deterministic health checks (orphan wikilinks, empty/stale articles, live-but-contradicted pairs) + an optional LLM audit pass.\n - `prune(relevance_floor=0.05, hard_delete_after_days=14)` — article relevance = tier freshness × reinforcement (reinforce-on-recall) + link-degree to LIVE memories; below the floor → reversible governance tombstone; entombed past the window → **HARD-DELETE** (the tombstone snapshot itself is purged — content and embedding irrecoverable; the true-deletion path). A node is never deleted without a tombstone first.\n - `recall(query, limit)` — article-first RAG (curated articles rank above raw nodes) that reinforces returned articles.\n- **`adk.routines` — agent heartbeat + self-programmed routines**: `RoutineStore` is a durable registry (`~/.aither/routines/{agent}.json`, `AITHER_DATA_DIR`-aware, injectable path) of cron-scheduled self-prompts `{name, cron, instruction, enabled, last_run, last_result, tags}` that rehydrates into the existing `adk.cron.CronScheduler` on `start()`. A fire runs `agent.chat(instruction)` with the agent's full toolset — the agent programs its future self with its own adk. Guardrails: `max_routines` (12), a 5-minute min fire interval per routine, a per-fire timeout, and every result ledgered (truncated) to `last_result`. Direct-callable routines (`register_direct`) fire a bound method instead of a self-prompt. Self-management tools (`routine_create/list/update/pause/resume/delete/run_now`) ship as OpenAI tool defs + handlers (`build_routine_tools` / `register_routine_tools` / `routine_tool_defs`); the handlers ONLY touch the RoutineStore — they can never modify agent config or safety-relevant settings (the leash principle).\n- **`AitherAgent(routines=True, memory_maintenance=True)`** (both default **False** → the default agent is byte-identical): `routines=True` builds the RoutineStore + scheduler, registers the self-management tools, and starts the heartbeat lazily on the first `chat()` (or via explicit `start_routines()`). `memory_maintenance=True` registers DEFAULT routines — `wiki_consolidate` (every 2 h), `wiki_lint` / `wiki_prune` / `graph_sweep` (daily) — as DIRECT method fires so memory upkeep runs even on tiny models, while staying visible/manageable via `routine_list`.\n- `TombstoneStore.purge(tombstone_id)` — the hard-delete endpoint: irrecoverably removes a tombstone snapshot and rewrites the persisted store without it (consumed by `memory_wiki.prune`).\n\n## [2.13.6] - 2026-07-02\n\n### Added\n- **Qdrant backend for GraphMemory dataplane sync — TRUE two-way, verified live.** Set `AITHER_FLEET_QDRANT_URL` and the sync uses Qdrant (`upsert` + `scroll` + filtered `search`) instead of Nexus. This is what actually delivers cross-agent memory sharing: a node is upserted with its OWN embedding under a deterministic per-(tenant,node) point id (idempotent re-push), and `fleet_pull` reliably SCROLLS a tenant's points back — the enumerate Nexus lacks (its `/search` doesn't return ingested docs and `/export` needs lancedb, which has a hard dependency conflict in the fleet). **Proven live with 2 agents:** agent-1 ingests → auto-syncs to the tenant dataplane; a fresh agent-2 in the same tenant `fleet_pull`s all of agent-1's nodes and can search them; a different tenant pulls nothing (isolation). Dimension-safe (never mixes vector dims in a collection). Local SQLite stays source of truth; best-effort, never raises.\n\n## [2.13.5] - 2026-07-02\n\n### Fixed\n- **CRITICAL — GraphMemory dataplane sync push was 100% failing.** `_fleet_push_node` sent `source_type=\"graph\"` / `content_type=\"graph_node\"`, which AitherNexus rejects as invalid enums (HTTP 500) — so NO graph node ever replicated in 2.13.4. Now sends the accepted `source_type=\"manual\"` / `content_type=\"text\"` (graph provenance is carried in `metadata.synced_from`). **Verified live** against a fleet Nexus: a 4-node ingest replicates with `pending==0` and the collection count reflects it.\n\n### Added\n- **Swarm-awareness notification** — on a successful sync batch, `GraphMemory` emits a best-effort `graph.synced` event (tenant/workspace/agent/collection/count) to `AITHER_FLEET_EVENTS_URL` so OTHER agents in the tenant/swarm can pull the fresh data and deconflict in-flight work (push-based awareness instead of polling). No-op unless the env is set.\n\n### Known limitation\n- Cross-agent **rehydration** (`fleet_pull`) is only reliable when the fleet Nexus is backed by lancedb (persistent + exportable). An in-memory Nexus stores on `/ingest` but neither its `/search` nor `/export` returns the docs — two-way read needs the persistent Nexus.\n\n## [2.13.4] - 2026-07-02\n\n### Added\n- **Memory-maintenance primitives on `GraphMemory`** (all opt-in; default behaviour byte-identical):\n - **Reinforce-on-recall**: `recall_with_activation(..., reinforce=True)` bumps `reinforcement_count`/`last_reinforced` on every RETURNED node's metadata mirror (one UPDATE per node, WAL-safe) — making the `MemoryRecord` reinforcement fields the activation scorer reads LIVE. Default `False` = zero writes.\n - **`sweep(now, archive_below=0.05, max_nodes=None, dry_run=False)`**: read-time decay enforced at rest — archives nodes past their tier TTL with freshness×reinforcement score below `archive_below` and `reinforcement_count <= 1`; `max_nodes` additionally archives the lowest-scored overflow. PERMANENT tier immune. Every archive is a reversible governance tombstone (`TombstoneStore.recover`) + a FORGET ledger entry; a node is never deleted without a tombstone. `dry_run` returns the would-archive list without acting.\n - **`promote(node_id, tier=?, role=?)`**: re-tier/re-role a node IN PLACE (tier/role columns + metadata mirror) — edges, embedding and ledger history survive; ledgers an UPDATE entry when governance is on.\n- `MutationType.UPDATE` ledger entry kind (in-place re-tier/re-role).\n- **Per-tenant dataplane sync for `GraphMemory`** — replicate the local graph to a per-tenant vector dataplane (Nexus/Qdrant) so a sovereign agent's memory is durable + rehydratable across restarts and hosting boundaries. `fleet_push_all_nodes()` (catch-up), `fleet_pull()` (best-effort semantic top-up), `fleet_sync_pending()`, and auto-sync on ingest (`_maybe_autosync` + `drain_sync()`). **On by default** (`AITHER_FLEET_SYNC=auto`; only `false/0/off` disables) with an inferred target (local Nexus `:8122` or the gateway in cloud mode) and a SEPARATE collection (`AITHER_FLEET_GRAPH_COLLECTION`, default `graph_memory`) from the KV memory sync. New `GraphMemory(tenant_id=, workspace_id=, fleet_url=, auto_sync=)` constructor args override env (required for multi-tenant host processes). Node `synced` column + schema v4 migration. TLS verify is CA-bundle-aware (`AITHER_CA_BUNDLE`) for the internal-TLS mesh. Local SQLite stays the source of truth; every sync path is best-effort and never raises into ingest.\n\n## [2.13.3] - 2026-07-02\n\n### Added\n- **Canonical self-deploying embeddings provider** (`adk.embeddings`): one embedding provider for the whole SDK so vectors are portable across scopes (platform / tenant / sovereign) — same model + dimension everywhere. Pinned to `nomic-embed-text` (768-d; the vLLM `--served-model-name` for nomic-embed-text-v1.5). Lazy, single-flight resolution chain: explicit `AITHER_EMBEDDINGS_URL` → local vLLM (`:8209` then `:8120`, HTTPS-then-HTTP) → local Ollama → gateway (`AITHER_GATEWAY_EMBEDDINGS_URL`) → auto-deploy a local vLLM embeddings container if a GPU + Docker are present (opt out with `AITHER_EMBED_AUTODEPLOY=0`) → CPU sentence-transformers (384-d, degraded) → feature-hash (384-d, degraded, always works). Every batch is dimension-tagged so callers never silently mix 768-d and 384-d vectors. `get_default_embedder()`, `embed_texts()`, `embed_one()`, `get_provider()`, `reset_provider()`.\n\n### Changed\n- **`GraphMemory` now defaults its embedder to the canonical provider** (opt out with `AITHER_GRAPH_EMBEDDER=legacy`), so every adk agent shares one 768-d space. Added a meta-table dimension guard: the index is pinned to its first embedding's dimension and later different-dimension vectors are refused (kills 768↔384 mixing that silently poisons cosine similarity).\n\n## [2.13.2] - 2026-07-01\n\n### Added\n- **Session persistence**: AitherShell now auto-saves the active session_id to `~/.aither/config.yaml` after each chat turn and auto-restores it on the next shell launch. Multi-turn conversations survive across restarts — the messages were always persisted in SQLite + JSON, only the session pointer was missing.\n- `/new` command to start a fresh session (clears saved session_id).\n- `/sessions` command to list recent sessions with agent name, message count, and timestamp. Resume any with `/resume <id>`.\n\n## [2.13.1] - 2026-07-01\n\n### Fixed\n- **CRITICAL:** `deploy_connect()` now sends a HEAD request to verify the GitHub release asset exists before downloading, with a clear error if the asset is missing.\n- **CRITICAL:** Compose file URLs are now pinned to a stable commit hash (configurable via `AITHER_COMPOSE_PIN` env) instead of tracking the mutable `main` branch.\n- **CRITICAL:** `deploy_adk_node()` now falls back to starting `adk-serve` natively when Docker is unavailable, instead of blocking with \"Docker not installed\".\n- **HIGH:** GHCR login failure now blocks deployment immediately with actionable guidance, instead of warning and continuing to a confusing image-pull failure.\n- **HIGH:** Health checks now stream the last 30 lines of Docker Compose logs on failure, so the user can see why a container didn't start.\n- **HIGH:** vLLM port allocation now probes for conflicts before assigning; if port 8200 is busy, workers are automatically remapped to the next free port.\n- **MEDIUM:** Compose file downloads are now cached with ETag; re-running `adk deploy node` skips re-downloading an unchanged file.\n- **MEDIUM:** DGX Spark discovery now respects `AITHER_DGX_HOSTS` env (comma-separated) for custom network topologies, instead of only checking `spark.local` and `192.168.0.33`.\n- **MEDIUM:** Cloud API keys (Anthropic, OpenAI, DeepSeek) are now validated with a lightweight probe during `adk setup` infra scan, so invalid keys surface early.\n- **MEDIUM:** `deploy_connect()` now validates the extracted extension has a `manifest.json` and warns if it's missing.\n\n## [2.13.0] - 2026-07-01\n\n### Removed\n- **`adk.faculties.memory_graph`** — the pickle-based `MemoryGraph` module is deleted. `from adk.faculties import MemoryGraph` now resolves to the canonical `adk.graph_memory.GraphMemory` for back-compat.\n- **`adk.platform.memory`** — the entire legacy memory subsystem (`MemoryManager`, `UnifiedMemorySystem`, `GameEngine`, `StoryboardEngine`, `AnchorGenerator`) is deleted. Agent memory is `adk.memory.Memory` + `adk.graph_memory.GraphMemory`.\n\n### Changed\n- `adk work` command now uses `GraphMemory` instead of the removed pickle-based faculty graph.\n- `adk.MemoryGraph` and `adk.faculties.MemoryGraph` lazy exports now resolve to `adk.graph_memory.GraphMemory`.\n- `agent.set_memory_graph()` kept for back-compat but documented as deprecated; agents already wire `GraphMemory` automatically in `__init__`.\n- Platform startup (`adk.platform.infrastructure.startup`) gracefully handles missing `MemoryManager`.\n- Platform CLI module listing and health check updated to reflect removed memory module.\n\n## [2.12.7] - 2026-07-01\n\n### Deprecated\n- `adk.faculties.MemoryGraph` — emits `DeprecationWarning` on instantiation; use `adk.graph_memory.GraphMemory` (SQLite-backed, governed, embedding-native) instead.\n- `adk.platform.memory` package — the legacy `MemoryManager`, `UnifiedMemorySystem`, `GameEngine`, and `StoryboardEngine` modules now emit `DeprecationWarning` on import; use `adk.memory.Memory` and `adk.graph_memory.GraphMemory` for new code.\n\n### Changed\n- Updated `adk.faculties` docstring and usage examples to point at the canonical memory surface.\n\n## [2.12.6] - 2026-07-01\n\n### Changed\n- Trimmed public package debt by removing grid runbook artifacts and shell/room binary placeholder directories from published artifacts.\n- Removed stale public-sync allowlist entries for deleted phase test files and added explicit public-sync guards for binary placeholder directories and grid artifacts.\n- Updated the public README grid note so it no longer links to runbook files excluded from the SDK package.\n\n## [2.12.5] - 2026-07-01\n\n### Added\n- Added best-effort fleet memory sync from local ADK memory into Nexus/Qdrant-compatible RAG stores, including batch catch-up and vector-search recall hooks.\n- Added saved config/env support for fleet memory URL, collection, and sync mode.\n\n### Changed\n- Excluded FormBridge runtime/test internals from public ADK packaging and public repo sync.\n\n## [2.12.4] - 2026-07-01\n\n### Added\n- Added a hot-reload endpoint for installed agent packs so newly applied pack tools can be picked up without restarting `aither-serve`.\n\n### Fixed\n- Fixed synchronous `ChatRelay` calls on Python 3.12 when no event loop is running; async WebSocket delivery is still scheduled when a loop exists.\n- Aligned gateway inference tier tests with the current public ladder: free, starter, pro, enterprise, platform.\n\n## [2.12.0] - 2026-06-28\n\n### Added — one-command local inference + upgrade path (click-to-run)\n- **`adk quickstart-local`** — detects hardware, picks a backend\n (Ollama / llama.cpp / vLLM), installs it, downloads a model, verifies, and\n prints next steps. Bootstraps Ollama itself via winget/brew/install.sh when\n missing (no \"go install it\" dead-ends). Default model `gemma4:e2b`.\n- **`adk backend switch <ollama|llamacpp|vllm>`** (+ `backend status`) — migrate\n the active deployment between engines; re-points config and smoke-tests.\n- **`adk install pack:<name>`** (+ `adk install list` / `adk packs`) — install\n ready-made agent packs (openclaw, hermes, claude-code) into `~/.aither/agents`.\n\n### Fixed (found via live end-to-end testing on Windows + CUDA)\n- quickstart-local reported success on a dead endpoint (smoke test was warn-only);\n a failed smoke now fails the command, with a service-readiness wait.\n- `--model` resolved to `None` (getattr default never fired) → uses the real default.\n- Smoke test: 30s→180s timeout for cold model load; accepts reasoning-model output\n (empty `content` at low token budgets is no longer a false failure).\n- `adk install pack:<name>` colon syntax (argparse rejected it before dispatch).\n- `adk backend status` health probe used a nonexistent `/health` path → 404; now\n probes `/v1/models` (uniform OpenAI-compatible liveness for all three backends).\n- Windows: removed non-ASCII glyphs from printed strings that crashed the cp1252\n console (a `✓` even made a successful install report failure).\n\n## [2.11.1] - 2026-06-14\n\n### Fixed — `adk host` / `adk login` device flow (autonomous onboarding)\nFound driving `adk host` against production; all broke real device-flow login:\n- **Device flow hit the portal FRONTEND, not AitherIdentity.** `adk login`/`adk host`\n POSTed `/auth/device/code` to portal/veil.aitherium.com, which 307-redirects API auth\n to `/login`. Added `_resolve_identity_url()` mapping the aitherium.com topology to\n `idp.aitherium.com` (the Identity API); localhost/bespoke pass through unchanged.\n- **Cloudflare 403'd urllib.** `idp.*` sits behind Cloudflare, which blocks urllib's\n default `Python-urllib/3.x` UA. Device-code + poll requests now send a real\n User-Agent + Accept (verified urllib→idp 200, was 403).\n- **`adk host` now self-authenticates via device flow** when there's no token, and\n **retries via device flow on a 401/403** (a stale saved token no longer dead-ends\n registration).\n\n## [2.11.0] - 2026-06-14\n\n### Added — attach your own MCP \"hands\" to a self-hosted agent\nA self-hosted agent now has the full brain (your model) · body (this loop) · **hands**\n(your tools) trifecta. The AitherOS platform relays the MCP servers you registered for\nyour agent in the `/stream` request body as `mcp_endpoints`, and the loop wires their\ntools into the ReAct turn:\n- **`adk/mcp_endpoint_tools.register_mcp_endpoint_tools(agent, endpoints)`** — speaks MCP\n JSON-RPC (`tools/list` to discover, `tools/call` to invoke) and registers a namespaced\n proxy tool (`{endpoint}__{tool}`) for each advertised tool. Mirrors `app_proxy_tools`.\n Best-effort: an unreachable endpoint registers nothing and never raises.\n- **`POST /stream` and `POST /chat/stream`** accept an optional `mcp_endpoints` list\n (`[{name, url, [headers]}]`); `_aitheros_stream` attaches them to the agent before the\n turn. Existing callers are unaffected (the field is optional).\n\n## [2.10.1] - 2026-06-14\n\n### Fixed — self-hosted `aither-serve` was crash-on-startup (2.10.0 regression)\nA fresh `pip install aither-adk && aither-serve --backend deepseek` could not boot. Found\nand fixed while standing up a real self-hosted DeepSeek agent end-to-end:\n- **Startup `NameError: name 'port' is not defined`** in three lifespan helpers\n (`_join_aithernet`, `_init_a2a_server`, `_start_mesh_hosting`) — they referenced a bare\n `port` no longer in scope. Now use `config.server_port`, and `main()` writes the resolved\n `--port`/`--host` back to `config` so every lifespan helper and the invoke_url see the\n actual bound port.\n- **Fleet self-registration crashed the whole server** — the registration handlers caught\n `(ImportError, RuntimeError, OSError, ConnectionError)` but **not** `httpx.HTTPError`, so a\n `RemoteProtocolError`/connect error talking to the control plane took the agent down. All\n network-facing lifespan handlers now also catch `httpx.HTTPError` (non-fatal, as intended).\n- **`--backend` was ignored when `AITHER_API_KEY` was set** — `_auto_detect` picked the cloud\n gateway before the explicit backend, and `_try_elysium_fallback` then replaced the chosen\n provider. An explicit backend (deepseek/openai/anthropic/your own vllm/ollama) now always\n wins; \"auto\"/\"gateway\" still flow through detection. The operator's own brain is never\n silently hijacked.\n- **Approved-tool trace truncated after `/sessions/{id}/confirm`** — the SSE relay treated\n `AgentResponse.tool_calls_made` (a `list[str]`) as dicts (`tc.get(...)`), raised\n `AttributeError`, and an over-narrow `except` swallowed it so the client saw only\n `session_start` + heartbeats. The relay now handles string tool names, the `except` is\n broad (always emits a typed `error` + terminal `complete`), and the module-level\n `_strata_ingest` `NameError` + Chronicle `session_id` kwarg mismatch are fixed.\n\n## [2.10.0] - 2026-06-13\n\n### Added — human-in-the-loop tool approval (`adk/approval.py`)\n- **Pause before a gated tool, resume on decision.** An agent can now pause its turn before\n executing a sensitive tool and wait for a human's allow/deny — so a managed control plane\n (or any operator) can gate tool use without forking the loop.\n- **Policy via `AITHER_TOOL_APPROVAL`** — a comma-separated list of tool names (optionally\n `agent:tool`), or `*` for every tool. Empty = no gating (unchanged behavior).\n- **Survives restarts / days-later approvals.** The paused turn (user message + pending tool\n calls) is persisted to disk keyed by `session_id`; resume is a fresh request, not a held\n connection. Decisions are recorded per `(session_id, tool_name)`.\n- **`AgentResponse.requires_action` / `.pending`** — set when a turn paused; `pending` lists\n the gated tool calls awaiting a decision.\n- **`AitherAgent.resume(session_id, decisions)`** — records the decisions and re-runs the\n paused turn (the gate consumes them: allowed tools execute, denied tools get a denial\n observation, the turn continues — and may pause again on a different gated tool).\n- **Server:** `POST /sessions/{id}/confirm` records decisions and streams the resumed turn;\n `/stream` emits a `requires_action` event when a turn pauses.\n\n## [2.9.0] - 2026-06-13\n\n### Added — universal LLM continuation primitive (`adk/llm/continuation.py`)\n- **`run_continuation()` + `stitch()`** — one shared continue-until-complete primitive. When a\n completion stops because it hit the output token cap (`finish_reason == \"length\"`), it continues\n the generation and STITCHES the chunks (overlap-dedup + restart-detect) into a complete answer,\n instead of every call-site hand-rolling its own retry. Bounded three ways (max rounds AND total\n output ceiling AND a no-growth break), and it never mutates the caller's message history.\n- **`LLMProvider.chat_with_continuation()`** and **`LLMRouter.chat_with_continuation()`** — every\n provider AND the router inherit the primitive, so any call-site gets continuation for free.\n- Kill-switch `ADK_LLM_CONTINUATION=off`; tunables `ADK_LLM_MAX_CONTINUATIONS`,\n `ADK_LLM_MAX_TOTAL_OUTPUT_TOKENS` (defaults: 2 rounds / 8192 tokens).\n\n### Changed\n- The ReAct loop's truncation recovery now uses the shared primitive. The previous inline\n doubling-retry REPLACED the partial response (losing earlier text in `resp.content`) and injected\n continuation scaffolding into the live message history; it now STITCHES and leaves history clean.\n Tool-call turns are never continued as text.\n\n### Included — previously-committed-but-unreleased changes\n- 2.8.2: TLS verification on by default (`verify=False` removed).\n- 2.8.1: baked license + pack-signing public keys.\n- 2.8.0: version-bump release.\n\n## [2.7.0] - 2026-06-05\n\n### Added — provider-agnostic LLM cost levers (at the `LLMProvider` ABC)\n- **Prompt caching.** New `ProviderCapabilities` (`prompt_cache: explicit|automatic|none`, `batch`) and\n normalized `LLMResponse.cache_read_tokens`/`cache_write_tokens`, so the \"tokens saved\" meter never\n depends on which backend served the turn. Anthropic inserts `cache_control` breakpoints over the stable\n system+tools prefix; OpenAI/DeepSeek caching is automatic and their `cached_tokens` are now surfaced\n (previously discarded). Default-on in `AitherAgent.chat`; override with `cache=False`.\n- **ResponseCache** (`adk/llm/cache.py`) — exact-match response cache with a pluggable backend, wired into\n `LLMRouter` as an opt-in (`LLMRouter(response_cache=…)` + per-call `cacheable=True`). OFF by default — no\n determinism change unless opted in.\n- **Gemini provider** (`adk/llm/gemini.py`) with explicit `cachedContents` caching; registered in the router.\n- **Async batch runner** (`adk/llm/batch_runner.py`) — `run_batch()` over the Anthropic Message Batches /\n OpenAI Batch APIs (~50% off) with a transparent fallback to concurrent `chat()` where unsupported.\n- **Graph-RAG retriever interface** (`adk/graph_retriever.py`).\n\n### Added — principal authority (prompt-injection defense enforced outside the model)\n- `adk/auth.py`: `Principal` / `AuthContext` / `PrincipalResolver` (+ deny-all default).\n- `ToolRegistry.execute(name, args, auth=…)` — a default-deny authorization gate at the tool boundary, so a\n forbidden tool call is blocked regardless of what the LLM emits. `ToolDef` gains `required_clearance` /\n `action_class`. Threaded through `AitherAgent.chat`; `auth=None` is fully backward-compatible.\n- `adk/channel_auth.py`: Telegram webhook-secret + CEO-id checks and HMAC-signed approvals.\n\n## [2.6.4] - 2026-06-05\n\n### Fixes\n- **Loop-guard nudges corrupted the message history → DeepSeek 400 mid-run.**\n When the ReAct loop emitted multiple tool calls in one assistant turn and the\n loop guard fired a WARN/BLOCK/CIRCUIT_BREAK on one of them, it spliced a\n standalone `system` nudge message *between* the tool results. DeepSeek (and\n strict vLLM chat templates) require an assistant `tool_calls` message to be\n followed by exactly one `tool` result per `tool_call_id`, contiguously — so the\n interleaved message produced `400 … insufficient tool messages following\n tool_calls message` and aborted the turn (this is reliably hit after crossing\n the 15-call circuit-break threshold, where every subsequent call WARNs).\n Per-call guidance is now folded into that call's own `tool` result, and any\n standalone steering is deferred to a single message AFTER the whole batch —\n preserving the `tool_calls`↔`tool_results` pairing every backend requires.\n Regression test: `deep-research-agent/tests/test_msg_structure.py`\n (loopguard-flood scenario).\n\n## [2.6.3] - 2026-06-04\n\n### Fixes\n- **Transient provider errors killed a run on the first blip.** A single\n `502 Bad Gateway` / `503` / `529 overloaded` / `429` from the upstream API\n aborted the whole request — the Anthropic provider had **no retry at all**, and\n the OpenAI-compatible provider only retried `429`. Both now retry transient\n statuses (`408/429/500/502/503/504/529`) with `Retry-After`-aware exponential\n backoff (3 attempts), and surface the provider's body if all retries are\n exhausted. Non-transient `4xx` (e.g. `400`) still fails fast. Applies to\n `chat` and `chat_stream` on both providers.\n\n## [2.6.2] - 2026-06-04\n\n### Fixes\n- **DeepSeek (and strict OpenAI-compatible backends) 400'd on long tool\n conversations.** The ReAct loop legitimately injects mid-conversation\n `system` steering messages (the \"[DIMINISHING RETURNS]\" hint after several\n low-output tool iterations, loop-guard nudges). OpenAI tolerates a non-leading\n `system` message; DeepSeek rejects it with a 400. Long research runs\n (many `fetch_url` calls) reliably tripped this. Fix: non-leading `system`\n messages are demoted to `user` at the OpenAI-compatible provider boundary\n (`_demote_nonleading_system`) — steering intent preserved, payload valid on\n every backend; leading system prompt(s) untouched.\n- **Opaque HTTP errors.** `resp.raise_for_status()` discarded the provider's\n response body, so a 400/422 surfaced as a bare `Client error '400 Bad\n Request'` with no cause. `_ensure_ok` now includes the provider's actual\n message (e.g. `context_length_exceeded`, `Invalid 'messages'`) in the raised\n error, for both `chat` and `chat_stream`.\n\n## [2.6.1] - 2026-06-04\n\n### Fixes\n- **`MCPServer.mount()` returned HTTP 422 for every `/mcp` call.** Under\n `from __future__ import annotations` the route handler's `request: Request`\n annotation is a string that FastAPI resolves against the *module* globals, but\n `Request` was imported locally inside `mount()` — so FastAPI couldn't resolve\n it and treated `request` as a required query param. The MCP server endpoint\n (`initialize`, `tools/list`, `tools/call`) was unusable when mounted. FastAPI\n symbols are now imported at module level (guarded; the `[node]` extra still\n gates actual use). Any agent's tools can now be exposed as a working MCP\n server with `MCPServer(...).mount(app)`.\n\n## [2.6.0] - 2026-06-04\n\n### Streaming + scaffolding\n- **`AitherAgent.stream_chat`** — reliable streaming sibling of `chat()`: native\n tool-calling loop (concludes on its own, unlike the text-protocol `stream_react`)\n that emits live `tool`/`tool_result` events and streams the answer as `token`\n events. `token_delay` paces tokens for a typing effect.\n- **`adk new <template>`** — scaffold a full, runnable template app. Ships the\n `deep-research` template (a cited web-research agent: pack + server + web UI):\n `adk new deep-research`.\n\n### Fixes\n- **BYO-key no longer community-capped.** The free-tier monthly token cap now applies\n only to the metered Aitherium gateway backend, never to a user's own provider key\n (Anthropic/OpenAI/DeepSeek/Ollama/vLLM). Previously it could brick a self-hosted\n agent after ~100k tokens on the user's own key.\n- **Stronger, grounded memory recall.** `chat()` now injects the top-6 knowledge-graph\n hits (untruncated) with a hard no-fabrication instruction — was top-3 truncated to\n 200 chars, which surfaced partial facts and let weaker models invent the rest.\n- **Tool-arg coercion.** `ToolRegistry.execute` coerces LLM-supplied args toward each\n parameter's annotated type, so a tool no longer crashes when a model passes a list\n or string where an int/float/bool was expected.\n\n### Docs\n- New `docs/AGENT_DEV_GUIDE.md` — the golden path for building agents/packs.\n\n## [2.5.0] - 2026-06-04\n\n### Continual learning — agents that learn from runs and reuse memory\n\nAgents now improve the second time they do a task instead of starting from scratch.\n\n### Added\n- **Recall-before**: `AitherAgent.chat()` searches the local `SkillStore` and injects the\n top matching learned skills as a system block, so the model reuses proven procedures.\n- **Learn-after**: `_learn_after()` runs after each response — reinforces (`success_count++`)\n any skills that were recalled+reused, and extracts + saves a NEW skill from successful\n multi-tool runs via `SkillExtractor`. Wired at both `chat()` return paths.\n- Gated by `AITHER_SKILLS` (default on). The previously-unused `SkillStore`/`SkillExtractor`\n are now connected to the run loop.\n\n### Notes\n- Pairs with the platform-side LearnedProcedure substrate (recipes → scored procedures →\n skills/tools/A2A/packs → MCTS priors → evolution). The SDK shares memory via the Spirit\n bridge when configured.\n\n## [2.0.0] - 2026-06-02\n\n### Open-core boundary (BREAKING)\n\nThis release draws a single, enforced free/paid line. The free tier stays\ngenuinely useful (real agent, typed + graph + code memory, ReAct, ~essential\ntools); paid tiers unlock the *scale* capabilities via portal.aitherium.com.\n\n### Added\n- **`adk/licensing.py`** — the entitlement keystone. Resolves a tier\n (COMMUNITY by default), verifies optional portal-signed `~/.aither/license.json`\n (Ed25519), and answers `can_use_fleet/channels/cron/swarm/auto_neurons`,\n `is_agent_licensed`, `max_effort`, etc. **Fail-closed**: no/invalid/expired\n license → free tier; an unsigned license never grants premium.\n `AITHER_TENANT_SLUG=aitherium` → unrestricted INTERNAL tier.\n- **Moat guard** (`scripts/check_moat_boundary.py`) wired into the publish\n workflow — blocks any wheel that leaks the moat or drops the keystone.\n- Tests: `tests/test_licensing.py`, `tests/test_moat_boundary.py`.\n\n### Changed (gating — fail-closed)\n- Free tier effort is capped at 3 (reasoning effort 7–10 needs Professional).\n- Monthly token budget hard-block on the free tier (no silent overspend).\n- Gated behind a paid tier: fleet mode (`FleetConfig`), cross-agent delegation\n (`AgentForge.delegate`), channel adapters, cron scheduler, proactive\n auto-neurons, and `swarm`/`swarm_code` dispatch.\n\n### Removed\n- **`adk/nanogpt.py`** (on-device training IP) removed from the published SDK\n and the public repo; relocated to the internal AitherOS moat. Excluded from\n the wheel as defense-in-depth.\n- Pre-2.0 releases shipped this IP and ungated capabilities and are being\n yanked (PyPI) / removed (GitHub releases). See `scripts/yank_leaky_releases.py`\n and `scripts/purge_public_leaks.sh`.\n\n## [1.20.0] - 2026-05-27\n\n### Fixed\n- **Pack discovery path mismatch** — `_find_local_packs()` now scans both\n `~/.aither/packs/` and `~/.aitheros/packs/`, so packs installed via\n `adk pack install` are discovered by `adk-workspace`.\n\n### Added\n- **Auto-register on pack install** — `adk pack install` now auto-registers\n with the portal if the pack contains an `agent.yaml` with a `portal` section.\n One command does download + extract + register.\n- **Flux event on agent registration** — `developer_portal.py` emits\n `agent.registered` Flux event so the portal fleet UI updates in real-time.\n\n## [1.15.0] - 2026-05-26\n\n### Added\n- **Cloud memory** — `adk quickstart --cloud` now tests gateway memory and\n auto-configures Spirit URL so memories persist across devices via\n `gateway.aitherium.com/v1/memory/teach` and `/v1/memory/recall`.\n- **Config→env export for cloud mode** — `Config.from_env()` now exports\n `AITHER_CLOUD_MODE`, `AITHER_SPIRIT_URL`, and spirit path env vars from\n saved config so Memory and other modules pick them up without needing\n explicit env vars set by the user.\n- **Cloud Quick Start section** in README — 3-command setup for users who\n just want a Claude/GPT-4/DeepSeek API key and the full agent harness.\n- **`--memory` profile for node deployment** — `adk deploy node --memory`\n adds Spirit (8087) + WorkingMemory (8101) containers for persistent\n vector memory. Without this, agents use local SQLite only.\n- **Configurable vLLM in node compose** — `AITHER_VLLM_MODEL`,\n `AITHER_VLLM_GPU_UTIL`, `AITHER_VLLM_CTX_LEN`, `AITHER_VLLM_QUANT_ARGS`\n env vars let `adk setup` write GPU-detected tier settings to `.env`\n and have the node compose pick them up (TQ4 for small GPUs, BNB for large).\n\n### Fixed\n- **Memory cloud routing** — `Memory._spirit_teach()` and `_spirit_recall()`\n now use configurable paths and auth headers for gateway proxy. Previously\n cloud-only users silently lost all Spirit memory calls.\n\n## [1.9.0] - 2026-05-24\n\n### Added\n- **Composable ToolPack system** — agents can load tool packs from\n `.toolpack.yaml` directories. Drop a pack in `~/.aitheros/packs/` or\n set `AITHER_PACKS_DIR`, and any agent discovers it automatically.\n- **`tools.packs` in agent.yaml** — list tool pack IDs to auto-load\n (e.g. `packs: [crm-tools, calendar-pro]`).\n- **`register_tool_packs(agent, pack_ids, packs_dir)`** in `builtin_tools` —\n programmatic pack loading for agents.\n- **`AITHER_TOOL_PACKS` env var** — comma-separated pack IDs loaded on\n agent init (alternative to YAML config).\n- **Unknown builtin categories resolve as pack IDs** — `tools.builtin:\n [file_io, crm-tools]` now works: `crm-tools` is looked up as a pack.\n\n## [1.8.0] - 2026-05-24\n\n### Removed\n- **11 dead modules** (3,023 lines) — `agent_deploy`, `cloud_deploy`, `create`,\n `phonehome`, `sdk_bridge`, `sovereign`, `telemetry`, `telemetry_config`,\n `error_reporter`, `anonymizer`, `package`. None were imported anywhere.\n- **29 ghost exports** from `__init__.py` — `secrets`, `otel`, `fs_sandbox`,\n and `vector_memory` modules were listed in `__all__` and `__getattr__` but\n the backing files never existed. Accessing them raised `ModuleNotFoundError`.\n- **Stale dist/ artifacts** — removed old wheels/tarballs (v0.4.0–v1.4.1, 11 MB).\n- **Orphan test scripts** — `test_phase1.py` and `test_phase23.py` (one-off\n validation scripts not part of the pytest suite).\n\n### Fixed\n- **Version mismatch** — `__init__.__version__` now matches `pyproject.toml`\n (was stuck at 1.6.0).\n\n## [1.5.0] - 2026-05-23\n\n### Added\n- **`--sovereign` flag for `adk deploy node`** — registers the node with the\n Aitherium hub via federation after deployment. Saves federation credentials\n to `~/.aither/.env.federation`. Accepts `--hub` (custom hub URL) and\n `--tenant` (tenant slug) options. Non-fatal: node runs standalone if\n registration fails.\n- **Federation → fleet cross-post** (Genesis) — sovereign nodes that register\n or heartbeat via `/federation/register` and `/federation/heartbeat` now\n automatically appear in the fleet dashboard (`/fleet/deployments`) with\n `node_type: \"sovereign\"` and live status/metrics.\n- **\"Deploy Sovereign Node\" section on Connect page** (AitherVeil) — Section 8\n with ADK deploy commands, fleet dashboard links, and provision page links.\n- **`deploy-node` action in tunnel proxy** (AitherVeil) — Connect page and\n external callers can now request bootstrap scripts via\n `POST /api/tunnel/devworkspace?action=deploy-node`.\n\n### Fixed\n- **Fleet provision page** — API URL corrected from `/api/fleet/provision` to\n `/api/bridge/genesis/fleet/provision` (matches bridge proxy pattern).\n- **Fleet detail page** — API URL corrected from `/api/fleet/deployments/` to\n `/api/bridge/genesis/fleet/deployments/` (same fix).\n- **Dockerfile** — entry point corrected from `aither-serve` to `adk-serve`\n (matches `pyproject.toml` script registration). Version label updated.\n\n## [1.4.1] - 2026-05-22\n\n### Fixed\n- **macOS executable builds** — dropped `--target-arch universal2` from\n `packaging/build_executable.py`. PyPI wheels for `pydantic_core` and other\n native deps are arch-specific, not fat binaries, so universal2 always failed\n with `IncompatibleBinaryArchError`. Now builds for the runner's native arch\n (`aither-macos-arm64`).\n- **Release workflow strategy** — added `fail-fast: false` so a single\n executable build failure no longer cancels the other OS jobs.\n- **PyPI publish** — added `skip-existing: true` so re-runs of the same\n version don't fail when the artifact already exists.\n- **GitHub Release job** — now runs whenever `pypi` succeeds even if some\n executable matrix entries fail, so the release page is always created with\n whatever binaries did build successfully.\n\n## [1.4.0] - 2026-05-22\n\n### Added\n- **Sovereign template now ships with `agent_core` 5-tier memory** — generated\n backends include the canonical `agent_core/` package (working → episodic →\n semantic → procedural → identity) plus `agent_memory.py` helper, vendored\n from `portal-kit-backend`.\n- `aiosqlite>=0.20.0` added to sovereign template `requirements.txt` for\n local agent_core SQLite storage.\n- Document tombstoning: `evict_cached_conversations_referencing` is wired\n through document-delete handlers in consumer apps so memories citing\n deleted documents are blocked from recall.\n- New tests: 13 smoke tests for `portal-kit-backend/agent_core` covering\n promotion thresholds, decay rates, store roundtrip, tombstone recall\n blocking, circuit breaker state machine, and chat-turn recording.\n\n### Changed\n- `scripts/vendor_agent_core.py` now syncs canonical `agent_core` to 4\n targets: WorkspaceRuntime, ADK sovereign template, and the GargBot +\n Chelle consumer mirrors (previously only 2).\n\n## [1.2.1] - 2026-05-21\n\n### Added\n- `adk train` command group — 6 subcommands: status, launch, logs, cancel, runs, register-gpu\n- `/slash-commands` endpoint — ADK server auto-exposes all 35 CLI commands as structured JSON manifest for AitherShell\n- `/cli/execute` endpoint — AitherShell can run any CLI command through the server\n- `build_command_manifest()` — introspects full argparse tree (commands, args, choices, subcommands)\n- `_register_commands()` extracted from main() for shared parser construction\n\n### Changed\n- `_get_genesis_url()` now probes: Genesis → ADK server → Aitherium cloud gateway (works without Genesis)\n\n## [1.2.0] - 2026-05-21\n\n### Added\n- **Runtime backend switching** — `agent.switch_backend(\"deepseek\")` and `agent.llm.switch_backend()` change provider without recreating agent\n- **Hybrid reasoning** — `agent.set_reasoning_backend(\"anthropic\")` routes effort 7+ to cloud API while keeping local orchestrator\n- **First-class DeepSeek provider** — `deepseek` in effort model table with `deepseek-chat` and `deepseek-reasoner`\n- **TQ4 quantization tiers** — `nano` (6GB, TurboQuant 4-bit), `standard-tq4` (12GB, both models TQ4), `hybrid-tq4` (6GB + cloud reasoning)\n- **`adk backend` CLI** — `list`, `set`, `set-reasoning`, `test` subcommands\n- **`adk quickstart`** — unified first-run wizard (setup + auth + shell)\n- **`adk tools`** — list local + MCP tools with tier markers\n- **`adk backup`** — tarball export of `~/.aither/`\n- **`adk ingest`** — feed docs/files into knowledge graph\n- **`adk setup nemotron`** shortcut — alias for `--tier lite`\n- **`--reasoning-api`** flag in setup — `anthropic`, `openai`, `deepseek`, `gateway`\n- **`--dgx-spark URL`** flag — remote vLLM endpoint configuration\n- **DGX Spark auto-detection** — scans `spark.local`, `192.168.0.33`, `AITHER_DGX_URL` env\n- **Post-setup smoke test** — sends real inference request after container startup\n- **Schema migration system** — version table + migration runner in `memory.py` and `graph_memory.py`\n- **Configurable vLLM port scan** — `AITHER_VLLM_PORTS` env var for custom ports\n- **`aithershell` entry point** — alias for `adk` in pyproject.toml\n- Shell auto-downloads binary on first `adk shell` (no `--install` needed)\n- Shell pre-flight check warns if no backend detected\n- Shell passes full config via env vars (API key, tenant, inference URL)\n- DGX/Remote and Cloud API checks added to `adk doctor`\n- Config fields: `reasoning_backend`, `reasoning_api_key`, `deepseek_api_key`, `dgx_url`, `vllm_extra_ports`\n\n### Fixed\n- `test_aeon_chat_basic` timeout — mock scope was exiting before request\n- `adk doctor` check_disk timeout — cap rglob at 5000 files\n\n## [1.1.7] - 2026-05-19\n\n### Added\n- Remote agent pipeline — `adk onboard` → fleet dispatch → remote inference\n- Agent-to-agent federation with mesh relay\n- `adk connect` for desktop mesh enrollment\n\n## [1.1.6] - 2026-05-19\n\n### Fixed\n- Tool call execution recovery across all backends\n- Hermes tool call fallback parsing for vLLM\n\n## [1.1.5] - 2026-05-19\n\n### Fixed\n- Tool call execution + Hermes fallback parsing improvements\n\n## [1.1.0] - 2026-05-18\n\n### Added\n- Multi-channel gateway (Telegram, Discord, Slack, webhook)\n- Aeon group chat with 7 presets\n- Skills auto-extraction from multi-step sessions\n- MCP stdio server for Claude Code integration\n- `adk gateway`, `adk cron`, `adk skills`, `adk soul` commands\n- SOUL.md import/export for portable agent identity\n- Elysium cloud device-flow authentication\n\n## [1.0.0] - 2026-05-18\n\n### Breaking\n- Package consolidated — `aither_adk` namespace replaced by `adk` + `adk.platform`\n- 93 duplicate files removed from `AitherOS/packages/aither_adk/`\n\n### Added\n- Standalone core primitives (`adk.core`: Agent, Tool, Memory, ModelBackend, Capability, Trace)\n- `adk.platform` sub-package merging all platform toolkit modules\n- Compatibility shims for legacy `from aither_adk.*` imports\n\n### Changed\n- Version synced across pyproject.toml, `adk/__init__.py`, `adk/platform/__init__.py`, npm package\n- Classifier promoted from Alpha to Beta\n\n## [0.16.0] - 2026-04-16\n\n### Swarm Coding Engine & Repowise Integration\n- **`repowise_search` tool** — Semantic + keyword hybrid code search via Repowise, with ripgrep fallback\n- **`swarm_code` tool** — Dispatch complex tasks to 11-agent swarm pipeline (ARCHITECT->SWARM->REVIEW->JUDGE)\n- **`agent.swarm()`** — Async convenience method with configurable mode, effort, timeout\n- **`agent.code_search()`** — Async convenience method returning structured results\n- New tool categories: `repowise` and `swarm` in TOOL_CATEGORIES\n- `repowise_search` and `swarm_code` added to `__init__.py` exports with lazy loading\n- `IDENTITY_DEFAULTS` updated: repowise for code-focused agents, swarm for orchestration agents\n- Standalone graceful degradation: repowise falls back to ripgrep, swarm returns structured error\n\n## [0.13.0] - 2026-04-02\n\n### Graph Faculties — Local Knowledge for Every Agent\n- **CodeGraph** (2,799 lines) — Full Python AST indexer with call graph, keyword/semantic/hybrid query, embedding matrix cache, incremental re-indexing, multi-hop chain expansion\n- **MemoryGraph** (1,339 lines) — Graph-based persistent agent memory with 10 edge types, hybrid query (keyword + semantic + graph expansion), multi-hop recall, pickle persistence\n- **EmbeddingProvider** — 4-backend fallback chain: sentence-transformers (GPU/CPU) -> Ollama -> Elysium cloud -> feature hashing (zero deps)\n- **BaseFacultyGraph** — Abstract base with HMAC-SHA256 validated pickle persistence\n\n### Agent Integration\n- `agent.set_code_graph(cg)` — auto-registers `code_search` + `code_context` tools\n- `agent.set_memory_graph(mg)` — auto-registers `remember` + `recall` + `memory_stats` tools\n- Both graphs inject context into LLM prompts automatically during chat\n\n### Zero-Config Onboarding\n- `adk start` / `adk` (no args) — auto-detect project, index code, detect LLM, persistent memory, interactive chat\n- Works for any directory: Python codebases, doc folders, mixed workspaces\n- Auto-detects LLM: Ollama -> vLLM -> Elysium -> OpenAI -> Anthropic\n- Per-project persistent memory in `~/.aither/memory/<project>`\n- `adk index <path>` — standalone indexing with progress bar and stats\n\n### MCP Gateway\n- `POST /v1/embeddings` — OpenAI-compatible embedding proxy to vLLM-embeddings:8209\n- Available to all tiers (embeddings are free)\n- ADK EmbeddingProvider uses this as Elysium cloud fallback\n\n### Optional Dependencies\n- `pip install aither-adk[graphs]` — numpy for 10x cosine similarity speedup\n- `pip install aither-adk[embedding]` — sentence-transformers + torch for local GPU embeddings\n- `pip install aither-adk` alone — graphs work with feature hashing (zero deps)\n\n## [0.12.0] - 2026-04-01\n\n### Bootstrap & Service Discovery\n- Version handshake -- ADK checks major.minor compatibility with Genesis/Node\n- Background reconnect loop -- ServiceBridge re-probes every 30s in standalone mode\n- Port 8080/8090 documented -- MCP vs OpenAI-compat clearly separated\n- Genesis URL configurable via GENESIS_URL env var (was hardcoded)\n- Standalone mode warning -- visible stderr alert when AitherOS not detected\n- Auto-reconnect on startup when services come online\n\n### Elysium Cloud Inference\n- Unified gateway URL -- gateway.aitherium.com handles auth + billing + inference\n- Streaming inference -- SSE passthrough for /v1/chat/completions with stream=true\n- Auth proxy routes -- /v1/auth/register, /v1/auth/login, /v1/auth/me\n- Billing proxy -- /v1/billing/balance through gateway\n- AitherConnect Elysium fallback -- cloud inference when local Genesis is down\n- AitherDesktop Elysium fallback -- third-tier chat fallback after Node\n\n### Infrastructure\n- /discovery endpoint on Genesis -- unified service URLs/versions/health\n- /api/config/services on Veil -- runtime port config for client-side JS\n- Veil healthcheck gates on Genesis -- unhealthy when backend is down\n- Desktop crash detection 90s->60s (threshold 3->2)\n\n## [0.11.0] - 2026-04-01\n\n### Agent Execution Quality (Claude Code Parity)\n- Raise loop guard block threshold from 3 to 4 -- agents get more room for iterative search\n- Soft synthesis nudge for effort >= 4 (no tool stripping, trust the model)\n- max_output_tokens escalation -- retry up to 3x when response is truncated\n- Tool result pairing guarantee -- synthetic error for orphaned tool_use blocks\n- Micro-compaction of old tool results -- save context tokens on long sessions\n- First-turn tool forcing only for effort >= 6 (trust model for lower effort)\n- Diminishing returns detection -- nudge agent when 3+ turns produce < 500 tokens\n- Message normalization -- merge consecutive same-role messages, strip empties\n- LLM retry with exponential backoff (5 retries, 500ms-16s, jitter)\n\n## [0.9.0] - 2026-03-16\n\nThe \"connected world\" release. Cross-platform identity pairing, voice capabilities, and multi-channel integration.\n\n### Added\n- **Pairing**: Cross-platform identity linking (`adk/pairing.py`)\n - `PairingManager` — SQLite-backed identity linking with 6-char pairing codes\n - Link users across Telegram, Discord, Slack, WhatsApp with 10min TTL codes\n - Canonical session IDs for cross-channel conversation continuity\n - `get_session_id()` returns \"user-{id}\" for paired users\n- **Voice**: Speech-to-text and text-to-speech client (`adk/voice.py`)\n - `VoiceClient` — async STT/TTS/emotion via AitherVoice service\n - Convenience functions: `hear()`, `say()`, `feel()`\n - 6 voice options: alloy, echo, fable, nova, onyx, shimmer\n - Emotion detection with intensity scoring\n- New exports: `PairingManager`, `PairingResult`, `PlatformIdentity`, `VoiceClient`, `TranscriptionResult`, `SynthesisResult`, `EmotionResult`\n\n### Changed\n- `__init__.py` exports expanded with pairing and voice symbols\n\n## [0.6.0] - 2026-03-13\n\nThe \"group mind\" release. Multi-agent group chat, creative tools, and Iris identity.\n\n### Added\n- **Aeon**: Multi-agent group chat engine (`adk/aeon.py`)\n - `AeonSession` — persistent group chat with parallel agent execution\n - 7 presets: balanced, creative, technical, security, minimal, duo_code, research\n - Orchestrator synthesis: Aither summarizes all agent responses\n - Serial execution for Ollama, parallel for vLLM/cloud\n - ConversationStore persistence with `type: \"aeon\"` metadata\n - `group_chat()` one-shot convenience function\n- `aither aeon` CLI command — interactive terminal group chat with color-coded agents\n - `-p/--preset`, `-a/--agents`, `-r/--rounds`, `--no-synthesize` flags\n - `reset` and `quit` commands\n- Server endpoints: `POST /aeon/chat`, `GET /aeon/presets`, `GET /aeon/sessions/{id}`\n- Creative tools in builtin_tools: `image_generate`, `image_refine`, `image_search`, `video_generate`\n- Iris agent identity with visual generation capabilities\n- 48 Aeon tests (data models, presets, chat, context, persistence, server, exports)\n\n### Changed\n- `__init__.py` exports: `AeonSession`, `AeonResponse`, `AeonMessage`, `group_chat`, `AEON_PRESETS`\n\n## [0.5.0] - 2026-03-13\n\nThe \"tenant-ready\" release. Multi-tenant admin, permission grants, safety profiles, and a full setup wizard.\n\n### Added\n- `aither setup` interactive setup wizard with hardware detection, model selection, identity config\n- Strata storage backend: SQLite WAL persistence for conversations, memories, knowledge graphs\n- CLI test runner: `aither test` with auto-discovery and parallel execution\n- Permission grants system for MCP tool access control\n- MCP account management tools\n- LLM provider auto-detection for Ollama and vLLM endpoints\n- Apache-2.0 LICENSE file\n- Elysium desktop sync module\n- Comprehensive CLI test suite (493 tests)\n- Strata storage test suite (972 tests)\n- LLM provider tests (82 tests)\n\n### Changed\n- CLI expanded: `aither setup`, `aither test`, `aither bugreport`, `aither doctor`\n- Strata module rewritten as full local-first storage engine\n- Server startup includes Strata initialization\n- README rewritten with clearer quickstart and architecture docs\n\n### Fixed\n- Dead documentation links\n- Python 3.11 compatibility issues\n- Ruff per-file-ignores configuration\n- Identity provisioning edge cases\n- Docker node-gyp native dependency builds (python3 + build tools)\n\n## [0.4.0] - 2026-03-13\n\nThe \"own your AI\" release. Self-hosted agent OS for people who don't want their data on someone else's servers.\n\n### Added\n- Muse agent identity (creative/artistic generation)\n- Port 8120 to vLLM scan for ExoNodes discovery\n- Public roadmap with milestone-based porting schedule\n- Competitive positioning: self-hosted alternative to cloud-locked AI appliances\n\n### Changed\n- LLM router: compute-aware effort routing (replaces effort-level context gating)\n- README rewritten for sovereignty-first messaging\n- Package description updated\n- Promoted from alpha to stable release\n\n### Fixed\n- Version string consistency between pyproject.toml and __init__.py\n- vLLM port scanning now includes port 8120 (ExoNodes)\n\n## [0.3.1] - 2026-03-09\n\n### Added\n- GraphMemory: SQLite knowledge graph with Ollama embeddings and hybrid search\n- NeuronPool: auto-fire pre-LLM data gathering agents\n- NanoGPT: pure Python char-level transformer with LoRA fine-tuning\n- Safety gates: IntakeGuard (input), LoopGuard (recursion), Sandbox (code exec)\n- Event system: EventEmitter with chat/tool/forge event types\n- Builtin tools: identity-based tool selection\n- ServiceBridge: auto-discovery of AitherOS services\n- Streaming: chat_stream with safety gate integration\n- Auth middleware: Bearer token authentication\n- CLI: `aither init` and `aither serve` commands\n\n### Changed\n- Wired 5 previously disconnected modules into agent loop\n- 522 total passing tests (up from 85)\n\n## [0.3.0] - 2026-03-07\n\n### Added\n- Clean-room agent development kit\n- Multi-backend LLM providers (Ollama, OpenAI, Anthropic)\n- AitherAgent class with @tool decorator\n- SQLite conversation memory\n- OpenAI-compatible server (`aither-serve`)\n- 16 agent identities as package data\n- MCP bridge to mcp.aitherium.com\n- Privacy-centric opt-in telemetry\n- Bug reporting CLI and API\n- FastAPI server with OpenAI-compatible endpoints\n- Hardware auto-detection (5 tiers, 11 profiles)\n- Fleet orchestration and multi-agent coordination\n- A2A mesh protocol\n- Federation protocol for cross-instance agent dispatch\n\n### Initial release\n- 85 passing tests\n- Apache-2.0 license\n"}, "56df86a322182d9d0724ba447eff656a7656a3c0": {"t": 1785748156.330672, "n": 98450, "h": "0018dbd4455be74d", "c": "# Changelog\n\nAll notable changes to aither-adk will be documented in this file.\n\n## [2.39.0] - 2026-07-25\n\n### Fixed — containerized agents were unreachable\n\n- **`docker` runtime: added `-i` to `docker run`.** Without it Docker does not\n attach the container's stdin, so *every* containerized stdio agent (acp, mcp)\n silently never received a request. Measured against a real container: no `-i` →\n the child read nothing; with `-i` → the request round-trips. A real customer\n pack *is* a container, so this runtime was effectively unusable.\n- **`runtime.cmd` is now `shlex`-split** for the `docker` and `node` runtimes.\n `cmd: \"python -u server.py\"` was passed as a single argv element, so docker\n tried to exec a binary literally named `python -u server.py`.\n- `Supervisor._build_command()` extracted from `spawn()` so argv is testable\n without launching a process — the missing `-i` survived precisely because it\n wasn't.\n- Env is deliberately **not** forwarded as `-e KEY=value`: that puts credentials\n in argv, readable by any local `ps` / `docker inspect`.\n\n### Fixed — CodeActLoop `cell_timeout` did not bound CPU-bound cells\n\n- `asyncio.wait_for` cannot preempt a coroutine that never yields, so\n `while True: pass` was never interrupted and hung the agent indefinitely. Now\n also bounded by a line-level trace deadline (`CellTimeout`), scoped to the\n cell's own frames so a cell that awaits cannot fire the deadline inside\n unrelated coroutines.\n- The `CodeActLoop` docstring now states plainly that **it is not a sandbox**, and\n names what a cell can actually do (`import os`, `subprocess`, `open(..., \"w\")`).\n Do not run cells from an untrusted source.\n\n### Fixed — MCP stdio hung silently on Windows\n\n- `adk/mcp_stdio.py` and `adk/shell/mcp_bridge.py` attached stdin with\n `loop.connect_read_pipe`, which fails inside the Proactor loop's read handler\n such that the read future never resolves — the server didn't crash, it just\n never answered. Both now use the new shared `adk.stdio_compat`\n (`ThreadStdinReader` / `ThreadStdoutWriter`), which `acp_server.py` also uses.\n\n### Added — per-install pack credentials are now actually used\n\n- `adk/pack_credentials.py` was orphaned: mint/revoke were never called, so pack\n installs kept using the shared credential the module was written to replace.\n Both install paths now mint, and a reinstall revokes the outgoing install's\n credential before its metadata becomes unreachable.\n- **New `/packs uninstall <pack-id>`** (aliases `remove`, `rm`) — revokes the\n per-install credential first, then removes the pack. Previously the only revoke\n path was a reinstall, so deleting a pack any other way left a live,\n unrevocable credential. The target is confined to the pack root.\n\n### Fixed — packaging\n\n- The brew formula's own `sha256` was left as `PLACEHOLDER_SHA256` on every\n release, so `brew install` could never verify the download.\n `sync_versions.py` now fills it from PyPI and `--check` **fails** when a\n published version still carries a placeholder or a stale digest.\n- `sync_brew` rewrote *every* `url` in the formula, repointing the `httpx`,\n `pyyaml`, `fastapi` and `uvicorn` `resource` blocks at the aither-adk tarball.\n Fixed with `count=1`; the resource digests are now real.\n- `adk/__init__.py`'s source-checkout fallback `__version__` had been stale since\n 2.32.0 because the canonical release path never ran `sync_versions.py`. That\n check now runs in `sync-adk.yml`.\n\n## [2.38.1] - 2026-07-24\n\nRelease plumbing only — 2.38.0 was tagged before the version manifests\n(npm/brew/winget) were synced, so its publish gate failed and it never reached\nPyPI. 2.38.1 is the same code with the manifests in sync. See 2.38.0 below for\nwhat actually changed.\n\n## [2.38.0] - 2026-07-24 (never published — superseded by 2.38.1)\n\n### Fixed — `adk join` could only ever have worked for us\n\n- **`adk join` defaulted identity to `https://localhost:8115`** — a fleet-INTERNAL\n address. `adk join` is the one command a stranger runs on their own GPU box, so on\n any machine not already running AitherOS locally, step 1 (the GitHub device flow)\n hit a refused connection, and so did node registration and the mesh-key issue. The\n conductor default was already public, which is exactly why this went unnoticed.\n Identity now defaults to `https://idp.aitherium.com`.\n\n This fix landed *after* the 2.37.0 tag, so **2.37.0 and every earlier release on\n PyPI still ship the broken default** — 2.38.0 is the first release a new\n contributor can actually run. Verified live from a non-fleet machine (DGX):\n `POST /auth/github/device/start` → HTTP 200 with a real `user_code` and\n `verification_uri`.\n\n- **Upgrade check compared versions as strings** — `latest != current` reported a\n downgrade as an available upgrade. Now uses `packaging.version` with a numeric-tuple\n fallback.\n\n## [2.37.0] - 2026-07-24\n\n### Added — Agent Onboarding Fabric: bring your own agent, or run a managed one\n\nOnboard an agent built on **any** framework, or let Aitherium run it for you.\n\n- **ACP (Agent Client Protocol), both directions** — `ACPClient` drives any ACP agent;\n `ACPServer`/`serve_stdio` exposes an adk agent to any ACP host (Zed, VS Code,\n JetBrains). Both proven live against Zed's reference `agent-client-protocol`.\n- **Universal Agent Pack** — `AgentPackManifest` + `Supervisor` describe and run an\n external agent across 6 frameworks × 6 protocols, fail-closed on anything unknown.\n- **Protocol drivers** — `get_driver()` returns an `http`, `langgraph_rest`, `a2a`, or\n `mcp` driver, so REST/graph-orchestrated agents can be *managed*, not just connected.\n- **Pack registry** — `PackRegistry.publish/browse/versions/get/yank/verify_digest`,\n SHA256-digested, semver-ordered, validation fail-closed before anything is written.\n- **Managed identity** — `ManagedAgentIdentityProvider` with a real gateway minter and a\n PROVISIONED→REGISTERED→ACTIVE→ROTATED→REVOKED lifecycle; default-deny `authorize()`.\n- **Zero-code connect templates** — `render_connect(framework, …)` emits config pinned to\n each framework's real schema.\n\n### Added — NOOA-style agent capabilities\n\n- **`CodeActLoop`** — code as action: the model writes Python cells that run against a\n persistent namespace over live objects, each cell AST-validated before execution, with\n per-cell timeouts and bounded observations.\n- **`ObjectRegistry` / `render_observation`** — pass by reference: large values become\n handles instead of flooding the context with serialized text.\n- **`EventLog` / `ContextBlocks`** — harness APIs the model itself can call to query its\n own event history and manage named context blocks in KV-cache-stable order.\n- **`has_ellipsis_body` / `@strategy`** — NOOA's `async def f(...) -> X: ...` ergonomic.\n- **`CodeValidator`** — best-effort AST validation (ported from NVIDIA's OO-Agents,\n Apache-2.0; NOTICE included). A defense-in-depth layer, not a sandbox boundary.\n- **`PredictLoop`** — single-shot strategy with structured output (`output_model=`) and\n validation retry; `Strategy` is now a first-class alias of `AgentLoop`, and both loops\n accept a per-call `model=` override.\n- **Typed I/O** — `AgentResult` validates its fields at construction and at the\n `Agent.run` boundary.\n\nAll new capabilities are exported lazily from the top-level `adk` namespace.\n\n## [2.27.0] - 2026-07-18\n\n### Added — Moonshot Kimi K3 provider (DeepSeek-equivalent cloud backend)\n\n- **New `moonshot` provider** across the LLM router: OpenAI-compatible\n `https://api.moonshot.ai/v1`, default model `kimi-k3` (131K context default, up to 1M;\n tool calls, JSON mode, structured output). Wired everywhere `deepseek` is:\n - `LLMRouter(provider=\"moonshot\")` + `_COMPAT_URLS`/`_COMPAT_MODELS`/model tiers\n (small/medium/large → `kimi-k3`)\n - Config: `moonshot_api_key` field (env `MOONSHOT_API_KEY`), saved-config load,\n provider-keys map (`provider_keys.json` key `moonshot`)\n - Auto-detect chain: tried after DeepSeek (config key, then env key)\n - Hybrid reasoning backend: `AITHER_REASONING_BACKEND=moonshot` for effort 7+\n - Reasoning tiers: `backend: moonshot` (alias `kimi`) via new `MoonshotBackend`\n - CLI: `adk backend set moonshot --api-key …`, `adk backend list` shows moonshot\n- No breaking changes; DeepSeek and all other providers unchanged.\n\n## [2.26.0] - 2026-07-16\n\n### Added — Aeon group-chat web UI pack + POST /aeon/stream SSE endpoint\n\n- **New `aeon` UI pack** (`adk/webui/packs/aeon/index.html`) — group chat for multi-agent\n discussion. Mirrors the `llamacpp` pack's auth + SSE pattern (bearer from URL fragment #k=),\n with a preset selector (balanced/creative/technical/security/minimal/duo_code/research) and\n distinct colored agent messages. Synthesis block highlighted. Agent content is HTML-escaped\n before render. Fully self-contained HTML+CSS+JS, light+dark, no external assets. (No\n temperature/max-tokens controls — Aeon runs a fixed multi-agent round and doesn't thread\n per-turn generation params, so exposing them would be a no-op.)\n- **POST /aeon/stream SSE endpoint** — accepts body {message, preset?, agents?, rounds?,\n session_id?}, emits SSE with: `session_start` (participants + orchestrator), `agent_message`\n per non-orchestrator, `synthesis` (if enabled), `error` on exception, `complete` (total tokens\n + latency). Mirrors /chat/stream structure — like every streaming endpoint here it is gated by\n the server auth middleware **when `AITHER_SERVER_API_KEY` is set** (a local `adk up` with no key\n is unauthenticated by design, same as /chat/stream).\n- **Default UI pack flipped to `llamacpp`** — `resolve_ui_pack_name()` now returns `llamacpp`\n instead of `console` when no env/config is set (new users see the clean chat, not the admin\n SPA; existing users with AITHER_AGENT_UI or saved config unaffected).\n\n### Added — swappable agent web UI packs (llama.cpp-style built-in chat)\n\n- The page an agent serves at `/` is now a **swappable UI pack**, so you can\n drop in, test, and deploy different chat frontends without touching the agent.\n Select with `adk ui set <name>` (persists to `~/.aither/config.json`) or the\n `$AITHER_AGENT_UI` env; `adk ui ls` lists packs, `adk ui path` shows what\n resolves.\n- **New built-in `llamacpp` pack** — a clean, self-contained llama.cpp-style\n chat UI (centered conversation, streaming token-by-token with a cursor, a\n Stop button, a settings drawer for temperature / max-tokens that is actually\n honored end-to-end — `/chat/stream` now forwards them to the provider —\n minimal safe markdown, light+dark). Hits the agent's own gated `/chat/stream`\n with the bearer read from the URL fragment (never sent in the URL). Built-ins\n also include `console` (the full admin SPA, still the default) and `minimal`.\n- **Drop-in custom packs**: a UI pack is just a folder with an `index.html` in\n `~/.aither/ui-packs/<name>/` (override via `$AITHER_UI_PACKS_DIR`) — no rebuild.\n- Resolution is **fail-soft**: an unknown/missing pack falls back\n console → minimal, so `/` is never blank.\n\n### Added — MCP-gateway-first platform access + mesh A2A trust\n\n- **Gateway-first platform access** (`adk/client/_gateway_mcp.py`, `GatewayMCPClient`):\n a self-hosted agent connects its MCP **client** to `mcp.aitherium.com` authenticated\n as the OWNER (device-flow / PAT / ACTA token), RBAC-scoped to that identity — so it can\n use platform tools + `get_secret` **whether or not the node runs local AitherOS\n services**. Fail-closed: no token → client disabled, the agent still runs. Wired into\n the server lifespan (`_connect_gateway_mcp`).\n- **`genesis` LLM backend**: `adk backend set genesis` points the agent brain at a local\n Genesis (`http://localhost:8001/v1`, model `workflow`) — the 5090/controller pattern\n where inference is served by the local fleet, not the cloud gateway or a raw vLLM.\n- **`adk mesh` command**: `adk mesh onboard` drives the Conductor 5-step onboard\n (`/v1/mesh/onboard` → registers this node's public key, returns the `endpoint:mesh`\n trust token); `adk mesh ls` lists peer nodes/agents (AitherMesh `/nodes` + Directory)\n with their A2A cards + invoke URLs for discovery.\n\n### Security — A2A inter-agent trust enforcement (fail-closed)\n\n- **`adk/a2a_trust.py`** (new): Ed25519 signature verification + trusted-key check\n (`AITHER_A2A_TRUSTED_KEYS`), opt-in via `AITHER_A2A_REQUIRE_TRUST` (default `false` for\n backward compatibility; `audit` logs untrusted, `true` rejects).\n- **Fixed a fail-OPEN in the inbound A2A handler** (`adk/a2a.py`): the trust check\n previously ran *only when signature headers were present*, so an unsigned request\n bypassed `AITHER_A2A_REQUIRE_TRUST` entirely. Now genuinely fail-closed — required mode\n with no/invalid signature → `403`.\n- **Sovereign template no longer scaffolds `verify=False`**: the generated\n `services/llm.py` now uses the project's own `tls_verify()` policy helper for every\n provider call instead of hardcoding disabled TLS verification.\n- **`adk/_tls.py`**: disabling verification via `AITHER_TLS_VERIFY=false` now emits a\n one-time loud warning (was silent) — the escape hatch stays, the silence goes.\n\n## [2.25.1] - 2026-07-15\n\n### Fixed — self-hosted OpenAI-compatible backends honor a custom base URL\n\n- `adk backend set llamacpp --base-url http://localhost:8090/v1 --model <id>` (and\n `vllm`/`lmstudio`, and the `AITHER_LLM_BASE_URL` env) now actually point the agent\n at YOUR server. Previously the base URL was saved but never read, so an explicit\n `--backend llamacpp` fell back to the provider's public API default (openai.com).\n New `Config.llm_base_url` field + saved-config `inference_url` wiring; the explicit\n backend path in `LLMRouter` now passes it for the local OpenAI-compatible family.\n This is what lets a self-hosted node run its OWN local model (e.g. a PrismML\n Bonsai llama.cpp) as the agent brain instead of ollama/cloud.\n\n## [2.25.0] - 2026-07-15\n\n### Added — node_bootstrap tool pack: bootstrap LLM inference on any hardware\n\n- **New bundled tool pack `node-bootstrap`** (`adk/toolpacks/node_bootstrap/`):\n seven agent tools — `node_detect_hardware`, `node_resolve_recipe`,\n `node_plan_deployment`, `node_apply`, `node_enroll`, `node_register_backend`,\n `node_verify` — plus a CLI shim (`python -m adk.toolpacks.node_bootstrap\n detect|resolve|plan|apply|enroll|register|verify`). Detect a box's hardware,\n resolve a deployment recipe, actually deploy the inference engine (docker\n compose / native / delegate), enroll with a control plane, register the\n backend, and live-verify a completion.\n- **9 hardware recipes** shipped in the wheel: `cpu-1bit-llamacpp`, `cpu-ollama`,\n `cuda-vllm-8gb/24gb/40gb`, `cuda-dual-stack-32gb` (32GB-card co-resident\n stack), `unified-memory-vllm` (DGX/Grace-class), `metal-ollama` (native —\n Docker on macOS has no Metal passthrough), `cloud-api` fallback. Resolution\n scores hardware against min/max VRAM bands, RAM, cores and unified memory;\n ties prefer higher tiers and self-contained recipes over fleet delegates.\n- Security posture: enrollment and backend registration are fail-closed\n (missing token/URL → error dict, never anonymous, never `verify=False`);\n tokens are redacted in outputs; all endpoints come from args/env\n (`AITHER_CONTROL_PLANE_URL`, `AITHER_GENESIS_URL`, `AITHER_AUTH_TOKEN`).\n\n## [2.24.0] - 2026-07-14\n\n### Changed — sync-family consolidation\n\n- **Removed dead code:** `adk/sync.py` (528 lines) deleted — it was unreachable,\n shadowed by the `adk/sync/` package (`import adk.sync` always resolved to the\n package; nothing loaded the module by path — verified).\n- **Grouped the sync domain modules into the `adk.sync` package:**\n `brain_sync`→`adk.sync.brain`, `files_sync`→`adk.sync.files`,\n `lockbox_sync`→`adk.sync.lockbox`, `secrets_sync`→`adk.sync.secrets`,\n `session_sync`→`adk.sync.sessions`, `settings_sync`→`adk.sync.settings`,\n `sync_watermark`→`adk.sync.watermark`. All in-tree importers updated. No\n back-compat shims were left because **zero external/monorepo code imported the\n old root paths** (verified) — a clean move, not a shimmed one. **Breaking only\n for code that imported `adk.<name>_sync` directly** (none known).\n\n### Docs — MCP module layering clarified\n\n- `adk.mcp` (enterprise AitherOS gateway client: auth/billing/token-tracking) and\n `adk.core.mcp` (minimal generic JSON-RPC adapter for any MCP server) are\n DISTINCT layers, not duplicates — each now carries a LAYER/ROLE/See-also\n docstring so they are not mistakenly consolidated.\n\n## [2.23.2] - 2026-07-14\n\n### Fixed — CompletionGate no longer false-fails on correct results\n\n- **Tolerant judge JSON extraction.** The LLM judge parsed its reply with `json.loads(text[first\"{\":last\"}\"])`, which raised `JSONDecodeError` (\"Extra data\") whenever the model appended prose after the verdict or emitted a second object — and `verify()` turned that into a fail-closed verdict, so a genuinely completed task reported `unverified`. New `_first_json_object()` uses `JSONDecoder.raw_decode` from the first `{` that begins a valid object and ignores trailing content (prose, markdown fences, extra objects). A judge that answers with no parseable JSON still fails closed (never a soft pass), now with a clear reason. The same extractor backs `_derive_criteria`.\n- **Bare code-like tokens are hard-checked.** A natural criterion like `output contains ADK_AUTO_OK` (no quotes) previously fell through to the judge; `hard_checks` now also matches bare identifier/code shapes (contains an underscore, or 4+ CAPS/digit chars), with a stopword guard so a descriptive uppercase word (`OUTPUT`, `CONTAIN`) never becomes a false required token. Quote a token to force it regardless.\n- +9 tests (24 total), including the exact trailing-data regression.\n\n## [2.23.0] - 2026-07-14\n\n### Removed — dead modules and internal integrations\n\n- **`adk.swarm`, `adk.provisioning_tools`, `adk.session_sync_integration`, `adk.addon_metering`** — four orphan modules with zero public usage have been deleted. These were internal prototypes kept for legacy monorepo consumers; public packages have not imported them since 2.21.0 (tracked as D-348). **Breaking for anyone directly importing these modules** (import will fail). They are no longer exempt from the orphan-module check.\n- **`adk.aither_bridge`** — the AitherOS-internal IRC ↔ chat gateway bridge is no longer shipped in the public aither-adk package (stripped at sync time). All public importers already degrade behind `except ImportError` (in `adk.server` and `adk.builtin_tools`), so public packages remain unaffected. The module stays in the private AitherOS monorepo for internal use.\n- **Identity document trim** — removed internal agent names from `adk/identities/aither.yaml` delegation guidance, replacing with role-generic wording (\"delegate code review, refactoring, security analysis, performance and testing to specialist agents when available\"). Effort-tier routing philosophy remains unchanged and is product design, not a leak.\n\n### Changed — genericized customer/product names in help text\n\n- CLI help/examples no longer name specific customer deployments (`--scope` template list and `deploy agent` name example use generic placeholders). Functional deploy paths are unchanged.\n\n## [2.22.1] - 2026-07-14\n\n### Fixed — real bugs surfaced by reviving the public CI (red since Jul 5)\n\n- **TLS**: bundled toolpacks (arc-brainpack, cloudflare) disabled certificate\n verification on gateway/vault calls; all now route `adk._tls.tls_verify()`\n (verified by default, internal CA bundle when present).\n- **`system_prompt` contract**: bundled-pack `[PACK DIRECTIVES]` were appended\n even to an explicit `system_prompt`; an explicit prompt now wins wholesale.\n- **Headless secrets**: `_derive_key()` used `os.getlogin()`, which raises\n `OSError` without a controlling terminal (CI, daemons, containers) — falls\n back to `getpass.getuser()` (same value on interactive boxes, so existing\n secret files keep decrypting).\n- **`pack_verifier`**: missing `cryptography` crashed with `UnboundLocalError`\n in the exception handler instead of failing closed; now degrades gracefully\n (and `cryptography` was added to the dev extra so CI tests the real path).\n- **`fleet_manager._kill_pid` (POSIX)**: reported success immediately after an\n asynchronous SIGTERM and never reaped children (zombies read as alive); now\n polls, escalates to SIGKILL, and reaps.\n- **Identity**: `aither` regained the `creative` tool category (dropped\n accidentally in a 2.7.0 WIP sweep).\n\n## [2.22.0] - 2026-07-14\n\n### Changed — internal toolkit relocation\n\n- **`adk.platform` relocated** — the internal aither-platform toolkit (ComfyUI/image-gen\n pipelines, fleet agent clients, parallel CLI/UI/tools stack) has been moved out of the\n adk SDK tree to the standalone internal `aither-platform` package at\n `AitherOS/agents/aither_platform/`. The public package remains unaffected (it already\n excluded `adk.platform` since 2.21.1). Internal AitherOS agent builds now install\n `aither-platform` separately. `adk platform` falls through to the same \"not available\"\n message on public installs; internal builds work via the relocated package.\n\n### Removed — backward-compat re-export deleted\n\n- **`adk.gateway` module removed** — the backward-compatibility re-export of `GatewayClient`\n from `adk.gateway` has been removed. Use `from adk.client import GatewayClient` instead\n (the established public API since 2.21.0). All internal importers have been updated.\n\n## [2.21.1] - 2026-07-14\n\n### Removed — internal toolkit and license-gated product no longer ship publicly\n\n- **`adk.platform`** — the internal aither-platform toolkit (ComfyUI/image-gen\n pipelines, fleet agent clients, a parallel cli/ui/tools stack) is no longer\n included in the public repo or the PyPI wheel. It was internal-only code that\n had been riding along since the aither-platform merge. `adk platform` now\n reports \"Platform toolkit not available\" on public installs; internal AitherOS\n builds are unaffected.\n- **`adk.formbridge`** — the FormBridge form-automation product is license-gated\n (PROFESSIONAL+); its implementation no longer ships in the free package. All\n import sites (`builtin_tools`, `server` route mount) already degrade behind\n `except ImportError`, so nothing else changes behavior.\n- Product test suites (`test_formbridge.py`, `test_encounter_keying.py`,\n `test_flow_tools.py`) are excluded from the public sync alongside them.\n\n## [2.21.0] - 2026-07-14\n\n### Added — Completion gate: verified-or-retried task execution\n\n- **`adk.gate.CompletionGate`** — a composable wrapper that closes the \"silent\n success\" gap: an agent's ReAct loop returns whatever the model produced and\n calls it done (`finish_reason = stop/max_steps/length`), which is a *liveness*\n signal, not a *completion* one — so an agent can \"succeed\" while doing nothing.\n `CompletionGate` runs the agent, verifies the result against acceptance\n criteria, retries with the failure fed back, and returns the response with\n `finish_reason` set to `verified` or an honest `unverified`. Never a false pass.\n - **Hard-first, un-soft-passable checks** (`adk.gate.hard_checks`): a filesystem\n path a criterion says must exist is checked on disk; a required quoted token\n must appear in the output. Only genuinely subjective criteria fall through to\n an optional LLM judge (reuses the agent's own model), which **fails closed**\n if unavailable.\n - **`gated_run(agent, task, criteria=..., max_retries=2)`** — one-shot helper.\n - `auto_criteria` (LLM writes the machine-checkable definition-of-done from the\n task), a `verifier=` override for custom checks (tests-pass / HTTP probe), and\n a `requires_action` short-circuit so it never fights human-approval pauses.\n - Compose it onto any agent exposing `run(task) -> resp{.content, .finish_reason}`\n (`AitherAgent`, `core.Agent`, or a thin adapter over a remote dispatch).\n - Exported from the top-level package: `from adk import CompletionGate, gated_run`.\n\n## [2.19.0] - 2026-07-12\n\n### Added — Eve agent importer + durable A2A tasks + canonical agent discovery\n\n- **`adk pack import <eve_agent_path>`** — convert external Eve agents to AitherADK packs\n (from GitHub or local filesystem). Maps Eve structure (instructions → system prompt,\n agent.ts → agent.yaml, skills/*.md → skills, connections/*.ts → MCP, tools/*.ts →\n tools/node verbatim) to AitherADK format with a single command. Eve agents (compiled\n manifest format, schema v35) become installable packs compatible with `adk pack install`.\n- **Durable A2A task lifecycle (`TaskManager` backed by FileStore)** — agent-to-agent\n tasks now survive container restarts. Tasks persist as append-only JSONL\n (`.adk/tasks.jsonl`); in-memory dict for fast access; `create_task()` / `update_status()` /\n `add_artifact()` / `add_message()` all save to disk. Google A2A v0.3.0 spec compliant.\n- **Canonical agent card discovery** — `A2AServer.mount()` now exposes:\n - `GET /.well-known/agent-card.json` — Agent Card (canonical; contains agent identity,\n capabilities, skills, endpoints)\n - `GET /.well-known/agent.json` — Agent Card (legacy, redirects with 308 to canonical path)\n - `POST /a2a` — JSON-RPC 2.0 for message send/task lifecycle\n - `GET /a2a/tasks/{id}/subscribe` — SSE streaming task updates\n- **Four built-in agent packs now complete and catalogued**:\n - `hermes` — architecture review & trade-off analysis\n - `openclaw` — web research & source verification\n - `claude-code` — feature development & debugging\n - `analyst` — structured data inference & anomaly detection\n All discoverable via `adk packs` / `adk install pack:<name>`.\n- **Tolerant tool-call JSON parsing** — malformed tool JSON (extra properties, missing\n name/arguments, string-encoded nested args, missing keys) is now gracefully degraded\n instead of silently failing. `_parse_tool_json()` accepts both `\"arguments\"` and\n `\"parameters\"` keys, coerces string-encoded args, and skips unparseable calls instead\n of crashing the turn.\n\n### Known limitations\n\n- Eve agent importer: remote GitHub fetching not yet implemented (local paths only).\n- Durable task store: currently JSONL (suitable for thousands of records, not millions).\n\n## [2.18.0] - 2026-07-10\n\n### Added — AitherShell command center + Claude Code session management\n\n- **`aither sessions`** — Claude Code session manager: interactive full-screen\n browser (type-to-filter, transcript preview, deep full-text `search`, resume\n **in the current terminal** or as Windows Terminal tabs), crash guard\n (`guard install` = at-logon watchdog that snapshots live sessions and\n auto-restores the whole set after a terminal crash), and `ingest [--watch]`\n (incremental, secret-guarded sync of session conversations into the local KB\n / CompanyBrain via the standard ingest pipeline).\n- **`aither hq`** — command-center dashboard: per-service fleet health, LLM\n queue (depth/VRAM/models), Pulse alerts, live session count, inbox unread —\n auto-refreshing, with one-key jumps into chat, sessions, inbox, agents,\n brief, watchtower, and docker recovery.\n- **`aither inbox`** — unified queue over CommCore mail, Relay\n mentions/notifications, and Pulse alerts; open/mark-read/DM-reply from the\n terminal. Every source degrades independently.\n- **`aither agents`** — roster console + `agents ask -e <1-10> <agent> <q>`\n (effort-tiered ask via Genesis, with automatic `/chat/stream` fallback when\n the `/agent/sync` execute gate rejects host callers) + forge dispatch\n inspection + routine health.\n- **`aither palette`** — universal fuzzy picker across actions, sessions, and\n services; **`aither brief`** — renders Atlas's executive briefing in the\n terminal; **`aither watch`** — fleet watchtower with named wedge-signature\n detection (docker-WSL wedge, LLM-queue stall, crash-loop uptime regression)\n and optional docker auto-recovery.\n- All fleet reads go through a shared fail-soft `FleetClient` (internal-CA\n TLS via `adk._tls`, dashboard-grade timeouts, per-source degraded states).\n- Node shell-cli (`@aitherium/shell-cli` 1.13.0): `aither\n sessions|hq|inbox|palette|brief|watch|agents|docker` now pass through to the\n Python shell instead of being intent-classified as chat prompts.\n\n## [2.17.1] - 2026-07-05\n\n### Fully self-service mesh join — no customer key handling\n\n- **Auto-join headscale off the onboard response.** The conductor now auto-issues a\n headscale pre-auth key for NAT'd nodes and returns it in the `/v1/mesh/onboard`\n response; `mesh.join` consumes it and brings up the tunnel automatically. A customer\n runs one command — they never mint, fetch, or set a headscale key. Explicit\n `--headscale-key`/`AITHER_HEADSCALE_AUTH_KEY` still take precedence.\n- **Security:** `_tailscale_up` now redacts the auth key from all logs and exceptions\n (a failed `tailscale up` previously echoed the full argv, leaking the key into logs).\n\n## [2.17.0] - 2026-07-05\n\n### Added — Headscale mesh transport + local Qdrant + AitherConnect onboarding\n\n- **Headscale NAT-friendly mesh transport.** When raw WireGuard UDP:51820 is not\n viable (customer boxes behind NAT, corporate firewalls, CGNAT), `adk mesh join\n --headscale` routes the mesh tunnel through Tailscale's Headscale control plane.\n The overlay IP is still Conductor-assigned (10.77.0.0/16); Headscale provides\n only the transport layer. Configured via `AITHER_MESH_TRANSPORT=headscale`,\n `AITHER_HEADSCALE_URL` (default: https://headscale.aitherium.com), and\n `AITHER_HEADSCALE_AUTH_KEY` (pre-generated key). Automatic fallback to raw\n WireGuard if Headscale setup fails.\n- **Local Qdrant vector DB provisioning.** `adk stack` now provisions Qdrant\n locally when `AITHER_VECTOR_DB=qdrant` is set, enabling offline-first RAG\n without external dependencies. Tiered: local Qdrant in-fleet, cloud Nexus\n for enterprise multi-tenant deployments.\n- **Conductor URL public fallback.** `adk mesh join` resolves the internal\n `aitheros-conductor:8193` hostname; if unreachable (e.g., self-hosted nodes),\n it falls back to the public `conductor.aitherium.com:8193` endpoint,\n eliminating bootstrap configuration friction.\n\n### Documentation\n\n- **Self-hosting runbook.** New `docs/SELF_HOSTING.md` covers deploying AitherOS\n on customer infrastructure with Qdrant + mesh + AitherConnect onboarding.\n- **Mesh transport constraints documented.** Raw WireGuard requires public\n UDP:51820 endpoint (cloud instances, dedicated hardware); Headscale for\n NAT'd networks. Noted in CLI help and module docstrings.\n\n## [2.15.0] - 2026-07-04\n\n### Added — built-in web admin console + portal-profile settings sync\n\n- **Web admin console.** The page `adk up` opens (served at `/` and `/ui`) is now a full,\n self-contained console — Chat plus six admin tabs: **Backend** (view/switch LLM provider,\n set keys, test connection), **Packs** (list/enable/disable/reload tool packs),\n **Sessions** (browse/delete conversations), **Logs** (redacted agent-log tail),\n **Graph** (knowledge-graph search/neighborhood/stats), and **MCP** (add/remove external\n MCP servers). No build step, no CDN — a single packaged HTML asset. The original minimal\n streaming chat page stays at `/chat` for back-compat.\n- **Admin API (`adk/admin_api.py`).** ~20 endpoints under `/admin/*`, all bearer-gated by the\n existing middleware (never in `_skip_auth_paths`). Operates on the live agent — LLM backend\n swap (`LLMRouter.switch_backend`), pack reload, and MCP registration take effect without a\n restart.\n- **Settings sync (`adk/settings_sync.py`).** The user's portal profile is the source of\n truth: on startup the agent pulls `preferences.adk` and applies it over the local\n `~/.aither/config.yaml` cache; admin-console edits push a debounced snapshot back to\n `PUT /api/settings/preferences`. Provider API keys and MCP auth headers are device-local\n secrets and are never included in the pushed snapshot. Opt out with `AITHER_SETTINGS_SYNC=false`.\n\n### Security\n\n- **Timing-safe token comparison.** The server bearer check now uses `hmac.compare_digest`\n (the token is reachable over the public tunnel; a byte-wise `!=` leaked it to timing attacks).\n- **MCP add is SSRF-guarded + confirmed.** Adding an external MCP server is a two-step\n prepare→confirm flow; URLs resolving to loopback / private / link-local / metadata addresses\n are rejected (override with `AITHER_ALLOW_PRIVATE_MCP=1` for trusted local servers).\n- **Config editor is allowlisted.** `PATCH /admin/config` writes only safe scalar prefs and\n hard-denies fields that could execute code or redirect traffic at startup (e.g.\n `aither_toolpack_dirs`, backend base URLs).\n- **Log tail is scrubbed.** Bearer tokens, `#k=` fragments, and `sk-`/`aither_sk_` keys are\n redacted from the log-tail endpoint; the tunnel log is excluded.\n\n## [2.14.8] - 2026-07-04\n\n### Added — `adk up` for normal humans (no API key, no prereqs, live chat)\n\n- **Hosted-brain default — no API key required.** When there's no local model and no\n provider key, `adk up` signs in (device-flow) and routes inference through the gateway\n using the portal token (`AITHER_LLM_BACKEND=gateway` + bearer). A non-technical user\n never needs to obtain or paste an LLM API key. Explicit `--provider`/local model still\n take precedence; unattended `--yes` with no token/backend fails cleanly (exit 3).\n- **cloudflared auto-download.** If the tunnel binary is missing, `adk up` downloads the\n right platform asset to `~/.aither/bin` (SHA256-verified against the release; opt out with\n `AITHER_CLOUDFLARED_REQUIRE_CHECKSUM=0`) instead of exiting with an install hint.\n- **Built-in streaming chat page.** The agent now serves a self-contained chat UI at `/`\n (and `/chat`) with a live \"thinking…\" indicator and token-by-token streaming — so a human\n has somewhere to talk to it with immediate feedback instead of a blank wait. `adk up` opens\n it locally and passes the callback bearer in the URL fragment (`#k=…`), so `/chat/stream`\n stays authenticated (not an open proxy) with nothing to paste.\n- **Human-readable failures.** `adk up` errors now carry a plain-English `next_action`\n (kept machine-readable in `--yes` JSON).\n\n### Fixed\n\n- `adk up` chat link pointed at a non-existent `/settings/agent` route; it now points at the\n agent's own streaming chat page.\n\n## [2.14.7] - 2026-07-04\n\n### Added\n\n- **`adk up` — one command to run a persistent, fleet-connected agent.** Collapses\n the scattered onboarding surface (`host`/`quickstart`/`connect`/`login`/`setup`)\n into a single command that starts `aither-serve` (single agent, identity `aither`\n by default), opens a Cloudflare quick-tunnel for fleet reachability, registers with\n the portal, and installs a reboot-persistent autostart entry (Windows Task Scheduler\n / systemd-user / launchd). Detaches by default so the terminal is freed; `--foreground`\n blocks. Reuses the existing device-flow login, cloudflared discovery, provider-key\n store, and `_preflight_check` backend detection.\n- **Fully non-interactive path for autonomous agents.** `adk up --yes` (or any\n non-TTY invocation) takes ZERO prompts — everything resolves from flags, env, and\n saved config — and emits a single machine-readable JSON status line with meaningful\n exit codes (`0` ok, `2` bad-input, `3` no-backend, `4` tunnel-missing, `5` register-failed).\n Degrades to local-only (warn, not fail) when a portal token is absent unless\n `--require-register` is set.\n- **`adk down` / `adk status --json` companions.** `down` stops the agent + tunnel,\n best-effort deregisters, and removes the autostart entry; `status` reports the running\n agent (liveness + `/health` + tunnel + registration), with `--json` for agents/CI.\n- **`adk stack`** — the previous `adk up` behaviour (supervise the Room + Ollama consumer\n stack) now lives here; `adk up` is the connected-agent command.\n\n### Internal\n\n- New `adk/agent_daemon.py`: detached process spawn, status file\n (`~/.aither/adk-up.json`), pid liveness/teardown, and cross-platform autostart install.\n\n## [2.14.6] - 2026-07-04\n\n### Added\n\n- **`adk.preflight` — self-bootstrap capability probe.** `run_preflight(agent,\n spec)` wakes an agent up knowing what it is actually plugged into: a bounded\n (1.5s) parallel `LivenessProbe` over the router's resolved backends\n (primary/reasoning), the embeddings winner, remote MCP tools, the structured-ML\n / `/ml/teach` endpoints, and voice — returning an honest `CapabilityReport`.\n Status vocabulary distinguishes reachability from entitlement\n (`OK`/`MISSING`/`UNREACHABLE`/`AUTH`/`TIER_DENIED`/`UNSUPPORTED`), a hosted slot\n never prints a bare `OK`, and a required-but-unsatisfied slot aborts before the\n loop instead of blind-firing. Headless task/rules resolution + an allowed-roots\n policy binding (`builtin_tools.set_allowed_roots`, which resets the memoized\n cache so the override actually enforces).\n- **Multimodal (image) messages.** `Message.content` now accepts an OpenAI-style\n content-part list (`str | list[dict]`), passed through verbatim to the\n OpenAI-compatible body (gateway / vLLM / OpenAI). New `adk.llm.multimodal`\n helpers — `image_message()`, `image_content_parts()`, `to_image_url()` — build\n the parts and encode local image bytes/paths into `data:` URIs.\n- **Real vision preflight probe.** The vision slot is no longer unconditionally\n `UNSUPPORTED`: it sends a discriminable image (a solid red square) and requires\n the model to name the color, so a blind/text-only model cannot false-pass. `OK`\n only on a real round-trip; `AUTH` on 401/403; `UNSUPPORTED` when there is no\n OpenAI-compatible provider or the model cannot see the image.\n\n## [2.14.5] - 2026-07-04\n\n### Added\n\n- **`adk.reasoning.mcts` — generic Monte-Carlo Tree Search library.** A reusable\n MCTS engine (ported from the internal `UnifiedMCTS`, with all internal couplings\n removed) that any agent can drive over its own environment:\n - `MCTSEnvironment` protocol (`get_state_hash` / `get_actions` / `step` /\n `evaluate` / `clone`) — implement it and search.\n - Three **optional, default-off** model seams — `TransitionModel`, `PolicyModel`\n (PUCT priors), `ValueModel` — so a learned world-model / policy / value can be\n injected without touching the core; with all three `None` the engine is a plain\n UCT search.\n - `ObservedTransitionModel` adapter — a cheap online lookup world-model (hit =\n exact stored transition; miss = identity + negative \"unknown\" reward +\n `is_uncertain`, biasing exploration toward learning), with JSONL save/load for\n cross-run reuse.\n - `UnifiedMCTS(config).search(env)` returns an `MCTSResult` (best action, value,\n confidence, principal-variation path) and an optional `MCTSTrace`\n (visit-distribution policy target) for distillation.\n- **Self-bootstrapping agent machinery.** Build an agent from a declarative spec\n instead of bespoke wiring:\n - `adk.bootstrap.build_agent_from_spec(path) -> (AitherAgent, RunCtx)` — resolves\n prompts, activates required/optional tool packs, and carries a `memory_map`\n (a `node_type -> (role, tier)` override for memory writes, never a gate).\n - `adk.prompts.PromptBridge` — resolves an agent's prompt bindings from files\n (relative to the spec) or `AitherPrompts` dot-keys (optional import).\n - `adk.packs.PackActivator` — eager activation of installed tool packs by name\n over `register_tool_packs`; a required pack that registers zero tools raises\n `PackUnavailable`, optional packs soft-degrade.\n - `python -m adk.bootstrap.generic_agent <spec.yaml>` inspects a spec (resolved\n name, activated packs + tool counts, prompt keys, memory map) without running a\n live loop.\n- **`deep_research` tool pack** — a full multi-engine web-research workflow as\n `dr_*` agent tools (keyless search, page fetch + cache, site mirror, a citable\n findings knowledge graph, and markdown/PDF/DOCX report generation), activatable\n by name via the tool-pack loader.\n\n## [2.14.4] - 2026-07-04\n\n### Added\n\n- **`structured_ml` tool domain** — zero-shot structured-data inference for agents,\n backed by TabFM (tabular classification/regression) and TimesFM (time-series\n forecasting). Three tools:\n - `tabular_classify(support_rows, target, query_rows)` — classify rows from a\n labeled support set, in-context (no training step; up to 10 classes).\n - `tabular_regress(support_rows, target, query_rows)` — predict a numeric target.\n - `timeseries_forecast(series, horizon)` — forecast future values of a series.\n\n The tools POST to a structured-ML inference service resolved from\n `AITHER_STRUCTURED_ML_URL` (default `http://localhost:8192`), reject non-http(s)\n URLs, and summarise oversized responses to protect the agent's context.\n- **`Capability.STRUCTURED_INFERENCE`** capability token and a new **`analyst`**\n identity/pack that ships the `structured_ml` domain by default. The domain is\n opt-in — only identities that enable the `structured_ml` category get the tools.\n\n## [2.14.3] - 2026-07-03\n\n### Fixed\n\nAll four bugs below were found by actually booting `adk run` against a real\nchat model and driving a real tool-calling request end-to-end (not inferred\nfrom reading the code) — each one silently broke the \"local agent uses its\nconfigured model and its tools actually work\" path.\n\n- **`AITHER_MODEL` / `config.model` silently ignored by the OpenAI-compatible\n backend**: `LLMRouter.__init__` never read `config.model` into `self._model`\n unless the caller passed `model=` directly to the constructor — which nothing\n did. Every explicit model choice (env var, config file) was dropped, the\n provider fell back to a hardcoded per-backend default (`gpt-4o-mini` for the\n `openai` backend), and an unrecognized model name got silently remapped\n server-side to the wrong backing model. Concretely: setting\n `AITHER_MODEL=aither-orchestrator` had no effect — every request actually\n went to a different model that was never meant to be called directly and\n can't reliably use tools.\n- **`OPENAI_BASE_URL` ignored for explicit `--backend`/`AITHER_LLM_BACKEND`\n selection**: `LLMRouter._auto_detect()`'s explicit-backend branch hardcoded\n `base_url=None` when constructing the provider, instead of reading\n `config.openai_base_url` (which two other call sites in the same file\n already did correctly). Any self-hosted OpenAI-compatible endpoint\n (MicroScheduler, vLLM, LM Studio) configured via `OPENAI_BASE_URL` was\n silently ignored in favor of `https://api.openai.com/v1`.\n- **Tool-call steering never engaged on default-effort chat**: the retry that\n nudges a model back into structured tool-calling when it responds with prose\n instead of a function call was gated behind `effort >= 6`. Default `effort`\n for a plain `agent.chat(message)` call resolves to `5`, so the safety net —\n built specifically for this failure mode — never fired for default requests.\n Also now forces `tool_choice=\"required\"` on the retry instead of relying on a\n text nudge alone.\n- **Every non-string builtin-tool parameter silently typed as `\"string\"` in\n the generated tool schema**: `builtin_tools.py` uses\n `from __future__ import annotations`, so `_extract_parameters()` in\n `tools.py` was reading postponed (string) type hints (`\"int\"`, not `int`)\n straight out of `fn.__annotations__`, which never matched\n `_type_to_schema`'s type-object keys and silently fell through to\n `{\"type\": \"string\"}`. Every builtin tool with an `int`/`float`/`bool`/`list`\n parameter was affected — e.g. `file_read(start_line: int, end_line: int)`\n advertised both as strings, so a model correctly calling the tool with\n `\"start_line\": \"0\"` crashed the actual implementation with\n `unsupported operand type(s) for -: 'str' and 'int'`. Fixed by resolving\n hints through `typing.get_type_hints()` instead of the raw annotations dict.\n `_coerce_arguments()` — the runtime safety net that's supposed to coerce a\n model-supplied `\"0\"` string into `0` before a tool call executes — had the\n exact same raw-`__annotations__` bug and silently no-opped for every builtin\n tool; fixed the same way.\n- **Streaming tool-loop could hang a connection forever**: `chat_stream()`'s\n fallback to the sync tool loop (tool calls can't stream token-by-token) had\n no timeout, so a model that didn't cleanly terminate a tool-calling round\n trip left the SSE connection open indefinitely with zero bytes sent. Bounded\n with a 60s `asyncio.wait_for`; on timeout the stream yields a clear message\n instead of hanging.\n\n## [2.14.2] - 2026-07-03\n\n### Fixed\n- **AitherShell session amnesia**: the REPL generated a fresh `session_id` for\n EVERY message, so conversation history never followed across turns (the\n server stored each turn under a different session). One stable id is now\n minted per shell run; `/resume` and `/new` semantics unchanged, and the\n cross-restart auto-restore of the last session now actually works.\n\n## [2.14.1] - 2026-07-02\n\n### Added\n- **Account-tied licensing via device-flow login** — `adk login` now persists the account\n license returned by the device-flow handshake to `~/.aither/license.json` (base64\n `license_key` → `{payload, signature}` envelope), so entitlements from the authenticated\n account carry into every later `adk` invocation without re-auth. Pairs with the fleet-side\n node tool-pack gate that grants packs by plan-tier rank.\n- **`can_use_formbridge` / `can_use_untether` entitlements** (PROFESSIONAL+ rank ≥ 3) backing\n the FormBridge and UNTETHER agent tool-packs.\n\n### Fixed\n- `adk` CLI: guard the optional `memory_graph` singleton (was a latent `NameError` on the\n `/stats`, `/memory`, and save paths when memory was not constructed).\n\n## [2.14.0] - 2026-07-02\n\n### Added\n- **`adk.memory_wiki` — LyraWiki-style self-managed semantic knowledge** (opt-in; nothing constructs it by default): `MemoryWiki(graph_memory)` maintains curated wiki ARTICLES as `wiki_article` nodes (title/slug, LLM-owned markdown with `[[wikilinks]]`, embedding, durable tier, `CONSOLIDATED_FROM` edges to source memories, governance-ledgered revisions).\n - `consolidate(llm, since=, budget=)` clusters unconsolidated episodic/fact nodes (embedding + keyword), drafts/UPDATEs articles via the INJECTED llm callable (`str -> str` or `messages -> str`, sync or async — no hard model dependency), cites source node ids, marks contradicted sources superseded, then DEMOTES consolidated sources to a fast-decay tier via `promote()` (represented knowledge doesn't need raw copies).\n - `lint(llm=None)` — deterministic health checks (orphan wikilinks, empty/stale articles, live-but-contradicted pairs) + an optional LLM audit pass.\n - `prune(relevance_floor=0.05, hard_delete_after_days=14)` — article relevance = tier freshness × reinforcement (reinforce-on-recall) + link-degree to LIVE memories; below the floor → reversible governance tombstone; entombed past the window → **HARD-DELETE** (the tombstone snapshot itself is purged — content and embedding irrecoverable; the true-deletion path). A node is never deleted without a tombstone first.\n - `recall(query, limit)` — article-first RAG (curated articles rank above raw nodes) that reinforces returned articles.\n- **`adk.routines` — agent heartbeat + self-programmed routines**: `RoutineStore` is a durable registry (`~/.aither/routines/{agent}.json`, `AITHER_DATA_DIR`-aware, injectable path) of cron-scheduled self-prompts `{name, cron, instruction, enabled, last_run, last_result, tags}` that rehydrates into the existing `adk.cron.CronScheduler` on `start()`. A fire runs `agent.chat(instruction)` with the agent's full toolset — the agent programs its future self with its own adk. Guardrails: `max_routines` (12), a 5-minute min fire interval per routine, a per-fire timeout, and every result ledgered (truncated) to `last_result`. Direct-callable routines (`register_direct`) fire a bound method instead of a self-prompt. Self-management tools (`routine_create/list/update/pause/resume/delete/run_now`) ship as OpenAI tool defs + handlers (`build_routine_tools` / `register_routine_tools` / `routine_tool_defs`); the handlers ONLY touch the RoutineStore — they can never modify agent config or safety-relevant settings (the leash principle).\n- **`AitherAgent(routines=True, memory_maintenance=True)`** (both default **False** → the default agent is byte-identical): `routines=True` builds the RoutineStore + scheduler, registers the self-management tools, and starts the heartbeat lazily on the first `chat()` (or via explicit `start_routines()`). `memory_maintenance=True` registers DEFAULT routines — `wiki_consolidate` (every 2 h), `wiki_lint` / `wiki_prune` / `graph_sweep` (daily) — as DIRECT method fires so memory upkeep runs even on tiny models, while staying visible/manageable via `routine_list`.\n- `TombstoneStore.purge(tombstone_id)` — the hard-delete endpoint: irrecoverably removes a tombstone snapshot and rewrites the persisted store without it (consumed by `memory_wiki.prune`).\n\n## [2.13.6] - 2026-07-02\n\n### Added\n- **Qdrant backend for GraphMemory dataplane sync — TRUE two-way, verified live.** Set `AITHER_FLEET_QDRANT_URL` and the sync uses Qdrant (`upsert` + `scroll` + filtered `search`) instead of Nexus. This is what actually delivers cross-agent memory sharing: a node is upserted with its OWN embedding under a deterministic per-(tenant,node) point id (idempotent re-push), and `fleet_pull` reliably SCROLLS a tenant's points back — the enumerate Nexus lacks (its `/search` doesn't return ingested docs and `/export` needs lancedb, which has a hard dependency conflict in the fleet). **Proven live with 2 agents:** agent-1 ingests → auto-syncs to the tenant dataplane; a fresh agent-2 in the same tenant `fleet_pull`s all of agent-1's nodes and can search them; a different tenant pulls nothing (isolation). Dimension-safe (never mixes vector dims in a collection). Local SQLite stays source of truth; best-effort, never raises.\n\n## [2.13.5] - 2026-07-02\n\n### Fixed\n- **CRITICAL — GraphMemory dataplane sync push was 100% failing.** `_fleet_push_node` sent `source_type=\"graph\"` / `content_type=\"graph_node\"`, which AitherNexus rejects as invalid enums (HTTP 500) — so NO graph node ever replicated in 2.13.4. Now sends the accepted `source_type=\"manual\"` / `content_type=\"text\"` (graph provenance is carried in `metadata.synced_from`). **Verified live** against a fleet Nexus: a 4-node ingest replicates with `pending==0` and the collection count reflects it.\n\n### Added\n- **Swarm-awareness notification** — on a successful sync batch, `GraphMemory` emits a best-effort `graph.synced` event (tenant/workspace/agent/collection/count) to `AITHER_FLEET_EVENTS_URL` so OTHER agents in the tenant/swarm can pull the fresh data and deconflict in-flight work (push-based awareness instead of polling). No-op unless the env is set.\n\n### Known limitation\n- Cross-agent **rehydration** (`fleet_pull`) is only reliable when the fleet Nexus is backed by lancedb (persistent + exportable). An in-memory Nexus stores on `/ingest` but neither its `/search` nor `/export` returns the docs — two-way read needs the persistent Nexus.\n\n## [2.13.4] - 2026-07-02\n\n### Added\n- **Memory-maintenance primitives on `GraphMemory`** (all opt-in; default behaviour byte-identical):\n - **Reinforce-on-recall**: `recall_with_activation(..., reinforce=True)` bumps `reinforcement_count`/`last_reinforced` on every RETURNED node's metadata mirror (one UPDATE per node, WAL-safe) — making the `MemoryRecord` reinforcement fields the activation scorer reads LIVE. Default `False` = zero writes.\n - **`sweep(now, archive_below=0.05, max_nodes=None, dry_run=False)`**: read-time decay enforced at rest — archives nodes past their tier TTL with freshness×reinforcement score below `archive_below` and `reinforcement_count <= 1`; `max_nodes` additionally archives the lowest-scored overflow. PERMANENT tier immune. Every archive is a reversible governance tombstone (`TombstoneStore.recover`) + a FORGET ledger entry; a node is never deleted without a tombstone. `dry_run` returns the would-archive list without acting.\n - **`promote(node_id, tier=?, role=?)`**: re-tier/re-role a node IN PLACE (tier/role columns + metadata mirror) — edges, embedding and ledger history survive; ledgers an UPDATE entry when governance is on.\n- `MutationType.UPDATE` ledger entry kind (in-place re-tier/re-role).\n- **Per-tenant dataplane sync for `GraphMemory`** — replicate the local graph to a per-tenant vector dataplane (Nexus/Qdrant) so a sovereign agent's memory is durable + rehydratable across restarts and hosting boundaries. `fleet_push_all_nodes()` (catch-up), `fleet_pull()` (best-effort semantic top-up), `fleet_sync_pending()`, and auto-sync on ingest (`_maybe_autosync` + `drain_sync()`). **On by default** (`AITHER_FLEET_SYNC=auto`; only `false/0/off` disables) with an inferred target (local Nexus `:8122` or the gateway in cloud mode) and a SEPARATE collection (`AITHER_FLEET_GRAPH_COLLECTION`, default `graph_memory`) from the KV memory sync. New `GraphMemory(tenant_id=, workspace_id=, fleet_url=, auto_sync=)` constructor args override env (required for multi-tenant host processes). Node `synced` column + schema v4 migration. TLS verify is CA-bundle-aware (`AITHER_CA_BUNDLE`) for the internal-TLS mesh. Local SQLite stays the source of truth; every sync path is best-effort and never raises into ingest.\n\n## [2.13.3] - 2026-07-02\n\n### Added\n- **Canonical self-deploying embeddings provider** (`adk.embeddings`): one embedding provider for the whole SDK so vectors are portable across scopes (platform / tenant / sovereign) — same model + dimension everywhere. Pinned to `nomic-embed-text` (768-d; the vLLM `--served-model-name` for nomic-embed-text-v1.5). Lazy, single-flight resolution chain: explicit `AITHER_EMBEDDINGS_URL` → local vLLM (`:8209` then `:8120`, HTTPS-then-HTTP) → local Ollama → gateway (`AITHER_GATEWAY_EMBEDDINGS_URL`) → auto-deploy a local vLLM embeddings container if a GPU + Docker are present (opt out with `AITHER_EMBED_AUTODEPLOY=0`) → CPU sentence-transformers (384-d, degraded) → feature-hash (384-d, degraded, always works). Every batch is dimension-tagged so callers never silently mix 768-d and 384-d vectors. `get_default_embedder()`, `embed_texts()`, `embed_one()`, `get_provider()`, `reset_provider()`.\n\n### Changed\n- **`GraphMemory` now defaults its embedder to the canonical provider** (opt out with `AITHER_GRAPH_EMBEDDER=legacy`), so every adk agent shares one 768-d space. Added a meta-table dimension guard: the index is pinned to its first embedding's dimension and later different-dimension vectors are refused (kills 768↔384 mixing that silently poisons cosine similarity).\n\n## [2.13.2] - 2026-07-01\n\n### Added\n- **Session persistence**: AitherShell now auto-saves the active session_id to `~/.aither/config.yaml` after each chat turn and auto-restores it on the next shell launch. Multi-turn conversations survive across restarts — the messages were always persisted in SQLite + JSON, only the session pointer was missing.\n- `/new` command to start a fresh session (clears saved session_id).\n- `/sessions` command to list recent sessions with agent name, message count, and timestamp. Resume any with `/resume <id>`.\n\n## [2.13.1] - 2026-07-01\n\n### Fixed\n- **CRITICAL:** `deploy_connect()` now sends a HEAD request to verify the GitHub release asset exists before downloading, with a clear error if the asset is missing.\n- **CRITICAL:** Compose file URLs are now pinned to a stable commit hash (configurable via `AITHER_COMPOSE_PIN` env) instead of tracking the mutable `main` branch.\n- **CRITICAL:** `deploy_adk_node()` now falls back to starting `adk-serve` natively when Docker is unavailable, instead of blocking with \"Docker not installed\".\n- **HIGH:** GHCR login failure now blocks deployment immediately with actionable guidance, instead of warning and continuing to a confusing image-pull failure.\n- **HIGH:** Health checks now stream the last 30 lines of Docker Compose logs on failure, so the user can see why a container didn't start.\n- **HIGH:** vLLM port allocation now probes for conflicts before assigning; if port 8200 is busy, workers are automatically remapped to the next free port.\n- **MEDIUM:** Compose file downloads are now cached with ETag; re-running `adk deploy node` skips re-downloading an unchanged file.\n- **MEDIUM:** DGX Spark discovery now respects `AITHER_DGX_HOSTS` env (comma-separated) for custom network topologies, instead of only checking `spark.local` and `192.168.0.33`.\n- **MEDIUM:** Cloud API keys (Anthropic, OpenAI, DeepSeek) are now validated with a lightweight probe during `adk setup` infra scan, so invalid keys surface early.\n- **MEDIUM:** `deploy_connect()` now validates the extracted extension has a `manifest.json` and warns if it's missing.\n\n## [2.13.0] - 2026-07-01\n\n### Removed\n- **`adk.faculties.memory_graph`** — the pickle-based `MemoryGraph` module is deleted. `from adk.faculties import MemoryGraph` now resolves to the canonical `adk.graph_memory.GraphMemory` for back-compat.\n- **`adk.platform.memory`** — the entire legacy memory subsystem (`MemoryManager`, `UnifiedMemorySystem`, `GameEngine`, `StoryboardEngine`, `AnchorGenerator`) is deleted. Agent memory is `adk.memory.Memory` + `adk.graph_memory.GraphMemory`.\n\n### Changed\n- `adk work` command now uses `GraphMemory` instead of the removed pickle-based faculty graph.\n- `adk.MemoryGraph` and `adk.faculties.MemoryGraph` lazy exports now resolve to `adk.graph_memory.GraphMemory`.\n- `agent.set_memory_graph()` kept for back-compat but documented as deprecated; agents already wire `GraphMemory` automatically in `__init__`.\n- Platform startup (`adk.platform.infrastructure.startup`) gracefully handles missing `MemoryManager`.\n- Platform CLI module listing and health check updated to reflect removed memory module.\n\n## [2.12.7] - 2026-07-01\n\n### Deprecated\n- `adk.faculties.MemoryGraph` — emits `DeprecationWarning` on instantiation; use `adk.graph_memory.GraphMemory` (SQLite-backed, governed, embedding-native) instead.\n- `adk.platform.memory` package — the legacy `MemoryManager`, `UnifiedMemorySystem`, `GameEngine`, and `StoryboardEngine` modules now emit `DeprecationWarning` on import; use `adk.memory.Memory` and `adk.graph_memory.GraphMemory` for new code.\n\n### Changed\n- Updated `adk.faculties` docstring and usage examples to point at the canonical memory surface.\n\n## [2.12.6] - 2026-07-01\n\n### Changed\n- Trimmed public package debt by removing grid runbook artifacts and shell/room binary placeholder directories from published artifacts.\n- Removed stale public-sync allowlist entries for deleted phase test files and added explicit public-sync guards for binary placeholder directories and grid artifacts.\n- Updated the public README grid note so it no longer links to runbook files excluded from the SDK package.\n\n## [2.12.5] - 2026-07-01\n\n### Added\n- Added best-effort fleet memory sync from local ADK memory into Nexus/Qdrant-compatible RAG stores, including batch catch-up and vector-search recall hooks.\n- Added saved config/env support for fleet memory URL, collection, and sync mode.\n\n### Changed\n- Excluded FormBridge runtime/test internals from public ADK packaging and public repo sync.\n\n## [2.12.4] - 2026-07-01\n\n### Added\n- Added a hot-reload endpoint for installed agent packs so newly applied pack tools can be picked up without restarting `aither-serve`.\n\n### Fixed\n- Fixed synchronous `ChatRelay` calls on Python 3.12 when no event loop is running; async WebSocket delivery is still scheduled when a loop exists.\n- Aligned gateway inference tier tests with the current public ladder: free, starter, pro, enterprise, platform.\n\n## [2.12.0] - 2026-06-28\n\n### Added — one-command local inference + upgrade path (click-to-run)\n- **`adk quickstart-local`** — detects hardware, picks a backend\n (Ollama / llama.cpp / vLLM), installs it, downloads a model, verifies, and\n prints next steps. Bootstraps Ollama itself via winget/brew/install.sh when\n missing (no \"go install it\" dead-ends). Default model `gemma4:e2b`.\n- **`adk backend switch <ollama|llamacpp|vllm>`** (+ `backend status`) — migrate\n the active deployment between engines; re-points config and smoke-tests.\n- **`adk install pack:<name>`** (+ `adk install list` / `adk packs`) — install\n ready-made agent packs (openclaw, hermes, claude-code) into `~/.aither/agents`.\n\n### Fixed (found via live end-to-end testing on Windows + CUDA)\n- quickstart-local reported success on a dead endpoint (smoke test was warn-only);\n a failed smoke now fails the command, with a service-readiness wait.\n- `--model` resolved to `None` (getattr default never fired) → uses the real default.\n- Smoke test: 30s→180s timeout for cold model load; accepts reasoning-model output\n (empty `content` at low token budgets is no longer a false failure).\n- `adk install pack:<name>` colon syntax (argparse rejected it before dispatch).\n- `adk backend status` health probe used a nonexistent `/health` path → 404; now\n probes `/v1/models` (uniform OpenAI-compatible liveness for all three backends).\n- Windows: removed non-ASCII glyphs from printed strings that crashed the cp1252\n console (a `✓` even made a successful install report failure).\n\n## [2.11.1] - 2026-06-14\n\n### Fixed — `adk host` / `adk login` device flow (autonomous onboarding)\nFound driving `adk host` against production; all broke real device-flow login:\n- **Device flow hit the portal FRONTEND, not AitherIdentity.** `adk login`/`adk host`\n POSTed `/auth/device/code` to portal/veil.aitherium.com, which 307-redirects API auth\n to `/login`. Added `_resolve_identity_url()` mapping the aitherium.com topology to\n `idp.aitherium.com` (the Identity API); localhost/bespoke pass through unchanged.\n- **Cloudflare 403'd urllib.** `idp.*` sits behind Cloudflare, which blocks urllib's\n default `Python-urllib/3.x` UA. Device-code + poll requests now send a real\n User-Agent + Accept (verified urllib→idp 200, was 403).\n- **`adk host` now self-authenticates via device flow** when there's no token, and\n **retries via device flow on a 401/403** (a stale saved token no longer dead-ends\n registration).\n\n## [2.11.0] - 2026-06-14\n\n### Added — attach your own MCP \"hands\" to a self-hosted agent\nA self-hosted agent now has the full brain (your model) · body (this loop) · **hands**\n(your tools) trifecta. The AitherOS platform relays the MCP servers you registered for\nyour agent in the `/stream` request body as `mcp_endpoints`, and the loop wires their\ntools into the ReAct turn:\n- **`adk/mcp_endpoint_tools.register_mcp_endpoint_tools(agent, endpoints)`** — speaks MCP\n JSON-RPC (`tools/list` to discover, `tools/call` to invoke) and registers a namespaced\n proxy tool (`{endpoint}__{tool}`) for each advertised tool. Mirrors `app_proxy_tools`.\n Best-effort: an unreachable endpoint registers nothing and never raises.\n- **`POST /stream` and `POST /chat/stream`** accept an optional `mcp_endpoints` list\n (`[{name, url, [headers]}]`); `_aitheros_stream` attaches them to the agent before the\n turn. Existing callers are unaffected (the field is optional).\n\n## [2.10.1] - 2026-06-14\n\n### Fixed — self-hosted `aither-serve` was crash-on-startup (2.10.0 regression)\nA fresh `pip install aither-adk && aither-serve --backend deepseek` could not boot. Found\nand fixed while standing up a real self-hosted DeepSeek agent end-to-end:\n- **Startup `NameError: name 'port' is not defined`** in three lifespan helpers\n (`_join_aithernet`, `_init_a2a_server`, `_start_mesh_hosting`) — they referenced a bare\n `port` no longer in scope. Now use `config.server_port`, and `main()` writes the resolved\n `--port`/`--host` back to `config` so every lifespan helper and the invoke_url see the\n actual bound port.\n- **Fleet self-registration crashed the whole server** — the registration handlers caught\n `(ImportError, RuntimeError, OSError, ConnectionError)` but **not** `httpx.HTTPError`, so a\n `RemoteProtocolError`/connect error talking to the control plane took the agent down. All\n network-facing lifespan handlers now also catch `httpx.HTTPError` (non-fatal, as intended).\n- **`--backend` was ignored when `AITHER_API_KEY` was set** — `_auto_detect` picked the cloud\n gateway before the explicit backend, and `_try_elysium_fallback` then replaced the chosen\n provider. An explicit backend (deepseek/openai/anthropic/your own vllm/ollama) now always\n wins; \"auto\"/\"gateway\" still flow through detection. The operator's own brain is never\n silently hijacked.\n- **Approved-tool trace truncated after `/sessions/{id}/confirm`** — the SSE relay treated\n `AgentResponse.tool_calls_made` (a `list[str]`) as dicts (`tc.get(...)`), raised\n `AttributeError`, and an over-narrow `except` swallowed it so the client saw only\n `session_start` + heartbeats. The relay now handles string tool names, the `except` is\n broad (always emits a typed `error` + terminal `complete`), and the module-level\n `_strata_ingest` `NameError` + Chronicle `session_id` kwarg mismatch are fixed.\n\n## [2.10.0] - 2026-06-13\n\n### Added — human-in-the-loop tool approval (`adk/approval.py`)\n- **Pause before a gated tool, resume on decision.** An agent can now pause its turn before\n executing a sensitive tool and wait for a human's allow/deny — so a managed control plane\n (or any operator) can gate tool use without forking the loop.\n- **Policy via `AITHER_TOOL_APPROVAL`** — a comma-separated list of tool names (optionally\n `agent:tool`), or `*` for every tool. Empty = no gating (unchanged behavior).\n- **Survives restarts / days-later approvals.** The paused turn (user message + pending tool\n calls) is persisted to disk keyed by `session_id`; resume is a fresh request, not a held\n connection. Decisions are recorded per `(session_id, tool_name)`.\n- **`AgentResponse.requires_action` / `.pending`** — set when a turn paused; `pending` lists\n the gated tool calls awaiting a decision.\n- **`AitherAgent.resume(session_id, decisions)`** — records the decisions and re-runs the\n paused turn (the gate consumes them: allowed tools execute, denied tools get a denial\n observation, the turn continues — and may pause again on a different gated tool).\n- **Server:** `POST /sessions/{id}/confirm` records decisions and streams the resumed turn;\n `/stream` emits a `requires_action` event when a turn pauses.\n\n## [2.9.0] - 2026-06-13\n\n### Added — universal LLM continuation primitive (`adk/llm/continuation.py`)\n- **`run_continuation()` + `stitch()`** — one shared continue-until-complete primitive. When a\n completion stops because it hit the output token cap (`finish_reason == \"length\"`), it continues\n the generation and STITCHES the chunks (overlap-dedup + restart-detect) into a complete answer,\n instead of every call-site hand-rolling its own retry. Bounded three ways (max rounds AND total\n output ceiling AND a no-growth break), and it never mutates the caller's message history.\n- **`LLMProvider.chat_with_continuation()`** and **`LLMRouter.chat_with_continuation()`** — every\n provider AND the router inherit the primitive, so any call-site gets continuation for free.\n- Kill-switch `ADK_LLM_CONTINUATION=off`; tunables `ADK_LLM_MAX_CONTINUATIONS`,\n `ADK_LLM_MAX_TOTAL_OUTPUT_TOKENS` (defaults: 2 rounds / 8192 tokens).\n\n### Changed\n- The ReAct loop's truncation recovery now uses the shared primitive. The previous inline\n doubling-retry REPLACED the partial response (losing earlier text in `resp.content`) and injected\n continuation scaffolding into the live message history; it now STITCHES and leaves history clean.\n Tool-call turns are never continued as text.\n\n### Included — previously-committed-but-unreleased changes\n- 2.8.2: TLS verification on by default (`verify=False` removed).\n- 2.8.1: baked license + pack-signing public keys.\n- 2.8.0: version-bump release.\n\n## [2.7.0] - 2026-06-05\n\n### Added — provider-agnostic LLM cost levers (at the `LLMProvider` ABC)\n- **Prompt caching.** New `ProviderCapabilities` (`prompt_cache: explicit|automatic|none`, `batch`) and\n normalized `LLMResponse.cache_read_tokens`/`cache_write_tokens`, so the \"tokens saved\" meter never\n depends on which backend served the turn. Anthropic inserts `cache_control` breakpoints over the stable\n system+tools prefix; OpenAI/DeepSeek caching is automatic and their `cached_tokens` are now surfaced\n (previously discarded). Default-on in `AitherAgent.chat`; override with `cache=False`.\n- **ResponseCache** (`adk/llm/cache.py`) — exact-match response cache with a pluggable backend, wired into\n `LLMRouter` as an opt-in (`LLMRouter(response_cache=…)` + per-call `cacheable=True`). OFF by default — no\n determinism change unless opted in.\n- **Gemini provider** (`adk/llm/gemini.py`) with explicit `cachedContents` caching; registered in the router.\n- **Async batch runner** (`adk/llm/batch_runner.py`) — `run_batch()` over the Anthropic Message Batches /\n OpenAI Batch APIs (~50% off) with a transparent fallback to concurrent `chat()` where unsupported.\n- **Graph-RAG retriever interface** (`adk/graph_retriever.py`).\n\n### Added — principal authority (prompt-injection defense enforced outside the model)\n- `adk/auth.py`: `Principal` / `AuthContext` / `PrincipalResolver` (+ deny-all default).\n- `ToolRegistry.execute(name, args, auth=…)` — a default-deny authorization gate at the tool boundary, so a\n forbidden tool call is blocked regardless of what the LLM emits. `ToolDef` gains `required_clearance` /\n `action_class`. Threaded through `AitherAgent.chat`; `auth=None` is fully backward-compatible.\n- `adk/channel_auth.py`: Telegram webhook-secret + CEO-id checks and HMAC-signed approvals.\n\n## [2.6.4] - 2026-06-05\n\n### Fixes\n- **Loop-guard nudges corrupted the message history → DeepSeek 400 mid-run.**\n When the ReAct loop emitted multiple tool calls in one assistant turn and the\n loop guard fired a WARN/BLOCK/CIRCUIT_BREAK on one of them, it spliced a\n standalone `system` nudge message *between* the tool results. DeepSeek (and\n strict vLLM chat templates) require an assistant `tool_calls` message to be\n followed by exactly one `tool` result per `tool_call_id`, contiguously — so the\n interleaved message produced `400 … insufficient tool messages following\n tool_calls message` and aborted the turn (this is reliably hit after crossing\n the 15-call circuit-break threshold, where every subsequent call WARNs).\n Per-call guidance is now folded into that call's own `tool` result, and any\n standalone steering is deferred to a single message AFTER the whole batch —\n preserving the `tool_calls`↔`tool_results` pairing every backend requires.\n Regression test: `deep-research-agent/tests/test_msg_structure.py`\n (loopguard-flood scenario).\n\n## [2.6.3] - 2026-06-04\n\n### Fixes\n- **Transient provider errors killed a run on the first blip.** A single\n `502 Bad Gateway` / `503` / `529 overloaded` / `429` from the upstream API\n aborted the whole request — the Anthropic provider had **no retry at all**, and\n the OpenAI-compatible provider only retried `429`. Both now retry transient\n statuses (`408/429/500/502/503/504/529`) with `Retry-After`-aware exponential\n backoff (3 attempts), and surface the provider's body if all retries are\n exhausted. Non-transient `4xx` (e.g. `400`) still fails fast. Applies to\n `chat` and `chat_stream` on both providers.\n\n## [2.6.2] - 2026-06-04\n\n### Fixes\n- **DeepSeek (and strict OpenAI-compatible backends) 400'd on long tool\n conversations.** The ReAct loop legitimately injects mid-conversation\n `system` steering messages (the \"[DIMINISHING RETURNS]\" hint after several\n low-output tool iterations, loop-guard nudges). OpenAI tolerates a non-leading\n `system` message; DeepSeek rejects it with a 400. Long research runs\n (many `fetch_url` calls) reliably tripped this. Fix: non-leading `system`\n messages are demoted to `user` at the OpenAI-compatible provider boundary\n (`_demote_nonleading_system`) — steering intent preserved, payload valid on\n every backend; leading system prompt(s) untouched.\n- **Opaque HTTP errors.** `resp.raise_for_status()` discarded the provider's\n response body, so a 400/422 surfaced as a bare `Client error '400 Bad\n Request'` with no cause. `_ensure_ok` now includes the provider's actual\n message (e.g. `context_length_exceeded`, `Invalid 'messages'`) in the raised\n error, for both `chat` and `chat_stream`.\n\n## [2.6.1] - 2026-06-04\n\n### Fixes\n- **`MCPServer.mount()` returned HTTP 422 for every `/mcp` call.** Under\n `from __future__ import annotations` the route handler's `request: Request`\n annotation is a string that FastAPI resolves against the *module* globals, but\n `Request` was imported locally inside `mount()` — so FastAPI couldn't resolve\n it and treated `request` as a required query param. The MCP server endpoint\n (`initialize`, `tools/list`, `tools/call`) was unusable when mounted. FastAPI\n symbols are now imported at module level (guarded; the `[node]` extra still\n gates actual use). Any agent's tools can now be exposed as a working MCP\n server with `MCPServer(...).mount(app)`.\n\n## [2.6.0] - 2026-06-04\n\n### Streaming + scaffolding\n- **`AitherAgent.stream_chat`** — reliable streaming sibling of `chat()`: native\n tool-calling loop (concludes on its own, unlike the text-protocol `stream_react`)\n that emits live `tool`/`tool_result` events and streams the answer as `token`\n events. `token_delay` paces tokens for a typing effect.\n- **`adk new <template>`** — scaffold a full, runnable template app. Ships the\n `deep-research` template (a cited web-research agent: pack + server + web UI):\n `adk new deep-research`.\n\n### Fixes\n- **BYO-key no longer community-capped.** The free-tier monthly token cap now applies\n only to the metered Aitherium gateway backend, never to a user's own provider key\n (Anthropic/OpenAI/DeepSeek/Ollama/vLLM). Previously it could brick a self-hosted\n agent after ~100k tokens on the user's own key.\n- **Stronger, grounded memory recall.** `chat()` now injects the top-6 knowledge-graph\n hits (untruncated) with a hard no-fabrication instruction — was top-3 truncated to\n 200 chars, which surfaced partial facts and let weaker models invent the rest.\n- **Tool-arg coercion.** `ToolRegistry.execute` coerces LLM-supplied args toward each\n parameter's annotated type, so a tool no longer crashes when a model passes a list\n or string where an int/float/bool was expected.\n\n### Docs\n- New `docs/AGENT_DEV_GUIDE.md` — the golden path for building agents/packs.\n\n## [2.5.0] - 2026-06-04\n\n### Continual learning — agents that learn from runs and reuse memory\n\nAgents now improve the second time they do a task instead of starting from scratch.\n\n### Added\n- **Recall-before**: `AitherAgent.chat()` searches the local `SkillStore` and injects the\n top matching learned skills as a system block, so the model reuses proven procedures.\n- **Learn-after**: `_learn_after()` runs after each response — reinforces (`success_count++`)\n any skills that were recalled+reused, and extracts + saves a NEW skill from successful\n multi-tool runs via `SkillExtractor`. Wired at both `chat()` return paths.\n- Gated by `AITHER_SKILLS` (default on). The previously-unused `SkillStore`/`SkillExtractor`\n are now connected to the run loop.\n\n### Notes\n- Pairs with the platform-side LearnedProcedure substrate (recipes → scored procedures →\n skills/tools/A2A/packs → MCTS priors → evolution). The SDK shares memory via the Spirit\n bridge when configured.\n\n## [2.0.0] - 2026-06-02\n\n### Open-core boundary (BREAKING)\n\nThis release draws a single, enforced free/paid line. The free tier stays\ngenuinely useful (real agent, typed + graph + code memory, ReAct, ~essential\ntools); paid tiers unlock the *scale* capabilities via portal.aitherium.com.\n\n### Added\n- **`adk/licensing.py`** — the entitlement keystone. Resolves a tier\n (COMMUNITY by default), verifies optional portal-signed `~/.aither/license.json`\n (Ed25519), and answers `can_use_fleet/channels/cron/swarm/auto_neurons`,\n `is_agent_licensed`, `max_effort`, etc. **Fail-closed**: no/invalid/expired\n license → free tier; an unsigned license never grants premium.\n `AITHER_TENANT_SLUG=aitherium` → unrestricted INTERNAL tier.\n- **Moat guard** (`scripts/check_moat_boundary.py`) wired into the publish\n workflow — blocks any wheel that leaks the moat or drops the keystone.\n- Tests: `tests/test_licensing.py`, `tests/test_moat_boundary.py`.\n\n### Changed (gating — fail-closed)\n- Free tier effort is capped at 3 (reasoning effort 7–10 needs Professional).\n- Monthly token budget hard-block on the free tier (no silent overspend).\n- Gated behind a paid tier: fleet mode (`FleetConfig`), cross-agent delegation\n (`AgentForge.delegate`), channel adapters, cron scheduler, proactive\n auto-neurons, and `swarm`/`swarm_code` dispatch.\n\n### Removed\n- **`adk/nanogpt.py`** (on-device training IP) removed from the published SDK\n and the public repo; relocated to the internal AitherOS moat. Excluded from\n the wheel as defense-in-depth.\n- Pre-2.0 releases shipped this IP and ungated capabilities and are being\n yanked (PyPI) / removed (GitHub releases). See `scripts/yank_leaky_releases.py`\n and `scripts/purge_public_leaks.sh`.\n\n## [1.20.0] - 2026-05-27\n\n### Fixed\n- **Pack discovery path mismatch** — `_find_local_packs()` now scans both\n `~/.aither/packs/` and `~/.aitheros/packs/`, so packs installed via\n `adk pack install` are discovered by `adk-workspace`.\n\n### Added\n- **Auto-register on pack install** — `adk pack install` now auto-registers\n with the portal if the pack contains an `agent.yaml` with a `portal` section.\n One command does download + extract + register.\n- **Flux event on agent registration** — `developer_portal.py` emits\n `agent.registered` Flux event so the portal fleet UI updates in real-time.\n\n## [1.15.0] - 2026-05-26\n\n### Added\n- **Cloud memory** — `adk quickstart --cloud` now tests gateway memory and\n auto-configures Spirit URL so memories persist across devices via\n `gateway.aitherium.com/v1/memory/teach` and `/v1/memory/recall`.\n- **Config→env export for cloud mode** — `Config.from_env()` now exports\n `AITHER_CLOUD_MODE`, `AITHER_SPIRIT_URL`, and spirit path env vars from\n saved config so Memory and other modules pick them up without needing\n explicit env vars set by the user.\n- **Cloud Quick Start section** in README — 3-command setup for users who\n just want a Claude/GPT-4/DeepSeek API key and the full agent harness.\n- **`--memory` profile for node deployment** — `adk deploy node --memory`\n adds Spirit (8087) + WorkingMemory (8101) containers for persistent\n vector memory. Without this, agents use local SQLite only.\n- **Configurable vLLM in node compose** — `AITHER_VLLM_MODEL`,\n `AITHER_VLLM_GPU_UTIL`, `AITHER_VLLM_CTX_LEN`, `AITHER_VLLM_QUANT_ARGS`\n env vars let `adk setup` write GPU-detected tier settings to `.env`\n and have the node compose pick them up (TQ4 for small GPUs, BNB for large).\n\n### Fixed\n- **Memory cloud routing** — `Memory._spirit_teach()` and `_spirit_recall()`\n now use configurable paths and auth headers for gateway proxy. Previously\n cloud-only users silently lost all Spirit memory calls.\n\n## [1.9.0] - 2026-05-24\n\n### Added\n- **Composable ToolPack system** — agents can load tool packs from\n `.toolpack.yaml` directories. Drop a pack in `~/.aitheros/packs/` or\n set `AITHER_PACKS_DIR`, and any agent discovers it automatically.\n- **`tools.packs` in agent.yaml** — list tool pack IDs to auto-load\n (e.g. `packs: [crm-tools, calendar-pro]`).\n- **`register_tool_packs(agent, pack_ids, packs_dir)`** in `builtin_tools` —\n programmatic pack loading for agents.\n- **`AITHER_TOOL_PACKS` env var** — comma-separated pack IDs loaded on\n agent init (alternative to YAML config).\n- **Unknown builtin categories resolve as pack IDs** — `tools.builtin:\n [file_io, crm-tools]` now works: `crm-tools` is looked up as a pack.\n\n## [1.8.0] - 2026-05-24\n\n### Removed\n- **11 dead modules** (3,023 lines) — `agent_deploy`, `cloud_deploy`, `create`,\n `phonehome`, `sdk_bridge`, `sovereign`, `telemetry`, `telemetry_config`,\n `error_reporter`, `anonymizer`, `package`. None were imported anywhere.\n- **29 ghost exports** from `__init__.py` — `secrets`, `otel`, `fs_sandbox`,\n and `vector_memory` modules were listed in `__all__` and `__getattr__` but\n the backing files never existed. Accessing them raised `ModuleNotFoundError`.\n- **Stale dist/ artifacts** — removed old wheels/tarballs (v0.4.0–v1.4.1, 11 MB).\n- **Orphan test scripts** — `test_phase1.py` and `test_phase23.py` (one-off\n validation scripts not part of the pytest suite).\n\n### Fixed\n- **Version mismatch** — `__init__.__version__` now matches `pyproject.toml`\n (was stuck at 1.6.0).\n\n## [1.5.0] - 2026-05-23\n\n### Added\n- **`--sovereign` flag for `adk deploy node`** — registers the node with the\n Aitherium hub via federation after deployment. Saves federation credentials\n to `~/.aither/.env.federation`. Accepts `--hub` (custom hub URL) and\n `--tenant` (tenant slug) options. Non-fatal: node runs standalone if\n registration fails.\n- **Federation → fleet cross-post** (Genesis) — sovereign nodes that register\n or heartbeat via `/federation/register` and `/federation/heartbeat` now\n automatically appear in the fleet dashboard (`/fleet/deployments`) with\n `node_type: \"sovereign\"` and live status/metrics.\n- **\"Deploy Sovereign Node\" section on Connect page** (AitherVeil) — Section 8\n with ADK deploy commands, fleet dashboard links, and provision page links.\n- **`deploy-node` action in tunnel proxy** (AitherVeil) — Connect page and\n external callers can now request bootstrap scripts via\n `POST /api/tunnel/devworkspace?action=deploy-node`.\n\n### Fixed\n- **Fleet provision page** — API URL corrected from `/api/fleet/provision` to\n `/api/bridge/genesis/fleet/provision` (matches bridge proxy pattern).\n- **Fleet detail page** — API URL corrected from `/api/fleet/deployments/` to\n `/api/bridge/genesis/fleet/deployments/` (same fix).\n- **Dockerfile** — entry point corrected from `aither-serve` to `adk-serve`\n (matches `pyproject.toml` script registration). Version label updated.\n\n## [1.4.1] - 2026-05-22\n\n### Fixed\n- **macOS executable builds** — dropped `--target-arch universal2` from\n `packaging/build_executable.py`. PyPI wheels for `pydantic_core` and other\n native deps are arch-specific, not fat binaries, so universal2 always failed\n with `IncompatibleBinaryArchError`. Now builds for the runner's native arch\n (`aither-macos-arm64`).\n- **Release workflow strategy** — added `fail-fast: false` so a single\n executable build failure no longer cancels the other OS jobs.\n- **PyPI publish** — added `skip-existing: true` so re-runs of the same\n version don't fail when the artifact already exists.\n- **GitHub Release job** — now runs whenever `pypi` succeeds even if some\n executable matrix entries fail, so the release page is always created with\n whatever binaries did build successfully.\n\n## [1.4.0] - 2026-05-22\n\n### Added\n- **Sovereign template now ships with `agent_core` 5-tier memory** — generated\n backends include the canonical `agent_core/` package (working → episodic →\n semantic → procedural → identity) plus `agent_memory.py` helper, vendored\n from `portal-kit-backend`.\n- `aiosqlite>=0.20.0` added to sovereign template `requirements.txt` for\n local agent_core SQLite storage.\n- Document tombstoning: `evict_cached_conversations_referencing` is wired\n through document-delete handlers in consumer apps so memories citing\n deleted documents are blocked from recall.\n- New tests: 13 smoke tests for `portal-kit-backend/agent_core` covering\n promotion thresholds, decay rates, store roundtrip, tombstone recall\n blocking, circuit breaker state machine, and chat-turn recording.\n\n### Changed\n- `scripts/vendor_agent_core.py` now syncs canonical `agent_core` to 4\n targets: WorkspaceRuntime, ADK sovereign template, and the GargBot +\n Chelle consumer mirrors (previously only 2).\n\n## [1.2.1] - 2026-05-21\n\n### Added\n- `adk train` command group — 6 subcommands: status, launch, logs, cancel, runs, register-gpu\n- `/slash-commands` endpoint — ADK server auto-exposes all 35 CLI commands as structured JSON manifest for AitherShell\n- `/cli/execute` endpoint — AitherShell can run any CLI command through the server\n- `build_command_manifest()` — introspects full argparse tree (commands, args, choices, subcommands)\n- `_register_commands()` extracted from main() for shared parser construction\n\n### Changed\n- `_get_genesis_url()` now probes: Genesis → ADK server → Aitherium cloud gateway (works without Genesis)\n\n## [1.2.0] - 2026-05-21\n\n### Added\n- **Runtime backend switching** — `agent.switch_backend(\"deepseek\")` and `agent.llm.switch_backend()` change provider without recreating agent\n- **Hybrid reasoning** — `agent.set_reasoning_backend(\"anthropic\")` routes effort 7+ to cloud API while keeping local orchestrator\n- **First-class DeepSeek provider** — `deepseek` in effort model table with `deepseek-chat` and `deepseek-reasoner`\n- **TQ4 quantization tiers** — `nano` (6GB, TurboQuant 4-bit), `standard-tq4` (12GB, both models TQ4), `hybrid-tq4` (6GB + cloud reasoning)\n- **`adk backend` CLI** — `list`, `set`, `set-reasoning`, `test` subcommands\n- **`adk quickstart`** — unified first-run wizard (setup + auth + shell)\n- **`adk tools`** — list local + MCP tools with tier markers\n- **`adk backup`** — tarball export of `~/.aither/`\n- **`adk ingest`** — feed docs/files into knowledge graph\n- **`adk setup nemotron`** shortcut — alias for `--tier lite`\n- **`--reasoning-api`** flag in setup — `anthropic`, `openai`, `deepseek`, `gateway`\n- **`--dgx-spark URL`** flag — remote vLLM endpoint configuration\n- **DGX Spark auto-detection** — scans `spark.local`, `192.168.0.33`, `AITHER_DGX_URL` env\n- **Post-setup smoke test** — sends real inference request after container startup\n- **Schema migration system** — version table + migration runner in `memory.py` and `graph_memory.py`\n- **Configurable vLLM port scan** — `AITHER_VLLM_PORTS` env var for custom ports\n- **`aithershell` entry point** — alias for `adk` in pyproject.toml\n- Shell auto-downloads binary on first `adk shell` (no `--install` needed)\n- Shell pre-flight check warns if no backend detected\n- Shell passes full config via env vars (API key, tenant, inference URL)\n- DGX/Remote and Cloud API checks added to `adk doctor`\n- Config fields: `reasoning_backend`, `reasoning_api_key`, `deepseek_api_key`, `dgx_url`, `vllm_extra_ports`\n\n### Fixed\n- `test_aeon_chat_basic` timeout — mock scope was exiting before request\n- `adk doctor` check_disk timeout — cap rglob at 5000 files\n\n## [1.1.7] - 2026-05-19\n\n### Added\n- Remote agent pipeline — `adk onboard` → fleet dispatch → remote inference\n- Agent-to-agent federation with mesh relay\n- `adk connect` for desktop mesh enrollment\n\n## [1.1.6] - 2026-05-19\n\n### Fixed\n- Tool call execution recovery across all backends\n- Hermes tool call fallback parsing for vLLM\n\n## [1.1.5] - 2026-05-19\n\n### Fixed\n- Tool call execution + Hermes fallback parsing improvements\n\n## [1.1.0] - 2026-05-18\n\n### Added\n- Multi-channel gateway (Telegram, Discord, Slack, webhook)\n- Aeon group chat with 7 presets\n- Skills auto-extraction from multi-step sessions\n- MCP stdio server for Claude Code integration\n- `adk gateway`, `adk cron`, `adk skills`, `adk soul` commands\n- SOUL.md import/export for portable agent identity\n- Elysium cloud device-flow authentication\n\n## [1.0.0] - 2026-05-18\n\n### Breaking\n- Package consolidated — `aither_adk` namespace replaced by `adk` + `adk.platform`\n- 93 duplicate files removed from `AitherOS/packages/aither_adk/`\n\n### Added\n- Standalone core primitives (`adk.core`: Agent, Tool, Memory, ModelBackend, Capability, Trace)\n- `adk.platform` sub-package merging all platform toolkit modules\n- Compatibility shims for legacy `from aither_adk.*` imports\n\n### Changed\n- Version synced across pyproject.toml, `adk/__init__.py`, `adk/platform/__init__.py`, npm package\n- Classifier promoted from Alpha to Beta\n\n## [0.16.0] - 2026-04-16\n\n### Swarm Coding Engine & Repowise Integration\n- **`repowise_search` tool** — Semantic + keyword hybrid code search via Repowise, with ripgrep fallback\n- **`swarm_code` tool** — Dispatch complex tasks to 11-agent swarm pipeline (ARCHITECT->SWARM->REVIEW->JUDGE)\n- **`agent.swarm()`** — Async convenience method with configurable mode, effort, timeout\n- **`agent.code_search()`** — Async convenience method returning structured results\n- New tool categories: `repowise` and `swarm` in TOOL_CATEGORIES\n- `repowise_search` and `swarm_code` added to `__init__.py` exports with lazy loading\n- `IDENTITY_DEFAULTS` updated: repowise for code-focused agents, swarm for orchestration agents\n- Standalone graceful degradation: repowise falls back to ripgrep, swarm returns structured error\n\n## [0.13.0] - 2026-04-02\n\n### Graph Faculties — Local Knowledge for Every Agent\n- **CodeGraph** (2,799 lines) — Full Python AST indexer with call graph, keyword/semantic/hybrid query, embedding matrix cache, incremental re-indexing, multi-hop chain expansion\n- **MemoryGraph** (1,339 lines) — Graph-based persistent agent memory with 10 edge types, hybrid query (keyword + semantic + graph expansion), multi-hop recall, pickle persistence\n- **EmbeddingProvider** — 4-backend fallback chain: sentence-transformers (GPU/CPU) -> Ollama -> Elysium cloud -> feature hashing (zero deps)\n- **BaseFacultyGraph** — Abstract base with HMAC-SHA256 validated pickle persistence\n\n### Agent Integration\n- `agent.set_code_graph(cg)` — auto-registers `code_search` + `code_context` tools\n- `agent.set_memory_graph(mg)` — auto-registers `remember` + `recall` + `memory_stats` tools\n- Both graphs inject context into LLM prompts automatically during chat\n\n### Zero-Config Onboarding\n- `adk start` / `adk` (no args) — auto-detect project, index code, detect LLM, persistent memory, interactive chat\n- Works for any directory: Python codebases, doc folders, mixed workspaces\n- Auto-detects LLM: Ollama -> vLLM -> Elysium -> OpenAI -> Anthropic\n- Per-project persistent memory in `~/.aither/memory/<project>`\n- `adk index <path>` — standalone indexing with progress bar and stats\n\n### MCP Gateway\n- `POST /v1/embeddings` — OpenAI-compatible embedding proxy to vLLM-embeddings:8209\n- Available to all tiers (embeddings are free)\n- ADK EmbeddingProvider uses this as Elysium cloud fallback\n\n### Optional Dependencies\n- `pip install aither-adk[graphs]` — numpy for 10x cosine similarity speedup\n- `pip install aither-adk[embedding]` — sentence-transformers + torch for local GPU embeddings\n- `pip install aither-adk` alone — graphs work with feature hashing (zero deps)\n\n## [0.12.0] - 2026-04-01\n\n### Bootstrap & Service Discovery\n- Version handshake -- ADK checks major.minor compatibility with Genesis/Node\n- Background reconnect loop -- ServiceBridge re-probes every 30s in standalone mode\n- Port 8080/8090 documented -- MCP vs OpenAI-compat clearly separated\n- Genesis URL configurable via GENESIS_URL env var (was hardcoded)\n- Standalone mode warning -- visible stderr alert when AitherOS not detected\n- Auto-reconnect on startup when services come online\n\n### Elysium Cloud Inference\n- Unified gateway URL -- gateway.aitherium.com handles auth + billing + inference\n- Streaming inference -- SSE passthrough for /v1/chat/completions with stream=true\n- Auth proxy routes -- /v1/auth/register, /v1/auth/login, /v1/auth/me\n- Billing proxy -- /v1/billing/balance through gateway\n- AitherConnect Elysium fallback -- cloud inference when local Genesis is down\n- AitherDesktop Elysium fallback -- third-tier chat fallback after Node\n\n### Infrastructure\n- /discovery endpoint on Genesis -- unified service URLs/versions/health\n- /api/config/services on Veil -- runtime port config for client-side JS\n- Veil healthcheck gates on Genesis -- unhealthy when backend is down\n- Desktop crash detection 90s->60s (threshold 3->2)\n\n## [0.11.0] - 2026-04-01\n\n### Agent Execution Quality (Claude Code Parity)\n- Raise loop guard block threshold from 3 to 4 -- agents get more room for iterative search\n- Soft synthesis nudge for effort >= 4 (no tool stripping, trust the model)\n- max_output_tokens escalation -- retry up to 3x when response is truncated\n- Tool result pairing guarantee -- synthetic error for orphaned tool_use blocks\n- Micro-compaction of old tool results -- save context tokens on long sessions\n- First-turn tool forcing only for effort >= 6 (trust model for lower effort)\n- Diminishing returns detection -- nudge agent when 3+ turns produce < 500 tokens\n- Message normalization -- merge consecutive same-role messages, strip empties\n- LLM retry with exponential backoff (5 retries, 500ms-16s, jitter)\n\n## [0.9.0] - 2026-03-16\n\nThe \"connected world\" release. Cross-platform identity pairing, voice capabilities, and multi-channel integration.\n\n### Added\n- **Pairing**: Cross-platform identity linking (`adk/pairing.py`)\n - `PairingManager` — SQLite-backed identity linking with 6-char pairing codes\n - Link users across Telegram, Discord, Slack, WhatsApp with 10min TTL codes\n - Canonical session IDs for cross-channel conversation continuity\n - `get_session_id()` returns \"user-{id}\" for paired users\n- **Voice**: Speech-to-text and text-to-speech client (`adk/voice.py`)\n - `VoiceClient` — async STT/TTS/emotion via AitherVoice service\n - Convenience functions: `hear()`, `say()`, `feel()`\n - 6 voice options: alloy, echo, fable, nova, onyx, shimmer\n - Emotion detection with intensity scoring\n- New exports: `PairingManager`, `PairingResult`, `PlatformIdentity`, `VoiceClient`, `TranscriptionResult`, `SynthesisResult`, `EmotionResult`\n\n### Changed\n- `__init__.py` exports expanded with pairing and voice symbols\n\n## [0.6.0] - 2026-03-13\n\nThe \"group mind\" release. Multi-agent group chat, creative tools, and Iris identity.\n\n### Added\n- **Aeon**: Multi-agent group chat engine (`adk/aeon.py`)\n - `AeonSession` — persistent group chat with parallel agent execution\n - 7 presets: balanced, creative, technical, security, minimal, duo_code, research\n - Orchestrator synthesis: Aither summarizes all agent responses\n - Serial execution for Ollama, parallel for vLLM/cloud\n - ConversationStore persistence with `type: \"aeon\"` metadata\n - `group_chat()` one-shot convenience function\n- `aither aeon` CLI command — interactive terminal group chat with color-coded agents\n - `-p/--preset`, `-a/--agents`, `-r/--rounds`, `--no-synthesize` flags\n - `reset` and `quit` commands\n- Server endpoints: `POST /aeon/chat`, `GET /aeon/presets`, `GET /aeon/sessions/{id}`\n- Creative tools in builtin_tools: `image_generate`, `image_refine`, `image_search`, `video_generate`\n- Iris agent identity with visual generation capabilities\n- 48 Aeon tests (data models, presets, chat, context, persistence, server, exports)\n\n### Changed\n- `__init__.py` exports: `AeonSession`, `AeonResponse`, `AeonMessage`, `group_chat`, `AEON_PRESETS`\n\n## [0.5.0] - 2026-03-13\n\nThe \"tenant-ready\" release. Multi-tenant admin, permission grants, safety profiles, and a full setup wizard.\n\n### Added\n- `aither setup` interactive setup wizard with hardware detection, model selection, identity config\n- Strata storage backend: SQLite WAL persistence for conversations, memories, knowledge graphs\n- CLI test runner: `aither test` with auto-discovery and parallel execution\n- Permission grants system for MCP tool access control\n- MCP account management tools\n- LLM provider auto-detection for Ollama and vLLM endpoints\n- Apache-2.0 LICENSE file\n- Elysium desktop sync module\n- Comprehensive CLI test suite (493 tests)\n- Strata storage test suite (972 tests)\n- LLM provider tests (82 tests)\n\n### Changed\n- CLI expanded: `aither setup`, `aither test`, `aither bugreport`, `aither doctor`\n- Strata module rewritten as full local-first storage engine\n- Server startup includes Strata initialization\n- README rewritten with clearer quickstart and architecture docs\n\n### Fixed\n- Dead documentation links\n- Python 3.11 compatibility issues\n- Ruff per-file-ignores configuration\n- Identity provisioning edge cases\n- Docker node-gyp native dependency builds (python3 + build tools)\n\n## [0.4.0] - 2026-03-13\n\nThe \"own your AI\" release. Self-hosted agent OS for people who don't want their data on someone else's servers.\n\n### Added\n- Muse agent identity (creative/artistic generation)\n- Port 8120 to vLLM scan for ExoNodes discovery\n- Public roadmap with milestone-based porting schedule\n- Competitive positioning: self-hosted alternative to cloud-locked AI appliances\n\n### Changed\n- LLM router: compute-aware effort routing (replaces effort-level context gating)\n- README rewritten for sovereignty-first messaging\n- Package description updated\n- Promoted from alpha to stable release\n\n### Fixed\n- Version string consistency between pyproject.toml and __init__.py\n- vLLM port scanning now includes port 8120 (ExoNodes)\n\n## [0.3.1] - 2026-03-09\n\n### Added\n- GraphMemory: SQLite knowledge graph with Ollama embeddings and hybrid search\n- NeuronPool: auto-fire pre-LLM data gathering agents\n- NanoGPT: pure Python char-level transformer with LoRA fine-tuning\n- Safety gates: IntakeGuard (input), LoopGuard (recursion), Sandbox (code exec)\n- Event system: EventEmitter with chat/tool/forge event types\n- Builtin tools: identity-based tool selection\n- ServiceBridge: auto-discovery of AitherOS services\n- Streaming: chat_stream with safety gate integration\n- Auth middleware: Bearer token authentication\n- CLI: `aither init` and `aither serve` commands\n\n### Changed\n- Wired 5 previously disconnected modules into agent loop\n- 522 total passing tests (up from 85)\n\n## [0.3.0] - 2026-03-07\n\n### Added\n- Clean-room agent development kit\n- Multi-backend LLM providers (Ollama, OpenAI, Anthropic)\n- AitherAgent class with @tool decorator\n- SQLite conversation memory\n- OpenAI-compatible server (`aither-serve`)\n- 16 agent identities as package data\n- MCP bridge to mcp.aitherium.com\n- Privacy-centric opt-in telemetry\n- Bug reporting CLI and API\n- FastAPI server with OpenAI-compatible endpoints\n- Hardware auto-detection (5 tiers, 11 profiles)\n- Fleet orchestration and multi-agent coordination\n- A2A mesh protocol\n- Federation protocol for cross-instance agent dispatch\n\n### Initial release\n- 85 passing tests\n- Apache-2.0 license\n"}, "779a50a9e7edb7ec5bda1ba273a6b7e3679e4092": {"t": 1785748157.7636228, "n": 95057, "h": "fe3c7b6c583cacd8", "c": "# Changelog\n\nAll notable changes to aither-adk will be documented in this file.\n\n## [2.38.1] - 2026-07-24\n\nRelease plumbing only — 2.38.0 was tagged before the version manifests\n(npm/brew/winget) were synced, so its publish gate failed and it never reached\nPyPI. 2.38.1 is the same code with the manifests in sync. See 2.38.0 below for\nwhat actually changed.\n\n## [2.38.0] - 2026-07-24 (never published — superseded by 2.38.1)\n\n### Fixed — `adk join` could only ever have worked for us\n\n- **`adk join` defaulted identity to `https://localhost:8115`** — a fleet-INTERNAL\n address. `adk join` is the one command a stranger runs on their own GPU box, so on\n any machine not already running AitherOS locally, step 1 (the GitHub device flow)\n hit a refused connection, and so did node registration and the mesh-key issue. The\n conductor default was already public, which is exactly why this went unnoticed.\n Identity now defaults to `https://idp.aitherium.com`.\n\n This fix landed *after* the 2.37.0 tag, so **2.37.0 and every earlier release on\n PyPI still ship the broken default** — 2.38.0 is the first release a new\n contributor can actually run. Verified live from a non-fleet machine (DGX):\n `POST /auth/github/device/start` → HTTP 200 with a real `user_code` and\n `verification_uri`.\n\n- **Upgrade check compared versions as strings** — `latest != current` reported a\n downgrade as an available upgrade. Now uses `packaging.version` with a numeric-tuple\n fallback.\n\n## [2.37.0] - 2026-07-24\n\n### Added — Agent Onboarding Fabric: bring your own agent, or run a managed one\n\nOnboard an agent built on **any** framework, or let Aitherium run it for you.\n\n- **ACP (Agent Client Protocol), both directions** — `ACPClient` drives any ACP agent;\n `ACPServer`/`serve_stdio` exposes an adk agent to any ACP host (Zed, VS Code,\n JetBrains). Both proven live against Zed's reference `agent-client-protocol`.\n- **Universal Agent Pack** — `AgentPackManifest` + `Supervisor` describe and run an\n external agent across 6 frameworks × 6 protocols, fail-closed on anything unknown.\n- **Protocol drivers** — `get_driver()` returns an `http`, `langgraph_rest`, `a2a`, or\n `mcp` driver, so REST/graph-orchestrated agents can be *managed*, not just connected.\n- **Pack registry** — `PackRegistry.publish/browse/versions/get/yank/verify_digest`,\n SHA256-digested, semver-ordered, validation fail-closed before anything is written.\n- **Managed identity** — `ManagedAgentIdentityProvider` with a real gateway minter and a\n PROVISIONED→REGISTERED→ACTIVE→ROTATED→REVOKED lifecycle; default-deny `authorize()`.\n- **Zero-code connect templates** — `render_connect(framework, …)` emits config pinned to\n each framework's real schema.\n\n### Added — NOOA-style agent capabilities\n\n- **`CodeActLoop`** — code as action: the model writes Python cells that run against a\n persistent namespace over live objects, each cell AST-validated before execution, with\n per-cell timeouts and bounded observations.\n- **`ObjectRegistry` / `render_observation`** — pass by reference: large values become\n handles instead of flooding the context with serialized text.\n- **`EventLog` / `ContextBlocks`** — harness APIs the model itself can call to query its\n own event history and manage named context blocks in KV-cache-stable order.\n- **`has_ellipsis_body` / `@strategy`** — NOOA's `async def f(...) -> X: ...` ergonomic.\n- **`CodeValidator`** — best-effort AST validation (ported from NVIDIA's OO-Agents,\n Apache-2.0; NOTICE included). A defense-in-depth layer, not a sandbox boundary.\n- **`PredictLoop`** — single-shot strategy with structured output (`output_model=`) and\n validation retry; `Strategy` is now a first-class alias of `AgentLoop`, and both loops\n accept a per-call `model=` override.\n- **Typed I/O** — `AgentResult` validates its fields at construction and at the\n `Agent.run` boundary.\n\nAll new capabilities are exported lazily from the top-level `adk` namespace.\n\n## [2.27.0] - 2026-07-18\n\n### Added — Moonshot Kimi K3 provider (DeepSeek-equivalent cloud backend)\n\n- **New `moonshot` provider** across the LLM router: OpenAI-compatible\n `https://api.moonshot.ai/v1`, default model `kimi-k3` (131K context default, up to 1M;\n tool calls, JSON mode, structured output). Wired everywhere `deepseek` is:\n - `LLMRouter(provider=\"moonshot\")` + `_COMPAT_URLS`/`_COMPAT_MODELS`/model tiers\n (small/medium/large → `kimi-k3`)\n - Config: `moonshot_api_key` field (env `MOONSHOT_API_KEY`), saved-config load,\n provider-keys map (`provider_keys.json` key `moonshot`)\n - Auto-detect chain: tried after DeepSeek (config key, then env key)\n - Hybrid reasoning backend: `AITHER_REASONING_BACKEND=moonshot` for effort 7+\n - Reasoning tiers: `backend: moonshot` (alias `kimi`) via new `MoonshotBackend`\n - CLI: `adk backend set moonshot --api-key …`, `adk backend list` shows moonshot\n- No breaking changes; DeepSeek and all other providers unchanged.\n\n## [2.26.0] - 2026-07-16\n\n### Added — Aeon group-chat web UI pack + POST /aeon/stream SSE endpoint\n\n- **New `aeon` UI pack** (`adk/webui/packs/aeon/index.html`) — group chat for multi-agent\n discussion. Mirrors the `llamacpp` pack's auth + SSE pattern (bearer from URL fragment #k=),\n with a preset selector (balanced/creative/technical/security/minimal/duo_code/research) and\n distinct colored agent messages. Synthesis block highlighted. Agent content is HTML-escaped\n before render. Fully self-contained HTML+CSS+JS, light+dark, no external assets. (No\n temperature/max-tokens controls — Aeon runs a fixed multi-agent round and doesn't thread\n per-turn generation params, so exposing them would be a no-op.)\n- **POST /aeon/stream SSE endpoint** — accepts body {message, preset?, agents?, rounds?,\n session_id?}, emits SSE with: `session_start` (participants + orchestrator), `agent_message`\n per non-orchestrator, `synthesis` (if enabled), `error` on exception, `complete` (total tokens\n + latency). Mirrors /chat/stream structure — like every streaming endpoint here it is gated by\n the server auth middleware **when `AITHER_SERVER_API_KEY` is set** (a local `adk up` with no key\n is unauthenticated by design, same as /chat/stream).\n- **Default UI pack flipped to `llamacpp`** — `resolve_ui_pack_name()` now returns `llamacpp`\n instead of `console` when no env/config is set (new users see the clean chat, not the admin\n SPA; existing users with AITHER_AGENT_UI or saved config unaffected).\n\n### Added — swappable agent web UI packs (llama.cpp-style built-in chat)\n\n- The page an agent serves at `/` is now a **swappable UI pack**, so you can\n drop in, test, and deploy different chat frontends without touching the agent.\n Select with `adk ui set <name>` (persists to `~/.aither/config.json`) or the\n `$AITHER_AGENT_UI` env; `adk ui ls` lists packs, `adk ui path` shows what\n resolves.\n- **New built-in `llamacpp` pack** — a clean, self-contained llama.cpp-style\n chat UI (centered conversation, streaming token-by-token with a cursor, a\n Stop button, a settings drawer for temperature / max-tokens that is actually\n honored end-to-end — `/chat/stream` now forwards them to the provider —\n minimal safe markdown, light+dark). Hits the agent's own gated `/chat/stream`\n with the bearer read from the URL fragment (never sent in the URL). Built-ins\n also include `console` (the full admin SPA, still the default) and `minimal`.\n- **Drop-in custom packs**: a UI pack is just a folder with an `index.html` in\n `~/.aither/ui-packs/<name>/` (override via `$AITHER_UI_PACKS_DIR`) — no rebuild.\n- Resolution is **fail-soft**: an unknown/missing pack falls back\n console → minimal, so `/` is never blank.\n\n### Added — MCP-gateway-first platform access + mesh A2A trust\n\n- **Gateway-first platform access** (`adk/client/_gateway_mcp.py`, `GatewayMCPClient`):\n a self-hosted agent connects its MCP **client** to `mcp.aitherium.com` authenticated\n as the OWNER (device-flow / PAT / ACTA token), RBAC-scoped to that identity — so it can\n use platform tools + `get_secret` **whether or not the node runs local AitherOS\n services**. Fail-closed: no token → client disabled, the agent still runs. Wired into\n the server lifespan (`_connect_gateway_mcp`).\n- **`genesis` LLM backend**: `adk backend set genesis` points the agent brain at a local\n Genesis (`http://localhost:8001/v1`, model `workflow`) — the 5090/controller pattern\n where inference is served by the local fleet, not the cloud gateway or a raw vLLM.\n- **`adk mesh` command**: `adk mesh onboard` drives the Conductor 5-step onboard\n (`/v1/mesh/onboard` → registers this node's public key, returns the `endpoint:mesh`\n trust token); `adk mesh ls` lists peer nodes/agents (AitherMesh `/nodes` + Directory)\n with their A2A cards + invoke URLs for discovery.\n\n### Security — A2A inter-agent trust enforcement (fail-closed)\n\n- **`adk/a2a_trust.py`** (new): Ed25519 signature verification + trusted-key check\n (`AITHER_A2A_TRUSTED_KEYS`), opt-in via `AITHER_A2A_REQUIRE_TRUST` (default `false` for\n backward compatibility; `audit` logs untrusted, `true` rejects).\n- **Fixed a fail-OPEN in the inbound A2A handler** (`adk/a2a.py`): the trust check\n previously ran *only when signature headers were present*, so an unsigned request\n bypassed `AITHER_A2A_REQUIRE_TRUST` entirely. Now genuinely fail-closed — required mode\n with no/invalid signature → `403`.\n- **Sovereign template no longer scaffolds `verify=False`**: the generated\n `services/llm.py` now uses the project's own `tls_verify()` policy helper for every\n provider call instead of hardcoding disabled TLS verification.\n- **`adk/_tls.py`**: disabling verification via `AITHER_TLS_VERIFY=false` now emits a\n one-time loud warning (was silent) — the escape hatch stays, the silence goes.\n\n## [2.25.1] - 2026-07-15\n\n### Fixed — self-hosted OpenAI-compatible backends honor a custom base URL\n\n- `adk backend set llamacpp --base-url http://localhost:8090/v1 --model <id>` (and\n `vllm`/`lmstudio`, and the `AITHER_LLM_BASE_URL` env) now actually point the agent\n at YOUR server. Previously the base URL was saved but never read, so an explicit\n `--backend llamacpp` fell back to the provider's public API default (openai.com).\n New `Config.llm_base_url` field + saved-config `inference_url` wiring; the explicit\n backend path in `LLMRouter` now passes it for the local OpenAI-compatible family.\n This is what lets a self-hosted node run its OWN local model (e.g. a PrismML\n Bonsai llama.cpp) as the agent brain instead of ollama/cloud.\n\n## [2.25.0] - 2026-07-15\n\n### Added — node_bootstrap tool pack: bootstrap LLM inference on any hardware\n\n- **New bundled tool pack `node-bootstrap`** (`adk/toolpacks/node_bootstrap/`):\n seven agent tools — `node_detect_hardware`, `node_resolve_recipe`,\n `node_plan_deployment`, `node_apply`, `node_enroll`, `node_register_backend`,\n `node_verify` — plus a CLI shim (`python -m adk.toolpacks.node_bootstrap\n detect|resolve|plan|apply|enroll|register|verify`). Detect a box's hardware,\n resolve a deployment recipe, actually deploy the inference engine (docker\n compose / native / delegate), enroll with a control plane, register the\n backend, and live-verify a completion.\n- **9 hardware recipes** shipped in the wheel: `cpu-1bit-llamacpp`, `cpu-ollama`,\n `cuda-vllm-8gb/24gb/40gb`, `cuda-dual-stack-32gb` (32GB-card co-resident\n stack), `unified-memory-vllm` (DGX/Grace-class), `metal-ollama` (native —\n Docker on macOS has no Metal passthrough), `cloud-api` fallback. Resolution\n scores hardware against min/max VRAM bands, RAM, cores and unified memory;\n ties prefer higher tiers and self-contained recipes over fleet delegates.\n- Security posture: enrollment and backend registration are fail-closed\n (missing token/URL → error dict, never anonymous, never `verify=False`);\n tokens are redacted in outputs; all endpoints come from args/env\n (`AITHER_CONTROL_PLANE_URL`, `AITHER_GENESIS_URL`, `AITHER_AUTH_TOKEN`).\n\n## [2.24.0] - 2026-07-14\n\n### Changed — sync-family consolidation\n\n- **Removed dead code:** `adk/sync.py` (528 lines) deleted — it was unreachable,\n shadowed by the `adk/sync/` package (`import adk.sync` always resolved to the\n package; nothing loaded the module by path — verified).\n- **Grouped the sync domain modules into the `adk.sync` package:**\n `brain_sync`→`adk.sync.brain`, `files_sync`→`adk.sync.files`,\n `lockbox_sync`→`adk.sync.lockbox`, `secrets_sync`→`adk.sync.secrets`,\n `session_sync`→`adk.sync.sessions`, `settings_sync`→`adk.sync.settings`,\n `sync_watermark`→`adk.sync.watermark`. All in-tree importers updated. No\n back-compat shims were left because **zero external/monorepo code imported the\n old root paths** (verified) — a clean move, not a shimmed one. **Breaking only\n for code that imported `adk.<name>_sync` directly** (none known).\n\n### Docs — MCP module layering clarified\n\n- `adk.mcp` (enterprise AitherOS gateway client: auth/billing/token-tracking) and\n `adk.core.mcp` (minimal generic JSON-RPC adapter for any MCP server) are\n DISTINCT layers, not duplicates — each now carries a LAYER/ROLE/See-also\n docstring so they are not mistakenly consolidated.\n\n## [2.23.2] - 2026-07-14\n\n### Fixed — CompletionGate no longer false-fails on correct results\n\n- **Tolerant judge JSON extraction.** The LLM judge parsed its reply with `json.loads(text[first\"{\":last\"}\"])`, which raised `JSONDecodeError` (\"Extra data\") whenever the model appended prose after the verdict or emitted a second object — and `verify()` turned that into a fail-closed verdict, so a genuinely completed task reported `unverified`. New `_first_json_object()` uses `JSONDecoder.raw_decode` from the first `{` that begins a valid object and ignores trailing content (prose, markdown fences, extra objects). A judge that answers with no parseable JSON still fails closed (never a soft pass), now with a clear reason. The same extractor backs `_derive_criteria`.\n- **Bare code-like tokens are hard-checked.** A natural criterion like `output contains ADK_AUTO_OK` (no quotes) previously fell through to the judge; `hard_checks` now also matches bare identifier/code shapes (contains an underscore, or 4+ CAPS/digit chars), with a stopword guard so a descriptive uppercase word (`OUTPUT`, `CONTAIN`) never becomes a false required token. Quote a token to force it regardless.\n- +9 tests (24 total), including the exact trailing-data regression.\n\n## [2.23.0] - 2026-07-14\n\n### Removed — dead modules and internal integrations\n\n- **`adk.swarm`, `adk.provisioning_tools`, `adk.session_sync_integration`, `adk.addon_metering`** — four orphan modules with zero public usage have been deleted. These were internal prototypes kept for legacy monorepo consumers; public packages have not imported them since 2.21.0 (tracked as D-348). **Breaking for anyone directly importing these modules** (import will fail). They are no longer exempt from the orphan-module check.\n- **`adk.aither_bridge`** — the AitherOS-internal IRC ↔ chat gateway bridge is no longer shipped in the public aither-adk package (stripped at sync time). All public importers already degrade behind `except ImportError` (in `adk.server` and `adk.builtin_tools`), so public packages remain unaffected. The module stays in the private AitherOS monorepo for internal use.\n- **Identity document trim** — removed internal agent names from `adk/identities/aither.yaml` delegation guidance, replacing with role-generic wording (\"delegate code review, refactoring, security analysis, performance and testing to specialist agents when available\"). Effort-tier routing philosophy remains unchanged and is product design, not a leak.\n\n### Changed — genericized customer/product names in help text\n\n- CLI help/examples no longer name specific customer deployments (`--scope` template list and `deploy agent` name example use generic placeholders). Functional deploy paths are unchanged.\n\n## [2.22.1] - 2026-07-14\n\n### Fixed — real bugs surfaced by reviving the public CI (red since Jul 5)\n\n- **TLS**: bundled toolpacks (arc-brainpack, cloudflare) disabled certificate\n verification on gateway/vault calls; all now route `adk._tls.tls_verify()`\n (verified by default, internal CA bundle when present).\n- **`system_prompt` contract**: bundled-pack `[PACK DIRECTIVES]` were appended\n even to an explicit `system_prompt`; an explicit prompt now wins wholesale.\n- **Headless secrets**: `_derive_key()` used `os.getlogin()`, which raises\n `OSError` without a controlling terminal (CI, daemons, containers) — falls\n back to `getpass.getuser()` (same value on interactive boxes, so existing\n secret files keep decrypting).\n- **`pack_verifier`**: missing `cryptography` crashed with `UnboundLocalError`\n in the exception handler instead of failing closed; now degrades gracefully\n (and `cryptography` was added to the dev extra so CI tests the real path).\n- **`fleet_manager._kill_pid` (POSIX)**: reported success immediately after an\n asynchronous SIGTERM and never reaped children (zombies read as alive); now\n polls, escalates to SIGKILL, and reaps.\n- **Identity**: `aither` regained the `creative` tool category (dropped\n accidentally in a 2.7.0 WIP sweep).\n\n## [2.22.0] - 2026-07-14\n\n### Changed — internal toolkit relocation\n\n- **`adk.platform` relocated** — the internal aither-platform toolkit (ComfyUI/image-gen\n pipelines, fleet agent clients, parallel CLI/UI/tools stack) has been moved out of the\n adk SDK tree to the standalone internal `aither-platform` package at\n `AitherOS/agents/aither_platform/`. The public package remains unaffected (it already\n excluded `adk.platform` since 2.21.1). Internal AitherOS agent builds now install\n `aither-platform` separately. `adk platform` falls through to the same \"not available\"\n message on public installs; internal builds work via the relocated package.\n\n### Removed — backward-compat re-export deleted\n\n- **`adk.gateway` module removed** — the backward-compatibility re-export of `GatewayClient`\n from `adk.gateway` has been removed. Use `from adk.client import GatewayClient` instead\n (the established public API since 2.21.0). All internal importers have been updated.\n\n## [2.21.1] - 2026-07-14\n\n### Removed — internal toolkit and license-gated product no longer ship publicly\n\n- **`adk.platform`** — the internal aither-platform toolkit (ComfyUI/image-gen\n pipelines, fleet agent clients, a parallel cli/ui/tools stack) is no longer\n included in the public repo or the PyPI wheel. It was internal-only code that\n had been riding along since the aither-platform merge. `adk platform` now\n reports \"Platform toolkit not available\" on public installs; internal AitherOS\n builds are unaffected.\n- **`adk.formbridge`** — the FormBridge form-automation product is license-gated\n (PROFESSIONAL+); its implementation no longer ships in the free package. All\n import sites (`builtin_tools`, `server` route mount) already degrade behind\n `except ImportError`, so nothing else changes behavior.\n- Product test suites (`test_formbridge.py`, `test_encounter_keying.py`,\n `test_flow_tools.py`) are excluded from the public sync alongside them.\n\n## [2.21.0] - 2026-07-14\n\n### Added — Completion gate: verified-or-retried task execution\n\n- **`adk.gate.CompletionGate`** — a composable wrapper that closes the \"silent\n success\" gap: an agent's ReAct loop returns whatever the model produced and\n calls it done (`finish_reason = stop/max_steps/length`), which is a *liveness*\n signal, not a *completion* one — so an agent can \"succeed\" while doing nothing.\n `CompletionGate` runs the agent, verifies the result against acceptance\n criteria, retries with the failure fed back, and returns the response with\n `finish_reason` set to `verified` or an honest `unverified`. Never a false pass.\n - **Hard-first, un-soft-passable checks** (`adk.gate.hard_checks`): a filesystem\n path a criterion says must exist is checked on disk; a required quoted token\n must appear in the output. Only genuinely subjective criteria fall through to\n an optional LLM judge (reuses the agent's own model), which **fails closed**\n if unavailable.\n - **`gated_run(agent, task, criteria=..., max_retries=2)`** — one-shot helper.\n - `auto_criteria` (LLM writes the machine-checkable definition-of-done from the\n task), a `verifier=` override for custom checks (tests-pass / HTTP probe), and\n a `requires_action` short-circuit so it never fights human-approval pauses.\n - Compose it onto any agent exposing `run(task) -> resp{.content, .finish_reason}`\n (`AitherAgent`, `core.Agent`, or a thin adapter over a remote dispatch).\n - Exported from the top-level package: `from adk import CompletionGate, gated_run`.\n\n## [2.19.0] - 2026-07-12\n\n### Added — Eve agent importer + durable A2A tasks + canonical agent discovery\n\n- **`adk pack import <eve_agent_path>`** — convert external Eve agents to AitherADK packs\n (from GitHub or local filesystem). Maps Eve structure (instructions → system prompt,\n agent.ts → agent.yaml, skills/*.md → skills, connections/*.ts → MCP, tools/*.ts →\n tools/node verbatim) to AitherADK format with a single command. Eve agents (compiled\n manifest format, schema v35) become installable packs compatible with `adk pack install`.\n- **Durable A2A task lifecycle (`TaskManager` backed by FileStore)** — agent-to-agent\n tasks now survive container restarts. Tasks persist as append-only JSONL\n (`.adk/tasks.jsonl`); in-memory dict for fast access; `create_task()` / `update_status()` /\n `add_artifact()` / `add_message()` all save to disk. Google A2A v0.3.0 spec compliant.\n- **Canonical agent card discovery** — `A2AServer.mount()` now exposes:\n - `GET /.well-known/agent-card.json` — Agent Card (canonical; contains agent identity,\n capabilities, skills, endpoints)\n - `GET /.well-known/agent.json` — Agent Card (legacy, redirects with 308 to canonical path)\n - `POST /a2a` — JSON-RPC 2.0 for message send/task lifecycle\n - `GET /a2a/tasks/{id}/subscribe` — SSE streaming task updates\n- **Four built-in agent packs now complete and catalogued**:\n - `hermes` — architecture review & trade-off analysis\n - `openclaw` — web research & source verification\n - `claude-code` — feature development & debugging\n - `analyst` — structured data inference & anomaly detection\n All discoverable via `adk packs` / `adk install pack:<name>`.\n- **Tolerant tool-call JSON parsing** — malformed tool JSON (extra properties, missing\n name/arguments, string-encoded nested args, missing keys) is now gracefully degraded\n instead of silently failing. `_parse_tool_json()` accepts both `\"arguments\"` and\n `\"parameters\"` keys, coerces string-encoded args, and skips unparseable calls instead\n of crashing the turn.\n\n### Known limitations\n\n- Eve agent importer: remote GitHub fetching not yet implemented (local paths only).\n- Durable task store: currently JSONL (suitable for thousands of records, not millions).\n\n## [2.18.0] - 2026-07-10\n\n### Added — AitherShell command center + Claude Code session management\n\n- **`aither sessions`** — Claude Code session manager: interactive full-screen\n browser (type-to-filter, transcript preview, deep full-text `search`, resume\n **in the current terminal** or as Windows Terminal tabs), crash guard\n (`guard install` = at-logon watchdog that snapshots live sessions and\n auto-restores the whole set after a terminal crash), and `ingest [--watch]`\n (incremental, secret-guarded sync of session conversations into the local KB\n / CompanyBrain via the standard ingest pipeline).\n- **`aither hq`** — command-center dashboard: per-service fleet health, LLM\n queue (depth/VRAM/models), Pulse alerts, live session count, inbox unread —\n auto-refreshing, with one-key jumps into chat, sessions, inbox, agents,\n brief, watchtower, and docker recovery.\n- **`aither inbox`** — unified queue over CommCore mail, Relay\n mentions/notifications, and Pulse alerts; open/mark-read/DM-reply from the\n terminal. Every source degrades independently.\n- **`aither agents`** — roster console + `agents ask -e <1-10> <agent> <q>`\n (effort-tiered ask via Genesis, with automatic `/chat/stream` fallback when\n the `/agent/sync` execute gate rejects host callers) + forge dispatch\n inspection + routine health.\n- **`aither palette`** — universal fuzzy picker across actions, sessions, and\n services; **`aither brief`** — renders Atlas's executive briefing in the\n terminal; **`aither watch`** — fleet watchtower with named wedge-signature\n detection (docker-WSL wedge, LLM-queue stall, crash-loop uptime regression)\n and optional docker auto-recovery.\n- All fleet reads go through a shared fail-soft `FleetClient` (internal-CA\n TLS via `adk._tls`, dashboard-grade timeouts, per-source degraded states).\n- Node shell-cli (`@aitherium/shell-cli` 1.13.0): `aither\n sessions|hq|inbox|palette|brief|watch|agents|docker` now pass through to the\n Python shell instead of being intent-classified as chat prompts.\n\n## [2.17.1] - 2026-07-05\n\n### Fully self-service mesh join — no customer key handling\n\n- **Auto-join headscale off the onboard response.** The conductor now auto-issues a\n headscale pre-auth key for NAT'd nodes and returns it in the `/v1/mesh/onboard`\n response; `mesh.join` consumes it and brings up the tunnel automatically. A customer\n runs one command — they never mint, fetch, or set a headscale key. Explicit\n `--headscale-key`/`AITHER_HEADSCALE_AUTH_KEY` still take precedence.\n- **Security:** `_tailscale_up` now redacts the auth key from all logs and exceptions\n (a failed `tailscale up` previously echoed the full argv, leaking the key into logs).\n\n## [2.17.0] - 2026-07-05\n\n### Added — Headscale mesh transport + local Qdrant + AitherConnect onboarding\n\n- **Headscale NAT-friendly mesh transport.** When raw WireGuard UDP:51820 is not\n viable (customer boxes behind NAT, corporate firewalls, CGNAT), `adk mesh join\n --headscale` routes the mesh tunnel through Tailscale's Headscale control plane.\n The overlay IP is still Conductor-assigned (10.77.0.0/16); Headscale provides\n only the transport layer. Configured via `AITHER_MESH_TRANSPORT=headscale`,\n `AITHER_HEADSCALE_URL` (default: https://headscale.aitherium.com), and\n `AITHER_HEADSCALE_AUTH_KEY` (pre-generated key). Automatic fallback to raw\n WireGuard if Headscale setup fails.\n- **Local Qdrant vector DB provisioning.** `adk stack` now provisions Qdrant\n locally when `AITHER_VECTOR_DB=qdrant` is set, enabling offline-first RAG\n without external dependencies. Tiered: local Qdrant in-fleet, cloud Nexus\n for enterprise multi-tenant deployments.\n- **Conductor URL public fallback.** `adk mesh join` resolves the internal\n `aitheros-conductor:8193` hostname; if unreachable (e.g., self-hosted nodes),\n it falls back to the public `conductor.aitherium.com:8193` endpoint,\n eliminating bootstrap configuration friction.\n\n### Documentation\n\n- **Self-hosting runbook.** New `docs/SELF_HOSTING.md` covers deploying AitherOS\n on customer infrastructure with Qdrant + mesh + AitherConnect onboarding.\n- **Mesh transport constraints documented.** Raw WireGuard requires public\n UDP:51820 endpoint (cloud instances, dedicated hardware); Headscale for\n NAT'd networks. Noted in CLI help and module docstrings.\n\n## [2.15.0] - 2026-07-04\n\n### Added — built-in web admin console + portal-profile settings sync\n\n- **Web admin console.** The page `adk up` opens (served at `/` and `/ui`) is now a full,\n self-contained console — Chat plus six admin tabs: **Backend** (view/switch LLM provider,\n set keys, test connection), **Packs** (list/enable/disable/reload tool packs),\n **Sessions** (browse/delete conversations), **Logs** (redacted agent-log tail),\n **Graph** (knowledge-graph search/neighborhood/stats), and **MCP** (add/remove external\n MCP servers). No build step, no CDN — a single packaged HTML asset. The original minimal\n streaming chat page stays at `/chat` for back-compat.\n- **Admin API (`adk/admin_api.py`).** ~20 endpoints under `/admin/*`, all bearer-gated by the\n existing middleware (never in `_skip_auth_paths`). Operates on the live agent — LLM backend\n swap (`LLMRouter.switch_backend`), pack reload, and MCP registration take effect without a\n restart.\n- **Settings sync (`adk/settings_sync.py`).** The user's portal profile is the source of\n truth: on startup the agent pulls `preferences.adk` and applies it over the local\n `~/.aither/config.yaml` cache; admin-console edits push a debounced snapshot back to\n `PUT /api/settings/preferences`. Provider API keys and MCP auth headers are device-local\n secrets and are never included in the pushed snapshot. Opt out with `AITHER_SETTINGS_SYNC=false`.\n\n### Security\n\n- **Timing-safe token comparison.** The server bearer check now uses `hmac.compare_digest`\n (the token is reachable over the public tunnel; a byte-wise `!=` leaked it to timing attacks).\n- **MCP add is SSRF-guarded + confirmed.** Adding an external MCP server is a two-step\n prepare→confirm flow; URLs resolving to loopback / private / link-local / metadata addresses\n are rejected (override with `AITHER_ALLOW_PRIVATE_MCP=1` for trusted local servers).\n- **Config editor is allowlisted.** `PATCH /admin/config` writes only safe scalar prefs and\n hard-denies fields that could execute code or redirect traffic at startup (e.g.\n `aither_toolpack_dirs`, backend base URLs).\n- **Log tail is scrubbed.** Bearer tokens, `#k=` fragments, and `sk-`/`aither_sk_` keys are\n redacted from the log-tail endpoint; the tunnel log is excluded.\n\n## [2.14.8] - 2026-07-04\n\n### Added — `adk up` for normal humans (no API key, no prereqs, live chat)\n\n- **Hosted-brain default — no API key required.** When there's no local model and no\n provider key, `adk up` signs in (device-flow) and routes inference through the gateway\n using the portal token (`AITHER_LLM_BACKEND=gateway` + bearer). A non-technical user\n never needs to obtain or paste an LLM API key. Explicit `--provider`/local model still\n take precedence; unattended `--yes` with no token/backend fails cleanly (exit 3).\n- **cloudflared auto-download.** If the tunnel binary is missing, `adk up` downloads the\n right platform asset to `~/.aither/bin` (SHA256-verified against the release; opt out with\n `AITHER_CLOUDFLARED_REQUIRE_CHECKSUM=0`) instead of exiting with an install hint.\n- **Built-in streaming chat page.** The agent now serves a self-contained chat UI at `/`\n (and `/chat`) with a live \"thinking…\" indicator and token-by-token streaming — so a human\n has somewhere to talk to it with immediate feedback instead of a blank wait. `adk up` opens\n it locally and passes the callback bearer in the URL fragment (`#k=…`), so `/chat/stream`\n stays authenticated (not an open proxy) with nothing to paste.\n- **Human-readable failures.** `adk up` errors now carry a plain-English `next_action`\n (kept machine-readable in `--yes` JSON).\n\n### Fixed\n\n- `adk up` chat link pointed at a non-existent `/settings/agent` route; it now points at the\n agent's own streaming chat page.\n\n## [2.14.7] - 2026-07-04\n\n### Added\n\n- **`adk up` — one command to run a persistent, fleet-connected agent.** Collapses\n the scattered onboarding surface (`host`/`quickstart`/`connect`/`login`/`setup`)\n into a single command that starts `aither-serve` (single agent, identity `aither`\n by default), opens a Cloudflare quick-tunnel for fleet reachability, registers with\n the portal, and installs a reboot-persistent autostart entry (Windows Task Scheduler\n / systemd-user / launchd). Detaches by default so the terminal is freed; `--foreground`\n blocks. Reuses the existing device-flow login, cloudflared discovery, provider-key\n store, and `_preflight_check` backend detection.\n- **Fully non-interactive path for autonomous agents.** `adk up --yes` (or any\n non-TTY invocation) takes ZERO prompts — everything resolves from flags, env, and\n saved config — and emits a single machine-readable JSON status line with meaningful\n exit codes (`0` ok, `2` bad-input, `3` no-backend, `4` tunnel-missing, `5` register-failed).\n Degrades to local-only (warn, not fail) when a portal token is absent unless\n `--require-register` is set.\n- **`adk down` / `adk status --json` companions.** `down` stops the agent + tunnel,\n best-effort deregisters, and removes the autostart entry; `status` reports the running\n agent (liveness + `/health` + tunnel + registration), with `--json` for agents/CI.\n- **`adk stack`** — the previous `adk up` behaviour (supervise the Room + Ollama consumer\n stack) now lives here; `adk up` is the connected-agent command.\n\n### Internal\n\n- New `adk/agent_daemon.py`: detached process spawn, status file\n (`~/.aither/adk-up.json`), pid liveness/teardown, and cross-platform autostart install.\n\n## [2.14.6] - 2026-07-04\n\n### Added\n\n- **`adk.preflight` — self-bootstrap capability probe.** `run_preflight(agent,\n spec)` wakes an agent up knowing what it is actually plugged into: a bounded\n (1.5s) parallel `LivenessProbe` over the router's resolved backends\n (primary/reasoning), the embeddings winner, remote MCP tools, the structured-ML\n / `/ml/teach` endpoints, and voice — returning an honest `CapabilityReport`.\n Status vocabulary distinguishes reachability from entitlement\n (`OK`/`MISSING`/`UNREACHABLE`/`AUTH`/`TIER_DENIED`/`UNSUPPORTED`), a hosted slot\n never prints a bare `OK`, and a required-but-unsatisfied slot aborts before the\n loop instead of blind-firing. Headless task/rules resolution + an allowed-roots\n policy binding (`builtin_tools.set_allowed_roots`, which resets the memoized\n cache so the override actually enforces).\n- **Multimodal (image) messages.** `Message.content` now accepts an OpenAI-style\n content-part list (`str | list[dict]`), passed through verbatim to the\n OpenAI-compatible body (gateway / vLLM / OpenAI). New `adk.llm.multimodal`\n helpers — `image_message()`, `image_content_parts()`, `to_image_url()` — build\n the parts and encode local image bytes/paths into `data:` URIs.\n- **Real vision preflight probe.** The vision slot is no longer unconditionally\n `UNSUPPORTED`: it sends a discriminable image (a solid red square) and requires\n the model to name the color, so a blind/text-only model cannot false-pass. `OK`\n only on a real round-trip; `AUTH` on 401/403; `UNSUPPORTED` when there is no\n OpenAI-compatible provider or the model cannot see the image.\n\n## [2.14.5] - 2026-07-04\n\n### Added\n\n- **`adk.reasoning.mcts` — generic Monte-Carlo Tree Search library.** A reusable\n MCTS engine (ported from the internal `UnifiedMCTS`, with all internal couplings\n removed) that any agent can drive over its own environment:\n - `MCTSEnvironment` protocol (`get_state_hash` / `get_actions` / `step` /\n `evaluate` / `clone`) — implement it and search.\n - Three **optional, default-off** model seams — `TransitionModel`, `PolicyModel`\n (PUCT priors), `ValueModel` — so a learned world-model / policy / value can be\n injected without touching the core; with all three `None` the engine is a plain\n UCT search.\n - `ObservedTransitionModel` adapter — a cheap online lookup world-model (hit =\n exact stored transition; miss = identity + negative \"unknown\" reward +\n `is_uncertain`, biasing exploration toward learning), with JSONL save/load for\n cross-run reuse.\n - `UnifiedMCTS(config).search(env)` returns an `MCTSResult` (best action, value,\n confidence, principal-variation path) and an optional `MCTSTrace`\n (visit-distribution policy target) for distillation.\n- **Self-bootstrapping agent machinery.** Build an agent from a declarative spec\n instead of bespoke wiring:\n - `adk.bootstrap.build_agent_from_spec(path) -> (AitherAgent, RunCtx)` — resolves\n prompts, activates required/optional tool packs, and carries a `memory_map`\n (a `node_type -> (role, tier)` override for memory writes, never a gate).\n - `adk.prompts.PromptBridge` — resolves an agent's prompt bindings from files\n (relative to the spec) or `AitherPrompts` dot-keys (optional import).\n - `adk.packs.PackActivator` — eager activation of installed tool packs by name\n over `register_tool_packs`; a required pack that registers zero tools raises\n `PackUnavailable`, optional packs soft-degrade.\n - `python -m adk.bootstrap.generic_agent <spec.yaml>` inspects a spec (resolved\n name, activated packs + tool counts, prompt keys, memory map) without running a\n live loop.\n- **`deep_research` tool pack** — a full multi-engine web-research workflow as\n `dr_*` agent tools (keyless search, page fetch + cache, site mirror, a citable\n findings knowledge graph, and markdown/PDF/DOCX report generation), activatable\n by name via the tool-pack loader.\n\n## [2.14.4] - 2026-07-04\n\n### Added\n\n- **`structured_ml` tool domain** — zero-shot structured-data inference for agents,\n backed by TabFM (tabular classification/regression) and TimesFM (time-series\n forecasting). Three tools:\n - `tabular_classify(support_rows, target, query_rows)` — classify rows from a\n labeled support set, in-context (no training step; up to 10 classes).\n - `tabular_regress(support_rows, target, query_rows)` — predict a numeric target.\n - `timeseries_forecast(series, horizon)` — forecast future values of a series.\n\n The tools POST to a structured-ML inference service resolved from\n `AITHER_STRUCTURED_ML_URL` (default `http://localhost:8192`), reject non-http(s)\n URLs, and summarise oversized responses to protect the agent's context.\n- **`Capability.STRUCTURED_INFERENCE`** capability token and a new **`analyst`**\n identity/pack that ships the `structured_ml` domain by default. The domain is\n opt-in — only identities that enable the `structured_ml` category get the tools.\n\n## [2.14.3] - 2026-07-03\n\n### Fixed\n\nAll four bugs below were found by actually booting `adk run` against a real\nchat model and driving a real tool-calling request end-to-end (not inferred\nfrom reading the code) — each one silently broke the \"local agent uses its\nconfigured model and its tools actually work\" path.\n\n- **`AITHER_MODEL` / `config.model` silently ignored by the OpenAI-compatible\n backend**: `LLMRouter.__init__` never read `config.model` into `self._model`\n unless the caller passed `model=` directly to the constructor — which nothing\n did. Every explicit model choice (env var, config file) was dropped, the\n provider fell back to a hardcoded per-backend default (`gpt-4o-mini` for the\n `openai` backend), and an unrecognized model name got silently remapped\n server-side to the wrong backing model. Concretely: setting\n `AITHER_MODEL=aither-orchestrator` had no effect — every request actually\n went to a different model that was never meant to be called directly and\n can't reliably use tools.\n- **`OPENAI_BASE_URL` ignored for explicit `--backend`/`AITHER_LLM_BACKEND`\n selection**: `LLMRouter._auto_detect()`'s explicit-backend branch hardcoded\n `base_url=None` when constructing the provider, instead of reading\n `config.openai_base_url` (which two other call sites in the same file\n already did correctly). Any self-hosted OpenAI-compatible endpoint\n (MicroScheduler, vLLM, LM Studio) configured via `OPENAI_BASE_URL` was\n silently ignored in favor of `https://api.openai.com/v1`.\n- **Tool-call steering never engaged on default-effort chat**: the retry that\n nudges a model back into structured tool-calling when it responds with prose\n instead of a function call was gated behind `effort >= 6`. Default `effort`\n for a plain `agent.chat(message)` call resolves to `5`, so the safety net —\n built specifically for this failure mode — never fired for default requests.\n Also now forces `tool_choice=\"required\"` on the retry instead of relying on a\n text nudge alone.\n- **Every non-string builtin-tool parameter silently typed as `\"string\"` in\n the generated tool schema**: `builtin_tools.py` uses\n `from __future__ import annotations`, so `_extract_parameters()` in\n `tools.py` was reading postponed (string) type hints (`\"int\"`, not `int`)\n straight out of `fn.__annotations__`, which never matched\n `_type_to_schema`'s type-object keys and silently fell through to\n `{\"type\": \"string\"}`. Every builtin tool with an `int`/`float`/`bool`/`list`\n parameter was affected — e.g. `file_read(start_line: int, end_line: int)`\n advertised both as strings, so a model correctly calling the tool with\n `\"start_line\": \"0\"` crashed the actual implementation with\n `unsupported operand type(s) for -: 'str' and 'int'`. Fixed by resolving\n hints through `typing.get_type_hints()` instead of the raw annotations dict.\n `_coerce_arguments()` — the runtime safety net that's supposed to coerce a\n model-supplied `\"0\"` string into `0` before a tool call executes — had the\n exact same raw-`__annotations__` bug and silently no-opped for every builtin\n tool; fixed the same way.\n- **Streaming tool-loop could hang a connection forever**: `chat_stream()`'s\n fallback to the sync tool loop (tool calls can't stream token-by-token) had\n no timeout, so a model that didn't cleanly terminate a tool-calling round\n trip left the SSE connection open indefinitely with zero bytes sent. Bounded\n with a 60s `asyncio.wait_for`; on timeout the stream yields a clear message\n instead of hanging.\n\n## [2.14.2] - 2026-07-03\n\n### Fixed\n- **AitherShell session amnesia**: the REPL generated a fresh `session_id` for\n EVERY message, so conversation history never followed across turns (the\n server stored each turn under a different session). One stable id is now\n minted per shell run; `/resume` and `/new` semantics unchanged, and the\n cross-restart auto-restore of the last session now actually works.\n\n## [2.14.1] - 2026-07-02\n\n### Added\n- **Account-tied licensing via device-flow login** — `adk login` now persists the account\n license returned by the device-flow handshake to `~/.aither/license.json` (base64\n `license_key` → `{payload, signature}` envelope), so entitlements from the authenticated\n account carry into every later `adk` invocation without re-auth. Pairs with the fleet-side\n node tool-pack gate that grants packs by plan-tier rank.\n- **`can_use_formbridge` / `can_use_untether` entitlements** (PROFESSIONAL+ rank ≥ 3) backing\n the FormBridge and UNTETHER agent tool-packs.\n\n### Fixed\n- `adk` CLI: guard the optional `memory_graph` singleton (was a latent `NameError` on the\n `/stats`, `/memory`, and save paths when memory was not constructed).\n\n## [2.14.0] - 2026-07-02\n\n### Added\n- **`adk.memory_wiki` — LyraWiki-style self-managed semantic knowledge** (opt-in; nothing constructs it by default): `MemoryWiki(graph_memory)` maintains curated wiki ARTICLES as `wiki_article` nodes (title/slug, LLM-owned markdown with `[[wikilinks]]`, embedding, durable tier, `CONSOLIDATED_FROM` edges to source memories, governance-ledgered revisions).\n - `consolidate(llm, since=, budget=)` clusters unconsolidated episodic/fact nodes (embedding + keyword), drafts/UPDATEs articles via the INJECTED llm callable (`str -> str` or `messages -> str`, sync or async — no hard model dependency), cites source node ids, marks contradicted sources superseded, then DEMOTES consolidated sources to a fast-decay tier via `promote()` (represented knowledge doesn't need raw copies).\n - `lint(llm=None)` — deterministic health checks (orphan wikilinks, empty/stale articles, live-but-contradicted pairs) + an optional LLM audit pass.\n - `prune(relevance_floor=0.05, hard_delete_after_days=14)` — article relevance = tier freshness × reinforcement (reinforce-on-recall) + link-degree to LIVE memories; below the floor → reversible governance tombstone; entombed past the window → **HARD-DELETE** (the tombstone snapshot itself is purged — content and embedding irrecoverable; the true-deletion path). A node is never deleted without a tombstone first.\n - `recall(query, limit)` — article-first RAG (curated articles rank above raw nodes) that reinforces returned articles.\n- **`adk.routines` — agent heartbeat + self-programmed routines**: `RoutineStore` is a durable registry (`~/.aither/routines/{agent}.json`, `AITHER_DATA_DIR`-aware, injectable path) of cron-scheduled self-prompts `{name, cron, instruction, enabled, last_run, last_result, tags}` that rehydrates into the existing `adk.cron.CronScheduler` on `start()`. A fire runs `agent.chat(instruction)` with the agent's full toolset — the agent programs its future self with its own adk. Guardrails: `max_routines` (12), a 5-minute min fire interval per routine, a per-fire timeout, and every result ledgered (truncated) to `last_result`. Direct-callable routines (`register_direct`) fire a bound method instead of a self-prompt. Self-management tools (`routine_create/list/update/pause/resume/delete/run_now`) ship as OpenAI tool defs + handlers (`build_routine_tools` / `register_routine_tools` / `routine_tool_defs`); the handlers ONLY touch the RoutineStore — they can never modify agent config or safety-relevant settings (the leash principle).\n- **`AitherAgent(routines=True, memory_maintenance=True)`** (both default **False** → the default agent is byte-identical): `routines=True` builds the RoutineStore + scheduler, registers the self-management tools, and starts the heartbeat lazily on the first `chat()` (or via explicit `start_routines()`). `memory_maintenance=True` registers DEFAULT routines — `wiki_consolidate` (every 2 h), `wiki_lint` / `wiki_prune` / `graph_sweep` (daily) — as DIRECT method fires so memory upkeep runs even on tiny models, while staying visible/manageable via `routine_list`.\n- `TombstoneStore.purge(tombstone_id)` — the hard-delete endpoint: irrecoverably removes a tombstone snapshot and rewrites the persisted store without it (consumed by `memory_wiki.prune`).\n\n## [2.13.6] - 2026-07-02\n\n### Added\n- **Qdrant backend for GraphMemory dataplane sync — TRUE two-way, verified live.** Set `AITHER_FLEET_QDRANT_URL` and the sync uses Qdrant (`upsert` + `scroll` + filtered `search`) instead of Nexus. This is what actually delivers cross-agent memory sharing: a node is upserted with its OWN embedding under a deterministic per-(tenant,node) point id (idempotent re-push), and `fleet_pull` reliably SCROLLS a tenant's points back — the enumerate Nexus lacks (its `/search` doesn't return ingested docs and `/export` needs lancedb, which has a hard dependency conflict in the fleet). **Proven live with 2 agents:** agent-1 ingests → auto-syncs to the tenant dataplane; a fresh agent-2 in the same tenant `fleet_pull`s all of agent-1's nodes and can search them; a different tenant pulls nothing (isolation). Dimension-safe (never mixes vector dims in a collection). Local SQLite stays source of truth; best-effort, never raises.\n\n## [2.13.5] - 2026-07-02\n\n### Fixed\n- **CRITICAL — GraphMemory dataplane sync push was 100% failing.** `_fleet_push_node` sent `source_type=\"graph\"` / `content_type=\"graph_node\"`, which AitherNexus rejects as invalid enums (HTTP 500) — so NO graph node ever replicated in 2.13.4. Now sends the accepted `source_type=\"manual\"` / `content_type=\"text\"` (graph provenance is carried in `metadata.synced_from`). **Verified live** against a fleet Nexus: a 4-node ingest replicates with `pending==0` and the collection count reflects it.\n\n### Added\n- **Swarm-awareness notification** — on a successful sync batch, `GraphMemory` emits a best-effort `graph.synced` event (tenant/workspace/agent/collection/count) to `AITHER_FLEET_EVENTS_URL` so OTHER agents in the tenant/swarm can pull the fresh data and deconflict in-flight work (push-based awareness instead of polling). No-op unless the env is set.\n\n### Known limitation\n- Cross-agent **rehydration** (`fleet_pull`) is only reliable when the fleet Nexus is backed by lancedb (persistent + exportable). An in-memory Nexus stores on `/ingest` but neither its `/search` nor `/export` returns the docs — two-way read needs the persistent Nexus.\n\n## [2.13.4] - 2026-07-02\n\n### Added\n- **Memory-maintenance primitives on `GraphMemory`** (all opt-in; default behaviour byte-identical):\n - **Reinforce-on-recall**: `recall_with_activation(..., reinforce=True)` bumps `reinforcement_count`/`last_reinforced` on every RETURNED node's metadata mirror (one UPDATE per node, WAL-safe) — making the `MemoryRecord` reinforcement fields the activation scorer reads LIVE. Default `False` = zero writes.\n - **`sweep(now, archive_below=0.05, max_nodes=None, dry_run=False)`**: read-time decay enforced at rest — archives nodes past their tier TTL with freshness×reinforcement score below `archive_below` and `reinforcement_count <= 1`; `max_nodes` additionally archives the lowest-scored overflow. PERMANENT tier immune. Every archive is a reversible governance tombstone (`TombstoneStore.recover`) + a FORGET ledger entry; a node is never deleted without a tombstone. `dry_run` returns the would-archive list without acting.\n - **`promote(node_id, tier=?, role=?)`**: re-tier/re-role a node IN PLACE (tier/role columns + metadata mirror) — edges, embedding and ledger history survive; ledgers an UPDATE entry when governance is on.\n- `MutationType.UPDATE` ledger entry kind (in-place re-tier/re-role).\n- **Per-tenant dataplane sync for `GraphMemory`** — replicate the local graph to a per-tenant vector dataplane (Nexus/Qdrant) so a sovereign agent's memory is durable + rehydratable across restarts and hosting boundaries. `fleet_push_all_nodes()` (catch-up), `fleet_pull()` (best-effort semantic top-up), `fleet_sync_pending()`, and auto-sync on ingest (`_maybe_autosync` + `drain_sync()`). **On by default** (`AITHER_FLEET_SYNC=auto`; only `false/0/off` disables) with an inferred target (local Nexus `:8122` or the gateway in cloud mode) and a SEPARATE collection (`AITHER_FLEET_GRAPH_COLLECTION`, default `graph_memory`) from the KV memory sync. New `GraphMemory(tenant_id=, workspace_id=, fleet_url=, auto_sync=)` constructor args override env (required for multi-tenant host processes). Node `synced` column + schema v4 migration. TLS verify is CA-bundle-aware (`AITHER_CA_BUNDLE`) for the internal-TLS mesh. Local SQLite stays the source of truth; every sync path is best-effort and never raises into ingest.\n\n## [2.13.3] - 2026-07-02\n\n### Added\n- **Canonical self-deploying embeddings provider** (`adk.embeddings`): one embedding provider for the whole SDK so vectors are portable across scopes (platform / tenant / sovereign) — same model + dimension everywhere. Pinned to `nomic-embed-text` (768-d; the vLLM `--served-model-name` for nomic-embed-text-v1.5). Lazy, single-flight resolution chain: explicit `AITHER_EMBEDDINGS_URL` → local vLLM (`:8209` then `:8120`, HTTPS-then-HTTP) → local Ollama → gateway (`AITHER_GATEWAY_EMBEDDINGS_URL`) → auto-deploy a local vLLM embeddings container if a GPU + Docker are present (opt out with `AITHER_EMBED_AUTODEPLOY=0`) → CPU sentence-transformers (384-d, degraded) → feature-hash (384-d, degraded, always works). Every batch is dimension-tagged so callers never silently mix 768-d and 384-d vectors. `get_default_embedder()`, `embed_texts()`, `embed_one()`, `get_provider()`, `reset_provider()`.\n\n### Changed\n- **`GraphMemory` now defaults its embedder to the canonical provider** (opt out with `AITHER_GRAPH_EMBEDDER=legacy`), so every adk agent shares one 768-d space. Added a meta-table dimension guard: the index is pinned to its first embedding's dimension and later different-dimension vectors are refused (kills 768↔384 mixing that silently poisons cosine similarity).\n\n## [2.13.2] - 2026-07-01\n\n### Added\n- **Session persistence**: AitherShell now auto-saves the active session_id to `~/.aither/config.yaml` after each chat turn and auto-restores it on the next shell launch. Multi-turn conversations survive across restarts — the messages were always persisted in SQLite + JSON, only the session pointer was missing.\n- `/new` command to start a fresh session (clears saved session_id).\n- `/sessions` command to list recent sessions with agent name, message count, and timestamp. Resume any with `/resume <id>`.\n\n## [2.13.1] - 2026-07-01\n\n### Fixed\n- **CRITICAL:** `deploy_connect()` now sends a HEAD request to verify the GitHub release asset exists before downloading, with a clear error if the asset is missing.\n- **CRITICAL:** Compose file URLs are now pinned to a stable commit hash (configurable via `AITHER_COMPOSE_PIN` env) instead of tracking the mutable `main` branch.\n- **CRITICAL:** `deploy_adk_node()` now falls back to starting `adk-serve` natively when Docker is unavailable, instead of blocking with \"Docker not installed\".\n- **HIGH:** GHCR login failure now blocks deployment immediately with actionable guidance, instead of warning and continuing to a confusing image-pull failure.\n- **HIGH:** Health checks now stream the last 30 lines of Docker Compose logs on failure, so the user can see why a container didn't start.\n- **HIGH:** vLLM port allocation now probes for conflicts before assigning; if port 8200 is busy, workers are automatically remapped to the next free port.\n- **MEDIUM:** Compose file downloads are now cached with ETag; re-running `adk deploy node` skips re-downloading an unchanged file.\n- **MEDIUM:** DGX Spark discovery now respects `AITHER_DGX_HOSTS` env (comma-separated) for custom network topologies, instead of only checking `spark.local` and `192.168.0.33`.\n- **MEDIUM:** Cloud API keys (Anthropic, OpenAI, DeepSeek) are now validated with a lightweight probe during `adk setup` infra scan, so invalid keys surface early.\n- **MEDIUM:** `deploy_connect()` now validates the extracted extension has a `manifest.json` and warns if it's missing.\n\n## [2.13.0] - 2026-07-01\n\n### Removed\n- **`adk.faculties.memory_graph`** — the pickle-based `MemoryGraph` module is deleted. `from adk.faculties import MemoryGraph` now resolves to the canonical `adk.graph_memory.GraphMemory` for back-compat.\n- **`adk.platform.memory`** — the entire legacy memory subsystem (`MemoryManager`, `UnifiedMemorySystem`, `GameEngine`, `StoryboardEngine`, `AnchorGenerator`) is deleted. Agent memory is `adk.memory.Memory` + `adk.graph_memory.GraphMemory`.\n\n### Changed\n- `adk work` command now uses `GraphMemory` instead of the removed pickle-based faculty graph.\n- `adk.MemoryGraph` and `adk.faculties.MemoryGraph` lazy exports now resolve to `adk.graph_memory.GraphMemory`.\n- `agent.set_memory_graph()` kept for back-compat but documented as deprecated; agents already wire `GraphMemory` automatically in `__init__`.\n- Platform startup (`adk.platform.infrastructure.startup`) gracefully handles missing `MemoryManager`.\n- Platform CLI module listing and health check updated to reflect removed memory module.\n\n## [2.12.7] - 2026-07-01\n\n### Deprecated\n- `adk.faculties.MemoryGraph` — emits `DeprecationWarning` on instantiation; use `adk.graph_memory.GraphMemory` (SQLite-backed, governed, embedding-native) instead.\n- `adk.platform.memory` package — the legacy `MemoryManager`, `UnifiedMemorySystem`, `GameEngine`, and `StoryboardEngine` modules now emit `DeprecationWarning` on import; use `adk.memory.Memory` and `adk.graph_memory.GraphMemory` for new code.\n\n### Changed\n- Updated `adk.faculties` docstring and usage examples to point at the canonical memory surface.\n\n## [2.12.6] - 2026-07-01\n\n### Changed\n- Trimmed public package debt by removing grid runbook artifacts and shell/room binary placeholder directories from published artifacts.\n- Removed stale public-sync allowlist entries for deleted phase test files and added explicit public-sync guards for binary placeholder directories and grid artifacts.\n- Updated the public README grid note so it no longer links to runbook files excluded from the SDK package.\n\n## [2.12.5] - 2026-07-01\n\n### Added\n- Added best-effort fleet memory sync from local ADK memory into Nexus/Qdrant-compatible RAG stores, including batch catch-up and vector-search recall hooks.\n- Added saved config/env support for fleet memory URL, collection, and sync mode.\n\n### Changed\n- Excluded FormBridge runtime/test internals from public ADK packaging and public repo sync.\n\n## [2.12.4] - 2026-07-01\n\n### Added\n- Added a hot-reload endpoint for installed agent packs so newly applied pack tools can be picked up without restarting `aither-serve`.\n\n### Fixed\n- Fixed synchronous `ChatRelay` calls on Python 3.12 when no event loop is running; async WebSocket delivery is still scheduled when a loop exists.\n- Aligned gateway inference tier tests with the current public ladder: free, starter, pro, enterprise, platform.\n\n## [2.12.0] - 2026-06-28\n\n### Added — one-command local inference + upgrade path (click-to-run)\n- **`adk quickstart-local`** — detects hardware, picks a backend\n (Ollama / llama.cpp / vLLM), installs it, downloads a model, verifies, and\n prints next steps. Bootstraps Ollama itself via winget/brew/install.sh when\n missing (no \"go install it\" dead-ends). Default model `gemma4:e2b`.\n- **`adk backend switch <ollama|llamacpp|vllm>`** (+ `backend status`) — migrate\n the active deployment between engines; re-points config and smoke-tests.\n- **`adk install pack:<name>`** (+ `adk install list` / `adk packs`) — install\n ready-made agent packs (openclaw, hermes, claude-code) into `~/.aither/agents`.\n\n### Fixed (found via live end-to-end testing on Windows + CUDA)\n- quickstart-local reported success on a dead endpoint (smoke test was warn-only);\n a failed smoke now fails the command, with a service-readiness wait.\n- `--model` resolved to `None` (getattr default never fired) → uses the real default.\n- Smoke test: 30s→180s timeout for cold model load; accepts reasoning-model output\n (empty `content` at low token budgets is no longer a false failure).\n- `adk install pack:<name>` colon syntax (argparse rejected it before dispatch).\n- `adk backend status` health probe used a nonexistent `/health` path → 404; now\n probes `/v1/models` (uniform OpenAI-compatible liveness for all three backends).\n- Windows: removed non-ASCII glyphs from printed strings that crashed the cp1252\n console (a `✓` even made a successful install report failure).\n\n## [2.11.1] - 2026-06-14\n\n### Fixed — `adk host` / `adk login` device flow (autonomous onboarding)\nFound driving `adk host` against production; all broke real device-flow login:\n- **Device flow hit the portal FRONTEND, not AitherIdentity.** `adk login`/`adk host`\n POSTed `/auth/device/code` to portal/veil.aitherium.com, which 307-redirects API auth\n to `/login`. Added `_resolve_identity_url()` mapping the aitherium.com topology to\n `idp.aitherium.com` (the Identity API); localhost/bespoke pass through unchanged.\n- **Cloudflare 403'd urllib.** `idp.*` sits behind Cloudflare, which blocks urllib's\n default `Python-urllib/3.x` UA. Device-code + poll requests now send a real\n User-Agent + Accept (verified urllib→idp 200, was 403).\n- **`adk host` now self-authenticates via device flow** when there's no token, and\n **retries via device flow on a 401/403** (a stale saved token no longer dead-ends\n registration).\n\n## [2.11.0] - 2026-06-14\n\n### Added — attach your own MCP \"hands\" to a self-hosted agent\nA self-hosted agent now has the full brain (your model) · body (this loop) · **hands**\n(your tools) trifecta. The AitherOS platform relays the MCP servers you registered for\nyour agent in the `/stream` request body as `mcp_endpoints`, and the loop wires their\ntools into the ReAct turn:\n- **`adk/mcp_endpoint_tools.register_mcp_endpoint_tools(agent, endpoints)`** — speaks MCP\n JSON-RPC (`tools/list` to discover, `tools/call` to invoke) and registers a namespaced\n proxy tool (`{endpoint}__{tool}`) for each advertised tool. Mirrors `app_proxy_tools`.\n Best-effort: an unreachable endpoint registers nothing and never raises.\n- **`POST /stream` and `POST /chat/stream`** accept an optional `mcp_endpoints` list\n (`[{name, url, [headers]}]`); `_aitheros_stream` attaches them to the agent before the\n turn. Existing callers are unaffected (the field is optional).\n\n## [2.10.1] - 2026-06-14\n\n### Fixed — self-hosted `aither-serve` was crash-on-startup (2.10.0 regression)\nA fresh `pip install aither-adk && aither-serve --backend deepseek` could not boot. Found\nand fixed while standing up a real self-hosted DeepSeek agent end-to-end:\n- **Startup `NameError: name 'port' is not defined`** in three lifespan helpers\n (`_join_aithernet`, `_init_a2a_server`, `_start_mesh_hosting`) — they referenced a bare\n `port` no longer in scope. Now use `config.server_port`, and `main()` writes the resolved\n `--port`/`--host` back to `config` so every lifespan helper and the invoke_url see the\n actual bound port.\n- **Fleet self-registration crashed the whole server** — the registration handlers caught\n `(ImportError, RuntimeError, OSError, ConnectionError)` but **not** `httpx.HTTPError`, so a\n `RemoteProtocolError`/connect error talking to the control plane took the agent down. All\n network-facing lifespan handlers now also catch `httpx.HTTPError` (non-fatal, as intended).\n- **`--backend` was ignored when `AITHER_API_KEY` was set** — `_auto_detect` picked the cloud\n gateway before the explicit backend, and `_try_elysium_fallback` then replaced the chosen\n provider. An explicit backend (deepseek/openai/anthropic/your own vllm/ollama) now always\n wins; \"auto\"/\"gateway\" still flow through detection. The operator's own brain is never\n silently hijacked.\n- **Approved-tool trace truncated after `/sessions/{id}/confirm`** — the SSE relay treated\n `AgentResponse.tool_calls_made` (a `list[str]`) as dicts (`tc.get(...)`), raised\n `AttributeError`, and an over-narrow `except` swallowed it so the client saw only\n `session_start` + heartbeats. The relay now handles string tool names, the `except` is\n broad (always emits a typed `error` + terminal `complete`), and the module-level\n `_strata_ingest` `NameError` + Chronicle `session_id` kwarg mismatch are fixed.\n\n## [2.10.0] - 2026-06-13\n\n### Added — human-in-the-loop tool approval (`adk/approval.py`)\n- **Pause before a gated tool, resume on decision.** An agent can now pause its turn before\n executing a sensitive tool and wait for a human's allow/deny — so a managed control plane\n (or any operator) can gate tool use without forking the loop.\n- **Policy via `AITHER_TOOL_APPROVAL`** — a comma-separated list of tool names (optionally\n `agent:tool`), or `*` for every tool. Empty = no gating (unchanged behavior).\n- **Survives restarts / days-later approvals.** The paused turn (user message + pending tool\n calls) is persisted to disk keyed by `session_id`; resume is a fresh request, not a held\n connection. Decisions are recorded per `(session_id, tool_name)`.\n- **`AgentResponse.requires_action` / `.pending`** — set when a turn paused; `pending` lists\n the gated tool calls awaiting a decision.\n- **`AitherAgent.resume(session_id, decisions)`** — records the decisions and re-runs the\n paused turn (the gate consumes them: allowed tools execute, denied tools get a denial\n observation, the turn continues — and may pause again on a different gated tool).\n- **Server:** `POST /sessions/{id}/confirm` records decisions and streams the resumed turn;\n `/stream` emits a `requires_action` event when a turn pauses.\n\n## [2.9.0] - 2026-06-13\n\n### Added — universal LLM continuation primitive (`adk/llm/continuation.py`)\n- **`run_continuation()` + `stitch()`** — one shared continue-until-complete primitive. When a\n completion stops because it hit the output token cap (`finish_reason == \"length\"`), it continues\n the generation and STITCHES the chunks (overlap-dedup + restart-detect) into a complete answer,\n instead of every call-site hand-rolling its own retry. Bounded three ways (max rounds AND total\n output ceiling AND a no-growth break), and it never mutates the caller's message history.\n- **`LLMProvider.chat_with_continuation()`** and **`LLMRouter.chat_with_continuation()`** — every\n provider AND the router inherit the primitive, so any call-site gets continuation for free.\n- Kill-switch `ADK_LLM_CONTINUATION=off`; tunables `ADK_LLM_MAX_CONTINUATIONS`,\n `ADK_LLM_MAX_TOTAL_OUTPUT_TOKENS` (defaults: 2 rounds / 8192 tokens).\n\n### Changed\n- The ReAct loop's truncation recovery now uses the shared primitive. The previous inline\n doubling-retry REPLACED the partial response (losing earlier text in `resp.content`) and injected\n continuation scaffolding into the live message history; it now STITCHES and leaves history clean.\n Tool-call turns are never continued as text.\n\n### Included — previously-committed-but-unreleased changes\n- 2.8.2: TLS verification on by default (`verify=False` removed).\n- 2.8.1: baked license + pack-signing public keys.\n- 2.8.0: version-bump release.\n\n## [2.7.0] - 2026-06-05\n\n### Added — provider-agnostic LLM cost levers (at the `LLMProvider` ABC)\n- **Prompt caching.** New `ProviderCapabilities` (`prompt_cache: explicit|automatic|none`, `batch`) and\n normalized `LLMResponse.cache_read_tokens`/`cache_write_tokens`, so the \"tokens saved\" meter never\n depends on which backend served the turn. Anthropic inserts `cache_control` breakpoints over the stable\n system+tools prefix; OpenAI/DeepSeek caching is automatic and their `cached_tokens` are now surfaced\n (previously discarded). Default-on in `AitherAgent.chat`; override with `cache=False`.\n- **ResponseCache** (`adk/llm/cache.py`) — exact-match response cache with a pluggable backend, wired into\n `LLMRouter` as an opt-in (`LLMRouter(response_cache=…)` + per-call `cacheable=True`). OFF by default — no\n determinism change unless opted in.\n- **Gemini provider** (`adk/llm/gemini.py`) with explicit `cachedContents` caching; registered in the router.\n- **Async batch runner** (`adk/llm/batch_runner.py`) — `run_batch()` over the Anthropic Message Batches /\n OpenAI Batch APIs (~50% off) with a transparent fallback to concurrent `chat()` where unsupported.\n- **Graph-RAG retriever interface** (`adk/graph_retriever.py`).\n\n### Added — principal authority (prompt-injection defense enforced outside the model)\n- `adk/auth.py`: `Principal` / `AuthContext` / `PrincipalResolver` (+ deny-all default).\n- `ToolRegistry.execute(name, args, auth=…)` — a default-deny authorization gate at the tool boundary, so a\n forbidden tool call is blocked regardless of what the LLM emits. `ToolDef` gains `required_clearance` /\n `action_class`. Threaded through `AitherAgent.chat`; `auth=None` is fully backward-compatible.\n- `adk/channel_auth.py`: Telegram webhook-secret + CEO-id checks and HMAC-signed approvals.\n\n## [2.6.4] - 2026-06-05\n\n### Fixes\n- **Loop-guard nudges corrupted the message history → DeepSeek 400 mid-run.**\n When the ReAct loop emitted multiple tool calls in one assistant turn and the\n loop guard fired a WARN/BLOCK/CIRCUIT_BREAK on one of them, it spliced a\n standalone `system` nudge message *between* the tool results. DeepSeek (and\n strict vLLM chat templates) require an assistant `tool_calls` message to be\n followed by exactly one `tool` result per `tool_call_id`, contiguously — so the\n interleaved message produced `400 … insufficient tool messages following\n tool_calls message` and aborted the turn (this is reliably hit after crossing\n the 15-call circuit-break threshold, where every subsequent call WARNs).\n Per-call guidance is now folded into that call's own `tool` result, and any\n standalone steering is deferred to a single message AFTER the whole batch —\n preserving the `tool_calls`↔`tool_results` pairing every backend requires.\n Regression test: `deep-research-agent/tests/test_msg_structure.py`\n (loopguard-flood scenario).\n\n## [2.6.3] - 2026-06-04\n\n### Fixes\n- **Transient provider errors killed a run on the first blip.** A single\n `502 Bad Gateway` / `503` / `529 overloaded` / `429` from the upstream API\n aborted the whole request — the Anthropic provider had **no retry at all**, and\n the OpenAI-compatible provider only retried `429`. Both now retry transient\n statuses (`408/429/500/502/503/504/529`) with `Retry-After`-aware exponential\n backoff (3 attempts), and surface the provider's body if all retries are\n exhausted. Non-transient `4xx` (e.g. `400`) still fails fast. Applies to\n `chat` and `chat_stream` on both providers.\n\n## [2.6.2] - 2026-06-04\n\n### Fixes\n- **DeepSeek (and strict OpenAI-compatible backends) 400'd on long tool\n conversations.** The ReAct loop legitimately injects mid-conversation\n `system` steering messages (the \"[DIMINISHING RETURNS]\" hint after several\n low-output tool iterations, loop-guard nudges). OpenAI tolerates a non-leading\n `system` message; DeepSeek rejects it with a 400. Long research runs\n (many `fetch_url` calls) reliably tripped this. Fix: non-leading `system`\n messages are demoted to `user` at the OpenAI-compatible provider boundary\n (`_demote_nonleading_system`) — steering intent preserved, payload valid on\n every backend; leading system prompt(s) untouched.\n- **Opaque HTTP errors.** `resp.raise_for_status()` discarded the provider's\n response body, so a 400/422 surfaced as a bare `Client error '400 Bad\n Request'` with no cause. `_ensure_ok` now includes the provider's actual\n message (e.g. `context_length_exceeded`, `Invalid 'messages'`) in the raised\n error, for both `chat` and `chat_stream`.\n\n## [2.6.1] - 2026-06-04\n\n### Fixes\n- **`MCPServer.mount()` returned HTTP 422 for every `/mcp` call.** Under\n `from __future__ import annotations` the route handler's `request: Request`\n annotation is a string that FastAPI resolves against the *module* globals, but\n `Request` was imported locally inside `mount()` — so FastAPI couldn't resolve\n it and treated `request` as a required query param. The MCP server endpoint\n (`initialize`, `tools/list`, `tools/call`) was unusable when mounted. FastAPI\n symbols are now imported at module level (guarded; the `[node]` extra still\n gates actual use). Any agent's tools can now be exposed as a working MCP\n server with `MCPServer(...).mount(app)`.\n\n## [2.6.0] - 2026-06-04\n\n### Streaming + scaffolding\n- **`AitherAgent.stream_chat`** — reliable streaming sibling of `chat()`: native\n tool-calling loop (concludes on its own, unlike the text-protocol `stream_react`)\n that emits live `tool`/`tool_result` events and streams the answer as `token`\n events. `token_delay` paces tokens for a typing effect.\n- **`adk new <template>`** — scaffold a full, runnable template app. Ships the\n `deep-research` template (a cited web-research agent: pack + server + web UI):\n `adk new deep-research`.\n\n### Fixes\n- **BYO-key no longer community-capped.** The free-tier monthly token cap now applies\n only to the metered Aitherium gateway backend, never to a user's own provider key\n (Anthropic/OpenAI/DeepSeek/Ollama/vLLM). Previously it could brick a self-hosted\n agent after ~100k tokens on the user's own key.\n- **Stronger, grounded memory recall.** `chat()` now injects the top-6 knowledge-graph\n hits (untruncated) with a hard no-fabrication instruction — was top-3 truncated to\n 200 chars, which surfaced partial facts and let weaker models invent the rest.\n- **Tool-arg coercion.** `ToolRegistry.execute` coerces LLM-supplied args toward each\n parameter's annotated type, so a tool no longer crashes when a model passes a list\n or string where an int/float/bool was expected.\n\n### Docs\n- New `docs/AGENT_DEV_GUIDE.md` — the golden path for building agents/packs.\n\n## [2.5.0] - 2026-06-04\n\n### Continual learning — agents that learn from runs and reuse memory\n\nAgents now improve the second time they do a task instead of starting from scratch.\n\n### Added\n- **Recall-before**: `AitherAgent.chat()` searches the local `SkillStore` and injects the\n top matching learned skills as a system block, so the model reuses proven procedures.\n- **Learn-after**: `_learn_after()` runs after each response — reinforces (`success_count++`)\n any skills that were recalled+reused, and extracts + saves a NEW skill from successful\n multi-tool runs via `SkillExtractor`. Wired at both `chat()` return paths.\n- Gated by `AITHER_SKILLS` (default on). The previously-unused `SkillStore`/`SkillExtractor`\n are now connected to the run loop.\n\n### Notes\n- Pairs with the platform-side LearnedProcedure substrate (recipes → scored procedures →\n skills/tools/A2A/packs → MCTS priors → evolution). The SDK shares memory via the Spirit\n bridge when configured.\n\n## [2.0.0] - 2026-06-02\n\n### Open-core boundary (BREAKING)\n\nThis release draws a single, enforced free/paid line. The free tier stays\ngenuinely useful (real agent, typed + graph + code memory, ReAct, ~essential\ntools); paid tiers unlock the *scale* capabilities via portal.aitherium.com.\n\n### Added\n- **`adk/licensing.py`** — the entitlement keystone. Resolves a tier\n (COMMUNITY by default), verifies optional portal-signed `~/.aither/license.json`\n (Ed25519), and answers `can_use_fleet/channels/cron/swarm/auto_neurons`,\n `is_agent_licensed`, `max_effort`, etc. **Fail-closed**: no/invalid/expired\n license → free tier; an unsigned license never grants premium.\n `AITHER_TENANT_SLUG=aitherium` → unrestricted INTERNAL tier.\n- **Moat guard** (`scripts/check_moat_boundary.py`) wired into the publish\n workflow — blocks any wheel that leaks the moat or drops the keystone.\n- Tests: `tests/test_licensing.py`, `tests/test_moat_boundary.py`.\n\n### Changed (gating — fail-closed)\n- Free tier effort is capped at 3 (reasoning effort 7–10 needs Professional).\n- Monthly token budget hard-block on the free tier (no silent overspend).\n- Gated behind a paid tier: fleet mode (`FleetConfig`), cross-agent delegation\n (`AgentForge.delegate`), channel adapters, cron scheduler, proactive\n auto-neurons, and `swarm`/`swarm_code` dispatch.\n\n### Removed\n- **`adk/nanogpt.py`** (on-device training IP) removed from the published SDK\n and the public repo; relocated to the internal AitherOS moat. Excluded from\n the wheel as defense-in-depth.\n- Pre-2.0 releases shipped this IP and ungated capabilities and are being\n yanked (PyPI) / removed (GitHub releases). See `scripts/yank_leaky_releases.py`\n and `scripts/purge_public_leaks.sh`.\n\n## [1.20.0] - 2026-05-27\n\n### Fixed\n- **Pack discovery path mismatch** — `_find_local_packs()` now scans both\n `~/.aither/packs/` and `~/.aitheros/packs/`, so packs installed via\n `adk pack install` are discovered by `adk-workspace`.\n\n### Added\n- **Auto-register on pack install** — `adk pack install` now auto-registers\n with the portal if the pack contains an `agent.yaml` with a `portal` section.\n One command does download + extract + register.\n- **Flux event on agent registration** — `developer_portal.py` emits\n `agent.registered` Flux event so the portal fleet UI updates in real-time.\n\n## [1.15.0] - 2026-05-26\n\n### Added\n- **Cloud memory** — `adk quickstart --cloud` now tests gateway memory and\n auto-configures Spirit URL so memories persist across devices via\n `gateway.aitherium.com/v1/memory/teach` and `/v1/memory/recall`.\n- **Config→env export for cloud mode** — `Config.from_env()` now exports\n `AITHER_CLOUD_MODE`, `AITHER_SPIRIT_URL`, and spirit path env vars from\n saved config so Memory and other modules pick them up without needing\n explicit env vars set by the user.\n- **Cloud Quick Start section** in README — 3-command setup for users who\n just want a Claude/GPT-4/DeepSeek API key and the full agent harness.\n- **`--memory` profile for node deployment** — `adk deploy node --memory`\n adds Spirit (8087) + WorkingMemory (8101) containers for persistent\n vector memory. Without this, agents use local SQLite only.\n- **Configurable vLLM in node compose** — `AITHER_VLLM_MODEL`,\n `AITHER_VLLM_GPU_UTIL`, `AITHER_VLLM_CTX_LEN`, `AITHER_VLLM_QUANT_ARGS`\n env vars let `adk setup` write GPU-detected tier settings to `.env`\n and have the node compose pick them up (TQ4 for small GPUs, BNB for large).\n\n### Fixed\n- **Memory cloud routing** — `Memory._spirit_teach()` and `_spirit_recall()`\n now use configurable paths and auth headers for gateway proxy. Previously\n cloud-only users silently lost all Spirit memory calls.\n\n## [1.9.0] - 2026-05-24\n\n### Added\n- **Composable ToolPack system** — agents can load tool packs from\n `.toolpack.yaml` directories. Drop a pack in `~/.aitheros/packs/` or\n set `AITHER_PACKS_DIR`, and any agent discovers it automatically.\n- **`tools.packs` in agent.yaml** — list tool pack IDs to auto-load\n (e.g. `packs: [crm-tools, calendar-pro]`).\n- **`register_tool_packs(agent, pack_ids, packs_dir)`** in `builtin_tools` —\n programmatic pack loading for agents.\n- **`AITHER_TOOL_PACKS` env var** — comma-separated pack IDs loaded on\n agent init (alternative to YAML config).\n- **Unknown builtin categories resolve as pack IDs** — `tools.builtin:\n [file_io, crm-tools]` now works: `crm-tools` is looked up as a pack.\n\n## [1.8.0] - 2026-05-24\n\n### Removed\n- **11 dead modules** (3,023 lines) — `agent_deploy`, `cloud_deploy`, `create`,\n `phonehome`, `sdk_bridge`, `sovereign`, `telemetry`, `telemetry_config`,\n `error_reporter`, `anonymizer`, `package`. None were imported anywhere.\n- **29 ghost exports** from `__init__.py` — `secrets`, `otel`, `fs_sandbox`,\n and `vector_memory` modules were listed in `__all__` and `__getattr__` but\n the backing files never existed. Accessing them raised `ModuleNotFoundError`.\n- **Stale dist/ artifacts** — removed old wheels/tarballs (v0.4.0–v1.4.1, 11 MB).\n- **Orphan test scripts** — `test_phase1.py` and `test_phase23.py` (one-off\n validation scripts not part of the pytest suite).\n\n### Fixed\n- **Version mismatch** — `__init__.__version__` now matches `pyproject.toml`\n (was stuck at 1.6.0).\n\n## [1.5.0] - 2026-05-23\n\n### Added\n- **`--sovereign` flag for `adk deploy node`** — registers the node with the\n Aitherium hub via federation after deployment. Saves federation credentials\n to `~/.aither/.env.federation`. Accepts `--hub` (custom hub URL) and\n `--tenant` (tenant slug) options. Non-fatal: node runs standalone if\n registration fails.\n- **Federation → fleet cross-post** (Genesis) — sovereign nodes that register\n or heartbeat via `/federation/register` and `/federation/heartbeat` now\n automatically appear in the fleet dashboard (`/fleet/deployments`) with\n `node_type: \"sovereign\"` and live status/metrics.\n- **\"Deploy Sovereign Node\" section on Connect page** (AitherVeil) — Section 8\n with ADK deploy commands, fleet dashboard links, and provision page links.\n- **`deploy-node` action in tunnel proxy** (AitherVeil) — Connect page and\n external callers can now request bootstrap scripts via\n `POST /api/tunnel/devworkspace?action=deploy-node`.\n\n### Fixed\n- **Fleet provision page** — API URL corrected from `/api/fleet/provision` to\n `/api/bridge/genesis/fleet/provision` (matches bridge proxy pattern).\n- **Fleet detail page** — API URL corrected from `/api/fleet/deployments/` to\n `/api/bridge/genesis/fleet/deployments/` (same fix).\n- **Dockerfile** — entry point corrected from `aither-serve` to `adk-serve`\n (matches `pyproject.toml` script registration). Version label updated.\n\n## [1.4.1] - 2026-05-22\n\n### Fixed\n- **macOS executable builds** — dropped `--target-arch universal2` from\n `packaging/build_executable.py`. PyPI wheels for `pydantic_core` and other\n native deps are arch-specific, not fat binaries, so universal2 always failed\n with `IncompatibleBinaryArchError`. Now builds for the runner's native arch\n (`aither-macos-arm64`).\n- **Release workflow strategy** — added `fail-fast: false` so a single\n executable build failure no longer cancels the other OS jobs.\n- **PyPI publish** — added `skip-existing: true` so re-runs of the same\n version don't fail when the artifact already exists.\n- **GitHub Release job** — now runs whenever `pypi` succeeds even if some\n executable matrix entries fail, so the release page is always created with\n whatever binaries did build successfully.\n\n## [1.4.0] - 2026-05-22\n\n### Added\n- **Sovereign template now ships with `agent_core` 5-tier memory** — generated\n backends include the canonical `agent_core/` package (working → episodic →\n semantic → procedural → identity) plus `agent_memory.py` helper, vendored\n from `portal-kit-backend`.\n- `aiosqlite>=0.20.0` added to sovereign template `requirements.txt` for\n local agent_core SQLite storage.\n- Document tombstoning: `evict_cached_conversations_referencing` is wired\n through document-delete handlers in consumer apps so memories citing\n deleted documents are blocked from recall.\n- New tests: 13 smoke tests for `portal-kit-backend/agent_core` covering\n promotion thresholds, decay rates, store roundtrip, tombstone recall\n blocking, circuit breaker state machine, and chat-turn recording.\n\n### Changed\n- `scripts/vendor_agent_core.py` now syncs canonical `agent_core` to 4\n targets: WorkspaceRuntime, ADK sovereign template, and the GargBot +\n Chelle consumer mirrors (previously only 2).\n\n## [1.2.1] - 2026-05-21\n\n### Added\n- `adk train` command group — 6 subcommands: status, launch, logs, cancel, runs, register-gpu\n- `/slash-commands` endpoint — ADK server auto-exposes all 35 CLI commands as structured JSON manifest for AitherShell\n- `/cli/execute` endpoint — AitherShell can run any CLI command through the server\n- `build_command_manifest()` — introspects full argparse tree (commands, args, choices, subcommands)\n- `_register_commands()` extracted from main() for shared parser construction\n\n### Changed\n- `_get_genesis_url()` now probes: Genesis → ADK server → Aitherium cloud gateway (works without Genesis)\n\n## [1.2.0] - 2026-05-21\n\n### Added\n- **Runtime backend switching** — `agent.switch_backend(\"deepseek\")` and `agent.llm.switch_backend()` change provider without recreating agent\n- **Hybrid reasoning** — `agent.set_reasoning_backend(\"anthropic\")` routes effort 7+ to cloud API while keeping local orchestrator\n- **First-class DeepSeek provider** — `deepseek` in effort model table with `deepseek-chat` and `deepseek-reasoner`\n- **TQ4 quantization tiers** — `nano` (6GB, TurboQuant 4-bit), `standard-tq4` (12GB, both models TQ4), `hybrid-tq4` (6GB + cloud reasoning)\n- **`adk backend` CLI** — `list`, `set`, `set-reasoning`, `test` subcommands\n- **`adk quickstart`** — unified first-run wizard (setup + auth + shell)\n- **`adk tools`** — list local + MCP tools with tier markers\n- **`adk backup`** — tarball export of `~/.aither/`\n- **`adk ingest`** — feed docs/files into knowledge graph\n- **`adk setup nemotron`** shortcut — alias for `--tier lite`\n- **`--reasoning-api`** flag in setup — `anthropic`, `openai`, `deepseek`, `gateway`\n- **`--dgx-spark URL`** flag — remote vLLM endpoint configuration\n- **DGX Spark auto-detection** — scans `spark.local`, `192.168.0.33`, `AITHER_DGX_URL` env\n- **Post-setup smoke test** — sends real inference request after container startup\n- **Schema migration system** — version table + migration runner in `memory.py` and `graph_memory.py`\n- **Configurable vLLM port scan** — `AITHER_VLLM_PORTS` env var for custom ports\n- **`aithershell` entry point** — alias for `adk` in pyproject.toml\n- Shell auto-downloads binary on first `adk shell` (no `--install` needed)\n- Shell pre-flight check warns if no backend detected\n- Shell passes full config via env vars (API key, tenant, inference URL)\n- DGX/Remote and Cloud API checks added to `adk doctor`\n- Config fields: `reasoning_backend`, `reasoning_api_key`, `deepseek_api_key`, `dgx_url`, `vllm_extra_ports`\n\n### Fixed\n- `test_aeon_chat_basic` timeout — mock scope was exiting before request\n- `adk doctor` check_disk timeout — cap rglob at 5000 files\n\n## [1.1.7] - 2026-05-19\n\n### Added\n- Remote agent pipeline — `adk onboard` → fleet dispatch → remote inference\n- Agent-to-agent federation with mesh relay\n- `adk connect` for desktop mesh enrollment\n\n## [1.1.6] - 2026-05-19\n\n### Fixed\n- Tool call execution recovery across all backends\n- Hermes tool call fallback parsing for vLLM\n\n## [1.1.5] - 2026-05-19\n\n### Fixed\n- Tool call execution + Hermes fallback parsing improvements\n\n## [1.1.0] - 2026-05-18\n\n### Added\n- Multi-channel gateway (Telegram, Discord, Slack, webhook)\n- Aeon group chat with 7 presets\n- Skills auto-extraction from multi-step sessions\n- MCP stdio server for Claude Code integration\n- `adk gateway`, `adk cron`, `adk skills`, `adk soul` commands\n- SOUL.md import/export for portable agent identity\n- Elysium cloud device-flow authentication\n\n## [1.0.0] - 2026-05-18\n\n### Breaking\n- Package consolidated — `aither_adk` namespace replaced by `adk` + `adk.platform`\n- 93 duplicate files removed from `AitherOS/packages/aither_adk/`\n\n### Added\n- Standalone core primitives (`adk.core`: Agent, Tool, Memory, ModelBackend, Capability, Trace)\n- `adk.platform` sub-package merging all platform toolkit modules\n- Compatibility shims for legacy `from aither_adk.*` imports\n\n### Changed\n- Version synced across pyproject.toml, `adk/__init__.py`, `adk/platform/__init__.py`, npm package\n- Classifier promoted from Alpha to Beta\n\n## [0.16.0] - 2026-04-16\n\n### Swarm Coding Engine & Repowise Integration\n- **`repowise_search` tool** — Semantic + keyword hybrid code search via Repowise, with ripgrep fallback\n- **`swarm_code` tool** — Dispatch complex tasks to 11-agent swarm pipeline (ARCHITECT->SWARM->REVIEW->JUDGE)\n- **`agent.swarm()`** — Async convenience method with configurable mode, effort, timeout\n- **`agent.code_search()`** — Async convenience method returning structured results\n- New tool categories: `repowise` and `swarm` in TOOL_CATEGORIES\n- `repowise_search` and `swarm_code` added to `__init__.py` exports with lazy loading\n- `IDENTITY_DEFAULTS` updated: repowise for code-focused agents, swarm for orchestration agents\n- Standalone graceful degradation: repowise falls back to ripgrep, swarm returns structured error\n\n## [0.13.0] - 2026-04-02\n\n### Graph Faculties — Local Knowledge for Every Agent\n- **CodeGraph** (2,799 lines) — Full Python AST indexer with call graph, keyword/semantic/hybrid query, embedding matrix cache, incremental re-indexing, multi-hop chain expansion\n- **MemoryGraph** (1,339 lines) — Graph-based persistent agent memory with 10 edge types, hybrid query (keyword + semantic + graph expansion), multi-hop recall, pickle persistence\n- **EmbeddingProvider** — 4-backend fallback chain: sentence-transformers (GPU/CPU) -> Ollama -> Elysium cloud -> feature hashing (zero deps)\n- **BaseFacultyGraph** — Abstract base with HMAC-SHA256 validated pickle persistence\n\n### Agent Integration\n- `agent.set_code_graph(cg)` — auto-registers `code_search` + `code_context` tools\n- `agent.set_memory_graph(mg)` — auto-registers `remember` + `recall` + `memory_stats` tools\n- Both graphs inject context into LLM prompts automatically during chat\n\n### Zero-Config Onboarding\n- `adk start` / `adk` (no args) — auto-detect project, index code, detect LLM, persistent memory, interactive chat\n- Works for any directory: Python codebases, doc folders, mixed workspaces\n- Auto-detects LLM: Ollama -> vLLM -> Elysium -> OpenAI -> Anthropic\n- Per-project persistent memory in `~/.aither/memory/<project>`\n- `adk index <path>` — standalone indexing with progress bar and stats\n\n### MCP Gateway\n- `POST /v1/embeddings` — OpenAI-compatible embedding proxy to vLLM-embeddings:8209\n- Available to all tiers (embeddings are free)\n- ADK EmbeddingProvider uses this as Elysium cloud fallback\n\n### Optional Dependencies\n- `pip install aither-adk[graphs]` — numpy for 10x cosine similarity speedup\n- `pip install aither-adk[embedding]` — sentence-transformers + torch for local GPU embeddings\n- `pip install aither-adk` alone — graphs work with feature hashing (zero deps)\n\n## [0.12.0] - 2026-04-01\n\n### Bootstrap & Service Discovery\n- Version handshake -- ADK checks major.minor compatibility with Genesis/Node\n- Background reconnect loop -- ServiceBridge re-probes every 30s in standalone mode\n- Port 8080/8090 documented -- MCP vs OpenAI-compat clearly separated\n- Genesis URL configurable via GENESIS_URL env var (was hardcoded)\n- Standalone mode warning -- visible stderr alert when AitherOS not detected\n- Auto-reconnect on startup when services come online\n\n### Elysium Cloud Inference\n- Unified gateway URL -- gateway.aitherium.com handles auth + billing + inference\n- Streaming inference -- SSE passthrough for /v1/chat/completions with stream=true\n- Auth proxy routes -- /v1/auth/register, /v1/auth/login, /v1/auth/me\n- Billing proxy -- /v1/billing/balance through gateway\n- AitherConnect Elysium fallback -- cloud inference when local Genesis is down\n- AitherDesktop Elysium fallback -- third-tier chat fallback after Node\n\n### Infrastructure\n- /discovery endpoint on Genesis -- unified service URLs/versions/health\n- /api/config/services on Veil -- runtime port config for client-side JS\n- Veil healthcheck gates on Genesis -- unhealthy when backend is down\n- Desktop crash detection 90s->60s (threshold 3->2)\n\n## [0.11.0] - 2026-04-01\n\n### Agent Execution Quality (Claude Code Parity)\n- Raise loop guard block threshold from 3 to 4 -- agents get more room for iterative search\n- Soft synthesis nudge for effort >= 4 (no tool stripping, trust the model)\n- max_output_tokens escalation -- retry up to 3x when response is truncated\n- Tool result pairing guarantee -- synthetic error for orphaned tool_use blocks\n- Micro-compaction of old tool results -- save context tokens on long sessions\n- First-turn tool forcing only for effort >= 6 (trust model for lower effort)\n- Diminishing returns detection -- nudge agent when 3+ turns produce < 500 tokens\n- Message normalization -- merge consecutive same-role messages, strip empties\n- LLM retry with exponential backoff (5 retries, 500ms-16s, jitter)\n\n## [0.9.0] - 2026-03-16\n\nThe \"connected world\" release. Cross-platform identity pairing, voice capabilities, and multi-channel integration.\n\n### Added\n- **Pairing**: Cross-platform identity linking (`adk/pairing.py`)\n - `PairingManager` — SQLite-backed identity linking with 6-char pairing codes\n - Link users across Telegram, Discord, Slack, WhatsApp with 10min TTL codes\n - Canonical session IDs for cross-channel conversation continuity\n - `get_session_id()` returns \"user-{id}\" for paired users\n- **Voice**: Speech-to-text and text-to-speech client (`adk/voice.py`)\n - `VoiceClient` — async STT/TTS/emotion via AitherVoice service\n - Convenience functions: `hear()`, `say()`, `feel()`\n - 6 voice options: alloy, echo, fable, nova, onyx, shimmer\n - Emotion detection with intensity scoring\n- New exports: `PairingManager`, `PairingResult`, `PlatformIdentity`, `VoiceClient`, `TranscriptionResult`, `SynthesisResult`, `EmotionResult`\n\n### Changed\n- `__init__.py` exports expanded with pairing and voice symbols\n\n## [0.6.0] - 2026-03-13\n\nThe \"group mind\" release. Multi-agent group chat, creative tools, and Iris identity.\n\n### Added\n- **Aeon**: Multi-agent group chat engine (`adk/aeon.py`)\n - `AeonSession` — persistent group chat with parallel agent execution\n - 7 presets: balanced, creative, technical, security, minimal, duo_code, research\n - Orchestrator synthesis: Aither summarizes all agent responses\n - Serial execution for Ollama, parallel for vLLM/cloud\n - ConversationStore persistence with `type: \"aeon\"` metadata\n - `group_chat()` one-shot convenience function\n- `aither aeon` CLI command — interactive terminal group chat with color-coded agents\n - `-p/--preset`, `-a/--agents`, `-r/--rounds`, `--no-synthesize` flags\n - `reset` and `quit` commands\n- Server endpoints: `POST /aeon/chat`, `GET /aeon/presets`, `GET /aeon/sessions/{id}`\n- Creative tools in builtin_tools: `image_generate`, `image_refine`, `image_search`, `video_generate`\n- Iris agent identity with visual generation capabilities\n- 48 Aeon tests (data models, presets, chat, context, persistence, server, exports)\n\n### Changed\n- `__init__.py` exports: `AeonSession`, `AeonResponse`, `AeonMessage`, `group_chat`, `AEON_PRESETS`\n\n## [0.5.0] - 2026-03-13\n\nThe \"tenant-ready\" release. Multi-tenant admin, permission grants, safety profiles, and a full setup wizard.\n\n### Added\n- `aither setup` interactive setup wizard with hardware detection, model selection, identity config\n- Strata storage backend: SQLite WAL persistence for conversations, memories, knowledge graphs\n- CLI test runner: `aither test` with auto-discovery and parallel execution\n- Permission grants system for MCP tool access control\n- MCP account management tools\n- LLM provider auto-detection for Ollama and vLLM endpoints\n- Apache-2.0 LICENSE file\n- Elysium desktop sync module\n- Comprehensive CLI test suite (493 tests)\n- Strata storage test suite (972 tests)\n- LLM provider tests (82 tests)\n\n### Changed\n- CLI expanded: `aither setup`, `aither test`, `aither bugreport`, `aither doctor`\n- Strata module rewritten as full local-first storage engine\n- Server startup includes Strata initialization\n- README rewritten with clearer quickstart and architecture docs\n\n### Fixed\n- Dead documentation links\n- Python 3.11 compatibility issues\n- Ruff per-file-ignores configuration\n- Identity provisioning edge cases\n- Docker node-gyp native dependency builds (python3 + build tools)\n\n## [0.4.0] - 2026-03-13\n\nThe \"own your AI\" release. Self-hosted agent OS for people who don't want their data on someone else's servers.\n\n### Added\n- Muse agent identity (creative/artistic generation)\n- Port 8120 to vLLM scan for ExoNodes discovery\n- Public roadmap with milestone-based porting schedule\n- Competitive positioning: self-hosted alternative to cloud-locked AI appliances\n\n### Changed\n- LLM router: compute-aware effort routing (replaces effort-level context gating)\n- README rewritten for sovereignty-first messaging\n- Package description updated\n- Promoted from alpha to stable release\n\n### Fixed\n- Version string consistency between pyproject.toml and __init__.py\n- vLLM port scanning now includes port 8120 (ExoNodes)\n\n## [0.3.1] - 2026-03-09\n\n### Added\n- GraphMemory: SQLite knowledge graph with Ollama embeddings and hybrid search\n- NeuronPool: auto-fire pre-LLM data gathering agents\n- NanoGPT: pure Python char-level transformer with LoRA fine-tuning\n- Safety gates: IntakeGuard (input), LoopGuard (recursion), Sandbox (code exec)\n- Event system: EventEmitter with chat/tool/forge event types\n- Builtin tools: identity-based tool selection\n- ServiceBridge: auto-discovery of AitherOS services\n- Streaming: chat_stream with safety gate integration\n- Auth middleware: Bearer token authentication\n- CLI: `aither init` and `aither serve` commands\n\n### Changed\n- Wired 5 previously disconnected modules into agent loop\n- 522 total passing tests (up from 85)\n\n## [0.3.0] - 2026-03-07\n\n### Added\n- Clean-room agent development kit\n- Multi-backend LLM providers (Ollama, OpenAI, Anthropic)\n- AitherAgent class with @tool decorator\n- SQLite conversation memory\n- OpenAI-compatible server (`aither-serve`)\n- 16 agent identities as package data\n- MCP bridge to mcp.aitherium.com\n- Privacy-centric opt-in telemetry\n- Bug reporting CLI and API\n- FastAPI server with OpenAI-compatible endpoints\n- Hardware auto-detection (5 tiers, 11 profiles)\n- Fleet orchestration and multi-agent coordination\n- A2A mesh protocol\n- Federation protocol for cross-instance agent dispatch\n\n### Initial release\n- 85 passing tests\n- Apache-2.0 license\n"}, "5e9d5e2d65c0ee4f27eb495c7d1a24f5264ad47e": {"t": 1785748159.2256687, "n": 93661, "h": "64369957afc131ac", "c": "# Changelog\n\nAll notable changes to aither-adk will be documented in this file.\n\n## [2.37.0] - 2026-07-24\n\n### Added — Agent Onboarding Fabric: bring your own agent, or run a managed one\n\nOnboard an agent built on **any** framework, or let Aitherium run it for you.\n\n- **ACP (Agent Client Protocol), both directions** — `ACPClient` drives any ACP agent;\n `ACPServer`/`serve_stdio` exposes an adk agent to any ACP host (Zed, VS Code,\n JetBrains). Both proven live against Zed's reference `agent-client-protocol`.\n- **Universal Agent Pack** — `AgentPackManifest` + `Supervisor` describe and run an\n external agent across 6 frameworks × 6 protocols, fail-closed on anything unknown.\n- **Protocol drivers** — `get_driver()` returns an `http`, `langgraph_rest`, `a2a`, or\n `mcp` driver, so REST/graph-orchestrated agents can be *managed*, not just connected.\n- **Pack registry** — `PackRegistry.publish/browse/versions/get/yank/verify_digest`,\n SHA256-digested, semver-ordered, validation fail-closed before anything is written.\n- **Managed identity** — `ManagedAgentIdentityProvider` with a real gateway minter and a\n PROVISIONED→REGISTERED→ACTIVE→ROTATED→REVOKED lifecycle; default-deny `authorize()`.\n- **Zero-code connect templates** — `render_connect(framework, …)` emits config pinned to\n each framework's real schema.\n\n### Added — NOOA-style agent capabilities\n\n- **`CodeActLoop`** — code as action: the model writes Python cells that run against a\n persistent namespace over live objects, each cell AST-validated before execution, with\n per-cell timeouts and bounded observations.\n- **`ObjectRegistry` / `render_observation`** — pass by reference: large values become\n handles instead of flooding the context with serialized text.\n- **`EventLog` / `ContextBlocks`** — harness APIs the model itself can call to query its\n own event history and manage named context blocks in KV-cache-stable order.\n- **`has_ellipsis_body` / `@strategy`** — NOOA's `async def f(...) -> X: ...` ergonomic.\n- **`CodeValidator`** — best-effort AST validation (ported from NVIDIA's OO-Agents,\n Apache-2.0; NOTICE included). A defense-in-depth layer, not a sandbox boundary.\n- **`PredictLoop`** — single-shot strategy with structured output (`output_model=`) and\n validation retry; `Strategy` is now a first-class alias of `AgentLoop`, and both loops\n accept a per-call `model=` override.\n- **Typed I/O** — `AgentResult` validates its fields at construction and at the\n `Agent.run` boundary.\n\nAll new capabilities are exported lazily from the top-level `adk` namespace.\n\n## [2.27.0] - 2026-07-18\n\n### Added — Moonshot Kimi K3 provider (DeepSeek-equivalent cloud backend)\n\n- **New `moonshot` provider** across the LLM router: OpenAI-compatible\n `https://api.moonshot.ai/v1`, default model `kimi-k3` (131K context default, up to 1M;\n tool calls, JSON mode, structured output). Wired everywhere `deepseek` is:\n - `LLMRouter(provider=\"moonshot\")` + `_COMPAT_URLS`/`_COMPAT_MODELS`/model tiers\n (small/medium/large → `kimi-k3`)\n - Config: `moonshot_api_key` field (env `MOONSHOT_API_KEY`), saved-config load,\n provider-keys map (`provider_keys.json` key `moonshot`)\n - Auto-detect chain: tried after DeepSeek (config key, then env key)\n - Hybrid reasoning backend: `AITHER_REASONING_BACKEND=moonshot` for effort 7+\n - Reasoning tiers: `backend: moonshot` (alias `kimi`) via new `MoonshotBackend`\n - CLI: `adk backend set moonshot --api-key …`, `adk backend list` shows moonshot\n- No breaking changes; DeepSeek and all other providers unchanged.\n\n## [2.26.0] - 2026-07-16\n\n### Added — Aeon group-chat web UI pack + POST /aeon/stream SSE endpoint\n\n- **New `aeon` UI pack** (`adk/webui/packs/aeon/index.html`) — group chat for multi-agent\n discussion. Mirrors the `llamacpp` pack's auth + SSE pattern (bearer from URL fragment #k=),\n with a preset selector (balanced/creative/technical/security/minimal/duo_code/research) and\n distinct colored agent messages. Synthesis block highlighted. Agent content is HTML-escaped\n before render. Fully self-contained HTML+CSS+JS, light+dark, no external assets. (No\n temperature/max-tokens controls — Aeon runs a fixed multi-agent round and doesn't thread\n per-turn generation params, so exposing them would be a no-op.)\n- **POST /aeon/stream SSE endpoint** — accepts body {message, preset?, agents?, rounds?,\n session_id?}, emits SSE with: `session_start` (participants + orchestrator), `agent_message`\n per non-orchestrator, `synthesis` (if enabled), `error` on exception, `complete` (total tokens\n + latency). Mirrors /chat/stream structure — like every streaming endpoint here it is gated by\n the server auth middleware **when `AITHER_SERVER_API_KEY` is set** (a local `adk up` with no key\n is unauthenticated by design, same as /chat/stream).\n- **Default UI pack flipped to `llamacpp`** — `resolve_ui_pack_name()` now returns `llamacpp`\n instead of `console` when no env/config is set (new users see the clean chat, not the admin\n SPA; existing users with AITHER_AGENT_UI or saved config unaffected).\n\n### Added — swappable agent web UI packs (llama.cpp-style built-in chat)\n\n- The page an agent serves at `/` is now a **swappable UI pack**, so you can\n drop in, test, and deploy different chat frontends without touching the agent.\n Select with `adk ui set <name>` (persists to `~/.aither/config.json`) or the\n `$AITHER_AGENT_UI` env; `adk ui ls` lists packs, `adk ui path` shows what\n resolves.\n- **New built-in `llamacpp` pack** — a clean, self-contained llama.cpp-style\n chat UI (centered conversation, streaming token-by-token with a cursor, a\n Stop button, a settings drawer for temperature / max-tokens that is actually\n honored end-to-end — `/chat/stream` now forwards them to the provider —\n minimal safe markdown, light+dark). Hits the agent's own gated `/chat/stream`\n with the bearer read from the URL fragment (never sent in the URL). Built-ins\n also include `console` (the full admin SPA, still the default) and `minimal`.\n- **Drop-in custom packs**: a UI pack is just a folder with an `index.html` in\n `~/.aither/ui-packs/<name>/` (override via `$AITHER_UI_PACKS_DIR`) — no rebuild.\n- Resolution is **fail-soft**: an unknown/missing pack falls back\n console → minimal, so `/` is never blank.\n\n### Added — MCP-gateway-first platform access + mesh A2A trust\n\n- **Gateway-first platform access** (`adk/client/_gateway_mcp.py`, `GatewayMCPClient`):\n a self-hosted agent connects its MCP **client** to `mcp.aitherium.com` authenticated\n as the OWNER (device-flow / PAT / ACTA token), RBAC-scoped to that identity — so it can\n use platform tools + `get_secret` **whether or not the node runs local AitherOS\n services**. Fail-closed: no token → client disabled, the agent still runs. Wired into\n the server lifespan (`_connect_gateway_mcp`).\n- **`genesis` LLM backend**: `adk backend set genesis` points the agent brain at a local\n Genesis (`http://localhost:8001/v1`, model `workflow`) — the 5090/controller pattern\n where inference is served by the local fleet, not the cloud gateway or a raw vLLM.\n- **`adk mesh` command**: `adk mesh onboard` drives the Conductor 5-step onboard\n (`/v1/mesh/onboard` → registers this node's public key, returns the `endpoint:mesh`\n trust token); `adk mesh ls` lists peer nodes/agents (AitherMesh `/nodes` + Directory)\n with their A2A cards + invoke URLs for discovery.\n\n### Security — A2A inter-agent trust enforcement (fail-closed)\n\n- **`adk/a2a_trust.py`** (new): Ed25519 signature verification + trusted-key check\n (`AITHER_A2A_TRUSTED_KEYS`), opt-in via `AITHER_A2A_REQUIRE_TRUST` (default `false` for\n backward compatibility; `audit` logs untrusted, `true` rejects).\n- **Fixed a fail-OPEN in the inbound A2A handler** (`adk/a2a.py`): the trust check\n previously ran *only when signature headers were present*, so an unsigned request\n bypassed `AITHER_A2A_REQUIRE_TRUST` entirely. Now genuinely fail-closed — required mode\n with no/invalid signature → `403`.\n- **Sovereign template no longer scaffolds `verify=False`**: the generated\n `services/llm.py` now uses the project's own `tls_verify()` policy helper for every\n provider call instead of hardcoding disabled TLS verification.\n- **`adk/_tls.py`**: disabling verification via `AITHER_TLS_VERIFY=false` now emits a\n one-time loud warning (was silent) — the escape hatch stays, the silence goes.\n\n## [2.25.1] - 2026-07-15\n\n### Fixed — self-hosted OpenAI-compatible backends honor a custom base URL\n\n- `adk backend set llamacpp --base-url http://localhost:8090/v1 --model <id>` (and\n `vllm`/`lmstudio`, and the `AITHER_LLM_BASE_URL` env) now actually point the agent\n at YOUR server. Previously the base URL was saved but never read, so an explicit\n `--backend llamacpp` fell back to the provider's public API default (openai.com).\n New `Config.llm_base_url` field + saved-config `inference_url` wiring; the explicit\n backend path in `LLMRouter` now passes it for the local OpenAI-compatible family.\n This is what lets a self-hosted node run its OWN local model (e.g. a PrismML\n Bonsai llama.cpp) as the agent brain instead of ollama/cloud.\n\n## [2.25.0] - 2026-07-15\n\n### Added — node_bootstrap tool pack: bootstrap LLM inference on any hardware\n\n- **New bundled tool pack `node-bootstrap`** (`adk/toolpacks/node_bootstrap/`):\n seven agent tools — `node_detect_hardware`, `node_resolve_recipe`,\n `node_plan_deployment`, `node_apply`, `node_enroll`, `node_register_backend`,\n `node_verify` — plus a CLI shim (`python -m adk.toolpacks.node_bootstrap\n detect|resolve|plan|apply|enroll|register|verify`). Detect a box's hardware,\n resolve a deployment recipe, actually deploy the inference engine (docker\n compose / native / delegate), enroll with a control plane, register the\n backend, and live-verify a completion.\n- **9 hardware recipes** shipped in the wheel: `cpu-1bit-llamacpp`, `cpu-ollama`,\n `cuda-vllm-8gb/24gb/40gb`, `cuda-dual-stack-32gb` (32GB-card co-resident\n stack), `unified-memory-vllm` (DGX/Grace-class), `metal-ollama` (native —\n Docker on macOS has no Metal passthrough), `cloud-api` fallback. Resolution\n scores hardware against min/max VRAM bands, RAM, cores and unified memory;\n ties prefer higher tiers and self-contained recipes over fleet delegates.\n- Security posture: enrollment and backend registration are fail-closed\n (missing token/URL → error dict, never anonymous, never `verify=False`);\n tokens are redacted in outputs; all endpoints come from args/env\n (`AITHER_CONTROL_PLANE_URL`, `AITHER_GENESIS_URL`, `AITHER_AUTH_TOKEN`).\n\n## [2.24.0] - 2026-07-14\n\n### Changed — sync-family consolidation\n\n- **Removed dead code:** `adk/sync.py` (528 lines) deleted — it was unreachable,\n shadowed by the `adk/sync/` package (`import adk.sync` always resolved to the\n package; nothing loaded the module by path — verified).\n- **Grouped the sync domain modules into the `adk.sync` package:**\n `brain_sync`→`adk.sync.brain`, `files_sync`→`adk.sync.files`,\n `lockbox_sync`→`adk.sync.lockbox`, `secrets_sync`→`adk.sync.secrets`,\n `session_sync`→`adk.sync.sessions`, `settings_sync`→`adk.sync.settings`,\n `sync_watermark`→`adk.sync.watermark`. All in-tree importers updated. No\n back-compat shims were left because **zero external/monorepo code imported the\n old root paths** (verified) — a clean move, not a shimmed one. **Breaking only\n for code that imported `adk.<name>_sync` directly** (none known).\n\n### Docs — MCP module layering clarified\n\n- `adk.mcp` (enterprise AitherOS gateway client: auth/billing/token-tracking) and\n `adk.core.mcp` (minimal generic JSON-RPC adapter for any MCP server) are\n DISTINCT layers, not duplicates — each now carries a LAYER/ROLE/See-also\n docstring so they are not mistakenly consolidated.\n\n## [2.23.2] - 2026-07-14\n\n### Fixed — CompletionGate no longer false-fails on correct results\n\n- **Tolerant judge JSON extraction.** The LLM judge parsed its reply with `json.loads(text[first\"{\":last\"}\"])`, which raised `JSONDecodeError` (\"Extra data\") whenever the model appended prose after the verdict or emitted a second object — and `verify()` turned that into a fail-closed verdict, so a genuinely completed task reported `unverified`. New `_first_json_object()` uses `JSONDecoder.raw_decode` from the first `{` that begins a valid object and ignores trailing content (prose, markdown fences, extra objects). A judge that answers with no parseable JSON still fails closed (never a soft pass), now with a clear reason. The same extractor backs `_derive_criteria`.\n- **Bare code-like tokens are hard-checked.** A natural criterion like `output contains ADK_AUTO_OK` (no quotes) previously fell through to the judge; `hard_checks` now also matches bare identifier/code shapes (contains an underscore, or 4+ CAPS/digit chars), with a stopword guard so a descriptive uppercase word (`OUTPUT`, `CONTAIN`) never becomes a false required token. Quote a token to force it regardless.\n- +9 tests (24 total), including the exact trailing-data regression.\n\n## [2.23.0] - 2026-07-14\n\n### Removed — dead modules and internal integrations\n\n- **`adk.swarm`, `adk.provisioning_tools`, `adk.session_sync_integration`, `adk.addon_metering`** — four orphan modules with zero public usage have been deleted. These were internal prototypes kept for legacy monorepo consumers; public packages have not imported them since 2.21.0 (tracked as D-348). **Breaking for anyone directly importing these modules** (import will fail). They are no longer exempt from the orphan-module check.\n- **`adk.aither_bridge`** — the AitherOS-internal IRC ↔ chat gateway bridge is no longer shipped in the public aither-adk package (stripped at sync time). All public importers already degrade behind `except ImportError` (in `adk.server` and `adk.builtin_tools`), so public packages remain unaffected. The module stays in the private AitherOS monorepo for internal use.\n- **Identity document trim** — removed internal agent names from `adk/identities/aither.yaml` delegation guidance, replacing with role-generic wording (\"delegate code review, refactoring, security analysis, performance and testing to specialist agents when available\"). Effort-tier routing philosophy remains unchanged and is product design, not a leak.\n\n### Changed — genericized customer/product names in help text\n\n- CLI help/examples no longer name specific customer deployments (`--scope` template list and `deploy agent` name example use generic placeholders). Functional deploy paths are unchanged.\n\n## [2.22.1] - 2026-07-14\n\n### Fixed — real bugs surfaced by reviving the public CI (red since Jul 5)\n\n- **TLS**: bundled toolpacks (arc-brainpack, cloudflare) disabled certificate\n verification on gateway/vault calls; all now route `adk._tls.tls_verify()`\n (verified by default, internal CA bundle when present).\n- **`system_prompt` contract**: bundled-pack `[PACK DIRECTIVES]` were appended\n even to an explicit `system_prompt`; an explicit prompt now wins wholesale.\n- **Headless secrets**: `_derive_key()` used `os.getlogin()`, which raises\n `OSError` without a controlling terminal (CI, daemons, containers) — falls\n back to `getpass.getuser()` (same value on interactive boxes, so existing\n secret files keep decrypting).\n- **`pack_verifier`**: missing `cryptography` crashed with `UnboundLocalError`\n in the exception handler instead of failing closed; now degrades gracefully\n (and `cryptography` was added to the dev extra so CI tests the real path).\n- **`fleet_manager._kill_pid` (POSIX)**: reported success immediately after an\n asynchronous SIGTERM and never reaped children (zombies read as alive); now\n polls, escalates to SIGKILL, and reaps.\n- **Identity**: `aither` regained the `creative` tool category (dropped\n accidentally in a 2.7.0 WIP sweep).\n\n## [2.22.0] - 2026-07-14\n\n### Changed — internal toolkit relocation\n\n- **`adk.platform` relocated** — the internal aither-platform toolkit (ComfyUI/image-gen\n pipelines, fleet agent clients, parallel CLI/UI/tools stack) has been moved out of the\n adk SDK tree to the standalone internal `aither-platform` package at\n `AitherOS/agents/aither_platform/`. The public package remains unaffected (it already\n excluded `adk.platform` since 2.21.1). Internal AitherOS agent builds now install\n `aither-platform` separately. `adk platform` falls through to the same \"not available\"\n message on public installs; internal builds work via the relocated package.\n\n### Removed — backward-compat re-export deleted\n\n- **`adk.gateway` module removed** — the backward-compatibility re-export of `GatewayClient`\n from `adk.gateway` has been removed. Use `from adk.client import GatewayClient` instead\n (the established public API since 2.21.0). All internal importers have been updated.\n\n## [2.21.1] - 2026-07-14\n\n### Removed — internal toolkit and license-gated product no longer ship publicly\n\n- **`adk.platform`** — the internal aither-platform toolkit (ComfyUI/image-gen\n pipelines, fleet agent clients, a parallel cli/ui/tools stack) is no longer\n included in the public repo or the PyPI wheel. It was internal-only code that\n had been riding along since the aither-platform merge. `adk platform` now\n reports \"Platform toolkit not available\" on public installs; internal AitherOS\n builds are unaffected.\n- **`adk.formbridge`** — the FormBridge form-automation product is license-gated\n (PROFESSIONAL+); its implementation no longer ships in the free package. All\n import sites (`builtin_tools`, `server` route mount) already degrade behind\n `except ImportError`, so nothing else changes behavior.\n- Product test suites (`test_formbridge.py`, `test_encounter_keying.py`,\n `test_flow_tools.py`) are excluded from the public sync alongside them.\n\n## [2.21.0] - 2026-07-14\n\n### Added — Completion gate: verified-or-retried task execution\n\n- **`adk.gate.CompletionGate`** — a composable wrapper that closes the \"silent\n success\" gap: an agent's ReAct loop returns whatever the model produced and\n calls it done (`finish_reason = stop/max_steps/length`), which is a *liveness*\n signal, not a *completion* one — so an agent can \"succeed\" while doing nothing.\n `CompletionGate` runs the agent, verifies the result against acceptance\n criteria, retries with the failure fed back, and returns the response with\n `finish_reason` set to `verified` or an honest `unverified`. Never a false pass.\n - **Hard-first, un-soft-passable checks** (`adk.gate.hard_checks`): a filesystem\n path a criterion says must exist is checked on disk; a required quoted token\n must appear in the output. Only genuinely subjective criteria fall through to\n an optional LLM judge (reuses the agent's own model), which **fails closed**\n if unavailable.\n - **`gated_run(agent, task, criteria=..., max_retries=2)`** — one-shot helper.\n - `auto_criteria` (LLM writes the machine-checkable definition-of-done from the\n task), a `verifier=` override for custom checks (tests-pass / HTTP probe), and\n a `requires_action` short-circuit so it never fights human-approval pauses.\n - Compose it onto any agent exposing `run(task) -> resp{.content, .finish_reason}`\n (`AitherAgent`, `core.Agent`, or a thin adapter over a remote dispatch).\n - Exported from the top-level package: `from adk import CompletionGate, gated_run`.\n\n## [2.19.0] - 2026-07-12\n\n### Added — Eve agent importer + durable A2A tasks + canonical agent discovery\n\n- **`adk pack import <eve_agent_path>`** — convert external Eve agents to AitherADK packs\n (from GitHub or local filesystem). Maps Eve structure (instructions → system prompt,\n agent.ts → agent.yaml, skills/*.md → skills, connections/*.ts → MCP, tools/*.ts →\n tools/node verbatim) to AitherADK format with a single command. Eve agents (compiled\n manifest format, schema v35) become installable packs compatible with `adk pack install`.\n- **Durable A2A task lifecycle (`TaskManager` backed by FileStore)** — agent-to-agent\n tasks now survive container restarts. Tasks persist as append-only JSONL\n (`.adk/tasks.jsonl`); in-memory dict for fast access; `create_task()` / `update_status()` /\n `add_artifact()` / `add_message()` all save to disk. Google A2A v0.3.0 spec compliant.\n- **Canonical agent card discovery** — `A2AServer.mount()` now exposes:\n - `GET /.well-known/agent-card.json` — Agent Card (canonical; contains agent identity,\n capabilities, skills, endpoints)\n - `GET /.well-known/agent.json` — Agent Card (legacy, redirects with 308 to canonical path)\n - `POST /a2a` — JSON-RPC 2.0 for message send/task lifecycle\n - `GET /a2a/tasks/{id}/subscribe` — SSE streaming task updates\n- **Four built-in agent packs now complete and catalogued**:\n - `hermes` — architecture review & trade-off analysis\n - `openclaw` — web research & source verification\n - `claude-code` — feature development & debugging\n - `analyst` — structured data inference & anomaly detection\n All discoverable via `adk packs` / `adk install pack:<name>`.\n- **Tolerant tool-call JSON parsing** — malformed tool JSON (extra properties, missing\n name/arguments, string-encoded nested args, missing keys) is now gracefully degraded\n instead of silently failing. `_parse_tool_json()` accepts both `\"arguments\"` and\n `\"parameters\"` keys, coerces string-encoded args, and skips unparseable calls instead\n of crashing the turn.\n\n### Known limitations\n\n- Eve agent importer: remote GitHub fetching not yet implemented (local paths only).\n- Durable task store: currently JSONL (suitable for thousands of records, not millions).\n\n## [2.18.0] - 2026-07-10\n\n### Added — AitherShell command center + Claude Code session management\n\n- **`aither sessions`** — Claude Code session manager: interactive full-screen\n browser (type-to-filter, transcript preview, deep full-text `search`, resume\n **in the current terminal** or as Windows Terminal tabs), crash guard\n (`guard install` = at-logon watchdog that snapshots live sessions and\n auto-restores the whole set after a terminal crash), and `ingest [--watch]`\n (incremental, secret-guarded sync of session conversations into the local KB\n / CompanyBrain via the standard ingest pipeline).\n- **`aither hq`** — command-center dashboard: per-service fleet health, LLM\n queue (depth/VRAM/models), Pulse alerts, live session count, inbox unread —\n auto-refreshing, with one-key jumps into chat, sessions, inbox, agents,\n brief, watchtower, and docker recovery.\n- **`aither inbox`** — unified queue over CommCore mail, Relay\n mentions/notifications, and Pulse alerts; open/mark-read/DM-reply from the\n terminal. Every source degrades independently.\n- **`aither agents`** — roster console + `agents ask -e <1-10> <agent> <q>`\n (effort-tiered ask via Genesis, with automatic `/chat/stream` fallback when\n the `/agent/sync` execute gate rejects host callers) + forge dispatch\n inspection + routine health.\n- **`aither palette`** — universal fuzzy picker across actions, sessions, and\n services; **`aither brief`** — renders Atlas's executive briefing in the\n terminal; **`aither watch`** — fleet watchtower with named wedge-signature\n detection (docker-WSL wedge, LLM-queue stall, crash-loop uptime regression)\n and optional docker auto-recovery.\n- All fleet reads go through a shared fail-soft `FleetClient` (internal-CA\n TLS via `adk._tls`, dashboard-grade timeouts, per-source degraded states).\n- Node shell-cli (`@aitherium/shell-cli` 1.13.0): `aither\n sessions|hq|inbox|palette|brief|watch|agents|docker` now pass through to the\n Python shell instead of being intent-classified as chat prompts.\n\n## [2.17.1] - 2026-07-05\n\n### Fully self-service mesh join — no customer key handling\n\n- **Auto-join headscale off the onboard response.** The conductor now auto-issues a\n headscale pre-auth key for NAT'd nodes and returns it in the `/v1/mesh/onboard`\n response; `mesh.join` consumes it and brings up the tunnel automatically. A customer\n runs one command — they never mint, fetch, or set a headscale key. Explicit\n `--headscale-key`/`AITHER_HEADSCALE_AUTH_KEY` still take precedence.\n- **Security:** `_tailscale_up` now redacts the auth key from all logs and exceptions\n (a failed `tailscale up` previously echoed the full argv, leaking the key into logs).\n\n## [2.17.0] - 2026-07-05\n\n### Added — Headscale mesh transport + local Qdrant + AitherConnect onboarding\n\n- **Headscale NAT-friendly mesh transport.** When raw WireGuard UDP:51820 is not\n viable (customer boxes behind NAT, corporate firewalls, CGNAT), `adk mesh join\n --headscale` routes the mesh tunnel through Tailscale's Headscale control plane.\n The overlay IP is still Conductor-assigned (10.77.0.0/16); Headscale provides\n only the transport layer. Configured via `AITHER_MESH_TRANSPORT=headscale`,\n `AITHER_HEADSCALE_URL` (default: https://headscale.aitherium.com), and\n `AITHER_HEADSCALE_AUTH_KEY` (pre-generated key). Automatic fallback to raw\n WireGuard if Headscale setup fails.\n- **Local Qdrant vector DB provisioning.** `adk stack` now provisions Qdrant\n locally when `AITHER_VECTOR_DB=qdrant` is set, enabling offline-first RAG\n without external dependencies. Tiered: local Qdrant in-fleet, cloud Nexus\n for enterprise multi-tenant deployments.\n- **Conductor URL public fallback.** `adk mesh join` resolves the internal\n `aitheros-conductor:8193` hostname; if unreachable (e.g., self-hosted nodes),\n it falls back to the public `conductor.aitherium.com:8193` endpoint,\n eliminating bootstrap configuration friction.\n\n### Documentation\n\n- **Self-hosting runbook.** New `docs/SELF_HOSTING.md` covers deploying AitherOS\n on customer infrastructure with Qdrant + mesh + AitherConnect onboarding.\n- **Mesh transport constraints documented.** Raw WireGuard requires public\n UDP:51820 endpoint (cloud instances, dedicated hardware); Headscale for\n NAT'd networks. Noted in CLI help and module docstrings.\n\n## [2.15.0] - 2026-07-04\n\n### Added — built-in web admin console + portal-profile settings sync\n\n- **Web admin console.** The page `adk up` opens (served at `/` and `/ui`) is now a full,\n self-contained console — Chat plus six admin tabs: **Backend** (view/switch LLM provider,\n set keys, test connection), **Packs** (list/enable/disable/reload tool packs),\n **Sessions** (browse/delete conversations), **Logs** (redacted agent-log tail),\n **Graph** (knowledge-graph search/neighborhood/stats), and **MCP** (add/remove external\n MCP servers). No build step, no CDN — a single packaged HTML asset. The original minimal\n streaming chat page stays at `/chat` for back-compat.\n- **Admin API (`adk/admin_api.py`).** ~20 endpoints under `/admin/*`, all bearer-gated by the\n existing middleware (never in `_skip_auth_paths`). Operates on the live agent — LLM backend\n swap (`LLMRouter.switch_backend`), pack reload, and MCP registration take effect without a\n restart.\n- **Settings sync (`adk/settings_sync.py`).** The user's portal profile is the source of\n truth: on startup the agent pulls `preferences.adk` and applies it over the local\n `~/.aither/config.yaml` cache; admin-console edits push a debounced snapshot back to\n `PUT /api/settings/preferences`. Provider API keys and MCP auth headers are device-local\n secrets and are never included in the pushed snapshot. Opt out with `AITHER_SETTINGS_SYNC=false`.\n\n### Security\n\n- **Timing-safe token comparison.** The server bearer check now uses `hmac.compare_digest`\n (the token is reachable over the public tunnel; a byte-wise `!=` leaked it to timing attacks).\n- **MCP add is SSRF-guarded + confirmed.** Adding an external MCP server is a two-step\n prepare→confirm flow; URLs resolving to loopback / private / link-local / metadata addresses\n are rejected (override with `AITHER_ALLOW_PRIVATE_MCP=1` for trusted local servers).\n- **Config editor is allowlisted.** `PATCH /admin/config` writes only safe scalar prefs and\n hard-denies fields that could execute code or redirect traffic at startup (e.g.\n `aither_toolpack_dirs`, backend base URLs).\n- **Log tail is scrubbed.** Bearer tokens, `#k=` fragments, and `sk-`/`aither_sk_` keys are\n redacted from the log-tail endpoint; the tunnel log is excluded.\n\n## [2.14.8] - 2026-07-04\n\n### Added — `adk up` for normal humans (no API key, no prereqs, live chat)\n\n- **Hosted-brain default — no API key required.** When there's no local model and no\n provider key, `adk up` signs in (device-flow) and routes inference through the gateway\n using the portal token (`AITHER_LLM_BACKEND=gateway` + bearer). A non-technical user\n never needs to obtain or paste an LLM API key. Explicit `--provider`/local model still\n take precedence; unattended `--yes` with no token/backend fails cleanly (exit 3).\n- **cloudflared auto-download.** If the tunnel binary is missing, `adk up` downloads the\n right platform asset to `~/.aither/bin` (SHA256-verified against the release; opt out with\n `AITHER_CLOUDFLARED_REQUIRE_CHECKSUM=0`) instead of exiting with an install hint.\n- **Built-in streaming chat page.** The agent now serves a self-contained chat UI at `/`\n (and `/chat`) with a live \"thinking…\" indicator and token-by-token streaming — so a human\n has somewhere to talk to it with immediate feedback instead of a blank wait. `adk up` opens\n it locally and passes the callback bearer in the URL fragment (`#k=…`), so `/chat/stream`\n stays authenticated (not an open proxy) with nothing to paste.\n- **Human-readable failures.** `adk up` errors now carry a plain-English `next_action`\n (kept machine-readable in `--yes` JSON).\n\n### Fixed\n\n- `adk up` chat link pointed at a non-existent `/settings/agent` route; it now points at the\n agent's own streaming chat page.\n\n## [2.14.7] - 2026-07-04\n\n### Added\n\n- **`adk up` — one command to run a persistent, fleet-connected agent.** Collapses\n the scattered onboarding surface (`host`/`quickstart`/`connect`/`login`/`setup`)\n into a single command that starts `aither-serve` (single agent, identity `aither`\n by default), opens a Cloudflare quick-tunnel for fleet reachability, registers with\n the portal, and installs a reboot-persistent autostart entry (Windows Task Scheduler\n / systemd-user / launchd). Detaches by default so the terminal is freed; `--foreground`\n blocks. Reuses the existing device-flow login, cloudflared discovery, provider-key\n store, and `_preflight_check` backend detection.\n- **Fully non-interactive path for autonomous agents.** `adk up --yes` (or any\n non-TTY invocation) takes ZERO prompts — everything resolves from flags, env, and\n saved config — and emits a single machine-readable JSON status line with meaningful\n exit codes (`0` ok, `2` bad-input, `3` no-backend, `4` tunnel-missing, `5` register-failed).\n Degrades to local-only (warn, not fail) when a portal token is absent unless\n `--require-register` is set.\n- **`adk down` / `adk status --json` companions.** `down` stops the agent + tunnel,\n best-effort deregisters, and removes the autostart entry; `status` reports the running\n agent (liveness + `/health` + tunnel + registration), with `--json` for agents/CI.\n- **`adk stack`** — the previous `adk up` behaviour (supervise the Room + Ollama consumer\n stack) now lives here; `adk up` is the connected-agent command.\n\n### Internal\n\n- New `adk/agent_daemon.py`: detached process spawn, status file\n (`~/.aither/adk-up.json`), pid liveness/teardown, and cross-platform autostart install.\n\n## [2.14.6] - 2026-07-04\n\n### Added\n\n- **`adk.preflight` — self-bootstrap capability probe.** `run_preflight(agent,\n spec)` wakes an agent up knowing what it is actually plugged into: a bounded\n (1.5s) parallel `LivenessProbe` over the router's resolved backends\n (primary/reasoning), the embeddings winner, remote MCP tools, the structured-ML\n / `/ml/teach` endpoints, and voice — returning an honest `CapabilityReport`.\n Status vocabulary distinguishes reachability from entitlement\n (`OK`/`MISSING`/`UNREACHABLE`/`AUTH`/`TIER_DENIED`/`UNSUPPORTED`), a hosted slot\n never prints a bare `OK`, and a required-but-unsatisfied slot aborts before the\n loop instead of blind-firing. Headless task/rules resolution + an allowed-roots\n policy binding (`builtin_tools.set_allowed_roots`, which resets the memoized\n cache so the override actually enforces).\n- **Multimodal (image) messages.** `Message.content` now accepts an OpenAI-style\n content-part list (`str | list[dict]`), passed through verbatim to the\n OpenAI-compatible body (gateway / vLLM / OpenAI). New `adk.llm.multimodal`\n helpers — `image_message()`, `image_content_parts()`, `to_image_url()` — build\n the parts and encode local image bytes/paths into `data:` URIs.\n- **Real vision preflight probe.** The vision slot is no longer unconditionally\n `UNSUPPORTED`: it sends a discriminable image (a solid red square) and requires\n the model to name the color, so a blind/text-only model cannot false-pass. `OK`\n only on a real round-trip; `AUTH` on 401/403; `UNSUPPORTED` when there is no\n OpenAI-compatible provider or the model cannot see the image.\n\n## [2.14.5] - 2026-07-04\n\n### Added\n\n- **`adk.reasoning.mcts` — generic Monte-Carlo Tree Search library.** A reusable\n MCTS engine (ported from the internal `UnifiedMCTS`, with all internal couplings\n removed) that any agent can drive over its own environment:\n - `MCTSEnvironment` protocol (`get_state_hash` / `get_actions` / `step` /\n `evaluate` / `clone`) — implement it and search.\n - Three **optional, default-off** model seams — `TransitionModel`, `PolicyModel`\n (PUCT priors), `ValueModel` — so a learned world-model / policy / value can be\n injected without touching the core; with all three `None` the engine is a plain\n UCT search.\n - `ObservedTransitionModel` adapter — a cheap online lookup world-model (hit =\n exact stored transition; miss = identity + negative \"unknown\" reward +\n `is_uncertain`, biasing exploration toward learning), with JSONL save/load for\n cross-run reuse.\n - `UnifiedMCTS(config).search(env)` returns an `MCTSResult` (best action, value,\n confidence, principal-variation path) and an optional `MCTSTrace`\n (visit-distribution policy target) for distillation.\n- **Self-bootstrapping agent machinery.** Build an agent from a declarative spec\n instead of bespoke wiring:\n - `adk.bootstrap.build_agent_from_spec(path) -> (AitherAgent, RunCtx)` — resolves\n prompts, activates required/optional tool packs, and carries a `memory_map`\n (a `node_type -> (role, tier)` override for memory writes, never a gate).\n - `adk.prompts.PromptBridge` — resolves an agent's prompt bindings from files\n (relative to the spec) or `AitherPrompts` dot-keys (optional import).\n - `adk.packs.PackActivator` — eager activation of installed tool packs by name\n over `register_tool_packs`; a required pack that registers zero tools raises\n `PackUnavailable`, optional packs soft-degrade.\n - `python -m adk.bootstrap.generic_agent <spec.yaml>` inspects a spec (resolved\n name, activated packs + tool counts, prompt keys, memory map) without running a\n live loop.\n- **`deep_research` tool pack** — a full multi-engine web-research workflow as\n `dr_*` agent tools (keyless search, page fetch + cache, site mirror, a citable\n findings knowledge graph, and markdown/PDF/DOCX report generation), activatable\n by name via the tool-pack loader.\n\n## [2.14.4] - 2026-07-04\n\n### Added\n\n- **`structured_ml` tool domain** — zero-shot structured-data inference for agents,\n backed by TabFM (tabular classification/regression) and TimesFM (time-series\n forecasting). Three tools:\n - `tabular_classify(support_rows, target, query_rows)` — classify rows from a\n labeled support set, in-context (no training step; up to 10 classes).\n - `tabular_regress(support_rows, target, query_rows)` — predict a numeric target.\n - `timeseries_forecast(series, horizon)` — forecast future values of a series.\n\n The tools POST to a structured-ML inference service resolved from\n `AITHER_STRUCTURED_ML_URL` (default `http://localhost:8192`), reject non-http(s)\n URLs, and summarise oversized responses to protect the agent's context.\n- **`Capability.STRUCTURED_INFERENCE`** capability token and a new **`analyst`**\n identity/pack that ships the `structured_ml` domain by default. The domain is\n opt-in — only identities that enable the `structured_ml` category get the tools.\n\n## [2.14.3] - 2026-07-03\n\n### Fixed\n\nAll four bugs below were found by actually booting `adk run` against a real\nchat model and driving a real tool-calling request end-to-end (not inferred\nfrom reading the code) — each one silently broke the \"local agent uses its\nconfigured model and its tools actually work\" path.\n\n- **`AITHER_MODEL` / `config.model` silently ignored by the OpenAI-compatible\n backend**: `LLMRouter.__init__` never read `config.model` into `self._model`\n unless the caller passed `model=` directly to the constructor — which nothing\n did. Every explicit model choice (env var, config file) was dropped, the\n provider fell back to a hardcoded per-backend default (`gpt-4o-mini` for the\n `openai` backend), and an unrecognized model name got silently remapped\n server-side to the wrong backing model. Concretely: setting\n `AITHER_MODEL=aither-orchestrator` had no effect — every request actually\n went to a different model that was never meant to be called directly and\n can't reliably use tools.\n- **`OPENAI_BASE_URL` ignored for explicit `--backend`/`AITHER_LLM_BACKEND`\n selection**: `LLMRouter._auto_detect()`'s explicit-backend branch hardcoded\n `base_url=None` when constructing the provider, instead of reading\n `config.openai_base_url` (which two other call sites in the same file\n already did correctly). Any self-hosted OpenAI-compatible endpoint\n (MicroScheduler, vLLM, LM Studio) configured via `OPENAI_BASE_URL` was\n silently ignored in favor of `https://api.openai.com/v1`.\n- **Tool-call steering never engaged on default-effort chat**: the retry that\n nudges a model back into structured tool-calling when it responds with prose\n instead of a function call was gated behind `effort >= 6`. Default `effort`\n for a plain `agent.chat(message)` call resolves to `5`, so the safety net —\n built specifically for this failure mode — never fired for default requests.\n Also now forces `tool_choice=\"required\"` on the retry instead of relying on a\n text nudge alone.\n- **Every non-string builtin-tool parameter silently typed as `\"string\"` in\n the generated tool schema**: `builtin_tools.py` uses\n `from __future__ import annotations`, so `_extract_parameters()` in\n `tools.py` was reading postponed (string) type hints (`\"int\"`, not `int`)\n straight out of `fn.__annotations__`, which never matched\n `_type_to_schema`'s type-object keys and silently fell through to\n `{\"type\": \"string\"}`. Every builtin tool with an `int`/`float`/`bool`/`list`\n parameter was affected — e.g. `file_read(start_line: int, end_line: int)`\n advertised both as strings, so a model correctly calling the tool with\n `\"start_line\": \"0\"` crashed the actual implementation with\n `unsupported operand type(s) for -: 'str' and 'int'`. Fixed by resolving\n hints through `typing.get_type_hints()` instead of the raw annotations dict.\n `_coerce_arguments()` — the runtime safety net that's supposed to coerce a\n model-supplied `\"0\"` string into `0` before a tool call executes — had the\n exact same raw-`__annotations__` bug and silently no-opped for every builtin\n tool; fixed the same way.\n- **Streaming tool-loop could hang a connection forever**: `chat_stream()`'s\n fallback to the sync tool loop (tool calls can't stream token-by-token) had\n no timeout, so a model that didn't cleanly terminate a tool-calling round\n trip left the SSE connection open indefinitely with zero bytes sent. Bounded\n with a 60s `asyncio.wait_for`; on timeout the stream yields a clear message\n instead of hanging.\n\n## [2.14.2] - 2026-07-03\n\n### Fixed\n- **AitherShell session amnesia**: the REPL generated a fresh `session_id` for\n EVERY message, so conversation history never followed across turns (the\n server stored each turn under a different session). One stable id is now\n minted per shell run; `/resume` and `/new` semantics unchanged, and the\n cross-restart auto-restore of the last session now actually works.\n\n## [2.14.1] - 2026-07-02\n\n### Added\n- **Account-tied licensing via device-flow login** — `adk login` now persists the account\n license returned by the device-flow handshake to `~/.aither/license.json` (base64\n `license_key` → `{payload, signature}` envelope), so entitlements from the authenticated\n account carry into every later `adk` invocation without re-auth. Pairs with the fleet-side\n node tool-pack gate that grants packs by plan-tier rank.\n- **`can_use_formbridge` / `can_use_untether` entitlements** (PROFESSIONAL+ rank ≥ 3) backing\n the FormBridge and UNTETHER agent tool-packs.\n\n### Fixed\n- `adk` CLI: guard the optional `memory_graph` singleton (was a latent `NameError` on the\n `/stats`, `/memory`, and save paths when memory was not constructed).\n\n## [2.14.0] - 2026-07-02\n\n### Added\n- **`adk.memory_wiki` — LyraWiki-style self-managed semantic knowledge** (opt-in; nothing constructs it by default): `MemoryWiki(graph_memory)` maintains curated wiki ARTICLES as `wiki_article` nodes (title/slug, LLM-owned markdown with `[[wikilinks]]`, embedding, durable tier, `CONSOLIDATED_FROM` edges to source memories, governance-ledgered revisions).\n - `consolidate(llm, since=, budget=)` clusters unconsolidated episodic/fact nodes (embedding + keyword), drafts/UPDATEs articles via the INJECTED llm callable (`str -> str` or `messages -> str`, sync or async — no hard model dependency), cites source node ids, marks contradicted sources superseded, then DEMOTES consolidated sources to a fast-decay tier via `promote()` (represented knowledge doesn't need raw copies).\n - `lint(llm=None)` — deterministic health checks (orphan wikilinks, empty/stale articles, live-but-contradicted pairs) + an optional LLM audit pass.\n - `prune(relevance_floor=0.05, hard_delete_after_days=14)` — article relevance = tier freshness × reinforcement (reinforce-on-recall) + link-degree to LIVE memories; below the floor → reversible governance tombstone; entombed past the window → **HARD-DELETE** (the tombstone snapshot itself is purged — content and embedding irrecoverable; the true-deletion path). A node is never deleted without a tombstone first.\n - `recall(query, limit)` — article-first RAG (curated articles rank above raw nodes) that reinforces returned articles.\n- **`adk.routines` — agent heartbeat + self-programmed routines**: `RoutineStore` is a durable registry (`~/.aither/routines/{agent}.json`, `AITHER_DATA_DIR`-aware, injectable path) of cron-scheduled self-prompts `{name, cron, instruction, enabled, last_run, last_result, tags}` that rehydrates into the existing `adk.cron.CronScheduler` on `start()`. A fire runs `agent.chat(instruction)` with the agent's full toolset — the agent programs its future self with its own adk. Guardrails: `max_routines` (12), a 5-minute min fire interval per routine, a per-fire timeout, and every result ledgered (truncated) to `last_result`. Direct-callable routines (`register_direct`) fire a bound method instead of a self-prompt. Self-management tools (`routine_create/list/update/pause/resume/delete/run_now`) ship as OpenAI tool defs + handlers (`build_routine_tools` / `register_routine_tools` / `routine_tool_defs`); the handlers ONLY touch the RoutineStore — they can never modify agent config or safety-relevant settings (the leash principle).\n- **`AitherAgent(routines=True, memory_maintenance=True)`** (both default **False** → the default agent is byte-identical): `routines=True` builds the RoutineStore + scheduler, registers the self-management tools, and starts the heartbeat lazily on the first `chat()` (or via explicit `start_routines()`). `memory_maintenance=True` registers DEFAULT routines — `wiki_consolidate` (every 2 h), `wiki_lint` / `wiki_prune` / `graph_sweep` (daily) — as DIRECT method fires so memory upkeep runs even on tiny models, while staying visible/manageable via `routine_list`.\n- `TombstoneStore.purge(tombstone_id)` — the hard-delete endpoint: irrecoverably removes a tombstone snapshot and rewrites the persisted store without it (consumed by `memory_wiki.prune`).\n\n## [2.13.6] - 2026-07-02\n\n### Added\n- **Qdrant backend for GraphMemory dataplane sync — TRUE two-way, verified live.** Set `AITHER_FLEET_QDRANT_URL` and the sync uses Qdrant (`upsert` + `scroll` + filtered `search`) instead of Nexus. This is what actually delivers cross-agent memory sharing: a node is upserted with its OWN embedding under a deterministic per-(tenant,node) point id (idempotent re-push), and `fleet_pull` reliably SCROLLS a tenant's points back — the enumerate Nexus lacks (its `/search` doesn't return ingested docs and `/export` needs lancedb, which has a hard dependency conflict in the fleet). **Proven live with 2 agents:** agent-1 ingests → auto-syncs to the tenant dataplane; a fresh agent-2 in the same tenant `fleet_pull`s all of agent-1's nodes and can search them; a different tenant pulls nothing (isolation). Dimension-safe (never mixes vector dims in a collection). Local SQLite stays source of truth; best-effort, never raises.\n\n## [2.13.5] - 2026-07-02\n\n### Fixed\n- **CRITICAL — GraphMemory dataplane sync push was 100% failing.** `_fleet_push_node` sent `source_type=\"graph\"` / `content_type=\"graph_node\"`, which AitherNexus rejects as invalid enums (HTTP 500) — so NO graph node ever replicated in 2.13.4. Now sends the accepted `source_type=\"manual\"` / `content_type=\"text\"` (graph provenance is carried in `metadata.synced_from`). **Verified live** against a fleet Nexus: a 4-node ingest replicates with `pending==0` and the collection count reflects it.\n\n### Added\n- **Swarm-awareness notification** — on a successful sync batch, `GraphMemory` emits a best-effort `graph.synced` event (tenant/workspace/agent/collection/count) to `AITHER_FLEET_EVENTS_URL` so OTHER agents in the tenant/swarm can pull the fresh data and deconflict in-flight work (push-based awareness instead of polling). No-op unless the env is set.\n\n### Known limitation\n- Cross-agent **rehydration** (`fleet_pull`) is only reliable when the fleet Nexus is backed by lancedb (persistent + exportable). An in-memory Nexus stores on `/ingest` but neither its `/search` nor `/export` returns the docs — two-way read needs the persistent Nexus.\n\n## [2.13.4] - 2026-07-02\n\n### Added\n- **Memory-maintenance primitives on `GraphMemory`** (all opt-in; default behaviour byte-identical):\n - **Reinforce-on-recall**: `recall_with_activation(..., reinforce=True)` bumps `reinforcement_count`/`last_reinforced` on every RETURNED node's metadata mirror (one UPDATE per node, WAL-safe) — making the `MemoryRecord` reinforcement fields the activation scorer reads LIVE. Default `False` = zero writes.\n - **`sweep(now, archive_below=0.05, max_nodes=None, dry_run=False)`**: read-time decay enforced at rest — archives nodes past their tier TTL with freshness×reinforcement score below `archive_below` and `reinforcement_count <= 1`; `max_nodes` additionally archives the lowest-scored overflow. PERMANENT tier immune. Every archive is a reversible governance tombstone (`TombstoneStore.recover`) + a FORGET ledger entry; a node is never deleted without a tombstone. `dry_run` returns the would-archive list without acting.\n - **`promote(node_id, tier=?, role=?)`**: re-tier/re-role a node IN PLACE (tier/role columns + metadata mirror) — edges, embedding and ledger history survive; ledgers an UPDATE entry when governance is on.\n- `MutationType.UPDATE` ledger entry kind (in-place re-tier/re-role).\n- **Per-tenant dataplane sync for `GraphMemory`** — replicate the local graph to a per-tenant vector dataplane (Nexus/Qdrant) so a sovereign agent's memory is durable + rehydratable across restarts and hosting boundaries. `fleet_push_all_nodes()` (catch-up), `fleet_pull()` (best-effort semantic top-up), `fleet_sync_pending()`, and auto-sync on ingest (`_maybe_autosync` + `drain_sync()`). **On by default** (`AITHER_FLEET_SYNC=auto`; only `false/0/off` disables) with an inferred target (local Nexus `:8122` or the gateway in cloud mode) and a SEPARATE collection (`AITHER_FLEET_GRAPH_COLLECTION`, default `graph_memory`) from the KV memory sync. New `GraphMemory(tenant_id=, workspace_id=, fleet_url=, auto_sync=)` constructor args override env (required for multi-tenant host processes). Node `synced` column + schema v4 migration. TLS verify is CA-bundle-aware (`AITHER_CA_BUNDLE`) for the internal-TLS mesh. Local SQLite stays the source of truth; every sync path is best-effort and never raises into ingest.\n\n## [2.13.3] - 2026-07-02\n\n### Added\n- **Canonical self-deploying embeddings provider** (`adk.embeddings`): one embedding provider for the whole SDK so vectors are portable across scopes (platform / tenant / sovereign) — same model + dimension everywhere. Pinned to `nomic-embed-text` (768-d; the vLLM `--served-model-name` for nomic-embed-text-v1.5). Lazy, single-flight resolution chain: explicit `AITHER_EMBEDDINGS_URL` → local vLLM (`:8209` then `:8120`, HTTPS-then-HTTP) → local Ollama → gateway (`AITHER_GATEWAY_EMBEDDINGS_URL`) → auto-deploy a local vLLM embeddings container if a GPU + Docker are present (opt out with `AITHER_EMBED_AUTODEPLOY=0`) → CPU sentence-transformers (384-d, degraded) → feature-hash (384-d, degraded, always works). Every batch is dimension-tagged so callers never silently mix 768-d and 384-d vectors. `get_default_embedder()`, `embed_texts()`, `embed_one()`, `get_provider()`, `reset_provider()`.\n\n### Changed\n- **`GraphMemory` now defaults its embedder to the canonical provider** (opt out with `AITHER_GRAPH_EMBEDDER=legacy`), so every adk agent shares one 768-d space. Added a meta-table dimension guard: the index is pinned to its first embedding's dimension and later different-dimension vectors are refused (kills 768↔384 mixing that silently poisons cosine similarity).\n\n## [2.13.2] - 2026-07-01\n\n### Added\n- **Session persistence**: AitherShell now auto-saves the active session_id to `~/.aither/config.yaml` after each chat turn and auto-restores it on the next shell launch. Multi-turn conversations survive across restarts — the messages were always persisted in SQLite + JSON, only the session pointer was missing.\n- `/new` command to start a fresh session (clears saved session_id).\n- `/sessions` command to list recent sessions with agent name, message count, and timestamp. Resume any with `/resume <id>`.\n\n## [2.13.1] - 2026-07-01\n\n### Fixed\n- **CRITICAL:** `deploy_connect()` now sends a HEAD request to verify the GitHub release asset exists before downloading, with a clear error if the asset is missing.\n- **CRITICAL:** Compose file URLs are now pinned to a stable commit hash (configurable via `AITHER_COMPOSE_PIN` env) instead of tracking the mutable `main` branch.\n- **CRITICAL:** `deploy_adk_node()` now falls back to starting `adk-serve` natively when Docker is unavailable, instead of blocking with \"Docker not installed\".\n- **HIGH:** GHCR login failure now blocks deployment immediately with actionable guidance, instead of warning and continuing to a confusing image-pull failure.\n- **HIGH:** Health checks now stream the last 30 lines of Docker Compose logs on failure, so the user can see why a container didn't start.\n- **HIGH:** vLLM port allocation now probes for conflicts before assigning; if port 8200 is busy, workers are automatically remapped to the next free port.\n- **MEDIUM:** Compose file downloads are now cached with ETag; re-running `adk deploy node` skips re-downloading an unchanged file.\n- **MEDIUM:** DGX Spark discovery now respects `AITHER_DGX_HOSTS` env (comma-separated) for custom network topologies, instead of only checking `spark.local` and `192.168.0.33`.\n- **MEDIUM:** Cloud API keys (Anthropic, OpenAI, DeepSeek) are now validated with a lightweight probe during `adk setup` infra scan, so invalid keys surface early.\n- **MEDIUM:** `deploy_connect()` now validates the extracted extension has a `manifest.json` and warns if it's missing.\n\n## [2.13.0] - 2026-07-01\n\n### Removed\n- **`adk.faculties.memory_graph`** — the pickle-based `MemoryGraph` module is deleted. `from adk.faculties import MemoryGraph` now resolves to the canonical `adk.graph_memory.GraphMemory` for back-compat.\n- **`adk.platform.memory`** — the entire legacy memory subsystem (`MemoryManager`, `UnifiedMemorySystem`, `GameEngine`, `StoryboardEngine`, `AnchorGenerator`) is deleted. Agent memory is `adk.memory.Memory` + `adk.graph_memory.GraphMemory`.\n\n### Changed\n- `adk work` command now uses `GraphMemory` instead of the removed pickle-based faculty graph.\n- `adk.MemoryGraph` and `adk.faculties.MemoryGraph` lazy exports now resolve to `adk.graph_memory.GraphMemory`.\n- `agent.set_memory_graph()` kept for back-compat but documented as deprecated; agents already wire `GraphMemory` automatically in `__init__`.\n- Platform startup (`adk.platform.infrastructure.startup`) gracefully handles missing `MemoryManager`.\n- Platform CLI module listing and health check updated to reflect removed memory module.\n\n## [2.12.7] - 2026-07-01\n\n### Deprecated\n- `adk.faculties.MemoryGraph` — emits `DeprecationWarning` on instantiation; use `adk.graph_memory.GraphMemory` (SQLite-backed, governed, embedding-native) instead.\n- `adk.platform.memory` package — the legacy `MemoryManager`, `UnifiedMemorySystem`, `GameEngine`, and `StoryboardEngine` modules now emit `DeprecationWarning` on import; use `adk.memory.Memory` and `adk.graph_memory.GraphMemory` for new code.\n\n### Changed\n- Updated `adk.faculties` docstring and usage examples to point at the canonical memory surface.\n\n## [2.12.6] - 2026-07-01\n\n### Changed\n- Trimmed public package debt by removing grid runbook artifacts and shell/room binary placeholder directories from published artifacts.\n- Removed stale public-sync allowlist entries for deleted phase test files and added explicit public-sync guards for binary placeholder directories and grid artifacts.\n- Updated the public README grid note so it no longer links to runbook files excluded from the SDK package.\n\n## [2.12.5] - 2026-07-01\n\n### Added\n- Added best-effort fleet memory sync from local ADK memory into Nexus/Qdrant-compatible RAG stores, including batch catch-up and vector-search recall hooks.\n- Added saved config/env support for fleet memory URL, collection, and sync mode.\n\n### Changed\n- Excluded FormBridge runtime/test internals from public ADK packaging and public repo sync.\n\n## [2.12.4] - 2026-07-01\n\n### Added\n- Added a hot-reload endpoint for installed agent packs so newly applied pack tools can be picked up without restarting `aither-serve`.\n\n### Fixed\n- Fixed synchronous `ChatRelay` calls on Python 3.12 when no event loop is running; async WebSocket delivery is still scheduled when a loop exists.\n- Aligned gateway inference tier tests with the current public ladder: free, starter, pro, enterprise, platform.\n\n## [2.12.0] - 2026-06-28\n\n### Added — one-command local inference + upgrade path (click-to-run)\n- **`adk quickstart-local`** — detects hardware, picks a backend\n (Ollama / llama.cpp / vLLM), installs it, downloads a model, verifies, and\n prints next steps. Bootstraps Ollama itself via winget/brew/install.sh when\n missing (no \"go install it\" dead-ends). Default model `gemma4:e2b`.\n- **`adk backend switch <ollama|llamacpp|vllm>`** (+ `backend status`) — migrate\n the active deployment between engines; re-points config and smoke-tests.\n- **`adk install pack:<name>`** (+ `adk install list` / `adk packs`) — install\n ready-made agent packs (openclaw, hermes, claude-code) into `~/.aither/agents`.\n\n### Fixed (found via live end-to-end testing on Windows + CUDA)\n- quickstart-local reported success on a dead endpoint (smoke test was warn-only);\n a failed smoke now fails the command, with a service-readiness wait.\n- `--model` resolved to `None` (getattr default never fired) → uses the real default.\n- Smoke test: 30s→180s timeout for cold model load; accepts reasoning-model output\n (empty `content` at low token budgets is no longer a false failure).\n- `adk install pack:<name>` colon syntax (argparse rejected it before dispatch).\n- `adk backend status` health probe used a nonexistent `/health` path → 404; now\n probes `/v1/models` (uniform OpenAI-compatible liveness for all three backends).\n- Windows: removed non-ASCII glyphs from printed strings that crashed the cp1252\n console (a `✓` even made a successful install report failure).\n\n## [2.11.1] - 2026-06-14\n\n### Fixed — `adk host` / `adk login` device flow (autonomous onboarding)\nFound driving `adk host` against production; all broke real device-flow login:\n- **Device flow hit the portal FRONTEND, not AitherIdentity.** `adk login`/`adk host`\n POSTed `/auth/device/code` to portal/veil.aitherium.com, which 307-redirects API auth\n to `/login`. Added `_resolve_identity_url()` mapping the aitherium.com topology to\n `idp.aitherium.com` (the Identity API); localhost/bespoke pass through unchanged.\n- **Cloudflare 403'd urllib.** `idp.*` sits behind Cloudflare, which blocks urllib's\n default `Python-urllib/3.x` UA. Device-code + poll requests now send a real\n User-Agent + Accept (verified urllib→idp 200, was 403).\n- **`adk host` now self-authenticates via device flow** when there's no token, and\n **retries via device flow on a 401/403** (a stale saved token no longer dead-ends\n registration).\n\n## [2.11.0] - 2026-06-14\n\n### Added — attach your own MCP \"hands\" to a self-hosted agent\nA self-hosted agent now has the full brain (your model) · body (this loop) · **hands**\n(your tools) trifecta. The AitherOS platform relays the MCP servers you registered for\nyour agent in the `/stream` request body as `mcp_endpoints`, and the loop wires their\ntools into the ReAct turn:\n- **`adk/mcp_endpoint_tools.register_mcp_endpoint_tools(agent, endpoints)`** — speaks MCP\n JSON-RPC (`tools/list` to discover, `tools/call` to invoke) and registers a namespaced\n proxy tool (`{endpoint}__{tool}`) for each advertised tool. Mirrors `app_proxy_tools`.\n Best-effort: an unreachable endpoint registers nothing and never raises.\n- **`POST /stream` and `POST /chat/stream`** accept an optional `mcp_endpoints` list\n (`[{name, url, [headers]}]`); `_aitheros_stream` attaches them to the agent before the\n turn. Existing callers are unaffected (the field is optional).\n\n## [2.10.1] - 2026-06-14\n\n### Fixed — self-hosted `aither-serve` was crash-on-startup (2.10.0 regression)\nA fresh `pip install aither-adk && aither-serve --backend deepseek` could not boot. Found\nand fixed while standing up a real self-hosted DeepSeek agent end-to-end:\n- **Startup `NameError: name 'port' is not defined`** in three lifespan helpers\n (`_join_aithernet`, `_init_a2a_server`, `_start_mesh_hosting`) — they referenced a bare\n `port` no longer in scope. Now use `config.server_port`, and `main()` writes the resolved\n `--port`/`--host` back to `config` so every lifespan helper and the invoke_url see the\n actual bound port.\n- **Fleet self-registration crashed the whole server** — the registration handlers caught\n `(ImportError, RuntimeError, OSError, ConnectionError)` but **not** `httpx.HTTPError`, so a\n `RemoteProtocolError`/connect error talking to the control plane took the agent down. All\n network-facing lifespan handlers now also catch `httpx.HTTPError` (non-fatal, as intended).\n- **`--backend` was ignored when `AITHER_API_KEY` was set** — `_auto_detect` picked the cloud\n gateway before the explicit backend, and `_try_elysium_fallback` then replaced the chosen\n provider. An explicit backend (deepseek/openai/anthropic/your own vllm/ollama) now always\n wins; \"auto\"/\"gateway\" still flow through detection. The operator's own brain is never\n silently hijacked.\n- **Approved-tool trace truncated after `/sessions/{id}/confirm`** — the SSE relay treated\n `AgentResponse.tool_calls_made` (a `list[str]`) as dicts (`tc.get(...)`), raised\n `AttributeError`, and an over-narrow `except` swallowed it so the client saw only\n `session_start` + heartbeats. The relay now handles string tool names, the `except` is\n broad (always emits a typed `error` + terminal `complete`), and the module-level\n `_strata_ingest` `NameError` + Chronicle `session_id` kwarg mismatch are fixed.\n\n## [2.10.0] - 2026-06-13\n\n### Added — human-in-the-loop tool approval (`adk/approval.py`)\n- **Pause before a gated tool, resume on decision.** An agent can now pause its turn before\n executing a sensitive tool and wait for a human's allow/deny — so a managed control plane\n (or any operator) can gate tool use without forking the loop.\n- **Policy via `AITHER_TOOL_APPROVAL`** — a comma-separated list of tool names (optionally\n `agent:tool`), or `*` for every tool. Empty = no gating (unchanged behavior).\n- **Survives restarts / days-later approvals.** The paused turn (user message + pending tool\n calls) is persisted to disk keyed by `session_id`; resume is a fresh request, not a held\n connection. Decisions are recorded per `(session_id, tool_name)`.\n- **`AgentResponse.requires_action` / `.pending`** — set when a turn paused; `pending` lists\n the gated tool calls awaiting a decision.\n- **`AitherAgent.resume(session_id, decisions)`** — records the decisions and re-runs the\n paused turn (the gate consumes them: allowed tools execute, denied tools get a denial\n observation, the turn continues — and may pause again on a different gated tool).\n- **Server:** `POST /sessions/{id}/confirm` records decisions and streams the resumed turn;\n `/stream` emits a `requires_action` event when a turn pauses.\n\n## [2.9.0] - 2026-06-13\n\n### Added — universal LLM continuation primitive (`adk/llm/continuation.py`)\n- **`run_continuation()` + `stitch()`** — one shared continue-until-complete primitive. When a\n completion stops because it hit the output token cap (`finish_reason == \"length\"`), it continues\n the generation and STITCHES the chunks (overlap-dedup + restart-detect) into a complete answer,\n instead of every call-site hand-rolling its own retry. Bounded three ways (max rounds AND total\n output ceiling AND a no-growth break), and it never mutates the caller's message history.\n- **`LLMProvider.chat_with_continuation()`** and **`LLMRouter.chat_with_continuation()`** — every\n provider AND the router inherit the primitive, so any call-site gets continuation for free.\n- Kill-switch `ADK_LLM_CONTINUATION=off`; tunables `ADK_LLM_MAX_CONTINUATIONS`,\n `ADK_LLM_MAX_TOTAL_OUTPUT_TOKENS` (defaults: 2 rounds / 8192 tokens).\n\n### Changed\n- The ReAct loop's truncation recovery now uses the shared primitive. The previous inline\n doubling-retry REPLACED the partial response (losing earlier text in `resp.content`) and injected\n continuation scaffolding into the live message history; it now STITCHES and leaves history clean.\n Tool-call turns are never continued as text.\n\n### Included — previously-committed-but-unreleased changes\n- 2.8.2: TLS verification on by default (`verify=False` removed).\n- 2.8.1: baked license + pack-signing public keys.\n- 2.8.0: version-bump release.\n\n## [2.7.0] - 2026-06-05\n\n### Added — provider-agnostic LLM cost levers (at the `LLMProvider` ABC)\n- **Prompt caching.** New `ProviderCapabilities` (`prompt_cache: explicit|automatic|none`, `batch`) and\n normalized `LLMResponse.cache_read_tokens`/`cache_write_tokens`, so the \"tokens saved\" meter never\n depends on which backend served the turn. Anthropic inserts `cache_control` breakpoints over the stable\n system+tools prefix; OpenAI/DeepSeek caching is automatic and their `cached_tokens` are now surfaced\n (previously discarded). Default-on in `AitherAgent.chat`; override with `cache=False`.\n- **ResponseCache** (`adk/llm/cache.py`) — exact-match response cache with a pluggable backend, wired into\n `LLMRouter` as an opt-in (`LLMRouter(response_cache=…)` + per-call `cacheable=True`). OFF by default — no\n determinism change unless opted in.\n- **Gemini provider** (`adk/llm/gemini.py`) with explicit `cachedContents` caching; registered in the router.\n- **Async batch runner** (`adk/llm/batch_runner.py`) — `run_batch()` over the Anthropic Message Batches /\n OpenAI Batch APIs (~50% off) with a transparent fallback to concurrent `chat()` where unsupported.\n- **Graph-RAG retriever interface** (`adk/graph_retriever.py`).\n\n### Added — principal authority (prompt-injection defense enforced outside the model)\n- `adk/auth.py`: `Principal` / `AuthContext` / `PrincipalResolver` (+ deny-all default).\n- `ToolRegistry.execute(name, args, auth=…)` — a default-deny authorization gate at the tool boundary, so a\n forbidden tool call is blocked regardless of what the LLM emits. `ToolDef` gains `required_clearance` /\n `action_class`. Threaded through `AitherAgent.chat`; `auth=None` is fully backward-compatible.\n- `adk/channel_auth.py`: Telegram webhook-secret + CEO-id checks and HMAC-signed approvals.\n\n## [2.6.4] - 2026-06-05\n\n### Fixes\n- **Loop-guard nudges corrupted the message history → DeepSeek 400 mid-run.**\n When the ReAct loop emitted multiple tool calls in one assistant turn and the\n loop guard fired a WARN/BLOCK/CIRCUIT_BREAK on one of them, it spliced a\n standalone `system` nudge message *between* the tool results. DeepSeek (and\n strict vLLM chat templates) require an assistant `tool_calls` message to be\n followed by exactly one `tool` result per `tool_call_id`, contiguously — so the\n interleaved message produced `400 … insufficient tool messages following\n tool_calls message` and aborted the turn (this is reliably hit after crossing\n the 15-call circuit-break threshold, where every subsequent call WARNs).\n Per-call guidance is now folded into that call's own `tool` result, and any\n standalone steering is deferred to a single message AFTER the whole batch —\n preserving the `tool_calls`↔`tool_results` pairing every backend requires.\n Regression test: `deep-research-agent/tests/test_msg_structure.py`\n (loopguard-flood scenario).\n\n## [2.6.3] - 2026-06-04\n\n### Fixes\n- **Transient provider errors killed a run on the first blip.** A single\n `502 Bad Gateway` / `503` / `529 overloaded` / `429` from the upstream API\n aborted the whole request — the Anthropic provider had **no retry at all**, and\n the OpenAI-compatible provider only retried `429`. Both now retry transient\n statuses (`408/429/500/502/503/504/529`) with `Retry-After`-aware exponential\n backoff (3 attempts), and surface the provider's body if all retries are\n exhausted. Non-transient `4xx` (e.g. `400`) still fails fast. Applies to\n `chat` and `chat_stream` on both providers.\n\n## [2.6.2] - 2026-06-04\n\n### Fixes\n- **DeepSeek (and strict OpenAI-compatible backends) 400'd on long tool\n conversations.** The ReAct loop legitimately injects mid-conversation\n `system` steering messages (the \"[DIMINISHING RETURNS]\" hint after several\n low-output tool iterations, loop-guard nudges). OpenAI tolerates a non-leading\n `system` message; DeepSeek rejects it with a 400. Long research runs\n (many `fetch_url` calls) reliably tripped this. Fix: non-leading `system`\n messages are demoted to `user` at the OpenAI-compatible provider boundary\n (`_demote_nonleading_system`) — steering intent preserved, payload valid on\n every backend; leading system prompt(s) untouched.\n- **Opaque HTTP errors.** `resp.raise_for_status()` discarded the provider's\n response body, so a 400/422 surfaced as a bare `Client error '400 Bad\n Request'` with no cause. `_ensure_ok` now includes the provider's actual\n message (e.g. `context_length_exceeded`, `Invalid 'messages'`) in the raised\n error, for both `chat` and `chat_stream`.\n\n## [2.6.1] - 2026-06-04\n\n### Fixes\n- **`MCPServer.mount()` returned HTTP 422 for every `/mcp` call.** Under\n `from __future__ import annotations` the route handler's `request: Request`\n annotation is a string that FastAPI resolves against the *module* globals, but\n `Request` was imported locally inside `mount()` — so FastAPI couldn't resolve\n it and treated `request` as a required query param. The MCP server endpoint\n (`initialize`, `tools/list`, `tools/call`) was unusable when mounted. FastAPI\n symbols are now imported at module level (guarded; the `[node]` extra still\n gates actual use). Any agent's tools can now be exposed as a working MCP\n server with `MCPServer(...).mount(app)`.\n\n## [2.6.0] - 2026-06-04\n\n### Streaming + scaffolding\n- **`AitherAgent.stream_chat`** — reliable streaming sibling of `chat()`: native\n tool-calling loop (concludes on its own, unlike the text-protocol `stream_react`)\n that emits live `tool`/`tool_result` events and streams the answer as `token`\n events. `token_delay` paces tokens for a typing effect.\n- **`adk new <template>`** — scaffold a full, runnable template app. Ships the\n `deep-research` template (a cited web-research agent: pack + server + web UI):\n `adk new deep-research`.\n\n### Fixes\n- **BYO-key no longer community-capped.** The free-tier monthly token cap now applies\n only to the metered Aitherium gateway backend, never to a user's own provider key\n (Anthropic/OpenAI/DeepSeek/Ollama/vLLM). Previously it could brick a self-hosted\n agent after ~100k tokens on the user's own key.\n- **Stronger, grounded memory recall.** `chat()` now injects the top-6 knowledge-graph\n hits (untruncated) with a hard no-fabrication instruction — was top-3 truncated to\n 200 chars, which surfaced partial facts and let weaker models invent the rest.\n- **Tool-arg coercion.** `ToolRegistry.execute` coerces LLM-supplied args toward each\n parameter's annotated type, so a tool no longer crashes when a model passes a list\n or string where an int/float/bool was expected.\n\n### Docs\n- New `docs/AGENT_DEV_GUIDE.md` — the golden path for building agents/packs.\n\n## [2.5.0] - 2026-06-04\n\n### Continual learning — agents that learn from runs and reuse memory\n\nAgents now improve the second time they do a task instead of starting from scratch.\n\n### Added\n- **Recall-before**: `AitherAgent.chat()` searches the local `SkillStore` and injects the\n top matching learned skills as a system block, so the model reuses proven procedures.\n- **Learn-after**: `_learn_after()` runs after each response — reinforces (`success_count++`)\n any skills that were recalled+reused, and extracts + saves a NEW skill from successful\n multi-tool runs via `SkillExtractor`. Wired at both `chat()` return paths.\n- Gated by `AITHER_SKILLS` (default on). The previously-unused `SkillStore`/`SkillExtractor`\n are now connected to the run loop.\n\n### Notes\n- Pairs with the platform-side LearnedProcedure substrate (recipes → scored procedures →\n skills/tools/A2A/packs → MCTS priors → evolution). The SDK shares memory via the Spirit\n bridge when configured.\n\n## [2.0.0] - 2026-06-02\n\n### Open-core boundary (BREAKING)\n\nThis release draws a single, enforced free/paid line. The free tier stays\ngenuinely useful (real agent, typed + graph + code memory, ReAct, ~essential\ntools); paid tiers unlock the *scale* capabilities via portal.aitherium.com.\n\n### Added\n- **`adk/licensing.py`** — the entitlement keystone. Resolves a tier\n (COMMUNITY by default), verifies optional portal-signed `~/.aither/license.json`\n (Ed25519), and answers `can_use_fleet/channels/cron/swarm/auto_neurons`,\n `is_agent_licensed`, `max_effort`, etc. **Fail-closed**: no/invalid/expired\n license → free tier; an unsigned license never grants premium.\n `AITHER_TENANT_SLUG=aitherium` → unrestricted INTERNAL tier.\n- **Moat guard** (`scripts/check_moat_boundary.py`) wired into the publish\n workflow — blocks any wheel that leaks the moat or drops the keystone.\n- Tests: `tests/test_licensing.py`, `tests/test_moat_boundary.py`.\n\n### Changed (gating — fail-closed)\n- Free tier effort is capped at 3 (reasoning effort 7–10 needs Professional).\n- Monthly token budget hard-block on the free tier (no silent overspend).\n- Gated behind a paid tier: fleet mode (`FleetConfig`), cross-agent delegation\n (`AgentForge.delegate`), channel adapters, cron scheduler, proactive\n auto-neurons, and `swarm`/`swarm_code` dispatch.\n\n### Removed\n- **`adk/nanogpt.py`** (on-device training IP) removed from the published SDK\n and the public repo; relocated to the internal AitherOS moat. Excluded from\n the wheel as defense-in-depth.\n- Pre-2.0 releases shipped this IP and ungated capabilities and are being\n yanked (PyPI) / removed (GitHub releases). See `scripts/yank_leaky_releases.py`\n and `scripts/purge_public_leaks.sh`.\n\n## [1.20.0] - 2026-05-27\n\n### Fixed\n- **Pack discovery path mismatch** — `_find_local_packs()` now scans both\n `~/.aither/packs/` and `~/.aitheros/packs/`, so packs installed via\n `adk pack install` are discovered by `adk-workspace`.\n\n### Added\n- **Auto-register on pack install** — `adk pack install` now auto-registers\n with the portal if the pack contains an `agent.yaml` with a `portal` section.\n One command does download + extract + register.\n- **Flux event on agent registration** — `developer_portal.py` emits\n `agent.registered` Flux event so the portal fleet UI updates in real-time.\n\n## [1.15.0] - 2026-05-26\n\n### Added\n- **Cloud memory** — `adk quickstart --cloud` now tests gateway memory and\n auto-configures Spirit URL so memories persist across devices via\n `gateway.aitherium.com/v1/memory/teach` and `/v1/memory/recall`.\n- **Config→env export for cloud mode** — `Config.from_env()` now exports\n `AITHER_CLOUD_MODE`, `AITHER_SPIRIT_URL`, and spirit path env vars from\n saved config so Memory and other modules pick them up without needing\n explicit env vars set by the user.\n- **Cloud Quick Start section** in README — 3-command setup for users who\n just want a Claude/GPT-4/DeepSeek API key and the full agent harness.\n- **`--memory` profile for node deployment** — `adk deploy node --memory`\n adds Spirit (8087) + WorkingMemory (8101) containers for persistent\n vector memory. Without this, agents use local SQLite only.\n- **Configurable vLLM in node compose** — `AITHER_VLLM_MODEL`,\n `AITHER_VLLM_GPU_UTIL`, `AITHER_VLLM_CTX_LEN`, `AITHER_VLLM_QUANT_ARGS`\n env vars let `adk setup` write GPU-detected tier settings to `.env`\n and have the node compose pick them up (TQ4 for small GPUs, BNB for large).\n\n### Fixed\n- **Memory cloud routing** — `Memory._spirit_teach()` and `_spirit_recall()`\n now use configurable paths and auth headers for gateway proxy. Previously\n cloud-only users silently lost all Spirit memory calls.\n\n## [1.9.0] - 2026-05-24\n\n### Added\n- **Composable ToolPack system** — agents can load tool packs from\n `.toolpack.yaml` directories. Drop a pack in `~/.aitheros/packs/` or\n set `AITHER_PACKS_DIR`, and any agent discovers it automatically.\n- **`tools.packs` in agent.yaml** — list tool pack IDs to auto-load\n (e.g. `packs: [crm-tools, calendar-pro]`).\n- **`register_tool_packs(agent, pack_ids, packs_dir)`** in `builtin_tools` —\n programmatic pack loading for agents.\n- **`AITHER_TOOL_PACKS` env var** — comma-separated pack IDs loaded on\n agent init (alternative to YAML config).\n- **Unknown builtin categories resolve as pack IDs** — `tools.builtin:\n [file_io, crm-tools]` now works: `crm-tools` is looked up as a pack.\n\n## [1.8.0] - 2026-05-24\n\n### Removed\n- **11 dead modules** (3,023 lines) — `agent_deploy`, `cloud_deploy`, `create`,\n `phonehome`, `sdk_bridge`, `sovereign`, `telemetry`, `telemetry_config`,\n `error_reporter`, `anonymizer`, `package`. None were imported anywhere.\n- **29 ghost exports** from `__init__.py` — `secrets`, `otel`, `fs_sandbox`,\n and `vector_memory` modules were listed in `__all__` and `__getattr__` but\n the backing files never existed. Accessing them raised `ModuleNotFoundError`.\n- **Stale dist/ artifacts** — removed old wheels/tarballs (v0.4.0–v1.4.1, 11 MB).\n- **Orphan test scripts** — `test_phase1.py` and `test_phase23.py` (one-off\n validation scripts not part of the pytest suite).\n\n### Fixed\n- **Version mismatch** — `__init__.__version__` now matches `pyproject.toml`\n (was stuck at 1.6.0).\n\n## [1.5.0] - 2026-05-23\n\n### Added\n- **`--sovereign` flag for `adk deploy node`** — registers the node with the\n Aitherium hub via federation after deployment. Saves federation credentials\n to `~/.aither/.env.federation`. Accepts `--hub` (custom hub URL) and\n `--tenant` (tenant slug) options. Non-fatal: node runs standalone if\n registration fails.\n- **Federation → fleet cross-post** (Genesis) — sovereign nodes that register\n or heartbeat via `/federation/register` and `/federation/heartbeat` now\n automatically appear in the fleet dashboard (`/fleet/deployments`) with\n `node_type: \"sovereign\"` and live status/metrics.\n- **\"Deploy Sovereign Node\" section on Connect page** (AitherVeil) — Section 8\n with ADK deploy commands, fleet dashboard links, and provision page links.\n- **`deploy-node` action in tunnel proxy** (AitherVeil) — Connect page and\n external callers can now request bootstrap scripts via\n `POST /api/tunnel/devworkspace?action=deploy-node`.\n\n### Fixed\n- **Fleet provision page** — API URL corrected from `/api/fleet/provision` to\n `/api/bridge/genesis/fleet/provision` (matches bridge proxy pattern).\n- **Fleet detail page** — API URL corrected from `/api/fleet/deployments/` to\n `/api/bridge/genesis/fleet/deployments/` (same fix).\n- **Dockerfile** — entry point corrected from `aither-serve` to `adk-serve`\n (matches `pyproject.toml` script registration). Version label updated.\n\n## [1.4.1] - 2026-05-22\n\n### Fixed\n- **macOS executable builds** — dropped `--target-arch universal2` from\n `packaging/build_executable.py`. PyPI wheels for `pydantic_core` and other\n native deps are arch-specific, not fat binaries, so universal2 always failed\n with `IncompatibleBinaryArchError`. Now builds for the runner's native arch\n (`aither-macos-arm64`).\n- **Release workflow strategy** — added `fail-fast: false` so a single\n executable build failure no longer cancels the other OS jobs.\n- **PyPI publish** — added `skip-existing: true` so re-runs of the same\n version don't fail when the artifact already exists.\n- **GitHub Release job** — now runs whenever `pypi` succeeds even if some\n executable matrix entries fail, so the release page is always created with\n whatever binaries did build successfully.\n\n## [1.4.0] - 2026-05-22\n\n### Added\n- **Sovereign template now ships with `agent_core` 5-tier memory** — generated\n backends include the canonical `agent_core/` package (working → episodic →\n semantic → procedural → identity) plus `agent_memory.py` helper, vendored\n from `portal-kit-backend`.\n- `aiosqlite>=0.20.0` added to sovereign template `requirements.txt` for\n local agent_core SQLite storage.\n- Document tombstoning: `evict_cached_conversations_referencing` is wired\n through document-delete handlers in consumer apps so memories citing\n deleted documents are blocked from recall.\n- New tests: 13 smoke tests for `portal-kit-backend/agent_core` covering\n promotion thresholds, decay rates, store roundtrip, tombstone recall\n blocking, circuit breaker state machine, and chat-turn recording.\n\n### Changed\n- `scripts/vendor_agent_core.py` now syncs canonical `agent_core` to 4\n targets: WorkspaceRuntime, ADK sovereign template, and the GargBot +\n Chelle consumer mirrors (previously only 2).\n\n## [1.2.1] - 2026-05-21\n\n### Added\n- `adk train` command group — 6 subcommands: status, launch, logs, cancel, runs, register-gpu\n- `/slash-commands` endpoint — ADK server auto-exposes all 35 CLI commands as structured JSON manifest for AitherShell\n- `/cli/execute` endpoint — AitherShell can run any CLI command through the server\n- `build_command_manifest()` — introspects full argparse tree (commands, args, choices, subcommands)\n- `_register_commands()` extracted from main() for shared parser construction\n\n### Changed\n- `_get_genesis_url()` now probes: Genesis → ADK server → Aitherium cloud gateway (works without Genesis)\n\n## [1.2.0] - 2026-05-21\n\n### Added\n- **Runtime backend switching** — `agent.switch_backend(\"deepseek\")` and `agent.llm.switch_backend()` change provider without recreating agent\n- **Hybrid reasoning** — `agent.set_reasoning_backend(\"anthropic\")` routes effort 7+ to cloud API while keeping local orchestrator\n- **First-class DeepSeek provider** — `deepseek` in effort model table with `deepseek-chat` and `deepseek-reasoner`\n- **TQ4 quantization tiers** — `nano` (6GB, TurboQuant 4-bit), `standard-tq4` (12GB, both models TQ4), `hybrid-tq4` (6GB + cloud reasoning)\n- **`adk backend` CLI** — `list`, `set`, `set-reasoning`, `test` subcommands\n- **`adk quickstart`** — unified first-run wizard (setup + auth + shell)\n- **`adk tools`** — list local + MCP tools with tier markers\n- **`adk backup`** — tarball export of `~/.aither/`\n- **`adk ingest`** — feed docs/files into knowledge graph\n- **`adk setup nemotron`** shortcut — alias for `--tier lite`\n- **`--reasoning-api`** flag in setup — `anthropic`, `openai`, `deepseek`, `gateway`\n- **`--dgx-spark URL`** flag — remote vLLM endpoint configuration\n- **DGX Spark auto-detection** — scans `spark.local`, `192.168.0.33`, `AITHER_DGX_URL` env\n- **Post-setup smoke test** — sends real inference request after container startup\n- **Schema migration system** — version table + migration runner in `memory.py` and `graph_memory.py`\n- **Configurable vLLM port scan** — `AITHER_VLLM_PORTS` env var for custom ports\n- **`aithershell` entry point** — alias for `adk` in pyproject.toml\n- Shell auto-downloads binary on first `adk shell` (no `--install` needed)\n- Shell pre-flight check warns if no backend detected\n- Shell passes full config via env vars (API key, tenant, inference URL)\n- DGX/Remote and Cloud API checks added to `adk doctor`\n- Config fields: `reasoning_backend`, `reasoning_api_key`, `deepseek_api_key`, `dgx_url`, `vllm_extra_ports`\n\n### Fixed\n- `test_aeon_chat_basic` timeout — mock scope was exiting before request\n- `adk doctor` check_disk timeout — cap rglob at 5000 files\n\n## [1.1.7] - 2026-05-19\n\n### Added\n- Remote agent pipeline — `adk onboard` → fleet dispatch → remote inference\n- Agent-to-agent federation with mesh relay\n- `adk connect` for desktop mesh enrollment\n\n## [1.1.6] - 2026-05-19\n\n### Fixed\n- Tool call execution recovery across all backends\n- Hermes tool call fallback parsing for vLLM\n\n## [1.1.5] - 2026-05-19\n\n### Fixed\n- Tool call execution + Hermes fallback parsing improvements\n\n## [1.1.0] - 2026-05-18\n\n### Added\n- Multi-channel gateway (Telegram, Discord, Slack, webhook)\n- Aeon group chat with 7 presets\n- Skills auto-extraction from multi-step sessions\n- MCP stdio server for Claude Code integration\n- `adk gateway`, `adk cron`, `adk skills`, `adk soul` commands\n- SOUL.md import/export for portable agent identity\n- Elysium cloud device-flow authentication\n\n## [1.0.0] - 2026-05-18\n\n### Breaking\n- Package consolidated — `aither_adk` namespace replaced by `adk` + `adk.platform`\n- 93 duplicate files removed from `AitherOS/packages/aither_adk/`\n\n### Added\n- Standalone core primitives (`adk.core`: Agent, Tool, Memory, ModelBackend, Capability, Trace)\n- `adk.platform` sub-package merging all platform toolkit modules\n- Compatibility shims for legacy `from aither_adk.*` imports\n\n### Changed\n- Version synced across pyproject.toml, `adk/__init__.py`, `adk/platform/__init__.py`, npm package\n- Classifier promoted from Alpha to Beta\n\n## [0.16.0] - 2026-04-16\n\n### Swarm Coding Engine & Repowise Integration\n- **`repowise_search` tool** — Semantic + keyword hybrid code search via Repowise, with ripgrep fallback\n- **`swarm_code` tool** — Dispatch complex tasks to 11-agent swarm pipeline (ARCHITECT->SWARM->REVIEW->JUDGE)\n- **`agent.swarm()`** — Async convenience method with configurable mode, effort, timeout\n- **`agent.code_search()`** — Async convenience method returning structured results\n- New tool categories: `repowise` and `swarm` in TOOL_CATEGORIES\n- `repowise_search` and `swarm_code` added to `__init__.py` exports with lazy loading\n- `IDENTITY_DEFAULTS` updated: repowise for code-focused agents, swarm for orchestration agents\n- Standalone graceful degradation: repowise falls back to ripgrep, swarm returns structured error\n\n## [0.13.0] - 2026-04-02\n\n### Graph Faculties — Local Knowledge for Every Agent\n- **CodeGraph** (2,799 lines) — Full Python AST indexer with call graph, keyword/semantic/hybrid query, embedding matrix cache, incremental re-indexing, multi-hop chain expansion\n- **MemoryGraph** (1,339 lines) — Graph-based persistent agent memory with 10 edge types, hybrid query (keyword + semantic + graph expansion), multi-hop recall, pickle persistence\n- **EmbeddingProvider** — 4-backend fallback chain: sentence-transformers (GPU/CPU) -> Ollama -> Elysium cloud -> feature hashing (zero deps)\n- **BaseFacultyGraph** — Abstract base with HMAC-SHA256 validated pickle persistence\n\n### Agent Integration\n- `agent.set_code_graph(cg)` — auto-registers `code_search` + `code_context` tools\n- `agent.set_memory_graph(mg)` — auto-registers `remember` + `recall` + `memory_stats` tools\n- Both graphs inject context into LLM prompts automatically during chat\n\n### Zero-Config Onboarding\n- `adk start` / `adk` (no args) — auto-detect project, index code, detect LLM, persistent memory, interactive chat\n- Works for any directory: Python codebases, doc folders, mixed workspaces\n- Auto-detects LLM: Ollama -> vLLM -> Elysium -> OpenAI -> Anthropic\n- Per-project persistent memory in `~/.aither/memory/<project>`\n- `adk index <path>` — standalone indexing with progress bar and stats\n\n### MCP Gateway\n- `POST /v1/embeddings` — OpenAI-compatible embedding proxy to vLLM-embeddings:8209\n- Available to all tiers (embeddings are free)\n- ADK EmbeddingProvider uses this as Elysium cloud fallback\n\n### Optional Dependencies\n- `pip install aither-adk[graphs]` — numpy for 10x cosine similarity speedup\n- `pip install aither-adk[embedding]` — sentence-transformers + torch for local GPU embeddings\n- `pip install aither-adk` alone — graphs work with feature hashing (zero deps)\n\n## [0.12.0] - 2026-04-01\n\n### Bootstrap & Service Discovery\n- Version handshake -- ADK checks major.minor compatibility with Genesis/Node\n- Background reconnect loop -- ServiceBridge re-probes every 30s in standalone mode\n- Port 8080/8090 documented -- MCP vs OpenAI-compat clearly separated\n- Genesis URL configurable via GENESIS_URL env var (was hardcoded)\n- Standalone mode warning -- visible stderr alert when AitherOS not detected\n- Auto-reconnect on startup when services come online\n\n### Elysium Cloud Inference\n- Unified gateway URL -- gateway.aitherium.com handles auth + billing + inference\n- Streaming inference -- SSE passthrough for /v1/chat/completions with stream=true\n- Auth proxy routes -- /v1/auth/register, /v1/auth/login, /v1/auth/me\n- Billing proxy -- /v1/billing/balance through gateway\n- AitherConnect Elysium fallback -- cloud inference when local Genesis is down\n- AitherDesktop Elysium fallback -- third-tier chat fallback after Node\n\n### Infrastructure\n- /discovery endpoint on Genesis -- unified service URLs/versions/health\n- /api/config/services on Veil -- runtime port config for client-side JS\n- Veil healthcheck gates on Genesis -- unhealthy when backend is down\n- Desktop crash detection 90s->60s (threshold 3->2)\n\n## [0.11.0] - 2026-04-01\n\n### Agent Execution Quality (Claude Code Parity)\n- Raise loop guard block threshold from 3 to 4 -- agents get more room for iterative search\n- Soft synthesis nudge for effort >= 4 (no tool stripping, trust the model)\n- max_output_tokens escalation -- retry up to 3x when response is truncated\n- Tool result pairing guarantee -- synthetic error for orphaned tool_use blocks\n- Micro-compaction of old tool results -- save context tokens on long sessions\n- First-turn tool forcing only for effort >= 6 (trust model for lower effort)\n- Diminishing returns detection -- nudge agent when 3+ turns produce < 500 tokens\n- Message normalization -- merge consecutive same-role messages, strip empties\n- LLM retry with exponential backoff (5 retries, 500ms-16s, jitter)\n\n## [0.9.0] - 2026-03-16\n\nThe \"connected world\" release. Cross-platform identity pairing, voice capabilities, and multi-channel integration.\n\n### Added\n- **Pairing**: Cross-platform identity linking (`adk/pairing.py`)\n - `PairingManager` — SQLite-backed identity linking with 6-char pairing codes\n - Link users across Telegram, Discord, Slack, WhatsApp with 10min TTL codes\n - Canonical session IDs for cross-channel conversation continuity\n - `get_session_id()` returns \"user-{id}\" for paired users\n- **Voice**: Speech-to-text and text-to-speech client (`adk/voice.py`)\n - `VoiceClient` — async STT/TTS/emotion via AitherVoice service\n - Convenience functions: `hear()`, `say()`, `feel()`\n - 6 voice options: alloy, echo, fable, nova, onyx, shimmer\n - Emotion detection with intensity scoring\n- New exports: `PairingManager`, `PairingResult`, `PlatformIdentity`, `VoiceClient`, `TranscriptionResult`, `SynthesisResult`, `EmotionResult`\n\n### Changed\n- `__init__.py` exports expanded with pairing and voice symbols\n\n## [0.6.0] - 2026-03-13\n\nThe \"group mind\" release. Multi-agent group chat, creative tools, and Iris identity.\n\n### Added\n- **Aeon**: Multi-agent group chat engine (`adk/aeon.py`)\n - `AeonSession` — persistent group chat with parallel agent execution\n - 7 presets: balanced, creative, technical, security, minimal, duo_code, research\n - Orchestrator synthesis: Aither summarizes all agent responses\n - Serial execution for Ollama, parallel for vLLM/cloud\n - ConversationStore persistence with `type: \"aeon\"` metadata\n - `group_chat()` one-shot convenience function\n- `aither aeon` CLI command — interactive terminal group chat with color-coded agents\n - `-p/--preset`, `-a/--agents`, `-r/--rounds`, `--no-synthesize` flags\n - `reset` and `quit` commands\n- Server endpoints: `POST /aeon/chat`, `GET /aeon/presets`, `GET /aeon/sessions/{id}`\n- Creative tools in builtin_tools: `image_generate`, `image_refine`, `image_search`, `video_generate`\n- Iris agent identity with visual generation capabilities\n- 48 Aeon tests (data models, presets, chat, context, persistence, server, exports)\n\n### Changed\n- `__init__.py` exports: `AeonSession`, `AeonResponse`, `AeonMessage`, `group_chat`, `AEON_PRESETS`\n\n## [0.5.0] - 2026-03-13\n\nThe \"tenant-ready\" release. Multi-tenant admin, permission grants, safety profiles, and a full setup wizard.\n\n### Added\n- `aither setup` interactive setup wizard with hardware detection, model selection, identity config\n- Strata storage backend: SQLite WAL persistence for conversations, memories, knowledge graphs\n- CLI test runner: `aither test` with auto-discovery and parallel execution\n- Permission grants system for MCP tool access control\n- MCP account management tools\n- LLM provider auto-detection for Ollama and vLLM endpoints\n- Apache-2.0 LICENSE file\n- Elysium desktop sync module\n- Comprehensive CLI test suite (493 tests)\n- Strata storage test suite (972 tests)\n- LLM provider tests (82 tests)\n\n### Changed\n- CLI expanded: `aither setup`, `aither test`, `aither bugreport`, `aither doctor`\n- Strata module rewritten as full local-first storage engine\n- Server startup includes Strata initialization\n- README rewritten with clearer quickstart and architecture docs\n\n### Fixed\n- Dead documentation links\n- Python 3.11 compatibility issues\n- Ruff per-file-ignores configuration\n- Identity provisioning edge cases\n- Docker node-gyp native dependency builds (python3 + build tools)\n\n## [0.4.0] - 2026-03-13\n\nThe \"own your AI\" release. Self-hosted agent OS for people who don't want their data on someone else's servers.\n\n### Added\n- Muse agent identity (creative/artistic generation)\n- Port 8120 to vLLM scan for ExoNodes discovery\n- Public roadmap with milestone-based porting schedule\n- Competitive positioning: self-hosted alternative to cloud-locked AI appliances\n\n### Changed\n- LLM router: compute-aware effort routing (replaces effort-level context gating)\n- README rewritten for sovereignty-first messaging\n- Package description updated\n- Promoted from alpha to stable release\n\n### Fixed\n- Version string consistency between pyproject.toml and __init__.py\n- vLLM port scanning now includes port 8120 (ExoNodes)\n\n## [0.3.1] - 2026-03-09\n\n### Added\n- GraphMemory: SQLite knowledge graph with Ollama embeddings and hybrid search\n- NeuronPool: auto-fire pre-LLM data gathering agents\n- NanoGPT: pure Python char-level transformer with LoRA fine-tuning\n- Safety gates: IntakeGuard (input), LoopGuard (recursion), Sandbox (code exec)\n- Event system: EventEmitter with chat/tool/forge event types\n- Builtin tools: identity-based tool selection\n- ServiceBridge: auto-discovery of AitherOS services\n- Streaming: chat_stream with safety gate integration\n- Auth middleware: Bearer token authentication\n- CLI: `aither init` and `aither serve` commands\n\n### Changed\n- Wired 5 previously disconnected modules into agent loop\n- 522 total passing tests (up from 85)\n\n## [0.3.0] - 2026-03-07\n\n### Added\n- Clean-room agent development kit\n- Multi-backend LLM providers (Ollama, OpenAI, Anthropic)\n- AitherAgent class with @tool decorator\n- SQLite conversation memory\n- OpenAI-compatible server (`aither-serve`)\n- 16 agent identities as package data\n- MCP bridge to mcp.aitherium.com\n- Privacy-centric opt-in telemetry\n- Bug reporting CLI and API\n- FastAPI server with OpenAI-compatible endpoints\n- Hardware auto-detection (5 tiers, 11 profiles)\n- Fleet orchestration and multi-agent coordination\n- A2A mesh protocol\n- Federation protocol for cross-instance agent dispatch\n\n### Initial release\n- 85 passing tests\n- Apache-2.0 license\n"}, "05e4d6d6892f71db254687093fa6b8d3cc439251": {"t": 1785748161.2473602, "n": 91177, "h": "839ce04402f4eba2", "c": "# Changelog\n\nAll notable changes to aither-adk will be documented in this file.\n\n## [2.27.0] - 2026-07-18\n\n### Added — Moonshot Kimi K3 provider (DeepSeek-equivalent cloud backend)\n\n- **New `moonshot` provider** across the LLM router: OpenAI-compatible\n `https://api.moonshot.ai/v1`, default model `kimi-k3` (131K context default, up to 1M;\n tool calls, JSON mode, structured output). Wired everywhere `deepseek` is:\n - `LLMRouter(provider=\"moonshot\")` + `_COMPAT_URLS`/`_COMPAT_MODELS`/model tiers\n (small/medium/large → `kimi-k3`)\n - Config: `moonshot_api_key` field (env `MOONSHOT_API_KEY`), saved-config load,\n provider-keys map (`provider_keys.json` key `moonshot`)\n - Auto-detect chain: tried after DeepSeek (config key, then env key)\n - Hybrid reasoning backend: `AITHER_REASONING_BACKEND=moonshot` for effort 7+\n - Reasoning tiers: `backend: moonshot` (alias `kimi`) via new `MoonshotBackend`\n - CLI: `adk backend set moonshot --api-key …`, `adk backend list` shows moonshot\n- No breaking changes; DeepSeek and all other providers unchanged.\n\n## [2.26.0] - 2026-07-16\n\n### Added — Aeon group-chat web UI pack + POST /aeon/stream SSE endpoint\n\n- **New `aeon` UI pack** (`adk/webui/packs/aeon/index.html`) — group chat for multi-agent\n discussion. Mirrors the `llamacpp` pack's auth + SSE pattern (bearer from URL fragment #k=),\n with a preset selector (balanced/creative/technical/security/minimal/duo_code/research) and\n distinct colored agent messages. Synthesis block highlighted. Agent content is HTML-escaped\n before render. Fully self-contained HTML+CSS+JS, light+dark, no external assets. (No\n temperature/max-tokens controls — Aeon runs a fixed multi-agent round and doesn't thread\n per-turn generation params, so exposing them would be a no-op.)\n- **POST /aeon/stream SSE endpoint** — accepts body {message, preset?, agents?, rounds?,\n session_id?}, emits SSE with: `session_start` (participants + orchestrator), `agent_message`\n per non-orchestrator, `synthesis` (if enabled), `error` on exception, `complete` (total tokens\n + latency). Mirrors /chat/stream structure — like every streaming endpoint here it is gated by\n the server auth middleware **when `AITHER_SERVER_API_KEY` is set** (a local `adk up` with no key\n is unauthenticated by design, same as /chat/stream).\n- **Default UI pack flipped to `llamacpp`** — `resolve_ui_pack_name()` now returns `llamacpp`\n instead of `console` when no env/config is set (new users see the clean chat, not the admin\n SPA; existing users with AITHER_AGENT_UI or saved config unaffected).\n\n### Added — swappable agent web UI packs (llama.cpp-style built-in chat)\n\n- The page an agent serves at `/` is now a **swappable UI pack**, so you can\n drop in, test, and deploy different chat frontends without touching the agent.\n Select with `adk ui set <name>` (persists to `~/.aither/config.json`) or the\n `$AITHER_AGENT_UI` env; `adk ui ls` lists packs, `adk ui path` shows what\n resolves.\n- **New built-in `llamacpp` pack** — a clean, self-contained llama.cpp-style\n chat UI (centered conversation, streaming token-by-token with a cursor, a\n Stop button, a settings drawer for temperature / max-tokens that is actually\n honored end-to-end — `/chat/stream` now forwards them to the provider —\n minimal safe markdown, light+dark). Hits the agent's own gated `/chat/stream`\n with the bearer read from the URL fragment (never sent in the URL). Built-ins\n also include `console` (the full admin SPA, still the default) and `minimal`.\n- **Drop-in custom packs**: a UI pack is just a folder with an `index.html` in\n `~/.aither/ui-packs/<name>/` (override via `$AITHER_UI_PACKS_DIR`) — no rebuild.\n- Resolution is **fail-soft**: an unknown/missing pack falls back\n console → minimal, so `/` is never blank.\n\n### Added — MCP-gateway-first platform access + mesh A2A trust\n\n- **Gateway-first platform access** (`adk/client/_gateway_mcp.py`, `GatewayMCPClient`):\n a self-hosted agent connects its MCP **client** to `mcp.aitherium.com` authenticated\n as the OWNER (device-flow / PAT / ACTA token), RBAC-scoped to that identity — so it can\n use platform tools + `get_secret` **whether or not the node runs local AitherOS\n services**. Fail-closed: no token → client disabled, the agent still runs. Wired into\n the server lifespan (`_connect_gateway_mcp`).\n- **`genesis` LLM backend**: `adk backend set genesis` points the agent brain at a local\n Genesis (`http://localhost:8001/v1`, model `workflow`) — the 5090/controller pattern\n where inference is served by the local fleet, not the cloud gateway or a raw vLLM.\n- **`adk mesh` command**: `adk mesh onboard` drives the Conductor 5-step onboard\n (`/v1/mesh/onboard` → registers this node's public key, returns the `endpoint:mesh`\n trust token); `adk mesh ls` lists peer nodes/agents (AitherMesh `/nodes` + Directory)\n with their A2A cards + invoke URLs for discovery.\n\n### Security — A2A inter-agent trust enforcement (fail-closed)\n\n- **`adk/a2a_trust.py`** (new): Ed25519 signature verification + trusted-key check\n (`AITHER_A2A_TRUSTED_KEYS`), opt-in via `AITHER_A2A_REQUIRE_TRUST` (default `false` for\n backward compatibility; `audit` logs untrusted, `true` rejects).\n- **Fixed a fail-OPEN in the inbound A2A handler** (`adk/a2a.py`): the trust check\n previously ran *only when signature headers were present*, so an unsigned request\n bypassed `AITHER_A2A_REQUIRE_TRUST` entirely. Now genuinely fail-closed — required mode\n with no/invalid signature → `403`.\n- **Sovereign template no longer scaffolds `verify=False`**: the generated\n `services/llm.py` now uses the project's own `tls_verify()` policy helper for every\n provider call instead of hardcoding disabled TLS verification.\n- **`adk/_tls.py`**: disabling verification via `AITHER_TLS_VERIFY=false` now emits a\n one-time loud warning (was silent) — the escape hatch stays, the silence goes.\n\n## [2.25.1] - 2026-07-15\n\n### Fixed — self-hosted OpenAI-compatible backends honor a custom base URL\n\n- `adk backend set llamacpp --base-url http://localhost:8090/v1 --model <id>` (and\n `vllm`/`lmstudio`, and the `AITHER_LLM_BASE_URL` env) now actually point the agent\n at YOUR server. Previously the base URL was saved but never read, so an explicit\n `--backend llamacpp` fell back to the provider's public API default (openai.com).\n New `Config.llm_base_url` field + saved-config `inference_url` wiring; the explicit\n backend path in `LLMRouter` now passes it for the local OpenAI-compatible family.\n This is what lets a self-hosted node run its OWN local model (e.g. a PrismML\n Bonsai llama.cpp) as the agent brain instead of ollama/cloud.\n\n## [2.25.0] - 2026-07-15\n\n### Added — node_bootstrap tool pack: bootstrap LLM inference on any hardware\n\n- **New bundled tool pack `node-bootstrap`** (`adk/toolpacks/node_bootstrap/`):\n seven agent tools — `node_detect_hardware`, `node_resolve_recipe`,\n `node_plan_deployment`, `node_apply`, `node_enroll`, `node_register_backend`,\n `node_verify` — plus a CLI shim (`python -m adk.toolpacks.node_bootstrap\n detect|resolve|plan|apply|enroll|register|verify`). Detect a box's hardware,\n resolve a deployment recipe, actually deploy the inference engine (docker\n compose / native / delegate), enroll with a control plane, register the\n backend, and live-verify a completion.\n- **9 hardware recipes** shipped in the wheel: `cpu-1bit-llamacpp`, `cpu-ollama`,\n `cuda-vllm-8gb/24gb/40gb`, `cuda-dual-stack-32gb` (32GB-card co-resident\n stack), `unified-memory-vllm` (DGX/Grace-class), `metal-ollama` (native —\n Docker on macOS has no Metal passthrough), `cloud-api` fallback. Resolution\n scores hardware against min/max VRAM bands, RAM, cores and unified memory;\n ties prefer higher tiers and self-contained recipes over fleet delegates.\n- Security posture: enrollment and backend registration are fail-closed\n (missing token/URL → error dict, never anonymous, never `verify=False`);\n tokens are redacted in outputs; all endpoints come from args/env\n (`AITHER_CONTROL_PLANE_URL`, `AITHER_GENESIS_URL`, `AITHER_AUTH_TOKEN`).\n\n## [2.24.0] - 2026-07-14\n\n### Changed — sync-family consolidation\n\n- **Removed dead code:** `adk/sync.py` (528 lines) deleted — it was unreachable,\n shadowed by the `adk/sync/` package (`import adk.sync` always resolved to the\n package; nothing loaded the module by path — verified).\n- **Grouped the sync domain modules into the `adk.sync` package:**\n `brain_sync`→`adk.sync.brain`, `files_sync`→`adk.sync.files`,\n `lockbox_sync`→`adk.sync.lockbox`, `secrets_sync`→`adk.sync.secrets`,\n `session_sync`→`adk.sync.sessions`, `settings_sync`→`adk.sync.settings`,\n `sync_watermark`→`adk.sync.watermark`. All in-tree importers updated. No\n back-compat shims were left because **zero external/monorepo code imported the\n old root paths** (verified) — a clean move, not a shimmed one. **Breaking only\n for code that imported `adk.<name>_sync` directly** (none known).\n\n### Docs — MCP module layering clarified\n\n- `adk.mcp` (enterprise AitherOS gateway client: auth/billing/token-tracking) and\n `adk.core.mcp` (minimal generic JSON-RPC adapter for any MCP server) are\n DISTINCT layers, not duplicates — each now carries a LAYER/ROLE/See-also\n docstring so they are not mistakenly consolidated.\n\n## [2.23.2] - 2026-07-14\n\n### Fixed — CompletionGate no longer false-fails on correct results\n\n- **Tolerant judge JSON extraction.** The LLM judge parsed its reply with `json.loads(text[first\"{\":last\"}\"])`, which raised `JSONDecodeError` (\"Extra data\") whenever the model appended prose after the verdict or emitted a second object — and `verify()` turned that into a fail-closed verdict, so a genuinely completed task reported `unverified`. New `_first_json_object()` uses `JSONDecoder.raw_decode` from the first `{` that begins a valid object and ignores trailing content (prose, markdown fences, extra objects). A judge that answers with no parseable JSON still fails closed (never a soft pass), now with a clear reason. The same extractor backs `_derive_criteria`.\n- **Bare code-like tokens are hard-checked.** A natural criterion like `output contains ADK_AUTO_OK` (no quotes) previously fell through to the judge; `hard_checks` now also matches bare identifier/code shapes (contains an underscore, or 4+ CAPS/digit chars), with a stopword guard so a descriptive uppercase word (`OUTPUT`, `CONTAIN`) never becomes a false required token. Quote a token to force it regardless.\n- +9 tests (24 total), including the exact trailing-data regression.\n\n## [2.23.0] - 2026-07-14\n\n### Removed — dead modules and internal integrations\n\n- **`adk.swarm`, `adk.provisioning_tools`, `adk.session_sync_integration`, `adk.addon_metering`** — four orphan modules with zero public usage have been deleted. These were internal prototypes kept for legacy monorepo consumers; public packages have not imported them since 2.21.0 (tracked as D-348). **Breaking for anyone directly importing these modules** (import will fail). They are no longer exempt from the orphan-module check.\n- **`adk.aither_bridge`** — the AitherOS-internal IRC ↔ chat gateway bridge is no longer shipped in the public aither-adk package (stripped at sync time). All public importers already degrade behind `except ImportError` (in `adk.server` and `adk.builtin_tools`), so public packages remain unaffected. The module stays in the private AitherOS monorepo for internal use.\n- **Identity document trim** — removed internal agent names from `adk/identities/aither.yaml` delegation guidance, replacing with role-generic wording (\"delegate code review, refactoring, security analysis, performance and testing to specialist agents when available\"). Effort-tier routing philosophy remains unchanged and is product design, not a leak.\n\n### Changed — genericized customer/product names in help text\n\n- CLI help/examples no longer name specific customer deployments (`--scope` template list and `deploy agent` name example use generic placeholders). Functional deploy paths are unchanged.\n\n## [2.22.1] - 2026-07-14\n\n### Fixed — real bugs surfaced by reviving the public CI (red since Jul 5)\n\n- **TLS**: bundled toolpacks (arc-brainpack, cloudflare) disabled certificate\n verification on gateway/vault calls; all now route `adk._tls.tls_verify()`\n (verified by default, internal CA bundle when present).\n- **`system_prompt` contract**: bundled-pack `[PACK DIRECTIVES]` were appended\n even to an explicit `system_prompt`; an explicit prompt now wins wholesale.\n- **Headless secrets**: `_derive_key()` used `os.getlogin()`, which raises\n `OSError` without a controlling terminal (CI, daemons, containers) — falls\n back to `getpass.getuser()` (same value on interactive boxes, so existing\n secret files keep decrypting).\n- **`pack_verifier`**: missing `cryptography` crashed with `UnboundLocalError`\n in the exception handler instead of failing closed; now degrades gracefully\n (and `cryptography` was added to the dev extra so CI tests the real path).\n- **`fleet_manager._kill_pid` (POSIX)**: reported success immediately after an\n asynchronous SIGTERM and never reaped children (zombies read as alive); now\n polls, escalates to SIGKILL, and reaps.\n- **Identity**: `aither` regained the `creative` tool category (dropped\n accidentally in a 2.7.0 WIP sweep).\n\n## [2.22.0] - 2026-07-14\n\n### Changed — internal toolkit relocation\n\n- **`adk.platform` relocated** — the internal aither-platform toolkit (ComfyUI/image-gen\n pipelines, fleet agent clients, parallel CLI/UI/tools stack) has been moved out of the\n adk SDK tree to the standalone internal `aither-platform` package at\n `AitherOS/agents/aither_platform/`. The public package remains unaffected (it already\n excluded `adk.platform` since 2.21.1). Internal AitherOS agent builds now install\n `aither-platform` separately. `adk platform` falls through to the same \"not available\"\n message on public installs; internal builds work via the relocated package.\n\n### Removed — backward-compat re-export deleted\n\n- **`adk.gateway` module removed** — the backward-compatibility re-export of `GatewayClient`\n from `adk.gateway` has been removed. Use `from adk.client import GatewayClient` instead\n (the established public API since 2.21.0). All internal importers have been updated.\n\n## [2.21.1] - 2026-07-14\n\n### Removed — internal toolkit and license-gated product no longer ship publicly\n\n- **`adk.platform`** — the internal aither-platform toolkit (ComfyUI/image-gen\n pipelines, fleet agent clients, a parallel cli/ui/tools stack) is no longer\n included in the public repo or the PyPI wheel. It was internal-only code that\n had been riding along since the aither-platform merge. `adk platform` now\n reports \"Platform toolkit not available\" on public installs; internal AitherOS\n builds are unaffected.\n- **`adk.formbridge`** — the FormBridge form-automation product is license-gated\n (PROFESSIONAL+); its implementation no longer ships in the free package. All\n import sites (`builtin_tools`, `server` route mount) already degrade behind\n `except ImportError`, so nothing else changes behavior.\n- Product test suites (`test_formbridge.py`, `test_encounter_keying.py`,\n `test_flow_tools.py`) are excluded from the public sync alongside them.\n\n## [2.21.0] - 2026-07-14\n\n### Added — Completion gate: verified-or-retried task execution\n\n- **`adk.gate.CompletionGate`** — a composable wrapper that closes the \"silent\n success\" gap: an agent's ReAct loop returns whatever the model produced and\n calls it done (`finish_reason = stop/max_steps/length`), which is a *liveness*\n signal, not a *completion* one — so an agent can \"succeed\" while doing nothing.\n `CompletionGate` runs the agent, verifies the result against acceptance\n criteria, retries with the failure fed back, and returns the response with\n `finish_reason` set to `verified` or an honest `unverified`. Never a false pass.\n - **Hard-first, un-soft-passable checks** (`adk.gate.hard_checks`): a filesystem\n path a criterion says must exist is checked on disk; a required quoted token\n must appear in the output. Only genuinely subjective criteria fall through to\n an optional LLM judge (reuses the agent's own model), which **fails closed**\n if unavailable.\n - **`gated_run(agent, task, criteria=..., max_retries=2)`** — one-shot helper.\n - `auto_criteria` (LLM writes the machine-checkable definition-of-done from the\n task), a `verifier=` override for custom checks (tests-pass / HTTP probe), and\n a `requires_action` short-circuit so it never fights human-approval pauses.\n - Compose it onto any agent exposing `run(task) -> resp{.content, .finish_reason}`\n (`AitherAgent`, `core.Agent`, or a thin adapter over a remote dispatch).\n - Exported from the top-level package: `from adk import CompletionGate, gated_run`.\n\n## [2.19.0] - 2026-07-12\n\n### Added — Eve agent importer + durable A2A tasks + canonical agent discovery\n\n- **`adk pack import <eve_agent_path>`** — convert external Eve agents to AitherADK packs\n (from GitHub or local filesystem). Maps Eve structure (instructions → system prompt,\n agent.ts → agent.yaml, skills/*.md → skills, connections/*.ts → MCP, tools/*.ts →\n tools/node verbatim) to AitherADK format with a single command. Eve agents (compiled\n manifest format, schema v35) become installable packs compatible with `adk pack install`.\n- **Durable A2A task lifecycle (`TaskManager` backed by FileStore)** — agent-to-agent\n tasks now survive container restarts. Tasks persist as append-only JSONL\n (`.adk/tasks.jsonl`); in-memory dict for fast access; `create_task()` / `update_status()` /\n `add_artifact()` / `add_message()` all save to disk. Google A2A v0.3.0 spec compliant.\n- **Canonical agent card discovery** — `A2AServer.mount()` now exposes:\n - `GET /.well-known/agent-card.json` — Agent Card (canonical; contains agent identity,\n capabilities, skills, endpoints)\n - `GET /.well-known/agent.json` — Agent Card (legacy, redirects with 308 to canonical path)\n - `POST /a2a` — JSON-RPC 2.0 for message send/task lifecycle\n - `GET /a2a/tasks/{id}/subscribe` — SSE streaming task updates\n- **Four built-in agent packs now complete and catalogued**:\n - `hermes` — architecture review & trade-off analysis\n - `openclaw` — web research & source verification\n - `claude-code` — feature development & debugging\n - `analyst` — structured data inference & anomaly detection\n All discoverable via `adk packs` / `adk install pack:<name>`.\n- **Tolerant tool-call JSON parsing** — malformed tool JSON (extra properties, missing\n name/arguments, string-encoded nested args, missing keys) is now gracefully degraded\n instead of silently failing. `_parse_tool_json()` accepts both `\"arguments\"` and\n `\"parameters\"` keys, coerces string-encoded args, and skips unparseable calls instead\n of crashing the turn.\n\n### Known limitations\n\n- Eve agent importer: remote GitHub fetching not yet implemented (local paths only).\n- Durable task store: currently JSONL (suitable for thousands of records, not millions).\n\n## [2.18.0] - 2026-07-10\n\n### Added — AitherShell command center + Claude Code session management\n\n- **`aither sessions`** — Claude Code session manager: interactive full-screen\n browser (type-to-filter, transcript preview, deep full-text `search`, resume\n **in the current terminal** or as Windows Terminal tabs), crash guard\n (`guard install` = at-logon watchdog that snapshots live sessions and\n auto-restores the whole set after a terminal crash), and `ingest [--watch]`\n (incremental, secret-guarded sync of session conversations into the local KB\n / CompanyBrain via the standard ingest pipeline).\n- **`aither hq`** — command-center dashboard: per-service fleet health, LLM\n queue (depth/VRAM/models), Pulse alerts, live session count, inbox unread —\n auto-refreshing, with one-key jumps into chat, sessions, inbox, agents,\n brief, watchtower, and docker recovery.\n- **`aither inbox`** — unified queue over CommCore mail, Relay\n mentions/notifications, and Pulse alerts; open/mark-read/DM-reply from the\n terminal. Every source degrades independently.\n- **`aither agents`** — roster console + `agents ask -e <1-10> <agent> <q>`\n (effort-tiered ask via Genesis, with automatic `/chat/stream` fallback when\n the `/agent/sync` execute gate rejects host callers) + forge dispatch\n inspection + routine health.\n- **`aither palette`** — universal fuzzy picker across actions, sessions, and\n services; **`aither brief`** — renders Atlas's executive briefing in the\n terminal; **`aither watch`** — fleet watchtower with named wedge-signature\n detection (docker-WSL wedge, LLM-queue stall, crash-loop uptime regression)\n and optional docker auto-recovery.\n- All fleet reads go through a shared fail-soft `FleetClient` (internal-CA\n TLS via `adk._tls`, dashboard-grade timeouts, per-source degraded states).\n- Node shell-cli (`@aitherium/shell-cli` 1.13.0): `aither\n sessions|hq|inbox|palette|brief|watch|agents|docker` now pass through to the\n Python shell instead of being intent-classified as chat prompts.\n\n## [2.17.1] - 2026-07-05\n\n### Fully self-service mesh join — no customer key handling\n\n- **Auto-join headscale off the onboard response.** The conductor now auto-issues a\n headscale pre-auth key for NAT'd nodes and returns it in the `/v1/mesh/onboard`\n response; `mesh.join` consumes it and brings up the tunnel automatically. A customer\n runs one command — they never mint, fetch, or set a headscale key. Explicit\n `--headscale-key`/`AITHER_HEADSCALE_AUTH_KEY` still take precedence.\n- **Security:** `_tailscale_up` now redacts the auth key from all logs and exceptions\n (a failed `tailscale up` previously echoed the full argv, leaking the key into logs).\n\n## [2.17.0] - 2026-07-05\n\n### Added — Headscale mesh transport + local Qdrant + AitherConnect onboarding\n\n- **Headscale NAT-friendly mesh transport.** When raw WireGuard UDP:51820 is not\n viable (customer boxes behind NAT, corporate firewalls, CGNAT), `adk mesh join\n --headscale` routes the mesh tunnel through Tailscale's Headscale control plane.\n The overlay IP is still Conductor-assigned (10.77.0.0/16); Headscale provides\n only the transport layer. Configured via `AITHER_MESH_TRANSPORT=headscale`,\n `AITHER_HEADSCALE_URL` (default: https://headscale.aitherium.com), and\n `AITHER_HEADSCALE_AUTH_KEY` (pre-generated key). Automatic fallback to raw\n WireGuard if Headscale setup fails.\n- **Local Qdrant vector DB provisioning.** `adk stack` now provisions Qdrant\n locally when `AITHER_VECTOR_DB=qdrant` is set, enabling offline-first RAG\n without external dependencies. Tiered: local Qdrant in-fleet, cloud Nexus\n for enterprise multi-tenant deployments.\n- **Conductor URL public fallback.** `adk mesh join` resolves the internal\n `aitheros-conductor:8193` hostname; if unreachable (e.g., self-hosted nodes),\n it falls back to the public `conductor.aitherium.com:8193` endpoint,\n eliminating bootstrap configuration friction.\n\n### Documentation\n\n- **Self-hosting runbook.** New `docs/SELF_HOSTING.md` covers deploying AitherOS\n on customer infrastructure with Qdrant + mesh + AitherConnect onboarding.\n- **Mesh transport constraints documented.** Raw WireGuard requires public\n UDP:51820 endpoint (cloud instances, dedicated hardware); Headscale for\n NAT'd networks. Noted in CLI help and module docstrings.\n\n## [2.15.0] - 2026-07-04\n\n### Added — built-in web admin console + portal-profile settings sync\n\n- **Web admin console.** The page `adk up` opens (served at `/` and `/ui`) is now a full,\n self-contained console — Chat plus six admin tabs: **Backend** (view/switch LLM provider,\n set keys, test connection), **Packs** (list/enable/disable/reload tool packs),\n **Sessions** (browse/delete conversations), **Logs** (redacted agent-log tail),\n **Graph** (knowledge-graph search/neighborhood/stats), and **MCP** (add/remove external\n MCP servers). No build step, no CDN — a single packaged HTML asset. The original minimal\n streaming chat page stays at `/chat` for back-compat.\n- **Admin API (`adk/admin_api.py`).** ~20 endpoints under `/admin/*`, all bearer-gated by the\n existing middleware (never in `_skip_auth_paths`). Operates on the live agent — LLM backend\n swap (`LLMRouter.switch_backend`), pack reload, and MCP registration take effect without a\n restart.\n- **Settings sync (`adk/settings_sync.py`).** The user's portal profile is the source of\n truth: on startup the agent pulls `preferences.adk` and applies it over the local\n `~/.aither/config.yaml` cache; admin-console edits push a debounced snapshot back to\n `PUT /api/settings/preferences`. Provider API keys and MCP auth headers are device-local\n secrets and are never included in the pushed snapshot. Opt out with `AITHER_SETTINGS_SYNC=false`.\n\n### Security\n\n- **Timing-safe token comparison.** The server bearer check now uses `hmac.compare_digest`\n (the token is reachable over the public tunnel; a byte-wise `!=` leaked it to timing attacks).\n- **MCP add is SSRF-guarded + confirmed.** Adding an external MCP server is a two-step\n prepare→confirm flow; URLs resolving to loopback / private / link-local / metadata addresses\n are rejected (override with `AITHER_ALLOW_PRIVATE_MCP=1` for trusted local servers).\n- **Config editor is allowlisted.** `PATCH /admin/config` writes only safe scalar prefs and\n hard-denies fields that could execute code or redirect traffic at startup (e.g.\n `aither_toolpack_dirs`, backend base URLs).\n- **Log tail is scrubbed.** Bearer tokens, `#k=` fragments, and `sk-`/`aither_sk_` keys are\n redacted from the log-tail endpoint; the tunnel log is excluded.\n\n## [2.14.8] - 2026-07-04\n\n### Added — `adk up` for normal humans (no API key, no prereqs, live chat)\n\n- **Hosted-brain default — no API key required.** When there's no local model and no\n provider key, `adk up` signs in (device-flow) and routes inference through the gateway\n using the portal token (`AITHER_LLM_BACKEND=gateway` + bearer). A non-technical user\n never needs to obtain or paste an LLM API key. Explicit `--provider`/local model still\n take precedence; unattended `--yes` with no token/backend fails cleanly (exit 3).\n- **cloudflared auto-download.** If the tunnel binary is missing, `adk up` downloads the\n right platform asset to `~/.aither/bin` (SHA256-verified against the release; opt out with\n `AITHER_CLOUDFLARED_REQUIRE_CHECKSUM=0`) instead of exiting with an install hint.\n- **Built-in streaming chat page.** The agent now serves a self-contained chat UI at `/`\n (and `/chat`) with a live \"thinking…\" indicator and token-by-token streaming — so a human\n has somewhere to talk to it with immediate feedback instead of a blank wait. `adk up` opens\n it locally and passes the callback bearer in the URL fragment (`#k=…`), so `/chat/stream`\n stays authenticated (not an open proxy) with nothing to paste.\n- **Human-readable failures.** `adk up` errors now carry a plain-English `next_action`\n (kept machine-readable in `--yes` JSON).\n\n### Fixed\n\n- `adk up` chat link pointed at a non-existent `/settings/agent` route; it now points at the\n agent's own streaming chat page.\n\n## [2.14.7] - 2026-07-04\n\n### Added\n\n- **`adk up` — one command to run a persistent, fleet-connected agent.** Collapses\n the scattered onboarding surface (`host`/`quickstart`/`connect`/`login`/`setup`)\n into a single command that starts `aither-serve` (single agent, identity `aither`\n by default), opens a Cloudflare quick-tunnel for fleet reachability, registers with\n the portal, and installs a reboot-persistent autostart entry (Windows Task Scheduler\n / systemd-user / launchd). Detaches by default so the terminal is freed; `--foreground`\n blocks. Reuses the existing device-flow login, cloudflared discovery, provider-key\n store, and `_preflight_check` backend detection.\n- **Fully non-interactive path for autonomous agents.** `adk up --yes` (or any\n non-TTY invocation) takes ZERO prompts — everything resolves from flags, env, and\n saved config — and emits a single machine-readable JSON status line with meaningful\n exit codes (`0` ok, `2` bad-input, `3` no-backend, `4` tunnel-missing, `5` register-failed).\n Degrades to local-only (warn, not fail) when a portal token is absent unless\n `--require-register` is set.\n- **`adk down` / `adk status --json` companions.** `down` stops the agent + tunnel,\n best-effort deregisters, and removes the autostart entry; `status` reports the running\n agent (liveness + `/health` + tunnel + registration), with `--json` for agents/CI.\n- **`adk stack`** — the previous `adk up` behaviour (supervise the Room + Ollama consumer\n stack) now lives here; `adk up` is the connected-agent command.\n\n### Internal\n\n- New `adk/agent_daemon.py`: detached process spawn, status file\n (`~/.aither/adk-up.json`), pid liveness/teardown, and cross-platform autostart install.\n\n## [2.14.6] - 2026-07-04\n\n### Added\n\n- **`adk.preflight` — self-bootstrap capability probe.** `run_preflight(agent,\n spec)` wakes an agent up knowing what it is actually plugged into: a bounded\n (1.5s) parallel `LivenessProbe` over the router's resolved backends\n (primary/reasoning), the embeddings winner, remote MCP tools, the structured-ML\n / `/ml/teach` endpoints, and voice — returning an honest `CapabilityReport`.\n Status vocabulary distinguishes reachability from entitlement\n (`OK`/`MISSING`/`UNREACHABLE`/`AUTH`/`TIER_DENIED`/`UNSUPPORTED`), a hosted slot\n never prints a bare `OK`, and a required-but-unsatisfied slot aborts before the\n loop instead of blind-firing. Headless task/rules resolution + an allowed-roots\n policy binding (`builtin_tools.set_allowed_roots`, which resets the memoized\n cache so the override actually enforces).\n- **Multimodal (image) messages.** `Message.content` now accepts an OpenAI-style\n content-part list (`str | list[dict]`), passed through verbatim to the\n OpenAI-compatible body (gateway / vLLM / OpenAI). New `adk.llm.multimodal`\n helpers — `image_message()`, `image_content_parts()`, `to_image_url()` — build\n the parts and encode local image bytes/paths into `data:` URIs.\n- **Real vision preflight probe.** The vision slot is no longer unconditionally\n `UNSUPPORTED`: it sends a discriminable image (a solid red square) and requires\n the model to name the color, so a blind/text-only model cannot false-pass. `OK`\n only on a real round-trip; `AUTH` on 401/403; `UNSUPPORTED` when there is no\n OpenAI-compatible provider or the model cannot see the image.\n\n## [2.14.5] - 2026-07-04\n\n### Added\n\n- **`adk.reasoning.mcts` — generic Monte-Carlo Tree Search library.** A reusable\n MCTS engine (ported from the internal `UnifiedMCTS`, with all internal couplings\n removed) that any agent can drive over its own environment:\n - `MCTSEnvironment` protocol (`get_state_hash` / `get_actions` / `step` /\n `evaluate` / `clone`) — implement it and search.\n - Three **optional, default-off** model seams — `TransitionModel`, `PolicyModel`\n (PUCT priors), `ValueModel` — so a learned world-model / policy / value can be\n injected without touching the core; with all three `None` the engine is a plain\n UCT search.\n - `ObservedTransitionModel` adapter — a cheap online lookup world-model (hit =\n exact stored transition; miss = identity + negative \"unknown\" reward +\n `is_uncertain`, biasing exploration toward learning), with JSONL save/load for\n cross-run reuse.\n - `UnifiedMCTS(config).search(env)` returns an `MCTSResult` (best action, value,\n confidence, principal-variation path) and an optional `MCTSTrace`\n (visit-distribution policy target) for distillation.\n- **Self-bootstrapping agent machinery.** Build an agent from a declarative spec\n instead of bespoke wiring:\n - `adk.bootstrap.build_agent_from_spec(path) -> (AitherAgent, RunCtx)` — resolves\n prompts, activates required/optional tool packs, and carries a `memory_map`\n (a `node_type -> (role, tier)` override for memory writes, never a gate).\n - `adk.prompts.PromptBridge` — resolves an agent's prompt bindings from files\n (relative to the spec) or `AitherPrompts` dot-keys (optional import).\n - `adk.packs.PackActivator` — eager activation of installed tool packs by name\n over `register_tool_packs`; a required pack that registers zero tools raises\n `PackUnavailable`, optional packs soft-degrade.\n - `python -m adk.bootstrap.generic_agent <spec.yaml>` inspects a spec (resolved\n name, activated packs + tool counts, prompt keys, memory map) without running a\n live loop.\n- **`deep_research` tool pack** — a full multi-engine web-research workflow as\n `dr_*` agent tools (keyless search, page fetch + cache, site mirror, a citable\n findings knowledge graph, and markdown/PDF/DOCX report generation), activatable\n by name via the tool-pack loader.\n\n## [2.14.4] - 2026-07-04\n\n### Added\n\n- **`structured_ml` tool domain** — zero-shot structured-data inference for agents,\n backed by TabFM (tabular classification/regression) and TimesFM (time-series\n forecasting). Three tools:\n - `tabular_classify(support_rows, target, query_rows)` — classify rows from a\n labeled support set, in-context (no training step; up to 10 classes).\n - `tabular_regress(support_rows, target, query_rows)` — predict a numeric target.\n - `timeseries_forecast(series, horizon)` — forecast future values of a series.\n\n The tools POST to a structured-ML inference service resolved from\n `AITHER_STRUCTURED_ML_URL` (default `http://localhost:8192`), reject non-http(s)\n URLs, and summarise oversized responses to protect the agent's context.\n- **`Capability.STRUCTURED_INFERENCE`** capability token and a new **`analyst`**\n identity/pack that ships the `structured_ml` domain by default. The domain is\n opt-in — only identities that enable the `structured_ml` category get the tools.\n\n## [2.14.3] - 2026-07-03\n\n### Fixed\n\nAll four bugs below were found by actually booting `adk run` against a real\nchat model and driving a real tool-calling request end-to-end (not inferred\nfrom reading the code) — each one silently broke the \"local agent uses its\nconfigured model and its tools actually work\" path.\n\n- **`AITHER_MODEL` / `config.model` silently ignored by the OpenAI-compatible\n backend**: `LLMRouter.__init__` never read `config.model` into `self._model`\n unless the caller passed `model=` directly to the constructor — which nothing\n did. Every explicit model choice (env var, config file) was dropped, the\n provider fell back to a hardcoded per-backend default (`gpt-4o-mini` for the\n `openai` backend), and an unrecognized model name got silently remapped\n server-side to the wrong backing model. Concretely: setting\n `AITHER_MODEL=aither-orchestrator` had no effect — every request actually\n went to a different model that was never meant to be called directly and\n can't reliably use tools.\n- **`OPENAI_BASE_URL` ignored for explicit `--backend`/`AITHER_LLM_BACKEND`\n selection**: `LLMRouter._auto_detect()`'s explicit-backend branch hardcoded\n `base_url=None` when constructing the provider, instead of reading\n `config.openai_base_url` (which two other call sites in the same file\n already did correctly). Any self-hosted OpenAI-compatible endpoint\n (MicroScheduler, vLLM, LM Studio) configured via `OPENAI_BASE_URL` was\n silently ignored in favor of `https://api.openai.com/v1`.\n- **Tool-call steering never engaged on default-effort chat**: the retry that\n nudges a model back into structured tool-calling when it responds with prose\n instead of a function call was gated behind `effort >= 6`. Default `effort`\n for a plain `agent.chat(message)` call resolves to `5`, so the safety net —\n built specifically for this failure mode — never fired for default requests.\n Also now forces `tool_choice=\"required\"` on the retry instead of relying on a\n text nudge alone.\n- **Every non-string builtin-tool parameter silently typed as `\"string\"` in\n the generated tool schema**: `builtin_tools.py` uses\n `from __future__ import annotations`, so `_extract_parameters()` in\n `tools.py` was reading postponed (string) type hints (`\"int\"`, not `int`)\n straight out of `fn.__annotations__`, which never matched\n `_type_to_schema`'s type-object keys and silently fell through to\n `{\"type\": \"string\"}`. Every builtin tool with an `int`/`float`/`bool`/`list`\n parameter was affected — e.g. `file_read(start_line: int, end_line: int)`\n advertised both as strings, so a model correctly calling the tool with\n `\"start_line\": \"0\"` crashed the actual implementation with\n `unsupported operand type(s) for -: 'str' and 'int'`. Fixed by resolving\n hints through `typing.get_type_hints()` instead of the raw annotations dict.\n `_coerce_arguments()` — the runtime safety net that's supposed to coerce a\n model-supplied `\"0\"` string into `0` before a tool call executes — had the\n exact same raw-`__annotations__` bug and silently no-opped for every builtin\n tool; fixed the same way.\n- **Streaming tool-loop could hang a connection forever**: `chat_stream()`'s\n fallback to the sync tool loop (tool calls can't stream token-by-token) had\n no timeout, so a model that didn't cleanly terminate a tool-calling round\n trip left the SSE connection open indefinitely with zero bytes sent. Bounded\n with a 60s `asyncio.wait_for`; on timeout the stream yields a clear message\n instead of hanging.\n\n## [2.14.2] - 2026-07-03\n\n### Fixed\n- **AitherShell session amnesia**: the REPL generated a fresh `session_id` for\n EVERY message, so conversation history never followed across turns (the\n server stored each turn under a different session). One stable id is now\n minted per shell run; `/resume` and `/new` semantics unchanged, and the\n cross-restart auto-restore of the last session now actually works.\n\n## [2.14.1] - 2026-07-02\n\n### Added\n- **Account-tied licensing via device-flow login** — `adk login` now persists the account\n license returned by the device-flow handshake to `~/.aither/license.json` (base64\n `license_key` → `{payload, signature}` envelope), so entitlements from the authenticated\n account carry into every later `adk` invocation without re-auth. Pairs with the fleet-side\n node tool-pack gate that grants packs by plan-tier rank.\n- **`can_use_formbridge` / `can_use_untether` entitlements** (PROFESSIONAL+ rank ≥ 3) backing\n the FormBridge and UNTETHER agent tool-packs.\n\n### Fixed\n- `adk` CLI: guard the optional `memory_graph` singleton (was a latent `NameError` on the\n `/stats`, `/memory`, and save paths when memory was not constructed).\n\n## [2.14.0] - 2026-07-02\n\n### Added\n- **`adk.memory_wiki` — LyraWiki-style self-managed semantic knowledge** (opt-in; nothing constructs it by default): `MemoryWiki(graph_memory)` maintains curated wiki ARTICLES as `wiki_article` nodes (title/slug, LLM-owned markdown with `[[wikilinks]]`, embedding, durable tier, `CONSOLIDATED_FROM` edges to source memories, governance-ledgered revisions).\n - `consolidate(llm, since=, budget=)` clusters unconsolidated episodic/fact nodes (embedding + keyword), drafts/UPDATEs articles via the INJECTED llm callable (`str -> str` or `messages -> str`, sync or async — no hard model dependency), cites source node ids, marks contradicted sources superseded, then DEMOTES consolidated sources to a fast-decay tier via `promote()` (represented knowledge doesn't need raw copies).\n - `lint(llm=None)` — deterministic health checks (orphan wikilinks, empty/stale articles, live-but-contradicted pairs) + an optional LLM audit pass.\n - `prune(relevance_floor=0.05, hard_delete_after_days=14)` — article relevance = tier freshness × reinforcement (reinforce-on-recall) + link-degree to LIVE memories; below the floor → reversible governance tombstone; entombed past the window → **HARD-DELETE** (the tombstone snapshot itself is purged — content and embedding irrecoverable; the true-deletion path). A node is never deleted without a tombstone first.\n - `recall(query, limit)` — article-first RAG (curated articles rank above raw nodes) that reinforces returned articles.\n- **`adk.routines` — agent heartbeat + self-programmed routines**: `RoutineStore` is a durable registry (`~/.aither/routines/{agent}.json`, `AITHER_DATA_DIR`-aware, injectable path) of cron-scheduled self-prompts `{name, cron, instruction, enabled, last_run, last_result, tags}` that rehydrates into the existing `adk.cron.CronScheduler` on `start()`. A fire runs `agent.chat(instruction)` with the agent's full toolset — the agent programs its future self with its own adk. Guardrails: `max_routines` (12), a 5-minute min fire interval per routine, a per-fire timeout, and every result ledgered (truncated) to `last_result`. Direct-callable routines (`register_direct`) fire a bound method instead of a self-prompt. Self-management tools (`routine_create/list/update/pause/resume/delete/run_now`) ship as OpenAI tool defs + handlers (`build_routine_tools` / `register_routine_tools` / `routine_tool_defs`); the handlers ONLY touch the RoutineStore — they can never modify agent config or safety-relevant settings (the leash principle).\n- **`AitherAgent(routines=True, memory_maintenance=True)`** (both default **False** → the default agent is byte-identical): `routines=True` builds the RoutineStore + scheduler, registers the self-management tools, and starts the heartbeat lazily on the first `chat()` (or via explicit `start_routines()`). `memory_maintenance=True` registers DEFAULT routines — `wiki_consolidate` (every 2 h), `wiki_lint` / `wiki_prune` / `graph_sweep` (daily) — as DIRECT method fires so memory upkeep runs even on tiny models, while staying visible/manageable via `routine_list`.\n- `TombstoneStore.purge(tombstone_id)` — the hard-delete endpoint: irrecoverably removes a tombstone snapshot and rewrites the persisted store without it (consumed by `memory_wiki.prune`).\n\n## [2.13.6] - 2026-07-02\n\n### Added\n- **Qdrant backend for GraphMemory dataplane sync — TRUE two-way, verified live.** Set `AITHER_FLEET_QDRANT_URL` and the sync uses Qdrant (`upsert` + `scroll` + filtered `search`) instead of Nexus. This is what actually delivers cross-agent memory sharing: a node is upserted with its OWN embedding under a deterministic per-(tenant,node) point id (idempotent re-push), and `fleet_pull` reliably SCROLLS a tenant's points back — the enumerate Nexus lacks (its `/search` doesn't return ingested docs and `/export` needs lancedb, which has a hard dependency conflict in the fleet). **Proven live with 2 agents:** agent-1 ingests → auto-syncs to the tenant dataplane; a fresh agent-2 in the same tenant `fleet_pull`s all of agent-1's nodes and can search them; a different tenant pulls nothing (isolation). Dimension-safe (never mixes vector dims in a collection). Local SQLite stays source of truth; best-effort, never raises.\n\n## [2.13.5] - 2026-07-02\n\n### Fixed\n- **CRITICAL — GraphMemory dataplane sync push was 100% failing.** `_fleet_push_node` sent `source_type=\"graph\"` / `content_type=\"graph_node\"`, which AitherNexus rejects as invalid enums (HTTP 500) — so NO graph node ever replicated in 2.13.4. Now sends the accepted `source_type=\"manual\"` / `content_type=\"text\"` (graph provenance is carried in `metadata.synced_from`). **Verified live** against a fleet Nexus: a 4-node ingest replicates with `pending==0` and the collection count reflects it.\n\n### Added\n- **Swarm-awareness notification** — on a successful sync batch, `GraphMemory` emits a best-effort `graph.synced` event (tenant/workspace/agent/collection/count) to `AITHER_FLEET_EVENTS_URL` so OTHER agents in the tenant/swarm can pull the fresh data and deconflict in-flight work (push-based awareness instead of polling). No-op unless the env is set.\n\n### Known limitation\n- Cross-agent **rehydration** (`fleet_pull`) is only reliable when the fleet Nexus is backed by lancedb (persistent + exportable). An in-memory Nexus stores on `/ingest` but neither its `/search` nor `/export` returns the docs — two-way read needs the persistent Nexus.\n\n## [2.13.4] - 2026-07-02\n\n### Added\n- **Memory-maintenance primitives on `GraphMemory`** (all opt-in; default behaviour byte-identical):\n - **Reinforce-on-recall**: `recall_with_activation(..., reinforce=True)` bumps `reinforcement_count`/`last_reinforced` on every RETURNED node's metadata mirror (one UPDATE per node, WAL-safe) — making the `MemoryRecord` reinforcement fields the activation scorer reads LIVE. Default `False` = zero writes.\n - **`sweep(now, archive_below=0.05, max_nodes=None, dry_run=False)`**: read-time decay enforced at rest — archives nodes past their tier TTL with freshness×reinforcement score below `archive_below` and `reinforcement_count <= 1`; `max_nodes` additionally archives the lowest-scored overflow. PERMANENT tier immune. Every archive is a reversible governance tombstone (`TombstoneStore.recover`) + a FORGET ledger entry; a node is never deleted without a tombstone. `dry_run` returns the would-archive list without acting.\n - **`promote(node_id, tier=?, role=?)`**: re-tier/re-role a node IN PLACE (tier/role columns + metadata mirror) — edges, embedding and ledger history survive; ledgers an UPDATE entry when governance is on.\n- `MutationType.UPDATE` ledger entry kind (in-place re-tier/re-role).\n- **Per-tenant dataplane sync for `GraphMemory`** — replicate the local graph to a per-tenant vector dataplane (Nexus/Qdrant) so a sovereign agent's memory is durable + rehydratable across restarts and hosting boundaries. `fleet_push_all_nodes()` (catch-up), `fleet_pull()` (best-effort semantic top-up), `fleet_sync_pending()`, and auto-sync on ingest (`_maybe_autosync` + `drain_sync()`). **On by default** (`AITHER_FLEET_SYNC=auto`; only `false/0/off` disables) with an inferred target (local Nexus `:8122` or the gateway in cloud mode) and a SEPARATE collection (`AITHER_FLEET_GRAPH_COLLECTION`, default `graph_memory`) from the KV memory sync. New `GraphMemory(tenant_id=, workspace_id=, fleet_url=, auto_sync=)` constructor args override env (required for multi-tenant host processes). Node `synced` column + schema v4 migration. TLS verify is CA-bundle-aware (`AITHER_CA_BUNDLE`) for the internal-TLS mesh. Local SQLite stays the source of truth; every sync path is best-effort and never raises into ingest.\n\n## [2.13.3] - 2026-07-02\n\n### Added\n- **Canonical self-deploying embeddings provider** (`adk.embeddings`): one embedding provider for the whole SDK so vectors are portable across scopes (platform / tenant / sovereign) — same model + dimension everywhere. Pinned to `nomic-embed-text` (768-d; the vLLM `--served-model-name` for nomic-embed-text-v1.5). Lazy, single-flight resolution chain: explicit `AITHER_EMBEDDINGS_URL` → local vLLM (`:8209` then `:8120`, HTTPS-then-HTTP) → local Ollama → gateway (`AITHER_GATEWAY_EMBEDDINGS_URL`) → auto-deploy a local vLLM embeddings container if a GPU + Docker are present (opt out with `AITHER_EMBED_AUTODEPLOY=0`) → CPU sentence-transformers (384-d, degraded) → feature-hash (384-d, degraded, always works). Every batch is dimension-tagged so callers never silently mix 768-d and 384-d vectors. `get_default_embedder()`, `embed_texts()`, `embed_one()`, `get_provider()`, `reset_provider()`.\n\n### Changed\n- **`GraphMemory` now defaults its embedder to the canonical provider** (opt out with `AITHER_GRAPH_EMBEDDER=legacy`), so every adk agent shares one 768-d space. Added a meta-table dimension guard: the index is pinned to its first embedding's dimension and later different-dimension vectors are refused (kills 768↔384 mixing that silently poisons cosine similarity).\n\n## [2.13.2] - 2026-07-01\n\n### Added\n- **Session persistence**: AitherShell now auto-saves the active session_id to `~/.aither/config.yaml` after each chat turn and auto-restores it on the next shell launch. Multi-turn conversations survive across restarts — the messages were always persisted in SQLite + JSON, only the session pointer was missing.\n- `/new` command to start a fresh session (clears saved session_id).\n- `/sessions` command to list recent sessions with agent name, message count, and timestamp. Resume any with `/resume <id>`.\n\n## [2.13.1] - 2026-07-01\n\n### Fixed\n- **CRITICAL:** `deploy_connect()` now sends a HEAD request to verify the GitHub release asset exists before downloading, with a clear error if the asset is missing.\n- **CRITICAL:** Compose file URLs are now pinned to a stable commit hash (configurable via `AITHER_COMPOSE_PIN` env) instead of tracking the mutable `main` branch.\n- **CRITICAL:** `deploy_adk_node()` now falls back to starting `adk-serve` natively when Docker is unavailable, instead of blocking with \"Docker not installed\".\n- **HIGH:** GHCR login failure now blocks deployment immediately with actionable guidance, instead of warning and continuing to a confusing image-pull failure.\n- **HIGH:** Health checks now stream the last 30 lines of Docker Compose logs on failure, so the user can see why a container didn't start.\n- **HIGH:** vLLM port allocation now probes for conflicts before assigning; if port 8200 is busy, workers are automatically remapped to the next free port.\n- **MEDIUM:** Compose file downloads are now cached with ETag; re-running `adk deploy node` skips re-downloading an unchanged file.\n- **MEDIUM:** DGX Spark discovery now respects `AITHER_DGX_HOSTS` env (comma-separated) for custom network topologies, instead of only checking `spark.local` and `192.168.0.33`.\n- **MEDIUM:** Cloud API keys (Anthropic, OpenAI, DeepSeek) are now validated with a lightweight probe during `adk setup` infra scan, so invalid keys surface early.\n- **MEDIUM:** `deploy_connect()` now validates the extracted extension has a `manifest.json` and warns if it's missing.\n\n## [2.13.0] - 2026-07-01\n\n### Removed\n- **`adk.faculties.memory_graph`** — the pickle-based `MemoryGraph` module is deleted. `from adk.faculties import MemoryGraph` now resolves to the canonical `adk.graph_memory.GraphMemory` for back-compat.\n- **`adk.platform.memory`** — the entire legacy memory subsystem (`MemoryManager`, `UnifiedMemorySystem`, `GameEngine`, `StoryboardEngine`, `AnchorGenerator`) is deleted. Agent memory is `adk.memory.Memory` + `adk.graph_memory.GraphMemory`.\n\n### Changed\n- `adk work` command now uses `GraphMemory` instead of the removed pickle-based faculty graph.\n- `adk.MemoryGraph` and `adk.faculties.MemoryGraph` lazy exports now resolve to `adk.graph_memory.GraphMemory`.\n- `agent.set_memory_graph()` kept for back-compat but documented as deprecated; agents already wire `GraphMemory` automatically in `__init__`.\n- Platform startup (`adk.platform.infrastructure.startup`) gracefully handles missing `MemoryManager`.\n- Platform CLI module listing and health check updated to reflect removed memory module.\n\n## [2.12.7] - 2026-07-01\n\n### Deprecated\n- `adk.faculties.MemoryGraph` — emits `DeprecationWarning` on instantiation; use `adk.graph_memory.GraphMemory` (SQLite-backed, governed, embedding-native) instead.\n- `adk.platform.memory` package — the legacy `MemoryManager`, `UnifiedMemorySystem`, `GameEngine`, and `StoryboardEngine` modules now emit `DeprecationWarning` on import; use `adk.memory.Memory` and `adk.graph_memory.GraphMemory` for new code.\n\n### Changed\n- Updated `adk.faculties` docstring and usage examples to point at the canonical memory surface.\n\n## [2.12.6] - 2026-07-01\n\n### Changed\n- Trimmed public package debt by removing grid runbook artifacts and shell/room binary placeholder directories from published artifacts.\n- Removed stale public-sync allowlist entries for deleted phase test files and added explicit public-sync guards for binary placeholder directories and grid artifacts.\n- Updated the public README grid note so it no longer links to runbook files excluded from the SDK package.\n\n## [2.12.5] - 2026-07-01\n\n### Added\n- Added best-effort fleet memory sync from local ADK memory into Nexus/Qdrant-compatible RAG stores, including batch catch-up and vector-search recall hooks.\n- Added saved config/env support for fleet memory URL, collection, and sync mode.\n\n### Changed\n- Excluded FormBridge runtime/test internals from public ADK packaging and public repo sync.\n\n## [2.12.4] - 2026-07-01\n\n### Added\n- Added a hot-reload endpoint for installed agent packs so newly applied pack tools can be picked up without restarting `aither-serve`.\n\n### Fixed\n- Fixed synchronous `ChatRelay` calls on Python 3.12 when no event loop is running; async WebSocket delivery is still scheduled when a loop exists.\n- Aligned gateway inference tier tests with the current public ladder: free, starter, pro, enterprise, platform.\n\n## [2.12.0] - 2026-06-28\n\n### Added — one-command local inference + upgrade path (click-to-run)\n- **`adk quickstart-local`** — detects hardware, picks a backend\n (Ollama / llama.cpp / vLLM), installs it, downloads a model, verifies, and\n prints next steps. Bootstraps Ollama itself via winget/brew/install.sh when\n missing (no \"go install it\" dead-ends). Default model `gemma4:e2b`.\n- **`adk backend switch <ollama|llamacpp|vllm>`** (+ `backend status`) — migrate\n the active deployment between engines; re-points config and smoke-tests.\n- **`adk install pack:<name>`** (+ `adk install list` / `adk packs`) — install\n ready-made agent packs (openclaw, hermes, claude-code) into `~/.aither/agents`.\n\n### Fixed (found via live end-to-end testing on Windows + CUDA)\n- quickstart-local reported success on a dead endpoint (smoke test was warn-only);\n a failed smoke now fails the command, with a service-readiness wait.\n- `--model` resolved to `None` (getattr default never fired) → uses the real default.\n- Smoke test: 30s→180s timeout for cold model load; accepts reasoning-model output\n (empty `content` at low token budgets is no longer a false failure).\n- `adk install pack:<name>` colon syntax (argparse rejected it before dispatch).\n- `adk backend status` health probe used a nonexistent `/health` path → 404; now\n probes `/v1/models` (uniform OpenAI-compatible liveness for all three backends).\n- Windows: removed non-ASCII glyphs from printed strings that crashed the cp1252\n console (a `✓` even made a successful install report failure).\n\n## [2.11.1] - 2026-06-14\n\n### Fixed — `adk host` / `adk login` device flow (autonomous onboarding)\nFound driving `adk host` against production; all broke real device-flow login:\n- **Device flow hit the portal FRONTEND, not AitherIdentity.** `adk login`/`adk host`\n POSTed `/auth/device/code` to portal/veil.aitherium.com, which 307-redirects API auth\n to `/login`. Added `_resolve_identity_url()` mapping the aitherium.com topology to\n `idp.aitherium.com` (the Identity API); localhost/bespoke pass through unchanged.\n- **Cloudflare 403'd urllib.** `idp.*` sits behind Cloudflare, which blocks urllib's\n default `Python-urllib/3.x` UA. Device-code + poll requests now send a real\n User-Agent + Accept (verified urllib→idp 200, was 403).\n- **`adk host` now self-authenticates via device flow** when there's no token, and\n **retries via device flow on a 401/403** (a stale saved token no longer dead-ends\n registration).\n\n## [2.11.0] - 2026-06-14\n\n### Added — attach your own MCP \"hands\" to a self-hosted agent\nA self-hosted agent now has the full brain (your model) · body (this loop) · **hands**\n(your tools) trifecta. The AitherOS platform relays the MCP servers you registered for\nyour agent in the `/stream` request body as `mcp_endpoints`, and the loop wires their\ntools into the ReAct turn:\n- **`adk/mcp_endpoint_tools.register_mcp_endpoint_tools(agent, endpoints)`** — speaks MCP\n JSON-RPC (`tools/list` to discover, `tools/call` to invoke) and registers a namespaced\n proxy tool (`{endpoint}__{tool}`) for each advertised tool. Mirrors `app_proxy_tools`.\n Best-effort: an unreachable endpoint registers nothing and never raises.\n- **`POST /stream` and `POST /chat/stream`** accept an optional `mcp_endpoints` list\n (`[{name, url, [headers]}]`); `_aitheros_stream` attaches them to the agent before the\n turn. Existing callers are unaffected (the field is optional).\n\n## [2.10.1] - 2026-06-14\n\n### Fixed — self-hosted `aither-serve` was crash-on-startup (2.10.0 regression)\nA fresh `pip install aither-adk && aither-serve --backend deepseek` could not boot. Found\nand fixed while standing up a real self-hosted DeepSeek agent end-to-end:\n- **Startup `NameError: name 'port' is not defined`** in three lifespan helpers\n (`_join_aithernet`, `_init_a2a_server`, `_start_mesh_hosting`) — they referenced a bare\n `port` no longer in scope. Now use `config.server_port`, and `main()` writes the resolved\n `--port`/`--host` back to `config` so every lifespan helper and the invoke_url see the\n actual bound port.\n- **Fleet self-registration crashed the whole server** — the registration handlers caught\n `(ImportError, RuntimeError, OSError, ConnectionError)` but **not** `httpx.HTTPError`, so a\n `RemoteProtocolError`/connect error talking to the control plane took the agent down. All\n network-facing lifespan handlers now also catch `httpx.HTTPError` (non-fatal, as intended).\n- **`--backend` was ignored when `AITHER_API_KEY` was set** — `_auto_detect` picked the cloud\n gateway before the explicit backend, and `_try_elysium_fallback` then replaced the chosen\n provider. An explicit backend (deepseek/openai/anthropic/your own vllm/ollama) now always\n wins; \"auto\"/\"gateway\" still flow through detection. The operator's own brain is never\n silently hijacked.\n- **Approved-tool trace truncated after `/sessions/{id}/confirm`** — the SSE relay treated\n `AgentResponse.tool_calls_made` (a `list[str]`) as dicts (`tc.get(...)`), raised\n `AttributeError`, and an over-narrow `except` swallowed it so the client saw only\n `session_start` + heartbeats. The relay now handles string tool names, the `except` is\n broad (always emits a typed `error` + terminal `complete`), and the module-level\n `_strata_ingest` `NameError` + Chronicle `session_id` kwarg mismatch are fixed.\n\n## [2.10.0] - 2026-06-13\n\n### Added — human-in-the-loop tool approval (`adk/approval.py`)\n- **Pause before a gated tool, resume on decision.** An agent can now pause its turn before\n executing a sensitive tool and wait for a human's allow/deny — so a managed control plane\n (or any operator) can gate tool use without forking the loop.\n- **Policy via `AITHER_TOOL_APPROVAL`** — a comma-separated list of tool names (optionally\n `agent:tool`), or `*` for every tool. Empty = no gating (unchanged behavior).\n- **Survives restarts / days-later approvals.** The paused turn (user message + pending tool\n calls) is persisted to disk keyed by `session_id`; resume is a fresh request, not a held\n connection. Decisions are recorded per `(session_id, tool_name)`.\n- **`AgentResponse.requires_action` / `.pending`** — set when a turn paused; `pending` lists\n the gated tool calls awaiting a decision.\n- **`AitherAgent.resume(session_id, decisions)`** — records the decisions and re-runs the\n paused turn (the gate consumes them: allowed tools execute, denied tools get a denial\n observation, the turn continues — and may pause again on a different gated tool).\n- **Server:** `POST /sessions/{id}/confirm` records decisions and streams the resumed turn;\n `/stream` emits a `requires_action` event when a turn pauses.\n\n## [2.9.0] - 2026-06-13\n\n### Added — universal LLM continuation primitive (`adk/llm/continuation.py`)\n- **`run_continuation()` + `stitch()`** — one shared continue-until-complete primitive. When a\n completion stops because it hit the output token cap (`finish_reason == \"length\"`), it continues\n the generation and STITCHES the chunks (overlap-dedup + restart-detect) into a complete answer,\n instead of every call-site hand-rolling its own retry. Bounded three ways (max rounds AND total\n output ceiling AND a no-growth break), and it never mutates the caller's message history.\n- **`LLMProvider.chat_with_continuation()`** and **`LLMRouter.chat_with_continuation()`** — every\n provider AND the router inherit the primitive, so any call-site gets continuation for free.\n- Kill-switch `ADK_LLM_CONTINUATION=off`; tunables `ADK_LLM_MAX_CONTINUATIONS`,\n `ADK_LLM_MAX_TOTAL_OUTPUT_TOKENS` (defaults: 2 rounds / 8192 tokens).\n\n### Changed\n- The ReAct loop's truncation recovery now uses the shared primitive. The previous inline\n doubling-retry REPLACED the partial response (losing earlier text in `resp.content`) and injected\n continuation scaffolding into the live message history; it now STITCHES and leaves history clean.\n Tool-call turns are never continued as text.\n\n### Included — previously-committed-but-unreleased changes\n- 2.8.2: TLS verification on by default (`verify=False` removed).\n- 2.8.1: baked license + pack-signing public keys.\n- 2.8.0: version-bump release.\n\n## [2.7.0] - 2026-06-05\n\n### Added — provider-agnostic LLM cost levers (at the `LLMProvider` ABC)\n- **Prompt caching.** New `ProviderCapabilities` (`prompt_cache: explicit|automatic|none`, `batch`) and\n normalized `LLMResponse.cache_read_tokens`/`cache_write_tokens`, so the \"tokens saved\" meter never\n depends on which backend served the turn. Anthropic inserts `cache_control` breakpoints over the stable\n system+tools prefix; OpenAI/DeepSeek caching is automatic and their `cached_tokens` are now surfaced\n (previously discarded). Default-on in `AitherAgent.chat`; override with `cache=False`.\n- **ResponseCache** (`adk/llm/cache.py`) — exact-match response cache with a pluggable backend, wired into\n `LLMRouter` as an opt-in (`LLMRouter(response_cache=…)` + per-call `cacheable=True`). OFF by default — no\n determinism change unless opted in.\n- **Gemini provider** (`adk/llm/gemini.py`) with explicit `cachedContents` caching; registered in the router.\n- **Async batch runner** (`adk/llm/batch_runner.py`) — `run_batch()` over the Anthropic Message Batches /\n OpenAI Batch APIs (~50% off) with a transparent fallback to concurrent `chat()` where unsupported.\n- **Graph-RAG retriever interface** (`adk/graph_retriever.py`).\n\n### Added — principal authority (prompt-injection defense enforced outside the model)\n- `adk/auth.py`: `Principal` / `AuthContext` / `PrincipalResolver` (+ deny-all default).\n- `ToolRegistry.execute(name, args, auth=…)` — a default-deny authorization gate at the tool boundary, so a\n forbidden tool call is blocked regardless of what the LLM emits. `ToolDef` gains `required_clearance` /\n `action_class`. Threaded through `AitherAgent.chat`; `auth=None` is fully backward-compatible.\n- `adk/channel_auth.py`: Telegram webhook-secret + CEO-id checks and HMAC-signed approvals.\n\n## [2.6.4] - 2026-06-05\n\n### Fixes\n- **Loop-guard nudges corrupted the message history → DeepSeek 400 mid-run.**\n When the ReAct loop emitted multiple tool calls in one assistant turn and the\n loop guard fired a WARN/BLOCK/CIRCUIT_BREAK on one of them, it spliced a\n standalone `system` nudge message *between* the tool results. DeepSeek (and\n strict vLLM chat templates) require an assistant `tool_calls` message to be\n followed by exactly one `tool` result per `tool_call_id`, contiguously — so the\n interleaved message produced `400 … insufficient tool messages following\n tool_calls message` and aborted the turn (this is reliably hit after crossing\n the 15-call circuit-break threshold, where every subsequent call WARNs).\n Per-call guidance is now folded into that call's own `tool` result, and any\n standalone steering is deferred to a single message AFTER the whole batch —\n preserving the `tool_calls`↔`tool_results` pairing every backend requires.\n Regression test: `deep-research-agent/tests/test_msg_structure.py`\n (loopguard-flood scenario).\n\n## [2.6.3] - 2026-06-04\n\n### Fixes\n- **Transient provider errors killed a run on the first blip.** A single\n `502 Bad Gateway` / `503` / `529 overloaded` / `429` from the upstream API\n aborted the whole request — the Anthropic provider had **no retry at all**, and\n the OpenAI-compatible provider only retried `429`. Both now retry transient\n statuses (`408/429/500/502/503/504/529`) with `Retry-After`-aware exponential\n backoff (3 attempts), and surface the provider's body if all retries are\n exhausted. Non-transient `4xx` (e.g. `400`) still fails fast. Applies to\n `chat` and `chat_stream` on both providers.\n\n## [2.6.2] - 2026-06-04\n\n### Fixes\n- **DeepSeek (and strict OpenAI-compatible backends) 400'd on long tool\n conversations.** The ReAct loop legitimately injects mid-conversation\n `system` steering messages (the \"[DIMINISHING RETURNS]\" hint after several\n low-output tool iterations, loop-guard nudges). OpenAI tolerates a non-leading\n `system` message; DeepSeek rejects it with a 400. Long research runs\n (many `fetch_url` calls) reliably tripped this. Fix: non-leading `system`\n messages are demoted to `user` at the OpenAI-compatible provider boundary\n (`_demote_nonleading_system`) — steering intent preserved, payload valid on\n every backend; leading system prompt(s) untouched.\n- **Opaque HTTP errors.** `resp.raise_for_status()` discarded the provider's\n response body, so a 400/422 surfaced as a bare `Client error '400 Bad\n Request'` with no cause. `_ensure_ok` now includes the provider's actual\n message (e.g. `context_length_exceeded`, `Invalid 'messages'`) in the raised\n error, for both `chat` and `chat_stream`.\n\n## [2.6.1] - 2026-06-04\n\n### Fixes\n- **`MCPServer.mount()` returned HTTP 422 for every `/mcp` call.** Under\n `from __future__ import annotations` the route handler's `request: Request`\n annotation is a string that FastAPI resolves against the *module* globals, but\n `Request` was imported locally inside `mount()` — so FastAPI couldn't resolve\n it and treated `request` as a required query param. The MCP server endpoint\n (`initialize`, `tools/list`, `tools/call`) was unusable when mounted. FastAPI\n symbols are now imported at module level (guarded; the `[node]` extra still\n gates actual use). Any agent's tools can now be exposed as a working MCP\n server with `MCPServer(...).mount(app)`.\n\n## [2.6.0] - 2026-06-04\n\n### Streaming + scaffolding\n- **`AitherAgent.stream_chat`** — reliable streaming sibling of `chat()`: native\n tool-calling loop (concludes on its own, unlike the text-protocol `stream_react`)\n that emits live `tool`/`tool_result` events and streams the answer as `token`\n events. `token_delay` paces tokens for a typing effect.\n- **`adk new <template>`** — scaffold a full, runnable template app. Ships the\n `deep-research` template (a cited web-research agent: pack + server + web UI):\n `adk new deep-research`.\n\n### Fixes\n- **BYO-key no longer community-capped.** The free-tier monthly token cap now applies\n only to the metered Aitherium gateway backend, never to a user's own provider key\n (Anthropic/OpenAI/DeepSeek/Ollama/vLLM). Previously it could brick a self-hosted\n agent after ~100k tokens on the user's own key.\n- **Stronger, grounded memory recall.** `chat()` now injects the top-6 knowledge-graph\n hits (untruncated) with a hard no-fabrication instruction — was top-3 truncated to\n 200 chars, which surfaced partial facts and let weaker models invent the rest.\n- **Tool-arg coercion.** `ToolRegistry.execute` coerces LLM-supplied args toward each\n parameter's annotated type, so a tool no longer crashes when a model passes a list\n or string where an int/float/bool was expected.\n\n### Docs\n- New `docs/AGENT_DEV_GUIDE.md` — the golden path for building agents/packs.\n\n## [2.5.0] - 2026-06-04\n\n### Continual learning — agents that learn from runs and reuse memory\n\nAgents now improve the second time they do a task instead of starting from scratch.\n\n### Added\n- **Recall-before**: `AitherAgent.chat()` searches the local `SkillStore` and injects the\n top matching learned skills as a system block, so the model reuses proven procedures.\n- **Learn-after**: `_learn_after()` runs after each response — reinforces (`success_count++`)\n any skills that were recalled+reused, and extracts + saves a NEW skill from successful\n multi-tool runs via `SkillExtractor`. Wired at both `chat()` return paths.\n- Gated by `AITHER_SKILLS` (default on). The previously-unused `SkillStore`/`SkillExtractor`\n are now connected to the run loop.\n\n### Notes\n- Pairs with the platform-side LearnedProcedure substrate (recipes → scored procedures →\n skills/tools/A2A/packs → MCTS priors → evolution). The SDK shares memory via the Spirit\n bridge when configured.\n\n## [2.0.0] - 2026-06-02\n\n### Open-core boundary (BREAKING)\n\nThis release draws a single, enforced free/paid line. The free tier stays\ngenuinely useful (real agent, typed + graph + code memory, ReAct, ~essential\ntools); paid tiers unlock the *scale* capabilities via portal.aitherium.com.\n\n### Added\n- **`adk/licensing.py`** — the entitlement keystone. Resolves a tier\n (COMMUNITY by default), verifies optional portal-signed `~/.aither/license.json`\n (Ed25519), and answers `can_use_fleet/channels/cron/swarm/auto_neurons`,\n `is_agent_licensed`, `max_effort`, etc. **Fail-closed**: no/invalid/expired\n license → free tier; an unsigned license never grants premium.\n `AITHER_TENANT_SLUG=aitherium` → unrestricted INTERNAL tier.\n- **Moat guard** (`scripts/check_moat_boundary.py`) wired into the publish\n workflow — blocks any wheel that leaks the moat or drops the keystone.\n- Tests: `tests/test_licensing.py`, `tests/test_moat_boundary.py`.\n\n### Changed (gating — fail-closed)\n- Free tier effort is capped at 3 (reasoning effort 7–10 needs Professional).\n- Monthly token budget hard-block on the free tier (no silent overspend).\n- Gated behind a paid tier: fleet mode (`FleetConfig`), cross-agent delegation\n (`AgentForge.delegate`), channel adapters, cron scheduler, proactive\n auto-neurons, and `swarm`/`swarm_code` dispatch.\n\n### Removed\n- **`adk/nanogpt.py`** (on-device training IP) removed from the published SDK\n and the public repo; relocated to the internal AitherOS moat. Excluded from\n the wheel as defense-in-depth.\n- Pre-2.0 releases shipped this IP and ungated capabilities and are being\n yanked (PyPI) / removed (GitHub releases). See `scripts/yank_leaky_releases.py`\n and `scripts/purge_public_leaks.sh`.\n\n## [1.20.0] - 2026-05-27\n\n### Fixed\n- **Pack discovery path mismatch** — `_find_local_packs()` now scans both\n `~/.aither/packs/` and `~/.aitheros/packs/`, so packs installed via\n `adk pack install` are discovered by `adk-workspace`.\n\n### Added\n- **Auto-register on pack install** — `adk pack install` now auto-registers\n with the portal if the pack contains an `agent.yaml` with a `portal` section.\n One command does download + extract + register.\n- **Flux event on agent registration** — `developer_portal.py` emits\n `agent.registered` Flux event so the portal fleet UI updates in real-time.\n\n## [1.15.0] - 2026-05-26\n\n### Added\n- **Cloud memory** — `adk quickstart --cloud` now tests gateway memory and\n auto-configures Spirit URL so memories persist across devices via\n `gateway.aitherium.com/v1/memory/teach` and `/v1/memory/recall`.\n- **Config→env export for cloud mode** — `Config.from_env()` now exports\n `AITHER_CLOUD_MODE`, `AITHER_SPIRIT_URL`, and spirit path env vars from\n saved config so Memory and other modules pick them up without needing\n explicit env vars set by the user.\n- **Cloud Quick Start section** in README — 3-command setup for users who\n just want a Claude/GPT-4/DeepSeek API key and the full agent harness.\n- **`--memory` profile for node deployment** — `adk deploy node --memory`\n adds Spirit (8087) + WorkingMemory (8101) containers for persistent\n vector memory. Without this, agents use local SQLite only.\n- **Configurable vLLM in node compose** — `AITHER_VLLM_MODEL`,\n `AITHER_VLLM_GPU_UTIL`, `AITHER_VLLM_CTX_LEN`, `AITHER_VLLM_QUANT_ARGS`\n env vars let `adk setup` write GPU-detected tier settings to `.env`\n and have the node compose pick them up (TQ4 for small GPUs, BNB for large).\n\n### Fixed\n- **Memory cloud routing** — `Memory._spirit_teach()` and `_spirit_recall()`\n now use configurable paths and auth headers for gateway proxy. Previously\n cloud-only users silently lost all Spirit memory calls.\n\n## [1.9.0] - 2026-05-24\n\n### Added\n- **Composable ToolPack system** — agents can load tool packs from\n `.toolpack.yaml` directories. Drop a pack in `~/.aitheros/packs/` or\n set `AITHER_PACKS_DIR`, and any agent discovers it automatically.\n- **`tools.packs` in agent.yaml** — list tool pack IDs to auto-load\n (e.g. `packs: [crm-tools, calendar-pro]`).\n- **`register_tool_packs(agent, pack_ids, packs_dir)`** in `builtin_tools` —\n programmatic pack loading for agents.\n- **`AITHER_TOOL_PACKS` env var** — comma-separated pack IDs loaded on\n agent init (alternative to YAML config).\n- **Unknown builtin categories resolve as pack IDs** — `tools.builtin:\n [file_io, crm-tools]` now works: `crm-tools` is looked up as a pack.\n\n## [1.8.0] - 2026-05-24\n\n### Removed\n- **11 dead modules** (3,023 lines) — `agent_deploy`, `cloud_deploy`, `create`,\n `phonehome`, `sdk_bridge`, `sovereign`, `telemetry`, `telemetry_config`,\n `error_reporter`, `anonymizer`, `package`. None were imported anywhere.\n- **29 ghost exports** from `__init__.py` — `secrets`, `otel`, `fs_sandbox`,\n and `vector_memory` modules were listed in `__all__` and `__getattr__` but\n the backing files never existed. Accessing them raised `ModuleNotFoundError`.\n- **Stale dist/ artifacts** — removed old wheels/tarballs (v0.4.0–v1.4.1, 11 MB).\n- **Orphan test scripts** — `test_phase1.py` and `test_phase23.py` (one-off\n validation scripts not part of the pytest suite).\n\n### Fixed\n- **Version mismatch** — `__init__.__version__` now matches `pyproject.toml`\n (was stuck at 1.6.0).\n\n## [1.5.0] - 2026-05-23\n\n### Added\n- **`--sovereign` flag for `adk deploy node`** — registers the node with the\n Aitherium hub via federation after deployment. Saves federation credentials\n to `~/.aither/.env.federation`. Accepts `--hub` (custom hub URL) and\n `--tenant` (tenant slug) options. Non-fatal: node runs standalone if\n registration fails.\n- **Federation → fleet cross-post** (Genesis) — sovereign nodes that register\n or heartbeat via `/federation/register` and `/federation/heartbeat` now\n automatically appear in the fleet dashboard (`/fleet/deployments`) with\n `node_type: \"sovereign\"` and live status/metrics.\n- **\"Deploy Sovereign Node\" section on Connect page** (AitherVeil) — Section 8\n with ADK deploy commands, fleet dashboard links, and provision page links.\n- **`deploy-node` action in tunnel proxy** (AitherVeil) — Connect page and\n external callers can now request bootstrap scripts via\n `POST /api/tunnel/devworkspace?action=deploy-node`.\n\n### Fixed\n- **Fleet provision page** — API URL corrected from `/api/fleet/provision` to\n `/api/bridge/genesis/fleet/provision` (matches bridge proxy pattern).\n- **Fleet detail page** — API URL corrected from `/api/fleet/deployments/` to\n `/api/bridge/genesis/fleet/deployments/` (same fix).\n- **Dockerfile** — entry point corrected from `aither-serve` to `adk-serve`\n (matches `pyproject.toml` script registration). Version label updated.\n\n## [1.4.1] - 2026-05-22\n\n### Fixed\n- **macOS executable builds** — dropped `--target-arch universal2` from\n `packaging/build_executable.py`. PyPI wheels for `pydantic_core` and other\n native deps are arch-specific, not fat binaries, so universal2 always failed\n with `IncompatibleBinaryArchError`. Now builds for the runner's native arch\n (`aither-macos-arm64`).\n- **Release workflow strategy** — added `fail-fast: false` so a single\n executable build failure no longer cancels the other OS jobs.\n- **PyPI publish** — added `skip-existing: true` so re-runs of the same\n version don't fail when the artifact already exists.\n- **GitHub Release job** — now runs whenever `pypi` succeeds even if some\n executable matrix entries fail, so the release page is always created with\n whatever binaries did build successfully.\n\n## [1.4.0] - 2026-05-22\n\n### Added\n- **Sovereign template now ships with `agent_core` 5-tier memory** — generated\n backends include the canonical `agent_core/` package (working → episodic →\n semantic → procedural → identity) plus `agent_memory.py` helper, vendored\n from `portal-kit-backend`.\n- `aiosqlite>=0.20.0` added to sovereign template `requirements.txt` for\n local agent_core SQLite storage.\n- Document tombstoning: `evict_cached_conversations_referencing` is wired\n through document-delete handlers in consumer apps so memories citing\n deleted documents are blocked from recall.\n- New tests: 13 smoke tests for `portal-kit-backend/agent_core` covering\n promotion thresholds, decay rates, store roundtrip, tombstone recall\n blocking, circuit breaker state machine, and chat-turn recording.\n\n### Changed\n- `scripts/vendor_agent_core.py` now syncs canonical `agent_core` to 4\n targets: WorkspaceRuntime, ADK sovereign template, and the GargBot +\n Chelle consumer mirrors (previously only 2).\n\n## [1.2.1] - 2026-05-21\n\n### Added\n- `adk train` command group — 6 subcommands: status, launch, logs, cancel, runs, register-gpu\n- `/slash-commands` endpoint — ADK server auto-exposes all 35 CLI commands as structured JSON manifest for AitherShell\n- `/cli/execute` endpoint — AitherShell can run any CLI command through the server\n- `build_command_manifest()` — introspects full argparse tree (commands, args, choices, subcommands)\n- `_register_commands()` extracted from main() for shared parser construction\n\n### Changed\n- `_get_genesis_url()` now probes: Genesis → ADK server → Aitherium cloud gateway (works without Genesis)\n\n## [1.2.0] - 2026-05-21\n\n### Added\n- **Runtime backend switching** — `agent.switch_backend(\"deepseek\")` and `agent.llm.switch_backend()` change provider without recreating agent\n- **Hybrid reasoning** — `agent.set_reasoning_backend(\"anthropic\")` routes effort 7+ to cloud API while keeping local orchestrator\n- **First-class DeepSeek provider** — `deepseek` in effort model table with `deepseek-chat` and `deepseek-reasoner`\n- **TQ4 quantization tiers** — `nano` (6GB, TurboQuant 4-bit), `standard-tq4` (12GB, both models TQ4), `hybrid-tq4` (6GB + cloud reasoning)\n- **`adk backend` CLI** — `list`, `set`, `set-reasoning`, `test` subcommands\n- **`adk quickstart`** — unified first-run wizard (setup + auth + shell)\n- **`adk tools`** — list local + MCP tools with tier markers\n- **`adk backup`** — tarball export of `~/.aither/`\n- **`adk ingest`** — feed docs/files into knowledge graph\n- **`adk setup nemotron`** shortcut — alias for `--tier lite`\n- **`--reasoning-api`** flag in setup — `anthropic`, `openai`, `deepseek`, `gateway`\n- **`--dgx-spark URL`** flag — remote vLLM endpoint configuration\n- **DGX Spark auto-detection** — scans `spark.local`, `192.168.0.33`, `AITHER_DGX_URL` env\n- **Post-setup smoke test** — sends real inference request after container startup\n- **Schema migration system** — version table + migration runner in `memory.py` and `graph_memory.py`\n- **Configurable vLLM port scan** — `AITHER_VLLM_PORTS` env var for custom ports\n- **`aithershell` entry point** — alias for `adk` in pyproject.toml\n- Shell auto-downloads binary on first `adk shell` (no `--install` needed)\n- Shell pre-flight check warns if no backend detected\n- Shell passes full config via env vars (API key, tenant, inference URL)\n- DGX/Remote and Cloud API checks added to `adk doctor`\n- Config fields: `reasoning_backend`, `reasoning_api_key`, `deepseek_api_key`, `dgx_url`, `vllm_extra_ports`\n\n### Fixed\n- `test_aeon_chat_basic` timeout — mock scope was exiting before request\n- `adk doctor` check_disk timeout — cap rglob at 5000 files\n\n## [1.1.7] - 2026-05-19\n\n### Added\n- Remote agent pipeline — `adk onboard` → fleet dispatch → remote inference\n- Agent-to-agent federation with mesh relay\n- `adk connect` for desktop mesh enrollment\n\n## [1.1.6] - 2026-05-19\n\n### Fixed\n- Tool call execution recovery across all backends\n- Hermes tool call fallback parsing for vLLM\n\n## [1.1.5] - 2026-05-19\n\n### Fixed\n- Tool call execution + Hermes fallback parsing improvements\n\n## [1.1.0] - 2026-05-18\n\n### Added\n- Multi-channel gateway (Telegram, Discord, Slack, webhook)\n- Aeon group chat with 7 presets\n- Skills auto-extraction from multi-step sessions\n- MCP stdio server for Claude Code integration\n- `adk gateway`, `adk cron`, `adk skills`, `adk soul` commands\n- SOUL.md import/export for portable agent identity\n- Elysium cloud device-flow authentication\n\n## [1.0.0] - 2026-05-18\n\n### Breaking\n- Package consolidated — `aither_adk` namespace replaced by `adk` + `adk.platform`\n- 93 duplicate files removed from `AitherOS/packages/aither_adk/`\n\n### Added\n- Standalone core primitives (`adk.core`: Agent, Tool, Memory, ModelBackend, Capability, Trace)\n- `adk.platform` sub-package merging all platform toolkit modules\n- Compatibility shims for legacy `from aither_adk.*` imports\n\n### Changed\n- Version synced across pyproject.toml, `adk/__init__.py`, `adk/platform/__init__.py`, npm package\n- Classifier promoted from Alpha to Beta\n\n## [0.16.0] - 2026-04-16\n\n### Swarm Coding Engine & Repowise Integration\n- **`repowise_search` tool** — Semantic + keyword hybrid code search via Repowise, with ripgrep fallback\n- **`swarm_code` tool** — Dispatch complex tasks to 11-agent swarm pipeline (ARCHITECT->SWARM->REVIEW->JUDGE)\n- **`agent.swarm()`** — Async convenience method with configurable mode, effort, timeout\n- **`agent.code_search()`** — Async convenience method returning structured results\n- New tool categories: `repowise` and `swarm` in TOOL_CATEGORIES\n- `repowise_search` and `swarm_code` added to `__init__.py` exports with lazy loading\n- `IDENTITY_DEFAULTS` updated: repowise for code-focused agents, swarm for orchestration agents\n- Standalone graceful degradation: repowise falls back to ripgrep, swarm returns structured error\n\n## [0.13.0] - 2026-04-02\n\n### Graph Faculties — Local Knowledge for Every Agent\n- **CodeGraph** (2,799 lines) — Full Python AST indexer with call graph, keyword/semantic/hybrid query, embedding matrix cache, incremental re-indexing, multi-hop chain expansion\n- **MemoryGraph** (1,339 lines) — Graph-based persistent agent memory with 10 edge types, hybrid query (keyword + semantic + graph expansion), multi-hop recall, pickle persistence\n- **EmbeddingProvider** — 4-backend fallback chain: sentence-transformers (GPU/CPU) -> Ollama -> Elysium cloud -> feature hashing (zero deps)\n- **BaseFacultyGraph** — Abstract base with HMAC-SHA256 validated pickle persistence\n\n### Agent Integration\n- `agent.set_code_graph(cg)` — auto-registers `code_search` + `code_context` tools\n- `agent.set_memory_graph(mg)` — auto-registers `remember` + `recall` + `memory_stats` tools\n- Both graphs inject context into LLM prompts automatically during chat\n\n### Zero-Config Onboarding\n- `adk start` / `adk` (no args) — auto-detect project, index code, detect LLM, persistent memory, interactive chat\n- Works for any directory: Python codebases, doc folders, mixed workspaces\n- Auto-detects LLM: Ollama -> vLLM -> Elysium -> OpenAI -> Anthropic\n- Per-project persistent memory in `~/.aither/memory/<project>`\n- `adk index <path>` — standalone indexing with progress bar and stats\n\n### MCP Gateway\n- `POST /v1/embeddings` — OpenAI-compatible embedding proxy to vLLM-embeddings:8209\n- Available to all tiers (embeddings are free)\n- ADK EmbeddingProvider uses this as Elysium cloud fallback\n\n### Optional Dependencies\n- `pip install aither-adk[graphs]` — numpy for 10x cosine similarity speedup\n- `pip install aither-adk[embedding]` — sentence-transformers + torch for local GPU embeddings\n- `pip install aither-adk` alone — graphs work with feature hashing (zero deps)\n\n## [0.12.0] - 2026-04-01\n\n### Bootstrap & Service Discovery\n- Version handshake -- ADK checks major.minor compatibility with Genesis/Node\n- Background reconnect loop -- ServiceBridge re-probes every 30s in standalone mode\n- Port 8080/8090 documented -- MCP vs OpenAI-compat clearly separated\n- Genesis URL configurable via GENESIS_URL env var (was hardcoded)\n- Standalone mode warning -- visible stderr alert when AitherOS not detected\n- Auto-reconnect on startup when services come online\n\n### Elysium Cloud Inference\n- Unified gateway URL -- gateway.aitherium.com handles auth + billing + inference\n- Streaming inference -- SSE passthrough for /v1/chat/completions with stream=true\n- Auth proxy routes -- /v1/auth/register, /v1/auth/login, /v1/auth/me\n- Billing proxy -- /v1/billing/balance through gateway\n- AitherConnect Elysium fallback -- cloud inference when local Genesis is down\n- AitherDesktop Elysium fallback -- third-tier chat fallback after Node\n\n### Infrastructure\n- /discovery endpoint on Genesis -- unified service URLs/versions/health\n- /api/config/services on Veil -- runtime port config for client-side JS\n- Veil healthcheck gates on Genesis -- unhealthy when backend is down\n- Desktop crash detection 90s->60s (threshold 3->2)\n\n## [0.11.0] - 2026-04-01\n\n### Agent Execution Quality (Claude Code Parity)\n- Raise loop guard block threshold from 3 to 4 -- agents get more room for iterative search\n- Soft synthesis nudge for effort >= 4 (no tool stripping, trust the model)\n- max_output_tokens escalation -- retry up to 3x when response is truncated\n- Tool result pairing guarantee -- synthetic error for orphaned tool_use blocks\n- Micro-compaction of old tool results -- save context tokens on long sessions\n- First-turn tool forcing only for effort >= 6 (trust model for lower effort)\n- Diminishing returns detection -- nudge agent when 3+ turns produce < 500 tokens\n- Message normalization -- merge consecutive same-role messages, strip empties\n- LLM retry with exponential backoff (5 retries, 500ms-16s, jitter)\n\n## [0.9.0] - 2026-03-16\n\nThe \"connected world\" release. Cross-platform identity pairing, voice capabilities, and multi-channel integration.\n\n### Added\n- **Pairing**: Cross-platform identity linking (`adk/pairing.py`)\n - `PairingManager` — SQLite-backed identity linking with 6-char pairing codes\n - Link users across Telegram, Discord, Slack, WhatsApp with 10min TTL codes\n - Canonical session IDs for cross-channel conversation continuity\n - `get_session_id()` returns \"user-{id}\" for paired users\n- **Voice**: Speech-to-text and text-to-speech client (`adk/voice.py`)\n - `VoiceClient` — async STT/TTS/emotion via AitherVoice service\n - Convenience functions: `hear()`, `say()`, `feel()`\n - 6 voice options: alloy, echo, fable, nova, onyx, shimmer\n - Emotion detection with intensity scoring\n- New exports: `PairingManager`, `PairingResult`, `PlatformIdentity`, `VoiceClient`, `TranscriptionResult`, `SynthesisResult`, `EmotionResult`\n\n### Changed\n- `__init__.py` exports expanded with pairing and voice symbols\n\n## [0.6.0] - 2026-03-13\n\nThe \"group mind\" release. Multi-agent group chat, creative tools, and Iris identity.\n\n### Added\n- **Aeon**: Multi-agent group chat engine (`adk/aeon.py`)\n - `AeonSession` — persistent group chat with parallel agent execution\n - 7 presets: balanced, creative, technical, security, minimal, duo_code, research\n - Orchestrator synthesis: Aither summarizes all agent responses\n - Serial execution for Ollama, parallel for vLLM/cloud\n - ConversationStore persistence with `type: \"aeon\"` metadata\n - `group_chat()` one-shot convenience function\n- `aither aeon` CLI command — interactive terminal group chat with color-coded agents\n - `-p/--preset`, `-a/--agents`, `-r/--rounds`, `--no-synthesize` flags\n - `reset` and `quit` commands\n- Server endpoints: `POST /aeon/chat`, `GET /aeon/presets`, `GET /aeon/sessions/{id}`\n- Creative tools in builtin_tools: `image_generate`, `image_refine`, `image_search`, `video_generate`\n- Iris agent identity with visual generation capabilities\n- 48 Aeon tests (data models, presets, chat, context, persistence, server, exports)\n\n### Changed\n- `__init__.py` exports: `AeonSession`, `AeonResponse`, `AeonMessage`, `group_chat`, `AEON_PRESETS`\n\n## [0.5.0] - 2026-03-13\n\nThe \"tenant-ready\" release. Multi-tenant admin, permission grants, safety profiles, and a full setup wizard.\n\n### Added\n- `aither setup` interactive setup wizard with hardware detection, model selection, identity config\n- Strata storage backend: SQLite WAL persistence for conversations, memories, knowledge graphs\n- CLI test runner: `aither test` with auto-discovery and parallel execution\n- Permission grants system for MCP tool access control\n- MCP account management tools\n- LLM provider auto-detection for Ollama and vLLM endpoints\n- Apache-2.0 LICENSE file\n- Elysium desktop sync module\n- Comprehensive CLI test suite (493 tests)\n- Strata storage test suite (972 tests)\n- LLM provider tests (82 tests)\n\n### Changed\n- CLI expanded: `aither setup`, `aither test`, `aither bugreport`, `aither doctor`\n- Strata module rewritten as full local-first storage engine\n- Server startup includes Strata initialization\n- README rewritten with clearer quickstart and architecture docs\n\n### Fixed\n- Dead documentation links\n- Python 3.11 compatibility issues\n- Ruff per-file-ignores configuration\n- Identity provisioning edge cases\n- Docker node-gyp native dependency builds (python3 + build tools)\n\n## [0.4.0] - 2026-03-13\n\nThe \"own your AI\" release. Self-hosted agent OS for people who don't want their data on someone else's servers.\n\n### Added\n- Muse agent identity (creative/artistic generation)\n- Port 8120 to vLLM scan for ExoNodes discovery\n- Public roadmap with milestone-based porting schedule\n- Competitive positioning: self-hosted alternative to cloud-locked AI appliances\n\n### Changed\n- LLM router: compute-aware effort routing (replaces effort-level context gating)\n- README rewritten for sovereignty-first messaging\n- Package description updated\n- Promoted from alpha to stable release\n\n### Fixed\n- Version string consistency between pyproject.toml and __init__.py\n- vLLM port scanning now includes port 8120 (ExoNodes)\n\n## [0.3.1] - 2026-03-09\n\n### Added\n- GraphMemory: SQLite knowledge graph with Ollama embeddings and hybrid search\n- NeuronPool: auto-fire pre-LLM data gathering agents\n- NanoGPT: pure Python char-level transformer with LoRA fine-tuning\n- Safety gates: IntakeGuard (input), LoopGuard (recursion), Sandbox (code exec)\n- Event system: EventEmitter with chat/tool/forge event types\n- Builtin tools: identity-based tool selection\n- ServiceBridge: auto-discovery of AitherOS services\n- Streaming: chat_stream with safety gate integration\n- Auth middleware: Bearer token authentication\n- CLI: `aither init` and `aither serve` commands\n\n### Changed\n- Wired 5 previously disconnected modules into agent loop\n- 522 total passing tests (up from 85)\n\n## [0.3.0] - 2026-03-07\n\n### Added\n- Clean-room agent development kit\n- Multi-backend LLM providers (Ollama, OpenAI, Anthropic)\n- AitherAgent class with @tool decorator\n- SQLite conversation memory\n- OpenAI-compatible server (`aither-serve`)\n- 16 agent identities as package data\n- MCP bridge to mcp.aitherium.com\n- Privacy-centric opt-in telemetry\n- Bug reporting CLI and API\n- FastAPI server with OpenAI-compatible endpoints\n- Hardware auto-detection (5 tiers, 11 profiles)\n- Fleet orchestration and multi-agent coordination\n- A2A mesh protocol\n- Federation protocol for cross-instance agent dispatch\n\n### Initial release\n- 85 passing tests\n- Apache-2.0 license\n"}, "0b08aece041092b8a8c217cc27fc41a651786628": {"t": 1785748162.6954901, "n": 91814, "h": "868612740231937b", "c": "# Changelog\n\n## [2.29.1]\n\n### Added\n- `adk onboard --webgpu` — self-bootstrap onto in-browser inference: sets the on-device\n GUI as default and records the choice (no server model; the browser runs the model on\n the user's GPU).\n\n\n## [2.29.0]\n\n### Added — on-device WebGPU inference in the local GUI\n- New \"⚡ On-device (WebGPU)\" backend in the default console: runs a small model\n (Gemma-4-E2B) 100% in the browser on the user's GPU — no server, no API cost, private.\n- One-time ~900 MB model download with a consent gate + aggregated progress; cached after.\n- Streaming replies, multi-turn history, Stop, and a graceful WebGPU-absent fallback.\n\n\nAll notable changes to aither-adk will be documented in this file.\n\n## [2.27.0] - 2026-07-18\n\n### Added — Moonshot Kimi K3 provider (DeepSeek-equivalent cloud backend)\n\n- **New `moonshot` provider** across the LLM router: OpenAI-compatible\n `https://api.moonshot.ai/v1`, default model `kimi-k3` (131K context default, up to 1M;\n tool calls, JSON mode, structured output). Wired everywhere `deepseek` is:\n - `LLMRouter(provider=\"moonshot\")` + `_COMPAT_URLS`/`_COMPAT_MODELS`/model tiers\n (small/medium/large → `kimi-k3`)\n - Config: `moonshot_api_key` field (env `MOONSHOT_API_KEY`), saved-config load,\n provider-keys map (`provider_keys.json` key `moonshot`)\n - Auto-detect chain: tried after DeepSeek (config key, then env key)\n - Hybrid reasoning backend: `AITHER_REASONING_BACKEND=moonshot` for effort 7+\n - Reasoning tiers: `backend: moonshot` (alias `kimi`) via new `MoonshotBackend`\n - CLI: `adk backend set moonshot --api-key …`, `adk backend list` shows moonshot\n- No breaking changes; DeepSeek and all other providers unchanged.\n\n## [2.26.0] - 2026-07-16\n\n### Added — Aeon group-chat web UI pack + POST /aeon/stream SSE endpoint\n\n- **New `aeon` UI pack** (`adk/webui/packs/aeon/index.html`) — group chat for multi-agent\n discussion. Mirrors the `llamacpp` pack's auth + SSE pattern (bearer from URL fragment #k=),\n with a preset selector (balanced/creative/technical/security/minimal/duo_code/research) and\n distinct colored agent messages. Synthesis block highlighted. Agent content is HTML-escaped\n before render. Fully self-contained HTML+CSS+JS, light+dark, no external assets. (No\n temperature/max-tokens controls — Aeon runs a fixed multi-agent round and doesn't thread\n per-turn generation params, so exposing them would be a no-op.)\n- **POST /aeon/stream SSE endpoint** — accepts body {message, preset?, agents?, rounds?,\n session_id?}, emits SSE with: `session_start` (participants + orchestrator), `agent_message`\n per non-orchestrator, `synthesis` (if enabled), `error` on exception, `complete` (total tokens\n + latency). Mirrors /chat/stream structure — like every streaming endpoint here it is gated by\n the server auth middleware **when `AITHER_SERVER_API_KEY` is set** (a local `adk up` with no key\n is unauthenticated by design, same as /chat/stream).\n- **Default UI pack flipped to `llamacpp`** — `resolve_ui_pack_name()` now returns `llamacpp`\n instead of `console` when no env/config is set (new users see the clean chat, not the admin\n SPA; existing users with AITHER_AGENT_UI or saved config unaffected).\n\n### Added — swappable agent web UI packs (llama.cpp-style built-in chat)\n\n- The page an agent serves at `/` is now a **swappable UI pack**, so you can\n drop in, test, and deploy different chat frontends without touching the agent.\n Select with `adk ui set <name>` (persists to `~/.aither/config.json`) or the\n `$AITHER_AGENT_UI` env; `adk ui ls` lists packs, `adk ui path` shows what\n resolves.\n- **New built-in `llamacpp` pack** — a clean, self-contained llama.cpp-style\n chat UI (centered conversation, streaming token-by-token with a cursor, a\n Stop button, a settings drawer for temperature / max-tokens that is actually\n honored end-to-end — `/chat/stream` now forwards them to the provider —\n minimal safe markdown, light+dark). Hits the agent's own gated `/chat/stream`\n with the bearer read from the URL fragment (never sent in the URL). Built-ins\n also include `console` (the full admin SPA, still the default) and `minimal`.\n- **Drop-in custom packs**: a UI pack is just a folder with an `index.html` in\n `~/.aither/ui-packs/<name>/` (override via `$AITHER_UI_PACKS_DIR`) — no rebuild.\n- Resolution is **fail-soft**: an unknown/missing pack falls back\n console → minimal, so `/` is never blank.\n\n### Added — MCP-gateway-first platform access + mesh A2A trust\n\n- **Gateway-first platform access** (`adk/client/_gateway_mcp.py`, `GatewayMCPClient`):\n a self-hosted agent connects its MCP **client** to `mcp.aitherium.com` authenticated\n as the OWNER (device-flow / PAT / ACTA token), RBAC-scoped to that identity — so it can\n use platform tools + `get_secret` **whether or not the node runs local AitherOS\n services**. Fail-closed: no token → client disabled, the agent still runs. Wired into\n the server lifespan (`_connect_gateway_mcp`).\n- **`genesis` LLM backend**: `adk backend set genesis` points the agent brain at a local\n Genesis (`http://localhost:8001/v1`, model `workflow`) — the 5090/controller pattern\n where inference is served by the local fleet, not the cloud gateway or a raw vLLM.\n- **`adk mesh` command**: `adk mesh onboard` drives the Conductor 5-step onboard\n (`/v1/mesh/onboard` → registers this node's public key, returns the `endpoint:mesh`\n trust token); `adk mesh ls` lists peer nodes/agents (AitherMesh `/nodes` + Directory)\n with their A2A cards + invoke URLs for discovery.\n\n### Security — A2A inter-agent trust enforcement (fail-closed)\n\n- **`adk/a2a_trust.py`** (new): Ed25519 signature verification + trusted-key check\n (`AITHER_A2A_TRUSTED_KEYS`), opt-in via `AITHER_A2A_REQUIRE_TRUST` (default `false` for\n backward compatibility; `audit` logs untrusted, `true` rejects).\n- **Fixed a fail-OPEN in the inbound A2A handler** (`adk/a2a.py`): the trust check\n previously ran *only when signature headers were present*, so an unsigned request\n bypassed `AITHER_A2A_REQUIRE_TRUST` entirely. Now genuinely fail-closed — required mode\n with no/invalid signature → `403`.\n- **Sovereign template no longer scaffolds `verify=False`**: the generated\n `services/llm.py` now uses the project's own `tls_verify()` policy helper for every\n provider call instead of hardcoding disabled TLS verification.\n- **`adk/_tls.py`**: disabling verification via `AITHER_TLS_VERIFY=false` now emits a\n one-time loud warning (was silent) — the escape hatch stays, the silence goes.\n\n## [2.25.1] - 2026-07-15\n\n### Fixed — self-hosted OpenAI-compatible backends honor a custom base URL\n\n- `adk backend set llamacpp --base-url http://localhost:8090/v1 --model <id>` (and\n `vllm`/`lmstudio`, and the `AITHER_LLM_BASE_URL` env) now actually point the agent\n at YOUR server. Previously the base URL was saved but never read, so an explicit\n `--backend llamacpp` fell back to the provider's public API default (openai.com).\n New `Config.llm_base_url` field + saved-config `inference_url` wiring; the explicit\n backend path in `LLMRouter` now passes it for the local OpenAI-compatible family.\n This is what lets a self-hosted node run its OWN local model (e.g. a PrismML\n Bonsai llama.cpp) as the agent brain instead of ollama/cloud.\n\n## [2.25.0] - 2026-07-15\n\n### Added — node_bootstrap tool pack: bootstrap LLM inference on any hardware\n\n- **New bundled tool pack `node-bootstrap`** (`adk/toolpacks/node_bootstrap/`):\n seven agent tools — `node_detect_hardware`, `node_resolve_recipe`,\n `node_plan_deployment`, `node_apply`, `node_enroll`, `node_register_backend`,\n `node_verify` — plus a CLI shim (`python -m adk.toolpacks.node_bootstrap\n detect|resolve|plan|apply|enroll|register|verify`). Detect a box's hardware,\n resolve a deployment recipe, actually deploy the inference engine (docker\n compose / native / delegate), enroll with a control plane, register the\n backend, and live-verify a completion.\n- **9 hardware recipes** shipped in the wheel: `cpu-1bit-llamacpp`, `cpu-ollama`,\n `cuda-vllm-8gb/24gb/40gb`, `cuda-dual-stack-32gb` (32GB-card co-resident\n stack), `unified-memory-vllm` (DGX/Grace-class), `metal-ollama` (native —\n Docker on macOS has no Metal passthrough), `cloud-api` fallback. Resolution\n scores hardware against min/max VRAM bands, RAM, cores and unified memory;\n ties prefer higher tiers and self-contained recipes over fleet delegates.\n- Security posture: enrollment and backend registration are fail-closed\n (missing token/URL → error dict, never anonymous, never `verify=False`);\n tokens are redacted in outputs; all endpoints come from args/env\n (`AITHER_CONTROL_PLANE_URL`, `AITHER_GENESIS_URL`, `AITHER_AUTH_TOKEN`).\n\n## [2.24.0] - 2026-07-14\n\n### Changed — sync-family consolidation\n\n- **Removed dead code:** `adk/sync.py` (528 lines) deleted — it was unreachable,\n shadowed by the `adk/sync/` package (`import adk.sync` always resolved to the\n package; nothing loaded the module by path — verified).\n- **Grouped the sync domain modules into the `adk.sync` package:**\n `brain_sync`→`adk.sync.brain`, `files_sync`→`adk.sync.files`,\n `lockbox_sync`→`adk.sync.lockbox`, `secrets_sync`→`adk.sync.secrets`,\n `session_sync`→`adk.sync.sessions`, `settings_sync`→`adk.sync.settings`,\n `sync_watermark`→`adk.sync.watermark`. All in-tree importers updated. No\n back-compat shims were left because **zero external/monorepo code imported the\n old root paths** (verified) — a clean move, not a shimmed one. **Breaking only\n for code that imported `adk.<name>_sync` directly** (none known).\n\n### Docs — MCP module layering clarified\n\n- `adk.mcp` (enterprise AitherOS gateway client: auth/billing/token-tracking) and\n `adk.core.mcp` (minimal generic JSON-RPC adapter for any MCP server) are\n DISTINCT layers, not duplicates — each now carries a LAYER/ROLE/See-also\n docstring so they are not mistakenly consolidated.\n\n## [2.23.2] - 2026-07-14\n\n### Fixed — CompletionGate no longer false-fails on correct results\n\n- **Tolerant judge JSON extraction.** The LLM judge parsed its reply with `json.loads(text[first\"{\":last\"}\"])`, which raised `JSONDecodeError` (\"Extra data\") whenever the model appended prose after the verdict or emitted a second object — and `verify()` turned that into a fail-closed verdict, so a genuinely completed task reported `unverified`. New `_first_json_object()` uses `JSONDecoder.raw_decode` from the first `{` that begins a valid object and ignores trailing content (prose, markdown fences, extra objects). A judge that answers with no parseable JSON still fails closed (never a soft pass), now with a clear reason. The same extractor backs `_derive_criteria`.\n- **Bare code-like tokens are hard-checked.** A natural criterion like `output contains ADK_AUTO_OK` (no quotes) previously fell through to the judge; `hard_checks` now also matches bare identifier/code shapes (contains an underscore, or 4+ CAPS/digit chars), with a stopword guard so a descriptive uppercase word (`OUTPUT`, `CONTAIN`) never becomes a false required token. Quote a token to force it regardless.\n- +9 tests (24 total), including the exact trailing-data regression.\n\n## [2.23.0] - 2026-07-14\n\n### Removed — dead modules and internal integrations\n\n- **`adk.swarm`, `adk.provisioning_tools`, `adk.session_sync_integration`, `adk.addon_metering`** — four orphan modules with zero public usage have been deleted. These were internal prototypes kept for legacy monorepo consumers; public packages have not imported them since 2.21.0 (tracked as D-348). **Breaking for anyone directly importing these modules** (import will fail). They are no longer exempt from the orphan-module check.\n- **`adk.aither_bridge`** — the AitherOS-internal IRC ↔ chat gateway bridge is no longer shipped in the public aither-adk package (stripped at sync time). All public importers already degrade behind `except ImportError` (in `adk.server` and `adk.builtin_tools`), so public packages remain unaffected. The module stays in the private AitherOS monorepo for internal use.\n- **Identity document trim** — removed internal agent names from `adk/identities/aither.yaml` delegation guidance, replacing with role-generic wording (\"delegate code review, refactoring, security analysis, performance and testing to specialist agents when available\"). Effort-tier routing philosophy remains unchanged and is product design, not a leak.\n\n### Changed — genericized customer/product names in help text\n\n- CLI help/examples no longer name specific customer deployments (`--scope` template list and `deploy agent` name example use generic placeholders). Functional deploy paths are unchanged.\n\n## [2.22.1] - 2026-07-14\n\n### Fixed — real bugs surfaced by reviving the public CI (red since Jul 5)\n\n- **TLS**: bundled toolpacks (arc-brainpack, cloudflare) disabled certificate\n verification on gateway/vault calls; all now route `adk._tls.tls_verify()`\n (verified by default, internal CA bundle when present).\n- **`system_prompt` contract**: bundled-pack `[PACK DIRECTIVES]` were appended\n even to an explicit `system_prompt`; an explicit prompt now wins wholesale.\n- **Headless secrets**: `_derive_key()` used `os.getlogin()`, which raises\n `OSError` without a controlling terminal (CI, daemons, containers) — falls\n back to `getpass.getuser()` (same value on interactive boxes, so existing\n secret files keep decrypting).\n- **`pack_verifier`**: missing `cryptography` crashed with `UnboundLocalError`\n in the exception handler instead of failing closed; now degrades gracefully\n (and `cryptography` was added to the dev extra so CI tests the real path).\n- **`fleet_manager._kill_pid` (POSIX)**: reported success immediately after an\n asynchronous SIGTERM and never reaped children (zombies read as alive); now\n polls, escalates to SIGKILL, and reaps.\n- **Identity**: `aither` regained the `creative` tool category (dropped\n accidentally in a 2.7.0 WIP sweep).\n\n## [2.22.0] - 2026-07-14\n\n### Changed — internal toolkit relocation\n\n- **`adk.platform` relocated** — the internal aither-platform toolkit (ComfyUI/image-gen\n pipelines, fleet agent clients, parallel CLI/UI/tools stack) has been moved out of the\n adk SDK tree to the standalone internal `aither-platform` package at\n `AitherOS/agents/aither_platform/`. The public package remains unaffected (it already\n excluded `adk.platform` since 2.21.1). Internal AitherOS agent builds now install\n `aither-platform` separately. `adk platform` falls through to the same \"not available\"\n message on public installs; internal builds work via the relocated package.\n\n### Removed — backward-compat re-export deleted\n\n- **`adk.gateway` module removed** — the backward-compatibility re-export of `GatewayClient`\n from `adk.gateway` has been removed. Use `from adk.client import GatewayClient` instead\n (the established public API since 2.21.0). All internal importers have been updated.\n\n## [2.21.1] - 2026-07-14\n\n### Removed — internal toolkit and license-gated product no longer ship publicly\n\n- **`adk.platform`** — the internal aither-platform toolkit (ComfyUI/image-gen\n pipelines, fleet agent clients, a parallel cli/ui/tools stack) is no longer\n included in the public repo or the PyPI wheel. It was internal-only code that\n had been riding along since the aither-platform merge. `adk platform` now\n reports \"Platform toolkit not available\" on public installs; internal AitherOS\n builds are unaffected.\n- **`adk.formbridge`** — the FormBridge form-automation product is license-gated\n (PROFESSIONAL+); its implementation no longer ships in the free package. All\n import sites (`builtin_tools`, `server` route mount) already degrade behind\n `except ImportError`, so nothing else changes behavior.\n- Product test suites (`test_formbridge.py`, `test_encounter_keying.py`,\n `test_flow_tools.py`) are excluded from the public sync alongside them.\n\n## [2.21.0] - 2026-07-14\n\n### Added — Completion gate: verified-or-retried task execution\n\n- **`adk.gate.CompletionGate`** — a composable wrapper that closes the \"silent\n success\" gap: an agent's ReAct loop returns whatever the model produced and\n calls it done (`finish_reason = stop/max_steps/length`), which is a *liveness*\n signal, not a *completion* one — so an agent can \"succeed\" while doing nothing.\n `CompletionGate` runs the agent, verifies the result against acceptance\n criteria, retries with the failure fed back, and returns the response with\n `finish_reason` set to `verified` or an honest `unverified`. Never a false pass.\n - **Hard-first, un-soft-passable checks** (`adk.gate.hard_checks`): a filesystem\n path a criterion says must exist is checked on disk; a required quoted token\n must appear in the output. Only genuinely subjective criteria fall through to\n an optional LLM judge (reuses the agent's own model), which **fails closed**\n if unavailable.\n - **`gated_run(agent, task, criteria=..., max_retries=2)`** — one-shot helper.\n - `auto_criteria` (LLM writes the machine-checkable definition-of-done from the\n task), a `verifier=` override for custom checks (tests-pass / HTTP probe), and\n a `requires_action` short-circuit so it never fights human-approval pauses.\n - Compose it onto any agent exposing `run(task) -> resp{.content, .finish_reason}`\n (`AitherAgent`, `core.Agent`, or a thin adapter over a remote dispatch).\n - Exported from the top-level package: `from adk import CompletionGate, gated_run`.\n\n## [2.19.0] - 2026-07-12\n\n### Added — Eve agent importer + durable A2A tasks + canonical agent discovery\n\n- **`adk pack import <eve_agent_path>`** — convert external Eve agents to AitherADK packs\n (from GitHub or local filesystem). Maps Eve structure (instructions → system prompt,\n agent.ts → agent.yaml, skills/*.md → skills, connections/*.ts → MCP, tools/*.ts →\n tools/node verbatim) to AitherADK format with a single command. Eve agents (compiled\n manifest format, schema v35) become installable packs compatible with `adk pack install`.\n- **Durable A2A task lifecycle (`TaskManager` backed by FileStore)** — agent-to-agent\n tasks now survive container restarts. Tasks persist as append-only JSONL\n (`.adk/tasks.jsonl`); in-memory dict for fast access; `create_task()` / `update_status()` /\n `add_artifact()` / `add_message()` all save to disk. Google A2A v0.3.0 spec compliant.\n- **Canonical agent card discovery** — `A2AServer.mount()` now exposes:\n - `GET /.well-known/agent-card.json` — Agent Card (canonical; contains agent identity,\n capabilities, skills, endpoints)\n - `GET /.well-known/agent.json` — Agent Card (legacy, redirects with 308 to canonical path)\n - `POST /a2a` — JSON-RPC 2.0 for message send/task lifecycle\n - `GET /a2a/tasks/{id}/subscribe` — SSE streaming task updates\n- **Four built-in agent packs now complete and catalogued**:\n - `hermes` — architecture review & trade-off analysis\n - `openclaw` — web research & source verification\n - `claude-code` — feature development & debugging\n - `analyst` — structured data inference & anomaly detection\n All discoverable via `adk packs` / `adk install pack:<name>`.\n- **Tolerant tool-call JSON parsing** — malformed tool JSON (extra properties, missing\n name/arguments, string-encoded nested args, missing keys) is now gracefully degraded\n instead of silently failing. `_parse_tool_json()` accepts both `\"arguments\"` and\n `\"parameters\"` keys, coerces string-encoded args, and skips unparseable calls instead\n of crashing the turn.\n\n### Known limitations\n\n- Eve agent importer: remote GitHub fetching not yet implemented (local paths only).\n- Durable task store: currently JSONL (suitable for thousands of records, not millions).\n\n## [2.18.0] - 2026-07-10\n\n### Added — AitherShell command center + Claude Code session management\n\n- **`aither sessions`** — Claude Code session manager: interactive full-screen\n browser (type-to-filter, transcript preview, deep full-text `search`, resume\n **in the current terminal** or as Windows Terminal tabs), crash guard\n (`guard install` = at-logon watchdog that snapshots live sessions and\n auto-restores the whole set after a terminal crash), and `ingest [--watch]`\n (incremental, secret-guarded sync of session conversations into the local KB\n / CompanyBrain via the standard ingest pipeline).\n- **`aither hq`** — command-center dashboard: per-service fleet health, LLM\n queue (depth/VRAM/models), Pulse alerts, live session count, inbox unread —\n auto-refreshing, with one-key jumps into chat, sessions, inbox, agents,\n brief, watchtower, and docker recovery.\n- **`aither inbox`** — unified queue over CommCore mail, Relay\n mentions/notifications, and Pulse alerts; open/mark-read/DM-reply from the\n terminal. Every source degrades independently.\n- **`aither agents`** — roster console + `agents ask -e <1-10> <agent> <q>`\n (effort-tiered ask via Genesis, with automatic `/chat/stream` fallback when\n the `/agent/sync` execute gate rejects host callers) + forge dispatch\n inspection + routine health.\n- **`aither palette`** — universal fuzzy picker across actions, sessions, and\n services; **`aither brief`** — renders Atlas's executive briefing in the\n terminal; **`aither watch`** — fleet watchtower with named wedge-signature\n detection (docker-WSL wedge, LLM-queue stall, crash-loop uptime regression)\n and optional docker auto-recovery.\n- All fleet reads go through a shared fail-soft `FleetClient` (internal-CA\n TLS via `adk._tls`, dashboard-grade timeouts, per-source degraded states).\n- Node shell-cli (`@aitherium/shell-cli` 1.13.0): `aither\n sessions|hq|inbox|palette|brief|watch|agents|docker` now pass through to the\n Python shell instead of being intent-classified as chat prompts.\n\n## [2.17.1] - 2026-07-05\n\n### Fully self-service mesh join — no customer key handling\n\n- **Auto-join headscale off the onboard response.** The conductor now auto-issues a\n headscale pre-auth key for NAT'd nodes and returns it in the `/v1/mesh/onboard`\n response; `mesh.join` consumes it and brings up the tunnel automatically. A customer\n runs one command — they never mint, fetch, or set a headscale key. Explicit\n `--headscale-key`/`AITHER_HEADSCALE_AUTH_KEY` still take precedence.\n- **Security:** `_tailscale_up` now redacts the auth key from all logs and exceptions\n (a failed `tailscale up` previously echoed the full argv, leaking the key into logs).\n\n## [2.17.0] - 2026-07-05\n\n### Added — Headscale mesh transport + local Qdrant + AitherConnect onboarding\n\n- **Headscale NAT-friendly mesh transport.** When raw WireGuard UDP:51820 is not\n viable (customer boxes behind NAT, corporate firewalls, CGNAT), `adk mesh join\n --headscale` routes the mesh tunnel through Tailscale's Headscale control plane.\n The overlay IP is still Conductor-assigned (10.77.0.0/16); Headscale provides\n only the transport layer. Configured via `AITHER_MESH_TRANSPORT=headscale`,\n `AITHER_HEADSCALE_URL` (default: https://headscale.aitherium.com), and\n `AITHER_HEADSCALE_AUTH_KEY` (pre-generated key). Automatic fallback to raw\n WireGuard if Headscale setup fails.\n- **Local Qdrant vector DB provisioning.** `adk stack` now provisions Qdrant\n locally when `AITHER_VECTOR_DB=qdrant` is set, enabling offline-first RAG\n without external dependencies. Tiered: local Qdrant in-fleet, cloud Nexus\n for enterprise multi-tenant deployments.\n- **Conductor URL public fallback.** `adk mesh join` resolves the internal\n `aitheros-conductor:8193` hostname; if unreachable (e.g., self-hosted nodes),\n it falls back to the public `conductor.aitherium.com:8193` endpoint,\n eliminating bootstrap configuration friction.\n\n### Documentation\n\n- **Self-hosting runbook.** New `docs/SELF_HOSTING.md` covers deploying AitherOS\n on customer infrastructure with Qdrant + mesh + AitherConnect onboarding.\n- **Mesh transport constraints documented.** Raw WireGuard requires public\n UDP:51820 endpoint (cloud instances, dedicated hardware); Headscale for\n NAT'd networks. Noted in CLI help and module docstrings.\n\n## [2.15.0] - 2026-07-04\n\n### Added — built-in web admin console + portal-profile settings sync\n\n- **Web admin console.** The page `adk up` opens (served at `/` and `/ui`) is now a full,\n self-contained console — Chat plus six admin tabs: **Backend** (view/switch LLM provider,\n set keys, test connection), **Packs** (list/enable/disable/reload tool packs),\n **Sessions** (browse/delete conversations), **Logs** (redacted agent-log tail),\n **Graph** (knowledge-graph search/neighborhood/stats), and **MCP** (add/remove external\n MCP servers). No build step, no CDN — a single packaged HTML asset. The original minimal\n streaming chat page stays at `/chat` for back-compat.\n- **Admin API (`adk/admin_api.py`).** ~20 endpoints under `/admin/*`, all bearer-gated by the\n existing middleware (never in `_skip_auth_paths`). Operates on the live agent — LLM backend\n swap (`LLMRouter.switch_backend`), pack reload, and MCP registration take effect without a\n restart.\n- **Settings sync (`adk/settings_sync.py`).** The user's portal profile is the source of\n truth: on startup the agent pulls `preferences.adk` and applies it over the local\n `~/.aither/config.yaml` cache; admin-console edits push a debounced snapshot back to\n `PUT /api/settings/preferences`. Provider API keys and MCP auth headers are device-local\n secrets and are never included in the pushed snapshot. Opt out with `AITHER_SETTINGS_SYNC=false`.\n\n### Security\n\n- **Timing-safe token comparison.** The server bearer check now uses `hmac.compare_digest`\n (the token is reachable over the public tunnel; a byte-wise `!=` leaked it to timing attacks).\n- **MCP add is SSRF-guarded + confirmed.** Adding an external MCP server is a two-step\n prepare→confirm flow; URLs resolving to loopback / private / link-local / metadata addresses\n are rejected (override with `AITHER_ALLOW_PRIVATE_MCP=1` for trusted local servers).\n- **Config editor is allowlisted.** `PATCH /admin/config` writes only safe scalar prefs and\n hard-denies fields that could execute code or redirect traffic at startup (e.g.\n `aither_toolpack_dirs`, backend base URLs).\n- **Log tail is scrubbed.** Bearer tokens, `#k=` fragments, and `sk-`/`aither_sk_` keys are\n redacted from the log-tail endpoint; the tunnel log is excluded.\n\n## [2.14.8] - 2026-07-04\n\n### Added — `adk up` for normal humans (no API key, no prereqs, live chat)\n\n- **Hosted-brain default — no API key required.** When there's no local model and no\n provider key, `adk up` signs in (device-flow) and routes inference through the gateway\n using the portal token (`AITHER_LLM_BACKEND=gateway` + bearer). A non-technical user\n never needs to obtain or paste an LLM API key. Explicit `--provider`/local model still\n take precedence; unattended `--yes` with no token/backend fails cleanly (exit 3).\n- **cloudflared auto-download.** If the tunnel binary is missing, `adk up` downloads the\n right platform asset to `~/.aither/bin` (SHA256-verified against the release; opt out with\n `AITHER_CLOUDFLARED_REQUIRE_CHECKSUM=0`) instead of exiting with an install hint.\n- **Built-in streaming chat page.** The agent now serves a self-contained chat UI at `/`\n (and `/chat`) with a live \"thinking…\" indicator and token-by-token streaming — so a human\n has somewhere to talk to it with immediate feedback instead of a blank wait. `adk up` opens\n it locally and passes the callback bearer in the URL fragment (`#k=…`), so `/chat/stream`\n stays authenticated (not an open proxy) with nothing to paste.\n- **Human-readable failures.** `adk up` errors now carry a plain-English `next_action`\n (kept machine-readable in `--yes` JSON).\n\n### Fixed\n\n- `adk up` chat link pointed at a non-existent `/settings/agent` route; it now points at the\n agent's own streaming chat page.\n\n## [2.14.7] - 2026-07-04\n\n### Added\n\n- **`adk up` — one command to run a persistent, fleet-connected agent.** Collapses\n the scattered onboarding surface (`host`/`quickstart`/`connect`/`login`/`setup`)\n into a single command that starts `aither-serve` (single agent, identity `aither`\n by default), opens a Cloudflare quick-tunnel for fleet reachability, registers with\n the portal, and installs a reboot-persistent autostart entry (Windows Task Scheduler\n / systemd-user / launchd). Detaches by default so the terminal is freed; `--foreground`\n blocks. Reuses the existing device-flow login, cloudflared discovery, provider-key\n store, and `_preflight_check` backend detection.\n- **Fully non-interactive path for autonomous agents.** `adk up --yes` (or any\n non-TTY invocation) takes ZERO prompts — everything resolves from flags, env, and\n saved config — and emits a single machine-readable JSON status line with meaningful\n exit codes (`0` ok, `2` bad-input, `3` no-backend, `4` tunnel-missing, `5` register-failed).\n Degrades to local-only (warn, not fail) when a portal token is absent unless\n `--require-register` is set.\n- **`adk down` / `adk status --json` companions.** `down` stops the agent + tunnel,\n best-effort deregisters, and removes the autostart entry; `status` reports the running\n agent (liveness + `/health` + tunnel + registration), with `--json` for agents/CI.\n- **`adk stack`** — the previous `adk up` behaviour (supervise the Room + Ollama consumer\n stack) now lives here; `adk up` is the connected-agent command.\n\n### Internal\n\n- New `adk/agent_daemon.py`: detached process spawn, status file\n (`~/.aither/adk-up.json`), pid liveness/teardown, and cross-platform autostart install.\n\n## [2.14.6] - 2026-07-04\n\n### Added\n\n- **`adk.preflight` — self-bootstrap capability probe.** `run_preflight(agent,\n spec)` wakes an agent up knowing what it is actually plugged into: a bounded\n (1.5s) parallel `LivenessProbe` over the router's resolved backends\n (primary/reasoning), the embeddings winner, remote MCP tools, the structured-ML\n / `/ml/teach` endpoints, and voice — returning an honest `CapabilityReport`.\n Status vocabulary distinguishes reachability from entitlement\n (`OK`/`MISSING`/`UNREACHABLE`/`AUTH`/`TIER_DENIED`/`UNSUPPORTED`), a hosted slot\n never prints a bare `OK`, and a required-but-unsatisfied slot aborts before the\n loop instead of blind-firing. Headless task/rules resolution + an allowed-roots\n policy binding (`builtin_tools.set_allowed_roots`, which resets the memoized\n cache so the override actually enforces).\n- **Multimodal (image) messages.** `Message.content` now accepts an OpenAI-style\n content-part list (`str | list[dict]`), passed through verbatim to the\n OpenAI-compatible body (gateway / vLLM / OpenAI). New `adk.llm.multimodal`\n helpers — `image_message()`, `image_content_parts()`, `to_image_url()` — build\n the parts and encode local image bytes/paths into `data:` URIs.\n- **Real vision preflight probe.** The vision slot is no longer unconditionally\n `UNSUPPORTED`: it sends a discriminable image (a solid red square) and requires\n the model to name the color, so a blind/text-only model cannot false-pass. `OK`\n only on a real round-trip; `AUTH` on 401/403; `UNSUPPORTED` when there is no\n OpenAI-compatible provider or the model cannot see the image.\n\n## [2.14.5] - 2026-07-04\n\n### Added\n\n- **`adk.reasoning.mcts` — generic Monte-Carlo Tree Search library.** A reusable\n MCTS engine (ported from the internal `UnifiedMCTS`, with all internal couplings\n removed) that any agent can drive over its own environment:\n - `MCTSEnvironment` protocol (`get_state_hash` / `get_actions` / `step` /\n `evaluate` / `clone`) — implement it and search.\n - Three **optional, default-off** model seams — `TransitionModel`, `PolicyModel`\n (PUCT priors), `ValueModel` — so a learned world-model / policy / value can be\n injected without touching the core; with all three `None` the engine is a plain\n UCT search.\n - `ObservedTransitionModel` adapter — a cheap online lookup world-model (hit =\n exact stored transition; miss = identity + negative \"unknown\" reward +\n `is_uncertain`, biasing exploration toward learning), with JSONL save/load for\n cross-run reuse.\n - `UnifiedMCTS(config).search(env)` returns an `MCTSResult` (best action, value,\n confidence, principal-variation path) and an optional `MCTSTrace`\n (visit-distribution policy target) for distillation.\n- **Self-bootstrapping agent machinery.** Build an agent from a declarative spec\n instead of bespoke wiring:\n - `adk.bootstrap.build_agent_from_spec(path) -> (AitherAgent, RunCtx)` — resolves\n prompts, activates required/optional tool packs, and carries a `memory_map`\n (a `node_type -> (role, tier)` override for memory writes, never a gate).\n - `adk.prompts.PromptBridge` — resolves an agent's prompt bindings from files\n (relative to the spec) or `AitherPrompts` dot-keys (optional import).\n - `adk.packs.PackActivator` — eager activation of installed tool packs by name\n over `register_tool_packs`; a required pack that registers zero tools raises\n `PackUnavailable`, optional packs soft-degrade.\n - `python -m adk.bootstrap.generic_agent <spec.yaml>` inspects a spec (resolved\n name, activated packs + tool counts, prompt keys, memory map) without running a\n live loop.\n- **`deep_research` tool pack** — a full multi-engine web-research workflow as\n `dr_*` agent tools (keyless search, page fetch + cache, site mirror, a citable\n findings knowledge graph, and markdown/PDF/DOCX report generation), activatable\n by name via the tool-pack loader.\n\n## [2.14.4] - 2026-07-04\n\n### Added\n\n- **`structured_ml` tool domain** — zero-shot structured-data inference for agents,\n backed by TabFM (tabular classification/regression) and TimesFM (time-series\n forecasting). Three tools:\n - `tabular_classify(support_rows, target, query_rows)` — classify rows from a\n labeled support set, in-context (no training step; up to 10 classes).\n - `tabular_regress(support_rows, target, query_rows)` — predict a numeric target.\n - `timeseries_forecast(series, horizon)` — forecast future values of a series.\n\n The tools POST to a structured-ML inference service resolved from\n `AITHER_STRUCTURED_ML_URL` (default `http://localhost:8192`), reject non-http(s)\n URLs, and summarise oversized responses to protect the agent's context.\n- **`Capability.STRUCTURED_INFERENCE`** capability token and a new **`analyst`**\n identity/pack that ships the `structured_ml` domain by default. The domain is\n opt-in — only identities that enable the `structured_ml` category get the tools.\n\n## [2.14.3] - 2026-07-03\n\n### Fixed\n\nAll four bugs below were found by actually booting `adk run` against a real\nchat model and driving a real tool-calling request end-to-end (not inferred\nfrom reading the code) — each one silently broke the \"local agent uses its\nconfigured model and its tools actually work\" path.\n\n- **`AITHER_MODEL` / `config.model` silently ignored by the OpenAI-compatible\n backend**: `LLMRouter.__init__` never read `config.model` into `self._model`\n unless the caller passed `model=` directly to the constructor — which nothing\n did. Every explicit model choice (env var, config file) was dropped, the\n provider fell back to a hardcoded per-backend default (`gpt-4o-mini` for the\n `openai` backend), and an unrecognized model name got silently remapped\n server-side to the wrong backing model. Concretely: setting\n `AITHER_MODEL=aither-orchestrator` had no effect — every request actually\n went to a different model that was never meant to be called directly and\n can't reliably use tools.\n- **`OPENAI_BASE_URL` ignored for explicit `--backend`/`AITHER_LLM_BACKEND`\n selection**: `LLMRouter._auto_detect()`'s explicit-backend branch hardcoded\n `base_url=None` when constructing the provider, instead of reading\n `config.openai_base_url` (which two other call sites in the same file\n already did correctly). Any self-hosted OpenAI-compatible endpoint\n (MicroScheduler, vLLM, LM Studio) configured via `OPENAI_BASE_URL` was\n silently ignored in favor of `https://api.openai.com/v1`.\n- **Tool-call steering never engaged on default-effort chat**: the retry that\n nudges a model back into structured tool-calling when it responds with prose\n instead of a function call was gated behind `effort >= 6`. Default `effort`\n for a plain `agent.chat(message)` call resolves to `5`, so the safety net —\n built specifically for this failure mode — never fired for default requests.\n Also now forces `tool_choice=\"required\"` on the retry instead of relying on a\n text nudge alone.\n- **Every non-string builtin-tool parameter silently typed as `\"string\"` in\n the generated tool schema**: `builtin_tools.py` uses\n `from __future__ import annotations`, so `_extract_parameters()` in\n `tools.py` was reading postponed (string) type hints (`\"int\"`, not `int`)\n straight out of `fn.__annotations__`, which never matched\n `_type_to_schema`'s type-object keys and silently fell through to\n `{\"type\": \"string\"}`. Every builtin tool with an `int`/`float`/`bool`/`list`\n parameter was affected — e.g. `file_read(start_line: int, end_line: int)`\n advertised both as strings, so a model correctly calling the tool with\n `\"start_line\": \"0\"` crashed the actual implementation with\n `unsupported operand type(s) for -: 'str' and 'int'`. Fixed by resolving\n hints through `typing.get_type_hints()` instead of the raw annotations dict.\n `_coerce_arguments()` — the runtime safety net that's supposed to coerce a\n model-supplied `\"0\"` string into `0` before a tool call executes — had the\n exact same raw-`__annotations__` bug and silently no-opped for every builtin\n tool; fixed the same way.\n- **Streaming tool-loop could hang a connection forever**: `chat_stream()`'s\n fallback to the sync tool loop (tool calls can't stream token-by-token) had\n no timeout, so a model that didn't cleanly terminate a tool-calling round\n trip left the SSE connection open indefinitely with zero bytes sent. Bounded\n with a 60s `asyncio.wait_for`; on timeout the stream yields a clear message\n instead of hanging.\n\n## [2.14.2] - 2026-07-03\n\n### Fixed\n- **AitherShell session amnesia**: the REPL generated a fresh `session_id` for\n EVERY message, so conversation history never followed across turns (the\n server stored each turn under a different session). One stable id is now\n minted per shell run; `/resume` and `/new` semantics unchanged, and the\n cross-restart auto-restore of the last session now actually works.\n\n## [2.14.1] - 2026-07-02\n\n### Added\n- **Account-tied licensing via device-flow login** — `adk login` now persists the account\n license returned by the device-flow handshake to `~/.aither/license.json` (base64\n `license_key` → `{payload, signature}` envelope), so entitlements from the authenticated\n account carry into every later `adk` invocation without re-auth. Pairs with the fleet-side\n node tool-pack gate that grants packs by plan-tier rank.\n- **`can_use_formbridge` / `can_use_untether` entitlements** (PROFESSIONAL+ rank ≥ 3) backing\n the FormBridge and UNTETHER agent tool-packs.\n\n### Fixed\n- `adk` CLI: guard the optional `memory_graph` singleton (was a latent `NameError` on the\n `/stats`, `/memory`, and save paths when memory was not constructed).\n\n## [2.14.0] - 2026-07-02\n\n### Added\n- **`adk.memory_wiki` — LyraWiki-style self-managed semantic knowledge** (opt-in; nothing constructs it by default): `MemoryWiki(graph_memory)` maintains curated wiki ARTICLES as `wiki_article` nodes (title/slug, LLM-owned markdown with `[[wikilinks]]`, embedding, durable tier, `CONSOLIDATED_FROM` edges to source memories, governance-ledgered revisions).\n - `consolidate(llm, since=, budget=)` clusters unconsolidated episodic/fact nodes (embedding + keyword), drafts/UPDATEs articles via the INJECTED llm callable (`str -> str` or `messages -> str`, sync or async — no hard model dependency), cites source node ids, marks contradicted sources superseded, then DEMOTES consolidated sources to a fast-decay tier via `promote()` (represented knowledge doesn't need raw copies).\n - `lint(llm=None)` — deterministic health checks (orphan wikilinks, empty/stale articles, live-but-contradicted pairs) + an optional LLM audit pass.\n - `prune(relevance_floor=0.05, hard_delete_after_days=14)` — article relevance = tier freshness × reinforcement (reinforce-on-recall) + link-degree to LIVE memories; below the floor → reversible governance tombstone; entombed past the window → **HARD-DELETE** (the tombstone snapshot itself is purged — content and embedding irrecoverable; the true-deletion path). A node is never deleted without a tombstone first.\n - `recall(query, limit)` — article-first RAG (curated articles rank above raw nodes) that reinforces returned articles.\n- **`adk.routines` — agent heartbeat + self-programmed routines**: `RoutineStore` is a durable registry (`~/.aither/routines/{agent}.json`, `AITHER_DATA_DIR`-aware, injectable path) of cron-scheduled self-prompts `{name, cron, instruction, enabled, last_run, last_result, tags}` that rehydrates into the existing `adk.cron.CronScheduler` on `start()`. A fire runs `agent.chat(instruction)` with the agent's full toolset — the agent programs its future self with its own adk. Guardrails: `max_routines` (12), a 5-minute min fire interval per routine, a per-fire timeout, and every result ledgered (truncated) to `last_result`. Direct-callable routines (`register_direct`) fire a bound method instead of a self-prompt. Self-management tools (`routine_create/list/update/pause/resume/delete/run_now`) ship as OpenAI tool defs + handlers (`build_routine_tools` / `register_routine_tools` / `routine_tool_defs`); the handlers ONLY touch the RoutineStore — they can never modify agent config or safety-relevant settings (the leash principle).\n- **`AitherAgent(routines=True, memory_maintenance=True)`** (both default **False** → the default agent is byte-identical): `routines=True` builds the RoutineStore + scheduler, registers the self-management tools, and starts the heartbeat lazily on the first `chat()` (or via explicit `start_routines()`). `memory_maintenance=True` registers DEFAULT routines — `wiki_consolidate` (every 2 h), `wiki_lint` / `wiki_prune` / `graph_sweep` (daily) — as DIRECT method fires so memory upkeep runs even on tiny models, while staying visible/manageable via `routine_list`.\n- `TombstoneStore.purge(tombstone_id)` — the hard-delete endpoint: irrecoverably removes a tombstone snapshot and rewrites the persisted store without it (consumed by `memory_wiki.prune`).\n\n## [2.13.6] - 2026-07-02\n\n### Added\n- **Qdrant backend for GraphMemory dataplane sync — TRUE two-way, verified live.** Set `AITHER_FLEET_QDRANT_URL` and the sync uses Qdrant (`upsert` + `scroll` + filtered `search`) instead of Nexus. This is what actually delivers cross-agent memory sharing: a node is upserted with its OWN embedding under a deterministic per-(tenant,node) point id (idempotent re-push), and `fleet_pull` reliably SCROLLS a tenant's points back — the enumerate Nexus lacks (its `/search` doesn't return ingested docs and `/export` needs lancedb, which has a hard dependency conflict in the fleet). **Proven live with 2 agents:** agent-1 ingests → auto-syncs to the tenant dataplane; a fresh agent-2 in the same tenant `fleet_pull`s all of agent-1's nodes and can search them; a different tenant pulls nothing (isolation). Dimension-safe (never mixes vector dims in a collection). Local SQLite stays source of truth; best-effort, never raises.\n\n## [2.13.5] - 2026-07-02\n\n### Fixed\n- **CRITICAL — GraphMemory dataplane sync push was 100% failing.** `_fleet_push_node` sent `source_type=\"graph\"` / `content_type=\"graph_node\"`, which AitherNexus rejects as invalid enums (HTTP 500) — so NO graph node ever replicated in 2.13.4. Now sends the accepted `source_type=\"manual\"` / `content_type=\"text\"` (graph provenance is carried in `metadata.synced_from`). **Verified live** against a fleet Nexus: a 4-node ingest replicates with `pending==0` and the collection count reflects it.\n\n### Added\n- **Swarm-awareness notification** — on a successful sync batch, `GraphMemory` emits a best-effort `graph.synced` event (tenant/workspace/agent/collection/count) to `AITHER_FLEET_EVENTS_URL` so OTHER agents in the tenant/swarm can pull the fresh data and deconflict in-flight work (push-based awareness instead of polling). No-op unless the env is set.\n\n### Known limitation\n- Cross-agent **rehydration** (`fleet_pull`) is only reliable when the fleet Nexus is backed by lancedb (persistent + exportable). An in-memory Nexus stores on `/ingest` but neither its `/search` nor `/export` returns the docs — two-way read needs the persistent Nexus.\n\n## [2.13.4] - 2026-07-02\n\n### Added\n- **Memory-maintenance primitives on `GraphMemory`** (all opt-in; default behaviour byte-identical):\n - **Reinforce-on-recall**: `recall_with_activation(..., reinforce=True)` bumps `reinforcement_count`/`last_reinforced` on every RETURNED node's metadata mirror (one UPDATE per node, WAL-safe) — making the `MemoryRecord` reinforcement fields the activation scorer reads LIVE. Default `False` = zero writes.\n - **`sweep(now, archive_below=0.05, max_nodes=None, dry_run=False)`**: read-time decay enforced at rest — archives nodes past their tier TTL with freshness×reinforcement score below `archive_below` and `reinforcement_count <= 1`; `max_nodes` additionally archives the lowest-scored overflow. PERMANENT tier immune. Every archive is a reversible governance tombstone (`TombstoneStore.recover`) + a FORGET ledger entry; a node is never deleted without a tombstone. `dry_run` returns the would-archive list without acting.\n - **`promote(node_id, tier=?, role=?)`**: re-tier/re-role a node IN PLACE (tier/role columns + metadata mirror) — edges, embedding and ledger history survive; ledgers an UPDATE entry when governance is on.\n- `MutationType.UPDATE` ledger entry kind (in-place re-tier/re-role).\n- **Per-tenant dataplane sync for `GraphMemory`** — replicate the local graph to a per-tenant vector dataplane (Nexus/Qdrant) so a sovereign agent's memory is durable + rehydratable across restarts and hosting boundaries. `fleet_push_all_nodes()` (catch-up), `fleet_pull()` (best-effort semantic top-up), `fleet_sync_pending()`, and auto-sync on ingest (`_maybe_autosync` + `drain_sync()`). **On by default** (`AITHER_FLEET_SYNC=auto`; only `false/0/off` disables) with an inferred target (local Nexus `:8122` or the gateway in cloud mode) and a SEPARATE collection (`AITHER_FLEET_GRAPH_COLLECTION`, default `graph_memory`) from the KV memory sync. New `GraphMemory(tenant_id=, workspace_id=, fleet_url=, auto_sync=)` constructor args override env (required for multi-tenant host processes). Node `synced` column + schema v4 migration. TLS verify is CA-bundle-aware (`AITHER_CA_BUNDLE`) for the internal-TLS mesh. Local SQLite stays the source of truth; every sync path is best-effort and never raises into ingest.\n\n## [2.13.3] - 2026-07-02\n\n### Added\n- **Canonical self-deploying embeddings provider** (`adk.embeddings`): one embedding provider for the whole SDK so vectors are portable across scopes (platform / tenant / sovereign) — same model + dimension everywhere. Pinned to `nomic-embed-text` (768-d; the vLLM `--served-model-name` for nomic-embed-text-v1.5). Lazy, single-flight resolution chain: explicit `AITHER_EMBEDDINGS_URL` → local vLLM (`:8209` then `:8120`, HTTPS-then-HTTP) → local Ollama → gateway (`AITHER_GATEWAY_EMBEDDINGS_URL`) → auto-deploy a local vLLM embeddings container if a GPU + Docker are present (opt out with `AITHER_EMBED_AUTODEPLOY=0`) → CPU sentence-transformers (384-d, degraded) → feature-hash (384-d, degraded, always works). Every batch is dimension-tagged so callers never silently mix 768-d and 384-d vectors. `get_default_embedder()`, `embed_texts()`, `embed_one()`, `get_provider()`, `reset_provider()`.\n\n### Changed\n- **`GraphMemory` now defaults its embedder to the canonical provider** (opt out with `AITHER_GRAPH_EMBEDDER=legacy`), so every adk agent shares one 768-d space. Added a meta-table dimension guard: the index is pinned to its first embedding's dimension and later different-dimension vectors are refused (kills 768↔384 mixing that silently poisons cosine similarity).\n\n## [2.13.2] - 2026-07-01\n\n### Added\n- **Session persistence**: AitherShell now auto-saves the active session_id to `~/.aither/config.yaml` after each chat turn and auto-restores it on the next shell launch. Multi-turn conversations survive across restarts — the messages were always persisted in SQLite + JSON, only the session pointer was missing.\n- `/new` command to start a fresh session (clears saved session_id).\n- `/sessions` command to list recent sessions with agent name, message count, and timestamp. Resume any with `/resume <id>`.\n\n## [2.13.1] - 2026-07-01\n\n### Fixed\n- **CRITICAL:** `deploy_connect()` now sends a HEAD request to verify the GitHub release asset exists before downloading, with a clear error if the asset is missing.\n- **CRITICAL:** Compose file URLs are now pinned to a stable commit hash (configurable via `AITHER_COMPOSE_PIN` env) instead of tracking the mutable `main` branch.\n- **CRITICAL:** `deploy_adk_node()` now falls back to starting `adk-serve` natively when Docker is unavailable, instead of blocking with \"Docker not installed\".\n- **HIGH:** GHCR login failure now blocks deployment immediately with actionable guidance, instead of warning and continuing to a confusing image-pull failure.\n- **HIGH:** Health checks now stream the last 30 lines of Docker Compose logs on failure, so the user can see why a container didn't start.\n- **HIGH:** vLLM port allocation now probes for conflicts before assigning; if port 8200 is busy, workers are automatically remapped to the next free port.\n- **MEDIUM:** Compose file downloads are now cached with ETag; re-running `adk deploy node` skips re-downloading an unchanged file.\n- **MEDIUM:** DGX Spark discovery now respects `AITHER_DGX_HOSTS` env (comma-separated) for custom network topologies, instead of only checking `spark.local` and `192.168.0.33`.\n- **MEDIUM:** Cloud API keys (Anthropic, OpenAI, DeepSeek) are now validated with a lightweight probe during `adk setup` infra scan, so invalid keys surface early.\n- **MEDIUM:** `deploy_connect()` now validates the extracted extension has a `manifest.json` and warns if it's missing.\n\n## [2.13.0] - 2026-07-01\n\n### Removed\n- **`adk.faculties.memory_graph`** — the pickle-based `MemoryGraph` module is deleted. `from adk.faculties import MemoryGraph` now resolves to the canonical `adk.graph_memory.GraphMemory` for back-compat.\n- **`adk.platform.memory`** — the entire legacy memory subsystem (`MemoryManager`, `UnifiedMemorySystem`, `GameEngine`, `StoryboardEngine`, `AnchorGenerator`) is deleted. Agent memory is `adk.memory.Memory` + `adk.graph_memory.GraphMemory`.\n\n### Changed\n- `adk work` command now uses `GraphMemory` instead of the removed pickle-based faculty graph.\n- `adk.MemoryGraph` and `adk.faculties.MemoryGraph` lazy exports now resolve to `adk.graph_memory.GraphMemory`.\n- `agent.set_memory_graph()` kept for back-compat but documented as deprecated; agents already wire `GraphMemory` automatically in `__init__`.\n- Platform startup (`adk.platform.infrastructure.startup`) gracefully handles missing `MemoryManager`.\n- Platform CLI module listing and health check updated to reflect removed memory module.\n\n## [2.12.7] - 2026-07-01\n\n### Deprecated\n- `adk.faculties.MemoryGraph` — emits `DeprecationWarning` on instantiation; use `adk.graph_memory.GraphMemory` (SQLite-backed, governed, embedding-native) instead.\n- `adk.platform.memory` package — the legacy `MemoryManager`, `UnifiedMemorySystem`, `GameEngine`, and `StoryboardEngine` modules now emit `DeprecationWarning` on import; use `adk.memory.Memory` and `adk.graph_memory.GraphMemory` for new code.\n\n### Changed\n- Updated `adk.faculties` docstring and usage examples to point at the canonical memory surface.\n\n## [2.12.6] - 2026-07-01\n\n### Changed\n- Trimmed public package debt by removing grid runbook artifacts and shell/room binary placeholder directories from published artifacts.\n- Removed stale public-sync allowlist entries for deleted phase test files and added explicit public-sync guards for binary placeholder directories and grid artifacts.\n- Updated the public README grid note so it no longer links to runbook files excluded from the SDK package.\n\n## [2.12.5] - 2026-07-01\n\n### Added\n- Added best-effort fleet memory sync from local ADK memory into Nexus/Qdrant-compatible RAG stores, including batch catch-up and vector-search recall hooks.\n- Added saved config/env support for fleet memory URL, collection, and sync mode.\n\n### Changed\n- Excluded FormBridge runtime/test internals from public ADK packaging and public repo sync.\n\n## [2.12.4] - 2026-07-01\n\n### Added\n- Added a hot-reload endpoint for installed agent packs so newly applied pack tools can be picked up without restarting `aither-serve`.\n\n### Fixed\n- Fixed synchronous `ChatRelay` calls on Python 3.12 when no event loop is running; async WebSocket delivery is still scheduled when a loop exists.\n- Aligned gateway inference tier tests with the current public ladder: free, starter, pro, enterprise, platform.\n\n## [2.12.0] - 2026-06-28\n\n### Added — one-command local inference + upgrade path (click-to-run)\n- **`adk quickstart-local`** — detects hardware, picks a backend\n (Ollama / llama.cpp / vLLM), installs it, downloads a model, verifies, and\n prints next steps. Bootstraps Ollama itself via winget/brew/install.sh when\n missing (no \"go install it\" dead-ends). Default model `gemma4:e2b`.\n- **`adk backend switch <ollama|llamacpp|vllm>`** (+ `backend status`) — migrate\n the active deployment between engines; re-points config and smoke-tests.\n- **`adk install pack:<name>`** (+ `adk install list` / `adk packs`) — install\n ready-made agent packs (openclaw, hermes, claude-code) into `~/.aither/agents`.\n\n### Fixed (found via live end-to-end testing on Windows + CUDA)\n- quickstart-local reported success on a dead endpoint (smoke test was warn-only);\n a failed smoke now fails the command, with a service-readiness wait.\n- `--model` resolved to `None` (getattr default never fired) → uses the real default.\n- Smoke test: 30s→180s timeout for cold model load; accepts reasoning-model output\n (empty `content` at low token budgets is no longer a false failure).\n- `adk install pack:<name>` colon syntax (argparse rejected it before dispatch).\n- `adk backend status` health probe used a nonexistent `/health` path → 404; now\n probes `/v1/models` (uniform OpenAI-compatible liveness for all three backends).\n- Windows: removed non-ASCII glyphs from printed strings that crashed the cp1252\n console (a `✓` even made a successful install report failure).\n\n## [2.11.1] - 2026-06-14\n\n### Fixed — `adk host` / `adk login` device flow (autonomous onboarding)\nFound driving `adk host` against production; all broke real device-flow login:\n- **Device flow hit the portal FRONTEND, not AitherIdentity.** `adk login`/`adk host`\n POSTed `/auth/device/code` to portal/veil.aitherium.com, which 307-redirects API auth\n to `/login`. Added `_resolve_identity_url()` mapping the aitherium.com topology to\n `idp.aitherium.com` (the Identity API); localhost/bespoke pass through unchanged.\n- **Cloudflare 403'd urllib.** `idp.*` sits behind Cloudflare, which blocks urllib's\n default `Python-urllib/3.x` UA. Device-code + poll requests now send a real\n User-Agent + Accept (verified urllib→idp 200, was 403).\n- **`adk host` now self-authenticates via device flow** when there's no token, and\n **retries via device flow on a 401/403** (a stale saved token no longer dead-ends\n registration).\n\n## [2.11.0] - 2026-06-14\n\n### Added — attach your own MCP \"hands\" to a self-hosted agent\nA self-hosted agent now has the full brain (your model) · body (this loop) · **hands**\n(your tools) trifecta. The AitherOS platform relays the MCP servers you registered for\nyour agent in the `/stream` request body as `mcp_endpoints`, and the loop wires their\ntools into the ReAct turn:\n- **`adk/mcp_endpoint_tools.register_mcp_endpoint_tools(agent, endpoints)`** — speaks MCP\n JSON-RPC (`tools/list` to discover, `tools/call` to invoke) and registers a namespaced\n proxy tool (`{endpoint}__{tool}`) for each advertised tool. Mirrors `app_proxy_tools`.\n Best-effort: an unreachable endpoint registers nothing and never raises.\n- **`POST /stream` and `POST /chat/stream`** accept an optional `mcp_endpoints` list\n (`[{name, url, [headers]}]`); `_aitheros_stream` attaches them to the agent before the\n turn. Existing callers are unaffected (the field is optional).\n\n## [2.10.1] - 2026-06-14\n\n### Fixed — self-hosted `aither-serve` was crash-on-startup (2.10.0 regression)\nA fresh `pip install aither-adk && aither-serve --backend deepseek` could not boot. Found\nand fixed while standing up a real self-hosted DeepSeek agent end-to-end:\n- **Startup `NameError: name 'port' is not defined`** in three lifespan helpers\n (`_join_aithernet`, `_init_a2a_server`, `_start_mesh_hosting`) — they referenced a bare\n `port` no longer in scope. Now use `config.server_port`, and `main()` writes the resolved\n `--port`/`--host` back to `config` so every lifespan helper and the invoke_url see the\n actual bound port.\n- **Fleet self-registration crashed the whole server** — the registration handlers caught\n `(ImportError, RuntimeError, OSError, ConnectionError)` but **not** `httpx.HTTPError`, so a\n `RemoteProtocolError`/connect error talking to the control plane took the agent down. All\n network-facing lifespan handlers now also catch `httpx.HTTPError` (non-fatal, as intended).\n- **`--backend` was ignored when `AITHER_API_KEY` was set** — `_auto_detect` picked the cloud\n gateway before the explicit backend, and `_try_elysium_fallback` then replaced the chosen\n provider. An explicit backend (deepseek/openai/anthropic/your own vllm/ollama) now always\n wins; \"auto\"/\"gateway\" still flow through detection. The operator's own brain is never\n silently hijacked.\n- **Approved-tool trace truncated after `/sessions/{id}/confirm`** — the SSE relay treated\n `AgentResponse.tool_calls_made` (a `list[str]`) as dicts (`tc.get(...)`), raised\n `AttributeError`, and an over-narrow `except` swallowed it so the client saw only\n `session_start` + heartbeats. The relay now handles string tool names, the `except` is\n broad (always emits a typed `error` + terminal `complete`), and the module-level\n `_strata_ingest` `NameError` + Chronicle `session_id` kwarg mismatch are fixed.\n\n## [2.10.0] - 2026-06-13\n\n### Added — human-in-the-loop tool approval (`adk/approval.py`)\n- **Pause before a gated tool, resume on decision.** An agent can now pause its turn before\n executing a sensitive tool and wait for a human's allow/deny — so a managed control plane\n (or any operator) can gate tool use without forking the loop.\n- **Policy via `AITHER_TOOL_APPROVAL`** — a comma-separated list of tool names (optionally\n `agent:tool`), or `*` for every tool. Empty = no gating (unchanged behavior).\n- **Survives restarts / days-later approvals.** The paused turn (user message + pending tool\n calls) is persisted to disk keyed by `session_id`; resume is a fresh request, not a held\n connection. Decisions are recorded per `(session_id, tool_name)`.\n- **`AgentResponse.requires_action` / `.pending`** — set when a turn paused; `pending` lists\n the gated tool calls awaiting a decision.\n- **`AitherAgent.resume(session_id, decisions)`** — records the decisions and re-runs the\n paused turn (the gate consumes them: allowed tools execute, denied tools get a denial\n observation, the turn continues — and may pause again on a different gated tool).\n- **Server:** `POST /sessions/{id}/confirm` records decisions and streams the resumed turn;\n `/stream` emits a `requires_action` event when a turn pauses.\n\n## [2.9.0] - 2026-06-13\n\n### Added — universal LLM continuation primitive (`adk/llm/continuation.py`)\n- **`run_continuation()` + `stitch()`** — one shared continue-until-complete primitive. When a\n completion stops because it hit the output token cap (`finish_reason == \"length\"`), it continues\n the generation and STITCHES the chunks (overlap-dedup + restart-detect) into a complete answer,\n instead of every call-site hand-rolling its own retry. Bounded three ways (max rounds AND total\n output ceiling AND a no-growth break), and it never mutates the caller's message history.\n- **`LLMProvider.chat_with_continuation()`** and **`LLMRouter.chat_with_continuation()`** — every\n provider AND the router inherit the primitive, so any call-site gets continuation for free.\n- Kill-switch `ADK_LLM_CONTINUATION=off`; tunables `ADK_LLM_MAX_CONTINUATIONS`,\n `ADK_LLM_MAX_TOTAL_OUTPUT_TOKENS` (defaults: 2 rounds / 8192 tokens).\n\n### Changed\n- The ReAct loop's truncation recovery now uses the shared primitive. The previous inline\n doubling-retry REPLACED the partial response (losing earlier text in `resp.content`) and injected\n continuation scaffolding into the live message history; it now STITCHES and leaves history clean.\n Tool-call turns are never continued as text.\n\n### Included — previously-committed-but-unreleased changes\n- 2.8.2: TLS verification on by default (`verify=False` removed).\n- 2.8.1: baked license + pack-signing public keys.\n- 2.8.0: version-bump release.\n\n## [2.7.0] - 2026-06-05\n\n### Added — provider-agnostic LLM cost levers (at the `LLMProvider` ABC)\n- **Prompt caching.** New `ProviderCapabilities` (`prompt_cache: explicit|automatic|none`, `batch`) and\n normalized `LLMResponse.cache_read_tokens`/`cache_write_tokens`, so the \"tokens saved\" meter never\n depends on which backend served the turn. Anthropic inserts `cache_control` breakpoints over the stable\n system+tools prefix; OpenAI/DeepSeek caching is automatic and their `cached_tokens` are now surfaced\n (previously discarded). Default-on in `AitherAgent.chat`; override with `cache=False`.\n- **ResponseCache** (`adk/llm/cache.py`) — exact-match response cache with a pluggable backend, wired into\n `LLMRouter` as an opt-in (`LLMRouter(response_cache=…)` + per-call `cacheable=True`). OFF by default — no\n determinism change unless opted in.\n- **Gemini provider** (`adk/llm/gemini.py`) with explicit `cachedContents` caching; registered in the router.\n- **Async batch runner** (`adk/llm/batch_runner.py`) — `run_batch()` over the Anthropic Message Batches /\n OpenAI Batch APIs (~50% off) with a transparent fallback to concurrent `chat()` where unsupported.\n- **Graph-RAG retriever interface** (`adk/graph_retriever.py`).\n\n### Added — principal authority (prompt-injection defense enforced outside the model)\n- `adk/auth.py`: `Principal` / `AuthContext` / `PrincipalResolver` (+ deny-all default).\n- `ToolRegistry.execute(name, args, auth=…)` — a default-deny authorization gate at the tool boundary, so a\n forbidden tool call is blocked regardless of what the LLM emits. `ToolDef` gains `required_clearance` /\n `action_class`. Threaded through `AitherAgent.chat`; `auth=None` is fully backward-compatible.\n- `adk/channel_auth.py`: Telegram webhook-secret + CEO-id checks and HMAC-signed approvals.\n\n## [2.6.4] - 2026-06-05\n\n### Fixes\n- **Loop-guard nudges corrupted the message history → DeepSeek 400 mid-run.**\n When the ReAct loop emitted multiple tool calls in one assistant turn and the\n loop guard fired a WARN/BLOCK/CIRCUIT_BREAK on one of them, it spliced a\n standalone `system` nudge message *between* the tool results. DeepSeek (and\n strict vLLM chat templates) require an assistant `tool_calls` message to be\n followed by exactly one `tool` result per `tool_call_id`, contiguously — so the\n interleaved message produced `400 … insufficient tool messages following\n tool_calls message` and aborted the turn (this is reliably hit after crossing\n the 15-call circuit-break threshold, where every subsequent call WARNs).\n Per-call guidance is now folded into that call's own `tool` result, and any\n standalone steering is deferred to a single message AFTER the whole batch —\n preserving the `tool_calls`↔`tool_results` pairing every backend requires.\n Regression test: `deep-research-agent/tests/test_msg_structure.py`\n (loopguard-flood scenario).\n\n## [2.6.3] - 2026-06-04\n\n### Fixes\n- **Transient provider errors killed a run on the first blip.** A single\n `502 Bad Gateway` / `503` / `529 overloaded` / `429` from the upstream API\n aborted the whole request — the Anthropic provider had **no retry at all**, and\n the OpenAI-compatible provider only retried `429`. Both now retry transient\n statuses (`408/429/500/502/503/504/529`) with `Retry-After`-aware exponential\n backoff (3 attempts), and surface the provider's body if all retries are\n exhausted. Non-transient `4xx` (e.g. `400`) still fails fast. Applies to\n `chat` and `chat_stream` on both providers.\n\n## [2.6.2] - 2026-06-04\n\n### Fixes\n- **DeepSeek (and strict OpenAI-compatible backends) 400'd on long tool\n conversations.** The ReAct loop legitimately injects mid-conversation\n `system` steering messages (the \"[DIMINISHING RETURNS]\" hint after several\n low-output tool iterations, loop-guard nudges). OpenAI tolerates a non-leading\n `system` message; DeepSeek rejects it with a 400. Long research runs\n (many `fetch_url` calls) reliably tripped this. Fix: non-leading `system`\n messages are demoted to `user` at the OpenAI-compatible provider boundary\n (`_demote_nonleading_system`) — steering intent preserved, payload valid on\n every backend; leading system prompt(s) untouched.\n- **Opaque HTTP errors.** `resp.raise_for_status()` discarded the provider's\n response body, so a 400/422 surfaced as a bare `Client error '400 Bad\n Request'` with no cause. `_ensure_ok` now includes the provider's actual\n message (e.g. `context_length_exceeded`, `Invalid 'messages'`) in the raised\n error, for both `chat` and `chat_stream`.\n\n## [2.6.1] - 2026-06-04\n\n### Fixes\n- **`MCPServer.mount()` returned HTTP 422 for every `/mcp` call.** Under\n `from __future__ import annotations` the route handler's `request: Request`\n annotation is a string that FastAPI resolves against the *module* globals, but\n `Request` was imported locally inside `mount()` — so FastAPI couldn't resolve\n it and treated `request` as a required query param. The MCP server endpoint\n (`initialize`, `tools/list`, `tools/call`) was unusable when mounted. FastAPI\n symbols are now imported at module level (guarded; the `[node]` extra still\n gates actual use). Any agent's tools can now be exposed as a working MCP\n server with `MCPServer(...).mount(app)`.\n\n## [2.6.0] - 2026-06-04\n\n### Streaming + scaffolding\n- **`AitherAgent.stream_chat`** — reliable streaming sibling of `chat()`: native\n tool-calling loop (concludes on its own, unlike the text-protocol `stream_react`)\n that emits live `tool`/`tool_result` events and streams the answer as `token`\n events. `token_delay` paces tokens for a typing effect.\n- **`adk new <template>`** — scaffold a full, runnable template app. Ships the\n `deep-research` template (a cited web-research agent: pack + server + web UI):\n `adk new deep-research`.\n\n### Fixes\n- **BYO-key no longer community-capped.** The free-tier monthly token cap now applies\n only to the metered Aitherium gateway backend, never to a user's own provider key\n (Anthropic/OpenAI/DeepSeek/Ollama/vLLM). Previously it could brick a self-hosted\n agent after ~100k tokens on the user's own key.\n- **Stronger, grounded memory recall.** `chat()` now injects the top-6 knowledge-graph\n hits (untruncated) with a hard no-fabrication instruction — was top-3 truncated to\n 200 chars, which surfaced partial facts and let weaker models invent the rest.\n- **Tool-arg coercion.** `ToolRegistry.execute` coerces LLM-supplied args toward each\n parameter's annotated type, so a tool no longer crashes when a model passes a list\n or string where an int/float/bool was expected.\n\n### Docs\n- New `docs/AGENT_DEV_GUIDE.md` — the golden path for building agents/packs.\n\n## [2.5.0] - 2026-06-04\n\n### Continual learning — agents that learn from runs and reuse memory\n\nAgents now improve the second time they do a task instead of starting from scratch.\n\n### Added\n- **Recall-before**: `AitherAgent.chat()` searches the local `SkillStore` and injects the\n top matching learned skills as a system block, so the model reuses proven procedures.\n- **Learn-after**: `_learn_after()` runs after each response — reinforces (`success_count++`)\n any skills that were recalled+reused, and extracts + saves a NEW skill from successful\n multi-tool runs via `SkillExtractor`. Wired at both `chat()` return paths.\n- Gated by `AITHER_SKILLS` (default on). The previously-unused `SkillStore`/`SkillExtractor`\n are now connected to the run loop.\n\n### Notes\n- Pairs with the platform-side LearnedProcedure substrate (recipes → scored procedures →\n skills/tools/A2A/packs → MCTS priors → evolution). The SDK shares memory via the Spirit\n bridge when configured.\n\n## [2.0.0] - 2026-06-02\n\n### Open-core boundary (BREAKING)\n\nThis release draws a single, enforced free/paid line. The free tier stays\ngenuinely useful (real agent, typed + graph + code memory, ReAct, ~essential\ntools); paid tiers unlock the *scale* capabilities via portal.aitherium.com.\n\n### Added\n- **`adk/licensing.py`** — the entitlement keystone. Resolves a tier\n (COMMUNITY by default), verifies optional portal-signed `~/.aither/license.json`\n (Ed25519), and answers `can_use_fleet/channels/cron/swarm/auto_neurons`,\n `is_agent_licensed`, `max_effort`, etc. **Fail-closed**: no/invalid/expired\n license → free tier; an unsigned license never grants premium.\n `AITHER_TENANT_SLUG=aitherium` → unrestricted INTERNAL tier.\n- **Moat guard** (`scripts/check_moat_boundary.py`) wired into the publish\n workflow — blocks any wheel that leaks the moat or drops the keystone.\n- Tests: `tests/test_licensing.py`, `tests/test_moat_boundary.py`.\n\n### Changed (gating — fail-closed)\n- Free tier effort is capped at 3 (reasoning effort 7–10 needs Professional).\n- Monthly token budget hard-block on the free tier (no silent overspend).\n- Gated behind a paid tier: fleet mode (`FleetConfig`), cross-agent delegation\n (`AgentForge.delegate`), channel adapters, cron scheduler, proactive\n auto-neurons, and `swarm`/`swarm_code` dispatch.\n\n### Removed\n- **`adk/nanogpt.py`** (on-device training IP) removed from the published SDK\n and the public repo; relocated to the internal AitherOS moat. Excluded from\n the wheel as defense-in-depth.\n- Pre-2.0 releases shipped this IP and ungated capabilities and are being\n yanked (PyPI) / removed (GitHub releases). See `scripts/yank_leaky_releases.py`\n and `scripts/purge_public_leaks.sh`.\n\n## [1.20.0] - 2026-05-27\n\n### Fixed\n- **Pack discovery path mismatch** — `_find_local_packs()` now scans both\n `~/.aither/packs/` and `~/.aitheros/packs/`, so packs installed via\n `adk pack install` are discovered by `adk-workspace`.\n\n### Added\n- **Auto-register on pack install** — `adk pack install` now auto-registers\n with the portal if the pack contains an `agent.yaml` with a `portal` section.\n One command does download + extract + register.\n- **Flux event on agent registration** — `developer_portal.py` emits\n `agent.registered` Flux event so the portal fleet UI updates in real-time.\n\n## [1.15.0] - 2026-05-26\n\n### Added\n- **Cloud memory** — `adk quickstart --cloud` now tests gateway memory and\n auto-configures Spirit URL so memories persist across devices via\n `gateway.aitherium.com/v1/memory/teach` and `/v1/memory/recall`.\n- **Config→env export for cloud mode** — `Config.from_env()` now exports\n `AITHER_CLOUD_MODE`, `AITHER_SPIRIT_URL`, and spirit path env vars from\n saved config so Memory and other modules pick them up without needing\n explicit env vars set by the user.\n- **Cloud Quick Start section** in README — 3-command setup for users who\n just want a Claude/GPT-4/DeepSeek API key and the full agent harness.\n- **`--memory` profile for node deployment** — `adk deploy node --memory`\n adds Spirit (8087) + WorkingMemory (8101) containers for persistent\n vector memory. Without this, agents use local SQLite only.\n- **Configurable vLLM in node compose** — `AITHER_VLLM_MODEL`,\n `AITHER_VLLM_GPU_UTIL`, `AITHER_VLLM_CTX_LEN`, `AITHER_VLLM_QUANT_ARGS`\n env vars let `adk setup` write GPU-detected tier settings to `.env`\n and have the node compose pick them up (TQ4 for small GPUs, BNB for large).\n\n### Fixed\n- **Memory cloud routing** — `Memory._spirit_teach()` and `_spirit_recall()`\n now use configurable paths and auth headers for gateway proxy. Previously\n cloud-only users silently lost all Spirit memory calls.\n\n## [1.9.0] - 2026-05-24\n\n### Added\n- **Composable ToolPack system** — agents can load tool packs from\n `.toolpack.yaml` directories. Drop a pack in `~/.aitheros/packs/` or\n set `AITHER_PACKS_DIR`, and any agent discovers it automatically.\n- **`tools.packs` in agent.yaml** — list tool pack IDs to auto-load\n (e.g. `packs: [crm-tools, calendar-pro]`).\n- **`register_tool_packs(agent, pack_ids, packs_dir)`** in `builtin_tools` —\n programmatic pack loading for agents.\n- **`AITHER_TOOL_PACKS` env var** — comma-separated pack IDs loaded on\n agent init (alternative to YAML config).\n- **Unknown builtin categories resolve as pack IDs** — `tools.builtin:\n [file_io, crm-tools]` now works: `crm-tools` is looked up as a pack.\n\n## [1.8.0] - 2026-05-24\n\n### Removed\n- **11 dead modules** (3,023 lines) — `agent_deploy`, `cloud_deploy`, `create`,\n `phonehome`, `sdk_bridge`, `sovereign`, `telemetry`, `telemetry_config`,\n `error_reporter`, `anonymizer`, `package`. None were imported anywhere.\n- **29 ghost exports** from `__init__.py` — `secrets`, `otel`, `fs_sandbox`,\n and `vector_memory` modules were listed in `__all__` and `__getattr__` but\n the backing files never existed. Accessing them raised `ModuleNotFoundError`.\n- **Stale dist/ artifacts** — removed old wheels/tarballs (v0.4.0–v1.4.1, 11 MB).\n- **Orphan test scripts** — `test_phase1.py` and `test_phase23.py` (one-off\n validation scripts not part of the pytest suite).\n\n### Fixed\n- **Version mismatch** — `__init__.__version__` now matches `pyproject.toml`\n (was stuck at 1.6.0).\n\n## [1.5.0] - 2026-05-23\n\n### Added\n- **`--sovereign` flag for `adk deploy node`** — registers the node with the\n Aitherium hub via federation after deployment. Saves federation credentials\n to `~/.aither/.env.federation`. Accepts `--hub` (custom hub URL) and\n `--tenant` (tenant slug) options. Non-fatal: node runs standalone if\n registration fails.\n- **Federation → fleet cross-post** (Genesis) — sovereign nodes that register\n or heartbeat via `/federation/register` and `/federation/heartbeat` now\n automatically appear in the fleet dashboard (`/fleet/deployments`) with\n `node_type: \"sovereign\"` and live status/metrics.\n- **\"Deploy Sovereign Node\" section on Connect page** (AitherVeil) — Section 8\n with ADK deploy commands, fleet dashboard links, and provision page links.\n- **`deploy-node` action in tunnel proxy** (AitherVeil) — Connect page and\n external callers can now request bootstrap scripts via\n `POST /api/tunnel/devworkspace?action=deploy-node`.\n\n### Fixed\n- **Fleet provision page** — API URL corrected from `/api/fleet/provision` to\n `/api/bridge/genesis/fleet/provision` (matches bridge proxy pattern).\n- **Fleet detail page** — API URL corrected from `/api/fleet/deployments/` to\n `/api/bridge/genesis/fleet/deployments/` (same fix).\n- **Dockerfile** — entry point corrected from `aither-serve` to `adk-serve`\n (matches `pyproject.toml` script registration). Version label updated.\n\n## [1.4.1] - 2026-05-22\n\n### Fixed\n- **macOS executable builds** — dropped `--target-arch universal2` from\n `packaging/build_executable.py`. PyPI wheels for `pydantic_core` and other\n native deps are arch-specific, not fat binaries, so universal2 always failed\n with `IncompatibleBinaryArchError`. Now builds for the runner's native arch\n (`aither-macos-arm64`).\n- **Release workflow strategy** — added `fail-fast: false` so a single\n executable build failure no longer cancels the other OS jobs.\n- **PyPI publish** — added `skip-existing: true` so re-runs of the same\n version don't fail when the artifact already exists.\n- **GitHub Release job** — now runs whenever `pypi` succeeds even if some\n executable matrix entries fail, so the release page is always created with\n whatever binaries did build successfully.\n\n## [1.4.0] - 2026-05-22\n\n### Added\n- **Sovereign template now ships with `agent_core` 5-tier memory** — generated\n backends include the canonical `agent_core/` package (working → episodic →\n semantic → procedural → identity) plus `agent_memory.py` helper, vendored\n from `portal-kit-backend`.\n- `aiosqlite>=0.20.0` added to sovereign template `requirements.txt` for\n local agent_core SQLite storage.\n- Document tombstoning: `evict_cached_conversations_referencing` is wired\n through document-delete handlers in consumer apps so memories citing\n deleted documents are blocked from recall.\n- New tests: 13 smoke tests for `portal-kit-backend/agent_core` covering\n promotion thresholds, decay rates, store roundtrip, tombstone recall\n blocking, circuit breaker state machine, and chat-turn recording.\n\n### Changed\n- `scripts/vendor_agent_core.py` now syncs canonical `agent_core` to 4\n targets: WorkspaceRuntime, ADK sovereign template, and the GargBot +\n Chelle consumer mirrors (previously only 2).\n\n## [1.2.1] - 2026-05-21\n\n### Added\n- `adk train` command group — 6 subcommands: status, launch, logs, cancel, runs, register-gpu\n- `/slash-commands` endpoint — ADK server auto-exposes all 35 CLI commands as structured JSON manifest for AitherShell\n- `/cli/execute` endpoint — AitherShell can run any CLI command through the server\n- `build_command_manifest()` — introspects full argparse tree (commands, args, choices, subcommands)\n- `_register_commands()` extracted from main() for shared parser construction\n\n### Changed\n- `_get_genesis_url()` now probes: Genesis → ADK server → Aitherium cloud gateway (works without Genesis)\n\n## [1.2.0] - 2026-05-21\n\n### Added\n- **Runtime backend switching** — `agent.switch_backend(\"deepseek\")` and `agent.llm.switch_backend()` change provider without recreating agent\n- **Hybrid reasoning** — `agent.set_reasoning_backend(\"anthropic\")` routes effort 7+ to cloud API while keeping local orchestrator\n- **First-class DeepSeek provider** — `deepseek` in effort model table with `deepseek-chat` and `deepseek-reasoner`\n- **TQ4 quantization tiers** — `nano` (6GB, TurboQuant 4-bit), `standard-tq4` (12GB, both models TQ4), `hybrid-tq4` (6GB + cloud reasoning)\n- **`adk backend` CLI** — `list`, `set`, `set-reasoning`, `test` subcommands\n- **`adk quickstart`** — unified first-run wizard (setup + auth + shell)\n- **`adk tools`** — list local + MCP tools with tier markers\n- **`adk backup`** — tarball export of `~/.aither/`\n- **`adk ingest`** — feed docs/files into knowledge graph\n- **`adk setup nemotron`** shortcut — alias for `--tier lite`\n- **`--reasoning-api`** flag in setup — `anthropic`, `openai`, `deepseek`, `gateway`\n- **`--dgx-spark URL`** flag — remote vLLM endpoint configuration\n- **DGX Spark auto-detection** — scans `spark.local`, `192.168.0.33`, `AITHER_DGX_URL` env\n- **Post-setup smoke test** — sends real inference request after container startup\n- **Schema migration system** — version table + migration runner in `memory.py` and `graph_memory.py`\n- **Configurable vLLM port scan** — `AITHER_VLLM_PORTS` env var for custom ports\n- **`aithershell` entry point** — alias for `adk` in pyproject.toml\n- Shell auto-downloads binary on first `adk shell` (no `--install` needed)\n- Shell pre-flight check warns if no backend detected\n- Shell passes full config via env vars (API key, tenant, inference URL)\n- DGX/Remote and Cloud API checks added to `adk doctor`\n- Config fields: `reasoning_backend`, `reasoning_api_key`, `deepseek_api_key`, `dgx_url`, `vllm_extra_ports`\n\n### Fixed\n- `test_aeon_chat_basic` timeout — mock scope was exiting before request\n- `adk doctor` check_disk timeout — cap rglob at 5000 files\n\n## [1.1.7] - 2026-05-19\n\n### Added\n- Remote agent pipeline — `adk onboard` → fleet dispatch → remote inference\n- Agent-to-agent federation with mesh relay\n- `adk connect` for desktop mesh enrollment\n\n## [1.1.6] - 2026-05-19\n\n### Fixed\n- Tool call execution recovery across all backends\n- Hermes tool call fallback parsing for vLLM\n\n## [1.1.5] - 2026-05-19\n\n### Fixed\n- Tool call execution + Hermes fallback parsing improvements\n\n## [1.1.0] - 2026-05-18\n\n### Added\n- Multi-channel gateway (Telegram, Discord, Slack, webhook)\n- Aeon group chat with 7 presets\n- Skills auto-extraction from multi-step sessions\n- MCP stdio server for Claude Code integration\n- `adk gateway`, `adk cron`, `adk skills`, `adk soul` commands\n- SOUL.md import/export for portable agent identity\n- Elysium cloud device-flow authentication\n\n## [1.0.0] - 2026-05-18\n\n### Breaking\n- Package consolidated — `aither_adk` namespace replaced by `adk` + `adk.platform`\n- 93 duplicate files removed from `AitherOS/packages/aither_adk/`\n\n### Added\n- Standalone core primitives (`adk.core`: Agent, Tool, Memory, ModelBackend, Capability, Trace)\n- `adk.platform` sub-package merging all platform toolkit modules\n- Compatibility shims for legacy `from aither_adk.*` imports\n\n### Changed\n- Version synced across pyproject.toml, `adk/__init__.py`, `adk/platform/__init__.py`, npm package\n- Classifier promoted from Alpha to Beta\n\n## [0.16.0] - 2026-04-16\n\n### Swarm Coding Engine & Repowise Integration\n- **`repowise_search` tool** — Semantic + keyword hybrid code search via Repowise, with ripgrep fallback\n- **`swarm_code` tool** — Dispatch complex tasks to 11-agent swarm pipeline (ARCHITECT->SWARM->REVIEW->JUDGE)\n- **`agent.swarm()`** — Async convenience method with configurable mode, effort, timeout\n- **`agent.code_search()`** — Async convenience method returning structured results\n- New tool categories: `repowise` and `swarm` in TOOL_CATEGORIES\n- `repowise_search` and `swarm_code` added to `__init__.py` exports with lazy loading\n- `IDENTITY_DEFAULTS` updated: repowise for code-focused agents, swarm for orchestration agents\n- Standalone graceful degradation: repowise falls back to ripgrep, swarm returns structured error\n\n## [0.13.0] - 2026-04-02\n\n### Graph Faculties — Local Knowledge for Every Agent\n- **CodeGraph** (2,799 lines) — Full Python AST indexer with call graph, keyword/semantic/hybrid query, embedding matrix cache, incremental re-indexing, multi-hop chain expansion\n- **MemoryGraph** (1,339 lines) — Graph-based persistent agent memory with 10 edge types, hybrid query (keyword + semantic + graph expansion), multi-hop recall, pickle persistence\n- **EmbeddingProvider** — 4-backend fallback chain: sentence-transformers (GPU/CPU) -> Ollama -> Elysium cloud -> feature hashing (zero deps)\n- **BaseFacultyGraph** — Abstract base with HMAC-SHA256 validated pickle persistence\n\n### Agent Integration\n- `agent.set_code_graph(cg)` — auto-registers `code_search` + `code_context` tools\n- `agent.set_memory_graph(mg)` — auto-registers `remember` + `recall` + `memory_stats` tools\n- Both graphs inject context into LLM prompts automatically during chat\n\n### Zero-Config Onboarding\n- `adk start` / `adk` (no args) — auto-detect project, index code, detect LLM, persistent memory, interactive chat\n- Works for any directory: Python codebases, doc folders, mixed workspaces\n- Auto-detects LLM: Ollama -> vLLM -> Elysium -> OpenAI -> Anthropic\n- Per-project persistent memory in `~/.aither/memory/<project>`\n- `adk index <path>` — standalone indexing with progress bar and stats\n\n### MCP Gateway\n- `POST /v1/embeddings` — OpenAI-compatible embedding proxy to vLLM-embeddings:8209\n- Available to all tiers (embeddings are free)\n- ADK EmbeddingProvider uses this as Elysium cloud fallback\n\n### Optional Dependencies\n- `pip install aither-adk[graphs]` — numpy for 10x cosine similarity speedup\n- `pip install aither-adk[embedding]` — sentence-transformers + torch for local GPU embeddings\n- `pip install aither-adk` alone — graphs work with feature hashing (zero deps)\n\n## [0.12.0] - 2026-04-01\n\n### Bootstrap & Service Discovery\n- Version handshake -- ADK checks major.minor compatibility with Genesis/Node\n- Background reconnect loop -- ServiceBridge re-probes every 30s in standalone mode\n- Port 8080/8090 documented -- MCP vs OpenAI-compat clearly separated\n- Genesis URL configurable via GENESIS_URL env var (was hardcoded)\n- Standalone mode warning -- visible stderr alert when AitherOS not detected\n- Auto-reconnect on startup when services come online\n\n### Elysium Cloud Inference\n- Unified gateway URL -- gateway.aitherium.com handles auth + billing + inference\n- Streaming inference -- SSE passthrough for /v1/chat/completions with stream=true\n- Auth proxy routes -- /v1/auth/register, /v1/auth/login, /v1/auth/me\n- Billing proxy -- /v1/billing/balance through gateway\n- AitherConnect Elysium fallback -- cloud inference when local Genesis is down\n- AitherDesktop Elysium fallback -- third-tier chat fallback after Node\n\n### Infrastructure\n- /discovery endpoint on Genesis -- unified service URLs/versions/health\n- /api/config/services on Veil -- runtime port config for client-side JS\n- Veil healthcheck gates on Genesis -- unhealthy when backend is down\n- Desktop crash detection 90s->60s (threshold 3->2)\n\n## [0.11.0] - 2026-04-01\n\n### Agent Execution Quality (Claude Code Parity)\n- Raise loop guard block threshold from 3 to 4 -- agents get more room for iterative search\n- Soft synthesis nudge for effort >= 4 (no tool stripping, trust the model)\n- max_output_tokens escalation -- retry up to 3x when response is truncated\n- Tool result pairing guarantee -- synthetic error for orphaned tool_use blocks\n- Micro-compaction of old tool results -- save context tokens on long sessions\n- First-turn tool forcing only for effort >= 6 (trust model for lower effort)\n- Diminishing returns detection -- nudge agent when 3+ turns produce < 500 tokens\n- Message normalization -- merge consecutive same-role messages, strip empties\n- LLM retry with exponential backoff (5 retries, 500ms-16s, jitter)\n\n## [0.9.0] - 2026-03-16\n\nThe \"connected world\" release. Cross-platform identity pairing, voice capabilities, and multi-channel integration.\n\n### Added\n- **Pairing**: Cross-platform identity linking (`adk/pairing.py`)\n - `PairingManager` — SQLite-backed identity linking with 6-char pairing codes\n - Link users across Telegram, Discord, Slack, WhatsApp with 10min TTL codes\n - Canonical session IDs for cross-channel conversation continuity\n - `get_session_id()` returns \"user-{id}\" for paired users\n- **Voice**: Speech-to-text and text-to-speech client (`adk/voice.py`)\n - `VoiceClient` — async STT/TTS/emotion via AitherVoice service\n - Convenience functions: `hear()`, `say()`, `feel()`\n - 6 voice options: alloy, echo, fable, nova, onyx, shimmer\n - Emotion detection with intensity scoring\n- New exports: `PairingManager`, `PairingResult`, `PlatformIdentity`, `VoiceClient`, `TranscriptionResult`, `SynthesisResult`, `EmotionResult`\n\n### Changed\n- `__init__.py` exports expanded with pairing and voice symbols\n\n## [0.6.0] - 2026-03-13\n\nThe \"group mind\" release. Multi-agent group chat, creative tools, and Iris identity.\n\n### Added\n- **Aeon**: Multi-agent group chat engine (`adk/aeon.py`)\n - `AeonSession` — persistent group chat with parallel agent execution\n - 7 presets: balanced, creative, technical, security, minimal, duo_code, research\n - Orchestrator synthesis: Aither summarizes all agent responses\n - Serial execution for Ollama, parallel for vLLM/cloud\n - ConversationStore persistence with `type: \"aeon\"` metadata\n - `group_chat()` one-shot convenience function\n- `aither aeon` CLI command — interactive terminal group chat with color-coded agents\n - `-p/--preset`, `-a/--agents`, `-r/--rounds`, `--no-synthesize` flags\n - `reset` and `quit` commands\n- Server endpoints: `POST /aeon/chat`, `GET /aeon/presets`, `GET /aeon/sessions/{id}`\n- Creative tools in builtin_tools: `image_generate`, `image_refine`, `image_search`, `video_generate`\n- Iris agent identity with visual generation capabilities\n- 48 Aeon tests (data models, presets, chat, context, persistence, server, exports)\n\n### Changed\n- `__init__.py` exports: `AeonSession`, `AeonResponse`, `AeonMessage`, `group_chat`, `AEON_PRESETS`\n\n## [0.5.0] - 2026-03-13\n\nThe \"tenant-ready\" release. Multi-tenant admin, permission grants, safety profiles, and a full setup wizard.\n\n### Added\n- `aither setup` interactive setup wizard with hardware detection, model selection, identity config\n- Strata storage backend: SQLite WAL persistence for conversations, memories, knowledge graphs\n- CLI test runner: `aither test` with auto-discovery and parallel execution\n- Permission grants system for MCP tool access control\n- MCP account management tools\n- LLM provider auto-detection for Ollama and vLLM endpoints\n- Apache-2.0 LICENSE file\n- Elysium desktop sync module\n- Comprehensive CLI test suite (493 tests)\n- Strata storage test suite (972 tests)\n- LLM provider tests (82 tests)\n\n### Changed\n- CLI expanded: `aither setup`, `aither test`, `aither bugreport`, `aither doctor`\n- Strata module rewritten as full local-first storage engine\n- Server startup includes Strata initialization\n- README rewritten with clearer quickstart and architecture docs\n\n### Fixed\n- Dead documentation links\n- Python 3.11 compatibility issues\n- Ruff per-file-ignores configuration\n- Identity provisioning edge cases\n- Docker node-gyp native dependency builds (python3 + build tools)\n\n## [0.4.0] - 2026-03-13\n\nThe \"own your AI\" release. Self-hosted agent OS for people who don't want their data on someone else's servers.\n\n### Added\n- Muse agent identity (creative/artistic generation)\n- Port 8120 to vLLM scan for ExoNodes discovery\n- Public roadmap with milestone-based porting schedule\n- Competitive positioning: self-hosted alternative to cloud-locked AI appliances\n\n### Changed\n- LLM router: compute-aware effort routing (replaces effort-level context gating)\n- README rewritten for sovereignty-first messaging\n- Package description updated\n- Promoted from alpha to stable release\n\n### Fixed\n- Version string consistency between pyproject.toml and __init__.py\n- vLLM port scanning now includes port 8120 (ExoNodes)\n\n## [0.3.1] - 2026-03-09\n\n### Added\n- GraphMemory: SQLite knowledge graph with Ollama embeddings and hybrid search\n- NeuronPool: auto-fire pre-LLM data gathering agents\n- NanoGPT: pure Python char-level transformer with LoRA fine-tuning\n- Safety gates: IntakeGuard (input), LoopGuard (recursion), Sandbox (code exec)\n- Event system: EventEmitter with chat/tool/forge event types\n- Builtin tools: identity-based tool selection\n- ServiceBridge: auto-discovery of AitherOS services\n- Streaming: chat_stream with safety gate integration\n- Auth middleware: Bearer token authentication\n- CLI: `aither init` and `aither serve` commands\n\n### Changed\n- Wired 5 previously disconnected modules into agent loop\n- 522 total passing tests (up from 85)\n\n## [0.3.0] - 2026-03-07\n\n### Added\n- Clean-room agent development kit\n- Multi-backend LLM providers (Ollama, OpenAI, Anthropic)\n- AitherAgent class with @tool decorator\n- SQLite conversation memory\n- OpenAI-compatible server (`aither-serve`)\n- 16 agent identities as package data\n- MCP bridge to mcp.aitherium.com\n- Privacy-centric opt-in telemetry\n- Bug reporting CLI and API\n- FastAPI server with OpenAI-compatible endpoints\n- Hardware auto-detection (5 tiers, 11 profiles)\n- Fleet orchestration and multi-agent coordination\n- A2A mesh protocol\n- Federation protocol for cross-instance agent dispatch\n\n### Initial release\n- 85 passing tests\n- Apache-2.0 license\n"}, "c0065934e7fc6fc5a049a20ead920442d9c9ddde": {"t": 1785748164.1469164, "n": 91594, "h": "e35d9579e7521173", "c": "# Changelog\n\n## [2.29.0]\n\n### Added — on-device WebGPU inference in the local GUI\n- New \"⚡ On-device (WebGPU)\" backend in the default console: runs a small model\n (Gemma-4-E2B) 100% in the browser on the user's GPU — no server, no API cost, private.\n- One-time ~900 MB model download with a consent gate + aggregated progress; cached after.\n- Streaming replies, multi-turn history, Stop, and a graceful WebGPU-absent fallback.\n\n\nAll notable changes to aither-adk will be documented in this file.\n\n## [2.27.0] - 2026-07-18\n\n### Added — Moonshot Kimi K3 provider (DeepSeek-equivalent cloud backend)\n\n- **New `moonshot` provider** across the LLM router: OpenAI-compatible\n `https://api.moonshot.ai/v1`, default model `kimi-k3` (131K context default, up to 1M;\n tool calls, JSON mode, structured output). Wired everywhere `deepseek` is:\n - `LLMRouter(provider=\"moonshot\")` + `_COMPAT_URLS`/`_COMPAT_MODELS`/model tiers\n (small/medium/large → `kimi-k3`)\n - Config: `moonshot_api_key` field (env `MOONSHOT_API_KEY`), saved-config load,\n provider-keys map (`provider_keys.json` key `moonshot`)\n - Auto-detect chain: tried after DeepSeek (config key, then env key)\n - Hybrid reasoning backend: `AITHER_REASONING_BACKEND=moonshot` for effort 7+\n - Reasoning tiers: `backend: moonshot` (alias `kimi`) via new `MoonshotBackend`\n - CLI: `adk backend set moonshot --api-key …`, `adk backend list` shows moonshot\n- No breaking changes; DeepSeek and all other providers unchanged.\n\n## [2.26.0] - 2026-07-16\n\n### Added — Aeon group-chat web UI pack + POST /aeon/stream SSE endpoint\n\n- **New `aeon` UI pack** (`adk/webui/packs/aeon/index.html`) — group chat for multi-agent\n discussion. Mirrors the `llamacpp` pack's auth + SSE pattern (bearer from URL fragment #k=),\n with a preset selector (balanced/creative/technical/security/minimal/duo_code/research) and\n distinct colored agent messages. Synthesis block highlighted. Agent content is HTML-escaped\n before render. Fully self-contained HTML+CSS+JS, light+dark, no external assets. (No\n temperature/max-tokens controls — Aeon runs a fixed multi-agent round and doesn't thread\n per-turn generation params, so exposing them would be a no-op.)\n- **POST /aeon/stream SSE endpoint** — accepts body {message, preset?, agents?, rounds?,\n session_id?}, emits SSE with: `session_start` (participants + orchestrator), `agent_message`\n per non-orchestrator, `synthesis` (if enabled), `error` on exception, `complete` (total tokens\n + latency). Mirrors /chat/stream structure — like every streaming endpoint here it is gated by\n the server auth middleware **when `AITHER_SERVER_API_KEY` is set** (a local `adk up` with no key\n is unauthenticated by design, same as /chat/stream).\n- **Default UI pack flipped to `llamacpp`** — `resolve_ui_pack_name()` now returns `llamacpp`\n instead of `console` when no env/config is set (new users see the clean chat, not the admin\n SPA; existing users with AITHER_AGENT_UI or saved config unaffected).\n\n### Added — swappable agent web UI packs (llama.cpp-style built-in chat)\n\n- The page an agent serves at `/` is now a **swappable UI pack**, so you can\n drop in, test, and deploy different chat frontends without touching the agent.\n Select with `adk ui set <name>` (persists to `~/.aither/config.json`) or the\n `$AITHER_AGENT_UI` env; `adk ui ls` lists packs, `adk ui path` shows what\n resolves.\n- **New built-in `llamacpp` pack** — a clean, self-contained llama.cpp-style\n chat UI (centered conversation, streaming token-by-token with a cursor, a\n Stop button, a settings drawer for temperature / max-tokens that is actually\n honored end-to-end — `/chat/stream` now forwards them to the provider —\n minimal safe markdown, light+dark). Hits the agent's own gated `/chat/stream`\n with the bearer read from the URL fragment (never sent in the URL). Built-ins\n also include `console` (the full admin SPA, still the default) and `minimal`.\n- **Drop-in custom packs**: a UI pack is just a folder with an `index.html` in\n `~/.aither/ui-packs/<name>/` (override via `$AITHER_UI_PACKS_DIR`) — no rebuild.\n- Resolution is **fail-soft**: an unknown/missing pack falls back\n console → minimal, so `/` is never blank.\n\n### Added — MCP-gateway-first platform access + mesh A2A trust\n\n- **Gateway-first platform access** (`adk/client/_gateway_mcp.py`, `GatewayMCPClient`):\n a self-hosted agent connects its MCP **client** to `mcp.aitherium.com` authenticated\n as the OWNER (device-flow / PAT / ACTA token), RBAC-scoped to that identity — so it can\n use platform tools + `get_secret` **whether or not the node runs local AitherOS\n services**. Fail-closed: no token → client disabled, the agent still runs. Wired into\n the server lifespan (`_connect_gateway_mcp`).\n- **`genesis` LLM backend**: `adk backend set genesis` points the agent brain at a local\n Genesis (`http://localhost:8001/v1`, model `workflow`) — the 5090/controller pattern\n where inference is served by the local fleet, not the cloud gateway or a raw vLLM.\n- **`adk mesh` command**: `adk mesh onboard` drives the Conductor 5-step onboard\n (`/v1/mesh/onboard` → registers this node's public key, returns the `endpoint:mesh`\n trust token); `adk mesh ls` lists peer nodes/agents (AitherMesh `/nodes` + Directory)\n with their A2A cards + invoke URLs for discovery.\n\n### Security — A2A inter-agent trust enforcement (fail-closed)\n\n- **`adk/a2a_trust.py`** (new): Ed25519 signature verification + trusted-key check\n (`AITHER_A2A_TRUSTED_KEYS`), opt-in via `AITHER_A2A_REQUIRE_TRUST` (default `false` for\n backward compatibility; `audit` logs untrusted, `true` rejects).\n- **Fixed a fail-OPEN in the inbound A2A handler** (`adk/a2a.py`): the trust check\n previously ran *only when signature headers were present*, so an unsigned request\n bypassed `AITHER_A2A_REQUIRE_TRUST` entirely. Now genuinely fail-closed — required mode\n with no/invalid signature → `403`.\n- **Sovereign template no longer scaffolds `verify=False`**: the generated\n `services/llm.py` now uses the project's own `tls_verify()` policy helper for every\n provider call instead of hardcoding disabled TLS verification.\n- **`adk/_tls.py`**: disabling verification via `AITHER_TLS_VERIFY=false` now emits a\n one-time loud warning (was silent) — the escape hatch stays, the silence goes.\n\n## [2.25.1] - 2026-07-15\n\n### Fixed — self-hosted OpenAI-compatible backends honor a custom base URL\n\n- `adk backend set llamacpp --base-url http://localhost:8090/v1 --model <id>` (and\n `vllm`/`lmstudio`, and the `AITHER_LLM_BASE_URL` env) now actually point the agent\n at YOUR server. Previously the base URL was saved but never read, so an explicit\n `--backend llamacpp` fell back to the provider's public API default (openai.com).\n New `Config.llm_base_url` field + saved-config `inference_url` wiring; the explicit\n backend path in `LLMRouter` now passes it for the local OpenAI-compatible family.\n This is what lets a self-hosted node run its OWN local model (e.g. a PrismML\n Bonsai llama.cpp) as the agent brain instead of ollama/cloud.\n\n## [2.25.0] - 2026-07-15\n\n### Added — node_bootstrap tool pack: bootstrap LLM inference on any hardware\n\n- **New bundled tool pack `node-bootstrap`** (`adk/toolpacks/node_bootstrap/`):\n seven agent tools — `node_detect_hardware`, `node_resolve_recipe`,\n `node_plan_deployment`, `node_apply`, `node_enroll`, `node_register_backend`,\n `node_verify` — plus a CLI shim (`python -m adk.toolpacks.node_bootstrap\n detect|resolve|plan|apply|enroll|register|verify`). Detect a box's hardware,\n resolve a deployment recipe, actually deploy the inference engine (docker\n compose / native / delegate), enroll with a control plane, register the\n backend, and live-verify a completion.\n- **9 hardware recipes** shipped in the wheel: `cpu-1bit-llamacpp`, `cpu-ollama`,\n `cuda-vllm-8gb/24gb/40gb`, `cuda-dual-stack-32gb` (32GB-card co-resident\n stack), `unified-memory-vllm` (DGX/Grace-class), `metal-ollama` (native —\n Docker on macOS has no Metal passthrough), `cloud-api` fallback. Resolution\n scores hardware against min/max VRAM bands, RAM, cores and unified memory;\n ties prefer higher tiers and self-contained recipes over fleet delegates.\n- Security posture: enrollment and backend registration are fail-closed\n (missing token/URL → error dict, never anonymous, never `verify=False`);\n tokens are redacted in outputs; all endpoints come from args/env\n (`AITHER_CONTROL_PLANE_URL`, `AITHER_GENESIS_URL`, `AITHER_AUTH_TOKEN`).\n\n## [2.24.0] - 2026-07-14\n\n### Changed — sync-family consolidation\n\n- **Removed dead code:** `adk/sync.py` (528 lines) deleted — it was unreachable,\n shadowed by the `adk/sync/` package (`import adk.sync` always resolved to the\n package; nothing loaded the module by path — verified).\n- **Grouped the sync domain modules into the `adk.sync` package:**\n `brain_sync`→`adk.sync.brain`, `files_sync`→`adk.sync.files`,\n `lockbox_sync`→`adk.sync.lockbox`, `secrets_sync`→`adk.sync.secrets`,\n `session_sync`→`adk.sync.sessions`, `settings_sync`→`adk.sync.settings`,\n `sync_watermark`→`adk.sync.watermark`. All in-tree importers updated. No\n back-compat shims were left because **zero external/monorepo code imported the\n old root paths** (verified) — a clean move, not a shimmed one. **Breaking only\n for code that imported `adk.<name>_sync` directly** (none known).\n\n### Docs — MCP module layering clarified\n\n- `adk.mcp` (enterprise AitherOS gateway client: auth/billing/token-tracking) and\n `adk.core.mcp` (minimal generic JSON-RPC adapter for any MCP server) are\n DISTINCT layers, not duplicates — each now carries a LAYER/ROLE/See-also\n docstring so they are not mistakenly consolidated.\n\n## [2.23.2] - 2026-07-14\n\n### Fixed — CompletionGate no longer false-fails on correct results\n\n- **Tolerant judge JSON extraction.** The LLM judge parsed its reply with `json.loads(text[first\"{\":last\"}\"])`, which raised `JSONDecodeError` (\"Extra data\") whenever the model appended prose after the verdict or emitted a second object — and `verify()` turned that into a fail-closed verdict, so a genuinely completed task reported `unverified`. New `_first_json_object()` uses `JSONDecoder.raw_decode` from the first `{` that begins a valid object and ignores trailing content (prose, markdown fences, extra objects). A judge that answers with no parseable JSON still fails closed (never a soft pass), now with a clear reason. The same extractor backs `_derive_criteria`.\n- **Bare code-like tokens are hard-checked.** A natural criterion like `output contains ADK_AUTO_OK` (no quotes) previously fell through to the judge; `hard_checks` now also matches bare identifier/code shapes (contains an underscore, or 4+ CAPS/digit chars), with a stopword guard so a descriptive uppercase word (`OUTPUT`, `CONTAIN`) never becomes a false required token. Quote a token to force it regardless.\n- +9 tests (24 total), including the exact trailing-data regression.\n\n## [2.23.0] - 2026-07-14\n\n### Removed — dead modules and internal integrations\n\n- **`adk.swarm`, `adk.provisioning_tools`, `adk.session_sync_integration`, `adk.addon_metering`** — four orphan modules with zero public usage have been deleted. These were internal prototypes kept for legacy monorepo consumers; public packages have not imported them since 2.21.0 (tracked as D-348). **Breaking for anyone directly importing these modules** (import will fail). They are no longer exempt from the orphan-module check.\n- **`adk.aither_bridge`** — the AitherOS-internal IRC ↔ chat gateway bridge is no longer shipped in the public aither-adk package (stripped at sync time). All public importers already degrade behind `except ImportError` (in `adk.server` and `adk.builtin_tools`), so public packages remain unaffected. The module stays in the private AitherOS monorepo for internal use.\n- **Identity document trim** — removed internal agent names from `adk/identities/aither.yaml` delegation guidance, replacing with role-generic wording (\"delegate code review, refactoring, security analysis, performance and testing to specialist agents when available\"). Effort-tier routing philosophy remains unchanged and is product design, not a leak.\n\n### Changed — genericized customer/product names in help text\n\n- CLI help/examples no longer name specific customer deployments (`--scope` template list and `deploy agent` name example use generic placeholders). Functional deploy paths are unchanged.\n\n## [2.22.1] - 2026-07-14\n\n### Fixed — real bugs surfaced by reviving the public CI (red since Jul 5)\n\n- **TLS**: bundled toolpacks (arc-brainpack, cloudflare) disabled certificate\n verification on gateway/vault calls; all now route `adk._tls.tls_verify()`\n (verified by default, internal CA bundle when present).\n- **`system_prompt` contract**: bundled-pack `[PACK DIRECTIVES]` were appended\n even to an explicit `system_prompt`; an explicit prompt now wins wholesale.\n- **Headless secrets**: `_derive_key()` used `os.getlogin()`, which raises\n `OSError` without a controlling terminal (CI, daemons, containers) — falls\n back to `getpass.getuser()` (same value on interactive boxes, so existing\n secret files keep decrypting).\n- **`pack_verifier`**: missing `cryptography` crashed with `UnboundLocalError`\n in the exception handler instead of failing closed; now degrades gracefully\n (and `cryptography` was added to the dev extra so CI tests the real path).\n- **`fleet_manager._kill_pid` (POSIX)**: reported success immediately after an\n asynchronous SIGTERM and never reaped children (zombies read as alive); now\n polls, escalates to SIGKILL, and reaps.\n- **Identity**: `aither` regained the `creative` tool category (dropped\n accidentally in a 2.7.0 WIP sweep).\n\n## [2.22.0] - 2026-07-14\n\n### Changed — internal toolkit relocation\n\n- **`adk.platform` relocated** — the internal aither-platform toolkit (ComfyUI/image-gen\n pipelines, fleet agent clients, parallel CLI/UI/tools stack) has been moved out of the\n adk SDK tree to the standalone internal `aither-platform` package at\n `AitherOS/agents/aither_platform/`. The public package remains unaffected (it already\n excluded `adk.platform` since 2.21.1). Internal AitherOS agent builds now install\n `aither-platform` separately. `adk platform` falls through to the same \"not available\"\n message on public installs; internal builds work via the relocated package.\n\n### Removed — backward-compat re-export deleted\n\n- **`adk.gateway` module removed** — the backward-compatibility re-export of `GatewayClient`\n from `adk.gateway` has been removed. Use `from adk.client import GatewayClient` instead\n (the established public API since 2.21.0). All internal importers have been updated.\n\n## [2.21.1] - 2026-07-14\n\n### Removed — internal toolkit and license-gated product no longer ship publicly\n\n- **`adk.platform`** — the internal aither-platform toolkit (ComfyUI/image-gen\n pipelines, fleet agent clients, a parallel cli/ui/tools stack) is no longer\n included in the public repo or the PyPI wheel. It was internal-only code that\n had been riding along since the aither-platform merge. `adk platform` now\n reports \"Platform toolkit not available\" on public installs; internal AitherOS\n builds are unaffected.\n- **`adk.formbridge`** — the FormBridge form-automation product is license-gated\n (PROFESSIONAL+); its implementation no longer ships in the free package. All\n import sites (`builtin_tools`, `server` route mount) already degrade behind\n `except ImportError`, so nothing else changes behavior.\n- Product test suites (`test_formbridge.py`, `test_encounter_keying.py`,\n `test_flow_tools.py`) are excluded from the public sync alongside them.\n\n## [2.21.0] - 2026-07-14\n\n### Added — Completion gate: verified-or-retried task execution\n\n- **`adk.gate.CompletionGate`** — a composable wrapper that closes the \"silent\n success\" gap: an agent's ReAct loop returns whatever the model produced and\n calls it done (`finish_reason = stop/max_steps/length`), which is a *liveness*\n signal, not a *completion* one — so an agent can \"succeed\" while doing nothing.\n `CompletionGate` runs the agent, verifies the result against acceptance\n criteria, retries with the failure fed back, and returns the response with\n `finish_reason` set to `verified` or an honest `unverified`. Never a false pass.\n - **Hard-first, un-soft-passable checks** (`adk.gate.hard_checks`): a filesystem\n path a criterion says must exist is checked on disk; a required quoted token\n must appear in the output. Only genuinely subjective criteria fall through to\n an optional LLM judge (reuses the agent's own model), which **fails closed**\n if unavailable.\n - **`gated_run(agent, task, criteria=..., max_retries=2)`** — one-shot helper.\n - `auto_criteria` (LLM writes the machine-checkable definition-of-done from the\n task), a `verifier=` override for custom checks (tests-pass / HTTP probe), and\n a `requires_action` short-circuit so it never fights human-approval pauses.\n - Compose it onto any agent exposing `run(task) -> resp{.content, .finish_reason}`\n (`AitherAgent`, `core.Agent`, or a thin adapter over a remote dispatch).\n - Exported from the top-level package: `from adk import CompletionGate, gated_run`.\n\n## [2.19.0] - 2026-07-12\n\n### Added — Eve agent importer + durable A2A tasks + canonical agent discovery\n\n- **`adk pack import <eve_agent_path>`** — convert external Eve agents to AitherADK packs\n (from GitHub or local filesystem). Maps Eve structure (instructions → system prompt,\n agent.ts → agent.yaml, skills/*.md → skills, connections/*.ts → MCP, tools/*.ts →\n tools/node verbatim) to AitherADK format with a single command. Eve agents (compiled\n manifest format, schema v35) become installable packs compatible with `adk pack install`.\n- **Durable A2A task lifecycle (`TaskManager` backed by FileStore)** — agent-to-agent\n tasks now survive container restarts. Tasks persist as append-only JSONL\n (`.adk/tasks.jsonl`); in-memory dict for fast access; `create_task()` / `update_status()` /\n `add_artifact()` / `add_message()` all save to disk. Google A2A v0.3.0 spec compliant.\n- **Canonical agent card discovery** — `A2AServer.mount()` now exposes:\n - `GET /.well-known/agent-card.json` — Agent Card (canonical; contains agent identity,\n capabilities, skills, endpoints)\n - `GET /.well-known/agent.json` — Agent Card (legacy, redirects with 308 to canonical path)\n - `POST /a2a` — JSON-RPC 2.0 for message send/task lifecycle\n - `GET /a2a/tasks/{id}/subscribe` — SSE streaming task updates\n- **Four built-in agent packs now complete and catalogued**:\n - `hermes` — architecture review & trade-off analysis\n - `openclaw` — web research & source verification\n - `claude-code` — feature development & debugging\n - `analyst` — structured data inference & anomaly detection\n All discoverable via `adk packs` / `adk install pack:<name>`.\n- **Tolerant tool-call JSON parsing** — malformed tool JSON (extra properties, missing\n name/arguments, string-encoded nested args, missing keys) is now gracefully degraded\n instead of silently failing. `_parse_tool_json()` accepts both `\"arguments\"` and\n `\"parameters\"` keys, coerces string-encoded args, and skips unparseable calls instead\n of crashing the turn.\n\n### Known limitations\n\n- Eve agent importer: remote GitHub fetching not yet implemented (local paths only).\n- Durable task store: currently JSONL (suitable for thousands of records, not millions).\n\n## [2.18.0] - 2026-07-10\n\n### Added — AitherShell command center + Claude Code session management\n\n- **`aither sessions`** — Claude Code session manager: interactive full-screen\n browser (type-to-filter, transcript preview, deep full-text `search`, resume\n **in the current terminal** or as Windows Terminal tabs), crash guard\n (`guard install` = at-logon watchdog that snapshots live sessions and\n auto-restores the whole set after a terminal crash), and `ingest [--watch]`\n (incremental, secret-guarded sync of session conversations into the local KB\n / CompanyBrain via the standard ingest pipeline).\n- **`aither hq`** — command-center dashboard: per-service fleet health, LLM\n queue (depth/VRAM/models), Pulse alerts, live session count, inbox unread —\n auto-refreshing, with one-key jumps into chat, sessions, inbox, agents,\n brief, watchtower, and docker recovery.\n- **`aither inbox`** — unified queue over CommCore mail, Relay\n mentions/notifications, and Pulse alerts; open/mark-read/DM-reply from the\n terminal. Every source degrades independently.\n- **`aither agents`** — roster console + `agents ask -e <1-10> <agent> <q>`\n (effort-tiered ask via Genesis, with automatic `/chat/stream` fallback when\n the `/agent/sync` execute gate rejects host callers) + forge dispatch\n inspection + routine health.\n- **`aither palette`** — universal fuzzy picker across actions, sessions, and\n services; **`aither brief`** — renders Atlas's executive briefing in the\n terminal; **`aither watch`** — fleet watchtower with named wedge-signature\n detection (docker-WSL wedge, LLM-queue stall, crash-loop uptime regression)\n and optional docker auto-recovery.\n- All fleet reads go through a shared fail-soft `FleetClient` (internal-CA\n TLS via `adk._tls`, dashboard-grade timeouts, per-source degraded states).\n- Node shell-cli (`@aitherium/shell-cli` 1.13.0): `aither\n sessions|hq|inbox|palette|brief|watch|agents|docker` now pass through to the\n Python shell instead of being intent-classified as chat prompts.\n\n## [2.17.1] - 2026-07-05\n\n### Fully self-service mesh join — no customer key handling\n\n- **Auto-join headscale off the onboard response.** The conductor now auto-issues a\n headscale pre-auth key for NAT'd nodes and returns it in the `/v1/mesh/onboard`\n response; `mesh.join` consumes it and brings up the tunnel automatically. A customer\n runs one command — they never mint, fetch, or set a headscale key. Explicit\n `--headscale-key`/`AITHER_HEADSCALE_AUTH_KEY` still take precedence.\n- **Security:** `_tailscale_up` now redacts the auth key from all logs and exceptions\n (a failed `tailscale up` previously echoed the full argv, leaking the key into logs).\n\n## [2.17.0] - 2026-07-05\n\n### Added — Headscale mesh transport + local Qdrant + AitherConnect onboarding\n\n- **Headscale NAT-friendly mesh transport.** When raw WireGuard UDP:51820 is not\n viable (customer boxes behind NAT, corporate firewalls, CGNAT), `adk mesh join\n --headscale` routes the mesh tunnel through Tailscale's Headscale control plane.\n The overlay IP is still Conductor-assigned (10.77.0.0/16); Headscale provides\n only the transport layer. Configured via `AITHER_MESH_TRANSPORT=headscale`,\n `AITHER_HEADSCALE_URL` (default: https://headscale.aitherium.com), and\n `AITHER_HEADSCALE_AUTH_KEY` (pre-generated key). Automatic fallback to raw\n WireGuard if Headscale setup fails.\n- **Local Qdrant vector DB provisioning.** `adk stack` now provisions Qdrant\n locally when `AITHER_VECTOR_DB=qdrant` is set, enabling offline-first RAG\n without external dependencies. Tiered: local Qdrant in-fleet, cloud Nexus\n for enterprise multi-tenant deployments.\n- **Conductor URL public fallback.** `adk mesh join` resolves the internal\n `aitheros-conductor:8193` hostname; if unreachable (e.g., self-hosted nodes),\n it falls back to the public `conductor.aitherium.com:8193` endpoint,\n eliminating bootstrap configuration friction.\n\n### Documentation\n\n- **Self-hosting runbook.** New `docs/SELF_HOSTING.md` covers deploying AitherOS\n on customer infrastructure with Qdrant + mesh + AitherConnect onboarding.\n- **Mesh transport constraints documented.** Raw WireGuard requires public\n UDP:51820 endpoint (cloud instances, dedicated hardware); Headscale for\n NAT'd networks. Noted in CLI help and module docstrings.\n\n## [2.15.0] - 2026-07-04\n\n### Added — built-in web admin console + portal-profile settings sync\n\n- **Web admin console.** The page `adk up` opens (served at `/` and `/ui`) is now a full,\n self-contained console — Chat plus six admin tabs: **Backend** (view/switch LLM provider,\n set keys, test connection), **Packs** (list/enable/disable/reload tool packs),\n **Sessions** (browse/delete conversations), **Logs** (redacted agent-log tail),\n **Graph** (knowledge-graph search/neighborhood/stats), and **MCP** (add/remove external\n MCP servers). No build step, no CDN — a single packaged HTML asset. The original minimal\n streaming chat page stays at `/chat` for back-compat.\n- **Admin API (`adk/admin_api.py`).** ~20 endpoints under `/admin/*`, all bearer-gated by the\n existing middleware (never in `_skip_auth_paths`). Operates on the live agent — LLM backend\n swap (`LLMRouter.switch_backend`), pack reload, and MCP registration take effect without a\n restart.\n- **Settings sync (`adk/settings_sync.py`).** The user's portal profile is the source of\n truth: on startup the agent pulls `preferences.adk` and applies it over the local\n `~/.aither/config.yaml` cache; admin-console edits push a debounced snapshot back to\n `PUT /api/settings/preferences`. Provider API keys and MCP auth headers are device-local\n secrets and are never included in the pushed snapshot. Opt out with `AITHER_SETTINGS_SYNC=false`.\n\n### Security\n\n- **Timing-safe token comparison.** The server bearer check now uses `hmac.compare_digest`\n (the token is reachable over the public tunnel; a byte-wise `!=` leaked it to timing attacks).\n- **MCP add is SSRF-guarded + confirmed.** Adding an external MCP server is a two-step\n prepare→confirm flow; URLs resolving to loopback / private / link-local / metadata addresses\n are rejected (override with `AITHER_ALLOW_PRIVATE_MCP=1` for trusted local servers).\n- **Config editor is allowlisted.** `PATCH /admin/config` writes only safe scalar prefs and\n hard-denies fields that could execute code or redirect traffic at startup (e.g.\n `aither_toolpack_dirs`, backend base URLs).\n- **Log tail is scrubbed.** Bearer tokens, `#k=` fragments, and `sk-`/`aither_sk_` keys are\n redacted from the log-tail endpoint; the tunnel log is excluded.\n\n## [2.14.8] - 2026-07-04\n\n### Added — `adk up` for normal humans (no API key, no prereqs, live chat)\n\n- **Hosted-brain default — no API key required.** When there's no local model and no\n provider key, `adk up` signs in (device-flow) and routes inference through the gateway\n using the portal token (`AITHER_LLM_BACKEND=gateway` + bearer). A non-technical user\n never needs to obtain or paste an LLM API key. Explicit `--provider`/local model still\n take precedence; unattended `--yes` with no token/backend fails cleanly (exit 3).\n- **cloudflared auto-download.** If the tunnel binary is missing, `adk up` downloads the\n right platform asset to `~/.aither/bin` (SHA256-verified against the release; opt out with\n `AITHER_CLOUDFLARED_REQUIRE_CHECKSUM=0`) instead of exiting with an install hint.\n- **Built-in streaming chat page.** The agent now serves a self-contained chat UI at `/`\n (and `/chat`) with a live \"thinking…\" indicator and token-by-token streaming — so a human\n has somewhere to talk to it with immediate feedback instead of a blank wait. `adk up` opens\n it locally and passes the callback bearer in the URL fragment (`#k=…`), so `/chat/stream`\n stays authenticated (not an open proxy) with nothing to paste.\n- **Human-readable failures.** `adk up` errors now carry a plain-English `next_action`\n (kept machine-readable in `--yes` JSON).\n\n### Fixed\n\n- `adk up` chat link pointed at a non-existent `/settings/agent` route; it now points at the\n agent's own streaming chat page.\n\n## [2.14.7] - 2026-07-04\n\n### Added\n\n- **`adk up` — one command to run a persistent, fleet-connected agent.** Collapses\n the scattered onboarding surface (`host`/`quickstart`/`connect`/`login`/`setup`)\n into a single command that starts `aither-serve` (single agent, identity `aither`\n by default), opens a Cloudflare quick-tunnel for fleet reachability, registers with\n the portal, and installs a reboot-persistent autostart entry (Windows Task Scheduler\n / systemd-user / launchd). Detaches by default so the terminal is freed; `--foreground`\n blocks. Reuses the existing device-flow login, cloudflared discovery, provider-key\n store, and `_preflight_check` backend detection.\n- **Fully non-interactive path for autonomous agents.** `adk up --yes` (or any\n non-TTY invocation) takes ZERO prompts — everything resolves from flags, env, and\n saved config — and emits a single machine-readable JSON status line with meaningful\n exit codes (`0` ok, `2` bad-input, `3` no-backend, `4` tunnel-missing, `5` register-failed).\n Degrades to local-only (warn, not fail) when a portal token is absent unless\n `--require-register` is set.\n- **`adk down` / `adk status --json` companions.** `down` stops the agent + tunnel,\n best-effort deregisters, and removes the autostart entry; `status` reports the running\n agent (liveness + `/health` + tunnel + registration), with `--json` for agents/CI.\n- **`adk stack`** — the previous `adk up` behaviour (supervise the Room + Ollama consumer\n stack) now lives here; `adk up` is the connected-agent command.\n\n### Internal\n\n- New `adk/agent_daemon.py`: detached process spawn, status file\n (`~/.aither/adk-up.json`), pid liveness/teardown, and cross-platform autostart install.\n\n## [2.14.6] - 2026-07-04\n\n### Added\n\n- **`adk.preflight` — self-bootstrap capability probe.** `run_preflight(agent,\n spec)` wakes an agent up knowing what it is actually plugged into: a bounded\n (1.5s) parallel `LivenessProbe` over the router's resolved backends\n (primary/reasoning), the embeddings winner, remote MCP tools, the structured-ML\n / `/ml/teach` endpoints, and voice — returning an honest `CapabilityReport`.\n Status vocabulary distinguishes reachability from entitlement\n (`OK`/`MISSING`/`UNREACHABLE`/`AUTH`/`TIER_DENIED`/`UNSUPPORTED`), a hosted slot\n never prints a bare `OK`, and a required-but-unsatisfied slot aborts before the\n loop instead of blind-firing. Headless task/rules resolution + an allowed-roots\n policy binding (`builtin_tools.set_allowed_roots`, which resets the memoized\n cache so the override actually enforces).\n- **Multimodal (image) messages.** `Message.content` now accepts an OpenAI-style\n content-part list (`str | list[dict]`), passed through verbatim to the\n OpenAI-compatible body (gateway / vLLM / OpenAI). New `adk.llm.multimodal`\n helpers — `image_message()`, `image_content_parts()`, `to_image_url()` — build\n the parts and encode local image bytes/paths into `data:` URIs.\n- **Real vision preflight probe.** The vision slot is no longer unconditionally\n `UNSUPPORTED`: it sends a discriminable image (a solid red square) and requires\n the model to name the color, so a blind/text-only model cannot false-pass. `OK`\n only on a real round-trip; `AUTH` on 401/403; `UNSUPPORTED` when there is no\n OpenAI-compatible provider or the model cannot see the image.\n\n## [2.14.5] - 2026-07-04\n\n### Added\n\n- **`adk.reasoning.mcts` — generic Monte-Carlo Tree Search library.** A reusable\n MCTS engine (ported from the internal `UnifiedMCTS`, with all internal couplings\n removed) that any agent can drive over its own environment:\n - `MCTSEnvironment` protocol (`get_state_hash` / `get_actions` / `step` /\n `evaluate` / `clone`) — implement it and search.\n - Three **optional, default-off** model seams — `TransitionModel`, `PolicyModel`\n (PUCT priors), `ValueModel` — so a learned world-model / policy / value can be\n injected without touching the core; with all three `None` the engine is a plain\n UCT search.\n - `ObservedTransitionModel` adapter — a cheap online lookup world-model (hit =\n exact stored transition; miss = identity + negative \"unknown\" reward +\n `is_uncertain`, biasing exploration toward learning), with JSONL save/load for\n cross-run reuse.\n - `UnifiedMCTS(config).search(env)` returns an `MCTSResult` (best action, value,\n confidence, principal-variation path) and an optional `MCTSTrace`\n (visit-distribution policy target) for distillation.\n- **Self-bootstrapping agent machinery.** Build an agent from a declarative spec\n instead of bespoke wiring:\n - `adk.bootstrap.build_agent_from_spec(path) -> (AitherAgent, RunCtx)` — resolves\n prompts, activates required/optional tool packs, and carries a `memory_map`\n (a `node_type -> (role, tier)` override for memory writes, never a gate).\n - `adk.prompts.PromptBridge` — resolves an agent's prompt bindings from files\n (relative to the spec) or `AitherPrompts` dot-keys (optional import).\n - `adk.packs.PackActivator` — eager activation of installed tool packs by name\n over `register_tool_packs`; a required pack that registers zero tools raises\n `PackUnavailable`, optional packs soft-degrade.\n - `python -m adk.bootstrap.generic_agent <spec.yaml>` inspects a spec (resolved\n name, activated packs + tool counts, prompt keys, memory map) without running a\n live loop.\n- **`deep_research` tool pack** — a full multi-engine web-research workflow as\n `dr_*` agent tools (keyless search, page fetch + cache, site mirror, a citable\n findings knowledge graph, and markdown/PDF/DOCX report generation), activatable\n by name via the tool-pack loader.\n\n## [2.14.4] - 2026-07-04\n\n### Added\n\n- **`structured_ml` tool domain** — zero-shot structured-data inference for agents,\n backed by TabFM (tabular classification/regression) and TimesFM (time-series\n forecasting). Three tools:\n - `tabular_classify(support_rows, target, query_rows)` — classify rows from a\n labeled support set, in-context (no training step; up to 10 classes).\n - `tabular_regress(support_rows, target, query_rows)` — predict a numeric target.\n - `timeseries_forecast(series, horizon)` — forecast future values of a series.\n\n The tools POST to a structured-ML inference service resolved from\n `AITHER_STRUCTURED_ML_URL` (default `http://localhost:8192`), reject non-http(s)\n URLs, and summarise oversized responses to protect the agent's context.\n- **`Capability.STRUCTURED_INFERENCE`** capability token and a new **`analyst`**\n identity/pack that ships the `structured_ml` domain by default. The domain is\n opt-in — only identities that enable the `structured_ml` category get the tools.\n\n## [2.14.3] - 2026-07-03\n\n### Fixed\n\nAll four bugs below were found by actually booting `adk run` against a real\nchat model and driving a real tool-calling request end-to-end (not inferred\nfrom reading the code) — each one silently broke the \"local agent uses its\nconfigured model and its tools actually work\" path.\n\n- **`AITHER_MODEL` / `config.model` silently ignored by the OpenAI-compatible\n backend**: `LLMRouter.__init__` never read `config.model` into `self._model`\n unless the caller passed `model=` directly to the constructor — which nothing\n did. Every explicit model choice (env var, config file) was dropped, the\n provider fell back to a hardcoded per-backend default (`gpt-4o-mini` for the\n `openai` backend), and an unrecognized model name got silently remapped\n server-side to the wrong backing model. Concretely: setting\n `AITHER_MODEL=aither-orchestrator` had no effect — every request actually\n went to a different model that was never meant to be called directly and\n can't reliably use tools.\n- **`OPENAI_BASE_URL` ignored for explicit `--backend`/`AITHER_LLM_BACKEND`\n selection**: `LLMRouter._auto_detect()`'s explicit-backend branch hardcoded\n `base_url=None` when constructing the provider, instead of reading\n `config.openai_base_url` (which two other call sites in the same file\n already did correctly). Any self-hosted OpenAI-compatible endpoint\n (MicroScheduler, vLLM, LM Studio) configured via `OPENAI_BASE_URL` was\n silently ignored in favor of `https://api.openai.com/v1`.\n- **Tool-call steering never engaged on default-effort chat**: the retry that\n nudges a model back into structured tool-calling when it responds with prose\n instead of a function call was gated behind `effort >= 6`. Default `effort`\n for a plain `agent.chat(message)` call resolves to `5`, so the safety net —\n built specifically for this failure mode — never fired for default requests.\n Also now forces `tool_choice=\"required\"` on the retry instead of relying on a\n text nudge alone.\n- **Every non-string builtin-tool parameter silently typed as `\"string\"` in\n the generated tool schema**: `builtin_tools.py` uses\n `from __future__ import annotations`, so `_extract_parameters()` in\n `tools.py` was reading postponed (string) type hints (`\"int\"`, not `int`)\n straight out of `fn.__annotations__`, which never matched\n `_type_to_schema`'s type-object keys and silently fell through to\n `{\"type\": \"string\"}`. Every builtin tool with an `int`/`float`/`bool`/`list`\n parameter was affected — e.g. `file_read(start_line: int, end_line: int)`\n advertised both as strings, so a model correctly calling the tool with\n `\"start_line\": \"0\"` crashed the actual implementation with\n `unsupported operand type(s) for -: 'str' and 'int'`. Fixed by resolving\n hints through `typing.get_type_hints()` instead of the raw annotations dict.\n `_coerce_arguments()` — the runtime safety net that's supposed to coerce a\n model-supplied `\"0\"` string into `0` before a tool call executes — had the\n exact same raw-`__annotations__` bug and silently no-opped for every builtin\n tool; fixed the same way.\n- **Streaming tool-loop could hang a connection forever**: `chat_stream()`'s\n fallback to the sync tool loop (tool calls can't stream token-by-token) had\n no timeout, so a model that didn't cleanly terminate a tool-calling round\n trip left the SSE connection open indefinitely with zero bytes sent. Bounded\n with a 60s `asyncio.wait_for`; on timeout the stream yields a clear message\n instead of hanging.\n\n## [2.14.2] - 2026-07-03\n\n### Fixed\n- **AitherShell session amnesia**: the REPL generated a fresh `session_id` for\n EVERY message, so conversation history never followed across turns (the\n server stored each turn under a different session). One stable id is now\n minted per shell run; `/resume` and `/new` semantics unchanged, and the\n cross-restart auto-restore of the last session now actually works.\n\n## [2.14.1] - 2026-07-02\n\n### Added\n- **Account-tied licensing via device-flow login** — `adk login` now persists the account\n license returned by the device-flow handshake to `~/.aither/license.json` (base64\n `license_key` → `{payload, signature}` envelope), so entitlements from the authenticated\n account carry into every later `adk` invocation without re-auth. Pairs with the fleet-side\n node tool-pack gate that grants packs by plan-tier rank.\n- **`can_use_formbridge` / `can_use_untether` entitlements** (PROFESSIONAL+ rank ≥ 3) backing\n the FormBridge and UNTETHER agent tool-packs.\n\n### Fixed\n- `adk` CLI: guard the optional `memory_graph` singleton (was a latent `NameError` on the\n `/stats`, `/memory`, and save paths when memory was not constructed).\n\n## [2.14.0] - 2026-07-02\n\n### Added\n- **`adk.memory_wiki` — LyraWiki-style self-managed semantic knowledge** (opt-in; nothing constructs it by default): `MemoryWiki(graph_memory)` maintains curated wiki ARTICLES as `wiki_article` nodes (title/slug, LLM-owned markdown with `[[wikilinks]]`, embedding, durable tier, `CONSOLIDATED_FROM` edges to source memories, governance-ledgered revisions).\n - `consolidate(llm, since=, budget=)` clusters unconsolidated episodic/fact nodes (embedding + keyword), drafts/UPDATEs articles via the INJECTED llm callable (`str -> str` or `messages -> str`, sync or async — no hard model dependency), cites source node ids, marks contradicted sources superseded, then DEMOTES consolidated sources to a fast-decay tier via `promote()` (represented knowledge doesn't need raw copies).\n - `lint(llm=None)` — deterministic health checks (orphan wikilinks, empty/stale articles, live-but-contradicted pairs) + an optional LLM audit pass.\n - `prune(relevance_floor=0.05, hard_delete_after_days=14)` — article relevance = tier freshness × reinforcement (reinforce-on-recall) + link-degree to LIVE memories; below the floor → reversible governance tombstone; entombed past the window → **HARD-DELETE** (the tombstone snapshot itself is purged — content and embedding irrecoverable; the true-deletion path). A node is never deleted without a tombstone first.\n - `recall(query, limit)` — article-first RAG (curated articles rank above raw nodes) that reinforces returned articles.\n- **`adk.routines` — agent heartbeat + self-programmed routines**: `RoutineStore` is a durable registry (`~/.aither/routines/{agent}.json`, `AITHER_DATA_DIR`-aware, injectable path) of cron-scheduled self-prompts `{name, cron, instruction, enabled, last_run, last_result, tags}` that rehydrates into the existing `adk.cron.CronScheduler` on `start()`. A fire runs `agent.chat(instruction)` with the agent's full toolset — the agent programs its future self with its own adk. Guardrails: `max_routines` (12), a 5-minute min fire interval per routine, a per-fire timeout, and every result ledgered (truncated) to `last_result`. Direct-callable routines (`register_direct`) fire a bound method instead of a self-prompt. Self-management tools (`routine_create/list/update/pause/resume/delete/run_now`) ship as OpenAI tool defs + handlers (`build_routine_tools` / `register_routine_tools` / `routine_tool_defs`); the handlers ONLY touch the RoutineStore — they can never modify agent config or safety-relevant settings (the leash principle).\n- **`AitherAgent(routines=True, memory_maintenance=True)`** (both default **False** → the default agent is byte-identical): `routines=True` builds the RoutineStore + scheduler, registers the self-management tools, and starts the heartbeat lazily on the first `chat()` (or via explicit `start_routines()`). `memory_maintenance=True` registers DEFAULT routines — `wiki_consolidate` (every 2 h), `wiki_lint` / `wiki_prune` / `graph_sweep` (daily) — as DIRECT method fires so memory upkeep runs even on tiny models, while staying visible/manageable via `routine_list`.\n- `TombstoneStore.purge(tombstone_id)` — the hard-delete endpoint: irrecoverably removes a tombstone snapshot and rewrites the persisted store without it (consumed by `memory_wiki.prune`).\n\n## [2.13.6] - 2026-07-02\n\n### Added\n- **Qdrant backend for GraphMemory dataplane sync — TRUE two-way, verified live.** Set `AITHER_FLEET_QDRANT_URL` and the sync uses Qdrant (`upsert` + `scroll` + filtered `search`) instead of Nexus. This is what actually delivers cross-agent memory sharing: a node is upserted with its OWN embedding under a deterministic per-(tenant,node) point id (idempotent re-push), and `fleet_pull` reliably SCROLLS a tenant's points back — the enumerate Nexus lacks (its `/search` doesn't return ingested docs and `/export` needs lancedb, which has a hard dependency conflict in the fleet). **Proven live with 2 agents:** agent-1 ingests → auto-syncs to the tenant dataplane; a fresh agent-2 in the same tenant `fleet_pull`s all of agent-1's nodes and can search them; a different tenant pulls nothing (isolation). Dimension-safe (never mixes vector dims in a collection). Local SQLite stays source of truth; best-effort, never raises.\n\n## [2.13.5] - 2026-07-02\n\n### Fixed\n- **CRITICAL — GraphMemory dataplane sync push was 100% failing.** `_fleet_push_node` sent `source_type=\"graph\"` / `content_type=\"graph_node\"`, which AitherNexus rejects as invalid enums (HTTP 500) — so NO graph node ever replicated in 2.13.4. Now sends the accepted `source_type=\"manual\"` / `content_type=\"text\"` (graph provenance is carried in `metadata.synced_from`). **Verified live** against a fleet Nexus: a 4-node ingest replicates with `pending==0` and the collection count reflects it.\n\n### Added\n- **Swarm-awareness notification** — on a successful sync batch, `GraphMemory` emits a best-effort `graph.synced` event (tenant/workspace/agent/collection/count) to `AITHER_FLEET_EVENTS_URL` so OTHER agents in the tenant/swarm can pull the fresh data and deconflict in-flight work (push-based awareness instead of polling). No-op unless the env is set.\n\n### Known limitation\n- Cross-agent **rehydration** (`fleet_pull`) is only reliable when the fleet Nexus is backed by lancedb (persistent + exportable). An in-memory Nexus stores on `/ingest` but neither its `/search` nor `/export` returns the docs — two-way read needs the persistent Nexus.\n\n## [2.13.4] - 2026-07-02\n\n### Added\n- **Memory-maintenance primitives on `GraphMemory`** (all opt-in; default behaviour byte-identical):\n - **Reinforce-on-recall**: `recall_with_activation(..., reinforce=True)` bumps `reinforcement_count`/`last_reinforced` on every RETURNED node's metadata mirror (one UPDATE per node, WAL-safe) — making the `MemoryRecord` reinforcement fields the activation scorer reads LIVE. Default `False` = zero writes.\n - **`sweep(now, archive_below=0.05, max_nodes=None, dry_run=False)`**: read-time decay enforced at rest — archives nodes past their tier TTL with freshness×reinforcement score below `archive_below` and `reinforcement_count <= 1`; `max_nodes` additionally archives the lowest-scored overflow. PERMANENT tier immune. Every archive is a reversible governance tombstone (`TombstoneStore.recover`) + a FORGET ledger entry; a node is never deleted without a tombstone. `dry_run` returns the would-archive list without acting.\n - **`promote(node_id, tier=?, role=?)`**: re-tier/re-role a node IN PLACE (tier/role columns + metadata mirror) — edges, embedding and ledger history survive; ledgers an UPDATE entry when governance is on.\n- `MutationType.UPDATE` ledger entry kind (in-place re-tier/re-role).\n- **Per-tenant dataplane sync for `GraphMemory`** — replicate the local graph to a per-tenant vector dataplane (Nexus/Qdrant) so a sovereign agent's memory is durable + rehydratable across restarts and hosting boundaries. `fleet_push_all_nodes()` (catch-up), `fleet_pull()` (best-effort semantic top-up), `fleet_sync_pending()`, and auto-sync on ingest (`_maybe_autosync` + `drain_sync()`). **On by default** (`AITHER_FLEET_SYNC=auto`; only `false/0/off` disables) with an inferred target (local Nexus `:8122` or the gateway in cloud mode) and a SEPARATE collection (`AITHER_FLEET_GRAPH_COLLECTION`, default `graph_memory`) from the KV memory sync. New `GraphMemory(tenant_id=, workspace_id=, fleet_url=, auto_sync=)` constructor args override env (required for multi-tenant host processes). Node `synced` column + schema v4 migration. TLS verify is CA-bundle-aware (`AITHER_CA_BUNDLE`) for the internal-TLS mesh. Local SQLite stays the source of truth; every sync path is best-effort and never raises into ingest.\n\n## [2.13.3] - 2026-07-02\n\n### Added\n- **Canonical self-deploying embeddings provider** (`adk.embeddings`): one embedding provider for the whole SDK so vectors are portable across scopes (platform / tenant / sovereign) — same model + dimension everywhere. Pinned to `nomic-embed-text` (768-d; the vLLM `--served-model-name` for nomic-embed-text-v1.5). Lazy, single-flight resolution chain: explicit `AITHER_EMBEDDINGS_URL` → local vLLM (`:8209` then `:8120`, HTTPS-then-HTTP) → local Ollama → gateway (`AITHER_GATEWAY_EMBEDDINGS_URL`) → auto-deploy a local vLLM embeddings container if a GPU + Docker are present (opt out with `AITHER_EMBED_AUTODEPLOY=0`) → CPU sentence-transformers (384-d, degraded) → feature-hash (384-d, degraded, always works). Every batch is dimension-tagged so callers never silently mix 768-d and 384-d vectors. `get_default_embedder()`, `embed_texts()`, `embed_one()`, `get_provider()`, `reset_provider()`.\n\n### Changed\n- **`GraphMemory` now defaults its embedder to the canonical provider** (opt out with `AITHER_GRAPH_EMBEDDER=legacy`), so every adk agent shares one 768-d space. Added a meta-table dimension guard: the index is pinned to its first embedding's dimension and later different-dimension vectors are refused (kills 768↔384 mixing that silently poisons cosine similarity).\n\n## [2.13.2] - 2026-07-01\n\n### Added\n- **Session persistence**: AitherShell now auto-saves the active session_id to `~/.aither/config.yaml` after each chat turn and auto-restores it on the next shell launch. Multi-turn conversations survive across restarts — the messages were always persisted in SQLite + JSON, only the session pointer was missing.\n- `/new` command to start a fresh session (clears saved session_id).\n- `/sessions` command to list recent sessions with agent name, message count, and timestamp. Resume any with `/resume <id>`.\n\n## [2.13.1] - 2026-07-01\n\n### Fixed\n- **CRITICAL:** `deploy_connect()` now sends a HEAD request to verify the GitHub release asset exists before downloading, with a clear error if the asset is missing.\n- **CRITICAL:** Compose file URLs are now pinned to a stable commit hash (configurable via `AITHER_COMPOSE_PIN` env) instead of tracking the mutable `main` branch.\n- **CRITICAL:** `deploy_adk_node()` now falls back to starting `adk-serve` natively when Docker is unavailable, instead of blocking with \"Docker not installed\".\n- **HIGH:** GHCR login failure now blocks deployment immediately with actionable guidance, instead of warning and continuing to a confusing image-pull failure.\n- **HIGH:** Health checks now stream the last 30 lines of Docker Compose logs on failure, so the user can see why a container didn't start.\n- **HIGH:** vLLM port allocation now probes for conflicts before assigning; if port 8200 is busy, workers are automatically remapped to the next free port.\n- **MEDIUM:** Compose file downloads are now cached with ETag; re-running `adk deploy node` skips re-downloading an unchanged file.\n- **MEDIUM:** DGX Spark discovery now respects `AITHER_DGX_HOSTS` env (comma-separated) for custom network topologies, instead of only checking `spark.local` and `192.168.0.33`.\n- **MEDIUM:** Cloud API keys (Anthropic, OpenAI, DeepSeek) are now validated with a lightweight probe during `adk setup` infra scan, so invalid keys surface early.\n- **MEDIUM:** `deploy_connect()` now validates the extracted extension has a `manifest.json` and warns if it's missing.\n\n## [2.13.0] - 2026-07-01\n\n### Removed\n- **`adk.faculties.memory_graph`** — the pickle-based `MemoryGraph` module is deleted. `from adk.faculties import MemoryGraph` now resolves to the canonical `adk.graph_memory.GraphMemory` for back-compat.\n- **`adk.platform.memory`** — the entire legacy memory subsystem (`MemoryManager`, `UnifiedMemorySystem`, `GameEngine`, `StoryboardEngine`, `AnchorGenerator`) is deleted. Agent memory is `adk.memory.Memory` + `adk.graph_memory.GraphMemory`.\n\n### Changed\n- `adk work` command now uses `GraphMemory` instead of the removed pickle-based faculty graph.\n- `adk.MemoryGraph` and `adk.faculties.MemoryGraph` lazy exports now resolve to `adk.graph_memory.GraphMemory`.\n- `agent.set_memory_graph()` kept for back-compat but documented as deprecated; agents already wire `GraphMemory` automatically in `__init__`.\n- Platform startup (`adk.platform.infrastructure.startup`) gracefully handles missing `MemoryManager`.\n- Platform CLI module listing and health check updated to reflect removed memory module.\n\n## [2.12.7] - 2026-07-01\n\n### Deprecated\n- `adk.faculties.MemoryGraph` — emits `DeprecationWarning` on instantiation; use `adk.graph_memory.GraphMemory` (SQLite-backed, governed, embedding-native) instead.\n- `adk.platform.memory` package — the legacy `MemoryManager`, `UnifiedMemorySystem`, `GameEngine`, and `StoryboardEngine` modules now emit `DeprecationWarning` on import; use `adk.memory.Memory` and `adk.graph_memory.GraphMemory` for new code.\n\n### Changed\n- Updated `adk.faculties` docstring and usage examples to point at the canonical memory surface.\n\n## [2.12.6] - 2026-07-01\n\n### Changed\n- Trimmed public package debt by removing grid runbook artifacts and shell/room binary placeholder directories from published artifacts.\n- Removed stale public-sync allowlist entries for deleted phase test files and added explicit public-sync guards for binary placeholder directories and grid artifacts.\n- Updated the public README grid note so it no longer links to runbook files excluded from the SDK package.\n\n## [2.12.5] - 2026-07-01\n\n### Added\n- Added best-effort fleet memory sync from local ADK memory into Nexus/Qdrant-compatible RAG stores, including batch catch-up and vector-search recall hooks.\n- Added saved config/env support for fleet memory URL, collection, and sync mode.\n\n### Changed\n- Excluded FormBridge runtime/test internals from public ADK packaging and public repo sync.\n\n## [2.12.4] - 2026-07-01\n\n### Added\n- Added a hot-reload endpoint for installed agent packs so newly applied pack tools can be picked up without restarting `aither-serve`.\n\n### Fixed\n- Fixed synchronous `ChatRelay` calls on Python 3.12 when no event loop is running; async WebSocket delivery is still scheduled when a loop exists.\n- Aligned gateway inference tier tests with the current public ladder: free, starter, pro, enterprise, platform.\n\n## [2.12.0] - 2026-06-28\n\n### Added — one-command local inference + upgrade path (click-to-run)\n- **`adk quickstart-local`** — detects hardware, picks a backend\n (Ollama / llama.cpp / vLLM), installs it, downloads a model, verifies, and\n prints next steps. Bootstraps Ollama itself via winget/brew/install.sh when\n missing (no \"go install it\" dead-ends). Default model `gemma4:e2b`.\n- **`adk backend switch <ollama|llamacpp|vllm>`** (+ `backend status`) — migrate\n the active deployment between engines; re-points config and smoke-tests.\n- **`adk install pack:<name>`** (+ `adk install list` / `adk packs`) — install\n ready-made agent packs (openclaw, hermes, claude-code) into `~/.aither/agents`.\n\n### Fixed (found via live end-to-end testing on Windows + CUDA)\n- quickstart-local reported success on a dead endpoint (smoke test was warn-only);\n a failed smoke now fails the command, with a service-readiness wait.\n- `--model` resolved to `None` (getattr default never fired) → uses the real default.\n- Smoke test: 30s→180s timeout for cold model load; accepts reasoning-model output\n (empty `content` at low token budgets is no longer a false failure).\n- `adk install pack:<name>` colon syntax (argparse rejected it before dispatch).\n- `adk backend status` health probe used a nonexistent `/health` path → 404; now\n probes `/v1/models` (uniform OpenAI-compatible liveness for all three backends).\n- Windows: removed non-ASCII glyphs from printed strings that crashed the cp1252\n console (a `✓` even made a successful install report failure).\n\n## [2.11.1] - 2026-06-14\n\n### Fixed — `adk host` / `adk login` device flow (autonomous onboarding)\nFound driving `adk host` against production; all broke real device-flow login:\n- **Device flow hit the portal FRONTEND, not AitherIdentity.** `adk login`/`adk host`\n POSTed `/auth/device/code` to portal/veil.aitherium.com, which 307-redirects API auth\n to `/login`. Added `_resolve_identity_url()` mapping the aitherium.com topology to\n `idp.aitherium.com` (the Identity API); localhost/bespoke pass through unchanged.\n- **Cloudflare 403'd urllib.** `idp.*` sits behind Cloudflare, which blocks urllib's\n default `Python-urllib/3.x` UA. Device-code + poll requests now send a real\n User-Agent + Accept (verified urllib→idp 200, was 403).\n- **`adk host` now self-authenticates via device flow** when there's no token, and\n **retries via device flow on a 401/403** (a stale saved token no longer dead-ends\n registration).\n\n## [2.11.0] - 2026-06-14\n\n### Added — attach your own MCP \"hands\" to a self-hosted agent\nA self-hosted agent now has the full brain (your model) · body (this loop) · **hands**\n(your tools) trifecta. The AitherOS platform relays the MCP servers you registered for\nyour agent in the `/stream` request body as `mcp_endpoints`, and the loop wires their\ntools into the ReAct turn:\n- **`adk/mcp_endpoint_tools.register_mcp_endpoint_tools(agent, endpoints)`** — speaks MCP\n JSON-RPC (`tools/list` to discover, `tools/call` to invoke) and registers a namespaced\n proxy tool (`{endpoint}__{tool}`) for each advertised tool. Mirrors `app_proxy_tools`.\n Best-effort: an unreachable endpoint registers nothing and never raises.\n- **`POST /stream` and `POST /chat/stream`** accept an optional `mcp_endpoints` list\n (`[{name, url, [headers]}]`); `_aitheros_stream` attaches them to the agent before the\n turn. Existing callers are unaffected (the field is optional).\n\n## [2.10.1] - 2026-06-14\n\n### Fixed — self-hosted `aither-serve` was crash-on-startup (2.10.0 regression)\nA fresh `pip install aither-adk && aither-serve --backend deepseek` could not boot. Found\nand fixed while standing up a real self-hosted DeepSeek agent end-to-end:\n- **Startup `NameError: name 'port' is not defined`** in three lifespan helpers\n (`_join_aithernet`, `_init_a2a_server`, `_start_mesh_hosting`) — they referenced a bare\n `port` no longer in scope. Now use `config.server_port`, and `main()` writes the resolved\n `--port`/`--host` back to `config` so every lifespan helper and the invoke_url see the\n actual bound port.\n- **Fleet self-registration crashed the whole server** — the registration handlers caught\n `(ImportError, RuntimeError, OSError, ConnectionError)` but **not** `httpx.HTTPError`, so a\n `RemoteProtocolError`/connect error talking to the control plane took the agent down. All\n network-facing lifespan handlers now also catch `httpx.HTTPError` (non-fatal, as intended).\n- **`--backend` was ignored when `AITHER_API_KEY` was set** — `_auto_detect` picked the cloud\n gateway before the explicit backend, and `_try_elysium_fallback` then replaced the chosen\n provider. An explicit backend (deepseek/openai/anthropic/your own vllm/ollama) now always\n wins; \"auto\"/\"gateway\" still flow through detection. The operator's own brain is never\n silently hijacked.\n- **Approved-tool trace truncated after `/sessions/{id}/confirm`** — the SSE relay treated\n `AgentResponse.tool_calls_made` (a `list[str]`) as dicts (`tc.get(...)`), raised\n `AttributeError`, and an over-narrow `except` swallowed it so the client saw only\n `session_start` + heartbeats. The relay now handles string tool names, the `except` is\n broad (always emits a typed `error` + terminal `complete`), and the module-level\n `_strata_ingest` `NameError` + Chronicle `session_id` kwarg mismatch are fixed.\n\n## [2.10.0] - 2026-06-13\n\n### Added — human-in-the-loop tool approval (`adk/approval.py`)\n- **Pause before a gated tool, resume on decision.** An agent can now pause its turn before\n executing a sensitive tool and wait for a human's allow/deny — so a managed control plane\n (or any operator) can gate tool use without forking the loop.\n- **Policy via `AITHER_TOOL_APPROVAL`** — a comma-separated list of tool names (optionally\n `agent:tool`), or `*` for every tool. Empty = no gating (unchanged behavior).\n- **Survives restarts / days-later approvals.** The paused turn (user message + pending tool\n calls) is persisted to disk keyed by `session_id`; resume is a fresh request, not a held\n connection. Decisions are recorded per `(session_id, tool_name)`.\n- **`AgentResponse.requires_action` / `.pending`** — set when a turn paused; `pending` lists\n the gated tool calls awaiting a decision.\n- **`AitherAgent.resume(session_id, decisions)`** — records the decisions and re-runs the\n paused turn (the gate consumes them: allowed tools execute, denied tools get a denial\n observation, the turn continues — and may pause again on a different gated tool).\n- **Server:** `POST /sessions/{id}/confirm` records decisions and streams the resumed turn;\n `/stream` emits a `requires_action` event when a turn pauses.\n\n## [2.9.0] - 2026-06-13\n\n### Added — universal LLM continuation primitive (`adk/llm/continuation.py`)\n- **`run_continuation()` + `stitch()`** — one shared continue-until-complete primitive. When a\n completion stops because it hit the output token cap (`finish_reason == \"length\"`), it continues\n the generation and STITCHES the chunks (overlap-dedup + restart-detect) into a complete answer,\n instead of every call-site hand-rolling its own retry. Bounded three ways (max rounds AND total\n output ceiling AND a no-growth break), and it never mutates the caller's message history.\n- **`LLMProvider.chat_with_continuation()`** and **`LLMRouter.chat_with_continuation()`** — every\n provider AND the router inherit the primitive, so any call-site gets continuation for free.\n- Kill-switch `ADK_LLM_CONTINUATION=off`; tunables `ADK_LLM_MAX_CONTINUATIONS`,\n `ADK_LLM_MAX_TOTAL_OUTPUT_TOKENS` (defaults: 2 rounds / 8192 tokens).\n\n### Changed\n- The ReAct loop's truncation recovery now uses the shared primitive. The previous inline\n doubling-retry REPLACED the partial response (losing earlier text in `resp.content`) and injected\n continuation scaffolding into the live message history; it now STITCHES and leaves history clean.\n Tool-call turns are never continued as text.\n\n### Included — previously-committed-but-unreleased changes\n- 2.8.2: TLS verification on by default (`verify=False` removed).\n- 2.8.1: baked license + pack-signing public keys.\n- 2.8.0: version-bump release.\n\n## [2.7.0] - 2026-06-05\n\n### Added — provider-agnostic LLM cost levers (at the `LLMProvider` ABC)\n- **Prompt caching.** New `ProviderCapabilities` (`prompt_cache: explicit|automatic|none`, `batch`) and\n normalized `LLMResponse.cache_read_tokens`/`cache_write_tokens`, so the \"tokens saved\" meter never\n depends on which backend served the turn. Anthropic inserts `cache_control` breakpoints over the stable\n system+tools prefix; OpenAI/DeepSeek caching is automatic and their `cached_tokens` are now surfaced\n (previously discarded). Default-on in `AitherAgent.chat`; override with `cache=False`.\n- **ResponseCache** (`adk/llm/cache.py`) — exact-match response cache with a pluggable backend, wired into\n `LLMRouter` as an opt-in (`LLMRouter(response_cache=…)` + per-call `cacheable=True`). OFF by default — no\n determinism change unless opted in.\n- **Gemini provider** (`adk/llm/gemini.py`) with explicit `cachedContents` caching; registered in the router.\n- **Async batch runner** (`adk/llm/batch_runner.py`) — `run_batch()` over the Anthropic Message Batches /\n OpenAI Batch APIs (~50% off) with a transparent fallback to concurrent `chat()` where unsupported.\n- **Graph-RAG retriever interface** (`adk/graph_retriever.py`).\n\n### Added — principal authority (prompt-injection defense enforced outside the model)\n- `adk/auth.py`: `Principal` / `AuthContext` / `PrincipalResolver` (+ deny-all default).\n- `ToolRegistry.execute(name, args, auth=…)` — a default-deny authorization gate at the tool boundary, so a\n forbidden tool call is blocked regardless of what the LLM emits. `ToolDef` gains `required_clearance` /\n `action_class`. Threaded through `AitherAgent.chat`; `auth=None` is fully backward-compatible.\n- `adk/channel_auth.py`: Telegram webhook-secret + CEO-id checks and HMAC-signed approvals.\n\n## [2.6.4] - 2026-06-05\n\n### Fixes\n- **Loop-guard nudges corrupted the message history → DeepSeek 400 mid-run.**\n When the ReAct loop emitted multiple tool calls in one assistant turn and the\n loop guard fired a WARN/BLOCK/CIRCUIT_BREAK on one of them, it spliced a\n standalone `system` nudge message *between* the tool results. DeepSeek (and\n strict vLLM chat templates) require an assistant `tool_calls` message to be\n followed by exactly one `tool` result per `tool_call_id`, contiguously — so the\n interleaved message produced `400 … insufficient tool messages following\n tool_calls message` and aborted the turn (this is reliably hit after crossing\n the 15-call circuit-break threshold, where every subsequent call WARNs).\n Per-call guidance is now folded into that call's own `tool` result, and any\n standalone steering is deferred to a single message AFTER the whole batch —\n preserving the `tool_calls`↔`tool_results` pairing every backend requires.\n Regression test: `deep-research-agent/tests/test_msg_structure.py`\n (loopguard-flood scenario).\n\n## [2.6.3] - 2026-06-04\n\n### Fixes\n- **Transient provider errors killed a run on the first blip.** A single\n `502 Bad Gateway` / `503` / `529 overloaded` / `429` from the upstream API\n aborted the whole request — the Anthropic provider had **no retry at all**, and\n the OpenAI-compatible provider only retried `429`. Both now retry transient\n statuses (`408/429/500/502/503/504/529`) with `Retry-After`-aware exponential\n backoff (3 attempts), and surface the provider's body if all retries are\n exhausted. Non-transient `4xx` (e.g. `400`) still fails fast. Applies to\n `chat` and `chat_stream` on both providers.\n\n## [2.6.2] - 2026-06-04\n\n### Fixes\n- **DeepSeek (and strict OpenAI-compatible backends) 400'd on long tool\n conversations.** The ReAct loop legitimately injects mid-conversation\n `system` steering messages (the \"[DIMINISHING RETURNS]\" hint after several\n low-output tool iterations, loop-guard nudges). OpenAI tolerates a non-leading\n `system` message; DeepSeek rejects it with a 400. Long research runs\n (many `fetch_url` calls) reliably tripped this. Fix: non-leading `system`\n messages are demoted to `user` at the OpenAI-compatible provider boundary\n (`_demote_nonleading_system`) — steering intent preserved, payload valid on\n every backend; leading system prompt(s) untouched.\n- **Opaque HTTP errors.** `resp.raise_for_status()` discarded the provider's\n response body, so a 400/422 surfaced as a bare `Client error '400 Bad\n Request'` with no cause. `_ensure_ok` now includes the provider's actual\n message (e.g. `context_length_exceeded`, `Invalid 'messages'`) in the raised\n error, for both `chat` and `chat_stream`.\n\n## [2.6.1] - 2026-06-04\n\n### Fixes\n- **`MCPServer.mount()` returned HTTP 422 for every `/mcp` call.** Under\n `from __future__ import annotations` the route handler's `request: Request`\n annotation is a string that FastAPI resolves against the *module* globals, but\n `Request` was imported locally inside `mount()` — so FastAPI couldn't resolve\n it and treated `request` as a required query param. The MCP server endpoint\n (`initialize`, `tools/list`, `tools/call`) was unusable when mounted. FastAPI\n symbols are now imported at module level (guarded; the `[node]` extra still\n gates actual use). Any agent's tools can now be exposed as a working MCP\n server with `MCPServer(...).mount(app)`.\n\n## [2.6.0] - 2026-06-04\n\n### Streaming + scaffolding\n- **`AitherAgent.stream_chat`** — reliable streaming sibling of `chat()`: native\n tool-calling loop (concludes on its own, unlike the text-protocol `stream_react`)\n that emits live `tool`/`tool_result` events and streams the answer as `token`\n events. `token_delay` paces tokens for a typing effect.\n- **`adk new <template>`** — scaffold a full, runnable template app. Ships the\n `deep-research` template (a cited web-research agent: pack + server + web UI):\n `adk new deep-research`.\n\n### Fixes\n- **BYO-key no longer community-capped.** The free-tier monthly token cap now applies\n only to the metered Aitherium gateway backend, never to a user's own provider key\n (Anthropic/OpenAI/DeepSeek/Ollama/vLLM). Previously it could brick a self-hosted\n agent after ~100k tokens on the user's own key.\n- **Stronger, grounded memory recall.** `chat()` now injects the top-6 knowledge-graph\n hits (untruncated) with a hard no-fabrication instruction — was top-3 truncated to\n 200 chars, which surfaced partial facts and let weaker models invent the rest.\n- **Tool-arg coercion.** `ToolRegistry.execute` coerces LLM-supplied args toward each\n parameter's annotated type, so a tool no longer crashes when a model passes a list\n or string where an int/float/bool was expected.\n\n### Docs\n- New `docs/AGENT_DEV_GUIDE.md` — the golden path for building agents/packs.\n\n## [2.5.0] - 2026-06-04\n\n### Continual learning — agents that learn from runs and reuse memory\n\nAgents now improve the second time they do a task instead of starting from scratch.\n\n### Added\n- **Recall-before**: `AitherAgent.chat()` searches the local `SkillStore` and injects the\n top matching learned skills as a system block, so the model reuses proven procedures.\n- **Learn-after**: `_learn_after()` runs after each response — reinforces (`success_count++`)\n any skills that were recalled+reused, and extracts + saves a NEW skill from successful\n multi-tool runs via `SkillExtractor`. Wired at both `chat()` return paths.\n- Gated by `AITHER_SKILLS` (default on). The previously-unused `SkillStore`/`SkillExtractor`\n are now connected to the run loop.\n\n### Notes\n- Pairs with the platform-side LearnedProcedure substrate (recipes → scored procedures →\n skills/tools/A2A/packs → MCTS priors → evolution). The SDK shares memory via the Spirit\n bridge when configured.\n\n## [2.0.0] - 2026-06-02\n\n### Open-core boundary (BREAKING)\n\nThis release draws a single, enforced free/paid line. The free tier stays\ngenuinely useful (real agent, typed + graph + code memory, ReAct, ~essential\ntools); paid tiers unlock the *scale* capabilities via portal.aitherium.com.\n\n### Added\n- **`adk/licensing.py`** — the entitlement keystone. Resolves a tier\n (COMMUNITY by default), verifies optional portal-signed `~/.aither/license.json`\n (Ed25519), and answers `can_use_fleet/channels/cron/swarm/auto_neurons`,\n `is_agent_licensed`, `max_effort`, etc. **Fail-closed**: no/invalid/expired\n license → free tier; an unsigned license never grants premium.\n `AITHER_TENANT_SLUG=aitherium` → unrestricted INTERNAL tier.\n- **Moat guard** (`scripts/check_moat_boundary.py`) wired into the publish\n workflow — blocks any wheel that leaks the moat or drops the keystone.\n- Tests: `tests/test_licensing.py`, `tests/test_moat_boundary.py`.\n\n### Changed (gating — fail-closed)\n- Free tier effort is capped at 3 (reasoning effort 7–10 needs Professional).\n- Monthly token budget hard-block on the free tier (no silent overspend).\n- Gated behind a paid tier: fleet mode (`FleetConfig`), cross-agent delegation\n (`AgentForge.delegate`), channel adapters, cron scheduler, proactive\n auto-neurons, and `swarm`/`swarm_code` dispatch.\n\n### Removed\n- **`adk/nanogpt.py`** (on-device training IP) removed from the published SDK\n and the public repo; relocated to the internal AitherOS moat. Excluded from\n the wheel as defense-in-depth.\n- Pre-2.0 releases shipped this IP and ungated capabilities and are being\n yanked (PyPI) / removed (GitHub releases). See `scripts/yank_leaky_releases.py`\n and `scripts/purge_public_leaks.sh`.\n\n## [1.20.0] - 2026-05-27\n\n### Fixed\n- **Pack discovery path mismatch** — `_find_local_packs()` now scans both\n `~/.aither/packs/` and `~/.aitheros/packs/`, so packs installed via\n `adk pack install` are discovered by `adk-workspace`.\n\n### Added\n- **Auto-register on pack install** — `adk pack install` now auto-registers\n with the portal if the pack contains an `agent.yaml` with a `portal` section.\n One command does download + extract + register.\n- **Flux event on agent registration** — `developer_portal.py` emits\n `agent.registered` Flux event so the portal fleet UI updates in real-time.\n\n## [1.15.0] - 2026-05-26\n\n### Added\n- **Cloud memory** — `adk quickstart --cloud` now tests gateway memory and\n auto-configures Spirit URL so memories persist across devices via\n `gateway.aitherium.com/v1/memory/teach` and `/v1/memory/recall`.\n- **Config→env export for cloud mode** — `Config.from_env()` now exports\n `AITHER_CLOUD_MODE`, `AITHER_SPIRIT_URL`, and spirit path env vars from\n saved config so Memory and other modules pick them up without needing\n explicit env vars set by the user.\n- **Cloud Quick Start section** in README — 3-command setup for users who\n just want a Claude/GPT-4/DeepSeek API key and the full agent harness.\n- **`--memory` profile for node deployment** — `adk deploy node --memory`\n adds Spirit (8087) + WorkingMemory (8101) containers for persistent\n vector memory. Without this, agents use local SQLite only.\n- **Configurable vLLM in node compose** — `AITHER_VLLM_MODEL`,\n `AITHER_VLLM_GPU_UTIL`, `AITHER_VLLM_CTX_LEN`, `AITHER_VLLM_QUANT_ARGS`\n env vars let `adk setup` write GPU-detected tier settings to `.env`\n and have the node compose pick them up (TQ4 for small GPUs, BNB for large).\n\n### Fixed\n- **Memory cloud routing** — `Memory._spirit_teach()` and `_spirit_recall()`\n now use configurable paths and auth headers for gateway proxy. Previously\n cloud-only users silently lost all Spirit memory calls.\n\n## [1.9.0] - 2026-05-24\n\n### Added\n- **Composable ToolPack system** — agents can load tool packs from\n `.toolpack.yaml` directories. Drop a pack in `~/.aitheros/packs/` or\n set `AITHER_PACKS_DIR`, and any agent discovers it automatically.\n- **`tools.packs` in agent.yaml** — list tool pack IDs to auto-load\n (e.g. `packs: [crm-tools, calendar-pro]`).\n- **`register_tool_packs(agent, pack_ids, packs_dir)`** in `builtin_tools` —\n programmatic pack loading for agents.\n- **`AITHER_TOOL_PACKS` env var** — comma-separated pack IDs loaded on\n agent init (alternative to YAML config).\n- **Unknown builtin categories resolve as pack IDs** — `tools.builtin:\n [file_io, crm-tools]` now works: `crm-tools` is looked up as a pack.\n\n## [1.8.0] - 2026-05-24\n\n### Removed\n- **11 dead modules** (3,023 lines) — `agent_deploy`, `cloud_deploy`, `create`,\n `phonehome`, `sdk_bridge`, `sovereign`, `telemetry`, `telemetry_config`,\n `error_reporter`, `anonymizer`, `package`. None were imported anywhere.\n- **29 ghost exports** from `__init__.py` — `secrets`, `otel`, `fs_sandbox`,\n and `vector_memory` modules were listed in `__all__` and `__getattr__` but\n the backing files never existed. Accessing them raised `ModuleNotFoundError`.\n- **Stale dist/ artifacts** — removed old wheels/tarballs (v0.4.0–v1.4.1, 11 MB).\n- **Orphan test scripts** — `test_phase1.py` and `test_phase23.py` (one-off\n validation scripts not part of the pytest suite).\n\n### Fixed\n- **Version mismatch** — `__init__.__version__` now matches `pyproject.toml`\n (was stuck at 1.6.0).\n\n## [1.5.0] - 2026-05-23\n\n### Added\n- **`--sovereign` flag for `adk deploy node`** — registers the node with the\n Aitherium hub via federation after deployment. Saves federation credentials\n to `~/.aither/.env.federation`. Accepts `--hub` (custom hub URL) and\n `--tenant` (tenant slug) options. Non-fatal: node runs standalone if\n registration fails.\n- **Federation → fleet cross-post** (Genesis) — sovereign nodes that register\n or heartbeat via `/federation/register` and `/federation/heartbeat` now\n automatically appear in the fleet dashboard (`/fleet/deployments`) with\n `node_type: \"sovereign\"` and live status/metrics.\n- **\"Deploy Sovereign Node\" section on Connect page** (AitherVeil) — Section 8\n with ADK deploy commands, fleet dashboard links, and provision page links.\n- **`deploy-node` action in tunnel proxy** (AitherVeil) — Connect page and\n external callers can now request bootstrap scripts via\n `POST /api/tunnel/devworkspace?action=deploy-node`.\n\n### Fixed\n- **Fleet provision page** — API URL corrected from `/api/fleet/provision` to\n `/api/bridge/genesis/fleet/provision` (matches bridge proxy pattern).\n- **Fleet detail page** — API URL corrected from `/api/fleet/deployments/` to\n `/api/bridge/genesis/fleet/deployments/` (same fix).\n- **Dockerfile** — entry point corrected from `aither-serve` to `adk-serve`\n (matches `pyproject.toml` script registration). Version label updated.\n\n## [1.4.1] - 2026-05-22\n\n### Fixed\n- **macOS executable builds** — dropped `--target-arch universal2` from\n `packaging/build_executable.py`. PyPI wheels for `pydantic_core` and other\n native deps are arch-specific, not fat binaries, so universal2 always failed\n with `IncompatibleBinaryArchError`. Now builds for the runner's native arch\n (`aither-macos-arm64`).\n- **Release workflow strategy** — added `fail-fast: false` so a single\n executable build failure no longer cancels the other OS jobs.\n- **PyPI publish** — added `skip-existing: true` so re-runs of the same\n version don't fail when the artifact already exists.\n- **GitHub Release job** — now runs whenever `pypi` succeeds even if some\n executable matrix entries fail, so the release page is always created with\n whatever binaries did build successfully.\n\n## [1.4.0] - 2026-05-22\n\n### Added\n- **Sovereign template now ships with `agent_core` 5-tier memory** — generated\n backends include the canonical `agent_core/` package (working → episodic →\n semantic → procedural → identity) plus `agent_memory.py` helper, vendored\n from `portal-kit-backend`.\n- `aiosqlite>=0.20.0` added to sovereign template `requirements.txt` for\n local agent_core SQLite storage.\n- Document tombstoning: `evict_cached_conversations_referencing` is wired\n through document-delete handlers in consumer apps so memories citing\n deleted documents are blocked from recall.\n- New tests: 13 smoke tests for `portal-kit-backend/agent_core` covering\n promotion thresholds, decay rates, store roundtrip, tombstone recall\n blocking, circuit breaker state machine, and chat-turn recording.\n\n### Changed\n- `scripts/vendor_agent_core.py` now syncs canonical `agent_core` to 4\n targets: WorkspaceRuntime, ADK sovereign template, and the GargBot +\n Chelle consumer mirrors (previously only 2).\n\n## [1.2.1] - 2026-05-21\n\n### Added\n- `adk train` command group — 6 subcommands: status, launch, logs, cancel, runs, register-gpu\n- `/slash-commands` endpoint — ADK server auto-exposes all 35 CLI commands as structured JSON manifest for AitherShell\n- `/cli/execute` endpoint — AitherShell can run any CLI command through the server\n- `build_command_manifest()` — introspects full argparse tree (commands, args, choices, subcommands)\n- `_register_commands()` extracted from main() for shared parser construction\n\n### Changed\n- `_get_genesis_url()` now probes: Genesis → ADK server → Aitherium cloud gateway (works without Genesis)\n\n## [1.2.0] - 2026-05-21\n\n### Added\n- **Runtime backend switching** — `agent.switch_backend(\"deepseek\")` and `agent.llm.switch_backend()` change provider without recreating agent\n- **Hybrid reasoning** — `agent.set_reasoning_backend(\"anthropic\")` routes effort 7+ to cloud API while keeping local orchestrator\n- **First-class DeepSeek provider** — `deepseek` in effort model table with `deepseek-chat` and `deepseek-reasoner`\n- **TQ4 quantization tiers** — `nano` (6GB, TurboQuant 4-bit), `standard-tq4` (12GB, both models TQ4), `hybrid-tq4` (6GB + cloud reasoning)\n- **`adk backend` CLI** — `list`, `set`, `set-reasoning`, `test` subcommands\n- **`adk quickstart`** — unified first-run wizard (setup + auth + shell)\n- **`adk tools`** — list local + MCP tools with tier markers\n- **`adk backup`** — tarball export of `~/.aither/`\n- **`adk ingest`** — feed docs/files into knowledge graph\n- **`adk setup nemotron`** shortcut — alias for `--tier lite`\n- **`--reasoning-api`** flag in setup — `anthropic`, `openai`, `deepseek`, `gateway`\n- **`--dgx-spark URL`** flag — remote vLLM endpoint configuration\n- **DGX Spark auto-detection** — scans `spark.local`, `192.168.0.33`, `AITHER_DGX_URL` env\n- **Post-setup smoke test** — sends real inference request after container startup\n- **Schema migration system** — version table + migration runner in `memory.py` and `graph_memory.py`\n- **Configurable vLLM port scan** — `AITHER_VLLM_PORTS` env var for custom ports\n- **`aithershell` entry point** — alias for `adk` in pyproject.toml\n- Shell auto-downloads binary on first `adk shell` (no `--install` needed)\n- Shell pre-flight check warns if no backend detected\n- Shell passes full config via env vars (API key, tenant, inference URL)\n- DGX/Remote and Cloud API checks added to `adk doctor`\n- Config fields: `reasoning_backend`, `reasoning_api_key`, `deepseek_api_key`, `dgx_url`, `vllm_extra_ports`\n\n### Fixed\n- `test_aeon_chat_basic` timeout — mock scope was exiting before request\n- `adk doctor` check_disk timeout — cap rglob at 5000 files\n\n## [1.1.7] - 2026-05-19\n\n### Added\n- Remote agent pipeline — `adk onboard` → fleet dispatch → remote inference\n- Agent-to-agent federation with mesh relay\n- `adk connect` for desktop mesh enrollment\n\n## [1.1.6] - 2026-05-19\n\n### Fixed\n- Tool call execution recovery across all backends\n- Hermes tool call fallback parsing for vLLM\n\n## [1.1.5] - 2026-05-19\n\n### Fixed\n- Tool call execution + Hermes fallback parsing improvements\n\n## [1.1.0] - 2026-05-18\n\n### Added\n- Multi-channel gateway (Telegram, Discord, Slack, webhook)\n- Aeon group chat with 7 presets\n- Skills auto-extraction from multi-step sessions\n- MCP stdio server for Claude Code integration\n- `adk gateway`, `adk cron`, `adk skills`, `adk soul` commands\n- SOUL.md import/export for portable agent identity\n- Elysium cloud device-flow authentication\n\n## [1.0.0] - 2026-05-18\n\n### Breaking\n- Package consolidated — `aither_adk` namespace replaced by `adk` + `adk.platform`\n- 93 duplicate files removed from `AitherOS/packages/aither_adk/`\n\n### Added\n- Standalone core primitives (`adk.core`: Agent, Tool, Memory, ModelBackend, Capability, Trace)\n- `adk.platform` sub-package merging all platform toolkit modules\n- Compatibility shims for legacy `from aither_adk.*` imports\n\n### Changed\n- Version synced across pyproject.toml, `adk/__init__.py`, `adk/platform/__init__.py`, npm package\n- Classifier promoted from Alpha to Beta\n\n## [0.16.0] - 2026-04-16\n\n### Swarm Coding Engine & Repowise Integration\n- **`repowise_search` tool** — Semantic + keyword hybrid code search via Repowise, with ripgrep fallback\n- **`swarm_code` tool** — Dispatch complex tasks to 11-agent swarm pipeline (ARCHITECT->SWARM->REVIEW->JUDGE)\n- **`agent.swarm()`** — Async convenience method with configurable mode, effort, timeout\n- **`agent.code_search()`** — Async convenience method returning structured results\n- New tool categories: `repowise` and `swarm` in TOOL_CATEGORIES\n- `repowise_search` and `swarm_code` added to `__init__.py` exports with lazy loading\n- `IDENTITY_DEFAULTS` updated: repowise for code-focused agents, swarm for orchestration agents\n- Standalone graceful degradation: repowise falls back to ripgrep, swarm returns structured error\n\n## [0.13.0] - 2026-04-02\n\n### Graph Faculties — Local Knowledge for Every Agent\n- **CodeGraph** (2,799 lines) — Full Python AST indexer with call graph, keyword/semantic/hybrid query, embedding matrix cache, incremental re-indexing, multi-hop chain expansion\n- **MemoryGraph** (1,339 lines) — Graph-based persistent agent memory with 10 edge types, hybrid query (keyword + semantic + graph expansion), multi-hop recall, pickle persistence\n- **EmbeddingProvider** — 4-backend fallback chain: sentence-transformers (GPU/CPU) -> Ollama -> Elysium cloud -> feature hashing (zero deps)\n- **BaseFacultyGraph** — Abstract base with HMAC-SHA256 validated pickle persistence\n\n### Agent Integration\n- `agent.set_code_graph(cg)` — auto-registers `code_search` + `code_context` tools\n- `agent.set_memory_graph(mg)` — auto-registers `remember` + `recall` + `memory_stats` tools\n- Both graphs inject context into LLM prompts automatically during chat\n\n### Zero-Config Onboarding\n- `adk start` / `adk` (no args) — auto-detect project, index code, detect LLM, persistent memory, interactive chat\n- Works for any directory: Python codebases, doc folders, mixed workspaces\n- Auto-detects LLM: Ollama -> vLLM -> Elysium -> OpenAI -> Anthropic\n- Per-project persistent memory in `~/.aither/memory/<project>`\n- `adk index <path>` — standalone indexing with progress bar and stats\n\n### MCP Gateway\n- `POST /v1/embeddings` — OpenAI-compatible embedding proxy to vLLM-embeddings:8209\n- Available to all tiers (embeddings are free)\n- ADK EmbeddingProvider uses this as Elysium cloud fallback\n\n### Optional Dependencies\n- `pip install aither-adk[graphs]` — numpy for 10x cosine similarity speedup\n- `pip install aither-adk[embedding]` — sentence-transformers + torch for local GPU embeddings\n- `pip install aither-adk` alone — graphs work with feature hashing (zero deps)\n\n## [0.12.0] - 2026-04-01\n\n### Bootstrap & Service Discovery\n- Version handshake -- ADK checks major.minor compatibility with Genesis/Node\n- Background reconnect loop -- ServiceBridge re-probes every 30s in standalone mode\n- Port 8080/8090 documented -- MCP vs OpenAI-compat clearly separated\n- Genesis URL configurable via GENESIS_URL env var (was hardcoded)\n- Standalone mode warning -- visible stderr alert when AitherOS not detected\n- Auto-reconnect on startup when services come online\n\n### Elysium Cloud Inference\n- Unified gateway URL -- gateway.aitherium.com handles auth + billing + inference\n- Streaming inference -- SSE passthrough for /v1/chat/completions with stream=true\n- Auth proxy routes -- /v1/auth/register, /v1/auth/login, /v1/auth/me\n- Billing proxy -- /v1/billing/balance through gateway\n- AitherConnect Elysium fallback -- cloud inference when local Genesis is down\n- AitherDesktop Elysium fallback -- third-tier chat fallback after Node\n\n### Infrastructure\n- /discovery endpoint on Genesis -- unified service URLs/versions/health\n- /api/config/services on Veil -- runtime port config for client-side JS\n- Veil healthcheck gates on Genesis -- unhealthy when backend is down\n- Desktop crash detection 90s->60s (threshold 3->2)\n\n## [0.11.0] - 2026-04-01\n\n### Agent Execution Quality (Claude Code Parity)\n- Raise loop guard block threshold from 3 to 4 -- agents get more room for iterative search\n- Soft synthesis nudge for effort >= 4 (no tool stripping, trust the model)\n- max_output_tokens escalation -- retry up to 3x when response is truncated\n- Tool result pairing guarantee -- synthetic error for orphaned tool_use blocks\n- Micro-compaction of old tool results -- save context tokens on long sessions\n- First-turn tool forcing only for effort >= 6 (trust model for lower effort)\n- Diminishing returns detection -- nudge agent when 3+ turns produce < 500 tokens\n- Message normalization -- merge consecutive same-role messages, strip empties\n- LLM retry with exponential backoff (5 retries, 500ms-16s, jitter)\n\n## [0.9.0] - 2026-03-16\n\nThe \"connected world\" release. Cross-platform identity pairing, voice capabilities, and multi-channel integration.\n\n### Added\n- **Pairing**: Cross-platform identity linking (`adk/pairing.py`)\n - `PairingManager` — SQLite-backed identity linking with 6-char pairing codes\n - Link users across Telegram, Discord, Slack, WhatsApp with 10min TTL codes\n - Canonical session IDs for cross-channel conversation continuity\n - `get_session_id()` returns \"user-{id}\" for paired users\n- **Voice**: Speech-to-text and text-to-speech client (`adk/voice.py`)\n - `VoiceClient` — async STT/TTS/emotion via AitherVoice service\n - Convenience functions: `hear()`, `say()`, `feel()`\n - 6 voice options: alloy, echo, fable, nova, onyx, shimmer\n - Emotion detection with intensity scoring\n- New exports: `PairingManager`, `PairingResult`, `PlatformIdentity`, `VoiceClient`, `TranscriptionResult`, `SynthesisResult`, `EmotionResult`\n\n### Changed\n- `__init__.py` exports expanded with pairing and voice symbols\n\n## [0.6.0] - 2026-03-13\n\nThe \"group mind\" release. Multi-agent group chat, creative tools, and Iris identity.\n\n### Added\n- **Aeon**: Multi-agent group chat engine (`adk/aeon.py`)\n - `AeonSession` — persistent group chat with parallel agent execution\n - 7 presets: balanced, creative, technical, security, minimal, duo_code, research\n - Orchestrator synthesis: Aither summarizes all agent responses\n - Serial execution for Ollama, parallel for vLLM/cloud\n - ConversationStore persistence with `type: \"aeon\"` metadata\n - `group_chat()` one-shot convenience function\n- `aither aeon` CLI command — interactive terminal group chat with color-coded agents\n - `-p/--preset`, `-a/--agents`, `-r/--rounds`, `--no-synthesize` flags\n - `reset` and `quit` commands\n- Server endpoints: `POST /aeon/chat`, `GET /aeon/presets`, `GET /aeon/sessions/{id}`\n- Creative tools in builtin_tools: `image_generate`, `image_refine`, `image_search`, `video_generate`\n- Iris agent identity with visual generation capabilities\n- 48 Aeon tests (data models, presets, chat, context, persistence, server, exports)\n\n### Changed\n- `__init__.py` exports: `AeonSession`, `AeonResponse`, `AeonMessage`, `group_chat`, `AEON_PRESETS`\n\n## [0.5.0] - 2026-03-13\n\nThe \"tenant-ready\" release. Multi-tenant admin, permission grants, safety profiles, and a full setup wizard.\n\n### Added\n- `aither setup` interactive setup wizard with hardware detection, model selection, identity config\n- Strata storage backend: SQLite WAL persistence for conversations, memories, knowledge graphs\n- CLI test runner: `aither test` with auto-discovery and parallel execution\n- Permission grants system for MCP tool access control\n- MCP account management tools\n- LLM provider auto-detection for Ollama and vLLM endpoints\n- Apache-2.0 LICENSE file\n- Elysium desktop sync module\n- Comprehensive CLI test suite (493 tests)\n- Strata storage test suite (972 tests)\n- LLM provider tests (82 tests)\n\n### Changed\n- CLI expanded: `aither setup`, `aither test`, `aither bugreport`, `aither doctor`\n- Strata module rewritten as full local-first storage engine\n- Server startup includes Strata initialization\n- README rewritten with clearer quickstart and architecture docs\n\n### Fixed\n- Dead documentation links\n- Python 3.11 compatibility issues\n- Ruff per-file-ignores configuration\n- Identity provisioning edge cases\n- Docker node-gyp native dependency builds (python3 + build tools)\n\n## [0.4.0] - 2026-03-13\n\nThe \"own your AI\" release. Self-hosted agent OS for people who don't want their data on someone else's servers.\n\n### Added\n- Muse agent identity (creative/artistic generation)\n- Port 8120 to vLLM scan for ExoNodes discovery\n- Public roadmap with milestone-based porting schedule\n- Competitive positioning: self-hosted alternative to cloud-locked AI appliances\n\n### Changed\n- LLM router: compute-aware effort routing (replaces effort-level context gating)\n- README rewritten for sovereignty-first messaging\n- Package description updated\n- Promoted from alpha to stable release\n\n### Fixed\n- Version string consistency between pyproject.toml and __init__.py\n- vLLM port scanning now includes port 8120 (ExoNodes)\n\n## [0.3.1] - 2026-03-09\n\n### Added\n- GraphMemory: SQLite knowledge graph with Ollama embeddings and hybrid search\n- NeuronPool: auto-fire pre-LLM data gathering agents\n- NanoGPT: pure Python char-level transformer with LoRA fine-tuning\n- Safety gates: IntakeGuard (input), LoopGuard (recursion), Sandbox (code exec)\n- Event system: EventEmitter with chat/tool/forge event types\n- Builtin tools: identity-based tool selection\n- ServiceBridge: auto-discovery of AitherOS services\n- Streaming: chat_stream with safety gate integration\n- Auth middleware: Bearer token authentication\n- CLI: `aither init` and `aither serve` commands\n\n### Changed\n- Wired 5 previously disconnected modules into agent loop\n- 522 total passing tests (up from 85)\n\n## [0.3.0] - 2026-03-07\n\n### Added\n- Clean-room agent development kit\n- Multi-backend LLM providers (Ollama, OpenAI, Anthropic)\n- AitherAgent class with @tool decorator\n- SQLite conversation memory\n- OpenAI-compatible server (`aither-serve`)\n- 16 agent identities as package data\n- MCP bridge to mcp.aitherium.com\n- Privacy-centric opt-in telemetry\n- Bug reporting CLI and API\n- FastAPI server with OpenAI-compatible endpoints\n- Hardware auto-detection (5 tiers, 11 profiles)\n- Fleet orchestration and multi-agent coordination\n- A2A mesh protocol\n- Federation protocol for cross-instance agent dispatch\n\n### Initial release\n- 85 passing tests\n- Apache-2.0 license\n"}, "9c6f6fb160e7b9b4fc276902be60bf38179cb3f0": {"t": 1785748165.5915291, "n": 91177, "h": "839ce04402f4eba2", "c": "# Changelog\n\nAll notable changes to aither-adk will be documented in this file.\n\n## [2.27.0] - 2026-07-18\n\n### Added — Moonshot Kimi K3 provider (DeepSeek-equivalent cloud backend)\n\n- **New `moonshot` provider** across the LLM router: OpenAI-compatible\n `https://api.moonshot.ai/v1`, default model `kimi-k3` (131K context default, up to 1M;\n tool calls, JSON mode, structured output). Wired everywhere `deepseek` is:\n - `LLMRouter(provider=\"moonshot\")` + `_COMPAT_URLS`/`_COMPAT_MODELS`/model tiers\n (small/medium/large → `kimi-k3`)\n - Config: `moonshot_api_key` field (env `MOONSHOT_API_KEY`), saved-config load,\n provider-keys map (`provider_keys.json` key `moonshot`)\n - Auto-detect chain: tried after DeepSeek (config key, then env key)\n - Hybrid reasoning backend: `AITHER_REASONING_BACKEND=moonshot` for effort 7+\n - Reasoning tiers: `backend: moonshot` (alias `kimi`) via new `MoonshotBackend`\n - CLI: `adk backend set moonshot --api-key …`, `adk backend list` shows moonshot\n- No breaking changes; DeepSeek and all other providers unchanged.\n\n## [2.26.0] - 2026-07-16\n\n### Added — Aeon group-chat web UI pack + POST /aeon/stream SSE endpoint\n\n- **New `aeon` UI pack** (`adk/webui/packs/aeon/index.html`) — group chat for multi-agent\n discussion. Mirrors the `llamacpp` pack's auth + SSE pattern (bearer from URL fragment #k=),\n with a preset selector (balanced/creative/technical/security/minimal/duo_code/research) and\n distinct colored agent messages. Synthesis block highlighted. Agent content is HTML-escaped\n before render. Fully self-contained HTML+CSS+JS, light+dark, no external assets. (No\n temperature/max-tokens controls — Aeon runs a fixed multi-agent round and doesn't thread\n per-turn generation params, so exposing them would be a no-op.)\n- **POST /aeon/stream SSE endpoint** — accepts body {message, preset?, agents?, rounds?,\n session_id?}, emits SSE with: `session_start` (participants + orchestrator), `agent_message`\n per non-orchestrator, `synthesis` (if enabled), `error` on exception, `complete` (total tokens\n + latency). Mirrors /chat/stream structure — like every streaming endpoint here it is gated by\n the server auth middleware **when `AITHER_SERVER_API_KEY` is set** (a local `adk up` with no key\n is unauthenticated by design, same as /chat/stream).\n- **Default UI pack flipped to `llamacpp`** — `resolve_ui_pack_name()` now returns `llamacpp`\n instead of `console` when no env/config is set (new users see the clean chat, not the admin\n SPA; existing users with AITHER_AGENT_UI or saved config unaffected).\n\n### Added — swappable agent web UI packs (llama.cpp-style built-in chat)\n\n- The page an agent serves at `/` is now a **swappable UI pack**, so you can\n drop in, test, and deploy different chat frontends without touching the agent.\n Select with `adk ui set <name>` (persists to `~/.aither/config.json`) or the\n `$AITHER_AGENT_UI` env; `adk ui ls` lists packs, `adk ui path` shows what\n resolves.\n- **New built-in `llamacpp` pack** — a clean, self-contained llama.cpp-style\n chat UI (centered conversation, streaming token-by-token with a cursor, a\n Stop button, a settings drawer for temperature / max-tokens that is actually\n honored end-to-end — `/chat/stream` now forwards them to the provider —\n minimal safe markdown, light+dark). Hits the agent's own gated `/chat/stream`\n with the bearer read from the URL fragment (never sent in the URL). Built-ins\n also include `console` (the full admin SPA, still the default) and `minimal`.\n- **Drop-in custom packs**: a UI pack is just a folder with an `index.html` in\n `~/.aither/ui-packs/<name>/` (override via `$AITHER_UI_PACKS_DIR`) — no rebuild.\n- Resolution is **fail-soft**: an unknown/missing pack falls back\n console → minimal, so `/` is never blank.\n\n### Added — MCP-gateway-first platform access + mesh A2A trust\n\n- **Gateway-first platform access** (`adk/client/_gateway_mcp.py`, `GatewayMCPClient`):\n a self-hosted agent connects its MCP **client** to `mcp.aitherium.com` authenticated\n as the OWNER (device-flow / PAT / ACTA token), RBAC-scoped to that identity — so it can\n use platform tools + `get_secret` **whether or not the node runs local AitherOS\n services**. Fail-closed: no token → client disabled, the agent still runs. Wired into\n the server lifespan (`_connect_gateway_mcp`).\n- **`genesis` LLM backend**: `adk backend set genesis` points the agent brain at a local\n Genesis (`http://localhost:8001/v1`, model `workflow`) — the 5090/controller pattern\n where inference is served by the local fleet, not the cloud gateway or a raw vLLM.\n- **`adk mesh` command**: `adk mesh onboard` drives the Conductor 5-step onboard\n (`/v1/mesh/onboard` → registers this node's public key, returns the `endpoint:mesh`\n trust token); `adk mesh ls` lists peer nodes/agents (AitherMesh `/nodes` + Directory)\n with their A2A cards + invoke URLs for discovery.\n\n### Security — A2A inter-agent trust enforcement (fail-closed)\n\n- **`adk/a2a_trust.py`** (new): Ed25519 signature verification + trusted-key check\n (`AITHER_A2A_TRUSTED_KEYS`), opt-in via `AITHER_A2A_REQUIRE_TRUST` (default `false` for\n backward compatibility; `audit` logs untrusted, `true` rejects).\n- **Fixed a fail-OPEN in the inbound A2A handler** (`adk/a2a.py`): the trust check\n previously ran *only when signature headers were present*, so an unsigned request\n bypassed `AITHER_A2A_REQUIRE_TRUST` entirely. Now genuinely fail-closed — required mode\n with no/invalid signature → `403`.\n- **Sovereign template no longer scaffolds `verify=False`**: the generated\n `services/llm.py` now uses the project's own `tls_verify()` policy helper for every\n provider call instead of hardcoding disabled TLS verification.\n- **`adk/_tls.py`**: disabling verification via `AITHER_TLS_VERIFY=false` now emits a\n one-time loud warning (was silent) — the escape hatch stays, the silence goes.\n\n## [2.25.1] - 2026-07-15\n\n### Fixed — self-hosted OpenAI-compatible backends honor a custom base URL\n\n- `adk backend set llamacpp --base-url http://localhost:8090/v1 --model <id>` (and\n `vllm`/`lmstudio`, and the `AITHER_LLM_BASE_URL` env) now actually point the agent\n at YOUR server. Previously the base URL was saved but never read, so an explicit\n `--backend llamacpp` fell back to the provider's public API default (openai.com).\n New `Config.llm_base_url` field + saved-config `inference_url` wiring; the explicit\n backend path in `LLMRouter` now passes it for the local OpenAI-compatible family.\n This is what lets a self-hosted node run its OWN local model (e.g. a PrismML\n Bonsai llama.cpp) as the agent brain instead of ollama/cloud.\n\n## [2.25.0] - 2026-07-15\n\n### Added — node_bootstrap tool pack: bootstrap LLM inference on any hardware\n\n- **New bundled tool pack `node-bootstrap`** (`adk/toolpacks/node_bootstrap/`):\n seven agent tools — `node_detect_hardware`, `node_resolve_recipe`,\n `node_plan_deployment`, `node_apply`, `node_enroll`, `node_register_backend`,\n `node_verify` — plus a CLI shim (`python -m adk.toolpacks.node_bootstrap\n detect|resolve|plan|apply|enroll|register|verify`). Detect a box's hardware,\n resolve a deployment recipe, actually deploy the inference engine (docker\n compose / native / delegate), enroll with a control plane, register the\n backend, and live-verify a completion.\n- **9 hardware recipes** shipped in the wheel: `cpu-1bit-llamacpp`, `cpu-ollama`,\n `cuda-vllm-8gb/24gb/40gb`, `cuda-dual-stack-32gb` (32GB-card co-resident\n stack), `unified-memory-vllm` (DGX/Grace-class), `metal-ollama` (native —\n Docker on macOS has no Metal passthrough), `cloud-api` fallback. Resolution\n scores hardware against min/max VRAM bands, RAM, cores and unified memory;\n ties prefer higher tiers and self-contained recipes over fleet delegates.\n- Security posture: enrollment and backend registration are fail-closed\n (missing token/URL → error dict, never anonymous, never `verify=False`);\n tokens are redacted in outputs; all endpoints come from args/env\n (`AITHER_CONTROL_PLANE_URL`, `AITHER_GENESIS_URL`, `AITHER_AUTH_TOKEN`).\n\n## [2.24.0] - 2026-07-14\n\n### Changed — sync-family consolidation\n\n- **Removed dead code:** `adk/sync.py` (528 lines) deleted — it was unreachable,\n shadowed by the `adk/sync/` package (`import adk.sync` always resolved to the\n package; nothing loaded the module by path — verified).\n- **Grouped the sync domain modules into the `adk.sync` package:**\n `brain_sync`→`adk.sync.brain`, `files_sync`→`adk.sync.files`,\n `lockbox_sync`→`adk.sync.lockbox`, `secrets_sync`→`adk.sync.secrets`,\n `session_sync`→`adk.sync.sessions`, `settings_sync`→`adk.sync.settings`,\n `sync_watermark`→`adk.sync.watermark`. All in-tree importers updated. No\n back-compat shims were left because **zero external/monorepo code imported the\n old root paths** (verified) — a clean move, not a shimmed one. **Breaking only\n for code that imported `adk.<name>_sync` directly** (none known).\n\n### Docs — MCP module layering clarified\n\n- `adk.mcp` (enterprise AitherOS gateway client: auth/billing/token-tracking) and\n `adk.core.mcp` (minimal generic JSON-RPC adapter for any MCP server) are\n DISTINCT layers, not duplicates — each now carries a LAYER/ROLE/See-also\n docstring so they are not mistakenly consolidated.\n\n## [2.23.2] - 2026-07-14\n\n### Fixed — CompletionGate no longer false-fails on correct results\n\n- **Tolerant judge JSON extraction.** The LLM judge parsed its reply with `json.loads(text[first\"{\":last\"}\"])`, which raised `JSONDecodeError` (\"Extra data\") whenever the model appended prose after the verdict or emitted a second object — and `verify()` turned that into a fail-closed verdict, so a genuinely completed task reported `unverified`. New `_first_json_object()` uses `JSONDecoder.raw_decode` from the first `{` that begins a valid object and ignores trailing content (prose, markdown fences, extra objects). A judge that answers with no parseable JSON still fails closed (never a soft pass), now with a clear reason. The same extractor backs `_derive_criteria`.\n- **Bare code-like tokens are hard-checked.** A natural criterion like `output contains ADK_AUTO_OK` (no quotes) previously fell through to the judge; `hard_checks` now also matches bare identifier/code shapes (contains an underscore, or 4+ CAPS/digit chars), with a stopword guard so a descriptive uppercase word (`OUTPUT`, `CONTAIN`) never becomes a false required token. Quote a token to force it regardless.\n- +9 tests (24 total), including the exact trailing-data regression.\n\n## [2.23.0] - 2026-07-14\n\n### Removed — dead modules and internal integrations\n\n- **`adk.swarm`, `adk.provisioning_tools`, `adk.session_sync_integration`, `adk.addon_metering`** — four orphan modules with zero public usage have been deleted. These were internal prototypes kept for legacy monorepo consumers; public packages have not imported them since 2.21.0 (tracked as D-348). **Breaking for anyone directly importing these modules** (import will fail). They are no longer exempt from the orphan-module check.\n- **`adk.aither_bridge`** — the AitherOS-internal IRC ↔ chat gateway bridge is no longer shipped in the public aither-adk package (stripped at sync time). All public importers already degrade behind `except ImportError` (in `adk.server` and `adk.builtin_tools`), so public packages remain unaffected. The module stays in the private AitherOS monorepo for internal use.\n- **Identity document trim** — removed internal agent names from `adk/identities/aither.yaml` delegation guidance, replacing with role-generic wording (\"delegate code review, refactoring, security analysis, performance and testing to specialist agents when available\"). Effort-tier routing philosophy remains unchanged and is product design, not a leak.\n\n### Changed — genericized customer/product names in help text\n\n- CLI help/examples no longer name specific customer deployments (`--scope` template list and `deploy agent` name example use generic placeholders). Functional deploy paths are unchanged.\n\n## [2.22.1] - 2026-07-14\n\n### Fixed — real bugs surfaced by reviving the public CI (red since Jul 5)\n\n- **TLS**: bundled toolpacks (arc-brainpack, cloudflare) disabled certificate\n verification on gateway/vault calls; all now route `adk._tls.tls_verify()`\n (verified by default, internal CA bundle when present).\n- **`system_prompt` contract**: bundled-pack `[PACK DIRECTIVES]` were appended\n even to an explicit `system_prompt`; an explicit prompt now wins wholesale.\n- **Headless secrets**: `_derive_key()` used `os.getlogin()`, which raises\n `OSError` without a controlling terminal (CI, daemons, containers) — falls\n back to `getpass.getuser()` (same value on interactive boxes, so existing\n secret files keep decrypting).\n- **`pack_verifier`**: missing `cryptography` crashed with `UnboundLocalError`\n in the exception handler instead of failing closed; now degrades gracefully\n (and `cryptography` was added to the dev extra so CI tests the real path).\n- **`fleet_manager._kill_pid` (POSIX)**: reported success immediately after an\n asynchronous SIGTERM and never reaped children (zombies read as alive); now\n polls, escalates to SIGKILL, and reaps.\n- **Identity**: `aither` regained the `creative` tool category (dropped\n accidentally in a 2.7.0 WIP sweep).\n\n## [2.22.0] - 2026-07-14\n\n### Changed — internal toolkit relocation\n\n- **`adk.platform` relocated** — the internal aither-platform toolkit (ComfyUI/image-gen\n pipelines, fleet agent clients, parallel CLI/UI/tools stack) has been moved out of the\n adk SDK tree to the standalone internal `aither-platform` package at\n `AitherOS/agents/aither_platform/`. The public package remains unaffected (it already\n excluded `adk.platform` since 2.21.1). Internal AitherOS agent builds now install\n `aither-platform` separately. `adk platform` falls through to the same \"not available\"\n message on public installs; internal builds work via the relocated package.\n\n### Removed — backward-compat re-export deleted\n\n- **`adk.gateway` module removed** — the backward-compatibility re-export of `GatewayClient`\n from `adk.gateway` has been removed. Use `from adk.client import GatewayClient` instead\n (the established public API since 2.21.0). All internal importers have been updated.\n\n## [2.21.1] - 2026-07-14\n\n### Removed — internal toolkit and license-gated product no longer ship publicly\n\n- **`adk.platform`** — the internal aither-platform toolkit (ComfyUI/image-gen\n pipelines, fleet agent clients, a parallel cli/ui/tools stack) is no longer\n included in the public repo or the PyPI wheel. It was internal-only code that\n had been riding along since the aither-platform merge. `adk platform` now\n reports \"Platform toolkit not available\" on public installs; internal AitherOS\n builds are unaffected.\n- **`adk.formbridge`** — the FormBridge form-automation product is license-gated\n (PROFESSIONAL+); its implementation no longer ships in the free package. All\n import sites (`builtin_tools`, `server` route mount) already degrade behind\n `except ImportError`, so nothing else changes behavior.\n- Product test suites (`test_formbridge.py`, `test_encounter_keying.py`,\n `test_flow_tools.py`) are excluded from the public sync alongside them.\n\n## [2.21.0] - 2026-07-14\n\n### Added — Completion gate: verified-or-retried task execution\n\n- **`adk.gate.CompletionGate`** — a composable wrapper that closes the \"silent\n success\" gap: an agent's ReAct loop returns whatever the model produced and\n calls it done (`finish_reason = stop/max_steps/length`), which is a *liveness*\n signal, not a *completion* one — so an agent can \"succeed\" while doing nothing.\n `CompletionGate` runs the agent, verifies the result against acceptance\n criteria, retries with the failure fed back, and returns the response with\n `finish_reason` set to `verified` or an honest `unverified`. Never a false pass.\n - **Hard-first, un-soft-passable checks** (`adk.gate.hard_checks`): a filesystem\n path a criterion says must exist is checked on disk; a required quoted token\n must appear in the output. Only genuinely subjective criteria fall through to\n an optional LLM judge (reuses the agent's own model), which **fails closed**\n if unavailable.\n - **`gated_run(agent, task, criteria=..., max_retries=2)`** — one-shot helper.\n - `auto_criteria` (LLM writes the machine-checkable definition-of-done from the\n task), a `verifier=` override for custom checks (tests-pass / HTTP probe), and\n a `requires_action` short-circuit so it never fights human-approval pauses.\n - Compose it onto any agent exposing `run(task) -> resp{.content, .finish_reason}`\n (`AitherAgent`, `core.Agent`, or a thin adapter over a remote dispatch).\n - Exported from the top-level package: `from adk import CompletionGate, gated_run`.\n\n## [2.19.0] - 2026-07-12\n\n### Added — Eve agent importer + durable A2A tasks + canonical agent discovery\n\n- **`adk pack import <eve_agent_path>`** — convert external Eve agents to AitherADK packs\n (from GitHub or local filesystem). Maps Eve structure (instructions → system prompt,\n agent.ts → agent.yaml, skills/*.md → skills, connections/*.ts → MCP, tools/*.ts →\n tools/node verbatim) to AitherADK format with a single command. Eve agents (compiled\n manifest format, schema v35) become installable packs compatible with `adk pack install`.\n- **Durable A2A task lifecycle (`TaskManager` backed by FileStore)** — agent-to-agent\n tasks now survive container restarts. Tasks persist as append-only JSONL\n (`.adk/tasks.jsonl`); in-memory dict for fast access; `create_task()` / `update_status()` /\n `add_artifact()` / `add_message()` all save to disk. Google A2A v0.3.0 spec compliant.\n- **Canonical agent card discovery** — `A2AServer.mount()` now exposes:\n - `GET /.well-known/agent-card.json` — Agent Card (canonical; contains agent identity,\n capabilities, skills, endpoints)\n - `GET /.well-known/agent.json` — Agent Card (legacy, redirects with 308 to canonical path)\n - `POST /a2a` — JSON-RPC 2.0 for message send/task lifecycle\n - `GET /a2a/tasks/{id}/subscribe` — SSE streaming task updates\n- **Four built-in agent packs now complete and catalogued**:\n - `hermes` — architecture review & trade-off analysis\n - `openclaw` — web research & source verification\n - `claude-code` — feature development & debugging\n - `analyst` — structured data inference & anomaly detection\n All discoverable via `adk packs` / `adk install pack:<name>`.\n- **Tolerant tool-call JSON parsing** — malformed tool JSON (extra properties, missing\n name/arguments, string-encoded nested args, missing keys) is now gracefully degraded\n instead of silently failing. `_parse_tool_json()` accepts both `\"arguments\"` and\n `\"parameters\"` keys, coerces string-encoded args, and skips unparseable calls instead\n of crashing the turn.\n\n### Known limitations\n\n- Eve agent importer: remote GitHub fetching not yet implemented (local paths only).\n- Durable task store: currently JSONL (suitable for thousands of records, not millions).\n\n## [2.18.0] - 2026-07-10\n\n### Added — AitherShell command center + Claude Code session management\n\n- **`aither sessions`** — Claude Code session manager: interactive full-screen\n browser (type-to-filter, transcript preview, deep full-text `search`, resume\n **in the current terminal** or as Windows Terminal tabs), crash guard\n (`guard install` = at-logon watchdog that snapshots live sessions and\n auto-restores the whole set after a terminal crash), and `ingest [--watch]`\n (incremental, secret-guarded sync of session conversations into the local KB\n / CompanyBrain via the standard ingest pipeline).\n- **`aither hq`** — command-center dashboard: per-service fleet health, LLM\n queue (depth/VRAM/models), Pulse alerts, live session count, inbox unread —\n auto-refreshing, with one-key jumps into chat, sessions, inbox, agents,\n brief, watchtower, and docker recovery.\n- **`aither inbox`** — unified queue over CommCore mail, Relay\n mentions/notifications, and Pulse alerts; open/mark-read/DM-reply from the\n terminal. Every source degrades independently.\n- **`aither agents`** — roster console + `agents ask -e <1-10> <agent> <q>`\n (effort-tiered ask via Genesis, with automatic `/chat/stream` fallback when\n the `/agent/sync` execute gate rejects host callers) + forge dispatch\n inspection + routine health.\n- **`aither palette`** — universal fuzzy picker across actions, sessions, and\n services; **`aither brief`** — renders Atlas's executive briefing in the\n terminal; **`aither watch`** — fleet watchtower with named wedge-signature\n detection (docker-WSL wedge, LLM-queue stall, crash-loop uptime regression)\n and optional docker auto-recovery.\n- All fleet reads go through a shared fail-soft `FleetClient` (internal-CA\n TLS via `adk._tls`, dashboard-grade timeouts, per-source degraded states).\n- Node shell-cli (`@aitherium/shell-cli` 1.13.0): `aither\n sessions|hq|inbox|palette|brief|watch|agents|docker` now pass through to the\n Python shell instead of being intent-classified as chat prompts.\n\n## [2.17.1] - 2026-07-05\n\n### Fully self-service mesh join — no customer key handling\n\n- **Auto-join headscale off the onboard response.** The conductor now auto-issues a\n headscale pre-auth key for NAT'd nodes and returns it in the `/v1/mesh/onboard`\n response; `mesh.join` consumes it and brings up the tunnel automatically. A customer\n runs one command — they never mint, fetch, or set a headscale key. Explicit\n `--headscale-key`/`AITHER_HEADSCALE_AUTH_KEY` still take precedence.\n- **Security:** `_tailscale_up` now redacts the auth key from all logs and exceptions\n (a failed `tailscale up` previously echoed the full argv, leaking the key into logs).\n\n## [2.17.0] - 2026-07-05\n\n### Added — Headscale mesh transport + local Qdrant + AitherConnect onboarding\n\n- **Headscale NAT-friendly mesh transport.** When raw WireGuard UDP:51820 is not\n viable (customer boxes behind NAT, corporate firewalls, CGNAT), `adk mesh join\n --headscale` routes the mesh tunnel through Tailscale's Headscale control plane.\n The overlay IP is still Conductor-assigned (10.77.0.0/16); Headscale provides\n only the transport layer. Configured via `AITHER_MESH_TRANSPORT=headscale`,\n `AITHER_HEADSCALE_URL` (default: https://headscale.aitherium.com), and\n `AITHER_HEADSCALE_AUTH_KEY` (pre-generated key). Automatic fallback to raw\n WireGuard if Headscale setup fails.\n- **Local Qdrant vector DB provisioning.** `adk stack` now provisions Qdrant\n locally when `AITHER_VECTOR_DB=qdrant` is set, enabling offline-first RAG\n without external dependencies. Tiered: local Qdrant in-fleet, cloud Nexus\n for enterprise multi-tenant deployments.\n- **Conductor URL public fallback.** `adk mesh join` resolves the internal\n `aitheros-conductor:8193` hostname; if unreachable (e.g., self-hosted nodes),\n it falls back to the public `conductor.aitherium.com:8193` endpoint,\n eliminating bootstrap configuration friction.\n\n### Documentation\n\n- **Self-hosting runbook.** New `docs/SELF_HOSTING.md` covers deploying AitherOS\n on customer infrastructure with Qdrant + mesh + AitherConnect onboarding.\n- **Mesh transport constraints documented.** Raw WireGuard requires public\n UDP:51820 endpoint (cloud instances, dedicated hardware); Headscale for\n NAT'd networks. Noted in CLI help and module docstrings.\n\n## [2.15.0] - 2026-07-04\n\n### Added — built-in web admin console + portal-profile settings sync\n\n- **Web admin console.** The page `adk up` opens (served at `/` and `/ui`) is now a full,\n self-contained console — Chat plus six admin tabs: **Backend** (view/switch LLM provider,\n set keys, test connection), **Packs** (list/enable/disable/reload tool packs),\n **Sessions** (browse/delete conversations), **Logs** (redacted agent-log tail),\n **Graph** (knowledge-graph search/neighborhood/stats), and **MCP** (add/remove external\n MCP servers). No build step, no CDN — a single packaged HTML asset. The original minimal\n streaming chat page stays at `/chat` for back-compat.\n- **Admin API (`adk/admin_api.py`).** ~20 endpoints under `/admin/*`, all bearer-gated by the\n existing middleware (never in `_skip_auth_paths`). Operates on the live agent — LLM backend\n swap (`LLMRouter.switch_backend`), pack reload, and MCP registration take effect without a\n restart.\n- **Settings sync (`adk/settings_sync.py`).** The user's portal profile is the source of\n truth: on startup the agent pulls `preferences.adk` and applies it over the local\n `~/.aither/config.yaml` cache; admin-console edits push a debounced snapshot back to\n `PUT /api/settings/preferences`. Provider API keys and MCP auth headers are device-local\n secrets and are never included in the pushed snapshot. Opt out with `AITHER_SETTINGS_SYNC=false`.\n\n### Security\n\n- **Timing-safe token comparison.** The server bearer check now uses `hmac.compare_digest`\n (the token is reachable over the public tunnel; a byte-wise `!=` leaked it to timing attacks).\n- **MCP add is SSRF-guarded + confirmed.** Adding an external MCP server is a two-step\n prepare→confirm flow; URLs resolving to loopback / private / link-local / metadata addresses\n are rejected (override with `AITHER_ALLOW_PRIVATE_MCP=1` for trusted local servers).\n- **Config editor is allowlisted.** `PATCH /admin/config` writes only safe scalar prefs and\n hard-denies fields that could execute code or redirect traffic at startup (e.g.\n `aither_toolpack_dirs`, backend base URLs).\n- **Log tail is scrubbed.** Bearer tokens, `#k=` fragments, and `sk-`/`aither_sk_` keys are\n redacted from the log-tail endpoint; the tunnel log is excluded.\n\n## [2.14.8] - 2026-07-04\n\n### Added — `adk up` for normal humans (no API key, no prereqs, live chat)\n\n- **Hosted-brain default — no API key required.** When there's no local model and no\n provider key, `adk up` signs in (device-flow) and routes inference through the gateway\n using the portal token (`AITHER_LLM_BACKEND=gateway` + bearer). A non-technical user\n never needs to obtain or paste an LLM API key. Explicit `--provider`/local model still\n take precedence; unattended `--yes` with no token/backend fails cleanly (exit 3).\n- **cloudflared auto-download.** If the tunnel binary is missing, `adk up` downloads the\n right platform asset to `~/.aither/bin` (SHA256-verified against the release; opt out with\n `AITHER_CLOUDFLARED_REQUIRE_CHECKSUM=0`) instead of exiting with an install hint.\n- **Built-in streaming chat page.** The agent now serves a self-contained chat UI at `/`\n (and `/chat`) with a live \"thinking…\" indicator and token-by-token streaming — so a human\n has somewhere to talk to it with immediate feedback instead of a blank wait. `adk up` opens\n it locally and passes the callback bearer in the URL fragment (`#k=…`), so `/chat/stream`\n stays authenticated (not an open proxy) with nothing to paste.\n- **Human-readable failures.** `adk up` errors now carry a plain-English `next_action`\n (kept machine-readable in `--yes` JSON).\n\n### Fixed\n\n- `adk up` chat link pointed at a non-existent `/settings/agent` route; it now points at the\n agent's own streaming chat page.\n\n## [2.14.7] - 2026-07-04\n\n### Added\n\n- **`adk up` — one command to run a persistent, fleet-connected agent.** Collapses\n the scattered onboarding surface (`host`/`quickstart`/`connect`/`login`/`setup`)\n into a single command that starts `aither-serve` (single agent, identity `aither`\n by default), opens a Cloudflare quick-tunnel for fleet reachability, registers with\n the portal, and installs a reboot-persistent autostart entry (Windows Task Scheduler\n / systemd-user / launchd). Detaches by default so the terminal is freed; `--foreground`\n blocks. Reuses the existing device-flow login, cloudflared discovery, provider-key\n store, and `_preflight_check` backend detection.\n- **Fully non-interactive path for autonomous agents.** `adk up --yes` (or any\n non-TTY invocation) takes ZERO prompts — everything resolves from flags, env, and\n saved config — and emits a single machine-readable JSON status line with meaningful\n exit codes (`0` ok, `2` bad-input, `3` no-backend, `4` tunnel-missing, `5` register-failed).\n Degrades to local-only (warn, not fail) when a portal token is absent unless\n `--require-register` is set.\n- **`adk down` / `adk status --json` companions.** `down` stops the agent + tunnel,\n best-effort deregisters, and removes the autostart entry; `status` reports the running\n agent (liveness + `/health` + tunnel + registration), with `--json` for agents/CI.\n- **`adk stack`** — the previous `adk up` behaviour (supervise the Room + Ollama consumer\n stack) now lives here; `adk up` is the connected-agent command.\n\n### Internal\n\n- New `adk/agent_daemon.py`: detached process spawn, status file\n (`~/.aither/adk-up.json`), pid liveness/teardown, and cross-platform autostart install.\n\n## [2.14.6] - 2026-07-04\n\n### Added\n\n- **`adk.preflight` — self-bootstrap capability probe.** `run_preflight(agent,\n spec)` wakes an agent up knowing what it is actually plugged into: a bounded\n (1.5s) parallel `LivenessProbe` over the router's resolved backends\n (primary/reasoning), the embeddings winner, remote MCP tools, the structured-ML\n / `/ml/teach` endpoints, and voice — returning an honest `CapabilityReport`.\n Status vocabulary distinguishes reachability from entitlement\n (`OK`/`MISSING`/`UNREACHABLE`/`AUTH`/`TIER_DENIED`/`UNSUPPORTED`), a hosted slot\n never prints a bare `OK`, and a required-but-unsatisfied slot aborts before the\n loop instead of blind-firing. Headless task/rules resolution + an allowed-roots\n policy binding (`builtin_tools.set_allowed_roots`, which resets the memoized\n cache so the override actually enforces).\n- **Multimodal (image) messages.** `Message.content` now accepts an OpenAI-style\n content-part list (`str | list[dict]`), passed through verbatim to the\n OpenAI-compatible body (gateway / vLLM / OpenAI). New `adk.llm.multimodal`\n helpers — `image_message()`, `image_content_parts()`, `to_image_url()` — build\n the parts and encode local image bytes/paths into `data:` URIs.\n- **Real vision preflight probe.** The vision slot is no longer unconditionally\n `UNSUPPORTED`: it sends a discriminable image (a solid red square) and requires\n the model to name the color, so a blind/text-only model cannot false-pass. `OK`\n only on a real round-trip; `AUTH` on 401/403; `UNSUPPORTED` when there is no\n OpenAI-compatible provider or the model cannot see the image.\n\n## [2.14.5] - 2026-07-04\n\n### Added\n\n- **`adk.reasoning.mcts` — generic Monte-Carlo Tree Search library.** A reusable\n MCTS engine (ported from the internal `UnifiedMCTS`, with all internal couplings\n removed) that any agent can drive over its own environment:\n - `MCTSEnvironment` protocol (`get_state_hash` / `get_actions` / `step` /\n `evaluate` / `clone`) — implement it and search.\n - Three **optional, default-off** model seams — `TransitionModel`, `PolicyModel`\n (PUCT priors), `ValueModel` — so a learned world-model / policy / value can be\n injected without touching the core; with all three `None` the engine is a plain\n UCT search.\n - `ObservedTransitionModel` adapter — a cheap online lookup world-model (hit =\n exact stored transition; miss = identity + negative \"unknown\" reward +\n `is_uncertain`, biasing exploration toward learning), with JSONL save/load for\n cross-run reuse.\n - `UnifiedMCTS(config).search(env)` returns an `MCTSResult` (best action, value,\n confidence, principal-variation path) and an optional `MCTSTrace`\n (visit-distribution policy target) for distillation.\n- **Self-bootstrapping agent machinery.** Build an agent from a declarative spec\n instead of bespoke wiring:\n - `adk.bootstrap.build_agent_from_spec(path) -> (AitherAgent, RunCtx)` — resolves\n prompts, activates required/optional tool packs, and carries a `memory_map`\n (a `node_type -> (role, tier)` override for memory writes, never a gate).\n - `adk.prompts.PromptBridge` — resolves an agent's prompt bindings from files\n (relative to the spec) or `AitherPrompts` dot-keys (optional import).\n - `adk.packs.PackActivator` — eager activation of installed tool packs by name\n over `register_tool_packs`; a required pack that registers zero tools raises\n `PackUnavailable`, optional packs soft-degrade.\n - `python -m adk.bootstrap.generic_agent <spec.yaml>` inspects a spec (resolved\n name, activated packs + tool counts, prompt keys, memory map) without running a\n live loop.\n- **`deep_research` tool pack** — a full multi-engine web-research workflow as\n `dr_*` agent tools (keyless search, page fetch + cache, site mirror, a citable\n findings knowledge graph, and markdown/PDF/DOCX report generation), activatable\n by name via the tool-pack loader.\n\n## [2.14.4] - 2026-07-04\n\n### Added\n\n- **`structured_ml` tool domain** — zero-shot structured-data inference for agents,\n backed by TabFM (tabular classification/regression) and TimesFM (time-series\n forecasting). Three tools:\n - `tabular_classify(support_rows, target, query_rows)` — classify rows from a\n labeled support set, in-context (no training step; up to 10 classes).\n - `tabular_regress(support_rows, target, query_rows)` — predict a numeric target.\n - `timeseries_forecast(series, horizon)` — forecast future values of a series.\n\n The tools POST to a structured-ML inference service resolved from\n `AITHER_STRUCTURED_ML_URL` (default `http://localhost:8192`), reject non-http(s)\n URLs, and summarise oversized responses to protect the agent's context.\n- **`Capability.STRUCTURED_INFERENCE`** capability token and a new **`analyst`**\n identity/pack that ships the `structured_ml` domain by default. The domain is\n opt-in — only identities that enable the `structured_ml` category get the tools.\n\n## [2.14.3] - 2026-07-03\n\n### Fixed\n\nAll four bugs below were found by actually booting `adk run` against a real\nchat model and driving a real tool-calling request end-to-end (not inferred\nfrom reading the code) — each one silently broke the \"local agent uses its\nconfigured model and its tools actually work\" path.\n\n- **`AITHER_MODEL` / `config.model` silently ignored by the OpenAI-compatible\n backend**: `LLMRouter.__init__` never read `config.model` into `self._model`\n unless the caller passed `model=` directly to the constructor — which nothing\n did. Every explicit model choice (env var, config file) was dropped, the\n provider fell back to a hardcoded per-backend default (`gpt-4o-mini` for the\n `openai` backend), and an unrecognized model name got silently remapped\n server-side to the wrong backing model. Concretely: setting\n `AITHER_MODEL=aither-orchestrator` had no effect — every request actually\n went to a different model that was never meant to be called directly and\n can't reliably use tools.\n- **`OPENAI_BASE_URL` ignored for explicit `--backend`/`AITHER_LLM_BACKEND`\n selection**: `LLMRouter._auto_detect()`'s explicit-backend branch hardcoded\n `base_url=None` when constructing the provider, instead of reading\n `config.openai_base_url` (which two other call sites in the same file\n already did correctly). Any self-hosted OpenAI-compatible endpoint\n (MicroScheduler, vLLM, LM Studio) configured via `OPENAI_BASE_URL` was\n silently ignored in favor of `https://api.openai.com/v1`.\n- **Tool-call steering never engaged on default-effort chat**: the retry that\n nudges a model back into structured tool-calling when it responds with prose\n instead of a function call was gated behind `effort >= 6`. Default `effort`\n for a plain `agent.chat(message)` call resolves to `5`, so the safety net —\n built specifically for this failure mode — never fired for default requests.\n Also now forces `tool_choice=\"required\"` on the retry instead of relying on a\n text nudge alone.\n- **Every non-string builtin-tool parameter silently typed as `\"string\"` in\n the generated tool schema**: `builtin_tools.py` uses\n `from __future__ import annotations`, so `_extract_parameters()` in\n `tools.py` was reading postponed (string) type hints (`\"int\"`, not `int`)\n straight out of `fn.__annotations__`, which never matched\n `_type_to_schema`'s type-object keys and silently fell through to\n `{\"type\": \"string\"}`. Every builtin tool with an `int`/`float`/`bool`/`list`\n parameter was affected — e.g. `file_read(start_line: int, end_line: int)`\n advertised both as strings, so a model correctly calling the tool with\n `\"start_line\": \"0\"` crashed the actual implementation with\n `unsupported operand type(s) for -: 'str' and 'int'`. Fixed by resolving\n hints through `typing.get_type_hints()` instead of the raw annotations dict.\n `_coerce_arguments()` — the runtime safety net that's supposed to coerce a\n model-supplied `\"0\"` string into `0` before a tool call executes — had the\n exact same raw-`__annotations__` bug and silently no-opped for every builtin\n tool; fixed the same way.\n- **Streaming tool-loop could hang a connection forever**: `chat_stream()`'s\n fallback to the sync tool loop (tool calls can't stream token-by-token) had\n no timeout, so a model that didn't cleanly terminate a tool-calling round\n trip left the SSE connection open indefinitely with zero bytes sent. Bounded\n with a 60s `asyncio.wait_for`; on timeout the stream yields a clear message\n instead of hanging.\n\n## [2.14.2] - 2026-07-03\n\n### Fixed\n- **AitherShell session amnesia**: the REPL generated a fresh `session_id` for\n EVERY message, so conversation history never followed across turns (the\n server stored each turn under a different session). One stable id is now\n minted per shell run; `/resume` and `/new` semantics unchanged, and the\n cross-restart auto-restore of the last session now actually works.\n\n## [2.14.1] - 2026-07-02\n\n### Added\n- **Account-tied licensing via device-flow login** — `adk login` now persists the account\n license returned by the device-flow handshake to `~/.aither/license.json` (base64\n `license_key` → `{payload, signature}` envelope), so entitlements from the authenticated\n account carry into every later `adk` invocation without re-auth. Pairs with the fleet-side\n node tool-pack gate that grants packs by plan-tier rank.\n- **`can_use_formbridge` / `can_use_untether` entitlements** (PROFESSIONAL+ rank ≥ 3) backing\n the FormBridge and UNTETHER agent tool-packs.\n\n### Fixed\n- `adk` CLI: guard the optional `memory_graph` singleton (was a latent `NameError` on the\n `/stats`, `/memory`, and save paths when memory was not constructed).\n\n## [2.14.0] - 2026-07-02\n\n### Added\n- **`adk.memory_wiki` — LyraWiki-style self-managed semantic knowledge** (opt-in; nothing constructs it by default): `MemoryWiki(graph_memory)` maintains curated wiki ARTICLES as `wiki_article` nodes (title/slug, LLM-owned markdown with `[[wikilinks]]`, embedding, durable tier, `CONSOLIDATED_FROM` edges to source memories, governance-ledgered revisions).\n - `consolidate(llm, since=, budget=)` clusters unconsolidated episodic/fact nodes (embedding + keyword), drafts/UPDATEs articles via the INJECTED llm callable (`str -> str` or `messages -> str`, sync or async — no hard model dependency), cites source node ids, marks contradicted sources superseded, then DEMOTES consolidated sources to a fast-decay tier via `promote()` (represented knowledge doesn't need raw copies).\n - `lint(llm=None)` — deterministic health checks (orphan wikilinks, empty/stale articles, live-but-contradicted pairs) + an optional LLM audit pass.\n - `prune(relevance_floor=0.05, hard_delete_after_days=14)` — article relevance = tier freshness × reinforcement (reinforce-on-recall) + link-degree to LIVE memories; below the floor → reversible governance tombstone; entombed past the window → **HARD-DELETE** (the tombstone snapshot itself is purged — content and embedding irrecoverable; the true-deletion path). A node is never deleted without a tombstone first.\n - `recall(query, limit)` — article-first RAG (curated articles rank above raw nodes) that reinforces returned articles.\n- **`adk.routines` — agent heartbeat + self-programmed routines**: `RoutineStore` is a durable registry (`~/.aither/routines/{agent}.json`, `AITHER_DATA_DIR`-aware, injectable path) of cron-scheduled self-prompts `{name, cron, instruction, enabled, last_run, last_result, tags}` that rehydrates into the existing `adk.cron.CronScheduler` on `start()`. A fire runs `agent.chat(instruction)` with the agent's full toolset — the agent programs its future self with its own adk. Guardrails: `max_routines` (12), a 5-minute min fire interval per routine, a per-fire timeout, and every result ledgered (truncated) to `last_result`. Direct-callable routines (`register_direct`) fire a bound method instead of a self-prompt. Self-management tools (`routine_create/list/update/pause/resume/delete/run_now`) ship as OpenAI tool defs + handlers (`build_routine_tools` / `register_routine_tools` / `routine_tool_defs`); the handlers ONLY touch the RoutineStore — they can never modify agent config or safety-relevant settings (the leash principle).\n- **`AitherAgent(routines=True, memory_maintenance=True)`** (both default **False** → the default agent is byte-identical): `routines=True` builds the RoutineStore + scheduler, registers the self-management tools, and starts the heartbeat lazily on the first `chat()` (or via explicit `start_routines()`). `memory_maintenance=True` registers DEFAULT routines — `wiki_consolidate` (every 2 h), `wiki_lint` / `wiki_prune` / `graph_sweep` (daily) — as DIRECT method fires so memory upkeep runs even on tiny models, while staying visible/manageable via `routine_list`.\n- `TombstoneStore.purge(tombstone_id)` — the hard-delete endpoint: irrecoverably removes a tombstone snapshot and rewrites the persisted store without it (consumed by `memory_wiki.prune`).\n\n## [2.13.6] - 2026-07-02\n\n### Added\n- **Qdrant backend for GraphMemory dataplane sync — TRUE two-way, verified live.** Set `AITHER_FLEET_QDRANT_URL` and the sync uses Qdrant (`upsert` + `scroll` + filtered `search`) instead of Nexus. This is what actually delivers cross-agent memory sharing: a node is upserted with its OWN embedding under a deterministic per-(tenant,node) point id (idempotent re-push), and `fleet_pull` reliably SCROLLS a tenant's points back — the enumerate Nexus lacks (its `/search` doesn't return ingested docs and `/export` needs lancedb, which has a hard dependency conflict in the fleet). **Proven live with 2 agents:** agent-1 ingests → auto-syncs to the tenant dataplane; a fresh agent-2 in the same tenant `fleet_pull`s all of agent-1's nodes and can search them; a different tenant pulls nothing (isolation). Dimension-safe (never mixes vector dims in a collection). Local SQLite stays source of truth; best-effort, never raises.\n\n## [2.13.5] - 2026-07-02\n\n### Fixed\n- **CRITICAL — GraphMemory dataplane sync push was 100% failing.** `_fleet_push_node` sent `source_type=\"graph\"` / `content_type=\"graph_node\"`, which AitherNexus rejects as invalid enums (HTTP 500) — so NO graph node ever replicated in 2.13.4. Now sends the accepted `source_type=\"manual\"` / `content_type=\"text\"` (graph provenance is carried in `metadata.synced_from`). **Verified live** against a fleet Nexus: a 4-node ingest replicates with `pending==0` and the collection count reflects it.\n\n### Added\n- **Swarm-awareness notification** — on a successful sync batch, `GraphMemory` emits a best-effort `graph.synced` event (tenant/workspace/agent/collection/count) to `AITHER_FLEET_EVENTS_URL` so OTHER agents in the tenant/swarm can pull the fresh data and deconflict in-flight work (push-based awareness instead of polling). No-op unless the env is set.\n\n### Known limitation\n- Cross-agent **rehydration** (`fleet_pull`) is only reliable when the fleet Nexus is backed by lancedb (persistent + exportable). An in-memory Nexus stores on `/ingest` but neither its `/search` nor `/export` returns the docs — two-way read needs the persistent Nexus.\n\n## [2.13.4] - 2026-07-02\n\n### Added\n- **Memory-maintenance primitives on `GraphMemory`** (all opt-in; default behaviour byte-identical):\n - **Reinforce-on-recall**: `recall_with_activation(..., reinforce=True)` bumps `reinforcement_count`/`last_reinforced` on every RETURNED node's metadata mirror (one UPDATE per node, WAL-safe) — making the `MemoryRecord` reinforcement fields the activation scorer reads LIVE. Default `False` = zero writes.\n - **`sweep(now, archive_below=0.05, max_nodes=None, dry_run=False)`**: read-time decay enforced at rest — archives nodes past their tier TTL with freshness×reinforcement score below `archive_below` and `reinforcement_count <= 1`; `max_nodes` additionally archives the lowest-scored overflow. PERMANENT tier immune. Every archive is a reversible governance tombstone (`TombstoneStore.recover`) + a FORGET ledger entry; a node is never deleted without a tombstone. `dry_run` returns the would-archive list without acting.\n - **`promote(node_id, tier=?, role=?)`**: re-tier/re-role a node IN PLACE (tier/role columns + metadata mirror) — edges, embedding and ledger history survive; ledgers an UPDATE entry when governance is on.\n- `MutationType.UPDATE` ledger entry kind (in-place re-tier/re-role).\n- **Per-tenant dataplane sync for `GraphMemory`** — replicate the local graph to a per-tenant vector dataplane (Nexus/Qdrant) so a sovereign agent's memory is durable + rehydratable across restarts and hosting boundaries. `fleet_push_all_nodes()` (catch-up), `fleet_pull()` (best-effort semantic top-up), `fleet_sync_pending()`, and auto-sync on ingest (`_maybe_autosync` + `drain_sync()`). **On by default** (`AITHER_FLEET_SYNC=auto`; only `false/0/off` disables) with an inferred target (local Nexus `:8122` or the gateway in cloud mode) and a SEPARATE collection (`AITHER_FLEET_GRAPH_COLLECTION`, default `graph_memory`) from the KV memory sync. New `GraphMemory(tenant_id=, workspace_id=, fleet_url=, auto_sync=)` constructor args override env (required for multi-tenant host processes). Node `synced` column + schema v4 migration. TLS verify is CA-bundle-aware (`AITHER_CA_BUNDLE`) for the internal-TLS mesh. Local SQLite stays the source of truth; every sync path is best-effort and never raises into ingest.\n\n## [2.13.3] - 2026-07-02\n\n### Added\n- **Canonical self-deploying embeddings provider** (`adk.embeddings`): one embedding provider for the whole SDK so vectors are portable across scopes (platform / tenant / sovereign) — same model + dimension everywhere. Pinned to `nomic-embed-text` (768-d; the vLLM `--served-model-name` for nomic-embed-text-v1.5). Lazy, single-flight resolution chain: explicit `AITHER_EMBEDDINGS_URL` → local vLLM (`:8209` then `:8120`, HTTPS-then-HTTP) → local Ollama → gateway (`AITHER_GATEWAY_EMBEDDINGS_URL`) → auto-deploy a local vLLM embeddings container if a GPU + Docker are present (opt out with `AITHER_EMBED_AUTODEPLOY=0`) → CPU sentence-transformers (384-d, degraded) → feature-hash (384-d, degraded, always works). Every batch is dimension-tagged so callers never silently mix 768-d and 384-d vectors. `get_default_embedder()`, `embed_texts()`, `embed_one()`, `get_provider()`, `reset_provider()`.\n\n### Changed\n- **`GraphMemory` now defaults its embedder to the canonical provider** (opt out with `AITHER_GRAPH_EMBEDDER=legacy`), so every adk agent shares one 768-d space. Added a meta-table dimension guard: the index is pinned to its first embedding's dimension and later different-dimension vectors are refused (kills 768↔384 mixing that silently poisons cosine similarity).\n\n## [2.13.2] - 2026-07-01\n\n### Added\n- **Session persistence**: AitherShell now auto-saves the active session_id to `~/.aither/config.yaml` after each chat turn and auto-restores it on the next shell launch. Multi-turn conversations survive across restarts — the messages were always persisted in SQLite + JSON, only the session pointer was missing.\n- `/new` command to start a fresh session (clears saved session_id).\n- `/sessions` command to list recent sessions with agent name, message count, and timestamp. Resume any with `/resume <id>`.\n\n## [2.13.1] - 2026-07-01\n\n### Fixed\n- **CRITICAL:** `deploy_connect()` now sends a HEAD request to verify the GitHub release asset exists before downloading, with a clear error if the asset is missing.\n- **CRITICAL:** Compose file URLs are now pinned to a stable commit hash (configurable via `AITHER_COMPOSE_PIN` env) instead of tracking the mutable `main` branch.\n- **CRITICAL:** `deploy_adk_node()` now falls back to starting `adk-serve` natively when Docker is unavailable, instead of blocking with \"Docker not installed\".\n- **HIGH:** GHCR login failure now blocks deployment immediately with actionable guidance, instead of warning and continuing to a confusing image-pull failure.\n- **HIGH:** Health checks now stream the last 30 lines of Docker Compose logs on failure, so the user can see why a container didn't start.\n- **HIGH:** vLLM port allocation now probes for conflicts before assigning; if port 8200 is busy, workers are automatically remapped to the next free port.\n- **MEDIUM:** Compose file downloads are now cached with ETag; re-running `adk deploy node` skips re-downloading an unchanged file.\n- **MEDIUM:** DGX Spark discovery now respects `AITHER_DGX_HOSTS` env (comma-separated) for custom network topologies, instead of only checking `spark.local` and `192.168.0.33`.\n- **MEDIUM:** Cloud API keys (Anthropic, OpenAI, DeepSeek) are now validated with a lightweight probe during `adk setup` infra scan, so invalid keys surface early.\n- **MEDIUM:** `deploy_connect()` now validates the extracted extension has a `manifest.json` and warns if it's missing.\n\n## [2.13.0] - 2026-07-01\n\n### Removed\n- **`adk.faculties.memory_graph`** — the pickle-based `MemoryGraph` module is deleted. `from adk.faculties import MemoryGraph` now resolves to the canonical `adk.graph_memory.GraphMemory` for back-compat.\n- **`adk.platform.memory`** — the entire legacy memory subsystem (`MemoryManager`, `UnifiedMemorySystem`, `GameEngine`, `StoryboardEngine`, `AnchorGenerator`) is deleted. Agent memory is `adk.memory.Memory` + `adk.graph_memory.GraphMemory`.\n\n### Changed\n- `adk work` command now uses `GraphMemory` instead of the removed pickle-based faculty graph.\n- `adk.MemoryGraph` and `adk.faculties.MemoryGraph` lazy exports now resolve to `adk.graph_memory.GraphMemory`.\n- `agent.set_memory_graph()` kept for back-compat but documented as deprecated; agents already wire `GraphMemory` automatically in `__init__`.\n- Platform startup (`adk.platform.infrastructure.startup`) gracefully handles missing `MemoryManager`.\n- Platform CLI module listing and health check updated to reflect removed memory module.\n\n## [2.12.7] - 2026-07-01\n\n### Deprecated\n- `adk.faculties.MemoryGraph` — emits `DeprecationWarning` on instantiation; use `adk.graph_memory.GraphMemory` (SQLite-backed, governed, embedding-native) instead.\n- `adk.platform.memory` package — the legacy `MemoryManager`, `UnifiedMemorySystem`, `GameEngine`, and `StoryboardEngine` modules now emit `DeprecationWarning` on import; use `adk.memory.Memory` and `adk.graph_memory.GraphMemory` for new code.\n\n### Changed\n- Updated `adk.faculties` docstring and usage examples to point at the canonical memory surface.\n\n## [2.12.6] - 2026-07-01\n\n### Changed\n- Trimmed public package debt by removing grid runbook artifacts and shell/room binary placeholder directories from published artifacts.\n- Removed stale public-sync allowlist entries for deleted phase test files and added explicit public-sync guards for binary placeholder directories and grid artifacts.\n- Updated the public README grid note so it no longer links to runbook files excluded from the SDK package.\n\n## [2.12.5] - 2026-07-01\n\n### Added\n- Added best-effort fleet memory sync from local ADK memory into Nexus/Qdrant-compatible RAG stores, including batch catch-up and vector-search recall hooks.\n- Added saved config/env support for fleet memory URL, collection, and sync mode.\n\n### Changed\n- Excluded FormBridge runtime/test internals from public ADK packaging and public repo sync.\n\n## [2.12.4] - 2026-07-01\n\n### Added\n- Added a hot-reload endpoint for installed agent packs so newly applied pack tools can be picked up without restarting `aither-serve`.\n\n### Fixed\n- Fixed synchronous `ChatRelay` calls on Python 3.12 when no event loop is running; async WebSocket delivery is still scheduled when a loop exists.\n- Aligned gateway inference tier tests with the current public ladder: free, starter, pro, enterprise, platform.\n\n## [2.12.0] - 2026-06-28\n\n### Added — one-command local inference + upgrade path (click-to-run)\n- **`adk quickstart-local`** — detects hardware, picks a backend\n (Ollama / llama.cpp / vLLM), installs it, downloads a model, verifies, and\n prints next steps. Bootstraps Ollama itself via winget/brew/install.sh when\n missing (no \"go install it\" dead-ends). Default model `gemma4:e2b`.\n- **`adk backend switch <ollama|llamacpp|vllm>`** (+ `backend status`) — migrate\n the active deployment between engines; re-points config and smoke-tests.\n- **`adk install pack:<name>`** (+ `adk install list` / `adk packs`) — install\n ready-made agent packs (openclaw, hermes, claude-code) into `~/.aither/agents`.\n\n### Fixed (found via live end-to-end testing on Windows + CUDA)\n- quickstart-local reported success on a dead endpoint (smoke test was warn-only);\n a failed smoke now fails the command, with a service-readiness wait.\n- `--model` resolved to `None` (getattr default never fired) → uses the real default.\n- Smoke test: 30s→180s timeout for cold model load; accepts reasoning-model output\n (empty `content` at low token budgets is no longer a false failure).\n- `adk install pack:<name>` colon syntax (argparse rejected it before dispatch).\n- `adk backend status` health probe used a nonexistent `/health` path → 404; now\n probes `/v1/models` (uniform OpenAI-compatible liveness for all three backends).\n- Windows: removed non-ASCII glyphs from printed strings that crashed the cp1252\n console (a `✓` even made a successful install report failure).\n\n## [2.11.1] - 2026-06-14\n\n### Fixed — `adk host` / `adk login` device flow (autonomous onboarding)\nFound driving `adk host` against production; all broke real device-flow login:\n- **Device flow hit the portal FRONTEND, not AitherIdentity.** `adk login`/`adk host`\n POSTed `/auth/device/code` to portal/veil.aitherium.com, which 307-redirects API auth\n to `/login`. Added `_resolve_identity_url()` mapping the aitherium.com topology to\n `idp.aitherium.com` (the Identity API); localhost/bespoke pass through unchanged.\n- **Cloudflare 403'd urllib.** `idp.*` sits behind Cloudflare, which blocks urllib's\n default `Python-urllib/3.x` UA. Device-code + poll requests now send a real\n User-Agent + Accept (verified urllib→idp 200, was 403).\n- **`adk host` now self-authenticates via device flow** when there's no token, and\n **retries via device flow on a 401/403** (a stale saved token no longer dead-ends\n registration).\n\n## [2.11.0] - 2026-06-14\n\n### Added — attach your own MCP \"hands\" to a self-hosted agent\nA self-hosted agent now has the full brain (your model) · body (this loop) · **hands**\n(your tools) trifecta. The AitherOS platform relays the MCP servers you registered for\nyour agent in the `/stream` request body as `mcp_endpoints`, and the loop wires their\ntools into the ReAct turn:\n- **`adk/mcp_endpoint_tools.register_mcp_endpoint_tools(agent, endpoints)`** — speaks MCP\n JSON-RPC (`tools/list` to discover, `tools/call` to invoke) and registers a namespaced\n proxy tool (`{endpoint}__{tool}`) for each advertised tool. Mirrors `app_proxy_tools`.\n Best-effort: an unreachable endpoint registers nothing and never raises.\n- **`POST /stream` and `POST /chat/stream`** accept an optional `mcp_endpoints` list\n (`[{name, url, [headers]}]`); `_aitheros_stream` attaches them to the agent before the\n turn. Existing callers are unaffected (the field is optional).\n\n## [2.10.1] - 2026-06-14\n\n### Fixed — self-hosted `aither-serve` was crash-on-startup (2.10.0 regression)\nA fresh `pip install aither-adk && aither-serve --backend deepseek` could not boot. Found\nand fixed while standing up a real self-hosted DeepSeek agent end-to-end:\n- **Startup `NameError: name 'port' is not defined`** in three lifespan helpers\n (`_join_aithernet`, `_init_a2a_server`, `_start_mesh_hosting`) — they referenced a bare\n `port` no longer in scope. Now use `config.server_port`, and `main()` writes the resolved\n `--port`/`--host` back to `config` so every lifespan helper and the invoke_url see the\n actual bound port.\n- **Fleet self-registration crashed the whole server** — the registration handlers caught\n `(ImportError, RuntimeError, OSError, ConnectionError)` but **not** `httpx.HTTPError`, so a\n `RemoteProtocolError`/connect error talking to the control plane took the agent down. All\n network-facing lifespan handlers now also catch `httpx.HTTPError` (non-fatal, as intended).\n- **`--backend` was ignored when `AITHER_API_KEY` was set** — `_auto_detect` picked the cloud\n gateway before the explicit backend, and `_try_elysium_fallback` then replaced the chosen\n provider. An explicit backend (deepseek/openai/anthropic/your own vllm/ollama) now always\n wins; \"auto\"/\"gateway\" still flow through detection. The operator's own brain is never\n silently hijacked.\n- **Approved-tool trace truncated after `/sessions/{id}/confirm`** — the SSE relay treated\n `AgentResponse.tool_calls_made` (a `list[str]`) as dicts (`tc.get(...)`), raised\n `AttributeError`, and an over-narrow `except` swallowed it so the client saw only\n `session_start` + heartbeats. The relay now handles string tool names, the `except` is\n broad (always emits a typed `error` + terminal `complete`), and the module-level\n `_strata_ingest` `NameError` + Chronicle `session_id` kwarg mismatch are fixed.\n\n## [2.10.0] - 2026-06-13\n\n### Added — human-in-the-loop tool approval (`adk/approval.py`)\n- **Pause before a gated tool, resume on decision.** An agent can now pause its turn before\n executing a sensitive tool and wait for a human's allow/deny — so a managed control plane\n (or any operator) can gate tool use without forking the loop.\n- **Policy via `AITHER_TOOL_APPROVAL`** — a comma-separated list of tool names (optionally\n `agent:tool`), or `*` for every tool. Empty = no gating (unchanged behavior).\n- **Survives restarts / days-later approvals.** The paused turn (user message + pending tool\n calls) is persisted to disk keyed by `session_id`; resume is a fresh request, not a held\n connection. Decisions are recorded per `(session_id, tool_name)`.\n- **`AgentResponse.requires_action` / `.pending`** — set when a turn paused; `pending` lists\n the gated tool calls awaiting a decision.\n- **`AitherAgent.resume(session_id, decisions)`** — records the decisions and re-runs the\n paused turn (the gate consumes them: allowed tools execute, denied tools get a denial\n observation, the turn continues — and may pause again on a different gated tool).\n- **Server:** `POST /sessions/{id}/confirm` records decisions and streams the resumed turn;\n `/stream` emits a `requires_action` event when a turn pauses.\n\n## [2.9.0] - 2026-06-13\n\n### Added — universal LLM continuation primitive (`adk/llm/continuation.py`)\n- **`run_continuation()` + `stitch()`** — one shared continue-until-complete primitive. When a\n completion stops because it hit the output token cap (`finish_reason == \"length\"`), it continues\n the generation and STITCHES the chunks (overlap-dedup + restart-detect) into a complete answer,\n instead of every call-site hand-rolling its own retry. Bounded three ways (max rounds AND total\n output ceiling AND a no-growth break), and it never mutates the caller's message history.\n- **`LLMProvider.chat_with_continuation()`** and **`LLMRouter.chat_with_continuation()`** — every\n provider AND the router inherit the primitive, so any call-site gets continuation for free.\n- Kill-switch `ADK_LLM_CONTINUATION=off`; tunables `ADK_LLM_MAX_CONTINUATIONS`,\n `ADK_LLM_MAX_TOTAL_OUTPUT_TOKENS` (defaults: 2 rounds / 8192 tokens).\n\n### Changed\n- The ReAct loop's truncation recovery now uses the shared primitive. The previous inline\n doubling-retry REPLACED the partial response (losing earlier text in `resp.content`) and injected\n continuation scaffolding into the live message history; it now STITCHES and leaves history clean.\n Tool-call turns are never continued as text.\n\n### Included — previously-committed-but-unreleased changes\n- 2.8.2: TLS verification on by default (`verify=False` removed).\n- 2.8.1: baked license + pack-signing public keys.\n- 2.8.0: version-bump release.\n\n## [2.7.0] - 2026-06-05\n\n### Added — provider-agnostic LLM cost levers (at the `LLMProvider` ABC)\n- **Prompt caching.** New `ProviderCapabilities` (`prompt_cache: explicit|automatic|none`, `batch`) and\n normalized `LLMResponse.cache_read_tokens`/`cache_write_tokens`, so the \"tokens saved\" meter never\n depends on which backend served the turn. Anthropic inserts `cache_control` breakpoints over the stable\n system+tools prefix; OpenAI/DeepSeek caching is automatic and their `cached_tokens` are now surfaced\n (previously discarded). Default-on in `AitherAgent.chat`; override with `cache=False`.\n- **ResponseCache** (`adk/llm/cache.py`) — exact-match response cache with a pluggable backend, wired into\n `LLMRouter` as an opt-in (`LLMRouter(response_cache=…)` + per-call `cacheable=True`). OFF by default — no\n determinism change unless opted in.\n- **Gemini provider** (`adk/llm/gemini.py`) with explicit `cachedContents` caching; registered in the router.\n- **Async batch runner** (`adk/llm/batch_runner.py`) — `run_batch()` over the Anthropic Message Batches /\n OpenAI Batch APIs (~50% off) with a transparent fallback to concurrent `chat()` where unsupported.\n- **Graph-RAG retriever interface** (`adk/graph_retriever.py`).\n\n### Added — principal authority (prompt-injection defense enforced outside the model)\n- `adk/auth.py`: `Principal` / `AuthContext` / `PrincipalResolver` (+ deny-all default).\n- `ToolRegistry.execute(name, args, auth=…)` — a default-deny authorization gate at the tool boundary, so a\n forbidden tool call is blocked regardless of what the LLM emits. `ToolDef` gains `required_clearance` /\n `action_class`. Threaded through `AitherAgent.chat`; `auth=None` is fully backward-compatible.\n- `adk/channel_auth.py`: Telegram webhook-secret + CEO-id checks and HMAC-signed approvals.\n\n## [2.6.4] - 2026-06-05\n\n### Fixes\n- **Loop-guard nudges corrupted the message history → DeepSeek 400 mid-run.**\n When the ReAct loop emitted multiple tool calls in one assistant turn and the\n loop guard fired a WARN/BLOCK/CIRCUIT_BREAK on one of them, it spliced a\n standalone `system` nudge message *between* the tool results. DeepSeek (and\n strict vLLM chat templates) require an assistant `tool_calls` message to be\n followed by exactly one `tool` result per `tool_call_id`, contiguously — so the\n interleaved message produced `400 … insufficient tool messages following\n tool_calls message` and aborted the turn (this is reliably hit after crossing\n the 15-call circuit-break threshold, where every subsequent call WARNs).\n Per-call guidance is now folded into that call's own `tool` result, and any\n standalone steering is deferred to a single message AFTER the whole batch —\n preserving the `tool_calls`↔`tool_results` pairing every backend requires.\n Regression test: `deep-research-agent/tests/test_msg_structure.py`\n (loopguard-flood scenario).\n\n## [2.6.3] - 2026-06-04\n\n### Fixes\n- **Transient provider errors killed a run on the first blip.** A single\n `502 Bad Gateway` / `503` / `529 overloaded` / `429` from the upstream API\n aborted the whole request — the Anthropic provider had **no retry at all**, and\n the OpenAI-compatible provider only retried `429`. Both now retry transient\n statuses (`408/429/500/502/503/504/529`) with `Retry-After`-aware exponential\n backoff (3 attempts), and surface the provider's body if all retries are\n exhausted. Non-transient `4xx` (e.g. `400`) still fails fast. Applies to\n `chat` and `chat_stream` on both providers.\n\n## [2.6.2] - 2026-06-04\n\n### Fixes\n- **DeepSeek (and strict OpenAI-compatible backends) 400'd on long tool\n conversations.** The ReAct loop legitimately injects mid-conversation\n `system` steering messages (the \"[DIMINISHING RETURNS]\" hint after several\n low-output tool iterations, loop-guard nudges). OpenAI tolerates a non-leading\n `system` message; DeepSeek rejects it with a 400. Long research runs\n (many `fetch_url` calls) reliably tripped this. Fix: non-leading `system`\n messages are demoted to `user` at the OpenAI-compatible provider boundary\n (`_demote_nonleading_system`) — steering intent preserved, payload valid on\n every backend; leading system prompt(s) untouched.\n- **Opaque HTTP errors.** `resp.raise_for_status()` discarded the provider's\n response body, so a 400/422 surfaced as a bare `Client error '400 Bad\n Request'` with no cause. `_ensure_ok` now includes the provider's actual\n message (e.g. `context_length_exceeded`, `Invalid 'messages'`) in the raised\n error, for both `chat` and `chat_stream`.\n\n## [2.6.1] - 2026-06-04\n\n### Fixes\n- **`MCPServer.mount()` returned HTTP 422 for every `/mcp` call.** Under\n `from __future__ import annotations` the route handler's `request: Request`\n annotation is a string that FastAPI resolves against the *module* globals, but\n `Request` was imported locally inside `mount()` — so FastAPI couldn't resolve\n it and treated `request` as a required query param. The MCP server endpoint\n (`initialize`, `tools/list`, `tools/call`) was unusable when mounted. FastAPI\n symbols are now imported at module level (guarded; the `[node]` extra still\n gates actual use). Any agent's tools can now be exposed as a working MCP\n server with `MCPServer(...).mount(app)`.\n\n## [2.6.0] - 2026-06-04\n\n### Streaming + scaffolding\n- **`AitherAgent.stream_chat`** — reliable streaming sibling of `chat()`: native\n tool-calling loop (concludes on its own, unlike the text-protocol `stream_react`)\n that emits live `tool`/`tool_result` events and streams the answer as `token`\n events. `token_delay` paces tokens for a typing effect.\n- **`adk new <template>`** — scaffold a full, runnable template app. Ships the\n `deep-research` template (a cited web-research agent: pack + server + web UI):\n `adk new deep-research`.\n\n### Fixes\n- **BYO-key no longer community-capped.** The free-tier monthly token cap now applies\n only to the metered Aitherium gateway backend, never to a user's own provider key\n (Anthropic/OpenAI/DeepSeek/Ollama/vLLM). Previously it could brick a self-hosted\n agent after ~100k tokens on the user's own key.\n- **Stronger, grounded memory recall.** `chat()` now injects the top-6 knowledge-graph\n hits (untruncated) with a hard no-fabrication instruction — was top-3 truncated to\n 200 chars, which surfaced partial facts and let weaker models invent the rest.\n- **Tool-arg coercion.** `ToolRegistry.execute` coerces LLM-supplied args toward each\n parameter's annotated type, so a tool no longer crashes when a model passes a list\n or string where an int/float/bool was expected.\n\n### Docs\n- New `docs/AGENT_DEV_GUIDE.md` — the golden path for building agents/packs.\n\n## [2.5.0] - 2026-06-04\n\n### Continual learning — agents that learn from runs and reuse memory\n\nAgents now improve the second time they do a task instead of starting from scratch.\n\n### Added\n- **Recall-before**: `AitherAgent.chat()` searches the local `SkillStore` and injects the\n top matching learned skills as a system block, so the model reuses proven procedures.\n- **Learn-after**: `_learn_after()` runs after each response — reinforces (`success_count++`)\n any skills that were recalled+reused, and extracts + saves a NEW skill from successful\n multi-tool runs via `SkillExtractor`. Wired at both `chat()` return paths.\n- Gated by `AITHER_SKILLS` (default on). The previously-unused `SkillStore`/`SkillExtractor`\n are now connected to the run loop.\n\n### Notes\n- Pairs with the platform-side LearnedProcedure substrate (recipes → scored procedures →\n skills/tools/A2A/packs → MCTS priors → evolution). The SDK shares memory via the Spirit\n bridge when configured.\n\n## [2.0.0] - 2026-06-02\n\n### Open-core boundary (BREAKING)\n\nThis release draws a single, enforced free/paid line. The free tier stays\ngenuinely useful (real agent, typed + graph + code memory, ReAct, ~essential\ntools); paid tiers unlock the *scale* capabilities via portal.aitherium.com.\n\n### Added\n- **`adk/licensing.py`** — the entitlement keystone. Resolves a tier\n (COMMUNITY by default), verifies optional portal-signed `~/.aither/license.json`\n (Ed25519), and answers `can_use_fleet/channels/cron/swarm/auto_neurons`,\n `is_agent_licensed`, `max_effort`, etc. **Fail-closed**: no/invalid/expired\n license → free tier; an unsigned license never grants premium.\n `AITHER_TENANT_SLUG=aitherium` → unrestricted INTERNAL tier.\n- **Moat guard** (`scripts/check_moat_boundary.py`) wired into the publish\n workflow — blocks any wheel that leaks the moat or drops the keystone.\n- Tests: `tests/test_licensing.py`, `tests/test_moat_boundary.py`.\n\n### Changed (gating — fail-closed)\n- Free tier effort is capped at 3 (reasoning effort 7–10 needs Professional).\n- Monthly token budget hard-block on the free tier (no silent overspend).\n- Gated behind a paid tier: fleet mode (`FleetConfig`), cross-agent delegation\n (`AgentForge.delegate`), channel adapters, cron scheduler, proactive\n auto-neurons, and `swarm`/`swarm_code` dispatch.\n\n### Removed\n- **`adk/nanogpt.py`** (on-device training IP) removed from the published SDK\n and the public repo; relocated to the internal AitherOS moat. Excluded from\n the wheel as defense-in-depth.\n- Pre-2.0 releases shipped this IP and ungated capabilities and are being\n yanked (PyPI) / removed (GitHub releases). See `scripts/yank_leaky_releases.py`\n and `scripts/purge_public_leaks.sh`.\n\n## [1.20.0] - 2026-05-27\n\n### Fixed\n- **Pack discovery path mismatch** — `_find_local_packs()` now scans both\n `~/.aither/packs/` and `~/.aitheros/packs/`, so packs installed via\n `adk pack install` are discovered by `adk-workspace`.\n\n### Added\n- **Auto-register on pack install** — `adk pack install` now auto-registers\n with the portal if the pack contains an `agent.yaml` with a `portal` section.\n One command does download + extract + register.\n- **Flux event on agent registration** — `developer_portal.py` emits\n `agent.registered` Flux event so the portal fleet UI updates in real-time.\n\n## [1.15.0] - 2026-05-26\n\n### Added\n- **Cloud memory** — `adk quickstart --cloud` now tests gateway memory and\n auto-configures Spirit URL so memories persist across devices via\n `gateway.aitherium.com/v1/memory/teach` and `/v1/memory/recall`.\n- **Config→env export for cloud mode** — `Config.from_env()` now exports\n `AITHER_CLOUD_MODE`, `AITHER_SPIRIT_URL`, and spirit path env vars from\n saved config so Memory and other modules pick them up without needing\n explicit env vars set by the user.\n- **Cloud Quick Start section** in README — 3-command setup for users who\n just want a Claude/GPT-4/DeepSeek API key and the full agent harness.\n- **`--memory` profile for node deployment** — `adk deploy node --memory`\n adds Spirit (8087) + WorkingMemory (8101) containers for persistent\n vector memory. Without this, agents use local SQLite only.\n- **Configurable vLLM in node compose** — `AITHER_VLLM_MODEL`,\n `AITHER_VLLM_GPU_UTIL`, `AITHER_VLLM_CTX_LEN`, `AITHER_VLLM_QUANT_ARGS`\n env vars let `adk setup` write GPU-detected tier settings to `.env`\n and have the node compose pick them up (TQ4 for small GPUs, BNB for large).\n\n### Fixed\n- **Memory cloud routing** — `Memory._spirit_teach()` and `_spirit_recall()`\n now use configurable paths and auth headers for gateway proxy. Previously\n cloud-only users silently lost all Spirit memory calls.\n\n## [1.9.0] - 2026-05-24\n\n### Added\n- **Composable ToolPack system** — agents can load tool packs from\n `.toolpack.yaml` directories. Drop a pack in `~/.aitheros/packs/` or\n set `AITHER_PACKS_DIR`, and any agent discovers it automatically.\n- **`tools.packs` in agent.yaml** — list tool pack IDs to auto-load\n (e.g. `packs: [crm-tools, calendar-pro]`).\n- **`register_tool_packs(agent, pack_ids, packs_dir)`** in `builtin_tools` —\n programmatic pack loading for agents.\n- **`AITHER_TOOL_PACKS` env var** — comma-separated pack IDs loaded on\n agent init (alternative to YAML config).\n- **Unknown builtin categories resolve as pack IDs** — `tools.builtin:\n [file_io, crm-tools]` now works: `crm-tools` is looked up as a pack.\n\n## [1.8.0] - 2026-05-24\n\n### Removed\n- **11 dead modules** (3,023 lines) — `agent_deploy`, `cloud_deploy`, `create`,\n `phonehome`, `sdk_bridge`, `sovereign`, `telemetry`, `telemetry_config`,\n `error_reporter`, `anonymizer`, `package`. None were imported anywhere.\n- **29 ghost exports** from `__init__.py` — `secrets`, `otel`, `fs_sandbox`,\n and `vector_memory` modules were listed in `__all__` and `__getattr__` but\n the backing files never existed. Accessing them raised `ModuleNotFoundError`.\n- **Stale dist/ artifacts** — removed old wheels/tarballs (v0.4.0–v1.4.1, 11 MB).\n- **Orphan test scripts** — `test_phase1.py` and `test_phase23.py` (one-off\n validation scripts not part of the pytest suite).\n\n### Fixed\n- **Version mismatch** — `__init__.__version__` now matches `pyproject.toml`\n (was stuck at 1.6.0).\n\n## [1.5.0] - 2026-05-23\n\n### Added\n- **`--sovereign` flag for `adk deploy node`** — registers the node with the\n Aitherium hub via federation after deployment. Saves federation credentials\n to `~/.aither/.env.federation`. Accepts `--hub` (custom hub URL) and\n `--tenant` (tenant slug) options. Non-fatal: node runs standalone if\n registration fails.\n- **Federation → fleet cross-post** (Genesis) — sovereign nodes that register\n or heartbeat via `/federation/register` and `/federation/heartbeat` now\n automatically appear in the fleet dashboard (`/fleet/deployments`) with\n `node_type: \"sovereign\"` and live status/metrics.\n- **\"Deploy Sovereign Node\" section on Connect page** (AitherVeil) — Section 8\n with ADK deploy commands, fleet dashboard links, and provision page links.\n- **`deploy-node` action in tunnel proxy** (AitherVeil) — Connect page and\n external callers can now request bootstrap scripts via\n `POST /api/tunnel/devworkspace?action=deploy-node`.\n\n### Fixed\n- **Fleet provision page** — API URL corrected from `/api/fleet/provision` to\n `/api/bridge/genesis/fleet/provision` (matches bridge proxy pattern).\n- **Fleet detail page** — API URL corrected from `/api/fleet/deployments/` to\n `/api/bridge/genesis/fleet/deployments/` (same fix).\n- **Dockerfile** — entry point corrected from `aither-serve` to `adk-serve`\n (matches `pyproject.toml` script registration). Version label updated.\n\n## [1.4.1] - 2026-05-22\n\n### Fixed\n- **macOS executable builds** — dropped `--target-arch universal2` from\n `packaging/build_executable.py`. PyPI wheels for `pydantic_core` and other\n native deps are arch-specific, not fat binaries, so universal2 always failed\n with `IncompatibleBinaryArchError`. Now builds for the runner's native arch\n (`aither-macos-arm64`).\n- **Release workflow strategy** — added `fail-fast: false` so a single\n executable build failure no longer cancels the other OS jobs.\n- **PyPI publish** — added `skip-existing: true` so re-runs of the same\n version don't fail when the artifact already exists.\n- **GitHub Release job** — now runs whenever `pypi` succeeds even if some\n executable matrix entries fail, so the release page is always created with\n whatever binaries did build successfully.\n\n## [1.4.0] - 2026-05-22\n\n### Added\n- **Sovereign template now ships with `agent_core` 5-tier memory** — generated\n backends include the canonical `agent_core/` package (working → episodic →\n semantic → procedural → identity) plus `agent_memory.py` helper, vendored\n from `portal-kit-backend`.\n- `aiosqlite>=0.20.0` added to sovereign template `requirements.txt` for\n local agent_core SQLite storage.\n- Document tombstoning: `evict_cached_conversations_referencing` is wired\n through document-delete handlers in consumer apps so memories citing\n deleted documents are blocked from recall.\n- New tests: 13 smoke tests for `portal-kit-backend/agent_core` covering\n promotion thresholds, decay rates, store roundtrip, tombstone recall\n blocking, circuit breaker state machine, and chat-turn recording.\n\n### Changed\n- `scripts/vendor_agent_core.py` now syncs canonical `agent_core` to 4\n targets: WorkspaceRuntime, ADK sovereign template, and the GargBot +\n Chelle consumer mirrors (previously only 2).\n\n## [1.2.1] - 2026-05-21\n\n### Added\n- `adk train` command group — 6 subcommands: status, launch, logs, cancel, runs, register-gpu\n- `/slash-commands` endpoint — ADK server auto-exposes all 35 CLI commands as structured JSON manifest for AitherShell\n- `/cli/execute` endpoint — AitherShell can run any CLI command through the server\n- `build_command_manifest()` — introspects full argparse tree (commands, args, choices, subcommands)\n- `_register_commands()` extracted from main() for shared parser construction\n\n### Changed\n- `_get_genesis_url()` now probes: Genesis → ADK server → Aitherium cloud gateway (works without Genesis)\n\n## [1.2.0] - 2026-05-21\n\n### Added\n- **Runtime backend switching** — `agent.switch_backend(\"deepseek\")` and `agent.llm.switch_backend()` change provider without recreating agent\n- **Hybrid reasoning** — `agent.set_reasoning_backend(\"anthropic\")` routes effort 7+ to cloud API while keeping local orchestrator\n- **First-class DeepSeek provider** — `deepseek` in effort model table with `deepseek-chat` and `deepseek-reasoner`\n- **TQ4 quantization tiers** — `nano` (6GB, TurboQuant 4-bit), `standard-tq4` (12GB, both models TQ4), `hybrid-tq4` (6GB + cloud reasoning)\n- **`adk backend` CLI** — `list`, `set`, `set-reasoning`, `test` subcommands\n- **`adk quickstart`** — unified first-run wizard (setup + auth + shell)\n- **`adk tools`** — list local + MCP tools with tier markers\n- **`adk backup`** — tarball export of `~/.aither/`\n- **`adk ingest`** — feed docs/files into knowledge graph\n- **`adk setup nemotron`** shortcut — alias for `--tier lite`\n- **`--reasoning-api`** flag in setup — `anthropic`, `openai`, `deepseek`, `gateway`\n- **`--dgx-spark URL`** flag — remote vLLM endpoint configuration\n- **DGX Spark auto-detection** — scans `spark.local`, `192.168.0.33`, `AITHER_DGX_URL` env\n- **Post-setup smoke test** — sends real inference request after container startup\n- **Schema migration system** — version table + migration runner in `memory.py` and `graph_memory.py`\n- **Configurable vLLM port scan** — `AITHER_VLLM_PORTS` env var for custom ports\n- **`aithershell` entry point** — alias for `adk` in pyproject.toml\n- Shell auto-downloads binary on first `adk shell` (no `--install` needed)\n- Shell pre-flight check warns if no backend detected\n- Shell passes full config via env vars (API key, tenant, inference URL)\n- DGX/Remote and Cloud API checks added to `adk doctor`\n- Config fields: `reasoning_backend`, `reasoning_api_key`, `deepseek_api_key`, `dgx_url`, `vllm_extra_ports`\n\n### Fixed\n- `test_aeon_chat_basic` timeout — mock scope was exiting before request\n- `adk doctor` check_disk timeout — cap rglob at 5000 files\n\n## [1.1.7] - 2026-05-19\n\n### Added\n- Remote agent pipeline — `adk onboard` → fleet dispatch → remote inference\n- Agent-to-agent federation with mesh relay\n- `adk connect` for desktop mesh enrollment\n\n## [1.1.6] - 2026-05-19\n\n### Fixed\n- Tool call execution recovery across all backends\n- Hermes tool call fallback parsing for vLLM\n\n## [1.1.5] - 2026-05-19\n\n### Fixed\n- Tool call execution + Hermes fallback parsing improvements\n\n## [1.1.0] - 2026-05-18\n\n### Added\n- Multi-channel gateway (Telegram, Discord, Slack, webhook)\n- Aeon group chat with 7 presets\n- Skills auto-extraction from multi-step sessions\n- MCP stdio server for Claude Code integration\n- `adk gateway`, `adk cron`, `adk skills`, `adk soul` commands\n- SOUL.md import/export for portable agent identity\n- Elysium cloud device-flow authentication\n\n## [1.0.0] - 2026-05-18\n\n### Breaking\n- Package consolidated — `aither_adk` namespace replaced by `adk` + `adk.platform`\n- 93 duplicate files removed from `AitherOS/packages/aither_adk/`\n\n### Added\n- Standalone core primitives (`adk.core`: Agent, Tool, Memory, ModelBackend, Capability, Trace)\n- `adk.platform` sub-package merging all platform toolkit modules\n- Compatibility shims for legacy `from aither_adk.*` imports\n\n### Changed\n- Version synced across pyproject.toml, `adk/__init__.py`, `adk/platform/__init__.py`, npm package\n- Classifier promoted from Alpha to Beta\n\n## [0.16.0] - 2026-04-16\n\n### Swarm Coding Engine & Repowise Integration\n- **`repowise_search` tool** — Semantic + keyword hybrid code search via Repowise, with ripgrep fallback\n- **`swarm_code` tool** — Dispatch complex tasks to 11-agent swarm pipeline (ARCHITECT->SWARM->REVIEW->JUDGE)\n- **`agent.swarm()`** — Async convenience method with configurable mode, effort, timeout\n- **`agent.code_search()`** — Async convenience method returning structured results\n- New tool categories: `repowise` and `swarm` in TOOL_CATEGORIES\n- `repowise_search` and `swarm_code` added to `__init__.py` exports with lazy loading\n- `IDENTITY_DEFAULTS` updated: repowise for code-focused agents, swarm for orchestration agents\n- Standalone graceful degradation: repowise falls back to ripgrep, swarm returns structured error\n\n## [0.13.0] - 2026-04-02\n\n### Graph Faculties — Local Knowledge for Every Agent\n- **CodeGraph** (2,799 lines) — Full Python AST indexer with call graph, keyword/semantic/hybrid query, embedding matrix cache, incremental re-indexing, multi-hop chain expansion\n- **MemoryGraph** (1,339 lines) — Graph-based persistent agent memory with 10 edge types, hybrid query (keyword + semantic + graph expansion), multi-hop recall, pickle persistence\n- **EmbeddingProvider** — 4-backend fallback chain: sentence-transformers (GPU/CPU) -> Ollama -> Elysium cloud -> feature hashing (zero deps)\n- **BaseFacultyGraph** — Abstract base with HMAC-SHA256 validated pickle persistence\n\n### Agent Integration\n- `agent.set_code_graph(cg)` — auto-registers `code_search` + `code_context` tools\n- `agent.set_memory_graph(mg)` — auto-registers `remember` + `recall` + `memory_stats` tools\n- Both graphs inject context into LLM prompts automatically during chat\n\n### Zero-Config Onboarding\n- `adk start` / `adk` (no args) — auto-detect project, index code, detect LLM, persistent memory, interactive chat\n- Works for any directory: Python codebases, doc folders, mixed workspaces\n- Auto-detects LLM: Ollama -> vLLM -> Elysium -> OpenAI -> Anthropic\n- Per-project persistent memory in `~/.aither/memory/<project>`\n- `adk index <path>` — standalone indexing with progress bar and stats\n\n### MCP Gateway\n- `POST /v1/embeddings` — OpenAI-compatible embedding proxy to vLLM-embeddings:8209\n- Available to all tiers (embeddings are free)\n- ADK EmbeddingProvider uses this as Elysium cloud fallback\n\n### Optional Dependencies\n- `pip install aither-adk[graphs]` — numpy for 10x cosine similarity speedup\n- `pip install aither-adk[embedding]` — sentence-transformers + torch for local GPU embeddings\n- `pip install aither-adk` alone — graphs work with feature hashing (zero deps)\n\n## [0.12.0] - 2026-04-01\n\n### Bootstrap & Service Discovery\n- Version handshake -- ADK checks major.minor compatibility with Genesis/Node\n- Background reconnect loop -- ServiceBridge re-probes every 30s in standalone mode\n- Port 8080/8090 documented -- MCP vs OpenAI-compat clearly separated\n- Genesis URL configurable via GENESIS_URL env var (was hardcoded)\n- Standalone mode warning -- visible stderr alert when AitherOS not detected\n- Auto-reconnect on startup when services come online\n\n### Elysium Cloud Inference\n- Unified gateway URL -- gateway.aitherium.com handles auth + billing + inference\n- Streaming inference -- SSE passthrough for /v1/chat/completions with stream=true\n- Auth proxy routes -- /v1/auth/register, /v1/auth/login, /v1/auth/me\n- Billing proxy -- /v1/billing/balance through gateway\n- AitherConnect Elysium fallback -- cloud inference when local Genesis is down\n- AitherDesktop Elysium fallback -- third-tier chat fallback after Node\n\n### Infrastructure\n- /discovery endpoint on Genesis -- unified service URLs/versions/health\n- /api/config/services on Veil -- runtime port config for client-side JS\n- Veil healthcheck gates on Genesis -- unhealthy when backend is down\n- Desktop crash detection 90s->60s (threshold 3->2)\n\n## [0.11.0] - 2026-04-01\n\n### Agent Execution Quality (Claude Code Parity)\n- Raise loop guard block threshold from 3 to 4 -- agents get more room for iterative search\n- Soft synthesis nudge for effort >= 4 (no tool stripping, trust the model)\n- max_output_tokens escalation -- retry up to 3x when response is truncated\n- Tool result pairing guarantee -- synthetic error for orphaned tool_use blocks\n- Micro-compaction of old tool results -- save context tokens on long sessions\n- First-turn tool forcing only for effort >= 6 (trust model for lower effort)\n- Diminishing returns detection -- nudge agent when 3+ turns produce < 500 tokens\n- Message normalization -- merge consecutive same-role messages, strip empties\n- LLM retry with exponential backoff (5 retries, 500ms-16s, jitter)\n\n## [0.9.0] - 2026-03-16\n\nThe \"connected world\" release. Cross-platform identity pairing, voice capabilities, and multi-channel integration.\n\n### Added\n- **Pairing**: Cross-platform identity linking (`adk/pairing.py`)\n - `PairingManager` — SQLite-backed identity linking with 6-char pairing codes\n - Link users across Telegram, Discord, Slack, WhatsApp with 10min TTL codes\n - Canonical session IDs for cross-channel conversation continuity\n - `get_session_id()` returns \"user-{id}\" for paired users\n- **Voice**: Speech-to-text and text-to-speech client (`adk/voice.py`)\n - `VoiceClient` — async STT/TTS/emotion via AitherVoice service\n - Convenience functions: `hear()`, `say()`, `feel()`\n - 6 voice options: alloy, echo, fable, nova, onyx, shimmer\n - Emotion detection with intensity scoring\n- New exports: `PairingManager`, `PairingResult`, `PlatformIdentity`, `VoiceClient`, `TranscriptionResult`, `SynthesisResult`, `EmotionResult`\n\n### Changed\n- `__init__.py` exports expanded with pairing and voice symbols\n\n## [0.6.0] - 2026-03-13\n\nThe \"group mind\" release. Multi-agent group chat, creative tools, and Iris identity.\n\n### Added\n- **Aeon**: Multi-agent group chat engine (`adk/aeon.py`)\n - `AeonSession` — persistent group chat with parallel agent execution\n - 7 presets: balanced, creative, technical, security, minimal, duo_code, research\n - Orchestrator synthesis: Aither summarizes all agent responses\n - Serial execution for Ollama, parallel for vLLM/cloud\n - ConversationStore persistence with `type: \"aeon\"` metadata\n - `group_chat()` one-shot convenience function\n- `aither aeon` CLI command — interactive terminal group chat with color-coded agents\n - `-p/--preset`, `-a/--agents`, `-r/--rounds`, `--no-synthesize` flags\n - `reset` and `quit` commands\n- Server endpoints: `POST /aeon/chat`, `GET /aeon/presets`, `GET /aeon/sessions/{id}`\n- Creative tools in builtin_tools: `image_generate`, `image_refine`, `image_search`, `video_generate`\n- Iris agent identity with visual generation capabilities\n- 48 Aeon tests (data models, presets, chat, context, persistence, server, exports)\n\n### Changed\n- `__init__.py` exports: `AeonSession`, `AeonResponse`, `AeonMessage`, `group_chat`, `AEON_PRESETS`\n\n## [0.5.0] - 2026-03-13\n\nThe \"tenant-ready\" release. Multi-tenant admin, permission grants, safety profiles, and a full setup wizard.\n\n### Added\n- `aither setup` interactive setup wizard with hardware detection, model selection, identity config\n- Strata storage backend: SQLite WAL persistence for conversations, memories, knowledge graphs\n- CLI test runner: `aither test` with auto-discovery and parallel execution\n- Permission grants system for MCP tool access control\n- MCP account management tools\n- LLM provider auto-detection for Ollama and vLLM endpoints\n- Apache-2.0 LICENSE file\n- Elysium desktop sync module\n- Comprehensive CLI test suite (493 tests)\n- Strata storage test suite (972 tests)\n- LLM provider tests (82 tests)\n\n### Changed\n- CLI expanded: `aither setup`, `aither test`, `aither bugreport`, `aither doctor`\n- Strata module rewritten as full local-first storage engine\n- Server startup includes Strata initialization\n- README rewritten with clearer quickstart and architecture docs\n\n### Fixed\n- Dead documentation links\n- Python 3.11 compatibility issues\n- Ruff per-file-ignores configuration\n- Identity provisioning edge cases\n- Docker node-gyp native dependency builds (python3 + build tools)\n\n## [0.4.0] - 2026-03-13\n\nThe \"own your AI\" release. Self-hosted agent OS for people who don't want their data on someone else's servers.\n\n### Added\n- Muse agent identity (creative/artistic generation)\n- Port 8120 to vLLM scan for ExoNodes discovery\n- Public roadmap with milestone-based porting schedule\n- Competitive positioning: self-hosted alternative to cloud-locked AI appliances\n\n### Changed\n- LLM router: compute-aware effort routing (replaces effort-level context gating)\n- README rewritten for sovereignty-first messaging\n- Package description updated\n- Promoted from alpha to stable release\n\n### Fixed\n- Version string consistency between pyproject.toml and __init__.py\n- vLLM port scanning now includes port 8120 (ExoNodes)\n\n## [0.3.1] - 2026-03-09\n\n### Added\n- GraphMemory: SQLite knowledge graph with Ollama embeddings and hybrid search\n- NeuronPool: auto-fire pre-LLM data gathering agents\n- NanoGPT: pure Python char-level transformer with LoRA fine-tuning\n- Safety gates: IntakeGuard (input), LoopGuard (recursion), Sandbox (code exec)\n- Event system: EventEmitter with chat/tool/forge event types\n- Builtin tools: identity-based tool selection\n- ServiceBridge: auto-discovery of AitherOS services\n- Streaming: chat_stream with safety gate integration\n- Auth middleware: Bearer token authentication\n- CLI: `aither init` and `aither serve` commands\n\n### Changed\n- Wired 5 previously disconnected modules into agent loop\n- 522 total passing tests (up from 85)\n\n## [0.3.0] - 2026-03-07\n\n### Added\n- Clean-room agent development kit\n- Multi-backend LLM providers (Ollama, OpenAI, Anthropic)\n- AitherAgent class with @tool decorator\n- SQLite conversation memory\n- OpenAI-compatible server (`aither-serve`)\n- 16 agent identities as package data\n- MCP bridge to mcp.aitherium.com\n- Privacy-centric opt-in telemetry\n- Bug reporting CLI and API\n- FastAPI server with OpenAI-compatible endpoints\n- Hardware auto-detection (5 tiers, 11 profiles)\n- Fleet orchestration and multi-agent coordination\n- A2A mesh protocol\n- Federation protocol for cross-instance agent dispatch\n\n### Initial release\n- 85 passing tests\n- Apache-2.0 license\n"}, "0d2d62c7b9c77248c71407706a66fcef7478f9e4": {"t": 1785748167.0542247, "n": 90193, "h": "4a2c4a9b8fed7df0", "c": "# Changelog\n\nAll notable changes to aither-adk will be documented in this file.\n\n## [2.26.0] - 2026-07-16\n\n### Added — Aeon group-chat web UI pack + POST /aeon/stream SSE endpoint\n\n- **New `aeon` UI pack** (`adk/webui/packs/aeon/index.html`) — group chat for multi-agent\n discussion. Mirrors the `llamacpp` pack's auth + SSE pattern (bearer from URL fragment #k=),\n with a preset selector (balanced/creative/technical/security/minimal/duo_code/research) and\n distinct colored agent messages. Synthesis block highlighted. Agent content is HTML-escaped\n before render. Fully self-contained HTML+CSS+JS, light+dark, no external assets. (No\n temperature/max-tokens controls — Aeon runs a fixed multi-agent round and doesn't thread\n per-turn generation params, so exposing them would be a no-op.)\n- **POST /aeon/stream SSE endpoint** — accepts body {message, preset?, agents?, rounds?,\n session_id?}, emits SSE with: `session_start` (participants + orchestrator), `agent_message`\n per non-orchestrator, `synthesis` (if enabled), `error` on exception, `complete` (total tokens\n + latency). Mirrors /chat/stream structure — like every streaming endpoint here it is gated by\n the server auth middleware **when `AITHER_SERVER_API_KEY` is set** (a local `adk up` with no key\n is unauthenticated by design, same as /chat/stream).\n- **Default UI pack flipped to `llamacpp`** — `resolve_ui_pack_name()` now returns `llamacpp`\n instead of `console` when no env/config is set (new users see the clean chat, not the admin\n SPA; existing users with AITHER_AGENT_UI or saved config unaffected).\n\n### Added — swappable agent web UI packs (llama.cpp-style built-in chat)\n\n- The page an agent serves at `/` is now a **swappable UI pack**, so you can\n drop in, test, and deploy different chat frontends without touching the agent.\n Select with `adk ui set <name>` (persists to `~/.aither/config.json`) or the\n `$AITHER_AGENT_UI` env; `adk ui ls` lists packs, `adk ui path` shows what\n resolves.\n- **New built-in `llamacpp` pack** — a clean, self-contained llama.cpp-style\n chat UI (centered conversation, streaming token-by-token with a cursor, a\n Stop button, a settings drawer for temperature / max-tokens that is actually\n honored end-to-end — `/chat/stream` now forwards them to the provider —\n minimal safe markdown, light+dark). Hits the agent's own gated `/chat/stream`\n with the bearer read from the URL fragment (never sent in the URL). Built-ins\n also include `console` (the full admin SPA, still the default) and `minimal`.\n- **Drop-in custom packs**: a UI pack is just a folder with an `index.html` in\n `~/.aither/ui-packs/<name>/` (override via `$AITHER_UI_PACKS_DIR`) — no rebuild.\n- Resolution is **fail-soft**: an unknown/missing pack falls back\n console → minimal, so `/` is never blank.\n\n### Added — MCP-gateway-first platform access + mesh A2A trust\n\n- **Gateway-first platform access** (`adk/client/_gateway_mcp.py`, `GatewayMCPClient`):\n a self-hosted agent connects its MCP **client** to `mcp.aitherium.com` authenticated\n as the OWNER (device-flow / PAT / ACTA token), RBAC-scoped to that identity — so it can\n use platform tools + `get_secret` **whether or not the node runs local AitherOS\n services**. Fail-closed: no token → client disabled, the agent still runs. Wired into\n the server lifespan (`_connect_gateway_mcp`).\n- **`genesis` LLM backend**: `adk backend set genesis` points the agent brain at a local\n Genesis (`http://localhost:8001/v1`, model `workflow`) — the 5090/controller pattern\n where inference is served by the local fleet, not the cloud gateway or a raw vLLM.\n- **`adk mesh` command**: `adk mesh onboard` drives the Conductor 5-step onboard\n (`/v1/mesh/onboard` → registers this node's public key, returns the `endpoint:mesh`\n trust token); `adk mesh ls` lists peer nodes/agents (AitherMesh `/nodes` + Directory)\n with their A2A cards + invoke URLs for discovery.\n\n### Security — A2A inter-agent trust enforcement (fail-closed)\n\n- **`adk/a2a_trust.py`** (new): Ed25519 signature verification + trusted-key check\n (`AITHER_A2A_TRUSTED_KEYS`), opt-in via `AITHER_A2A_REQUIRE_TRUST` (default `false` for\n backward compatibility; `audit` logs untrusted, `true` rejects).\n- **Fixed a fail-OPEN in the inbound A2A handler** (`adk/a2a.py`): the trust check\n previously ran *only when signature headers were present*, so an unsigned request\n bypassed `AITHER_A2A_REQUIRE_TRUST` entirely. Now genuinely fail-closed — required mode\n with no/invalid signature → `403`.\n- **Sovereign template no longer scaffolds `verify=False`**: the generated\n `services/llm.py` now uses the project's own `tls_verify()` policy helper for every\n provider call instead of hardcoding disabled TLS verification.\n- **`adk/_tls.py`**: disabling verification via `AITHER_TLS_VERIFY=false` now emits a\n one-time loud warning (was silent) — the escape hatch stays, the silence goes.\n\n## [2.25.1] - 2026-07-15\n\n### Fixed — self-hosted OpenAI-compatible backends honor a custom base URL\n\n- `adk backend set llamacpp --base-url http://localhost:8090/v1 --model <id>` (and\n `vllm`/`lmstudio`, and the `AITHER_LLM_BASE_URL` env) now actually point the agent\n at YOUR server. Previously the base URL was saved but never read, so an explicit\n `--backend llamacpp` fell back to the provider's public API default (openai.com).\n New `Config.llm_base_url` field + saved-config `inference_url` wiring; the explicit\n backend path in `LLMRouter` now passes it for the local OpenAI-compatible family.\n This is what lets a self-hosted node run its OWN local model (e.g. a PrismML\n Bonsai llama.cpp) as the agent brain instead of ollama/cloud.\n\n## [2.25.0] - 2026-07-15\n\n### Added — node_bootstrap tool pack: bootstrap LLM inference on any hardware\n\n- **New bundled tool pack `node-bootstrap`** (`adk/toolpacks/node_bootstrap/`):\n seven agent tools — `node_detect_hardware`, `node_resolve_recipe`,\n `node_plan_deployment`, `node_apply`, `node_enroll`, `node_register_backend`,\n `node_verify` — plus a CLI shim (`python -m adk.toolpacks.node_bootstrap\n detect|resolve|plan|apply|enroll|register|verify`). Detect a box's hardware,\n resolve a deployment recipe, actually deploy the inference engine (docker\n compose / native / delegate), enroll with a control plane, register the\n backend, and live-verify a completion.\n- **9 hardware recipes** shipped in the wheel: `cpu-1bit-llamacpp`, `cpu-ollama`,\n `cuda-vllm-8gb/24gb/40gb`, `cuda-dual-stack-32gb` (32GB-card co-resident\n stack), `unified-memory-vllm` (DGX/Grace-class), `metal-ollama` (native —\n Docker on macOS has no Metal passthrough), `cloud-api` fallback. Resolution\n scores hardware against min/max VRAM bands, RAM, cores and unified memory;\n ties prefer higher tiers and self-contained recipes over fleet delegates.\n- Security posture: enrollment and backend registration are fail-closed\n (missing token/URL → error dict, never anonymous, never `verify=False`);\n tokens are redacted in outputs; all endpoints come from args/env\n (`AITHER_CONTROL_PLANE_URL`, `AITHER_GENESIS_URL`, `AITHER_AUTH_TOKEN`).\n\n## [2.24.0] - 2026-07-14\n\n### Changed — sync-family consolidation\n\n- **Removed dead code:** `adk/sync.py` (528 lines) deleted — it was unreachable,\n shadowed by the `adk/sync/` package (`import adk.sync` always resolved to the\n package; nothing loaded the module by path — verified).\n- **Grouped the sync domain modules into the `adk.sync` package:**\n `brain_sync`→`adk.sync.brain`, `files_sync`→`adk.sync.files`,\n `lockbox_sync`→`adk.sync.lockbox`, `secrets_sync`→`adk.sync.secrets`,\n `session_sync`→`adk.sync.sessions`, `settings_sync`→`adk.sync.settings`,\n `sync_watermark`→`adk.sync.watermark`. All in-tree importers updated. No\n back-compat shims were left because **zero external/monorepo code imported the\n old root paths** (verified) — a clean move, not a shimmed one. **Breaking only\n for code that imported `adk.<name>_sync` directly** (none known).\n\n### Docs — MCP module layering clarified\n\n- `adk.mcp` (enterprise AitherOS gateway client: auth/billing/token-tracking) and\n `adk.core.mcp` (minimal generic JSON-RPC adapter for any MCP server) are\n DISTINCT layers, not duplicates — each now carries a LAYER/ROLE/See-also\n docstring so they are not mistakenly consolidated.\n\n## [2.23.2] - 2026-07-14\n\n### Fixed — CompletionGate no longer false-fails on correct results\n\n- **Tolerant judge JSON extraction.** The LLM judge parsed its reply with `json.loads(text[first\"{\":last\"}\"])`, which raised `JSONDecodeError` (\"Extra data\") whenever the model appended prose after the verdict or emitted a second object — and `verify()` turned that into a fail-closed verdict, so a genuinely completed task reported `unverified`. New `_first_json_object()` uses `JSONDecoder.raw_decode` from the first `{` that begins a valid object and ignores trailing content (prose, markdown fences, extra objects). A judge that answers with no parseable JSON still fails closed (never a soft pass), now with a clear reason. The same extractor backs `_derive_criteria`.\n- **Bare code-like tokens are hard-checked.** A natural criterion like `output contains ADK_AUTO_OK` (no quotes) previously fell through to the judge; `hard_checks` now also matches bare identifier/code shapes (contains an underscore, or 4+ CAPS/digit chars), with a stopword guard so a descriptive uppercase word (`OUTPUT`, `CONTAIN`) never becomes a false required token. Quote a token to force it regardless.\n- +9 tests (24 total), including the exact trailing-data regression.\n\n## [2.23.0] - 2026-07-14\n\n### Removed — dead modules and internal integrations\n\n- **`adk.swarm`, `adk.provisioning_tools`, `adk.session_sync_integration`, `adk.addon_metering`** — four orphan modules with zero public usage have been deleted. These were internal prototypes kept for legacy monorepo consumers; public packages have not imported them since 2.21.0 (tracked as D-348). **Breaking for anyone directly importing these modules** (import will fail). They are no longer exempt from the orphan-module check.\n- **`adk.aither_bridge`** — the AitherOS-internal IRC ↔ chat gateway bridge is no longer shipped in the public aither-adk package (stripped at sync time). All public importers already degrade behind `except ImportError` (in `adk.server` and `adk.builtin_tools`), so public packages remain unaffected. The module stays in the private AitherOS monorepo for internal use.\n- **Identity document trim** — removed internal agent names from `adk/identities/aither.yaml` delegation guidance, replacing with role-generic wording (\"delegate code review, refactoring, security analysis, performance and testing to specialist agents when available\"). Effort-tier routing philosophy remains unchanged and is product design, not a leak.\n\n### Changed — genericized customer/product names in help text\n\n- CLI help/examples no longer name specific customer deployments (`--scope` template list and `deploy agent` name example use generic placeholders). Functional deploy paths are unchanged.\n\n## [2.22.1] - 2026-07-14\n\n### Fixed — real bugs surfaced by reviving the public CI (red since Jul 5)\n\n- **TLS**: bundled toolpacks (arc-brainpack, cloudflare) disabled certificate\n verification on gateway/vault calls; all now route `adk._tls.tls_verify()`\n (verified by default, internal CA bundle when present).\n- **`system_prompt` contract**: bundled-pack `[PACK DIRECTIVES]` were appended\n even to an explicit `system_prompt`; an explicit prompt now wins wholesale.\n- **Headless secrets**: `_derive_key()` used `os.getlogin()`, which raises\n `OSError` without a controlling terminal (CI, daemons, containers) — falls\n back to `getpass.getuser()` (same value on interactive boxes, so existing\n secret files keep decrypting).\n- **`pack_verifier`**: missing `cryptography` crashed with `UnboundLocalError`\n in the exception handler instead of failing closed; now degrades gracefully\n (and `cryptography` was added to the dev extra so CI tests the real path).\n- **`fleet_manager._kill_pid` (POSIX)**: reported success immediately after an\n asynchronous SIGTERM and never reaped children (zombies read as alive); now\n polls, escalates to SIGKILL, and reaps.\n- **Identity**: `aither` regained the `creative` tool category (dropped\n accidentally in a 2.7.0 WIP sweep).\n\n## [2.22.0] - 2026-07-14\n\n### Changed — internal toolkit relocation\n\n- **`adk.platform` relocated** — the internal aither-platform toolkit (ComfyUI/image-gen\n pipelines, fleet agent clients, parallel CLI/UI/tools stack) has been moved out of the\n adk SDK tree to the standalone internal `aither-platform` package at\n `AitherOS/agents/aither_platform/`. The public package remains unaffected (it already\n excluded `adk.platform` since 2.21.1). Internal AitherOS agent builds now install\n `aither-platform` separately. `adk platform` falls through to the same \"not available\"\n message on public installs; internal builds work via the relocated package.\n\n### Removed — backward-compat re-export deleted\n\n- **`adk.gateway` module removed** — the backward-compatibility re-export of `GatewayClient`\n from `adk.gateway` has been removed. Use `from adk.client import GatewayClient` instead\n (the established public API since 2.21.0). All internal importers have been updated.\n\n## [2.21.1] - 2026-07-14\n\n### Removed — internal toolkit and license-gated product no longer ship publicly\n\n- **`adk.platform`** — the internal aither-platform toolkit (ComfyUI/image-gen\n pipelines, fleet agent clients, a parallel cli/ui/tools stack) is no longer\n included in the public repo or the PyPI wheel. It was internal-only code that\n had been riding along since the aither-platform merge. `adk platform` now\n reports \"Platform toolkit not available\" on public installs; internal AitherOS\n builds are unaffected.\n- **`adk.formbridge`** — the FormBridge form-automation product is license-gated\n (PROFESSIONAL+); its implementation no longer ships in the free package. All\n import sites (`builtin_tools`, `server` route mount) already degrade behind\n `except ImportError`, so nothing else changes behavior.\n- Product test suites (`test_formbridge.py`, `test_encounter_keying.py`,\n `test_flow_tools.py`) are excluded from the public sync alongside them.\n\n## [2.21.0] - 2026-07-14\n\n### Added — Completion gate: verified-or-retried task execution\n\n- **`adk.gate.CompletionGate`** — a composable wrapper that closes the \"silent\n success\" gap: an agent's ReAct loop returns whatever the model produced and\n calls it done (`finish_reason = stop/max_steps/length`), which is a *liveness*\n signal, not a *completion* one — so an agent can \"succeed\" while doing nothing.\n `CompletionGate` runs the agent, verifies the result against acceptance\n criteria, retries with the failure fed back, and returns the response with\n `finish_reason` set to `verified` or an honest `unverified`. Never a false pass.\n - **Hard-first, un-soft-passable checks** (`adk.gate.hard_checks`): a filesystem\n path a criterion says must exist is checked on disk; a required quoted token\n must appear in the output. Only genuinely subjective criteria fall through to\n an optional LLM judge (reuses the agent's own model), which **fails closed**\n if unavailable.\n - **`gated_run(agent, task, criteria=..., max_retries=2)`** — one-shot helper.\n - `auto_criteria` (LLM writes the machine-checkable definition-of-done from the\n task), a `verifier=` override for custom checks (tests-pass / HTTP probe), and\n a `requires_action` short-circuit so it never fights human-approval pauses.\n - Compose it onto any agent exposing `run(task) -> resp{.content, .finish_reason}`\n (`AitherAgent`, `core.Agent`, or a thin adapter over a remote dispatch).\n - Exported from the top-level package: `from adk import CompletionGate, gated_run`.\n\n## [2.19.0] - 2026-07-12\n\n### Added — Eve agent importer + durable A2A tasks + canonical agent discovery\n\n- **`adk pack import <eve_agent_path>`** — convert external Eve agents to AitherADK packs\n (from GitHub or local filesystem). Maps Eve structure (instructions → system prompt,\n agent.ts → agent.yaml, skills/*.md → skills, connections/*.ts → MCP, tools/*.ts →\n tools/node verbatim) to AitherADK format with a single command. Eve agents (compiled\n manifest format, schema v35) become installable packs compatible with `adk pack install`.\n- **Durable A2A task lifecycle (`TaskManager` backed by FileStore)** — agent-to-agent\n tasks now survive container restarts. Tasks persist as append-only JSONL\n (`.adk/tasks.jsonl`); in-memory dict for fast access; `create_task()` / `update_status()` /\n `add_artifact()` / `add_message()` all save to disk. Google A2A v0.3.0 spec compliant.\n- **Canonical agent card discovery** — `A2AServer.mount()` now exposes:\n - `GET /.well-known/agent-card.json` — Agent Card (canonical; contains agent identity,\n capabilities, skills, endpoints)\n - `GET /.well-known/agent.json` — Agent Card (legacy, redirects with 308 to canonical path)\n - `POST /a2a` — JSON-RPC 2.0 for message send/task lifecycle\n - `GET /a2a/tasks/{id}/subscribe` — SSE streaming task updates\n- **Four built-in agent packs now complete and catalogued**:\n - `hermes` — architecture review & trade-off analysis\n - `openclaw` — web research & source verification\n - `claude-code` — feature development & debugging\n - `analyst` — structured data inference & anomaly detection\n All discoverable via `adk packs` / `adk install pack:<name>`.\n- **Tolerant tool-call JSON parsing** — malformed tool JSON (extra properties, missing\n name/arguments, string-encoded nested args, missing keys) is now gracefully degraded\n instead of silently failing. `_parse_tool_json()` accepts both `\"arguments\"` and\n `\"parameters\"` keys, coerces string-encoded args, and skips unparseable calls instead\n of crashing the turn.\n\n### Known limitations\n\n- Eve agent importer: remote GitHub fetching not yet implemented (local paths only).\n- Durable task store: currently JSONL (suitable for thousands of records, not millions).\n\n## [2.18.0] - 2026-07-10\n\n### Added — AitherShell command center + Claude Code session management\n\n- **`aither sessions`** — Claude Code session manager: interactive full-screen\n browser (type-to-filter, transcript preview, deep full-text `search`, resume\n **in the current terminal** or as Windows Terminal tabs), crash guard\n (`guard install` = at-logon watchdog that snapshots live sessions and\n auto-restores the whole set after a terminal crash), and `ingest [--watch]`\n (incremental, secret-guarded sync of session conversations into the local KB\n / CompanyBrain via the standard ingest pipeline).\n- **`aither hq`** — command-center dashboard: per-service fleet health, LLM\n queue (depth/VRAM/models), Pulse alerts, live session count, inbox unread —\n auto-refreshing, with one-key jumps into chat, sessions, inbox, agents,\n brief, watchtower, and docker recovery.\n- **`aither inbox`** — unified queue over CommCore mail, Relay\n mentions/notifications, and Pulse alerts; open/mark-read/DM-reply from the\n terminal. Every source degrades independently.\n- **`aither agents`** — roster console + `agents ask -e <1-10> <agent> <q>`\n (effort-tiered ask via Genesis, with automatic `/chat/stream` fallback when\n the `/agent/sync` execute gate rejects host callers) + forge dispatch\n inspection + routine health.\n- **`aither palette`** — universal fuzzy picker across actions, sessions, and\n services; **`aither brief`** — renders Atlas's executive briefing in the\n terminal; **`aither watch`** — fleet watchtower with named wedge-signature\n detection (docker-WSL wedge, LLM-queue stall, crash-loop uptime regression)\n and optional docker auto-recovery.\n- All fleet reads go through a shared fail-soft `FleetClient` (internal-CA\n TLS via `adk._tls`, dashboard-grade timeouts, per-source degraded states).\n- Node shell-cli (`@aitherium/shell-cli` 1.13.0): `aither\n sessions|hq|inbox|palette|brief|watch|agents|docker` now pass through to the\n Python shell instead of being intent-classified as chat prompts.\n\n## [2.17.1] - 2026-07-05\n\n### Fully self-service mesh join — no customer key handling\n\n- **Auto-join headscale off the onboard response.** The conductor now auto-issues a\n headscale pre-auth key for NAT'd nodes and returns it in the `/v1/mesh/onboard`\n response; `mesh.join` consumes it and brings up the tunnel automatically. A customer\n runs one command — they never mint, fetch, or set a headscale key. Explicit\n `--headscale-key`/`AITHER_HEADSCALE_AUTH_KEY` still take precedence.\n- **Security:** `_tailscale_up` now redacts the auth key from all logs and exceptions\n (a failed `tailscale up` previously echoed the full argv, leaking the key into logs).\n\n## [2.17.0] - 2026-07-05\n\n### Added — Headscale mesh transport + local Qdrant + AitherConnect onboarding\n\n- **Headscale NAT-friendly mesh transport.** When raw WireGuard UDP:51820 is not\n viable (customer boxes behind NAT, corporate firewalls, CGNAT), `adk mesh join\n --headscale` routes the mesh tunnel through Tailscale's Headscale control plane.\n The overlay IP is still Conductor-assigned (10.77.0.0/16); Headscale provides\n only the transport layer. Configured via `AITHER_MESH_TRANSPORT=headscale`,\n `AITHER_HEADSCALE_URL` (default: https://headscale.aitherium.com), and\n `AITHER_HEADSCALE_AUTH_KEY` (pre-generated key). Automatic fallback to raw\n WireGuard if Headscale setup fails.\n- **Local Qdrant vector DB provisioning.** `adk stack` now provisions Qdrant\n locally when `AITHER_VECTOR_DB=qdrant` is set, enabling offline-first RAG\n without external dependencies. Tiered: local Qdrant in-fleet, cloud Nexus\n for enterprise multi-tenant deployments.\n- **Conductor URL public fallback.** `adk mesh join` resolves the internal\n `aitheros-conductor:8193` hostname; if unreachable (e.g., self-hosted nodes),\n it falls back to the public `conductor.aitherium.com:8193` endpoint,\n eliminating bootstrap configuration friction.\n\n### Documentation\n\n- **Self-hosting runbook.** New `docs/SELF_HOSTING.md` covers deploying AitherOS\n on customer infrastructure with Qdrant + mesh + AitherConnect onboarding.\n- **Mesh transport constraints documented.** Raw WireGuard requires public\n UDP:51820 endpoint (cloud instances, dedicated hardware); Headscale for\n NAT'd networks. Noted in CLI help and module docstrings.\n\n## [2.15.0] - 2026-07-04\n\n### Added — built-in web admin console + portal-profile settings sync\n\n- **Web admin console.** The page `adk up` opens (served at `/` and `/ui`) is now a full,\n self-contained console — Chat plus six admin tabs: **Backend** (view/switch LLM provider,\n set keys, test connection), **Packs** (list/enable/disable/reload tool packs),\n **Sessions** (browse/delete conversations), **Logs** (redacted agent-log tail),\n **Graph** (knowledge-graph search/neighborhood/stats), and **MCP** (add/remove external\n MCP servers). No build step, no CDN — a single packaged HTML asset. The original minimal\n streaming chat page stays at `/chat` for back-compat.\n- **Admin API (`adk/admin_api.py`).** ~20 endpoints under `/admin/*`, all bearer-gated by the\n existing middleware (never in `_skip_auth_paths`). Operates on the live agent — LLM backend\n swap (`LLMRouter.switch_backend`), pack reload, and MCP registration take effect without a\n restart.\n- **Settings sync (`adk/settings_sync.py`).** The user's portal profile is the source of\n truth: on startup the agent pulls `preferences.adk` and applies it over the local\n `~/.aither/config.yaml` cache; admin-console edits push a debounced snapshot back to\n `PUT /api/settings/preferences`. Provider API keys and MCP auth headers are device-local\n secrets and are never included in the pushed snapshot. Opt out with `AITHER_SETTINGS_SYNC=false`.\n\n### Security\n\n- **Timing-safe token comparison.** The server bearer check now uses `hmac.compare_digest`\n (the token is reachable over the public tunnel; a byte-wise `!=` leaked it to timing attacks).\n- **MCP add is SSRF-guarded + confirmed.** Adding an external MCP server is a two-step\n prepare→confirm flow; URLs resolving to loopback / private / link-local / metadata addresses\n are rejected (override with `AITHER_ALLOW_PRIVATE_MCP=1` for trusted local servers).\n- **Config editor is allowlisted.** `PATCH /admin/config` writes only safe scalar prefs and\n hard-denies fields that could execute code or redirect traffic at startup (e.g.\n `aither_toolpack_dirs`, backend base URLs).\n- **Log tail is scrubbed.** Bearer tokens, `#k=` fragments, and `sk-`/`aither_sk_` keys are\n redacted from the log-tail endpoint; the tunnel log is excluded.\n\n## [2.14.8] - 2026-07-04\n\n### Added — `adk up` for normal humans (no API key, no prereqs, live chat)\n\n- **Hosted-brain default — no API key required.** When there's no local model and no\n provider key, `adk up` signs in (device-flow) and routes inference through the gateway\n using the portal token (`AITHER_LLM_BACKEND=gateway` + bearer). A non-technical user\n never needs to obtain or paste an LLM API key. Explicit `--provider`/local model still\n take precedence; unattended `--yes` with no token/backend fails cleanly (exit 3).\n- **cloudflared auto-download.** If the tunnel binary is missing, `adk up` downloads the\n right platform asset to `~/.aither/bin` (SHA256-verified against the release; opt out with\n `AITHER_CLOUDFLARED_REQUIRE_CHECKSUM=0`) instead of exiting with an install hint.\n- **Built-in streaming chat page.** The agent now serves a self-contained chat UI at `/`\n (and `/chat`) with a live \"thinking…\" indicator and token-by-token streaming — so a human\n has somewhere to talk to it with immediate feedback instead of a blank wait. `adk up` opens\n it locally and passes the callback bearer in the URL fragment (`#k=…`), so `/chat/stream`\n stays authenticated (not an open proxy) with nothing to paste.\n- **Human-readable failures.** `adk up` errors now carry a plain-English `next_action`\n (kept machine-readable in `--yes` JSON).\n\n### Fixed\n\n- `adk up` chat link pointed at a non-existent `/settings/agent` route; it now points at the\n agent's own streaming chat page.\n\n## [2.14.7] - 2026-07-04\n\n### Added\n\n- **`adk up` — one command to run a persistent, fleet-connected agent.** Collapses\n the scattered onboarding surface (`host`/`quickstart`/`connect`/`login`/`setup`)\n into a single command that starts `aither-serve` (single agent, identity `aither`\n by default), opens a Cloudflare quick-tunnel for fleet reachability, registers with\n the portal, and installs a reboot-persistent autostart entry (Windows Task Scheduler\n / systemd-user / launchd). Detaches by default so the terminal is freed; `--foreground`\n blocks. Reuses the existing device-flow login, cloudflared discovery, provider-key\n store, and `_preflight_check` backend detection.\n- **Fully non-interactive path for autonomous agents.** `adk up --yes` (or any\n non-TTY invocation) takes ZERO prompts — everything resolves from flags, env, and\n saved config — and emits a single machine-readable JSON status line with meaningful\n exit codes (`0` ok, `2` bad-input, `3` no-backend, `4` tunnel-missing, `5` register-failed).\n Degrades to local-only (warn, not fail) when a portal token is absent unless\n `--require-register` is set.\n- **`adk down` / `adk status --json` companions.** `down` stops the agent + tunnel,\n best-effort deregisters, and removes the autostart entry; `status` reports the running\n agent (liveness + `/health` + tunnel + registration), with `--json` for agents/CI.\n- **`adk stack`** — the previous `adk up` behaviour (supervise the Room + Ollama consumer\n stack) now lives here; `adk up` is the connected-agent command.\n\n### Internal\n\n- New `adk/agent_daemon.py`: detached process spawn, status file\n (`~/.aither/adk-up.json`), pid liveness/teardown, and cross-platform autostart install.\n\n## [2.14.6] - 2026-07-04\n\n### Added\n\n- **`adk.preflight` — self-bootstrap capability probe.** `run_preflight(agent,\n spec)` wakes an agent up knowing what it is actually plugged into: a bounded\n (1.5s) parallel `LivenessProbe` over the router's resolved backends\n (primary/reasoning), the embeddings winner, remote MCP tools, the structured-ML\n / `/ml/teach` endpoints, and voice — returning an honest `CapabilityReport`.\n Status vocabulary distinguishes reachability from entitlement\n (`OK`/`MISSING`/`UNREACHABLE`/`AUTH`/`TIER_DENIED`/`UNSUPPORTED`), a hosted slot\n never prints a bare `OK`, and a required-but-unsatisfied slot aborts before the\n loop instead of blind-firing. Headless task/rules resolution + an allowed-roots\n policy binding (`builtin_tools.set_allowed_roots`, which resets the memoized\n cache so the override actually enforces).\n- **Multimodal (image) messages.** `Message.content` now accepts an OpenAI-style\n content-part list (`str | list[dict]`), passed through verbatim to the\n OpenAI-compatible body (gateway / vLLM / OpenAI). New `adk.llm.multimodal`\n helpers — `image_message()`, `image_content_parts()`, `to_image_url()` — build\n the parts and encode local image bytes/paths into `data:` URIs.\n- **Real vision preflight probe.** The vision slot is no longer unconditionally\n `UNSUPPORTED`: it sends a discriminable image (a solid red square) and requires\n the model to name the color, so a blind/text-only model cannot false-pass. `OK`\n only on a real round-trip; `AUTH` on 401/403; `UNSUPPORTED` when there is no\n OpenAI-compatible provider or the model cannot see the image.\n\n## [2.14.5] - 2026-07-04\n\n### Added\n\n- **`adk.reasoning.mcts` — generic Monte-Carlo Tree Search library.** A reusable\n MCTS engine (ported from the internal `UnifiedMCTS`, with all internal couplings\n removed) that any agent can drive over its own environment:\n - `MCTSEnvironment` protocol (`get_state_hash` / `get_actions` / `step` /\n `evaluate` / `clone`) — implement it and search.\n - Three **optional, default-off** model seams — `TransitionModel`, `PolicyModel`\n (PUCT priors), `ValueModel` — so a learned world-model / policy / value can be\n injected without touching the core; with all three `None` the engine is a plain\n UCT search.\n - `ObservedTransitionModel` adapter — a cheap online lookup world-model (hit =\n exact stored transition; miss = identity + negative \"unknown\" reward +\n `is_uncertain`, biasing exploration toward learning), with JSONL save/load for\n cross-run reuse.\n - `UnifiedMCTS(config).search(env)` returns an `MCTSResult` (best action, value,\n confidence, principal-variation path) and an optional `MCTSTrace`\n (visit-distribution policy target) for distillation.\n- **Self-bootstrapping agent machinery.** Build an agent from a declarative spec\n instead of bespoke wiring:\n - `adk.bootstrap.build_agent_from_spec(path) -> (AitherAgent, RunCtx)` — resolves\n prompts, activates required/optional tool packs, and carries a `memory_map`\n (a `node_type -> (role, tier)` override for memory writes, never a gate).\n - `adk.prompts.PromptBridge` — resolves an agent's prompt bindings from files\n (relative to the spec) or `AitherPrompts` dot-keys (optional import).\n - `adk.packs.PackActivator` — eager activation of installed tool packs by name\n over `register_tool_packs`; a required pack that registers zero tools raises\n `PackUnavailable`, optional packs soft-degrade.\n - `python -m adk.bootstrap.generic_agent <spec.yaml>` inspects a spec (resolved\n name, activated packs + tool counts, prompt keys, memory map) without running a\n live loop.\n- **`deep_research` tool pack** — a full multi-engine web-research workflow as\n `dr_*` agent tools (keyless search, page fetch + cache, site mirror, a citable\n findings knowledge graph, and markdown/PDF/DOCX report generation), activatable\n by name via the tool-pack loader.\n\n## [2.14.4] - 2026-07-04\n\n### Added\n\n- **`structured_ml` tool domain** — zero-shot structured-data inference for agents,\n backed by TabFM (tabular classification/regression) and TimesFM (time-series\n forecasting). Three tools:\n - `tabular_classify(support_rows, target, query_rows)` — classify rows from a\n labeled support set, in-context (no training step; up to 10 classes).\n - `tabular_regress(support_rows, target, query_rows)` — predict a numeric target.\n - `timeseries_forecast(series, horizon)` — forecast future values of a series.\n\n The tools POST to a structured-ML inference service resolved from\n `AITHER_STRUCTURED_ML_URL` (default `http://localhost:8192`), reject non-http(s)\n URLs, and summarise oversized responses to protect the agent's context.\n- **`Capability.STRUCTURED_INFERENCE`** capability token and a new **`analyst`**\n identity/pack that ships the `structured_ml` domain by default. The domain is\n opt-in — only identities that enable the `structured_ml` category get the tools.\n\n## [2.14.3] - 2026-07-03\n\n### Fixed\n\nAll four bugs below were found by actually booting `adk run` against a real\nchat model and driving a real tool-calling request end-to-end (not inferred\nfrom reading the code) — each one silently broke the \"local agent uses its\nconfigured model and its tools actually work\" path.\n\n- **`AITHER_MODEL` / `config.model` silently ignored by the OpenAI-compatible\n backend**: `LLMRouter.__init__` never read `config.model` into `self._model`\n unless the caller passed `model=` directly to the constructor — which nothing\n did. Every explicit model choice (env var, config file) was dropped, the\n provider fell back to a hardcoded per-backend default (`gpt-4o-mini` for the\n `openai` backend), and an unrecognized model name got silently remapped\n server-side to the wrong backing model. Concretely: setting\n `AITHER_MODEL=aither-orchestrator` had no effect — every request actually\n went to a different model that was never meant to be called directly and\n can't reliably use tools.\n- **`OPENAI_BASE_URL` ignored for explicit `--backend`/`AITHER_LLM_BACKEND`\n selection**: `LLMRouter._auto_detect()`'s explicit-backend branch hardcoded\n `base_url=None` when constructing the provider, instead of reading\n `config.openai_base_url` (which two other call sites in the same file\n already did correctly). Any self-hosted OpenAI-compatible endpoint\n (MicroScheduler, vLLM, LM Studio) configured via `OPENAI_BASE_URL` was\n silently ignored in favor of `https://api.openai.com/v1`.\n- **Tool-call steering never engaged on default-effort chat**: the retry that\n nudges a model back into structured tool-calling when it responds with prose\n instead of a function call was gated behind `effort >= 6`. Default `effort`\n for a plain `agent.chat(message)` call resolves to `5`, so the safety net —\n built specifically for this failure mode — never fired for default requests.\n Also now forces `tool_choice=\"required\"` on the retry instead of relying on a\n text nudge alone.\n- **Every non-string builtin-tool parameter silently typed as `\"string\"` in\n the generated tool schema**: `builtin_tools.py` uses\n `from __future__ import annotations`, so `_extract_parameters()` in\n `tools.py` was reading postponed (string) type hints (`\"int\"`, not `int`)\n straight out of `fn.__annotations__`, which never matched\n `_type_to_schema`'s type-object keys and silently fell through to\n `{\"type\": \"string\"}`. Every builtin tool with an `int`/`float`/`bool`/`list`\n parameter was affected — e.g. `file_read(start_line: int, end_line: int)`\n advertised both as strings, so a model correctly calling the tool with\n `\"start_line\": \"0\"` crashed the actual implementation with\n `unsupported operand type(s) for -: 'str' and 'int'`. Fixed by resolving\n hints through `typing.get_type_hints()` instead of the raw annotations dict.\n `_coerce_arguments()` — the runtime safety net that's supposed to coerce a\n model-supplied `\"0\"` string into `0` before a tool call executes — had the\n exact same raw-`__annotations__` bug and silently no-opped for every builtin\n tool; fixed the same way.\n- **Streaming tool-loop could hang a connection forever**: `chat_stream()`'s\n fallback to the sync tool loop (tool calls can't stream token-by-token) had\n no timeout, so a model that didn't cleanly terminate a tool-calling round\n trip left the SSE connection open indefinitely with zero bytes sent. Bounded\n with a 60s `asyncio.wait_for`; on timeout the stream yields a clear message\n instead of hanging.\n\n## [2.14.2] - 2026-07-03\n\n### Fixed\n- **AitherShell session amnesia**: the REPL generated a fresh `session_id` for\n EVERY message, so conversation history never followed across turns (the\n server stored each turn under a different session). One stable id is now\n minted per shell run; `/resume` and `/new` semantics unchanged, and the\n cross-restart auto-restore of the last session now actually works.\n\n## [2.14.1] - 2026-07-02\n\n### Added\n- **Account-tied licensing via device-flow login** — `adk login` now persists the account\n license returned by the device-flow handshake to `~/.aither/license.json` (base64\n `license_key` → `{payload, signature}` envelope), so entitlements from the authenticated\n account carry into every later `adk` invocation without re-auth. Pairs with the fleet-side\n node tool-pack gate that grants packs by plan-tier rank.\n- **`can_use_formbridge` / `can_use_untether` entitlements** (PROFESSIONAL+ rank ≥ 3) backing\n the FormBridge and UNTETHER agent tool-packs.\n\n### Fixed\n- `adk` CLI: guard the optional `memory_graph` singleton (was a latent `NameError` on the\n `/stats`, `/memory`, and save paths when memory was not constructed).\n\n## [2.14.0] - 2026-07-02\n\n### Added\n- **`adk.memory_wiki` — LyraWiki-style self-managed semantic knowledge** (opt-in; nothing constructs it by default): `MemoryWiki(graph_memory)` maintains curated wiki ARTICLES as `wiki_article` nodes (title/slug, LLM-owned markdown with `[[wikilinks]]`, embedding, durable tier, `CONSOLIDATED_FROM` edges to source memories, governance-ledgered revisions).\n - `consolidate(llm, since=, budget=)` clusters unconsolidated episodic/fact nodes (embedding + keyword), drafts/UPDATEs articles via the INJECTED llm callable (`str -> str` or `messages -> str`, sync or async — no hard model dependency), cites source node ids, marks contradicted sources superseded, then DEMOTES consolidated sources to a fast-decay tier via `promote()` (represented knowledge doesn't need raw copies).\n - `lint(llm=None)` — deterministic health checks (orphan wikilinks, empty/stale articles, live-but-contradicted pairs) + an optional LLM audit pass.\n - `prune(relevance_floor=0.05, hard_delete_after_days=14)` — article relevance = tier freshness × reinforcement (reinforce-on-recall) + link-degree to LIVE memories; below the floor → reversible governance tombstone; entombed past the window → **HARD-DELETE** (the tombstone snapshot itself is purged — content and embedding irrecoverable; the true-deletion path). A node is never deleted without a tombstone first.\n - `recall(query, limit)` — article-first RAG (curated articles rank above raw nodes) that reinforces returned articles.\n- **`adk.routines` — agent heartbeat + self-programmed routines**: `RoutineStore` is a durable registry (`~/.aither/routines/{agent}.json`, `AITHER_DATA_DIR`-aware, injectable path) of cron-scheduled self-prompts `{name, cron, instruction, enabled, last_run, last_result, tags}` that rehydrates into the existing `adk.cron.CronScheduler` on `start()`. A fire runs `agent.chat(instruction)` with the agent's full toolset — the agent programs its future self with its own adk. Guardrails: `max_routines` (12), a 5-minute min fire interval per routine, a per-fire timeout, and every result ledgered (truncated) to `last_result`. Direct-callable routines (`register_direct`) fire a bound method instead of a self-prompt. Self-management tools (`routine_create/list/update/pause/resume/delete/run_now`) ship as OpenAI tool defs + handlers (`build_routine_tools` / `register_routine_tools` / `routine_tool_defs`); the handlers ONLY touch the RoutineStore — they can never modify agent config or safety-relevant settings (the leash principle).\n- **`AitherAgent(routines=True, memory_maintenance=True)`** (both default **False** → the default agent is byte-identical): `routines=True` builds the RoutineStore + scheduler, registers the self-management tools, and starts the heartbeat lazily on the first `chat()` (or via explicit `start_routines()`). `memory_maintenance=True` registers DEFAULT routines — `wiki_consolidate` (every 2 h), `wiki_lint` / `wiki_prune` / `graph_sweep` (daily) — as DIRECT method fires so memory upkeep runs even on tiny models, while staying visible/manageable via `routine_list`.\n- `TombstoneStore.purge(tombstone_id)` — the hard-delete endpoint: irrecoverably removes a tombstone snapshot and rewrites the persisted store without it (consumed by `memory_wiki.prune`).\n\n## [2.13.6] - 2026-07-02\n\n### Added\n- **Qdrant backend for GraphMemory dataplane sync — TRUE two-way, verified live.** Set `AITHER_FLEET_QDRANT_URL` and the sync uses Qdrant (`upsert` + `scroll` + filtered `search`) instead of Nexus. This is what actually delivers cross-agent memory sharing: a node is upserted with its OWN embedding under a deterministic per-(tenant,node) point id (idempotent re-push), and `fleet_pull` reliably SCROLLS a tenant's points back — the enumerate Nexus lacks (its `/search` doesn't return ingested docs and `/export` needs lancedb, which has a hard dependency conflict in the fleet). **Proven live with 2 agents:** agent-1 ingests → auto-syncs to the tenant dataplane; a fresh agent-2 in the same tenant `fleet_pull`s all of agent-1's nodes and can search them; a different tenant pulls nothing (isolation). Dimension-safe (never mixes vector dims in a collection). Local SQLite stays source of truth; best-effort, never raises.\n\n## [2.13.5] - 2026-07-02\n\n### Fixed\n- **CRITICAL — GraphMemory dataplane sync push was 100% failing.** `_fleet_push_node` sent `source_type=\"graph\"` / `content_type=\"graph_node\"`, which AitherNexus rejects as invalid enums (HTTP 500) — so NO graph node ever replicated in 2.13.4. Now sends the accepted `source_type=\"manual\"` / `content_type=\"text\"` (graph provenance is carried in `metadata.synced_from`). **Verified live** against a fleet Nexus: a 4-node ingest replicates with `pending==0` and the collection count reflects it.\n\n### Added\n- **Swarm-awareness notification** — on a successful sync batch, `GraphMemory` emits a best-effort `graph.synced` event (tenant/workspace/agent/collection/count) to `AITHER_FLEET_EVENTS_URL` so OTHER agents in the tenant/swarm can pull the fresh data and deconflict in-flight work (push-based awareness instead of polling). No-op unless the env is set.\n\n### Known limitation\n- Cross-agent **rehydration** (`fleet_pull`) is only reliable when the fleet Nexus is backed by lancedb (persistent + exportable). An in-memory Nexus stores on `/ingest` but neither its `/search` nor `/export` returns the docs — two-way read needs the persistent Nexus.\n\n## [2.13.4] - 2026-07-02\n\n### Added\n- **Memory-maintenance primitives on `GraphMemory`** (all opt-in; default behaviour byte-identical):\n - **Reinforce-on-recall**: `recall_with_activation(..., reinforce=True)` bumps `reinforcement_count`/`last_reinforced` on every RETURNED node's metadata mirror (one UPDATE per node, WAL-safe) — making the `MemoryRecord` reinforcement fields the activation scorer reads LIVE. Default `False` = zero writes.\n - **`sweep(now, archive_below=0.05, max_nodes=None, dry_run=False)`**: read-time decay enforced at rest — archives nodes past their tier TTL with freshness×reinforcement score below `archive_below` and `reinforcement_count <= 1`; `max_nodes` additionally archives the lowest-scored overflow. PERMANENT tier immune. Every archive is a reversible governance tombstone (`TombstoneStore.recover`) + a FORGET ledger entry; a node is never deleted without a tombstone. `dry_run` returns the would-archive list without acting.\n - **`promote(node_id, tier=?, role=?)`**: re-tier/re-role a node IN PLACE (tier/role columns + metadata mirror) — edges, embedding and ledger history survive; ledgers an UPDATE entry when governance is on.\n- `MutationType.UPDATE` ledger entry kind (in-place re-tier/re-role).\n- **Per-tenant dataplane sync for `GraphMemory`** — replicate the local graph to a per-tenant vector dataplane (Nexus/Qdrant) so a sovereign agent's memory is durable + rehydratable across restarts and hosting boundaries. `fleet_push_all_nodes()` (catch-up), `fleet_pull()` (best-effort semantic top-up), `fleet_sync_pending()`, and auto-sync on ingest (`_maybe_autosync` + `drain_sync()`). **On by default** (`AITHER_FLEET_SYNC=auto`; only `false/0/off` disables) with an inferred target (local Nexus `:8122` or the gateway in cloud mode) and a SEPARATE collection (`AITHER_FLEET_GRAPH_COLLECTION`, default `graph_memory`) from the KV memory sync. New `GraphMemory(tenant_id=, workspace_id=, fleet_url=, auto_sync=)` constructor args override env (required for multi-tenant host processes). Node `synced` column + schema v4 migration. TLS verify is CA-bundle-aware (`AITHER_CA_BUNDLE`) for the internal-TLS mesh. Local SQLite stays the source of truth; every sync path is best-effort and never raises into ingest.\n\n## [2.13.3] - 2026-07-02\n\n### Added\n- **Canonical self-deploying embeddings provider** (`adk.embeddings`): one embedding provider for the whole SDK so vectors are portable across scopes (platform / tenant / sovereign) — same model + dimension everywhere. Pinned to `nomic-embed-text` (768-d; the vLLM `--served-model-name` for nomic-embed-text-v1.5). Lazy, single-flight resolution chain: explicit `AITHER_EMBEDDINGS_URL` → local vLLM (`:8209` then `:8120`, HTTPS-then-HTTP) → local Ollama → gateway (`AITHER_GATEWAY_EMBEDDINGS_URL`) → auto-deploy a local vLLM embeddings container if a GPU + Docker are present (opt out with `AITHER_EMBED_AUTODEPLOY=0`) → CPU sentence-transformers (384-d, degraded) → feature-hash (384-d, degraded, always works). Every batch is dimension-tagged so callers never silently mix 768-d and 384-d vectors. `get_default_embedder()`, `embed_texts()`, `embed_one()`, `get_provider()`, `reset_provider()`.\n\n### Changed\n- **`GraphMemory` now defaults its embedder to the canonical provider** (opt out with `AITHER_GRAPH_EMBEDDER=legacy`), so every adk agent shares one 768-d space. Added a meta-table dimension guard: the index is pinned to its first embedding's dimension and later different-dimension vectors are refused (kills 768↔384 mixing that silently poisons cosine similarity).\n\n## [2.13.2] - 2026-07-01\n\n### Added\n- **Session persistence**: AitherShell now auto-saves the active session_id to `~/.aither/config.yaml` after each chat turn and auto-restores it on the next shell launch. Multi-turn conversations survive across restarts — the messages were always persisted in SQLite + JSON, only the session pointer was missing.\n- `/new` command to start a fresh session (clears saved session_id).\n- `/sessions` command to list recent sessions with agent name, message count, and timestamp. Resume any with `/resume <id>`.\n\n## [2.13.1] - 2026-07-01\n\n### Fixed\n- **CRITICAL:** `deploy_connect()` now sends a HEAD request to verify the GitHub release asset exists before downloading, with a clear error if the asset is missing.\n- **CRITICAL:** Compose file URLs are now pinned to a stable commit hash (configurable via `AITHER_COMPOSE_PIN` env) instead of tracking the mutable `main` branch.\n- **CRITICAL:** `deploy_adk_node()` now falls back to starting `adk-serve` natively when Docker is unavailable, instead of blocking with \"Docker not installed\".\n- **HIGH:** GHCR login failure now blocks deployment immediately with actionable guidance, instead of warning and continuing to a confusing image-pull failure.\n- **HIGH:** Health checks now stream the last 30 lines of Docker Compose logs on failure, so the user can see why a container didn't start.\n- **HIGH:** vLLM port allocation now probes for conflicts before assigning; if port 8200 is busy, workers are automatically remapped to the next free port.\n- **MEDIUM:** Compose file downloads are now cached with ETag; re-running `adk deploy node` skips re-downloading an unchanged file.\n- **MEDIUM:** DGX Spark discovery now respects `AITHER_DGX_HOSTS` env (comma-separated) for custom network topologies, instead of only checking `spark.local` and `192.168.0.33`.\n- **MEDIUM:** Cloud API keys (Anthropic, OpenAI, DeepSeek) are now validated with a lightweight probe during `adk setup` infra scan, so invalid keys surface early.\n- **MEDIUM:** `deploy_connect()` now validates the extracted extension has a `manifest.json` and warns if it's missing.\n\n## [2.13.0] - 2026-07-01\n\n### Removed\n- **`adk.faculties.memory_graph`** — the pickle-based `MemoryGraph` module is deleted. `from adk.faculties import MemoryGraph` now resolves to the canonical `adk.graph_memory.GraphMemory` for back-compat.\n- **`adk.platform.memory`** — the entire legacy memory subsystem (`MemoryManager`, `UnifiedMemorySystem`, `GameEngine`, `StoryboardEngine`, `AnchorGenerator`) is deleted. Agent memory is `adk.memory.Memory` + `adk.graph_memory.GraphMemory`.\n\n### Changed\n- `adk work` command now uses `GraphMemory` instead of the removed pickle-based faculty graph.\n- `adk.MemoryGraph` and `adk.faculties.MemoryGraph` lazy exports now resolve to `adk.graph_memory.GraphMemory`.\n- `agent.set_memory_graph()` kept for back-compat but documented as deprecated; agents already wire `GraphMemory` automatically in `__init__`.\n- Platform startup (`adk.platform.infrastructure.startup`) gracefully handles missing `MemoryManager`.\n- Platform CLI module listing and health check updated to reflect removed memory module.\n\n## [2.12.7] - 2026-07-01\n\n### Deprecated\n- `adk.faculties.MemoryGraph` — emits `DeprecationWarning` on instantiation; use `adk.graph_memory.GraphMemory` (SQLite-backed, governed, embedding-native) instead.\n- `adk.platform.memory` package — the legacy `MemoryManager`, `UnifiedMemorySystem`, `GameEngine`, and `StoryboardEngine` modules now emit `DeprecationWarning` on import; use `adk.memory.Memory` and `adk.graph_memory.GraphMemory` for new code.\n\n### Changed\n- Updated `adk.faculties` docstring and usage examples to point at the canonical memory surface.\n\n## [2.12.6] - 2026-07-01\n\n### Changed\n- Trimmed public package debt by removing grid runbook artifacts and shell/room binary placeholder directories from published artifacts.\n- Removed stale public-sync allowlist entries for deleted phase test files and added explicit public-sync guards for binary placeholder directories and grid artifacts.\n- Updated the public README grid note so it no longer links to runbook files excluded from the SDK package.\n\n## [2.12.5] - 2026-07-01\n\n### Added\n- Added best-effort fleet memory sync from local ADK memory into Nexus/Qdrant-compatible RAG stores, including batch catch-up and vector-search recall hooks.\n- Added saved config/env support for fleet memory URL, collection, and sync mode.\n\n### Changed\n- Excluded FormBridge runtime/test internals from public ADK packaging and public repo sync.\n\n## [2.12.4] - 2026-07-01\n\n### Added\n- Added a hot-reload endpoint for installed agent packs so newly applied pack tools can be picked up without restarting `aither-serve`.\n\n### Fixed\n- Fixed synchronous `ChatRelay` calls on Python 3.12 when no event loop is running; async WebSocket delivery is still scheduled when a loop exists.\n- Aligned gateway inference tier tests with the current public ladder: free, starter, pro, enterprise, platform.\n\n## [2.12.0] - 2026-06-28\n\n### Added — one-command local inference + upgrade path (click-to-run)\n- **`adk quickstart-local`** — detects hardware, picks a backend\n (Ollama / llama.cpp / vLLM), installs it, downloads a model, verifies, and\n prints next steps. Bootstraps Ollama itself via winget/brew/install.sh when\n missing (no \"go install it\" dead-ends). Default model `gemma4:e2b`.\n- **`adk backend switch <ollama|llamacpp|vllm>`** (+ `backend status`) — migrate\n the active deployment between engines; re-points config and smoke-tests.\n- **`adk install pack:<name>`** (+ `adk install list` / `adk packs`) — install\n ready-made agent packs (openclaw, hermes, claude-code) into `~/.aither/agents`.\n\n### Fixed (found via live end-to-end testing on Windows + CUDA)\n- quickstart-local reported success on a dead endpoint (smoke test was warn-only);\n a failed smoke now fails the command, with a service-readiness wait.\n- `--model` resolved to `None` (getattr default never fired) → uses the real default.\n- Smoke test: 30s→180s timeout for cold model load; accepts reasoning-model output\n (empty `content` at low token budgets is no longer a false failure).\n- `adk install pack:<name>` colon syntax (argparse rejected it before dispatch).\n- `adk backend status` health probe used a nonexistent `/health` path → 404; now\n probes `/v1/models` (uniform OpenAI-compatible liveness for all three backends).\n- Windows: removed non-ASCII glyphs from printed strings that crashed the cp1252\n console (a `✓` even made a successful install report failure).\n\n## [2.11.1] - 2026-06-14\n\n### Fixed — `adk host` / `adk login` device flow (autonomous onboarding)\nFound driving `adk host` against production; all broke real device-flow login:\n- **Device flow hit the portal FRONTEND, not AitherIdentity.** `adk login`/`adk host`\n POSTed `/auth/device/code` to portal/veil.aitherium.com, which 307-redirects API auth\n to `/login`. Added `_resolve_identity_url()` mapping the aitherium.com topology to\n `idp.aitherium.com` (the Identity API); localhost/bespoke pass through unchanged.\n- **Cloudflare 403'd urllib.** `idp.*` sits behind Cloudflare, which blocks urllib's\n default `Python-urllib/3.x` UA. Device-code + poll requests now send a real\n User-Agent + Accept (verified urllib→idp 200, was 403).\n- **`adk host` now self-authenticates via device flow** when there's no token, and\n **retries via device flow on a 401/403** (a stale saved token no longer dead-ends\n registration).\n\n## [2.11.0] - 2026-06-14\n\n### Added — attach your own MCP \"hands\" to a self-hosted agent\nA self-hosted agent now has the full brain (your model) · body (this loop) · **hands**\n(your tools) trifecta. The AitherOS platform relays the MCP servers you registered for\nyour agent in the `/stream` request body as `mcp_endpoints`, and the loop wires their\ntools into the ReAct turn:\n- **`adk/mcp_endpoint_tools.register_mcp_endpoint_tools(agent, endpoints)`** — speaks MCP\n JSON-RPC (`tools/list` to discover, `tools/call` to invoke) and registers a namespaced\n proxy tool (`{endpoint}__{tool}`) for each advertised tool. Mirrors `app_proxy_tools`.\n Best-effort: an unreachable endpoint registers nothing and never raises.\n- **`POST /stream` and `POST /chat/stream`** accept an optional `mcp_endpoints` list\n (`[{name, url, [headers]}]`); `_aitheros_stream` attaches them to the agent before the\n turn. Existing callers are unaffected (the field is optional).\n\n## [2.10.1] - 2026-06-14\n\n### Fixed — self-hosted `aither-serve` was crash-on-startup (2.10.0 regression)\nA fresh `pip install aither-adk && aither-serve --backend deepseek` could not boot. Found\nand fixed while standing up a real self-hosted DeepSeek agent end-to-end:\n- **Startup `NameError: name 'port' is not defined`** in three lifespan helpers\n (`_join_aithernet`, `_init_a2a_server`, `_start_mesh_hosting`) — they referenced a bare\n `port` no longer in scope. Now use `config.server_port`, and `main()` writes the resolved\n `--port`/`--host` back to `config` so every lifespan helper and the invoke_url see the\n actual bound port.\n- **Fleet self-registration crashed the whole server** — the registration handlers caught\n `(ImportError, RuntimeError, OSError, ConnectionError)` but **not** `httpx.HTTPError`, so a\n `RemoteProtocolError`/connect error talking to the control plane took the agent down. All\n network-facing lifespan handlers now also catch `httpx.HTTPError` (non-fatal, as intended).\n- **`--backend` was ignored when `AITHER_API_KEY` was set** — `_auto_detect` picked the cloud\n gateway before the explicit backend, and `_try_elysium_fallback` then replaced the chosen\n provider. An explicit backend (deepseek/openai/anthropic/your own vllm/ollama) now always\n wins; \"auto\"/\"gateway\" still flow through detection. The operator's own brain is never\n silently hijacked.\n- **Approved-tool trace truncated after `/sessions/{id}/confirm`** — the SSE relay treated\n `AgentResponse.tool_calls_made` (a `list[str]`) as dicts (`tc.get(...)`), raised\n `AttributeError`, and an over-narrow `except` swallowed it so the client saw only\n `session_start` + heartbeats. The relay now handles string tool names, the `except` is\n broad (always emits a typed `error` + terminal `complete`), and the module-level\n `_strata_ingest` `NameError` + Chronicle `session_id` kwarg mismatch are fixed.\n\n## [2.10.0] - 2026-06-13\n\n### Added — human-in-the-loop tool approval (`adk/approval.py`)\n- **Pause before a gated tool, resume on decision.** An agent can now pause its turn before\n executing a sensitive tool and wait for a human's allow/deny — so a managed control plane\n (or any operator) can gate tool use without forking the loop.\n- **Policy via `AITHER_TOOL_APPROVAL`** — a comma-separated list of tool names (optionally\n `agent:tool`), or `*` for every tool. Empty = no gating (unchanged behavior).\n- **Survives restarts / days-later approvals.** The paused turn (user message + pending tool\n calls) is persisted to disk keyed by `session_id`; resume is a fresh request, not a held\n connection. Decisions are recorded per `(session_id, tool_name)`.\n- **`AgentResponse.requires_action` / `.pending`** — set when a turn paused; `pending` lists\n the gated tool calls awaiting a decision.\n- **`AitherAgent.resume(session_id, decisions)`** — records the decisions and re-runs the\n paused turn (the gate consumes them: allowed tools execute, denied tools get a denial\n observation, the turn continues — and may pause again on a different gated tool).\n- **Server:** `POST /sessions/{id}/confirm` records decisions and streams the resumed turn;\n `/stream` emits a `requires_action` event when a turn pauses.\n\n## [2.9.0] - 2026-06-13\n\n### Added — universal LLM continuation primitive (`adk/llm/continuation.py`)\n- **`run_continuation()` + `stitch()`** — one shared continue-until-complete primitive. When a\n completion stops because it hit the output token cap (`finish_reason == \"length\"`), it continues\n the generation and STITCHES the chunks (overlap-dedup + restart-detect) into a complete answer,\n instead of every call-site hand-rolling its own retry. Bounded three ways (max rounds AND total\n output ceiling AND a no-growth break), and it never mutates the caller's message history.\n- **`LLMProvider.chat_with_continuation()`** and **`LLMRouter.chat_with_continuation()`** — every\n provider AND the router inherit the primitive, so any call-site gets continuation for free.\n- Kill-switch `ADK_LLM_CONTINUATION=off`; tunables `ADK_LLM_MAX_CONTINUATIONS`,\n `ADK_LLM_MAX_TOTAL_OUTPUT_TOKENS` (defaults: 2 rounds / 8192 tokens).\n\n### Changed\n- The ReAct loop's truncation recovery now uses the shared primitive. The previous inline\n doubling-retry REPLACED the partial response (losing earlier text in `resp.content`) and injected\n continuation scaffolding into the live message history; it now STITCHES and leaves history clean.\n Tool-call turns are never continued as text.\n\n### Included — previously-committed-but-unreleased changes\n- 2.8.2: TLS verification on by default (`verify=False` removed).\n- 2.8.1: baked license + pack-signing public keys.\n- 2.8.0: version-bump release.\n\n## [2.7.0] - 2026-06-05\n\n### Added — provider-agnostic LLM cost levers (at the `LLMProvider` ABC)\n- **Prompt caching.** New `ProviderCapabilities` (`prompt_cache: explicit|automatic|none`, `batch`) and\n normalized `LLMResponse.cache_read_tokens`/`cache_write_tokens`, so the \"tokens saved\" meter never\n depends on which backend served the turn. Anthropic inserts `cache_control` breakpoints over the stable\n system+tools prefix; OpenAI/DeepSeek caching is automatic and their `cached_tokens` are now surfaced\n (previously discarded). Default-on in `AitherAgent.chat`; override with `cache=False`.\n- **ResponseCache** (`adk/llm/cache.py`) — exact-match response cache with a pluggable backend, wired into\n `LLMRouter` as an opt-in (`LLMRouter(response_cache=…)` + per-call `cacheable=True`). OFF by default — no\n determinism change unless opted in.\n- **Gemini provider** (`adk/llm/gemini.py`) with explicit `cachedContents` caching; registered in the router.\n- **Async batch runner** (`adk/llm/batch_runner.py`) — `run_batch()` over the Anthropic Message Batches /\n OpenAI Batch APIs (~50% off) with a transparent fallback to concurrent `chat()` where unsupported.\n- **Graph-RAG retriever interface** (`adk/graph_retriever.py`).\n\n### Added — principal authority (prompt-injection defense enforced outside the model)\n- `adk/auth.py`: `Principal` / `AuthContext` / `PrincipalResolver` (+ deny-all default).\n- `ToolRegistry.execute(name, args, auth=…)` — a default-deny authorization gate at the tool boundary, so a\n forbidden tool call is blocked regardless of what the LLM emits. `ToolDef` gains `required_clearance` /\n `action_class`. Threaded through `AitherAgent.chat`; `auth=None` is fully backward-compatible.\n- `adk/channel_auth.py`: Telegram webhook-secret + CEO-id checks and HMAC-signed approvals.\n\n## [2.6.4] - 2026-06-05\n\n### Fixes\n- **Loop-guard nudges corrupted the message history → DeepSeek 400 mid-run.**\n When the ReAct loop emitted multiple tool calls in one assistant turn and the\n loop guard fired a WARN/BLOCK/CIRCUIT_BREAK on one of them, it spliced a\n standalone `system` nudge message *between* the tool results. DeepSeek (and\n strict vLLM chat templates) require an assistant `tool_calls` message to be\n followed by exactly one `tool` result per `tool_call_id`, contiguously — so the\n interleaved message produced `400 … insufficient tool messages following\n tool_calls message` and aborted the turn (this is reliably hit after crossing\n the 15-call circuit-break threshold, where every subsequent call WARNs).\n Per-call guidance is now folded into that call's own `tool` result, and any\n standalone steering is deferred to a single message AFTER the whole batch —\n preserving the `tool_calls`↔`tool_results` pairing every backend requires.\n Regression test: `deep-research-agent/tests/test_msg_structure.py`\n (loopguard-flood scenario).\n\n## [2.6.3] - 2026-06-04\n\n### Fixes\n- **Transient provider errors killed a run on the first blip.** A single\n `502 Bad Gateway` / `503` / `529 overloaded` / `429` from the upstream API\n aborted the whole request — the Anthropic provider had **no retry at all**, and\n the OpenAI-compatible provider only retried `429`. Both now retry transient\n statuses (`408/429/500/502/503/504/529`) with `Retry-After`-aware exponential\n backoff (3 attempts), and surface the provider's body if all retries are\n exhausted. Non-transient `4xx` (e.g. `400`) still fails fast. Applies to\n `chat` and `chat_stream` on both providers.\n\n## [2.6.2] - 2026-06-04\n\n### Fixes\n- **DeepSeek (and strict OpenAI-compatible backends) 400'd on long tool\n conversations.** The ReAct loop legitimately injects mid-conversation\n `system` steering messages (the \"[DIMINISHING RETURNS]\" hint after several\n low-output tool iterations, loop-guard nudges). OpenAI tolerates a non-leading\n `system` message; DeepSeek rejects it with a 400. Long research runs\n (many `fetch_url` calls) reliably tripped this. Fix: non-leading `system`\n messages are demoted to `user` at the OpenAI-compatible provider boundary\n (`_demote_nonleading_system`) — steering intent preserved, payload valid on\n every backend; leading system prompt(s) untouched.\n- **Opaque HTTP errors.** `resp.raise_for_status()` discarded the provider's\n response body, so a 400/422 surfaced as a bare `Client error '400 Bad\n Request'` with no cause. `_ensure_ok` now includes the provider's actual\n message (e.g. `context_length_exceeded`, `Invalid 'messages'`) in the raised\n error, for both `chat` and `chat_stream`.\n\n## [2.6.1] - 2026-06-04\n\n### Fixes\n- **`MCPServer.mount()` returned HTTP 422 for every `/mcp` call.** Under\n `from __future__ import annotations` the route handler's `request: Request`\n annotation is a string that FastAPI resolves against the *module* globals, but\n `Request` was imported locally inside `mount()` — so FastAPI couldn't resolve\n it and treated `request` as a required query param. The MCP server endpoint\n (`initialize`, `tools/list`, `tools/call`) was unusable when mounted. FastAPI\n symbols are now imported at module level (guarded; the `[node]` extra still\n gates actual use). Any agent's tools can now be exposed as a working MCP\n server with `MCPServer(...).mount(app)`.\n\n## [2.6.0] - 2026-06-04\n\n### Streaming + scaffolding\n- **`AitherAgent.stream_chat`** — reliable streaming sibling of `chat()`: native\n tool-calling loop (concludes on its own, unlike the text-protocol `stream_react`)\n that emits live `tool`/`tool_result` events and streams the answer as `token`\n events. `token_delay` paces tokens for a typing effect.\n- **`adk new <template>`** — scaffold a full, runnable template app. Ships the\n `deep-research` template (a cited web-research agent: pack + server + web UI):\n `adk new deep-research`.\n\n### Fixes\n- **BYO-key no longer community-capped.** The free-tier monthly token cap now applies\n only to the metered Aitherium gateway backend, never to a user's own provider key\n (Anthropic/OpenAI/DeepSeek/Ollama/vLLM). Previously it could brick a self-hosted\n agent after ~100k tokens on the user's own key.\n- **Stronger, grounded memory recall.** `chat()` now injects the top-6 knowledge-graph\n hits (untruncated) with a hard no-fabrication instruction — was top-3 truncated to\n 200 chars, which surfaced partial facts and let weaker models invent the rest.\n- **Tool-arg coercion.** `ToolRegistry.execute` coerces LLM-supplied args toward each\n parameter's annotated type, so a tool no longer crashes when a model passes a list\n or string where an int/float/bool was expected.\n\n### Docs\n- New `docs/AGENT_DEV_GUIDE.md` — the golden path for building agents/packs.\n\n## [2.5.0] - 2026-06-04\n\n### Continual learning — agents that learn from runs and reuse memory\n\nAgents now improve the second time they do a task instead of starting from scratch.\n\n### Added\n- **Recall-before**: `AitherAgent.chat()` searches the local `SkillStore` and injects the\n top matching learned skills as a system block, so the model reuses proven procedures.\n- **Learn-after**: `_learn_after()` runs after each response — reinforces (`success_count++`)\n any skills that were recalled+reused, and extracts + saves a NEW skill from successful\n multi-tool runs via `SkillExtractor`. Wired at both `chat()` return paths.\n- Gated by `AITHER_SKILLS` (default on). The previously-unused `SkillStore`/`SkillExtractor`\n are now connected to the run loop.\n\n### Notes\n- Pairs with the platform-side LearnedProcedure substrate (recipes → scored procedures →\n skills/tools/A2A/packs → MCTS priors → evolution). The SDK shares memory via the Spirit\n bridge when configured.\n\n## [2.0.0] - 2026-06-02\n\n### Open-core boundary (BREAKING)\n\nThis release draws a single, enforced free/paid line. The free tier stays\ngenuinely useful (real agent, typed + graph + code memory, ReAct, ~essential\ntools); paid tiers unlock the *scale* capabilities via portal.aitherium.com.\n\n### Added\n- **`adk/licensing.py`** — the entitlement keystone. Resolves a tier\n (COMMUNITY by default), verifies optional portal-signed `~/.aither/license.json`\n (Ed25519), and answers `can_use_fleet/channels/cron/swarm/auto_neurons`,\n `is_agent_licensed`, `max_effort`, etc. **Fail-closed**: no/invalid/expired\n license → free tier; an unsigned license never grants premium.\n `AITHER_TENANT_SLUG=aitherium` → unrestricted INTERNAL tier.\n- **Moat guard** (`scripts/check_moat_boundary.py`) wired into the publish\n workflow — blocks any wheel that leaks the moat or drops the keystone.\n- Tests: `tests/test_licensing.py`, `tests/test_moat_boundary.py`.\n\n### Changed (gating — fail-closed)\n- Free tier effort is capped at 3 (reasoning effort 7–10 needs Professional).\n- Monthly token budget hard-block on the free tier (no silent overspend).\n- Gated behind a paid tier: fleet mode (`FleetConfig`), cross-agent delegation\n (`AgentForge.delegate`), channel adapters, cron scheduler, proactive\n auto-neurons, and `swarm`/`swarm_code` dispatch.\n\n### Removed\n- **`adk/nanogpt.py`** (on-device training IP) removed from the published SDK\n and the public repo; relocated to the internal AitherOS moat. Excluded from\n the wheel as defense-in-depth.\n- Pre-2.0 releases shipped this IP and ungated capabilities and are being\n yanked (PyPI) / removed (GitHub releases). See `scripts/yank_leaky_releases.py`\n and `scripts/purge_public_leaks.sh`.\n\n## [1.20.0] - 2026-05-27\n\n### Fixed\n- **Pack discovery path mismatch** — `_find_local_packs()` now scans both\n `~/.aither/packs/` and `~/.aitheros/packs/`, so packs installed via\n `adk pack install` are discovered by `adk-workspace`.\n\n### Added\n- **Auto-register on pack install** — `adk pack install` now auto-registers\n with the portal if the pack contains an `agent.yaml` with a `portal` section.\n One command does download + extract + register.\n- **Flux event on agent registration** — `developer_portal.py` emits\n `agent.registered` Flux event so the portal fleet UI updates in real-time.\n\n## [1.15.0] - 2026-05-26\n\n### Added\n- **Cloud memory** — `adk quickstart --cloud` now tests gateway memory and\n auto-configures Spirit URL so memories persist across devices via\n `gateway.aitherium.com/v1/memory/teach` and `/v1/memory/recall`.\n- **Config→env export for cloud mode** — `Config.from_env()` now exports\n `AITHER_CLOUD_MODE`, `AITHER_SPIRIT_URL`, and spirit path env vars from\n saved config so Memory and other modules pick them up without needing\n explicit env vars set by the user.\n- **Cloud Quick Start section** in README — 3-command setup for users who\n just want a Claude/GPT-4/DeepSeek API key and the full agent harness.\n- **`--memory` profile for node deployment** — `adk deploy node --memory`\n adds Spirit (8087) + WorkingMemory (8101) containers for persistent\n vector memory. Without this, agents use local SQLite only.\n- **Configurable vLLM in node compose** — `AITHER_VLLM_MODEL`,\n `AITHER_VLLM_GPU_UTIL`, `AITHER_VLLM_CTX_LEN`, `AITHER_VLLM_QUANT_ARGS`\n env vars let `adk setup` write GPU-detected tier settings to `.env`\n and have the node compose pick them up (TQ4 for small GPUs, BNB for large).\n\n### Fixed\n- **Memory cloud routing** — `Memory._spirit_teach()` and `_spirit_recall()`\n now use configurable paths and auth headers for gateway proxy. Previously\n cloud-only users silently lost all Spirit memory calls.\n\n## [1.9.0] - 2026-05-24\n\n### Added\n- **Composable ToolPack system** — agents can load tool packs from\n `.toolpack.yaml` directories. Drop a pack in `~/.aitheros/packs/` or\n set `AITHER_PACKS_DIR`, and any agent discovers it automatically.\n- **`tools.packs` in agent.yaml** — list tool pack IDs to auto-load\n (e.g. `packs: [crm-tools, calendar-pro]`).\n- **`register_tool_packs(agent, pack_ids, packs_dir)`** in `builtin_tools` —\n programmatic pack loading for agents.\n- **`AITHER_TOOL_PACKS` env var** — comma-separated pack IDs loaded on\n agent init (alternative to YAML config).\n- **Unknown builtin categories resolve as pack IDs** — `tools.builtin:\n [file_io, crm-tools]` now works: `crm-tools` is looked up as a pack.\n\n## [1.8.0] - 2026-05-24\n\n### Removed\n- **11 dead modules** (3,023 lines) — `agent_deploy`, `cloud_deploy`, `create`,\n `phonehome`, `sdk_bridge`, `sovereign`, `telemetry`, `telemetry_config`,\n `error_reporter`, `anonymizer`, `package`. None were imported anywhere.\n- **29 ghost exports** from `__init__.py` — `secrets`, `otel`, `fs_sandbox`,\n and `vector_memory` modules were listed in `__all__` and `__getattr__` but\n the backing files never existed. Accessing them raised `ModuleNotFoundError`.\n- **Stale dist/ artifacts** — removed old wheels/tarballs (v0.4.0–v1.4.1, 11 MB).\n- **Orphan test scripts** — `test_phase1.py` and `test_phase23.py` (one-off\n validation scripts not part of the pytest suite).\n\n### Fixed\n- **Version mismatch** — `__init__.__version__` now matches `pyproject.toml`\n (was stuck at 1.6.0).\n\n## [1.5.0] - 2026-05-23\n\n### Added\n- **`--sovereign` flag for `adk deploy node`** — registers the node with the\n Aitherium hub via federation after deployment. Saves federation credentials\n to `~/.aither/.env.federation`. Accepts `--hub` (custom hub URL) and\n `--tenant` (tenant slug) options. Non-fatal: node runs standalone if\n registration fails.\n- **Federation → fleet cross-post** (Genesis) — sovereign nodes that register\n or heartbeat via `/federation/register` and `/federation/heartbeat` now\n automatically appear in the fleet dashboard (`/fleet/deployments`) with\n `node_type: \"sovereign\"` and live status/metrics.\n- **\"Deploy Sovereign Node\" section on Connect page** (AitherVeil) — Section 8\n with ADK deploy commands, fleet dashboard links, and provision page links.\n- **`deploy-node` action in tunnel proxy** (AitherVeil) — Connect page and\n external callers can now request bootstrap scripts via\n `POST /api/tunnel/devworkspace?action=deploy-node`.\n\n### Fixed\n- **Fleet provision page** — API URL corrected from `/api/fleet/provision` to\n `/api/bridge/genesis/fleet/provision` (matches bridge proxy pattern).\n- **Fleet detail page** — API URL corrected from `/api/fleet/deployments/` to\n `/api/bridge/genesis/fleet/deployments/` (same fix).\n- **Dockerfile** — entry point corrected from `aither-serve` to `adk-serve`\n (matches `pyproject.toml` script registration). Version label updated.\n\n## [1.4.1] - 2026-05-22\n\n### Fixed\n- **macOS executable builds** — dropped `--target-arch universal2` from\n `packaging/build_executable.py`. PyPI wheels for `pydantic_core` and other\n native deps are arch-specific, not fat binaries, so universal2 always failed\n with `IncompatibleBinaryArchError`. Now builds for the runner's native arch\n (`aither-macos-arm64`).\n- **Release workflow strategy** — added `fail-fast: false` so a single\n executable build failure no longer cancels the other OS jobs.\n- **PyPI publish** — added `skip-existing: true` so re-runs of the same\n version don't fail when the artifact already exists.\n- **GitHub Release job** — now runs whenever `pypi` succeeds even if some\n executable matrix entries fail, so the release page is always created with\n whatever binaries did build successfully.\n\n## [1.4.0] - 2026-05-22\n\n### Added\n- **Sovereign template now ships with `agent_core` 5-tier memory** — generated\n backends include the canonical `agent_core/` package (working → episodic →\n semantic → procedural → identity) plus `agent_memory.py` helper, vendored\n from `portal-kit-backend`.\n- `aiosqlite>=0.20.0` added to sovereign template `requirements.txt` for\n local agent_core SQLite storage.\n- Document tombstoning: `evict_cached_conversations_referencing` is wired\n through document-delete handlers in consumer apps so memories citing\n deleted documents are blocked from recall.\n- New tests: 13 smoke tests for `portal-kit-backend/agent_core` covering\n promotion thresholds, decay rates, store roundtrip, tombstone recall\n blocking, circuit breaker state machine, and chat-turn recording.\n\n### Changed\n- `scripts/vendor_agent_core.py` now syncs canonical `agent_core` to 4\n targets: WorkspaceRuntime, ADK sovereign template, and the GargBot +\n Chelle consumer mirrors (previously only 2).\n\n## [1.2.1] - 2026-05-21\n\n### Added\n- `adk train` command group — 6 subcommands: status, launch, logs, cancel, runs, register-gpu\n- `/slash-commands` endpoint — ADK server auto-exposes all 35 CLI commands as structured JSON manifest for AitherShell\n- `/cli/execute` endpoint — AitherShell can run any CLI command through the server\n- `build_command_manifest()` — introspects full argparse tree (commands, args, choices, subcommands)\n- `_register_commands()` extracted from main() for shared parser construction\n\n### Changed\n- `_get_genesis_url()` now probes: Genesis → ADK server → Aitherium cloud gateway (works without Genesis)\n\n## [1.2.0] - 2026-05-21\n\n### Added\n- **Runtime backend switching** — `agent.switch_backend(\"deepseek\")` and `agent.llm.switch_backend()` change provider without recreating agent\n- **Hybrid reasoning** — `agent.set_reasoning_backend(\"anthropic\")` routes effort 7+ to cloud API while keeping local orchestrator\n- **First-class DeepSeek provider** — `deepseek` in effort model table with `deepseek-chat` and `deepseek-reasoner`\n- **TQ4 quantization tiers** — `nano` (6GB, TurboQuant 4-bit), `standard-tq4` (12GB, both models TQ4), `hybrid-tq4` (6GB + cloud reasoning)\n- **`adk backend` CLI** — `list`, `set`, `set-reasoning`, `test` subcommands\n- **`adk quickstart`** — unified first-run wizard (setup + auth + shell)\n- **`adk tools`** — list local + MCP tools with tier markers\n- **`adk backup`** — tarball export of `~/.aither/`\n- **`adk ingest`** — feed docs/files into knowledge graph\n- **`adk setup nemotron`** shortcut — alias for `--tier lite`\n- **`--reasoning-api`** flag in setup — `anthropic`, `openai`, `deepseek`, `gateway`\n- **`--dgx-spark URL`** flag — remote vLLM endpoint configuration\n- **DGX Spark auto-detection** — scans `spark.local`, `192.168.0.33`, `AITHER_DGX_URL` env\n- **Post-setup smoke test** — sends real inference request after container startup\n- **Schema migration system** — version table + migration runner in `memory.py` and `graph_memory.py`\n- **Configurable vLLM port scan** — `AITHER_VLLM_PORTS` env var for custom ports\n- **`aithershell` entry point** — alias for `adk` in pyproject.toml\n- Shell auto-downloads binary on first `adk shell` (no `--install` needed)\n- Shell pre-flight check warns if no backend detected\n- Shell passes full config via env vars (API key, tenant, inference URL)\n- DGX/Remote and Cloud API checks added to `adk doctor`\n- Config fields: `reasoning_backend`, `reasoning_api_key`, `deepseek_api_key`, `dgx_url`, `vllm_extra_ports`\n\n### Fixed\n- `test_aeon_chat_basic` timeout — mock scope was exiting before request\n- `adk doctor` check_disk timeout — cap rglob at 5000 files\n\n## [1.1.7] - 2026-05-19\n\n### Added\n- Remote agent pipeline — `adk onboard` → fleet dispatch → remote inference\n- Agent-to-agent federation with mesh relay\n- `adk connect` for desktop mesh enrollment\n\n## [1.1.6] - 2026-05-19\n\n### Fixed\n- Tool call execution recovery across all backends\n- Hermes tool call fallback parsing for vLLM\n\n## [1.1.5] - 2026-05-19\n\n### Fixed\n- Tool call execution + Hermes fallback parsing improvements\n\n## [1.1.0] - 2026-05-18\n\n### Added\n- Multi-channel gateway (Telegram, Discord, Slack, webhook)\n- Aeon group chat with 7 presets\n- Skills auto-extraction from multi-step sessions\n- MCP stdio server for Claude Code integration\n- `adk gateway`, `adk cron`, `adk skills`, `adk soul` commands\n- SOUL.md import/export for portable agent identity\n- Elysium cloud device-flow authentication\n\n## [1.0.0] - 2026-05-18\n\n### Breaking\n- Package consolidated — `aither_adk` namespace replaced by `adk` + `adk.platform`\n- 93 duplicate files removed from `AitherOS/packages/aither_adk/`\n\n### Added\n- Standalone core primitives (`adk.core`: Agent, Tool, Memory, ModelBackend, Capability, Trace)\n- `adk.platform` sub-package merging all platform toolkit modules\n- Compatibility shims for legacy `from aither_adk.*` imports\n\n### Changed\n- Version synced across pyproject.toml, `adk/__init__.py`, `adk/platform/__init__.py`, npm package\n- Classifier promoted from Alpha to Beta\n\n## [0.16.0] - 2026-04-16\n\n### Swarm Coding Engine & Repowise Integration\n- **`repowise_search` tool** — Semantic + keyword hybrid code search via Repowise, with ripgrep fallback\n- **`swarm_code` tool** — Dispatch complex tasks to 11-agent swarm pipeline (ARCHITECT->SWARM->REVIEW->JUDGE)\n- **`agent.swarm()`** — Async convenience method with configurable mode, effort, timeout\n- **`agent.code_search()`** — Async convenience method returning structured results\n- New tool categories: `repowise` and `swarm` in TOOL_CATEGORIES\n- `repowise_search` and `swarm_code` added to `__init__.py` exports with lazy loading\n- `IDENTITY_DEFAULTS` updated: repowise for code-focused agents, swarm for orchestration agents\n- Standalone graceful degradation: repowise falls back to ripgrep, swarm returns structured error\n\n## [0.13.0] - 2026-04-02\n\n### Graph Faculties — Local Knowledge for Every Agent\n- **CodeGraph** (2,799 lines) — Full Python AST indexer with call graph, keyword/semantic/hybrid query, embedding matrix cache, incremental re-indexing, multi-hop chain expansion\n- **MemoryGraph** (1,339 lines) — Graph-based persistent agent memory with 10 edge types, hybrid query (keyword + semantic + graph expansion), multi-hop recall, pickle persistence\n- **EmbeddingProvider** — 4-backend fallback chain: sentence-transformers (GPU/CPU) -> Ollama -> Elysium cloud -> feature hashing (zero deps)\n- **BaseFacultyGraph** — Abstract base with HMAC-SHA256 validated pickle persistence\n\n### Agent Integration\n- `agent.set_code_graph(cg)` — auto-registers `code_search` + `code_context` tools\n- `agent.set_memory_graph(mg)` — auto-registers `remember` + `recall` + `memory_stats` tools\n- Both graphs inject context into LLM prompts automatically during chat\n\n### Zero-Config Onboarding\n- `adk start` / `adk` (no args) — auto-detect project, index code, detect LLM, persistent memory, interactive chat\n- Works for any directory: Python codebases, doc folders, mixed workspaces\n- Auto-detects LLM: Ollama -> vLLM -> Elysium -> OpenAI -> Anthropic\n- Per-project persistent memory in `~/.aither/memory/<project>`\n- `adk index <path>` — standalone indexing with progress bar and stats\n\n### MCP Gateway\n- `POST /v1/embeddings` — OpenAI-compatible embedding proxy to vLLM-embeddings:8209\n- Available to all tiers (embeddings are free)\n- ADK EmbeddingProvider uses this as Elysium cloud fallback\n\n### Optional Dependencies\n- `pip install aither-adk[graphs]` — numpy for 10x cosine similarity speedup\n- `pip install aither-adk[embedding]` — sentence-transformers + torch for local GPU embeddings\n- `pip install aither-adk` alone — graphs work with feature hashing (zero deps)\n\n## [0.12.0] - 2026-04-01\n\n### Bootstrap & Service Discovery\n- Version handshake -- ADK checks major.minor compatibility with Genesis/Node\n- Background reconnect loop -- ServiceBridge re-probes every 30s in standalone mode\n- Port 8080/8090 documented -- MCP vs OpenAI-compat clearly separated\n- Genesis URL configurable via GENESIS_URL env var (was hardcoded)\n- Standalone mode warning -- visible stderr alert when AitherOS not detected\n- Auto-reconnect on startup when services come online\n\n### Elysium Cloud Inference\n- Unified gateway URL -- gateway.aitherium.com handles auth + billing + inference\n- Streaming inference -- SSE passthrough for /v1/chat/completions with stream=true\n- Auth proxy routes -- /v1/auth/register, /v1/auth/login, /v1/auth/me\n- Billing proxy -- /v1/billing/balance through gateway\n- AitherConnect Elysium fallback -- cloud inference when local Genesis is down\n- AitherDesktop Elysium fallback -- third-tier chat fallback after Node\n\n### Infrastructure\n- /discovery endpoint on Genesis -- unified service URLs/versions/health\n- /api/config/services on Veil -- runtime port config for client-side JS\n- Veil healthcheck gates on Genesis -- unhealthy when backend is down\n- Desktop crash detection 90s->60s (threshold 3->2)\n\n## [0.11.0] - 2026-04-01\n\n### Agent Execution Quality (Claude Code Parity)\n- Raise loop guard block threshold from 3 to 4 -- agents get more room for iterative search\n- Soft synthesis nudge for effort >= 4 (no tool stripping, trust the model)\n- max_output_tokens escalation -- retry up to 3x when response is truncated\n- Tool result pairing guarantee -- synthetic error for orphaned tool_use blocks\n- Micro-compaction of old tool results -- save context tokens on long sessions\n- First-turn tool forcing only for effort >= 6 (trust model for lower effort)\n- Diminishing returns detection -- nudge agent when 3+ turns produce < 500 tokens\n- Message normalization -- merge consecutive same-role messages, strip empties\n- LLM retry with exponential backoff (5 retries, 500ms-16s, jitter)\n\n## [0.9.0] - 2026-03-16\n\nThe \"connected world\" release. Cross-platform identity pairing, voice capabilities, and multi-channel integration.\n\n### Added\n- **Pairing**: Cross-platform identity linking (`adk/pairing.py`)\n - `PairingManager` — SQLite-backed identity linking with 6-char pairing codes\n - Link users across Telegram, Discord, Slack, WhatsApp with 10min TTL codes\n - Canonical session IDs for cross-channel conversation continuity\n - `get_session_id()` returns \"user-{id}\" for paired users\n- **Voice**: Speech-to-text and text-to-speech client (`adk/voice.py`)\n - `VoiceClient` — async STT/TTS/emotion via AitherVoice service\n - Convenience functions: `hear()`, `say()`, `feel()`\n - 6 voice options: alloy, echo, fable, nova, onyx, shimmer\n - Emotion detection with intensity scoring\n- New exports: `PairingManager`, `PairingResult`, `PlatformIdentity`, `VoiceClient`, `TranscriptionResult`, `SynthesisResult`, `EmotionResult`\n\n### Changed\n- `__init__.py` exports expanded with pairing and voice symbols\n\n## [0.6.0] - 2026-03-13\n\nThe \"group mind\" release. Multi-agent group chat, creative tools, and Iris identity.\n\n### Added\n- **Aeon**: Multi-agent group chat engine (`adk/aeon.py`)\n - `AeonSession` — persistent group chat with parallel agent execution\n - 7 presets: balanced, creative, technical, security, minimal, duo_code, research\n - Orchestrator synthesis: Aither summarizes all agent responses\n - Serial execution for Ollama, parallel for vLLM/cloud\n - ConversationStore persistence with `type: \"aeon\"` metadata\n - `group_chat()` one-shot convenience function\n- `aither aeon` CLI command — interactive terminal group chat with color-coded agents\n - `-p/--preset`, `-a/--agents`, `-r/--rounds`, `--no-synthesize` flags\n - `reset` and `quit` commands\n- Server endpoints: `POST /aeon/chat`, `GET /aeon/presets`, `GET /aeon/sessions/{id}`\n- Creative tools in builtin_tools: `image_generate`, `image_refine`, `image_search`, `video_generate`\n- Iris agent identity with visual generation capabilities\n- 48 Aeon tests (data models, presets, chat, context, persistence, server, exports)\n\n### Changed\n- `__init__.py` exports: `AeonSession`, `AeonResponse`, `AeonMessage`, `group_chat`, `AEON_PRESETS`\n\n## [0.5.0] - 2026-03-13\n\nThe \"tenant-ready\" release. Multi-tenant admin, permission grants, safety profiles, and a full setup wizard.\n\n### Added\n- `aither setup` interactive setup wizard with hardware detection, model selection, identity config\n- Strata storage backend: SQLite WAL persistence for conversations, memories, knowledge graphs\n- CLI test runner: `aither test` with auto-discovery and parallel execution\n- Permission grants system for MCP tool access control\n- MCP account management tools\n- LLM provider auto-detection for Ollama and vLLM endpoints\n- Apache-2.0 LICENSE file\n- Elysium desktop sync module\n- Comprehensive CLI test suite (493 tests)\n- Strata storage test suite (972 tests)\n- LLM provider tests (82 tests)\n\n### Changed\n- CLI expanded: `aither setup`, `aither test`, `aither bugreport`, `aither doctor`\n- Strata module rewritten as full local-first storage engine\n- Server startup includes Strata initialization\n- README rewritten with clearer quickstart and architecture docs\n\n### Fixed\n- Dead documentation links\n- Python 3.11 compatibility issues\n- Ruff per-file-ignores configuration\n- Identity provisioning edge cases\n- Docker node-gyp native dependency builds (python3 + build tools)\n\n## [0.4.0] - 2026-03-13\n\nThe \"own your AI\" release. Self-hosted agent OS for people who don't want their data on someone else's servers.\n\n### Added\n- Muse agent identity (creative/artistic generation)\n- Port 8120 to vLLM scan for ExoNodes discovery\n- Public roadmap with milestone-based porting schedule\n- Competitive positioning: self-hosted alternative to cloud-locked AI appliances\n\n### Changed\n- LLM router: compute-aware effort routing (replaces effort-level context gating)\n- README rewritten for sovereignty-first messaging\n- Package description updated\n- Promoted from alpha to stable release\n\n### Fixed\n- Version string consistency between pyproject.toml and __init__.py\n- vLLM port scanning now includes port 8120 (ExoNodes)\n\n## [0.3.1] - 2026-03-09\n\n### Added\n- GraphMemory: SQLite knowledge graph with Ollama embeddings and hybrid search\n- NeuronPool: auto-fire pre-LLM data gathering agents\n- NanoGPT: pure Python char-level transformer with LoRA fine-tuning\n- Safety gates: IntakeGuard (input), LoopGuard (recursion), Sandbox (code exec)\n- Event system: EventEmitter with chat/tool/forge event types\n- Builtin tools: identity-based tool selection\n- ServiceBridge: auto-discovery of AitherOS services\n- Streaming: chat_stream with safety gate integration\n- Auth middleware: Bearer token authentication\n- CLI: `aither init` and `aither serve` commands\n\n### Changed\n- Wired 5 previously disconnected modules into agent loop\n- 522 total passing tests (up from 85)\n\n## [0.3.0] - 2026-03-07\n\n### Added\n- Clean-room agent development kit\n- Multi-backend LLM providers (Ollama, OpenAI, Anthropic)\n- AitherAgent class with @tool decorator\n- SQLite conversation memory\n- OpenAI-compatible server (`aither-serve`)\n- 16 agent identities as package data\n- MCP bridge to mcp.aitherium.com\n- Privacy-centric opt-in telemetry\n- Bug reporting CLI and API\n- FastAPI server with OpenAI-compatible endpoints\n- Hardware auto-detection (5 tiers, 11 profiles)\n- Fleet orchestration and multi-agent coordination\n- A2A mesh protocol\n- Federation protocol for cross-instance agent dispatch\n\n### Initial release\n- 85 passing tests\n- Apache-2.0 license\n"}, "44d529888df4b3dc2bcc4a9ce1f4ffe029062016": {"t": 1785748168.478035, "n": 83173, "h": "81a7b6469c0a4380", "c": "# Changelog\n\nAll notable changes to aither-adk will be documented in this file.\n\n## [2.24.0] - 2026-07-14\n\n### Changed — sync-family consolidation\n\n- **Removed dead code:** `adk/sync.py` (528 lines) deleted — it was unreachable,\n shadowed by the `adk/sync/` package (`import adk.sync` always resolved to the\n package; nothing loaded the module by path — verified).\n- **Grouped the sync domain modules into the `adk.sync` package:**\n `brain_sync`→`adk.sync.brain`, `files_sync`→`adk.sync.files`,\n `lockbox_sync`→`adk.sync.lockbox`, `secrets_sync`→`adk.sync.secrets`,\n `session_sync`→`adk.sync.sessions`, `settings_sync`→`adk.sync.settings`,\n `sync_watermark`→`adk.sync.watermark`. All in-tree importers updated. No\n back-compat shims were left because **zero external/monorepo code imported the\n old root paths** (verified) — a clean move, not a shimmed one. **Breaking only\n for code that imported `adk.<name>_sync` directly** (none known).\n\n### Docs — MCP module layering clarified\n\n- `adk.mcp` (enterprise AitherOS gateway client: auth/billing/token-tracking) and\n `adk.core.mcp` (minimal generic JSON-RPC adapter for any MCP server) are\n DISTINCT layers, not duplicates — each now carries a LAYER/ROLE/See-also\n docstring so they are not mistakenly consolidated.\n\n## [2.23.2] - 2026-07-14\n\n### Fixed — CompletionGate no longer false-fails on correct results\n\n- **Tolerant judge JSON extraction.** The LLM judge parsed its reply with `json.loads(text[first\"{\":last\"}\"])`, which raised `JSONDecodeError` (\"Extra data\") whenever the model appended prose after the verdict or emitted a second object — and `verify()` turned that into a fail-closed verdict, so a genuinely completed task reported `unverified`. New `_first_json_object()` uses `JSONDecoder.raw_decode` from the first `{` that begins a valid object and ignores trailing content (prose, markdown fences, extra objects). A judge that answers with no parseable JSON still fails closed (never a soft pass), now with a clear reason. The same extractor backs `_derive_criteria`.\n- **Bare code-like tokens are hard-checked.** A natural criterion like `output contains ADK_AUTO_OK` (no quotes) previously fell through to the judge; `hard_checks` now also matches bare identifier/code shapes (contains an underscore, or 4+ CAPS/digit chars), with a stopword guard so a descriptive uppercase word (`OUTPUT`, `CONTAIN`) never becomes a false required token. Quote a token to force it regardless.\n- +9 tests (24 total), including the exact trailing-data regression.\n\n## [2.23.0] - 2026-07-14\n\n### Removed — dead modules and internal integrations\n\n- **`adk.swarm`, `adk.provisioning_tools`, `adk.session_sync_integration`, `adk.addon_metering`** — four orphan modules with zero public usage have been deleted. These were internal prototypes kept for legacy monorepo consumers; public packages have not imported them since 2.21.0 (tracked as D-348). **Breaking for anyone directly importing these modules** (import will fail). They are no longer exempt from the orphan-module check.\n- **`adk.aither_bridge`** — the AitherOS-internal IRC ↔ chat gateway bridge is no longer shipped in the public aither-adk package (stripped at sync time). All public importers already degrade behind `except ImportError` (in `adk.server` and `adk.builtin_tools`), so public packages remain unaffected. The module stays in the private AitherOS monorepo for internal use.\n- **Identity document trim** — removed internal agent names from `adk/identities/aither.yaml` delegation guidance, replacing with role-generic wording (\"delegate code review, refactoring, security analysis, performance and testing to specialist agents when available\"). Effort-tier routing philosophy remains unchanged and is product design, not a leak.\n\n### Changed — genericized customer/product names in help text\n\n- CLI help/examples no longer name specific customer deployments (`--scope` template list and `deploy agent` name example use generic placeholders). Functional deploy paths are unchanged.\n\n## [2.22.1] - 2026-07-14\n\n### Fixed — real bugs surfaced by reviving the public CI (red since Jul 5)\n\n- **TLS**: bundled toolpacks (arc-brainpack, cloudflare) disabled certificate\n verification on gateway/vault calls; all now route `adk._tls.tls_verify()`\n (verified by default, internal CA bundle when present).\n- **`system_prompt` contract**: bundled-pack `[PACK DIRECTIVES]` were appended\n even to an explicit `system_prompt`; an explicit prompt now wins wholesale.\n- **Headless secrets**: `_derive_key()` used `os.getlogin()`, which raises\n `OSError` without a controlling terminal (CI, daemons, containers) — falls\n back to `getpass.getuser()` (same value on interactive boxes, so existing\n secret files keep decrypting).\n- **`pack_verifier`**: missing `cryptography` crashed with `UnboundLocalError`\n in the exception handler instead of failing closed; now degrades gracefully\n (and `cryptography` was added to the dev extra so CI tests the real path).\n- **`fleet_manager._kill_pid` (POSIX)**: reported success immediately after an\n asynchronous SIGTERM and never reaped children (zombies read as alive); now\n polls, escalates to SIGKILL, and reaps.\n- **Identity**: `aither` regained the `creative` tool category (dropped\n accidentally in a 2.7.0 WIP sweep).\n\n## [2.22.0] - 2026-07-14\n\n### Changed — internal toolkit relocation\n\n- **`adk.platform` relocated** — the internal aither-platform toolkit (ComfyUI/image-gen\n pipelines, fleet agent clients, parallel CLI/UI/tools stack) has been moved out of the\n adk SDK tree to the standalone internal `aither-platform` package at\n `AitherOS/agents/aither_platform/`. The public package remains unaffected (it already\n excluded `adk.platform` since 2.21.1). Internal AitherOS agent builds now install\n `aither-platform` separately. `adk platform` falls through to the same \"not available\"\n message on public installs; internal builds work via the relocated package.\n\n### Removed — backward-compat re-export deleted\n\n- **`adk.gateway` module removed** — the backward-compatibility re-export of `GatewayClient`\n from `adk.gateway` has been removed. Use `from adk.client import GatewayClient` instead\n (the established public API since 2.21.0). All internal importers have been updated.\n\n## [2.21.1] - 2026-07-14\n\n### Removed — internal toolkit and license-gated product no longer ship publicly\n\n- **`adk.platform`** — the internal aither-platform toolkit (ComfyUI/image-gen\n pipelines, fleet agent clients, a parallel cli/ui/tools stack) is no longer\n included in the public repo or the PyPI wheel. It was internal-only code that\n had been riding along since the aither-platform merge. `adk platform` now\n reports \"Platform toolkit not available\" on public installs; internal AitherOS\n builds are unaffected.\n- **`adk.formbridge`** — the FormBridge form-automation product is license-gated\n (PROFESSIONAL+); its implementation no longer ships in the free package. All\n import sites (`builtin_tools`, `server` route mount) already degrade behind\n `except ImportError`, so nothing else changes behavior.\n- Product test suites (`test_formbridge.py`, `test_encounter_keying.py`,\n `test_flow_tools.py`) are excluded from the public sync alongside them.\n\n## [2.21.0] - 2026-07-14\n\n### Added — Completion gate: verified-or-retried task execution\n\n- **`adk.gate.CompletionGate`** — a composable wrapper that closes the \"silent\n success\" gap: an agent's ReAct loop returns whatever the model produced and\n calls it done (`finish_reason = stop/max_steps/length`), which is a *liveness*\n signal, not a *completion* one — so an agent can \"succeed\" while doing nothing.\n `CompletionGate` runs the agent, verifies the result against acceptance\n criteria, retries with the failure fed back, and returns the response with\n `finish_reason` set to `verified` or an honest `unverified`. Never a false pass.\n - **Hard-first, un-soft-passable checks** (`adk.gate.hard_checks`): a filesystem\n path a criterion says must exist is checked on disk; a required quoted token\n must appear in the output. Only genuinely subjective criteria fall through to\n an optional LLM judge (reuses the agent's own model), which **fails closed**\n if unavailable.\n - **`gated_run(agent, task, criteria=..., max_retries=2)`** — one-shot helper.\n - `auto_criteria` (LLM writes the machine-checkable definition-of-done from the\n task), a `verifier=` override for custom checks (tests-pass / HTTP probe), and\n a `requires_action` short-circuit so it never fights human-approval pauses.\n - Compose it onto any agent exposing `run(task) -> resp{.content, .finish_reason}`\n (`AitherAgent`, `core.Agent`, or a thin adapter over a remote dispatch).\n - Exported from the top-level package: `from adk import CompletionGate, gated_run`.\n\n## [2.19.0] - 2026-07-12\n\n### Added — Eve agent importer + durable A2A tasks + canonical agent discovery\n\n- **`adk pack import <eve_agent_path>`** — convert external Eve agents to AitherADK packs\n (from GitHub or local filesystem). Maps Eve structure (instructions → system prompt,\n agent.ts → agent.yaml, skills/*.md → skills, connections/*.ts → MCP, tools/*.ts →\n tools/node verbatim) to AitherADK format with a single command. Eve agents (compiled\n manifest format, schema v35) become installable packs compatible with `adk pack install`.\n- **Durable A2A task lifecycle (`TaskManager` backed by FileStore)** — agent-to-agent\n tasks now survive container restarts. Tasks persist as append-only JSONL\n (`.adk/tasks.jsonl`); in-memory dict for fast access; `create_task()` / `update_status()` /\n `add_artifact()` / `add_message()` all save to disk. Google A2A v0.3.0 spec compliant.\n- **Canonical agent card discovery** — `A2AServer.mount()` now exposes:\n - `GET /.well-known/agent-card.json` — Agent Card (canonical; contains agent identity,\n capabilities, skills, endpoints)\n - `GET /.well-known/agent.json` — Agent Card (legacy, redirects with 308 to canonical path)\n - `POST /a2a` — JSON-RPC 2.0 for message send/task lifecycle\n - `GET /a2a/tasks/{id}/subscribe` — SSE streaming task updates\n- **Four built-in agent packs now complete and catalogued**:\n - `hermes` — architecture review & trade-off analysis\n - `openclaw` — web research & source verification\n - `claude-code` — feature development & debugging\n - `analyst` — structured data inference & anomaly detection\n All discoverable via `adk packs` / `adk install pack:<name>`.\n- **Tolerant tool-call JSON parsing** — malformed tool JSON (extra properties, missing\n name/arguments, string-encoded nested args, missing keys) is now gracefully degraded\n instead of silently failing. `_parse_tool_json()` accepts both `\"arguments\"` and\n `\"parameters\"` keys, coerces string-encoded args, and skips unparseable calls instead\n of crashing the turn.\n\n### Known limitations\n\n- Eve agent importer: remote GitHub fetching not yet implemented (local paths only).\n- Durable task store: currently JSONL (suitable for thousands of records, not millions).\n\n## [2.18.0] - 2026-07-10\n\n### Added — AitherShell command center + Claude Code session management\n\n- **`aither sessions`** — Claude Code session manager: interactive full-screen\n browser (type-to-filter, transcript preview, deep full-text `search`, resume\n **in the current terminal** or as Windows Terminal tabs), crash guard\n (`guard install` = at-logon watchdog that snapshots live sessions and\n auto-restores the whole set after a terminal crash), and `ingest [--watch]`\n (incremental, secret-guarded sync of session conversations into the local KB\n / CompanyBrain via the standard ingest pipeline).\n- **`aither hq`** — command-center dashboard: per-service fleet health, LLM\n queue (depth/VRAM/models), Pulse alerts, live session count, inbox unread —\n auto-refreshing, with one-key jumps into chat, sessions, inbox, agents,\n brief, watchtower, and docker recovery.\n- **`aither inbox`** — unified queue over CommCore mail, Relay\n mentions/notifications, and Pulse alerts; open/mark-read/DM-reply from the\n terminal. Every source degrades independently.\n- **`aither agents`** — roster console + `agents ask -e <1-10> <agent> <q>`\n (effort-tiered ask via Genesis, with automatic `/chat/stream` fallback when\n the `/agent/sync` execute gate rejects host callers) + forge dispatch\n inspection + routine health.\n- **`aither palette`** — universal fuzzy picker across actions, sessions, and\n services; **`aither brief`** — renders Atlas's executive briefing in the\n terminal; **`aither watch`** — fleet watchtower with named wedge-signature\n detection (docker-WSL wedge, LLM-queue stall, crash-loop uptime regression)\n and optional docker auto-recovery.\n- All fleet reads go through a shared fail-soft `FleetClient` (internal-CA\n TLS via `adk._tls`, dashboard-grade timeouts, per-source degraded states).\n- Node shell-cli (`@aitherium/shell-cli` 1.13.0): `aither\n sessions|hq|inbox|palette|brief|watch|agents|docker` now pass through to the\n Python shell instead of being intent-classified as chat prompts.\n\n## [2.17.1] - 2026-07-05\n\n### Fully self-service mesh join — no customer key handling\n\n- **Auto-join headscale off the onboard response.** The conductor now auto-issues a\n headscale pre-auth key for NAT'd nodes and returns it in the `/v1/mesh/onboard`\n response; `mesh.join` consumes it and brings up the tunnel automatically. A customer\n runs one command — they never mint, fetch, or set a headscale key. Explicit\n `--headscale-key`/`AITHER_HEADSCALE_AUTH_KEY` still take precedence.\n- **Security:** `_tailscale_up` now redacts the auth key from all logs and exceptions\n (a failed `tailscale up` previously echoed the full argv, leaking the key into logs).\n\n## [2.17.0] - 2026-07-05\n\n### Added — Headscale mesh transport + local Qdrant + AitherConnect onboarding\n\n- **Headscale NAT-friendly mesh transport.** When raw WireGuard UDP:51820 is not\n viable (customer boxes behind NAT, corporate firewalls, CGNAT), `adk mesh join\n --headscale` routes the mesh tunnel through Tailscale's Headscale control plane.\n The overlay IP is still Conductor-assigned (10.77.0.0/16); Headscale provides\n only the transport layer. Configured via `AITHER_MESH_TRANSPORT=headscale`,\n `AITHER_HEADSCALE_URL` (default: https://headscale.aitherium.com), and\n `AITHER_HEADSCALE_AUTH_KEY` (pre-generated key). Automatic fallback to raw\n WireGuard if Headscale setup fails.\n- **Local Qdrant vector DB provisioning.** `adk stack` now provisions Qdrant\n locally when `AITHER_VECTOR_DB=qdrant` is set, enabling offline-first RAG\n without external dependencies. Tiered: local Qdrant in-fleet, cloud Nexus\n for enterprise multi-tenant deployments.\n- **Conductor URL public fallback.** `adk mesh join` resolves the internal\n `aitheros-conductor:8193` hostname; if unreachable (e.g., self-hosted nodes),\n it falls back to the public `conductor.aitherium.com:8193` endpoint,\n eliminating bootstrap configuration friction.\n\n### Documentation\n\n- **Self-hosting runbook.** New `docs/SELF_HOSTING.md` covers deploying AitherOS\n on customer infrastructure with Qdrant + mesh + AitherConnect onboarding.\n- **Mesh transport constraints documented.** Raw WireGuard requires public\n UDP:51820 endpoint (cloud instances, dedicated hardware); Headscale for\n NAT'd networks. Noted in CLI help and module docstrings.\n\n## [2.15.0] - 2026-07-04\n\n### Added — built-in web admin console + portal-profile settings sync\n\n- **Web admin console.** The page `adk up` opens (served at `/` and `/ui`) is now a full,\n self-contained console — Chat plus six admin tabs: **Backend** (view/switch LLM provider,\n set keys, test connection), **Packs** (list/enable/disable/reload tool packs),\n **Sessions** (browse/delete conversations), **Logs** (redacted agent-log tail),\n **Graph** (knowledge-graph search/neighborhood/stats), and **MCP** (add/remove external\n MCP servers). No build step, no CDN — a single packaged HTML asset. The original minimal\n streaming chat page stays at `/chat` for back-compat.\n- **Admin API (`adk/admin_api.py`).** ~20 endpoints under `/admin/*`, all bearer-gated by the\n existing middleware (never in `_skip_auth_paths`). Operates on the live agent — LLM backend\n swap (`LLMRouter.switch_backend`), pack reload, and MCP registration take effect without a\n restart.\n- **Settings sync (`adk/settings_sync.py`).** The user's portal profile is the source of\n truth: on startup the agent pulls `preferences.adk` and applies it over the local\n `~/.aither/config.yaml` cache; admin-console edits push a debounced snapshot back to\n `PUT /api/settings/preferences`. Provider API keys and MCP auth headers are device-local\n secrets and are never included in the pushed snapshot. Opt out with `AITHER_SETTINGS_SYNC=false`.\n\n### Security\n\n- **Timing-safe token comparison.** The server bearer check now uses `hmac.compare_digest`\n (the token is reachable over the public tunnel; a byte-wise `!=` leaked it to timing attacks).\n- **MCP add is SSRF-guarded + confirmed.** Adding an external MCP server is a two-step\n prepare→confirm flow; URLs resolving to loopback / private / link-local / metadata addresses\n are rejected (override with `AITHER_ALLOW_PRIVATE_MCP=1` for trusted local servers).\n- **Config editor is allowlisted.** `PATCH /admin/config` writes only safe scalar prefs and\n hard-denies fields that could execute code or redirect traffic at startup (e.g.\n `aither_toolpack_dirs`, backend base URLs).\n- **Log tail is scrubbed.** Bearer tokens, `#k=` fragments, and `sk-`/`aither_sk_` keys are\n redacted from the log-tail endpoint; the tunnel log is excluded.\n\n## [2.14.8] - 2026-07-04\n\n### Added — `adk up` for normal humans (no API key, no prereqs, live chat)\n\n- **Hosted-brain default — no API key required.** When there's no local model and no\n provider key, `adk up` signs in (device-flow) and routes inference through the gateway\n using the portal token (`AITHER_LLM_BACKEND=gateway` + bearer). A non-technical user\n never needs to obtain or paste an LLM API key. Explicit `--provider`/local model still\n take precedence; unattended `--yes` with no token/backend fails cleanly (exit 3).\n- **cloudflared auto-download.** If the tunnel binary is missing, `adk up` downloads the\n right platform asset to `~/.aither/bin` (SHA256-verified against the release; opt out with\n `AITHER_CLOUDFLARED_REQUIRE_CHECKSUM=0`) instead of exiting with an install hint.\n- **Built-in streaming chat page.** The agent now serves a self-contained chat UI at `/`\n (and `/chat`) with a live \"thinking…\" indicator and token-by-token streaming — so a human\n has somewhere to talk to it with immediate feedback instead of a blank wait. `adk up` opens\n it locally and passes the callback bearer in the URL fragment (`#k=…`), so `/chat/stream`\n stays authenticated (not an open proxy) with nothing to paste.\n- **Human-readable failures.** `adk up` errors now carry a plain-English `next_action`\n (kept machine-readable in `--yes` JSON).\n\n### Fixed\n\n- `adk up` chat link pointed at a non-existent `/settings/agent` route; it now points at the\n agent's own streaming chat page.\n\n## [2.14.7] - 2026-07-04\n\n### Added\n\n- **`adk up` — one command to run a persistent, fleet-connected agent.** Collapses\n the scattered onboarding surface (`host`/`quickstart`/`connect`/`login`/`setup`)\n into a single command that starts `aither-serve` (single agent, identity `aither`\n by default), opens a Cloudflare quick-tunnel for fleet reachability, registers with\n the portal, and installs a reboot-persistent autostart entry (Windows Task Scheduler\n / systemd-user / launchd). Detaches by default so the terminal is freed; `--foreground`\n blocks. Reuses the existing device-flow login, cloudflared discovery, provider-key\n store, and `_preflight_check` backend detection.\n- **Fully non-interactive path for autonomous agents.** `adk up --yes` (or any\n non-TTY invocation) takes ZERO prompts — everything resolves from flags, env, and\n saved config — and emits a single machine-readable JSON status line with meaningful\n exit codes (`0` ok, `2` bad-input, `3` no-backend, `4` tunnel-missing, `5` register-failed).\n Degrades to local-only (warn, not fail) when a portal token is absent unless\n `--require-register` is set.\n- **`adk down` / `adk status --json` companions.** `down` stops the agent + tunnel,\n best-effort deregisters, and removes the autostart entry; `status` reports the running\n agent (liveness + `/health` + tunnel + registration), with `--json` for agents/CI.\n- **`adk stack`** — the previous `adk up` behaviour (supervise the Room + Ollama consumer\n stack) now lives here; `adk up` is the connected-agent command.\n\n### Internal\n\n- New `adk/agent_daemon.py`: detached process spawn, status file\n (`~/.aither/adk-up.json`), pid liveness/teardown, and cross-platform autostart install.\n\n## [2.14.6] - 2026-07-04\n\n### Added\n\n- **`adk.preflight` — self-bootstrap capability probe.** `run_preflight(agent,\n spec)` wakes an agent up knowing what it is actually plugged into: a bounded\n (1.5s) parallel `LivenessProbe` over the router's resolved backends\n (primary/reasoning), the embeddings winner, remote MCP tools, the structured-ML\n / `/ml/teach` endpoints, and voice — returning an honest `CapabilityReport`.\n Status vocabulary distinguishes reachability from entitlement\n (`OK`/`MISSING`/`UNREACHABLE`/`AUTH`/`TIER_DENIED`/`UNSUPPORTED`), a hosted slot\n never prints a bare `OK`, and a required-but-unsatisfied slot aborts before the\n loop instead of blind-firing. Headless task/rules resolution + an allowed-roots\n policy binding (`builtin_tools.set_allowed_roots`, which resets the memoized\n cache so the override actually enforces).\n- **Multimodal (image) messages.** `Message.content` now accepts an OpenAI-style\n content-part list (`str | list[dict]`), passed through verbatim to the\n OpenAI-compatible body (gateway / vLLM / OpenAI). New `adk.llm.multimodal`\n helpers — `image_message()`, `image_content_parts()`, `to_image_url()` — build\n the parts and encode local image bytes/paths into `data:` URIs.\n- **Real vision preflight probe.** The vision slot is no longer unconditionally\n `UNSUPPORTED`: it sends a discriminable image (a solid red square) and requires\n the model to name the color, so a blind/text-only model cannot false-pass. `OK`\n only on a real round-trip; `AUTH` on 401/403; `UNSUPPORTED` when there is no\n OpenAI-compatible provider or the model cannot see the image.\n\n## [2.14.5] - 2026-07-04\n\n### Added\n\n- **`adk.reasoning.mcts` — generic Monte-Carlo Tree Search library.** A reusable\n MCTS engine (ported from the internal `UnifiedMCTS`, with all internal couplings\n removed) that any agent can drive over its own environment:\n - `MCTSEnvironment` protocol (`get_state_hash` / `get_actions` / `step` /\n `evaluate` / `clone`) — implement it and search.\n - Three **optional, default-off** model seams — `TransitionModel`, `PolicyModel`\n (PUCT priors), `ValueModel` — so a learned world-model / policy / value can be\n injected without touching the core; with all three `None` the engine is a plain\n UCT search.\n - `ObservedTransitionModel` adapter — a cheap online lookup world-model (hit =\n exact stored transition; miss = identity + negative \"unknown\" reward +\n `is_uncertain`, biasing exploration toward learning), with JSONL save/load for\n cross-run reuse.\n - `UnifiedMCTS(config).search(env)` returns an `MCTSResult` (best action, value,\n confidence, principal-variation path) and an optional `MCTSTrace`\n (visit-distribution policy target) for distillation.\n- **Self-bootstrapping agent machinery.** Build an agent from a declarative spec\n instead of bespoke wiring:\n - `adk.bootstrap.build_agent_from_spec(path) -> (AitherAgent, RunCtx)` — resolves\n prompts, activates required/optional tool packs, and carries a `memory_map`\n (a `node_type -> (role, tier)` override for memory writes, never a gate).\n - `adk.prompts.PromptBridge` — resolves an agent's prompt bindings from files\n (relative to the spec) or `AitherPrompts` dot-keys (optional import).\n - `adk.packs.PackActivator` — eager activation of installed tool packs by name\n over `register_tool_packs`; a required pack that registers zero tools raises\n `PackUnavailable`, optional packs soft-degrade.\n - `python -m adk.bootstrap.generic_agent <spec.yaml>` inspects a spec (resolved\n name, activated packs + tool counts, prompt keys, memory map) without running a\n live loop.\n- **`deep_research` tool pack** — a full multi-engine web-research workflow as\n `dr_*` agent tools (keyless search, page fetch + cache, site mirror, a citable\n findings knowledge graph, and markdown/PDF/DOCX report generation), activatable\n by name via the tool-pack loader.\n\n## [2.14.4] - 2026-07-04\n\n### Added\n\n- **`structured_ml` tool domain** — zero-shot structured-data inference for agents,\n backed by TabFM (tabular classification/regression) and TimesFM (time-series\n forecasting). Three tools:\n - `tabular_classify(support_rows, target, query_rows)` — classify rows from a\n labeled support set, in-context (no training step; up to 10 classes).\n - `tabular_regress(support_rows, target, query_rows)` — predict a numeric target.\n - `timeseries_forecast(series, horizon)` — forecast future values of a series.\n\n The tools POST to a structured-ML inference service resolved from\n `AITHER_STRUCTURED_ML_URL` (default `http://localhost:8192`), reject non-http(s)\n URLs, and summarise oversized responses to protect the agent's context.\n- **`Capability.STRUCTURED_INFERENCE`** capability token and a new **`analyst`**\n identity/pack that ships the `structured_ml` domain by default. The domain is\n opt-in — only identities that enable the `structured_ml` category get the tools.\n\n## [2.14.3] - 2026-07-03\n\n### Fixed\n\nAll four bugs below were found by actually booting `adk run` against a real\nchat model and driving a real tool-calling request end-to-end (not inferred\nfrom reading the code) — each one silently broke the \"local agent uses its\nconfigured model and its tools actually work\" path.\n\n- **`AITHER_MODEL` / `config.model` silently ignored by the OpenAI-compatible\n backend**: `LLMRouter.__init__` never read `config.model` into `self._model`\n unless the caller passed `model=` directly to the constructor — which nothing\n did. Every explicit model choice (env var, config file) was dropped, the\n provider fell back to a hardcoded per-backend default (`gpt-4o-mini` for the\n `openai` backend), and an unrecognized model name got silently remapped\n server-side to the wrong backing model. Concretely: setting\n `AITHER_MODEL=aither-orchestrator` had no effect — every request actually\n went to a different model that was never meant to be called directly and\n can't reliably use tools.\n- **`OPENAI_BASE_URL` ignored for explicit `--backend`/`AITHER_LLM_BACKEND`\n selection**: `LLMRouter._auto_detect()`'s explicit-backend branch hardcoded\n `base_url=None` when constructing the provider, instead of reading\n `config.openai_base_url` (which two other call sites in the same file\n already did correctly). Any self-hosted OpenAI-compatible endpoint\n (MicroScheduler, vLLM, LM Studio) configured via `OPENAI_BASE_URL` was\n silently ignored in favor of `https://api.openai.com/v1`.\n- **Tool-call steering never engaged on default-effort chat**: the retry that\n nudges a model back into structured tool-calling when it responds with prose\n instead of a function call was gated behind `effort >= 6`. Default `effort`\n for a plain `agent.chat(message)` call resolves to `5`, so the safety net —\n built specifically for this failure mode — never fired for default requests.\n Also now forces `tool_choice=\"required\"` on the retry instead of relying on a\n text nudge alone.\n- **Every non-string builtin-tool parameter silently typed as `\"string\"` in\n the generated tool schema**: `builtin_tools.py` uses\n `from __future__ import annotations`, so `_extract_parameters()` in\n `tools.py` was reading postponed (string) type hints (`\"int\"`, not `int`)\n straight out of `fn.__annotations__`, which never matched\n `_type_to_schema`'s type-object keys and silently fell through to\n `{\"type\": \"string\"}`. Every builtin tool with an `int`/`float`/`bool`/`list`\n parameter was affected — e.g. `file_read(start_line: int, end_line: int)`\n advertised both as strings, so a model correctly calling the tool with\n `\"start_line\": \"0\"` crashed the actual implementation with\n `unsupported operand type(s) for -: 'str' and 'int'`. Fixed by resolving\n hints through `typing.get_type_hints()` instead of the raw annotations dict.\n `_coerce_arguments()` — the runtime safety net that's supposed to coerce a\n model-supplied `\"0\"` string into `0` before a tool call executes — had the\n exact same raw-`__annotations__` bug and silently no-opped for every builtin\n tool; fixed the same way.\n- **Streaming tool-loop could hang a connection forever**: `chat_stream()`'s\n fallback to the sync tool loop (tool calls can't stream token-by-token) had\n no timeout, so a model that didn't cleanly terminate a tool-calling round\n trip left the SSE connection open indefinitely with zero bytes sent. Bounded\n with a 60s `asyncio.wait_for`; on timeout the stream yields a clear message\n instead of hanging.\n\n## [2.14.2] - 2026-07-03\n\n### Fixed\n- **AitherShell session amnesia**: the REPL generated a fresh `session_id` for\n EVERY message, so conversation history never followed across turns (the\n server stored each turn under a different session). One stable id is now\n minted per shell run; `/resume` and `/new` semantics unchanged, and the\n cross-restart auto-restore of the last session now actually works.\n\n## [2.14.1] - 2026-07-02\n\n### Added\n- **Account-tied licensing via device-flow login** — `adk login` now persists the account\n license returned by the device-flow handshake to `~/.aither/license.json` (base64\n `license_key` → `{payload, signature}` envelope), so entitlements from the authenticated\n account carry into every later `adk` invocation without re-auth. Pairs with the fleet-side\n node tool-pack gate that grants packs by plan-tier rank.\n- **`can_use_formbridge` / `can_use_untether` entitlements** (PROFESSIONAL+ rank ≥ 3) backing\n the FormBridge and UNTETHER agent tool-packs.\n\n### Fixed\n- `adk` CLI: guard the optional `memory_graph` singleton (was a latent `NameError` on the\n `/stats`, `/memory`, and save paths when memory was not constructed).\n\n## [2.14.0] - 2026-07-02\n\n### Added\n- **`adk.memory_wiki` — LyraWiki-style self-managed semantic knowledge** (opt-in; nothing constructs it by default): `MemoryWiki(graph_memory)` maintains curated wiki ARTICLES as `wiki_article` nodes (title/slug, LLM-owned markdown with `[[wikilinks]]`, embedding, durable tier, `CONSOLIDATED_FROM` edges to source memories, governance-ledgered revisions).\n - `consolidate(llm, since=, budget=)` clusters unconsolidated episodic/fact nodes (embedding + keyword), drafts/UPDATEs articles via the INJECTED llm callable (`str -> str` or `messages -> str`, sync or async — no hard model dependency), cites source node ids, marks contradicted sources superseded, then DEMOTES consolidated sources to a fast-decay tier via `promote()` (represented knowledge doesn't need raw copies).\n - `lint(llm=None)` — deterministic health checks (orphan wikilinks, empty/stale articles, live-but-contradicted pairs) + an optional LLM audit pass.\n - `prune(relevance_floor=0.05, hard_delete_after_days=14)` — article relevance = tier freshness × reinforcement (reinforce-on-recall) + link-degree to LIVE memories; below the floor → reversible governance tombstone; entombed past the window → **HARD-DELETE** (the tombstone snapshot itself is purged — content and embedding irrecoverable; the true-deletion path). A node is never deleted without a tombstone first.\n - `recall(query, limit)` — article-first RAG (curated articles rank above raw nodes) that reinforces returned articles.\n- **`adk.routines` — agent heartbeat + self-programmed routines**: `RoutineStore` is a durable registry (`~/.aither/routines/{agent}.json`, `AITHER_DATA_DIR`-aware, injectable path) of cron-scheduled self-prompts `{name, cron, instruction, enabled, last_run, last_result, tags}` that rehydrates into the existing `adk.cron.CronScheduler` on `start()`. A fire runs `agent.chat(instruction)` with the agent's full toolset — the agent programs its future self with its own adk. Guardrails: `max_routines` (12), a 5-minute min fire interval per routine, a per-fire timeout, and every result ledgered (truncated) to `last_result`. Direct-callable routines (`register_direct`) fire a bound method instead of a self-prompt. Self-management tools (`routine_create/list/update/pause/resume/delete/run_now`) ship as OpenAI tool defs + handlers (`build_routine_tools` / `register_routine_tools` / `routine_tool_defs`); the handlers ONLY touch the RoutineStore — they can never modify agent config or safety-relevant settings (the leash principle).\n- **`AitherAgent(routines=True, memory_maintenance=True)`** (both default **False** → the default agent is byte-identical): `routines=True` builds the RoutineStore + scheduler, registers the self-management tools, and starts the heartbeat lazily on the first `chat()` (or via explicit `start_routines()`). `memory_maintenance=True` registers DEFAULT routines — `wiki_consolidate` (every 2 h), `wiki_lint` / `wiki_prune` / `graph_sweep` (daily) — as DIRECT method fires so memory upkeep runs even on tiny models, while staying visible/manageable via `routine_list`.\n- `TombstoneStore.purge(tombstone_id)` — the hard-delete endpoint: irrecoverably removes a tombstone snapshot and rewrites the persisted store without it (consumed by `memory_wiki.prune`).\n\n## [2.13.6] - 2026-07-02\n\n### Added\n- **Qdrant backend for GraphMemory dataplane sync — TRUE two-way, verified live.** Set `AITHER_FLEET_QDRANT_URL` and the sync uses Qdrant (`upsert` + `scroll` + filtered `search`) instead of Nexus. This is what actually delivers cross-agent memory sharing: a node is upserted with its OWN embedding under a deterministic per-(tenant,node) point id (idempotent re-push), and `fleet_pull` reliably SCROLLS a tenant's points back — the enumerate Nexus lacks (its `/search` doesn't return ingested docs and `/export` needs lancedb, which has a hard dependency conflict in the fleet). **Proven live with 2 agents:** agent-1 ingests → auto-syncs to the tenant dataplane; a fresh agent-2 in the same tenant `fleet_pull`s all of agent-1's nodes and can search them; a different tenant pulls nothing (isolation). Dimension-safe (never mixes vector dims in a collection). Local SQLite stays source of truth; best-effort, never raises.\n\n## [2.13.5] - 2026-07-02\n\n### Fixed\n- **CRITICAL — GraphMemory dataplane sync push was 100% failing.** `_fleet_push_node` sent `source_type=\"graph\"` / `content_type=\"graph_node\"`, which AitherNexus rejects as invalid enums (HTTP 500) — so NO graph node ever replicated in 2.13.4. Now sends the accepted `source_type=\"manual\"` / `content_type=\"text\"` (graph provenance is carried in `metadata.synced_from`). **Verified live** against a fleet Nexus: a 4-node ingest replicates with `pending==0` and the collection count reflects it.\n\n### Added\n- **Swarm-awareness notification** — on a successful sync batch, `GraphMemory` emits a best-effort `graph.synced` event (tenant/workspace/agent/collection/count) to `AITHER_FLEET_EVENTS_URL` so OTHER agents in the tenant/swarm can pull the fresh data and deconflict in-flight work (push-based awareness instead of polling). No-op unless the env is set.\n\n### Known limitation\n- Cross-agent **rehydration** (`fleet_pull`) is only reliable when the fleet Nexus is backed by lancedb (persistent + exportable). An in-memory Nexus stores on `/ingest` but neither its `/search` nor `/export` returns the docs — two-way read needs the persistent Nexus.\n\n## [2.13.4] - 2026-07-02\n\n### Added\n- **Memory-maintenance primitives on `GraphMemory`** (all opt-in; default behaviour byte-identical):\n - **Reinforce-on-recall**: `recall_with_activation(..., reinforce=True)` bumps `reinforcement_count`/`last_reinforced` on every RETURNED node's metadata mirror (one UPDATE per node, WAL-safe) — making the `MemoryRecord` reinforcement fields the activation scorer reads LIVE. Default `False` = zero writes.\n - **`sweep(now, archive_below=0.05, max_nodes=None, dry_run=False)`**: read-time decay enforced at rest — archives nodes past their tier TTL with freshness×reinforcement score below `archive_below` and `reinforcement_count <= 1`; `max_nodes` additionally archives the lowest-scored overflow. PERMANENT tier immune. Every archive is a reversible governance tombstone (`TombstoneStore.recover`) + a FORGET ledger entry; a node is never deleted without a tombstone. `dry_run` returns the would-archive list without acting.\n - **`promote(node_id, tier=?, role=?)`**: re-tier/re-role a node IN PLACE (tier/role columns + metadata mirror) — edges, embedding and ledger history survive; ledgers an UPDATE entry when governance is on.\n- `MutationType.UPDATE` ledger entry kind (in-place re-tier/re-role).\n- **Per-tenant dataplane sync for `GraphMemory`** — replicate the local graph to a per-tenant vector dataplane (Nexus/Qdrant) so a sovereign agent's memory is durable + rehydratable across restarts and hosting boundaries. `fleet_push_all_nodes()` (catch-up), `fleet_pull()` (best-effort semantic top-up), `fleet_sync_pending()`, and auto-sync on ingest (`_maybe_autosync` + `drain_sync()`). **On by default** (`AITHER_FLEET_SYNC=auto`; only `false/0/off` disables) with an inferred target (local Nexus `:8122` or the gateway in cloud mode) and a SEPARATE collection (`AITHER_FLEET_GRAPH_COLLECTION`, default `graph_memory`) from the KV memory sync. New `GraphMemory(tenant_id=, workspace_id=, fleet_url=, auto_sync=)` constructor args override env (required for multi-tenant host processes). Node `synced` column + schema v4 migration. TLS verify is CA-bundle-aware (`AITHER_CA_BUNDLE`) for the internal-TLS mesh. Local SQLite stays the source of truth; every sync path is best-effort and never raises into ingest.\n\n## [2.13.3] - 2026-07-02\n\n### Added\n- **Canonical self-deploying embeddings provider** (`adk.embeddings`): one embedding provider for the whole SDK so vectors are portable across scopes (platform / tenant / sovereign) — same model + dimension everywhere. Pinned to `nomic-embed-text` (768-d; the vLLM `--served-model-name` for nomic-embed-text-v1.5). Lazy, single-flight resolution chain: explicit `AITHER_EMBEDDINGS_URL` → local vLLM (`:8209` then `:8120`, HTTPS-then-HTTP) → local Ollama → gateway (`AITHER_GATEWAY_EMBEDDINGS_URL`) → auto-deploy a local vLLM embeddings container if a GPU + Docker are present (opt out with `AITHER_EMBED_AUTODEPLOY=0`) → CPU sentence-transformers (384-d, degraded) → feature-hash (384-d, degraded, always works). Every batch is dimension-tagged so callers never silently mix 768-d and 384-d vectors. `get_default_embedder()`, `embed_texts()`, `embed_one()`, `get_provider()`, `reset_provider()`.\n\n### Changed\n- **`GraphMemory` now defaults its embedder to the canonical provider** (opt out with `AITHER_GRAPH_EMBEDDER=legacy`), so every adk agent shares one 768-d space. Added a meta-table dimension guard: the index is pinned to its first embedding's dimension and later different-dimension vectors are refused (kills 768↔384 mixing that silently poisons cosine similarity).\n\n## [2.13.2] - 2026-07-01\n\n### Added\n- **Session persistence**: AitherShell now auto-saves the active session_id to `~/.aither/config.yaml` after each chat turn and auto-restores it on the next shell launch. Multi-turn conversations survive across restarts — the messages were always persisted in SQLite + JSON, only the session pointer was missing.\n- `/new` command to start a fresh session (clears saved session_id).\n- `/sessions` command to list recent sessions with agent name, message count, and timestamp. Resume any with `/resume <id>`.\n\n## [2.13.1] - 2026-07-01\n\n### Fixed\n- **CRITICAL:** `deploy_connect()` now sends a HEAD request to verify the GitHub release asset exists before downloading, with a clear error if the asset is missing.\n- **CRITICAL:** Compose file URLs are now pinned to a stable commit hash (configurable via `AITHER_COMPOSE_PIN` env) instead of tracking the mutable `main` branch.\n- **CRITICAL:** `deploy_adk_node()` now falls back to starting `adk-serve` natively when Docker is unavailable, instead of blocking with \"Docker not installed\".\n- **HIGH:** GHCR login failure now blocks deployment immediately with actionable guidance, instead of warning and continuing to a confusing image-pull failure.\n- **HIGH:** Health checks now stream the last 30 lines of Docker Compose logs on failure, so the user can see why a container didn't start.\n- **HIGH:** vLLM port allocation now probes for conflicts before assigning; if port 8200 is busy, workers are automatically remapped to the next free port.\n- **MEDIUM:** Compose file downloads are now cached with ETag; re-running `adk deploy node` skips re-downloading an unchanged file.\n- **MEDIUM:** DGX Spark discovery now respects `AITHER_DGX_HOSTS` env (comma-separated) for custom network topologies, instead of only checking `spark.local` and `192.168.0.33`.\n- **MEDIUM:** Cloud API keys (Anthropic, OpenAI, DeepSeek) are now validated with a lightweight probe during `adk setup` infra scan, so invalid keys surface early.\n- **MEDIUM:** `deploy_connect()` now validates the extracted extension has a `manifest.json` and warns if it's missing.\n\n## [2.13.0] - 2026-07-01\n\n### Removed\n- **`adk.faculties.memory_graph`** — the pickle-based `MemoryGraph` module is deleted. `from adk.faculties import MemoryGraph` now resolves to the canonical `adk.graph_memory.GraphMemory` for back-compat.\n- **`adk.platform.memory`** — the entire legacy memory subsystem (`MemoryManager`, `UnifiedMemorySystem`, `GameEngine`, `StoryboardEngine`, `AnchorGenerator`) is deleted. Agent memory is `adk.memory.Memory` + `adk.graph_memory.GraphMemory`.\n\n### Changed\n- `adk work` command now uses `GraphMemory` instead of the removed pickle-based faculty graph.\n- `adk.MemoryGraph` and `adk.faculties.MemoryGraph` lazy exports now resolve to `adk.graph_memory.GraphMemory`.\n- `agent.set_memory_graph()` kept for back-compat but documented as deprecated; agents already wire `GraphMemory` automatically in `__init__`.\n- Platform startup (`adk.platform.infrastructure.startup`) gracefully handles missing `MemoryManager`.\n- Platform CLI module listing and health check updated to reflect removed memory module.\n\n## [2.12.7] - 2026-07-01\n\n### Deprecated\n- `adk.faculties.MemoryGraph` — emits `DeprecationWarning` on instantiation; use `adk.graph_memory.GraphMemory` (SQLite-backed, governed, embedding-native) instead.\n- `adk.platform.memory` package — the legacy `MemoryManager`, `UnifiedMemorySystem`, `GameEngine`, and `StoryboardEngine` modules now emit `DeprecationWarning` on import; use `adk.memory.Memory` and `adk.graph_memory.GraphMemory` for new code.\n\n### Changed\n- Updated `adk.faculties` docstring and usage examples to point at the canonical memory surface.\n\n## [2.12.6] - 2026-07-01\n\n### Changed\n- Trimmed public package debt by removing grid runbook artifacts and shell/room binary placeholder directories from published artifacts.\n- Removed stale public-sync allowlist entries for deleted phase test files and added explicit public-sync guards for binary placeholder directories and grid artifacts.\n- Updated the public README grid note so it no longer links to runbook files excluded from the SDK package.\n\n## [2.12.5] - 2026-07-01\n\n### Added\n- Added best-effort fleet memory sync from local ADK memory into Nexus/Qdrant-compatible RAG stores, including batch catch-up and vector-search recall hooks.\n- Added saved config/env support for fleet memory URL, collection, and sync mode.\n\n### Changed\n- Excluded FormBridge runtime/test internals from public ADK packaging and public repo sync.\n\n## [2.12.4] - 2026-07-01\n\n### Added\n- Added a hot-reload endpoint for installed agent packs so newly applied pack tools can be picked up without restarting `aither-serve`.\n\n### Fixed\n- Fixed synchronous `ChatRelay` calls on Python 3.12 when no event loop is running; async WebSocket delivery is still scheduled when a loop exists.\n- Aligned gateway inference tier tests with the current public ladder: free, starter, pro, enterprise, platform.\n\n## [2.12.0] - 2026-06-28\n\n### Added — one-command local inference + upgrade path (click-to-run)\n- **`adk quickstart-local`** — detects hardware, picks a backend\n (Ollama / llama.cpp / vLLM), installs it, downloads a model, verifies, and\n prints next steps. Bootstraps Ollama itself via winget/brew/install.sh when\n missing (no \"go install it\" dead-ends). Default model `gemma4:e2b`.\n- **`adk backend switch <ollama|llamacpp|vllm>`** (+ `backend status`) — migrate\n the active deployment between engines; re-points config and smoke-tests.\n- **`adk install pack:<name>`** (+ `adk install list` / `adk packs`) — install\n ready-made agent packs (openclaw, hermes, claude-code) into `~/.aither/agents`.\n\n### Fixed (found via live end-to-end testing on Windows + CUDA)\n- quickstart-local reported success on a dead endpoint (smoke test was warn-only);\n a failed smoke now fails the command, with a service-readiness wait.\n- `--model` resolved to `None` (getattr default never fired) → uses the real default.\n- Smoke test: 30s→180s timeout for cold model load; accepts reasoning-model output\n (empty `content` at low token budgets is no longer a false failure).\n- `adk install pack:<name>` colon syntax (argparse rejected it before dispatch).\n- `adk backend status` health probe used a nonexistent `/health` path → 404; now\n probes `/v1/models` (uniform OpenAI-compatible liveness for all three backends).\n- Windows: removed non-ASCII glyphs from printed strings that crashed the cp1252\n console (a `✓` even made a successful install report failure).\n\n## [2.11.1] - 2026-06-14\n\n### Fixed — `adk host` / `adk login` device flow (autonomous onboarding)\nFound driving `adk host` against production; all broke real device-flow login:\n- **Device flow hit the portal FRONTEND, not AitherIdentity.** `adk login`/`adk host`\n POSTed `/auth/device/code` to portal/veil.aitherium.com, which 307-redirects API auth\n to `/login`. Added `_resolve_identity_url()` mapping the aitherium.com topology to\n `idp.aitherium.com` (the Identity API); localhost/bespoke pass through unchanged.\n- **Cloudflare 403'd urllib.** `idp.*` sits behind Cloudflare, which blocks urllib's\n default `Python-urllib/3.x` UA. Device-code + poll requests now send a real\n User-Agent + Accept (verified urllib→idp 200, was 403).\n- **`adk host` now self-authenticates via device flow** when there's no token, and\n **retries via device flow on a 401/403** (a stale saved token no longer dead-ends\n registration).\n\n## [2.11.0] - 2026-06-14\n\n### Added — attach your own MCP \"hands\" to a self-hosted agent\nA self-hosted agent now has the full brain (your model) · body (this loop) · **hands**\n(your tools) trifecta. The AitherOS platform relays the MCP servers you registered for\nyour agent in the `/stream` request body as `mcp_endpoints`, and the loop wires their\ntools into the ReAct turn:\n- **`adk/mcp_endpoint_tools.register_mcp_endpoint_tools(agent, endpoints)`** — speaks MCP\n JSON-RPC (`tools/list` to discover, `tools/call` to invoke) and registers a namespaced\n proxy tool (`{endpoint}__{tool}`) for each advertised tool. Mirrors `app_proxy_tools`.\n Best-effort: an unreachable endpoint registers nothing and never raises.\n- **`POST /stream` and `POST /chat/stream`** accept an optional `mcp_endpoints` list\n (`[{name, url, [headers]}]`); `_aitheros_stream` attaches them to the agent before the\n turn. Existing callers are unaffected (the field is optional).\n\n## [2.10.1] - 2026-06-14\n\n### Fixed — self-hosted `aither-serve` was crash-on-startup (2.10.0 regression)\nA fresh `pip install aither-adk && aither-serve --backend deepseek` could not boot. Found\nand fixed while standing up a real self-hosted DeepSeek agent end-to-end:\n- **Startup `NameError: name 'port' is not defined`** in three lifespan helpers\n (`_join_aithernet`, `_init_a2a_server`, `_start_mesh_hosting`) — they referenced a bare\n `port` no longer in scope. Now use `config.server_port`, and `main()` writes the resolved\n `--port`/`--host` back to `config` so every lifespan helper and the invoke_url see the\n actual bound port.\n- **Fleet self-registration crashed the whole server** — the registration handlers caught\n `(ImportError, RuntimeError, OSError, ConnectionError)` but **not** `httpx.HTTPError`, so a\n `RemoteProtocolError`/connect error talking to the control plane took the agent down. All\n network-facing lifespan handlers now also catch `httpx.HTTPError` (non-fatal, as intended).\n- **`--backend` was ignored when `AITHER_API_KEY` was set** — `_auto_detect` picked the cloud\n gateway before the explicit backend, and `_try_elysium_fallback` then replaced the chosen\n provider. An explicit backend (deepseek/openai/anthropic/your own vllm/ollama) now always\n wins; \"auto\"/\"gateway\" still flow through detection. The operator's own brain is never\n silently hijacked.\n- **Approved-tool trace truncated after `/sessions/{id}/confirm`** — the SSE relay treated\n `AgentResponse.tool_calls_made` (a `list[str]`) as dicts (`tc.get(...)`), raised\n `AttributeError`, and an over-narrow `except` swallowed it so the client saw only\n `session_start` + heartbeats. The relay now handles string tool names, the `except` is\n broad (always emits a typed `error` + terminal `complete`), and the module-level\n `_strata_ingest` `NameError` + Chronicle `session_id` kwarg mismatch are fixed.\n\n## [2.10.0] - 2026-06-13\n\n### Added — human-in-the-loop tool approval (`adk/approval.py`)\n- **Pause before a gated tool, resume on decision.** An agent can now pause its turn before\n executing a sensitive tool and wait for a human's allow/deny — so a managed control plane\n (or any operator) can gate tool use without forking the loop.\n- **Policy via `AITHER_TOOL_APPROVAL`** — a comma-separated list of tool names (optionally\n `agent:tool`), or `*` for every tool. Empty = no gating (unchanged behavior).\n- **Survives restarts / days-later approvals.** The paused turn (user message + pending tool\n calls) is persisted to disk keyed by `session_id`; resume is a fresh request, not a held\n connection. Decisions are recorded per `(session_id, tool_name)`.\n- **`AgentResponse.requires_action` / `.pending`** — set when a turn paused; `pending` lists\n the gated tool calls awaiting a decision.\n- **`AitherAgent.resume(session_id, decisions)`** — records the decisions and re-runs the\n paused turn (the gate consumes them: allowed tools execute, denied tools get a denial\n observation, the turn continues — and may pause again on a different gated tool).\n- **Server:** `POST /sessions/{id}/confirm` records decisions and streams the resumed turn;\n `/stream` emits a `requires_action` event when a turn pauses.\n\n## [2.9.0] - 2026-06-13\n\n### Added — universal LLM continuation primitive (`adk/llm/continuation.py`)\n- **`run_continuation()` + `stitch()`** — one shared continue-until-complete primitive. When a\n completion stops because it hit the output token cap (`finish_reason == \"length\"`), it continues\n the generation and STITCHES the chunks (overlap-dedup + restart-detect) into a complete answer,\n instead of every call-site hand-rolling its own retry. Bounded three ways (max rounds AND total\n output ceiling AND a no-growth break), and it never mutates the caller's message history.\n- **`LLMProvider.chat_with_continuation()`** and **`LLMRouter.chat_with_continuation()`** — every\n provider AND the router inherit the primitive, so any call-site gets continuation for free.\n- Kill-switch `ADK_LLM_CONTINUATION=off`; tunables `ADK_LLM_MAX_CONTINUATIONS`,\n `ADK_LLM_MAX_TOTAL_OUTPUT_TOKENS` (defaults: 2 rounds / 8192 tokens).\n\n### Changed\n- The ReAct loop's truncation recovery now uses the shared primitive. The previous inline\n doubling-retry REPLACED the partial response (losing earlier text in `resp.content`) and injected\n continuation scaffolding into the live message history; it now STITCHES and leaves history clean.\n Tool-call turns are never continued as text.\n\n### Included — previously-committed-but-unreleased changes\n- 2.8.2: TLS verification on by default (`verify=False` removed).\n- 2.8.1: baked license + pack-signing public keys.\n- 2.8.0: version-bump release.\n\n## [2.7.0] - 2026-06-05\n\n### Added — provider-agnostic LLM cost levers (at the `LLMProvider` ABC)\n- **Prompt caching.** New `ProviderCapabilities` (`prompt_cache: explicit|automatic|none`, `batch`) and\n normalized `LLMResponse.cache_read_tokens`/`cache_write_tokens`, so the \"tokens saved\" meter never\n depends on which backend served the turn. Anthropic inserts `cache_control` breakpoints over the stable\n system+tools prefix; OpenAI/DeepSeek caching is automatic and their `cached_tokens` are now surfaced\n (previously discarded). Default-on in `AitherAgent.chat`; override with `cache=False`.\n- **ResponseCache** (`adk/llm/cache.py`) — exact-match response cache with a pluggable backend, wired into\n `LLMRouter` as an opt-in (`LLMRouter(response_cache=…)` + per-call `cacheable=True`). OFF by default — no\n determinism change unless opted in.\n- **Gemini provider** (`adk/llm/gemini.py`) with explicit `cachedContents` caching; registered in the router.\n- **Async batch runner** (`adk/llm/batch_runner.py`) — `run_batch()` over the Anthropic Message Batches /\n OpenAI Batch APIs (~50% off) with a transparent fallback to concurrent `chat()` where unsupported.\n- **Graph-RAG retriever interface** (`adk/graph_retriever.py`).\n\n### Added — principal authority (prompt-injection defense enforced outside the model)\n- `adk/auth.py`: `Principal` / `AuthContext` / `PrincipalResolver` (+ deny-all default).\n- `ToolRegistry.execute(name, args, auth=…)` — a default-deny authorization gate at the tool boundary, so a\n forbidden tool call is blocked regardless of what the LLM emits. `ToolDef` gains `required_clearance` /\n `action_class`. Threaded through `AitherAgent.chat`; `auth=None` is fully backward-compatible.\n- `adk/channel_auth.py`: Telegram webhook-secret + CEO-id checks and HMAC-signed approvals.\n\n## [2.6.4] - 2026-06-05\n\n### Fixes\n- **Loop-guard nudges corrupted the message history → DeepSeek 400 mid-run.**\n When the ReAct loop emitted multiple tool calls in one assistant turn and the\n loop guard fired a WARN/BLOCK/CIRCUIT_BREAK on one of them, it spliced a\n standalone `system` nudge message *between* the tool results. DeepSeek (and\n strict vLLM chat templates) require an assistant `tool_calls` message to be\n followed by exactly one `tool` result per `tool_call_id`, contiguously — so the\n interleaved message produced `400 … insufficient tool messages following\n tool_calls message` and aborted the turn (this is reliably hit after crossing\n the 15-call circuit-break threshold, where every subsequent call WARNs).\n Per-call guidance is now folded into that call's own `tool` result, and any\n standalone steering is deferred to a single message AFTER the whole batch —\n preserving the `tool_calls`↔`tool_results` pairing every backend requires.\n Regression test: `deep-research-agent/tests/test_msg_structure.py`\n (loopguard-flood scenario).\n\n## [2.6.3] - 2026-06-04\n\n### Fixes\n- **Transient provider errors killed a run on the first blip.** A single\n `502 Bad Gateway` / `503` / `529 overloaded` / `429` from the upstream API\n aborted the whole request — the Anthropic provider had **no retry at all**, and\n the OpenAI-compatible provider only retried `429`. Both now retry transient\n statuses (`408/429/500/502/503/504/529`) with `Retry-After`-aware exponential\n backoff (3 attempts), and surface the provider's body if all retries are\n exhausted. Non-transient `4xx` (e.g. `400`) still fails fast. Applies to\n `chat` and `chat_stream` on both providers.\n\n## [2.6.2] - 2026-06-04\n\n### Fixes\n- **DeepSeek (and strict OpenAI-compatible backends) 400'd on long tool\n conversations.** The ReAct loop legitimately injects mid-conversation\n `system` steering messages (the \"[DIMINISHING RETURNS]\" hint after several\n low-output tool iterations, loop-guard nudges). OpenAI tolerates a non-leading\n `system` message; DeepSeek rejects it with a 400. Long research runs\n (many `fetch_url` calls) reliably tripped this. Fix: non-leading `system`\n messages are demoted to `user` at the OpenAI-compatible provider boundary\n (`_demote_nonleading_system`) — steering intent preserved, payload valid on\n every backend; leading system prompt(s) untouched.\n- **Opaque HTTP errors.** `resp.raise_for_status()` discarded the provider's\n response body, so a 400/422 surfaced as a bare `Client error '400 Bad\n Request'` with no cause. `_ensure_ok` now includes the provider's actual\n message (e.g. `context_length_exceeded`, `Invalid 'messages'`) in the raised\n error, for both `chat` and `chat_stream`.\n\n## [2.6.1] - 2026-06-04\n\n### Fixes\n- **`MCPServer.mount()` returned HTTP 422 for every `/mcp` call.** Under\n `from __future__ import annotations` the route handler's `request: Request`\n annotation is a string that FastAPI resolves against the *module* globals, but\n `Request` was imported locally inside `mount()` — so FastAPI couldn't resolve\n it and treated `request` as a required query param. The MCP server endpoint\n (`initialize`, `tools/list`, `tools/call`) was unusable when mounted. FastAPI\n symbols are now imported at module level (guarded; the `[node]` extra still\n gates actual use). Any agent's tools can now be exposed as a working MCP\n server with `MCPServer(...).mount(app)`.\n\n## [2.6.0] - 2026-06-04\n\n### Streaming + scaffolding\n- **`AitherAgent.stream_chat`** — reliable streaming sibling of `chat()`: native\n tool-calling loop (concludes on its own, unlike the text-protocol `stream_react`)\n that emits live `tool`/`tool_result` events and streams the answer as `token`\n events. `token_delay` paces tokens for a typing effect.\n- **`adk new <template>`** — scaffold a full, runnable template app. Ships the\n `deep-research` template (a cited web-research agent: pack + server + web UI):\n `adk new deep-research`.\n\n### Fixes\n- **BYO-key no longer community-capped.** The free-tier monthly token cap now applies\n only to the metered Aitherium gateway backend, never to a user's own provider key\n (Anthropic/OpenAI/DeepSeek/Ollama/vLLM). Previously it could brick a self-hosted\n agent after ~100k tokens on the user's own key.\n- **Stronger, grounded memory recall.** `chat()` now injects the top-6 knowledge-graph\n hits (untruncated) with a hard no-fabrication instruction — was top-3 truncated to\n 200 chars, which surfaced partial facts and let weaker models invent the rest.\n- **Tool-arg coercion.** `ToolRegistry.execute` coerces LLM-supplied args toward each\n parameter's annotated type, so a tool no longer crashes when a model passes a list\n or string where an int/float/bool was expected.\n\n### Docs\n- New `docs/AGENT_DEV_GUIDE.md` — the golden path for building agents/packs.\n\n## [2.5.0] - 2026-06-04\n\n### Continual learning — agents that learn from runs and reuse memory\n\nAgents now improve the second time they do a task instead of starting from scratch.\n\n### Added\n- **Recall-before**: `AitherAgent.chat()` searches the local `SkillStore` and injects the\n top matching learned skills as a system block, so the model reuses proven procedures.\n- **Learn-after**: `_learn_after()` runs after each response — reinforces (`success_count++`)\n any skills that were recalled+reused, and extracts + saves a NEW skill from successful\n multi-tool runs via `SkillExtractor`. Wired at both `chat()` return paths.\n- Gated by `AITHER_SKILLS` (default on). The previously-unused `SkillStore`/`SkillExtractor`\n are now connected to the run loop.\n\n### Notes\n- Pairs with the platform-side LearnedProcedure substrate (recipes → scored procedures →\n skills/tools/A2A/packs → MCTS priors → evolution). The SDK shares memory via the Spirit\n bridge when configured.\n\n## [2.0.0] - 2026-06-02\n\n### Open-core boundary (BREAKING)\n\nThis release draws a single, enforced free/paid line. The free tier stays\ngenuinely useful (real agent, typed + graph + code memory, ReAct, ~essential\ntools); paid tiers unlock the *scale* capabilities via portal.aitherium.com.\n\n### Added\n- **`adk/licensing.py`** — the entitlement keystone. Resolves a tier\n (COMMUNITY by default), verifies optional portal-signed `~/.aither/license.json`\n (Ed25519), and answers `can_use_fleet/channels/cron/swarm/auto_neurons`,\n `is_agent_licensed`, `max_effort`, etc. **Fail-closed**: no/invalid/expired\n license → free tier; an unsigned license never grants premium.\n `AITHER_TENANT_SLUG=aitherium` → unrestricted INTERNAL tier.\n- **Moat guard** (`scripts/check_moat_boundary.py`) wired into the publish\n workflow — blocks any wheel that leaks the moat or drops the keystone.\n- Tests: `tests/test_licensing.py`, `tests/test_moat_boundary.py`.\n\n### Changed (gating — fail-closed)\n- Free tier effort is capped at 3 (reasoning effort 7–10 needs Professional).\n- Monthly token budget hard-block on the free tier (no silent overspend).\n- Gated behind a paid tier: fleet mode (`FleetConfig`), cross-agent delegation\n (`AgentForge.delegate`), channel adapters, cron scheduler, proactive\n auto-neurons, and `swarm`/`swarm_code` dispatch.\n\n### Removed\n- **`adk/nanogpt.py`** (on-device training IP) removed from the published SDK\n and the public repo; relocated to the internal AitherOS moat. Excluded from\n the wheel as defense-in-depth.\n- Pre-2.0 releases shipped this IP and ungated capabilities and are being\n yanked (PyPI) / removed (GitHub releases). See `scripts/yank_leaky_releases.py`\n and `scripts/purge_public_leaks.sh`.\n\n## [1.20.0] - 2026-05-27\n\n### Fixed\n- **Pack discovery path mismatch** — `_find_local_packs()` now scans both\n `~/.aither/packs/` and `~/.aitheros/packs/`, so packs installed via\n `adk pack install` are discovered by `adk-workspace`.\n\n### Added\n- **Auto-register on pack install** — `adk pack install` now auto-registers\n with the portal if the pack contains an `agent.yaml` with a `portal` section.\n One command does download + extract + register.\n- **Flux event on agent registration** — `developer_portal.py` emits\n `agent.registered` Flux event so the portal fleet UI updates in real-time.\n\n## [1.15.0] - 2026-05-26\n\n### Added\n- **Cloud memory** — `adk quickstart --cloud` now tests gateway memory and\n auto-configures Spirit URL so memories persist across devices via\n `gateway.aitherium.com/v1/memory/teach` and `/v1/memory/recall`.\n- **Config→env export for cloud mode** — `Config.from_env()` now exports\n `AITHER_CLOUD_MODE`, `AITHER_SPIRIT_URL`, and spirit path env vars from\n saved config so Memory and other modules pick them up without needing\n explicit env vars set by the user.\n- **Cloud Quick Start section** in README — 3-command setup for users who\n just want a Claude/GPT-4/DeepSeek API key and the full agent harness.\n- **`--memory` profile for node deployment** — `adk deploy node --memory`\n adds Spirit (8087) + WorkingMemory (8101) containers for persistent\n vector memory. Without this, agents use local SQLite only.\n- **Configurable vLLM in node compose** — `AITHER_VLLM_MODEL`,\n `AITHER_VLLM_GPU_UTIL`, `AITHER_VLLM_CTX_LEN`, `AITHER_VLLM_QUANT_ARGS`\n env vars let `adk setup` write GPU-detected tier settings to `.env`\n and have the node compose pick them up (TQ4 for small GPUs, BNB for large).\n\n### Fixed\n- **Memory cloud routing** — `Memory._spirit_teach()` and `_spirit_recall()`\n now use configurable paths and auth headers for gateway proxy. Previously\n cloud-only users silently lost all Spirit memory calls.\n\n## [1.9.0] - 2026-05-24\n\n### Added\n- **Composable ToolPack system** — agents can load tool packs from\n `.toolpack.yaml` directories. Drop a pack in `~/.aitheros/packs/` or\n set `AITHER_PACKS_DIR`, and any agent discovers it automatically.\n- **`tools.packs` in agent.yaml** — list tool pack IDs to auto-load\n (e.g. `packs: [crm-tools, calendar-pro]`).\n- **`register_tool_packs(agent, pack_ids, packs_dir)`** in `builtin_tools` —\n programmatic pack loading for agents.\n- **`AITHER_TOOL_PACKS` env var** — comma-separated pack IDs loaded on\n agent init (alternative to YAML config).\n- **Unknown builtin categories resolve as pack IDs** — `tools.builtin:\n [file_io, crm-tools]` now works: `crm-tools` is looked up as a pack.\n\n## [1.8.0] - 2026-05-24\n\n### Removed\n- **11 dead modules** (3,023 lines) — `agent_deploy`, `cloud_deploy`, `create`,\n `phonehome`, `sdk_bridge`, `sovereign`, `telemetry`, `telemetry_config`,\n `error_reporter`, `anonymizer`, `package`. None were imported anywhere.\n- **29 ghost exports** from `__init__.py` — `secrets`, `otel`, `fs_sandbox`,\n and `vector_memory` modules were listed in `__all__` and `__getattr__` but\n the backing files never existed. Accessing them raised `ModuleNotFoundError`.\n- **Stale dist/ artifacts** — removed old wheels/tarballs (v0.4.0–v1.4.1, 11 MB).\n- **Orphan test scripts** — `test_phase1.py` and `test_phase23.py` (one-off\n validation scripts not part of the pytest suite).\n\n### Fixed\n- **Version mismatch** — `__init__.__version__` now matches `pyproject.toml`\n (was stuck at 1.6.0).\n\n## [1.5.0] - 2026-05-23\n\n### Added\n- **`--sovereign` flag for `adk deploy node`** — registers the node with the\n Aitherium hub via federation after deployment. Saves federation credentials\n to `~/.aither/.env.federation`. Accepts `--hub` (custom hub URL) and\n `--tenant` (tenant slug) options. Non-fatal: node runs standalone if\n registration fails.\n- **Federation → fleet cross-post** (Genesis) — sovereign nodes that register\n or heartbeat via `/federation/register` and `/federation/heartbeat` now\n automatically appear in the fleet dashboard (`/fleet/deployments`) with\n `node_type: \"sovereign\"` and live status/metrics.\n- **\"Deploy Sovereign Node\" section on Connect page** (AitherVeil) — Section 8\n with ADK deploy commands, fleet dashboard links, and provision page links.\n- **`deploy-node` action in tunnel proxy** (AitherVeil) — Connect page and\n external callers can now request bootstrap scripts via\n `POST /api/tunnel/devworkspace?action=deploy-node`.\n\n### Fixed\n- **Fleet provision page** — API URL corrected from `/api/fleet/provision` to\n `/api/bridge/genesis/fleet/provision` (matches bridge proxy pattern).\n- **Fleet detail page** — API URL corrected from `/api/fleet/deployments/` to\n `/api/bridge/genesis/fleet/deployments/` (same fix).\n- **Dockerfile** — entry point corrected from `aither-serve` to `adk-serve`\n (matches `pyproject.toml` script registration). Version label updated.\n\n## [1.4.1] - 2026-05-22\n\n### Fixed\n- **macOS executable builds** — dropped `--target-arch universal2` from\n `packaging/build_executable.py`. PyPI wheels for `pydantic_core` and other\n native deps are arch-specific, not fat binaries, so universal2 always failed\n with `IncompatibleBinaryArchError`. Now builds for the runner's native arch\n (`aither-macos-arm64`).\n- **Release workflow strategy** — added `fail-fast: false` so a single\n executable build failure no longer cancels the other OS jobs.\n- **PyPI publish** — added `skip-existing: true` so re-runs of the same\n version don't fail when the artifact already exists.\n- **GitHub Release job** — now runs whenever `pypi` succeeds even if some\n executable matrix entries fail, so the release page is always created with\n whatever binaries did build successfully.\n\n## [1.4.0] - 2026-05-22\n\n### Added\n- **Sovereign template now ships with `agent_core` 5-tier memory** — generated\n backends include the canonical `agent_core/` package (working → episodic →\n semantic → procedural → identity) plus `agent_memory.py` helper, vendored\n from `portal-kit-backend`.\n- `aiosqlite>=0.20.0` added to sovereign template `requirements.txt` for\n local agent_core SQLite storage.\n- Document tombstoning: `evict_cached_conversations_referencing` is wired\n through document-delete handlers in consumer apps so memories citing\n deleted documents are blocked from recall.\n- New tests: 13 smoke tests for `portal-kit-backend/agent_core` covering\n promotion thresholds, decay rates, store roundtrip, tombstone recall\n blocking, circuit breaker state machine, and chat-turn recording.\n\n### Changed\n- `scripts/vendor_agent_core.py` now syncs canonical `agent_core` to 4\n targets: WorkspaceRuntime, ADK sovereign template, and the GargBot +\n Chelle consumer mirrors (previously only 2).\n\n## [1.2.1] - 2026-05-21\n\n### Added\n- `adk train` command group — 6 subcommands: status, launch, logs, cancel, runs, register-gpu\n- `/slash-commands` endpoint — ADK server auto-exposes all 35 CLI commands as structured JSON manifest for AitherShell\n- `/cli/execute` endpoint — AitherShell can run any CLI command through the server\n- `build_command_manifest()` — introspects full argparse tree (commands, args, choices, subcommands)\n- `_register_commands()` extracted from main() for shared parser construction\n\n### Changed\n- `_get_genesis_url()` now probes: Genesis → ADK server → Aitherium cloud gateway (works without Genesis)\n\n## [1.2.0] - 2026-05-21\n\n### Added\n- **Runtime backend switching** — `agent.switch_backend(\"deepseek\")` and `agent.llm.switch_backend()` change provider without recreating agent\n- **Hybrid reasoning** — `agent.set_reasoning_backend(\"anthropic\")` routes effort 7+ to cloud API while keeping local orchestrator\n- **First-class DeepSeek provider** — `deepseek` in effort model table with `deepseek-chat` and `deepseek-reasoner`\n- **TQ4 quantization tiers** — `nano` (6GB, TurboQuant 4-bit), `standard-tq4` (12GB, both models TQ4), `hybrid-tq4` (6GB + cloud reasoning)\n- **`adk backend` CLI** — `list`, `set`, `set-reasoning`, `test` subcommands\n- **`adk quickstart`** — unified first-run wizard (setup + auth + shell)\n- **`adk tools`** — list local + MCP tools with tier markers\n- **`adk backup`** — tarball export of `~/.aither/`\n- **`adk ingest`** — feed docs/files into knowledge graph\n- **`adk setup nemotron`** shortcut — alias for `--tier lite`\n- **`--reasoning-api`** flag in setup — `anthropic`, `openai`, `deepseek`, `gateway`\n- **`--dgx-spark URL`** flag — remote vLLM endpoint configuration\n- **DGX Spark auto-detection** — scans `spark.local`, `192.168.0.33`, `AITHER_DGX_URL` env\n- **Post-setup smoke test** — sends real inference request after container startup\n- **Schema migration system** — version table + migration runner in `memory.py` and `graph_memory.py`\n- **Configurable vLLM port scan** — `AITHER_VLLM_PORTS` env var for custom ports\n- **`aithershell` entry point** — alias for `adk` in pyproject.toml\n- Shell auto-downloads binary on first `adk shell` (no `--install` needed)\n- Shell pre-flight check warns if no backend detected\n- Shell passes full config via env vars (API key, tenant, inference URL)\n- DGX/Remote and Cloud API checks added to `adk doctor`\n- Config fields: `reasoning_backend`, `reasoning_api_key`, `deepseek_api_key`, `dgx_url`, `vllm_extra_ports`\n\n### Fixed\n- `test_aeon_chat_basic` timeout — mock scope was exiting before request\n- `adk doctor` check_disk timeout — cap rglob at 5000 files\n\n## [1.1.7] - 2026-05-19\n\n### Added\n- Remote agent pipeline — `adk onboard` → fleet dispatch → remote inference\n- Agent-to-agent federation with mesh relay\n- `adk connect` for desktop mesh enrollment\n\n## [1.1.6] - 2026-05-19\n\n### Fixed\n- Tool call execution recovery across all backends\n- Hermes tool call fallback parsing for vLLM\n\n## [1.1.5] - 2026-05-19\n\n### Fixed\n- Tool call execution + Hermes fallback parsing improvements\n\n## [1.1.0] - 2026-05-18\n\n### Added\n- Multi-channel gateway (Telegram, Discord, Slack, webhook)\n- Aeon group chat with 7 presets\n- Skills auto-extraction from multi-step sessions\n- MCP stdio server for Claude Code integration\n- `adk gateway`, `adk cron`, `adk skills`, `adk soul` commands\n- SOUL.md import/export for portable agent identity\n- Elysium cloud device-flow authentication\n\n## [1.0.0] - 2026-05-18\n\n### Breaking\n- Package consolidated — `aither_adk` namespace replaced by `adk` + `adk.platform`\n- 93 duplicate files removed from `AitherOS/packages/aither_adk/`\n\n### Added\n- Standalone core primitives (`adk.core`: Agent, Tool, Memory, ModelBackend, Capability, Trace)\n- `adk.platform` sub-package merging all platform toolkit modules\n- Compatibility shims for legacy `from aither_adk.*` imports\n\n### Changed\n- Version synced across pyproject.toml, `adk/__init__.py`, `adk/platform/__init__.py`, npm package\n- Classifier promoted from Alpha to Beta\n\n## [0.16.0] - 2026-04-16\n\n### Swarm Coding Engine & Repowise Integration\n- **`repowise_search` tool** — Semantic + keyword hybrid code search via Repowise, with ripgrep fallback\n- **`swarm_code` tool** — Dispatch complex tasks to 11-agent swarm pipeline (ARCHITECT->SWARM->REVIEW->JUDGE)\n- **`agent.swarm()`** — Async convenience method with configurable mode, effort, timeout\n- **`agent.code_search()`** — Async convenience method returning structured results\n- New tool categories: `repowise` and `swarm` in TOOL_CATEGORIES\n- `repowise_search` and `swarm_code` added to `__init__.py` exports with lazy loading\n- `IDENTITY_DEFAULTS` updated: repowise for code-focused agents, swarm for orchestration agents\n- Standalone graceful degradation: repowise falls back to ripgrep, swarm returns structured error\n\n## [0.13.0] - 2026-04-02\n\n### Graph Faculties — Local Knowledge for Every Agent\n- **CodeGraph** (2,799 lines) — Full Python AST indexer with call graph, keyword/semantic/hybrid query, embedding matrix cache, incremental re-indexing, multi-hop chain expansion\n- **MemoryGraph** (1,339 lines) — Graph-based persistent agent memory with 10 edge types, hybrid query (keyword + semantic + graph expansion), multi-hop recall, pickle persistence\n- **EmbeddingProvider** — 4-backend fallback chain: sentence-transformers (GPU/CPU) -> Ollama -> Elysium cloud -> feature hashing (zero deps)\n- **BaseFacultyGraph** — Abstract base with HMAC-SHA256 validated pickle persistence\n\n### Agent Integration\n- `agent.set_code_graph(cg)` — auto-registers `code_search` + `code_context` tools\n- `agent.set_memory_graph(mg)` — auto-registers `remember` + `recall` + `memory_stats` tools\n- Both graphs inject context into LLM prompts automatically during chat\n\n### Zero-Config Onboarding\n- `adk start` / `adk` (no args) — auto-detect project, index code, detect LLM, persistent memory, interactive chat\n- Works for any directory: Python codebases, doc folders, mixed workspaces\n- Auto-detects LLM: Ollama -> vLLM -> Elysium -> OpenAI -> Anthropic\n- Per-project persistent memory in `~/.aither/memory/<project>`\n- `adk index <path>` — standalone indexing with progress bar and stats\n\n### MCP Gateway\n- `POST /v1/embeddings` — OpenAI-compatible embedding proxy to vLLM-embeddings:8209\n- Available to all tiers (embeddings are free)\n- ADK EmbeddingProvider uses this as Elysium cloud fallback\n\n### Optional Dependencies\n- `pip install aither-adk[graphs]` — numpy for 10x cosine similarity speedup\n- `pip install aither-adk[embedding]` — sentence-transformers + torch for local GPU embeddings\n- `pip install aither-adk` alone — graphs work with feature hashing (zero deps)\n\n## [0.12.0] - 2026-04-01\n\n### Bootstrap & Service Discovery\n- Version handshake -- ADK checks major.minor compatibility with Genesis/Node\n- Background reconnect loop -- ServiceBridge re-probes every 30s in standalone mode\n- Port 8080/8090 documented -- MCP vs OpenAI-compat clearly separated\n- Genesis URL configurable via GENESIS_URL env var (was hardcoded)\n- Standalone mode warning -- visible stderr alert when AitherOS not detected\n- Auto-reconnect on startup when services come online\n\n### Elysium Cloud Inference\n- Unified gateway URL -- gateway.aitherium.com handles auth + billing + inference\n- Streaming inference -- SSE passthrough for /v1/chat/completions with stream=true\n- Auth proxy routes -- /v1/auth/register, /v1/auth/login, /v1/auth/me\n- Billing proxy -- /v1/billing/balance through gateway\n- AitherConnect Elysium fallback -- cloud inference when local Genesis is down\n- AitherDesktop Elysium fallback -- third-tier chat fallback after Node\n\n### Infrastructure\n- /discovery endpoint on Genesis -- unified service URLs/versions/health\n- /api/config/services on Veil -- runtime port config for client-side JS\n- Veil healthcheck gates on Genesis -- unhealthy when backend is down\n- Desktop crash detection 90s->60s (threshold 3->2)\n\n## [0.11.0] - 2026-04-01\n\n### Agent Execution Quality (Claude Code Parity)\n- Raise loop guard block threshold from 3 to 4 -- agents get more room for iterative search\n- Soft synthesis nudge for effort >= 4 (no tool stripping, trust the model)\n- max_output_tokens escalation -- retry up to 3x when response is truncated\n- Tool result pairing guarantee -- synthetic error for orphaned tool_use blocks\n- Micro-compaction of old tool results -- save context tokens on long sessions\n- First-turn tool forcing only for effort >= 6 (trust model for lower effort)\n- Diminishing returns detection -- nudge agent when 3+ turns produce < 500 tokens\n- Message normalization -- merge consecutive same-role messages, strip empties\n- LLM retry with exponential backoff (5 retries, 500ms-16s, jitter)\n\n## [0.9.0] - 2026-03-16\n\nThe \"connected world\" release. Cross-platform identity pairing, voice capabilities, and multi-channel integration.\n\n### Added\n- **Pairing**: Cross-platform identity linking (`adk/pairing.py`)\n - `PairingManager` — SQLite-backed identity linking with 6-char pairing codes\n - Link users across Telegram, Discord, Slack, WhatsApp with 10min TTL codes\n - Canonical session IDs for cross-channel conversation continuity\n - `get_session_id()` returns \"user-{id}\" for paired users\n- **Voice**: Speech-to-text and text-to-speech client (`adk/voice.py`)\n - `VoiceClient` — async STT/TTS/emotion via AitherVoice service\n - Convenience functions: `hear()`, `say()`, `feel()`\n - 6 voice options: alloy, echo, fable, nova, onyx, shimmer\n - Emotion detection with intensity scoring\n- New exports: `PairingManager`, `PairingResult`, `PlatformIdentity`, `VoiceClient`, `TranscriptionResult`, `SynthesisResult`, `EmotionResult`\n\n### Changed\n- `__init__.py` exports expanded with pairing and voice symbols\n\n## [0.6.0] - 2026-03-13\n\nThe \"group mind\" release. Multi-agent group chat, creative tools, and Iris identity.\n\n### Added\n- **Aeon**: Multi-agent group chat engine (`adk/aeon.py`)\n - `AeonSession` — persistent group chat with parallel agent execution\n - 7 presets: balanced, creative, technical, security, minimal, duo_code, research\n - Orchestrator synthesis: Aither summarizes all agent responses\n - Serial execution for Ollama, parallel for vLLM/cloud\n - ConversationStore persistence with `type: \"aeon\"` metadata\n - `group_chat()` one-shot convenience function\n- `aither aeon` CLI command — interactive terminal group chat with color-coded agents\n - `-p/--preset`, `-a/--agents`, `-r/--rounds`, `--no-synthesize` flags\n - `reset` and `quit` commands\n- Server endpoints: `POST /aeon/chat`, `GET /aeon/presets`, `GET /aeon/sessions/{id}`\n- Creative tools in builtin_tools: `image_generate`, `image_refine`, `image_search`, `video_generate`\n- Iris agent identity with visual generation capabilities\n- 48 Aeon tests (data models, presets, chat, context, persistence, server, exports)\n\n### Changed\n- `__init__.py` exports: `AeonSession`, `AeonResponse`, `AeonMessage`, `group_chat`, `AEON_PRESETS`\n\n## [0.5.0] - 2026-03-13\n\nThe \"tenant-ready\" release. Multi-tenant admin, permission grants, safety profiles, and a full setup wizard.\n\n### Added\n- `aither setup` interactive setup wizard with hardware detection, model selection, identity config\n- Strata storage backend: SQLite WAL persistence for conversations, memories, knowledge graphs\n- CLI test runner: `aither test` with auto-discovery and parallel execution\n- Permission grants system for MCP tool access control\n- MCP account management tools\n- LLM provider auto-detection for Ollama and vLLM endpoints\n- Apache-2.0 LICENSE file\n- Elysium desktop sync module\n- Comprehensive CLI test suite (493 tests)\n- Strata storage test suite (972 tests)\n- LLM provider tests (82 tests)\n\n### Changed\n- CLI expanded: `aither setup`, `aither test`, `aither bugreport`, `aither doctor`\n- Strata module rewritten as full local-first storage engine\n- Server startup includes Strata initialization\n- README rewritten with clearer quickstart and architecture docs\n\n### Fixed\n- Dead documentation links\n- Python 3.11 compatibility issues\n- Ruff per-file-ignores configuration\n- Identity provisioning edge cases\n- Docker node-gyp native dependency builds (python3 + build tools)\n\n## [0.4.0] - 2026-03-13\n\nThe \"own your AI\" release. Self-hosted agent OS for people who don't want their data on someone else's servers.\n\n### Added\n- Muse agent identity (creative/artistic generation)\n- Port 8120 to vLLM scan for ExoNodes discovery\n- Public roadmap with milestone-based porting schedule\n- Competitive positioning: self-hosted alternative to cloud-locked AI appliances\n\n### Changed\n- LLM router: compute-aware effort routing (replaces effort-level context gating)\n- README rewritten for sovereignty-first messaging\n- Package description updated\n- Promoted from alpha to stable release\n\n### Fixed\n- Version string consistency between pyproject.toml and __init__.py\n- vLLM port scanning now includes port 8120 (ExoNodes)\n\n## [0.3.1] - 2026-03-09\n\n### Added\n- GraphMemory: SQLite knowledge graph with Ollama embeddings and hybrid search\n- NeuronPool: auto-fire pre-LLM data gathering agents\n- NanoGPT: pure Python char-level transformer with LoRA fine-tuning\n- Safety gates: IntakeGuard (input), LoopGuard (recursion), Sandbox (code exec)\n- Event system: EventEmitter with chat/tool/forge event types\n- Builtin tools: identity-based tool selection\n- ServiceBridge: auto-discovery of AitherOS services\n- Streaming: chat_stream with safety gate integration\n- Auth middleware: Bearer token authentication\n- CLI: `aither init` and `aither serve` commands\n\n### Changed\n- Wired 5 previously disconnected modules into agent loop\n- 522 total passing tests (up from 85)\n\n## [0.3.0] - 2026-03-07\n\n### Added\n- Clean-room agent development kit\n- Multi-backend LLM providers (Ollama, OpenAI, Anthropic)\n- AitherAgent class with @tool decorator\n- SQLite conversation memory\n- OpenAI-compatible server (`aither-serve`)\n- 16 agent identities as package data\n- MCP bridge to mcp.aitherium.com\n- Privacy-centric opt-in telemetry\n- Bug reporting CLI and API\n- FastAPI server with OpenAI-compatible endpoints\n- Hardware auto-detection (5 tiers, 11 profiles)\n- Fleet orchestration and multi-agent coordination\n- A2A mesh protocol\n- Federation protocol for cross-instance agent dispatch\n\n### Initial release\n- 85 passing tests\n- Apache-2.0 license\n"}, "26a627c9c0a145aade73736d8a30ac8893f8c672": {"t": 1785748169.9306793, "n": 81975, "h": "1c35886724f83a9c", "c": "# Changelog\n\nAll notable changes to aither-adk will be documented in this file.\n\n## [2.23.2] - 2026-07-14\n\n### Fixed — CompletionGate no longer false-fails on correct results\n\n- **Tolerant judge JSON extraction.** The LLM judge parsed its reply with `json.loads(text[first\"{\":last\"}\"])`, which raised `JSONDecodeError` (\"Extra data\") whenever the model appended prose after the verdict or emitted a second object — and `verify()` turned that into a fail-closed verdict, so a genuinely completed task reported `unverified`. New `_first_json_object()` uses `JSONDecoder.raw_decode` from the first `{` that begins a valid object and ignores trailing content (prose, markdown fences, extra objects). A judge that answers with no parseable JSON still fails closed (never a soft pass), now with a clear reason. The same extractor backs `_derive_criteria`.\n- **Bare code-like tokens are hard-checked.** A natural criterion like `output contains ADK_AUTO_OK` (no quotes) previously fell through to the judge; `hard_checks` now also matches bare identifier/code shapes (contains an underscore, or 4+ CAPS/digit chars), with a stopword guard so a descriptive uppercase word (`OUTPUT`, `CONTAIN`) never becomes a false required token. Quote a token to force it regardless.\n- +9 tests (24 total), including the exact trailing-data regression.\n\n## [2.23.0] - 2026-07-14\n\n### Removed — dead modules and internal integrations\n\n- **`adk.swarm`, `adk.provisioning_tools`, `adk.session_sync_integration`, `adk.addon_metering`** — four orphan modules with zero public usage have been deleted. These were internal prototypes kept for legacy monorepo consumers; public packages have not imported them since 2.21.0 (tracked as D-348). **Breaking for anyone directly importing these modules** (import will fail). They are no longer exempt from the orphan-module check.\n- **`adk.aither_bridge`** — the AitherOS-internal IRC ↔ chat gateway bridge is no longer shipped in the public aither-adk package (stripped at sync time). All public importers already degrade behind `except ImportError` (in `adk.server` and `adk.builtin_tools`), so public packages remain unaffected. The module stays in the private AitherOS monorepo for internal use.\n- **Identity document trim** — removed internal agent names from `adk/identities/aither.yaml` delegation guidance, replacing with role-generic wording (\"delegate code review, refactoring, security analysis, performance and testing to specialist agents when available\"). Effort-tier routing philosophy remains unchanged and is product design, not a leak.\n\n### Changed — genericized customer/product names in help text\n\n- CLI help/examples no longer name specific customer deployments (`--scope` template list and `deploy agent` name example use generic placeholders). Functional deploy paths are unchanged.\n\n## [2.22.1] - 2026-07-14\n\n### Fixed — real bugs surfaced by reviving the public CI (red since Jul 5)\n\n- **TLS**: bundled toolpacks (arc-brainpack, cloudflare) disabled certificate\n verification on gateway/vault calls; all now route `adk._tls.tls_verify()`\n (verified by default, internal CA bundle when present).\n- **`system_prompt` contract**: bundled-pack `[PACK DIRECTIVES]` were appended\n even to an explicit `system_prompt`; an explicit prompt now wins wholesale.\n- **Headless secrets**: `_derive_key()` used `os.getlogin()`, which raises\n `OSError` without a controlling terminal (CI, daemons, containers) — falls\n back to `getpass.getuser()` (same value on interactive boxes, so existing\n secret files keep decrypting).\n- **`pack_verifier`**: missing `cryptography` crashed with `UnboundLocalError`\n in the exception handler instead of failing closed; now degrades gracefully\n (and `cryptography` was added to the dev extra so CI tests the real path).\n- **`fleet_manager._kill_pid` (POSIX)**: reported success immediately after an\n asynchronous SIGTERM and never reaped children (zombies read as alive); now\n polls, escalates to SIGKILL, and reaps.\n- **Identity**: `aither` regained the `creative` tool category (dropped\n accidentally in a 2.7.0 WIP sweep).\n\n## [2.22.0] - 2026-07-14\n\n### Changed — internal toolkit relocation\n\n- **`adk.platform` relocated** — the internal aither-platform toolkit (ComfyUI/image-gen\n pipelines, fleet agent clients, parallel CLI/UI/tools stack) has been moved out of the\n adk SDK tree to the standalone internal `aither-platform` package at\n `AitherOS/agents/aither_platform/`. The public package remains unaffected (it already\n excluded `adk.platform` since 2.21.1). Internal AitherOS agent builds now install\n `aither-platform` separately. `adk platform` falls through to the same \"not available\"\n message on public installs; internal builds work via the relocated package.\n\n### Removed — backward-compat re-export deleted\n\n- **`adk.gateway` module removed** — the backward-compatibility re-export of `GatewayClient`\n from `adk.gateway` has been removed. Use `from adk.client import GatewayClient` instead\n (the established public API since 2.21.0). All internal importers have been updated.\n\n## [2.21.1] - 2026-07-14\n\n### Removed — internal toolkit and license-gated product no longer ship publicly\n\n- **`adk.platform`** — the internal aither-platform toolkit (ComfyUI/image-gen\n pipelines, fleet agent clients, a parallel cli/ui/tools stack) is no longer\n included in the public repo or the PyPI wheel. It was internal-only code that\n had been riding along since the aither-platform merge. `adk platform` now\n reports \"Platform toolkit not available\" on public installs; internal AitherOS\n builds are unaffected.\n- **`adk.formbridge`** — the FormBridge form-automation product is license-gated\n (PROFESSIONAL+); its implementation no longer ships in the free package. All\n import sites (`builtin_tools`, `server` route mount) already degrade behind\n `except ImportError`, so nothing else changes behavior.\n- Product test suites (`test_formbridge.py`, `test_encounter_keying.py`,\n `test_flow_tools.py`) are excluded from the public sync alongside them.\n\n## [2.21.0] - 2026-07-14\n\n### Added — Completion gate: verified-or-retried task execution\n\n- **`adk.gate.CompletionGate`** — a composable wrapper that closes the \"silent\n success\" gap: an agent's ReAct loop returns whatever the model produced and\n calls it done (`finish_reason = stop/max_steps/length`), which is a *liveness*\n signal, not a *completion* one — so an agent can \"succeed\" while doing nothing.\n `CompletionGate` runs the agent, verifies the result against acceptance\n criteria, retries with the failure fed back, and returns the response with\n `finish_reason` set to `verified` or an honest `unverified`. Never a false pass.\n - **Hard-first, un-soft-passable checks** (`adk.gate.hard_checks`): a filesystem\n path a criterion says must exist is checked on disk; a required quoted token\n must appear in the output. Only genuinely subjective criteria fall through to\n an optional LLM judge (reuses the agent's own model), which **fails closed**\n if unavailable.\n - **`gated_run(agent, task, criteria=..., max_retries=2)`** — one-shot helper.\n - `auto_criteria` (LLM writes the machine-checkable definition-of-done from the\n task), a `verifier=` override for custom checks (tests-pass / HTTP probe), and\n a `requires_action` short-circuit so it never fights human-approval pauses.\n - Compose it onto any agent exposing `run(task) -> resp{.content, .finish_reason}`\n (`AitherAgent`, `core.Agent`, or a thin adapter over a remote dispatch).\n - Exported from the top-level package: `from adk import CompletionGate, gated_run`.\n\n## [2.19.0] - 2026-07-12\n\n### Added — Eve agent importer + durable A2A tasks + canonical agent discovery\n\n- **`adk pack import <eve_agent_path>`** — convert external Eve agents to AitherADK packs\n (from GitHub or local filesystem). Maps Eve structure (instructions → system prompt,\n agent.ts → agent.yaml, skills/*.md → skills, connections/*.ts → MCP, tools/*.ts →\n tools/node verbatim) to AitherADK format with a single command. Eve agents (compiled\n manifest format, schema v35) become installable packs compatible with `adk pack install`.\n- **Durable A2A task lifecycle (`TaskManager` backed by FileStore)** — agent-to-agent\n tasks now survive container restarts. Tasks persist as append-only JSONL\n (`.adk/tasks.jsonl`); in-memory dict for fast access; `create_task()` / `update_status()` /\n `add_artifact()` / `add_message()` all save to disk. Google A2A v0.3.0 spec compliant.\n- **Canonical agent card discovery** — `A2AServer.mount()` now exposes:\n - `GET /.well-known/agent-card.json` — Agent Card (canonical; contains agent identity,\n capabilities, skills, endpoints)\n - `GET /.well-known/agent.json` — Agent Card (legacy, redirects with 308 to canonical path)\n - `POST /a2a` — JSON-RPC 2.0 for message send/task lifecycle\n - `GET /a2a/tasks/{id}/subscribe` — SSE streaming task updates\n- **Four built-in agent packs now complete and catalogued**:\n - `hermes` — architecture review & trade-off analysis\n - `openclaw` — web research & source verification\n - `claude-code` — feature development & debugging\n - `analyst` — structured data inference & anomaly detection\n All discoverable via `adk packs` / `adk install pack:<name>`.\n- **Tolerant tool-call JSON parsing** — malformed tool JSON (extra properties, missing\n name/arguments, string-encoded nested args, missing keys) is now gracefully degraded\n instead of silently failing. `_parse_tool_json()` accepts both `\"arguments\"` and\n `\"parameters\"` keys, coerces string-encoded args, and skips unparseable calls instead\n of crashing the turn.\n\n### Known limitations\n\n- Eve agent importer: remote GitHub fetching not yet implemented (local paths only).\n- Durable task store: currently JSONL (suitable for thousands of records, not millions).\n\n## [2.18.0] - 2026-07-10\n\n### Added — AitherShell command center + Claude Code session management\n\n- **`aither sessions`** — Claude Code session manager: interactive full-screen\n browser (type-to-filter, transcript preview, deep full-text `search`, resume\n **in the current terminal** or as Windows Terminal tabs), crash guard\n (`guard install` = at-logon watchdog that snapshots live sessions and\n auto-restores the whole set after a terminal crash), and `ingest [--watch]`\n (incremental, secret-guarded sync of session conversations into the local KB\n / CompanyBrain via the standard ingest pipeline).\n- **`aither hq`** — command-center dashboard: per-service fleet health, LLM\n queue (depth/VRAM/models), Pulse alerts, live session count, inbox unread —\n auto-refreshing, with one-key jumps into chat, sessions, inbox, agents,\n brief, watchtower, and docker recovery.\n- **`aither inbox`** — unified queue over CommCore mail, Relay\n mentions/notifications, and Pulse alerts; open/mark-read/DM-reply from the\n terminal. Every source degrades independently.\n- **`aither agents`** — roster console + `agents ask -e <1-10> <agent> <q>`\n (effort-tiered ask via Genesis, with automatic `/chat/stream` fallback when\n the `/agent/sync` execute gate rejects host callers) + forge dispatch\n inspection + routine health.\n- **`aither palette`** — universal fuzzy picker across actions, sessions, and\n services; **`aither brief`** — renders Atlas's executive briefing in the\n terminal; **`aither watch`** — fleet watchtower with named wedge-signature\n detection (docker-WSL wedge, LLM-queue stall, crash-loop uptime regression)\n and optional docker auto-recovery.\n- All fleet reads go through a shared fail-soft `FleetClient` (internal-CA\n TLS via `adk._tls`, dashboard-grade timeouts, per-source degraded states).\n- Node shell-cli (`@aitherium/shell-cli` 1.13.0): `aither\n sessions|hq|inbox|palette|brief|watch|agents|docker` now pass through to the\n Python shell instead of being intent-classified as chat prompts.\n\n## [2.17.1] - 2026-07-05\n\n### Fully self-service mesh join — no customer key handling\n\n- **Auto-join headscale off the onboard response.** The conductor now auto-issues a\n headscale pre-auth key for NAT'd nodes and returns it in the `/v1/mesh/onboard`\n response; `mesh.join` consumes it and brings up the tunnel automatically. A customer\n runs one command — they never mint, fetch, or set a headscale key. Explicit\n `--headscale-key`/`AITHER_HEADSCALE_AUTH_KEY` still take precedence.\n- **Security:** `_tailscale_up` now redacts the auth key from all logs and exceptions\n (a failed `tailscale up` previously echoed the full argv, leaking the key into logs).\n\n## [2.17.0] - 2026-07-05\n\n### Added — Headscale mesh transport + local Qdrant + AitherConnect onboarding\n\n- **Headscale NAT-friendly mesh transport.** When raw WireGuard UDP:51820 is not\n viable (customer boxes behind NAT, corporate firewalls, CGNAT), `adk mesh join\n --headscale` routes the mesh tunnel through Tailscale's Headscale control plane.\n The overlay IP is still Conductor-assigned (10.77.0.0/16); Headscale provides\n only the transport layer. Configured via `AITHER_MESH_TRANSPORT=headscale`,\n `AITHER_HEADSCALE_URL` (default: https://headscale.aitherium.com), and\n `AITHER_HEADSCALE_AUTH_KEY` (pre-generated key). Automatic fallback to raw\n WireGuard if Headscale setup fails.\n- **Local Qdrant vector DB provisioning.** `adk stack` now provisions Qdrant\n locally when `AITHER_VECTOR_DB=qdrant` is set, enabling offline-first RAG\n without external dependencies. Tiered: local Qdrant in-fleet, cloud Nexus\n for enterprise multi-tenant deployments.\n- **Conductor URL public fallback.** `adk mesh join` resolves the internal\n `aitheros-conductor:8193` hostname; if unreachable (e.g., self-hosted nodes),\n it falls back to the public `conductor.aitherium.com:8193` endpoint,\n eliminating bootstrap configuration friction.\n\n### Documentation\n\n- **Self-hosting runbook.** New `docs/SELF_HOSTING.md` covers deploying AitherOS\n on customer infrastructure with Qdrant + mesh + AitherConnect onboarding.\n- **Mesh transport constraints documented.** Raw WireGuard requires public\n UDP:51820 endpoint (cloud instances, dedicated hardware); Headscale for\n NAT'd networks. Noted in CLI help and module docstrings.\n\n## [2.15.0] - 2026-07-04\n\n### Added — built-in web admin console + portal-profile settings sync\n\n- **Web admin console.** The page `adk up` opens (served at `/` and `/ui`) is now a full,\n self-contained console — Chat plus six admin tabs: **Backend** (view/switch LLM provider,\n set keys, test connection), **Packs** (list/enable/disable/reload tool packs),\n **Sessions** (browse/delete conversations), **Logs** (redacted agent-log tail),\n **Graph** (knowledge-graph search/neighborhood/stats), and **MCP** (add/remove external\n MCP servers). No build step, no CDN — a single packaged HTML asset. The original minimal\n streaming chat page stays at `/chat` for back-compat.\n- **Admin API (`adk/admin_api.py`).** ~20 endpoints under `/admin/*`, all bearer-gated by the\n existing middleware (never in `_skip_auth_paths`). Operates on the live agent — LLM backend\n swap (`LLMRouter.switch_backend`), pack reload, and MCP registration take effect without a\n restart.\n- **Settings sync (`adk/settings_sync.py`).** The user's portal profile is the source of\n truth: on startup the agent pulls `preferences.adk` and applies it over the local\n `~/.aither/config.yaml` cache; admin-console edits push a debounced snapshot back to\n `PUT /api/settings/preferences`. Provider API keys and MCP auth headers are device-local\n secrets and are never included in the pushed snapshot. Opt out with `AITHER_SETTINGS_SYNC=false`.\n\n### Security\n\n- **Timing-safe token comparison.** The server bearer check now uses `hmac.compare_digest`\n (the token is reachable over the public tunnel; a byte-wise `!=` leaked it to timing attacks).\n- **MCP add is SSRF-guarded + confirmed.** Adding an external MCP server is a two-step\n prepare→confirm flow; URLs resolving to loopback / private / link-local / metadata addresses\n are rejected (override with `AITHER_ALLOW_PRIVATE_MCP=1` for trusted local servers).\n- **Config editor is allowlisted.** `PATCH /admin/config` writes only safe scalar prefs and\n hard-denies fields that could execute code or redirect traffic at startup (e.g.\n `aither_toolpack_dirs`, backend base URLs).\n- **Log tail is scrubbed.** Bearer tokens, `#k=` fragments, and `sk-`/`aither_sk_` keys are\n redacted from the log-tail endpoint; the tunnel log is excluded.\n\n## [2.14.8] - 2026-07-04\n\n### Added — `adk up` for normal humans (no API key, no prereqs, live chat)\n\n- **Hosted-brain default — no API key required.** When there's no local model and no\n provider key, `adk up` signs in (device-flow) and routes inference through the gateway\n using the portal token (`AITHER_LLM_BACKEND=gateway` + bearer). A non-technical user\n never needs to obtain or paste an LLM API key. Explicit `--provider`/local model still\n take precedence; unattended `--yes` with no token/backend fails cleanly (exit 3).\n- **cloudflared auto-download.** If the tunnel binary is missing, `adk up` downloads the\n right platform asset to `~/.aither/bin` (SHA256-verified against the release; opt out with\n `AITHER_CLOUDFLARED_REQUIRE_CHECKSUM=0`) instead of exiting with an install hint.\n- **Built-in streaming chat page.** The agent now serves a self-contained chat UI at `/`\n (and `/chat`) with a live \"thinking…\" indicator and token-by-token streaming — so a human\n has somewhere to talk to it with immediate feedback instead of a blank wait. `adk up` opens\n it locally and passes the callback bearer in the URL fragment (`#k=…`), so `/chat/stream`\n stays authenticated (not an open proxy) with nothing to paste.\n- **Human-readable failures.** `adk up` errors now carry a plain-English `next_action`\n (kept machine-readable in `--yes` JSON).\n\n### Fixed\n\n- `adk up` chat link pointed at a non-existent `/settings/agent` route; it now points at the\n agent's own streaming chat page.\n\n## [2.14.7] - 2026-07-04\n\n### Added\n\n- **`adk up` — one command to run a persistent, fleet-connected agent.** Collapses\n the scattered onboarding surface (`host`/`quickstart`/`connect`/`login`/`setup`)\n into a single command that starts `aither-serve` (single agent, identity `aither`\n by default), opens a Cloudflare quick-tunnel for fleet reachability, registers with\n the portal, and installs a reboot-persistent autostart entry (Windows Task Scheduler\n / systemd-user / launchd). Detaches by default so the terminal is freed; `--foreground`\n blocks. Reuses the existing device-flow login, cloudflared discovery, provider-key\n store, and `_preflight_check` backend detection.\n- **Fully non-interactive path for autonomous agents.** `adk up --yes` (or any\n non-TTY invocation) takes ZERO prompts — everything resolves from flags, env, and\n saved config — and emits a single machine-readable JSON status line with meaningful\n exit codes (`0` ok, `2` bad-input, `3` no-backend, `4` tunnel-missing, `5` register-failed).\n Degrades to local-only (warn, not fail) when a portal token is absent unless\n `--require-register` is set.\n- **`adk down` / `adk status --json` companions.** `down` stops the agent + tunnel,\n best-effort deregisters, and removes the autostart entry; `status` reports the running\n agent (liveness + `/health` + tunnel + registration), with `--json` for agents/CI.\n- **`adk stack`** — the previous `adk up` behaviour (supervise the Room + Ollama consumer\n stack) now lives here; `adk up` is the connected-agent command.\n\n### Internal\n\n- New `adk/agent_daemon.py`: detached process spawn, status file\n (`~/.aither/adk-up.json`), pid liveness/teardown, and cross-platform autostart install.\n\n## [2.14.6] - 2026-07-04\n\n### Added\n\n- **`adk.preflight` — self-bootstrap capability probe.** `run_preflight(agent,\n spec)` wakes an agent up knowing what it is actually plugged into: a bounded\n (1.5s) parallel `LivenessProbe` over the router's resolved backends\n (primary/reasoning), the embeddings winner, remote MCP tools, the structured-ML\n / `/ml/teach` endpoints, and voice — returning an honest `CapabilityReport`.\n Status vocabulary distinguishes reachability from entitlement\n (`OK`/`MISSING`/`UNREACHABLE`/`AUTH`/`TIER_DENIED`/`UNSUPPORTED`), a hosted slot\n never prints a bare `OK`, and a required-but-unsatisfied slot aborts before the\n loop instead of blind-firing. Headless task/rules resolution + an allowed-roots\n policy binding (`builtin_tools.set_allowed_roots`, which resets the memoized\n cache so the override actually enforces).\n- **Multimodal (image) messages.** `Message.content` now accepts an OpenAI-style\n content-part list (`str | list[dict]`), passed through verbatim to the\n OpenAI-compatible body (gateway / vLLM / OpenAI). New `adk.llm.multimodal`\n helpers — `image_message()`, `image_content_parts()`, `to_image_url()` — build\n the parts and encode local image bytes/paths into `data:` URIs.\n- **Real vision preflight probe.** The vision slot is no longer unconditionally\n `UNSUPPORTED`: it sends a discriminable image (a solid red square) and requires\n the model to name the color, so a blind/text-only model cannot false-pass. `OK`\n only on a real round-trip; `AUTH` on 401/403; `UNSUPPORTED` when there is no\n OpenAI-compatible provider or the model cannot see the image.\n\n## [2.14.5] - 2026-07-04\n\n### Added\n\n- **`adk.reasoning.mcts` — generic Monte-Carlo Tree Search library.** A reusable\n MCTS engine (ported from the internal `UnifiedMCTS`, with all internal couplings\n removed) that any agent can drive over its own environment:\n - `MCTSEnvironment` protocol (`get_state_hash` / `get_actions` / `step` /\n `evaluate` / `clone`) — implement it and search.\n - Three **optional, default-off** model seams — `TransitionModel`, `PolicyModel`\n (PUCT priors), `ValueModel` — so a learned world-model / policy / value can be\n injected without touching the core; with all three `None` the engine is a plain\n UCT search.\n - `ObservedTransitionModel` adapter — a cheap online lookup world-model (hit =\n exact stored transition; miss = identity + negative \"unknown\" reward +\n `is_uncertain`, biasing exploration toward learning), with JSONL save/load for\n cross-run reuse.\n - `UnifiedMCTS(config).search(env)` returns an `MCTSResult` (best action, value,\n confidence, principal-variation path) and an optional `MCTSTrace`\n (visit-distribution policy target) for distillation.\n- **Self-bootstrapping agent machinery.** Build an agent from a declarative spec\n instead of bespoke wiring:\n - `adk.bootstrap.build_agent_from_spec(path) -> (AitherAgent, RunCtx)` — resolves\n prompts, activates required/optional tool packs, and carries a `memory_map`\n (a `node_type -> (role, tier)` override for memory writes, never a gate).\n - `adk.prompts.PromptBridge` — resolves an agent's prompt bindings from files\n (relative to the spec) or `AitherPrompts` dot-keys (optional import).\n - `adk.packs.PackActivator` — eager activation of installed tool packs by name\n over `register_tool_packs`; a required pack that registers zero tools raises\n `PackUnavailable`, optional packs soft-degrade.\n - `python -m adk.bootstrap.generic_agent <spec.yaml>` inspects a spec (resolved\n name, activated packs + tool counts, prompt keys, memory map) without running a\n live loop.\n- **`deep_research` tool pack** — a full multi-engine web-research workflow as\n `dr_*` agent tools (keyless search, page fetch + cache, site mirror, a citable\n findings knowledge graph, and markdown/PDF/DOCX report generation), activatable\n by name via the tool-pack loader.\n\n## [2.14.4] - 2026-07-04\n\n### Added\n\n- **`structured_ml` tool domain** — zero-shot structured-data inference for agents,\n backed by TabFM (tabular classification/regression) and TimesFM (time-series\n forecasting). Three tools:\n - `tabular_classify(support_rows, target, query_rows)` — classify rows from a\n labeled support set, in-context (no training step; up to 10 classes).\n - `tabular_regress(support_rows, target, query_rows)` — predict a numeric target.\n - `timeseries_forecast(series, horizon)` — forecast future values of a series.\n\n The tools POST to a structured-ML inference service resolved from\n `AITHER_STRUCTURED_ML_URL` (default `http://localhost:8192`), reject non-http(s)\n URLs, and summarise oversized responses to protect the agent's context.\n- **`Capability.STRUCTURED_INFERENCE`** capability token and a new **`analyst`**\n identity/pack that ships the `structured_ml` domain by default. The domain is\n opt-in — only identities that enable the `structured_ml` category get the tools.\n\n## [2.14.3] - 2026-07-03\n\n### Fixed\n\nAll four bugs below were found by actually booting `adk run` against a real\nchat model and driving a real tool-calling request end-to-end (not inferred\nfrom reading the code) — each one silently broke the \"local agent uses its\nconfigured model and its tools actually work\" path.\n\n- **`AITHER_MODEL` / `config.model` silently ignored by the OpenAI-compatible\n backend**: `LLMRouter.__init__` never read `config.model` into `self._model`\n unless the caller passed `model=` directly to the constructor — which nothing\n did. Every explicit model choice (env var, config file) was dropped, the\n provider fell back to a hardcoded per-backend default (`gpt-4o-mini` for the\n `openai` backend), and an unrecognized model name got silently remapped\n server-side to the wrong backing model. Concretely: setting\n `AITHER_MODEL=aither-orchestrator` had no effect — every request actually\n went to a different model that was never meant to be called directly and\n can't reliably use tools.\n- **`OPENAI_BASE_URL` ignored for explicit `--backend`/`AITHER_LLM_BACKEND`\n selection**: `LLMRouter._auto_detect()`'s explicit-backend branch hardcoded\n `base_url=None` when constructing the provider, instead of reading\n `config.openai_base_url` (which two other call sites in the same file\n already did correctly). Any self-hosted OpenAI-compatible endpoint\n (MicroScheduler, vLLM, LM Studio) configured via `OPENAI_BASE_URL` was\n silently ignored in favor of `https://api.openai.com/v1`.\n- **Tool-call steering never engaged on default-effort chat**: the retry that\n nudges a model back into structured tool-calling when it responds with prose\n instead of a function call was gated behind `effort >= 6`. Default `effort`\n for a plain `agent.chat(message)` call resolves to `5`, so the safety net —\n built specifically for this failure mode — never fired for default requests.\n Also now forces `tool_choice=\"required\"` on the retry instead of relying on a\n text nudge alone.\n- **Every non-string builtin-tool parameter silently typed as `\"string\"` in\n the generated tool schema**: `builtin_tools.py` uses\n `from __future__ import annotations`, so `_extract_parameters()` in\n `tools.py` was reading postponed (string) type hints (`\"int\"`, not `int`)\n straight out of `fn.__annotations__`, which never matched\n `_type_to_schema`'s type-object keys and silently fell through to\n `{\"type\": \"string\"}`. Every builtin tool with an `int`/`float`/`bool`/`list`\n parameter was affected — e.g. `file_read(start_line: int, end_line: int)`\n advertised both as strings, so a model correctly calling the tool with\n `\"start_line\": \"0\"` crashed the actual implementation with\n `unsupported operand type(s) for -: 'str' and 'int'`. Fixed by resolving\n hints through `typing.get_type_hints()` instead of the raw annotations dict.\n `_coerce_arguments()` — the runtime safety net that's supposed to coerce a\n model-supplied `\"0\"` string into `0` before a tool call executes — had the\n exact same raw-`__annotations__` bug and silently no-opped for every builtin\n tool; fixed the same way.\n- **Streaming tool-loop could hang a connection forever**: `chat_stream()`'s\n fallback to the sync tool loop (tool calls can't stream token-by-token) had\n no timeout, so a model that didn't cleanly terminate a tool-calling round\n trip left the SSE connection open indefinitely with zero bytes sent. Bounded\n with a 60s `asyncio.wait_for`; on timeout the stream yields a clear message\n instead of hanging.\n\n## [2.14.2] - 2026-07-03\n\n### Fixed\n- **AitherShell session amnesia**: the REPL generated a fresh `session_id` for\n EVERY message, so conversation history never followed across turns (the\n server stored each turn under a different session). One stable id is now\n minted per shell run; `/resume` and `/new` semantics unchanged, and the\n cross-restart auto-restore of the last session now actually works.\n\n## [2.14.1] - 2026-07-02\n\n### Added\n- **Account-tied licensing via device-flow login** — `adk login` now persists the account\n license returned by the device-flow handshake to `~/.aither/license.json` (base64\n `license_key` → `{payload, signature}` envelope), so entitlements from the authenticated\n account carry into every later `adk` invocation without re-auth. Pairs with the fleet-side\n node tool-pack gate that grants packs by plan-tier rank.\n- **`can_use_formbridge` / `can_use_untether` entitlements** (PROFESSIONAL+ rank ≥ 3) backing\n the FormBridge and UNTETHER agent tool-packs.\n\n### Fixed\n- `adk` CLI: guard the optional `memory_graph` singleton (was a latent `NameError` on the\n `/stats`, `/memory`, and save paths when memory was not constructed).\n\n## [2.14.0] - 2026-07-02\n\n### Added\n- **`adk.memory_wiki` — LyraWiki-style self-managed semantic knowledge** (opt-in; nothing constructs it by default): `MemoryWiki(graph_memory)` maintains curated wiki ARTICLES as `wiki_article` nodes (title/slug, LLM-owned markdown with `[[wikilinks]]`, embedding, durable tier, `CONSOLIDATED_FROM` edges to source memories, governance-ledgered revisions).\n - `consolidate(llm, since=, budget=)` clusters unconsolidated episodic/fact nodes (embedding + keyword), drafts/UPDATEs articles via the INJECTED llm callable (`str -> str` or `messages -> str`, sync or async — no hard model dependency), cites source node ids, marks contradicted sources superseded, then DEMOTES consolidated sources to a fast-decay tier via `promote()` (represented knowledge doesn't need raw copies).\n - `lint(llm=None)` — deterministic health checks (orphan wikilinks, empty/stale articles, live-but-contradicted pairs) + an optional LLM audit pass.\n - `prune(relevance_floor=0.05, hard_delete_after_days=14)` — article relevance = tier freshness × reinforcement (reinforce-on-recall) + link-degree to LIVE memories; below the floor → reversible governance tombstone; entombed past the window → **HARD-DELETE** (the tombstone snapshot itself is purged — content and embedding irrecoverable; the true-deletion path). A node is never deleted without a tombstone first.\n - `recall(query, limit)` — article-first RAG (curated articles rank above raw nodes) that reinforces returned articles.\n- **`adk.routines` — agent heartbeat + self-programmed routines**: `RoutineStore` is a durable registry (`~/.aither/routines/{agent}.json`, `AITHER_DATA_DIR`-aware, injectable path) of cron-scheduled self-prompts `{name, cron, instruction, enabled, last_run, last_result, tags}` that rehydrates into the existing `adk.cron.CronScheduler` on `start()`. A fire runs `agent.chat(instruction)` with the agent's full toolset — the agent programs its future self with its own adk. Guardrails: `max_routines` (12), a 5-minute min fire interval per routine, a per-fire timeout, and every result ledgered (truncated) to `last_result`. Direct-callable routines (`register_direct`) fire a bound method instead of a self-prompt. Self-management tools (`routine_create/list/update/pause/resume/delete/run_now`) ship as OpenAI tool defs + handlers (`build_routine_tools` / `register_routine_tools` / `routine_tool_defs`); the handlers ONLY touch the RoutineStore — they can never modify agent config or safety-relevant settings (the leash principle).\n- **`AitherAgent(routines=True, memory_maintenance=True)`** (both default **False** → the default agent is byte-identical): `routines=True` builds the RoutineStore + scheduler, registers the self-management tools, and starts the heartbeat lazily on the first `chat()` (or via explicit `start_routines()`). `memory_maintenance=True` registers DEFAULT routines — `wiki_consolidate` (every 2 h), `wiki_lint` / `wiki_prune` / `graph_sweep` (daily) — as DIRECT method fires so memory upkeep runs even on tiny models, while staying visible/manageable via `routine_list`.\n- `TombstoneStore.purge(tombstone_id)` — the hard-delete endpoint: irrecoverably removes a tombstone snapshot and rewrites the persisted store without it (consumed by `memory_wiki.prune`).\n\n## [2.13.6] - 2026-07-02\n\n### Added\n- **Qdrant backend for GraphMemory dataplane sync — TRUE two-way, verified live.** Set `AITHER_FLEET_QDRANT_URL` and the sync uses Qdrant (`upsert` + `scroll` + filtered `search`) instead of Nexus. This is what actually delivers cross-agent memory sharing: a node is upserted with its OWN embedding under a deterministic per-(tenant,node) point id (idempotent re-push), and `fleet_pull` reliably SCROLLS a tenant's points back — the enumerate Nexus lacks (its `/search` doesn't return ingested docs and `/export` needs lancedb, which has a hard dependency conflict in the fleet). **Proven live with 2 agents:** agent-1 ingests → auto-syncs to the tenant dataplane; a fresh agent-2 in the same tenant `fleet_pull`s all of agent-1's nodes and can search them; a different tenant pulls nothing (isolation). Dimension-safe (never mixes vector dims in a collection). Local SQLite stays source of truth; best-effort, never raises.\n\n## [2.13.5] - 2026-07-02\n\n### Fixed\n- **CRITICAL — GraphMemory dataplane sync push was 100% failing.** `_fleet_push_node` sent `source_type=\"graph\"` / `content_type=\"graph_node\"`, which AitherNexus rejects as invalid enums (HTTP 500) — so NO graph node ever replicated in 2.13.4. Now sends the accepted `source_type=\"manual\"` / `content_type=\"text\"` (graph provenance is carried in `metadata.synced_from`). **Verified live** against a fleet Nexus: a 4-node ingest replicates with `pending==0` and the collection count reflects it.\n\n### Added\n- **Swarm-awareness notification** — on a successful sync batch, `GraphMemory` emits a best-effort `graph.synced` event (tenant/workspace/agent/collection/count) to `AITHER_FLEET_EVENTS_URL` so OTHER agents in the tenant/swarm can pull the fresh data and deconflict in-flight work (push-based awareness instead of polling). No-op unless the env is set.\n\n### Known limitation\n- Cross-agent **rehydration** (`fleet_pull`) is only reliable when the fleet Nexus is backed by lancedb (persistent + exportable). An in-memory Nexus stores on `/ingest` but neither its `/search` nor `/export` returns the docs — two-way read needs the persistent Nexus.\n\n## [2.13.4] - 2026-07-02\n\n### Added\n- **Memory-maintenance primitives on `GraphMemory`** (all opt-in; default behaviour byte-identical):\n - **Reinforce-on-recall**: `recall_with_activation(..., reinforce=True)` bumps `reinforcement_count`/`last_reinforced` on every RETURNED node's metadata mirror (one UPDATE per node, WAL-safe) — making the `MemoryRecord` reinforcement fields the activation scorer reads LIVE. Default `False` = zero writes.\n - **`sweep(now, archive_below=0.05, max_nodes=None, dry_run=False)`**: read-time decay enforced at rest — archives nodes past their tier TTL with freshness×reinforcement score below `archive_below` and `reinforcement_count <= 1`; `max_nodes` additionally archives the lowest-scored overflow. PERMANENT tier immune. Every archive is a reversible governance tombstone (`TombstoneStore.recover`) + a FORGET ledger entry; a node is never deleted without a tombstone. `dry_run` returns the would-archive list without acting.\n - **`promote(node_id, tier=?, role=?)`**: re-tier/re-role a node IN PLACE (tier/role columns + metadata mirror) — edges, embedding and ledger history survive; ledgers an UPDATE entry when governance is on.\n- `MutationType.UPDATE` ledger entry kind (in-place re-tier/re-role).\n- **Per-tenant dataplane sync for `GraphMemory`** — replicate the local graph to a per-tenant vector dataplane (Nexus/Qdrant) so a sovereign agent's memory is durable + rehydratable across restarts and hosting boundaries. `fleet_push_all_nodes()` (catch-up), `fleet_pull()` (best-effort semantic top-up), `fleet_sync_pending()`, and auto-sync on ingest (`_maybe_autosync` + `drain_sync()`). **On by default** (`AITHER_FLEET_SYNC=auto`; only `false/0/off` disables) with an inferred target (local Nexus `:8122` or the gateway in cloud mode) and a SEPARATE collection (`AITHER_FLEET_GRAPH_COLLECTION`, default `graph_memory`) from the KV memory sync. New `GraphMemory(tenant_id=, workspace_id=, fleet_url=, auto_sync=)` constructor args override env (required for multi-tenant host processes). Node `synced` column + schema v4 migration. TLS verify is CA-bundle-aware (`AITHER_CA_BUNDLE`) for the internal-TLS mesh. Local SQLite stays the source of truth; every sync path is best-effort and never raises into ingest.\n\n## [2.13.3] - 2026-07-02\n\n### Added\n- **Canonical self-deploying embeddings provider** (`adk.embeddings`): one embedding provider for the whole SDK so vectors are portable across scopes (platform / tenant / sovereign) — same model + dimension everywhere. Pinned to `nomic-embed-text` (768-d; the vLLM `--served-model-name` for nomic-embed-text-v1.5). Lazy, single-flight resolution chain: explicit `AITHER_EMBEDDINGS_URL` → local vLLM (`:8209` then `:8120`, HTTPS-then-HTTP) → local Ollama → gateway (`AITHER_GATEWAY_EMBEDDINGS_URL`) → auto-deploy a local vLLM embeddings container if a GPU + Docker are present (opt out with `AITHER_EMBED_AUTODEPLOY=0`) → CPU sentence-transformers (384-d, degraded) → feature-hash (384-d, degraded, always works). Every batch is dimension-tagged so callers never silently mix 768-d and 384-d vectors. `get_default_embedder()`, `embed_texts()`, `embed_one()`, `get_provider()`, `reset_provider()`.\n\n### Changed\n- **`GraphMemory` now defaults its embedder to the canonical provider** (opt out with `AITHER_GRAPH_EMBEDDER=legacy`), so every adk agent shares one 768-d space. Added a meta-table dimension guard: the index is pinned to its first embedding's dimension and later different-dimension vectors are refused (kills 768↔384 mixing that silently poisons cosine similarity).\n\n## [2.13.2] - 2026-07-01\n\n### Added\n- **Session persistence**: AitherShell now auto-saves the active session_id to `~/.aither/config.yaml` after each chat turn and auto-restores it on the next shell launch. Multi-turn conversations survive across restarts — the messages were always persisted in SQLite + JSON, only the session pointer was missing.\n- `/new` command to start a fresh session (clears saved session_id).\n- `/sessions` command to list recent sessions with agent name, message count, and timestamp. Resume any with `/resume <id>`.\n\n## [2.13.1] - 2026-07-01\n\n### Fixed\n- **CRITICAL:** `deploy_connect()` now sends a HEAD request to verify the GitHub release asset exists before downloading, with a clear error if the asset is missing.\n- **CRITICAL:** Compose file URLs are now pinned to a stable commit hash (configurable via `AITHER_COMPOSE_PIN` env) instead of tracking the mutable `main` branch.\n- **CRITICAL:** `deploy_adk_node()` now falls back to starting `adk-serve` natively when Docker is unavailable, instead of blocking with \"Docker not installed\".\n- **HIGH:** GHCR login failure now blocks deployment immediately with actionable guidance, instead of warning and continuing to a confusing image-pull failure.\n- **HIGH:** Health checks now stream the last 30 lines of Docker Compose logs on failure, so the user can see why a container didn't start.\n- **HIGH:** vLLM port allocation now probes for conflicts before assigning; if port 8200 is busy, workers are automatically remapped to the next free port.\n- **MEDIUM:** Compose file downloads are now cached with ETag; re-running `adk deploy node` skips re-downloading an unchanged file.\n- **MEDIUM:** DGX Spark discovery now respects `AITHER_DGX_HOSTS` env (comma-separated) for custom network topologies, instead of only checking `spark.local` and `192.168.0.33`.\n- **MEDIUM:** Cloud API keys (Anthropic, OpenAI, DeepSeek) are now validated with a lightweight probe during `adk setup` infra scan, so invalid keys surface early.\n- **MEDIUM:** `deploy_connect()` now validates the extracted extension has a `manifest.json` and warns if it's missing.\n\n## [2.13.0] - 2026-07-01\n\n### Removed\n- **`adk.faculties.memory_graph`** — the pickle-based `MemoryGraph` module is deleted. `from adk.faculties import MemoryGraph` now resolves to the canonical `adk.graph_memory.GraphMemory` for back-compat.\n- **`adk.platform.memory`** — the entire legacy memory subsystem (`MemoryManager`, `UnifiedMemorySystem`, `GameEngine`, `StoryboardEngine`, `AnchorGenerator`) is deleted. Agent memory is `adk.memory.Memory` + `adk.graph_memory.GraphMemory`.\n\n### Changed\n- `adk work` command now uses `GraphMemory` instead of the removed pickle-based faculty graph.\n- `adk.MemoryGraph` and `adk.faculties.MemoryGraph` lazy exports now resolve to `adk.graph_memory.GraphMemory`.\n- `agent.set_memory_graph()` kept for back-compat but documented as deprecated; agents already wire `GraphMemory` automatically in `__init__`.\n- Platform startup (`adk.platform.infrastructure.startup`) gracefully handles missing `MemoryManager`.\n- Platform CLI module listing and health check updated to reflect removed memory module.\n\n## [2.12.7] - 2026-07-01\n\n### Deprecated\n- `adk.faculties.MemoryGraph` — emits `DeprecationWarning` on instantiation; use `adk.graph_memory.GraphMemory` (SQLite-backed, governed, embedding-native) instead.\n- `adk.platform.memory` package — the legacy `MemoryManager`, `UnifiedMemorySystem`, `GameEngine`, and `StoryboardEngine` modules now emit `DeprecationWarning` on import; use `adk.memory.Memory` and `adk.graph_memory.GraphMemory` for new code.\n\n### Changed\n- Updated `adk.faculties` docstring and usage examples to point at the canonical memory surface.\n\n## [2.12.6] - 2026-07-01\n\n### Changed\n- Trimmed public package debt by removing grid runbook artifacts and shell/room binary placeholder directories from published artifacts.\n- Removed stale public-sync allowlist entries for deleted phase test files and added explicit public-sync guards for binary placeholder directories and grid artifacts.\n- Updated the public README grid note so it no longer links to runbook files excluded from the SDK package.\n\n## [2.12.5] - 2026-07-01\n\n### Added\n- Added best-effort fleet memory sync from local ADK memory into Nexus/Qdrant-compatible RAG stores, including batch catch-up and vector-search recall hooks.\n- Added saved config/env support for fleet memory URL, collection, and sync mode.\n\n### Changed\n- Excluded FormBridge runtime/test internals from public ADK packaging and public repo sync.\n\n## [2.12.4] - 2026-07-01\n\n### Added\n- Added a hot-reload endpoint for installed agent packs so newly applied pack tools can be picked up without restarting `aither-serve`.\n\n### Fixed\n- Fixed synchronous `ChatRelay` calls on Python 3.12 when no event loop is running; async WebSocket delivery is still scheduled when a loop exists.\n- Aligned gateway inference tier tests with the current public ladder: free, starter, pro, enterprise, platform.\n\n## [2.12.0] - 2026-06-28\n\n### Added — one-command local inference + upgrade path (click-to-run)\n- **`adk quickstart-local`** — detects hardware, picks a backend\n (Ollama / llama.cpp / vLLM), installs it, downloads a model, verifies, and\n prints next steps. Bootstraps Ollama itself via winget/brew/install.sh when\n missing (no \"go install it\" dead-ends). Default model `gemma4:e2b`.\n- **`adk backend switch <ollama|llamacpp|vllm>`** (+ `backend status`) — migrate\n the active deployment between engines; re-points config and smoke-tests.\n- **`adk install pack:<name>`** (+ `adk install list` / `adk packs`) — install\n ready-made agent packs (openclaw, hermes, claude-code) into `~/.aither/agents`.\n\n### Fixed (found via live end-to-end testing on Windows + CUDA)\n- quickstart-local reported success on a dead endpoint (smoke test was warn-only);\n a failed smoke now fails the command, with a service-readiness wait.\n- `--model` resolved to `None` (getattr default never fired) → uses the real default.\n- Smoke test: 30s→180s timeout for cold model load; accepts reasoning-model output\n (empty `content` at low token budgets is no longer a false failure).\n- `adk install pack:<name>` colon syntax (argparse rejected it before dispatch).\n- `adk backend status` health probe used a nonexistent `/health` path → 404; now\n probes `/v1/models` (uniform OpenAI-compatible liveness for all three backends).\n- Windows: removed non-ASCII glyphs from printed strings that crashed the cp1252\n console (a `✓` even made a successful install report failure).\n\n## [2.11.1] - 2026-06-14\n\n### Fixed — `adk host` / `adk login` device flow (autonomous onboarding)\nFound driving `adk host` against production; all broke real device-flow login:\n- **Device flow hit the portal FRONTEND, not AitherIdentity.** `adk login`/`adk host`\n POSTed `/auth/device/code` to portal/veil.aitherium.com, which 307-redirects API auth\n to `/login`. Added `_resolve_identity_url()` mapping the aitherium.com topology to\n `idp.aitherium.com` (the Identity API); localhost/bespoke pass through unchanged.\n- **Cloudflare 403'd urllib.** `idp.*` sits behind Cloudflare, which blocks urllib's\n default `Python-urllib/3.x` UA. Device-code + poll requests now send a real\n User-Agent + Accept (verified urllib→idp 200, was 403).\n- **`adk host` now self-authenticates via device flow** when there's no token, and\n **retries via device flow on a 401/403** (a stale saved token no longer dead-ends\n registration).\n\n## [2.11.0] - 2026-06-14\n\n### Added — attach your own MCP \"hands\" to a self-hosted agent\nA self-hosted agent now has the full brain (your model) · body (this loop) · **hands**\n(your tools) trifecta. The AitherOS platform relays the MCP servers you registered for\nyour agent in the `/stream` request body as `mcp_endpoints`, and the loop wires their\ntools into the ReAct turn:\n- **`adk/mcp_endpoint_tools.register_mcp_endpoint_tools(agent, endpoints)`** — speaks MCP\n JSON-RPC (`tools/list` to discover, `tools/call` to invoke) and registers a namespaced\n proxy tool (`{endpoint}__{tool}`) for each advertised tool. Mirrors `app_proxy_tools`.\n Best-effort: an unreachable endpoint registers nothing and never raises.\n- **`POST /stream` and `POST /chat/stream`** accept an optional `mcp_endpoints` list\n (`[{name, url, [headers]}]`); `_aitheros_stream` attaches them to the agent before the\n turn. Existing callers are unaffected (the field is optional).\n\n## [2.10.1] - 2026-06-14\n\n### Fixed — self-hosted `aither-serve` was crash-on-startup (2.10.0 regression)\nA fresh `pip install aither-adk && aither-serve --backend deepseek` could not boot. Found\nand fixed while standing up a real self-hosted DeepSeek agent end-to-end:\n- **Startup `NameError: name 'port' is not defined`** in three lifespan helpers\n (`_join_aithernet`, `_init_a2a_server`, `_start_mesh_hosting`) — they referenced a bare\n `port` no longer in scope. Now use `config.server_port`, and `main()` writes the resolved\n `--port`/`--host` back to `config` so every lifespan helper and the invoke_url see the\n actual bound port.\n- **Fleet self-registration crashed the whole server** — the registration handlers caught\n `(ImportError, RuntimeError, OSError, ConnectionError)` but **not** `httpx.HTTPError`, so a\n `RemoteProtocolError`/connect error talking to the control plane took the agent down. All\n network-facing lifespan handlers now also catch `httpx.HTTPError` (non-fatal, as intended).\n- **`--backend` was ignored when `AITHER_API_KEY` was set** — `_auto_detect` picked the cloud\n gateway before the explicit backend, and `_try_elysium_fallback` then replaced the chosen\n provider. An explicit backend (deepseek/openai/anthropic/your own vllm/ollama) now always\n wins; \"auto\"/\"gateway\" still flow through detection. The operator's own brain is never\n silently hijacked.\n- **Approved-tool trace truncated after `/sessions/{id}/confirm`** — the SSE relay treated\n `AgentResponse.tool_calls_made` (a `list[str]`) as dicts (`tc.get(...)`), raised\n `AttributeError`, and an over-narrow `except` swallowed it so the client saw only\n `session_start` + heartbeats. The relay now handles string tool names, the `except` is\n broad (always emits a typed `error` + terminal `complete`), and the module-level\n `_strata_ingest` `NameError` + Chronicle `session_id` kwarg mismatch are fixed.\n\n## [2.10.0] - 2026-06-13\n\n### Added — human-in-the-loop tool approval (`adk/approval.py`)\n- **Pause before a gated tool, resume on decision.** An agent can now pause its turn before\n executing a sensitive tool and wait for a human's allow/deny — so a managed control plane\n (or any operator) can gate tool use without forking the loop.\n- **Policy via `AITHER_TOOL_APPROVAL`** — a comma-separated list of tool names (optionally\n `agent:tool`), or `*` for every tool. Empty = no gating (unchanged behavior).\n- **Survives restarts / days-later approvals.** The paused turn (user message + pending tool\n calls) is persisted to disk keyed by `session_id`; resume is a fresh request, not a held\n connection. Decisions are recorded per `(session_id, tool_name)`.\n- **`AgentResponse.requires_action` / `.pending`** — set when a turn paused; `pending` lists\n the gated tool calls awaiting a decision.\n- **`AitherAgent.resume(session_id, decisions)`** — records the decisions and re-runs the\n paused turn (the gate consumes them: allowed tools execute, denied tools get a denial\n observation, the turn continues — and may pause again on a different gated tool).\n- **Server:** `POST /sessions/{id}/confirm` records decisions and streams the resumed turn;\n `/stream` emits a `requires_action` event when a turn pauses.\n\n## [2.9.0] - 2026-06-13\n\n### Added — universal LLM continuation primitive (`adk/llm/continuation.py`)\n- **`run_continuation()` + `stitch()`** — one shared continue-until-complete primitive. When a\n completion stops because it hit the output token cap (`finish_reason == \"length\"`), it continues\n the generation and STITCHES the chunks (overlap-dedup + restart-detect) into a complete answer,\n instead of every call-site hand-rolling its own retry. Bounded three ways (max rounds AND total\n output ceiling AND a no-growth break), and it never mutates the caller's message history.\n- **`LLMProvider.chat_with_continuation()`** and **`LLMRouter.chat_with_continuation()`** — every\n provider AND the router inherit the primitive, so any call-site gets continuation for free.\n- Kill-switch `ADK_LLM_CONTINUATION=off`; tunables `ADK_LLM_MAX_CONTINUATIONS`,\n `ADK_LLM_MAX_TOTAL_OUTPUT_TOKENS` (defaults: 2 rounds / 8192 tokens).\n\n### Changed\n- The ReAct loop's truncation recovery now uses the shared primitive. The previous inline\n doubling-retry REPLACED the partial response (losing earlier text in `resp.content`) and injected\n continuation scaffolding into the live message history; it now STITCHES and leaves history clean.\n Tool-call turns are never continued as text.\n\n### Included — previously-committed-but-unreleased changes\n- 2.8.2: TLS verification on by default (`verify=False` removed).\n- 2.8.1: baked license + pack-signing public keys.\n- 2.8.0: version-bump release.\n\n## [2.7.0] - 2026-06-05\n\n### Added — provider-agnostic LLM cost levers (at the `LLMProvider` ABC)\n- **Prompt caching.** New `ProviderCapabilities` (`prompt_cache: explicit|automatic|none`, `batch`) and\n normalized `LLMResponse.cache_read_tokens`/`cache_write_tokens`, so the \"tokens saved\" meter never\n depends on which backend served the turn. Anthropic inserts `cache_control` breakpoints over the stable\n system+tools prefix; OpenAI/DeepSeek caching is automatic and their `cached_tokens` are now surfaced\n (previously discarded). Default-on in `AitherAgent.chat`; override with `cache=False`.\n- **ResponseCache** (`adk/llm/cache.py`) — exact-match response cache with a pluggable backend, wired into\n `LLMRouter` as an opt-in (`LLMRouter(response_cache=…)` + per-call `cacheable=True`). OFF by default — no\n determinism change unless opted in.\n- **Gemini provider** (`adk/llm/gemini.py`) with explicit `cachedContents` caching; registered in the router.\n- **Async batch runner** (`adk/llm/batch_runner.py`) — `run_batch()` over the Anthropic Message Batches /\n OpenAI Batch APIs (~50% off) with a transparent fallback to concurrent `chat()` where unsupported.\n- **Graph-RAG retriever interface** (`adk/graph_retriever.py`).\n\n### Added — principal authority (prompt-injection defense enforced outside the model)\n- `adk/auth.py`: `Principal` / `AuthContext` / `PrincipalResolver` (+ deny-all default).\n- `ToolRegistry.execute(name, args, auth=…)` — a default-deny authorization gate at the tool boundary, so a\n forbidden tool call is blocked regardless of what the LLM emits. `ToolDef` gains `required_clearance` /\n `action_class`. Threaded through `AitherAgent.chat`; `auth=None` is fully backward-compatible.\n- `adk/channel_auth.py`: Telegram webhook-secret + CEO-id checks and HMAC-signed approvals.\n\n## [2.6.4] - 2026-06-05\n\n### Fixes\n- **Loop-guard nudges corrupted the message history → DeepSeek 400 mid-run.**\n When the ReAct loop emitted multiple tool calls in one assistant turn and the\n loop guard fired a WARN/BLOCK/CIRCUIT_BREAK on one of them, it spliced a\n standalone `system` nudge message *between* the tool results. DeepSeek (and\n strict vLLM chat templates) require an assistant `tool_calls` message to be\n followed by exactly one `tool` result per `tool_call_id`, contiguously — so the\n interleaved message produced `400 … insufficient tool messages following\n tool_calls message` and aborted the turn (this is reliably hit after crossing\n the 15-call circuit-break threshold, where every subsequent call WARNs).\n Per-call guidance is now folded into that call's own `tool` result, and any\n standalone steering is deferred to a single message AFTER the whole batch —\n preserving the `tool_calls`↔`tool_results` pairing every backend requires.\n Regression test: `deep-research-agent/tests/test_msg_structure.py`\n (loopguard-flood scenario).\n\n## [2.6.3] - 2026-06-04\n\n### Fixes\n- **Transient provider errors killed a run on the first blip.** A single\n `502 Bad Gateway` / `503` / `529 overloaded` / `429` from the upstream API\n aborted the whole request — the Anthropic provider had **no retry at all**, and\n the OpenAI-compatible provider only retried `429`. Both now retry transient\n statuses (`408/429/500/502/503/504/529`) with `Retry-After`-aware exponential\n backoff (3 attempts), and surface the provider's body if all retries are\n exhausted. Non-transient `4xx` (e.g. `400`) still fails fast. Applies to\n `chat` and `chat_stream` on both providers.\n\n## [2.6.2] - 2026-06-04\n\n### Fixes\n- **DeepSeek (and strict OpenAI-compatible backends) 400'd on long tool\n conversations.** The ReAct loop legitimately injects mid-conversation\n `system` steering messages (the \"[DIMINISHING RETURNS]\" hint after several\n low-output tool iterations, loop-guard nudges). OpenAI tolerates a non-leading\n `system` message; DeepSeek rejects it with a 400. Long research runs\n (many `fetch_url` calls) reliably tripped this. Fix: non-leading `system`\n messages are demoted to `user` at the OpenAI-compatible provider boundary\n (`_demote_nonleading_system`) — steering intent preserved, payload valid on\n every backend; leading system prompt(s) untouched.\n- **Opaque HTTP errors.** `resp.raise_for_status()` discarded the provider's\n response body, so a 400/422 surfaced as a bare `Client error '400 Bad\n Request'` with no cause. `_ensure_ok` now includes the provider's actual\n message (e.g. `context_length_exceeded`, `Invalid 'messages'`) in the raised\n error, for both `chat` and `chat_stream`.\n\n## [2.6.1] - 2026-06-04\n\n### Fixes\n- **`MCPServer.mount()` returned HTTP 422 for every `/mcp` call.** Under\n `from __future__ import annotations` the route handler's `request: Request`\n annotation is a string that FastAPI resolves against the *module* globals, but\n `Request` was imported locally inside `mount()` — so FastAPI couldn't resolve\n it and treated `request` as a required query param. The MCP server endpoint\n (`initialize`, `tools/list`, `tools/call`) was unusable when mounted. FastAPI\n symbols are now imported at module level (guarded; the `[node]` extra still\n gates actual use). Any agent's tools can now be exposed as a working MCP\n server with `MCPServer(...).mount(app)`.\n\n## [2.6.0] - 2026-06-04\n\n### Streaming + scaffolding\n- **`AitherAgent.stream_chat`** — reliable streaming sibling of `chat()`: native\n tool-calling loop (concludes on its own, unlike the text-protocol `stream_react`)\n that emits live `tool`/`tool_result` events and streams the answer as `token`\n events. `token_delay` paces tokens for a typing effect.\n- **`adk new <template>`** — scaffold a full, runnable template app. Ships the\n `deep-research` template (a cited web-research agent: pack + server + web UI):\n `adk new deep-research`.\n\n### Fixes\n- **BYO-key no longer community-capped.** The free-tier monthly token cap now applies\n only to the metered Aitherium gateway backend, never to a user's own provider key\n (Anthropic/OpenAI/DeepSeek/Ollama/vLLM). Previously it could brick a self-hosted\n agent after ~100k tokens on the user's own key.\n- **Stronger, grounded memory recall.** `chat()` now injects the top-6 knowledge-graph\n hits (untruncated) with a hard no-fabrication instruction — was top-3 truncated to\n 200 chars, which surfaced partial facts and let weaker models invent the rest.\n- **Tool-arg coercion.** `ToolRegistry.execute` coerces LLM-supplied args toward each\n parameter's annotated type, so a tool no longer crashes when a model passes a list\n or string where an int/float/bool was expected.\n\n### Docs\n- New `docs/AGENT_DEV_GUIDE.md` — the golden path for building agents/packs.\n\n## [2.5.0] - 2026-06-04\n\n### Continual learning — agents that learn from runs and reuse memory\n\nAgents now improve the second time they do a task instead of starting from scratch.\n\n### Added\n- **Recall-before**: `AitherAgent.chat()` searches the local `SkillStore` and injects the\n top matching learned skills as a system block, so the model reuses proven procedures.\n- **Learn-after**: `_learn_after()` runs after each response — reinforces (`success_count++`)\n any skills that were recalled+reused, and extracts + saves a NEW skill from successful\n multi-tool runs via `SkillExtractor`. Wired at both `chat()` return paths.\n- Gated by `AITHER_SKILLS` (default on). The previously-unused `SkillStore`/`SkillExtractor`\n are now connected to the run loop.\n\n### Notes\n- Pairs with the platform-side LearnedProcedure substrate (recipes → scored procedures →\n skills/tools/A2A/packs → MCTS priors → evolution). The SDK shares memory via the Spirit\n bridge when configured.\n\n## [2.0.0] - 2026-06-02\n\n### Open-core boundary (BREAKING)\n\nThis release draws a single, enforced free/paid line. The free tier stays\ngenuinely useful (real agent, typed + graph + code memory, ReAct, ~essential\ntools); paid tiers unlock the *scale* capabilities via portal.aitherium.com.\n\n### Added\n- **`adk/licensing.py`** — the entitlement keystone. Resolves a tier\n (COMMUNITY by default), verifies optional portal-signed `~/.aither/license.json`\n (Ed25519), and answers `can_use_fleet/channels/cron/swarm/auto_neurons`,\n `is_agent_licensed`, `max_effort`, etc. **Fail-closed**: no/invalid/expired\n license → free tier; an unsigned license never grants premium.\n `AITHER_TENANT_SLUG=aitherium` → unrestricted INTERNAL tier.\n- **Moat guard** (`scripts/check_moat_boundary.py`) wired into the publish\n workflow — blocks any wheel that leaks the moat or drops the keystone.\n- Tests: `tests/test_licensing.py`, `tests/test_moat_boundary.py`.\n\n### Changed (gating — fail-closed)\n- Free tier effort is capped at 3 (reasoning effort 7–10 needs Professional).\n- Monthly token budget hard-block on the free tier (no silent overspend).\n- Gated behind a paid tier: fleet mode (`FleetConfig`), cross-agent delegation\n (`AgentForge.delegate`), channel adapters, cron scheduler, proactive\n auto-neurons, and `swarm`/`swarm_code` dispatch.\n\n### Removed\n- **`adk/nanogpt.py`** (on-device training IP) removed from the published SDK\n and the public repo; relocated to the internal AitherOS moat. Excluded from\n the wheel as defense-in-depth.\n- Pre-2.0 releases shipped this IP and ungated capabilities and are being\n yanked (PyPI) / removed (GitHub releases). See `scripts/yank_leaky_releases.py`\n and `scripts/purge_public_leaks.sh`.\n\n## [1.20.0] - 2026-05-27\n\n### Fixed\n- **Pack discovery path mismatch** — `_find_local_packs()` now scans both\n `~/.aither/packs/` and `~/.aitheros/packs/`, so packs installed via\n `adk pack install` are discovered by `adk-workspace`.\n\n### Added\n- **Auto-register on pack install** — `adk pack install` now auto-registers\n with the portal if the pack contains an `agent.yaml` with a `portal` section.\n One command does download + extract + register.\n- **Flux event on agent registration** — `developer_portal.py` emits\n `agent.registered` Flux event so the portal fleet UI updates in real-time.\n\n## [1.15.0] - 2026-05-26\n\n### Added\n- **Cloud memory** — `adk quickstart --cloud` now tests gateway memory and\n auto-configures Spirit URL so memories persist across devices via\n `gateway.aitherium.com/v1/memory/teach` and `/v1/memory/recall`.\n- **Config→env export for cloud mode** — `Config.from_env()` now exports\n `AITHER_CLOUD_MODE`, `AITHER_SPIRIT_URL`, and spirit path env vars from\n saved config so Memory and other modules pick them up without needing\n explicit env vars set by the user.\n- **Cloud Quick Start section** in README — 3-command setup for users who\n just want a Claude/GPT-4/DeepSeek API key and the full agent harness.\n- **`--memory` profile for node deployment** — `adk deploy node --memory`\n adds Spirit (8087) + WorkingMemory (8101) containers for persistent\n vector memory. Without this, agents use local SQLite only.\n- **Configurable vLLM in node compose** — `AITHER_VLLM_MODEL`,\n `AITHER_VLLM_GPU_UTIL`, `AITHER_VLLM_CTX_LEN`, `AITHER_VLLM_QUANT_ARGS`\n env vars let `adk setup` write GPU-detected tier settings to `.env`\n and have the node compose pick them up (TQ4 for small GPUs, BNB for large).\n\n### Fixed\n- **Memory cloud routing** — `Memory._spirit_teach()` and `_spirit_recall()`\n now use configurable paths and auth headers for gateway proxy. Previously\n cloud-only users silently lost all Spirit memory calls.\n\n## [1.9.0] - 2026-05-24\n\n### Added\n- **Composable ToolPack system** — agents can load tool packs from\n `.toolpack.yaml` directories. Drop a pack in `~/.aitheros/packs/` or\n set `AITHER_PACKS_DIR`, and any agent discovers it automatically.\n- **`tools.packs` in agent.yaml** — list tool pack IDs to auto-load\n (e.g. `packs: [crm-tools, calendar-pro]`).\n- **`register_tool_packs(agent, pack_ids, packs_dir)`** in `builtin_tools` —\n programmatic pack loading for agents.\n- **`AITHER_TOOL_PACKS` env var** — comma-separated pack IDs loaded on\n agent init (alternative to YAML config).\n- **Unknown builtin categories resolve as pack IDs** — `tools.builtin:\n [file_io, crm-tools]` now works: `crm-tools` is looked up as a pack.\n\n## [1.8.0] - 2026-05-24\n\n### Removed\n- **11 dead modules** (3,023 lines) — `agent_deploy`, `cloud_deploy`, `create`,\n `phonehome`, `sdk_bridge`, `sovereign`, `telemetry`, `telemetry_config`,\n `error_reporter`, `anonymizer`, `package`. None were imported anywhere.\n- **29 ghost exports** from `__init__.py` — `secrets`, `otel`, `fs_sandbox`,\n and `vector_memory` modules were listed in `__all__` and `__getattr__` but\n the backing files never existed. Accessing them raised `ModuleNotFoundError`.\n- **Stale dist/ artifacts** — removed old wheels/tarballs (v0.4.0–v1.4.1, 11 MB).\n- **Orphan test scripts** — `test_phase1.py` and `test_phase23.py` (one-off\n validation scripts not part of the pytest suite).\n\n### Fixed\n- **Version mismatch** — `__init__.__version__` now matches `pyproject.toml`\n (was stuck at 1.6.0).\n\n## [1.5.0] - 2026-05-23\n\n### Added\n- **`--sovereign` flag for `adk deploy node`** — registers the node with the\n Aitherium hub via federation after deployment. Saves federation credentials\n to `~/.aither/.env.federation`. Accepts `--hub` (custom hub URL) and\n `--tenant` (tenant slug) options. Non-fatal: node runs standalone if\n registration fails.\n- **Federation → fleet cross-post** (Genesis) — sovereign nodes that register\n or heartbeat via `/federation/register` and `/federation/heartbeat` now\n automatically appear in the fleet dashboard (`/fleet/deployments`) with\n `node_type: \"sovereign\"` and live status/metrics.\n- **\"Deploy Sovereign Node\" section on Connect page** (AitherVeil) — Section 8\n with ADK deploy commands, fleet dashboard links, and provision page links.\n- **`deploy-node` action in tunnel proxy** (AitherVeil) — Connect page and\n external callers can now request bootstrap scripts via\n `POST /api/tunnel/devworkspace?action=deploy-node`.\n\n### Fixed\n- **Fleet provision page** — API URL corrected from `/api/fleet/provision` to\n `/api/bridge/genesis/fleet/provision` (matches bridge proxy pattern).\n- **Fleet detail page** — API URL corrected from `/api/fleet/deployments/` to\n `/api/bridge/genesis/fleet/deployments/` (same fix).\n- **Dockerfile** — entry point corrected from `aither-serve` to `adk-serve`\n (matches `pyproject.toml` script registration). Version label updated.\n\n## [1.4.1] - 2026-05-22\n\n### Fixed\n- **macOS executable builds** — dropped `--target-arch universal2` from\n `packaging/build_executable.py`. PyPI wheels for `pydantic_core` and other\n native deps are arch-specific, not fat binaries, so universal2 always failed\n with `IncompatibleBinaryArchError`. Now builds for the runner's native arch\n (`aither-macos-arm64`).\n- **Release workflow strategy** — added `fail-fast: false` so a single\n executable build failure no longer cancels the other OS jobs.\n- **PyPI publish** — added `skip-existing: true` so re-runs of the same\n version don't fail when the artifact already exists.\n- **GitHub Release job** — now runs whenever `pypi` succeeds even if some\n executable matrix entries fail, so the release page is always created with\n whatever binaries did build successfully.\n\n## [1.4.0] - 2026-05-22\n\n### Added\n- **Sovereign template now ships with `agent_core` 5-tier memory** — generated\n backends include the canonical `agent_core/` package (working → episodic →\n semantic → procedural → identity) plus `agent_memory.py` helper, vendored\n from `portal-kit-backend`.\n- `aiosqlite>=0.20.0` added to sovereign template `requirements.txt` for\n local agent_core SQLite storage.\n- Document tombstoning: `evict_cached_conversations_referencing` is wired\n through document-delete handlers in consumer apps so memories citing\n deleted documents are blocked from recall.\n- New tests: 13 smoke tests for `portal-kit-backend/agent_core` covering\n promotion thresholds, decay rates, store roundtrip, tombstone recall\n blocking, circuit breaker state machine, and chat-turn recording.\n\n### Changed\n- `scripts/vendor_agent_core.py` now syncs canonical `agent_core` to 4\n targets: WorkspaceRuntime, ADK sovereign template, and the GargBot +\n Chelle consumer mirrors (previously only 2).\n\n## [1.2.1] - 2026-05-21\n\n### Added\n- `adk train` command group — 6 subcommands: status, launch, logs, cancel, runs, register-gpu\n- `/slash-commands` endpoint — ADK server auto-exposes all 35 CLI commands as structured JSON manifest for AitherShell\n- `/cli/execute` endpoint — AitherShell can run any CLI command through the server\n- `build_command_manifest()` — introspects full argparse tree (commands, args, choices, subcommands)\n- `_register_commands()` extracted from main() for shared parser construction\n\n### Changed\n- `_get_genesis_url()` now probes: Genesis → ADK server → Aitherium cloud gateway (works without Genesis)\n\n## [1.2.0] - 2026-05-21\n\n### Added\n- **Runtime backend switching** — `agent.switch_backend(\"deepseek\")` and `agent.llm.switch_backend()` change provider without recreating agent\n- **Hybrid reasoning** — `agent.set_reasoning_backend(\"anthropic\")` routes effort 7+ to cloud API while keeping local orchestrator\n- **First-class DeepSeek provider** — `deepseek` in effort model table with `deepseek-chat` and `deepseek-reasoner`\n- **TQ4 quantization tiers** — `nano` (6GB, TurboQuant 4-bit), `standard-tq4` (12GB, both models TQ4), `hybrid-tq4` (6GB + cloud reasoning)\n- **`adk backend` CLI** — `list`, `set`, `set-reasoning`, `test` subcommands\n- **`adk quickstart`** — unified first-run wizard (setup + auth + shell)\n- **`adk tools`** — list local + MCP tools with tier markers\n- **`adk backup`** — tarball export of `~/.aither/`\n- **`adk ingest`** — feed docs/files into knowledge graph\n- **`adk setup nemotron`** shortcut — alias for `--tier lite`\n- **`--reasoning-api`** flag in setup — `anthropic`, `openai`, `deepseek`, `gateway`\n- **`--dgx-spark URL`** flag — remote vLLM endpoint configuration\n- **DGX Spark auto-detection** — scans `spark.local`, `192.168.0.33`, `AITHER_DGX_URL` env\n- **Post-setup smoke test** — sends real inference request after container startup\n- **Schema migration system** — version table + migration runner in `memory.py` and `graph_memory.py`\n- **Configurable vLLM port scan** — `AITHER_VLLM_PORTS` env var for custom ports\n- **`aithershell` entry point** — alias for `adk` in pyproject.toml\n- Shell auto-downloads binary on first `adk shell` (no `--install` needed)\n- Shell pre-flight check warns if no backend detected\n- Shell passes full config via env vars (API key, tenant, inference URL)\n- DGX/Remote and Cloud API checks added to `adk doctor`\n- Config fields: `reasoning_backend`, `reasoning_api_key`, `deepseek_api_key`, `dgx_url`, `vllm_extra_ports`\n\n### Fixed\n- `test_aeon_chat_basic` timeout — mock scope was exiting before request\n- `adk doctor` check_disk timeout — cap rglob at 5000 files\n\n## [1.1.7] - 2026-05-19\n\n### Added\n- Remote agent pipeline — `adk onboard` → fleet dispatch → remote inference\n- Agent-to-agent federation with mesh relay\n- `adk connect` for desktop mesh enrollment\n\n## [1.1.6] - 2026-05-19\n\n### Fixed\n- Tool call execution recovery across all backends\n- Hermes tool call fallback parsing for vLLM\n\n## [1.1.5] - 2026-05-19\n\n### Fixed\n- Tool call execution + Hermes fallback parsing improvements\n\n## [1.1.0] - 2026-05-18\n\n### Added\n- Multi-channel gateway (Telegram, Discord, Slack, webhook)\n- Aeon group chat with 7 presets\n- Skills auto-extraction from multi-step sessions\n- MCP stdio server for Claude Code integration\n- `adk gateway`, `adk cron`, `adk skills`, `adk soul` commands\n- SOUL.md import/export for portable agent identity\n- Elysium cloud device-flow authentication\n\n## [1.0.0] - 2026-05-18\n\n### Breaking\n- Package consolidated — `aither_adk` namespace replaced by `adk` + `adk.platform`\n- 93 duplicate files removed from `AitherOS/packages/aither_adk/`\n\n### Added\n- Standalone core primitives (`adk.core`: Agent, Tool, Memory, ModelBackend, Capability, Trace)\n- `adk.platform` sub-package merging all platform toolkit modules\n- Compatibility shims for legacy `from aither_adk.*` imports\n\n### Changed\n- Version synced across pyproject.toml, `adk/__init__.py`, `adk/platform/__init__.py`, npm package\n- Classifier promoted from Alpha to Beta\n\n## [0.16.0] - 2026-04-16\n\n### Swarm Coding Engine & Repowise Integration\n- **`repowise_search` tool** — Semantic + keyword hybrid code search via Repowise, with ripgrep fallback\n- **`swarm_code` tool** — Dispatch complex tasks to 11-agent swarm pipeline (ARCHITECT->SWARM->REVIEW->JUDGE)\n- **`agent.swarm()`** — Async convenience method with configurable mode, effort, timeout\n- **`agent.code_search()`** — Async convenience method returning structured results\n- New tool categories: `repowise` and `swarm` in TOOL_CATEGORIES\n- `repowise_search` and `swarm_code` added to `__init__.py` exports with lazy loading\n- `IDENTITY_DEFAULTS` updated: repowise for code-focused agents, swarm for orchestration agents\n- Standalone graceful degradation: repowise falls back to ripgrep, swarm returns structured error\n\n## [0.13.0] - 2026-04-02\n\n### Graph Faculties — Local Knowledge for Every Agent\n- **CodeGraph** (2,799 lines) — Full Python AST indexer with call graph, keyword/semantic/hybrid query, embedding matrix cache, incremental re-indexing, multi-hop chain expansion\n- **MemoryGraph** (1,339 lines) — Graph-based persistent agent memory with 10 edge types, hybrid query (keyword + semantic + graph expansion), multi-hop recall, pickle persistence\n- **EmbeddingProvider** — 4-backend fallback chain: sentence-transformers (GPU/CPU) -> Ollama -> Elysium cloud -> feature hashing (zero deps)\n- **BaseFacultyGraph** — Abstract base with HMAC-SHA256 validated pickle persistence\n\n### Agent Integration\n- `agent.set_code_graph(cg)` — auto-registers `code_search` + `code_context` tools\n- `agent.set_memory_graph(mg)` — auto-registers `remember` + `recall` + `memory_stats` tools\n- Both graphs inject context into LLM prompts automatically during chat\n\n### Zero-Config Onboarding\n- `adk start` / `adk` (no args) — auto-detect project, index code, detect LLM, persistent memory, interactive chat\n- Works for any directory: Python codebases, doc folders, mixed workspaces\n- Auto-detects LLM: Ollama -> vLLM -> Elysium -> OpenAI -> Anthropic\n- Per-project persistent memory in `~/.aither/memory/<project>`\n- `adk index <path>` — standalone indexing with progress bar and stats\n\n### MCP Gateway\n- `POST /v1/embeddings` — OpenAI-compatible embedding proxy to vLLM-embeddings:8209\n- Available to all tiers (embeddings are free)\n- ADK EmbeddingProvider uses this as Elysium cloud fallback\n\n### Optional Dependencies\n- `pip install aither-adk[graphs]` — numpy for 10x cosine similarity speedup\n- `pip install aither-adk[embedding]` — sentence-transformers + torch for local GPU embeddings\n- `pip install aither-adk` alone — graphs work with feature hashing (zero deps)\n\n## [0.12.0] - 2026-04-01\n\n### Bootstrap & Service Discovery\n- Version handshake -- ADK checks major.minor compatibility with Genesis/Node\n- Background reconnect loop -- ServiceBridge re-probes every 30s in standalone mode\n- Port 8080/8090 documented -- MCP vs OpenAI-compat clearly separated\n- Genesis URL configurable via GENESIS_URL env var (was hardcoded)\n- Standalone mode warning -- visible stderr alert when AitherOS not detected\n- Auto-reconnect on startup when services come online\n\n### Elysium Cloud Inference\n- Unified gateway URL -- gateway.aitherium.com handles auth + billing + inference\n- Streaming inference -- SSE passthrough for /v1/chat/completions with stream=true\n- Auth proxy routes -- /v1/auth/register, /v1/auth/login, /v1/auth/me\n- Billing proxy -- /v1/billing/balance through gateway\n- AitherConnect Elysium fallback -- cloud inference when local Genesis is down\n- AitherDesktop Elysium fallback -- third-tier chat fallback after Node\n\n### Infrastructure\n- /discovery endpoint on Genesis -- unified service URLs/versions/health\n- /api/config/services on Veil -- runtime port config for client-side JS\n- Veil healthcheck gates on Genesis -- unhealthy when backend is down\n- Desktop crash detection 90s->60s (threshold 3->2)\n\n## [0.11.0] - 2026-04-01\n\n### Agent Execution Quality (Claude Code Parity)\n- Raise loop guard block threshold from 3 to 4 -- agents get more room for iterative search\n- Soft synthesis nudge for effort >= 4 (no tool stripping, trust the model)\n- max_output_tokens escalation -- retry up to 3x when response is truncated\n- Tool result pairing guarantee -- synthetic error for orphaned tool_use blocks\n- Micro-compaction of old tool results -- save context tokens on long sessions\n- First-turn tool forcing only for effort >= 6 (trust model for lower effort)\n- Diminishing returns detection -- nudge agent when 3+ turns produce < 500 tokens\n- Message normalization -- merge consecutive same-role messages, strip empties\n- LLM retry with exponential backoff (5 retries, 500ms-16s, jitter)\n\n## [0.9.0] - 2026-03-16\n\nThe \"connected world\" release. Cross-platform identity pairing, voice capabilities, and multi-channel integration.\n\n### Added\n- **Pairing**: Cross-platform identity linking (`adk/pairing.py`)\n - `PairingManager` — SQLite-backed identity linking with 6-char pairing codes\n - Link users across Telegram, Discord, Slack, WhatsApp with 10min TTL codes\n - Canonical session IDs for cross-channel conversation continuity\n - `get_session_id()` returns \"user-{id}\" for paired users\n- **Voice**: Speech-to-text and text-to-speech client (`adk/voice.py`)\n - `VoiceClient` — async STT/TTS/emotion via AitherVoice service\n - Convenience functions: `hear()`, `say()`, `feel()`\n - 6 voice options: alloy, echo, fable, nova, onyx, shimmer\n - Emotion detection with intensity scoring\n- New exports: `PairingManager`, `PairingResult`, `PlatformIdentity`, `VoiceClient`, `TranscriptionResult`, `SynthesisResult`, `EmotionResult`\n\n### Changed\n- `__init__.py` exports expanded with pairing and voice symbols\n\n## [0.6.0] - 2026-03-13\n\nThe \"group mind\" release. Multi-agent group chat, creative tools, and Iris identity.\n\n### Added\n- **Aeon**: Multi-agent group chat engine (`adk/aeon.py`)\n - `AeonSession` — persistent group chat with parallel agent execution\n - 7 presets: balanced, creative, technical, security, minimal, duo_code, research\n - Orchestrator synthesis: Aither summarizes all agent responses\n - Serial execution for Ollama, parallel for vLLM/cloud\n - ConversationStore persistence with `type: \"aeon\"` metadata\n - `group_chat()` one-shot convenience function\n- `aither aeon` CLI command — interactive terminal group chat with color-coded agents\n - `-p/--preset`, `-a/--agents`, `-r/--rounds`, `--no-synthesize` flags\n - `reset` and `quit` commands\n- Server endpoints: `POST /aeon/chat`, `GET /aeon/presets`, `GET /aeon/sessions/{id}`\n- Creative tools in builtin_tools: `image_generate`, `image_refine`, `image_search`, `video_generate`\n- Iris agent identity with visual generation capabilities\n- 48 Aeon tests (data models, presets, chat, context, persistence, server, exports)\n\n### Changed\n- `__init__.py` exports: `AeonSession`, `AeonResponse`, `AeonMessage`, `group_chat`, `AEON_PRESETS`\n\n## [0.5.0] - 2026-03-13\n\nThe \"tenant-ready\" release. Multi-tenant admin, permission grants, safety profiles, and a full setup wizard.\n\n### Added\n- `aither setup` interactive setup wizard with hardware detection, model selection, identity config\n- Strata storage backend: SQLite WAL persistence for conversations, memories, knowledge graphs\n- CLI test runner: `aither test` with auto-discovery and parallel execution\n- Permission grants system for MCP tool access control\n- MCP account management tools\n- LLM provider auto-detection for Ollama and vLLM endpoints\n- Apache-2.0 LICENSE file\n- Elysium desktop sync module\n- Comprehensive CLI test suite (493 tests)\n- Strata storage test suite (972 tests)\n- LLM provider tests (82 tests)\n\n### Changed\n- CLI expanded: `aither setup`, `aither test`, `aither bugreport`, `aither doctor`\n- Strata module rewritten as full local-first storage engine\n- Server startup includes Strata initialization\n- README rewritten with clearer quickstart and architecture docs\n\n### Fixed\n- Dead documentation links\n- Python 3.11 compatibility issues\n- Ruff per-file-ignores configuration\n- Identity provisioning edge cases\n- Docker node-gyp native dependency builds (python3 + build tools)\n\n## [0.4.0] - 2026-03-13\n\nThe \"own your AI\" release. Self-hosted agent OS for people who don't want their data on someone else's servers.\n\n### Added\n- Muse agent identity (creative/artistic generation)\n- Port 8120 to vLLM scan for ExoNodes discovery\n- Public roadmap with milestone-based porting schedule\n- Competitive positioning: self-hosted alternative to cloud-locked AI appliances\n\n### Changed\n- LLM router: compute-aware effort routing (replaces effort-level context gating)\n- README rewritten for sovereignty-first messaging\n- Package description updated\n- Promoted from alpha to stable release\n\n### Fixed\n- Version string consistency between pyproject.toml and __init__.py\n- vLLM port scanning now includes port 8120 (ExoNodes)\n\n## [0.3.1] - 2026-03-09\n\n### Added\n- GraphMemory: SQLite knowledge graph with Ollama embeddings and hybrid search\n- NeuronPool: auto-fire pre-LLM data gathering agents\n- NanoGPT: pure Python char-level transformer with LoRA fine-tuning\n- Safety gates: IntakeGuard (input), LoopGuard (recursion), Sandbox (code exec)\n- Event system: EventEmitter with chat/tool/forge event types\n- Builtin tools: identity-based tool selection\n- ServiceBridge: auto-discovery of AitherOS services\n- Streaming: chat_stream with safety gate integration\n- Auth middleware: Bearer token authentication\n- CLI: `aither init` and `aither serve` commands\n\n### Changed\n- Wired 5 previously disconnected modules into agent loop\n- 522 total passing tests (up from 85)\n\n## [0.3.0] - 2026-03-07\n\n### Added\n- Clean-room agent development kit\n- Multi-backend LLM providers (Ollama, OpenAI, Anthropic)\n- AitherAgent class with @tool decorator\n- SQLite conversation memory\n- OpenAI-compatible server (`aither-serve`)\n- 16 agent identities as package data\n- MCP bridge to mcp.aitherium.com\n- Privacy-centric opt-in telemetry\n- Bug reporting CLI and API\n- FastAPI server with OpenAI-compatible endpoints\n- Hardware auto-detection (5 tiers, 11 profiles)\n- Fleet orchestration and multi-agent coordination\n- A2A mesh protocol\n- Federation protocol for cross-instance agent dispatch\n\n### Initial release\n- 85 passing tests\n- Apache-2.0 license\n"}, "9369ab0103baefcdf94d28a4bb92e43be56d1fc8": {"t": 1785748171.3604944, "n": 81975, "h": "7762e1ceb9df3050", "c": "# Changelog\n\nAll notable changes to aither-adk will be documented in this file.\n\n## [2.23.1] - 2026-07-14\n\n### Fixed — CompletionGate no longer false-fails on correct results\n\n- **Tolerant judge JSON extraction.** The LLM judge parsed its reply with `json.loads(text[first\"{\":last\"}\"])`, which raised `JSONDecodeError` (\"Extra data\") whenever the model appended prose after the verdict or emitted a second object — and `verify()` turned that into a fail-closed verdict, so a genuinely completed task reported `unverified`. New `_first_json_object()` uses `JSONDecoder.raw_decode` from the first `{` that begins a valid object and ignores trailing content (prose, markdown fences, extra objects). A judge that answers with no parseable JSON still fails closed (never a soft pass), now with a clear reason. The same extractor backs `_derive_criteria`.\n- **Bare code-like tokens are hard-checked.** A natural criterion like `output contains ADK_AUTO_OK` (no quotes) previously fell through to the judge; `hard_checks` now also matches bare identifier/code shapes (contains an underscore, or 4+ CAPS/digit chars), with a stopword guard so a descriptive uppercase word (`OUTPUT`, `CONTAIN`) never becomes a false required token. Quote a token to force it regardless.\n- +9 tests (24 total), including the exact trailing-data regression.\n\n## [2.23.0] - 2026-07-14\n\n### Removed — dead modules and internal integrations\n\n- **`adk.swarm`, `adk.provisioning_tools`, `adk.session_sync_integration`, `adk.addon_metering`** — four orphan modules with zero public usage have been deleted. These were internal prototypes kept for legacy monorepo consumers; public packages have not imported them since 2.21.0 (tracked as D-348). **Breaking for anyone directly importing these modules** (import will fail). They are no longer exempt from the orphan-module check.\n- **`adk.aither_bridge`** — the AitherOS-internal IRC ↔ chat gateway bridge is no longer shipped in the public aither-adk package (stripped at sync time). All public importers already degrade behind `except ImportError` (in `adk.server` and `adk.builtin_tools`), so public packages remain unaffected. The module stays in the private AitherOS monorepo for internal use.\n- **Identity document trim** — removed internal agent names from `adk/identities/aither.yaml` delegation guidance, replacing with role-generic wording (\"delegate code review, refactoring, security analysis, performance and testing to specialist agents when available\"). Effort-tier routing philosophy remains unchanged and is product design, not a leak.\n\n### Changed — genericized customer/product names in help text\n\n- CLI help/examples no longer name specific customer deployments (`--scope` template list and `deploy agent` name example use generic placeholders). Functional deploy paths are unchanged.\n\n## [2.22.1] - 2026-07-14\n\n### Fixed — real bugs surfaced by reviving the public CI (red since Jul 5)\n\n- **TLS**: bundled toolpacks (arc-brainpack, cloudflare) disabled certificate\n verification on gateway/vault calls; all now route `adk._tls.tls_verify()`\n (verified by default, internal CA bundle when present).\n- **`system_prompt` contract**: bundled-pack `[PACK DIRECTIVES]` were appended\n even to an explicit `system_prompt`; an explicit prompt now wins wholesale.\n- **Headless secrets**: `_derive_key()` used `os.getlogin()`, which raises\n `OSError` without a controlling terminal (CI, daemons, containers) — falls\n back to `getpass.getuser()` (same value on interactive boxes, so existing\n secret files keep decrypting).\n- **`pack_verifier`**: missing `cryptography` crashed with `UnboundLocalError`\n in the exception handler instead of failing closed; now degrades gracefully\n (and `cryptography` was added to the dev extra so CI tests the real path).\n- **`fleet_manager._kill_pid` (POSIX)**: reported success immediately after an\n asynchronous SIGTERM and never reaped children (zombies read as alive); now\n polls, escalates to SIGKILL, and reaps.\n- **Identity**: `aither` regained the `creative` tool category (dropped\n accidentally in a 2.7.0 WIP sweep).\n\n## [2.22.0] - 2026-07-14\n\n### Changed — internal toolkit relocation\n\n- **`adk.platform` relocated** — the internal aither-platform toolkit (ComfyUI/image-gen\n pipelines, fleet agent clients, parallel CLI/UI/tools stack) has been moved out of the\n adk SDK tree to the standalone internal `aither-platform` package at\n `AitherOS/agents/aither_platform/`. The public package remains unaffected (it already\n excluded `adk.platform` since 2.21.1). Internal AitherOS agent builds now install\n `aither-platform` separately. `adk platform` falls through to the same \"not available\"\n message on public installs; internal builds work via the relocated package.\n\n### Removed — backward-compat re-export deleted\n\n- **`adk.gateway` module removed** — the backward-compatibility re-export of `GatewayClient`\n from `adk.gateway` has been removed. Use `from adk.client import GatewayClient` instead\n (the established public API since 2.21.0). All internal importers have been updated.\n\n## [2.21.1] - 2026-07-14\n\n### Removed — internal toolkit and license-gated product no longer ship publicly\n\n- **`adk.platform`** — the internal aither-platform toolkit (ComfyUI/image-gen\n pipelines, fleet agent clients, a parallel cli/ui/tools stack) is no longer\n included in the public repo or the PyPI wheel. It was internal-only code that\n had been riding along since the aither-platform merge. `adk platform` now\n reports \"Platform toolkit not available\" on public installs; internal AitherOS\n builds are unaffected.\n- **`adk.formbridge`** — the FormBridge form-automation product is license-gated\n (PROFESSIONAL+); its implementation no longer ships in the free package. All\n import sites (`builtin_tools`, `server` route mount) already degrade behind\n `except ImportError`, so nothing else changes behavior.\n- Product test suites (`test_formbridge.py`, `test_encounter_keying.py`,\n `test_flow_tools.py`) are excluded from the public sync alongside them.\n\n## [2.21.0] - 2026-07-14\n\n### Added — Completion gate: verified-or-retried task execution\n\n- **`adk.gate.CompletionGate`** — a composable wrapper that closes the \"silent\n success\" gap: an agent's ReAct loop returns whatever the model produced and\n calls it done (`finish_reason = stop/max_steps/length`), which is a *liveness*\n signal, not a *completion* one — so an agent can \"succeed\" while doing nothing.\n `CompletionGate` runs the agent, verifies the result against acceptance\n criteria, retries with the failure fed back, and returns the response with\n `finish_reason` set to `verified` or an honest `unverified`. Never a false pass.\n - **Hard-first, un-soft-passable checks** (`adk.gate.hard_checks`): a filesystem\n path a criterion says must exist is checked on disk; a required quoted token\n must appear in the output. Only genuinely subjective criteria fall through to\n an optional LLM judge (reuses the agent's own model), which **fails closed**\n if unavailable.\n - **`gated_run(agent, task, criteria=..., max_retries=2)`** — one-shot helper.\n - `auto_criteria` (LLM writes the machine-checkable definition-of-done from the\n task), a `verifier=` override for custom checks (tests-pass / HTTP probe), and\n a `requires_action` short-circuit so it never fights human-approval pauses.\n - Compose it onto any agent exposing `run(task) -> resp{.content, .finish_reason}`\n (`AitherAgent`, `core.Agent`, or a thin adapter over a remote dispatch).\n - Exported from the top-level package: `from adk import CompletionGate, gated_run`.\n\n## [2.19.0] - 2026-07-12\n\n### Added — Eve agent importer + durable A2A tasks + canonical agent discovery\n\n- **`adk pack import <eve_agent_path>`** — convert external Eve agents to AitherADK packs\n (from GitHub or local filesystem). Maps Eve structure (instructions → system prompt,\n agent.ts → agent.yaml, skills/*.md → skills, connections/*.ts → MCP, tools/*.ts →\n tools/node verbatim) to AitherADK format with a single command. Eve agents (compiled\n manifest format, schema v35) become installable packs compatible with `adk pack install`.\n- **Durable A2A task lifecycle (`TaskManager` backed by FileStore)** — agent-to-agent\n tasks now survive container restarts. Tasks persist as append-only JSONL\n (`.adk/tasks.jsonl`); in-memory dict for fast access; `create_task()` / `update_status()` /\n `add_artifact()` / `add_message()` all save to disk. Google A2A v0.3.0 spec compliant.\n- **Canonical agent card discovery** — `A2AServer.mount()` now exposes:\n - `GET /.well-known/agent-card.json` — Agent Card (canonical; contains agent identity,\n capabilities, skills, endpoints)\n - `GET /.well-known/agent.json` — Agent Card (legacy, redirects with 308 to canonical path)\n - `POST /a2a` — JSON-RPC 2.0 for message send/task lifecycle\n - `GET /a2a/tasks/{id}/subscribe` — SSE streaming task updates\n- **Four built-in agent packs now complete and catalogued**:\n - `hermes` — architecture review & trade-off analysis\n - `openclaw` — web research & source verification\n - `claude-code` — feature development & debugging\n - `analyst` — structured data inference & anomaly detection\n All discoverable via `adk packs` / `adk install pack:<name>`.\n- **Tolerant tool-call JSON parsing** — malformed tool JSON (extra properties, missing\n name/arguments, string-encoded nested args, missing keys) is now gracefully degraded\n instead of silently failing. `_parse_tool_json()` accepts both `\"arguments\"` and\n `\"parameters\"` keys, coerces string-encoded args, and skips unparseable calls instead\n of crashing the turn.\n\n### Known limitations\n\n- Eve agent importer: remote GitHub fetching not yet implemented (local paths only).\n- Durable task store: currently JSONL (suitable for thousands of records, not millions).\n\n## [2.18.0] - 2026-07-10\n\n### Added — AitherShell command center + Claude Code session management\n\n- **`aither sessions`** — Claude Code session manager: interactive full-screen\n browser (type-to-filter, transcript preview, deep full-text `search`, resume\n **in the current terminal** or as Windows Terminal tabs), crash guard\n (`guard install` = at-logon watchdog that snapshots live sessions and\n auto-restores the whole set after a terminal crash), and `ingest [--watch]`\n (incremental, secret-guarded sync of session conversations into the local KB\n / CompanyBrain via the standard ingest pipeline).\n- **`aither hq`** — command-center dashboard: per-service fleet health, LLM\n queue (depth/VRAM/models), Pulse alerts, live session count, inbox unread —\n auto-refreshing, with one-key jumps into chat, sessions, inbox, agents,\n brief, watchtower, and docker recovery.\n- **`aither inbox`** — unified queue over CommCore mail, Relay\n mentions/notifications, and Pulse alerts; open/mark-read/DM-reply from the\n terminal. Every source degrades independently.\n- **`aither agents`** — roster console + `agents ask -e <1-10> <agent> <q>`\n (effort-tiered ask via Genesis, with automatic `/chat/stream` fallback when\n the `/agent/sync` execute gate rejects host callers) + forge dispatch\n inspection + routine health.\n- **`aither palette`** — universal fuzzy picker across actions, sessions, and\n services; **`aither brief`** — renders Atlas's executive briefing in the\n terminal; **`aither watch`** — fleet watchtower with named wedge-signature\n detection (docker-WSL wedge, LLM-queue stall, crash-loop uptime regression)\n and optional docker auto-recovery.\n- All fleet reads go through a shared fail-soft `FleetClient` (internal-CA\n TLS via `adk._tls`, dashboard-grade timeouts, per-source degraded states).\n- Node shell-cli (`@aitherium/shell-cli` 1.13.0): `aither\n sessions|hq|inbox|palette|brief|watch|agents|docker` now pass through to the\n Python shell instead of being intent-classified as chat prompts.\n\n## [2.17.1] - 2026-07-05\n\n### Fully self-service mesh join — no customer key handling\n\n- **Auto-join headscale off the onboard response.** The conductor now auto-issues a\n headscale pre-auth key for NAT'd nodes and returns it in the `/v1/mesh/onboard`\n response; `mesh.join` consumes it and brings up the tunnel automatically. A customer\n runs one command — they never mint, fetch, or set a headscale key. Explicit\n `--headscale-key`/`AITHER_HEADSCALE_AUTH_KEY` still take precedence.\n- **Security:** `_tailscale_up` now redacts the auth key from all logs and exceptions\n (a failed `tailscale up` previously echoed the full argv, leaking the key into logs).\n\n## [2.17.0] - 2026-07-05\n\n### Added — Headscale mesh transport + local Qdrant + AitherConnect onboarding\n\n- **Headscale NAT-friendly mesh transport.** When raw WireGuard UDP:51820 is not\n viable (customer boxes behind NAT, corporate firewalls, CGNAT), `adk mesh join\n --headscale` routes the mesh tunnel through Tailscale's Headscale control plane.\n The overlay IP is still Conductor-assigned (10.77.0.0/16); Headscale provides\n only the transport layer. Configured via `AITHER_MESH_TRANSPORT=headscale`,\n `AITHER_HEADSCALE_URL` (default: https://headscale.aitherium.com), and\n `AITHER_HEADSCALE_AUTH_KEY` (pre-generated key). Automatic fallback to raw\n WireGuard if Headscale setup fails.\n- **Local Qdrant vector DB provisioning.** `adk stack` now provisions Qdrant\n locally when `AITHER_VECTOR_DB=qdrant` is set, enabling offline-first RAG\n without external dependencies. Tiered: local Qdrant in-fleet, cloud Nexus\n for enterprise multi-tenant deployments.\n- **Conductor URL public fallback.** `adk mesh join` resolves the internal\n `aitheros-conductor:8193` hostname; if unreachable (e.g., self-hosted nodes),\n it falls back to the public `conductor.aitherium.com:8193` endpoint,\n eliminating bootstrap configuration friction.\n\n### Documentation\n\n- **Self-hosting runbook.** New `docs/SELF_HOSTING.md` covers deploying AitherOS\n on customer infrastructure with Qdrant + mesh + AitherConnect onboarding.\n- **Mesh transport constraints documented.** Raw WireGuard requires public\n UDP:51820 endpoint (cloud instances, dedicated hardware); Headscale for\n NAT'd networks. Noted in CLI help and module docstrings.\n\n## [2.15.0] - 2026-07-04\n\n### Added — built-in web admin console + portal-profile settings sync\n\n- **Web admin console.** The page `adk up` opens (served at `/` and `/ui`) is now a full,\n self-contained console — Chat plus six admin tabs: **Backend** (view/switch LLM provider,\n set keys, test connection), **Packs** (list/enable/disable/reload tool packs),\n **Sessions** (browse/delete conversations), **Logs** (redacted agent-log tail),\n **Graph** (knowledge-graph search/neighborhood/stats), and **MCP** (add/remove external\n MCP servers). No build step, no CDN — a single packaged HTML asset. The original minimal\n streaming chat page stays at `/chat` for back-compat.\n- **Admin API (`adk/admin_api.py`).** ~20 endpoints under `/admin/*`, all bearer-gated by the\n existing middleware (never in `_skip_auth_paths`). Operates on the live agent — LLM backend\n swap (`LLMRouter.switch_backend`), pack reload, and MCP registration take effect without a\n restart.\n- **Settings sync (`adk/settings_sync.py`).** The user's portal profile is the source of\n truth: on startup the agent pulls `preferences.adk` and applies it over the local\n `~/.aither/config.yaml` cache; admin-console edits push a debounced snapshot back to\n `PUT /api/settings/preferences`. Provider API keys and MCP auth headers are device-local\n secrets and are never included in the pushed snapshot. Opt out with `AITHER_SETTINGS_SYNC=false`.\n\n### Security\n\n- **Timing-safe token comparison.** The server bearer check now uses `hmac.compare_digest`\n (the token is reachable over the public tunnel; a byte-wise `!=` leaked it to timing attacks).\n- **MCP add is SSRF-guarded + confirmed.** Adding an external MCP server is a two-step\n prepare→confirm flow; URLs resolving to loopback / private / link-local / metadata addresses\n are rejected (override with `AITHER_ALLOW_PRIVATE_MCP=1` for trusted local servers).\n- **Config editor is allowlisted.** `PATCH /admin/config` writes only safe scalar prefs and\n hard-denies fields that could execute code or redirect traffic at startup (e.g.\n `aither_toolpack_dirs`, backend base URLs).\n- **Log tail is scrubbed.** Bearer tokens, `#k=` fragments, and `sk-`/`aither_sk_` keys are\n redacted from the log-tail endpoint; the tunnel log is excluded.\n\n## [2.14.8] - 2026-07-04\n\n### Added — `adk up` for normal humans (no API key, no prereqs, live chat)\n\n- **Hosted-brain default — no API key required.** When there's no local model and no\n provider key, `adk up` signs in (device-flow) and routes inference through the gateway\n using the portal token (`AITHER_LLM_BACKEND=gateway` + bearer). A non-technical user\n never needs to obtain or paste an LLM API key. Explicit `--provider`/local model still\n take precedence; unattended `--yes` with no token/backend fails cleanly (exit 3).\n- **cloudflared auto-download.** If the tunnel binary is missing, `adk up` downloads the\n right platform asset to `~/.aither/bin` (SHA256-verified against the release; opt out with\n `AITHER_CLOUDFLARED_REQUIRE_CHECKSUM=0`) instead of exiting with an install hint.\n- **Built-in streaming chat page.** The agent now serves a self-contained chat UI at `/`\n (and `/chat`) with a live \"thinking…\" indicator and token-by-token streaming — so a human\n has somewhere to talk to it with immediate feedback instead of a blank wait. `adk up` opens\n it locally and passes the callback bearer in the URL fragment (`#k=…`), so `/chat/stream`\n stays authenticated (not an open proxy) with nothing to paste.\n- **Human-readable failures.** `adk up` errors now carry a plain-English `next_action`\n (kept machine-readable in `--yes` JSON).\n\n### Fixed\n\n- `adk up` chat link pointed at a non-existent `/settings/agent` route; it now points at the\n agent's own streaming chat page.\n\n## [2.14.7] - 2026-07-04\n\n### Added\n\n- **`adk up` — one command to run a persistent, fleet-connected agent.** Collapses\n the scattered onboarding surface (`host`/`quickstart`/`connect`/`login`/`setup`)\n into a single command that starts `aither-serve` (single agent, identity `aither`\n by default), opens a Cloudflare quick-tunnel for fleet reachability, registers with\n the portal, and installs a reboot-persistent autostart entry (Windows Task Scheduler\n / systemd-user / launchd). Detaches by default so the terminal is freed; `--foreground`\n blocks. Reuses the existing device-flow login, cloudflared discovery, provider-key\n store, and `_preflight_check` backend detection.\n- **Fully non-interactive path for autonomous agents.** `adk up --yes` (or any\n non-TTY invocation) takes ZERO prompts — everything resolves from flags, env, and\n saved config — and emits a single machine-readable JSON status line with meaningful\n exit codes (`0` ok, `2` bad-input, `3` no-backend, `4` tunnel-missing, `5` register-failed).\n Degrades to local-only (warn, not fail) when a portal token is absent unless\n `--require-register` is set.\n- **`adk down` / `adk status --json` companions.** `down` stops the agent + tunnel,\n best-effort deregisters, and removes the autostart entry; `status` reports the running\n agent (liveness + `/health` + tunnel + registration), with `--json` for agents/CI.\n- **`adk stack`** — the previous `adk up` behaviour (supervise the Room + Ollama consumer\n stack) now lives here; `adk up` is the connected-agent command.\n\n### Internal\n\n- New `adk/agent_daemon.py`: detached process spawn, status file\n (`~/.aither/adk-up.json`), pid liveness/teardown, and cross-platform autostart install.\n\n## [2.14.6] - 2026-07-04\n\n### Added\n\n- **`adk.preflight` — self-bootstrap capability probe.** `run_preflight(agent,\n spec)` wakes an agent up knowing what it is actually plugged into: a bounded\n (1.5s) parallel `LivenessProbe` over the router's resolved backends\n (primary/reasoning), the embeddings winner, remote MCP tools, the structured-ML\n / `/ml/teach` endpoints, and voice — returning an honest `CapabilityReport`.\n Status vocabulary distinguishes reachability from entitlement\n (`OK`/`MISSING`/`UNREACHABLE`/`AUTH`/`TIER_DENIED`/`UNSUPPORTED`), a hosted slot\n never prints a bare `OK`, and a required-but-unsatisfied slot aborts before the\n loop instead of blind-firing. Headless task/rules resolution + an allowed-roots\n policy binding (`builtin_tools.set_allowed_roots`, which resets the memoized\n cache so the override actually enforces).\n- **Multimodal (image) messages.** `Message.content` now accepts an OpenAI-style\n content-part list (`str | list[dict]`), passed through verbatim to the\n OpenAI-compatible body (gateway / vLLM / OpenAI). New `adk.llm.multimodal`\n helpers — `image_message()`, `image_content_parts()`, `to_image_url()` — build\n the parts and encode local image bytes/paths into `data:` URIs.\n- **Real vision preflight probe.** The vision slot is no longer unconditionally\n `UNSUPPORTED`: it sends a discriminable image (a solid red square) and requires\n the model to name the color, so a blind/text-only model cannot false-pass. `OK`\n only on a real round-trip; `AUTH` on 401/403; `UNSUPPORTED` when there is no\n OpenAI-compatible provider or the model cannot see the image.\n\n## [2.14.5] - 2026-07-04\n\n### Added\n\n- **`adk.reasoning.mcts` — generic Monte-Carlo Tree Search library.** A reusable\n MCTS engine (ported from the internal `UnifiedMCTS`, with all internal couplings\n removed) that any agent can drive over its own environment:\n - `MCTSEnvironment` protocol (`get_state_hash` / `get_actions` / `step` /\n `evaluate` / `clone`) — implement it and search.\n - Three **optional, default-off** model seams — `TransitionModel`, `PolicyModel`\n (PUCT priors), `ValueModel` — so a learned world-model / policy / value can be\n injected without touching the core; with all three `None` the engine is a plain\n UCT search.\n - `ObservedTransitionModel` adapter — a cheap online lookup world-model (hit =\n exact stored transition; miss = identity + negative \"unknown\" reward +\n `is_uncertain`, biasing exploration toward learning), with JSONL save/load for\n cross-run reuse.\n - `UnifiedMCTS(config).search(env)` returns an `MCTSResult` (best action, value,\n confidence, principal-variation path) and an optional `MCTSTrace`\n (visit-distribution policy target) for distillation.\n- **Self-bootstrapping agent machinery.** Build an agent from a declarative spec\n instead of bespoke wiring:\n - `adk.bootstrap.build_agent_from_spec(path) -> (AitherAgent, RunCtx)` — resolves\n prompts, activates required/optional tool packs, and carries a `memory_map`\n (a `node_type -> (role, tier)` override for memory writes, never a gate).\n - `adk.prompts.PromptBridge` — resolves an agent's prompt bindings from files\n (relative to the spec) or `AitherPrompts` dot-keys (optional import).\n - `adk.packs.PackActivator` — eager activation of installed tool packs by name\n over `register_tool_packs`; a required pack that registers zero tools raises\n `PackUnavailable`, optional packs soft-degrade.\n - `python -m adk.bootstrap.generic_agent <spec.yaml>` inspects a spec (resolved\n name, activated packs + tool counts, prompt keys, memory map) without running a\n live loop.\n- **`deep_research` tool pack** — a full multi-engine web-research workflow as\n `dr_*` agent tools (keyless search, page fetch + cache, site mirror, a citable\n findings knowledge graph, and markdown/PDF/DOCX report generation), activatable\n by name via the tool-pack loader.\n\n## [2.14.4] - 2026-07-04\n\n### Added\n\n- **`structured_ml` tool domain** — zero-shot structured-data inference for agents,\n backed by TabFM (tabular classification/regression) and TimesFM (time-series\n forecasting). Three tools:\n - `tabular_classify(support_rows, target, query_rows)` — classify rows from a\n labeled support set, in-context (no training step; up to 10 classes).\n - `tabular_regress(support_rows, target, query_rows)` — predict a numeric target.\n - `timeseries_forecast(series, horizon)` — forecast future values of a series.\n\n The tools POST to a structured-ML inference service resolved from\n `AITHER_STRUCTURED_ML_URL` (default `http://localhost:8192`), reject non-http(s)\n URLs, and summarise oversized responses to protect the agent's context.\n- **`Capability.STRUCTURED_INFERENCE`** capability token and a new **`analyst`**\n identity/pack that ships the `structured_ml` domain by default. The domain is\n opt-in — only identities that enable the `structured_ml` category get the tools.\n\n## [2.14.3] - 2026-07-03\n\n### Fixed\n\nAll four bugs below were found by actually booting `adk run` against a real\nchat model and driving a real tool-calling request end-to-end (not inferred\nfrom reading the code) — each one silently broke the \"local agent uses its\nconfigured model and its tools actually work\" path.\n\n- **`AITHER_MODEL` / `config.model` silently ignored by the OpenAI-compatible\n backend**: `LLMRouter.__init__` never read `config.model` into `self._model`\n unless the caller passed `model=` directly to the constructor — which nothing\n did. Every explicit model choice (env var, config file) was dropped, the\n provider fell back to a hardcoded per-backend default (`gpt-4o-mini` for the\n `openai` backend), and an unrecognized model name got silently remapped\n server-side to the wrong backing model. Concretely: setting\n `AITHER_MODEL=aither-orchestrator` had no effect — every request actually\n went to a different model that was never meant to be called directly and\n can't reliably use tools.\n- **`OPENAI_BASE_URL` ignored for explicit `--backend`/`AITHER_LLM_BACKEND`\n selection**: `LLMRouter._auto_detect()`'s explicit-backend branch hardcoded\n `base_url=None` when constructing the provider, instead of reading\n `config.openai_base_url` (which two other call sites in the same file\n already did correctly). Any self-hosted OpenAI-compatible endpoint\n (MicroScheduler, vLLM, LM Studio) configured via `OPENAI_BASE_URL` was\n silently ignored in favor of `https://api.openai.com/v1`.\n- **Tool-call steering never engaged on default-effort chat**: the retry that\n nudges a model back into structured tool-calling when it responds with prose\n instead of a function call was gated behind `effort >= 6`. Default `effort`\n for a plain `agent.chat(message)` call resolves to `5`, so the safety net —\n built specifically for this failure mode — never fired for default requests.\n Also now forces `tool_choice=\"required\"` on the retry instead of relying on a\n text nudge alone.\n- **Every non-string builtin-tool parameter silently typed as `\"string\"` in\n the generated tool schema**: `builtin_tools.py` uses\n `from __future__ import annotations`, so `_extract_parameters()` in\n `tools.py` was reading postponed (string) type hints (`\"int\"`, not `int`)\n straight out of `fn.__annotations__`, which never matched\n `_type_to_schema`'s type-object keys and silently fell through to\n `{\"type\": \"string\"}`. Every builtin tool with an `int`/`float`/`bool`/`list`\n parameter was affected — e.g. `file_read(start_line: int, end_line: int)`\n advertised both as strings, so a model correctly calling the tool with\n `\"start_line\": \"0\"` crashed the actual implementation with\n `unsupported operand type(s) for -: 'str' and 'int'`. Fixed by resolving\n hints through `typing.get_type_hints()` instead of the raw annotations dict.\n `_coerce_arguments()` — the runtime safety net that's supposed to coerce a\n model-supplied `\"0\"` string into `0` before a tool call executes — had the\n exact same raw-`__annotations__` bug and silently no-opped for every builtin\n tool; fixed the same way.\n- **Streaming tool-loop could hang a connection forever**: `chat_stream()`'s\n fallback to the sync tool loop (tool calls can't stream token-by-token) had\n no timeout, so a model that didn't cleanly terminate a tool-calling round\n trip left the SSE connection open indefinitely with zero bytes sent. Bounded\n with a 60s `asyncio.wait_for`; on timeout the stream yields a clear message\n instead of hanging.\n\n## [2.14.2] - 2026-07-03\n\n### Fixed\n- **AitherShell session amnesia**: the REPL generated a fresh `session_id` for\n EVERY message, so conversation history never followed across turns (the\n server stored each turn under a different session). One stable id is now\n minted per shell run; `/resume` and `/new` semantics unchanged, and the\n cross-restart auto-restore of the last session now actually works.\n\n## [2.14.1] - 2026-07-02\n\n### Added\n- **Account-tied licensing via device-flow login** — `adk login` now persists the account\n license returned by the device-flow handshake to `~/.aither/license.json` (base64\n `license_key` → `{payload, signature}` envelope), so entitlements from the authenticated\n account carry into every later `adk` invocation without re-auth. Pairs with the fleet-side\n node tool-pack gate that grants packs by plan-tier rank.\n- **`can_use_formbridge` / `can_use_untether` entitlements** (PROFESSIONAL+ rank ≥ 3) backing\n the FormBridge and UNTETHER agent tool-packs.\n\n### Fixed\n- `adk` CLI: guard the optional `memory_graph` singleton (was a latent `NameError` on the\n `/stats`, `/memory`, and save paths when memory was not constructed).\n\n## [2.14.0] - 2026-07-02\n\n### Added\n- **`adk.memory_wiki` — LyraWiki-style self-managed semantic knowledge** (opt-in; nothing constructs it by default): `MemoryWiki(graph_memory)` maintains curated wiki ARTICLES as `wiki_article` nodes (title/slug, LLM-owned markdown with `[[wikilinks]]`, embedding, durable tier, `CONSOLIDATED_FROM` edges to source memories, governance-ledgered revisions).\n - `consolidate(llm, since=, budget=)` clusters unconsolidated episodic/fact nodes (embedding + keyword), drafts/UPDATEs articles via the INJECTED llm callable (`str -> str` or `messages -> str`, sync or async — no hard model dependency), cites source node ids, marks contradicted sources superseded, then DEMOTES consolidated sources to a fast-decay tier via `promote()` (represented knowledge doesn't need raw copies).\n - `lint(llm=None)` — deterministic health checks (orphan wikilinks, empty/stale articles, live-but-contradicted pairs) + an optional LLM audit pass.\n - `prune(relevance_floor=0.05, hard_delete_after_days=14)` — article relevance = tier freshness × reinforcement (reinforce-on-recall) + link-degree to LIVE memories; below the floor → reversible governance tombstone; entombed past the window → **HARD-DELETE** (the tombstone snapshot itself is purged — content and embedding irrecoverable; the true-deletion path). A node is never deleted without a tombstone first.\n - `recall(query, limit)` — article-first RAG (curated articles rank above raw nodes) that reinforces returned articles.\n- **`adk.routines` — agent heartbeat + self-programmed routines**: `RoutineStore` is a durable registry (`~/.aither/routines/{agent}.json`, `AITHER_DATA_DIR`-aware, injectable path) of cron-scheduled self-prompts `{name, cron, instruction, enabled, last_run, last_result, tags}` that rehydrates into the existing `adk.cron.CronScheduler` on `start()`. A fire runs `agent.chat(instruction)` with the agent's full toolset — the agent programs its future self with its own adk. Guardrails: `max_routines` (12), a 5-minute min fire interval per routine, a per-fire timeout, and every result ledgered (truncated) to `last_result`. Direct-callable routines (`register_direct`) fire a bound method instead of a self-prompt. Self-management tools (`routine_create/list/update/pause/resume/delete/run_now`) ship as OpenAI tool defs + handlers (`build_routine_tools` / `register_routine_tools` / `routine_tool_defs`); the handlers ONLY touch the RoutineStore — they can never modify agent config or safety-relevant settings (the leash principle).\n- **`AitherAgent(routines=True, memory_maintenance=True)`** (both default **False** → the default agent is byte-identical): `routines=True` builds the RoutineStore + scheduler, registers the self-management tools, and starts the heartbeat lazily on the first `chat()` (or via explicit `start_routines()`). `memory_maintenance=True` registers DEFAULT routines — `wiki_consolidate` (every 2 h), `wiki_lint` / `wiki_prune` / `graph_sweep` (daily) — as DIRECT method fires so memory upkeep runs even on tiny models, while staying visible/manageable via `routine_list`.\n- `TombstoneStore.purge(tombstone_id)` — the hard-delete endpoint: irrecoverably removes a tombstone snapshot and rewrites the persisted store without it (consumed by `memory_wiki.prune`).\n\n## [2.13.6] - 2026-07-02\n\n### Added\n- **Qdrant backend for GraphMemory dataplane sync — TRUE two-way, verified live.** Set `AITHER_FLEET_QDRANT_URL` and the sync uses Qdrant (`upsert` + `scroll` + filtered `search`) instead of Nexus. This is what actually delivers cross-agent memory sharing: a node is upserted with its OWN embedding under a deterministic per-(tenant,node) point id (idempotent re-push), and `fleet_pull` reliably SCROLLS a tenant's points back — the enumerate Nexus lacks (its `/search` doesn't return ingested docs and `/export` needs lancedb, which has a hard dependency conflict in the fleet). **Proven live with 2 agents:** agent-1 ingests → auto-syncs to the tenant dataplane; a fresh agent-2 in the same tenant `fleet_pull`s all of agent-1's nodes and can search them; a different tenant pulls nothing (isolation). Dimension-safe (never mixes vector dims in a collection). Local SQLite stays source of truth; best-effort, never raises.\n\n## [2.13.5] - 2026-07-02\n\n### Fixed\n- **CRITICAL — GraphMemory dataplane sync push was 100% failing.** `_fleet_push_node` sent `source_type=\"graph\"` / `content_type=\"graph_node\"`, which AitherNexus rejects as invalid enums (HTTP 500) — so NO graph node ever replicated in 2.13.4. Now sends the accepted `source_type=\"manual\"` / `content_type=\"text\"` (graph provenance is carried in `metadata.synced_from`). **Verified live** against a fleet Nexus: a 4-node ingest replicates with `pending==0` and the collection count reflects it.\n\n### Added\n- **Swarm-awareness notification** — on a successful sync batch, `GraphMemory` emits a best-effort `graph.synced` event (tenant/workspace/agent/collection/count) to `AITHER_FLEET_EVENTS_URL` so OTHER agents in the tenant/swarm can pull the fresh data and deconflict in-flight work (push-based awareness instead of polling). No-op unless the env is set.\n\n### Known limitation\n- Cross-agent **rehydration** (`fleet_pull`) is only reliable when the fleet Nexus is backed by lancedb (persistent + exportable). An in-memory Nexus stores on `/ingest` but neither its `/search` nor `/export` returns the docs — two-way read needs the persistent Nexus.\n\n## [2.13.4] - 2026-07-02\n\n### Added\n- **Memory-maintenance primitives on `GraphMemory`** (all opt-in; default behaviour byte-identical):\n - **Reinforce-on-recall**: `recall_with_activation(..., reinforce=True)` bumps `reinforcement_count`/`last_reinforced` on every RETURNED node's metadata mirror (one UPDATE per node, WAL-safe) — making the `MemoryRecord` reinforcement fields the activation scorer reads LIVE. Default `False` = zero writes.\n - **`sweep(now, archive_below=0.05, max_nodes=None, dry_run=False)`**: read-time decay enforced at rest — archives nodes past their tier TTL with freshness×reinforcement score below `archive_below` and `reinforcement_count <= 1`; `max_nodes` additionally archives the lowest-scored overflow. PERMANENT tier immune. Every archive is a reversible governance tombstone (`TombstoneStore.recover`) + a FORGET ledger entry; a node is never deleted without a tombstone. `dry_run` returns the would-archive list without acting.\n - **`promote(node_id, tier=?, role=?)`**: re-tier/re-role a node IN PLACE (tier/role columns + metadata mirror) — edges, embedding and ledger history survive; ledgers an UPDATE entry when governance is on.\n- `MutationType.UPDATE` ledger entry kind (in-place re-tier/re-role).\n- **Per-tenant dataplane sync for `GraphMemory`** — replicate the local graph to a per-tenant vector dataplane (Nexus/Qdrant) so a sovereign agent's memory is durable + rehydratable across restarts and hosting boundaries. `fleet_push_all_nodes()` (catch-up), `fleet_pull()` (best-effort semantic top-up), `fleet_sync_pending()`, and auto-sync on ingest (`_maybe_autosync` + `drain_sync()`). **On by default** (`AITHER_FLEET_SYNC=auto`; only `false/0/off` disables) with an inferred target (local Nexus `:8122` or the gateway in cloud mode) and a SEPARATE collection (`AITHER_FLEET_GRAPH_COLLECTION`, default `graph_memory`) from the KV memory sync. New `GraphMemory(tenant_id=, workspace_id=, fleet_url=, auto_sync=)` constructor args override env (required for multi-tenant host processes). Node `synced` column + schema v4 migration. TLS verify is CA-bundle-aware (`AITHER_CA_BUNDLE`) for the internal-TLS mesh. Local SQLite stays the source of truth; every sync path is best-effort and never raises into ingest.\n\n## [2.13.3] - 2026-07-02\n\n### Added\n- **Canonical self-deploying embeddings provider** (`adk.embeddings`): one embedding provider for the whole SDK so vectors are portable across scopes (platform / tenant / sovereign) — same model + dimension everywhere. Pinned to `nomic-embed-text` (768-d; the vLLM `--served-model-name` for nomic-embed-text-v1.5). Lazy, single-flight resolution chain: explicit `AITHER_EMBEDDINGS_URL` → local vLLM (`:8209` then `:8120`, HTTPS-then-HTTP) → local Ollama → gateway (`AITHER_GATEWAY_EMBEDDINGS_URL`) → auto-deploy a local vLLM embeddings container if a GPU + Docker are present (opt out with `AITHER_EMBED_AUTODEPLOY=0`) → CPU sentence-transformers (384-d, degraded) → feature-hash (384-d, degraded, always works). Every batch is dimension-tagged so callers never silently mix 768-d and 384-d vectors. `get_default_embedder()`, `embed_texts()`, `embed_one()`, `get_provider()`, `reset_provider()`.\n\n### Changed\n- **`GraphMemory` now defaults its embedder to the canonical provider** (opt out with `AITHER_GRAPH_EMBEDDER=legacy`), so every adk agent shares one 768-d space. Added a meta-table dimension guard: the index is pinned to its first embedding's dimension and later different-dimension vectors are refused (kills 768↔384 mixing that silently poisons cosine similarity).\n\n## [2.13.2] - 2026-07-01\n\n### Added\n- **Session persistence**: AitherShell now auto-saves the active session_id to `~/.aither/config.yaml` after each chat turn and auto-restores it on the next shell launch. Multi-turn conversations survive across restarts — the messages were always persisted in SQLite + JSON, only the session pointer was missing.\n- `/new` command to start a fresh session (clears saved session_id).\n- `/sessions` command to list recent sessions with agent name, message count, and timestamp. Resume any with `/resume <id>`.\n\n## [2.13.1] - 2026-07-01\n\n### Fixed\n- **CRITICAL:** `deploy_connect()` now sends a HEAD request to verify the GitHub release asset exists before downloading, with a clear error if the asset is missing.\n- **CRITICAL:** Compose file URLs are now pinned to a stable commit hash (configurable via `AITHER_COMPOSE_PIN` env) instead of tracking the mutable `main` branch.\n- **CRITICAL:** `deploy_adk_node()` now falls back to starting `adk-serve` natively when Docker is unavailable, instead of blocking with \"Docker not installed\".\n- **HIGH:** GHCR login failure now blocks deployment immediately with actionable guidance, instead of warning and continuing to a confusing image-pull failure.\n- **HIGH:** Health checks now stream the last 30 lines of Docker Compose logs on failure, so the user can see why a container didn't start.\n- **HIGH:** vLLM port allocation now probes for conflicts before assigning; if port 8200 is busy, workers are automatically remapped to the next free port.\n- **MEDIUM:** Compose file downloads are now cached with ETag; re-running `adk deploy node` skips re-downloading an unchanged file.\n- **MEDIUM:** DGX Spark discovery now respects `AITHER_DGX_HOSTS` env (comma-separated) for custom network topologies, instead of only checking `spark.local` and `192.168.0.33`.\n- **MEDIUM:** Cloud API keys (Anthropic, OpenAI, DeepSeek) are now validated with a lightweight probe during `adk setup` infra scan, so invalid keys surface early.\n- **MEDIUM:** `deploy_connect()` now validates the extracted extension has a `manifest.json` and warns if it's missing.\n\n## [2.13.0] - 2026-07-01\n\n### Removed\n- **`adk.faculties.memory_graph`** — the pickle-based `MemoryGraph` module is deleted. `from adk.faculties import MemoryGraph` now resolves to the canonical `adk.graph_memory.GraphMemory` for back-compat.\n- **`adk.platform.memory`** — the entire legacy memory subsystem (`MemoryManager`, `UnifiedMemorySystem`, `GameEngine`, `StoryboardEngine`, `AnchorGenerator`) is deleted. Agent memory is `adk.memory.Memory` + `adk.graph_memory.GraphMemory`.\n\n### Changed\n- `adk work` command now uses `GraphMemory` instead of the removed pickle-based faculty graph.\n- `adk.MemoryGraph` and `adk.faculties.MemoryGraph` lazy exports now resolve to `adk.graph_memory.GraphMemory`.\n- `agent.set_memory_graph()` kept for back-compat but documented as deprecated; agents already wire `GraphMemory` automatically in `__init__`.\n- Platform startup (`adk.platform.infrastructure.startup`) gracefully handles missing `MemoryManager`.\n- Platform CLI module listing and health check updated to reflect removed memory module.\n\n## [2.12.7] - 2026-07-01\n\n### Deprecated\n- `adk.faculties.MemoryGraph` — emits `DeprecationWarning` on instantiation; use `adk.graph_memory.GraphMemory` (SQLite-backed, governed, embedding-native) instead.\n- `adk.platform.memory` package — the legacy `MemoryManager`, `UnifiedMemorySystem`, `GameEngine`, and `StoryboardEngine` modules now emit `DeprecationWarning` on import; use `adk.memory.Memory` and `adk.graph_memory.GraphMemory` for new code.\n\n### Changed\n- Updated `adk.faculties` docstring and usage examples to point at the canonical memory surface.\n\n## [2.12.6] - 2026-07-01\n\n### Changed\n- Trimmed public package debt by removing grid runbook artifacts and shell/room binary placeholder directories from published artifacts.\n- Removed stale public-sync allowlist entries for deleted phase test files and added explicit public-sync guards for binary placeholder directories and grid artifacts.\n- Updated the public README grid note so it no longer links to runbook files excluded from the SDK package.\n\n## [2.12.5] - 2026-07-01\n\n### Added\n- Added best-effort fleet memory sync from local ADK memory into Nexus/Qdrant-compatible RAG stores, including batch catch-up and vector-search recall hooks.\n- Added saved config/env support for fleet memory URL, collection, and sync mode.\n\n### Changed\n- Excluded FormBridge runtime/test internals from public ADK packaging and public repo sync.\n\n## [2.12.4] - 2026-07-01\n\n### Added\n- Added a hot-reload endpoint for installed agent packs so newly applied pack tools can be picked up without restarting `aither-serve`.\n\n### Fixed\n- Fixed synchronous `ChatRelay` calls on Python 3.12 when no event loop is running; async WebSocket delivery is still scheduled when a loop exists.\n- Aligned gateway inference tier tests with the current public ladder: free, starter, pro, enterprise, platform.\n\n## [2.12.0] - 2026-06-28\n\n### Added — one-command local inference + upgrade path (click-to-run)\n- **`adk quickstart-local`** — detects hardware, picks a backend\n (Ollama / llama.cpp / vLLM), installs it, downloads a model, verifies, and\n prints next steps. Bootstraps Ollama itself via winget/brew/install.sh when\n missing (no \"go install it\" dead-ends). Default model `gemma4:e2b`.\n- **`adk backend switch <ollama|llamacpp|vllm>`** (+ `backend status`) — migrate\n the active deployment between engines; re-points config and smoke-tests.\n- **`adk install pack:<name>`** (+ `adk install list` / `adk packs`) — install\n ready-made agent packs (openclaw, hermes, claude-code) into `~/.aither/agents`.\n\n### Fixed (found via live end-to-end testing on Windows + CUDA)\n- quickstart-local reported success on a dead endpoint (smoke test was warn-only);\n a failed smoke now fails the command, with a service-readiness wait.\n- `--model` resolved to `None` (getattr default never fired) → uses the real default.\n- Smoke test: 30s→180s timeout for cold model load; accepts reasoning-model output\n (empty `content` at low token budgets is no longer a false failure).\n- `adk install pack:<name>` colon syntax (argparse rejected it before dispatch).\n- `adk backend status` health probe used a nonexistent `/health` path → 404; now\n probes `/v1/models` (uniform OpenAI-compatible liveness for all three backends).\n- Windows: removed non-ASCII glyphs from printed strings that crashed the cp1252\n console (a `✓` even made a successful install report failure).\n\n## [2.11.1] - 2026-06-14\n\n### Fixed — `adk host` / `adk login` device flow (autonomous onboarding)\nFound driving `adk host` against production; all broke real device-flow login:\n- **Device flow hit the portal FRONTEND, not AitherIdentity.** `adk login`/`adk host`\n POSTed `/auth/device/code` to portal/veil.aitherium.com, which 307-redirects API auth\n to `/login`. Added `_resolve_identity_url()` mapping the aitherium.com topology to\n `idp.aitherium.com` (the Identity API); localhost/bespoke pass through unchanged.\n- **Cloudflare 403'd urllib.** `idp.*` sits behind Cloudflare, which blocks urllib's\n default `Python-urllib/3.x` UA. Device-code + poll requests now send a real\n User-Agent + Accept (verified urllib→idp 200, was 403).\n- **`adk host` now self-authenticates via device flow** when there's no token, and\n **retries via device flow on a 401/403** (a stale saved token no longer dead-ends\n registration).\n\n## [2.11.0] - 2026-06-14\n\n### Added — attach your own MCP \"hands\" to a self-hosted agent\nA self-hosted agent now has the full brain (your model) · body (this loop) · **hands**\n(your tools) trifecta. The AitherOS platform relays the MCP servers you registered for\nyour agent in the `/stream` request body as `mcp_endpoints`, and the loop wires their\ntools into the ReAct turn:\n- **`adk/mcp_endpoint_tools.register_mcp_endpoint_tools(agent, endpoints)`** — speaks MCP\n JSON-RPC (`tools/list` to discover, `tools/call` to invoke) and registers a namespaced\n proxy tool (`{endpoint}__{tool}`) for each advertised tool. Mirrors `app_proxy_tools`.\n Best-effort: an unreachable endpoint registers nothing and never raises.\n- **`POST /stream` and `POST /chat/stream`** accept an optional `mcp_endpoints` list\n (`[{name, url, [headers]}]`); `_aitheros_stream` attaches them to the agent before the\n turn. Existing callers are unaffected (the field is optional).\n\n## [2.10.1] - 2026-06-14\n\n### Fixed — self-hosted `aither-serve` was crash-on-startup (2.10.0 regression)\nA fresh `pip install aither-adk && aither-serve --backend deepseek` could not boot. Found\nand fixed while standing up a real self-hosted DeepSeek agent end-to-end:\n- **Startup `NameError: name 'port' is not defined`** in three lifespan helpers\n (`_join_aithernet`, `_init_a2a_server`, `_start_mesh_hosting`) — they referenced a bare\n `port` no longer in scope. Now use `config.server_port`, and `main()` writes the resolved\n `--port`/`--host` back to `config` so every lifespan helper and the invoke_url see the\n actual bound port.\n- **Fleet self-registration crashed the whole server** — the registration handlers caught\n `(ImportError, RuntimeError, OSError, ConnectionError)` but **not** `httpx.HTTPError`, so a\n `RemoteProtocolError`/connect error talking to the control plane took the agent down. All\n network-facing lifespan handlers now also catch `httpx.HTTPError` (non-fatal, as intended).\n- **`--backend` was ignored when `AITHER_API_KEY` was set** — `_auto_detect` picked the cloud\n gateway before the explicit backend, and `_try_elysium_fallback` then replaced the chosen\n provider. An explicit backend (deepseek/openai/anthropic/your own vllm/ollama) now always\n wins; \"auto\"/\"gateway\" still flow through detection. The operator's own brain is never\n silently hijacked.\n- **Approved-tool trace truncated after `/sessions/{id}/confirm`** — the SSE relay treated\n `AgentResponse.tool_calls_made` (a `list[str]`) as dicts (`tc.get(...)`), raised\n `AttributeError`, and an over-narrow `except` swallowed it so the client saw only\n `session_start` + heartbeats. The relay now handles string tool names, the `except` is\n broad (always emits a typed `error` + terminal `complete`), and the module-level\n `_strata_ingest` `NameError` + Chronicle `session_id` kwarg mismatch are fixed.\n\n## [2.10.0] - 2026-06-13\n\n### Added — human-in-the-loop tool approval (`adk/approval.py`)\n- **Pause before a gated tool, resume on decision.** An agent can now pause its turn before\n executing a sensitive tool and wait for a human's allow/deny — so a managed control plane\n (or any operator) can gate tool use without forking the loop.\n- **Policy via `AITHER_TOOL_APPROVAL`** — a comma-separated list of tool names (optionally\n `agent:tool`), or `*` for every tool. Empty = no gating (unchanged behavior).\n- **Survives restarts / days-later approvals.** The paused turn (user message + pending tool\n calls) is persisted to disk keyed by `session_id`; resume is a fresh request, not a held\n connection. Decisions are recorded per `(session_id, tool_name)`.\n- **`AgentResponse.requires_action` / `.pending`** — set when a turn paused; `pending` lists\n the gated tool calls awaiting a decision.\n- **`AitherAgent.resume(session_id, decisions)`** — records the decisions and re-runs the\n paused turn (the gate consumes them: allowed tools execute, denied tools get a denial\n observation, the turn continues — and may pause again on a different gated tool).\n- **Server:** `POST /sessions/{id}/confirm` records decisions and streams the resumed turn;\n `/stream` emits a `requires_action` event when a turn pauses.\n\n## [2.9.0] - 2026-06-13\n\n### Added — universal LLM continuation primitive (`adk/llm/continuation.py`)\n- **`run_continuation()` + `stitch()`** — one shared continue-until-complete primitive. When a\n completion stops because it hit the output token cap (`finish_reason == \"length\"`), it continues\n the generation and STITCHES the chunks (overlap-dedup + restart-detect) into a complete answer,\n instead of every call-site hand-rolling its own retry. Bounded three ways (max rounds AND total\n output ceiling AND a no-growth break), and it never mutates the caller's message history.\n- **`LLMProvider.chat_with_continuation()`** and **`LLMRouter.chat_with_continuation()`** — every\n provider AND the router inherit the primitive, so any call-site gets continuation for free.\n- Kill-switch `ADK_LLM_CONTINUATION=off`; tunables `ADK_LLM_MAX_CONTINUATIONS`,\n `ADK_LLM_MAX_TOTAL_OUTPUT_TOKENS` (defaults: 2 rounds / 8192 tokens).\n\n### Changed\n- The ReAct loop's truncation recovery now uses the shared primitive. The previous inline\n doubling-retry REPLACED the partial response (losing earlier text in `resp.content`) and injected\n continuation scaffolding into the live message history; it now STITCHES and leaves history clean.\n Tool-call turns are never continued as text.\n\n### Included — previously-committed-but-unreleased changes\n- 2.8.2: TLS verification on by default (`verify=False` removed).\n- 2.8.1: baked license + pack-signing public keys.\n- 2.8.0: version-bump release.\n\n## [2.7.0] - 2026-06-05\n\n### Added — provider-agnostic LLM cost levers (at the `LLMProvider` ABC)\n- **Prompt caching.** New `ProviderCapabilities` (`prompt_cache: explicit|automatic|none`, `batch`) and\n normalized `LLMResponse.cache_read_tokens`/`cache_write_tokens`, so the \"tokens saved\" meter never\n depends on which backend served the turn. Anthropic inserts `cache_control` breakpoints over the stable\n system+tools prefix; OpenAI/DeepSeek caching is automatic and their `cached_tokens` are now surfaced\n (previously discarded). Default-on in `AitherAgent.chat`; override with `cache=False`.\n- **ResponseCache** (`adk/llm/cache.py`) — exact-match response cache with a pluggable backend, wired into\n `LLMRouter` as an opt-in (`LLMRouter(response_cache=…)` + per-call `cacheable=True`). OFF by default — no\n determinism change unless opted in.\n- **Gemini provider** (`adk/llm/gemini.py`) with explicit `cachedContents` caching; registered in the router.\n- **Async batch runner** (`adk/llm/batch_runner.py`) — `run_batch()` over the Anthropic Message Batches /\n OpenAI Batch APIs (~50% off) with a transparent fallback to concurrent `chat()` where unsupported.\n- **Graph-RAG retriever interface** (`adk/graph_retriever.py`).\n\n### Added — principal authority (prompt-injection defense enforced outside the model)\n- `adk/auth.py`: `Principal` / `AuthContext` / `PrincipalResolver` (+ deny-all default).\n- `ToolRegistry.execute(name, args, auth=…)` — a default-deny authorization gate at the tool boundary, so a\n forbidden tool call is blocked regardless of what the LLM emits. `ToolDef` gains `required_clearance` /\n `action_class`. Threaded through `AitherAgent.chat`; `auth=None` is fully backward-compatible.\n- `adk/channel_auth.py`: Telegram webhook-secret + CEO-id checks and HMAC-signed approvals.\n\n## [2.6.4] - 2026-06-05\n\n### Fixes\n- **Loop-guard nudges corrupted the message history → DeepSeek 400 mid-run.**\n When the ReAct loop emitted multiple tool calls in one assistant turn and the\n loop guard fired a WARN/BLOCK/CIRCUIT_BREAK on one of them, it spliced a\n standalone `system` nudge message *between* the tool results. DeepSeek (and\n strict vLLM chat templates) require an assistant `tool_calls` message to be\n followed by exactly one `tool` result per `tool_call_id`, contiguously — so the\n interleaved message produced `400 … insufficient tool messages following\n tool_calls message` and aborted the turn (this is reliably hit after crossing\n the 15-call circuit-break threshold, where every subsequent call WARNs).\n Per-call guidance is now folded into that call's own `tool` result, and any\n standalone steering is deferred to a single message AFTER the whole batch —\n preserving the `tool_calls`↔`tool_results` pairing every backend requires.\n Regression test: `deep-research-agent/tests/test_msg_structure.py`\n (loopguard-flood scenario).\n\n## [2.6.3] - 2026-06-04\n\n### Fixes\n- **Transient provider errors killed a run on the first blip.** A single\n `502 Bad Gateway` / `503` / `529 overloaded` / `429` from the upstream API\n aborted the whole request — the Anthropic provider had **no retry at all**, and\n the OpenAI-compatible provider only retried `429`. Both now retry transient\n statuses (`408/429/500/502/503/504/529`) with `Retry-After`-aware exponential\n backoff (3 attempts), and surface the provider's body if all retries are\n exhausted. Non-transient `4xx` (e.g. `400`) still fails fast. Applies to\n `chat` and `chat_stream` on both providers.\n\n## [2.6.2] - 2026-06-04\n\n### Fixes\n- **DeepSeek (and strict OpenAI-compatible backends) 400'd on long tool\n conversations.** The ReAct loop legitimately injects mid-conversation\n `system` steering messages (the \"[DIMINISHING RETURNS]\" hint after several\n low-output tool iterations, loop-guard nudges). OpenAI tolerates a non-leading\n `system` message; DeepSeek rejects it with a 400. Long research runs\n (many `fetch_url` calls) reliably tripped this. Fix: non-leading `system`\n messages are demoted to `user` at the OpenAI-compatible provider boundary\n (`_demote_nonleading_system`) — steering intent preserved, payload valid on\n every backend; leading system prompt(s) untouched.\n- **Opaque HTTP errors.** `resp.raise_for_status()` discarded the provider's\n response body, so a 400/422 surfaced as a bare `Client error '400 Bad\n Request'` with no cause. `_ensure_ok` now includes the provider's actual\n message (e.g. `context_length_exceeded`, `Invalid 'messages'`) in the raised\n error, for both `chat` and `chat_stream`.\n\n## [2.6.1] - 2026-06-04\n\n### Fixes\n- **`MCPServer.mount()` returned HTTP 422 for every `/mcp` call.** Under\n `from __future__ import annotations` the route handler's `request: Request`\n annotation is a string that FastAPI resolves against the *module* globals, but\n `Request` was imported locally inside `mount()` — so FastAPI couldn't resolve\n it and treated `request` as a required query param. The MCP server endpoint\n (`initialize`, `tools/list`, `tools/call`) was unusable when mounted. FastAPI\n symbols are now imported at module level (guarded; the `[node]` extra still\n gates actual use). Any agent's tools can now be exposed as a working MCP\n server with `MCPServer(...).mount(app)`.\n\n## [2.6.0] - 2026-06-04\n\n### Streaming + scaffolding\n- **`AitherAgent.stream_chat`** — reliable streaming sibling of `chat()`: native\n tool-calling loop (concludes on its own, unlike the text-protocol `stream_react`)\n that emits live `tool`/`tool_result` events and streams the answer as `token`\n events. `token_delay` paces tokens for a typing effect.\n- **`adk new <template>`** — scaffold a full, runnable template app. Ships the\n `deep-research` template (a cited web-research agent: pack + server + web UI):\n `adk new deep-research`.\n\n### Fixes\n- **BYO-key no longer community-capped.** The free-tier monthly token cap now applies\n only to the metered Aitherium gateway backend, never to a user's own provider key\n (Anthropic/OpenAI/DeepSeek/Ollama/vLLM). Previously it could brick a self-hosted\n agent after ~100k tokens on the user's own key.\n- **Stronger, grounded memory recall.** `chat()` now injects the top-6 knowledge-graph\n hits (untruncated) with a hard no-fabrication instruction — was top-3 truncated to\n 200 chars, which surfaced partial facts and let weaker models invent the rest.\n- **Tool-arg coercion.** `ToolRegistry.execute` coerces LLM-supplied args toward each\n parameter's annotated type, so a tool no longer crashes when a model passes a list\n or string where an int/float/bool was expected.\n\n### Docs\n- New `docs/AGENT_DEV_GUIDE.md` — the golden path for building agents/packs.\n\n## [2.5.0] - 2026-06-04\n\n### Continual learning — agents that learn from runs and reuse memory\n\nAgents now improve the second time they do a task instead of starting from scratch.\n\n### Added\n- **Recall-before**: `AitherAgent.chat()` searches the local `SkillStore` and injects the\n top matching learned skills as a system block, so the model reuses proven procedures.\n- **Learn-after**: `_learn_after()` runs after each response — reinforces (`success_count++`)\n any skills that were recalled+reused, and extracts + saves a NEW skill from successful\n multi-tool runs via `SkillExtractor`. Wired at both `chat()` return paths.\n- Gated by `AITHER_SKILLS` (default on). The previously-unused `SkillStore`/`SkillExtractor`\n are now connected to the run loop.\n\n### Notes\n- Pairs with the platform-side LearnedProcedure substrate (recipes → scored procedures →\n skills/tools/A2A/packs → MCTS priors → evolution). The SDK shares memory via the Spirit\n bridge when configured.\n\n## [2.0.0] - 2026-06-02\n\n### Open-core boundary (BREAKING)\n\nThis release draws a single, enforced free/paid line. The free tier stays\ngenuinely useful (real agent, typed + graph + code memory, ReAct, ~essential\ntools); paid tiers unlock the *scale* capabilities via portal.aitherium.com.\n\n### Added\n- **`adk/licensing.py`** — the entitlement keystone. Resolves a tier\n (COMMUNITY by default), verifies optional portal-signed `~/.aither/license.json`\n (Ed25519), and answers `can_use_fleet/channels/cron/swarm/auto_neurons`,\n `is_agent_licensed`, `max_effort`, etc. **Fail-closed**: no/invalid/expired\n license → free tier; an unsigned license never grants premium.\n `AITHER_TENANT_SLUG=aitherium` → unrestricted INTERNAL tier.\n- **Moat guard** (`scripts/check_moat_boundary.py`) wired into the publish\n workflow — blocks any wheel that leaks the moat or drops the keystone.\n- Tests: `tests/test_licensing.py`, `tests/test_moat_boundary.py`.\n\n### Changed (gating — fail-closed)\n- Free tier effort is capped at 3 (reasoning effort 7–10 needs Professional).\n- Monthly token budget hard-block on the free tier (no silent overspend).\n- Gated behind a paid tier: fleet mode (`FleetConfig`), cross-agent delegation\n (`AgentForge.delegate`), channel adapters, cron scheduler, proactive\n auto-neurons, and `swarm`/`swarm_code` dispatch.\n\n### Removed\n- **`adk/nanogpt.py`** (on-device training IP) removed from the published SDK\n and the public repo; relocated to the internal AitherOS moat. Excluded from\n the wheel as defense-in-depth.\n- Pre-2.0 releases shipped this IP and ungated capabilities and are being\n yanked (PyPI) / removed (GitHub releases). See `scripts/yank_leaky_releases.py`\n and `scripts/purge_public_leaks.sh`.\n\n## [1.20.0] - 2026-05-27\n\n### Fixed\n- **Pack discovery path mismatch** — `_find_local_packs()` now scans both\n `~/.aither/packs/` and `~/.aitheros/packs/`, so packs installed via\n `adk pack install` are discovered by `adk-workspace`.\n\n### Added\n- **Auto-register on pack install** — `adk pack install` now auto-registers\n with the portal if the pack contains an `agent.yaml` with a `portal` section.\n One command does download + extract + register.\n- **Flux event on agent registration** — `developer_portal.py` emits\n `agent.registered` Flux event so the portal fleet UI updates in real-time.\n\n## [1.15.0] - 2026-05-26\n\n### Added\n- **Cloud memory** — `adk quickstart --cloud` now tests gateway memory and\n auto-configures Spirit URL so memories persist across devices via\n `gateway.aitherium.com/v1/memory/teach` and `/v1/memory/recall`.\n- **Config→env export for cloud mode** — `Config.from_env()` now exports\n `AITHER_CLOUD_MODE`, `AITHER_SPIRIT_URL`, and spirit path env vars from\n saved config so Memory and other modules pick them up without needing\n explicit env vars set by the user.\n- **Cloud Quick Start section** in README — 3-command setup for users who\n just want a Claude/GPT-4/DeepSeek API key and the full agent harness.\n- **`--memory` profile for node deployment** — `adk deploy node --memory`\n adds Spirit (8087) + WorkingMemory (8101) containers for persistent\n vector memory. Without this, agents use local SQLite only.\n- **Configurable vLLM in node compose** — `AITHER_VLLM_MODEL`,\n `AITHER_VLLM_GPU_UTIL`, `AITHER_VLLM_CTX_LEN`, `AITHER_VLLM_QUANT_ARGS`\n env vars let `adk setup` write GPU-detected tier settings to `.env`\n and have the node compose pick them up (TQ4 for small GPUs, BNB for large).\n\n### Fixed\n- **Memory cloud routing** — `Memory._spirit_teach()` and `_spirit_recall()`\n now use configurable paths and auth headers for gateway proxy. Previously\n cloud-only users silently lost all Spirit memory calls.\n\n## [1.9.0] - 2026-05-24\n\n### Added\n- **Composable ToolPack system** — agents can load tool packs from\n `.toolpack.yaml` directories. Drop a pack in `~/.aitheros/packs/` or\n set `AITHER_PACKS_DIR`, and any agent discovers it automatically.\n- **`tools.packs` in agent.yaml** — list tool pack IDs to auto-load\n (e.g. `packs: [crm-tools, calendar-pro]`).\n- **`register_tool_packs(agent, pack_ids, packs_dir)`** in `builtin_tools` —\n programmatic pack loading for agents.\n- **`AITHER_TOOL_PACKS` env var** — comma-separated pack IDs loaded on\n agent init (alternative to YAML config).\n- **Unknown builtin categories resolve as pack IDs** — `tools.builtin:\n [file_io, crm-tools]` now works: `crm-tools` is looked up as a pack.\n\n## [1.8.0] - 2026-05-24\n\n### Removed\n- **11 dead modules** (3,023 lines) — `agent_deploy`, `cloud_deploy`, `create`,\n `phonehome`, `sdk_bridge`, `sovereign`, `telemetry`, `telemetry_config`,\n `error_reporter`, `anonymizer`, `package`. None were imported anywhere.\n- **29 ghost exports** from `__init__.py` — `secrets`, `otel`, `fs_sandbox`,\n and `vector_memory` modules were listed in `__all__` and `__getattr__` but\n the backing files never existed. Accessing them raised `ModuleNotFoundError`.\n- **Stale dist/ artifacts** — removed old wheels/tarballs (v0.4.0–v1.4.1, 11 MB).\n- **Orphan test scripts** — `test_phase1.py` and `test_phase23.py` (one-off\n validation scripts not part of the pytest suite).\n\n### Fixed\n- **Version mismatch** — `__init__.__version__` now matches `pyproject.toml`\n (was stuck at 1.6.0).\n\n## [1.5.0] - 2026-05-23\n\n### Added\n- **`--sovereign` flag for `adk deploy node`** — registers the node with the\n Aitherium hub via federation after deployment. Saves federation credentials\n to `~/.aither/.env.federation`. Accepts `--hub` (custom hub URL) and\n `--tenant` (tenant slug) options. Non-fatal: node runs standalone if\n registration fails.\n- **Federation → fleet cross-post** (Genesis) — sovereign nodes that register\n or heartbeat via `/federation/register` and `/federation/heartbeat` now\n automatically appear in the fleet dashboard (`/fleet/deployments`) with\n `node_type: \"sovereign\"` and live status/metrics.\n- **\"Deploy Sovereign Node\" section on Connect page** (AitherVeil) — Section 8\n with ADK deploy commands, fleet dashboard links, and provision page links.\n- **`deploy-node` action in tunnel proxy** (AitherVeil) — Connect page and\n external callers can now request bootstrap scripts via\n `POST /api/tunnel/devworkspace?action=deploy-node`.\n\n### Fixed\n- **Fleet provision page** — API URL corrected from `/api/fleet/provision` to\n `/api/bridge/genesis/fleet/provision` (matches bridge proxy pattern).\n- **Fleet detail page** — API URL corrected from `/api/fleet/deployments/` to\n `/api/bridge/genesis/fleet/deployments/` (same fix).\n- **Dockerfile** — entry point corrected from `aither-serve` to `adk-serve`\n (matches `pyproject.toml` script registration). Version label updated.\n\n## [1.4.1] - 2026-05-22\n\n### Fixed\n- **macOS executable builds** — dropped `--target-arch universal2` from\n `packaging/build_executable.py`. PyPI wheels for `pydantic_core` and other\n native deps are arch-specific, not fat binaries, so universal2 always failed\n with `IncompatibleBinaryArchError`. Now builds for the runner's native arch\n (`aither-macos-arm64`).\n- **Release workflow strategy** — added `fail-fast: false` so a single\n executable build failure no longer cancels the other OS jobs.\n- **PyPI publish** — added `skip-existing: true` so re-runs of the same\n version don't fail when the artifact already exists.\n- **GitHub Release job** — now runs whenever `pypi` succeeds even if some\n executable matrix entries fail, so the release page is always created with\n whatever binaries did build successfully.\n\n## [1.4.0] - 2026-05-22\n\n### Added\n- **Sovereign template now ships with `agent_core` 5-tier memory** — generated\n backends include the canonical `agent_core/` package (working → episodic →\n semantic → procedural → identity) plus `agent_memory.py` helper, vendored\n from `portal-kit-backend`.\n- `aiosqlite>=0.20.0` added to sovereign template `requirements.txt` for\n local agent_core SQLite storage.\n- Document tombstoning: `evict_cached_conversations_referencing` is wired\n through document-delete handlers in consumer apps so memories citing\n deleted documents are blocked from recall.\n- New tests: 13 smoke tests for `portal-kit-backend/agent_core` covering\n promotion thresholds, decay rates, store roundtrip, tombstone recall\n blocking, circuit breaker state machine, and chat-turn recording.\n\n### Changed\n- `scripts/vendor_agent_core.py` now syncs canonical `agent_core` to 4\n targets: WorkspaceRuntime, ADK sovereign template, and the GargBot +\n Chelle consumer mirrors (previously only 2).\n\n## [1.2.1] - 2026-05-21\n\n### Added\n- `adk train` command group — 6 subcommands: status, launch, logs, cancel, runs, register-gpu\n- `/slash-commands` endpoint — ADK server auto-exposes all 35 CLI commands as structured JSON manifest for AitherShell\n- `/cli/execute` endpoint — AitherShell can run any CLI command through the server\n- `build_command_manifest()` — introspects full argparse tree (commands, args, choices, subcommands)\n- `_register_commands()` extracted from main() for shared parser construction\n\n### Changed\n- `_get_genesis_url()` now probes: Genesis → ADK server → Aitherium cloud gateway (works without Genesis)\n\n## [1.2.0] - 2026-05-21\n\n### Added\n- **Runtime backend switching** — `agent.switch_backend(\"deepseek\")` and `agent.llm.switch_backend()` change provider without recreating agent\n- **Hybrid reasoning** — `agent.set_reasoning_backend(\"anthropic\")` routes effort 7+ to cloud API while keeping local orchestrator\n- **First-class DeepSeek provider** — `deepseek` in effort model table with `deepseek-chat` and `deepseek-reasoner`\n- **TQ4 quantization tiers** — `nano` (6GB, TurboQuant 4-bit), `standard-tq4` (12GB, both models TQ4), `hybrid-tq4` (6GB + cloud reasoning)\n- **`adk backend` CLI** — `list`, `set`, `set-reasoning`, `test` subcommands\n- **`adk quickstart`** — unified first-run wizard (setup + auth + shell)\n- **`adk tools`** — list local + MCP tools with tier markers\n- **`adk backup`** — tarball export of `~/.aither/`\n- **`adk ingest`** — feed docs/files into knowledge graph\n- **`adk setup nemotron`** shortcut — alias for `--tier lite`\n- **`--reasoning-api`** flag in setup — `anthropic`, `openai`, `deepseek`, `gateway`\n- **`--dgx-spark URL`** flag — remote vLLM endpoint configuration\n- **DGX Spark auto-detection** — scans `spark.local`, `192.168.0.33`, `AITHER_DGX_URL` env\n- **Post-setup smoke test** — sends real inference request after container startup\n- **Schema migration system** — version table + migration runner in `memory.py` and `graph_memory.py`\n- **Configurable vLLM port scan** — `AITHER_VLLM_PORTS` env var for custom ports\n- **`aithershell` entry point** — alias for `adk` in pyproject.toml\n- Shell auto-downloads binary on first `adk shell` (no `--install` needed)\n- Shell pre-flight check warns if no backend detected\n- Shell passes full config via env vars (API key, tenant, inference URL)\n- DGX/Remote and Cloud API checks added to `adk doctor`\n- Config fields: `reasoning_backend`, `reasoning_api_key`, `deepseek_api_key`, `dgx_url`, `vllm_extra_ports`\n\n### Fixed\n- `test_aeon_chat_basic` timeout — mock scope was exiting before request\n- `adk doctor` check_disk timeout — cap rglob at 5000 files\n\n## [1.1.7] - 2026-05-19\n\n### Added\n- Remote agent pipeline — `adk onboard` → fleet dispatch → remote inference\n- Agent-to-agent federation with mesh relay\n- `adk connect` for desktop mesh enrollment\n\n## [1.1.6] - 2026-05-19\n\n### Fixed\n- Tool call execution recovery across all backends\n- Hermes tool call fallback parsing for vLLM\n\n## [1.1.5] - 2026-05-19\n\n### Fixed\n- Tool call execution + Hermes fallback parsing improvements\n\n## [1.1.0] - 2026-05-18\n\n### Added\n- Multi-channel gateway (Telegram, Discord, Slack, webhook)\n- Aeon group chat with 7 presets\n- Skills auto-extraction from multi-step sessions\n- MCP stdio server for Claude Code integration\n- `adk gateway`, `adk cron`, `adk skills`, `adk soul` commands\n- SOUL.md import/export for portable agent identity\n- Elysium cloud device-flow authentication\n\n## [1.0.0] - 2026-05-18\n\n### Breaking\n- Package consolidated — `aither_adk` namespace replaced by `adk` + `adk.platform`\n- 93 duplicate files removed from `AitherOS/packages/aither_adk/`\n\n### Added\n- Standalone core primitives (`adk.core`: Agent, Tool, Memory, ModelBackend, Capability, Trace)\n- `adk.platform` sub-package merging all platform toolkit modules\n- Compatibility shims for legacy `from aither_adk.*` imports\n\n### Changed\n- Version synced across pyproject.toml, `adk/__init__.py`, `adk/platform/__init__.py`, npm package\n- Classifier promoted from Alpha to Beta\n\n## [0.16.0] - 2026-04-16\n\n### Swarm Coding Engine & Repowise Integration\n- **`repowise_search` tool** — Semantic + keyword hybrid code search via Repowise, with ripgrep fallback\n- **`swarm_code` tool** — Dispatch complex tasks to 11-agent swarm pipeline (ARCHITECT->SWARM->REVIEW->JUDGE)\n- **`agent.swarm()`** — Async convenience method with configurable mode, effort, timeout\n- **`agent.code_search()`** — Async convenience method returning structured results\n- New tool categories: `repowise` and `swarm` in TOOL_CATEGORIES\n- `repowise_search` and `swarm_code` added to `__init__.py` exports with lazy loading\n- `IDENTITY_DEFAULTS` updated: repowise for code-focused agents, swarm for orchestration agents\n- Standalone graceful degradation: repowise falls back to ripgrep, swarm returns structured error\n\n## [0.13.0] - 2026-04-02\n\n### Graph Faculties — Local Knowledge for Every Agent\n- **CodeGraph** (2,799 lines) — Full Python AST indexer with call graph, keyword/semantic/hybrid query, embedding matrix cache, incremental re-indexing, multi-hop chain expansion\n- **MemoryGraph** (1,339 lines) — Graph-based persistent agent memory with 10 edge types, hybrid query (keyword + semantic + graph expansion), multi-hop recall, pickle persistence\n- **EmbeddingProvider** — 4-backend fallback chain: sentence-transformers (GPU/CPU) -> Ollama -> Elysium cloud -> feature hashing (zero deps)\n- **BaseFacultyGraph** — Abstract base with HMAC-SHA256 validated pickle persistence\n\n### Agent Integration\n- `agent.set_code_graph(cg)` — auto-registers `code_search` + `code_context` tools\n- `agent.set_memory_graph(mg)` — auto-registers `remember` + `recall` + `memory_stats` tools\n- Both graphs inject context into LLM prompts automatically during chat\n\n### Zero-Config Onboarding\n- `adk start` / `adk` (no args) — auto-detect project, index code, detect LLM, persistent memory, interactive chat\n- Works for any directory: Python codebases, doc folders, mixed workspaces\n- Auto-detects LLM: Ollama -> vLLM -> Elysium -> OpenAI -> Anthropic\n- Per-project persistent memory in `~/.aither/memory/<project>`\n- `adk index <path>` — standalone indexing with progress bar and stats\n\n### MCP Gateway\n- `POST /v1/embeddings` — OpenAI-compatible embedding proxy to vLLM-embeddings:8209\n- Available to all tiers (embeddings are free)\n- ADK EmbeddingProvider uses this as Elysium cloud fallback\n\n### Optional Dependencies\n- `pip install aither-adk[graphs]` — numpy for 10x cosine similarity speedup\n- `pip install aither-adk[embedding]` — sentence-transformers + torch for local GPU embeddings\n- `pip install aither-adk` alone — graphs work with feature hashing (zero deps)\n\n## [0.12.0] - 2026-04-01\n\n### Bootstrap & Service Discovery\n- Version handshake -- ADK checks major.minor compatibility with Genesis/Node\n- Background reconnect loop -- ServiceBridge re-probes every 30s in standalone mode\n- Port 8080/8090 documented -- MCP vs OpenAI-compat clearly separated\n- Genesis URL configurable via GENESIS_URL env var (was hardcoded)\n- Standalone mode warning -- visible stderr alert when AitherOS not detected\n- Auto-reconnect on startup when services come online\n\n### Elysium Cloud Inference\n- Unified gateway URL -- gateway.aitherium.com handles auth + billing + inference\n- Streaming inference -- SSE passthrough for /v1/chat/completions with stream=true\n- Auth proxy routes -- /v1/auth/register, /v1/auth/login, /v1/auth/me\n- Billing proxy -- /v1/billing/balance through gateway\n- AitherConnect Elysium fallback -- cloud inference when local Genesis is down\n- AitherDesktop Elysium fallback -- third-tier chat fallback after Node\n\n### Infrastructure\n- /discovery endpoint on Genesis -- unified service URLs/versions/health\n- /api/config/services on Veil -- runtime port config for client-side JS\n- Veil healthcheck gates on Genesis -- unhealthy when backend is down\n- Desktop crash detection 90s->60s (threshold 3->2)\n\n## [0.11.0] - 2026-04-01\n\n### Agent Execution Quality (Claude Code Parity)\n- Raise loop guard block threshold from 3 to 4 -- agents get more room for iterative search\n- Soft synthesis nudge for effort >= 4 (no tool stripping, trust the model)\n- max_output_tokens escalation -- retry up to 3x when response is truncated\n- Tool result pairing guarantee -- synthetic error for orphaned tool_use blocks\n- Micro-compaction of old tool results -- save context tokens on long sessions\n- First-turn tool forcing only for effort >= 6 (trust model for lower effort)\n- Diminishing returns detection -- nudge agent when 3+ turns produce < 500 tokens\n- Message normalization -- merge consecutive same-role messages, strip empties\n- LLM retry with exponential backoff (5 retries, 500ms-16s, jitter)\n\n## [0.9.0] - 2026-03-16\n\nThe \"connected world\" release. Cross-platform identity pairing, voice capabilities, and multi-channel integration.\n\n### Added\n- **Pairing**: Cross-platform identity linking (`adk/pairing.py`)\n - `PairingManager` — SQLite-backed identity linking with 6-char pairing codes\n - Link users across Telegram, Discord, Slack, WhatsApp with 10min TTL codes\n - Canonical session IDs for cross-channel conversation continuity\n - `get_session_id()` returns \"user-{id}\" for paired users\n- **Voice**: Speech-to-text and text-to-speech client (`adk/voice.py`)\n - `VoiceClient` — async STT/TTS/emotion via AitherVoice service\n - Convenience functions: `hear()`, `say()`, `feel()`\n - 6 voice options: alloy, echo, fable, nova, onyx, shimmer\n - Emotion detection with intensity scoring\n- New exports: `PairingManager`, `PairingResult`, `PlatformIdentity`, `VoiceClient`, `TranscriptionResult`, `SynthesisResult`, `EmotionResult`\n\n### Changed\n- `__init__.py` exports expanded with pairing and voice symbols\n\n## [0.6.0] - 2026-03-13\n\nThe \"group mind\" release. Multi-agent group chat, creative tools, and Iris identity.\n\n### Added\n- **Aeon**: Multi-agent group chat engine (`adk/aeon.py`)\n - `AeonSession` — persistent group chat with parallel agent execution\n - 7 presets: balanced, creative, technical, security, minimal, duo_code, research\n - Orchestrator synthesis: Aither summarizes all agent responses\n - Serial execution for Ollama, parallel for vLLM/cloud\n - ConversationStore persistence with `type: \"aeon\"` metadata\n - `group_chat()` one-shot convenience function\n- `aither aeon` CLI command — interactive terminal group chat with color-coded agents\n - `-p/--preset`, `-a/--agents`, `-r/--rounds`, `--no-synthesize` flags\n - `reset` and `quit` commands\n- Server endpoints: `POST /aeon/chat`, `GET /aeon/presets`, `GET /aeon/sessions/{id}`\n- Creative tools in builtin_tools: `image_generate`, `image_refine`, `image_search`, `video_generate`\n- Iris agent identity with visual generation capabilities\n- 48 Aeon tests (data models, presets, chat, context, persistence, server, exports)\n\n### Changed\n- `__init__.py` exports: `AeonSession`, `AeonResponse`, `AeonMessage`, `group_chat`, `AEON_PRESETS`\n\n## [0.5.0] - 2026-03-13\n\nThe \"tenant-ready\" release. Multi-tenant admin, permission grants, safety profiles, and a full setup wizard.\n\n### Added\n- `aither setup` interactive setup wizard with hardware detection, model selection, identity config\n- Strata storage backend: SQLite WAL persistence for conversations, memories, knowledge graphs\n- CLI test runner: `aither test` with auto-discovery and parallel execution\n- Permission grants system for MCP tool access control\n- MCP account management tools\n- LLM provider auto-detection for Ollama and vLLM endpoints\n- Apache-2.0 LICENSE file\n- Elysium desktop sync module\n- Comprehensive CLI test suite (493 tests)\n- Strata storage test suite (972 tests)\n- LLM provider tests (82 tests)\n\n### Changed\n- CLI expanded: `aither setup`, `aither test`, `aither bugreport`, `aither doctor`\n- Strata module rewritten as full local-first storage engine\n- Server startup includes Strata initialization\n- README rewritten with clearer quickstart and architecture docs\n\n### Fixed\n- Dead documentation links\n- Python 3.11 compatibility issues\n- Ruff per-file-ignores configuration\n- Identity provisioning edge cases\n- Docker node-gyp native dependency builds (python3 + build tools)\n\n## [0.4.0] - 2026-03-13\n\nThe \"own your AI\" release. Self-hosted agent OS for people who don't want their data on someone else's servers.\n\n### Added\n- Muse agent identity (creative/artistic generation)\n- Port 8120 to vLLM scan for ExoNodes discovery\n- Public roadmap with milestone-based porting schedule\n- Competitive positioning: self-hosted alternative to cloud-locked AI appliances\n\n### Changed\n- LLM router: compute-aware effort routing (replaces effort-level context gating)\n- README rewritten for sovereignty-first messaging\n- Package description updated\n- Promoted from alpha to stable release\n\n### Fixed\n- Version string consistency between pyproject.toml and __init__.py\n- vLLM port scanning now includes port 8120 (ExoNodes)\n\n## [0.3.1] - 2026-03-09\n\n### Added\n- GraphMemory: SQLite knowledge graph with Ollama embeddings and hybrid search\n- NeuronPool: auto-fire pre-LLM data gathering agents\n- NanoGPT: pure Python char-level transformer with LoRA fine-tuning\n- Safety gates: IntakeGuard (input), LoopGuard (recursion), Sandbox (code exec)\n- Event system: EventEmitter with chat/tool/forge event types\n- Builtin tools: identity-based tool selection\n- ServiceBridge: auto-discovery of AitherOS services\n- Streaming: chat_stream with safety gate integration\n- Auth middleware: Bearer token authentication\n- CLI: `aither init` and `aither serve` commands\n\n### Changed\n- Wired 5 previously disconnected modules into agent loop\n- 522 total passing tests (up from 85)\n\n## [0.3.0] - 2026-03-07\n\n### Added\n- Clean-room agent development kit\n- Multi-backend LLM providers (Ollama, OpenAI, Anthropic)\n- AitherAgent class with @tool decorator\n- SQLite conversation memory\n- OpenAI-compatible server (`aither-serve`)\n- 16 agent identities as package data\n- MCP bridge to mcp.aitherium.com\n- Privacy-centric opt-in telemetry\n- Bug reporting CLI and API\n- FastAPI server with OpenAI-compatible endpoints\n- Hardware auto-detection (5 tiers, 11 profiles)\n- Fleet orchestration and multi-agent coordination\n- A2A mesh protocol\n- Federation protocol for cross-instance agent dispatch\n\n### Initial release\n- 85 passing tests\n- Apache-2.0 license\n"}, "6167435979a1f14f309465c1ca7a44e5cc84c873": {"t": 1785748172.8901534, "n": 80724, "h": "a21d04765a9ac49c", "c": "# Changelog\n\nAll notable changes to aither-adk will be documented in this file.\n\n## [2.23.0] - 2026-07-14\n\n### Removed — dead modules and internal integrations\n\n- **`adk.swarm`, `adk.provisioning_tools`, `adk.session_sync_integration`, `adk.addon_metering`** — four orphan modules with zero public usage have been deleted. These were internal prototypes kept for legacy monorepo consumers; public packages have not imported them since 2.21.0 (tracked as D-348). **Breaking for anyone directly importing these modules** (import will fail). They are no longer exempt from the orphan-module check.\n- **`adk.aither_bridge`** — the AitherOS-internal IRC ↔ chat gateway bridge is no longer shipped in the public aither-adk package (stripped at sync time). All public importers already degrade behind `except ImportError` (in `adk.server` and `adk.builtin_tools`), so public packages remain unaffected. The module stays in the private AitherOS monorepo for internal use.\n- **Identity document trim** — removed internal agent names from `adk/identities/aither.yaml` delegation guidance, replacing with role-generic wording (\"delegate code review, refactoring, security analysis, performance and testing to specialist agents when available\"). Effort-tier routing philosophy remains unchanged and is product design, not a leak.\n\n### Changed — genericized customer/product names in help text\n\n- CLI help/examples no longer name specific customer deployments (`--scope` template list and `deploy agent` name example use generic placeholders). Functional deploy paths are unchanged.\n\n## [2.22.1] - 2026-07-14\n\n### Fixed — real bugs surfaced by reviving the public CI (red since Jul 5)\n\n- **TLS**: bundled toolpacks (arc-brainpack, cloudflare) disabled certificate\n verification on gateway/vault calls; all now route `adk._tls.tls_verify()`\n (verified by default, internal CA bundle when present).\n- **`system_prompt` contract**: bundled-pack `[PACK DIRECTIVES]` were appended\n even to an explicit `system_prompt`; an explicit prompt now wins wholesale.\n- **Headless secrets**: `_derive_key()` used `os.getlogin()`, which raises\n `OSError` without a controlling terminal (CI, daemons, containers) — falls\n back to `getpass.getuser()` (same value on interactive boxes, so existing\n secret files keep decrypting).\n- **`pack_verifier`**: missing `cryptography` crashed with `UnboundLocalError`\n in the exception handler instead of failing closed; now degrades gracefully\n (and `cryptography` was added to the dev extra so CI tests the real path).\n- **`fleet_manager._kill_pid` (POSIX)**: reported success immediately after an\n asynchronous SIGTERM and never reaped children (zombies read as alive); now\n polls, escalates to SIGKILL, and reaps.\n- **Identity**: `aither` regained the `creative` tool category (dropped\n accidentally in a 2.7.0 WIP sweep).\n\n## [2.22.0] - 2026-07-14\n\n### Changed — internal toolkit relocation\n\n- **`adk.platform` relocated** — the internal aither-platform toolkit (ComfyUI/image-gen\n pipelines, fleet agent clients, parallel CLI/UI/tools stack) has been moved out of the\n adk SDK tree to the standalone internal `aither-platform` package at\n `AitherOS/agents/aither_platform/`. The public package remains unaffected (it already\n excluded `adk.platform` since 2.21.1). Internal AitherOS agent builds now install\n `aither-platform` separately. `adk platform` falls through to the same \"not available\"\n message on public installs; internal builds work via the relocated package.\n\n### Removed — backward-compat re-export deleted\n\n- **`adk.gateway` module removed** — the backward-compatibility re-export of `GatewayClient`\n from `adk.gateway` has been removed. Use `from adk.client import GatewayClient` instead\n (the established public API since 2.21.0). All internal importers have been updated.\n\n## [2.21.1] - 2026-07-14\n\n### Removed — internal toolkit and license-gated product no longer ship publicly\n\n- **`adk.platform`** — the internal aither-platform toolkit (ComfyUI/image-gen\n pipelines, fleet agent clients, a parallel cli/ui/tools stack) is no longer\n included in the public repo or the PyPI wheel. It was internal-only code that\n had been riding along since the aither-platform merge. `adk platform` now\n reports \"Platform toolkit not available\" on public installs; internal AitherOS\n builds are unaffected.\n- **`adk.formbridge`** — the FormBridge form-automation product is license-gated\n (PROFESSIONAL+); its implementation no longer ships in the free package. All\n import sites (`builtin_tools`, `server` route mount) already degrade behind\n `except ImportError`, so nothing else changes behavior.\n- Product test suites (`test_formbridge.py`, `test_encounter_keying.py`,\n `test_flow_tools.py`) are excluded from the public sync alongside them.\n\n## [2.21.0] - 2026-07-14\n\n### Added — Completion gate: verified-or-retried task execution\n\n- **`adk.gate.CompletionGate`** — a composable wrapper that closes the \"silent\n success\" gap: an agent's ReAct loop returns whatever the model produced and\n calls it done (`finish_reason = stop/max_steps/length`), which is a *liveness*\n signal, not a *completion* one — so an agent can \"succeed\" while doing nothing.\n `CompletionGate` runs the agent, verifies the result against acceptance\n criteria, retries with the failure fed back, and returns the response with\n `finish_reason` set to `verified` or an honest `unverified`. Never a false pass.\n - **Hard-first, un-soft-passable checks** (`adk.gate.hard_checks`): a filesystem\n path a criterion says must exist is checked on disk; a required quoted token\n must appear in the output. Only genuinely subjective criteria fall through to\n an optional LLM judge (reuses the agent's own model), which **fails closed**\n if unavailable.\n - **`gated_run(agent, task, criteria=..., max_retries=2)`** — one-shot helper.\n - `auto_criteria` (LLM writes the machine-checkable definition-of-done from the\n task), a `verifier=` override for custom checks (tests-pass / HTTP probe), and\n a `requires_action` short-circuit so it never fights human-approval pauses.\n - Compose it onto any agent exposing `run(task) -> resp{.content, .finish_reason}`\n (`AitherAgent`, `core.Agent`, or a thin adapter over a remote dispatch).\n - Exported from the top-level package: `from adk import CompletionGate, gated_run`.\n\n## [2.19.0] - 2026-07-12\n\n### Added — Eve agent importer + durable A2A tasks + canonical agent discovery\n\n- **`adk pack import <eve_agent_path>`** — convert external Eve agents to AitherADK packs\n (from GitHub or local filesystem). Maps Eve structure (instructions → system prompt,\n agent.ts → agent.yaml, skills/*.md → skills, connections/*.ts → MCP, tools/*.ts →\n tools/node verbatim) to AitherADK format with a single command. Eve agents (compiled\n manifest format, schema v35) become installable packs compatible with `adk pack install`.\n- **Durable A2A task lifecycle (`TaskManager` backed by FileStore)** — agent-to-agent\n tasks now survive container restarts. Tasks persist as append-only JSONL\n (`.adk/tasks.jsonl`); in-memory dict for fast access; `create_task()` / `update_status()` /\n `add_artifact()` / `add_message()` all save to disk. Google A2A v0.3.0 spec compliant.\n- **Canonical agent card discovery** — `A2AServer.mount()` now exposes:\n - `GET /.well-known/agent-card.json` — Agent Card (canonical; contains agent identity,\n capabilities, skills, endpoints)\n - `GET /.well-known/agent.json` — Agent Card (legacy, redirects with 308 to canonical path)\n - `POST /a2a` — JSON-RPC 2.0 for message send/task lifecycle\n - `GET /a2a/tasks/{id}/subscribe` — SSE streaming task updates\n- **Four built-in agent packs now complete and catalogued**:\n - `hermes` — architecture review & trade-off analysis\n - `openclaw` — web research & source verification\n - `claude-code` — feature development & debugging\n - `analyst` — structured data inference & anomaly detection\n All discoverable via `adk packs` / `adk install pack:<name>`.\n- **Tolerant tool-call JSON parsing** — malformed tool JSON (extra properties, missing\n name/arguments, string-encoded nested args, missing keys) is now gracefully degraded\n instead of silently failing. `_parse_tool_json()` accepts both `\"arguments\"` and\n `\"parameters\"` keys, coerces string-encoded args, and skips unparseable calls instead\n of crashing the turn.\n\n### Known limitations\n\n- Eve agent importer: remote GitHub fetching not yet implemented (local paths only).\n- Durable task store: currently JSONL (suitable for thousands of records, not millions).\n\n## [2.18.0] - 2026-07-10\n\n### Added — AitherShell command center + Claude Code session management\n\n- **`aither sessions`** — Claude Code session manager: interactive full-screen\n browser (type-to-filter, transcript preview, deep full-text `search`, resume\n **in the current terminal** or as Windows Terminal tabs), crash guard\n (`guard install` = at-logon watchdog that snapshots live sessions and\n auto-restores the whole set after a terminal crash), and `ingest [--watch]`\n (incremental, secret-guarded sync of session conversations into the local KB\n / CompanyBrain via the standard ingest pipeline).\n- **`aither hq`** — command-center dashboard: per-service fleet health, LLM\n queue (depth/VRAM/models), Pulse alerts, live session count, inbox unread —\n auto-refreshing, with one-key jumps into chat, sessions, inbox, agents,\n brief, watchtower, and docker recovery.\n- **`aither inbox`** — unified queue over CommCore mail, Relay\n mentions/notifications, and Pulse alerts; open/mark-read/DM-reply from the\n terminal. Every source degrades independently.\n- **`aither agents`** — roster console + `agents ask -e <1-10> <agent> <q>`\n (effort-tiered ask via Genesis, with automatic `/chat/stream` fallback when\n the `/agent/sync` execute gate rejects host callers) + forge dispatch\n inspection + routine health.\n- **`aither palette`** — universal fuzzy picker across actions, sessions, and\n services; **`aither brief`** — renders Atlas's executive briefing in the\n terminal; **`aither watch`** — fleet watchtower with named wedge-signature\n detection (docker-WSL wedge, LLM-queue stall, crash-loop uptime regression)\n and optional docker auto-recovery.\n- All fleet reads go through a shared fail-soft `FleetClient` (internal-CA\n TLS via `adk._tls`, dashboard-grade timeouts, per-source degraded states).\n- Node shell-cli (`@aitherium/shell-cli` 1.13.0): `aither\n sessions|hq|inbox|palette|brief|watch|agents|docker` now pass through to the\n Python shell instead of being intent-classified as chat prompts.\n\n## [2.17.1] - 2026-07-05\n\n### Fully self-service mesh join — no customer key handling\n\n- **Auto-join headscale off the onboard response.** The conductor now auto-issues a\n headscale pre-auth key for NAT'd nodes and returns it in the `/v1/mesh/onboard`\n response; `mesh.join` consumes it and brings up the tunnel automatically. A customer\n runs one command — they never mint, fetch, or set a headscale key. Explicit\n `--headscale-key`/`AITHER_HEADSCALE_AUTH_KEY` still take precedence.\n- **Security:** `_tailscale_up` now redacts the auth key from all logs and exceptions\n (a failed `tailscale up` previously echoed the full argv, leaking the key into logs).\n\n## [2.17.0] - 2026-07-05\n\n### Added — Headscale mesh transport + local Qdrant + AitherConnect onboarding\n\n- **Headscale NAT-friendly mesh transport.** When raw WireGuard UDP:51820 is not\n viable (customer boxes behind NAT, corporate firewalls, CGNAT), `adk mesh join\n --headscale` routes the mesh tunnel through Tailscale's Headscale control plane.\n The overlay IP is still Conductor-assigned (10.77.0.0/16); Headscale provides\n only the transport layer. Configured via `AITHER_MESH_TRANSPORT=headscale`,\n `AITHER_HEADSCALE_URL` (default: https://headscale.aitherium.com), and\n `AITHER_HEADSCALE_AUTH_KEY` (pre-generated key). Automatic fallback to raw\n WireGuard if Headscale setup fails.\n- **Local Qdrant vector DB provisioning.** `adk stack` now provisions Qdrant\n locally when `AITHER_VECTOR_DB=qdrant` is set, enabling offline-first RAG\n without external dependencies. Tiered: local Qdrant in-fleet, cloud Nexus\n for enterprise multi-tenant deployments.\n- **Conductor URL public fallback.** `adk mesh join` resolves the internal\n `aitheros-conductor:8193` hostname; if unreachable (e.g., self-hosted nodes),\n it falls back to the public `conductor.aitherium.com:8193` endpoint,\n eliminating bootstrap configuration friction.\n\n### Documentation\n\n- **Self-hosting runbook.** New `docs/SELF_HOSTING.md` covers deploying AitherOS\n on customer infrastructure with Qdrant + mesh + AitherConnect onboarding.\n- **Mesh transport constraints documented.** Raw WireGuard requires public\n UDP:51820 endpoint (cloud instances, dedicated hardware); Headscale for\n NAT'd networks. Noted in CLI help and module docstrings.\n\n## [2.15.0] - 2026-07-04\n\n### Added — built-in web admin console + portal-profile settings sync\n\n- **Web admin console.** The page `adk up` opens (served at `/` and `/ui`) is now a full,\n self-contained console — Chat plus six admin tabs: **Backend** (view/switch LLM provider,\n set keys, test connection), **Packs** (list/enable/disable/reload tool packs),\n **Sessions** (browse/delete conversations), **Logs** (redacted agent-log tail),\n **Graph** (knowledge-graph search/neighborhood/stats), and **MCP** (add/remove external\n MCP servers). No build step, no CDN — a single packaged HTML asset. The original minimal\n streaming chat page stays at `/chat` for back-compat.\n- **Admin API (`adk/admin_api.py`).** ~20 endpoints under `/admin/*`, all bearer-gated by the\n existing middleware (never in `_skip_auth_paths`). Operates on the live agent — LLM backend\n swap (`LLMRouter.switch_backend`), pack reload, and MCP registration take effect without a\n restart.\n- **Settings sync (`adk/settings_sync.py`).** The user's portal profile is the source of\n truth: on startup the agent pulls `preferences.adk` and applies it over the local\n `~/.aither/config.yaml` cache; admin-console edits push a debounced snapshot back to\n `PUT /api/settings/preferences`. Provider API keys and MCP auth headers are device-local\n secrets and are never included in the pushed snapshot. Opt out with `AITHER_SETTINGS_SYNC=false`.\n\n### Security\n\n- **Timing-safe token comparison.** The server bearer check now uses `hmac.compare_digest`\n (the token is reachable over the public tunnel; a byte-wise `!=` leaked it to timing attacks).\n- **MCP add is SSRF-guarded + confirmed.** Adding an external MCP server is a two-step\n prepare→confirm flow; URLs resolving to loopback / private / link-local / metadata addresses\n are rejected (override with `AITHER_ALLOW_PRIVATE_MCP=1` for trusted local servers).\n- **Config editor is allowlisted.** `PATCH /admin/config` writes only safe scalar prefs and\n hard-denies fields that could execute code or redirect traffic at startup (e.g.\n `aither_toolpack_dirs`, backend base URLs).\n- **Log tail is scrubbed.** Bearer tokens, `#k=` fragments, and `sk-`/`aither_sk_` keys are\n redacted from the log-tail endpoint; the tunnel log is excluded.\n\n## [2.14.8] - 2026-07-04\n\n### Added — `adk up` for normal humans (no API key, no prereqs, live chat)\n\n- **Hosted-brain default — no API key required.** When there's no local model and no\n provider key, `adk up` signs in (device-flow) and routes inference through the gateway\n using the portal token (`AITHER_LLM_BACKEND=gateway` + bearer). A non-technical user\n never needs to obtain or paste an LLM API key. Explicit `--provider`/local model still\n take precedence; unattended `--yes` with no token/backend fails cleanly (exit 3).\n- **cloudflared auto-download.** If the tunnel binary is missing, `adk up` downloads the\n right platform asset to `~/.aither/bin` (SHA256-verified against the release; opt out with\n `AITHER_CLOUDFLARED_REQUIRE_CHECKSUM=0`) instead of exiting with an install hint.\n- **Built-in streaming chat page.** The agent now serves a self-contained chat UI at `/`\n (and `/chat`) with a live \"thinking…\" indicator and token-by-token streaming — so a human\n has somewhere to talk to it with immediate feedback instead of a blank wait. `adk up` opens\n it locally and passes the callback bearer in the URL fragment (`#k=…`), so `/chat/stream`\n stays authenticated (not an open proxy) with nothing to paste.\n- **Human-readable failures.** `adk up` errors now carry a plain-English `next_action`\n (kept machine-readable in `--yes` JSON).\n\n### Fixed\n\n- `adk up` chat link pointed at a non-existent `/settings/agent` route; it now points at the\n agent's own streaming chat page.\n\n## [2.14.7] - 2026-07-04\n\n### Added\n\n- **`adk up` — one command to run a persistent, fleet-connected agent.** Collapses\n the scattered onboarding surface (`host`/`quickstart`/`connect`/`login`/`setup`)\n into a single command that starts `aither-serve` (single agent, identity `aither`\n by default), opens a Cloudflare quick-tunnel for fleet reachability, registers with\n the portal, and installs a reboot-persistent autostart entry (Windows Task Scheduler\n / systemd-user / launchd). Detaches by default so the terminal is freed; `--foreground`\n blocks. Reuses the existing device-flow login, cloudflared discovery, provider-key\n store, and `_preflight_check` backend detection.\n- **Fully non-interactive path for autonomous agents.** `adk up --yes` (or any\n non-TTY invocation) takes ZERO prompts — everything resolves from flags, env, and\n saved config — and emits a single machine-readable JSON status line with meaningful\n exit codes (`0` ok, `2` bad-input, `3` no-backend, `4` tunnel-missing, `5` register-failed).\n Degrades to local-only (warn, not fail) when a portal token is absent unless\n `--require-register` is set.\n- **`adk down` / `adk status --json` companions.** `down` stops the agent + tunnel,\n best-effort deregisters, and removes the autostart entry; `status` reports the running\n agent (liveness + `/health` + tunnel + registration), with `--json` for agents/CI.\n- **`adk stack`** — the previous `adk up` behaviour (supervise the Room + Ollama consumer\n stack) now lives here; `adk up` is the connected-agent command.\n\n### Internal\n\n- New `adk/agent_daemon.py`: detached process spawn, status file\n (`~/.aither/adk-up.json`), pid liveness/teardown, and cross-platform autostart install.\n\n## [2.14.6] - 2026-07-04\n\n### Added\n\n- **`adk.preflight` — self-bootstrap capability probe.** `run_preflight(agent,\n spec)` wakes an agent up knowing what it is actually plugged into: a bounded\n (1.5s) parallel `LivenessProbe` over the router's resolved backends\n (primary/reasoning), the embeddings winner, remote MCP tools, the structured-ML\n / `/ml/teach` endpoints, and voice — returning an honest `CapabilityReport`.\n Status vocabulary distinguishes reachability from entitlement\n (`OK`/`MISSING`/`UNREACHABLE`/`AUTH`/`TIER_DENIED`/`UNSUPPORTED`), a hosted slot\n never prints a bare `OK`, and a required-but-unsatisfied slot aborts before the\n loop instead of blind-firing. Headless task/rules resolution + an allowed-roots\n policy binding (`builtin_tools.set_allowed_roots`, which resets the memoized\n cache so the override actually enforces).\n- **Multimodal (image) messages.** `Message.content` now accepts an OpenAI-style\n content-part list (`str | list[dict]`), passed through verbatim to the\n OpenAI-compatible body (gateway / vLLM / OpenAI). New `adk.llm.multimodal`\n helpers — `image_message()`, `image_content_parts()`, `to_image_url()` — build\n the parts and encode local image bytes/paths into `data:` URIs.\n- **Real vision preflight probe.** The vision slot is no longer unconditionally\n `UNSUPPORTED`: it sends a discriminable image (a solid red square) and requires\n the model to name the color, so a blind/text-only model cannot false-pass. `OK`\n only on a real round-trip; `AUTH` on 401/403; `UNSUPPORTED` when there is no\n OpenAI-compatible provider or the model cannot see the image.\n\n## [2.14.5] - 2026-07-04\n\n### Added\n\n- **`adk.reasoning.mcts` — generic Monte-Carlo Tree Search library.** A reusable\n MCTS engine (ported from the internal `UnifiedMCTS`, with all internal couplings\n removed) that any agent can drive over its own environment:\n - `MCTSEnvironment` protocol (`get_state_hash` / `get_actions` / `step` /\n `evaluate` / `clone`) — implement it and search.\n - Three **optional, default-off** model seams — `TransitionModel`, `PolicyModel`\n (PUCT priors), `ValueModel` — so a learned world-model / policy / value can be\n injected without touching the core; with all three `None` the engine is a plain\n UCT search.\n - `ObservedTransitionModel` adapter — a cheap online lookup world-model (hit =\n exact stored transition; miss = identity + negative \"unknown\" reward +\n `is_uncertain`, biasing exploration toward learning), with JSONL save/load for\n cross-run reuse.\n - `UnifiedMCTS(config).search(env)` returns an `MCTSResult` (best action, value,\n confidence, principal-variation path) and an optional `MCTSTrace`\n (visit-distribution policy target) for distillation.\n- **Self-bootstrapping agent machinery.** Build an agent from a declarative spec\n instead of bespoke wiring:\n - `adk.bootstrap.build_agent_from_spec(path) -> (AitherAgent, RunCtx)` — resolves\n prompts, activates required/optional tool packs, and carries a `memory_map`\n (a `node_type -> (role, tier)` override for memory writes, never a gate).\n - `adk.prompts.PromptBridge` — resolves an agent's prompt bindings from files\n (relative to the spec) or `AitherPrompts` dot-keys (optional import).\n - `adk.packs.PackActivator` — eager activation of installed tool packs by name\n over `register_tool_packs`; a required pack that registers zero tools raises\n `PackUnavailable`, optional packs soft-degrade.\n - `python -m adk.bootstrap.generic_agent <spec.yaml>` inspects a spec (resolved\n name, activated packs + tool counts, prompt keys, memory map) without running a\n live loop.\n- **`deep_research` tool pack** — a full multi-engine web-research workflow as\n `dr_*` agent tools (keyless search, page fetch + cache, site mirror, a citable\n findings knowledge graph, and markdown/PDF/DOCX report generation), activatable\n by name via the tool-pack loader.\n\n## [2.14.4] - 2026-07-04\n\n### Added\n\n- **`structured_ml` tool domain** — zero-shot structured-data inference for agents,\n backed by TabFM (tabular classification/regression) and TimesFM (time-series\n forecasting). Three tools:\n - `tabular_classify(support_rows, target, query_rows)` — classify rows from a\n labeled support set, in-context (no training step; up to 10 classes).\n - `tabular_regress(support_rows, target, query_rows)` — predict a numeric target.\n - `timeseries_forecast(series, horizon)` — forecast future values of a series.\n\n The tools POST to a structured-ML inference service resolved from\n `AITHER_STRUCTURED_ML_URL` (default `http://localhost:8192`), reject non-http(s)\n URLs, and summarise oversized responses to protect the agent's context.\n- **`Capability.STRUCTURED_INFERENCE`** capability token and a new **`analyst`**\n identity/pack that ships the `structured_ml` domain by default. The domain is\n opt-in — only identities that enable the `structured_ml` category get the tools.\n\n## [2.14.3] - 2026-07-03\n\n### Fixed\n\nAll four bugs below were found by actually booting `adk run` against a real\nchat model and driving a real tool-calling request end-to-end (not inferred\nfrom reading the code) — each one silently broke the \"local agent uses its\nconfigured model and its tools actually work\" path.\n\n- **`AITHER_MODEL` / `config.model` silently ignored by the OpenAI-compatible\n backend**: `LLMRouter.__init__` never read `config.model` into `self._model`\n unless the caller passed `model=` directly to the constructor — which nothing\n did. Every explicit model choice (env var, config file) was dropped, the\n provider fell back to a hardcoded per-backend default (`gpt-4o-mini` for the\n `openai` backend), and an unrecognized model name got silently remapped\n server-side to the wrong backing model. Concretely: setting\n `AITHER_MODEL=aither-orchestrator` had no effect — every request actually\n went to a different model that was never meant to be called directly and\n can't reliably use tools.\n- **`OPENAI_BASE_URL` ignored for explicit `--backend`/`AITHER_LLM_BACKEND`\n selection**: `LLMRouter._auto_detect()`'s explicit-backend branch hardcoded\n `base_url=None` when constructing the provider, instead of reading\n `config.openai_base_url` (which two other call sites in the same file\n already did correctly). Any self-hosted OpenAI-compatible endpoint\n (MicroScheduler, vLLM, LM Studio) configured via `OPENAI_BASE_URL` was\n silently ignored in favor of `https://api.openai.com/v1`.\n- **Tool-call steering never engaged on default-effort chat**: the retry that\n nudges a model back into structured tool-calling when it responds with prose\n instead of a function call was gated behind `effort >= 6`. Default `effort`\n for a plain `agent.chat(message)` call resolves to `5`, so the safety net —\n built specifically for this failure mode — never fired for default requests.\n Also now forces `tool_choice=\"required\"` on the retry instead of relying on a\n text nudge alone.\n- **Every non-string builtin-tool parameter silently typed as `\"string\"` in\n the generated tool schema**: `builtin_tools.py` uses\n `from __future__ import annotations`, so `_extract_parameters()` in\n `tools.py` was reading postponed (string) type hints (`\"int\"`, not `int`)\n straight out of `fn.__annotations__`, which never matched\n `_type_to_schema`'s type-object keys and silently fell through to\n `{\"type\": \"string\"}`. Every builtin tool with an `int`/`float`/`bool`/`list`\n parameter was affected — e.g. `file_read(start_line: int, end_line: int)`\n advertised both as strings, so a model correctly calling the tool with\n `\"start_line\": \"0\"` crashed the actual implementation with\n `unsupported operand type(s) for -: 'str' and 'int'`. Fixed by resolving\n hints through `typing.get_type_hints()` instead of the raw annotations dict.\n `_coerce_arguments()` — the runtime safety net that's supposed to coerce a\n model-supplied `\"0\"` string into `0` before a tool call executes — had the\n exact same raw-`__annotations__` bug and silently no-opped for every builtin\n tool; fixed the same way.\n- **Streaming tool-loop could hang a connection forever**: `chat_stream()`'s\n fallback to the sync tool loop (tool calls can't stream token-by-token) had\n no timeout, so a model that didn't cleanly terminate a tool-calling round\n trip left the SSE connection open indefinitely with zero bytes sent. Bounded\n with a 60s `asyncio.wait_for`; on timeout the stream yields a clear message\n instead of hanging.\n\n## [2.14.2] - 2026-07-03\n\n### Fixed\n- **AitherShell session amnesia**: the REPL generated a fresh `session_id` for\n EVERY message, so conversation history never followed across turns (the\n server stored each turn under a different session). One stable id is now\n minted per shell run; `/resume` and `/new` semantics unchanged, and the\n cross-restart auto-restore of the last session now actually works.\n\n## [2.14.1] - 2026-07-02\n\n### Added\n- **Account-tied licensing via device-flow login** — `adk login` now persists the account\n license returned by the device-flow handshake to `~/.aither/license.json` (base64\n `license_key` → `{payload, signature}` envelope), so entitlements from the authenticated\n account carry into every later `adk` invocation without re-auth. Pairs with the fleet-side\n node tool-pack gate that grants packs by plan-tier rank.\n- **`can_use_formbridge` / `can_use_untether` entitlements** (PROFESSIONAL+ rank ≥ 3) backing\n the FormBridge and UNTETHER agent tool-packs.\n\n### Fixed\n- `adk` CLI: guard the optional `memory_graph` singleton (was a latent `NameError` on the\n `/stats`, `/memory`, and save paths when memory was not constructed).\n\n## [2.14.0] - 2026-07-02\n\n### Added\n- **`adk.memory_wiki` — LyraWiki-style self-managed semantic knowledge** (opt-in; nothing constructs it by default): `MemoryWiki(graph_memory)` maintains curated wiki ARTICLES as `wiki_article` nodes (title/slug, LLM-owned markdown with `[[wikilinks]]`, embedding, durable tier, `CONSOLIDATED_FROM` edges to source memories, governance-ledgered revisions).\n - `consolidate(llm, since=, budget=)` clusters unconsolidated episodic/fact nodes (embedding + keyword), drafts/UPDATEs articles via the INJECTED llm callable (`str -> str` or `messages -> str`, sync or async — no hard model dependency), cites source node ids, marks contradicted sources superseded, then DEMOTES consolidated sources to a fast-decay tier via `promote()` (represented knowledge doesn't need raw copies).\n - `lint(llm=None)` — deterministic health checks (orphan wikilinks, empty/stale articles, live-but-contradicted pairs) + an optional LLM audit pass.\n - `prune(relevance_floor=0.05, hard_delete_after_days=14)` — article relevance = tier freshness × reinforcement (reinforce-on-recall) + link-degree to LIVE memories; below the floor → reversible governance tombstone; entombed past the window → **HARD-DELETE** (the tombstone snapshot itself is purged — content and embedding irrecoverable; the true-deletion path). A node is never deleted without a tombstone first.\n - `recall(query, limit)` — article-first RAG (curated articles rank above raw nodes) that reinforces returned articles.\n- **`adk.routines` — agent heartbeat + self-programmed routines**: `RoutineStore` is a durable registry (`~/.aither/routines/{agent}.json`, `AITHER_DATA_DIR`-aware, injectable path) of cron-scheduled self-prompts `{name, cron, instruction, enabled, last_run, last_result, tags}` that rehydrates into the existing `adk.cron.CronScheduler` on `start()`. A fire runs `agent.chat(instruction)` with the agent's full toolset — the agent programs its future self with its own adk. Guardrails: `max_routines` (12), a 5-minute min fire interval per routine, a per-fire timeout, and every result ledgered (truncated) to `last_result`. Direct-callable routines (`register_direct`) fire a bound method instead of a self-prompt. Self-management tools (`routine_create/list/update/pause/resume/delete/run_now`) ship as OpenAI tool defs + handlers (`build_routine_tools` / `register_routine_tools` / `routine_tool_defs`); the handlers ONLY touch the RoutineStore — they can never modify agent config or safety-relevant settings (the leash principle).\n- **`AitherAgent(routines=True, memory_maintenance=True)`** (both default **False** → the default agent is byte-identical): `routines=True` builds the RoutineStore + scheduler, registers the self-management tools, and starts the heartbeat lazily on the first `chat()` (or via explicit `start_routines()`). `memory_maintenance=True` registers DEFAULT routines — `wiki_consolidate` (every 2 h), `wiki_lint` / `wiki_prune` / `graph_sweep` (daily) — as DIRECT method fires so memory upkeep runs even on tiny models, while staying visible/manageable via `routine_list`.\n- `TombstoneStore.purge(tombstone_id)` — the hard-delete endpoint: irrecoverably removes a tombstone snapshot and rewrites the persisted store without it (consumed by `memory_wiki.prune`).\n\n## [2.13.6] - 2026-07-02\n\n### Added\n- **Qdrant backend for GraphMemory dataplane sync — TRUE two-way, verified live.** Set `AITHER_FLEET_QDRANT_URL` and the sync uses Qdrant (`upsert` + `scroll` + filtered `search`) instead of Nexus. This is what actually delivers cross-agent memory sharing: a node is upserted with its OWN embedding under a deterministic per-(tenant,node) point id (idempotent re-push), and `fleet_pull` reliably SCROLLS a tenant's points back — the enumerate Nexus lacks (its `/search` doesn't return ingested docs and `/export` needs lancedb, which has a hard dependency conflict in the fleet). **Proven live with 2 agents:** agent-1 ingests → auto-syncs to the tenant dataplane; a fresh agent-2 in the same tenant `fleet_pull`s all of agent-1's nodes and can search them; a different tenant pulls nothing (isolation). Dimension-safe (never mixes vector dims in a collection). Local SQLite stays source of truth; best-effort, never raises.\n\n## [2.13.5] - 2026-07-02\n\n### Fixed\n- **CRITICAL — GraphMemory dataplane sync push was 100% failing.** `_fleet_push_node` sent `source_type=\"graph\"` / `content_type=\"graph_node\"`, which AitherNexus rejects as invalid enums (HTTP 500) — so NO graph node ever replicated in 2.13.4. Now sends the accepted `source_type=\"manual\"` / `content_type=\"text\"` (graph provenance is carried in `metadata.synced_from`). **Verified live** against a fleet Nexus: a 4-node ingest replicates with `pending==0` and the collection count reflects it.\n\n### Added\n- **Swarm-awareness notification** — on a successful sync batch, `GraphMemory` emits a best-effort `graph.synced` event (tenant/workspace/agent/collection/count) to `AITHER_FLEET_EVENTS_URL` so OTHER agents in the tenant/swarm can pull the fresh data and deconflict in-flight work (push-based awareness instead of polling). No-op unless the env is set.\n\n### Known limitation\n- Cross-agent **rehydration** (`fleet_pull`) is only reliable when the fleet Nexus is backed by lancedb (persistent + exportable). An in-memory Nexus stores on `/ingest` but neither its `/search` nor `/export` returns the docs — two-way read needs the persistent Nexus.\n\n## [2.13.4] - 2026-07-02\n\n### Added\n- **Memory-maintenance primitives on `GraphMemory`** (all opt-in; default behaviour byte-identical):\n - **Reinforce-on-recall**: `recall_with_activation(..., reinforce=True)` bumps `reinforcement_count`/`last_reinforced` on every RETURNED node's metadata mirror (one UPDATE per node, WAL-safe) — making the `MemoryRecord` reinforcement fields the activation scorer reads LIVE. Default `False` = zero writes.\n - **`sweep(now, archive_below=0.05, max_nodes=None, dry_run=False)`**: read-time decay enforced at rest — archives nodes past their tier TTL with freshness×reinforcement score below `archive_below` and `reinforcement_count <= 1`; `max_nodes` additionally archives the lowest-scored overflow. PERMANENT tier immune. Every archive is a reversible governance tombstone (`TombstoneStore.recover`) + a FORGET ledger entry; a node is never deleted without a tombstone. `dry_run` returns the would-archive list without acting.\n - **`promote(node_id, tier=?, role=?)`**: re-tier/re-role a node IN PLACE (tier/role columns + metadata mirror) — edges, embedding and ledger history survive; ledgers an UPDATE entry when governance is on.\n- `MutationType.UPDATE` ledger entry kind (in-place re-tier/re-role).\n- **Per-tenant dataplane sync for `GraphMemory`** — replicate the local graph to a per-tenant vector dataplane (Nexus/Qdrant) so a sovereign agent's memory is durable + rehydratable across restarts and hosting boundaries. `fleet_push_all_nodes()` (catch-up), `fleet_pull()` (best-effort semantic top-up), `fleet_sync_pending()`, and auto-sync on ingest (`_maybe_autosync` + `drain_sync()`). **On by default** (`AITHER_FLEET_SYNC=auto`; only `false/0/off` disables) with an inferred target (local Nexus `:8122` or the gateway in cloud mode) and a SEPARATE collection (`AITHER_FLEET_GRAPH_COLLECTION`, default `graph_memory`) from the KV memory sync. New `GraphMemory(tenant_id=, workspace_id=, fleet_url=, auto_sync=)` constructor args override env (required for multi-tenant host processes). Node `synced` column + schema v4 migration. TLS verify is CA-bundle-aware (`AITHER_CA_BUNDLE`) for the internal-TLS mesh. Local SQLite stays the source of truth; every sync path is best-effort and never raises into ingest.\n\n## [2.13.3] - 2026-07-02\n\n### Added\n- **Canonical self-deploying embeddings provider** (`adk.embeddings`): one embedding provider for the whole SDK so vectors are portable across scopes (platform / tenant / sovereign) — same model + dimension everywhere. Pinned to `nomic-embed-text` (768-d; the vLLM `--served-model-name` for nomic-embed-text-v1.5). Lazy, single-flight resolution chain: explicit `AITHER_EMBEDDINGS_URL` → local vLLM (`:8209` then `:8120`, HTTPS-then-HTTP) → local Ollama → gateway (`AITHER_GATEWAY_EMBEDDINGS_URL`) → auto-deploy a local vLLM embeddings container if a GPU + Docker are present (opt out with `AITHER_EMBED_AUTODEPLOY=0`) → CPU sentence-transformers (384-d, degraded) → feature-hash (384-d, degraded, always works). Every batch is dimension-tagged so callers never silently mix 768-d and 384-d vectors. `get_default_embedder()`, `embed_texts()`, `embed_one()`, `get_provider()`, `reset_provider()`.\n\n### Changed\n- **`GraphMemory` now defaults its embedder to the canonical provider** (opt out with `AITHER_GRAPH_EMBEDDER=legacy`), so every adk agent shares one 768-d space. Added a meta-table dimension guard: the index is pinned to its first embedding's dimension and later different-dimension vectors are refused (kills 768↔384 mixing that silently poisons cosine similarity).\n\n## [2.13.2] - 2026-07-01\n\n### Added\n- **Session persistence**: AitherShell now auto-saves the active session_id to `~/.aither/config.yaml` after each chat turn and auto-restores it on the next shell launch. Multi-turn conversations survive across restarts — the messages were always persisted in SQLite + JSON, only the session pointer was missing.\n- `/new` command to start a fresh session (clears saved session_id).\n- `/sessions` command to list recent sessions with agent name, message count, and timestamp. Resume any with `/resume <id>`.\n\n## [2.13.1] - 2026-07-01\n\n### Fixed\n- **CRITICAL:** `deploy_connect()` now sends a HEAD request to verify the GitHub release asset exists before downloading, with a clear error if the asset is missing.\n- **CRITICAL:** Compose file URLs are now pinned to a stable commit hash (configurable via `AITHER_COMPOSE_PIN` env) instead of tracking the mutable `main` branch.\n- **CRITICAL:** `deploy_adk_node()` now falls back to starting `adk-serve` natively when Docker is unavailable, instead of blocking with \"Docker not installed\".\n- **HIGH:** GHCR login failure now blocks deployment immediately with actionable guidance, instead of warning and continuing to a confusing image-pull failure.\n- **HIGH:** Health checks now stream the last 30 lines of Docker Compose logs on failure, so the user can see why a container didn't start.\n- **HIGH:** vLLM port allocation now probes for conflicts before assigning; if port 8200 is busy, workers are automatically remapped to the next free port.\n- **MEDIUM:** Compose file downloads are now cached with ETag; re-running `adk deploy node` skips re-downloading an unchanged file.\n- **MEDIUM:** DGX Spark discovery now respects `AITHER_DGX_HOSTS` env (comma-separated) for custom network topologies, instead of only checking `spark.local` and `192.168.0.33`.\n- **MEDIUM:** Cloud API keys (Anthropic, OpenAI, DeepSeek) are now validated with a lightweight probe during `adk setup` infra scan, so invalid keys surface early.\n- **MEDIUM:** `deploy_connect()` now validates the extracted extension has a `manifest.json` and warns if it's missing.\n\n## [2.13.0] - 2026-07-01\n\n### Removed\n- **`adk.faculties.memory_graph`** — the pickle-based `MemoryGraph` module is deleted. `from adk.faculties import MemoryGraph` now resolves to the canonical `adk.graph_memory.GraphMemory` for back-compat.\n- **`adk.platform.memory`** — the entire legacy memory subsystem (`MemoryManager`, `UnifiedMemorySystem`, `GameEngine`, `StoryboardEngine`, `AnchorGenerator`) is deleted. Agent memory is `adk.memory.Memory` + `adk.graph_memory.GraphMemory`.\n\n### Changed\n- `adk work` command now uses `GraphMemory` instead of the removed pickle-based faculty graph.\n- `adk.MemoryGraph` and `adk.faculties.MemoryGraph` lazy exports now resolve to `adk.graph_memory.GraphMemory`.\n- `agent.set_memory_graph()` kept for back-compat but documented as deprecated; agents already wire `GraphMemory` automatically in `__init__`.\n- Platform startup (`adk.platform.infrastructure.startup`) gracefully handles missing `MemoryManager`.\n- Platform CLI module listing and health check updated to reflect removed memory module.\n\n## [2.12.7] - 2026-07-01\n\n### Deprecated\n- `adk.faculties.MemoryGraph` — emits `DeprecationWarning` on instantiation; use `adk.graph_memory.GraphMemory` (SQLite-backed, governed, embedding-native) instead.\n- `adk.platform.memory` package — the legacy `MemoryManager`, `UnifiedMemorySystem`, `GameEngine`, and `StoryboardEngine` modules now emit `DeprecationWarning` on import; use `adk.memory.Memory` and `adk.graph_memory.GraphMemory` for new code.\n\n### Changed\n- Updated `adk.faculties` docstring and usage examples to point at the canonical memory surface.\n\n## [2.12.6] - 2026-07-01\n\n### Changed\n- Trimmed public package debt by removing grid runbook artifacts and shell/room binary placeholder directories from published artifacts.\n- Removed stale public-sync allowlist entries for deleted phase test files and added explicit public-sync guards for binary placeholder directories and grid artifacts.\n- Updated the public README grid note so it no longer links to runbook files excluded from the SDK package.\n\n## [2.12.5] - 2026-07-01\n\n### Added\n- Added best-effort fleet memory sync from local ADK memory into Nexus/Qdrant-compatible RAG stores, including batch catch-up and vector-search recall hooks.\n- Added saved config/env support for fleet memory URL, collection, and sync mode.\n\n### Changed\n- Excluded FormBridge runtime/test internals from public ADK packaging and public repo sync.\n\n## [2.12.4] - 2026-07-01\n\n### Added\n- Added a hot-reload endpoint for installed agent packs so newly applied pack tools can be picked up without restarting `aither-serve`.\n\n### Fixed\n- Fixed synchronous `ChatRelay` calls on Python 3.12 when no event loop is running; async WebSocket delivery is still scheduled when a loop exists.\n- Aligned gateway inference tier tests with the current public ladder: free, starter, pro, enterprise, platform.\n\n## [2.12.0] - 2026-06-28\n\n### Added — one-command local inference + upgrade path (click-to-run)\n- **`adk quickstart-local`** — detects hardware, picks a backend\n (Ollama / llama.cpp / vLLM), installs it, downloads a model, verifies, and\n prints next steps. Bootstraps Ollama itself via winget/brew/install.sh when\n missing (no \"go install it\" dead-ends). Default model `gemma4:e2b`.\n- **`adk backend switch <ollama|llamacpp|vllm>`** (+ `backend status`) — migrate\n the active deployment between engines; re-points config and smoke-tests.\n- **`adk install pack:<name>`** (+ `adk install list` / `adk packs`) — install\n ready-made agent packs (openclaw, hermes, claude-code) into `~/.aither/agents`.\n\n### Fixed (found via live end-to-end testing on Windows + CUDA)\n- quickstart-local reported success on a dead endpoint (smoke test was warn-only);\n a failed smoke now fails the command, with a service-readiness wait.\n- `--model` resolved to `None` (getattr default never fired) → uses the real default.\n- Smoke test: 30s→180s timeout for cold model load; accepts reasoning-model output\n (empty `content` at low token budgets is no longer a false failure).\n- `adk install pack:<name>` colon syntax (argparse rejected it before dispatch).\n- `adk backend status` health probe used a nonexistent `/health` path → 404; now\n probes `/v1/models` (uniform OpenAI-compatible liveness for all three backends).\n- Windows: removed non-ASCII glyphs from printed strings that crashed the cp1252\n console (a `✓` even made a successful install report failure).\n\n## [2.11.1] - 2026-06-14\n\n### Fixed — `adk host` / `adk login` device flow (autonomous onboarding)\nFound driving `adk host` against production; all broke real device-flow login:\n- **Device flow hit the portal FRONTEND, not AitherIdentity.** `adk login`/`adk host`\n POSTed `/auth/device/code` to portal/veil.aitherium.com, which 307-redirects API auth\n to `/login`. Added `_resolve_identity_url()` mapping the aitherium.com topology to\n `idp.aitherium.com` (the Identity API); localhost/bespoke pass through unchanged.\n- **Cloudflare 403'd urllib.** `idp.*` sits behind Cloudflare, which blocks urllib's\n default `Python-urllib/3.x` UA. Device-code + poll requests now send a real\n User-Agent + Accept (verified urllib→idp 200, was 403).\n- **`adk host` now self-authenticates via device flow** when there's no token, and\n **retries via device flow on a 401/403** (a stale saved token no longer dead-ends\n registration).\n\n## [2.11.0] - 2026-06-14\n\n### Added — attach your own MCP \"hands\" to a self-hosted agent\nA self-hosted agent now has the full brain (your model) · body (this loop) · **hands**\n(your tools) trifecta. The AitherOS platform relays the MCP servers you registered for\nyour agent in the `/stream` request body as `mcp_endpoints`, and the loop wires their\ntools into the ReAct turn:\n- **`adk/mcp_endpoint_tools.register_mcp_endpoint_tools(agent, endpoints)`** — speaks MCP\n JSON-RPC (`tools/list` to discover, `tools/call` to invoke) and registers a namespaced\n proxy tool (`{endpoint}__{tool}`) for each advertised tool. Mirrors `app_proxy_tools`.\n Best-effort: an unreachable endpoint registers nothing and never raises.\n- **`POST /stream` and `POST /chat/stream`** accept an optional `mcp_endpoints` list\n (`[{name, url, [headers]}]`); `_aitheros_stream` attaches them to the agent before the\n turn. Existing callers are unaffected (the field is optional).\n\n## [2.10.1] - 2026-06-14\n\n### Fixed — self-hosted `aither-serve` was crash-on-startup (2.10.0 regression)\nA fresh `pip install aither-adk && aither-serve --backend deepseek` could not boot. Found\nand fixed while standing up a real self-hosted DeepSeek agent end-to-end:\n- **Startup `NameError: name 'port' is not defined`** in three lifespan helpers\n (`_join_aithernet`, `_init_a2a_server`, `_start_mesh_hosting`) — they referenced a bare\n `port` no longer in scope. Now use `config.server_port`, and `main()` writes the resolved\n `--port`/`--host` back to `config` so every lifespan helper and the invoke_url see the\n actual bound port.\n- **Fleet self-registration crashed the whole server** — the registration handlers caught\n `(ImportError, RuntimeError, OSError, ConnectionError)` but **not** `httpx.HTTPError`, so a\n `RemoteProtocolError`/connect error talking to the control plane took the agent down. All\n network-facing lifespan handlers now also catch `httpx.HTTPError` (non-fatal, as intended).\n- **`--backend` was ignored when `AITHER_API_KEY` was set** — `_auto_detect` picked the cloud\n gateway before the explicit backend, and `_try_elysium_fallback` then replaced the chosen\n provider. An explicit backend (deepseek/openai/anthropic/your own vllm/ollama) now always\n wins; \"auto\"/\"gateway\" still flow through detection. The operator's own brain is never\n silently hijacked.\n- **Approved-tool trace truncated after `/sessions/{id}/confirm`** — the SSE relay treated\n `AgentResponse.tool_calls_made` (a `list[str]`) as dicts (`tc.get(...)`), raised\n `AttributeError`, and an over-narrow `except` swallowed it so the client saw only\n `session_start` + heartbeats. The relay now handles string tool names, the `except` is\n broad (always emits a typed `error` + terminal `complete`), and the module-level\n `_strata_ingest` `NameError` + Chronicle `session_id` kwarg mismatch are fixed.\n\n## [2.10.0] - 2026-06-13\n\n### Added — human-in-the-loop tool approval (`adk/approval.py`)\n- **Pause before a gated tool, resume on decision.** An agent can now pause its turn before\n executing a sensitive tool and wait for a human's allow/deny — so a managed control plane\n (or any operator) can gate tool use without forking the loop.\n- **Policy via `AITHER_TOOL_APPROVAL`** — a comma-separated list of tool names (optionally\n `agent:tool`), or `*` for every tool. Empty = no gating (unchanged behavior).\n- **Survives restarts / days-later approvals.** The paused turn (user message + pending tool\n calls) is persisted to disk keyed by `session_id`; resume is a fresh request, not a held\n connection. Decisions are recorded per `(session_id, tool_name)`.\n- **`AgentResponse.requires_action` / `.pending`** — set when a turn paused; `pending` lists\n the gated tool calls awaiting a decision.\n- **`AitherAgent.resume(session_id, decisions)`** — records the decisions and re-runs the\n paused turn (the gate consumes them: allowed tools execute, denied tools get a denial\n observation, the turn continues — and may pause again on a different gated tool).\n- **Server:** `POST /sessions/{id}/confirm` records decisions and streams the resumed turn;\n `/stream` emits a `requires_action` event when a turn pauses.\n\n## [2.9.0] - 2026-06-13\n\n### Added — universal LLM continuation primitive (`adk/llm/continuation.py`)\n- **`run_continuation()` + `stitch()`** — one shared continue-until-complete primitive. When a\n completion stops because it hit the output token cap (`finish_reason == \"length\"`), it continues\n the generation and STITCHES the chunks (overlap-dedup + restart-detect) into a complete answer,\n instead of every call-site hand-rolling its own retry. Bounded three ways (max rounds AND total\n output ceiling AND a no-growth break), and it never mutates the caller's message history.\n- **`LLMProvider.chat_with_continuation()`** and **`LLMRouter.chat_with_continuation()`** — every\n provider AND the router inherit the primitive, so any call-site gets continuation for free.\n- Kill-switch `ADK_LLM_CONTINUATION=off`; tunables `ADK_LLM_MAX_CONTINUATIONS`,\n `ADK_LLM_MAX_TOTAL_OUTPUT_TOKENS` (defaults: 2 rounds / 8192 tokens).\n\n### Changed\n- The ReAct loop's truncation recovery now uses the shared primitive. The previous inline\n doubling-retry REPLACED the partial response (losing earlier text in `resp.content`) and injected\n continuation scaffolding into the live message history; it now STITCHES and leaves history clean.\n Tool-call turns are never continued as text.\n\n### Included — previously-committed-but-unreleased changes\n- 2.8.2: TLS verification on by default (`verify=False` removed).\n- 2.8.1: baked license + pack-signing public keys.\n- 2.8.0: version-bump release.\n\n## [2.7.0] - 2026-06-05\n\n### Added — provider-agnostic LLM cost levers (at the `LLMProvider` ABC)\n- **Prompt caching.** New `ProviderCapabilities` (`prompt_cache: explicit|automatic|none`, `batch`) and\n normalized `LLMResponse.cache_read_tokens`/`cache_write_tokens`, so the \"tokens saved\" meter never\n depends on which backend served the turn. Anthropic inserts `cache_control` breakpoints over the stable\n system+tools prefix; OpenAI/DeepSeek caching is automatic and their `cached_tokens` are now surfaced\n (previously discarded). Default-on in `AitherAgent.chat`; override with `cache=False`.\n- **ResponseCache** (`adk/llm/cache.py`) — exact-match response cache with a pluggable backend, wired into\n `LLMRouter` as an opt-in (`LLMRouter(response_cache=…)` + per-call `cacheable=True`). OFF by default — no\n determinism change unless opted in.\n- **Gemini provider** (`adk/llm/gemini.py`) with explicit `cachedContents` caching; registered in the router.\n- **Async batch runner** (`adk/llm/batch_runner.py`) — `run_batch()` over the Anthropic Message Batches /\n OpenAI Batch APIs (~50% off) with a transparent fallback to concurrent `chat()` where unsupported.\n- **Graph-RAG retriever interface** (`adk/graph_retriever.py`).\n\n### Added — principal authority (prompt-injection defense enforced outside the model)\n- `adk/auth.py`: `Principal` / `AuthContext` / `PrincipalResolver` (+ deny-all default).\n- `ToolRegistry.execute(name, args, auth=…)` — a default-deny authorization gate at the tool boundary, so a\n forbidden tool call is blocked regardless of what the LLM emits. `ToolDef` gains `required_clearance` /\n `action_class`. Threaded through `AitherAgent.chat`; `auth=None` is fully backward-compatible.\n- `adk/channel_auth.py`: Telegram webhook-secret + CEO-id checks and HMAC-signed approvals.\n\n## [2.6.4] - 2026-06-05\n\n### Fixes\n- **Loop-guard nudges corrupted the message history → DeepSeek 400 mid-run.**\n When the ReAct loop emitted multiple tool calls in one assistant turn and the\n loop guard fired a WARN/BLOCK/CIRCUIT_BREAK on one of them, it spliced a\n standalone `system` nudge message *between* the tool results. DeepSeek (and\n strict vLLM chat templates) require an assistant `tool_calls` message to be\n followed by exactly one `tool` result per `tool_call_id`, contiguously — so the\n interleaved message produced `400 … insufficient tool messages following\n tool_calls message` and aborted the turn (this is reliably hit after crossing\n the 15-call circuit-break threshold, where every subsequent call WARNs).\n Per-call guidance is now folded into that call's own `tool` result, and any\n standalone steering is deferred to a single message AFTER the whole batch —\n preserving the `tool_calls`↔`tool_results` pairing every backend requires.\n Regression test: `deep-research-agent/tests/test_msg_structure.py`\n (loopguard-flood scenario).\n\n## [2.6.3] - 2026-06-04\n\n### Fixes\n- **Transient provider errors killed a run on the first blip.** A single\n `502 Bad Gateway` / `503` / `529 overloaded` / `429` from the upstream API\n aborted the whole request — the Anthropic provider had **no retry at all**, and\n the OpenAI-compatible provider only retried `429`. Both now retry transient\n statuses (`408/429/500/502/503/504/529`) with `Retry-After`-aware exponential\n backoff (3 attempts), and surface the provider's body if all retries are\n exhausted. Non-transient `4xx` (e.g. `400`) still fails fast. Applies to\n `chat` and `chat_stream` on both providers.\n\n## [2.6.2] - 2026-06-04\n\n### Fixes\n- **DeepSeek (and strict OpenAI-compatible backends) 400'd on long tool\n conversations.** The ReAct loop legitimately injects mid-conversation\n `system` steering messages (the \"[DIMINISHING RETURNS]\" hint after several\n low-output tool iterations, loop-guard nudges). OpenAI tolerates a non-leading\n `system` message; DeepSeek rejects it with a 400. Long research runs\n (many `fetch_url` calls) reliably tripped this. Fix: non-leading `system`\n messages are demoted to `user` at the OpenAI-compatible provider boundary\n (`_demote_nonleading_system`) — steering intent preserved, payload valid on\n every backend; leading system prompt(s) untouched.\n- **Opaque HTTP errors.** `resp.raise_for_status()` discarded the provider's\n response body, so a 400/422 surfaced as a bare `Client error '400 Bad\n Request'` with no cause. `_ensure_ok` now includes the provider's actual\n message (e.g. `context_length_exceeded`, `Invalid 'messages'`) in the raised\n error, for both `chat` and `chat_stream`.\n\n## [2.6.1] - 2026-06-04\n\n### Fixes\n- **`MCPServer.mount()` returned HTTP 422 for every `/mcp` call.** Under\n `from __future__ import annotations` the route handler's `request: Request`\n annotation is a string that FastAPI resolves against the *module* globals, but\n `Request` was imported locally inside `mount()` — so FastAPI couldn't resolve\n it and treated `request` as a required query param. The MCP server endpoint\n (`initialize`, `tools/list`, `tools/call`) was unusable when mounted. FastAPI\n symbols are now imported at module level (guarded; the `[node]` extra still\n gates actual use). Any agent's tools can now be exposed as a working MCP\n server with `MCPServer(...).mount(app)`.\n\n## [2.6.0] - 2026-06-04\n\n### Streaming + scaffolding\n- **`AitherAgent.stream_chat`** — reliable streaming sibling of `chat()`: native\n tool-calling loop (concludes on its own, unlike the text-protocol `stream_react`)\n that emits live `tool`/`tool_result` events and streams the answer as `token`\n events. `token_delay` paces tokens for a typing effect.\n- **`adk new <template>`** — scaffold a full, runnable template app. Ships the\n `deep-research` template (a cited web-research agent: pack + server + web UI):\n `adk new deep-research`.\n\n### Fixes\n- **BYO-key no longer community-capped.** The free-tier monthly token cap now applies\n only to the metered Aitherium gateway backend, never to a user's own provider key\n (Anthropic/OpenAI/DeepSeek/Ollama/vLLM). Previously it could brick a self-hosted\n agent after ~100k tokens on the user's own key.\n- **Stronger, grounded memory recall.** `chat()` now injects the top-6 knowledge-graph\n hits (untruncated) with a hard no-fabrication instruction — was top-3 truncated to\n 200 chars, which surfaced partial facts and let weaker models invent the rest.\n- **Tool-arg coercion.** `ToolRegistry.execute` coerces LLM-supplied args toward each\n parameter's annotated type, so a tool no longer crashes when a model passes a list\n or string where an int/float/bool was expected.\n\n### Docs\n- New `docs/AGENT_DEV_GUIDE.md` — the golden path for building agents/packs.\n\n## [2.5.0] - 2026-06-04\n\n### Continual learning — agents that learn from runs and reuse memory\n\nAgents now improve the second time they do a task instead of starting from scratch.\n\n### Added\n- **Recall-before**: `AitherAgent.chat()` searches the local `SkillStore` and injects the\n top matching learned skills as a system block, so the model reuses proven procedures.\n- **Learn-after**: `_learn_after()` runs after each response — reinforces (`success_count++`)\n any skills that were recalled+reused, and extracts + saves a NEW skill from successful\n multi-tool runs via `SkillExtractor`. Wired at both `chat()` return paths.\n- Gated by `AITHER_SKILLS` (default on). The previously-unused `SkillStore`/`SkillExtractor`\n are now connected to the run loop.\n\n### Notes\n- Pairs with the platform-side LearnedProcedure substrate (recipes → scored procedures →\n skills/tools/A2A/packs → MCTS priors → evolution). The SDK shares memory via the Spirit\n bridge when configured.\n\n## [2.0.0] - 2026-06-02\n\n### Open-core boundary (BREAKING)\n\nThis release draws a single, enforced free/paid line. The free tier stays\ngenuinely useful (real agent, typed + graph + code memory, ReAct, ~essential\ntools); paid tiers unlock the *scale* capabilities via portal.aitherium.com.\n\n### Added\n- **`adk/licensing.py`** — the entitlement keystone. Resolves a tier\n (COMMUNITY by default), verifies optional portal-signed `~/.aither/license.json`\n (Ed25519), and answers `can_use_fleet/channels/cron/swarm/auto_neurons`,\n `is_agent_licensed`, `max_effort`, etc. **Fail-closed**: no/invalid/expired\n license → free tier; an unsigned license never grants premium.\n `AITHER_TENANT_SLUG=aitherium` → unrestricted INTERNAL tier.\n- **Moat guard** (`scripts/check_moat_boundary.py`) wired into the publish\n workflow — blocks any wheel that leaks the moat or drops the keystone.\n- Tests: `tests/test_licensing.py`, `tests/test_moat_boundary.py`.\n\n### Changed (gating — fail-closed)\n- Free tier effort is capped at 3 (reasoning effort 7–10 needs Professional).\n- Monthly token budget hard-block on the free tier (no silent overspend).\n- Gated behind a paid tier: fleet mode (`FleetConfig`), cross-agent delegation\n (`AgentForge.delegate`), channel adapters, cron scheduler, proactive\n auto-neurons, and `swarm`/`swarm_code` dispatch.\n\n### Removed\n- **`adk/nanogpt.py`** (on-device training IP) removed from the published SDK\n and the public repo; relocated to the internal AitherOS moat. Excluded from\n the wheel as defense-in-depth.\n- Pre-2.0 releases shipped this IP and ungated capabilities and are being\n yanked (PyPI) / removed (GitHub releases). See `scripts/yank_leaky_releases.py`\n and `scripts/purge_public_leaks.sh`.\n\n## [1.20.0] - 2026-05-27\n\n### Fixed\n- **Pack discovery path mismatch** — `_find_local_packs()` now scans both\n `~/.aither/packs/` and `~/.aitheros/packs/`, so packs installed via\n `adk pack install` are discovered by `adk-workspace`.\n\n### Added\n- **Auto-register on pack install** — `adk pack install` now auto-registers\n with the portal if the pack contains an `agent.yaml` with a `portal` section.\n One command does download + extract + register.\n- **Flux event on agent registration** — `developer_portal.py` emits\n `agent.registered` Flux event so the portal fleet UI updates in real-time.\n\n## [1.15.0] - 2026-05-26\n\n### Added\n- **Cloud memory** — `adk quickstart --cloud` now tests gateway memory and\n auto-configures Spirit URL so memories persist across devices via\n `gateway.aitherium.com/v1/memory/teach` and `/v1/memory/recall`.\n- **Config→env export for cloud mode** — `Config.from_env()` now exports\n `AITHER_CLOUD_MODE`, `AITHER_SPIRIT_URL`, and spirit path env vars from\n saved config so Memory and other modules pick them up without needing\n explicit env vars set by the user.\n- **Cloud Quick Start section** in README — 3-command setup for users who\n just want a Claude/GPT-4/DeepSeek API key and the full agent harness.\n- **`--memory` profile for node deployment** — `adk deploy node --memory`\n adds Spirit (8087) + WorkingMemory (8101) containers for persistent\n vector memory. Without this, agents use local SQLite only.\n- **Configurable vLLM in node compose** — `AITHER_VLLM_MODEL`,\n `AITHER_VLLM_GPU_UTIL`, `AITHER_VLLM_CTX_LEN`, `AITHER_VLLM_QUANT_ARGS`\n env vars let `adk setup` write GPU-detected tier settings to `.env`\n and have the node compose pick them up (TQ4 for small GPUs, BNB for large).\n\n### Fixed\n- **Memory cloud routing** — `Memory._spirit_teach()` and `_spirit_recall()`\n now use configurable paths and auth headers for gateway proxy. Previously\n cloud-only users silently lost all Spirit memory calls.\n\n## [1.9.0] - 2026-05-24\n\n### Added\n- **Composable ToolPack system** — agents can load tool packs from\n `.toolpack.yaml` directories. Drop a pack in `~/.aitheros/packs/` or\n set `AITHER_PACKS_DIR`, and any agent discovers it automatically.\n- **`tools.packs` in agent.yaml** — list tool pack IDs to auto-load\n (e.g. `packs: [crm-tools, calendar-pro]`).\n- **`register_tool_packs(agent, pack_ids, packs_dir)`** in `builtin_tools` —\n programmatic pack loading for agents.\n- **`AITHER_TOOL_PACKS` env var** — comma-separated pack IDs loaded on\n agent init (alternative to YAML config).\n- **Unknown builtin categories resolve as pack IDs** — `tools.builtin:\n [file_io, crm-tools]` now works: `crm-tools` is looked up as a pack.\n\n## [1.8.0] - 2026-05-24\n\n### Removed\n- **11 dead modules** (3,023 lines) — `agent_deploy`, `cloud_deploy`, `create`,\n `phonehome`, `sdk_bridge`, `sovereign`, `telemetry`, `telemetry_config`,\n `error_reporter`, `anonymizer`, `package`. None were imported anywhere.\n- **29 ghost exports** from `__init__.py` — `secrets`, `otel`, `fs_sandbox`,\n and `vector_memory` modules were listed in `__all__` and `__getattr__` but\n the backing files never existed. Accessing them raised `ModuleNotFoundError`.\n- **Stale dist/ artifacts** — removed old wheels/tarballs (v0.4.0–v1.4.1, 11 MB).\n- **Orphan test scripts** — `test_phase1.py` and `test_phase23.py` (one-off\n validation scripts not part of the pytest suite).\n\n### Fixed\n- **Version mismatch** — `__init__.__version__` now matches `pyproject.toml`\n (was stuck at 1.6.0).\n\n## [1.5.0] - 2026-05-23\n\n### Added\n- **`--sovereign` flag for `adk deploy node`** — registers the node with the\n Aitherium hub via federation after deployment. Saves federation credentials\n to `~/.aither/.env.federation`. Accepts `--hub` (custom hub URL) and\n `--tenant` (tenant slug) options. Non-fatal: node runs standalone if\n registration fails.\n- **Federation → fleet cross-post** (Genesis) — sovereign nodes that register\n or heartbeat via `/federation/register` and `/federation/heartbeat` now\n automatically appear in the fleet dashboard (`/fleet/deployments`) with\n `node_type: \"sovereign\"` and live status/metrics.\n- **\"Deploy Sovereign Node\" section on Connect page** (AitherVeil) — Section 8\n with ADK deploy commands, fleet dashboard links, and provision page links.\n- **`deploy-node` action in tunnel proxy** (AitherVeil) — Connect page and\n external callers can now request bootstrap scripts via\n `POST /api/tunnel/devworkspace?action=deploy-node`.\n\n### Fixed\n- **Fleet provision page** — API URL corrected from `/api/fleet/provision` to\n `/api/bridge/genesis/fleet/provision` (matches bridge proxy pattern).\n- **Fleet detail page** — API URL corrected from `/api/fleet/deployments/` to\n `/api/bridge/genesis/fleet/deployments/` (same fix).\n- **Dockerfile** — entry point corrected from `aither-serve` to `adk-serve`\n (matches `pyproject.toml` script registration). Version label updated.\n\n## [1.4.1] - 2026-05-22\n\n### Fixed\n- **macOS executable builds** — dropped `--target-arch universal2` from\n `packaging/build_executable.py`. PyPI wheels for `pydantic_core` and other\n native deps are arch-specific, not fat binaries, so universal2 always failed\n with `IncompatibleBinaryArchError`. Now builds for the runner's native arch\n (`aither-macos-arm64`).\n- **Release workflow strategy** — added `fail-fast: false` so a single\n executable build failure no longer cancels the other OS jobs.\n- **PyPI publish** — added `skip-existing: true` so re-runs of the same\n version don't fail when the artifact already exists.\n- **GitHub Release job** — now runs whenever `pypi` succeeds even if some\n executable matrix entries fail, so the release page is always created with\n whatever binaries did build successfully.\n\n## [1.4.0] - 2026-05-22\n\n### Added\n- **Sovereign template now ships with `agent_core` 5-tier memory** — generated\n backends include the canonical `agent_core/` package (working → episodic →\n semantic → procedural → identity) plus `agent_memory.py` helper, vendored\n from `portal-kit-backend`.\n- `aiosqlite>=0.20.0` added to sovereign template `requirements.txt` for\n local agent_core SQLite storage.\n- Document tombstoning: `evict_cached_conversations_referencing` is wired\n through document-delete handlers in consumer apps so memories citing\n deleted documents are blocked from recall.\n- New tests: 13 smoke tests for `portal-kit-backend/agent_core` covering\n promotion thresholds, decay rates, store roundtrip, tombstone recall\n blocking, circuit breaker state machine, and chat-turn recording.\n\n### Changed\n- `scripts/vendor_agent_core.py` now syncs canonical `agent_core` to 4\n targets: WorkspaceRuntime, ADK sovereign template, and the GargBot +\n Chelle consumer mirrors (previously only 2).\n\n## [1.2.1] - 2026-05-21\n\n### Added\n- `adk train` command group — 6 subcommands: status, launch, logs, cancel, runs, register-gpu\n- `/slash-commands` endpoint — ADK server auto-exposes all 35 CLI commands as structured JSON manifest for AitherShell\n- `/cli/execute` endpoint — AitherShell can run any CLI command through the server\n- `build_command_manifest()` — introspects full argparse tree (commands, args, choices, subcommands)\n- `_register_commands()` extracted from main() for shared parser construction\n\n### Changed\n- `_get_genesis_url()` now probes: Genesis → ADK server → Aitherium cloud gateway (works without Genesis)\n\n## [1.2.0] - 2026-05-21\n\n### Added\n- **Runtime backend switching** — `agent.switch_backend(\"deepseek\")` and `agent.llm.switch_backend()` change provider without recreating agent\n- **Hybrid reasoning** — `agent.set_reasoning_backend(\"anthropic\")` routes effort 7+ to cloud API while keeping local orchestrator\n- **First-class DeepSeek provider** — `deepseek` in effort model table with `deepseek-chat` and `deepseek-reasoner`\n- **TQ4 quantization tiers** — `nano` (6GB, TurboQuant 4-bit), `standard-tq4` (12GB, both models TQ4), `hybrid-tq4` (6GB + cloud reasoning)\n- **`adk backend` CLI** — `list`, `set`, `set-reasoning`, `test` subcommands\n- **`adk quickstart`** — unified first-run wizard (setup + auth + shell)\n- **`adk tools`** — list local + MCP tools with tier markers\n- **`adk backup`** — tarball export of `~/.aither/`\n- **`adk ingest`** — feed docs/files into knowledge graph\n- **`adk setup nemotron`** shortcut — alias for `--tier lite`\n- **`--reasoning-api`** flag in setup — `anthropic`, `openai`, `deepseek`, `gateway`\n- **`--dgx-spark URL`** flag — remote vLLM endpoint configuration\n- **DGX Spark auto-detection** — scans `spark.local`, `192.168.0.33`, `AITHER_DGX_URL` env\n- **Post-setup smoke test** — sends real inference request after container startup\n- **Schema migration system** — version table + migration runner in `memory.py` and `graph_memory.py`\n- **Configurable vLLM port scan** — `AITHER_VLLM_PORTS` env var for custom ports\n- **`aithershell` entry point** — alias for `adk` in pyproject.toml\n- Shell auto-downloads binary on first `adk shell` (no `--install` needed)\n- Shell pre-flight check warns if no backend detected\n- Shell passes full config via env vars (API key, tenant, inference URL)\n- DGX/Remote and Cloud API checks added to `adk doctor`\n- Config fields: `reasoning_backend`, `reasoning_api_key`, `deepseek_api_key`, `dgx_url`, `vllm_extra_ports`\n\n### Fixed\n- `test_aeon_chat_basic` timeout — mock scope was exiting before request\n- `adk doctor` check_disk timeout — cap rglob at 5000 files\n\n## [1.1.7] - 2026-05-19\n\n### Added\n- Remote agent pipeline — `adk onboard` → fleet dispatch → remote inference\n- Agent-to-agent federation with mesh relay\n- `adk connect` for desktop mesh enrollment\n\n## [1.1.6] - 2026-05-19\n\n### Fixed\n- Tool call execution recovery across all backends\n- Hermes tool call fallback parsing for vLLM\n\n## [1.1.5] - 2026-05-19\n\n### Fixed\n- Tool call execution + Hermes fallback parsing improvements\n\n## [1.1.0] - 2026-05-18\n\n### Added\n- Multi-channel gateway (Telegram, Discord, Slack, webhook)\n- Aeon group chat with 7 presets\n- Skills auto-extraction from multi-step sessions\n- MCP stdio server for Claude Code integration\n- `adk gateway`, `adk cron`, `adk skills`, `adk soul` commands\n- SOUL.md import/export for portable agent identity\n- Elysium cloud device-flow authentication\n\n## [1.0.0] - 2026-05-18\n\n### Breaking\n- Package consolidated — `aither_adk` namespace replaced by `adk` + `adk.platform`\n- 93 duplicate files removed from `AitherOS/packages/aither_adk/`\n\n### Added\n- Standalone core primitives (`adk.core`: Agent, Tool, Memory, ModelBackend, Capability, Trace)\n- `adk.platform` sub-package merging all platform toolkit modules\n- Compatibility shims for legacy `from aither_adk.*` imports\n\n### Changed\n- Version synced across pyproject.toml, `adk/__init__.py`, `adk/platform/__init__.py`, npm package\n- Classifier promoted from Alpha to Beta\n\n## [0.16.0] - 2026-04-16\n\n### Swarm Coding Engine & Repowise Integration\n- **`repowise_search` tool** — Semantic + keyword hybrid code search via Repowise, with ripgrep fallback\n- **`swarm_code` tool** — Dispatch complex tasks to 11-agent swarm pipeline (ARCHITECT->SWARM->REVIEW->JUDGE)\n- **`agent.swarm()`** — Async convenience method with configurable mode, effort, timeout\n- **`agent.code_search()`** — Async convenience method returning structured results\n- New tool categories: `repowise` and `swarm` in TOOL_CATEGORIES\n- `repowise_search` and `swarm_code` added to `__init__.py` exports with lazy loading\n- `IDENTITY_DEFAULTS` updated: repowise for code-focused agents, swarm for orchestration agents\n- Standalone graceful degradation: repowise falls back to ripgrep, swarm returns structured error\n\n## [0.13.0] - 2026-04-02\n\n### Graph Faculties — Local Knowledge for Every Agent\n- **CodeGraph** (2,799 lines) — Full Python AST indexer with call graph, keyword/semantic/hybrid query, embedding matrix cache, incremental re-indexing, multi-hop chain expansion\n- **MemoryGraph** (1,339 lines) — Graph-based persistent agent memory with 10 edge types, hybrid query (keyword + semantic + graph expansion), multi-hop recall, pickle persistence\n- **EmbeddingProvider** — 4-backend fallback chain: sentence-transformers (GPU/CPU) -> Ollama -> Elysium cloud -> feature hashing (zero deps)\n- **BaseFacultyGraph** — Abstract base with HMAC-SHA256 validated pickle persistence\n\n### Agent Integration\n- `agent.set_code_graph(cg)` — auto-registers `code_search` + `code_context` tools\n- `agent.set_memory_graph(mg)` — auto-registers `remember` + `recall` + `memory_stats` tools\n- Both graphs inject context into LLM prompts automatically during chat\n\n### Zero-Config Onboarding\n- `adk start` / `adk` (no args) — auto-detect project, index code, detect LLM, persistent memory, interactive chat\n- Works for any directory: Python codebases, doc folders, mixed workspaces\n- Auto-detects LLM: Ollama -> vLLM -> Elysium -> OpenAI -> Anthropic\n- Per-project persistent memory in `~/.aither/memory/<project>`\n- `adk index <path>` — standalone indexing with progress bar and stats\n\n### MCP Gateway\n- `POST /v1/embeddings` — OpenAI-compatible embedding proxy to vLLM-embeddings:8209\n- Available to all tiers (embeddings are free)\n- ADK EmbeddingProvider uses this as Elysium cloud fallback\n\n### Optional Dependencies\n- `pip install aither-adk[graphs]` — numpy for 10x cosine similarity speedup\n- `pip install aither-adk[embedding]` — sentence-transformers + torch for local GPU embeddings\n- `pip install aither-adk` alone — graphs work with feature hashing (zero deps)\n\n## [0.12.0] - 2026-04-01\n\n### Bootstrap & Service Discovery\n- Version handshake -- ADK checks major.minor compatibility with Genesis/Node\n- Background reconnect loop -- ServiceBridge re-probes every 30s in standalone mode\n- Port 8080/8090 documented -- MCP vs OpenAI-compat clearly separated\n- Genesis URL configurable via GENESIS_URL env var (was hardcoded)\n- Standalone mode warning -- visible stderr alert when AitherOS not detected\n- Auto-reconnect on startup when services come online\n\n### Elysium Cloud Inference\n- Unified gateway URL -- gateway.aitherium.com handles auth + billing + inference\n- Streaming inference -- SSE passthrough for /v1/chat/completions with stream=true\n- Auth proxy routes -- /v1/auth/register, /v1/auth/login, /v1/auth/me\n- Billing proxy -- /v1/billing/balance through gateway\n- AitherConnect Elysium fallback -- cloud inference when local Genesis is down\n- AitherDesktop Elysium fallback -- third-tier chat fallback after Node\n\n### Infrastructure\n- /discovery endpoint on Genesis -- unified service URLs/versions/health\n- /api/config/services on Veil -- runtime port config for client-side JS\n- Veil healthcheck gates on Genesis -- unhealthy when backend is down\n- Desktop crash detection 90s->60s (threshold 3->2)\n\n## [0.11.0] - 2026-04-01\n\n### Agent Execution Quality (Claude Code Parity)\n- Raise loop guard block threshold from 3 to 4 -- agents get more room for iterative search\n- Soft synthesis nudge for effort >= 4 (no tool stripping, trust the model)\n- max_output_tokens escalation -- retry up to 3x when response is truncated\n- Tool result pairing guarantee -- synthetic error for orphaned tool_use blocks\n- Micro-compaction of old tool results -- save context tokens on long sessions\n- First-turn tool forcing only for effort >= 6 (trust model for lower effort)\n- Diminishing returns detection -- nudge agent when 3+ turns produce < 500 tokens\n- Message normalization -- merge consecutive same-role messages, strip empties\n- LLM retry with exponential backoff (5 retries, 500ms-16s, jitter)\n\n## [0.9.0] - 2026-03-16\n\nThe \"connected world\" release. Cross-platform identity pairing, voice capabilities, and multi-channel integration.\n\n### Added\n- **Pairing**: Cross-platform identity linking (`adk/pairing.py`)\n - `PairingManager` — SQLite-backed identity linking with 6-char pairing codes\n - Link users across Telegram, Discord, Slack, WhatsApp with 10min TTL codes\n - Canonical session IDs for cross-channel conversation continuity\n - `get_session_id()` returns \"user-{id}\" for paired users\n- **Voice**: Speech-to-text and text-to-speech client (`adk/voice.py`)\n - `VoiceClient` — async STT/TTS/emotion via AitherVoice service\n - Convenience functions: `hear()`, `say()`, `feel()`\n - 6 voice options: alloy, echo, fable, nova, onyx, shimmer\n - Emotion detection with intensity scoring\n- New exports: `PairingManager`, `PairingResult`, `PlatformIdentity`, `VoiceClient`, `TranscriptionResult`, `SynthesisResult`, `EmotionResult`\n\n### Changed\n- `__init__.py` exports expanded with pairing and voice symbols\n\n## [0.6.0] - 2026-03-13\n\nThe \"group mind\" release. Multi-agent group chat, creative tools, and Iris identity.\n\n### Added\n- **Aeon**: Multi-agent group chat engine (`adk/aeon.py`)\n - `AeonSession` — persistent group chat with parallel agent execution\n - 7 presets: balanced, creative, technical, security, minimal, duo_code, research\n - Orchestrator synthesis: Aither summarizes all agent responses\n - Serial execution for Ollama, parallel for vLLM/cloud\n - ConversationStore persistence with `type: \"aeon\"` metadata\n - `group_chat()` one-shot convenience function\n- `aither aeon` CLI command — interactive terminal group chat with color-coded agents\n - `-p/--preset`, `-a/--agents`, `-r/--rounds`, `--no-synthesize` flags\n - `reset` and `quit` commands\n- Server endpoints: `POST /aeon/chat`, `GET /aeon/presets`, `GET /aeon/sessions/{id}`\n- Creative tools in builtin_tools: `image_generate`, `image_refine`, `image_search`, `video_generate`\n- Iris agent identity with visual generation capabilities\n- 48 Aeon tests (data models, presets, chat, context, persistence, server, exports)\n\n### Changed\n- `__init__.py` exports: `AeonSession`, `AeonResponse`, `AeonMessage`, `group_chat`, `AEON_PRESETS`\n\n## [0.5.0] - 2026-03-13\n\nThe \"tenant-ready\" release. Multi-tenant admin, permission grants, safety profiles, and a full setup wizard.\n\n### Added\n- `aither setup` interactive setup wizard with hardware detection, model selection, identity config\n- Strata storage backend: SQLite WAL persistence for conversations, memories, knowledge graphs\n- CLI test runner: `aither test` with auto-discovery and parallel execution\n- Permission grants system for MCP tool access control\n- MCP account management tools\n- LLM provider auto-detection for Ollama and vLLM endpoints\n- Apache-2.0 LICENSE file\n- Elysium desktop sync module\n- Comprehensive CLI test suite (493 tests)\n- Strata storage test suite (972 tests)\n- LLM provider tests (82 tests)\n\n### Changed\n- CLI expanded: `aither setup`, `aither test`, `aither bugreport`, `aither doctor`\n- Strata module rewritten as full local-first storage engine\n- Server startup includes Strata initialization\n- README rewritten with clearer quickstart and architecture docs\n\n### Fixed\n- Dead documentation links\n- Python 3.11 compatibility issues\n- Ruff per-file-ignores configuration\n- Identity provisioning edge cases\n- Docker node-gyp native dependency builds (python3 + build tools)\n\n## [0.4.0] - 2026-03-13\n\nThe \"own your AI\" release. Self-hosted agent OS for people who don't want their data on someone else's servers.\n\n### Added\n- Muse agent identity (creative/artistic generation)\n- Port 8120 to vLLM scan for ExoNodes discovery\n- Public roadmap with milestone-based porting schedule\n- Competitive positioning: self-hosted alternative to cloud-locked AI appliances\n\n### Changed\n- LLM router: compute-aware effort routing (replaces effort-level context gating)\n- README rewritten for sovereignty-first messaging\n- Package description updated\n- Promoted from alpha to stable release\n\n### Fixed\n- Version string consistency between pyproject.toml and __init__.py\n- vLLM port scanning now includes port 8120 (ExoNodes)\n\n## [0.3.1] - 2026-03-09\n\n### Added\n- GraphMemory: SQLite knowledge graph with Ollama embeddings and hybrid search\n- NeuronPool: auto-fire pre-LLM data gathering agents\n- NanoGPT: pure Python char-level transformer with LoRA fine-tuning\n- Safety gates: IntakeGuard (input), LoopGuard (recursion), Sandbox (code exec)\n- Event system: EventEmitter with chat/tool/forge event types\n- Builtin tools: identity-based tool selection\n- ServiceBridge: auto-discovery of AitherOS services\n- Streaming: chat_stream with safety gate integration\n- Auth middleware: Bearer token authentication\n- CLI: `aither init` and `aither serve` commands\n\n### Changed\n- Wired 5 previously disconnected modules into agent loop\n- 522 total passing tests (up from 85)\n\n## [0.3.0] - 2026-03-07\n\n### Added\n- Clean-room agent development kit\n- Multi-backend LLM providers (Ollama, OpenAI, Anthropic)\n- AitherAgent class with @tool decorator\n- SQLite conversation memory\n- OpenAI-compatible server (`aither-serve`)\n- 16 agent identities as package data\n- MCP bridge to mcp.aitherium.com\n- Privacy-centric opt-in telemetry\n- Bug reporting CLI and API\n- FastAPI server with OpenAI-compatible endpoints\n- Hardware auto-detection (5 tiers, 11 profiles)\n- Fleet orchestration and multi-agent coordination\n- A2A mesh protocol\n- Federation protocol for cross-instance agent dispatch\n\n### Initial release\n- 85 passing tests\n- Apache-2.0 license\n"}, "4bddae31a3c6d53167bdabd79599f009b20a3ed0": {"t": 1785748174.3155682, "n": 79234, "h": "95dc0ca0bd8ed301", "c": "# Changelog\n\nAll notable changes to aither-adk will be documented in this file.\n\n## [2.22.1] - 2026-07-14\n\n### Fixed — real bugs surfaced by reviving the public CI (red since Jul 5)\n\n- **TLS**: bundled toolpacks (arc-brainpack, cloudflare) disabled certificate\n verification on gateway/vault calls; all now route `adk._tls.tls_verify()`\n (verified by default, internal CA bundle when present).\n- **`system_prompt` contract**: bundled-pack `[PACK DIRECTIVES]` were appended\n even to an explicit `system_prompt`; an explicit prompt now wins wholesale.\n- **Headless secrets**: `_derive_key()` used `os.getlogin()`, which raises\n `OSError` without a controlling terminal (CI, daemons, containers) — falls\n back to `getpass.getuser()` (same value on interactive boxes, so existing\n secret files keep decrypting).\n- **`pack_verifier`**: missing `cryptography` crashed with `UnboundLocalError`\n in the exception handler instead of failing closed; now degrades gracefully\n (and `cryptography` was added to the dev extra so CI tests the real path).\n- **`fleet_manager._kill_pid` (POSIX)**: reported success immediately after an\n asynchronous SIGTERM and never reaped children (zombies read as alive); now\n polls, escalates to SIGKILL, and reaps.\n- **Identity**: `aither` regained the `creative` tool category (dropped\n accidentally in a 2.7.0 WIP sweep).\n\n## [2.22.0] - 2026-07-14\n\n### Changed — internal toolkit relocation\n\n- **`adk.platform` relocated** — the internal aither-platform toolkit (ComfyUI/image-gen\n pipelines, fleet agent clients, parallel CLI/UI/tools stack) has been moved out of the\n adk SDK tree to the standalone internal `aither-platform` package at\n `AitherOS/agents/aither_platform/`. The public package remains unaffected (it already\n excluded `adk.platform` since 2.21.1). Internal AitherOS agent builds now install\n `aither-platform` separately. `adk platform` falls through to the same \"not available\"\n message on public installs; internal builds work via the relocated package.\n\n### Removed — backward-compat re-export deleted\n\n- **`adk.gateway` module removed** — the backward-compatibility re-export of `GatewayClient`\n from `adk.gateway` has been removed. Use `from adk.client import GatewayClient` instead\n (the established public API since 2.21.0). All internal importers have been updated.\n\n## [2.21.1] - 2026-07-14\n\n### Removed — internal toolkit and license-gated product no longer ship publicly\n\n- **`adk.platform`** — the internal aither-platform toolkit (ComfyUI/image-gen\n pipelines, fleet agent clients, a parallel cli/ui/tools stack) is no longer\n included in the public repo or the PyPI wheel. It was internal-only code that\n had been riding along since the aither-platform merge. `adk platform` now\n reports \"Platform toolkit not available\" on public installs; internal AitherOS\n builds are unaffected.\n- **`adk.formbridge`** — the FormBridge form-automation product is license-gated\n (PROFESSIONAL+); its implementation no longer ships in the free package. All\n import sites (`builtin_tools`, `server` route mount) already degrade behind\n `except ImportError`, so nothing else changes behavior.\n- Product test suites (`test_formbridge.py`, `test_encounter_keying.py`,\n `test_flow_tools.py`) are excluded from the public sync alongside them.\n\n## [2.21.0] - 2026-07-14\n\n### Added — Completion gate: verified-or-retried task execution\n\n- **`adk.gate.CompletionGate`** — a composable wrapper that closes the \"silent\n success\" gap: an agent's ReAct loop returns whatever the model produced and\n calls it done (`finish_reason = stop/max_steps/length`), which is a *liveness*\n signal, not a *completion* one — so an agent can \"succeed\" while doing nothing.\n `CompletionGate` runs the agent, verifies the result against acceptance\n criteria, retries with the failure fed back, and returns the response with\n `finish_reason` set to `verified` or an honest `unverified`. Never a false pass.\n - **Hard-first, un-soft-passable checks** (`adk.gate.hard_checks`): a filesystem\n path a criterion says must exist is checked on disk; a required quoted token\n must appear in the output. Only genuinely subjective criteria fall through to\n an optional LLM judge (reuses the agent's own model), which **fails closed**\n if unavailable.\n - **`gated_run(agent, task, criteria=..., max_retries=2)`** — one-shot helper.\n - `auto_criteria` (LLM writes the machine-checkable definition-of-done from the\n task), a `verifier=` override for custom checks (tests-pass / HTTP probe), and\n a `requires_action` short-circuit so it never fights human-approval pauses.\n - Compose it onto any agent exposing `run(task) -> resp{.content, .finish_reason}`\n (`AitherAgent`, `core.Agent`, or a thin adapter over a remote dispatch).\n - Exported from the top-level package: `from adk import CompletionGate, gated_run`.\n\n## [2.19.0] - 2026-07-12\n\n### Added — Eve agent importer + durable A2A tasks + canonical agent discovery\n\n- **`adk pack import <eve_agent_path>`** — convert external Eve agents to AitherADK packs\n (from GitHub or local filesystem). Maps Eve structure (instructions → system prompt,\n agent.ts → agent.yaml, skills/*.md → skills, connections/*.ts → MCP, tools/*.ts →\n tools/node verbatim) to AitherADK format with a single command. Eve agents (compiled\n manifest format, schema v35) become installable packs compatible with `adk pack install`.\n- **Durable A2A task lifecycle (`TaskManager` backed by FileStore)** — agent-to-agent\n tasks now survive container restarts. Tasks persist as append-only JSONL\n (`.adk/tasks.jsonl`); in-memory dict for fast access; `create_task()` / `update_status()` /\n `add_artifact()` / `add_message()` all save to disk. Google A2A v0.3.0 spec compliant.\n- **Canonical agent card discovery** — `A2AServer.mount()` now exposes:\n - `GET /.well-known/agent-card.json` — Agent Card (canonical; contains agent identity,\n capabilities, skills, endpoints)\n - `GET /.well-known/agent.json` — Agent Card (legacy, redirects with 308 to canonical path)\n - `POST /a2a` — JSON-RPC 2.0 for message send/task lifecycle\n - `GET /a2a/tasks/{id}/subscribe` — SSE streaming task updates\n- **Four built-in agent packs now complete and catalogued**:\n - `hermes` — architecture review & trade-off analysis\n - `openclaw` — web research & source verification\n - `claude-code` — feature development & debugging\n - `analyst` — structured data inference & anomaly detection\n All discoverable via `adk packs` / `adk install pack:<name>`.\n- **Tolerant tool-call JSON parsing** — malformed tool JSON (extra properties, missing\n name/arguments, string-encoded nested args, missing keys) is now gracefully degraded\n instead of silently failing. `_parse_tool_json()` accepts both `\"arguments\"` and\n `\"parameters\"` keys, coerces string-encoded args, and skips unparseable calls instead\n of crashing the turn.\n\n### Known limitations\n\n- Eve agent importer: remote GitHub fetching not yet implemented (local paths only).\n- Durable task store: currently JSONL (suitable for thousands of records, not millions).\n\n## [2.18.0] - 2026-07-10\n\n### Added — AitherShell command center + Claude Code session management\n\n- **`aither sessions`** — Claude Code session manager: interactive full-screen\n browser (type-to-filter, transcript preview, deep full-text `search`, resume\n **in the current terminal** or as Windows Terminal tabs), crash guard\n (`guard install` = at-logon watchdog that snapshots live sessions and\n auto-restores the whole set after a terminal crash), and `ingest [--watch]`\n (incremental, secret-guarded sync of session conversations into the local KB\n / CompanyBrain via the standard ingest pipeline).\n- **`aither hq`** — command-center dashboard: per-service fleet health, LLM\n queue (depth/VRAM/models), Pulse alerts, live session count, inbox unread —\n auto-refreshing, with one-key jumps into chat, sessions, inbox, agents,\n brief, watchtower, and docker recovery.\n- **`aither inbox`** — unified queue over CommCore mail, Relay\n mentions/notifications, and Pulse alerts; open/mark-read/DM-reply from the\n terminal. Every source degrades independently.\n- **`aither agents`** — roster console + `agents ask -e <1-10> <agent> <q>`\n (effort-tiered ask via Genesis, with automatic `/chat/stream` fallback when\n the `/agent/sync` execute gate rejects host callers) + forge dispatch\n inspection + routine health.\n- **`aither palette`** — universal fuzzy picker across actions, sessions, and\n services; **`aither brief`** — renders Atlas's executive briefing in the\n terminal; **`aither watch`** — fleet watchtower with named wedge-signature\n detection (docker-WSL wedge, LLM-queue stall, crash-loop uptime regression)\n and optional docker auto-recovery.\n- All fleet reads go through a shared fail-soft `FleetClient` (internal-CA\n TLS via `adk._tls`, dashboard-grade timeouts, per-source degraded states).\n- Node shell-cli (`@aitherium/shell-cli` 1.13.0): `aither\n sessions|hq|inbox|palette|brief|watch|agents|docker` now pass through to the\n Python shell instead of being intent-classified as chat prompts.\n\n## [2.17.1] - 2026-07-05\n\n### Fully self-service mesh join — no customer key handling\n\n- **Auto-join headscale off the onboard response.** The conductor now auto-issues a\n headscale pre-auth key for NAT'd nodes and returns it in the `/v1/mesh/onboard`\n response; `mesh.join` consumes it and brings up the tunnel automatically. A customer\n runs one command — they never mint, fetch, or set a headscale key. Explicit\n `--headscale-key`/`AITHER_HEADSCALE_AUTH_KEY` still take precedence.\n- **Security:** `_tailscale_up` now redacts the auth key from all logs and exceptions\n (a failed `tailscale up` previously echoed the full argv, leaking the key into logs).\n\n## [2.17.0] - 2026-07-05\n\n### Added — Headscale mesh transport + local Qdrant + AitherConnect onboarding\n\n- **Headscale NAT-friendly mesh transport.** When raw WireGuard UDP:51820 is not\n viable (customer boxes behind NAT, corporate firewalls, CGNAT), `adk mesh join\n --headscale` routes the mesh tunnel through Tailscale's Headscale control plane.\n The overlay IP is still Conductor-assigned (10.77.0.0/16); Headscale provides\n only the transport layer. Configured via `AITHER_MESH_TRANSPORT=headscale`,\n `AITHER_HEADSCALE_URL` (default: https://headscale.aitherium.com), and\n `AITHER_HEADSCALE_AUTH_KEY` (pre-generated key). Automatic fallback to raw\n WireGuard if Headscale setup fails.\n- **Local Qdrant vector DB provisioning.** `adk stack` now provisions Qdrant\n locally when `AITHER_VECTOR_DB=qdrant` is set, enabling offline-first RAG\n without external dependencies. Tiered: local Qdrant in-fleet, cloud Nexus\n for enterprise multi-tenant deployments.\n- **Conductor URL public fallback.** `adk mesh join` resolves the internal\n `aitheros-conductor:8193` hostname; if unreachable (e.g., self-hosted nodes),\n it falls back to the public `conductor.aitherium.com:8193` endpoint,\n eliminating bootstrap configuration friction.\n\n### Documentation\n\n- **Self-hosting runbook.** New `docs/SELF_HOSTING.md` covers deploying AitherOS\n on customer infrastructure with Qdrant + mesh + AitherConnect onboarding.\n- **Mesh transport constraints documented.** Raw WireGuard requires public\n UDP:51820 endpoint (cloud instances, dedicated hardware); Headscale for\n NAT'd networks. Noted in CLI help and module docstrings.\n\n## [2.15.0] - 2026-07-04\n\n### Added — built-in web admin console + portal-profile settings sync\n\n- **Web admin console.** The page `adk up` opens (served at `/` and `/ui`) is now a full,\n self-contained console — Chat plus six admin tabs: **Backend** (view/switch LLM provider,\n set keys, test connection), **Packs** (list/enable/disable/reload tool packs),\n **Sessions** (browse/delete conversations), **Logs** (redacted agent-log tail),\n **Graph** (knowledge-graph search/neighborhood/stats), and **MCP** (add/remove external\n MCP servers). No build step, no CDN — a single packaged HTML asset. The original minimal\n streaming chat page stays at `/chat` for back-compat.\n- **Admin API (`adk/admin_api.py`).** ~20 endpoints under `/admin/*`, all bearer-gated by the\n existing middleware (never in `_skip_auth_paths`). Operates on the live agent — LLM backend\n swap (`LLMRouter.switch_backend`), pack reload, and MCP registration take effect without a\n restart.\n- **Settings sync (`adk/settings_sync.py`).** The user's portal profile is the source of\n truth: on startup the agent pulls `preferences.adk` and applies it over the local\n `~/.aither/config.yaml` cache; admin-console edits push a debounced snapshot back to\n `PUT /api/settings/preferences`. Provider API keys and MCP auth headers are device-local\n secrets and are never included in the pushed snapshot. Opt out with `AITHER_SETTINGS_SYNC=false`.\n\n### Security\n\n- **Timing-safe token comparison.** The server bearer check now uses `hmac.compare_digest`\n (the token is reachable over the public tunnel; a byte-wise `!=` leaked it to timing attacks).\n- **MCP add is SSRF-guarded + confirmed.** Adding an external MCP server is a two-step\n prepare→confirm flow; URLs resolving to loopback / private / link-local / metadata addresses\n are rejected (override with `AITHER_ALLOW_PRIVATE_MCP=1` for trusted local servers).\n- **Config editor is allowlisted.** `PATCH /admin/config` writes only safe scalar prefs and\n hard-denies fields that could execute code or redirect traffic at startup (e.g.\n `aither_toolpack_dirs`, backend base URLs).\n- **Log tail is scrubbed.** Bearer tokens, `#k=` fragments, and `sk-`/`aither_sk_` keys are\n redacted from the log-tail endpoint; the tunnel log is excluded.\n\n## [2.14.8] - 2026-07-04\n\n### Added — `adk up` for normal humans (no API key, no prereqs, live chat)\n\n- **Hosted-brain default — no API key required.** When there's no local model and no\n provider key, `adk up` signs in (device-flow) and routes inference through the gateway\n using the portal token (`AITHER_LLM_BACKEND=gateway` + bearer). A non-technical user\n never needs to obtain or paste an LLM API key. Explicit `--provider`/local model still\n take precedence; unattended `--yes` with no token/backend fails cleanly (exit 3).\n- **cloudflared auto-download.** If the tunnel binary is missing, `adk up` downloads the\n right platform asset to `~/.aither/bin` (SHA256-verified against the release; opt out with\n `AITHER_CLOUDFLARED_REQUIRE_CHECKSUM=0`) instead of exiting with an install hint.\n- **Built-in streaming chat page.** The agent now serves a self-contained chat UI at `/`\n (and `/chat`) with a live \"thinking…\" indicator and token-by-token streaming — so a human\n has somewhere to talk to it with immediate feedback instead of a blank wait. `adk up` opens\n it locally and passes the callback bearer in the URL fragment (`#k=…`), so `/chat/stream`\n stays authenticated (not an open proxy) with nothing to paste.\n- **Human-readable failures.** `adk up` errors now carry a plain-English `next_action`\n (kept machine-readable in `--yes` JSON).\n\n### Fixed\n\n- `adk up` chat link pointed at a non-existent `/settings/agent` route; it now points at the\n agent's own streaming chat page.\n\n## [2.14.7] - 2026-07-04\n\n### Added\n\n- **`adk up` — one command to run a persistent, fleet-connected agent.** Collapses\n the scattered onboarding surface (`host`/`quickstart`/`connect`/`login`/`setup`)\n into a single command that starts `aither-serve` (single agent, identity `aither`\n by default), opens a Cloudflare quick-tunnel for fleet reachability, registers with\n the portal, and installs a reboot-persistent autostart entry (Windows Task Scheduler\n / systemd-user / launchd). Detaches by default so the terminal is freed; `--foreground`\n blocks. Reuses the existing device-flow login, cloudflared discovery, provider-key\n store, and `_preflight_check` backend detection.\n- **Fully non-interactive path for autonomous agents.** `adk up --yes` (or any\n non-TTY invocation) takes ZERO prompts — everything resolves from flags, env, and\n saved config — and emits a single machine-readable JSON status line with meaningful\n exit codes (`0` ok, `2` bad-input, `3` no-backend, `4` tunnel-missing, `5` register-failed).\n Degrades to local-only (warn, not fail) when a portal token is absent unless\n `--require-register` is set.\n- **`adk down` / `adk status --json` companions.** `down` stops the agent + tunnel,\n best-effort deregisters, and removes the autostart entry; `status` reports the running\n agent (liveness + `/health` + tunnel + registration), with `--json` for agents/CI.\n- **`adk stack`** — the previous `adk up` behaviour (supervise the Room + Ollama consumer\n stack) now lives here; `adk up` is the connected-agent command.\n\n### Internal\n\n- New `adk/agent_daemon.py`: detached process spawn, status file\n (`~/.aither/adk-up.json`), pid liveness/teardown, and cross-platform autostart install.\n\n## [2.14.6] - 2026-07-04\n\n### Added\n\n- **`adk.preflight` — self-bootstrap capability probe.** `run_preflight(agent,\n spec)` wakes an agent up knowing what it is actually plugged into: a bounded\n (1.5s) parallel `LivenessProbe` over the router's resolved backends\n (primary/reasoning), the embeddings winner, remote MCP tools, the structured-ML\n / `/ml/teach` endpoints, and voice — returning an honest `CapabilityReport`.\n Status vocabulary distinguishes reachability from entitlement\n (`OK`/`MISSING`/`UNREACHABLE`/`AUTH`/`TIER_DENIED`/`UNSUPPORTED`), a hosted slot\n never prints a bare `OK`, and a required-but-unsatisfied slot aborts before the\n loop instead of blind-firing. Headless task/rules resolution + an allowed-roots\n policy binding (`builtin_tools.set_allowed_roots`, which resets the memoized\n cache so the override actually enforces).\n- **Multimodal (image) messages.** `Message.content` now accepts an OpenAI-style\n content-part list (`str | list[dict]`), passed through verbatim to the\n OpenAI-compatible body (gateway / vLLM / OpenAI). New `adk.llm.multimodal`\n helpers — `image_message()`, `image_content_parts()`, `to_image_url()` — build\n the parts and encode local image bytes/paths into `data:` URIs.\n- **Real vision preflight probe.** The vision slot is no longer unconditionally\n `UNSUPPORTED`: it sends a discriminable image (a solid red square) and requires\n the model to name the color, so a blind/text-only model cannot false-pass. `OK`\n only on a real round-trip; `AUTH` on 401/403; `UNSUPPORTED` when there is no\n OpenAI-compatible provider or the model cannot see the image.\n\n## [2.14.5] - 2026-07-04\n\n### Added\n\n- **`adk.reasoning.mcts` — generic Monte-Carlo Tree Search library.** A reusable\n MCTS engine (ported from the internal `UnifiedMCTS`, with all internal couplings\n removed) that any agent can drive over its own environment:\n - `MCTSEnvironment` protocol (`get_state_hash` / `get_actions` / `step` /\n `evaluate` / `clone`) — implement it and search.\n - Three **optional, default-off** model seams — `TransitionModel`, `PolicyModel`\n (PUCT priors), `ValueModel` — so a learned world-model / policy / value can be\n injected without touching the core; with all three `None` the engine is a plain\n UCT search.\n - `ObservedTransitionModel` adapter — a cheap online lookup world-model (hit =\n exact stored transition; miss = identity + negative \"unknown\" reward +\n `is_uncertain`, biasing exploration toward learning), with JSONL save/load for\n cross-run reuse.\n - `UnifiedMCTS(config).search(env)` returns an `MCTSResult` (best action, value,\n confidence, principal-variation path) and an optional `MCTSTrace`\n (visit-distribution policy target) for distillation.\n- **Self-bootstrapping agent machinery.** Build an agent from a declarative spec\n instead of bespoke wiring:\n - `adk.bootstrap.build_agent_from_spec(path) -> (AitherAgent, RunCtx)` — resolves\n prompts, activates required/optional tool packs, and carries a `memory_map`\n (a `node_type -> (role, tier)` override for memory writes, never a gate).\n - `adk.prompts.PromptBridge` — resolves an agent's prompt bindings from files\n (relative to the spec) or `AitherPrompts` dot-keys (optional import).\n - `adk.packs.PackActivator` — eager activation of installed tool packs by name\n over `register_tool_packs`; a required pack that registers zero tools raises\n `PackUnavailable`, optional packs soft-degrade.\n - `python -m adk.bootstrap.generic_agent <spec.yaml>` inspects a spec (resolved\n name, activated packs + tool counts, prompt keys, memory map) without running a\n live loop.\n- **`deep_research` tool pack** — a full multi-engine web-research workflow as\n `dr_*` agent tools (keyless search, page fetch + cache, site mirror, a citable\n findings knowledge graph, and markdown/PDF/DOCX report generation), activatable\n by name via the tool-pack loader.\n\n## [2.14.4] - 2026-07-04\n\n### Added\n\n- **`structured_ml` tool domain** — zero-shot structured-data inference for agents,\n backed by TabFM (tabular classification/regression) and TimesFM (time-series\n forecasting). Three tools:\n - `tabular_classify(support_rows, target, query_rows)` — classify rows from a\n labeled support set, in-context (no training step; up to 10 classes).\n - `tabular_regress(support_rows, target, query_rows)` — predict a numeric target.\n - `timeseries_forecast(series, horizon)` — forecast future values of a series.\n\n The tools POST to a structured-ML inference service resolved from\n `AITHER_STRUCTURED_ML_URL` (default `http://localhost:8192`), reject non-http(s)\n URLs, and summarise oversized responses to protect the agent's context.\n- **`Capability.STRUCTURED_INFERENCE`** capability token and a new **`analyst`**\n identity/pack that ships the `structured_ml` domain by default. The domain is\n opt-in — only identities that enable the `structured_ml` category get the tools.\n\n## [2.14.3] - 2026-07-03\n\n### Fixed\n\nAll four bugs below were found by actually booting `adk run` against a real\nchat model and driving a real tool-calling request end-to-end (not inferred\nfrom reading the code) — each one silently broke the \"local agent uses its\nconfigured model and its tools actually work\" path.\n\n- **`AITHER_MODEL` / `config.model` silently ignored by the OpenAI-compatible\n backend**: `LLMRouter.__init__` never read `config.model` into `self._model`\n unless the caller passed `model=` directly to the constructor — which nothing\n did. Every explicit model choice (env var, config file) was dropped, the\n provider fell back to a hardcoded per-backend default (`gpt-4o-mini` for the\n `openai` backend), and an unrecognized model name got silently remapped\n server-side to the wrong backing model. Concretely: setting\n `AITHER_MODEL=aither-orchestrator` had no effect — every request actually\n went to a different model that was never meant to be called directly and\n can't reliably use tools.\n- **`OPENAI_BASE_URL` ignored for explicit `--backend`/`AITHER_LLM_BACKEND`\n selection**: `LLMRouter._auto_detect()`'s explicit-backend branch hardcoded\n `base_url=None` when constructing the provider, instead of reading\n `config.openai_base_url` (which two other call sites in the same file\n already did correctly). Any self-hosted OpenAI-compatible endpoint\n (MicroScheduler, vLLM, LM Studio) configured via `OPENAI_BASE_URL` was\n silently ignored in favor of `https://api.openai.com/v1`.\n- **Tool-call steering never engaged on default-effort chat**: the retry that\n nudges a model back into structured tool-calling when it responds with prose\n instead of a function call was gated behind `effort >= 6`. Default `effort`\n for a plain `agent.chat(message)` call resolves to `5`, so the safety net —\n built specifically for this failure mode — never fired for default requests.\n Also now forces `tool_choice=\"required\"` on the retry instead of relying on a\n text nudge alone.\n- **Every non-string builtin-tool parameter silently typed as `\"string\"` in\n the generated tool schema**: `builtin_tools.py` uses\n `from __future__ import annotations`, so `_extract_parameters()` in\n `tools.py` was reading postponed (string) type hints (`\"int\"`, not `int`)\n straight out of `fn.__annotations__`, which never matched\n `_type_to_schema`'s type-object keys and silently fell through to\n `{\"type\": \"string\"}`. Every builtin tool with an `int`/`float`/`bool`/`list`\n parameter was affected — e.g. `file_read(start_line: int, end_line: int)`\n advertised both as strings, so a model correctly calling the tool with\n `\"start_line\": \"0\"` crashed the actual implementation with\n `unsupported operand type(s) for -: 'str' and 'int'`. Fixed by resolving\n hints through `typing.get_type_hints()` instead of the raw annotations dict.\n `_coerce_arguments()` — the runtime safety net that's supposed to coerce a\n model-supplied `\"0\"` string into `0` before a tool call executes — had the\n exact same raw-`__annotations__` bug and silently no-opped for every builtin\n tool; fixed the same way.\n- **Streaming tool-loop could hang a connection forever**: `chat_stream()`'s\n fallback to the sync tool loop (tool calls can't stream token-by-token) had\n no timeout, so a model that didn't cleanly terminate a tool-calling round\n trip left the SSE connection open indefinitely with zero bytes sent. Bounded\n with a 60s `asyncio.wait_for`; on timeout the stream yields a clear message\n instead of hanging.\n\n## [2.14.2] - 2026-07-03\n\n### Fixed\n- **AitherShell session amnesia**: the REPL generated a fresh `session_id` for\n EVERY message, so conversation history never followed across turns (the\n server stored each turn under a different session). One stable id is now\n minted per shell run; `/resume` and `/new` semantics unchanged, and the\n cross-restart auto-restore of the last session now actually works.\n\n## [2.14.1] - 2026-07-02\n\n### Added\n- **Account-tied licensing via device-flow login** — `adk login` now persists the account\n license returned by the device-flow handshake to `~/.aither/license.json` (base64\n `license_key` → `{payload, signature}` envelope), so entitlements from the authenticated\n account carry into every later `adk` invocation without re-auth. Pairs with the fleet-side\n node tool-pack gate that grants packs by plan-tier rank.\n- **`can_use_formbridge` / `can_use_untether` entitlements** (PROFESSIONAL+ rank ≥ 3) backing\n the FormBridge and UNTETHER agent tool-packs.\n\n### Fixed\n- `adk` CLI: guard the optional `memory_graph` singleton (was a latent `NameError` on the\n `/stats`, `/memory`, and save paths when memory was not constructed).\n\n## [2.14.0] - 2026-07-02\n\n### Added\n- **`adk.memory_wiki` — LyraWiki-style self-managed semantic knowledge** (opt-in; nothing constructs it by default): `MemoryWiki(graph_memory)` maintains curated wiki ARTICLES as `wiki_article` nodes (title/slug, LLM-owned markdown with `[[wikilinks]]`, embedding, durable tier, `CONSOLIDATED_FROM` edges to source memories, governance-ledgered revisions).\n - `consolidate(llm, since=, budget=)` clusters unconsolidated episodic/fact nodes (embedding + keyword), drafts/UPDATEs articles via the INJECTED llm callable (`str -> str` or `messages -> str`, sync or async — no hard model dependency), cites source node ids, marks contradicted sources superseded, then DEMOTES consolidated sources to a fast-decay tier via `promote()` (represented knowledge doesn't need raw copies).\n - `lint(llm=None)` — deterministic health checks (orphan wikilinks, empty/stale articles, live-but-contradicted pairs) + an optional LLM audit pass.\n - `prune(relevance_floor=0.05, hard_delete_after_days=14)` — article relevance = tier freshness × reinforcement (reinforce-on-recall) + link-degree to LIVE memories; below the floor → reversible governance tombstone; entombed past the window → **HARD-DELETE** (the tombstone snapshot itself is purged — content and embedding irrecoverable; the true-deletion path). A node is never deleted without a tombstone first.\n - `recall(query, limit)` — article-first RAG (curated articles rank above raw nodes) that reinforces returned articles.\n- **`adk.routines` — agent heartbeat + self-programmed routines**: `RoutineStore` is a durable registry (`~/.aither/routines/{agent}.json`, `AITHER_DATA_DIR`-aware, injectable path) of cron-scheduled self-prompts `{name, cron, instruction, enabled, last_run, last_result, tags}` that rehydrates into the existing `adk.cron.CronScheduler` on `start()`. A fire runs `agent.chat(instruction)` with the agent's full toolset — the agent programs its future self with its own adk. Guardrails: `max_routines` (12), a 5-minute min fire interval per routine, a per-fire timeout, and every result ledgered (truncated) to `last_result`. Direct-callable routines (`register_direct`) fire a bound method instead of a self-prompt. Self-management tools (`routine_create/list/update/pause/resume/delete/run_now`) ship as OpenAI tool defs + handlers (`build_routine_tools` / `register_routine_tools` / `routine_tool_defs`); the handlers ONLY touch the RoutineStore — they can never modify agent config or safety-relevant settings (the leash principle).\n- **`AitherAgent(routines=True, memory_maintenance=True)`** (both default **False** → the default agent is byte-identical): `routines=True` builds the RoutineStore + scheduler, registers the self-management tools, and starts the heartbeat lazily on the first `chat()` (or via explicit `start_routines()`). `memory_maintenance=True` registers DEFAULT routines — `wiki_consolidate` (every 2 h), `wiki_lint` / `wiki_prune` / `graph_sweep` (daily) — as DIRECT method fires so memory upkeep runs even on tiny models, while staying visible/manageable via `routine_list`.\n- `TombstoneStore.purge(tombstone_id)` — the hard-delete endpoint: irrecoverably removes a tombstone snapshot and rewrites the persisted store without it (consumed by `memory_wiki.prune`).\n\n## [2.13.6] - 2026-07-02\n\n### Added\n- **Qdrant backend for GraphMemory dataplane sync — TRUE two-way, verified live.** Set `AITHER_FLEET_QDRANT_URL` and the sync uses Qdrant (`upsert` + `scroll` + filtered `search`) instead of Nexus. This is what actually delivers cross-agent memory sharing: a node is upserted with its OWN embedding under a deterministic per-(tenant,node) point id (idempotent re-push), and `fleet_pull` reliably SCROLLS a tenant's points back — the enumerate Nexus lacks (its `/search` doesn't return ingested docs and `/export` needs lancedb, which has a hard dependency conflict in the fleet). **Proven live with 2 agents:** agent-1 ingests → auto-syncs to the tenant dataplane; a fresh agent-2 in the same tenant `fleet_pull`s all of agent-1's nodes and can search them; a different tenant pulls nothing (isolation). Dimension-safe (never mixes vector dims in a collection). Local SQLite stays source of truth; best-effort, never raises.\n\n## [2.13.5] - 2026-07-02\n\n### Fixed\n- **CRITICAL — GraphMemory dataplane sync push was 100% failing.** `_fleet_push_node` sent `source_type=\"graph\"` / `content_type=\"graph_node\"`, which AitherNexus rejects as invalid enums (HTTP 500) — so NO graph node ever replicated in 2.13.4. Now sends the accepted `source_type=\"manual\"` / `content_type=\"text\"` (graph provenance is carried in `metadata.synced_from`). **Verified live** against a fleet Nexus: a 4-node ingest replicates with `pending==0` and the collection count reflects it.\n\n### Added\n- **Swarm-awareness notification** — on a successful sync batch, `GraphMemory` emits a best-effort `graph.synced` event (tenant/workspace/agent/collection/count) to `AITHER_FLEET_EVENTS_URL` so OTHER agents in the tenant/swarm can pull the fresh data and deconflict in-flight work (push-based awareness instead of polling). No-op unless the env is set.\n\n### Known limitation\n- Cross-agent **rehydration** (`fleet_pull`) is only reliable when the fleet Nexus is backed by lancedb (persistent + exportable). An in-memory Nexus stores on `/ingest` but neither its `/search` nor `/export` returns the docs — two-way read needs the persistent Nexus.\n\n## [2.13.4] - 2026-07-02\n\n### Added\n- **Memory-maintenance primitives on `GraphMemory`** (all opt-in; default behaviour byte-identical):\n - **Reinforce-on-recall**: `recall_with_activation(..., reinforce=True)` bumps `reinforcement_count`/`last_reinforced` on every RETURNED node's metadata mirror (one UPDATE per node, WAL-safe) — making the `MemoryRecord` reinforcement fields the activation scorer reads LIVE. Default `False` = zero writes.\n - **`sweep(now, archive_below=0.05, max_nodes=None, dry_run=False)`**: read-time decay enforced at rest — archives nodes past their tier TTL with freshness×reinforcement score below `archive_below` and `reinforcement_count <= 1`; `max_nodes` additionally archives the lowest-scored overflow. PERMANENT tier immune. Every archive is a reversible governance tombstone (`TombstoneStore.recover`) + a FORGET ledger entry; a node is never deleted without a tombstone. `dry_run` returns the would-archive list without acting.\n - **`promote(node_id, tier=?, role=?)`**: re-tier/re-role a node IN PLACE (tier/role columns + metadata mirror) — edges, embedding and ledger history survive; ledgers an UPDATE entry when governance is on.\n- `MutationType.UPDATE` ledger entry kind (in-place re-tier/re-role).\n- **Per-tenant dataplane sync for `GraphMemory`** — replicate the local graph to a per-tenant vector dataplane (Nexus/Qdrant) so a sovereign agent's memory is durable + rehydratable across restarts and hosting boundaries. `fleet_push_all_nodes()` (catch-up), `fleet_pull()` (best-effort semantic top-up), `fleet_sync_pending()`, and auto-sync on ingest (`_maybe_autosync` + `drain_sync()`). **On by default** (`AITHER_FLEET_SYNC=auto`; only `false/0/off` disables) with an inferred target (local Nexus `:8122` or the gateway in cloud mode) and a SEPARATE collection (`AITHER_FLEET_GRAPH_COLLECTION`, default `graph_memory`) from the KV memory sync. New `GraphMemory(tenant_id=, workspace_id=, fleet_url=, auto_sync=)` constructor args override env (required for multi-tenant host processes). Node `synced` column + schema v4 migration. TLS verify is CA-bundle-aware (`AITHER_CA_BUNDLE`) for the internal-TLS mesh. Local SQLite stays the source of truth; every sync path is best-effort and never raises into ingest.\n\n## [2.13.3] - 2026-07-02\n\n### Added\n- **Canonical self-deploying embeddings provider** (`adk.embeddings`): one embedding provider for the whole SDK so vectors are portable across scopes (platform / tenant / sovereign) — same model + dimension everywhere. Pinned to `nomic-embed-text` (768-d; the vLLM `--served-model-name` for nomic-embed-text-v1.5). Lazy, single-flight resolution chain: explicit `AITHER_EMBEDDINGS_URL` → local vLLM (`:8209` then `:8120`, HTTPS-then-HTTP) → local Ollama → gateway (`AITHER_GATEWAY_EMBEDDINGS_URL`) → auto-deploy a local vLLM embeddings container if a GPU + Docker are present (opt out with `AITHER_EMBED_AUTODEPLOY=0`) → CPU sentence-transformers (384-d, degraded) → feature-hash (384-d, degraded, always works). Every batch is dimension-tagged so callers never silently mix 768-d and 384-d vectors. `get_default_embedder()`, `embed_texts()`, `embed_one()`, `get_provider()`, `reset_provider()`.\n\n### Changed\n- **`GraphMemory` now defaults its embedder to the canonical provider** (opt out with `AITHER_GRAPH_EMBEDDER=legacy`), so every adk agent shares one 768-d space. Added a meta-table dimension guard: the index is pinned to its first embedding's dimension and later different-dimension vectors are refused (kills 768↔384 mixing that silently poisons cosine similarity).\n\n## [2.13.2] - 2026-07-01\n\n### Added\n- **Session persistence**: AitherShell now auto-saves the active session_id to `~/.aither/config.yaml` after each chat turn and auto-restores it on the next shell launch. Multi-turn conversations survive across restarts — the messages were always persisted in SQLite + JSON, only the session pointer was missing.\n- `/new` command to start a fresh session (clears saved session_id).\n- `/sessions` command to list recent sessions with agent name, message count, and timestamp. Resume any with `/resume <id>`.\n\n## [2.13.1] - 2026-07-01\n\n### Fixed\n- **CRITICAL:** `deploy_connect()` now sends a HEAD request to verify the GitHub release asset exists before downloading, with a clear error if the asset is missing.\n- **CRITICAL:** Compose file URLs are now pinned to a stable commit hash (configurable via `AITHER_COMPOSE_PIN` env) instead of tracking the mutable `main` branch.\n- **CRITICAL:** `deploy_adk_node()` now falls back to starting `adk-serve` natively when Docker is unavailable, instead of blocking with \"Docker not installed\".\n- **HIGH:** GHCR login failure now blocks deployment immediately with actionable guidance, instead of warning and continuing to a confusing image-pull failure.\n- **HIGH:** Health checks now stream the last 30 lines of Docker Compose logs on failure, so the user can see why a container didn't start.\n- **HIGH:** vLLM port allocation now probes for conflicts before assigning; if port 8200 is busy, workers are automatically remapped to the next free port.\n- **MEDIUM:** Compose file downloads are now cached with ETag; re-running `adk deploy node` skips re-downloading an unchanged file.\n- **MEDIUM:** DGX Spark discovery now respects `AITHER_DGX_HOSTS` env (comma-separated) for custom network topologies, instead of only checking `spark.local` and `192.168.0.33`.\n- **MEDIUM:** Cloud API keys (Anthropic, OpenAI, DeepSeek) are now validated with a lightweight probe during `adk setup` infra scan, so invalid keys surface early.\n- **MEDIUM:** `deploy_connect()` now validates the extracted extension has a `manifest.json` and warns if it's missing.\n\n## [2.13.0] - 2026-07-01\n\n### Removed\n- **`adk.faculties.memory_graph`** — the pickle-based `MemoryGraph` module is deleted. `from adk.faculties import MemoryGraph` now resolves to the canonical `adk.graph_memory.GraphMemory` for back-compat.\n- **`adk.platform.memory`** — the entire legacy memory subsystem (`MemoryManager`, `UnifiedMemorySystem`, `GameEngine`, `StoryboardEngine`, `AnchorGenerator`) is deleted. Agent memory is `adk.memory.Memory` + `adk.graph_memory.GraphMemory`.\n\n### Changed\n- `adk work` command now uses `GraphMemory` instead of the removed pickle-based faculty graph.\n- `adk.MemoryGraph` and `adk.faculties.MemoryGraph` lazy exports now resolve to `adk.graph_memory.GraphMemory`.\n- `agent.set_memory_graph()` kept for back-compat but documented as deprecated; agents already wire `GraphMemory` automatically in `__init__`.\n- Platform startup (`adk.platform.infrastructure.startup`) gracefully handles missing `MemoryManager`.\n- Platform CLI module listing and health check updated to reflect removed memory module.\n\n## [2.12.7] - 2026-07-01\n\n### Deprecated\n- `adk.faculties.MemoryGraph` — emits `DeprecationWarning` on instantiation; use `adk.graph_memory.GraphMemory` (SQLite-backed, governed, embedding-native) instead.\n- `adk.platform.memory` package — the legacy `MemoryManager`, `UnifiedMemorySystem`, `GameEngine`, and `StoryboardEngine` modules now emit `DeprecationWarning` on import; use `adk.memory.Memory` and `adk.graph_memory.GraphMemory` for new code.\n\n### Changed\n- Updated `adk.faculties` docstring and usage examples to point at the canonical memory surface.\n\n## [2.12.6] - 2026-07-01\n\n### Changed\n- Trimmed public package debt by removing grid runbook artifacts and shell/room binary placeholder directories from published artifacts.\n- Removed stale public-sync allowlist entries for deleted phase test files and added explicit public-sync guards for binary placeholder directories and grid artifacts.\n- Updated the public README grid note so it no longer links to runbook files excluded from the SDK package.\n\n## [2.12.5] - 2026-07-01\n\n### Added\n- Added best-effort fleet memory sync from local ADK memory into Nexus/Qdrant-compatible RAG stores, including batch catch-up and vector-search recall hooks.\n- Added saved config/env support for fleet memory URL, collection, and sync mode.\n\n### Changed\n- Excluded FormBridge runtime/test internals from public ADK packaging and public repo sync.\n\n## [2.12.4] - 2026-07-01\n\n### Added\n- Added a hot-reload endpoint for installed agent packs so newly applied pack tools can be picked up without restarting `aither-serve`.\n\n### Fixed\n- Fixed synchronous `ChatRelay` calls on Python 3.12 when no event loop is running; async WebSocket delivery is still scheduled when a loop exists.\n- Aligned gateway inference tier tests with the current public ladder: free, starter, pro, enterprise, platform.\n\n## [2.12.0] - 2026-06-28\n\n### Added — one-command local inference + upgrade path (click-to-run)\n- **`adk quickstart-local`** — detects hardware, picks a backend\n (Ollama / llama.cpp / vLLM), installs it, downloads a model, verifies, and\n prints next steps. Bootstraps Ollama itself via winget/brew/install.sh when\n missing (no \"go install it\" dead-ends). Default model `gemma4:e2b`.\n- **`adk backend switch <ollama|llamacpp|vllm>`** (+ `backend status`) — migrate\n the active deployment between engines; re-points config and smoke-tests.\n- **`adk install pack:<name>`** (+ `adk install list` / `adk packs`) — install\n ready-made agent packs (openclaw, hermes, claude-code) into `~/.aither/agents`.\n\n### Fixed (found via live end-to-end testing on Windows + CUDA)\n- quickstart-local reported success on a dead endpoint (smoke test was warn-only);\n a failed smoke now fails the command, with a service-readiness wait.\n- `--model` resolved to `None` (getattr default never fired) → uses the real default.\n- Smoke test: 30s→180s timeout for cold model load; accepts reasoning-model output\n (empty `content` at low token budgets is no longer a false failure).\n- `adk install pack:<name>` colon syntax (argparse rejected it before dispatch).\n- `adk backend status` health probe used a nonexistent `/health` path → 404; now\n probes `/v1/models` (uniform OpenAI-compatible liveness for all three backends).\n- Windows: removed non-ASCII glyphs from printed strings that crashed the cp1252\n console (a `✓` even made a successful install report failure).\n\n## [2.11.1] - 2026-06-14\n\n### Fixed — `adk host` / `adk login` device flow (autonomous onboarding)\nFound driving `adk host` against production; all broke real device-flow login:\n- **Device flow hit the portal FRONTEND, not AitherIdentity.** `adk login`/`adk host`\n POSTed `/auth/device/code` to portal/veil.aitherium.com, which 307-redirects API auth\n to `/login`. Added `_resolve_identity_url()` mapping the aitherium.com topology to\n `idp.aitherium.com` (the Identity API); localhost/bespoke pass through unchanged.\n- **Cloudflare 403'd urllib.** `idp.*` sits behind Cloudflare, which blocks urllib's\n default `Python-urllib/3.x` UA. Device-code + poll requests now send a real\n User-Agent + Accept (verified urllib→idp 200, was 403).\n- **`adk host` now self-authenticates via device flow** when there's no token, and\n **retries via device flow on a 401/403** (a stale saved token no longer dead-ends\n registration).\n\n## [2.11.0] - 2026-06-14\n\n### Added — attach your own MCP \"hands\" to a self-hosted agent\nA self-hosted agent now has the full brain (your model) · body (this loop) · **hands**\n(your tools) trifecta. The AitherOS platform relays the MCP servers you registered for\nyour agent in the `/stream` request body as `mcp_endpoints`, and the loop wires their\ntools into the ReAct turn:\n- **`adk/mcp_endpoint_tools.register_mcp_endpoint_tools(agent, endpoints)`** — speaks MCP\n JSON-RPC (`tools/list` to discover, `tools/call` to invoke) and registers a namespaced\n proxy tool (`{endpoint}__{tool}`) for each advertised tool. Mirrors `app_proxy_tools`.\n Best-effort: an unreachable endpoint registers nothing and never raises.\n- **`POST /stream` and `POST /chat/stream`** accept an optional `mcp_endpoints` list\n (`[{name, url, [headers]}]`); `_aitheros_stream` attaches them to the agent before the\n turn. Existing callers are unaffected (the field is optional).\n\n## [2.10.1] - 2026-06-14\n\n### Fixed — self-hosted `aither-serve` was crash-on-startup (2.10.0 regression)\nA fresh `pip install aither-adk && aither-serve --backend deepseek` could not boot. Found\nand fixed while standing up a real self-hosted DeepSeek agent end-to-end:\n- **Startup `NameError: name 'port' is not defined`** in three lifespan helpers\n (`_join_aithernet`, `_init_a2a_server`, `_start_mesh_hosting`) — they referenced a bare\n `port` no longer in scope. Now use `config.server_port`, and `main()` writes the resolved\n `--port`/`--host` back to `config` so every lifespan helper and the invoke_url see the\n actual bound port.\n- **Fleet self-registration crashed the whole server** — the registration handlers caught\n `(ImportError, RuntimeError, OSError, ConnectionError)` but **not** `httpx.HTTPError`, so a\n `RemoteProtocolError`/connect error talking to the control plane took the agent down. All\n network-facing lifespan handlers now also catch `httpx.HTTPError` (non-fatal, as intended).\n- **`--backend` was ignored when `AITHER_API_KEY` was set** — `_auto_detect` picked the cloud\n gateway before the explicit backend, and `_try_elysium_fallback` then replaced the chosen\n provider. An explicit backend (deepseek/openai/anthropic/your own vllm/ollama) now always\n wins; \"auto\"/\"gateway\" still flow through detection. The operator's own brain is never\n silently hijacked.\n- **Approved-tool trace truncated after `/sessions/{id}/confirm`** — the SSE relay treated\n `AgentResponse.tool_calls_made` (a `list[str]`) as dicts (`tc.get(...)`), raised\n `AttributeError`, and an over-narrow `except` swallowed it so the client saw only\n `session_start` + heartbeats. The relay now handles string tool names, the `except` is\n broad (always emits a typed `error` + terminal `complete`), and the module-level\n `_strata_ingest` `NameError` + Chronicle `session_id` kwarg mismatch are fixed.\n\n## [2.10.0] - 2026-06-13\n\n### Added — human-in-the-loop tool approval (`adk/approval.py`)\n- **Pause before a gated tool, resume on decision.** An agent can now pause its turn before\n executing a sensitive tool and wait for a human's allow/deny — so a managed control plane\n (or any operator) can gate tool use without forking the loop.\n- **Policy via `AITHER_TOOL_APPROVAL`** — a comma-separated list of tool names (optionally\n `agent:tool`), or `*` for every tool. Empty = no gating (unchanged behavior).\n- **Survives restarts / days-later approvals.** The paused turn (user message + pending tool\n calls) is persisted to disk keyed by `session_id`; resume is a fresh request, not a held\n connection. Decisions are recorded per `(session_id, tool_name)`.\n- **`AgentResponse.requires_action` / `.pending`** — set when a turn paused; `pending` lists\n the gated tool calls awaiting a decision.\n- **`AitherAgent.resume(session_id, decisions)`** — records the decisions and re-runs the\n paused turn (the gate consumes them: allowed tools execute, denied tools get a denial\n observation, the turn continues — and may pause again on a different gated tool).\n- **Server:** `POST /sessions/{id}/confirm` records decisions and streams the resumed turn;\n `/stream` emits a `requires_action` event when a turn pauses.\n\n## [2.9.0] - 2026-06-13\n\n### Added — universal LLM continuation primitive (`adk/llm/continuation.py`)\n- **`run_continuation()` + `stitch()`** — one shared continue-until-complete primitive. When a\n completion stops because it hit the output token cap (`finish_reason == \"length\"`), it continues\n the generation and STITCHES the chunks (overlap-dedup + restart-detect) into a complete answer,\n instead of every call-site hand-rolling its own retry. Bounded three ways (max rounds AND total\n output ceiling AND a no-growth break), and it never mutates the caller's message history.\n- **`LLMProvider.chat_with_continuation()`** and **`LLMRouter.chat_with_continuation()`** — every\n provider AND the router inherit the primitive, so any call-site gets continuation for free.\n- Kill-switch `ADK_LLM_CONTINUATION=off`; tunables `ADK_LLM_MAX_CONTINUATIONS`,\n `ADK_LLM_MAX_TOTAL_OUTPUT_TOKENS` (defaults: 2 rounds / 8192 tokens).\n\n### Changed\n- The ReAct loop's truncation recovery now uses the shared primitive. The previous inline\n doubling-retry REPLACED the partial response (losing earlier text in `resp.content`) and injected\n continuation scaffolding into the live message history; it now STITCHES and leaves history clean.\n Tool-call turns are never continued as text.\n\n### Included — previously-committed-but-unreleased changes\n- 2.8.2: TLS verification on by default (`verify=False` removed).\n- 2.8.1: baked license + pack-signing public keys.\n- 2.8.0: version-bump release.\n\n## [2.7.0] - 2026-06-05\n\n### Added — provider-agnostic LLM cost levers (at the `LLMProvider` ABC)\n- **Prompt caching.** New `ProviderCapabilities` (`prompt_cache: explicit|automatic|none`, `batch`) and\n normalized `LLMResponse.cache_read_tokens`/`cache_write_tokens`, so the \"tokens saved\" meter never\n depends on which backend served the turn. Anthropic inserts `cache_control` breakpoints over the stable\n system+tools prefix; OpenAI/DeepSeek caching is automatic and their `cached_tokens` are now surfaced\n (previously discarded). Default-on in `AitherAgent.chat`; override with `cache=False`.\n- **ResponseCache** (`adk/llm/cache.py`) — exact-match response cache with a pluggable backend, wired into\n `LLMRouter` as an opt-in (`LLMRouter(response_cache=…)` + per-call `cacheable=True`). OFF by default — no\n determinism change unless opted in.\n- **Gemini provider** (`adk/llm/gemini.py`) with explicit `cachedContents` caching; registered in the router.\n- **Async batch runner** (`adk/llm/batch_runner.py`) — `run_batch()` over the Anthropic Message Batches /\n OpenAI Batch APIs (~50% off) with a transparent fallback to concurrent `chat()` where unsupported.\n- **Graph-RAG retriever interface** (`adk/graph_retriever.py`).\n\n### Added — principal authority (prompt-injection defense enforced outside the model)\n- `adk/auth.py`: `Principal` / `AuthContext` / `PrincipalResolver` (+ deny-all default).\n- `ToolRegistry.execute(name, args, auth=…)` — a default-deny authorization gate at the tool boundary, so a\n forbidden tool call is blocked regardless of what the LLM emits. `ToolDef` gains `required_clearance` /\n `action_class`. Threaded through `AitherAgent.chat`; `auth=None` is fully backward-compatible.\n- `adk/channel_auth.py`: Telegram webhook-secret + CEO-id checks and HMAC-signed approvals.\n\n## [2.6.4] - 2026-06-05\n\n### Fixes\n- **Loop-guard nudges corrupted the message history → DeepSeek 400 mid-run.**\n When the ReAct loop emitted multiple tool calls in one assistant turn and the\n loop guard fired a WARN/BLOCK/CIRCUIT_BREAK on one of them, it spliced a\n standalone `system` nudge message *between* the tool results. DeepSeek (and\n strict vLLM chat templates) require an assistant `tool_calls` message to be\n followed by exactly one `tool` result per `tool_call_id`, contiguously — so the\n interleaved message produced `400 … insufficient tool messages following\n tool_calls message` and aborted the turn (this is reliably hit after crossing\n the 15-call circuit-break threshold, where every subsequent call WARNs).\n Per-call guidance is now folded into that call's own `tool` result, and any\n standalone steering is deferred to a single message AFTER the whole batch —\n preserving the `tool_calls`↔`tool_results` pairing every backend requires.\n Regression test: `deep-research-agent/tests/test_msg_structure.py`\n (loopguard-flood scenario).\n\n## [2.6.3] - 2026-06-04\n\n### Fixes\n- **Transient provider errors killed a run on the first blip.** A single\n `502 Bad Gateway` / `503` / `529 overloaded` / `429` from the upstream API\n aborted the whole request — the Anthropic provider had **no retry at all**, and\n the OpenAI-compatible provider only retried `429`. Both now retry transient\n statuses (`408/429/500/502/503/504/529`) with `Retry-After`-aware exponential\n backoff (3 attempts), and surface the provider's body if all retries are\n exhausted. Non-transient `4xx` (e.g. `400`) still fails fast. Applies to\n `chat` and `chat_stream` on both providers.\n\n## [2.6.2] - 2026-06-04\n\n### Fixes\n- **DeepSeek (and strict OpenAI-compatible backends) 400'd on long tool\n conversations.** The ReAct loop legitimately injects mid-conversation\n `system` steering messages (the \"[DIMINISHING RETURNS]\" hint after several\n low-output tool iterations, loop-guard nudges). OpenAI tolerates a non-leading\n `system` message; DeepSeek rejects it with a 400. Long research runs\n (many `fetch_url` calls) reliably tripped this. Fix: non-leading `system`\n messages are demoted to `user` at the OpenAI-compatible provider boundary\n (`_demote_nonleading_system`) — steering intent preserved, payload valid on\n every backend; leading system prompt(s) untouched.\n- **Opaque HTTP errors.** `resp.raise_for_status()` discarded the provider's\n response body, so a 400/422 surfaced as a bare `Client error '400 Bad\n Request'` with no cause. `_ensure_ok` now includes the provider's actual\n message (e.g. `context_length_exceeded`, `Invalid 'messages'`) in the raised\n error, for both `chat` and `chat_stream`.\n\n## [2.6.1] - 2026-06-04\n\n### Fixes\n- **`MCPServer.mount()` returned HTTP 422 for every `/mcp` call.** Under\n `from __future__ import annotations` the route handler's `request: Request`\n annotation is a string that FastAPI resolves against the *module* globals, but\n `Request` was imported locally inside `mount()` — so FastAPI couldn't resolve\n it and treated `request` as a required query param. The MCP server endpoint\n (`initialize`, `tools/list`, `tools/call`) was unusable when mounted. FastAPI\n symbols are now imported at module level (guarded; the `[node]` extra still\n gates actual use). Any agent's tools can now be exposed as a working MCP\n server with `MCPServer(...).mount(app)`.\n\n## [2.6.0] - 2026-06-04\n\n### Streaming + scaffolding\n- **`AitherAgent.stream_chat`** — reliable streaming sibling of `chat()`: native\n tool-calling loop (concludes on its own, unlike the text-protocol `stream_react`)\n that emits live `tool`/`tool_result` events and streams the answer as `token`\n events. `token_delay` paces tokens for a typing effect.\n- **`adk new <template>`** — scaffold a full, runnable template app. Ships the\n `deep-research` template (a cited web-research agent: pack + server + web UI):\n `adk new deep-research`.\n\n### Fixes\n- **BYO-key no longer community-capped.** The free-tier monthly token cap now applies\n only to the metered Aitherium gateway backend, never to a user's own provider key\n (Anthropic/OpenAI/DeepSeek/Ollama/vLLM). Previously it could brick a self-hosted\n agent after ~100k tokens on the user's own key.\n- **Stronger, grounded memory recall.** `chat()` now injects the top-6 knowledge-graph\n hits (untruncated) with a hard no-fabrication instruction — was top-3 truncated to\n 200 chars, which surfaced partial facts and let weaker models invent the rest.\n- **Tool-arg coercion.** `ToolRegistry.execute` coerces LLM-supplied args toward each\n parameter's annotated type, so a tool no longer crashes when a model passes a list\n or string where an int/float/bool was expected.\n\n### Docs\n- New `docs/AGENT_DEV_GUIDE.md` — the golden path for building agents/packs.\n\n## [2.5.0] - 2026-06-04\n\n### Continual learning — agents that learn from runs and reuse memory\n\nAgents now improve the second time they do a task instead of starting from scratch.\n\n### Added\n- **Recall-before**: `AitherAgent.chat()` searches the local `SkillStore` and injects the\n top matching learned skills as a system block, so the model reuses proven procedures.\n- **Learn-after**: `_learn_after()` runs after each response — reinforces (`success_count++`)\n any skills that were recalled+reused, and extracts + saves a NEW skill from successful\n multi-tool runs via `SkillExtractor`. Wired at both `chat()` return paths.\n- Gated by `AITHER_SKILLS` (default on). The previously-unused `SkillStore`/`SkillExtractor`\n are now connected to the run loop.\n\n### Notes\n- Pairs with the platform-side LearnedProcedure substrate (recipes → scored procedures →\n skills/tools/A2A/packs → MCTS priors → evolution). The SDK shares memory via the Spirit\n bridge when configured.\n\n## [2.0.0] - 2026-06-02\n\n### Open-core boundary (BREAKING)\n\nThis release draws a single, enforced free/paid line. The free tier stays\ngenuinely useful (real agent, typed + graph + code memory, ReAct, ~essential\ntools); paid tiers unlock the *scale* capabilities via portal.aitherium.com.\n\n### Added\n- **`adk/licensing.py`** — the entitlement keystone. Resolves a tier\n (COMMUNITY by default), verifies optional portal-signed `~/.aither/license.json`\n (Ed25519), and answers `can_use_fleet/channels/cron/swarm/auto_neurons`,\n `is_agent_licensed`, `max_effort`, etc. **Fail-closed**: no/invalid/expired\n license → free tier; an unsigned license never grants premium.\n `AITHER_TENANT_SLUG=aitherium` → unrestricted INTERNAL tier.\n- **Moat guard** (`scripts/check_moat_boundary.py`) wired into the publish\n workflow — blocks any wheel that leaks the moat or drops the keystone.\n- Tests: `tests/test_licensing.py`, `tests/test_moat_boundary.py`.\n\n### Changed (gating — fail-closed)\n- Free tier effort is capped at 3 (reasoning effort 7–10 needs Professional).\n- Monthly token budget hard-block on the free tier (no silent overspend).\n- Gated behind a paid tier: fleet mode (`FleetConfig`), cross-agent delegation\n (`AgentForge.delegate`), channel adapters, cron scheduler, proactive\n auto-neurons, and `swarm`/`swarm_code` dispatch.\n\n### Removed\n- **`adk/nanogpt.py`** (on-device training IP) removed from the published SDK\n and the public repo; relocated to the internal AitherOS moat. Excluded from\n the wheel as defense-in-depth.\n- Pre-2.0 releases shipped this IP and ungated capabilities and are being\n yanked (PyPI) / removed (GitHub releases). See `scripts/yank_leaky_releases.py`\n and `scripts/purge_public_leaks.sh`.\n\n## [1.20.0] - 2026-05-27\n\n### Fixed\n- **Pack discovery path mismatch** — `_find_local_packs()` now scans both\n `~/.aither/packs/` and `~/.aitheros/packs/`, so packs installed via\n `adk pack install` are discovered by `adk-workspace`.\n\n### Added\n- **Auto-register on pack install** — `adk pack install` now auto-registers\n with the portal if the pack contains an `agent.yaml` with a `portal` section.\n One command does download + extract + register.\n- **Flux event on agent registration** — `developer_portal.py` emits\n `agent.registered` Flux event so the portal fleet UI updates in real-time.\n\n## [1.15.0] - 2026-05-26\n\n### Added\n- **Cloud memory** — `adk quickstart --cloud` now tests gateway memory and\n auto-configures Spirit URL so memories persist across devices via\n `gateway.aitherium.com/v1/memory/teach` and `/v1/memory/recall`.\n- **Config→env export for cloud mode** — `Config.from_env()` now exports\n `AITHER_CLOUD_MODE`, `AITHER_SPIRIT_URL`, and spirit path env vars from\n saved config so Memory and other modules pick them up without needing\n explicit env vars set by the user.\n- **Cloud Quick Start section** in README — 3-command setup for users who\n just want a Claude/GPT-4/DeepSeek API key and the full agent harness.\n- **`--memory` profile for node deployment** — `adk deploy node --memory`\n adds Spirit (8087) + WorkingMemory (8101) containers for persistent\n vector memory. Without this, agents use local SQLite only.\n- **Configurable vLLM in node compose** — `AITHER_VLLM_MODEL`,\n `AITHER_VLLM_GPU_UTIL`, `AITHER_VLLM_CTX_LEN`, `AITHER_VLLM_QUANT_ARGS`\n env vars let `adk setup` write GPU-detected tier settings to `.env`\n and have the node compose pick them up (TQ4 for small GPUs, BNB for large).\n\n### Fixed\n- **Memory cloud routing** — `Memory._spirit_teach()` and `_spirit_recall()`\n now use configurable paths and auth headers for gateway proxy. Previously\n cloud-only users silently lost all Spirit memory calls.\n\n## [1.9.0] - 2026-05-24\n\n### Added\n- **Composable ToolPack system** — agents can load tool packs from\n `.toolpack.yaml` directories. Drop a pack in `~/.aitheros/packs/` or\n set `AITHER_PACKS_DIR`, and any agent discovers it automatically.\n- **`tools.packs` in agent.yaml** — list tool pack IDs to auto-load\n (e.g. `packs: [crm-tools, calendar-pro]`).\n- **`register_tool_packs(agent, pack_ids, packs_dir)`** in `builtin_tools` —\n programmatic pack loading for agents.\n- **`AITHER_TOOL_PACKS` env var** — comma-separated pack IDs loaded on\n agent init (alternative to YAML config).\n- **Unknown builtin categories resolve as pack IDs** — `tools.builtin:\n [file_io, crm-tools]` now works: `crm-tools` is looked up as a pack.\n\n## [1.8.0] - 2026-05-24\n\n### Removed\n- **11 dead modules** (3,023 lines) — `agent_deploy`, `cloud_deploy`, `create`,\n `phonehome`, `sdk_bridge`, `sovereign`, `telemetry`, `telemetry_config`,\n `error_reporter`, `anonymizer`, `package`. None were imported anywhere.\n- **29 ghost exports** from `__init__.py` — `secrets`, `otel`, `fs_sandbox`,\n and `vector_memory` modules were listed in `__all__` and `__getattr__` but\n the backing files never existed. Accessing them raised `ModuleNotFoundError`.\n- **Stale dist/ artifacts** — removed old wheels/tarballs (v0.4.0–v1.4.1, 11 MB).\n- **Orphan test scripts** — `test_phase1.py` and `test_phase23.py` (one-off\n validation scripts not part of the pytest suite).\n\n### Fixed\n- **Version mismatch** — `__init__.__version__` now matches `pyproject.toml`\n (was stuck at 1.6.0).\n\n## [1.5.0] - 2026-05-23\n\n### Added\n- **`--sovereign` flag for `adk deploy node`** — registers the node with the\n Aitherium hub via federation after deployment. Saves federation credentials\n to `~/.aither/.env.federation`. Accepts `--hub` (custom hub URL) and\n `--tenant` (tenant slug) options. Non-fatal: node runs standalone if\n registration fails.\n- **Federation → fleet cross-post** (Genesis) — sovereign nodes that register\n or heartbeat via `/federation/register` and `/federation/heartbeat` now\n automatically appear in the fleet dashboard (`/fleet/deployments`) with\n `node_type: \"sovereign\"` and live status/metrics.\n- **\"Deploy Sovereign Node\" section on Connect page** (AitherVeil) — Section 8\n with ADK deploy commands, fleet dashboard links, and provision page links.\n- **`deploy-node` action in tunnel proxy** (AitherVeil) — Connect page and\n external callers can now request bootstrap scripts via\n `POST /api/tunnel/devworkspace?action=deploy-node`.\n\n### Fixed\n- **Fleet provision page** — API URL corrected from `/api/fleet/provision` to\n `/api/bridge/genesis/fleet/provision` (matches bridge proxy pattern).\n- **Fleet detail page** — API URL corrected from `/api/fleet/deployments/` to\n `/api/bridge/genesis/fleet/deployments/` (same fix).\n- **Dockerfile** — entry point corrected from `aither-serve` to `adk-serve`\n (matches `pyproject.toml` script registration). Version label updated.\n\n## [1.4.1] - 2026-05-22\n\n### Fixed\n- **macOS executable builds** — dropped `--target-arch universal2` from\n `packaging/build_executable.py`. PyPI wheels for `pydantic_core` and other\n native deps are arch-specific, not fat binaries, so universal2 always failed\n with `IncompatibleBinaryArchError`. Now builds for the runner's native arch\n (`aither-macos-arm64`).\n- **Release workflow strategy** — added `fail-fast: false` so a single\n executable build failure no longer cancels the other OS jobs.\n- **PyPI publish** — added `skip-existing: true` so re-runs of the same\n version don't fail when the artifact already exists.\n- **GitHub Release job** — now runs whenever `pypi` succeeds even if some\n executable matrix entries fail, so the release page is always created with\n whatever binaries did build successfully.\n\n## [1.4.0] - 2026-05-22\n\n### Added\n- **Sovereign template now ships with `agent_core` 5-tier memory** — generated\n backends include the canonical `agent_core/` package (working → episodic →\n semantic → procedural → identity) plus `agent_memory.py` helper, vendored\n from `portal-kit-backend`.\n- `aiosqlite>=0.20.0` added to sovereign template `requirements.txt` for\n local agent_core SQLite storage.\n- Document tombstoning: `evict_cached_conversations_referencing` is wired\n through document-delete handlers in consumer apps so memories citing\n deleted documents are blocked from recall.\n- New tests: 13 smoke tests for `portal-kit-backend/agent_core` covering\n promotion thresholds, decay rates, store roundtrip, tombstone recall\n blocking, circuit breaker state machine, and chat-turn recording.\n\n### Changed\n- `scripts/vendor_agent_core.py` now syncs canonical `agent_core` to 4\n targets: WorkspaceRuntime, ADK sovereign template, and the GargBot +\n Chelle consumer mirrors (previously only 2).\n\n## [1.2.1] - 2026-05-21\n\n### Added\n- `adk train` command group — 6 subcommands: status, launch, logs, cancel, runs, register-gpu\n- `/slash-commands` endpoint — ADK server auto-exposes all 35 CLI commands as structured JSON manifest for AitherShell\n- `/cli/execute` endpoint — AitherShell can run any CLI command through the server\n- `build_command_manifest()` — introspects full argparse tree (commands, args, choices, subcommands)\n- `_register_commands()` extracted from main() for shared parser construction\n\n### Changed\n- `_get_genesis_url()` now probes: Genesis → ADK server → Aitherium cloud gateway (works without Genesis)\n\n## [1.2.0] - 2026-05-21\n\n### Added\n- **Runtime backend switching** — `agent.switch_backend(\"deepseek\")` and `agent.llm.switch_backend()` change provider without recreating agent\n- **Hybrid reasoning** — `agent.set_reasoning_backend(\"anthropic\")` routes effort 7+ to cloud API while keeping local orchestrator\n- **First-class DeepSeek provider** — `deepseek` in effort model table with `deepseek-chat` and `deepseek-reasoner`\n- **TQ4 quantization tiers** — `nano` (6GB, TurboQuant 4-bit), `standard-tq4` (12GB, both models TQ4), `hybrid-tq4` (6GB + cloud reasoning)\n- **`adk backend` CLI** — `list`, `set`, `set-reasoning`, `test` subcommands\n- **`adk quickstart`** — unified first-run wizard (setup + auth + shell)\n- **`adk tools`** — list local + MCP tools with tier markers\n- **`adk backup`** — tarball export of `~/.aither/`\n- **`adk ingest`** — feed docs/files into knowledge graph\n- **`adk setup nemotron`** shortcut — alias for `--tier lite`\n- **`--reasoning-api`** flag in setup — `anthropic`, `openai`, `deepseek`, `gateway`\n- **`--dgx-spark URL`** flag — remote vLLM endpoint configuration\n- **DGX Spark auto-detection** — scans `spark.local`, `192.168.0.33`, `AITHER_DGX_URL` env\n- **Post-setup smoke test** — sends real inference request after container startup\n- **Schema migration system** — version table + migration runner in `memory.py` and `graph_memory.py`\n- **Configurable vLLM port scan** — `AITHER_VLLM_PORTS` env var for custom ports\n- **`aithershell` entry point** — alias for `adk` in pyproject.toml\n- Shell auto-downloads binary on first `adk shell` (no `--install` needed)\n- Shell pre-flight check warns if no backend detected\n- Shell passes full config via env vars (API key, tenant, inference URL)\n- DGX/Remote and Cloud API checks added to `adk doctor`\n- Config fields: `reasoning_backend`, `reasoning_api_key`, `deepseek_api_key`, `dgx_url`, `vllm_extra_ports`\n\n### Fixed\n- `test_aeon_chat_basic` timeout — mock scope was exiting before request\n- `adk doctor` check_disk timeout — cap rglob at 5000 files\n\n## [1.1.7] - 2026-05-19\n\n### Added\n- Remote agent pipeline — `adk onboard` → fleet dispatch → remote inference\n- Agent-to-agent federation with mesh relay\n- `adk connect` for desktop mesh enrollment\n\n## [1.1.6] - 2026-05-19\n\n### Fixed\n- Tool call execution recovery across all backends\n- Hermes tool call fallback parsing for vLLM\n\n## [1.1.5] - 2026-05-19\n\n### Fixed\n- Tool call execution + Hermes fallback parsing improvements\n\n## [1.1.0] - 2026-05-18\n\n### Added\n- Multi-channel gateway (Telegram, Discord, Slack, webhook)\n- Aeon group chat with 7 presets\n- Skills auto-extraction from multi-step sessions\n- MCP stdio server for Claude Code integration\n- `adk gateway`, `adk cron`, `adk skills`, `adk soul` commands\n- SOUL.md import/export for portable agent identity\n- Elysium cloud device-flow authentication\n\n## [1.0.0] - 2026-05-18\n\n### Breaking\n- Package consolidated — `aither_adk` namespace replaced by `adk` + `adk.platform`\n- 93 duplicate files removed from `AitherOS/packages/aither_adk/`\n\n### Added\n- Standalone core primitives (`adk.core`: Agent, Tool, Memory, ModelBackend, Capability, Trace)\n- `adk.platform` sub-package merging all platform toolkit modules\n- Compatibility shims for legacy `from aither_adk.*` imports\n\n### Changed\n- Version synced across pyproject.toml, `adk/__init__.py`, `adk/platform/__init__.py`, npm package\n- Classifier promoted from Alpha to Beta\n\n## [0.16.0] - 2026-04-16\n\n### Swarm Coding Engine & Repowise Integration\n- **`repowise_search` tool** — Semantic + keyword hybrid code search via Repowise, with ripgrep fallback\n- **`swarm_code` tool** — Dispatch complex tasks to 11-agent swarm pipeline (ARCHITECT->SWARM->REVIEW->JUDGE)\n- **`agent.swarm()`** — Async convenience method with configurable mode, effort, timeout\n- **`agent.code_search()`** — Async convenience method returning structured results\n- New tool categories: `repowise` and `swarm` in TOOL_CATEGORIES\n- `repowise_search` and `swarm_code` added to `__init__.py` exports with lazy loading\n- `IDENTITY_DEFAULTS` updated: repowise for code-focused agents, swarm for orchestration agents\n- Standalone graceful degradation: repowise falls back to ripgrep, swarm returns structured error\n\n## [0.13.0] - 2026-04-02\n\n### Graph Faculties — Local Knowledge for Every Agent\n- **CodeGraph** (2,799 lines) — Full Python AST indexer with call graph, keyword/semantic/hybrid query, embedding matrix cache, incremental re-indexing, multi-hop chain expansion\n- **MemoryGraph** (1,339 lines) — Graph-based persistent agent memory with 10 edge types, hybrid query (keyword + semantic + graph expansion), multi-hop recall, pickle persistence\n- **EmbeddingProvider** — 4-backend fallback chain: sentence-transformers (GPU/CPU) -> Ollama -> Elysium cloud -> feature hashing (zero deps)\n- **BaseFacultyGraph** — Abstract base with HMAC-SHA256 validated pickle persistence\n\n### Agent Integration\n- `agent.set_code_graph(cg)` — auto-registers `code_search` + `code_context` tools\n- `agent.set_memory_graph(mg)` — auto-registers `remember` + `recall` + `memory_stats` tools\n- Both graphs inject context into LLM prompts automatically during chat\n\n### Zero-Config Onboarding\n- `adk start` / `adk` (no args) — auto-detect project, index code, detect LLM, persistent memory, interactive chat\n- Works for any directory: Python codebases, doc folders, mixed workspaces\n- Auto-detects LLM: Ollama -> vLLM -> Elysium -> OpenAI -> Anthropic\n- Per-project persistent memory in `~/.aither/memory/<project>`\n- `adk index <path>` — standalone indexing with progress bar and stats\n\n### MCP Gateway\n- `POST /v1/embeddings` — OpenAI-compatible embedding proxy to vLLM-embeddings:8209\n- Available to all tiers (embeddings are free)\n- ADK EmbeddingProvider uses this as Elysium cloud fallback\n\n### Optional Dependencies\n- `pip install aither-adk[graphs]` — numpy for 10x cosine similarity speedup\n- `pip install aither-adk[embedding]` — sentence-transformers + torch for local GPU embeddings\n- `pip install aither-adk` alone — graphs work with feature hashing (zero deps)\n\n## [0.12.0] - 2026-04-01\n\n### Bootstrap & Service Discovery\n- Version handshake -- ADK checks major.minor compatibility with Genesis/Node\n- Background reconnect loop -- ServiceBridge re-probes every 30s in standalone mode\n- Port 8080/8090 documented -- MCP vs OpenAI-compat clearly separated\n- Genesis URL configurable via GENESIS_URL env var (was hardcoded)\n- Standalone mode warning -- visible stderr alert when AitherOS not detected\n- Auto-reconnect on startup when services come online\n\n### Elysium Cloud Inference\n- Unified gateway URL -- gateway.aitherium.com handles auth + billing + inference\n- Streaming inference -- SSE passthrough for /v1/chat/completions with stream=true\n- Auth proxy routes -- /v1/auth/register, /v1/auth/login, /v1/auth/me\n- Billing proxy -- /v1/billing/balance through gateway\n- AitherConnect Elysium fallback -- cloud inference when local Genesis is down\n- AitherDesktop Elysium fallback -- third-tier chat fallback after Node\n\n### Infrastructure\n- /discovery endpoint on Genesis -- unified service URLs/versions/health\n- /api/config/services on Veil -- runtime port config for client-side JS\n- Veil healthcheck gates on Genesis -- unhealthy when backend is down\n- Desktop crash detection 90s->60s (threshold 3->2)\n\n## [0.11.0] - 2026-04-01\n\n### Agent Execution Quality (Claude Code Parity)\n- Raise loop guard block threshold from 3 to 4 -- agents get more room for iterative search\n- Soft synthesis nudge for effort >= 4 (no tool stripping, trust the model)\n- max_output_tokens escalation -- retry up to 3x when response is truncated\n- Tool result pairing guarantee -- synthetic error for orphaned tool_use blocks\n- Micro-compaction of old tool results -- save context tokens on long sessions\n- First-turn tool forcing only for effort >= 6 (trust model for lower effort)\n- Diminishing returns detection -- nudge agent when 3+ turns produce < 500 tokens\n- Message normalization -- merge consecutive same-role messages, strip empties\n- LLM retry with exponential backoff (5 retries, 500ms-16s, jitter)\n\n## [0.9.0] - 2026-03-16\n\nThe \"connected world\" release. Cross-platform identity pairing, voice capabilities, and multi-channel integration.\n\n### Added\n- **Pairing**: Cross-platform identity linking (`adk/pairing.py`)\n - `PairingManager` — SQLite-backed identity linking with 6-char pairing codes\n - Link users across Telegram, Discord, Slack, WhatsApp with 10min TTL codes\n - Canonical session IDs for cross-channel conversation continuity\n - `get_session_id()` returns \"user-{id}\" for paired users\n- **Voice**: Speech-to-text and text-to-speech client (`adk/voice.py`)\n - `VoiceClient` — async STT/TTS/emotion via AitherVoice service\n - Convenience functions: `hear()`, `say()`, `feel()`\n - 6 voice options: alloy, echo, fable, nova, onyx, shimmer\n - Emotion detection with intensity scoring\n- New exports: `PairingManager`, `PairingResult`, `PlatformIdentity`, `VoiceClient`, `TranscriptionResult`, `SynthesisResult`, `EmotionResult`\n\n### Changed\n- `__init__.py` exports expanded with pairing and voice symbols\n\n## [0.6.0] - 2026-03-13\n\nThe \"group mind\" release. Multi-agent group chat, creative tools, and Iris identity.\n\n### Added\n- **Aeon**: Multi-agent group chat engine (`adk/aeon.py`)\n - `AeonSession` — persistent group chat with parallel agent execution\n - 7 presets: balanced, creative, technical, security, minimal, duo_code, research\n - Orchestrator synthesis: Aither summarizes all agent responses\n - Serial execution for Ollama, parallel for vLLM/cloud\n - ConversationStore persistence with `type: \"aeon\"` metadata\n - `group_chat()` one-shot convenience function\n- `aither aeon` CLI command — interactive terminal group chat with color-coded agents\n - `-p/--preset`, `-a/--agents`, `-r/--rounds`, `--no-synthesize` flags\n - `reset` and `quit` commands\n- Server endpoints: `POST /aeon/chat`, `GET /aeon/presets`, `GET /aeon/sessions/{id}`\n- Creative tools in builtin_tools: `image_generate`, `image_refine`, `image_search`, `video_generate`\n- Iris agent identity with visual generation capabilities\n- 48 Aeon tests (data models, presets, chat, context, persistence, server, exports)\n\n### Changed\n- `__init__.py` exports: `AeonSession`, `AeonResponse`, `AeonMessage`, `group_chat`, `AEON_PRESETS`\n\n## [0.5.0] - 2026-03-13\n\nThe \"tenant-ready\" release. Multi-tenant admin, permission grants, safety profiles, and a full setup wizard.\n\n### Added\n- `aither setup` interactive setup wizard with hardware detection, model selection, identity config\n- Strata storage backend: SQLite WAL persistence for conversations, memories, knowledge graphs\n- CLI test runner: `aither test` with auto-discovery and parallel execution\n- Permission grants system for MCP tool access control\n- MCP account management tools\n- LLM provider auto-detection for Ollama and vLLM endpoints\n- Apache-2.0 LICENSE file\n- Elysium desktop sync module\n- Comprehensive CLI test suite (493 tests)\n- Strata storage test suite (972 tests)\n- LLM provider tests (82 tests)\n\n### Changed\n- CLI expanded: `aither setup`, `aither test`, `aither bugreport`, `aither doctor`\n- Strata module rewritten as full local-first storage engine\n- Server startup includes Strata initialization\n- README rewritten with clearer quickstart and architecture docs\n\n### Fixed\n- Dead documentation links\n- Python 3.11 compatibility issues\n- Ruff per-file-ignores configuration\n- Identity provisioning edge cases\n- Docker node-gyp native dependency builds (python3 + build tools)\n\n## [0.4.0] - 2026-03-13\n\nThe \"own your AI\" release. Self-hosted agent OS for people who don't want their data on someone else's servers.\n\n### Added\n- Muse agent identity (creative/artistic generation)\n- Port 8120 to vLLM scan for ExoNodes discovery\n- Public roadmap with milestone-based porting schedule\n- Competitive positioning: self-hosted alternative to cloud-locked AI appliances\n\n### Changed\n- LLM router: compute-aware effort routing (replaces effort-level context gating)\n- README rewritten for sovereignty-first messaging\n- Package description updated\n- Promoted from alpha to stable release\n\n### Fixed\n- Version string consistency between pyproject.toml and __init__.py\n- vLLM port scanning now includes port 8120 (ExoNodes)\n\n## [0.3.1] - 2026-03-09\n\n### Added\n- GraphMemory: SQLite knowledge graph with Ollama embeddings and hybrid search\n- NeuronPool: auto-fire pre-LLM data gathering agents\n- NanoGPT: pure Python char-level transformer with LoRA fine-tuning\n- Safety gates: IntakeGuard (input), LoopGuard (recursion), Sandbox (code exec)\n- Event system: EventEmitter with chat/tool/forge event types\n- Builtin tools: identity-based tool selection\n- ServiceBridge: auto-discovery of AitherOS services\n- Streaming: chat_stream with safety gate integration\n- Auth middleware: Bearer token authentication\n- CLI: `aither init` and `aither serve` commands\n\n### Changed\n- Wired 5 previously disconnected modules into agent loop\n- 522 total passing tests (up from 85)\n\n## [0.3.0] - 2026-03-07\n\n### Added\n- Clean-room agent development kit\n- Multi-backend LLM providers (Ollama, OpenAI, Anthropic)\n- AitherAgent class with @tool decorator\n- SQLite conversation memory\n- OpenAI-compatible server (`aither-serve`)\n- 16 agent identities as package data\n- MCP bridge to mcp.aitherium.com\n- Privacy-centric opt-in telemetry\n- Bug reporting CLI and API\n- FastAPI server with OpenAI-compatible endpoints\n- Hardware auto-detection (5 tiers, 11 profiles)\n- Fleet orchestration and multi-agent coordination\n- A2A mesh protocol\n- Federation protocol for cross-instance agent dispatch\n\n### Initial release\n- 85 passing tests\n- Apache-2.0 license\n"}, "7b640660c97a667912896e321ac07bbe88297963": {"t": 1785748175.7686262, "n": 77957, "h": "b39bf9b2c0328252", "c": "# Changelog\n\nAll notable changes to aither-adk will be documented in this file.\n\n## [2.22.0] - 2026-07-14\n\n### Changed — internal toolkit relocation\n\n- **`adk.platform` relocated** — the internal aither-platform toolkit (ComfyUI/image-gen\n pipelines, fleet agent clients, parallel CLI/UI/tools stack) has been moved out of the\n adk SDK tree to the standalone internal `aither-platform` package at\n `AitherOS/agents/aither_platform/`. The public package remains unaffected (it already\n excluded `adk.platform` since 2.21.1). Internal AitherOS agent builds now install\n `aither-platform` separately. `adk platform` falls through to the same \"not available\"\n message on public installs; internal builds work via the relocated package.\n\n### Removed — backward-compat re-export deleted\n\n- **`adk.gateway` module removed** — the backward-compatibility re-export of `GatewayClient`\n from `adk.gateway` has been removed. Use `from adk.client import GatewayClient` instead\n (the established public API since 2.21.0). All internal importers have been updated.\n\n## [2.21.1] - 2026-07-14\n\n### Removed — internal toolkit and license-gated product no longer ship publicly\n\n- **`adk.platform`** — the internal aither-platform toolkit (ComfyUI/image-gen\n pipelines, fleet agent clients, a parallel cli/ui/tools stack) is no longer\n included in the public repo or the PyPI wheel. It was internal-only code that\n had been riding along since the aither-platform merge. `adk platform` now\n reports \"Platform toolkit not available\" on public installs; internal AitherOS\n builds are unaffected.\n- **`adk.formbridge`** — the FormBridge form-automation product is license-gated\n (PROFESSIONAL+); its implementation no longer ships in the free package. All\n import sites (`builtin_tools`, `server` route mount) already degrade behind\n `except ImportError`, so nothing else changes behavior.\n- Product test suites (`test_formbridge.py`, `test_encounter_keying.py`,\n `test_flow_tools.py`) are excluded from the public sync alongside them.\n\n## [2.21.0] - 2026-07-14\n\n### Added — Completion gate: verified-or-retried task execution\n\n- **`adk.gate.CompletionGate`** — a composable wrapper that closes the \"silent\n success\" gap: an agent's ReAct loop returns whatever the model produced and\n calls it done (`finish_reason = stop/max_steps/length`), which is a *liveness*\n signal, not a *completion* one — so an agent can \"succeed\" while doing nothing.\n `CompletionGate` runs the agent, verifies the result against acceptance\n criteria, retries with the failure fed back, and returns the response with\n `finish_reason` set to `verified` or an honest `unverified`. Never a false pass.\n - **Hard-first, un-soft-passable checks** (`adk.gate.hard_checks`): a filesystem\n path a criterion says must exist is checked on disk; a required quoted token\n must appear in the output. Only genuinely subjective criteria fall through to\n an optional LLM judge (reuses the agent's own model), which **fails closed**\n if unavailable.\n - **`gated_run(agent, task, criteria=..., max_retries=2)`** — one-shot helper.\n - `auto_criteria` (LLM writes the machine-checkable definition-of-done from the\n task), a `verifier=` override for custom checks (tests-pass / HTTP probe), and\n a `requires_action` short-circuit so it never fights human-approval pauses.\n - Compose it onto any agent exposing `run(task) -> resp{.content, .finish_reason}`\n (`AitherAgent`, `core.Agent`, or a thin adapter over a remote dispatch).\n - Exported from the top-level package: `from adk import CompletionGate, gated_run`.\n\n## [2.19.0] - 2026-07-12\n\n### Added — Eve agent importer + durable A2A tasks + canonical agent discovery\n\n- **`adk pack import <eve_agent_path>`** — convert external Eve agents to AitherADK packs\n (from GitHub or local filesystem). Maps Eve structure (instructions → system prompt,\n agent.ts → agent.yaml, skills/*.md → skills, connections/*.ts → MCP, tools/*.ts →\n tools/node verbatim) to AitherADK format with a single command. Eve agents (compiled\n manifest format, schema v35) become installable packs compatible with `adk pack install`.\n- **Durable A2A task lifecycle (`TaskManager` backed by FileStore)** — agent-to-agent\n tasks now survive container restarts. Tasks persist as append-only JSONL\n (`.adk/tasks.jsonl`); in-memory dict for fast access; `create_task()` / `update_status()` /\n `add_artifact()` / `add_message()` all save to disk. Google A2A v0.3.0 spec compliant.\n- **Canonical agent card discovery** — `A2AServer.mount()` now exposes:\n - `GET /.well-known/agent-card.json` — Agent Card (canonical; contains agent identity,\n capabilities, skills, endpoints)\n - `GET /.well-known/agent.json` — Agent Card (legacy, redirects with 308 to canonical path)\n - `POST /a2a` — JSON-RPC 2.0 for message send/task lifecycle\n - `GET /a2a/tasks/{id}/subscribe` — SSE streaming task updates\n- **Four built-in agent packs now complete and catalogued**:\n - `hermes` — architecture review & trade-off analysis\n - `openclaw` — web research & source verification\n - `claude-code` — feature development & debugging\n - `analyst` — structured data inference & anomaly detection\n All discoverable via `adk packs` / `adk install pack:<name>`.\n- **Tolerant tool-call JSON parsing** — malformed tool JSON (extra properties, missing\n name/arguments, string-encoded nested args, missing keys) is now gracefully degraded\n instead of silently failing. `_parse_tool_json()` accepts both `\"arguments\"` and\n `\"parameters\"` keys, coerces string-encoded args, and skips unparseable calls instead\n of crashing the turn.\n\n### Known limitations\n\n- Eve agent importer: remote GitHub fetching not yet implemented (local paths only).\n- Durable task store: currently JSONL (suitable for thousands of records, not millions).\n\n## [2.18.0] - 2026-07-10\n\n### Added — AitherShell command center + Claude Code session management\n\n- **`aither sessions`** — Claude Code session manager: interactive full-screen\n browser (type-to-filter, transcript preview, deep full-text `search`, resume\n **in the current terminal** or as Windows Terminal tabs), crash guard\n (`guard install` = at-logon watchdog that snapshots live sessions and\n auto-restores the whole set after a terminal crash), and `ingest [--watch]`\n (incremental, secret-guarded sync of session conversations into the local KB\n / CompanyBrain via the standard ingest pipeline).\n- **`aither hq`** — command-center dashboard: per-service fleet health, LLM\n queue (depth/VRAM/models), Pulse alerts, live session count, inbox unread —\n auto-refreshing, with one-key jumps into chat, sessions, inbox, agents,\n brief, watchtower, and docker recovery.\n- **`aither inbox`** — unified queue over CommCore mail, Relay\n mentions/notifications, and Pulse alerts; open/mark-read/DM-reply from the\n terminal. Every source degrades independently.\n- **`aither agents`** — roster console + `agents ask -e <1-10> <agent> <q>`\n (effort-tiered ask via Genesis, with automatic `/chat/stream` fallback when\n the `/agent/sync` execute gate rejects host callers) + forge dispatch\n inspection + routine health.\n- **`aither palette`** — universal fuzzy picker across actions, sessions, and\n services; **`aither brief`** — renders Atlas's executive briefing in the\n terminal; **`aither watch`** — fleet watchtower with named wedge-signature\n detection (docker-WSL wedge, LLM-queue stall, crash-loop uptime regression)\n and optional docker auto-recovery.\n- All fleet reads go through a shared fail-soft `FleetClient` (internal-CA\n TLS via `adk._tls`, dashboard-grade timeouts, per-source degraded states).\n- Node shell-cli (`@aitherium/shell-cli` 1.13.0): `aither\n sessions|hq|inbox|palette|brief|watch|agents|docker` now pass through to the\n Python shell instead of being intent-classified as chat prompts.\n\n## [2.17.1] - 2026-07-05\n\n### Fully self-service mesh join — no customer key handling\n\n- **Auto-join headscale off the onboard response.** The conductor now auto-issues a\n headscale pre-auth key for NAT'd nodes and returns it in the `/v1/mesh/onboard`\n response; `mesh.join` consumes it and brings up the tunnel automatically. A customer\n runs one command — they never mint, fetch, or set a headscale key. Explicit\n `--headscale-key`/`AITHER_HEADSCALE_AUTH_KEY` still take precedence.\n- **Security:** `_tailscale_up` now redacts the auth key from all logs and exceptions\n (a failed `tailscale up` previously echoed the full argv, leaking the key into logs).\n\n## [2.17.0] - 2026-07-05\n\n### Added — Headscale mesh transport + local Qdrant + AitherConnect onboarding\n\n- **Headscale NAT-friendly mesh transport.** When raw WireGuard UDP:51820 is not\n viable (customer boxes behind NAT, corporate firewalls, CGNAT), `adk mesh join\n --headscale` routes the mesh tunnel through Tailscale's Headscale control plane.\n The overlay IP is still Conductor-assigned (10.77.0.0/16); Headscale provides\n only the transport layer. Configured via `AITHER_MESH_TRANSPORT=headscale`,\n `AITHER_HEADSCALE_URL` (default: https://headscale.aitherium.com), and\n `AITHER_HEADSCALE_AUTH_KEY` (pre-generated key). Automatic fallback to raw\n WireGuard if Headscale setup fails.\n- **Local Qdrant vector DB provisioning.** `adk stack` now provisions Qdrant\n locally when `AITHER_VECTOR_DB=qdrant` is set, enabling offline-first RAG\n without external dependencies. Tiered: local Qdrant in-fleet, cloud Nexus\n for enterprise multi-tenant deployments.\n- **Conductor URL public fallback.** `adk mesh join` resolves the internal\n `aitheros-conductor:8193` hostname; if unreachable (e.g., self-hosted nodes),\n it falls back to the public `conductor.aitherium.com:8193` endpoint,\n eliminating bootstrap configuration friction.\n\n### Documentation\n\n- **Self-hosting runbook.** New `docs/SELF_HOSTING.md` covers deploying AitherOS\n on customer infrastructure with Qdrant + mesh + AitherConnect onboarding.\n- **Mesh transport constraints documented.** Raw WireGuard requires public\n UDP:51820 endpoint (cloud instances, dedicated hardware); Headscale for\n NAT'd networks. Noted in CLI help and module docstrings.\n\n## [2.15.0] - 2026-07-04\n\n### Added — built-in web admin console + portal-profile settings sync\n\n- **Web admin console.** The page `adk up` opens (served at `/` and `/ui`) is now a full,\n self-contained console — Chat plus six admin tabs: **Backend** (view/switch LLM provider,\n set keys, test connection), **Packs** (list/enable/disable/reload tool packs),\n **Sessions** (browse/delete conversations), **Logs** (redacted agent-log tail),\n **Graph** (knowledge-graph search/neighborhood/stats), and **MCP** (add/remove external\n MCP servers). No build step, no CDN — a single packaged HTML asset. The original minimal\n streaming chat page stays at `/chat` for back-compat.\n- **Admin API (`adk/admin_api.py`).** ~20 endpoints under `/admin/*`, all bearer-gated by the\n existing middleware (never in `_skip_auth_paths`). Operates on the live agent — LLM backend\n swap (`LLMRouter.switch_backend`), pack reload, and MCP registration take effect without a\n restart.\n- **Settings sync (`adk/settings_sync.py`).** The user's portal profile is the source of\n truth: on startup the agent pulls `preferences.adk` and applies it over the local\n `~/.aither/config.yaml` cache; admin-console edits push a debounced snapshot back to\n `PUT /api/settings/preferences`. Provider API keys and MCP auth headers are device-local\n secrets and are never included in the pushed snapshot. Opt out with `AITHER_SETTINGS_SYNC=false`.\n\n### Security\n\n- **Timing-safe token comparison.** The server bearer check now uses `hmac.compare_digest`\n (the token is reachable over the public tunnel; a byte-wise `!=` leaked it to timing attacks).\n- **MCP add is SSRF-guarded + confirmed.** Adding an external MCP server is a two-step\n prepare→confirm flow; URLs resolving to loopback / private / link-local / metadata addresses\n are rejected (override with `AITHER_ALLOW_PRIVATE_MCP=1` for trusted local servers).\n- **Config editor is allowlisted.** `PATCH /admin/config` writes only safe scalar prefs and\n hard-denies fields that could execute code or redirect traffic at startup (e.g.\n `aither_toolpack_dirs`, backend base URLs).\n- **Log tail is scrubbed.** Bearer tokens, `#k=` fragments, and `sk-`/`aither_sk_` keys are\n redacted from the log-tail endpoint; the tunnel log is excluded.\n\n## [2.14.8] - 2026-07-04\n\n### Added — `adk up` for normal humans (no API key, no prereqs, live chat)\n\n- **Hosted-brain default — no API key required.** When there's no local model and no\n provider key, `adk up` signs in (device-flow) and routes inference through the gateway\n using the portal token (`AITHER_LLM_BACKEND=gateway` + bearer). A non-technical user\n never needs to obtain or paste an LLM API key. Explicit `--provider`/local model still\n take precedence; unattended `--yes` with no token/backend fails cleanly (exit 3).\n- **cloudflared auto-download.** If the tunnel binary is missing, `adk up` downloads the\n right platform asset to `~/.aither/bin` (SHA256-verified against the release; opt out with\n `AITHER_CLOUDFLARED_REQUIRE_CHECKSUM=0`) instead of exiting with an install hint.\n- **Built-in streaming chat page.** The agent now serves a self-contained chat UI at `/`\n (and `/chat`) with a live \"thinking…\" indicator and token-by-token streaming — so a human\n has somewhere to talk to it with immediate feedback instead of a blank wait. `adk up` opens\n it locally and passes the callback bearer in the URL fragment (`#k=…`), so `/chat/stream`\n stays authenticated (not an open proxy) with nothing to paste.\n- **Human-readable failures.** `adk up` errors now carry a plain-English `next_action`\n (kept machine-readable in `--yes` JSON).\n\n### Fixed\n\n- `adk up` chat link pointed at a non-existent `/settings/agent` route; it now points at the\n agent's own streaming chat page.\n\n## [2.14.7] - 2026-07-04\n\n### Added\n\n- **`adk up` — one command to run a persistent, fleet-connected agent.** Collapses\n the scattered onboarding surface (`host`/`quickstart`/`connect`/`login`/`setup`)\n into a single command that starts `aither-serve` (single agent, identity `aither`\n by default), opens a Cloudflare quick-tunnel for fleet reachability, registers with\n the portal, and installs a reboot-persistent autostart entry (Windows Task Scheduler\n / systemd-user / launchd). Detaches by default so the terminal is freed; `--foreground`\n blocks. Reuses the existing device-flow login, cloudflared discovery, provider-key\n store, and `_preflight_check` backend detection.\n- **Fully non-interactive path for autonomous agents.** `adk up --yes` (or any\n non-TTY invocation) takes ZERO prompts — everything resolves from flags, env, and\n saved config — and emits a single machine-readable JSON status line with meaningful\n exit codes (`0` ok, `2` bad-input, `3` no-backend, `4` tunnel-missing, `5` register-failed).\n Degrades to local-only (warn, not fail) when a portal token is absent unless\n `--require-register` is set.\n- **`adk down` / `adk status --json` companions.** `down` stops the agent + tunnel,\n best-effort deregisters, and removes the autostart entry; `status` reports the running\n agent (liveness + `/health` + tunnel + registration), with `--json` for agents/CI.\n- **`adk stack`** — the previous `adk up` behaviour (supervise the Room + Ollama consumer\n stack) now lives here; `adk up` is the connected-agent command.\n\n### Internal\n\n- New `adk/agent_daemon.py`: detached process spawn, status file\n (`~/.aither/adk-up.json`), pid liveness/teardown, and cross-platform autostart install.\n\n## [2.14.6] - 2026-07-04\n\n### Added\n\n- **`adk.preflight` — self-bootstrap capability probe.** `run_preflight(agent,\n spec)` wakes an agent up knowing what it is actually plugged into: a bounded\n (1.5s) parallel `LivenessProbe` over the router's resolved backends\n (primary/reasoning), the embeddings winner, remote MCP tools, the structured-ML\n / `/ml/teach` endpoints, and voice — returning an honest `CapabilityReport`.\n Status vocabulary distinguishes reachability from entitlement\n (`OK`/`MISSING`/`UNREACHABLE`/`AUTH`/`TIER_DENIED`/`UNSUPPORTED`), a hosted slot\n never prints a bare `OK`, and a required-but-unsatisfied slot aborts before the\n loop instead of blind-firing. Headless task/rules resolution + an allowed-roots\n policy binding (`builtin_tools.set_allowed_roots`, which resets the memoized\n cache so the override actually enforces).\n- **Multimodal (image) messages.** `Message.content` now accepts an OpenAI-style\n content-part list (`str | list[dict]`), passed through verbatim to the\n OpenAI-compatible body (gateway / vLLM / OpenAI). New `adk.llm.multimodal`\n helpers — `image_message()`, `image_content_parts()`, `to_image_url()` — build\n the parts and encode local image bytes/paths into `data:` URIs.\n- **Real vision preflight probe.** The vision slot is no longer unconditionally\n `UNSUPPORTED`: it sends a discriminable image (a solid red square) and requires\n the model to name the color, so a blind/text-only model cannot false-pass. `OK`\n only on a real round-trip; `AUTH` on 401/403; `UNSUPPORTED` when there is no\n OpenAI-compatible provider or the model cannot see the image.\n\n## [2.14.5] - 2026-07-04\n\n### Added\n\n- **`adk.reasoning.mcts` — generic Monte-Carlo Tree Search library.** A reusable\n MCTS engine (ported from the internal `UnifiedMCTS`, with all internal couplings\n removed) that any agent can drive over its own environment:\n - `MCTSEnvironment` protocol (`get_state_hash` / `get_actions` / `step` /\n `evaluate` / `clone`) — implement it and search.\n - Three **optional, default-off** model seams — `TransitionModel`, `PolicyModel`\n (PUCT priors), `ValueModel` — so a learned world-model / policy / value can be\n injected without touching the core; with all three `None` the engine is a plain\n UCT search.\n - `ObservedTransitionModel` adapter — a cheap online lookup world-model (hit =\n exact stored transition; miss = identity + negative \"unknown\" reward +\n `is_uncertain`, biasing exploration toward learning), with JSONL save/load for\n cross-run reuse.\n - `UnifiedMCTS(config).search(env)` returns an `MCTSResult` (best action, value,\n confidence, principal-variation path) and an optional `MCTSTrace`\n (visit-distribution policy target) for distillation.\n- **Self-bootstrapping agent machinery.** Build an agent from a declarative spec\n instead of bespoke wiring:\n - `adk.bootstrap.build_agent_from_spec(path) -> (AitherAgent, RunCtx)` — resolves\n prompts, activates required/optional tool packs, and carries a `memory_map`\n (a `node_type -> (role, tier)` override for memory writes, never a gate).\n - `adk.prompts.PromptBridge` — resolves an agent's prompt bindings from files\n (relative to the spec) or `AitherPrompts` dot-keys (optional import).\n - `adk.packs.PackActivator` — eager activation of installed tool packs by name\n over `register_tool_packs`; a required pack that registers zero tools raises\n `PackUnavailable`, optional packs soft-degrade.\n - `python -m adk.bootstrap.generic_agent <spec.yaml>` inspects a spec (resolved\n name, activated packs + tool counts, prompt keys, memory map) without running a\n live loop.\n- **`deep_research` tool pack** — a full multi-engine web-research workflow as\n `dr_*` agent tools (keyless search, page fetch + cache, site mirror, a citable\n findings knowledge graph, and markdown/PDF/DOCX report generation), activatable\n by name via the tool-pack loader.\n\n## [2.14.4] - 2026-07-04\n\n### Added\n\n- **`structured_ml` tool domain** — zero-shot structured-data inference for agents,\n backed by TabFM (tabular classification/regression) and TimesFM (time-series\n forecasting). Three tools:\n - `tabular_classify(support_rows, target, query_rows)` — classify rows from a\n labeled support set, in-context (no training step; up to 10 classes).\n - `tabular_regress(support_rows, target, query_rows)` — predict a numeric target.\n - `timeseries_forecast(series, horizon)` — forecast future values of a series.\n\n The tools POST to a structured-ML inference service resolved from\n `AITHER_STRUCTURED_ML_URL` (default `http://localhost:8192`), reject non-http(s)\n URLs, and summarise oversized responses to protect the agent's context.\n- **`Capability.STRUCTURED_INFERENCE`** capability token and a new **`analyst`**\n identity/pack that ships the `structured_ml` domain by default. The domain is\n opt-in — only identities that enable the `structured_ml` category get the tools.\n\n## [2.14.3] - 2026-07-03\n\n### Fixed\n\nAll four bugs below were found by actually booting `adk run` against a real\nchat model and driving a real tool-calling request end-to-end (not inferred\nfrom reading the code) — each one silently broke the \"local agent uses its\nconfigured model and its tools actually work\" path.\n\n- **`AITHER_MODEL` / `config.model` silently ignored by the OpenAI-compatible\n backend**: `LLMRouter.__init__` never read `config.model` into `self._model`\n unless the caller passed `model=` directly to the constructor — which nothing\n did. Every explicit model choice (env var, config file) was dropped, the\n provider fell back to a hardcoded per-backend default (`gpt-4o-mini` for the\n `openai` backend), and an unrecognized model name got silently remapped\n server-side to the wrong backing model. Concretely: setting\n `AITHER_MODEL=aither-orchestrator` had no effect — every request actually\n went to a different model that was never meant to be called directly and\n can't reliably use tools.\n- **`OPENAI_BASE_URL` ignored for explicit `--backend`/`AITHER_LLM_BACKEND`\n selection**: `LLMRouter._auto_detect()`'s explicit-backend branch hardcoded\n `base_url=None` when constructing the provider, instead of reading\n `config.openai_base_url` (which two other call sites in the same file\n already did correctly). Any self-hosted OpenAI-compatible endpoint\n (MicroScheduler, vLLM, LM Studio) configured via `OPENAI_BASE_URL` was\n silently ignored in favor of `https://api.openai.com/v1`.\n- **Tool-call steering never engaged on default-effort chat**: the retry that\n nudges a model back into structured tool-calling when it responds with prose\n instead of a function call was gated behind `effort >= 6`. Default `effort`\n for a plain `agent.chat(message)` call resolves to `5`, so the safety net —\n built specifically for this failure mode — never fired for default requests.\n Also now forces `tool_choice=\"required\"` on the retry instead of relying on a\n text nudge alone.\n- **Every non-string builtin-tool parameter silently typed as `\"string\"` in\n the generated tool schema**: `builtin_tools.py` uses\n `from __future__ import annotations`, so `_extract_parameters()` in\n `tools.py` was reading postponed (string) type hints (`\"int\"`, not `int`)\n straight out of `fn.__annotations__`, which never matched\n `_type_to_schema`'s type-object keys and silently fell through to\n `{\"type\": \"string\"}`. Every builtin tool with an `int`/`float`/`bool`/`list`\n parameter was affected — e.g. `file_read(start_line: int, end_line: int)`\n advertised both as strings, so a model correctly calling the tool with\n `\"start_line\": \"0\"` crashed the actual implementation with\n `unsupported operand type(s) for -: 'str' and 'int'`. Fixed by resolving\n hints through `typing.get_type_hints()` instead of the raw annotations dict.\n `_coerce_arguments()` — the runtime safety net that's supposed to coerce a\n model-supplied `\"0\"` string into `0` before a tool call executes — had the\n exact same raw-`__annotations__` bug and silently no-opped for every builtin\n tool; fixed the same way.\n- **Streaming tool-loop could hang a connection forever**: `chat_stream()`'s\n fallback to the sync tool loop (tool calls can't stream token-by-token) had\n no timeout, so a model that didn't cleanly terminate a tool-calling round\n trip left the SSE connection open indefinitely with zero bytes sent. Bounded\n with a 60s `asyncio.wait_for`; on timeout the stream yields a clear message\n instead of hanging.\n\n## [2.14.2] - 2026-07-03\n\n### Fixed\n- **AitherShell session amnesia**: the REPL generated a fresh `session_id` for\n EVERY message, so conversation history never followed across turns (the\n server stored each turn under a different session). One stable id is now\n minted per shell run; `/resume` and `/new` semantics unchanged, and the\n cross-restart auto-restore of the last session now actually works.\n\n## [2.14.1] - 2026-07-02\n\n### Added\n- **Account-tied licensing via device-flow login** — `adk login` now persists the account\n license returned by the device-flow handshake to `~/.aither/license.json` (base64\n `license_key` → `{payload, signature}` envelope), so entitlements from the authenticated\n account carry into every later `adk` invocation without re-auth. Pairs with the fleet-side\n node tool-pack gate that grants packs by plan-tier rank.\n- **`can_use_formbridge` / `can_use_untether` entitlements** (PROFESSIONAL+ rank ≥ 3) backing\n the FormBridge and UNTETHER agent tool-packs.\n\n### Fixed\n- `adk` CLI: guard the optional `memory_graph` singleton (was a latent `NameError` on the\n `/stats`, `/memory`, and save paths when memory was not constructed).\n\n## [2.14.0] - 2026-07-02\n\n### Added\n- **`adk.memory_wiki` — LyraWiki-style self-managed semantic knowledge** (opt-in; nothing constructs it by default): `MemoryWiki(graph_memory)` maintains curated wiki ARTICLES as `wiki_article` nodes (title/slug, LLM-owned markdown with `[[wikilinks]]`, embedding, durable tier, `CONSOLIDATED_FROM` edges to source memories, governance-ledgered revisions).\n - `consolidate(llm, since=, budget=)` clusters unconsolidated episodic/fact nodes (embedding + keyword), drafts/UPDATEs articles via the INJECTED llm callable (`str -> str` or `messages -> str`, sync or async — no hard model dependency), cites source node ids, marks contradicted sources superseded, then DEMOTES consolidated sources to a fast-decay tier via `promote()` (represented knowledge doesn't need raw copies).\n - `lint(llm=None)` — deterministic health checks (orphan wikilinks, empty/stale articles, live-but-contradicted pairs) + an optional LLM audit pass.\n - `prune(relevance_floor=0.05, hard_delete_after_days=14)` — article relevance = tier freshness × reinforcement (reinforce-on-recall) + link-degree to LIVE memories; below the floor → reversible governance tombstone; entombed past the window → **HARD-DELETE** (the tombstone snapshot itself is purged — content and embedding irrecoverable; the true-deletion path). A node is never deleted without a tombstone first.\n - `recall(query, limit)` — article-first RAG (curated articles rank above raw nodes) that reinforces returned articles.\n- **`adk.routines` — agent heartbeat + self-programmed routines**: `RoutineStore` is a durable registry (`~/.aither/routines/{agent}.json`, `AITHER_DATA_DIR`-aware, injectable path) of cron-scheduled self-prompts `{name, cron, instruction, enabled, last_run, last_result, tags}` that rehydrates into the existing `adk.cron.CronScheduler` on `start()`. A fire runs `agent.chat(instruction)` with the agent's full toolset — the agent programs its future self with its own adk. Guardrails: `max_routines` (12), a 5-minute min fire interval per routine, a per-fire timeout, and every result ledgered (truncated) to `last_result`. Direct-callable routines (`register_direct`) fire a bound method instead of a self-prompt. Self-management tools (`routine_create/list/update/pause/resume/delete/run_now`) ship as OpenAI tool defs + handlers (`build_routine_tools` / `register_routine_tools` / `routine_tool_defs`); the handlers ONLY touch the RoutineStore — they can never modify agent config or safety-relevant settings (the leash principle).\n- **`AitherAgent(routines=True, memory_maintenance=True)`** (both default **False** → the default agent is byte-identical): `routines=True` builds the RoutineStore + scheduler, registers the self-management tools, and starts the heartbeat lazily on the first `chat()` (or via explicit `start_routines()`). `memory_maintenance=True` registers DEFAULT routines — `wiki_consolidate` (every 2 h), `wiki_lint` / `wiki_prune` / `graph_sweep` (daily) — as DIRECT method fires so memory upkeep runs even on tiny models, while staying visible/manageable via `routine_list`.\n- `TombstoneStore.purge(tombstone_id)` — the hard-delete endpoint: irrecoverably removes a tombstone snapshot and rewrites the persisted store without it (consumed by `memory_wiki.prune`).\n\n## [2.13.6] - 2026-07-02\n\n### Added\n- **Qdrant backend for GraphMemory dataplane sync — TRUE two-way, verified live.** Set `AITHER_FLEET_QDRANT_URL` and the sync uses Qdrant (`upsert` + `scroll` + filtered `search`) instead of Nexus. This is what actually delivers cross-agent memory sharing: a node is upserted with its OWN embedding under a deterministic per-(tenant,node) point id (idempotent re-push), and `fleet_pull` reliably SCROLLS a tenant's points back — the enumerate Nexus lacks (its `/search` doesn't return ingested docs and `/export` needs lancedb, which has a hard dependency conflict in the fleet). **Proven live with 2 agents:** agent-1 ingests → auto-syncs to the tenant dataplane; a fresh agent-2 in the same tenant `fleet_pull`s all of agent-1's nodes and can search them; a different tenant pulls nothing (isolation). Dimension-safe (never mixes vector dims in a collection). Local SQLite stays source of truth; best-effort, never raises.\n\n## [2.13.5] - 2026-07-02\n\n### Fixed\n- **CRITICAL — GraphMemory dataplane sync push was 100% failing.** `_fleet_push_node` sent `source_type=\"graph\"` / `content_type=\"graph_node\"`, which AitherNexus rejects as invalid enums (HTTP 500) — so NO graph node ever replicated in 2.13.4. Now sends the accepted `source_type=\"manual\"` / `content_type=\"text\"` (graph provenance is carried in `metadata.synced_from`). **Verified live** against a fleet Nexus: a 4-node ingest replicates with `pending==0` and the collection count reflects it.\n\n### Added\n- **Swarm-awareness notification** — on a successful sync batch, `GraphMemory` emits a best-effort `graph.synced` event (tenant/workspace/agent/collection/count) to `AITHER_FLEET_EVENTS_URL` so OTHER agents in the tenant/swarm can pull the fresh data and deconflict in-flight work (push-based awareness instead of polling). No-op unless the env is set.\n\n### Known limitation\n- Cross-agent **rehydration** (`fleet_pull`) is only reliable when the fleet Nexus is backed by lancedb (persistent + exportable). An in-memory Nexus stores on `/ingest` but neither its `/search` nor `/export` returns the docs — two-way read needs the persistent Nexus.\n\n## [2.13.4] - 2026-07-02\n\n### Added\n- **Memory-maintenance primitives on `GraphMemory`** (all opt-in; default behaviour byte-identical):\n - **Reinforce-on-recall**: `recall_with_activation(..., reinforce=True)` bumps `reinforcement_count`/`last_reinforced` on every RETURNED node's metadata mirror (one UPDATE per node, WAL-safe) — making the `MemoryRecord` reinforcement fields the activation scorer reads LIVE. Default `False` = zero writes.\n - **`sweep(now, archive_below=0.05, max_nodes=None, dry_run=False)`**: read-time decay enforced at rest — archives nodes past their tier TTL with freshness×reinforcement score below `archive_below` and `reinforcement_count <= 1`; `max_nodes` additionally archives the lowest-scored overflow. PERMANENT tier immune. Every archive is a reversible governance tombstone (`TombstoneStore.recover`) + a FORGET ledger entry; a node is never deleted without a tombstone. `dry_run` returns the would-archive list without acting.\n - **`promote(node_id, tier=?, role=?)`**: re-tier/re-role a node IN PLACE (tier/role columns + metadata mirror) — edges, embedding and ledger history survive; ledgers an UPDATE entry when governance is on.\n- `MutationType.UPDATE` ledger entry kind (in-place re-tier/re-role).\n- **Per-tenant dataplane sync for `GraphMemory`** — replicate the local graph to a per-tenant vector dataplane (Nexus/Qdrant) so a sovereign agent's memory is durable + rehydratable across restarts and hosting boundaries. `fleet_push_all_nodes()` (catch-up), `fleet_pull()` (best-effort semantic top-up), `fleet_sync_pending()`, and auto-sync on ingest (`_maybe_autosync` + `drain_sync()`). **On by default** (`AITHER_FLEET_SYNC=auto`; only `false/0/off` disables) with an inferred target (local Nexus `:8122` or the gateway in cloud mode) and a SEPARATE collection (`AITHER_FLEET_GRAPH_COLLECTION`, default `graph_memory`) from the KV memory sync. New `GraphMemory(tenant_id=, workspace_id=, fleet_url=, auto_sync=)` constructor args override env (required for multi-tenant host processes). Node `synced` column + schema v4 migration. TLS verify is CA-bundle-aware (`AITHER_CA_BUNDLE`) for the internal-TLS mesh. Local SQLite stays the source of truth; every sync path is best-effort and never raises into ingest.\n\n## [2.13.3] - 2026-07-02\n\n### Added\n- **Canonical self-deploying embeddings provider** (`adk.embeddings`): one embedding provider for the whole SDK so vectors are portable across scopes (platform / tenant / sovereign) — same model + dimension everywhere. Pinned to `nomic-embed-text` (768-d; the vLLM `--served-model-name` for nomic-embed-text-v1.5). Lazy, single-flight resolution chain: explicit `AITHER_EMBEDDINGS_URL` → local vLLM (`:8209` then `:8120`, HTTPS-then-HTTP) → local Ollama → gateway (`AITHER_GATEWAY_EMBEDDINGS_URL`) → auto-deploy a local vLLM embeddings container if a GPU + Docker are present (opt out with `AITHER_EMBED_AUTODEPLOY=0`) → CPU sentence-transformers (384-d, degraded) → feature-hash (384-d, degraded, always works). Every batch is dimension-tagged so callers never silently mix 768-d and 384-d vectors. `get_default_embedder()`, `embed_texts()`, `embed_one()`, `get_provider()`, `reset_provider()`.\n\n### Changed\n- **`GraphMemory` now defaults its embedder to the canonical provider** (opt out with `AITHER_GRAPH_EMBEDDER=legacy`), so every adk agent shares one 768-d space. Added a meta-table dimension guard: the index is pinned to its first embedding's dimension and later different-dimension vectors are refused (kills 768↔384 mixing that silently poisons cosine similarity).\n\n## [2.13.2] - 2026-07-01\n\n### Added\n- **Session persistence**: AitherShell now auto-saves the active session_id to `~/.aither/config.yaml` after each chat turn and auto-restores it on the next shell launch. Multi-turn conversations survive across restarts — the messages were always persisted in SQLite + JSON, only the session pointer was missing.\n- `/new` command to start a fresh session (clears saved session_id).\n- `/sessions` command to list recent sessions with agent name, message count, and timestamp. Resume any with `/resume <id>`.\n\n## [2.13.1] - 2026-07-01\n\n### Fixed\n- **CRITICAL:** `deploy_connect()` now sends a HEAD request to verify the GitHub release asset exists before downloading, with a clear error if the asset is missing.\n- **CRITICAL:** Compose file URLs are now pinned to a stable commit hash (configurable via `AITHER_COMPOSE_PIN` env) instead of tracking the mutable `main` branch.\n- **CRITICAL:** `deploy_adk_node()` now falls back to starting `adk-serve` natively when Docker is unavailable, instead of blocking with \"Docker not installed\".\n- **HIGH:** GHCR login failure now blocks deployment immediately with actionable guidance, instead of warning and continuing to a confusing image-pull failure.\n- **HIGH:** Health checks now stream the last 30 lines of Docker Compose logs on failure, so the user can see why a container didn't start.\n- **HIGH:** vLLM port allocation now probes for conflicts before assigning; if port 8200 is busy, workers are automatically remapped to the next free port.\n- **MEDIUM:** Compose file downloads are now cached with ETag; re-running `adk deploy node` skips re-downloading an unchanged file.\n- **MEDIUM:** DGX Spark discovery now respects `AITHER_DGX_HOSTS` env (comma-separated) for custom network topologies, instead of only checking `spark.local` and `192.168.0.33`.\n- **MEDIUM:** Cloud API keys (Anthropic, OpenAI, DeepSeek) are now validated with a lightweight probe during `adk setup` infra scan, so invalid keys surface early.\n- **MEDIUM:** `deploy_connect()` now validates the extracted extension has a `manifest.json` and warns if it's missing.\n\n## [2.13.0] - 2026-07-01\n\n### Removed\n- **`adk.faculties.memory_graph`** — the pickle-based `MemoryGraph` module is deleted. `from adk.faculties import MemoryGraph` now resolves to the canonical `adk.graph_memory.GraphMemory` for back-compat.\n- **`adk.platform.memory`** — the entire legacy memory subsystem (`MemoryManager`, `UnifiedMemorySystem`, `GameEngine`, `StoryboardEngine`, `AnchorGenerator`) is deleted. Agent memory is `adk.memory.Memory` + `adk.graph_memory.GraphMemory`.\n\n### Changed\n- `adk work` command now uses `GraphMemory` instead of the removed pickle-based faculty graph.\n- `adk.MemoryGraph` and `adk.faculties.MemoryGraph` lazy exports now resolve to `adk.graph_memory.GraphMemory`.\n- `agent.set_memory_graph()` kept for back-compat but documented as deprecated; agents already wire `GraphMemory` automatically in `__init__`.\n- Platform startup (`adk.platform.infrastructure.startup`) gracefully handles missing `MemoryManager`.\n- Platform CLI module listing and health check updated to reflect removed memory module.\n\n## [2.12.7] - 2026-07-01\n\n### Deprecated\n- `adk.faculties.MemoryGraph` — emits `DeprecationWarning` on instantiation; use `adk.graph_memory.GraphMemory` (SQLite-backed, governed, embedding-native) instead.\n- `adk.platform.memory` package — the legacy `MemoryManager`, `UnifiedMemorySystem`, `GameEngine`, and `StoryboardEngine` modules now emit `DeprecationWarning` on import; use `adk.memory.Memory` and `adk.graph_memory.GraphMemory` for new code.\n\n### Changed\n- Updated `adk.faculties` docstring and usage examples to point at the canonical memory surface.\n\n## [2.12.6] - 2026-07-01\n\n### Changed\n- Trimmed public package debt by removing grid runbook artifacts and shell/room binary placeholder directories from published artifacts.\n- Removed stale public-sync allowlist entries for deleted phase test files and added explicit public-sync guards for binary placeholder directories and grid artifacts.\n- Updated the public README grid note so it no longer links to runbook files excluded from the SDK package.\n\n## [2.12.5] - 2026-07-01\n\n### Added\n- Added best-effort fleet memory sync from local ADK memory into Nexus/Qdrant-compatible RAG stores, including batch catch-up and vector-search recall hooks.\n- Added saved config/env support for fleet memory URL, collection, and sync mode.\n\n### Changed\n- Excluded FormBridge runtime/test internals from public ADK packaging and public repo sync.\n\n## [2.12.4] - 2026-07-01\n\n### Added\n- Added a hot-reload endpoint for installed agent packs so newly applied pack tools can be picked up without restarting `aither-serve`.\n\n### Fixed\n- Fixed synchronous `ChatRelay` calls on Python 3.12 when no event loop is running; async WebSocket delivery is still scheduled when a loop exists.\n- Aligned gateway inference tier tests with the current public ladder: free, starter, pro, enterprise, platform.\n\n## [2.12.0] - 2026-06-28\n\n### Added — one-command local inference + upgrade path (click-to-run)\n- **`adk quickstart-local`** — detects hardware, picks a backend\n (Ollama / llama.cpp / vLLM), installs it, downloads a model, verifies, and\n prints next steps. Bootstraps Ollama itself via winget/brew/install.sh when\n missing (no \"go install it\" dead-ends). Default model `gemma4:e2b`.\n- **`adk backend switch <ollama|llamacpp|vllm>`** (+ `backend status`) — migrate\n the active deployment between engines; re-points config and smoke-tests.\n- **`adk install pack:<name>`** (+ `adk install list` / `adk packs`) — install\n ready-made agent packs (openclaw, hermes, claude-code) into `~/.aither/agents`.\n\n### Fixed (found via live end-to-end testing on Windows + CUDA)\n- quickstart-local reported success on a dead endpoint (smoke test was warn-only);\n a failed smoke now fails the command, with a service-readiness wait.\n- `--model` resolved to `None` (getattr default never fired) → uses the real default.\n- Smoke test: 30s→180s timeout for cold model load; accepts reasoning-model output\n (empty `content` at low token budgets is no longer a false failure).\n- `adk install pack:<name>` colon syntax (argparse rejected it before dispatch).\n- `adk backend status` health probe used a nonexistent `/health` path → 404; now\n probes `/v1/models` (uniform OpenAI-compatible liveness for all three backends).\n- Windows: removed non-ASCII glyphs from printed strings that crashed the cp1252\n console (a `✓` even made a successful install report failure).\n\n## [2.11.1] - 2026-06-14\n\n### Fixed — `adk host` / `adk login` device flow (autonomous onboarding)\nFound driving `adk host` against production; all broke real device-flow login:\n- **Device flow hit the portal FRONTEND, not AitherIdentity.** `adk login`/`adk host`\n POSTed `/auth/device/code` to portal/veil.aitherium.com, which 307-redirects API auth\n to `/login`. Added `_resolve_identity_url()` mapping the aitherium.com topology to\n `idp.aitherium.com` (the Identity API); localhost/bespoke pass through unchanged.\n- **Cloudflare 403'd urllib.** `idp.*` sits behind Cloudflare, which blocks urllib's\n default `Python-urllib/3.x` UA. Device-code + poll requests now send a real\n User-Agent + Accept (verified urllib→idp 200, was 403).\n- **`adk host` now self-authenticates via device flow** when there's no token, and\n **retries via device flow on a 401/403** (a stale saved token no longer dead-ends\n registration).\n\n## [2.11.0] - 2026-06-14\n\n### Added — attach your own MCP \"hands\" to a self-hosted agent\nA self-hosted agent now has the full brain (your model) · body (this loop) · **hands**\n(your tools) trifecta. The AitherOS platform relays the MCP servers you registered for\nyour agent in the `/stream` request body as `mcp_endpoints`, and the loop wires their\ntools into the ReAct turn:\n- **`adk/mcp_endpoint_tools.register_mcp_endpoint_tools(agent, endpoints)`** — speaks MCP\n JSON-RPC (`tools/list` to discover, `tools/call` to invoke) and registers a namespaced\n proxy tool (`{endpoint}__{tool}`) for each advertised tool. Mirrors `app_proxy_tools`.\n Best-effort: an unreachable endpoint registers nothing and never raises.\n- **`POST /stream` and `POST /chat/stream`** accept an optional `mcp_endpoints` list\n (`[{name, url, [headers]}]`); `_aitheros_stream` attaches them to the agent before the\n turn. Existing callers are unaffected (the field is optional).\n\n## [2.10.1] - 2026-06-14\n\n### Fixed — self-hosted `aither-serve` was crash-on-startup (2.10.0 regression)\nA fresh `pip install aither-adk && aither-serve --backend deepseek` could not boot. Found\nand fixed while standing up a real self-hosted DeepSeek agent end-to-end:\n- **Startup `NameError: name 'port' is not defined`** in three lifespan helpers\n (`_join_aithernet`, `_init_a2a_server`, `_start_mesh_hosting`) — they referenced a bare\n `port` no longer in scope. Now use `config.server_port`, and `main()` writes the resolved\n `--port`/`--host` back to `config` so every lifespan helper and the invoke_url see the\n actual bound port.\n- **Fleet self-registration crashed the whole server** — the registration handlers caught\n `(ImportError, RuntimeError, OSError, ConnectionError)` but **not** `httpx.HTTPError`, so a\n `RemoteProtocolError`/connect error talking to the control plane took the agent down. All\n network-facing lifespan handlers now also catch `httpx.HTTPError` (non-fatal, as intended).\n- **`--backend` was ignored when `AITHER_API_KEY` was set** — `_auto_detect` picked the cloud\n gateway before the explicit backend, and `_try_elysium_fallback` then replaced the chosen\n provider. An explicit backend (deepseek/openai/anthropic/your own vllm/ollama) now always\n wins; \"auto\"/\"gateway\" still flow through detection. The operator's own brain is never\n silently hijacked.\n- **Approved-tool trace truncated after `/sessions/{id}/confirm`** — the SSE relay treated\n `AgentResponse.tool_calls_made` (a `list[str]`) as dicts (`tc.get(...)`), raised\n `AttributeError`, and an over-narrow `except` swallowed it so the client saw only\n `session_start` + heartbeats. The relay now handles string tool names, the `except` is\n broad (always emits a typed `error` + terminal `complete`), and the module-level\n `_strata_ingest` `NameError` + Chronicle `session_id` kwarg mismatch are fixed.\n\n## [2.10.0] - 2026-06-13\n\n### Added — human-in-the-loop tool approval (`adk/approval.py`)\n- **Pause before a gated tool, resume on decision.** An agent can now pause its turn before\n executing a sensitive tool and wait for a human's allow/deny — so a managed control plane\n (or any operator) can gate tool use without forking the loop.\n- **Policy via `AITHER_TOOL_APPROVAL`** — a comma-separated list of tool names (optionally\n `agent:tool`), or `*` for every tool. Empty = no gating (unchanged behavior).\n- **Survives restarts / days-later approvals.** The paused turn (user message + pending tool\n calls) is persisted to disk keyed by `session_id`; resume is a fresh request, not a held\n connection. Decisions are recorded per `(session_id, tool_name)`.\n- **`AgentResponse.requires_action` / `.pending`** — set when a turn paused; `pending` lists\n the gated tool calls awaiting a decision.\n- **`AitherAgent.resume(session_id, decisions)`** — records the decisions and re-runs the\n paused turn (the gate consumes them: allowed tools execute, denied tools get a denial\n observation, the turn continues — and may pause again on a different gated tool).\n- **Server:** `POST /sessions/{id}/confirm` records decisions and streams the resumed turn;\n `/stream` emits a `requires_action` event when a turn pauses.\n\n## [2.9.0] - 2026-06-13\n\n### Added — universal LLM continuation primitive (`adk/llm/continuation.py`)\n- **`run_continuation()` + `stitch()`** — one shared continue-until-complete primitive. When a\n completion stops because it hit the output token cap (`finish_reason == \"length\"`), it continues\n the generation and STITCHES the chunks (overlap-dedup + restart-detect) into a complete answer,\n instead of every call-site hand-rolling its own retry. Bounded three ways (max rounds AND total\n output ceiling AND a no-growth break), and it never mutates the caller's message history.\n- **`LLMProvider.chat_with_continuation()`** and **`LLMRouter.chat_with_continuation()`** — every\n provider AND the router inherit the primitive, so any call-site gets continuation for free.\n- Kill-switch `ADK_LLM_CONTINUATION=off`; tunables `ADK_LLM_MAX_CONTINUATIONS`,\n `ADK_LLM_MAX_TOTAL_OUTPUT_TOKENS` (defaults: 2 rounds / 8192 tokens).\n\n### Changed\n- The ReAct loop's truncation recovery now uses the shared primitive. The previous inline\n doubling-retry REPLACED the partial response (losing earlier text in `resp.content`) and injected\n continuation scaffolding into the live message history; it now STITCHES and leaves history clean.\n Tool-call turns are never continued as text.\n\n### Included — previously-committed-but-unreleased changes\n- 2.8.2: TLS verification on by default (`verify=False` removed).\n- 2.8.1: baked license + pack-signing public keys.\n- 2.8.0: version-bump release.\n\n## [2.7.0] - 2026-06-05\n\n### Added — provider-agnostic LLM cost levers (at the `LLMProvider` ABC)\n- **Prompt caching.** New `ProviderCapabilities` (`prompt_cache: explicit|automatic|none`, `batch`) and\n normalized `LLMResponse.cache_read_tokens`/`cache_write_tokens`, so the \"tokens saved\" meter never\n depends on which backend served the turn. Anthropic inserts `cache_control` breakpoints over the stable\n system+tools prefix; OpenAI/DeepSeek caching is automatic and their `cached_tokens` are now surfaced\n (previously discarded). Default-on in `AitherAgent.chat`; override with `cache=False`.\n- **ResponseCache** (`adk/llm/cache.py`) — exact-match response cache with a pluggable backend, wired into\n `LLMRouter` as an opt-in (`LLMRouter(response_cache=…)` + per-call `cacheable=True`). OFF by default — no\n determinism change unless opted in.\n- **Gemini provider** (`adk/llm/gemini.py`) with explicit `cachedContents` caching; registered in the router.\n- **Async batch runner** (`adk/llm/batch_runner.py`) — `run_batch()` over the Anthropic Message Batches /\n OpenAI Batch APIs (~50% off) with a transparent fallback to concurrent `chat()` where unsupported.\n- **Graph-RAG retriever interface** (`adk/graph_retriever.py`).\n\n### Added — principal authority (prompt-injection defense enforced outside the model)\n- `adk/auth.py`: `Principal` / `AuthContext` / `PrincipalResolver` (+ deny-all default).\n- `ToolRegistry.execute(name, args, auth=…)` — a default-deny authorization gate at the tool boundary, so a\n forbidden tool call is blocked regardless of what the LLM emits. `ToolDef` gains `required_clearance` /\n `action_class`. Threaded through `AitherAgent.chat`; `auth=None` is fully backward-compatible.\n- `adk/channel_auth.py`: Telegram webhook-secret + CEO-id checks and HMAC-signed approvals.\n\n## [2.6.4] - 2026-06-05\n\n### Fixes\n- **Loop-guard nudges corrupted the message history → DeepSeek 400 mid-run.**\n When the ReAct loop emitted multiple tool calls in one assistant turn and the\n loop guard fired a WARN/BLOCK/CIRCUIT_BREAK on one of them, it spliced a\n standalone `system` nudge message *between* the tool results. DeepSeek (and\n strict vLLM chat templates) require an assistant `tool_calls` message to be\n followed by exactly one `tool` result per `tool_call_id`, contiguously — so the\n interleaved message produced `400 … insufficient tool messages following\n tool_calls message` and aborted the turn (this is reliably hit after crossing\n the 15-call circuit-break threshold, where every subsequent call WARNs).\n Per-call guidance is now folded into that call's own `tool` result, and any\n standalone steering is deferred to a single message AFTER the whole batch —\n preserving the `tool_calls`↔`tool_results` pairing every backend requires.\n Regression test: `deep-research-agent/tests/test_msg_structure.py`\n (loopguard-flood scenario).\n\n## [2.6.3] - 2026-06-04\n\n### Fixes\n- **Transient provider errors killed a run on the first blip.** A single\n `502 Bad Gateway` / `503` / `529 overloaded` / `429` from the upstream API\n aborted the whole request — the Anthropic provider had **no retry at all**, and\n the OpenAI-compatible provider only retried `429`. Both now retry transient\n statuses (`408/429/500/502/503/504/529`) with `Retry-After`-aware exponential\n backoff (3 attempts), and surface the provider's body if all retries are\n exhausted. Non-transient `4xx` (e.g. `400`) still fails fast. Applies to\n `chat` and `chat_stream` on both providers.\n\n## [2.6.2] - 2026-06-04\n\n### Fixes\n- **DeepSeek (and strict OpenAI-compatible backends) 400'd on long tool\n conversations.** The ReAct loop legitimately injects mid-conversation\n `system` steering messages (the \"[DIMINISHING RETURNS]\" hint after several\n low-output tool iterations, loop-guard nudges). OpenAI tolerates a non-leading\n `system` message; DeepSeek rejects it with a 400. Long research runs\n (many `fetch_url` calls) reliably tripped this. Fix: non-leading `system`\n messages are demoted to `user` at the OpenAI-compatible provider boundary\n (`_demote_nonleading_system`) — steering intent preserved, payload valid on\n every backend; leading system prompt(s) untouched.\n- **Opaque HTTP errors.** `resp.raise_for_status()` discarded the provider's\n response body, so a 400/422 surfaced as a bare `Client error '400 Bad\n Request'` with no cause. `_ensure_ok` now includes the provider's actual\n message (e.g. `context_length_exceeded`, `Invalid 'messages'`) in the raised\n error, for both `chat` and `chat_stream`.\n\n## [2.6.1] - 2026-06-04\n\n### Fixes\n- **`MCPServer.mount()` returned HTTP 422 for every `/mcp` call.** Under\n `from __future__ import annotations` the route handler's `request: Request`\n annotation is a string that FastAPI resolves against the *module* globals, but\n `Request` was imported locally inside `mount()` — so FastAPI couldn't resolve\n it and treated `request` as a required query param. The MCP server endpoint\n (`initialize`, `tools/list`, `tools/call`) was unusable when mounted. FastAPI\n symbols are now imported at module level (guarded; the `[node]` extra still\n gates actual use). Any agent's tools can now be exposed as a working MCP\n server with `MCPServer(...).mount(app)`.\n\n## [2.6.0] - 2026-06-04\n\n### Streaming + scaffolding\n- **`AitherAgent.stream_chat`** — reliable streaming sibling of `chat()`: native\n tool-calling loop (concludes on its own, unlike the text-protocol `stream_react`)\n that emits live `tool`/`tool_result` events and streams the answer as `token`\n events. `token_delay` paces tokens for a typing effect.\n- **`adk new <template>`** — scaffold a full, runnable template app. Ships the\n `deep-research` template (a cited web-research agent: pack + server + web UI):\n `adk new deep-research`.\n\n### Fixes\n- **BYO-key no longer community-capped.** The free-tier monthly token cap now applies\n only to the metered Aitherium gateway backend, never to a user's own provider key\n (Anthropic/OpenAI/DeepSeek/Ollama/vLLM). Previously it could brick a self-hosted\n agent after ~100k tokens on the user's own key.\n- **Stronger, grounded memory recall.** `chat()` now injects the top-6 knowledge-graph\n hits (untruncated) with a hard no-fabrication instruction — was top-3 truncated to\n 200 chars, which surfaced partial facts and let weaker models invent the rest.\n- **Tool-arg coercion.** `ToolRegistry.execute` coerces LLM-supplied args toward each\n parameter's annotated type, so a tool no longer crashes when a model passes a list\n or string where an int/float/bool was expected.\n\n### Docs\n- New `docs/AGENT_DEV_GUIDE.md` — the golden path for building agents/packs.\n\n## [2.5.0] - 2026-06-04\n\n### Continual learning — agents that learn from runs and reuse memory\n\nAgents now improve the second time they do a task instead of starting from scratch.\n\n### Added\n- **Recall-before**: `AitherAgent.chat()` searches the local `SkillStore` and injects the\n top matching learned skills as a system block, so the model reuses proven procedures.\n- **Learn-after**: `_learn_after()` runs after each response — reinforces (`success_count++`)\n any skills that were recalled+reused, and extracts + saves a NEW skill from successful\n multi-tool runs via `SkillExtractor`. Wired at both `chat()` return paths.\n- Gated by `AITHER_SKILLS` (default on). The previously-unused `SkillStore`/`SkillExtractor`\n are now connected to the run loop.\n\n### Notes\n- Pairs with the platform-side LearnedProcedure substrate (recipes → scored procedures →\n skills/tools/A2A/packs → MCTS priors → evolution). The SDK shares memory via the Spirit\n bridge when configured.\n\n## [2.0.0] - 2026-06-02\n\n### Open-core boundary (BREAKING)\n\nThis release draws a single, enforced free/paid line. The free tier stays\ngenuinely useful (real agent, typed + graph + code memory, ReAct, ~essential\ntools); paid tiers unlock the *scale* capabilities via portal.aitherium.com.\n\n### Added\n- **`adk/licensing.py`** — the entitlement keystone. Resolves a tier\n (COMMUNITY by default), verifies optional portal-signed `~/.aither/license.json`\n (Ed25519), and answers `can_use_fleet/channels/cron/swarm/auto_neurons`,\n `is_agent_licensed`, `max_effort`, etc. **Fail-closed**: no/invalid/expired\n license → free tier; an unsigned license never grants premium.\n `AITHER_TENANT_SLUG=aitherium` → unrestricted INTERNAL tier.\n- **Moat guard** (`scripts/check_moat_boundary.py`) wired into the publish\n workflow — blocks any wheel that leaks the moat or drops the keystone.\n- Tests: `tests/test_licensing.py`, `tests/test_moat_boundary.py`.\n\n### Changed (gating — fail-closed)\n- Free tier effort is capped at 3 (reasoning effort 7–10 needs Professional).\n- Monthly token budget hard-block on the free tier (no silent overspend).\n- Gated behind a paid tier: fleet mode (`FleetConfig`), cross-agent delegation\n (`AgentForge.delegate`), channel adapters, cron scheduler, proactive\n auto-neurons, and `swarm`/`swarm_code` dispatch.\n\n### Removed\n- **`adk/nanogpt.py`** (on-device training IP) removed from the published SDK\n and the public repo; relocated to the internal AitherOS moat. Excluded from\n the wheel as defense-in-depth.\n- Pre-2.0 releases shipped this IP and ungated capabilities and are being\n yanked (PyPI) / removed (GitHub releases). See `scripts/yank_leaky_releases.py`\n and `scripts/purge_public_leaks.sh`.\n\n## [1.20.0] - 2026-05-27\n\n### Fixed\n- **Pack discovery path mismatch** — `_find_local_packs()` now scans both\n `~/.aither/packs/` and `~/.aitheros/packs/`, so packs installed via\n `adk pack install` are discovered by `adk-workspace`.\n\n### Added\n- **Auto-register on pack install** — `adk pack install` now auto-registers\n with the portal if the pack contains an `agent.yaml` with a `portal` section.\n One command does download + extract + register.\n- **Flux event on agent registration** — `developer_portal.py` emits\n `agent.registered` Flux event so the portal fleet UI updates in real-time.\n\n## [1.15.0] - 2026-05-26\n\n### Added\n- **Cloud memory** — `adk quickstart --cloud` now tests gateway memory and\n auto-configures Spirit URL so memories persist across devices via\n `gateway.aitherium.com/v1/memory/teach` and `/v1/memory/recall`.\n- **Config→env export for cloud mode** — `Config.from_env()` now exports\n `AITHER_CLOUD_MODE`, `AITHER_SPIRIT_URL`, and spirit path env vars from\n saved config so Memory and other modules pick them up without needing\n explicit env vars set by the user.\n- **Cloud Quick Start section** in README — 3-command setup for users who\n just want a Claude/GPT-4/DeepSeek API key and the full agent harness.\n- **`--memory` profile for node deployment** — `adk deploy node --memory`\n adds Spirit (8087) + WorkingMemory (8101) containers for persistent\n vector memory. Without this, agents use local SQLite only.\n- **Configurable vLLM in node compose** — `AITHER_VLLM_MODEL`,\n `AITHER_VLLM_GPU_UTIL`, `AITHER_VLLM_CTX_LEN`, `AITHER_VLLM_QUANT_ARGS`\n env vars let `adk setup` write GPU-detected tier settings to `.env`\n and have the node compose pick them up (TQ4 for small GPUs, BNB for large).\n\n### Fixed\n- **Memory cloud routing** — `Memory._spirit_teach()` and `_spirit_recall()`\n now use configurable paths and auth headers for gateway proxy. Previously\n cloud-only users silently lost all Spirit memory calls.\n\n## [1.9.0] - 2026-05-24\n\n### Added\n- **Composable ToolPack system** — agents can load tool packs from\n `.toolpack.yaml` directories. Drop a pack in `~/.aitheros/packs/` or\n set `AITHER_PACKS_DIR`, and any agent discovers it automatically.\n- **`tools.packs` in agent.yaml** — list tool pack IDs to auto-load\n (e.g. `packs: [crm-tools, calendar-pro]`).\n- **`register_tool_packs(agent, pack_ids, packs_dir)`** in `builtin_tools` —\n programmatic pack loading for agents.\n- **`AITHER_TOOL_PACKS` env var** — comma-separated pack IDs loaded on\n agent init (alternative to YAML config).\n- **Unknown builtin categories resolve as pack IDs** — `tools.builtin:\n [file_io, crm-tools]` now works: `crm-tools` is looked up as a pack.\n\n## [1.8.0] - 2026-05-24\n\n### Removed\n- **11 dead modules** (3,023 lines) — `agent_deploy`, `cloud_deploy`, `create`,\n `phonehome`, `sdk_bridge`, `sovereign`, `telemetry`, `telemetry_config`,\n `error_reporter`, `anonymizer`, `package`. None were imported anywhere.\n- **29 ghost exports** from `__init__.py` — `secrets`, `otel`, `fs_sandbox`,\n and `vector_memory` modules were listed in `__all__` and `__getattr__` but\n the backing files never existed. Accessing them raised `ModuleNotFoundError`.\n- **Stale dist/ artifacts** — removed old wheels/tarballs (v0.4.0–v1.4.1, 11 MB).\n- **Orphan test scripts** — `test_phase1.py` and `test_phase23.py` (one-off\n validation scripts not part of the pytest suite).\n\n### Fixed\n- **Version mismatch** — `__init__.__version__` now matches `pyproject.toml`\n (was stuck at 1.6.0).\n\n## [1.5.0] - 2026-05-23\n\n### Added\n- **`--sovereign` flag for `adk deploy node`** — registers the node with the\n Aitherium hub via federation after deployment. Saves federation credentials\n to `~/.aither/.env.federation`. Accepts `--hub` (custom hub URL) and\n `--tenant` (tenant slug) options. Non-fatal: node runs standalone if\n registration fails.\n- **Federation → fleet cross-post** (Genesis) — sovereign nodes that register\n or heartbeat via `/federation/register` and `/federation/heartbeat` now\n automatically appear in the fleet dashboard (`/fleet/deployments`) with\n `node_type: \"sovereign\"` and live status/metrics.\n- **\"Deploy Sovereign Node\" section on Connect page** (AitherVeil) — Section 8\n with ADK deploy commands, fleet dashboard links, and provision page links.\n- **`deploy-node` action in tunnel proxy** (AitherVeil) — Connect page and\n external callers can now request bootstrap scripts via\n `POST /api/tunnel/devworkspace?action=deploy-node`.\n\n### Fixed\n- **Fleet provision page** — API URL corrected from `/api/fleet/provision` to\n `/api/bridge/genesis/fleet/provision` (matches bridge proxy pattern).\n- **Fleet detail page** — API URL corrected from `/api/fleet/deployments/` to\n `/api/bridge/genesis/fleet/deployments/` (same fix).\n- **Dockerfile** — entry point corrected from `aither-serve` to `adk-serve`\n (matches `pyproject.toml` script registration). Version label updated.\n\n## [1.4.1] - 2026-05-22\n\n### Fixed\n- **macOS executable builds** — dropped `--target-arch universal2` from\n `packaging/build_executable.py`. PyPI wheels for `pydantic_core` and other\n native deps are arch-specific, not fat binaries, so universal2 always failed\n with `IncompatibleBinaryArchError`. Now builds for the runner's native arch\n (`aither-macos-arm64`).\n- **Release workflow strategy** — added `fail-fast: false` so a single\n executable build failure no longer cancels the other OS jobs.\n- **PyPI publish** — added `skip-existing: true` so re-runs of the same\n version don't fail when the artifact already exists.\n- **GitHub Release job** — now runs whenever `pypi` succeeds even if some\n executable matrix entries fail, so the release page is always created with\n whatever binaries did build successfully.\n\n## [1.4.0] - 2026-05-22\n\n### Added\n- **Sovereign template now ships with `agent_core` 5-tier memory** — generated\n backends include the canonical `agent_core/` package (working → episodic →\n semantic → procedural → identity) plus `agent_memory.py` helper, vendored\n from `portal-kit-backend`.\n- `aiosqlite>=0.20.0` added to sovereign template `requirements.txt` for\n local agent_core SQLite storage.\n- Document tombstoning: `evict_cached_conversations_referencing` is wired\n through document-delete handlers in consumer apps so memories citing\n deleted documents are blocked from recall.\n- New tests: 13 smoke tests for `portal-kit-backend/agent_core` covering\n promotion thresholds, decay rates, store roundtrip, tombstone recall\n blocking, circuit breaker state machine, and chat-turn recording.\n\n### Changed\n- `scripts/vendor_agent_core.py` now syncs canonical `agent_core` to 4\n targets: WorkspaceRuntime, ADK sovereign template, and the GargBot +\n Chelle consumer mirrors (previously only 2).\n\n## [1.2.1] - 2026-05-21\n\n### Added\n- `adk train` command group — 6 subcommands: status, launch, logs, cancel, runs, register-gpu\n- `/slash-commands` endpoint — ADK server auto-exposes all 35 CLI commands as structured JSON manifest for AitherShell\n- `/cli/execute` endpoint — AitherShell can run any CLI command through the server\n- `build_command_manifest()` — introspects full argparse tree (commands, args, choices, subcommands)\n- `_register_commands()` extracted from main() for shared parser construction\n\n### Changed\n- `_get_genesis_url()` now probes: Genesis → ADK server → Aitherium cloud gateway (works without Genesis)\n\n## [1.2.0] - 2026-05-21\n\n### Added\n- **Runtime backend switching** — `agent.switch_backend(\"deepseek\")` and `agent.llm.switch_backend()` change provider without recreating agent\n- **Hybrid reasoning** — `agent.set_reasoning_backend(\"anthropic\")` routes effort 7+ to cloud API while keeping local orchestrator\n- **First-class DeepSeek provider** — `deepseek` in effort model table with `deepseek-chat` and `deepseek-reasoner`\n- **TQ4 quantization tiers** — `nano` (6GB, TurboQuant 4-bit), `standard-tq4` (12GB, both models TQ4), `hybrid-tq4` (6GB + cloud reasoning)\n- **`adk backend` CLI** — `list`, `set`, `set-reasoning`, `test` subcommands\n- **`adk quickstart`** — unified first-run wizard (setup + auth + shell)\n- **`adk tools`** — list local + MCP tools with tier markers\n- **`adk backup`** — tarball export of `~/.aither/`\n- **`adk ingest`** — feed docs/files into knowledge graph\n- **`adk setup nemotron`** shortcut — alias for `--tier lite`\n- **`--reasoning-api`** flag in setup — `anthropic`, `openai`, `deepseek`, `gateway`\n- **`--dgx-spark URL`** flag — remote vLLM endpoint configuration\n- **DGX Spark auto-detection** — scans `spark.local`, `192.168.0.33`, `AITHER_DGX_URL` env\n- **Post-setup smoke test** — sends real inference request after container startup\n- **Schema migration system** — version table + migration runner in `memory.py` and `graph_memory.py`\n- **Configurable vLLM port scan** — `AITHER_VLLM_PORTS` env var for custom ports\n- **`aithershell` entry point** — alias for `adk` in pyproject.toml\n- Shell auto-downloads binary on first `adk shell` (no `--install` needed)\n- Shell pre-flight check warns if no backend detected\n- Shell passes full config via env vars (API key, tenant, inference URL)\n- DGX/Remote and Cloud API checks added to `adk doctor`\n- Config fields: `reasoning_backend`, `reasoning_api_key`, `deepseek_api_key`, `dgx_url`, `vllm_extra_ports`\n\n### Fixed\n- `test_aeon_chat_basic` timeout — mock scope was exiting before request\n- `adk doctor` check_disk timeout — cap rglob at 5000 files\n\n## [1.1.7] - 2026-05-19\n\n### Added\n- Remote agent pipeline — `adk onboard` → fleet dispatch → remote inference\n- Agent-to-agent federation with mesh relay\n- `adk connect` for desktop mesh enrollment\n\n## [1.1.6] - 2026-05-19\n\n### Fixed\n- Tool call execution recovery across all backends\n- Hermes tool call fallback parsing for vLLM\n\n## [1.1.5] - 2026-05-19\n\n### Fixed\n- Tool call execution + Hermes fallback parsing improvements\n\n## [1.1.0] - 2026-05-18\n\n### Added\n- Multi-channel gateway (Telegram, Discord, Slack, webhook)\n- Aeon group chat with 7 presets\n- Skills auto-extraction from multi-step sessions\n- MCP stdio server for Claude Code integration\n- `adk gateway`, `adk cron`, `adk skills`, `adk soul` commands\n- SOUL.md import/export for portable agent identity\n- Elysium cloud device-flow authentication\n\n## [1.0.0] - 2026-05-18\n\n### Breaking\n- Package consolidated — `aither_adk` namespace replaced by `adk` + `adk.platform`\n- 93 duplicate files removed from `AitherOS/packages/aither_adk/`\n\n### Added\n- Standalone core primitives (`adk.core`: Agent, Tool, Memory, ModelBackend, Capability, Trace)\n- `adk.platform` sub-package merging all platform toolkit modules\n- Compatibility shims for legacy `from aither_adk.*` imports\n\n### Changed\n- Version synced across pyproject.toml, `adk/__init__.py`, `adk/platform/__init__.py`, npm package\n- Classifier promoted from Alpha to Beta\n\n## [0.16.0] - 2026-04-16\n\n### Swarm Coding Engine & Repowise Integration\n- **`repowise_search` tool** — Semantic + keyword hybrid code search via Repowise, with ripgrep fallback\n- **`swarm_code` tool** — Dispatch complex tasks to 11-agent swarm pipeline (ARCHITECT->SWARM->REVIEW->JUDGE)\n- **`agent.swarm()`** — Async convenience method with configurable mode, effort, timeout\n- **`agent.code_search()`** — Async convenience method returning structured results\n- New tool categories: `repowise` and `swarm` in TOOL_CATEGORIES\n- `repowise_search` and `swarm_code` added to `__init__.py` exports with lazy loading\n- `IDENTITY_DEFAULTS` updated: repowise for code-focused agents, swarm for orchestration agents\n- Standalone graceful degradation: repowise falls back to ripgrep, swarm returns structured error\n\n## [0.13.0] - 2026-04-02\n\n### Graph Faculties — Local Knowledge for Every Agent\n- **CodeGraph** (2,799 lines) — Full Python AST indexer with call graph, keyword/semantic/hybrid query, embedding matrix cache, incremental re-indexing, multi-hop chain expansion\n- **MemoryGraph** (1,339 lines) — Graph-based persistent agent memory with 10 edge types, hybrid query (keyword + semantic + graph expansion), multi-hop recall, pickle persistence\n- **EmbeddingProvider** — 4-backend fallback chain: sentence-transformers (GPU/CPU) -> Ollama -> Elysium cloud -> feature hashing (zero deps)\n- **BaseFacultyGraph** — Abstract base with HMAC-SHA256 validated pickle persistence\n\n### Agent Integration\n- `agent.set_code_graph(cg)` — auto-registers `code_search` + `code_context` tools\n- `agent.set_memory_graph(mg)` — auto-registers `remember` + `recall` + `memory_stats` tools\n- Both graphs inject context into LLM prompts automatically during chat\n\n### Zero-Config Onboarding\n- `adk start` / `adk` (no args) — auto-detect project, index code, detect LLM, persistent memory, interactive chat\n- Works for any directory: Python codebases, doc folders, mixed workspaces\n- Auto-detects LLM: Ollama -> vLLM -> Elysium -> OpenAI -> Anthropic\n- Per-project persistent memory in `~/.aither/memory/<project>`\n- `adk index <path>` — standalone indexing with progress bar and stats\n\n### MCP Gateway\n- `POST /v1/embeddings` — OpenAI-compatible embedding proxy to vLLM-embeddings:8209\n- Available to all tiers (embeddings are free)\n- ADK EmbeddingProvider uses this as Elysium cloud fallback\n\n### Optional Dependencies\n- `pip install aither-adk[graphs]` — numpy for 10x cosine similarity speedup\n- `pip install aither-adk[embedding]` — sentence-transformers + torch for local GPU embeddings\n- `pip install aither-adk` alone — graphs work with feature hashing (zero deps)\n\n## [0.12.0] - 2026-04-01\n\n### Bootstrap & Service Discovery\n- Version handshake -- ADK checks major.minor compatibility with Genesis/Node\n- Background reconnect loop -- ServiceBridge re-probes every 30s in standalone mode\n- Port 8080/8090 documented -- MCP vs OpenAI-compat clearly separated\n- Genesis URL configurable via GENESIS_URL env var (was hardcoded)\n- Standalone mode warning -- visible stderr alert when AitherOS not detected\n- Auto-reconnect on startup when services come online\n\n### Elysium Cloud Inference\n- Unified gateway URL -- gateway.aitherium.com handles auth + billing + inference\n- Streaming inference -- SSE passthrough for /v1/chat/completions with stream=true\n- Auth proxy routes -- /v1/auth/register, /v1/auth/login, /v1/auth/me\n- Billing proxy -- /v1/billing/balance through gateway\n- AitherConnect Elysium fallback -- cloud inference when local Genesis is down\n- AitherDesktop Elysium fallback -- third-tier chat fallback after Node\n\n### Infrastructure\n- /discovery endpoint on Genesis -- unified service URLs/versions/health\n- /api/config/services on Veil -- runtime port config for client-side JS\n- Veil healthcheck gates on Genesis -- unhealthy when backend is down\n- Desktop crash detection 90s->60s (threshold 3->2)\n\n## [0.11.0] - 2026-04-01\n\n### Agent Execution Quality (Claude Code Parity)\n- Raise loop guard block threshold from 3 to 4 -- agents get more room for iterative search\n- Soft synthesis nudge for effort >= 4 (no tool stripping, trust the model)\n- max_output_tokens escalation -- retry up to 3x when response is truncated\n- Tool result pairing guarantee -- synthetic error for orphaned tool_use blocks\n- Micro-compaction of old tool results -- save context tokens on long sessions\n- First-turn tool forcing only for effort >= 6 (trust model for lower effort)\n- Diminishing returns detection -- nudge agent when 3+ turns produce < 500 tokens\n- Message normalization -- merge consecutive same-role messages, strip empties\n- LLM retry with exponential backoff (5 retries, 500ms-16s, jitter)\n\n## [0.9.0] - 2026-03-16\n\nThe \"connected world\" release. Cross-platform identity pairing, voice capabilities, and multi-channel integration.\n\n### Added\n- **Pairing**: Cross-platform identity linking (`adk/pairing.py`)\n - `PairingManager` — SQLite-backed identity linking with 6-char pairing codes\n - Link users across Telegram, Discord, Slack, WhatsApp with 10min TTL codes\n - Canonical session IDs for cross-channel conversation continuity\n - `get_session_id()` returns \"user-{id}\" for paired users\n- **Voice**: Speech-to-text and text-to-speech client (`adk/voice.py`)\n - `VoiceClient` — async STT/TTS/emotion via AitherVoice service\n - Convenience functions: `hear()`, `say()`, `feel()`\n - 6 voice options: alloy, echo, fable, nova, onyx, shimmer\n - Emotion detection with intensity scoring\n- New exports: `PairingManager`, `PairingResult`, `PlatformIdentity`, `VoiceClient`, `TranscriptionResult`, `SynthesisResult`, `EmotionResult`\n\n### Changed\n- `__init__.py` exports expanded with pairing and voice symbols\n\n## [0.6.0] - 2026-03-13\n\nThe \"group mind\" release. Multi-agent group chat, creative tools, and Iris identity.\n\n### Added\n- **Aeon**: Multi-agent group chat engine (`adk/aeon.py`)\n - `AeonSession` — persistent group chat with parallel agent execution\n - 7 presets: balanced, creative, technical, security, minimal, duo_code, research\n - Orchestrator synthesis: Aither summarizes all agent responses\n - Serial execution for Ollama, parallel for vLLM/cloud\n - ConversationStore persistence with `type: \"aeon\"` metadata\n - `group_chat()` one-shot convenience function\n- `aither aeon` CLI command — interactive terminal group chat with color-coded agents\n - `-p/--preset`, `-a/--agents`, `-r/--rounds`, `--no-synthesize` flags\n - `reset` and `quit` commands\n- Server endpoints: `POST /aeon/chat`, `GET /aeon/presets`, `GET /aeon/sessions/{id}`\n- Creative tools in builtin_tools: `image_generate`, `image_refine`, `image_search`, `video_generate`\n- Iris agent identity with visual generation capabilities\n- 48 Aeon tests (data models, presets, chat, context, persistence, server, exports)\n\n### Changed\n- `__init__.py` exports: `AeonSession`, `AeonResponse`, `AeonMessage`, `group_chat`, `AEON_PRESETS`\n\n## [0.5.0] - 2026-03-13\n\nThe \"tenant-ready\" release. Multi-tenant admin, permission grants, safety profiles, and a full setup wizard.\n\n### Added\n- `aither setup` interactive setup wizard with hardware detection, model selection, identity config\n- Strata storage backend: SQLite WAL persistence for conversations, memories, knowledge graphs\n- CLI test runner: `aither test` with auto-discovery and parallel execution\n- Permission grants system for MCP tool access control\n- MCP account management tools\n- LLM provider auto-detection for Ollama and vLLM endpoints\n- Apache-2.0 LICENSE file\n- Elysium desktop sync module\n- Comprehensive CLI test suite (493 tests)\n- Strata storage test suite (972 tests)\n- LLM provider tests (82 tests)\n\n### Changed\n- CLI expanded: `aither setup`, `aither test`, `aither bugreport`, `aither doctor`\n- Strata module rewritten as full local-first storage engine\n- Server startup includes Strata initialization\n- README rewritten with clearer quickstart and architecture docs\n\n### Fixed\n- Dead documentation links\n- Python 3.11 compatibility issues\n- Ruff per-file-ignores configuration\n- Identity provisioning edge cases\n- Docker node-gyp native dependency builds (python3 + build tools)\n\n## [0.4.0] - 2026-03-13\n\nThe \"own your AI\" release. Self-hosted agent OS for people who don't want their data on someone else's servers.\n\n### Added\n- Muse agent identity (creative/artistic generation)\n- Port 8120 to vLLM scan for ExoNodes discovery\n- Public roadmap with milestone-based porting schedule\n- Competitive positioning: self-hosted alternative to cloud-locked AI appliances\n\n### Changed\n- LLM router: compute-aware effort routing (replaces effort-level context gating)\n- README rewritten for sovereignty-first messaging\n- Package description updated\n- Promoted from alpha to stable release\n\n### Fixed\n- Version string consistency between pyproject.toml and __init__.py\n- vLLM port scanning now includes port 8120 (ExoNodes)\n\n## [0.3.1] - 2026-03-09\n\n### Added\n- GraphMemory: SQLite knowledge graph with Ollama embeddings and hybrid search\n- NeuronPool: auto-fire pre-LLM data gathering agents\n- NanoGPT: pure Python char-level transformer with LoRA fine-tuning\n- Safety gates: IntakeGuard (input), LoopGuard (recursion), Sandbox (code exec)\n- Event system: EventEmitter with chat/tool/forge event types\n- Builtin tools: identity-based tool selection\n- ServiceBridge: auto-discovery of AitherOS services\n- Streaming: chat_stream with safety gate integration\n- Auth middleware: Bearer token authentication\n- CLI: `aither init` and `aither serve` commands\n\n### Changed\n- Wired 5 previously disconnected modules into agent loop\n- 522 total passing tests (up from 85)\n\n## [0.3.0] - 2026-03-07\n\n### Added\n- Clean-room agent development kit\n- Multi-backend LLM providers (Ollama, OpenAI, Anthropic)\n- AitherAgent class with @tool decorator\n- SQLite conversation memory\n- OpenAI-compatible server (`aither-serve`)\n- 16 agent identities as package data\n- MCP bridge to mcp.aitherium.com\n- Privacy-centric opt-in telemetry\n- Bug reporting CLI and API\n- FastAPI server with OpenAI-compatible endpoints\n- Hardware auto-detection (5 tiers, 11 profiles)\n- Fleet orchestration and multi-agent coordination\n- A2A mesh protocol\n- Federation protocol for cross-instance agent dispatch\n\n### Initial release\n- 85 passing tests\n- Apache-2.0 license\n"}, "6dcff80f9dbd4ee2d042a14b81f826743bb8b832": {"t": 1785748177.1816456, "n": 76979, "h": "75f7ed69e778dbc9", "c": "# Changelog\n\nAll notable changes to aither-adk will be documented in this file.\n\n## [2.21.1] - 2026-07-14\n\n### Removed — internal toolkit and license-gated product no longer ship publicly\n\n- **`adk.platform`** — the internal aither-platform toolkit (ComfyUI/image-gen\n pipelines, fleet agent clients, a parallel cli/ui/tools stack) is no longer\n included in the public repo or the PyPI wheel. It was internal-only code that\n had been riding along since the aither-platform merge. `adk platform` now\n reports \"Platform toolkit not available\" on public installs; internal AitherOS\n builds are unaffected.\n- **`adk.formbridge`** — the FormBridge form-automation product is license-gated\n (PROFESSIONAL+); its implementation no longer ships in the free package. All\n import sites (`builtin_tools`, `server` route mount) already degrade behind\n `except ImportError`, so nothing else changes behavior.\n- Product test suites (`test_formbridge.py`, `test_encounter_keying.py`,\n `test_flow_tools.py`) are excluded from the public sync alongside them.\n\n## [2.21.0] - 2026-07-14\n\n### Added — Completion gate: verified-or-retried task execution\n\n- **`adk.gate.CompletionGate`** — a composable wrapper that closes the \"silent\n success\" gap: an agent's ReAct loop returns whatever the model produced and\n calls it done (`finish_reason = stop/max_steps/length`), which is a *liveness*\n signal, not a *completion* one — so an agent can \"succeed\" while doing nothing.\n `CompletionGate` runs the agent, verifies the result against acceptance\n criteria, retries with the failure fed back, and returns the response with\n `finish_reason` set to `verified` or an honest `unverified`. Never a false pass.\n - **Hard-first, un-soft-passable checks** (`adk.gate.hard_checks`): a filesystem\n path a criterion says must exist is checked on disk; a required quoted token\n must appear in the output. Only genuinely subjective criteria fall through to\n an optional LLM judge (reuses the agent's own model), which **fails closed**\n if unavailable.\n - **`gated_run(agent, task, criteria=..., max_retries=2)`** — one-shot helper.\n - `auto_criteria` (LLM writes the machine-checkable definition-of-done from the\n task), a `verifier=` override for custom checks (tests-pass / HTTP probe), and\n a `requires_action` short-circuit so it never fights human-approval pauses.\n - Compose it onto any agent exposing `run(task) -> resp{.content, .finish_reason}`\n (`AitherAgent`, `core.Agent`, or a thin adapter over a remote dispatch).\n - Exported from the top-level package: `from adk import CompletionGate, gated_run`.\n\n## [2.19.0] - 2026-07-12\n\n### Added — Eve agent importer + durable A2A tasks + canonical agent discovery\n\n- **`adk pack import <eve_agent_path>`** — convert external Eve agents to AitherADK packs\n (from GitHub or local filesystem). Maps Eve structure (instructions → system prompt,\n agent.ts → agent.yaml, skills/*.md → skills, connections/*.ts → MCP, tools/*.ts →\n tools/node verbatim) to AitherADK format with a single command. Eve agents (compiled\n manifest format, schema v35) become installable packs compatible with `adk pack install`.\n- **Durable A2A task lifecycle (`TaskManager` backed by FileStore)** — agent-to-agent\n tasks now survive container restarts. Tasks persist as append-only JSONL\n (`.adk/tasks.jsonl`); in-memory dict for fast access; `create_task()` / `update_status()` /\n `add_artifact()` / `add_message()` all save to disk. Google A2A v0.3.0 spec compliant.\n- **Canonical agent card discovery** — `A2AServer.mount()` now exposes:\n - `GET /.well-known/agent-card.json` — Agent Card (canonical; contains agent identity,\n capabilities, skills, endpoints)\n - `GET /.well-known/agent.json` — Agent Card (legacy, redirects with 308 to canonical path)\n - `POST /a2a` — JSON-RPC 2.0 for message send/task lifecycle\n - `GET /a2a/tasks/{id}/subscribe` — SSE streaming task updates\n- **Four built-in agent packs now complete and catalogued**:\n - `hermes` — architecture review & trade-off analysis\n - `openclaw` — web research & source verification\n - `claude-code` — feature development & debugging\n - `analyst` — structured data inference & anomaly detection\n All discoverable via `adk packs` / `adk install pack:<name>`.\n- **Tolerant tool-call JSON parsing** — malformed tool JSON (extra properties, missing\n name/arguments, string-encoded nested args, missing keys) is now gracefully degraded\n instead of silently failing. `_parse_tool_json()` accepts both `\"arguments\"` and\n `\"parameters\"` keys, coerces string-encoded args, and skips unparseable calls instead\n of crashing the turn.\n\n### Known limitations\n\n- Eve agent importer: remote GitHub fetching not yet implemented (local paths only).\n- Durable task store: currently JSONL (suitable for thousands of records, not millions).\n\n## [2.18.0] - 2026-07-10\n\n### Added — AitherShell command center + Claude Code session management\n\n- **`aither sessions`** — Claude Code session manager: interactive full-screen\n browser (type-to-filter, transcript preview, deep full-text `search`, resume\n **in the current terminal** or as Windows Terminal tabs), crash guard\n (`guard install` = at-logon watchdog that snapshots live sessions and\n auto-restores the whole set after a terminal crash), and `ingest [--watch]`\n (incremental, secret-guarded sync of session conversations into the local KB\n / CompanyBrain via the standard ingest pipeline).\n- **`aither hq`** — command-center dashboard: per-service fleet health, LLM\n queue (depth/VRAM/models), Pulse alerts, live session count, inbox unread —\n auto-refreshing, with one-key jumps into chat, sessions, inbox, agents,\n brief, watchtower, and docker recovery.\n- **`aither inbox`** — unified queue over CommCore mail, Relay\n mentions/notifications, and Pulse alerts; open/mark-read/DM-reply from the\n terminal. Every source degrades independently.\n- **`aither agents`** — roster console + `agents ask -e <1-10> <agent> <q>`\n (effort-tiered ask via Genesis, with automatic `/chat/stream` fallback when\n the `/agent/sync` execute gate rejects host callers) + forge dispatch\n inspection + routine health.\n- **`aither palette`** — universal fuzzy picker across actions, sessions, and\n services; **`aither brief`** — renders Atlas's executive briefing in the\n terminal; **`aither watch`** — fleet watchtower with named wedge-signature\n detection (docker-WSL wedge, LLM-queue stall, crash-loop uptime regression)\n and optional docker auto-recovery.\n- All fleet reads go through a shared fail-soft `FleetClient` (internal-CA\n TLS via `adk._tls`, dashboard-grade timeouts, per-source degraded states).\n- Node shell-cli (`@aitherium/shell-cli` 1.13.0): `aither\n sessions|hq|inbox|palette|brief|watch|agents|docker` now pass through to the\n Python shell instead of being intent-classified as chat prompts.\n\n## [2.17.1] - 2026-07-05\n\n### Fully self-service mesh join — no customer key handling\n\n- **Auto-join headscale off the onboard response.** The conductor now auto-issues a\n headscale pre-auth key for NAT'd nodes and returns it in the `/v1/mesh/onboard`\n response; `mesh.join` consumes it and brings up the tunnel automatically. A customer\n runs one command — they never mint, fetch, or set a headscale key. Explicit\n `--headscale-key`/`AITHER_HEADSCALE_AUTH_KEY` still take precedence.\n- **Security:** `_tailscale_up` now redacts the auth key from all logs and exceptions\n (a failed `tailscale up` previously echoed the full argv, leaking the key into logs).\n\n## [2.17.0] - 2026-07-05\n\n### Added — Headscale mesh transport + local Qdrant + AitherConnect onboarding\n\n- **Headscale NAT-friendly mesh transport.** When raw WireGuard UDP:51820 is not\n viable (customer boxes behind NAT, corporate firewalls, CGNAT), `adk mesh join\n --headscale` routes the mesh tunnel through Tailscale's Headscale control plane.\n The overlay IP is still Conductor-assigned (10.77.0.0/16); Headscale provides\n only the transport layer. Configured via `AITHER_MESH_TRANSPORT=headscale`,\n `AITHER_HEADSCALE_URL` (default: https://headscale.aitherium.com), and\n `AITHER_HEADSCALE_AUTH_KEY` (pre-generated key). Automatic fallback to raw\n WireGuard if Headscale setup fails.\n- **Local Qdrant vector DB provisioning.** `adk stack` now provisions Qdrant\n locally when `AITHER_VECTOR_DB=qdrant` is set, enabling offline-first RAG\n without external dependencies. Tiered: local Qdrant in-fleet, cloud Nexus\n for enterprise multi-tenant deployments.\n- **Conductor URL public fallback.** `adk mesh join` resolves the internal\n `aitheros-conductor:8193` hostname; if unreachable (e.g., self-hosted nodes),\n it falls back to the public `conductor.aitherium.com:8193` endpoint,\n eliminating bootstrap configuration friction.\n\n### Documentation\n\n- **Self-hosting runbook.** New `docs/SELF_HOSTING.md` covers deploying AitherOS\n on customer infrastructure with Qdrant + mesh + AitherConnect onboarding.\n- **Mesh transport constraints documented.** Raw WireGuard requires public\n UDP:51820 endpoint (cloud instances, dedicated hardware); Headscale for\n NAT'd networks. Noted in CLI help and module docstrings.\n\n## [2.15.0] - 2026-07-04\n\n### Added — built-in web admin console + portal-profile settings sync\n\n- **Web admin console.** The page `adk up` opens (served at `/` and `/ui`) is now a full,\n self-contained console — Chat plus six admin tabs: **Backend** (view/switch LLM provider,\n set keys, test connection), **Packs** (list/enable/disable/reload tool packs),\n **Sessions** (browse/delete conversations), **Logs** (redacted agent-log tail),\n **Graph** (knowledge-graph search/neighborhood/stats), and **MCP** (add/remove external\n MCP servers). No build step, no CDN — a single packaged HTML asset. The original minimal\n streaming chat page stays at `/chat` for back-compat.\n- **Admin API (`adk/admin_api.py`).** ~20 endpoints under `/admin/*`, all bearer-gated by the\n existing middleware (never in `_skip_auth_paths`). Operates on the live agent — LLM backend\n swap (`LLMRouter.switch_backend`), pack reload, and MCP registration take effect without a\n restart.\n- **Settings sync (`adk/settings_sync.py`).** The user's portal profile is the source of\n truth: on startup the agent pulls `preferences.adk` and applies it over the local\n `~/.aither/config.yaml` cache; admin-console edits push a debounced snapshot back to\n `PUT /api/settings/preferences`. Provider API keys and MCP auth headers are device-local\n secrets and are never included in the pushed snapshot. Opt out with `AITHER_SETTINGS_SYNC=false`.\n\n### Security\n\n- **Timing-safe token comparison.** The server bearer check now uses `hmac.compare_digest`\n (the token is reachable over the public tunnel; a byte-wise `!=` leaked it to timing attacks).\n- **MCP add is SSRF-guarded + confirmed.** Adding an external MCP server is a two-step\n prepare→confirm flow; URLs resolving to loopback / private / link-local / metadata addresses\n are rejected (override with `AITHER_ALLOW_PRIVATE_MCP=1` for trusted local servers).\n- **Config editor is allowlisted.** `PATCH /admin/config` writes only safe scalar prefs and\n hard-denies fields that could execute code or redirect traffic at startup (e.g.\n `aither_toolpack_dirs`, backend base URLs).\n- **Log tail is scrubbed.** Bearer tokens, `#k=` fragments, and `sk-`/`aither_sk_` keys are\n redacted from the log-tail endpoint; the tunnel log is excluded.\n\n## [2.14.8] - 2026-07-04\n\n### Added — `adk up` for normal humans (no API key, no prereqs, live chat)\n\n- **Hosted-brain default — no API key required.** When there's no local model and no\n provider key, `adk up` signs in (device-flow) and routes inference through the gateway\n using the portal token (`AITHER_LLM_BACKEND=gateway` + bearer). A non-technical user\n never needs to obtain or paste an LLM API key. Explicit `--provider`/local model still\n take precedence; unattended `--yes` with no token/backend fails cleanly (exit 3).\n- **cloudflared auto-download.** If the tunnel binary is missing, `adk up` downloads the\n right platform asset to `~/.aither/bin` (SHA256-verified against the release; opt out with\n `AITHER_CLOUDFLARED_REQUIRE_CHECKSUM=0`) instead of exiting with an install hint.\n- **Built-in streaming chat page.** The agent now serves a self-contained chat UI at `/`\n (and `/chat`) with a live \"thinking…\" indicator and token-by-token streaming — so a human\n has somewhere to talk to it with immediate feedback instead of a blank wait. `adk up` opens\n it locally and passes the callback bearer in the URL fragment (`#k=…`), so `/chat/stream`\n stays authenticated (not an open proxy) with nothing to paste.\n- **Human-readable failures.** `adk up` errors now carry a plain-English `next_action`\n (kept machine-readable in `--yes` JSON).\n\n### Fixed\n\n- `adk up` chat link pointed at a non-existent `/settings/agent` route; it now points at the\n agent's own streaming chat page.\n\n## [2.14.7] - 2026-07-04\n\n### Added\n\n- **`adk up` — one command to run a persistent, fleet-connected agent.** Collapses\n the scattered onboarding surface (`host`/`quickstart`/`connect`/`login`/`setup`)\n into a single command that starts `aither-serve` (single agent, identity `aither`\n by default), opens a Cloudflare quick-tunnel for fleet reachability, registers with\n the portal, and installs a reboot-persistent autostart entry (Windows Task Scheduler\n / systemd-user / launchd). Detaches by default so the terminal is freed; `--foreground`\n blocks. Reuses the existing device-flow login, cloudflared discovery, provider-key\n store, and `_preflight_check` backend detection.\n- **Fully non-interactive path for autonomous agents.** `adk up --yes` (or any\n non-TTY invocation) takes ZERO prompts — everything resolves from flags, env, and\n saved config — and emits a single machine-readable JSON status line with meaningful\n exit codes (`0` ok, `2` bad-input, `3` no-backend, `4` tunnel-missing, `5` register-failed).\n Degrades to local-only (warn, not fail) when a portal token is absent unless\n `--require-register` is set.\n- **`adk down` / `adk status --json` companions.** `down` stops the agent + tunnel,\n best-effort deregisters, and removes the autostart entry; `status` reports the running\n agent (liveness + `/health` + tunnel + registration), with `--json` for agents/CI.\n- **`adk stack`** — the previous `adk up` behaviour (supervise the Room + Ollama consumer\n stack) now lives here; `adk up` is the connected-agent command.\n\n### Internal\n\n- New `adk/agent_daemon.py`: detached process spawn, status file\n (`~/.aither/adk-up.json`), pid liveness/teardown, and cross-platform autostart install.\n\n## [2.14.6] - 2026-07-04\n\n### Added\n\n- **`adk.preflight` — self-bootstrap capability probe.** `run_preflight(agent,\n spec)` wakes an agent up knowing what it is actually plugged into: a bounded\n (1.5s) parallel `LivenessProbe` over the router's resolved backends\n (primary/reasoning), the embeddings winner, remote MCP tools, the structured-ML\n / `/ml/teach` endpoints, and voice — returning an honest `CapabilityReport`.\n Status vocabulary distinguishes reachability from entitlement\n (`OK`/`MISSING`/`UNREACHABLE`/`AUTH`/`TIER_DENIED`/`UNSUPPORTED`), a hosted slot\n never prints a bare `OK`, and a required-but-unsatisfied slot aborts before the\n loop instead of blind-firing. Headless task/rules resolution + an allowed-roots\n policy binding (`builtin_tools.set_allowed_roots`, which resets the memoized\n cache so the override actually enforces).\n- **Multimodal (image) messages.** `Message.content` now accepts an OpenAI-style\n content-part list (`str | list[dict]`), passed through verbatim to the\n OpenAI-compatible body (gateway / vLLM / OpenAI). New `adk.llm.multimodal`\n helpers — `image_message()`, `image_content_parts()`, `to_image_url()` — build\n the parts and encode local image bytes/paths into `data:` URIs.\n- **Real vision preflight probe.** The vision slot is no longer unconditionally\n `UNSUPPORTED`: it sends a discriminable image (a solid red square) and requires\n the model to name the color, so a blind/text-only model cannot false-pass. `OK`\n only on a real round-trip; `AUTH` on 401/403; `UNSUPPORTED` when there is no\n OpenAI-compatible provider or the model cannot see the image.\n\n## [2.14.5] - 2026-07-04\n\n### Added\n\n- **`adk.reasoning.mcts` — generic Monte-Carlo Tree Search library.** A reusable\n MCTS engine (ported from the internal `UnifiedMCTS`, with all internal couplings\n removed) that any agent can drive over its own environment:\n - `MCTSEnvironment` protocol (`get_state_hash` / `get_actions` / `step` /\n `evaluate` / `clone`) — implement it and search.\n - Three **optional, default-off** model seams — `TransitionModel`, `PolicyModel`\n (PUCT priors), `ValueModel` — so a learned world-model / policy / value can be\n injected without touching the core; with all three `None` the engine is a plain\n UCT search.\n - `ObservedTransitionModel` adapter — a cheap online lookup world-model (hit =\n exact stored transition; miss = identity + negative \"unknown\" reward +\n `is_uncertain`, biasing exploration toward learning), with JSONL save/load for\n cross-run reuse.\n - `UnifiedMCTS(config).search(env)` returns an `MCTSResult` (best action, value,\n confidence, principal-variation path) and an optional `MCTSTrace`\n (visit-distribution policy target) for distillation.\n- **Self-bootstrapping agent machinery.** Build an agent from a declarative spec\n instead of bespoke wiring:\n - `adk.bootstrap.build_agent_from_spec(path) -> (AitherAgent, RunCtx)` — resolves\n prompts, activates required/optional tool packs, and carries a `memory_map`\n (a `node_type -> (role, tier)` override for memory writes, never a gate).\n - `adk.prompts.PromptBridge` — resolves an agent's prompt bindings from files\n (relative to the spec) or `AitherPrompts` dot-keys (optional import).\n - `adk.packs.PackActivator` — eager activation of installed tool packs by name\n over `register_tool_packs`; a required pack that registers zero tools raises\n `PackUnavailable`, optional packs soft-degrade.\n - `python -m adk.bootstrap.generic_agent <spec.yaml>` inspects a spec (resolved\n name, activated packs + tool counts, prompt keys, memory map) without running a\n live loop.\n- **`deep_research` tool pack** — a full multi-engine web-research workflow as\n `dr_*` agent tools (keyless search, page fetch + cache, site mirror, a citable\n findings knowledge graph, and markdown/PDF/DOCX report generation), activatable\n by name via the tool-pack loader.\n\n## [2.14.4] - 2026-07-04\n\n### Added\n\n- **`structured_ml` tool domain** — zero-shot structured-data inference for agents,\n backed by TabFM (tabular classification/regression) and TimesFM (time-series\n forecasting). Three tools:\n - `tabular_classify(support_rows, target, query_rows)` — classify rows from a\n labeled support set, in-context (no training step; up to 10 classes).\n - `tabular_regress(support_rows, target, query_rows)` — predict a numeric target.\n - `timeseries_forecast(series, horizon)` — forecast future values of a series.\n\n The tools POST to a structured-ML inference service resolved from\n `AITHER_STRUCTURED_ML_URL` (default `http://localhost:8192`), reject non-http(s)\n URLs, and summarise oversized responses to protect the agent's context.\n- **`Capability.STRUCTURED_INFERENCE`** capability token and a new **`analyst`**\n identity/pack that ships the `structured_ml` domain by default. The domain is\n opt-in — only identities that enable the `structured_ml` category get the tools.\n\n## [2.14.3] - 2026-07-03\n\n### Fixed\n\nAll four bugs below were found by actually booting `adk run` against a real\nchat model and driving a real tool-calling request end-to-end (not inferred\nfrom reading the code) — each one silently broke the \"local agent uses its\nconfigured model and its tools actually work\" path.\n\n- **`AITHER_MODEL` / `config.model` silently ignored by the OpenAI-compatible\n backend**: `LLMRouter.__init__` never read `config.model` into `self._model`\n unless the caller passed `model=` directly to the constructor — which nothing\n did. Every explicit model choice (env var, config file) was dropped, the\n provider fell back to a hardcoded per-backend default (`gpt-4o-mini` for the\n `openai` backend), and an unrecognized model name got silently remapped\n server-side to the wrong backing model. Concretely: setting\n `AITHER_MODEL=aither-orchestrator` had no effect — every request actually\n went to a different model that was never meant to be called directly and\n can't reliably use tools.\n- **`OPENAI_BASE_URL` ignored for explicit `--backend`/`AITHER_LLM_BACKEND`\n selection**: `LLMRouter._auto_detect()`'s explicit-backend branch hardcoded\n `base_url=None` when constructing the provider, instead of reading\n `config.openai_base_url` (which two other call sites in the same file\n already did correctly). Any self-hosted OpenAI-compatible endpoint\n (MicroScheduler, vLLM, LM Studio) configured via `OPENAI_BASE_URL` was\n silently ignored in favor of `https://api.openai.com/v1`.\n- **Tool-call steering never engaged on default-effort chat**: the retry that\n nudges a model back into structured tool-calling when it responds with prose\n instead of a function call was gated behind `effort >= 6`. Default `effort`\n for a plain `agent.chat(message)` call resolves to `5`, so the safety net —\n built specifically for this failure mode — never fired for default requests.\n Also now forces `tool_choice=\"required\"` on the retry instead of relying on a\n text nudge alone.\n- **Every non-string builtin-tool parameter silently typed as `\"string\"` in\n the generated tool schema**: `builtin_tools.py` uses\n `from __future__ import annotations`, so `_extract_parameters()` in\n `tools.py` was reading postponed (string) type hints (`\"int\"`, not `int`)\n straight out of `fn.__annotations__`, which never matched\n `_type_to_schema`'s type-object keys and silently fell through to\n `{\"type\": \"string\"}`. Every builtin tool with an `int`/`float`/`bool`/`list`\n parameter was affected — e.g. `file_read(start_line: int, end_line: int)`\n advertised both as strings, so a model correctly calling the tool with\n `\"start_line\": \"0\"` crashed the actual implementation with\n `unsupported operand type(s) for -: 'str' and 'int'`. Fixed by resolving\n hints through `typing.get_type_hints()` instead of the raw annotations dict.\n `_coerce_arguments()` — the runtime safety net that's supposed to coerce a\n model-supplied `\"0\"` string into `0` before a tool call executes — had the\n exact same raw-`__annotations__` bug and silently no-opped for every builtin\n tool; fixed the same way.\n- **Streaming tool-loop could hang a connection forever**: `chat_stream()`'s\n fallback to the sync tool loop (tool calls can't stream token-by-token) had\n no timeout, so a model that didn't cleanly terminate a tool-calling round\n trip left the SSE connection open indefinitely with zero bytes sent. Bounded\n with a 60s `asyncio.wait_for`; on timeout the stream yields a clear message\n instead of hanging.\n\n## [2.14.2] - 2026-07-03\n\n### Fixed\n- **AitherShell session amnesia**: the REPL generated a fresh `session_id` for\n EVERY message, so conversation history never followed across turns (the\n server stored each turn under a different session). One stable id is now\n minted per shell run; `/resume` and `/new` semantics unchanged, and the\n cross-restart auto-restore of the last session now actually works.\n\n## [2.14.1] - 2026-07-02\n\n### Added\n- **Account-tied licensing via device-flow login** — `adk login` now persists the account\n license returned by the device-flow handshake to `~/.aither/license.json` (base64\n `license_key` → `{payload, signature}` envelope), so entitlements from the authenticated\n account carry into every later `adk` invocation without re-auth. Pairs with the fleet-side\n node tool-pack gate that grants packs by plan-tier rank.\n- **`can_use_formbridge` / `can_use_untether` entitlements** (PROFESSIONAL+ rank ≥ 3) backing\n the FormBridge and UNTETHER agent tool-packs.\n\n### Fixed\n- `adk` CLI: guard the optional `memory_graph` singleton (was a latent `NameError` on the\n `/stats`, `/memory`, and save paths when memory was not constructed).\n\n## [2.14.0] - 2026-07-02\n\n### Added\n- **`adk.memory_wiki` — LyraWiki-style self-managed semantic knowledge** (opt-in; nothing constructs it by default): `MemoryWiki(graph_memory)` maintains curated wiki ARTICLES as `wiki_article` nodes (title/slug, LLM-owned markdown with `[[wikilinks]]`, embedding, durable tier, `CONSOLIDATED_FROM` edges to source memories, governance-ledgered revisions).\n - `consolidate(llm, since=, budget=)` clusters unconsolidated episodic/fact nodes (embedding + keyword), drafts/UPDATEs articles via the INJECTED llm callable (`str -> str` or `messages -> str`, sync or async — no hard model dependency), cites source node ids, marks contradicted sources superseded, then DEMOTES consolidated sources to a fast-decay tier via `promote()` (represented knowledge doesn't need raw copies).\n - `lint(llm=None)` — deterministic health checks (orphan wikilinks, empty/stale articles, live-but-contradicted pairs) + an optional LLM audit pass.\n - `prune(relevance_floor=0.05, hard_delete_after_days=14)` — article relevance = tier freshness × reinforcement (reinforce-on-recall) + link-degree to LIVE memories; below the floor → reversible governance tombstone; entombed past the window → **HARD-DELETE** (the tombstone snapshot itself is purged — content and embedding irrecoverable; the true-deletion path). A node is never deleted without a tombstone first.\n - `recall(query, limit)` — article-first RAG (curated articles rank above raw nodes) that reinforces returned articles.\n- **`adk.routines` — agent heartbeat + self-programmed routines**: `RoutineStore` is a durable registry (`~/.aither/routines/{agent}.json`, `AITHER_DATA_DIR`-aware, injectable path) of cron-scheduled self-prompts `{name, cron, instruction, enabled, last_run, last_result, tags}` that rehydrates into the existing `adk.cron.CronScheduler` on `start()`. A fire runs `agent.chat(instruction)` with the agent's full toolset — the agent programs its future self with its own adk. Guardrails: `max_routines` (12), a 5-minute min fire interval per routine, a per-fire timeout, and every result ledgered (truncated) to `last_result`. Direct-callable routines (`register_direct`) fire a bound method instead of a self-prompt. Self-management tools (`routine_create/list/update/pause/resume/delete/run_now`) ship as OpenAI tool defs + handlers (`build_routine_tools` / `register_routine_tools` / `routine_tool_defs`); the handlers ONLY touch the RoutineStore — they can never modify agent config or safety-relevant settings (the leash principle).\n- **`AitherAgent(routines=True, memory_maintenance=True)`** (both default **False** → the default agent is byte-identical): `routines=True` builds the RoutineStore + scheduler, registers the self-management tools, and starts the heartbeat lazily on the first `chat()` (or via explicit `start_routines()`). `memory_maintenance=True` registers DEFAULT routines — `wiki_consolidate` (every 2 h), `wiki_lint` / `wiki_prune` / `graph_sweep` (daily) — as DIRECT method fires so memory upkeep runs even on tiny models, while staying visible/manageable via `routine_list`.\n- `TombstoneStore.purge(tombstone_id)` — the hard-delete endpoint: irrecoverably removes a tombstone snapshot and rewrites the persisted store without it (consumed by `memory_wiki.prune`).\n\n## [2.13.6] - 2026-07-02\n\n### Added\n- **Qdrant backend for GraphMemory dataplane sync — TRUE two-way, verified live.** Set `AITHER_FLEET_QDRANT_URL` and the sync uses Qdrant (`upsert` + `scroll` + filtered `search`) instead of Nexus. This is what actually delivers cross-agent memory sharing: a node is upserted with its OWN embedding under a deterministic per-(tenant,node) point id (idempotent re-push), and `fleet_pull` reliably SCROLLS a tenant's points back — the enumerate Nexus lacks (its `/search` doesn't return ingested docs and `/export` needs lancedb, which has a hard dependency conflict in the fleet). **Proven live with 2 agents:** agent-1 ingests → auto-syncs to the tenant dataplane; a fresh agent-2 in the same tenant `fleet_pull`s all of agent-1's nodes and can search them; a different tenant pulls nothing (isolation). Dimension-safe (never mixes vector dims in a collection). Local SQLite stays source of truth; best-effort, never raises.\n\n## [2.13.5] - 2026-07-02\n\n### Fixed\n- **CRITICAL — GraphMemory dataplane sync push was 100% failing.** `_fleet_push_node` sent `source_type=\"graph\"` / `content_type=\"graph_node\"`, which AitherNexus rejects as invalid enums (HTTP 500) — so NO graph node ever replicated in 2.13.4. Now sends the accepted `source_type=\"manual\"` / `content_type=\"text\"` (graph provenance is carried in `metadata.synced_from`). **Verified live** against a fleet Nexus: a 4-node ingest replicates with `pending==0` and the collection count reflects it.\n\n### Added\n- **Swarm-awareness notification** — on a successful sync batch, `GraphMemory` emits a best-effort `graph.synced` event (tenant/workspace/agent/collection/count) to `AITHER_FLEET_EVENTS_URL` so OTHER agents in the tenant/swarm can pull the fresh data and deconflict in-flight work (push-based awareness instead of polling). No-op unless the env is set.\n\n### Known limitation\n- Cross-agent **rehydration** (`fleet_pull`) is only reliable when the fleet Nexus is backed by lancedb (persistent + exportable). An in-memory Nexus stores on `/ingest` but neither its `/search` nor `/export` returns the docs — two-way read needs the persistent Nexus.\n\n## [2.13.4] - 2026-07-02\n\n### Added\n- **Memory-maintenance primitives on `GraphMemory`** (all opt-in; default behaviour byte-identical):\n - **Reinforce-on-recall**: `recall_with_activation(..., reinforce=True)` bumps `reinforcement_count`/`last_reinforced` on every RETURNED node's metadata mirror (one UPDATE per node, WAL-safe) — making the `MemoryRecord` reinforcement fields the activation scorer reads LIVE. Default `False` = zero writes.\n - **`sweep(now, archive_below=0.05, max_nodes=None, dry_run=False)`**: read-time decay enforced at rest — archives nodes past their tier TTL with freshness×reinforcement score below `archive_below` and `reinforcement_count <= 1`; `max_nodes` additionally archives the lowest-scored overflow. PERMANENT tier immune. Every archive is a reversible governance tombstone (`TombstoneStore.recover`) + a FORGET ledger entry; a node is never deleted without a tombstone. `dry_run` returns the would-archive list without acting.\n - **`promote(node_id, tier=?, role=?)`**: re-tier/re-role a node IN PLACE (tier/role columns + metadata mirror) — edges, embedding and ledger history survive; ledgers an UPDATE entry when governance is on.\n- `MutationType.UPDATE` ledger entry kind (in-place re-tier/re-role).\n- **Per-tenant dataplane sync for `GraphMemory`** — replicate the local graph to a per-tenant vector dataplane (Nexus/Qdrant) so a sovereign agent's memory is durable + rehydratable across restarts and hosting boundaries. `fleet_push_all_nodes()` (catch-up), `fleet_pull()` (best-effort semantic top-up), `fleet_sync_pending()`, and auto-sync on ingest (`_maybe_autosync` + `drain_sync()`). **On by default** (`AITHER_FLEET_SYNC=auto`; only `false/0/off` disables) with an inferred target (local Nexus `:8122` or the gateway in cloud mode) and a SEPARATE collection (`AITHER_FLEET_GRAPH_COLLECTION`, default `graph_memory`) from the KV memory sync. New `GraphMemory(tenant_id=, workspace_id=, fleet_url=, auto_sync=)` constructor args override env (required for multi-tenant host processes). Node `synced` column + schema v4 migration. TLS verify is CA-bundle-aware (`AITHER_CA_BUNDLE`) for the internal-TLS mesh. Local SQLite stays the source of truth; every sync path is best-effort and never raises into ingest.\n\n## [2.13.3] - 2026-07-02\n\n### Added\n- **Canonical self-deploying embeddings provider** (`adk.embeddings`): one embedding provider for the whole SDK so vectors are portable across scopes (platform / tenant / sovereign) — same model + dimension everywhere. Pinned to `nomic-embed-text` (768-d; the vLLM `--served-model-name` for nomic-embed-text-v1.5). Lazy, single-flight resolution chain: explicit `AITHER_EMBEDDINGS_URL` → local vLLM (`:8209` then `:8120`, HTTPS-then-HTTP) → local Ollama → gateway (`AITHER_GATEWAY_EMBEDDINGS_URL`) → auto-deploy a local vLLM embeddings container if a GPU + Docker are present (opt out with `AITHER_EMBED_AUTODEPLOY=0`) → CPU sentence-transformers (384-d, degraded) → feature-hash (384-d, degraded, always works). Every batch is dimension-tagged so callers never silently mix 768-d and 384-d vectors. `get_default_embedder()`, `embed_texts()`, `embed_one()`, `get_provider()`, `reset_provider()`.\n\n### Changed\n- **`GraphMemory` now defaults its embedder to the canonical provider** (opt out with `AITHER_GRAPH_EMBEDDER=legacy`), so every adk agent shares one 768-d space. Added a meta-table dimension guard: the index is pinned to its first embedding's dimension and later different-dimension vectors are refused (kills 768↔384 mixing that silently poisons cosine similarity).\n\n## [2.13.2] - 2026-07-01\n\n### Added\n- **Session persistence**: AitherShell now auto-saves the active session_id to `~/.aither/config.yaml` after each chat turn and auto-restores it on the next shell launch. Multi-turn conversations survive across restarts — the messages were always persisted in SQLite + JSON, only the session pointer was missing.\n- `/new` command to start a fresh session (clears saved session_id).\n- `/sessions` command to list recent sessions with agent name, message count, and timestamp. Resume any with `/resume <id>`.\n\n## [2.13.1] - 2026-07-01\n\n### Fixed\n- **CRITICAL:** `deploy_connect()` now sends a HEAD request to verify the GitHub release asset exists before downloading, with a clear error if the asset is missing.\n- **CRITICAL:** Compose file URLs are now pinned to a stable commit hash (configurable via `AITHER_COMPOSE_PIN` env) instead of tracking the mutable `main` branch.\n- **CRITICAL:** `deploy_adk_node()` now falls back to starting `adk-serve` natively when Docker is unavailable, instead of blocking with \"Docker not installed\".\n- **HIGH:** GHCR login failure now blocks deployment immediately with actionable guidance, instead of warning and continuing to a confusing image-pull failure.\n- **HIGH:** Health checks now stream the last 30 lines of Docker Compose logs on failure, so the user can see why a container didn't start.\n- **HIGH:** vLLM port allocation now probes for conflicts before assigning; if port 8200 is busy, workers are automatically remapped to the next free port.\n- **MEDIUM:** Compose file downloads are now cached with ETag; re-running `adk deploy node` skips re-downloading an unchanged file.\n- **MEDIUM:** DGX Spark discovery now respects `AITHER_DGX_HOSTS` env (comma-separated) for custom network topologies, instead of only checking `spark.local` and `192.168.0.33`.\n- **MEDIUM:** Cloud API keys (Anthropic, OpenAI, DeepSeek) are now validated with a lightweight probe during `adk setup` infra scan, so invalid keys surface early.\n- **MEDIUM:** `deploy_connect()` now validates the extracted extension has a `manifest.json` and warns if it's missing.\n\n## [2.13.0] - 2026-07-01\n\n### Removed\n- **`adk.faculties.memory_graph`** — the pickle-based `MemoryGraph` module is deleted. `from adk.faculties import MemoryGraph` now resolves to the canonical `adk.graph_memory.GraphMemory` for back-compat.\n- **`adk.platform.memory`** — the entire legacy memory subsystem (`MemoryManager`, `UnifiedMemorySystem`, `GameEngine`, `StoryboardEngine`, `AnchorGenerator`) is deleted. Agent memory is `adk.memory.Memory` + `adk.graph_memory.GraphMemory`.\n\n### Changed\n- `adk work` command now uses `GraphMemory` instead of the removed pickle-based faculty graph.\n- `adk.MemoryGraph` and `adk.faculties.MemoryGraph` lazy exports now resolve to `adk.graph_memory.GraphMemory`.\n- `agent.set_memory_graph()` kept for back-compat but documented as deprecated; agents already wire `GraphMemory` automatically in `__init__`.\n- Platform startup (`adk.platform.infrastructure.startup`) gracefully handles missing `MemoryManager`.\n- Platform CLI module listing and health check updated to reflect removed memory module.\n\n## [2.12.7] - 2026-07-01\n\n### Deprecated\n- `adk.faculties.MemoryGraph` — emits `DeprecationWarning` on instantiation; use `adk.graph_memory.GraphMemory` (SQLite-backed, governed, embedding-native) instead.\n- `adk.platform.memory` package — the legacy `MemoryManager`, `UnifiedMemorySystem`, `GameEngine`, and `StoryboardEngine` modules now emit `DeprecationWarning` on import; use `adk.memory.Memory` and `adk.graph_memory.GraphMemory` for new code.\n\n### Changed\n- Updated `adk.faculties` docstring and usage examples to point at the canonical memory surface.\n\n## [2.12.6] - 2026-07-01\n\n### Changed\n- Trimmed public package debt by removing grid runbook artifacts and shell/room binary placeholder directories from published artifacts.\n- Removed stale public-sync allowlist entries for deleted phase test files and added explicit public-sync guards for binary placeholder directories and grid artifacts.\n- Updated the public README grid note so it no longer links to runbook files excluded from the SDK package.\n\n## [2.12.5] - 2026-07-01\n\n### Added\n- Added best-effort fleet memory sync from local ADK memory into Nexus/Qdrant-compatible RAG stores, including batch catch-up and vector-search recall hooks.\n- Added saved config/env support for fleet memory URL, collection, and sync mode.\n\n### Changed\n- Excluded FormBridge runtime/test internals from public ADK packaging and public repo sync.\n\n## [2.12.4] - 2026-07-01\n\n### Added\n- Added a hot-reload endpoint for installed agent packs so newly applied pack tools can be picked up without restarting `aither-serve`.\n\n### Fixed\n- Fixed synchronous `ChatRelay` calls on Python 3.12 when no event loop is running; async WebSocket delivery is still scheduled when a loop exists.\n- Aligned gateway inference tier tests with the current public ladder: free, starter, pro, enterprise, platform.\n\n## [2.12.0] - 2026-06-28\n\n### Added — one-command local inference + upgrade path (click-to-run)\n- **`adk quickstart-local`** — detects hardware, picks a backend\n (Ollama / llama.cpp / vLLM), installs it, downloads a model, verifies, and\n prints next steps. Bootstraps Ollama itself via winget/brew/install.sh when\n missing (no \"go install it\" dead-ends). Default model `gemma4:e2b`.\n- **`adk backend switch <ollama|llamacpp|vllm>`** (+ `backend status`) — migrate\n the active deployment between engines; re-points config and smoke-tests.\n- **`adk install pack:<name>`** (+ `adk install list` / `adk packs`) — install\n ready-made agent packs (openclaw, hermes, claude-code) into `~/.aither/agents`.\n\n### Fixed (found via live end-to-end testing on Windows + CUDA)\n- quickstart-local reported success on a dead endpoint (smoke test was warn-only);\n a failed smoke now fails the command, with a service-readiness wait.\n- `--model` resolved to `None` (getattr default never fired) → uses the real default.\n- Smoke test: 30s→180s timeout for cold model load; accepts reasoning-model output\n (empty `content` at low token budgets is no longer a false failure).\n- `adk install pack:<name>` colon syntax (argparse rejected it before dispatch).\n- `adk backend status` health probe used a nonexistent `/health` path → 404; now\n probes `/v1/models` (uniform OpenAI-compatible liveness for all three backends).\n- Windows: removed non-ASCII glyphs from printed strings that crashed the cp1252\n console (a `✓` even made a successful install report failure).\n\n## [2.11.1] - 2026-06-14\n\n### Fixed — `adk host` / `adk login` device flow (autonomous onboarding)\nFound driving `adk host` against production; all broke real device-flow login:\n- **Device flow hit the portal FRONTEND, not AitherIdentity.** `adk login`/`adk host`\n POSTed `/auth/device/code` to portal/veil.aitherium.com, which 307-redirects API auth\n to `/login`. Added `_resolve_identity_url()` mapping the aitherium.com topology to\n `idp.aitherium.com` (the Identity API); localhost/bespoke pass through unchanged.\n- **Cloudflare 403'd urllib.** `idp.*` sits behind Cloudflare, which blocks urllib's\n default `Python-urllib/3.x` UA. Device-code + poll requests now send a real\n User-Agent + Accept (verified urllib→idp 200, was 403).\n- **`adk host` now self-authenticates via device flow** when there's no token, and\n **retries via device flow on a 401/403** (a stale saved token no longer dead-ends\n registration).\n\n## [2.11.0] - 2026-06-14\n\n### Added — attach your own MCP \"hands\" to a self-hosted agent\nA self-hosted agent now has the full brain (your model) · body (this loop) · **hands**\n(your tools) trifecta. The AitherOS platform relays the MCP servers you registered for\nyour agent in the `/stream` request body as `mcp_endpoints`, and the loop wires their\ntools into the ReAct turn:\n- **`adk/mcp_endpoint_tools.register_mcp_endpoint_tools(agent, endpoints)`** — speaks MCP\n JSON-RPC (`tools/list` to discover, `tools/call` to invoke) and registers a namespaced\n proxy tool (`{endpoint}__{tool}`) for each advertised tool. Mirrors `app_proxy_tools`.\n Best-effort: an unreachable endpoint registers nothing and never raises.\n- **`POST /stream` and `POST /chat/stream`** accept an optional `mcp_endpoints` list\n (`[{name, url, [headers]}]`); `_aitheros_stream` attaches them to the agent before the\n turn. Existing callers are unaffected (the field is optional).\n\n## [2.10.1] - 2026-06-14\n\n### Fixed — self-hosted `aither-serve` was crash-on-startup (2.10.0 regression)\nA fresh `pip install aither-adk && aither-serve --backend deepseek` could not boot. Found\nand fixed while standing up a real self-hosted DeepSeek agent end-to-end:\n- **Startup `NameError: name 'port' is not defined`** in three lifespan helpers\n (`_join_aithernet`, `_init_a2a_server`, `_start_mesh_hosting`) — they referenced a bare\n `port` no longer in scope. Now use `config.server_port`, and `main()` writes the resolved\n `--port`/`--host` back to `config` so every lifespan helper and the invoke_url see the\n actual bound port.\n- **Fleet self-registration crashed the whole server** — the registration handlers caught\n `(ImportError, RuntimeError, OSError, ConnectionError)` but **not** `httpx.HTTPError`, so a\n `RemoteProtocolError`/connect error talking to the control plane took the agent down. All\n network-facing lifespan handlers now also catch `httpx.HTTPError` (non-fatal, as intended).\n- **`--backend` was ignored when `AITHER_API_KEY` was set** — `_auto_detect` picked the cloud\n gateway before the explicit backend, and `_try_elysium_fallback` then replaced the chosen\n provider. An explicit backend (deepseek/openai/anthropic/your own vllm/ollama) now always\n wins; \"auto\"/\"gateway\" still flow through detection. The operator's own brain is never\n silently hijacked.\n- **Approved-tool trace truncated after `/sessions/{id}/confirm`** — the SSE relay treated\n `AgentResponse.tool_calls_made` (a `list[str]`) as dicts (`tc.get(...)`), raised\n `AttributeError`, and an over-narrow `except` swallowed it so the client saw only\n `session_start` + heartbeats. The relay now handles string tool names, the `except` is\n broad (always emits a typed `error` + terminal `complete`), and the module-level\n `_strata_ingest` `NameError` + Chronicle `session_id` kwarg mismatch are fixed.\n\n## [2.10.0] - 2026-06-13\n\n### Added — human-in-the-loop tool approval (`adk/approval.py`)\n- **Pause before a gated tool, resume on decision.** An agent can now pause its turn before\n executing a sensitive tool and wait for a human's allow/deny — so a managed control plane\n (or any operator) can gate tool use without forking the loop.\n- **Policy via `AITHER_TOOL_APPROVAL`** — a comma-separated list of tool names (optionally\n `agent:tool`), or `*` for every tool. Empty = no gating (unchanged behavior).\n- **Survives restarts / days-later approvals.** The paused turn (user message + pending tool\n calls) is persisted to disk keyed by `session_id`; resume is a fresh request, not a held\n connection. Decisions are recorded per `(session_id, tool_name)`.\n- **`AgentResponse.requires_action` / `.pending`** — set when a turn paused; `pending` lists\n the gated tool calls awaiting a decision.\n- **`AitherAgent.resume(session_id, decisions)`** — records the decisions and re-runs the\n paused turn (the gate consumes them: allowed tools execute, denied tools get a denial\n observation, the turn continues — and may pause again on a different gated tool).\n- **Server:** `POST /sessions/{id}/confirm` records decisions and streams the resumed turn;\n `/stream` emits a `requires_action` event when a turn pauses.\n\n## [2.9.0] - 2026-06-13\n\n### Added — universal LLM continuation primitive (`adk/llm/continuation.py`)\n- **`run_continuation()` + `stitch()`** — one shared continue-until-complete primitive. When a\n completion stops because it hit the output token cap (`finish_reason == \"length\"`), it continues\n the generation and STITCHES the chunks (overlap-dedup + restart-detect) into a complete answer,\n instead of every call-site hand-rolling its own retry. Bounded three ways (max rounds AND total\n output ceiling AND a no-growth break), and it never mutates the caller's message history.\n- **`LLMProvider.chat_with_continuation()`** and **`LLMRouter.chat_with_continuation()`** — every\n provider AND the router inherit the primitive, so any call-site gets continuation for free.\n- Kill-switch `ADK_LLM_CONTINUATION=off`; tunables `ADK_LLM_MAX_CONTINUATIONS`,\n `ADK_LLM_MAX_TOTAL_OUTPUT_TOKENS` (defaults: 2 rounds / 8192 tokens).\n\n### Changed\n- The ReAct loop's truncation recovery now uses the shared primitive. The previous inline\n doubling-retry REPLACED the partial response (losing earlier text in `resp.content`) and injected\n continuation scaffolding into the live message history; it now STITCHES and leaves history clean.\n Tool-call turns are never continued as text.\n\n### Included — previously-committed-but-unreleased changes\n- 2.8.2: TLS verification on by default (`verify=False` removed).\n- 2.8.1: baked license + pack-signing public keys.\n- 2.8.0: version-bump release.\n\n## [2.7.0] - 2026-06-05\n\n### Added — provider-agnostic LLM cost levers (at the `LLMProvider` ABC)\n- **Prompt caching.** New `ProviderCapabilities` (`prompt_cache: explicit|automatic|none`, `batch`) and\n normalized `LLMResponse.cache_read_tokens`/`cache_write_tokens`, so the \"tokens saved\" meter never\n depends on which backend served the turn. Anthropic inserts `cache_control` breakpoints over the stable\n system+tools prefix; OpenAI/DeepSeek caching is automatic and their `cached_tokens` are now surfaced\n (previously discarded). Default-on in `AitherAgent.chat`; override with `cache=False`.\n- **ResponseCache** (`adk/llm/cache.py`) — exact-match response cache with a pluggable backend, wired into\n `LLMRouter` as an opt-in (`LLMRouter(response_cache=…)` + per-call `cacheable=True`). OFF by default — no\n determinism change unless opted in.\n- **Gemini provider** (`adk/llm/gemini.py`) with explicit `cachedContents` caching; registered in the router.\n- **Async batch runner** (`adk/llm/batch_runner.py`) — `run_batch()` over the Anthropic Message Batches /\n OpenAI Batch APIs (~50% off) with a transparent fallback to concurrent `chat()` where unsupported.\n- **Graph-RAG retriever interface** (`adk/graph_retriever.py`).\n\n### Added — principal authority (prompt-injection defense enforced outside the model)\n- `adk/auth.py`: `Principal` / `AuthContext` / `PrincipalResolver` (+ deny-all default).\n- `ToolRegistry.execute(name, args, auth=…)` — a default-deny authorization gate at the tool boundary, so a\n forbidden tool call is blocked regardless of what the LLM emits. `ToolDef` gains `required_clearance` /\n `action_class`. Threaded through `AitherAgent.chat`; `auth=None` is fully backward-compatible.\n- `adk/channel_auth.py`: Telegram webhook-secret + CEO-id checks and HMAC-signed approvals.\n\n## [2.6.4] - 2026-06-05\n\n### Fixes\n- **Loop-guard nudges corrupted the message history → DeepSeek 400 mid-run.**\n When the ReAct loop emitted multiple tool calls in one assistant turn and the\n loop guard fired a WARN/BLOCK/CIRCUIT_BREAK on one of them, it spliced a\n standalone `system` nudge message *between* the tool results. DeepSeek (and\n strict vLLM chat templates) require an assistant `tool_calls` message to be\n followed by exactly one `tool` result per `tool_call_id`, contiguously — so the\n interleaved message produced `400 … insufficient tool messages following\n tool_calls message` and aborted the turn (this is reliably hit after crossing\n the 15-call circuit-break threshold, where every subsequent call WARNs).\n Per-call guidance is now folded into that call's own `tool` result, and any\n standalone steering is deferred to a single message AFTER the whole batch —\n preserving the `tool_calls`↔`tool_results` pairing every backend requires.\n Regression test: `deep-research-agent/tests/test_msg_structure.py`\n (loopguard-flood scenario).\n\n## [2.6.3] - 2026-06-04\n\n### Fixes\n- **Transient provider errors killed a run on the first blip.** A single\n `502 Bad Gateway` / `503` / `529 overloaded` / `429` from the upstream API\n aborted the whole request — the Anthropic provider had **no retry at all**, and\n the OpenAI-compatible provider only retried `429`. Both now retry transient\n statuses (`408/429/500/502/503/504/529`) with `Retry-After`-aware exponential\n backoff (3 attempts), and surface the provider's body if all retries are\n exhausted. Non-transient `4xx` (e.g. `400`) still fails fast. Applies to\n `chat` and `chat_stream` on both providers.\n\n## [2.6.2] - 2026-06-04\n\n### Fixes\n- **DeepSeek (and strict OpenAI-compatible backends) 400'd on long tool\n conversations.** The ReAct loop legitimately injects mid-conversation\n `system` steering messages (the \"[DIMINISHING RETURNS]\" hint after several\n low-output tool iterations, loop-guard nudges). OpenAI tolerates a non-leading\n `system` message; DeepSeek rejects it with a 400. Long research runs\n (many `fetch_url` calls) reliably tripped this. Fix: non-leading `system`\n messages are demoted to `user` at the OpenAI-compatible provider boundary\n (`_demote_nonleading_system`) — steering intent preserved, payload valid on\n every backend; leading system prompt(s) untouched.\n- **Opaque HTTP errors.** `resp.raise_for_status()` discarded the provider's\n response body, so a 400/422 surfaced as a bare `Client error '400 Bad\n Request'` with no cause. `_ensure_ok` now includes the provider's actual\n message (e.g. `context_length_exceeded`, `Invalid 'messages'`) in the raised\n error, for both `chat` and `chat_stream`.\n\n## [2.6.1] - 2026-06-04\n\n### Fixes\n- **`MCPServer.mount()` returned HTTP 422 for every `/mcp` call.** Under\n `from __future__ import annotations` the route handler's `request: Request`\n annotation is a string that FastAPI resolves against the *module* globals, but\n `Request` was imported locally inside `mount()` — so FastAPI couldn't resolve\n it and treated `request` as a required query param. The MCP server endpoint\n (`initialize`, `tools/list`, `tools/call`) was unusable when mounted. FastAPI\n symbols are now imported at module level (guarded; the `[node]` extra still\n gates actual use). Any agent's tools can now be exposed as a working MCP\n server with `MCPServer(...).mount(app)`.\n\n## [2.6.0] - 2026-06-04\n\n### Streaming + scaffolding\n- **`AitherAgent.stream_chat`** — reliable streaming sibling of `chat()`: native\n tool-calling loop (concludes on its own, unlike the text-protocol `stream_react`)\n that emits live `tool`/`tool_result` events and streams the answer as `token`\n events. `token_delay` paces tokens for a typing effect.\n- **`adk new <template>`** — scaffold a full, runnable template app. Ships the\n `deep-research` template (a cited web-research agent: pack + server + web UI):\n `adk new deep-research`.\n\n### Fixes\n- **BYO-key no longer community-capped.** The free-tier monthly token cap now applies\n only to the metered Aitherium gateway backend, never to a user's own provider key\n (Anthropic/OpenAI/DeepSeek/Ollama/vLLM). Previously it could brick a self-hosted\n agent after ~100k tokens on the user's own key.\n- **Stronger, grounded memory recall.** `chat()` now injects the top-6 knowledge-graph\n hits (untruncated) with a hard no-fabrication instruction — was top-3 truncated to\n 200 chars, which surfaced partial facts and let weaker models invent the rest.\n- **Tool-arg coercion.** `ToolRegistry.execute` coerces LLM-supplied args toward each\n parameter's annotated type, so a tool no longer crashes when a model passes a list\n or string where an int/float/bool was expected.\n\n### Docs\n- New `docs/AGENT_DEV_GUIDE.md` — the golden path for building agents/packs.\n\n## [2.5.0] - 2026-06-04\n\n### Continual learning — agents that learn from runs and reuse memory\n\nAgents now improve the second time they do a task instead of starting from scratch.\n\n### Added\n- **Recall-before**: `AitherAgent.chat()` searches the local `SkillStore` and injects the\n top matching learned skills as a system block, so the model reuses proven procedures.\n- **Learn-after**: `_learn_after()` runs after each response — reinforces (`success_count++`)\n any skills that were recalled+reused, and extracts + saves a NEW skill from successful\n multi-tool runs via `SkillExtractor`. Wired at both `chat()` return paths.\n- Gated by `AITHER_SKILLS` (default on). The previously-unused `SkillStore`/`SkillExtractor`\n are now connected to the run loop.\n\n### Notes\n- Pairs with the platform-side LearnedProcedure substrate (recipes → scored procedures →\n skills/tools/A2A/packs → MCTS priors → evolution). The SDK shares memory via the Spirit\n bridge when configured.\n\n## [2.0.0] - 2026-06-02\n\n### Open-core boundary (BREAKING)\n\nThis release draws a single, enforced free/paid line. The free tier stays\ngenuinely useful (real agent, typed + graph + code memory, ReAct, ~essential\ntools); paid tiers unlock the *scale* capabilities via portal.aitherium.com.\n\n### Added\n- **`adk/licensing.py`** — the entitlement keystone. Resolves a tier\n (COMMUNITY by default), verifies optional portal-signed `~/.aither/license.json`\n (Ed25519), and answers `can_use_fleet/channels/cron/swarm/auto_neurons`,\n `is_agent_licensed`, `max_effort`, etc. **Fail-closed**: no/invalid/expired\n license → free tier; an unsigned license never grants premium.\n `AITHER_TENANT_SLUG=aitherium` → unrestricted INTERNAL tier.\n- **Moat guard** (`scripts/check_moat_boundary.py`) wired into the publish\n workflow — blocks any wheel that leaks the moat or drops the keystone.\n- Tests: `tests/test_licensing.py`, `tests/test_moat_boundary.py`.\n\n### Changed (gating — fail-closed)\n- Free tier effort is capped at 3 (reasoning effort 7–10 needs Professional).\n- Monthly token budget hard-block on the free tier (no silent overspend).\n- Gated behind a paid tier: fleet mode (`FleetConfig`), cross-agent delegation\n (`AgentForge.delegate`), channel adapters, cron scheduler, proactive\n auto-neurons, and `swarm`/`swarm_code` dispatch.\n\n### Removed\n- **`adk/nanogpt.py`** (on-device training IP) removed from the published SDK\n and the public repo; relocated to the internal AitherOS moat. Excluded from\n the wheel as defense-in-depth.\n- Pre-2.0 releases shipped this IP and ungated capabilities and are being\n yanked (PyPI) / removed (GitHub releases). See `scripts/yank_leaky_releases.py`\n and `scripts/purge_public_leaks.sh`.\n\n## [1.20.0] - 2026-05-27\n\n### Fixed\n- **Pack discovery path mismatch** — `_find_local_packs()` now scans both\n `~/.aither/packs/` and `~/.aitheros/packs/`, so packs installed via\n `adk pack install` are discovered by `adk-workspace`.\n\n### Added\n- **Auto-register on pack install** — `adk pack install` now auto-registers\n with the portal if the pack contains an `agent.yaml` with a `portal` section.\n One command does download + extract + register.\n- **Flux event on agent registration** — `developer_portal.py` emits\n `agent.registered` Flux event so the portal fleet UI updates in real-time.\n\n## [1.15.0] - 2026-05-26\n\n### Added\n- **Cloud memory** — `adk quickstart --cloud` now tests gateway memory and\n auto-configures Spirit URL so memories persist across devices via\n `gateway.aitherium.com/v1/memory/teach` and `/v1/memory/recall`.\n- **Config→env export for cloud mode** — `Config.from_env()` now exports\n `AITHER_CLOUD_MODE`, `AITHER_SPIRIT_URL`, and spirit path env vars from\n saved config so Memory and other modules pick them up without needing\n explicit env vars set by the user.\n- **Cloud Quick Start section** in README — 3-command setup for users who\n just want a Claude/GPT-4/DeepSeek API key and the full agent harness.\n- **`--memory` profile for node deployment** — `adk deploy node --memory`\n adds Spirit (8087) + WorkingMemory (8101) containers for persistent\n vector memory. Without this, agents use local SQLite only.\n- **Configurable vLLM in node compose** — `AITHER_VLLM_MODEL`,\n `AITHER_VLLM_GPU_UTIL`, `AITHER_VLLM_CTX_LEN`, `AITHER_VLLM_QUANT_ARGS`\n env vars let `adk setup` write GPU-detected tier settings to `.env`\n and have the node compose pick them up (TQ4 for small GPUs, BNB for large).\n\n### Fixed\n- **Memory cloud routing** — `Memory._spirit_teach()` and `_spirit_recall()`\n now use configurable paths and auth headers for gateway proxy. Previously\n cloud-only users silently lost all Spirit memory calls.\n\n## [1.9.0] - 2026-05-24\n\n### Added\n- **Composable ToolPack system** — agents can load tool packs from\n `.toolpack.yaml` directories. Drop a pack in `~/.aitheros/packs/` or\n set `AITHER_PACKS_DIR`, and any agent discovers it automatically.\n- **`tools.packs` in agent.yaml** — list tool pack IDs to auto-load\n (e.g. `packs: [crm-tools, calendar-pro]`).\n- **`register_tool_packs(agent, pack_ids, packs_dir)`** in `builtin_tools` —\n programmatic pack loading for agents.\n- **`AITHER_TOOL_PACKS` env var** — comma-separated pack IDs loaded on\n agent init (alternative to YAML config).\n- **Unknown builtin categories resolve as pack IDs** — `tools.builtin:\n [file_io, crm-tools]` now works: `crm-tools` is looked up as a pack.\n\n## [1.8.0] - 2026-05-24\n\n### Removed\n- **11 dead modules** (3,023 lines) — `agent_deploy`, `cloud_deploy`, `create`,\n `phonehome`, `sdk_bridge`, `sovereign`, `telemetry`, `telemetry_config`,\n `error_reporter`, `anonymizer`, `package`. None were imported anywhere.\n- **29 ghost exports** from `__init__.py` — `secrets`, `otel`, `fs_sandbox`,\n and `vector_memory` modules were listed in `__all__` and `__getattr__` but\n the backing files never existed. Accessing them raised `ModuleNotFoundError`.\n- **Stale dist/ artifacts** — removed old wheels/tarballs (v0.4.0–v1.4.1, 11 MB).\n- **Orphan test scripts** — `test_phase1.py` and `test_phase23.py` (one-off\n validation scripts not part of the pytest suite).\n\n### Fixed\n- **Version mismatch** — `__init__.__version__` now matches `pyproject.toml`\n (was stuck at 1.6.0).\n\n## [1.5.0] - 2026-05-23\n\n### Added\n- **`--sovereign` flag for `adk deploy node`** — registers the node with the\n Aitherium hub via federation after deployment. Saves federation credentials\n to `~/.aither/.env.federation`. Accepts `--hub` (custom hub URL) and\n `--tenant` (tenant slug) options. Non-fatal: node runs standalone if\n registration fails.\n- **Federation → fleet cross-post** (Genesis) — sovereign nodes that register\n or heartbeat via `/federation/register` and `/federation/heartbeat` now\n automatically appear in the fleet dashboard (`/fleet/deployments`) with\n `node_type: \"sovereign\"` and live status/metrics.\n- **\"Deploy Sovereign Node\" section on Connect page** (AitherVeil) — Section 8\n with ADK deploy commands, fleet dashboard links, and provision page links.\n- **`deploy-node` action in tunnel proxy** (AitherVeil) — Connect page and\n external callers can now request bootstrap scripts via\n `POST /api/tunnel/devworkspace?action=deploy-node`.\n\n### Fixed\n- **Fleet provision page** — API URL corrected from `/api/fleet/provision` to\n `/api/bridge/genesis/fleet/provision` (matches bridge proxy pattern).\n- **Fleet detail page** — API URL corrected from `/api/fleet/deployments/` to\n `/api/bridge/genesis/fleet/deployments/` (same fix).\n- **Dockerfile** — entry point corrected from `aither-serve` to `adk-serve`\n (matches `pyproject.toml` script registration). Version label updated.\n\n## [1.4.1] - 2026-05-22\n\n### Fixed\n- **macOS executable builds** — dropped `--target-arch universal2` from\n `packaging/build_executable.py`. PyPI wheels for `pydantic_core` and other\n native deps are arch-specific, not fat binaries, so universal2 always failed\n with `IncompatibleBinaryArchError`. Now builds for the runner's native arch\n (`aither-macos-arm64`).\n- **Release workflow strategy** — added `fail-fast: false` so a single\n executable build failure no longer cancels the other OS jobs.\n- **PyPI publish** — added `skip-existing: true` so re-runs of the same\n version don't fail when the artifact already exists.\n- **GitHub Release job** — now runs whenever `pypi` succeeds even if some\n executable matrix entries fail, so the release page is always created with\n whatever binaries did build successfully.\n\n## [1.4.0] - 2026-05-22\n\n### Added\n- **Sovereign template now ships with `agent_core` 5-tier memory** — generated\n backends include the canonical `agent_core/` package (working → episodic →\n semantic → procedural → identity) plus `agent_memory.py` helper, vendored\n from `portal-kit-backend`.\n- `aiosqlite>=0.20.0` added to sovereign template `requirements.txt` for\n local agent_core SQLite storage.\n- Document tombstoning: `evict_cached_conversations_referencing` is wired\n through document-delete handlers in consumer apps so memories citing\n deleted documents are blocked from recall.\n- New tests: 13 smoke tests for `portal-kit-backend/agent_core` covering\n promotion thresholds, decay rates, store roundtrip, tombstone recall\n blocking, circuit breaker state machine, and chat-turn recording.\n\n### Changed\n- `scripts/vendor_agent_core.py` now syncs canonical `agent_core` to 4\n targets: WorkspaceRuntime, ADK sovereign template, and the GargBot +\n Chelle consumer mirrors (previously only 2).\n\n## [1.2.1] - 2026-05-21\n\n### Added\n- `adk train` command group — 6 subcommands: status, launch, logs, cancel, runs, register-gpu\n- `/slash-commands` endpoint — ADK server auto-exposes all 35 CLI commands as structured JSON manifest for AitherShell\n- `/cli/execute` endpoint — AitherShell can run any CLI command through the server\n- `build_command_manifest()` — introspects full argparse tree (commands, args, choices, subcommands)\n- `_register_commands()` extracted from main() for shared parser construction\n\n### Changed\n- `_get_genesis_url()` now probes: Genesis → ADK server → Aitherium cloud gateway (works without Genesis)\n\n## [1.2.0] - 2026-05-21\n\n### Added\n- **Runtime backend switching** — `agent.switch_backend(\"deepseek\")` and `agent.llm.switch_backend()` change provider without recreating agent\n- **Hybrid reasoning** — `agent.set_reasoning_backend(\"anthropic\")` routes effort 7+ to cloud API while keeping local orchestrator\n- **First-class DeepSeek provider** — `deepseek` in effort model table with `deepseek-chat` and `deepseek-reasoner`\n- **TQ4 quantization tiers** — `nano` (6GB, TurboQuant 4-bit), `standard-tq4` (12GB, both models TQ4), `hybrid-tq4` (6GB + cloud reasoning)\n- **`adk backend` CLI** — `list`, `set`, `set-reasoning`, `test` subcommands\n- **`adk quickstart`** — unified first-run wizard (setup + auth + shell)\n- **`adk tools`** — list local + MCP tools with tier markers\n- **`adk backup`** — tarball export of `~/.aither/`\n- **`adk ingest`** — feed docs/files into knowledge graph\n- **`adk setup nemotron`** shortcut — alias for `--tier lite`\n- **`--reasoning-api`** flag in setup — `anthropic`, `openai`, `deepseek`, `gateway`\n- **`--dgx-spark URL`** flag — remote vLLM endpoint configuration\n- **DGX Spark auto-detection** — scans `spark.local`, `192.168.0.33`, `AITHER_DGX_URL` env\n- **Post-setup smoke test** — sends real inference request after container startup\n- **Schema migration system** — version table + migration runner in `memory.py` and `graph_memory.py`\n- **Configurable vLLM port scan** — `AITHER_VLLM_PORTS` env var for custom ports\n- **`aithershell` entry point** — alias for `adk` in pyproject.toml\n- Shell auto-downloads binary on first `adk shell` (no `--install` needed)\n- Shell pre-flight check warns if no backend detected\n- Shell passes full config via env vars (API key, tenant, inference URL)\n- DGX/Remote and Cloud API checks added to `adk doctor`\n- Config fields: `reasoning_backend`, `reasoning_api_key`, `deepseek_api_key`, `dgx_url`, `vllm_extra_ports`\n\n### Fixed\n- `test_aeon_chat_basic` timeout — mock scope was exiting before request\n- `adk doctor` check_disk timeout — cap rglob at 5000 files\n\n## [1.1.7] - 2026-05-19\n\n### Added\n- Remote agent pipeline — `adk onboard` → fleet dispatch → remote inference\n- Agent-to-agent federation with mesh relay\n- `adk connect` for desktop mesh enrollment\n\n## [1.1.6] - 2026-05-19\n\n### Fixed\n- Tool call execution recovery across all backends\n- Hermes tool call fallback parsing for vLLM\n\n## [1.1.5] - 2026-05-19\n\n### Fixed\n- Tool call execution + Hermes fallback parsing improvements\n\n## [1.1.0] - 2026-05-18\n\n### Added\n- Multi-channel gateway (Telegram, Discord, Slack, webhook)\n- Aeon group chat with 7 presets\n- Skills auto-extraction from multi-step sessions\n- MCP stdio server for Claude Code integration\n- `adk gateway`, `adk cron`, `adk skills`, `adk soul` commands\n- SOUL.md import/export for portable agent identity\n- Elysium cloud device-flow authentication\n\n## [1.0.0] - 2026-05-18\n\n### Breaking\n- Package consolidated — `aither_adk` namespace replaced by `adk` + `adk.platform`\n- 93 duplicate files removed from `AitherOS/packages/aither_adk/`\n\n### Added\n- Standalone core primitives (`adk.core`: Agent, Tool, Memory, ModelBackend, Capability, Trace)\n- `adk.platform` sub-package merging all platform toolkit modules\n- Compatibility shims for legacy `from aither_adk.*` imports\n\n### Changed\n- Version synced across pyproject.toml, `adk/__init__.py`, `adk/platform/__init__.py`, npm package\n- Classifier promoted from Alpha to Beta\n\n## [0.16.0] - 2026-04-16\n\n### Swarm Coding Engine & Repowise Integration\n- **`repowise_search` tool** — Semantic + keyword hybrid code search via Repowise, with ripgrep fallback\n- **`swarm_code` tool** — Dispatch complex tasks to 11-agent swarm pipeline (ARCHITECT->SWARM->REVIEW->JUDGE)\n- **`agent.swarm()`** — Async convenience method with configurable mode, effort, timeout\n- **`agent.code_search()`** — Async convenience method returning structured results\n- New tool categories: `repowise` and `swarm` in TOOL_CATEGORIES\n- `repowise_search` and `swarm_code` added to `__init__.py` exports with lazy loading\n- `IDENTITY_DEFAULTS` updated: repowise for code-focused agents, swarm for orchestration agents\n- Standalone graceful degradation: repowise falls back to ripgrep, swarm returns structured error\n\n## [0.13.0] - 2026-04-02\n\n### Graph Faculties — Local Knowledge for Every Agent\n- **CodeGraph** (2,799 lines) — Full Python AST indexer with call graph, keyword/semantic/hybrid query, embedding matrix cache, incremental re-indexing, multi-hop chain expansion\n- **MemoryGraph** (1,339 lines) — Graph-based persistent agent memory with 10 edge types, hybrid query (keyword + semantic + graph expansion), multi-hop recall, pickle persistence\n- **EmbeddingProvider** — 4-backend fallback chain: sentence-transformers (GPU/CPU) -> Ollama -> Elysium cloud -> feature hashing (zero deps)\n- **BaseFacultyGraph** — Abstract base with HMAC-SHA256 validated pickle persistence\n\n### Agent Integration\n- `agent.set_code_graph(cg)` — auto-registers `code_search` + `code_context` tools\n- `agent.set_memory_graph(mg)` — auto-registers `remember` + `recall` + `memory_stats` tools\n- Both graphs inject context into LLM prompts automatically during chat\n\n### Zero-Config Onboarding\n- `adk start` / `adk` (no args) — auto-detect project, index code, detect LLM, persistent memory, interactive chat\n- Works for any directory: Python codebases, doc folders, mixed workspaces\n- Auto-detects LLM: Ollama -> vLLM -> Elysium -> OpenAI -> Anthropic\n- Per-project persistent memory in `~/.aither/memory/<project>`\n- `adk index <path>` — standalone indexing with progress bar and stats\n\n### MCP Gateway\n- `POST /v1/embeddings` — OpenAI-compatible embedding proxy to vLLM-embeddings:8209\n- Available to all tiers (embeddings are free)\n- ADK EmbeddingProvider uses this as Elysium cloud fallback\n\n### Optional Dependencies\n- `pip install aither-adk[graphs]` — numpy for 10x cosine similarity speedup\n- `pip install aither-adk[embedding]` — sentence-transformers + torch for local GPU embeddings\n- `pip install aither-adk` alone — graphs work with feature hashing (zero deps)\n\n## [0.12.0] - 2026-04-01\n\n### Bootstrap & Service Discovery\n- Version handshake -- ADK checks major.minor compatibility with Genesis/Node\n- Background reconnect loop -- ServiceBridge re-probes every 30s in standalone mode\n- Port 8080/8090 documented -- MCP vs OpenAI-compat clearly separated\n- Genesis URL configurable via GENESIS_URL env var (was hardcoded)\n- Standalone mode warning -- visible stderr alert when AitherOS not detected\n- Auto-reconnect on startup when services come online\n\n### Elysium Cloud Inference\n- Unified gateway URL -- gateway.aitherium.com handles auth + billing + inference\n- Streaming inference -- SSE passthrough for /v1/chat/completions with stream=true\n- Auth proxy routes -- /v1/auth/register, /v1/auth/login, /v1/auth/me\n- Billing proxy -- /v1/billing/balance through gateway\n- AitherConnect Elysium fallback -- cloud inference when local Genesis is down\n- AitherDesktop Elysium fallback -- third-tier chat fallback after Node\n\n### Infrastructure\n- /discovery endpoint on Genesis -- unified service URLs/versions/health\n- /api/config/services on Veil -- runtime port config for client-side JS\n- Veil healthcheck gates on Genesis -- unhealthy when backend is down\n- Desktop crash detection 90s->60s (threshold 3->2)\n\n## [0.11.0] - 2026-04-01\n\n### Agent Execution Quality (Claude Code Parity)\n- Raise loop guard block threshold from 3 to 4 -- agents get more room for iterative search\n- Soft synthesis nudge for effort >= 4 (no tool stripping, trust the model)\n- max_output_tokens escalation -- retry up to 3x when response is truncated\n- Tool result pairing guarantee -- synthetic error for orphaned tool_use blocks\n- Micro-compaction of old tool results -- save context tokens on long sessions\n- First-turn tool forcing only for effort >= 6 (trust model for lower effort)\n- Diminishing returns detection -- nudge agent when 3+ turns produce < 500 tokens\n- Message normalization -- merge consecutive same-role messages, strip empties\n- LLM retry with exponential backoff (5 retries, 500ms-16s, jitter)\n\n## [0.9.0] - 2026-03-16\n\nThe \"connected world\" release. Cross-platform identity pairing, voice capabilities, and multi-channel integration.\n\n### Added\n- **Pairing**: Cross-platform identity linking (`adk/pairing.py`)\n - `PairingManager` — SQLite-backed identity linking with 6-char pairing codes\n - Link users across Telegram, Discord, Slack, WhatsApp with 10min TTL codes\n - Canonical session IDs for cross-channel conversation continuity\n - `get_session_id()` returns \"user-{id}\" for paired users\n- **Voice**: Speech-to-text and text-to-speech client (`adk/voice.py`)\n - `VoiceClient` — async STT/TTS/emotion via AitherVoice service\n - Convenience functions: `hear()`, `say()`, `feel()`\n - 6 voice options: alloy, echo, fable, nova, onyx, shimmer\n - Emotion detection with intensity scoring\n- New exports: `PairingManager`, `PairingResult`, `PlatformIdentity`, `VoiceClient`, `TranscriptionResult`, `SynthesisResult`, `EmotionResult`\n\n### Changed\n- `__init__.py` exports expanded with pairing and voice symbols\n\n## [0.6.0] - 2026-03-13\n\nThe \"group mind\" release. Multi-agent group chat, creative tools, and Iris identity.\n\n### Added\n- **Aeon**: Multi-agent group chat engine (`adk/aeon.py`)\n - `AeonSession` — persistent group chat with parallel agent execution\n - 7 presets: balanced, creative, technical, security, minimal, duo_code, research\n - Orchestrator synthesis: Aither summarizes all agent responses\n - Serial execution for Ollama, parallel for vLLM/cloud\n - ConversationStore persistence with `type: \"aeon\"` metadata\n - `group_chat()` one-shot convenience function\n- `aither aeon` CLI command — interactive terminal group chat with color-coded agents\n - `-p/--preset`, `-a/--agents`, `-r/--rounds`, `--no-synthesize` flags\n - `reset` and `quit` commands\n- Server endpoints: `POST /aeon/chat`, `GET /aeon/presets`, `GET /aeon/sessions/{id}`\n- Creative tools in builtin_tools: `image_generate`, `image_refine`, `image_search`, `video_generate`\n- Iris agent identity with visual generation capabilities\n- 48 Aeon tests (data models, presets, chat, context, persistence, server, exports)\n\n### Changed\n- `__init__.py` exports: `AeonSession`, `AeonResponse`, `AeonMessage`, `group_chat`, `AEON_PRESETS`\n\n## [0.5.0] - 2026-03-13\n\nThe \"tenant-ready\" release. Multi-tenant admin, permission grants, safety profiles, and a full setup wizard.\n\n### Added\n- `aither setup` interactive setup wizard with hardware detection, model selection, identity config\n- Strata storage backend: SQLite WAL persistence for conversations, memories, knowledge graphs\n- CLI test runner: `aither test` with auto-discovery and parallel execution\n- Permission grants system for MCP tool access control\n- MCP account management tools\n- LLM provider auto-detection for Ollama and vLLM endpoints\n- Apache-2.0 LICENSE file\n- Elysium desktop sync module\n- Comprehensive CLI test suite (493 tests)\n- Strata storage test suite (972 tests)\n- LLM provider tests (82 tests)\n\n### Changed\n- CLI expanded: `aither setup`, `aither test`, `aither bugreport`, `aither doctor`\n- Strata module rewritten as full local-first storage engine\n- Server startup includes Strata initialization\n- README rewritten with clearer quickstart and architecture docs\n\n### Fixed\n- Dead documentation links\n- Python 3.11 compatibility issues\n- Ruff per-file-ignores configuration\n- Identity provisioning edge cases\n- Docker node-gyp native dependency builds (python3 + build tools)\n\n## [0.4.0] - 2026-03-13\n\nThe \"own your AI\" release. Self-hosted agent OS for people who don't want their data on someone else's servers.\n\n### Added\n- Muse agent identity (creative/artistic generation)\n- Port 8120 to vLLM scan for ExoNodes discovery\n- Public roadmap with milestone-based porting schedule\n- Competitive positioning: self-hosted alternative to cloud-locked AI appliances\n\n### Changed\n- LLM router: compute-aware effort routing (replaces effort-level context gating)\n- README rewritten for sovereignty-first messaging\n- Package description updated\n- Promoted from alpha to stable release\n\n### Fixed\n- Version string consistency between pyproject.toml and __init__.py\n- vLLM port scanning now includes port 8120 (ExoNodes)\n\n## [0.3.1] - 2026-03-09\n\n### Added\n- GraphMemory: SQLite knowledge graph with Ollama embeddings and hybrid search\n- NeuronPool: auto-fire pre-LLM data gathering agents\n- NanoGPT: pure Python char-level transformer with LoRA fine-tuning\n- Safety gates: IntakeGuard (input), LoopGuard (recursion), Sandbox (code exec)\n- Event system: EventEmitter with chat/tool/forge event types\n- Builtin tools: identity-based tool selection\n- ServiceBridge: auto-discovery of AitherOS services\n- Streaming: chat_stream with safety gate integration\n- Auth middleware: Bearer token authentication\n- CLI: `aither init` and `aither serve` commands\n\n### Changed\n- Wired 5 previously disconnected modules into agent loop\n- 522 total passing tests (up from 85)\n\n## [0.3.0] - 2026-03-07\n\n### Added\n- Clean-room agent development kit\n- Multi-backend LLM providers (Ollama, OpenAI, Anthropic)\n- AitherAgent class with @tool decorator\n- SQLite conversation memory\n- OpenAI-compatible server (`aither-serve`)\n- 16 agent identities as package data\n- MCP bridge to mcp.aitherium.com\n- Privacy-centric opt-in telemetry\n- Bug reporting CLI and API\n- FastAPI server with OpenAI-compatible endpoints\n- Hardware auto-detection (5 tiers, 11 profiles)\n- Fleet orchestration and multi-agent coordination\n- A2A mesh protocol\n- Federation protocol for cross-instance agent dispatch\n\n### Initial release\n- 85 passing tests\n- Apache-2.0 license\n"}, "ddb9269595b4f21a88bad214cb02e928ebfe856e": {"t": 1785748178.582528, "n": 76008, "h": "048a7d90aa005dd3", "c": "# Changelog\n\nAll notable changes to aither-adk will be documented in this file.\n\n## [2.21.0] - 2026-07-14\n\n### Added — Completion gate: verified-or-retried task execution\n\n- **`adk.gate.CompletionGate`** — a composable wrapper that closes the \"silent\n success\" gap: an agent's ReAct loop returns whatever the model produced and\n calls it done (`finish_reason = stop/max_steps/length`), which is a *liveness*\n signal, not a *completion* one — so an agent can \"succeed\" while doing nothing.\n `CompletionGate` runs the agent, verifies the result against acceptance\n criteria, retries with the failure fed back, and returns the response with\n `finish_reason` set to `verified` or an honest `unverified`. Never a false pass.\n - **Hard-first, un-soft-passable checks** (`adk.gate.hard_checks`): a filesystem\n path a criterion says must exist is checked on disk; a required quoted token\n must appear in the output. Only genuinely subjective criteria fall through to\n an optional LLM judge (reuses the agent's own model), which **fails closed**\n if unavailable.\n - **`gated_run(agent, task, criteria=..., max_retries=2)`** — one-shot helper.\n - `auto_criteria` (LLM writes the machine-checkable definition-of-done from the\n task), a `verifier=` override for custom checks (tests-pass / HTTP probe), and\n a `requires_action` short-circuit so it never fights human-approval pauses.\n - Compose it onto any agent exposing `run(task) -> resp{.content, .finish_reason}`\n (`AitherAgent`, `core.Agent`, or a thin adapter over a remote dispatch).\n - Exported from the top-level package: `from adk import CompletionGate, gated_run`.\n\n## [2.19.0] - 2026-07-12\n\n### Added — Eve agent importer + durable A2A tasks + canonical agent discovery\n\n- **`adk pack import <eve_agent_path>`** — convert external Eve agents to AitherADK packs\n (from GitHub or local filesystem). Maps Eve structure (instructions → system prompt,\n agent.ts → agent.yaml, skills/*.md → skills, connections/*.ts → MCP, tools/*.ts →\n tools/node verbatim) to AitherADK format with a single command. Eve agents (compiled\n manifest format, schema v35) become installable packs compatible with `adk pack install`.\n- **Durable A2A task lifecycle (`TaskManager` backed by FileStore)** — agent-to-agent\n tasks now survive container restarts. Tasks persist as append-only JSONL\n (`.adk/tasks.jsonl`); in-memory dict for fast access; `create_task()` / `update_status()` /\n `add_artifact()` / `add_message()` all save to disk. Google A2A v0.3.0 spec compliant.\n- **Canonical agent card discovery** — `A2AServer.mount()` now exposes:\n - `GET /.well-known/agent-card.json` — Agent Card (canonical; contains agent identity,\n capabilities, skills, endpoints)\n - `GET /.well-known/agent.json` — Agent Card (legacy, redirects with 308 to canonical path)\n - `POST /a2a` — JSON-RPC 2.0 for message send/task lifecycle\n - `GET /a2a/tasks/{id}/subscribe` — SSE streaming task updates\n- **Four built-in agent packs now complete and catalogued**:\n - `hermes` — architecture review & trade-off analysis\n - `openclaw` — web research & source verification\n - `claude-code` — feature development & debugging\n - `analyst` — structured data inference & anomaly detection\n All discoverable via `adk packs` / `adk install pack:<name>`.\n- **Tolerant tool-call JSON parsing** — malformed tool JSON (extra properties, missing\n name/arguments, string-encoded nested args, missing keys) is now gracefully degraded\n instead of silently failing. `_parse_tool_json()` accepts both `\"arguments\"` and\n `\"parameters\"` keys, coerces string-encoded args, and skips unparseable calls instead\n of crashing the turn.\n\n### Known limitations\n\n- Eve agent importer: remote GitHub fetching not yet implemented (local paths only).\n- Durable task store: currently JSONL (suitable for thousands of records, not millions).\n\n## [2.18.0] - 2026-07-10\n\n### Added — AitherShell command center + Claude Code session management\n\n- **`aither sessions`** — Claude Code session manager: interactive full-screen\n browser (type-to-filter, transcript preview, deep full-text `search`, resume\n **in the current terminal** or as Windows Terminal tabs), crash guard\n (`guard install` = at-logon watchdog that snapshots live sessions and\n auto-restores the whole set after a terminal crash), and `ingest [--watch]`\n (incremental, secret-guarded sync of session conversations into the local KB\n / CompanyBrain via the standard ingest pipeline).\n- **`aither hq`** — command-center dashboard: per-service fleet health, LLM\n queue (depth/VRAM/models), Pulse alerts, live session count, inbox unread —\n auto-refreshing, with one-key jumps into chat, sessions, inbox, agents,\n brief, watchtower, and docker recovery.\n- **`aither inbox`** — unified queue over CommCore mail, Relay\n mentions/notifications, and Pulse alerts; open/mark-read/DM-reply from the\n terminal. Every source degrades independently.\n- **`aither agents`** — roster console + `agents ask -e <1-10> <agent> <q>`\n (effort-tiered ask via Genesis, with automatic `/chat/stream` fallback when\n the `/agent/sync` execute gate rejects host callers) + forge dispatch\n inspection + routine health.\n- **`aither palette`** — universal fuzzy picker across actions, sessions, and\n services; **`aither brief`** — renders Atlas's executive briefing in the\n terminal; **`aither watch`** — fleet watchtower with named wedge-signature\n detection (docker-WSL wedge, LLM-queue stall, crash-loop uptime regression)\n and optional docker auto-recovery.\n- All fleet reads go through a shared fail-soft `FleetClient` (internal-CA\n TLS via `adk._tls`, dashboard-grade timeouts, per-source degraded states).\n- Node shell-cli (`@aitherium/shell-cli` 1.13.0): `aither\n sessions|hq|inbox|palette|brief|watch|agents|docker` now pass through to the\n Python shell instead of being intent-classified as chat prompts.\n\n## [2.17.1] - 2026-07-05\n\n### Fully self-service mesh join — no customer key handling\n\n- **Auto-join headscale off the onboard response.** The conductor now auto-issues a\n headscale pre-auth key for NAT'd nodes and returns it in the `/v1/mesh/onboard`\n response; `mesh.join` consumes it and brings up the tunnel automatically. A customer\n runs one command — they never mint, fetch, or set a headscale key. Explicit\n `--headscale-key`/`AITHER_HEADSCALE_AUTH_KEY` still take precedence.\n- **Security:** `_tailscale_up` now redacts the auth key from all logs and exceptions\n (a failed `tailscale up` previously echoed the full argv, leaking the key into logs).\n\n## [2.17.0] - 2026-07-05\n\n### Added — Headscale mesh transport + local Qdrant + AitherConnect onboarding\n\n- **Headscale NAT-friendly mesh transport.** When raw WireGuard UDP:51820 is not\n viable (customer boxes behind NAT, corporate firewalls, CGNAT), `adk mesh join\n --headscale` routes the mesh tunnel through Tailscale's Headscale control plane.\n The overlay IP is still Conductor-assigned (10.77.0.0/16); Headscale provides\n only the transport layer. Configured via `AITHER_MESH_TRANSPORT=headscale`,\n `AITHER_HEADSCALE_URL` (default: https://headscale.aitherium.com), and\n `AITHER_HEADSCALE_AUTH_KEY` (pre-generated key). Automatic fallback to raw\n WireGuard if Headscale setup fails.\n- **Local Qdrant vector DB provisioning.** `adk stack` now provisions Qdrant\n locally when `AITHER_VECTOR_DB=qdrant` is set, enabling offline-first RAG\n without external dependencies. Tiered: local Qdrant in-fleet, cloud Nexus\n for enterprise multi-tenant deployments.\n- **Conductor URL public fallback.** `adk mesh join` resolves the internal\n `aitheros-conductor:8193` hostname; if unreachable (e.g., self-hosted nodes),\n it falls back to the public `conductor.aitherium.com:8193` endpoint,\n eliminating bootstrap configuration friction.\n\n### Documentation\n\n- **Self-hosting runbook.** New `docs/SELF_HOSTING.md` covers deploying AitherOS\n on customer infrastructure with Qdrant + mesh + AitherConnect onboarding.\n- **Mesh transport constraints documented.** Raw WireGuard requires public\n UDP:51820 endpoint (cloud instances, dedicated hardware); Headscale for\n NAT'd networks. Noted in CLI help and module docstrings.\n\n## [2.15.0] - 2026-07-04\n\n### Added — built-in web admin console + portal-profile settings sync\n\n- **Web admin console.** The page `adk up` opens (served at `/` and `/ui`) is now a full,\n self-contained console — Chat plus six admin tabs: **Backend** (view/switch LLM provider,\n set keys, test connection), **Packs** (list/enable/disable/reload tool packs),\n **Sessions** (browse/delete conversations), **Logs** (redacted agent-log tail),\n **Graph** (knowledge-graph search/neighborhood/stats), and **MCP** (add/remove external\n MCP servers). No build step, no CDN — a single packaged HTML asset. The original minimal\n streaming chat page stays at `/chat` for back-compat.\n- **Admin API (`adk/admin_api.py`).** ~20 endpoints under `/admin/*`, all bearer-gated by the\n existing middleware (never in `_skip_auth_paths`). Operates on the live agent — LLM backend\n swap (`LLMRouter.switch_backend`), pack reload, and MCP registration take effect without a\n restart.\n- **Settings sync (`adk/settings_sync.py`).** The user's portal profile is the source of\n truth: on startup the agent pulls `preferences.adk` and applies it over the local\n `~/.aither/config.yaml` cache; admin-console edits push a debounced snapshot back to\n `PUT /api/settings/preferences`. Provider API keys and MCP auth headers are device-local\n secrets and are never included in the pushed snapshot. Opt out with `AITHER_SETTINGS_SYNC=false`.\n\n### Security\n\n- **Timing-safe token comparison.** The server bearer check now uses `hmac.compare_digest`\n (the token is reachable over the public tunnel; a byte-wise `!=` leaked it to timing attacks).\n- **MCP add is SSRF-guarded + confirmed.** Adding an external MCP server is a two-step\n prepare→confirm flow; URLs resolving to loopback / private / link-local / metadata addresses\n are rejected (override with `AITHER_ALLOW_PRIVATE_MCP=1` for trusted local servers).\n- **Config editor is allowlisted.** `PATCH /admin/config` writes only safe scalar prefs and\n hard-denies fields that could execute code or redirect traffic at startup (e.g.\n `aither_toolpack_dirs`, backend base URLs).\n- **Log tail is scrubbed.** Bearer tokens, `#k=` fragments, and `sk-`/`aither_sk_` keys are\n redacted from the log-tail endpoint; the tunnel log is excluded.\n\n## [2.14.8] - 2026-07-04\n\n### Added — `adk up` for normal humans (no API key, no prereqs, live chat)\n\n- **Hosted-brain default — no API key required.** When there's no local model and no\n provider key, `adk up` signs in (device-flow) and routes inference through the gateway\n using the portal token (`AITHER_LLM_BACKEND=gateway` + bearer). A non-technical user\n never needs to obtain or paste an LLM API key. Explicit `--provider`/local model still\n take precedence; unattended `--yes` with no token/backend fails cleanly (exit 3).\n- **cloudflared auto-download.** If the tunnel binary is missing, `adk up` downloads the\n right platform asset to `~/.aither/bin` (SHA256-verified against the release; opt out with\n `AITHER_CLOUDFLARED_REQUIRE_CHECKSUM=0`) instead of exiting with an install hint.\n- **Built-in streaming chat page.** The agent now serves a self-contained chat UI at `/`\n (and `/chat`) with a live \"thinking…\" indicator and token-by-token streaming — so a human\n has somewhere to talk to it with immediate feedback instead of a blank wait. `adk up` opens\n it locally and passes the callback bearer in the URL fragment (`#k=…`), so `/chat/stream`\n stays authenticated (not an open proxy) with nothing to paste.\n- **Human-readable failures.** `adk up` errors now carry a plain-English `next_action`\n (kept machine-readable in `--yes` JSON).\n\n### Fixed\n\n- `adk up` chat link pointed at a non-existent `/settings/agent` route; it now points at the\n agent's own streaming chat page.\n\n## [2.14.7] - 2026-07-04\n\n### Added\n\n- **`adk up` — one command to run a persistent, fleet-connected agent.** Collapses\n the scattered onboarding surface (`host`/`quickstart`/`connect`/`login`/`setup`)\n into a single command that starts `aither-serve` (single agent, identity `aither`\n by default), opens a Cloudflare quick-tunnel for fleet reachability, registers with\n the portal, and installs a reboot-persistent autostart entry (Windows Task Scheduler\n / systemd-user / launchd). Detaches by default so the terminal is freed; `--foreground`\n blocks. Reuses the existing device-flow login, cloudflared discovery, provider-key\n store, and `_preflight_check` backend detection.\n- **Fully non-interactive path for autonomous agents.** `adk up --yes` (or any\n non-TTY invocation) takes ZERO prompts — everything resolves from flags, env, and\n saved config — and emits a single machine-readable JSON status line with meaningful\n exit codes (`0` ok, `2` bad-input, `3` no-backend, `4` tunnel-missing, `5` register-failed).\n Degrades to local-only (warn, not fail) when a portal token is absent unless\n `--require-register` is set.\n- **`adk down` / `adk status --json` companions.** `down` stops the agent + tunnel,\n best-effort deregisters, and removes the autostart entry; `status` reports the running\n agent (liveness + `/health` + tunnel + registration), with `--json` for agents/CI.\n- **`adk stack`** — the previous `adk up` behaviour (supervise the Room + Ollama consumer\n stack) now lives here; `adk up` is the connected-agent command.\n\n### Internal\n\n- New `adk/agent_daemon.py`: detached process spawn, status file\n (`~/.aither/adk-up.json`), pid liveness/teardown, and cross-platform autostart install.\n\n## [2.14.6] - 2026-07-04\n\n### Added\n\n- **`adk.preflight` — self-bootstrap capability probe.** `run_preflight(agent,\n spec)` wakes an agent up knowing what it is actually plugged into: a bounded\n (1.5s) parallel `LivenessProbe` over the router's resolved backends\n (primary/reasoning), the embeddings winner, remote MCP tools, the structured-ML\n / `/ml/teach` endpoints, and voice — returning an honest `CapabilityReport`.\n Status vocabulary distinguishes reachability from entitlement\n (`OK`/`MISSING`/`UNREACHABLE`/`AUTH`/`TIER_DENIED`/`UNSUPPORTED`), a hosted slot\n never prints a bare `OK`, and a required-but-unsatisfied slot aborts before the\n loop instead of blind-firing. Headless task/rules resolution + an allowed-roots\n policy binding (`builtin_tools.set_allowed_roots`, which resets the memoized\n cache so the override actually enforces).\n- **Multimodal (image) messages.** `Message.content` now accepts an OpenAI-style\n content-part list (`str | list[dict]`), passed through verbatim to the\n OpenAI-compatible body (gateway / vLLM / OpenAI). New `adk.llm.multimodal`\n helpers — `image_message()`, `image_content_parts()`, `to_image_url()` — build\n the parts and encode local image bytes/paths into `data:` URIs.\n- **Real vision preflight probe.** The vision slot is no longer unconditionally\n `UNSUPPORTED`: it sends a discriminable image (a solid red square) and requires\n the model to name the color, so a blind/text-only model cannot false-pass. `OK`\n only on a real round-trip; `AUTH` on 401/403; `UNSUPPORTED` when there is no\n OpenAI-compatible provider or the model cannot see the image.\n\n## [2.14.5] - 2026-07-04\n\n### Added\n\n- **`adk.reasoning.mcts` — generic Monte-Carlo Tree Search library.** A reusable\n MCTS engine (ported from the internal `UnifiedMCTS`, with all internal couplings\n removed) that any agent can drive over its own environment:\n - `MCTSEnvironment` protocol (`get_state_hash` / `get_actions` / `step` /\n `evaluate` / `clone`) — implement it and search.\n - Three **optional, default-off** model seams — `TransitionModel`, `PolicyModel`\n (PUCT priors), `ValueModel` — so a learned world-model / policy / value can be\n injected without touching the core; with all three `None` the engine is a plain\n UCT search.\n - `ObservedTransitionModel` adapter — a cheap online lookup world-model (hit =\n exact stored transition; miss = identity + negative \"unknown\" reward +\n `is_uncertain`, biasing exploration toward learning), with JSONL save/load for\n cross-run reuse.\n - `UnifiedMCTS(config).search(env)` returns an `MCTSResult` (best action, value,\n confidence, principal-variation path) and an optional `MCTSTrace`\n (visit-distribution policy target) for distillation.\n- **Self-bootstrapping agent machinery.** Build an agent from a declarative spec\n instead of bespoke wiring:\n - `adk.bootstrap.build_agent_from_spec(path) -> (AitherAgent, RunCtx)` — resolves\n prompts, activates required/optional tool packs, and carries a `memory_map`\n (a `node_type -> (role, tier)` override for memory writes, never a gate).\n - `adk.prompts.PromptBridge` — resolves an agent's prompt bindings from files\n (relative to the spec) or `AitherPrompts` dot-keys (optional import).\n - `adk.packs.PackActivator` — eager activation of installed tool packs by name\n over `register_tool_packs`; a required pack that registers zero tools raises\n `PackUnavailable`, optional packs soft-degrade.\n - `python -m adk.bootstrap.generic_agent <spec.yaml>` inspects a spec (resolved\n name, activated packs + tool counts, prompt keys, memory map) without running a\n live loop.\n- **`deep_research` tool pack** — a full multi-engine web-research workflow as\n `dr_*` agent tools (keyless search, page fetch + cache, site mirror, a citable\n findings knowledge graph, and markdown/PDF/DOCX report generation), activatable\n by name via the tool-pack loader.\n\n## [2.14.4] - 2026-07-04\n\n### Added\n\n- **`structured_ml` tool domain** — zero-shot structured-data inference for agents,\n backed by TabFM (tabular classification/regression) and TimesFM (time-series\n forecasting). Three tools:\n - `tabular_classify(support_rows, target, query_rows)` — classify rows from a\n labeled support set, in-context (no training step; up to 10 classes).\n - `tabular_regress(support_rows, target, query_rows)` — predict a numeric target.\n - `timeseries_forecast(series, horizon)` — forecast future values of a series.\n\n The tools POST to a structured-ML inference service resolved from\n `AITHER_STRUCTURED_ML_URL` (default `http://localhost:8192`), reject non-http(s)\n URLs, and summarise oversized responses to protect the agent's context.\n- **`Capability.STRUCTURED_INFERENCE`** capability token and a new **`analyst`**\n identity/pack that ships the `structured_ml` domain by default. The domain is\n opt-in — only identities that enable the `structured_ml` category get the tools.\n\n## [2.14.3] - 2026-07-03\n\n### Fixed\n\nAll four bugs below were found by actually booting `adk run` against a real\nchat model and driving a real tool-calling request end-to-end (not inferred\nfrom reading the code) — each one silently broke the \"local agent uses its\nconfigured model and its tools actually work\" path.\n\n- **`AITHER_MODEL` / `config.model` silently ignored by the OpenAI-compatible\n backend**: `LLMRouter.__init__` never read `config.model` into `self._model`\n unless the caller passed `model=` directly to the constructor — which nothing\n did. Every explicit model choice (env var, config file) was dropped, the\n provider fell back to a hardcoded per-backend default (`gpt-4o-mini` for the\n `openai` backend), and an unrecognized model name got silently remapped\n server-side to the wrong backing model. Concretely: setting\n `AITHER_MODEL=aither-orchestrator` had no effect — every request actually\n went to a different model that was never meant to be called directly and\n can't reliably use tools.\n- **`OPENAI_BASE_URL` ignored for explicit `--backend`/`AITHER_LLM_BACKEND`\n selection**: `LLMRouter._auto_detect()`'s explicit-backend branch hardcoded\n `base_url=None` when constructing the provider, instead of reading\n `config.openai_base_url` (which two other call sites in the same file\n already did correctly). Any self-hosted OpenAI-compatible endpoint\n (MicroScheduler, vLLM, LM Studio) configured via `OPENAI_BASE_URL` was\n silently ignored in favor of `https://api.openai.com/v1`.\n- **Tool-call steering never engaged on default-effort chat**: the retry that\n nudges a model back into structured tool-calling when it responds with prose\n instead of a function call was gated behind `effort >= 6`. Default `effort`\n for a plain `agent.chat(message)` call resolves to `5`, so the safety net —\n built specifically for this failure mode — never fired for default requests.\n Also now forces `tool_choice=\"required\"` on the retry instead of relying on a\n text nudge alone.\n- **Every non-string builtin-tool parameter silently typed as `\"string\"` in\n the generated tool schema**: `builtin_tools.py` uses\n `from __future__ import annotations`, so `_extract_parameters()` in\n `tools.py` was reading postponed (string) type hints (`\"int\"`, not `int`)\n straight out of `fn.__annotations__`, which never matched\n `_type_to_schema`'s type-object keys and silently fell through to\n `{\"type\": \"string\"}`. Every builtin tool with an `int`/`float`/`bool`/`list`\n parameter was affected — e.g. `file_read(start_line: int, end_line: int)`\n advertised both as strings, so a model correctly calling the tool with\n `\"start_line\": \"0\"` crashed the actual implementation with\n `unsupported operand type(s) for -: 'str' and 'int'`. Fixed by resolving\n hints through `typing.get_type_hints()` instead of the raw annotations dict.\n `_coerce_arguments()` — the runtime safety net that's supposed to coerce a\n model-supplied `\"0\"` string into `0` before a tool call executes — had the\n exact same raw-`__annotations__` bug and silently no-opped for every builtin\n tool; fixed the same way.\n- **Streaming tool-loop could hang a connection forever**: `chat_stream()`'s\n fallback to the sync tool loop (tool calls can't stream token-by-token) had\n no timeout, so a model that didn't cleanly terminate a tool-calling round\n trip left the SSE connection open indefinitely with zero bytes sent. Bounded\n with a 60s `asyncio.wait_for`; on timeout the stream yields a clear message\n instead of hanging.\n\n## [2.14.2] - 2026-07-03\n\n### Fixed\n- **AitherShell session amnesia**: the REPL generated a fresh `session_id` for\n EVERY message, so conversation history never followed across turns (the\n server stored each turn under a different session). One stable id is now\n minted per shell run; `/resume` and `/new` semantics unchanged, and the\n cross-restart auto-restore of the last session now actually works.\n\n## [2.14.1] - 2026-07-02\n\n### Added\n- **Account-tied licensing via device-flow login** — `adk login` now persists the account\n license returned by the device-flow handshake to `~/.aither/license.json` (base64\n `license_key` → `{payload, signature}` envelope), so entitlements from the authenticated\n account carry into every later `adk` invocation without re-auth. Pairs with the fleet-side\n node tool-pack gate that grants packs by plan-tier rank.\n- **`can_use_formbridge` / `can_use_untether` entitlements** (PROFESSIONAL+ rank ≥ 3) backing\n the FormBridge and UNTETHER agent tool-packs.\n\n### Fixed\n- `adk` CLI: guard the optional `memory_graph` singleton (was a latent `NameError` on the\n `/stats`, `/memory`, and save paths when memory was not constructed).\n\n## [2.14.0] - 2026-07-02\n\n### Added\n- **`adk.memory_wiki` — LyraWiki-style self-managed semantic knowledge** (opt-in; nothing constructs it by default): `MemoryWiki(graph_memory)` maintains curated wiki ARTICLES as `wiki_article` nodes (title/slug, LLM-owned markdown with `[[wikilinks]]`, embedding, durable tier, `CONSOLIDATED_FROM` edges to source memories, governance-ledgered revisions).\n - `consolidate(llm, since=, budget=)` clusters unconsolidated episodic/fact nodes (embedding + keyword), drafts/UPDATEs articles via the INJECTED llm callable (`str -> str` or `messages -> str`, sync or async — no hard model dependency), cites source node ids, marks contradicted sources superseded, then DEMOTES consolidated sources to a fast-decay tier via `promote()` (represented knowledge doesn't need raw copies).\n - `lint(llm=None)` — deterministic health checks (orphan wikilinks, empty/stale articles, live-but-contradicted pairs) + an optional LLM audit pass.\n - `prune(relevance_floor=0.05, hard_delete_after_days=14)` — article relevance = tier freshness × reinforcement (reinforce-on-recall) + link-degree to LIVE memories; below the floor → reversible governance tombstone; entombed past the window → **HARD-DELETE** (the tombstone snapshot itself is purged — content and embedding irrecoverable; the true-deletion path). A node is never deleted without a tombstone first.\n - `recall(query, limit)` — article-first RAG (curated articles rank above raw nodes) that reinforces returned articles.\n- **`adk.routines` — agent heartbeat + self-programmed routines**: `RoutineStore` is a durable registry (`~/.aither/routines/{agent}.json`, `AITHER_DATA_DIR`-aware, injectable path) of cron-scheduled self-prompts `{name, cron, instruction, enabled, last_run, last_result, tags}` that rehydrates into the existing `adk.cron.CronScheduler` on `start()`. A fire runs `agent.chat(instruction)` with the agent's full toolset — the agent programs its future self with its own adk. Guardrails: `max_routines` (12), a 5-minute min fire interval per routine, a per-fire timeout, and every result ledgered (truncated) to `last_result`. Direct-callable routines (`register_direct`) fire a bound method instead of a self-prompt. Self-management tools (`routine_create/list/update/pause/resume/delete/run_now`) ship as OpenAI tool defs + handlers (`build_routine_tools` / `register_routine_tools` / `routine_tool_defs`); the handlers ONLY touch the RoutineStore — they can never modify agent config or safety-relevant settings (the leash principle).\n- **`AitherAgent(routines=True, memory_maintenance=True)`** (both default **False** → the default agent is byte-identical): `routines=True` builds the RoutineStore + scheduler, registers the self-management tools, and starts the heartbeat lazily on the first `chat()` (or via explicit `start_routines()`). `memory_maintenance=True` registers DEFAULT routines — `wiki_consolidate` (every 2 h), `wiki_lint` / `wiki_prune` / `graph_sweep` (daily) — as DIRECT method fires so memory upkeep runs even on tiny models, while staying visible/manageable via `routine_list`.\n- `TombstoneStore.purge(tombstone_id)` — the hard-delete endpoint: irrecoverably removes a tombstone snapshot and rewrites the persisted store without it (consumed by `memory_wiki.prune`).\n\n## [2.13.6] - 2026-07-02\n\n### Added\n- **Qdrant backend for GraphMemory dataplane sync — TRUE two-way, verified live.** Set `AITHER_FLEET_QDRANT_URL` and the sync uses Qdrant (`upsert` + `scroll` + filtered `search`) instead of Nexus. This is what actually delivers cross-agent memory sharing: a node is upserted with its OWN embedding under a deterministic per-(tenant,node) point id (idempotent re-push), and `fleet_pull` reliably SCROLLS a tenant's points back — the enumerate Nexus lacks (its `/search` doesn't return ingested docs and `/export` needs lancedb, which has a hard dependency conflict in the fleet). **Proven live with 2 agents:** agent-1 ingests → auto-syncs to the tenant dataplane; a fresh agent-2 in the same tenant `fleet_pull`s all of agent-1's nodes and can search them; a different tenant pulls nothing (isolation). Dimension-safe (never mixes vector dims in a collection). Local SQLite stays source of truth; best-effort, never raises.\n\n## [2.13.5] - 2026-07-02\n\n### Fixed\n- **CRITICAL — GraphMemory dataplane sync push was 100% failing.** `_fleet_push_node` sent `source_type=\"graph\"` / `content_type=\"graph_node\"`, which AitherNexus rejects as invalid enums (HTTP 500) — so NO graph node ever replicated in 2.13.4. Now sends the accepted `source_type=\"manual\"` / `content_type=\"text\"` (graph provenance is carried in `metadata.synced_from`). **Verified live** against a fleet Nexus: a 4-node ingest replicates with `pending==0` and the collection count reflects it.\n\n### Added\n- **Swarm-awareness notification** — on a successful sync batch, `GraphMemory` emits a best-effort `graph.synced` event (tenant/workspace/agent/collection/count) to `AITHER_FLEET_EVENTS_URL` so OTHER agents in the tenant/swarm can pull the fresh data and deconflict in-flight work (push-based awareness instead of polling). No-op unless the env is set.\n\n### Known limitation\n- Cross-agent **rehydration** (`fleet_pull`) is only reliable when the fleet Nexus is backed by lancedb (persistent + exportable). An in-memory Nexus stores on `/ingest` but neither its `/search` nor `/export` returns the docs — two-way read needs the persistent Nexus.\n\n## [2.13.4] - 2026-07-02\n\n### Added\n- **Memory-maintenance primitives on `GraphMemory`** (all opt-in; default behaviour byte-identical):\n - **Reinforce-on-recall**: `recall_with_activation(..., reinforce=True)` bumps `reinforcement_count`/`last_reinforced` on every RETURNED node's metadata mirror (one UPDATE per node, WAL-safe) — making the `MemoryRecord` reinforcement fields the activation scorer reads LIVE. Default `False` = zero writes.\n - **`sweep(now, archive_below=0.05, max_nodes=None, dry_run=False)`**: read-time decay enforced at rest — archives nodes past their tier TTL with freshness×reinforcement score below `archive_below` and `reinforcement_count <= 1`; `max_nodes` additionally archives the lowest-scored overflow. PERMANENT tier immune. Every archive is a reversible governance tombstone (`TombstoneStore.recover`) + a FORGET ledger entry; a node is never deleted without a tombstone. `dry_run` returns the would-archive list without acting.\n - **`promote(node_id, tier=?, role=?)`**: re-tier/re-role a node IN PLACE (tier/role columns + metadata mirror) — edges, embedding and ledger history survive; ledgers an UPDATE entry when governance is on.\n- `MutationType.UPDATE` ledger entry kind (in-place re-tier/re-role).\n- **Per-tenant dataplane sync for `GraphMemory`** — replicate the local graph to a per-tenant vector dataplane (Nexus/Qdrant) so a sovereign agent's memory is durable + rehydratable across restarts and hosting boundaries. `fleet_push_all_nodes()` (catch-up), `fleet_pull()` (best-effort semantic top-up), `fleet_sync_pending()`, and auto-sync on ingest (`_maybe_autosync` + `drain_sync()`). **On by default** (`AITHER_FLEET_SYNC=auto`; only `false/0/off` disables) with an inferred target (local Nexus `:8122` or the gateway in cloud mode) and a SEPARATE collection (`AITHER_FLEET_GRAPH_COLLECTION`, default `graph_memory`) from the KV memory sync. New `GraphMemory(tenant_id=, workspace_id=, fleet_url=, auto_sync=)` constructor args override env (required for multi-tenant host processes). Node `synced` column + schema v4 migration. TLS verify is CA-bundle-aware (`AITHER_CA_BUNDLE`) for the internal-TLS mesh. Local SQLite stays the source of truth; every sync path is best-effort and never raises into ingest.\n\n## [2.13.3] - 2026-07-02\n\n### Added\n- **Canonical self-deploying embeddings provider** (`adk.embeddings`): one embedding provider for the whole SDK so vectors are portable across scopes (platform / tenant / sovereign) — same model + dimension everywhere. Pinned to `nomic-embed-text` (768-d; the vLLM `--served-model-name` for nomic-embed-text-v1.5). Lazy, single-flight resolution chain: explicit `AITHER_EMBEDDINGS_URL` → local vLLM (`:8209` then `:8120`, HTTPS-then-HTTP) → local Ollama → gateway (`AITHER_GATEWAY_EMBEDDINGS_URL`) → auto-deploy a local vLLM embeddings container if a GPU + Docker are present (opt out with `AITHER_EMBED_AUTODEPLOY=0`) → CPU sentence-transformers (384-d, degraded) → feature-hash (384-d, degraded, always works). Every batch is dimension-tagged so callers never silently mix 768-d and 384-d vectors. `get_default_embedder()`, `embed_texts()`, `embed_one()`, `get_provider()`, `reset_provider()`.\n\n### Changed\n- **`GraphMemory` now defaults its embedder to the canonical provider** (opt out with `AITHER_GRAPH_EMBEDDER=legacy`), so every adk agent shares one 768-d space. Added a meta-table dimension guard: the index is pinned to its first embedding's dimension and later different-dimension vectors are refused (kills 768↔384 mixing that silently poisons cosine similarity).\n\n## [2.13.2] - 2026-07-01\n\n### Added\n- **Session persistence**: AitherShell now auto-saves the active session_id to `~/.aither/config.yaml` after each chat turn and auto-restores it on the next shell launch. Multi-turn conversations survive across restarts — the messages were always persisted in SQLite + JSON, only the session pointer was missing.\n- `/new` command to start a fresh session (clears saved session_id).\n- `/sessions` command to list recent sessions with agent name, message count, and timestamp. Resume any with `/resume <id>`.\n\n## [2.13.1] - 2026-07-01\n\n### Fixed\n- **CRITICAL:** `deploy_connect()` now sends a HEAD request to verify the GitHub release asset exists before downloading, with a clear error if the asset is missing.\n- **CRITICAL:** Compose file URLs are now pinned to a stable commit hash (configurable via `AITHER_COMPOSE_PIN` env) instead of tracking the mutable `main` branch.\n- **CRITICAL:** `deploy_adk_node()` now falls back to starting `adk-serve` natively when Docker is unavailable, instead of blocking with \"Docker not installed\".\n- **HIGH:** GHCR login failure now blocks deployment immediately with actionable guidance, instead of warning and continuing to a confusing image-pull failure.\n- **HIGH:** Health checks now stream the last 30 lines of Docker Compose logs on failure, so the user can see why a container didn't start.\n- **HIGH:** vLLM port allocation now probes for conflicts before assigning; if port 8200 is busy, workers are automatically remapped to the next free port.\n- **MEDIUM:** Compose file downloads are now cached with ETag; re-running `adk deploy node` skips re-downloading an unchanged file.\n- **MEDIUM:** DGX Spark discovery now respects `AITHER_DGX_HOSTS` env (comma-separated) for custom network topologies, instead of only checking `spark.local` and `192.168.0.33`.\n- **MEDIUM:** Cloud API keys (Anthropic, OpenAI, DeepSeek) are now validated with a lightweight probe during `adk setup` infra scan, so invalid keys surface early.\n- **MEDIUM:** `deploy_connect()` now validates the extracted extension has a `manifest.json` and warns if it's missing.\n\n## [2.13.0] - 2026-07-01\n\n### Removed\n- **`adk.faculties.memory_graph`** — the pickle-based `MemoryGraph` module is deleted. `from adk.faculties import MemoryGraph` now resolves to the canonical `adk.graph_memory.GraphMemory` for back-compat.\n- **`adk.platform.memory`** — the entire legacy memory subsystem (`MemoryManager`, `UnifiedMemorySystem`, `GameEngine`, `StoryboardEngine`, `AnchorGenerator`) is deleted. Agent memory is `adk.memory.Memory` + `adk.graph_memory.GraphMemory`.\n\n### Changed\n- `adk work` command now uses `GraphMemory` instead of the removed pickle-based faculty graph.\n- `adk.MemoryGraph` and `adk.faculties.MemoryGraph` lazy exports now resolve to `adk.graph_memory.GraphMemory`.\n- `agent.set_memory_graph()` kept for back-compat but documented as deprecated; agents already wire `GraphMemory` automatically in `__init__`.\n- Platform startup (`adk.platform.infrastructure.startup`) gracefully handles missing `MemoryManager`.\n- Platform CLI module listing and health check updated to reflect removed memory module.\n\n## [2.12.7] - 2026-07-01\n\n### Deprecated\n- `adk.faculties.MemoryGraph` — emits `DeprecationWarning` on instantiation; use `adk.graph_memory.GraphMemory` (SQLite-backed, governed, embedding-native) instead.\n- `adk.platform.memory` package — the legacy `MemoryManager`, `UnifiedMemorySystem`, `GameEngine`, and `StoryboardEngine` modules now emit `DeprecationWarning` on import; use `adk.memory.Memory` and `adk.graph_memory.GraphMemory` for new code.\n\n### Changed\n- Updated `adk.faculties` docstring and usage examples to point at the canonical memory surface.\n\n## [2.12.6] - 2026-07-01\n\n### Changed\n- Trimmed public package debt by removing grid runbook artifacts and shell/room binary placeholder directories from published artifacts.\n- Removed stale public-sync allowlist entries for deleted phase test files and added explicit public-sync guards for binary placeholder directories and grid artifacts.\n- Updated the public README grid note so it no longer links to runbook files excluded from the SDK package.\n\n## [2.12.5] - 2026-07-01\n\n### Added\n- Added best-effort fleet memory sync from local ADK memory into Nexus/Qdrant-compatible RAG stores, including batch catch-up and vector-search recall hooks.\n- Added saved config/env support for fleet memory URL, collection, and sync mode.\n\n### Changed\n- Excluded FormBridge runtime/test internals from public ADK packaging and public repo sync.\n\n## [2.12.4] - 2026-07-01\n\n### Added\n- Added a hot-reload endpoint for installed agent packs so newly applied pack tools can be picked up without restarting `aither-serve`.\n\n### Fixed\n- Fixed synchronous `ChatRelay` calls on Python 3.12 when no event loop is running; async WebSocket delivery is still scheduled when a loop exists.\n- Aligned gateway inference tier tests with the current public ladder: free, starter, pro, enterprise, platform.\n\n## [2.12.0] - 2026-06-28\n\n### Added — one-command local inference + upgrade path (click-to-run)\n- **`adk quickstart-local`** — detects hardware, picks a backend\n (Ollama / llama.cpp / vLLM), installs it, downloads a model, verifies, and\n prints next steps. Bootstraps Ollama itself via winget/brew/install.sh when\n missing (no \"go install it\" dead-ends). Default model `gemma4:e2b`.\n- **`adk backend switch <ollama|llamacpp|vllm>`** (+ `backend status`) — migrate\n the active deployment between engines; re-points config and smoke-tests.\n- **`adk install pack:<name>`** (+ `adk install list` / `adk packs`) — install\n ready-made agent packs (openclaw, hermes, claude-code) into `~/.aither/agents`.\n\n### Fixed (found via live end-to-end testing on Windows + CUDA)\n- quickstart-local reported success on a dead endpoint (smoke test was warn-only);\n a failed smoke now fails the command, with a service-readiness wait.\n- `--model` resolved to `None` (getattr default never fired) → uses the real default.\n- Smoke test: 30s→180s timeout for cold model load; accepts reasoning-model output\n (empty `content` at low token budgets is no longer a false failure).\n- `adk install pack:<name>` colon syntax (argparse rejected it before dispatch).\n- `adk backend status` health probe used a nonexistent `/health` path → 404; now\n probes `/v1/models` (uniform OpenAI-compatible liveness for all three backends).\n- Windows: removed non-ASCII glyphs from printed strings that crashed the cp1252\n console (a `✓` even made a successful install report failure).\n\n## [2.11.1] - 2026-06-14\n\n### Fixed — `adk host` / `adk login` device flow (autonomous onboarding)\nFound driving `adk host` against production; all broke real device-flow login:\n- **Device flow hit the portal FRONTEND, not AitherIdentity.** `adk login`/`adk host`\n POSTed `/auth/device/code` to portal/veil.aitherium.com, which 307-redirects API auth\n to `/login`. Added `_resolve_identity_url()` mapping the aitherium.com topology to\n `idp.aitherium.com` (the Identity API); localhost/bespoke pass through unchanged.\n- **Cloudflare 403'd urllib.** `idp.*` sits behind Cloudflare, which blocks urllib's\n default `Python-urllib/3.x` UA. Device-code + poll requests now send a real\n User-Agent + Accept (verified urllib→idp 200, was 403).\n- **`adk host` now self-authenticates via device flow** when there's no token, and\n **retries via device flow on a 401/403** (a stale saved token no longer dead-ends\n registration).\n\n## [2.11.0] - 2026-06-14\n\n### Added — attach your own MCP \"hands\" to a self-hosted agent\nA self-hosted agent now has the full brain (your model) · body (this loop) · **hands**\n(your tools) trifecta. The AitherOS platform relays the MCP servers you registered for\nyour agent in the `/stream` request body as `mcp_endpoints`, and the loop wires their\ntools into the ReAct turn:\n- **`adk/mcp_endpoint_tools.register_mcp_endpoint_tools(agent, endpoints)`** — speaks MCP\n JSON-RPC (`tools/list` to discover, `tools/call` to invoke) and registers a namespaced\n proxy tool (`{endpoint}__{tool}`) for each advertised tool. Mirrors `app_proxy_tools`.\n Best-effort: an unreachable endpoint registers nothing and never raises.\n- **`POST /stream` and `POST /chat/stream`** accept an optional `mcp_endpoints` list\n (`[{name, url, [headers]}]`); `_aitheros_stream` attaches them to the agent before the\n turn. Existing callers are unaffected (the field is optional).\n\n## [2.10.1] - 2026-06-14\n\n### Fixed — self-hosted `aither-serve` was crash-on-startup (2.10.0 regression)\nA fresh `pip install aither-adk && aither-serve --backend deepseek` could not boot. Found\nand fixed while standing up a real self-hosted DeepSeek agent end-to-end:\n- **Startup `NameError: name 'port' is not defined`** in three lifespan helpers\n (`_join_aithernet`, `_init_a2a_server`, `_start_mesh_hosting`) — they referenced a bare\n `port` no longer in scope. Now use `config.server_port`, and `main()` writes the resolved\n `--port`/`--host` back to `config` so every lifespan helper and the invoke_url see the\n actual bound port.\n- **Fleet self-registration crashed the whole server** — the registration handlers caught\n `(ImportError, RuntimeError, OSError, ConnectionError)` but **not** `httpx.HTTPError`, so a\n `RemoteProtocolError`/connect error talking to the control plane took the agent down. All\n network-facing lifespan handlers now also catch `httpx.HTTPError` (non-fatal, as intended).\n- **`--backend` was ignored when `AITHER_API_KEY` was set** — `_auto_detect` picked the cloud\n gateway before the explicit backend, and `_try_elysium_fallback` then replaced the chosen\n provider. An explicit backend (deepseek/openai/anthropic/your own vllm/ollama) now always\n wins; \"auto\"/\"gateway\" still flow through detection. The operator's own brain is never\n silently hijacked.\n- **Approved-tool trace truncated after `/sessions/{id}/confirm`** — the SSE relay treated\n `AgentResponse.tool_calls_made` (a `list[str]`) as dicts (`tc.get(...)`), raised\n `AttributeError`, and an over-narrow `except` swallowed it so the client saw only\n `session_start` + heartbeats. The relay now handles string tool names, the `except` is\n broad (always emits a typed `error` + terminal `complete`), and the module-level\n `_strata_ingest` `NameError` + Chronicle `session_id` kwarg mismatch are fixed.\n\n## [2.10.0] - 2026-06-13\n\n### Added — human-in-the-loop tool approval (`adk/approval.py`)\n- **Pause before a gated tool, resume on decision.** An agent can now pause its turn before\n executing a sensitive tool and wait for a human's allow/deny — so a managed control plane\n (or any operator) can gate tool use without forking the loop.\n- **Policy via `AITHER_TOOL_APPROVAL`** — a comma-separated list of tool names (optionally\n `agent:tool`), or `*` for every tool. Empty = no gating (unchanged behavior).\n- **Survives restarts / days-later approvals.** The paused turn (user message + pending tool\n calls) is persisted to disk keyed by `session_id`; resume is a fresh request, not a held\n connection. Decisions are recorded per `(session_id, tool_name)`.\n- **`AgentResponse.requires_action` / `.pending`** — set when a turn paused; `pending` lists\n the gated tool calls awaiting a decision.\n- **`AitherAgent.resume(session_id, decisions)`** — records the decisions and re-runs the\n paused turn (the gate consumes them: allowed tools execute, denied tools get a denial\n observation, the turn continues — and may pause again on a different gated tool).\n- **Server:** `POST /sessions/{id}/confirm` records decisions and streams the resumed turn;\n `/stream` emits a `requires_action` event when a turn pauses.\n\n## [2.9.0] - 2026-06-13\n\n### Added — universal LLM continuation primitive (`adk/llm/continuation.py`)\n- **`run_continuation()` + `stitch()`** — one shared continue-until-complete primitive. When a\n completion stops because it hit the output token cap (`finish_reason == \"length\"`), it continues\n the generation and STITCHES the chunks (overlap-dedup + restart-detect) into a complete answer,\n instead of every call-site hand-rolling its own retry. Bounded three ways (max rounds AND total\n output ceiling AND a no-growth break), and it never mutates the caller's message history.\n- **`LLMProvider.chat_with_continuation()`** and **`LLMRouter.chat_with_continuation()`** — every\n provider AND the router inherit the primitive, so any call-site gets continuation for free.\n- Kill-switch `ADK_LLM_CONTINUATION=off`; tunables `ADK_LLM_MAX_CONTINUATIONS`,\n `ADK_LLM_MAX_TOTAL_OUTPUT_TOKENS` (defaults: 2 rounds / 8192 tokens).\n\n### Changed\n- The ReAct loop's truncation recovery now uses the shared primitive. The previous inline\n doubling-retry REPLACED the partial response (losing earlier text in `resp.content`) and injected\n continuation scaffolding into the live message history; it now STITCHES and leaves history clean.\n Tool-call turns are never continued as text.\n\n### Included — previously-committed-but-unreleased changes\n- 2.8.2: TLS verification on by default (`verify=False` removed).\n- 2.8.1: baked license + pack-signing public keys.\n- 2.8.0: version-bump release.\n\n## [2.7.0] - 2026-06-05\n\n### Added — provider-agnostic LLM cost levers (at the `LLMProvider` ABC)\n- **Prompt caching.** New `ProviderCapabilities` (`prompt_cache: explicit|automatic|none`, `batch`) and\n normalized `LLMResponse.cache_read_tokens`/`cache_write_tokens`, so the \"tokens saved\" meter never\n depends on which backend served the turn. Anthropic inserts `cache_control` breakpoints over the stable\n system+tools prefix; OpenAI/DeepSeek caching is automatic and their `cached_tokens` are now surfaced\n (previously discarded). Default-on in `AitherAgent.chat`; override with `cache=False`.\n- **ResponseCache** (`adk/llm/cache.py`) — exact-match response cache with a pluggable backend, wired into\n `LLMRouter` as an opt-in (`LLMRouter(response_cache=…)` + per-call `cacheable=True`). OFF by default — no\n determinism change unless opted in.\n- **Gemini provider** (`adk/llm/gemini.py`) with explicit `cachedContents` caching; registered in the router.\n- **Async batch runner** (`adk/llm/batch_runner.py`) — `run_batch()` over the Anthropic Message Batches /\n OpenAI Batch APIs (~50% off) with a transparent fallback to concurrent `chat()` where unsupported.\n- **Graph-RAG retriever interface** (`adk/graph_retriever.py`).\n\n### Added — principal authority (prompt-injection defense enforced outside the model)\n- `adk/auth.py`: `Principal` / `AuthContext` / `PrincipalResolver` (+ deny-all default).\n- `ToolRegistry.execute(name, args, auth=…)` — a default-deny authorization gate at the tool boundary, so a\n forbidden tool call is blocked regardless of what the LLM emits. `ToolDef` gains `required_clearance` /\n `action_class`. Threaded through `AitherAgent.chat`; `auth=None` is fully backward-compatible.\n- `adk/channel_auth.py`: Telegram webhook-secret + CEO-id checks and HMAC-signed approvals.\n\n## [2.6.4] - 2026-06-05\n\n### Fixes\n- **Loop-guard nudges corrupted the message history → DeepSeek 400 mid-run.**\n When the ReAct loop emitted multiple tool calls in one assistant turn and the\n loop guard fired a WARN/BLOCK/CIRCUIT_BREAK on one of them, it spliced a\n standalone `system` nudge message *between* the tool results. DeepSeek (and\n strict vLLM chat templates) require an assistant `tool_calls` message to be\n followed by exactly one `tool` result per `tool_call_id`, contiguously — so the\n interleaved message produced `400 … insufficient tool messages following\n tool_calls message` and aborted the turn (this is reliably hit after crossing\n the 15-call circuit-break threshold, where every subsequent call WARNs).\n Per-call guidance is now folded into that call's own `tool` result, and any\n standalone steering is deferred to a single message AFTER the whole batch —\n preserving the `tool_calls`↔`tool_results` pairing every backend requires.\n Regression test: `deep-research-agent/tests/test_msg_structure.py`\n (loopguard-flood scenario).\n\n## [2.6.3] - 2026-06-04\n\n### Fixes\n- **Transient provider errors killed a run on the first blip.** A single\n `502 Bad Gateway` / `503` / `529 overloaded` / `429` from the upstream API\n aborted the whole request — the Anthropic provider had **no retry at all**, and\n the OpenAI-compatible provider only retried `429`. Both now retry transient\n statuses (`408/429/500/502/503/504/529`) with `Retry-After`-aware exponential\n backoff (3 attempts), and surface the provider's body if all retries are\n exhausted. Non-transient `4xx` (e.g. `400`) still fails fast. Applies to\n `chat` and `chat_stream` on both providers.\n\n## [2.6.2] - 2026-06-04\n\n### Fixes\n- **DeepSeek (and strict OpenAI-compatible backends) 400'd on long tool\n conversations.** The ReAct loop legitimately injects mid-conversation\n `system` steering messages (the \"[DIMINISHING RETURNS]\" hint after several\n low-output tool iterations, loop-guard nudges). OpenAI tolerates a non-leading\n `system` message; DeepSeek rejects it with a 400. Long research runs\n (many `fetch_url` calls) reliably tripped this. Fix: non-leading `system`\n messages are demoted to `user` at the OpenAI-compatible provider boundary\n (`_demote_nonleading_system`) — steering intent preserved, payload valid on\n every backend; leading system prompt(s) untouched.\n- **Opaque HTTP errors.** `resp.raise_for_status()` discarded the provider's\n response body, so a 400/422 surfaced as a bare `Client error '400 Bad\n Request'` with no cause. `_ensure_ok` now includes the provider's actual\n message (e.g. `context_length_exceeded`, `Invalid 'messages'`) in the raised\n error, for both `chat` and `chat_stream`.\n\n## [2.6.1] - 2026-06-04\n\n### Fixes\n- **`MCPServer.mount()` returned HTTP 422 for every `/mcp` call.** Under\n `from __future__ import annotations` the route handler's `request: Request`\n annotation is a string that FastAPI resolves against the *module* globals, but\n `Request` was imported locally inside `mount()` — so FastAPI couldn't resolve\n it and treated `request` as a required query param. The MCP server endpoint\n (`initialize`, `tools/list`, `tools/call`) was unusable when mounted. FastAPI\n symbols are now imported at module level (guarded; the `[node]` extra still\n gates actual use). Any agent's tools can now be exposed as a working MCP\n server with `MCPServer(...).mount(app)`.\n\n## [2.6.0] - 2026-06-04\n\n### Streaming + scaffolding\n- **`AitherAgent.stream_chat`** — reliable streaming sibling of `chat()`: native\n tool-calling loop (concludes on its own, unlike the text-protocol `stream_react`)\n that emits live `tool`/`tool_result` events and streams the answer as `token`\n events. `token_delay` paces tokens for a typing effect.\n- **`adk new <template>`** — scaffold a full, runnable template app. Ships the\n `deep-research` template (a cited web-research agent: pack + server + web UI):\n `adk new deep-research`.\n\n### Fixes\n- **BYO-key no longer community-capped.** The free-tier monthly token cap now applies\n only to the metered Aitherium gateway backend, never to a user's own provider key\n (Anthropic/OpenAI/DeepSeek/Ollama/vLLM). Previously it could brick a self-hosted\n agent after ~100k tokens on the user's own key.\n- **Stronger, grounded memory recall.** `chat()` now injects the top-6 knowledge-graph\n hits (untruncated) with a hard no-fabrication instruction — was top-3 truncated to\n 200 chars, which surfaced partial facts and let weaker models invent the rest.\n- **Tool-arg coercion.** `ToolRegistry.execute` coerces LLM-supplied args toward each\n parameter's annotated type, so a tool no longer crashes when a model passes a list\n or string where an int/float/bool was expected.\n\n### Docs\n- New `docs/AGENT_DEV_GUIDE.md` — the golden path for building agents/packs.\n\n## [2.5.0] - 2026-06-04\n\n### Continual learning — agents that learn from runs and reuse memory\n\nAgents now improve the second time they do a task instead of starting from scratch.\n\n### Added\n- **Recall-before**: `AitherAgent.chat()` searches the local `SkillStore` and injects the\n top matching learned skills as a system block, so the model reuses proven procedures.\n- **Learn-after**: `_learn_after()` runs after each response — reinforces (`success_count++`)\n any skills that were recalled+reused, and extracts + saves a NEW skill from successful\n multi-tool runs via `SkillExtractor`. Wired at both `chat()` return paths.\n- Gated by `AITHER_SKILLS` (default on). The previously-unused `SkillStore`/`SkillExtractor`\n are now connected to the run loop.\n\n### Notes\n- Pairs with the platform-side LearnedProcedure substrate (recipes → scored procedures →\n skills/tools/A2A/packs → MCTS priors → evolution). The SDK shares memory via the Spirit\n bridge when configured.\n\n## [2.0.0] - 2026-06-02\n\n### Open-core boundary (BREAKING)\n\nThis release draws a single, enforced free/paid line. The free tier stays\ngenuinely useful (real agent, typed + graph + code memory, ReAct, ~essential\ntools); paid tiers unlock the *scale* capabilities via portal.aitherium.com.\n\n### Added\n- **`adk/licensing.py`** — the entitlement keystone. Resolves a tier\n (COMMUNITY by default), verifies optional portal-signed `~/.aither/license.json`\n (Ed25519), and answers `can_use_fleet/channels/cron/swarm/auto_neurons`,\n `is_agent_licensed`, `max_effort`, etc. **Fail-closed**: no/invalid/expired\n license → free tier; an unsigned license never grants premium.\n `AITHER_TENANT_SLUG=aitherium` → unrestricted INTERNAL tier.\n- **Moat guard** (`scripts/check_moat_boundary.py`) wired into the publish\n workflow — blocks any wheel that leaks the moat or drops the keystone.\n- Tests: `tests/test_licensing.py`, `tests/test_moat_boundary.py`.\n\n### Changed (gating — fail-closed)\n- Free tier effort is capped at 3 (reasoning effort 7–10 needs Professional).\n- Monthly token budget hard-block on the free tier (no silent overspend).\n- Gated behind a paid tier: fleet mode (`FleetConfig`), cross-agent delegation\n (`AgentForge.delegate`), channel adapters, cron scheduler, proactive\n auto-neurons, and `swarm`/`swarm_code` dispatch.\n\n### Removed\n- **`adk/nanogpt.py`** (on-device training IP) removed from the published SDK\n and the public repo; relocated to the internal AitherOS moat. Excluded from\n the wheel as defense-in-depth.\n- Pre-2.0 releases shipped this IP and ungated capabilities and are being\n yanked (PyPI) / removed (GitHub releases). See `scripts/yank_leaky_releases.py`\n and `scripts/purge_public_leaks.sh`.\n\n## [1.20.0] - 2026-05-27\n\n### Fixed\n- **Pack discovery path mismatch** — `_find_local_packs()` now scans both\n `~/.aither/packs/` and `~/.aitheros/packs/`, so packs installed via\n `adk pack install` are discovered by `adk-workspace`.\n\n### Added\n- **Auto-register on pack install** — `adk pack install` now auto-registers\n with the portal if the pack contains an `agent.yaml` with a `portal` section.\n One command does download + extract + register.\n- **Flux event on agent registration** — `developer_portal.py` emits\n `agent.registered` Flux event so the portal fleet UI updates in real-time.\n\n## [1.15.0] - 2026-05-26\n\n### Added\n- **Cloud memory** — `adk quickstart --cloud` now tests gateway memory and\n auto-configures Spirit URL so memories persist across devices via\n `gateway.aitherium.com/v1/memory/teach` and `/v1/memory/recall`.\n- **Config→env export for cloud mode** — `Config.from_env()` now exports\n `AITHER_CLOUD_MODE`, `AITHER_SPIRIT_URL`, and spirit path env vars from\n saved config so Memory and other modules pick them up without needing\n explicit env vars set by the user.\n- **Cloud Quick Start section** in README — 3-command setup for users who\n just want a Claude/GPT-4/DeepSeek API key and the full agent harness.\n- **`--memory` profile for node deployment** — `adk deploy node --memory`\n adds Spirit (8087) + WorkingMemory (8101) containers for persistent\n vector memory. Without this, agents use local SQLite only.\n- **Configurable vLLM in node compose** — `AITHER_VLLM_MODEL`,\n `AITHER_VLLM_GPU_UTIL`, `AITHER_VLLM_CTX_LEN`, `AITHER_VLLM_QUANT_ARGS`\n env vars let `adk setup` write GPU-detected tier settings to `.env`\n and have the node compose pick them up (TQ4 for small GPUs, BNB for large).\n\n### Fixed\n- **Memory cloud routing** — `Memory._spirit_teach()` and `_spirit_recall()`\n now use configurable paths and auth headers for gateway proxy. Previously\n cloud-only users silently lost all Spirit memory calls.\n\n## [1.9.0] - 2026-05-24\n\n### Added\n- **Composable ToolPack system** — agents can load tool packs from\n `.toolpack.yaml` directories. Drop a pack in `~/.aitheros/packs/` or\n set `AITHER_PACKS_DIR`, and any agent discovers it automatically.\n- **`tools.packs` in agent.yaml** — list tool pack IDs to auto-load\n (e.g. `packs: [crm-tools, calendar-pro]`).\n- **`register_tool_packs(agent, pack_ids, packs_dir)`** in `builtin_tools` —\n programmatic pack loading for agents.\n- **`AITHER_TOOL_PACKS` env var** — comma-separated pack IDs loaded on\n agent init (alternative to YAML config).\n- **Unknown builtin categories resolve as pack IDs** — `tools.builtin:\n [file_io, crm-tools]` now works: `crm-tools` is looked up as a pack.\n\n## [1.8.0] - 2026-05-24\n\n### Removed\n- **11 dead modules** (3,023 lines) — `agent_deploy`, `cloud_deploy`, `create`,\n `phonehome`, `sdk_bridge`, `sovereign`, `telemetry`, `telemetry_config`,\n `error_reporter`, `anonymizer`, `package`. None were imported anywhere.\n- **29 ghost exports** from `__init__.py` — `secrets`, `otel`, `fs_sandbox`,\n and `vector_memory` modules were listed in `__all__` and `__getattr__` but\n the backing files never existed. Accessing them raised `ModuleNotFoundError`.\n- **Stale dist/ artifacts** — removed old wheels/tarballs (v0.4.0–v1.4.1, 11 MB).\n- **Orphan test scripts** — `test_phase1.py` and `test_phase23.py` (one-off\n validation scripts not part of the pytest suite).\n\n### Fixed\n- **Version mismatch** — `__init__.__version__` now matches `pyproject.toml`\n (was stuck at 1.6.0).\n\n## [1.5.0] - 2026-05-23\n\n### Added\n- **`--sovereign` flag for `adk deploy node`** — registers the node with the\n Aitherium hub via federation after deployment. Saves federation credentials\n to `~/.aither/.env.federation`. Accepts `--hub` (custom hub URL) and\n `--tenant` (tenant slug) options. Non-fatal: node runs standalone if\n registration fails.\n- **Federation → fleet cross-post** (Genesis) — sovereign nodes that register\n or heartbeat via `/federation/register` and `/federation/heartbeat` now\n automatically appear in the fleet dashboard (`/fleet/deployments`) with\n `node_type: \"sovereign\"` and live status/metrics.\n- **\"Deploy Sovereign Node\" section on Connect page** (AitherVeil) — Section 8\n with ADK deploy commands, fleet dashboard links, and provision page links.\n- **`deploy-node` action in tunnel proxy** (AitherVeil) — Connect page and\n external callers can now request bootstrap scripts via\n `POST /api/tunnel/devworkspace?action=deploy-node`.\n\n### Fixed\n- **Fleet provision page** — API URL corrected from `/api/fleet/provision` to\n `/api/bridge/genesis/fleet/provision` (matches bridge proxy pattern).\n- **Fleet detail page** — API URL corrected from `/api/fleet/deployments/` to\n `/api/bridge/genesis/fleet/deployments/` (same fix).\n- **Dockerfile** — entry point corrected from `aither-serve` to `adk-serve`\n (matches `pyproject.toml` script registration). Version label updated.\n\n## [1.4.1] - 2026-05-22\n\n### Fixed\n- **macOS executable builds** — dropped `--target-arch universal2` from\n `packaging/build_executable.py`. PyPI wheels for `pydantic_core` and other\n native deps are arch-specific, not fat binaries, so universal2 always failed\n with `IncompatibleBinaryArchError`. Now builds for the runner's native arch\n (`aither-macos-arm64`).\n- **Release workflow strategy** — added `fail-fast: false` so a single\n executable build failure no longer cancels the other OS jobs.\n- **PyPI publish** — added `skip-existing: true` so re-runs of the same\n version don't fail when the artifact already exists.\n- **GitHub Release job** — now runs whenever `pypi` succeeds even if some\n executable matrix entries fail, so the release page is always created with\n whatever binaries did build successfully.\n\n## [1.4.0] - 2026-05-22\n\n### Added\n- **Sovereign template now ships with `agent_core` 5-tier memory** — generated\n backends include the canonical `agent_core/` package (working → episodic →\n semantic → procedural → identity) plus `agent_memory.py` helper, vendored\n from `portal-kit-backend`.\n- `aiosqlite>=0.20.0` added to sovereign template `requirements.txt` for\n local agent_core SQLite storage.\n- Document tombstoning: `evict_cached_conversations_referencing` is wired\n through document-delete handlers in consumer apps so memories citing\n deleted documents are blocked from recall.\n- New tests: 13 smoke tests for `portal-kit-backend/agent_core` covering\n promotion thresholds, decay rates, store roundtrip, tombstone recall\n blocking, circuit breaker state machine, and chat-turn recording.\n\n### Changed\n- `scripts/vendor_agent_core.py` now syncs canonical `agent_core` to 4\n targets: WorkspaceRuntime, ADK sovereign template, and the GargBot +\n Chelle consumer mirrors (previously only 2).\n\n## [1.2.1] - 2026-05-21\n\n### Added\n- `adk train` command group — 6 subcommands: status, launch, logs, cancel, runs, register-gpu\n- `/slash-commands` endpoint — ADK server auto-exposes all 35 CLI commands as structured JSON manifest for AitherShell\n- `/cli/execute` endpoint — AitherShell can run any CLI command through the server\n- `build_command_manifest()` — introspects full argparse tree (commands, args, choices, subcommands)\n- `_register_commands()` extracted from main() for shared parser construction\n\n### Changed\n- `_get_genesis_url()` now probes: Genesis → ADK server → Aitherium cloud gateway (works without Genesis)\n\n## [1.2.0] - 2026-05-21\n\n### Added\n- **Runtime backend switching** — `agent.switch_backend(\"deepseek\")` and `agent.llm.switch_backend()` change provider without recreating agent\n- **Hybrid reasoning** — `agent.set_reasoning_backend(\"anthropic\")` routes effort 7+ to cloud API while keeping local orchestrator\n- **First-class DeepSeek provider** — `deepseek` in effort model table with `deepseek-chat` and `deepseek-reasoner`\n- **TQ4 quantization tiers** — `nano` (6GB, TurboQuant 4-bit), `standard-tq4` (12GB, both models TQ4), `hybrid-tq4` (6GB + cloud reasoning)\n- **`adk backend` CLI** — `list`, `set`, `set-reasoning`, `test` subcommands\n- **`adk quickstart`** — unified first-run wizard (setup + auth + shell)\n- **`adk tools`** — list local + MCP tools with tier markers\n- **`adk backup`** — tarball export of `~/.aither/`\n- **`adk ingest`** — feed docs/files into knowledge graph\n- **`adk setup nemotron`** shortcut — alias for `--tier lite`\n- **`--reasoning-api`** flag in setup — `anthropic`, `openai`, `deepseek`, `gateway`\n- **`--dgx-spark URL`** flag — remote vLLM endpoint configuration\n- **DGX Spark auto-detection** — scans `spark.local`, `192.168.0.33`, `AITHER_DGX_URL` env\n- **Post-setup smoke test** — sends real inference request after container startup\n- **Schema migration system** — version table + migration runner in `memory.py` and `graph_memory.py`\n- **Configurable vLLM port scan** — `AITHER_VLLM_PORTS` env var for custom ports\n- **`aithershell` entry point** — alias for `adk` in pyproject.toml\n- Shell auto-downloads binary on first `adk shell` (no `--install` needed)\n- Shell pre-flight check warns if no backend detected\n- Shell passes full config via env vars (API key, tenant, inference URL)\n- DGX/Remote and Cloud API checks added to `adk doctor`\n- Config fields: `reasoning_backend`, `reasoning_api_key`, `deepseek_api_key`, `dgx_url`, `vllm_extra_ports`\n\n### Fixed\n- `test_aeon_chat_basic` timeout — mock scope was exiting before request\n- `adk doctor` check_disk timeout — cap rglob at 5000 files\n\n## [1.1.7] - 2026-05-19\n\n### Added\n- Remote agent pipeline — `adk onboard` → fleet dispatch → remote inference\n- Agent-to-agent federation with mesh relay\n- `adk connect` for desktop mesh enrollment\n\n## [1.1.6] - 2026-05-19\n\n### Fixed\n- Tool call execution recovery across all backends\n- Hermes tool call fallback parsing for vLLM\n\n## [1.1.5] - 2026-05-19\n\n### Fixed\n- Tool call execution + Hermes fallback parsing improvements\n\n## [1.1.0] - 2026-05-18\n\n### Added\n- Multi-channel gateway (Telegram, Discord, Slack, webhook)\n- Aeon group chat with 7 presets\n- Skills auto-extraction from multi-step sessions\n- MCP stdio server for Claude Code integration\n- `adk gateway`, `adk cron`, `adk skills`, `adk soul` commands\n- SOUL.md import/export for portable agent identity\n- Elysium cloud device-flow authentication\n\n## [1.0.0] - 2026-05-18\n\n### Breaking\n- Package consolidated — `aither_adk` namespace replaced by `adk` + `adk.platform`\n- 93 duplicate files removed from `AitherOS/packages/aither_adk/`\n\n### Added\n- Standalone core primitives (`adk.core`: Agent, Tool, Memory, ModelBackend, Capability, Trace)\n- `adk.platform` sub-package merging all platform toolkit modules\n- Compatibility shims for legacy `from aither_adk.*` imports\n\n### Changed\n- Version synced across pyproject.toml, `adk/__init__.py`, `adk/platform/__init__.py`, npm package\n- Classifier promoted from Alpha to Beta\n\n## [0.16.0] - 2026-04-16\n\n### Swarm Coding Engine & Repowise Integration\n- **`repowise_search` tool** — Semantic + keyword hybrid code search via Repowise, with ripgrep fallback\n- **`swarm_code` tool** — Dispatch complex tasks to 11-agent swarm pipeline (ARCHITECT->SWARM->REVIEW->JUDGE)\n- **`agent.swarm()`** — Async convenience method with configurable mode, effort, timeout\n- **`agent.code_search()`** — Async convenience method returning structured results\n- New tool categories: `repowise` and `swarm` in TOOL_CATEGORIES\n- `repowise_search` and `swarm_code` added to `__init__.py` exports with lazy loading\n- `IDENTITY_DEFAULTS` updated: repowise for code-focused agents, swarm for orchestration agents\n- Standalone graceful degradation: repowise falls back to ripgrep, swarm returns structured error\n\n## [0.13.0] - 2026-04-02\n\n### Graph Faculties — Local Knowledge for Every Agent\n- **CodeGraph** (2,799 lines) — Full Python AST indexer with call graph, keyword/semantic/hybrid query, embedding matrix cache, incremental re-indexing, multi-hop chain expansion\n- **MemoryGraph** (1,339 lines) — Graph-based persistent agent memory with 10 edge types, hybrid query (keyword + semantic + graph expansion), multi-hop recall, pickle persistence\n- **EmbeddingProvider** — 4-backend fallback chain: sentence-transformers (GPU/CPU) -> Ollama -> Elysium cloud -> feature hashing (zero deps)\n- **BaseFacultyGraph** — Abstract base with HMAC-SHA256 validated pickle persistence\n\n### Agent Integration\n- `agent.set_code_graph(cg)` — auto-registers `code_search` + `code_context` tools\n- `agent.set_memory_graph(mg)` — auto-registers `remember` + `recall` + `memory_stats` tools\n- Both graphs inject context into LLM prompts automatically during chat\n\n### Zero-Config Onboarding\n- `adk start` / `adk` (no args) — auto-detect project, index code, detect LLM, persistent memory, interactive chat\n- Works for any directory: Python codebases, doc folders, mixed workspaces\n- Auto-detects LLM: Ollama -> vLLM -> Elysium -> OpenAI -> Anthropic\n- Per-project persistent memory in `~/.aither/memory/<project>`\n- `adk index <path>` — standalone indexing with progress bar and stats\n\n### MCP Gateway\n- `POST /v1/embeddings` — OpenAI-compatible embedding proxy to vLLM-embeddings:8209\n- Available to all tiers (embeddings are free)\n- ADK EmbeddingProvider uses this as Elysium cloud fallback\n\n### Optional Dependencies\n- `pip install aither-adk[graphs]` — numpy for 10x cosine similarity speedup\n- `pip install aither-adk[embedding]` — sentence-transformers + torch for local GPU embeddings\n- `pip install aither-adk` alone — graphs work with feature hashing (zero deps)\n\n## [0.12.0] - 2026-04-01\n\n### Bootstrap & Service Discovery\n- Version handshake -- ADK checks major.minor compatibility with Genesis/Node\n- Background reconnect loop -- ServiceBridge re-probes every 30s in standalone mode\n- Port 8080/8090 documented -- MCP vs OpenAI-compat clearly separated\n- Genesis URL configurable via GENESIS_URL env var (was hardcoded)\n- Standalone mode warning -- visible stderr alert when AitherOS not detected\n- Auto-reconnect on startup when services come online\n\n### Elysium Cloud Inference\n- Unified gateway URL -- gateway.aitherium.com handles auth + billing + inference\n- Streaming inference -- SSE passthrough for /v1/chat/completions with stream=true\n- Auth proxy routes -- /v1/auth/register, /v1/auth/login, /v1/auth/me\n- Billing proxy -- /v1/billing/balance through gateway\n- AitherConnect Elysium fallback -- cloud inference when local Genesis is down\n- AitherDesktop Elysium fallback -- third-tier chat fallback after Node\n\n### Infrastructure\n- /discovery endpoint on Genesis -- unified service URLs/versions/health\n- /api/config/services on Veil -- runtime port config for client-side JS\n- Veil healthcheck gates on Genesis -- unhealthy when backend is down\n- Desktop crash detection 90s->60s (threshold 3->2)\n\n## [0.11.0] - 2026-04-01\n\n### Agent Execution Quality (Claude Code Parity)\n- Raise loop guard block threshold from 3 to 4 -- agents get more room for iterative search\n- Soft synthesis nudge for effort >= 4 (no tool stripping, trust the model)\n- max_output_tokens escalation -- retry up to 3x when response is truncated\n- Tool result pairing guarantee -- synthetic error for orphaned tool_use blocks\n- Micro-compaction of old tool results -- save context tokens on long sessions\n- First-turn tool forcing only for effort >= 6 (trust model for lower effort)\n- Diminishing returns detection -- nudge agent when 3+ turns produce < 500 tokens\n- Message normalization -- merge consecutive same-role messages, strip empties\n- LLM retry with exponential backoff (5 retries, 500ms-16s, jitter)\n\n## [0.9.0] - 2026-03-16\n\nThe \"connected world\" release. Cross-platform identity pairing, voice capabilities, and multi-channel integration.\n\n### Added\n- **Pairing**: Cross-platform identity linking (`adk/pairing.py`)\n - `PairingManager` — SQLite-backed identity linking with 6-char pairing codes\n - Link users across Telegram, Discord, Slack, WhatsApp with 10min TTL codes\n - Canonical session IDs for cross-channel conversation continuity\n - `get_session_id()` returns \"user-{id}\" for paired users\n- **Voice**: Speech-to-text and text-to-speech client (`adk/voice.py`)\n - `VoiceClient` — async STT/TTS/emotion via AitherVoice service\n - Convenience functions: `hear()`, `say()`, `feel()`\n - 6 voice options: alloy, echo, fable, nova, onyx, shimmer\n - Emotion detection with intensity scoring\n- New exports: `PairingManager`, `PairingResult`, `PlatformIdentity`, `VoiceClient`, `TranscriptionResult`, `SynthesisResult`, `EmotionResult`\n\n### Changed\n- `__init__.py` exports expanded with pairing and voice symbols\n\n## [0.6.0] - 2026-03-13\n\nThe \"group mind\" release. Multi-agent group chat, creative tools, and Iris identity.\n\n### Added\n- **Aeon**: Multi-agent group chat engine (`adk/aeon.py`)\n - `AeonSession` — persistent group chat with parallel agent execution\n - 7 presets: balanced, creative, technical, security, minimal, duo_code, research\n - Orchestrator synthesis: Aither summarizes all agent responses\n - Serial execution for Ollama, parallel for vLLM/cloud\n - ConversationStore persistence with `type: \"aeon\"` metadata\n - `group_chat()` one-shot convenience function\n- `aither aeon` CLI command — interactive terminal group chat with color-coded agents\n - `-p/--preset`, `-a/--agents`, `-r/--rounds`, `--no-synthesize` flags\n - `reset` and `quit` commands\n- Server endpoints: `POST /aeon/chat`, `GET /aeon/presets`, `GET /aeon/sessions/{id}`\n- Creative tools in builtin_tools: `image_generate`, `image_refine`, `image_search`, `video_generate`\n- Iris agent identity with visual generation capabilities\n- 48 Aeon tests (data models, presets, chat, context, persistence, server, exports)\n\n### Changed\n- `__init__.py` exports: `AeonSession`, `AeonResponse`, `AeonMessage`, `group_chat`, `AEON_PRESETS`\n\n## [0.5.0] - 2026-03-13\n\nThe \"tenant-ready\" release. Multi-tenant admin, permission grants, safety profiles, and a full setup wizard.\n\n### Added\n- `aither setup` interactive setup wizard with hardware detection, model selection, identity config\n- Strata storage backend: SQLite WAL persistence for conversations, memories, knowledge graphs\n- CLI test runner: `aither test` with auto-discovery and parallel execution\n- Permission grants system for MCP tool access control\n- MCP account management tools\n- LLM provider auto-detection for Ollama and vLLM endpoints\n- Apache-2.0 LICENSE file\n- Elysium desktop sync module\n- Comprehensive CLI test suite (493 tests)\n- Strata storage test suite (972 tests)\n- LLM provider tests (82 tests)\n\n### Changed\n- CLI expanded: `aither setup`, `aither test`, `aither bugreport`, `aither doctor`\n- Strata module rewritten as full local-first storage engine\n- Server startup includes Strata initialization\n- README rewritten with clearer quickstart and architecture docs\n\n### Fixed\n- Dead documentation links\n- Python 3.11 compatibility issues\n- Ruff per-file-ignores configuration\n- Identity provisioning edge cases\n- Docker node-gyp native dependency builds (python3 + build tools)\n\n## [0.4.0] - 2026-03-13\n\nThe \"own your AI\" release. Self-hosted agent OS for people who don't want their data on someone else's servers.\n\n### Added\n- Muse agent identity (creative/artistic generation)\n- Port 8120 to vLLM scan for ExoNodes discovery\n- Public roadmap with milestone-based porting schedule\n- Competitive positioning: self-hosted alternative to cloud-locked AI appliances\n\n### Changed\n- LLM router: compute-aware effort routing (replaces effort-level context gating)\n- README rewritten for sovereignty-first messaging\n- Package description updated\n- Promoted from alpha to stable release\n\n### Fixed\n- Version string consistency between pyproject.toml and __init__.py\n- vLLM port scanning now includes port 8120 (ExoNodes)\n\n## [0.3.1] - 2026-03-09\n\n### Added\n- GraphMemory: SQLite knowledge graph with Ollama embeddings and hybrid search\n- NeuronPool: auto-fire pre-LLM data gathering agents\n- NanoGPT: pure Python char-level transformer with LoRA fine-tuning\n- Safety gates: IntakeGuard (input), LoopGuard (recursion), Sandbox (code exec)\n- Event system: EventEmitter with chat/tool/forge event types\n- Builtin tools: identity-based tool selection\n- ServiceBridge: auto-discovery of AitherOS services\n- Streaming: chat_stream with safety gate integration\n- Auth middleware: Bearer token authentication\n- CLI: `aither init` and `aither serve` commands\n\n### Changed\n- Wired 5 previously disconnected modules into agent loop\n- 522 total passing tests (up from 85)\n\n## [0.3.0] - 2026-03-07\n\n### Added\n- Clean-room agent development kit\n- Multi-backend LLM providers (Ollama, OpenAI, Anthropic)\n- AitherAgent class with @tool decorator\n- SQLite conversation memory\n- OpenAI-compatible server (`aither-serve`)\n- 16 agent identities as package data\n- MCP bridge to mcp.aitherium.com\n- Privacy-centric opt-in telemetry\n- Bug reporting CLI and API\n- FastAPI server with OpenAI-compatible endpoints\n- Hardware auto-detection (5 tiers, 11 profiles)\n- Fleet orchestration and multi-agent coordination\n- A2A mesh protocol\n- Federation protocol for cross-instance agent dispatch\n\n### Initial release\n- 85 passing tests\n- Apache-2.0 license\n"}, "502537aca1e7663898e3ec4adde8235822a30e28": {"t": 1785748179.9642227, "n": 74441, "h": "01ddca98b9c7a529", "c": "# Changelog\n\nAll notable changes to aither-adk will be documented in this file.\n\n## [2.19.0] - 2026-07-12\n\n### Added — Eve agent importer + durable A2A tasks + canonical agent discovery\n\n- **`adk pack import <eve_agent_path>`** — convert external Eve agents to AitherADK packs\n (from GitHub or local filesystem). Maps Eve structure (instructions → system prompt,\n agent.ts → agent.yaml, skills/*.md → skills, connections/*.ts → MCP, tools/*.ts →\n tools/node verbatim) to AitherADK format with a single command. Eve agents (compiled\n manifest format, schema v35) become installable packs compatible with `adk pack install`.\n- **Durable A2A task lifecycle (`TaskManager` backed by FileStore)** — agent-to-agent\n tasks now survive container restarts. Tasks persist as append-only JSONL\n (`.adk/tasks.jsonl`); in-memory dict for fast access; `create_task()` / `update_status()` /\n `add_artifact()` / `add_message()` all save to disk. Google A2A v0.3.0 spec compliant.\n- **Canonical agent card discovery** — `A2AServer.mount()` now exposes:\n - `GET /.well-known/agent-card.json` — Agent Card (canonical; contains agent identity,\n capabilities, skills, endpoints)\n - `GET /.well-known/agent.json` — Agent Card (legacy, redirects with 308 to canonical path)\n - `POST /a2a` — JSON-RPC 2.0 for message send/task lifecycle\n - `GET /a2a/tasks/{id}/subscribe` — SSE streaming task updates\n- **Four built-in agent packs now complete and catalogued**:\n - `hermes` — architecture review & trade-off analysis\n - `openclaw` — web research & source verification\n - `claude-code` — feature development & debugging\n - `analyst` — structured data inference & anomaly detection\n All discoverable via `adk packs` / `adk install pack:<name>`.\n- **Tolerant tool-call JSON parsing** — malformed tool JSON (extra properties, missing\n name/arguments, string-encoded nested args, missing keys) is now gracefully degraded\n instead of silently failing. `_parse_tool_json()` accepts both `\"arguments\"` and\n `\"parameters\"` keys, coerces string-encoded args, and skips unparseable calls instead\n of crashing the turn.\n\n### Known limitations\n\n- Eve agent importer: remote GitHub fetching not yet implemented (local paths only).\n- Durable task store: currently JSONL (suitable for thousands of records, not millions).\n\n## [2.18.0] - 2026-07-10\n\n### Added — AitherShell command center + Claude Code session management\n\n- **`aither sessions`** — Claude Code session manager: interactive full-screen\n browser (type-to-filter, transcript preview, deep full-text `search`, resume\n **in the current terminal** or as Windows Terminal tabs), crash guard\n (`guard install` = at-logon watchdog that snapshots live sessions and\n auto-restores the whole set after a terminal crash), and `ingest [--watch]`\n (incremental, secret-guarded sync of session conversations into the local KB\n / CompanyBrain via the standard ingest pipeline).\n- **`aither hq`** — command-center dashboard: per-service fleet health, LLM\n queue (depth/VRAM/models), Pulse alerts, live session count, inbox unread —\n auto-refreshing, with one-key jumps into chat, sessions, inbox, agents,\n brief, watchtower, and docker recovery.\n- **`aither inbox`** — unified queue over CommCore mail, Relay\n mentions/notifications, and Pulse alerts; open/mark-read/DM-reply from the\n terminal. Every source degrades independently.\n- **`aither agents`** — roster console + `agents ask -e <1-10> <agent> <q>`\n (effort-tiered ask via Genesis, with automatic `/chat/stream` fallback when\n the `/agent/sync` execute gate rejects host callers) + forge dispatch\n inspection + routine health.\n- **`aither palette`** — universal fuzzy picker across actions, sessions, and\n services; **`aither brief`** — renders Atlas's executive briefing in the\n terminal; **`aither watch`** — fleet watchtower with named wedge-signature\n detection (docker-WSL wedge, LLM-queue stall, crash-loop uptime regression)\n and optional docker auto-recovery.\n- All fleet reads go through a shared fail-soft `FleetClient` (internal-CA\n TLS via `adk._tls`, dashboard-grade timeouts, per-source degraded states).\n- Node shell-cli (`@aitherium/shell-cli` 1.13.0): `aither\n sessions|hq|inbox|palette|brief|watch|agents|docker` now pass through to the\n Python shell instead of being intent-classified as chat prompts.\n\n## [2.17.1] - 2026-07-05\n\n### Fully self-service mesh join — no customer key handling\n\n- **Auto-join headscale off the onboard response.** The conductor now auto-issues a\n headscale pre-auth key for NAT'd nodes and returns it in the `/v1/mesh/onboard`\n response; `mesh.join` consumes it and brings up the tunnel automatically. A customer\n runs one command — they never mint, fetch, or set a headscale key. Explicit\n `--headscale-key`/`AITHER_HEADSCALE_AUTH_KEY` still take precedence.\n- **Security:** `_tailscale_up` now redacts the auth key from all logs and exceptions\n (a failed `tailscale up` previously echoed the full argv, leaking the key into logs).\n\n## [2.17.0] - 2026-07-05\n\n### Added — Headscale mesh transport + local Qdrant + AitherConnect onboarding\n\n- **Headscale NAT-friendly mesh transport.** When raw WireGuard UDP:51820 is not\n viable (customer boxes behind NAT, corporate firewalls, CGNAT), `adk mesh join\n --headscale` routes the mesh tunnel through Tailscale's Headscale control plane.\n The overlay IP is still Conductor-assigned (10.77.0.0/16); Headscale provides\n only the transport layer. Configured via `AITHER_MESH_TRANSPORT=headscale`,\n `AITHER_HEADSCALE_URL` (default: https://headscale.aitherium.com), and\n `AITHER_HEADSCALE_AUTH_KEY` (pre-generated key). Automatic fallback to raw\n WireGuard if Headscale setup fails.\n- **Local Qdrant vector DB provisioning.** `adk stack` now provisions Qdrant\n locally when `AITHER_VECTOR_DB=qdrant` is set, enabling offline-first RAG\n without external dependencies. Tiered: local Qdrant in-fleet, cloud Nexus\n for enterprise multi-tenant deployments.\n- **Conductor URL public fallback.** `adk mesh join` resolves the internal\n `aitheros-conductor:8193` hostname; if unreachable (e.g., self-hosted nodes),\n it falls back to the public `conductor.aitherium.com:8193` endpoint,\n eliminating bootstrap configuration friction.\n\n### Documentation\n\n- **Self-hosting runbook.** New `docs/SELF_HOSTING.md` covers deploying AitherOS\n on customer infrastructure with Qdrant + mesh + AitherConnect onboarding.\n- **Mesh transport constraints documented.** Raw WireGuard requires public\n UDP:51820 endpoint (cloud instances, dedicated hardware); Headscale for\n NAT'd networks. Noted in CLI help and module docstrings.\n\n## [2.15.0] - 2026-07-04\n\n### Added — built-in web admin console + portal-profile settings sync\n\n- **Web admin console.** The page `adk up` opens (served at `/` and `/ui`) is now a full,\n self-contained console — Chat plus six admin tabs: **Backend** (view/switch LLM provider,\n set keys, test connection), **Packs** (list/enable/disable/reload tool packs),\n **Sessions** (browse/delete conversations), **Logs** (redacted agent-log tail),\n **Graph** (knowledge-graph search/neighborhood/stats), and **MCP** (add/remove external\n MCP servers). No build step, no CDN — a single packaged HTML asset. The original minimal\n streaming chat page stays at `/chat` for back-compat.\n- **Admin API (`adk/admin_api.py`).** ~20 endpoints under `/admin/*`, all bearer-gated by the\n existing middleware (never in `_skip_auth_paths`). Operates on the live agent — LLM backend\n swap (`LLMRouter.switch_backend`), pack reload, and MCP registration take effect without a\n restart.\n- **Settings sync (`adk/settings_sync.py`).** The user's portal profile is the source of\n truth: on startup the agent pulls `preferences.adk` and applies it over the local\n `~/.aither/config.yaml` cache; admin-console edits push a debounced snapshot back to\n `PUT /api/settings/preferences`. Provider API keys and MCP auth headers are device-local\n secrets and are never included in the pushed snapshot. Opt out with `AITHER_SETTINGS_SYNC=false`.\n\n### Security\n\n- **Timing-safe token comparison.** The server bearer check now uses `hmac.compare_digest`\n (the token is reachable over the public tunnel; a byte-wise `!=` leaked it to timing attacks).\n- **MCP add is SSRF-guarded + confirmed.** Adding an external MCP server is a two-step\n prepare→confirm flow; URLs resolving to loopback / private / link-local / metadata addresses\n are rejected (override with `AITHER_ALLOW_PRIVATE_MCP=1` for trusted local servers).\n- **Config editor is allowlisted.** `PATCH /admin/config` writes only safe scalar prefs and\n hard-denies fields that could execute code or redirect traffic at startup (e.g.\n `aither_toolpack_dirs`, backend base URLs).\n- **Log tail is scrubbed.** Bearer tokens, `#k=` fragments, and `sk-`/`aither_sk_` keys are\n redacted from the log-tail endpoint; the tunnel log is excluded.\n\n## [2.14.8] - 2026-07-04\n\n### Added — `adk up` for normal humans (no API key, no prereqs, live chat)\n\n- **Hosted-brain default — no API key required.** When there's no local model and no\n provider key, `adk up` signs in (device-flow) and routes inference through the gateway\n using the portal token (`AITHER_LLM_BACKEND=gateway` + bearer). A non-technical user\n never needs to obtain or paste an LLM API key. Explicit `--provider`/local model still\n take precedence; unattended `--yes` with no token/backend fails cleanly (exit 3).\n- **cloudflared auto-download.** If the tunnel binary is missing, `adk up` downloads the\n right platform asset to `~/.aither/bin` (SHA256-verified against the release; opt out with\n `AITHER_CLOUDFLARED_REQUIRE_CHECKSUM=0`) instead of exiting with an install hint.\n- **Built-in streaming chat page.** The agent now serves a self-contained chat UI at `/`\n (and `/chat`) with a live \"thinking…\" indicator and token-by-token streaming — so a human\n has somewhere to talk to it with immediate feedback instead of a blank wait. `adk up` opens\n it locally and passes the callback bearer in the URL fragment (`#k=…`), so `/chat/stream`\n stays authenticated (not an open proxy) with nothing to paste.\n- **Human-readable failures.** `adk up` errors now carry a plain-English `next_action`\n (kept machine-readable in `--yes` JSON).\n\n### Fixed\n\n- `adk up` chat link pointed at a non-existent `/settings/agent` route; it now points at the\n agent's own streaming chat page.\n\n## [2.14.7] - 2026-07-04\n\n### Added\n\n- **`adk up` — one command to run a persistent, fleet-connected agent.** Collapses\n the scattered onboarding surface (`host`/`quickstart`/`connect`/`login`/`setup`)\n into a single command that starts `aither-serve` (single agent, identity `aither`\n by default), opens a Cloudflare quick-tunnel for fleet reachability, registers with\n the portal, and installs a reboot-persistent autostart entry (Windows Task Scheduler\n / systemd-user / launchd). Detaches by default so the terminal is freed; `--foreground`\n blocks. Reuses the existing device-flow login, cloudflared discovery, provider-key\n store, and `_preflight_check` backend detection.\n- **Fully non-interactive path for autonomous agents.** `adk up --yes` (or any\n non-TTY invocation) takes ZERO prompts — everything resolves from flags, env, and\n saved config — and emits a single machine-readable JSON status line with meaningful\n exit codes (`0` ok, `2` bad-input, `3` no-backend, `4` tunnel-missing, `5` register-failed).\n Degrades to local-only (warn, not fail) when a portal token is absent unless\n `--require-register` is set.\n- **`adk down` / `adk status --json` companions.** `down` stops the agent + tunnel,\n best-effort deregisters, and removes the autostart entry; `status` reports the running\n agent (liveness + `/health` + tunnel + registration), with `--json` for agents/CI.\n- **`adk stack`** — the previous `adk up` behaviour (supervise the Room + Ollama consumer\n stack) now lives here; `adk up` is the connected-agent command.\n\n### Internal\n\n- New `adk/agent_daemon.py`: detached process spawn, status file\n (`~/.aither/adk-up.json`), pid liveness/teardown, and cross-platform autostart install.\n\n## [2.14.6] - 2026-07-04\n\n### Added\n\n- **`adk.preflight` — self-bootstrap capability probe.** `run_preflight(agent,\n spec)` wakes an agent up knowing what it is actually plugged into: a bounded\n (1.5s) parallel `LivenessProbe` over the router's resolved backends\n (primary/reasoning), the embeddings winner, remote MCP tools, the structured-ML\n / `/ml/teach` endpoints, and voice — returning an honest `CapabilityReport`.\n Status vocabulary distinguishes reachability from entitlement\n (`OK`/`MISSING`/`UNREACHABLE`/`AUTH`/`TIER_DENIED`/`UNSUPPORTED`), a hosted slot\n never prints a bare `OK`, and a required-but-unsatisfied slot aborts before the\n loop instead of blind-firing. Headless task/rules resolution + an allowed-roots\n policy binding (`builtin_tools.set_allowed_roots`, which resets the memoized\n cache so the override actually enforces).\n- **Multimodal (image) messages.** `Message.content` now accepts an OpenAI-style\n content-part list (`str | list[dict]`), passed through verbatim to the\n OpenAI-compatible body (gateway / vLLM / OpenAI). New `adk.llm.multimodal`\n helpers — `image_message()`, `image_content_parts()`, `to_image_url()` — build\n the parts and encode local image bytes/paths into `data:` URIs.\n- **Real vision preflight probe.** The vision slot is no longer unconditionally\n `UNSUPPORTED`: it sends a discriminable image (a solid red square) and requires\n the model to name the color, so a blind/text-only model cannot false-pass. `OK`\n only on a real round-trip; `AUTH` on 401/403; `UNSUPPORTED` when there is no\n OpenAI-compatible provider or the model cannot see the image.\n\n## [2.14.5] - 2026-07-04\n\n### Added\n\n- **`adk.reasoning.mcts` — generic Monte-Carlo Tree Search library.** A reusable\n MCTS engine (ported from the internal `UnifiedMCTS`, with all internal couplings\n removed) that any agent can drive over its own environment:\n - `MCTSEnvironment` protocol (`get_state_hash` / `get_actions` / `step` /\n `evaluate` / `clone`) — implement it and search.\n - Three **optional, default-off** model seams — `TransitionModel`, `PolicyModel`\n (PUCT priors), `ValueModel` — so a learned world-model / policy / value can be\n injected without touching the core; with all three `None` the engine is a plain\n UCT search.\n - `ObservedTransitionModel` adapter — a cheap online lookup world-model (hit =\n exact stored transition; miss = identity + negative \"unknown\" reward +\n `is_uncertain`, biasing exploration toward learning), with JSONL save/load for\n cross-run reuse.\n - `UnifiedMCTS(config).search(env)` returns an `MCTSResult` (best action, value,\n confidence, principal-variation path) and an optional `MCTSTrace`\n (visit-distribution policy target) for distillation.\n- **Self-bootstrapping agent machinery.** Build an agent from a declarative spec\n instead of bespoke wiring:\n - `adk.bootstrap.build_agent_from_spec(path) -> (AitherAgent, RunCtx)` — resolves\n prompts, activates required/optional tool packs, and carries a `memory_map`\n (a `node_type -> (role, tier)` override for memory writes, never a gate).\n - `adk.prompts.PromptBridge` — resolves an agent's prompt bindings from files\n (relative to the spec) or `AitherPrompts` dot-keys (optional import).\n - `adk.packs.PackActivator` — eager activation of installed tool packs by name\n over `register_tool_packs`; a required pack that registers zero tools raises\n `PackUnavailable`, optional packs soft-degrade.\n - `python -m adk.bootstrap.generic_agent <spec.yaml>` inspects a spec (resolved\n name, activated packs + tool counts, prompt keys, memory map) without running a\n live loop.\n- **`deep_research` tool pack** — a full multi-engine web-research workflow as\n `dr_*` agent tools (keyless search, page fetch + cache, site mirror, a citable\n findings knowledge graph, and markdown/PDF/DOCX report generation), activatable\n by name via the tool-pack loader.\n\n## [2.14.4] - 2026-07-04\n\n### Added\n\n- **`structured_ml` tool domain** — zero-shot structured-data inference for agents,\n backed by TabFM (tabular classification/regression) and TimesFM (time-series\n forecasting). Three tools:\n - `tabular_classify(support_rows, target, query_rows)` — classify rows from a\n labeled support set, in-context (no training step; up to 10 classes).\n - `tabular_regress(support_rows, target, query_rows)` — predict a numeric target.\n - `timeseries_forecast(series, horizon)` — forecast future values of a series.\n\n The tools POST to a structured-ML inference service resolved from\n `AITHER_STRUCTURED_ML_URL` (default `http://localhost:8192`), reject non-http(s)\n URLs, and summarise oversized responses to protect the agent's context.\n- **`Capability.STRUCTURED_INFERENCE`** capability token and a new **`analyst`**\n identity/pack that ships the `structured_ml` domain by default. The domain is\n opt-in — only identities that enable the `structured_ml` category get the tools.\n\n## [2.14.3] - 2026-07-03\n\n### Fixed\n\nAll four bugs below were found by actually booting `adk run` against a real\nchat model and driving a real tool-calling request end-to-end (not inferred\nfrom reading the code) — each one silently broke the \"local agent uses its\nconfigured model and its tools actually work\" path.\n\n- **`AITHER_MODEL` / `config.model` silently ignored by the OpenAI-compatible\n backend**: `LLMRouter.__init__` never read `config.model` into `self._model`\n unless the caller passed `model=` directly to the constructor — which nothing\n did. Every explicit model choice (env var, config file) was dropped, the\n provider fell back to a hardcoded per-backend default (`gpt-4o-mini` for the\n `openai` backend), and an unrecognized model name got silently remapped\n server-side to the wrong backing model. Concretely: setting\n `AITHER_MODEL=aither-orchestrator` had no effect — every request actually\n went to a different model that was never meant to be called directly and\n can't reliably use tools.\n- **`OPENAI_BASE_URL` ignored for explicit `--backend`/`AITHER_LLM_BACKEND`\n selection**: `LLMRouter._auto_detect()`'s explicit-backend branch hardcoded\n `base_url=None` when constructing the provider, instead of reading\n `config.openai_base_url` (which two other call sites in the same file\n already did correctly). Any self-hosted OpenAI-compatible endpoint\n (MicroScheduler, vLLM, LM Studio) configured via `OPENAI_BASE_URL` was\n silently ignored in favor of `https://api.openai.com/v1`.\n- **Tool-call steering never engaged on default-effort chat**: the retry that\n nudges a model back into structured tool-calling when it responds with prose\n instead of a function call was gated behind `effort >= 6`. Default `effort`\n for a plain `agent.chat(message)` call resolves to `5`, so the safety net —\n built specifically for this failure mode — never fired for default requests.\n Also now forces `tool_choice=\"required\"` on the retry instead of relying on a\n text nudge alone.\n- **Every non-string builtin-tool parameter silently typed as `\"string\"` in\n the generated tool schema**: `builtin_tools.py` uses\n `from __future__ import annotations`, so `_extract_parameters()` in\n `tools.py` was reading postponed (string) type hints (`\"int\"`, not `int`)\n straight out of `fn.__annotations__`, which never matched\n `_type_to_schema`'s type-object keys and silently fell through to\n `{\"type\": \"string\"}`. Every builtin tool with an `int`/`float`/`bool`/`list`\n parameter was affected — e.g. `file_read(start_line: int, end_line: int)`\n advertised both as strings, so a model correctly calling the tool with\n `\"start_line\": \"0\"` crashed the actual implementation with\n `unsupported operand type(s) for -: 'str' and 'int'`. Fixed by resolving\n hints through `typing.get_type_hints()` instead of the raw annotations dict.\n `_coerce_arguments()` — the runtime safety net that's supposed to coerce a\n model-supplied `\"0\"` string into `0` before a tool call executes — had the\n exact same raw-`__annotations__` bug and silently no-opped for every builtin\n tool; fixed the same way.\n- **Streaming tool-loop could hang a connection forever**: `chat_stream()`'s\n fallback to the sync tool loop (tool calls can't stream token-by-token) had\n no timeout, so a model that didn't cleanly terminate a tool-calling round\n trip left the SSE connection open indefinitely with zero bytes sent. Bounded\n with a 60s `asyncio.wait_for`; on timeout the stream yields a clear message\n instead of hanging.\n\n## [2.14.2] - 2026-07-03\n\n### Fixed\n- **AitherShell session amnesia**: the REPL generated a fresh `session_id` for\n EVERY message, so conversation history never followed across turns (the\n server stored each turn under a different session). One stable id is now\n minted per shell run; `/resume` and `/new` semantics unchanged, and the\n cross-restart auto-restore of the last session now actually works.\n\n## [2.14.1] - 2026-07-02\n\n### Added\n- **Account-tied licensing via device-flow login** — `adk login` now persists the account\n license returned by the device-flow handshake to `~/.aither/license.json` (base64\n `license_key` → `{payload, signature}` envelope), so entitlements from the authenticated\n account carry into every later `adk` invocation without re-auth. Pairs with the fleet-side\n node tool-pack gate that grants packs by plan-tier rank.\n- **`can_use_formbridge` / `can_use_untether` entitlements** (PROFESSIONAL+ rank ≥ 3) backing\n the FormBridge and UNTETHER agent tool-packs.\n\n### Fixed\n- `adk` CLI: guard the optional `memory_graph` singleton (was a latent `NameError` on the\n `/stats`, `/memory`, and save paths when memory was not constructed).\n\n## [2.14.0] - 2026-07-02\n\n### Added\n- **`adk.memory_wiki` — LyraWiki-style self-managed semantic knowledge** (opt-in; nothing constructs it by default): `MemoryWiki(graph_memory)` maintains curated wiki ARTICLES as `wiki_article` nodes (title/slug, LLM-owned markdown with `[[wikilinks]]`, embedding, durable tier, `CONSOLIDATED_FROM` edges to source memories, governance-ledgered revisions).\n - `consolidate(llm, since=, budget=)` clusters unconsolidated episodic/fact nodes (embedding + keyword), drafts/UPDATEs articles via the INJECTED llm callable (`str -> str` or `messages -> str`, sync or async — no hard model dependency), cites source node ids, marks contradicted sources superseded, then DEMOTES consolidated sources to a fast-decay tier via `promote()` (represented knowledge doesn't need raw copies).\n - `lint(llm=None)` — deterministic health checks (orphan wikilinks, empty/stale articles, live-but-contradicted pairs) + an optional LLM audit pass.\n - `prune(relevance_floor=0.05, hard_delete_after_days=14)` — article relevance = tier freshness × reinforcement (reinforce-on-recall) + link-degree to LIVE memories; below the floor → reversible governance tombstone; entombed past the window → **HARD-DELETE** (the tombstone snapshot itself is purged — content and embedding irrecoverable; the true-deletion path). A node is never deleted without a tombstone first.\n - `recall(query, limit)` — article-first RAG (curated articles rank above raw nodes) that reinforces returned articles.\n- **`adk.routines` — agent heartbeat + self-programmed routines**: `RoutineStore` is a durable registry (`~/.aither/routines/{agent}.json`, `AITHER_DATA_DIR`-aware, injectable path) of cron-scheduled self-prompts `{name, cron, instruction, enabled, last_run, last_result, tags}` that rehydrates into the existing `adk.cron.CronScheduler` on `start()`. A fire runs `agent.chat(instruction)` with the agent's full toolset — the agent programs its future self with its own adk. Guardrails: `max_routines` (12), a 5-minute min fire interval per routine, a per-fire timeout, and every result ledgered (truncated) to `last_result`. Direct-callable routines (`register_direct`) fire a bound method instead of a self-prompt. Self-management tools (`routine_create/list/update/pause/resume/delete/run_now`) ship as OpenAI tool defs + handlers (`build_routine_tools` / `register_routine_tools` / `routine_tool_defs`); the handlers ONLY touch the RoutineStore — they can never modify agent config or safety-relevant settings (the leash principle).\n- **`AitherAgent(routines=True, memory_maintenance=True)`** (both default **False** → the default agent is byte-identical): `routines=True` builds the RoutineStore + scheduler, registers the self-management tools, and starts the heartbeat lazily on the first `chat()` (or via explicit `start_routines()`). `memory_maintenance=True` registers DEFAULT routines — `wiki_consolidate` (every 2 h), `wiki_lint` / `wiki_prune` / `graph_sweep` (daily) — as DIRECT method fires so memory upkeep runs even on tiny models, while staying visible/manageable via `routine_list`.\n- `TombstoneStore.purge(tombstone_id)` — the hard-delete endpoint: irrecoverably removes a tombstone snapshot and rewrites the persisted store without it (consumed by `memory_wiki.prune`).\n\n## [2.13.6] - 2026-07-02\n\n### Added\n- **Qdrant backend for GraphMemory dataplane sync — TRUE two-way, verified live.** Set `AITHER_FLEET_QDRANT_URL` and the sync uses Qdrant (`upsert` + `scroll` + filtered `search`) instead of Nexus. This is what actually delivers cross-agent memory sharing: a node is upserted with its OWN embedding under a deterministic per-(tenant,node) point id (idempotent re-push), and `fleet_pull` reliably SCROLLS a tenant's points back — the enumerate Nexus lacks (its `/search` doesn't return ingested docs and `/export` needs lancedb, which has a hard dependency conflict in the fleet). **Proven live with 2 agents:** agent-1 ingests → auto-syncs to the tenant dataplane; a fresh agent-2 in the same tenant `fleet_pull`s all of agent-1's nodes and can search them; a different tenant pulls nothing (isolation). Dimension-safe (never mixes vector dims in a collection). Local SQLite stays source of truth; best-effort, never raises.\n\n## [2.13.5] - 2026-07-02\n\n### Fixed\n- **CRITICAL — GraphMemory dataplane sync push was 100% failing.** `_fleet_push_node` sent `source_type=\"graph\"` / `content_type=\"graph_node\"`, which AitherNexus rejects as invalid enums (HTTP 500) — so NO graph node ever replicated in 2.13.4. Now sends the accepted `source_type=\"manual\"` / `content_type=\"text\"` (graph provenance is carried in `metadata.synced_from`). **Verified live** against a fleet Nexus: a 4-node ingest replicates with `pending==0` and the collection count reflects it.\n\n### Added\n- **Swarm-awareness notification** — on a successful sync batch, `GraphMemory` emits a best-effort `graph.synced` event (tenant/workspace/agent/collection/count) to `AITHER_FLEET_EVENTS_URL` so OTHER agents in the tenant/swarm can pull the fresh data and deconflict in-flight work (push-based awareness instead of polling). No-op unless the env is set.\n\n### Known limitation\n- Cross-agent **rehydration** (`fleet_pull`) is only reliable when the fleet Nexus is backed by lancedb (persistent + exportable). An in-memory Nexus stores on `/ingest` but neither its `/search` nor `/export` returns the docs — two-way read needs the persistent Nexus.\n\n## [2.13.4] - 2026-07-02\n\n### Added\n- **Memory-maintenance primitives on `GraphMemory`** (all opt-in; default behaviour byte-identical):\n - **Reinforce-on-recall**: `recall_with_activation(..., reinforce=True)` bumps `reinforcement_count`/`last_reinforced` on every RETURNED node's metadata mirror (one UPDATE per node, WAL-safe) — making the `MemoryRecord` reinforcement fields the activation scorer reads LIVE. Default `False` = zero writes.\n - **`sweep(now, archive_below=0.05, max_nodes=None, dry_run=False)`**: read-time decay enforced at rest — archives nodes past their tier TTL with freshness×reinforcement score below `archive_below` and `reinforcement_count <= 1`; `max_nodes` additionally archives the lowest-scored overflow. PERMANENT tier immune. Every archive is a reversible governance tombstone (`TombstoneStore.recover`) + a FORGET ledger entry; a node is never deleted without a tombstone. `dry_run` returns the would-archive list without acting.\n - **`promote(node_id, tier=?, role=?)`**: re-tier/re-role a node IN PLACE (tier/role columns + metadata mirror) — edges, embedding and ledger history survive; ledgers an UPDATE entry when governance is on.\n- `MutationType.UPDATE` ledger entry kind (in-place re-tier/re-role).\n- **Per-tenant dataplane sync for `GraphMemory`** — replicate the local graph to a per-tenant vector dataplane (Nexus/Qdrant) so a sovereign agent's memory is durable + rehydratable across restarts and hosting boundaries. `fleet_push_all_nodes()` (catch-up), `fleet_pull()` (best-effort semantic top-up), `fleet_sync_pending()`, and auto-sync on ingest (`_maybe_autosync` + `drain_sync()`). **On by default** (`AITHER_FLEET_SYNC=auto`; only `false/0/off` disables) with an inferred target (local Nexus `:8122` or the gateway in cloud mode) and a SEPARATE collection (`AITHER_FLEET_GRAPH_COLLECTION`, default `graph_memory`) from the KV memory sync. New `GraphMemory(tenant_id=, workspace_id=, fleet_url=, auto_sync=)` constructor args override env (required for multi-tenant host processes). Node `synced` column + schema v4 migration. TLS verify is CA-bundle-aware (`AITHER_CA_BUNDLE`) for the internal-TLS mesh. Local SQLite stays the source of truth; every sync path is best-effort and never raises into ingest.\n\n## [2.13.3] - 2026-07-02\n\n### Added\n- **Canonical self-deploying embeddings provider** (`adk.embeddings`): one embedding provider for the whole SDK so vectors are portable across scopes (platform / tenant / sovereign) — same model + dimension everywhere. Pinned to `nomic-embed-text` (768-d; the vLLM `--served-model-name` for nomic-embed-text-v1.5). Lazy, single-flight resolution chain: explicit `AITHER_EMBEDDINGS_URL` → local vLLM (`:8209` then `:8120`, HTTPS-then-HTTP) → local Ollama → gateway (`AITHER_GATEWAY_EMBEDDINGS_URL`) → auto-deploy a local vLLM embeddings container if a GPU + Docker are present (opt out with `AITHER_EMBED_AUTODEPLOY=0`) → CPU sentence-transformers (384-d, degraded) → feature-hash (384-d, degraded, always works). Every batch is dimension-tagged so callers never silently mix 768-d and 384-d vectors. `get_default_embedder()`, `embed_texts()`, `embed_one()`, `get_provider()`, `reset_provider()`.\n\n### Changed\n- **`GraphMemory` now defaults its embedder to the canonical provider** (opt out with `AITHER_GRAPH_EMBEDDER=legacy`), so every adk agent shares one 768-d space. Added a meta-table dimension guard: the index is pinned to its first embedding's dimension and later different-dimension vectors are refused (kills 768↔384 mixing that silently poisons cosine similarity).\n\n## [2.13.2] - 2026-07-01\n\n### Added\n- **Session persistence**: AitherShell now auto-saves the active session_id to `~/.aither/config.yaml` after each chat turn and auto-restores it on the next shell launch. Multi-turn conversations survive across restarts — the messages were always persisted in SQLite + JSON, only the session pointer was missing.\n- `/new` command to start a fresh session (clears saved session_id).\n- `/sessions` command to list recent sessions with agent name, message count, and timestamp. Resume any with `/resume <id>`.\n\n## [2.13.1] - 2026-07-01\n\n### Fixed\n- **CRITICAL:** `deploy_connect()` now sends a HEAD request to verify the GitHub release asset exists before downloading, with a clear error if the asset is missing.\n- **CRITICAL:** Compose file URLs are now pinned to a stable commit hash (configurable via `AITHER_COMPOSE_PIN` env) instead of tracking the mutable `main` branch.\n- **CRITICAL:** `deploy_adk_node()` now falls back to starting `adk-serve` natively when Docker is unavailable, instead of blocking with \"Docker not installed\".\n- **HIGH:** GHCR login failure now blocks deployment immediately with actionable guidance, instead of warning and continuing to a confusing image-pull failure.\n- **HIGH:** Health checks now stream the last 30 lines of Docker Compose logs on failure, so the user can see why a container didn't start.\n- **HIGH:** vLLM port allocation now probes for conflicts before assigning; if port 8200 is busy, workers are automatically remapped to the next free port.\n- **MEDIUM:** Compose file downloads are now cached with ETag; re-running `adk deploy node` skips re-downloading an unchanged file.\n- **MEDIUM:** DGX Spark discovery now respects `AITHER_DGX_HOSTS` env (comma-separated) for custom network topologies, instead of only checking `spark.local` and `192.168.0.33`.\n- **MEDIUM:** Cloud API keys (Anthropic, OpenAI, DeepSeek) are now validated with a lightweight probe during `adk setup` infra scan, so invalid keys surface early.\n- **MEDIUM:** `deploy_connect()` now validates the extracted extension has a `manifest.json` and warns if it's missing.\n\n## [2.13.0] - 2026-07-01\n\n### Removed\n- **`adk.faculties.memory_graph`** — the pickle-based `MemoryGraph` module is deleted. `from adk.faculties import MemoryGraph` now resolves to the canonical `adk.graph_memory.GraphMemory` for back-compat.\n- **`adk.platform.memory`** — the entire legacy memory subsystem (`MemoryManager`, `UnifiedMemorySystem`, `GameEngine`, `StoryboardEngine`, `AnchorGenerator`) is deleted. Agent memory is `adk.memory.Memory` + `adk.graph_memory.GraphMemory`.\n\n### Changed\n- `adk work` command now uses `GraphMemory` instead of the removed pickle-based faculty graph.\n- `adk.MemoryGraph` and `adk.faculties.MemoryGraph` lazy exports now resolve to `adk.graph_memory.GraphMemory`.\n- `agent.set_memory_graph()` kept for back-compat but documented as deprecated; agents already wire `GraphMemory` automatically in `__init__`.\n- Platform startup (`adk.platform.infrastructure.startup`) gracefully handles missing `MemoryManager`.\n- Platform CLI module listing and health check updated to reflect removed memory module.\n\n## [2.12.7] - 2026-07-01\n\n### Deprecated\n- `adk.faculties.MemoryGraph` — emits `DeprecationWarning` on instantiation; use `adk.graph_memory.GraphMemory` (SQLite-backed, governed, embedding-native) instead.\n- `adk.platform.memory` package — the legacy `MemoryManager`, `UnifiedMemorySystem`, `GameEngine`, and `StoryboardEngine` modules now emit `DeprecationWarning` on import; use `adk.memory.Memory` and `adk.graph_memory.GraphMemory` for new code.\n\n### Changed\n- Updated `adk.faculties` docstring and usage examples to point at the canonical memory surface.\n\n## [2.12.6] - 2026-07-01\n\n### Changed\n- Trimmed public package debt by removing grid runbook artifacts and shell/room binary placeholder directories from published artifacts.\n- Removed stale public-sync allowlist entries for deleted phase test files and added explicit public-sync guards for binary placeholder directories and grid artifacts.\n- Updated the public README grid note so it no longer links to runbook files excluded from the SDK package.\n\n## [2.12.5] - 2026-07-01\n\n### Added\n- Added best-effort fleet memory sync from local ADK memory into Nexus/Qdrant-compatible RAG stores, including batch catch-up and vector-search recall hooks.\n- Added saved config/env support for fleet memory URL, collection, and sync mode.\n\n### Changed\n- Excluded FormBridge runtime/test internals from public ADK packaging and public repo sync.\n\n## [2.12.4] - 2026-07-01\n\n### Added\n- Added a hot-reload endpoint for installed agent packs so newly applied pack tools can be picked up without restarting `aither-serve`.\n\n### Fixed\n- Fixed synchronous `ChatRelay` calls on Python 3.12 when no event loop is running; async WebSocket delivery is still scheduled when a loop exists.\n- Aligned gateway inference tier tests with the current public ladder: free, starter, pro, enterprise, platform.\n\n## [2.12.0] - 2026-06-28\n\n### Added — one-command local inference + upgrade path (click-to-run)\n- **`adk quickstart-local`** — detects hardware, picks a backend\n (Ollama / llama.cpp / vLLM), installs it, downloads a model, verifies, and\n prints next steps. Bootstraps Ollama itself via winget/brew/install.sh when\n missing (no \"go install it\" dead-ends). Default model `gemma4:e2b`.\n- **`adk backend switch <ollama|llamacpp|vllm>`** (+ `backend status`) — migrate\n the active deployment between engines; re-points config and smoke-tests.\n- **`adk install pack:<name>`** (+ `adk install list` / `adk packs`) — install\n ready-made agent packs (openclaw, hermes, claude-code) into `~/.aither/agents`.\n\n### Fixed (found via live end-to-end testing on Windows + CUDA)\n- quickstart-local reported success on a dead endpoint (smoke test was warn-only);\n a failed smoke now fails the command, with a service-readiness wait.\n- `--model` resolved to `None` (getattr default never fired) → uses the real default.\n- Smoke test: 30s→180s timeout for cold model load; accepts reasoning-model output\n (empty `content` at low token budgets is no longer a false failure).\n- `adk install pack:<name>` colon syntax (argparse rejected it before dispatch).\n- `adk backend status` health probe used a nonexistent `/health` path → 404; now\n probes `/v1/models` (uniform OpenAI-compatible liveness for all three backends).\n- Windows: removed non-ASCII glyphs from printed strings that crashed the cp1252\n console (a `✓` even made a successful install report failure).\n\n## [2.11.1] - 2026-06-14\n\n### Fixed — `adk host` / `adk login` device flow (autonomous onboarding)\nFound driving `adk host` against production; all broke real device-flow login:\n- **Device flow hit the portal FRONTEND, not AitherIdentity.** `adk login`/`adk host`\n POSTed `/auth/device/code` to portal/veil.aitherium.com, which 307-redirects API auth\n to `/login`. Added `_resolve_identity_url()` mapping the aitherium.com topology to\n `idp.aitherium.com` (the Identity API); localhost/bespoke pass through unchanged.\n- **Cloudflare 403'd urllib.** `idp.*` sits behind Cloudflare, which blocks urllib's\n default `Python-urllib/3.x` UA. Device-code + poll requests now send a real\n User-Agent + Accept (verified urllib→idp 200, was 403).\n- **`adk host` now self-authenticates via device flow** when there's no token, and\n **retries via device flow on a 401/403** (a stale saved token no longer dead-ends\n registration).\n\n## [2.11.0] - 2026-06-14\n\n### Added — attach your own MCP \"hands\" to a self-hosted agent\nA self-hosted agent now has the full brain (your model) · body (this loop) · **hands**\n(your tools) trifecta. The AitherOS platform relays the MCP servers you registered for\nyour agent in the `/stream` request body as `mcp_endpoints`, and the loop wires their\ntools into the ReAct turn:\n- **`adk/mcp_endpoint_tools.register_mcp_endpoint_tools(agent, endpoints)`** — speaks MCP\n JSON-RPC (`tools/list` to discover, `tools/call` to invoke) and registers a namespaced\n proxy tool (`{endpoint}__{tool}`) for each advertised tool. Mirrors `app_proxy_tools`.\n Best-effort: an unreachable endpoint registers nothing and never raises.\n- **`POST /stream` and `POST /chat/stream`** accept an optional `mcp_endpoints` list\n (`[{name, url, [headers]}]`); `_aitheros_stream` attaches them to the agent before the\n turn. Existing callers are unaffected (the field is optional).\n\n## [2.10.1] - 2026-06-14\n\n### Fixed — self-hosted `aither-serve` was crash-on-startup (2.10.0 regression)\nA fresh `pip install aither-adk && aither-serve --backend deepseek` could not boot. Found\nand fixed while standing up a real self-hosted DeepSeek agent end-to-end:\n- **Startup `NameError: name 'port' is not defined`** in three lifespan helpers\n (`_join_aithernet`, `_init_a2a_server`, `_start_mesh_hosting`) — they referenced a bare\n `port` no longer in scope. Now use `config.server_port`, and `main()` writes the resolved\n `--port`/`--host` back to `config` so every lifespan helper and the invoke_url see the\n actual bound port.\n- **Fleet self-registration crashed the whole server** — the registration handlers caught\n `(ImportError, RuntimeError, OSError, ConnectionError)` but **not** `httpx.HTTPError`, so a\n `RemoteProtocolError`/connect error talking to the control plane took the agent down. All\n network-facing lifespan handlers now also catch `httpx.HTTPError` (non-fatal, as intended).\n- **`--backend` was ignored when `AITHER_API_KEY` was set** — `_auto_detect` picked the cloud\n gateway before the explicit backend, and `_try_elysium_fallback` then replaced the chosen\n provider. An explicit backend (deepseek/openai/anthropic/your own vllm/ollama) now always\n wins; \"auto\"/\"gateway\" still flow through detection. The operator's own brain is never\n silently hijacked.\n- **Approved-tool trace truncated after `/sessions/{id}/confirm`** — the SSE relay treated\n `AgentResponse.tool_calls_made` (a `list[str]`) as dicts (`tc.get(...)`), raised\n `AttributeError`, and an over-narrow `except` swallowed it so the client saw only\n `session_start` + heartbeats. The relay now handles string tool names, the `except` is\n broad (always emits a typed `error` + terminal `complete`), and the module-level\n `_strata_ingest` `NameError` + Chronicle `session_id` kwarg mismatch are fixed.\n\n## [2.10.0] - 2026-06-13\n\n### Added — human-in-the-loop tool approval (`adk/approval.py`)\n- **Pause before a gated tool, resume on decision.** An agent can now pause its turn before\n executing a sensitive tool and wait for a human's allow/deny — so a managed control plane\n (or any operator) can gate tool use without forking the loop.\n- **Policy via `AITHER_TOOL_APPROVAL`** — a comma-separated list of tool names (optionally\n `agent:tool`), or `*` for every tool. Empty = no gating (unchanged behavior).\n- **Survives restarts / days-later approvals.** The paused turn (user message + pending tool\n calls) is persisted to disk keyed by `session_id`; resume is a fresh request, not a held\n connection. Decisions are recorded per `(session_id, tool_name)`.\n- **`AgentResponse.requires_action` / `.pending`** — set when a turn paused; `pending` lists\n the gated tool calls awaiting a decision.\n- **`AitherAgent.resume(session_id, decisions)`** — records the decisions and re-runs the\n paused turn (the gate consumes them: allowed tools execute, denied tools get a denial\n observation, the turn continues — and may pause again on a different gated tool).\n- **Server:** `POST /sessions/{id}/confirm` records decisions and streams the resumed turn;\n `/stream` emits a `requires_action` event when a turn pauses.\n\n## [2.9.0] - 2026-06-13\n\n### Added — universal LLM continuation primitive (`adk/llm/continuation.py`)\n- **`run_continuation()` + `stitch()`** — one shared continue-until-complete primitive. When a\n completion stops because it hit the output token cap (`finish_reason == \"length\"`), it continues\n the generation and STITCHES the chunks (overlap-dedup + restart-detect) into a complete answer,\n instead of every call-site hand-rolling its own retry. Bounded three ways (max rounds AND total\n output ceiling AND a no-growth break), and it never mutates the caller's message history.\n- **`LLMProvider.chat_with_continuation()`** and **`LLMRouter.chat_with_continuation()`** — every\n provider AND the router inherit the primitive, so any call-site gets continuation for free.\n- Kill-switch `ADK_LLM_CONTINUATION=off`; tunables `ADK_LLM_MAX_CONTINUATIONS`,\n `ADK_LLM_MAX_TOTAL_OUTPUT_TOKENS` (defaults: 2 rounds / 8192 tokens).\n\n### Changed\n- The ReAct loop's truncation recovery now uses the shared primitive. The previous inline\n doubling-retry REPLACED the partial response (losing earlier text in `resp.content`) and injected\n continuation scaffolding into the live message history; it now STITCHES and leaves history clean.\n Tool-call turns are never continued as text.\n\n### Included — previously-committed-but-unreleased changes\n- 2.8.2: TLS verification on by default (`verify=False` removed).\n- 2.8.1: baked license + pack-signing public keys.\n- 2.8.0: version-bump release.\n\n## [2.7.0] - 2026-06-05\n\n### Added — provider-agnostic LLM cost levers (at the `LLMProvider` ABC)\n- **Prompt caching.** New `ProviderCapabilities` (`prompt_cache: explicit|automatic|none`, `batch`) and\n normalized `LLMResponse.cache_read_tokens`/`cache_write_tokens`, so the \"tokens saved\" meter never\n depends on which backend served the turn. Anthropic inserts `cache_control` breakpoints over the stable\n system+tools prefix; OpenAI/DeepSeek caching is automatic and their `cached_tokens` are now surfaced\n (previously discarded). Default-on in `AitherAgent.chat`; override with `cache=False`.\n- **ResponseCache** (`adk/llm/cache.py`) — exact-match response cache with a pluggable backend, wired into\n `LLMRouter` as an opt-in (`LLMRouter(response_cache=…)` + per-call `cacheable=True`). OFF by default — no\n determinism change unless opted in.\n- **Gemini provider** (`adk/llm/gemini.py`) with explicit `cachedContents` caching; registered in the router.\n- **Async batch runner** (`adk/llm/batch_runner.py`) — `run_batch()` over the Anthropic Message Batches /\n OpenAI Batch APIs (~50% off) with a transparent fallback to concurrent `chat()` where unsupported.\n- **Graph-RAG retriever interface** (`adk/graph_retriever.py`).\n\n### Added — principal authority (prompt-injection defense enforced outside the model)\n- `adk/auth.py`: `Principal` / `AuthContext` / `PrincipalResolver` (+ deny-all default).\n- `ToolRegistry.execute(name, args, auth=…)` — a default-deny authorization gate at the tool boundary, so a\n forbidden tool call is blocked regardless of what the LLM emits. `ToolDef` gains `required_clearance` /\n `action_class`. Threaded through `AitherAgent.chat`; `auth=None` is fully backward-compatible.\n- `adk/channel_auth.py`: Telegram webhook-secret + CEO-id checks and HMAC-signed approvals.\n\n## [2.6.4] - 2026-06-05\n\n### Fixes\n- **Loop-guard nudges corrupted the message history → DeepSeek 400 mid-run.**\n When the ReAct loop emitted multiple tool calls in one assistant turn and the\n loop guard fired a WARN/BLOCK/CIRCUIT_BREAK on one of them, it spliced a\n standalone `system` nudge message *between* the tool results. DeepSeek (and\n strict vLLM chat templates) require an assistant `tool_calls` message to be\n followed by exactly one `tool` result per `tool_call_id`, contiguously — so the\n interleaved message produced `400 … insufficient tool messages following\n tool_calls message` and aborted the turn (this is reliably hit after crossing\n the 15-call circuit-break threshold, where every subsequent call WARNs).\n Per-call guidance is now folded into that call's own `tool` result, and any\n standalone steering is deferred to a single message AFTER the whole batch —\n preserving the `tool_calls`↔`tool_results` pairing every backend requires.\n Regression test: `deep-research-agent/tests/test_msg_structure.py`\n (loopguard-flood scenario).\n\n## [2.6.3] - 2026-06-04\n\n### Fixes\n- **Transient provider errors killed a run on the first blip.** A single\n `502 Bad Gateway` / `503` / `529 overloaded` / `429` from the upstream API\n aborted the whole request — the Anthropic provider had **no retry at all**, and\n the OpenAI-compatible provider only retried `429`. Both now retry transient\n statuses (`408/429/500/502/503/504/529`) with `Retry-After`-aware exponential\n backoff (3 attempts), and surface the provider's body if all retries are\n exhausted. Non-transient `4xx` (e.g. `400`) still fails fast. Applies to\n `chat` and `chat_stream` on both providers.\n\n## [2.6.2] - 2026-06-04\n\n### Fixes\n- **DeepSeek (and strict OpenAI-compatible backends) 400'd on long tool\n conversations.** The ReAct loop legitimately injects mid-conversation\n `system` steering messages (the \"[DIMINISHING RETURNS]\" hint after several\n low-output tool iterations, loop-guard nudges). OpenAI tolerates a non-leading\n `system` message; DeepSeek rejects it with a 400. Long research runs\n (many `fetch_url` calls) reliably tripped this. Fix: non-leading `system`\n messages are demoted to `user` at the OpenAI-compatible provider boundary\n (`_demote_nonleading_system`) — steering intent preserved, payload valid on\n every backend; leading system prompt(s) untouched.\n- **Opaque HTTP errors.** `resp.raise_for_status()` discarded the provider's\n response body, so a 400/422 surfaced as a bare `Client error '400 Bad\n Request'` with no cause. `_ensure_ok` now includes the provider's actual\n message (e.g. `context_length_exceeded`, `Invalid 'messages'`) in the raised\n error, for both `chat` and `chat_stream`.\n\n## [2.6.1] - 2026-06-04\n\n### Fixes\n- **`MCPServer.mount()` returned HTTP 422 for every `/mcp` call.** Under\n `from __future__ import annotations` the route handler's `request: Request`\n annotation is a string that FastAPI resolves against the *module* globals, but\n `Request` was imported locally inside `mount()` — so FastAPI couldn't resolve\n it and treated `request` as a required query param. The MCP server endpoint\n (`initialize`, `tools/list`, `tools/call`) was unusable when mounted. FastAPI\n symbols are now imported at module level (guarded; the `[node]` extra still\n gates actual use). Any agent's tools can now be exposed as a working MCP\n server with `MCPServer(...).mount(app)`.\n\n## [2.6.0] - 2026-06-04\n\n### Streaming + scaffolding\n- **`AitherAgent.stream_chat`** — reliable streaming sibling of `chat()`: native\n tool-calling loop (concludes on its own, unlike the text-protocol `stream_react`)\n that emits live `tool`/`tool_result` events and streams the answer as `token`\n events. `token_delay` paces tokens for a typing effect.\n- **`adk new <template>`** — scaffold a full, runnable template app. Ships the\n `deep-research` template (a cited web-research agent: pack + server + web UI):\n `adk new deep-research`.\n\n### Fixes\n- **BYO-key no longer community-capped.** The free-tier monthly token cap now applies\n only to the metered Aitherium gateway backend, never to a user's own provider key\n (Anthropic/OpenAI/DeepSeek/Ollama/vLLM). Previously it could brick a self-hosted\n agent after ~100k tokens on the user's own key.\n- **Stronger, grounded memory recall.** `chat()` now injects the top-6 knowledge-graph\n hits (untruncated) with a hard no-fabrication instruction — was top-3 truncated to\n 200 chars, which surfaced partial facts and let weaker models invent the rest.\n- **Tool-arg coercion.** `ToolRegistry.execute` coerces LLM-supplied args toward each\n parameter's annotated type, so a tool no longer crashes when a model passes a list\n or string where an int/float/bool was expected.\n\n### Docs\n- New `docs/AGENT_DEV_GUIDE.md` — the golden path for building agents/packs.\n\n## [2.5.0] - 2026-06-04\n\n### Continual learning — agents that learn from runs and reuse memory\n\nAgents now improve the second time they do a task instead of starting from scratch.\n\n### Added\n- **Recall-before**: `AitherAgent.chat()` searches the local `SkillStore` and injects the\n top matching learned skills as a system block, so the model reuses proven procedures.\n- **Learn-after**: `_learn_after()` runs after each response — reinforces (`success_count++`)\n any skills that were recalled+reused, and extracts + saves a NEW skill from successful\n multi-tool runs via `SkillExtractor`. Wired at both `chat()` return paths.\n- Gated by `AITHER_SKILLS` (default on). The previously-unused `SkillStore`/`SkillExtractor`\n are now connected to the run loop.\n\n### Notes\n- Pairs with the platform-side LearnedProcedure substrate (recipes → scored procedures →\n skills/tools/A2A/packs → MCTS priors → evolution). The SDK shares memory via the Spirit\n bridge when configured.\n\n## [2.0.0] - 2026-06-02\n\n### Open-core boundary (BREAKING)\n\nThis release draws a single, enforced free/paid line. The free tier stays\ngenuinely useful (real agent, typed + graph + code memory, ReAct, ~essential\ntools); paid tiers unlock the *scale* capabilities via portal.aitherium.com.\n\n### Added\n- **`adk/licensing.py`** — the entitlement keystone. Resolves a tier\n (COMMUNITY by default), verifies optional portal-signed `~/.aither/license.json`\n (Ed25519), and answers `can_use_fleet/channels/cron/swarm/auto_neurons`,\n `is_agent_licensed`, `max_effort`, etc. **Fail-closed**: no/invalid/expired\n license → free tier; an unsigned license never grants premium.\n `AITHER_TENANT_SLUG=aitherium` → unrestricted INTERNAL tier.\n- **Moat guard** (`scripts/check_moat_boundary.py`) wired into the publish\n workflow — blocks any wheel that leaks the moat or drops the keystone.\n- Tests: `tests/test_licensing.py`, `tests/test_moat_boundary.py`.\n\n### Changed (gating — fail-closed)\n- Free tier effort is capped at 3 (reasoning effort 7–10 needs Professional).\n- Monthly token budget hard-block on the free tier (no silent overspend).\n- Gated behind a paid tier: fleet mode (`FleetConfig`), cross-agent delegation\n (`AgentForge.delegate`), channel adapters, cron scheduler, proactive\n auto-neurons, and `swarm`/`swarm_code` dispatch.\n\n### Removed\n- **`adk/nanogpt.py`** (on-device training IP) removed from the published SDK\n and the public repo; relocated to the internal AitherOS moat. Excluded from\n the wheel as defense-in-depth.\n- Pre-2.0 releases shipped this IP and ungated capabilities and are being\n yanked (PyPI) / removed (GitHub releases). See `scripts/yank_leaky_releases.py`\n and `scripts/purge_public_leaks.sh`.\n\n## [1.20.0] - 2026-05-27\n\n### Fixed\n- **Pack discovery path mismatch** — `_find_local_packs()` now scans both\n `~/.aither/packs/` and `~/.aitheros/packs/`, so packs installed via\n `adk pack install` are discovered by `adk-workspace`.\n\n### Added\n- **Auto-register on pack install** — `adk pack install` now auto-registers\n with the portal if the pack contains an `agent.yaml` with a `portal` section.\n One command does download + extract + register.\n- **Flux event on agent registration** — `developer_portal.py` emits\n `agent.registered` Flux event so the portal fleet UI updates in real-time.\n\n## [1.15.0] - 2026-05-26\n\n### Added\n- **Cloud memory** — `adk quickstart --cloud` now tests gateway memory and\n auto-configures Spirit URL so memories persist across devices via\n `gateway.aitherium.com/v1/memory/teach` and `/v1/memory/recall`.\n- **Config→env export for cloud mode** — `Config.from_env()` now exports\n `AITHER_CLOUD_MODE`, `AITHER_SPIRIT_URL`, and spirit path env vars from\n saved config so Memory and other modules pick them up without needing\n explicit env vars set by the user.\n- **Cloud Quick Start section** in README — 3-command setup for users who\n just want a Claude/GPT-4/DeepSeek API key and the full agent harness.\n- **`--memory` profile for node deployment** — `adk deploy node --memory`\n adds Spirit (8087) + WorkingMemory (8101) containers for persistent\n vector memory. Without this, agents use local SQLite only.\n- **Configurable vLLM in node compose** — `AITHER_VLLM_MODEL`,\n `AITHER_VLLM_GPU_UTIL`, `AITHER_VLLM_CTX_LEN`, `AITHER_VLLM_QUANT_ARGS`\n env vars let `adk setup` write GPU-detected tier settings to `.env`\n and have the node compose pick them up (TQ4 for small GPUs, BNB for large).\n\n### Fixed\n- **Memory cloud routing** — `Memory._spirit_teach()` and `_spirit_recall()`\n now use configurable paths and auth headers for gateway proxy. Previously\n cloud-only users silently lost all Spirit memory calls.\n\n## [1.9.0] - 2026-05-24\n\n### Added\n- **Composable ToolPack system** — agents can load tool packs from\n `.toolpack.yaml` directories. Drop a pack in `~/.aitheros/packs/` or\n set `AITHER_PACKS_DIR`, and any agent discovers it automatically.\n- **`tools.packs` in agent.yaml** — list tool pack IDs to auto-load\n (e.g. `packs: [crm-tools, calendar-pro]`).\n- **`register_tool_packs(agent, pack_ids, packs_dir)`** in `builtin_tools` —\n programmatic pack loading for agents.\n- **`AITHER_TOOL_PACKS` env var** — comma-separated pack IDs loaded on\n agent init (alternative to YAML config).\n- **Unknown builtin categories resolve as pack IDs** — `tools.builtin:\n [file_io, crm-tools]` now works: `crm-tools` is looked up as a pack.\n\n## [1.8.0] - 2026-05-24\n\n### Removed\n- **11 dead modules** (3,023 lines) — `agent_deploy`, `cloud_deploy`, `create`,\n `phonehome`, `sdk_bridge`, `sovereign`, `telemetry`, `telemetry_config`,\n `error_reporter`, `anonymizer`, `package`. None were imported anywhere.\n- **29 ghost exports** from `__init__.py` — `secrets`, `otel`, `fs_sandbox`,\n and `vector_memory` modules were listed in `__all__` and `__getattr__` but\n the backing files never existed. Accessing them raised `ModuleNotFoundError`.\n- **Stale dist/ artifacts** — removed old wheels/tarballs (v0.4.0–v1.4.1, 11 MB).\n- **Orphan test scripts** — `test_phase1.py` and `test_phase23.py` (one-off\n validation scripts not part of the pytest suite).\n\n### Fixed\n- **Version mismatch** — `__init__.__version__` now matches `pyproject.toml`\n (was stuck at 1.6.0).\n\n## [1.5.0] - 2026-05-23\n\n### Added\n- **`--sovereign` flag for `adk deploy node`** — registers the node with the\n Aitherium hub via federation after deployment. Saves federation credentials\n to `~/.aither/.env.federation`. Accepts `--hub` (custom hub URL) and\n `--tenant` (tenant slug) options. Non-fatal: node runs standalone if\n registration fails.\n- **Federation → fleet cross-post** (Genesis) — sovereign nodes that register\n or heartbeat via `/federation/register` and `/federation/heartbeat` now\n automatically appear in the fleet dashboard (`/fleet/deployments`) with\n `node_type: \"sovereign\"` and live status/metrics.\n- **\"Deploy Sovereign Node\" section on Connect page** (AitherVeil) — Section 8\n with ADK deploy commands, fleet dashboard links, and provision page links.\n- **`deploy-node` action in tunnel proxy** (AitherVeil) — Connect page and\n external callers can now request bootstrap scripts via\n `POST /api/tunnel/devworkspace?action=deploy-node`.\n\n### Fixed\n- **Fleet provision page** — API URL corrected from `/api/fleet/provision` to\n `/api/bridge/genesis/fleet/provision` (matches bridge proxy pattern).\n- **Fleet detail page** — API URL corrected from `/api/fleet/deployments/` to\n `/api/bridge/genesis/fleet/deployments/` (same fix).\n- **Dockerfile** — entry point corrected from `aither-serve` to `adk-serve`\n (matches `pyproject.toml` script registration). Version label updated.\n\n## [1.4.1] - 2026-05-22\n\n### Fixed\n- **macOS executable builds** — dropped `--target-arch universal2` from\n `packaging/build_executable.py`. PyPI wheels for `pydantic_core` and other\n native deps are arch-specific, not fat binaries, so universal2 always failed\n with `IncompatibleBinaryArchError`. Now builds for the runner's native arch\n (`aither-macos-arm64`).\n- **Release workflow strategy** — added `fail-fast: false` so a single\n executable build failure no longer cancels the other OS jobs.\n- **PyPI publish** — added `skip-existing: true` so re-runs of the same\n version don't fail when the artifact already exists.\n- **GitHub Release job** — now runs whenever `pypi` succeeds even if some\n executable matrix entries fail, so the release page is always created with\n whatever binaries did build successfully.\n\n## [1.4.0] - 2026-05-22\n\n### Added\n- **Sovereign template now ships with `agent_core` 5-tier memory** — generated\n backends include the canonical `agent_core/` package (working → episodic →\n semantic → procedural → identity) plus `agent_memory.py` helper, vendored\n from `portal-kit-backend`.\n- `aiosqlite>=0.20.0` added to sovereign template `requirements.txt` for\n local agent_core SQLite storage.\n- Document tombstoning: `evict_cached_conversations_referencing` is wired\n through document-delete handlers in consumer apps so memories citing\n deleted documents are blocked from recall.\n- New tests: 13 smoke tests for `portal-kit-backend/agent_core` covering\n promotion thresholds, decay rates, store roundtrip, tombstone recall\n blocking, circuit breaker state machine, and chat-turn recording.\n\n### Changed\n- `scripts/vendor_agent_core.py` now syncs canonical `agent_core` to 4\n targets: WorkspaceRuntime, ADK sovereign template, and the GargBot +\n Chelle consumer mirrors (previously only 2).\n\n## [1.2.1] - 2026-05-21\n\n### Added\n- `adk train` command group — 6 subcommands: status, launch, logs, cancel, runs, register-gpu\n- `/slash-commands` endpoint — ADK server auto-exposes all 35 CLI commands as structured JSON manifest for AitherShell\n- `/cli/execute` endpoint — AitherShell can run any CLI command through the server\n- `build_command_manifest()` — introspects full argparse tree (commands, args, choices, subcommands)\n- `_register_commands()` extracted from main() for shared parser construction\n\n### Changed\n- `_get_genesis_url()` now probes: Genesis → ADK server → Aitherium cloud gateway (works without Genesis)\n\n## [1.2.0] - 2026-05-21\n\n### Added\n- **Runtime backend switching** — `agent.switch_backend(\"deepseek\")` and `agent.llm.switch_backend()` change provider without recreating agent\n- **Hybrid reasoning** — `agent.set_reasoning_backend(\"anthropic\")` routes effort 7+ to cloud API while keeping local orchestrator\n- **First-class DeepSeek provider** — `deepseek` in effort model table with `deepseek-chat` and `deepseek-reasoner`\n- **TQ4 quantization tiers** — `nano` (6GB, TurboQuant 4-bit), `standard-tq4` (12GB, both models TQ4), `hybrid-tq4` (6GB + cloud reasoning)\n- **`adk backend` CLI** — `list`, `set`, `set-reasoning`, `test` subcommands\n- **`adk quickstart`** — unified first-run wizard (setup + auth + shell)\n- **`adk tools`** — list local + MCP tools with tier markers\n- **`adk backup`** — tarball export of `~/.aither/`\n- **`adk ingest`** — feed docs/files into knowledge graph\n- **`adk setup nemotron`** shortcut — alias for `--tier lite`\n- **`--reasoning-api`** flag in setup — `anthropic`, `openai`, `deepseek`, `gateway`\n- **`--dgx-spark URL`** flag — remote vLLM endpoint configuration\n- **DGX Spark auto-detection** — scans `spark.local`, `192.168.0.33`, `AITHER_DGX_URL` env\n- **Post-setup smoke test** — sends real inference request after container startup\n- **Schema migration system** — version table + migration runner in `memory.py` and `graph_memory.py`\n- **Configurable vLLM port scan** — `AITHER_VLLM_PORTS` env var for custom ports\n- **`aithershell` entry point** — alias for `adk` in pyproject.toml\n- Shell auto-downloads binary on first `adk shell` (no `--install` needed)\n- Shell pre-flight check warns if no backend detected\n- Shell passes full config via env vars (API key, tenant, inference URL)\n- DGX/Remote and Cloud API checks added to `adk doctor`\n- Config fields: `reasoning_backend`, `reasoning_api_key`, `deepseek_api_key`, `dgx_url`, `vllm_extra_ports`\n\n### Fixed\n- `test_aeon_chat_basic` timeout — mock scope was exiting before request\n- `adk doctor` check_disk timeout — cap rglob at 5000 files\n\n## [1.1.7] - 2026-05-19\n\n### Added\n- Remote agent pipeline — `adk onboard` → fleet dispatch → remote inference\n- Agent-to-agent federation with mesh relay\n- `adk connect` for desktop mesh enrollment\n\n## [1.1.6] - 2026-05-19\n\n### Fixed\n- Tool call execution recovery across all backends\n- Hermes tool call fallback parsing for vLLM\n\n## [1.1.5] - 2026-05-19\n\n### Fixed\n- Tool call execution + Hermes fallback parsing improvements\n\n## [1.1.0] - 2026-05-18\n\n### Added\n- Multi-channel gateway (Telegram, Discord, Slack, webhook)\n- Aeon group chat with 7 presets\n- Skills auto-extraction from multi-step sessions\n- MCP stdio server for Claude Code integration\n- `adk gateway`, `adk cron`, `adk skills`, `adk soul` commands\n- SOUL.md import/export for portable agent identity\n- Elysium cloud device-flow authentication\n\n## [1.0.0] - 2026-05-18\n\n### Breaking\n- Package consolidated — `aither_adk` namespace replaced by `adk` + `adk.platform`\n- 93 duplicate files removed from `AitherOS/packages/aither_adk/`\n\n### Added\n- Standalone core primitives (`adk.core`: Agent, Tool, Memory, ModelBackend, Capability, Trace)\n- `adk.platform` sub-package merging all platform toolkit modules\n- Compatibility shims for legacy `from aither_adk.*` imports\n\n### Changed\n- Version synced across pyproject.toml, `adk/__init__.py`, `adk/platform/__init__.py`, npm package\n- Classifier promoted from Alpha to Beta\n\n## [0.16.0] - 2026-04-16\n\n### Swarm Coding Engine & Repowise Integration\n- **`repowise_search` tool** — Semantic + keyword hybrid code search via Repowise, with ripgrep fallback\n- **`swarm_code` tool** — Dispatch complex tasks to 11-agent swarm pipeline (ARCHITECT->SWARM->REVIEW->JUDGE)\n- **`agent.swarm()`** — Async convenience method with configurable mode, effort, timeout\n- **`agent.code_search()`** — Async convenience method returning structured results\n- New tool categories: `repowise` and `swarm` in TOOL_CATEGORIES\n- `repowise_search` and `swarm_code` added to `__init__.py` exports with lazy loading\n- `IDENTITY_DEFAULTS` updated: repowise for code-focused agents, swarm for orchestration agents\n- Standalone graceful degradation: repowise falls back to ripgrep, swarm returns structured error\n\n## [0.13.0] - 2026-04-02\n\n### Graph Faculties — Local Knowledge for Every Agent\n- **CodeGraph** (2,799 lines) — Full Python AST indexer with call graph, keyword/semantic/hybrid query, embedding matrix cache, incremental re-indexing, multi-hop chain expansion\n- **MemoryGraph** (1,339 lines) — Graph-based persistent agent memory with 10 edge types, hybrid query (keyword + semantic + graph expansion), multi-hop recall, pickle persistence\n- **EmbeddingProvider** — 4-backend fallback chain: sentence-transformers (GPU/CPU) -> Ollama -> Elysium cloud -> feature hashing (zero deps)\n- **BaseFacultyGraph** — Abstract base with HMAC-SHA256 validated pickle persistence\n\n### Agent Integration\n- `agent.set_code_graph(cg)` — auto-registers `code_search` + `code_context` tools\n- `agent.set_memory_graph(mg)` — auto-registers `remember` + `recall` + `memory_stats` tools\n- Both graphs inject context into LLM prompts automatically during chat\n\n### Zero-Config Onboarding\n- `adk start` / `adk` (no args) — auto-detect project, index code, detect LLM, persistent memory, interactive chat\n- Works for any directory: Python codebases, doc folders, mixed workspaces\n- Auto-detects LLM: Ollama -> vLLM -> Elysium -> OpenAI -> Anthropic\n- Per-project persistent memory in `~/.aither/memory/<project>`\n- `adk index <path>` — standalone indexing with progress bar and stats\n\n### MCP Gateway\n- `POST /v1/embeddings` — OpenAI-compatible embedding proxy to vLLM-embeddings:8209\n- Available to all tiers (embeddings are free)\n- ADK EmbeddingProvider uses this as Elysium cloud fallback\n\n### Optional Dependencies\n- `pip install aither-adk[graphs]` — numpy for 10x cosine similarity speedup\n- `pip install aither-adk[embedding]` — sentence-transformers + torch for local GPU embeddings\n- `pip install aither-adk` alone — graphs work with feature hashing (zero deps)\n\n## [0.12.0] - 2026-04-01\n\n### Bootstrap & Service Discovery\n- Version handshake -- ADK checks major.minor compatibility with Genesis/Node\n- Background reconnect loop -- ServiceBridge re-probes every 30s in standalone mode\n- Port 8080/8090 documented -- MCP vs OpenAI-compat clearly separated\n- Genesis URL configurable via GENESIS_URL env var (was hardcoded)\n- Standalone mode warning -- visible stderr alert when AitherOS not detected\n- Auto-reconnect on startup when services come online\n\n### Elysium Cloud Inference\n- Unified gateway URL -- gateway.aitherium.com handles auth + billing + inference\n- Streaming inference -- SSE passthrough for /v1/chat/completions with stream=true\n- Auth proxy routes -- /v1/auth/register, /v1/auth/login, /v1/auth/me\n- Billing proxy -- /v1/billing/balance through gateway\n- AitherConnect Elysium fallback -- cloud inference when local Genesis is down\n- AitherDesktop Elysium fallback -- third-tier chat fallback after Node\n\n### Infrastructure\n- /discovery endpoint on Genesis -- unified service URLs/versions/health\n- /api/config/services on Veil -- runtime port config for client-side JS\n- Veil healthcheck gates on Genesis -- unhealthy when backend is down\n- Desktop crash detection 90s->60s (threshold 3->2)\n\n## [0.11.0] - 2026-04-01\n\n### Agent Execution Quality (Claude Code Parity)\n- Raise loop guard block threshold from 3 to 4 -- agents get more room for iterative search\n- Soft synthesis nudge for effort >= 4 (no tool stripping, trust the model)\n- max_output_tokens escalation -- retry up to 3x when response is truncated\n- Tool result pairing guarantee -- synthetic error for orphaned tool_use blocks\n- Micro-compaction of old tool results -- save context tokens on long sessions\n- First-turn tool forcing only for effort >= 6 (trust model for lower effort)\n- Diminishing returns detection -- nudge agent when 3+ turns produce < 500 tokens\n- Message normalization -- merge consecutive same-role messages, strip empties\n- LLM retry with exponential backoff (5 retries, 500ms-16s, jitter)\n\n## [0.9.0] - 2026-03-16\n\nThe \"connected world\" release. Cross-platform identity pairing, voice capabilities, and multi-channel integration.\n\n### Added\n- **Pairing**: Cross-platform identity linking (`adk/pairing.py`)\n - `PairingManager` — SQLite-backed identity linking with 6-char pairing codes\n - Link users across Telegram, Discord, Slack, WhatsApp with 10min TTL codes\n - Canonical session IDs for cross-channel conversation continuity\n - `get_session_id()` returns \"user-{id}\" for paired users\n- **Voice**: Speech-to-text and text-to-speech client (`adk/voice.py`)\n - `VoiceClient` — async STT/TTS/emotion via AitherVoice service\n - Convenience functions: `hear()`, `say()`, `feel()`\n - 6 voice options: alloy, echo, fable, nova, onyx, shimmer\n - Emotion detection with intensity scoring\n- New exports: `PairingManager`, `PairingResult`, `PlatformIdentity`, `VoiceClient`, `TranscriptionResult`, `SynthesisResult`, `EmotionResult`\n\n### Changed\n- `__init__.py` exports expanded with pairing and voice symbols\n\n## [0.6.0] - 2026-03-13\n\nThe \"group mind\" release. Multi-agent group chat, creative tools, and Iris identity.\n\n### Added\n- **Aeon**: Multi-agent group chat engine (`adk/aeon.py`)\n - `AeonSession` — persistent group chat with parallel agent execution\n - 7 presets: balanced, creative, technical, security, minimal, duo_code, research\n - Orchestrator synthesis: Aither summarizes all agent responses\n - Serial execution for Ollama, parallel for vLLM/cloud\n - ConversationStore persistence with `type: \"aeon\"` metadata\n - `group_chat()` one-shot convenience function\n- `aither aeon` CLI command — interactive terminal group chat with color-coded agents\n - `-p/--preset`, `-a/--agents`, `-r/--rounds`, `--no-synthesize` flags\n - `reset` and `quit` commands\n- Server endpoints: `POST /aeon/chat`, `GET /aeon/presets`, `GET /aeon/sessions/{id}`\n- Creative tools in builtin_tools: `image_generate`, `image_refine`, `image_search`, `video_generate`\n- Iris agent identity with visual generation capabilities\n- 48 Aeon tests (data models, presets, chat, context, persistence, server, exports)\n\n### Changed\n- `__init__.py` exports: `AeonSession`, `AeonResponse`, `AeonMessage`, `group_chat`, `AEON_PRESETS`\n\n## [0.5.0] - 2026-03-13\n\nThe \"tenant-ready\" release. Multi-tenant admin, permission grants, safety profiles, and a full setup wizard.\n\n### Added\n- `aither setup` interactive setup wizard with hardware detection, model selection, identity config\n- Strata storage backend: SQLite WAL persistence for conversations, memories, knowledge graphs\n- CLI test runner: `aither test` with auto-discovery and parallel execution\n- Permission grants system for MCP tool access control\n- MCP account management tools\n- LLM provider auto-detection for Ollama and vLLM endpoints\n- Apache-2.0 LICENSE file\n- Elysium desktop sync module\n- Comprehensive CLI test suite (493 tests)\n- Strata storage test suite (972 tests)\n- LLM provider tests (82 tests)\n\n### Changed\n- CLI expanded: `aither setup`, `aither test`, `aither bugreport`, `aither doctor`\n- Strata module rewritten as full local-first storage engine\n- Server startup includes Strata initialization\n- README rewritten with clearer quickstart and architecture docs\n\n### Fixed\n- Dead documentation links\n- Python 3.11 compatibility issues\n- Ruff per-file-ignores configuration\n- Identity provisioning edge cases\n- Docker node-gyp native dependency builds (python3 + build tools)\n\n## [0.4.0] - 2026-03-13\n\nThe \"own your AI\" release. Self-hosted agent OS for people who don't want their data on someone else's servers.\n\n### Added\n- Muse agent identity (creative/artistic generation)\n- Port 8120 to vLLM scan for ExoNodes discovery\n- Public roadmap with milestone-based porting schedule\n- Competitive positioning: self-hosted alternative to cloud-locked AI appliances\n\n### Changed\n- LLM router: compute-aware effort routing (replaces effort-level context gating)\n- README rewritten for sovereignty-first messaging\n- Package description updated\n- Promoted from alpha to stable release\n\n### Fixed\n- Version string consistency between pyproject.toml and __init__.py\n- vLLM port scanning now includes port 8120 (ExoNodes)\n\n## [0.3.1] - 2026-03-09\n\n### Added\n- GraphMemory: SQLite knowledge graph with Ollama embeddings and hybrid search\n- NeuronPool: auto-fire pre-LLM data gathering agents\n- NanoGPT: pure Python char-level transformer with LoRA fine-tuning\n- Safety gates: IntakeGuard (input), LoopGuard (recursion), Sandbox (code exec)\n- Event system: EventEmitter with chat/tool/forge event types\n- Builtin tools: identity-based tool selection\n- ServiceBridge: auto-discovery of AitherOS services\n- Streaming: chat_stream with safety gate integration\n- Auth middleware: Bearer token authentication\n- CLI: `aither init` and `aither serve` commands\n\n### Changed\n- Wired 5 previously disconnected modules into agent loop\n- 522 total passing tests (up from 85)\n\n## [0.3.0] - 2026-03-07\n\n### Added\n- Clean-room agent development kit\n- Multi-backend LLM providers (Ollama, OpenAI, Anthropic)\n- AitherAgent class with @tool decorator\n- SQLite conversation memory\n- OpenAI-compatible server (`aither-serve`)\n- 16 agent identities as package data\n- MCP bridge to mcp.aitherium.com\n- Privacy-centric opt-in telemetry\n- Bug reporting CLI and API\n- FastAPI server with OpenAI-compatible endpoints\n- Hardware auto-detection (5 tiers, 11 profiles)\n- Fleet orchestration and multi-agent coordination\n- A2A mesh protocol\n- Federation protocol for cross-instance agent dispatch\n\n### Initial release\n- 85 passing tests\n- Apache-2.0 license\n"}, "400f795c6c0f9bb047edbcec91b8ed120cef2707": {"t": 1785748181.339995, "n": 72216, "h": "04d66bc3767ad94a", "c": "# Changelog\n\nAll notable changes to aither-adk will be documented in this file.\n\n## [2.18.0] - 2026-07-10\n\n### Added — AitherShell command center + Claude Code session management\n\n- **`aither sessions`** — Claude Code session manager: interactive full-screen\n browser (type-to-filter, transcript preview, deep full-text `search`, resume\n **in the current terminal** or as Windows Terminal tabs), crash guard\n (`guard install` = at-logon watchdog that snapshots live sessions and\n auto-restores the whole set after a terminal crash), and `ingest [--watch]`\n (incremental, secret-guarded sync of session conversations into the local KB\n / CompanyBrain via the standard ingest pipeline).\n- **`aither hq`** — command-center dashboard: per-service fleet health, LLM\n queue (depth/VRAM/models), Pulse alerts, live session count, inbox unread —\n auto-refreshing, with one-key jumps into chat, sessions, inbox, agents,\n brief, watchtower, and docker recovery.\n- **`aither inbox`** — unified queue over CommCore mail, Relay\n mentions/notifications, and Pulse alerts; open/mark-read/DM-reply from the\n terminal. Every source degrades independently.\n- **`aither agents`** — roster console + `agents ask -e <1-10> <agent> <q>`\n (effort-tiered ask via Genesis, with automatic `/chat/stream` fallback when\n the `/agent/sync` execute gate rejects host callers) + forge dispatch\n inspection + routine health.\n- **`aither palette`** — universal fuzzy picker across actions, sessions, and\n services; **`aither brief`** — renders Atlas's executive briefing in the\n terminal; **`aither watch`** — fleet watchtower with named wedge-signature\n detection (docker-WSL wedge, LLM-queue stall, crash-loop uptime regression)\n and optional docker auto-recovery.\n- All fleet reads go through a shared fail-soft `FleetClient` (internal-CA\n TLS via `adk._tls`, dashboard-grade timeouts, per-source degraded states).\n- Node shell-cli (`@aitherium/shell-cli` 1.13.0): `aither\n sessions|hq|inbox|palette|brief|watch|agents|docker` now pass through to the\n Python shell instead of being intent-classified as chat prompts.\n\n## [2.17.1] - 2026-07-05\n\n### Fully self-service mesh join — no customer key handling\n\n- **Auto-join headscale off the onboard response.** The conductor now auto-issues a\n headscale pre-auth key for NAT'd nodes and returns it in the `/v1/mesh/onboard`\n response; `mesh.join` consumes it and brings up the tunnel automatically. A customer\n runs one command — they never mint, fetch, or set a headscale key. Explicit\n `--headscale-key`/`AITHER_HEADSCALE_AUTH_KEY` still take precedence.\n- **Security:** `_tailscale_up` now redacts the auth key from all logs and exceptions\n (a failed `tailscale up` previously echoed the full argv, leaking the key into logs).\n\n## [2.17.0] - 2026-07-05\n\n### Added — Headscale mesh transport + local Qdrant + AitherConnect onboarding\n\n- **Headscale NAT-friendly mesh transport.** When raw WireGuard UDP:51820 is not\n viable (customer boxes behind NAT, corporate firewalls, CGNAT), `adk mesh join\n --headscale` routes the mesh tunnel through Tailscale's Headscale control plane.\n The overlay IP is still Conductor-assigned (10.77.0.0/16); Headscale provides\n only the transport layer. Configured via `AITHER_MESH_TRANSPORT=headscale`,\n `AITHER_HEADSCALE_URL` (default: https://headscale.aitherium.com), and\n `AITHER_HEADSCALE_AUTH_KEY` (pre-generated key). Automatic fallback to raw\n WireGuard if Headscale setup fails.\n- **Local Qdrant vector DB provisioning.** `adk stack` now provisions Qdrant\n locally when `AITHER_VECTOR_DB=qdrant` is set, enabling offline-first RAG\n without external dependencies. Tiered: local Qdrant in-fleet, cloud Nexus\n for enterprise multi-tenant deployments.\n- **Conductor URL public fallback.** `adk mesh join` resolves the internal\n `aitheros-conductor:8193` hostname; if unreachable (e.g., self-hosted nodes),\n it falls back to the public `conductor.aitherium.com:8193` endpoint,\n eliminating bootstrap configuration friction.\n\n### Documentation\n\n- **Self-hosting runbook.** New `docs/SELF_HOSTING.md` covers deploying AitherOS\n on customer infrastructure with Qdrant + mesh + AitherConnect onboarding.\n- **Mesh transport constraints documented.** Raw WireGuard requires public\n UDP:51820 endpoint (cloud instances, dedicated hardware); Headscale for\n NAT'd networks. Noted in CLI help and module docstrings.\n\n## [2.15.0] - 2026-07-04\n\n### Added — built-in web admin console + portal-profile settings sync\n\n- **Web admin console.** The page `adk up` opens (served at `/` and `/ui`) is now a full,\n self-contained console — Chat plus six admin tabs: **Backend** (view/switch LLM provider,\n set keys, test connection), **Packs** (list/enable/disable/reload tool packs),\n **Sessions** (browse/delete conversations), **Logs** (redacted agent-log tail),\n **Graph** (knowledge-graph search/neighborhood/stats), and **MCP** (add/remove external\n MCP servers). No build step, no CDN — a single packaged HTML asset. The original minimal\n streaming chat page stays at `/chat` for back-compat.\n- **Admin API (`adk/admin_api.py`).** ~20 endpoints under `/admin/*`, all bearer-gated by the\n existing middleware (never in `_skip_auth_paths`). Operates on the live agent — LLM backend\n swap (`LLMRouter.switch_backend`), pack reload, and MCP registration take effect without a\n restart.\n- **Settings sync (`adk/settings_sync.py`).** The user's portal profile is the source of\n truth: on startup the agent pulls `preferences.adk` and applies it over the local\n `~/.aither/config.yaml` cache; admin-console edits push a debounced snapshot back to\n `PUT /api/settings/preferences`. Provider API keys and MCP auth headers are device-local\n secrets and are never included in the pushed snapshot. Opt out with `AITHER_SETTINGS_SYNC=false`.\n\n### Security\n\n- **Timing-safe token comparison.** The server bearer check now uses `hmac.compare_digest`\n (the token is reachable over the public tunnel; a byte-wise `!=` leaked it to timing attacks).\n- **MCP add is SSRF-guarded + confirmed.** Adding an external MCP server is a two-step\n prepare→confirm flow; URLs resolving to loopback / private / link-local / metadata addresses\n are rejected (override with `AITHER_ALLOW_PRIVATE_MCP=1` for trusted local servers).\n- **Config editor is allowlisted.** `PATCH /admin/config` writes only safe scalar prefs and\n hard-denies fields that could execute code or redirect traffic at startup (e.g.\n `aither_toolpack_dirs`, backend base URLs).\n- **Log tail is scrubbed.** Bearer tokens, `#k=` fragments, and `sk-`/`aither_sk_` keys are\n redacted from the log-tail endpoint; the tunnel log is excluded.\n\n## [2.14.8] - 2026-07-04\n\n### Added — `adk up` for normal humans (no API key, no prereqs, live chat)\n\n- **Hosted-brain default — no API key required.** When there's no local model and no\n provider key, `adk up` signs in (device-flow) and routes inference through the gateway\n using the portal token (`AITHER_LLM_BACKEND=gateway` + bearer). A non-technical user\n never needs to obtain or paste an LLM API key. Explicit `--provider`/local model still\n take precedence; unattended `--yes` with no token/backend fails cleanly (exit 3).\n- **cloudflared auto-download.** If the tunnel binary is missing, `adk up` downloads the\n right platform asset to `~/.aither/bin` (SHA256-verified against the release; opt out with\n `AITHER_CLOUDFLARED_REQUIRE_CHECKSUM=0`) instead of exiting with an install hint.\n- **Built-in streaming chat page.** The agent now serves a self-contained chat UI at `/`\n (and `/chat`) with a live \"thinking…\" indicator and token-by-token streaming — so a human\n has somewhere to talk to it with immediate feedback instead of a blank wait. `adk up` opens\n it locally and passes the callback bearer in the URL fragment (`#k=…`), so `/chat/stream`\n stays authenticated (not an open proxy) with nothing to paste.\n- **Human-readable failures.** `adk up` errors now carry a plain-English `next_action`\n (kept machine-readable in `--yes` JSON).\n\n### Fixed\n\n- `adk up` chat link pointed at a non-existent `/settings/agent` route; it now points at the\n agent's own streaming chat page.\n\n## [2.14.7] - 2026-07-04\n\n### Added\n\n- **`adk up` — one command to run a persistent, fleet-connected agent.** Collapses\n the scattered onboarding surface (`host`/`quickstart`/`connect`/`login`/`setup`)\n into a single command that starts `aither-serve` (single agent, identity `aither`\n by default), opens a Cloudflare quick-tunnel for fleet reachability, registers with\n the portal, and installs a reboot-persistent autostart entry (Windows Task Scheduler\n / systemd-user / launchd). Detaches by default so the terminal is freed; `--foreground`\n blocks. Reuses the existing device-flow login, cloudflared discovery, provider-key\n store, and `_preflight_check` backend detection.\n- **Fully non-interactive path for autonomous agents.** `adk up --yes` (or any\n non-TTY invocation) takes ZERO prompts — everything resolves from flags, env, and\n saved config — and emits a single machine-readable JSON status line with meaningful\n exit codes (`0` ok, `2` bad-input, `3` no-backend, `4` tunnel-missing, `5` register-failed).\n Degrades to local-only (warn, not fail) when a portal token is absent unless\n `--require-register` is set.\n- **`adk down` / `adk status --json` companions.** `down` stops the agent + tunnel,\n best-effort deregisters, and removes the autostart entry; `status` reports the running\n agent (liveness + `/health` + tunnel + registration), with `--json` for agents/CI.\n- **`adk stack`** — the previous `adk up` behaviour (supervise the Room + Ollama consumer\n stack) now lives here; `adk up` is the connected-agent command.\n\n### Internal\n\n- New `adk/agent_daemon.py`: detached process spawn, status file\n (`~/.aither/adk-up.json`), pid liveness/teardown, and cross-platform autostart install.\n\n## [2.14.6] - 2026-07-04\n\n### Added\n\n- **`adk.preflight` — self-bootstrap capability probe.** `run_preflight(agent,\n spec)` wakes an agent up knowing what it is actually plugged into: a bounded\n (1.5s) parallel `LivenessProbe` over the router's resolved backends\n (primary/reasoning), the embeddings winner, remote MCP tools, the structured-ML\n / `/ml/teach` endpoints, and voice — returning an honest `CapabilityReport`.\n Status vocabulary distinguishes reachability from entitlement\n (`OK`/`MISSING`/`UNREACHABLE`/`AUTH`/`TIER_DENIED`/`UNSUPPORTED`), a hosted slot\n never prints a bare `OK`, and a required-but-unsatisfied slot aborts before the\n loop instead of blind-firing. Headless task/rules resolution + an allowed-roots\n policy binding (`builtin_tools.set_allowed_roots`, which resets the memoized\n cache so the override actually enforces).\n- **Multimodal (image) messages.** `Message.content` now accepts an OpenAI-style\n content-part list (`str | list[dict]`), passed through verbatim to the\n OpenAI-compatible body (gateway / vLLM / OpenAI). New `adk.llm.multimodal`\n helpers — `image_message()`, `image_content_parts()`, `to_image_url()` — build\n the parts and encode local image bytes/paths into `data:` URIs.\n- **Real vision preflight probe.** The vision slot is no longer unconditionally\n `UNSUPPORTED`: it sends a discriminable image (a solid red square) and requires\n the model to name the color, so a blind/text-only model cannot false-pass. `OK`\n only on a real round-trip; `AUTH` on 401/403; `UNSUPPORTED` when there is no\n OpenAI-compatible provider or the model cannot see the image.\n\n## [2.14.5] - 2026-07-04\n\n### Added\n\n- **`adk.reasoning.mcts` — generic Monte-Carlo Tree Search library.** A reusable\n MCTS engine (ported from the internal `UnifiedMCTS`, with all internal couplings\n removed) that any agent can drive over its own environment:\n - `MCTSEnvironment` protocol (`get_state_hash` / `get_actions` / `step` /\n `evaluate` / `clone`) — implement it and search.\n - Three **optional, default-off** model seams — `TransitionModel`, `PolicyModel`\n (PUCT priors), `ValueModel` — so a learned world-model / policy / value can be\n injected without touching the core; with all three `None` the engine is a plain\n UCT search.\n - `ObservedTransitionModel` adapter — a cheap online lookup world-model (hit =\n exact stored transition; miss = identity + negative \"unknown\" reward +\n `is_uncertain`, biasing exploration toward learning), with JSONL save/load for\n cross-run reuse.\n - `UnifiedMCTS(config).search(env)` returns an `MCTSResult` (best action, value,\n confidence, principal-variation path) and an optional `MCTSTrace`\n (visit-distribution policy target) for distillation.\n- **Self-bootstrapping agent machinery.** Build an agent from a declarative spec\n instead of bespoke wiring:\n - `adk.bootstrap.build_agent_from_spec(path) -> (AitherAgent, RunCtx)` — resolves\n prompts, activates required/optional tool packs, and carries a `memory_map`\n (a `node_type -> (role, tier)` override for memory writes, never a gate).\n - `adk.prompts.PromptBridge` — resolves an agent's prompt bindings from files\n (relative to the spec) or `AitherPrompts` dot-keys (optional import).\n - `adk.packs.PackActivator` — eager activation of installed tool packs by name\n over `register_tool_packs`; a required pack that registers zero tools raises\n `PackUnavailable`, optional packs soft-degrade.\n - `python -m adk.bootstrap.generic_agent <spec.yaml>` inspects a spec (resolved\n name, activated packs + tool counts, prompt keys, memory map) without running a\n live loop.\n- **`deep_research` tool pack** — a full multi-engine web-research workflow as\n `dr_*` agent tools (keyless search, page fetch + cache, site mirror, a citable\n findings knowledge graph, and markdown/PDF/DOCX report generation), activatable\n by name via the tool-pack loader.\n\n## [2.14.4] - 2026-07-04\n\n### Added\n\n- **`structured_ml` tool domain** — zero-shot structured-data inference for agents,\n backed by TabFM (tabular classification/regression) and TimesFM (time-series\n forecasting). Three tools:\n - `tabular_classify(support_rows, target, query_rows)` — classify rows from a\n labeled support set, in-context (no training step; up to 10 classes).\n - `tabular_regress(support_rows, target, query_rows)` — predict a numeric target.\n - `timeseries_forecast(series, horizon)` — forecast future values of a series.\n\n The tools POST to a structured-ML inference service resolved from\n `AITHER_STRUCTURED_ML_URL` (default `http://localhost:8192`), reject non-http(s)\n URLs, and summarise oversized responses to protect the agent's context.\n- **`Capability.STRUCTURED_INFERENCE`** capability token and a new **`analyst`**\n identity/pack that ships the `structured_ml` domain by default. The domain is\n opt-in — only identities that enable the `structured_ml` category get the tools.\n\n## [2.14.3] - 2026-07-03\n\n### Fixed\n\nAll four bugs below were found by actually booting `adk run` against a real\nchat model and driving a real tool-calling request end-to-end (not inferred\nfrom reading the code) — each one silently broke the \"local agent uses its\nconfigured model and its tools actually work\" path.\n\n- **`AITHER_MODEL` / `config.model` silently ignored by the OpenAI-compatible\n backend**: `LLMRouter.__init__` never read `config.model` into `self._model`\n unless the caller passed `model=` directly to the constructor — which nothing\n did. Every explicit model choice (env var, config file) was dropped, the\n provider fell back to a hardcoded per-backend default (`gpt-4o-mini` for the\n `openai` backend), and an unrecognized model name got silently remapped\n server-side to the wrong backing model. Concretely: setting\n `AITHER_MODEL=aither-orchestrator` had no effect — every request actually\n went to a different model that was never meant to be called directly and\n can't reliably use tools.\n- **`OPENAI_BASE_URL` ignored for explicit `--backend`/`AITHER_LLM_BACKEND`\n selection**: `LLMRouter._auto_detect()`'s explicit-backend branch hardcoded\n `base_url=None` when constructing the provider, instead of reading\n `config.openai_base_url` (which two other call sites in the same file\n already did correctly). Any self-hosted OpenAI-compatible endpoint\n (MicroScheduler, vLLM, LM Studio) configured via `OPENAI_BASE_URL` was\n silently ignored in favor of `https://api.openai.com/v1`.\n- **Tool-call steering never engaged on default-effort chat**: the retry that\n nudges a model back into structured tool-calling when it responds with prose\n instead of a function call was gated behind `effort >= 6`. Default `effort`\n for a plain `agent.chat(message)` call resolves to `5`, so the safety net —\n built specifically for this failure mode — never fired for default requests.\n Also now forces `tool_choice=\"required\"` on the retry instead of relying on a\n text nudge alone.\n- **Every non-string builtin-tool parameter silently typed as `\"string\"` in\n the generated tool schema**: `builtin_tools.py` uses\n `from __future__ import annotations`, so `_extract_parameters()` in\n `tools.py` was reading postponed (string) type hints (`\"int\"`, not `int`)\n straight out of `fn.__annotations__`, which never matched\n `_type_to_schema`'s type-object keys and silently fell through to\n `{\"type\": \"string\"}`. Every builtin tool with an `int`/`float`/`bool`/`list`\n parameter was affected — e.g. `file_read(start_line: int, end_line: int)`\n advertised both as strings, so a model correctly calling the tool with\n `\"start_line\": \"0\"` crashed the actual implementation with\n `unsupported operand type(s) for -: 'str' and 'int'`. Fixed by resolving\n hints through `typing.get_type_hints()` instead of the raw annotations dict.\n `_coerce_arguments()` — the runtime safety net that's supposed to coerce a\n model-supplied `\"0\"` string into `0` before a tool call executes — had the\n exact same raw-`__annotations__` bug and silently no-opped for every builtin\n tool; fixed the same way.\n- **Streaming tool-loop could hang a connection forever**: `chat_stream()`'s\n fallback to the sync tool loop (tool calls can't stream token-by-token) had\n no timeout, so a model that didn't cleanly terminate a tool-calling round\n trip left the SSE connection open indefinitely with zero bytes sent. Bounded\n with a 60s `asyncio.wait_for`; on timeout the stream yields a clear message\n instead of hanging.\n\n## [2.14.2] - 2026-07-03\n\n### Fixed\n- **AitherShell session amnesia**: the REPL generated a fresh `session_id` for\n EVERY message, so conversation history never followed across turns (the\n server stored each turn under a different session). One stable id is now\n minted per shell run; `/resume` and `/new` semantics unchanged, and the\n cross-restart auto-restore of the last session now actually works.\n\n## [2.14.1] - 2026-07-02\n\n### Added\n- **Account-tied licensing via device-flow login** — `adk login` now persists the account\n license returned by the device-flow handshake to `~/.aither/license.json` (base64\n `license_key` → `{payload, signature}` envelope), so entitlements from the authenticated\n account carry into every later `adk` invocation without re-auth. Pairs with the fleet-side\n node tool-pack gate that grants packs by plan-tier rank.\n- **`can_use_formbridge` / `can_use_untether` entitlements** (PROFESSIONAL+ rank ≥ 3) backing\n the FormBridge and UNTETHER agent tool-packs.\n\n### Fixed\n- `adk` CLI: guard the optional `memory_graph` singleton (was a latent `NameError` on the\n `/stats`, `/memory`, and save paths when memory was not constructed).\n\n## [2.14.0] - 2026-07-02\n\n### Added\n- **`adk.memory_wiki` — LyraWiki-style self-managed semantic knowledge** (opt-in; nothing constructs it by default): `MemoryWiki(graph_memory)` maintains curated wiki ARTICLES as `wiki_article` nodes (title/slug, LLM-owned markdown with `[[wikilinks]]`, embedding, durable tier, `CONSOLIDATED_FROM` edges to source memories, governance-ledgered revisions).\n - `consolidate(llm, since=, budget=)` clusters unconsolidated episodic/fact nodes (embedding + keyword), drafts/UPDATEs articles via the INJECTED llm callable (`str -> str` or `messages -> str`, sync or async — no hard model dependency), cites source node ids, marks contradicted sources superseded, then DEMOTES consolidated sources to a fast-decay tier via `promote()` (represented knowledge doesn't need raw copies).\n - `lint(llm=None)` — deterministic health checks (orphan wikilinks, empty/stale articles, live-but-contradicted pairs) + an optional LLM audit pass.\n - `prune(relevance_floor=0.05, hard_delete_after_days=14)` — article relevance = tier freshness × reinforcement (reinforce-on-recall) + link-degree to LIVE memories; below the floor → reversible governance tombstone; entombed past the window → **HARD-DELETE** (the tombstone snapshot itself is purged — content and embedding irrecoverable; the true-deletion path). A node is never deleted without a tombstone first.\n - `recall(query, limit)` — article-first RAG (curated articles rank above raw nodes) that reinforces returned articles.\n- **`adk.routines` — agent heartbeat + self-programmed routines**: `RoutineStore` is a durable registry (`~/.aither/routines/{agent}.json`, `AITHER_DATA_DIR`-aware, injectable path) of cron-scheduled self-prompts `{name, cron, instruction, enabled, last_run, last_result, tags}` that rehydrates into the existing `adk.cron.CronScheduler` on `start()`. A fire runs `agent.chat(instruction)` with the agent's full toolset — the agent programs its future self with its own adk. Guardrails: `max_routines` (12), a 5-minute min fire interval per routine, a per-fire timeout, and every result ledgered (truncated) to `last_result`. Direct-callable routines (`register_direct`) fire a bound method instead of a self-prompt. Self-management tools (`routine_create/list/update/pause/resume/delete/run_now`) ship as OpenAI tool defs + handlers (`build_routine_tools` / `register_routine_tools` / `routine_tool_defs`); the handlers ONLY touch the RoutineStore — they can never modify agent config or safety-relevant settings (the leash principle).\n- **`AitherAgent(routines=True, memory_maintenance=True)`** (both default **False** → the default agent is byte-identical): `routines=True` builds the RoutineStore + scheduler, registers the self-management tools, and starts the heartbeat lazily on the first `chat()` (or via explicit `start_routines()`). `memory_maintenance=True` registers DEFAULT routines — `wiki_consolidate` (every 2 h), `wiki_lint` / `wiki_prune` / `graph_sweep` (daily) — as DIRECT method fires so memory upkeep runs even on tiny models, while staying visible/manageable via `routine_list`.\n- `TombstoneStore.purge(tombstone_id)` — the hard-delete endpoint: irrecoverably removes a tombstone snapshot and rewrites the persisted store without it (consumed by `memory_wiki.prune`).\n\n## [2.13.6] - 2026-07-02\n\n### Added\n- **Qdrant backend for GraphMemory dataplane sync — TRUE two-way, verified live.** Set `AITHER_FLEET_QDRANT_URL` and the sync uses Qdrant (`upsert` + `scroll` + filtered `search`) instead of Nexus. This is what actually delivers cross-agent memory sharing: a node is upserted with its OWN embedding under a deterministic per-(tenant,node) point id (idempotent re-push), and `fleet_pull` reliably SCROLLS a tenant's points back — the enumerate Nexus lacks (its `/search` doesn't return ingested docs and `/export` needs lancedb, which has a hard dependency conflict in the fleet). **Proven live with 2 agents:** agent-1 ingests → auto-syncs to the tenant dataplane; a fresh agent-2 in the same tenant `fleet_pull`s all of agent-1's nodes and can search them; a different tenant pulls nothing (isolation). Dimension-safe (never mixes vector dims in a collection). Local SQLite stays source of truth; best-effort, never raises.\n\n## [2.13.5] - 2026-07-02\n\n### Fixed\n- **CRITICAL — GraphMemory dataplane sync push was 100% failing.** `_fleet_push_node` sent `source_type=\"graph\"` / `content_type=\"graph_node\"`, which AitherNexus rejects as invalid enums (HTTP 500) — so NO graph node ever replicated in 2.13.4. Now sends the accepted `source_type=\"manual\"` / `content_type=\"text\"` (graph provenance is carried in `metadata.synced_from`). **Verified live** against a fleet Nexus: a 4-node ingest replicates with `pending==0` and the collection count reflects it.\n\n### Added\n- **Swarm-awareness notification** — on a successful sync batch, `GraphMemory` emits a best-effort `graph.synced` event (tenant/workspace/agent/collection/count) to `AITHER_FLEET_EVENTS_URL` so OTHER agents in the tenant/swarm can pull the fresh data and deconflict in-flight work (push-based awareness instead of polling). No-op unless the env is set.\n\n### Known limitation\n- Cross-agent **rehydration** (`fleet_pull`) is only reliable when the fleet Nexus is backed by lancedb (persistent + exportable). An in-memory Nexus stores on `/ingest` but neither its `/search` nor `/export` returns the docs — two-way read needs the persistent Nexus.\n\n## [2.13.4] - 2026-07-02\n\n### Added\n- **Memory-maintenance primitives on `GraphMemory`** (all opt-in; default behaviour byte-identical):\n - **Reinforce-on-recall**: `recall_with_activation(..., reinforce=True)` bumps `reinforcement_count`/`last_reinforced` on every RETURNED node's metadata mirror (one UPDATE per node, WAL-safe) — making the `MemoryRecord` reinforcement fields the activation scorer reads LIVE. Default `False` = zero writes.\n - **`sweep(now, archive_below=0.05, max_nodes=None, dry_run=False)`**: read-time decay enforced at rest — archives nodes past their tier TTL with freshness×reinforcement score below `archive_below` and `reinforcement_count <= 1`; `max_nodes` additionally archives the lowest-scored overflow. PERMANENT tier immune. Every archive is a reversible governance tombstone (`TombstoneStore.recover`) + a FORGET ledger entry; a node is never deleted without a tombstone. `dry_run` returns the would-archive list without acting.\n - **`promote(node_id, tier=?, role=?)`**: re-tier/re-role a node IN PLACE (tier/role columns + metadata mirror) — edges, embedding and ledger history survive; ledgers an UPDATE entry when governance is on.\n- `MutationType.UPDATE` ledger entry kind (in-place re-tier/re-role).\n- **Per-tenant dataplane sync for `GraphMemory`** — replicate the local graph to a per-tenant vector dataplane (Nexus/Qdrant) so a sovereign agent's memory is durable + rehydratable across restarts and hosting boundaries. `fleet_push_all_nodes()` (catch-up), `fleet_pull()` (best-effort semantic top-up), `fleet_sync_pending()`, and auto-sync on ingest (`_maybe_autosync` + `drain_sync()`). **On by default** (`AITHER_FLEET_SYNC=auto`; only `false/0/off` disables) with an inferred target (local Nexus `:8122` or the gateway in cloud mode) and a SEPARATE collection (`AITHER_FLEET_GRAPH_COLLECTION`, default `graph_memory`) from the KV memory sync. New `GraphMemory(tenant_id=, workspace_id=, fleet_url=, auto_sync=)` constructor args override env (required for multi-tenant host processes). Node `synced` column + schema v4 migration. TLS verify is CA-bundle-aware (`AITHER_CA_BUNDLE`) for the internal-TLS mesh. Local SQLite stays the source of truth; every sync path is best-effort and never raises into ingest.\n\n## [2.13.3] - 2026-07-02\n\n### Added\n- **Canonical self-deploying embeddings provider** (`adk.embeddings`): one embedding provider for the whole SDK so vectors are portable across scopes (platform / tenant / sovereign) — same model + dimension everywhere. Pinned to `nomic-embed-text` (768-d; the vLLM `--served-model-name` for nomic-embed-text-v1.5). Lazy, single-flight resolution chain: explicit `AITHER_EMBEDDINGS_URL` → local vLLM (`:8209` then `:8120`, HTTPS-then-HTTP) → local Ollama → gateway (`AITHER_GATEWAY_EMBEDDINGS_URL`) → auto-deploy a local vLLM embeddings container if a GPU + Docker are present (opt out with `AITHER_EMBED_AUTODEPLOY=0`) → CPU sentence-transformers (384-d, degraded) → feature-hash (384-d, degraded, always works). Every batch is dimension-tagged so callers never silently mix 768-d and 384-d vectors. `get_default_embedder()`, `embed_texts()`, `embed_one()`, `get_provider()`, `reset_provider()`.\n\n### Changed\n- **`GraphMemory` now defaults its embedder to the canonical provider** (opt out with `AITHER_GRAPH_EMBEDDER=legacy`), so every adk agent shares one 768-d space. Added a meta-table dimension guard: the index is pinned to its first embedding's dimension and later different-dimension vectors are refused (kills 768↔384 mixing that silently poisons cosine similarity).\n\n## [2.13.2] - 2026-07-01\n\n### Added\n- **Session persistence**: AitherShell now auto-saves the active session_id to `~/.aither/config.yaml` after each chat turn and auto-restores it on the next shell launch. Multi-turn conversations survive across restarts — the messages were always persisted in SQLite + JSON, only the session pointer was missing.\n- `/new` command to start a fresh session (clears saved session_id).\n- `/sessions` command to list recent sessions with agent name, message count, and timestamp. Resume any with `/resume <id>`.\n\n## [2.13.1] - 2026-07-01\n\n### Fixed\n- **CRITICAL:** `deploy_connect()` now sends a HEAD request to verify the GitHub release asset exists before downloading, with a clear error if the asset is missing.\n- **CRITICAL:** Compose file URLs are now pinned to a stable commit hash (configurable via `AITHER_COMPOSE_PIN` env) instead of tracking the mutable `main` branch.\n- **CRITICAL:** `deploy_adk_node()` now falls back to starting `adk-serve` natively when Docker is unavailable, instead of blocking with \"Docker not installed\".\n- **HIGH:** GHCR login failure now blocks deployment immediately with actionable guidance, instead of warning and continuing to a confusing image-pull failure.\n- **HIGH:** Health checks now stream the last 30 lines of Docker Compose logs on failure, so the user can see why a container didn't start.\n- **HIGH:** vLLM port allocation now probes for conflicts before assigning; if port 8200 is busy, workers are automatically remapped to the next free port.\n- **MEDIUM:** Compose file downloads are now cached with ETag; re-running `adk deploy node` skips re-downloading an unchanged file.\n- **MEDIUM:** DGX Spark discovery now respects `AITHER_DGX_HOSTS` env (comma-separated) for custom network topologies, instead of only checking `spark.local` and `192.168.0.33`.\n- **MEDIUM:** Cloud API keys (Anthropic, OpenAI, DeepSeek) are now validated with a lightweight probe during `adk setup` infra scan, so invalid keys surface early.\n- **MEDIUM:** `deploy_connect()` now validates the extracted extension has a `manifest.json` and warns if it's missing.\n\n## [2.13.0] - 2026-07-01\n\n### Removed\n- **`adk.faculties.memory_graph`** — the pickle-based `MemoryGraph` module is deleted. `from adk.faculties import MemoryGraph` now resolves to the canonical `adk.graph_memory.GraphMemory` for back-compat.\n- **`adk.platform.memory`** — the entire legacy memory subsystem (`MemoryManager`, `UnifiedMemorySystem`, `GameEngine`, `StoryboardEngine`, `AnchorGenerator`) is deleted. Agent memory is `adk.memory.Memory` + `adk.graph_memory.GraphMemory`.\n\n### Changed\n- `adk work` command now uses `GraphMemory` instead of the removed pickle-based faculty graph.\n- `adk.MemoryGraph` and `adk.faculties.MemoryGraph` lazy exports now resolve to `adk.graph_memory.GraphMemory`.\n- `agent.set_memory_graph()` kept for back-compat but documented as deprecated; agents already wire `GraphMemory` automatically in `__init__`.\n- Platform startup (`adk.platform.infrastructure.startup`) gracefully handles missing `MemoryManager`.\n- Platform CLI module listing and health check updated to reflect removed memory module.\n\n## [2.12.7] - 2026-07-01\n\n### Deprecated\n- `adk.faculties.MemoryGraph` — emits `DeprecationWarning` on instantiation; use `adk.graph_memory.GraphMemory` (SQLite-backed, governed, embedding-native) instead.\n- `adk.platform.memory` package — the legacy `MemoryManager`, `UnifiedMemorySystem`, `GameEngine`, and `StoryboardEngine` modules now emit `DeprecationWarning` on import; use `adk.memory.Memory` and `adk.graph_memory.GraphMemory` for new code.\n\n### Changed\n- Updated `adk.faculties` docstring and usage examples to point at the canonical memory surface.\n\n## [2.12.6] - 2026-07-01\n\n### Changed\n- Trimmed public package debt by removing grid runbook artifacts and shell/room binary placeholder directories from published artifacts.\n- Removed stale public-sync allowlist entries for deleted phase test files and added explicit public-sync guards for binary placeholder directories and grid artifacts.\n- Updated the public README grid note so it no longer links to runbook files excluded from the SDK package.\n\n## [2.12.5] - 2026-07-01\n\n### Added\n- Added best-effort fleet memory sync from local ADK memory into Nexus/Qdrant-compatible RAG stores, including batch catch-up and vector-search recall hooks.\n- Added saved config/env support for fleet memory URL, collection, and sync mode.\n\n### Changed\n- Excluded FormBridge runtime/test internals from public ADK packaging and public repo sync.\n\n## [2.12.4] - 2026-07-01\n\n### Added\n- Added a hot-reload endpoint for installed agent packs so newly applied pack tools can be picked up without restarting `aither-serve`.\n\n### Fixed\n- Fixed synchronous `ChatRelay` calls on Python 3.12 when no event loop is running; async WebSocket delivery is still scheduled when a loop exists.\n- Aligned gateway inference tier tests with the current public ladder: free, starter, pro, enterprise, platform.\n\n## [2.12.0] - 2026-06-28\n\n### Added — one-command local inference + upgrade path (click-to-run)\n- **`adk quickstart-local`** — detects hardware, picks a backend\n (Ollama / llama.cpp / vLLM), installs it, downloads a model, verifies, and\n prints next steps. Bootstraps Ollama itself via winget/brew/install.sh when\n missing (no \"go install it\" dead-ends). Default model `gemma4:e2b`.\n- **`adk backend switch <ollama|llamacpp|vllm>`** (+ `backend status`) — migrate\n the active deployment between engines; re-points config and smoke-tests.\n- **`adk install pack:<name>`** (+ `adk install list` / `adk packs`) — install\n ready-made agent packs (openclaw, hermes, claude-code) into `~/.aither/agents`.\n\n### Fixed (found via live end-to-end testing on Windows + CUDA)\n- quickstart-local reported success on a dead endpoint (smoke test was warn-only);\n a failed smoke now fails the command, with a service-readiness wait.\n- `--model` resolved to `None` (getattr default never fired) → uses the real default.\n- Smoke test: 30s→180s timeout for cold model load; accepts reasoning-model output\n (empty `content` at low token budgets is no longer a false failure).\n- `adk install pack:<name>` colon syntax (argparse rejected it before dispatch).\n- `adk backend status` health probe used a nonexistent `/health` path → 404; now\n probes `/v1/models` (uniform OpenAI-compatible liveness for all three backends).\n- Windows: removed non-ASCII glyphs from printed strings that crashed the cp1252\n console (a `✓` even made a successful install report failure).\n\n## [2.11.1] - 2026-06-14\n\n### Fixed — `adk host` / `adk login` device flow (autonomous onboarding)\nFound driving `adk host` against production; all broke real device-flow login:\n- **Device flow hit the portal FRONTEND, not AitherIdentity.** `adk login`/`adk host`\n POSTed `/auth/device/code` to portal/veil.aitherium.com, which 307-redirects API auth\n to `/login`. Added `_resolve_identity_url()` mapping the aitherium.com topology to\n `idp.aitherium.com` (the Identity API); localhost/bespoke pass through unchanged.\n- **Cloudflare 403'd urllib.** `idp.*` sits behind Cloudflare, which blocks urllib's\n default `Python-urllib/3.x` UA. Device-code + poll requests now send a real\n User-Agent + Accept (verified urllib→idp 200, was 403).\n- **`adk host` now self-authenticates via device flow** when there's no token, and\n **retries via device flow on a 401/403** (a stale saved token no longer dead-ends\n registration).\n\n## [2.11.0] - 2026-06-14\n\n### Added — attach your own MCP \"hands\" to a self-hosted agent\nA self-hosted agent now has the full brain (your model) · body (this loop) · **hands**\n(your tools) trifecta. The AitherOS platform relays the MCP servers you registered for\nyour agent in the `/stream` request body as `mcp_endpoints`, and the loop wires their\ntools into the ReAct turn:\n- **`adk/mcp_endpoint_tools.register_mcp_endpoint_tools(agent, endpoints)`** — speaks MCP\n JSON-RPC (`tools/list` to discover, `tools/call` to invoke) and registers a namespaced\n proxy tool (`{endpoint}__{tool}`) for each advertised tool. Mirrors `app_proxy_tools`.\n Best-effort: an unreachable endpoint registers nothing and never raises.\n- **`POST /stream` and `POST /chat/stream`** accept an optional `mcp_endpoints` list\n (`[{name, url, [headers]}]`); `_aitheros_stream` attaches them to the agent before the\n turn. Existing callers are unaffected (the field is optional).\n\n## [2.10.1] - 2026-06-14\n\n### Fixed — self-hosted `aither-serve` was crash-on-startup (2.10.0 regression)\nA fresh `pip install aither-adk && aither-serve --backend deepseek` could not boot. Found\nand fixed while standing up a real self-hosted DeepSeek agent end-to-end:\n- **Startup `NameError: name 'port' is not defined`** in three lifespan helpers\n (`_join_aithernet`, `_init_a2a_server`, `_start_mesh_hosting`) — they referenced a bare\n `port` no longer in scope. Now use `config.server_port`, and `main()` writes the resolved\n `--port`/`--host` back to `config` so every lifespan helper and the invoke_url see the\n actual bound port.\n- **Fleet self-registration crashed the whole server** — the registration handlers caught\n `(ImportError, RuntimeError, OSError, ConnectionError)` but **not** `httpx.HTTPError`, so a\n `RemoteProtocolError`/connect error talking to the control plane took the agent down. All\n network-facing lifespan handlers now also catch `httpx.HTTPError` (non-fatal, as intended).\n- **`--backend` was ignored when `AITHER_API_KEY` was set** — `_auto_detect` picked the cloud\n gateway before the explicit backend, and `_try_elysium_fallback` then replaced the chosen\n provider. An explicit backend (deepseek/openai/anthropic/your own vllm/ollama) now always\n wins; \"auto\"/\"gateway\" still flow through detection. The operator's own brain is never\n silently hijacked.\n- **Approved-tool trace truncated after `/sessions/{id}/confirm`** — the SSE relay treated\n `AgentResponse.tool_calls_made` (a `list[str]`) as dicts (`tc.get(...)`), raised\n `AttributeError`, and an over-narrow `except` swallowed it so the client saw only\n `session_start` + heartbeats. The relay now handles string tool names, the `except` is\n broad (always emits a typed `error` + terminal `complete`), and the module-level\n `_strata_ingest` `NameError` + Chronicle `session_id` kwarg mismatch are fixed.\n\n## [2.10.0] - 2026-06-13\n\n### Added — human-in-the-loop tool approval (`adk/approval.py`)\n- **Pause before a gated tool, resume on decision.** An agent can now pause its turn before\n executing a sensitive tool and wait for a human's allow/deny — so a managed control plane\n (or any operator) can gate tool use without forking the loop.\n- **Policy via `AITHER_TOOL_APPROVAL`** — a comma-separated list of tool names (optionally\n `agent:tool`), or `*` for every tool. Empty = no gating (unchanged behavior).\n- **Survives restarts / days-later approvals.** The paused turn (user message + pending tool\n calls) is persisted to disk keyed by `session_id`; resume is a fresh request, not a held\n connection. Decisions are recorded per `(session_id, tool_name)`.\n- **`AgentResponse.requires_action` / `.pending`** — set when a turn paused; `pending` lists\n the gated tool calls awaiting a decision.\n- **`AitherAgent.resume(session_id, decisions)`** — records the decisions and re-runs the\n paused turn (the gate consumes them: allowed tools execute, denied tools get a denial\n observation, the turn continues — and may pause again on a different gated tool).\n- **Server:** `POST /sessions/{id}/confirm` records decisions and streams the resumed turn;\n `/stream` emits a `requires_action` event when a turn pauses.\n\n## [2.9.0] - 2026-06-13\n\n### Added — universal LLM continuation primitive (`adk/llm/continuation.py`)\n- **`run_continuation()` + `stitch()`** — one shared continue-until-complete primitive. When a\n completion stops because it hit the output token cap (`finish_reason == \"length\"`), it continues\n the generation and STITCHES the chunks (overlap-dedup + restart-detect) into a complete answer,\n instead of every call-site hand-rolling its own retry. Bounded three ways (max rounds AND total\n output ceiling AND a no-growth break), and it never mutates the caller's message history.\n- **`LLMProvider.chat_with_continuation()`** and **`LLMRouter.chat_with_continuation()`** — every\n provider AND the router inherit the primitive, so any call-site gets continuation for free.\n- Kill-switch `ADK_LLM_CONTINUATION=off`; tunables `ADK_LLM_MAX_CONTINUATIONS`,\n `ADK_LLM_MAX_TOTAL_OUTPUT_TOKENS` (defaults: 2 rounds / 8192 tokens).\n\n### Changed\n- The ReAct loop's truncation recovery now uses the shared primitive. The previous inline\n doubling-retry REPLACED the partial response (losing earlier text in `resp.content`) and injected\n continuation scaffolding into the live message history; it now STITCHES and leaves history clean.\n Tool-call turns are never continued as text.\n\n### Included — previously-committed-but-unreleased changes\n- 2.8.2: TLS verification on by default (`verify=False` removed).\n- 2.8.1: baked license + pack-signing public keys.\n- 2.8.0: version-bump release.\n\n## [2.7.0] - 2026-06-05\n\n### Added — provider-agnostic LLM cost levers (at the `LLMProvider` ABC)\n- **Prompt caching.** New `ProviderCapabilities` (`prompt_cache: explicit|automatic|none`, `batch`) and\n normalized `LLMResponse.cache_read_tokens`/`cache_write_tokens`, so the \"tokens saved\" meter never\n depends on which backend served the turn. Anthropic inserts `cache_control` breakpoints over the stable\n system+tools prefix; OpenAI/DeepSeek caching is automatic and their `cached_tokens` are now surfaced\n (previously discarded). Default-on in `AitherAgent.chat`; override with `cache=False`.\n- **ResponseCache** (`adk/llm/cache.py`) — exact-match response cache with a pluggable backend, wired into\n `LLMRouter` as an opt-in (`LLMRouter(response_cache=…)` + per-call `cacheable=True`). OFF by default — no\n determinism change unless opted in.\n- **Gemini provider** (`adk/llm/gemini.py`) with explicit `cachedContents` caching; registered in the router.\n- **Async batch runner** (`adk/llm/batch_runner.py`) — `run_batch()` over the Anthropic Message Batches /\n OpenAI Batch APIs (~50% off) with a transparent fallback to concurrent `chat()` where unsupported.\n- **Graph-RAG retriever interface** (`adk/graph_retriever.py`).\n\n### Added — principal authority (prompt-injection defense enforced outside the model)\n- `adk/auth.py`: `Principal` / `AuthContext` / `PrincipalResolver` (+ deny-all default).\n- `ToolRegistry.execute(name, args, auth=…)` — a default-deny authorization gate at the tool boundary, so a\n forbidden tool call is blocked regardless of what the LLM emits. `ToolDef` gains `required_clearance` /\n `action_class`. Threaded through `AitherAgent.chat`; `auth=None` is fully backward-compatible.\n- `adk/channel_auth.py`: Telegram webhook-secret + CEO-id checks and HMAC-signed approvals.\n\n## [2.6.4] - 2026-06-05\n\n### Fixes\n- **Loop-guard nudges corrupted the message history → DeepSeek 400 mid-run.**\n When the ReAct loop emitted multiple tool calls in one assistant turn and the\n loop guard fired a WARN/BLOCK/CIRCUIT_BREAK on one of them, it spliced a\n standalone `system` nudge message *between* the tool results. DeepSeek (and\n strict vLLM chat templates) require an assistant `tool_calls` message to be\n followed by exactly one `tool` result per `tool_call_id`, contiguously — so the\n interleaved message produced `400 … insufficient tool messages following\n tool_calls message` and aborted the turn (this is reliably hit after crossing\n the 15-call circuit-break threshold, where every subsequent call WARNs).\n Per-call guidance is now folded into that call's own `tool` result, and any\n standalone steering is deferred to a single message AFTER the whole batch —\n preserving the `tool_calls`↔`tool_results` pairing every backend requires.\n Regression test: `deep-research-agent/tests/test_msg_structure.py`\n (loopguard-flood scenario).\n\n## [2.6.3] - 2026-06-04\n\n### Fixes\n- **Transient provider errors killed a run on the first blip.** A single\n `502 Bad Gateway` / `503` / `529 overloaded` / `429` from the upstream API\n aborted the whole request — the Anthropic provider had **no retry at all**, and\n the OpenAI-compatible provider only retried `429`. Both now retry transient\n statuses (`408/429/500/502/503/504/529`) with `Retry-After`-aware exponential\n backoff (3 attempts), and surface the provider's body if all retries are\n exhausted. Non-transient `4xx` (e.g. `400`) still fails fast. Applies to\n `chat` and `chat_stream` on both providers.\n\n## [2.6.2] - 2026-06-04\n\n### Fixes\n- **DeepSeek (and strict OpenAI-compatible backends) 400'd on long tool\n conversations.** The ReAct loop legitimately injects mid-conversation\n `system` steering messages (the \"[DIMINISHING RETURNS]\" hint after several\n low-output tool iterations, loop-guard nudges). OpenAI tolerates a non-leading\n `system` message; DeepSeek rejects it with a 400. Long research runs\n (many `fetch_url` calls) reliably tripped this. Fix: non-leading `system`\n messages are demoted to `user` at the OpenAI-compatible provider boundary\n (`_demote_nonleading_system`) — steering intent preserved, payload valid on\n every backend; leading system prompt(s) untouched.\n- **Opaque HTTP errors.** `resp.raise_for_status()` discarded the provider's\n response body, so a 400/422 surfaced as a bare `Client error '400 Bad\n Request'` with no cause. `_ensure_ok` now includes the provider's actual\n message (e.g. `context_length_exceeded`, `Invalid 'messages'`) in the raised\n error, for both `chat` and `chat_stream`.\n\n## [2.6.1] - 2026-06-04\n\n### Fixes\n- **`MCPServer.mount()` returned HTTP 422 for every `/mcp` call.** Under\n `from __future__ import annotations` the route handler's `request: Request`\n annotation is a string that FastAPI resolves against the *module* globals, but\n `Request` was imported locally inside `mount()` — so FastAPI couldn't resolve\n it and treated `request` as a required query param. The MCP server endpoint\n (`initialize`, `tools/list`, `tools/call`) was unusable when mounted. FastAPI\n symbols are now imported at module level (guarded; the `[node]` extra still\n gates actual use). Any agent's tools can now be exposed as a working MCP\n server with `MCPServer(...).mount(app)`.\n\n## [2.6.0] - 2026-06-04\n\n### Streaming + scaffolding\n- **`AitherAgent.stream_chat`** — reliable streaming sibling of `chat()`: native\n tool-calling loop (concludes on its own, unlike the text-protocol `stream_react`)\n that emits live `tool`/`tool_result` events and streams the answer as `token`\n events. `token_delay` paces tokens for a typing effect.\n- **`adk new <template>`** — scaffold a full, runnable template app. Ships the\n `deep-research` template (a cited web-research agent: pack + server + web UI):\n `adk new deep-research`.\n\n### Fixes\n- **BYO-key no longer community-capped.** The free-tier monthly token cap now applies\n only to the metered Aitherium gateway backend, never to a user's own provider key\n (Anthropic/OpenAI/DeepSeek/Ollama/vLLM). Previously it could brick a self-hosted\n agent after ~100k tokens on the user's own key.\n- **Stronger, grounded memory recall.** `chat()` now injects the top-6 knowledge-graph\n hits (untruncated) with a hard no-fabrication instruction — was top-3 truncated to\n 200 chars, which surfaced partial facts and let weaker models invent the rest.\n- **Tool-arg coercion.** `ToolRegistry.execute` coerces LLM-supplied args toward each\n parameter's annotated type, so a tool no longer crashes when a model passes a list\n or string where an int/float/bool was expected.\n\n### Docs\n- New `docs/AGENT_DEV_GUIDE.md` — the golden path for building agents/packs.\n\n## [2.5.0] - 2026-06-04\n\n### Continual learning — agents that learn from runs and reuse memory\n\nAgents now improve the second time they do a task instead of starting from scratch.\n\n### Added\n- **Recall-before**: `AitherAgent.chat()` searches the local `SkillStore` and injects the\n top matching learned skills as a system block, so the model reuses proven procedures.\n- **Learn-after**: `_learn_after()` runs after each response — reinforces (`success_count++`)\n any skills that were recalled+reused, and extracts + saves a NEW skill from successful\n multi-tool runs via `SkillExtractor`. Wired at both `chat()` return paths.\n- Gated by `AITHER_SKILLS` (default on). The previously-unused `SkillStore`/`SkillExtractor`\n are now connected to the run loop.\n\n### Notes\n- Pairs with the platform-side LearnedProcedure substrate (recipes → scored procedures →\n skills/tools/A2A/packs → MCTS priors → evolution). The SDK shares memory via the Spirit\n bridge when configured.\n\n## [2.0.0] - 2026-06-02\n\n### Open-core boundary (BREAKING)\n\nThis release draws a single, enforced free/paid line. The free tier stays\ngenuinely useful (real agent, typed + graph + code memory, ReAct, ~essential\ntools); paid tiers unlock the *scale* capabilities via portal.aitherium.com.\n\n### Added\n- **`adk/licensing.py`** — the entitlement keystone. Resolves a tier\n (COMMUNITY by default), verifies optional portal-signed `~/.aither/license.json`\n (Ed25519), and answers `can_use_fleet/channels/cron/swarm/auto_neurons`,\n `is_agent_licensed`, `max_effort`, etc. **Fail-closed**: no/invalid/expired\n license → free tier; an unsigned license never grants premium.\n `AITHER_TENANT_SLUG=aitherium` → unrestricted INTERNAL tier.\n- **Moat guard** (`scripts/check_moat_boundary.py`) wired into the publish\n workflow — blocks any wheel that leaks the moat or drops the keystone.\n- Tests: `tests/test_licensing.py`, `tests/test_moat_boundary.py`.\n\n### Changed (gating — fail-closed)\n- Free tier effort is capped at 3 (reasoning effort 7–10 needs Professional).\n- Monthly token budget hard-block on the free tier (no silent overspend).\n- Gated behind a paid tier: fleet mode (`FleetConfig`), cross-agent delegation\n (`AgentForge.delegate`), channel adapters, cron scheduler, proactive\n auto-neurons, and `swarm`/`swarm_code` dispatch.\n\n### Removed\n- **`adk/nanogpt.py`** (on-device training IP) removed from the published SDK\n and the public repo; relocated to the internal AitherOS moat. Excluded from\n the wheel as defense-in-depth.\n- Pre-2.0 releases shipped this IP and ungated capabilities and are being\n yanked (PyPI) / removed (GitHub releases). See `scripts/yank_leaky_releases.py`\n and `scripts/purge_public_leaks.sh`.\n\n## [1.20.0] - 2026-05-27\n\n### Fixed\n- **Pack discovery path mismatch** — `_find_local_packs()` now scans both\n `~/.aither/packs/` and `~/.aitheros/packs/`, so packs installed via\n `adk pack install` are discovered by `adk-workspace`.\n\n### Added\n- **Auto-register on pack install** — `adk pack install` now auto-registers\n with the portal if the pack contains an `agent.yaml` with a `portal` section.\n One command does download + extract + register.\n- **Flux event on agent registration** — `developer_portal.py` emits\n `agent.registered` Flux event so the portal fleet UI updates in real-time.\n\n## [1.15.0] - 2026-05-26\n\n### Added\n- **Cloud memory** — `adk quickstart --cloud` now tests gateway memory and\n auto-configures Spirit URL so memories persist across devices via\n `gateway.aitherium.com/v1/memory/teach` and `/v1/memory/recall`.\n- **Config→env export for cloud mode** — `Config.from_env()` now exports\n `AITHER_CLOUD_MODE`, `AITHER_SPIRIT_URL`, and spirit path env vars from\n saved config so Memory and other modules pick them up without needing\n explicit env vars set by the user.\n- **Cloud Quick Start section** in README — 3-command setup for users who\n just want a Claude/GPT-4/DeepSeek API key and the full agent harness.\n- **`--memory` profile for node deployment** — `adk deploy node --memory`\n adds Spirit (8087) + WorkingMemory (8101) containers for persistent\n vector memory. Without this, agents use local SQLite only.\n- **Configurable vLLM in node compose** — `AITHER_VLLM_MODEL`,\n `AITHER_VLLM_GPU_UTIL`, `AITHER_VLLM_CTX_LEN`, `AITHER_VLLM_QUANT_ARGS`\n env vars let `adk setup` write GPU-detected tier settings to `.env`\n and have the node compose pick them up (TQ4 for small GPUs, BNB for large).\n\n### Fixed\n- **Memory cloud routing** — `Memory._spirit_teach()` and `_spirit_recall()`\n now use configurable paths and auth headers for gateway proxy. Previously\n cloud-only users silently lost all Spirit memory calls.\n\n## [1.9.0] - 2026-05-24\n\n### Added\n- **Composable ToolPack system** — agents can load tool packs from\n `.toolpack.yaml` directories. Drop a pack in `~/.aitheros/packs/` or\n set `AITHER_PACKS_DIR`, and any agent discovers it automatically.\n- **`tools.packs` in agent.yaml** — list tool pack IDs to auto-load\n (e.g. `packs: [crm-tools, calendar-pro]`).\n- **`register_tool_packs(agent, pack_ids, packs_dir)`** in `builtin_tools` —\n programmatic pack loading for agents.\n- **`AITHER_TOOL_PACKS` env var** — comma-separated pack IDs loaded on\n agent init (alternative to YAML config).\n- **Unknown builtin categories resolve as pack IDs** — `tools.builtin:\n [file_io, crm-tools]` now works: `crm-tools` is looked up as a pack.\n\n## [1.8.0] - 2026-05-24\n\n### Removed\n- **11 dead modules** (3,023 lines) — `agent_deploy`, `cloud_deploy`, `create`,\n `phonehome`, `sdk_bridge`, `sovereign`, `telemetry`, `telemetry_config`,\n `error_reporter`, `anonymizer`, `package`. None were imported anywhere.\n- **29 ghost exports** from `__init__.py` — `secrets`, `otel`, `fs_sandbox`,\n and `vector_memory` modules were listed in `__all__` and `__getattr__` but\n the backing files never existed. Accessing them raised `ModuleNotFoundError`.\n- **Stale dist/ artifacts** — removed old wheels/tarballs (v0.4.0–v1.4.1, 11 MB).\n- **Orphan test scripts** — `test_phase1.py` and `test_phase23.py` (one-off\n validation scripts not part of the pytest suite).\n\n### Fixed\n- **Version mismatch** — `__init__.__version__` now matches `pyproject.toml`\n (was stuck at 1.6.0).\n\n## [1.5.0] - 2026-05-23\n\n### Added\n- **`--sovereign` flag for `adk deploy node`** — registers the node with the\n Aitherium hub via federation after deployment. Saves federation credentials\n to `~/.aither/.env.federation`. Accepts `--hub` (custom hub URL) and\n `--tenant` (tenant slug) options. Non-fatal: node runs standalone if\n registration fails.\n- **Federation → fleet cross-post** (Genesis) — sovereign nodes that register\n or heartbeat via `/federation/register` and `/federation/heartbeat` now\n automatically appear in the fleet dashboard (`/fleet/deployments`) with\n `node_type: \"sovereign\"` and live status/metrics.\n- **\"Deploy Sovereign Node\" section on Connect page** (AitherVeil) — Section 8\n with ADK deploy commands, fleet dashboard links, and provision page links.\n- **`deploy-node` action in tunnel proxy** (AitherVeil) — Connect page and\n external callers can now request bootstrap scripts via\n `POST /api/tunnel/devworkspace?action=deploy-node`.\n\n### Fixed\n- **Fleet provision page** — API URL corrected from `/api/fleet/provision` to\n `/api/bridge/genesis/fleet/provision` (matches bridge proxy pattern).\n- **Fleet detail page** — API URL corrected from `/api/fleet/deployments/` to\n `/api/bridge/genesis/fleet/deployments/` (same fix).\n- **Dockerfile** — entry point corrected from `aither-serve` to `adk-serve`\n (matches `pyproject.toml` script registration). Version label updated.\n\n## [1.4.1] - 2026-05-22\n\n### Fixed\n- **macOS executable builds** — dropped `--target-arch universal2` from\n `packaging/build_executable.py`. PyPI wheels for `pydantic_core` and other\n native deps are arch-specific, not fat binaries, so universal2 always failed\n with `IncompatibleBinaryArchError`. Now builds for the runner's native arch\n (`aither-macos-arm64`).\n- **Release workflow strategy** — added `fail-fast: false` so a single\n executable build failure no longer cancels the other OS jobs.\n- **PyPI publish** — added `skip-existing: true` so re-runs of the same\n version don't fail when the artifact already exists.\n- **GitHub Release job** — now runs whenever `pypi` succeeds even if some\n executable matrix entries fail, so the release page is always created with\n whatever binaries did build successfully.\n\n## [1.4.0] - 2026-05-22\n\n### Added\n- **Sovereign template now ships with `agent_core` 5-tier memory** — generated\n backends include the canonical `agent_core/` package (working → episodic →\n semantic → procedural → identity) plus `agent_memory.py` helper, vendored\n from `portal-kit-backend`.\n- `aiosqlite>=0.20.0` added to sovereign template `requirements.txt` for\n local agent_core SQLite storage.\n- Document tombstoning: `evict_cached_conversations_referencing` is wired\n through document-delete handlers in consumer apps so memories citing\n deleted documents are blocked from recall.\n- New tests: 13 smoke tests for `portal-kit-backend/agent_core` covering\n promotion thresholds, decay rates, store roundtrip, tombstone recall\n blocking, circuit breaker state machine, and chat-turn recording.\n\n### Changed\n- `scripts/vendor_agent_core.py` now syncs canonical `agent_core` to 4\n targets: WorkspaceRuntime, ADK sovereign template, and the GargBot +\n Chelle consumer mirrors (previously only 2).\n\n## [1.2.1] - 2026-05-21\n\n### Added\n- `adk train` command group — 6 subcommands: status, launch, logs, cancel, runs, register-gpu\n- `/slash-commands` endpoint — ADK server auto-exposes all 35 CLI commands as structured JSON manifest for AitherShell\n- `/cli/execute` endpoint — AitherShell can run any CLI command through the server\n- `build_command_manifest()` — introspects full argparse tree (commands, args, choices, subcommands)\n- `_register_commands()` extracted from main() for shared parser construction\n\n### Changed\n- `_get_genesis_url()` now probes: Genesis → ADK server → Aitherium cloud gateway (works without Genesis)\n\n## [1.2.0] - 2026-05-21\n\n### Added\n- **Runtime backend switching** — `agent.switch_backend(\"deepseek\")` and `agent.llm.switch_backend()` change provider without recreating agent\n- **Hybrid reasoning** — `agent.set_reasoning_backend(\"anthropic\")` routes effort 7+ to cloud API while keeping local orchestrator\n- **First-class DeepSeek provider** — `deepseek` in effort model table with `deepseek-chat` and `deepseek-reasoner`\n- **TQ4 quantization tiers** — `nano` (6GB, TurboQuant 4-bit), `standard-tq4` (12GB, both models TQ4), `hybrid-tq4` (6GB + cloud reasoning)\n- **`adk backend` CLI** — `list`, `set`, `set-reasoning`, `test` subcommands\n- **`adk quickstart`** — unified first-run wizard (setup + auth + shell)\n- **`adk tools`** — list local + MCP tools with tier markers\n- **`adk backup`** — tarball export of `~/.aither/`\n- **`adk ingest`** — feed docs/files into knowledge graph\n- **`adk setup nemotron`** shortcut — alias for `--tier lite`\n- **`--reasoning-api`** flag in setup — `anthropic`, `openai`, `deepseek`, `gateway`\n- **`--dgx-spark URL`** flag — remote vLLM endpoint configuration\n- **DGX Spark auto-detection** — scans `spark.local`, `192.168.0.33`, `AITHER_DGX_URL` env\n- **Post-setup smoke test** — sends real inference request after container startup\n- **Schema migration system** — version table + migration runner in `memory.py` and `graph_memory.py`\n- **Configurable vLLM port scan** — `AITHER_VLLM_PORTS` env var for custom ports\n- **`aithershell` entry point** — alias for `adk` in pyproject.toml\n- Shell auto-downloads binary on first `adk shell` (no `--install` needed)\n- Shell pre-flight check warns if no backend detected\n- Shell passes full config via env vars (API key, tenant, inference URL)\n- DGX/Remote and Cloud API checks added to `adk doctor`\n- Config fields: `reasoning_backend`, `reasoning_api_key`, `deepseek_api_key`, `dgx_url`, `vllm_extra_ports`\n\n### Fixed\n- `test_aeon_chat_basic` timeout — mock scope was exiting before request\n- `adk doctor` check_disk timeout — cap rglob at 5000 files\n\n## [1.1.7] - 2026-05-19\n\n### Added\n- Remote agent pipeline — `adk onboard` → fleet dispatch → remote inference\n- Agent-to-agent federation with mesh relay\n- `adk connect` for desktop mesh enrollment\n\n## [1.1.6] - 2026-05-19\n\n### Fixed\n- Tool call execution recovery across all backends\n- Hermes tool call fallback parsing for vLLM\n\n## [1.1.5] - 2026-05-19\n\n### Fixed\n- Tool call execution + Hermes fallback parsing improvements\n\n## [1.1.0] - 2026-05-18\n\n### Added\n- Multi-channel gateway (Telegram, Discord, Slack, webhook)\n- Aeon group chat with 7 presets\n- Skills auto-extraction from multi-step sessions\n- MCP stdio server for Claude Code integration\n- `adk gateway`, `adk cron`, `adk skills`, `adk soul` commands\n- SOUL.md import/export for portable agent identity\n- Elysium cloud device-flow authentication\n\n## [1.0.0] - 2026-05-18\n\n### Breaking\n- Package consolidated — `aither_adk` namespace replaced by `adk` + `adk.platform`\n- 93 duplicate files removed from `AitherOS/packages/aither_adk/`\n\n### Added\n- Standalone core primitives (`adk.core`: Agent, Tool, Memory, ModelBackend, Capability, Trace)\n- `adk.platform` sub-package merging all platform toolkit modules\n- Compatibility shims for legacy `from aither_adk.*` imports\n\n### Changed\n- Version synced across pyproject.toml, `adk/__init__.py`, `adk/platform/__init__.py`, npm package\n- Classifier promoted from Alpha to Beta\n\n## [0.16.0] - 2026-04-16\n\n### Swarm Coding Engine & Repowise Integration\n- **`repowise_search` tool** — Semantic + keyword hybrid code search via Repowise, with ripgrep fallback\n- **`swarm_code` tool** — Dispatch complex tasks to 11-agent swarm pipeline (ARCHITECT->SWARM->REVIEW->JUDGE)\n- **`agent.swarm()`** — Async convenience method with configurable mode, effort, timeout\n- **`agent.code_search()`** — Async convenience method returning structured results\n- New tool categories: `repowise` and `swarm` in TOOL_CATEGORIES\n- `repowise_search` and `swarm_code` added to `__init__.py` exports with lazy loading\n- `IDENTITY_DEFAULTS` updated: repowise for code-focused agents, swarm for orchestration agents\n- Standalone graceful degradation: repowise falls back to ripgrep, swarm returns structured error\n\n## [0.13.0] - 2026-04-02\n\n### Graph Faculties — Local Knowledge for Every Agent\n- **CodeGraph** (2,799 lines) — Full Python AST indexer with call graph, keyword/semantic/hybrid query, embedding matrix cache, incremental re-indexing, multi-hop chain expansion\n- **MemoryGraph** (1,339 lines) — Graph-based persistent agent memory with 10 edge types, hybrid query (keyword + semantic + graph expansion), multi-hop recall, pickle persistence\n- **EmbeddingProvider** — 4-backend fallback chain: sentence-transformers (GPU/CPU) -> Ollama -> Elysium cloud -> feature hashing (zero deps)\n- **BaseFacultyGraph** — Abstract base with HMAC-SHA256 validated pickle persistence\n\n### Agent Integration\n- `agent.set_code_graph(cg)` — auto-registers `code_search` + `code_context` tools\n- `agent.set_memory_graph(mg)` — auto-registers `remember` + `recall` + `memory_stats` tools\n- Both graphs inject context into LLM prompts automatically during chat\n\n### Zero-Config Onboarding\n- `adk start` / `adk` (no args) — auto-detect project, index code, detect LLM, persistent memory, interactive chat\n- Works for any directory: Python codebases, doc folders, mixed workspaces\n- Auto-detects LLM: Ollama -> vLLM -> Elysium -> OpenAI -> Anthropic\n- Per-project persistent memory in `~/.aither/memory/<project>`\n- `adk index <path>` — standalone indexing with progress bar and stats\n\n### MCP Gateway\n- `POST /v1/embeddings` — OpenAI-compatible embedding proxy to vLLM-embeddings:8209\n- Available to all tiers (embeddings are free)\n- ADK EmbeddingProvider uses this as Elysium cloud fallback\n\n### Optional Dependencies\n- `pip install aither-adk[graphs]` — numpy for 10x cosine similarity speedup\n- `pip install aither-adk[embedding]` — sentence-transformers + torch for local GPU embeddings\n- `pip install aither-adk` alone — graphs work with feature hashing (zero deps)\n\n## [0.12.0] - 2026-04-01\n\n### Bootstrap & Service Discovery\n- Version handshake -- ADK checks major.minor compatibility with Genesis/Node\n- Background reconnect loop -- ServiceBridge re-probes every 30s in standalone mode\n- Port 8080/8090 documented -- MCP vs OpenAI-compat clearly separated\n- Genesis URL configurable via GENESIS_URL env var (was hardcoded)\n- Standalone mode warning -- visible stderr alert when AitherOS not detected\n- Auto-reconnect on startup when services come online\n\n### Elysium Cloud Inference\n- Unified gateway URL -- gateway.aitherium.com handles auth + billing + inference\n- Streaming inference -- SSE passthrough for /v1/chat/completions with stream=true\n- Auth proxy routes -- /v1/auth/register, /v1/auth/login, /v1/auth/me\n- Billing proxy -- /v1/billing/balance through gateway\n- AitherConnect Elysium fallback -- cloud inference when local Genesis is down\n- AitherDesktop Elysium fallback -- third-tier chat fallback after Node\n\n### Infrastructure\n- /discovery endpoint on Genesis -- unified service URLs/versions/health\n- /api/config/services on Veil -- runtime port config for client-side JS\n- Veil healthcheck gates on Genesis -- unhealthy when backend is down\n- Desktop crash detection 90s->60s (threshold 3->2)\n\n## [0.11.0] - 2026-04-01\n\n### Agent Execution Quality (Claude Code Parity)\n- Raise loop guard block threshold from 3 to 4 -- agents get more room for iterative search\n- Soft synthesis nudge for effort >= 4 (no tool stripping, trust the model)\n- max_output_tokens escalation -- retry up to 3x when response is truncated\n- Tool result pairing guarantee -- synthetic error for orphaned tool_use blocks\n- Micro-compaction of old tool results -- save context tokens on long sessions\n- First-turn tool forcing only for effort >= 6 (trust model for lower effort)\n- Diminishing returns detection -- nudge agent when 3+ turns produce < 500 tokens\n- Message normalization -- merge consecutive same-role messages, strip empties\n- LLM retry with exponential backoff (5 retries, 500ms-16s, jitter)\n\n## [0.9.0] - 2026-03-16\n\nThe \"connected world\" release. Cross-platform identity pairing, voice capabilities, and multi-channel integration.\n\n### Added\n- **Pairing**: Cross-platform identity linking (`adk/pairing.py`)\n - `PairingManager` — SQLite-backed identity linking with 6-char pairing codes\n - Link users across Telegram, Discord, Slack, WhatsApp with 10min TTL codes\n - Canonical session IDs for cross-channel conversation continuity\n - `get_session_id()` returns \"user-{id}\" for paired users\n- **Voice**: Speech-to-text and text-to-speech client (`adk/voice.py`)\n - `VoiceClient` — async STT/TTS/emotion via AitherVoice service\n - Convenience functions: `hear()`, `say()`, `feel()`\n - 6 voice options: alloy, echo, fable, nova, onyx, shimmer\n - Emotion detection with intensity scoring\n- New exports: `PairingManager`, `PairingResult`, `PlatformIdentity`, `VoiceClient`, `TranscriptionResult`, `SynthesisResult`, `EmotionResult`\n\n### Changed\n- `__init__.py` exports expanded with pairing and voice symbols\n\n## [0.6.0] - 2026-03-13\n\nThe \"group mind\" release. Multi-agent group chat, creative tools, and Iris identity.\n\n### Added\n- **Aeon**: Multi-agent group chat engine (`adk/aeon.py`)\n - `AeonSession` — persistent group chat with parallel agent execution\n - 7 presets: balanced, creative, technical, security, minimal, duo_code, research\n - Orchestrator synthesis: Aither summarizes all agent responses\n - Serial execution for Ollama, parallel for vLLM/cloud\n - ConversationStore persistence with `type: \"aeon\"` metadata\n - `group_chat()` one-shot convenience function\n- `aither aeon` CLI command — interactive terminal group chat with color-coded agents\n - `-p/--preset`, `-a/--agents`, `-r/--rounds`, `--no-synthesize` flags\n - `reset` and `quit` commands\n- Server endpoints: `POST /aeon/chat`, `GET /aeon/presets`, `GET /aeon/sessions/{id}`\n- Creative tools in builtin_tools: `image_generate`, `image_refine`, `image_search`, `video_generate`\n- Iris agent identity with visual generation capabilities\n- 48 Aeon tests (data models, presets, chat, context, persistence, server, exports)\n\n### Changed\n- `__init__.py` exports: `AeonSession`, `AeonResponse`, `AeonMessage`, `group_chat`, `AEON_PRESETS`\n\n## [0.5.0] - 2026-03-13\n\nThe \"tenant-ready\" release. Multi-tenant admin, permission grants, safety profiles, and a full setup wizard.\n\n### Added\n- `aither setup` interactive setup wizard with hardware detection, model selection, identity config\n- Strata storage backend: SQLite WAL persistence for conversations, memories, knowledge graphs\n- CLI test runner: `aither test` with auto-discovery and parallel execution\n- Permission grants system for MCP tool access control\n- MCP account management tools\n- LLM provider auto-detection for Ollama and vLLM endpoints\n- Apache-2.0 LICENSE file\n- Elysium desktop sync module\n- Comprehensive CLI test suite (493 tests)\n- Strata storage test suite (972 tests)\n- LLM provider tests (82 tests)\n\n### Changed\n- CLI expanded: `aither setup`, `aither test`, `aither bugreport`, `aither doctor`\n- Strata module rewritten as full local-first storage engine\n- Server startup includes Strata initialization\n- README rewritten with clearer quickstart and architecture docs\n\n### Fixed\n- Dead documentation links\n- Python 3.11 compatibility issues\n- Ruff per-file-ignores configuration\n- Identity provisioning edge cases\n- Docker node-gyp native dependency builds (python3 + build tools)\n\n## [0.4.0] - 2026-03-13\n\nThe \"own your AI\" release. Self-hosted agent OS for people who don't want their data on someone else's servers.\n\n### Added\n- Muse agent identity (creative/artistic generation)\n- Port 8120 to vLLM scan for ExoNodes discovery\n- Public roadmap with milestone-based porting schedule\n- Competitive positioning: self-hosted alternative to cloud-locked AI appliances\n\n### Changed\n- LLM router: compute-aware effort routing (replaces effort-level context gating)\n- README rewritten for sovereignty-first messaging\n- Package description updated\n- Promoted from alpha to stable release\n\n### Fixed\n- Version string consistency between pyproject.toml and __init__.py\n- vLLM port scanning now includes port 8120 (ExoNodes)\n\n## [0.3.1] - 2026-03-09\n\n### Added\n- GraphMemory: SQLite knowledge graph with Ollama embeddings and hybrid search\n- NeuronPool: auto-fire pre-LLM data gathering agents\n- NanoGPT: pure Python char-level transformer with LoRA fine-tuning\n- Safety gates: IntakeGuard (input), LoopGuard (recursion), Sandbox (code exec)\n- Event system: EventEmitter with chat/tool/forge event types\n- Builtin tools: identity-based tool selection\n- ServiceBridge: auto-discovery of AitherOS services\n- Streaming: chat_stream with safety gate integration\n- Auth middleware: Bearer token authentication\n- CLI: `aither init` and `aither serve` commands\n\n### Changed\n- Wired 5 previously disconnected modules into agent loop\n- 522 total passing tests (up from 85)\n\n## [0.3.0] - 2026-03-07\n\n### Added\n- Clean-room agent development kit\n- Multi-backend LLM providers (Ollama, OpenAI, Anthropic)\n- AitherAgent class with @tool decorator\n- SQLite conversation memory\n- OpenAI-compatible server (`aither-serve`)\n- 16 agent identities as package data\n- MCP bridge to mcp.aitherium.com\n- Privacy-centric opt-in telemetry\n- Bug reporting CLI and API\n- FastAPI server with OpenAI-compatible endpoints\n- Hardware auto-detection (5 tiers, 11 profiles)\n- Fleet orchestration and multi-agent coordination\n- A2A mesh protocol\n- Federation protocol for cross-instance agent dispatch\n\n### Initial release\n- 85 passing tests\n- Apache-2.0 license\n"}, "362ae9b98a8059c0ccf927387e100a9eca03c98e": {"t": 1785748182.9426515, "n": 70188, "h": "6a99c44d38615083", "c": "# Changelog\n\nAll notable changes to aither-adk will be documented in this file.\n\n## [2.17.1] - 2026-07-05\n\n### Fully self-service mesh join — no customer key handling\n\n- **Auto-join headscale off the onboard response.** The conductor now auto-issues a\n headscale pre-auth key for NAT'd nodes and returns it in the `/v1/mesh/onboard`\n response; `mesh.join` consumes it and brings up the tunnel automatically. A customer\n runs one command — they never mint, fetch, or set a headscale key. Explicit\n `--headscale-key`/`AITHER_HEADSCALE_AUTH_KEY` still take precedence.\n- **Security:** `_tailscale_up` now redacts the auth key from all logs and exceptions\n (a failed `tailscale up` previously echoed the full argv, leaking the key into logs).\n\n## [2.17.0] - 2026-07-05\n\n### Added — Headscale mesh transport + local Qdrant + AitherConnect onboarding\n\n- **Headscale NAT-friendly mesh transport.** When raw WireGuard UDP:51820 is not\n viable (customer boxes behind NAT, corporate firewalls, CGNAT), `adk mesh join\n --headscale` routes the mesh tunnel through Tailscale's Headscale control plane.\n The overlay IP is still Conductor-assigned (10.77.0.0/16); Headscale provides\n only the transport layer. Configured via `AITHER_MESH_TRANSPORT=headscale`,\n `AITHER_HEADSCALE_URL` (default: https://headscale.aitherium.com), and\n `AITHER_HEADSCALE_AUTH_KEY` (pre-generated key). Automatic fallback to raw\n WireGuard if Headscale setup fails.\n- **Local Qdrant vector DB provisioning.** `adk stack` now provisions Qdrant\n locally when `AITHER_VECTOR_DB=qdrant` is set, enabling offline-first RAG\n without external dependencies. Tiered: local Qdrant in-fleet, cloud Nexus\n for enterprise multi-tenant deployments.\n- **Conductor URL public fallback.** `adk mesh join` resolves the internal\n `aitheros-conductor:8193` hostname; if unreachable (e.g., self-hosted nodes),\n it falls back to the public `conductor.aitherium.com:8193` endpoint,\n eliminating bootstrap configuration friction.\n\n### Documentation\n\n- **Self-hosting runbook.** New `docs/SELF_HOSTING.md` covers deploying AitherOS\n on customer infrastructure with Qdrant + mesh + AitherConnect onboarding.\n- **Mesh transport constraints documented.** Raw WireGuard requires public\n UDP:51820 endpoint (cloud instances, dedicated hardware); Headscale for\n NAT'd networks. Noted in CLI help and module docstrings.\n\n## [2.15.0] - 2026-07-04\n\n### Added — built-in web admin console + portal-profile settings sync\n\n- **Web admin console.** The page `adk up` opens (served at `/` and `/ui`) is now a full,\n self-contained console — Chat plus six admin tabs: **Backend** (view/switch LLM provider,\n set keys, test connection), **Packs** (list/enable/disable/reload tool packs),\n **Sessions** (browse/delete conversations), **Logs** (redacted agent-log tail),\n **Graph** (knowledge-graph search/neighborhood/stats), and **MCP** (add/remove external\n MCP servers). No build step, no CDN — a single packaged HTML asset. The original minimal\n streaming chat page stays at `/chat` for back-compat.\n- **Admin API (`adk/admin_api.py`).** ~20 endpoints under `/admin/*`, all bearer-gated by the\n existing middleware (never in `_skip_auth_paths`). Operates on the live agent — LLM backend\n swap (`LLMRouter.switch_backend`), pack reload, and MCP registration take effect without a\n restart.\n- **Settings sync (`adk/settings_sync.py`).** The user's portal profile is the source of\n truth: on startup the agent pulls `preferences.adk` and applies it over the local\n `~/.aither/config.yaml` cache; admin-console edits push a debounced snapshot back to\n `PUT /api/settings/preferences`. Provider API keys and MCP auth headers are device-local\n secrets and are never included in the pushed snapshot. Opt out with `AITHER_SETTINGS_SYNC=false`.\n\n### Security\n\n- **Timing-safe token comparison.** The server bearer check now uses `hmac.compare_digest`\n (the token is reachable over the public tunnel; a byte-wise `!=` leaked it to timing attacks).\n- **MCP add is SSRF-guarded + confirmed.** Adding an external MCP server is a two-step\n prepare→confirm flow; URLs resolving to loopback / private / link-local / metadata addresses\n are rejected (override with `AITHER_ALLOW_PRIVATE_MCP=1` for trusted local servers).\n- **Config editor is allowlisted.** `PATCH /admin/config` writes only safe scalar prefs and\n hard-denies fields that could execute code or redirect traffic at startup (e.g.\n `aither_toolpack_dirs`, backend base URLs).\n- **Log tail is scrubbed.** Bearer tokens, `#k=` fragments, and `sk-`/`aither_sk_` keys are\n redacted from the log-tail endpoint; the tunnel log is excluded.\n\n## [2.14.8] - 2026-07-04\n\n### Added — `adk up` for normal humans (no API key, no prereqs, live chat)\n\n- **Hosted-brain default — no API key required.** When there's no local model and no\n provider key, `adk up` signs in (device-flow) and routes inference through the gateway\n using the portal token (`AITHER_LLM_BACKEND=gateway` + bearer). A non-technical user\n never needs to obtain or paste an LLM API key. Explicit `--provider`/local model still\n take precedence; unattended `--yes` with no token/backend fails cleanly (exit 3).\n- **cloudflared auto-download.** If the tunnel binary is missing, `adk up` downloads the\n right platform asset to `~/.aither/bin` (SHA256-verified against the release; opt out with\n `AITHER_CLOUDFLARED_REQUIRE_CHECKSUM=0`) instead of exiting with an install hint.\n- **Built-in streaming chat page.** The agent now serves a self-contained chat UI at `/`\n (and `/chat`) with a live \"thinking…\" indicator and token-by-token streaming — so a human\n has somewhere to talk to it with immediate feedback instead of a blank wait. `adk up` opens\n it locally and passes the callback bearer in the URL fragment (`#k=…`), so `/chat/stream`\n stays authenticated (not an open proxy) with nothing to paste.\n- **Human-readable failures.** `adk up` errors now carry a plain-English `next_action`\n (kept machine-readable in `--yes` JSON).\n\n### Fixed\n\n- `adk up` chat link pointed at a non-existent `/settings/agent` route; it now points at the\n agent's own streaming chat page.\n\n## [2.14.7] - 2026-07-04\n\n### Added\n\n- **`adk up` — one command to run a persistent, fleet-connected agent.** Collapses\n the scattered onboarding surface (`host`/`quickstart`/`connect`/`login`/`setup`)\n into a single command that starts `aither-serve` (single agent, identity `aither`\n by default), opens a Cloudflare quick-tunnel for fleet reachability, registers with\n the portal, and installs a reboot-persistent autostart entry (Windows Task Scheduler\n / systemd-user / launchd). Detaches by default so the terminal is freed; `--foreground`\n blocks. Reuses the existing device-flow login, cloudflared discovery, provider-key\n store, and `_preflight_check` backend detection.\n- **Fully non-interactive path for autonomous agents.** `adk up --yes` (or any\n non-TTY invocation) takes ZERO prompts — everything resolves from flags, env, and\n saved config — and emits a single machine-readable JSON status line with meaningful\n exit codes (`0` ok, `2` bad-input, `3` no-backend, `4` tunnel-missing, `5` register-failed).\n Degrades to local-only (warn, not fail) when a portal token is absent unless\n `--require-register` is set.\n- **`adk down` / `adk status --json` companions.** `down` stops the agent + tunnel,\n best-effort deregisters, and removes the autostart entry; `status` reports the running\n agent (liveness + `/health` + tunnel + registration), with `--json` for agents/CI.\n- **`adk stack`** — the previous `adk up` behaviour (supervise the Room + Ollama consumer\n stack) now lives here; `adk up` is the connected-agent command.\n\n### Internal\n\n- New `adk/agent_daemon.py`: detached process spawn, status file\n (`~/.aither/adk-up.json`), pid liveness/teardown, and cross-platform autostart install.\n\n## [2.14.6] - 2026-07-04\n\n### Added\n\n- **`adk.preflight` — self-bootstrap capability probe.** `run_preflight(agent,\n spec)` wakes an agent up knowing what it is actually plugged into: a bounded\n (1.5s) parallel `LivenessProbe` over the router's resolved backends\n (primary/reasoning), the embeddings winner, remote MCP tools, the structured-ML\n / `/ml/teach` endpoints, and voice — returning an honest `CapabilityReport`.\n Status vocabulary distinguishes reachability from entitlement\n (`OK`/`MISSING`/`UNREACHABLE`/`AUTH`/`TIER_DENIED`/`UNSUPPORTED`), a hosted slot\n never prints a bare `OK`, and a required-but-unsatisfied slot aborts before the\n loop instead of blind-firing. Headless task/rules resolution + an allowed-roots\n policy binding (`builtin_tools.set_allowed_roots`, which resets the memoized\n cache so the override actually enforces).\n- **Multimodal (image) messages.** `Message.content` now accepts an OpenAI-style\n content-part list (`str | list[dict]`), passed through verbatim to the\n OpenAI-compatible body (gateway / vLLM / OpenAI). New `adk.llm.multimodal`\n helpers — `image_message()`, `image_content_parts()`, `to_image_url()` — build\n the parts and encode local image bytes/paths into `data:` URIs.\n- **Real vision preflight probe.** The vision slot is no longer unconditionally\n `UNSUPPORTED`: it sends a discriminable image (a solid red square) and requires\n the model to name the color, so a blind/text-only model cannot false-pass. `OK`\n only on a real round-trip; `AUTH` on 401/403; `UNSUPPORTED` when there is no\n OpenAI-compatible provider or the model cannot see the image.\n\n## [2.14.5] - 2026-07-04\n\n### Added\n\n- **`adk.reasoning.mcts` — generic Monte-Carlo Tree Search library.** A reusable\n MCTS engine (ported from the internal `UnifiedMCTS`, with all internal couplings\n removed) that any agent can drive over its own environment:\n - `MCTSEnvironment` protocol (`get_state_hash` / `get_actions` / `step` /\n `evaluate` / `clone`) — implement it and search.\n - Three **optional, default-off** model seams — `TransitionModel`, `PolicyModel`\n (PUCT priors), `ValueModel` — so a learned world-model / policy / value can be\n injected without touching the core; with all three `None` the engine is a plain\n UCT search.\n - `ObservedTransitionModel` adapter — a cheap online lookup world-model (hit =\n exact stored transition; miss = identity + negative \"unknown\" reward +\n `is_uncertain`, biasing exploration toward learning), with JSONL save/load for\n cross-run reuse.\n - `UnifiedMCTS(config).search(env)` returns an `MCTSResult` (best action, value,\n confidence, principal-variation path) and an optional `MCTSTrace`\n (visit-distribution policy target) for distillation.\n- **Self-bootstrapping agent machinery.** Build an agent from a declarative spec\n instead of bespoke wiring:\n - `adk.bootstrap.build_agent_from_spec(path) -> (AitherAgent, RunCtx)` — resolves\n prompts, activates required/optional tool packs, and carries a `memory_map`\n (a `node_type -> (role, tier)` override for memory writes, never a gate).\n - `adk.prompts.PromptBridge` — resolves an agent's prompt bindings from files\n (relative to the spec) or `AitherPrompts` dot-keys (optional import).\n - `adk.packs.PackActivator` — eager activation of installed tool packs by name\n over `register_tool_packs`; a required pack that registers zero tools raises\n `PackUnavailable`, optional packs soft-degrade.\n - `python -m adk.bootstrap.generic_agent <spec.yaml>` inspects a spec (resolved\n name, activated packs + tool counts, prompt keys, memory map) without running a\n live loop.\n- **`deep_research` tool pack** — a full multi-engine web-research workflow as\n `dr_*` agent tools (keyless search, page fetch + cache, site mirror, a citable\n findings knowledge graph, and markdown/PDF/DOCX report generation), activatable\n by name via the tool-pack loader.\n\n## [2.14.4] - 2026-07-04\n\n### Added\n\n- **`structured_ml` tool domain** — zero-shot structured-data inference for agents,\n backed by TabFM (tabular classification/regression) and TimesFM (time-series\n forecasting). Three tools:\n - `tabular_classify(support_rows, target, query_rows)` — classify rows from a\n labeled support set, in-context (no training step; up to 10 classes).\n - `tabular_regress(support_rows, target, query_rows)` — predict a numeric target.\n - `timeseries_forecast(series, horizon)` — forecast future values of a series.\n\n The tools POST to a structured-ML inference service resolved from\n `AITHER_STRUCTURED_ML_URL` (default `http://localhost:8192`), reject non-http(s)\n URLs, and summarise oversized responses to protect the agent's context.\n- **`Capability.STRUCTURED_INFERENCE`** capability token and a new **`analyst`**\n identity/pack that ships the `structured_ml` domain by default. The domain is\n opt-in — only identities that enable the `structured_ml` category get the tools.\n\n## [2.14.3] - 2026-07-03\n\n### Fixed\n\nAll four bugs below were found by actually booting `adk run` against a real\nchat model and driving a real tool-calling request end-to-end (not inferred\nfrom reading the code) — each one silently broke the \"local agent uses its\nconfigured model and its tools actually work\" path.\n\n- **`AITHER_MODEL` / `config.model` silently ignored by the OpenAI-compatible\n backend**: `LLMRouter.__init__` never read `config.model` into `self._model`\n unless the caller passed `model=` directly to the constructor — which nothing\n did. Every explicit model choice (env var, config file) was dropped, the\n provider fell back to a hardcoded per-backend default (`gpt-4o-mini` for the\n `openai` backend), and an unrecognized model name got silently remapped\n server-side to the wrong backing model. Concretely: setting\n `AITHER_MODEL=aither-orchestrator` had no effect — every request actually\n went to a different model that was never meant to be called directly and\n can't reliably use tools.\n- **`OPENAI_BASE_URL` ignored for explicit `--backend`/`AITHER_LLM_BACKEND`\n selection**: `LLMRouter._auto_detect()`'s explicit-backend branch hardcoded\n `base_url=None` when constructing the provider, instead of reading\n `config.openai_base_url` (which two other call sites in the same file\n already did correctly). Any self-hosted OpenAI-compatible endpoint\n (MicroScheduler, vLLM, LM Studio) configured via `OPENAI_BASE_URL` was\n silently ignored in favor of `https://api.openai.com/v1`.\n- **Tool-call steering never engaged on default-effort chat**: the retry that\n nudges a model back into structured tool-calling when it responds with prose\n instead of a function call was gated behind `effort >= 6`. Default `effort`\n for a plain `agent.chat(message)` call resolves to `5`, so the safety net —\n built specifically for this failure mode — never fired for default requests.\n Also now forces `tool_choice=\"required\"` on the retry instead of relying on a\n text nudge alone.\n- **Every non-string builtin-tool parameter silently typed as `\"string\"` in\n the generated tool schema**: `builtin_tools.py` uses\n `from __future__ import annotations`, so `_extract_parameters()` in\n `tools.py` was reading postponed (string) type hints (`\"int\"`, not `int`)\n straight out of `fn.__annotations__`, which never matched\n `_type_to_schema`'s type-object keys and silently fell through to\n `{\"type\": \"string\"}`. Every builtin tool with an `int`/`float`/`bool`/`list`\n parameter was affected — e.g. `file_read(start_line: int, end_line: int)`\n advertised both as strings, so a model correctly calling the tool with\n `\"start_line\": \"0\"` crashed the actual implementation with\n `unsupported operand type(s) for -: 'str' and 'int'`. Fixed by resolving\n hints through `typing.get_type_hints()` instead of the raw annotations dict.\n `_coerce_arguments()` — the runtime safety net that's supposed to coerce a\n model-supplied `\"0\"` string into `0` before a tool call executes — had the\n exact same raw-`__annotations__` bug and silently no-opped for every builtin\n tool; fixed the same way.\n- **Streaming tool-loop could hang a connection forever**: `chat_stream()`'s\n fallback to the sync tool loop (tool calls can't stream token-by-token) had\n no timeout, so a model that didn't cleanly terminate a tool-calling round\n trip left the SSE connection open indefinitely with zero bytes sent. Bounded\n with a 60s `asyncio.wait_for`; on timeout the stream yields a clear message\n instead of hanging.\n\n## [2.14.2] - 2026-07-03\n\n### Fixed\n- **AitherShell session amnesia**: the REPL generated a fresh `session_id` for\n EVERY message, so conversation history never followed across turns (the\n server stored each turn under a different session). One stable id is now\n minted per shell run; `/resume` and `/new` semantics unchanged, and the\n cross-restart auto-restore of the last session now actually works.\n\n## [2.14.1] - 2026-07-02\n\n### Added\n- **Account-tied licensing via device-flow login** — `adk login` now persists the account\n license returned by the device-flow handshake to `~/.aither/license.json` (base64\n `license_key` → `{payload, signature}` envelope), so entitlements from the authenticated\n account carry into every later `adk` invocation without re-auth. Pairs with the fleet-side\n node tool-pack gate that grants packs by plan-tier rank.\n- **`can_use_formbridge` / `can_use_untether` entitlements** (PROFESSIONAL+ rank ≥ 3) backing\n the FormBridge and UNTETHER agent tool-packs.\n\n### Fixed\n- `adk` CLI: guard the optional `memory_graph` singleton (was a latent `NameError` on the\n `/stats`, `/memory`, and save paths when memory was not constructed).\n\n## [2.14.0] - 2026-07-02\n\n### Added\n- **`adk.memory_wiki` — LyraWiki-style self-managed semantic knowledge** (opt-in; nothing constructs it by default): `MemoryWiki(graph_memory)` maintains curated wiki ARTICLES as `wiki_article` nodes (title/slug, LLM-owned markdown with `[[wikilinks]]`, embedding, durable tier, `CONSOLIDATED_FROM` edges to source memories, governance-ledgered revisions).\n - `consolidate(llm, since=, budget=)` clusters unconsolidated episodic/fact nodes (embedding + keyword), drafts/UPDATEs articles via the INJECTED llm callable (`str -> str` or `messages -> str`, sync or async — no hard model dependency), cites source node ids, marks contradicted sources superseded, then DEMOTES consolidated sources to a fast-decay tier via `promote()` (represented knowledge doesn't need raw copies).\n - `lint(llm=None)` — deterministic health checks (orphan wikilinks, empty/stale articles, live-but-contradicted pairs) + an optional LLM audit pass.\n - `prune(relevance_floor=0.05, hard_delete_after_days=14)` — article relevance = tier freshness × reinforcement (reinforce-on-recall) + link-degree to LIVE memories; below the floor → reversible governance tombstone; entombed past the window → **HARD-DELETE** (the tombstone snapshot itself is purged — content and embedding irrecoverable; the true-deletion path). A node is never deleted without a tombstone first.\n - `recall(query, limit)` — article-first RAG (curated articles rank above raw nodes) that reinforces returned articles.\n- **`adk.routines` — agent heartbeat + self-programmed routines**: `RoutineStore` is a durable registry (`~/.aither/routines/{agent}.json`, `AITHER_DATA_DIR`-aware, injectable path) of cron-scheduled self-prompts `{name, cron, instruction, enabled, last_run, last_result, tags}` that rehydrates into the existing `adk.cron.CronScheduler` on `start()`. A fire runs `agent.chat(instruction)` with the agent's full toolset — the agent programs its future self with its own adk. Guardrails: `max_routines` (12), a 5-minute min fire interval per routine, a per-fire timeout, and every result ledgered (truncated) to `last_result`. Direct-callable routines (`register_direct`) fire a bound method instead of a self-prompt. Self-management tools (`routine_create/list/update/pause/resume/delete/run_now`) ship as OpenAI tool defs + handlers (`build_routine_tools` / `register_routine_tools` / `routine_tool_defs`); the handlers ONLY touch the RoutineStore — they can never modify agent config or safety-relevant settings (the leash principle).\n- **`AitherAgent(routines=True, memory_maintenance=True)`** (both default **False** → the default agent is byte-identical): `routines=True` builds the RoutineStore + scheduler, registers the self-management tools, and starts the heartbeat lazily on the first `chat()` (or via explicit `start_routines()`). `memory_maintenance=True` registers DEFAULT routines — `wiki_consolidate` (every 2 h), `wiki_lint` / `wiki_prune` / `graph_sweep` (daily) — as DIRECT method fires so memory upkeep runs even on tiny models, while staying visible/manageable via `routine_list`.\n- `TombstoneStore.purge(tombstone_id)` — the hard-delete endpoint: irrecoverably removes a tombstone snapshot and rewrites the persisted store without it (consumed by `memory_wiki.prune`).\n\n## [2.13.6] - 2026-07-02\n\n### Added\n- **Qdrant backend for GraphMemory dataplane sync — TRUE two-way, verified live.** Set `AITHER_FLEET_QDRANT_URL` and the sync uses Qdrant (`upsert` + `scroll` + filtered `search`) instead of Nexus. This is what actually delivers cross-agent memory sharing: a node is upserted with its OWN embedding under a deterministic per-(tenant,node) point id (idempotent re-push), and `fleet_pull` reliably SCROLLS a tenant's points back — the enumerate Nexus lacks (its `/search` doesn't return ingested docs and `/export` needs lancedb, which has a hard dependency conflict in the fleet). **Proven live with 2 agents:** agent-1 ingests → auto-syncs to the tenant dataplane; a fresh agent-2 in the same tenant `fleet_pull`s all of agent-1's nodes and can search them; a different tenant pulls nothing (isolation). Dimension-safe (never mixes vector dims in a collection). Local SQLite stays source of truth; best-effort, never raises.\n\n## [2.13.5] - 2026-07-02\n\n### Fixed\n- **CRITICAL — GraphMemory dataplane sync push was 100% failing.** `_fleet_push_node` sent `source_type=\"graph\"` / `content_type=\"graph_node\"`, which AitherNexus rejects as invalid enums (HTTP 500) — so NO graph node ever replicated in 2.13.4. Now sends the accepted `source_type=\"manual\"` / `content_type=\"text\"` (graph provenance is carried in `metadata.synced_from`). **Verified live** against a fleet Nexus: a 4-node ingest replicates with `pending==0` and the collection count reflects it.\n\n### Added\n- **Swarm-awareness notification** — on a successful sync batch, `GraphMemory` emits a best-effort `graph.synced` event (tenant/workspace/agent/collection/count) to `AITHER_FLEET_EVENTS_URL` so OTHER agents in the tenant/swarm can pull the fresh data and deconflict in-flight work (push-based awareness instead of polling). No-op unless the env is set.\n\n### Known limitation\n- Cross-agent **rehydration** (`fleet_pull`) is only reliable when the fleet Nexus is backed by lancedb (persistent + exportable). An in-memory Nexus stores on `/ingest` but neither its `/search` nor `/export` returns the docs — two-way read needs the persistent Nexus.\n\n## [2.13.4] - 2026-07-02\n\n### Added\n- **Memory-maintenance primitives on `GraphMemory`** (all opt-in; default behaviour byte-identical):\n - **Reinforce-on-recall**: `recall_with_activation(..., reinforce=True)` bumps `reinforcement_count`/`last_reinforced` on every RETURNED node's metadata mirror (one UPDATE per node, WAL-safe) — making the `MemoryRecord` reinforcement fields the activation scorer reads LIVE. Default `False` = zero writes.\n - **`sweep(now, archive_below=0.05, max_nodes=None, dry_run=False)`**: read-time decay enforced at rest — archives nodes past their tier TTL with freshness×reinforcement score below `archive_below` and `reinforcement_count <= 1`; `max_nodes` additionally archives the lowest-scored overflow. PERMANENT tier immune. Every archive is a reversible governance tombstone (`TombstoneStore.recover`) + a FORGET ledger entry; a node is never deleted without a tombstone. `dry_run` returns the would-archive list without acting.\n - **`promote(node_id, tier=?, role=?)`**: re-tier/re-role a node IN PLACE (tier/role columns + metadata mirror) — edges, embedding and ledger history survive; ledgers an UPDATE entry when governance is on.\n- `MutationType.UPDATE` ledger entry kind (in-place re-tier/re-role).\n- **Per-tenant dataplane sync for `GraphMemory`** — replicate the local graph to a per-tenant vector dataplane (Nexus/Qdrant) so a sovereign agent's memory is durable + rehydratable across restarts and hosting boundaries. `fleet_push_all_nodes()` (catch-up), `fleet_pull()` (best-effort semantic top-up), `fleet_sync_pending()`, and auto-sync on ingest (`_maybe_autosync` + `drain_sync()`). **On by default** (`AITHER_FLEET_SYNC=auto`; only `false/0/off` disables) with an inferred target (local Nexus `:8122` or the gateway in cloud mode) and a SEPARATE collection (`AITHER_FLEET_GRAPH_COLLECTION`, default `graph_memory`) from the KV memory sync. New `GraphMemory(tenant_id=, workspace_id=, fleet_url=, auto_sync=)` constructor args override env (required for multi-tenant host processes). Node `synced` column + schema v4 migration. TLS verify is CA-bundle-aware (`AITHER_CA_BUNDLE`) for the internal-TLS mesh. Local SQLite stays the source of truth; every sync path is best-effort and never raises into ingest.\n\n## [2.13.3] - 2026-07-02\n\n### Added\n- **Canonical self-deploying embeddings provider** (`adk.embeddings`): one embedding provider for the whole SDK so vectors are portable across scopes (platform / tenant / sovereign) — same model + dimension everywhere. Pinned to `nomic-embed-text` (768-d; the vLLM `--served-model-name` for nomic-embed-text-v1.5). Lazy, single-flight resolution chain: explicit `AITHER_EMBEDDINGS_URL` → local vLLM (`:8209` then `:8120`, HTTPS-then-HTTP) → local Ollama → gateway (`AITHER_GATEWAY_EMBEDDINGS_URL`) → auto-deploy a local vLLM embeddings container if a GPU + Docker are present (opt out with `AITHER_EMBED_AUTODEPLOY=0`) → CPU sentence-transformers (384-d, degraded) → feature-hash (384-d, degraded, always works). Every batch is dimension-tagged so callers never silently mix 768-d and 384-d vectors. `get_default_embedder()`, `embed_texts()`, `embed_one()`, `get_provider()`, `reset_provider()`.\n\n### Changed\n- **`GraphMemory` now defaults its embedder to the canonical provider** (opt out with `AITHER_GRAPH_EMBEDDER=legacy`), so every adk agent shares one 768-d space. Added a meta-table dimension guard: the index is pinned to its first embedding's dimension and later different-dimension vectors are refused (kills 768↔384 mixing that silently poisons cosine similarity).\n\n## [2.13.2] - 2026-07-01\n\n### Added\n- **Session persistence**: AitherShell now auto-saves the active session_id to `~/.aither/config.yaml` after each chat turn and auto-restores it on the next shell launch. Multi-turn conversations survive across restarts — the messages were always persisted in SQLite + JSON, only the session pointer was missing.\n- `/new` command to start a fresh session (clears saved session_id).\n- `/sessions` command to list recent sessions with agent name, message count, and timestamp. Resume any with `/resume <id>`.\n\n## [2.13.1] - 2026-07-01\n\n### Fixed\n- **CRITICAL:** `deploy_connect()` now sends a HEAD request to verify the GitHub release asset exists before downloading, with a clear error if the asset is missing.\n- **CRITICAL:** Compose file URLs are now pinned to a stable commit hash (configurable via `AITHER_COMPOSE_PIN` env) instead of tracking the mutable `main` branch.\n- **CRITICAL:** `deploy_adk_node()` now falls back to starting `adk-serve` natively when Docker is unavailable, instead of blocking with \"Docker not installed\".\n- **HIGH:** GHCR login failure now blocks deployment immediately with actionable guidance, instead of warning and continuing to a confusing image-pull failure.\n- **HIGH:** Health checks now stream the last 30 lines of Docker Compose logs on failure, so the user can see why a container didn't start.\n- **HIGH:** vLLM port allocation now probes for conflicts before assigning; if port 8200 is busy, workers are automatically remapped to the next free port.\n- **MEDIUM:** Compose file downloads are now cached with ETag; re-running `adk deploy node` skips re-downloading an unchanged file.\n- **MEDIUM:** DGX Spark discovery now respects `AITHER_DGX_HOSTS` env (comma-separated) for custom network topologies, instead of only checking `spark.local` and `192.168.0.33`.\n- **MEDIUM:** Cloud API keys (Anthropic, OpenAI, DeepSeek) are now validated with a lightweight probe during `adk setup` infra scan, so invalid keys surface early.\n- **MEDIUM:** `deploy_connect()` now validates the extracted extension has a `manifest.json` and warns if it's missing.\n\n## [2.13.0] - 2026-07-01\n\n### Removed\n- **`adk.faculties.memory_graph`** — the pickle-based `MemoryGraph` module is deleted. `from adk.faculties import MemoryGraph` now resolves to the canonical `adk.graph_memory.GraphMemory` for back-compat.\n- **`adk.platform.memory`** — the entire legacy memory subsystem (`MemoryManager`, `UnifiedMemorySystem`, `GameEngine`, `StoryboardEngine`, `AnchorGenerator`) is deleted. Agent memory is `adk.memory.Memory` + `adk.graph_memory.GraphMemory`.\n\n### Changed\n- `adk work` command now uses `GraphMemory` instead of the removed pickle-based faculty graph.\n- `adk.MemoryGraph` and `adk.faculties.MemoryGraph` lazy exports now resolve to `adk.graph_memory.GraphMemory`.\n- `agent.set_memory_graph()` kept for back-compat but documented as deprecated; agents already wire `GraphMemory` automatically in `__init__`.\n- Platform startup (`adk.platform.infrastructure.startup`) gracefully handles missing `MemoryManager`.\n- Platform CLI module listing and health check updated to reflect removed memory module.\n\n## [2.12.7] - 2026-07-01\n\n### Deprecated\n- `adk.faculties.MemoryGraph` — emits `DeprecationWarning` on instantiation; use `adk.graph_memory.GraphMemory` (SQLite-backed, governed, embedding-native) instead.\n- `adk.platform.memory` package — the legacy `MemoryManager`, `UnifiedMemorySystem`, `GameEngine`, and `StoryboardEngine` modules now emit `DeprecationWarning` on import; use `adk.memory.Memory` and `adk.graph_memory.GraphMemory` for new code.\n\n### Changed\n- Updated `adk.faculties` docstring and usage examples to point at the canonical memory surface.\n\n## [2.12.6] - 2026-07-01\n\n### Changed\n- Trimmed public package debt by removing grid runbook artifacts and shell/room binary placeholder directories from published artifacts.\n- Removed stale public-sync allowlist entries for deleted phase test files and added explicit public-sync guards for binary placeholder directories and grid artifacts.\n- Updated the public README grid note so it no longer links to runbook files excluded from the SDK package.\n\n## [2.12.5] - 2026-07-01\n\n### Added\n- Added best-effort fleet memory sync from local ADK memory into Nexus/Qdrant-compatible RAG stores, including batch catch-up and vector-search recall hooks.\n- Added saved config/env support for fleet memory URL, collection, and sync mode.\n\n### Changed\n- Excluded FormBridge runtime/test internals from public ADK packaging and public repo sync.\n\n## [2.12.4] - 2026-07-01\n\n### Added\n- Added a hot-reload endpoint for installed agent packs so newly applied pack tools can be picked up without restarting `aither-serve`.\n\n### Fixed\n- Fixed synchronous `ChatRelay` calls on Python 3.12 when no event loop is running; async WebSocket delivery is still scheduled when a loop exists.\n- Aligned gateway inference tier tests with the current public ladder: free, starter, pro, enterprise, platform.\n\n## [2.12.0] - 2026-06-28\n\n### Added — one-command local inference + upgrade path (click-to-run)\n- **`adk quickstart-local`** — detects hardware, picks a backend\n (Ollama / llama.cpp / vLLM), installs it, downloads a model, verifies, and\n prints next steps. Bootstraps Ollama itself via winget/brew/install.sh when\n missing (no \"go install it\" dead-ends). Default model `gemma4:e2b`.\n- **`adk backend switch <ollama|llamacpp|vllm>`** (+ `backend status`) — migrate\n the active deployment between engines; re-points config and smoke-tests.\n- **`adk install pack:<name>`** (+ `adk install list` / `adk packs`) — install\n ready-made agent packs (openclaw, hermes, claude-code) into `~/.aither/agents`.\n\n### Fixed (found via live end-to-end testing on Windows + CUDA)\n- quickstart-local reported success on a dead endpoint (smoke test was warn-only);\n a failed smoke now fails the command, with a service-readiness wait.\n- `--model` resolved to `None` (getattr default never fired) → uses the real default.\n- Smoke test: 30s→180s timeout for cold model load; accepts reasoning-model output\n (empty `content` at low token budgets is no longer a false failure).\n- `adk install pack:<name>` colon syntax (argparse rejected it before dispatch).\n- `adk backend status` health probe used a nonexistent `/health` path → 404; now\n probes `/v1/models` (uniform OpenAI-compatible liveness for all three backends).\n- Windows: removed non-ASCII glyphs from printed strings that crashed the cp1252\n console (a `✓` even made a successful install report failure).\n\n## [2.11.1] - 2026-06-14\n\n### Fixed — `adk host` / `adk login` device flow (autonomous onboarding)\nFound driving `adk host` against production; all broke real device-flow login:\n- **Device flow hit the portal FRONTEND, not AitherIdentity.** `adk login`/`adk host`\n POSTed `/auth/device/code` to portal/veil.aitherium.com, which 307-redirects API auth\n to `/login`. Added `_resolve_identity_url()` mapping the aitherium.com topology to\n `idp.aitherium.com` (the Identity API); localhost/bespoke pass through unchanged.\n- **Cloudflare 403'd urllib.** `idp.*` sits behind Cloudflare, which blocks urllib's\n default `Python-urllib/3.x` UA. Device-code + poll requests now send a real\n User-Agent + Accept (verified urllib→idp 200, was 403).\n- **`adk host` now self-authenticates via device flow** when there's no token, and\n **retries via device flow on a 401/403** (a stale saved token no longer dead-ends\n registration).\n\n## [2.11.0] - 2026-06-14\n\n### Added — attach your own MCP \"hands\" to a self-hosted agent\nA self-hosted agent now has the full brain (your model) · body (this loop) · **hands**\n(your tools) trifecta. The AitherOS platform relays the MCP servers you registered for\nyour agent in the `/stream` request body as `mcp_endpoints`, and the loop wires their\ntools into the ReAct turn:\n- **`adk/mcp_endpoint_tools.register_mcp_endpoint_tools(agent, endpoints)`** — speaks MCP\n JSON-RPC (`tools/list` to discover, `tools/call` to invoke) and registers a namespaced\n proxy tool (`{endpoint}__{tool}`) for each advertised tool. Mirrors `app_proxy_tools`.\n Best-effort: an unreachable endpoint registers nothing and never raises.\n- **`POST /stream` and `POST /chat/stream`** accept an optional `mcp_endpoints` list\n (`[{name, url, [headers]}]`); `_aitheros_stream` attaches them to the agent before the\n turn. Existing callers are unaffected (the field is optional).\n\n## [2.10.1] - 2026-06-14\n\n### Fixed — self-hosted `aither-serve` was crash-on-startup (2.10.0 regression)\nA fresh `pip install aither-adk && aither-serve --backend deepseek` could not boot. Found\nand fixed while standing up a real self-hosted DeepSeek agent end-to-end:\n- **Startup `NameError: name 'port' is not defined`** in three lifespan helpers\n (`_join_aithernet`, `_init_a2a_server`, `_start_mesh_hosting`) — they referenced a bare\n `port` no longer in scope. Now use `config.server_port`, and `main()` writes the resolved\n `--port`/`--host` back to `config` so every lifespan helper and the invoke_url see the\n actual bound port.\n- **Fleet self-registration crashed the whole server** — the registration handlers caught\n `(ImportError, RuntimeError, OSError, ConnectionError)` but **not** `httpx.HTTPError`, so a\n `RemoteProtocolError`/connect error talking to the control plane took the agent down. All\n network-facing lifespan handlers now also catch `httpx.HTTPError` (non-fatal, as intended).\n- **`--backend` was ignored when `AITHER_API_KEY` was set** — `_auto_detect` picked the cloud\n gateway before the explicit backend, and `_try_elysium_fallback` then replaced the chosen\n provider. An explicit backend (deepseek/openai/anthropic/your own vllm/ollama) now always\n wins; \"auto\"/\"gateway\" still flow through detection. The operator's own brain is never\n silently hijacked.\n- **Approved-tool trace truncated after `/sessions/{id}/confirm`** — the SSE relay treated\n `AgentResponse.tool_calls_made` (a `list[str]`) as dicts (`tc.get(...)`), raised\n `AttributeError`, and an over-narrow `except` swallowed it so the client saw only\n `session_start` + heartbeats. The relay now handles string tool names, the `except` is\n broad (always emits a typed `error` + terminal `complete`), and the module-level\n `_strata_ingest` `NameError` + Chronicle `session_id` kwarg mismatch are fixed.\n\n## [2.10.0] - 2026-06-13\n\n### Added — human-in-the-loop tool approval (`adk/approval.py`)\n- **Pause before a gated tool, resume on decision.** An agent can now pause its turn before\n executing a sensitive tool and wait for a human's allow/deny — so a managed control plane\n (or any operator) can gate tool use without forking the loop.\n- **Policy via `AITHER_TOOL_APPROVAL`** — a comma-separated list of tool names (optionally\n `agent:tool`), or `*` for every tool. Empty = no gating (unchanged behavior).\n- **Survives restarts / days-later approvals.** The paused turn (user message + pending tool\n calls) is persisted to disk keyed by `session_id`; resume is a fresh request, not a held\n connection. Decisions are recorded per `(session_id, tool_name)`.\n- **`AgentResponse.requires_action` / `.pending`** — set when a turn paused; `pending` lists\n the gated tool calls awaiting a decision.\n- **`AitherAgent.resume(session_id, decisions)`** — records the decisions and re-runs the\n paused turn (the gate consumes them: allowed tools execute, denied tools get a denial\n observation, the turn continues — and may pause again on a different gated tool).\n- **Server:** `POST /sessions/{id}/confirm` records decisions and streams the resumed turn;\n `/stream` emits a `requires_action` event when a turn pauses.\n\n## [2.9.0] - 2026-06-13\n\n### Added — universal LLM continuation primitive (`adk/llm/continuation.py`)\n- **`run_continuation()` + `stitch()`** — one shared continue-until-complete primitive. When a\n completion stops because it hit the output token cap (`finish_reason == \"length\"`), it continues\n the generation and STITCHES the chunks (overlap-dedup + restart-detect) into a complete answer,\n instead of every call-site hand-rolling its own retry. Bounded three ways (max rounds AND total\n output ceiling AND a no-growth break), and it never mutates the caller's message history.\n- **`LLMProvider.chat_with_continuation()`** and **`LLMRouter.chat_with_continuation()`** — every\n provider AND the router inherit the primitive, so any call-site gets continuation for free.\n- Kill-switch `ADK_LLM_CONTINUATION=off`; tunables `ADK_LLM_MAX_CONTINUATIONS`,\n `ADK_LLM_MAX_TOTAL_OUTPUT_TOKENS` (defaults: 2 rounds / 8192 tokens).\n\n### Changed\n- The ReAct loop's truncation recovery now uses the shared primitive. The previous inline\n doubling-retry REPLACED the partial response (losing earlier text in `resp.content`) and injected\n continuation scaffolding into the live message history; it now STITCHES and leaves history clean.\n Tool-call turns are never continued as text.\n\n### Included — previously-committed-but-unreleased changes\n- 2.8.2: TLS verification on by default (`verify=False` removed).\n- 2.8.1: baked license + pack-signing public keys.\n- 2.8.0: version-bump release.\n\n## [2.7.0] - 2026-06-05\n\n### Added — provider-agnostic LLM cost levers (at the `LLMProvider` ABC)\n- **Prompt caching.** New `ProviderCapabilities` (`prompt_cache: explicit|automatic|none`, `batch`) and\n normalized `LLMResponse.cache_read_tokens`/`cache_write_tokens`, so the \"tokens saved\" meter never\n depends on which backend served the turn. Anthropic inserts `cache_control` breakpoints over the stable\n system+tools prefix; OpenAI/DeepSeek caching is automatic and their `cached_tokens` are now surfaced\n (previously discarded). Default-on in `AitherAgent.chat`; override with `cache=False`.\n- **ResponseCache** (`adk/llm/cache.py`) — exact-match response cache with a pluggable backend, wired into\n `LLMRouter` as an opt-in (`LLMRouter(response_cache=…)` + per-call `cacheable=True`). OFF by default — no\n determinism change unless opted in.\n- **Gemini provider** (`adk/llm/gemini.py`) with explicit `cachedContents` caching; registered in the router.\n- **Async batch runner** (`adk/llm/batch_runner.py`) — `run_batch()` over the Anthropic Message Batches /\n OpenAI Batch APIs (~50% off) with a transparent fallback to concurrent `chat()` where unsupported.\n- **Graph-RAG retriever interface** (`adk/graph_retriever.py`).\n\n### Added — principal authority (prompt-injection defense enforced outside the model)\n- `adk/auth.py`: `Principal` / `AuthContext` / `PrincipalResolver` (+ deny-all default).\n- `ToolRegistry.execute(name, args, auth=…)` — a default-deny authorization gate at the tool boundary, so a\n forbidden tool call is blocked regardless of what the LLM emits. `ToolDef` gains `required_clearance` /\n `action_class`. Threaded through `AitherAgent.chat`; `auth=None` is fully backward-compatible.\n- `adk/channel_auth.py`: Telegram webhook-secret + CEO-id checks and HMAC-signed approvals.\n\n## [2.6.4] - 2026-06-05\n\n### Fixes\n- **Loop-guard nudges corrupted the message history → DeepSeek 400 mid-run.**\n When the ReAct loop emitted multiple tool calls in one assistant turn and the\n loop guard fired a WARN/BLOCK/CIRCUIT_BREAK on one of them, it spliced a\n standalone `system` nudge message *between* the tool results. DeepSeek (and\n strict vLLM chat templates) require an assistant `tool_calls` message to be\n followed by exactly one `tool` result per `tool_call_id`, contiguously — so the\n interleaved message produced `400 … insufficient tool messages following\n tool_calls message` and aborted the turn (this is reliably hit after crossing\n the 15-call circuit-break threshold, where every subsequent call WARNs).\n Per-call guidance is now folded into that call's own `tool` result, and any\n standalone steering is deferred to a single message AFTER the whole batch —\n preserving the `tool_calls`↔`tool_results` pairing every backend requires.\n Regression test: `deep-research-agent/tests/test_msg_structure.py`\n (loopguard-flood scenario).\n\n## [2.6.3] - 2026-06-04\n\n### Fixes\n- **Transient provider errors killed a run on the first blip.** A single\n `502 Bad Gateway` / `503` / `529 overloaded` / `429` from the upstream API\n aborted the whole request — the Anthropic provider had **no retry at all**, and\n the OpenAI-compatible provider only retried `429`. Both now retry transient\n statuses (`408/429/500/502/503/504/529`) with `Retry-After`-aware exponential\n backoff (3 attempts), and surface the provider's body if all retries are\n exhausted. Non-transient `4xx` (e.g. `400`) still fails fast. Applies to\n `chat` and `chat_stream` on both providers.\n\n## [2.6.2] - 2026-06-04\n\n### Fixes\n- **DeepSeek (and strict OpenAI-compatible backends) 400'd on long tool\n conversations.** The ReAct loop legitimately injects mid-conversation\n `system` steering messages (the \"[DIMINISHING RETURNS]\" hint after several\n low-output tool iterations, loop-guard nudges). OpenAI tolerates a non-leading\n `system` message; DeepSeek rejects it with a 400. Long research runs\n (many `fetch_url` calls) reliably tripped this. Fix: non-leading `system`\n messages are demoted to `user` at the OpenAI-compatible provider boundary\n (`_demote_nonleading_system`) — steering intent preserved, payload valid on\n every backend; leading system prompt(s) untouched.\n- **Opaque HTTP errors.** `resp.raise_for_status()` discarded the provider's\n response body, so a 400/422 surfaced as a bare `Client error '400 Bad\n Request'` with no cause. `_ensure_ok` now includes the provider's actual\n message (e.g. `context_length_exceeded`, `Invalid 'messages'`) in the raised\n error, for both `chat` and `chat_stream`.\n\n## [2.6.1] - 2026-06-04\n\n### Fixes\n- **`MCPServer.mount()` returned HTTP 422 for every `/mcp` call.** Under\n `from __future__ import annotations` the route handler's `request: Request`\n annotation is a string that FastAPI resolves against the *module* globals, but\n `Request` was imported locally inside `mount()` — so FastAPI couldn't resolve\n it and treated `request` as a required query param. The MCP server endpoint\n (`initialize`, `tools/list`, `tools/call`) was unusable when mounted. FastAPI\n symbols are now imported at module level (guarded; the `[node]` extra still\n gates actual use). Any agent's tools can now be exposed as a working MCP\n server with `MCPServer(...).mount(app)`.\n\n## [2.6.0] - 2026-06-04\n\n### Streaming + scaffolding\n- **`AitherAgent.stream_chat`** — reliable streaming sibling of `chat()`: native\n tool-calling loop (concludes on its own, unlike the text-protocol `stream_react`)\n that emits live `tool`/`tool_result` events and streams the answer as `token`\n events. `token_delay` paces tokens for a typing effect.\n- **`adk new <template>`** — scaffold a full, runnable template app. Ships the\n `deep-research` template (a cited web-research agent: pack + server + web UI):\n `adk new deep-research`.\n\n### Fixes\n- **BYO-key no longer community-capped.** The free-tier monthly token cap now applies\n only to the metered Aitherium gateway backend, never to a user's own provider key\n (Anthropic/OpenAI/DeepSeek/Ollama/vLLM). Previously it could brick a self-hosted\n agent after ~100k tokens on the user's own key.\n- **Stronger, grounded memory recall.** `chat()` now injects the top-6 knowledge-graph\n hits (untruncated) with a hard no-fabrication instruction — was top-3 truncated to\n 200 chars, which surfaced partial facts and let weaker models invent the rest.\n- **Tool-arg coercion.** `ToolRegistry.execute` coerces LLM-supplied args toward each\n parameter's annotated type, so a tool no longer crashes when a model passes a list\n or string where an int/float/bool was expected.\n\n### Docs\n- New `docs/AGENT_DEV_GUIDE.md` — the golden path for building agents/packs.\n\n## [2.5.0] - 2026-06-04\n\n### Continual learning — agents that learn from runs and reuse memory\n\nAgents now improve the second time they do a task instead of starting from scratch.\n\n### Added\n- **Recall-before**: `AitherAgent.chat()` searches the local `SkillStore` and injects the\n top matching learned skills as a system block, so the model reuses proven procedures.\n- **Learn-after**: `_learn_after()` runs after each response — reinforces (`success_count++`)\n any skills that were recalled+reused, and extracts + saves a NEW skill from successful\n multi-tool runs via `SkillExtractor`. Wired at both `chat()` return paths.\n- Gated by `AITHER_SKILLS` (default on). The previously-unused `SkillStore`/`SkillExtractor`\n are now connected to the run loop.\n\n### Notes\n- Pairs with the platform-side LearnedProcedure substrate (recipes → scored procedures →\n skills/tools/A2A/packs → MCTS priors → evolution). The SDK shares memory via the Spirit\n bridge when configured.\n\n## [2.0.0] - 2026-06-02\n\n### Open-core boundary (BREAKING)\n\nThis release draws a single, enforced free/paid line. The free tier stays\ngenuinely useful (real agent, typed + graph + code memory, ReAct, ~essential\ntools); paid tiers unlock the *scale* capabilities via portal.aitherium.com.\n\n### Added\n- **`adk/licensing.py`** — the entitlement keystone. Resolves a tier\n (COMMUNITY by default), verifies optional portal-signed `~/.aither/license.json`\n (Ed25519), and answers `can_use_fleet/channels/cron/swarm/auto_neurons`,\n `is_agent_licensed`, `max_effort`, etc. **Fail-closed**: no/invalid/expired\n license → free tier; an unsigned license never grants premium.\n `AITHER_TENANT_SLUG=aitherium` → unrestricted INTERNAL tier.\n- **Moat guard** (`scripts/check_moat_boundary.py`) wired into the publish\n workflow — blocks any wheel that leaks the moat or drops the keystone.\n- Tests: `tests/test_licensing.py`, `tests/test_moat_boundary.py`.\n\n### Changed (gating — fail-closed)\n- Free tier effort is capped at 3 (reasoning effort 7–10 needs Professional).\n- Monthly token budget hard-block on the free tier (no silent overspend).\n- Gated behind a paid tier: fleet mode (`FleetConfig`), cross-agent delegation\n (`AgentForge.delegate`), channel adapters, cron scheduler, proactive\n auto-neurons, and `swarm`/`swarm_code` dispatch.\n\n### Removed\n- **`adk/nanogpt.py`** (on-device training IP) removed from the published SDK\n and the public repo; relocated to the internal AitherOS moat. Excluded from\n the wheel as defense-in-depth.\n- Pre-2.0 releases shipped this IP and ungated capabilities and are being\n yanked (PyPI) / removed (GitHub releases). See `scripts/yank_leaky_releases.py`\n and `scripts/purge_public_leaks.sh`.\n\n## [1.20.0] - 2026-05-27\n\n### Fixed\n- **Pack discovery path mismatch** — `_find_local_packs()` now scans both\n `~/.aither/packs/` and `~/.aitheros/packs/`, so packs installed via\n `adk pack install` are discovered by `adk-workspace`.\n\n### Added\n- **Auto-register on pack install** — `adk pack install` now auto-registers\n with the portal if the pack contains an `agent.yaml` with a `portal` section.\n One command does download + extract + register.\n- **Flux event on agent registration** — `developer_portal.py` emits\n `agent.registered` Flux event so the portal fleet UI updates in real-time.\n\n## [1.15.0] - 2026-05-26\n\n### Added\n- **Cloud memory** — `adk quickstart --cloud` now tests gateway memory and\n auto-configures Spirit URL so memories persist across devices via\n `gateway.aitherium.com/v1/memory/teach` and `/v1/memory/recall`.\n- **Config→env export for cloud mode** — `Config.from_env()` now exports\n `AITHER_CLOUD_MODE`, `AITHER_SPIRIT_URL`, and spirit path env vars from\n saved config so Memory and other modules pick them up without needing\n explicit env vars set by the user.\n- **Cloud Quick Start section** in README — 3-command setup for users who\n just want a Claude/GPT-4/DeepSeek API key and the full agent harness.\n- **`--memory` profile for node deployment** — `adk deploy node --memory`\n adds Spirit (8087) + WorkingMemory (8101) containers for persistent\n vector memory. Without this, agents use local SQLite only.\n- **Configurable vLLM in node compose** — `AITHER_VLLM_MODEL`,\n `AITHER_VLLM_GPU_UTIL`, `AITHER_VLLM_CTX_LEN`, `AITHER_VLLM_QUANT_ARGS`\n env vars let `adk setup` write GPU-detected tier settings to `.env`\n and have the node compose pick them up (TQ4 for small GPUs, BNB for large).\n\n### Fixed\n- **Memory cloud routing** — `Memory._spirit_teach()` and `_spirit_recall()`\n now use configurable paths and auth headers for gateway proxy. Previously\n cloud-only users silently lost all Spirit memory calls.\n\n## [1.9.0] - 2026-05-24\n\n### Added\n- **Composable ToolPack system** — agents can load tool packs from\n `.toolpack.yaml` directories. Drop a pack in `~/.aitheros/packs/` or\n set `AITHER_PACKS_DIR`, and any agent discovers it automatically.\n- **`tools.packs` in agent.yaml** — list tool pack IDs to auto-load\n (e.g. `packs: [crm-tools, calendar-pro]`).\n- **`register_tool_packs(agent, pack_ids, packs_dir)`** in `builtin_tools` —\n programmatic pack loading for agents.\n- **`AITHER_TOOL_PACKS` env var** — comma-separated pack IDs loaded on\n agent init (alternative to YAML config).\n- **Unknown builtin categories resolve as pack IDs** — `tools.builtin:\n [file_io, crm-tools]` now works: `crm-tools` is looked up as a pack.\n\n## [1.8.0] - 2026-05-24\n\n### Removed\n- **11 dead modules** (3,023 lines) — `agent_deploy`, `cloud_deploy`, `create`,\n `phonehome`, `sdk_bridge`, `sovereign`, `telemetry`, `telemetry_config`,\n `error_reporter`, `anonymizer`, `package`. None were imported anywhere.\n- **29 ghost exports** from `__init__.py` — `secrets`, `otel`, `fs_sandbox`,\n and `vector_memory` modules were listed in `__all__` and `__getattr__` but\n the backing files never existed. Accessing them raised `ModuleNotFoundError`.\n- **Stale dist/ artifacts** — removed old wheels/tarballs (v0.4.0–v1.4.1, 11 MB).\n- **Orphan test scripts** — `test_phase1.py` and `test_phase23.py` (one-off\n validation scripts not part of the pytest suite).\n\n### Fixed\n- **Version mismatch** — `__init__.__version__` now matches `pyproject.toml`\n (was stuck at 1.6.0).\n\n## [1.5.0] - 2026-05-23\n\n### Added\n- **`--sovereign` flag for `adk deploy node`** — registers the node with the\n Aitherium hub via federation after deployment. Saves federation credentials\n to `~/.aither/.env.federation`. Accepts `--hub` (custom hub URL) and\n `--tenant` (tenant slug) options. Non-fatal: node runs standalone if\n registration fails.\n- **Federation → fleet cross-post** (Genesis) — sovereign nodes that register\n or heartbeat via `/federation/register` and `/federation/heartbeat` now\n automatically appear in the fleet dashboard (`/fleet/deployments`) with\n `node_type: \"sovereign\"` and live status/metrics.\n- **\"Deploy Sovereign Node\" section on Connect page** (AitherVeil) — Section 8\n with ADK deploy commands, fleet dashboard links, and provision page links.\n- **`deploy-node` action in tunnel proxy** (AitherVeil) — Connect page and\n external callers can now request bootstrap scripts via\n `POST /api/tunnel/devworkspace?action=deploy-node`.\n\n### Fixed\n- **Fleet provision page** — API URL corrected from `/api/fleet/provision` to\n `/api/bridge/genesis/fleet/provision` (matches bridge proxy pattern).\n- **Fleet detail page** — API URL corrected from `/api/fleet/deployments/` to\n `/api/bridge/genesis/fleet/deployments/` (same fix).\n- **Dockerfile** — entry point corrected from `aither-serve` to `adk-serve`\n (matches `pyproject.toml` script registration). Version label updated.\n\n## [1.4.1] - 2026-05-22\n\n### Fixed\n- **macOS executable builds** — dropped `--target-arch universal2` from\n `packaging/build_executable.py`. PyPI wheels for `pydantic_core` and other\n native deps are arch-specific, not fat binaries, so universal2 always failed\n with `IncompatibleBinaryArchError`. Now builds for the runner's native arch\n (`aither-macos-arm64`).\n- **Release workflow strategy** — added `fail-fast: false` so a single\n executable build failure no longer cancels the other OS jobs.\n- **PyPI publish** — added `skip-existing: true` so re-runs of the same\n version don't fail when the artifact already exists.\n- **GitHub Release job** — now runs whenever `pypi` succeeds even if some\n executable matrix entries fail, so the release page is always created with\n whatever binaries did build successfully.\n\n## [1.4.0] - 2026-05-22\n\n### Added\n- **Sovereign template now ships with `agent_core` 5-tier memory** — generated\n backends include the canonical `agent_core/` package (working → episodic →\n semantic → procedural → identity) plus `agent_memory.py` helper, vendored\n from `portal-kit-backend`.\n- `aiosqlite>=0.20.0` added to sovereign template `requirements.txt` for\n local agent_core SQLite storage.\n- Document tombstoning: `evict_cached_conversations_referencing` is wired\n through document-delete handlers in consumer apps so memories citing\n deleted documents are blocked from recall.\n- New tests: 13 smoke tests for `portal-kit-backend/agent_core` covering\n promotion thresholds, decay rates, store roundtrip, tombstone recall\n blocking, circuit breaker state machine, and chat-turn recording.\n\n### Changed\n- `scripts/vendor_agent_core.py` now syncs canonical `agent_core` to 4\n targets: WorkspaceRuntime, ADK sovereign template, and the GargBot +\n Chelle consumer mirrors (previously only 2).\n\n## [1.2.1] - 2026-05-21\n\n### Added\n- `adk train` command group — 6 subcommands: status, launch, logs, cancel, runs, register-gpu\n- `/slash-commands` endpoint — ADK server auto-exposes all 35 CLI commands as structured JSON manifest for AitherShell\n- `/cli/execute` endpoint — AitherShell can run any CLI command through the server\n- `build_command_manifest()` — introspects full argparse tree (commands, args, choices, subcommands)\n- `_register_commands()` extracted from main() for shared parser construction\n\n### Changed\n- `_get_genesis_url()` now probes: Genesis → ADK server → Aitherium cloud gateway (works without Genesis)\n\n## [1.2.0] - 2026-05-21\n\n### Added\n- **Runtime backend switching** — `agent.switch_backend(\"deepseek\")` and `agent.llm.switch_backend()` change provider without recreating agent\n- **Hybrid reasoning** — `agent.set_reasoning_backend(\"anthropic\")` routes effort 7+ to cloud API while keeping local orchestrator\n- **First-class DeepSeek provider** — `deepseek` in effort model table with `deepseek-chat` and `deepseek-reasoner`\n- **TQ4 quantization tiers** — `nano` (6GB, TurboQuant 4-bit), `standard-tq4` (12GB, both models TQ4), `hybrid-tq4` (6GB + cloud reasoning)\n- **`adk backend` CLI** — `list`, `set`, `set-reasoning`, `test` subcommands\n- **`adk quickstart`** — unified first-run wizard (setup + auth + shell)\n- **`adk tools`** — list local + MCP tools with tier markers\n- **`adk backup`** — tarball export of `~/.aither/`\n- **`adk ingest`** — feed docs/files into knowledge graph\n- **`adk setup nemotron`** shortcut — alias for `--tier lite`\n- **`--reasoning-api`** flag in setup — `anthropic`, `openai`, `deepseek`, `gateway`\n- **`--dgx-spark URL`** flag — remote vLLM endpoint configuration\n- **DGX Spark auto-detection** — scans `spark.local`, `192.168.0.33`, `AITHER_DGX_URL` env\n- **Post-setup smoke test** — sends real inference request after container startup\n- **Schema migration system** — version table + migration runner in `memory.py` and `graph_memory.py`\n- **Configurable vLLM port scan** — `AITHER_VLLM_PORTS` env var for custom ports\n- **`aithershell` entry point** — alias for `adk` in pyproject.toml\n- Shell auto-downloads binary on first `adk shell` (no `--install` needed)\n- Shell pre-flight check warns if no backend detected\n- Shell passes full config via env vars (API key, tenant, inference URL)\n- DGX/Remote and Cloud API checks added to `adk doctor`\n- Config fields: `reasoning_backend`, `reasoning_api_key`, `deepseek_api_key`, `dgx_url`, `vllm_extra_ports`\n\n### Fixed\n- `test_aeon_chat_basic` timeout — mock scope was exiting before request\n- `adk doctor` check_disk timeout — cap rglob at 5000 files\n\n## [1.1.7] - 2026-05-19\n\n### Added\n- Remote agent pipeline — `adk onboard` → fleet dispatch → remote inference\n- Agent-to-agent federation with mesh relay\n- `adk connect` for desktop mesh enrollment\n\n## [1.1.6] - 2026-05-19\n\n### Fixed\n- Tool call execution recovery across all backends\n- Hermes tool call fallback parsing for vLLM\n\n## [1.1.5] - 2026-05-19\n\n### Fixed\n- Tool call execution + Hermes fallback parsing improvements\n\n## [1.1.0] - 2026-05-18\n\n### Added\n- Multi-channel gateway (Telegram, Discord, Slack, webhook)\n- Aeon group chat with 7 presets\n- Skills auto-extraction from multi-step sessions\n- MCP stdio server for Claude Code integration\n- `adk gateway`, `adk cron`, `adk skills`, `adk soul` commands\n- SOUL.md import/export for portable agent identity\n- Elysium cloud device-flow authentication\n\n## [1.0.0] - 2026-05-18\n\n### Breaking\n- Package consolidated — `aither_adk` namespace replaced by `adk` + `adk.platform`\n- 93 duplicate files removed from `AitherOS/packages/aither_adk/`\n\n### Added\n- Standalone core primitives (`adk.core`: Agent, Tool, Memory, ModelBackend, Capability, Trace)\n- `adk.platform` sub-package merging all platform toolkit modules\n- Compatibility shims for legacy `from aither_adk.*` imports\n\n### Changed\n- Version synced across pyproject.toml, `adk/__init__.py`, `adk/platform/__init__.py`, npm package\n- Classifier promoted from Alpha to Beta\n\n## [0.16.0] - 2026-04-16\n\n### Swarm Coding Engine & Repowise Integration\n- **`repowise_search` tool** — Semantic + keyword hybrid code search via Repowise, with ripgrep fallback\n- **`swarm_code` tool** — Dispatch complex tasks to 11-agent swarm pipeline (ARCHITECT->SWARM->REVIEW->JUDGE)\n- **`agent.swarm()`** — Async convenience method with configurable mode, effort, timeout\n- **`agent.code_search()`** — Async convenience method returning structured results\n- New tool categories: `repowise` and `swarm` in TOOL_CATEGORIES\n- `repowise_search` and `swarm_code` added to `__init__.py` exports with lazy loading\n- `IDENTITY_DEFAULTS` updated: repowise for code-focused agents, swarm for orchestration agents\n- Standalone graceful degradation: repowise falls back to ripgrep, swarm returns structured error\n\n## [0.13.0] - 2026-04-02\n\n### Graph Faculties — Local Knowledge for Every Agent\n- **CodeGraph** (2,799 lines) — Full Python AST indexer with call graph, keyword/semantic/hybrid query, embedding matrix cache, incremental re-indexing, multi-hop chain expansion\n- **MemoryGraph** (1,339 lines) — Graph-based persistent agent memory with 10 edge types, hybrid query (keyword + semantic + graph expansion), multi-hop recall, pickle persistence\n- **EmbeddingProvider** — 4-backend fallback chain: sentence-transformers (GPU/CPU) -> Ollama -> Elysium cloud -> feature hashing (zero deps)\n- **BaseFacultyGraph** — Abstract base with HMAC-SHA256 validated pickle persistence\n\n### Agent Integration\n- `agent.set_code_graph(cg)` — auto-registers `code_search` + `code_context` tools\n- `agent.set_memory_graph(mg)` — auto-registers `remember` + `recall` + `memory_stats` tools\n- Both graphs inject context into LLM prompts automatically during chat\n\n### Zero-Config Onboarding\n- `adk start` / `adk` (no args) — auto-detect project, index code, detect LLM, persistent memory, interactive chat\n- Works for any directory: Python codebases, doc folders, mixed workspaces\n- Auto-detects LLM: Ollama -> vLLM -> Elysium -> OpenAI -> Anthropic\n- Per-project persistent memory in `~/.aither/memory/<project>`\n- `adk index <path>` — standalone indexing with progress bar and stats\n\n### MCP Gateway\n- `POST /v1/embeddings` — OpenAI-compatible embedding proxy to vLLM-embeddings:8209\n- Available to all tiers (embeddings are free)\n- ADK EmbeddingProvider uses this as Elysium cloud fallback\n\n### Optional Dependencies\n- `pip install aither-adk[graphs]` — numpy for 10x cosine similarity speedup\n- `pip install aither-adk[embedding]` — sentence-transformers + torch for local GPU embeddings\n- `pip install aither-adk` alone — graphs work with feature hashing (zero deps)\n\n## [0.12.0] - 2026-04-01\n\n### Bootstrap & Service Discovery\n- Version handshake -- ADK checks major.minor compatibility with Genesis/Node\n- Background reconnect loop -- ServiceBridge re-probes every 30s in standalone mode\n- Port 8080/8090 documented -- MCP vs OpenAI-compat clearly separated\n- Genesis URL configurable via GENESIS_URL env var (was hardcoded)\n- Standalone mode warning -- visible stderr alert when AitherOS not detected\n- Auto-reconnect on startup when services come online\n\n### Elysium Cloud Inference\n- Unified gateway URL -- gateway.aitherium.com handles auth + billing + inference\n- Streaming inference -- SSE passthrough for /v1/chat/completions with stream=true\n- Auth proxy routes -- /v1/auth/register, /v1/auth/login, /v1/auth/me\n- Billing proxy -- /v1/billing/balance through gateway\n- AitherConnect Elysium fallback -- cloud inference when local Genesis is down\n- AitherDesktop Elysium fallback -- third-tier chat fallback after Node\n\n### Infrastructure\n- /discovery endpoint on Genesis -- unified service URLs/versions/health\n- /api/config/services on Veil -- runtime port config for client-side JS\n- Veil healthcheck gates on Genesis -- unhealthy when backend is down\n- Desktop crash detection 90s->60s (threshold 3->2)\n\n## [0.11.0] - 2026-04-01\n\n### Agent Execution Quality (Claude Code Parity)\n- Raise loop guard block threshold from 3 to 4 -- agents get more room for iterative search\n- Soft synthesis nudge for effort >= 4 (no tool stripping, trust the model)\n- max_output_tokens escalation -- retry up to 3x when response is truncated\n- Tool result pairing guarantee -- synthetic error for orphaned tool_use blocks\n- Micro-compaction of old tool results -- save context tokens on long sessions\n- First-turn tool forcing only for effort >= 6 (trust model for lower effort)\n- Diminishing returns detection -- nudge agent when 3+ turns produce < 500 tokens\n- Message normalization -- merge consecutive same-role messages, strip empties\n- LLM retry with exponential backoff (5 retries, 500ms-16s, jitter)\n\n## [0.9.0] - 2026-03-16\n\nThe \"connected world\" release. Cross-platform identity pairing, voice capabilities, and multi-channel integration.\n\n### Added\n- **Pairing**: Cross-platform identity linking (`adk/pairing.py`)\n - `PairingManager` — SQLite-backed identity linking with 6-char pairing codes\n - Link users across Telegram, Discord, Slack, WhatsApp with 10min TTL codes\n - Canonical session IDs for cross-channel conversation continuity\n - `get_session_id()` returns \"user-{id}\" for paired users\n- **Voice**: Speech-to-text and text-to-speech client (`adk/voice.py`)\n - `VoiceClient` — async STT/TTS/emotion via AitherVoice service\n - Convenience functions: `hear()`, `say()`, `feel()`\n - 6 voice options: alloy, echo, fable, nova, onyx, shimmer\n - Emotion detection with intensity scoring\n- New exports: `PairingManager`, `PairingResult`, `PlatformIdentity`, `VoiceClient`, `TranscriptionResult`, `SynthesisResult`, `EmotionResult`\n\n### Changed\n- `__init__.py` exports expanded with pairing and voice symbols\n\n## [0.6.0] - 2026-03-13\n\nThe \"group mind\" release. Multi-agent group chat, creative tools, and Iris identity.\n\n### Added\n- **Aeon**: Multi-agent group chat engine (`adk/aeon.py`)\n - `AeonSession` — persistent group chat with parallel agent execution\n - 7 presets: balanced, creative, technical, security, minimal, duo_code, research\n - Orchestrator synthesis: Aither summarizes all agent responses\n - Serial execution for Ollama, parallel for vLLM/cloud\n - ConversationStore persistence with `type: \"aeon\"` metadata\n - `group_chat()` one-shot convenience function\n- `aither aeon` CLI command — interactive terminal group chat with color-coded agents\n - `-p/--preset`, `-a/--agents`, `-r/--rounds`, `--no-synthesize` flags\n - `reset` and `quit` commands\n- Server endpoints: `POST /aeon/chat`, `GET /aeon/presets`, `GET /aeon/sessions/{id}`\n- Creative tools in builtin_tools: `image_generate`, `image_refine`, `image_search`, `video_generate`\n- Iris agent identity with visual generation capabilities\n- 48 Aeon tests (data models, presets, chat, context, persistence, server, exports)\n\n### Changed\n- `__init__.py` exports: `AeonSession`, `AeonResponse`, `AeonMessage`, `group_chat`, `AEON_PRESETS`\n\n## [0.5.0] - 2026-03-13\n\nThe \"tenant-ready\" release. Multi-tenant admin, permission grants, safety profiles, and a full setup wizard.\n\n### Added\n- `aither setup` interactive setup wizard with hardware detection, model selection, identity config\n- Strata storage backend: SQLite WAL persistence for conversations, memories, knowledge graphs\n- CLI test runner: `aither test` with auto-discovery and parallel execution\n- Permission grants system for MCP tool access control\n- MCP account management tools\n- LLM provider auto-detection for Ollama and vLLM endpoints\n- Apache-2.0 LICENSE file\n- Elysium desktop sync module\n- Comprehensive CLI test suite (493 tests)\n- Strata storage test suite (972 tests)\n- LLM provider tests (82 tests)\n\n### Changed\n- CLI expanded: `aither setup`, `aither test`, `aither bugreport`, `aither doctor`\n- Strata module rewritten as full local-first storage engine\n- Server startup includes Strata initialization\n- README rewritten with clearer quickstart and architecture docs\n\n### Fixed\n- Dead documentation links\n- Python 3.11 compatibility issues\n- Ruff per-file-ignores configuration\n- Identity provisioning edge cases\n- Docker node-gyp native dependency builds (python3 + build tools)\n\n## [0.4.0] - 2026-03-13\n\nThe \"own your AI\" release. Self-hosted agent OS for people who don't want their data on someone else's servers.\n\n### Added\n- Muse agent identity (creative/artistic generation)\n- Port 8120 to vLLM scan for ExoNodes discovery\n- Public roadmap with milestone-based porting schedule\n- Competitive positioning: self-hosted alternative to cloud-locked AI appliances\n\n### Changed\n- LLM router: compute-aware effort routing (replaces effort-level context gating)\n- README rewritten for sovereignty-first messaging\n- Package description updated\n- Promoted from alpha to stable release\n\n### Fixed\n- Version string consistency between pyproject.toml and __init__.py\n- vLLM port scanning now includes port 8120 (ExoNodes)\n\n## [0.3.1] - 2026-03-09\n\n### Added\n- GraphMemory: SQLite knowledge graph with Ollama embeddings and hybrid search\n- NeuronPool: auto-fire pre-LLM data gathering agents\n- NanoGPT: pure Python char-level transformer with LoRA fine-tuning\n- Safety gates: IntakeGuard (input), LoopGuard (recursion), Sandbox (code exec)\n- Event system: EventEmitter with chat/tool/forge event types\n- Builtin tools: identity-based tool selection\n- ServiceBridge: auto-discovery of AitherOS services\n- Streaming: chat_stream with safety gate integration\n- Auth middleware: Bearer token authentication\n- CLI: `aither init` and `aither serve` commands\n\n### Changed\n- Wired 5 previously disconnected modules into agent loop\n- 522 total passing tests (up from 85)\n\n## [0.3.0] - 2026-03-07\n\n### Added\n- Clean-room agent development kit\n- Multi-backend LLM providers (Ollama, OpenAI, Anthropic)\n- AitherAgent class with @tool decorator\n- SQLite conversation memory\n- OpenAI-compatible server (`aither-serve`)\n- 16 agent identities as package data\n- MCP bridge to mcp.aitherium.com\n- Privacy-centric opt-in telemetry\n- Bug reporting CLI and API\n- FastAPI server with OpenAI-compatible endpoints\n- Hardware auto-detection (5 tiers, 11 profiles)\n- Fleet orchestration and multi-agent coordination\n- A2A mesh protocol\n- Federation protocol for cross-instance agent dispatch\n\n### Initial release\n- 85 passing tests\n- Apache-2.0 license\n"}, "0cbadc74e70d000180a10f76d2778d430dfe74c8": {"t": 1785748184.3529878, "n": 69524, "h": "5fcf51b72c637de7", "c": "# Changelog\n\nAll notable changes to aither-adk will be documented in this file.\n\n## [2.17.0] - 2026-07-05\n\n### Added — Headscale mesh transport + local Qdrant + AitherConnect onboarding\n\n- **Headscale NAT-friendly mesh transport.** When raw WireGuard UDP:51820 is not\n viable (customer boxes behind NAT, corporate firewalls, CGNAT), `adk mesh join\n --headscale` routes the mesh tunnel through Tailscale's Headscale control plane.\n The overlay IP is still Conductor-assigned (10.77.0.0/16); Headscale provides\n only the transport layer. Configured via `AITHER_MESH_TRANSPORT=headscale`,\n `AITHER_HEADSCALE_URL` (default: https://headscale.aitherium.com), and\n `AITHER_HEADSCALE_AUTH_KEY` (pre-generated key). Automatic fallback to raw\n WireGuard if Headscale setup fails.\n- **Local Qdrant vector DB provisioning.** `adk stack` now provisions Qdrant\n locally when `AITHER_VECTOR_DB=qdrant` is set, enabling offline-first RAG\n without external dependencies. Tiered: local Qdrant in-fleet, cloud Nexus\n for enterprise multi-tenant deployments.\n- **Conductor URL public fallback.** `adk mesh join` resolves the internal\n `aitheros-conductor:8193` hostname; if unreachable (e.g., self-hosted nodes),\n it falls back to the public `conductor.aitherium.com:8193` endpoint,\n eliminating bootstrap configuration friction.\n\n### Documentation\n\n- **Self-hosting runbook.** New `docs/SELF_HOSTING.md` covers deploying AitherOS\n on customer infrastructure with Qdrant + mesh + AitherConnect onboarding.\n- **Mesh transport constraints documented.** Raw WireGuard requires public\n UDP:51820 endpoint (cloud instances, dedicated hardware); Headscale for\n NAT'd networks. Noted in CLI help and module docstrings.\n\n## [2.15.0] - 2026-07-04\n\n### Added — built-in web admin console + portal-profile settings sync\n\n- **Web admin console.** The page `adk up` opens (served at `/` and `/ui`) is now a full,\n self-contained console — Chat plus six admin tabs: **Backend** (view/switch LLM provider,\n set keys, test connection), **Packs** (list/enable/disable/reload tool packs),\n **Sessions** (browse/delete conversations), **Logs** (redacted agent-log tail),\n **Graph** (knowledge-graph search/neighborhood/stats), and **MCP** (add/remove external\n MCP servers). No build step, no CDN — a single packaged HTML asset. The original minimal\n streaming chat page stays at `/chat` for back-compat.\n- **Admin API (`adk/admin_api.py`).** ~20 endpoints under `/admin/*`, all bearer-gated by the\n existing middleware (never in `_skip_auth_paths`). Operates on the live agent — LLM backend\n swap (`LLMRouter.switch_backend`), pack reload, and MCP registration take effect without a\n restart.\n- **Settings sync (`adk/settings_sync.py`).** The user's portal profile is the source of\n truth: on startup the agent pulls `preferences.adk` and applies it over the local\n `~/.aither/config.yaml` cache; admin-console edits push a debounced snapshot back to\n `PUT /api/settings/preferences`. Provider API keys and MCP auth headers are device-local\n secrets and are never included in the pushed snapshot. Opt out with `AITHER_SETTINGS_SYNC=false`.\n\n### Security\n\n- **Timing-safe token comparison.** The server bearer check now uses `hmac.compare_digest`\n (the token is reachable over the public tunnel; a byte-wise `!=` leaked it to timing attacks).\n- **MCP add is SSRF-guarded + confirmed.** Adding an external MCP server is a two-step\n prepare→confirm flow; URLs resolving to loopback / private / link-local / metadata addresses\n are rejected (override with `AITHER_ALLOW_PRIVATE_MCP=1` for trusted local servers).\n- **Config editor is allowlisted.** `PATCH /admin/config` writes only safe scalar prefs and\n hard-denies fields that could execute code or redirect traffic at startup (e.g.\n `aither_toolpack_dirs`, backend base URLs).\n- **Log tail is scrubbed.** Bearer tokens, `#k=` fragments, and `sk-`/`aither_sk_` keys are\n redacted from the log-tail endpoint; the tunnel log is excluded.\n\n## [2.14.8] - 2026-07-04\n\n### Added — `adk up` for normal humans (no API key, no prereqs, live chat)\n\n- **Hosted-brain default — no API key required.** When there's no local model and no\n provider key, `adk up` signs in (device-flow) and routes inference through the gateway\n using the portal token (`AITHER_LLM_BACKEND=gateway` + bearer). A non-technical user\n never needs to obtain or paste an LLM API key. Explicit `--provider`/local model still\n take precedence; unattended `--yes` with no token/backend fails cleanly (exit 3).\n- **cloudflared auto-download.** If the tunnel binary is missing, `adk up` downloads the\n right platform asset to `~/.aither/bin` (SHA256-verified against the release; opt out with\n `AITHER_CLOUDFLARED_REQUIRE_CHECKSUM=0`) instead of exiting with an install hint.\n- **Built-in streaming chat page.** The agent now serves a self-contained chat UI at `/`\n (and `/chat`) with a live \"thinking…\" indicator and token-by-token streaming — so a human\n has somewhere to talk to it with immediate feedback instead of a blank wait. `adk up` opens\n it locally and passes the callback bearer in the URL fragment (`#k=…`), so `/chat/stream`\n stays authenticated (not an open proxy) with nothing to paste.\n- **Human-readable failures.** `adk up` errors now carry a plain-English `next_action`\n (kept machine-readable in `--yes` JSON).\n\n### Fixed\n\n- `adk up` chat link pointed at a non-existent `/settings/agent` route; it now points at the\n agent's own streaming chat page.\n\n## [2.14.7] - 2026-07-04\n\n### Added\n\n- **`adk up` — one command to run a persistent, fleet-connected agent.** Collapses\n the scattered onboarding surface (`host`/`quickstart`/`connect`/`login`/`setup`)\n into a single command that starts `aither-serve` (single agent, identity `aither`\n by default), opens a Cloudflare quick-tunnel for fleet reachability, registers with\n the portal, and installs a reboot-persistent autostart entry (Windows Task Scheduler\n / systemd-user / launchd). Detaches by default so the terminal is freed; `--foreground`\n blocks. Reuses the existing device-flow login, cloudflared discovery, provider-key\n store, and `_preflight_check` backend detection.\n- **Fully non-interactive path for autonomous agents.** `adk up --yes` (or any\n non-TTY invocation) takes ZERO prompts — everything resolves from flags, env, and\n saved config — and emits a single machine-readable JSON status line with meaningful\n exit codes (`0` ok, `2` bad-input, `3` no-backend, `4` tunnel-missing, `5` register-failed).\n Degrades to local-only (warn, not fail) when a portal token is absent unless\n `--require-register` is set.\n- **`adk down` / `adk status --json` companions.** `down` stops the agent + tunnel,\n best-effort deregisters, and removes the autostart entry; `status` reports the running\n agent (liveness + `/health` + tunnel + registration), with `--json` for agents/CI.\n- **`adk stack`** — the previous `adk up` behaviour (supervise the Room + Ollama consumer\n stack) now lives here; `adk up` is the connected-agent command.\n\n### Internal\n\n- New `adk/agent_daemon.py`: detached process spawn, status file\n (`~/.aither/adk-up.json`), pid liveness/teardown, and cross-platform autostart install.\n\n## [2.14.6] - 2026-07-04\n\n### Added\n\n- **`adk.preflight` — self-bootstrap capability probe.** `run_preflight(agent,\n spec)` wakes an agent up knowing what it is actually plugged into: a bounded\n (1.5s) parallel `LivenessProbe` over the router's resolved backends\n (primary/reasoning), the embeddings winner, remote MCP tools, the structured-ML\n / `/ml/teach` endpoints, and voice — returning an honest `CapabilityReport`.\n Status vocabulary distinguishes reachability from entitlement\n (`OK`/`MISSING`/`UNREACHABLE`/`AUTH`/`TIER_DENIED`/`UNSUPPORTED`), a hosted slot\n never prints a bare `OK`, and a required-but-unsatisfied slot aborts before the\n loop instead of blind-firing. Headless task/rules resolution + an allowed-roots\n policy binding (`builtin_tools.set_allowed_roots`, which resets the memoized\n cache so the override actually enforces).\n- **Multimodal (image) messages.** `Message.content` now accepts an OpenAI-style\n content-part list (`str | list[dict]`), passed through verbatim to the\n OpenAI-compatible body (gateway / vLLM / OpenAI). New `adk.llm.multimodal`\n helpers — `image_message()`, `image_content_parts()`, `to_image_url()` — build\n the parts and encode local image bytes/paths into `data:` URIs.\n- **Real vision preflight probe.** The vision slot is no longer unconditionally\n `UNSUPPORTED`: it sends a discriminable image (a solid red square) and requires\n the model to name the color, so a blind/text-only model cannot false-pass. `OK`\n only on a real round-trip; `AUTH` on 401/403; `UNSUPPORTED` when there is no\n OpenAI-compatible provider or the model cannot see the image.\n\n## [2.14.5] - 2026-07-04\n\n### Added\n\n- **`adk.reasoning.mcts` — generic Monte-Carlo Tree Search library.** A reusable\n MCTS engine (ported from the internal `UnifiedMCTS`, with all internal couplings\n removed) that any agent can drive over its own environment:\n - `MCTSEnvironment` protocol (`get_state_hash` / `get_actions` / `step` /\n `evaluate` / `clone`) — implement it and search.\n - Three **optional, default-off** model seams — `TransitionModel`, `PolicyModel`\n (PUCT priors), `ValueModel` — so a learned world-model / policy / value can be\n injected without touching the core; with all three `None` the engine is a plain\n UCT search.\n - `ObservedTransitionModel` adapter — a cheap online lookup world-model (hit =\n exact stored transition; miss = identity + negative \"unknown\" reward +\n `is_uncertain`, biasing exploration toward learning), with JSONL save/load for\n cross-run reuse.\n - `UnifiedMCTS(config).search(env)` returns an `MCTSResult` (best action, value,\n confidence, principal-variation path) and an optional `MCTSTrace`\n (visit-distribution policy target) for distillation.\n- **Self-bootstrapping agent machinery.** Build an agent from a declarative spec\n instead of bespoke wiring:\n - `adk.bootstrap.build_agent_from_spec(path) -> (AitherAgent, RunCtx)` — resolves\n prompts, activates required/optional tool packs, and carries a `memory_map`\n (a `node_type -> (role, tier)` override for memory writes, never a gate).\n - `adk.prompts.PromptBridge` — resolves an agent's prompt bindings from files\n (relative to the spec) or `AitherPrompts` dot-keys (optional import).\n - `adk.packs.PackActivator` — eager activation of installed tool packs by name\n over `register_tool_packs`; a required pack that registers zero tools raises\n `PackUnavailable`, optional packs soft-degrade.\n - `python -m adk.bootstrap.generic_agent <spec.yaml>` inspects a spec (resolved\n name, activated packs + tool counts, prompt keys, memory map) without running a\n live loop.\n- **`deep_research` tool pack** — a full multi-engine web-research workflow as\n `dr_*` agent tools (keyless search, page fetch + cache, site mirror, a citable\n findings knowledge graph, and markdown/PDF/DOCX report generation), activatable\n by name via the tool-pack loader.\n\n## [2.14.4] - 2026-07-04\n\n### Added\n\n- **`structured_ml` tool domain** — zero-shot structured-data inference for agents,\n backed by TabFM (tabular classification/regression) and TimesFM (time-series\n forecasting). Three tools:\n - `tabular_classify(support_rows, target, query_rows)` — classify rows from a\n labeled support set, in-context (no training step; up to 10 classes).\n - `tabular_regress(support_rows, target, query_rows)` — predict a numeric target.\n - `timeseries_forecast(series, horizon)` — forecast future values of a series.\n\n The tools POST to a structured-ML inference service resolved from\n `AITHER_STRUCTURED_ML_URL` (default `http://localhost:8192`), reject non-http(s)\n URLs, and summarise oversized responses to protect the agent's context.\n- **`Capability.STRUCTURED_INFERENCE`** capability token and a new **`analyst`**\n identity/pack that ships the `structured_ml` domain by default. The domain is\n opt-in — only identities that enable the `structured_ml` category get the tools.\n\n## [2.14.3] - 2026-07-03\n\n### Fixed\n\nAll four bugs below were found by actually booting `adk run` against a real\nchat model and driving a real tool-calling request end-to-end (not inferred\nfrom reading the code) — each one silently broke the \"local agent uses its\nconfigured model and its tools actually work\" path.\n\n- **`AITHER_MODEL` / `config.model` silently ignored by the OpenAI-compatible\n backend**: `LLMRouter.__init__` never read `config.model` into `self._model`\n unless the caller passed `model=` directly to the constructor — which nothing\n did. Every explicit model choice (env var, config file) was dropped, the\n provider fell back to a hardcoded per-backend default (`gpt-4o-mini` for the\n `openai` backend), and an unrecognized model name got silently remapped\n server-side to the wrong backing model. Concretely: setting\n `AITHER_MODEL=aither-orchestrator` had no effect — every request actually\n went to a different model that was never meant to be called directly and\n can't reliably use tools.\n- **`OPENAI_BASE_URL` ignored for explicit `--backend`/`AITHER_LLM_BACKEND`\n selection**: `LLMRouter._auto_detect()`'s explicit-backend branch hardcoded\n `base_url=None` when constructing the provider, instead of reading\n `config.openai_base_url` (which two other call sites in the same file\n already did correctly). Any self-hosted OpenAI-compatible endpoint\n (MicroScheduler, vLLM, LM Studio) configured via `OPENAI_BASE_URL` was\n silently ignored in favor of `https://api.openai.com/v1`.\n- **Tool-call steering never engaged on default-effort chat**: the retry that\n nudges a model back into structured tool-calling when it responds with prose\n instead of a function call was gated behind `effort >= 6`. Default `effort`\n for a plain `agent.chat(message)` call resolves to `5`, so the safety net —\n built specifically for this failure mode — never fired for default requests.\n Also now forces `tool_choice=\"required\"` on the retry instead of relying on a\n text nudge alone.\n- **Every non-string builtin-tool parameter silently typed as `\"string\"` in\n the generated tool schema**: `builtin_tools.py` uses\n `from __future__ import annotations`, so `_extract_parameters()` in\n `tools.py` was reading postponed (string) type hints (`\"int\"`, not `int`)\n straight out of `fn.__annotations__`, which never matched\n `_type_to_schema`'s type-object keys and silently fell through to\n `{\"type\": \"string\"}`. Every builtin tool with an `int`/`float`/`bool`/`list`\n parameter was affected — e.g. `file_read(start_line: int, end_line: int)`\n advertised both as strings, so a model correctly calling the tool with\n `\"start_line\": \"0\"` crashed the actual implementation with\n `unsupported operand type(s) for -: 'str' and 'int'`. Fixed by resolving\n hints through `typing.get_type_hints()` instead of the raw annotations dict.\n `_coerce_arguments()` — the runtime safety net that's supposed to coerce a\n model-supplied `\"0\"` string into `0` before a tool call executes — had the\n exact same raw-`__annotations__` bug and silently no-opped for every builtin\n tool; fixed the same way.\n- **Streaming tool-loop could hang a connection forever**: `chat_stream()`'s\n fallback to the sync tool loop (tool calls can't stream token-by-token) had\n no timeout, so a model that didn't cleanly terminate a tool-calling round\n trip left the SSE connection open indefinitely with zero bytes sent. Bounded\n with a 60s `asyncio.wait_for`; on timeout the stream yields a clear message\n instead of hanging.\n\n## [2.14.2] - 2026-07-03\n\n### Fixed\n- **AitherShell session amnesia**: the REPL generated a fresh `session_id` for\n EVERY message, so conversation history never followed across turns (the\n server stored each turn under a different session). One stable id is now\n minted per shell run; `/resume` and `/new` semantics unchanged, and the\n cross-restart auto-restore of the last session now actually works.\n\n## [2.14.1] - 2026-07-02\n\n### Added\n- **Account-tied licensing via device-flow login** — `adk login` now persists the account\n license returned by the device-flow handshake to `~/.aither/license.json` (base64\n `license_key` → `{payload, signature}` envelope), so entitlements from the authenticated\n account carry into every later `adk` invocation without re-auth. Pairs with the fleet-side\n node tool-pack gate that grants packs by plan-tier rank.\n- **`can_use_formbridge` / `can_use_untether` entitlements** (PROFESSIONAL+ rank ≥ 3) backing\n the FormBridge and UNTETHER agent tool-packs.\n\n### Fixed\n- `adk` CLI: guard the optional `memory_graph` singleton (was a latent `NameError` on the\n `/stats`, `/memory`, and save paths when memory was not constructed).\n\n## [2.14.0] - 2026-07-02\n\n### Added\n- **`adk.memory_wiki` — LyraWiki-style self-managed semantic knowledge** (opt-in; nothing constructs it by default): `MemoryWiki(graph_memory)` maintains curated wiki ARTICLES as `wiki_article` nodes (title/slug, LLM-owned markdown with `[[wikilinks]]`, embedding, durable tier, `CONSOLIDATED_FROM` edges to source memories, governance-ledgered revisions).\n - `consolidate(llm, since=, budget=)` clusters unconsolidated episodic/fact nodes (embedding + keyword), drafts/UPDATEs articles via the INJECTED llm callable (`str -> str` or `messages -> str`, sync or async — no hard model dependency), cites source node ids, marks contradicted sources superseded, then DEMOTES consolidated sources to a fast-decay tier via `promote()` (represented knowledge doesn't need raw copies).\n - `lint(llm=None)` — deterministic health checks (orphan wikilinks, empty/stale articles, live-but-contradicted pairs) + an optional LLM audit pass.\n - `prune(relevance_floor=0.05, hard_delete_after_days=14)` — article relevance = tier freshness × reinforcement (reinforce-on-recall) + link-degree to LIVE memories; below the floor → reversible governance tombstone; entombed past the window → **HARD-DELETE** (the tombstone snapshot itself is purged — content and embedding irrecoverable; the true-deletion path). A node is never deleted without a tombstone first.\n - `recall(query, limit)` — article-first RAG (curated articles rank above raw nodes) that reinforces returned articles.\n- **`adk.routines` — agent heartbeat + self-programmed routines**: `RoutineStore` is a durable registry (`~/.aither/routines/{agent}.json`, `AITHER_DATA_DIR`-aware, injectable path) of cron-scheduled self-prompts `{name, cron, instruction, enabled, last_run, last_result, tags}` that rehydrates into the existing `adk.cron.CronScheduler` on `start()`. A fire runs `agent.chat(instruction)` with the agent's full toolset — the agent programs its future self with its own adk. Guardrails: `max_routines` (12), a 5-minute min fire interval per routine, a per-fire timeout, and every result ledgered (truncated) to `last_result`. Direct-callable routines (`register_direct`) fire a bound method instead of a self-prompt. Self-management tools (`routine_create/list/update/pause/resume/delete/run_now`) ship as OpenAI tool defs + handlers (`build_routine_tools` / `register_routine_tools` / `routine_tool_defs`); the handlers ONLY touch the RoutineStore — they can never modify agent config or safety-relevant settings (the leash principle).\n- **`AitherAgent(routines=True, memory_maintenance=True)`** (both default **False** → the default agent is byte-identical): `routines=True` builds the RoutineStore + scheduler, registers the self-management tools, and starts the heartbeat lazily on the first `chat()` (or via explicit `start_routines()`). `memory_maintenance=True` registers DEFAULT routines — `wiki_consolidate` (every 2 h), `wiki_lint` / `wiki_prune` / `graph_sweep` (daily) — as DIRECT method fires so memory upkeep runs even on tiny models, while staying visible/manageable via `routine_list`.\n- `TombstoneStore.purge(tombstone_id)` — the hard-delete endpoint: irrecoverably removes a tombstone snapshot and rewrites the persisted store without it (consumed by `memory_wiki.prune`).\n\n## [2.13.6] - 2026-07-02\n\n### Added\n- **Qdrant backend for GraphMemory dataplane sync — TRUE two-way, verified live.** Set `AITHER_FLEET_QDRANT_URL` and the sync uses Qdrant (`upsert` + `scroll` + filtered `search`) instead of Nexus. This is what actually delivers cross-agent memory sharing: a node is upserted with its OWN embedding under a deterministic per-(tenant,node) point id (idempotent re-push), and `fleet_pull` reliably SCROLLS a tenant's points back — the enumerate Nexus lacks (its `/search` doesn't return ingested docs and `/export` needs lancedb, which has a hard dependency conflict in the fleet). **Proven live with 2 agents:** agent-1 ingests → auto-syncs to the tenant dataplane; a fresh agent-2 in the same tenant `fleet_pull`s all of agent-1's nodes and can search them; a different tenant pulls nothing (isolation). Dimension-safe (never mixes vector dims in a collection). Local SQLite stays source of truth; best-effort, never raises.\n\n## [2.13.5] - 2026-07-02\n\n### Fixed\n- **CRITICAL — GraphMemory dataplane sync push was 100% failing.** `_fleet_push_node` sent `source_type=\"graph\"` / `content_type=\"graph_node\"`, which AitherNexus rejects as invalid enums (HTTP 500) — so NO graph node ever replicated in 2.13.4. Now sends the accepted `source_type=\"manual\"` / `content_type=\"text\"` (graph provenance is carried in `metadata.synced_from`). **Verified live** against a fleet Nexus: a 4-node ingest replicates with `pending==0` and the collection count reflects it.\n\n### Added\n- **Swarm-awareness notification** — on a successful sync batch, `GraphMemory` emits a best-effort `graph.synced` event (tenant/workspace/agent/collection/count) to `AITHER_FLEET_EVENTS_URL` so OTHER agents in the tenant/swarm can pull the fresh data and deconflict in-flight work (push-based awareness instead of polling). No-op unless the env is set.\n\n### Known limitation\n- Cross-agent **rehydration** (`fleet_pull`) is only reliable when the fleet Nexus is backed by lancedb (persistent + exportable). An in-memory Nexus stores on `/ingest` but neither its `/search` nor `/export` returns the docs — two-way read needs the persistent Nexus.\n\n## [2.13.4] - 2026-07-02\n\n### Added\n- **Memory-maintenance primitives on `GraphMemory`** (all opt-in; default behaviour byte-identical):\n - **Reinforce-on-recall**: `recall_with_activation(..., reinforce=True)` bumps `reinforcement_count`/`last_reinforced` on every RETURNED node's metadata mirror (one UPDATE per node, WAL-safe) — making the `MemoryRecord` reinforcement fields the activation scorer reads LIVE. Default `False` = zero writes.\n - **`sweep(now, archive_below=0.05, max_nodes=None, dry_run=False)`**: read-time decay enforced at rest — archives nodes past their tier TTL with freshness×reinforcement score below `archive_below` and `reinforcement_count <= 1`; `max_nodes` additionally archives the lowest-scored overflow. PERMANENT tier immune. Every archive is a reversible governance tombstone (`TombstoneStore.recover`) + a FORGET ledger entry; a node is never deleted without a tombstone. `dry_run` returns the would-archive list without acting.\n - **`promote(node_id, tier=?, role=?)`**: re-tier/re-role a node IN PLACE (tier/role columns + metadata mirror) — edges, embedding and ledger history survive; ledgers an UPDATE entry when governance is on.\n- `MutationType.UPDATE` ledger entry kind (in-place re-tier/re-role).\n- **Per-tenant dataplane sync for `GraphMemory`** — replicate the local graph to a per-tenant vector dataplane (Nexus/Qdrant) so a sovereign agent's memory is durable + rehydratable across restarts and hosting boundaries. `fleet_push_all_nodes()` (catch-up), `fleet_pull()` (best-effort semantic top-up), `fleet_sync_pending()`, and auto-sync on ingest (`_maybe_autosync` + `drain_sync()`). **On by default** (`AITHER_FLEET_SYNC=auto`; only `false/0/off` disables) with an inferred target (local Nexus `:8122` or the gateway in cloud mode) and a SEPARATE collection (`AITHER_FLEET_GRAPH_COLLECTION`, default `graph_memory`) from the KV memory sync. New `GraphMemory(tenant_id=, workspace_id=, fleet_url=, auto_sync=)` constructor args override env (required for multi-tenant host processes). Node `synced` column + schema v4 migration. TLS verify is CA-bundle-aware (`AITHER_CA_BUNDLE`) for the internal-TLS mesh. Local SQLite stays the source of truth; every sync path is best-effort and never raises into ingest.\n\n## [2.13.3] - 2026-07-02\n\n### Added\n- **Canonical self-deploying embeddings provider** (`adk.embeddings`): one embedding provider for the whole SDK so vectors are portable across scopes (platform / tenant / sovereign) — same model + dimension everywhere. Pinned to `nomic-embed-text` (768-d; the vLLM `--served-model-name` for nomic-embed-text-v1.5). Lazy, single-flight resolution chain: explicit `AITHER_EMBEDDINGS_URL` → local vLLM (`:8209` then `:8120`, HTTPS-then-HTTP) → local Ollama → gateway (`AITHER_GATEWAY_EMBEDDINGS_URL`) → auto-deploy a local vLLM embeddings container if a GPU + Docker are present (opt out with `AITHER_EMBED_AUTODEPLOY=0`) → CPU sentence-transformers (384-d, degraded) → feature-hash (384-d, degraded, always works). Every batch is dimension-tagged so callers never silently mix 768-d and 384-d vectors. `get_default_embedder()`, `embed_texts()`, `embed_one()`, `get_provider()`, `reset_provider()`.\n\n### Changed\n- **`GraphMemory` now defaults its embedder to the canonical provider** (opt out with `AITHER_GRAPH_EMBEDDER=legacy`), so every adk agent shares one 768-d space. Added a meta-table dimension guard: the index is pinned to its first embedding's dimension and later different-dimension vectors are refused (kills 768↔384 mixing that silently poisons cosine similarity).\n\n## [2.13.2] - 2026-07-01\n\n### Added\n- **Session persistence**: AitherShell now auto-saves the active session_id to `~/.aither/config.yaml` after each chat turn and auto-restores it on the next shell launch. Multi-turn conversations survive across restarts — the messages were always persisted in SQLite + JSON, only the session pointer was missing.\n- `/new` command to start a fresh session (clears saved session_id).\n- `/sessions` command to list recent sessions with agent name, message count, and timestamp. Resume any with `/resume <id>`.\n\n## [2.13.1] - 2026-07-01\n\n### Fixed\n- **CRITICAL:** `deploy_connect()` now sends a HEAD request to verify the GitHub release asset exists before downloading, with a clear error if the asset is missing.\n- **CRITICAL:** Compose file URLs are now pinned to a stable commit hash (configurable via `AITHER_COMPOSE_PIN` env) instead of tracking the mutable `main` branch.\n- **CRITICAL:** `deploy_adk_node()` now falls back to starting `adk-serve` natively when Docker is unavailable, instead of blocking with \"Docker not installed\".\n- **HIGH:** GHCR login failure now blocks deployment immediately with actionable guidance, instead of warning and continuing to a confusing image-pull failure.\n- **HIGH:** Health checks now stream the last 30 lines of Docker Compose logs on failure, so the user can see why a container didn't start.\n- **HIGH:** vLLM port allocation now probes for conflicts before assigning; if port 8200 is busy, workers are automatically remapped to the next free port.\n- **MEDIUM:** Compose file downloads are now cached with ETag; re-running `adk deploy node` skips re-downloading an unchanged file.\n- **MEDIUM:** DGX Spark discovery now respects `AITHER_DGX_HOSTS` env (comma-separated) for custom network topologies, instead of only checking `spark.local` and `192.168.0.33`.\n- **MEDIUM:** Cloud API keys (Anthropic, OpenAI, DeepSeek) are now validated with a lightweight probe during `adk setup` infra scan, so invalid keys surface early.\n- **MEDIUM:** `deploy_connect()` now validates the extracted extension has a `manifest.json` and warns if it's missing.\n\n## [2.13.0] - 2026-07-01\n\n### Removed\n- **`adk.faculties.memory_graph`** — the pickle-based `MemoryGraph` module is deleted. `from adk.faculties import MemoryGraph` now resolves to the canonical `adk.graph_memory.GraphMemory` for back-compat.\n- **`adk.platform.memory`** — the entire legacy memory subsystem (`MemoryManager`, `UnifiedMemorySystem`, `GameEngine`, `StoryboardEngine`, `AnchorGenerator`) is deleted. Agent memory is `adk.memory.Memory` + `adk.graph_memory.GraphMemory`.\n\n### Changed\n- `adk work` command now uses `GraphMemory` instead of the removed pickle-based faculty graph.\n- `adk.MemoryGraph` and `adk.faculties.MemoryGraph` lazy exports now resolve to `adk.graph_memory.GraphMemory`.\n- `agent.set_memory_graph()` kept for back-compat but documented as deprecated; agents already wire `GraphMemory` automatically in `__init__`.\n- Platform startup (`adk.platform.infrastructure.startup`) gracefully handles missing `MemoryManager`.\n- Platform CLI module listing and health check updated to reflect removed memory module.\n\n## [2.12.7] - 2026-07-01\n\n### Deprecated\n- `adk.faculties.MemoryGraph` — emits `DeprecationWarning` on instantiation; use `adk.graph_memory.GraphMemory` (SQLite-backed, governed, embedding-native) instead.\n- `adk.platform.memory` package — the legacy `MemoryManager`, `UnifiedMemorySystem`, `GameEngine`, and `StoryboardEngine` modules now emit `DeprecationWarning` on import; use `adk.memory.Memory` and `adk.graph_memory.GraphMemory` for new code.\n\n### Changed\n- Updated `adk.faculties` docstring and usage examples to point at the canonical memory surface.\n\n## [2.12.6] - 2026-07-01\n\n### Changed\n- Trimmed public package debt by removing grid runbook artifacts and shell/room binary placeholder directories from published artifacts.\n- Removed stale public-sync allowlist entries for deleted phase test files and added explicit public-sync guards for binary placeholder directories and grid artifacts.\n- Updated the public README grid note so it no longer links to runbook files excluded from the SDK package.\n\n## [2.12.5] - 2026-07-01\n\n### Added\n- Added best-effort fleet memory sync from local ADK memory into Nexus/Qdrant-compatible RAG stores, including batch catch-up and vector-search recall hooks.\n- Added saved config/env support for fleet memory URL, collection, and sync mode.\n\n### Changed\n- Excluded FormBridge runtime/test internals from public ADK packaging and public repo sync.\n\n## [2.12.4] - 2026-07-01\n\n### Added\n- Added a hot-reload endpoint for installed agent packs so newly applied pack tools can be picked up without restarting `aither-serve`.\n\n### Fixed\n- Fixed synchronous `ChatRelay` calls on Python 3.12 when no event loop is running; async WebSocket delivery is still scheduled when a loop exists.\n- Aligned gateway inference tier tests with the current public ladder: free, starter, pro, enterprise, platform.\n\n## [2.12.0] - 2026-06-28\n\n### Added — one-command local inference + upgrade path (click-to-run)\n- **`adk quickstart-local`** — detects hardware, picks a backend\n (Ollama / llama.cpp / vLLM), installs it, downloads a model, verifies, and\n prints next steps. Bootstraps Ollama itself via winget/brew/install.sh when\n missing (no \"go install it\" dead-ends). Default model `gemma4:e2b`.\n- **`adk backend switch <ollama|llamacpp|vllm>`** (+ `backend status`) — migrate\n the active deployment between engines; re-points config and smoke-tests.\n- **`adk install pack:<name>`** (+ `adk install list` / `adk packs`) — install\n ready-made agent packs (openclaw, hermes, claude-code) into `~/.aither/agents`.\n\n### Fixed (found via live end-to-end testing on Windows + CUDA)\n- quickstart-local reported success on a dead endpoint (smoke test was warn-only);\n a failed smoke now fails the command, with a service-readiness wait.\n- `--model` resolved to `None` (getattr default never fired) → uses the real default.\n- Smoke test: 30s→180s timeout for cold model load; accepts reasoning-model output\n (empty `content` at low token budgets is no longer a false failure).\n- `adk install pack:<name>` colon syntax (argparse rejected it before dispatch).\n- `adk backend status` health probe used a nonexistent `/health` path → 404; now\n probes `/v1/models` (uniform OpenAI-compatible liveness for all three backends).\n- Windows: removed non-ASCII glyphs from printed strings that crashed the cp1252\n console (a `✓` even made a successful install report failure).\n\n## [2.11.1] - 2026-06-14\n\n### Fixed — `adk host` / `adk login` device flow (autonomous onboarding)\nFound driving `adk host` against production; all broke real device-flow login:\n- **Device flow hit the portal FRONTEND, not AitherIdentity.** `adk login`/`adk host`\n POSTed `/auth/device/code` to portal/veil.aitherium.com, which 307-redirects API auth\n to `/login`. Added `_resolve_identity_url()` mapping the aitherium.com topology to\n `idp.aitherium.com` (the Identity API); localhost/bespoke pass through unchanged.\n- **Cloudflare 403'd urllib.** `idp.*` sits behind Cloudflare, which blocks urllib's\n default `Python-urllib/3.x` UA. Device-code + poll requests now send a real\n User-Agent + Accept (verified urllib→idp 200, was 403).\n- **`adk host` now self-authenticates via device flow** when there's no token, and\n **retries via device flow on a 401/403** (a stale saved token no longer dead-ends\n registration).\n\n## [2.11.0] - 2026-06-14\n\n### Added — attach your own MCP \"hands\" to a self-hosted agent\nA self-hosted agent now has the full brain (your model) · body (this loop) · **hands**\n(your tools) trifecta. The AitherOS platform relays the MCP servers you registered for\nyour agent in the `/stream` request body as `mcp_endpoints`, and the loop wires their\ntools into the ReAct turn:\n- **`adk/mcp_endpoint_tools.register_mcp_endpoint_tools(agent, endpoints)`** — speaks MCP\n JSON-RPC (`tools/list` to discover, `tools/call` to invoke) and registers a namespaced\n proxy tool (`{endpoint}__{tool}`) for each advertised tool. Mirrors `app_proxy_tools`.\n Best-effort: an unreachable endpoint registers nothing and never raises.\n- **`POST /stream` and `POST /chat/stream`** accept an optional `mcp_endpoints` list\n (`[{name, url, [headers]}]`); `_aitheros_stream` attaches them to the agent before the\n turn. Existing callers are unaffected (the field is optional).\n\n## [2.10.1] - 2026-06-14\n\n### Fixed — self-hosted `aither-serve` was crash-on-startup (2.10.0 regression)\nA fresh `pip install aither-adk && aither-serve --backend deepseek` could not boot. Found\nand fixed while standing up a real self-hosted DeepSeek agent end-to-end:\n- **Startup `NameError: name 'port' is not defined`** in three lifespan helpers\n (`_join_aithernet`, `_init_a2a_server`, `_start_mesh_hosting`) — they referenced a bare\n `port` no longer in scope. Now use `config.server_port`, and `main()` writes the resolved\n `--port`/`--host` back to `config` so every lifespan helper and the invoke_url see the\n actual bound port.\n- **Fleet self-registration crashed the whole server** — the registration handlers caught\n `(ImportError, RuntimeError, OSError, ConnectionError)` but **not** `httpx.HTTPError`, so a\n `RemoteProtocolError`/connect error talking to the control plane took the agent down. All\n network-facing lifespan handlers now also catch `httpx.HTTPError` (non-fatal, as intended).\n- **`--backend` was ignored when `AITHER_API_KEY` was set** — `_auto_detect` picked the cloud\n gateway before the explicit backend, and `_try_elysium_fallback` then replaced the chosen\n provider. An explicit backend (deepseek/openai/anthropic/your own vllm/ollama) now always\n wins; \"auto\"/\"gateway\" still flow through detection. The operator's own brain is never\n silently hijacked.\n- **Approved-tool trace truncated after `/sessions/{id}/confirm`** — the SSE relay treated\n `AgentResponse.tool_calls_made` (a `list[str]`) as dicts (`tc.get(...)`), raised\n `AttributeError`, and an over-narrow `except` swallowed it so the client saw only\n `session_start` + heartbeats. The relay now handles string tool names, the `except` is\n broad (always emits a typed `error` + terminal `complete`), and the module-level\n `_strata_ingest` `NameError` + Chronicle `session_id` kwarg mismatch are fixed.\n\n## [2.10.0] - 2026-06-13\n\n### Added — human-in-the-loop tool approval (`adk/approval.py`)\n- **Pause before a gated tool, resume on decision.** An agent can now pause its turn before\n executing a sensitive tool and wait for a human's allow/deny — so a managed control plane\n (or any operator) can gate tool use without forking the loop.\n- **Policy via `AITHER_TOOL_APPROVAL`** — a comma-separated list of tool names (optionally\n `agent:tool`), or `*` for every tool. Empty = no gating (unchanged behavior).\n- **Survives restarts / days-later approvals.** The paused turn (user message + pending tool\n calls) is persisted to disk keyed by `session_id`; resume is a fresh request, not a held\n connection. Decisions are recorded per `(session_id, tool_name)`.\n- **`AgentResponse.requires_action` / `.pending`** — set when a turn paused; `pending` lists\n the gated tool calls awaiting a decision.\n- **`AitherAgent.resume(session_id, decisions)`** — records the decisions and re-runs the\n paused turn (the gate consumes them: allowed tools execute, denied tools get a denial\n observation, the turn continues — and may pause again on a different gated tool).\n- **Server:** `POST /sessions/{id}/confirm` records decisions and streams the resumed turn;\n `/stream` emits a `requires_action` event when a turn pauses.\n\n## [2.9.0] - 2026-06-13\n\n### Added — universal LLM continuation primitive (`adk/llm/continuation.py`)\n- **`run_continuation()` + `stitch()`** — one shared continue-until-complete primitive. When a\n completion stops because it hit the output token cap (`finish_reason == \"length\"`), it continues\n the generation and STITCHES the chunks (overlap-dedup + restart-detect) into a complete answer,\n instead of every call-site hand-rolling its own retry. Bounded three ways (max rounds AND total\n output ceiling AND a no-growth break), and it never mutates the caller's message history.\n- **`LLMProvider.chat_with_continuation()`** and **`LLMRouter.chat_with_continuation()`** — every\n provider AND the router inherit the primitive, so any call-site gets continuation for free.\n- Kill-switch `ADK_LLM_CONTINUATION=off`; tunables `ADK_LLM_MAX_CONTINUATIONS`,\n `ADK_LLM_MAX_TOTAL_OUTPUT_TOKENS` (defaults: 2 rounds / 8192 tokens).\n\n### Changed\n- The ReAct loop's truncation recovery now uses the shared primitive. The previous inline\n doubling-retry REPLACED the partial response (losing earlier text in `resp.content`) and injected\n continuation scaffolding into the live message history; it now STITCHES and leaves history clean.\n Tool-call turns are never continued as text.\n\n### Included — previously-committed-but-unreleased changes\n- 2.8.2: TLS verification on by default (`verify=False` removed).\n- 2.8.1: baked license + pack-signing public keys.\n- 2.8.0: version-bump release.\n\n## [2.7.0] - 2026-06-05\n\n### Added — provider-agnostic LLM cost levers (at the `LLMProvider` ABC)\n- **Prompt caching.** New `ProviderCapabilities` (`prompt_cache: explicit|automatic|none`, `batch`) and\n normalized `LLMResponse.cache_read_tokens`/`cache_write_tokens`, so the \"tokens saved\" meter never\n depends on which backend served the turn. Anthropic inserts `cache_control` breakpoints over the stable\n system+tools prefix; OpenAI/DeepSeek caching is automatic and their `cached_tokens` are now surfaced\n (previously discarded). Default-on in `AitherAgent.chat`; override with `cache=False`.\n- **ResponseCache** (`adk/llm/cache.py`) — exact-match response cache with a pluggable backend, wired into\n `LLMRouter` as an opt-in (`LLMRouter(response_cache=…)` + per-call `cacheable=True`). OFF by default — no\n determinism change unless opted in.\n- **Gemini provider** (`adk/llm/gemini.py`) with explicit `cachedContents` caching; registered in the router.\n- **Async batch runner** (`adk/llm/batch_runner.py`) — `run_batch()` over the Anthropic Message Batches /\n OpenAI Batch APIs (~50% off) with a transparent fallback to concurrent `chat()` where unsupported.\n- **Graph-RAG retriever interface** (`adk/graph_retriever.py`).\n\n### Added — principal authority (prompt-injection defense enforced outside the model)\n- `adk/auth.py`: `Principal` / `AuthContext` / `PrincipalResolver` (+ deny-all default).\n- `ToolRegistry.execute(name, args, auth=…)` — a default-deny authorization gate at the tool boundary, so a\n forbidden tool call is blocked regardless of what the LLM emits. `ToolDef` gains `required_clearance` /\n `action_class`. Threaded through `AitherAgent.chat`; `auth=None` is fully backward-compatible.\n- `adk/channel_auth.py`: Telegram webhook-secret + CEO-id checks and HMAC-signed approvals.\n\n## [2.6.4] - 2026-06-05\n\n### Fixes\n- **Loop-guard nudges corrupted the message history → DeepSeek 400 mid-run.**\n When the ReAct loop emitted multiple tool calls in one assistant turn and the\n loop guard fired a WARN/BLOCK/CIRCUIT_BREAK on one of them, it spliced a\n standalone `system` nudge message *between* the tool results. DeepSeek (and\n strict vLLM chat templates) require an assistant `tool_calls` message to be\n followed by exactly one `tool` result per `tool_call_id`, contiguously — so the\n interleaved message produced `400 … insufficient tool messages following\n tool_calls message` and aborted the turn (this is reliably hit after crossing\n the 15-call circuit-break threshold, where every subsequent call WARNs).\n Per-call guidance is now folded into that call's own `tool` result, and any\n standalone steering is deferred to a single message AFTER the whole batch —\n preserving the `tool_calls`↔`tool_results` pairing every backend requires.\n Regression test: `deep-research-agent/tests/test_msg_structure.py`\n (loopguard-flood scenario).\n\n## [2.6.3] - 2026-06-04\n\n### Fixes\n- **Transient provider errors killed a run on the first blip.** A single\n `502 Bad Gateway` / `503` / `529 overloaded` / `429` from the upstream API\n aborted the whole request — the Anthropic provider had **no retry at all**, and\n the OpenAI-compatible provider only retried `429`. Both now retry transient\n statuses (`408/429/500/502/503/504/529`) with `Retry-After`-aware exponential\n backoff (3 attempts), and surface the provider's body if all retries are\n exhausted. Non-transient `4xx` (e.g. `400`) still fails fast. Applies to\n `chat` and `chat_stream` on both providers.\n\n## [2.6.2] - 2026-06-04\n\n### Fixes\n- **DeepSeek (and strict OpenAI-compatible backends) 400'd on long tool\n conversations.** The ReAct loop legitimately injects mid-conversation\n `system` steering messages (the \"[DIMINISHING RETURNS]\" hint after several\n low-output tool iterations, loop-guard nudges). OpenAI tolerates a non-leading\n `system` message; DeepSeek rejects it with a 400. Long research runs\n (many `fetch_url` calls) reliably tripped this. Fix: non-leading `system`\n messages are demoted to `user` at the OpenAI-compatible provider boundary\n (`_demote_nonleading_system`) — steering intent preserved, payload valid on\n every backend; leading system prompt(s) untouched.\n- **Opaque HTTP errors.** `resp.raise_for_status()` discarded the provider's\n response body, so a 400/422 surfaced as a bare `Client error '400 Bad\n Request'` with no cause. `_ensure_ok` now includes the provider's actual\n message (e.g. `context_length_exceeded`, `Invalid 'messages'`) in the raised\n error, for both `chat` and `chat_stream`.\n\n## [2.6.1] - 2026-06-04\n\n### Fixes\n- **`MCPServer.mount()` returned HTTP 422 for every `/mcp` call.** Under\n `from __future__ import annotations` the route handler's `request: Request`\n annotation is a string that FastAPI resolves against the *module* globals, but\n `Request` was imported locally inside `mount()` — so FastAPI couldn't resolve\n it and treated `request` as a required query param. The MCP server endpoint\n (`initialize`, `tools/list`, `tools/call`) was unusable when mounted. FastAPI\n symbols are now imported at module level (guarded; the `[node]` extra still\n gates actual use). Any agent's tools can now be exposed as a working MCP\n server with `MCPServer(...).mount(app)`.\n\n## [2.6.0] - 2026-06-04\n\n### Streaming + scaffolding\n- **`AitherAgent.stream_chat`** — reliable streaming sibling of `chat()`: native\n tool-calling loop (concludes on its own, unlike the text-protocol `stream_react`)\n that emits live `tool`/`tool_result` events and streams the answer as `token`\n events. `token_delay` paces tokens for a typing effect.\n- **`adk new <template>`** — scaffold a full, runnable template app. Ships the\n `deep-research` template (a cited web-research agent: pack + server + web UI):\n `adk new deep-research`.\n\n### Fixes\n- **BYO-key no longer community-capped.** The free-tier monthly token cap now applies\n only to the metered Aitherium gateway backend, never to a user's own provider key\n (Anthropic/OpenAI/DeepSeek/Ollama/vLLM). Previously it could brick a self-hosted\n agent after ~100k tokens on the user's own key.\n- **Stronger, grounded memory recall.** `chat()` now injects the top-6 knowledge-graph\n hits (untruncated) with a hard no-fabrication instruction — was top-3 truncated to\n 200 chars, which surfaced partial facts and let weaker models invent the rest.\n- **Tool-arg coercion.** `ToolRegistry.execute` coerces LLM-supplied args toward each\n parameter's annotated type, so a tool no longer crashes when a model passes a list\n or string where an int/float/bool was expected.\n\n### Docs\n- New `docs/AGENT_DEV_GUIDE.md` — the golden path for building agents/packs.\n\n## [2.5.0] - 2026-06-04\n\n### Continual learning — agents that learn from runs and reuse memory\n\nAgents now improve the second time they do a task instead of starting from scratch.\n\n### Added\n- **Recall-before**: `AitherAgent.chat()` searches the local `SkillStore` and injects the\n top matching learned skills as a system block, so the model reuses proven procedures.\n- **Learn-after**: `_learn_after()` runs after each response — reinforces (`success_count++`)\n any skills that were recalled+reused, and extracts + saves a NEW skill from successful\n multi-tool runs via `SkillExtractor`. Wired at both `chat()` return paths.\n- Gated by `AITHER_SKILLS` (default on). The previously-unused `SkillStore`/`SkillExtractor`\n are now connected to the run loop.\n\n### Notes\n- Pairs with the platform-side LearnedProcedure substrate (recipes → scored procedures →\n skills/tools/A2A/packs → MCTS priors → evolution). The SDK shares memory via the Spirit\n bridge when configured.\n\n## [2.0.0] - 2026-06-02\n\n### Open-core boundary (BREAKING)\n\nThis release draws a single, enforced free/paid line. The free tier stays\ngenuinely useful (real agent, typed + graph + code memory, ReAct, ~essential\ntools); paid tiers unlock the *scale* capabilities via portal.aitherium.com.\n\n### Added\n- **`adk/licensing.py`** — the entitlement keystone. Resolves a tier\n (COMMUNITY by default), verifies optional portal-signed `~/.aither/license.json`\n (Ed25519), and answers `can_use_fleet/channels/cron/swarm/auto_neurons`,\n `is_agent_licensed`, `max_effort`, etc. **Fail-closed**: no/invalid/expired\n license → free tier; an unsigned license never grants premium.\n `AITHER_TENANT_SLUG=aitherium` → unrestricted INTERNAL tier.\n- **Moat guard** (`scripts/check_moat_boundary.py`) wired into the publish\n workflow — blocks any wheel that leaks the moat or drops the keystone.\n- Tests: `tests/test_licensing.py`, `tests/test_moat_boundary.py`.\n\n### Changed (gating — fail-closed)\n- Free tier effort is capped at 3 (reasoning effort 7–10 needs Professional).\n- Monthly token budget hard-block on the free tier (no silent overspend).\n- Gated behind a paid tier: fleet mode (`FleetConfig`), cross-agent delegation\n (`AgentForge.delegate`), channel adapters, cron scheduler, proactive\n auto-neurons, and `swarm`/`swarm_code` dispatch.\n\n### Removed\n- **`adk/nanogpt.py`** (on-device training IP) removed from the published SDK\n and the public repo; relocated to the internal AitherOS moat. Excluded from\n the wheel as defense-in-depth.\n- Pre-2.0 releases shipped this IP and ungated capabilities and are being\n yanked (PyPI) / removed (GitHub releases). See `scripts/yank_leaky_releases.py`\n and `scripts/purge_public_leaks.sh`.\n\n## [1.20.0] - 2026-05-27\n\n### Fixed\n- **Pack discovery path mismatch** — `_find_local_packs()` now scans both\n `~/.aither/packs/` and `~/.aitheros/packs/`, so packs installed via\n `adk pack install` are discovered by `adk-workspace`.\n\n### Added\n- **Auto-register on pack install** — `adk pack install` now auto-registers\n with the portal if the pack contains an `agent.yaml` with a `portal` section.\n One command does download + extract + register.\n- **Flux event on agent registration** — `developer_portal.py` emits\n `agent.registered` Flux event so the portal fleet UI updates in real-time.\n\n## [1.15.0] - 2026-05-26\n\n### Added\n- **Cloud memory** — `adk quickstart --cloud` now tests gateway memory and\n auto-configures Spirit URL so memories persist across devices via\n `gateway.aitherium.com/v1/memory/teach` and `/v1/memory/recall`.\n- **Config→env export for cloud mode** — `Config.from_env()` now exports\n `AITHER_CLOUD_MODE`, `AITHER_SPIRIT_URL`, and spirit path env vars from\n saved config so Memory and other modules pick them up without needing\n explicit env vars set by the user.\n- **Cloud Quick Start section** in README — 3-command setup for users who\n just want a Claude/GPT-4/DeepSeek API key and the full agent harness.\n- **`--memory` profile for node deployment** — `adk deploy node --memory`\n adds Spirit (8087) + WorkingMemory (8101) containers for persistent\n vector memory. Without this, agents use local SQLite only.\n- **Configurable vLLM in node compose** — `AITHER_VLLM_MODEL`,\n `AITHER_VLLM_GPU_UTIL`, `AITHER_VLLM_CTX_LEN`, `AITHER_VLLM_QUANT_ARGS`\n env vars let `adk setup` write GPU-detected tier settings to `.env`\n and have the node compose pick them up (TQ4 for small GPUs, BNB for large).\n\n### Fixed\n- **Memory cloud routing** — `Memory._spirit_teach()` and `_spirit_recall()`\n now use configurable paths and auth headers for gateway proxy. Previously\n cloud-only users silently lost all Spirit memory calls.\n\n## [1.9.0] - 2026-05-24\n\n### Added\n- **Composable ToolPack system** — agents can load tool packs from\n `.toolpack.yaml` directories. Drop a pack in `~/.aitheros/packs/` or\n set `AITHER_PACKS_DIR`, and any agent discovers it automatically.\n- **`tools.packs` in agent.yaml** — list tool pack IDs to auto-load\n (e.g. `packs: [crm-tools, calendar-pro]`).\n- **`register_tool_packs(agent, pack_ids, packs_dir)`** in `builtin_tools` —\n programmatic pack loading for agents.\n- **`AITHER_TOOL_PACKS` env var** — comma-separated pack IDs loaded on\n agent init (alternative to YAML config).\n- **Unknown builtin categories resolve as pack IDs** — `tools.builtin:\n [file_io, crm-tools]` now works: `crm-tools` is looked up as a pack.\n\n## [1.8.0] - 2026-05-24\n\n### Removed\n- **11 dead modules** (3,023 lines) — `agent_deploy`, `cloud_deploy`, `create`,\n `phonehome`, `sdk_bridge`, `sovereign`, `telemetry`, `telemetry_config`,\n `error_reporter`, `anonymizer`, `package`. None were imported anywhere.\n- **29 ghost exports** from `__init__.py` — `secrets`, `otel`, `fs_sandbox`,\n and `vector_memory` modules were listed in `__all__` and `__getattr__` but\n the backing files never existed. Accessing them raised `ModuleNotFoundError`.\n- **Stale dist/ artifacts** — removed old wheels/tarballs (v0.4.0–v1.4.1, 11 MB).\n- **Orphan test scripts** — `test_phase1.py` and `test_phase23.py` (one-off\n validation scripts not part of the pytest suite).\n\n### Fixed\n- **Version mismatch** — `__init__.__version__` now matches `pyproject.toml`\n (was stuck at 1.6.0).\n\n## [1.5.0] - 2026-05-23\n\n### Added\n- **`--sovereign` flag for `adk deploy node`** — registers the node with the\n Aitherium hub via federation after deployment. Saves federation credentials\n to `~/.aither/.env.federation`. Accepts `--hub` (custom hub URL) and\n `--tenant` (tenant slug) options. Non-fatal: node runs standalone if\n registration fails.\n- **Federation → fleet cross-post** (Genesis) — sovereign nodes that register\n or heartbeat via `/federation/register` and `/federation/heartbeat` now\n automatically appear in the fleet dashboard (`/fleet/deployments`) with\n `node_type: \"sovereign\"` and live status/metrics.\n- **\"Deploy Sovereign Node\" section on Connect page** (AitherVeil) — Section 8\n with ADK deploy commands, fleet dashboard links, and provision page links.\n- **`deploy-node` action in tunnel proxy** (AitherVeil) — Connect page and\n external callers can now request bootstrap scripts via\n `POST /api/tunnel/devworkspace?action=deploy-node`.\n\n### Fixed\n- **Fleet provision page** — API URL corrected from `/api/fleet/provision` to\n `/api/bridge/genesis/fleet/provision` (matches bridge proxy pattern).\n- **Fleet detail page** — API URL corrected from `/api/fleet/deployments/` to\n `/api/bridge/genesis/fleet/deployments/` (same fix).\n- **Dockerfile** — entry point corrected from `aither-serve` to `adk-serve`\n (matches `pyproject.toml` script registration). Version label updated.\n\n## [1.4.1] - 2026-05-22\n\n### Fixed\n- **macOS executable builds** — dropped `--target-arch universal2` from\n `packaging/build_executable.py`. PyPI wheels for `pydantic_core` and other\n native deps are arch-specific, not fat binaries, so universal2 always failed\n with `IncompatibleBinaryArchError`. Now builds for the runner's native arch\n (`aither-macos-arm64`).\n- **Release workflow strategy** — added `fail-fast: false` so a single\n executable build failure no longer cancels the other OS jobs.\n- **PyPI publish** — added `skip-existing: true` so re-runs of the same\n version don't fail when the artifact already exists.\n- **GitHub Release job** — now runs whenever `pypi` succeeds even if some\n executable matrix entries fail, so the release page is always created with\n whatever binaries did build successfully.\n\n## [1.4.0] - 2026-05-22\n\n### Added\n- **Sovereign template now ships with `agent_core` 5-tier memory** — generated\n backends include the canonical `agent_core/` package (working → episodic →\n semantic → procedural → identity) plus `agent_memory.py` helper, vendored\n from `portal-kit-backend`.\n- `aiosqlite>=0.20.0` added to sovereign template `requirements.txt` for\n local agent_core SQLite storage.\n- Document tombstoning: `evict_cached_conversations_referencing` is wired\n through document-delete handlers in consumer apps so memories citing\n deleted documents are blocked from recall.\n- New tests: 13 smoke tests for `portal-kit-backend/agent_core` covering\n promotion thresholds, decay rates, store roundtrip, tombstone recall\n blocking, circuit breaker state machine, and chat-turn recording.\n\n### Changed\n- `scripts/vendor_agent_core.py` now syncs canonical `agent_core` to 4\n targets: WorkspaceRuntime, ADK sovereign template, and the GargBot +\n Chelle consumer mirrors (previously only 2).\n\n## [1.2.1] - 2026-05-21\n\n### Added\n- `adk train` command group — 6 subcommands: status, launch, logs, cancel, runs, register-gpu\n- `/slash-commands` endpoint — ADK server auto-exposes all 35 CLI commands as structured JSON manifest for AitherShell\n- `/cli/execute` endpoint — AitherShell can run any CLI command through the server\n- `build_command_manifest()` — introspects full argparse tree (commands, args, choices, subcommands)\n- `_register_commands()` extracted from main() for shared parser construction\n\n### Changed\n- `_get_genesis_url()` now probes: Genesis → ADK server → Aitherium cloud gateway (works without Genesis)\n\n## [1.2.0] - 2026-05-21\n\n### Added\n- **Runtime backend switching** — `agent.switch_backend(\"deepseek\")` and `agent.llm.switch_backend()` change provider without recreating agent\n- **Hybrid reasoning** — `agent.set_reasoning_backend(\"anthropic\")` routes effort 7+ to cloud API while keeping local orchestrator\n- **First-class DeepSeek provider** — `deepseek` in effort model table with `deepseek-chat` and `deepseek-reasoner`\n- **TQ4 quantization tiers** — `nano` (6GB, TurboQuant 4-bit), `standard-tq4` (12GB, both models TQ4), `hybrid-tq4` (6GB + cloud reasoning)\n- **`adk backend` CLI** — `list`, `set`, `set-reasoning`, `test` subcommands\n- **`adk quickstart`** — unified first-run wizard (setup + auth + shell)\n- **`adk tools`** — list local + MCP tools with tier markers\n- **`adk backup`** — tarball export of `~/.aither/`\n- **`adk ingest`** — feed docs/files into knowledge graph\n- **`adk setup nemotron`** shortcut — alias for `--tier lite`\n- **`--reasoning-api`** flag in setup — `anthropic`, `openai`, `deepseek`, `gateway`\n- **`--dgx-spark URL`** flag — remote vLLM endpoint configuration\n- **DGX Spark auto-detection** — scans `spark.local`, `192.168.0.33`, `AITHER_DGX_URL` env\n- **Post-setup smoke test** — sends real inference request after container startup\n- **Schema migration system** — version table + migration runner in `memory.py` and `graph_memory.py`\n- **Configurable vLLM port scan** — `AITHER_VLLM_PORTS` env var for custom ports\n- **`aithershell` entry point** — alias for `adk` in pyproject.toml\n- Shell auto-downloads binary on first `adk shell` (no `--install` needed)\n- Shell pre-flight check warns if no backend detected\n- Shell passes full config via env vars (API key, tenant, inference URL)\n- DGX/Remote and Cloud API checks added to `adk doctor`\n- Config fields: `reasoning_backend`, `reasoning_api_key`, `deepseek_api_key`, `dgx_url`, `vllm_extra_ports`\n\n### Fixed\n- `test_aeon_chat_basic` timeout — mock scope was exiting before request\n- `adk doctor` check_disk timeout — cap rglob at 5000 files\n\n## [1.1.7] - 2026-05-19\n\n### Added\n- Remote agent pipeline — `adk onboard` → fleet dispatch → remote inference\n- Agent-to-agent federation with mesh relay\n- `adk connect` for desktop mesh enrollment\n\n## [1.1.6] - 2026-05-19\n\n### Fixed\n- Tool call execution recovery across all backends\n- Hermes tool call fallback parsing for vLLM\n\n## [1.1.5] - 2026-05-19\n\n### Fixed\n- Tool call execution + Hermes fallback parsing improvements\n\n## [1.1.0] - 2026-05-18\n\n### Added\n- Multi-channel gateway (Telegram, Discord, Slack, webhook)\n- Aeon group chat with 7 presets\n- Skills auto-extraction from multi-step sessions\n- MCP stdio server for Claude Code integration\n- `adk gateway`, `adk cron`, `adk skills`, `adk soul` commands\n- SOUL.md import/export for portable agent identity\n- Elysium cloud device-flow authentication\n\n## [1.0.0] - 2026-05-18\n\n### Breaking\n- Package consolidated — `aither_adk` namespace replaced by `adk` + `adk.platform`\n- 93 duplicate files removed from `AitherOS/packages/aither_adk/`\n\n### Added\n- Standalone core primitives (`adk.core`: Agent, Tool, Memory, ModelBackend, Capability, Trace)\n- `adk.platform` sub-package merging all platform toolkit modules\n- Compatibility shims for legacy `from aither_adk.*` imports\n\n### Changed\n- Version synced across pyproject.toml, `adk/__init__.py`, `adk/platform/__init__.py`, npm package\n- Classifier promoted from Alpha to Beta\n\n## [0.16.0] - 2026-04-16\n\n### Swarm Coding Engine & Repowise Integration\n- **`repowise_search` tool** — Semantic + keyword hybrid code search via Repowise, with ripgrep fallback\n- **`swarm_code` tool** — Dispatch complex tasks to 11-agent swarm pipeline (ARCHITECT->SWARM->REVIEW->JUDGE)\n- **`agent.swarm()`** — Async convenience method with configurable mode, effort, timeout\n- **`agent.code_search()`** — Async convenience method returning structured results\n- New tool categories: `repowise` and `swarm` in TOOL_CATEGORIES\n- `repowise_search` and `swarm_code` added to `__init__.py` exports with lazy loading\n- `IDENTITY_DEFAULTS` updated: repowise for code-focused agents, swarm for orchestration agents\n- Standalone graceful degradation: repowise falls back to ripgrep, swarm returns structured error\n\n## [0.13.0] - 2026-04-02\n\n### Graph Faculties — Local Knowledge for Every Agent\n- **CodeGraph** (2,799 lines) — Full Python AST indexer with call graph, keyword/semantic/hybrid query, embedding matrix cache, incremental re-indexing, multi-hop chain expansion\n- **MemoryGraph** (1,339 lines) — Graph-based persistent agent memory with 10 edge types, hybrid query (keyword + semantic + graph expansion), multi-hop recall, pickle persistence\n- **EmbeddingProvider** — 4-backend fallback chain: sentence-transformers (GPU/CPU) -> Ollama -> Elysium cloud -> feature hashing (zero deps)\n- **BaseFacultyGraph** — Abstract base with HMAC-SHA256 validated pickle persistence\n\n### Agent Integration\n- `agent.set_code_graph(cg)` — auto-registers `code_search` + `code_context` tools\n- `agent.set_memory_graph(mg)` — auto-registers `remember` + `recall` + `memory_stats` tools\n- Both graphs inject context into LLM prompts automatically during chat\n\n### Zero-Config Onboarding\n- `adk start` / `adk` (no args) — auto-detect project, index code, detect LLM, persistent memory, interactive chat\n- Works for any directory: Python codebases, doc folders, mixed workspaces\n- Auto-detects LLM: Ollama -> vLLM -> Elysium -> OpenAI -> Anthropic\n- Per-project persistent memory in `~/.aither/memory/<project>`\n- `adk index <path>` — standalone indexing with progress bar and stats\n\n### MCP Gateway\n- `POST /v1/embeddings` — OpenAI-compatible embedding proxy to vLLM-embeddings:8209\n- Available to all tiers (embeddings are free)\n- ADK EmbeddingProvider uses this as Elysium cloud fallback\n\n### Optional Dependencies\n- `pip install aither-adk[graphs]` — numpy for 10x cosine similarity speedup\n- `pip install aither-adk[embedding]` — sentence-transformers + torch for local GPU embeddings\n- `pip install aither-adk` alone — graphs work with feature hashing (zero deps)\n\n## [0.12.0] - 2026-04-01\n\n### Bootstrap & Service Discovery\n- Version handshake -- ADK checks major.minor compatibility with Genesis/Node\n- Background reconnect loop -- ServiceBridge re-probes every 30s in standalone mode\n- Port 8080/8090 documented -- MCP vs OpenAI-compat clearly separated\n- Genesis URL configurable via GENESIS_URL env var (was hardcoded)\n- Standalone mode warning -- visible stderr alert when AitherOS not detected\n- Auto-reconnect on startup when services come online\n\n### Elysium Cloud Inference\n- Unified gateway URL -- gateway.aitherium.com handles auth + billing + inference\n- Streaming inference -- SSE passthrough for /v1/chat/completions with stream=true\n- Auth proxy routes -- /v1/auth/register, /v1/auth/login, /v1/auth/me\n- Billing proxy -- /v1/billing/balance through gateway\n- AitherConnect Elysium fallback -- cloud inference when local Genesis is down\n- AitherDesktop Elysium fallback -- third-tier chat fallback after Node\n\n### Infrastructure\n- /discovery endpoint on Genesis -- unified service URLs/versions/health\n- /api/config/services on Veil -- runtime port config for client-side JS\n- Veil healthcheck gates on Genesis -- unhealthy when backend is down\n- Desktop crash detection 90s->60s (threshold 3->2)\n\n## [0.11.0] - 2026-04-01\n\n### Agent Execution Quality (Claude Code Parity)\n- Raise loop guard block threshold from 3 to 4 -- agents get more room for iterative search\n- Soft synthesis nudge for effort >= 4 (no tool stripping, trust the model)\n- max_output_tokens escalation -- retry up to 3x when response is truncated\n- Tool result pairing guarantee -- synthetic error for orphaned tool_use blocks\n- Micro-compaction of old tool results -- save context tokens on long sessions\n- First-turn tool forcing only for effort >= 6 (trust model for lower effort)\n- Diminishing returns detection -- nudge agent when 3+ turns produce < 500 tokens\n- Message normalization -- merge consecutive same-role messages, strip empties\n- LLM retry with exponential backoff (5 retries, 500ms-16s, jitter)\n\n## [0.9.0] - 2026-03-16\n\nThe \"connected world\" release. Cross-platform identity pairing, voice capabilities, and multi-channel integration.\n\n### Added\n- **Pairing**: Cross-platform identity linking (`adk/pairing.py`)\n - `PairingManager` — SQLite-backed identity linking with 6-char pairing codes\n - Link users across Telegram, Discord, Slack, WhatsApp with 10min TTL codes\n - Canonical session IDs for cross-channel conversation continuity\n - `get_session_id()` returns \"user-{id}\" for paired users\n- **Voice**: Speech-to-text and text-to-speech client (`adk/voice.py`)\n - `VoiceClient` — async STT/TTS/emotion via AitherVoice service\n - Convenience functions: `hear()`, `say()`, `feel()`\n - 6 voice options: alloy, echo, fable, nova, onyx, shimmer\n - Emotion detection with intensity scoring\n- New exports: `PairingManager`, `PairingResult`, `PlatformIdentity`, `VoiceClient`, `TranscriptionResult`, `SynthesisResult`, `EmotionResult`\n\n### Changed\n- `__init__.py` exports expanded with pairing and voice symbols\n\n## [0.6.0] - 2026-03-13\n\nThe \"group mind\" release. Multi-agent group chat, creative tools, and Iris identity.\n\n### Added\n- **Aeon**: Multi-agent group chat engine (`adk/aeon.py`)\n - `AeonSession` — persistent group chat with parallel agent execution\n - 7 presets: balanced, creative, technical, security, minimal, duo_code, research\n - Orchestrator synthesis: Aither summarizes all agent responses\n - Serial execution for Ollama, parallel for vLLM/cloud\n - ConversationStore persistence with `type: \"aeon\"` metadata\n - `group_chat()` one-shot convenience function\n- `aither aeon` CLI command — interactive terminal group chat with color-coded agents\n - `-p/--preset`, `-a/--agents`, `-r/--rounds`, `--no-synthesize` flags\n - `reset` and `quit` commands\n- Server endpoints: `POST /aeon/chat`, `GET /aeon/presets`, `GET /aeon/sessions/{id}`\n- Creative tools in builtin_tools: `image_generate`, `image_refine`, `image_search`, `video_generate`\n- Iris agent identity with visual generation capabilities\n- 48 Aeon tests (data models, presets, chat, context, persistence, server, exports)\n\n### Changed\n- `__init__.py` exports: `AeonSession`, `AeonResponse`, `AeonMessage`, `group_chat`, `AEON_PRESETS`\n\n## [0.5.0] - 2026-03-13\n\nThe \"tenant-ready\" release. Multi-tenant admin, permission grants, safety profiles, and a full setup wizard.\n\n### Added\n- `aither setup` interactive setup wizard with hardware detection, model selection, identity config\n- Strata storage backend: SQLite WAL persistence for conversations, memories, knowledge graphs\n- CLI test runner: `aither test` with auto-discovery and parallel execution\n- Permission grants system for MCP tool access control\n- MCP account management tools\n- LLM provider auto-detection for Ollama and vLLM endpoints\n- Apache-2.0 LICENSE file\n- Elysium desktop sync module\n- Comprehensive CLI test suite (493 tests)\n- Strata storage test suite (972 tests)\n- LLM provider tests (82 tests)\n\n### Changed\n- CLI expanded: `aither setup`, `aither test`, `aither bugreport`, `aither doctor`\n- Strata module rewritten as full local-first storage engine\n- Server startup includes Strata initialization\n- README rewritten with clearer quickstart and architecture docs\n\n### Fixed\n- Dead documentation links\n- Python 3.11 compatibility issues\n- Ruff per-file-ignores configuration\n- Identity provisioning edge cases\n- Docker node-gyp native dependency builds (python3 + build tools)\n\n## [0.4.0] - 2026-03-13\n\nThe \"own your AI\" release. Self-hosted agent OS for people who don't want their data on someone else's servers.\n\n### Added\n- Muse agent identity (creative/artistic generation)\n- Port 8120 to vLLM scan for ExoNodes discovery\n- Public roadmap with milestone-based porting schedule\n- Competitive positioning: self-hosted alternative to cloud-locked AI appliances\n\n### Changed\n- LLM router: compute-aware effort routing (replaces effort-level context gating)\n- README rewritten for sovereignty-first messaging\n- Package description updated\n- Promoted from alpha to stable release\n\n### Fixed\n- Version string consistency between pyproject.toml and __init__.py\n- vLLM port scanning now includes port 8120 (ExoNodes)\n\n## [0.3.1] - 2026-03-09\n\n### Added\n- GraphMemory: SQLite knowledge graph with Ollama embeddings and hybrid search\n- NeuronPool: auto-fire pre-LLM data gathering agents\n- NanoGPT: pure Python char-level transformer with LoRA fine-tuning\n- Safety gates: IntakeGuard (input), LoopGuard (recursion), Sandbox (code exec)\n- Event system: EventEmitter with chat/tool/forge event types\n- Builtin tools: identity-based tool selection\n- ServiceBridge: auto-discovery of AitherOS services\n- Streaming: chat_stream with safety gate integration\n- Auth middleware: Bearer token authentication\n- CLI: `aither init` and `aither serve` commands\n\n### Changed\n- Wired 5 previously disconnected modules into agent loop\n- 522 total passing tests (up from 85)\n\n## [0.3.0] - 2026-03-07\n\n### Added\n- Clean-room agent development kit\n- Multi-backend LLM providers (Ollama, OpenAI, Anthropic)\n- AitherAgent class with @tool decorator\n- SQLite conversation memory\n- OpenAI-compatible server (`aither-serve`)\n- 16 agent identities as package data\n- MCP bridge to mcp.aitherium.com\n- Privacy-centric opt-in telemetry\n- Bug reporting CLI and API\n- FastAPI server with OpenAI-compatible endpoints\n- Hardware auto-detection (5 tiers, 11 profiles)\n- Fleet orchestration and multi-agent coordination\n- A2A mesh protocol\n- Federation protocol for cross-instance agent dispatch\n\n### Initial release\n- 85 passing tests\n- Apache-2.0 license\n"}, "20c02f949f3366a03e68c5bda9e992e53ee838ef": {"t": 1785748185.7250242, "n": 67895, "h": "84ffa0012508e138", "c": "# Changelog\n\nAll notable changes to aither-adk will be documented in this file.\n\n## [2.15.0] - 2026-07-04\n\n### Added — built-in web admin console + portal-profile settings sync\n\n- **Web admin console.** The page `adk up` opens (served at `/` and `/ui`) is now a full,\n self-contained console — Chat plus six admin tabs: **Backend** (view/switch LLM provider,\n set keys, test connection), **Packs** (list/enable/disable/reload tool packs),\n **Sessions** (browse/delete conversations), **Logs** (redacted agent-log tail),\n **Graph** (knowledge-graph search/neighborhood/stats), and **MCP** (add/remove external\n MCP servers). No build step, no CDN — a single packaged HTML asset. The original minimal\n streaming chat page stays at `/chat` for back-compat.\n- **Admin API (`adk/admin_api.py`).** ~20 endpoints under `/admin/*`, all bearer-gated by the\n existing middleware (never in `_skip_auth_paths`). Operates on the live agent — LLM backend\n swap (`LLMRouter.switch_backend`), pack reload, and MCP registration take effect without a\n restart.\n- **Settings sync (`adk/settings_sync.py`).** The user's portal profile is the source of\n truth: on startup the agent pulls `preferences.adk` and applies it over the local\n `~/.aither/config.yaml` cache; admin-console edits push a debounced snapshot back to\n `PUT /api/settings/preferences`. Provider API keys and MCP auth headers are device-local\n secrets and are never included in the pushed snapshot. Opt out with `AITHER_SETTINGS_SYNC=false`.\n\n### Security\n\n- **Timing-safe token comparison.** The server bearer check now uses `hmac.compare_digest`\n (the token is reachable over the public tunnel; a byte-wise `!=` leaked it to timing attacks).\n- **MCP add is SSRF-guarded + confirmed.** Adding an external MCP server is a two-step\n prepare→confirm flow; URLs resolving to loopback / private / link-local / metadata addresses\n are rejected (override with `AITHER_ALLOW_PRIVATE_MCP=1` for trusted local servers).\n- **Config editor is allowlisted.** `PATCH /admin/config` writes only safe scalar prefs and\n hard-denies fields that could execute code or redirect traffic at startup (e.g.\n `aither_toolpack_dirs`, backend base URLs).\n- **Log tail is scrubbed.** Bearer tokens, `#k=` fragments, and `sk-`/`aither_sk_` keys are\n redacted from the log-tail endpoint; the tunnel log is excluded.\n\n## [2.14.8] - 2026-07-04\n\n### Added — `adk up` for normal humans (no API key, no prereqs, live chat)\n\n- **Hosted-brain default — no API key required.** When there's no local model and no\n provider key, `adk up` signs in (device-flow) and routes inference through the gateway\n using the portal token (`AITHER_LLM_BACKEND=gateway` + bearer). A non-technical user\n never needs to obtain or paste an LLM API key. Explicit `--provider`/local model still\n take precedence; unattended `--yes` with no token/backend fails cleanly (exit 3).\n- **cloudflared auto-download.** If the tunnel binary is missing, `adk up` downloads the\n right platform asset to `~/.aither/bin` (SHA256-verified against the release; opt out with\n `AITHER_CLOUDFLARED_REQUIRE_CHECKSUM=0`) instead of exiting with an install hint.\n- **Built-in streaming chat page.** The agent now serves a self-contained chat UI at `/`\n (and `/chat`) with a live \"thinking…\" indicator and token-by-token streaming — so a human\n has somewhere to talk to it with immediate feedback instead of a blank wait. `adk up` opens\n it locally and passes the callback bearer in the URL fragment (`#k=…`), so `/chat/stream`\n stays authenticated (not an open proxy) with nothing to paste.\n- **Human-readable failures.** `adk up` errors now carry a plain-English `next_action`\n (kept machine-readable in `--yes` JSON).\n\n### Fixed\n\n- `adk up` chat link pointed at a non-existent `/settings/agent` route; it now points at the\n agent's own streaming chat page.\n\n## [2.14.7] - 2026-07-04\n\n### Added\n\n- **`adk up` — one command to run a persistent, fleet-connected agent.** Collapses\n the scattered onboarding surface (`host`/`quickstart`/`connect`/`login`/`setup`)\n into a single command that starts `aither-serve` (single agent, identity `aither`\n by default), opens a Cloudflare quick-tunnel for fleet reachability, registers with\n the portal, and installs a reboot-persistent autostart entry (Windows Task Scheduler\n / systemd-user / launchd). Detaches by default so the terminal is freed; `--foreground`\n blocks. Reuses the existing device-flow login, cloudflared discovery, provider-key\n store, and `_preflight_check` backend detection.\n- **Fully non-interactive path for autonomous agents.** `adk up --yes` (or any\n non-TTY invocation) takes ZERO prompts — everything resolves from flags, env, and\n saved config — and emits a single machine-readable JSON status line with meaningful\n exit codes (`0` ok, `2` bad-input, `3` no-backend, `4` tunnel-missing, `5` register-failed).\n Degrades to local-only (warn, not fail) when a portal token is absent unless\n `--require-register` is set.\n- **`adk down` / `adk status --json` companions.** `down` stops the agent + tunnel,\n best-effort deregisters, and removes the autostart entry; `status` reports the running\n agent (liveness + `/health` + tunnel + registration), with `--json` for agents/CI.\n- **`adk stack`** — the previous `adk up` behaviour (supervise the Room + Ollama consumer\n stack) now lives here; `adk up` is the connected-agent command.\n\n### Internal\n\n- New `adk/agent_daemon.py`: detached process spawn, status file\n (`~/.aither/adk-up.json`), pid liveness/teardown, and cross-platform autostart install.\n\n## [2.14.6] - 2026-07-04\n\n### Added\n\n- **`adk.preflight` — self-bootstrap capability probe.** `run_preflight(agent,\n spec)` wakes an agent up knowing what it is actually plugged into: a bounded\n (1.5s) parallel `LivenessProbe` over the router's resolved backends\n (primary/reasoning), the embeddings winner, remote MCP tools, the structured-ML\n / `/ml/teach` endpoints, and voice — returning an honest `CapabilityReport`.\n Status vocabulary distinguishes reachability from entitlement\n (`OK`/`MISSING`/`UNREACHABLE`/`AUTH`/`TIER_DENIED`/`UNSUPPORTED`), a hosted slot\n never prints a bare `OK`, and a required-but-unsatisfied slot aborts before the\n loop instead of blind-firing. Headless task/rules resolution + an allowed-roots\n policy binding (`builtin_tools.set_allowed_roots`, which resets the memoized\n cache so the override actually enforces).\n- **Multimodal (image) messages.** `Message.content` now accepts an OpenAI-style\n content-part list (`str | list[dict]`), passed through verbatim to the\n OpenAI-compatible body (gateway / vLLM / OpenAI). New `adk.llm.multimodal`\n helpers — `image_message()`, `image_content_parts()`, `to_image_url()` — build\n the parts and encode local image bytes/paths into `data:` URIs.\n- **Real vision preflight probe.** The vision slot is no longer unconditionally\n `UNSUPPORTED`: it sends a discriminable image (a solid red square) and requires\n the model to name the color, so a blind/text-only model cannot false-pass. `OK`\n only on a real round-trip; `AUTH` on 401/403; `UNSUPPORTED` when there is no\n OpenAI-compatible provider or the model cannot see the image.\n\n## [2.14.5] - 2026-07-04\n\n### Added\n\n- **`adk.reasoning.mcts` — generic Monte-Carlo Tree Search library.** A reusable\n MCTS engine (ported from the internal `UnifiedMCTS`, with all internal couplings\n removed) that any agent can drive over its own environment:\n - `MCTSEnvironment` protocol (`get_state_hash` / `get_actions` / `step` /\n `evaluate` / `clone`) — implement it and search.\n - Three **optional, default-off** model seams — `TransitionModel`, `PolicyModel`\n (PUCT priors), `ValueModel` — so a learned world-model / policy / value can be\n injected without touching the core; with all three `None` the engine is a plain\n UCT search.\n - `ObservedTransitionModel` adapter — a cheap online lookup world-model (hit =\n exact stored transition; miss = identity + negative \"unknown\" reward +\n `is_uncertain`, biasing exploration toward learning), with JSONL save/load for\n cross-run reuse.\n - `UnifiedMCTS(config).search(env)` returns an `MCTSResult` (best action, value,\n confidence, principal-variation path) and an optional `MCTSTrace`\n (visit-distribution policy target) for distillation.\n- **Self-bootstrapping agent machinery.** Build an agent from a declarative spec\n instead of bespoke wiring:\n - `adk.bootstrap.build_agent_from_spec(path) -> (AitherAgent, RunCtx)` — resolves\n prompts, activates required/optional tool packs, and carries a `memory_map`\n (a `node_type -> (role, tier)` override for memory writes, never a gate).\n - `adk.prompts.PromptBridge` — resolves an agent's prompt bindings from files\n (relative to the spec) or `AitherPrompts` dot-keys (optional import).\n - `adk.packs.PackActivator` — eager activation of installed tool packs by name\n over `register_tool_packs`; a required pack that registers zero tools raises\n `PackUnavailable`, optional packs soft-degrade.\n - `python -m adk.bootstrap.generic_agent <spec.yaml>` inspects a spec (resolved\n name, activated packs + tool counts, prompt keys, memory map) without running a\n live loop.\n- **`deep_research` tool pack** — a full multi-engine web-research workflow as\n `dr_*` agent tools (keyless search, page fetch + cache, site mirror, a citable\n findings knowledge graph, and markdown/PDF/DOCX report generation), activatable\n by name via the tool-pack loader.\n\n## [2.14.4] - 2026-07-04\n\n### Added\n\n- **`structured_ml` tool domain** — zero-shot structured-data inference for agents,\n backed by TabFM (tabular classification/regression) and TimesFM (time-series\n forecasting). Three tools:\n - `tabular_classify(support_rows, target, query_rows)` — classify rows from a\n labeled support set, in-context (no training step; up to 10 classes).\n - `tabular_regress(support_rows, target, query_rows)` — predict a numeric target.\n - `timeseries_forecast(series, horizon)` — forecast future values of a series.\n\n The tools POST to a structured-ML inference service resolved from\n `AITHER_STRUCTURED_ML_URL` (default `http://localhost:8192`), reject non-http(s)\n URLs, and summarise oversized responses to protect the agent's context.\n- **`Capability.STRUCTURED_INFERENCE`** capability token and a new **`analyst`**\n identity/pack that ships the `structured_ml` domain by default. The domain is\n opt-in — only identities that enable the `structured_ml` category get the tools.\n\n## [2.14.3] - 2026-07-03\n\n### Fixed\n\nAll four bugs below were found by actually booting `adk run` against a real\nchat model and driving a real tool-calling request end-to-end (not inferred\nfrom reading the code) — each one silently broke the \"local agent uses its\nconfigured model and its tools actually work\" path.\n\n- **`AITHER_MODEL` / `config.model` silently ignored by the OpenAI-compatible\n backend**: `LLMRouter.__init__` never read `config.model` into `self._model`\n unless the caller passed `model=` directly to the constructor — which nothing\n did. Every explicit model choice (env var, config file) was dropped, the\n provider fell back to a hardcoded per-backend default (`gpt-4o-mini` for the\n `openai` backend), and an unrecognized model name got silently remapped\n server-side to the wrong backing model. Concretely: setting\n `AITHER_MODEL=aither-orchestrator` had no effect — every request actually\n went to a different model that was never meant to be called directly and\n can't reliably use tools.\n- **`OPENAI_BASE_URL` ignored for explicit `--backend`/`AITHER_LLM_BACKEND`\n selection**: `LLMRouter._auto_detect()`'s explicit-backend branch hardcoded\n `base_url=None` when constructing the provider, instead of reading\n `config.openai_base_url` (which two other call sites in the same file\n already did correctly). Any self-hosted OpenAI-compatible endpoint\n (MicroScheduler, vLLM, LM Studio) configured via `OPENAI_BASE_URL` was\n silently ignored in favor of `https://api.openai.com/v1`.\n- **Tool-call steering never engaged on default-effort chat**: the retry that\n nudges a model back into structured tool-calling when it responds with prose\n instead of a function call was gated behind `effort >= 6`. Default `effort`\n for a plain `agent.chat(message)` call resolves to `5`, so the safety net —\n built specifically for this failure mode — never fired for default requests.\n Also now forces `tool_choice=\"required\"` on the retry instead of relying on a\n text nudge alone.\n- **Every non-string builtin-tool parameter silently typed as `\"string\"` in\n the generated tool schema**: `builtin_tools.py` uses\n `from __future__ import annotations`, so `_extract_parameters()` in\n `tools.py` was reading postponed (string) type hints (`\"int\"`, not `int`)\n straight out of `fn.__annotations__`, which never matched\n `_type_to_schema`'s type-object keys and silently fell through to\n `{\"type\": \"string\"}`. Every builtin tool with an `int`/`float`/`bool`/`list`\n parameter was affected — e.g. `file_read(start_line: int, end_line: int)`\n advertised both as strings, so a model correctly calling the tool with\n `\"start_line\": \"0\"` crashed the actual implementation with\n `unsupported operand type(s) for -: 'str' and 'int'`. Fixed by resolving\n hints through `typing.get_type_hints()` instead of the raw annotations dict.\n `_coerce_arguments()` — the runtime safety net that's supposed to coerce a\n model-supplied `\"0\"` string into `0` before a tool call executes — had the\n exact same raw-`__annotations__` bug and silently no-opped for every builtin\n tool; fixed the same way.\n- **Streaming tool-loop could hang a connection forever**: `chat_stream()`'s\n fallback to the sync tool loop (tool calls can't stream token-by-token) had\n no timeout, so a model that didn't cleanly terminate a tool-calling round\n trip left the SSE connection open indefinitely with zero bytes sent. Bounded\n with a 60s `asyncio.wait_for`; on timeout the stream yields a clear message\n instead of hanging.\n\n## [2.14.2] - 2026-07-03\n\n### Fixed\n- **AitherShell session amnesia**: the REPL generated a fresh `session_id` for\n EVERY message, so conversation history never followed across turns (the\n server stored each turn under a different session). One stable id is now\n minted per shell run; `/resume` and `/new` semantics unchanged, and the\n cross-restart auto-restore of the last session now actually works.\n\n## [2.14.1] - 2026-07-02\n\n### Added\n- **Account-tied licensing via device-flow login** — `adk login` now persists the account\n license returned by the device-flow handshake to `~/.aither/license.json` (base64\n `license_key` → `{payload, signature}` envelope), so entitlements from the authenticated\n account carry into every later `adk` invocation without re-auth. Pairs with the fleet-side\n node tool-pack gate that grants packs by plan-tier rank.\n- **`can_use_formbridge` / `can_use_untether` entitlements** (PROFESSIONAL+ rank ≥ 3) backing\n the FormBridge and UNTETHER agent tool-packs.\n\n### Fixed\n- `adk` CLI: guard the optional `memory_graph` singleton (was a latent `NameError` on the\n `/stats`, `/memory`, and save paths when memory was not constructed).\n\n## [2.14.0] - 2026-07-02\n\n### Added\n- **`adk.memory_wiki` — LyraWiki-style self-managed semantic knowledge** (opt-in; nothing constructs it by default): `MemoryWiki(graph_memory)` maintains curated wiki ARTICLES as `wiki_article` nodes (title/slug, LLM-owned markdown with `[[wikilinks]]`, embedding, durable tier, `CONSOLIDATED_FROM` edges to source memories, governance-ledgered revisions).\n - `consolidate(llm, since=, budget=)` clusters unconsolidated episodic/fact nodes (embedding + keyword), drafts/UPDATEs articles via the INJECTED llm callable (`str -> str` or `messages -> str`, sync or async — no hard model dependency), cites source node ids, marks contradicted sources superseded, then DEMOTES consolidated sources to a fast-decay tier via `promote()` (represented knowledge doesn't need raw copies).\n - `lint(llm=None)` — deterministic health checks (orphan wikilinks, empty/stale articles, live-but-contradicted pairs) + an optional LLM audit pass.\n - `prune(relevance_floor=0.05, hard_delete_after_days=14)` — article relevance = tier freshness × reinforcement (reinforce-on-recall) + link-degree to LIVE memories; below the floor → reversible governance tombstone; entombed past the window → **HARD-DELETE** (the tombstone snapshot itself is purged — content and embedding irrecoverable; the true-deletion path). A node is never deleted without a tombstone first.\n - `recall(query, limit)` — article-first RAG (curated articles rank above raw nodes) that reinforces returned articles.\n- **`adk.routines` — agent heartbeat + self-programmed routines**: `RoutineStore` is a durable registry (`~/.aither/routines/{agent}.json`, `AITHER_DATA_DIR`-aware, injectable path) of cron-scheduled self-prompts `{name, cron, instruction, enabled, last_run, last_result, tags}` that rehydrates into the existing `adk.cron.CronScheduler` on `start()`. A fire runs `agent.chat(instruction)` with the agent's full toolset — the agent programs its future self with its own adk. Guardrails: `max_routines` (12), a 5-minute min fire interval per routine, a per-fire timeout, and every result ledgered (truncated) to `last_result`. Direct-callable routines (`register_direct`) fire a bound method instead of a self-prompt. Self-management tools (`routine_create/list/update/pause/resume/delete/run_now`) ship as OpenAI tool defs + handlers (`build_routine_tools` / `register_routine_tools` / `routine_tool_defs`); the handlers ONLY touch the RoutineStore — they can never modify agent config or safety-relevant settings (the leash principle).\n- **`AitherAgent(routines=True, memory_maintenance=True)`** (both default **False** → the default agent is byte-identical): `routines=True` builds the RoutineStore + scheduler, registers the self-management tools, and starts the heartbeat lazily on the first `chat()` (or via explicit `start_routines()`). `memory_maintenance=True` registers DEFAULT routines — `wiki_consolidate` (every 2 h), `wiki_lint` / `wiki_prune` / `graph_sweep` (daily) — as DIRECT method fires so memory upkeep runs even on tiny models, while staying visible/manageable via `routine_list`.\n- `TombstoneStore.purge(tombstone_id)` — the hard-delete endpoint: irrecoverably removes a tombstone snapshot and rewrites the persisted store without it (consumed by `memory_wiki.prune`).\n\n## [2.13.6] - 2026-07-02\n\n### Added\n- **Qdrant backend for GraphMemory dataplane sync — TRUE two-way, verified live.** Set `AITHER_FLEET_QDRANT_URL` and the sync uses Qdrant (`upsert` + `scroll` + filtered `search`) instead of Nexus. This is what actually delivers cross-agent memory sharing: a node is upserted with its OWN embedding under a deterministic per-(tenant,node) point id (idempotent re-push), and `fleet_pull` reliably SCROLLS a tenant's points back — the enumerate Nexus lacks (its `/search` doesn't return ingested docs and `/export` needs lancedb, which has a hard dependency conflict in the fleet). **Proven live with 2 agents:** agent-1 ingests → auto-syncs to the tenant dataplane; a fresh agent-2 in the same tenant `fleet_pull`s all of agent-1's nodes and can search them; a different tenant pulls nothing (isolation). Dimension-safe (never mixes vector dims in a collection). Local SQLite stays source of truth; best-effort, never raises.\n\n## [2.13.5] - 2026-07-02\n\n### Fixed\n- **CRITICAL — GraphMemory dataplane sync push was 100% failing.** `_fleet_push_node` sent `source_type=\"graph\"` / `content_type=\"graph_node\"`, which AitherNexus rejects as invalid enums (HTTP 500) — so NO graph node ever replicated in 2.13.4. Now sends the accepted `source_type=\"manual\"` / `content_type=\"text\"` (graph provenance is carried in `metadata.synced_from`). **Verified live** against a fleet Nexus: a 4-node ingest replicates with `pending==0` and the collection count reflects it.\n\n### Added\n- **Swarm-awareness notification** — on a successful sync batch, `GraphMemory` emits a best-effort `graph.synced` event (tenant/workspace/agent/collection/count) to `AITHER_FLEET_EVENTS_URL` so OTHER agents in the tenant/swarm can pull the fresh data and deconflict in-flight work (push-based awareness instead of polling). No-op unless the env is set.\n\n### Known limitation\n- Cross-agent **rehydration** (`fleet_pull`) is only reliable when the fleet Nexus is backed by lancedb (persistent + exportable). An in-memory Nexus stores on `/ingest` but neither its `/search` nor `/export` returns the docs — two-way read needs the persistent Nexus.\n\n## [2.13.4] - 2026-07-02\n\n### Added\n- **Memory-maintenance primitives on `GraphMemory`** (all opt-in; default behaviour byte-identical):\n - **Reinforce-on-recall**: `recall_with_activation(..., reinforce=True)` bumps `reinforcement_count`/`last_reinforced` on every RETURNED node's metadata mirror (one UPDATE per node, WAL-safe) — making the `MemoryRecord` reinforcement fields the activation scorer reads LIVE. Default `False` = zero writes.\n - **`sweep(now, archive_below=0.05, max_nodes=None, dry_run=False)`**: read-time decay enforced at rest — archives nodes past their tier TTL with freshness×reinforcement score below `archive_below` and `reinforcement_count <= 1`; `max_nodes` additionally archives the lowest-scored overflow. PERMANENT tier immune. Every archive is a reversible governance tombstone (`TombstoneStore.recover`) + a FORGET ledger entry; a node is never deleted without a tombstone. `dry_run` returns the would-archive list without acting.\n - **`promote(node_id, tier=?, role=?)`**: re-tier/re-role a node IN PLACE (tier/role columns + metadata mirror) — edges, embedding and ledger history survive; ledgers an UPDATE entry when governance is on.\n- `MutationType.UPDATE` ledger entry kind (in-place re-tier/re-role).\n- **Per-tenant dataplane sync for `GraphMemory`** — replicate the local graph to a per-tenant vector dataplane (Nexus/Qdrant) so a sovereign agent's memory is durable + rehydratable across restarts and hosting boundaries. `fleet_push_all_nodes()` (catch-up), `fleet_pull()` (best-effort semantic top-up), `fleet_sync_pending()`, and auto-sync on ingest (`_maybe_autosync` + `drain_sync()`). **On by default** (`AITHER_FLEET_SYNC=auto`; only `false/0/off` disables) with an inferred target (local Nexus `:8122` or the gateway in cloud mode) and a SEPARATE collection (`AITHER_FLEET_GRAPH_COLLECTION`, default `graph_memory`) from the KV memory sync. New `GraphMemory(tenant_id=, workspace_id=, fleet_url=, auto_sync=)` constructor args override env (required for multi-tenant host processes). Node `synced` column + schema v4 migration. TLS verify is CA-bundle-aware (`AITHER_CA_BUNDLE`) for the internal-TLS mesh. Local SQLite stays the source of truth; every sync path is best-effort and never raises into ingest.\n\n## [2.13.3] - 2026-07-02\n\n### Added\n- **Canonical self-deploying embeddings provider** (`adk.embeddings`): one embedding provider for the whole SDK so vectors are portable across scopes (platform / tenant / sovereign) — same model + dimension everywhere. Pinned to `nomic-embed-text` (768-d; the vLLM `--served-model-name` for nomic-embed-text-v1.5). Lazy, single-flight resolution chain: explicit `AITHER_EMBEDDINGS_URL` → local vLLM (`:8209` then `:8120`, HTTPS-then-HTTP) → local Ollama → gateway (`AITHER_GATEWAY_EMBEDDINGS_URL`) → auto-deploy a local vLLM embeddings container if a GPU + Docker are present (opt out with `AITHER_EMBED_AUTODEPLOY=0`) → CPU sentence-transformers (384-d, degraded) → feature-hash (384-d, degraded, always works). Every batch is dimension-tagged so callers never silently mix 768-d and 384-d vectors. `get_default_embedder()`, `embed_texts()`, `embed_one()`, `get_provider()`, `reset_provider()`.\n\n### Changed\n- **`GraphMemory` now defaults its embedder to the canonical provider** (opt out with `AITHER_GRAPH_EMBEDDER=legacy`), so every adk agent shares one 768-d space. Added a meta-table dimension guard: the index is pinned to its first embedding's dimension and later different-dimension vectors are refused (kills 768↔384 mixing that silently poisons cosine similarity).\n\n## [2.13.2] - 2026-07-01\n\n### Added\n- **Session persistence**: AitherShell now auto-saves the active session_id to `~/.aither/config.yaml` after each chat turn and auto-restores it on the next shell launch. Multi-turn conversations survive across restarts — the messages were always persisted in SQLite + JSON, only the session pointer was missing.\n- `/new` command to start a fresh session (clears saved session_id).\n- `/sessions` command to list recent sessions with agent name, message count, and timestamp. Resume any with `/resume <id>`.\n\n## [2.13.1] - 2026-07-01\n\n### Fixed\n- **CRITICAL:** `deploy_connect()` now sends a HEAD request to verify the GitHub release asset exists before downloading, with a clear error if the asset is missing.\n- **CRITICAL:** Compose file URLs are now pinned to a stable commit hash (configurable via `AITHER_COMPOSE_PIN` env) instead of tracking the mutable `main` branch.\n- **CRITICAL:** `deploy_adk_node()` now falls back to starting `adk-serve` natively when Docker is unavailable, instead of blocking with \"Docker not installed\".\n- **HIGH:** GHCR login failure now blocks deployment immediately with actionable guidance, instead of warning and continuing to a confusing image-pull failure.\n- **HIGH:** Health checks now stream the last 30 lines of Docker Compose logs on failure, so the user can see why a container didn't start.\n- **HIGH:** vLLM port allocation now probes for conflicts before assigning; if port 8200 is busy, workers are automatically remapped to the next free port.\n- **MEDIUM:** Compose file downloads are now cached with ETag; re-running `adk deploy node` skips re-downloading an unchanged file.\n- **MEDIUM:** DGX Spark discovery now respects `AITHER_DGX_HOSTS` env (comma-separated) for custom network topologies, instead of only checking `spark.local` and `192.168.0.33`.\n- **MEDIUM:** Cloud API keys (Anthropic, OpenAI, DeepSeek) are now validated with a lightweight probe during `adk setup` infra scan, so invalid keys surface early.\n- **MEDIUM:** `deploy_connect()` now validates the extracted extension has a `manifest.json` and warns if it's missing.\n\n## [2.13.0] - 2026-07-01\n\n### Removed\n- **`adk.faculties.memory_graph`** — the pickle-based `MemoryGraph` module is deleted. `from adk.faculties import MemoryGraph` now resolves to the canonical `adk.graph_memory.GraphMemory` for back-compat.\n- **`adk.platform.memory`** — the entire legacy memory subsystem (`MemoryManager`, `UnifiedMemorySystem`, `GameEngine`, `StoryboardEngine`, `AnchorGenerator`) is deleted. Agent memory is `adk.memory.Memory` + `adk.graph_memory.GraphMemory`.\n\n### Changed\n- `adk work` command now uses `GraphMemory` instead of the removed pickle-based faculty graph.\n- `adk.MemoryGraph` and `adk.faculties.MemoryGraph` lazy exports now resolve to `adk.graph_memory.GraphMemory`.\n- `agent.set_memory_graph()` kept for back-compat but documented as deprecated; agents already wire `GraphMemory` automatically in `__init__`.\n- Platform startup (`adk.platform.infrastructure.startup`) gracefully handles missing `MemoryManager`.\n- Platform CLI module listing and health check updated to reflect removed memory module.\n\n## [2.12.7] - 2026-07-01\n\n### Deprecated\n- `adk.faculties.MemoryGraph` — emits `DeprecationWarning` on instantiation; use `adk.graph_memory.GraphMemory` (SQLite-backed, governed, embedding-native) instead.\n- `adk.platform.memory` package — the legacy `MemoryManager`, `UnifiedMemorySystem`, `GameEngine`, and `StoryboardEngine` modules now emit `DeprecationWarning` on import; use `adk.memory.Memory` and `adk.graph_memory.GraphMemory` for new code.\n\n### Changed\n- Updated `adk.faculties` docstring and usage examples to point at the canonical memory surface.\n\n## [2.12.6] - 2026-07-01\n\n### Changed\n- Trimmed public package debt by removing grid runbook artifacts and shell/room binary placeholder directories from published artifacts.\n- Removed stale public-sync allowlist entries for deleted phase test files and added explicit public-sync guards for binary placeholder directories and grid artifacts.\n- Updated the public README grid note so it no longer links to runbook files excluded from the SDK package.\n\n## [2.12.5] - 2026-07-01\n\n### Added\n- Added best-effort fleet memory sync from local ADK memory into Nexus/Qdrant-compatible RAG stores, including batch catch-up and vector-search recall hooks.\n- Added saved config/env support for fleet memory URL, collection, and sync mode.\n\n### Changed\n- Excluded FormBridge runtime/test internals from public ADK packaging and public repo sync.\n\n## [2.12.4] - 2026-07-01\n\n### Added\n- Added a hot-reload endpoint for installed agent packs so newly applied pack tools can be picked up without restarting `aither-serve`.\n\n### Fixed\n- Fixed synchronous `ChatRelay` calls on Python 3.12 when no event loop is running; async WebSocket delivery is still scheduled when a loop exists.\n- Aligned gateway inference tier tests with the current public ladder: free, starter, pro, enterprise, platform.\n\n## [2.12.0] - 2026-06-28\n\n### Added — one-command local inference + upgrade path (click-to-run)\n- **`adk quickstart-local`** — detects hardware, picks a backend\n (Ollama / llama.cpp / vLLM), installs it, downloads a model, verifies, and\n prints next steps. Bootstraps Ollama itself via winget/brew/install.sh when\n missing (no \"go install it\" dead-ends). Default model `gemma4:e2b`.\n- **`adk backend switch <ollama|llamacpp|vllm>`** (+ `backend status`) — migrate\n the active deployment between engines; re-points config and smoke-tests.\n- **`adk install pack:<name>`** (+ `adk install list` / `adk packs`) — install\n ready-made agent packs (openclaw, hermes, claude-code) into `~/.aither/agents`.\n\n### Fixed (found via live end-to-end testing on Windows + CUDA)\n- quickstart-local reported success on a dead endpoint (smoke test was warn-only);\n a failed smoke now fails the command, with a service-readiness wait.\n- `--model` resolved to `None` (getattr default never fired) → uses the real default.\n- Smoke test: 30s→180s timeout for cold model load; accepts reasoning-model output\n (empty `content` at low token budgets is no longer a false failure).\n- `adk install pack:<name>` colon syntax (argparse rejected it before dispatch).\n- `adk backend status` health probe used a nonexistent `/health` path → 404; now\n probes `/v1/models` (uniform OpenAI-compatible liveness for all three backends).\n- Windows: removed non-ASCII glyphs from printed strings that crashed the cp1252\n console (a `✓` even made a successful install report failure).\n\n## [2.11.1] - 2026-06-14\n\n### Fixed — `adk host` / `adk login` device flow (autonomous onboarding)\nFound driving `adk host` against production; all broke real device-flow login:\n- **Device flow hit the portal FRONTEND, not AitherIdentity.** `adk login`/`adk host`\n POSTed `/auth/device/code` to portal/veil.aitherium.com, which 307-redirects API auth\n to `/login`. Added `_resolve_identity_url()` mapping the aitherium.com topology to\n `idp.aitherium.com` (the Identity API); localhost/bespoke pass through unchanged.\n- **Cloudflare 403'd urllib.** `idp.*` sits behind Cloudflare, which blocks urllib's\n default `Python-urllib/3.x` UA. Device-code + poll requests now send a real\n User-Agent + Accept (verified urllib→idp 200, was 403).\n- **`adk host` now self-authenticates via device flow** when there's no token, and\n **retries via device flow on a 401/403** (a stale saved token no longer dead-ends\n registration).\n\n## [2.11.0] - 2026-06-14\n\n### Added — attach your own MCP \"hands\" to a self-hosted agent\nA self-hosted agent now has the full brain (your model) · body (this loop) · **hands**\n(your tools) trifecta. The AitherOS platform relays the MCP servers you registered for\nyour agent in the `/stream` request body as `mcp_endpoints`, and the loop wires their\ntools into the ReAct turn:\n- **`adk/mcp_endpoint_tools.register_mcp_endpoint_tools(agent, endpoints)`** — speaks MCP\n JSON-RPC (`tools/list` to discover, `tools/call` to invoke) and registers a namespaced\n proxy tool (`{endpoint}__{tool}`) for each advertised tool. Mirrors `app_proxy_tools`.\n Best-effort: an unreachable endpoint registers nothing and never raises.\n- **`POST /stream` and `POST /chat/stream`** accept an optional `mcp_endpoints` list\n (`[{name, url, [headers]}]`); `_aitheros_stream` attaches them to the agent before the\n turn. Existing callers are unaffected (the field is optional).\n\n## [2.10.1] - 2026-06-14\n\n### Fixed — self-hosted `aither-serve` was crash-on-startup (2.10.0 regression)\nA fresh `pip install aither-adk && aither-serve --backend deepseek` could not boot. Found\nand fixed while standing up a real self-hosted DeepSeek agent end-to-end:\n- **Startup `NameError: name 'port' is not defined`** in three lifespan helpers\n (`_join_aithernet`, `_init_a2a_server`, `_start_mesh_hosting`) — they referenced a bare\n `port` no longer in scope. Now use `config.server_port`, and `main()` writes the resolved\n `--port`/`--host` back to `config` so every lifespan helper and the invoke_url see the\n actual bound port.\n- **Fleet self-registration crashed the whole server** — the registration handlers caught\n `(ImportError, RuntimeError, OSError, ConnectionError)` but **not** `httpx.HTTPError`, so a\n `RemoteProtocolError`/connect error talking to the control plane took the agent down. All\n network-facing lifespan handlers now also catch `httpx.HTTPError` (non-fatal, as intended).\n- **`--backend` was ignored when `AITHER_API_KEY` was set** — `_auto_detect` picked the cloud\n gateway before the explicit backend, and `_try_elysium_fallback` then replaced the chosen\n provider. An explicit backend (deepseek/openai/anthropic/your own vllm/ollama) now always\n wins; \"auto\"/\"gateway\" still flow through detection. The operator's own brain is never\n silently hijacked.\n- **Approved-tool trace truncated after `/sessions/{id}/confirm`** — the SSE relay treated\n `AgentResponse.tool_calls_made` (a `list[str]`) as dicts (`tc.get(...)`), raised\n `AttributeError`, and an over-narrow `except` swallowed it so the client saw only\n `session_start` + heartbeats. The relay now handles string tool names, the `except` is\n broad (always emits a typed `error` + terminal `complete`), and the module-level\n `_strata_ingest` `NameError` + Chronicle `session_id` kwarg mismatch are fixed.\n\n## [2.10.0] - 2026-06-13\n\n### Added — human-in-the-loop tool approval (`adk/approval.py`)\n- **Pause before a gated tool, resume on decision.** An agent can now pause its turn before\n executing a sensitive tool and wait for a human's allow/deny — so a managed control plane\n (or any operator) can gate tool use without forking the loop.\n- **Policy via `AITHER_TOOL_APPROVAL`** — a comma-separated list of tool names (optionally\n `agent:tool`), or `*` for every tool. Empty = no gating (unchanged behavior).\n- **Survives restarts / days-later approvals.** The paused turn (user message + pending tool\n calls) is persisted to disk keyed by `session_id`; resume is a fresh request, not a held\n connection. Decisions are recorded per `(session_id, tool_name)`.\n- **`AgentResponse.requires_action` / `.pending`** — set when a turn paused; `pending` lists\n the gated tool calls awaiting a decision.\n- **`AitherAgent.resume(session_id, decisions)`** — records the decisions and re-runs the\n paused turn (the gate consumes them: allowed tools execute, denied tools get a denial\n observation, the turn continues — and may pause again on a different gated tool).\n- **Server:** `POST /sessions/{id}/confirm` records decisions and streams the resumed turn;\n `/stream` emits a `requires_action` event when a turn pauses.\n\n## [2.9.0] - 2026-06-13\n\n### Added — universal LLM continuation primitive (`adk/llm/continuation.py`)\n- **`run_continuation()` + `stitch()`** — one shared continue-until-complete primitive. When a\n completion stops because it hit the output token cap (`finish_reason == \"length\"`), it continues\n the generation and STITCHES the chunks (overlap-dedup + restart-detect) into a complete answer,\n instead of every call-site hand-rolling its own retry. Bounded three ways (max rounds AND total\n output ceiling AND a no-growth break), and it never mutates the caller's message history.\n- **`LLMProvider.chat_with_continuation()`** and **`LLMRouter.chat_with_continuation()`** — every\n provider AND the router inherit the primitive, so any call-site gets continuation for free.\n- Kill-switch `ADK_LLM_CONTINUATION=off`; tunables `ADK_LLM_MAX_CONTINUATIONS`,\n `ADK_LLM_MAX_TOTAL_OUTPUT_TOKENS` (defaults: 2 rounds / 8192 tokens).\n\n### Changed\n- The ReAct loop's truncation recovery now uses the shared primitive. The previous inline\n doubling-retry REPLACED the partial response (losing earlier text in `resp.content`) and injected\n continuation scaffolding into the live message history; it now STITCHES and leaves history clean.\n Tool-call turns are never continued as text.\n\n### Included — previously-committed-but-unreleased changes\n- 2.8.2: TLS verification on by default (`verify=False` removed).\n- 2.8.1: baked license + pack-signing public keys.\n- 2.8.0: version-bump release.\n\n## [2.7.0] - 2026-06-05\n\n### Added — provider-agnostic LLM cost levers (at the `LLMProvider` ABC)\n- **Prompt caching.** New `ProviderCapabilities` (`prompt_cache: explicit|automatic|none`, `batch`) and\n normalized `LLMResponse.cache_read_tokens`/`cache_write_tokens`, so the \"tokens saved\" meter never\n depends on which backend served the turn. Anthropic inserts `cache_control` breakpoints over the stable\n system+tools prefix; OpenAI/DeepSeek caching is automatic and their `cached_tokens` are now surfaced\n (previously discarded). Default-on in `AitherAgent.chat`; override with `cache=False`.\n- **ResponseCache** (`adk/llm/cache.py`) — exact-match response cache with a pluggable backend, wired into\n `LLMRouter` as an opt-in (`LLMRouter(response_cache=…)` + per-call `cacheable=True`). OFF by default — no\n determinism change unless opted in.\n- **Gemini provider** (`adk/llm/gemini.py`) with explicit `cachedContents` caching; registered in the router.\n- **Async batch runner** (`adk/llm/batch_runner.py`) — `run_batch()` over the Anthropic Message Batches /\n OpenAI Batch APIs (~50% off) with a transparent fallback to concurrent `chat()` where unsupported.\n- **Graph-RAG retriever interface** (`adk/graph_retriever.py`).\n\n### Added — principal authority (prompt-injection defense enforced outside the model)\n- `adk/auth.py`: `Principal` / `AuthContext` / `PrincipalResolver` (+ deny-all default).\n- `ToolRegistry.execute(name, args, auth=…)` — a default-deny authorization gate at the tool boundary, so a\n forbidden tool call is blocked regardless of what the LLM emits. `ToolDef` gains `required_clearance` /\n `action_class`. Threaded through `AitherAgent.chat`; `auth=None` is fully backward-compatible.\n- `adk/channel_auth.py`: Telegram webhook-secret + CEO-id checks and HMAC-signed approvals.\n\n## [2.6.4] - 2026-06-05\n\n### Fixes\n- **Loop-guard nudges corrupted the message history → DeepSeek 400 mid-run.**\n When the ReAct loop emitted multiple tool calls in one assistant turn and the\n loop guard fired a WARN/BLOCK/CIRCUIT_BREAK on one of them, it spliced a\n standalone `system` nudge message *between* the tool results. DeepSeek (and\n strict vLLM chat templates) require an assistant `tool_calls` message to be\n followed by exactly one `tool` result per `tool_call_id`, contiguously — so the\n interleaved message produced `400 … insufficient tool messages following\n tool_calls message` and aborted the turn (this is reliably hit after crossing\n the 15-call circuit-break threshold, where every subsequent call WARNs).\n Per-call guidance is now folded into that call's own `tool` result, and any\n standalone steering is deferred to a single message AFTER the whole batch —\n preserving the `tool_calls`↔`tool_results` pairing every backend requires.\n Regression test: `deep-research-agent/tests/test_msg_structure.py`\n (loopguard-flood scenario).\n\n## [2.6.3] - 2026-06-04\n\n### Fixes\n- **Transient provider errors killed a run on the first blip.** A single\n `502 Bad Gateway` / `503` / `529 overloaded` / `429` from the upstream API\n aborted the whole request — the Anthropic provider had **no retry at all**, and\n the OpenAI-compatible provider only retried `429`. Both now retry transient\n statuses (`408/429/500/502/503/504/529`) with `Retry-After`-aware exponential\n backoff (3 attempts), and surface the provider's body if all retries are\n exhausted. Non-transient `4xx` (e.g. `400`) still fails fast. Applies to\n `chat` and `chat_stream` on both providers.\n\n## [2.6.2] - 2026-06-04\n\n### Fixes\n- **DeepSeek (and strict OpenAI-compatible backends) 400'd on long tool\n conversations.** The ReAct loop legitimately injects mid-conversation\n `system` steering messages (the \"[DIMINISHING RETURNS]\" hint after several\n low-output tool iterations, loop-guard nudges). OpenAI tolerates a non-leading\n `system` message; DeepSeek rejects it with a 400. Long research runs\n (many `fetch_url` calls) reliably tripped this. Fix: non-leading `system`\n messages are demoted to `user` at the OpenAI-compatible provider boundary\n (`_demote_nonleading_system`) — steering intent preserved, payload valid on\n every backend; leading system prompt(s) untouched.\n- **Opaque HTTP errors.** `resp.raise_for_status()` discarded the provider's\n response body, so a 400/422 surfaced as a bare `Client error '400 Bad\n Request'` with no cause. `_ensure_ok` now includes the provider's actual\n message (e.g. `context_length_exceeded`, `Invalid 'messages'`) in the raised\n error, for both `chat` and `chat_stream`.\n\n## [2.6.1] - 2026-06-04\n\n### Fixes\n- **`MCPServer.mount()` returned HTTP 422 for every `/mcp` call.** Under\n `from __future__ import annotations` the route handler's `request: Request`\n annotation is a string that FastAPI resolves against the *module* globals, but\n `Request` was imported locally inside `mount()` — so FastAPI couldn't resolve\n it and treated `request` as a required query param. The MCP server endpoint\n (`initialize`, `tools/list`, `tools/call`) was unusable when mounted. FastAPI\n symbols are now imported at module level (guarded; the `[node]` extra still\n gates actual use). Any agent's tools can now be exposed as a working MCP\n server with `MCPServer(...).mount(app)`.\n\n## [2.6.0] - 2026-06-04\n\n### Streaming + scaffolding\n- **`AitherAgent.stream_chat`** — reliable streaming sibling of `chat()`: native\n tool-calling loop (concludes on its own, unlike the text-protocol `stream_react`)\n that emits live `tool`/`tool_result` events and streams the answer as `token`\n events. `token_delay` paces tokens for a typing effect.\n- **`adk new <template>`** — scaffold a full, runnable template app. Ships the\n `deep-research` template (a cited web-research agent: pack + server + web UI):\n `adk new deep-research`.\n\n### Fixes\n- **BYO-key no longer community-capped.** The free-tier monthly token cap now applies\n only to the metered Aitherium gateway backend, never to a user's own provider key\n (Anthropic/OpenAI/DeepSeek/Ollama/vLLM). Previously it could brick a self-hosted\n agent after ~100k tokens on the user's own key.\n- **Stronger, grounded memory recall.** `chat()` now injects the top-6 knowledge-graph\n hits (untruncated) with a hard no-fabrication instruction — was top-3 truncated to\n 200 chars, which surfaced partial facts and let weaker models invent the rest.\n- **Tool-arg coercion.** `ToolRegistry.execute` coerces LLM-supplied args toward each\n parameter's annotated type, so a tool no longer crashes when a model passes a list\n or string where an int/float/bool was expected.\n\n### Docs\n- New `docs/AGENT_DEV_GUIDE.md` — the golden path for building agents/packs.\n\n## [2.5.0] - 2026-06-04\n\n### Continual learning — agents that learn from runs and reuse memory\n\nAgents now improve the second time they do a task instead of starting from scratch.\n\n### Added\n- **Recall-before**: `AitherAgent.chat()` searches the local `SkillStore` and injects the\n top matching learned skills as a system block, so the model reuses proven procedures.\n- **Learn-after**: `_learn_after()` runs after each response — reinforces (`success_count++`)\n any skills that were recalled+reused, and extracts + saves a NEW skill from successful\n multi-tool runs via `SkillExtractor`. Wired at both `chat()` return paths.\n- Gated by `AITHER_SKILLS` (default on). The previously-unused `SkillStore`/`SkillExtractor`\n are now connected to the run loop.\n\n### Notes\n- Pairs with the platform-side LearnedProcedure substrate (recipes → scored procedures →\n skills/tools/A2A/packs → MCTS priors → evolution). The SDK shares memory via the Spirit\n bridge when configured.\n\n## [2.0.0] - 2026-06-02\n\n### Open-core boundary (BREAKING)\n\nThis release draws a single, enforced free/paid line. The free tier stays\ngenuinely useful (real agent, typed + graph + code memory, ReAct, ~essential\ntools); paid tiers unlock the *scale* capabilities via portal.aitherium.com.\n\n### Added\n- **`adk/licensing.py`** — the entitlement keystone. Resolves a tier\n (COMMUNITY by default), verifies optional portal-signed `~/.aither/license.json`\n (Ed25519), and answers `can_use_fleet/channels/cron/swarm/auto_neurons`,\n `is_agent_licensed`, `max_effort`, etc. **Fail-closed**: no/invalid/expired\n license → free tier; an unsigned license never grants premium.\n `AITHER_TENANT_SLUG=aitherium` → unrestricted INTERNAL tier.\n- **Moat guard** (`scripts/check_moat_boundary.py`) wired into the publish\n workflow — blocks any wheel that leaks the moat or drops the keystone.\n- Tests: `tests/test_licensing.py`, `tests/test_moat_boundary.py`.\n\n### Changed (gating — fail-closed)\n- Free tier effort is capped at 3 (reasoning effort 7–10 needs Professional).\n- Monthly token budget hard-block on the free tier (no silent overspend).\n- Gated behind a paid tier: fleet mode (`FleetConfig`), cross-agent delegation\n (`AgentForge.delegate`), channel adapters, cron scheduler, proactive\n auto-neurons, and `swarm`/`swarm_code` dispatch.\n\n### Removed\n- **`adk/nanogpt.py`** (on-device training IP) removed from the published SDK\n and the public repo; relocated to the internal AitherOS moat. Excluded from\n the wheel as defense-in-depth.\n- Pre-2.0 releases shipped this IP and ungated capabilities and are being\n yanked (PyPI) / removed (GitHub releases). See `scripts/yank_leaky_releases.py`\n and `scripts/purge_public_leaks.sh`.\n\n## [1.20.0] - 2026-05-27\n\n### Fixed\n- **Pack discovery path mismatch** — `_find_local_packs()` now scans both\n `~/.aither/packs/` and `~/.aitheros/packs/`, so packs installed via\n `adk pack install` are discovered by `adk-workspace`.\n\n### Added\n- **Auto-register on pack install** — `adk pack install` now auto-registers\n with the portal if the pack contains an `agent.yaml` with a `portal` section.\n One command does download + extract + register.\n- **Flux event on agent registration** — `developer_portal.py` emits\n `agent.registered` Flux event so the portal fleet UI updates in real-time.\n\n## [1.15.0] - 2026-05-26\n\n### Added\n- **Cloud memory** — `adk quickstart --cloud` now tests gateway memory and\n auto-configures Spirit URL so memories persist across devices via\n `gateway.aitherium.com/v1/memory/teach` and `/v1/memory/recall`.\n- **Config→env export for cloud mode** — `Config.from_env()` now exports\n `AITHER_CLOUD_MODE`, `AITHER_SPIRIT_URL`, and spirit path env vars from\n saved config so Memory and other modules pick them up without needing\n explicit env vars set by the user.\n- **Cloud Quick Start section** in README — 3-command setup for users who\n just want a Claude/GPT-4/DeepSeek API key and the full agent harness.\n- **`--memory` profile for node deployment** — `adk deploy node --memory`\n adds Spirit (8087) + WorkingMemory (8101) containers for persistent\n vector memory. Without this, agents use local SQLite only.\n- **Configurable vLLM in node compose** — `AITHER_VLLM_MODEL`,\n `AITHER_VLLM_GPU_UTIL`, `AITHER_VLLM_CTX_LEN`, `AITHER_VLLM_QUANT_ARGS`\n env vars let `adk setup` write GPU-detected tier settings to `.env`\n and have the node compose pick them up (TQ4 for small GPUs, BNB for large).\n\n### Fixed\n- **Memory cloud routing** — `Memory._spirit_teach()` and `_spirit_recall()`\n now use configurable paths and auth headers for gateway proxy. Previously\n cloud-only users silently lost all Spirit memory calls.\n\n## [1.9.0] - 2026-05-24\n\n### Added\n- **Composable ToolPack system** — agents can load tool packs from\n `.toolpack.yaml` directories. Drop a pack in `~/.aitheros/packs/` or\n set `AITHER_PACKS_DIR`, and any agent discovers it automatically.\n- **`tools.packs` in agent.yaml** — list tool pack IDs to auto-load\n (e.g. `packs: [crm-tools, calendar-pro]`).\n- **`register_tool_packs(agent, pack_ids, packs_dir)`** in `builtin_tools` —\n programmatic pack loading for agents.\n- **`AITHER_TOOL_PACKS` env var** — comma-separated pack IDs loaded on\n agent init (alternative to YAML config).\n- **Unknown builtin categories resolve as pack IDs** — `tools.builtin:\n [file_io, crm-tools]` now works: `crm-tools` is looked up as a pack.\n\n## [1.8.0] - 2026-05-24\n\n### Removed\n- **11 dead modules** (3,023 lines) — `agent_deploy`, `cloud_deploy`, `create`,\n `phonehome`, `sdk_bridge`, `sovereign`, `telemetry`, `telemetry_config`,\n `error_reporter`, `anonymizer`, `package`. None were imported anywhere.\n- **29 ghost exports** from `__init__.py` — `secrets`, `otel`, `fs_sandbox`,\n and `vector_memory` modules were listed in `__all__` and `__getattr__` but\n the backing files never existed. Accessing them raised `ModuleNotFoundError`.\n- **Stale dist/ artifacts** — removed old wheels/tarballs (v0.4.0–v1.4.1, 11 MB).\n- **Orphan test scripts** — `test_phase1.py` and `test_phase23.py` (one-off\n validation scripts not part of the pytest suite).\n\n### Fixed\n- **Version mismatch** — `__init__.__version__` now matches `pyproject.toml`\n (was stuck at 1.6.0).\n\n## [1.5.0] - 2026-05-23\n\n### Added\n- **`--sovereign` flag for `adk deploy node`** — registers the node with the\n Aitherium hub via federation after deployment. Saves federation credentials\n to `~/.aither/.env.federation`. Accepts `--hub` (custom hub URL) and\n `--tenant` (tenant slug) options. Non-fatal: node runs standalone if\n registration fails.\n- **Federation → fleet cross-post** (Genesis) — sovereign nodes that register\n or heartbeat via `/federation/register` and `/federation/heartbeat` now\n automatically appear in the fleet dashboard (`/fleet/deployments`) with\n `node_type: \"sovereign\"` and live status/metrics.\n- **\"Deploy Sovereign Node\" section on Connect page** (AitherVeil) — Section 8\n with ADK deploy commands, fleet dashboard links, and provision page links.\n- **`deploy-node` action in tunnel proxy** (AitherVeil) — Connect page and\n external callers can now request bootstrap scripts via\n `POST /api/tunnel/devworkspace?action=deploy-node`.\n\n### Fixed\n- **Fleet provision page** — API URL corrected from `/api/fleet/provision` to\n `/api/bridge/genesis/fleet/provision` (matches bridge proxy pattern).\n- **Fleet detail page** — API URL corrected from `/api/fleet/deployments/` to\n `/api/bridge/genesis/fleet/deployments/` (same fix).\n- **Dockerfile** — entry point corrected from `aither-serve` to `adk-serve`\n (matches `pyproject.toml` script registration). Version label updated.\n\n## [1.4.1] - 2026-05-22\n\n### Fixed\n- **macOS executable builds** — dropped `--target-arch universal2` from\n `packaging/build_executable.py`. PyPI wheels for `pydantic_core` and other\n native deps are arch-specific, not fat binaries, so universal2 always failed\n with `IncompatibleBinaryArchError`. Now builds for the runner's native arch\n (`aither-macos-arm64`).\n- **Release workflow strategy** — added `fail-fast: false` so a single\n executable build failure no longer cancels the other OS jobs.\n- **PyPI publish** — added `skip-existing: true` so re-runs of the same\n version don't fail when the artifact already exists.\n- **GitHub Release job** — now runs whenever `pypi` succeeds even if some\n executable matrix entries fail, so the release page is always created with\n whatever binaries did build successfully.\n\n## [1.4.0] - 2026-05-22\n\n### Added\n- **Sovereign template now ships with `agent_core` 5-tier memory** — generated\n backends include the canonical `agent_core/` package (working → episodic →\n semantic → procedural → identity) plus `agent_memory.py` helper, vendored\n from `portal-kit-backend`.\n- `aiosqlite>=0.20.0` added to sovereign template `requirements.txt` for\n local agent_core SQLite storage.\n- Document tombstoning: `evict_cached_conversations_referencing` is wired\n through document-delete handlers in consumer apps so memories citing\n deleted documents are blocked from recall.\n- New tests: 13 smoke tests for `portal-kit-backend/agent_core` covering\n promotion thresholds, decay rates, store roundtrip, tombstone recall\n blocking, circuit breaker state machine, and chat-turn recording.\n\n### Changed\n- `scripts/vendor_agent_core.py` now syncs canonical `agent_core` to 4\n targets: WorkspaceRuntime, ADK sovereign template, and the GargBot +\n Chelle consumer mirrors (previously only 2).\n\n## [1.2.1] - 2026-05-21\n\n### Added\n- `adk train` command group — 6 subcommands: status, launch, logs, cancel, runs, register-gpu\n- `/slash-commands` endpoint — ADK server auto-exposes all 35 CLI commands as structured JSON manifest for AitherShell\n- `/cli/execute` endpoint — AitherShell can run any CLI command through the server\n- `build_command_manifest()` — introspects full argparse tree (commands, args, choices, subcommands)\n- `_register_commands()` extracted from main() for shared parser construction\n\n### Changed\n- `_get_genesis_url()` now probes: Genesis → ADK server → Aitherium cloud gateway (works without Genesis)\n\n## [1.2.0] - 2026-05-21\n\n### Added\n- **Runtime backend switching** — `agent.switch_backend(\"deepseek\")` and `agent.llm.switch_backend()` change provider without recreating agent\n- **Hybrid reasoning** — `agent.set_reasoning_backend(\"anthropic\")` routes effort 7+ to cloud API while keeping local orchestrator\n- **First-class DeepSeek provider** — `deepseek` in effort model table with `deepseek-chat` and `deepseek-reasoner`\n- **TQ4 quantization tiers** — `nano` (6GB, TurboQuant 4-bit), `standard-tq4` (12GB, both models TQ4), `hybrid-tq4` (6GB + cloud reasoning)\n- **`adk backend` CLI** — `list`, `set`, `set-reasoning`, `test` subcommands\n- **`adk quickstart`** — unified first-run wizard (setup + auth + shell)\n- **`adk tools`** — list local + MCP tools with tier markers\n- **`adk backup`** — tarball export of `~/.aither/`\n- **`adk ingest`** — feed docs/files into knowledge graph\n- **`adk setup nemotron`** shortcut — alias for `--tier lite`\n- **`--reasoning-api`** flag in setup — `anthropic`, `openai`, `deepseek`, `gateway`\n- **`--dgx-spark URL`** flag — remote vLLM endpoint configuration\n- **DGX Spark auto-detection** — scans `spark.local`, `192.168.0.33`, `AITHER_DGX_URL` env\n- **Post-setup smoke test** — sends real inference request after container startup\n- **Schema migration system** — version table + migration runner in `memory.py` and `graph_memory.py`\n- **Configurable vLLM port scan** — `AITHER_VLLM_PORTS` env var for custom ports\n- **`aithershell` entry point** — alias for `adk` in pyproject.toml\n- Shell auto-downloads binary on first `adk shell` (no `--install` needed)\n- Shell pre-flight check warns if no backend detected\n- Shell passes full config via env vars (API key, tenant, inference URL)\n- DGX/Remote and Cloud API checks added to `adk doctor`\n- Config fields: `reasoning_backend`, `reasoning_api_key`, `deepseek_api_key`, `dgx_url`, `vllm_extra_ports`\n\n### Fixed\n- `test_aeon_chat_basic` timeout — mock scope was exiting before request\n- `adk doctor` check_disk timeout — cap rglob at 5000 files\n\n## [1.1.7] - 2026-05-19\n\n### Added\n- Remote agent pipeline — `adk onboard` → fleet dispatch → remote inference\n- Agent-to-agent federation with mesh relay\n- `adk connect` for desktop mesh enrollment\n\n## [1.1.6] - 2026-05-19\n\n### Fixed\n- Tool call execution recovery across all backends\n- Hermes tool call fallback parsing for vLLM\n\n## [1.1.5] - 2026-05-19\n\n### Fixed\n- Tool call execution + Hermes fallback parsing improvements\n\n## [1.1.0] - 2026-05-18\n\n### Added\n- Multi-channel gateway (Telegram, Discord, Slack, webhook)\n- Aeon group chat with 7 presets\n- Skills auto-extraction from multi-step sessions\n- MCP stdio server for Claude Code integration\n- `adk gateway`, `adk cron`, `adk skills`, `adk soul` commands\n- SOUL.md import/export for portable agent identity\n- Elysium cloud device-flow authentication\n\n## [1.0.0] - 2026-05-18\n\n### Breaking\n- Package consolidated — `aither_adk` namespace replaced by `adk` + `adk.platform`\n- 93 duplicate files removed from `AitherOS/packages/aither_adk/`\n\n### Added\n- Standalone core primitives (`adk.core`: Agent, Tool, Memory, ModelBackend, Capability, Trace)\n- `adk.platform` sub-package merging all platform toolkit modules\n- Compatibility shims for legacy `from aither_adk.*` imports\n\n### Changed\n- Version synced across pyproject.toml, `adk/__init__.py`, `adk/platform/__init__.py`, npm package\n- Classifier promoted from Alpha to Beta\n\n## [0.16.0] - 2026-04-16\n\n### Swarm Coding Engine & Repowise Integration\n- **`repowise_search` tool** — Semantic + keyword hybrid code search via Repowise, with ripgrep fallback\n- **`swarm_code` tool** — Dispatch complex tasks to 11-agent swarm pipeline (ARCHITECT->SWARM->REVIEW->JUDGE)\n- **`agent.swarm()`** — Async convenience method with configurable mode, effort, timeout\n- **`agent.code_search()`** — Async convenience method returning structured results\n- New tool categories: `repowise` and `swarm` in TOOL_CATEGORIES\n- `repowise_search` and `swarm_code` added to `__init__.py` exports with lazy loading\n- `IDENTITY_DEFAULTS` updated: repowise for code-focused agents, swarm for orchestration agents\n- Standalone graceful degradation: repowise falls back to ripgrep, swarm returns structured error\n\n## [0.13.0] - 2026-04-02\n\n### Graph Faculties — Local Knowledge for Every Agent\n- **CodeGraph** (2,799 lines) — Full Python AST indexer with call graph, keyword/semantic/hybrid query, embedding matrix cache, incremental re-indexing, multi-hop chain expansion\n- **MemoryGraph** (1,339 lines) — Graph-based persistent agent memory with 10 edge types, hybrid query (keyword + semantic + graph expansion), multi-hop recall, pickle persistence\n- **EmbeddingProvider** — 4-backend fallback chain: sentence-transformers (GPU/CPU) -> Ollama -> Elysium cloud -> feature hashing (zero deps)\n- **BaseFacultyGraph** — Abstract base with HMAC-SHA256 validated pickle persistence\n\n### Agent Integration\n- `agent.set_code_graph(cg)` — auto-registers `code_search` + `code_context` tools\n- `agent.set_memory_graph(mg)` — auto-registers `remember` + `recall` + `memory_stats` tools\n- Both graphs inject context into LLM prompts automatically during chat\n\n### Zero-Config Onboarding\n- `adk start` / `adk` (no args) — auto-detect project, index code, detect LLM, persistent memory, interactive chat\n- Works for any directory: Python codebases, doc folders, mixed workspaces\n- Auto-detects LLM: Ollama -> vLLM -> Elysium -> OpenAI -> Anthropic\n- Per-project persistent memory in `~/.aither/memory/<project>`\n- `adk index <path>` — standalone indexing with progress bar and stats\n\n### MCP Gateway\n- `POST /v1/embeddings` — OpenAI-compatible embedding proxy to vLLM-embeddings:8209\n- Available to all tiers (embeddings are free)\n- ADK EmbeddingProvider uses this as Elysium cloud fallback\n\n### Optional Dependencies\n- `pip install aither-adk[graphs]` — numpy for 10x cosine similarity speedup\n- `pip install aither-adk[embedding]` — sentence-transformers + torch for local GPU embeddings\n- `pip install aither-adk` alone — graphs work with feature hashing (zero deps)\n\n## [0.12.0] - 2026-04-01\n\n### Bootstrap & Service Discovery\n- Version handshake -- ADK checks major.minor compatibility with Genesis/Node\n- Background reconnect loop -- ServiceBridge re-probes every 30s in standalone mode\n- Port 8080/8090 documented -- MCP vs OpenAI-compat clearly separated\n- Genesis URL configurable via GENESIS_URL env var (was hardcoded)\n- Standalone mode warning -- visible stderr alert when AitherOS not detected\n- Auto-reconnect on startup when services come online\n\n### Elysium Cloud Inference\n- Unified gateway URL -- gateway.aitherium.com handles auth + billing + inference\n- Streaming inference -- SSE passthrough for /v1/chat/completions with stream=true\n- Auth proxy routes -- /v1/auth/register, /v1/auth/login, /v1/auth/me\n- Billing proxy -- /v1/billing/balance through gateway\n- AitherConnect Elysium fallback -- cloud inference when local Genesis is down\n- AitherDesktop Elysium fallback -- third-tier chat fallback after Node\n\n### Infrastructure\n- /discovery endpoint on Genesis -- unified service URLs/versions/health\n- /api/config/services on Veil -- runtime port config for client-side JS\n- Veil healthcheck gates on Genesis -- unhealthy when backend is down\n- Desktop crash detection 90s->60s (threshold 3->2)\n\n## [0.11.0] - 2026-04-01\n\n### Agent Execution Quality (Claude Code Parity)\n- Raise loop guard block threshold from 3 to 4 -- agents get more room for iterative search\n- Soft synthesis nudge for effort >= 4 (no tool stripping, trust the model)\n- max_output_tokens escalation -- retry up to 3x when response is truncated\n- Tool result pairing guarantee -- synthetic error for orphaned tool_use blocks\n- Micro-compaction of old tool results -- save context tokens on long sessions\n- First-turn tool forcing only for effort >= 6 (trust model for lower effort)\n- Diminishing returns detection -- nudge agent when 3+ turns produce < 500 tokens\n- Message normalization -- merge consecutive same-role messages, strip empties\n- LLM retry with exponential backoff (5 retries, 500ms-16s, jitter)\n\n## [0.9.0] - 2026-03-16\n\nThe \"connected world\" release. Cross-platform identity pairing, voice capabilities, and multi-channel integration.\n\n### Added\n- **Pairing**: Cross-platform identity linking (`adk/pairing.py`)\n - `PairingManager` — SQLite-backed identity linking with 6-char pairing codes\n - Link users across Telegram, Discord, Slack, WhatsApp with 10min TTL codes\n - Canonical session IDs for cross-channel conversation continuity\n - `get_session_id()` returns \"user-{id}\" for paired users\n- **Voice**: Speech-to-text and text-to-speech client (`adk/voice.py`)\n - `VoiceClient` — async STT/TTS/emotion via AitherVoice service\n - Convenience functions: `hear()`, `say()`, `feel()`\n - 6 voice options: alloy, echo, fable, nova, onyx, shimmer\n - Emotion detection with intensity scoring\n- New exports: `PairingManager`, `PairingResult`, `PlatformIdentity`, `VoiceClient`, `TranscriptionResult`, `SynthesisResult`, `EmotionResult`\n\n### Changed\n- `__init__.py` exports expanded with pairing and voice symbols\n\n## [0.6.0] - 2026-03-13\n\nThe \"group mind\" release. Multi-agent group chat, creative tools, and Iris identity.\n\n### Added\n- **Aeon**: Multi-agent group chat engine (`adk/aeon.py`)\n - `AeonSession` — persistent group chat with parallel agent execution\n - 7 presets: balanced, creative, technical, security, minimal, duo_code, research\n - Orchestrator synthesis: Aither summarizes all agent responses\n - Serial execution for Ollama, parallel for vLLM/cloud\n - ConversationStore persistence with `type: \"aeon\"` metadata\n - `group_chat()` one-shot convenience function\n- `aither aeon` CLI command — interactive terminal group chat with color-coded agents\n - `-p/--preset`, `-a/--agents`, `-r/--rounds`, `--no-synthesize` flags\n - `reset` and `quit` commands\n- Server endpoints: `POST /aeon/chat`, `GET /aeon/presets`, `GET /aeon/sessions/{id}`\n- Creative tools in builtin_tools: `image_generate`, `image_refine`, `image_search`, `video_generate`\n- Iris agent identity with visual generation capabilities\n- 48 Aeon tests (data models, presets, chat, context, persistence, server, exports)\n\n### Changed\n- `__init__.py` exports: `AeonSession`, `AeonResponse`, `AeonMessage`, `group_chat`, `AEON_PRESETS`\n\n## [0.5.0] - 2026-03-13\n\nThe \"tenant-ready\" release. Multi-tenant admin, permission grants, safety profiles, and a full setup wizard.\n\n### Added\n- `aither setup` interactive setup wizard with hardware detection, model selection, identity config\n- Strata storage backend: SQLite WAL persistence for conversations, memories, knowledge graphs\n- CLI test runner: `aither test` with auto-discovery and parallel execution\n- Permission grants system for MCP tool access control\n- MCP account management tools\n- LLM provider auto-detection for Ollama and vLLM endpoints\n- Apache-2.0 LICENSE file\n- Elysium desktop sync module\n- Comprehensive CLI test suite (493 tests)\n- Strata storage test suite (972 tests)\n- LLM provider tests (82 tests)\n\n### Changed\n- CLI expanded: `aither setup`, `aither test`, `aither bugreport`, `aither doctor`\n- Strata module rewritten as full local-first storage engine\n- Server startup includes Strata initialization\n- README rewritten with clearer quickstart and architecture docs\n\n### Fixed\n- Dead documentation links\n- Python 3.11 compatibility issues\n- Ruff per-file-ignores configuration\n- Identity provisioning edge cases\n- Docker node-gyp native dependency builds (python3 + build tools)\n\n## [0.4.0] - 2026-03-13\n\nThe \"own your AI\" release. Self-hosted agent OS for people who don't want their data on someone else's servers.\n\n### Added\n- Muse agent identity (creative/artistic generation)\n- Port 8120 to vLLM scan for ExoNodes discovery\n- Public roadmap with milestone-based porting schedule\n- Competitive positioning: self-hosted alternative to cloud-locked AI appliances\n\n### Changed\n- LLM router: compute-aware effort routing (replaces effort-level context gating)\n- README rewritten for sovereignty-first messaging\n- Package description updated\n- Promoted from alpha to stable release\n\n### Fixed\n- Version string consistency between pyproject.toml and __init__.py\n- vLLM port scanning now includes port 8120 (ExoNodes)\n\n## [0.3.1] - 2026-03-09\n\n### Added\n- GraphMemory: SQLite knowledge graph with Ollama embeddings and hybrid search\n- NeuronPool: auto-fire pre-LLM data gathering agents\n- NanoGPT: pure Python char-level transformer with LoRA fine-tuning\n- Safety gates: IntakeGuard (input), LoopGuard (recursion), Sandbox (code exec)\n- Event system: EventEmitter with chat/tool/forge event types\n- Builtin tools: identity-based tool selection\n- ServiceBridge: auto-discovery of AitherOS services\n- Streaming: chat_stream with safety gate integration\n- Auth middleware: Bearer token authentication\n- CLI: `aither init` and `aither serve` commands\n\n### Changed\n- Wired 5 previously disconnected modules into agent loop\n- 522 total passing tests (up from 85)\n\n## [0.3.0] - 2026-03-07\n\n### Added\n- Clean-room agent development kit\n- Multi-backend LLM providers (Ollama, OpenAI, Anthropic)\n- AitherAgent class with @tool decorator\n- SQLite conversation memory\n- OpenAI-compatible server (`aither-serve`)\n- 16 agent identities as package data\n- MCP bridge to mcp.aitherium.com\n- Privacy-centric opt-in telemetry\n- Bug reporting CLI and API\n- FastAPI server with OpenAI-compatible endpoints\n- Hardware auto-detection (5 tiers, 11 profiles)\n- Fleet orchestration and multi-agent coordination\n- A2A mesh protocol\n- Federation protocol for cross-instance agent dispatch\n\n### Initial release\n- 85 passing tests\n- Apache-2.0 license\n"}, "d3fe8a224db4177c0d68d72749387b5faba0b84d": {"t": 1785748187.1387196, "n": 65621, "h": "ed86d01da5c91159", "c": "# Changelog\n\nAll notable changes to aither-adk will be documented in this file.\n\n## [2.14.8] - 2026-07-04\n\n### Added — `adk up` for normal humans (no API key, no prereqs, live chat)\n\n- **Hosted-brain default — no API key required.** When there's no local model and no\n provider key, `adk up` signs in (device-flow) and routes inference through the gateway\n using the portal token (`AITHER_LLM_BACKEND=gateway` + bearer). A non-technical user\n never needs to obtain or paste an LLM API key. Explicit `--provider`/local model still\n take precedence; unattended `--yes` with no token/backend fails cleanly (exit 3).\n- **cloudflared auto-download.** If the tunnel binary is missing, `adk up` downloads the\n right platform asset to `~/.aither/bin` (SHA256-verified against the release; opt out with\n `AITHER_CLOUDFLARED_REQUIRE_CHECKSUM=0`) instead of exiting with an install hint.\n- **Built-in streaming chat page.** The agent now serves a self-contained chat UI at `/`\n (and `/chat`) with a live \"thinking…\" indicator and token-by-token streaming — so a human\n has somewhere to talk to it with immediate feedback instead of a blank wait. `adk up` opens\n it locally and passes the callback bearer in the URL fragment (`#k=…`), so `/chat/stream`\n stays authenticated (not an open proxy) with nothing to paste.\n- **Human-readable failures.** `adk up` errors now carry a plain-English `next_action`\n (kept machine-readable in `--yes` JSON).\n\n### Fixed\n\n- `adk up` chat link pointed at a non-existent `/settings/agent` route; it now points at the\n agent's own streaming chat page.\n\n## [2.14.7] - 2026-07-04\n\n### Added\n\n- **`adk up` — one command to run a persistent, fleet-connected agent.** Collapses\n the scattered onboarding surface (`host`/`quickstart`/`connect`/`login`/`setup`)\n into a single command that starts `aither-serve` (single agent, identity `aither`\n by default), opens a Cloudflare quick-tunnel for fleet reachability, registers with\n the portal, and installs a reboot-persistent autostart entry (Windows Task Scheduler\n / systemd-user / launchd). Detaches by default so the terminal is freed; `--foreground`\n blocks. Reuses the existing device-flow login, cloudflared discovery, provider-key\n store, and `_preflight_check` backend detection.\n- **Fully non-interactive path for autonomous agents.** `adk up --yes` (or any\n non-TTY invocation) takes ZERO prompts — everything resolves from flags, env, and\n saved config — and emits a single machine-readable JSON status line with meaningful\n exit codes (`0` ok, `2` bad-input, `3` no-backend, `4` tunnel-missing, `5` register-failed).\n Degrades to local-only (warn, not fail) when a portal token is absent unless\n `--require-register` is set.\n- **`adk down` / `adk status --json` companions.** `down` stops the agent + tunnel,\n best-effort deregisters, and removes the autostart entry; `status` reports the running\n agent (liveness + `/health` + tunnel + registration), with `--json` for agents/CI.\n- **`adk stack`** — the previous `adk up` behaviour (supervise the Room + Ollama consumer\n stack) now lives here; `adk up` is the connected-agent command.\n\n### Internal\n\n- New `adk/agent_daemon.py`: detached process spawn, status file\n (`~/.aither/adk-up.json`), pid liveness/teardown, and cross-platform autostart install.\n\n## [2.14.6] - 2026-07-04\n\n### Added\n\n- **`adk.preflight` — self-bootstrap capability probe.** `run_preflight(agent,\n spec)` wakes an agent up knowing what it is actually plugged into: a bounded\n (1.5s) parallel `LivenessProbe` over the router's resolved backends\n (primary/reasoning), the embeddings winner, remote MCP tools, the structured-ML\n / `/ml/teach` endpoints, and voice — returning an honest `CapabilityReport`.\n Status vocabulary distinguishes reachability from entitlement\n (`OK`/`MISSING`/`UNREACHABLE`/`AUTH`/`TIER_DENIED`/`UNSUPPORTED`), a hosted slot\n never prints a bare `OK`, and a required-but-unsatisfied slot aborts before the\n loop instead of blind-firing. Headless task/rules resolution + an allowed-roots\n policy binding (`builtin_tools.set_allowed_roots`, which resets the memoized\n cache so the override actually enforces).\n- **Multimodal (image) messages.** `Message.content` now accepts an OpenAI-style\n content-part list (`str | list[dict]`), passed through verbatim to the\n OpenAI-compatible body (gateway / vLLM / OpenAI). New `adk.llm.multimodal`\n helpers — `image_message()`, `image_content_parts()`, `to_image_url()` — build\n the parts and encode local image bytes/paths into `data:` URIs.\n- **Real vision preflight probe.** The vision slot is no longer unconditionally\n `UNSUPPORTED`: it sends a discriminable image (a solid red square) and requires\n the model to name the color, so a blind/text-only model cannot false-pass. `OK`\n only on a real round-trip; `AUTH` on 401/403; `UNSUPPORTED` when there is no\n OpenAI-compatible provider or the model cannot see the image.\n\n## [2.14.5] - 2026-07-04\n\n### Added\n\n- **`adk.reasoning.mcts` — generic Monte-Carlo Tree Search library.** A reusable\n MCTS engine (ported from the internal `UnifiedMCTS`, with all internal couplings\n removed) that any agent can drive over its own environment:\n - `MCTSEnvironment` protocol (`get_state_hash` / `get_actions` / `step` /\n `evaluate` / `clone`) — implement it and search.\n - Three **optional, default-off** model seams — `TransitionModel`, `PolicyModel`\n (PUCT priors), `ValueModel` — so a learned world-model / policy / value can be\n injected without touching the core; with all three `None` the engine is a plain\n UCT search.\n - `ObservedTransitionModel` adapter — a cheap online lookup world-model (hit =\n exact stored transition; miss = identity + negative \"unknown\" reward +\n `is_uncertain`, biasing exploration toward learning), with JSONL save/load for\n cross-run reuse.\n - `UnifiedMCTS(config).search(env)` returns an `MCTSResult` (best action, value,\n confidence, principal-variation path) and an optional `MCTSTrace`\n (visit-distribution policy target) for distillation.\n- **Self-bootstrapping agent machinery.** Build an agent from a declarative spec\n instead of bespoke wiring:\n - `adk.bootstrap.build_agent_from_spec(path) -> (AitherAgent, RunCtx)` — resolves\n prompts, activates required/optional tool packs, and carries a `memory_map`\n (a `node_type -> (role, tier)` override for memory writes, never a gate).\n - `adk.prompts.PromptBridge` — resolves an agent's prompt bindings from files\n (relative to the spec) or `AitherPrompts` dot-keys (optional import).\n - `adk.packs.PackActivator` — eager activation of installed tool packs by name\n over `register_tool_packs`; a required pack that registers zero tools raises\n `PackUnavailable`, optional packs soft-degrade.\n - `python -m adk.bootstrap.generic_agent <spec.yaml>` inspects a spec (resolved\n name, activated packs + tool counts, prompt keys, memory map) without running a\n live loop.\n- **`deep_research` tool pack** — a full multi-engine web-research workflow as\n `dr_*` agent tools (keyless search, page fetch + cache, site mirror, a citable\n findings knowledge graph, and markdown/PDF/DOCX report generation), activatable\n by name via the tool-pack loader.\n\n## [2.14.4] - 2026-07-04\n\n### Added\n\n- **`structured_ml` tool domain** — zero-shot structured-data inference for agents,\n backed by TabFM (tabular classification/regression) and TimesFM (time-series\n forecasting). Three tools:\n - `tabular_classify(support_rows, target, query_rows)` — classify rows from a\n labeled support set, in-context (no training step; up to 10 classes).\n - `tabular_regress(support_rows, target, query_rows)` — predict a numeric target.\n - `timeseries_forecast(series, horizon)` — forecast future values of a series.\n\n The tools POST to a structured-ML inference service resolved from\n `AITHER_STRUCTURED_ML_URL` (default `http://localhost:8192`), reject non-http(s)\n URLs, and summarise oversized responses to protect the agent's context.\n- **`Capability.STRUCTURED_INFERENCE`** capability token and a new **`analyst`**\n identity/pack that ships the `structured_ml` domain by default. The domain is\n opt-in — only identities that enable the `structured_ml` category get the tools.\n\n## [2.14.3] - 2026-07-03\n\n### Fixed\n\nAll four bugs below were found by actually booting `adk run` against a real\nchat model and driving a real tool-calling request end-to-end (not inferred\nfrom reading the code) — each one silently broke the \"local agent uses its\nconfigured model and its tools actually work\" path.\n\n- **`AITHER_MODEL` / `config.model` silently ignored by the OpenAI-compatible\n backend**: `LLMRouter.__init__` never read `config.model` into `self._model`\n unless the caller passed `model=` directly to the constructor — which nothing\n did. Every explicit model choice (env var, config file) was dropped, the\n provider fell back to a hardcoded per-backend default (`gpt-4o-mini` for the\n `openai` backend), and an unrecognized model name got silently remapped\n server-side to the wrong backing model. Concretely: setting\n `AITHER_MODEL=aither-orchestrator` had no effect — every request actually\n went to a different model that was never meant to be called directly and\n can't reliably use tools.\n- **`OPENAI_BASE_URL` ignored for explicit `--backend`/`AITHER_LLM_BACKEND`\n selection**: `LLMRouter._auto_detect()`'s explicit-backend branch hardcoded\n `base_url=None` when constructing the provider, instead of reading\n `config.openai_base_url` (which two other call sites in the same file\n already did correctly). Any self-hosted OpenAI-compatible endpoint\n (MicroScheduler, vLLM, LM Studio) configured via `OPENAI_BASE_URL` was\n silently ignored in favor of `https://api.openai.com/v1`.\n- **Tool-call steering never engaged on default-effort chat**: the retry that\n nudges a model back into structured tool-calling when it responds with prose\n instead of a function call was gated behind `effort >= 6`. Default `effort`\n for a plain `agent.chat(message)` call resolves to `5`, so the safety net —\n built specifically for this failure mode — never fired for default requests.\n Also now forces `tool_choice=\"required\"` on the retry instead of relying on a\n text nudge alone.\n- **Every non-string builtin-tool parameter silently typed as `\"string\"` in\n the generated tool schema**: `builtin_tools.py` uses\n `from __future__ import annotations`, so `_extract_parameters()` in\n `tools.py` was reading postponed (string) type hints (`\"int\"`, not `int`)\n straight out of `fn.__annotations__`, which never matched\n `_type_to_schema`'s type-object keys and silently fell through to\n `{\"type\": \"string\"}`. Every builtin tool with an `int`/`float`/`bool`/`list`\n parameter was affected — e.g. `file_read(start_line: int, end_line: int)`\n advertised both as strings, so a model correctly calling the tool with\n `\"start_line\": \"0\"` crashed the actual implementation with\n `unsupported operand type(s) for -: 'str' and 'int'`. Fixed by resolving\n hints through `typing.get_type_hints()` instead of the raw annotations dict.\n `_coerce_arguments()` — the runtime safety net that's supposed to coerce a\n model-supplied `\"0\"` string into `0` before a tool call executes — had the\n exact same raw-`__annotations__` bug and silently no-opped for every builtin\n tool; fixed the same way.\n- **Streaming tool-loop could hang a connection forever**: `chat_stream()`'s\n fallback to the sync tool loop (tool calls can't stream token-by-token) had\n no timeout, so a model that didn't cleanly terminate a tool-calling round\n trip left the SSE connection open indefinitely with zero bytes sent. Bounded\n with a 60s `asyncio.wait_for`; on timeout the stream yields a clear message\n instead of hanging.\n\n## [2.14.2] - 2026-07-03\n\n### Fixed\n- **AitherShell session amnesia**: the REPL generated a fresh `session_id` for\n EVERY message, so conversation history never followed across turns (the\n server stored each turn under a different session). One stable id is now\n minted per shell run; `/resume` and `/new` semantics unchanged, and the\n cross-restart auto-restore of the last session now actually works.\n\n## [2.14.1] - 2026-07-02\n\n### Added\n- **Account-tied licensing via device-flow login** — `adk login` now persists the account\n license returned by the device-flow handshake to `~/.aither/license.json` (base64\n `license_key` → `{payload, signature}` envelope), so entitlements from the authenticated\n account carry into every later `adk` invocation without re-auth. Pairs with the fleet-side\n node tool-pack gate that grants packs by plan-tier rank.\n- **`can_use_formbridge` / `can_use_untether` entitlements** (PROFESSIONAL+ rank ≥ 3) backing\n the FormBridge and UNTETHER agent tool-packs.\n\n### Fixed\n- `adk` CLI: guard the optional `memory_graph` singleton (was a latent `NameError` on the\n `/stats`, `/memory`, and save paths when memory was not constructed).\n\n## [2.14.0] - 2026-07-02\n\n### Added\n- **`adk.memory_wiki` — LyraWiki-style self-managed semantic knowledge** (opt-in; nothing constructs it by default): `MemoryWiki(graph_memory)` maintains curated wiki ARTICLES as `wiki_article` nodes (title/slug, LLM-owned markdown with `[[wikilinks]]`, embedding, durable tier, `CONSOLIDATED_FROM` edges to source memories, governance-ledgered revisions).\n - `consolidate(llm, since=, budget=)` clusters unconsolidated episodic/fact nodes (embedding + keyword), drafts/UPDATEs articles via the INJECTED llm callable (`str -> str` or `messages -> str`, sync or async — no hard model dependency), cites source node ids, marks contradicted sources superseded, then DEMOTES consolidated sources to a fast-decay tier via `promote()` (represented knowledge doesn't need raw copies).\n - `lint(llm=None)` — deterministic health checks (orphan wikilinks, empty/stale articles, live-but-contradicted pairs) + an optional LLM audit pass.\n - `prune(relevance_floor=0.05, hard_delete_after_days=14)` — article relevance = tier freshness × reinforcement (reinforce-on-recall) + link-degree to LIVE memories; below the floor → reversible governance tombstone; entombed past the window → **HARD-DELETE** (the tombstone snapshot itself is purged — content and embedding irrecoverable; the true-deletion path). A node is never deleted without a tombstone first.\n - `recall(query, limit)` — article-first RAG (curated articles rank above raw nodes) that reinforces returned articles.\n- **`adk.routines` — agent heartbeat + self-programmed routines**: `RoutineStore` is a durable registry (`~/.aither/routines/{agent}.json`, `AITHER_DATA_DIR`-aware, injectable path) of cron-scheduled self-prompts `{name, cron, instruction, enabled, last_run, last_result, tags}` that rehydrates into the existing `adk.cron.CronScheduler` on `start()`. A fire runs `agent.chat(instruction)` with the agent's full toolset — the agent programs its future self with its own adk. Guardrails: `max_routines` (12), a 5-minute min fire interval per routine, a per-fire timeout, and every result ledgered (truncated) to `last_result`. Direct-callable routines (`register_direct`) fire a bound method instead of a self-prompt. Self-management tools (`routine_create/list/update/pause/resume/delete/run_now`) ship as OpenAI tool defs + handlers (`build_routine_tools` / `register_routine_tools` / `routine_tool_defs`); the handlers ONLY touch the RoutineStore — they can never modify agent config or safety-relevant settings (the leash principle).\n- **`AitherAgent(routines=True, memory_maintenance=True)`** (both default **False** → the default agent is byte-identical): `routines=True` builds the RoutineStore + scheduler, registers the self-management tools, and starts the heartbeat lazily on the first `chat()` (or via explicit `start_routines()`). `memory_maintenance=True` registers DEFAULT routines — `wiki_consolidate` (every 2 h), `wiki_lint` / `wiki_prune` / `graph_sweep` (daily) — as DIRECT method fires so memory upkeep runs even on tiny models, while staying visible/manageable via `routine_list`.\n- `TombstoneStore.purge(tombstone_id)` — the hard-delete endpoint: irrecoverably removes a tombstone snapshot and rewrites the persisted store without it (consumed by `memory_wiki.prune`).\n\n## [2.13.6] - 2026-07-02\n\n### Added\n- **Qdrant backend for GraphMemory dataplane sync — TRUE two-way, verified live.** Set `AITHER_FLEET_QDRANT_URL` and the sync uses Qdrant (`upsert` + `scroll` + filtered `search`) instead of Nexus. This is what actually delivers cross-agent memory sharing: a node is upserted with its OWN embedding under a deterministic per-(tenant,node) point id (idempotent re-push), and `fleet_pull` reliably SCROLLS a tenant's points back — the enumerate Nexus lacks (its `/search` doesn't return ingested docs and `/export` needs lancedb, which has a hard dependency conflict in the fleet). **Proven live with 2 agents:** agent-1 ingests → auto-syncs to the tenant dataplane; a fresh agent-2 in the same tenant `fleet_pull`s all of agent-1's nodes and can search them; a different tenant pulls nothing (isolation). Dimension-safe (never mixes vector dims in a collection). Local SQLite stays source of truth; best-effort, never raises.\n\n## [2.13.5] - 2026-07-02\n\n### Fixed\n- **CRITICAL — GraphMemory dataplane sync push was 100% failing.** `_fleet_push_node` sent `source_type=\"graph\"` / `content_type=\"graph_node\"`, which AitherNexus rejects as invalid enums (HTTP 500) — so NO graph node ever replicated in 2.13.4. Now sends the accepted `source_type=\"manual\"` / `content_type=\"text\"` (graph provenance is carried in `metadata.synced_from`). **Verified live** against a fleet Nexus: a 4-node ingest replicates with `pending==0` and the collection count reflects it.\n\n### Added\n- **Swarm-awareness notification** — on a successful sync batch, `GraphMemory` emits a best-effort `graph.synced` event (tenant/workspace/agent/collection/count) to `AITHER_FLEET_EVENTS_URL` so OTHER agents in the tenant/swarm can pull the fresh data and deconflict in-flight work (push-based awareness instead of polling). No-op unless the env is set.\n\n### Known limitation\n- Cross-agent **rehydration** (`fleet_pull`) is only reliable when the fleet Nexus is backed by lancedb (persistent + exportable). An in-memory Nexus stores on `/ingest` but neither its `/search` nor `/export` returns the docs — two-way read needs the persistent Nexus.\n\n## [2.13.4] - 2026-07-02\n\n### Added\n- **Memory-maintenance primitives on `GraphMemory`** (all opt-in; default behaviour byte-identical):\n - **Reinforce-on-recall**: `recall_with_activation(..., reinforce=True)` bumps `reinforcement_count`/`last_reinforced` on every RETURNED node's metadata mirror (one UPDATE per node, WAL-safe) — making the `MemoryRecord` reinforcement fields the activation scorer reads LIVE. Default `False` = zero writes.\n - **`sweep(now, archive_below=0.05, max_nodes=None, dry_run=False)`**: read-time decay enforced at rest — archives nodes past their tier TTL with freshness×reinforcement score below `archive_below` and `reinforcement_count <= 1`; `max_nodes` additionally archives the lowest-scored overflow. PERMANENT tier immune. Every archive is a reversible governance tombstone (`TombstoneStore.recover`) + a FORGET ledger entry; a node is never deleted without a tombstone. `dry_run` returns the would-archive list without acting.\n - **`promote(node_id, tier=?, role=?)`**: re-tier/re-role a node IN PLACE (tier/role columns + metadata mirror) — edges, embedding and ledger history survive; ledgers an UPDATE entry when governance is on.\n- `MutationType.UPDATE` ledger entry kind (in-place re-tier/re-role).\n- **Per-tenant dataplane sync for `GraphMemory`** — replicate the local graph to a per-tenant vector dataplane (Nexus/Qdrant) so a sovereign agent's memory is durable + rehydratable across restarts and hosting boundaries. `fleet_push_all_nodes()` (catch-up), `fleet_pull()` (best-effort semantic top-up), `fleet_sync_pending()`, and auto-sync on ingest (`_maybe_autosync` + `drain_sync()`). **On by default** (`AITHER_FLEET_SYNC=auto`; only `false/0/off` disables) with an inferred target (local Nexus `:8122` or the gateway in cloud mode) and a SEPARATE collection (`AITHER_FLEET_GRAPH_COLLECTION`, default `graph_memory`) from the KV memory sync. New `GraphMemory(tenant_id=, workspace_id=, fleet_url=, auto_sync=)` constructor args override env (required for multi-tenant host processes). Node `synced` column + schema v4 migration. TLS verify is CA-bundle-aware (`AITHER_CA_BUNDLE`) for the internal-TLS mesh. Local SQLite stays the source of truth; every sync path is best-effort and never raises into ingest.\n\n## [2.13.3] - 2026-07-02\n\n### Added\n- **Canonical self-deploying embeddings provider** (`adk.embeddings`): one embedding provider for the whole SDK so vectors are portable across scopes (platform / tenant / sovereign) — same model + dimension everywhere. Pinned to `nomic-embed-text` (768-d; the vLLM `--served-model-name` for nomic-embed-text-v1.5). Lazy, single-flight resolution chain: explicit `AITHER_EMBEDDINGS_URL` → local vLLM (`:8209` then `:8120`, HTTPS-then-HTTP) → local Ollama → gateway (`AITHER_GATEWAY_EMBEDDINGS_URL`) → auto-deploy a local vLLM embeddings container if a GPU + Docker are present (opt out with `AITHER_EMBED_AUTODEPLOY=0`) → CPU sentence-transformers (384-d, degraded) → feature-hash (384-d, degraded, always works). Every batch is dimension-tagged so callers never silently mix 768-d and 384-d vectors. `get_default_embedder()`, `embed_texts()`, `embed_one()`, `get_provider()`, `reset_provider()`.\n\n### Changed\n- **`GraphMemory` now defaults its embedder to the canonical provider** (opt out with `AITHER_GRAPH_EMBEDDER=legacy`), so every adk agent shares one 768-d space. Added a meta-table dimension guard: the index is pinned to its first embedding's dimension and later different-dimension vectors are refused (kills 768↔384 mixing that silently poisons cosine similarity).\n\n## [2.13.2] - 2026-07-01\n\n### Added\n- **Session persistence**: AitherShell now auto-saves the active session_id to `~/.aither/config.yaml` after each chat turn and auto-restores it on the next shell launch. Multi-turn conversations survive across restarts — the messages were always persisted in SQLite + JSON, only the session pointer was missing.\n- `/new` command to start a fresh session (clears saved session_id).\n- `/sessions` command to list recent sessions with agent name, message count, and timestamp. Resume any with `/resume <id>`.\n\n## [2.13.1] - 2026-07-01\n\n### Fixed\n- **CRITICAL:** `deploy_connect()` now sends a HEAD request to verify the GitHub release asset exists before downloading, with a clear error if the asset is missing.\n- **CRITICAL:** Compose file URLs are now pinned to a stable commit hash (configurable via `AITHER_COMPOSE_PIN` env) instead of tracking the mutable `main` branch.\n- **CRITICAL:** `deploy_adk_node()` now falls back to starting `adk-serve` natively when Docker is unavailable, instead of blocking with \"Docker not installed\".\n- **HIGH:** GHCR login failure now blocks deployment immediately with actionable guidance, instead of warning and continuing to a confusing image-pull failure.\n- **HIGH:** Health checks now stream the last 30 lines of Docker Compose logs on failure, so the user can see why a container didn't start.\n- **HIGH:** vLLM port allocation now probes for conflicts before assigning; if port 8200 is busy, workers are automatically remapped to the next free port.\n- **MEDIUM:** Compose file downloads are now cached with ETag; re-running `adk deploy node` skips re-downloading an unchanged file.\n- **MEDIUM:** DGX Spark discovery now respects `AITHER_DGX_HOSTS` env (comma-separated) for custom network topologies, instead of only checking `spark.local` and `192.168.0.33`.\n- **MEDIUM:** Cloud API keys (Anthropic, OpenAI, DeepSeek) are now validated with a lightweight probe during `adk setup` infra scan, so invalid keys surface early.\n- **MEDIUM:** `deploy_connect()` now validates the extracted extension has a `manifest.json` and warns if it's missing.\n\n## [2.13.0] - 2026-07-01\n\n### Removed\n- **`adk.faculties.memory_graph`** — the pickle-based `MemoryGraph` module is deleted. `from adk.faculties import MemoryGraph` now resolves to the canonical `adk.graph_memory.GraphMemory` for back-compat.\n- **`adk.platform.memory`** — the entire legacy memory subsystem (`MemoryManager`, `UnifiedMemorySystem`, `GameEngine`, `StoryboardEngine`, `AnchorGenerator`) is deleted. Agent memory is `adk.memory.Memory` + `adk.graph_memory.GraphMemory`.\n\n### Changed\n- `adk work` command now uses `GraphMemory` instead of the removed pickle-based faculty graph.\n- `adk.MemoryGraph` and `adk.faculties.MemoryGraph` lazy exports now resolve to `adk.graph_memory.GraphMemory`.\n- `agent.set_memory_graph()` kept for back-compat but documented as deprecated; agents already wire `GraphMemory` automatically in `__init__`.\n- Platform startup (`adk.platform.infrastructure.startup`) gracefully handles missing `MemoryManager`.\n- Platform CLI module listing and health check updated to reflect removed memory module.\n\n## [2.12.7] - 2026-07-01\n\n### Deprecated\n- `adk.faculties.MemoryGraph` — emits `DeprecationWarning` on instantiation; use `adk.graph_memory.GraphMemory` (SQLite-backed, governed, embedding-native) instead.\n- `adk.platform.memory` package — the legacy `MemoryManager`, `UnifiedMemorySystem`, `GameEngine`, and `StoryboardEngine` modules now emit `DeprecationWarning` on import; use `adk.memory.Memory` and `adk.graph_memory.GraphMemory` for new code.\n\n### Changed\n- Updated `adk.faculties` docstring and usage examples to point at the canonical memory surface.\n\n## [2.12.6] - 2026-07-01\n\n### Changed\n- Trimmed public package debt by removing grid runbook artifacts and shell/room binary placeholder directories from published artifacts.\n- Removed stale public-sync allowlist entries for deleted phase test files and added explicit public-sync guards for binary placeholder directories and grid artifacts.\n- Updated the public README grid note so it no longer links to runbook files excluded from the SDK package.\n\n## [2.12.5] - 2026-07-01\n\n### Added\n- Added best-effort fleet memory sync from local ADK memory into Nexus/Qdrant-compatible RAG stores, including batch catch-up and vector-search recall hooks.\n- Added saved config/env support for fleet memory URL, collection, and sync mode.\n\n### Changed\n- Excluded FormBridge runtime/test internals from public ADK packaging and public repo sync.\n\n## [2.12.4] - 2026-07-01\n\n### Added\n- Added a hot-reload endpoint for installed agent packs so newly applied pack tools can be picked up without restarting `aither-serve`.\n\n### Fixed\n- Fixed synchronous `ChatRelay` calls on Python 3.12 when no event loop is running; async WebSocket delivery is still scheduled when a loop exists.\n- Aligned gateway inference tier tests with the current public ladder: free, starter, pro, enterprise, platform.\n\n## [2.12.0] - 2026-06-28\n\n### Added — one-command local inference + upgrade path (click-to-run)\n- **`adk quickstart-local`** — detects hardware, picks a backend\n (Ollama / llama.cpp / vLLM), installs it, downloads a model, verifies, and\n prints next steps. Bootstraps Ollama itself via winget/brew/install.sh when\n missing (no \"go install it\" dead-ends). Default model `gemma4:e2b`.\n- **`adk backend switch <ollama|llamacpp|vllm>`** (+ `backend status`) — migrate\n the active deployment between engines; re-points config and smoke-tests.\n- **`adk install pack:<name>`** (+ `adk install list` / `adk packs`) — install\n ready-made agent packs (openclaw, hermes, claude-code) into `~/.aither/agents`.\n\n### Fixed (found via live end-to-end testing on Windows + CUDA)\n- quickstart-local reported success on a dead endpoint (smoke test was warn-only);\n a failed smoke now fails the command, with a service-readiness wait.\n- `--model` resolved to `None` (getattr default never fired) → uses the real default.\n- Smoke test: 30s→180s timeout for cold model load; accepts reasoning-model output\n (empty `content` at low token budgets is no longer a false failure).\n- `adk install pack:<name>` colon syntax (argparse rejected it before dispatch).\n- `adk backend status` health probe used a nonexistent `/health` path → 404; now\n probes `/v1/models` (uniform OpenAI-compatible liveness for all three backends).\n- Windows: removed non-ASCII glyphs from printed strings that crashed the cp1252\n console (a `✓` even made a successful install report failure).\n\n## [2.11.1] - 2026-06-14\n\n### Fixed — `adk host` / `adk login` device flow (autonomous onboarding)\nFound driving `adk host` against production; all broke real device-flow login:\n- **Device flow hit the portal FRONTEND, not AitherIdentity.** `adk login`/`adk host`\n POSTed `/auth/device/code` to portal/veil.aitherium.com, which 307-redirects API auth\n to `/login`. Added `_resolve_identity_url()` mapping the aitherium.com topology to\n `idp.aitherium.com` (the Identity API); localhost/bespoke pass through unchanged.\n- **Cloudflare 403'd urllib.** `idp.*` sits behind Cloudflare, which blocks urllib's\n default `Python-urllib/3.x` UA. Device-code + poll requests now send a real\n User-Agent + Accept (verified urllib→idp 200, was 403).\n- **`adk host` now self-authenticates via device flow** when there's no token, and\n **retries via device flow on a 401/403** (a stale saved token no longer dead-ends\n registration).\n\n## [2.11.0] - 2026-06-14\n\n### Added — attach your own MCP \"hands\" to a self-hosted agent\nA self-hosted agent now has the full brain (your model) · body (this loop) · **hands**\n(your tools) trifecta. The AitherOS platform relays the MCP servers you registered for\nyour agent in the `/stream` request body as `mcp_endpoints`, and the loop wires their\ntools into the ReAct turn:\n- **`adk/mcp_endpoint_tools.register_mcp_endpoint_tools(agent, endpoints)`** — speaks MCP\n JSON-RPC (`tools/list` to discover, `tools/call` to invoke) and registers a namespaced\n proxy tool (`{endpoint}__{tool}`) for each advertised tool. Mirrors `app_proxy_tools`.\n Best-effort: an unreachable endpoint registers nothing and never raises.\n- **`POST /stream` and `POST /chat/stream`** accept an optional `mcp_endpoints` list\n (`[{name, url, [headers]}]`); `_aitheros_stream` attaches them to the agent before the\n turn. Existing callers are unaffected (the field is optional).\n\n## [2.10.1] - 2026-06-14\n\n### Fixed — self-hosted `aither-serve` was crash-on-startup (2.10.0 regression)\nA fresh `pip install aither-adk && aither-serve --backend deepseek` could not boot. Found\nand fixed while standing up a real self-hosted DeepSeek agent end-to-end:\n- **Startup `NameError: name 'port' is not defined`** in three lifespan helpers\n (`_join_aithernet`, `_init_a2a_server`, `_start_mesh_hosting`) — they referenced a bare\n `port` no longer in scope. Now use `config.server_port`, and `main()` writes the resolved\n `--port`/`--host` back to `config` so every lifespan helper and the invoke_url see the\n actual bound port.\n- **Fleet self-registration crashed the whole server** — the registration handlers caught\n `(ImportError, RuntimeError, OSError, ConnectionError)` but **not** `httpx.HTTPError`, so a\n `RemoteProtocolError`/connect error talking to the control plane took the agent down. All\n network-facing lifespan handlers now also catch `httpx.HTTPError` (non-fatal, as intended).\n- **`--backend` was ignored when `AITHER_API_KEY` was set** — `_auto_detect` picked the cloud\n gateway before the explicit backend, and `_try_elysium_fallback` then replaced the chosen\n provider. An explicit backend (deepseek/openai/anthropic/your own vllm/ollama) now always\n wins; \"auto\"/\"gateway\" still flow through detection. The operator's own brain is never\n silently hijacked.\n- **Approved-tool trace truncated after `/sessions/{id}/confirm`** — the SSE relay treated\n `AgentResponse.tool_calls_made` (a `list[str]`) as dicts (`tc.get(...)`), raised\n `AttributeError`, and an over-narrow `except` swallowed it so the client saw only\n `session_start` + heartbeats. The relay now handles string tool names, the `except` is\n broad (always emits a typed `error` + terminal `complete`), and the module-level\n `_strata_ingest` `NameError` + Chronicle `session_id` kwarg mismatch are fixed.\n\n## [2.10.0] - 2026-06-13\n\n### Added — human-in-the-loop tool approval (`adk/approval.py`)\n- **Pause before a gated tool, resume on decision.** An agent can now pause its turn before\n executing a sensitive tool and wait for a human's allow/deny — so a managed control plane\n (or any operator) can gate tool use without forking the loop.\n- **Policy via `AITHER_TOOL_APPROVAL`** — a comma-separated list of tool names (optionally\n `agent:tool`), or `*` for every tool. Empty = no gating (unchanged behavior).\n- **Survives restarts / days-later approvals.** The paused turn (user message + pending tool\n calls) is persisted to disk keyed by `session_id`; resume is a fresh request, not a held\n connection. Decisions are recorded per `(session_id, tool_name)`.\n- **`AgentResponse.requires_action` / `.pending`** — set when a turn paused; `pending` lists\n the gated tool calls awaiting a decision.\n- **`AitherAgent.resume(session_id, decisions)`** — records the decisions and re-runs the\n paused turn (the gate consumes them: allowed tools execute, denied tools get a denial\n observation, the turn continues — and may pause again on a different gated tool).\n- **Server:** `POST /sessions/{id}/confirm` records decisions and streams the resumed turn;\n `/stream` emits a `requires_action` event when a turn pauses.\n\n## [2.9.0] - 2026-06-13\n\n### Added — universal LLM continuation primitive (`adk/llm/continuation.py`)\n- **`run_continuation()` + `stitch()`** — one shared continue-until-complete primitive. When a\n completion stops because it hit the output token cap (`finish_reason == \"length\"`), it continues\n the generation and STITCHES the chunks (overlap-dedup + restart-detect) into a complete answer,\n instead of every call-site hand-rolling its own retry. Bounded three ways (max rounds AND total\n output ceiling AND a no-growth break), and it never mutates the caller's message history.\n- **`LLMProvider.chat_with_continuation()`** and **`LLMRouter.chat_with_continuation()`** — every\n provider AND the router inherit the primitive, so any call-site gets continuation for free.\n- Kill-switch `ADK_LLM_CONTINUATION=off`; tunables `ADK_LLM_MAX_CONTINUATIONS`,\n `ADK_LLM_MAX_TOTAL_OUTPUT_TOKENS` (defaults: 2 rounds / 8192 tokens).\n\n### Changed\n- The ReAct loop's truncation recovery now uses the shared primitive. The previous inline\n doubling-retry REPLACED the partial response (losing earlier text in `resp.content`) and injected\n continuation scaffolding into the live message history; it now STITCHES and leaves history clean.\n Tool-call turns are never continued as text.\n\n### Included — previously-committed-but-unreleased changes\n- 2.8.2: TLS verification on by default (`verify=False` removed).\n- 2.8.1: baked license + pack-signing public keys.\n- 2.8.0: version-bump release.\n\n## [2.7.0] - 2026-06-05\n\n### Added — provider-agnostic LLM cost levers (at the `LLMProvider` ABC)\n- **Prompt caching.** New `ProviderCapabilities` (`prompt_cache: explicit|automatic|none`, `batch`) and\n normalized `LLMResponse.cache_read_tokens`/`cache_write_tokens`, so the \"tokens saved\" meter never\n depends on which backend served the turn. Anthropic inserts `cache_control` breakpoints over the stable\n system+tools prefix; OpenAI/DeepSeek caching is automatic and their `cached_tokens` are now surfaced\n (previously discarded). Default-on in `AitherAgent.chat`; override with `cache=False`.\n- **ResponseCache** (`adk/llm/cache.py`) — exact-match response cache with a pluggable backend, wired into\n `LLMRouter` as an opt-in (`LLMRouter(response_cache=…)` + per-call `cacheable=True`). OFF by default — no\n determinism change unless opted in.\n- **Gemini provider** (`adk/llm/gemini.py`) with explicit `cachedContents` caching; registered in the router.\n- **Async batch runner** (`adk/llm/batch_runner.py`) — `run_batch()` over the Anthropic Message Batches /\n OpenAI Batch APIs (~50% off) with a transparent fallback to concurrent `chat()` where unsupported.\n- **Graph-RAG retriever interface** (`adk/graph_retriever.py`).\n\n### Added — principal authority (prompt-injection defense enforced outside the model)\n- `adk/auth.py`: `Principal` / `AuthContext` / `PrincipalResolver` (+ deny-all default).\n- `ToolRegistry.execute(name, args, auth=…)` — a default-deny authorization gate at the tool boundary, so a\n forbidden tool call is blocked regardless of what the LLM emits. `ToolDef` gains `required_clearance` /\n `action_class`. Threaded through `AitherAgent.chat`; `auth=None` is fully backward-compatible.\n- `adk/channel_auth.py`: Telegram webhook-secret + CEO-id checks and HMAC-signed approvals.\n\n## [2.6.4] - 2026-06-05\n\n### Fixes\n- **Loop-guard nudges corrupted the message history → DeepSeek 400 mid-run.**\n When the ReAct loop emitted multiple tool calls in one assistant turn and the\n loop guard fired a WARN/BLOCK/CIRCUIT_BREAK on one of them, it spliced a\n standalone `system` nudge message *between* the tool results. DeepSeek (and\n strict vLLM chat templates) require an assistant `tool_calls` message to be\n followed by exactly one `tool` result per `tool_call_id`, contiguously — so the\n interleaved message produced `400 … insufficient tool messages following\n tool_calls message` and aborted the turn (this is reliably hit after crossing\n the 15-call circuit-break threshold, where every subsequent call WARNs).\n Per-call guidance is now folded into that call's own `tool` result, and any\n standalone steering is deferred to a single message AFTER the whole batch —\n preserving the `tool_calls`↔`tool_results` pairing every backend requires.\n Regression test: `deep-research-agent/tests/test_msg_structure.py`\n (loopguard-flood scenario).\n\n## [2.6.3] - 2026-06-04\n\n### Fixes\n- **Transient provider errors killed a run on the first blip.** A single\n `502 Bad Gateway` / `503` / `529 overloaded` / `429` from the upstream API\n aborted the whole request — the Anthropic provider had **no retry at all**, and\n the OpenAI-compatible provider only retried `429`. Both now retry transient\n statuses (`408/429/500/502/503/504/529`) with `Retry-After`-aware exponential\n backoff (3 attempts), and surface the provider's body if all retries are\n exhausted. Non-transient `4xx` (e.g. `400`) still fails fast. Applies to\n `chat` and `chat_stream` on both providers.\n\n## [2.6.2] - 2026-06-04\n\n### Fixes\n- **DeepSeek (and strict OpenAI-compatible backends) 400'd on long tool\n conversations.** The ReAct loop legitimately injects mid-conversation\n `system` steering messages (the \"[DIMINISHING RETURNS]\" hint after several\n low-output tool iterations, loop-guard nudges). OpenAI tolerates a non-leading\n `system` message; DeepSeek rejects it with a 400. Long research runs\n (many `fetch_url` calls) reliably tripped this. Fix: non-leading `system`\n messages are demoted to `user` at the OpenAI-compatible provider boundary\n (`_demote_nonleading_system`) — steering intent preserved, payload valid on\n every backend; leading system prompt(s) untouched.\n- **Opaque HTTP errors.** `resp.raise_for_status()` discarded the provider's\n response body, so a 400/422 surfaced as a bare `Client error '400 Bad\n Request'` with no cause. `_ensure_ok` now includes the provider's actual\n message (e.g. `context_length_exceeded`, `Invalid 'messages'`) in the raised\n error, for both `chat` and `chat_stream`.\n\n## [2.6.1] - 2026-06-04\n\n### Fixes\n- **`MCPServer.mount()` returned HTTP 422 for every `/mcp` call.** Under\n `from __future__ import annotations` the route handler's `request: Request`\n annotation is a string that FastAPI resolves against the *module* globals, but\n `Request` was imported locally inside `mount()` — so FastAPI couldn't resolve\n it and treated `request` as a required query param. The MCP server endpoint\n (`initialize`, `tools/list`, `tools/call`) was unusable when mounted. FastAPI\n symbols are now imported at module level (guarded; the `[node]` extra still\n gates actual use). Any agent's tools can now be exposed as a working MCP\n server with `MCPServer(...).mount(app)`.\n\n## [2.6.0] - 2026-06-04\n\n### Streaming + scaffolding\n- **`AitherAgent.stream_chat`** — reliable streaming sibling of `chat()`: native\n tool-calling loop (concludes on its own, unlike the text-protocol `stream_react`)\n that emits live `tool`/`tool_result` events and streams the answer as `token`\n events. `token_delay` paces tokens for a typing effect.\n- **`adk new <template>`** — scaffold a full, runnable template app. Ships the\n `deep-research` template (a cited web-research agent: pack + server + web UI):\n `adk new deep-research`.\n\n### Fixes\n- **BYO-key no longer community-capped.** The free-tier monthly token cap now applies\n only to the metered Aitherium gateway backend, never to a user's own provider key\n (Anthropic/OpenAI/DeepSeek/Ollama/vLLM). Previously it could brick a self-hosted\n agent after ~100k tokens on the user's own key.\n- **Stronger, grounded memory recall.** `chat()` now injects the top-6 knowledge-graph\n hits (untruncated) with a hard no-fabrication instruction — was top-3 truncated to\n 200 chars, which surfaced partial facts and let weaker models invent the rest.\n- **Tool-arg coercion.** `ToolRegistry.execute` coerces LLM-supplied args toward each\n parameter's annotated type, so a tool no longer crashes when a model passes a list\n or string where an int/float/bool was expected.\n\n### Docs\n- New `docs/AGENT_DEV_GUIDE.md` — the golden path for building agents/packs.\n\n## [2.5.0] - 2026-06-04\n\n### Continual learning — agents that learn from runs and reuse memory\n\nAgents now improve the second time they do a task instead of starting from scratch.\n\n### Added\n- **Recall-before**: `AitherAgent.chat()` searches the local `SkillStore` and injects the\n top matching learned skills as a system block, so the model reuses proven procedures.\n- **Learn-after**: `_learn_after()` runs after each response — reinforces (`success_count++`)\n any skills that were recalled+reused, and extracts + saves a NEW skill from successful\n multi-tool runs via `SkillExtractor`. Wired at both `chat()` return paths.\n- Gated by `AITHER_SKILLS` (default on). The previously-unused `SkillStore`/`SkillExtractor`\n are now connected to the run loop.\n\n### Notes\n- Pairs with the platform-side LearnedProcedure substrate (recipes → scored procedures →\n skills/tools/A2A/packs → MCTS priors → evolution). The SDK shares memory via the Spirit\n bridge when configured.\n\n## [2.0.0] - 2026-06-02\n\n### Open-core boundary (BREAKING)\n\nThis release draws a single, enforced free/paid line. The free tier stays\ngenuinely useful (real agent, typed + graph + code memory, ReAct, ~essential\ntools); paid tiers unlock the *scale* capabilities via portal.aitherium.com.\n\n### Added\n- **`adk/licensing.py`** — the entitlement keystone. Resolves a tier\n (COMMUNITY by default), verifies optional portal-signed `~/.aither/license.json`\n (Ed25519), and answers `can_use_fleet/channels/cron/swarm/auto_neurons`,\n `is_agent_licensed`, `max_effort`, etc. **Fail-closed**: no/invalid/expired\n license → free tier; an unsigned license never grants premium.\n `AITHER_TENANT_SLUG=aitherium` → unrestricted INTERNAL tier.\n- **Moat guard** (`scripts/check_moat_boundary.py`) wired into the publish\n workflow — blocks any wheel that leaks the moat or drops the keystone.\n- Tests: `tests/test_licensing.py`, `tests/test_moat_boundary.py`.\n\n### Changed (gating — fail-closed)\n- Free tier effort is capped at 3 (reasoning effort 7–10 needs Professional).\n- Monthly token budget hard-block on the free tier (no silent overspend).\n- Gated behind a paid tier: fleet mode (`FleetConfig`), cross-agent delegation\n (`AgentForge.delegate`), channel adapters, cron scheduler, proactive\n auto-neurons, and `swarm`/`swarm_code` dispatch.\n\n### Removed\n- **`adk/nanogpt.py`** (on-device training IP) removed from the published SDK\n and the public repo; relocated to the internal AitherOS moat. Excluded from\n the wheel as defense-in-depth.\n- Pre-2.0 releases shipped this IP and ungated capabilities and are being\n yanked (PyPI) / removed (GitHub releases). See `scripts/yank_leaky_releases.py`\n and `scripts/purge_public_leaks.sh`.\n\n## [1.20.0] - 2026-05-27\n\n### Fixed\n- **Pack discovery path mismatch** — `_find_local_packs()` now scans both\n `~/.aither/packs/` and `~/.aitheros/packs/`, so packs installed via\n `adk pack install` are discovered by `adk-workspace`.\n\n### Added\n- **Auto-register on pack install** — `adk pack install` now auto-registers\n with the portal if the pack contains an `agent.yaml` with a `portal` section.\n One command does download + extract + register.\n- **Flux event on agent registration** — `developer_portal.py` emits\n `agent.registered` Flux event so the portal fleet UI updates in real-time.\n\n## [1.15.0] - 2026-05-26\n\n### Added\n- **Cloud memory** — `adk quickstart --cloud` now tests gateway memory and\n auto-configures Spirit URL so memories persist across devices via\n `gateway.aitherium.com/v1/memory/teach` and `/v1/memory/recall`.\n- **Config→env export for cloud mode** — `Config.from_env()` now exports\n `AITHER_CLOUD_MODE`, `AITHER_SPIRIT_URL`, and spirit path env vars from\n saved config so Memory and other modules pick them up without needing\n explicit env vars set by the user.\n- **Cloud Quick Start section** in README — 3-command setup for users who\n just want a Claude/GPT-4/DeepSeek API key and the full agent harness.\n- **`--memory` profile for node deployment** — `adk deploy node --memory`\n adds Spirit (8087) + WorkingMemory (8101) containers for persistent\n vector memory. Without this, agents use local SQLite only.\n- **Configurable vLLM in node compose** — `AITHER_VLLM_MODEL`,\n `AITHER_VLLM_GPU_UTIL`, `AITHER_VLLM_CTX_LEN`, `AITHER_VLLM_QUANT_ARGS`\n env vars let `adk setup` write GPU-detected tier settings to `.env`\n and have the node compose pick them up (TQ4 for small GPUs, BNB for large).\n\n### Fixed\n- **Memory cloud routing** — `Memory._spirit_teach()` and `_spirit_recall()`\n now use configurable paths and auth headers for gateway proxy. Previously\n cloud-only users silently lost all Spirit memory calls.\n\n## [1.9.0] - 2026-05-24\n\n### Added\n- **Composable ToolPack system** — agents can load tool packs from\n `.toolpack.yaml` directories. Drop a pack in `~/.aitheros/packs/` or\n set `AITHER_PACKS_DIR`, and any agent discovers it automatically.\n- **`tools.packs` in agent.yaml** — list tool pack IDs to auto-load\n (e.g. `packs: [crm-tools, calendar-pro]`).\n- **`register_tool_packs(agent, pack_ids, packs_dir)`** in `builtin_tools` —\n programmatic pack loading for agents.\n- **`AITHER_TOOL_PACKS` env var** — comma-separated pack IDs loaded on\n agent init (alternative to YAML config).\n- **Unknown builtin categories resolve as pack IDs** — `tools.builtin:\n [file_io, crm-tools]` now works: `crm-tools` is looked up as a pack.\n\n## [1.8.0] - 2026-05-24\n\n### Removed\n- **11 dead modules** (3,023 lines) — `agent_deploy`, `cloud_deploy`, `create`,\n `phonehome`, `sdk_bridge`, `sovereign`, `telemetry`, `telemetry_config`,\n `error_reporter`, `anonymizer`, `package`. None were imported anywhere.\n- **29 ghost exports** from `__init__.py` — `secrets`, `otel`, `fs_sandbox`,\n and `vector_memory` modules were listed in `__all__` and `__getattr__` but\n the backing files never existed. Accessing them raised `ModuleNotFoundError`.\n- **Stale dist/ artifacts** — removed old wheels/tarballs (v0.4.0–v1.4.1, 11 MB).\n- **Orphan test scripts** — `test_phase1.py` and `test_phase23.py` (one-off\n validation scripts not part of the pytest suite).\n\n### Fixed\n- **Version mismatch** — `__init__.__version__` now matches `pyproject.toml`\n (was stuck at 1.6.0).\n\n## [1.5.0] - 2026-05-23\n\n### Added\n- **`--sovereign` flag for `adk deploy node`** — registers the node with the\n Aitherium hub via federation after deployment. Saves federation credentials\n to `~/.aither/.env.federation`. Accepts `--hub` (custom hub URL) and\n `--tenant` (tenant slug) options. Non-fatal: node runs standalone if\n registration fails.\n- **Federation → fleet cross-post** (Genesis) — sovereign nodes that register\n or heartbeat via `/federation/register` and `/federation/heartbeat` now\n automatically appear in the fleet dashboard (`/fleet/deployments`) with\n `node_type: \"sovereign\"` and live status/metrics.\n- **\"Deploy Sovereign Node\" section on Connect page** (AitherVeil) — Section 8\n with ADK deploy commands, fleet dashboard links, and provision page links.\n- **`deploy-node` action in tunnel proxy** (AitherVeil) — Connect page and\n external callers can now request bootstrap scripts via\n `POST /api/tunnel/devworkspace?action=deploy-node`.\n\n### Fixed\n- **Fleet provision page** — API URL corrected from `/api/fleet/provision` to\n `/api/bridge/genesis/fleet/provision` (matches bridge proxy pattern).\n- **Fleet detail page** — API URL corrected from `/api/fleet/deployments/` to\n `/api/bridge/genesis/fleet/deployments/` (same fix).\n- **Dockerfile** — entry point corrected from `aither-serve` to `adk-serve`\n (matches `pyproject.toml` script registration). Version label updated.\n\n## [1.4.1] - 2026-05-22\n\n### Fixed\n- **macOS executable builds** — dropped `--target-arch universal2` from\n `packaging/build_executable.py`. PyPI wheels for `pydantic_core` and other\n native deps are arch-specific, not fat binaries, so universal2 always failed\n with `IncompatibleBinaryArchError`. Now builds for the runner's native arch\n (`aither-macos-arm64`).\n- **Release workflow strategy** — added `fail-fast: false` so a single\n executable build failure no longer cancels the other OS jobs.\n- **PyPI publish** — added `skip-existing: true` so re-runs of the same\n version don't fail when the artifact already exists.\n- **GitHub Release job** — now runs whenever `pypi` succeeds even if some\n executable matrix entries fail, so the release page is always created with\n whatever binaries did build successfully.\n\n## [1.4.0] - 2026-05-22\n\n### Added\n- **Sovereign template now ships with `agent_core` 5-tier memory** — generated\n backends include the canonical `agent_core/` package (working → episodic →\n semantic → procedural → identity) plus `agent_memory.py` helper, vendored\n from `portal-kit-backend`.\n- `aiosqlite>=0.20.0` added to sovereign template `requirements.txt` for\n local agent_core SQLite storage.\n- Document tombstoning: `evict_cached_conversations_referencing` is wired\n through document-delete handlers in consumer apps so memories citing\n deleted documents are blocked from recall.\n- New tests: 13 smoke tests for `portal-kit-backend/agent_core` covering\n promotion thresholds, decay rates, store roundtrip, tombstone recall\n blocking, circuit breaker state machine, and chat-turn recording.\n\n### Changed\n- `scripts/vendor_agent_core.py` now syncs canonical `agent_core` to 4\n targets: WorkspaceRuntime, ADK sovereign template, and the GargBot +\n Chelle consumer mirrors (previously only 2).\n\n## [1.2.1] - 2026-05-21\n\n### Added\n- `adk train` command group — 6 subcommands: status, launch, logs, cancel, runs, register-gpu\n- `/slash-commands` endpoint — ADK server auto-exposes all 35 CLI commands as structured JSON manifest for AitherShell\n- `/cli/execute` endpoint — AitherShell can run any CLI command through the server\n- `build_command_manifest()` — introspects full argparse tree (commands, args, choices, subcommands)\n- `_register_commands()` extracted from main() for shared parser construction\n\n### Changed\n- `_get_genesis_url()` now probes: Genesis → ADK server → Aitherium cloud gateway (works without Genesis)\n\n## [1.2.0] - 2026-05-21\n\n### Added\n- **Runtime backend switching** — `agent.switch_backend(\"deepseek\")` and `agent.llm.switch_backend()` change provider without recreating agent\n- **Hybrid reasoning** — `agent.set_reasoning_backend(\"anthropic\")` routes effort 7+ to cloud API while keeping local orchestrator\n- **First-class DeepSeek provider** — `deepseek` in effort model table with `deepseek-chat` and `deepseek-reasoner`\n- **TQ4 quantization tiers** — `nano` (6GB, TurboQuant 4-bit), `standard-tq4` (12GB, both models TQ4), `hybrid-tq4` (6GB + cloud reasoning)\n- **`adk backend` CLI** — `list`, `set`, `set-reasoning`, `test` subcommands\n- **`adk quickstart`** — unified first-run wizard (setup + auth + shell)\n- **`adk tools`** — list local + MCP tools with tier markers\n- **`adk backup`** — tarball export of `~/.aither/`\n- **`adk ingest`** — feed docs/files into knowledge graph\n- **`adk setup nemotron`** shortcut — alias for `--tier lite`\n- **`--reasoning-api`** flag in setup — `anthropic`, `openai`, `deepseek`, `gateway`\n- **`--dgx-spark URL`** flag — remote vLLM endpoint configuration\n- **DGX Spark auto-detection** — scans `spark.local`, `192.168.0.33`, `AITHER_DGX_URL` env\n- **Post-setup smoke test** — sends real inference request after container startup\n- **Schema migration system** — version table + migration runner in `memory.py` and `graph_memory.py`\n- **Configurable vLLM port scan** — `AITHER_VLLM_PORTS` env var for custom ports\n- **`aithershell` entry point** — alias for `adk` in pyproject.toml\n- Shell auto-downloads binary on first `adk shell` (no `--install` needed)\n- Shell pre-flight check warns if no backend detected\n- Shell passes full config via env vars (API key, tenant, inference URL)\n- DGX/Remote and Cloud API checks added to `adk doctor`\n- Config fields: `reasoning_backend`, `reasoning_api_key`, `deepseek_api_key`, `dgx_url`, `vllm_extra_ports`\n\n### Fixed\n- `test_aeon_chat_basic` timeout — mock scope was exiting before request\n- `adk doctor` check_disk timeout — cap rglob at 5000 files\n\n## [1.1.7] - 2026-05-19\n\n### Added\n- Remote agent pipeline — `adk onboard` → fleet dispatch → remote inference\n- Agent-to-agent federation with mesh relay\n- `adk connect` for desktop mesh enrollment\n\n## [1.1.6] - 2026-05-19\n\n### Fixed\n- Tool call execution recovery across all backends\n- Hermes tool call fallback parsing for vLLM\n\n## [1.1.5] - 2026-05-19\n\n### Fixed\n- Tool call execution + Hermes fallback parsing improvements\n\n## [1.1.0] - 2026-05-18\n\n### Added\n- Multi-channel gateway (Telegram, Discord, Slack, webhook)\n- Aeon group chat with 7 presets\n- Skills auto-extraction from multi-step sessions\n- MCP stdio server for Claude Code integration\n- `adk gateway`, `adk cron`, `adk skills`, `adk soul` commands\n- SOUL.md import/export for portable agent identity\n- Elysium cloud device-flow authentication\n\n## [1.0.0] - 2026-05-18\n\n### Breaking\n- Package consolidated — `aither_adk` namespace replaced by `adk` + `adk.platform`\n- 93 duplicate files removed from `AitherOS/packages/aither_adk/`\n\n### Added\n- Standalone core primitives (`adk.core`: Agent, Tool, Memory, ModelBackend, Capability, Trace)\n- `adk.platform` sub-package merging all platform toolkit modules\n- Compatibility shims for legacy `from aither_adk.*` imports\n\n### Changed\n- Version synced across pyproject.toml, `adk/__init__.py`, `adk/platform/__init__.py`, npm package\n- Classifier promoted from Alpha to Beta\n\n## [0.16.0] - 2026-04-16\n\n### Swarm Coding Engine & Repowise Integration\n- **`repowise_search` tool** — Semantic + keyword hybrid code search via Repowise, with ripgrep fallback\n- **`swarm_code` tool** — Dispatch complex tasks to 11-agent swarm pipeline (ARCHITECT->SWARM->REVIEW->JUDGE)\n- **`agent.swarm()`** — Async convenience method with configurable mode, effort, timeout\n- **`agent.code_search()`** — Async convenience method returning structured results\n- New tool categories: `repowise` and `swarm` in TOOL_CATEGORIES\n- `repowise_search` and `swarm_code` added to `__init__.py` exports with lazy loading\n- `IDENTITY_DEFAULTS` updated: repowise for code-focused agents, swarm for orchestration agents\n- Standalone graceful degradation: repowise falls back to ripgrep, swarm returns structured error\n\n## [0.13.0] - 2026-04-02\n\n### Graph Faculties — Local Knowledge for Every Agent\n- **CodeGraph** (2,799 lines) — Full Python AST indexer with call graph, keyword/semantic/hybrid query, embedding matrix cache, incremental re-indexing, multi-hop chain expansion\n- **MemoryGraph** (1,339 lines) — Graph-based persistent agent memory with 10 edge types, hybrid query (keyword + semantic + graph expansion), multi-hop recall, pickle persistence\n- **EmbeddingProvider** — 4-backend fallback chain: sentence-transformers (GPU/CPU) -> Ollama -> Elysium cloud -> feature hashing (zero deps)\n- **BaseFacultyGraph** — Abstract base with HMAC-SHA256 validated pickle persistence\n\n### Agent Integration\n- `agent.set_code_graph(cg)` — auto-registers `code_search` + `code_context` tools\n- `agent.set_memory_graph(mg)` — auto-registers `remember` + `recall` + `memory_stats` tools\n- Both graphs inject context into LLM prompts automatically during chat\n\n### Zero-Config Onboarding\n- `adk start` / `adk` (no args) — auto-detect project, index code, detect LLM, persistent memory, interactive chat\n- Works for any directory: Python codebases, doc folders, mixed workspaces\n- Auto-detects LLM: Ollama -> vLLM -> Elysium -> OpenAI -> Anthropic\n- Per-project persistent memory in `~/.aither/memory/<project>`\n- `adk index <path>` — standalone indexing with progress bar and stats\n\n### MCP Gateway\n- `POST /v1/embeddings` — OpenAI-compatible embedding proxy to vLLM-embeddings:8209\n- Available to all tiers (embeddings are free)\n- ADK EmbeddingProvider uses this as Elysium cloud fallback\n\n### Optional Dependencies\n- `pip install aither-adk[graphs]` — numpy for 10x cosine similarity speedup\n- `pip install aither-adk[embedding]` — sentence-transformers + torch for local GPU embeddings\n- `pip install aither-adk` alone — graphs work with feature hashing (zero deps)\n\n## [0.12.0] - 2026-04-01\n\n### Bootstrap & Service Discovery\n- Version handshake -- ADK checks major.minor compatibility with Genesis/Node\n- Background reconnect loop -- ServiceBridge re-probes every 30s in standalone mode\n- Port 8080/8090 documented -- MCP vs OpenAI-compat clearly separated\n- Genesis URL configurable via GENESIS_URL env var (was hardcoded)\n- Standalone mode warning -- visible stderr alert when AitherOS not detected\n- Auto-reconnect on startup when services come online\n\n### Elysium Cloud Inference\n- Unified gateway URL -- gateway.aitherium.com handles auth + billing + inference\n- Streaming inference -- SSE passthrough for /v1/chat/completions with stream=true\n- Auth proxy routes -- /v1/auth/register, /v1/auth/login, /v1/auth/me\n- Billing proxy -- /v1/billing/balance through gateway\n- AitherConnect Elysium fallback -- cloud inference when local Genesis is down\n- AitherDesktop Elysium fallback -- third-tier chat fallback after Node\n\n### Infrastructure\n- /discovery endpoint on Genesis -- unified service URLs/versions/health\n- /api/config/services on Veil -- runtime port config for client-side JS\n- Veil healthcheck gates on Genesis -- unhealthy when backend is down\n- Desktop crash detection 90s->60s (threshold 3->2)\n\n## [0.11.0] - 2026-04-01\n\n### Agent Execution Quality (Claude Code Parity)\n- Raise loop guard block threshold from 3 to 4 -- agents get more room for iterative search\n- Soft synthesis nudge for effort >= 4 (no tool stripping, trust the model)\n- max_output_tokens escalation -- retry up to 3x when response is truncated\n- Tool result pairing guarantee -- synthetic error for orphaned tool_use blocks\n- Micro-compaction of old tool results -- save context tokens on long sessions\n- First-turn tool forcing only for effort >= 6 (trust model for lower effort)\n- Diminishing returns detection -- nudge agent when 3+ turns produce < 500 tokens\n- Message normalization -- merge consecutive same-role messages, strip empties\n- LLM retry with exponential backoff (5 retries, 500ms-16s, jitter)\n\n## [0.9.0] - 2026-03-16\n\nThe \"connected world\" release. Cross-platform identity pairing, voice capabilities, and multi-channel integration.\n\n### Added\n- **Pairing**: Cross-platform identity linking (`adk/pairing.py`)\n - `PairingManager` — SQLite-backed identity linking with 6-char pairing codes\n - Link users across Telegram, Discord, Slack, WhatsApp with 10min TTL codes\n - Canonical session IDs for cross-channel conversation continuity\n - `get_session_id()` returns \"user-{id}\" for paired users\n- **Voice**: Speech-to-text and text-to-speech client (`adk/voice.py`)\n - `VoiceClient` — async STT/TTS/emotion via AitherVoice service\n - Convenience functions: `hear()`, `say()`, `feel()`\n - 6 voice options: alloy, echo, fable, nova, onyx, shimmer\n - Emotion detection with intensity scoring\n- New exports: `PairingManager`, `PairingResult`, `PlatformIdentity`, `VoiceClient`, `TranscriptionResult`, `SynthesisResult`, `EmotionResult`\n\n### Changed\n- `__init__.py` exports expanded with pairing and voice symbols\n\n## [0.6.0] - 2026-03-13\n\nThe \"group mind\" release. Multi-agent group chat, creative tools, and Iris identity.\n\n### Added\n- **Aeon**: Multi-agent group chat engine (`adk/aeon.py`)\n - `AeonSession` — persistent group chat with parallel agent execution\n - 7 presets: balanced, creative, technical, security, minimal, duo_code, research\n - Orchestrator synthesis: Aither summarizes all agent responses\n - Serial execution for Ollama, parallel for vLLM/cloud\n - ConversationStore persistence with `type: \"aeon\"` metadata\n - `group_chat()` one-shot convenience function\n- `aither aeon` CLI command — interactive terminal group chat with color-coded agents\n - `-p/--preset`, `-a/--agents`, `-r/--rounds`, `--no-synthesize` flags\n - `reset` and `quit` commands\n- Server endpoints: `POST /aeon/chat`, `GET /aeon/presets`, `GET /aeon/sessions/{id}`\n- Creative tools in builtin_tools: `image_generate`, `image_refine`, `image_search`, `video_generate`\n- Iris agent identity with visual generation capabilities\n- 48 Aeon tests (data models, presets, chat, context, persistence, server, exports)\n\n### Changed\n- `__init__.py` exports: `AeonSession`, `AeonResponse`, `AeonMessage`, `group_chat`, `AEON_PRESETS`\n\n## [0.5.0] - 2026-03-13\n\nThe \"tenant-ready\" release. Multi-tenant admin, permission grants, safety profiles, and a full setup wizard.\n\n### Added\n- `aither setup` interactive setup wizard with hardware detection, model selection, identity config\n- Strata storage backend: SQLite WAL persistence for conversations, memories, knowledge graphs\n- CLI test runner: `aither test` with auto-discovery and parallel execution\n- Permission grants system for MCP tool access control\n- MCP account management tools\n- LLM provider auto-detection for Ollama and vLLM endpoints\n- Apache-2.0 LICENSE file\n- Elysium desktop sync module\n- Comprehensive CLI test suite (493 tests)\n- Strata storage test suite (972 tests)\n- LLM provider tests (82 tests)\n\n### Changed\n- CLI expanded: `aither setup`, `aither test`, `aither bugreport`, `aither doctor`\n- Strata module rewritten as full local-first storage engine\n- Server startup includes Strata initialization\n- README rewritten with clearer quickstart and architecture docs\n\n### Fixed\n- Dead documentation links\n- Python 3.11 compatibility issues\n- Ruff per-file-ignores configuration\n- Identity provisioning edge cases\n- Docker node-gyp native dependency builds (python3 + build tools)\n\n## [0.4.0] - 2026-03-13\n\nThe \"own your AI\" release. Self-hosted agent OS for people who don't want their data on someone else's servers.\n\n### Added\n- Muse agent identity (creative/artistic generation)\n- Port 8120 to vLLM scan for ExoNodes discovery\n- Public roadmap with milestone-based porting schedule\n- Competitive positioning: self-hosted alternative to cloud-locked AI appliances\n\n### Changed\n- LLM router: compute-aware effort routing (replaces effort-level context gating)\n- README rewritten for sovereignty-first messaging\n- Package description updated\n- Promoted from alpha to stable release\n\n### Fixed\n- Version string consistency between pyproject.toml and __init__.py\n- vLLM port scanning now includes port 8120 (ExoNodes)\n\n## [0.3.1] - 2026-03-09\n\n### Added\n- GraphMemory: SQLite knowledge graph with Ollama embeddings and hybrid search\n- NeuronPool: auto-fire pre-LLM data gathering agents\n- NanoGPT: pure Python char-level transformer with LoRA fine-tuning\n- Safety gates: IntakeGuard (input), LoopGuard (recursion), Sandbox (code exec)\n- Event system: EventEmitter with chat/tool/forge event types\n- Builtin tools: identity-based tool selection\n- ServiceBridge: auto-discovery of AitherOS services\n- Streaming: chat_stream with safety gate integration\n- Auth middleware: Bearer token authentication\n- CLI: `aither init` and `aither serve` commands\n\n### Changed\n- Wired 5 previously disconnected modules into agent loop\n- 522 total passing tests (up from 85)\n\n## [0.3.0] - 2026-03-07\n\n### Added\n- Clean-room agent development kit\n- Multi-backend LLM providers (Ollama, OpenAI, Anthropic)\n- AitherAgent class with @tool decorator\n- SQLite conversation memory\n- OpenAI-compatible server (`aither-serve`)\n- 16 agent identities as package data\n- MCP bridge to mcp.aitherium.com\n- Privacy-centric opt-in telemetry\n- Bug reporting CLI and API\n- FastAPI server with OpenAI-compatible endpoints\n- Hardware auto-detection (5 tiers, 11 profiles)\n- Fleet orchestration and multi-agent coordination\n- A2A mesh protocol\n- Federation protocol for cross-instance agent dispatch\n\n### Initial release\n- 85 passing tests\n- Apache-2.0 license\n"}, "81ac5485fa9c4bd42598c487e6209d4ebb3cafde": {"t": 1785748188.5236776, "n": 64116, "h": "7c1f23ab95f863fd", "c": "# Changelog\n\nAll notable changes to aither-adk will be documented in this file.\n\n## [2.14.7] - 2026-07-04\n\n### Added\n\n- **`adk up` — one command to run a persistent, fleet-connected agent.** Collapses\n the scattered onboarding surface (`host`/`quickstart`/`connect`/`login`/`setup`)\n into a single command that starts `aither-serve` (single agent, identity `aither`\n by default), opens a Cloudflare quick-tunnel for fleet reachability, registers with\n the portal, and installs a reboot-persistent autostart entry (Windows Task Scheduler\n / systemd-user / launchd). Detaches by default so the terminal is freed; `--foreground`\n blocks. Reuses the existing device-flow login, cloudflared discovery, provider-key\n store, and `_preflight_check` backend detection.\n- **Fully non-interactive path for autonomous agents.** `adk up --yes` (or any\n non-TTY invocation) takes ZERO prompts — everything resolves from flags, env, and\n saved config — and emits a single machine-readable JSON status line with meaningful\n exit codes (`0` ok, `2` bad-input, `3` no-backend, `4` tunnel-missing, `5` register-failed).\n Degrades to local-only (warn, not fail) when a portal token is absent unless\n `--require-register` is set.\n- **`adk down` / `adk status --json` companions.** `down` stops the agent + tunnel,\n best-effort deregisters, and removes the autostart entry; `status` reports the running\n agent (liveness + `/health` + tunnel + registration), with `--json` for agents/CI.\n- **`adk stack`** — the previous `adk up` behaviour (supervise the Room + Ollama consumer\n stack) now lives here; `adk up` is the connected-agent command.\n\n### Internal\n\n- New `adk/agent_daemon.py`: detached process spawn, status file\n (`~/.aither/adk-up.json`), pid liveness/teardown, and cross-platform autostart install.\n\n## [2.14.6] - 2026-07-04\n\n### Added\n\n- **`adk.preflight` — self-bootstrap capability probe.** `run_preflight(agent,\n spec)` wakes an agent up knowing what it is actually plugged into: a bounded\n (1.5s) parallel `LivenessProbe` over the router's resolved backends\n (primary/reasoning), the embeddings winner, remote MCP tools, the structured-ML\n / `/ml/teach` endpoints, and voice — returning an honest `CapabilityReport`.\n Status vocabulary distinguishes reachability from entitlement\n (`OK`/`MISSING`/`UNREACHABLE`/`AUTH`/`TIER_DENIED`/`UNSUPPORTED`), a hosted slot\n never prints a bare `OK`, and a required-but-unsatisfied slot aborts before the\n loop instead of blind-firing. Headless task/rules resolution + an allowed-roots\n policy binding (`builtin_tools.set_allowed_roots`, which resets the memoized\n cache so the override actually enforces).\n- **Multimodal (image) messages.** `Message.content` now accepts an OpenAI-style\n content-part list (`str | list[dict]`), passed through verbatim to the\n OpenAI-compatible body (gateway / vLLM / OpenAI). New `adk.llm.multimodal`\n helpers — `image_message()`, `image_content_parts()`, `to_image_url()` — build\n the parts and encode local image bytes/paths into `data:` URIs.\n- **Real vision preflight probe.** The vision slot is no longer unconditionally\n `UNSUPPORTED`: it sends a discriminable image (a solid red square) and requires\n the model to name the color, so a blind/text-only model cannot false-pass. `OK`\n only on a real round-trip; `AUTH` on 401/403; `UNSUPPORTED` when there is no\n OpenAI-compatible provider or the model cannot see the image.\n\n## [2.14.5] - 2026-07-04\n\n### Added\n\n- **`adk.reasoning.mcts` — generic Monte-Carlo Tree Search library.** A reusable\n MCTS engine (ported from the internal `UnifiedMCTS`, with all internal couplings\n removed) that any agent can drive over its own environment:\n - `MCTSEnvironment` protocol (`get_state_hash` / `get_actions` / `step` /\n `evaluate` / `clone`) — implement it and search.\n - Three **optional, default-off** model seams — `TransitionModel`, `PolicyModel`\n (PUCT priors), `ValueModel` — so a learned world-model / policy / value can be\n injected without touching the core; with all three `None` the engine is a plain\n UCT search.\n - `ObservedTransitionModel` adapter — a cheap online lookup world-model (hit =\n exact stored transition; miss = identity + negative \"unknown\" reward +\n `is_uncertain`, biasing exploration toward learning), with JSONL save/load for\n cross-run reuse.\n - `UnifiedMCTS(config).search(env)` returns an `MCTSResult` (best action, value,\n confidence, principal-variation path) and an optional `MCTSTrace`\n (visit-distribution policy target) for distillation.\n- **Self-bootstrapping agent machinery.** Build an agent from a declarative spec\n instead of bespoke wiring:\n - `adk.bootstrap.build_agent_from_spec(path) -> (AitherAgent, RunCtx)` — resolves\n prompts, activates required/optional tool packs, and carries a `memory_map`\n (a `node_type -> (role, tier)` override for memory writes, never a gate).\n - `adk.prompts.PromptBridge` — resolves an agent's prompt bindings from files\n (relative to the spec) or `AitherPrompts` dot-keys (optional import).\n - `adk.packs.PackActivator` — eager activation of installed tool packs by name\n over `register_tool_packs`; a required pack that registers zero tools raises\n `PackUnavailable`, optional packs soft-degrade.\n - `python -m adk.bootstrap.generic_agent <spec.yaml>` inspects a spec (resolved\n name, activated packs + tool counts, prompt keys, memory map) without running a\n live loop.\n- **`deep_research` tool pack** — a full multi-engine web-research workflow as\n `dr_*` agent tools (keyless search, page fetch + cache, site mirror, a citable\n findings knowledge graph, and markdown/PDF/DOCX report generation), activatable\n by name via the tool-pack loader.\n\n## [2.14.4] - 2026-07-04\n\n### Added\n\n- **`structured_ml` tool domain** — zero-shot structured-data inference for agents,\n backed by TabFM (tabular classification/regression) and TimesFM (time-series\n forecasting). Three tools:\n - `tabular_classify(support_rows, target, query_rows)` — classify rows from a\n labeled support set, in-context (no training step; up to 10 classes).\n - `tabular_regress(support_rows, target, query_rows)` — predict a numeric target.\n - `timeseries_forecast(series, horizon)` — forecast future values of a series.\n\n The tools POST to a structured-ML inference service resolved from\n `AITHER_STRUCTURED_ML_URL` (default `http://localhost:8192`), reject non-http(s)\n URLs, and summarise oversized responses to protect the agent's context.\n- **`Capability.STRUCTURED_INFERENCE`** capability token and a new **`analyst`**\n identity/pack that ships the `structured_ml` domain by default. The domain is\n opt-in — only identities that enable the `structured_ml` category get the tools.\n\n## [2.14.3] - 2026-07-03\n\n### Fixed\n\nAll four bugs below were found by actually booting `adk run` against a real\nchat model and driving a real tool-calling request end-to-end (not inferred\nfrom reading the code) — each one silently broke the \"local agent uses its\nconfigured model and its tools actually work\" path.\n\n- **`AITHER_MODEL` / `config.model` silently ignored by the OpenAI-compatible\n backend**: `LLMRouter.__init__` never read `config.model` into `self._model`\n unless the caller passed `model=` directly to the constructor — which nothing\n did. Every explicit model choice (env var, config file) was dropped, the\n provider fell back to a hardcoded per-backend default (`gpt-4o-mini` for the\n `openai` backend), and an unrecognized model name got silently remapped\n server-side to the wrong backing model. Concretely: setting\n `AITHER_MODEL=aither-orchestrator` had no effect — every request actually\n went to a different model that was never meant to be called directly and\n can't reliably use tools.\n- **`OPENAI_BASE_URL` ignored for explicit `--backend`/`AITHER_LLM_BACKEND`\n selection**: `LLMRouter._auto_detect()`'s explicit-backend branch hardcoded\n `base_url=None` when constructing the provider, instead of reading\n `config.openai_base_url` (which two other call sites in the same file\n already did correctly). Any self-hosted OpenAI-compatible endpoint\n (MicroScheduler, vLLM, LM Studio) configured via `OPENAI_BASE_URL` was\n silently ignored in favor of `https://api.openai.com/v1`.\n- **Tool-call steering never engaged on default-effort chat**: the retry that\n nudges a model back into structured tool-calling when it responds with prose\n instead of a function call was gated behind `effort >= 6`. Default `effort`\n for a plain `agent.chat(message)` call resolves to `5`, so the safety net —\n built specifically for this failure mode — never fired for default requests.\n Also now forces `tool_choice=\"required\"` on the retry instead of relying on a\n text nudge alone.\n- **Every non-string builtin-tool parameter silently typed as `\"string\"` in\n the generated tool schema**: `builtin_tools.py` uses\n `from __future__ import annotations`, so `_extract_parameters()` in\n `tools.py` was reading postponed (string) type hints (`\"int\"`, not `int`)\n straight out of `fn.__annotations__`, which never matched\n `_type_to_schema`'s type-object keys and silently fell through to\n `{\"type\": \"string\"}`. Every builtin tool with an `int`/`float`/`bool`/`list`\n parameter was affected — e.g. `file_read(start_line: int, end_line: int)`\n advertised both as strings, so a model correctly calling the tool with\n `\"start_line\": \"0\"` crashed the actual implementation with\n `unsupported operand type(s) for -: 'str' and 'int'`. Fixed by resolving\n hints through `typing.get_type_hints()` instead of the raw annotations dict.\n `_coerce_arguments()` — the runtime safety net that's supposed to coerce a\n model-supplied `\"0\"` string into `0` before a tool call executes — had the\n exact same raw-`__annotations__` bug and silently no-opped for every builtin\n tool; fixed the same way.\n- **Streaming tool-loop could hang a connection forever**: `chat_stream()`'s\n fallback to the sync tool loop (tool calls can't stream token-by-token) had\n no timeout, so a model that didn't cleanly terminate a tool-calling round\n trip left the SSE connection open indefinitely with zero bytes sent. Bounded\n with a 60s `asyncio.wait_for`; on timeout the stream yields a clear message\n instead of hanging.\n\n## [2.14.2] - 2026-07-03\n\n### Fixed\n- **AitherShell session amnesia**: the REPL generated a fresh `session_id` for\n EVERY message, so conversation history never followed across turns (the\n server stored each turn under a different session). One stable id is now\n minted per shell run; `/resume` and `/new` semantics unchanged, and the\n cross-restart auto-restore of the last session now actually works.\n\n## [2.14.1] - 2026-07-02\n\n### Added\n- **Account-tied licensing via device-flow login** — `adk login` now persists the account\n license returned by the device-flow handshake to `~/.aither/license.json` (base64\n `license_key` → `{payload, signature}` envelope), so entitlements from the authenticated\n account carry into every later `adk` invocation without re-auth. Pairs with the fleet-side\n node tool-pack gate that grants packs by plan-tier rank.\n- **`can_use_formbridge` / `can_use_untether` entitlements** (PROFESSIONAL+ rank ≥ 3) backing\n the FormBridge and UNTETHER agent tool-packs.\n\n### Fixed\n- `adk` CLI: guard the optional `memory_graph` singleton (was a latent `NameError` on the\n `/stats`, `/memory`, and save paths when memory was not constructed).\n\n## [2.14.0] - 2026-07-02\n\n### Added\n- **`adk.memory_wiki` — LyraWiki-style self-managed semantic knowledge** (opt-in; nothing constructs it by default): `MemoryWiki(graph_memory)` maintains curated wiki ARTICLES as `wiki_article` nodes (title/slug, LLM-owned markdown with `[[wikilinks]]`, embedding, durable tier, `CONSOLIDATED_FROM` edges to source memories, governance-ledgered revisions).\n - `consolidate(llm, since=, budget=)` clusters unconsolidated episodic/fact nodes (embedding + keyword), drafts/UPDATEs articles via the INJECTED llm callable (`str -> str` or `messages -> str`, sync or async — no hard model dependency), cites source node ids, marks contradicted sources superseded, then DEMOTES consolidated sources to a fast-decay tier via `promote()` (represented knowledge doesn't need raw copies).\n - `lint(llm=None)` — deterministic health checks (orphan wikilinks, empty/stale articles, live-but-contradicted pairs) + an optional LLM audit pass.\n - `prune(relevance_floor=0.05, hard_delete_after_days=14)` — article relevance = tier freshness × reinforcement (reinforce-on-recall) + link-degree to LIVE memories; below the floor → reversible governance tombstone; entombed past the window → **HARD-DELETE** (the tombstone snapshot itself is purged — content and embedding irrecoverable; the true-deletion path). A node is never deleted without a tombstone first.\n - `recall(query, limit)` — article-first RAG (curated articles rank above raw nodes) that reinforces returned articles.\n- **`adk.routines` — agent heartbeat + self-programmed routines**: `RoutineStore` is a durable registry (`~/.aither/routines/{agent}.json`, `AITHER_DATA_DIR`-aware, injectable path) of cron-scheduled self-prompts `{name, cron, instruction, enabled, last_run, last_result, tags}` that rehydrates into the existing `adk.cron.CronScheduler` on `start()`. A fire runs `agent.chat(instruction)` with the agent's full toolset — the agent programs its future self with its own adk. Guardrails: `max_routines` (12), a 5-minute min fire interval per routine, a per-fire timeout, and every result ledgered (truncated) to `last_result`. Direct-callable routines (`register_direct`) fire a bound method instead of a self-prompt. Self-management tools (`routine_create/list/update/pause/resume/delete/run_now`) ship as OpenAI tool defs + handlers (`build_routine_tools` / `register_routine_tools` / `routine_tool_defs`); the handlers ONLY touch the RoutineStore — they can never modify agent config or safety-relevant settings (the leash principle).\n- **`AitherAgent(routines=True, memory_maintenance=True)`** (both default **False** → the default agent is byte-identical): `routines=True` builds the RoutineStore + scheduler, registers the self-management tools, and starts the heartbeat lazily on the first `chat()` (or via explicit `start_routines()`). `memory_maintenance=True` registers DEFAULT routines — `wiki_consolidate` (every 2 h), `wiki_lint` / `wiki_prune` / `graph_sweep` (daily) — as DIRECT method fires so memory upkeep runs even on tiny models, while staying visible/manageable via `routine_list`.\n- `TombstoneStore.purge(tombstone_id)` — the hard-delete endpoint: irrecoverably removes a tombstone snapshot and rewrites the persisted store without it (consumed by `memory_wiki.prune`).\n\n## [2.13.6] - 2026-07-02\n\n### Added\n- **Qdrant backend for GraphMemory dataplane sync — TRUE two-way, verified live.** Set `AITHER_FLEET_QDRANT_URL` and the sync uses Qdrant (`upsert` + `scroll` + filtered `search`) instead of Nexus. This is what actually delivers cross-agent memory sharing: a node is upserted with its OWN embedding under a deterministic per-(tenant,node) point id (idempotent re-push), and `fleet_pull` reliably SCROLLS a tenant's points back — the enumerate Nexus lacks (its `/search` doesn't return ingested docs and `/export` needs lancedb, which has a hard dependency conflict in the fleet). **Proven live with 2 agents:** agent-1 ingests → auto-syncs to the tenant dataplane; a fresh agent-2 in the same tenant `fleet_pull`s all of agent-1's nodes and can search them; a different tenant pulls nothing (isolation). Dimension-safe (never mixes vector dims in a collection). Local SQLite stays source of truth; best-effort, never raises.\n\n## [2.13.5] - 2026-07-02\n\n### Fixed\n- **CRITICAL — GraphMemory dataplane sync push was 100% failing.** `_fleet_push_node` sent `source_type=\"graph\"` / `content_type=\"graph_node\"`, which AitherNexus rejects as invalid enums (HTTP 500) — so NO graph node ever replicated in 2.13.4. Now sends the accepted `source_type=\"manual\"` / `content_type=\"text\"` (graph provenance is carried in `metadata.synced_from`). **Verified live** against a fleet Nexus: a 4-node ingest replicates with `pending==0` and the collection count reflects it.\n\n### Added\n- **Swarm-awareness notification** — on a successful sync batch, `GraphMemory` emits a best-effort `graph.synced` event (tenant/workspace/agent/collection/count) to `AITHER_FLEET_EVENTS_URL` so OTHER agents in the tenant/swarm can pull the fresh data and deconflict in-flight work (push-based awareness instead of polling). No-op unless the env is set.\n\n### Known limitation\n- Cross-agent **rehydration** (`fleet_pull`) is only reliable when the fleet Nexus is backed by lancedb (persistent + exportable). An in-memory Nexus stores on `/ingest` but neither its `/search` nor `/export` returns the docs — two-way read needs the persistent Nexus.\n\n## [2.13.4] - 2026-07-02\n\n### Added\n- **Memory-maintenance primitives on `GraphMemory`** (all opt-in; default behaviour byte-identical):\n - **Reinforce-on-recall**: `recall_with_activation(..., reinforce=True)` bumps `reinforcement_count`/`last_reinforced` on every RETURNED node's metadata mirror (one UPDATE per node, WAL-safe) — making the `MemoryRecord` reinforcement fields the activation scorer reads LIVE. Default `False` = zero writes.\n - **`sweep(now, archive_below=0.05, max_nodes=None, dry_run=False)`**: read-time decay enforced at rest — archives nodes past their tier TTL with freshness×reinforcement score below `archive_below` and `reinforcement_count <= 1`; `max_nodes` additionally archives the lowest-scored overflow. PERMANENT tier immune. Every archive is a reversible governance tombstone (`TombstoneStore.recover`) + a FORGET ledger entry; a node is never deleted without a tombstone. `dry_run` returns the would-archive list without acting.\n - **`promote(node_id, tier=?, role=?)`**: re-tier/re-role a node IN PLACE (tier/role columns + metadata mirror) — edges, embedding and ledger history survive; ledgers an UPDATE entry when governance is on.\n- `MutationType.UPDATE` ledger entry kind (in-place re-tier/re-role).\n- **Per-tenant dataplane sync for `GraphMemory`** — replicate the local graph to a per-tenant vector dataplane (Nexus/Qdrant) so a sovereign agent's memory is durable + rehydratable across restarts and hosting boundaries. `fleet_push_all_nodes()` (catch-up), `fleet_pull()` (best-effort semantic top-up), `fleet_sync_pending()`, and auto-sync on ingest (`_maybe_autosync` + `drain_sync()`). **On by default** (`AITHER_FLEET_SYNC=auto`; only `false/0/off` disables) with an inferred target (local Nexus `:8122` or the gateway in cloud mode) and a SEPARATE collection (`AITHER_FLEET_GRAPH_COLLECTION`, default `graph_memory`) from the KV memory sync. New `GraphMemory(tenant_id=, workspace_id=, fleet_url=, auto_sync=)` constructor args override env (required for multi-tenant host processes). Node `synced` column + schema v4 migration. TLS verify is CA-bundle-aware (`AITHER_CA_BUNDLE`) for the internal-TLS mesh. Local SQLite stays the source of truth; every sync path is best-effort and never raises into ingest.\n\n## [2.13.3] - 2026-07-02\n\n### Added\n- **Canonical self-deploying embeddings provider** (`adk.embeddings`): one embedding provider for the whole SDK so vectors are portable across scopes (platform / tenant / sovereign) — same model + dimension everywhere. Pinned to `nomic-embed-text` (768-d; the vLLM `--served-model-name` for nomic-embed-text-v1.5). Lazy, single-flight resolution chain: explicit `AITHER_EMBEDDINGS_URL` → local vLLM (`:8209` then `:8120`, HTTPS-then-HTTP) → local Ollama → gateway (`AITHER_GATEWAY_EMBEDDINGS_URL`) → auto-deploy a local vLLM embeddings container if a GPU + Docker are present (opt out with `AITHER_EMBED_AUTODEPLOY=0`) → CPU sentence-transformers (384-d, degraded) → feature-hash (384-d, degraded, always works). Every batch is dimension-tagged so callers never silently mix 768-d and 384-d vectors. `get_default_embedder()`, `embed_texts()`, `embed_one()`, `get_provider()`, `reset_provider()`.\n\n### Changed\n- **`GraphMemory` now defaults its embedder to the canonical provider** (opt out with `AITHER_GRAPH_EMBEDDER=legacy`), so every adk agent shares one 768-d space. Added a meta-table dimension guard: the index is pinned to its first embedding's dimension and later different-dimension vectors are refused (kills 768↔384 mixing that silently poisons cosine similarity).\n\n## [2.13.2] - 2026-07-01\n\n### Added\n- **Session persistence**: AitherShell now auto-saves the active session_id to `~/.aither/config.yaml` after each chat turn and auto-restores it on the next shell launch. Multi-turn conversations survive across restarts — the messages were always persisted in SQLite + JSON, only the session pointer was missing.\n- `/new` command to start a fresh session (clears saved session_id).\n- `/sessions` command to list recent sessions with agent name, message count, and timestamp. Resume any with `/resume <id>`.\n\n## [2.13.1] - 2026-07-01\n\n### Fixed\n- **CRITICAL:** `deploy_connect()` now sends a HEAD request to verify the GitHub release asset exists before downloading, with a clear error if the asset is missing.\n- **CRITICAL:** Compose file URLs are now pinned to a stable commit hash (configurable via `AITHER_COMPOSE_PIN` env) instead of tracking the mutable `main` branch.\n- **CRITICAL:** `deploy_adk_node()` now falls back to starting `adk-serve` natively when Docker is unavailable, instead of blocking with \"Docker not installed\".\n- **HIGH:** GHCR login failure now blocks deployment immediately with actionable guidance, instead of warning and continuing to a confusing image-pull failure.\n- **HIGH:** Health checks now stream the last 30 lines of Docker Compose logs on failure, so the user can see why a container didn't start.\n- **HIGH:** vLLM port allocation now probes for conflicts before assigning; if port 8200 is busy, workers are automatically remapped to the next free port.\n- **MEDIUM:** Compose file downloads are now cached with ETag; re-running `adk deploy node` skips re-downloading an unchanged file.\n- **MEDIUM:** DGX Spark discovery now respects `AITHER_DGX_HOSTS` env (comma-separated) for custom network topologies, instead of only checking `spark.local` and `192.168.0.33`.\n- **MEDIUM:** Cloud API keys (Anthropic, OpenAI, DeepSeek) are now validated with a lightweight probe during `adk setup` infra scan, so invalid keys surface early.\n- **MEDIUM:** `deploy_connect()` now validates the extracted extension has a `manifest.json` and warns if it's missing.\n\n## [2.13.0] - 2026-07-01\n\n### Removed\n- **`adk.faculties.memory_graph`** — the pickle-based `MemoryGraph` module is deleted. `from adk.faculties import MemoryGraph` now resolves to the canonical `adk.graph_memory.GraphMemory` for back-compat.\n- **`adk.platform.memory`** — the entire legacy memory subsystem (`MemoryManager`, `UnifiedMemorySystem`, `GameEngine`, `StoryboardEngine`, `AnchorGenerator`) is deleted. Agent memory is `adk.memory.Memory` + `adk.graph_memory.GraphMemory`.\n\n### Changed\n- `adk work` command now uses `GraphMemory` instead of the removed pickle-based faculty graph.\n- `adk.MemoryGraph` and `adk.faculties.MemoryGraph` lazy exports now resolve to `adk.graph_memory.GraphMemory`.\n- `agent.set_memory_graph()` kept for back-compat but documented as deprecated; agents already wire `GraphMemory` automatically in `__init__`.\n- Platform startup (`adk.platform.infrastructure.startup`) gracefully handles missing `MemoryManager`.\n- Platform CLI module listing and health check updated to reflect removed memory module.\n\n## [2.12.7] - 2026-07-01\n\n### Deprecated\n- `adk.faculties.MemoryGraph` — emits `DeprecationWarning` on instantiation; use `adk.graph_memory.GraphMemory` (SQLite-backed, governed, embedding-native) instead.\n- `adk.platform.memory` package — the legacy `MemoryManager`, `UnifiedMemorySystem`, `GameEngine`, and `StoryboardEngine` modules now emit `DeprecationWarning` on import; use `adk.memory.Memory` and `adk.graph_memory.GraphMemory` for new code.\n\n### Changed\n- Updated `adk.faculties` docstring and usage examples to point at the canonical memory surface.\n\n## [2.12.6] - 2026-07-01\n\n### Changed\n- Trimmed public package debt by removing grid runbook artifacts and shell/room binary placeholder directories from published artifacts.\n- Removed stale public-sync allowlist entries for deleted phase test files and added explicit public-sync guards for binary placeholder directories and grid artifacts.\n- Updated the public README grid note so it no longer links to runbook files excluded from the SDK package.\n\n## [2.12.5] - 2026-07-01\n\n### Added\n- Added best-effort fleet memory sync from local ADK memory into Nexus/Qdrant-compatible RAG stores, including batch catch-up and vector-search recall hooks.\n- Added saved config/env support for fleet memory URL, collection, and sync mode.\n\n### Changed\n- Excluded FormBridge runtime/test internals from public ADK packaging and public repo sync.\n\n## [2.12.4] - 2026-07-01\n\n### Added\n- Added a hot-reload endpoint for installed agent packs so newly applied pack tools can be picked up without restarting `aither-serve`.\n\n### Fixed\n- Fixed synchronous `ChatRelay` calls on Python 3.12 when no event loop is running; async WebSocket delivery is still scheduled when a loop exists.\n- Aligned gateway inference tier tests with the current public ladder: free, starter, pro, enterprise, platform.\n\n## [2.12.0] - 2026-06-28\n\n### Added — one-command local inference + upgrade path (click-to-run)\n- **`adk quickstart-local`** — detects hardware, picks a backend\n (Ollama / llama.cpp / vLLM), installs it, downloads a model, verifies, and\n prints next steps. Bootstraps Ollama itself via winget/brew/install.sh when\n missing (no \"go install it\" dead-ends). Default model `gemma4:e2b`.\n- **`adk backend switch <ollama|llamacpp|vllm>`** (+ `backend status`) — migrate\n the active deployment between engines; re-points config and smoke-tests.\n- **`adk install pack:<name>`** (+ `adk install list` / `adk packs`) — install\n ready-made agent packs (openclaw, hermes, claude-code) into `~/.aither/agents`.\n\n### Fixed (found via live end-to-end testing on Windows + CUDA)\n- quickstart-local reported success on a dead endpoint (smoke test was warn-only);\n a failed smoke now fails the command, with a service-readiness wait.\n- `--model` resolved to `None` (getattr default never fired) → uses the real default.\n- Smoke test: 30s→180s timeout for cold model load; accepts reasoning-model output\n (empty `content` at low token budgets is no longer a false failure).\n- `adk install pack:<name>` colon syntax (argparse rejected it before dispatch).\n- `adk backend status` health probe used a nonexistent `/health` path → 404; now\n probes `/v1/models` (uniform OpenAI-compatible liveness for all three backends).\n- Windows: removed non-ASCII glyphs from printed strings that crashed the cp1252\n console (a `✓` even made a successful install report failure).\n\n## [2.11.1] - 2026-06-14\n\n### Fixed — `adk host` / `adk login` device flow (autonomous onboarding)\nFound driving `adk host` against production; all broke real device-flow login:\n- **Device flow hit the portal FRONTEND, not AitherIdentity.** `adk login`/`adk host`\n POSTed `/auth/device/code` to portal/veil.aitherium.com, which 307-redirects API auth\n to `/login`. Added `_resolve_identity_url()` mapping the aitherium.com topology to\n `idp.aitherium.com` (the Identity API); localhost/bespoke pass through unchanged.\n- **Cloudflare 403'd urllib.** `idp.*` sits behind Cloudflare, which blocks urllib's\n default `Python-urllib/3.x` UA. Device-code + poll requests now send a real\n User-Agent + Accept (verified urllib→idp 200, was 403).\n- **`adk host` now self-authenticates via device flow** when there's no token, and\n **retries via device flow on a 401/403** (a stale saved token no longer dead-ends\n registration).\n\n## [2.11.0] - 2026-06-14\n\n### Added — attach your own MCP \"hands\" to a self-hosted agent\nA self-hosted agent now has the full brain (your model) · body (this loop) · **hands**\n(your tools) trifecta. The AitherOS platform relays the MCP servers you registered for\nyour agent in the `/stream` request body as `mcp_endpoints`, and the loop wires their\ntools into the ReAct turn:\n- **`adk/mcp_endpoint_tools.register_mcp_endpoint_tools(agent, endpoints)`** — speaks MCP\n JSON-RPC (`tools/list` to discover, `tools/call` to invoke) and registers a namespaced\n proxy tool (`{endpoint}__{tool}`) for each advertised tool. Mirrors `app_proxy_tools`.\n Best-effort: an unreachable endpoint registers nothing and never raises.\n- **`POST /stream` and `POST /chat/stream`** accept an optional `mcp_endpoints` list\n (`[{name, url, [headers]}]`); `_aitheros_stream` attaches them to the agent before the\n turn. Existing callers are unaffected (the field is optional).\n\n## [2.10.1] - 2026-06-14\n\n### Fixed — self-hosted `aither-serve` was crash-on-startup (2.10.0 regression)\nA fresh `pip install aither-adk && aither-serve --backend deepseek` could not boot. Found\nand fixed while standing up a real self-hosted DeepSeek agent end-to-end:\n- **Startup `NameError: name 'port' is not defined`** in three lifespan helpers\n (`_join_aithernet`, `_init_a2a_server`, `_start_mesh_hosting`) — they referenced a bare\n `port` no longer in scope. Now use `config.server_port`, and `main()` writes the resolved\n `--port`/`--host` back to `config` so every lifespan helper and the invoke_url see the\n actual bound port.\n- **Fleet self-registration crashed the whole server** — the registration handlers caught\n `(ImportError, RuntimeError, OSError, ConnectionError)` but **not** `httpx.HTTPError`, so a\n `RemoteProtocolError`/connect error talking to the control plane took the agent down. All\n network-facing lifespan handlers now also catch `httpx.HTTPError` (non-fatal, as intended).\n- **`--backend` was ignored when `AITHER_API_KEY` was set** — `_auto_detect` picked the cloud\n gateway before the explicit backend, and `_try_elysium_fallback` then replaced the chosen\n provider. An explicit backend (deepseek/openai/anthropic/your own vllm/ollama) now always\n wins; \"auto\"/\"gateway\" still flow through detection. The operator's own brain is never\n silently hijacked.\n- **Approved-tool trace truncated after `/sessions/{id}/confirm`** — the SSE relay treated\n `AgentResponse.tool_calls_made` (a `list[str]`) as dicts (`tc.get(...)`), raised\n `AttributeError`, and an over-narrow `except` swallowed it so the client saw only\n `session_start` + heartbeats. The relay now handles string tool names, the `except` is\n broad (always emits a typed `error` + terminal `complete`), and the module-level\n `_strata_ingest` `NameError` + Chronicle `session_id` kwarg mismatch are fixed.\n\n## [2.10.0] - 2026-06-13\n\n### Added — human-in-the-loop tool approval (`adk/approval.py`)\n- **Pause before a gated tool, resume on decision.** An agent can now pause its turn before\n executing a sensitive tool and wait for a human's allow/deny — so a managed control plane\n (or any operator) can gate tool use without forking the loop.\n- **Policy via `AITHER_TOOL_APPROVAL`** — a comma-separated list of tool names (optionally\n `agent:tool`), or `*` for every tool. Empty = no gating (unchanged behavior).\n- **Survives restarts / days-later approvals.** The paused turn (user message + pending tool\n calls) is persisted to disk keyed by `session_id`; resume is a fresh request, not a held\n connection. Decisions are recorded per `(session_id, tool_name)`.\n- **`AgentResponse.requires_action` / `.pending`** — set when a turn paused; `pending` lists\n the gated tool calls awaiting a decision.\n- **`AitherAgent.resume(session_id, decisions)`** — records the decisions and re-runs the\n paused turn (the gate consumes them: allowed tools execute, denied tools get a denial\n observation, the turn continues — and may pause again on a different gated tool).\n- **Server:** `POST /sessions/{id}/confirm` records decisions and streams the resumed turn;\n `/stream` emits a `requires_action` event when a turn pauses.\n\n## [2.9.0] - 2026-06-13\n\n### Added — universal LLM continuation primitive (`adk/llm/continuation.py`)\n- **`run_continuation()` + `stitch()`** — one shared continue-until-complete primitive. When a\n completion stops because it hit the output token cap (`finish_reason == \"length\"`), it continues\n the generation and STITCHES the chunks (overlap-dedup + restart-detect) into a complete answer,\n instead of every call-site hand-rolling its own retry. Bounded three ways (max rounds AND total\n output ceiling AND a no-growth break), and it never mutates the caller's message history.\n- **`LLMProvider.chat_with_continuation()`** and **`LLMRouter.chat_with_continuation()`** — every\n provider AND the router inherit the primitive, so any call-site gets continuation for free.\n- Kill-switch `ADK_LLM_CONTINUATION=off`; tunables `ADK_LLM_MAX_CONTINUATIONS`,\n `ADK_LLM_MAX_TOTAL_OUTPUT_TOKENS` (defaults: 2 rounds / 8192 tokens).\n\n### Changed\n- The ReAct loop's truncation recovery now uses the shared primitive. The previous inline\n doubling-retry REPLACED the partial response (losing earlier text in `resp.content`) and injected\n continuation scaffolding into the live message history; it now STITCHES and leaves history clean.\n Tool-call turns are never continued as text.\n\n### Included — previously-committed-but-unreleased changes\n- 2.8.2: TLS verification on by default (`verify=False` removed).\n- 2.8.1: baked license + pack-signing public keys.\n- 2.8.0: version-bump release.\n\n## [2.7.0] - 2026-06-05\n\n### Added — provider-agnostic LLM cost levers (at the `LLMProvider` ABC)\n- **Prompt caching.** New `ProviderCapabilities` (`prompt_cache: explicit|automatic|none`, `batch`) and\n normalized `LLMResponse.cache_read_tokens`/`cache_write_tokens`, so the \"tokens saved\" meter never\n depends on which backend served the turn. Anthropic inserts `cache_control` breakpoints over the stable\n system+tools prefix; OpenAI/DeepSeek caching is automatic and their `cached_tokens` are now surfaced\n (previously discarded). Default-on in `AitherAgent.chat`; override with `cache=False`.\n- **ResponseCache** (`adk/llm/cache.py`) — exact-match response cache with a pluggable backend, wired into\n `LLMRouter` as an opt-in (`LLMRouter(response_cache=…)` + per-call `cacheable=True`). OFF by default — no\n determinism change unless opted in.\n- **Gemini provider** (`adk/llm/gemini.py`) with explicit `cachedContents` caching; registered in the router.\n- **Async batch runner** (`adk/llm/batch_runner.py`) — `run_batch()` over the Anthropic Message Batches /\n OpenAI Batch APIs (~50% off) with a transparent fallback to concurrent `chat()` where unsupported.\n- **Graph-RAG retriever interface** (`adk/graph_retriever.py`).\n\n### Added — principal authority (prompt-injection defense enforced outside the model)\n- `adk/auth.py`: `Principal` / `AuthContext` / `PrincipalResolver` (+ deny-all default).\n- `ToolRegistry.execute(name, args, auth=…)` — a default-deny authorization gate at the tool boundary, so a\n forbidden tool call is blocked regardless of what the LLM emits. `ToolDef` gains `required_clearance` /\n `action_class`. Threaded through `AitherAgent.chat`; `auth=None` is fully backward-compatible.\n- `adk/channel_auth.py`: Telegram webhook-secret + CEO-id checks and HMAC-signed approvals.\n\n## [2.6.4] - 2026-06-05\n\n### Fixes\n- **Loop-guard nudges corrupted the message history → DeepSeek 400 mid-run.**\n When the ReAct loop emitted multiple tool calls in one assistant turn and the\n loop guard fired a WARN/BLOCK/CIRCUIT_BREAK on one of them, it spliced a\n standalone `system` nudge message *between* the tool results. DeepSeek (and\n strict vLLM chat templates) require an assistant `tool_calls` message to be\n followed by exactly one `tool` result per `tool_call_id`, contiguously — so the\n interleaved message produced `400 … insufficient tool messages following\n tool_calls message` and aborted the turn (this is reliably hit after crossing\n the 15-call circuit-break threshold, where every subsequent call WARNs).\n Per-call guidance is now folded into that call's own `tool` result, and any\n standalone steering is deferred to a single message AFTER the whole batch —\n preserving the `tool_calls`↔`tool_results` pairing every backend requires.\n Regression test: `deep-research-agent/tests/test_msg_structure.py`\n (loopguard-flood scenario).\n\n## [2.6.3] - 2026-06-04\n\n### Fixes\n- **Transient provider errors killed a run on the first blip.** A single\n `502 Bad Gateway` / `503` / `529 overloaded` / `429` from the upstream API\n aborted the whole request — the Anthropic provider had **no retry at all**, and\n the OpenAI-compatible provider only retried `429`. Both now retry transient\n statuses (`408/429/500/502/503/504/529`) with `Retry-After`-aware exponential\n backoff (3 attempts), and surface the provider's body if all retries are\n exhausted. Non-transient `4xx` (e.g. `400`) still fails fast. Applies to\n `chat` and `chat_stream` on both providers.\n\n## [2.6.2] - 2026-06-04\n\n### Fixes\n- **DeepSeek (and strict OpenAI-compatible backends) 400'd on long tool\n conversations.** The ReAct loop legitimately injects mid-conversation\n `system` steering messages (the \"[DIMINISHING RETURNS]\" hint after several\n low-output tool iterations, loop-guard nudges). OpenAI tolerates a non-leading\n `system` message; DeepSeek rejects it with a 400. Long research runs\n (many `fetch_url` calls) reliably tripped this. Fix: non-leading `system`\n messages are demoted to `user` at the OpenAI-compatible provider boundary\n (`_demote_nonleading_system`) — steering intent preserved, payload valid on\n every backend; leading system prompt(s) untouched.\n- **Opaque HTTP errors.** `resp.raise_for_status()` discarded the provider's\n response body, so a 400/422 surfaced as a bare `Client error '400 Bad\n Request'` with no cause. `_ensure_ok` now includes the provider's actual\n message (e.g. `context_length_exceeded`, `Invalid 'messages'`) in the raised\n error, for both `chat` and `chat_stream`.\n\n## [2.6.1] - 2026-06-04\n\n### Fixes\n- **`MCPServer.mount()` returned HTTP 422 for every `/mcp` call.** Under\n `from __future__ import annotations` the route handler's `request: Request`\n annotation is a string that FastAPI resolves against the *module* globals, but\n `Request` was imported locally inside `mount()` — so FastAPI couldn't resolve\n it and treated `request` as a required query param. The MCP server endpoint\n (`initialize`, `tools/list`, `tools/call`) was unusable when mounted. FastAPI\n symbols are now imported at module level (guarded; the `[node]` extra still\n gates actual use). Any agent's tools can now be exposed as a working MCP\n server with `MCPServer(...).mount(app)`.\n\n## [2.6.0] - 2026-06-04\n\n### Streaming + scaffolding\n- **`AitherAgent.stream_chat`** — reliable streaming sibling of `chat()`: native\n tool-calling loop (concludes on its own, unlike the text-protocol `stream_react`)\n that emits live `tool`/`tool_result` events and streams the answer as `token`\n events. `token_delay` paces tokens for a typing effect.\n- **`adk new <template>`** — scaffold a full, runnable template app. Ships the\n `deep-research` template (a cited web-research agent: pack + server + web UI):\n `adk new deep-research`.\n\n### Fixes\n- **BYO-key no longer community-capped.** The free-tier monthly token cap now applies\n only to the metered Aitherium gateway backend, never to a user's own provider key\n (Anthropic/OpenAI/DeepSeek/Ollama/vLLM). Previously it could brick a self-hosted\n agent after ~100k tokens on the user's own key.\n- **Stronger, grounded memory recall.** `chat()` now injects the top-6 knowledge-graph\n hits (untruncated) with a hard no-fabrication instruction — was top-3 truncated to\n 200 chars, which surfaced partial facts and let weaker models invent the rest.\n- **Tool-arg coercion.** `ToolRegistry.execute` coerces LLM-supplied args toward each\n parameter's annotated type, so a tool no longer crashes when a model passes a list\n or string where an int/float/bool was expected.\n\n### Docs\n- New `docs/AGENT_DEV_GUIDE.md` — the golden path for building agents/packs.\n\n## [2.5.0] - 2026-06-04\n\n### Continual learning — agents that learn from runs and reuse memory\n\nAgents now improve the second time they do a task instead of starting from scratch.\n\n### Added\n- **Recall-before**: `AitherAgent.chat()` searches the local `SkillStore` and injects the\n top matching learned skills as a system block, so the model reuses proven procedures.\n- **Learn-after**: `_learn_after()` runs after each response — reinforces (`success_count++`)\n any skills that were recalled+reused, and extracts + saves a NEW skill from successful\n multi-tool runs via `SkillExtractor`. Wired at both `chat()` return paths.\n- Gated by `AITHER_SKILLS` (default on). The previously-unused `SkillStore`/`SkillExtractor`\n are now connected to the run loop.\n\n### Notes\n- Pairs with the platform-side LearnedProcedure substrate (recipes → scored procedures →\n skills/tools/A2A/packs → MCTS priors → evolution). The SDK shares memory via the Spirit\n bridge when configured.\n\n## [2.0.0] - 2026-06-02\n\n### Open-core boundary (BREAKING)\n\nThis release draws a single, enforced free/paid line. The free tier stays\ngenuinely useful (real agent, typed + graph + code memory, ReAct, ~essential\ntools); paid tiers unlock the *scale* capabilities via portal.aitherium.com.\n\n### Added\n- **`adk/licensing.py`** — the entitlement keystone. Resolves a tier\n (COMMUNITY by default), verifies optional portal-signed `~/.aither/license.json`\n (Ed25519), and answers `can_use_fleet/channels/cron/swarm/auto_neurons`,\n `is_agent_licensed`, `max_effort`, etc. **Fail-closed**: no/invalid/expired\n license → free tier; an unsigned license never grants premium.\n `AITHER_TENANT_SLUG=aitherium` → unrestricted INTERNAL tier.\n- **Moat guard** (`scripts/check_moat_boundary.py`) wired into the publish\n workflow — blocks any wheel that leaks the moat or drops the keystone.\n- Tests: `tests/test_licensing.py`, `tests/test_moat_boundary.py`.\n\n### Changed (gating — fail-closed)\n- Free tier effort is capped at 3 (reasoning effort 7–10 needs Professional).\n- Monthly token budget hard-block on the free tier (no silent overspend).\n- Gated behind a paid tier: fleet mode (`FleetConfig`), cross-agent delegation\n (`AgentForge.delegate`), channel adapters, cron scheduler, proactive\n auto-neurons, and `swarm`/`swarm_code` dispatch.\n\n### Removed\n- **`adk/nanogpt.py`** (on-device training IP) removed from the published SDK\n and the public repo; relocated to the internal AitherOS moat. Excluded from\n the wheel as defense-in-depth.\n- Pre-2.0 releases shipped this IP and ungated capabilities and are being\n yanked (PyPI) / removed (GitHub releases). See `scripts/yank_leaky_releases.py`\n and `scripts/purge_public_leaks.sh`.\n\n## [1.20.0] - 2026-05-27\n\n### Fixed\n- **Pack discovery path mismatch** — `_find_local_packs()` now scans both\n `~/.aither/packs/` and `~/.aitheros/packs/`, so packs installed via\n `adk pack install` are discovered by `adk-workspace`.\n\n### Added\n- **Auto-register on pack install** — `adk pack install` now auto-registers\n with the portal if the pack contains an `agent.yaml` with a `portal` section.\n One command does download + extract + register.\n- **Flux event on agent registration** — `developer_portal.py` emits\n `agent.registered` Flux event so the portal fleet UI updates in real-time.\n\n## [1.15.0] - 2026-05-26\n\n### Added\n- **Cloud memory** — `adk quickstart --cloud` now tests gateway memory and\n auto-configures Spirit URL so memories persist across devices via\n `gateway.aitherium.com/v1/memory/teach` and `/v1/memory/recall`.\n- **Config→env export for cloud mode** — `Config.from_env()` now exports\n `AITHER_CLOUD_MODE`, `AITHER_SPIRIT_URL`, and spirit path env vars from\n saved config so Memory and other modules pick them up without needing\n explicit env vars set by the user.\n- **Cloud Quick Start section** in README — 3-command setup for users who\n just want a Claude/GPT-4/DeepSeek API key and the full agent harness.\n- **`--memory` profile for node deployment** — `adk deploy node --memory`\n adds Spirit (8087) + WorkingMemory (8101) containers for persistent\n vector memory. Without this, agents use local SQLite only.\n- **Configurable vLLM in node compose** — `AITHER_VLLM_MODEL`,\n `AITHER_VLLM_GPU_UTIL`, `AITHER_VLLM_CTX_LEN`, `AITHER_VLLM_QUANT_ARGS`\n env vars let `adk setup` write GPU-detected tier settings to `.env`\n and have the node compose pick them up (TQ4 for small GPUs, BNB for large).\n\n### Fixed\n- **Memory cloud routing** — `Memory._spirit_teach()` and `_spirit_recall()`\n now use configurable paths and auth headers for gateway proxy. Previously\n cloud-only users silently lost all Spirit memory calls.\n\n## [1.9.0] - 2026-05-24\n\n### Added\n- **Composable ToolPack system** — agents can load tool packs from\n `.toolpack.yaml` directories. Drop a pack in `~/.aitheros/packs/` or\n set `AITHER_PACKS_DIR`, and any agent discovers it automatically.\n- **`tools.packs` in agent.yaml** — list tool pack IDs to auto-load\n (e.g. `packs: [crm-tools, calendar-pro]`).\n- **`register_tool_packs(agent, pack_ids, packs_dir)`** in `builtin_tools` —\n programmatic pack loading for agents.\n- **`AITHER_TOOL_PACKS` env var** — comma-separated pack IDs loaded on\n agent init (alternative to YAML config).\n- **Unknown builtin categories resolve as pack IDs** — `tools.builtin:\n [file_io, crm-tools]` now works: `crm-tools` is looked up as a pack.\n\n## [1.8.0] - 2026-05-24\n\n### Removed\n- **11 dead modules** (3,023 lines) — `agent_deploy`, `cloud_deploy`, `create`,\n `phonehome`, `sdk_bridge`, `sovereign`, `telemetry`, `telemetry_config`,\n `error_reporter`, `anonymizer`, `package`. None were imported anywhere.\n- **29 ghost exports** from `__init__.py` — `secrets`, `otel`, `fs_sandbox`,\n and `vector_memory` modules were listed in `__all__` and `__getattr__` but\n the backing files never existed. Accessing them raised `ModuleNotFoundError`.\n- **Stale dist/ artifacts** — removed old wheels/tarballs (v0.4.0–v1.4.1, 11 MB).\n- **Orphan test scripts** — `test_phase1.py` and `test_phase23.py` (one-off\n validation scripts not part of the pytest suite).\n\n### Fixed\n- **Version mismatch** — `__init__.__version__` now matches `pyproject.toml`\n (was stuck at 1.6.0).\n\n## [1.5.0] - 2026-05-23\n\n### Added\n- **`--sovereign` flag for `adk deploy node`** — registers the node with the\n Aitherium hub via federation after deployment. Saves federation credentials\n to `~/.aither/.env.federation`. Accepts `--hub` (custom hub URL) and\n `--tenant` (tenant slug) options. Non-fatal: node runs standalone if\n registration fails.\n- **Federation → fleet cross-post** (Genesis) — sovereign nodes that register\n or heartbeat via `/federation/register` and `/federation/heartbeat` now\n automatically appear in the fleet dashboard (`/fleet/deployments`) with\n `node_type: \"sovereign\"` and live status/metrics.\n- **\"Deploy Sovereign Node\" section on Connect page** (AitherVeil) — Section 8\n with ADK deploy commands, fleet dashboard links, and provision page links.\n- **`deploy-node` action in tunnel proxy** (AitherVeil) — Connect page and\n external callers can now request bootstrap scripts via\n `POST /api/tunnel/devworkspace?action=deploy-node`.\n\n### Fixed\n- **Fleet provision page** — API URL corrected from `/api/fleet/provision` to\n `/api/bridge/genesis/fleet/provision` (matches bridge proxy pattern).\n- **Fleet detail page** — API URL corrected from `/api/fleet/deployments/` to\n `/api/bridge/genesis/fleet/deployments/` (same fix).\n- **Dockerfile** — entry point corrected from `aither-serve` to `adk-serve`\n (matches `pyproject.toml` script registration). Version label updated.\n\n## [1.4.1] - 2026-05-22\n\n### Fixed\n- **macOS executable builds** — dropped `--target-arch universal2` from\n `packaging/build_executable.py`. PyPI wheels for `pydantic_core` and other\n native deps are arch-specific, not fat binaries, so universal2 always failed\n with `IncompatibleBinaryArchError`. Now builds for the runner's native arch\n (`aither-macos-arm64`).\n- **Release workflow strategy** — added `fail-fast: false` so a single\n executable build failure no longer cancels the other OS jobs.\n- **PyPI publish** — added `skip-existing: true` so re-runs of the same\n version don't fail when the artifact already exists.\n- **GitHub Release job** — now runs whenever `pypi` succeeds even if some\n executable matrix entries fail, so the release page is always created with\n whatever binaries did build successfully.\n\n## [1.4.0] - 2026-05-22\n\n### Added\n- **Sovereign template now ships with `agent_core` 5-tier memory** — generated\n backends include the canonical `agent_core/` package (working → episodic →\n semantic → procedural → identity) plus `agent_memory.py` helper, vendored\n from `portal-kit-backend`.\n- `aiosqlite>=0.20.0` added to sovereign template `requirements.txt` for\n local agent_core SQLite storage.\n- Document tombstoning: `evict_cached_conversations_referencing` is wired\n through document-delete handlers in consumer apps so memories citing\n deleted documents are blocked from recall.\n- New tests: 13 smoke tests for `portal-kit-backend/agent_core` covering\n promotion thresholds, decay rates, store roundtrip, tombstone recall\n blocking, circuit breaker state machine, and chat-turn recording.\n\n### Changed\n- `scripts/vendor_agent_core.py` now syncs canonical `agent_core` to 4\n targets: WorkspaceRuntime, ADK sovereign template, and the GargBot +\n Chelle consumer mirrors (previously only 2).\n\n## [1.2.1] - 2026-05-21\n\n### Added\n- `adk train` command group — 6 subcommands: status, launch, logs, cancel, runs, register-gpu\n- `/slash-commands` endpoint — ADK server auto-exposes all 35 CLI commands as structured JSON manifest for AitherShell\n- `/cli/execute` endpoint — AitherShell can run any CLI command through the server\n- `build_command_manifest()` — introspects full argparse tree (commands, args, choices, subcommands)\n- `_register_commands()` extracted from main() for shared parser construction\n\n### Changed\n- `_get_genesis_url()` now probes: Genesis → ADK server → Aitherium cloud gateway (works without Genesis)\n\n## [1.2.0] - 2026-05-21\n\n### Added\n- **Runtime backend switching** — `agent.switch_backend(\"deepseek\")` and `agent.llm.switch_backend()` change provider without recreating agent\n- **Hybrid reasoning** — `agent.set_reasoning_backend(\"anthropic\")` routes effort 7+ to cloud API while keeping local orchestrator\n- **First-class DeepSeek provider** — `deepseek` in effort model table with `deepseek-chat` and `deepseek-reasoner`\n- **TQ4 quantization tiers** — `nano` (6GB, TurboQuant 4-bit), `standard-tq4` (12GB, both models TQ4), `hybrid-tq4` (6GB + cloud reasoning)\n- **`adk backend` CLI** — `list`, `set`, `set-reasoning`, `test` subcommands\n- **`adk quickstart`** — unified first-run wizard (setup + auth + shell)\n- **`adk tools`** — list local + MCP tools with tier markers\n- **`adk backup`** — tarball export of `~/.aither/`\n- **`adk ingest`** — feed docs/files into knowledge graph\n- **`adk setup nemotron`** shortcut — alias for `--tier lite`\n- **`--reasoning-api`** flag in setup — `anthropic`, `openai`, `deepseek`, `gateway`\n- **`--dgx-spark URL`** flag — remote vLLM endpoint configuration\n- **DGX Spark auto-detection** — scans `spark.local`, `192.168.0.33`, `AITHER_DGX_URL` env\n- **Post-setup smoke test** — sends real inference request after container startup\n- **Schema migration system** — version table + migration runner in `memory.py` and `graph_memory.py`\n- **Configurable vLLM port scan** — `AITHER_VLLM_PORTS` env var for custom ports\n- **`aithershell` entry point** — alias for `adk` in pyproject.toml\n- Shell auto-downloads binary on first `adk shell` (no `--install` needed)\n- Shell pre-flight check warns if no backend detected\n- Shell passes full config via env vars (API key, tenant, inference URL)\n- DGX/Remote and Cloud API checks added to `adk doctor`\n- Config fields: `reasoning_backend`, `reasoning_api_key`, `deepseek_api_key`, `dgx_url`, `vllm_extra_ports`\n\n### Fixed\n- `test_aeon_chat_basic` timeout — mock scope was exiting before request\n- `adk doctor` check_disk timeout — cap rglob at 5000 files\n\n## [1.1.7] - 2026-05-19\n\n### Added\n- Remote agent pipeline — `adk onboard` → fleet dispatch → remote inference\n- Agent-to-agent federation with mesh relay\n- `adk connect` for desktop mesh enrollment\n\n## [1.1.6] - 2026-05-19\n\n### Fixed\n- Tool call execution recovery across all backends\n- Hermes tool call fallback parsing for vLLM\n\n## [1.1.5] - 2026-05-19\n\n### Fixed\n- Tool call execution + Hermes fallback parsing improvements\n\n## [1.1.0] - 2026-05-18\n\n### Added\n- Multi-channel gateway (Telegram, Discord, Slack, webhook)\n- Aeon group chat with 7 presets\n- Skills auto-extraction from multi-step sessions\n- MCP stdio server for Claude Code integration\n- `adk gateway`, `adk cron`, `adk skills`, `adk soul` commands\n- SOUL.md import/export for portable agent identity\n- Elysium cloud device-flow authentication\n\n## [1.0.0] - 2026-05-18\n\n### Breaking\n- Package consolidated — `aither_adk` namespace replaced by `adk` + `adk.platform`\n- 93 duplicate files removed from `AitherOS/packages/aither_adk/`\n\n### Added\n- Standalone core primitives (`adk.core`: Agent, Tool, Memory, ModelBackend, Capability, Trace)\n- `adk.platform` sub-package merging all platform toolkit modules\n- Compatibility shims for legacy `from aither_adk.*` imports\n\n### Changed\n- Version synced across pyproject.toml, `adk/__init__.py`, `adk/platform/__init__.py`, npm package\n- Classifier promoted from Alpha to Beta\n\n## [0.16.0] - 2026-04-16\n\n### Swarm Coding Engine & Repowise Integration\n- **`repowise_search` tool** — Semantic + keyword hybrid code search via Repowise, with ripgrep fallback\n- **`swarm_code` tool** — Dispatch complex tasks to 11-agent swarm pipeline (ARCHITECT->SWARM->REVIEW->JUDGE)\n- **`agent.swarm()`** — Async convenience method with configurable mode, effort, timeout\n- **`agent.code_search()`** — Async convenience method returning structured results\n- New tool categories: `repowise` and `swarm` in TOOL_CATEGORIES\n- `repowise_search` and `swarm_code` added to `__init__.py` exports with lazy loading\n- `IDENTITY_DEFAULTS` updated: repowise for code-focused agents, swarm for orchestration agents\n- Standalone graceful degradation: repowise falls back to ripgrep, swarm returns structured error\n\n## [0.13.0] - 2026-04-02\n\n### Graph Faculties — Local Knowledge for Every Agent\n- **CodeGraph** (2,799 lines) — Full Python AST indexer with call graph, keyword/semantic/hybrid query, embedding matrix cache, incremental re-indexing, multi-hop chain expansion\n- **MemoryGraph** (1,339 lines) — Graph-based persistent agent memory with 10 edge types, hybrid query (keyword + semantic + graph expansion), multi-hop recall, pickle persistence\n- **EmbeddingProvider** — 4-backend fallback chain: sentence-transformers (GPU/CPU) -> Ollama -> Elysium cloud -> feature hashing (zero deps)\n- **BaseFacultyGraph** — Abstract base with HMAC-SHA256 validated pickle persistence\n\n### Agent Integration\n- `agent.set_code_graph(cg)` — auto-registers `code_search` + `code_context` tools\n- `agent.set_memory_graph(mg)` — auto-registers `remember` + `recall` + `memory_stats` tools\n- Both graphs inject context into LLM prompts automatically during chat\n\n### Zero-Config Onboarding\n- `adk start` / `adk` (no args) — auto-detect project, index code, detect LLM, persistent memory, interactive chat\n- Works for any directory: Python codebases, doc folders, mixed workspaces\n- Auto-detects LLM: Ollama -> vLLM -> Elysium -> OpenAI -> Anthropic\n- Per-project persistent memory in `~/.aither/memory/<project>`\n- `adk index <path>` — standalone indexing with progress bar and stats\n\n### MCP Gateway\n- `POST /v1/embeddings` — OpenAI-compatible embedding proxy to vLLM-embeddings:8209\n- Available to all tiers (embeddings are free)\n- ADK EmbeddingProvider uses this as Elysium cloud fallback\n\n### Optional Dependencies\n- `pip install aither-adk[graphs]` — numpy for 10x cosine similarity speedup\n- `pip install aither-adk[embedding]` — sentence-transformers + torch for local GPU embeddings\n- `pip install aither-adk` alone — graphs work with feature hashing (zero deps)\n\n## [0.12.0] - 2026-04-01\n\n### Bootstrap & Service Discovery\n- Version handshake -- ADK checks major.minor compatibility with Genesis/Node\n- Background reconnect loop -- ServiceBridge re-probes every 30s in standalone mode\n- Port 8080/8090 documented -- MCP vs OpenAI-compat clearly separated\n- Genesis URL configurable via GENESIS_URL env var (was hardcoded)\n- Standalone mode warning -- visible stderr alert when AitherOS not detected\n- Auto-reconnect on startup when services come online\n\n### Elysium Cloud Inference\n- Unified gateway URL -- gateway.aitherium.com handles auth + billing + inference\n- Streaming inference -- SSE passthrough for /v1/chat/completions with stream=true\n- Auth proxy routes -- /v1/auth/register, /v1/auth/login, /v1/auth/me\n- Billing proxy -- /v1/billing/balance through gateway\n- AitherConnect Elysium fallback -- cloud inference when local Genesis is down\n- AitherDesktop Elysium fallback -- third-tier chat fallback after Node\n\n### Infrastructure\n- /discovery endpoint on Genesis -- unified service URLs/versions/health\n- /api/config/services on Veil -- runtime port config for client-side JS\n- Veil healthcheck gates on Genesis -- unhealthy when backend is down\n- Desktop crash detection 90s->60s (threshold 3->2)\n\n## [0.11.0] - 2026-04-01\n\n### Agent Execution Quality (Claude Code Parity)\n- Raise loop guard block threshold from 3 to 4 -- agents get more room for iterative search\n- Soft synthesis nudge for effort >= 4 (no tool stripping, trust the model)\n- max_output_tokens escalation -- retry up to 3x when response is truncated\n- Tool result pairing guarantee -- synthetic error for orphaned tool_use blocks\n- Micro-compaction of old tool results -- save context tokens on long sessions\n- First-turn tool forcing only for effort >= 6 (trust model for lower effort)\n- Diminishing returns detection -- nudge agent when 3+ turns produce < 500 tokens\n- Message normalization -- merge consecutive same-role messages, strip empties\n- LLM retry with exponential backoff (5 retries, 500ms-16s, jitter)\n\n## [0.9.0] - 2026-03-16\n\nThe \"connected world\" release. Cross-platform identity pairing, voice capabilities, and multi-channel integration.\n\n### Added\n- **Pairing**: Cross-platform identity linking (`adk/pairing.py`)\n - `PairingManager` — SQLite-backed identity linking with 6-char pairing codes\n - Link users across Telegram, Discord, Slack, WhatsApp with 10min TTL codes\n - Canonical session IDs for cross-channel conversation continuity\n - `get_session_id()` returns \"user-{id}\" for paired users\n- **Voice**: Speech-to-text and text-to-speech client (`adk/voice.py`)\n - `VoiceClient` — async STT/TTS/emotion via AitherVoice service\n - Convenience functions: `hear()`, `say()`, `feel()`\n - 6 voice options: alloy, echo, fable, nova, onyx, shimmer\n - Emotion detection with intensity scoring\n- New exports: `PairingManager`, `PairingResult`, `PlatformIdentity`, `VoiceClient`, `TranscriptionResult`, `SynthesisResult`, `EmotionResult`\n\n### Changed\n- `__init__.py` exports expanded with pairing and voice symbols\n\n## [0.6.0] - 2026-03-13\n\nThe \"group mind\" release. Multi-agent group chat, creative tools, and Iris identity.\n\n### Added\n- **Aeon**: Multi-agent group chat engine (`adk/aeon.py`)\n - `AeonSession` — persistent group chat with parallel agent execution\n - 7 presets: balanced, creative, technical, security, minimal, duo_code, research\n - Orchestrator synthesis: Aither summarizes all agent responses\n - Serial execution for Ollama, parallel for vLLM/cloud\n - ConversationStore persistence with `type: \"aeon\"` metadata\n - `group_chat()` one-shot convenience function\n- `aither aeon` CLI command — interactive terminal group chat with color-coded agents\n - `-p/--preset`, `-a/--agents`, `-r/--rounds`, `--no-synthesize` flags\n - `reset` and `quit` commands\n- Server endpoints: `POST /aeon/chat`, `GET /aeon/presets`, `GET /aeon/sessions/{id}`\n- Creative tools in builtin_tools: `image_generate`, `image_refine`, `image_search`, `video_generate`\n- Iris agent identity with visual generation capabilities\n- 48 Aeon tests (data models, presets, chat, context, persistence, server, exports)\n\n### Changed\n- `__init__.py` exports: `AeonSession`, `AeonResponse`, `AeonMessage`, `group_chat`, `AEON_PRESETS`\n\n## [0.5.0] - 2026-03-13\n\nThe \"tenant-ready\" release. Multi-tenant admin, permission grants, safety profiles, and a full setup wizard.\n\n### Added\n- `aither setup` interactive setup wizard with hardware detection, model selection, identity config\n- Strata storage backend: SQLite WAL persistence for conversations, memories, knowledge graphs\n- CLI test runner: `aither test` with auto-discovery and parallel execution\n- Permission grants system for MCP tool access control\n- MCP account management tools\n- LLM provider auto-detection for Ollama and vLLM endpoints\n- Apache-2.0 LICENSE file\n- Elysium desktop sync module\n- Comprehensive CLI test suite (493 tests)\n- Strata storage test suite (972 tests)\n- LLM provider tests (82 tests)\n\n### Changed\n- CLI expanded: `aither setup`, `aither test`, `aither bugreport`, `aither doctor`\n- Strata module rewritten as full local-first storage engine\n- Server startup includes Strata initialization\n- README rewritten with clearer quickstart and architecture docs\n\n### Fixed\n- Dead documentation links\n- Python 3.11 compatibility issues\n- Ruff per-file-ignores configuration\n- Identity provisioning edge cases\n- Docker node-gyp native dependency builds (python3 + build tools)\n\n## [0.4.0] - 2026-03-13\n\nThe \"own your AI\" release. Self-hosted agent OS for people who don't want their data on someone else's servers.\n\n### Added\n- Muse agent identity (creative/artistic generation)\n- Port 8120 to vLLM scan for ExoNodes discovery\n- Public roadmap with milestone-based porting schedule\n- Competitive positioning: self-hosted alternative to cloud-locked AI appliances\n\n### Changed\n- LLM router: compute-aware effort routing (replaces effort-level context gating)\n- README rewritten for sovereignty-first messaging\n- Package description updated\n- Promoted from alpha to stable release\n\n### Fixed\n- Version string consistency between pyproject.toml and __init__.py\n- vLLM port scanning now includes port 8120 (ExoNodes)\n\n## [0.3.1] - 2026-03-09\n\n### Added\n- GraphMemory: SQLite knowledge graph with Ollama embeddings and hybrid search\n- NeuronPool: auto-fire pre-LLM data gathering agents\n- NanoGPT: pure Python char-level transformer with LoRA fine-tuning\n- Safety gates: IntakeGuard (input), LoopGuard (recursion), Sandbox (code exec)\n- Event system: EventEmitter with chat/tool/forge event types\n- Builtin tools: identity-based tool selection\n- ServiceBridge: auto-discovery of AitherOS services\n- Streaming: chat_stream with safety gate integration\n- Auth middleware: Bearer token authentication\n- CLI: `aither init` and `aither serve` commands\n\n### Changed\n- Wired 5 previously disconnected modules into agent loop\n- 522 total passing tests (up from 85)\n\n## [0.3.0] - 2026-03-07\n\n### Added\n- Clean-room agent development kit\n- Multi-backend LLM providers (Ollama, OpenAI, Anthropic)\n- AitherAgent class with @tool decorator\n- SQLite conversation memory\n- OpenAI-compatible server (`aither-serve`)\n- 16 agent identities as package data\n- MCP bridge to mcp.aitherium.com\n- Privacy-centric opt-in telemetry\n- Bug reporting CLI and API\n- FastAPI server with OpenAI-compatible endpoints\n- Hardware auto-detection (5 tiers, 11 profiles)\n- Fleet orchestration and multi-agent coordination\n- A2A mesh protocol\n- Federation protocol for cross-instance agent dispatch\n\n### Initial release\n- 85 passing tests\n- Apache-2.0 license\n"}, "653f4614615b838b715b0ccb35c53e888f69d886": {"t": 1785748189.915884, "n": 62391, "h": "74888692f3e6d6db", "c": "# Changelog\n\nAll notable changes to aither-adk will be documented in this file.\n\n## [2.14.6] - 2026-07-04\n\n### Added\n\n- **`adk.preflight` — self-bootstrap capability probe.** `run_preflight(agent,\n spec)` wakes an agent up knowing what it is actually plugged into: a bounded\n (1.5s) parallel `LivenessProbe` over the router's resolved backends\n (primary/reasoning), the embeddings winner, remote MCP tools, the structured-ML\n / `/ml/teach` endpoints, and voice — returning an honest `CapabilityReport`.\n Status vocabulary distinguishes reachability from entitlement\n (`OK`/`MISSING`/`UNREACHABLE`/`AUTH`/`TIER_DENIED`/`UNSUPPORTED`), a hosted slot\n never prints a bare `OK`, and a required-but-unsatisfied slot aborts before the\n loop instead of blind-firing. Headless task/rules resolution + an allowed-roots\n policy binding (`builtin_tools.set_allowed_roots`, which resets the memoized\n cache so the override actually enforces).\n- **Multimodal (image) messages.** `Message.content` now accepts an OpenAI-style\n content-part list (`str | list[dict]`), passed through verbatim to the\n OpenAI-compatible body (gateway / vLLM / OpenAI). New `adk.llm.multimodal`\n helpers — `image_message()`, `image_content_parts()`, `to_image_url()` — build\n the parts and encode local image bytes/paths into `data:` URIs.\n- **Real vision preflight probe.** The vision slot is no longer unconditionally\n `UNSUPPORTED`: it sends a discriminable image (a solid red square) and requires\n the model to name the color, so a blind/text-only model cannot false-pass. `OK`\n only on a real round-trip; `AUTH` on 401/403; `UNSUPPORTED` when there is no\n OpenAI-compatible provider or the model cannot see the image.\n\n## [2.14.5] - 2026-07-04\n\n### Added\n\n- **`adk.reasoning.mcts` — generic Monte-Carlo Tree Search library.** A reusable\n MCTS engine (ported from the internal `UnifiedMCTS`, with all internal couplings\n removed) that any agent can drive over its own environment:\n - `MCTSEnvironment` protocol (`get_state_hash` / `get_actions` / `step` /\n `evaluate` / `clone`) — implement it and search.\n - Three **optional, default-off** model seams — `TransitionModel`, `PolicyModel`\n (PUCT priors), `ValueModel` — so a learned world-model / policy / value can be\n injected without touching the core; with all three `None` the engine is a plain\n UCT search.\n - `ObservedTransitionModel` adapter — a cheap online lookup world-model (hit =\n exact stored transition; miss = identity + negative \"unknown\" reward +\n `is_uncertain`, biasing exploration toward learning), with JSONL save/load for\n cross-run reuse.\n - `UnifiedMCTS(config).search(env)` returns an `MCTSResult` (best action, value,\n confidence, principal-variation path) and an optional `MCTSTrace`\n (visit-distribution policy target) for distillation.\n- **Self-bootstrapping agent machinery.** Build an agent from a declarative spec\n instead of bespoke wiring:\n - `adk.bootstrap.build_agent_from_spec(path) -> (AitherAgent, RunCtx)` — resolves\n prompts, activates required/optional tool packs, and carries a `memory_map`\n (a `node_type -> (role, tier)` override for memory writes, never a gate).\n - `adk.prompts.PromptBridge` — resolves an agent's prompt bindings from files\n (relative to the spec) or `AitherPrompts` dot-keys (optional import).\n - `adk.packs.PackActivator` — eager activation of installed tool packs by name\n over `register_tool_packs`; a required pack that registers zero tools raises\n `PackUnavailable`, optional packs soft-degrade.\n - `python -m adk.bootstrap.generic_agent <spec.yaml>` inspects a spec (resolved\n name, activated packs + tool counts, prompt keys, memory map) without running a\n live loop.\n- **`deep_research` tool pack** — a full multi-engine web-research workflow as\n `dr_*` agent tools (keyless search, page fetch + cache, site mirror, a citable\n findings knowledge graph, and markdown/PDF/DOCX report generation), activatable\n by name via the tool-pack loader.\n\n## [2.14.4] - 2026-07-04\n\n### Added\n\n- **`structured_ml` tool domain** — zero-shot structured-data inference for agents,\n backed by TabFM (tabular classification/regression) and TimesFM (time-series\n forecasting). Three tools:\n - `tabular_classify(support_rows, target, query_rows)` — classify rows from a\n labeled support set, in-context (no training step; up to 10 classes).\n - `tabular_regress(support_rows, target, query_rows)` — predict a numeric target.\n - `timeseries_forecast(series, horizon)` — forecast future values of a series.\n\n The tools POST to a structured-ML inference service resolved from\n `AITHER_STRUCTURED_ML_URL` (default `http://localhost:8192`), reject non-http(s)\n URLs, and summarise oversized responses to protect the agent's context.\n- **`Capability.STRUCTURED_INFERENCE`** capability token and a new **`analyst`**\n identity/pack that ships the `structured_ml` domain by default. The domain is\n opt-in — only identities that enable the `structured_ml` category get the tools.\n\n## [2.14.3] - 2026-07-03\n\n### Fixed\n\nAll four bugs below were found by actually booting `adk run` against a real\nchat model and driving a real tool-calling request end-to-end (not inferred\nfrom reading the code) — each one silently broke the \"local agent uses its\nconfigured model and its tools actually work\" path.\n\n- **`AITHER_MODEL` / `config.model` silently ignored by the OpenAI-compatible\n backend**: `LLMRouter.__init__` never read `config.model` into `self._model`\n unless the caller passed `model=` directly to the constructor — which nothing\n did. Every explicit model choice (env var, config file) was dropped, the\n provider fell back to a hardcoded per-backend default (`gpt-4o-mini` for the\n `openai` backend), and an unrecognized model name got silently remapped\n server-side to the wrong backing model. Concretely: setting\n `AITHER_MODEL=aither-orchestrator` had no effect — every request actually\n went to a different model that was never meant to be called directly and\n can't reliably use tools.\n- **`OPENAI_BASE_URL` ignored for explicit `--backend`/`AITHER_LLM_BACKEND`\n selection**: `LLMRouter._auto_detect()`'s explicit-backend branch hardcoded\n `base_url=None` when constructing the provider, instead of reading\n `config.openai_base_url` (which two other call sites in the same file\n already did correctly). Any self-hosted OpenAI-compatible endpoint\n (MicroScheduler, vLLM, LM Studio) configured via `OPENAI_BASE_URL` was\n silently ignored in favor of `https://api.openai.com/v1`.\n- **Tool-call steering never engaged on default-effort chat**: the retry that\n nudges a model back into structured tool-calling when it responds with prose\n instead of a function call was gated behind `effort >= 6`. Default `effort`\n for a plain `agent.chat(message)` call resolves to `5`, so the safety net —\n built specifically for this failure mode — never fired for default requests.\n Also now forces `tool_choice=\"required\"` on the retry instead of relying on a\n text nudge alone.\n- **Every non-string builtin-tool parameter silently typed as `\"string\"` in\n the generated tool schema**: `builtin_tools.py` uses\n `from __future__ import annotations`, so `_extract_parameters()` in\n `tools.py` was reading postponed (string) type hints (`\"int\"`, not `int`)\n straight out of `fn.__annotations__`, which never matched\n `_type_to_schema`'s type-object keys and silently fell through to\n `{\"type\": \"string\"}`. Every builtin tool with an `int`/`float`/`bool`/`list`\n parameter was affected — e.g. `file_read(start_line: int, end_line: int)`\n advertised both as strings, so a model correctly calling the tool with\n `\"start_line\": \"0\"` crashed the actual implementation with\n `unsupported operand type(s) for -: 'str' and 'int'`. Fixed by resolving\n hints through `typing.get_type_hints()` instead of the raw annotations dict.\n `_coerce_arguments()` — the runtime safety net that's supposed to coerce a\n model-supplied `\"0\"` string into `0` before a tool call executes — had the\n exact same raw-`__annotations__` bug and silently no-opped for every builtin\n tool; fixed the same way.\n- **Streaming tool-loop could hang a connection forever**: `chat_stream()`'s\n fallback to the sync tool loop (tool calls can't stream token-by-token) had\n no timeout, so a model that didn't cleanly terminate a tool-calling round\n trip left the SSE connection open indefinitely with zero bytes sent. Bounded\n with a 60s `asyncio.wait_for`; on timeout the stream yields a clear message\n instead of hanging.\n\n## [2.14.2] - 2026-07-03\n\n### Fixed\n- **AitherShell session amnesia**: the REPL generated a fresh `session_id` for\n EVERY message, so conversation history never followed across turns (the\n server stored each turn under a different session). One stable id is now\n minted per shell run; `/resume` and `/new` semantics unchanged, and the\n cross-restart auto-restore of the last session now actually works.\n\n## [2.14.1] - 2026-07-02\n\n### Added\n- **Account-tied licensing via device-flow login** — `adk login` now persists the account\n license returned by the device-flow handshake to `~/.aither/license.json` (base64\n `license_key` → `{payload, signature}` envelope), so entitlements from the authenticated\n account carry into every later `adk` invocation without re-auth. Pairs with the fleet-side\n node tool-pack gate that grants packs by plan-tier rank.\n- **`can_use_formbridge` / `can_use_untether` entitlements** (PROFESSIONAL+ rank ≥ 3) backing\n the FormBridge and UNTETHER agent tool-packs.\n\n### Fixed\n- `adk` CLI: guard the optional `memory_graph` singleton (was a latent `NameError` on the\n `/stats`, `/memory`, and save paths when memory was not constructed).\n\n## [2.14.0] - 2026-07-02\n\n### Added\n- **`adk.memory_wiki` — LyraWiki-style self-managed semantic knowledge** (opt-in; nothing constructs it by default): `MemoryWiki(graph_memory)` maintains curated wiki ARTICLES as `wiki_article` nodes (title/slug, LLM-owned markdown with `[[wikilinks]]`, embedding, durable tier, `CONSOLIDATED_FROM` edges to source memories, governance-ledgered revisions).\n - `consolidate(llm, since=, budget=)` clusters unconsolidated episodic/fact nodes (embedding + keyword), drafts/UPDATEs articles via the INJECTED llm callable (`str -> str` or `messages -> str`, sync or async — no hard model dependency), cites source node ids, marks contradicted sources superseded, then DEMOTES consolidated sources to a fast-decay tier via `promote()` (represented knowledge doesn't need raw copies).\n - `lint(llm=None)` — deterministic health checks (orphan wikilinks, empty/stale articles, live-but-contradicted pairs) + an optional LLM audit pass.\n - `prune(relevance_floor=0.05, hard_delete_after_days=14)` — article relevance = tier freshness × reinforcement (reinforce-on-recall) + link-degree to LIVE memories; below the floor → reversible governance tombstone; entombed past the window → **HARD-DELETE** (the tombstone snapshot itself is purged — content and embedding irrecoverable; the true-deletion path). A node is never deleted without a tombstone first.\n - `recall(query, limit)` — article-first RAG (curated articles rank above raw nodes) that reinforces returned articles.\n- **`adk.routines` — agent heartbeat + self-programmed routines**: `RoutineStore` is a durable registry (`~/.aither/routines/{agent}.json`, `AITHER_DATA_DIR`-aware, injectable path) of cron-scheduled self-prompts `{name, cron, instruction, enabled, last_run, last_result, tags}` that rehydrates into the existing `adk.cron.CronScheduler` on `start()`. A fire runs `agent.chat(instruction)` with the agent's full toolset — the agent programs its future self with its own adk. Guardrails: `max_routines` (12), a 5-minute min fire interval per routine, a per-fire timeout, and every result ledgered (truncated) to `last_result`. Direct-callable routines (`register_direct`) fire a bound method instead of a self-prompt. Self-management tools (`routine_create/list/update/pause/resume/delete/run_now`) ship as OpenAI tool defs + handlers (`build_routine_tools` / `register_routine_tools` / `routine_tool_defs`); the handlers ONLY touch the RoutineStore — they can never modify agent config or safety-relevant settings (the leash principle).\n- **`AitherAgent(routines=True, memory_maintenance=True)`** (both default **False** → the default agent is byte-identical): `routines=True` builds the RoutineStore + scheduler, registers the self-management tools, and starts the heartbeat lazily on the first `chat()` (or via explicit `start_routines()`). `memory_maintenance=True` registers DEFAULT routines — `wiki_consolidate` (every 2 h), `wiki_lint` / `wiki_prune` / `graph_sweep` (daily) — as DIRECT method fires so memory upkeep runs even on tiny models, while staying visible/manageable via `routine_list`.\n- `TombstoneStore.purge(tombstone_id)` — the hard-delete endpoint: irrecoverably removes a tombstone snapshot and rewrites the persisted store without it (consumed by `memory_wiki.prune`).\n\n## [2.13.6] - 2026-07-02\n\n### Added\n- **Qdrant backend for GraphMemory dataplane sync — TRUE two-way, verified live.** Set `AITHER_FLEET_QDRANT_URL` and the sync uses Qdrant (`upsert` + `scroll` + filtered `search`) instead of Nexus. This is what actually delivers cross-agent memory sharing: a node is upserted with its OWN embedding under a deterministic per-(tenant,node) point id (idempotent re-push), and `fleet_pull` reliably SCROLLS a tenant's points back — the enumerate Nexus lacks (its `/search` doesn't return ingested docs and `/export` needs lancedb, which has a hard dependency conflict in the fleet). **Proven live with 2 agents:** agent-1 ingests → auto-syncs to the tenant dataplane; a fresh agent-2 in the same tenant `fleet_pull`s all of agent-1's nodes and can search them; a different tenant pulls nothing (isolation). Dimension-safe (never mixes vector dims in a collection). Local SQLite stays source of truth; best-effort, never raises.\n\n## [2.13.5] - 2026-07-02\n\n### Fixed\n- **CRITICAL — GraphMemory dataplane sync push was 100% failing.** `_fleet_push_node` sent `source_type=\"graph\"` / `content_type=\"graph_node\"`, which AitherNexus rejects as invalid enums (HTTP 500) — so NO graph node ever replicated in 2.13.4. Now sends the accepted `source_type=\"manual\"` / `content_type=\"text\"` (graph provenance is carried in `metadata.synced_from`). **Verified live** against a fleet Nexus: a 4-node ingest replicates with `pending==0` and the collection count reflects it.\n\n### Added\n- **Swarm-awareness notification** — on a successful sync batch, `GraphMemory` emits a best-effort `graph.synced` event (tenant/workspace/agent/collection/count) to `AITHER_FLEET_EVENTS_URL` so OTHER agents in the tenant/swarm can pull the fresh data and deconflict in-flight work (push-based awareness instead of polling). No-op unless the env is set.\n\n### Known limitation\n- Cross-agent **rehydration** (`fleet_pull`) is only reliable when the fleet Nexus is backed by lancedb (persistent + exportable). An in-memory Nexus stores on `/ingest` but neither its `/search` nor `/export` returns the docs — two-way read needs the persistent Nexus.\n\n## [2.13.4] - 2026-07-02\n\n### Added\n- **Memory-maintenance primitives on `GraphMemory`** (all opt-in; default behaviour byte-identical):\n - **Reinforce-on-recall**: `recall_with_activation(..., reinforce=True)` bumps `reinforcement_count`/`last_reinforced` on every RETURNED node's metadata mirror (one UPDATE per node, WAL-safe) — making the `MemoryRecord` reinforcement fields the activation scorer reads LIVE. Default `False` = zero writes.\n - **`sweep(now, archive_below=0.05, max_nodes=None, dry_run=False)`**: read-time decay enforced at rest — archives nodes past their tier TTL with freshness×reinforcement score below `archive_below` and `reinforcement_count <= 1`; `max_nodes` additionally archives the lowest-scored overflow. PERMANENT tier immune. Every archive is a reversible governance tombstone (`TombstoneStore.recover`) + a FORGET ledger entry; a node is never deleted without a tombstone. `dry_run` returns the would-archive list without acting.\n - **`promote(node_id, tier=?, role=?)`**: re-tier/re-role a node IN PLACE (tier/role columns + metadata mirror) — edges, embedding and ledger history survive; ledgers an UPDATE entry when governance is on.\n- `MutationType.UPDATE` ledger entry kind (in-place re-tier/re-role).\n- **Per-tenant dataplane sync for `GraphMemory`** — replicate the local graph to a per-tenant vector dataplane (Nexus/Qdrant) so a sovereign agent's memory is durable + rehydratable across restarts and hosting boundaries. `fleet_push_all_nodes()` (catch-up), `fleet_pull()` (best-effort semantic top-up), `fleet_sync_pending()`, and auto-sync on ingest (`_maybe_autosync` + `drain_sync()`). **On by default** (`AITHER_FLEET_SYNC=auto`; only `false/0/off` disables) with an inferred target (local Nexus `:8122` or the gateway in cloud mode) and a SEPARATE collection (`AITHER_FLEET_GRAPH_COLLECTION`, default `graph_memory`) from the KV memory sync. New `GraphMemory(tenant_id=, workspace_id=, fleet_url=, auto_sync=)` constructor args override env (required for multi-tenant host processes). Node `synced` column + schema v4 migration. TLS verify is CA-bundle-aware (`AITHER_CA_BUNDLE`) for the internal-TLS mesh. Local SQLite stays the source of truth; every sync path is best-effort and never raises into ingest.\n\n## [2.13.3] - 2026-07-02\n\n### Added\n- **Canonical self-deploying embeddings provider** (`adk.embeddings`): one embedding provider for the whole SDK so vectors are portable across scopes (platform / tenant / sovereign) — same model + dimension everywhere. Pinned to `nomic-embed-text` (768-d; the vLLM `--served-model-name` for nomic-embed-text-v1.5). Lazy, single-flight resolution chain: explicit `AITHER_EMBEDDINGS_URL` → local vLLM (`:8209` then `:8120`, HTTPS-then-HTTP) → local Ollama → gateway (`AITHER_GATEWAY_EMBEDDINGS_URL`) → auto-deploy a local vLLM embeddings container if a GPU + Docker are present (opt out with `AITHER_EMBED_AUTODEPLOY=0`) → CPU sentence-transformers (384-d, degraded) → feature-hash (384-d, degraded, always works). Every batch is dimension-tagged so callers never silently mix 768-d and 384-d vectors. `get_default_embedder()`, `embed_texts()`, `embed_one()`, `get_provider()`, `reset_provider()`.\n\n### Changed\n- **`GraphMemory` now defaults its embedder to the canonical provider** (opt out with `AITHER_GRAPH_EMBEDDER=legacy`), so every adk agent shares one 768-d space. Added a meta-table dimension guard: the index is pinned to its first embedding's dimension and later different-dimension vectors are refused (kills 768↔384 mixing that silently poisons cosine similarity).\n\n## [2.13.2] - 2026-07-01\n\n### Added\n- **Session persistence**: AitherShell now auto-saves the active session_id to `~/.aither/config.yaml` after each chat turn and auto-restores it on the next shell launch. Multi-turn conversations survive across restarts — the messages were always persisted in SQLite + JSON, only the session pointer was missing.\n- `/new` command to start a fresh session (clears saved session_id).\n- `/sessions` command to list recent sessions with agent name, message count, and timestamp. Resume any with `/resume <id>`.\n\n## [2.13.1] - 2026-07-01\n\n### Fixed\n- **CRITICAL:** `deploy_connect()` now sends a HEAD request to verify the GitHub release asset exists before downloading, with a clear error if the asset is missing.\n- **CRITICAL:** Compose file URLs are now pinned to a stable commit hash (configurable via `AITHER_COMPOSE_PIN` env) instead of tracking the mutable `main` branch.\n- **CRITICAL:** `deploy_adk_node()` now falls back to starting `adk-serve` natively when Docker is unavailable, instead of blocking with \"Docker not installed\".\n- **HIGH:** GHCR login failure now blocks deployment immediately with actionable guidance, instead of warning and continuing to a confusing image-pull failure.\n- **HIGH:** Health checks now stream the last 30 lines of Docker Compose logs on failure, so the user can see why a container didn't start.\n- **HIGH:** vLLM port allocation now probes for conflicts before assigning; if port 8200 is busy, workers are automatically remapped to the next free port.\n- **MEDIUM:** Compose file downloads are now cached with ETag; re-running `adk deploy node` skips re-downloading an unchanged file.\n- **MEDIUM:** DGX Spark discovery now respects `AITHER_DGX_HOSTS` env (comma-separated) for custom network topologies, instead of only checking `spark.local` and `192.168.0.33`.\n- **MEDIUM:** Cloud API keys (Anthropic, OpenAI, DeepSeek) are now validated with a lightweight probe during `adk setup` infra scan, so invalid keys surface early.\n- **MEDIUM:** `deploy_connect()` now validates the extracted extension has a `manifest.json` and warns if it's missing.\n\n## [2.13.0] - 2026-07-01\n\n### Removed\n- **`adk.faculties.memory_graph`** — the pickle-based `MemoryGraph` module is deleted. `from adk.faculties import MemoryGraph` now resolves to the canonical `adk.graph_memory.GraphMemory` for back-compat.\n- **`adk.platform.memory`** — the entire legacy memory subsystem (`MemoryManager`, `UnifiedMemorySystem`, `GameEngine`, `StoryboardEngine`, `AnchorGenerator`) is deleted. Agent memory is `adk.memory.Memory` + `adk.graph_memory.GraphMemory`.\n\n### Changed\n- `adk work` command now uses `GraphMemory` instead of the removed pickle-based faculty graph.\n- `adk.MemoryGraph` and `adk.faculties.MemoryGraph` lazy exports now resolve to `adk.graph_memory.GraphMemory`.\n- `agent.set_memory_graph()` kept for back-compat but documented as deprecated; agents already wire `GraphMemory` automatically in `__init__`.\n- Platform startup (`adk.platform.infrastructure.startup`) gracefully handles missing `MemoryManager`.\n- Platform CLI module listing and health check updated to reflect removed memory module.\n\n## [2.12.7] - 2026-07-01\n\n### Deprecated\n- `adk.faculties.MemoryGraph` — emits `DeprecationWarning` on instantiation; use `adk.graph_memory.GraphMemory` (SQLite-backed, governed, embedding-native) instead.\n- `adk.platform.memory` package — the legacy `MemoryManager`, `UnifiedMemorySystem`, `GameEngine`, and `StoryboardEngine` modules now emit `DeprecationWarning` on import; use `adk.memory.Memory` and `adk.graph_memory.GraphMemory` for new code.\n\n### Changed\n- Updated `adk.faculties` docstring and usage examples to point at the canonical memory surface.\n\n## [2.12.6] - 2026-07-01\n\n### Changed\n- Trimmed public package debt by removing grid runbook artifacts and shell/room binary placeholder directories from published artifacts.\n- Removed stale public-sync allowlist entries for deleted phase test files and added explicit public-sync guards for binary placeholder directories and grid artifacts.\n- Updated the public README grid note so it no longer links to runbook files excluded from the SDK package.\n\n## [2.12.5] - 2026-07-01\n\n### Added\n- Added best-effort fleet memory sync from local ADK memory into Nexus/Qdrant-compatible RAG stores, including batch catch-up and vector-search recall hooks.\n- Added saved config/env support for fleet memory URL, collection, and sync mode.\n\n### Changed\n- Excluded FormBridge runtime/test internals from public ADK packaging and public repo sync.\n\n## [2.12.4] - 2026-07-01\n\n### Added\n- Added a hot-reload endpoint for installed agent packs so newly applied pack tools can be picked up without restarting `aither-serve`.\n\n### Fixed\n- Fixed synchronous `ChatRelay` calls on Python 3.12 when no event loop is running; async WebSocket delivery is still scheduled when a loop exists.\n- Aligned gateway inference tier tests with the current public ladder: free, starter, pro, enterprise, platform.\n\n## [2.12.0] - 2026-06-28\n\n### Added — one-command local inference + upgrade path (click-to-run)\n- **`adk quickstart-local`** — detects hardware, picks a backend\n (Ollama / llama.cpp / vLLM), installs it, downloads a model, verifies, and\n prints next steps. Bootstraps Ollama itself via winget/brew/install.sh when\n missing (no \"go install it\" dead-ends). Default model `gemma4:e2b`.\n- **`adk backend switch <ollama|llamacpp|vllm>`** (+ `backend status`) — migrate\n the active deployment between engines; re-points config and smoke-tests.\n- **`adk install pack:<name>`** (+ `adk install list` / `adk packs`) — install\n ready-made agent packs (openclaw, hermes, claude-code) into `~/.aither/agents`.\n\n### Fixed (found via live end-to-end testing on Windows + CUDA)\n- quickstart-local reported success on a dead endpoint (smoke test was warn-only);\n a failed smoke now fails the command, with a service-readiness wait.\n- `--model` resolved to `None` (getattr default never fired) → uses the real default.\n- Smoke test: 30s→180s timeout for cold model load; accepts reasoning-model output\n (empty `content` at low token budgets is no longer a false failure).\n- `adk install pack:<name>` colon syntax (argparse rejected it before dispatch).\n- `adk backend status` health probe used a nonexistent `/health` path → 404; now\n probes `/v1/models` (uniform OpenAI-compatible liveness for all three backends).\n- Windows: removed non-ASCII glyphs from printed strings that crashed the cp1252\n console (a `✓` even made a successful install report failure).\n\n## [2.11.1] - 2026-06-14\n\n### Fixed — `adk host` / `adk login` device flow (autonomous onboarding)\nFound driving `adk host` against production; all broke real device-flow login:\n- **Device flow hit the portal FRONTEND, not AitherIdentity.** `adk login`/`adk host`\n POSTed `/auth/device/code` to portal/veil.aitherium.com, which 307-redirects API auth\n to `/login`. Added `_resolve_identity_url()` mapping the aitherium.com topology to\n `idp.aitherium.com` (the Identity API); localhost/bespoke pass through unchanged.\n- **Cloudflare 403'd urllib.** `idp.*` sits behind Cloudflare, which blocks urllib's\n default `Python-urllib/3.x` UA. Device-code + poll requests now send a real\n User-Agent + Accept (verified urllib→idp 200, was 403).\n- **`adk host` now self-authenticates via device flow** when there's no token, and\n **retries via device flow on a 401/403** (a stale saved token no longer dead-ends\n registration).\n\n## [2.11.0] - 2026-06-14\n\n### Added — attach your own MCP \"hands\" to a self-hosted agent\nA self-hosted agent now has the full brain (your model) · body (this loop) · **hands**\n(your tools) trifecta. The AitherOS platform relays the MCP servers you registered for\nyour agent in the `/stream` request body as `mcp_endpoints`, and the loop wires their\ntools into the ReAct turn:\n- **`adk/mcp_endpoint_tools.register_mcp_endpoint_tools(agent, endpoints)`** — speaks MCP\n JSON-RPC (`tools/list` to discover, `tools/call` to invoke) and registers a namespaced\n proxy tool (`{endpoint}__{tool}`) for each advertised tool. Mirrors `app_proxy_tools`.\n Best-effort: an unreachable endpoint registers nothing and never raises.\n- **`POST /stream` and `POST /chat/stream`** accept an optional `mcp_endpoints` list\n (`[{name, url, [headers]}]`); `_aitheros_stream` attaches them to the agent before the\n turn. Existing callers are unaffected (the field is optional).\n\n## [2.10.1] - 2026-06-14\n\n### Fixed — self-hosted `aither-serve` was crash-on-startup (2.10.0 regression)\nA fresh `pip install aither-adk && aither-serve --backend deepseek` could not boot. Found\nand fixed while standing up a real self-hosted DeepSeek agent end-to-end:\n- **Startup `NameError: name 'port' is not defined`** in three lifespan helpers\n (`_join_aithernet`, `_init_a2a_server`, `_start_mesh_hosting`) — they referenced a bare\n `port` no longer in scope. Now use `config.server_port`, and `main()` writes the resolved\n `--port`/`--host` back to `config` so every lifespan helper and the invoke_url see the\n actual bound port.\n- **Fleet self-registration crashed the whole server** — the registration handlers caught\n `(ImportError, RuntimeError, OSError, ConnectionError)` but **not** `httpx.HTTPError`, so a\n `RemoteProtocolError`/connect error talking to the control plane took the agent down. All\n network-facing lifespan handlers now also catch `httpx.HTTPError` (non-fatal, as intended).\n- **`--backend` was ignored when `AITHER_API_KEY` was set** — `_auto_detect` picked the cloud\n gateway before the explicit backend, and `_try_elysium_fallback` then replaced the chosen\n provider. An explicit backend (deepseek/openai/anthropic/your own vllm/ollama) now always\n wins; \"auto\"/\"gateway\" still flow through detection. The operator's own brain is never\n silently hijacked.\n- **Approved-tool trace truncated after `/sessions/{id}/confirm`** — the SSE relay treated\n `AgentResponse.tool_calls_made` (a `list[str]`) as dicts (`tc.get(...)`), raised\n `AttributeError`, and an over-narrow `except` swallowed it so the client saw only\n `session_start` + heartbeats. The relay now handles string tool names, the `except` is\n broad (always emits a typed `error` + terminal `complete`), and the module-level\n `_strata_ingest` `NameError` + Chronicle `session_id` kwarg mismatch are fixed.\n\n## [2.10.0] - 2026-06-13\n\n### Added — human-in-the-loop tool approval (`adk/approval.py`)\n- **Pause before a gated tool, resume on decision.** An agent can now pause its turn before\n executing a sensitive tool and wait for a human's allow/deny — so a managed control plane\n (or any operator) can gate tool use without forking the loop.\n- **Policy via `AITHER_TOOL_APPROVAL`** — a comma-separated list of tool names (optionally\n `agent:tool`), or `*` for every tool. Empty = no gating (unchanged behavior).\n- **Survives restarts / days-later approvals.** The paused turn (user message + pending tool\n calls) is persisted to disk keyed by `session_id`; resume is a fresh request, not a held\n connection. Decisions are recorded per `(session_id, tool_name)`.\n- **`AgentResponse.requires_action` / `.pending`** — set when a turn paused; `pending` lists\n the gated tool calls awaiting a decision.\n- **`AitherAgent.resume(session_id, decisions)`** — records the decisions and re-runs the\n paused turn (the gate consumes them: allowed tools execute, denied tools get a denial\n observation, the turn continues — and may pause again on a different gated tool).\n- **Server:** `POST /sessions/{id}/confirm` records decisions and streams the resumed turn;\n `/stream` emits a `requires_action` event when a turn pauses.\n\n## [2.9.0] - 2026-06-13\n\n### Added — universal LLM continuation primitive (`adk/llm/continuation.py`)\n- **`run_continuation()` + `stitch()`** — one shared continue-until-complete primitive. When a\n completion stops because it hit the output token cap (`finish_reason == \"length\"`), it continues\n the generation and STITCHES the chunks (overlap-dedup + restart-detect) into a complete answer,\n instead of every call-site hand-rolling its own retry. Bounded three ways (max rounds AND total\n output ceiling AND a no-growth break), and it never mutates the caller's message history.\n- **`LLMProvider.chat_with_continuation()`** and **`LLMRouter.chat_with_continuation()`** — every\n provider AND the router inherit the primitive, so any call-site gets continuation for free.\n- Kill-switch `ADK_LLM_CONTINUATION=off`; tunables `ADK_LLM_MAX_CONTINUATIONS`,\n `ADK_LLM_MAX_TOTAL_OUTPUT_TOKENS` (defaults: 2 rounds / 8192 tokens).\n\n### Changed\n- The ReAct loop's truncation recovery now uses the shared primitive. The previous inline\n doubling-retry REPLACED the partial response (losing earlier text in `resp.content`) and injected\n continuation scaffolding into the live message history; it now STITCHES and leaves history clean.\n Tool-call turns are never continued as text.\n\n### Included — previously-committed-but-unreleased changes\n- 2.8.2: TLS verification on by default (`verify=False` removed).\n- 2.8.1: baked license + pack-signing public keys.\n- 2.8.0: version-bump release.\n\n## [2.7.0] - 2026-06-05\n\n### Added — provider-agnostic LLM cost levers (at the `LLMProvider` ABC)\n- **Prompt caching.** New `ProviderCapabilities` (`prompt_cache: explicit|automatic|none`, `batch`) and\n normalized `LLMResponse.cache_read_tokens`/`cache_write_tokens`, so the \"tokens saved\" meter never\n depends on which backend served the turn. Anthropic inserts `cache_control` breakpoints over the stable\n system+tools prefix; OpenAI/DeepSeek caching is automatic and their `cached_tokens` are now surfaced\n (previously discarded). Default-on in `AitherAgent.chat`; override with `cache=False`.\n- **ResponseCache** (`adk/llm/cache.py`) — exact-match response cache with a pluggable backend, wired into\n `LLMRouter` as an opt-in (`LLMRouter(response_cache=…)` + per-call `cacheable=True`). OFF by default — no\n determinism change unless opted in.\n- **Gemini provider** (`adk/llm/gemini.py`) with explicit `cachedContents` caching; registered in the router.\n- **Async batch runner** (`adk/llm/batch_runner.py`) — `run_batch()` over the Anthropic Message Batches /\n OpenAI Batch APIs (~50% off) with a transparent fallback to concurrent `chat()` where unsupported.\n- **Graph-RAG retriever interface** (`adk/graph_retriever.py`).\n\n### Added — principal authority (prompt-injection defense enforced outside the model)\n- `adk/auth.py`: `Principal` / `AuthContext` / `PrincipalResolver` (+ deny-all default).\n- `ToolRegistry.execute(name, args, auth=…)` — a default-deny authorization gate at the tool boundary, so a\n forbidden tool call is blocked regardless of what the LLM emits. `ToolDef` gains `required_clearance` /\n `action_class`. Threaded through `AitherAgent.chat`; `auth=None` is fully backward-compatible.\n- `adk/channel_auth.py`: Telegram webhook-secret + CEO-id checks and HMAC-signed approvals.\n\n## [2.6.4] - 2026-06-05\n\n### Fixes\n- **Loop-guard nudges corrupted the message history → DeepSeek 400 mid-run.**\n When the ReAct loop emitted multiple tool calls in one assistant turn and the\n loop guard fired a WARN/BLOCK/CIRCUIT_BREAK on one of them, it spliced a\n standalone `system` nudge message *between* the tool results. DeepSeek (and\n strict vLLM chat templates) require an assistant `tool_calls` message to be\n followed by exactly one `tool` result per `tool_call_id`, contiguously — so the\n interleaved message produced `400 … insufficient tool messages following\n tool_calls message` and aborted the turn (this is reliably hit after crossing\n the 15-call circuit-break threshold, where every subsequent call WARNs).\n Per-call guidance is now folded into that call's own `tool` result, and any\n standalone steering is deferred to a single message AFTER the whole batch —\n preserving the `tool_calls`↔`tool_results` pairing every backend requires.\n Regression test: `deep-research-agent/tests/test_msg_structure.py`\n (loopguard-flood scenario).\n\n## [2.6.3] - 2026-06-04\n\n### Fixes\n- **Transient provider errors killed a run on the first blip.** A single\n `502 Bad Gateway` / `503` / `529 overloaded` / `429` from the upstream API\n aborted the whole request — the Anthropic provider had **no retry at all**, and\n the OpenAI-compatible provider only retried `429`. Both now retry transient\n statuses (`408/429/500/502/503/504/529`) with `Retry-After`-aware exponential\n backoff (3 attempts), and surface the provider's body if all retries are\n exhausted. Non-transient `4xx` (e.g. `400`) still fails fast. Applies to\n `chat` and `chat_stream` on both providers.\n\n## [2.6.2] - 2026-06-04\n\n### Fixes\n- **DeepSeek (and strict OpenAI-compatible backends) 400'd on long tool\n conversations.** The ReAct loop legitimately injects mid-conversation\n `system` steering messages (the \"[DIMINISHING RETURNS]\" hint after several\n low-output tool iterations, loop-guard nudges). OpenAI tolerates a non-leading\n `system` message; DeepSeek rejects it with a 400. Long research runs\n (many `fetch_url` calls) reliably tripped this. Fix: non-leading `system`\n messages are demoted to `user` at the OpenAI-compatible provider boundary\n (`_demote_nonleading_system`) — steering intent preserved, payload valid on\n every backend; leading system prompt(s) untouched.\n- **Opaque HTTP errors.** `resp.raise_for_status()` discarded the provider's\n response body, so a 400/422 surfaced as a bare `Client error '400 Bad\n Request'` with no cause. `_ensure_ok` now includes the provider's actual\n message (e.g. `context_length_exceeded`, `Invalid 'messages'`) in the raised\n error, for both `chat` and `chat_stream`.\n\n## [2.6.1] - 2026-06-04\n\n### Fixes\n- **`MCPServer.mount()` returned HTTP 422 for every `/mcp` call.** Under\n `from __future__ import annotations` the route handler's `request: Request`\n annotation is a string that FastAPI resolves against the *module* globals, but\n `Request` was imported locally inside `mount()` — so FastAPI couldn't resolve\n it and treated `request` as a required query param. The MCP server endpoint\n (`initialize`, `tools/list`, `tools/call`) was unusable when mounted. FastAPI\n symbols are now imported at module level (guarded; the `[node]` extra still\n gates actual use). Any agent's tools can now be exposed as a working MCP\n server with `MCPServer(...).mount(app)`.\n\n## [2.6.0] - 2026-06-04\n\n### Streaming + scaffolding\n- **`AitherAgent.stream_chat`** — reliable streaming sibling of `chat()`: native\n tool-calling loop (concludes on its own, unlike the text-protocol `stream_react`)\n that emits live `tool`/`tool_result` events and streams the answer as `token`\n events. `token_delay` paces tokens for a typing effect.\n- **`adk new <template>`** — scaffold a full, runnable template app. Ships the\n `deep-research` template (a cited web-research agent: pack + server + web UI):\n `adk new deep-research`.\n\n### Fixes\n- **BYO-key no longer community-capped.** The free-tier monthly token cap now applies\n only to the metered Aitherium gateway backend, never to a user's own provider key\n (Anthropic/OpenAI/DeepSeek/Ollama/vLLM). Previously it could brick a self-hosted\n agent after ~100k tokens on the user's own key.\n- **Stronger, grounded memory recall.** `chat()` now injects the top-6 knowledge-graph\n hits (untruncated) with a hard no-fabrication instruction — was top-3 truncated to\n 200 chars, which surfaced partial facts and let weaker models invent the rest.\n- **Tool-arg coercion.** `ToolRegistry.execute` coerces LLM-supplied args toward each\n parameter's annotated type, so a tool no longer crashes when a model passes a list\n or string where an int/float/bool was expected.\n\n### Docs\n- New `docs/AGENT_DEV_GUIDE.md` — the golden path for building agents/packs.\n\n## [2.5.0] - 2026-06-04\n\n### Continual learning — agents that learn from runs and reuse memory\n\nAgents now improve the second time they do a task instead of starting from scratch.\n\n### Added\n- **Recall-before**: `AitherAgent.chat()` searches the local `SkillStore` and injects the\n top matching learned skills as a system block, so the model reuses proven procedures.\n- **Learn-after**: `_learn_after()` runs after each response — reinforces (`success_count++`)\n any skills that were recalled+reused, and extracts + saves a NEW skill from successful\n multi-tool runs via `SkillExtractor`. Wired at both `chat()` return paths.\n- Gated by `AITHER_SKILLS` (default on). The previously-unused `SkillStore`/`SkillExtractor`\n are now connected to the run loop.\n\n### Notes\n- Pairs with the platform-side LearnedProcedure substrate (recipes → scored procedures →\n skills/tools/A2A/packs → MCTS priors → evolution). The SDK shares memory via the Spirit\n bridge when configured.\n\n## [2.0.0] - 2026-06-02\n\n### Open-core boundary (BREAKING)\n\nThis release draws a single, enforced free/paid line. The free tier stays\ngenuinely useful (real agent, typed + graph + code memory, ReAct, ~essential\ntools); paid tiers unlock the *scale* capabilities via portal.aitherium.com.\n\n### Added\n- **`adk/licensing.py`** — the entitlement keystone. Resolves a tier\n (COMMUNITY by default), verifies optional portal-signed `~/.aither/license.json`\n (Ed25519), and answers `can_use_fleet/channels/cron/swarm/auto_neurons`,\n `is_agent_licensed`, `max_effort`, etc. **Fail-closed**: no/invalid/expired\n license → free tier; an unsigned license never grants premium.\n `AITHER_TENANT_SLUG=aitherium` → unrestricted INTERNAL tier.\n- **Moat guard** (`scripts/check_moat_boundary.py`) wired into the publish\n workflow — blocks any wheel that leaks the moat or drops the keystone.\n- Tests: `tests/test_licensing.py`, `tests/test_moat_boundary.py`.\n\n### Changed (gating — fail-closed)\n- Free tier effort is capped at 3 (reasoning effort 7–10 needs Professional).\n- Monthly token budget hard-block on the free tier (no silent overspend).\n- Gated behind a paid tier: fleet mode (`FleetConfig`), cross-agent delegation\n (`AgentForge.delegate`), channel adapters, cron scheduler, proactive\n auto-neurons, and `swarm`/`swarm_code` dispatch.\n\n### Removed\n- **`adk/nanogpt.py`** (on-device training IP) removed from the published SDK\n and the public repo; relocated to the internal AitherOS moat. Excluded from\n the wheel as defense-in-depth.\n- Pre-2.0 releases shipped this IP and ungated capabilities and are being\n yanked (PyPI) / removed (GitHub releases). See `scripts/yank_leaky_releases.py`\n and `scripts/purge_public_leaks.sh`.\n\n## [1.20.0] - 2026-05-27\n\n### Fixed\n- **Pack discovery path mismatch** — `_find_local_packs()` now scans both\n `~/.aither/packs/` and `~/.aitheros/packs/`, so packs installed via\n `adk pack install` are discovered by `adk-workspace`.\n\n### Added\n- **Auto-register on pack install** — `adk pack install` now auto-registers\n with the portal if the pack contains an `agent.yaml` with a `portal` section.\n One command does download + extract + register.\n- **Flux event on agent registration** — `developer_portal.py` emits\n `agent.registered` Flux event so the portal fleet UI updates in real-time.\n\n## [1.15.0] - 2026-05-26\n\n### Added\n- **Cloud memory** — `adk quickstart --cloud` now tests gateway memory and\n auto-configures Spirit URL so memories persist across devices via\n `gateway.aitherium.com/v1/memory/teach` and `/v1/memory/recall`.\n- **Config→env export for cloud mode** — `Config.from_env()` now exports\n `AITHER_CLOUD_MODE`, `AITHER_SPIRIT_URL`, and spirit path env vars from\n saved config so Memory and other modules pick them up without needing\n explicit env vars set by the user.\n- **Cloud Quick Start section** in README — 3-command setup for users who\n just want a Claude/GPT-4/DeepSeek API key and the full agent harness.\n- **`--memory` profile for node deployment** — `adk deploy node --memory`\n adds Spirit (8087) + WorkingMemory (8101) containers for persistent\n vector memory. Without this, agents use local SQLite only.\n- **Configurable vLLM in node compose** — `AITHER_VLLM_MODEL`,\n `AITHER_VLLM_GPU_UTIL`, `AITHER_VLLM_CTX_LEN`, `AITHER_VLLM_QUANT_ARGS`\n env vars let `adk setup` write GPU-detected tier settings to `.env`\n and have the node compose pick them up (TQ4 for small GPUs, BNB for large).\n\n### Fixed\n- **Memory cloud routing** — `Memory._spirit_teach()` and `_spirit_recall()`\n now use configurable paths and auth headers for gateway proxy. Previously\n cloud-only users silently lost all Spirit memory calls.\n\n## [1.9.0] - 2026-05-24\n\n### Added\n- **Composable ToolPack system** — agents can load tool packs from\n `.toolpack.yaml` directories. Drop a pack in `~/.aitheros/packs/` or\n set `AITHER_PACKS_DIR`, and any agent discovers it automatically.\n- **`tools.packs` in agent.yaml** — list tool pack IDs to auto-load\n (e.g. `packs: [crm-tools, calendar-pro]`).\n- **`register_tool_packs(agent, pack_ids, packs_dir)`** in `builtin_tools` —\n programmatic pack loading for agents.\n- **`AITHER_TOOL_PACKS` env var** — comma-separated pack IDs loaded on\n agent init (alternative to YAML config).\n- **Unknown builtin categories resolve as pack IDs** — `tools.builtin:\n [file_io, crm-tools]` now works: `crm-tools` is looked up as a pack.\n\n## [1.8.0] - 2026-05-24\n\n### Removed\n- **11 dead modules** (3,023 lines) — `agent_deploy`, `cloud_deploy`, `create`,\n `phonehome`, `sdk_bridge`, `sovereign`, `telemetry`, `telemetry_config`,\n `error_reporter`, `anonymizer`, `package`. None were imported anywhere.\n- **29 ghost exports** from `__init__.py` — `secrets`, `otel`, `fs_sandbox`,\n and `vector_memory` modules were listed in `__all__` and `__getattr__` but\n the backing files never existed. Accessing them raised `ModuleNotFoundError`.\n- **Stale dist/ artifacts** — removed old wheels/tarballs (v0.4.0–v1.4.1, 11 MB).\n- **Orphan test scripts** — `test_phase1.py` and `test_phase23.py` (one-off\n validation scripts not part of the pytest suite).\n\n### Fixed\n- **Version mismatch** — `__init__.__version__` now matches `pyproject.toml`\n (was stuck at 1.6.0).\n\n## [1.5.0] - 2026-05-23\n\n### Added\n- **`--sovereign` flag for `adk deploy node`** — registers the node with the\n Aitherium hub via federation after deployment. Saves federation credentials\n to `~/.aither/.env.federation`. Accepts `--hub` (custom hub URL) and\n `--tenant` (tenant slug) options. Non-fatal: node runs standalone if\n registration fails.\n- **Federation → fleet cross-post** (Genesis) — sovereign nodes that register\n or heartbeat via `/federation/register` and `/federation/heartbeat` now\n automatically appear in the fleet dashboard (`/fleet/deployments`) with\n `node_type: \"sovereign\"` and live status/metrics.\n- **\"Deploy Sovereign Node\" section on Connect page** (AitherVeil) — Section 8\n with ADK deploy commands, fleet dashboard links, and provision page links.\n- **`deploy-node` action in tunnel proxy** (AitherVeil) — Connect page and\n external callers can now request bootstrap scripts via\n `POST /api/tunnel/devworkspace?action=deploy-node`.\n\n### Fixed\n- **Fleet provision page** — API URL corrected from `/api/fleet/provision` to\n `/api/bridge/genesis/fleet/provision` (matches bridge proxy pattern).\n- **Fleet detail page** — API URL corrected from `/api/fleet/deployments/` to\n `/api/bridge/genesis/fleet/deployments/` (same fix).\n- **Dockerfile** — entry point corrected from `aither-serve` to `adk-serve`\n (matches `pyproject.toml` script registration). Version label updated.\n\n## [1.4.1] - 2026-05-22\n\n### Fixed\n- **macOS executable builds** — dropped `--target-arch universal2` from\n `packaging/build_executable.py`. PyPI wheels for `pydantic_core` and other\n native deps are arch-specific, not fat binaries, so universal2 always failed\n with `IncompatibleBinaryArchError`. Now builds for the runner's native arch\n (`aither-macos-arm64`).\n- **Release workflow strategy** — added `fail-fast: false` so a single\n executable build failure no longer cancels the other OS jobs.\n- **PyPI publish** — added `skip-existing: true` so re-runs of the same\n version don't fail when the artifact already exists.\n- **GitHub Release job** — now runs whenever `pypi` succeeds even if some\n executable matrix entries fail, so the release page is always created with\n whatever binaries did build successfully.\n\n## [1.4.0] - 2026-05-22\n\n### Added\n- **Sovereign template now ships with `agent_core` 5-tier memory** — generated\n backends include the canonical `agent_core/` package (working → episodic →\n semantic → procedural → identity) plus `agent_memory.py` helper, vendored\n from `portal-kit-backend`.\n- `aiosqlite>=0.20.0` added to sovereign template `requirements.txt` for\n local agent_core SQLite storage.\n- Document tombstoning: `evict_cached_conversations_referencing` is wired\n through document-delete handlers in consumer apps so memories citing\n deleted documents are blocked from recall.\n- New tests: 13 smoke tests for `portal-kit-backend/agent_core` covering\n promotion thresholds, decay rates, store roundtrip, tombstone recall\n blocking, circuit breaker state machine, and chat-turn recording.\n\n### Changed\n- `scripts/vendor_agent_core.py` now syncs canonical `agent_core` to 4\n targets: WorkspaceRuntime, ADK sovereign template, and the GargBot +\n Chelle consumer mirrors (previously only 2).\n\n## [1.2.1] - 2026-05-21\n\n### Added\n- `adk train` command group — 6 subcommands: status, launch, logs, cancel, runs, register-gpu\n- `/slash-commands` endpoint — ADK server auto-exposes all 35 CLI commands as structured JSON manifest for AitherShell\n- `/cli/execute` endpoint — AitherShell can run any CLI command through the server\n- `build_command_manifest()` — introspects full argparse tree (commands, args, choices, subcommands)\n- `_register_commands()` extracted from main() for shared parser construction\n\n### Changed\n- `_get_genesis_url()` now probes: Genesis → ADK server → Aitherium cloud gateway (works without Genesis)\n\n## [1.2.0] - 2026-05-21\n\n### Added\n- **Runtime backend switching** — `agent.switch_backend(\"deepseek\")` and `agent.llm.switch_backend()` change provider without recreating agent\n- **Hybrid reasoning** — `agent.set_reasoning_backend(\"anthropic\")` routes effort 7+ to cloud API while keeping local orchestrator\n- **First-class DeepSeek provider** — `deepseek` in effort model table with `deepseek-chat` and `deepseek-reasoner`\n- **TQ4 quantization tiers** — `nano` (6GB, TurboQuant 4-bit), `standard-tq4` (12GB, both models TQ4), `hybrid-tq4` (6GB + cloud reasoning)\n- **`adk backend` CLI** — `list`, `set`, `set-reasoning`, `test` subcommands\n- **`adk quickstart`** — unified first-run wizard (setup + auth + shell)\n- **`adk tools`** — list local + MCP tools with tier markers\n- **`adk backup`** — tarball export of `~/.aither/`\n- **`adk ingest`** — feed docs/files into knowledge graph\n- **`adk setup nemotron`** shortcut — alias for `--tier lite`\n- **`--reasoning-api`** flag in setup — `anthropic`, `openai`, `deepseek`, `gateway`\n- **`--dgx-spark URL`** flag — remote vLLM endpoint configuration\n- **DGX Spark auto-detection** — scans `spark.local`, `192.168.0.33`, `AITHER_DGX_URL` env\n- **Post-setup smoke test** — sends real inference request after container startup\n- **Schema migration system** — version table + migration runner in `memory.py` and `graph_memory.py`\n- **Configurable vLLM port scan** — `AITHER_VLLM_PORTS` env var for custom ports\n- **`aithershell` entry point** — alias for `adk` in pyproject.toml\n- Shell auto-downloads binary on first `adk shell` (no `--install` needed)\n- Shell pre-flight check warns if no backend detected\n- Shell passes full config via env vars (API key, tenant, inference URL)\n- DGX/Remote and Cloud API checks added to `adk doctor`\n- Config fields: `reasoning_backend`, `reasoning_api_key`, `deepseek_api_key`, `dgx_url`, `vllm_extra_ports`\n\n### Fixed\n- `test_aeon_chat_basic` timeout — mock scope was exiting before request\n- `adk doctor` check_disk timeout — cap rglob at 5000 files\n\n## [1.1.7] - 2026-05-19\n\n### Added\n- Remote agent pipeline — `adk onboard` → fleet dispatch → remote inference\n- Agent-to-agent federation with mesh relay\n- `adk connect` for desktop mesh enrollment\n\n## [1.1.6] - 2026-05-19\n\n### Fixed\n- Tool call execution recovery across all backends\n- Hermes tool call fallback parsing for vLLM\n\n## [1.1.5] - 2026-05-19\n\n### Fixed\n- Tool call execution + Hermes fallback parsing improvements\n\n## [1.1.0] - 2026-05-18\n\n### Added\n- Multi-channel gateway (Telegram, Discord, Slack, webhook)\n- Aeon group chat with 7 presets\n- Skills auto-extraction from multi-step sessions\n- MCP stdio server for Claude Code integration\n- `adk gateway`, `adk cron`, `adk skills`, `adk soul` commands\n- SOUL.md import/export for portable agent identity\n- Elysium cloud device-flow authentication\n\n## [1.0.0] - 2026-05-18\n\n### Breaking\n- Package consolidated — `aither_adk` namespace replaced by `adk` + `adk.platform`\n- 93 duplicate files removed from `AitherOS/packages/aither_adk/`\n\n### Added\n- Standalone core primitives (`adk.core`: Agent, Tool, Memory, ModelBackend, Capability, Trace)\n- `adk.platform` sub-package merging all platform toolkit modules\n- Compatibility shims for legacy `from aither_adk.*` imports\n\n### Changed\n- Version synced across pyproject.toml, `adk/__init__.py`, `adk/platform/__init__.py`, npm package\n- Classifier promoted from Alpha to Beta\n\n## [0.16.0] - 2026-04-16\n\n### Swarm Coding Engine & Repowise Integration\n- **`repowise_search` tool** — Semantic + keyword hybrid code search via Repowise, with ripgrep fallback\n- **`swarm_code` tool** — Dispatch complex tasks to 11-agent swarm pipeline (ARCHITECT->SWARM->REVIEW->JUDGE)\n- **`agent.swarm()`** — Async convenience method with configurable mode, effort, timeout\n- **`agent.code_search()`** — Async convenience method returning structured results\n- New tool categories: `repowise` and `swarm` in TOOL_CATEGORIES\n- `repowise_search` and `swarm_code` added to `__init__.py` exports with lazy loading\n- `IDENTITY_DEFAULTS` updated: repowise for code-focused agents, swarm for orchestration agents\n- Standalone graceful degradation: repowise falls back to ripgrep, swarm returns structured error\n\n## [0.13.0] - 2026-04-02\n\n### Graph Faculties — Local Knowledge for Every Agent\n- **CodeGraph** (2,799 lines) — Full Python AST indexer with call graph, keyword/semantic/hybrid query, embedding matrix cache, incremental re-indexing, multi-hop chain expansion\n- **MemoryGraph** (1,339 lines) — Graph-based persistent agent memory with 10 edge types, hybrid query (keyword + semantic + graph expansion), multi-hop recall, pickle persistence\n- **EmbeddingProvider** — 4-backend fallback chain: sentence-transformers (GPU/CPU) -> Ollama -> Elysium cloud -> feature hashing (zero deps)\n- **BaseFacultyGraph** — Abstract base with HMAC-SHA256 validated pickle persistence\n\n### Agent Integration\n- `agent.set_code_graph(cg)` — auto-registers `code_search` + `code_context` tools\n- `agent.set_memory_graph(mg)` — auto-registers `remember` + `recall` + `memory_stats` tools\n- Both graphs inject context into LLM prompts automatically during chat\n\n### Zero-Config Onboarding\n- `adk start` / `adk` (no args) — auto-detect project, index code, detect LLM, persistent memory, interactive chat\n- Works for any directory: Python codebases, doc folders, mixed workspaces\n- Auto-detects LLM: Ollama -> vLLM -> Elysium -> OpenAI -> Anthropic\n- Per-project persistent memory in `~/.aither/memory/<project>`\n- `adk index <path>` — standalone indexing with progress bar and stats\n\n### MCP Gateway\n- `POST /v1/embeddings` — OpenAI-compatible embedding proxy to vLLM-embeddings:8209\n- Available to all tiers (embeddings are free)\n- ADK EmbeddingProvider uses this as Elysium cloud fallback\n\n### Optional Dependencies\n- `pip install aither-adk[graphs]` — numpy for 10x cosine similarity speedup\n- `pip install aither-adk[embedding]` — sentence-transformers + torch for local GPU embeddings\n- `pip install aither-adk` alone — graphs work with feature hashing (zero deps)\n\n## [0.12.0] - 2026-04-01\n\n### Bootstrap & Service Discovery\n- Version handshake -- ADK checks major.minor compatibility with Genesis/Node\n- Background reconnect loop -- ServiceBridge re-probes every 30s in standalone mode\n- Port 8080/8090 documented -- MCP vs OpenAI-compat clearly separated\n- Genesis URL configurable via GENESIS_URL env var (was hardcoded)\n- Standalone mode warning -- visible stderr alert when AitherOS not detected\n- Auto-reconnect on startup when services come online\n\n### Elysium Cloud Inference\n- Unified gateway URL -- gateway.aitherium.com handles auth + billing + inference\n- Streaming inference -- SSE passthrough for /v1/chat/completions with stream=true\n- Auth proxy routes -- /v1/auth/register, /v1/auth/login, /v1/auth/me\n- Billing proxy -- /v1/billing/balance through gateway\n- AitherConnect Elysium fallback -- cloud inference when local Genesis is down\n- AitherDesktop Elysium fallback -- third-tier chat fallback after Node\n\n### Infrastructure\n- /discovery endpoint on Genesis -- unified service URLs/versions/health\n- /api/config/services on Veil -- runtime port config for client-side JS\n- Veil healthcheck gates on Genesis -- unhealthy when backend is down\n- Desktop crash detection 90s->60s (threshold 3->2)\n\n## [0.11.0] - 2026-04-01\n\n### Agent Execution Quality (Claude Code Parity)\n- Raise loop guard block threshold from 3 to 4 -- agents get more room for iterative search\n- Soft synthesis nudge for effort >= 4 (no tool stripping, trust the model)\n- max_output_tokens escalation -- retry up to 3x when response is truncated\n- Tool result pairing guarantee -- synthetic error for orphaned tool_use blocks\n- Micro-compaction of old tool results -- save context tokens on long sessions\n- First-turn tool forcing only for effort >= 6 (trust model for lower effort)\n- Diminishing returns detection -- nudge agent when 3+ turns produce < 500 tokens\n- Message normalization -- merge consecutive same-role messages, strip empties\n- LLM retry with exponential backoff (5 retries, 500ms-16s, jitter)\n\n## [0.9.0] - 2026-03-16\n\nThe \"connected world\" release. Cross-platform identity pairing, voice capabilities, and multi-channel integration.\n\n### Added\n- **Pairing**: Cross-platform identity linking (`adk/pairing.py`)\n - `PairingManager` — SQLite-backed identity linking with 6-char pairing codes\n - Link users across Telegram, Discord, Slack, WhatsApp with 10min TTL codes\n - Canonical session IDs for cross-channel conversation continuity\n - `get_session_id()` returns \"user-{id}\" for paired users\n- **Voice**: Speech-to-text and text-to-speech client (`adk/voice.py`)\n - `VoiceClient` — async STT/TTS/emotion via AitherVoice service\n - Convenience functions: `hear()`, `say()`, `feel()`\n - 6 voice options: alloy, echo, fable, nova, onyx, shimmer\n - Emotion detection with intensity scoring\n- New exports: `PairingManager`, `PairingResult`, `PlatformIdentity`, `VoiceClient`, `TranscriptionResult`, `SynthesisResult`, `EmotionResult`\n\n### Changed\n- `__init__.py` exports expanded with pairing and voice symbols\n\n## [0.6.0] - 2026-03-13\n\nThe \"group mind\" release. Multi-agent group chat, creative tools, and Iris identity.\n\n### Added\n- **Aeon**: Multi-agent group chat engine (`adk/aeon.py`)\n - `AeonSession` — persistent group chat with parallel agent execution\n - 7 presets: balanced, creative, technical, security, minimal, duo_code, research\n - Orchestrator synthesis: Aither summarizes all agent responses\n - Serial execution for Ollama, parallel for vLLM/cloud\n - ConversationStore persistence with `type: \"aeon\"` metadata\n - `group_chat()` one-shot convenience function\n- `aither aeon` CLI command — interactive terminal group chat with color-coded agents\n - `-p/--preset`, `-a/--agents`, `-r/--rounds`, `--no-synthesize` flags\n - `reset` and `quit` commands\n- Server endpoints: `POST /aeon/chat`, `GET /aeon/presets`, `GET /aeon/sessions/{id}`\n- Creative tools in builtin_tools: `image_generate`, `image_refine`, `image_search`, `video_generate`\n- Iris agent identity with visual generation capabilities\n- 48 Aeon tests (data models, presets, chat, context, persistence, server, exports)\n\n### Changed\n- `__init__.py` exports: `AeonSession`, `AeonResponse`, `AeonMessage`, `group_chat`, `AEON_PRESETS`\n\n## [0.5.0] - 2026-03-13\n\nThe \"tenant-ready\" release. Multi-tenant admin, permission grants, safety profiles, and a full setup wizard.\n\n### Added\n- `aither setup` interactive setup wizard with hardware detection, model selection, identity config\n- Strata storage backend: SQLite WAL persistence for conversations, memories, knowledge graphs\n- CLI test runner: `aither test` with auto-discovery and parallel execution\n- Permission grants system for MCP tool access control\n- MCP account management tools\n- LLM provider auto-detection for Ollama and vLLM endpoints\n- Apache-2.0 LICENSE file\n- Elysium desktop sync module\n- Comprehensive CLI test suite (493 tests)\n- Strata storage test suite (972 tests)\n- LLM provider tests (82 tests)\n\n### Changed\n- CLI expanded: `aither setup`, `aither test`, `aither bugreport`, `aither doctor`\n- Strata module rewritten as full local-first storage engine\n- Server startup includes Strata initialization\n- README rewritten with clearer quickstart and architecture docs\n\n### Fixed\n- Dead documentation links\n- Python 3.11 compatibility issues\n- Ruff per-file-ignores configuration\n- Identity provisioning edge cases\n- Docker node-gyp native dependency builds (python3 + build tools)\n\n## [0.4.0] - 2026-03-13\n\nThe \"own your AI\" release. Self-hosted agent OS for people who don't want their data on someone else's servers.\n\n### Added\n- Muse agent identity (creative/artistic generation)\n- Port 8120 to vLLM scan for ExoNodes discovery\n- Public roadmap with milestone-based porting schedule\n- Competitive positioning: self-hosted alternative to cloud-locked AI appliances\n\n### Changed\n- LLM router: compute-aware effort routing (replaces effort-level context gating)\n- README rewritten for sovereignty-first messaging\n- Package description updated\n- Promoted from alpha to stable release\n\n### Fixed\n- Version string consistency between pyproject.toml and __init__.py\n- vLLM port scanning now includes port 8120 (ExoNodes)\n\n## [0.3.1] - 2026-03-09\n\n### Added\n- GraphMemory: SQLite knowledge graph with Ollama embeddings and hybrid search\n- NeuronPool: auto-fire pre-LLM data gathering agents\n- NanoGPT: pure Python char-level transformer with LoRA fine-tuning\n- Safety gates: IntakeGuard (input), LoopGuard (recursion), Sandbox (code exec)\n- Event system: EventEmitter with chat/tool/forge event types\n- Builtin tools: identity-based tool selection\n- ServiceBridge: auto-discovery of AitherOS services\n- Streaming: chat_stream with safety gate integration\n- Auth middleware: Bearer token authentication\n- CLI: `aither init` and `aither serve` commands\n\n### Changed\n- Wired 5 previously disconnected modules into agent loop\n- 522 total passing tests (up from 85)\n\n## [0.3.0] - 2026-03-07\n\n### Added\n- Clean-room agent development kit\n- Multi-backend LLM providers (Ollama, OpenAI, Anthropic)\n- AitherAgent class with @tool decorator\n- SQLite conversation memory\n- OpenAI-compatible server (`aither-serve`)\n- 16 agent identities as package data\n- MCP bridge to mcp.aitherium.com\n- Privacy-centric opt-in telemetry\n- Bug reporting CLI and API\n- FastAPI server with OpenAI-compatible endpoints\n- Hardware auto-detection (5 tiers, 11 profiles)\n- Fleet orchestration and multi-agent coordination\n- A2A mesh protocol\n- Federation protocol for cross-instance agent dispatch\n\n### Initial release\n- 85 passing tests\n- Apache-2.0 license\n"}, "9733cbed74ee5cc53e6ba5fdb580637eaa9e1bc3": {"t": 1785748191.4743412, "n": 60765, "h": "f7784f28f189ff3c", "c": "# Changelog\n\nAll notable changes to aither-adk will be documented in this file.\n\n## [2.14.5] - 2026-07-04\n\n### Added\n\n- **`adk.reasoning.mcts` — generic Monte-Carlo Tree Search library.** A reusable\n MCTS engine (ported from the internal `UnifiedMCTS`, with all internal couplings\n removed) that any agent can drive over its own environment:\n - `MCTSEnvironment` protocol (`get_state_hash` / `get_actions` / `step` /\n `evaluate` / `clone`) — implement it and search.\n - Three **optional, default-off** model seams — `TransitionModel`, `PolicyModel`\n (PUCT priors), `ValueModel` — so a learned world-model / policy / value can be\n injected without touching the core; with all three `None` the engine is a plain\n UCT search.\n - `ObservedTransitionModel` adapter — a cheap online lookup world-model (hit =\n exact stored transition; miss = identity + negative \"unknown\" reward +\n `is_uncertain`, biasing exploration toward learning), with JSONL save/load for\n cross-run reuse.\n - `UnifiedMCTS(config).search(env)` returns an `MCTSResult` (best action, value,\n confidence, principal-variation path) and an optional `MCTSTrace`\n (visit-distribution policy target) for distillation.\n- **Self-bootstrapping agent machinery.** Build an agent from a declarative spec\n instead of bespoke wiring:\n - `adk.bootstrap.build_agent_from_spec(path) -> (AitherAgent, RunCtx)` — resolves\n prompts, activates required/optional tool packs, and carries a `memory_map`\n (a `node_type -> (role, tier)` override for memory writes, never a gate).\n - `adk.prompts.PromptBridge` — resolves an agent's prompt bindings from files\n (relative to the spec) or `AitherPrompts` dot-keys (optional import).\n - `adk.packs.PackActivator` — eager activation of installed tool packs by name\n over `register_tool_packs`; a required pack that registers zero tools raises\n `PackUnavailable`, optional packs soft-degrade.\n - `python -m adk.bootstrap.generic_agent <spec.yaml>` inspects a spec (resolved\n name, activated packs + tool counts, prompt keys, memory map) without running a\n live loop.\n- **`deep_research` tool pack** — a full multi-engine web-research workflow as\n `dr_*` agent tools (keyless search, page fetch + cache, site mirror, a citable\n findings knowledge graph, and markdown/PDF/DOCX report generation), activatable\n by name via the tool-pack loader.\n\n## [2.14.4] - 2026-07-04\n\n### Added\n\n- **`structured_ml` tool domain** — zero-shot structured-data inference for agents,\n backed by TabFM (tabular classification/regression) and TimesFM (time-series\n forecasting). Three tools:\n - `tabular_classify(support_rows, target, query_rows)` — classify rows from a\n labeled support set, in-context (no training step; up to 10 classes).\n - `tabular_regress(support_rows, target, query_rows)` — predict a numeric target.\n - `timeseries_forecast(series, horizon)` — forecast future values of a series.\n\n The tools POST to a structured-ML inference service resolved from\n `AITHER_STRUCTURED_ML_URL` (default `http://localhost:8192`), reject non-http(s)\n URLs, and summarise oversized responses to protect the agent's context.\n- **`Capability.STRUCTURED_INFERENCE`** capability token and a new **`analyst`**\n identity/pack that ships the `structured_ml` domain by default. The domain is\n opt-in — only identities that enable the `structured_ml` category get the tools.\n\n## [2.14.3] - 2026-07-03\n\n### Fixed\n\nAll four bugs below were found by actually booting `adk run` against a real\nchat model and driving a real tool-calling request end-to-end (not inferred\nfrom reading the code) — each one silently broke the \"local agent uses its\nconfigured model and its tools actually work\" path.\n\n- **`AITHER_MODEL` / `config.model` silently ignored by the OpenAI-compatible\n backend**: `LLMRouter.__init__` never read `config.model` into `self._model`\n unless the caller passed `model=` directly to the constructor — which nothing\n did. Every explicit model choice (env var, config file) was dropped, the\n provider fell back to a hardcoded per-backend default (`gpt-4o-mini` for the\n `openai` backend), and an unrecognized model name got silently remapped\n server-side to the wrong backing model. Concretely: setting\n `AITHER_MODEL=aither-orchestrator` had no effect — every request actually\n went to a different model that was never meant to be called directly and\n can't reliably use tools.\n- **`OPENAI_BASE_URL` ignored for explicit `--backend`/`AITHER_LLM_BACKEND`\n selection**: `LLMRouter._auto_detect()`'s explicit-backend branch hardcoded\n `base_url=None` when constructing the provider, instead of reading\n `config.openai_base_url` (which two other call sites in the same file\n already did correctly). Any self-hosted OpenAI-compatible endpoint\n (MicroScheduler, vLLM, LM Studio) configured via `OPENAI_BASE_URL` was\n silently ignored in favor of `https://api.openai.com/v1`.\n- **Tool-call steering never engaged on default-effort chat**: the retry that\n nudges a model back into structured tool-calling when it responds with prose\n instead of a function call was gated behind `effort >= 6`. Default `effort`\n for a plain `agent.chat(message)` call resolves to `5`, so the safety net —\n built specifically for this failure mode — never fired for default requests.\n Also now forces `tool_choice=\"required\"` on the retry instead of relying on a\n text nudge alone.\n- **Every non-string builtin-tool parameter silently typed as `\"string\"` in\n the generated tool schema**: `builtin_tools.py` uses\n `from __future__ import annotations`, so `_extract_parameters()` in\n `tools.py` was reading postponed (string) type hints (`\"int\"`, not `int`)\n straight out of `fn.__annotations__`, which never matched\n `_type_to_schema`'s type-object keys and silently fell through to\n `{\"type\": \"string\"}`. Every builtin tool with an `int`/`float`/`bool`/`list`\n parameter was affected — e.g. `file_read(start_line: int, end_line: int)`\n advertised both as strings, so a model correctly calling the tool with\n `\"start_line\": \"0\"` crashed the actual implementation with\n `unsupported operand type(s) for -: 'str' and 'int'`. Fixed by resolving\n hints through `typing.get_type_hints()` instead of the raw annotations dict.\n `_coerce_arguments()` — the runtime safety net that's supposed to coerce a\n model-supplied `\"0\"` string into `0` before a tool call executes — had the\n exact same raw-`__annotations__` bug and silently no-opped for every builtin\n tool; fixed the same way.\n- **Streaming tool-loop could hang a connection forever**: `chat_stream()`'s\n fallback to the sync tool loop (tool calls can't stream token-by-token) had\n no timeout, so a model that didn't cleanly terminate a tool-calling round\n trip left the SSE connection open indefinitely with zero bytes sent. Bounded\n with a 60s `asyncio.wait_for`; on timeout the stream yields a clear message\n instead of hanging.\n\n## [2.14.2] - 2026-07-03\n\n### Fixed\n- **AitherShell session amnesia**: the REPL generated a fresh `session_id` for\n EVERY message, so conversation history never followed across turns (the\n server stored each turn under a different session). One stable id is now\n minted per shell run; `/resume` and `/new` semantics unchanged, and the\n cross-restart auto-restore of the last session now actually works.\n\n## [2.14.1] - 2026-07-02\n\n### Added\n- **Account-tied licensing via device-flow login** — `adk login` now persists the account\n license returned by the device-flow handshake to `~/.aither/license.json` (base64\n `license_key` → `{payload, signature}` envelope), so entitlements from the authenticated\n account carry into every later `adk` invocation without re-auth. Pairs with the fleet-side\n node tool-pack gate that grants packs by plan-tier rank.\n- **`can_use_formbridge` / `can_use_untether` entitlements** (PROFESSIONAL+ rank ≥ 3) backing\n the FormBridge and UNTETHER agent tool-packs.\n\n### Fixed\n- `adk` CLI: guard the optional `memory_graph` singleton (was a latent `NameError` on the\n `/stats`, `/memory`, and save paths when memory was not constructed).\n\n## [2.14.0] - 2026-07-02\n\n### Added\n- **`adk.memory_wiki` — LyraWiki-style self-managed semantic knowledge** (opt-in; nothing constructs it by default): `MemoryWiki(graph_memory)` maintains curated wiki ARTICLES as `wiki_article` nodes (title/slug, LLM-owned markdown with `[[wikilinks]]`, embedding, durable tier, `CONSOLIDATED_FROM` edges to source memories, governance-ledgered revisions).\n - `consolidate(llm, since=, budget=)` clusters unconsolidated episodic/fact nodes (embedding + keyword), drafts/UPDATEs articles via the INJECTED llm callable (`str -> str` or `messages -> str`, sync or async — no hard model dependency), cites source node ids, marks contradicted sources superseded, then DEMOTES consolidated sources to a fast-decay tier via `promote()` (represented knowledge doesn't need raw copies).\n - `lint(llm=None)` — deterministic health checks (orphan wikilinks, empty/stale articles, live-but-contradicted pairs) + an optional LLM audit pass.\n - `prune(relevance_floor=0.05, hard_delete_after_days=14)` — article relevance = tier freshness × reinforcement (reinforce-on-recall) + link-degree to LIVE memories; below the floor → reversible governance tombstone; entombed past the window → **HARD-DELETE** (the tombstone snapshot itself is purged — content and embedding irrecoverable; the true-deletion path). A node is never deleted without a tombstone first.\n - `recall(query, limit)` — article-first RAG (curated articles rank above raw nodes) that reinforces returned articles.\n- **`adk.routines` — agent heartbeat + self-programmed routines**: `RoutineStore` is a durable registry (`~/.aither/routines/{agent}.json`, `AITHER_DATA_DIR`-aware, injectable path) of cron-scheduled self-prompts `{name, cron, instruction, enabled, last_run, last_result, tags}` that rehydrates into the existing `adk.cron.CronScheduler` on `start()`. A fire runs `agent.chat(instruction)` with the agent's full toolset — the agent programs its future self with its own adk. Guardrails: `max_routines` (12), a 5-minute min fire interval per routine, a per-fire timeout, and every result ledgered (truncated) to `last_result`. Direct-callable routines (`register_direct`) fire a bound method instead of a self-prompt. Self-management tools (`routine_create/list/update/pause/resume/delete/run_now`) ship as OpenAI tool defs + handlers (`build_routine_tools` / `register_routine_tools` / `routine_tool_defs`); the handlers ONLY touch the RoutineStore — they can never modify agent config or safety-relevant settings (the leash principle).\n- **`AitherAgent(routines=True, memory_maintenance=True)`** (both default **False** → the default agent is byte-identical): `routines=True` builds the RoutineStore + scheduler, registers the self-management tools, and starts the heartbeat lazily on the first `chat()` (or via explicit `start_routines()`). `memory_maintenance=True` registers DEFAULT routines — `wiki_consolidate` (every 2 h), `wiki_lint` / `wiki_prune` / `graph_sweep` (daily) — as DIRECT method fires so memory upkeep runs even on tiny models, while staying visible/manageable via `routine_list`.\n- `TombstoneStore.purge(tombstone_id)` — the hard-delete endpoint: irrecoverably removes a tombstone snapshot and rewrites the persisted store without it (consumed by `memory_wiki.prune`).\n\n## [2.13.6] - 2026-07-02\n\n### Added\n- **Qdrant backend for GraphMemory dataplane sync — TRUE two-way, verified live.** Set `AITHER_FLEET_QDRANT_URL` and the sync uses Qdrant (`upsert` + `scroll` + filtered `search`) instead of Nexus. This is what actually delivers cross-agent memory sharing: a node is upserted with its OWN embedding under a deterministic per-(tenant,node) point id (idempotent re-push), and `fleet_pull` reliably SCROLLS a tenant's points back — the enumerate Nexus lacks (its `/search` doesn't return ingested docs and `/export` needs lancedb, which has a hard dependency conflict in the fleet). **Proven live with 2 agents:** agent-1 ingests → auto-syncs to the tenant dataplane; a fresh agent-2 in the same tenant `fleet_pull`s all of agent-1's nodes and can search them; a different tenant pulls nothing (isolation). Dimension-safe (never mixes vector dims in a collection). Local SQLite stays source of truth; best-effort, never raises.\n\n## [2.13.5] - 2026-07-02\n\n### Fixed\n- **CRITICAL — GraphMemory dataplane sync push was 100% failing.** `_fleet_push_node` sent `source_type=\"graph\"` / `content_type=\"graph_node\"`, which AitherNexus rejects as invalid enums (HTTP 500) — so NO graph node ever replicated in 2.13.4. Now sends the accepted `source_type=\"manual\"` / `content_type=\"text\"` (graph provenance is carried in `metadata.synced_from`). **Verified live** against a fleet Nexus: a 4-node ingest replicates with `pending==0` and the collection count reflects it.\n\n### Added\n- **Swarm-awareness notification** — on a successful sync batch, `GraphMemory` emits a best-effort `graph.synced` event (tenant/workspace/agent/collection/count) to `AITHER_FLEET_EVENTS_URL` so OTHER agents in the tenant/swarm can pull the fresh data and deconflict in-flight work (push-based awareness instead of polling). No-op unless the env is set.\n\n### Known limitation\n- Cross-agent **rehydration** (`fleet_pull`) is only reliable when the fleet Nexus is backed by lancedb (persistent + exportable). An in-memory Nexus stores on `/ingest` but neither its `/search` nor `/export` returns the docs — two-way read needs the persistent Nexus.\n\n## [2.13.4] - 2026-07-02\n\n### Added\n- **Memory-maintenance primitives on `GraphMemory`** (all opt-in; default behaviour byte-identical):\n - **Reinforce-on-recall**: `recall_with_activation(..., reinforce=True)` bumps `reinforcement_count`/`last_reinforced` on every RETURNED node's metadata mirror (one UPDATE per node, WAL-safe) — making the `MemoryRecord` reinforcement fields the activation scorer reads LIVE. Default `False` = zero writes.\n - **`sweep(now, archive_below=0.05, max_nodes=None, dry_run=False)`**: read-time decay enforced at rest — archives nodes past their tier TTL with freshness×reinforcement score below `archive_below` and `reinforcement_count <= 1`; `max_nodes` additionally archives the lowest-scored overflow. PERMANENT tier immune. Every archive is a reversible governance tombstone (`TombstoneStore.recover`) + a FORGET ledger entry; a node is never deleted without a tombstone. `dry_run` returns the would-archive list without acting.\n - **`promote(node_id, tier=?, role=?)`**: re-tier/re-role a node IN PLACE (tier/role columns + metadata mirror) — edges, embedding and ledger history survive; ledgers an UPDATE entry when governance is on.\n- `MutationType.UPDATE` ledger entry kind (in-place re-tier/re-role).\n- **Per-tenant dataplane sync for `GraphMemory`** — replicate the local graph to a per-tenant vector dataplane (Nexus/Qdrant) so a sovereign agent's memory is durable + rehydratable across restarts and hosting boundaries. `fleet_push_all_nodes()` (catch-up), `fleet_pull()` (best-effort semantic top-up), `fleet_sync_pending()`, and auto-sync on ingest (`_maybe_autosync` + `drain_sync()`). **On by default** (`AITHER_FLEET_SYNC=auto`; only `false/0/off` disables) with an inferred target (local Nexus `:8122` or the gateway in cloud mode) and a SEPARATE collection (`AITHER_FLEET_GRAPH_COLLECTION`, default `graph_memory`) from the KV memory sync. New `GraphMemory(tenant_id=, workspace_id=, fleet_url=, auto_sync=)` constructor args override env (required for multi-tenant host processes). Node `synced` column + schema v4 migration. TLS verify is CA-bundle-aware (`AITHER_CA_BUNDLE`) for the internal-TLS mesh. Local SQLite stays the source of truth; every sync path is best-effort and never raises into ingest.\n\n## [2.13.3] - 2026-07-02\n\n### Added\n- **Canonical self-deploying embeddings provider** (`adk.embeddings`): one embedding provider for the whole SDK so vectors are portable across scopes (platform / tenant / sovereign) — same model + dimension everywhere. Pinned to `nomic-embed-text` (768-d; the vLLM `--served-model-name` for nomic-embed-text-v1.5). Lazy, single-flight resolution chain: explicit `AITHER_EMBEDDINGS_URL` → local vLLM (`:8209` then `:8120`, HTTPS-then-HTTP) → local Ollama → gateway (`AITHER_GATEWAY_EMBEDDINGS_URL`) → auto-deploy a local vLLM embeddings container if a GPU + Docker are present (opt out with `AITHER_EMBED_AUTODEPLOY=0`) → CPU sentence-transformers (384-d, degraded) → feature-hash (384-d, degraded, always works). Every batch is dimension-tagged so callers never silently mix 768-d and 384-d vectors. `get_default_embedder()`, `embed_texts()`, `embed_one()`, `get_provider()`, `reset_provider()`.\n\n### Changed\n- **`GraphMemory` now defaults its embedder to the canonical provider** (opt out with `AITHER_GRAPH_EMBEDDER=legacy`), so every adk agent shares one 768-d space. Added a meta-table dimension guard: the index is pinned to its first embedding's dimension and later different-dimension vectors are refused (kills 768↔384 mixing that silently poisons cosine similarity).\n\n## [2.13.2] - 2026-07-01\n\n### Added\n- **Session persistence**: AitherShell now auto-saves the active session_id to `~/.aither/config.yaml` after each chat turn and auto-restores it on the next shell launch. Multi-turn conversations survive across restarts — the messages were always persisted in SQLite + JSON, only the session pointer was missing.\n- `/new` command to start a fresh session (clears saved session_id).\n- `/sessions` command to list recent sessions with agent name, message count, and timestamp. Resume any with `/resume <id>`.\n\n## [2.13.1] - 2026-07-01\n\n### Fixed\n- **CRITICAL:** `deploy_connect()` now sends a HEAD request to verify the GitHub release asset exists before downloading, with a clear error if the asset is missing.\n- **CRITICAL:** Compose file URLs are now pinned to a stable commit hash (configurable via `AITHER_COMPOSE_PIN` env) instead of tracking the mutable `main` branch.\n- **CRITICAL:** `deploy_adk_node()` now falls back to starting `adk-serve` natively when Docker is unavailable, instead of blocking with \"Docker not installed\".\n- **HIGH:** GHCR login failure now blocks deployment immediately with actionable guidance, instead of warning and continuing to a confusing image-pull failure.\n- **HIGH:** Health checks now stream the last 30 lines of Docker Compose logs on failure, so the user can see why a container didn't start.\n- **HIGH:** vLLM port allocation now probes for conflicts before assigning; if port 8200 is busy, workers are automatically remapped to the next free port.\n- **MEDIUM:** Compose file downloads are now cached with ETag; re-running `adk deploy node` skips re-downloading an unchanged file.\n- **MEDIUM:** DGX Spark discovery now respects `AITHER_DGX_HOSTS` env (comma-separated) for custom network topologies, instead of only checking `spark.local` and `192.168.0.33`.\n- **MEDIUM:** Cloud API keys (Anthropic, OpenAI, DeepSeek) are now validated with a lightweight probe during `adk setup` infra scan, so invalid keys surface early.\n- **MEDIUM:** `deploy_connect()` now validates the extracted extension has a `manifest.json` and warns if it's missing.\n\n## [2.13.0] - 2026-07-01\n\n### Removed\n- **`adk.faculties.memory_graph`** — the pickle-based `MemoryGraph` module is deleted. `from adk.faculties import MemoryGraph` now resolves to the canonical `adk.graph_memory.GraphMemory` for back-compat.\n- **`adk.platform.memory`** — the entire legacy memory subsystem (`MemoryManager`, `UnifiedMemorySystem`, `GameEngine`, `StoryboardEngine`, `AnchorGenerator`) is deleted. Agent memory is `adk.memory.Memory` + `adk.graph_memory.GraphMemory`.\n\n### Changed\n- `adk work` command now uses `GraphMemory` instead of the removed pickle-based faculty graph.\n- `adk.MemoryGraph` and `adk.faculties.MemoryGraph` lazy exports now resolve to `adk.graph_memory.GraphMemory`.\n- `agent.set_memory_graph()` kept for back-compat but documented as deprecated; agents already wire `GraphMemory` automatically in `__init__`.\n- Platform startup (`adk.platform.infrastructure.startup`) gracefully handles missing `MemoryManager`.\n- Platform CLI module listing and health check updated to reflect removed memory module.\n\n## [2.12.7] - 2026-07-01\n\n### Deprecated\n- `adk.faculties.MemoryGraph` — emits `DeprecationWarning` on instantiation; use `adk.graph_memory.GraphMemory` (SQLite-backed, governed, embedding-native) instead.\n- `adk.platform.memory` package — the legacy `MemoryManager`, `UnifiedMemorySystem`, `GameEngine`, and `StoryboardEngine` modules now emit `DeprecationWarning` on import; use `adk.memory.Memory` and `adk.graph_memory.GraphMemory` for new code.\n\n### Changed\n- Updated `adk.faculties` docstring and usage examples to point at the canonical memory surface.\n\n## [2.12.6] - 2026-07-01\n\n### Changed\n- Trimmed public package debt by removing grid runbook artifacts and shell/room binary placeholder directories from published artifacts.\n- Removed stale public-sync allowlist entries for deleted phase test files and added explicit public-sync guards for binary placeholder directories and grid artifacts.\n- Updated the public README grid note so it no longer links to runbook files excluded from the SDK package.\n\n## [2.12.5] - 2026-07-01\n\n### Added\n- Added best-effort fleet memory sync from local ADK memory into Nexus/Qdrant-compatible RAG stores, including batch catch-up and vector-search recall hooks.\n- Added saved config/env support for fleet memory URL, collection, and sync mode.\n\n### Changed\n- Excluded FormBridge runtime/test internals from public ADK packaging and public repo sync.\n\n## [2.12.4] - 2026-07-01\n\n### Added\n- Added a hot-reload endpoint for installed agent packs so newly applied pack tools can be picked up without restarting `aither-serve`.\n\n### Fixed\n- Fixed synchronous `ChatRelay` calls on Python 3.12 when no event loop is running; async WebSocket delivery is still scheduled when a loop exists.\n- Aligned gateway inference tier tests with the current public ladder: free, starter, pro, enterprise, platform.\n\n## [2.12.0] - 2026-06-28\n\n### Added — one-command local inference + upgrade path (click-to-run)\n- **`adk quickstart-local`** — detects hardware, picks a backend\n (Ollama / llama.cpp / vLLM), installs it, downloads a model, verifies, and\n prints next steps. Bootstraps Ollama itself via winget/brew/install.sh when\n missing (no \"go install it\" dead-ends). Default model `gemma4:e2b`.\n- **`adk backend switch <ollama|llamacpp|vllm>`** (+ `backend status`) — migrate\n the active deployment between engines; re-points config and smoke-tests.\n- **`adk install pack:<name>`** (+ `adk install list` / `adk packs`) — install\n ready-made agent packs (openclaw, hermes, claude-code) into `~/.aither/agents`.\n\n### Fixed (found via live end-to-end testing on Windows + CUDA)\n- quickstart-local reported success on a dead endpoint (smoke test was warn-only);\n a failed smoke now fails the command, with a service-readiness wait.\n- `--model` resolved to `None` (getattr default never fired) → uses the real default.\n- Smoke test: 30s→180s timeout for cold model load; accepts reasoning-model output\n (empty `content` at low token budgets is no longer a false failure).\n- `adk install pack:<name>` colon syntax (argparse rejected it before dispatch).\n- `adk backend status` health probe used a nonexistent `/health` path → 404; now\n probes `/v1/models` (uniform OpenAI-compatible liveness for all three backends).\n- Windows: removed non-ASCII glyphs from printed strings that crashed the cp1252\n console (a `✓` even made a successful install report failure).\n\n## [2.11.1] - 2026-06-14\n\n### Fixed — `adk host` / `adk login` device flow (autonomous onboarding)\nFound driving `adk host` against production; all broke real device-flow login:\n- **Device flow hit the portal FRONTEND, not AitherIdentity.** `adk login`/`adk host`\n POSTed `/auth/device/code` to portal/veil.aitherium.com, which 307-redirects API auth\n to `/login`. Added `_resolve_identity_url()` mapping the aitherium.com topology to\n `idp.aitherium.com` (the Identity API); localhost/bespoke pass through unchanged.\n- **Cloudflare 403'd urllib.** `idp.*` sits behind Cloudflare, which blocks urllib's\n default `Python-urllib/3.x` UA. Device-code + poll requests now send a real\n User-Agent + Accept (verified urllib→idp 200, was 403).\n- **`adk host` now self-authenticates via device flow** when there's no token, and\n **retries via device flow on a 401/403** (a stale saved token no longer dead-ends\n registration).\n\n## [2.11.0] - 2026-06-14\n\n### Added — attach your own MCP \"hands\" to a self-hosted agent\nA self-hosted agent now has the full brain (your model) · body (this loop) · **hands**\n(your tools) trifecta. The AitherOS platform relays the MCP servers you registered for\nyour agent in the `/stream` request body as `mcp_endpoints`, and the loop wires their\ntools into the ReAct turn:\n- **`adk/mcp_endpoint_tools.register_mcp_endpoint_tools(agent, endpoints)`** — speaks MCP\n JSON-RPC (`tools/list` to discover, `tools/call` to invoke) and registers a namespaced\n proxy tool (`{endpoint}__{tool}`) for each advertised tool. Mirrors `app_proxy_tools`.\n Best-effort: an unreachable endpoint registers nothing and never raises.\n- **`POST /stream` and `POST /chat/stream`** accept an optional `mcp_endpoints` list\n (`[{name, url, [headers]}]`); `_aitheros_stream` attaches them to the agent before the\n turn. Existing callers are unaffected (the field is optional).\n\n## [2.10.1] - 2026-06-14\n\n### Fixed — self-hosted `aither-serve` was crash-on-startup (2.10.0 regression)\nA fresh `pip install aither-adk && aither-serve --backend deepseek` could not boot. Found\nand fixed while standing up a real self-hosted DeepSeek agent end-to-end:\n- **Startup `NameError: name 'port' is not defined`** in three lifespan helpers\n (`_join_aithernet`, `_init_a2a_server`, `_start_mesh_hosting`) — they referenced a bare\n `port` no longer in scope. Now use `config.server_port`, and `main()` writes the resolved\n `--port`/`--host` back to `config` so every lifespan helper and the invoke_url see the\n actual bound port.\n- **Fleet self-registration crashed the whole server** — the registration handlers caught\n `(ImportError, RuntimeError, OSError, ConnectionError)` but **not** `httpx.HTTPError`, so a\n `RemoteProtocolError`/connect error talking to the control plane took the agent down. All\n network-facing lifespan handlers now also catch `httpx.HTTPError` (non-fatal, as intended).\n- **`--backend` was ignored when `AITHER_API_KEY` was set** — `_auto_detect` picked the cloud\n gateway before the explicit backend, and `_try_elysium_fallback` then replaced the chosen\n provider. An explicit backend (deepseek/openai/anthropic/your own vllm/ollama) now always\n wins; \"auto\"/\"gateway\" still flow through detection. The operator's own brain is never\n silently hijacked.\n- **Approved-tool trace truncated after `/sessions/{id}/confirm`** — the SSE relay treated\n `AgentResponse.tool_calls_made` (a `list[str]`) as dicts (`tc.get(...)`), raised\n `AttributeError`, and an over-narrow `except` swallowed it so the client saw only\n `session_start` + heartbeats. The relay now handles string tool names, the `except` is\n broad (always emits a typed `error` + terminal `complete`), and the module-level\n `_strata_ingest` `NameError` + Chronicle `session_id` kwarg mismatch are fixed.\n\n## [2.10.0] - 2026-06-13\n\n### Added — human-in-the-loop tool approval (`adk/approval.py`)\n- **Pause before a gated tool, resume on decision.** An agent can now pause its turn before\n executing a sensitive tool and wait for a human's allow/deny — so a managed control plane\n (or any operator) can gate tool use without forking the loop.\n- **Policy via `AITHER_TOOL_APPROVAL`** — a comma-separated list of tool names (optionally\n `agent:tool`), or `*` for every tool. Empty = no gating (unchanged behavior).\n- **Survives restarts / days-later approvals.** The paused turn (user message + pending tool\n calls) is persisted to disk keyed by `session_id`; resume is a fresh request, not a held\n connection. Decisions are recorded per `(session_id, tool_name)`.\n- **`AgentResponse.requires_action` / `.pending`** — set when a turn paused; `pending` lists\n the gated tool calls awaiting a decision.\n- **`AitherAgent.resume(session_id, decisions)`** — records the decisions and re-runs the\n paused turn (the gate consumes them: allowed tools execute, denied tools get a denial\n observation, the turn continues — and may pause again on a different gated tool).\n- **Server:** `POST /sessions/{id}/confirm` records decisions and streams the resumed turn;\n `/stream` emits a `requires_action` event when a turn pauses.\n\n## [2.9.0] - 2026-06-13\n\n### Added — universal LLM continuation primitive (`adk/llm/continuation.py`)\n- **`run_continuation()` + `stitch()`** — one shared continue-until-complete primitive. When a\n completion stops because it hit the output token cap (`finish_reason == \"length\"`), it continues\n the generation and STITCHES the chunks (overlap-dedup + restart-detect) into a complete answer,\n instead of every call-site hand-rolling its own retry. Bounded three ways (max rounds AND total\n output ceiling AND a no-growth break), and it never mutates the caller's message history.\n- **`LLMProvider.chat_with_continuation()`** and **`LLMRouter.chat_with_continuation()`** — every\n provider AND the router inherit the primitive, so any call-site gets continuation for free.\n- Kill-switch `ADK_LLM_CONTINUATION=off`; tunables `ADK_LLM_MAX_CONTINUATIONS`,\n `ADK_LLM_MAX_TOTAL_OUTPUT_TOKENS` (defaults: 2 rounds / 8192 tokens).\n\n### Changed\n- The ReAct loop's truncation recovery now uses the shared primitive. The previous inline\n doubling-retry REPLACED the partial response (losing earlier text in `resp.content`) and injected\n continuation scaffolding into the live message history; it now STITCHES and leaves history clean.\n Tool-call turns are never continued as text.\n\n### Included — previously-committed-but-unreleased changes\n- 2.8.2: TLS verification on by default (`verify=False` removed).\n- 2.8.1: baked license + pack-signing public keys.\n- 2.8.0: version-bump release.\n\n## [2.7.0] - 2026-06-05\n\n### Added — provider-agnostic LLM cost levers (at the `LLMProvider` ABC)\n- **Prompt caching.** New `ProviderCapabilities` (`prompt_cache: explicit|automatic|none`, `batch`) and\n normalized `LLMResponse.cache_read_tokens`/`cache_write_tokens`, so the \"tokens saved\" meter never\n depends on which backend served the turn. Anthropic inserts `cache_control` breakpoints over the stable\n system+tools prefix; OpenAI/DeepSeek caching is automatic and their `cached_tokens` are now surfaced\n (previously discarded). Default-on in `AitherAgent.chat`; override with `cache=False`.\n- **ResponseCache** (`adk/llm/cache.py`) — exact-match response cache with a pluggable backend, wired into\n `LLMRouter` as an opt-in (`LLMRouter(response_cache=…)` + per-call `cacheable=True`). OFF by default — no\n determinism change unless opted in.\n- **Gemini provider** (`adk/llm/gemini.py`) with explicit `cachedContents` caching; registered in the router.\n- **Async batch runner** (`adk/llm/batch_runner.py`) — `run_batch()` over the Anthropic Message Batches /\n OpenAI Batch APIs (~50% off) with a transparent fallback to concurrent `chat()` where unsupported.\n- **Graph-RAG retriever interface** (`adk/graph_retriever.py`).\n\n### Added — principal authority (prompt-injection defense enforced outside the model)\n- `adk/auth.py`: `Principal` / `AuthContext` / `PrincipalResolver` (+ deny-all default).\n- `ToolRegistry.execute(name, args, auth=…)` — a default-deny authorization gate at the tool boundary, so a\n forbidden tool call is blocked regardless of what the LLM emits. `ToolDef` gains `required_clearance` /\n `action_class`. Threaded through `AitherAgent.chat`; `auth=None` is fully backward-compatible.\n- `adk/channel_auth.py`: Telegram webhook-secret + CEO-id checks and HMAC-signed approvals.\n\n## [2.6.4] - 2026-06-05\n\n### Fixes\n- **Loop-guard nudges corrupted the message history → DeepSeek 400 mid-run.**\n When the ReAct loop emitted multiple tool calls in one assistant turn and the\n loop guard fired a WARN/BLOCK/CIRCUIT_BREAK on one of them, it spliced a\n standalone `system` nudge message *between* the tool results. DeepSeek (and\n strict vLLM chat templates) require an assistant `tool_calls` message to be\n followed by exactly one `tool` result per `tool_call_id`, contiguously — so the\n interleaved message produced `400 … insufficient tool messages following\n tool_calls message` and aborted the turn (this is reliably hit after crossing\n the 15-call circuit-break threshold, where every subsequent call WARNs).\n Per-call guidance is now folded into that call's own `tool` result, and any\n standalone steering is deferred to a single message AFTER the whole batch —\n preserving the `tool_calls`↔`tool_results` pairing every backend requires.\n Regression test: `deep-research-agent/tests/test_msg_structure.py`\n (loopguard-flood scenario).\n\n## [2.6.3] - 2026-06-04\n\n### Fixes\n- **Transient provider errors killed a run on the first blip.** A single\n `502 Bad Gateway` / `503` / `529 overloaded` / `429` from the upstream API\n aborted the whole request — the Anthropic provider had **no retry at all**, and\n the OpenAI-compatible provider only retried `429`. Both now retry transient\n statuses (`408/429/500/502/503/504/529`) with `Retry-After`-aware exponential\n backoff (3 attempts), and surface the provider's body if all retries are\n exhausted. Non-transient `4xx` (e.g. `400`) still fails fast. Applies to\n `chat` and `chat_stream` on both providers.\n\n## [2.6.2] - 2026-06-04\n\n### Fixes\n- **DeepSeek (and strict OpenAI-compatible backends) 400'd on long tool\n conversations.** The ReAct loop legitimately injects mid-conversation\n `system` steering messages (the \"[DIMINISHING RETURNS]\" hint after several\n low-output tool iterations, loop-guard nudges). OpenAI tolerates a non-leading\n `system` message; DeepSeek rejects it with a 400. Long research runs\n (many `fetch_url` calls) reliably tripped this. Fix: non-leading `system`\n messages are demoted to `user` at the OpenAI-compatible provider boundary\n (`_demote_nonleading_system`) — steering intent preserved, payload valid on\n every backend; leading system prompt(s) untouched.\n- **Opaque HTTP errors.** `resp.raise_for_status()` discarded the provider's\n response body, so a 400/422 surfaced as a bare `Client error '400 Bad\n Request'` with no cause. `_ensure_ok` now includes the provider's actual\n message (e.g. `context_length_exceeded`, `Invalid 'messages'`) in the raised\n error, for both `chat` and `chat_stream`.\n\n## [2.6.1] - 2026-06-04\n\n### Fixes\n- **`MCPServer.mount()` returned HTTP 422 for every `/mcp` call.** Under\n `from __future__ import annotations` the route handler's `request: Request`\n annotation is a string that FastAPI resolves against the *module* globals, but\n `Request` was imported locally inside `mount()` — so FastAPI couldn't resolve\n it and treated `request` as a required query param. The MCP server endpoint\n (`initialize`, `tools/list`, `tools/call`) was unusable when mounted. FastAPI\n symbols are now imported at module level (guarded; the `[node]` extra still\n gates actual use). Any agent's tools can now be exposed as a working MCP\n server with `MCPServer(...).mount(app)`.\n\n## [2.6.0] - 2026-06-04\n\n### Streaming + scaffolding\n- **`AitherAgent.stream_chat`** — reliable streaming sibling of `chat()`: native\n tool-calling loop (concludes on its own, unlike the text-protocol `stream_react`)\n that emits live `tool`/`tool_result` events and streams the answer as `token`\n events. `token_delay` paces tokens for a typing effect.\n- **`adk new <template>`** — scaffold a full, runnable template app. Ships the\n `deep-research` template (a cited web-research agent: pack + server + web UI):\n `adk new deep-research`.\n\n### Fixes\n- **BYO-key no longer community-capped.** The free-tier monthly token cap now applies\n only to the metered Aitherium gateway backend, never to a user's own provider key\n (Anthropic/OpenAI/DeepSeek/Ollama/vLLM). Previously it could brick a self-hosted\n agent after ~100k tokens on the user's own key.\n- **Stronger, grounded memory recall.** `chat()` now injects the top-6 knowledge-graph\n hits (untruncated) with a hard no-fabrication instruction — was top-3 truncated to\n 200 chars, which surfaced partial facts and let weaker models invent the rest.\n- **Tool-arg coercion.** `ToolRegistry.execute` coerces LLM-supplied args toward each\n parameter's annotated type, so a tool no longer crashes when a model passes a list\n or string where an int/float/bool was expected.\n\n### Docs\n- New `docs/AGENT_DEV_GUIDE.md` — the golden path for building agents/packs.\n\n## [2.5.0] - 2026-06-04\n\n### Continual learning — agents that learn from runs and reuse memory\n\nAgents now improve the second time they do a task instead of starting from scratch.\n\n### Added\n- **Recall-before**: `AitherAgent.chat()` searches the local `SkillStore` and injects the\n top matching learned skills as a system block, so the model reuses proven procedures.\n- **Learn-after**: `_learn_after()` runs after each response — reinforces (`success_count++`)\n any skills that were recalled+reused, and extracts + saves a NEW skill from successful\n multi-tool runs via `SkillExtractor`. Wired at both `chat()` return paths.\n- Gated by `AITHER_SKILLS` (default on). The previously-unused `SkillStore`/`SkillExtractor`\n are now connected to the run loop.\n\n### Notes\n- Pairs with the platform-side LearnedProcedure substrate (recipes → scored procedures →\n skills/tools/A2A/packs → MCTS priors → evolution). The SDK shares memory via the Spirit\n bridge when configured.\n\n## [2.0.0] - 2026-06-02\n\n### Open-core boundary (BREAKING)\n\nThis release draws a single, enforced free/paid line. The free tier stays\ngenuinely useful (real agent, typed + graph + code memory, ReAct, ~essential\ntools); paid tiers unlock the *scale* capabilities via portal.aitherium.com.\n\n### Added\n- **`adk/licensing.py`** — the entitlement keystone. Resolves a tier\n (COMMUNITY by default), verifies optional portal-signed `~/.aither/license.json`\n (Ed25519), and answers `can_use_fleet/channels/cron/swarm/auto_neurons`,\n `is_agent_licensed`, `max_effort`, etc. **Fail-closed**: no/invalid/expired\n license → free tier; an unsigned license never grants premium.\n `AITHER_TENANT_SLUG=aitherium` → unrestricted INTERNAL tier.\n- **Moat guard** (`scripts/check_moat_boundary.py`) wired into the publish\n workflow — blocks any wheel that leaks the moat or drops the keystone.\n- Tests: `tests/test_licensing.py`, `tests/test_moat_boundary.py`.\n\n### Changed (gating — fail-closed)\n- Free tier effort is capped at 3 (reasoning effort 7–10 needs Professional).\n- Monthly token budget hard-block on the free tier (no silent overspend).\n- Gated behind a paid tier: fleet mode (`FleetConfig`), cross-agent delegation\n (`AgentForge.delegate`), channel adapters, cron scheduler, proactive\n auto-neurons, and `swarm`/`swarm_code` dispatch.\n\n### Removed\n- **`adk/nanogpt.py`** (on-device training IP) removed from the published SDK\n and the public repo; relocated to the internal AitherOS moat. Excluded from\n the wheel as defense-in-depth.\n- Pre-2.0 releases shipped this IP and ungated capabilities and are being\n yanked (PyPI) / removed (GitHub releases). See `scripts/yank_leaky_releases.py`\n and `scripts/purge_public_leaks.sh`.\n\n## [1.20.0] - 2026-05-27\n\n### Fixed\n- **Pack discovery path mismatch** — `_find_local_packs()` now scans both\n `~/.aither/packs/` and `~/.aitheros/packs/`, so packs installed via\n `adk pack install` are discovered by `adk-workspace`.\n\n### Added\n- **Auto-register on pack install** — `adk pack install` now auto-registers\n with the portal if the pack contains an `agent.yaml` with a `portal` section.\n One command does download + extract + register.\n- **Flux event on agent registration** — `developer_portal.py` emits\n `agent.registered` Flux event so the portal fleet UI updates in real-time.\n\n## [1.15.0] - 2026-05-26\n\n### Added\n- **Cloud memory** — `adk quickstart --cloud` now tests gateway memory and\n auto-configures Spirit URL so memories persist across devices via\n `gateway.aitherium.com/v1/memory/teach` and `/v1/memory/recall`.\n- **Config→env export for cloud mode** — `Config.from_env()` now exports\n `AITHER_CLOUD_MODE`, `AITHER_SPIRIT_URL`, and spirit path env vars from\n saved config so Memory and other modules pick them up without needing\n explicit env vars set by the user.\n- **Cloud Quick Start section** in README — 3-command setup for users who\n just want a Claude/GPT-4/DeepSeek API key and the full agent harness.\n- **`--memory` profile for node deployment** — `adk deploy node --memory`\n adds Spirit (8087) + WorkingMemory (8101) containers for persistent\n vector memory. Without this, agents use local SQLite only.\n- **Configurable vLLM in node compose** — `AITHER_VLLM_MODEL`,\n `AITHER_VLLM_GPU_UTIL`, `AITHER_VLLM_CTX_LEN`, `AITHER_VLLM_QUANT_ARGS`\n env vars let `adk setup` write GPU-detected tier settings to `.env`\n and have the node compose pick them up (TQ4 for small GPUs, BNB for large).\n\n### Fixed\n- **Memory cloud routing** — `Memory._spirit_teach()` and `_spirit_recall()`\n now use configurable paths and auth headers for gateway proxy. Previously\n cloud-only users silently lost all Spirit memory calls.\n\n## [1.9.0] - 2026-05-24\n\n### Added\n- **Composable ToolPack system** — agents can load tool packs from\n `.toolpack.yaml` directories. Drop a pack in `~/.aitheros/packs/` or\n set `AITHER_PACKS_DIR`, and any agent discovers it automatically.\n- **`tools.packs` in agent.yaml** — list tool pack IDs to auto-load\n (e.g. `packs: [crm-tools, calendar-pro]`).\n- **`register_tool_packs(agent, pack_ids, packs_dir)`** in `builtin_tools` —\n programmatic pack loading for agents.\n- **`AITHER_TOOL_PACKS` env var** — comma-separated pack IDs loaded on\n agent init (alternative to YAML config).\n- **Unknown builtin categories resolve as pack IDs** — `tools.builtin:\n [file_io, crm-tools]` now works: `crm-tools` is looked up as a pack.\n\n## [1.8.0] - 2026-05-24\n\n### Removed\n- **11 dead modules** (3,023 lines) — `agent_deploy`, `cloud_deploy`, `create`,\n `phonehome`, `sdk_bridge`, `sovereign`, `telemetry`, `telemetry_config`,\n `error_reporter`, `anonymizer`, `package`. None were imported anywhere.\n- **29 ghost exports** from `__init__.py` — `secrets`, `otel`, `fs_sandbox`,\n and `vector_memory` modules were listed in `__all__` and `__getattr__` but\n the backing files never existed. Accessing them raised `ModuleNotFoundError`.\n- **Stale dist/ artifacts** — removed old wheels/tarballs (v0.4.0–v1.4.1, 11 MB).\n- **Orphan test scripts** — `test_phase1.py` and `test_phase23.py` (one-off\n validation scripts not part of the pytest suite).\n\n### Fixed\n- **Version mismatch** — `__init__.__version__` now matches `pyproject.toml`\n (was stuck at 1.6.0).\n\n## [1.5.0] - 2026-05-23\n\n### Added\n- **`--sovereign` flag for `adk deploy node`** — registers the node with the\n Aitherium hub via federation after deployment. Saves federation credentials\n to `~/.aither/.env.federation`. Accepts `--hub` (custom hub URL) and\n `--tenant` (tenant slug) options. Non-fatal: node runs standalone if\n registration fails.\n- **Federation → fleet cross-post** (Genesis) — sovereign nodes that register\n or heartbeat via `/federation/register` and `/federation/heartbeat` now\n automatically appear in the fleet dashboard (`/fleet/deployments`) with\n `node_type: \"sovereign\"` and live status/metrics.\n- **\"Deploy Sovereign Node\" section on Connect page** (AitherVeil) — Section 8\n with ADK deploy commands, fleet dashboard links, and provision page links.\n- **`deploy-node` action in tunnel proxy** (AitherVeil) — Connect page and\n external callers can now request bootstrap scripts via\n `POST /api/tunnel/devworkspace?action=deploy-node`.\n\n### Fixed\n- **Fleet provision page** — API URL corrected from `/api/fleet/provision` to\n `/api/bridge/genesis/fleet/provision` (matches bridge proxy pattern).\n- **Fleet detail page** — API URL corrected from `/api/fleet/deployments/` to\n `/api/bridge/genesis/fleet/deployments/` (same fix).\n- **Dockerfile** — entry point corrected from `aither-serve` to `adk-serve`\n (matches `pyproject.toml` script registration). Version label updated.\n\n## [1.4.1] - 2026-05-22\n\n### Fixed\n- **macOS executable builds** — dropped `--target-arch universal2` from\n `packaging/build_executable.py`. PyPI wheels for `pydantic_core` and other\n native deps are arch-specific, not fat binaries, so universal2 always failed\n with `IncompatibleBinaryArchError`. Now builds for the runner's native arch\n (`aither-macos-arm64`).\n- **Release workflow strategy** — added `fail-fast: false` so a single\n executable build failure no longer cancels the other OS jobs.\n- **PyPI publish** — added `skip-existing: true` so re-runs of the same\n version don't fail when the artifact already exists.\n- **GitHub Release job** — now runs whenever `pypi` succeeds even if some\n executable matrix entries fail, so the release page is always created with\n whatever binaries did build successfully.\n\n## [1.4.0] - 2026-05-22\n\n### Added\n- **Sovereign template now ships with `agent_core` 5-tier memory** — generated\n backends include the canonical `agent_core/` package (working → episodic →\n semantic → procedural → identity) plus `agent_memory.py` helper, vendored\n from `portal-kit-backend`.\n- `aiosqlite>=0.20.0` added to sovereign template `requirements.txt` for\n local agent_core SQLite storage.\n- Document tombstoning: `evict_cached_conversations_referencing` is wired\n through document-delete handlers in consumer apps so memories citing\n deleted documents are blocked from recall.\n- New tests: 13 smoke tests for `portal-kit-backend/agent_core` covering\n promotion thresholds, decay rates, store roundtrip, tombstone recall\n blocking, circuit breaker state machine, and chat-turn recording.\n\n### Changed\n- `scripts/vendor_agent_core.py` now syncs canonical `agent_core` to 4\n targets: WorkspaceRuntime, ADK sovereign template, and the GargBot +\n Chelle consumer mirrors (previously only 2).\n\n## [1.2.1] - 2026-05-21\n\n### Added\n- `adk train` command group — 6 subcommands: status, launch, logs, cancel, runs, register-gpu\n- `/slash-commands` endpoint — ADK server auto-exposes all 35 CLI commands as structured JSON manifest for AitherShell\n- `/cli/execute` endpoint — AitherShell can run any CLI command through the server\n- `build_command_manifest()` — introspects full argparse tree (commands, args, choices, subcommands)\n- `_register_commands()` extracted from main() for shared parser construction\n\n### Changed\n- `_get_genesis_url()` now probes: Genesis → ADK server → Aitherium cloud gateway (works without Genesis)\n\n## [1.2.0] - 2026-05-21\n\n### Added\n- **Runtime backend switching** — `agent.switch_backend(\"deepseek\")` and `agent.llm.switch_backend()` change provider without recreating agent\n- **Hybrid reasoning** — `agent.set_reasoning_backend(\"anthropic\")` routes effort 7+ to cloud API while keeping local orchestrator\n- **First-class DeepSeek provider** — `deepseek` in effort model table with `deepseek-chat` and `deepseek-reasoner`\n- **TQ4 quantization tiers** — `nano` (6GB, TurboQuant 4-bit), `standard-tq4` (12GB, both models TQ4), `hybrid-tq4` (6GB + cloud reasoning)\n- **`adk backend` CLI** — `list`, `set`, `set-reasoning`, `test` subcommands\n- **`adk quickstart`** — unified first-run wizard (setup + auth + shell)\n- **`adk tools`** — list local + MCP tools with tier markers\n- **`adk backup`** — tarball export of `~/.aither/`\n- **`adk ingest`** — feed docs/files into knowledge graph\n- **`adk setup nemotron`** shortcut — alias for `--tier lite`\n- **`--reasoning-api`** flag in setup — `anthropic`, `openai`, `deepseek`, `gateway`\n- **`--dgx-spark URL`** flag — remote vLLM endpoint configuration\n- **DGX Spark auto-detection** — scans `spark.local`, `192.168.0.33`, `AITHER_DGX_URL` env\n- **Post-setup smoke test** — sends real inference request after container startup\n- **Schema migration system** — version table + migration runner in `memory.py` and `graph_memory.py`\n- **Configurable vLLM port scan** — `AITHER_VLLM_PORTS` env var for custom ports\n- **`aithershell` entry point** — alias for `adk` in pyproject.toml\n- Shell auto-downloads binary on first `adk shell` (no `--install` needed)\n- Shell pre-flight check warns if no backend detected\n- Shell passes full config via env vars (API key, tenant, inference URL)\n- DGX/Remote and Cloud API checks added to `adk doctor`\n- Config fields: `reasoning_backend`, `reasoning_api_key`, `deepseek_api_key`, `dgx_url`, `vllm_extra_ports`\n\n### Fixed\n- `test_aeon_chat_basic` timeout — mock scope was exiting before request\n- `adk doctor` check_disk timeout — cap rglob at 5000 files\n\n## [1.1.7] - 2026-05-19\n\n### Added\n- Remote agent pipeline — `adk onboard` → fleet dispatch → remote inference\n- Agent-to-agent federation with mesh relay\n- `adk connect` for desktop mesh enrollment\n\n## [1.1.6] - 2026-05-19\n\n### Fixed\n- Tool call execution recovery across all backends\n- Hermes tool call fallback parsing for vLLM\n\n## [1.1.5] - 2026-05-19\n\n### Fixed\n- Tool call execution + Hermes fallback parsing improvements\n\n## [1.1.0] - 2026-05-18\n\n### Added\n- Multi-channel gateway (Telegram, Discord, Slack, webhook)\n- Aeon group chat with 7 presets\n- Skills auto-extraction from multi-step sessions\n- MCP stdio server for Claude Code integration\n- `adk gateway`, `adk cron`, `adk skills`, `adk soul` commands\n- SOUL.md import/export for portable agent identity\n- Elysium cloud device-flow authentication\n\n## [1.0.0] - 2026-05-18\n\n### Breaking\n- Package consolidated — `aither_adk` namespace replaced by `adk` + `adk.platform`\n- 93 duplicate files removed from `AitherOS/packages/aither_adk/`\n\n### Added\n- Standalone core primitives (`adk.core`: Agent, Tool, Memory, ModelBackend, Capability, Trace)\n- `adk.platform` sub-package merging all platform toolkit modules\n- Compatibility shims for legacy `from aither_adk.*` imports\n\n### Changed\n- Version synced across pyproject.toml, `adk/__init__.py`, `adk/platform/__init__.py`, npm package\n- Classifier promoted from Alpha to Beta\n\n## [0.16.0] - 2026-04-16\n\n### Swarm Coding Engine & Repowise Integration\n- **`repowise_search` tool** — Semantic + keyword hybrid code search via Repowise, with ripgrep fallback\n- **`swarm_code` tool** — Dispatch complex tasks to 11-agent swarm pipeline (ARCHITECT->SWARM->REVIEW->JUDGE)\n- **`agent.swarm()`** — Async convenience method with configurable mode, effort, timeout\n- **`agent.code_search()`** — Async convenience method returning structured results\n- New tool categories: `repowise` and `swarm` in TOOL_CATEGORIES\n- `repowise_search` and `swarm_code` added to `__init__.py` exports with lazy loading\n- `IDENTITY_DEFAULTS` updated: repowise for code-focused agents, swarm for orchestration agents\n- Standalone graceful degradation: repowise falls back to ripgrep, swarm returns structured error\n\n## [0.13.0] - 2026-04-02\n\n### Graph Faculties — Local Knowledge for Every Agent\n- **CodeGraph** (2,799 lines) — Full Python AST indexer with call graph, keyword/semantic/hybrid query, embedding matrix cache, incremental re-indexing, multi-hop chain expansion\n- **MemoryGraph** (1,339 lines) — Graph-based persistent agent memory with 10 edge types, hybrid query (keyword + semantic + graph expansion), multi-hop recall, pickle persistence\n- **EmbeddingProvider** — 4-backend fallback chain: sentence-transformers (GPU/CPU) -> Ollama -> Elysium cloud -> feature hashing (zero deps)\n- **BaseFacultyGraph** — Abstract base with HMAC-SHA256 validated pickle persistence\n\n### Agent Integration\n- `agent.set_code_graph(cg)` — auto-registers `code_search` + `code_context` tools\n- `agent.set_memory_graph(mg)` — auto-registers `remember` + `recall` + `memory_stats` tools\n- Both graphs inject context into LLM prompts automatically during chat\n\n### Zero-Config Onboarding\n- `adk start` / `adk` (no args) — auto-detect project, index code, detect LLM, persistent memory, interactive chat\n- Works for any directory: Python codebases, doc folders, mixed workspaces\n- Auto-detects LLM: Ollama -> vLLM -> Elysium -> OpenAI -> Anthropic\n- Per-project persistent memory in `~/.aither/memory/<project>`\n- `adk index <path>` — standalone indexing with progress bar and stats\n\n### MCP Gateway\n- `POST /v1/embeddings` — OpenAI-compatible embedding proxy to vLLM-embeddings:8209\n- Available to all tiers (embeddings are free)\n- ADK EmbeddingProvider uses this as Elysium cloud fallback\n\n### Optional Dependencies\n- `pip install aither-adk[graphs]` — numpy for 10x cosine similarity speedup\n- `pip install aither-adk[embedding]` — sentence-transformers + torch for local GPU embeddings\n- `pip install aither-adk` alone — graphs work with feature hashing (zero deps)\n\n## [0.12.0] - 2026-04-01\n\n### Bootstrap & Service Discovery\n- Version handshake -- ADK checks major.minor compatibility with Genesis/Node\n- Background reconnect loop -- ServiceBridge re-probes every 30s in standalone mode\n- Port 8080/8090 documented -- MCP vs OpenAI-compat clearly separated\n- Genesis URL configurable via GENESIS_URL env var (was hardcoded)\n- Standalone mode warning -- visible stderr alert when AitherOS not detected\n- Auto-reconnect on startup when services come online\n\n### Elysium Cloud Inference\n- Unified gateway URL -- gateway.aitherium.com handles auth + billing + inference\n- Streaming inference -- SSE passthrough for /v1/chat/completions with stream=true\n- Auth proxy routes -- /v1/auth/register, /v1/auth/login, /v1/auth/me\n- Billing proxy -- /v1/billing/balance through gateway\n- AitherConnect Elysium fallback -- cloud inference when local Genesis is down\n- AitherDesktop Elysium fallback -- third-tier chat fallback after Node\n\n### Infrastructure\n- /discovery endpoint on Genesis -- unified service URLs/versions/health\n- /api/config/services on Veil -- runtime port config for client-side JS\n- Veil healthcheck gates on Genesis -- unhealthy when backend is down\n- Desktop crash detection 90s->60s (threshold 3->2)\n\n## [0.11.0] - 2026-04-01\n\n### Agent Execution Quality (Claude Code Parity)\n- Raise loop guard block threshold from 3 to 4 -- agents get more room for iterative search\n- Soft synthesis nudge for effort >= 4 (no tool stripping, trust the model)\n- max_output_tokens escalation -- retry up to 3x when response is truncated\n- Tool result pairing guarantee -- synthetic error for orphaned tool_use blocks\n- Micro-compaction of old tool results -- save context tokens on long sessions\n- First-turn tool forcing only for effort >= 6 (trust model for lower effort)\n- Diminishing returns detection -- nudge agent when 3+ turns produce < 500 tokens\n- Message normalization -- merge consecutive same-role messages, strip empties\n- LLM retry with exponential backoff (5 retries, 500ms-16s, jitter)\n\n## [0.9.0] - 2026-03-16\n\nThe \"connected world\" release. Cross-platform identity pairing, voice capabilities, and multi-channel integration.\n\n### Added\n- **Pairing**: Cross-platform identity linking (`adk/pairing.py`)\n - `PairingManager` — SQLite-backed identity linking with 6-char pairing codes\n - Link users across Telegram, Discord, Slack, WhatsApp with 10min TTL codes\n - Canonical session IDs for cross-channel conversation continuity\n - `get_session_id()` returns \"user-{id}\" for paired users\n- **Voice**: Speech-to-text and text-to-speech client (`adk/voice.py`)\n - `VoiceClient` — async STT/TTS/emotion via AitherVoice service\n - Convenience functions: `hear()`, `say()`, `feel()`\n - 6 voice options: alloy, echo, fable, nova, onyx, shimmer\n - Emotion detection with intensity scoring\n- New exports: `PairingManager`, `PairingResult`, `PlatformIdentity`, `VoiceClient`, `TranscriptionResult`, `SynthesisResult`, `EmotionResult`\n\n### Changed\n- `__init__.py` exports expanded with pairing and voice symbols\n\n## [0.6.0] - 2026-03-13\n\nThe \"group mind\" release. Multi-agent group chat, creative tools, and Iris identity.\n\n### Added\n- **Aeon**: Multi-agent group chat engine (`adk/aeon.py`)\n - `AeonSession` — persistent group chat with parallel agent execution\n - 7 presets: balanced, creative, technical, security, minimal, duo_code, research\n - Orchestrator synthesis: Aither summarizes all agent responses\n - Serial execution for Ollama, parallel for vLLM/cloud\n - ConversationStore persistence with `type: \"aeon\"` metadata\n - `group_chat()` one-shot convenience function\n- `aither aeon` CLI command — interactive terminal group chat with color-coded agents\n - `-p/--preset`, `-a/--agents`, `-r/--rounds`, `--no-synthesize` flags\n - `reset` and `quit` commands\n- Server endpoints: `POST /aeon/chat`, `GET /aeon/presets`, `GET /aeon/sessions/{id}`\n- Creative tools in builtin_tools: `image_generate`, `image_refine`, `image_search`, `video_generate`\n- Iris agent identity with visual generation capabilities\n- 48 Aeon tests (data models, presets, chat, context, persistence, server, exports)\n\n### Changed\n- `__init__.py` exports: `AeonSession`, `AeonResponse`, `AeonMessage`, `group_chat`, `AEON_PRESETS`\n\n## [0.5.0] - 2026-03-13\n\nThe \"tenant-ready\" release. Multi-tenant admin, permission grants, safety profiles, and a full setup wizard.\n\n### Added\n- `aither setup` interactive setup wizard with hardware detection, model selection, identity config\n- Strata storage backend: SQLite WAL persistence for conversations, memories, knowledge graphs\n- CLI test runner: `aither test` with auto-discovery and parallel execution\n- Permission grants system for MCP tool access control\n- MCP account management tools\n- LLM provider auto-detection for Ollama and vLLM endpoints\n- Apache-2.0 LICENSE file\n- Elysium desktop sync module\n- Comprehensive CLI test suite (493 tests)\n- Strata storage test suite (972 tests)\n- LLM provider tests (82 tests)\n\n### Changed\n- CLI expanded: `aither setup`, `aither test`, `aither bugreport`, `aither doctor`\n- Strata module rewritten as full local-first storage engine\n- Server startup includes Strata initialization\n- README rewritten with clearer quickstart and architecture docs\n\n### Fixed\n- Dead documentation links\n- Python 3.11 compatibility issues\n- Ruff per-file-ignores configuration\n- Identity provisioning edge cases\n- Docker node-gyp native dependency builds (python3 + build tools)\n\n## [0.4.0] - 2026-03-13\n\nThe \"own your AI\" release. Self-hosted agent OS for people who don't want their data on someone else's servers.\n\n### Added\n- Muse agent identity (creative/artistic generation)\n- Port 8120 to vLLM scan for ExoNodes discovery\n- Public roadmap with milestone-based porting schedule\n- Competitive positioning: self-hosted alternative to cloud-locked AI appliances\n\n### Changed\n- LLM router: compute-aware effort routing (replaces effort-level context gating)\n- README rewritten for sovereignty-first messaging\n- Package description updated\n- Promoted from alpha to stable release\n\n### Fixed\n- Version string consistency between pyproject.toml and __init__.py\n- vLLM port scanning now includes port 8120 (ExoNodes)\n\n## [0.3.1] - 2026-03-09\n\n### Added\n- GraphMemory: SQLite knowledge graph with Ollama embeddings and hybrid search\n- NeuronPool: auto-fire pre-LLM data gathering agents\n- NanoGPT: pure Python char-level transformer with LoRA fine-tuning\n- Safety gates: IntakeGuard (input), LoopGuard (recursion), Sandbox (code exec)\n- Event system: EventEmitter with chat/tool/forge event types\n- Builtin tools: identity-based tool selection\n- ServiceBridge: auto-discovery of AitherOS services\n- Streaming: chat_stream with safety gate integration\n- Auth middleware: Bearer token authentication\n- CLI: `aither init` and `aither serve` commands\n\n### Changed\n- Wired 5 previously disconnected modules into agent loop\n- 522 total passing tests (up from 85)\n\n## [0.3.0] - 2026-03-07\n\n### Added\n- Clean-room agent development kit\n- Multi-backend LLM providers (Ollama, OpenAI, Anthropic)\n- AitherAgent class with @tool decorator\n- SQLite conversation memory\n- OpenAI-compatible server (`aither-serve`)\n- 16 agent identities as package data\n- MCP bridge to mcp.aitherium.com\n- Privacy-centric opt-in telemetry\n- Bug reporting CLI and API\n- FastAPI server with OpenAI-compatible endpoints\n- Hardware auto-detection (5 tiers, 11 profiles)\n- Fleet orchestration and multi-agent coordination\n- A2A mesh protocol\n- Federation protocol for cross-instance agent dispatch\n\n### Initial release\n- 85 passing tests\n- Apache-2.0 license\n"}, "0f19529b2f3d41f2266faa4fb8ccff113ba213c2": {"t": 1785748192.825351, "n": 58457, "h": "0fd14b6ac0ca2f2b", "c": "# Changelog\n\nAll notable changes to aither-adk will be documented in this file.\n\n## [2.14.4] - 2026-07-04\n\n### Added\n\n- **`structured_ml` tool domain** — zero-shot structured-data inference for agents,\n backed by TabFM (tabular classification/regression) and TimesFM (time-series\n forecasting). Three tools:\n - `tabular_classify(support_rows, target, query_rows)` — classify rows from a\n labeled support set, in-context (no training step; up to 10 classes).\n - `tabular_regress(support_rows, target, query_rows)` — predict a numeric target.\n - `timeseries_forecast(series, horizon)` — forecast future values of a series.\n\n The tools POST to a structured-ML inference service resolved from\n `AITHER_STRUCTURED_ML_URL` (default `http://localhost:8192`), reject non-http(s)\n URLs, and summarise oversized responses to protect the agent's context.\n- **`Capability.STRUCTURED_INFERENCE`** capability token and a new **`analyst`**\n identity/pack that ships the `structured_ml` domain by default. The domain is\n opt-in — only identities that enable the `structured_ml` category get the tools.\n\n## [2.14.3] - 2026-07-03\n\n### Fixed\n\nAll four bugs below were found by actually booting `adk run` against a real\nchat model and driving a real tool-calling request end-to-end (not inferred\nfrom reading the code) — each one silently broke the \"local agent uses its\nconfigured model and its tools actually work\" path.\n\n- **`AITHER_MODEL` / `config.model` silently ignored by the OpenAI-compatible\n backend**: `LLMRouter.__init__` never read `config.model` into `self._model`\n unless the caller passed `model=` directly to the constructor — which nothing\n did. Every explicit model choice (env var, config file) was dropped, the\n provider fell back to a hardcoded per-backend default (`gpt-4o-mini` for the\n `openai` backend), and an unrecognized model name got silently remapped\n server-side to the wrong backing model. Concretely: setting\n `AITHER_MODEL=aither-orchestrator` had no effect — every request actually\n went to a different model that was never meant to be called directly and\n can't reliably use tools.\n- **`OPENAI_BASE_URL` ignored for explicit `--backend`/`AITHER_LLM_BACKEND`\n selection**: `LLMRouter._auto_detect()`'s explicit-backend branch hardcoded\n `base_url=None` when constructing the provider, instead of reading\n `config.openai_base_url` (which two other call sites in the same file\n already did correctly). Any self-hosted OpenAI-compatible endpoint\n (MicroScheduler, vLLM, LM Studio) configured via `OPENAI_BASE_URL` was\n silently ignored in favor of `https://api.openai.com/v1`.\n- **Tool-call steering never engaged on default-effort chat**: the retry that\n nudges a model back into structured tool-calling when it responds with prose\n instead of a function call was gated behind `effort >= 6`. Default `effort`\n for a plain `agent.chat(message)` call resolves to `5`, so the safety net —\n built specifically for this failure mode — never fired for default requests.\n Also now forces `tool_choice=\"required\"` on the retry instead of relying on a\n text nudge alone.\n- **Every non-string builtin-tool parameter silently typed as `\"string\"` in\n the generated tool schema**: `builtin_tools.py` uses\n `from __future__ import annotations`, so `_extract_parameters()` in\n `tools.py` was reading postponed (string) type hints (`\"int\"`, not `int`)\n straight out of `fn.__annotations__`, which never matched\n `_type_to_schema`'s type-object keys and silently fell through to\n `{\"type\": \"string\"}`. Every builtin tool with an `int`/`float`/`bool`/`list`\n parameter was affected — e.g. `file_read(start_line: int, end_line: int)`\n advertised both as strings, so a model correctly calling the tool with\n `\"start_line\": \"0\"` crashed the actual implementation with\n `unsupported operand type(s) for -: 'str' and 'int'`. Fixed by resolving\n hints through `typing.get_type_hints()` instead of the raw annotations dict.\n `_coerce_arguments()` — the runtime safety net that's supposed to coerce a\n model-supplied `\"0\"` string into `0` before a tool call executes — had the\n exact same raw-`__annotations__` bug and silently no-opped for every builtin\n tool; fixed the same way.\n- **Streaming tool-loop could hang a connection forever**: `chat_stream()`'s\n fallback to the sync tool loop (tool calls can't stream token-by-token) had\n no timeout, so a model that didn't cleanly terminate a tool-calling round\n trip left the SSE connection open indefinitely with zero bytes sent. Bounded\n with a 60s `asyncio.wait_for`; on timeout the stream yields a clear message\n instead of hanging.\n\n## [2.14.2] - 2026-07-03\n\n### Fixed\n- **AitherShell session amnesia**: the REPL generated a fresh `session_id` for\n EVERY message, so conversation history never followed across turns (the\n server stored each turn under a different session). One stable id is now\n minted per shell run; `/resume` and `/new` semantics unchanged, and the\n cross-restart auto-restore of the last session now actually works.\n\n## [2.14.1] - 2026-07-02\n\n### Added\n- **Account-tied licensing via device-flow login** — `adk login` now persists the account\n license returned by the device-flow handshake to `~/.aither/license.json` (base64\n `license_key` → `{payload, signature}` envelope), so entitlements from the authenticated\n account carry into every later `adk` invocation without re-auth. Pairs with the fleet-side\n node tool-pack gate that grants packs by plan-tier rank.\n- **`can_use_formbridge` / `can_use_untether` entitlements** (PROFESSIONAL+ rank ≥ 3) backing\n the FormBridge and UNTETHER agent tool-packs.\n\n### Fixed\n- `adk` CLI: guard the optional `memory_graph` singleton (was a latent `NameError` on the\n `/stats`, `/memory`, and save paths when memory was not constructed).\n\n## [2.14.0] - 2026-07-02\n\n### Added\n- **`adk.memory_wiki` — LyraWiki-style self-managed semantic knowledge** (opt-in; nothing constructs it by default): `MemoryWiki(graph_memory)` maintains curated wiki ARTICLES as `wiki_article` nodes (title/slug, LLM-owned markdown with `[[wikilinks]]`, embedding, durable tier, `CONSOLIDATED_FROM` edges to source memories, governance-ledgered revisions).\n - `consolidate(llm, since=, budget=)` clusters unconsolidated episodic/fact nodes (embedding + keyword), drafts/UPDATEs articles via the INJECTED llm callable (`str -> str` or `messages -> str`, sync or async — no hard model dependency), cites source node ids, marks contradicted sources superseded, then DEMOTES consolidated sources to a fast-decay tier via `promote()` (represented knowledge doesn't need raw copies).\n - `lint(llm=None)` — deterministic health checks (orphan wikilinks, empty/stale articles, live-but-contradicted pairs) + an optional LLM audit pass.\n - `prune(relevance_floor=0.05, hard_delete_after_days=14)` — article relevance = tier freshness × reinforcement (reinforce-on-recall) + link-degree to LIVE memories; below the floor → reversible governance tombstone; entombed past the window → **HARD-DELETE** (the tombstone snapshot itself is purged — content and embedding irrecoverable; the true-deletion path). A node is never deleted without a tombstone first.\n - `recall(query, limit)` — article-first RAG (curated articles rank above raw nodes) that reinforces returned articles.\n- **`adk.routines` — agent heartbeat + self-programmed routines**: `RoutineStore` is a durable registry (`~/.aither/routines/{agent}.json`, `AITHER_DATA_DIR`-aware, injectable path) of cron-scheduled self-prompts `{name, cron, instruction, enabled, last_run, last_result, tags}` that rehydrates into the existing `adk.cron.CronScheduler` on `start()`. A fire runs `agent.chat(instruction)` with the agent's full toolset — the agent programs its future self with its own adk. Guardrails: `max_routines` (12), a 5-minute min fire interval per routine, a per-fire timeout, and every result ledgered (truncated) to `last_result`. Direct-callable routines (`register_direct`) fire a bound method instead of a self-prompt. Self-management tools (`routine_create/list/update/pause/resume/delete/run_now`) ship as OpenAI tool defs + handlers (`build_routine_tools` / `register_routine_tools` / `routine_tool_defs`); the handlers ONLY touch the RoutineStore — they can never modify agent config or safety-relevant settings (the leash principle).\n- **`AitherAgent(routines=True, memory_maintenance=True)`** (both default **False** → the default agent is byte-identical): `routines=True` builds the RoutineStore + scheduler, registers the self-management tools, and starts the heartbeat lazily on the first `chat()` (or via explicit `start_routines()`). `memory_maintenance=True` registers DEFAULT routines — `wiki_consolidate` (every 2 h), `wiki_lint` / `wiki_prune` / `graph_sweep` (daily) — as DIRECT method fires so memory upkeep runs even on tiny models, while staying visible/manageable via `routine_list`.\n- `TombstoneStore.purge(tombstone_id)` — the hard-delete endpoint: irrecoverably removes a tombstone snapshot and rewrites the persisted store without it (consumed by `memory_wiki.prune`).\n\n## [2.13.6] - 2026-07-02\n\n### Added\n- **Qdrant backend for GraphMemory dataplane sync — TRUE two-way, verified live.** Set `AITHER_FLEET_QDRANT_URL` and the sync uses Qdrant (`upsert` + `scroll` + filtered `search`) instead of Nexus. This is what actually delivers cross-agent memory sharing: a node is upserted with its OWN embedding under a deterministic per-(tenant,node) point id (idempotent re-push), and `fleet_pull` reliably SCROLLS a tenant's points back — the enumerate Nexus lacks (its `/search` doesn't return ingested docs and `/export` needs lancedb, which has a hard dependency conflict in the fleet). **Proven live with 2 agents:** agent-1 ingests → auto-syncs to the tenant dataplane; a fresh agent-2 in the same tenant `fleet_pull`s all of agent-1's nodes and can search them; a different tenant pulls nothing (isolation). Dimension-safe (never mixes vector dims in a collection). Local SQLite stays source of truth; best-effort, never raises.\n\n## [2.13.5] - 2026-07-02\n\n### Fixed\n- **CRITICAL — GraphMemory dataplane sync push was 100% failing.** `_fleet_push_node` sent `source_type=\"graph\"` / `content_type=\"graph_node\"`, which AitherNexus rejects as invalid enums (HTTP 500) — so NO graph node ever replicated in 2.13.4. Now sends the accepted `source_type=\"manual\"` / `content_type=\"text\"` (graph provenance is carried in `metadata.synced_from`). **Verified live** against a fleet Nexus: a 4-node ingest replicates with `pending==0` and the collection count reflects it.\n\n### Added\n- **Swarm-awareness notification** — on a successful sync batch, `GraphMemory` emits a best-effort `graph.synced` event (tenant/workspace/agent/collection/count) to `AITHER_FLEET_EVENTS_URL` so OTHER agents in the tenant/swarm can pull the fresh data and deconflict in-flight work (push-based awareness instead of polling). No-op unless the env is set.\n\n### Known limitation\n- Cross-agent **rehydration** (`fleet_pull`) is only reliable when the fleet Nexus is backed by lancedb (persistent + exportable). An in-memory Nexus stores on `/ingest` but neither its `/search` nor `/export` returns the docs — two-way read needs the persistent Nexus.\n\n## [2.13.4] - 2026-07-02\n\n### Added\n- **Memory-maintenance primitives on `GraphMemory`** (all opt-in; default behaviour byte-identical):\n - **Reinforce-on-recall**: `recall_with_activation(..., reinforce=True)` bumps `reinforcement_count`/`last_reinforced` on every RETURNED node's metadata mirror (one UPDATE per node, WAL-safe) — making the `MemoryRecord` reinforcement fields the activation scorer reads LIVE. Default `False` = zero writes.\n - **`sweep(now, archive_below=0.05, max_nodes=None, dry_run=False)`**: read-time decay enforced at rest — archives nodes past their tier TTL with freshness×reinforcement score below `archive_below` and `reinforcement_count <= 1`; `max_nodes` additionally archives the lowest-scored overflow. PERMANENT tier immune. Every archive is a reversible governance tombstone (`TombstoneStore.recover`) + a FORGET ledger entry; a node is never deleted without a tombstone. `dry_run` returns the would-archive list without acting.\n - **`promote(node_id, tier=?, role=?)`**: re-tier/re-role a node IN PLACE (tier/role columns + metadata mirror) — edges, embedding and ledger history survive; ledgers an UPDATE entry when governance is on.\n- `MutationType.UPDATE` ledger entry kind (in-place re-tier/re-role).\n- **Per-tenant dataplane sync for `GraphMemory`** — replicate the local graph to a per-tenant vector dataplane (Nexus/Qdrant) so a sovereign agent's memory is durable + rehydratable across restarts and hosting boundaries. `fleet_push_all_nodes()` (catch-up), `fleet_pull()` (best-effort semantic top-up), `fleet_sync_pending()`, and auto-sync on ingest (`_maybe_autosync` + `drain_sync()`). **On by default** (`AITHER_FLEET_SYNC=auto`; only `false/0/off` disables) with an inferred target (local Nexus `:8122` or the gateway in cloud mode) and a SEPARATE collection (`AITHER_FLEET_GRAPH_COLLECTION`, default `graph_memory`) from the KV memory sync. New `GraphMemory(tenant_id=, workspace_id=, fleet_url=, auto_sync=)` constructor args override env (required for multi-tenant host processes). Node `synced` column + schema v4 migration. TLS verify is CA-bundle-aware (`AITHER_CA_BUNDLE`) for the internal-TLS mesh. Local SQLite stays the source of truth; every sync path is best-effort and never raises into ingest.\n\n## [2.13.3] - 2026-07-02\n\n### Added\n- **Canonical self-deploying embeddings provider** (`adk.embeddings`): one embedding provider for the whole SDK so vectors are portable across scopes (platform / tenant / sovereign) — same model + dimension everywhere. Pinned to `nomic-embed-text` (768-d; the vLLM `--served-model-name` for nomic-embed-text-v1.5). Lazy, single-flight resolution chain: explicit `AITHER_EMBEDDINGS_URL` → local vLLM (`:8209` then `:8120`, HTTPS-then-HTTP) → local Ollama → gateway (`AITHER_GATEWAY_EMBEDDINGS_URL`) → auto-deploy a local vLLM embeddings container if a GPU + Docker are present (opt out with `AITHER_EMBED_AUTODEPLOY=0`) → CPU sentence-transformers (384-d, degraded) → feature-hash (384-d, degraded, always works). Every batch is dimension-tagged so callers never silently mix 768-d and 384-d vectors. `get_default_embedder()`, `embed_texts()`, `embed_one()`, `get_provider()`, `reset_provider()`.\n\n### Changed\n- **`GraphMemory` now defaults its embedder to the canonical provider** (opt out with `AITHER_GRAPH_EMBEDDER=legacy`), so every adk agent shares one 768-d space. Added a meta-table dimension guard: the index is pinned to its first embedding's dimension and later different-dimension vectors are refused (kills 768↔384 mixing that silently poisons cosine similarity).\n\n## [2.13.2] - 2026-07-01\n\n### Added\n- **Session persistence**: AitherShell now auto-saves the active session_id to `~/.aither/config.yaml` after each chat turn and auto-restores it on the next shell launch. Multi-turn conversations survive across restarts — the messages were always persisted in SQLite + JSON, only the session pointer was missing.\n- `/new` command to start a fresh session (clears saved session_id).\n- `/sessions` command to list recent sessions with agent name, message count, and timestamp. Resume any with `/resume <id>`.\n\n## [2.13.1] - 2026-07-01\n\n### Fixed\n- **CRITICAL:** `deploy_connect()` now sends a HEAD request to verify the GitHub release asset exists before downloading, with a clear error if the asset is missing.\n- **CRITICAL:** Compose file URLs are now pinned to a stable commit hash (configurable via `AITHER_COMPOSE_PIN` env) instead of tracking the mutable `main` branch.\n- **CRITICAL:** `deploy_adk_node()` now falls back to starting `adk-serve` natively when Docker is unavailable, instead of blocking with \"Docker not installed\".\n- **HIGH:** GHCR login failure now blocks deployment immediately with actionable guidance, instead of warning and continuing to a confusing image-pull failure.\n- **HIGH:** Health checks now stream the last 30 lines of Docker Compose logs on failure, so the user can see why a container didn't start.\n- **HIGH:** vLLM port allocation now probes for conflicts before assigning; if port 8200 is busy, workers are automatically remapped to the next free port.\n- **MEDIUM:** Compose file downloads are now cached with ETag; re-running `adk deploy node` skips re-downloading an unchanged file.\n- **MEDIUM:** DGX Spark discovery now respects `AITHER_DGX_HOSTS` env (comma-separated) for custom network topologies, instead of only checking `spark.local` and `192.168.0.33`.\n- **MEDIUM:** Cloud API keys (Anthropic, OpenAI, DeepSeek) are now validated with a lightweight probe during `adk setup` infra scan, so invalid keys surface early.\n- **MEDIUM:** `deploy_connect()` now validates the extracted extension has a `manifest.json` and warns if it's missing.\n\n## [2.13.0] - 2026-07-01\n\n### Removed\n- **`adk.faculties.memory_graph`** — the pickle-based `MemoryGraph` module is deleted. `from adk.faculties import MemoryGraph` now resolves to the canonical `adk.graph_memory.GraphMemory` for back-compat.\n- **`adk.platform.memory`** — the entire legacy memory subsystem (`MemoryManager`, `UnifiedMemorySystem`, `GameEngine`, `StoryboardEngine`, `AnchorGenerator`) is deleted. Agent memory is `adk.memory.Memory` + `adk.graph_memory.GraphMemory`.\n\n### Changed\n- `adk work` command now uses `GraphMemory` instead of the removed pickle-based faculty graph.\n- `adk.MemoryGraph` and `adk.faculties.MemoryGraph` lazy exports now resolve to `adk.graph_memory.GraphMemory`.\n- `agent.set_memory_graph()` kept for back-compat but documented as deprecated; agents already wire `GraphMemory` automatically in `__init__`.\n- Platform startup (`adk.platform.infrastructure.startup`) gracefully handles missing `MemoryManager`.\n- Platform CLI module listing and health check updated to reflect removed memory module.\n\n## [2.12.7] - 2026-07-01\n\n### Deprecated\n- `adk.faculties.MemoryGraph` — emits `DeprecationWarning` on instantiation; use `adk.graph_memory.GraphMemory` (SQLite-backed, governed, embedding-native) instead.\n- `adk.platform.memory` package — the legacy `MemoryManager`, `UnifiedMemorySystem`, `GameEngine`, and `StoryboardEngine` modules now emit `DeprecationWarning` on import; use `adk.memory.Memory` and `adk.graph_memory.GraphMemory` for new code.\n\n### Changed\n- Updated `adk.faculties` docstring and usage examples to point at the canonical memory surface.\n\n## [2.12.6] - 2026-07-01\n\n### Changed\n- Trimmed public package debt by removing grid runbook artifacts and shell/room binary placeholder directories from published artifacts.\n- Removed stale public-sync allowlist entries for deleted phase test files and added explicit public-sync guards for binary placeholder directories and grid artifacts.\n- Updated the public README grid note so it no longer links to runbook files excluded from the SDK package.\n\n## [2.12.5] - 2026-07-01\n\n### Added\n- Added best-effort fleet memory sync from local ADK memory into Nexus/Qdrant-compatible RAG stores, including batch catch-up and vector-search recall hooks.\n- Added saved config/env support for fleet memory URL, collection, and sync mode.\n\n### Changed\n- Excluded FormBridge runtime/test internals from public ADK packaging and public repo sync.\n\n## [2.12.4] - 2026-07-01\n\n### Added\n- Added a hot-reload endpoint for installed agent packs so newly applied pack tools can be picked up without restarting `aither-serve`.\n\n### Fixed\n- Fixed synchronous `ChatRelay` calls on Python 3.12 when no event loop is running; async WebSocket delivery is still scheduled when a loop exists.\n- Aligned gateway inference tier tests with the current public ladder: free, starter, pro, enterprise, platform.\n\n## [2.12.0] - 2026-06-28\n\n### Added — one-command local inference + upgrade path (click-to-run)\n- **`adk quickstart-local`** — detects hardware, picks a backend\n (Ollama / llama.cpp / vLLM), installs it, downloads a model, verifies, and\n prints next steps. Bootstraps Ollama itself via winget/brew/install.sh when\n missing (no \"go install it\" dead-ends). Default model `gemma4:e2b`.\n- **`adk backend switch <ollama|llamacpp|vllm>`** (+ `backend status`) — migrate\n the active deployment between engines; re-points config and smoke-tests.\n- **`adk install pack:<name>`** (+ `adk install list` / `adk packs`) — install\n ready-made agent packs (openclaw, hermes, claude-code) into `~/.aither/agents`.\n\n### Fixed (found via live end-to-end testing on Windows + CUDA)\n- quickstart-local reported success on a dead endpoint (smoke test was warn-only);\n a failed smoke now fails the command, with a service-readiness wait.\n- `--model` resolved to `None` (getattr default never fired) → uses the real default.\n- Smoke test: 30s→180s timeout for cold model load; accepts reasoning-model output\n (empty `content` at low token budgets is no longer a false failure).\n- `adk install pack:<name>` colon syntax (argparse rejected it before dispatch).\n- `adk backend status` health probe used a nonexistent `/health` path → 404; now\n probes `/v1/models` (uniform OpenAI-compatible liveness for all three backends).\n- Windows: removed non-ASCII glyphs from printed strings that crashed the cp1252\n console (a `✓` even made a successful install report failure).\n\n## [2.11.1] - 2026-06-14\n\n### Fixed — `adk host` / `adk login` device flow (autonomous onboarding)\nFound driving `adk host` against production; all broke real device-flow login:\n- **Device flow hit the portal FRONTEND, not AitherIdentity.** `adk login`/`adk host`\n POSTed `/auth/device/code` to portal/veil.aitherium.com, which 307-redirects API auth\n to `/login`. Added `_resolve_identity_url()` mapping the aitherium.com topology to\n `idp.aitherium.com` (the Identity API); localhost/bespoke pass through unchanged.\n- **Cloudflare 403'd urllib.** `idp.*` sits behind Cloudflare, which blocks urllib's\n default `Python-urllib/3.x` UA. Device-code + poll requests now send a real\n User-Agent + Accept (verified urllib→idp 200, was 403).\n- **`adk host` now self-authenticates via device flow** when there's no token, and\n **retries via device flow on a 401/403** (a stale saved token no longer dead-ends\n registration).\n\n## [2.11.0] - 2026-06-14\n\n### Added — attach your own MCP \"hands\" to a self-hosted agent\nA self-hosted agent now has the full brain (your model) · body (this loop) · **hands**\n(your tools) trifecta. The AitherOS platform relays the MCP servers you registered for\nyour agent in the `/stream` request body as `mcp_endpoints`, and the loop wires their\ntools into the ReAct turn:\n- **`adk/mcp_endpoint_tools.register_mcp_endpoint_tools(agent, endpoints)`** — speaks MCP\n JSON-RPC (`tools/list` to discover, `tools/call` to invoke) and registers a namespaced\n proxy tool (`{endpoint}__{tool}`) for each advertised tool. Mirrors `app_proxy_tools`.\n Best-effort: an unreachable endpoint registers nothing and never raises.\n- **`POST /stream` and `POST /chat/stream`** accept an optional `mcp_endpoints` list\n (`[{name, url, [headers]}]`); `_aitheros_stream` attaches them to the agent before the\n turn. Existing callers are unaffected (the field is optional).\n\n## [2.10.1] - 2026-06-14\n\n### Fixed — self-hosted `aither-serve` was crash-on-startup (2.10.0 regression)\nA fresh `pip install aither-adk && aither-serve --backend deepseek` could not boot. Found\nand fixed while standing up a real self-hosted DeepSeek agent end-to-end:\n- **Startup `NameError: name 'port' is not defined`** in three lifespan helpers\n (`_join_aithernet`, `_init_a2a_server`, `_start_mesh_hosting`) — they referenced a bare\n `port` no longer in scope. Now use `config.server_port`, and `main()` writes the resolved\n `--port`/`--host` back to `config` so every lifespan helper and the invoke_url see the\n actual bound port.\n- **Fleet self-registration crashed the whole server** — the registration handlers caught\n `(ImportError, RuntimeError, OSError, ConnectionError)` but **not** `httpx.HTTPError`, so a\n `RemoteProtocolError`/connect error talking to the control plane took the agent down. All\n network-facing lifespan handlers now also catch `httpx.HTTPError` (non-fatal, as intended).\n- **`--backend` was ignored when `AITHER_API_KEY` was set** — `_auto_detect` picked the cloud\n gateway before the explicit backend, and `_try_elysium_fallback` then replaced the chosen\n provider. An explicit backend (deepseek/openai/anthropic/your own vllm/ollama) now always\n wins; \"auto\"/\"gateway\" still flow through detection. The operator's own brain is never\n silently hijacked.\n- **Approved-tool trace truncated after `/sessions/{id}/confirm`** — the SSE relay treated\n `AgentResponse.tool_calls_made` (a `list[str]`) as dicts (`tc.get(...)`), raised\n `AttributeError`, and an over-narrow `except` swallowed it so the client saw only\n `session_start` + heartbeats. The relay now handles string tool names, the `except` is\n broad (always emits a typed `error` + terminal `complete`), and the module-level\n `_strata_ingest` `NameError` + Chronicle `session_id` kwarg mismatch are fixed.\n\n## [2.10.0] - 2026-06-13\n\n### Added — human-in-the-loop tool approval (`adk/approval.py`)\n- **Pause before a gated tool, resume on decision.** An agent can now pause its turn before\n executing a sensitive tool and wait for a human's allow/deny — so a managed control plane\n (or any operator) can gate tool use without forking the loop.\n- **Policy via `AITHER_TOOL_APPROVAL`** — a comma-separated list of tool names (optionally\n `agent:tool`), or `*` for every tool. Empty = no gating (unchanged behavior).\n- **Survives restarts / days-later approvals.** The paused turn (user message + pending tool\n calls) is persisted to disk keyed by `session_id`; resume is a fresh request, not a held\n connection. Decisions are recorded per `(session_id, tool_name)`.\n- **`AgentResponse.requires_action` / `.pending`** — set when a turn paused; `pending` lists\n the gated tool calls awaiting a decision.\n- **`AitherAgent.resume(session_id, decisions)`** — records the decisions and re-runs the\n paused turn (the gate consumes them: allowed tools execute, denied tools get a denial\n observation, the turn continues — and may pause again on a different gated tool).\n- **Server:** `POST /sessions/{id}/confirm` records decisions and streams the resumed turn;\n `/stream` emits a `requires_action` event when a turn pauses.\n\n## [2.9.0] - 2026-06-13\n\n### Added — universal LLM continuation primitive (`adk/llm/continuation.py`)\n- **`run_continuation()` + `stitch()`** — one shared continue-until-complete primitive. When a\n completion stops because it hit the output token cap (`finish_reason == \"length\"`), it continues\n the generation and STITCHES the chunks (overlap-dedup + restart-detect) into a complete answer,\n instead of every call-site hand-rolling its own retry. Bounded three ways (max rounds AND total\n output ceiling AND a no-growth break), and it never mutates the caller's message history.\n- **`LLMProvider.chat_with_continuation()`** and **`LLMRouter.chat_with_continuation()`** — every\n provider AND the router inherit the primitive, so any call-site gets continuation for free.\n- Kill-switch `ADK_LLM_CONTINUATION=off`; tunables `ADK_LLM_MAX_CONTINUATIONS`,\n `ADK_LLM_MAX_TOTAL_OUTPUT_TOKENS` (defaults: 2 rounds / 8192 tokens).\n\n### Changed\n- The ReAct loop's truncation recovery now uses the shared primitive. The previous inline\n doubling-retry REPLACED the partial response (losing earlier text in `resp.content`) and injected\n continuation scaffolding into the live message history; it now STITCHES and leaves history clean.\n Tool-call turns are never continued as text.\n\n### Included — previously-committed-but-unreleased changes\n- 2.8.2: TLS verification on by default (`verify=False` removed).\n- 2.8.1: baked license + pack-signing public keys.\n- 2.8.0: version-bump release.\n\n## [2.7.0] - 2026-06-05\n\n### Added — provider-agnostic LLM cost levers (at the `LLMProvider` ABC)\n- **Prompt caching.** New `ProviderCapabilities` (`prompt_cache: explicit|automatic|none`, `batch`) and\n normalized `LLMResponse.cache_read_tokens`/`cache_write_tokens`, so the \"tokens saved\" meter never\n depends on which backend served the turn. Anthropic inserts `cache_control` breakpoints over the stable\n system+tools prefix; OpenAI/DeepSeek caching is automatic and their `cached_tokens` are now surfaced\n (previously discarded). Default-on in `AitherAgent.chat`; override with `cache=False`.\n- **ResponseCache** (`adk/llm/cache.py`) — exact-match response cache with a pluggable backend, wired into\n `LLMRouter` as an opt-in (`LLMRouter(response_cache=…)` + per-call `cacheable=True`). OFF by default — no\n determinism change unless opted in.\n- **Gemini provider** (`adk/llm/gemini.py`) with explicit `cachedContents` caching; registered in the router.\n- **Async batch runner** (`adk/llm/batch_runner.py`) — `run_batch()` over the Anthropic Message Batches /\n OpenAI Batch APIs (~50% off) with a transparent fallback to concurrent `chat()` where unsupported.\n- **Graph-RAG retriever interface** (`adk/graph_retriever.py`).\n\n### Added — principal authority (prompt-injection defense enforced outside the model)\n- `adk/auth.py`: `Principal` / `AuthContext` / `PrincipalResolver` (+ deny-all default).\n- `ToolRegistry.execute(name, args, auth=…)` — a default-deny authorization gate at the tool boundary, so a\n forbidden tool call is blocked regardless of what the LLM emits. `ToolDef` gains `required_clearance` /\n `action_class`. Threaded through `AitherAgent.chat`; `auth=None` is fully backward-compatible.\n- `adk/channel_auth.py`: Telegram webhook-secret + CEO-id checks and HMAC-signed approvals.\n\n## [2.6.4] - 2026-06-05\n\n### Fixes\n- **Loop-guard nudges corrupted the message history → DeepSeek 400 mid-run.**\n When the ReAct loop emitted multiple tool calls in one assistant turn and the\n loop guard fired a WARN/BLOCK/CIRCUIT_BREAK on one of them, it spliced a\n standalone `system` nudge message *between* the tool results. DeepSeek (and\n strict vLLM chat templates) require an assistant `tool_calls` message to be\n followed by exactly one `tool` result per `tool_call_id`, contiguously — so the\n interleaved message produced `400 … insufficient tool messages following\n tool_calls message` and aborted the turn (this is reliably hit after crossing\n the 15-call circuit-break threshold, where every subsequent call WARNs).\n Per-call guidance is now folded into that call's own `tool` result, and any\n standalone steering is deferred to a single message AFTER the whole batch —\n preserving the `tool_calls`↔`tool_results` pairing every backend requires.\n Regression test: `deep-research-agent/tests/test_msg_structure.py`\n (loopguard-flood scenario).\n\n## [2.6.3] - 2026-06-04\n\n### Fixes\n- **Transient provider errors killed a run on the first blip.** A single\n `502 Bad Gateway` / `503` / `529 overloaded` / `429` from the upstream API\n aborted the whole request — the Anthropic provider had **no retry at all**, and\n the OpenAI-compatible provider only retried `429`. Both now retry transient\n statuses (`408/429/500/502/503/504/529`) with `Retry-After`-aware exponential\n backoff (3 attempts), and surface the provider's body if all retries are\n exhausted. Non-transient `4xx` (e.g. `400`) still fails fast. Applies to\n `chat` and `chat_stream` on both providers.\n\n## [2.6.2] - 2026-06-04\n\n### Fixes\n- **DeepSeek (and strict OpenAI-compatible backends) 400'd on long tool\n conversations.** The ReAct loop legitimately injects mid-conversation\n `system` steering messages (the \"[DIMINISHING RETURNS]\" hint after several\n low-output tool iterations, loop-guard nudges). OpenAI tolerates a non-leading\n `system` message; DeepSeek rejects it with a 400. Long research runs\n (many `fetch_url` calls) reliably tripped this. Fix: non-leading `system`\n messages are demoted to `user` at the OpenAI-compatible provider boundary\n (`_demote_nonleading_system`) — steering intent preserved, payload valid on\n every backend; leading system prompt(s) untouched.\n- **Opaque HTTP errors.** `resp.raise_for_status()` discarded the provider's\n response body, so a 400/422 surfaced as a bare `Client error '400 Bad\n Request'` with no cause. `_ensure_ok` now includes the provider's actual\n message (e.g. `context_length_exceeded`, `Invalid 'messages'`) in the raised\n error, for both `chat` and `chat_stream`.\n\n## [2.6.1] - 2026-06-04\n\n### Fixes\n- **`MCPServer.mount()` returned HTTP 422 for every `/mcp` call.** Under\n `from __future__ import annotations` the route handler's `request: Request`\n annotation is a string that FastAPI resolves against the *module* globals, but\n `Request` was imported locally inside `mount()` — so FastAPI couldn't resolve\n it and treated `request` as a required query param. The MCP server endpoint\n (`initialize`, `tools/list`, `tools/call`) was unusable when mounted. FastAPI\n symbols are now imported at module level (guarded; the `[node]` extra still\n gates actual use). Any agent's tools can now be exposed as a working MCP\n server with `MCPServer(...).mount(app)`.\n\n## [2.6.0] - 2026-06-04\n\n### Streaming + scaffolding\n- **`AitherAgent.stream_chat`** — reliable streaming sibling of `chat()`: native\n tool-calling loop (concludes on its own, unlike the text-protocol `stream_react`)\n that emits live `tool`/`tool_result` events and streams the answer as `token`\n events. `token_delay` paces tokens for a typing effect.\n- **`adk new <template>`** — scaffold a full, runnable template app. Ships the\n `deep-research` template (a cited web-research agent: pack + server + web UI):\n `adk new deep-research`.\n\n### Fixes\n- **BYO-key no longer community-capped.** The free-tier monthly token cap now applies\n only to the metered Aitherium gateway backend, never to a user's own provider key\n (Anthropic/OpenAI/DeepSeek/Ollama/vLLM). Previously it could brick a self-hosted\n agent after ~100k tokens on the user's own key.\n- **Stronger, grounded memory recall.** `chat()` now injects the top-6 knowledge-graph\n hits (untruncated) with a hard no-fabrication instruction — was top-3 truncated to\n 200 chars, which surfaced partial facts and let weaker models invent the rest.\n- **Tool-arg coercion.** `ToolRegistry.execute` coerces LLM-supplied args toward each\n parameter's annotated type, so a tool no longer crashes when a model passes a list\n or string where an int/float/bool was expected.\n\n### Docs\n- New `docs/AGENT_DEV_GUIDE.md` — the golden path for building agents/packs.\n\n## [2.5.0] - 2026-06-04\n\n### Continual learning — agents that learn from runs and reuse memory\n\nAgents now improve the second time they do a task instead of starting from scratch.\n\n### Added\n- **Recall-before**: `AitherAgent.chat()` searches the local `SkillStore` and injects the\n top matching learned skills as a system block, so the model reuses proven procedures.\n- **Learn-after**: `_learn_after()` runs after each response — reinforces (`success_count++`)\n any skills that were recalled+reused, and extracts + saves a NEW skill from successful\n multi-tool runs via `SkillExtractor`. Wired at both `chat()` return paths.\n- Gated by `AITHER_SKILLS` (default on). The previously-unused `SkillStore`/`SkillExtractor`\n are now connected to the run loop.\n\n### Notes\n- Pairs with the platform-side LearnedProcedure substrate (recipes → scored procedures →\n skills/tools/A2A/packs → MCTS priors → evolution). The SDK shares memory via the Spirit\n bridge when configured.\n\n## [2.0.0] - 2026-06-02\n\n### Open-core boundary (BREAKING)\n\nThis release draws a single, enforced free/paid line. The free tier stays\ngenuinely useful (real agent, typed + graph + code memory, ReAct, ~essential\ntools); paid tiers unlock the *scale* capabilities via portal.aitherium.com.\n\n### Added\n- **`adk/licensing.py`** — the entitlement keystone. Resolves a tier\n (COMMUNITY by default), verifies optional portal-signed `~/.aither/license.json`\n (Ed25519), and answers `can_use_fleet/channels/cron/swarm/auto_neurons`,\n `is_agent_licensed`, `max_effort`, etc. **Fail-closed**: no/invalid/expired\n license → free tier; an unsigned license never grants premium.\n `AITHER_TENANT_SLUG=aitherium` → unrestricted INTERNAL tier.\n- **Moat guard** (`scripts/check_moat_boundary.py`) wired into the publish\n workflow — blocks any wheel that leaks the moat or drops the keystone.\n- Tests: `tests/test_licensing.py`, `tests/test_moat_boundary.py`.\n\n### Changed (gating — fail-closed)\n- Free tier effort is capped at 3 (reasoning effort 7–10 needs Professional).\n- Monthly token budget hard-block on the free tier (no silent overspend).\n- Gated behind a paid tier: fleet mode (`FleetConfig`), cross-agent delegation\n (`AgentForge.delegate`), channel adapters, cron scheduler, proactive\n auto-neurons, and `swarm`/`swarm_code` dispatch.\n\n### Removed\n- **`adk/nanogpt.py`** (on-device training IP) removed from the published SDK\n and the public repo; relocated to the internal AitherOS moat. Excluded from\n the wheel as defense-in-depth.\n- Pre-2.0 releases shipped this IP and ungated capabilities and are being\n yanked (PyPI) / removed (GitHub releases). See `scripts/yank_leaky_releases.py`\n and `scripts/purge_public_leaks.sh`.\n\n## [1.20.0] - 2026-05-27\n\n### Fixed\n- **Pack discovery path mismatch** — `_find_local_packs()` now scans both\n `~/.aither/packs/` and `~/.aitheros/packs/`, so packs installed via\n `adk pack install` are discovered by `adk-workspace`.\n\n### Added\n- **Auto-register on pack install** — `adk pack install` now auto-registers\n with the portal if the pack contains an `agent.yaml` with a `portal` section.\n One command does download + extract + register.\n- **Flux event on agent registration** — `developer_portal.py` emits\n `agent.registered` Flux event so the portal fleet UI updates in real-time.\n\n## [1.15.0] - 2026-05-26\n\n### Added\n- **Cloud memory** — `adk quickstart --cloud` now tests gateway memory and\n auto-configures Spirit URL so memories persist across devices via\n `gateway.aitherium.com/v1/memory/teach` and `/v1/memory/recall`.\n- **Config→env export for cloud mode** — `Config.from_env()` now exports\n `AITHER_CLOUD_MODE`, `AITHER_SPIRIT_URL`, and spirit path env vars from\n saved config so Memory and other modules pick them up without needing\n explicit env vars set by the user.\n- **Cloud Quick Start section** in README — 3-command setup for users who\n just want a Claude/GPT-4/DeepSeek API key and the full agent harness.\n- **`--memory` profile for node deployment** — `adk deploy node --memory`\n adds Spirit (8087) + WorkingMemory (8101) containers for persistent\n vector memory. Without this, agents use local SQLite only.\n- **Configurable vLLM in node compose** — `AITHER_VLLM_MODEL`,\n `AITHER_VLLM_GPU_UTIL`, `AITHER_VLLM_CTX_LEN`, `AITHER_VLLM_QUANT_ARGS`\n env vars let `adk setup` write GPU-detected tier settings to `.env`\n and have the node compose pick them up (TQ4 for small GPUs, BNB for large).\n\n### Fixed\n- **Memory cloud routing** — `Memory._spirit_teach()` and `_spirit_recall()`\n now use configurable paths and auth headers for gateway proxy. Previously\n cloud-only users silently lost all Spirit memory calls.\n\n## [1.9.0] - 2026-05-24\n\n### Added\n- **Composable ToolPack system** — agents can load tool packs from\n `.toolpack.yaml` directories. Drop a pack in `~/.aitheros/packs/` or\n set `AITHER_PACKS_DIR`, and any agent discovers it automatically.\n- **`tools.packs` in agent.yaml** — list tool pack IDs to auto-load\n (e.g. `packs: [crm-tools, calendar-pro]`).\n- **`register_tool_packs(agent, pack_ids, packs_dir)`** in `builtin_tools` —\n programmatic pack loading for agents.\n- **`AITHER_TOOL_PACKS` env var** — comma-separated pack IDs loaded on\n agent init (alternative to YAML config).\n- **Unknown builtin categories resolve as pack IDs** — `tools.builtin:\n [file_io, crm-tools]` now works: `crm-tools` is looked up as a pack.\n\n## [1.8.0] - 2026-05-24\n\n### Removed\n- **11 dead modules** (3,023 lines) — `agent_deploy`, `cloud_deploy`, `create`,\n `phonehome`, `sdk_bridge`, `sovereign`, `telemetry`, `telemetry_config`,\n `error_reporter`, `anonymizer`, `package`. None were imported anywhere.\n- **29 ghost exports** from `__init__.py` — `secrets`, `otel`, `fs_sandbox`,\n and `vector_memory` modules were listed in `__all__` and `__getattr__` but\n the backing files never existed. Accessing them raised `ModuleNotFoundError`.\n- **Stale dist/ artifacts** — removed old wheels/tarballs (v0.4.0–v1.4.1, 11 MB).\n- **Orphan test scripts** — `test_phase1.py` and `test_phase23.py` (one-off\n validation scripts not part of the pytest suite).\n\n### Fixed\n- **Version mismatch** — `__init__.__version__` now matches `pyproject.toml`\n (was stuck at 1.6.0).\n\n## [1.5.0] - 2026-05-23\n\n### Added\n- **`--sovereign` flag for `adk deploy node`** — registers the node with the\n Aitherium hub via federation after deployment. Saves federation credentials\n to `~/.aither/.env.federation`. Accepts `--hub` (custom hub URL) and\n `--tenant` (tenant slug) options. Non-fatal: node runs standalone if\n registration fails.\n- **Federation → fleet cross-post** (Genesis) — sovereign nodes that register\n or heartbeat via `/federation/register` and `/federation/heartbeat` now\n automatically appear in the fleet dashboard (`/fleet/deployments`) with\n `node_type: \"sovereign\"` and live status/metrics.\n- **\"Deploy Sovereign Node\" section on Connect page** (AitherVeil) — Section 8\n with ADK deploy commands, fleet dashboard links, and provision page links.\n- **`deploy-node` action in tunnel proxy** (AitherVeil) — Connect page and\n external callers can now request bootstrap scripts via\n `POST /api/tunnel/devworkspace?action=deploy-node`.\n\n### Fixed\n- **Fleet provision page** — API URL corrected from `/api/fleet/provision` to\n `/api/bridge/genesis/fleet/provision` (matches bridge proxy pattern).\n- **Fleet detail page** — API URL corrected from `/api/fleet/deployments/` to\n `/api/bridge/genesis/fleet/deployments/` (same fix).\n- **Dockerfile** — entry point corrected from `aither-serve` to `adk-serve`\n (matches `pyproject.toml` script registration). Version label updated.\n\n## [1.4.1] - 2026-05-22\n\n### Fixed\n- **macOS executable builds** — dropped `--target-arch universal2` from\n `packaging/build_executable.py`. PyPI wheels for `pydantic_core` and other\n native deps are arch-specific, not fat binaries, so universal2 always failed\n with `IncompatibleBinaryArchError`. Now builds for the runner's native arch\n (`aither-macos-arm64`).\n- **Release workflow strategy** — added `fail-fast: false` so a single\n executable build failure no longer cancels the other OS jobs.\n- **PyPI publish** — added `skip-existing: true` so re-runs of the same\n version don't fail when the artifact already exists.\n- **GitHub Release job** — now runs whenever `pypi` succeeds even if some\n executable matrix entries fail, so the release page is always created with\n whatever binaries did build successfully.\n\n## [1.4.0] - 2026-05-22\n\n### Added\n- **Sovereign template now ships with `agent_core` 5-tier memory** — generated\n backends include the canonical `agent_core/` package (working → episodic →\n semantic → procedural → identity) plus `agent_memory.py` helper, vendored\n from `portal-kit-backend`.\n- `aiosqlite>=0.20.0` added to sovereign template `requirements.txt` for\n local agent_core SQLite storage.\n- Document tombstoning: `evict_cached_conversations_referencing` is wired\n through document-delete handlers in consumer apps so memories citing\n deleted documents are blocked from recall.\n- New tests: 13 smoke tests for `portal-kit-backend/agent_core` covering\n promotion thresholds, decay rates, store roundtrip, tombstone recall\n blocking, circuit breaker state machine, and chat-turn recording.\n\n### Changed\n- `scripts/vendor_agent_core.py` now syncs canonical `agent_core` to 4\n targets: WorkspaceRuntime, ADK sovereign template, and the GargBot +\n Chelle consumer mirrors (previously only 2).\n\n## [1.2.1] - 2026-05-21\n\n### Added\n- `adk train` command group — 6 subcommands: status, launch, logs, cancel, runs, register-gpu\n- `/slash-commands` endpoint — ADK server auto-exposes all 35 CLI commands as structured JSON manifest for AitherShell\n- `/cli/execute` endpoint — AitherShell can run any CLI command through the server\n- `build_command_manifest()` — introspects full argparse tree (commands, args, choices, subcommands)\n- `_register_commands()` extracted from main() for shared parser construction\n\n### Changed\n- `_get_genesis_url()` now probes: Genesis → ADK server → Aitherium cloud gateway (works without Genesis)\n\n## [1.2.0] - 2026-05-21\n\n### Added\n- **Runtime backend switching** — `agent.switch_backend(\"deepseek\")` and `agent.llm.switch_backend()` change provider without recreating agent\n- **Hybrid reasoning** — `agent.set_reasoning_backend(\"anthropic\")` routes effort 7+ to cloud API while keeping local orchestrator\n- **First-class DeepSeek provider** — `deepseek` in effort model table with `deepseek-chat` and `deepseek-reasoner`\n- **TQ4 quantization tiers** — `nano` (6GB, TurboQuant 4-bit), `standard-tq4` (12GB, both models TQ4), `hybrid-tq4` (6GB + cloud reasoning)\n- **`adk backend` CLI** — `list`, `set`, `set-reasoning`, `test` subcommands\n- **`adk quickstart`** — unified first-run wizard (setup + auth + shell)\n- **`adk tools`** — list local + MCP tools with tier markers\n- **`adk backup`** — tarball export of `~/.aither/`\n- **`adk ingest`** — feed docs/files into knowledge graph\n- **`adk setup nemotron`** shortcut — alias for `--tier lite`\n- **`--reasoning-api`** flag in setup — `anthropic`, `openai`, `deepseek`, `gateway`\n- **`--dgx-spark URL`** flag — remote vLLM endpoint configuration\n- **DGX Spark auto-detection** — scans `spark.local`, `192.168.0.33`, `AITHER_DGX_URL` env\n- **Post-setup smoke test** — sends real inference request after container startup\n- **Schema migration system** — version table + migration runner in `memory.py` and `graph_memory.py`\n- **Configurable vLLM port scan** — `AITHER_VLLM_PORTS` env var for custom ports\n- **`aithershell` entry point** — alias for `adk` in pyproject.toml\n- Shell auto-downloads binary on first `adk shell` (no `--install` needed)\n- Shell pre-flight check warns if no backend detected\n- Shell passes full config via env vars (API key, tenant, inference URL)\n- DGX/Remote and Cloud API checks added to `adk doctor`\n- Config fields: `reasoning_backend`, `reasoning_api_key`, `deepseek_api_key`, `dgx_url`, `vllm_extra_ports`\n\n### Fixed\n- `test_aeon_chat_basic` timeout — mock scope was exiting before request\n- `adk doctor` check_disk timeout — cap rglob at 5000 files\n\n## [1.1.7] - 2026-05-19\n\n### Added\n- Remote agent pipeline — `adk onboard` → fleet dispatch → remote inference\n- Agent-to-agent federation with mesh relay\n- `adk connect` for desktop mesh enrollment\n\n## [1.1.6] - 2026-05-19\n\n### Fixed\n- Tool call execution recovery across all backends\n- Hermes tool call fallback parsing for vLLM\n\n## [1.1.5] - 2026-05-19\n\n### Fixed\n- Tool call execution + Hermes fallback parsing improvements\n\n## [1.1.0] - 2026-05-18\n\n### Added\n- Multi-channel gateway (Telegram, Discord, Slack, webhook)\n- Aeon group chat with 7 presets\n- Skills auto-extraction from multi-step sessions\n- MCP stdio server for Claude Code integration\n- `adk gateway`, `adk cron`, `adk skills`, `adk soul` commands\n- SOUL.md import/export for portable agent identity\n- Elysium cloud device-flow authentication\n\n## [1.0.0] - 2026-05-18\n\n### Breaking\n- Package consolidated — `aither_adk` namespace replaced by `adk` + `adk.platform`\n- 93 duplicate files removed from `AitherOS/packages/aither_adk/`\n\n### Added\n- Standalone core primitives (`adk.core`: Agent, Tool, Memory, ModelBackend, Capability, Trace)\n- `adk.platform` sub-package merging all platform toolkit modules\n- Compatibility shims for legacy `from aither_adk.*` imports\n\n### Changed\n- Version synced across pyproject.toml, `adk/__init__.py`, `adk/platform/__init__.py`, npm package\n- Classifier promoted from Alpha to Beta\n\n## [0.16.0] - 2026-04-16\n\n### Swarm Coding Engine & Repowise Integration\n- **`repowise_search` tool** — Semantic + keyword hybrid code search via Repowise, with ripgrep fallback\n- **`swarm_code` tool** — Dispatch complex tasks to 11-agent swarm pipeline (ARCHITECT->SWARM->REVIEW->JUDGE)\n- **`agent.swarm()`** — Async convenience method with configurable mode, effort, timeout\n- **`agent.code_search()`** — Async convenience method returning structured results\n- New tool categories: `repowise` and `swarm` in TOOL_CATEGORIES\n- `repowise_search` and `swarm_code` added to `__init__.py` exports with lazy loading\n- `IDENTITY_DEFAULTS` updated: repowise for code-focused agents, swarm for orchestration agents\n- Standalone graceful degradation: repowise falls back to ripgrep, swarm returns structured error\n\n## [0.13.0] - 2026-04-02\n\n### Graph Faculties — Local Knowledge for Every Agent\n- **CodeGraph** (2,799 lines) — Full Python AST indexer with call graph, keyword/semantic/hybrid query, embedding matrix cache, incremental re-indexing, multi-hop chain expansion\n- **MemoryGraph** (1,339 lines) — Graph-based persistent agent memory with 10 edge types, hybrid query (keyword + semantic + graph expansion), multi-hop recall, pickle persistence\n- **EmbeddingProvider** — 4-backend fallback chain: sentence-transformers (GPU/CPU) -> Ollama -> Elysium cloud -> feature hashing (zero deps)\n- **BaseFacultyGraph** — Abstract base with HMAC-SHA256 validated pickle persistence\n\n### Agent Integration\n- `agent.set_code_graph(cg)` — auto-registers `code_search` + `code_context` tools\n- `agent.set_memory_graph(mg)` — auto-registers `remember` + `recall` + `memory_stats` tools\n- Both graphs inject context into LLM prompts automatically during chat\n\n### Zero-Config Onboarding\n- `adk start` / `adk` (no args) — auto-detect project, index code, detect LLM, persistent memory, interactive chat\n- Works for any directory: Python codebases, doc folders, mixed workspaces\n- Auto-detects LLM: Ollama -> vLLM -> Elysium -> OpenAI -> Anthropic\n- Per-project persistent memory in `~/.aither/memory/<project>`\n- `adk index <path>` — standalone indexing with progress bar and stats\n\n### MCP Gateway\n- `POST /v1/embeddings` — OpenAI-compatible embedding proxy to vLLM-embeddings:8209\n- Available to all tiers (embeddings are free)\n- ADK EmbeddingProvider uses this as Elysium cloud fallback\n\n### Optional Dependencies\n- `pip install aither-adk[graphs]` — numpy for 10x cosine similarity speedup\n- `pip install aither-adk[embedding]` — sentence-transformers + torch for local GPU embeddings\n- `pip install aither-adk` alone — graphs work with feature hashing (zero deps)\n\n## [0.12.0] - 2026-04-01\n\n### Bootstrap & Service Discovery\n- Version handshake -- ADK checks major.minor compatibility with Genesis/Node\n- Background reconnect loop -- ServiceBridge re-probes every 30s in standalone mode\n- Port 8080/8090 documented -- MCP vs OpenAI-compat clearly separated\n- Genesis URL configurable via GENESIS_URL env var (was hardcoded)\n- Standalone mode warning -- visible stderr alert when AitherOS not detected\n- Auto-reconnect on startup when services come online\n\n### Elysium Cloud Inference\n- Unified gateway URL -- gateway.aitherium.com handles auth + billing + inference\n- Streaming inference -- SSE passthrough for /v1/chat/completions with stream=true\n- Auth proxy routes -- /v1/auth/register, /v1/auth/login, /v1/auth/me\n- Billing proxy -- /v1/billing/balance through gateway\n- AitherConnect Elysium fallback -- cloud inference when local Genesis is down\n- AitherDesktop Elysium fallback -- third-tier chat fallback after Node\n\n### Infrastructure\n- /discovery endpoint on Genesis -- unified service URLs/versions/health\n- /api/config/services on Veil -- runtime port config for client-side JS\n- Veil healthcheck gates on Genesis -- unhealthy when backend is down\n- Desktop crash detection 90s->60s (threshold 3->2)\n\n## [0.11.0] - 2026-04-01\n\n### Agent Execution Quality (Claude Code Parity)\n- Raise loop guard block threshold from 3 to 4 -- agents get more room for iterative search\n- Soft synthesis nudge for effort >= 4 (no tool stripping, trust the model)\n- max_output_tokens escalation -- retry up to 3x when response is truncated\n- Tool result pairing guarantee -- synthetic error for orphaned tool_use blocks\n- Micro-compaction of old tool results -- save context tokens on long sessions\n- First-turn tool forcing only for effort >= 6 (trust model for lower effort)\n- Diminishing returns detection -- nudge agent when 3+ turns produce < 500 tokens\n- Message normalization -- merge consecutive same-role messages, strip empties\n- LLM retry with exponential backoff (5 retries, 500ms-16s, jitter)\n\n## [0.9.0] - 2026-03-16\n\nThe \"connected world\" release. Cross-platform identity pairing, voice capabilities, and multi-channel integration.\n\n### Added\n- **Pairing**: Cross-platform identity linking (`adk/pairing.py`)\n - `PairingManager` — SQLite-backed identity linking with 6-char pairing codes\n - Link users across Telegram, Discord, Slack, WhatsApp with 10min TTL codes\n - Canonical session IDs for cross-channel conversation continuity\n - `get_session_id()` returns \"user-{id}\" for paired users\n- **Voice**: Speech-to-text and text-to-speech client (`adk/voice.py`)\n - `VoiceClient` — async STT/TTS/emotion via AitherVoice service\n - Convenience functions: `hear()`, `say()`, `feel()`\n - 6 voice options: alloy, echo, fable, nova, onyx, shimmer\n - Emotion detection with intensity scoring\n- New exports: `PairingManager`, `PairingResult`, `PlatformIdentity`, `VoiceClient`, `TranscriptionResult`, `SynthesisResult`, `EmotionResult`\n\n### Changed\n- `__init__.py` exports expanded with pairing and voice symbols\n\n## [0.6.0] - 2026-03-13\n\nThe \"group mind\" release. Multi-agent group chat, creative tools, and Iris identity.\n\n### Added\n- **Aeon**: Multi-agent group chat engine (`adk/aeon.py`)\n - `AeonSession` — persistent group chat with parallel agent execution\n - 7 presets: balanced, creative, technical, security, minimal, duo_code, research\n - Orchestrator synthesis: Aither summarizes all agent responses\n - Serial execution for Ollama, parallel for vLLM/cloud\n - ConversationStore persistence with `type: \"aeon\"` metadata\n - `group_chat()` one-shot convenience function\n- `aither aeon` CLI command — interactive terminal group chat with color-coded agents\n - `-p/--preset`, `-a/--agents`, `-r/--rounds`, `--no-synthesize` flags\n - `reset` and `quit` commands\n- Server endpoints: `POST /aeon/chat`, `GET /aeon/presets`, `GET /aeon/sessions/{id}`\n- Creative tools in builtin_tools: `image_generate`, `image_refine`, `image_search`, `video_generate`\n- Iris agent identity with visual generation capabilities\n- 48 Aeon tests (data models, presets, chat, context, persistence, server, exports)\n\n### Changed\n- `__init__.py` exports: `AeonSession`, `AeonResponse`, `AeonMessage`, `group_chat`, `AEON_PRESETS`\n\n## [0.5.0] - 2026-03-13\n\nThe \"tenant-ready\" release. Multi-tenant admin, permission grants, safety profiles, and a full setup wizard.\n\n### Added\n- `aither setup` interactive setup wizard with hardware detection, model selection, identity config\n- Strata storage backend: SQLite WAL persistence for conversations, memories, knowledge graphs\n- CLI test runner: `aither test` with auto-discovery and parallel execution\n- Permission grants system for MCP tool access control\n- MCP account management tools\n- LLM provider auto-detection for Ollama and vLLM endpoints\n- Apache-2.0 LICENSE file\n- Elysium desktop sync module\n- Comprehensive CLI test suite (493 tests)\n- Strata storage test suite (972 tests)\n- LLM provider tests (82 tests)\n\n### Changed\n- CLI expanded: `aither setup`, `aither test`, `aither bugreport`, `aither doctor`\n- Strata module rewritten as full local-first storage engine\n- Server startup includes Strata initialization\n- README rewritten with clearer quickstart and architecture docs\n\n### Fixed\n- Dead documentation links\n- Python 3.11 compatibility issues\n- Ruff per-file-ignores configuration\n- Identity provisioning edge cases\n- Docker node-gyp native dependency builds (python3 + build tools)\n\n## [0.4.0] - 2026-03-13\n\nThe \"own your AI\" release. Self-hosted agent OS for people who don't want their data on someone else's servers.\n\n### Added\n- Muse agent identity (creative/artistic generation)\n- Port 8120 to vLLM scan for ExoNodes discovery\n- Public roadmap with milestone-based porting schedule\n- Competitive positioning: self-hosted alternative to cloud-locked AI appliances\n\n### Changed\n- LLM router: compute-aware effort routing (replaces effort-level context gating)\n- README rewritten for sovereignty-first messaging\n- Package description updated\n- Promoted from alpha to stable release\n\n### Fixed\n- Version string consistency between pyproject.toml and __init__.py\n- vLLM port scanning now includes port 8120 (ExoNodes)\n\n## [0.3.1] - 2026-03-09\n\n### Added\n- GraphMemory: SQLite knowledge graph with Ollama embeddings and hybrid search\n- NeuronPool: auto-fire pre-LLM data gathering agents\n- NanoGPT: pure Python char-level transformer with LoRA fine-tuning\n- Safety gates: IntakeGuard (input), LoopGuard (recursion), Sandbox (code exec)\n- Event system: EventEmitter with chat/tool/forge event types\n- Builtin tools: identity-based tool selection\n- ServiceBridge: auto-discovery of AitherOS services\n- Streaming: chat_stream with safety gate integration\n- Auth middleware: Bearer token authentication\n- CLI: `aither init` and `aither serve` commands\n\n### Changed\n- Wired 5 previously disconnected modules into agent loop\n- 522 total passing tests (up from 85)\n\n## [0.3.0] - 2026-03-07\n\n### Added\n- Clean-room agent development kit\n- Multi-backend LLM providers (Ollama, OpenAI, Anthropic)\n- AitherAgent class with @tool decorator\n- SQLite conversation memory\n- OpenAI-compatible server (`aither-serve`)\n- 16 agent identities as package data\n- MCP bridge to mcp.aitherium.com\n- Privacy-centric opt-in telemetry\n- Bug reporting CLI and API\n- FastAPI server with OpenAI-compatible endpoints\n- Hardware auto-detection (5 tiers, 11 profiles)\n- Fleet orchestration and multi-agent coordination\n- A2A mesh protocol\n- Federation protocol for cross-instance agent dispatch\n\n### Initial release\n- 85 passing tests\n- Apache-2.0 license\n"}}}}