# AbstractFramework - llms-full
> Full text of key files from this repo. Sections are separated by `--- <path> ---`.

--- README.md ---
# AbstractFramework

**Write once. Generate everything.**

A modular, open-source ecosystem for building **durable, observable, multimodal** AI systems. Text, voice, image, video, music — one unified interface, any provider, any model, local or cloud.

AbstractFramework is an ecosystem of composable packages for building AI systems that work in operational reality:

- **Durable by default**: workflows **pause and resume** safely (survive crashes and restarts)
- **Observable**: an append-only **ledger** so any UI can reconstruct state by replaying history
- **Controlled actions**: explicit boundaries for **tool execution**, approvals, and evidence
- **Multimodal**: capability plugins (voice, vision, music) that stay out of your way until you need them

Think of it as an **agentic OS**: durable runs + replay-first observability + multimodal capabilities — write once, run across providers and deployment modes.

> **Prerequisites**: Python 3.10+. Node.js 18+ for browser UIs. An LLM backend (Ollama, LM Studio, vLLM, or a cloud API key).

---

## Two entrypoints

Start lightweight with just the LLM library, or go all-in with a production gateway. Both paths lead to the same ecosystem.

### 1) AbstractCore — LLM SDK + OpenAI-compatible `/v1` server

Start here if you need a lightweight LLM library for scripts, notebooks, or existing applications. No infrastructure required — just `pip install` and call. Add multimodal capabilities with plugins as you grow.

- 9+ providers with identical API (local + cloud)
- Universal tool calling, structured output, streaming
- Media handling (images, PDFs, audio, video)
- OpenAI-compatible HTTP server mode (`/v1`)
- Multimodal via capability plugins (Voice, Vision, Music)

```bash
pip install abstractcore
```

```python
from abstractcore import create_llm

llm = create_llm("ollama", model="qwen3:4b-instruct")
resp = llm.generate("Explain durable execution in 3 bullets.")
print(resp.content)
```

AbstractCore gives you one interface for provider switching, tools, structured output, and media — as a Python SDK or via `/v1` for any OpenAI-compatible client.

### 2) AbstractGateway — durable run control plane (HTTP/SSE APIs)

Start here if you're building persistent AI applications — agents that run for hours, workflows that survive crashes, scheduled tasks. The gateway is your AI control plane: durable runs with ledger replay/streaming and thin clients that can attach/detach across devices.

- Durable execution that survives crashes and restarts
- Append-only ledger (replay-first) for auditability
- Scheduled workflows (cron-style, recurring)
- Multi-client: terminal, browser, tray, Telegram, email
- Start on one device, continue on another

```bash
pip install abstractgateway

export ABSTRACTGATEWAY_USER_AUTH=1
export ABSTRACTGATEWAY_ALLOWED_ORIGINS="http://localhost:*,http://127.0.0.1:*"
export ABSTRACTGATEWAY_WORKFLOW_SOURCE=bundle
export ABSTRACTGATEWAY_FLOWS_DIR="$PWD/bundles"
export ABSTRACTGATEWAY_DATA_DIR="$PWD/runtime/gateway"

abstractgateway serve --host 127.0.0.1 --port 8080
```

On first local start, Gateway creates `default/admin`, writes the browser-login
token to `runtime/gateway/auth/bootstrap-admin-token`, and prints the token in
the terminal. Use that `admin` user token in `/console`, AbstractFlow,
AbstractCode Web, or AbstractObserver. `ABSTRACTGATEWAY_AUTH_TOKEN` remains a
legacy server/operator bearer token; it is not a browser sign-in token.

Monitor runs from a browser:

```bash
npx @abstractframework/observer   # open http://localhost:3001
```

For artifact and runtime-resource investigation, see
`docs/guide/runtime-artifacts.md`.

---

## Author once, run everywhere (AbstractFlow)

AbstractFlow lets you author complex agentic orchestration as portable `.flow` bundles:

1. Open the Flow Editor (`npx @abstractframework/flow`)
2. Build a workflow: LLM steps, tool steps, branching, loops, subflows
3. Export a `.flow` bundle and copy it to `ABSTRACTGATEWAY_FLOWS_DIR`
4. Run it from any gateway-backed client (Observer, AbstractAssistant, Code Web UI, your app)

**AbstractAgent** provides ready-made agent patterns (ReAct, CodeAct, MemAct) that can be used inside flows or standalone.

---

## Monitor and schedule with AbstractObserver

- **Observe**: replay the full ledger of any run, or watch one live over SSE
- **Control**: cancel, resume, or inspect runs from the browser
- **Schedule**: durable schedules (cron-style) owned by the gateway — they survive restarts

---

## Example apps

| App | What it does | Install |
|---|---|---|
| **AbstractCode** | Terminal agentic dev client — durable sessions, tool approvals, `/workflow` support | `pip install abstractcode` |
| **AbstractAssistant** | macOS tray client — gateway-first, workflow picker per session, voice support | `pip install abstractassistant` |
| **AbstractObserver** | Browser UI — monitor, control, and schedule gateway runs | `npx @abstractframework/observer` |
| **Code Web UI** | Browser coding assistant (gateway-backed) | `npx @abstractframework/code` |

---

## Install the pinned ecosystem profile

### Light / Apple / GPU profiles

Choose how the framework runs based on your hardware and constraints. All profiles keep the same interfaces; they mainly change which **local inference stacks** are available.

**Light (default)** — endpoint-only inference (cloud APIs or local OpenAI-compatible servers), no in-process ML engine stacks:

```bash
pip install abstractframework
```

**Apple** — native Apple Silicon local stacks (MLX/Metal) in addition to endpoint providers:

```bash
pip install "abstractframework[apple]"
```

**GPU** — native GPU local stacks (CUDA/ROCm) in addition to endpoint providers:

```bash
pip install "abstractframework[gpu]"
```

See [docs/install.md](docs/install.md) for the full install chooser, `uv`/venv guidance,
`abstractframework doctor`, and the generated installer manifest contract.

---

## Documentation

| Page | What it covers |
|---|---|
| [docs/README.md](docs/README.md) | Documentation hub — pick your starting point |
| [docs/install.md](docs/install.md) | Light / Apple / GPU install chooser and first checks |
| [docs/getting-started.md](docs/getting-started.md) | Two entry points + first end-to-end run |
| [docs/architecture.md](docs/architecture.md) | Layered model, durable execution primitives, comparisons |
| [docs/configuration.md](docs/configuration.md) | Minimal config, where defaults live, Core vs Gateway |
| [docs/glossary.md](docs/glossary.md) | Shared terminology (run, ledger, effect, wait, bundle, …) |
| [docs/faq.md](docs/faq.md) | Common questions, comparisons, troubleshooting |
| [docs/api.md](docs/api.md) | Meta-package API (pins, helpers, re-exports) |

---

## Developer setup (from source)

Clone all sibling repos and build everything in editable mode:

```bash
./scripts/clone.sh           # clone 14 repos as siblings
source ./scripts/build.sh    # editable installs into .venv (use `source` to stay in the venv)
```

Then configure:

```bash
abstractcore --config
abstractcore --install
```

---

## License

MIT. See [LICENSE](LICENSE).

--- llms.txt ---
# AbstractFramework
> Meta-package + documentation hub for the AbstractFramework ecosystem (durable, observable agents/workflows).

This repo pins ecosystem package versions and provides a tiny helper API in `abstractframework/`.
Most runtime behavior lives in the individual package repos (AbstractCore/Runtime/Agent/Flow/Gateway/Code).

Quick commands:
- Install pinned stack: `pip install abstractframework`
- Check install health: `abstractframework doctor`
- Print installer manifest: `abstractframework manifest`
- Show detected component versions: `python -c "from abstractframework import print_status; print_status()"`

Notes for LLMs working in this repo:
- Prefer `docs/` as the ecosystem-level source of truth.
- Keep changes minimal and ecosystem-level (docs + version pins).
- If you need to change runtime behavior, you likely want the relevant package repo instead of this meta repo.
- Gateway-first hosted browser setups use Gateway user auth, opaque browser
  sessions, and `/console` for account/admin/defaults management. AbstractFlow,
  AbstractCode Web, and AbstractObserver exchange user tokens for app-scoped
  browser sessions and do not persist bearer tokens in browser settings.
  Gateway login responses do not expose session ids/CSRF tokens in JSON, and
  default resolution is Core defaults -> Gateway baseline Core config ->
  per-runtime Core config override.
  Do not treat the bootstrap `ABSTRACTGATEWAY_AUTH_TOKEN` as a browser-user
  login token. Gateway session cookies are HTTP-only for the session id, use a
  separate CSRF cookie, set `Secure` only under HTTPS, expire, logout cleanly,
  and are revoked when the backing user is disabled/deleted/token-rotated.
- Gateway user isolation is enforced through per-principal runtime routing plus
  a central route-family policy. Ordinary users can use their own runs, ledgers,
  artifacts, KG/session memory, private workflows, session artifacts,
  prompt-cache session names, and runtime-scoped Core defaults. Admin-only workspace
  helpers/import/export and provider prompt-cache controls are advertised as
  unavailable in discovery with machine-readable `admin_required` metadata.
  Deleting a user reserves the retained runtime id for that principal so the
  runtime cannot be reassigned to a different same-tenant user while data is
  still retained. Admins may explicitly transfer a retained runtime to an
  existing same-tenant user or purge the retained runtime directory before
  releasing the runtime id for reuse.
- Gateway provider connections are Gateway-owned reusable profiles for OpenAI,
  Anthropic, OpenRouter, Portkey, LM Studio, Ollama, and custom
  OpenAI-compatible endpoints. They include a description, provider family,
  base URL, optional model allowlist, and a write-only API key. Discovery
  exposes them as virtual providers such as `endpoint:office-vllm`; Runtime
  resolves the virtual id into transient provider/base URL/key parameters
  without storing secrets in workflow JSON or browser storage.
- AbstractCore also supports local single-principal provider endpoint profiles
  through `abstractcore config set-provider ...`. These profiles expose the
  same virtual provider shape (`endpoint:<id>`) for Core-only developers, while
  Gateway remains responsible for hosted user/runtime scoping.
- Gateway Console separates provider configuration from defaults: the Providers
  tab stores endpoint URLs/API keys and tests discovery; the Defaults tab only
  maps capability routes to configured virtual providers and discovered models.
  LLM/embedding model pickers use Core `capability_route` filters such as
  `input.image,output.text` and `embedding.text`; generated-media defaults use
  capability plugin catalogs.
- AbstractFlow uses the same route-aware discovery for text model selectors and
  Models Catalog nodes. Gateway `/runs/start` accepts optional `thinking`, and
  Flow LLM Call/Agent nodes can set or pin `thinking` so reasoning controls
  flow through Runtime and AbstractAgent into Core.
- Hosted Flow, Code Web, and Observer use the server-configured Gateway URL on
  non-loopback UI hostnames. Browser-supplied Gateway URL changes are rejected
  unless the app-specific remote override is enabled behind access control.
  Host checks trust `Host` by default; forwarded host headers require an
  explicit trust-proxy env setting.
- Gateway workflow sharing uses the explicit workflow catalog, not another
  user's private `/bundles` directory. Catalog bundle versions are immutable,
  admins move default pointers and ACLs, and users start catalog workflows with
  `registry_scope: "tenant_catalog"` so the run executes in their own runtime.
  Catalog scope is explicit, direct private-bundle routes reject
  catalog-internal ids, and Gateway signs catalog workflow policy before
  passing it to Runtime.

## Start Here

- [README.md](README.md): Ecosystem overview and install paths.
- [docs/README.md](docs/README.md): Docs index for this repo.
- [docs/install.md](docs/install.md): Light / Apple / GPU install chooser and generated manifest contract.
- [docs/getting-started.md](docs/getting-started.md): Practical entry paths (core, runtime, gateway, UIs, bundles).
- [docs/architecture.md](docs/architecture.md): How the stack fits together.
- [docs/glossary.md](docs/glossary.md): Shared terminology used across docs.
- [docs/adr/README.md](docs/adr/README.md): Cross-package ADR index and accepted platform contracts.

## Scenarios

- [docs/scenarios/README.md](docs/scenarios/README.md): Scenarios index.
- [docs/scenarios/offline-coding-assistant.md](docs/scenarios/offline-coding-assistant.md): Local durable coding assistant.
- [docs/scenarios/gateway-first-local-dev.md](docs/scenarios/gateway-first-local-dev.md): Local gateway + web UIs.
- [docs/scenarios/specialized-agent-flow.md](docs/scenarios/specialized-agent-flow.md): Portable `.flow` agent across clients.
- [docs/scenarios/workflow-bundle-lifecycle.md](docs/scenarios/workflow-bundle-lifecycle.md): Publish/install/deprecate bundles.
- [docs/scenarios/phone-thin-client.md](docs/scenarios/phone-thin-client.md): iPhone via Web/PWA + gateway.
- [docs/scenarios/telegram-permanent-contact.md](docs/scenarios/telegram-permanent-contact.md): Telegram bridge + workflow.
- [docs/scenarios/email-inbox-agent.md](docs/scenarios/email-inbox-agent.md): Email bridge + workflow.

## Guides

- [docs/guide/README.md](docs/guide/README.md): Guides index.
- [docs/guide/deployment-topologies.md](docs/guide/deployment-topologies.md): Supported deployment patterns.
- [docs/guide/deployment-web.md](docs/guide/deployment-web.md): Browser UIs and gateway setup.
- [docs/guide/deployment-iphone.md](docs/guide/deployment-iphone.md): iPhone/PWA notes.
- [docs/guide/gateway-security.md](docs/guide/gateway-security.md): Safe gateway exposure defaults.
- [docs/guide/capability-routing-defaults.md](docs/guide/capability-routing-defaults.md): Provider/model/base URL defaults for input, output, embedding, and rerank routes.
- [docs/guide/runtime-artifacts.md](docs/guide/runtime-artifacts.md): Runtime artifact descriptors, Gateway artifact search, Observer views, ledger/KG boundaries, and safe retrieval practice.
- [docs/guide/workflow-bundles.md](docs/guide/workflow-bundles.md): `.flow` bundles and lifecycle.
- [docs/guide/agent-skills.md](docs/guide/agent-skills.md): Agent Skills (SKILL.md) proposal and how it composes with flows.
- [docs/guide/process-manager-env-vars.md](docs/guide/process-manager-env-vars.md): Write-only env var config from Observer.

## Release Profile (Pinned Versions)

- [pyproject.toml](pyproject.toml): Single source of truth for pinned ecosystem versions and extras.
- [abstractframework/__init__.py](abstractframework/__init__.py): `RELEASE_VERSIONS`, `get_release_profile()`, `print_status()`.

## Full Context

- [llms-full.txt](llms-full.txt): Concatenated context for copy/paste into an LLM (`python scripts/gen_llms_full.py`).

## Proposals / Research (non-canonical)

- [docs/backlog/overview.md](docs/backlog/overview.md): Root backlog index and active cross-package work.
- [docs/backlog/completed/0141_flow_browser_session_gateway_auth.md](docs/backlog/completed/0141_flow_browser_session_gateway_auth.md): Completed Flow browser-session Gateway auth isolation.
- [docs/backlog/planned/0142_gateway_tenant_isolation_and_shared_runtime.md](docs/backlog/planned/0142_gateway_tenant_isolation_and_shared_runtime.md): Planned Gateway tenant isolation and shared runtime design.
- [docs/backlog/planned/0143_shared_gateway_per_principal_runtime_router.md](docs/backlog/planned/0143_shared_gateway_per_principal_runtime_router.md): In-progress shared Gateway per-principal runtime router work.
- [docs/backlog/planned/gateway-control-plane/README.md](docs/backlog/planned/gateway-control-plane/README.md): Gateway admin/config/workflow-permission control-plane backlog track.
- [docs/backlog/proposed/gateway-control-plane/README.md](docs/backlog/proposed/gateway-control-plane/README.md): Proposed Explorer/Manager package-boundary follow-ups.
- [docs/backlog/completed/0154_multi_user_security_release_blockers.md](docs/backlog/completed/0154_multi_user_security_release_blockers.md): Completed multi-user release-blocking security fixes.
- [docs/backlog/completed/0157_gateway_provider_endpoint_profiles.md](docs/backlog/completed/0157_gateway_provider_endpoint_profiles.md): Completed Gateway-owned provider endpoint profiles and virtual `endpoint:*` discovery.
- [docs/backlog/planned/0164_gateway_docker_ghcr_deployment_track.md](docs/backlog/planned/0164_gateway_docker_ghcr_deployment_track.md): Planned Gateway Docker/GHCR deployment track for light and GPU images.
- [docs/backlog/proposed/gateway-control-plane/0155_hosted_proxy_shared_helper_extraction.md](docs/backlog/proposed/gateway-control-plane/0155_hosted_proxy_shared_helper_extraction.md): Proposed threshold for extracting shared hosted proxy helpers.
- [docs/backlog/planned/074_agent_skills_integration.md](docs/backlog/planned/074_agent_skills_integration.md): Backlog item for Agent Skills integration.
- [docs/backlog/planned/074_agent_skills_integration_plan.md](docs/backlog/planned/074_agent_skills_integration_plan.md): Phased plan for Agent Skills integration.
- [docs/skills/](docs/skills/): Research notes (spec links + ecosystem scan) for Agent Skills.
- [docs/claude/](docs/claude/): Research notes about Claude (non-Agent-Skills capability/skills scan).

## Optional

- [AbstractCore](https://github.com/lpalbou/abstractcore): LLM providers, tools, structured output, media.
- [AbstractRuntime](https://github.com/lpalbou/abstractruntime): Durable execution (runs, effects, waits, ledger, stores).
- [AbstractAgent](https://github.com/lpalbou/abstractagent): Agent patterns (ReAct/CodeAct/MemAct) built on runtime + core.
- [AbstractFlow](https://github.com/lpalbou/abstractflow): npm web editor for VisualFlow authoring on top of AbstractGateway.
- [AbstractGateway](https://github.com/lpalbou/abstractgateway): HTTP/SSE control plane for gateway-first deployments.
- [AbstractCode](https://github.com/lpalbou/abstractcode): Terminal + web host UIs for durable runs.

--- pyproject.toml ---
[build-system]
requires = ["setuptools>=61.0", "wheel"]
build-backend = "setuptools.build_meta"

[project]
name = "abstractframework"
version = "0.1.11"
description = "Unified installer and documentation hub for the AbstractFramework ecosystem"
readme = "README.md"
license = {text = "MIT"}
authors = [
    {name = "Laurent-Philippe Albou", email = "contact@abstractcore.ai"}
]
keywords = [
    "agentic-os",
    "ai-agents",
    "llm",
    "autonomous-agents",
    "workflows",
    "multi-agent",
    "durable-execution",
    "knowledge-graph",
    "openai",
    "anthropic",
    "ollama",
    "local-llm"
]
classifiers = [
    "Development Status :: 4 - Beta",
    "Intended Audience :: Developers",
    "License :: OSI Approved :: MIT License",
    "Operating System :: OS Independent",
    "Programming Language :: Python :: 3",
    "Programming Language :: Python :: 3.10",
    "Programming Language :: Python :: 3.11",
    "Programming Language :: Python :: 3.12",
    "Topic :: Scientific/Engineering :: Artificial Intelligence",
    "Typing :: Typed",
]
requires-python = ">=3.10"

# Base dependencies (remote-first, lightweight).
#
# Design constraints:
# - `pip install abstractframework` should default to *remote inference* (no MLX/vLLM/Diffusers local engine stacks).
# - Hardware-local stacks are opt-in via `abstractframework[apple]` and `abstractframework[gpu]`.
# - Keep the meta-package aligned with the real per-package profiles (especially `abstractgateway`).
dependencies = [
    # Durable Gateway control plane (remote-light by default).
    "abstractgateway==0.2.28",

    # Workflow authoring UI is distributed through npm as @abstractframework/flow.
    "abstractcode==0.3.9",
    "abstractassistant==0.4.11",

    # Pin the core Python stack for release determinism (also pulled transitively).
    "abstractcore==2.13.38",
    "AbstractRuntime==0.4.29",
    "abstractagent==0.3.12",
    "AbstractMemory==0.2.6",
    "abstractsemantics==0.0.4",

    # Capability plugins are remote-first by default; local runtimes live behind
    # their package-specific `apple`/`gpu` (or `all-*`) extras.
    "abstractvoice==0.10.18",
    "abstractvision==0.3.26",
    "abstractmusic==0.1.13",
]

[project.optional-dependencies]
# NOTE: Extras here are additive. The base install already pins and installs the
# full Python ecosystem; `apple`/`gpu` select hardware-local stacks and upgrade
# apps to their matching profiles.
#
# AbstractObserver is distributed as an npm package (`npx @abstractframework/observer`),
# so it is not installable via pip extras.

# Native macOS Python installs. These are intentionally not Docker profiles:
# MLX/Metal-backed engines run on the host Apple runtime. This profile upgrades
# the stack by selecting the hardware-local dependency aggregates (owned by Gateway),
# then adds user-facing apps.
apple = [
    "abstractgateway[apple]==0.2.28",
    "abstractassistant[apple]==0.4.11; platform_system == \"Darwin\"",
]

gpu = [
    "abstractgateway[gpu]==0.2.28",
    "abstractassistant[gpu]==0.4.11",
]

[project.urls]
Homepage = "https://github.com/lpalbou/AbstractFramework"
Documentation = "https://github.com/lpalbou/AbstractFramework/tree/main/docs"
Repository = "https://github.com/lpalbou/AbstractFramework"
Issues = "https://github.com/lpalbou/AbstractFramework/issues"

[project.scripts]
abstractframework = "abstractframework.cli:main"

[tool.setuptools.packages.find]
where = ["."]
include = ["abstractframework*"]

[tool.setuptools.package-data]
abstractframework = ["py.typed"]

[tool.black]
line-length = 100
target-version = ["py310", "py311", "py312"]

[tool.ruff]
line-length = 100

[tool.ruff.lint]
select = ["E", "F", "I", "N", "W"]

[tool.mypy]
python_version = "3.10"
warn_return_any = true
warn_unused_configs = true

[tool.pytest.ini_options]
testpaths = ["tests"]

--- abstractframework/__init__.py ---
"""
AbstractFramework unified distribution package.

This package provides:
- a single install entrypoint for the full AbstractFramework ecosystem
- lightweight helpers to inspect installed component versions

Most implementation functionality still lives in component projects.
"""

from __future__ import annotations

__version__ = "0.1.11"
__author__ = "Laurent-Philippe Albou"
__license__ = "MIT"

RELEASE_VERSIONS: dict[str, str] = {
    "abstractcore": "2.13.38",
    "abstractruntime": "0.4.29",
    "abstractagent": "0.3.12",
    "abstractcode": "0.3.9",
    "abstractgateway": "0.2.28",
    "abstractmemory": "0.2.6",
    "abstractsemantics": "0.0.4",
    "abstractvoice": "0.10.18",
    "abstractvision": "0.3.26",
    "abstractmusic": "0.1.13",
    "abstractassistant": "0.4.11",
}

PACKAGE_DISTRIBUTIONS: dict[str, str] = {
    "abstractcore": "abstractcore",
    "abstractruntime": "AbstractRuntime",
    "abstractagent": "abstractagent",
    "abstractcode": "abstractcode",
    "abstractgateway": "abstractgateway",
    "abstractmemory": "AbstractMemory",
    "abstractsemantics": "abstractsemantics",
    "abstractvoice": "abstractvoice",
    "abstractvision": "abstractvision",
    "abstractmusic": "abstractmusic",
    "abstractassistant": "abstractassistant",
}

NPM_RELEASE_VERSIONS: dict[str, str] = {
    "@abstractframework/flow": "0.3.19",
    "@abstractframework/code": "0.3.9",
    "@abstractframework/observer": "0.1.11",
}

CORE_DEFAULT_EXTRAS = [
    "remote",
    "tools",
    "media",
    "vision",
    "voice",
    "audio",
    "music",
]

# Convenience re-exports (AbstractCore is a base dependency of this meta-package).
# Keep this import lightweight: do not import optional tool/media deps here.
try:
    from abstractcore import GenerateResponse, create_llm  # type: ignore

    __all__ = [
        "CORE_DEFAULT_EXTRAS",
        "RELEASE_VERSIONS",
        "GenerateResponse",
        "create_llm",
        "get_installed_packages",
        "get_release_profile",
        "print_status",
    ]
except Exception:  # pragma: no cover
    __all__ = [
        "CORE_DEFAULT_EXTRAS",
        "RELEASE_VERSIONS",
        "get_installed_packages",
        "get_release_profile",
        "print_status",
    ]


def get_release_profile() -> dict[str, object]:
    """Return the pinned global release profile shipped by this package."""

    return {
        "abstractframework": __version__,
        "packages": RELEASE_VERSIONS.copy(),
        "distributions": PACKAGE_DISTRIBUTIONS.copy(),
        "npm_packages": NPM_RELEASE_VERSIONS.copy(),
        "core_extras": list(CORE_DEFAULT_EXTRAS),
        "install_profiles": {
            "light": "pip install abstractframework",
            "apple": 'pip install "abstractframework[apple]"',
            "gpu": 'pip install "abstractframework[gpu]"',
        },
    }


def get_installed_packages() -> dict[str, str]:
    """Return a dict of installed AbstractFramework Python packages and versions."""

    packages: dict[str, str] = {}

    def _maybe_add(import_name: str) -> None:
        try:
            mod = __import__(import_name)
            packages[import_name] = getattr(mod, "__version__", "installed")
        except Exception:
            return

    for name in PACKAGE_DISTRIBUTIONS:
        _maybe_add(name)

    return packages


def print_status() -> None:
    """Print installation status of the main AbstractFramework Python packages."""

    installed = get_installed_packages()
    all_packages = list(PACKAGE_DISTRIBUTIONS)

    print("AbstractFramework installation status")
    print("=" * 40)

    for pkg in all_packages:
        if pkg in installed:
            print(f"  ✓ {pkg}: {installed[pkg]}")
        else:
            print(f"  ✗ {pkg}: not installed")

    print("")
    print(f"Installed: {len(installed)}/{len(all_packages)} packages")

    if len(installed) < len(all_packages):
        print("")
        print("To install the framework profile:")
        print("  pip install abstractframework")
        print("")
        print("Hardware-local profiles:")
        print('  pip install "abstractframework[apple]"')
        print('  pip install "abstractframework[gpu]"')

--- abstractframework/install_manifest.py ---
"""Generated install manifest helpers for AbstractFramework."""

from __future__ import annotations

import json
from pathlib import Path
from typing import Any

from . import NPM_RELEASE_VERSIONS, PACKAGE_DISTRIBUTIONS, RELEASE_VERSIONS, __version__

MANIFEST_SCHEMA_VERSION = 1
MINIMUM_INSTALLER_VERSION = "0.1.0"


def _python_packages() -> list[dict[str, str]]:
    return [
        {
            "id": package_id,
            "distribution": PACKAGE_DISTRIBUTIONS[package_id],
            "version": version,
            "registry": "pypi",
        }
        for package_id, version in RELEASE_VERSIONS.items()
    ]


def _npm_apps() -> list[dict[str, str]]:
    return [
        {
            "id": package_name.rsplit("/", 1)[-1],
            "package": package_name,
            "version": version,
            "registry": "npm",
            "command": f"npx {package_name}",
        }
        for package_name, version in NPM_RELEASE_VERSIONS.items()
    ]


def build_install_manifest() -> dict[str, Any]:
    """Build the installer-consumable manifest from root release pins."""

    return {
        "schema_version": MANIFEST_SCHEMA_VERSION,
        "minimum_installer_version": MINIMUM_INSTALLER_VERSION,
        "framework": {
            "id": "abstractframework",
            "name": "AbstractFramework",
            "distribution": "abstractframework",
            "version": __version__,
            "registry": "pypi",
            "python_requires": ">=3.10",
        },
        "source": {
            "repository": "https://github.com/lpalbou/AbstractFramework",
            "release_profile": "abstractframework.RELEASE_VERSIONS",
        },
        "profiles": [
            {
                "id": "light",
                "name": "Light",
                "summary": (
                    "Remote-first install. Full framework functionality is available through "
                    "remote or OpenAI-compatible endpoints; no local MLX, CUDA, Diffusers, or "
                    "model-runtime stacks are installed by this profile."
                ),
                "pip_requirements": [f"abstractframework=={__version__}"],
                "local_inference": False,
                "platforms": ["macos", "linux", "windows"],
                "prerequisites": ["python>=3.10", "network"],
                "best_for": [
                    "cloud APIs",
                    "LM Studio, Ollama, vLLM, llama.cpp, or other endpoint servers",
                    "lowest-friction install",
                ],
                "excludes": ["local MLX engines", "local CUDA/ROCm engines"],
            },
            {
                "id": "apple",
                "name": "Apple",
                "summary": (
                    "Native Apple Silicon profile. Adds local MLX/Metal-capable stacks on top "
                    "of the same framework interfaces and endpoint providers."
                ),
                "pip_requirements": [f"abstractframework[apple]=={__version__}"],
                "local_inference": True,
                "platforms": ["macos"],
                "prerequisites": ["python>=3.10", "apple-silicon", "network"],
                "best_for": ["Mac users who want local Apple Silicon inferencers"],
                "excludes": ["CUDA/ROCm engines"],
            },
            {
                "id": "gpu",
                "name": "GPU",
                "summary": (
                    "Native GPU profile. Adds CUDA/ROCm-oriented local stacks on top of the "
                    "same framework interfaces and endpoint providers."
                ),
                "pip_requirements": [f"abstractframework[gpu]=={__version__}"],
                "local_inference": True,
                "platforms": ["linux", "windows"],
                "prerequisites": ["python>=3.10", "gpu-driver", "network"],
                "best_for": ["workstations or servers with supported discrete GPUs"],
                "excludes": ["Apple MLX-only engines"],
            },
        ],
        "python_packages": _python_packages(),
        "npm_apps": _npm_apps(),
        "post_install": {
            "doctor": ["abstractframework", "doctor"],
            "core_config": ["abstractcore", "--config"],
            "gateway_flow": {
                "gateway": ["abstractgateway", "serve"],
                "flow": ["npx", "@abstractframework/flow"],
            },
        },
        "security": {
            "secrets_in_manifest": False,
            "native_artifacts_signed": False,
            "notes": "Prototype manifest; production installer artifacts must add signatures.",
        },
    }


def manifest_json(indent: int = 2) -> str:
    """Return the install manifest as stable JSON."""

    return json.dumps(build_install_manifest(), indent=indent, sort_keys=True) + "\n"


def write_install_manifest(path: str | Path) -> None:
    """Write the generated install manifest to a path."""

    Path(path).write_text(manifest_json(), encoding="utf-8")


def check_install_manifest(path: str | Path) -> tuple[bool, str]:
    """Compare a checked-in manifest file with the generated manifest."""

    manifest_path = Path(path)
    expected = manifest_json()
    actual = manifest_path.read_text(encoding="utf-8")
    if actual == expected:
        return True, f"{manifest_path} is up to date"
    return False, f"{manifest_path} differs from generated AbstractFramework install manifest"

--- abstractframework/cli.py ---
"""Command line helpers for the AbstractFramework meta-package."""

from __future__ import annotations

import argparse
import importlib.metadata
import json
import platform
import shutil
import subprocess
import sys
from dataclasses import dataclass
from pathlib import Path
from typing import Sequence

from . import PACKAGE_DISTRIBUTIONS, RELEASE_VERSIONS, __version__
from .install_manifest import check_install_manifest, manifest_json, write_install_manifest


@dataclass(frozen=True)
class Check:
    id: str
    status: str
    message: str
    detail: str | None = None

    def as_dict(self) -> dict[str, str]:
        data = {"id": self.id, "status": self.status, "message": self.message}
        if self.detail:
            data["detail"] = self.detail
        return data


def _distribution_version(distribution: str) -> str | None:
    try:
        return importlib.metadata.version(distribution)
    except importlib.metadata.PackageNotFoundError:
        return None


def _command_version(command: str) -> str | None:
    executable = shutil.which(command)
    if not executable:
        return None
    try:
        result = subprocess.run(
            [executable, "--version"],
            check=False,
            capture_output=True,
            text=True,
            timeout=5,
        )
    except Exception:
        return "available"
    text = (result.stdout or result.stderr).strip().splitlines()
    return text[0] if text else "available"


def build_doctor_report(include_environment: bool = True) -> dict[str, object]:
    """Return a doctor report without importing heavy local inference stacks."""

    checks: list[Check] = []

    python_version = ".".join(str(part) for part in sys.version_info[:3])
    if sys.version_info >= (3, 10):
        checks.append(Check("python", "ok", f"Python {python_version} satisfies >=3.10"))
    else:
        checks.append(Check("python", "error", f"Python {python_version} is below required >=3.10"))

    installed_framework = _distribution_version("abstractframework")
    if installed_framework in {None, __version__}:
        status = "ok" if installed_framework == __version__ else "warn"
        message = (
            f"abstractframework {installed_framework} matches release profile"
            if installed_framework
            else "abstractframework distribution metadata is not installed"
        )
        checks.append(Check("abstractframework", status, message))
    else:
        checks.append(
            Check(
                "abstractframework",
                "error",
                f"abstractframework {installed_framework} does not match {__version__}",
            )
        )

    for package_id, expected in RELEASE_VERSIONS.items():
        distribution = PACKAGE_DISTRIBUTIONS[package_id]
        actual = _distribution_version(distribution)
        if actual is None:
            checks.append(
                Check(
                    f"package:{package_id}",
                    "error",
                    f"{distribution} is not installed",
                    f"Expected {distribution}=={expected}",
                )
            )
        elif actual == expected:
            checks.append(Check(f"package:{package_id}", "ok", f"{distribution}=={actual}"))
        else:
            checks.append(
                Check(
                    f"package:{package_id}",
                    "error",
                    f"{distribution}=={actual} does not match pinned {expected}",
                )
            )

    if include_environment:
        node_version = _command_version("node")
        npm_version = _command_version("npm")
        if node_version:
            checks.append(Check("node", "ok", f"Node is available: {node_version}"))
        else:
            checks.append(Check("node", "warn", "Node is not available; browser UIs need Node/npm"))
        if npm_version:
            checks.append(Check("npm", "ok", f"npm is available: {npm_version}"))
        else:
            checks.append(Check("npm", "warn", "npm is not available; browser UIs need npm/npx"))

        system = platform.system()
        machine = platform.machine().lower()
        if system == "Darwin" and machine in {"arm64", "aarch64"}:
            checks.append(Check("hardware:apple", "ok", "Apple Silicon local profile can be used"))
        elif system == "Darwin":
            checks.append(
                Check("hardware:apple", "warn", "Apple local profile expects Apple Silicon")
            )
        else:
            checks.append(Check("hardware:apple", "warn", "Apple local profile is macOS-only"))

        if shutil.which("nvidia-smi"):
            checks.append(Check("hardware:gpu", "ok", "nvidia-smi is available"))
        else:
            checks.append(
                Check(
                    "hardware:gpu",
                    "warn",
                    "No nvidia-smi found; GPU profile may still work with another supported stack",
                )
            )

    status_rank = {"error": 2, "warn": 1, "ok": 0}
    worst = max((status_rank[check.status] for check in checks), default=0)
    status = "error" if worst == 2 else "warn" if worst == 1 else "ok"
    return {
        "abstractframework": __version__,
        "status": status,
        "checks": [check.as_dict() for check in checks],
    }


def _print_doctor(report: dict[str, object]) -> None:
    print(f"AbstractFramework doctor ({report['status']})")
    print("=" * 40)
    for raw in report["checks"]:  # type: ignore[index]
        check = raw  # type: ignore[assignment]
        marker = {"ok": "OK", "warn": "WARN", "error": "ERROR"}[check["status"]]
        print(f"[{marker}] {check['message']}")
        if check.get("detail"):
            print(f"       {check['detail']}")


def _doctor(args: argparse.Namespace) -> int:
    report = build_doctor_report(include_environment=not args.no_environment)
    if args.json:
        print(json.dumps(report, indent=2, sort_keys=True))
    else:
        _print_doctor(report)
    return 1 if report["status"] == "error" else 0


def _manifest(args: argparse.Namespace) -> int:
    if args.write:
        write_install_manifest(args.write)
        print(f"Wrote {args.write}")
        return 0
    if args.check:
        ok, message = check_install_manifest(args.check)
        print(message)
        return 0 if ok else 1
    print(manifest_json(), end="")
    return 0


def main(argv: Sequence[str] | None = None) -> int:
    parser = argparse.ArgumentParser(prog="abstractframework")
    subparsers = parser.add_subparsers(dest="command")

    doctor = subparsers.add_parser("doctor", help="Check install health and profile consistency")
    doctor.add_argument("--json", action="store_true", help="Emit machine-readable JSON")
    doctor.add_argument(
        "--no-environment",
        action="store_true",
        help="Skip Node/npm/hardware probes and only check Python package profile consistency",
    )
    doctor.set_defaults(func=_doctor)

    manifest = subparsers.add_parser("manifest", help="Print or validate the install manifest")
    manifest.add_argument("--write", type=Path, help="Write the generated manifest to a path")
    manifest.add_argument("--check", type=Path, help="Check a manifest file against the generator")
    manifest.set_defaults(func=_manifest)

    args = parser.parse_args(argv)
    if not hasattr(args, "func"):
        parser.print_help()
        return 0
    return args.func(args)


if __name__ == "__main__":  # pragma: no cover
    raise SystemExit(main())

--- docs/README.md ---
# AbstractFramework documentation

**Write once. Generate everything.**

A modular, open-source ecosystem for building **durable, observable, multimodal** AI systems. Text, voice, image, video, music — one unified interface, any provider, any model, local or cloud.

This doc set focuses on two things:

1. **How to pick the right entry point** (AbstractCore SDK vs AbstractGateway control plane)
2. **How the pieces compose** (Core → Runtime → Gateway → Flow → Observer)

Most implementation lives in component repositories. This repo ships the `abstractframework` meta-package (a pinned install profile) and the cross-package docs you're reading now.

---

## Start here

### Choose your entry point

Start lightweight with just the LLM library, or go all-in with a production gateway. Both paths lead to the same ecosystem.

### AbstractCore (SDK + optional `/v1`)

Start with **AbstractCore**:

- 9+ providers with identical API (local + cloud)
- Universal tool calling, structured output, streaming
- Media handling (images, PDFs, audio, video)
- OpenAI-compatible HTTP server mode (`/v1`)
- Multimodal via capability plugins (Voice, Vision, Music)

Read **[Getting Started](getting-started.md)** → "Core-first" section.

### AbstractGateway (durable control plane)

Start with **AbstractGateway** + **AbstractFlow**:

- Durable execution that survives crashes and restarts
- Append-only ledger (replay-first) for auditability
- Scheduled workflows (cron-style, recurring)
- Multi-client: terminal, browser, tray, Telegram, email
- Start on one device, continue on another

Read **[Getting Started](getting-started.md)** → "Gateway-first" section.

---

## How the pieces fit (one picture)

```
Authoring                                Operations
──────────────────────────────────────────────────────────────

 AbstractFlow (author workflows)      AbstractGateway (control plane)
 + Flow Editor (web UI)               - run control + scheduling
 - exports .flow bundles              - bundle discovery + SSE ledger
               │                                │
               └─────────────┬──────────────────┘
                             ▼
          AbstractAgent (ReAct / CodeAct / MemAct)
                             │
                             ▼
                  AbstractRuntime (durable kernel)
                  - runs, effects, waits
                  - ledger + artifacts
                             │
                             ▼
                  AbstractCore (LLM + tools + media)
                  - provider abstraction + routing defaults
                  - capability plugins: voice / vision / music
```

---

## Doc map

| Page | What it covers |
|---|---|
| **[Install](install.md)** | Light / Apple / GPU install chooser, `abstractframework doctor`, and installer manifest contract |
| **[Getting Started](getting-started.md)** | The two entry points + first end-to-end run |
| **[Architecture](architecture.md)** | Layered model, durable execution primitives, honest comparisons |
| **[Configuration](configuration.md)** | Minimal config, where defaults live, Core vs Gateway |
| **[Glossary](glossary.md)** | Shared terminology (run, ledger, effect, wait, bundle, …) |
| **[ADR index](adr/README.md)** | Cross-package architectural decisions and accepted platform contracts |
| **[FAQ](faq.md)** | Common questions, troubleshooting, comparisons |
| **[API](api.md)** | The `abstractframework` meta-package API (pins + helpers + re-exports) |
| **[Runtime artifacts and retrieval](guide/runtime-artifacts.md)** | Runtime, Gateway, Observer, ledger, and KG responsibility map for artifact/retrieval work |

---

## Example apps

| App | What it does |
|---|---|
| **AbstractCode** | Terminal agentic dev client (local, durable sessions) |
| **AbstractAssistant** | macOS tray client (gateway-first, workflow picker, voice) |
| **AbstractObserver** | Browser UI to monitor, control, and schedule gateway runs |
| **Code Web UI** | Browser coding assistant (gateway-backed) |

---

## More docs

| Folder | What's inside |
|---|---|
| [docs/guide/](guide/) | Focused "how it works" notes |
| [docs/scenarios/](scenarios/) | End-to-end walkthroughs by use case |
| [docs/installers/](installers/) | Installer strategy, manifest contract, and prototype history |
| [docs/comparisons/](comparisons/) | Trade-offs vs other frameworks |

--- docs/install.md ---
# Install AbstractFramework

Choose the install path by deciding where inference should run. The framework APIs stay the same
across profiles; the profiles mainly change whether local inference engines are installed.

## Quick chooser

| Profile | Command | Use when | Local inference stacks |
|---|---|---|---|
| Light | `pip install abstractframework` | You use cloud APIs or endpoint servers such as LM Studio, Ollama, vLLM, llama.cpp, OpenRouter, or OpenAI-compatible services. | No |
| Apple | `pip install "abstractframework[apple]"` | You are on Apple Silicon and want local MLX/Metal-capable engines as well as endpoint providers. | Yes, Apple-focused |
| GPU | `pip install "abstractframework[gpu]"` | You have a supported discrete GPU and want local GPU-capable engines as well as endpoint providers. | Yes, GPU-focused |

Light is not a reduced-functionality framework. It is the remote-first profile: multimodal input,
multimodal output, embeddings, tools, durable runs, workflows, and Gateway/Flow still work when
they are backed by remote or local endpoint providers.

## Recommended technical install

Use a clean virtual environment:

```bash
python3 -m venv .venv
source .venv/bin/activate
python -m pip install -U pip
python -m pip install abstractframework
abstractframework doctor
```

Use `uv` if you prefer faster environment management:

```bash
uv venv
source .venv/bin/activate
uv pip install abstractframework
abstractframework doctor
```

`pipx` is useful for isolated command-line apps, but a normal venv is usually clearer for the full
framework because Gateway, Flow, Core, and local plugins share one environment.

## Light profile

```bash
pip install abstractframework
```

Choose Light when:

- you use OpenAI, Anthropic, OpenRouter, Portkey, or other hosted providers;
- you use local model servers through HTTP, such as LM Studio, Ollama, vLLM, llama.cpp, or LocalAI;
- you want the smallest and least surprising install;
- you do not want pip to install MLX, CUDA, Diffusers, or local model-runtime stacks.

After install:

```bash
abstractframework doctor
abstractcore --config
```

## Apple profile

```bash
pip install "abstractframework[apple]"
```

Choose Apple when:

- you are on Apple Silicon;
- you want local Apple/MLX-capable inferencers in addition to endpoint providers;
- you accept larger downloads and platform-specific native dependencies.

Run:

```bash
abstractframework doctor
abstractcore --config
```

## GPU profile

```bash
pip install "abstractframework[gpu]"
```

Choose GPU when:

- you have a supported GPU stack and drivers;
- you want local GPU-capable inferencers in addition to endpoint providers;
- you accept larger downloads and platform-specific native dependencies.

Run:

```bash
abstractframework doctor
abstractcore --config
```

## Start Gateway and Flow

For a local development setup, start Gateway and Flow from their package commands or from the
workspace helper scripts when working from source. The first health check should always be:

```bash
abstractframework doctor
```

Then configure providers:

```bash
abstractcore --config
```

Gateway-hosted browser apps use Gateway user tokens and browser sessions. Do not use the bootstrap
server token as a browser login token.

## Container deployment

For a server/VPS deployment, prefer the Gateway container rather than installing every app package
on the host:

```bash
docker run \
  -p 8080:8080 \
  -v "$PWD/runtime:/data" \
  -e ABSTRACTGATEWAY_DATA_DIR=/data \
  -e ABSTRACTGATEWAY_USER_AUTH=1 \
  ghcr.io/lpalbou/abstractgateway:latest
```

This is the Light container: full framework capabilities through remote/endpoint inference, without
local MLX/CUDA stacks. On first start it creates `default/admin` and writes the login token to
`runtime/auth/bootstrap-admin-token`. Use `ghcr.io/lpalbou/abstractgateway:gpu-latest` only on an
NVIDIA host when you explicitly want the local GPU profile.

## Non-technical installs

Native GUI installers are moving to the standalone
[`AbstractInstallers`](https://github.com/lpalbou/AbstractInstallers) repository. Until signed
installer artifacts are published, the supported production path is the Python install profile
described above.

## Generated install manifest

The installer-facing contract is generated from the root release profile:

```bash
abstractframework manifest
abstractframework manifest --check docs/installers/install-manifest.json
```

Installers should consume this manifest instead of maintaining independent package pins.

--- docs/getting-started.md ---
# Getting started

This guide helps you build a correct mental model quickly, then run something end-to-end.

> **Write once. Generate everything.** Durable, observable, multimodal AI systems — one unified interface, any provider, any model, local or cloud.

AbstractFramework is a **stack**:

| Layer | Package | Role |
|---|---|---|
| SDK | **AbstractCore** | Provider/model abstraction, tools, structured output, media, embeddings |
| Agent patterns | **AbstractAgent** | Ready-made loops: ReAct (tool-first), CodeAct (code execution), MemAct (memory-enhanced) |
| Workflow authoring | **AbstractFlow** | Visual editor, portable `.flow` bundles, subflows |
| Durable kernel | **AbstractRuntime** | Runs, effects, waits, ledger, artifacts |
| Control plane | **AbstractGateway** | Persistence, scheduling, bundle discovery, SSE streaming |
| Operations | **AbstractObserver** | Browser UI to monitor, control, and schedule runs |

**Rule of thumb**: start with **Core** when you want a lightweight LLM library (SDK or `/v1`) for scripts/notebooks/apps; add **Gateway** when you need persistent runs, scheduling, and multi-client continuity.

> **Prerequisites**: Python 3.10+. Node.js 18+ (only for browser UIs). An LLM backend — local (Ollama, LM Studio, vLLM, llama.cpp) or cloud (OpenAI, Anthropic, etc.).

---

## Choose your entry point

| If you are… | Start with | Why |
|---|---|---|
| Calling LLMs/tools/media (SDK or OpenAI-compatible `/v1`) | **AbstractCore** | Lightweight, smallest surface area, fastest feedback loop |
| Building persistent agents/workflows (durable runs) | **AbstractGateway** + **AbstractFlow** | Durability, scheduling, bundle discovery, ledger replay/streaming |

You can also install the entire pinned ecosystem in one command:

```bash
pip install abstractframework
```

For the full Light / Apple / GPU profile chooser, see [Install AbstractFramework](install.md).

---

## Core-first: integrate via AbstractCore (SDK or `/v1`)

### 1. Install

```bash
pip install abstractcore
```

### 2. Configure a provider

**Local (Ollama)** — free, no API key:

```bash
ollama serve
ollama pull qwen3:4b-instruct
export OLLAMA_HOST="http://localhost:11434"
```

**OpenAI-compatible** (LM Studio, vLLM, LocalAI, llama.cpp):

```bash
export OPENAI_BASE_URL="http://127.0.0.1:1234/v1"
export OPENAI_API_KEY="local"
```

**Cloud APIs**:

```bash
export OPENAI_API_KEY="sk-..."
export ANTHROPIC_API_KEY="sk-ant-..."
```

**Or use the interactive wizard** (persists config to `~/.abstractcore/config/`):

```bash
abstractcore --config
abstractcore --status
```

### 3. Call the model

```python
from abstractcore import create_llm

llm = create_llm("ollama", model="qwen3:4b-instruct")
resp = llm.generate("Explain durable execution in 3 bullets.")
print(resp.content)
```

### What else you can do with Core

```python
# Tool calling
resp = llm.generate("What's the weather?", tools=[get_weather])

# Structured output (Pydantic)
report = llm.generate("Analyze this.", response_model=Report)

# Media input (images, audio, video, documents)
resp = llm.generate("Describe this image.", media=["photo.jpg"])

# Embeddings
vectors = llm.embed(["first document", "second document"])

# Streaming
for chunk in llm.generate("Write a poem.", stream=True):
    print(chunk.content or "", end="", flush=True)
```

---

## Gateway-first: durable runs + monitoring + scheduling

### 1. Install

```bash
pip install abstractgateway
```

### 2. Configure

```bash
export ABSTRACTGATEWAY_USER_AUTH=1
export ABSTRACTGATEWAY_ALLOWED_ORIGINS="http://localhost:*,http://127.0.0.1:*"
export ABSTRACTGATEWAY_WORKFLOW_SOURCE=bundle
export ABSTRACTGATEWAY_DATA_DIR="$PWD/runtime/gateway"

# Optional: set only for a custom bundle registry. Packaged Gateway includes
# the shipped basic-agent bundle when this is unset.
# export ABSTRACTGATEWAY_FLOWS_DIR="$PWD/bundles"
```

### 3. Start the gateway

```bash
abstractgateway serve --host 127.0.0.1 --port 8080
```

On first local start, Gateway creates `default/admin`, writes the browser-login
token to `runtime/gateway/auth/bootstrap-admin-token`, and prints it in the
terminal. Use that token with user `admin` in `/console`, AbstractFlow,
AbstractCode Web, or AbstractObserver. `ABSTRACTGATEWAY_AUTH_TOKEN` is only the
legacy server/operator bearer-token path; it does not sign in browsers.

Verify:

```bash
curl -sS "http://127.0.0.1:8080/api/health"
```

### 4. Monitor runs with AbstractObserver

In another terminal:

```bash
npx @abstractframework/observer
```

Open http://localhost:3001 and connect. In hosted user-auth mode, enter Gateway
URL, Gateway user, and that user's token; Observer exchanges the token for a
browser session and does not persist the token in browser settings.

AbstractObserver is replay-first: it renders runs by replaying the ledger, then streams new steps live via SSE.

### 5. Schedule recurring work

Schedules are owned by the gateway (they survive restarts):

```bash
curl -X POST "http://127.0.0.1:8080/api/gateway/runs/schedule" \
  -H "Authorization: Bearer $(cat "$ABSTRACTGATEWAY_DATA_DIR/auth/bootstrap-admin-token")" \
  -H "Content-Type: application/json" \
  -d '{"bundle_id":"my-bundle","flow_id":"my-entrypoint","start_at":"now","interval":"24h"}'
```

---

## Author orchestration with AbstractFlow

The ecosystem's distribution unit is a **workflow bundle** (`.flow` file): a VisualFlow graph + metadata. Gateways discover bundles and expose them to all clients.

### 1. Open the Flow Editor

With the gateway running:

```bash
npx @abstractframework/flow
```

Open http://localhost:3003 and connect to your gateway. In hosted user-auth
mode, use Gateway URL, Gateway user, and that user's token; Flow keeps an
opaque browser session instead of storing the token.

### 2. Build a workflow

- **On Flow Start** → takes input (prompt, provider, model, …)
- LLM steps, tool steps, branching, loops, subflows
- **On Flow End** → returns output (response, success, metadata)

To make it reusable across clients, implement an **interface contract** (for example `abstractcode.agent.v1` — a standard chat-like agent I/O contract).

### 3. Export and deploy

```bash
mkdir -p "$PWD/bundles"
cp my-agent.flow "$PWD/bundles/"
```

Start or restart Gateway with `ABSTRACTGATEWAY_FLOWS_DIR="$PWD/bundles"` when
you want that directory to be the active custom bundle registry. You can also
publish bundles through the Gateway API from AbstractFlow.

### 4. Run from any client

Once deployed, the bundle appears in:

- **AbstractObserver** — workflow picker / run launcher
- **AbstractAssistant** — workflow picker (per session)
- **Code Web UI** — workflow picker
- **Your own client** — via the gateway bundle discovery API

---

## Example apps

### AbstractCode (terminal)

A local dev client for agentic sessions — no server required:

```bash
pip install abstractcode
abstractcode --provider ollama --model qwen3:4b-instruct
```

Sessions are durable: close and reopen, your full context is preserved. Type `/help` for commands.

### AbstractAssistant (macOS tray)

Gateway-first by default. Select a workflow per session from the tray UI:

```bash
pip install abstractassistant
assistant tray
```

---

## Next steps

- **[Architecture](architecture.md)** — the layered model (Core / Runtime / Agent / Gateway / Flow / Observer)
- **[Configuration](configuration.md)** — where defaults live and how to configure them
- **[Glossary](glossary.md)** — shared terms (run, ledger, effect, wait, bundle, interface contract)
- **[FAQ](faq.md)** — comparisons, offline operation, troubleshooting

--- docs/architecture.md ---
# Architecture

AbstractFramework is open-source AI infrastructure built around one idea: **durable, observable execution** for AI workflows.

Most LLM frameworks optimize for prototyping speed. AbstractFramework optimizes for **operational reality**: workflows that pause/resume safely, runs that survive restarts, UIs that reconstruct state from history, and clear boundaries around tool execution and approvals.

---

## Choose your entry point

Start lightweight with just the LLM library, or go all-in with a production gateway. Both paths lead to the same ecosystem.

### AbstractCore (SDK + optional `/v1`)

Start here if you need a lightweight LLM library for scripts, notebooks, or existing applications. No infrastructure required — just install and call. Add multimodal capabilities with plugins as you grow.

- 9+ providers with identical API (local + cloud)
- Universal tool calling, structured output, streaming
- Media handling (images, PDFs, audio, video)
- OpenAI-compatible HTTP server mode (`/v1`)
- Multimodal via capability plugins (Voice, Vision, Music)

The right first step when you mainly care about calling models/tools/media (in-process via Python or via `/v1`) and want the smallest surface area.

### AbstractGateway (durable control plane)

Start here if you're building persistent AI applications — agents that run for hours, workflows that survive crashes, scheduled tasks. The gateway is your AI control plane: durable runs with ledger replay/streaming and thin clients that can attach/detach across devices.

- Durable execution that survives crashes and restarts
- Append-only ledger (replay-first) for auditability
- Scheduled workflows (cron-style, recurring)
- Multi-client: terminal, browser, tray, Telegram, email
- Start on one device, continue on another

The composition root when you need a control plane (local or remote).

---

## Layered view

```
Apps / UIs (thin clients)
──────────────────────────────────────────────────────
 AbstractObserver (monitor / control / schedule)
 Flow Editor (author workflows)
 Code Web UI, AbstractAssistant, your custom app
                       │  HTTP/SSE
                       ▼
AbstractGateway (control plane)
──────────────────────────────────────────────────────
 run lifecycle (start / resume / cancel)
 scheduling (durable, survives restarts)
 private bundle discovery + shared workflow catalog
 artifact + ledger serving / streaming
                       │
                       ▼
Composition layer
──────────────────────────────────────────────────────
 AbstractAgent: ReAct, CodeAct, MemAct patterns
 AbstractFlow: visual workflows → portable .flow bundles
                       │
                       ▼
Durable execution kernel
──────────────────────────────────────────────────────
 AbstractRuntime
 runs, effects, waits
 ledger (append-only history) + artifacts (large payloads)
                       │
                       ▼
LLM + tools + multimodality
──────────────────────────────────────────────────────
 AbstractCore
 provider/model abstraction + routing defaults
 tools, structured output, media input, embeddings, MCP
 capability plugins: voice / vision / music
```

**AbstractFlow** is the authoring/distribution layer: you design a VisualFlow graph, export a `.flow` bundle, and run it anywhere a compatible host exists.

**AbstractAgent** is the composition layer: ready-made agent loops (ReAct, CodeAct, MemAct) built on top of Runtime. These can be used standalone or inside a Flow as agent nodes.

---

## Durable execution primitives

These are the "why" behind the design — the properties that make the framework operationally useful.

### Run

A durable workflow instance with persisted state. Identified by a `run_id`.

### Ledger

The append-only history of a run: every step, effect, result, wait, and error is recorded.

This is what makes replay-first UIs possible: a client reconstructs state by replaying history, then follows along by streaming new events over SSE.

### Effects and waits

Work happens at explicit boundaries:

- An **effect** is a request for a side-effect (LLM call, tool call, ask user, wait-until, …).
- A **wait** is a checkpointed pause until external input arrives (tool results, user answer, a timer).

The key property: if a process dies while waiting, **the run is still correct**. Another process (or a restart) can resume it from the recorded wait.

### Artifacts

Large payloads (files, media, big tool results) are stored as **artifacts** and referenced by handle from JSON state and the ledger. This keeps state JSON-safe without losing evidence.

### Tool execution boundary

Tool **schemas** are durable (stored in the ledger). Tool **callables** are not (they live in the host process). This is intentional:

- Tool calls become explicit approval points (configurable per tool)
- Execution is auditable (arguments + results in the ledger)
- Runs remain restart-safe (the tool call is a wait, not an in-process function call)

---

## Workflow lifecycle: author → deploy → run → observe

### 1) Author with AbstractFlow

Build a workflow graph in the Flow Editor and export it as a `.flow` bundle.

### 2) Deploy the bundle

Copy bundles to `ABSTRACTGATEWAY_FLOWS_DIR`. The gateway discovers them automatically.

For hosted or multi-user gateways, admins can also promote immutable bundle
versions into the Gateway workflow catalog. The catalog owns shared/default
workflow pointers and ACLs; each catalog run still executes in the requesting
user's runtime unless a future explicit system-run mode is added.

### 3) Run from any client

Any gateway-backed client can: list bundles/entrypoints, start a run, attach to ledger replay/streaming, and resume waits (approvals, user input, tool results).

### 4) Observe and schedule with AbstractObserver

- Inspect any run (ledger replay)
- Watch a run live (SSE)
- Control runs (cancel, resume)
- Schedule recurring runs

## File-like sources in hosted clients

Hosted clients such as AbstractFlow need a clear split between file origin and
runtime value:

- `Artifact`: a saved Runtime-owned file payload. Reusable across runs.
- `Local File`: a file chosen from the client device. In hosted/browser mode it
  is uploaded and normalized into an Artifact before durable execution.
- `Server File`: a user-facing label for a file inside Gateway-approved
  workspace scope on the server. Path-based operations still depend on
  workspace policy and current grants; importing it creates an Artifact
  snapshot.

Internally, the durable payload handle is the artifact ref. Server-side path
access stays a Gateway-owned workspace capability rather than a generic server
filesystem abstraction. Current UI surfaces may still say `Workspace` in some
places until terminology alignment work lands.

---

## Multimodality

AbstractCore stays lightweight by treating modalities as **capability plugins** (discovered internally via Python entry points, then exposed via the AbstractCore SDK and its optional `/v1` endpoints; gateway-first deployments can also surface them through Gateway):

| Plugin | Capability | API surface |
|---|---|---|
| `abstractvoice` | TTS + STT | `llm.voice.tts(...)`, `llm.audio.transcribe(...)` |
| `abstractvision` | Image generation | `llm.vision.t2i(...)` |
| `abstractmusic` | Music generation | `llm.music.t2m(...)` |

Install a plugin; the API appears on your `llm` instance. Don't install it; Core stays small.

---

## How it compares (honest positioning)

### vs direct provider SDKs (OpenAI, Anthropic)

Direct SDKs are the right choice when you only use one provider and don't need durable orchestration.

AbstractCore adds value when you need: provider portability, consistent tool/structured-output behavior across backends, media policies, modality plugins, or a stable configuration layer that doesn't leak into app code.

### vs LangChain / LlamaIndex / PydanticAI

Those are primarily **in-process orchestration libraries**. AbstractFramework occupies a different niche: an **agentic OS-style** stack for durable, observable execution (runtime + append-only ledger + control plane), where the same workflows can run across providers, devices, and deployment modes.

**Where AbstractFramework is stronger**: durability and pause/resume as primitives, replay-first observability, portable `.flow` bundles that run across clients.

**Where others are stronger**: large connector/RAG ecosystems, minimal boilerplate for simple use cases, broader community examples.

### vs Temporal / Step Functions / job schedulers

AbstractGateway is architecturally closer to these systems, but specialized for LLM/tool loops: tool approval waits, AI-oriented artifacts, and replay-first thin-client UIs over HTTP/SSE.

---

## Where to go deeper

- **[Getting Started](getting-started.md)** — run Core-first or Gateway-first
- **[Configuration](configuration.md)** — minimal config, where defaults live
- **[Glossary](glossary.md)** — shared definitions (run, ledger, effect, wait, bundle, interface contract)
- Per-project architecture docs live in the component repositories

--- docs/configuration.md ---
# Configuration

This page answers two practical questions:

1. **What do I need to configure first to be productive?**
2. **Where do defaults live in a Core-first vs Gateway-first setup?**

Key principle: **Core owns model/provider defaults; Gateway owns durable execution and operations.**

---

## Core-first quick start

### Interactive wizard (recommended)

```bash
abstractcore --config      # guided setup — persists to ~/.abstractcore/config/
abstractcore --status      # show current config
abstractcore --install     # check readiness + download missing assets
```

The wizard walks through: default provider/model, base URLs, API keys, vision fallback, audio/video strategies, embeddings, and logging. Config is stored in `~/.abstractcore/config/abstractcore.json`.

### Manual environment variables

**Ollama** (local, free):

```bash
export OLLAMA_HOST="http://localhost:11434"
```

**OpenAI-compatible server** (LM Studio, vLLM, LocalAI, llama.cpp):

```bash
export OPENAI_BASE_URL="http://127.0.0.1:1234/v1"
export OPENAI_API_KEY="local"
```

**Cloud APIs**:

```bash
export OPENAI_API_KEY="sk-..."
export ANTHROPIC_API_KEY="sk-ant-..."
```

---

## Gateway-first quick start

### Required

```bash
export ABSTRACTGATEWAY_USER_AUTH=1
export ABSTRACTGATEWAY_ALLOWED_ORIGINS="http://localhost:*,http://127.0.0.1:*"
```

When user auth is enabled, `abstractgateway serve` ensures `default/admin`
exists and writes the first browser-login token to
`$ABSTRACTGATEWAY_DATA_DIR/auth/bootstrap-admin-token`. Users sign in with a
Gateway user id and that user's token, then browser apps keep only an opaque
Gateway session. `ABSTRACTGATEWAY_AUTH_TOKEN` is still available for legacy
server/operator bearer-token deployments, but it maps to `local-admin` and is
not a browser sign-in token.

Gateway serves `/console` for account/runtime summary, admin user management,
optional user email metadata, token rotation, retained runtime transfer/purge,
provider connections, and multimodal capability defaults selected from
available providers. Deleted users leave retained runtime reservations;
admins can transfer retained runtime data to an existing same-tenant user or
purge the retained runtime directory before releasing the runtime id for reuse.

### Recommended (bundle-based workflows)

```bash
export ABSTRACTGATEWAY_WORKFLOW_SOURCE=bundle
export ABSTRACTGATEWAY_DATA_DIR="$PWD/runtime/gateway"

# Optional: set only for a custom bundle registry. When unset, Gateway uses
# the packaged shipped bundle directory containing basic-agent.
# export ABSTRACTGATEWAY_FLOWS_DIR="$PWD/bundles"
```

### Start

```bash
abstractgateway serve --host 127.0.0.1 --port 8080
```

---

## Model/provider defaults (capability routes)

AbstractCore organizes defaults as **capability routes** — stable slots scoped by capability, not by application:

| Route | Meaning |
|---|---|
| `input.text` | Canonical LLM text model for understanding and generation |
| `output.text` | Read-only derived view of `input.text` |
| `input.image` | Image-understanding fallback when `input.text` is not vision-capable |
| `input.video` | Video-understanding fallback when native video/frame support is not available or should be overridden |
| `input.voice` | Speech-to-text fallback for audio attachments |
| `input.sound` | Non-speech audio/SFX understanding route, not a speech transcription route |
| `input.music` | Music-audio understanding route, not a speech transcription route |
| `embedding.text` | Default embeddings model |

### Set defaults in Core (single-host)

```bash
abstractcore config set-default input.text \
  --provider ollama \
  --model qwen3:4b-instruct

abstractcore config defaults
```

The older `abstractcore --set-global-default ...` and
`abstractcore --set-capability-default ...` flags remain supported for
compatibility. The `abstractcore config ...` form is the preferred explicit
route-default syntax.

Model discovery for LLM and embedding defaults can filter by Core route keys,
for example `capability_route=input.image,output.text`,
`capability_route=input.sound,output.text`, or
`capability_route=embedding.text`. Generated image/video/voice/sound/music
defaults use capability plugin catalogs instead, so plugin readiness and
download/setup state are not stored in `model_capabilities.json`.

AbstractFlow uses the same Gateway/Core discovery contract for text model
authoring. Text provider/model selectors ask Gateway for `output.text` models,
and the Models Catalog node can store a capability route such as
`input.image,output.text` so workflows can discover the provider's models for a
specific input/output shape at run time. Flow stores only selection intent; Core
and Gateway remain the source of truth for model capability metadata.

### Reasoning and thinking controls

AbstractCore exposes a provider-neutral `thinking` generation option for models
that support explicit reasoning controls. Gateway accepts the same field on
`POST /api/gateway/runs/start` and stores it as `_runtime.thinking` for the
run:

```json
{
  "bundle_id": "basic-agent",
  "input_data": {"prompt": "Plan the migration"},
  "thinking": "high"
}
```

Flow LLM Call and Agent nodes also expose a Reasoning selector and a `thinking`
input pin. A node setting or pin value overrides the run default for that node;
leaving it on Auto inherits the Gateway/runtime default. Core performs the
provider-specific translation or unsupported-parameter handling.

### Core provider endpoint profiles

For reusable OpenAI-compatible endpoints, configure a named Core provider
profile once and point route defaults at its virtual provider id:

```bash
export OVH_AI_API_KEY="..."

abstractcore config set-provider ovh-provider \
  --family openai-compatible \
  --base-url https://oai.endpoints.kepler.ai.cloud.ovh.net/v1 \
  --api-key $OVH_AI_API_KEY \
  --name "OVH Provider" \
  --description "OVH hosted OpenAI-compatible endpoint"

abstractcore config models ovh-provider

abstractcore config set-default input.text \
  --provider endpoint:ovh-provider \
  --model Qwen3.5-9B
```

`endpoint:ovh-provider` is the reusable public id. The URL and key stay in the
Core config file, not in exported workflows. Use
`abstractcore config providers --json` to list profiles; raw keys are not
printed.

If you want the config to store an environment reference instead of the expanded
secret value, pass the variable literally, for example `--api-key
'$OVH_AI_API_KEY'`.

### Set defaults through the Gateway (control plane)

In gateway-first deployments, set defaults via gateway tooling so the execution host stays consistent:

```bash
abstractgateway-config set-default input.text \
  --provider ollama \
  --model qwen3:4b-instruct

abstractgateway-config defaults
```

The gateway can update route defaults, but the default schema and file format
are Core-owned. When Gateway user auth is enabled, the Gateway baseline is
stored as a Core config file:

```text
$ABSTRACTGATEWAY_DATA_DIR/config/abstractcore.json
```

Per-user writes through
`/api/gateway/config/capability-defaults/{kind}/{modality}` or
`/api/gateway/config/capability-defaults/{kind}/{modality}/{task}` are stored
as a runtime-scoped Core config file:

```text
$ABSTRACTGATEWAY_DATA_DIR/users/<tenant>/<runtime>/runtime/config/abstractcore.json
```

User runtime defaults override the Gateway baseline only for that runtime, so
one user's provider/model defaults do not mutate another user's defaults.
Gateway no longer reads or writes `config/capability_defaults.json`; existing
overlay files are ignored. Recreate those defaults with
`abstractgateway-config set-default ...`.

### Gateway provider connections

Gateway Console also lets signed-in users create reusable provider connections
through a guided setup flow for OpenAI, Anthropic, OpenRouter, Portkey, LM
Studio, Ollama, and custom OpenAI-compatible endpoints. A connection stores a
display name, description, provider family, optional base URL, optional API key,
and optional advanced model allowlist. The raw API key is write-only through the
API/UI and is not returned in discovery responses. AbstractCore owns model
capability metadata, so normal setup does not ask users to classify models
manually.

The **Providers** tab is where endpoint base URLs and API keys are configured.
Its **Test** action calls the selected provider or endpoint and previews model
discovery before saving. Leaving all advanced model restrictions unselected
keeps live discovery active; selecting models stores a fixed allowlist for that
profile. The **Multimodal Capabilities** tab intentionally does not ask for a
base URL or API key: it maps each capability route to one available provider
and one discovered/allowed model. Available providers include saved Gateway
provider connections plus direct providers such as `openai` or `anthropic`
when their required key is already present in scoped Core config or process
environment. LM Studio and Ollama also appear automatically when Gateway can
reach their configured or default local endpoints and discover models from
them.

The **Sandbox** tab uses the same provider/default contract. Choose a configured
capability route, then send a short prompt. Text chat uses the selected text
default, and generated media routes such as `output.image.text_to_image`,
`output.video.text_to_video`, `output.voice`, `output.sound`, or
`output.music` run through that route's configured provider/model and return
the generated artifact link. Image edit, image upscale, and image-to-video use
separate defaults: `output.image.image_to_image`,
`output.image.image_upscale`, and `output.video.image_to_video`. Gateway
Console presents these concrete generated-media routes instead of the broad
`output.image` and `output.video` compatibility defaults.

`input.voice`, `input.video`, `input.sound`, and `input.music` are fallback gates, not hidden
package probes. If a route is unconfigured and the primary text model cannot
handle that input natively, the request fails with a configuration error rather
than silently using an installed speech, vision, audio, or music package.
`input.video`, `input.sound`, and `input.music` can be reported as covered by
`input.text` when the text model supports those inputs; operators can still
override them with dedicated routes.

After creation, the profile appears in provider discovery as a virtual provider
id such as `endpoint:office-vllm`. Use that provider id in AbstractFlow nodes or
Gateway capability defaults, then select a discovered/allowed model normally.
At run time Gateway resolves the virtual provider into the real provider family,
base URL, and key for the current runtime call; workflow JSON and exported
bundles should not contain raw secrets.

---

## Storage and persistence

### Gateway data directory

`ABSTRACTGATEWAY_DATA_DIR` is the durability root:

- Run state
- Ledger history
- Artifacts (files, media, big payloads)
- Schedules

**Back up this directory** if you care about long-lived runs and audit trails.

### Optional SQLite backend

For production deployments, you can use SQLite for run metadata (artifacts stay file-backed):

```bash
export ABSTRACTGATEWAY_STORE_BACKEND=sqlite
export ABSTRACTGATEWAY_DB_PATH="$ABSTRACTGATEWAY_DATA_DIR/gateway.sqlite3"
```

### Core config directory

`~/.abstractcore/config/` stores persisted provider keys, base URLs, and
defaults for direct Core usage (written by `abstractcore --config` or
`abstractcore config ...`). You can target another config explicitly with
`ABSTRACTCORE_CONFIG_FILE`, `ABSTRACTCORE_CONFIG_DIR`, or
`abstractcore config --config-file /path/to/abstractcore.json ...`.

---

## Client configuration (Observer / Flow Editor / Code Web UI)

All gateway-backed browser UIs need two things:

- **Gateway base URL** (example: `http://127.0.0.1:8080`)
- **Gateway user + user token** in hosted user-auth mode

Local single-user tools can still use `ABSTRACTGATEWAY_AUTH_TOKEN` as an
operator token. Hosted browser apps should exchange user tokens for Gateway
browser sessions and should not store bearer tokens in browser storage.
AbstractFlow, AbstractCode Web, and AbstractObserver use this hosted
browser-session path when you provide a Gateway user id.

When these browser UIs are served from a non-loopback hostname, the Gateway URL
comes from the UI server configuration. Browser-supplied Gateway URL changes
are rejected unless the app-specific remote override is enabled behind your own
access control.

### AbstractObserver

```bash
npx @abstractframework/observer
```

Set Gateway URL, Gateway user, and that user's token in the UI
(http://localhost:3001). Observer exchanges the token for an app-scoped browser
session and does not persist the token in browser settings.

### Flow Editor

```bash
npx @abstractframework/flow
```

Optional convenience variable:

```bash
export ABSTRACTFLOW_GATEWAY_URL="http://127.0.0.1:8080"
```

### Code Web UI

```bash
npx @abstractframework/code
```

Set the gateway URL in the UI settings (http://localhost:3002).

---

## Multimodal capability plugins

Modalities are optional and become available when installed:

| Plugin | Capability | API surface |
|---|---|---|
| `abstractvoice` | Voice | `llm.voice` / `llm.audio` |
| `abstractvision` | Images | `llm.vision` |
| `abstractmusic` | Music | `llm.music` |

**Plugins are configured on the host that actually executes** (local app or gateway host), because that's where model downloads and hardware constraints apply.

---

## Per-project configuration references

This repo covers the overview. Each component repo owns its detailed configuration surface:

- **AbstractCore**: [centralized config](https://github.com/lpalbou/abstractcore/blob/main/docs/centralized-config.md), [prerequisites](https://github.com/lpalbou/abstractcore/blob/main/docs/prerequisites.md)
- **AbstractGateway**: [configuration](https://github.com/lpalbou/AbstractGateway/blob/main/docs/configuration.md), [security](https://github.com/lpalbou/AbstractGateway/blob/main/docs/security.md)
- **AbstractVoice**: [installation](https://github.com/lpalbou/abstractvoice/blob/main/docs/installation.md), [model management](https://github.com/lpalbou/abstractvoice/blob/main/docs/model-management.md)
- **AbstractVision**: [configuration](https://github.com/lpalbou/abstractvision/blob/main/docs/reference/configuration.md), [backends](https://github.com/lpalbou/abstractvision/blob/main/docs/reference/backends.md)

---

## Related docs

- **[Getting Started](getting-started.md)** — first run Core-first or Gateway-first
- **[Architecture](architecture.md)** — what lives where and why
- **[Glossary](glossary.md)** — capability routes, durable execution terms

--- docs/api.md ---
# API (meta-package)

This page documents the API exported by `abstractframework`, the meta-package shipped by this repository.

`abstractframework` is a **pinned distribution profile** plus a few lightweight helpers. AbstractFramework has two entrypoints: **AbstractCore** (LLM SDK + optional OpenAI-compatible `/v1` server) and **AbstractGateway** (durable run control plane over HTTP/SSE). Most functional APIs live in component packages — especially **AbstractCore** for the LLM SDK.

---

## Install

Full pinned ecosystem:

```bash
pip install abstractframework
```

Only the LLM SDK:

```bash
pip install abstractcore
```

Hardware-specific profiles (native installs, not Docker):

```bash
pip install "abstractframework[apple]"       # Apple Silicon native stack (MLX/Metal)
pip install "abstractframework[gpu]"         # GPU native stack (CUDA/ROCm)
```

See [Install AbstractFramework](install.md) for the profile chooser and first health checks.

---

## Convenience re-exports

`abstractframework` re-exports two common AbstractCore entry points so simple scripts can `from abstractframework import ...` without a separate `abstractcore` import.

### `create_llm`

```python
from abstractframework import create_llm

llm = create_llm("ollama", model="qwen3:4b-instruct")
resp = llm.generate("hello")
print(resp.content)
```

### `GenerateResponse`

The response type returned by `llm.generate(...)`.

---

## Release profile helpers

### `RELEASE_VERSIONS`

Dictionary mapping each ecosystem package name to the pinned version for this release.

### `CORE_DEFAULT_EXTRAS`

List of AbstractCore extras implied by the default framework install profile (remote-first): `remote`, `tools`, `media`, `vision`, `voice`, `audio`, `music`.

### `get_release_profile()`

Returns the full pinned profile metadata as a dict.

```python
from abstractframework import get_release_profile

profile = get_release_profile()
print(profile["abstractframework"])        # meta-package version
print(profile["packages"]["abstractcore"]) # pinned Core version
```

### `get_installed_packages()`

Returns a dict of installed AbstractFramework package versions detected in the current environment.

```python
from abstractframework import get_installed_packages
print(get_installed_packages())
```

### `print_status()`

Prints a human-readable status report of detected packages (installed vs missing).

```python
from abstractframework import print_status
print_status()
```

### `abstractframework doctor`

Checks Python version, pinned package versions, Node/npm availability for browser UIs, and local
hardware indicators for Apple/GPU profiles. It does not import heavy local inference stacks.

```bash
abstractframework doctor
abstractframework doctor --json
```

### `abstractframework manifest`

Prints or validates the installer-facing manifest generated from the root release profile.

```bash
abstractframework manifest
abstractframework manifest --check docs/installers/install-manifest.json
```

---

## Where to find the functional APIs

| What you need | Package |
|---|---|
| LLM calls, tools, structured output, media, embeddings, MCP | `abstractcore` |
| Durable execution kernel (runs, ledger, effects, waits) | `abstractruntime` |
| Agent patterns (ReAct, CodeAct, MemAct) | `abstractagent` |
| Control plane (HTTP server, scheduling, bundle discovery, SSE) | `abstractgateway` |
| Workflow authoring UI | `@abstractframework/flow` (npm) |
| Monitoring / operations UI | `@abstractframework/observer` (npm) |

See **[Getting Started](getting-started.md)** for the two entry points and a first end-to-end run.

Gateway-hosted workflow APIs distinguish private runtime bundles from the
shared workflow catalog:

- `/api/gateway/bundles` remains the caller runtime's private bundle surface.
- `/api/gateway/workflow-catalog` lists catalog workflows visible to the signed
  in principal.
- `/api/gateway/admin/workflow-catalog/*` is admin-only for immutable catalog
  upload/promote/default/ACL/status operations.
- `/api/gateway/runs/start` and `/api/gateway/runs/schedule` accept
  `registry_scope: "tenant_catalog"` to start a catalog workflow in the
  requesting user's runtime.
- Catalog scope is explicit. Without `registry_scope`, Gateway starts only
  private runtime bundles. Catalog flow/schema inspection uses ACL-aware
  `/api/gateway/workflow-catalog/{bundle_id}/versions/{version}/flows/{flow_id}`
  routes.

Gateway-hosted user administration keeps retained runtime data explicit:

- `/api/gateway/admin/users` is the admin-only user list/create/read/update/delete
  surface.
- `/api/gateway/admin/runtime-reservations` lists retained runtime reservations
  left by deleted or reassigned users.
- `/api/gateway/admin/runtime-reservations/{runtime_id}/transfer` intentionally
  assigns retained runtime data to an existing same-tenant user.
- `/api/gateway/admin/runtime-reservations/{runtime_id}/purge` requires exact
  runtime-id confirmation, deletes the retained runtime directory, then releases
  the runtime id for reuse.

Gateway-hosted provider endpoint profiles make reusable hosted endpoints
discoverable without exposing raw keys:

- `/api/gateway/config/provider-endpoint-profiles` lists and creates profiles
  for the current principal.
- `/api/gateway/config/provider-endpoint-profiles/discover-models` previews the
  model list for a draft or saved profile by calling the configured provider
  family and base URL with the server-side or entered key. The response never
  echoes the raw key.
- `/api/gateway/config/provider-endpoint-profiles/{profile_id}` updates or
  deletes an existing profile. Gateway-scoped profiles require an admin
  principal.
- `/api/gateway/discovery/providers` includes enabled profiles as virtual
  providers such as `endpoint:office-vllm`.
- `/api/gateway/discovery/providers/{provider_name}/models` resolves virtual
  providers through the stored profile and returns the allowed or discovered
  model list without returning the raw API key.

--- docs/faq.md ---
# FAQ

## What is AbstractFramework?

An open-source ecosystem for building **durable, observable, multimodal AI systems**.

Two things share the name:

- **The ecosystem**: composable packages (Core, Runtime, Agent, Gateway, Flow, Observer, apps, modality plugins).
- **This repo / meta-package**: `abstractframework` is a pinned install profile + cross-package docs.

If you're looking for the main SDK, that's **AbstractCore**. If you need durable orchestration, that's **AbstractGateway**.

---

## Where should I start?

- **AbstractCore** — when you want direct LLM/tool/media integration with a clean, provider-agnostic API (Python SDK **or** OpenAI-compatible `/v1`).
- **AbstractGateway** — when you need durability, orchestration, scheduling, or language-agnostic access via HTTP/SSE routes.

Most teams start with Core (SDK or `/v1`), then introduce Gateway when workflows become long-running, scheduled, or shared across clients. See **[Getting Started](getting-started.md)**.

---

## Do I need to install the whole stack?

No.

| Goal | Install |
|---|---|
| Smallest useful (LLM SDK only) | `pip install abstractcore` |
| Gateway-first deployment | `pip install abstractgateway` |
| Everything at compatible versions | `pip install abstractframework` |

See [Install AbstractFramework](install.md) for the Light / Apple / GPU chooser. Light is
remote-first, not reduced-functionality: multimodal and embeddings still work through remote or
local endpoint providers.

---

## Can I run everything offline with local models?

Yes. The core execution stack works fully offline with local model servers (Ollama, LM Studio, vLLM, llama.cpp, LocalAI). You need internet only to download models or use cloud APIs.

Multimodal plugins also work offline:

- **AbstractVoice**: Piper TTS (ONNX) + faster-whisper STT — prefetch once, run offline.
- **AbstractVision**: local Diffusers models or GGUF via stable-diffusion.cpp.
- **AbstractMusic**: local ACE-Step inference.

---

## What agent patterns are available?

AbstractAgent ships three patterns, all built on the durable Runtime kernel:

| Pattern | How it works |
|---|---|
| **ReAct** | Tool-first reasoning: observe → think → choose tool → execute → reflect |
| **CodeAct** | Code execution: generate Python, run it, observe output |
| **MemAct** | Memory-enhanced: reads/writes a knowledge graph during the loop |

Agents can run standalone or as nodes inside an AbstractFlow workflow.

---

## How does AbstractFramework compare to other frameworks?

### vs direct provider SDKs (OpenAI, Anthropic)

Direct SDKs are fine when you only use one provider and don't need durable orchestration.

AbstractCore adds value when you need: provider portability (local ↔ cloud), consistent tool/structured-output behavior across backends, media policies, modality plugins, or a configuration layer that doesn't leak into app code.

### vs LangChain / LlamaIndex / PydanticAI

Most agent libraries are **in-process orchestration**. AbstractFramework is a **durable orchestration stack**.

**Where AbstractFramework is stronger**: durability and pause/resume as primitives, replay-first observability, portable `.flow` bundles that run across clients.

**Where others are stronger**: large connector/RAG ecosystems, minimal boilerplate for simple use cases, broader community examples.

### vs Temporal / Step Functions / job schedulers

AbstractGateway is architecturally closer to these, but specialized for LLM/tool loops: tool approval waits, AI-oriented artifacts, and replay-first thin-client UIs over HTTP/SSE.

### Can I use AbstractFramework with LangChain/LlamaIndex?

Yes. Use AbstractFramework for orchestration and durability; integrate other libraries as tools or subflows:

- Use LlamaIndex retrievers as tools within an AbstractAgent
- Wrap LangChain chains as tool executors
- Let AbstractRuntime handle durability while external libraries handle specific capabilities

---

## What is a ".flow bundle" and why does it matter?

A `.flow` file is the portable distribution unit for workflows. It packages:

- A VisualFlow workflow graph
- Metadata (entry points, interfaces)
- Optional subflows and assets

Deploy a bundle to a gateway and any gateway-backed client can discover and run it.

---

## How do I author complex agentic orchestration?

1. Run a gateway (for durability + discovery).
2. Open the Flow Editor (`npx @abstractframework/flow`) and connect to the gateway.
3. Build a workflow: LLM steps, tool steps, agent nodes, branching, loops, subflows.
4. Export to `.flow`.
5. Copy into `ABSTRACTGATEWAY_FLOWS_DIR` to deploy.

To make it reusable across clients, implement an **interface contract** (for example `abstractcode.agent.v1`).

See **[Getting Started](getting-started.md)** → "Author orchestration with AbstractFlow".

---

## How do I monitor and schedule agentic work?

- **Monitoring**: use **AbstractObserver** — replay ledger, stream live execution, inspect errors, control runs.
- **Scheduling**: durable schedules are owned by the **gateway** (survive restarts). Create them from a client UI or via the gateway scheduling API.

See **[Getting Started](getting-started.md)** → "Gateway-first" section.

---

## How does multimodality work?

AbstractCore supports modalities via **capability plugins** (installed separately, discovered via entry points):

| Plugin | Capability |
|---|---|
| `abstractvoice` | `llm.voice` (TTS) / `llm.audio` (STT) |
| `abstractvision` | `llm.vision` (image generation) |
| `abstractmusic` | `llm.music` (text-to-music) |

Plugins are configured on the machine that actually executes (local app host or gateway host). Don't install a plugin; Core stays lightweight.

---

## Where is data stored?

- **Gateway**: `ABSTRACTGATEWAY_DATA_DIR` is the durability root (runs, ledger, artifacts, schedules).
- **Core config**: `~/.abstractcore/config/` (persisted by `abstractcore --config`).
- **Local apps**: typically `~/.abstractcode/`, `~/.abstractassistant/`, etc.

If you care about auditability and long-lived workflows, back up the gateway data directory.

---

## Troubleshooting

### Provider calls fail

- Verify provider environment variables (see **[Configuration](configuration.md)**).
- For local backends, make sure the server is running.
- Run `abstractcore --status` to check persisted config.

### Observer can't connect to the gateway

- Verify the gateway URL and auth token match.
- Ensure `ABSTRACTGATEWAY_ALLOWED_ORIGINS` includes the Observer origin (for local dev: `http://localhost:*`).
- Confirm the gateway is reachable: `curl http://127.0.0.1:8080/api/health`.

### Voice models not found

Prefetch explicitly (offline-first design):

```bash
abstractvoice-prefetch --stt small --piper en
```

--- docs/glossary.md ---
# Glossary

Shared terminology used across AbstractFramework documentation.

If you're new, read these groups first:

- **Durable execution**: run, ledger, effect, wait, artifact
- **Workflows**: flow, bundle, interface contract
- **Control plane**: gateway, schedule, observer

---

## Core (LLM SDK)

### Provider

An LLM backend integration (Ollama, OpenAI, Anthropic, any OpenAI-compatible server, etc.).

### Model

A provider-specific model identifier (`qwen3:4b-instruct`, `gpt-4o-mini`, `claude-3-5-sonnet-latest`, etc.).

### Capability route

A stable "slot" for a default model/provider choice, scoped by capability rather than by application. Examples: `input.text` (canonical LLM text route), `input.image` (fallback image-understanding route when the text model is not vision-capable), and `embedding.text` (default embeddings). `output.text` is a read-only derived view of `input.text`.

### Capability plugin

An optional package that extends AbstractCore with a modality API without bloating the base install. Install a plugin and the API appears on `llm` instances:

- `abstractvoice` → `llm.voice` (TTS) / `llm.audio` (STT)
- `abstractvision` → `llm.vision` (image generation)
- `abstractmusic` → `llm.music` (text-to-music)

---

## Tools

### Tool spec (schema)

A JSON-serializable description of a tool: name, description, and input schema. Tool specs are durable — they can be stored in the ledger and replayed.

### Tool executor (callable)

The host-side implementation that actually runs a tool. Executors are **not** durable; they live in the process that owns tool execution.

### Approval boundary

By default, tool execution is gated behind an explicit approval/resume step. This makes tool side-effects auditable, controllable, and restart-safe. Approval policy is configurable per tool (auto-approve safe tools, require manual approval for mutations).

---

## Durable execution (Runtime)

### Run

A durable workflow instance, identified by a `run_id`. A run has persisted state and a full append-only history.

### Session ID

A stable identifier used to group multiple runs into a long-lived "session" across time and clients (a chat thread, a device session, etc.).

### Ledger

The append-only history of what happened in a run (steps, effects, results, waits, errors). Replay-first clients render by replaying the ledger and then streaming new events.

### Step

One recorded unit in the ledger (node transitions, effect requests, results, errors).

### Effect

A typed request for a side-effect (LLM call, tool calls, ask user, wait-until, …). Effects are recorded so a run can resume correctly after restarts.

### Wait

A durable pause point: the run is checkpointed and stops progressing until it is resumed with external input (tool results, user input, time, or an event).

### Artifact

A Runtime-owned durable file payload referenced from JSON state/ledger. Use
artifacts for large payloads (files, media, evidence, big tool results) so run
state stays JSON-safe while the bytes remain reusable, searchable, and
observable across runs.

### Workspace File / Workspace Folder

A server-side file or folder inside the current Gateway-approved workspace
scope. This is the engineering term for server paths used by file helpers,
imports, exports, and run workspaces. A workspace file is not generic arbitrary
server filesystem access.

### Local File / Local Folder

A client-side source chosen from the current device. In hosted/browser mode,
this is an intake source, not a durable runtime path. Local file bytes are
typically uploaded and stored as an Artifact before durable execution.

### Server File / Server Folder

A user-facing source label for a workspace-scoped server file or folder. In
product copy, `Server File` means “a file inside the Gateway-approved workspace
scope,” not “any file the server can see.”

---

## Agent patterns

### Agent

A runtime workflow that implements a reasoning loop: observe → think → act → repeat. AbstractAgent ships three patterns:

- **ReAct**: tool-first reasoning (observe environment, choose tool, execute, reflect)
- **CodeAct**: code execution (generate Python, execute, observe output)
- **MemAct**: memory-enhanced (read/write a knowledge graph during the loop)

Agents can run standalone or as nodes inside a Flow.

---

## Workflows (Flow)

### Flow

A workflow graph: nodes + edges + state transitions. Flows encode orchestration logic: LLM steps, tool steps, branching, loops, subflows, and agent nodes.

### VisualFlow

The JSON workflow graph format used by AbstractFlow and executed by AbstractRuntime.

### Workflow bundle (`.flow`)

A portable distribution unit that packages a VisualFlow graph plus metadata (and optionally subflows/assets). Gateways discover `.flow` bundles and expose them to clients.

### Interface contract

A versioned input/output contract a flow can implement so multiple clients can run it consistently (for example `abstractcode.agent.v1` for chat-like agent flows).

---

## Control plane (Gateway) + operations (Observer)

### Gateway

The control plane for durable runs: start/resume/cancel, persistence, scheduling, bundle discovery, and ledger serving/streaming over HTTP/SSE.

### Schedule

A durable recurring trigger owned by the gateway ("run this workflow every 24h"). Schedules survive restarts.

### Observer

A thin-client browser UI for operations: monitor runs, inspect ledger history, watch live execution, control runs, and (when enabled) create schedules.

---

## Memory

### Active context

The current message view sent to the model (what the LLM "sees"). A derived view that can be compacted without losing underlying history.

### Stored history

The durable record of what happened (ledger + artifacts). The source of truth.

### Knowledge graph (KG) memory

Long-term memory stored as temporal triples (AbstractMemory), validated through the shared semantics registry (AbstractSemantics). Optional — not a hard dependency of the runtime kernel.

--- docs/scenarios/README.md ---
# Scenarios

This section is use-case oriented. Each scenario is an end-to-end path with concrete commands and "what to expect".

- [Offline coding assistant (terminal)](offline-coding-assistant.md)
- [Gateway-first local development](gateway-first-local-dev.md)
- [Specialized agent as a portable `.flow`](specialized-agent-flow.md)
- [Workflow bundle lifecycle (publish/install/deprecate)](workflow-bundle-lifecycle.md)
- [Telegram permanent contact](telegram-permanent-contact.md)
- [Email inbox agent](email-inbox-agent.md)
- [Phone thin client (iPhone via Web/PWA)](phone-thin-client.md)

If you prefer component-first docs, start with [Getting Started](../getting-started.md).

--- docs/scenarios/offline-coding-assistant.md ---
# Scenario: Offline Coding Assistant (Terminal)

Goal: run a durable coding assistant locally, offline-first, with Ollama (or an OpenAI-compatible local server).

## Prereqs

- Python 3.10+
- An LLM backend:
  - Ollama (recommended)
  - LM Studio / vLLM / LocalAI (OpenAI-compatible)

## Step 1: Install

Minimal install:

```bash
pip install abstractcode
```

Full pinned stack (includes AbstractCode):

```bash
pip install abstractframework
```

## Step 2: Start a local model

### Ollama

```bash
ollama serve
ollama pull qwen3:4b-instruct
export OLLAMA_HOST="http://localhost:11434"
```

## Step 3: Run AbstractCode

```bash
abstractcode --provider ollama --model qwen3:4b-instruct
```

## Step 4: Work with files and tools

- Mention files in prompts with `@path/to/file`.
- Type `/help` for commands.
- Tools are approval-gated by default:
  - Toggle in-session: `/auto-accept`
  - Start with: `--auto-approve`

## What "durable" means here

- Closing and reopening the app keeps state.
- Default storage:
  - `~/.abstractcode/state.json` (UI snapshot)
  - `~/.abstractcode/state.d/` (durable run state + ledger + artifacts)
- Start fresh: `/clear`
- Disable persistence: `--no-state`

## When to switch to the Gateway path

Use a gateway when you want:
- multiple thin clients observing the same run
- remote execution
- scheduling and a durable command inbox
- bundle discovery for specialized agents

See [Gateway-first local development](gateway-first-local-dev.md).

--- docs/scenarios/gateway-first-local-dev.md ---
# Scenario: Gateway-first Local Development

Goal: run the full "thin clients + gateway control plane" stack locally:

- AbstractGateway (HTTP/SSE control plane)
- AbstractObserver (run observability)
- AbstractFlow Editor (author `.flow` workflows)
- Code Web UI (browser coding assistant)

This is the recommended topology because execution is unified and clients can attach/detach freely.

## Step 0: Install the pinned stack (recommended)

```bash
pip install abstractframework
```

From a source checkout, `./scripts/gateway-flow-local.sh` starts Gateway and
Flow together and prints the default `admin` browser-login token. For a
published-package smoke run, `./scripts/gateway-flow.sh` creates an isolated
published-package venv, enables Gateway user auth, prepares the same `admin`
user, and prints the Gateway URL, user, and token to enter in Flow.

If you want a minimal install instead, you need at least:
- `abstractgateway`
- `npx @abstractframework/flow` for the editor UI

## Step 1: Prepare directories

Pick the Gateway data folder. Packaged Gateway installs already include the
shipped `basic-agent` bundle. In a source checkout, point the gateway at the
repo's bundled workflows.

- `DATA_DIR`: where gateway stores run state/ledger/artifacts
- `FLOWS_DIR`: optional custom `.flow` bundle directory; in this repo use
  `./abstractgateway/flows/bundles`

Example:

```bash
mkdir -p ./runtime/gateway
```

## Step 2: Configure the gateway

```bash
export ABSTRACTGATEWAY_USER_AUTH=1
export ABSTRACTGATEWAY_ALLOWED_ORIGINS="http://localhost:*,http://127.0.0.1:*"
export ABSTRACTGATEWAY_DATA_DIR="$PWD/runtime/gateway"

# Source checkout only. Packaged installs can omit this and use the shipped bundle path.
export ABSTRACTGATEWAY_FLOWS_DIR="$PWD/abstractgateway/flows/bundles"
```

When the gateway starts, it creates `default/admin` if needed and writes the
browser-login token to `$ABSTRACTGATEWAY_DATA_DIR/auth/bootstrap-admin-token`.

Optional route defaults (if your flows use LLM nodes):

```bash
abstractgateway-config set-default input.text \
  --provider ollama \
  --model qwen3:4b-instruct
```

You can also set the text default through Core:

```bash
abstractcore --set-global-default ollama/qwen3:4b-instruct
```

Capability routes are the durable default surface for local setups.

## Step 3: Start the gateway

```bash
abstractgateway serve --host 127.0.0.1 --port 8080
```

Smoke check:

```bash
curl -sS http://127.0.0.1:8080/api/health
```

Read the local admin user token for browser UIs:

```bash
cat "$ABSTRACTGATEWAY_DATA_DIR/auth/bootstrap-admin-token"
```

## Step 4: Start the thin clients

In separate terminals:

```bash
npx @abstractframework/observer
npx @abstractframework/flow
npx @abstractframework/code
```

Default ports:
- Observer: http://localhost:3001
- Code Web: http://localhost:3002
- Flow Editor: http://localhost:3003

## Step 5: Connect UIs to the gateway

In each UI:
- Set Gateway URL: `http://127.0.0.1:8080`
- Set User: `admin`
- Paste the generated Gateway user token
- Sign in

## Step 6: Author and run a specialized agent

1. In Flow Editor, create a workflow implementing `abstractcode.agent.v1`.
2. Export as a `.flow` bundle.
3. Put the `.flow` file into `FLOWS_DIR` (or configure the editor to publish to the gateway).
4. In Observer or Code Web, pick the workflow and start a run.
5. Watch the ledger stream in Observer.

See [Specialized agent as a portable `.flow`](specialized-agent-flow.md).

## Troubleshooting

- CORS errors in browser: widen `ABSTRACTGATEWAY_ALLOWED_ORIGINS` for your UI origin.
- "Unauthorized": ensure the browser UI is signed in with a Gateway user token, not the admin token.
- Bundles not showing up: verify `ABSTRACTGATEWAY_FLOWS_DIR` contains the shipped `basic-agent` bundle and any custom `.flow`
  files, then reload bundles from the UI (or restart the gateway).

--- docs/scenarios/specialized-agent-flow.md ---
# Scenario: Specialized Agent as a Portable `.flow`

Goal: build a specialized agent once, then run it in:

- AbstractCode (terminal)
- Code Web UI (browser)
- AbstractObserver (browser)
- your own apps (via gateway bundle discovery)

The key is an interface contract: `abstractcode.agent.v1`.

## Step 1: Create the flow

In the Flow Editor (`npx @abstractframework/flow`):

1. Add an **On Flow Start** node.
2. Add an **Agent** node (or **LLM Call** for a single-shot step).
3. Add an **On Flow End** node.
4. Wire the pins:
   - Start outputs to Agent inputs: `provider`, `model`, `prompt` (and optional `tools`, `context`, `memory`)
   - Agent outputs to End inputs: `response`, `success`, `meta` (and optional `scratchpad`)
5. In flow properties, set:
   - `interfaces: ["abstractcode.agent.v1"]`

## Agent vs LLM Call

- Use **Agent** when you want an internal multi-step loop (ReAct/CodeAct/MemAct style).
- Use **LLM Call** when you want a single request/response and explicit tool wiring in the graph.

See [Guide: Agent vs LLM Call](../guide/agent-vs-llm.md).

## Step 2: Export as a bundle

Export the workflow as a `.flow` bundle.

A `.flow` bundle packages:
- the root VisualFlow JSON
- any referenced subflows
- optional assets

## Step 3: Run locally (terminal)

With AbstractCode:

```bash
abstractcode --workflow /path/to/my-agent.flow
```

Or install the bundle into the local registry:

```bash
abstractcode workflow install /path/to/my-agent.flow
abstractcode --workflow my-agent
```

## Step 4: Deploy to a gateway

Copy the `.flow` bundle into `ABSTRACTGATEWAY_FLOWS_DIR`.

Then it will appear in:
- Observer workflow picker
- Code Web UI workflow picker
- Gateway discovery endpoints

## Step 5: Pass memory (optional)

If you want KG memory recall/writeback, wire a `memory` object into the Agent/LLM Call node.

See [Guide: Flow + KG memory](../guide/flow-and-kg-memory.md).


--- docs/scenarios/workflow-bundle-lifecycle.md ---
# Scenario: Publish, Install, and Deprecate Workflows (`.flow` bundles)

Goal: author a workflow once, distribute it as a portable `.flow` bundle, and manage its lifecycle on a gateway so it is
discoverable across clients.

## What you're managing

AbstractFramework distributes workflows as WorkflowBundles (`.flow` files):
- a zip bundle containing `manifest.json` + `flows/*.json` (and optional assets)
- entrypoints can advertise interface contracts (for example `abstractcode.agent.v1`) for discovery across clients

## Step 1: Author the workflow (Flow Editor)

Run the editor UI:

```bash
npx @abstractframework/flow
```

Open http://localhost:3003 and create/update your workflow.

## Step 2: Export/publish a `.flow` bundle

In the editor, export a `.flow` file (the bundle includes the root flow plus referenced subflows).

## Step 3: Put the bundle where the gateway loads bundles

On the gateway host, configure:

```bash
export ABSTRACTGATEWAY_FLOWS_DIR="/path/to/workflows"   # directory containing *.flow
export ABSTRACTGATEWAY_DATA_DIR="/path/to/gateway-data" # durable stores (run, ledger, artifacts)
```

Copy your bundle into `ABSTRACTGATEWAY_FLOWS_DIR`, for example:

- `my-bundle@0.1.0.flow`

Start/restart the gateway (bundle mode is the default):

```bash
abstractgateway serve --host 127.0.0.1 --port 8080
```

## Step 4: Discover and run from clients

Once loaded, the bundle entrypoints show up in:
- Observer workflow picker
- Code Web UI workflow picker
- Gateway discovery endpoints (for custom clients)

If you built a chat-like agent flow, declare `interfaces: ["abstractcode.agent.v1"]` so clients can run it as an agent.

## Step 5: Deprecate instead of deleting (recommended)

Deprecation hides workflows from discovery and blocks new starts, while keeping installed bundles for:
- reproducibility of past runs
- auditability and traceability

Look for gateway lifecycle controls in your UI client (Observer / Flow Editor), or use the gateway API if needed.


--- docs/scenarios/telegram-permanent-contact.md ---
# Scenario: Telegram "Permanent Contact" (Gateway Bridge + Agent Workflow)

Goal: run a Telegram contact that forwards inbound Telegram messages to a durable workflow (one run per message) and sends
replies back to Telegram.

This is a gateway-first scenario: the gateway host owns durability and stores plaintext history for replay/observability.

## High-level architecture

1. Telegram bridge receives a message.
2. Gateway maps it to a stable `session_id` (typically `telegram:<chat_id>:r<rev>`).
3. Gateway starts a new run for the configured flow (thin-client semantics).
4. The bridge sends the run output back to Telegram.

## Security model choices

Telegram has two integration paths:

1. TDLib + Secret Chats (E2EE in transit; recommended)
2. Bot API (easy, not E2EE)

Even with E2EE, messages are decrypted on the gateway host and persisted to the durable stores in plaintext by design.
Secure the gateway host and its storage.

## Step 1: Install AbstractGateway (Telegram bridge)

```bash
pip install abstractgateway
# Optional (only if your workflows call Telegram tools like `send_telegram_message`):
# pip install "abstractcore[tools]"
```

## Step 2: Configure the gateway (minimum)

You need a normal gateway configuration plus Telegram bridge settings. At minimum:

```bash
# Bundles (*.flow). Include the shipped `basic-agent` bundle (and any custom bundles).
export ABSTRACTGATEWAY_FLOWS_DIR="/path/to/bundles"
export ABSTRACTGATEWAY_AUTH_TOKEN="..."  # required
export ABSTRACTGATEWAY_ALLOWED_ORIGINS="http://localhost:*,http://127.0.0.1:*"
export ABSTRACTGATEWAY_DATA_DIR="$PWD/runtime/gateway"

# Tool execution + approvals:
# - `approval` (default): safe tools run in-process; dangerous/unknown tools require a Telegram reply: `/approve` or `/deny`.
export ABSTRACTGATEWAY_TOOL_MODE="approval"

export ABSTRACT_TELEGRAM_BRIDGE=1
# Bot API (easy, not E2EE). If ABSTRACT_TELEGRAM_BOT_TOKEN is set, transport defaults to bot_api.
export ABSTRACT_TELEGRAM_BOT_TOKEN="..."      # from @BotFather
# Optional: TDLib (E2EE) instead of Bot API:
# export ABSTRACT_TELEGRAM_TRANSPORT="tdlib"
# Optional: override which workflow to run per message.
# Default (when unset): shipped `basic-agent` bundle entrypoint.
# export ABSTRACT_TELEGRAM_BUNDLE_ID="basic-agent"
# export ABSTRACT_TELEGRAM_FLOW_ID="81795ea9"

# Access control (required by default)
# Use /whoami in Telegram to discover your numeric user_id.
export ABSTRACT_TELEGRAM_ALLOWED_USERS="123456789"

# Optional: pairing mode (lets unknown users request access, but requires an admin):
# export ABSTRACT_TELEGRAM_DM_POLICY="pairing"
# export ABSTRACT_TELEGRAM_ADMIN_USERS="123456789"

# Optional: group chat support (disabled by default):
# export ABSTRACT_TELEGRAM_GROUP_POLICY="allowlist"
# export ABSTRACT_TELEGRAM_ALLOWED_CHATS="-100123456789"  # use /whoami inside the group to discover chat_id
# export ABSTRACT_TELEGRAM_REQUIRE_MENTION_IN_GROUPS=1     # default true
```

Then start the gateway:

```bash
abstractgateway serve --host 127.0.0.1 --port 8080
```

Notes:
- Default LLM routing comes from the execution-host `input.text` capability
  route. Set it with `abstractcore --set-global-default ...` or
  `abstractgateway-config set-default input.text ...`.
- Telegram-only routing override: set `ABSTRACT_TELEGRAM_MODEL="..."` (and optionally `ABSTRACT_TELEGRAM_PROVIDER="..."`) without changing other gateway traffic.
- Durable history limit: `ABSTRACT_TELEGRAM_MAX_HISTORY_MESSAGES` (default: 30).
- STT fallback and vision caption fallback are configured via `abstractcore --config` (audio strategy + vision fallback).
- `/reset` clears the durable session; optional best-effort message deletion is controlled by `ABSTRACT_TELEGRAM_RESET_DELETE_MESSAGES` and `ABSTRACT_TELEGRAM_RESET_DELETE_MAX`. The confirmation text is configurable via `ABSTRACT_TELEGRAM_RESET_MESSAGE`.
- For tool approvals, the bridge will prompt you in chat; reply with `/approve` to run tools, or `/deny` to cancel.

## Step 3: Workflow wiring

Telegram is a thin client: any workflow that reads `prompt`/`context` (like `abstractcode.agent.v1`) and writes a string
to `run.output.answer` or `run.output.response` will work. Durable memory comes from the Telegram `session_id`.

## Step 4: TDLib (E2EE) bootstrap (recommended path)

TDLib requires a real Telegram user account and the TDLib shared library (`tdjson`) installed on the gateway host. You
then authenticate once to create a persistent TDLib session directory.

Because TDLib setup is platform-specific, follow:
- [Guide: Telegram integration](../guide/telegram-integration.md)

## Step 5: Test

Send a Telegram message to the AI contact. You should see:
- gateway emits the event into the session
- your run progresses and sends a reply via tools
- Observer can replay the ledger for the session/run

## See also

- [Guide: Telegram integration](../guide/telegram-integration.md) — configuration and TDLib details

--- docs/scenarios/email-inbox-agent.md ---
# Scenario: Email Inbox Agent (IMAP Bridge + SMTP Replies)

Goal: ingest inbound emails as durable events and let a workflow reply (or take actions) with framework-native email tools.

## High-level architecture

- Inbound: gateway email bridge polls IMAP, stores raw + attachments as artifacts, emits `email.message` events into a
  stable `session_id` per thread.
- Outbound: workflows call `send_email` with centralized SMTP defaults (no repeating host/user per tool call).

## Step 1: Configure email accounts on the tool-execution host

Email tools are account-scoped: IMAP/SMTP host/user are configured on the process that executes tools (gateway local
tools, CLI host, or a tool worker).

For the full configuration matrix (env vs YAML vs AbstractCore config), use the canonical guide in the main framework
workspace:
- [Guide: Email integration](../guide/email-integration.md)

## Step 2: Enable the inbound email bridge on the gateway host

Minimum env vars (plus your email account config):

```bash
export ABSTRACT_EMAIL_BRIDGE=1
export ABSTRACT_EMAIL_POLL_SECONDS=60

export ABSTRACT_EMAIL_FLOW_ID="<bundle_id>:<flow_id>"   # autostart/attach a workflow per thread/session
```

Start the gateway with a persistent `ABSTRACTGATEWAY_DATA_DIR` so bridge state survives restarts.

## Step 3: Wire the workflow

Create a workflow that:
1. handles the `email.message` event
2. reads `payload.email.*` (subject/from/body and artifact-backed attachments)
3. optionally opens attachments via `open_attachment(artifact_id=...)`
4. replies with `send_email(to=..., subject=..., body_text=...)`

## Step 4: Test

Send an email into the mailbox. Expected:
- the bridge emits `email.message`
- a session-run starts (or is resumed) for that thread
- your workflow replies via `send_email`

--- docs/scenarios/phone-thin-client.md ---
# Scenario: Phone Thin Client (iPhone via Web/PWA) + Gateway

Goal: run a thin client UI on a phone that attaches to runs and controls them, while the gateway host owns durability and
execution.

This is especially useful for:
- remote coding/agent sessions from a phone
- observing runs while away from your workstation

## Mental model (thin client)

- The phone does not tick the runtime.
- It renders by replaying/streaming the ledger.
- It acts by sending durable commands (resume/pause/cancel/emit_event).

## Quickstart (LAN dev, no HTTPS)

1. Run the gateway bound to all interfaces (still use auth):
   - `abstractgateway serve --host 0.0.0.0 --port 8080`
2. Run the web UI host on all interfaces (dev server):
   - `npx @abstractframework/code`
3. Allow the dev origin in gateway CORS/origin allowlist.
4. Open the web UI URL on your iPhone Safari and connect it to the gateway.

## Production note (recommended)

For installable iOS PWA behavior, host the web UI over HTTPS and run the gateway behind HTTPS termination (reverse proxy
or tunnel). Restrict `ABSTRACTGATEWAY_ALLOWED_ORIGINS` to the exact UI origin.

## Canonical iPhone guide (deeper)

For a step-by-step iPhone/PWA guide, see:
- [Guide: Web deployment](../guide/deployment-web.md)
- [Guide: iPhone notes](../guide/deployment-iphone.md)

--- docs/guide/README.md ---
# Guides

Short, focused guides for common framework questions.

- [Agent vs LLM Call (VisualFlow)](agent-vs-llm.md)
- [Capability plugins (voice/audio/vision)](capability-plugins.md)
- [Capability routing defaults](capability-routing-defaults.md)
- [Deployment topologies](deployment-topologies.md)
- [Web deployment (browser UI + gateway)](deployment-web.md)
- [iPhone notes (Safari / PWA)](deployment-iphone.md)
- [Gateway exposure security](gateway-security.md)
- [Runtime scope (run/session/global/all)](runtime-scope.md)
- [Runtime artifacts and retrieval](runtime-artifacts.md)
- [Flow + KG memory (memory object)](flow-and-kg-memory.md)
- [Scheduled workflows (durable jobs)](scheduled-workflows.md)
- [Prompt caching (prompt/KV)](prompt-caching.md)
- [Agent Skills (SKILL.md) — proposal](agent-skills.md)
- [WorkflowBundles (`.flow`) lifecycle](workflow-bundles.md)
- [Telegram integration](telegram-integration.md)
- [Email integration](email-integration.md)
- [Process manager env vars (write-only)](process-manager-env-vars.md)

--- docs/guide/agent-vs-llm.md ---
# Agent vs LLM Call (VisualFlow)

VisualFlow has two LLM-oriented nodes that intentionally look similar, but differ in **autonomy**.

## When to use which

### Use **LLM Call**

- You want a **single** LLM request/response step (no internal loop).
- You want to explicitly wire tool execution in the graph:
  - `LLM Call.tool_calls` -> `Tool Calls.tool_calls` -> (your next node).

### Use **Agent**

- You want the node to run an **internal multi-step loop** (ReAct-style) until it finishes or hits a cap.
- You want a runtime-owned **scratchpad** (trace/transcript) for observability.

## Inputs (shared contract)

Agent and LLM Call share the same parameter set and ordering (Agent has one extra cap: `max_iterations`):

1. `use_context` (boolean): include the run's active context messages (`context.messages`) in the request.
2. `context` (object): explicit context override. When provided, `context.messages` overrides inherited run context
   messages.
3. `provider` (provider), `model` (model): route the call.
4. `system` (string): optional system instructions for this node.
5. `prompt` (string): the user prompt/content for this node.
6. `tools` (tools): allowlist of tools the model may request (execution is still explicit in the graph).
7. Agent-only: `max_iterations` (number): maximum internal loop iterations (safety cap).
8. `max_in_tokens` (number): optional per-call/per-agent input token budget (VisualFlow shorthand for
   `max_input_tokens`).
9. `temperature` (number), `seed` (number): sampling controls.
10. `resp_schema` (object): optional JSON Schema for schema-constrained responses.

Notes:
- The canonical prompt key/pin is always `prompt` (there is no `request` alias).

## Outputs (what differs)

### LLM Call outputs

- `response` (string), `success` (boolean), `meta` (object), `tool_calls` (array)

### Agent outputs

- `response` (string), `success` (boolean), `meta` (object), `scratchpad` (object)

There is no separate `result` output pin in the durable contract.

### Agent `scratchpad` (what it contains)

The Agent scratchpad is runtime-owned observability (it can be large). Common fields:

- `messages`: agent-internal transcript for the sub-run (ReAct loop)
- `task`: the agent prompt/task for this node
- `context_extra`: any extra fields passed in `context` besides `task`/`messages` (host-defined)
- `node_traces` / `steps`: structured per-node trace + flattened UI-friendly steps
- `tool_calls` / `tool_results`: best-effort extraction from the trace

## `resp_schema` (structured responses)

When `resp_schema` is provided:

- `response` is a JSON string matching the schema (so it stays a simple `string` pin).
- Use a JSON parser node if you want to treat it as an object downstream.

## RunnableFlow interface (for chat-like clients)

The RunnableFlow (v1) interface (id: `abstractcode.agent.v1`) is the host contract used by AbstractCode, AbstractObserver,
and similar clients:

- `On Flow Start` exposes the same parameter set (in the same order) so hosts can configure a workflow run.
- Required `On Flow Start` pins: `provider`, `model`, `prompt` (everything else optional).
- Required `On Flow End` pins: `response`, `success`, `meta` (others optional).


--- docs/guide/capability-plugins.md ---
# Capability plugins (Voice/Audio/Vision/Music)

This guide explains how to add optional audio/voice/vision capabilities to AbstractFramework without turning
`abstractcore` into a kitchen sink.

## Mental model (two concepts; don't mix them)

1. **LLM input modalities (AbstractCore)**
   - Attaching image/audio/video to an LLM call (`generate(..., media=[...])`) depends on the selected provider/model's
     input capabilities.

2. **Deterministic capabilities (plugins)**
   - STT/TTS and generative vision are deterministic APIs that can be used with or without an LLM call:
     - `core.voice` / `core.audio` (speech-to-text, text-to-speech) via `abstractvoice`
     - `core.vision` (text-to-image, image-to-image, ...) via `abstractvision`
     - `core.music` (text-to-music) via `abstractmusic`

This split keeps `abstractcore` lightweight by default.

## Library mode (Python)

### Install

```bash
pip install abstractcore
pip install abstractvoice      # enables core.voice + core.audio
pip install abstractvision     # enables core.vision
pip install abstractmusic      # enables core.music
```

### Discover what's available

```python
from abstractcore import create_llm

llm = create_llm("ollama", model="qwen3:4b-instruct")  # example; pick a provider/model you have access to
print(llm.capabilities.status())
```

Notes:
- Capabilities load lazily the first time you access `llm.capabilities` / `llm.voice` / `llm.audio` / `llm.vision` / `llm.music`.
- Missing plugins raise an actionable error (includes an install hint).

### Use voice/audio (STT/TTS)

```python
wav_bytes = llm.voice.tts("Hello from AbstractVoice", format="wav")
open("hello.wav", "wb").write(wav_bytes)

text = llm.audio.transcribe("speech.wav")
print(text)
```

### Use generative vision (T2I/I2I/...)

`core.vision` can use an OpenAI-compatible images backend (configured via `vision_base_url` / `ABSTRACTVISION_BASE_URL`).

```python
llm = create_llm(
    "openai",
    model="gpt-4o-mini",
    vision_base_url="http://localhost:8000/v1",  # any OpenAI-compatible images endpoint
)

png_bytes = llm.vision.t2i("a red square on white background")
open("out.png", "wb").write(png_bytes)
```

### Use music generation (T2M)

`core.music` is plugin-backed (like voice/vision). `abstractmusic` provides
**local in-process** generation, with **ACE-Step Official**
(`acestep-official`) as the recommended backend and Diffusers audio pipelines
as alternative backends.

```python
llm = create_llm(
    # Any provider/model works here. The LLM does *not* generate music audio.
    # Music generation is performed by the configured AbstractMusic backend.
    "ollama",
    model="qwen3:4b-instruct",
    music_backend="acestep-official",
    music_model_id="ACE-Step/Ace-Step1.5",
)

wav_bytes = llm.music.t2m("uplifting synthwave, 120bpm, catchy chorus", format="wav", duration_s=10.0)
open("out.wav", "wb").write(wav_bytes)
```

## Framework mode (gateway/runtime)

Install modality plugins on the durable host (the machine/process that runs the runtime + tool execution and imports
`abstractcore`), typically the AbstractGateway runner.

Thin clients (web, remote TUI) do not need `abstractvoice`/`abstractvision`/`abstractmusic` installed locally.

## Server mode (OpenAI-compatible `/v1`)

AbstractCore Server can optionally expose OpenAI-compatible endpoints by delegating to plugins:
- `/v1/images/*` (via `abstractvision`)
- `/v1/audio/*` (via the capability plugin layer, typically `abstractvoice`; plus `/v1/audio/music` when `abstractmusic` is installed)

These endpoints are interoperability-first. For durable artifact-backed outputs, prefer gateway/runtime + ArtifactStore.

--- docs/guide/deployment-topologies.md ---
# Deployment Topologies (Supported Patterns)

AbstractFramework is easier to reason about if you think in roles, not packages:

- UI / host UX: renders progress, collects approvals (AbstractCode, browser UIs, custom apps)
- Orchestrator: durable state machine + stores (AbstractRuntime + stores)
- Agent logic: produces effects/steps (AbstractAgent patterns, flows)
- LLM gateway: provider abstraction + tool-call parsing (AbstractCore)
- Tool executors: side effects (local tools, MCP workers, sandboxes)

## Topology A: Single machine (local everything)

Best for local development and offline-first workflows.

- UI + runtime + tools run in one process (or one machine).
- You can still use file-backed stores for durability.

## Topology B: Local orchestration + remote inference

Best when you want local tool execution but a remote model (GPU box, cloud API, hosted vLLM).

- Runtime + tools stay local.
- AbstractCore routes LLM calls to a remote provider endpoint.

## Topology C: Remote tool execution (MCP-backed)

Best when tools must run near the target environment (servers, private networks, sandboxes).

- Runtime stays on the durable host.
- Tool calls are delegated to an MCP worker.

## Topology D: Thin client UI + remote durable host (Gateway-first)

Recommended for multi-device use and trusted multi-client use.

- The gateway host owns the durable runtime + stores and progresses runs.
- Thin clients render by replaying/streaming the ledger and act by submitting durable commands.
- Bundles (`.flow`) provide portable, discoverable specialized agents.
- Independent users should use Gateway user auth. Gateway routes each principal
  to a runtime/data plane and reserves retained runtime ids after deletion until
  an admin explicitly transfers or purges that runtime.

Container baseline:

```bash
docker run \
  -p 8080:8080 \
  -v "$PWD/runtime:/data" \
  -e ABSTRACTGATEWAY_DATA_DIR=/data \
  -e ABSTRACTGATEWAY_USER_AUTH=1 \
  ghcr.io/lpalbou/abstractgateway:latest
```

Use `ghcr.io/lpalbou/abstractgateway:gpu-latest` only for explicit NVIDIA/GPU
deployments. Apple/MLX inference should run natively on macOS and be exposed to
the light Gateway container as an OpenAI-compatible endpoint.

## Topology E: Multi-host orchestration (planned/advanced)

Only needed when you want to distribute durable orchestration itself across multiple hosts.
For v0, prefer picking a host per run (avoid mid-run migration).

## See also

- [Scenario: Gateway-first local development](../scenarios/gateway-first-local-dev.md)
- [Guide: Gateway exposure security](gateway-security.md)

--- docs/guide/deployment-web.md ---
# Web Deployment (Browser UI + Gateway)

This guide covers deploying a browser UI (Observer / Flow Editor / Code Web UI) against an AbstractGateway.

## What "gateway-first" means

- The browser does not tick the runtime.
- It renders by replaying/streaming the ledger.
- It acts by sending durable commands (start/resume/pause/cancel/emit_event).

## Minimum gateway settings (browser access)

Set these on the gateway host:

```bash
export ABSTRACTGATEWAY_USER_AUTH=1
export ABSTRACTGATEWAY_ALLOWED_ORIGINS="http://localhost:*,http://127.0.0.1:*"
```

Start the gateway:

```bash
abstractgateway serve --host 127.0.0.1 --port 8080
```

Gateway creates `default/admin` if needed and writes the first browser-login
token to `$ABSTRACTGATEWAY_DATA_DIR/auth/bootstrap-admin-token`. Use that token
for `/console` and browser apps, then create named users or rotate tokens from
the console.

## Run the UIs

```bash
npx @abstractframework/observer
npx @abstractframework/flow
npx @abstractframework/code
```

Default ports:
- Observer: http://localhost:3001
- Code Web UI: http://localhost:3002
- Flow Editor: http://localhost:3003

In each UI, set:
- Gateway URL: `http://127.0.0.1:8080`
- User: the Gateway user id assigned by the Gateway admin
- Gateway token: that user's token, used only to create the browser session

For hosted deployments on a non-local UI hostname, configure the Gateway URL on
the UI server. Browser-supplied Gateway URL changes are rejected by Flow, Code
Web, and Observer unless the app-specific
`*_ALLOW_REMOTE_BROWSER_GATEWAY_CONFIG=1` override is enabled behind your own
access control. If the UI is behind a reverse proxy that rewrites `Host`, enable
`*_TRUST_PROXY_HEADERS=1` only after the proxy strips client-supplied forwarded
headers.

## Production notes (high-signal)

- Terminate TLS at a reverse proxy and forward to `127.0.0.1:8080`.
- Restrict `ABSTRACTGATEWAY_ALLOWED_ORIGINS` to exact UI origins (avoid broad wildcards).
- Keep the Gateway admin token secret and rotate it like any control-plane
  credential.
- Hosted browser apps should keep only their app-scoped Gateway session cookie;
  do not persist user bearer tokens in browser storage.
- Do not use one shared user token for independent users. Use Gateway
  per-principal routing so each user token maps to that user's runtime.

See [Guide: Gateway exposure security](gateway-security.md).

--- docs/guide/deployment-iphone.md ---
# iPhone Notes (Safari / PWA)

Code Web UI is designed to run on iPhone as a thin host UI that connects to a remote gateway deployment.

## Prereqs (recommended)

- Gateway reachable over HTTPS (reverse proxy + TLS).
- Web UI hosted over HTTPS.
- Gateway configured with:
  - `ABSTRACTGATEWAY_USER_AUTH=1` so each browser user signs in with a Gateway user token
  - `ABSTRACTGATEWAY_ALLOWED_ORIGINS` including your web UI origin (exact host recommended for prod)

## Steps

1. Open the web UI URL in Safari.
2. In Settings:
   - set Gateway URL (for example `https://gateway.example.com`)
   - sign in with your Gateway user id and user token
3. Optional: Safari -> Share -> Add to Home Screen.

## Constraints

- iOS suspends background tabs aggressively; durability depends on ledger replay, not "staying connected".
- File access is remote (via gateway); the phone does not run local tools in v1.

--- docs/guide/gateway-security.md ---
# Gateway Exposure Security (Checklist)

Treat `abstractgateway serve` as a control-plane service: it can access runs/ledgers/attachments, and (optionally) execute
maintenance actions depending on your deployment.

## Recommended defaults (local dev)

- Bind to loopback: `--host 127.0.0.1`
- Enable Gateway user auth for browser apps and per-user runtime routing:

```bash
export ABSTRACTGATEWAY_USER_AUTH=1
```

- Use the generated `default/admin` browser-login token from
  `$ABSTRACTGATEWAY_DATA_DIR/auth/bootstrap-admin-token` for first setup, then
  rotate it or create named users in `/console`.
- Use a strong `ABSTRACTGATEWAY_AUTH_TOKEN` only for legacy server/operator
  bearer-token deployments; it is not a browser sign-in token.

```bash
export ABSTRACTGATEWAY_AUTH_TOKEN="$(python -c 'import secrets; print(secrets.token_urlsafe(32))')"
```

```bash
export ABSTRACTGATEWAY_USER_AUTH=1
```

- Allow only localhost origins for browser UIs:

```bash
export ABSTRACTGATEWAY_ALLOWED_ORIGINS="http://localhost:*,http://127.0.0.1:*"
```

## If exposing beyond localhost (LAN, tunnels, internet)

1. Run behind TLS (reverse proxy or a trusted tunnel).
2. Use a strong token and rotate it periodically.
3. Restrict `ABSTRACTGATEWAY_ALLOWED_ORIGINS` to exact UI origins (avoid broad wildcards).
4. Protect reads as well as writes (ledgers contain prompts and tool outputs).

## Hosted file-source terms

When hosted browser clients talk about files, use this vocabulary:

- `Artifact`: a saved Runtime-owned file payload.
- `Local File`: a file chosen from the client device. In hosted/browser mode it
  is uploaded and becomes an Artifact before durable execution.
- `Server File`: a user-facing label for a file inside Gateway-approved
  workspace scope on the server. It does **not** mean arbitrary server
  filesystem access.

Canonical server paths use:

- `rel/path` for the main workspace root
- `mount_alias/rel/path` for approved mounts

If two allowed mounts share the same basename, Gateway now assigns
deterministic digest-suffixed mount aliases so the public path string is stable
across Gateway discovery, import/export, and Runtime execution.

Current UI surfaces may still say `Workspace` in some places. The engineering
term remains `Workspace File` / `Workspace Folder`.

## User isolation

When Gateway user auth is enabled, each user token resolves to a Gateway
principal and runtime mapping. Hosted browser apps such as AbstractFlow,
AbstractCode Web, and AbstractObserver exchange the signed-in user's token for
a Gateway browser session, store only an app-scoped opaque session id in an
HTTP-only browser cookie, and forward that session to Gateway server-side. The
raw user token is not stored in browser settings, and Gateway login response
bodies do not expose the session id or CSRF token. Mutating proxied Gateway
requests also carry a CSRF token. A server/admin token does not sign in
browsers. Gateway browser session cookies use an HTTP-only cookie for the
session id, a separate CSRF cookie, `SameSite=Lax`, path `/`, no `Secure` flag
on plain HTTP, and `Secure` when served over HTTPS. Sessions expire, logout
revokes the session record, and disabling/deleting/rotating the backing Gateway
user invalidates existing sessions.

For independent users, prefer `1 user = 1 runtime` through Gateway's
per-principal router. Gateway rejects duplicate runtime ids within the same
tenant when creating or updating users, so an admin cannot accidentally map two
tenant users to the same runtime. When a user is deleted, Gateway removes that
user's credential but reserves the retained runtime id for that deleted
principal; assigning the retained runtime to a different user is rejected unless
an admin explicitly transfers or purges the retained runtime reservation. Purge
requires exact runtime-id confirmation and deletes the retained runtime directory
before releasing the runtime id. Transfer assigns the retained runtime to an
existing same-tenant user and reserves that user's previous runtime id. Keep
shared/cross-user memory or collaboration explicit, permissioned, and auditable.

Hosted browser apps use the server-configured Gateway URL on non-local hosts.
AbstractFlow, AbstractCode Web, and AbstractObserver reject browser-supplied
Gateway URL changes when the UI is served from a non-loopback hostname. Enable
the app-specific override only behind your own access control:
`ABSTRACTFLOW_ALLOW_REMOTE_BROWSER_GATEWAY_CONFIG=1`,
`ABSTRACTCODE_ALLOW_REMOTE_BROWSER_GATEWAY_CONFIG=1`, or
`ABSTRACTOBSERVER_ALLOW_REMOTE_BROWSER_GATEWAY_CONFIG=1`. The host check uses
the request `Host` header by default. Trust proxy headers only when your reverse
proxy strips client-supplied forwarded headers, using the app-specific
`*_TRUST_PROXY_HEADERS=1` setting or `ABSTRACTGATEWAY_TRUST_PROXY_HEADERS=1`.

Shared workflows are handled through the Gateway workflow catalog rather than
by sharing a user's private runtime bundle directory. Catalog versions are
immutable by `bundle_id@version`; admins can move a default pointer, set ACLs,
deprecate or block a version, or tombstone it without deleting the stored
bundle bytes. Catalog workflows run in the requesting user's runtime by
default, so the shared workflow definition does not imply shared run state.
Run-start policy checks the signed-in user's tenant, roles, and catalog ACL
before execution. Private `/api/gateway/bundles` routes remain per-runtime
authoring surfaces.

Catalog bundles are loaded under internal host ids, but those internal ids are
not a public authorization surface. Direct private-bundle inspection routes
reject catalog-internal ids; use the ACL-aware `/api/gateway/workflow-catalog`
routes to list or inspect shared workflows. Gateway also strips client-supplied
`_runtime.workflow_policy` values and replaces catalog starts with a signed
Gateway-issued policy snapshot before Runtime sees the run.

Gateway serves a built-in admin/account console at `/console`. The console uses
the same browser-session contract: users sign in with a Gateway user id and
token, then the browser keeps only the opaque session cookie. Because the
console is served by Gateway itself, it does not ask for a Gateway URL; the
current origin is the Gateway. Admin users can
manage Gateway users, rotate tokens, and handle retained runtime reservations
from this console. Signed-in users configure provider connections in the
Providers tab, then set capability defaults from those configured virtual
providers and discovered models. Admin/root defaults act as the Gateway
baseline; normal users inherit that baseline and can override it only for their
own runtime.

The console also supports Gateway-owned provider endpoint profiles. Profiles
can describe OpenAI-compatible or hosted provider endpoints with a display name,
description, provider family, base URL, API key, capabilities, and optional
model allowlist. Discovery exposes only non-secret metadata and a virtual
provider id such as `endpoint:office-vllm`; Gateway injects the raw key only into
the runtime call that resolves that profile. Normal users can create user-scoped
profiles. Gateway-scoped profiles require an admin principal.

The console Sandbox sends browser-local prompt-grounding metadata such as local
datetime, timezone, timezone offset, and locale with test chat requests. Runtime
may use those fields to ground the model response for the browser user, but the
fields are explicitly untrusted and are never used for authorization, runtime
routing, provider credential selection, or audit authority. Server-derived
grounding remains recorded as provenance. When both browser timezone and browser
locale are present, country grounding prefers the timezone mapping over the
locale region because browser language is not a reliable location signal.

Gateway keeps operator surfaces admin-only through a central route-family
policy. User management, audit/process/backlog/triage/report routes, email
bridge routes, host metrics, model residency list/load/unload, server workspace
file helpers, and server workspace artifact import/export require an admin
principal. Browser local files should use upload routes; server filesystem
read/import/export is not exposed to ordinary hosted users.

In hosted user-auth mode today, ordinary users can still upload `Local File`
sources and reuse Artifacts, but server workspace helper routes and server
workspace artifact import/export remain admin/operator controlled until a
stronger per-principal workspace grant model lands.

Discovery metadata is permission-aware for these high-trust surfaces. Ordinary
users see admin-only workspace artifact import/export and provider prompt-cache
control operations marked unavailable with `admin_required` metadata. Their
session prompt-cache names are still usable, but the private hash includes the
current principal scope so two users cannot collide by choosing the same
session id, provider, and model.

## Verify quickly

- `/api/health` returns `200`
- `/api/gateway/*` endpoints return `401` without `Authorization: Bearer ...`
- `/console` loads the Gateway Console, and admin-only actions return `403` for
  non-admin users

## See also

AbstractGateway has a deeper security guide (env vars, limits, lockouts, audit log):
- https://github.com/lpalbou/abstractgateway/blob/main/docs/security.md

--- docs/guide/capability-routing-defaults.md ---
# Capability Routing Defaults

Capability routing defaults define which provider/model/backend the framework should use when a
request does not provide an explicit route.

They are configuration, not residency. A route can be configured even when the provider does not
currently have that model loaded. Loaded-model state is reported separately by provider residency
endpoints and the AbstractFlow "Loaded models" view.

## Route Keys

Routes use:

```text
<kind>.<modality>[.<task>]
```

The optional task suffix is used only for generated-media defaults that need
different provider/model pairs inside one modality. Model capability metadata
stays broad unless a separate ADR changes it.

Route kinds:

- `input`: understanding or enrichment of request content.
- `output`: generation targets.
- `embedding`: vectorization for retrieval and indexes.
- `rerank`: ranking routes for a future reranker manager.

Core modalities:

- `text`
- `image`
- `video`
- `voice`
- `sound`
- `music`
- `scene3d`

Examples:

- `input.text`: canonical LLM route for text understanding and text generation.
- `input.image`: VLM or captioning fallback route for images when `input.text`
  is not vision-capable.
- `input.video`: native video or video-frame understanding fallback. When
  `input.text` is known to handle visual frames, this route may be reported as
  covered by `input.text`; unlike `input.image`, it remains overrideable.
- `input.voice`: speech-to-text fallback route for audio attachments.
- `input.sound`: non-speech audio understanding route. It is not used as a
  speech-to-text fallback.
- `output.text`: read-only derived view of `input.text`.
- `output.image.text_to_image`: text-to-image generation backend.
- `output.image.image_to_image`: image edit / image-to-image backend.
- `output.image.image_upscale`: image restoration / upscale backend.
- `output.video.text_to_video`: text-to-video generation backend.
- `output.video.image_to_video`: image-to-video generation backend.
- `output.voice`: TTS route.
- `output.sound`: sound effects / text-to-audio route.
- `output.music`: music generation route.
- `embedding.text`: text embedding model for semantic retrieval.
- `rerank.text`: reserved route for text reranking.

## Route Payload

Each route stores a small JSON-safe target:

```json
{
  "provider": "lmstudio",
  "model": "qwen/qwen3.6-35b-a3b",
  "base_url": "http://127.0.0.1:1234/v1",
  "options": {
    "voice": "M1"
  }
}
```

`provider`, `model`, and `base_url` are shared fields. `options` is provider/plugin-specific and can
carry values such as a voice, language, quality preset, or backend profile.

Secrets do not belong in route defaults. API keys remain provider credentials managed by
AbstractCore, Gateway deployment secrets, or the capability plugin.

## Configuration Ownership

AbstractCore owns the schema and persistence. Routes live in:

```text
~/.abstractcore/config/abstractcore.json
```

Gateway is the control plane:

- co-located Gateway reads/writes the local AbstractCore config;
- split Gateway proxies to the configured AbstractCore server;
- Gateway does not create a separate provider/model defaults file.

In split deployments, `base_url` is interpreted from the execution host that actually calls the
provider. A URL that works from the Core/Runtime host may not work from the browser or Gateway host.

Task-specific generated-media routes are the operator-facing configuration
surface. Core still accepts broad `output.image` and `output.video`
compatibility defaults for older configurations, but Gateway Console does not
display them because `output.image.text_to_image` and
`output.video.text_to_video` are the concrete defaults for those tasks.

## Configure From Core

Set the framework text default:

```bash
abstractcore config set-default input.text \
  --provider lmstudio \
  --model qwen/qwen3.6-35b-a3b
```

`output.text` is accepted as a compatibility alias, but Core persists it as
`input.text`. Use `abstractcore --set-global-default ...` only when you want the
older global-default helper, which now writes that same canonical route.

Configure `input.image` only as a fallback for text models that cannot accept
images. When AbstractCore's model-capability registry knows the `input.text`
model supports image input, Gateway and Core report `input.image` as covered by
`input.text` instead of as an independently editable route.

Configure `input.voice` when speech attachments should be transcribed before a
text model receives the request:

```bash
abstractcore config set-default input.voice \
  --provider faster-whisper \
  --model large-v3
```

Core does not silently use installed STT packages as a hidden fallback. If the
current text model cannot accept audio natively, `audio_policy=auto` needs this
`input.voice` route. `audio_policy=speech_to_text` remains available for
explicit per-call routing, but normal framework defaults should use the route.

Configure `input.sound` only for non-speech audio understanding such as sound
events, audio scenes, and SFX. This is not the same as STT: Whisper-style
transcription models belong under `input.voice`, while audio-language models
such as `qwen3-omni-30b-a3b-instruct`,
`qwen3-omni-30b-a3b-captioner`, `qwen2.5-omni-7b`, or
`qwen2-audio-7b-instruct` are better candidates when a provider can serve them.
`qwen/qwen3.6-35b-a3b` remains a text/image/video default candidate, not an
audio-understanding model.

Configure `input.video` when a text route should use a separate video/VLM
fallback instead of native video support or the `input.text` model's frame
support:

```bash
abstractcore config set-provider office-vlm \
  --family openai-compatible \
  --base-url https://vlm.example.com/v1 \
  --api-key $OFFICE_VLM_API_KEY \
  --description "Office vision endpoint"

abstractcore config set-default input.video \
  --provider endpoint:office-vlm \
  --model qwen2.5-vl-72b
```

If no native video route and no `input.video` default are available, Core
reports a configuration error instead of silently choosing an unrelated
vision/video backend.

Set one route directly:

```bash
abstractcore config set-default output.voice \
  --provider supertonic \
  --model supertonic-3 \
  --base-url http://127.0.0.1:5000/v1 \
  --option voice=M1
```

Configure text embeddings:

```bash
abstractcore config set-default embedding.text \
  --provider lmstudio \
  --model text-embedding-nomic-embed-text-v1.5 \
  --base-url http://127.0.0.1:1234/v1
```

or through the embedding convenience commands:

```bash
abstractcore --set-embeddings-provider lmstudio
abstractcore --set-embeddings-model lmstudio:text-embedding-nomic-embed-text-v1.5
abstractcore --set-embeddings-base-url http://127.0.0.1:1234/v1
```

## Configure Through Gateway

Use Gateway when it is the operator/control-plane entry point:

```bash
abstractgateway-config defaults

abstractgateway-config set-default input.text \
  --provider lmstudio \
  --model qwen/qwen3.6-35b-a3b \
  --base-url http://127.0.0.1:1234/v1

abstractgateway-config set-default embedding.text \
  --provider lmstudio \
  --model text-embedding-nomic-embed-text-v1.5 \
  --base-url http://127.0.0.1:1234/v1
```

With Gateway user auth enabled, the default command edits the Gateway baseline
Core config at `$ABSTRACTGATEWAY_DATA_DIR/config/abstractcore.json`. Target one
runtime explicitly with:

```bash
abstractgateway-config set-default input.text \
  --scope user \
  --tenant default \
  --user alice \
  --provider endpoint:alice-openai \
  --model gpt-4.1
```

Gateway still has deployment settings such as host, port, auth, store backend, and the Core server
URL/token it uses to reach the execution host. Those are Gateway internals, not framework model
defaults.

## AbstractFlow UI

AbstractFlow authoring surfaces treat blank provider/model pins as
`Auto (Gateway default)`. This is the preferred portable setting for LLM Call,
Agent, and generative media nodes because the saved workflow does not bake in a
deployment-specific provider/model.

Provider dropdowns include `Auto (Gateway default)` as the first option so a
user can switch back after pinning a provider.

The AbstractFlow Model Residency modal is loaded-state only:

- **Loaded models**: provider-reported runtime residency.

Changing a default route does not load a model. Loading/unloading is an operator action against the
provider/runtime residency surface. Configure capability defaults in Gateway
Console or with the Core/Gateway config CLIs.

## Related

- ADR: `docs/adr/0035-capability-routing-defaults.md`
- Gateway configuration: `abstractgateway/docs/configuration.md`
- Core configuration: `abstractcore/docs/centralized-config.md`

--- docs/guide/runtime-scope.md ---
# Runtime Scope (run / session / global / all)

This guide defines the meaning of **scope** across AbstractRuntime's memory effects and host UX.

## Why scope exists

The runtime stores durable "memory" under a runtime-owned namespace in run state. A scope decides **which durable owner**
receives reads/writes, and therefore which runs share memory.

## Scopes

### `run`

- The current `run_id`.
- Best for per-run working memory (notes/tags/compaction spans that should not leak outside this run).

### `session`

- Shared across runs that share the same `session_id`.
- Use when you want continuity across multiple runs launched by the same client "session".

Important: `session` is a host contract. If a host does not provide a stable `session_id`, session scope may degrade to
"per-run" behavior.

### `global`

- A single global owner shared across the whole runtime instance.
- Use for durable cross-session memory (preferences, stable facts).

### `all`

`all` is a query fan-out scope used by some operations to search across:
- `run`
- `session`
- `global`

This is why `global != all`:
- `global` means "only the global owner"
- `all` means "run + session + global"

## Practical examples

- "Remember this for the rest of this run": `scope=run`
- "Remember this for this user session": `scope=session`
- "Remember this forever": `scope=global`
- "Search everything I know": `scope=all`

## What survives a backend restart?

Scope controls *which durable owner* receives reads/writes, but persistence depends on the host stores:

- With file-backed stores (run store + ledger store + artifact store), `run`/`session`/`global` scoped memory persists.
- With in-memory stores, scope still works, but everything is lost on restart.

## Gateway API note (important)

If you start runs via the gateway and you want `scope=session` behavior across multiple runs, you must send a stable
`session_id` when starting those runs.


--- docs/guide/runtime-artifacts.md ---
# Runtime artifacts and retrieval

This guide explains how to investigate runtime resources without conflating
artifacts, ledgers, provider traces, and semantic memory.

## Responsibility map

| Layer | Responsibility |
|---|---|
| AbstractRuntime | Stores runs, ledgers, waits, artifacts, artifact descriptors, media facts, and access stats. Runtime is the source of truth for artifact identity and canonical descriptors. |
| AbstractGateway | Exposes Runtime resources over HTTP, applies auth/RBAC, projects `artifact_envelope_v1`, provides exact stats/facets/paging, and adds action links where available. |
| AbstractObserver | Visualizes Gateway data. Observe explains workflow narratives from ledger records; Runtime Activity supervises runs; Artifact Explorer inventories artifacts. |
| AbstractMemory / KG | Stores semantic memory and knowledge graph records. It is for concept/entity/relationship retrieval, not byte-level artifact inventory. |
| AbstractSemantics | Validates semantic predicates and schema-level meaning where KG/memory data is used. |

## Artifact vs local/server file sources

Use these terms consistently when a hosted client works with files:

- `Artifact`: the durable Runtime-owned file payload. This is what Artifact
  Explorer inventories.
- `Local File`: a client-device source. In hosted/browser mode it is uploaded
  and stored as an Artifact before durable execution.
- `Server File`: a user-facing label for a file inside Gateway-approved
  workspace scope. For artifact-style inputs it may be imported into a new
  Artifact; for path-based operations it remains a workspace-scoped server path
  whose availability depends on current Gateway policy and grants.

This guide is about Artifacts. A server workspace file that has not been
imported or produced as an Artifact will not appear in Artifact Explorer.

## Which surface to use

Use **Observe** when the question is about a workflow: what was requested, what
steps ran, what is waiting, what failed, what subworkflows exist, and what the
final outcome was. Observe replays the run ledger and can stream updates.

Use **Runtime Activity** when the question is operational: what is running,
waiting, failed, scheduled, stale, or needs a human action. Activity rows expose
open Observe, open ledger, open artifacts, open logs, copy ids, and cancel/stop
actions where Gateway authorizes them. Queue counts are for the loaded run page
unless Gateway exposes a broader run-stats endpoint.

Use **Artifact Explorer** when the question is about outputs: images, markdown,
HTML, JSON, voice, music, transcripts, documents, workflow snapshots, and other
files. Artifact Explorer uses Gateway artifact search with exact stats/facets
when available and pages the result set.

If the question is “what files are available in the server workspace for this
run?”, use workspace/file-helper surfaces instead. Artifact search only answers
questions about stored artifacts.

Use **Mindmap/KG** when the question is semantic: entities, relationships,
notes, memories, and graph-level knowledge. Do not use artifact metadata search
as a substitute for KG retrieval.

Use **Gateway audit/provider links** for host-level request traces. Audit tails
are global system activity unless an artifact envelope or ledger record links a
trace to a specific run/artifact.
Gateway artifact envelopes only expose safe relative Gateway/UI action links;
external provider URLs should be converted into Gateway-owned trace records or
redacted trace artifacts before users open them.

## Artifact search contract

Gateway artifact search is the canonical thin-client query path:

```bash
curl -sS -H "$AUTH" \
  "$BASE_URL/api/gateway/artifacts/search?scope=all&artifact_kind=music,voice&include_stats=true&limit=500"
```

Useful filters include `scope`, `session_id`, `run_id`, `artifact_kind`,
`semantic_kind`, `render_kind`, `modality`, `content_type`, `workflow_id`,
`node_id`, `created_after`, `created_before`, `query`, and `tags`.

Use `include_stats=true` when a UI needs exact totals, byte totals, or facet
counts. The stats are independent of the current page limit. Use `limit`,
`offset`, and cursors for bounded pages.

`artifact_kind` is UI-oriented. Canonical filters should prefer descriptor
fields:

- `semantic_kind=music` for generated music.
- `semantic_kind=voice` for TTS/voice artifacts.
- `render_kind=markdown` for Markdown rendering.
- `render_kind=html` for HTML source rendering and full-page preview.
- Generic `audio` should be treated as unclassified audio, not as voice or
  music unless the descriptor says so.

## Provenance and descriptors

Runtime-owned artifact descriptors are the reliable source for artifact meaning.
For descriptor-aware generated media, the envelope may include:

- producer package, capability route, provider, model, backend;
- prompt or TTS text, requested format, redacted parameters, output index;
- source artifacts used for edits, reference media, image-to-video, or other
  derivations;
- media facts such as dimensions, sample rate, channels, duration, and frame
  counts;
- links back to run, workflow, node, turn, ledger cursor, and provider trace
  availability.

Missing descriptor fields mean the producer did not record that fact or the
artifact is legacy. Consumers should show that absence plainly.

## Direct Runtime inspection

When investigating from the filesystem, start with the Gateway data directory
or runtime root configured by `ABSTRACTGATEWAY_DATA_DIR`. Artifact bytes,
metadata, and catalog state are Runtime-owned. Prefer Gateway APIs when the
server is running because Gateway applies authorization and records access
actions for previews/downloads.

For package-level details, see:

- `abstractruntime/docs/artifacts.md`
- `abstractruntime/docs/api.md#artifacts-store-by-reference`
- `abstractgateway/docs/api.md#artifacts-and-filesystem-handoff`
- `abstractobserver/docs/architecture.md#runtime-boundary`

## Privacy and safety

Artifact metadata can include prompts, source refs, provider/model ids, and
bounded generation parameters. Do not dump raw provider payloads, secrets, or
large artifact contents in summaries. Prefer ids, counts, descriptors, and
redacted snippets unless the user explicitly asks to inspect a non-image
artifact's content.

--- docs/guide/flow-and-kg-memory.md ---
# Flow + KG Memory (Memory object v0) - Guide

This guide explains how to configure KG memory recall + writeback in VisualFlow using the first-class `memory` object.

It is written for novice users of AbstractFlow and focuses on practical, step-by-step setup.

## What is the `memory` object?

`memory` is a JSON-safe object that groups "what memory the model can use" and "how KG memory is queried/written" into a
single value.

It is designed to:
- keep Agent / LLM Call nodes clean (one pin instead of many),
- be portable (host can pass one object),
- stay backward compatible (legacy per-pin keys still work).

Where it's used:
- Agent node: connect `memory` -> `agent.memory`
- LLM Call node: connect `memory` -> `llm_call.memory`
- RunnableFlow start (optional): `On Flow Start.memory` can be supplied by the client/run modal

## Quick start (recommended): use the Memory literal node

1. In AbstractFlow, add a **Memory** node (Literals -> Memory).
2. Edit its JSON to your desired configuration.
3. Connect:
   - `Memory.value` -> `Agent.memory` (or `LLM Call.memory`)
4. Run the flow and inspect the node trace:
   - When `use_kg_memory=true`, the model should receive a bounded "KG ACTIVE MEMORY" system block when recall finds
     relevant items.

## Provide memory from the client (RunnableFlow)

RunnableFlow workflows (interface `abstractcode.agent.v1`) can accept a `memory` input at start:

1. Ensure your `On Flow Start` node exposes an output pin `memory` (type `memory`).
2. Wire `On Flow Start.memory` into your Agent/LLM Call.
3. In your client (run modal / API), set `memory` as JSON.

### Best practice: defaults + overrides

If you want the flow to have good defaults but still allow overrides from the client:

1. Create a Memory (defaults) literal node (typed `memory`).
2. Ensure `On Flow Start` provides `memory = {}` by default.
3. Merge defaults + overrides and use that output as the effective memory object.

## Memory object schema (v0)

All keys are optional. Omitted keys mean "no override" (runtime defaults apply).

### Recall/source controls (Agent + LLM Call)

- `use_session_attachments: boolean`
- `use_span_memory: boolean`
- `use_semantic_search: boolean` (reserved; not implemented in v0)
- `use_kg_memory: boolean`
- `memory_query: string` (defaults to the node prompt/task if omitted)
- `memory_scope: string` (`run | session | global | all`)
- `recall_level: string` (`urgent | standard | deep`)
- `max_span_messages: number`
- `kg_max_input_tokens: number`
- `kg_limit: number`
- `kg_min_score: number` (0..1)

### KG write defaults (useful for ingest subflows)

- `kg_write_scope: string` (`run | session | global`)
- `kg_domain_focus: string`
- `kg_max_out_tokens: number` (0 means "no cap")

## Example memory object

```json
{
  "use_session_attachments": true,
  "use_span_memory": false,
  "use_semantic_search": false,
  "use_kg_memory": true,
  "memory_query": "",
  "memory_scope": "session",
  "recall_level": "standard",
  "max_span_messages": 24,
  "kg_max_input_tokens": 1200,
  "kg_limit": 80,
  "kg_min_score": 0.35,
  "kg_write_scope": "session",
  "kg_domain_focus": "software / agents / memory systems",
  "kg_max_out_tokens": 0
}
```

## Query and insert (KG recall + KG writeback)

### Query (recall)

To make an Agent/LLM Call use KG recall:
1. Set `memory.use_kg_memory = true`.
2. Optionally set `memory.memory_query` (otherwise the prompt is used).
3. Choose `memory.memory_scope`:
   - `session` is common when you want continuity within one client session.
   - `global` is common when you want durability across independent runs.

### Insert (writeback)

To write new knowledge into the KG, run an ingestion step/subflow after you produce an answer.
The typical pattern is to build a turn transcript string and pass it to an extractor that writes assertions using:
- scope from `memory.kg_write_scope` (or a fixed literal)
- domain hint from `memory.kg_domain_focus`


--- docs/guide/scheduled-workflows.md ---
# Scheduled Workflows (Durable Jobs)

This guide explains how scheduled workflows work when using `abstractgateway` + `abstractruntime`.

## What "scheduled workflows" means in AbstractFramework

A scheduled workflow is a durable parent run that triggers a target workflow as child runs over time.

Key properties:
- Durable: schedule state is persisted and survives restarts.
- Replay-first observability: you can attach later and reconstruct what happened from the ledger.
- Single authority: the gateway host owns ticking/resuming.

## Who "runs" the schedule (and why it stops when processes stop)

AbstractRuntime is a library. It can represent "wait until time X", but it does not create OS timers or wake itself up.

Something must keep calling `Runtime.tick(...)` to advance runs. In the gateway topology, the gateway runner loop:
- ticks running runs
- resumes due waits (including "wait until" for scheduled jobs)
- applies durable commands (pause/resume/cancel/emit_event)

If you stop the gateway process, nothing ticks. When it restarts, due waits resume on the next poll cycle.

## Creating a scheduled run (Gateway HTTP API)

Endpoint: `POST /api/gateway/runs/schedule`

Example: start now, repeat every 20 minutes forever:

```bash
curl -sS -X POST "http://127.0.0.1:8080/api/gateway/runs/schedule" \
  -H "Authorization: Bearer $(cat "$ABSTRACTGATEWAY_DATA_DIR/auth/bootstrap-admin-token")" \
  -H "Content-Type: application/json" \
  -d '{
    "bundle_id": "my-bundle",
    "flow_id": "root",
    "input_data": { "prompt": "write my report" },
    "start_at": "now",
    "interval": "20m",
    "share_context": true
  }'
```

Example: start at a specific time (UTC ISO), run 10 times:

```bash
curl -sS -X POST "http://127.0.0.1:8080/api/gateway/runs/schedule" \
  -H "Authorization: Bearer $(cat "$ABSTRACTGATEWAY_DATA_DIR/auth/bootstrap-admin-token")" \
  -H "Content-Type: application/json" \
  -d '{
    "bundle_id": "my-bundle",
    "flow_id": "root",
    "input_data": { "prompt": "write my report" },
    "start_at": "2026-01-15T15:06:00+00:00",
    "interval": "20m",
    "repeat_count": 10
  }'
```

Notes:
- Intervals are relative (drift is expected if runs take time or the gateway is down).
- If a child run blocks on a durable wait (ask-user, approvals, wait-event), the schedule blocks too (it waits for the
  child to finish).
- Durable execution is not transactional I/O (at-least-once semantics). Prefer idempotent outputs for scheduled jobs.

## Controlling scheduled runs

Scheduled runs are normal durable runs:
- Pause the parent run to pause the schedule.
- Cancel the parent run to stop the schedule and cancel active children.

This is done via `POST /api/gateway/commands`.

--- docs/guide/prompt-caching.md ---
# Prompt Caching (Prompt / KV)

This guide explains how the framework should think about caching for LLM calls, with an emphasis on prompt/KV caching
(prefill reuse) rather than response memoization.

## Terminology (three different "caches")

1. Response cache (exact/semantic)
   - Memoize final model outputs keyed by the input request.
   - Useful for repetitive questions, but risky for correctness drift.

2. Prompt/KV cache (prefix/prefill reuse)
   - Reuse the model's internal KV state for repeated prompt prefixes.
   - This can dramatically reduce time-to-first-token for long prompts with stable prefixes.

3. Composable KV modules (advanced)
   - Precompute caches for separate chunks (docs, history) and stitch them later.
   - This is typically an engine-level feature and not guaranteed uniformly across providers.

## AbstractCore support (best-effort)

AbstractCore exposes a provider-optional prompt cache surface. Depending on provider/backend, it may be:
- fully supported (in-process local backends),
- pass-through to an upstream server,
- or a no-op.

Typical usage patterns:

```python
from abstractcore import create_llm

llm = create_llm("mlx", model="mlx-community/Qwen3-4B")
llm.prompt_cache_set("tenantA:session123")  # also sets the default key

llm.generate("Hello")
llm.generate("Continue, but shorter.")
```

Or per-call:

```python
resp = llm.generate("Summarize this.", prompt_cache_key="tenantA:session123")
```

## Gateway/runtime note

In gateway-first deployments, prompt caching (when enabled) should be scoped per user/session to avoid accidental
cross-user reuse. Prefer stable `session_id` values so cache keys remain stable across multiple runs.


--- docs/guide/workflow-bundles.md ---
# WorkflowBundles (`.flow`) and Lifecycle

WorkflowBundles (`.flow`) are the portable distribution unit for VisualFlow workflows:

- a zip bundle containing `manifest.json` + `flows/*.json` (and optional assets)
- entrypoints can advertise interface contracts (for example `abstractcode.agent.v1`) for discovery across clients

## Where bundles live (gateway-first)

On a gateway host, configure a bundles directory:

```bash
export ABSTRACTGATEWAY_FLOWS_DIR="/path/to/workflows"   # contains *.flow
```

The gateway discovers and serves bundles to thin clients via discovery endpoints.

Hosted gateways now distinguish two bundle registries:

- **Private runtime bundles** live in the caller's routed runtime and are served
  through `/api/gateway/bundles`. AbstractFlow's normal save/publish/test loop
  continues to use this private surface.
- **Workflow catalog bundles** live in the Gateway control plane and are served
  through `/api/gateway/workflow-catalog`. Admins upload or promote immutable
  `.flow` versions, move explicit default pointers, set ACLs, and deprecate,
  block, or tombstone versions without deleting bundle bytes.

Catalog workflows run in the requesting user's runtime by default. Clients
start them with `registry_scope: "tenant_catalog"` plus `bundle_id`,
`bundle_version` when an exact version is required, and `flow_id`. If the
version is omitted, Gateway resolves the admin-managed default pointer rather
than guessing from semantic version order. Exact older versions keep working
until that specific version is deprecated, blocked, or tombstoned.

Catalog starts must be explicit. If `registry_scope` is omitted, Gateway treats
the request as a private-runtime bundle start and will not silently fall through
to the shared catalog. Catalog run policy is Gateway-issued and signed before it
is passed into Runtime state; clients cannot authorize catalog subworkflow
starts by sending their own `_runtime.workflow_policy`.

## Recommended lifecycle controls

- Publish/install new versions instead of editing deployed bundles in place.
- Deprecate workflows instead of deleting:
  - hides from discovery
  - blocks new starts
  - keeps old versions available for replay/audit of historical runs
- For shared/default workflows, use the workflow catalog. Do not overwrite
  catalog bundle bytes for an existing `bundle_id@version`; publish a new
  immutable version and move the default pointer.
- `framework_catalog` is reserved for a later cross-tenant catalog. The
  implemented shared catalog scope today is `tenant_catalog`.

## See also

- [Scenario: Publish, install, and deprecate workflows](../scenarios/workflow-bundle-lifecycle.md)

--- docs/guide/agent-skills.md ---
# Agent Skills (SKILL.md) — Proposal

This guide captures a **planned** integration of the Agent Skills (`SKILL.md`) format into AbstractFramework.
Nothing in this document implies the feature is already shipped; it records a design direction so the knowledge
is not lost.

## What are “Agent Skills”?

In the Agent Skills ecosystem, a **skill** is a shareable folder with a required `SKILL.md` (YAML frontmatter +
instructions) plus optional `scripts/`, `references/`, and `assets/`. Skills are designed for **progressive disclosure**:
systems load only `name`/`description` for discovery and fetch the full content only when a skill is activated.

Spec constraints worth carrying into AbstractFramework:
- `name` is constrained (lowercase alphanumeric + hyphens, 1–64 chars) and must match the skill’s leaf directory name.
- `allowed-tools` is **experimental** and is a space-delimited list of “pre-approved” tools in the originating ecosystem.

## Skills vs flows (what each is “for”)

- **Flows** (`.flow` bundles / VisualFlow) are **executable programs** in AbstractFramework:
  durable execution, explicit waits, tool boundaries, replayable ledger history.
- **Skills** (`SKILL.md`) are **portable procedure packs**:
  prompt/instructions + optional scripts/resources, designed to be shared across agents/ecosystems.

### Are flows “more advanced” than skills?

They’re “more advanced” in different dimensions:

- **Execution**: flows are more advanced (they *run* as durable state machines).
- **Portability**: skills are more advanced (they’re a widely adopted, tool-agnostic packaging standard).

### Can every skill be modeled as a flow?

**Conceptually yes**: a skill is “a procedure + resources”, and a flow can orchestrate any procedure.
In practice, the limit is **tooling/environment assumptions**, not the flow model:
a skill may assume Playwright is installed, or a specific “container” tool surface exists.

Also, not every skill is *worth* turning into a dedicated flow:
“guidelines/checklists/style” skills often work best as **prompt modules** attached to a generic agent flow.

## Proposed interaction model (v0)

The key design choice is: **flows run; skills are activated/loaded** (and skill scripts only run as explicit tools).

### 1) Run-attached skills (primary)

- Clients/hosts attach an `available_skills` metadata set to a run (name/description only).
- Users explicitly activate a skill (e.g. `/skill <name>`), which loads full `SKILL.md` and any resources.
- Activation is logged durably (ledger record). For replay/resume safety, activation should snapshot skill content
  (or at least record a content hash) so a long-running run doesn’t silently pick up a modified skill file.

### 2) Bundle-declared skill dependencies (secondary)

Flows may declare skill dependencies without changing the `.flow` format by using `manifest.metadata`, for example:

- `skills.required`: skills that must be present on the host/gateway
- `skills.defaults`: skills to auto-activate at run start

This lets organizations ship a workflow that says “this workflow expects these skills”, while keeping skills
as separately managed artifacts.

### 3) Bundle-embedded skills (optional; later)

WorkflowBundles support `assets/*`. If we want hermetic distribution (“workflow + skills in one file”),
we can embed skills under bundle assets (e.g. `assets/skills/<id>/...`) and expose them via the gateway.

## Implementation notes (what fits best with existing runtime/flow mechanics)

The lowest-friction implementation in AbstractFramework is to add skills as **runtime-owned tools** in the
AbstractRuntime ↔ AbstractCore integration (the same pattern already used for `open_attachment`):

- `list_skills()` for metadata-only discovery (progressive disclosure).
- `open_skill(...)` to load full `SKILL.md` (and optionally specific resources), snapshotting large payloads as artifacts
  and recording a content hash.
- optional `activate_skill(...)` to update durable run state (`_runtime.skills.active`) and apply `allowed-tools` as a
  restriction (intersection with the run’s tool allowlist).

Because these are tools, **flows can compose over skills immediately** using existing Tool/CallTool nodes, and agent
loops can activate skills without introducing new effect types.

Note: bundle-declared dependencies via `manifest.metadata.skills.*` are supported as a pattern, but VisualFlow JSON
currently has no `metadata` field; making this authorable requires a UI/CLI surface (or a schema extension).

## Safety and tool gating

- Skills may include scripts. **Scripts must never run implicitly** “because a skill exists”.
- Skills may declare `allowed-tools` (ecosystem field; experimental). In AbstractFramework the safe behavior is:
  - treat it as a **restriction** when it can be mapped to AbstractFramework tool names (intersection with the run’s tool allowlist),
  - if it cannot be mapped (unknown grammar/tool ids), emit `#FALLBACK` and **do not relax** the run’s tool policy,
  - deny and log out-of-policy tool calls with actionable `#FALLBACK` warnings,
  - keep run state JSON-safe (store bodies/resources as artifacts when large).

## Where this fits (packages)

- `abstractagent`: prompt injection of metadata; “activate skill” as an explicit step; optional schema-only built-in
  (e.g. `open_skill`) so the runtime/host performs the read.
- `abstractruntime`: runtime-owned “skill read/activate” handler that is durable + ledger-recorded + artifact-backed,
  plus enforcement hooks for `allowed-tools`.
- `abstractgateway`: optional “skills registry” (list/fetch/install/deprecate), parallel to `.flow` bundle distribution.
- `abstractcore`: stays lean; any provider-specific “container skills” integration remains optional and gated.

## References and next steps

- Backlog item: `docs/backlog/planned/074_agent_skills_integration.md`
- Implementation plan (phased): `docs/backlog/planned/074_agent_skills_integration_plan.md`
- Research notes (spec + ecosystem scan): `docs/skills/`

--- docs/guide/telegram-integration.md ---
# Telegram Integration (Gateway Bridge + Workflow)

This guide explains how to run a Telegram "permanent contact" that talks to an agent workflow through the gateway.

Telegram is implemented as a **thin client**:
- Inbound Telegram messages -> gateway starts a new run per message (stable `session_id` for durable memory)
- Outbound replies -> the bridge sends the run output back to Telegram
- Attachments -> stored in the ArtifactStore and passed as `context.attachments` / `context.media`

## Security model choices

Telegram has two integration paths:

1. TDLib + Secret Chats (E2EE in transit; recommended)
2. Bot API (easy, not E2EE)

Even with E2EE, messages are decrypted on the gateway host and persisted to durable stores in plaintext by design. Secure
the gateway host and its storage.

## Access control (critical)

Telegram bots and user accounts are discoverable. Without access control, anyone who finds the handle can message it and
trigger durable runs + LLM calls.

The bridge is **fail-closed by default**:
- DMs: `ABSTRACT_TELEGRAM_DM_POLICY=allowlist` (default) — only allowlisted Telegram `user_id`s are processed.
- Groups: `ABSTRACT_TELEGRAM_GROUP_POLICY=disabled` (default) — all group/supergroup/channel messages are ignored.
- Unauthorized messages are ignored (no run created, no token spend). `/whoami` always works.

Commands:
- `/whoami` — always works; prints your `user_id` and `chat_id` (useful for allowlists).
- `/pair ...` — pairing workflow (DM only; only when `ABSTRACT_TELEGRAM_DM_POLICY=pairing`).

Minimal env vars (Bot API + DM allowlist):

```bash
export ABSTRACT_TELEGRAM_BRIDGE=1

# Bot API (easy, not E2EE). If ABSTRACT_TELEGRAM_BOT_TOKEN is set, transport defaults to bot_api.
export ABSTRACT_TELEGRAM_BOT_TOKEN="..."  # from @BotFather

# Allowlisted DM users (numeric Telegram user_id). Use /whoami to discover yours.
# Accepts comma/newline-separated ints or JSON list.
export ABSTRACT_TELEGRAM_ALLOWED_USERS="123456789"
```

## Quickstart (DM-only allowlist)

This is a practical checklist to verify access control + approvals end-to-end.

1. Configure the bridge (Bot API):

```bash
# Gateway (required for `abstractgateway serve`).
export ABSTRACTGATEWAY_FLOWS_DIR="/path/to/bundles"  # directory containing *.flow bundles (incl. shipped `basic-agent`)
export ABSTRACTGATEWAY_AUTH_TOKEN="$(python -c 'import secrets; print(secrets.token_urlsafe(32))')"

export ABSTRACT_TELEGRAM_BRIDGE=1
export ABSTRACT_TELEGRAM_BOT_TOKEN="..."               # from @BotFather
```

2. Start the gateway.
   - You may see a warning about an empty DM allowlist; this is expected until you set `ABSTRACT_TELEGRAM_ALLOWED_USERS`.
   - Then DM the bot and run `/whoami`.
   - Copy your `user_id` (the bot also prints an `export ABSTRACT_TELEGRAM_ALLOWED_USERS="..."` hint).

3. Set your allowlist and restart the gateway:

   - `export ABSTRACT_TELEGRAM_ALLOWED_USERS="123456789"`

4. Verify:
   - From an allowlisted account, send “hi” → expected: you receive a reply.
   - From a non-allowlisted account, send “hi” → expected: ignored (except `/whoami`).

5. Verify tool approvals:
   - In the chat, ask: `run free -m` (or `uname -a`).
   - Expected: the bot asks you to reply `/approve` before executing `execute_command`.
   - Expected: after `/approve`, you receive the final answer/result in Telegram (read-only tools like `web_search` should not require approval).

## Optional: pairing mode (DMs)

Pairing lets unknown users request access without editing `ABSTRACT_TELEGRAM_ALLOWED_USERS`, but it requires an admin.

```bash
export ABSTRACT_TELEGRAM_DM_POLICY="pairing"
export ABSTRACT_TELEGRAM_ADMIN_USERS="123456789"   # your operator user_id (use /whoami)
export ABSTRACT_TELEGRAM_PAIRING_TTL_S="3600"      # optional
```

## Optional: group chat support

Group chats are disabled by default. To enable allowlisted groups:

```bash
export ABSTRACT_TELEGRAM_GROUP_POLICY="allowlist"
export ABSTRACT_TELEGRAM_ALLOWED_CHATS="-100123456789"  # use /whoami inside the group to discover chat_id
# Optional (default true): require @mention in groups
export ABSTRACT_TELEGRAM_REQUIRE_MENTION_IN_GROUPS=1
```

## Viewing Telegram sessions in AbstractCode

Telegram runs are durable; you can replay them from any thin client.

1. Open AbstractCode (default): http://localhost:3002
2. Go to **History**, click **Refresh**, then open the Telegram session (e.g. `telegram:<chat_id>:r<rev>`).

Notes:
- Each incoming Telegram message creates a new run under the same `session_id` (durable memory across turns).

## Minimal gateway configuration

Install Telegram support:

```bash
pip install abstractgateway
# Optional (only if your workflows call Telegram tools like `send_telegram_message`):
# pip install "abstractcore[tools]"
```

Set env vars on the gateway host:

```bash
export ABSTRACTGATEWAY_FLOWS_DIR="/path/to/bundles"  # directory containing *.flow bundles
export ABSTRACTGATEWAY_AUTH_TOKEN="$(python -c 'import secrets; print(secrets.token_urlsafe(32))')"  # required

export ABSTRACT_TELEGRAM_BRIDGE=1

# Bot API (easy, not E2EE). If ABSTRACT_TELEGRAM_BOT_TOKEN is set, transport defaults to bot_api.
export ABSTRACT_TELEGRAM_BOT_TOKEN="..."
export ABSTRACT_TELEGRAM_ALLOWED_USERS="123456789"  # use /whoami

# Tool execution + approvals:
# - `approval` (default): safe tools run in-process; dangerous/unknown tools still require a Telegram reply: `/approve` or `/deny`.
# - `passthrough`: delegated execution (advanced; not recommended for thin clients).
export ABSTRACTGATEWAY_TOOL_MODE="approval"

# Optional: override which workflow to run per message.
# Default (when unset): shipped `basic-agent` bundle entrypoint.
# export ABSTRACT_TELEGRAM_BUNDLE_ID="basic-agent"
# export ABSTRACT_TELEGRAM_FLOW_ID="81795ea9"
```

Notes:
- Default LLM routing comes from the execution-host `input.text` capability
  route. Set it with `abstractcore --set-global-default ...` or
  `abstractgateway-config set-default input.text ...`.
- Telegram-only routing override (does not affect other gateway traffic): set `ABSTRACT_TELEGRAM_MODEL="..."` (and optionally `ABSTRACT_TELEGRAM_PROVIDER="..."`).
- Durable history limit: `ABSTRACT_TELEGRAM_MAX_HISTORY_MESSAGES` (default: 30; `0` keeps only system messages).
- STT fallback and vision caption fallback are configured via `abstractcore --config` (audio strategy + vision fallback).
- Telegram typing keepalive is best-effort: tune with `ABSTRACT_TELEGRAM_TYPING_INTERVAL_S` (default: 4s) and `ABSTRACT_TELEGRAM_TYPING_MAX_S` (default: 600s; set to `0` to disable).
- `/reset` behavior is best-effort: the bridge clears the durable session, sends a confirmation message, and optionally deletes recent messages in the background. Controls: `ABSTRACT_TELEGRAM_RESET_DELETE_MESSAGES` (default: true), `ABSTRACT_TELEGRAM_RESET_DELETE_MAX` (default: 200), `ABSTRACT_TELEGRAM_RESET_MESSAGE` (confirmation text). Telegram may still reject deletions depending on chat permissions and age.

Start the gateway normally.

## Local dev test (Bot API + LMStudio)

If you're in the AbstractFramework repo, you can use `./execute.sh` as a convenience env setup:

```bash
source ./execute.sh
```

1. Start LMStudio “Local Server” and load `google/gemma-3n-e4b`.
2. Set the text output route for this run:

```bash
abstractgateway-config set-default input.text \
  --provider lmstudio \
  --model google/gemma-3n-e4b \
  --base-url http://127.0.0.1:1234/v1
export LMSTUDIO_BASE_URL="http://127.0.0.1:1234/v1"
```

3. Ensure the gateway can see the shipped bundle (a directory containing `*.flow`):

```bash
export ABSTRACTGATEWAY_FLOWS_DIR="/path/to/bundles"  # e.g. ./abstractgateway/flows/bundles in this repo
```

4. Send a Telegram message to the bot and verify:
   - you receive a reply
   - a follow-up (“What did I just say?”) works (durable memory)
   - media messages (photo/voice/video/document) are handled

## Workflow wiring (VisualFlow)

### Default workflow (recommended)

By default, the bridge runs the shipped `basic-agent` bundle entrypoint once per incoming message and sends the run output back to Telegram.
Durable memory comes from the stable Telegram `session_id` (no special workflow shape required).

### Custom workflow

Any flow that reads `prompt`/`context` (like `abstractcode.agent.v1`) and writes a string to `run.output.answer` or `run.output.response` will work.
The bridge provides Telegram metadata in `input_data.telegram` if you want to branch on it.

## TDLib notes (E2EE path)

TDLib requires:
- a real Telegram user account for the AI identity
- the TDLib shared library (`tdjson`) installed on the gateway host
- a persistent TDLib session directory (so you authenticate once)

Because TDLib is platform-specific, keep your setup steps close to your deployment scripts. The gateway integration code
and configuration surface live in:
- https://github.com/lpalbou/abstractgateway

## Testing checklist

1. Confirm the gateway loaded your bundles (API: `GET /api/gateway/bundles`) and that the configured Telegram flow exists.
2. Send a message to the bot; verify you get a reply and that Observer can replay the run.
3. Send a follow-up (“What did I just say?”) to confirm durable memory works.
4. Send media:
   - photo (with and without caption)
   - voice note (STT fallback depends on your `abstractcore --config` audio strategy + installed plugins)
   - video and document
5. Send `/reset` to clear the binding/runs, then confirm the next message starts fresh.

## Tool approvals (Telegram)

When the agent requests a tool call that requires explicit permission (for example `write_file` or `execute_command`),
the bridge sends an approval prompt into the chat. Reply with:
- `/approve` (or `approve`) to execute the tool calls and continue
- anything else to cancel the tool calls and let the workflow continue with failure results
 
Note: `/tools` is intentionally disabled in thin-client mode; use `/approve` and `/deny`.

--- docs/guide/email-integration.md ---
# Email Integration (Inbound IMAP -> Events, Outbound SMTP Defaults)

This guide explains how to use framework-native email tooling:

- Outbound: `send_email` with centralized SMTP defaults (no repeated host/user per call)
- Inbound: gateway email bridge polls IMAP and emits durable `email.message` events into stable sessions, with
  artifact-backed attachments

## Minimal outbound configuration (SMTP defaults)

Configure on the process that executes tools (gateway local tools, CLI host, or tool worker):

```bash
export ABSTRACT_EMAIL_SMTP_HOST="smtp.example.com"
export ABSTRACT_EMAIL_SMTP_USERNAME="me@example.com"
export ABSTRACT_EMAIL_SMTP_PASSWORD_ENV_VAR="EMAIL_PASSWORD"
export EMAIL_PASSWORD="..."
```

Then a minimal tool call can be:

```json
{ "name": "send_email", "arguments": { "to": "you@example.com", "subject": "Hello", "body_text": "Hi!" } }
```

## Minimal inbound configuration (gateway email bridge)

Configure IMAP + enable the bridge on the gateway host:

```bash
export ABSTRACT_EMAIL_BRIDGE=1
export ABSTRACT_EMAIL_IMAP_HOST="imap.example.com"
export ABSTRACT_EMAIL_IMAP_USERNAME="me@example.com"
export ABSTRACT_EMAIL_IMAP_PASSWORD_ENV_VAR="EMAIL_PASSWORD"
export ABSTRACT_EMAIL_POLL_SECONDS=60
```

Recommended for v0: auto-start a workflow per thread/session:

```bash
export ABSTRACT_EMAIL_FLOW_ID="<bundle_id>:<flow_id>"
```

## Workflow wiring

Create a flow that:
1. handles `email.message` (On Event; scope `session`)
2. reads `payload.email.*` (subject/from/body and artifact-backed attachments)
3. optionally opens attachments via `open_attachment(artifact_id=...)`
4. replies with `send_email(...)`

## See also

Gateway maintenance docs mention the bridge and email inbox endpoints:
- https://github.com/lpalbou/abstractgateway/blob/main/docs/maintenance.md


--- docs/guide/process-manager-env-vars.md ---
# Process Manager Env Vars (Write-only) - Operator Guide

This guide explains how to configure a small allowlist of environment variables on the gateway host via AbstractObserver,
without exposing the values back to browsers/clients.

## What this is (and why)

- Goal: configure framework integrations (for example email) from the UI.
- Security model:
  - allowlist-only keys (no arbitrary env var editing)
  - write-only values (the gateway API never returns env var values)
  - values are persisted on the gateway host with restrictive file permissions

## Requirements

- Gateway process manager enabled: `ABSTRACTGATEWAY_ENABLE_PROCESS_MANAGER=1`
- AbstractObserver connected to that gateway

## Where values are stored (host-side)

- `<ABSTRACTGATEWAY_DATA_DIR>/process_manager/env_overrides.json`

## How values are applied

- When you set/unset an allowlisted env var in the UI:
  - the gateway persists it to `env_overrides.json`
  - the gateway applies it to its own `os.environ` (so integrations reading env vars can see it)
- When the process manager launches managed processes, it merges:
  1. gateway `os.environ`
  2. allowlisted overrides
  3. per-process env (static)

If a service reads env vars only at startup, restart that service after changing env vars.

## Allowlisted keys

The allowlist is gateway-defined. Email-related keys are commonly allowlisted for inbox and SMTP defaults.

## See also

- AbstractGateway docs: https://github.com/lpalbou/abstractgateway


--- docs/backlog/overview.md ---
# AbstractFramework Backlog Overview

This root backlog is the framework-level planning ledger for cross-package work. Some older items
use legacy naming and duplicate numeric prefixes; new items should use four-digit global IDs and
the lifecycle folders described by the backlog process.

## Current Counts

- Planned: many legacy items plus active cross-package work.
- Proposed: legacy proposed items exist.
- Completed: historical completion ledger exists under `completed/`.
- Deprecated: not yet normalized at the root level.
- Recurrent: not yet normalized at the root level.

## Next Recommended Work

1. Investigate Observer wait handling as a replayable session chat/handoff instead of continuing to refine the narrow answer modal.
2. Design a first-class Session -> Turn -> Run/Subrun -> Artifact/Log hierarchy for Runtime Activity before adding more run-table complexity.
3. Complete the Observer/Manager responsibility split so high-trust admin/process controls stop competing with observability surfaces.
4. Add a Gateway run-stats endpoint if Activity queue counts must be exact across the whole runtime instead of the loaded run page.
5. Adopt `build_artifact_descriptor_payload(...)` in direct `abstractvision`, `abstractvoice`, and `abstractmusic` artifact writers that bypass the Runtime AbstractCore generated-media path.

## Active Planned Work

| ID | Item | Status | Notes |
|----|------|--------|-------|
| 0142 | [Gateway tenant isolation and shared runtime design](planned/0142_gateway_tenant_isolation_and_shared_runtime.md) | Planned | Define and implement tenant-aware Gateway/Runtime isolation; current shared Gateway deployments are single-user or trusted-cohort only. |
| 076 | [OpenAI Responses API integration](planned/076_openai_responses_api_integration.md) | Planned | Define a true Core Server `/v1/responses` contract, use native Responses transports where available, and require Core-owned first-class Responses adapters for MLX, HuggingFace, Anthropic, and non-native endpoint profiles. |
| 0143 | [Shared Gateway per-principal runtime router](planned/0143_shared_gateway_per_principal_runtime_router.md) | In progress | Gateway principal auth, admin user CRUD, per-principal GatewayService routing, and Flow browser-session routing landed; broader app auth and route-family isolation remain open. |
| 0145-0153 | [Gateway control plane track](planned/gateway-control-plane/README.md) | Planned | Gateway-owned admin/account/config/workflow-permission control plane; starts with responsibility, RBAC, and browser-session contracts before broad UI/app migration. |
| 0164 | [Gateway Docker GHCR deployment track](planned/0164_gateway_docker_ghcr_deployment_track.md) | In progress | Align Gateway containers with ADR-0033/0034: `ghcr.io/lpalbou/abstractgateway` light/GPU tags, user-auth bootstrap, `/data` volume, PyPI-based image builds, and Docker docs. |
| 0166 | [Gateway local user-auth bootstrap UX](planned/0166_gateway_local_user_auth_bootstrap_ux.md) | In progress | Native `abstractgateway serve` should match Docker by creating/printing the `default/admin` browser-login token path when user auth is enabled. |
| 0167 | [Gateway provider connection setup console](planned/0167_gateway_provider_connection_setup_console.md) | In progress | Providers tab now owns endpoint URL/key setup with Test/Confirm modals; Defaults now maps capability routes only to configured provider connections and discovered models. |
| 0168 | [Abstract release root profile pin guard](planned/0168_release_skill_root_profile_pin_guard.md) | In progress | Tighten the `abstract-release` skill so partial lower-package releases cannot silently leave root pins/docs behind. |
| 0179 | [LLM and Agent model input artifacts](planned/0179_llm_agent_model_input_artifacts.md) | Planned | Add first-class Flow model-input artifact-list authoring for LLM Call and Agent nodes, lowering ordered refs to Runtime media/context attachments while preserving Gateway artifact validation and Core route compatibility. |
| 0182-0186 | [VisualFlow recursion budget track](planned/visualflow-recursion-budget/README.md) | Planned | Runtime-enforced recursive Subflow budget defaulting to 3, Flow cycle detection and controls, Gateway observability, a separate same-flow feedback-loop budget, and ADR/docs/vocabulary cleanup. |
| 0200 | [Gateway experimental NVIDIA image publish fix](planned/0200_gateway_experimental_nvidia_image_publish_fix.md) | Planned | Repair the experimental NVIDIA Gateway image path so GHCR GPU tags publish for real instead of remaining best-effort release attempts. |

## Gateway Control Plane Planned Track

| ID | Item | Status | Notes |
|----|------|--------|-------|
| 0145 | [Gateway admin console bootstrap](planned/gateway-control-plane/0145_gateway_admin_console_bootstrap.md) | In progress | Console v0 now serves `/console` with session sign-in, account/runtime summary, admin user management, optional email, create/delete confirmations, token rotation, and discovered provider/model defaults; richer runtime activity remains optional follow-up. |
| 0146 | [Gateway RBAC scope policy matrix](planned/gateway-control-plane/0146_gateway_rbac_scope_policy_matrix.md) | In progress | Central route-family policy gates operator/server-workspace/model-residency surfaces, runtime ids are tenant-unique, and the Alice/Bob matrix now covers runs, ledgers, artifacts, session artifacts, KG/session memory, private workflows, prompt-cache naming, runtime-scoped defaults, workspace helper denials, and discovery leak behavior. |
| 0147 | [Gateway per-principal config, secrets, and defaults](planned/gateway-control-plane/0147_gateway_per_principal_config_secrets_defaults.md) | In progress | Gateway-baseline plus per-user capability-default overlays and console UX landed; raw provider-secret storage/injection remains deliberately deferred pending a Core/Gateway secret boundary. |
| 0148 | [Gateway workflow registry ACLs](planned/gateway-control-plane/0148_gateway_workflow_registry_acl.md) | In progress | Immutable tenant catalog versions, admin default pointers, ACL APIs, explicit catalog scope, signed run-start/schedule policy, host-side catalog guards, ACL-aware catalog inspection, and discovery metadata landed; per-tool/workspace/secret policy intersection and UI remain. |
| 0150 | [Observer and Manager responsibility split](planned/gateway-control-plane/0150_observer_manager_responsibility_split.md) | Planned | Early containment audit to keep Observer focused on observability and admin/config ownership in Gateway/Gateway Console or later Manager surfaces. |
| 0153 | [Gateway browser session security contract](planned/gateway-control-plane/0153_gateway_browser_session_security_contract.md) | In progress | Gateway/Flow opaque browser sessions, CSRF, logout, token-rotation revocation, Code/Observer hosted proxy convergence, and HTTP/HTTPS/origin/expiry/logout/revocation cookie matrix tests landed. |

## VisualFlow Recursion Budget Planned Track

| ID | Item | Status | Notes |
|----|------|--------|-------|
| 0182 | [Runtime recursive subworkflow budget](planned/visualflow-recursion-budget/0182_runtime_recursive_subworkflow_budget.md) | Planned | Runtime owns recursive call detection at `START_SUBWORKFLOW`, with default 3 recursive calls and stable over-limit failure semantics. |
| 0183 | [Flow recursive Subflow analysis and controls](planned/visualflow-recursion-budget/0183_flow_recursive_subflow_analysis_and_controls.md) | Planned | AbstractFlow detects direct/mutual Subflow cycles, warns in preflight, and exposes synchronized recursive-call controls without enforcing execution. |
| 0184 | [Gateway recursion observability and runner coverage](planned/visualflow-recursion-budget/0184_gateway_recursion_observability_and_runner_coverage.md) | Planned | Gateway projects Runtime results, preserves ledger/history visibility, and tests no-stuck-parent behavior without owning enforcement. |
| 0185 | [VisualFlow feedback loop budget](planned/visualflow-recursion-budget/0185_visualflow_feedback_loop_budget.md) | Planned | Same-flow improvement loops get their own runtime-enforced feedback-cycle budget, separate from recursive Subflow calls and Agent iterations. |
| 0186 | [Recursion contract ADR, docs, and iteration vocabulary](planned/visualflow-recursion-budget/0186_recursion_contract_adr_docs_and_vocabulary.md) | Planned | Create or revise the durable ADR contract, document counting/defaults, and align labels such as Recursive Subflow calls, Agent loop iterations, and Feedback loop cycles. |

## Runtime Artifact Observability Completed Track

| ID | Item | Status | Notes |
|----|------|--------|-------|
| 0198 | [Observer observability replay workbench](completed/runtime-artifact-observability/0198_observer_observability_replay_workbench.md) | Completed | Runtime history bundles now carry bounded replay artifact summaries and indexed best-effort session turns; Observer has a read-only Replay tab and Runtime Activity no longer exposes inline cancel controls. |
| 0197 | [Runtime artifact type OR filters and stable facets](completed/runtime-artifact-observability/0197_runtime_artifact_type_or_filter_facets.md) | Completed | Observer type chips now visibly combine with OR, selected results remain server-filtered, and chip counts come from the same non-type scope/search/date stats instead of the filtered result set. |
| 0194 | [Observer runtime activity monitor and wait actions](completed/runtime-artifact-observability/0194_observer_runtime_activity_monitor_and_wait_actions.md) | Completed | Runtime Activity now has explicit attention queues, a dense searchable/sortable run table, selected-run context/actions, direct cancel/stop, loaded-page count labeling, and offline/focus/responsive fixes. |
| 0193 | [Runtime artifact coredoc and explore skill](completed/runtime-artifact-observability/0193_runtime_artifact_coredoc_and_explore_skill.md) | Completed | Added Runtime artifact docs, root runtime-artifacts guide, package/root LLM updates, and a validated `runtime-explore` skill for bounded redacted investigations. |
| 0190 | [Media generation provenance and enrichment](completed/runtime-artifact-observability/0190_media_generation_provenance_and_enrichment.md) | Completed | Runtime/Gateway generated-media paths now store descriptors/structured metadata, preserve child projection provenance, redact sensitive metadata, and cover image/video/voice/music/transcript tests. |
| 0192 | [Observer canonical artifact explorer UI](completed/runtime-artifact-observability/0192_observer_canonical_artifact_explorer_ui.md) | Completed | Observer Runtime Artifact Explorer now consumes Gateway envelopes/stats, separates Voice/Music/Sound/Unclassified audio, shows server-backed counts/pages, previews content, and surfaces provenance/actions with explicit legacy labels. |
| 0191 | [Gateway artifact envelope, query, and provider traces](completed/runtime-artifact-observability/0191_gateway_artifact_envelope_query_and_provider_traces.md) | Completed | Gateway artifact search/list/detail now expose `artifact_envelope_v1`, exact stats/facets, bounded paging, access-action stats, action links, capability descriptors, and UI-safe `artifact_kind` filtering. |
| 0188 | [Artifact descriptor contract and ADR](completed/runtime-artifact-observability/0188_artifact_descriptor_contract_and_adr.md) | Completed | Added ADR-0036 and Runtime-owned `ArtifactDescriptor`/`ArtifactAccessStats` vocabulary with legacy projection and fallback labels. |
| 0189 | [Runtime artifact catalog and access stats](completed/runtime-artifact-observability/0189_runtime_artifact_catalog_and_access_stats.md) | Completed | Added descriptor-compatible Runtime metadata, explicit update/access APIs, exact counts/facets, filters, paging, and a repairable SQLite file-store catalog. |

## Active Proposed Work

| ID | Item | Status | Notes |
|----|------|--------|-------|
| 0144 | [User profile metadata for selective model grounding](proposed/0144_user_profile_context_grounding.md) | Proposed | Discuss first/last name, birth date, inferred country, provenance, and query-time selective context injection before implementation. |
| 0151 | [Runtime Explorer contract](proposed/gateway-control-plane/0151_runtime_explorer_contract.md) | Proposed | Reviewer consensus: start with a read-only Gateway envelope contract and Observer page for typed runtime resources; defer `abstractexplorer`, delete/export, raw workspace browsing, and admin cross-user exploration. |
| 0152 | [AbstractManager package extraction](proposed/gateway-control-plane/0152_abstractmanager_package_extraction.md) | Proposed | Revisit a separate `abstractmanager` package only after console/config/workflow ACL surfaces prove real maintenance or reuse pressure. |
| 0155 | [Hosted proxy shared helper extraction](proposed/gateway-control-plane/0155_hosted_proxy_shared_helper_extraction.md) | Proposed | Keep conformance tests now; extract a shared Node helper only if Code/Observer or future hosted apps drift again. |
| 0162-0163 | [Installer and setup track](proposed/installers/README.md) | Proposed | Prepare signed installer CI and evaluate a CPU-local profile after the extraction, generated manifest, doctor, and install chooser work landed. |
| 0169 | [Gateway Console route-specific default catalogs](proposed/0169_gateway_console_route_specific_default_catalogs.md) | Proposed | Decide the smallest Defaults-modal adapter for embeddings, image/video, voice, and music catalog filtering without moving URL/key setup out of Providers. |
| 0181 | [Code node managed Python packages with simple authoring UX](proposed/0181_code_node_managed_python_packages_simplified_ux.md) | Proposed | Preserve the package-install architecture guardrails while making the user process simple: write imports, confirm package chips, prepare/test through Gateway, and run with Runtime/worker-managed provenance. |
| 0195-0196 | [Runtime artifact observability proposed track](proposed/runtime-artifact-observability/README.md) | Proposed | Parked follow-ups for wait handling via replayable session chat/handoff and first-class Session -> Turn -> Run hierarchy in Observer Runtime Activity. |

## Runtime Artifact Observability Proposed Track

| ID | Item | Status | Notes |
|----|------|--------|-------|
| 0195 | [Observer wait handling via session replay and chat handoff](proposed/runtime-artifact-observability/0195_observer_wait_replay_chat_session_handoff.md) | Proposed | Investigate whether waiting runs should be answered through a replayable session/chat context rather than a narrow modal. |
| 0196 | [Observer session-turn-runtime hierarchy](proposed/runtime-artifact-observability/0196_observer_session_turn_runtime_hierarchy.md) | Proposed | Investigate a first-class Session -> Turn -> Run/Subrun -> Artifact/Log hierarchy for Runtime Activity and artifact/log navigation. |

## Installer And Setup Proposed Track

| ID | Item | Status | Notes |
|----|------|--------|-------|
| 0162 | [Signed installer CI and distribution](proposed/installers/0162_signed_installer_ci_and_distribution.md) | Proposed | Move from prototype builds to signed/notarized native installer artifacts with checksums and rollback/support logs. |
| 0163 | [CPU local inference install profile](proposed/installers/0163_cpu_local_inference_install_profile.md) | Proposed | Evaluate `abstractframework[cpu]` separately from Light; require package-by-package backend and dependency evidence before promotion. |

## Multimodal Capabilities Completed Track

| ID | Item | Status | Notes |
|----|------|--------|-------|
| 0175 | [Multimodal capability taxonomy and schema](completed/multimodal-capabilities/0175_multimodal_capability_taxonomy_schema.md) | Completed | Added route-keyed `capability_routes`, JSON Schema validation, Core helper APIs, route-aware `/v1/models`, Runtime forwarding, Gateway Console route filters, and `input.music` default-route support; media-policy helper migration remains a follow-up. |

## Multimodal Capabilities Proposed Track

| ID | Item | Status | Notes |
|----|------|--------|-------|
| 0176 | [Multimodal model acquisition guidance](proposed/multimodal-capabilities/0176_multimodal_model_acquisition_guidance.md) | Proposed | Explore CLI/doctor and later console guidance for downloading/loading configured local models without conflating acquisition with defaults. |

## Recent Completed Work

| ID | Item | Completed | Notes |
|----|------|-----------|-------|
| 0198 | [Observer observability replay workbench](completed/runtime-artifact-observability/0198_observer_observability_replay_workbench.md) | 2026-06-06 | Added bounded artifact summaries and indexed session-turn discovery to Runtime history bundles, plus an Observe Replay tab and monitor-only Runtime Activity actions. |
| 0199 | [AbstractFlow and AbstractAssistant vision LoRA and batch surface](completed/0199_abstractflow_and_abstractassistant_vision_lora_and_batch_surface.md) | 2026-06-14 | Flow now surfaces task-filtered provider/model discovery, `count`, ordered `seeds`, and stacked LoRA adapters in the media node authoring UI, while Assistant forwards the same route fields and adapter discovery through its Gateway thin-client path. |
| 0197 | [Runtime artifact type OR filters and stable facets](completed/runtime-artifact-observability/0197_runtime_artifact_type_or_filter_facets.md) | 2026-06-06 | Type chips now compose as OR, keep available counts visible from base facets, and Gateway regression coverage verifies mixed-kind artifact filters return a union. |
| 0194 | [Observer runtime activity monitor and wait actions](completed/runtime-artifact-observability/0194_observer_runtime_activity_monitor_and_wait_actions.md) | 2026-06-06 | Runtime Activity now separates operational supervision from artifact browsing with attention queues, readable waiting context, safe actions, honest loaded-page counts, and offline/focus/responsive fixes. |
| 0193 | [Runtime artifact coredoc and explore skill](completed/runtime-artifact-observability/0193_runtime_artifact_coredoc_and_explore_skill.md) | 2026-06-06 | Added cross-package artifact/retrieval docs, regenerated LLM docs, and validated the `runtime-explore` skill. |
| 0190 | [Media generation provenance and enrichment](completed/runtime-artifact-observability/0190_media_generation_provenance_and_enrichment.md) | 2026-06-06 | Added Runtime-owned descriptor payload helper, generated-media descriptors/metadata, Gateway projection preservation, STT descriptors, link sanitization, and media provenance tests. |
| 0192 | [Observer canonical artifact explorer UI](completed/runtime-artifact-observability/0192_observer_canonical_artifact_explorer_ui.md) | 2026-06-06 | Observer now uses Gateway-backed artifact envelopes/stats/pages, separates semantic media kinds from render formats, and exposes preview/provenance/action detail with focused tests/build/browser smoke validation. |
| 0191 | [Gateway artifact envelope, query, and provider traces](completed/runtime-artifact-observability/0191_gateway_artifact_envelope_query_and_provider_traces.md) | 2026-06-06 | Gateway now projects Runtime artifact descriptors into `artifact_envelope_v1`, exact stats/facets, bounded pages, access-action stats, and canonical/legacy-safe filters. |
| 0189 | [Runtime artifact catalog and access stats](completed/runtime-artifact-observability/0189_runtime_artifact_catalog_and_access_stats.md) | 2026-06-06 | Runtime artifact metadata now persists descriptors/access stats and supports exact counts, facets, filters, paging, and repairable file-store catalogs. |
| 0188 | [Artifact descriptor contract and ADR](completed/runtime-artifact-observability/0188_artifact_descriptor_contract_and_adr.md) | 2026-06-06 | Added ADR-0036 and the Runtime-owned artifact descriptor contract separating render kind, semantic kind, provenance, media facts, and legacy fallback labels. |
| 0187 | [Framework PDF profile pins](completed/0187_framework_pdf_profile_pins.md) | 2026-06-06 | Root Light, Apple, and GPU pins now consume Core/Runtime/Gateway versions with permissive PDF read/write support and regenerated installer manifest coverage. |
| 0180 | [AbstractFlow compact node pin disclosure](completed/0180_abstractflow_compact_node_pin_disclosure.md) | 2026-06-04 | Added a generic UI-only node pin disclosure policy, compact bottom chevron, focused Vitest coverage, lint/build validation, and browser QA for compact/expanded/connected optional pins. |
| 0178 | [Gateway and Flow reasoning control propagation](completed/0178_gateway_flow_reasoning_control.md) | 2026-06-03 | Added Gateway run-scoped `thinking`, Flow LLM/Agent controls and pins, Runtime VisualFlow propagation, AbstractAgent generation-param forwarding, docs, and focused Gateway/Runtime/Agent/Flow validation. |
| 0177 | [Flow route-aware provider and model selection](completed/0177_flow_route_aware_model_selection.md) | 2026-06-03 | Threaded Core/Gateway `capability_route` discovery into Flow selectors and Runtime Provider Models execution, including fail-closed invalid route handling, docs, and focused Runtime/Flow validation. |
| 0175 | [Multimodal capability taxonomy and schema](completed/multimodal-capabilities/0175_multimodal_capability_taxonomy_schema.md) | 2026-06-03 | Added route-keyed model capability metadata and schema, Core route helpers, `/v1/models?capability_route=...`, Runtime/Gateway forwarding, Gateway Console route filters, docs, and focused cross-package tests. |
| 0174 | [Audio understanding model registry coverage](completed/0174_audio_understanding_model_registry.md) | 2026-06-02 | Added source-backed Core registry entries and route hints for Qwen3-Omni Captioner/Instruct, Qwen2.5-Omni, Qwen2-Audio, Audio Flamingo 3, and MOSS-Audio; Qwen3.6 remains audio false; Qwen3.6 MTP GGUF variants no longer overclaim image/video; schema, alias, lookup, and duplicate-key checks passed. |
| 0173 | [Core provider endpoint profiles](completed/0173_core_provider_endpoint_profiles.md) | 2026-06-02 | Added Core-owned single-principal provider endpoint profiles, `abstractcore config set-provider/providers/models/test-provider/delete-provider`, `endpoint:*` registry/factory and embedding-manager resolution, OVH-style docs, and redacted CLI output. |
| 0172 | [Explicit multimodal default fallback routing](completed/0172_explicit_multimodal_default_fallback_routing.md) | 2026-06-02 | Made `input.voice` and explicit/covered `input.video` the fallback gates, removed Flow default editing from Model Residency, allowed blank LLM/Agent pins to use Gateway defaults, added Auto provider switch-back options, and validated Core/Gateway/Runtime/Flow behavior. |
| 0171 | [Gateway Console sandbox client grounding and media attachments](completed/0171_gateway_console_sandbox_client_grounding_and_media.md) | 2026-06-01 | Added browser-local prompt grounding for Console Sandbox text tests, fixed artifact-backed image uploads through Gateway/Runtime/Core into native OpenAI-compatible `image_url` payloads, and documented the prompt-only untrusted metadata boundary. |
| 0170 | [Core and Gateway capability-default config convergence](completed/0170_core_gateway_capability_defaults_config_convergence.md) | 2026-06-01 | Added scoped Core config files and `abstractcore config` defaults commands; Gateway baseline/user defaults now write runtime-scoped Core config only. |
| 0165 | [AbstractFlow web-only product migration](completed/0165_abstractflow_web_only_product_migration.md) | 2026-05-31 | Flattened AbstractFlow into the root npm web package, removed Python package/backend/tests, moved examples to `examples/flows`, removed Python Flow from root pip profiles, and updated launch/docs/release paths. |
| 0161 | [Three-path public install guide](completed/0161_three_path_public_install_guide.md) | 2026-05-31 | Added `docs/install.md`, linked it from user docs, and clarified that Light is remote-first rather than reduced-functionality. |
| 0160 | [Framework doctor and manifest CLI](completed/0160_framework_doctor_and_launch_cli.md) | 2026-05-31 | Added the `abstractframework` console script with `doctor` and `manifest` commands; launch remains deferred unless it can delegate cleanly. |
| 0159 | [Generated install manifest contract](completed/0159_generated_install_manifest_contract.md) | 2026-05-31 | Added manifest generator, checked-in JSON/schema, CLI drift check, and tests tying the manifest to root pins. |
| 0158 | [Installer repository extraction](completed/0158_installer_repository_extraction.md) | 2026-05-31 | Moved installer prototypes to `https://github.com/lpalbou/AbstractInstallers` and removed the tracked root `abstractinstallers/` tree. |
| 0157 | [Gateway provider endpoint profiles](completed/0157_gateway_provider_endpoint_profiles.md) | 2026-05-31 | Added Gateway-owned provider endpoint profiles with descriptions, write-only API keys, virtual `endpoint:*` providers in discovery, Runtime resolution, local dynamic provider construction, console UI with model discovery, and tests. |
| 0156 | [Retained runtime admin lifecycle](completed/0156_retained_runtime_admin_lifecycle.md) | 2026-05-30 | Added admin-only retained runtime list/transfer/purge routes, Gateway Console actions, scoped purge deletion, transfer semantics, and regression tests. |
| 0154 | [Multi-user security release blockers](completed/0154_multi_user_security_release_blockers.md) | 2026-05-30 | Added retained-runtime reservations, Code/Observer hosted URL guards, published launcher user bootstrap, and `.DS_Store` cleanup. |
| 0149 | [Cross-app Gateway auth and defaults convergence](completed/0149_cross_app_gateway_auth_defaults_convergence.md) | 2026-05-30 | Per-app hosted/local auth/default matrix completed; Flow, Code Web, and Observer use hosted browser-session proxy auth; shared auth/default component intentionally deferred until duplication creates real pressure. |
| 0141 | [Flow browser-session Gateway auth](completed/0141_flow_browser_session_gateway_auth.md) | 2026-05-30 | Initial Flow browser sign-in removed server/admin ambient browser auth; 0153 now supersedes raw token cookies with opaque Gateway browser sessions. |
| 0140 | [Abstract Release Skill](completed/0140_abstract_release_skill.md) | 2026-05-24 | Added a read-only framework release orchestration skill with package discovery, release-wave planning, dependency-floor review, root profile pin drift checks, PyPI visibility gates, and approval/traceability guidance. |
| 0139 | [Unified Framework Capability Defaults](completed/0139_unified_framework_capability_defaults.md) | 2026-05-24 | Core-owned routing defaults for input/output/embedding/rerank, Gateway control-plane access, atomic provider/model resolution, catalog-backed Flow defaults UI, and qwen3.6 text default. |

## Operating Notes

- Use `docs/adr/` for durable architecture policy.
- Use this backlog for execution traceability, validation evidence, and follow-up state.
- New backlog item filenames should use `NNNN_<slug>.md`; date-prefixed legacy files should not be copied for new work.

--- docs/backlog/completed/0141_flow_browser_session_gateway_auth.md ---
# Completed: Flow browser-session Gateway auth

## Metadata
- Created: 2026-05-29
- Status: Completed
- Completed: 2026-05-30

## ADR status
- Governing ADRs: ADR-0018, ADR-0033
- ADR impact: May revise existing ADR

## Context
AbstractFlow's browser editor uses a Flow server/proxy to inject Gateway auth
into `/api/gateway/*` requests. Browser login is now scoped to the browser
session and does not treat server/admin tokens as browser login state. This
item describes the initial Flow-side boundary; planned item 0153 supersedes the
raw token cookie implementation with opaque Gateway browser sessions and CSRF.

## Current code reality
- `abstractflow/web/backend/services/gateway_connection.py` persists only the
  Gateway URL and resolves browser auth only from session cookies.
- `abstractflow/web/backend/routes/connection.py` validates submitted user tokens
  through Gateway `/me`, rejects admin/server principals, and sets only
  HTTP-only browser cookies.
- `abstractflow/web/frontend/bin/cli.js` and
  `abstractflow/web/frontend/vite.config.ts` use the same browser-cookie
  session model for status and proxy auth.
- `abstractflow/web/frontend/src/components/GatewayConnectionModal.tsx` presents
  Gateway URL, user, and Gateway token; tenant/runtime are returned by Gateway
  as read-only principal metadata.

## Problem
The original problem was that server-configured Gateway tokens could become
ambient browser auth. The current implementation removes that fallback for
browser requests.

## What we want to do
Keep browser-session authentication as the default AbstractFlow hosted behavior
so each browser/user must authenticate and only receives access to its own
Gateway connection state.

## Why
Hosted Flow deployments need a clear security boundary. Gateway tokens are control-plane credentials and must not become ambient shared credentials for every browser that can load the Flow UI.

## Requirements
- Store Gateway connection state per browser session, not as a single global
  process token.
- Ensure `/api/gateway/*` proxy injection resolves the token from the browser
  session.
- Preserve local loopback ergonomics while still requiring browser sign-in.
- Keep Gateway bearer tokens out of browser `localStorage`/`sessionStorage`.
- Make remote Gateway URL changes explicit through
  `ABSTRACTFLOW_ALLOW_REMOTE_BROWSER_GATEWAY_CONFIG`.

## Suggested implementation
- Use HTTP-only browser cookies for Gateway URL/token session state.
- Remove `CONNECTION.gatewayToken`/`ABSTRACTGATEWAY_AUTH_TOKEN` fallback use from
  browser status and proxy paths.
- Teach the Python FastAPI host, Node static server, and Vite dev proxy the
  same session resolution semantics.

## Scope
- AbstractFlow web backend connection routes.
- AbstractFlow Node static server connection/proxy path.
- Vite dev connection/proxy path.
- Documentation and security guidance for hosted Flow deployments.

## Non-goals
- Do not change AbstractGateway's own bearer-token model in this item.
- Do not store Gateway tokens in browser storage.
- Do not make public multi-user hosting depend on a single shared Gateway token unless explicitly configured by the operator.

## Dependencies and related tasks
- ADR-0018 durable gateway/control-plane model.
- ADR-0033 install profiles and server boundaries.
- `abstractflow/docs/web-editor.md`
- `abstractflow/docs/architecture.md`
- `abstractflow/web/backend/routes/connection.py`
- `abstractflow/web/frontend/bin/cli.js`

## Expected outcomes
- A distinct browser session connecting to the same hosted Flow server cannot use another session's Gateway token.
- Hosted Flow can require each browser/user to authenticate or provide its own token.
- Local single-user development remains easy and documented.

## Validation
- Unit/integration test with two browser clients and separate cookies proves isolation.
- Regression test proves loopback local configuration still works.
- Proxy auth tests prove `/api/gateway/*` injects the session token, not a stale process-global token.
- Documentation explains local, single-user hosted, and multi-user hosted security modes.

## Progress checklist
- [x] Choose session/auth store design and hosted-mode defaults.
- [x] Implement Python backend session-scoped connection storage.
- [x] Implement Node static server session-scoped connection storage.
- [x] Align Vite dev proxy behavior for local development.
- [x] Add browser-session proxy isolation tests.
- [x] Update Flow security docs and generated docs indexes.

## Follow-up guidance
Keep browser-session auth as the default Flow behavior. Future work should add
CSRF/session-hardening tests and extend the same current-user auth model to
Code, Assistant, Observer, and bridge-hosted app flows.

--- docs/backlog/planned/0142_gateway_tenant_isolation_and_shared_runtime.md ---
# Planned: Gateway tenant isolation and shared runtime design

## Metadata
- Created: 2026-05-29
- Status: Planned
- Completed: N/A

## ADR status
- Governing ADRs: ADR-0018, ADR-0021, ADR-0033
- ADR impact: May revise existing ADR

## Context
AbstractGateway currently uses bearer-token security for the gateway API
surface. In user-auth mode, bearer tokens resolve to a user principal and
Gateway can route each principal to a separate runtime/data plane. AbstractFlow
uses browser-session cookies for user tokens and does not let a server/admin
token become shared browser login state.

The present model is suitable for local single-user deployments and trusted
cohorts. It is not a multi-user isolation model for independent users.

## Current code reality
- `abstractflow/web/backend/main.py`, `abstractflow/web/frontend/bin/cli.js`, and
  `abstractflow/web/frontend/vite.config.ts` proxy `/api/gateway/*` and inject
  the signed-in browser session's Gateway user token.
- `abstractgateway/src/abstractgateway/security/gateway_security.py` validates
  global bearer tokens and origin policy, but does not resolve a tenant/user
  principal.
- Gateway run, ledger, command, artifact, KG, prompt-cache, and VisualFlow
  routes are scoped by ids and request parameters, not by authenticated owner.
- AbstractRuntime state carries `actor_id` and `session_id`; those are
  provenance/correlation fields, not authorization boundaries.
- Memory owner ids include run/session/global concepts; global means the runtime
  instance's global owner, not a tenant-isolated namespace.

## Problem
If several independent users share one Flow/Gateway/runtime/data plane today,
one user can potentially list or access another user's run metadata, ledgers,
history bundles, artifacts, prompt-cache state, workflow mutations, memory, tool
execution surface, workspace effects, provider credentials, and audit scope.
`session_id`, `run_id`, `artifact_id`, and `owner_id` are not safe authorization
proofs.

## What we want to do
Define and implement a tenant-aware Gateway architecture that supports
independent users safely. Until that exists, document and enforce the supported
deployment pattern: an authenticated front door routes each user or tenant to a
separate Gateway/runtime/data plane.

## Why
Gateway is the durable control plane. Shared use without tenant authorization
risks confidentiality leaks, integrity failures, workspace/file access problems,
memory poisoning, credential bleed, and weak auditability.

## Requirements
- Add a `Principal` or `TenantContext` resolved from trusted Gateway auth.
- Persist `tenant_id`, `user_id`, roles/scopes, and token fingerprint on runs and
  audit records without logging bearer tokens.
- Enforce tenant filtering for run list/detail, ledger replay/stream, history,
  commands, artifacts, uploads/import/export, prompt cache, workflow CRUD/publish,
  KG/memory owner access, workspace policy, tool execution, and provider
  credentials.
- Treat `session_id`, `run_id`, `artifact_id`, and `owner_id` as references only;
  never as authorization.
- Make `global` memory tenant-global by default; cross-tenant shared memory must
  be an explicit namespace with provenance, consent, deletion, and poisoning
  controls.
- Keep local single-user ergonomics through an explicit single-tenant default.
- Add two-user isolation tests across the control plane and stores.

## Suggested implementation
1. Near term: deploy an identity-aware front door that routes each authenticated
   user or tenant to a separate Gateway process, data dir, artifact store, memory
   store, workspace root, provider credential set, and quota bucket.
2. Add Gateway token-to-principal mapping for single-process deployments, but keep
   it single-tenant until storage and route enforcement are complete.
3. Introduce tenant/user columns or metadata in run stores, artifact metadata,
   prompt-cache keys, memory owner ids, workflow records, and audit logs.
4. Centralize authorization checks in service/storage layers so route handlers
   cannot accidentally bypass tenant filtering.
5. Admin-gate or disable global enumeration paths (`scope=all`, arbitrary
   `owner_id`, `all_owners`, broad artifact search, workflow mutation, and local
   tools) until tenant-aware policy exists.

## Scope
- AbstractFlow hosted connection model.
- AbstractGateway auth, routes, stores, artifact handling, memory/KG access,
  prompt-cache routes, workflow CRUD/publish, commands, audit, and workspace
  policy.
- AbstractRuntime run state and store schemas where tenant ownership must be
  durable.
- Documentation, ADRs, and release gates for supported deployment topologies.

## Non-goals
- Do not claim current shared Gateway deployments are tenant-isolated.
- Do not use caller-supplied `session_id`, `actor_id`, `owner_id`, or headers as a
  trusted identity source.
- Do not make cross-user learning implicit. Shared/org memory must be explicit
  and auditable.
- Do not treat workspace scoping or `execute_command` as an OS sandbox.

## Dependencies and related tasks
- `docs/backlog/completed/0141_flow_browser_session_gateway_auth.md`
- `abstractgateway/docs/backlog/proposed/2026-05-13_shared_identity_context.md`
- ADR-0018 durable Gateway/control-plane contract.
- ADR-0021 deployment topologies and supported scenarios.
- ADR-0033 install profiles and server boundaries.

## Expected outcomes
- Operators can choose between clearly documented single-tenant, trusted-cohort,
  and future shared-tenant modes.
- Independent users cannot enumerate or access each other's runs, artifacts,
  memory, workflow mutations, workspaces, credentials, prompt-cache state, or
  audit details.
- Shared memory/cross-user learning is opt-in, provenance-rich, revocable, and
  separate from private user/tenant memory.

## Validation
- Two-user integration tests for Flow proxy sessions, Gateway runs/list/history,
  ledger stream/replay, commands, artifact search/content/upload/import/export,
  KG/memory queries, prompt-cache routes, VisualFlow CRUD/publish, workspace
  tools, and local tool execution policy.
- Store-level tests proving tenant filters are enforced below route handlers.
- Security docs and ADR checks confirming shared Gateway is not advertised as
  isolated before the tenant model lands.

## Progress checklist
- [ ] Finalize principal/tenant data model and single-tenant compatibility mode.
- [ ] Implement per-user/per-tenant Flow connection routing or front-door contract.
- [ ] Persist tenant/user ownership on Gateway and Runtime records.
- [ ] Enforce tenant filtering in Gateway service/store layers.
- [ ] Add two-user authorization test matrix.
- [ ] Document shared memory and cross-user learning as explicit opt-in.

## Guidance for the implementing agent
Start from code reality, not desired UX. Add authorization at the lowest shared
storage/service layer available, then wire routes to it. Preserve single-user
local ergonomics, but fail closed for shared hosted deployments.

--- docs/backlog/planned/0143_shared_gateway_per_principal_runtime_router.md ---
# Planned: Shared Gateway per-principal runtime router

## Metadata
- Created: 2026-05-30
- Status: Planned
- Completed: N/A

## ADR status
- Governing ADRs: ADR-0018, ADR-0021, ADR-0033
- ADR impact: May revise existing ADR

## Context
A shared AbstractGateway front door can be the right hosted architecture when it
acts as an authenticated router/control plane and routes each principal to an
isolated runtime/data plane. This avoids a separate public Gateway URL per user
while preserving the isolation properties of separate runtimes.

An adversarial review compared the strongest pro and con arguments. Both sides
converged on the same boundary: the design is sound if the shared Gateway routes
to isolated per-user or per-tenant `GatewayService`/Runtime contexts. It is not
sound if the Gateway keeps one shared service/stores/runtime and merely adds
`user_id` labels.

## Current code reality
- `abstractgateway/src/abstractgateway/security/gateway_security.py` validates
  global bearer tokens and origin policy. It does not currently resolve a
  trusted `Principal` with `tenant_id`, `user_id`, roles, or scopes.
- `abstractgateway/src/abstractgateway/service.py` exposes a singleton
  `GatewayService` with one config, one store set, one host, one runner, one
  auth policy, one embedding client, and optional bridges.
- `abstractgateway/src/abstractgateway/stores.py` builds one file or SQLite
  store set rooted at the configured Gateway data directory.
- `abstractgateway/src/abstractgateway/routes/gateway.py` has many endpoints
  that operate on caller-supplied ids such as `run_id`, `session_id`,
  `artifact_id`, and `bundle_id`. Those ids are references, not authorization
  proofs.
- Session memory owner ids are derived from caller-controlled session ids in
  several paths; global memory is runtime-instance global, not tenant global.
- VisualFlow/bundle publish, artifact search/listing, workspace/file helpers,
  prompt-cache routes, maintenance/process-manager routes, Telegram/email
  bridges, and background command processing are currently designed around the
  singleton service/data-plane shape.

## Problem
Hosted Flow/Gateway needs a multi-user story that is both ergonomic and safe.
One Gateway per user is conceptually simple but operationally heavy: it creates
endpoint sprawl, certificate/domain management overhead, duplicated routing,
harder upgrades, version skew, and more complicated Flow connection UX.

A single shared Gateway front door can solve those operational issues, but a
partial implementation can be worse than no implementation. Routing only LLM
calls per user while leaving runs, ledgers, artifacts, commands, memory,
workspaces, prompt cache, bundles, provider credentials, or background workers
global would still leak or mix users.

## What we want to do
Introduce a hosted-mode architecture where:

```text
Flow/browser
  -> authenticated shared Gateway front door
    -> trusted principal resolution
      -> server-side principal-to-runtime routing
        -> isolated GatewayService / Runtime context
          -> isolated stores, memory, artifacts, workspace, secrets, queues
```

The first implementation should prefer isolated per-principal data planes over
shared stores with tenant columns. A single process may hold several
per-principal service objects, but each object must own its own data directory,
stores, runner/queue, workspace root, provider defaults, secret scope, memory,
artifact store, and prompt-cache namespace.

## Why
This architecture keeps the hosted product manageable while preserving the main
security benefit of per-user runtimes. It gives Flow one stable API endpoint and
lets login determine identity and runtime selection. It also creates a better
foundation for later org/team features: admin consoles, billing, quotas, scoped
collaboration, audit, and explicit shared memory.

## Requirements
- Add a trusted `Principal` model resolved by Gateway auth middleware or a
  trusted front-door integration. It should include at least `tenant_id`,
  `user_id`, roles/scopes, and token/session fingerprint metadata.
- Gateway owns hosted-mode authentication. Apps such as AbstractFlow,
  AbstractCode, AbstractAssistant, and AbstractObserver must authenticate as the
  current user/session and forward only trusted Gateway credentials or cookies;
  they must not share one app-server Gateway token across all browser users.
- Add admin-level user management routes for Gateway operators to list, create,
  update, disable, delete, and rotate credentials for users. These routes must
  require an admin principal and must never disclose stored token hashes.
- Clients must not send authoritative `user_id`, `tenant_id`, `runtime_id`, or
  workspace root values. If accepted for UX, they must be advisory and checked
  against server-side policy.
- Add a server-side `Principal -> RuntimeContext/GatewayService` router.
- Each per-principal context must own an isolated data plane:
  - data directory;
  - run store and ledger store;
  - command inbox/cursor store and runner;
  - artifact store;
  - memory/KG owner namespace or store;
  - prompt-cache/bloc namespace or disabled hosted-mode prompt cache;
  - workspace root and mount policy;
  - provider defaults, credentials, quotas, and secrets.
- Admin/global endpoints must be separated from user endpoints. Hosted mode
  should disable or admin-gate unscoped global enumeration and mutation paths.
- Bundles/VisualFlows must be either tenant-local or global read-only with
  admin-only publishing.
- Every read/write path must authorize against the resolved principal, including
  run start/list/detail, ledger replay/SSE, history bundle, resume/cancel,
  commands, artifacts, uploads/import/export, memory/KG, prompt cache, workflow
  CRUD/publish, workspace/file tools, provider config, maintenance routes, and
  bridge-triggered runs.
- Background workers must carry tenant context through queued commands, child
  runs, event emission, artifact projection, and resume paths.
- Logs, traces, temporary files, metrics, audit records, and error reports must
  avoid unscoped user content or include principal-safe ownership metadata.

## Suggested implementation
1. Add a principal abstraction and middleware hook, but keep local single-user
   mode as a default principal.
2. Introduce a `GatewayServiceRouter` composition root that returns the current
   principal's service/context instead of the process-wide singleton service.
   In the first code slice, preserve the public `get_gateway_service()` helper
   but make it resolve through a request context variable when hosted user auth
   is enabled. This lets the existing route surface inherit per-principal data
   planes before every endpoint is converted to explicit dependencies.
3. Implement hosted v0 using separate data directories and store sets per
   principal. Avoid a shared DB/schema migration in the first version.
4. Add `/api/gateway/me` and `/api/gateway/admin/users` before app-specific
   login UX so Flow and native apps can test the resolved principal contract.
5. Move route dependencies from `get_gateway_service()` to
   `get_gateway_service_for_principal(...)` or an equivalent request-scoped
   dependency as route families are touched.
6. Make global/admin surfaces explicit and fail closed in hosted mode:
   broad artifact search, `scope=all`, arbitrary owner ids, workflow publishing,
   process manager, backlog execution, local workspace tools, bridge admin
   config, and provider credential mutation.
7. Add app-auth adapters: browser apps should rely on Gateway session cookies or
   request-bound user bearer tokens, while native apps should use user API
   tokens/device login. All apps should call `/api/gateway/me` after connect.
8. Add a two-user test harness that tries guessed ids across every route family.

## Scope
- AbstractGateway security, service composition, routes, stores, runner,
  artifacts, workflow/bundle registry, memory/KG integration, prompt cache,
  workspace policy, provider defaults, maintenance routes, and bridge entry
  points.
- AbstractRuntime ownership context where run state, child runs, memory owner
  ids, prompt-cache bindings, and artifact projection need durable tenant
  provenance.
- AbstractFlow hosted connection UX for identity-aware Gateway discovery and
  no browser-controlled runtime selection.
- Documentation and ADR updates for supported hosted topologies.

## Non-goals
- Do not build a shared `GatewayService`/store set with only `user_id` labels as
  the first version.
- Do not claim that the current Gateway bearer token model provides per-user
  isolation.
- Do not trust caller-supplied `session_id`, `run_id`, `artifact_id`,
  `owner_id`, or `bundle_id` as authorization.
- Do not make cross-user memory or learning implicit. Shared/team memory must be
  a later explicit namespace with consent, ACLs, audit, deletion semantics, and
  poisoning controls.
- Do not turn workspace scoping into a claimed OS sandbox.
- Do not expose per-user runtime URLs or internal routing identifiers to the
  browser as control inputs.

## Dependencies and related tasks
- `docs/backlog/completed/0141_flow_browser_session_gateway_auth.md`
- `docs/backlog/planned/0142_gateway_tenant_isolation_and_shared_runtime.md`
- `abstractgateway/docs/backlog/proposed/2026-05-13_shared_identity_context.md`
- ADR-0018 durable Gateway/control-plane contract.
- ADR-0021 deployment topologies and supported scenarios.
- ADR-0033 install profiles and server boundaries.

## Expected outcomes
- Hosted Flow can connect to one public Gateway endpoint while each
  authenticated user/tenant receives an isolated runtime/data plane.
- Alice cannot enumerate, read, stream, resume, cancel, export, import, mutate,
  or infer Bob's runs, ledgers, artifacts, sessions, memory, prompt cache,
  workspaces, bundles, provider credentials, or command inbox.
- Gateway operators can run local single-user mode, trusted-cohort mode, or
  hosted per-principal runtime-router mode with explicit documentation.
- Shared/team memory and collaboration remain future explicit product features,
  not accidental leakage from shared infrastructure.

## Validation
- Unit tests for principal extraction, token/session fingerprinting, scope
  checks, and service-router selection.
- Two-user integration tests proving guessed `run_id`, `session_id`,
  `artifact_id`, `bundle_id`, and memory owner ids cannot cross principals.
- SSE/ledger/history tests proving streams and batch replay are principal-bound.
- Command/resume/cancel tests proving queued records carry tenant context.
- Artifact upload/list/search/content/export/import tests with Alice/Bob data.
- VisualFlow/bundle tests for tenant-local or admin-only global publishing.
- Workspace/file-tool tests proving hosted mode uses per-principal roots and
  rejects arbitrary absolute roots.
- Prompt-cache tests proving hosted mode scopes or disables shared cache state.
- Audit/log checks proving bearer tokens are never logged and user content is
  scoped/redacted.

## Progress checklist
- [x] Run adversarial two-subagent review for the shared Gateway router design.
- [x] Define `Principal` and hosted-mode auth contract.
- [x] Add admin user registry and admin-only CRUD routes.
- [x] Design `GatewayServiceRouter` and local single-user compatibility mode.
- [x] Implement per-principal data directories and service contexts.
- [x] Convert existing route dependencies to request-scoped service lookup via
      the `get_gateway_service()` composition root.
- [ ] Admin-gate or disable global surfaces in hosted mode.
- [ ] Carry principal context through runner/command/background paths.
- [x] Add `/api/gateway/me` and app policy display.
- [x] Change AbstractFlow hosted auth to current-user Gateway credentials rather
      than one app-server token.
- [ ] Change AbstractCode/Assistant/Observer hosted auth to current-user Gateway
      credentials rather than one app-server token.
- [ ] Add the two-user isolation test matrix.
- [ ] Update ADRs and hosted deployment docs.

## Review notes: 2026-05-30
Two independent subagent reviews agreed that a shared Gateway can scale as a
control-plane/router only if the per-user data planes are separate. The first
implementation should therefore add identity resolution, admin user CRUD, and
request-scoped service routing before attempting route-by-route tenant columns.

The reviews also called out a hard boundary for hosted apps: AbstractFlow and
other frontends cannot safely use one server-held Gateway token for all users.
They need a current-user auth/session path that lets Gateway resolve the
principal and route to that user's runtime. Until that app-auth migration is
finished, hosted multi-user deployments must be treated as an incremental beta
surface rather than a complete isolation guarantee across every app workflow.

## Implementation report: 2026-05-30
- Added `GatewayPrincipal`, request context propagation, and legacy-token
  compatibility where `ABSTRACTGATEWAY_AUTH_TOKEN` resolves to `local-admin`.
- Added a file-backed `GatewayUserRegistry` at
  `<ABSTRACTGATEWAY_DATA_DIR>/auth/users.json` by default. User tokens are
  PBKDF2-SHA256 hashed; generated/rotated tokens are returned once.
- Added `GET /api/gateway/me` and admin-only
  `/api/gateway/admin/users` list/create/read/update/delete routes.
- Added request-scoped service routing: when user auth is enabled,
  `get_gateway_service()` resolves the current principal and returns a
  per-principal `GatewayService` with separate runtime/flows directories under
  `<DATA_DIR>/users/<tenant_id>/<runtime_id>/`.
- Kept local/single-user Gateway behavior compatible: without user auth, the
  process-wide singleton service and legacy token model remain.
- Changed AbstractFlow hosted connection handling so remote browser sessions
  sign in with Gateway URL, Gateway user id, and user token. Flow validates that
  Gateway `/me` resolves to that expected user before accepting the connection.
  Gateway owns the user's tenant/runtime mapping and returns it as read-only
  principal metadata. This was later hardened by 0153: Flow now exchanges the
  user token for an opaque Gateway browser session, keeps only the session id in
  an HTTP-only browser cookie, and forwards the session plus CSRF token for
  mutating Gateway proxy calls. Different browsers must connect as their own
  Gateway users.
- Simplified the AbstractFlow modal to the three intended fields
  (`Gateway URL`, `User`, `Gateway token`), removed the tenant/runtime input,
  renamed the primary action to sign in, and made the resolved runtime a
  read-only status detail returned by Gateway.
- Hardened the Flow Gateway proxy so browser-supplied `Authorization`, `Cookie`,
  `X-Forwarded-*`, and other unapproved headers are stripped before proxying;
  Flow injects only the resolved Gateway browser session.
- Hardened Gateway admin routes so they fail closed if the router is mounted
  without `GatewaySecurityMiddleware` while Gateway security is enabled.
- Updated Gateway and Flow docs plus `llms.txt`/`llms-full.txt` files to
  describe the new auth/routing contract and remaining hosted-mode limits.

Remaining work is intentionally still open: admin-gating every global/operator
route family, carrying principal context through every background/bridge path,
changing Code/Assistant/Observer hosted auth, and expanding the Alice/Bob
guessed-id isolation matrix across run, artifact, memory, workspace,
prompt-cache, process, and maintenance endpoints.

## Guidance for the implementing agent
Start by preserving the strong isolation boundary: separate per-principal data
planes. Resist the shortcut of adding owner labels to the existing singleton
service. Add authorization at the lowest shared layer available, not only in
route handlers. Re-run the adversarial review before promoting shared stores,
shared prompt cache, shared memory, or collaborative features.

--- docs/backlog/planned/gateway-control-plane/README.md ---
# Gateway control plane backlog track

## Status
Planned/in progress, with adjacent proposals for package extraction decisions.
Gateway/Flow now have browser-session auth, central route-family authorization,
the first Gateway Console, Gateway-baseline plus per-user capability-default
overlays, secret-free browser login responses, full Gateway cookie/origin
session matrix tests, an extended Alice/Bob isolation matrix for the current
user-owned data surfaces, retained-runtime reservations after user deletion,
retained-runtime transfer/purge lifecycle, and completed cross-app auth/default
convergence planning. Gateway provider endpoint profiles now cover the first
secret-safe reusable endpoint path. The track remains open for future
route-family growth, workflow ACL UI, runtime exploration contracts, and a
fuller provider-secret vault/bridge policy.

## Purpose
This track groups the Gateway admin/account/config/runtime-control work raised
by the multi-user Gateway review. Gateway is the trust boundary and control
plane; Flow, Code, Observer, and Assistant should become task UIs that consume
Gateway auth, defaults, permissions, and catalogs instead of each owning global
identity or model configuration.

## Items
- `0145_gateway_admin_console_bootstrap.md`: Gateway-served admin/account console v0.
- `0146_gateway_rbac_scope_policy_matrix.md`: route-family role/scope hardening.
- `0147_gateway_per_principal_config_secrets_defaults.md`: per-principal config, provider keys, and defaults through Gateway with Core as schema/persistence authority.
- `0148_gateway_workflow_registry_acl.md`: private workflow registry plus tenant/framework catalog ACLs.
- `0150_observer_manager_responsibility_split.md`: keep Observer focused and move admin/config ownership out.
- `0153_gateway_browser_session_security_contract.md`: define hosted browser session, cookie, CSRF, logout, and revocation semantics.

Adjacent proposals:
- `../../proposed/gateway-control-plane/0151_runtime_explorer_contract.md`
- `../../proposed/gateway-control-plane/0152_abstractmanager_package_extraction.md`

Completed in this track:
- `../../completed/0149_cross_app_gateway_auth_defaults_convergence.md`: converged Flow, Code Web, Observer, Gateway Console, Assistant, and automation behavior expectations for hosted/local auth and defaults; shared component extraction is deferred.
- `../../completed/0154_multi_user_security_release_blockers.md`: closed the retained-runtime reuse, Code/Observer hosted Gateway URL guard, published launcher bootstrap, and `.DS_Store` hygiene release blockers.
- `../../completed/0156_retained_runtime_admin_lifecycle.md`: added explicit admin list/transfer/purge lifecycle for retained runtime reservations.
- `../../completed/0157_gateway_provider_endpoint_profiles.md`: added Gateway-owned endpoint profiles that surface as virtual `endpoint:*` providers with write-only credentials.

## Reading order
Continue by keeping `0146` and `0153` tests current as new route families and
browser apps appear. `0145` now has a narrow console v0 with user email, confirmations,
    and discovered provider/model selectors; keep it narrow. `0147` now has a
Gateway baseline, per-principal defaults, and the initial Gateway provider
endpoint profile injection boundary; encryption, bridge, delegated-tool, and
audit policy remain. Use `0148` after the
authorization/session boundaries are stable, with immutable catalog versions
and explicit default pointers. `0149` is complete; do not extract a shared auth
component until duplication creates a real maintenance problem. Keep `0151`,
`0152`, and `0155` proposed until overlap, package pressure, or repeated hosted
proxy drift is proven.

## Relevant ADRs and docs
- ADR-0018: Durable Run Gateway and Remote Host Control Plane.
- ADR-0021: Deployment topologies and supported scenarios.
- ADR-0033: Install profiles, config entrypoints, and server boundaries.
- ADR-0035: Capability routing defaults.
- `docs/guide/capability-routing-defaults.md`
- `docs/guide/gateway-security.md`
- `docs/backlog/planned/0143_shared_gateway_per_principal_runtime_router.md`

## Non-goals
This track does not make Gateway a workflow authoring UI, coding UI, or rich run
trace viewer. Flow remains the workflow authoring surface. Code remains the
coding-agent UX. Observer remains the run/ledger observability UX unless future
proposals intentionally split runtime exploration into a new app.

--- docs/backlog/planned/gateway-control-plane/0145_gateway_admin_console_bootstrap.md ---
# Planned: Gateway admin console bootstrap

## Metadata
- Created: 2026-05-30
- Status: Planned
- Completed: N/A

## ADR status
- Governing ADRs: ADR-0018, ADR-0021, ADR-0033
- ADR impact: May revise existing ADR

## Context
Gateway now has concrete principals, user-registry auth, admin-only user CRUD,
and per-principal runtime routing. The first usable hosted experience still
requires operators to use raw routes or scripts to see who they are, create
users, rotate tokens, and understand what runtime a user owns.

## Current code reality
- `abstractgateway/src/abstractgateway/security/principal.py` defines
  `GatewayPrincipal.is_admin()` as the `admin` role check.
- `abstractgateway/src/abstractgateway/routes/gateway.py` exposes
  `/api/gateway/me` and admin-only `/api/gateway/admin/users`.
- `abstractgateway/src/abstractgateway/users.py` stores user records with
  hashed bearer tokens, roles, scopes, enabled state, and `runtime_id`.
- `scripts/gateway-flow-local.sh` now prepares `default/admin` by default and
  gives it roles `admin,user`.
- Gateway now serves a dependency-free console at `/console`, with `/`
  redirecting there. The console uses Gateway browser sessions, reads `/me`,
  lets admins manage `/admin/users`, and lets users edit per-principal
  capability defaults.
- The console now lists retained runtime reservations and lets admins explicitly
  transfer or purge them after confirmation.
- Console user creation now collects optional email contact metadata, asks for
  confirmation before creating/deleting users, and uses Gateway discovery
  endpoints for provider/model selection instead of free-text provider/model
  fields.

## Problem
Installing or hosting Gateway is opaque. Admins need a browser-visible control
plane for first-run account state, user creation, runtime assignment, token
rotation, and runtime summary. Normal users need an account/runtime page that
does not require raw API calls.

## What we want to do
Add a narrow Gateway-served console v0 at canonical `/console`, while keeping
OpenAPI docs at `/docs`. `/` may redirect to `/console` when the console is
enabled. The first version should focus on sign-in/session state, `/me`,
account/runtime summary, and admin user management only for routes already
covered by the RBAC policy matrix. Normal users should see their own
account/runtime state and token-management options that policy permits.

## Why
Gateway is the trust boundary. A clean built-in control-plane UX reduces setup
mistakes, avoids duplicating admin configuration in Flow/Observer/Code, and
makes the `1 user = 1 runtime` model visible.

## Requirements
- Admin mutation pages are blocked on `0146` enforcement for every route they
  call; frontend hiding is not authorization.
- First-run credential disclosure is local-dev/loopback only, never a hosted
  production pattern. Hosted deployments must use explicit operator bootstrap
  and rotation paths.
- Local-dev token display must avoid server logs, respect file permissions,
  explain the token cache/recovery path, and encourage rotation.
- Login follows the browser-session contract in `0153`: Gateway user tokens are
  exchanged for app/browser sessions and are not retained in browser storage.
- Admin views: list/create/update/disable/delete users, rotate tokens, assign
  runtime ids, collect optional account email, inspect roles/scopes, transfer or
  purge retained runtime reservations.
- User views: current principal, runtime id, created date, enabled state, token
  rotation if allowed, recent activity summary if available.
- Never display token hashes or existing bearer tokens after issuance.
- Keep `/docs` available for OpenAPI.

## Suggested implementation
Start with a small static app served by Gateway and backed by existing
`/api/gateway/me` and `/api/gateway/admin/users`. Reuse AbstractUIC visual
tokens/theme primitives where practical, but keep the first version small.

## Scope
- Gateway-served console route and static assets.
- Account page and admin user-management page.
- Minimal runtime summary endpoints if current API data is insufficient and
  `0146` authorizes the route family.
- Documentation for local and hosted setup.

## Non-goals
- Do not build workflow authoring into Gateway Console.
- Do not build full run trace visualization; link to Observer.
- Do not add per-user provider secret storage in this item; that is `0147`.

## Dependencies and related tasks
- `0146_gateway_rbac_scope_policy_matrix.md` (hard prerequisite for admin
  mutation UX)
- `0153_gateway_browser_session_security_contract.md`
- `0147_gateway_per_principal_config_secrets_defaults.md`
- `docs/backlog/planned/0143_shared_gateway_per_principal_runtime_router.md`
- `abstractgateway/docs/security.md`

## Expected outcomes
- A fresh local Gateway can be administered from a browser without raw curl.
- The default local prepared user is `admin`.
- Admin-only user CRUD remains enforced server-side.
- Normal users cannot access admin pages or admin APIs.

## Validation
- Unit/API tests for admin and non-admin console API access.
- Browser/manual check: admin can create a user and rotate token; normal user
  cannot.
- Security check: no token hashes or existing tokens are returned in user lists.

## Recent validation
- `python -m pytest abstractgateway/tests/test_gateway_security_middleware_unit.py abstractgateway/tests/test_gateway_principal_auth.py abstractgateway/tests/test_gateway_console.py abstractflow/tests/test_gateway_connection_config.py abstractflow/tests/test_web_gateway_proxy_auth.py -q` -> 51 passed, 2 warnings.
- `python -m compileall -q abstractgateway/src/abstractgateway abstractflow/web/backend` -> passed.
- Earlier full-gateway validation: `python -m pytest abstractgateway/tests` -> 261 passed, 2 skipped.

## Progress checklist
- [x] Decide console route and static asset packaging.
- [x] Implement `0153` session semantics for console login.
- [x] Add account/runtime summary page.
- [x] Add admin user-management page after `0146` route checks exist.
- [x] Add minimal per-user defaults UX through `0147` overlay APIs.
- [x] Add retained-runtime reservation transfer/purge UX.
- [ ] Add richer runtime summary/activity API if needed.
- [x] Add tests and docs.

## Implementation note - 2026-05-30

Console v0 is implemented in `abstractgateway/src/abstractgateway/console.py`
and mounted from `abstractgateway/src/abstractgateway/app.py`. It intentionally
stays narrow: session sign-in/logout, account/runtime summary, admin user CRUD,
token rotation, retained runtime reservation lifecycle, and per-principal
capability-default editing. It does not own workflow authoring, deep runtime
exploration, or raw provider secret storage.

Follow-up console polish landed in the same track: provider/model defaults now
come from Gateway discovery selectors, admin create/delete uses confirmation
modals, and user records include optional email metadata for future
notification/invite workflows. User deletion currently removes account/token
access; runtime data retention/deletion must be handled by a separate retention
policy before destructive runtime cleanup is exposed.

2026-05-31 sign-in hardening: the console sign-in form now mirrors the shared
AbstractUIC Gateway browser-session sign-in card shape, omits Gateway URL
because `/console` is same-origin with Gateway, and has a generated JavaScript
syntax regression test so a malformed inline script cannot silently break the
login button again. React thin clients should use
`GatewaySessionSignInCard` from `@abstractframework/ui-kit`; the built-in
Gateway Console remains dependency-free static HTML and mirrors the same visual
contract.

## Guidance for the implementing agent
Keep this console narrow. If it starts becoming a full observability or workflow
authoring product, stop and split the concern into later items.

--- docs/backlog/planned/gateway-control-plane/0146_gateway_rbac_scope_policy_matrix.md ---
# Planned: Gateway RBAC scope policy matrix

## Metadata
- Created: 2026-05-30
- Status: Planned
- Completed: N/A

## ADR status
- Governing ADRs: ADR-0018, ADR-0021, ADR-0033
- ADR impact: Needs new ADR or ADR revision

## Context
Gateway has an `admin` role and admin-only user CRUD. Broader Gateway route
families are not yet governed by a complete role/scope matrix. The current
per-principal service router is a strong data-plane baseline, but route-family
authorization still needs explicit policy.

## Current code reality
- `GatewayPrincipal.is_admin()` is a string-role check.
- `/api/gateway/admin/users` is admin-gated.
- `/api/gateway/admin/runtime-reservations` list/transfer/purge routes are
  admin-gated.
- Non-admin users get per-principal data directories through Gateway service
  routing.
- Gateway now has a central authorization helper and a first route-family
  policy gate in the security middleware for admin, audit, process, backlog,
  triage, reports, capability-default writes, model load/unload, bloc writes,
  and prompt-cache writes.
- Gateway route-family policy is now centralized in
  `abstractgateway/src/abstractgateway/security/authorization.py`, and
  `GatewaySecurityMiddleware` delegates route-family checks to that table.
- Non-admin users can no longer satisfy admin-only route families via broad
  scopes such as `*`; `admin_required` means the `admin` role is required.
- Server-workspace file helpers, server-workspace artifact import/export, host
  metrics, email bridge routes, model residency listing, and model load/unload
  are admin-only in hosted user-auth mode. Browser file upload remains
  available to ordinary users.
- Capability-default writes are no longer treated as global admin writes in
  hosted user-auth mode; they are stored as per-principal overlays under the
  caller's runtime data plane by `0147`.
- Gateway user records now reject duplicate runtime ids within the same tenant,
  so `1 user = 1 runtime` cannot be accidentally broken by user creation or
  user update. Deleted users also leave a retained-runtime reservation, so a
  runtime whose data is retained cannot be assigned to a different principal in
  the same tenant unless an admin explicitly transfers the retained runtime or
  purges its data. The same runtime id can still exist in a different tenant.
- Per-principal routing is now covered by an initial Alice/Bob matrix for runs,
  run history, run input, ledger reads/streams/batches, artifact metadata,
  artifact content, and artifact search. Cross-user guessed ids return `404` or
  are omitted from batch/search results instead of returning empty records that
  imply existence.
- The Alice/Bob matrix now also covers private workflow bundles, VisualFlow
  drafts, per-principal capability-default overlays, session artifacts,
  prompt-cache key naming, and KG/session memory. Prompt-cache key hashing now
  includes a private principal scope while keeping the returned public identity
  app-level and portable.
- Capability discovery now reports admin-only server-workspace artifact
  import/export and provider prompt-cache control surfaces as unavailable for
  ordinary users, with machine-readable `admin_required` metadata. Admin
  principals still see those operations as available.
- `ABSTRACTGATEWAY_DEV_READ_NO_AUTH=1` now uses a non-admin
  `loopback-readonly` principal, and admin/operator route families still return
  machine-readable `403` denials.
- Powerful surfaces still need audit: workflow publish/remove/deprecate,
  workspace/file helpers, broader prompt-cache variants, provider defaults,
  credentials, broad runtime enumeration, bridge administration, discovery
  visibility, and global/operator endpoints.

## Problem
The words `admin` and `user` exist, but most of Gateway does not yet have a
decision-grade authorization matrix. Without one, future UI work can accidentally
surface privileged operations to ordinary users.

## What we want to do
Define and enforce a route-family RBAC/scope policy matrix for Gateway. The
matrix should state subject, action, resource class, owner/caller relation,
required role, required scope, audit requirement, discovery visibility, and
destructive/side-effect classification for each route family.

## Why
Gateway is now the shared control plane. Authorization must be centralized and
testable before Gateway Console, workflow catalog permissions, per-user
secrets, and runtime exploration can be trusted.

## Requirements
- Formalize at least `admin` and `user` roles plus concrete scope fields for
  narrow grants; do not leave downstream items to invent their own scope model.
- Model route authorization as subject/action/resource/condition checks, not
  scattered `if admin` branches.
- Admin-only: user management, credential/global config writes, global workflow
  catalog promotion/removal, process control, broad tenant/runtime enumeration,
  operator maintenance, and global policy changes.
- User-owned: own runs, ledgers, artifacts, memory, private workflows, and
  allowed runtime state.
- Explicit policy needed for shared/org memory, shared workspaces, workflow
  publishing, cross-runtime search, and destructive actions.
- Dev read bypass must never create a principal that satisfies `is_admin()`.
  Replace `local-admin` semantics with a distinct loopback/read-only principal,
  route allowlist, and tests proving admin/user/secret/catalog data remains
  unreachable.
- Denial responses and discovery metadata must not leak hidden resource ids,
  user existence, workflow existence, secret provider presence, or cross-runtime
  counts.
- Denials should be machine-readable, for example `reason_code`,
  `required_role`, `required_scope`, and `resource_class`, without exposing
  forbidden resource details.
- Admin/global writes and denied high-risk attempts are audit-log events.

## Suggested implementation
Add a centralized Gateway authorization helper and route-family policy table.
Refactor high-risk routes to call it. Keep denial reasons machine-readable so
UIs can hide or disable actions cleanly.

## Scope
- Gateway policy definitions and helper APIs.
- Route-family audits and tests.
- Discovery metadata for allowed/denied actions where clients need UX hints.

## Non-goals
- Do not implement the full Gateway Console here.
- Do not rely on frontend hiding as authorization.
- Do not treat caller-supplied ids as authorization proof.

## Dependencies and related tasks
- `0143_shared_gateway_per_principal_runtime_router.md`
- `0145_gateway_admin_console_bootstrap.md`
- `0153_gateway_browser_session_security_contract.md`
- `0148_gateway_workflow_registry_acl.md`
- `0151_runtime_explorer_contract.md`

## Expected outcomes
- A non-admin cannot call admin-only route families.
- A user cannot read or mutate another user's runtime by guessing ids.
- Discovery can report permission-aware action availability.

## Validation
- Alice/Bob route-family isolation tests across users, runs, ledgers,
  artifacts, memory, workflows, prompt cache, workspace files, and admin routes.
- Dev read-bypass tests proving admin user lists and sensitive data remain
  protected.
- Contract tests for permission metadata.

## Recent validation
- `python -m pytest abstractgateway/tests/test_gateway_principal_auth.py abstractgateway/tests/test_gateway_security_middleware_unit.py -q` -> 31 passed.
- `python -m pytest abstractgateway/tests/test_gateway_principal_auth.py::test_gateway_user_runtime_ids_are_unique_per_tenant abstractgateway/tests/test_gateway_principal_isolation_matrix.py -q` -> 2 passed.
- `python -m py_compile abstractgateway/src/abstractgateway/users.py abstractgateway/src/abstractgateway/routes/gateway.py` -> passed.
- `python -m pytest abstractgateway/tests/test_gateway_security_middleware_unit.py abstractgateway/tests/test_gateway_principal_auth.py abstractgateway/tests/test_gateway_console.py abstractflow/tests/test_gateway_connection_config.py abstractflow/tests/test_web_gateway_proxy_auth.py -q` -> 51 passed, 2 warnings.
- `python -m compileall -q abstractgateway/src/abstractgateway abstractflow/web/backend` -> passed.
- `python -m pytest abstractgateway/tests/test_gateway_principal_auth.py abstractgateway/tests/test_gateway_security_middleware_unit.py abstractflow/tests/test_gateway_connection_config.py abstractflow/tests/test_web_gateway_proxy_auth.py` -> 44 passed.
- `python -m pytest abstractgateway/tests` -> 261 passed, 2 skipped.
- `python -m pytest abstractflow/tests/test_gateway_connection_config.py abstractflow/tests/test_web_gateway_proxy_auth.py` -> 20 passed.
- `python -m pytest abstractgateway/tests/test_gateway_install_profiles.py abstractgateway/tests/test_gateway_capability_catalog_proxy.py` -> 29 passed.
- `python -m compileall -q abstractgateway/src/abstractgateway` -> passed.
- `python -m pytest abstractgateway/tests/test_gateway_principal_auth.py abstractgateway/tests/test_gateway_principal_isolation_matrix.py abstractgateway/tests/test_capabilities_endpoint_contract.py abstractgateway/tests/test_abstractflow_editor_gateway_contract.py -q` -> 26 passed.
- `python -m py_compile abstractgateway/src/abstractgateway/routes/gateway.py abstractgateway/src/abstractgateway/security/sessions.py abstractgateway/src/abstractgateway/security/gateway_security.py` -> passed.

## Progress checklist
- [ ] Write route-family policy matrix.
- [x] Add central authorization helper.
- [x] Harden dev read bypass for admin/operator route families.
- [x] Add first hosted route-family policy table in Gateway middleware.
- [x] Admin-gate server-workspace and operator route families.
- [x] Add runtime-id uniqueness and retained-runtime reservation guards for
  tenant-local Gateway users.
- [x] Add admin-only retained runtime list/transfer/purge routes.
- [x] Add initial Alice/Bob isolation tests for runs, ledgers, and artifacts.
- [x] Extend Alice/Bob matrix to KG/session memory, private workflows,
  prompt-cache key naming, session artifacts, and runtime-scoped defaults.
- [x] Prove regular users cannot use admin-only workspace helper/import/export
  surfaces.
- [x] Add machine-readable denial tests for first operator route families.
- [x] Add discovery leak tests for admin-only workspace artifact and
  prompt-cache provider-control availability.
- [x] Update docs with current route-family boundaries.

## Guidance for the implementing agent
Add authorization at the lowest shared Gateway layer available. Avoid scattering
ad hoc `if admin` checks without a policy table.

## Implementation note - 2026-05-30

This item is materially advanced but not fully closed. The current code now has
a central policy table and tested denials for operator/server-workspace route
families. The remaining closure criterion is the broader Alice/Bob isolation
matrix and discovery leak tests across every user-owned route family.

The latest security pass adds table-driven denial coverage for admin, audit,
process, backlog, triage, report, email, host, model residency, workspace,
bloc, and prompt-cache route families using a non-admin user with wildcard
scopes. This proves broad scopes cannot bypass `admin_required=True`; it does
not replace the remaining user-owned data-plane isolation matrix.

The latest isolation pass adds runtime-id uniqueness enforcement and extends
the cross-user matrix to run, ledger, artifact, session artifact, KG/session
memory, private workflow bundle, VisualFlow draft, prompt-cache naming, and
capability-default overlay surfaces. It also verifies that ordinary users see
admin-only workspace artifact import/export and provider prompt-cache controls
as unavailable in discovery, and that direct workspace helper/import calls
return machine-readable `403` denials.

This item should remain open only for future route families as they are added
or promoted. The user-requested Alice/Bob matrix extension is complete.

--- docs/backlog/planned/gateway-control-plane/0147_gateway_per_principal_config_secrets_defaults.md ---
# Planned: Gateway per-principal config, secrets, and defaults

## Metadata
- Created: 2026-05-30
- Status: Planned
- Completed: N/A

## ADR status
- Governing ADRs: ADR-0033, ADR-0035
- ADR impact: May revise existing ADR

## Context
Provider/model defaults and API keys are currently configured through Core
local config, environment variables, or duplicated app UI settings. Gateway is
becoming the hosted control-plane UX, but Core should remain the schema and
persistence authority for capability defaults and provider credentials.

## Current code reality
- ADR-0035 states Core owns capability default schema and persistence, while
  Gateway is the control-plane access layer.
- AbstractCore has config primitives for default models, provider API keys,
  base URLs, and capability route defaults.
- Flow mostly supports `Auto` and Gateway-discovered defaults.
- Code and Observer still have app-local settings/auth/defaults that do not yet
  fully match the Gateway browser-session model.
- Gateway currently supports per-principal capability defaults in hosted
  user-auth mode. Writes to
  `/api/gateway/config/capability-defaults/...` are stored under the selected
  runtime data plane as `config/abstractcore.json`, using Core's
  `capability_defaults` schema. Gateway no longer reads or writes
  `config/capability_defaults.json`.
- The admin/root data plane Core config acts as the Gateway baseline.
  Normal users inherit that Gateway baseline first, then apply their own
  runtime-scoped Core config override. User overrides do not mutate the Gateway
  baseline or other users' defaults. The target model is the same cascade
  expressed as Core capability defaults scoped to the selected runtime/user
  context.
- Gateway Console v0 exposes those runtime-scoped Core defaults.
- Gateway provider endpoint profiles now cover the first explicit
  per-principal/gateway-scoped provider-secret injection path for reusable
  virtual providers such as `endpoint:office-vllm`; broader secret vault,
  encryption, bridge, and delegated-tool rules remain open.

## Problem
Users must configure provider/model defaults repeatedly across apps, and hosted
users need their own API keys/defaults isolated from other users. Storing keys
in browser localStorage or mutating global env is wrong for multi-user Gateway
deployments.

## What we want to do
Expose Gateway APIs and UI for per-principal provider credentials, base URLs,
and capability defaults, while delegating schema/persistence mechanics to Core
or runtime-scoped Core config. Apps should consume Gateway defaults and only ask
for provider/model when a user intentionally pins an override.

## Why
This removes duplicated UX, aligns apps behind Gateway, and preserves user
isolation for secrets and model choices.

## Requirements
- Default resolution is preference selection within policy ceilings:
  operator/tenant allowlist and capability policy constrain request overrides,
  workflow pins, user defaults, tenant defaults, gateway defaults, and
  execution-host Core defaults.
- Resolved provider/model metadata reports non-secret provenance so UIs can show
  whether a value came from request, workflow, user, tenant, Gateway baseline,
  or Core.
- Explicit workflow pins remain reproducible unless denied by current host
  policy, in which case denial is explicit rather than silently rerouted.
- API keys and provider secrets are never stored in browser storage.
- Per-user secrets are injected only into that user's runtime context.
- Capability defaults must not expose secret values.
- Gateway Console can collect keys/defaults but must not become a second
  defaults owner independent of Core.
- Before implementation, record the Core/Gateway storage boundary: who encrypts
  secrets, who can read raw values, how rotation/deletion works, how backups are
  handled, and how access is audited.
- Secrets must be redacted from logs, ledgers, artifacts, tool traces, audit
  payloads, and discovery responses.
- Define propagation rules for subruns, bridges, delegated tools, and any
  admin/system execution path before those paths can use per-principal secrets.

## Suggested implementation
Add Gateway config endpoints that wrap Core config/default APIs for the current
principal. Use a per-runtime Core config root/file for capability defaults, and
keep Gateway provider endpoint profiles as the first Gateway-owned secret
injection path. Expose readiness checks and catalog previews in Gateway Console
only after that boundary is explicit.

## Scope
- Gateway APIs for provider credentials and capability defaults.
- Gateway Console config page.
- Per-principal isolation tests for secrets/defaults.
- App docs that point users to Gateway defaults.

## Non-goals
- Do not remove direct AbstractCore CLI/config for local developer usage.
- Do not force every app to remove explicit provider/model overrides.
- Do not store raw secrets in Gateway audit logs or browser storage.

## Dependencies and related tasks
- ADR-0035 and `docs/guide/capability-routing-defaults.md`.
- `0145_gateway_admin_console_bootstrap.md`
- `0146_gateway_rbac_scope_policy_matrix.md`
- `0153_gateway_browser_session_security_contract.md`
- `../../completed/0170_core_gateway_capability_defaults_config_convergence.md`
- `../../completed/0149_cross_app_gateway_auth_defaults_convergence.md`

## Expected outcomes
- A Gateway user can configure provider keys/defaults once and use them from
  Flow, Code, Observer, Assistant, and bridges.
- Alice's provider keys/defaults are not visible or usable by Bob.
- Apps default to Gateway/Core defaults unless a run/workflow explicitly pins.

## Validation
- Alice/Bob secret isolation tests.
- Default cascade tests.
- UI tests proving secrets are write-only or masked after creation.
- App smoke tests with blank provider/model resolving through Gateway defaults.

## Recent validation
- `python -m pytest abstractgateway/tests/test_gateway_security_middleware_unit.py abstractgateway/tests/test_gateway_principal_auth.py abstractgateway/tests/test_gateway_console.py abstractflow/tests/test_gateway_connection_config.py abstractflow/tests/test_web_gateway_proxy_auth.py -q` -> 51 passed, 2 warnings.
- `python -m compileall -q abstractgateway/src/abstractgateway abstractflow/web/backend` -> passed.
- `python -m pytest abstractgateway/tests` -> 261 passed, 2 skipped.
- `python -m pytest abstractgateway/tests/test_gateway_principal_auth.py abstractgateway/tests/test_gateway_security_middleware_unit.py abstractgateway/tests/test_gateway_console.py` -> 29 passed.
- `python -m pytest abstractgateway/tests/test_gateway_install_profiles.py abstractgateway/tests/test_gateway_capability_catalog_proxy.py` -> 29 passed.

## Progress checklist
- [x] Define initial per-principal capability-default storage boundary with
      Core schema and Gateway per-principal access control.
- [x] Migrate the initial Gateway overlay storage to per-runtime Core config
      files through `0170_core_gateway_capability_defaults_config_convergence`.
- [x] Define and implement the initial provider endpoint profile injection
      boundary for Gateway-owned virtual providers.
- [ ] Define provider-secret encryption/rotation/audit boundary beyond the
      initial local JSON store.
- [x] Define initial provenance behavior for Gateway and runtime-scoped Core
      defaults (`source=abstractcore.gateway_runtime` and
      `source=abstractcore.runtime`).
- [ ] Define runtime/subrun/bridge secret injection rules.
- [x] Add Gateway APIs for runtime-scoped capability-default overrides.
- [x] Add Gateway Console config UX for runtime-scoped defaults.
- [x] Update app/framework docs for current defaults behavior.
- [x] Add Alice/Bob default isolation tests.
- [x] Add initial endpoint profile secret redaction and non-admin scope tests.
- [ ] Add broader secret isolation tests after a complete vault/bridge boundary exists.

## Implementation note - 2026-05-30

This item is partially implemented for provider/model/base URL defaults.
Gateway provider endpoint profiles now provide the first raw provider credential
path without writing user secrets into browser storage, workflow JSON, or
process-wide environment variables. Profiles are resolved per request into
transient Runtime parameters and redacted from persisted Runtime observability.
The remaining design work is a fuller Core/Gateway secret contract covering
encryption at rest, rotation semantics, subruns, bridges, delegated tools,
audit, and deletion.

## Implementation note - 2026-06-01

The defaults boundary is now stricter: capability defaults are Core defaults
even when edited through Gateway. Gateway selects a Core config context for the
active runtime/user and writes Core's `capability_defaults` format there.
Existing `config/capability_defaults.json` Gateway overlays are ignored;
operators should recreate those defaults with `abstractgateway-config
set-default ...`.

Default cascade as implemented today: execution-host Core defaults provide the
base route set, the Gateway/root Core config provides the operator baseline,
and the current user's runtime Core config wins only for that user. Workflow pins and
per-request overrides still need to be resolved by run-start policy in later
items; do not silently reroute explicit workflow pins without an explicit
policy denial or override record.

## Guidance for the implementing agent
Keep the authority split crisp: Core owns config schema and low-level execution
config; Gateway owns authenticated hosted UX and per-principal access control.

--- docs/backlog/planned/gateway-control-plane/0148_gateway_workflow_registry_acl.md ---
# Planned: Gateway workflow registry ACLs

## Metadata
- Created: 2026-05-30
- Status: In progress
- Completed: N/A

## ADR status
- Governing ADRs: ADR-0018, ADR-0021, ADR-0033
- ADR impact: Needs new ADR or ADR revision

## Context
Flow authors VisualFlows and publishes bundles through Gateway. Runtime treats
workflow bundles as transport/execution artifacts and intentionally does not
own trust policy. Gateway must decide who can import, publish, run, promote,
deprecate, remove, or grant access to workflows.

## Current code reality
- Gateway has per-principal service routing and per-user flow/bundle storage.
- Gateway VisualFlow CRUD, publish, bundle upload/remove/deprecate endpoints
  exist but need clearer admin/user/catalog permission semantics.
- Flow currently consumes Gateway capability/discovery metadata and can hide or
  show actions, but server-side Gateway must enforce permissions.
- Existing per-principal `/visualflows` and bundle directories are private
  authoring/storage surfaces. A shared tenant/framework catalog should be an
  explicit Gateway-level metadata store, not an accidental reuse of one user's
  bundle directory.

## Problem
Workflow registration and permissions are not yet a clean control-plane model.
A globally visible workflow must not imply shared runtime state or unchecked
write/tool permissions. Users should be able to draft private workflows, while
admins control tenant/framework catalogs.

## What we want to do
Implement a two-tier Gateway workflow registry:
- private/user registry for drafts and user-owned workflows;
- tenant/framework catalog for admin-promoted immutable bundle versions with
  explicit ACLs and run policies.

Registry scopes should be explicit (`private`, `tenant_catalog`,
`framework_catalog`, and later `system`) so a `bundle_id` collision or guessed
id cannot silently switch between private and shared authority.

## Why
This lets Flow remain an authoring tool while Gateway becomes the trusted
workflow registry and permission authority.

## Requirements
- Non-admin users can draft/test private workflows in their own runtime.
- Admins can import/register/promote/deprecate/remove tenant/framework catalog
  bundles.
- Global/catalog workflows execute in the requesting user's runtime unless
  explicitly launched as admin/system workflows.
- ACL metadata lives in Gateway, not inside trusted bundle manifest fields.
- Workspace/tool/write permissions are intersected with user role, workflow ACL,
  tenant policy, and operator workspace policy.
- Effective run policy is materialized at run start and includes `run_as`,
  workflow visibility, allowed tools, workspace grants, secret access, approval
  mode, network/process permissions, and destructive-action permissions.
- Enforcement happens both at run start and at later tool/workspace/secret
  access points; a workflow allowed to start is not automatically allowed to
  perform every side effect it requests.
- Admin/system workflow launch semantics require explicit service-principal
  rules, approval gates, workspace binding, secret-source rules, and audit logs
  before they are enabled.
- Discovery exposes permission-aware action metadata for Flow UX.
- Existing workflows that depend on an older catalog workflow version keep
  running against that immutable version unless the version is explicitly
  blocked for safety. Updating a default workflow pointer must not rewrite
  historical bundle references or private workflows built on top of a previous
  version.

## Suggested implementation
Add catalog metadata records keyed by bundle id, version, sha256, status,
visibility, owner, publisher, promoter, timestamps, and ACL. Include review
status, checksum verification, and the immutable policy snapshot or policy
version used at run start. Keep bundle content immutable by version; updates
create new versions and optional default-pointer changes. Add run-start,
scheduled-run, subworkflow, and per-action checks that resolve catalog ACL and
effective run policy before execution.

Recommended state machine:
`private/draft -> submitted -> approved/published -> deprecated -> removed/tombstoned`.
Deprecation blocks new starts but preserves history/replay. Removal should keep
tombstones so dependent workflows can explain why a referenced version is no
longer startable.

## Scope
- Gateway registry metadata and ACL APIs.
- Run-start authorization for catalog/private workflows.
- Flow discovery contract updates for allowed/denied actions.
- Tests for private vs catalog workflows.
- Migration compatibility for existing private `/visualflows` and user bundle
  storage.

## Non-goals
- Do not make Runtime own ACL policy.
- Do not trust bundle-declared permissions without Gateway grants.
- Do not add a full marketplace UX in this item.
- Do not change `/bundles` from "current runtime bundles" to "global catalog"
  without an explicit scope or a separate catalog endpoint.

## Dependencies and related tasks
- `0146_gateway_rbac_scope_policy_matrix.md`
- `0145_gateway_admin_console_bootstrap.md`
- `0153_gateway_browser_session_security_contract.md` for browser-admin
  interactions with registry controls.
- Flow publish/import UI.
- Runtime workflow bundle validation.

## Expected outcomes
- Users cannot run or mutate workflows by guessing bundle ids.
- Admins can promote workflows to a tenant/framework catalog.
- Catalog workflows run in the caller's data plane by default.
- Deprecation blocks new starts while preserving historical replay.

## Validation
- Alice/Bob private workflow isolation tests.
- Non-admin publish-to-catalog denial tests.
- Catalog ACL start/deny tests.
- Immutable bundle version/sha256 tests.
- Default-pointer update tests proving existing version references are not
  rewritten.
- Scheduled-run and subworkflow policy inheritance tests.
- Workspace policy enforcement tests.

## Progress checklist
- [x] Define registry tiers and metadata schema.
- [ ] Define effective run policy and admin/system workflow semantics.
- [x] Add catalog ACL APIs.
- [x] Enforce run-start ACL/policy.
- [x] Enforce scheduled-run and catalog subworkflow status/policy checks.
- [ ] Enforce per-tool/per-workspace policy checks.
- [x] Update Gateway discovery metadata for catalog/private action split.
- [x] Add two-user tests.

## Implementation note - 2026-05-30

Implemented the first catalog-control slice in `abstractgateway`:

- Added a Gateway-owned workflow catalog store under the root Gateway data dir.
  Catalog bundle bytes are immutable by `scope + tenant + bundle_id +
  bundle_version + sha256`; re-uploading the same version with different bytes
  is rejected.
- Added explicit catalog endpoints:
  - user-visible `GET /api/gateway/workflow-catalog`;
  - admin-only upload, promote, default pointer, ACL, deprecate, block, and
    tombstone routes under `/api/gateway/admin/workflow-catalog`.
- Kept private `/api/gateway/visualflows` and `/api/gateway/bundles` as
  per-principal runtime authoring/storage surfaces.
- Loaded tenant catalog bundles into each user's runtime host under internal
  catalog bundle ids so private bundle ids cannot shadow catalog ids.
- Added `registry_scope` to run start and scheduled run requests. Catalog
  starts resolve through ACLs and the admin default pointer, then run in the
  requesting user's runtime.
- Added host-side guards so direct internal catalog starts require a
  Gateway-issued signed workflow-policy snapshot, and catalog subworkflow
  starts are blocked when the signature, runtime binding, ACL, content hash, or
  catalog status no longer validates.
- Made catalog scope explicit: omitted `registry_scope` stays on the private
  runtime bundle surface and does not silently fall through to the shared
  catalog.
- Blocked direct private-bundle inspection of catalog-internal bundle ids and
  added ACL-aware catalog flow/schema inspection routes.
- Reworked catalog install so byte immutability and metadata updates are
  checked in one critical section; same-content inactive re-uploads cannot move
  the default pointer back to an inactive version.
- Rejected `framework_catalog` at the API until cross-tenant host loading and
  policy semantics are implemented.
- Added discovery metadata so clients can distinguish private bundle actions
  from catalog/admin actions.

Validation:

- `python -m py_compile abstractgateway/src/abstractgateway/workflow_catalog.py abstractgateway/src/abstractgateway/config.py abstractgateway/src/abstractgateway/service.py abstractgateway/src/abstractgateway/hosts/bundle_host.py abstractgateway/src/abstractgateway/routes/gateway.py abstractgateway/tests/test_gateway_workflow_catalog_acl.py`
- `python -m pytest abstractgateway/tests/test_gateway_workflow_catalog_acl.py -q` -> `2 passed`
- `python -m pytest abstractgateway/tests/test_gateway_workflow_catalog_acl.py abstractgateway/tests/test_gateway_workflow_deprecation.py abstractgateway/tests/test_gateway_principal_auth.py abstractgateway/tests/test_capabilities_endpoint_contract.py abstractgateway/tests/test_abstractflow_editor_gateway_contract.py -q`
- `python -m pytest abstractgateway/tests -q` -> `265 passed, 2 skipped`

Remaining work:

- Define any future admin/system run mode separately; current catalog runs are
  caller-runtime only.
- Intersect workflow catalog policy with tool, workspace, secret, network, and
  destructive-action policies at effect/action boundaries.
- Build Flow/Gateway Console UX for catalog promotion, default movement, ACLs,
  and version status operations.

## Refinement note - 2026-05-30

Admin install/update/remove of default workflows should be modeled as catalog
version management, not file replacement. Admins can promote a new immutable
bundle version and move a default pointer, but private workflows and historical
runs must retain their exact version reference. A later admin/system workflow
execution mode must be explicit and audited; the default for catalog workflows
is still "run in the requesting user's runtime."

## Guidance for the implementing agent
Keep authoring and authority separate: Flow may request publication, but Gateway
must decide and audit the permission result.

--- docs/backlog/completed/0149_cross_app_gateway_auth_defaults_convergence.md ---
# Completed: Cross-app Gateway auth and defaults convergence

## Metadata
- Created: 2026-05-30
- Status: Completed
- Completed: 2026-05-30

## ADR status
- Governing ADRs: ADR-0018, ADR-0033, ADR-0035
- ADR impact: No ADR change in this pass; the convergence behavior is covered
  by the existing Gateway/control-plane/defaults ADRs and the browser-session
  contract tracked in `0153`.

## Context
AbstractFlow now uses browser-session Gateway user auth. Other apps still have
older or duplicated auth/default patterns. Code, Observer, Assistant, and any
browser UI should converge on the same Gateway session and default-resolution
model.

## Current code reality
- Flow uses Gateway URL + user + token only during sign-in; it exchanges the
  user token for a Gateway browser session and stores only the opaque session id
  in an HTTP-only browser cookie.
- Flow mutating Gateway proxy calls carry a CSRF token, and Gateway validates it
  for session-authenticated writes.
- Code Web now supports the same hosted Gateway URL + user + token sign-in
  shape. Its static server creates an app-scoped browser session by exchanging
  the user token with Gateway server-side, stores no bearer token in
  localStorage, proxies `/api/*` with `X-AbstractGateway-Session`, and requires
  an app CSRF header for mutating calls.
- Observer now has the same hosted browser-session path and no longer persists
  Gateway bearer tokens in localStorage. Its direct bearer mode remains a local
  development escape hatch.
- Flow static/Vite-hosted session login now consumes Gateway's cookie-based
  login response correctly; it no longer assumes the Gateway session id and
  CSRF token are returned in JSON.
- Code Web start flow no longer blocks when provider/model are blank; hosted
  runs can rely on Gateway/Core defaults unless the user sets an explicit
  override.
- Observer and Code Web still have app-specific UI copy and settings layouts;
  a shared auth/default component remains optional if duplication grows.

## Problem
Users must learn different auth/default models for each app. Duplicated
provider/model/default UI causes drift and can defeat per-user isolation.

## What we want to do
Move every hosted app to Gateway user-session auth and Gateway/Core defaults.
Apps should expose provider/model only as explicit per-run, per-workflow, or
advanced overrides.

## Why
This improves security, reduces duplicated UI, and makes Gateway the consistent
deployment control plane.

## Requirements
- Flow, Code, Observer, Assistant, and future browser apps use Gateway
  user-session auth in hosted mode.
- No hosted browser app stores bearer tokens in localStorage.
- Hosted mode detection and session semantics come from
  `0153_gateway_browser_session_security_contract.md`; local/offline app modes
  remain allowed to keep local development ergonomics.
- Apps use Gateway catalogs/defaults by default.
- Provider/model pins remain available where they are natural task-specific
  overrides.
- Disconnect/sign-out clears only the browser session for that app/domain.

## Per-app hosted/local behavior matrix

This matrix is the concrete meaning of "cross-app convergence": each app keeps
its natural local ergonomics, but hosted browser surfaces share the Gateway
session/default model.

| App/surface | Hosted auth behavior | Local/development behavior | Default provider/model behavior | Notes |
|---|---|---|---|---|
| AbstractFlow web | User enters Gateway URL, user id, and user token once. The host exchanges the token for a Gateway browser session and proxies Gateway calls with session + CSRF. | Local script can print a dev user/token and start Flow/Gateway together. No ambient admin login should appear in the browser. | Nodes default to Gateway/Core capability defaults unless the flow pins provider/model. | Reference browser UX for Gateway sign-in. |
| AbstractCode Web | Hosted server exchanges Gateway user credentials server-side, stores only app-scoped session cookies, strips bearer tokens from saved browser settings, and proxies `/api/*`. | CLI/env bearer token remains acceptable for trusted local runs. | Blank provider/model means "use Gateway defaults"; explicit per-run overrides remain allowed. | No localStorage bearer token in hosted mode. |
| AbstractObserver web | Hosted server follows the same app-scoped session proxy and token-storage stripping as Code Web. | Direct bearer-token mode remains a local/operator escape hatch. | Observer should read Gateway catalogs/defaults for display and filters, not own global model defaults. | Observer remains observability UX, not the admin setup surface. |
| Gateway Console | Native Gateway browser-session app. Users sign in with user id + token; admins manage users and Gateway baselines; normal users manage their own runtime defaults. | Same URL works locally; dev scripts print the admin/user credential to enter. | Admin/default runtime owns Gateway baseline Core config; user runtime owns a scoped Core config override. Provider/model selectors must be discovery-driven. | Console is account/admin/config, not deep runtime exploration. |
| AbstractAssistant desktop | Not a hosted browser app today. It can keep CLI/env token or stored local config for trusted desktop use. | Same as hosted until a browser Assistant exists. | Gateway mode should prefer Gateway/Core defaults and only send explicit overrides when the user chooses them. | Do not force browser-cookie auth onto terminal/desktop-only surfaces. |
| Scripts/automation | Use bearer tokens or admin/user tokens directly as server-to-server credentials. | Same. | Prefer Gateway defaults unless a reproducible run needs explicit provider/model. | Automation is not browser storage; protect tokens with OS/process controls. |

Shared auth/default UI extraction is intentionally deferred. The current
duplication is understandable and app-specific enough that a shared component
could harden the wrong abstraction too early. Extract a shared component only
after two or more browser apps need the same fields, validation, cookie-status
display, logout behavior, and defaults picker with no app-specific branching.

## Design review tensions

The useful disagreement in this item is not "should apps converge" versus "do
nothing"; it is where convergence should stop.

- Centralized hosted auth/defaults vs app-local ergonomics: hosted browser apps
  should share the Gateway browser-session and Gateway/Core defaults model, but
  desktop, CLI, and trusted local development flows should keep direct token
  ergonomics. Forcing cookie auth onto every client would make automation and
  local development worse without improving hosted browser safety.
- Shared component now vs delayed extraction: a common sign-in/defaults
  component would reduce visual drift, but the apps still have different
  layouts, status affordances, and local modes. Extracting now would likely
  encode the wrong abstraction. The threshold is repeated identical behavior,
  not the existence of similar fields.
- Gateway baseline defaults vs per-user overrides: Gateway should provide the
  tenant/admin baseline so apps stop duplicating global provider/model
  configuration. Per-user overlays remain a separate `0147` concern so one
  user's defaults do not silently change everyone else's behavior.
- Gateway Console vs Observer or a future Manager/Explorer: account, admin,
  auth, and baseline configuration belong in the Gateway Console. Observer
  should stay focused on runs and platform observability. Runtime exploration is
  intentionally separate until `0151` proves the contract and UX pressure.
- Browser security vs operator escape hatches: hosted browser mode must not
  persist bearer tokens in localStorage. Server-to-server scripts and local
  operator tools can still use tokens directly, but docs and startup scripts
  must label that as trusted-local or automation behavior.

## Suggested implementation
Audit each app. Introduce shared session/auth helpers or a small common UI kit
component if duplication becomes high. Migrate app docs and settings screens to
Gateway defaults.

## Scope
- App auth/session flows.
- App provider/model default UX.
- Docs and tests.

## Non-goals
- Do not remove local/offline app modes.
- Do not remove explicit provider/model overrides needed for reproducibility.
- Do not make Observer the admin setup UI.

## Dependencies and related tasks
- `0147_gateway_per_principal_config_secrets_defaults.md`
- `0145_gateway_admin_console_bootstrap.md`
- `0153_gateway_browser_session_security_contract.md`
- `0150_observer_manager_responsibility_split.md`

## Expected outcomes
- A Gateway user can sign in consistently across apps.
- Apps default to Gateway/Core configuration without duplicating global default
  settings.
- Hosted app auth no longer relies on app-local admin/server tokens.

## Validation
- Per-app login/logout tests.
- Token storage audit proving no localStorage bearer token in hosted mode.
- Blank provider/model smoke tests resolving through Gateway defaults.
- Cross-app Alice/Bob isolation smoke tests.

## Progress checklist
- [x] Audit Code Web auth/default storage.
- [x] Audit Observer auth/default storage.
- [x] Audit Assistant Gateway mode auth/defaults.
- [x] Migrate Code Web hosted mode to Gateway browser-session proxy auth.
- [x] Migrate Observer hosted mode to Gateway browser-session proxy auth.
- [x] Fix Flow static/Vite session login to parse Gateway login cookies.
- [x] Define per-app hosted/local auth/default behavior matrix.
- [x] Decide whether to extract shared auth/default UX now. Result: defer; the
  current app-specific duplication is acceptable and a shared component should
  wait until two or more apps need the same fields, validation, status display,
  logout behavior, and defaults picker without app-specific branching.
- [x] Add initial token-storage and hosted proxy smoke tests.
- [x] Update package and framework docs/tests for the current convergence
  behavior.

## Recent audit notes
- Code Web and Observer now store only app-scoped browser-session cookies in
  hosted mode. Their settings persistence explicitly strips `auth_token`, and
  their packaged servers reject browser-supplied Gateway URL changes on
  non-local hosted UI hostnames unless an explicit app override is enabled.
- Assistant remains a local/desktop client and can keep CLI/env bearer-token
  ergonomics until a hosted browser surface exists.
- Flow is the reference implementation for the current browser session contract.

## Recent validation
- `npm test -- --run src/lib/storage.test.ts` in `abstractcode/web` -> 1 passed.
- `npm test -- --run src/ui/auth_storage.test.ts` in `abstractobserver` -> 1 passed.
- `npm run build` in `abstractcode/web` -> passed.
- `npm run build` in `abstractobserver` -> passed.
- `npm run build` in `abstractflow/web/frontend` -> passed.
- `node --check abstractcode/web/bin/cli.js && node --check abstractobserver/bin/cli.js && node --check abstractflow/web/frontend/bin/cli.js` -> passed.
- Stub-Gateway hosted-session proxy smoke tests for AbstractCode Web,
  AbstractObserver, and AbstractFlow static server -> passed. The smoke tests
  verified cookie-based login exchange, no token leakage into app cookies,
  Gateway session/CSRF injection, browser cookie stripping, Authorization
  stripping, and app CSRF enforcement for mutating proxied calls.
- `python -m pytest abstractflow/tests/test_gateway_connection_config.py abstractflow/tests/test_web_gateway_proxy_auth.py -q` -> 21 passed, 2 warnings.
- `python -m pytest abstractgateway/tests/test_gateway_principal_auth.py abstractgateway/tests/test_gateway_principal_isolation_matrix.py abstractgateway/tests/test_capabilities_endpoint_contract.py abstractgateway/tests/test_abstractflow_editor_gateway_contract.py -q` -> 26 passed.

## Guidance for the implementing agent
Preserve task-specific app ergonomics. The goal is not to make all apps look the
same, but to make identity, defaults, and secrets consistent.

## Completion report

Date: 2026-05-30

Summary:
- Documented the concrete per-app hosted/local auth/default behavior matrix for
  AbstractFlow Web, AbstractCode Web, AbstractObserver Web, Gateway Console,
  AbstractAssistant desktop, and scripts/automation.
- Confirmed the shared auth/default component should not be extracted yet. The
  right criterion is repeated identical behavior across multiple browser apps,
  not a desire for visual uniformity.
- Verified the current hosted browser apps use Gateway browser-session proxy
  auth and do not persist Gateway bearer tokens in browser settings.
- Repaired one stale AbstractFlow CLI doc sentence so it now says Flow stores
  the opaque Gateway session id, not the user token.

Files or symbols touched:
- `docs/backlog/completed/0149_cross_app_gateway_auth_defaults_convergence.md`
- `docs/backlog/overview.md`
- `docs/backlog/planned/gateway-control-plane/README.md`
- `docs/backlog/planned/gateway-control-plane/0147_gateway_per_principal_config_secrets_defaults.md`
- `docs/backlog/planned/gateway-control-plane/0153_gateway_browser_session_security_contract.md`
- `docs/guide/gateway-security.md`
- `llms.txt`, `llms-full.txt`
- `abstractflow/docs/cli.md`

Validation:
- `rg` audit across AbstractFlow, AbstractCode Web, AbstractObserver, and root
  docs for hosted auth/default/token-storage wording.
- `python -m pytest abstractgateway/tests/test_gateway_principal_auth.py abstractgateway/tests/test_gateway_principal_isolation_matrix.py abstractgateway/tests/test_capabilities_endpoint_contract.py abstractgateway/tests/test_abstractflow_editor_gateway_contract.py -q` -> 26 passed.
- `python -m py_compile abstractgateway/src/abstractgateway/routes/gateway.py abstractgateway/src/abstractgateway/security/sessions.py abstractgateway/src/abstractgateway/security/gateway_security.py` -> passed.
- `python /Users/albou/.codex/skills/.system/skill-creator/scripts/quick_validate.py /Users/albou/.codex/skills/architect` -> passed.
- `python /Users/albou/.codex/skills/.system/skill-creator/scripts/quick_validate.py /Users/albou/.codex/skills/review` -> passed.

Behavior changes:
- No runtime behavior change is introduced by completing this item. The
  behavior had already landed in Flow, Code Web, Observer, Gateway session
  routing, and Gateway defaults. This completion closes the planning item by
  making the behavior matrix, deferral decision, docs state, and validation
  explicit.

Residual risks:
- Direct bearer-token mode remains available for local/operator development in
  Code Web and Observer. This is intentional, but hosted docs must continue to
  steer users toward browser-session proxy auth.
- AbstractAssistant is still a local/desktop client. If a hosted browser
  Assistant appears, it should implement the same Gateway browser-session
  contract rather than reusing desktop token storage.
- A shared auth/default component should be revisited only when duplicate app
  implementation creates proven drift or maintenance cost.

Follow-ups:
- Keep `0153` open as the durable session/cookie contract while new browser
  apps are added.
- Keep `0147` open for provider-secret storage/injection boundaries; `0149`
  does not solve per-user provider credentials.
- See proposed item `0155` before extracting a shared hosted proxy helper; the
  current posture is package-local code plus conformance tests.

--- docs/backlog/planned/gateway-control-plane/0150_observer_manager_responsibility_split.md ---
# Planned: Observer and Manager responsibility split

## Metadata
- Created: 2026-05-30
- Status: Planned
- Completed: N/A

## ADR status
- Governing ADRs: ADR-0018, ADR-0021, ADR-0033
- ADR impact: May revise existing ADR

## Context
Observer is useful but broad. It has grown toward observability, launch,
backlog, email, process, and operational pages. Gateway now needs account,
admin, config, user/runtime, and workflow-permission UX. Putting all of that
into Observer would make app boundaries unclear.

## Current code reality
- Observer is the existing runtime/run observability app.
- Gateway has control-plane APIs and is gaining identity/routing authority.
- There is no dedicated Manager/Admin app yet.
- Gateway Console v0 is planned as a narrow built-in control-plane UX.

## Problem
Without a responsibility split, Observer can become a catch-all operational app
and Gateway can become a monolithic UI. Users need a clearer product map.

## What we want to do
Define app responsibilities early enough to contain scope before Gateway
Console, Runtime Explorer, and cross-app migrations expand. Proposed ownership:
- Gateway/Gateway Console: identity, account, admin, config, runtime summary,
  workflow registry permissions.
- Observer: run/ledger/artifact observability, runtime health, audit views, and
  operational monitoring.
- Flow: workflow authoring.
- Code: coding-agent UX.
- Assistant: personal assistant UX.
- Future Explorer/Manager: only if Gateway Console or Observer becomes too broad.

## Why
Clear product boundaries improve user onboarding, code ownership, security, and
release planning.

## Requirements
- Audit Observer high-trust surfaces: backlog execution, email, process control,
  env management, scheduling, broad runtime management.
- Immediately classify existing high-trust surfaces as observability,
  admin/config, operator-only, domain-app, or migration candidate.
- Hosted-mode unsafe paths must be marked operator-only behind Gateway
  authorization or hidden/disabled until `0146` enforcement exists.
- Decide which surfaces remain Observer operator views and which move behind
  Gateway Console/Manager APIs.
- Define links between apps instead of duplicating full functionality.
- Keep all sensitive operations authorized by Gateway, not by frontend routing.

## Suggested implementation
Write a short app-boundary decision doc or ADR update, then migrate one surface
at a time. Start by documenting which current Observer pages are observability,
which are admin/config, and which are domain apps.

## Scope
- App responsibility audit.
- Docs/ADR update.
- Containment notes for high-trust Observer/Gateway routes that should not be
  presented as ordinary hosted-user UX.
- Follow-up tickets for migrations.

## Non-goals
- Do not create a new package before the boundary is proven.
- Do not remove useful Observer pages without replacement.
- Do not move Flow authoring into Gateway.

## Dependencies and related tasks
- `0145_gateway_admin_console_bootstrap.md`
- `0146_gateway_rbac_scope_policy_matrix.md`
- `0151_runtime_explorer_contract.md`
- `0152_abstractmanager_package_extraction.md`

## Expected outcomes
- Users can understand which app to open for admin/config vs observability vs
  authoring.
- High-trust Observer surfaces are either clearly operator-only or moved into
  Gateway-managed control plane.
- Future package split decisions are grounded in actual use, not naming alone.

## Validation
- Documentation names each app's responsibility and non-goals.
- UI navigation links users to the right app/surface.
- Authorization tests protect high-trust surfaces.

## Progress checklist
- [ ] Audit Observer surfaces.
- [ ] Draft app responsibility matrix.
- [ ] Classify high-trust surfaces and containment status.
- [ ] Update ADR/docs.
- [ ] Create migration backlog items where needed.

## Guidance for the implementing agent
Do not solve this by naming a new app too early. First make the ownership model
explicit, then extract only when code and UX pressure justify it.

--- docs/backlog/planned/gateway-control-plane/0153_gateway_browser_session_security_contract.md ---
# Planned: Gateway browser session security contract

## Metadata
- Created: 2026-05-30
- Status: Planned
- Completed: N/A

## ADR status
- Governing ADRs: ADR-0018, ADR-0021, ADR-0033
- ADR impact: Needs new ADR or ADR revision

## Context
Flow already uses browser-session Gateway auth, and the Gateway Console plus
other hosted apps need the same security model. Gateway user tokens are bearer
credentials assigned to a principal. Browser sessions are app-facing sessions
that should be shorter-lived, revocable, and scoped to the browser/app origin.

## Current code reality
- Gateway exposes `/api/gateway/session/login` and
  `/api/gateway/session/logout` for registry-backed user browser sessions.
- Flow accepts Gateway URL, user id, and user token at connection time, validates
  the token, exchanges it for an opaque Gateway browser session, and stores only
  the session id in an HTTP-only Flow cookie across the Python host, Node static
  host, and Vite dev proxy.
- Flow stores a CSRF token in a separate browser cookie and sends it as
  `X-AbstractFlow-CSRF`; the Flow proxy forwards `X-AbstractGateway-Session`
  and `X-AbstractGateway-CSRF` to Gateway for mutating requests.
- Gateway invalidates browser sessions when the backing user is disabled,
  deleted, or its token hash changes.
- Gateway session cookies are HTTP-only for the session id and readable only for
  the CSRF token. They use `SameSite=Lax`, path `/`, no `Secure` flag on plain
  HTTP, and `Secure` when the request scheme or trusted proxy header is HTTPS.
  Non-remembered sessions omit `Max-Age`; remembered sessions set `Max-Age`.
- Gateway user records store hashed bearer tokens and return generated/rotated
  tokens only once.
- Code Web and Observer now follow the same hosted-session proxy contract in
  their packaged Node servers: Gateway user credentials are exchanged
  server-side, app-scoped session cookies are issued, `/api/*` is proxied with
  `X-AbstractGateway-Session`, writes require app CSRF headers, and saved
  browser settings strip bearer tokens. They now also reject browser-supplied
  Gateway URL changes on non-local hosted UI hostnames unless an explicit
  app-specific override is enabled. Host checks trust the request `Host` header
  by default; forwarded host headers require an explicit trust-proxy env setting.
- Assistant remains a local/desktop app and can keep its CLI/env token
  ergonomics until a hosted browser surface exists.

## Problem
Without a formal contract, apps can blur long-lived Gateway user tokens with
browser sessions, duplicate token handling, store bearer tokens in browser
storage, or implement inconsistent logout/revocation semantics. Admin UI work
would then normalize brittle auth behavior.

## What we want to do
Define and implement a hosted browser-session contract for Gateway-connected
apps. Apps should exchange a Gateway user token for an app/browser session and
then proxy Gateway requests with server-side session credentials. Browser code
must not retain the bearer token after connection.

## Requirements
- Browser apps never store Gateway bearer tokens in `localStorage`,
  `sessionStorage`, IndexedDB, or readable cookies in hosted mode.
- Session cookies are `HttpOnly`, `Secure` when served over HTTPS, use an
  explicit `SameSite` posture, and have deliberate domain/path scoping.
- Mutating routes have a CSRF posture appropriate to the deployment mode.
- Sessions have expiry, logout, and server-side revocation behavior.
- A rotated or disabled Gateway user token invalidates future session creation;
  policy defines whether existing sessions are revoked immediately.
- Error and discovery responses distinguish unauthenticated, expired,
  revoked, and forbidden states without leaking hidden resources.
- Local/offline app modes may keep their existing development ergonomics, but
  hosted mode must use this contract.

## Scope
- Gateway-connected browser session contract.
- Shared app expectations for Flow, Code Web, Observer, Gateway Console, and
  future browser apps.
- Tests for cookie flags, logout, revocation, and token-storage audit.

## Non-goals
- Do not replace Gateway user tokens as the principal credential.
- Do not force terminal/local-only apps to use browser cookies.
- Do not implement a full external IdP/OIDC flow in this item.

## Dependencies and related tasks
- `0146_gateway_rbac_scope_policy_matrix.md`
- `0145_gateway_admin_console_bootstrap.md`
- `../../completed/0149_cross_app_gateway_auth_defaults_convergence.md`
- `docs/backlog/completed/0141_flow_browser_session_gateway_auth.md`

## Expected outcomes
- Hosted browser apps share one clear session/security model.
- Admin console work does not rely on raw bearer-token storage in the browser.
- Cross-app convergence has a concrete auth contract to implement against.

## Validation
- Browser tests prove Gateway bearer tokens are not present in web storage.
- Cookie flag tests cover hosted and local-dev variants.
- Logout and revocation tests prevent continued Gateway proxying.
- CSRF tests cover mutating routes exposed through app sessions.
- Origin tests cover same-origin/localhost and explicitly allowlisted
  cross-origin hosted apps while rejecting untrusted origins.

## Recent validation
- `npm test -- --run src/lib/storage.test.ts` in `abstractcode/web` -> 1 passed.
- `npm test -- --run src/ui/auth_storage.test.ts` in `abstractobserver` -> 1 passed.
- Stub-Gateway hosted-session proxy smoke tests for AbstractCode Web,
  AbstractObserver, and AbstractFlow static server -> passed.
- `npm run build` in `abstractcode/web`, `abstractobserver`, and
  `abstractflow/web/frontend` -> passed.
- `python -m pytest abstractgateway/tests/test_gateway_principal_auth.py abstractgateway/tests/test_gateway_security_middleware_unit.py abstractflow/tests/test_gateway_connection_config.py abstractflow/tests/test_web_gateway_proxy_auth.py` -> 44 passed.
- `npm run build` in `abstractflow/web/frontend` -> TypeScript and Vite production build passed.
- `python -m pytest abstractgateway/tests/test_gateway_principal_auth.py abstractgateway/tests/test_gateway_principal_isolation_matrix.py abstractgateway/tests/test_capabilities_endpoint_contract.py abstractgateway/tests/test_abstractflow_editor_gateway_contract.py -q` -> 26 passed.
- `python -m py_compile abstractgateway/src/abstractgateway/routes/gateway.py abstractgateway/src/abstractgateway/security/sessions.py abstractgateway/src/abstractgateway/security/gateway_security.py` -> passed.

## Progress checklist
- [x] Define session issuance and cookie semantics for Gateway and Flow.
- [x] Define CSRF and logout/revocation behavior for Gateway and Flow.
- [x] Add shared hosted-mode detection expectations.
- [x] Add Gateway/Flow tests and app migration guidance.
- [x] Migrate Code Web and Observer from raw browser bearer-token storage.
- [x] Add full cookie/origin/session matrix tests across HTTP, HTTPS,
  same-origin, allowlisted cross-origin, untrusted cross-origin, expiry, logout,
  and token-rotation revocation.

## Guidance for the implementing agent
Keep user tokens and browser sessions separate. A token authenticates a
principal to Gateway; a browser session lets a specific app/browser continue
acting for that principal under tighter cookie, expiry, and revocation rules.

--- docs/backlog/completed/0154_multi_user_security_release_blockers.md ---
# Planned: Multi-user security release blockers

## Metadata
- Created: 2026-05-30
- Status: Completed
- Completed: 2026-05-30

## ADR status
- Governing ADRs: ADR-0018, ADR-0021, ADR-0033
- ADR impact: None. This item enforces the current Gateway control-plane and browser-session decisions.

## Context
The multi-user Gateway work introduced user-authenticated browser sessions,
per-principal runtime routing, and hosted app proxy sessions. A review found
three release-blocking gaps: retained runtime data could be reassigned after
user deletion, Code Web and Observer allowed browser-supplied Gateway URL
changes on hosted hosts, and the published Gateway/Flow launcher did not
bootstrap Gateway users for browser login.

## Current code reality
- `abstractgateway/src/abstractgateway/users.py` rejects duplicate runtime ids
  among live users but, before this item, user deletion removed the only
  assignment record while retaining runtime data on disk.
- `abstractflow` rejects browser Gateway URL changes on non-local hosts unless
  explicitly enabled, but `abstractcode/web/bin/cli.js` and
  `abstractobserver/bin/cli.js` did not have the same guard.
- `scripts/gateway-flow-local.sh` enables Gateway user auth, prepares the
  default `admin` user, and prints Flow login credentials. `scripts/gateway-flow.sh`
  still used only the legacy Gateway token and did not create a browser-login user.

## Problem
These gaps can break isolation, allow hosted app proxy misuse, or make the
published startup path unusable for the new browser-session auth model.

## What we want to do
Close the release blockers with targeted changes that preserve the current
architecture: Gateway owns users/runtime assignment, hosted apps proxy through
browser sessions, and scripts print only the user credentials needed by Flow.

## Why
Multi-user Gateway support must fail closed before release. A user or admin
should not accidentally route one person's retained runtime data to another
principal, and hosted apps must not become arbitrary browser-directed proxies.

## Requirements
- Deleting a Gateway user reserves that user's runtime id while runtime data is
  retained, preventing assignment to a different principal in the same tenant.
- Flow, Code Web, and Observer share the same hosted/non-local browser Gateway
  URL mutation rule.
- Published-package `gateway-flow.sh` enables user auth, prepares an `admin`
  user by default, caches the generated user token locally, and prints Gateway
  URL/user/token for Flow sign-in.
- `.DS_Store` files are ignored and must not remain tracked.

## Suggested implementation
- Add runtime reservation/tombstone records to the Gateway user registry.
- Add Code Web and Observer URL guards matching Flow and cover them with
  hosted-server tests.
- Mirror the local launcher's user-bootstrap behavior in the published script.
- Remove tracked `.DS_Store` files and keep ignore files aligned.

## Scope
- AbstractGateway user registry and console copy.
- AbstractCode Web and AbstractObserver packaged Node servers plus tests.
- Root startup scripts and docs.
- Ignore hygiene for `.DS_Store`.

## Non-goals
- Do not add a purge, restore, or runtime-transfer UI in this item.
- Do not extract a shared hosted proxy helper unless implementation pressure is
  proven by tests or another drift incident.
- Do not change Core or Runtime behavior.

## Dependencies and related tasks
- `0146_gateway_rbac_scope_policy_matrix.md`
- `0153_gateway_browser_session_security_contract.md`
- `../../completed/0149_cross_app_gateway_auth_defaults_convergence.md`

## Expected outcomes
- A deleted user's retained runtime cannot be assigned to another user.
- Hosted Code/Observer reject remote-host Gateway URL changes just like Flow.
- `./scripts/gateway-flow.sh` produces usable `admin` browser-login credentials.
- Release hygiene no longer includes tracked `.DS_Store` files.

## Validation
- Gateway user-registry tests cover delete-plus-cross-user-runtime-reuse.
- Code/Observer hosted server tests cover remote-host URL mutation rejection.
- `bash -n scripts/gateway-flow.sh scripts/gateway-flow-local.sh`.
- Targeted Gateway, Flow, Code, and Observer test suites pass.

## Progress checklist
- [x] Add Gateway runtime reservations.
- [x] Add Code Web and Observer hosted URL guards.
- [x] Align published launcher user bootstrap.
- [x] Clean `.DS_Store` tracking/ignore state.
- [x] Update docs and completion report.

## Guidance for the implementing agent
Keep the fixes narrow. The security model belongs in Gateway and in hosted app
proxy boundaries; do not move user/routing ownership into Flow, Code, Observer,
Core, or Runtime.

## Architecture decision summary

Decision question: how should the release blockers be closed without blurring
Gateway/app responsibilities?

Alternatives considered:
- Disable instead of delete users. This would preserve runtime ownership, but
  would change the user-management API and make the console's delete action
  misleading.
- Add retained-runtime reservations on delete. This preserves existing delete
  UX while preventing cross-principal runtime reassignment.
- Add a full purge/transfer workflow now. This would be most explicit, but it
  adds admin UX and irreversible-data semantics beyond the release blocker.

Chosen design: delete removes the user credential and creates a retained-runtime
reservation. A different same-tenant principal cannot reuse the runtime id while
data is retained. Recreating the same logical principal may reclaim its own
reservation. Explicit purge/transfer remains a future admin workflow, not a
silent side effect.

For hosted app proxy duplication, the chosen near-term design is conformance
tests plus matching URL guards in Code Web and Observer. A shared helper is
proposed in `0155`, but not extracted in this item because the immediate
security risk is closed by tests and the package boundary is not yet proven.

## Completion report

Date: 2026-05-30

Summary:
- Added `runtime_reservations` to the Gateway user registry. Deleting a user now
  reserves the retained runtime id for that principal and blocks assigning it
  to another user in the same tenant.
- Updated Gateway Console delete copy so admins see that runtime data is
  retained and the runtime id remains reserved.
- Added hosted non-local Gateway URL guards to AbstractCode Web and
  AbstractObserver packaged Node servers, matching Flow's behavior.
- Tightened Flow/Code/Observer host checks so forwarded host headers are used
  only when explicit trust-proxy env vars are set.
- Added Code/Observer hosted-server tests proving remote-host browser Gateway
  URL changes are rejected, including forged `X-Forwarded-Host` loopback
  headers.
- Updated `scripts/gateway-flow.sh` to enable Gateway user auth, prepare the
  default `admin` browser user, cache the generated user token, and print the
  Gateway URL/user/token for Flow sign-in.
- Removed tracked `.DS_Store` files from AbstractObserver and added the missing
  ignore rule.
- Updated user-facing docs, backlog, and LLM context files.

Files or symbols touched:
- `abstractgateway/src/abstractgateway/users.py`
- `abstractgateway/src/abstractgateway/console.py`
- `abstractgateway/tests/test_gateway_principal_auth.py`
- `abstractcode/web/bin/cli.js`
- `abstractcode/web/src/lib/hosted_gateway_url_guard.test.ts`
- `abstractobserver/bin/cli.js`
- `abstractobserver/src/ui/hosted_gateway_url_guard.test.ts`
- `abstractobserver/.gitignore`
- `abstractflow/web/backend/services/gateway_connection.py`
- `abstractflow/web/backend/routes/connection.py`
- `abstractflow/web/frontend/bin/cli.js`
- `abstractflow/web/frontend/vite.config.ts`
- `abstractflow/tests/test_gateway_connection_config.py`
- `scripts/gateway-flow.sh`
- Root, Code, Observer, and Gateway docs/LLM context files.

Validation:
- `python -m pytest tests/test_gateway_principal_auth.py tests/test_gateway_principal_isolation_matrix.py tests/test_gateway_security_middleware_unit.py tests/test_gateway_workflow_catalog_acl.py tests/test_gateway_console.py tests/test_gateway_install_profiles.py tests/test_gateway_embeddings_endpoint.py -q` in `abstractgateway` -> 54 passed.
- `python -m pytest tests/test_gateway_connection_config.py tests/test_web_gateway_proxy_auth.py -q` in `abstractflow` -> 21 passed, 2 FastAPI deprecation warnings.
- `node --check bin/cli.js && npm test -- --run src/lib/storage.test.ts src/lib/hosted_gateway_url_guard.test.ts` in `abstractcode/web` -> 2 passed.
- `node --check bin/cli.js && npm test -- --run src/ui/auth_storage.test.ts src/ui/hosted_gateway_url_guard.test.ts` in `abstractobserver` -> 2 passed.
- `node --check web/frontend/bin/cli.js && npm --prefix web/frontend run build` in `abstractflow` -> passed.
- `python -m py_compile web/backend/services/gateway_connection.py web/backend/routes/connection.py` in `abstractflow` -> passed.
- `bash -n scripts/gateway-flow.sh scripts/gateway-flow-local.sh` -> passed.
- Root, Gateway, Flow, Code, and Observer `llms-full` generation -> passed.

Residual risks:
- The explicit retained-runtime purge/transfer workflow was intentionally left
  out of this release-blocker item and is handled by follow-up item `0156`.
- Code Web and Observer still duplicate hosted proxy code. Proposed item `0155`
  records the extraction threshold; current protection is conformance tests.

--- docs/backlog/completed/0156_retained_runtime_admin_lifecycle.md ---
# Completed: Retained runtime admin lifecycle

## Metadata
- Created: 2026-05-30
- Status: Completed
- Completed: 2026-05-30

## ADR status
- Governing ADRs: None
- ADR impact: None

## Context
Gateway user deletion now removes account/token access while retaining the
runtime data directory and reserving the runtime id for the deleted principal.
That prevents accidental cross-user data exposure, but it leaves admins without
an explicit way to intentionally purge retained runtime data or transfer it to
another user.

## Current code reality
- `abstractgateway/src/abstractgateway/users.py` stores live users and
  `runtime_reservations` in the Gateway user registry.
- `GatewayUserRegistry.create_user()` and `update_user()` reject reuse of a
  retained runtime reservation by another principal.
- `GatewayUserRegistry.delete_user()` creates a retained-runtime reservation.
- `abstractgateway/src/abstractgateway/routes/gateway.py` exposes admin user
  CRUD, but no retained-runtime lifecycle routes.
- `abstractgateway/src/abstractgateway/console.py` tells admins that runtime
  data is retained, but has no reservation table, transfer action, or purge
  action.

## Problem
The retained-runtime safety guard is secure but operationally incomplete.
Admins can see that deletion retained data only through the delete confirmation
copy and registry file internals. They cannot intentionally purge stale data or
transfer retained data to a new owner through supported Gateway APIs.

## What we want to do
Add explicit admin-only lifecycle operations for retained runtime reservations:
list, transfer, and purge. Transfer must be intentional and auditable through
the registry. Purge must remove retained runtime files before releasing the
runtime id for reuse.

## Why
This keeps the secure default while giving admins a deliberate recovery path for
real account lifecycle events: employee departure, account rename, mistaken
delete, or data-retention cleanup.

## Requirements
- Admins can list retained runtime reservations.
- Non-admin users cannot list, purge, or transfer retained runtimes.
- Transfer assigns a retained runtime to an existing same-tenant user and
  reserves that user's previous runtime id.
- Purge requires an exact runtime-id confirmation and deletes the retained
  runtime directory before releasing the reservation.
- Runtime file deletion must stay inside the Gateway per-user runtime root.
- The Gateway Console exposes transfer and purge actions with confirmations.
- Tests prove cross-user reuse remains blocked until explicit purge or transfer.

## Suggested implementation
- Extend `GatewayUserRegistry` with reservation listing, lookup, release, and
  transfer helpers.
- Add admin routes under `/api/gateway/admin/runtime-reservations`.
- Add a Gateway service-cache invalidation helper for affected runtime ids.
- Add a console section for retained runtime reservations.
- Add focused Gateway tests around RBAC, transfer, purge, and data deletion.

## Scope
- AbstractGateway registry, admin API, console, tests, and docs.
- Root/Gateway docs and AI-readable docs updates.

## Non-goals
- No runtime explorer UI.
- No partial artifact-level purge.
- No automatic data migration between different runtime layouts.
- No cross-tenant transfer.

## Dependencies and related tasks
- `0145_gateway_admin_console_bootstrap.md`
- `0146_gateway_rbac_scope_policy_matrix.md`
- `0154_multi_user_security_release_blockers.md`

## Expected outcomes
- Deleted users still protect retained data by default.
- Admins have an explicit, confirmed purge path.
- Admins have an explicit, confirmed transfer path.
- Tests and docs describe the lifecycle clearly.

## Validation
- `python -m pytest tests/test_gateway_principal_auth.py -q`
- `python -m pytest tests/test_gateway_console.py -q`
- `python -m py_compile src/abstractgateway/users.py src/abstractgateway/routes/gateway.py src/abstractgateway/service.py src/abstractgateway/console.py`

## Progress checklist
- [x] Add registry lifecycle helpers.
- [x] Add admin routes and service-cache invalidation.
- [x] Add Gateway Console retained-runtime UI.
- [x] Add tests for RBAC, transfer, purge, and reuse.
- [x] Update docs and backlog completion report.

## Guidance for the implementing agent
Preserve the conservative default. A retained runtime id must never become
reusable just because the account row disappeared; reuse must follow an
explicit purge or transfer operation.

## Completion report

Date: 2026-05-30

Summary:
- Added retained-runtime lifecycle helpers to the Gateway user registry.
- Added admin-only list/transfer/purge routes under
  `/api/gateway/admin/runtime-reservations`.
- Added service-cache invalidation for affected runtime ids after transfer or
  purge.
- Added a `purging` reservation state so concurrent transfer attempts cannot
  claim a retained runtime between purge validation and file deletion.
- Added a Gateway Console retained-runtime table with transfer and purge actions
  behind confirmation modals.
- Added tests proving non-admin denial, purge-before-reuse, transfer to an
  existing same-tenant user, previous-runtime reservation, and continued
  cross-user reuse blocking.

Files or symbols touched:
- `abstractgateway/src/abstractgateway/users.py`
- `abstractgateway/src/abstractgateway/routes/gateway.py`
- `abstractgateway/src/abstractgateway/service.py`
- `abstractgateway/src/abstractgateway/console.py`
- `abstractgateway/tests/test_gateway_principal_auth.py`
- `abstractgateway/tests/test_gateway_console.py`
- `abstractgateway/docs/security.md`
- `abstractgateway/docs/configuration.md`
- `abstractgateway/README.md`
- `abstractgateway/llms.txt`
- `docs/guide/gateway-security.md`
- `docs/configuration.md`
- `docs/api.md`
- `llms.txt`

Validation:
- `python -m pytest tests/test_gateway_principal_auth.py tests/test_gateway_console.py -q`
  in `abstractgateway` -> 18 passed.
- `python -m pytest tests/test_gateway_principal_isolation_matrix.py tests/test_gateway_security_middleware_unit.py tests/test_gateway_workflow_catalog_acl.py -q`
  in `abstractgateway` -> 26 passed.
- `python -m py_compile src/abstractgateway/users.py src/abstractgateway/routes/gateway.py src/abstractgateway/service.py src/abstractgateway/console.py`
  in `abstractgateway` -> passed.

Review notes:
- Code quality: purge atomically marks reservations as `purging` before file
  deletion, keeps deletion scoped under
  `<ABSTRACTGATEWAY_DATA_DIR>/users/<tenant>/<runtime>`, and leaves the
  reservation in place if file deletion fails.
- Architecture: Gateway remains the owner of user/runtime assignment and
  retained-runtime lifecycle. Runtime internals are not asked to understand
  Gateway user ownership.
- Naive user: the console now shows retained runtimes directly instead of
  making admins infer them from failed user creation.
- Expert user: the API exposes explicit lifecycle routes and returns data-path
  diagnostics for admin inspection.
- Operations: transfer does not copy data; it changes routing ownership. Purge
  deletes the retained runtime root and then releases the reservation.

Residual risks:
- There is still no rich runtime explorer or artifact-level delete/export for
  retained runtime data. That remains in the proposed runtime explorer track.
- Transfer is same-tenant and existing-user only by design. Account rename flows
  that need create-and-transfer can create the target first, then transfer.

--- docs/backlog/completed/0157_gateway_provider_endpoint_profiles.md ---
# Completed: Gateway provider endpoint profiles

## Metadata
- Created: 2026-05-31
- Status: Completed
- Completed: 2026-05-31

## ADR status
- Governing ADRs: ADR-0033, ADR-0035
- ADR impact: May revise existing ADR after the wider provider-secret vault and workflow export/import rules settle.

## Context
Gateway Console could edit capability defaults for the current principal:
route, provider, model, and optional base URL. That was not enough to configure
an OpenAI-compatible endpoint with the API key needed by hosted, multi-user
Gateway deployments. Users also needed more than one endpoint; defaults and
custom endpoints are separate concerns.

## Problem
Users need to configure OpenAI-compatible and other custom remote endpoints once
in Gateway, then select and reuse them from Flow nodes without hardcoding raw
secrets in workflow JSON, browser storage, run ledgers, or exported bundles.

## Implemented design
Gateway now owns provider endpoint profiles:

- `id`: stable local identifier.
- `display_name`: user-facing label.
- `description`: operator/user explanation of what the endpoint is for.
- `provider_family`: canonical provider implementation such as
  `openai-compatible`, `openai`, `openrouter`, or `anthropic`.
- `base_url`: optional endpoint URL; blank means provider default.
- `api_key`: stored server-side and returned only as non-secret status and
  fingerprint.
- `scope`: `user` or `gateway`; gateway scope requires admin.
- `capabilities`: text, embeddings, image, video, voice, music, or other route
  families.
- `allowed_models`: optional static allowlist; otherwise Gateway asks the
  underlying discovery facade.

Enabled profiles surface through provider discovery as virtual providers such
as `endpoint:office-vllm`. Runtime resolves that virtual id through a
host-provided resolver into the real provider family, base URL, and API key
only for the transient provider call.

## Scope
- AbstractGateway profile store, config API, discovery integration, console UI,
  bundle-host resolver, tests, and docs.
- AbstractRuntime generic optional resolver hook and local multi-client support
  for per-call provider construction with profile base URL/key.

## Non-goals
- No browser-stored provider keys.
- No raw key in workflow JSON, exported bundles, discovery responses, or Runtime
  observability.
- No new provider class per endpoint alias.
- No full workflow export/import endpoint-requirement mapper yet.
- No encrypted-at-rest vault yet; the initial local profile JSON is permissioned
  with `0600`, and the broader vault/audit policy remains under `0147`.

## Completion report

Date: 2026-05-31

Summary:
- Added `ProviderEndpointProfileStore` under AbstractGateway with public
  non-secret profile views and private runtime resolution.
- Added `/api/gateway/config/provider-endpoint-profiles` CRUD routes.
- Added `/api/gateway/config/provider-endpoint-profiles/discover-models` so the
  console can preview models from a draft or saved endpoint profile without
  exposing the raw key.
- Added profile-backed provider discovery and model discovery. Profiles with
  an allowlist return that model list directly; other profiles discover through
  the configured provider family, base URL, and profile key.
- Added Gateway Console controls for profile id, name, description, provider
  family, scope, base URL, API key, capabilities, and a discovered model picker
  where selected models become the optional allowlist.
- Added virtual provider labels to console provider selectors so profiles are
  selectable like normal providers while preserving the `endpoint:*` id.
- Added Runtime LLM-call profile resolution, provider/base URL/key injection,
  and redaction from persisted observability traces.
- Added local `MultiLocalAbstractCoreLLMClient` support for per-call provider
  construction from profile `base_url` and `api_key`.
- Added bundle-host resolver wiring so Gateway-hosted runs can resolve virtual
  providers without Runtime importing Gateway.

Validation:
- `python -m pytest abstractgateway/tests/test_gateway_provider_endpoint_profiles.py abstractgateway/tests/test_gateway_discovery_endpoints.py abstractgateway/tests/test_gateway_console.py abstractruntime/tests/test_provider_endpoint_profile_resolution.py -q` -> 15 passed.
- `python -m compileall -q abstractgateway/src/abstractgateway abstractruntime/src/abstractruntime` -> passed.

Review notes:
- Code quality: raw keys are write-only through API/UI responses and redacted in
  Runtime observability. Scope changes upsert before deleting the old scoped row
  to avoid losing a profile on validation failure.
- Architecture: Gateway owns endpoint profiles and credentials; Runtime exposes
  only a generic optional resolver hook and does not import Gateway.
- Naive user: Gateway Console presents the endpoint as a named reusable object
  with description, a discover-models action, and a visible virtual provider id
  to copy/select.
- Expert user: profiles support static model allowlists, dynamic discovery,
  provider-family routing, gateway/user scope, and direct use in capability
  defaults or Flow node provider pins.
- Operations: initial storage is local JSON under the relevant Gateway data
  plane with `0600` permissions. A stronger vault/encryption/audit layer remains
  a follow-up under `0147`.

Residual risks:
- Workflow export/import still needs a portable endpoint requirement model so a
  shared workflow can ask the importing user to map or create an endpoint
  profile without exporting secrets.
- Profile resolution is implemented for text provider/model discovery and
  Runtime `LLM_CALL`/Agent execution, including output-selector calls that flow
  through that effect. Direct Gateway embedding/media catalog routes can adopt
  the same profile projection when those UI paths need virtual endpoint
  selection outside `LLM_CALL`.
- Gateway-scoped profile policy is currently admin-only create/update/delete and
  inherited discovery/use. Finer tenant ACLs can be added when endpoint sharing
  policy becomes stricter.
- Secrets are permissioned local JSON, not encrypted at rest. That is acceptable
  for the initial Gateway-local control-plane path but should not be treated as
  the final hosted vault design.

--- docs/backlog/planned/0164_gateway_docker_ghcr_deployment_track.md ---
# 0164 Gateway Docker GHCR Deployment Track

Status: In progress
Owner: AbstractGateway
Created: 2026-05-31

## Problem

ADR-0033 and ADR-0034 define Docker as a Gateway deployment artifact with two
strategies: a lightweight server image and an explicit NVIDIA image. The actual
Gateway Docker surface lagged behind that architecture:

- public examples used `abstractgateway-server` image names instead of the
  desired `ghcr.io/lpalbou/abstractgateway`;
- container defaults still centered on the legacy shared Gateway token instead
  of Gateway user auth and browser sessions;
- simple `docker run -v "$PWD/runtime:/data"` examples did not have a clean
  first-admin bootstrap story;
- release workflows built images from the checkout even after the PyPI package
  was published, unlike the AbstractCore image pattern.

## Decision

Use GHCR as the primary registry now. DockerHub remains optional until an org,
secrets, and support process exist.

Publish:

- `ghcr.io/lpalbou/abstractgateway:<version>` and `:latest` for the light image;
- `ghcr.io/lpalbou/abstractgateway:<version>-gpu` and `:gpu-latest` for the
  NVIDIA/GPU image;
- legacy `abstractgateway-server` and `abstractgateway-server-nvidia` aliases
  during a transition period.

Container runtime defaults:

- `ABSTRACTGATEWAY_DATA_DIR=/data`
- `ABSTRACTGATEWAY_USER_AUTH=1`
- bundled `basic-agent` remains available by default; `/data/flows` is only
  used when Compose or the operator explicitly sets `ABSTRACTGATEWAY_FLOWS_DIR`.
- the entrypoint ensures `default/admin` exists when user auth is active and
  writes the first-login token to `<DATA_DIR>/auth/bootstrap-admin-token`.

Release workflow defaults:

- wait for the matching PyPI package;
- install `abstractgateway==<version>` from PyPI for the light image;
- install `abstractgateway[gpu]==<version>` from PyPI for the GPU image.

## Target UX

The supported one-command server path should work as:

```bash
docker run \
  -p 8080:8080 \
  -v "$PWD/runtime:/data" \
  -e ABSTRACTGATEWAY_DATA_DIR=/data \
  -e ABSTRACTGATEWAY_USER_AUTH=1 \
  ghcr.io/lpalbou/abstractgateway:latest
```

Then:

```bash
cat runtime/auth/bootstrap-admin-token
open http://localhost:8080/console
```

The admin signs in as `admin`, creates named users, rotates tokens, and configures
Gateway defaults/provider endpoint profiles from the console.

## Scope

- Dockerfiles and Compose profiles in `abstractgateway/docker/abstractgateway-server/`
- Gateway release and manual GHCR workflows
- `abstractgateway-config bootstrap-admin`
- Gateway Docker/deployment docs and coredoc indexes
- Root deployment docs that explain when to use Python profiles vs Gateway
  containers

## Non-Goals

- DockerHub publishing before registry ownership and secrets are configured
- Apple/MLX Docker image
- CPU-local inference profile
- Kubernetes Helm chart or Terraform module
- changing Core/Runtime package boundaries

## Validation

Required before completion:

- targeted Gateway config/Docker profile tests pass;
- shell entrypoint syntax check passes;
- Docker build is attempted for the light image when Docker is available;
- docs and `llms-full.txt` are regenerated.

Current evidence:

- 2026-05-31: local light image build passed with
  `docker build --build-arg ABSTRACTGATEWAY_INSTALL_MODE=local ... -t abstractgateway:test-light-local`.
- 2026-05-31: container smoke passed with a temporary `/data` mount:
  `/api/health` became ready, `/data/auth/bootstrap-admin-token` was created,
  token format was `agw_*`, and authenticated `/api/gateway/me` returned
  `default/admin` with `user_auth_enabled=true`.

## Follow-Ups

- Add a real CUDA host smoke gate before calling the GPU image production-ready.
- Consider a tiny `abstractframework doctor --container` probe that verifies
  Gateway health, user auth, admin token presence, and mounted data writability.
- Revisit DockerHub only if users need DockerHub specifically rather than GHCR.

--- docs/backlog/proposed/installers/README.md ---
# Installer And Setup Track

This proposed track collects follow-up work for turning the current AbstractFramework install
experience into a production-grade setup system for both technical and non-technical users.

The items are grouped because they share the same boundary: `abstractframework` should remain the
Python release profile and install contract, while native installer applications should consume
that contract rather than duplicating pins by hand.

Recommended reading order:

1. `../../completed/0158_installer_repository_extraction.md`
2. `../../completed/0159_generated_install_manifest_contract.md`
3. `../../completed/0160_framework_doctor_and_launch_cli.md`
4. `../../completed/0161_three_path_public_install_guide.md`
5. `0162_signed_installer_ci_and_distribution.md`
6. `0163_cpu_local_inference_install_profile.md`

Relevant docs and decisions:

- `docs/installers/`
- `docs/adr/0033-install-profiles-config-entrypoints-and-server-boundaries.md`
- `docs/adr/0034-framework-release-sequence-and-gates.md`
- `docs/adr/reorg/2026-02-21_installer_manager_strategy.md`
- `docs/adr/reorg/2026-02-21_macos_installer_manager.md`
- `https://github.com/lpalbou/AbstractInstallers`

Non-goals for this proposed track:

- Do not make the root `abstractframework` wheel ship native installer source or binaries.
- Do not make installers own package version pins independently from the root release profile.
- Do not bypass OS signing, notarization, or checksum requirements for production artifacts.
- Do not add a CPU local profile until each package's CPU backend story is audited.

--- docs/backlog/completed/0158_installer_repository_extraction.md ---
# Proposed: Installer Repository Extraction

## Metadata
- Created: 2026-05-31
- Status: Completed
- Completed: 2026-05-31

## ADR status
- Governing ADRs: `docs/adr/reorg/2026-02-21_installer_manager_strategy.md`, `docs/adr/reorg/2026-02-21_macos_installer_manager.md`
- ADR impact: May revise existing ADR

## Context
The root `AbstractFramework` repository currently contains `abstractinstallers/` as a sandbox for
installer experiments. The root PyPI package does not include these files in the wheel or sdist,
but the source tree now mixes Python release-profile concerns with Rust/Tauri installer product
code.

## Current code reality
- `AbstractInstallers` now exists at `https://github.com/lpalbou/AbstractInstallers`.
- The root `AbstractFramework` repo no longer tracks `abstractinstallers/`.
- `pyproject.toml` still only packages `abstractframework*`.
- `docs/installers/` already documents an installer-manager strategy.

## Problem or opportunity
Installer applications have a different lifecycle from the Python meta-package: native app
toolchains, OS signing, notarization, updater artifacts, platform CI, and support logs. Keeping
that source in the root repo risks confusing release ownership and encouraging hand-maintained
manifest drift.

## Proposed direction
Create a separate GitHub repository named `AbstractInstallers` and move production installer app
source there once the manifest contract is ready. Keep root `AbstractFramework` as the versioned
Python install profile, docs hub, and manifest source of truth.

Use `AbstractInstallers` rather than `AbstractInstaller`, `AbstractInstall`, or `AbstractSetup`:

- `AbstractInstallers` matches the existing directory name and supports multiple artifacts
  (`macOS`, `Windows`, `Linux`, future helper apps).
- Plural naming is clearer for a repo that will hold more than one installer target and shared
  installer infrastructure.
- `AbstractInstaller` sounds like a single application rather than a repo owning several packages.
- `AbstractInstall` reads like a command or package action, not a product/repo.
- `AbstractSetup` is friendlier but less precise; it could mean first-run configuration rather
  than installation, updates, repair, signing, and distribution.

## Why it might matter
Separating the installer repo keeps the framework package clean and makes native release
engineering visible. It also lets installer CI evolve without blocking Python package releases.

## Promotion criteria
- The generated install manifest contract exists or has an accepted schema.
- The macOS prototype needs production signing/distribution work.
- Installer code changes start dominating root framework diffs or release planning.

## Validation ideas
- Verify root `abstractframework` wheel/sdist still exclude installer source.
- Verify `AbstractInstallers` can consume a released manifest from `AbstractFramework`.
- Build the macOS prototype from the new repo without path assumptions back into the root repo.

## Non-goals
- This proposal does not authorize deleting installer docs from `AbstractFramework`.
- This proposal does not require moving historical backlog or ADR records.
- This proposal does not decide the final UI toolkit beyond preserving the current Tauri prototype
  as the first migration candidate.

## Guidance for future agents
Treat `AbstractFramework` as the release-profile authority and `AbstractInstallers` as a consumer.
If a future design requires installers to mutate framework release pins directly, re-check the
architecture before implementing it.

## Completion report
- Moved the tracked installer prototype source into the standalone `AbstractInstallers` repository.
- Added repository-level `.gitignore`, `LICENSE`, and README updates there.
- Pushed `AbstractInstallers` commit `3618978` to `https://github.com/lpalbou/AbstractInstallers`.
- Removed tracked `abstractinstallers/` files and leftover local build artifacts from the root
  `AbstractFramework` checkout.
- Updated root docs to point users and future installer work at the standalone repository.

--- docs/backlog/completed/0159_generated_install_manifest_contract.md ---
# Proposed: Generated Install Manifest Contract

## Metadata
- Created: 2026-05-31
- Status: Completed
- Completed: 2026-05-31

## ADR status
- Governing ADRs: `docs/adr/0033-install-profiles-config-entrypoints-and-server-boundaries.md`, `docs/adr/0034-framework-release-sequence-and-gates.md`
- ADR impact: May revise existing ADR

## Context
Installers need a machine-readable description of AbstractFramework release profiles, components,
versions, package extras, prerequisites, and launch commands. Today the macOS prototype uses a
local `manifest.local.json`, and release work had to patch that file manually.

## Current code reality
- Root `pyproject.toml` pins the release profile. The Light profile is the base install;
  `apple` and `gpu` are the hardware-local extras.
- `abstractframework.RELEASE_VERSIONS` duplicates the release matrix for runtime inspection.
- `abstractframework.install_manifest.build_install_manifest()` generates the installer-facing
  manifest from root constants.
- `docs/installers/install-manifest.json` is checked in and tested against the generator.
- `docs/installers/install-manifest.schema.json` defines the current schema shape.
- `abstractframework manifest --check docs/installers/install-manifest.json` validates drift.

## Problem or opportunity
Hand-maintained installer manifests drift. Drift is risky because installers are aimed at users
least able to diagnose version/profile mistakes.

## Proposed direction
Create a generated, versioned install manifest contract owned by `AbstractFramework`. The manifest
should be produced from root pins and profile definitions, then consumed by installer apps.

The contract should include:

- framework version and manifest schema version;
- package pins and package registry type;
- install profiles: Light, Apple, GPU;
- per-profile pip requirements and npm app requirements where applicable;
- Python/Node/platform prerequisites;
- post-install commands such as doctor/config/launch;
- checksum/signature fields for native installer artifacts when applicable;
- compatibility metadata for minimum installer-manager version.

The first implementation can generate a checked-in JSON file under `docs/installers/` or
`abstractframework/assets/`, then later publish it as a GitHub release asset.

## Why it might matter
A generated contract makes the installer trustworthy and reduces duplicated release logic. It also
lets a GUI installer, CLI doctor, docs, and future updater reason about the same release profile.

## Promotion criteria
- Installer app source moves to a separate repository.
- Any release requires editing both root pins and installer manifests.
- Install profile tests need to cover more than the current local macOS prototype.

## Validation ideas
- Generate the manifest from `pyproject.toml` and `abstractframework.RELEASE_VERSIONS`.
- Validate it against a JSON schema.
- Assert generated manifest package pins match root pins in tests.
- Run a dry-run installer plan from the generated manifest for Light, Apple, and GPU profiles.

## Non-goals
- Do not make installers infer package versions from PyPI "latest".
- Do not make the manifest execute arbitrary scripts.
- Do not embed API keys, user secrets, or machine-local paths.

## Guidance for future agents
The manifest should be boring and explicit. Prefer a small schema that covers real installer needs
over a flexible plugin system that can become a hidden package manager.

## Completion report
- Added `abstractframework/install_manifest.py`.
- Added `docs/installers/install-manifest.json` and
  `docs/installers/install-manifest.schema.json`.
- Added `abstractframework manifest`, including `--write` and `--check`.
- Updated install-profile tests so the checked-in manifest must match the generator and root pins.
- Left signed native artifact metadata as explicit future work in item 0162.

--- docs/backlog/completed/0160_framework_doctor_and_launch_cli.md ---
# Proposed: Framework Doctor And Launch CLI

## Metadata
- Created: 2026-05-31
- Status: Completed
- Completed: 2026-05-31

## ADR status
- Governing ADRs: `docs/adr/0033-install-profiles-config-entrypoints-and-server-boundaries.md`
- ADR impact: None

## Context
The root `abstractframework` package currently exposes helper functions for release profile and
installed package status, but no CLI. Installers and technical users both need the same readiness
checks and launch guidance.

## Current code reality
- `abstractframework/__init__.py` defines release pins, distribution mappings,
  `get_release_profile()`, `get_installed_packages()`, and `print_status()`.
- `pyproject.toml` exposes the `abstractframework` console script.
- `abstractframework.cli` implements `doctor` and `manifest`.
- Existing app launch behavior lives in package-specific commands and scripts, such as Gateway,
  Flow, Code, Observer, and local launcher scripts.
- `docs/install.md`, `docs/api.md`, and README references document `abstractframework doctor`.

## Problem or opportunity
Without a shared doctor/launch surface, GUI installers tend to reimplement checks and technical
users get inconsistent troubleshooting paths.

## Proposed direction
Add a small root CLI with two likely commands:

- `abstractframework doctor`: inspect installed versions, profile consistency, Python/Node
  prerequisites, Gateway/Flow readiness, local hardware availability, and common config problems.
- `abstractframework launch`: start or delegate to the default local Gateway/Flow experience once
  the install is healthy, using the same checks as the installer.

Start with `doctor`. Add `launch` only if it can delegate cleanly to existing package CLIs without
becoming another process manager.

## Why it might matter
The CLI becomes the shared support surface for users, installers, docs, and future updater flows.
It helps technical users self-diagnose and lets non-technical installer UI show reliable, familiar
failure messages.

## Promotion criteria
- Installer app needs reusable readiness checks.
- Support reports show install failures that could be diagnosed by a root command.
- The root package remains a meta-package but needs a small operational helper surface.

## Validation ideas
- Unit-test version/pin checks against `RELEASE_VERSIONS`.
- Run `abstractframework doctor` in a base install and in missing-dependency simulations.
- Verify the command does not import heavy local inference stacks unless explicitly checking them.
- If `launch` is added, verify it starts Gateway/Flow without leaking admin credentials to browser
  sessions.

## Non-goals
- Do not duplicate Gateway admin/user management.
- Do not make root `abstractframework` a long-running service.
- Do not replace package-specific CLIs.

## Guidance for future agents
Keep the CLI thin. It should orchestrate checks and delegate. If it starts to own app logic, move
that logic back to the relevant package or installer repo.

## Completion report
- Added `abstractframework doctor` with package pin checks, Python version checks, Node/npm probes,
  and Apple/GPU hardware indicators.
- Added JSON output support through `abstractframework doctor --json`.
- Added tests for the doctor report and manifest check command.
- Did not add `abstractframework launch`; the design still says launch should only be added if it
  can delegate cleanly without becoming a new process manager.

--- docs/backlog/completed/0161_three_path_public_install_guide.md ---
# Proposed: Three-Path Public Install Guide

## Metadata
- Created: 2026-05-31
- Status: Completed
- Completed: 2026-05-31

## ADR status
- Governing ADRs: `docs/adr/0033-install-profiles-config-entrypoints-and-server-boundaries.md`
- ADR impact: None

## Context
The release profile now has three clear installation modes: remote-first Light, Apple local, and
GPU local. The docs mention these commands, but users need a more deliberate decision guide.

## Current code reality
- `README.md` documents `pip install abstractframework`,
  `pip install "abstractframework[apple]"`, and `pip install "abstractframework[gpu]"`.
- `docs/install.md` is the concise user-facing install chooser.
- `docs/api.md`, `docs/faq.md`, `docs/getting-started.md`, `docs/README.md`, and README link to
  the install chooser.
- `docs/guide/apple-local-gateway-flow.md` covers a specific Apple local Gateway/Flow path.

## Problem or opportunity
Install decisions are high-friction because "full functionality" and "local inference" are easy
to confuse. Users need to understand that Light still supports multimodal and embeddings through
remote endpoints, while Apple/GPU add local inferencer stacks.

## Proposed direction
Create a public install guide with three primary paths:

- Light: remote-first, lowest friction, no local MLX/CUDA/Diffusers stacks.
- Apple: local Apple Silicon engines where supported, with larger downloads and macOS-specific
  prerequisites.
- GPU: local GPU engines where supported, with CUDA/ROCm/system-driver expectations.

The guide should include:

- who should choose each path;
- exact commands using `pipx`, `uv`, and venv/pip options where appropriate;
- expected disk/download implications;
- first-run config steps;
- how Gateway/Flow should be started;
- how to run `abstractframework doctor` once it exists;
- when to use the GUI installer instead.

## Why it might matter
This is the main bridge between technical and non-technical installs. It also reduces repeat
confusion about Light installs accidentally installing local inference stacks.

## Promotion criteria
- Root `abstractframework` release profile is stable after `0.1.6`.
- Generated manifest or doctor CLI work starts.
- Installer repo extraction needs public docs that explain how CLI and GUI paths relate.

## Validation ideas
- Dry-run each documented pip command.
- Verify Light install does not pull MLX/CUDA/local inference dependencies.
- Verify Apple/GPU docs align with actual extras in root and package manifests.
- Cross-link from `README.md`, `docs/getting-started.md`, `docs/installers/README.md`, and
  `llms-full.txt`.

## Non-goals
- Do not document unsupported one-off package-manager flows as primary paths.
- Do not claim native signed installers exist before release artifacts are available.
- Do not bury warnings about large local model downloads.

## Guidance for future agents
Write this for a user making a decision, not for maintainers. The guide should be short enough to
read before installing but precise enough to prevent the wrong profile choice.

## Completion report
- Added `docs/install.md` with Light / Apple / GPU profile guidance, venv/uv setup, first checks,
  Gateway/Flow notes, and the generated manifest command.
- Updated README, docs index, getting-started, API, FAQ, installer docs, `llms.txt`, and
  `llms-full.txt`.
- Kept CPU local inference out of the public install chooser pending item 0163.

--- docs/backlog/completed/0171_gateway_console_sandbox_client_grounding_and_media.md ---
# Planned: Gateway Console sandbox client grounding and media attachments

## Metadata
- Created: 2026-06-01
- Status: Completed
- Completed: 2026-06-01

## ADR status
- Governing ADRs: ADR-0023, ADR-0031, ADR-0035
- ADR impact: None

## Context
The AbstractGateway Console Sandbox lets a signed-in Gateway user test configured providers and
capability defaults directly from `/console`. The chat UI supports browser file upload and sends
requests to `/api/gateway/sandbox/generate`, which calls AbstractRuntime's AbstractCore-backed LLM
client.

The sandbox is a browser-facing UX, so user-facing grounding such as current local time, timezone,
and country should describe the browser user's context. It must not become an authorization,
routing, or audit trust signal.

## Current code reality
- Before completion, `abstractgateway/src/abstractgateway/console.py` uploaded files to
  `/api/gateway/attachments/upload`, displays attachment chips, and sends only provider/model,
  prompt, messages, system prompt, and artifact refs to `/api/gateway/sandbox/generate`.
- Before completion, `abstractgateway/src/abstractgateway/routes/gateway.py` modeled sandbox requests with
  `_GatewaySandboxGenerateRequest(extra="forbid")`; it currently has no client context field.
- Before completion, `gateway_sandbox_generate` created `LocalAbstractCoreLLMClient` server-side and did not pass
  browser timezone, browser local time, locale, or country into Runtime.
- Before completion, `abstractruntime/src/abstractruntime/integrations/abstractcore/llm_client.py` derived runtime
  grounding from the server process timezone, locale, environment, and current clock. That is
  correct for server-only callers, but wrong for browser-facing sandbox prompt grounding.
- Artifact-backed browser uploads resolve through Runtime into file paths. File-backed image media
  must reach AbstractCore providers as native multimodal content, not as a text-only prompt.

## Problem
The sandbox can ground chat turns with server metadata instead of browser metadata. In remote or
Docker deployments this can show the wrong country/time context to the model.

Image attachments can also be visible in the browser transcript while failing to reach the
OpenAI-compatible provider as native image content, causing the model to answer as if no image was
attached.

## What we want to do
Make Console Sandbox requests carry explicit browser-local grounding context and ensure uploaded
image artifacts become native multimodal `image_url` blocks when routed through Gateway, Runtime,
and AbstractCore.

## Why
Users need the sandbox to behave like the app they are testing: the model should see the user's
current local context and any attached image, not the server's locale and a text-only placeholder.

## Requirements
- Browser context must include only bounded, allowlisted fields such as timezone, local datetime,
  locale, and optional locale-derived country.
- Runtime may use browser context for prompt grounding only; it must keep server-derived context as
  provenance and never use browser context for auth, routing, or permission checks.
- Media turns should not inject runtime metadata into the visible multimodal prompt.
- Browser-uploaded image artifacts must be resolved with content type and provider-ready image
  encoding.
- Generic OpenAI-compatible endpoint profiles and LM Studio-style providers must both be covered by
  tests.

## Suggested implementation
- Add a `client_context` field to the sandbox request schema.
- Collect browser timezone/local datetime/locale/country in Console Sandbox JavaScript.
- Pass sanitized client context in `params.trace_metadata.client_context`.
- Teach Runtime grounding to prefer allowlisted browser context for prompt grounding while keeping
  server context in metadata.
- Add tests at Core provider, Runtime artifact-resolution, and Gateway route/UI levels.

## Scope
- AbstractGateway Console Sandbox request/route behavior.
- AbstractRuntime grounding metadata merge behavior.
- AbstractCore media processing for `file_path` dicts produced by artifact resolution.
- Focused docs for current behavior where user-facing documentation is needed.

## Non-goals
- Do not use browser context for authorization, runtime routing, or provider credential selection.
- Do not redesign Gateway sessions or user profiles.
- Do not add broad geolocation; country remains locale/timezone-derived unless user profile work is
  implemented separately.

## Dependencies and related tasks
- ADR-0023 file attachment path resolution and authorization.
- ADR-0031 workflow routing overrides.
- ADR-0035 capability routing defaults.
- Proposed item 0144 for richer user profile grounding.

## Expected outcomes
- Console Sandbox image+text requests reach OpenAI-compatible VLMs as native multimodal requests.
- Browser-local timezone/current time/country are reflected in prompt grounding metadata when
  provided by the browser.
- Runtime metadata records provenance so maintainers can tell browser-derived grounding from
  server-derived fallback grounding.

## Validation
- Focused Python tests for Gateway sandbox request payloads, Runtime grounding, artifact media
  resolution, and AbstractCore OpenAI-compatible media formatting.
- Manual AbstractCore-only payload capture that proves `Qwen3.5-9B` image requests produce an
  `image_url` content block.
- Browser/manual verification should upload an image in `/console` Sandbox and receive an
  image-aware response from a configured VLM.

## Progress checklist
- [x] Add browser client context collection and Gateway request validation.
- [x] Add Runtime prompt-grounding merge with browser-over-server precedence.
- [x] Harden artifact-backed image media formatting through AbstractCore.
- [x] Add and run focused regression tests.
- [x] Update user-facing docs if behavior or troubleshooting guidance changed.

## Guidance for the implementing agent
Keep the fix narrow and evidence-driven. If an attached image still fails, inspect the final
provider request payload before changing UI copy or provider defaults.

## Completion report

Date: 2026-06-01

Summary:
- Gateway Console Sandbox now sends bounded browser-local grounding context with text sandbox
  requests: local datetime, UTC datetime, timezone, timezone offset, locale, and locale-derived
  country fallback.
- Gateway sanitizes that browser context and forwards it only as
  `params.trace_metadata.client_context`.
- Runtime prefers browser timezone/local datetime for prompt grounding while keeping server-derived
  context in result metadata as provenance. Browser locale no longer wins over timezone for country;
  timezone mapping wins, with locale as fallback.
- Runtime still skips injecting runtime metadata into media turns, so attached images are not
  preceded by grounding XML in the provider-visible user text.
- Artifact-backed image uploads now keep MIME/modality metadata through Gateway and Runtime and are
  materialized into typed temp files before AbstractCore provider formatting.
- AbstractCore provider media processing now treats `file_path` media dicts as files through
  `AutoMediaHandler`, so generic OpenAI-compatible and LM Studio providers receive native
  `image_url` content for vision-capable models.

Files or symbols touched:
- `abstractgateway/src/abstractgateway/console.py`: `sandboxClientContext()` and Sandbox request
  payload.
- `abstractgateway/src/abstractgateway/routes/gateway.py`:
  `_GatewaySandboxGenerateRequest.client_context`, `_sandbox_client_context()`, and
  `gateway_sandbox_generate()`.
- `abstractruntime/src/abstractruntime/integrations/abstractcore/llm_client.py`:
  client grounding merge and artifact media resolution.
- `abstractcore/abstractcore/providers/base.py`: file-backed media dict handling.
- `abstractcore/abstractcore/media/types.py`: `content_type` alias handling.
- Focused tests in `abstractgateway/tests`, `abstractruntime/tests`, and `abstractcore/tests`.
- Docs in `abstractgateway/docs/configuration.md` and `docs/guide/gateway-security.md`.

Validation:
- `python -m py_compile abstractruntime/src/abstractruntime/integrations/abstractcore/llm_client.py abstractgateway/src/abstractgateway/routes/gateway.py abstractgateway/src/abstractgateway/console.py`
- `PYTHONPATH=abstractgateway/src:abstractruntime/src:abstractcore python -m pytest abstractcore/tests/providers/test_lmstudio_requires_user_message_unit.py abstractruntime/tests/test_multimodal_abstractcore_integration.py::test_resolve_media_artifacts_uses_typed_temp_path_for_blob_store abstractgateway/tests/test_gateway_console.py abstractgateway/tests/test_gateway_provider_endpoint_profiles.py::test_gateway_sandbox_text_generation_uses_server_side_endpoint_credentials abstractruntime/tests/test_llm_client_system_context.py abstractruntime/tests/test_media_artifact_resolution.py abstractruntime/tests/test_llm_client_media_artifacts.py abstractcore/tests/media_handling/test_media_content_dict_roundtrip.py -q`
  passed with `30 passed, 6 warnings`.
- `PYTHONPATH=abstractgateway/src:abstractruntime/src:abstractcore python -m pytest abstractgateway/tests/test_gateway_provider_endpoint_profiles.py abstractgateway/tests/test_gateway_console.py abstractruntime/tests/test_llm_client_system_context.py abstractcore/tests/providers/test_lmstudio_requires_user_message_unit.py -q`
  passed with `34 passed, 6 warnings`.

Behavior changes:
- Console Sandbox text chat with an uploaded image now sends the upload as a media artifact and the
  resolved AbstractCore provider request includes an OpenAI `image_url` content block for
  vision-capable OpenAI-compatible models such as `qwen/qwen3.5-9b`.
- Browser-local time and timezone are used for user-facing prompt grounding. Server context remains
  recorded but is not the browser-facing default when browser context is present.
- Browser-provided grounding is explicitly untrusted and is not used for authorization, routing,
  provider credential selection, or audit authority.

Residual risks:
- Browser timezone is still not a precise geolocation signal. This is acceptable for prompt
  grounding, but richer user profile/location behavior remains covered by proposed item 0144.
- Manual verification against a live OVH endpoint should still be performed after restarting the
  local Gateway so the browser serves the updated Console JavaScript.

Priority impact:
- This removes the immediate Sandbox image-analysis blocker and the browser/server grounding drift.
- No ADR update was needed because the existing Gateway security and capability-default boundaries
  already cover the behavior; this item clarifies and validates the implementation.

--- docs/backlog/proposed/installers/0162_signed_installer_ci_and_distribution.md ---
# Proposed: Signed Installer CI And Distribution

## Metadata
- Created: 2026-05-31
- Status: Proposed
- Completed: N/A

## ADR status
- Governing ADRs: `docs/adr/reorg/2026-02-21_installer_manager_strategy.md`, `docs/adr/reorg/2026-02-21_macos_installer_manager.md`
- ADR impact: May revise existing ADR

## Context
The macOS installer prototype can build locally, but production installers need OS-specific
release pipelines, signing, notarization, checksum publication, rollback/repair logs, and
secret-safe automation.

## Current code reality
- `abstractinstallers/abstractframework-macos/BUILDING.md` describes local Tauri build steps.
- `docs/installers/security-and-os-blocks.md` describes signing and OS trust requirements.
- `docs/installers/release-and-manifest.md` describes a future release and manifest process.
- No production installer CI exists in a dedicated installer repository.

## Problem or opportunity
Unsigned GUI installers create friction and security warnings for exactly the users the installer
is meant to help. A production installer must feel trustworthy, recover cleanly, and avoid
embedding secrets in build artifacts.

## Proposed direction
After the installer repo and manifest contract exist, add signed installer CI:

- macOS signed/notarized `.dmg` or `.pkg`;
- Windows signed `.msi` or `.exe`;
- Linux AppImage first, with `.deb` later if demand justifies it;
- release manifest update with artifact URLs, checksums, signatures, minimum app versions, and
  rollback metadata;
- install logs that users can export for support;
- no hardcoded API keys or provider secrets in installer artifacts.

## Why it might matter
This is the difference between a prototype and a real non-technical install path. OS-native trust,
rollback, and support logs reduce failed installs and support load.

## Promotion criteria
- `AbstractInstallers` exists as a dedicated repo.
- macOS prototype is migrated or rebuilt there.
- Generated manifest contract exists and can reference native installer artifacts.
- Signing identities and GitHub environment secrets are available.

## Validation ideas
- CI builds signed artifacts on release tags.
- macOS notarization gate passes.
- Checksums in the manifest match uploaded artifacts.
- A clean machine can install, run doctor checks, launch Gateway/Flow, and uninstall or repair.
- Secrets scan confirms no provider/API keys are bundled.

## Non-goals
- Do not implement auto-update before basic install/repair is solid.
- Do not ship unsigned artifacts as "production".
- Do not use installer CI to publish Python packages; Python package release remains in package
  repos and the root `abstractframework` profile.

## Guidance for future agents
Treat signing and rollback as release gates, not polish. A non-technical installer that trips OS
security warnings is not production-ready.

--- docs/backlog/proposed/installers/0163_cpu_local_inference_install_profile.md ---
# Proposed: CPU Local Inference Install Profile

## Metadata
- Created: 2026-05-31
- Status: Proposed
- Completed: N/A

## ADR status
- Governing ADRs: `docs/adr/0033-install-profiles-config-entrypoints-and-server-boundaries.md`
- ADR impact: May revise existing ADR

## Context
The current root install profiles are Light, Apple, and GPU. Light is remote-first and avoids local
inference stacks. Apple and GPU add hardware-local inferencer stacks. Modern desktop CPUs can run
some models locally, so a fourth CPU profile may be useful for users without Apple Silicon or a
discrete GPU.

## Current code reality
- Root `pyproject.toml` exposes only `apple` and `gpu` extras.
- `docs/install.md` documents Light, Apple, and GPU only.
- AbstractCore can use endpoint servers that may themselves run on CPU, such as Ollama,
  llama.cpp, LM Studio, or LocalAI, through the Light profile.
- Local in-process CPU support is not clearly audited across `abstractvoice`, `abstractvision`,
  `abstractmusic`, `abstractcore`, `abstractruntime`, `abstractagent`, and `abstractgateway`.

## Problem or opportunity
Some users may want local inference on CPU-only machines. This can be especially plausible for
text, embeddings, small STT/TTS, or small llama.cpp/Ollama workflows. However, CPU-local image,
video, and music generation can be extremely slow, dependency-heavy, or unsupported depending on
backend.

## Proposed direction
Evaluate a fourth `cpu` install profile before adding it:

- `abstractframework[cpu]` would mean local CPU inferencers, not endpoint-only Light.
- Each package must define what CPU local inference means and which backends are included.
- Text/embedding CPU paths should be considered first.
- Vision, video, music, and voice CPU paths require explicit performance and dependency warnings.
- Docs must keep Light distinct from CPU: Light uses endpoints; CPU installs local inferencer
  dependencies.

## Why it might matter
A CPU profile could help users on strong desktop CPUs, servers without GPUs, or machines where
GPU/Apple stacks are unavailable. It could also provide a more predictable "offline but slow"
path for basic text and embeddings.

## Promotion criteria
- Package-by-package audit identifies safe CPU backend choices.
- Dry-run installs show no accidental CUDA/MLX dependency pull.
- At least one practical text/embedding workflow works locally on CPU.
- Docs can set honest expectations for latency, model size, and unsupported modalities.

## Validation ideas
- Add dependency tests asserting `abstractframework[cpu]` does not pull CUDA/MLX packages.
- Run a CPU-only smoke workflow for text generation and embeddings.
- Run or explicitly skip voice, vision, video, and music CPU smoke tests with documented reasons.
- Verify `abstractframework doctor` can detect and explain the CPU profile separately from Light.

## Non-goals
- Do not implement CPU profile as an alias for Light.
- Do not promise fast image/video/music generation on CPU.
- Do not add CPU dependencies that make the default Light install heavy.

## Guidance for future agents
Start with evidence. CPU-local can be valuable, but only if the user can predict what works and
what will be painfully slow.

--- docs/backlog/proposed/gateway-control-plane/README.md ---
# Gateway control plane proposals

## Status
Proposed.

## Purpose
These proposals preserve package-boundary options that are plausible but not
yet implementation commitments. The planned Gateway control-plane track should
prove the API and UX needs first.

## Items
- `0151_runtime_explorer_contract.md`: define whether runtime browsing becomes
  Gateway Console tabs, Observer pages, or a future `abstractexplorer`.
- `0152_abstractmanager_package_extraction.md`: decide whether the Gateway
  admin/config UI should later become a separate `abstractmanager` package.
- `0155_hosted_proxy_shared_helper_extraction.md`: decide when repeated hosted
  browser-session proxy logic in Node apps should become a shared helper rather
  than package-local code plus conformance tests.

## Reading order
Read the planned track first:
`../../planned/gateway-control-plane/README.md`.

## Non-goals
These proposals do not authorize new packages yet. They record names and
boundaries to revisit after Gateway Console v0, RBAC hardening, and config APIs
land.

--- docs/backlog/proposed/gateway-control-plane/0151_runtime_explorer_contract.md ---
# Proposed: Runtime Explorer contract

## Metadata
- Created: 2026-05-30
- Status: Proposed
- Completed: N/A

## ADR status
- Governing ADRs: ADR-0018, ADR-0021
- ADR impact: May revise existing ADR

## Context
Users need to understand their runtime: runs, ledgers, artifacts, memories,
workflows, workspaces, quotas, storage, and recent activity. Gateway can expose
the data, Observer already visualizes runs, and a future `abstractexplorer`
could become a dedicated runtime browser.

## Current code reality
- Gateway has many endpoints for runs, artifacts, workflows, prompt cache,
  workspaces, schedules, and maintenance.
- Observer already handles run/ledger observability and some broad operational
  surfaces.
- Gateway Console v0 is planned for account/admin/runtime summary, not deep
  runtime exploration.
- Hosted user auth now routes normal users to per-principal runtime data
  planes; any explorer contract must derive the browsed owner from the current
  principal unless an admin-only audited route explicitly selects another user.

## Problem or opportunity
Runtime state is opaque. Users want a file-system-like way to list, search,
inspect, export, delete, and understand their own runtime data. It is unclear
whether that belongs in Gateway Console, Observer, or a new app.

## Proposed direction
Define an API-first Runtime Explorer contract before creating a package. The
contract should cover list/search/detail/export/delete for current user's runs,
artifacts, memory, workflows, sessions, workspace files, prompt-cache metadata,
and storage usage. Keep admin cross-user exploration behind explicit RBAC.
Before promotion, classify data by sensitivity: prompts, tool parameters,
artifacts, memory, logs, PII, secrets, deleted data, retention state, and export
redaction needs.

Start with a read-only normalized resource envelope:
`type`, `id`, `scope`, `owner`, `sensitivity`, `created_at`, `updated_at`,
`summary`, `metadata`, `links`, and permission-aware `actions`. Keep
`browse/search/detail` separate from `export/delete`; export and deletion need
separate RBAC, audit logging, retention semantics, and confirmation UX.

The runtime should feel queryable like an operating/file system, but that does
not mean it should expose raw files first. The first API should expose typed
runtime resources (runs, artifacts, memories, workflows, sessions, prompt-cache
entries, workspace files, and storage summaries) with cursor pagination,
date/type filters, and redacted previews.

## Why it might matter
If runtime exploration becomes a large UX, `abstractexplorer` is a clean name
and boundary. If it stays small, it can remain Gateway Console tabs or Observer
pages.

## Promotion criteria
- Gateway Console v0 and Observer both need overlapping runtime browsing.
- Users need cross-cutting runtime search beyond run observability.
- RBAC policy matrix can safely expose explorer routes.
- Data classification, pagination, quota, export redaction, delete/retention,
  and audit requirements are written down.

## Validation ideas
- Alice/Bob cannot explore each other's runtime data.
- User can search own artifacts/runs/memory by metadata.
- Admin cross-user views require admin role and audit logging.
- Non-admin explorer requests cannot pass `owner_id`, `all_owners`, or arbitrary
  runtime paths to escape their principal-derived runtime.
- Export/delete actions require explicit action-level authorization and produce
  audit events.

## Non-goals
- This proposal does not authorize a new package yet.
- Do not build broad runtime exploration before route-family RBAC exists.
- Do not implement delete/export as ordinary browsing actions; treat them as
  high-risk operations with explicit RBAC, audit, and retention semantics.
- Do not turn the initial Gateway Console into a deep explorer before the API
  contract and sensitivity labels exist. It can link to Explorer/Observer once
  the boundary is stable.

## Guidance for future agents
Reserve `abstractexplorer` as a plausible future package name, but start with
contracts and narrow Gateway/Observer integrations.

## Review synthesis - 2026-05-30

Three independent review perspectives converged on the same boundary:

- Product/UX perspective: build Runtime Explorer as a read-only Observer page
  first. A user should be able to inspect their own runs, artifacts,
  session-level memory, workflow references, and storage summaries without
  learning raw Gateway routes. Keep destructive operations and admin
  cross-user views out of v0.
- Security/control-plane perspective: Gateway should own the explorer API
  contract, RBAC, redaction, sensitivity labels, links, and permission-aware
  actions. Normal user routes must derive owner from the authenticated
  principal; do not accept `tenant_id`, `runtime_id`, `owner_id`, raw workspace
  paths, or `all_owners` as ordinary user selectors.
- Architecture perspective: do not create `abstractexplorer` yet. Gateway
  already owns the data access and Observer already owns observability UX. A
  separate package becomes justified only if cross-resource search, saved
  views, export/retention tooling, and runtime "file system" navigation grow
  beyond Observer's natural scope.

If this proposal is promoted, narrow the first planned item to
`runtime_explorer_read_only_v1`: a Gateway envelope contract plus an
Observer-hosted page. The first envelope should cover `run`, `artifact`,
`memory`, `workflow`, and `defaults` resources with bounded previews,
provenance links, sensitivity labels, and action availability. Defer raw
workspace browsing, prompt-cache internals, delete/export, and admin cross-user
runtime browsing until separate RBAC, audit, redaction, and retention semantics
are written.

--- docs/backlog/proposed/gateway-control-plane/0152_abstractmanager_package_extraction.md ---
# Proposed: AbstractManager package extraction

## Metadata
- Created: 2026-05-30
- Status: Proposed
- Completed: N/A

## ADR status
- Governing ADRs: ADR-0018, ADR-0021, ADR-0033, ADR-0035
- ADR impact: May revise existing ADR

## Context
Gateway needs an admin/account/config UX. The first version should likely be
Gateway-served to keep first-run setup simple. If the UI grows into a rich
control-plane product, a separate package such as `abstractmanager` may become
cleaner.

## Current code reality
- Gateway owns identity, routing, user CRUD, and control-plane APIs.
- Core owns config/default schema and persistence.
- Flow, Code, Observer, and Assistant are task-specific apps.
- There is no Manager package.

## Problem or opportunity
A built-in Gateway Console can become too large if it tries to own users,
secrets, defaults, workflows, runtime browsing, audit, setup, and operations.
A separate Manager package could improve frontend code quality and release
boundaries, but creating it too early increases package sprawl.

## Proposed direction
Start with Gateway Console v0 inside Gateway. Reassess extraction after admin
console, config/defaults, and workflow ACL pages are real. If extracted,
`abstractmanager` should be a Gateway-hosted or Gateway-connected control-plane
app, not a second authority.
Gateway Console v0 should still be built behind a clean frontend boundary:
typed Gateway API client, no direct Core calls, no app-local auth assumptions,
and asset/package layout that can be extracted later without rewriting
authorization.

## Why it might matter
This keeps initial setup simple while preserving a path to a cleaner product
boundary if the admin/config UX grows.

## Promotion criteria
- Gateway Console frontend becomes too large for Gateway package maintenance.
- Multiple Gateway deployments need the same rich admin UI.
- AbstractUIC components and release flow make a package split cheaper than
  embedding assets in Gateway.
- Console v0, per-principal config/defaults, workflow ACLs, and the
  responsibility split have shipped or been accepted.
- Extraction does not make first-run setup harder.

## Validation ideas
- Compare bundle size, release overhead, install profile complexity, and user
  setup friction before extraction.
- Confirm Manager never bypasses Gateway authorization.

## Non-goals
- Do not create `abstractmanager` before Gateway Console v0 proves the need.
- Do not let Manager own secrets/config independently of Gateway/Core.
- Do not extract merely because the name is attractive; extract only when
  maintenance, reuse, or release pressure proves the package boundary.

## Guidance for future agents
Treat `abstractmanager` as a package-boundary option, not a feature requirement.
The authority remains Gateway plus Core config APIs either way.

--- docs/backlog/proposed/gateway-control-plane/0155_hosted_proxy_shared_helper_extraction.md ---
# Proposed: Hosted proxy shared helper extraction

## Metadata
- Created: 2026-05-30
- Status: Proposed
- Completed: N/A

## ADR status
- Governing ADRs: ADR-0018, ADR-0033
- ADR impact: None unless promoted to a package-boundary change.

## Context
Flow, Code Web, and Observer all implement hosted Gateway browser-session
proxying. Code Web and Observer currently duplicate similar Node server logic
for app cookies, Gateway session exchange, CSRF forwarding, logout, and Gateway
URL selection. A review found real drift: Flow blocked remote-host browser
Gateway URL changes while Code Web and Observer did not.

## Current code reality
- Flow's Python backend and static/Vite server implementations already reject
  browser Gateway URL changes from non-local hosts unless explicitly enabled.
- Code Web and Observer now have matching guards and focused tests, but their
  Node server implementations remain package-local.
- No shared JavaScript package currently owns hosted Gateway proxy semantics.

## Problem or opportunity
Duplicated hosted proxy logic can drift again as app behavior evolves. A shared
helper could reduce security drift, but extracting too early may create a
cross-package abstraction that fits only today's two Node servers and not Flow's
Python host or future apps.

## Alternatives
- Keep package-local code and require conformance tests in every hosted browser
  app. This is small and reversible, but duplicated code remains.
- Extract a tiny shared Node helper for Code Web and Observer only. This reduces
  drift for the duplicated Node servers, but adds a new dependency boundary and
  does not help Flow's Python host.
- Define a language-neutral conformance suite first, then extract helpers only
  when at least two apps need identical behavior beyond tests. This has slower
  reuse but better evidence for the abstraction.

## Recommendation
Use conformance tests immediately and defer helper extraction. Promote this
proposal if another hosted app is added or Code/Observer drift again despite
tests. At that point, extract the smallest shared Node helper that owns only
cookie/session/proxy policy, not app UI or provider/default UX.

## Promotion criteria
- Two or more Node hosted apps need the same code changes at the same time.
- A conformance test catches repeated drift that would be easier to prevent by
  centralizing the helper.
- The helper can stay independent of app layout, routing, and feature flags.

## Non-goals
- Do not force Flow's Python backend into a JavaScript helper.
- Do not create a shared UI component for login/defaults from this proposal.
- Do not move Gateway authorization decisions out of Gateway.

## Validation if promoted
- Shared helper unit tests for cookie flags, URL guards, session exchange,
  logout, CSRF injection, and header stripping.
- App-level tests proving Code Web and Observer still expose the expected API
  shape after adopting the helper.

--- docs/backlog/planned/074_agent_skills_integration.md ---
# 074 — Agent Skills (`SKILL.md`) Integration

**Status**: Planned  
**Date**: 2026-02-21  
**Priority**: Medium–High (interoperability + distribution)  
**Components**: abstractagent, abstractruntime, abstractgateway, docs

## Summary

Add first-class support for the **Agent Skills** ecosystem format (a folder with a required `SKILL.md`) so
AbstractFramework can import/share procedural “skill packs” across hosts and gateway-first deployments.

Key design: **flows run; skills are activated/loaded**. Skill scripts (when enabled) execute only via explicit tools
with approvals and evidence capture.

## Reason

- Skills are an emerging interoperability layer across agent ecosystems (Claude Code/API and third-party packs).
- AbstractFramework already has the right execution substrate (durable runs, tool boundaries, replay-first ledger).
- Skills complement (do not replace) `.flow` bundles: skills are lightweight procedure packs; flows are durable programs.

## Scope

### In scope

- Skill discovery (metadata-only), progressive disclosure, and activation.
- Durable/observable activation:
  - activation and resource reads produce ledger records
  - large skill bodies/resources stored as artifacts (keep run state JSON-safe)
- Optional enforcement of skill `allowed-tools` as a restriction (intersection with run tool allowlists).
- Optional gateway skills registry (list/fetch/install/deprecate) parallel to `.flow` bundle distribution.

### Out of scope (v0)

- “Claude Code extensions” (slash commands, hooks, subagent forks) unless they map cleanly to runtime primitives.
- Provider-specific “container skills” support unless explicitly configured (keep `abstractcore` lean).
- Implicit script execution. Skill scripts only run as explicit tools.

## Proposed interaction model

1) **Run-attached skills (primary)**: hosts attach available-skill metadata to a run; users activate skills explicitly.
2) **Bundle-declared dependencies (secondary)**: `.flow` bundles may declare `skills.required` / `skills.defaults` via
   `manifest.metadata` without changing the bundle format.
3) **Bundle-embedded skills (optional later)**: embed skills under bundle `assets/*` for hermetic distribution.

## Dependencies / decisions

- Skill identity/version semantics:
  - spec-level identity is the skill `name` (leaf directory name; constrained format)
  - additional namespacing (publisher/source) is optional but may be needed to avoid collisions across multiple roots/registries
  - version can live in `metadata` (spec) and/or be derived from content hash (host policy)
- Default search paths (user + project) and precedence rules.
- Runtime data model for active skills (likely under `_runtime` namespace).
- Security policy for `allowed-tools` and for script execution (approvals + sandboxing).
- `allowed-tools` parsing policy:
  - field is experimental and often uses environment-specific tokens (e.g., Claude Code tool names)
  - enforce only when tokens can be mapped to AbstractFramework tool names; otherwise emit `#FALLBACK` and do not relax tool policy
- Authoring UX for bundle-declared skill dependencies:
  - VisualFlow JSON currently has no `metadata` field, and the gateway publish path auto-generates manifest metadata.
  - If we want `manifest.metadata.skills.*` to be author-editable, we need an authoring surface (CLI metadata args,
    Flow Editor fields, or extend the VisualFlow schema).

## Plan (high level)

1) Add a small shared parser/loader library (recommended: new `abstractskills` package).
2) Add local host UX in AbstractCode/Assistant: discovery + explicit activation.
3) Add runtime-owned activation handler + ledger + artifacts + allowlist intersection.
4) Add gateway skills registry (optional): distribution and thin-client discovery.
5) Add script-to-tool mapping with evidence/approvals (safe-by-default).

## Acceptance criteria

- Metadata discovery does not bloat prompts (caps + progressive disclosure).
- Activation is durable and replayable:
  - skill bodies/resources are artifact-backed when large
  - activation records a content hash (and/or the artifact id) so resumed runs don’t silently pick up modified skill files
- `allowed-tools` restricts execution; out-of-policy calls are denied with actionable `#FALLBACK`.
- No silent fallbacks; truncation is labeled `#TRUNCATION`.

## References

- Proposal guide: `docs/guide/agent-skills.md`
- Detailed phased plan: `docs/backlog/planned/074_agent_skills_integration_plan.md`
- Research notes (spec + ecosystem scan): `docs/skills/`

--- docs/backlog/planned/074_agent_skills_integration_plan.md ---
# Agent Skills integration plan (phased) — AbstractFramework

Supplement to: `docs/backlog/planned/074_agent_skills_integration.md`

## Scope and goal

Integrate the **Agent Skills** (SKILL.md) format into AbstractFramework so that:

- skills are **discoverable** (local + gateway-first),
- skills are **loaded progressively** (metadata first; full content on activation),
- skill usage is **durable + observable** (ledger-recorded, replay-first),
- skill scripts are **safe by default** (explicit approvals, sandboxed execution, no silent fallbacks),
- the integration stays **modular** (does not bloat `abstractcore` or break layering).

This plan focuses on the core packages and contracts:
`abstractcore`, `abstractruntime`, `abstractagent`, `abstractgateway`, plus optional `abstractmemory`/`abstractsemantics`.

## What we learned (clarifications from the spec/ecosystem)

- The Agent Skills spec requires `SKILL.md` with YAML frontmatter including at least `name` + `description`.
- The spec constrains `name` (1–64 chars, lowercase alnum + hyphens) and requires it to match the skill’s directory name.
- The spec also defines optional fields (notably `license`, `compatibility`, `metadata`) and an **experimental** `allowed-tools` field.
- Skills support two primary integration shapes:
  - **Filesystem-based** (local): the agent reads skill folders from disk.
  - **Tool-based** (remote): the agent requests skill content via a tool/API.
- The ecosystem often packages skills as a zipped **`.skill` file** (convention used by Anthropic tooling), even though the base spec is folder-oriented.
- Claude Code adds extra features (slash commands, hooks, subagents) that are useful but **not** required to get value in AbstractFramework.

## Non-negotiable constraints (AbstractFramework architecture)

These come from the implemented contracts in `abstractruntime` + `abstractgateway`:

- **Durable state is JSON-safe** (`RunState.vars`); large content must be artifact-backed.
- **Replay-first**: the ledger is source-of-truth; streaming is an optimization.
- **Tool boundary**: tool schemas may cross boundaries; tool callables do not. Side effects must remain host-executed.
- **No silent fallbacks**: degraded paths must emit actionable `#FALLBACK` warnings; any truncation must be labeled `#TRUNCATION`.

## Skills vs flows (comparison + recommendation)

**Which is “more advanced”?**
- **Flows are more advanced as an execution model** inside AbstractFramework: they are durable, replayable programs (a persisted state machine) with explicit waits, tool boundaries, and ledger visibility.
- **Skills are more advanced as a portability/distribution format** across agent ecosystems: they are a lightweight, tool-agnostic packaging of procedures, prompts, scripts, and references.

**Can every skill be modeled as a flow?**
- **Conceptually: yes.** A skill is “a procedure + resources”; a flow is “a durable procedure.” If the host has the needed tools, a flow can orchestrate the same steps (including loops, approvals, and evidence capture).
- **Practically: the limiting factor is tool availability and environment assumptions**, not the flow model. Many ecosystem skills implicitly assume a specific toolset (Claude container tools, Playwright installed, doc tooling present, etc.). AbstractFramework can model the workflow, but it must also supply or map the required tools.
- **Some skills are not worth turning into flows**: “guidelines/checklists/style” skills are better as *optional prompt modules* that a generic agent flow can load, rather than as their own executable workflows.

### Recommended interaction model (v0)

Treat skills as **data modules** that can be attached to runs and optionally referenced by workflows.
In AbstractFramework terms: **flows run; skills are activated/loaded** (and skill scripts only execute as explicit tools).

1) **Run-attached skills (primary)**
   - A client/host (AbstractCode / AbstractAssistant / thin client via gateway) attaches `available_skills` metadata to the run and activates skills explicitly.
   - The agent workflow remains a normal `abstractcode.agent.v1` flow (or AbstractAgent pattern) that knows how to load/activate skills via a runtime-owned handler.

2) **Bundle-declared skill dependencies (secondary)**
   - A `.flow` bundle may declare skill dependencies in `manifest.metadata` (no format change required).
   - Example shape:
     - `skills.required`: list of `{id, version?}` that must be present
     - `skills.defaults`: list of `{id, version?}` to auto-activate at run start
   - This keeps workflows small and allows organizations to standardize “this workflow expects these skills”.

3) **Bundle-embedded skills (optional; later)**
   - WorkflowBundles already support `assets/*` in the format, but the current packer does not populate `manifest.assets`.
   - If we want hermetic distribution (“workflow + its skills in one file”), we can extend pack/unpack tooling to include `skills/<id>/SKILL.md` and resources as bundle assets, and expose them through the gateway like other bundle bytes.

### Implementation reality check (what fits best with existing code)

AbstractRuntime’s AbstractCore integration already supports **runtime-owned tools** via the `TOOL_CALLS` effect handler
(example: `open_attachment`). This is the best “lowest-friction” way to integrate skills without introducing new effect
types or special-casing flows:

- Add runtime-owned tool specs (no host callable) like:
  - `list_skills()` → returns metadata-only list (progressive disclosure)
  - `open_skill(skill_id, version?, resource_path?)` → returns full `SKILL.md` (and optionally a specific resource),
    snapshotting content into `ArtifactStore` (recording a content hash) and returning artifact refs for replay safety
  - optional `activate_skill(skill_id, ...)` → updates `_runtime.skills.active` and applies `allowed-tools` as a
    restriction (intersection with the run’s allowed tool policy)
- Execute these inside the existing `TOOL_CALLS` handler (same pattern as `open_attachment`), so:
  - activation/read events are ledger-recorded like any other effect
  - runs remain JSON-safe (bodies/resources artifact-backed when large)
  - flows can compose over skills naturally using existing Tool/CallTool nodes (no new VisualFlow node type required)

**Important edge case (passthrough/untrusted tool execution):**
runtime-owned tools cannot be interleaved with delegated tool execution. The existing `open_attachment` logic already
detects delegating executors and falls back to a durable WAIT that the host must resume; the skills tools should follow
the same contract.

## Proposed component map (where code should live)

### A) New small library: `abstractskills` (recommended)

Create a tiny, dependency-light package (like `abstractsemantics`) to avoid sprinkling parsers across clients:

- **Parsing**
  - Parse `SKILL.md` frontmatter + body.
  - Provide a stable `SkillProperties` model (name/description/license/compatibility/metadata/allowed_tools).
- **Validation**
  - Validate required files and basic folder layout.
  - Provide deterministic “available skills” prompt text (prefer spec-style `<available_skills>...</available_skills>`).
- **Packaging**
  - `pack_skill_dir(...) -> .skill` zip (convention)
  - `unpack_skill_zip(...) -> dir`
- **Loaders**
  - `FilesystemSkillLoader` (project + user scopes; precedence rules)
  - `RemoteSkillLoader` (fetch by id/version from a gateway API)

Why a separate package:
- Keeps `abstractcore`/`abstractruntime` lean.
- Lets UIs/hosts (AbstractCode, AbstractAssistant, web) share identical semantics.

### B) `abstractagent` (agent patterns)

Add a **skill-aware prompt builder** while keeping “logic stays runtime-agnostic”:

- Extend adapters to optionally inject *only* a compact “available skills” metadata block into the system prompt.
- Support “activation” as an explicit step (user-invoked first; model-invoked later).
- If we add a model-invoked path, define a **schema-only built-in tool** (e.g., `open_skill`) so the runtime/host executes the read and logs it.

### C) `abstractruntime` (durable execution + ledger)

Add a runtime-owned integration point so skill loading/activation is durable and observable:

- Provide a runtime-owned tool/effect handler for reading skill content by `(skill_id, version, path?)` that:
  - stores full SKILL.md content as an artifact (or returns it if small),
  - appends a ledger record for “skill activated/read”,
  - enforces limits (max bytes, max refs) and emits `#FALLBACK`/`#TRUNCATION` as needed.
- Add a small policy hook that merges a skill’s `allowed-tools` into `_runtime.allowed_tools` (intersection, not union) when present.

### D) `abstractgateway` (distribution + multi-client)

Add a **skills registry** parallel to the existing WorkflowBundles (`.flow`) registry:

- Storage: `<data_dir>/skills/` (folders and/or `.skill` zips), with a small index file.
- API:
  - list skills (metadata-only)
  - fetch skill content/resources on demand
  - install/update/deprecate skills (admin endpoints; auth-protected)
- Thin-client support: include skills metadata in discovery endpoints so UIs can show “available skills” without fetching full bodies.

### E) Optional: `abstractmemory` + `abstractsemantics`

Later, add “skills search” and governance:

- Store skill metadata + embeddings in AbstractMemory for semantic lookup and recommendations.
- Use AbstractSemantics to standardize skill categories/tags if you want “approved skill taxonomy”.

## Phased delivery plan

### Phase 0 — ADR + minimal core utilities (1–2 weeks)

Deliverables:
- ADR: “Agent Skills integration in AbstractFramework”
  - define goals/non-goals, security posture, default discovery paths, and what “activation” means.
- New package `abstractskills` (or a minimal module under `abstractcore` if you want fewer repos)
  - parse SKILL.md, expose `SkillProperties`, validate folder, compile metadata prompt block.
- Unit tests for parsing/validation (no runtime/gateway changes yet).

Decisions to lock:
- Skill identity:
  - spec-level identity is the skill `name` (must match the leaf directory name)
  - AbstractFramework may optionally add an external namespace/source dimension (e.g., multiple skill roots or gateway registry) to avoid collisions
- Skills ↔ flows contract:
  - run-attached skills are the primary mechanism
  - `.flow` bundles may declare dependencies in `manifest.metadata`
  - bundle-embedded skills (assets) is optional and can be deferred
- Default skill search paths:
  - user: `~/.abstractframework/skills/`
  - project: `.abstractframework/skills/`
  - plus an explicit CLI override list

### Phase 1 — Local skills in host apps (metadata + user-invoked activation) (1–2 weeks)

Target: AbstractCode + AbstractAssistant (local-host mode).

Deliverables:
- “Skills discovery” in the host process:
  - scan default paths; load only properties (`name`/`description`/optional metadata).
  - show skills in `/help` or a dedicated command (`/skills`).
- User-invoked activation:
  - `/skill <name>` loads full SKILL.md and injects it (or a summarized form) into the next agent cycle.
  - activation is **explicit** (no model-invoked loading yet).

Guardrails:
- Hard cap on metadata per skill + total skills injected.
- No `scripts/` execution in this phase; if present, display “requires enablement” with `#FALLBACK`.

### Phase 2 — Durable activation + ledger logging + allowed-tools gating (2–3 weeks)

Target: AbstractRuntime + AbstractAgent integration.

Deliverables:
- Runtime-owned “skill activation/read” handler:
  - artifacts for skill bodies/resources
  - ledger record for activation and any resource reads
- Tool gating:
  - if a skill provides `allowed-tools`, enforce it at the runtime tool executor boundary
  - log any denied tool calls with `#FALLBACK` and actionable instruction

Why now:
- This is where skills become “first-class and auditable” rather than a prompt trick.

### Phase 3 — Gateway skills registry (distribution + thin-client UX) (3–5 weeks)

Target: AbstractGateway + web/thin clients.

Deliverables:
- Gateway skills registry:
  - scan `<flows_dir>/`-adjacent `skills/` OR a new `ABSTRACTGATEWAY_SKILLS_DIR`
  - install/update via `.skill` zip upload (admin-only)
  - deprecate/disable skills without deleting (keeps historical replay intact)
- APIs:
  - `GET /api/gateway/skills` (metadata)
  - `GET /api/gateway/skills/{id}` (skill body + index of resources)
  - `GET /api/gateway/skills/{id}/resources/{path}` (resource bytes)
- Thin clients:
  - show available skills list; allow activation by sending a durable command that updates run vars (or triggers a runtime effect).

### Phase 4 — Script execution (safe-by-default) (3–6 weeks)

Target: host tool execution + approvals + evidence.

Deliverables:
- Define a safe contract for skill scripts:
  - scripts run only via explicit tools (never “implicitly because a skill exists”)
  - require allowlisted tool names + explicit approvals (and/or a sandbox)
  - capture evidence/artifacts for outputs
- Provide a “skill tool adapter”:
  - map skill scripts to runtime tools (e.g., `skill.<skill_id>.<script_name>`) with stable schemas.

### Phase 5 — Advanced features + ecosystem compatibility (ongoing)

Optional enhancements once core value is proven:
- Claude Code extensions support (selectively):
  - `user-invocable`, `disable-model-invocation`, `argument-hint`
  - `context: fork` (maps well to runtime subworkflows)
  - hooks (pre/post) as explicit, durable nodes/effects
- Skill search:
  - index skill metadata in AbstractMemory; add a “recommend skills” tool.
- Provider-specific integration:
  - Optional AbstractCore integration for Anthropic “container skills” (behind an extra and explicit config).

## Open questions (resolve in Phase 0 ADR)

- Skill id/version semantics: do we require semantic versions, or use content hashes?
- Where does “active skills” live in the durable model: `context`, `scratchpad`, or `_runtime`?
- Do we ever want a first-class `manifest.skills` field (instead of `manifest.metadata.skills`) for bundle-declared dependencies?
- Authoring UX for bundle-declared dependencies:
  - VisualFlow JSON currently has no `metadata` field; the gateway publish path auto-generates manifest metadata.
  - If we want authors to declare skill dependencies via manifests, we must add an authoring surface (CLI metadata,
    Flow Editor fields, or extend VisualFlow schema to carry an optional metadata object).
- Multi-tenant gateway policy:
  - who can install skills, who can activate, and how to audit skill usage?

--- docs/skills/claude-agent-skills-overview.md ---
# Claude Shareable Skills Overview (Agent Skills) — Feb 2026

## What “Skills” Means Here
This report covers **shareable Agent Skills**: portable, model-agnostic skill packs
defined by the **Agent Skills open standard** and used in Claude (Claude.ai, Claude Code,
and Claude API). These are *not* intrinsic model capabilities; they are **files and folders**
that specialize an agent’s behavior.

Primary definitions:
- Agent Skills are “folders of instructions, scripts, and resources” that agents can discover
  and use to perform tasks more accurately and efficiently.  
  Source: https://agentskills.io/what-are-skills.md
- Anthropic’s skills repository is an implementation of this standard for Claude.  
  Source: https://raw.githubusercontent.com/anthropics/skills/main/README.md

## Core Structure (How Skills Are Packaged)
An Agent Skill is a directory with a required `SKILL.md` and optional resources:
```
my-skill/
├── SKILL.md        # required: YAML frontmatter + instructions
├── scripts/        # optional: executable code
├── references/     # optional: on-demand docs
└── assets/         # optional: templates/resources
```
Sources:
- https://agentskills.io/what-are-skills.md
- https://agentskills.io/specification.md

### SKILL.md Format
`SKILL.md` includes:
- YAML frontmatter (required): `name`, `description`
- Markdown body: instructions and workflow details  
Source: https://agentskills.io/specification.md

## How Skills Work (High-Level)
Agent Skills use **progressive disclosure**:
1. **Discovery**: Only `name` and `description` are loaded at startup.
2. **Activation**: Full `SKILL.md` is loaded when relevant.
3. **Execution**: Optional scripts/resources are accessed as needed.  
Sources:
- https://agentskills.io/what-are-skills.md
- https://www.anthropic.com/engineering/equipping-agents-for-the-real-world-with-agent-skills

This keeps context small while enabling rich, on-demand specialization.

## How Skills Are Used In Claude Code
Claude Code implements the Agent Skills standard and extends it with:
- **Slash commands**: `/skill-name` invokes a skill directly.
- **Invocation control**: `disable-model-invocation` and `user-invocable` frontmatter.
- **Allowed tools**: `allowed-tools` to restrict or pre-approve tool use.
- **Subagents**: `context: fork` to run a skill in a dedicated subagent.
- **Dynamic context injection**: `!` command execution inside skill content.  
Source: https://code.claude.com/docs/en/skills

### Skill Locations In Claude Code
Claude Code loads skills from specific directories:
- Personal: `~/.claude/skills/`
- Project: `.claude/skills/`
- Enterprise-managed settings
- Plugins (skill packs in plugin directories)  
Source: https://code.claude.com/docs/en/skills

## How Skills Are Used In Claude API
The Claude API supports skills as **containers** combined with **code execution**:
- Skills are attached in the `container.skills` parameter.
- Types: `anthropic` (pre-built) or `custom` (uploaded).
- Versions can be pinned or set to `latest`.
- Requires beta headers and the code execution tool.  
Source: https://docs.claude.com/en/api/skills-guide

## How Skills Are Shared
Skills are shareable because they are just folders/files:
- **GitHub repos** and **plugin marketplaces** for discovery.
- **Version control** for team sharing (commit `.claude/skills/`).
- **Claude API Skills** allow uploading custom skills.  
Sources:
- https://raw.githubusercontent.com/anthropics/skills/main/README.md
- https://code.claude.com/docs/en/skills
- https://docs.claude.com/en/api/skills-guide

## Key Takeaways
- Skills are portable, model-agnostic, and *not tied to Claude only*.
- They enable **specialization** by bundling procedural knowledge and scripts.
- Claude Code and Claude API provide first‑party implementations and distribution paths.


--- docs/skills/claude-agent-skills-top-20.md ---
# Top 20 Shareable Claude Skills (Feb 2026)

Selection method:
- **16 skills** from Anthropic’s official `anthropics/skills` repository
- **4 canonical example skills** from Claude Code documentation (shareable, SKILL.md‑based)
This yields 20 widely cited, shareable skills as of Feb 2026.

## 1) `docx` — Word Documents
- **What:** Create/read/edit Word documents with formatting, tables, images, and tracked changes.
- **How:** Uses docx-js and XML edit workflows; provides detailed rules and scripts.
- **Source:** https://raw.githubusercontent.com/anthropics/skills/main/skills/docx/SKILL.md

## 2) `pdf` — PDF Processing
- **What:** Extract, merge, split, OCR, watermark, and create PDF files.
- **How:** Uses pypdf/pdfplumber/reportlab plus CLI tools and optional forms guide.
- **Source:** https://raw.githubusercontent.com/anthropics/skills/main/skills/pdf/SKILL.md

## 3) `pptx` — Presentation Creation/Editing
- **What:** Create or edit slide decks with strong layout and QA guidance.
- **How:** Uses markitdown for extraction; pptxgenjs or template editing; QA loop.
- **Source:** https://raw.githubusercontent.com/anthropics/skills/main/skills/pptx/SKILL.md

## 4) `xlsx` — Spreadsheet Modeling
- **What:** Create and edit spreadsheets with formulas, formatting, and recalculation.
- **How:** Uses pandas/openpyxl with a strict formula recalculation workflow.
- **Source:** https://raw.githubusercontent.com/anthropics/skills/main/skills/xlsx/SKILL.md

## 5) `webapp-testing` — Playwright UI Testing
- **What:** Test local web apps, take screenshots, and validate UI behavior.
- **How:** Playwright scripts plus helper server lifecycle scripts.
- **Source:** https://raw.githubusercontent.com/anthropics/skills/main/skills/webapp-testing/SKILL.md

## 6) `web-artifacts-builder` — Advanced HTML Artifacts
- **What:** Build complex Claude.ai artifacts with React/Tailwind/shadcn.
- **How:** Init project, build, then bundle into a single HTML artifact.
- **Source:** https://raw.githubusercontent.com/anthropics/skills/main/skills/web-artifacts-builder/SKILL.md

## 7) `frontend-design` — UI Design + Code
- **What:** Produce distinctive, production‑grade frontend UI.
- **How:** Strong aesthetic rules + real HTML/CSS/JS or framework code.
- **Source:** https://raw.githubusercontent.com/anthropics/skills/main/skills/frontend-design/SKILL.md

## 8) `algorithmic-art` — Generative p5.js Art
- **What:** Create algorithmic art and interactive HTML artifacts.
- **How:** Define a generative “philosophy” then implement in p5.js.
- **Source:** https://raw.githubusercontent.com/anthropics/skills/main/skills/algorithmic-art/SKILL.md

## 9) `canvas-design` — Visual Design Artifacts
- **What:** Create visual design output (PDF/PNG) from a design philosophy.
- **How:** Design manifest → render high‑craft visual work.
- **Source:** https://raw.githubusercontent.com/anthropics/skills/main/skills/canvas-design/SKILL.md

## 10) `theme-factory` — Theme Application
- **What:** Apply curated design themes (colors/fonts) to artifacts.
- **How:** Choose from pre‑set themes or generate a new one.
- **Source:** https://raw.githubusercontent.com/anthropics/skills/main/skills/theme-factory/SKILL.md

## 11) `brand-guidelines` — Anthropic Brand Styling
- **What:** Apply official Anthropic brand colors and typography.
- **How:** Prescriptive palette and font rules.
- **Source:** https://raw.githubusercontent.com/anthropics/skills/main/skills/brand-guidelines/SKILL.md

## 12) `internal-comms` — Internal Communications
- **What:** Produce 3P updates, newsletters, FAQs, and status reports.
- **How:** Select format from examples and follow structured guidelines.
- **Source:** https://raw.githubusercontent.com/anthropics/skills/main/skills/internal-comms/SKILL.md

## 13) `doc-coauthoring` — Doc Co‑Authoring Workflow
- **What:** Guided multi‑stage workflow for technical docs and specs.
- **How:** Context gathering → structured drafting → reader testing.
- **Source:** https://raw.githubusercontent.com/anthropics/skills/main/skills/doc-coauthoring/SKILL.md

## 14) `mcp-builder` — MCP Server Builder
- **What:** Build MCP servers with best‑practice tool design.
- **How:** Phased workflow with references for MCP SDKs and evaluation.
- **Source:** https://raw.githubusercontent.com/anthropics/skills/main/skills/mcp-builder/SKILL.md

## 15) `skill-creator` — Skill Authoring Guide
- **What:** Build new skills with proper structure and packaging.
- **How:** Progressive disclosure guidance, packaging scripts, and best practices.
- **Source:** https://raw.githubusercontent.com/anthropics/skills/main/skills/skill-creator/SKILL.md

## 16) `slack-gif-creator` — Slack GIFs
- **What:** Create Slack‑optimized GIFs and animations.
- **How:** Python/PIL workflow with helper utilities and validation.
- **Source:** https://raw.githubusercontent.com/anthropics/skills/main/skills/slack-gif-creator/SKILL.md

## 17) `deploy` — Manual Deployment Workflow (Example)
- **What:** A manually‑invoked deployment checklist.
- **How:** `disable-model-invocation: true` ensures only user can run it.
- **Source:** https://code.claude.com/docs/en/skills

## 18) `safe-reader` — Read‑Only Mode (Example)
- **What:** Restrict Claude to safe, read‑only tools.
- **How:** Use `allowed-tools` to limit tool access within the skill.
- **Source:** https://code.claude.com/docs/en/skills

## 19) `pr-summary` — Dynamic PR Summaries (Example)
- **What:** Summarize pull requests using live CLI data.
- **How:** `!` command injection runs shell commands before the prompt.
- **Source:** https://code.claude.com/docs/en/skills

## 20) `deep-research` — Subagent Research (Example)
- **What:** Launch a forked research subagent with isolated context.
- **How:** `context: fork` and `agent: Explore` run the skill in a subagent.
- **Source:** https://code.claude.com/docs/en/skills


--- docs/skills/claude-agent-skills-sources.md ---
# Claude Agent Skills Sources (Accessed Feb 2026)

## Agent Skills Standard
- Overview: https://agentskills.io/what-are-skills.md
- Specification: https://agentskills.io/specification.md
- Integration guide: https://agentskills.io/integrate-skills.md
- Standard repo: https://raw.githubusercontent.com/agentskills/agentskills/main/README.md

## Anthropic Engineering
- Agent Skills overview and progressive disclosure:
  https://www.anthropic.com/engineering/equipping-agents-for-the-real-world-with-agent-skills

## Anthropic Skills Repository (Official Examples)
- Repository overview:
  https://raw.githubusercontent.com/anthropics/skills/main/README.md

### SKILL.md sources (official examples)
- Algorithmic art:
  https://raw.githubusercontent.com/anthropics/skills/main/skills/algorithmic-art/SKILL.md
- Brand guidelines:
  https://raw.githubusercontent.com/anthropics/skills/main/skills/brand-guidelines/SKILL.md
- Canvas design:
  https://raw.githubusercontent.com/anthropics/skills/main/skills/canvas-design/SKILL.md
- Doc coauthoring:
  https://raw.githubusercontent.com/anthropics/skills/main/skills/doc-coauthoring/SKILL.md
- DOCX:
  https://raw.githubusercontent.com/anthropics/skills/main/skills/docx/SKILL.md
- Frontend design:
  https://raw.githubusercontent.com/anthropics/skills/main/skills/frontend-design/SKILL.md
- Internal comms:
  https://raw.githubusercontent.com/anthropics/skills/main/skills/internal-comms/SKILL.md
- MCP builder:
  https://raw.githubusercontent.com/anthropics/skills/main/skills/mcp-builder/SKILL.md
- PDF:
  https://raw.githubusercontent.com/anthropics/skills/main/skills/pdf/SKILL.md
- PPTX:
  https://raw.githubusercontent.com/anthropics/skills/main/skills/pptx/SKILL.md
- Skill creator:
  https://raw.githubusercontent.com/anthropics/skills/main/skills/skill-creator/SKILL.md
- Slack GIF creator:
  https://raw.githubusercontent.com/anthropics/skills/main/skills/slack-gif-creator/SKILL.md
- Theme factory:
  https://raw.githubusercontent.com/anthropics/skills/main/skills/theme-factory/SKILL.md
- Web artifacts builder:
  https://raw.githubusercontent.com/anthropics/skills/main/skills/web-artifacts-builder/SKILL.md
- Webapp testing:
  https://raw.githubusercontent.com/anthropics/skills/main/skills/webapp-testing/SKILL.md
- XLSX:
  https://raw.githubusercontent.com/anthropics/skills/main/skills/xlsx/SKILL.md

## Claude Code Skills Documentation
- Skills overview and examples:
  https://code.claude.com/docs/en/skills

## Claude API Skills Guide
- Skills + container usage + code execution:
  https://docs.claude.com/en/api/skills-guide

## Notes
- The Claude Help Center articles for skills were not accessible via the fetch tool.
- This report relies on official Anthropic and Agent Skills standard sources only.


--- docs/skills/agent-skills-ecosystem-scan.md ---
# Agent Skills Ecosystem Scan — Additional High-Value Skills (Feb 2026)

## Purpose
Continue the online search for **shareable Agent Skills** beyond Anthropic’s public repo and identify additional skill packs that could be high value for AbstractFramework.

## Methodology + Limits
- **Primary sources**: curated ecosystem lists and public GitHub repositories that publish `SKILL.md`.
- **Repository inspection**: read `SKILL.md` files directly for concrete scope and behavior.
- **Limitations**: GitHub code search API requires authentication (401). I used repository search + curated lists instead of code search.

## Additional Skill Libraries (Beyond Anthropic)

### 1) Vercel — Frontend performance + UI quality
Vercel publishes a curated skill pack with concrete UI/React guidance:
- **React Best Practices**: 57 performance rules for React/Next.js.  
  Source: https://raw.githubusercontent.com/vercel-labs/agent-skills/main/skills/react-best-practices/SKILL.md
- **React Composition Patterns**: guidance for scalable component APIs.  
  Source: https://raw.githubusercontent.com/vercel-labs/agent-skills/main/skills/composition-patterns/SKILL.md
- **React Native Skills**: performance and UI patterns for mobile apps.  
  Source: https://raw.githubusercontent.com/vercel-labs/agent-skills/main/skills/react-native-skills/SKILL.md
- **Web Design Guidelines**: UI review with external rule fetch.  
  Source: https://raw.githubusercontent.com/vercel-labs/agent-skills/main/skills/web-design-guidelines/SKILL.md

### 2) HashiCorp — Terraform/IaC workflows
HashiCorp’s pack focuses on Terraform generation and module design:
- **Terraform Style Guide**: code organization + security rules.  
  Source: https://raw.githubusercontent.com/hashicorp/agent-skills/main/terraform/code-generation/skills/terraform-style-guide/SKILL.md
- **Refactor Module**: monolith → reusable module workflow.  
  Source: https://raw.githubusercontent.com/hashicorp/agent-skills/main/terraform/module-generation/skills/refactor-module/SKILL.md
- **Run Acceptance Tests**: structured provider acceptance testing flow.  
  Source: https://raw.githubusercontent.com/hashicorp/agent-skills/main/terraform/provider-development/skills/run-acceptance-tests/SKILL.md

### 3) Trail of Bits — Security scanning + triage
Trail of Bits publishes a security-centric skill library with tooling guidance:
- **CodeQL**: structured database build + analysis workflow.  
  Source: https://raw.githubusercontent.com/trailofbits/skills/main/plugins/static-analysis/skills/codeql/SKILL.md
- **Semgrep**: multi-agent scan + triage workflow with hard approval gates.  
  Source: https://raw.githubusercontent.com/trailofbits/skills/main/plugins/static-analysis/skills/semgrep/SKILL.md
- **SARIF Parsing**: process and aggregate scan results.  
  Source: https://raw.githubusercontent.com/trailofbits/skills/main/plugins/static-analysis/skills/sarif-parsing/SKILL.md

### 4) OWASP Security Skill — Code review checklist
OWASP/ASVS-based security review guidance:
- **OWASP Security**: Top 10:2025 + ASVS 5.0 + agentic AI security checklist.  
  Source: https://raw.githubusercontent.com/agamm/claude-code-owasp/main/.claude/skills/owasp-security/SKILL.md

### 5) Varlock — Secrets hygiene
Skill focused on preventing secrets from leaking to logs or context:
- **Varlock**: safe environment variable handling rules.  
  Source: https://raw.githubusercontent.com/wrsmith108/varlock-claude-skill/main/skills/varlock/SKILL.md

### 6) Superpowers — Engineering process discipline
Reusable procedural skills for safer engineering workflows:
- **Test-Driven Development**  
  Source: https://raw.githubusercontent.com/obra/superpowers/main/skills/test-driven-development/SKILL.md
- **Systematic Debugging**  
  Source: https://raw.githubusercontent.com/obra/superpowers/main/skills/systematic-debugging/SKILL.md

### 7) Product/PM Skills — Problem framing and delivery
PM-oriented skills for better specification and experimentation:
- **Problem Statement**  
  Source: https://raw.githubusercontent.com/product-on-purpose/pm-skills/main/skills/define-problem-statement/SKILL.md
- **PRD**  
  Source: https://raw.githubusercontent.com/product-on-purpose/pm-skills/main/skills/deliver-prd/SKILL.md
- **Experiment Design**  
  Source: https://raw.githubusercontent.com/product-on-purpose/pm-skills/main/skills/measure-experiment-design/SKILL.md

### 8) Ecosystem discovery infrastructure
Curated lists and skill discovery tooling:
- **Awesome Claude Skills** (curated directory of skill packs).  
  Source: https://raw.githubusercontent.com/BehiSecc/awesome-claude-skills/main/README.md
- **Claude Skills MCP server** (vector search of skills).  
  Source: https://github.com/K-Dense-AI/claude-skills-mcp

## High-Value Skill Categories For AbstractFramework
These categories are likely high value based on the framework’s durable runtime, tool approval boundaries, gateway-first distribution, and observability model (see architecture deep dive notes).

1) **Security review + static analysis**
   - CodeQL, Semgrep, SARIF parsing, OWASP security checklists.
   - These align with durable runs, ledger evidence, and tool gating.

2) **Infrastructure / IaC workflows**
   - Terraform style guide + module refactoring skills.
   - Good fit for deterministic workflows and repeatable automation.

3) **Secrets and environment hygiene**
   - Varlock skill prevents sensitive data leakage.
   - Maps to AbstractFramework’s explicit tool approval boundaries.

4) **Engineering process rigor**
   - TDD + systematic debugging skills.
   - Fits agentic workflows and helps ensure safe, repeatable changes.

5) **Product/specification quality**
   - Problem statements, PRDs, experiment design.
   - Useful for SmartNote ingestion and workflow-driven documentation.

6) **Frontend performance + UX review**
   - Vercel React and UI guideline skills.
   - Helpful for AbstractCode usage and code review flows.

## Suggested Next Research Steps
- Deep-scan curated lists for additional domain skills (DevOps, data engineering, compliance).
- Evaluate skill packaging formats in each repo (compatibility + allowed-tools usage).
- Track skills that already embed MCP usage (for compatibility with AbstractRuntime tool gating).

## Sources Index
See `docs/skills/agent-skills-ecosystem-sources.md` for the complete list of sources.

--- docs/skills/agent-skills-ecosystem-sources.md ---
# Agent Skills Ecosystem Sources — Feb 2026

## Curated Lists / Discovery
- Awesome Claude Skills (curated list):  
  https://raw.githubusercontent.com/BehiSecc/awesome-claude-skills/main/README.md
- Claude Skills MCP server (skill search MCP):  
  https://github.com/K-Dense-AI/claude-skills-mcp

## Vercel Agent Skills
- React Best Practices:  
  https://raw.githubusercontent.com/vercel-labs/agent-skills/main/skills/react-best-practices/SKILL.md
- React Composition Patterns:  
  https://raw.githubusercontent.com/vercel-labs/agent-skills/main/skills/composition-patterns/SKILL.md
- React Native Skills:  
  https://raw.githubusercontent.com/vercel-labs/agent-skills/main/skills/react-native-skills/SKILL.md
- Web Design Guidelines:  
  https://raw.githubusercontent.com/vercel-labs/agent-skills/main/skills/web-design-guidelines/SKILL.md

## HashiCorp Agent Skills (Terraform)
- Terraform Style Guide:  
  https://raw.githubusercontent.com/hashicorp/agent-skills/main/terraform/code-generation/skills/terraform-style-guide/SKILL.md
- Refactor Module:  
  https://raw.githubusercontent.com/hashicorp/agent-skills/main/terraform/module-generation/skills/refactor-module/SKILL.md
- Run Acceptance Tests:  
  https://raw.githubusercontent.com/hashicorp/agent-skills/main/terraform/provider-development/skills/run-acceptance-tests/SKILL.md

## Trail of Bits Skills (Security)
- CodeQL skill:  
  https://raw.githubusercontent.com/trailofbits/skills/main/plugins/static-analysis/skills/codeql/SKILL.md
- Semgrep skill:  
  https://raw.githubusercontent.com/trailofbits/skills/main/plugins/static-analysis/skills/semgrep/SKILL.md
- SARIF parsing skill:  
  https://raw.githubusercontent.com/trailofbits/skills/main/plugins/static-analysis/skills/sarif-parsing/SKILL.md

## OWASP / Security
- OWASP security skill:  
  https://raw.githubusercontent.com/agamm/claude-code-owasp/main/.claude/skills/owasp-security/SKILL.md

## Secrets / Environment Hygiene
- Varlock skill:  
  https://raw.githubusercontent.com/wrsmith108/varlock-claude-skill/main/skills/varlock/SKILL.md

## Engineering Process
- Test-Driven Development skill:  
  https://raw.githubusercontent.com/obra/superpowers/main/skills/test-driven-development/SKILL.md
- Systematic Debugging skill:  
  https://raw.githubusercontent.com/obra/superpowers/main/skills/systematic-debugging/SKILL.md

## Product / PM Skills
- Problem Statement skill:  
  https://raw.githubusercontent.com/product-on-purpose/pm-skills/main/skills/define-problem-statement/SKILL.md
- PRD skill:  
  https://raw.githubusercontent.com/product-on-purpose/pm-skills/main/skills/deliver-prd/SKILL.md
- Experiment Design skill:  
  https://raw.githubusercontent.com/product-on-purpose/pm-skills/main/skills/measure-experiment-design/SKILL.md

--- docs/skills/abstractframework-agent-skills-fit.md ---
# AbstractFramework Fit Analysis — Shareable Agent Skills (Feb 2026)

## Executive Fit Summary
AbstractFramework **can benefit significantly** from Agent Skills because the format is
model‑agnostic, portable, and aligns with the framework’s goals: **durability, observability,
and modularity**. Skills are not a replacement for AbstractFlow, but a lightweight, shareable
layer for procedural knowledge and tool workflows.

Sources for skill behavior and format:
- https://agentskills.io/what-are-skills.md
- https://agentskills.io/specification.md
- https://code.claude.com/docs/en/skills
- https://docs.claude.com/en/api/skills-guide
- https://www.anthropic.com/engineering/equipping-agents-for-the-real-world-with-agent-skills

## Where AbstractFramework Benefits (Why + How)

### 1) Add Agent Skills Support To AbstractCode/Assistant
**Why:** Users can import Claude/Cursor/Codex‑style skills (portable SKILL.md packs) without
rewriting workflows. This makes AbstractCode more interoperable.

**How:** Implement a skills loader that:
- Discovers `SKILL.md` folders (project + user scopes).
- Injects `name`/`description` into system prompt (metadata only).
- Loads full `SKILL.md` on activation (progressive disclosure).
- Supports optional `allowed-tools` to enforce tool gating.

**Justification:** This mirrors the Agent Skills spec and Claude Code behavior
(`name`/`description` metadata + on‑demand skill loading).  
Sources: https://agentskills.io/what-are-skills.md, https://code.claude.com/docs/en/skills

### 2) Skill Registry In AbstractGateway
**Why:** The Gateway already distributes workflows; a skills registry enables organization‑wide
sharing of reusable procedural knowledge across all clients.

**How:** Add a skills registry parallel to `.flow` bundles:
- Store skills in a dedicated directory or artifact store.
- Provide an API for listing, enabling, and versioning skills.
- Broadcast skill metadata to thin clients for discovery.

**Justification:** Skills are portable folders; gateway distribution fits the existing architecture.

### 3) Tool Approval Integration Via `allowed-tools`
**Why:** Skills can explicitly list approved tools. This maps directly to AbstractRuntime’s
tool approval boundaries and audit trail.

**How:** When a skill is activated, merge `allowed-tools` with the tool policy for that run.
If a tool is outside the allowlist, require approval and emit `#FALLBACK`.

**Justification:** Agent Skills spec supports `allowed-tools` (experimental) and Claude Code
uses it for permissions.  
Sources: https://agentskills.io/specification.md, https://code.claude.com/docs/en/skills

### 4) Skill Execution Logging In The Ledger
**Why:** Skills are procedural knowledge; their activation and resource use should be auditable.

**How:** Record:
- Skill name/version on activation
- Files/scripts read or executed
- Any outputs or generated artifacts

**Justification:** Matches AbstractFramework’s observability and provenance model.

### 5) Support For `.skill` Packaging
**Why:** Shareable skills need a portable format for teams. The skill‑creator workflow in the
Anthropic repo uses a `.skill` package (zip). Supporting this improves interoperability.

**How:** Add a CLI helper to package/unpackage skills and validate against the spec.

**Justification:** Skills are file‑based; packaging enables controlled distribution and versioning.

### 6) Optional Anthropic Skills API Integration
**Why:** Some Anthropic skills (docx/pdf/pptx/xlsx) are server‑hosted and usable via the API.

**How:** Extend AbstractCore’s Anthropic provider to expose `container.skills` and
code execution when configured. Track skill metadata in the run state.

**Justification:** This is provider‑specific but valuable for users who want
Anthropic‑hosted skills without manual setup.  
Source: https://docs.claude.com/en/api/skills-guide

## Skills That Are Already Covered (Low Incremental Value)
- **Tool calling, MCP, and structured output** already exist in AbstractCore.
- **Durable workflows** are stronger than skills for complex multi‑agent orchestration.

Skills add value primarily as **portable procedural knowledge packs** and
as a compatibility layer with other agent ecosystems.

## Risks And Guardrails
- **Script execution risk:** Skills can include scripts; require sandboxing and explicit
  approvals. If disabled, emit `#FALLBACK`.
- **Context bloat:** Too many skills can overload the prompt; enforce metadata limits and
  avoid loading full skills unless activated.
- **Environment mismatch:** Respect the `compatibility` field; if requirements are unmet,
  warn with `#FALLBACK`.
- **Truncation:** If any skill content must be truncated, label `#TRUNCATION` with reason.

## ADR Recommendation
Create an ADR: **“Agent Skills Support in AbstractFramework”** covering:
- Skill discovery paths and registry model
- Tool approval and sandbox policy
- Ledger/provenance logging for skill activation
- `.skill` packaging support
- Provider‑specific API integration (Anthropic containers)

--- docs/skills/abstractframework-architecture-deep-dive.md ---
# AbstractFramework Architecture Deep Dive — Notes (Feb 2026)

## Scope
This note summarizes the **implemented architecture** across core packages and UIs, based on the official architecture docs in each repo. The goal is to ground any skills recommendations in real constraints.

## System-Level Architecture
AbstractFramework is designed around **durable, observable execution** with a **gateway-first** deployment path (recommended) and a **local in-process** path (alternative).  
Source: `docs/architecture.md`

Key implications:
- Every run is **durable** (explicit waits, restart-safe).
- Every step is **recorded** in an append-only ledger (replay-first UI).
- Tool execution happens at explicit **durable boundaries**.

## Core Invariants (Non-Negotiable)
- **Run state is JSON-safe**; large payloads must be stored as artifacts.  
  Source: `abstractruntime/docs/architecture.md`
- **Ledger is append-only** (hash-chained in some stores); it is the source of truth.  
  Source: `docs/architecture.md`, `abstractruntime/docs/architecture.md`
- **Tool schemas are durable; tool callables are not** (host executes tools after approval).  
  Source: `docs/architecture.md`, `abstractassistant/docs/architecture.md`, `abstractcode/docs/architecture.md`

## Foundation Layer

### AbstractCore (LLM + tools + media)
AbstractCore provides provider-agnostic LLM access, tool schema normalization, and media handling.  
Source: `abstractcore/docs/architecture.md`

Notable capabilities:
- Multi-provider factory (`create_llm`) and consistent interface.
- Tool-call parsing and tag-rewriting for provider compatibility.
- Unified streaming with incremental tool detection.
- Optional capability plugins (voice, vision, music) discovered via entry points.

### AbstractRuntime (durable execution kernel)
AbstractRuntime is the durable workflow engine.  
Source: `abstractruntime/docs/architecture.md`

Notable capabilities:
- `WorkflowSpec` nodes -> `StepPlan` effects.
- Explicit waits (`ASK_USER`, `WAIT_EVENT`, `WAIT_UNTIL`, `TOOL_CALLS`).
- Durable stores (RunStore, LedgerStore, ArtifactStore, Snapshots).
- Workflow bundles (`.flow`) and VisualFlow compiler integration.

## Composition Layer

### AbstractAgent (ReAct / CodeAct / MemAct)
Agent patterns are expressed as runtime workflows with strict namespaces and tool gating.  
Source: `abstractagent/docs/architecture.md`

Key design:
- Logic is runtime-agnostic; adapters wire to AbstractRuntime.
- Tool allowlists are stored in `_runtime.allowed_tools`.
- CodeAct supports fenced python execution as an explicit tool.

### AbstractFlow (VisualFlow + bundles)
AbstractFlow is the authoring layer for VisualFlow workflows and bundles.  
Source: `abstractflow/docs/architecture.md`

Key design:
- VisualFlow JSON is portable and executes in any host.
- `.flow` bundles package VisualFlow graphs + manifest + subflows.
- Session runners support event-driven flows (`on_event` → child workflows).

## Control Plane

### AbstractGateway (durable run gateway)
The gateway provides HTTP/SSE APIs for run control, ledger replay, scheduling, and security.  
Source: `abstractgateway/docs/architecture.md`

Key design:
- Replay-first ledger streaming (SSE is an optimization).
- Runner loop processes durable commands and ticks runs.
- Split-process deployment supported (API + runner).

## Clients and Hosts

### AbstractCode (CLI/TUI + web thin client)
Local CLI is a **host** (runs runtime + tools locally).  
Web app is a **thin client** that talks to the gateway.  
Source: `abstractcode/docs/architecture.md`

### AbstractAssistant (local host)
Tray UI runs AbstractAgent + AbstractRuntime locally; tools executed after approval.  
Source: `abstractassistant/docs/architecture.md`

### AbstractObserver (gateway-only UI)
UI renders by ledger replay + SSE; submits durable commands to gateway.  
Source: `abstractobserver/docs/architecture.md`

### SmartNote (thin client + durable backend)
SmartNote server runs chunked ingestion workflows and writes KG assertions.  
Source: `smartnote/docs/architecture.md`

## Memory and Semantics

### AbstractMemory (temporal triple store)
Stores append-only, provenance-aware triples with deterministic query semantics.  
Source: `abstractmemory/docs/architecture.md`

### AbstractSemantics (registry + schema)
Provides registry of predicates/types + JSON schema builder for KG assertions.  
Source: `abstractsemantics/docs/architecture.md`

## Observability and Evidence
Evidence is an explicit concept: ledger + artifacts + snapshots + history bundles.  
Sources: `docs/architecture.md`, `abstractruntime/docs/architecture.md`

## Implications for Skill Integration (Constraints)
These constraints must be respected by any skills proposal:
- **Durability**: skills should not rely on ephemeral in-memory state.
- **Tool boundary**: tools must remain host-executed with durable waits.
- **Replay-first**: skill activation and outputs should be ledger-recorded.
- **No silent fallbacks**: degraded paths must emit `#FALLBACK` warnings (project policy).

## References (Architecture Docs Used)
- `docs/architecture.md`
- `abstractcore/docs/architecture.md`
- `abstractruntime/docs/architecture.md`
- `abstractagent/docs/architecture.md`
- `abstractflow/docs/architecture.md`
- `abstractgateway/docs/architecture.md`
- `abstractcode/docs/architecture.md`
- `abstractassistant/docs/architecture.md`
- `abstractobserver/docs/architecture.md`
- `abstractmemory/docs/architecture.md`
- `abstractsemantics/docs/architecture.md`
- `smartnote/docs/architecture.md`

--- docs/claude/README.md ---
# Claude research notes (versioned)

This folder contains **versioned research notes** about Claude capabilities/skills (distinct from the shareable Agent
Skills `SKILL.md` ecosystem).

Related:
- Agent Skills (shareable `SKILL.md`) notes live in `docs/skills/`
- Agent Skills integration proposal: `docs/guide/agent-skills.md`

Files:
- `docs/claude/claude-skills-overview.md`
- `docs/claude/claude-skills-top-20.md`
- `docs/claude/claude-skills-sources.md`
- `docs/claude/abstractframework-fit.md`


--- docs/claude/claude-skills-overview.md ---
# Claude Skills Research Overview (Feb 2026)

## Definition
A "Claude skill" here means any publicly documented capability of Claude models or the Claude
developer platform (API + tools + claude.ai features) that enables users to perform a class of
tasks. This includes model-level capabilities (reasoning, vision, long-context) and platform
capabilities (tool use, web search/fetch, computer use, artifacts, memory).

## Sources And Scope
This overview is based on Anthropic’s public documentation and product announcements available
as of February 2026. See `untracked/skills/claude-skills-sources.md` for the full list of sources.

Key references include:
- “Building with Claude” (capabilities overview, tool use, vision, long context)  
  https://docs.anthropic.com/claude/docs/overview
- Tool use docs (client tools, server tools, strict tool use, MCP)  
  https://docs.anthropic.com/claude/docs/agents-and-tools/tool-use/overview
- Computer use tool (desktop automation, agent loop, thinking param)  
  https://docs.anthropic.com/claude/docs/agents-and-tools/tool-use/computer-use-tool
- Web search tool (citations, dynamic filtering with code execution)  
  https://docs.anthropic.com/claude/docs/agents-and-tools/tool-use/web-search-tool
- Web fetch tool (page/PDF fetch, citations, dynamic filtering)  
  https://docs.anthropic.com/claude/docs/agents-and-tools/tool-use/web-fetch-tool
- Claude model announcements (reasoning, vision, long context, artifacts, memory)  
  https://www.anthropic.com/news/claude-3-family  
  https://www.anthropic.com/news/claude-3-5-sonnet  
  https://www.anthropic.com/news/claude-sonnet-4-6

## How Claude Skills Work (High-Level)
1. **Model capabilities**  
   Claude’s core skills (reasoning, writing, coding, multilingual, vision) are model-level
   capabilities described in the model announcements and overview docs. These are accessed
   through the standard Messages API.  
   References: https://docs.anthropic.com/claude/docs/overview,  
   https://www.anthropic.com/news/claude-3-family

2. **Tool use (function calling)**  
   Claude can emit structured tool calls based on a schema you provide. The API returns
   `tool_use` blocks, your app executes them, then sends `tool_result` blocks back. This
   enables deterministic integration with external systems.  
   Reference: https://docs.anthropic.com/claude/docs/agents-and-tools/tool-use/overview

3. **Client tools vs server tools**  
   - **Client tools** run on your infrastructure (custom tools, computer use, bash, text editor).  
   - **Server tools** run on Anthropic’s infrastructure (web search, web fetch), with results
     embedded back into Claude’s response.  
   Reference: https://docs.anthropic.com/claude/docs/agents-and-tools/tool-use/overview

4. **Agent loop for multi-step tasks**  
   Claude can repeatedly request tool actions and consume results until a task completes.
   This “agent loop” is central to computer use and multi-step automation.  
   Reference: https://docs.anthropic.com/claude/docs/agents-and-tools/tool-use/computer-use-tool

5. **Server-side grounding and citations**  
   Web search and fetch tools provide citations, and can dynamically filter content using
   code execution to reduce token usage and improve relevance.  
   References:  
   https://docs.anthropic.com/claude/docs/agents-and-tools/tool-use/web-search-tool  
   https://docs.anthropic.com/claude/docs/agents-and-tools/tool-use/web-fetch-tool

6. **Reasoning transparency and long-horizon context**  
   Some models support “thinking” parameters (a dedicated reasoning budget) and context
   compaction to extend effective context for long-running tasks.  
   References:  
   https://docs.anthropic.com/claude/docs/agents-and-tools/tool-use/computer-use-tool  
   https://www.anthropic.com/news/claude-sonnet-4-6

7. **Product-layer skills (claude.ai)**  
   Features like Artifacts and Memory are positioned as collaboration and personalization
   features inside the Claude product experience.  
   References:  
   https://www.anthropic.com/news/claude-3-5-sonnet  
   https://www.anthropic.com/news/claude-sonnet-4-6

## Interpretation Notes
- Some skills are **model capabilities** (intrinsic) while others are **platform features**
  (API tools or claude.ai UX). This report distinguishes them explicitly.
- Several features are in **beta** (e.g., computer use) and carry additional security and
  privacy constraints documented by Anthropic.
- Where a feature is announced but not fully specified (e.g., Memory in early announcements),
  this report documents only what is explicitly stated by Anthropic.


--- docs/claude/claude-skills-top-20.md ---
# Claude Top 20 Skills (As Of Feb 2026)

This list curates the most impactful Claude skills based on Anthropic documentation and
announcements. Each entry includes what the skill is, how it works (high-level), and evidence.

## 1) Text Generation And Rewriting
- **What it is:** High-quality text creation (summaries, reports, support replies, creative writing).
- **How it works:** Standard Messages API prompts; model generates fluent text with instruction
  following and brand-voice control.
- **Evidence:** https://docs.anthropic.com/claude/docs/overview

## 2) Code Generation, Debugging, And Translation
- **What it is:** Write, edit, debug, and translate code across languages and codebases.
- **How it works:** Natural-language prompts + code context; often paired with tools for edits or execution.
- **Evidence:** https://www.anthropic.com/news/claude-3-family,  
  https://www.anthropic.com/news/claude-3-5-sonnet

## 3) Advanced Reasoning And Analysis
- **What it is:** Strong performance on reasoning, math, forecasting, and multi-step analysis.
- **How it works:** Model-level capability expressed through instruction-following and structured prompts.
- **Evidence:** https://docs.anthropic.com/claude/docs/overview,  
  https://www.anthropic.com/news/claude-3-family

## 4) Multilingual Fluency And Translation
- **What it is:** Accurate responses and translations across non-English languages.
- **How it works:** Same generation pipeline with multilingual training; usable for translation features.
- **Evidence:** https://docs.anthropic.com/claude/docs/overview,  
  https://www.anthropic.com/news/claude-3-family

## 5) Vision Understanding (Images, Charts, Diagrams)
- **What it is:** Analyze visual inputs like charts, photos, diagrams, and scanned documents.
- **How it works:** Multimodal inputs (image + text) processed by vision-capable Claude models.
- **Evidence:** https://docs.anthropic.com/claude/docs/overview,  
  https://www.anthropic.com/news/claude-3-family,  
  https://www.anthropic.com/news/claude-3-5-sonnet

## 6) Long-Context Processing (200K To 1M Tokens)
- **What it is:** Read and reason over very large document sets in a single request.
- **How it works:** Large context windows in Claude models; Sonnet 4.6 adds a 1M token context in beta.
- **Evidence:** https://www.anthropic.com/news/claude-3-family,  
  https://www.anthropic.com/news/claude-sonnet-4-6

## 7) Tool Use / Function Calling (Client Tools)
- **What it is:** Structured tool invocation to integrate with external systems.
- **How it works:** Provide tool schemas; Claude emits `tool_use` blocks; app executes and returns
  `tool_result` blocks.
- **Evidence:** https://docs.anthropic.com/claude/docs/agents-and-tools/tool-use/overview

## 8) Strict Tool Use / Structured Outputs
- **What it is:** Schema-validated tool inputs for robust production integrations.
- **How it works:** Tool definitions can be marked `strict: true` to guarantee schema conformance.
- **Evidence:** https://docs.anthropic.com/claude/docs/agents-and-tools/tool-use/overview

## 9) Parallel Tool Use
- **What it is:** Multiple independent tool calls in a single model response.
- **How it works:** Claude returns multiple `tool_use` blocks; client returns matching results in one turn.
- **Evidence:** https://docs.anthropic.com/claude/docs/agents-and-tools/tool-use/overview

## 10) MCP Tool Integration And Connectors
- **What it is:** Use tools from MCP servers and connectors without custom adapters.
- **How it works:** MCP tool schemas are converted to Claude tool schemas; connectors can be used
  in Claude.ai and related products.
- **Evidence:** https://docs.anthropic.com/claude/docs/agents-and-tools/tool-use/overview,  
  https://www.anthropic.com/news/claude-sonnet-4-6

## 11) Web Search Tool (Grounded Research + Citations)
- **What it is:** Server-side web search with citations in responses.
- **How it works:** Claude triggers `web_search` server tool; results are injected with citations.
  Newer versions add dynamic filtering with code execution.
- **Evidence:** https://docs.anthropic.com/claude/docs/agents-and-tools/tool-use/web-search-tool

## 12) Web Fetch Tool (Full Page/PDF Retrieval)
- **What it is:** Fetch and analyze full web pages or PDFs with optional citations.
- **How it works:** Claude calls `web_fetch`; the server retrieves content, optionally filters
  it, and returns it to the model.
- **Evidence:** https://docs.anthropic.com/claude/docs/agents-and-tools/tool-use/web-fetch-tool

## 13) Computer Use (Desktop Automation)
- **What it is:** Operate a desktop environment via screenshots, mouse, and keyboard actions.
- **How it works:** A specialized computer-use tool emits action requests (click, type, screenshot);
  the client executes them in a sandboxed environment and returns results.
- **Evidence:** https://docs.anthropic.com/claude/docs/agents-and-tools/tool-use/computer-use-tool

## 14) Multi-Step Agent Looping
- **What it is:** Iterative tool calling until a task is completed.
- **How it works:** Claude alternates between tool requests and tool results in a loop
  (“agent loop”) across multiple turns.
- **Evidence:** https://docs.anthropic.com/claude/docs/agents-and-tools/tool-use/computer-use-tool,  
  https://docs.anthropic.com/claude/docs/agents-and-tools/tool-use/overview

## 15) Thinking / Extended Thinking (Reasoning Budget)
- **What it is:** A controllable reasoning budget and optional visibility into the model’s
  reasoning process.
- **How it works:** A `thinking` parameter with a token budget is provided; the model returns
  reasoning traces and uses those tokens for deeper deliberation.
- **Evidence:** https://docs.anthropic.com/claude/docs/agents-and-tools/tool-use/computer-use-tool,  
  https://www.anthropic.com/news/claude-sonnet-4-6

## 16) Prompt Caching
- **What it is:** Reuse cached context for faster, cheaper follow-ups.
- **How it works:** Clients add `cache_control` breakpoints; cached tool results and context are
  reused in subsequent requests.
- **Evidence:** https://docs.anthropic.com/claude/docs/agents-and-tools/tool-use/web-search-tool,  
  https://docs.anthropic.com/claude/docs/agents-and-tools/tool-use/web-fetch-tool

## 17) Context Compaction
- **What it is:** Automatic summarization of older context to extend effective context length.
- **How it works:** As conversations approach limits, older context is compacted to preserve
  key information while freeing tokens.
- **Evidence:** https://www.anthropic.com/news/claude-sonnet-4-6

## 18) Artifacts (Interactive Workspace In Claude.ai)
- **What it is:** A dedicated workspace for generated code/documents alongside chat.
- **How it works:** Claude.ai renders outputs in a separate pane for editing and iterative work.
- **Evidence:** https://www.anthropic.com/news/claude-3-5-sonnet

## 19) Memory (Personalization / Long-Term Preferences)
- **What it is:** Persist user preferences and interaction history across sessions.
- **How it works:** Documented as a product feature; later announcements reference a memory tool.
- **Evidence:** https://www.anthropic.com/news/claude-3-5-sonnet,  
  https://www.anthropic.com/news/claude-sonnet-4-6

## 20) Safety And Prompt-Injection Defenses
- **What it is:** Guardrails to resist jailbreaks and prompt injection, especially in tool use.
- **How it works:** Model training plus classifier defenses; computer-use adds extra checks and
  may request user confirmation on risky steps.
- **Evidence:** https://docs.anthropic.com/claude/docs/overview,  
  https://docs.anthropic.com/claude/docs/agents-and-tools/tool-use/computer-use-tool


--- docs/claude/claude-skills-sources.md ---
# Claude Skills Sources (Accessed Feb 2026)

## Anthropic Documentation
- **Building with Claude (overview of capabilities)**  
  https://docs.anthropic.com/claude/docs/overview  
  Focus: high-level capabilities (text, code, vision, tool use), trust/safety positioning.

- **Tool use overview (client tools, server tools, strict tool use, MCP)**  
  https://docs.anthropic.com/claude/docs/agents-and-tools/tool-use/overview  
  Focus: tool schemas, tool use flow, strict tool use, MCP integration.

- **Computer use tool (desktop automation + agent loop + thinking)**  
  https://docs.anthropic.com/claude/docs/agents-and-tools/tool-use/computer-use-tool  
  Focus: computer-use tool actions, agent loop, security considerations, thinking parameter.

- **Web search tool (citations + dynamic filtering)**  
  https://docs.anthropic.com/claude/docs/agents-and-tools/tool-use/web-search-tool  
  Focus: server-side search, citations, dynamic filtering with code execution, prompt caching.

- **Web fetch tool (page/PDF retrieval + citations)**  
  https://docs.anthropic.com/claude/docs/agents-and-tools/tool-use/web-fetch-tool  
  Focus: server-side content retrieval, PDF support, citations, dynamic filtering, security.

## Anthropic Announcements
- **Claude 3 family (reasoning, vision, long context, structured output)**  
  https://www.anthropic.com/news/claude-3-family

- **Claude 3.5 Sonnet (agentic coding, vision, Artifacts, Memory direction)**  
  https://www.anthropic.com/news/claude-3-5-sonnet

- **Claude Sonnet 4.6 (1M context, computer use improvements, extended thinking, compaction)**  
  https://www.anthropic.com/news/claude-sonnet-4-6

## Notes
- Sources above are the only basis for claims in `claude-skills-overview.md` and
  `claude-skills-top-20.md`. No non-Anthropic sources were used.
- Several features are labeled “beta” in the docs; these are treated as optional capabilities.


--- docs/claude/abstractframework-fit.md ---
# AbstractFramework Fit Analysis For Claude Skills (Feb 2026)

## Current AbstractFramework Capabilities (Relevant Snapshot)
Based on `README.md` and `docs/architecture.md`, the framework already provides:
- **Universal tool calling + MCP** via AbstractCore (single tool schema across providers).
- **Structured output** and **streaming/async** across providers.
- **Durable execution and auditability** via AbstractRuntime’s ledger and artifacts.
- **Memory system** via AbstractMemory + AbstractSemantics.
- **Chat compaction** in AbstractCore `BasicSession` for long conversations.
- **Multimodal input** with policy-driven fallbacks.

These align well with Claude’s API-first skills. The main gaps are provider-specific
capabilities that are not yet explicitly integrated (e.g., Anthropic server tools and
computer-use tool).

## Where Claude Skills Would Benefit AbstractFramework (Why + How)

### 1) Web Search Tool (Server Tool + Citations)
- **Why:** AbstractFramework already supports “deep search” use cases, but Claude’s server-side
  web search provides built-in citations and dynamic filtering for more reliable research results.
- **How:** Add an Anthropic-specific tool adapter in AbstractCore that registers `web_search`
  server tools. Store citations as artifacts linked in the ledger. Expose allowlist/denylist and
  `max_uses` in config. If disabled, emit `#FALLBACK` warnings and do not silently simulate search.
- **Evidence:** Claude web search tool docs show citations and dynamic filtering.

### 2) Web Fetch Tool (Pages + PDFs With Optional Citations)
- **Why:** AbstractFramework workflows often require document ingestion; web fetch adds a direct,
  cited path to pull full pages or PDFs with guardrails.
- **How:** Implement a server-tool mapping for `web_fetch` with strict domain controls, and
  enforce `max_content_tokens`. If truncation occurs, label `#TRUNCATION` in the ledger.
- **Evidence:** Claude web fetch docs describe PDF support, citations, and content limits.

### 3) Dynamic Filtering Via Code Execution (Server Tool Optimization)
- **Why:** Long document retrieval can be expensive. Claude’s dynamic filtering reduces token
  costs and improves relevance.
- **How:** When `web_search_20260209` or `web_fetch_20260209` is enabled, require a code-execution
  tool and sandbox it. Log filtering decisions and parameters in the ledger for auditability.
- **Evidence:** Web search/fetch docs note dynamic filtering and dependency on code execution.

### 4) Computer Use Tool (GUI Automation)
- **Why:** AbstractFramework already targets durable automation. Computer use enables tasks
  against legacy GUIs where APIs don’t exist.
- **How:** Add a computer-use tool integration as an optional capability with explicit user
  approval gates. Run in a sandboxed VM/container; store screenshots and action logs as artifacts.
  Enforce allowlisted domains and add prompt-injection warnings (`#FALLBACK` if disabled).
- **Evidence:** Computer use docs specify agent loops, action schemas, and safety constraints.

### 5) Thinking / Extended Thinking Controls
- **Why:** Many AbstractFramework flows (planning, code review, audits) benefit from controllable
  reasoning budgets to increase reliability.
- **How:** Extend AbstractCore provider settings to accept Anthropic `thinking` parameters and
  expose them in Flow node configs. Record the chosen budget in the ledger for reproducibility.
- **Evidence:** Computer use docs and Sonnet 4.6 announcement describe thinking modes.

### 6) Prompt Caching For Long-Running Workflows
- **Why:** Durable workflows often re-use large context; caching can reduce cost and latency.
- **How:** Add a session-level option to emit `cache_control` breakpoints in Anthropic requests.
  Record cache hit/miss metrics in the ledger when exposed by the API.
- **Evidence:** Web search/fetch docs describe cache_control usage for prompt caching.

### 7) Context Compaction Alignment
- **Why:** AbstractCore already has compaction; Claude’s platform compaction can further extend
  effective context. Coordinating the two avoids redundant summarization.
- **How:** Make Claude context compaction optional and mutually exclusive with BasicSession
  auto-compaction for a given run; emit `#FALLBACK` warnings if forced to downgrade.
- **Evidence:** Sonnet 4.6 announcement references context compaction.

### 8) Artifacts-Like Workspace In AbstractObserver / Code Web
- **Why:** Claude Artifacts demonstrate strong UX for code/document iteration. AbstractObserver
  and Code Web could surface ledger artifacts in a side pane to match this workflow.
- **How:** Add an “artifact workspace” UI surface for generated code/docs with inline editing,
  backed by ledger artifacts. Keep edits as new artifacts for full provenance.
- **Evidence:** Claude 3.5 Sonnet announcement describes Artifacts as a dedicated workspace.

### 9) Memory Tool Integration (Optional)
- **Why:** Claude Memory is positioned for preference retention; AbstractFramework already has
  a richer memory system. A thin integration could provide model-side personalization when desired.
- **How:** Map user preferences stored in AbstractMemory into Claude’s memory tool where available.
  Require explicit user consent and provide opt-in controls. Never silently sync.
- **Evidence:** Claude 3.5 Sonnet and Sonnet 4.6 announcements reference memory features.

### 10) Citation-Aware Outputs In Ledger And UIs
- **Why:** Claude server tools produce citations; surfacing them makes AbstractFramework’s
  auditability even stronger.
- **How:** Persist citations alongside tool results and render them in AbstractObserver. Ensure
  citations are preserved when outputs are post-processed.
- **Evidence:** Web search and web fetch docs describe citation structures.

## Skills That Are Already Covered (Low Incremental Value)
- **Text generation, reasoning, multilingual, vision, long context** are model capabilities
  already accessible via AbstractCore by selecting Claude models.
- **Tool calling, structured output, streaming** are already part of AbstractCore’s core
  abstraction; Claude can be plugged in without new architecture.
- **MCP integration** already exists in AbstractCore; Claude’s MCP support is complementary.

## Risks And Guardrails (Align With Framework Principles)
- **Prompt injection & unsafe automation:** Computer use and web tools require allowlists,
  human approval gates, and explicit warnings (`#FALLBACK` when safety constraints disable tools).
- **Truncation risk:** Web fetch and large content limits must tag `#TRUNCATION` and preserve
  provenance in the ledger.
- **Privacy:** Server tools are not always ZDR; the system should surface privacy mode in UI and logs.

## ADR Suggestion (Crucial Architecture Decision)
Create an ADR for “Anthropic Server Tools + Computer Use Integration” covering:
- Security model (allowlists, sandboxing, approvals)
- Logging and provenance requirements
- Fallback and truncation policy (`#FALLBACK`, `#TRUNCATION`)
- UX for citations and artifact outputs

