diff --git a/CHANGELOG.md b/CHANGELOG.md index f6974750c..003039207 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Docs +- **Documentation audit — stale claims fixed, missing reference material added.** The docs + landing no longer claims Discord/Google ship first-party (they're official external + installs) or that cost-v1 rides a DataPart (it's the artifact metadata map); the README + points plugin publishers at `config/plugin-directory.yaml` instead of the generated + overlay; the desktop README's retired `PROTOAGENT_CONFIG_DIR` → `PROTOAGENT_HOME`; the + releasing guide's branch-protection table lists the real seven CI checks (incl. the + changelog gate) instead of three; `tools.hidden` + `settings.hidden` are documented in + the configuration reference at last; the ADR index gains its five missing rows + (0079/0086/0087/0088/0092), corrects eight shipped ADRs still marked Proposed, and is + re-sorted; the orphaned `protoagent` CLI guide joins the sidebar and the guides overview + (with four other unlisted guides); and the plugins/fleet/desktop guides pick up the + Installed table, archetype `requires`, and launch-time update check. + ### Changed - **Plugin views now receive the console's full theme, not six curated colors (#2225).** The ADR 0026 theming bridge — `consoleTheme()`, carried by the `protoagent:init` and diff --git a/README.md b/README.md index b91ecaa99..92ac5bcf8 100644 --- a/README.md +++ b/README.md @@ -126,7 +126,7 @@ deprecated alias for `console`. See [Run headless](./docs/guides/headless.md). ┌──────────────┐ A2A JSON-RPC + SSE ┌─────────────────┐ │ Consumer │ ──────────────────────────▶ │ A2A handler │ │ (any A2A │ │ (FastAPI) │ -│ client) │ ◀──── cost-v1 DataPart ─────│ │ +│ client) │ ◀─── cost-v1 (metadata) ────│ │ └──────────────┘ └────────┬────────┘ │ ▼ @@ -188,8 +188,11 @@ you. See [Build a communication plugin](./docs/guides/communication-plugins.md) ([ADR 0029](./docs/adr/0029-communication-plugins-standard.md)). **Publish your own:** tag your repo with the [`protoagent-plugin`](https://github.com/topics/protoagent-plugin) -GitHub topic, then open a PR adding it to [`plugins.json`](./sites/marketing/data/plugins.json) -to list it on the directory. See [Install & publish plugins](./docs/guides/plugin-registry.md), +GitHub topic, then open a PR adding an entry to +[`config/plugin-directory.yaml`](./config/plugin-directory.yaml) and run +`python scripts/plugin_directory.py build` — that one entry drives both the in-app +Discover catalog and the website directory (the JSON files are generated; CI fails on +drift). See [Install & publish plugins](./docs/guides/plugin-registry.md), [Plugins](./docs/guides/plugins.md), [Console views](./docs/guides/plugin-views.md). ## A2A extensions shipped by default diff --git a/ROADMAP.md b/ROADMAP.md index 1f8598991..fe0c7d944 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -13,7 +13,7 @@ In-progress ref points at a closed issue — rotate shipped work into `## Shippe - **Multi-window desktop chat** — "Open in New Window" spawns a real second desktop window with its own chat surface. (#1706) - **Federation token follow-ups** — management UI, peer rotation, and fleet integration for ADR 0066 tokens. (#1504) - **Backend-agnostic tracing** — generic OTLP / OpenInference trace export instead of hard-coding the Langfuse SDK. (#1884) -- **Ollama & Hugging Face listings** — register protoAgent as an Ollama community integration and a Hugging Face "Use this model" local app. (#1833, #1834) +- **Ollama & Hugging Face listings** — register protoAgent as an Ollama community integration and a Hugging Face "Use this model" local app. (#1990) ## In progress diff --git a/TEMPLATE.md b/TEMPLATE.md index d23927105..4cc4a562c 100644 --- a/TEMPLATE.md +++ b/TEMPLATE.md @@ -223,9 +223,10 @@ After forking, review the skill loop lifecycle: drop them in by hand; the agent can also distill a proven workflow into a new one via `/distill`. All land in the skill index (`/sandbox/skills.db`, SQLite + FTS5) as `source=disk`. -2. **Retrieval** — `KnowledgeMiddleware` injects the top-k most relevant skills - before each LLM call (shown as a "skills loaded" chip in chat), so the agent - reuses proven workflows. +2. **Retrieval** — progressive disclosure (ADR 0060): `KnowledgeMiddleware` + keeps an always-on `` index of every skill's name + summary + (most-recently-used first), and the agent pulls a full procedure on demand + with `load_skill` — full skill bodies are never bulk-injected. 3. **Curation** — run `python -m graph.skills.curator` periodically (or via cron) to deduplicate near-identical skills, apply the 90-day confidence half-life decay, and prune stale non-pinned entries below confidence 0.2. diff --git a/apps/desktop/README.md b/apps/desktop/README.md index 9cf653ea0..ddf668d8b 100644 --- a/apps/desktop/README.md +++ b/apps/desktop/README.md @@ -24,7 +24,7 @@ npm run desktop:dev The app **bundles and launches the protoAgent server itself** as a Tauri sidecar — no separately-running server required. - `apps/desktop/sidecar/build_sidecar.py` freezes the server into a single binary via PyInstaller, named `binaries/protoagent-server-` (the `externalBin` Tauri bundles). The React console is the UI, so the binary stays lean (~60 MB) rather than carrying a heavier server-rendered UI stack. -- On launch the Rust shell (`src-tauri/src/lib.rs`) spawns the sidecar on the **fixed port 7870** with `--ui console --port 7870` (the console UI tier — API + A2A + console; ADR 0010), sets `PROTOAGENT_CONFIG_DIR` to the per-user app-config dir (so the read-only binary still persists setup/secrets), drains its output to the log, and kills it on app exit. (The dynamic-free-port + window-injection handoff proved unreliable across Tauri v2 webview contexts, so the port is pinned to the web client's fallback — see the comment in `lib.rs`.) +- On launch the Rust shell (`src-tauri/src/lib.rs`) spawns the sidecar on the **fixed port 7870** with `--ui console --port 7870` (the console UI tier — API + A2A + console; ADR 0010), sets `PROTOAGENT_HOME` to the per-user app-config dir (so the read-only binary still persists setup/secrets), drains its output to the log, and kills it on app exit. (The dynamic-free-port + window-injection handoff proved unreliable across Tauri v2 webview contexts, so the port is pinned to the web client's fallback — see the comment in `lib.rs`.) - The shell creates the window itself and injects `window.__PROTOAGENT_API_BASE__` (the chosen `http://127.0.0.1:`) before any page script runs; the webview's React build reads it (`apps/web/src/lib/api.ts`) and calls the sidecar's `/api`, `/a2a`, and `/v1`. The console probes with backoff on startup so the few-second cold start doesn't surface as an error. The sidecar binary is gitignored — it's a build artifact produced per platform by step 1 (locally or in CI before `tauri build`). @@ -43,7 +43,7 @@ The app updates itself in place (tauri-plugin-updater): a silent check at launch (release builds only) plus a tray "Check for Updates…" item. It polls `latest.json` on the GitHub Release, verifies the bundle's minisign signature against the org public key baked into `tauri.conf.json`, installs, and -relaunches — agent data (`PROTOAGENT_CONFIG_DIR`, workspaces) is untouched. +relaunches — agent data (`PROTOAGENT_HOME`, workspaces) is untouched. - Updater bundles are only produced in CI when the org `TAURI_SIGNING_PRIVATE_KEY` is present; a release without them simply has no in-app update (the `latest.json` diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts index f6e3a1e6a..9a0295767 100644 --- a/docs/.vitepress/config.mts +++ b/docs/.vitepress/config.mts @@ -152,6 +152,7 @@ export default defineConfig({ text: "Operate & deploy", collapsed: true, items: [ + { text: "The protoagent command (CLI)", link: "/guides/cli" }, { text: "Deploy via GHCR", link: "/guides/deploy" }, { text: "Deploy in Docker (seed + UI override)", link: "/guides/deploy-docker" }, { text: "Deploy on Proxmox (reusable LXC template)", link: "/guides/deploy-proxmox" }, diff --git a/docs/adr/0057-command-palette.md b/docs/adr/0057-command-palette.md index 80e033788..859ee438b 100644 --- a/docs/adr/0057-command-palette.md +++ b/docs/adr/0057-command-palette.md @@ -1,6 +1,6 @@ # ADR 0057 — Command palette (⌘K): plugin-extensible quick command -- **Status:** Proposed (2026-06-18) +- **Status:** Accepted (proposed 2026-06-18; shipped v0.48.0) - **Date:** 2026-06-18 - **Deciders:** Josh Mabry; protoAgent maintainers - **Tags:** ux, command-palette, plugins, desktop, navigation, extensibility, ds diff --git a/docs/adr/0058-runtime-plugin-install-frozen-app.md b/docs/adr/0058-runtime-plugin-install-frozen-app.md index 43cba7a83..6afc70806 100644 --- a/docs/adr/0058-runtime-plugin-install-frozen-app.md +++ b/docs/adr/0058-runtime-plugin-install-frozen-app.md @@ -1,6 +1,6 @@ # 0058 — Runtime plugin install in the frozen desktop app (Discord as the first external comms plugin) -Status: **Proposed** +Status: **Accepted** (shipped v0.48.0) ## Context diff --git a/docs/adr/0059-unified-plugin-manager.md b/docs/adr/0059-unified-plugin-manager.md index 141138dcb..78f753d39 100644 --- a/docs/adr/0059-unified-plugin-manager.md +++ b/docs/adr/0059-unified-plugin-manager.md @@ -1,6 +1,6 @@ # 0059 — Unified plugin manager: in-app directory (Discover) + folded config (Installed) -Status: **Proposed** +Status: **Accepted** (shipped v0.48.0; extended through v0.112.0) ## Context diff --git a/docs/adr/0068-developer-flags-and-panel.md b/docs/adr/0068-developer-flags-and-panel.md index b005d7602..f55aea868 100644 --- a/docs/adr/0068-developer-flags-and-panel.md +++ b/docs/adr/0068-developer-flags-and-panel.md @@ -1,6 +1,6 @@ # 0068 — Developer flags & the Developer panel (gating pre-release functionality) -Status: **Proposed** +Status: **Accepted** (shipped — #1506, Developer panel + flag backend) > Resolves #1506. Features pass through stages — internal-testing → opt-in preview → GA — but > the repo has **no first-class way to gate a half-built feature**. Today it either ships diff --git a/docs/adr/0076-managed-git-acp-delegates.md b/docs/adr/0076-managed-git-acp-delegates.md index 4b7c41441..b3e43a575 100644 --- a/docs/adr/0076-managed-git-acp-delegates.md +++ b/docs/adr/0076-managed-git-acp-delegates.md @@ -1,6 +1,6 @@ # 0076 — Managed git for `acp` delegates: framework-owned branch/commit/push/PR -- Status: Proposed +- Status: Accepted (shipped #1845/#1846/#1847) - Date: 2026-07-05 - Builds on: ADR 0024 (spawn CLI coding agents over ACP — superseded; ACP lives on as the `acp` delegate), ADR 0025 (unified delegate registry — `delegate_to`), ADR 0033 diff --git a/docs/adr/0077-adversarial-code-review-workflow.md b/docs/adr/0077-adversarial-code-review-workflow.md index 9dadc8666..02577fd1a 100644 --- a/docs/adr/0077-adversarial-code-review-workflow.md +++ b/docs/adr/0077-adversarial-code-review-workflow.md @@ -1,6 +1,6 @@ # 0077 — Adversarial code-review workflow + the findings convention -- Status: Proposed +- Status: Accepted (recipe live — the /code-review findings engine) - Date: 2026-07-06 - Builds on: ADR 0002 (declarative workflow engine — the DAG this recipe runs on), ADR 0011 (deep-research adversarial roles — the reviewed-by-strangers pattern this diff --git a/docs/adr/index.md b/docs/adr/index.md index eb58c6c8d..99bbc8a5a 100644 --- a/docs/adr/index.md +++ b/docs/adr/index.md @@ -56,7 +56,7 @@ decision, numbered, never deleted (supersede instead). | [0045](./0045-chat-panel-slot.md) | The chat panel is a **slot** — fork (`src/ext` id `chat`) or plugin (`views: slot: "chat"`) replaces the built-in surface, inheriting the always-mounted contract (#613); the canonical chat-panel protocol is **A2A 1.0 + the protolabs DataParts** (no TS SDK, no AI-SDK core); records the spike's deliberate deferrals | Accepted | | [0046](./0046-pluggable-utility-bar.md) | Pluggable utility bar — an `AppShell.utilityBar` slot + **declarative** plugin widgets (indicator / ticker / action) rendered natively (not iframes), fed by the event bus (ADR 0039); safe-for-untrusted, overflow to a `Menu` | Proposed | | [0047](./0047-layered-settings-cascade.md) | Layered settings cascade — per-field App→Host→Agent override (`Field.scope`, git-style), built on the FIELDS single-source; `host-config.yaml` (`scope_leaf`'d) holds box-shared gateway/model/routing/telemetry defaults, agents override freely; remotes stay delegate refs (#839) | Accepted | -| [0048](./0048-settings-ia-two-scope-homes.md) | Settings IA — **scope is the primary axis**: two homes, **Host / App** (box-shared, the host = first agent's inherited defaults) + **Workspace** (the focused agent, folding in its makeup — Identity/SOUL/Tools/MCP/Subagents/Skills/Middleware — plus its agent-scoped knobs). Surfaces ADR 0047's `Field.scope`; dissolves the category tabs + the bolted-on "Host defaults" cross-cut | Proposed | +| [0048](./0048-settings-ia-two-scope-homes.md) | Settings IA — **scope is the primary axis**: two homes, **Host / App** (box-shared, the host = first agent's inherited defaults) + **Workspace** (the focused agent, folding in its makeup — Identity/SOUL/Tools/MCP/Subagents/Skills/Middleware — plus its agent-scoped knobs). Surfaces ADR 0047's `Field.scope`; dissolves the category tabs + the bolted-on "Host defaults" cross-cut | Accepted | | [0049](./0049-bundle-pin-lifecycle.md) | Bundle pin lifecycle — a bundle pin means **"last verified working"**: pin release **tags** not raw SHAs (annotated-tag peel fixed in `_ls_remote_sha`), record `verified_against:` core version, and a **verify-and-bump CI loop** (install pin set into a scratch agent → probe every declared view → auto-PR tag bumps) keeps it true; reference template in `examples/bundles/template/` | Accepted | | [0050](./0050-background-subagents-reactive-notifications.md) | Background subagents & reactive notifications — a delegation marked `run_in_background` runs **detached as a self-POSTed A2A turn** (reusing the scheduler's substrate → durable task store, lifecycle, telemetry for free), tracked in a `notified`-gated registry keyed to the spawning chat session; completion is **drained into that session's next turn** as a `` (exactly-once) + a `background.completed` bus event. The chat stays live instead of blocking on long sub-work. Phase 1 of a 4-phase plan (reactivity/UX/control follow) | Accepted | | [0051](./0051-a2a-realtime-streaming-and-component-rendering.md) | A2A realtime streaming & component rendering — an executor **progress/lifecycle hook** (mirroring `set_terminal_hook`) taps a turn's frames → a `background.progress` bus channel + live per-tool job card; real `stop_task` (the SDK's `CancelTask` genuinely stops the turn — a memory/docstring correction) + `task_output` + foreground→auto-background; cancel-path telemetry. Plus a typed `component-v1` DataPart for richer component rendering over the A2A envelope, and alignment polish. Slice 1 builds on ADR 0050 | Accepted | @@ -65,9 +65,9 @@ decision, numbered, never deleted (supersede instead). | [0054](./0054-dream-distill-curation-subagents.md) | `dream` & `distill` — scheduled self-curation **subagents** (`/dream` `/distill`, schedulable via the existing scheduler). `dream` consolidates durable facts into memory **and prunes** stale/duplicate/superseded ones (`forget_memory` by id; `memory_list` now leads with `#id`). `distill` mines recent activity for repeated workflows → **hybrid output**: auto-create high-confidence skills (`save_skill`, additive-only, `source="distilled"` → curator-managed), propose the rest as beads. New scoped, mostly read-only tools (`recent_activity`, `list_skills`, `save_skill`, `forget_memory`) — **no shell / raw SQL** (avoids MiMo-Code's writable-trajectory-DB risk). Out-of-graph runner now mirrors the lead's full tool set so allowlisted names don't silently drop | Accepted | | [0055](./0055-multi-team-orchestration-federated-boards.md) | Multi-team orchestration — **scale OUT**: each repo/team is its own protoAgent instance running a single `project_board` for its repo; a PM agent federates over **A2A** (reusing ADR 0042 fleet/remote-members/`delegate_to`), treating each team-agent as a remote board. `project_board` stays single-board (multiplicity = the fleet, not an in-process registry). Context at scale = portfolio **rollups + event/A2A deltas**, never raw reads; governance is per-instance. P0 = deterministic per-instance board pinning (kills the cwd `.beads` auto-discovery gap); cross-plugin composition moves to the ADR 0043 SDK seam | Accepted | | [0056](./0056-unified-dockable-view-model.md) | Unified dockable-view model — one addressable `View` (`kind: surface\|session\|plugin\|ext`) that can live in **any** container (a rail as a top-level panel, or a panel's tab strip as a tab), with one `dockLayout` store + a single shared `DndContext`. Makes the validated **reorder / tear-out / re-dock** gestures (POC `spike/dock-dnd-tabs`) one operation instead of a conversion between the `uiStore.railOrder` surfaces and the `chatStore.sessions` tabs. The crux is **session-as-View** (sessions become placeable peers → side-by-side multi-session). Sequenced after the DS `TabBar` reorder (protoContent #264); needs an AppShell "dock mode" (shared context) for the cross-container drag | Proposed | -| [0057](./0057-command-palette.md) | Command palette (⌘K) — consume the DS `@protolabsai/ui/command-palette` substrate (protoContent ADR 0006) behind a thin web **adapter**: every surface auto-becomes a *navigate* command via `useUI().setSurface` (free for plugin views), plus a **new declarative `commands:` manifest contribution type** (mirrors `views:`; `_parse_commands` → runtime status → adapter compiles `action`→`run`, web is the only dispatcher → no plugin code in the bundle). Inline plugin views reuse `pluginView()` + `consoleTheme()`/`authToken()`; live search via a `provider:` route. Forces ADR 0056's `viewFor(id)`. ⌘K in-app (overlay) + Tauri global shortcut (desktop). Greenfield (no palette today) | Proposed | -| [0058](./0058-runtime-plugin-install-frozen-app.md) | Runtime plugin install in the frozen desktop app — a **git-less HTTPS archive fetch** + a **bundled-dep gate** (`requires_pip ⊆ bundled`, checked via `find_spec`) extend ADR 0027 so external plugins install at **runtime** inside the read-only PyInstaller bundle (the loader already discovers + `importlib`-loads live-root plugins in frozen mode; only `git`/`pip` were missing). First consumer: **Discord** becomes an external comms plugin — opt-in everywhere (removed from the default bundle), added via **Settings** (no onboarding step), behavior identical once enabled (same code relocated, frontend affordances stay in core). Gated cut: don't remove from the bundle until runtime-install is parity-verified on a signed dmg + the repo is pinned. Epic bd-3uh | Proposed | -| [0059](./0059-unified-plugin-manager.md) | Unified plugin manager — collapse the split (PluginsSurface's Local/Market/Download tabs + Settings▸Plugins config) into **one** surface with two sections: **Discover** (an in-app official-plugin directory rendered from a host-served `config/plugin-catalog.json` → `GET /api/plugins/catalog`, browse + **one-click install** that works on every surface incl. the frozen desktop app via ADR 0058) and **Installed** (each plugin row folds in enable/disable/update/uninstall **+ its config + Test/Connect inline**; Settings▸Plugins → a pointer). The curated index ADR 0027 D9 / 0040 gestured at, now thin because runtime-install is the primitive. Subsumes the comms-channel one-click install. Epic bd-23a | Proposed | +| [0057](./0057-command-palette.md) | Command palette (⌘K) — consume the DS `@protolabsai/ui/command-palette` substrate (protoContent ADR 0006) behind a thin web **adapter**: every surface auto-becomes a *navigate* command via `useUI().setSurface` (free for plugin views), plus a **new declarative `commands:` manifest contribution type** (mirrors `views:`; `_parse_commands` → runtime status → adapter compiles `action`→`run`, web is the only dispatcher → no plugin code in the bundle). Inline plugin views reuse `pluginView()` + `consoleTheme()`/`authToken()`; live search via a `provider:` route. Forces ADR 0056's `viewFor(id)`. ⌘K in-app (overlay) + Tauri global shortcut (desktop). Greenfield (no palette today) | Accepted | +| [0058](./0058-runtime-plugin-install-frozen-app.md) | Runtime plugin install in the frozen desktop app — a **git-less HTTPS archive fetch** + a **bundled-dep gate** (`requires_pip ⊆ bundled`, checked via `find_spec`) extend ADR 0027 so external plugins install at **runtime** inside the read-only PyInstaller bundle (the loader already discovers + `importlib`-loads live-root plugins in frozen mode; only `git`/`pip` were missing). First consumer: **Discord** becomes an external comms plugin — opt-in everywhere (removed from the default bundle), added via **Settings** (no onboarding step), behavior identical once enabled (same code relocated, frontend affordances stay in core). Gated cut: don't remove from the bundle until runtime-install is parity-verified on a signed dmg + the repo is pinned. Epic bd-3uh | Accepted | +| [0059](./0059-unified-plugin-manager.md) | Unified plugin manager — collapse the split (PluginsSurface's Local/Market/Download tabs + Settings▸Plugins config) into **one** surface with two sections: **Discover** (an in-app official-plugin directory rendered from a host-served `config/plugin-catalog.json` → `GET /api/plugins/catalog`, browse + **one-click install** that works on every surface incl. the frozen desktop app via ADR 0058) and **Installed** (each plugin row folds in enable/disable/update/uninstall **+ its config + Test/Connect inline**; Settings▸Plugins → a pointer). The curated index ADR 0027 D9 / 0040 gestured at, now thin because runtime-install is the primitive. Subsumes the comms-channel one-click install. Epic bd-23a | Accepted | | [0060](./0060-skill-progressive-disclosure.md) | Skills: progressive disclosure — replace per-turn BM25 retrieval (which injected top-k **full skill bodies** every model call, built from a query that included the agent's own recent output → generic turns re-summoned every skill and pinned it in context) with **advertise cheaply, load deliberately**: an always-on `` **index** (name + description of up to `skills.top_k` discoverable skills, query-independent, `+N more → list_skills`) plus a lead-agent **`load_skill(name)`** tool returning one skill's full procedure on demand (visible as a tool card). Greenfield-deletes `load_skills`/`SkillRecord`/`_format_learned_skills`/`_build_skills_query` and the `skills_loaded` chip end-to-end; `skills.top_k` → "skills listed in index", `skills.announce`/`max_tokens` removed. Mirrors Anthropic Agent Skills / Cursor rule tiers. ACP external-brain feed lists the same index | Accepted | | [0061](./0061-frontend-extension-registries.md) | Frontend extension registries — give the console the backend's *extend-without-editing-core, update-safe* property. Extend the `src/ext/` fork seam (ADR 0038 D3) with **behavior registries** mirroring `registerSurface` (static, first-wins, HMR-safe): **`registerSlashCommand`** (shipped) lets a fork/core own a client-side `/` (claim-the-token, the frontend twin of `register_chat_command` #1334) — core's `/new`/`/clear`/`/effort` now register through it, no hardcoded verbs. Plus **`registerComposerAction`** (composer actions slot), **`registerPaletteCommand`** (root ⌘K — core deep-links dogfooded onto it), and **`createUISlice`** (a fork's own namespaced, per-agent-persisted store — not a merge into core `UIState`). A fork extends console behavior by dropping a `src/ext/` module — no core edit. Surfaced by the GitHub→plugin extraction (#1336/#1337). | Accepted | | [0062](./0062-full-screen-document-viewer.md) | Full-screen document viewer — one app-wide reader (`openDocument(spec)` → a root-mounted full-screen DS `Dialog` rendering console markdown), mirroring the context-menu store+host+imperative-open pattern (`src/docviewer/`). `DocumentSpec` is generic (body resolves `render()` → `load()` async markdown → inline `content`; + `title`/`subtitle`) so any surface opens the SAME reader. Fixes background-agent reports landing **truncated** in chat (server carries a 2000-char preview, ADR 0050) — a **"Read full report"** card button `load`s the FULL report by job id; the **Activity feed** opens entries into the same viewer. Extensible for future long-content views (tool output, knowledge, artifacts). DS gap: `Dialog` has no `size="fullscreen"` (achieved via a `.doc-viewer` CSS override) | Accepted | @@ -76,7 +76,7 @@ decision, numbered, never deleted (supersede instead). | [0065](./0065-two-tier-instance-paths.md) | Two-tier instance paths (box / instance) + single resolution rule — split the storage identity into a **box** root (per machine) and an **instance** id (per agent), both resolved from env (`PROTOAGENT_BOX_ROOT` / `PROTOAGENT_INSTANCE`, `infra/paths.py`) by **one** rule, *not* from the config file — so every store nests predictably under `//` and the dev sandbox instance is isolated from prod. Underpins fleet robustness + the multi-instance test harness (zombie-reap #1474). | Accepted | | [0066](./0066-goal-trust-operator-channel.md) | Goal trust-gate Phase 2: federation token + operator `/api` channel — an optional second credential `auth.federation_token` (`A2A_FEDERATION_TOKEN`) for semi-trusted A2A peers, confined to the `/a2a` + `/v1` consumer surfaces and **denied the `/api` operator surface** (plugin install, config/SOUL rewrite, subagent runs, goal/watch set-paths) with `403` — the R1 path ceiling. Opt-in (unset ⇒ single-token mode). The `/api` ceiling, not per-verb gating, is the control (D4). Follow-ups tracked in #1504. | Accepted | | [0067](./0067-standalone-watch-primitive.md) | Standalone `watch` primitive (many concurrent watches) — resolves the fork ADR 0030 left open: a first-class `watch` that polls a condition on a cadence and, when it trips, runs a follow-up agent turn (via `sdk.run_in_session`, #1494) and/or fires hooks — the passive counterpart to a goal (which the agent *drives*). Unlike a goal (one-per-session) you can hold **many**. Retires the monitor-goal *disposition* (goals are drive-only). | Accepted | -| [0068](./0068-developer-flags-and-panel.md) | Developer flags & the Developer panel — a small **local/static** feature-flag system (`off`·`dev`·`beta`·`on`) to gate pre-release functionality, built by **reusing** existing machinery: a single backend registry (`runtime/flags.py`, shaped like the config `FIELDS` list) exposed via `GET /api/flags`; a runtime **channel** (dev instance / `import.meta.env.DEV` / `developer.channel`) the tier is measured against; resolution precedence mirroring plugin-config **env > query-param > panel > channel**; a **Settings ▸ Developer** panel (`CONSOLE_SECTIONS`, device-local, gated to `≥ beta`) to view/toggle/reset flags; and a **cleanup contract** (`remove_by` + a staleness test) so graduating to `on` deletes the flag + old path in one PR. A flag is *not* a plugin (load-time capability) nor a setting (permanent user config). Non-goals: A/B, %-rollouts, remote flag service. Issue #1506 | Proposed | +| [0068](./0068-developer-flags-and-panel.md) | Developer flags & the Developer panel — a small **local/static** feature-flag system (`off`·`dev`·`beta`·`on`) to gate pre-release functionality, built by **reusing** existing machinery: a single backend registry (`runtime/flags.py`, shaped like the config `FIELDS` list) exposed via `GET /api/flags`; a runtime **channel** (dev instance / `import.meta.env.DEV` / `developer.channel`) the tier is measured against; resolution precedence mirroring plugin-config **env > query-param > panel > channel**; a **Settings ▸ Developer** panel (`CONSOLE_SECTIONS`, device-local, gated to `≥ beta`) to view/toggle/reset flags; and a **cleanup contract** (`remove_by` + a staleness test) so graduating to `on` deletes the flag + old path in one PR. A flag is *not* a plugin (load-time capability) nor a setting (permanent user config). Non-goals: A/B, %-rollouts, remote flag service. Issue #1506 | Accepted | | [0069](./0069-memory-delivery-layer.md) | Memory delivery layer — extends ADR 0021 from *what is stored* to *how memory reaches the prompt*: replace the raw pooled `` dump with an **attributed digest** (one line/session + `recall_session` tool), wrap ALL auto-injected memory (digest, hot, RAG) in an **untrusted-reference envelope**, namespace/incognito scoping, identity hygiene (`api-default`, `unknown.json`, prefix split). Phase 2: **provenance stamping** (fact→source session), a per-turn **injection record**, console **memory inspector** (a security control — how SpAIware-class poisoning gets detected). Phase 3: **trust tiers** by `source_type`, supersede-don't-delete staleness (`invalidated_at`, deterministic — no LLM freshness judging), memory-regression evals (knowledge-update/abstention/poisoning-replay). Anchored in OWASP ASI06 + product convergence (Claude project isolation, Copilot citation-validation, Gemini RESTRICTED-by-default, Claude Code v2.1.50) | Accepted | | [0070](./0070-background-results-push-resume.md) | Background results: push-resume, indexed reports, disposable workers — amends ADR 0050 pull-only delivery: on completion the server **self-nudges the origin session** (Claude Code-style push-inject-and-wake; notified-gated drain keeps exactly-once; `background.auto_resume` default on) so the agent briefs the operator; the full report is **indexed into the KB keyed to the origin session** (`source_type="background_report"`, tier 2) with a summary-sized drain + search/docviewer pointer; `background:*` workers become **disposable** (no session summary, no digest line, no harvest — jobs DB is the system of record); report card → real card (raised bg, shadow, clamped excerpt + bottom fade, Open CTA) + `GET /api/background/{id}` | Accepted | | [0071](./0071-plugin-permissions-trust-model.md) | Plugin permissions: a **trust-and-consent model, not a runtime sandbox** — consolidates the locked ComfyUI posture into an explicit permissions model. In-process plugins run with full runtime privileges; the **only** hard boundaries are the enable gate + `plugins.disabled`, `tools.disabled` (by tool name), route auth default-deny + namespace-scoped `public_paths`, `requires_env`/`min_version` load gates, and install-time source allowlist + SHA pinning. Everything else (fs, network egress, cross-section secret reads, internal APIs, `apply_settings`) is **unenforceable in-process**. Decision: (D1) trust not sandbox — untrusted code → MCP, and stop re-proposing an in-process sandbox; (D2) present only the boundary we enforce — reframe manifest **`capabilities:` as disclosure, not enforcement**; (D3) formalize provenance-based **consent** (`sources.official` default `github.com/protoLabsAI/*` fork-overridable · `sources.acked` · `trust_unverified`) gating auto-enable (#899) and inherited by auto-update (#1720). Design-only; consent layer is a tracked follow-up. Issue #1725 | Accepted | @@ -84,17 +84,22 @@ decision, numbered, never deleted (supersede instead). | [0073](./0073-goal-completion-contracts.md) | Goal completion contracts — an optional, structured **completion contract** on `GoalState` (`outcome` / `constraints` / `boundaries` / `stop_when`) layered OVER the existing verifier surface (ADR 0028). The contract is re-stated in the drive-loop continuation prompt every turn (Hermes-Judgment-style contract-directed continuation) but is **directive only**: DONE is still decided by the goal's real, deterministic verifier — never an LLM-judge-as-arbiter (our edge). All fields default-empty (existing goals unaffected); plumbed through `POST /api/goals` + `set_goal_operator`/`set_goal_safe`; `stop_when` v1 is prompt-injected (agent self-parks via `abandon_goal`). FE goal-creation form is a follow-up. | Accepted | | [0074](./0074-system-lifecycle-events.md) | System lifecycle events — a plugin seam for host lifecycle reactions (`on_app_loaded` / `on_agent_active` / `on_system_wake`) via `register_lifecycle_hook`, so a plugin can run work when the app loads, the agent becomes active, or the machine wakes, without editing core. | Accepted | | [0075](./0075-external-interfaces-cli-mcp-api.md) | External interfaces: a first-class `protoagent` CLI + tightened MCP / API / model onboarding — treats the CLI, operator MCP, and HTTP API as **one operation model with three faithful projections**. Retires `python -m server ` for an installable `protoagent` command (re-parenting the hidden `plugin`/`workspace`/`fleet`/`skills`/`config` dispatchers + new `up`/`down`/`status`/`serve`/`setup`); a shared `ops/` layer ends REST-only orchestration glue; the operator MCP grows the **full admin surface** behind curated profiles + ADR 0071 consent + the ADR 0066 `/api` ceiling (and stops HITL tools hanging a foreign client); the REST surface deduplicates split namespaces (`goal[s]`, `memory`/`knowledge`/`injections`), fills real `/v1` usage, and publishes a `/api/operations` catalog; plus fast local-LLM onboarding (Ollama/HF/LM Studio/vLLM discovery + `protoagent model use` + a HuggingFace "Use this model" local-app registration — a CLI snippet, not a deep-link, matching the hermes-agent/openclaw/pi pattern). `proto` stays the A2A chat client — the two meet at the wire, not in one binary. Ships `uv tool`/`pipx` + zero-Python frozen-binary `curl\|sh`. | Accepted | -| [0076](./0076-managed-git-acp-delegates.md) | Managed git for `acp` delegates — framework-owned branch/commit/push/PR behind an opt-in `manage_git` delegate field (+ `base_branch`, `branch_prefix`, and an `item_id` arg on `delegate_to`): the coder edits files and runs tests; a deterministic harness (`plugins/coding_agent/git_harness.py`, on `run_command`/`run_gh`) mints the branch (`/-`, no LLM naming), branches off fresh `origin/`, then isolation-guards (`stranded_on_base`) → secret/scratch-scans → commits on the coder's behalf (idempotent to partial git: adopt unpushed commits / fixup already-pushed) → rebases (conflict ⇒ abort+push+report) → pushes `--force-with-lease` with lock-contention retry + **remote-SHA verify** → idempotent `gh pr create` (0-ahead skip, pre-check, already-exists recovery). An event-loop-atomic single-claim registry on `item_id` (default `sha1(query)[:12]`) dedups fan-out — duplicate PRs are the #1 empirical agent-PR rejection cause. Ported from source-verified protoMaker; plan + due diligence in `docs/plans/coding-agent-deterministic-git.md` | Proposed | -| [0077](./0077-adversarial-code-review-workflow.md) | Adversarial code-review workflow + the findings convention — a first-class `code-review` recipe (4 parallel `review-finder` angles: correctness / removed-behavior / cross-file / conventions → `review-synthesizer` dedup/rank → ONE `verifier` pass over the merged list, github-tool-grounded → final report ending in a canonical fenced findings JSON) plus the convention itself in `graph/review/findings.py`: schema `[{file, line, severity, category, claim, evidence, verdict?}]`, `FINDINGS_CONTRACT` prompt snippet (schema written once), tolerant `parse_findings` (JSON-in-prose, coerced fields, verifier-vocabulary normalization), `render_findings_markdown`. The craft `/code-review` skill becomes a thin driver; the board review gate (M5) consumes the same recipe headlessly via `run_workflow`. Per-finding verify fan-out deliberately parked (needs a data-driven engine primitive). | Proposed | -| [0078](./0078-fleet-pr-review-qa-tier.md) | Fleet PR review as a protoAgent tier — harvest of protoWorkstacean's Quinn × protoPatch system around the ADR 0077 engine: four-layer placement (core engine · github-plugin formal Review-API verdict tools + `path_exists` with the CI-terminal guard enforced INSIDE the tool · new pr-reviewer-plugin for webhook dispatch/structural trigger/approve-on-green pure function + sweep/protoPatch client/eval · new qaEngineer bundle with archetype + persona). Guards ported verbatim from Quinn's failure ledger (#863 CI-terminal, #748 deterministic approve, #891 server-computed trigger, #858/#903 unresolved-threads gate); fail-closed at every judgement boundary (a partial panel is not a review; Gaps are never severities; never review own PRs); GitHub-native review memory (`prior_findings` delta re-reviews); shadow mode against Quinn's live traffic before per-repo cutover. | Proposed | +| [0076](./0076-managed-git-acp-delegates.md) | Managed git for `acp` delegates — framework-owned branch/commit/push/PR behind an opt-in `manage_git` delegate field (+ `base_branch`, `branch_prefix`, and an `item_id` arg on `delegate_to`): the coder edits files and runs tests; a deterministic harness (`plugins/coding_agent/git_harness.py`, on `run_command`/`run_gh`) mints the branch (`/-`, no LLM naming), branches off fresh `origin/`, then isolation-guards (`stranded_on_base`) → secret/scratch-scans → commits on the coder's behalf (idempotent to partial git: adopt unpushed commits / fixup already-pushed) → rebases (conflict ⇒ abort+push+report) → pushes `--force-with-lease` with lock-contention retry + **remote-SHA verify** → idempotent `gh pr create` (0-ahead skip, pre-check, already-exists recovery). An event-loop-atomic single-claim registry on `item_id` (default `sha1(query)[:12]`) dedups fan-out — duplicate PRs are the #1 empirical agent-PR rejection cause. Ported from source-verified protoMaker; plan + due diligence in `docs/plans/coding-agent-deterministic-git.md` | Accepted | +| [0077](./0077-adversarial-code-review-workflow.md) | Adversarial code-review workflow + the findings convention — a first-class `code-review` recipe (4 parallel `review-finder` angles: correctness / removed-behavior / cross-file / conventions → `review-synthesizer` dedup/rank → ONE `verifier` pass over the merged list, github-tool-grounded → final report ending in a canonical fenced findings JSON) plus the convention itself in `graph/review/findings.py`: schema `[{file, line, severity, category, claim, evidence, verdict?}]`, `FINDINGS_CONTRACT` prompt snippet (schema written once), tolerant `parse_findings` (JSON-in-prose, coerced fields, verifier-vocabulary normalization), `render_findings_markdown`. The craft `/code-review` skill becomes a thin driver; the board review gate (M5) consumes the same recipe headlessly via `run_workflow`. Per-finding verify fan-out deliberately parked (needs a data-driven engine primitive). | Accepted | +| [0078](./0078-fleet-pr-review-qa-tier.md) | Fleet PR review as a protoAgent tier — harvest of protoWorkstacean's Quinn × protoPatch system around the ADR 0077 engine: four-layer placement (core engine · github-plugin formal Review-API verdict tools + `path_exists` with the CI-terminal guard enforced INSIDE the tool · new pr-reviewer-plugin for webhook dispatch/structural trigger/approve-on-green pure function + sweep/protoPatch client/eval · new qaEngineer bundle with archetype + persona). Guards ported verbatim from Quinn's failure ledger (#863 CI-terminal, #748 deterministic approve, #891 server-computed trigger, #858/#903 unresolved-threads gate); fail-closed at every judgement boundary (a partial panel is not a review; Gaps are never severities; never review own PRs); GitHub-native review memory (`prior_findings` delta re-reviews); shadow mode against Quinn's live traffic before per-repo cutover. | Accepted | +| [0079](./0079-autonomous-operating-model.md) | Autonomous operating model — goals · tasks · scheduling · watches unified into one OODA loop | Accepted | | [0080](./0080-external-secrets-manager.md) | External secrets manager: **env hydration with pluggable providers, Infisical first** — a `SecretsProvider` contract + registry in `infra/secrets/` whose orchestrator owns every `os.environ` write (existing-env-wins, ownership/provenance so refresh only touches manager-set vars, protected bootstrap + `PROTOAGENT_*` identity vars, redaction registration); hydration runs in `from_yaml` **before** the parse so the env fallback tier (gateway key, `A2A_AUTH_TOKEN`, plugin `requires_env`, MCP child env) sees manager values on every load path — plugins load after config (ADR 0019), which is exactly why a plugin-provided backend is a non-goal. Infisical ships built-in over raw REST (universal-auth login → v3-raw list; no SDK — `infisicalsdk` drags boto3 and does no token renewal anyway); bootstrap machine identity is the one local secret (`SECRET_PATHS` + settings-schema `secret` fields → secrets.yaml, env fallback `INFISICAL_CLIENT_ID/_SECRET`). Refresh loop + `/api/secrets` status/sync/test; fail-open by default, `required: true` = fail-fast boot; TTL-gated in-process cache, **no disk cache**; `infisical run` stays the documented ops alternative. | Accepted | | [0081](./0081-self-authored-persona-edit-soul.md) | Self-authored persona — a guarded `edit_soul` tool that lets the **lead agent** rewrite sections of its own `SOUL.md`. Off by default behind `soul.self_edit_enabled` (a `ui_hidden` settings field); **section-scoped** (`replace`/`append`, never whole-file) and **persona-only** by contract, upholding ADR 0079's persona/doctrine split and the ADR 0066 operator-trust ceiling (subagents never get it). Writes go through `config_io.write_soul`, so every edit **snapshots to soul-history (#1691)** and is restorable from Settings ▸ Identity; a 64 KB cap keeps the prompt prefix bounded. Take-effect is **live next turn** via the server's `_reload_langgraph_agent` **injected** (not imported) as a `reload_callback` through `create_agent_graph → get_all_tools`, so `tools/`/`graph/` keep clear of `server/` (import-linter green); the reload rebinds `STATE.graph` atomically so the current turn is unaffected. Degrades to next-reload semantics when no callback is wired. Every edit is **never silent** — publishes a `persona.self_edited` event so the operator sees it (OpenClaw's "tell the user" made real). Due-diligenced against prior art: Hermes keeps SOUL.md operator-only (users want evolution, unshipped), OpenClaw invites unguarded self-edit (a prompt-injection surface), Letta added a read-only persona guard after unconstrained self-edits degraded identity — `edit_soul` is the guarded middle. | Accepted | | [0082](./0082-per-chat-acp-runtime.md) | Per-chat ACP runtime — let an operator pick an ACP coding agent from a chat tab's **model dropdown** and run that chat on it, hot-swapping the *runtime* the way a gateway *model* already hot-swaps. Extends ADR 0033's runtime/model split: today the native-vs-ACP fork reads the **global** `agent_runtime` at the top of the turn (`server/chat.py`), *before* the per-tab `model` is consulted, so the two never meet — and naively feeding `acp:` into the model picker would hit `create_llm`'s **text-only aux relay** (Trap A), not the real `AcpRuntime`. Decision: (D1) a per-turn `resolve_turn_runtime(request_metadata, config)` — an `acp:` per-tab model routes the turn to `_acp_drive_turn`; (D2) re-key the ACP session registry by **`(thread, agent)`** so a swap builds a fresh session while the old agent's survives; (D3) guard so `acp:*` never reaches the native lead-model path; (D4) **Option C continuity** — allow the mid-chat swap but emit an explicit context-boundary system-note and start fresh (history is split — native in the checkpointer, ACP in the agent's own stateful session — and never synced; two-way **history bridging is Option B, deferred**); (D5) **agent registration shipped in #1993**; (D6) the only real FE change is `ComposerModelSelect.tsx` (`chat-store`/`api` already carry an arbitrary model string). Out of scope v1: history bridging, and `/effort`//`compact`/HITL/skill parity under ACP. | Accepted | | [0083](./0083-cowork-mode-archetype.md) | Cowork mode — a prepackaged knowledge-worker archetype for Claude Cowork graduates: `cowork` soul preset + one catalog row pointing at the `cowork-stack` bundle (cowork-plugin skill pack + artifact + notes + execute_code + google-plugin + claude-bridge), shipped as **data over the existing ADR 0042 seams — zero new core seams in v1**. Grounded in verified research (Cowork = Claude Code headless in a VM; usage = 33.4% biz-ops / 16.4% content; ceilings we exceed: no cron, cloud tasks can't touch local files, siloed projects-only memory, no model choice, remote-default = files processed on Anthropic servers). D3 records the licensing finding: Anthropic's doc skills are all-rights-reserved (public repo ≠ redistributable) → cowork-plugin ships clean-room docx/xlsx/pptx/pdf over python-docx/openpyxl/python-pptx/pypdf, and the claude-bridge importer must skip `creatorType: anthropic`. D5 defers the missing seams (bundle `mcp:` field, rw-no-delete fence, approval tiers, env seeding) to filed issues. | Accepted | -| [0085](./0085-managed-node-runtime.md) | Managed Node.js runtime, **provisioned on demand** — the "works on download" answer to the desktop `npx` gap. The npx-based ACP coding agents (Claude Code, Codex) and most catalog MCP servers need `node`/`npx` on PATH, which the desktop app (Python-sidecar-only bundle) doesn't ship; the existing PATH machinery (#1299 Tauri login-shell PATH, `acp_client._discovered_node_dirs`) can only *find* a Node the user already installed. Rather than bundle Node in the installer (a second binary to notarize, paid for by users who never use it) or bundle it + vendor the adapters (100 MB+, owning the Claude SDK cadence), option 3: `protoagent runtime install-node` downloads a **pinned** Node (`NODE_VERSION` + an in-repo `_SHA256` table — a real integrity gate, not trust-on-first-use) into the **box-shared** `box_root/runtime/node/current` (one machine provisions once; every instance/dev-sandbox/fleet member shares it). Two halves split by layer: `infra/node_runtime.py` (light, importable everywhere) exposes the single consumption seam `augment_path_with_managed_node()` — a boot-time (`server.agent_init`) **PATH append** (append so a user's own Node wins; process-level so ACP, MCP, delegates, and `gh` inherit it uniformly, not one patch per launch site), re-run on install so a live server hot-adopts without restart; `runtime/node_install.py` (download/verify/extract/swap, old install kept until the new is in place) is consumed by the CLI + `runtime list` status, with a one-click console button deferred. `npx -y` still fetch-caches the adapter on first launch (we deliberately don't vendor it). | Accepted | | [0084](./0084-computer-use-cua-driver.md) | Computer use — an **out-of-process** `cua-driver` MCP wrapper, and the fence it voids. Records the posture first: computer use is the first capability that **voids our containment story wholesale** — the ADR 0008 egress allowlist lives inside `fetch_url` and the ADR 0007 filesystem fence is an in-process path check, so an agent that moves a mouse routes around both by *never touching them* (opens Safari → no `fetch_url` call → no allowlist). Not weakened; absent from the code path. Per ADR 0071 the risk is a *presented* model implying containment the *enforced* model lacks, so D2 makes the plugin say so plainly. Decision: (D1) option **A** — a standalone `cua-plugin` whose `register_mcp_server` factory spawns `cua-driver mcp` over stdio; zero venv packages, frozen-build-safe, the exact shape ADR 0027 D1 mandates for dangerous code; the factory **probes for the binary and returns `None`** when absent, so we never install it and the macOS TCC grant stays a human GUI action. (D3) **allowlist** the driver's tools to the documented loop — **17 of 38** (ADR 0005). (D4) ship a `SKILL.md` — the driver is **accessibility-tree-first, not screenshot-first** and the snapshot-before-**and**-after invariant is load-bearing (`element_index` is per-`(pid, window_id)` and stale across turns); note `screenshot` was **removed** upstream (cua PR #1692) → `get_window_state` `capture_mode:"vision"`. (D5) defer `cua-sandbox` on evidence — resolving it against our real tree goes **114 → 294 packages** (+154 `pyobjc-framework-*` via `cua-auto → pywinctl → pyobjc`, Twisted via `vncdotool`, exact `grpcio==1.78.0`/`protobuf==6.33.6` pins), cloud is sales-gated, and the `cua` meta-package's ≥3.12 floor breaks ours; revisit scoped to local Docker only, since a VM is the one path to **OS-enforced** isolation (the inversion: driver-on-host = least contained tool we ship, sandbox-in-VM = best). Reject the hand-rolled wire client (unversioned 0.1.x tax) and `cua-agent` **as a tool** (a competing loop; belongs behind the ADR 0025 delegate registry if ever). | Accepted | +| [0085](./0085-managed-node-runtime.md) | Managed Node.js runtime, **provisioned on demand** — the "works on download" answer to the desktop `npx` gap. The npx-based ACP coding agents (Claude Code, Codex) and most catalog MCP servers need `node`/`npx` on PATH, which the desktop app (Python-sidecar-only bundle) doesn't ship; the existing PATH machinery (#1299 Tauri login-shell PATH, `acp_client._discovered_node_dirs`) can only *find* a Node the user already installed. Rather than bundle Node in the installer (a second binary to notarize, paid for by users who never use it) or bundle it + vendor the adapters (100 MB+, owning the Claude SDK cadence), option 3: `protoagent runtime install-node` downloads a **pinned** Node (`NODE_VERSION` + an in-repo `_SHA256` table — a real integrity gate, not trust-on-first-use) into the **box-shared** `box_root/runtime/node/current` (one machine provisions once; every instance/dev-sandbox/fleet member shares it). Two halves split by layer: `infra/node_runtime.py` (light, importable everywhere) exposes the single consumption seam `augment_path_with_managed_node()` — a boot-time (`server.agent_init`) **PATH append** (append so a user's own Node wins; process-level so ACP, MCP, delegates, and `gh` inherit it uniformly, not one patch per launch site), re-run on install so a live server hot-adopts without restart; `runtime/node_install.py` (download/verify/extract/swap, old install kept until the new is in place) is consumed by the CLI + `runtime list` status, with a one-click console button deferred. `npx -y` still fetch-caches the adapter on first launch (we deliberately don't vendor it). | Accepted | +| [0086](./0086-chat-first-mobile-shell.md) | Chat-first mobile shell (amends ADR 0035 D6) | Accepted | +| [0087](./0087-device-pairing-and-per-device-tokens.md) | Device pairing — per-device revocable tokens + short-TTL QR codes | Accepted | +| [0088](./0088-per-finding-inline-review-threads.md) | Per-finding inline review threads — finding-granular, GitHub-native review memory | Proposed | | [0089](./0089-intra-instance-trust-boundary.md) | Intra-instance trust boundary — the hub authenticates, members trust a **fleet service token** — fixes "all plugins broken on sister agents": each fleet member has its own `instance_root` (ADR 0065), so its own `devices.json` (ADR 0087) and `auth.token`, and the hub reverse-proxy (ADR 0042) forwards the caller's `Authorization` **verbatim** to a local member — which re-authenticates the *external* credential against its *own* isolated state, so a hub-minted **device token is 401'd by every sister** (structural: per-device tokens can't be shared). It only ever "worked" because members run *open* on loopback (`supervisor.py` strips the token) — itself the local-process RCE hole. Decision: adopt the API-gateway model — (D1) hub is the single auth boundary, members are backends that never consult the device registry; (D2) one internal **fleet service token** generated once, injected into every member at spawn via env (`PROTOAGENT_FLEET_TOKEN`, never config/URL), accepted as `operator` by every agent; (D3) the proxy **swaps** the caller's `Authorization` for it on local-peer targets, **operator-tier-only** + preserves the #1890 `member_public` anonymization; (D4) every in-instance caller (proxy · `delegate_to` · portfolio-PM board dispatch) presents it; (D5, phased) members then stop running open, **closing the RCE hole**. Rejects a signed per-request assertion (loopback needs no per-request crypto; door left open) and a box-tier shared registry (breaks ADR 0087 dev/prod isolation). The `a.symbol` white-screen is a downstream unguarded-401 in the plugin view (D7 follow-up). | Accepted | | [0090](./0090-goals-drive-in-a-chat-tab.md) | Console-created goals drive in a dedicated chat tab (attach/detach) — the console surface for the ADR 0079 drive loop. A panel-set goal used to run *headless* (a `run_in_session` kick in the `operator` session), so the loop worked in the background but was never in the forefront. Decision: (D1) a Work-panel goal opens a focused chat **tab**, sets the goal with `kick:false`, and fires a hidden kickoff so the inline drive loop **streams live into the tab** (chat-set goals stay in chat); (D2) **attach/detach** — closing the tab prompts *keep running* (`/resume` a headless continuation, keep the server session/context) or *stop* (`DELETE …?close_tasks=true` — clear + close the goal's task backlog); (D3) a **detail drawer** surfaces the plan artifact, contract read-back (ADR 0073), evidence, and a per-iteration timeline, with **rearm** lifecycle (extend/restart); (D4) creation is operator-tier (ADR 0066 — any verifier); (D5) robustness — no-progress works for fuzzy `llm` goals (plan-hash), and the best-effort kick is guarded. Trade-off: driving from the tab means closing aborts the in-flight turn (detach re-kicks) — chosen over headless+observe (typing indicator only). | Accepted | | [0091](./0091-agent-snapshot-portability.md) | Agent snapshot portability — export an agent as a portable, SECRET-FREE zip and rehydrate a fresh agent from it. An agent = one instance root (~25 stores). A raw instance-root dump fails: secret-laden (secrets.yaml + `.fleet-token` + secret VALUES across ~15 sqlite DBs) and version-brittle — the `docker commit`/committed-`.tfstate` anti-pattern. Decision: the snapshot is a **declarative bundle + optional seed**, not a state dump. (D1) manifest modeled on the existing `protoagent.bundle.yaml` — SOUL + secret-stripped config + `plugins.lock` SHA pins + subagents + MCP + archetype ref; (D2) **secret-free by the 12-factor litmus** — reuse `strip_secrets_from_doc`/`secret_paths`, ALSO null `mcp.servers[].env/headers` (inline, uncovered), carry a `required_secrets` schema (names, no values — à la Letta Agent File nulling + A2A `securitySchemes`); (D3) rehydrate via a new secret-free entry into `workspaces/manager.create()` (NOT its secret-copying `from_config` clone), prompting for `required_secrets`; (D4) stateless definition always, runtime history seeded empty, knowledge/memory an opt-in operator-reviewed seed via `knowledge_add`; (D5) snapshot (portability) ≠ backup (DR encrypted tar, out of scope). ~80% reuse. Slice plan: export → import/rehydrate → knowledge seed → console UX. | Proposed | -| [0094](./0094-managed-python-runtime.md) | Managed Python runtime — `execute_code` on the frozen desktop app (closes #2137, completes ADR 0092's stated outcome). The engine spawns `sys.executable`, which in a PyInstaller build is the frozen server binary, so a hard gate leaves the tool unregistered on desktop — and **any capability that computes through code is silently inert on the flagship distribution** (cowork's docx/xlsx/pptx/pdf skills author through execute_code, so ADR 0092 D1 made cowork *install* but not *produce*). Two constraining facts: PyInstaller packs pure-Python libs into the PYZ (an external interpreter can never import the D1 bundle), and the child's library surface has always been the interpreter's own site-packages (scrubbed env, no PYTHONPATH). Decision: apply the ADR 0085 pattern to CPython — (D1) pinned **CPython 3.12.x** from python-build-standalone, in-repo SHA256 table, guarded extract, atomic swap into box-shared `runtime/python/current`, `python_status()`; (D2) frozen → managed interpreter ONLY (no system-Python fallback — deps roulette reproduces the invisible-breakage class), source → `sys.executable` unchanged; (D3) provisioning pip-installs the doc baseline from `requirements-docs.txt` (ADR 0092's single source of truth) into the runtime's own site-packages, reproducing source-run semantics; (D4) honest state everywhere — the tool registers on frozen and says "runtime not provisioned — install under Settings ▸ Tools (~35 MB)" instead of today's copy that implies the toggle works; (D5) `/api/runtime/python` + `install` (202+progress), `protoagent runtime install-python`, one-click button beside the toggle (consent = the click; plugin stays disabled-by-default per ADR 0071); (D6) out: PATH exposure (scoped to the execute_code spawn), superseding ADR 0093 (complementary — 0093 = frozen HOST imports, 0094 = child interpreter + deps; together the desktop compute tier), sandbox changes (subprocess + scrubbed env + timeout posture unchanged). Rejects in-process exec (credentials readable in-env, no crash isolation, no reliable kill), fixed first-party doc tools (docs-only, kills skill flexibility), system-Python discovery, and bundling an interpreter in the installer (~40 MB for users who never enable it). | Accepted | +| [0092](./0092-desktop-document-baseline-and-versioned-file-artifacts.md) | Desktop document-generation baseline + versioned file artifacts (D1 doc stack; D2 save_file_artifact) | Accepted | | [0093](./0093-pip-less-wheel-installer-frozen-plugins.md) | Pip-less wheel installer for frozen-app plugin deps (#1631 Scope A) — opens the open half of ADR 0058 D2. A third-party plugin whose `requires_pip` isn't bundled is no longer refused on the desktop app: behind the opt-in `plugins.allow_unbundled_deps`, its **pure-Python** deps install as wheels into a writable per-instance `plugin-deps/` dir prepended to `sys.path` (the same mechanism that makes the live plugins root work when frozen), where the plugin's own module imports them. Mirrors ADR 0058 D1's git-less HTTPS fetch — closes the remaining `pip` gap the same way: (D3) PyPI JSON resolve → download the wheel over httpx → sha256-verify vs the PyPI digest → unpack; (D5) transitive resolution with real `packaging` marker/specifier eval, extra-gated/inapplicable deps dropped, already-bundled short-circuit, depth+cycle guard; (D6) each `(name, version, sha256)` pinned in `plugins.lock`, spec rails kept. (D4) **P1 = pure wheels** (shipped #2151); **P2 = platform-wheel tag matching** via `packaging.tags` (deferred #2155 — the big compiled deps are already bundled by ADR 0092, so P2 is the third-party long tail). Composes with ADR 0094 P2 on the frozen `install_deps` seam: host wheel-deps dir (this ADR, module imports) + managed runtime (0094, execute_code skills), each behind its own opt-in. Opt-in + consent + hash-pin, not a sandbox (ADR 0071). Freeze-verified: a plugin declaring `requires_pip: [humanize]` installed + imported on a real frozen build. | Accepted | +| [0094](./0094-managed-python-runtime.md) | Managed Python runtime — `execute_code` on the frozen desktop app (closes #2137, completes ADR 0092's stated outcome). The engine spawns `sys.executable`, which in a PyInstaller build is the frozen server binary, so a hard gate leaves the tool unregistered on desktop — and **any capability that computes through code is silently inert on the flagship distribution** (cowork's docx/xlsx/pptx/pdf skills author through execute_code, so ADR 0092 D1 made cowork *install* but not *produce*). Two constraining facts: PyInstaller packs pure-Python libs into the PYZ (an external interpreter can never import the D1 bundle), and the child's library surface has always been the interpreter's own site-packages (scrubbed env, no PYTHONPATH). Decision: apply the ADR 0085 pattern to CPython — (D1) pinned **CPython 3.12.x** from python-build-standalone, in-repo SHA256 table, guarded extract, atomic swap into box-shared `runtime/python/current`, `python_status()`; (D2) frozen → managed interpreter ONLY (no system-Python fallback — deps roulette reproduces the invisible-breakage class), source → `sys.executable` unchanged; (D3) provisioning pip-installs the doc baseline from `requirements-docs.txt` (ADR 0092's single source of truth) into the runtime's own site-packages, reproducing source-run semantics; (D4) honest state everywhere — the tool registers on frozen and says "runtime not provisioned — install under Settings ▸ Tools (~35 MB)" instead of today's copy that implies the toggle works; (D5) `/api/runtime/python` + `install` (202+progress), `protoagent runtime install-python`, one-click button beside the toggle (consent = the click; plugin stays disabled-by-default per ADR 0071); (D6) out: PATH exposure (scoped to the execute_code spawn), superseding ADR 0093 (complementary — 0093 = frozen HOST imports, 0094 = child interpreter + deps; together the desktop compute tier), sandbox changes (subprocess + scrubbed env + timeout posture unchanged). Rejects in-process exec (credentials readable in-env, no crash isolation, no reliable kill), fixed first-party doc tools (docs-only, kills skill flexibility), system-Python discovery, and bundling an interpreter in the installer (~40 MB for users who never enable it). | Accepted | diff --git a/docs/guides/building-react-plugin-views.md b/docs/guides/building-react-plugin-views.md index 80791d180..87e2f95e7 100644 --- a/docs/guides/building-react-plugin-views.md +++ b/docs/guides/building-react-plugin-views.md @@ -335,7 +335,7 @@ React + Babel from a CDN inside its sandbox). The console just iframes your `pat - **[`examples/plugins/chat_example`](https://github.com/protoLabsAI/protoAgent/tree/main/examples/plugins/chat_example)** — the **gold-standard copy-me view**: a single-page chat panel covering all four rules + the handshake + live re-theme + slug-aware routing + a real turn over a gated route. Start here. -- **artifact-plugin** (external — github.com/protoLabsAI/artifact-plugin) — generative UI: the agent +- **[`plugins/artifact`](https://github.com/protoLabsAI/protoAgent/tree/main/plugins/artifact)** (ships built-in; the old external artifact-plugin repo is archived) — generative UI: the agent calls `show_artifact(kind, code)`; renders in a **nested, no-same-origin** sandboxed iframe (the untrusted-content posture above). Uses a **custom router prefix** — a good example of RULE 1 with a non-default prefix. diff --git a/docs/guides/fleet.md b/docs/guides/fleet.md index b6de275d3..4a12b954a 100644 --- a/docs/guides/fleet.md +++ b/docs/guides/fleet.md @@ -72,6 +72,10 @@ archetype: label: Product Manager icon: Compass blurb: Researches, strategizes, and specs products from evidence — renders roadmaps and personas inline. + # optional: host capabilities the archetype needs to be USEFUL — the picker warns at + # choose-time when one isn't provisioned (e.g. cowork's document skills route through + # execute_code, which on the desktop app needs the managed Python runtime, ADR 0094) + requires: [python_runtime] ``` The picker draws from **two** sources: diff --git a/docs/guides/index.md b/docs/guides/index.md index 7c66832f7..62f357020 100644 --- a/docs/guides/index.md +++ b/docs/guides/index.md @@ -21,6 +21,7 @@ Shape how the agent's loop behaves — standing goals, timers, middleware hooks, | [Watches](/guides/watches) | You want the agent to supervise many external conditions at once — poll a metric, react when it trips | | [Schedule future work](/guides/scheduler) | You want the agent to defer tasks to itself ("remind me tomorrow", recurring sweeps) — bundled local sqlite | | [Middleware](/guides/middleware) | You want pre/post hooks on the agent turn (plugin-contributed) | +| [System lifecycle events](/guides/lifecycle-events) | You want plugins/skills to react to boot, idle-wake, or the desktop shell coming to the foreground (event-bus broadcasts) | | [Run on a coding agent (ACP runtime)](/guides/acp-runtime) | You want an external coding agent (proto/Codex/Claude/Copilot/OpenCode) to *be* the runtime brain, with protoAgent as the shell | | [Run on Hermes (preset)](/guides/hermes) | You already run Hermes Agent and want protoAgent's console/A2A/scheduler wrapped around it — one command | @@ -34,6 +35,7 @@ Give the agent reusable, named capabilities and delegates. | [Add a custom skill (A2A card)](/guides/add-a-skill) | You want A2A callers to dispatch a named capability — a *card* skill, distinct from the `SKILL.md` skills above | | [Configure subagents](/guides/subagents) | You want specialized delegates beyond the shipped `researcher` | | [Reusable workflows](/guides/workflows) | You want declarative multi-step recipes (`*.yaml`) the agent can run on demand | +| [Verifier-grounded coder (`coder_solve`)](/guides/coder) | You want the agent to solve a verifiable coding task via an execution-grounded search ladder (the `coder` plugin) | ## Knowledge & memory @@ -67,6 +69,7 @@ Add capability without forking — external tools, drop-in packages, channels. | [Build a communication plugin](/guides/communication-plugins) | You want a new inbound/outbound channel (like Discord) as a plugin surface | | [Install & publish plugins (git URLs)](/guides/plugin-registry) | You want to install a plugin from a git URL, or publish one as a shareable repo (tools + skills + subagents + workflows + views) | | [Discord surface](/guides/discord) | You want the agent reachable from Discord (the first-party `discord` plugin) | +| [File GitHub issues (`/issue`)](/guides/file-github-issues) | You want to file a GitHub issue straight from the console — the `/issue` command or the util-bar widget | ## Console & UI @@ -86,7 +89,9 @@ Ship it, isolate it, fence it in, and watch it. | Guide | When to read | |---|---| +| [The `protoagent` command (CLI)](/guides/cli) | You want the terminal control plane — install, run, and manage an instance without the console | | [Deploy via GHCR](/guides/deploy) | You're ready to ship and want auto-deploy wired up | +| [Deploy in Docker (config-as-code)](/guides/deploy-docker) | You want a container that boots pre-configured from a baked seed while console edits still persist | | [Deploy on Proxmox (reusable LXC template)](/guides/deploy-proxmox) | You run Proxmox and want a clone-in-seconds template for test/fleet instances | | [Releasing](/guides/releasing) | You're cutting a versioned release (semver bump → image → GitHub release) | | [Run multiple instances](/guides/multi-instance) | You want several scoped agents (data isolation) on one host | diff --git a/docs/guides/plugins.md b/docs/guides/plugins.md index b32fb4cb3..ab8a0d516 100644 --- a/docs/guides/plugins.md +++ b/docs/guides/plugins.md @@ -637,9 +637,13 @@ or `enabled: true` in the plugin's own manifest (author opt-in for plugins you wrote/dropped in). Discovered-but-disabled plugins still appear in runtime status so you can see what's available. -From the console, the **Plugins** panel has a one-click **Enable / Disable** toggle per -plugin — it edits `plugins.enabled` and hot-reloads, so tools / middleware / MCP servers -apply immediately. A plugin that serves a **console view** or runs a **background surface** +From the console, the **Plugins ▸ Installed** panel is a sortable table: free-text search +matches plugin names, ids, versions, **and tool names** ("which plugin ships `search_jobs`?"), +status chips filter to All / Loaded / Disabled / **Attention** (error, unfinished setup, +update available, or missing pip deps), and plugins installed by a bundle carry the bundle's +name as a chip (searchable too). Each row has a one-click **Enable / Disable** toggle — it +edits `plugins.enabled` and hot-reloads, so tools / middleware / MCP servers apply +immediately. A plugin that serves a **console view** or runs a **background surface** (its router mounts at startup) needs a restart to finish — the toggle says so. Plugin tools that would shadow a core or MCP tool name are skipped (logged). diff --git a/docs/guides/react-tauri-ui.md b/docs/guides/react-tauri-ui.md index b6a3b806e..c9720c795 100644 --- a/docs/guides/react-tauri-ui.md +++ b/docs/guides/react-tauri-ui.md @@ -128,7 +128,10 @@ frozen build bundles the `plugins/` tree and `--collect-all`s `tools`/`websocket plugin, ADR 0058, can only import what's bundled). Signed macOS DMG / Linux AppImage+deb / Windows NSIS artifacts + an in-app updater ship from the desktop-build CI, dispatched manually per release (`gh workflow run desktop-build.yml -f tag=vX.Y.Z`) — see -`docs/guides/releasing.md` § Desktop. +`docs/guides/releasing.md` § Desktop. The shell also runs one update check **at launch, in +parallel with engine startup** (#2203): the in-app UpdateNotice pulls that result the moment +it mounts and opens the changelog modal if a newer build exists — so the prompt lands before +the engine finishes booting, then the normal 10s-settle + 6h re-check cycle takes over. On macOS, `spawn_sidecar` augments the sidecar's `PATH` with the user's login-shell `PATH` (via `$SHELL -ilc`, plus the Homebrew/local fallbacks) before spawning. A Finder/Dock launch diff --git a/docs/guides/releasing.md b/docs/guides/releasing.md index 50c0052d7..9444b04a4 100644 --- a/docs/guides/releasing.md +++ b/docs/guides/releasing.md @@ -82,7 +82,7 @@ polls) and promotes the release to **Latest**. See `apps/desktop/README.md` §§ 2. The workflow bumps the version, rolls the changelog, and opens `chore: release vX.Y.Z`. It **does not merge or tag** — that's deliberate (fleet policy: auto-merge fired on stale SHAs and broke stacked PRs). -3. **Merge the release PR** once the three checks pass (squash). +3. **Merge the release PR** once CI passes (squash). 4. **Push the tag** on the merged release commit — this is what triggers the release: ```sh @@ -121,6 +121,9 @@ We keep a [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)-style - **In your feature PR**, add a bullet under `## [Unreleased]` in the right group (`### Added` / `### Changed` / `### Fixed` / `### Removed` / `### Docs`). + CI **enforces** this (#2174): the `Changelog entry` check fails any PR whose + diff doesn't touch `CHANGELOG.md` — escape hatches are the `skip-changelog` + label, a `release/*` head branch, and dependabot. - **At release time**, `scripts/changelog.py roll ` (run by `prepare-release.yml`) moves everything under `[Unreleased]` into a dated `## [X.Y.Z] - YYYY-MM-DD` section and leaves a fresh empty `[Unreleased]`. @@ -136,13 +139,17 @@ We keep a [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)-style ## Branch protection `main` is protected by a repository **ruleset**: every change needs a PR, and -the three CI checks must pass to merge — +the `checks.yml` CI jobs must pass to merge — -| Check | Workflow | +| Check | What it runs | |---|---| -| Verify workspace config | `checks.yml` (runs `release-tools`' `verify-workspace-config`) | -| Python tests | `checks.yml` (`pytest`) | -| Web E2E smoke | `checks.yml` (Playwright vs. mock backend) | +| Verify workspace config | `release-tools`' `verify-workspace-config` | +| Changelog entry | `scripts/changelog_gate.sh` — the PR's merge-base diff must touch `CHANGELOG.md` (#2174; escape hatches: the `skip-changelog` label, `release/*` branches, dependabot) | +| Lint (ruff + import contracts) | `ruff` + the import-layering contract | +| Python tests | `pytest` | +| Fleet integration (multi-instance) | the multi-instance fleet suite | +| A2A live smoke (lean tier) | live A2A smoke against the lean tier | +| Web E2E smoke | Playwright vs. mock backend | Direct pushes, force-pushes, and branch deletion are blocked. Approvals are set to **0** so the solo/automated flow (you + the release bot) is never blocked on diff --git a/docs/index.md b/docs/index.md index 45dc4072a..1ebfb2486 100644 --- a/docs/index.md +++ b/docs/index.md @@ -16,11 +16,11 @@ features: - title: A2A-native, built for fleets details: Every agent is a spec-compliant A2A 1.0 server — agent card, JSON-RPC over /a2a, SSE streaming, tasks/* lifecycle, push. Delegate to other a2a / openai / acp endpoints; run one or orchestrate many. - title: cost-v1 + trace propagation - details: Every terminal task emits a cost-v1 DataPart with token usage and wall time. a2a.trace metadata nests this agent's Langfuse trace under the caller's. + details: Every terminal task carries cost-v1 (token usage + wall time) on the artifact's metadata map, keyed by extension URI. a2a.trace metadata nests this agent's Langfuse trace under the caller's. - title: Free starter tools details: DuckDuckGo web search, URL fetch, safe calculator, and IANA-timezone clock — zero API keys, enough to demo a real research loop on a fresh clone. - title: A lean core, opt-in plugins - details: Start small; add tools, skills, subagents, workflows, FastAPI routes, console dashboards, memory backends, and managed MCP servers as git-URL plugins (python -m server plugin install, pinned in plugins.lock) — without touching core. Discord, Google (Gmail+Calendar), and the plugin-devkit ship first-party. + details: Start small; add tools, skills, subagents, workflows, FastAPI routes, console dashboards, memory backends, and managed MCP servers as git-URL plugins (python -m server plugin install, pinned in plugins.lock) — without touching core. Telegram and the plugin-devkit ship in-tree; Discord, Google (Gmail+Calendar), and more install as official external plugins from the directory. - title: Release pipeline details: Dispatch prepare-release → semver bump PR → merge → tag → GHCR image → GitHub release → Discord embed. Flip the RELEASE_ENABLED repo variable to enable it on a fork. --- diff --git a/docs/plans/coding-agent-deterministic-git.md b/docs/plans/coding-agent-deterministic-git.md index ab6b25cb1..cd014076a 100644 --- a/docs/plans/coding-agent-deterministic-git.md +++ b/docs/plans/coding-agent-deterministic-git.md @@ -1,7 +1,8 @@ # Plan: deterministic, framework-owned git for ACP coder delegates -**Status:** validated by due diligence (2026-07-05); Phase 0 done — ADR drafted as -[ADR 0076](../adr/0076-managed-git-acp-delegates.md) (Proposed). Next: Phase 1. +**Status:** Executed — [ADR 0076](../adr/0076-managed-git-acp-delegates.md) Accepted; +shipped #1845/#1846/#1847 (`plugins/coding_agent/git_harness.py`, `manage_git` wired +through the delegates registry). Supersedes the 2026-07 draft that targeted `code_with`. **Owner:** protoAgent `delegates`/`coding_agent` surface (general capability → contribute upstream; test on roxy). diff --git a/docs/reference/configuration.md b/docs/reference/configuration.md index aca242dc7..862a942e6 100644 --- a/docs/reference/configuration.md +++ b/docs/reference/configuration.md @@ -235,6 +235,7 @@ execute_code: ```yaml tools: disabled: [] # tool names to DROP (the operator's denylist) + hidden: [] # tool names to REMOVE ENTIRELY — denied AND never shown in the console deferred: enabled: false # OFF by default — the full tool set is shown keep: [] # always-on tool names; empty = built-in base @@ -243,11 +244,26 @@ tools: | Key | Default | What | |---|---|---| | `disabled` | `[]` | Tool names to **drop** from the agent at graph build — covers the **fully assembled** set: core, plugin, MCP, the delegation tools, and the filesystem tools (so `disabled: [run_command]` removes shell access for this agent). Live-reloadable — in the console, **every row at Settings ▸ Capabilities ▸ Tools carries an on/off switch** that edits this list (a toggled-off tool stays listed, dimmed, so it can be re-enabled). Plugins still ADD tools on top (see [Plugins](/guides/plugins)). ([ADR 0005](../adr/0005-tool-pollution-and-progressive-disclosure.md)) | +| `hidden` | `[]` | A **hard superset of `disabled`** (#2172, [ADR 0071](../adr/0071-plugin-permissions-trust-model.md)): a hidden tool is denied at the graph like a disabled one, **and dropped from the console's tool inventory entirely** — it never renders as a toggle, so it can't be re-enabled from the UI. A setup-time trust control for restricted consoles and archetypes: the config file is the boundary, the UI is presentation. `disabled` = "off but visible"; `hidden` = "not available, and not offered". | | `deferred.enabled` | `false` | Withhold most tool schemas; expose them via `search_tools`. | | `deferred.keep` | `[]` | Tool names always shown. Empty → built-in base (keyless core + `task`/`task_batch`/`run_workflow`/`save_workflow` + `search_tools`). `search_tools` is always kept regardless. | Every tool remains **executable** even while deferred — `create_agent` registers all executors; deferral only trims what the model *sees* per turn. The agent loads tools by calling `search_tools("github pull request")`; matches stay available for the rest of the thread. Leave off unless you have a large catalog (e.g. a chatty MCP server) — for a handful of tools it adds a discovery hop for no benefit. +## `settings` + +**Hide settings from the console** — the settings half of `tools.hidden` (#2172, +[ADR 0071](../adr/0071-plugin-permissions-trust-model.md)). + +```yaml +settings: + hidden: [] # dotted field keys ("goal.max_iterations") or whole groups ("goal", "careercoach") +``` + +| Key | Default | What | +|---|---|---| +| `hidden` | `[]` | Entries are dotted settings keys (`goal.max_iterations`) or **group prefixes** (`goal` — plugin groups like `careercoach` work too). A hidden setting is dropped from the schema the console renders **and refused by the settings save/reset APIs**, so it can't be seen or changed from the UI. The live **value is untouched** (hiding ≠ disabling) — this locks a setup-time decision, it doesn't turn the feature off. Like `tools.hidden`, it's meant for restricted consoles and archetype enforcement (a bundle's `config:` block can seed it at create time). | + ## `telemetry` Local per-turn cost/latency rollup ([ADR 0006](../adr/0006-observability-and-the-self-improving-flywheel.md)). One row per terminal A2A turn (tokens incl. cache, USD cost, duration, LLM/tool call counts), queryable at `/api/telemetry/summary` + `/api/telemetry/recent`. diff --git a/plugins/docs/nav.json b/plugins/docs/nav.json index d384aa3aa..a00b906b0 100644 --- a/plugins/docs/nav.json +++ b/plugins/docs/nav.json @@ -204,6 +204,10 @@ { "label": "Operate & deploy", "items": [ + { + "path": "guides/cli.md", + "title": "The protoagent command (CLI)" + }, { "path": "guides/deploy.md", "title": "Deploy via GHCR" diff --git a/sites/marketing/data/roadmap.json b/sites/marketing/data/roadmap.json index a3a851afe..a14a0fa11 100644 --- a/sites/marketing/data/roadmap.json +++ b/sites/marketing/data/roadmap.json @@ -34,8 +34,7 @@ "title": "Ollama & Hugging Face listings", "detail": "register protoAgent as an Ollama community integration and a Hugging Face \"Use this model\" local app.", "refs": [ - "#1833", - "#1834" + "#1990" ] } ]