# 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**: none for the one-line install below (it provisions Python and, optionally, Node.js). For a manual install: Python 3.10–3.13, Node.js 18+ for browser UIs, and an LLM backend (Ollama, LM Studio, vLLM, or a cloud API key).

---

## Quick start

The installer sets up the gateway in your user account (no admin password, no system Python),
asks whether to start it at login, starts it on `127.0.0.1:8080`, and opens its web console in
your browser already signed in. A first-run guide then sets up a local engine (Ollama, LM Studio,
MLX, llama.cpp), downloads a model that fits your machine, and lists the apps.

- **Mac, no Terminal:** download and double-click
  [AbstractFramework-Installer.pkg](https://github.com/lpalbou/AbstractFramework/releases/latest/download/AbstractFramework-Installer.pkg).
  A Terminal window shows each step; press Return at its one question.
- **macOS / Linux, one line:**

  ```bash
  curl -LsSf https://raw.githubusercontent.com/lpalbou/AbstractFramework/main/scripts/install.sh | sh -s -- --interactive
  ```

- **Windows 10 22H2+ / 11** (PowerShell):

  ```powershell
  powershell -ExecutionPolicy ByPass -c "irm https://raw.githubusercontent.com/lpalbou/AbstractFramework/main/scripts/install.ps1 | iex"
  ```

Every failure says what to do next; running the installer again repairs or upgrades in place. To
remove it: `Uninstall AbstractFramework.command`, or
`curl -LsSf https://raw.githubusercontent.com/lpalbou/AbstractFramework/main/scripts/uninstall.sh | sh`.
Step by step, the failure table, options (`--with-apps`, `--with-ollama`, `--print`, …):
[Install](docs/install.md).

Already have Python? Either entry point works the same way: start it, then open the link it prints.

```bash
pip install abstractcore && abstractcore serve        # http://127.0.0.1:8000/console#claim=…
pip install abstractgateway && abstractgateway serve  # http://127.0.0.1:8080/console#claim=…
```

Both consoles have **Models** (browse models that fit this machine, download, delete) and
**Engines** (detect and install local engines) tabs; every action also shows its command-line
equivalent (`abstractcore models …`, `abstractcore engines …`, `abstractgateway models …`).

---

## 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 serve` starts the `/v1` server on `127.0.0.1:8000` and prints a one-time link to its
web console (Overview, Models, Engines, Providers). The same Models and Engines screens are
available from the command line (`abstractcore models catalog|list|download|delete`,
`abstractcore engines status|install`) and in the terminal console
(`cargo install abstractcore-console`).

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
abstractgateway serve
```

With no auth configured, `abstractgateway serve` binds `127.0.0.1:8080`, enables user auth,
creates `default/admin` in the per-user data folder, and prints a one-time sign-in link
(`http://127.0.0.1:8080/console#claim=…`, valid 10 minutes, this machine only). Open it to reach the
web console and its first-run guide. `abstractgateway claim` mints a new link;
`abstractgateway service install` starts the gateway at login.

To choose the data folder, the allowed browser origins or your own workflow bundles, set the
environment explicitly:

```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"
# export ABSTRACTGATEWAY_FLOWS_DIR="$PWD/bundles"   # serve your own bundle registry

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

Out of the box this serves a ready set of workflows — a verify-gated coding
agent, `deep-research`, and `co-scientist` among them. See
[shipped workflows](abstractgateway/docs/shipped-workflows.md).

The `admin` user token is kept in `<data dir>/auth/bootstrap-admin-token`; use it to sign in to
AbstractFlow, AbstractCode Web or AbstractObserver, or to the console without a claim link.
`ABSTRACTGATEWAY_AUTH_TOKEN` remains a legacy server/operator bearer token; it is not a browser
sign-in token.

Monitor runs from a browser, or from a terminal with the gateway console:

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

cargo install abstractgateway-console   # Rust 1.87+
ABSTRACTGATEWAY_AUTH_TOKEN=<token> abstractgateway-console --url http://127.0.0.1:8080
```

Container images are published for the gateway and the AbstractCore server:
`ghcr.io/lpalbou/abstractgateway:0.4.2` and `ghcr.io/lpalbou/abstractcore-server:2.15.1`.

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 into your own bundle directory and point `ABSTRACTGATEWAY_FLOWS_DIR` at it (or publish it through the Gateway API)
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. The workflows Gateway ships with are authored the same way — their editable sources are documented in [shipped workflow sources](abstractflow/docs/shipped-workflow-sources.md).

---

## 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

---

## Package map

The ecosystem, grouped by layer. Each name links to the package's own README.

### Foundation

| Package | What it is |
|---|---|
| [abstractcore](abstractcore/) | Unified LLM interface: 9+ providers, tools, structured output, media, embeddings, `/v1` server, capability plugins |
| [abstractsemantics](abstractsemantics/) | Central semantics registry (predicates + entity types) with JSON-Schema helpers |
| [abstractmemory](abstractmemory/) | Durable, append-only agent memory: usage-weighted graph + journal — recall, formation, consolidation (the entity mind engine) |

### Durable execution

| Package | What it is |
|---|---|
| [abstractruntime](abstractruntime/) | Durable execution kernel: runs, effects, waits, append-only ledger, artifacts; the VisualFlow compiler (visual graphs → executable workflows); the entity identity lane (homes, chat/life/visit drivers) |
| [abstractagent](abstractagent/) | Agent patterns (ReAct / CodeAct / MemAct) composing Runtime + Core |
| [abstractflow](abstractflow/) | Visual workflow editor + portable `.flow` bundles — author once, run anywhere |

### Control plane

| Package | What it is |
|---|---|
| [abstractgateway](abstractgateway/) | Deployable control plane: durable runs over HTTP/SSE, scheduling + run commands (cancel/steer), workflow catalog, artifact/ledger serving, multi-user auth with per-user runtimes, the summoned-entity lifecycle (create / summon / visit / state / blueprint), and the operator consoles (web + TUI) |

### Multimodal capabilities

| Package | What it is |
|---|---|
| [abstractvoice](abstractvoice/) | Voice I/O (TTS / STT), local and remote backends |
| [abstractvision](abstractvision/) | Model-agnostic generative vision (images, optional video) |
| [abstractmusic](abstractmusic/) | Text-to-music / text-to-audio (Core capability plugin) |
| [abstract3d](abstract3d/) | Local-first 3D generation |
| [abstractcamera](abstractcamera/) | Camera control and capture tools |
| [abstractsound](abstractsound/), [abstractvideo](abstractvideo/), [abstractspatial](abstractspatial/), [abstractgeometry](abstractgeometry/), [abstractcognition](abstractcognition/) | Reserved capability packages (namespaces held; APIs landing incrementally) |

### Apps and clients

| App | What it does | Install |
|---|---|---|
| [AbstractCode](abstractcode/) | Terminal agentic dev client (Rust, on the AbstractTUI engine) — durable sessions, tool approvals, `/workflow` support | `cargo install abstractcode`, or a prebuilt binary from the [GitHub release](https://github.com/lpalbou/AbstractCode/releases) |
| [AbstractAssistant](abstractassistant/) | macOS tray client — gateway-native, workflow picker per session, voice support | `pip install abstractassistant` |
| [AbstractObserver](abstractobserver/) | Browser UI — monitor, control, and schedule gateway runs | `npx @abstractframework/observer` |
| [AbstractEntity](abstractentity/) | Summoned-entity manager — roster, blueprint (cognition map + editing), chat drawer, live replay | `npx @abstractframework/entity` |
| [AbstractContinuum](abstractcontinuum/) | Continuous iterative development and deployment console | `npx @abstractframework/continuum` |
| **Gateway consoles** | Operator consoles for a running gateway: web at `/console` (first-run guide, Models, Engines, providers, users), terminal via `abstractgateway-console` | built into `abstractgateway`; `cargo install abstractgateway-console` |
| **Core consoles** | Consoles for AbstractCore: web at `/console` of `abstractcore serve`, terminal via `abstractcore-console` (config, Models, Engines) | built into `abstractcore`; `cargo install abstractcore-console` |
| **Code Web UI** | Browser client of AbstractCode (gateway-backed) | `npx @abstractframework/code` |
| **Flow Editor** | Visual workflow authoring in the browser | `npx @abstractframework/flow` |

### Shared libraries

| Package | What it is |
|---|---|
| [abstracttui](abstracttui/) | Rust terminal-UI engine built on fine-grained reactive signals |
| [abstractuic](abstractuic/) | Reusable UI kit for framework clients (React components + Web Components) |
| [abstractskill](abstractskill/) | Shared library for Agent Skills (`SKILL.md` folders: load, trust-gate, activate) |

---

## 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]"
```

| Profile | Command | Platforms | Python |
|---|---|---|---|
| Light | `pip install abstractframework` | macOS, Linux, Windows | 3.10–3.13 |
| Apple | `pip install "abstractframework[apple]"` | macOS 14+ on Apple Silicon | 3.10–3.13 (F5-TTS voice cloning needs 3.11+) |
| GPU | `pip install "abstractframework[gpu]"` | Linux / Windows with a CUDA or ROCm GPU | 3.10–3.13 (F5-TTS voice cloning needs 3.11+) |

### Release matrix (abstractframework 0.3.1)

`abstractframework` pins every Python package with `==`, so one version of the
meta-package always installs the same stack. The browser apps and Rust tools are
distributed through npm and crates.io; the versions below are the ones released
and tested together.

| Registry | Package | Version |
|---|---|---|
| PyPI | `abstractgateway` | 0.4.2 |
| PyPI | `abstractassistant` | 0.5.0 |
| PyPI | `abstractcore` | 2.15.1 |
| PyPI | `AbstractRuntime` | 0.4.34 |
| PyPI | `abstractagent` | 0.3.13 |
| PyPI | `AbstractMemory` | 0.3.0 |
| PyPI | `abstractsemantics` | 0.0.5 |
| PyPI | `abstractvoice` | 0.11.4 |
| PyPI | `abstractvision` | 0.3.29 |
| PyPI | `abstractmusic` | 0.1.15 |
| npm | `@abstractframework/flow` | 0.3.20 |
| npm | `@abstractframework/code` | 0.4.2 |
| npm | `@abstractframework/observer` | 0.1.12 |
| npm | `@abstractframework/continuum` | 0.3.0 |
| npm | `@abstractframework/entity` | 0.2.0 |
| crates.io | `abstractcode` | 0.5.1 |
| crates.io | `abstractgateway-console` | 0.8.0 |
| crates.io | `abstractcore-console` | 0.2.0 |
| crates.io | `abstracttui` | 0.6.0 |
| GHCR | `ghcr.io/lpalbou/abstractgateway` | 0.4.2 (`gpu-latest` / `<version>-gpu` experimental) |
| GHCR | `ghcr.io/lpalbou/abstractcore-server` | 2.15.1 |

Optional add-ons that are not part of any profile install separately:
`pip install abstract3d` (0.3.1), `pip install abstractcamera` (0.2.0) and
`pip install abstractskill` (0.2.1).

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) |
| [docs/workspace-scripts.md](docs/workspace-scripts.md) | Working from source: package tiers, build, status, pull/commit/push scripts |

---

## Developer setup (from source)

Clone all sibling repos and build everything in editable mode:

```bash
./scripts/clone.sh           # clone every sibling repository next to this one
./scripts/deps.sh            # dependency tiers: what builds and installs first, and why
source ./scripts/build.sh    # Python (editable, into .venv), npm and Rust builds, tier by tier
```

Keep the whole workspace in sync with `./scripts/status.sh` (git overview per tier; `--registry`
compares local versions with PyPI, npm and crates.io), `./scripts/pull.sh`, `./scripts/commit.sh`
and `./scripts/push.sh` (a dry run until you add `--yes`). See
[docs/workspace-scripts.md](docs/workspace-scripts.md) for every script and option.

Then configure providers and models in a console (`abstractcore serve` or
`abstractgateway serve`, then open the printed link), or from the terminal:

```bash
abstractcore --config    # interactive configuration wizard
abstractcore --install   # check every subsystem and download missing models and dependencies
```

---

## 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).
AbstractCode is a Rust terminal client (`cargo install abstractcode`) plus a browser client (`npx @abstractframework/code`); both need a running gateway.

Quick commands:
- One-line install (gateway + console, no admin, no system Python): `curl -LsSf https://raw.githubusercontent.com/lpalbou/AbstractFramework/main/scripts/install.sh | sh` (Windows: `powershell -ExecutionPolicy ByPass -c "irm https://raw.githubusercontent.com/lpalbou/AbstractFramework/main/scripts/install.ps1 | iex"`); installs uv + Python 3.12 + the pinned gateway as a uv tool, registers the login service, starts it on 127.0.0.1:8080 and opens `/console` through a one-time claim link; `--print` dry run, `--uninstall` to remove
- Start either entry point and open the printed one-time link: `abstractcore serve` (http://127.0.0.1:8000/console#claim=…) or `abstractgateway serve` (http://127.0.0.1:8080/console#claim=…); both consoles have Models (catalog + fit verdict, download, delete) and Engines (detect, install) tabs, mirrored by `abstractcore models|engines` and `abstractgateway models|engines`
- 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.
- A packaged Gateway serves shipped workflow bundles out of the box, with no
  manual install: `basic-agent` (default chat agent), `coding-agent` (a
  verify-gated coder, `coder` entrypoint), `deep-research`, `co-scientist`,
  `docs-qa`, `abstractassistant-orchestrator`, and the `react-agent` /
  `codeact-agent` / `memact-agent` native loops. Setting
  `ABSTRACTGATEWAY_FLOWS_DIR` replaces that registry with a custom one.

## 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): One-line bootstrap (install.sh / install.ps1: what it does, options, CLI equivalents, run at login, uninstall), Light / Apple / GPU chooser, generated manifest contract.
- [docs/installers/README.md](docs/installers/README.md): Install design: script bootstrap + gateway console (strategy, per-OS journeys, components, OS security/sudo/UAC/execution policy, manifest, operations).
- [docs/getting-started.md](docs/getting-started.md): Practical entry paths (core, runtime, gateway, UIs, bundles).
- [abstractgateway/docs/shipped-workflows.md](abstractgateway/docs/shipped-workflows.md): Workflows a fresh Gateway install already serves (coder, deep research, co-scientist) + how to run one.
- [docs/architecture.md](docs/architecture.md): How the stack fits together.
- [docs/glossary.md](docs/glossary.md): Shared terminology used across docs.
- [docs/workspace-scripts.md](docs/workspace-scripts.md): Working from source: package inventory (scripts/lib/packages.txt), dependency tiers, build/status/pull/commit/push scripts.
- [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`, `NPM_RELEASE_VERSIONS`, `get_release_profile()`, `print_status()`.
- [docs/installers/install-manifest.json](docs/installers/install-manifest.json): Generated install manifest, schema v2 (pins, profiles, npm apps, `bootstrap` gateway pin/scripts/flags, console-first `post_install`).
- [scripts/install.sh](scripts/install.sh) / [scripts/install.ps1](scripts/install.ps1): The bootstrap installers.
- abstractframework 0.3.1 pins (PyPI, `==`): abstractgateway 0.4.2, abstractassistant 0.5.0, abstractcore 2.15.1, AbstractRuntime 0.4.34, abstractagent 0.3.13, AbstractMemory 0.3.0, abstractsemantics 0.0.5, abstractvoice 0.11.4, abstractvision 0.3.29, abstractmusic 0.1.15.
- Released with it: npm `@abstractframework/flow` 0.3.20, `code` 0.4.2, `observer` 0.1.12, `continuum` 0.3.0, `entity` 0.2.0 (run with `npx`); crates (`CRATE_RELEASE_VERSIONS`) `abstractgateway-console` 0.8.0, `abstractcore-console` 0.2.0, `abstractcode` 0.5.1, `abstracttui` 0.6.0; images `ghcr.io/lpalbou/abstractgateway:0.4.2`, `ghcr.io/lpalbou/abstractcore-server:2.15.1`.
- Profiles: light (remote-only), apple (`abstractframework[apple]`, macOS 14+ Apple Silicon), gpu (`abstractframework[gpu]`); Python 3.10-3.13 (F5-TTS voice cloning in apple/gpu needs 3.11+).

## 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.3.1"
description = "Unified installer and documentation hub for the AbstractFramework ecosystem"
readme = "README.md"
license = {text = "MIT"}
authors = [
    {name = "Laurent-Philippe Albou", email = "contact@abstractframework.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",
    "Programming Language :: Python :: 3.13",
    "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.4.2",

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

    # Pin the core Python stack for release determinism (also pulled transitively).
    "abstractcore==2.15.1",
    "AbstractRuntime==0.4.34",
    "abstractagent==0.3.13",
    "AbstractMemory==0.3.0",
    "abstractsemantics==0.0.5",

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

[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.4.2",
    "abstractassistant[apple]==0.5.0; platform_system == \"Darwin\"",
]

gpu = [
    "abstractgateway[gpu]==0.4.2",
    "abstractassistant[gpu]==0.5.0",
]

[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.3.1"
__author__ = "Laurent-Philippe Albou"
__license__ = "MIT"

RELEASE_VERSIONS: dict[str, str] = {
    "abstractcore": "2.15.1",
    "abstractruntime": "0.4.34",
    "abstractagent": "0.3.13",
    "abstractgateway": "0.4.2",
    "abstractmemory": "0.3.0",
    "abstractsemantics": "0.0.5",
    "abstractvoice": "0.11.4",
    "abstractvision": "0.3.29",
    "abstractmusic": "0.1.15",
    "abstractassistant": "0.5.0",
}

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

NPM_RELEASE_VERSIONS: dict[str, str] = {
    "@abstractframework/flow": "0.3.20",
    "@abstractframework/code": "0.4.2",
    "@abstractframework/observer": "0.1.12",
    "@abstractframework/continuum": "0.3.0",
    "@abstractframework/entity": "0.2.0",
}

# Terminal tools published on crates.io alongside this release (installed with `cargo install`,
# or by the bootstrap scripts' --with-console / --with-code-cli flags).
CRATE_RELEASE_VERSIONS: dict[str, str] = {
    "abstractgateway-console": "0.8.0",
    "abstractcore-console": "0.2.0",
    "abstractcode": "0.5.1",
    "abstracttui": "0.6.0",
}

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(),
        "crates": CRATE_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 = 2
MINIMUM_INSTALLER_VERSION = "0.2.0"

REPOSITORY = "https://github.com/lpalbou/AbstractFramework"
RAW_SCRIPTS = "https://raw.githubusercontent.com/lpalbou/AbstractFramework/main/scripts"

# Python version the bootstrap provisions through uv. 3.12 satisfies MLX, F5-TTS (3.11+)
# and vLLM (3.10-3.13); it is passed explicitly because the packages have no upper bound.
BOOTSTRAP_PYTHON = "3.12"
DEFAULT_GATEWAY_PORT = 8080

# Flags shared by scripts/install.sh (POSIX) and scripts/install.ps1 (PowerShell).
BOOTSTRAP_FLAGS: list[dict[str, str]] = [
    {"sh": "--profile auto|light|apple|gpu", "ps": "-Profile", "env": "AF_PROFILE",
     "summary": "Install profile; auto picks apple on Apple Silicon macOS 14+, gpu when "
                "nvidia-smi/rocminfo works, light otherwise."},
    {"sh": "--port N", "ps": "-Port", "env": "AF_PORT",
     "summary": "Gateway port (default 8080; the next free port when 8080 is busy)."},
    {"sh": "--pin VERSION|latest", "ps": "-Pin", "env": "AF_PIN",
     "summary": "abstractgateway version (default: bootstrap.gateway_version)."},
    {"sh": "--from PATH|REQUIREMENT", "ps": "-From", "env": "AF_FROM",
     "summary": "Install the gateway from a checkout, wheel or requirement (testing)."},
    {"sh": "--data-dir DIR", "ps": "-DataDir", "env": "AF_DATA_DIR",
     "summary": "Gateway data dir (default: the per-OS user data dir)."},
    {"sh": "--with-apps", "ps": "-WithApps", "env": "",
     "summary": "Ensure Node.js >= 18 for the npx browser apps (uv tool install nodejs-wheel)."},
    {"sh": "--with-console", "ps": "-WithConsole", "env": "",
     "summary": "cargo install abstractgateway-console when cargo exists."},
    {"sh": "--with-code-cli", "ps": "-WithCodeCli", "env": "",
     "summary": "cargo install abstractcode (terminal client) when cargo exists."},
    {"sh": "--with-core-cli", "ps": "-WithCoreCli", "env": "",
     "summary": "Also expose the abstractcore command (--with-executables-from abstractcore)."},
    {"sh": "--with-ollama", "ps": "-WithOllama", "env": "",
     "summary": "Run Ollama's official installer (Linux/macOS may ask for sudo)."},
    {"sh": "--with-lmstudio", "ps": "-WithLmStudio", "env": "",
     "summary": "Install LM Studio (headless llmster on macOS/Linux, winget on Windows)."},
    {"sh": "--no-tray", "ps": "-NoTray", "env": "", "summary": "Skip the tray extra."},
    {"sh": "--no-service", "ps": "-NoService", "env": "",
     "summary": "Do not register a login service; start the gateway in the background."},
    {"sh": "--no-start", "ps": "-NoStart", "env": "", "summary": "Install only."},
    {"sh": "--no-open", "ps": "-NoOpen", "env": "", "summary": "Do not open the browser."},
    {"sh": "--print", "ps": "-Print", "env": "",
     "summary": "Dry run: preflight, then print every command; change nothing."},
    {"sh": "--uninstall [--purge]", "ps": "-Uninstall [-Purge]", "env": "",
     "summary": "Remove the service and uv tools (purge also deletes the data dir)."},
]


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", "macos>=14", "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(),
        "bootstrap": _bootstrap(),
        "post_install": _post_install(),
        "security": {
            "secrets_in_manifest": False,
            "native_artifacts_signed": False,
            "notes": (
                "The bootstrap is a script (curl | sh, irm | iex) that installs signed-by-vendor "
                "or PyPI/npm artifacts; nothing it runs needs our code signature. Only native "
                "double-click apps (AbstractAssistant .app, future launchers) need signing."
            ),
        },
    }


def _bootstrap() -> dict[str, Any]:
    gateway_version = RELEASE_VERSIONS["abstractgateway"]
    return {
        "gateway_version": gateway_version,
        "python": BOOTSTRAP_PYTHON,
        "tool_requirement": "abstractgateway[{extras}]==" + gateway_version,
        "profile_extras": {"light": [], "apple": ["apple"], "gpu": ["gpu"]},
        "optional_extras": ["tray"],
        "default_port": DEFAULT_GATEWAY_PORT,
        "scripts": {
            "unix": {
                "url": f"{RAW_SCRIPTS}/install.sh",
                "platforms": ["macos", "linux"],
                "one_liner": f"curl -LsSf {RAW_SCRIPTS}/install.sh | sh",
                "with_flags": f"curl -LsSf {RAW_SCRIPTS}/install.sh | sh -s -- --with-apps",
            },
            "windows": {
                "url": f"{RAW_SCRIPTS}/install.ps1",
                "platforms": ["windows"],
                "one_liner": (
                    'powershell -ExecutionPolicy ByPass -c "irm '
                    f'{RAW_SCRIPTS}/install.ps1 | iex"'
                ),
                "with_flags": (
                    "& ([scriptblock]::Create((irm "
                    f"{RAW_SCRIPTS}/install.ps1))) -WithApps"
                ),
            },
        },
        "flags": BOOTSTRAP_FLAGS,
        "steps": [
            "preflight (OS, arch, macOS >= 14 for apple, GPU driver for gpu, disk, port)",
            "install uv when missing (astral.sh official script, no admin)",
            f"uv python install {BOOTSTRAP_PYTHON}",
            f'uv tool install --python {BOOTSTRAP_PYTHON} "abstractgateway[<extras>]==<pin>"',
            "optional: nodejs-wheel, cargo crates, Ollama / LM Studio vendor installers",
            "abstractgateway service install (when supported) or a background start",
            "wait for GET /api/health",
            "abstractgateway-config claim-url (when supported) and open /console",
        ],
        "uninstall": [
            ["abstractgateway", "service", "uninstall"],
            ["uv", "tool", "uninstall", "abstractgateway"],
        ],
    }


def _post_install() -> dict[str, Any]:
    base_url = f"http://127.0.0.1:{DEFAULT_GATEWAY_PORT}"
    return {
        "entrypoint": "console",
        "gateway": [
            "abstractgateway",
            "serve",
            "--host",
            "127.0.0.1",
            "--port",
            str(DEFAULT_GATEWAY_PORT),
        ],
        "health_url": f"{base_url}/api/health",
        "console_url": f"{base_url}/console",
        "claim": ["abstractgateway-config", "claim-url"],
        "claim_fallback": {
            "token_file": "<data_dir>/auth/bootstrap-admin-token",
            "command": ["abstractgateway-config", "bootstrap-admin", "--print-token"],
        },
        "service": ["abstractgateway", "service", "install"],
        "doctor": ["abstractframework", "doctor"],
        "apps": [["npx", "-y", package] for package in NPM_RELEASE_VERSIONS],
        "notes": (
            "Providers, API keys, engines, models and users are configured in the gateway "
            "console (/console first-run wizard); every console action prints its CLI twin."
        ),
    }


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 os
import platform
import re
import shutil
import subprocess
import sys
import urllib.error
import urllib.request
from dataclasses import dataclass, field
from pathlib import Path
from typing import Any, Sequence

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


STATUS_RANK = {"error": 2, "warn": 1, "ok": 0, "info": 0}

DEFAULT_GATEWAY_URL = "http://127.0.0.1:8080"
DEFAULT_OLLAMA_URL = "http://127.0.0.1:11434"
DEFAULT_LMSTUDIO_URL = "http://127.0.0.1:1234/v1"
SUPPORTED_PYTHON = ((3, 10), (3, 13))
MIN_NODE_MAJOR = 18
MIN_MACOS_MAJOR = 14
DISK_WARN_BYTES = 5 * 1024**3
DISK_ERROR_BYTES = 1 * 1024**3


@dataclass(frozen=True)
class Check:
    """One doctor finding. ``status`` is ok | warn | error | info (info never fails)."""

    id: str
    status: str
    message: str
    detail: str | None = None
    data: dict[str, Any] = field(default_factory=dict)

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


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 _http_get_json(url: str, timeout: float) -> tuple[int | None, Any, str | None]:
    """Read-only GET. Returns (status, parsed JSON or None, error)."""

    request = urllib.request.Request(url, method="GET", headers={"Accept": "application/json"})
    try:
        with urllib.request.urlopen(request, timeout=timeout) as response:  # noqa: S310
            body = response.read(1_000_000)
            try:
                return response.status, json.loads(body.decode("utf-8")), None
            except (ValueError, UnicodeDecodeError):
                return response.status, None, None
    except urllib.error.HTTPError as exc:
        return exc.code, None, f"HTTP {exc.code}"
    except Exception as exc:  # connection refused, timeout, DNS ...
        reason = getattr(exc, "reason", exc)
        return None, None, str(reason)


def _uv_tool_bin_dir() -> Path:
    for env in ("UV_TOOL_BIN_DIR", "XDG_BIN_HOME"):
        value = os.environ.get(env)
        if value:
            return Path(value)
    return Path.home() / ".local" / "bin"


def _find_executable(name: str) -> str | None:
    found = shutil.which(name)
    if found:
        return found
    suffix = ".exe" if platform.system() == "Windows" else ""
    candidate = _uv_tool_bin_dir() / f"{name}{suffix}"
    return str(candidate) if candidate.exists() else None


def _parse_major(version_text: str | None) -> int | None:
    if not version_text:
        return None
    match = re.search(r"(\d+)(?:\.\d+)*", version_text)
    return int(match.group(1)) if match else None


def _macos_version() -> tuple[int, ...] | None:
    if platform.system() != "Darwin":
        return None
    raw = platform.mac_ver()[0]
    try:
        return tuple(int(part) for part in raw.split(".") if part)
    except ValueError:
        return None


def _gateway_url() -> str:
    return (os.environ.get("ABSTRACTGATEWAY_URL") or DEFAULT_GATEWAY_URL).rstrip("/")


def _ollama_url() -> str:
    raw = os.environ.get("OLLAMA_BASE_URL") or os.environ.get("OLLAMA_HOST") or DEFAULT_OLLAMA_URL
    if "://" not in raw:
        raw = f"http://{raw}"
    return raw.rstrip("/")


def _lmstudio_url() -> str:
    raw = (os.environ.get("LMSTUDIO_BASE_URL") or DEFAULT_LMSTUDIO_URL).rstrip("/")
    return raw if raw.endswith("/v1") else f"{raw}/v1"


def _python_check() -> Check:
    version = ".".join(str(part) for part in sys.version_info[:3])
    low, high = SUPPORTED_PYTHON
    data = {"version": version, "executable": sys.executable, "supported": "3.10-3.13"}
    if sys.version_info[:2] < low:
        return Check("python", "error", f"Python {version} is below the supported 3.10", data=data)
    if sys.version_info[:2] > high:
        return Check(
            "python",
            "warn",
            f"Python {version} is newer than the tested 3.10-3.13",
            "The bootstrap installs the framework on Python 3.12 through uv.",
            data=data,
        )
    return Check("python", "ok", f"Python {version} (supported: 3.10-3.13)", data=data)


def _platform_checks() -> list[Check]:
    system = platform.system()
    machine = platform.machine().lower()
    checks: list[Check] = []
    macos = _macos_version()
    data: dict[str, Any] = {"os": system, "arch": machine}
    if macos:
        data["macos"] = ".".join(str(part) for part in macos)
    if system == "Darwin" and machine in {"arm64", "aarch64"}:
        if macos and macos[0] >= MIN_MACOS_MAJOR:
            checks.append(
                Check(
                    "profile:apple",
                    "ok",
                    f"Apple Silicon on macOS {data.get('macos')}: the apple profile is supported",
                    data=data,
                )
            )
        else:
            checks.append(
                Check(
                    "profile:apple",
                    "warn",
                    f"The apple profile needs macOS {MIN_MACOS_MAJOR}+ (this is {data.get('macos')})",
                    "Use the light profile, or upgrade macOS for local MLX engines.",
                    data=data,
                )
            )
    elif system == "Darwin":
        checks.append(
            Check("profile:apple", "info", "Intel Mac: use the light profile", data=data)
        )
    else:
        checks.append(
            Check("profile:apple", "info", "The apple profile is for Apple Silicon Macs", data=data)
        )

    gpu_tool = None
    for tool in ("nvidia-smi", "rocminfo"):
        if shutil.which(tool):
            gpu_tool = tool
            break
    if gpu_tool:
        checks.append(
            Check("profile:gpu", "ok", f"{gpu_tool} is available", data={"tool": gpu_tool})
        )
    else:
        checks.append(
            Check(
                "profile:gpu",
                "info",
                "No nvidia-smi or rocminfo; the gpu profile would run local engines on CPU",
            )
        )
    return checks


def _tool_checks() -> list[Check]:
    checks: list[Check] = []
    uv = _find_executable("uv")
    if uv:
        version = _command_version(uv)
        checks.append(Check("uv", "ok", f"uv is available: {version}", data={"path": uv}))
    else:
        checks.append(
            Check(
                "uv",
                "warn",
                "uv is not installed",
                "The bootstrap (scripts/install.sh, install.ps1) installs it; see docs/install.md.",
            )
        )

    node = _find_executable("node")
    node_version = _command_version(node) if node else None
    major = _parse_major(node_version)
    npm = _find_executable("npm")
    node_data: dict[str, Any] = {"path": node, "version": node_version, "npm": npm}
    if node and major is not None and major >= MIN_NODE_MAJOR:
        source = "nodejs-wheel (uv tool)" if str(_uv_tool_bin_dir()) in node else "system"
        node_data["source"] = source
        checks.append(Check("node", "ok", f"Node {node_version} ({source})", data=node_data))
    elif node:
        checks.append(
            Check(
                "node",
                "warn",
                f"Node {node_version} is older than {MIN_NODE_MAJOR}; the browser apps need 18+",
                "Install a newer Node, or run: uv tool install nodejs-wheel",
                data=node_data,
            )
        )
    else:
        checks.append(
            Check(
                "node",
                "warn",
                "Node is not available; the browser apps (npx) need Node 18+",
                "No admin needed: uv tool install nodejs-wheel (or install.sh --with-apps)",
            )
        )
    return checks


def _disk_check() -> Check:
    target = Path.home()
    try:
        usage = shutil.disk_usage(target)
    except OSError as exc:
        return Check("disk", "warn", f"Could not read free disk space for {target}: {exc}")
    free_gb = usage.free / 1024**3
    data = {"path": str(target), "free_bytes": usage.free}
    if usage.free < DISK_ERROR_BYTES:
        return Check("disk", "error", f"Only {free_gb:.1f} GB free under {target}", data=data)
    if usage.free < DISK_WARN_BYTES:
        return Check(
            "disk",
            "warn",
            f"{free_gb:.1f} GB free under {target}; local models need several GB each",
            data=data,
        )
    return Check("disk", "ok", f"{free_gb:.1f} GB free under {target}", data=data)


def _gateway_checks(timeout: float) -> list[Check]:
    """Read-only probes of the gateway (GET /api/health) and its local config."""

    checks: list[Check] = []
    url = _gateway_url()
    status, body, error = _http_get_json(f"{url}/api/health", timeout)
    data: dict[str, Any] = {"url": url}
    if status == 200 and isinstance(body, dict) and body.get("service") == "abstractgateway":
        data["health"] = body.get("status")
        checks.append(Check("gateway", "ok", f"Gateway reachable at {url} ({body.get('status')})",
                            f"Console: {url}/console", data=data))
    elif status is not None:
        data["http_status"] = status
        checks.append(Check("gateway", "warn", f"{url}/api/health answered {status}, not a gateway",
                            data=data))
    else:
        data["error"] = error
        checks.append(
            Check(
                "gateway",
                "warn",
                f"No gateway at {url}",
                "Start it with `abstractgateway serve --host 127.0.0.1 --port 8080` or "
                "re-run the bootstrap; set ABSTRACTGATEWAY_URL for another address.",
                data=data,
            )
        )

    config_cli = _find_executable("abstractgateway-config")
    if not config_cli:
        checks.append(Check("gateway:config", "info", "abstractgateway-config is not on PATH"))
        return checks
    try:
        result = subprocess.run(
            [config_cli, "status", "--json"],
            check=False,
            capture_output=True,
            text=True,
            timeout=max(timeout, 30.0),
        )
        payload = json.loads(result.stdout) if result.stdout.strip() else {}
    except Exception as exc:
        checks.append(Check("gateway:config", "warn", f"abstractgateway-config status failed: {exc}"))
        return checks
    gateway = payload.get("gateway") if isinstance(payload, dict) else None
    gateway = gateway if isinstance(gateway, dict) else {}
    summary = {
        "data_dir": gateway.get("data_dir"),
        "auth_configured": gateway.get("auth_configured"),
        "auth_mode": gateway.get("auth_mode"),
        "store_backend": gateway.get("store_backend"),
        "service": payload.get("service") if isinstance(payload, dict) else None,
    }
    if result.returncode != 0 or not gateway:
        checks.append(
            Check("gateway:config", "warn", "abstractgateway-config status --json gave no gateway section",
                  data={k: v for k, v in summary.items() if v is not None})
        )
    else:
        checks.append(
            Check(
                "gateway:config",
                "ok",
                f"Gateway data dir: {summary['data_dir']}",
                data={k: v for k, v in summary.items() if v is not None},
            )
        )
    return checks


def _engine_checks(timeout: float) -> list[Check]:
    checks: list[Check] = []
    ollama = _ollama_url()
    status, body, error = _http_get_json(f"{ollama}/api/version", timeout)
    if status == 200:
        version = body.get("version") if isinstance(body, dict) else None
        label = f"Ollama {version}" if version else "Ollama"
        checks.append(Check("engine:ollama", "ok", f"{label} reachable at {ollama}",
                            data={"url": ollama, "version": version}))
    else:
        checks.append(Check("engine:ollama", "info", f"Ollama not reachable at {ollama} (optional)",
                            data={"url": ollama, "error": error or f"HTTP {status}"}))
    lmstudio = _lmstudio_url()
    status, body, error = _http_get_json(f"{lmstudio}/models", timeout)
    if status == 200:
        models = body.get("data") if isinstance(body, dict) else None
        count = len(models) if isinstance(models, list) else None
        checks.append(Check("engine:lmstudio", "ok", f"LM Studio reachable at {lmstudio}",
                            data={"url": lmstudio, "models": count}))
    else:
        checks.append(Check("engine:lmstudio", "info", f"LM Studio not reachable at {lmstudio} (optional)",
                            data={"url": lmstudio, "error": error or f"HTTP {status}"}))
    return checks


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

    Network probes are read-only GETs (gateway ``/api/health``, Ollama ``/api/version``,
    LM Studio ``/v1/models``); they run with the environment checks unless disabled.
    """

    if include_network is None:
        include_network = include_environment
    checks: list[Check] = [_python_check()]

    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:
        checks.extend(_platform_checks())
        checks.extend(_tool_checks())
        checks.append(_disk_check())
    if include_network:
        checks.extend(_gateway_checks(timeout))
        checks.extend(_engine_checks(timeout))

    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 {
        "schema": "abstractframework_doctor_v2",
        "abstractframework": __version__,
        "status": status,
        "platform": {
            "os": platform.system(),
            "arch": platform.machine(),
            "python": ".".join(str(part) for part in sys.version_info[:3]),
        },
        "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", "info": "INFO"}[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,
        include_network=not (args.no_environment or args.no_network),
        timeout=args.timeout,
    )
    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 host, tool and network probes; only check the Python package profile",
    )
    doctor.add_argument(
        "--no-network",
        action="store_true",
        help="Skip the read-only HTTP probes (gateway /api/health, Ollama, LM Studio)",
    )
    doctor.add_argument(
        "--timeout",
        type=float,
        default=2.0,
        help="Seconds per HTTP probe (default: 2)",
    )
    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)** | One-line install (`install.sh` / `install.ps1`), options and uninstall; Light / Apple / GPU chooser, `abstractframework doctor`, 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 |
| **[Workspace scripts](workspace-scripts.md)** | Working from source: package inventory and tiers, `build.sh`, `status.sh`, `pull.sh`, `commit.sh`, `push.sh`, launchers |
| **[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 |
| **[Shipped workflows](../abstractgateway/docs/shipped-workflows.md)** | The workflows a packaged Gateway serves out of the box — coder, deep research, co-scientist — and how to run them |

---

## Package map by layer

The root [README](../README.md) is the fuller package catalog. This shorter map
keeps the docs hub cross-linked to the package owners' entrypoints.

### Foundation

| Package | What it is |
|---|---|
| [abstractcore](../abstractcore/) | Unified LLM interface: providers, tools, structured output, media, embeddings, `/v1` server, capability plugins |
| [abstractsemantics](../abstractsemantics/) | Shared semantics registry for predicates and entity types |
| [abstractmemory](../abstractmemory/) | Durable, append-only agent memory: usage-weighted graph + journal — recall, formation, consolidation (the entity mind engine) |

### Durable execution

| Package | What it is |
|---|---|
| [abstractruntime](../abstractruntime/) | Durable execution kernel: runs, effects, waits, append-only ledger, artifacts, and the entity identity lane |
| [abstractagent](../abstractagent/) | ReAct, CodeAct, and MemAct patterns on top of Runtime + Core |
| [abstractflow](../abstractflow/) | Visual workflow editor and portable `.flow` bundles |

### Control plane

| Package | What it is |
|---|---|
| [abstractgateway](../abstractgateway/) | Deployable control plane: durable runs over HTTP/SSE, scheduling, workflow catalog, auth, artifact/ledger serving, and the summoned-entity door |

### Multimodal capabilities

| Package | What it is |
|---|---|
| [abstractvoice](../abstractvoice/) | Voice I/O (TTS / STT), local and remote backends |
| [abstractvision](../abstractvision/) | Model-agnostic image generation |
| [abstractmusic](../abstractmusic/) | Text-to-music / text-to-audio capability plugin |
| [abstract3d](../abstract3d/) | Local-first 3D generation |
| [abstractcamera](../abstractcamera/) | Camera control and capture tools |

### Apps and clients

| Package | What it is |
|---|---|
| [abstractcode](../abstractcode/) | Coding client with durable sessions and tool approvals: Rust terminal client (`cargo install abstractcode`) and browser client (`npx @abstractframework/code`) |
| [abstractassistant](../abstractassistant/) | macOS tray client for gateway-native chat and voice |
| [abstractobserver](../abstractobserver/) | Browser UI for monitoring, control, and scheduling |
| [abstractentity](../abstractentity/) | Summoned-entity manager and chat/replay UI |
| [abstractcontinuum](../abstractcontinuum/) | Continuous iterative development and deployment console |
| Consoles | Web consoles built into `abstractgateway serve` and `abstractcore serve` (`/console`, with Models and Engines tabs); terminal consoles `cargo install abstractgateway-console` and `cargo install abstractcore-console` |

### Shared libraries

| Package | What it is |
|---|---|
| [abstracttui](../abstracttui/) | Reactive Rust terminal UI engine |
| [abstractuic](../abstractuic/) | Shared React/Web Components UI kit |
| [abstractskill](../abstractskill/) | Shared Agent Skills (`SKILL.md`) loader and activation library |

---

## 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/README.md) | Install design: script bootstrap + gateway console, per-OS journeys, OS security, manifest, operations |
| [docs/comparisons/](comparisons/) | Trade-offs vs other frameworks |

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

Most people want AbstractFramework running on their own computer with nothing to set up by hand.
That is what the installer does: one download or one line, no admin password, and at the end
AbstractFramework opens in your web browser, already signed in. Developers who want the Python
libraries in their own environment go to [Install the Python framework](#install-the-python-framework-developers).

## Install on a Mac

You need a Mac with macOS 13 or later (Apple Silicon gets the fast local engines; they need
macOS 14), an internet connection, and about 5 GB of free disk space before models.

1. **Download** [AbstractFramework-Installer.pkg](https://github.com/lpalbou/AbstractFramework/releases/latest/download/AbstractFramework-Installer.pkg).
   The release process attaches it to each GitHub release (built by
   `scripts/lib/build_macos_installer.sh`); if the link does not work yet, use
   [the one line below](#or-paste-one-line-in-terminal) instead.
2. **Double-click it.** The macOS Installer opens. Click **Continue**, then **Install**. It installs
   for you only, so it does not ask for your password.
3. **A Terminal window opens** and shows each step as it happens. It asks one question:

   ```
   ? Start AbstractFramework automatically when you log in? (a per-user login item, no admin; the uninstaller removes it) [Y/n]
   ```

   Press **Return** for yes (recommended: it is then always there when you need it), or type `n`.
4. **Wait** 2 to 15 minutes, depending on your connection. The last lines say
   `AbstractFramework is ready.` and your browser opens AbstractFramework.
5. **In the browser**, the first-run guide helps you pick an engine (it detects what this Mac can
   run and installs it with one click) and a model that fits your Mac. You can close the Terminal
   window.

Afterwards, AbstractFramework is at `http://127.0.0.1:8080/console` (bookmark it), and its icon in
the menu bar opens it and shows its status.

What it puts on your Mac, all inside your home folder: uv and a private Python 3.12 in
`~/.local`, the AbstractFramework gateway (about 2.4 GB on Apple Silicon, with the MLX engines),
uv's download cache (about 2.4 GB, reused by upgrades), your data in
`~/Library/Application Support/AbstractGateway`, and, if you said yes, a login item
(`~/Library/LaunchAgents/ai.abstractframework.gateway.plist`). Models you download later come on
top.

**If macOS says the installer "cannot be opened because Apple cannot check it for malicious
software"**, you have an unsigned build: open **System Settings > Privacy & Security**, scroll
down, click **Open Anyway** next to the installer's name, and confirm. Signed releases do not show
this message.

The same installer also exists as a zip (`AbstractFramework-Installer-macOS.zip` on the release
page) with two double-clickable files: **Install AbstractFramework.command** and **Uninstall
AbstractFramework.command**. They do exactly what the package does.

### Or: paste one line in Terminal

On macOS and Linux, open Terminal, paste this line and press Return:

```bash
curl -LsSf https://raw.githubusercontent.com/lpalbou/AbstractFramework/main/scripts/install.sh | sh -s -- --interactive
```

`--interactive` makes it ask the start-at-login question; without it the answer is yes. Everything
else is the same as the Mac package above. On Linux the login item is a `systemd --user` service and
the data lives in `~/.local/share/abstractgateway`.

### Windows

Windows 10 22H2+ / 11: open PowerShell, paste this line and press Enter:

```powershell
powershell -ExecutionPolicy ByPass -c "irm https://raw.githubusercontent.com/lpalbou/AbstractFramework/main/scripts/install.ps1 | iex"
```

It installs under your user account (no administrator rights), starts AbstractFramework at sign-in
and opens it in your browser. The Windows script does not ask the start-at-login question yet; use
`-NoService` to skip it.

## If something goes wrong

The installer stops at the first problem, says what happened in plain words, and says what to do.
It never leaves a half-finished install behind that a second run cannot fix: after fixing the
cause, run it again (double-click the installer again, or paste the line again) and it continues
where it stopped.

| What you see | What to do |
|---|---|
| `no internet connection: the installer could not reach pypi.org` | Connect to the internet, then run the installer again. Nothing was changed. |
| `cannot reach pypi.org through the proxy set in your environment (…)` | Your computer is set up to use a proxy that does not answer. Fix the proxy (or remove the `HTTPS_PROXY` setting), then run it again. |
| `… failed because the internet connection dropped` | Reconnect and run it again; it continues where it stopped. |
| `this Terminal runs in Intel (Rosetta) mode on an Apple Silicon Mac` | Quit Terminal. In Finder open **Applications > Utilities**, select **Terminal**, choose **File > Get Info**, untick **Open using Rosetta**, then run the installer again. (Double-clicking the installer restarts itself in the right mode on its own.) |
| `the folder … belongs to 'root', so the installer … cannot write there` | An earlier command was run with `sudo`. Run the `sudo chown -R …` line the message shows (it asks for your password once), then run the installer again. |
| `macOS … is older than the versions AbstractFramework is tested on` | A warning, not a stop. If the install then fails, update macOS in **System Settings > General > Software Update**. |
| `the Apple Silicon engines (MLX) need macOS 14 or later` | You get the light version (remote and endpoint engines). Update macOS and run the installer again to add the local engines. |
| `the installed gateway does not start … reinstalling it` | Nothing to do: an earlier install was interrupted and the installer repairs it. |
| `the gateway did not answer … within 180 s` | Restart the computer (the login item starts it) or run the installer again, then open `http://127.0.0.1:8080/console`. |
| The browser page asks for a token | The one-time sign-in link lasts 10 minutes. Run the installer again: it opens a fresh link. |
| Anything else | Run the installer again. If it stops at the same step, report it with the log file named at the end of the message (`~/Library/Application Support/AbstractGateway/logs/install-….log`). |

## Remove AbstractFramework

Double-click **Uninstall AbstractFramework.command** (from the zip, or in
`~/Library/Application Support/AbstractFramework/Installer` after a package install), or paste:

```bash
curl -LsSf https://raw.githubusercontent.com/lpalbou/AbstractFramework/main/scripts/uninstall.sh | sh
```

It asks before removing anything, then asks two more questions, both defaulting to no:

- **Also delete your AbstractFramework data** (settings, users, chats, run history)? This cannot be
  undone.
- **Also remove uv, its Python and its download cache** (about 2.5 GB)? Asked only when the
  installer is what added uv; answer no if you use uv for anything else.

It always removes the login item, stops AbstractFramework and removes the gateway. It keeps Ollama
and LM Studio (they have their own uninstallers) and the one PATH line uv added to your shell
profile. Without questions: `sh uninstall.sh --yes` (keeps data), add `--purge` to delete the data
and `--remove-uv` to remove uv. Windows: `install.ps1 -Uninstall [-Purge]`.

## Advanced: what the installer does

The Mac package, the `.command` files and the one line all run the same script,
[`scripts/install.sh`](../scripts/install.sh) (Windows: `scripts/install.ps1`):

1. Checks the machine (OS, CPU, macOS 14+ for Apple Silicon, Rosetta, NVIDIA/ROCm, the folders it
   writes, internet access to PyPI, free disk, a free port) and picks a profile: `apple` on Apple
   Silicon, `gpu` when `nvidia-smi` or `rocminfo` works, `light` otherwise.
2. Asks whether to start at login (only with `--interactive`; the default is yes, and a previous
   "no" is remembered).
3. Installs [uv](https://docs.astral.sh/uv/) when it is missing, then Python 3.12 through uv.
4. Installs the gateway as an isolated uv tool, pinned to this release:
   `uv tool install --python 3.12 "abstractgateway[<profile>,tray]==0.4.2"`, from prebuilt wheels
   only (see [No compiler needed](#no-compiler-needed)), and checks that the command starts
   (reinstalling it in place when it does not).
5. Optionally installs Node.js for the browser apps, terminal tools, Ollama or LM Studio (flags
   below).
6. Registers the gateway to start at login with `abstractgateway service install` (a LaunchAgent on
   macOS, a `systemd --user` unit on Linux, a Startup shortcut on Windows) and starts it. With
   `--no-service` (or "no" to the question), or on a Linux host without a user systemd session, it
   starts the gateway in the background instead and removes a login item an earlier run
   registered.
7. Waits up to 180 seconds for `/api/health`, then opens `http://127.0.0.1:8080/console` through a
   one-time sign-in link (`abstractgateway-config claim-url`, valid 10 minutes, this machine only).
   If no link can be created, it shows where the admin token is.

Every command is printed as it runs, and the summary lists them all. Re-running the script
upgrades or repairs the install in place. The macOS package is built by
`scripts/lib/build_macos_installer.sh`, which signs and notarizes it when given a Developer ID
(see the header of that script).

### No compiler needed

By default the script never compiles anything, so you do not need Xcode Command Line Tools, gcc
or the MSVC Build Tools. It passes uv a small overrides file (`uv-overrides.txt` in the gateway
data directory; `--print` shows it) that swaps `webrtcvad` for `webrtcvad-wheels`, keeps `vllm`
to Linux, and leaves out the two compiled extras below, and it refuses to build those packages
from source, so a gap fails with a clear error instead of starting a compiler. If macOS asks you
to install the command line developer tools (an `xcode-select` prompt) during an install, you are
running an older copy of the script: cancel the prompt, fetch the script again with the one-liner
above and re-run it.

### llama.cpp GGUF models

Every profile, light included, gets in-process llama.cpp GGUF support (`llama-cpp-python`). PyPI
has only its source, so the script takes upstream's prebuilt wheel from
[abetlen's wheel index](https://abetlen.github.io/llama-cpp-python/whl/) (`--find-links` on the
package page, pinned in `uv-constraints.txt` next to the overrides file):

| Machine | Wheel |
|---|---|
| Apple Silicon Mac | `llama-cpp-python==0.3.28`, Metal (GPU offload) |
| Linux x86_64 / aarch64 (glibc) | `llama-cpp-python==0.3.35`, CPU |
| Windows x64 | `llama-cpp-python==0.3.35`, CPU |
| Intel Mac, musl Linux, Windows on ARM | no prebuilt wheel: skipped |

Where no wheel exists, or when the wheel install fails, the script installs everything else and
says `GGUF (llama.cpp) skipped: no prebuilt wheel for this machine`; `--full` then builds it from
source. The summary's `GGUF:` line says which wheel was installed. The Metal pin stays at 0.3.28
because the 0.3.32-0.3.35 Metal wheels fail zip integrity checks and uv refuses them.

### Compiled extras

Two optional engines publish no wheel on PyPI and are skipped by default: stable-diffusion.cpp
image generation (`stable-diffusion-cpp-python`) and voice echo cancellation
(`aec-audio-processing`). `--full` (Windows: `-Full`) keeps them and builds them, and llama.cpp,
from source, which takes several minutes and needs a C/C++ compiler (macOS:
`xcode-select --install`; Debian/Ubuntu: `sudo apt-get install -y build-essential`; Windows:
Visual Studio Build Tools with "Desktop development with C++"). Without a compiler, `--full` stops
before installing anything. You do not need them for MLX on Apple Silicon, for llama.cpp GGUF
models (above), for Ollama, LM Studio or other endpoint engines, or for cloud providers.

### Options

| macOS/Linux | Windows | What it does |
|---|---|---|
| `--profile auto\|light\|apple\|gpu` | `-Profile` | Override the profile choice |
| `--port N` | `-Port N` | Gateway port (default 8080; the next free port when 8080 is busy) |
| `--with-apps` | `-WithApps` | Make sure Node.js 18+ exists for the browser apps (`uv tool install nodejs-wheel`, no admin) |
| `--with-ollama` | `-WithOllama` | Run Ollama's official installer (Linux uses sudo; the script tells you first) |
| `--with-lmstudio` | `-WithLmStudio` | Install LM Studio (headless daemon on macOS/Linux, winget on Windows) |
| `--with-console` | `-WithConsole` | Install the `abstractgateway-console` crate with cargo (terminal console; needs Rust) |
| `--with-code-cli` | `-WithCodeCli` | Install the `abstractcode` crate with cargo (terminal client; needs Rust) |
| `--with-core-cli` | `-WithCoreCli` | Also put the `abstractcore` command on PATH |
| `--full` | `-Full` | Also build the [compiled extras](#compiled-extras) and llama.cpp from source (needs a C compiler) |
| `--no-service` | `-NoService` | Do not register a login service |
| `--no-open` | `-NoOpen` | Do not open the browser |
| `--pin X` / `--from PATH` | `-Pin` / `-From` | Install another gateway version or a local checkout |
| `--data-dir DIR` | `-DataDir` | Gateway data directory |
| `--interactive` | (not yet) | Ask whether to start at login (and, with `--uninstall`, whether to delete data and uv); the double-click installers pass it |
| `--print` | `-Print` (or `-WhatIf`) | Show the plan and every command; change nothing |
| `--uninstall [--purge] [--remove-uv]` | `-Uninstall [-Purge]` | Remove the service and uv tools (purge also deletes the data; `--remove-uv` also removes uv, its Python and cache when the installer added uv) |

Pass options through the one-liner like this:

```bash
curl -LsSf https://raw.githubusercontent.com/lpalbou/AbstractFramework/main/scripts/install.sh | sh -s -- --with-apps --with-ollama
```

```powershell
& ([scriptblock]::Create((irm https://raw.githubusercontent.com/lpalbou/AbstractFramework/main/scripts/install.ps1))) -WithApps -WithOllama
```

### The same install by hand

```bash
curl -LsSf https://astral.sh/uv/install.sh | sh             # Windows: irm https://astral.sh/uv/install.ps1 | iex
uv python install 3.12
uv tool install --python 3.12 "abstractgateway[tray]==0.4.2"    # [apple,tray] or [gpu,tray] for local engines
                       # (add --with and --overrides as shown by `install.sh --print` to avoid compiling)
uv tool update-shell                                          # puts ~/.local/bin on PATH; open a new terminal
abstractgateway service install --port 8080                   # or: abstractgateway serve
abstractgateway-config claim-url --base-url http://127.0.0.1:8080  # prints a one-time console link
```

### Run at login

`abstractgateway service install` registers the login service (LaunchAgent on macOS,
`systemd --user` on Linux, a Startup shortcut on Windows, experimental) and starts the gateway:

```bash
abstractgateway service install --host 127.0.0.1 --port 8080
abstractgateway service status
abstractgateway service uninstall
```

The bootstrap scripts use it by default. Gateways older than 0.3.0 (installed with `--pin`) have no
`service` command: `install.sh` then starts the gateway in the background and `install.ps1` adds a
Startup-folder shortcut. To write a login entry yourself on Linux, a user unit is enough:

```ini
# ~/.config/systemd/user/abstractgateway.service
[Service]
Environment=ABSTRACTGATEWAY_USER_AUTH=1
Environment=ABSTRACTGATEWAY_DATA_DIR=%h/.local/share/abstractgateway
ExecStart=%h/.local/bin/abstractgateway serve --host 127.0.0.1 --port 8080
Restart=on-failure

[Install]
WantedBy=default.target
```

Then `systemctl --user daemon-reload && systemctl --user enable --now abstractgateway`. On macOS
use a LaunchAgent in `~/Library/LaunchAgents/` with the same absolute command and environment
(launchd does not read your shell profile, so use absolute paths).

### Upgrade and uninstall

- Upgrade: re-run the one-liner (or `uv tool upgrade abstractgateway`).
- Uninstall: `curl -LsSf .../install.sh | sh -s -- --uninstall` (Windows: the script block with
  `-Uninstall`), or by hand: `abstractgateway service uninstall` (when registered), then
  `uv tool uninstall abstractgateway`. Your data stays in the data directory until you delete it
  (`--purge`). See [Operations and support](installers/operations-and-support.md) for locations.

### Check the install

```bash
uvx abstractframework doctor
```

It checks Python, uv, Node, disk, the gateway (`ABSTRACTGATEWAY_URL`, default
`http://127.0.0.1:8080`), and whether Ollama and LM Studio are reachable. It only reads.

## Install the Python framework (developers)

Use the profiles below when you want every framework library in one Python environment, for
example to build on AbstractCore or AbstractRuntime directly. Choose the profile 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 |

#### Requirements per profile

| Profile | Platforms | Python | Notes |
|---|---|---|---|
| Light | macOS, Linux, Windows | 3.10–3.13 | No local inference engines. |
| Apple | macOS 14 or later on Apple Silicon | 3.10–3.13 | MLX wheels need macOS 14+. F5-TTS voice cloning needs Python 3.11+; the rest of the profile works on 3.10. |
| GPU | Linux (and Windows where the engines publish wheels) with NVIDIA CUDA or AMD ROCm drivers | 3.10–3.13 | F5-TTS voice cloning needs Python 3.11+; the rest of the profile works on 3.10. |

A plain `pip install` of the `apple` or `gpu` profile builds the
[compiled extras](#compiled-extras) (`llama-cpp-python`, `stable-diffusion-cpp-python`,
`aec-audio-processing`) from source, so it needs a C/C++ compiler. The one-line install above
does not.

`abstractframework` 0.3.1 pins `abstractgateway==0.4.2`, `abstractassistant==0.5.0`,
`abstractcore==2.15.1`, `AbstractRuntime==0.4.34`, `abstractagent==0.3.13`,
`AbstractMemory==0.3.0`, `abstractsemantics==0.0.5`, `abstractvoice==0.11.4`,
`abstractvision==0.3.29` and `abstractmusic==0.1.15`. The `apple` and `gpu` extras select
`abstractgateway[apple|gpu]` and `abstractassistant[apple|gpu]` at the same versions
(`abstractassistant[apple]` is installed on macOS only). `abstractframework doctor` reports any
installed package whose version differs from these pins.

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, run `abstractframework doctor`, start the gateway and configure providers in its
console (see [Start the gateway](#start-the-gateway-and-apps)).

## 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.

Then run `abstractframework doctor`.

## 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.

Then run `abstractframework doctor`.

## Apps and tools outside pip

The browser apps and the Rust terminal tools are not Python packages, so no profile installs them.
Run or install them next to the Python stack:

| Tool | Command | Version released with 0.3.1 |
|---|---|---|
| Gateway web console | built into `abstractgateway`: open the link `abstractgateway serve` prints (`http://127.0.0.1:8080/console#claim=…`) | 0.4.2 |
| Core web console | built into `abstractcore`: open the link `abstractcore serve` prints (`http://127.0.0.1:8000/console#claim=…`) | 2.15.1 |
| Core terminal console | `cargo install abstractcore-console` (Rust 1.87+), then `abstractcore-console` (uses the `abstractcore` command) | 0.2.0 |
| Gateway terminal console | `cargo install abstractgateway-console` (Rust 1.87+), then `abstractgateway-console --url http://127.0.0.1:8080` | 0.8.0 |
| Flow Editor | `npx @abstractframework/flow` | 0.3.20 |
| Code Web UI | `npx @abstractframework/code` | 0.4.2 |
| Observer | `npx @abstractframework/observer` | 0.1.12 |
| Continuum console | `npx @abstractframework/continuum` | 0.3.0 |
| Entity manager | `npx @abstractframework/entity` | 0.2.0 |
| AbstractCode terminal client | `cargo install abstractcode`, or a prebuilt binary from the [AbstractCode GitHub release](https://github.com/lpalbou/AbstractCode/releases) | 0.5.1 |

The browser apps need Node.js 18 or later and a running gateway. Optional Python add-ons outside the
profiles install on their own: `pip install abstract3d`, `pip install abstractcamera`,
`pip install abstractskill`.

## Start the gateway and apps

Start the gateway, open its console, then any browser app against it:

```bash
abstractgateway serve            # binds 127.0.0.1:8080 and prints a one-time console link
npx @abstractframework/flow
```

Configure providers, API keys, engines and default models in the console. For library-only use
of AbstractCore, `abstractcore serve` opens the same Models and Engines screens in its own console,
and `abstractcore --config` remains available in the terminal. When you work from source, the workspace helper
scripts build and start the same services (see [Workspace scripts](workspace-scripts.md)).

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:0.4.2
```

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 (pinned tags are `<version>-gpu`, published on a best-effort basis; this image is
experimental). The AbstractCore OpenAI-compatible server is also published as
`ghcr.io/lpalbou/abstractcore-server:2.15.1`.

## How installs are designed

The scripts and the gateway console are the install experience on every OS. On macOS the
double-click artefacts (a payload-free `.pkg` and two `.command` files) only launch the same
script in Terminal; they add no install logic of their own. Design, security model and per-OS
behavior are in [docs/installers](installers/README.md).

## 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
```

The bootstrap scripts read `bootstrap.gateway_version` from it; other installers should consume
this manifest instead of maintaining independent package pins. Field reference:
[release-and-manifest.md](installers/release-and-manifest.md).

--- 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–3.13 for the manual installs below. Node.js 18+ (only for browser UIs). An LLM backend — local (Ollama, LM Studio, vLLM, llama.cpp) or cloud (OpenAI, Anthropic, etc.).

## Fastest path: the installer

If you want AbstractFramework running on your computer without setting up Python yourself, use
the installer. On a Mac, download and double-click
[AbstractFramework-Installer.pkg](https://github.com/lpalbou/AbstractFramework/releases/latest/download/AbstractFramework-Installer.pkg);
on macOS or Linux you can instead paste one line in Terminal:

```bash
curl -LsSf https://raw.githubusercontent.com/lpalbou/AbstractFramework/main/scripts/install.sh | sh -s -- --interactive
```

On Windows: `powershell -ExecutionPolicy ByPass -c "irm https://raw.githubusercontent.com/lpalbou/AbstractFramework/main/scripts/install.ps1 | iex"`.

It installs uv, Python 3.12 and the pinned gateway in your user account (no admin password), asks
whether to start it at login, starts it on `127.0.0.1:8080`, and opens its console in your browser
already signed in. The console's first-run guide sets up a local engine or a cloud key and a
default model (the **Models** tab lists the models that fit your machine and downloads them). Then
continue with [Monitor runs](#4-monitor-runs-with-abstractobserver) or
[AbstractFlow](#author-orchestration-with-abstractflow). Step by step, what to do when something
fails, and how to remove it: [Install](install.md). The sections below cover manual installs for
library and developer use.

---

## 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 a console.** `abstractcore serve` starts the server on `127.0.0.1:8000` and prints a
one-time link to its web console, where the **Engines** tab detects and installs local engines
(Ollama, LM Studio, MLX, llama.cpp) and the **Models** tab downloads a model that fits this machine.
The same actions exist on the command line and in the terminal console:

```bash
abstractcore serve                      # open the printed http://127.0.0.1:8000/console#claim=… link
abstractcore engines status             # which local engines are installed and running
abstractcore models catalog             # models, with a fit verdict for this machine
abstractcore models download ollama qwen3:4b-instruct
cargo install abstractcore-console      # terminal console (Rust 1.87+)
```

The interactive terminal 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 (optional)

With no configuration, `abstractgateway serve` binds `127.0.0.1:8080`, enables user auth, and keeps
its data in the per-user data folder (macOS `~/Library/Application Support/AbstractGateway`, Linux
`~/.local/share/abstractgateway`, Windows `%LOCALAPPDATA%\AbstractGateway`). Set the environment
when you want another data folder, browser origins or bundle registry:

```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. When this is unset,
# Gateway serves its shipped workflows (basic-agent, coding-agent,
# deep-research, co-scientist, and more).
# 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`, keeps its token in
`<data dir>/auth/bootstrap-admin-token` (readable by you only), and prints a one-time link:
`First run: open http://127.0.0.1:8080/console#claim=…`. Open it to sign in to the web console and
its first-run guide (engines, default model, apps); `abstractgateway claim --open` mints a new
link. Use the `admin` token from the file to sign in to AbstractFlow, AbstractCode Web or
AbstractObserver. `ABSTRACTGATEWAY_AUTH_TOKEN` is only the legacy server/operator bearer-token
path; it does not sign in browsers.

To start the gateway at login, use `abstractgateway service install --host 127.0.0.1 --port 8080`.

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.

You do not have to start from an empty registry. A packaged Gateway already
serves a set of ready workflows — a verify-gated coding agent, `deep-research`,
and `co-scientist` among them — listed in
[AbstractGateway's shipped workflows](../abstractgateway/docs/shipped-workflows.md).
Author your own when you need something they do not cover.

### 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 and browser)

A coding client for durable agentic sessions on the gateway you started above. Install the Rust
terminal client from crates.io (or download a prebuilt binary from the
[AbstractCode GitHub release](https://github.com/lpalbou/AbstractCode/releases)), or run the
browser client with `npx`:

```bash
cargo install abstractcode
abstractcode doctor              # check the gateway connection
abstractcode

npx @abstractframework/code      # browser client on http://127.0.0.1:3002
```

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
- **[Workspace scripts](workspace-scripts.md)** — work from source: clone, build and sync every package in dependency order

--- 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`), with a web console at `/console`
- Local model and engine management: browse models that fit this machine, download, delete, install engines
- 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)
 AbstractCode (terminal + web), AbstractAssistant, your custom app
 AbstractContinuum console, AbstractEntity manager
 Gateway consoles: web /console (first-run guide, Models, Engines),
                  terminal abstractgateway-console
                       │  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
 host profile, model catalog + fit, engines, download/delete jobs
   (CLI, /acore/* routes, web /console, terminal abstractcore-console)
 request/output normalization + call-scoped resolved-route truth
 tools, structured output, media input, embeddings, MCP
 capability plugins: voice / vision / music
```

## How the framework is distributed

Each layer ships through the registry that fits it. The `abstractframework` meta-package pins the
Python side with exact versions; the apps and terminal tools are installed next to it.

```mermaid
flowchart LR
    subgraph PyPI["PyPI (pinned by abstractframework)"]
        GW["abstractgateway<br/>server + /console"]
        AS["abstractassistant"]
        STACK["abstractcore · AbstractRuntime · abstractagent<br/>AbstractMemory · abstractsemantics<br/>abstractvoice · abstractvision · abstractmusic"]
    end
    subgraph npm["npm (npx @abstractframework/...)"]
        APPS["flow · code · observer<br/>continuum · entity"]
    end
    subgraph crates["crates.io (cargo install)"]
        CLI["abstractcode"]
        CON["abstractgateway-console"]
        CCON["abstractcore-console<br/>(app + Models/Engines screens library)"]
    end
    subgraph GHCR["GHCR images"]
        IMG["abstractgateway · abstractcore-server"]
    end
    BOOT["install.sh / install.ps1<br/>(uv tool install abstractgateway)"]
    BOOT -->|installs, starts, opens /console| GW
    AS -->|HTTP/SSE| GW
    APPS -->|HTTP/SSE| GW
    CLI -->|HTTP/SSE| GW
    CON -->|HTTP/SSE| GW
    CON -->|embeds screens| CCON
    CCON -->|abstractcore CLI| STACK
    GW --> STACK
    IMG -.->|same server, containerized| GW
```

The Models and Engines features are implemented once, in AbstractCore, and inherited by the
gateway:

```mermaid
flowchart TB
    CORE["AbstractCore<br/>host profile · catalog + fit · engines · jobs"]
    CORE --> CCLI["abstractcore models / engines (CLI)"]
    CORE --> CAPI["abstractcore serve<br/>/acore/* + web /console"]
    CORE --> CTUI["abstractcore-console<br/>screens 9 Models, 0 Engines"]
    CORE -->|via AbstractRuntime| GAPI["abstractgateway<br/>/api/gateway/models, engines, jobs, host/profile"]
    GAPI --> GWEB["gateway web /console<br/>embeds Core's Models and Engines screens"]
    GAPI --> GTUI["abstractgateway-console<br/>mounts the abstractcore-console screens over HTTP"]
    GAPI --> GCLI["abstractgateway models / engines (CLI)"]
```

Every console action shows its command-line equivalent. Download, delete and engine installs are
admin-only jobs; engine installs from a console are enabled by default only for a server bound to
loopback (`allow_engine_install` on the gateway, `ABSTRACTCORE_ALLOW_ENGINE_INSTALL` on the core
server).

See [Install AbstractFramework](install.md) for the versions released together and the commands
for each registry.

**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.

Portable workflow execution does not require every client to be a generic workflow picker. Some
products expose workflow selection, while others intentionally bind to one published workflow or
interface family for a specialized task. The invariant is that execution still happens as a durable
Gateway/Runtime workflow.

---

## 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.

For Core-backed multimodal generation, Runtime can also persist bounded `resolved_actions`
summaries in replay exports. Those records capture the normalized request/output summary and the
effective resolved route without turning route internals into the ordinary app-facing vocabulary.

### 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. In 0.3.1:
`abstractcore` 2.15.1, `abstractruntime` 0.4.34, `abstractagent` 0.3.13, `abstractgateway` 0.4.2,
`abstractmemory` 0.3.0, `abstractsemantics` 0.0.5, `abstractvoice` 0.11.4, `abstractvision` 0.3.29,
`abstractmusic` 0.1.15, `abstractassistant` 0.5.0.

### `PACKAGE_DISTRIBUTIONS`

Maps each package name in `RELEASE_VERSIONS` to its PyPI distribution name (for example
`abstractruntime` → `AbstractRuntime`).

### `NPM_RELEASE_VERSIONS`

The npm apps released with this version, each runnable with `npx <package>`:
`@abstractframework/flow` 0.3.20, `@abstractframework/code` 0.4.2,
`@abstractframework/observer` 0.1.12, `@abstractframework/continuum` 0.3.0 and
`@abstractframework/entity` 0.2.0. They also appear as `npm_apps` in the install manifest.

### `CRATE_RELEASE_VERSIONS`

The Rust terminal tools released with this version, installed with `cargo install <crate>`:
`abstractgateway-console` 0.8.0, `abstractcore-console` 0.2.0, `abstractcode` 0.5.1 and the
`abstracttui` engine 0.6.0. The bootstrap scripts install `abstractgateway-console` and
`abstractcode` at these versions with `--with-console` and `--with-code-cli`.

### `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
print(profile["crates"])                   # CRATE_RELEASE_VERSIONS
```

### `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 the Python version (3.10–3.13), pinned package versions, the Apple/GPU profile
prerequisites (macOS 14+ on Apple Silicon, `nvidia-smi` / `rocminfo`), uv, Node.js 18+ (system or
`nodejs-wheel`), free disk, and, over read-only GET requests, the gateway health
(`ABSTRACTGATEWAY_URL`, default `http://127.0.0.1:8080`), `abstractgateway-config status --json`,
and whether Ollama and LM Studio are reachable. It does not import heavy local inference stacks.
Checks report `ok`, `warn`, `error` or `info` (`info` never fails the run).

```bash
abstractframework doctor
abstractframework doctor --json          # schema abstractframework_doctor_v2
abstractframework doctor --no-network    # skip the gateway / engine probes
abstractframework doctor --timeout 5     # probe timeout in seconds (default 2)
```

### `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) |
| Coding client | `abstractcode` (crates.io) and `@abstractframework/code` (npm) |
| Gateway operator console | built-in `/console`, and `abstractgateway-console` (crates.io) |
| Local models and engines (catalog, fit, download, delete, engine installs) | `abstractcore` (`abstractcore models`, `abstractcore engines`, `/acore/*`), mirrored by `abstractgateway` (`/api/gateway/models`, `/engines`, `/jobs`) |
| AbstractCore consoles | built-in `/console` of `abstractcore serve`, and `abstractcore-console` (crates.io) |
| Continuous development console | `@abstractframework/continuum` (npm) |
| Summoned-entity manager | `@abstractframework/entity` (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 |
|---|---|
| A running gateway + web console, no Python setup | the installer (a Mac package, or one line) in [Install](install.md) |
| Smallest useful (LLM SDK only) | `pip install abstractcore` |
| Gateway-first deployment | `pip install abstractgateway` |
| Everything at compatible versions | `pip install abstractframework` |
| A browser app against an existing gateway | `npx @abstractframework/<flow\|code\|observer\|continuum\|entity>` |
| A terminal client | `cargo install abstractcode`, `cargo install abstractgateway-console` or `cargo install abstractcore-console` |
| A container deployment | `ghcr.io/lpalbou/abstractgateway:0.4.2` |

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). The bootstrap scripts set it to `~/Library/Application Support/AbstractGateway` (macOS), `~/.local/share/abstractgateway` (Linux) or `%LOCALAPPDATA%\AbstractGateway` (Windows).
- **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.

---

## Installing with the one-line script

### Why is `abstractgateway` not found after the install?

The commands live in `~/.local/bin` (`%USERPROFILE%\.local\bin` on Windows). The script runs
`uv tool update-shell` once to add that directory to your PATH, which applies to **new**
terminals. Open a new terminal, or call `~/.local/bin/abstractgateway` directly. Pass
`--no-modify-path` if you manage PATH yourself.

### Which port does the gateway use?

`8080` by default. When something else already listens there (for example `llama-server` or
`mlx_lm.server`, which also default to 8080), the script picks the next free port, remembers it
for later runs, and prints the console URL. Choose one with `--port N` (`-Port N`).

### Will it ask for my password or admin rights?

Not for the default install: uv, Python, the gateway and Node (`nodejs-wheel`) install in your
user account. Only optional vendor installers may: Ollama on Linux uses sudo (it installs a system
service), Ollama on macOS may ask to link `/usr/local/bin/ollama`, and LM Studio on Linux may ask
to install `libatomic1`. The script tells you before running them.

### Do I need Xcode or a C compiler?

No. The default install uses prebuilt wheels only. If macOS shows an "install the command line
developer tools" prompt, cancel it and re-run the current one-liner: an older copy of the script
compiled a few packages. llama.cpp GGUF models come from upstream's prebuilt wheel on Apple
Silicon, Linux and Windows x64 ([llama.cpp GGUF models](install.md#llamacpp-gguf-models)). You
need a compiler only for `--full`, which adds stable-diffusion.cpp and echo cancellation, and
llama.cpp on machines without a prebuilt wheel ([Compiled extras](install.md#compiled-extras)).

### Windows says scripts are disabled on this system

Pasting the one-liner works under the default `Restricted` policy because
`powershell -ExecutionPolicy ByPass -c "irm … | iex"` runs a command, not a script file, and the
bypass applies to that process only. If your organization sets the policy through Group Policy
(`Get-ExecutionPolicy -List` shows `MachinePolicy` or `UserPolicy`), a saved `install.ps1` will not
run; use the one-liner or ask your administrator. The installer reports this during preflight.

### The sign-in link expired or I closed the tab

The one-time link is valid for 10 minutes and works only from the same machine. Mint a new one with
`abstractgateway claim --open` (or `abstractgateway-config claim-url`). The admin token also stays
in `<data dir>/auth/bootstrap-admin-token`; the install summary prints that path.

### How do I stop or restart the gateway?

When the script registered the login service, `abstractgateway service status` shows it,
`abstractgateway service uninstall` stops the gateway and removes the login entry (your data is
kept), and `abstractgateway service install --host 127.0.0.1 --port 8080` registers and starts it
again. With `--no-service`, the install summary prints the stop and start commands; re-running the
installer starts it again.

### How do I download a model or install Ollama later?

Open the console's **Engines** tab (detect, install with the exact command shown first) and
**Models** tab (models that fit this machine, download, delete). From a terminal:
`abstractgateway engines status|install` and `abstractgateway models catalog|download|delete`.
Engine installs from the console run on the gateway host and are enabled by default only when the
gateway listens on loopback.

### How do I see what the script will do before running it?

Add `--print` (Windows: `-Print` or `-WhatIf`). It runs the read-only checks and prints every
command without changing anything.

### How do I remove it?

Re-run the script with `--uninstall` (`-Uninstall`). Add `--purge` (`-Purge`) to delete the gateway
data as well. See [Install](install.md#upgrade-and-uninstall).

---

## 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, gateway console
- **Distribution**: install profile, release pins

---

## 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.

### Gateway console

The operator console for one gateway. The web console is built into `abstractgateway` and served at `/console`; the terminal console is the separate `abstractgateway-console` crate (`cargo install abstractgateway-console`). Both include AbstractCore's **Models** and **Engines** screens.

### Core console

The console for AbstractCore on its own. The web console is served at `/console` by `abstractcore serve` (Overview, Models, Engines, Providers); the terminal console is the `abstractcore-console` crate, which is also the library that provides the Models and Engines screens to the gateway's terminal console.

### Claim link

A one-time console sign-in link (`/console#claim=<code>`), single use, valid for 10 minutes and redeemable only from the same machine. `abstractgateway serve` and `abstractcore serve` print one on a first local start; `abstractgateway claim` and `abstractgateway-config claim-url` mint a new one.

### First-run guide

The gateway console's setup flow, opened once per data folder by the claim link and later from the **Setup** button: host summary, local engines, a default model that fits the machine, and the apps.

### Models and Engines

The local model and engine management shared by AbstractCore and the gateway: a model catalog with a fit verdict for this machine (`fits`, `tight`, `too_large`, `partial_offload`, `unknown`), installed models with sizes, download and delete jobs, and detection and installation of local engines (Ollama, LM Studio, MLX, llama.cpp, vLLM, transformers). Available as `abstractcore models|engines`, `abstractgateway models|engines`, and in the consoles.

---

## Distribution

### Install profile

One of the three ways to install the pinned Python stack: **Light** (`pip install abstractframework`, remote/endpoint inference only), **Apple** (`abstractframework[apple]`, adds MLX/Metal engines on macOS 14+) and **GPU** (`abstractframework[gpu]`, adds CUDA/ROCm engines). See [Install](install.md).

### Bootstrap script

The one-line installer (`scripts/install.sh` for macOS and Linux, `scripts/install.ps1` for Windows). It installs uv, Python 3.12 and the pinned gateway as a uv tool, registers and starts it, and opens the console with a claim link. See [Install](install.md#advanced-what-the-installer-does).

### Release pins

The exact (`==`) versions a given `abstractframework` release installs, exposed as `RELEASE_VERSIONS` and checked by `abstractframework doctor`. The npm apps and crates released with it are listed in `NPM_RELEASE_VERSIONS` and `CRATE_RELEASE_VERSIONS`.

---

## 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/workspace-scripts.md ---
# Workspace scripts (working from source)

AbstractFramework is developed as one workspace: this repository at the root and every package
repository cloned next to its files (`abstractcore/`, `abstractgateway/`, `abstractuic/`, …). Each
directory is its own git repository. The scripts in `scripts/` let you clone, build, inspect,
commit, pull and push the whole set at once, always in dependency order.

If you only want to *use* the framework, install the published packages instead: see
[Install](install.md). This page is for contributors who build from the checkouts.

## The package inventory

`scripts/lib/packages.txt` is the single list every script reads. It has one row per published
package (30 packages in 21 repositories):

| Column | Meaning |
|---|---|
| `id` | Short package id used by the scripts (`abstractcore`, `panel-chat`, `code-cli`, …) |
| `repo` | Checkout directory under the workspace root (`.` is this repository) |
| `github` | GitHub `owner/Name`, used by `clone.sh` |
| `kind` | `python`, `npm`, `rust` or `meta` (the root meta-package) |
| `path` | Package directory inside the repository (`abstractuic/panel-chat`, `abstractcode/web`, `abstractgateway/console-tui`, …) |
| `registry`, `name` | Where it is published and under which name (PyPI distribution, npm package, crate) |
| `tier` | Dependency level: `0` has no internal dependency; otherwise one more than its highest dependency |
| `deps` | Internal dependency edges, `id:kind` |

Edge kinds explain *why* one package comes before another:

| Edge | Meaning |
|---|---|
| `dep` | Runtime dependency (pyproject `dependencies`, npm `dependencies`, Cargo `[dependencies]`) |
| `extra` | Only through an optional extra (for example `abstractcore[vision]` → `abstractvision`) |
| `peer` | npm peer dependency |
| `dev` | npm dev dependency bundled at build time from the registry |
| `alias` | Vite source alias to `../abstractuic/<package>/src`: the build needs the AbstractUIC checkout |
| `pin` | Exact `==` pin in the root `abstractframework` meta-package |
| `app` | Browser app version listed by the root install manifest |

`./scripts/deps.sh check` compares the inventory with the real `pyproject.toml`, `package.json`,
`Cargo.toml` and `vite.config.*` files and with each checkout's git origin, and fails on any
difference. Run it after you add a dependency between two packages, then update the row.

## Tiers: what comes first

`./scripts/deps.sh` prints every tier with its edges. The current order:

| Tier | Python (PyPI) | npm | Rust (crates.io) |
|---|---|---|---|
| 0 | abstractskill, abstractsemantics, AbstractMemory, abstractvision, abstractvoice, abstractmusic, abstractcamera | ui-kit, app-server, monitor-flow, monitor-gpu, monitor-memory, monitor-active-memory | abstracttui |
| 1 | abstract3d | panel-chat, flow | abstractcore-console, abstractcode |
| 2 | abstractcore | observer, continuum, entity, code (web) | abstractgateway-console |
| 3 | AbstractRuntime | | |
| 4 | abstractagent, abstractassistant | | |
| 5 | abstractgateway | | |
| 6 | abstractframework (meta-package) | | |

Install, build and release tier by tier: a package is only built or published after everything it
depends on. To see what has to follow a change to one package, ask for its reverse dependencies:

```bash
./scripts/deps.sh rdeps abstractcore
```

The same inventory drives the release sequence described in
[ADR 0034](adr/0034-framework-release-sequence-and-gates.md).

## Commands

| Script | What it does |
|---|---|
| `./scripts/clone.sh [DIR]` | Clone every repository (tier order), or fast-forward the ones already cloned. `--list` prints the repositories. |
| `./scripts/deps.sh` | Tiers and dependency edges. Also `order`, `rdeps ID`, `check`, `versions`, `json`, `--kind python\|npm\|rust`. |
| `source ./scripts/build.sh` | Build every package from the checkouts, tier by tier, and stay in the virtualenv. See below. |
| `./scripts/status.sh` | Git overview of every repository, grouped by tier: branch, changes, unpushed and unpulled commits, and the packages each repository holds. `--short` shows only repositories with pending work. |
| `./scripts/status.sh --registry` | Adds local version vs latest published version on PyPI, npm and crates.io for every package (network). `--versions` shows local versions only; `--tiers` adds the dependency view. |
| `./scripts/pull.sh` | Fetch and fast-forward `main` everywhere. Never merges or rebases; a diverged repository is reported and left alone. `--dry-run` fetches and reports only. |
| `./scripts/commit.sh "message"` | Commit every dirty repository with the same message (`git add -A` per repository). Does not push. |
| `./scripts/push.sh` | Dry run: lists, per repository, the commits `main` would push. `--yes` pushes them. Never force-pushes, never pushes tags, skips a diverged `main`. `--fetch` refreshes the upstream first. |
| `./scripts/install.sh` | Install the *published* release (not the checkouts); see [Install](install.md). `--print` shows the plan. |

`pull.sh` and `push.sh` accept `--only repo1,repo2` to work on a subset.

A typical day:

```bash
./scripts/pull.sh --dry-run      # what changed upstream
./scripts/pull.sh                # fast-forward
source ./scripts/build.sh        # rebuild what you work on
./scripts/status.sh --short      # what you changed
./scripts/commit.sh "Describe the change"
./scripts/push.sh                # review, then:
./scripts/push.sh --yes
```

## Building from source

`build.sh` builds the three ecosystems in dependency order:

- **Python**: editable installs (`pip install -e`) of the 13 Python packages and the root
  meta-package into one virtualenv. Third-party dependencies still come from PyPI.
- **npm**: the seven AbstractUIC packages (installed once at the `abstractuic` workspace root, built
  per package), then the apps `flow`, `observer`, `continuum`, `entity` and `code` (web).
- **Rust**: `cargo build` of `abstracttui`, `abstractcore-console` (`abstractcore/console-tui`),
  `abstractcode` (`abstractcode/tui`) and `abstractgateway-console` (`abstractgateway/console-tui`).

| Option | Effect |
|---|---|
| `--python`, `--npm`, `--rust` | Build only the selected ecosystems (they combine). Default: all three. |
| `--light` (default), `--apple`, `--gpu` | Python dependency profile; also `AF_BUILD_PROFILE=light\|apple\|gpu\|auto`. |
| `--clean` | Delete the virtualenv first. |
| `--plan` | Print the tier-ordered build plan and build nothing. |
| `AF_VENV_DIR=path` | Virtualenv location (default `<root>/.venv`). The `-local` launchers use the same variable. |

The npm and cargo output is kept in a log per package; the terminal shows one line per package and
the log tail when a step fails. The script exits non-zero when any selected ecosystem failed.

Prerequisites: Python 3.10+, Node.js 18+ for the npm packages, a Rust toolchain for the crates.

## Running the stack

| Published packages | Local checkouts | Starts |
|---|---|---|
| `start.sh` / `af.sh` | `start-local.sh` / `af-local.sh` | The whole stack under one supervisor: gateway first, then the apps |
| `gateway.sh` | `gateway-local.sh` | AbstractGateway (control plane, port 8080) |
| `flow.sh` | `flow-local.sh` | `@abstractframework/flow` |
| `observer.sh` | `observer-local.sh` | `@abstractframework/observer` |
| `code.sh` | `code-local.sh` | `@abstractframework/code` (web) |
| `console.sh` | `console-local.sh` | `@abstractframework/continuum` |
| `entity.sh` | `entity-local.sh` | `@abstractframework/entity` |
| `assistant.sh` | `assistant-local.sh` | AbstractAssistant (tray app) |

The stack launchers use one port map: gateway 8080, observer 3001, continuum 3002, code 3003,
entity 3004, flow 3005 (each overridable with `ABSTRACT<APP>_PORT`). `start-local.sh --build`
runs `build.sh` first.

## Script tests

```bash
bash scripts/tests/test_inventory.sh      # inventory vs package files, install pins vs manifest
bash scripts/tests/test_repo_scripts.sh   # clone/status/commit/push/pull/build in an offline sandbox
bash scripts/tests/test_af_supervisor.sh  # stack supervisor semantics with stub services
```

All three run against temporary directories and stub services; they do not modify the workspace.

--- docs/installers/README.md ---
# Installers

This directory documents how AbstractFramework is installed on a user's machine: a one-line
bootstrap script per OS that provisions the gateway with [uv](https://docs.astral.sh/uv/), and
the gateway's web console (`/console`) as the guided setup UI. The user-facing instructions are in
[Install](../install.md); these pages explain the design, the contracts and the security model
behind them. The decision is recorded in
[ADR-0038](../adr/0038-script-bootstrap-and-gateway-console-install.md).

## Document map

- [`strategy.md`](strategy.md): the install model and why it is script + console.
- [`user-journeys.md`](user-journeys.md): what happens step by step on macOS, Linux and Windows,
  for first install, apps, engines, upgrade and uninstall.
- [`components.md`](components.md): each component, how it reaches the machine, and what the
  bootstrap does with it.
- [`security-and-os-blocks.md`](security-and-os-blocks.md): Gatekeeper, SmartScreen, execution
  policy, sudo/UAC prompts, loopback binding, and where code signing still applies.
- [`release-and-manifest.md`](release-and-manifest.md): the generated install manifest
  (`install-manifest.json`, schema v2) and how releases update it.
- [`operations-and-support.md`](operations-and-support.md): data and log locations, health
  checks, troubleshooting.
- [`implementation-plan.md`](implementation-plan.md): what is delivered and what comes next.
- `install-manifest.json` / `install-manifest.schema.json`: the generated manifest and its schema.

## Scripts

| OS | Script | One-liner |
|---|---|---|
| macOS, Linux | [`scripts/install.sh`](../../scripts/install.sh) | `curl -LsSf https://raw.githubusercontent.com/lpalbou/AbstractFramework/main/scripts/install.sh \| sh` |
| Windows 10 22H2+ / 11 | [`scripts/install.ps1`](../../scripts/install.ps1) | `powershell -ExecutionPolicy ByPass -c "irm https://raw.githubusercontent.com/lpalbou/AbstractFramework/main/scripts/install.ps1 \| iex"` |

--- docs/installers/strategy.md ---
# Installer Strategy

AbstractFramework installs as a **one-line script per OS** plus the **AbstractGateway web
console** as the guided UI. The script provisions everything the gateway needs in user space,
starts it on `127.0.0.1`, and opens `/console` in the browser; from there the console configures
providers, engines, models and users. This page explains the model; [Install](../install.md) has
the commands and [ADR-0038](../adr/0038-script-bootstrap-and-gateway-console-install.md) records
the decision.

## The model in one picture

```mermaid
flowchart LR
    U["User pastes one line"] --> S["install.sh / install.ps1"]
    S --> UV["uv (installed if missing)"]
    UV --> PY["Python 3.12 (uv-managed)"]
    UV --> GW["abstractgateway[profile,tray]==pin<br/>(uv tool, ~/.local/bin)"]
    UV -. "--with-apps" .-> NODE["nodejs-wheel (node, npm, npx)"]
    S -. "--with-ollama / --with-lmstudio" .-> V["Vendor installers"]
    S --> SVC["abstractgateway service install<br/>(LaunchAgent / systemd --user / Startup)"]
    SVC --> H["GET /api/health"]
    H --> C["Browser: /console (claim link)"]
    C --> W["First-run wizard: engines, default model, apps"]
```

## Principles

- **No admin rights on the default path.** uv, Python, the gateway and Node all install under the
  user's home. Only optional vendor installers (Ollama on Linux, for example) may ask for sudo or
  UAC, and the script says so before running them.
- **No system Python.** uv downloads a standalone Python 3.12, which satisfies every profile (MLX,
  F5-TTS, vLLM). The script always passes `--python 3.12`.
- **One source of pins.** The gateway version comes from `bootstrap.gateway_version` in the
  generated [install manifest](release-and-manifest.md), which follows the root release pins.
- **Every step has a CLI twin.** The script prints each command; `--print` (`-Print` on Windows)
  shows the whole plan without changing anything; the final summary lists the commands so the
  install can be reproduced by hand. Power users can skip the script entirely.
- **The gateway owns the host integration.** Autostart is `abstractgateway service install`, and
  browser sign-in is a one-time claim link from `abstractgateway-config claim-url` (both in
  gateway 0.3.0 and later). With an older gateway selected through `--pin`, the script starts the
  gateway in the background and shows the admin token file instead.
- **Loopback first.** The gateway binds `127.0.0.1`. Remote hosts use an SSH tunnel or the
  container deployment.
- **Idempotent.** Re-running the script upgrades or repairs in place and keeps the port, profile and
  data directory from the previous run. `--uninstall` removes the service and the uv tools and keeps
  data unless `--purge` is given.

## Profiles

The script picks a profile from the machine and accepts `--profile` to override it.

| Profile | Picked when | Gateway extras | Local engines |
|---|---|---|---|
| `apple` | Apple Silicon, macOS 14 or later | `apple,tray` | MLX/Metal stacks in the gateway environment |
| `gpu` | `nvidia-smi` or `rocminfo` works (Linux; best-effort on Windows) | `gpu,tray` | CUDA/ROCm stacks |
| `light` | anything else | `tray` (Linux: only with a display) | none: remote APIs and endpoint servers (Ollama, LM Studio, vLLM, llama.cpp) |

The bootstrap installs the gateway distribution, which is the framework's deployment entry point
(ADR-0033). The full meta-package (`pip install "abstractframework[<profile>]"`) stays available
for developers who want every library in one environment.

## Where the guided UI lives

The console already covers providers, API keys, capability defaults, users and model downloads.
The first-run wizard adds: host profile, engine detection and one-click installs (with the exact
command shown), a default model filtered to what fits the machine, and app launch commands. The
terminal console (`abstractgateway-console`) offers the same screens on headless hosts.

## Large model assets

Models are not part of the bootstrap. The console's Models tab shows sizes and a fit verdict for
the machine before a download, and downloads run as background jobs with progress.

## Out of scope

Signed native launchers, enterprise MSI/pkg packages and offline bundles are not part of this
model; see [implementation-plan.md](implementation-plan.md).

--- docs/installers/user-journeys.md ---
# User Journeys

These flows describe exactly what the bootstrap scripts do. Commands are in
[Install](../install.md); the model is in [strategy.md](strategy.md).

## First install on macOS

1. Paste in Terminal:
   `curl -LsSf https://raw.githubusercontent.com/lpalbou/AbstractFramework/main/scripts/install.sh | sh`
2. **Preflight** (read-only): macOS version and CPU (`sw_vers`, `uname -m`), profile choice
   (`apple` on Apple Silicon with macOS 14+, else `light`), free disk under your home, port 8080
   (the next free port is used and remembered when 8080 is taken).
3. **uv**: installed into `~/.local/bin` by the official astral.sh script when missing
   (`UV_NO_MODIFY_PATH=1`), then `uv tool update-shell` adds `~/.local/bin` to your shell profile
   once.
4. **Python**: `uv python install 3.12`.
5. **Gateway**: `uv tool install --python 3.12 "abstractgateway[apple,tray]==<pin>"`. The
   commands `abstractgateway` and `abstractgateway-config` land in `~/.local/bin`.
6. **Optional** (flags): Node for the apps (`--with-apps` installs the `nodejs-wheel` uv tool when
   no Node 18+ exists), Ollama (`--with-ollama`: the official installer, which may ask for your
   password to link `/usr/local/bin/ollama`), LM Studio (`--with-lmstudio`: the headless `llmster`
   daemon), terminal tools (`--with-console`, `--with-code-cli` through cargo).
7. **Service**: `abstractgateway service install --host 127.0.0.1 --port 8080` registers a
   per-user LaunchAgent and starts it, so the gateway also starts at login. With `--no-service`,
   the script starts it in the background instead.
8. **Health**: waits up to 60 seconds for `GET /api/health`.
9. **Sign-in**: `abstractgateway-config claim-url` mints a one-time link valid for 10 minutes on
   this machine; if no link can be created, the script prints the path of the admin token file
   (`~/Library/Application Support/AbstractGateway/auth/bootstrap-admin-token`).
10. **Browser**: opens the claim link (`http://127.0.0.1:8080/console#claim=…`). The first-run guide
    walks through engines (detected on this Mac, with one-click installs), a default model that fits
    the machine, and the apps; the **Models** and **Engines** tabs stay available afterwards.

## First install on Linux

Same one-liner and steps, with these differences:

- Profile: `gpu` when `nvidia-smi` or `rocminfo` works, else `light`. The tray extra is added only
  when a display (`DISPLAY`/`WAYLAND_DISPLAY`) exists.
- Data: `${XDG_DATA_HOME:-~/.local/share}/abstractgateway`.
- Service: a `systemd --user` unit when a user session bus exists
  (`systemctl --user show-environment`). Containers and minimal SSH hosts have none; the script
  then starts the gateway in the background and says so. For always-on servers use the container
  deployment in [Install](../install.md#container-deployment).
- Browser: `xdg-open` when a display exists; otherwise the script prints the URL and an SSH tunnel
  hint (`ssh -L 8080:127.0.0.1:8080 <host>`).
- `--with-ollama` runs Ollama's Linux installer, which uses sudo, installs into `/usr/local` and
  creates a system service. The script announces this before running it.
- Linux ARM64: gateways before 0.3.0 (selected with `--pin`) pull an AbstractCore that caps
  `psutil` below 6, which has no aarch64 wheel, so a C compiler is needed
  (`sudo apt-get install -y gcc`). The preflight warns when none is found.

## First install on Windows

1. In PowerShell:
   `powershell -ExecutionPolicy ByPass -c "irm https://raw.githubusercontent.com/lpalbou/AbstractFramework/main/scripts/install.ps1 | iex"`
2. **Preflight**: Windows build (22H2/19045 or later), architecture, execution policy set by Group
   Policy (reported and explained), long-path support, free disk, port.
3. **uv**: the official `install.ps1` from astral.sh into `%USERPROFILE%\.local\bin`.
4. **Python and gateway**: `uv python install 3.12`, then
   `uv tool install --python 3.12 "abstractgateway[tray]==<pin>"` (`[gpu,tray]` when `nvidia-smi`
   works).
5. **Optional**: `-WithApps` (nodejs-wheel, no UAC), `-WithOllama` (`winget install Ollama.Ollama
   --scope user`, or Ollama's `install.ps1`), `-WithLmStudio` (`winget install
   ElementLabs.LMStudio --scope user`, or LM Studio's headless `install.ps1`).
6. **Autostart**: `abstractgateway service install` (a Startup-folder entry, experimental on
   Windows). With `-NoService` the gateway starts once in a hidden window instead.
7. **Start, health, sign-in, browser**: hidden-window start, `/api/health`, claim link or the token
   file under `%LOCALAPPDATA%\AbstractGateway\auth\`, then the console opens.

To pass options through the one-liner, use a script block:
`& ([scriptblock]::Create((irm https://raw.githubusercontent.com/lpalbou/AbstractFramework/main/scripts/install.ps1))) -WithApps`.

## Apps

Browser apps (Flow, Code, Observer, Continuum, Entity) run on demand with
`npx -y @abstractframework/<app>`; nothing is installed globally. They talk to the gateway at
`ABSTRACTGATEWAY_URL` (default `http://127.0.0.1:8080`). `--with-apps` only makes sure Node 18+
exists.

## Upgrade and repair

Re-run the same one-liner. The script keeps the previous port and profile, installs the pin from
the current manifest (a no-op when it is already installed), restarts the gateway only when the
package changed, and re-checks health. `uv tool upgrade abstractgateway` and the tray's "Check for
updates" are equivalent for uv-tool installs.

## Uninstall

`sh install.sh --uninstall` (or `install.ps1 -Uninstall`) removes the service or Startup shortcut,
stops the gateway, and uninstalls the `abstractgateway` uv tool (and `nodejs-wheel` when the script
installed it). The data directory is kept unless you add `--purge` (`-Purge`). uv, Ollama and LM
Studio stay installed; remove them with their own uninstallers.

## Dry run

`--print` (`-Print`, or `-WhatIf`) runs the read-only preflight and prints every command the
install would run, then stops.

--- docs/installers/components.md ---
# Components and Packaging Matrix

How each component reaches a user's machine under the [script bootstrap](strategy.md).

## Installed by the bootstrap

| Component | Delivered as | Installed by | Notes |
|---|---|---|---|
| uv | Static binary | astral.sh `install.sh` / `install.ps1` | Skipped when already on the machine. Lives in `~/.local/bin` (`%USERPROFILE%\.local\bin`). |
| Python 3.12 | python-build-standalone | `uv python install 3.12` | Isolated from any system Python. |
| AbstractGateway (+ AbstractCore, AbstractRuntime, AbstractAgent, AbstractMemory as dependencies) | PyPI wheel, uv tool | `uv tool install --python 3.12 "abstractgateway[<profile>,tray]==<pin>"` | Exposes `abstractgateway` and `abstractgateway-config`; `--with-core-cli` also exposes `abstractcore`. |
| Gateway service | LaunchAgent / systemd user unit / Startup entry | `abstractgateway service install` | Default (gateway 0.3.0+). `--no-service`, a Linux host without a user systemd session, or an older `--pin` start the gateway in the background instead (Windows: Startup-folder shortcut). |
| Node.js (optional) | `nodejs-wheel` uv tool | `--with-apps` | Only when no Node 18+ is present; no admin, same bin directory. |

## Run on demand

| Component | Command | Notes |
|---|---|---|
| Gateway web console | `http://127.0.0.1:8080/console` | Built into the gateway; the guided setup UI. |
| Flow editor, Code web, Observer, Continuum, Entity | `npx -y @abstractframework/<app>` | Need Node 18+ and a running gateway. |

## Optional tools (flags)

| Component | Flag | Installed with |
|---|---|---|
| Gateway terminal console | `--with-console` | `cargo install --locked abstractgateway-console` (needs Rust; the script prints the command when cargo is missing) |
| AbstractCode terminal client | `--with-code-cli` | `cargo install --locked abstractcode` |

## Third-party engines

| Engine | Flag | macOS / Linux | Windows | Detection (skip when found) |
|---|---|---|---|---|
| Ollama | `--with-ollama` | `curl -fsSL https://ollama.com/install.sh \| sh` (Linux: sudo, system service) | `winget install Ollama.Ollama --scope user`, else `irm https://ollama.com/install.ps1 \| iex` | `ollama` on PATH or `GET :11434/api/version` |
| LM Studio | `--with-lmstudio` | `curl -fsSL https://lmstudio.ai/install.sh \| bash` (headless daemon; Apple Silicon only on macOS) | `winget install ElementLabs.LMStudio --scope user`, else `irm https://lmstudio.ai/install.ps1 \| iex` | `lms`, `~/.lmstudio/bin/lms`, the app bundle, or `GET :1234/v1/models` |

The console's Engines tab offers the same installs later, with the command shown before it runs.

## Native apps (outside the bootstrap)

| Component | Delivery | Notes |
|---|---|---|
| AbstractAssistant | `pip install "abstractassistant[apple]"`; macOS `.app` build | The `.app` is the component that needs Developer ID signing and notarization. |
| Docker image | `ghcr.io/lpalbou/abstractgateway:<version>` | Server deployments; see [Install](../install.md#container-deployment). |

## Availability rules

- The `apple` profile is refused on anything but Apple Silicon with macOS 14+.
- The `gpu` profile warns (and continues) when no `nvidia-smi`/`rocminfo` works; engines then run
  on CPU.
- Engines that do not support the host (LM Studio on Intel Macs) are skipped with a message.

--- docs/installers/security-and-os-blocks.md ---
# OS Security and Installation Blocks

The bootstrap is a script, not a downloaded application, so the OS gates that block unsigned
installers do not apply to it. This page explains what each OS checks, what the scripts do about
it, and where code signing is still required.

## macOS (Gatekeeper)

- Gatekeeper checks **quarantined applications** (downloaded by a browser) for a Developer ID
  signature and notarization. Files fetched by `curl` carry no `com.apple.quarantine` attribute,
  so `curl … | sh` and the binaries it installs (uv, the uv-managed Python, wheels) run without a
  Gatekeeper prompt.
- The optional vendor installers (Ollama, LM Studio) are signed and notarized by their vendors.
- The gateway binds `127.0.0.1`, so the macOS Application Firewall does not ask to accept incoming
  connections.

## Windows (SmartScreen and execution policy)

- SmartScreen and Mark-of-the-Web apply to downloaded files. `irm … | iex` downloads the script
  into memory and writes no file, so SmartScreen does not prompt. The script launches `.exe`
  shims created by uv, not downloaded installers.
- **Execution policy** is the only script gate. Pasted commands always run; `.ps1` files do not
  under `Restricted` (the Windows client default) or `AllSigned`. The one-liner uses
  `powershell -ExecutionPolicy ByPass -c "…"`, which applies to that process only and is not
  persisted. Vendor one-liners run in a child `powershell -ExecutionPolicy ByPass` process.
- **Group Policy** (`MachinePolicy`/`UserPolicy`) overrides `-ExecutionPolicy ByPass`. The script
  detects an `AllSigned` or `Restricted` policy set this way and explains it: the pasted one-liner
  still works, a saved `install.ps1` does not, and managed machines may need an administrator.
- The autostart fallback is a Startup-folder shortcut that runs `powershell.exe -Command` (not a
  `.ps1` file), so the execution policy does not block it. No UAC prompt is needed: uv, Python,
  Node (`nodejs-wheel`), Ollama (`--scope user`) and LM Studio (`--scope user`) all install per
  user. `winget install OpenJS.NodeJS.LTS` is deliberately not used because it installs
  machine-wide and triggers UAC.

## Linux

- Nothing in the default path needs root. Everything installs under `~/.local` and
  `${XDG_DATA_HOME:-~/.local/share}`.
- `--with-ollama` runs Ollama's official installer, which **uses sudo**: it installs into
  `/usr/local`, creates an `ollama` user and a system `ollama.service`. The script prints this
  before running it. LM Studio's headless installer may ask for sudo to install `libatomic1`.
- On ARM64 without a C compiler, gateways before 0.3.0 (selected with `--pin`) fail while building
  `psutil`; install `gcc` from your distribution first. The pinned gateway needs no compiler.

## Sudo and UAC prompts at a glance

| Step | macOS | Linux | Windows |
|---|---|---|---|
| uv, Python, gateway, nodejs-wheel | none | none | none |
| Service / autostart | none (LaunchAgent) | none (`systemd --user`); `loginctl enable-linger` for always-on may need polkit | none (Startup folder) |
| `--with-ollama` | may ask for your password (`/usr/local/bin/ollama` link) | sudo | none (user scope) |
| `--with-lmstudio` | none | may ask sudo for `libatomic1` | none (user scope) |

## Network exposure

- The gateway binds `127.0.0.1` in every bootstrap path.
- One-time sign-in links (`abstractgateway-config claim-url`) are single use, expire after 10
  minutes and are accepted only from a loopback client.
- The admin token file is written with mode `0600`; the scripts print its path, not its content.
- Remote access: use an SSH tunnel (`ssh -L 8080:127.0.0.1:8080 <host>`) or the container
  deployment with explicit auth.

## Integrity

- Every download uses HTTPS from the vendor's official host (astral.sh, pypi.org, ollama.com,
  lmstudio.ai, raw.githubusercontent.com). uv verifies package hashes from the index.
- Review before running: fetch the script, read it, then run it, or use `--print` to see every
  command without changing anything.

## Where code signing still applies

Native double-click artifacts need signatures: the AbstractAssistant `.app` (Developer ID +
notarization + stapling) and any future Windows `.exe`/`.msi` launcher (Authenticode). The
bootstrap and everything it installs need no signature of ours.

## GPU drivers

The scripts never install drivers. They offer the `gpu` profile only when `nvidia-smi` or
`rocminfo` works; otherwise local engines fall back to CPU with a warning.

--- docs/installers/operations-and-support.md ---
# Operations and Support

Where a bootstrap install keeps its files, how to check it, and how to fix common problems. The
install itself is described in [user-journeys.md](user-journeys.md).

## Locations

| What | macOS | Linux | Windows |
|---|---|---|---|
| uv | `~/.local/bin/uv` | `~/.local/bin/uv` | `%USERPROFILE%\.local\bin\uv.exe` |
| Gateway commands (uv tool shims) | `~/.local/bin` | `~/.local/bin` | `%USERPROFILE%\.local\bin` |
| Gateway environment | `~/.local/share/uv/tools/abstractgateway` | same | `%APPDATA%\uv\data\tools\abstractgateway` |
| Gateway data | `~/Library/Application Support/AbstractGateway` | `${XDG_DATA_HOME:-~/.local/share}/abstractgateway` | `%LOCALAPPDATA%\AbstractGateway` |
| Admin token (0600) | `<data>/auth/bootstrap-admin-token` | same | same |
| Logs | `<data>/logs/gateway.log`, `<data>/logs/install-*.log` | same | `<data>\logs\gateway.err.log`, `install-*.log` |
| Bootstrap state (port, mode, profile) | `<data>/bootstrap.env` | same | same |
| AbstractCore config | `~/.abstractcore/config/abstractcore.json` | same | `%USERPROFILE%\.abstractcore\config\abstractcore.json` |

`--data-dir` (or `AF_DATA_DIR`) moves the gateway data directory.

## Health checks

```bash
curl http://127.0.0.1:8080/api/health          # the gateway answers
uvx abstractframework doctor                    # host, tools, gateway, engines
abstractgateway-config status --json            # data dir, auth, defaults
```

`abstractframework doctor` only reads: it sends `GET /api/health` to `ABSTRACTGATEWAY_URL`
(default `http://127.0.0.1:8080`), `GET /api/version` to Ollama and `GET /v1/models` to LM Studio.
Add `--json` for tooling and `--no-network` to skip the HTTP probes.

## Troubleshooting

- **`abstractgateway: command not found`**: open a new terminal (the PATH change applies to new
  shells) or call `~/.local/bin/abstractgateway` directly.
- **Port 8080 in use**: the script picks the next free port and records it in `bootstrap.env`;
  pass `--port` to choose one. The summary prints the URL.
- **Gateway exited during start**: the script prints the last log lines; the full log is
  `<data>/logs/gateway.log`.
- **No gateway after reboot**: the gateway starts at login only when a service or Startup entry was
  registered (see the `Mode:` line of the summary). Re-run the script, or start it by hand.
- **Browser apps cannot connect**: check `ABSTRACTGATEWAY_URL` and that the gateway is healthy.
- **Local engine not reachable**: start Ollama or the LM Studio server; the console's Engines tab
  and `abstractframework doctor` show what is reachable.
- **Linux ARM64 build error for psutil** (gateways before 0.3.0, selected with `--pin`): install a
  C compiler (`sudo apt-get install -y gcc`) and re-run.

## Uninstall

`sh install.sh --uninstall [--purge]` or `install.ps1 -Uninstall [-Purge]`. Data is kept unless
you purge it.

--- docs/installers/release-and-manifest.md ---
# Release and Install Manifest

The install manifest is the machine-readable contract between a release and everything that
installs it: the bootstrap scripts, the docs, and any tool that wants the pinned versions. It is
generated from the root `abstractframework` release pins, never edited by hand:

```bash
abstractframework manifest                                   # print it
abstractframework manifest --write docs/installers/install-manifest.json
abstractframework manifest --check docs/installers/install-manifest.json
```

## Fields (schema version 2)

`install-manifest.json` is generated from `abstractframework/__init__.py` (`__version__`,
`RELEASE_VERSIONS`, `PACKAGE_DISTRIBUTIONS`, `NPM_RELEASE_VERSIONS`) and
`abstractframework/install_manifest.py`, and validated by `install-manifest.schema.json`.

| Field | Description |
|---|---|
| `schema_version` | `2` |
| `minimum_installer_version` | Oldest consumer that understands this manifest |
| `framework` | The `abstractframework` distribution, its version and `python_requires` |
| `source` | Repository URL and the Python symbol the pins come from |
| `profiles` | `light`, `apple`, `gpu`: pip requirement, platforms, prerequisites, whether local inference is installed |
| `python_packages` | Every pinned PyPI package: id, distribution name, version |
| `npm_apps` | Every npm app released with this version and its `npx` command |
| `bootstrap` | What the one-line scripts install: `gateway_version`, `python` (`3.12`), `tool_requirement`, `profile_extras`, `optional_extras`, `default_port`, script URLs and one-liners, the shared flag table (`sh`/`ps`/env names), the step list, and the uninstall commands |
| `post_install` | Console-first next steps: `entrypoint: "console"`, the `serve` command on `127.0.0.1`, `health_url`, `console_url`, the `claim` command and its token-file fallback, the `service` command, `doctor`, and the `npx` app commands |
| `security` | Whether secrets or signed native artifacts are present |

`scripts/install.sh` reads `bootstrap.gateway_version` from the manifest next to it and embeds the
same value for `curl | sh` use; `scripts/install.ps1` does the same. `python -m pytest -q` and
`bash scripts/tests/test_inventory.sh` fail when the scripts, the manifest and the root pins
disagree.

## Updating it for a release

1. Release the lower packages first, in the order of
   [ADR-0034](../adr/0034-framework-release-sequence-and-gates.md).
2. Change the pins in `pyproject.toml` and the matching dictionaries in
   `abstractframework/__init__.py`.
3. Regenerate the manifest: `abstractframework manifest --write docs/installers/install-manifest.json`
   (from the repository checkout, so it reads the edited source).
4. Update the embedded pins in `scripts/install.sh` (`AF_GATEWAY_PIN_DEFAULT`, `AF_NPM_APPS`,
   crates) and `scripts/install.ps1` (`$AfGatewayPinDefault`, `$AfNpmApps`, crates).
5. Run `python -m pytest -q` and `bash scripts/tests/test_inventory.sh`. CI's `bootstrap-smoke`
   job installs the published pin on Ubuntu, macOS and Windows.

## Signed artifacts

The bootstrap installs PyPI and npm packages and vendor installers, so the release pipeline
publishes no signed installers. Native apps that need signing (the AbstractAssistant `.app`)
publish their artifacts on their own release pages; see
[security-and-os-blocks.md](security-and-os-blocks.md#where-code-signing-still-applies).

--- 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 on one machine, offline-first, with Ollama (or an
OpenAI-compatible local server) as the model backend.

AbstractCode is a client of AbstractGateway: the gateway runs the coding agent on your machine and
the terminal client connects to it over HTTP/SSE. Nothing leaves the machine when the model server
is local too.

## Prereqs

- Python 3.10+ (for the gateway)
- Rust 1.87+ (for `cargo install abstractcode`), or a prebuilt binary from the
  [AbstractCode GitHub release](https://github.com/lpalbou/AbstractCode/releases)
- An LLM backend:
  - Ollama (recommended)
  - LM Studio / vLLM / LocalAI (OpenAI-compatible)

## Step 1: Install

```bash
pip install abstractframework     # pinned stack, includes abstractgateway
cargo install abstractcode        # terminal client
```

`pip install abstractgateway` is enough if you only want the gateway.

## Step 2: Start a local model

### Ollama

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

Pick it as the default text model:

```bash
abstractcore --config
```

## Step 3: Start the gateway on loopback

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

The gateway ships the `coding-agent:coder` workflow that AbstractCode uses by default.

## Step 4: Run AbstractCode

```bash
abstractcode doctor               # checks the gateway connection and available workflows
abstractcode                      # connects to http://127.0.0.1:8080
```

Prefer a browser? `npx @abstractframework/code` serves the same client on
`http://127.0.0.1:3002`.

## Step 5: Work with files and tools

- Type a task and press Enter; reasoning cycles and tool cards stream in live.
- Tools are approval-gated by default: approve or reject each call, from either client.
- Type `/help` for commands.

## What "durable" means here

- The run lives in the gateway, not in the client. Close the terminal and reattach later; the
  session keeps its full history.
- A run gated on your approval in the terminal can be approved from the browser client, and the
  other way round.

## When to go further

Use the same 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",
  "reasoning": "medium",
  "options": {
    "voice": "M1"
  }
}
```

`provider`, `model`, and `base_url` are shared fields. `reasoning` is an optional non-secret
default for reasoning-capable text routes. `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 \
  --reasoning medium
```

`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
- Specialized applications may intentionally preselect or hardcode a
  `bundle_id`/`flow_id` when that workflow is the product contract. This is a
  valid use of WorkflowBundles; what matters is that the app still starts a
  normal durable Gateway run instead of bypassing workflow execution.
- 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. Finish the second wave of the Core/Runtime route substrate: direct Core, local Runtime, remote Runtime, and Core server must consume the same resolved-route semantics and denial rules.
2. Lock the Gateway action-descriptor contract over the implemented Core `request/output` and Runtime `resolved_actions` substrate instead of letting clients keep private capability taxonomies.
3. Formalize the replayable Gateway session/action envelope on top of Runtime `history_bundle` and `resolved_actions` so capability-aware runs stay explainable across lightweight apps.
4. Align Gateway-first apps and Flow authoring on the same durable action/replay handoff instead of app-local multimodal routers, semantic caches, or transport-specific heuristics.
5. Close current coverage gaps such as `generate_sound` and define the extension rules for future modalities like `scene3d`, characters, and environments, while keeping workflows as the product layer and AbstractCore as the lighter non-durable entrypoint.

## Active Planned Work

| ID | Item | Status | Notes |
|----|------|--------|-------|
| 0851 | [Rotate agora API keys leaked in sibling repo histories](planned/0851_rotate_agora_keys_leaked_in_sibling_repo_histories.md) | Planned (operator decision gate) | Seven sibling repos pushed `.cursor/mcp.json` with agora keys; rotate them and decide per repo on history rewrite. High priority. |
| 0850 | [Redesign the unresolvable `abstractcore[all]` extra](planned/0850_abstractcore_all_extra_is_unresolvable.md) | Planned (not started; still open in abstractcore 2.14.0) | MLX (transformers>=5, llguidance>=1.7) and vLLM <=0.19 (transformers<5) cannot share one extra; vLLM 0.30 needs openai>=2.25 vs the `openai<2` pin. Root profiles unaffected. |
| 0852 | [Harmonize install-profile extras on `apple` / `gpu`](planned/0852_harmonize_install_profile_extras_naming.md) | Planned (not started; the 0.2.0 wave kept the existing extras) | After 0.1.12: rename `all-apple`/`all-gpu` to `apple`/`gpu` in core, voice, vision, music, memory, 3d with one-release aliases; dependents and root move in the same wave. |
| 0856 | [Validate the Windows bootstrap and gateway service on real machines](planned/0856_validate_windows_bootstrap_on_real_machines.md) | Planned | `install.ps1` has CI (`windows-latest`, `-NoService`) and container parse/dry-run evidence only; validate PS 5.1/7 on Windows 10 22H2 and 11 (x64/ARM64), the Startup service entry, winget installs, NTFS token permissions. |
| 0857 | [crates.io trusted publishing for the console crates](planned/0857_crates_io_trusted_publishing_for_console_crates.md) | Planned | `abstractcore-console` 0.2.0 and `abstractgateway-console` 0.7.0 were published with a local token; configure OIDC trusted publishing in both release workflows. |
| 0858 | [Console toggle for `allow_engine_install`](planned/0858_console_toggle_for_allow_engine_install.md) | Planned | Today only `POST /admin/runtime-config` changes it; add an audited admin switch in the web and terminal consoles. |
| 0859 | [Launcher port defaults match the stack map](planned/0859_launcher_port_defaults_match_the_stack_map.md) | Planned | `apps_common.sh` / `gateway-flow*.sh` defaults differ from the documented stack ports; `local_pythonpath` is stale. |
| 0860 | [ADR-0034: release order from the inventory tiers](planned/0860_adr_0034_release_order_from_the_inventory_tiers.md) | Planned | The ADR's hand-written order is stale; `scripts/lib/packages.txt` + `deps.sh` are authoritative. |
| 0861 | [Compiled packages out of the default `all-apple` / `all-gpu` extras](planned/0861_compiled_packages_out_of_the_default_extras.md) | Planned | `llama-cpp-python`, `stable-diffusion-cpp-python`, `aec-audio-processing` are source-only on PyPI; the pip route needs a compiler until they become opt-in (later wave, owner decision 2026-09-24). |
| 0848 | [Supervisor must not blame model inference for a blocked gateway](planned/0848_supervisor_must_not_blame_inference_for_a_blocked_gateway.md) | Planned (not started) | The unhealthy banner asserts a cause it cannot observe; it was wrong 4/4 on 2026-09-17 (the cause was a workflow publish rebuilding the host on the event loop). Point at `runtime/audit_log.jsonl` and the ledgers. |
| 0849 | [Test suites must not reach the operator's live stack](planned/0849_test_suites_must_not_reach_the_operators_live_stack.md) | Planned (abstractassistant guarded; other packages not audited) | An assistant GUI test republished the live gateway's managed workflow twice, costing two ~60 s stalls. abstractassistant is now guarded; audit every other package. |
| 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. |
| 0201-0207,0209-0210 | [Multimodal capability projection and workflow-callability track](planned/multimodal-capability-projection/README.md) | In progress | The first Core/Runtime substrate wave is landed: Core `request=` + resolved-route metadata and Runtime `resolved_actions` export. Gateway descriptors, replay envelopes, and client adoption still remain. |
| 0212-0222 | [Agency parity track (Codex-0.89 gaps)](planned/agency-parity/README.md) | In progress | Implemented + tested: 0212 (prefix-cache stability), 0213 (context fidelity/thought retention), 0214 (parallel read-only tools), 0216 (edit_file safety + CRLF/dash-line follow-ups), 0217 (verifier + update_plan + inject_guidance + default RetryPolicy), 039 (arg coercion + PEP-563 schema-type fix), 0215 engine + generalized output offload, 0220 (persistent-shell tools — opt-in, approval-gated, live-verified on OVH gpt-oss-120b). LIVE evidence recorded 2026-07-08 (track README "Live evidence"): prefix stability 12/12 + OpenAI cached_tokens 60.9%, thought retention, 2.09× parallel tools, verifier live catch, edit_file traps 3/3. Remaining: 0218 (design-first context-budget survival), 0219 (retrieval/project memory — owned elsewhere), unified prompt-caching strategy (0221). ADR-0026 binding: no lossy truncation in the loop. |
| 0232 | [Sandbox `execute_command` and fix workspace path containment](planned/0232_execute_command_sandboxing_and_workspace_path_containment.md) | Planned | P0 security. `execute_command` is raw `sh -c` with the full inherited environment and no filesystem containment, so a `write_file` refused by `workspace_only` succeeds via shell heredoc (observed live 2026-07-30); `~/.abstractcode/gateway.json` and `~/.codex/auth.json` are readable, subsuming the `fetch_url` approval gate. Path containment is string-based, so case-variant and Unicode-NFD denylist bypasses work on APFS and `workspace_allowed_paths` pointing at the workspaces base exposes 1,746 sibling workspaces. Proposes fail-loud workspace clamping, identity-based (`st_dev`,`st_ino`) containment with `openat`+`O_NOFOLLOW`, per-invocation OS sandbox (sandbox-exec/bubblewrap) with container fallback, environment scrubbing, and approval tiering that forbids auto-approving `execute_command` while unsandboxed. |
| 0233 | [Real run cancellation: abort in-flight LLM calls](planned/0233_real_run_cancellation_abort_in_flight_llm_calls.md) | Planned | P1. `/cancel` is a state write only — `cancel_run` sets `status=CANCELLED` and returns while the tick thread stays blocked in `llm.generate()`, so the generation runs to completion and keeps burning GPU/paid tokens (observed: 5 overlapping generation pairs, largest 211s). Threads a cancellation token through abstractcore (HTTP abort, extending the existing `cancel_event`), abstractruntime, abstractgateway and abstractcode-tui, with an honest `cancelled_in_flight` ledger outcome and a client message that distinguishes 'marked cancelled' from 'stopped'. |

## 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. |

## Multimodal Capability Projection Planned Track

| ID | Item | Status | Notes |
|----|------|--------|-------|
| 0210 | [Core request/output contract and Gateway projection alignment](planned/multimodal-capability-projection/0210_core_request_output_contract_and_gateway_projection_alignment.md) | In progress | First-wave Core `request=` support, Core resolved-route metadata, reasoning-capable route defaults, and Runtime resolved-action export are landed; Gateway projection work remains. |
| 0201 | [Gateway action descriptor contract](planned/multimodal-capability-projection/0201_gateway_action_descriptor_contract.md) | Planned | Create the versioned Gateway-owned typed action contract and lock the durable boundary between Gateway actions, Core lower-level request/output semantics, tools, workflows, and canonical Runtime intent. |
| 0202 | [Gateway replayable session and action envelope](planned/multimodal-capability-projection/0202_gateway_replayable_session_and_action_envelope.md) | In progress | Runtime now exports `resolved_actions`; Gateway still needs to project the full portable decision/session envelope over that substrate. |
| 0207 | [Cross-client replayable action handoff](planned/multimodal-capability-projection/0207_cross_client_replayable_action_handoff.md) | Planned | Align live and replay consumers on the same bounded action handoff instead of forcing each lightweight app to reconstruct capability decisions locally. |
| 0203 | [Flow action descriptor alignment](planned/multimodal-capability-projection/0203_flow_action_descriptor_alignment.md) | Planned | Keep typed Flow media nodes but source availability, preflight, and task metadata from the shared action contract. |
| 0204 | [Runtime capability intent resolution and policy](planned/multimodal-capability-projection/0204_runtime_capability_intent_resolution_and_policy.md) | In progress | Runtime now derives and exports bounded resolved-action records from Core route metadata; broader action-policy and Gateway-fed action resolution remain. |
| 0205 | [Multimodal action coverage and future modality extension](planned/multimodal-capability-projection/0205_multimodal_action_coverage_and_future_modality_extension.md) | Planned | Close coverage gaps such as `generate_sound`, define route-backed truth rules like `voice_clone`, and set the extension rule for `scene3d` and later families. |
| 0209 | [Super-agent workflow-first adoption over the shared action/route substrate](planned/multimodal-capability-projection/0209_super_agent_workflow_adoption_over_shared_action_route_contract.md) | Planned | Align specialized orchestrator workflows with the shared action/route substrate while keeping `basic-agent` a simple baseline and avoiding private workflow-local routing logic. |

## Agency Parity Planned Track

Close flagship ReAct agent-loop gaps vs a Codex-CLI-0.89-class harness (quality + speed). ADR-0026 is
binding: no lossy truncation inside the loop. Source analysis:
`fable-opinions/agency_deep_dive_and_codex_comparison.md`.

| ID | Item | Status | Notes |
|----|------|--------|-------|
| 0212 | [ReAct prompt-prefix cache stability](planned/agency-parity/0212_react_prompt_prefix_cache_stability.md) | Done (tested) | Iteration/scratchpad/grounding moved out of the cached prefix; caching default-on. A/B: system prompt byte-stable across iterations (was growing). |
| 0213 | [ReAct context fidelity + thought retention](planned/agency-parity/0213_react_context_fidelity_thought_retention_dedup.md) | Done (tested) | Reasoning kept in the transcript; scratchpad no longer double-carried into the system prompt; ADR-0026 tags added. |
| 0214 | [Parallel read-only tool execution](planned/agency-parity/0214_parallel_readonly_tool_execution.md) | Done (tested) | Concurrent read-only batches (bounded pool), side-effects strictly sequential/ordered; timing proof. |
| 0215 | [Persistent exec session + output offload](planned/agency-parity/0215_persistent_exec_session_and_output_offload.md) | Done (engine + offload) | PTY shell engine (cwd/env/stdin/exit-code, hardened) + generalized output offload: stdout+stderr on any exit code, any tool's large string output, 50 MB retention cap, explicit >cap push-back notice (never silent). Tool exposure split to 0220. |
| 0216 | [edit_file safety + patch robustness](planned/agency-parity/0216_edit_file_safety_and_patch_robustness.md) | Done (tested) | Default single replacement + ambiguity fail; context-anchored diff; JSON/YAML parse-refuse. 33 tests. Follow-ups resolved 2026-07-08: CRLF preservation (dominant-style restore, mixed-endings note) + `-- `-deletion-line diff parse. +12 tests. |
| 0217 | [ReAct verifier/plan/steering/retry](planned/agency-parity/0217_react_verifier_plan_steering_and_retry.md) | Done (tested) | CodeAct verifier + update_plan wired into ReAct; gateway inject_guidance command; default RetryPolicy (llm=3, tools=1). |
| 0218 | [Interactive context-budget survival](planned/agency-parity/0218_interactive_context_budget_survival.md) | Planned (design-first) | num_ctx + typed overflow + eviction-to-artifact before any lossy summarization. |
| 0219 | [ReAct retrieval + project memory](planned/agency-parity/0219_react_retrieval_and_project_memory.md) | Planned (record only) | abstractmemory-backed recall + AGENTS.md loading; owned elsewhere. |
| 0220 | [Persistent shell tool exposure](planned/agency-parity/0220_persistent_shell_tool_exposure.md) | Done (tested + live) | `shell_exec`/`shell_write_stdin`/`shell_close`: opt-in (`ABSTRACT_ENABLE_SHELL_TOOLS`), approval-gated, run-id namespaced, terminal-hook teardown incl. cancel, non-durable with explicit new-session notice. 14 tests + live OVH gpt-oss-120b venv proof; A/B showed the one-shot arm silently polluting the wrong environment. |
| 0222 | [Mid-loop steering + soft interrupt](planned/agency-parity/0222_mid_loop_steering_and_soft_interrupt.md) | Design converged (TOP priority) | Type into a running loop + redirect/stop without cancel. 2 adversarial reviews folded (single-writer sidecar + vars watermark + in-loop drain hook + tree fan-out + wait semantics); Codex 1:1 verified; 21 required tests listed; hot-file handshake posted to runtime/gateway (commons 82). |
| 0221 | [Unified prompt-caching strategy](planned/agency-parity/0221_unified_prompt_caching_strategy.md) | Wave 1 done (live-verified) | Anthropic top-level cache_control was an ACTIVE cost increase (full-prompt write premium every call, zero reads — live-falsified); now an explicit breakpoint on the system head (write→read verified) + normalized `cached_input_tokens`/`cache_write_tokens` across Anthropic/OpenAI/compatible. Mechanism = provider class; registry = tuning values; endpoint quirks = profiles. Waves 2-3 planned. |
| 039 | [Tool argument type coercion](planned/039_tool_argument_type_coercion.md) | Done (tested) | Centralized schema-aware coercion at dispatch + PEP-563 schema-type inference fix. Both registry and runtime paths share it. |

## 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. |
| 0206 | [Micro-model gating and hierarchical multimodal planning](proposed/0206_micro_model_gating_and_hierarchical_multimodal_planning.md) | Proposed | Preserve the idea of deterministic pre-gates plus cheap classifiers and richer coordinators, but defer it until the shared action/policy contract is real enough to support stable gating inputs. |
| 0208 | [AbstractCore lightweight capability surface boundary](proposed/0208_abstractcore_lightweight_capability_surface_boundary.md) | Proposed | Preserve the Gateway/Core entrypoint split by keeping Gateway actions durable/high-level and only promoting a narrower Core-side discovery contract if direct Core demand proves it is needed. |
| 0211 | [Public generate route override surface](proposed/0211_public_generate_route_override_surface.md) | Proposed | Keep structured temporary route overrides deferred until the internal resolved-route contract, topology parity, and Gateway policy ceilings are stable; if promoted later, prefer a separate advanced override surface over nesting route objects inside `request` or `output`. |
| 0230 | [Spatial memory: rooms and objects as entity recall anchors](proposed/0230_spatial_memory_rooms_for_entities.md) | Proposed | Maintainer proposal (team vote): give each summoned entity an inhabitable space it can populate with valued objects/texts/sounds; wandering/passing objects becomes a place/object recall channel (like the participants channel), fighting inward loops and giving human-like rootedness. Layer 1 = object-anchor graph channel (2D/no 3D); later layers use meshvault/abstract3d/abstractvision/abstractmusic. Non-goal: anchors never become authoritative memory or identity. |
| 0862 | [Gateway roles: admin, member (and maybe viewer), with admin-authorised choices](proposed/0862_gateway_roles_members_and_admin_authorised_choices.md) | Proposed (operator decisions pending) | Named roles and a gateway-wide provider/model allow/deny list that members choose within; clamp member `tool_policy` and workspace self-service; per-role route contract test; prompt for a daily-use member account when network mode leaves localhost (not at first run); member invite links. Records two authorization defects found (token-only session login; `PROTECT_READ=0` reads as admin). |

## 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 (superseded by ADR-0038 for the bootstrap; still relevant to signed native apps only) | 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 |
|----|------|-----------|-------|
| 0855 | [One-line install, first-run console and model/engine management](completed/0855_one_line_install_and_model_management_wave.md) | 2026-09-23 | Root 0.2.0: uv-based `install.sh`/`install.ps1` defaulting to gateway 0.3.0 (service + claim link), doctor probes, manifest v2, `CRATE_RELEASE_VERSIONS`, 30-package inventory with `abstractcore-console`, docs; pins core 2.14.0, runtime 0.4.33, gateway 0.3.0. |
| 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 consume Gateway auth,
defaults, permissions, catalogs, and policy-signed workflow governance instead
of each owning global identity or route-merging logic. Client-local workflow
selection state is still acceptable when it selects among Gateway-exposed
workflows/defaults.

## 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.
- Hosted Gateway still resolves some temporary route decisions by mutating
  transient Runtime params rather than by producing one shared resolved-route
  object that survives replay and cross-topology execution.

## 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 route metadata reports non-secret provenance so UIs and replay
  surfaces can show whether a text route or a specific modality route came from
  request, workflow, user, tenant, Gateway baseline, or Core.
- Per-principal defaults must feed the same shared route resolver used by
  direct Core, local Runtime, remote Runtime, and Core server execution; the
  hosted path must not become a separate merge algorithm.
- Gateway should use one shared resolved-route merge path for request-time and
  run-start route changes, rather than letting each app spread ad hoc
  provider/model/base URL fields across local payloads. Gateway configures and
  supplies default layers, but the canonical resolved-route object and merge
  semantics remain a shared Core/Runtime contract.
- If a future public structured override payload is exposed, it must remain an advanced,
  policy-gated surface layered on top of that shared resolver rather than a second merge model.
- Optional non-secret `reasoning` defaults for reasoning-capable models may be
  configured through the same route-default cascade, but they must flow through
  the shared Core/Runtime route object rather than a Gateway-only execution
  knob.
- Explicit workflow pins remain reproducible unless denied by current host
  policy, in which case denial is explicit rather than silently rerouted.
- This does not forbid explicit hardcoded workflow selection where a product
  endpoint intentionally targets a specific published workflow or catalog
  default; the restriction is against silent rerouting or app-local
  reimplementation of route merge/policy logic.
- 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.
- Durable agent bundles, workflow subruns, and future super-agent workflows must consume the same
  shared resolved-route path; they must not gain a separate hosted-only merge algorithm.
- Request-scoped provider/model/base URL/profile overrides must be evaluated as
  temporary route layers over those defaults, with explicit allow/deny outcomes
  and redacted replay/audit provenance.

## 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. For temporary per-call changes, prefer one
route-override payload normalized into the same shared resolved-route object
required by Runtime/Core route resolution instead of new app-local override
shapes.

## 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`
- `../../proposed/0211_public_generate_route_override_surface.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.
- One-off per-modality overrides can inherit the broader default route set while
  remaining visible as explicit request-time decisions instead of hidden ambient
  config mutations.

## 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 the lower-level
route-resolution contract; Gateway owns authenticated hosted UX,
per-principal access control, and projection of scoped defaults into that
shared contract.

--- 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 ~/.codex/skills/.system/skill-creator/scripts/quick_validate.py ~/.codex/skills/architect` -> passed.
- `python ~/.codex/skills/.system/skill-creator/scripts/quick_validate.py ~/.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

