From cff24002507dd4621bba7afcfeb4957e3116990b Mon Sep 17 00:00:00 2001 From: Alon Muroch Date: Mon, 20 Jul 2026 10:33:51 +0300 Subject: [PATCH 1/2] fix(skills): correct tool-surface drift in build/integrations/agenda skills MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The layer-3 skills named tools that do not exist on stage and omitted large parts of the shipped tool surface. - brains-build: drop `get_overnight_digest` (the overnight-thinker service and its tool are gone); the daily brief is now the `starter-pack-my-day` recipe writing a "Daily digests" board. Correct `create_automation` -> `save_automation_draft`/`update_automation`. Add board forms, `run_board_skill`, and a compact "tools around a board" table covering sharing + share circles, dataset ACLs, board files/links, dashboard/mini-site/schema history + revert, and automation secrets. - brains-integrations: `list_recipes` (+ `recommend_recipes`) does enumerate the catalog over MCP — the "no MCP tool" claim was false. `list_installed_versions` reports version drift (the upgrade itself is still web-only). The recipe uninstall cascade (`preview_uninstall_recipe` / `uninstall_recipe`) is callable over MCP. Add install tuning (`set_integration_config`, `set_integration_overrides`, `run_integration_once`, `bulk_set_integration_state`) and a new plugin-only authoring section. - brains-agenda: same dead `get_overnight_digest` defect; read the daily brief from the "Daily digests" board instead. --- plugins/brains/skills/brains-agenda/SKILL.md | 24 +++--- plugins/brains/skills/brains-build/SKILL.md | 21 ++++- .../skills/brains-integrations/SKILL.md | 76 +++++++++++++++---- 3 files changed, 93 insertions(+), 28 deletions(-) diff --git a/plugins/brains/skills/brains-agenda/SKILL.md b/plugins/brains/skills/brains-agenda/SKILL.md index faffbe4..a91ee36 100644 --- a/plugins/brains/skills/brains-agenda/SKILL.md +++ b/plugins/brains/skills/brains-agenda/SKILL.md @@ -22,7 +22,10 @@ description: How to answer schedule / plan / agenda asks — "what's my day / we 1. `list_calendar_events start= end= limit=100` — **use this, NOT `list_pages type=calendar_event`**, which filters by ingest time not event time. Returns events sorted by start with attendees/location/RSVP parsed. -2. `get_overnight_digest` (no args = latest personal digest) — optional context. +2. The latest daily brief — optional context. There is no digest tool; the brief + is a board row written by the My Day recipe. `list_boards` → find "Daily + digests" → `get_board board_id= dataset=` and take the newest row. + Skip silently if the user doesn't have that board. If `list_calendar_events` returns 0 for the window, the ingestor may be behind: `fetch_from_integration source=calendar request="events from through @@ -40,17 +43,18 @@ with ⚠️ and note declined RSVPs, but never silently drop. ### Last night's read on things - -_(from overnight digest dated )_ + +_(from the daily brief dated )_ ### Decisions / open loops - + ``` -If `get_overnight_digest` returns `{found:false}`, omit **both** "Last night's read" -and "Decisions / open loops" silently — don't say "no digest available." +If there's no "Daily digests" board or no row for the window, omit **both** "Last +night's read" and "Decisions / open loops" silently — don't say "no digest +available." -If the connections lens is `(no connections worth surfacing this window)` or has no -`→ Action:` lines, omit "Decisions / open loops" silently — same rule. +If the brief has no action / follow-up lines, omit "Decisions / open loops" +silently — same rule. diff --git a/plugins/brains/skills/brains-build/SKILL.md b/plugins/brains/skills/brains-build/SKILL.md index 75c3b0e..bbf836b 100644 --- a/plugins/brains/skills/brains-build/SKILL.md +++ b/plugins/brains/skills/brains-build/SKILL.md @@ -1,6 +1,6 @@ --- name: brains-build -description: The catalog of higher-level brains features beyond search — boards, board skills, dataset recipes, dashboards, pages, automations, workflows, mini-sites, the Telegram bot, and the overnight digest. Use when the user wants to track a list, build a tracker/CRM, automate a recurring task, coordinate a goal with a team, publish a deck/one-pager, or asks "what can brains do." Names the right feature and the flow tool to start it. +description: The catalog of higher-level brains features beyond search — boards, board skills, dataset recipes, dashboards, pages, automations, workflows, mini-sites, board forms, the Telegram bot, and the daily digest. Use when the user wants to track a list, build a tracker/CRM, automate a recurring task, coordinate a goal with a team, publish a deck/one-pager, or asks "what can brains do." Names the right feature and the flow tool to start it. --- # brains feature catalog @@ -13,15 +13,16 @@ tools carry it. | Feature | What it is | Reach for it when… | Start with | |---|---|---|---| | **Board** | Spreadsheet-like dataset on a brain; rows are JSON, shared & queryable | "track a list of…", "keep a table/CRM/pipeline of…", "log every X" | `create_board_flow` | -| **Board skill** | Named saved LLM action over a board's rows | "every row, do X", "summarize/score/enrich each row", repeated lookup | `create_board_skill` | +| **Board skill** | Named saved LLM action over a board's rows | "every row, do X", "summarize/score/enrich each row", repeated lookup | `create_board_skill`; run a saved one with `run_board_skill` | | **Automation** (feed board) | An automation that fetches from email/cal/drive on a cron and appends rows | "run this nightly and feed a board", "keep this list fresh from email/cal/drive" | `create_automation_flow` | | **Dashboard** | Live HTML view over a board (stats / charts / table / kanban / tabs …) | "make me a dashboard", "show this as cards/kanban/chart", "change the dashboard" | `get_dashboard` → follow the returned `skill` → `set_dashboard` (`engine_version: "v2"`) | | **Page** | First-class note in a brain | "save this as a note", "keep this writeup findable" | `create_page` | | **Automation** | Sandboxed TS program on a cron schedule with a scoped token | "every morning do X", "auto-draft Y when Z", anything re-prompted on a timer | `create_automation_flow` | | **Workflow** | Goal container: charter + KPIs + deadlines + roster + owned board + paused template automations; one status flip pauses/un-pauses all | "ship X by Q3", "coordinate this initiative with a team & deadline" | `create_workflow_flow` | | **Mini-site** | Static sandboxed HTML on a brain — deck, one-pager, dashboard | "build me a deck/one-pager", "render this board visually", "share a link" | `create_mini_site` | +| **Board form** | Chat-with-agent intake page that appends a row to a board dataset on submit | "collect responses", "give people a form/signup link" | `create_board_form` (then `list_board_forms` / `update_board_form` / `list_board_form_responses` / `delete_board_form`) | | **Telegram bot** | The brain over Telegram | "text it from my phone", "ping me without the laptop" | (point to the bot) | -| **Overnight digest** | Auto daily/weekly read on what's coming up | "morning brief", "what should I be thinking about" | `get_overnight_digest` | +| **Daily digest** | Not a tool — a recipe-installed automation that writes a "Daily digests" board + dashboard | "morning brief", "what should I be thinking about" | `install_recipe slug="starter-pack-my-day"` (browse with `list_recipes`) | ## Picking among the three that overlap @@ -33,6 +34,18 @@ tools carry it. goal-shape is present.) Always use the **`*_flow`** tool for create/setup/scaffold asks — using -`create_board` / `create_automation` / `create_workflow` directly skips the +`create_board` / `save_automation_draft` / `create_workflow` directly skips the playbook and yields a structure that doesn't fit. Use the bare tools only when reproducing an existing entity verbatim or when the playbook tells you to. +(`update_automation` edits an existing automation in place — that one is fine +without a flow.) + +## Once it exists — the tools around a board + +| Need | Tools | +|---|---| +| **Share it** | `share_board` / `share_mini_site` / `share_folder` — `scope:'user'` (default, one email) or `scope:'org'` (whole workspace, needs `confirm:true`). Name a reusable audience once with `create_share_circle`. | +| **Per-dataset access** | `enable_board_dataset_acl` flips one board to deny-by-default (irreversible, owner-only), then `grant_board_dataset_access` / `revoke_board_dataset_access` / `list_board_dataset_access`; `set_board_dataset_subject` marks the column that identifies a row's subject. | +| **Files & external data** | `upload_board_file` attaches a file (`get_board_file_url` for the link); `create_board_link` binds the board to an adapter-backed external resource (http_json / ics / github / monday). | +| **Undo a bad edit** | Dashboards, mini-sites and board column-schemas are versioned: `list_history` → `get_history_entry` → `revert` (entity: `dashboard` \| `mini_site` \| `board_schema`). The revert is itself snapshotted, so it's reversible too — say so before anyone panics. | +| **Automation credentials** | `automation_secret_set` / `automation_secret_get` / `automation_secret_list` / `automation_secret_delete` — never inline an API key into automation code. | diff --git a/plugins/brains/skills/brains-integrations/SKILL.md b/plugins/brains/skills/brains-integrations/SKILL.md index 80a00ae..9378cf0 100644 --- a/plugins/brains/skills/brains-integrations/SKILL.md +++ b/plugins/brains/skills/brains-integrations/SKILL.md @@ -1,6 +1,6 @@ --- 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 build / publish a recipe of their own. --- # Integration lifecycle @@ -24,10 +24,13 @@ bounce the user to the browser for the common case: 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. +- **Browse the catalog over MCP:** `list_recipes` enumerates installable + recipes — `tier` is `'catalog'` (curated, default), `'yours'` (the caller's + own), or `'community'` (other users' published ones), with optional + `kind` / `category` / `q` filters. Use it to discover slugs the user doesn't + already know; `recommend_recipes` picks a few with an evidenced reason when + the user has no specific target in mind. `/recipes` in the web UI is the same + catalog with pictures. - **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 @@ -35,12 +38,18 @@ bounce the user to the browser for the common case: ## 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. +Read version drift from here: `list_installed_versions` returns one row per +install (`slug`, `name`, `kind`, `version`, `state`) — the caller's *actual* +pinned versions, so you never have to guess which My Day / starter-pack they're +on. Compare against the catalog's current version from `list_recipes`. + +The upgrade itself still runs through the web UI — there's no MCP upgrade tool. +`install_recipe` is idempotent and dedups rather than re-pinning an existing +install, so it is not an upgrade path. 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. ## Uninstall / manage @@ -50,8 +59,47 @@ user's secrets for that install), `pause_integration` / `resume_integration` 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 +Tune an install without reinstalling it: `set_integration_config` (installer-side +config, e.g. which repos github-issues ingests), `set_integration_overrides` +(tighten per-run / per-month limits — you can only lower the recipe's caps), +and `run_integration_once` for an on-demand test run instead of waiting for +cron. Publishers can flip state across every install of a recipe they own with +`bulk_set_integration_state`. + +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) without mutating anything, then +`uninstall_recipe` does it in one transaction — or pass `entity_id` to drop a +single entity. Always preview first for a bundle. The web UI exposes the same +thing per entity page. Everything lands in a 7-day recovery window; use `list_uninstalled` when the user asks "what did I just uninstall" before they click Recover. + +## Authoring a recipe (plugin-only) + +These tools are `pluginOnly` — they exist on this surface and nowhere else, so +if the user wants to *publish* something, this is the only place it can happen. + +- **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 auto-increments to `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. +- **`private` defaults to TRUE** for a new recipe: it's 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 recipes + install + counts by state), `get_publisher_recipe` (one recipe's detail — auth, limits, + recent install errors, publisher-secret *names*), and the publisher vault via + `list_publisher_secrets` / `rotate_publisher_secret` / `delete_publisher_secret`. + You cannot publish under a slug someone else owns. From a3ac90370f8b85290b371c08f8f89585f037dfe2 Mon Sep 17 00:00:00 2001 From: Alon Muroch Date: Tue, 21 Jul 2026 15:10:40 +0300 Subject: [PATCH 2/2] fix(skills): name the `daily` dataset in the agenda brief lookup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The agenda skill pointed at the "Daily digests" board but left the dataset as ``, and told the agent to take "the newest row". That board carries five datasets — `daily`, `weather`, `important_emails`, `telegram_log` and `unanswered_emails` — written by four different starter-pack automations. Only `daily` holds the My Day brief, so an unguided pick could surface a weather strip or a Telegram log under "Last night's read on things". Names the dataset and says why it matters. Same class as the drift this branch fixes: an identifier the agent had to guess. Verified against scripts/seed-starter-recipes/bundles.ts on ssvlabs/brains stage — board name "Daily digests" (bundles.ts:189), My Day's dataset "daily" (bundles.ts:96). Co-Authored-By: Claude Opus 4.8 (1M context) --- plugins/brains/skills/brains-agenda/SKILL.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/plugins/brains/skills/brains-agenda/SKILL.md b/plugins/brains/skills/brains-agenda/SKILL.md index a91ee36..45ecb51 100644 --- a/plugins/brains/skills/brains-agenda/SKILL.md +++ b/plugins/brains/skills/brains-agenda/SKILL.md @@ -23,9 +23,12 @@ description: How to answer schedule / plan / agenda asks — "what's my day / we `list_pages type=calendar_event`**, which filters by ingest time not event time. Returns events sorted by start with attendees/location/RSVP parsed. 2. The latest daily brief — optional context. There is no digest tool; the brief - is a board row written by the My Day recipe. `list_boards` → find "Daily - digests" → `get_board board_id= dataset=` and take the newest row. - Skip silently if the user doesn't have that board. + is a board row written by the My Day recipe. `list_boards` → find the board + named **"Daily digests"** → `get_board board_id= dataset="daily"` and take + the newest row. The dataset name matters: that board also carries `weather`, + `important_emails`, `telegram_log` and `unanswered_emails` from the other + starter-pack automations, and only `daily` holds the brief. Skip silently if + the user doesn't have that board. If `list_calendar_events` returns 0 for the window, the ingestor may be behind: `fetch_from_integration source=calendar request="events from through