chore: sync upstream → protoAgent v0.20.0#64
Conversation
…7, PR1) (#623) Author a plugin in its own GitHub repo, install it by URL — the deferred Slice 5 of the extensibility arc, done with a real safety model (vs ComfyUI's clone=RCE). - graph/plugins/installer.py: clone (--depth1, no submodules) at a ref → resolve commit SHA → validate manifest → move into the live plugins dir (already on loader._plugin_roots) → record in a committed plugins.lock. install/uninstall/ list/sync. Refuses to shadow a built-in, rejects a manifest-less repo, strips .git, optional plugins.sources.allow allowlist. Lock path env-overridable. - graph/plugins/cli.py + server `plugin` subcommand: `python -m server plugin install <url> [--ref] [--force] | list | uninstall <id> | sync`. Dispatched before the server argparse; fetches code and exits. - manifest: requires_pip / repository / homepage / min_protoagent_version. - .gitignore: config/plugins/ (installed code, reproducible via lock+sync); plugins.lock stays committed. Safety (ADR 0027): install ≠ enable ≠ trust. Install only puts code on disk + reads the manifest as data — never imports the plugin, never pip-installs deps (requires_pip declared, installed explicitly). Enabling (plugins.enabled → register()) is the trust decision; untrusted code → MCP. Tests: 9 installer cases (fetch+lock+no-enable, tag pin, force, builtin-shadow guard, no-manifest reject, bad-scheme, uninstall, sync-reclone, allowlist). Full suite 1136 passed; ruff clean; CLI live-smoked install→list→uninstall. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…624)
Install plugins from a git URL in the console (Settings → Integrations), for
non-CLI users. Mirrors the delegates panel.
- operator_api/plugin_routes.py: GET /api/plugins/installed (lock entries enriched
with manifest + declared capabilities + enabled state), POST /api/plugins/install
({url, ref?, force?} → summary + restart_required), DELETE /api/plugins/{id}.
Wired into the server route registration.
- apps/web PluginsSection: install form (URL + optional ref) + installed list with
a review surface (version, source, SHA, views, deps-to-install, env, secrets,
capabilities), enabled badge + the "add to plugins.enabled + restart" hint, and
uninstall. Rendered under Integrations (now always shown — Plugins is core).
api/types/queries + CSS.
install ≠ enable holds in the UI: installing fetches code (no run); enabling is the
config+restart step, surfaced as a hint. Untrusted → MCP (linked).
Tests: e2e plugin-install.spec (install → row shows NOT enabled + enable hint →
uninstall; form gating). 11 console specs green; web build green; ruff clean.
Live wire-smoke: POST install → GET installed (present, enabled=false, views) →
DELETE, against a real server with an isolated lock/plugins dir.
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
The safety rails for git-URL installs.
- install-deps: `python -m server plugin install-deps <id>` pip-installs a plugin's
declared requires_pip — the explicit, separate code-exec step that `install`
deliberately skips. When an enabled plugin's deps are missing, the loader now
emits a clear diagnostic ("declared deps not installed (…) — run: … install-deps
<id>") instead of a raw ModuleNotFoundError.
- audit: install / uninstall / install-deps are recorded to the audit log
(session_id=plugins, tool=plugin.*) with url/sha/deps.
- allowlist: `plugins.sources.allow` (host/org globs, e.g. github.com/org/*) is a
new config field, enforced on both console (route reads cfg) and CLI installs
(reads the live config). Empty = any URL (gated). Off-allowlist → refused.
Tests: install-deps (noop/missing/runs-pip-with-deps via a mocked subprocess),
configured_allowlist config read. 32 installer/plugin cases; full suite 1140;
ruff clean. CLI-smoked: allowlist block/allow + install-deps.
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…+ docs (ADR 0027, PR4) (#626) A plugin repo now contributes the WHOLE extension set, not just tools — so installing a git repo pulls in everything it ships. - register() already covers tools / subagents / routes / MCP / views. PR4 adds auto-discovery of conventional DATA subdirs in each enabled plugin (no boilerplate): skills/ (SKILL.md) → skills index, workflows/ (*.yaml) → WorkflowRegistry. Plus registry.register_workflow_dir() for non-standard paths. - loader collects workflow_dirs (registry + conventional) and the conventional skills/ dir; PluginLoadResult.workflow_dirs; STATE.plugin_workflow_dirs; _build_workflow_registry appends plugin workflow dirs (before the writable dir, so user-saved recipes still win a name clash). - docs/guides/plugin-registry.md: install (CLI + console) + publish the full bundle (repo layout + register() + skills/workflows/views + requires_pip) + the safety model (install≠enable, lock/pin, allowlist, untrusted→MCP). Sidebar + ADR 0027 updated. Tests: plugin workflow + skill auto-discovery, register_workflow_dir. 34 installer/plugin cases; full suite 1142; ruff clean. Live-smoked end-to-end: installed a repo shipping a tool + workflows/recipe.yaml + skills/ → enabled → tool loaded AND `plugin-recipe` appears in /api/workflows. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
|
👀 Quinn is reviewing — verdict (PASS / WARN / FAIL) + findings to follow. |
There was a problem hiding this comment.
QA Audit — PR #64 | chore: sync upstream → protoAgent v0.20.0
VERDICT: WARN (preliminary — CI not yet terminal)
CI Status
- Python tests: queued
- Web E2E smoke: queued
- Lint (ruff): queued
- Verify workspace config: queued
- A2A live smoke: queued
Diff Review
Large upstream sync (1625-line diff, 5 commits, zero conflicts). Brings ADR 0027 — install plugins from a git URL with install ≠ enable ≠ trust, pinned SHA lock, allowlist, audit logging, install-deps step, and a console Plugins panel. Full-bundle repos (skills + workflows auto-discovered). Merge commit strategy as specified.
Visible diff (first 200 lines) shows: .gitignore plugin dir exclusions, CHANGELOG.md for v0.20.0, e2e mock server plugin routes, new plugin-install.spec.ts (install → uninstall round-trip), plugin panel CSS, and api.ts type additions. Clean scope.
Observations
- Gap: Full diff is 1625 lines — backend plugin-install implementation (git clone, allowlist enforcement, pip install-deps, manifest parsing) is in the truncated portion. Cannot fully assess security correctness of the git-fetch + pip-invoke path without the full diff.
- Gap: Clawpatch unavailable for
protoLabsAI/roxy(not in project registry). Structural review deferred. - CI not yet terminal — this is a pre-merge COMMENT; will re-verify and upgrade to APPROVE once all checks are green and no blocking findings surface from the full diff.
— Quinn, QA Engineer
|
Submitted COMMENT review on |
Upstream sync (merge commit). 5 new commits, zero conflicts (base anchored at v0.19.0).
What comes in
Install plugins from a git URL (ADR 0027) —
python -m server plugin install <url>(+ list/uninstall/sync/install-deps) + a console Plugins panel. Safety: install ≠ enable ≠ trust, pinnedplugins.lock, declare-only deps, optional source allowlist, audit. Full-bundle repos (tools+subagents+skills+workflows+views auto-discovered).pytest -q→ green. Merge as a MERGE COMMIT (--merge).🤖 Generated with Claude Code