Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion plugins/brains/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -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.3",
"version": "2.9.0",
"author": {
"name": "brains (ssvlabs)"
},
Expand Down
2 changes: 1 addition & 1 deletion plugins/brains/.codex-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "brains",
"version": "2.8.3",
"version": "2.9.0",
"description": "Your personal memory layer for Codex: query Gmail, Calendar, Drive, and prior conversations, then build boards, automations, and workflows.",
"author": {
"name": "brains (ssvlabs)",
Expand Down
10 changes: 5 additions & 5 deletions plugins/brains/generated/capability-catalog.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@
"capability_id": "integration-actions",
"catalog_sha256": "29f58ddc5dab294814934fe7af0d808f4b7dc441e3c3d29bb0e67fa705d7add4",
"artifact_path": "plugins/brains/skills/brains-write/SKILL.md",
"artifact_sha256": "b27acd2ffc1ba4914890a88dba00e71f7052bd738369f28f9d86a1566bd2f548"
"artifact_sha256": "21f7cd40bedb7602efbbd43f718c1e5e78e41a19de3d96f3dd599b95fb17732d"
},
{
"capability_id": "brains-features",
"catalog_sha256": "6086cbe9877b2005b3cea06611ba74ab7b527eaed04af39e5a815c94c8806b37",
"catalog_sha256": "a1677c30594d44e516a3aaa8e6524829360e44d90f8304d2ab8291b7e5ed6a3a",
"artifact_path": "plugins/brains/skills/brains-build/SKILL.md",
"artifact_sha256": "753afa4d8b4833b91622fa8c55d2bc76304e9a9170faed3365c7febd53836ae0"
"artifact_sha256": "f9196254dd49af3cd8bb1d4d93b1ba36ff10d8efc0bb039fb464901e143e143f"
},
{
"capability_id": "procedure:board",
Expand All @@ -27,7 +27,7 @@
"capability_id": "procedure:automation",
"catalog_sha256": "ea1e883788e138e39b80627e5a14d6ac35dc7f658b4628e7e32084bb6620fe8b",
"artifact_path": "plugins/brains/skills/brains-automation/SKILL.md",
"artifact_sha256": "e71b5648d93e611de49fd81549217db8232427262e282af6d1c1d70c9c2e17c9"
"artifact_sha256": "78c63d6a09ffdf55f80f3b686b1cd0d1000585bc1d5d148323ea03ebda018bc2"
},
{
"capability_id": "procedure:workflow",
Expand All @@ -36,5 +36,5 @@
"artifact_sha256": "b22814fd630ed78964d54440ec327e3aebcde8c812aaa9985fff9c4d345d3f09"
}
],
"source_commit": "864c45fa7e1c4b4d60116b6697abc596a272fbff"
"source_commit": "e332512644b2f06a6b7068dcc9196eab2d31c05a"
Comment thread
olegshmuelov marked this conversation as resolved.
}
5 changes: 3 additions & 2 deletions plugins/brains/skills/brains-automation/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,9 @@ It returns slug/title/snippet, so call `get_page` on the selected
slug to read frontmatter. Use its `install_id`, `action_name`, and structured
`input` in `brains.act`; this tuple is the only call
shape. Partial tuples error; only bare legacy `source` returns `clarification`.
`requires_confirmation:true` drafts for out-of-band approval;
`requires_confirmation:false` runs inline. If
`requires_confirmation:true` drafts for out-of-band approval unless this
automation's owner set `write_policy:'auto_confirm_safe'`, which executes it
inline; `requires_confirmation:false` runs inline either way. If
Comment thread
olegshmuelov marked this conversation as resolved.
`requires_confirmation` is absent, the page predates the field: treat whether
it drafts or runs as unknown. `side_effect` says where it writes
(`external` = the provider, visible outside brains;
Expand Down
3 changes: 3 additions & 0 deletions plugins/brains/skills/brains-build/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,19 @@ Don't restate that artifact here — open it.
|---|---|---|
| **Board** | A spreadsheet-like dataset on a brain — rows are JSON, shared and queryable. | `create_board_flow` if your client exposes it (follow the playbook it returns), otherwise `create_board` |
| **Board skill** | A named, saved LLM action over a board's rows. | `create_board_skill` |
| **Board form** | A shareable page where someone else answers your questions in chat — one board row per submission. | `create_board_form` |
| **Bulk upload** | Load a large or arbitrary-format file into a board without reading it into context. | `create_import_grant` → `bulk_append_rows` |
| **Dashboard** | A live, interactive view of a board that you author as a full HTML app. | `get_dashboard` → `set_dashboard` |
| **Page** | A first-class note in a brain. | `create_page` |
| **Automation** | A sandboxed TS program on a cron schedule with a scoped token. | `create_automation_flow` if your client exposes it (follow the playbook it returns), otherwise the Agents page (`/agents`) |
| **Recurring brief / digest** | Install a prebuilt automation that sends a recurring summary instead of scaffolding one. | `list_recipes` → `install_recipe` |
| **Workflow** | A goal container: charter, KPIs, deadlines, roster, an owned board and template automations. | `create_workflow_flow` if your client exposes it (follow the playbook it returns), otherwise `create_workflow` |
| **Mini-site** | Static sandboxed HTML on a brain — a deck, a one-pager, a shareable link. | `create_mini_site` |
| **Sharing** | Give someone access — share_board, share_folder (cascades), share_mini_site, create_share_circle. | `share_board` |
| **Board source link** | Bind a board to an external resource through an adapter (http_json, ics, github, monday). | `create_board_link` |
| **Dataset recipe** | Schedule a board link to materialize into a dataset on a cron — set up on the board's sources page. | the board's Sources page (`/boards/<id>/sources`) — no agent tool for this |
| **Integrations** | See what's connected and install, upgrade or remove an integration. | the `brains-integrations` skill if your client loads skills, otherwise `list_my_integrations` → `install_integration` |
| **Recipe publishing** | Publish a board or other supported build to the catalog — publish_recipe names the kinds it accepts. | `publish_recipe` |
| **Telegram bot** | Reach the brain from a phone, without the laptop. | the Connect Telegram page (`/install/telegram`) — no agent tool for this |

Reach for a `*_flow` tool for any create/set-up/scaffold ask: it returns the
Expand Down
9 changes: 7 additions & 2 deletions plugins/brains/skills/brains-write/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,13 @@ It returns slug/title/snippet, so call `get_page` on the selected
slug to read frontmatter. Use its `install_id`, `action_name`, and structured
`input` in `act_on_integration`; this tuple is the only call
shape. Partial tuples error; only bare legacy `source` returns `clarification`.
`requires_confirmation:true` drafts for out-of-band approval;
`requires_confirmation:false` runs inline. If
`requires_confirmation:true` normally drafts for out-of-band approval;
`requires_confirmation:false` runs inline. A user may additionally enable direct
execution in their brains settings, in which case a short allowlist of low-risk
actions also runs inline when called from an eligible client signed in from the
user's own computer (CLI clients such as Claude Code and Codex) — every execution
is still recorded in /inbox. The mode is decided server-side per call, so treat
any call as potentially executing and never assume a `draft_id`. If
Comment thread
olegshmuelov marked this conversation as resolved.
`requires_confirmation` is absent, the page predates the field: treat whether
it drafts or runs as unknown. `side_effect` says where it writes
(`external` = the provider, visible outside brains;
Expand Down
89 changes: 88 additions & 1 deletion tests/plugin-contract/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1602,6 +1602,62 @@ for (const dir of skillDirs) {
);
}

// The whole rendered action contract, pinned verbatim. The `includes` assertions
// below stay — they name WHICH rule broke, which "the paragraph changed" cannot —
// but presence is not enough on its own: `includes` proves a sentence is there and
// says nothing about what sits NEXT to it. Demonstrated during review: leaving
// every pinned sentence intact and INSERTING "the setting is off for almost
// everyone, so plan on the draft" straight after the `draft_id` rule kept every
// substring check green while reversing what the contract tells an agent to do.
// Only whole-region equality refuses an addition.
//
// BOUNDARIES. Start: the `Discover with …` line that opens the contract. End: the
// `| \`kind\` |` table header, EXCLUSIVE — so the region covers the entire prose
// block and an insertion anywhere in it, including just past the `draft_id`
// sentence where the review's attack landed, falls INSIDE the pin. Stopping at the
// `draft_id` sentence would leave that exact evasion one character outside the
// region and change nothing. Both anchors are structural rather than editorial:
// the opener is the first line every rendered face of this contract starts with,
// and the table header is a fixed column row, so neither moves under a reword of
// the prose between them.
//
// COST, accepted deliberately: this reddens on EVERY upstream reword of the
// paragraph, including harmless ones. That is the point — this text tells an agent
// when a call may fire an external write without asking, so a human should have to
// read each change to it before it ships to users.
//
// TO UPDATE (the only correct procedure): read the upstream diff and satisfy
// yourself the new wording is true of what the server does, then replace this
// constant with the new bytes. Do NOT relax it back to a substring or trim the
// region to make a sync green — that reintroduces the additive hole above.
const WRITE_ACTION_CONTRACT_REGION = [
"Discover with `query type=integration_action text=\"<intent>\"`.",
"It returns slug/title/snippet, so call `get_page` on the selected",
"slug to read frontmatter. Use its `install_id`, `action_name`, and structured",
"`input` in `act_on_integration`; this tuple is the only call",
"shape. Partial tuples error; only bare legacy `source` returns `clarification`.",
"`requires_confirmation:true` normally drafts for out-of-band approval;",
"`requires_confirmation:false` runs inline. A user may additionally enable direct",
"execution in their brains settings, in which case a short allowlist of low-risk",
"actions also runs inline when called from an eligible client signed in from the",
"user's own computer (CLI clients such as Claude Code and Codex) — every execution",
"is still recorded in /inbox. The mode is decided server-side per call, so treat",
"any call as potentially executing and never assume a `draft_id`. If",
"`requires_confirmation` is absent, the page predates the field: treat whether",
"it drafts or runs as unknown. `side_effect` says where it writes",
"(`external` = the provider, visible outside brains;",
"`null` or absent = undeclared, treat as external). Inline external writes",
"include `rsvp_event`,",
"`create_draft`, and `add_labels`; do not infer safety from read vs write.",
"Cap: 30 auto-executions/install/60s.",
Comment thread
olegshmuelov marked this conversation as resolved.
"Automation `dry_run` suppresses external writes to no-call `[DRY RUN]` drafts.",
].join("\n");
// Asserted AFTER the per-rule checks below, deliberately: they run first and name
// the specific rule that broke, and this one is the catch-all for everything they
// cannot see — an addition, a reflow, a sentence nobody thought to pin. Ordered the
// other way it fires first on every in-region edit and the precise messages never
// surface, which is the whole reason for keeping them.

// Keep independent semantic assertions: digest equality proves provenance, not
// that the canonical source itself kept the load-bearing safety rules.
assert(
Expand All @@ -1615,12 +1671,29 @@ assert(
writeSkillNormalized.includes("treat whether it drafts or runs as unknown"),
"absent requires_confirmation must remain unknown rather than predict a draft",
);
// `normally` is load-bearing, not hedging: a user who enables direct execution
// has an allowlisted action run inline from a local CLI despite
// `requires_confirmation:true`. The absolute wording this used to pin became
// false server-side, so the pin follows the truth rather than the other way
// round. The other two clauses are unchanged.
assert(
writeSkillNormalized.includes("`requires_confirmation:true` drafts for out-of-band approval") &&
writeSkillNormalized.includes("`requires_confirmation:true` normally drafts for out-of-band approval") &&
writeSkillNormalized.includes("`requires_confirmation:false` runs inline") &&
writeSkillNormalized.includes("| `auto_executed` | It already ran; it carries `result` and `action_record_id`."),
"both requires_confirmation branches must retain their distinct behavior",
);
// The rule that carries the weight once `true` can execute: the caller must not
// plan on a draft. Pinned as ONE CONTIGUOUS run of the rendered sentence, not as
// separate `includes` calls for "decided server-side" and "never assume a
// `draft_id`" — two fragments can each be present in different sentences, in
// different sections, or under a contradicting condition, which pins the
// vocabulary while letting the rule itself be reworded away.
assert(
writeSkillNormalized.includes(
"The mode is decided server-side per call, so treat any call as potentially executing and never assume a `draft_id`.",
),
"per-call server-side mode and the never-assume-a-draft_id rule must stay one intact sentence",
);
assert(
writeSkillNormalized.includes("Partial tuples error") &&
writeSkillNormalized.includes("only bare legacy `source` returns `clarification`"),
Expand Down Expand Up @@ -1663,6 +1736,20 @@ assert(writeSkillNormalized.includes("remains approvable"), "expired drafts must
assert(writeSkillNormalized.includes("this tuple is the only call shape"), "source-only action fallback must stay prohibited");
assert(!/act_on_integration[^.]{0,200}request=/.test(writeSkillNormalized), "free-form action request must not return");

// The catch-all for the contract paragraph — see WRITE_ACTION_CONTRACT_REGION above.
const writeContractStart = writeSkill.indexOf("Discover with `query type=integration_action");
const writeContractEnd = writeSkill.indexOf("| `kind` | What happened |");
assert(writeContractStart > 0, "brains-write must keep its action-contract opener");
assert(
writeContractEnd > writeContractStart,
"brains-write's action contract must precede the kind table — the slice below depends on it",
);
assert(
normalizeRegion(writeSkill.slice(writeContractStart, writeContractEnd)) ===
WRITE_ACTION_CONTRACT_REGION,
"brains-write's action contract must match the approved copy exactly — an inserted sentence can contradict a rule the per-rule checks above still find (regenerate upstream, re-read the change, then update WRITE_ACTION_CONTRACT_REGION)",
);

// Same treatment for brains-automation, and for the same reason: the digest proves
// these bytes came from the catalog, not that the catalog kept the rails. Nothing
// here would notice if a regeneration DELETED the secret-hygiene guidance — the
Expand Down
Loading