diff --git a/plugins/brains/.claude-plugin/plugin.json b/plugins/brains/.claude-plugin/plugin.json index 194d8ad..5482872 100644 --- a/plugins/brains/.claude-plugin/plugin.json +++ b/plugins/brains/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "brains", "description": "Your memory layer: Gmail, Calendar, Drive, and prior Claude conversations as queryable pages, with reflexive recall, hook-driven turn-by-turn capture and inbox delivery, and boards/automations/workflows on top.", - "version": "2.8.2", + "version": "2.8.3", "author": { "name": "brains (ssvlabs)" }, diff --git a/plugins/brains/.codex-plugin/plugin.json b/plugins/brains/.codex-plugin/plugin.json index 6398af4..f715ee6 100644 --- a/plugins/brains/.codex-plugin/plugin.json +++ b/plugins/brains/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "brains", - "version": "2.8.2", + "version": "2.8.3", "description": "Your personal memory layer for Codex: query Gmail, Calendar, Drive, and prior conversations, then build boards, automations, and workflows.", "author": { "name": "brains (ssvlabs)", diff --git a/plugins/brains/skills/brains-integrations/SKILL.md b/plugins/brains/skills/brains-integrations/SKILL.md index 80a00ae..ac3b5cf 100644 --- a/plugins/brains/skills/brains-integrations/SKILL.md +++ b/plugins/brains/skills/brains-integrations/SKILL.md @@ -1,57 +1,153 @@ --- name: brains-integrations -description: Managing which integrations the user has — listing what's connected (Gmail, Calendar, Drive, Monday, GitHub, …), and installing / upgrading / uninstalling them via codex recipes and the starter-pack bundle. Use when the user asks "what's connected", "can brains read from X", or wants to set up / add / update / remove an integration. +description: Managing which integrations the user has — listing what's connected (Gmail, Calendar, Drive, Monday, GitHub, …), and installing / upgrading / uninstalling them via codex recipes and the starter-pack bundle. Also covers authoring — publishing your own recipe to the catalog and managing it as a publisher. Use when the user asks "what's connected", "can brains read from X", wants to set up / add / update / remove an integration, or wants to publish a recipe of their own. --- # Integration lifecycle ## Know what's connected (do this before suggesting a source) -`list_integrations` once per session (cache it). One row per known integration: -`name`, `label`, `connected`, `supports.{fetch,act,adapter}`, `description`. Lean -on it before proposing `fetch_from_integration` / `act_on_integration` so you -don't name a source the user hasn't wired up — the `source` enums are filtered -per-user too. It's also the canonical answer to "what integrations do I have." +`list_integrations` returns one row per known integration: `name`, `label`, +`connected`, `supports.{fetch,act,adapter}`, `description`. Caching it for the +session is fine, but re-read it after you install / pause / resume / uninstall +anything and after a source call fails on connection state — it's a live read, +and the cache goes stale exactly when it matters. Read it before proposing +`fetch_from_integration` / `act_on_integration`: those tools advertise a +**static** `source` enum listing every registered source whether or not this +user connected it, so presence in the enum proves nothing. No failure comes +either: an unconnected source returns a success-shaped fetch with +`ingested_count: 0` and a `note`, or an action with `kind: "noop"` and a +`reason`. Read those before treating empty as "nothing there" — then re-read +`list_integrations`, get the user to connect or resume at `/integrations`, and +retry. `list_integrations` is the per-user answer, and the canonical one to +"what integrations do I have." -## Install / re-install +## Browse the catalog + +`list_recipes` enumerates what's installable. `tier` is `'catalog'` (the +default — the public catalog: platform-curated rows plus recipes other users +published publicly, so don't present a row as vetted; `owner_user_id: null` is +what marks a curated one), `'yours'` (the caller's own), or `'community'` +(other users' published ones); `kind` / `category` / `q` narrow it. Each row +carries the slug's current `version`. Use it to find slugs the user doesn't +already know rather than sending them to a browser. `recommend_recipes` picks a +few with an evidenced reason when they have no specific target in mind. +`/recipes` in the web UI is the same catalog with pictures. -You can install a single integration **from here over MCP** — no need to -bounce the user to the browser for the common case: +## Install / re-install -- **MCP (preferred for one integration):** `install_integration slug=`. - api_key / none-auth recipes install immediately; oauth2 recipes return an - `authorize_url` the user opens in a browser to finish the handshake. Common +- **One integration:** `install_integration slug=`. none-auth installs + immediately; api_key installs immediately when you pass `api_key`, and + otherwise creates **no** install — it returns a `configure_url` for the user + to enter the key on the web (never collect a key through chat); oauth2 and + composio recipes return an `authorize_url` to open in a browser. Common slugs: `gmail-inbox`, `gcalendar`, `gdrive-files`, `github-issues`, - `monday-items`. Idempotent — re-calling for an already-installed recipe - returns the existing install. -- **Web UI — browse catalog:** for "what's available?" send the user to - `/recipes` (integrations, boards, automations, workflows, bundles). There's - no MCP tool to enumerate the catalog, so the web UI is the way to discover - slugs the user doesn't already know. -- **Web UI — starter pack:** for "set me up" / "install the basics", send the - user to `/recipes/starter-pack` — one click bootstraps the three Google - integrations (gmail-inbox, gcalendar, gdrive-files) atomically. Bundle - cascades only run through the web UI. + `monday-items`. Idempotent — an already-installed recipe returns the existing + install, and an oauth2 one still awaiting its handshake hands back the + `authorize_url` again. A composio recipe does **not** replay that link: a + pending re-call just reports "already installed", so send the user to + `/integrations` to finish connecting. +- **Anything else, bundles included:** `install_recipe slug= + brain_id=` — boards, automations, workflows, integrations and bundles, + missing dependencies auto-installed children-first, never duplicating rows on + a re-run. Keep installs one-at-a-time — the dedup is a check-then-insert, so + two overlapping calls can both insert; never retry while a previous call may + still be running. `slug="starter-pack"` is the full bootstrap: two boards, + seven automations, and the three Google integrations. Its oauth2 children land + unauthorized and this call returns no link for them, so follow up with + `install_integration` per oauth2 child to collect each `authorize_url` — or + send the user to `/recipes/starter-pack` and let the web UI walk them + through it. ## Upgrade -Upgrades run through the web UI (there is no MCP upgrade tool). Each -integration page (`/integrations/`) shows a "Newer version -available" banner when an upgrade exists. If the install came via the -starter-pack bundle (`source_bundle_slug` is set), the Upgrade button -redirects to upgrading the **bundle** — don't upgrade bundle-sourced -integrations in isolation. +`list_installed_versions` returns one row per install — `{slug, name, kind, +version, state}`, `kind` being `automation`, `bundle` or `integration` (narrow +with `kinds`; boards and workflows never appear). Compare `version` against the +matching `list_recipes` row to spot drift, but treat it as a provenance stamp, +not proof of what's running: a re-apply advances it without necessarily +bringing the content along, so never conclude "already current" from equality +alone. + +The upgrade itself runs through the web UI; there is no MCP upgrade tool, and +`install_recipe` is not a stand-in. A re-apply reuses the existing entity — it +restamps provenance, writes the automation's state (reviving an uninstalled +one, otherwise matching the template's paused/active), and overwrites an +un-customized board dashboard — but leaves the board's schema, rows, +description and recipe-shipped skills, the automation's intent, model, triggers +and grants, and a workflow's contents entirely, on the old version. + +Where to send the user depends on the row's `kind`: + +- `integration` → its install page, `/integrations/`, whose upgrade + banner is the authoritative check for this kind. +- `automation` → the recipe's own page, `/recipes/`, which carries a + per-entity upgrade banner for a standalone install. Same page for a board or + workflow you're checking by slug. +- `bundle` → `/recipes/`, which fans the upgrade across every + child. + +Children of a bundle deliberately show no upgrade of their own. For a +bundle-sourced integration the install page is authoritative: follow the bundle +link when it renders one, and take the standalone "Upgrade to vN" when it +renders that instead — it means the bundle path is a no-op while the leaf lags. +Never bypass the page by hand-reinstalling. ## Uninstall / manage Over MCP: `uninstall_integration install_id=` (revokes + wipes the user's secrets for that install), `pause_integration` / `resume_integration` (stop / restart cron firings), `list_my_integrations` (the user's codex -installs with state + cost), and `get_integration_status` (one install's -health). Get the `install_id` from `list_my_integrations`. - -The web UI also exposes Uninstall on each entity page with a 7-day recovery -window; from `/recipes/` it cascades to every entity whose -`source_recipe_slug` matches (and for a bundle, every child). Use -`list_uninstalled` when the user asks "what did I just uninstall" before they -click Recover. +installs with slug, name, version and state), and `get_integration_status` +(one install's state, config overrides, and a sanitized `last_error`). Get the +`install_id` from `list_my_integrations`. + +Tune an install instead of reinstalling it: `set_integration_config` +(installer-side config, e.g. which repos github-issues ingests), +`set_integration_overrides` (per-run / per-month limits — you can only lower +the recipe's caps, higher values are clamped down silently), and +`run_integration_once` for an on-demand test run instead of waiting for cron +(the install must be `active`). + +The whole-recipe cascade is callable over MCP too: `preview_uninstall_recipe +slug= brain_id=` shows the blast radius — entities stamped with +the slug, plus deps that would be orphaned — and mutates nothing, then +`uninstall_recipe` does it in one transaction, or takes `entity_id` instead to +drop a single entity. Always preview first for a bundle. Uninstalled boards, +automations and workflows sit in a 7-day recovery window; `list_uninstalled` +answers "what did I just uninstall" before they click Recover. Integration +installs are **not** in that window — uninstall revokes them outright, and +recovering one means installing it again. + +## Authoring a recipe + +Publishing is blocked on the autonomous surfaces (the web agent and Telegram), +so if the user wants to publish something of their own, here is where it +happens. + +- **Start with the playbook, not a blank template.** To author an integration + recipe, call **`create_integration_flow`** first — it returns the full + IntegrationTemplate shape (auth, trigger, source, outputs, allowed_origins, + tool_grants, …) as a playbook to follow. Do NOT hand-roll a template or guess + at its fields; the spec lives in the playbook, not here. +- **Publish:** `publish_recipe` (kind-agnostic — board / automation / workflow / + bundle / integration) or `publish_integration_recipe` (integration-specific). + Both validate the template server-side and write nothing if it's invalid. + `unpublish_recipe` / `unpublish_integration_recipe` pull a recipe from the + catalog; existing installs are untouched — uninstall those separately. +- **Versioning is append-only.** A brand-new slug publishes at v1; re-publishing + a slug you own inserts `max(version)+1`, the old version stays as a + historical row, and existing installs keep their pinned version. There is no + in-place edit — fix a mistake by publishing the next version. You cannot + publish under a slug someone else owns. +- **`private` defaults to TRUE** for a new slug: visible and installable only by + the author until it's published with `private: false`. Say this out loud + after publishing, or the user will wonder why nobody can see it. A version + bump inherits current visibility unless `private` is passed. +- **Manage what you publish:** `list_publisher_recipes` (your integration + recipes + install counts by state), `get_publisher_recipe` (one recipe's + detail — auth, limits, recent install errors, publisher-secret *names*), the + publisher vault via `list_publisher_secrets` / `rotate_publisher_secret` / + `delete_publisher_secret`, and `bulk_set_integration_state` to move every + connected (non-revoked, non-pending) install of a recipe you own to active / + paused / stopped.